@titan-design/react-ui 0.13.0 → 0.14.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 (146) hide show
  1. package/README.md +14 -0
  2. package/dist/bodymap.js +129 -76
  3. package/dist/bodymap.js.map +1 -1
  4. package/dist/bodymap.mjs +129 -76
  5. package/dist/bodymap.mjs.map +1 -1
  6. package/dist/index.d.mts +117 -26
  7. package/dist/index.d.ts +117 -26
  8. package/dist/index.js +677 -450
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +673 -451
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{pages-_3Ajpiqu.d.ts → pages-CsjsUn0G.d.ts} +30 -13
  13. package/dist/{pages-CpW3OB3g.d.mts → pages-ZbT_pD_U.d.mts} +30 -13
  14. package/dist/pages.d.mts +1 -1
  15. package/dist/pages.d.ts +1 -1
  16. package/dist/pages.js +676 -701
  17. package/dist/pages.js.map +1 -1
  18. package/dist/pages.mjs +676 -701
  19. package/dist/pages.mjs.map +1 -1
  20. package/dist/theme/index.d.mts +31 -7
  21. package/dist/theme/index.d.ts +31 -7
  22. package/dist/theme/index.js +84 -20
  23. package/dist/theme/index.js.map +1 -1
  24. package/dist/theme/index.mjs +83 -21
  25. package/dist/theme/index.mjs.map +1 -1
  26. package/dist/theme/tokens-css.js +84 -20
  27. package/dist/theme/tokens-css.js.map +1 -1
  28. package/dist/theme/tokens-css.mjs +84 -20
  29. package/dist/theme/tokens-css.mjs.map +1 -1
  30. package/dist/theme/tokens.d.mts +56 -7
  31. package/dist/theme/tokens.d.ts +56 -7
  32. package/dist/theme/tokens.js +72 -20
  33. package/dist/theme/tokens.js.map +1 -1
  34. package/dist/theme/tokens.mjs +71 -21
  35. package/dist/theme/tokens.mjs.map +1 -1
  36. package/docs/render-testing-pattern.md +130 -0
  37. package/package.json +1 -1
  38. package/src/arch/Architecture.stories.tsx +3 -1
  39. package/src/arch/arch-graph.json +74 -72
  40. package/src/components/custom/ActiveWork/InitiativeBrief.tsx +2 -1
  41. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +8 -3
  42. package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
  43. package/src/components/custom/Gauge/Gauge.stories.tsx +4 -1
  44. package/src/components/custom/Gauge/Gauge.tsx +3 -1
  45. package/src/components/custom/Scatter/Scatter.stories.tsx +10 -7
  46. package/src/components/custom/Scatter/Scatter.tsx +6 -4
  47. package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
  48. package/src/components/custom/Table/Table.stories.tsx +1 -1
  49. package/src/components/custom/Table/Table.tsx +2 -2
  50. package/src/components/custom/Treemap/Treemap.stories.tsx +13 -10
  51. package/src/components/custom/Treemap/Treemap.tsx +4 -1
  52. package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +2 -1
  53. package/src/components/custom/Workout/BaseBadge.stories.tsx +11 -7
  54. package/src/components/custom/Workout/BaseBadge.tsx +3 -2
  55. package/src/components/custom/Workout/BodyMap.tsx +5 -3
  56. package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +13 -3
  57. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +5 -3
  58. package/src/components/custom/Workout/CapacityBandChart.stories.tsx +2 -1
  59. package/src/components/custom/Workout/CapacityBandChart.tsx +5 -3
  60. package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
  61. package/src/components/custom/Workout/DeviationBar.tsx +5 -2
  62. package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +7 -6
  63. package/src/components/custom/Workout/DualVelocityStrip.test.tsx +29 -16
  64. package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -8
  65. package/src/components/custom/Workout/ExerciseCard.test.tsx +39 -19
  66. package/src/components/custom/Workout/ExerciseCard.tsx +92 -167
  67. package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +89 -6
  68. package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
  69. package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
  70. package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
  71. package/src/components/custom/Workout/ExerciseHeading.tsx +192 -47
  72. package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -5
  73. package/src/components/custom/Workout/InputBar.tsx +4 -2
  74. package/src/components/custom/Workout/IntensityBar.tsx +4 -1
  75. package/src/components/custom/Workout/MesoCard.tsx +2 -1
  76. package/src/components/custom/Workout/MesoStatusCard.tsx +11 -10
  77. package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +8 -3
  78. package/src/components/custom/Workout/PrHistoryModal.stories.tsx +12 -2
  79. package/src/components/custom/Workout/README.md +58 -12
  80. package/src/components/custom/Workout/ReadinessCheck.tsx +4 -2
  81. package/src/components/custom/Workout/RestTimer.stories.tsx +13 -2
  82. package/src/components/custom/Workout/RestTimer.tsx +4 -2
  83. package/src/components/custom/Workout/SegmentedBar.stories.tsx +16 -12
  84. package/src/components/custom/Workout/SegmentedBar.tsx +4 -3
  85. package/src/components/custom/Workout/SetStrip.stories.tsx +3 -2
  86. package/src/components/custom/Workout/SetsRepsLoad.tsx +23 -9
  87. package/src/components/custom/Workout/Sparkline.stories.tsx +13 -10
  88. package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +2 -1
  89. package/src/components/custom/Workout/StrengthTrendChart.tsx +8 -6
  90. package/src/components/custom/Workout/TempoDisplay.stories.tsx +4 -3
  91. package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
  92. package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +3 -2
  93. package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +1 -1
  94. package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +40 -0
  95. package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
  96. package/src/components/custom/Workout/VelocityStrip.tsx +23 -14
  97. package/src/components/custom/Workout/ZoneTrack.stories.tsx +3 -1
  98. package/src/components/custom/Workout/exerciseRowState.ts +29 -0
  99. package/src/components/custom/Workout/index.ts +12 -2
  100. package/src/components/custom/Workout/metricText.stories.tsx +9 -8
  101. package/src/components/custom/Workout/metricText.tsx +4 -2
  102. package/src/components/custom/Workout/setHeadingKit.tsx +12 -10
  103. package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
  104. package/src/components/custom/charts/README.md +5 -0
  105. package/src/components/custom/charts/SetBarChart.test.tsx +16 -0
  106. package/src/components/custom/charts/SetBarChart.tsx +14 -5
  107. package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
  108. package/src/components/custom/charts/flatBarGeometry.ts +32 -0
  109. package/src/components/custom/stepper/Stepper.tsx +8 -6
  110. package/src/components/ui/alert/Alert.stories.tsx +14 -4
  111. package/src/components/ui/alert/Alert.tsx +24 -3
  112. package/src/components/ui/button/Button.tsx +12 -12
  113. package/src/components/ui/checkbox/Checkbox.tsx +2 -2
  114. package/src/components/ui/data-row/DataRow.stories.tsx +6 -2
  115. package/src/components/ui/drawer/Drawer.tsx +4 -1
  116. package/src/components/ui/icon-box/IconBox.tsx +8 -6
  117. package/src/components/ui/indicator/Indicator.stories.tsx +4 -3
  118. package/src/components/ui/list-item/ListItem.stories.tsx +7 -4
  119. package/src/components/ui/modal/Modal.tsx +2 -0
  120. package/src/components/ui/pill/Pill.stories.tsx +53 -0
  121. package/src/components/ui/pill/Pill.tsx +10 -6
  122. package/src/components/ui/popover/Popover.stories.tsx +1 -1
  123. package/src/components/ui/progress/Progress.tsx +9 -6
  124. package/src/components/ui/radio/Radio.tsx +1 -1
  125. package/src/components/ui/section/Section.stories.tsx +10 -10
  126. package/src/components/ui/select/Select.tsx +2 -1
  127. package/src/components/ui/spinner/Spinner.tsx +3 -1
  128. package/src/components/ui/stack/Stack.stories.tsx +7 -7
  129. package/src/components/ui/surface/Surface.stories.tsx +75 -28
  130. package/src/components/ui/switch/Switch.tsx +1 -1
  131. package/src/components/ui/tabs/Tabs.tsx +4 -2
  132. package/src/components/ui/toast/Toast.tsx +6 -4
  133. package/src/components/ui/toolbar-button/ToolbarButton.tsx +13 -6
  134. package/src/stories/PresetShowcase.stories.tsx +1 -1
  135. package/src/test/no-raw-color.test.ts +39 -0
  136. package/src/theme/Alpha.stories.tsx +103 -0
  137. package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
  138. package/src/theme/config.ts +12 -0
  139. package/src/theme/global.css +15 -3
  140. package/src/theme/subtle-tone-weight.test.ts +163 -0
  141. package/src/theme/tailwind-var-opacity.test.ts +163 -0
  142. package/src/theme/tokens/primitives.ts +33 -0
  143. package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
  144. package/src/theme/tokens/semantic.ts +56 -20
  145. package/src/utils/colors.alpha-adoption.test.ts +408 -0
  146. package/tailwind.config.js +50 -9
package/dist/pages.js CHANGED
@@ -6,12 +6,37 @@ var jsxRuntime = require('react/jsx-runtime');
6
6
  var clsx = require('clsx');
7
7
  var tailwindMerge = require('tailwind-merge');
8
8
 
9
- // src/components/custom/Workout/ActiveWorkoutPage.tsx
9
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
10
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
11
+ }) : x)(function(x) {
12
+ if (typeof require !== "undefined") return require.apply(this, arguments);
13
+ throw Error('Dynamic require of "' + x + '" is not supported');
14
+ });
10
15
 
11
16
  // src/theme/tokens/primitives.ts
12
17
  var primitiveColors = {
13
18
  white: "#FFFFFF"};
14
19
  var alertRedVivid = "#FF4757";
20
+ var resultPaletteColors = {
21
+ improve: "#4caf50",
22
+ improveDark: "#248a24",
23
+ degrade: "#ef5350",
24
+ degradeDark: "#b30000",
25
+ inconclusive: "#9E9A97"
26
+ };
27
+ var semanticPins = {
28
+ // Cool navy-black; darker and more blue-shifted than any warm greyRamp step.
29
+ textPrimaryLight: "#121828",
30
+ // Cool blue-grey; outside greyRamp's warm (R>=G>=B) scale.
31
+ textSecondaryLight: "#65748B",
32
+ // Pure neutral grey (R=G=B); no warm greyRamp step lands on it exactly.
33
+ backgroundBaseLight: "#EBEBEB",
34
+ // Near-neutral grey, kept one step off backgroundBaseLight by design.
35
+ dividerLight: "#E8E9EB",
36
+ // Periwinkle/indigo; more purple than any primitiveRamps.blue step. Shared by
37
+ // dark-mode text-link, border-focus and border-input-focus.
38
+ focusIndigoDark: "#828DF8"
39
+ };
15
40
  var greyRamp = {
16
41
  50: "#F9F6F3",
17
42
  // L*97.0 W6
@@ -45,6 +70,7 @@ var greyRamp = {
45
70
  var primitiveRamps = {
46
71
  red: {
47
72
  50: "#FFF4F4",
73
+ 400: "#F77175",
48
74
  500: "#E05254",
49
75
  600: "#D14343",
50
76
  // pin
@@ -186,6 +212,12 @@ var semanticColorsLight = {
186
212
  // Text on brand backgrounds (on-*)
187
213
  "on-brand-primary": primitiveColors.white,
188
214
  "on-brand-secondary": primitiveColors.white,
215
+ // Text ON a `-subtle` fill. Light mode keeps today's pairing (the base token) so
216
+ // nothing moves here: light has the same disease mirrored — a light rung on a
217
+ // near-white ramp[50] fill — but correcting it belongs with the light-mode pass
218
+ // in AW-121, not this one.
219
+ "on-brand-primary-subtle": primitiveRamps.orange[400],
220
+ "on-brand-secondary-subtle": primitiveRamps.cyan[600],
189
221
  // Status colors (status-*)
190
222
  "status-success": primitiveRamps.green[300],
191
223
  "status-success-light": primitiveRamps.green[200],
@@ -221,21 +253,26 @@ var semanticColorsLight = {
221
253
  "status-info-subtle": primitiveRamps.blue[50],
222
254
  "status-info-muted": "rgba(33, 150, 243, 0.30)",
223
255
  "status-info-strong": "rgba(33, 150, 243, 0.50)",
256
+ // Text ON a `-subtle` fill — see the on-brand-*-subtle note above.
257
+ "on-status-success-subtle": primitiveRamps.green[300],
258
+ "on-status-error-subtle": primitiveRamps.red[600],
259
+ "on-status-warning-subtle": primitiveRamps.amber[300],
260
+ "on-status-info-subtle": primitiveRamps.blue[500],
224
261
  // Text on status backgrounds (on-status-*)
225
262
  "on-status-success": primitiveColors.white,
226
263
  "on-status-error": primitiveColors.white,
227
264
  "on-status-warning": primitiveColors.white,
228
265
  "on-status-info": primitiveColors.white,
229
266
  // Result/outcome indicators (result-*)
230
- "result-improve": "#4caf50",
267
+ "result-improve": resultPaletteColors.improve,
231
268
  // Green - positive outcome
232
269
  "result-improve-light": "rgba(76, 175, 80, 0.12)",
233
- "result-improve-dark": "#248a24",
234
- "result-degrade": "#ef5350",
270
+ "result-improve-dark": resultPaletteColors.improveDark,
271
+ "result-degrade": resultPaletteColors.degrade,
235
272
  // Red - negative outcome
236
273
  "result-degrade-light": "rgba(239, 83, 80, 0.12)",
237
- "result-degrade-dark": "#b30000",
238
- "result-inconclusive": "#9E9A97",
274
+ "result-degrade-dark": resultPaletteColors.degradeDark,
275
+ "result-inconclusive": resultPaletteColors.inconclusive,
239
276
  // Gray - no clear result
240
277
  "result-inconclusive-light": "rgba(158, 154, 151, 0.12)",
241
278
  "result-neutral": greyRamp[600],
@@ -267,8 +304,8 @@ var semanticColorsLight = {
267
304
  "data-10": discreteRainbow[22],
268
305
  // Dark Orange
269
306
  // Text colors (text-*)
270
- "text-primary": "#121828",
271
- "text-secondary": "#65748B",
307
+ "text-primary": semanticPins.textPrimaryLight,
308
+ "text-secondary": semanticPins.textSecondaryLight,
272
309
  "text-tertiary": greyRamp[400],
273
310
  "text-disabled": "rgba(55, 65, 81, 0.48)",
274
311
  "text-inverse": primitiveColors.white,
@@ -284,7 +321,7 @@ var semanticColorsLight = {
284
321
  "surface-input": greyRamp[50],
285
322
  // Input field background (filled variant)
286
323
  // Background colors (background-*)
287
- "background-base": "#EBEBEB",
324
+ "background-base": semanticPins.backgroundBaseLight,
288
325
  "background-default": primitiveColors.white,
289
326
  "background-subtle": greyRamp[50],
290
327
  // Frame/bezel chrome — top bar + side nav shell, one step below
@@ -315,7 +352,7 @@ var semanticColorsLight = {
315
352
  "interactive-disabled": "rgba(55, 65, 81, 0.12)",
316
353
  "interactive-disabled-text": "rgba(55, 65, 81, 0.26)",
317
354
  // Divider
318
- divider: "#E8E9EB",
355
+ divider: semanticPins.dividerLight,
319
356
  // Avatar default
320
357
  "avatar-background": greyRamp[600],
321
358
  "avatar-text": primitiveColors.white
@@ -333,7 +370,7 @@ var semanticColorsDark = {
333
370
  "brand-secondary": primitiveRamps.cyan[600],
334
371
  "brand-secondary-light": primitiveRamps.cyan[500],
335
372
  "brand-secondary-dark": primitiveRamps.cyan[700],
336
- "brand-secondary-subtle": "rgba(48, 123, 155, 0.12)",
373
+ "brand-secondary-subtle": "rgba(34, 211, 238, 0.12)",
337
374
  "brand-secondary-muted": "rgba(48, 123, 155, 0.30)",
338
375
  "brand-secondary-strong": "rgba(48, 123, 155, 0.50)",
339
376
  "brand-secondary-hover": primitiveRamps.cyan[500],
@@ -341,6 +378,14 @@ var semanticColorsDark = {
341
378
  // Text on brand backgrounds
342
379
  "on-brand-primary": primitiveColors.white,
343
380
  "on-brand-secondary": primitiveColors.white,
381
+ // Text ON a `-subtle` fill. Its own role: `brand-primary` and friends are tuned to
382
+ // carry a white label as a solid fill, which makes the two deepest of them
383
+ // (cyan[600], red[600]) too dark to read as text on a dark plane. Levelling the
384
+ // family at rung 300 puts every tone within OKLCH L 0.769-0.813 instead of
385
+ // 0.550-0.813. Brand is deliberately left at its own orange[400] (operator, AW-133)
386
+ // so the Voltras tone never drifts; it is the one tone under AA on a raised card.
387
+ "on-brand-primary-subtle": primitiveRamps.orange[400],
388
+ "on-brand-secondary-subtle": primitiveRamps.cyan[300],
344
389
  // Status colors
345
390
  "status-success": primitiveRamps.green[300],
346
391
  "status-success-light": primitiveRamps.green[200],
@@ -354,7 +399,10 @@ var semanticColorsDark = {
354
399
  "status-error": primitiveRamps.red[600],
355
400
  "status-error-light": primitiveRamps.red[500],
356
401
  "status-error-dark": primitiveRamps.red[700],
357
- "status-error-subtle": "rgba(209, 67, 67, 0.12)",
402
+ // Alpha 0.08, not the family's 0.12: error's label is red[400], a rung darker than
403
+ // its siblings so it reads RED rather than pink (rung 300 has only 0.121 chroma).
404
+ // Thinning the fill buys back the contrast that extra darkness costs. See AW-133.
405
+ "status-error-subtle": "rgba(247, 113, 117, 0.08)",
358
406
  "status-error-muted": "rgba(209, 67, 67, 0.30)",
359
407
  "status-error-strong": "rgba(209, 67, 67, 0.50)",
360
408
  "status-error-vivid": alertRedVivid,
@@ -372,7 +420,7 @@ var semanticColorsDark = {
372
420
  "status-info": primitiveRamps.blue[500],
373
421
  "status-info-light": primitiveRamps.blue[300],
374
422
  "status-info-dark": primitiveRamps.blue[600],
375
- "status-info-subtle": "rgba(33, 150, 243, 0.12)",
423
+ "status-info-subtle": "rgba(120, 194, 255, 0.12)",
376
424
  "status-info-muted": "rgba(33, 150, 243, 0.30)",
377
425
  "status-info-strong": "rgba(33, 150, 243, 0.50)",
378
426
  // Text on status backgrounds
@@ -380,14 +428,22 @@ var semanticColorsDark = {
380
428
  "on-status-error": primitiveColors.white,
381
429
  "on-status-warning": primitiveColors.white,
382
430
  "on-status-info": primitiveColors.white,
431
+ // Text ON a `-subtle` fill — see the on-brand-*-subtle note above. Error is the
432
+ // second deliberate exception to the rung-300 rule (operator, AW-133): red[300]
433
+ // levelled perfectly but read PINK, because a red that light can only hold 0.121
434
+ // chroma. red[400] carries 0.165 and reads red; its fill is thinned to compensate.
435
+ "on-status-success-subtle": primitiveRamps.green[300],
436
+ "on-status-error-subtle": primitiveRamps.red[400],
437
+ "on-status-warning-subtle": primitiveRamps.amber[300],
438
+ "on-status-info-subtle": primitiveRamps.blue[300],
383
439
  // Result/outcome indicators (result-*)
384
- "result-improve": "#4caf50",
440
+ "result-improve": resultPaletteColors.improve,
385
441
  "result-improve-light": "rgba(76, 175, 80, 0.16)",
386
- "result-improve-dark": "#248a24",
387
- "result-degrade": "#ef5350",
442
+ "result-improve-dark": resultPaletteColors.improveDark,
443
+ "result-degrade": resultPaletteColors.degrade,
388
444
  "result-degrade-light": "rgba(239, 83, 80, 0.16)",
389
- "result-degrade-dark": "#b30000",
390
- "result-inconclusive": "#9E9A97",
445
+ "result-degrade-dark": resultPaletteColors.degradeDark,
446
+ "result-inconclusive": resultPaletteColors.inconclusive,
391
447
  "result-inconclusive-light": "rgba(158, 154, 151, 0.16)",
392
448
  "result-neutral": greyRamp[400],
393
449
  // Text on result backgrounds
@@ -418,7 +474,7 @@ var semanticColorsDark = {
418
474
  "text-tertiary": greyRamp[500],
419
475
  "text-disabled": "rgba(255, 255, 255, 0.38)",
420
476
  "text-inverse": greyRamp[950],
421
- "text-link": "#828DF8",
477
+ "text-link": semanticPins.focusIndigoDark,
422
478
  "text-link-hover": primitiveRamps.blue[400],
423
479
  // Surface colors - dark backgrounds — warm-tapered DERIVED ramp (TD-surface-tokens,
424
480
  // S-1, re-spaced S-3). Shipped verbatim from `deriveSurfaceRamp()` — see
@@ -468,10 +524,10 @@ var semanticColorsDark = {
468
524
  // plane, so it keeps its job without collision.
469
525
  "border-prominent": greyRamp[800],
470
526
  // high-visibility divider
471
- "border-focus": "#828DF8",
527
+ "border-focus": semanticPins.focusIndigoDark,
472
528
  "border-input": greyRamp[700],
473
529
  "border-input-hover": greyRamp[600],
474
- "border-input-focus": "#828DF8",
530
+ "border-input-focus": semanticPins.focusIndigoDark,
475
531
  "border-input-error": primitiveRamps.red[500],
476
532
  // Alpha hairline separators — the primary separation cue (§4/S-2). Self-
477
533
  // normalizing: composites toward white by ~the same amount on ANY plane, so
@@ -736,6 +792,22 @@ function useLiveRepGrowth(active, liveRepIndex, liveVelocity, isNewPeak) {
736
792
  }, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveGrowth]);
737
793
  return liveGrowth;
738
794
  }
795
+
796
+ // src/components/custom/charts/flatBarGeometry.ts
797
+ var SET_LEVEL_FLAT_BAR = {
798
+ /** Track height (px). */
799
+ height: 8,
800
+ /** Gap between segment slots (px) — also SetStrip's between-set gap, see `SEGMENTED_BAR_GAP`. */
801
+ gap: 5,
802
+ /** Corner radius (px). */
803
+ radius: 2
804
+ };
805
+ var REP_LEVEL_FLAT_BAR = {
806
+ /** Inter-bar gap is proportional (`gapRatio · barWidth`), floored at this many px. */
807
+ gapFloor: 2,
808
+ /** Corner radius (px) default. */
809
+ radius: 5
810
+ };
739
811
  function wrapJsx(fn) {
740
812
  return function(type, props, ...rest) {
741
813
  if (props && typeof props.className === "string" && props.className) {
@@ -770,7 +842,7 @@ var CONTINUE_OUTLINE = primitiveRamps.cyan[800];
770
842
  var FIXED_MAX_VALUE = 1.15;
771
843
  var PEAK_HEADROOM = 1.03;
772
844
  function scaleDenominator(scale, maxValue) {
773
- return scale === "fixed" ? FIXED_MAX_VALUE : maxValue * PEAK_HEADROOM;
845
+ return (scale === "fixed" ? FIXED_MAX_VALUE : maxValue) * PEAK_HEADROOM;
774
846
  }
775
847
  function valueLabelFontSize(height) {
776
848
  return Math.round(Math.max(8, Math.min(12, height * 0.11)));
@@ -786,12 +858,12 @@ var SET_BAR_DEFAULT_HEIGHT = 220;
786
858
  var LABEL_ROW_HEIGHT = 16;
787
859
  var LABEL_GAP = 3;
788
860
  var GAP_RATIO = 0.08;
789
- var MIN_BAR_GAP = 2;
861
+ var MIN_BAR_GAP = REP_LEVEL_FLAT_BAR.gapFloor;
790
862
  var CHUNK_NOTCH_RATIO = 0.25;
791
863
  var CHUNK_NOTCH_MIN_PX = 10;
792
864
  var BAR_MAX_WIDTH = 120;
793
865
  var LABEL_MIN_BAR_WIDTH = 30;
794
- var DEFAULT_BAR_RADIUS = 5;
866
+ var DEFAULT_BAR_RADIUS = REP_LEVEL_FLAT_BAR.radius;
795
867
  var MIN_BAR_HEIGHT = 4;
796
868
  var FLAT_BAR_FRACTION = 0.5;
797
869
  var STUB_HEIGHT = 9;
@@ -1667,232 +1739,8 @@ function VelocityStrip({
1667
1739
  function cn(...inputs) {
1668
1740
  return tailwindMerge.twMerge(clsx.clsx(inputs));
1669
1741
  }
1670
- function resolveColor(token, mode = "dark") {
1671
- return reactNative.Platform.OS === "web" ? `var(--color-${token})` : getSemanticColors(mode)[token];
1672
- }
1673
-
1674
- // src/components/custom/Workout/PlaceholderStrip.tsx
1675
- var RAIL_TOKEN = "border-prominent";
1676
- function SingleStrip({
1677
- width,
1678
- className,
1679
- ...props
1680
- }) {
1681
- return /* @__PURE__ */ jsx(
1682
- reactNative.View,
1683
- {
1684
- className,
1685
- style: [
1686
- {
1687
- height: 3,
1688
- backgroundColor: resolveColor(RAIL_TOKEN),
1689
- borderRadius: 2,
1690
- opacity: 0.5
1691
- },
1692
- width != null ? { width } : { flex: 1 }
1693
- ],
1694
- accessibilityRole: "image",
1695
- accessibilityLabel: "Planned set, not yet completed",
1696
- testID: "placeholder-strip",
1697
- ...props
1698
- }
1699
- );
1700
- }
1701
- function SegmentedStrip({
1702
- segments = 3,
1703
- width,
1704
- className,
1705
- ...props
1706
- }) {
1707
- return /* @__PURE__ */ jsx(
1708
- reactNative.View,
1709
- {
1710
- className: cn("flex-row", className),
1711
- style: [
1712
- { height: 3, gap: 2, opacity: 0.5 },
1713
- width != null ? { width } : { flex: 1 }
1714
- ],
1715
- accessibilityRole: "image",
1716
- accessibilityLabel: "Planned set, not yet completed",
1717
- testID: "placeholder-strip",
1718
- ...props,
1719
- children: Array.from({ length: segments }, (_, i) => /* @__PURE__ */ jsx(
1720
- reactNative.View,
1721
- {
1722
- style: {
1723
- flex: 1,
1724
- height: 3,
1725
- backgroundColor: resolveColor(RAIL_TOKEN),
1726
- borderRadius: 1,
1727
- minWidth: 4
1728
- },
1729
- accessibilityElementsHidden: true,
1730
- testID: "placeholder-segment"
1731
- },
1732
- i
1733
- ))
1734
- }
1735
- );
1736
- }
1737
- function PlaceholderStrip({ mode = "single", segments, ...props }) {
1738
- if (mode === "segmented") {
1739
- return /* @__PURE__ */ jsx(SegmentedStrip, { segments, ...props });
1740
- }
1741
- return /* @__PURE__ */ jsx(SingleStrip, { ...props });
1742
- }
1743
-
1744
- // src/components/icons/SvgIcon.tsx
1745
- function SvgIcon({
1746
- size = 24,
1747
- color = "currentColor",
1748
- strokeWidth = 2,
1749
- fill = "none",
1750
- title,
1751
- children
1752
- }) {
1753
- return /* @__PURE__ */ jsxs(
1754
- "svg",
1755
- {
1756
- width: size,
1757
- height: size,
1758
- viewBox: "0 0 24 24",
1759
- fill,
1760
- stroke: color,
1761
- strokeWidth,
1762
- strokeLinecap: "round",
1763
- strokeLinejoin: "round",
1764
- ...title ? { role: "img", "aria-label": title } : { "aria-hidden": true },
1765
- children: [
1766
- title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
1767
- children
1768
- ]
1769
- }
1770
- );
1771
- }
1772
1742
 
1773
- // src/components/icons/icons.tsx
1774
- function StarIcon(props) {
1775
- 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" }) });
1776
- }
1777
- function ScaleIcon(props) {
1778
- return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1779
- /* @__PURE__ */ jsx("path", { d: "m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
1780
- /* @__PURE__ */ jsx("path", { d: "m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
1781
- /* @__PURE__ */ jsx("path", { d: "M7 21h10" }),
1782
- /* @__PURE__ */ jsx("path", { d: "M12 3v18" }),
1783
- /* @__PURE__ */ jsx("path", { d: "M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2" })
1784
- ] });
1785
- }
1786
- function AlertTriangleIcon(props) {
1787
- return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1788
- /* @__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" }),
1789
- /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
1790
- /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
1791
- ] });
1792
- }
1793
- function TrendingDownIcon(props) {
1794
- return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1795
- /* @__PURE__ */ jsx("path", { d: "M16 17h6v-6" }),
1796
- /* @__PURE__ */ jsx("path", { d: "m22 17-8.5-8.5-5 5L2 7" })
1797
- ] });
1798
- }
1799
- function CircleSlashIcon(props) {
1800
- return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1801
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
1802
- /* @__PURE__ */ jsx("path", { d: "m9 15 6-6" })
1803
- ] });
1804
- }
1805
- function AwardIcon(props) {
1806
- return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1807
- /* @__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" }),
1808
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "6" })
1809
- ] });
1810
- }
1811
- function InfoIcon(props) {
1812
- return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
1813
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
1814
- /* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
1815
- /* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
1816
- ] });
1817
- }
1818
- var t = getSemanticColors("dark");
1819
- var baseBadgeSizeConfig = {
1820
- sm: { fontSize: 9, paddingH: 6, paddingV: 2, iconSize: 10 },
1821
- md: { fontSize: 10, paddingH: 8, paddingV: 2, iconSize: 12 },
1822
- lg: { fontSize: 12, paddingH: 10, paddingV: 4, iconSize: 14 }
1823
- };
1824
- var variantColors = {
1825
- plain: { backgroundColor: t["surface-raised"], borderColor: t["hairline-default"] },
1826
- pr: {
1827
- backgroundColor: "rgba(255, 121, 0, 0.12)",
1828
- borderColor: "rgba(255, 121, 0, 0.3)"
1829
- }
1830
- };
1831
- function BaseBadge({
1832
- variant = "plain",
1833
- size = "md",
1834
- icon,
1835
- onPress,
1836
- className,
1837
- children,
1838
- ...props
1839
- }) {
1840
- const [scaleAnim] = react.useState(() => new reactNative.Animated.Value(1));
1841
- const config = baseBadgeSizeConfig[size];
1842
- const colors = variantColors[variant];
1843
- const handlePressIn = () => {
1844
- reactNative.Animated.timing(scaleAnim, {
1845
- toValue: 0.97,
1846
- duration: 150,
1847
- useNativeDriver: true
1848
- }).start();
1849
- };
1850
- const handlePressOut = () => {
1851
- reactNative.Animated.timing(scaleAnim, {
1852
- toValue: 1,
1853
- duration: 150,
1854
- useNativeDriver: true
1855
- }).start();
1856
- };
1857
- const badge = /* @__PURE__ */ jsxs(
1858
- reactNative.View,
1859
- {
1860
- className,
1861
- style: {
1862
- flexDirection: "row",
1863
- alignItems: "center",
1864
- gap: 3,
1865
- // borderRadius 2 is intentional per workout-tokens.ts (squared-off pill)
1866
- borderRadius: 2,
1867
- borderWidth: 1,
1868
- borderColor: colors.borderColor,
1869
- backgroundColor: colors.backgroundColor,
1870
- paddingHorizontal: config.paddingH,
1871
- paddingVertical: config.paddingV
1872
- },
1873
- ...props,
1874
- children: [
1875
- icon != null && /* @__PURE__ */ jsx(reactNative.View, { accessibilityElementsHidden: true, testID: "base-badge-icon", children: icon }),
1876
- children
1877
- ]
1878
- }
1879
- );
1880
- if (onPress) {
1881
- return /* @__PURE__ */ jsx(reactNative.Animated.View, { style: { transform: [{ scale: scaleAnim }] }, children: /* @__PURE__ */ jsx(
1882
- reactNative.Pressable,
1883
- {
1884
- onPress,
1885
- onPressIn: handlePressIn,
1886
- onPressOut: handlePressOut,
1887
- accessibilityRole: "button",
1888
- accessibilityLabel: props.accessibilityLabel,
1889
- testID: "base-badge-pressable",
1890
- children: badge
1891
- }
1892
- ) });
1893
- }
1894
- return badge;
1895
- }
1743
+ // src/components/custom/Typography/Typography.tsx
1896
1744
  var variantStyles = {
1897
1745
  h1: "font-heading text-5xl font-bold leading-tight",
1898
1746
  h2: "font-heading text-4xl font-bold leading-tight",
@@ -1983,81 +1831,11 @@ function Typography({
1983
1831
  }
1984
1832
  );
1985
1833
  }
1986
-
1987
- // src/components/custom/Workout/PrBadge.tsx
1988
- var typeLabels = {
1989
- e1rm: "PR e1RM",
1990
- weight: "PR Weight",
1991
- reps: "PR Reps",
1992
- volume: "PR Volume",
1993
- velocity: "PR Velocity"
1994
- };
1995
- function PrBadge({
1996
- type = "e1rm",
1997
- label: labelProp,
1998
- compact = false,
1999
- animate = true,
2000
- className,
2001
- ...props
2002
- }) {
2003
- const resolvedLabel = labelProp ?? typeLabels[type];
2004
- const brandPrimary = resolveColor("brand-primary");
2005
- const [scale] = react.useState(() => new reactNative.Animated.Value(animate ? 0.8 : 1));
2006
- const [opacity] = react.useState(() => new reactNative.Animated.Value(animate ? 0 : 1));
2007
- react.useEffect(() => {
2008
- if (!animate) return;
2009
- reactNative.Animated.parallel([
2010
- reactNative.Animated.timing(scale, {
2011
- toValue: 1,
2012
- duration: 400,
2013
- easing: reactNative.Easing.bezier(0.34, 1.56, 0.64, 1),
2014
- useNativeDriver: true
2015
- }),
2016
- reactNative.Animated.timing(opacity, {
2017
- toValue: 1,
2018
- duration: 400,
2019
- easing: reactNative.Easing.bezier(0.34, 1.56, 0.64, 1),
2020
- useNativeDriver: true
2021
- })
2022
- ]).start();
2023
- }, [animate, scale, opacity]);
2024
- const badge = compact ? /* @__PURE__ */ jsx(
2025
- reactNative.View,
2026
- {
2027
- className,
2028
- accessibilityRole: "image",
2029
- accessibilityLabel: `Personal record: ${resolvedLabel}`,
2030
- testID: "pr-badge-star",
2031
- ...props,
2032
- children: /* @__PURE__ */ jsx(StarIcon, { size: 14, color: brandPrimary, fill: brandPrimary, strokeWidth: 2 })
2033
- }
2034
- ) : /* @__PURE__ */ jsx(
2035
- BaseBadge,
2036
- {
2037
- variant: "pr",
2038
- className,
2039
- accessibilityLabel: `Personal record: ${resolvedLabel}`,
2040
- ...props,
2041
- children: /* @__PURE__ */ jsxs(
2042
- Typography,
2043
- {
2044
- variant: "boldLabel",
2045
- color: "inherit",
2046
- className: "leading-[normal] text-brand-primary",
2047
- children: [
2048
- "\u2605",
2049
- " ",
2050
- resolvedLabel
2051
- ]
2052
- }
2053
- )
2054
- }
2055
- );
2056
- if (animate) {
2057
- return /* @__PURE__ */ jsx(reactNative.Animated.View, { style: { transform: [{ scale }], opacity }, testID: "pr-badge-animated", children: badge });
2058
- }
2059
- return badge;
1834
+ function resolveColor(token, mode = "dark") {
1835
+ return reactNative.Platform.OS === "web" ? `var(--color-${token})` : getSemanticColors(mode)[token];
2060
1836
  }
1837
+
1838
+ // src/components/custom/Workout/SetRow.tsx
2061
1839
  var TEXT_ACTIVE = "text-primary";
2062
1840
  var TEXT_MUTED = "text-secondary";
2063
1841
  var COL = { set: 36, reps: 44, load: 56, rpe: 36 };
@@ -2242,37 +2020,180 @@ function SetTableHeader({
2242
2020
  }
2243
2021
  );
2244
2022
  }
2245
- var METRIC_FONT = "Inter, sans-serif";
2246
- function MetricCell({ children, color, fontSize = 11 }) {
2247
- return /* @__PURE__ */ jsx(
2248
- reactNative.Text,
2249
- {
2250
- style: {
2251
- fontFamily: METRIC_FONT,
2252
- fontSize,
2253
- color,
2254
- fontWeight: "600",
2255
- letterSpacing: 1
2256
- },
2257
- children
2258
- }
2023
+ var FLOATING_LIFT_MIN = 4;
2024
+ var LIFT_RIM_ALPHA = { dark: 0.12, light: 0.9 };
2025
+ var AMBIENT = {
2026
+ 1: [
2027
+ { y: 1, blur: 2, alpha: 0.35 },
2028
+ { y: 2, blur: 6, alpha: 0.3 }
2029
+ ],
2030
+ 2: [
2031
+ { y: 1, blur: 2, alpha: 0.35 },
2032
+ { y: 4, blur: 12, alpha: 0.32 }
2033
+ ],
2034
+ 3: [
2035
+ { y: 2, blur: 4, alpha: 0.35 },
2036
+ { y: 8, blur: 22, alpha: 0.35 }
2037
+ ],
2038
+ 4: [
2039
+ { y: 1, blur: 2, alpha: 0.35 },
2040
+ { y: 10, blur: 24, alpha: 0.3 },
2041
+ { y: 24, blur: 48, alpha: 0.2 }
2042
+ ],
2043
+ 5: [
2044
+ { y: 2, blur: 4, alpha: 0.4 },
2045
+ { y: 16, blur: 32, alpha: 0.35 },
2046
+ { y: 32, blur: 64, alpha: 0.25 }
2047
+ ]
2048
+ };
2049
+ var AMBIENT_ALPHA_SCALE = { dark: 1, light: 0.4 };
2050
+ function ambientLayers(step, mode) {
2051
+ const scale = AMBIENT_ALPHA_SCALE[mode];
2052
+ return AMBIENT[step].map(
2053
+ ({ y, blur, alpha: alpha2 }) => `0 ${y}px ${blur}px rgba(0,0,0,${(alpha2 * scale).toFixed(2)})`
2259
2054
  );
2260
2055
  }
2261
-
2262
- // src/components/custom/Workout/SetsRepsLoad.tsx
2263
- var TIMES = "\xD7";
2264
- var AT = "@";
2265
- function SetsRepsLoad({
2266
- sets,
2267
- reps,
2268
- load,
2269
- unit = "lb",
2056
+ function liftShadow(step, mode = "dark", opts = {}) {
2057
+ const rim = opts.rim ?? LIFT_RIM_ALPHA[mode];
2058
+ const layers = ambientLayers(step, mode);
2059
+ if (rim > 0) layers.unshift(`inset 0 1px 0 rgba(255,255,255,${rim.toFixed(2)})`);
2060
+ return layers.join(", ");
2061
+ }
2062
+ function liftStyle(step, mode = "dark", opts = {}) {
2063
+ const deepest = AMBIENT[step][AMBIENT[step].length - 1];
2064
+ return reactNative.Platform.select({
2065
+ web: { boxShadow: liftShadow(step, mode, opts) },
2066
+ default: {
2067
+ shadowColor: "#000",
2068
+ shadowOffset: { width: 0, height: Math.round(deepest.y / 2) },
2069
+ shadowOpacity: deepest.alpha * AMBIENT_ALPHA_SCALE[mode],
2070
+ shadowRadius: deepest.blur / 2,
2071
+ elevation: step
2072
+ }
2073
+ });
2074
+ }
2075
+
2076
+ // src/theme/elevation.ts
2077
+ var FLOATING_ELEVATION_MIN = FLOATING_LIFT_MIN;
2078
+ function getPressedRecessShadow(fillColor, _mode = "dark") {
2079
+ const { boxShadow } = insetWell(fillColor);
2080
+ return reactNative.Platform.select({
2081
+ web: { boxShadow },
2082
+ default: {}
2083
+ });
2084
+ }
2085
+ var glowConfig = {
2086
+ tight: { blur: 4, spread: 0, opacity: 0.4 },
2087
+ subtle: { blur: 12, spread: 0, opacity: 0.25 },
2088
+ medium: { blur: 20, spread: 2, opacity: 0.4 },
2089
+ strong: { blur: 30, spread: 4, opacity: 0.55 }
2090
+ };
2091
+ function getGlowShadow(color, intensity = "medium") {
2092
+ const config = glowConfig[intensity];
2093
+ const rgb = hexToRgb(color);
2094
+ if (!rgb) return {};
2095
+ return reactNative.Platform.select({
2096
+ web: {
2097
+ boxShadow: `0 0 ${config.blur}px ${config.spread}px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${config.opacity})`
2098
+ },
2099
+ default: {
2100
+ shadowColor: color,
2101
+ shadowOffset: { width: 0, height: 0 },
2102
+ shadowOpacity: config.opacity,
2103
+ shadowRadius: config.blur / 2,
2104
+ elevation: 0
2105
+ }
2106
+ });
2107
+ }
2108
+ function recessDepth(inherited, planesDown, mode) {
2109
+ let plane = inherited.level;
2110
+ for (let i = 0; i < planesDown; i++) plane = pressedLevel(plane);
2111
+ const backgroundColor = surfaceBackground(plane, mode);
2112
+ return { plane, backgroundColor, depthStyle: getPressedRecessShadow(backgroundColor, mode) };
2113
+ }
2114
+ function resolveSurfaceDepth(inherited, props) {
2115
+ const mode = props.theme ?? inherited.mode;
2116
+ const elevation = props.elevation ?? 0;
2117
+ if (props.pressed || elevation < 0) {
2118
+ const down = props.pressed ? 1 : -elevation;
2119
+ return { mode, step: 0, lifted: false, ...recessDepth(inherited, down, mode) };
2120
+ }
2121
+ const step = props.level != null ? 0 : props.raise ?? elevation;
2122
+ const floating = step >= FLOATING_ELEVATION_MIN;
2123
+ const plane = props.level ?? (floating ? "overlay" : raisedLevel(inherited.level, step));
2124
+ const lifted = props.lift ?? step > 0;
2125
+ const liftStep = Math.max(1, Math.min(5, step));
2126
+ const depthStyle = lifted ? liftStyle(liftStep, mode, props.liftOptions) : {};
2127
+ return { mode, plane, backgroundColor: surfaceBackground(plane, mode), depthStyle, step, lifted };
2128
+ }
2129
+ function useResolvedSurface(props) {
2130
+ const inherited = useSurface();
2131
+ const { level, raise, elevation, pressed, lift, liftOptions, theme } = props;
2132
+ return react.useMemo(
2133
+ () => resolveSurfaceDepth(inherited, {
2134
+ level,
2135
+ raise,
2136
+ elevation,
2137
+ pressed,
2138
+ lift,
2139
+ liftOptions,
2140
+ theme
2141
+ }),
2142
+ [inherited, level, raise, elevation, pressed, lift, liftOptions, theme]
2143
+ );
2144
+ }
2145
+
2146
+ // src/components/ui/tooltip/Tooltip.tsx
2147
+ var createPortal;
2148
+ if (reactNative.Platform.OS === "web") {
2149
+ try {
2150
+ createPortal = __require("react-dom").createPortal;
2151
+ } catch {
2152
+ }
2153
+ }
2154
+ function useHoverState() {
2155
+ const [hovered, setHovered] = react.useState(false);
2156
+ const onPointerEnter = react.useCallback(() => setHovered(true), []);
2157
+ const onPointerLeave = react.useCallback(() => setHovered(false), []);
2158
+ return { hovered, hoverProps: { onPointerEnter, onPointerLeave } };
2159
+ }
2160
+ var METRIC_FONT = "Inter, sans-serif";
2161
+ function MetricCell({ children, color, fontSize = 11, weight = 600 }) {
2162
+ return /* @__PURE__ */ jsx(
2163
+ reactNative.Text,
2164
+ {
2165
+ style: {
2166
+ fontFamily: METRIC_FONT,
2167
+ fontSize,
2168
+ color,
2169
+ fontWeight: String(weight),
2170
+ letterSpacing: 1
2171
+ },
2172
+ children
2173
+ }
2174
+ );
2175
+ }
2176
+
2177
+ // src/components/custom/Workout/SetsRepsLoad.tsx
2178
+ var TIMES = "\xD7";
2179
+ var AT = "@";
2180
+ function SetsRepsLoad({
2181
+ sets,
2182
+ reps,
2183
+ load,
2184
+ unit = "lb",
2270
2185
  fontSize,
2186
+ muted = false,
2271
2187
  className,
2272
2188
  ...props
2273
2189
  }) {
2274
- const value = useOnSurfaceColor("primary");
2275
- const sep = useOnSurfaceColor("tertiary");
2190
+ const primary = useOnSurfaceColor("primary");
2191
+ const tertiary = useOnSurfaceColor("tertiary");
2192
+ const secondary = useOnSurfaceColor("secondary");
2193
+ const value = muted ? secondary : primary;
2194
+ const sep = muted ? secondary : tertiary;
2195
+ const weight = muted ? 400 : 600;
2196
+ const times = muted ? TIMES : ` ${TIMES} `;
2276
2197
  return /* @__PURE__ */ jsxs(
2277
2198
  reactNative.View,
2278
2199
  {
@@ -2282,29 +2203,29 @@ function SetsRepsLoad({
2282
2203
  testID: "sets-reps-load",
2283
2204
  ...props,
2284
2205
  children: [
2285
- /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: sets }),
2286
- /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${TIMES} ` }),
2287
- /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: reps }),
2288
- /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${AT} ` }),
2289
- /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: load }),
2290
- /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${unit}` })
2206
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, weight, children: sets }),
2207
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, weight, children: times }),
2208
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, weight, children: reps }),
2209
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, weight, children: ` ${AT} ` }),
2210
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, weight, children: load }),
2211
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, weight, children: ` ${unit}` })
2291
2212
  ]
2292
2213
  }
2293
2214
  );
2294
2215
  }
2295
- var t2 = getSemanticColors("dark");
2216
+ var t = getSemanticColors("dark");
2296
2217
  var INTER = "Inter, sans-serif";
2297
2218
  var TEXT_TERTIARY = "#6B7280";
2298
- var STATUS_ERROR = t2["status-error"];
2299
- var STATUS_SUCCESS = t2["status-success"];
2300
- var STATUS_WARNING = t2["status-warning"];
2219
+ var STATUS_ERROR = t["status-error"];
2220
+ var STATUS_SUCCESS = t["status-success"];
2221
+ var STATUS_WARNING = t["status-warning"];
2301
2222
  var ON_TARGET_MS = 100;
2302
2223
  function getTempoFillPct(elapsedMs, targetMs) {
2303
2224
  if (!targetMs) return 100;
2304
2225
  return Math.min(100, elapsedMs / targetMs * 100);
2305
2226
  }
2306
2227
  function activeNumberTone(elapsedMs, targetMs) {
2307
- if (targetMs == null) return t2["text-primary"];
2228
+ if (targetMs == null) return t["text-primary"];
2308
2229
  const remainingMs = targetMs - elapsedMs;
2309
2230
  if (remainingMs > ON_TARGET_MS) return STATUS_WARNING;
2310
2231
  if (remainingMs >= -ON_TARGET_MS) return STATUS_SUCCESS;
@@ -2456,7 +2377,7 @@ function TempoDisplay({
2456
2377
  style: {
2457
2378
  flexDirection: "row",
2458
2379
  alignItems: "center",
2459
- backgroundColor: t2["surface-raised"],
2380
+ backgroundColor: t["surface-raised"],
2460
2381
  paddingHorizontal: chromePadX,
2461
2382
  paddingVertical: chromePadY,
2462
2383
  borderRadius: chromeRadius
@@ -2471,7 +2392,7 @@ function TempoDisplay({
2471
2392
  fontSize: labelFont,
2472
2393
  fontWeight: "500",
2473
2394
  // Live-muted green while a rep is running, tertiary at rest.
2474
- color: live ? t2["status-live-muted"] : TEXT_TERTIARY,
2395
+ color: live ? t["status-live-muted"] : TEXT_TERTIARY,
2475
2396
  letterSpacing: 0.5,
2476
2397
  textTransform: "uppercase",
2477
2398
  marginRight: Math.round(fontSize * 0.5)
@@ -2529,7 +2450,7 @@ function TempoDisplay({
2529
2450
  reactNative.View,
2530
2451
  {
2531
2452
  style: {
2532
- backgroundColor: t2["surface-overlay"],
2453
+ backgroundColor: t["surface-overlay"],
2533
2454
  borderRadius: 6,
2534
2455
  paddingVertical: 8,
2535
2456
  paddingHorizontal: 12,
@@ -2582,6 +2503,80 @@ function TempoDisplay({
2582
2503
  }
2583
2504
  );
2584
2505
  }
2506
+
2507
+ // src/components/icons/SvgIcon.tsx
2508
+ function SvgIcon({
2509
+ size = 24,
2510
+ color = "currentColor",
2511
+ strokeWidth = 2,
2512
+ fill = "none",
2513
+ title,
2514
+ children
2515
+ }) {
2516
+ return /* @__PURE__ */ jsxs(
2517
+ "svg",
2518
+ {
2519
+ width: size,
2520
+ height: size,
2521
+ viewBox: "0 0 24 24",
2522
+ fill,
2523
+ stroke: color,
2524
+ strokeWidth,
2525
+ strokeLinecap: "round",
2526
+ strokeLinejoin: "round",
2527
+ ...title ? { role: "img", "aria-label": title } : { "aria-hidden": true },
2528
+ children: [
2529
+ title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
2530
+ children
2531
+ ]
2532
+ }
2533
+ );
2534
+ }
2535
+
2536
+ // src/components/icons/icons.tsx
2537
+ function ScaleIcon(props) {
2538
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
2539
+ /* @__PURE__ */ jsx("path", { d: "m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
2540
+ /* @__PURE__ */ jsx("path", { d: "m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z" }),
2541
+ /* @__PURE__ */ jsx("path", { d: "M7 21h10" }),
2542
+ /* @__PURE__ */ jsx("path", { d: "M12 3v18" }),
2543
+ /* @__PURE__ */ jsx("path", { d: "M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2" })
2544
+ ] });
2545
+ }
2546
+ function AlertTriangleIcon(props) {
2547
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
2548
+ /* @__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" }),
2549
+ /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
2550
+ /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
2551
+ ] });
2552
+ }
2553
+ function TrendingDownIcon(props) {
2554
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
2555
+ /* @__PURE__ */ jsx("path", { d: "M16 17h6v-6" }),
2556
+ /* @__PURE__ */ jsx("path", { d: "m22 17-8.5-8.5-5 5L2 7" })
2557
+ ] });
2558
+ }
2559
+ function CircleSlashIcon(props) {
2560
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
2561
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
2562
+ /* @__PURE__ */ jsx("path", { d: "m9 15 6-6" })
2563
+ ] });
2564
+ }
2565
+ function AwardIcon(props) {
2566
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
2567
+ /* @__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" }),
2568
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "6" })
2569
+ ] });
2570
+ }
2571
+ function InfoIcon(props) {
2572
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
2573
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
2574
+ /* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
2575
+ /* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
2576
+ ] });
2577
+ }
2578
+
2579
+ // src/components/custom/Workout/ExerciseIndicator.tsx
2585
2580
  var INDICATOR_CONFIG = {
2586
2581
  imbalance: { Icon: ScaleIcon, token: "status-error", label: "Left/right imbalance" },
2587
2582
  overshoot: { Icon: AlertTriangleIcon, token: "status-error", label: "Load overshoot" },
@@ -2619,42 +2614,101 @@ function ExerciseIndicator({ kind, onPress, className, ...props }) {
2619
2614
  return chip;
2620
2615
  }
2621
2616
 
2617
+ // src/components/custom/Workout/exerciseRowState.ts
2618
+ var ROW_STATE_TOKEN = {
2619
+ hovered: "interactive-hover",
2620
+ selected: "interactive-selected",
2621
+ pressed: "interactive-active"
2622
+ };
2623
+ function exerciseRowStateColor(state, mode = "dark") {
2624
+ return getSemanticColors(mode)[ROW_STATE_TOKEN[state]];
2625
+ }
2626
+ function exerciseLiveColor(mode = "dark") {
2627
+ return getSemanticColors(mode)["status-live"];
2628
+ }
2629
+
2622
2630
  // src/components/custom/Workout/ExerciseHeading.tsx
2623
- function headingLabel(name, sets, reps, load, unit) {
2624
- const loadLabel = typeof load === "number" ? roundWeight(load) : load;
2625
- return `${name}, ${sets}\xD7${reps} @ ${loadLabel} ${unit}`;
2631
+ var DIMMED_OPACITY = 0.55;
2632
+ function prescriptionText(p, unit) {
2633
+ if (p.prescription !== void 0) return p.prescription;
2634
+ if (p.sets === void 0) return void 0;
2635
+ const loadLabel = typeof p.load === "number" ? roundWeight(p.load) : p.load;
2636
+ return `${p.sets}\xD7${p.reps} @ ${loadLabel} ${unit}`;
2637
+ }
2638
+ function exerciseHeadingLabel(p, unit = p.unit ?? "lbs") {
2639
+ const prescription = prescriptionText(p, unit);
2640
+ return prescription ? `${p.name}, ${prescription}` : p.name;
2641
+ }
2642
+ function pressProps(p, unit) {
2643
+ return {
2644
+ onPress: p.onPress,
2645
+ onPressIn: p.onPressIn,
2646
+ onPressOut: p.onPressOut,
2647
+ onHoverIn: p.onHoverIn,
2648
+ onHoverOut: p.onHoverOut,
2649
+ accessibilityRole: "button",
2650
+ accessibilityLabel: exerciseHeadingLabel(p, unit)
2651
+ };
2626
2652
  }
2627
- function ExerciseHeading({
2653
+ function HeadingName({
2628
2654
  name,
2629
- sets,
2630
- reps,
2631
- load,
2632
- unit = "lbs",
2633
- tempo,
2634
- indicator,
2635
- dimmed,
2636
- onPress
2655
+ nameStyle,
2656
+ className
2637
2657
  }) {
2638
- return /* @__PURE__ */ jsxs(reactNative.View, { style: { opacity: dimmed ? 0.55 : 1 }, testID: "exercise-heading", children: [
2658
+ return (
2659
+ // The row is a button label, not a heading, so `boldLabel` carries the weight and
2660
+ // no header role has to be undone. Size and the Space Grotesk face are pinned.
2661
+ /* @__PURE__ */ jsx(
2662
+ Typography,
2663
+ {
2664
+ variant: "boldLabel",
2665
+ color: nameStyle ? "inherit" : "primary",
2666
+ style: nameStyle,
2667
+ className: cn("font-heading text-sm leading-[normal]", className),
2668
+ testID: "exercise-card-name",
2669
+ children: name
2670
+ }
2671
+ )
2672
+ );
2673
+ }
2674
+ function prescriptionNode(p, unit) {
2675
+ if (p.prescription !== void 0) {
2676
+ return /* @__PURE__ */ jsx(
2677
+ Typography,
2678
+ {
2679
+ variant: "caption",
2680
+ color: "secondary",
2681
+ maxLines: 1,
2682
+ className: "font-sans shrink leading-[normal]",
2683
+ testID: "exercise-card-prescription",
2684
+ children: p.prescription
2685
+ }
2686
+ );
2687
+ }
2688
+ if (p.sets === void 0) return null;
2689
+ return /* @__PURE__ */ jsx(
2690
+ SetsRepsLoad,
2691
+ {
2692
+ sets: p.sets,
2693
+ reps: p.reps,
2694
+ load: p.load,
2695
+ unit,
2696
+ muted: p.mutedPrescription
2697
+ }
2698
+ );
2699
+ }
2700
+ function StackedHeading({ heading, nameStyle }) {
2701
+ const { unit = "lbs", tempo, indicator } = heading;
2702
+ return /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
2639
2703
  /* @__PURE__ */ jsxs(
2640
2704
  reactNative.Pressable,
2641
2705
  {
2642
- onPress,
2643
- accessibilityRole: "button",
2644
- accessibilityLabel: headingLabel(name, sets, reps, load, unit),
2706
+ ...pressProps(heading, unit),
2645
2707
  className: "flex-row items-center",
2646
2708
  style: { gap: 8 },
2647
2709
  testID: "exercise-card-header",
2648
2710
  children: [
2649
- /* @__PURE__ */ jsx(
2650
- Typography,
2651
- {
2652
- variant: "boldLabel",
2653
- className: "font-heading text-sm leading-[normal]",
2654
- testID: "exercise-card-name",
2655
- children: name
2656
- }
2657
- ),
2711
+ /* @__PURE__ */ jsx(HeadingName, { name: heading.name, nameStyle }),
2658
2712
  /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
2659
2713
  indicator && /* @__PURE__ */ jsx(ExerciseIndicator, { kind: indicator })
2660
2714
  ]
@@ -2667,7 +2721,7 @@ function ExerciseHeading({
2667
2721
  style: { gap: 8, marginTop: 1 },
2668
2722
  testID: "exercise-card-summary",
2669
2723
  children: [
2670
- /* @__PURE__ */ jsx(SetsRepsLoad, { sets, reps, load, unit }),
2724
+ prescriptionNode(heading, unit),
2671
2725
  /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
2672
2726
  tempo && /* @__PURE__ */ jsx(TempoDisplay, { tempo, size: "sm", showLabel: false, showInfo: false })
2673
2727
  ]
@@ -2675,9 +2729,44 @@ function ExerciseHeading({
2675
2729
  )
2676
2730
  ] });
2677
2731
  }
2732
+ function InlineHeading({ heading, nameStyle }) {
2733
+ const { unit = "lbs", indicator, previousBest } = heading;
2734
+ const Row = heading.pressTarget === "ancestor" ? reactNative.View : reactNative.Pressable;
2735
+ return /* @__PURE__ */ jsxs(
2736
+ Row,
2737
+ {
2738
+ ...heading.pressTarget === "ancestor" ? {} : pressProps(heading, unit),
2739
+ className: "flex-row items-center",
2740
+ style: { gap: 8 },
2741
+ testID: "exercise-card-header",
2742
+ children: [
2743
+ /* @__PURE__ */ jsx(HeadingName, { name: heading.name, nameStyle, className: "shrink-0" }),
2744
+ /* @__PURE__ */ jsx(reactNative.View, { className: "shrink flex-row items-center", testID: "exercise-card-summary", children: prescriptionNode(heading, unit) }),
2745
+ /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", style: { minWidth: 8 } }),
2746
+ indicator && /* @__PURE__ */ jsx(ExerciseIndicator, { kind: indicator }),
2747
+ previousBest && /* @__PURE__ */ jsx(
2748
+ Typography,
2749
+ {
2750
+ variant: "caption",
2751
+ color: "tertiary",
2752
+ maxLines: 1,
2753
+ className: "font-sans shrink text-2xs leading-[normal]",
2754
+ testID: "exercise-card-previous-best",
2755
+ children: previousBest
2756
+ }
2757
+ )
2758
+ ]
2759
+ }
2760
+ );
2761
+ }
2762
+ function ExerciseHeading(props) {
2763
+ const mode = useSurfaceMode();
2764
+ const nameStyle = props.isLive ? { color: exerciseLiveColor(mode) } : void 0;
2765
+ return /* @__PURE__ */ jsx(reactNative.View, { style: { opacity: props.dimmed ? DIMMED_OPACITY : 1 }, testID: "exercise-heading", children: props.layout === "inline" ? /* @__PURE__ */ jsx(InlineHeading, { heading: props, nameStyle }) : /* @__PURE__ */ jsx(StackedHeading, { heading: props, nameStyle }) });
2766
+ }
2678
2767
  var PULSE_HALF_MS = 950;
2679
2768
  var PULSE_MIN_OPACITY = 0.45;
2680
- var SEGMENTED_BAR_GAP = 5;
2769
+ var SEGMENTED_BAR_GAP = SET_LEVEL_FLAT_BAR.gap;
2681
2770
  function usePulse(active) {
2682
2771
  const [value] = react.useState(() => new reactNative.Animated.Value(0));
2683
2772
  react.useEffect(() => {
@@ -2705,9 +2794,9 @@ function usePulse(active) {
2705
2794
  }
2706
2795
  function SegmentedBar({
2707
2796
  segments,
2708
- height = 8,
2797
+ height = SET_LEVEL_FLAT_BAR.height,
2709
2798
  gap = SEGMENTED_BAR_GAP,
2710
- radius = 2,
2799
+ radius = SET_LEVEL_FLAT_BAR.radius,
2711
2800
  marker = null,
2712
2801
  segmentTestID,
2713
2802
  style,
@@ -2933,20 +3022,113 @@ function SetStrip({ sets, height = 8, className, ...props }) {
2933
3022
  }
2934
3023
 
2935
3024
  // src/components/custom/Workout/ExerciseCardHeading.tsx
2936
- function ExerciseCardHeading({
2937
- setStates,
2938
- stripHeight = 8,
2939
- dimmed,
2940
- testID = "exercise-card",
2941
- ...heading
2942
- }) {
3025
+ var DENSITY = {
3026
+ rail: {
3027
+ paddingVertical: 9,
3028
+ paddingHorizontal: 12,
3029
+ layout: "stacked",
3030
+ dimOpacity: 0.55,
3031
+ dimByDefault: false,
3032
+ mutedPrescription: false
3033
+ },
3034
+ compact: {
3035
+ paddingVertical: 12,
3036
+ paddingHorizontal: 14,
3037
+ layout: "inline",
3038
+ dimOpacity: 0.55,
3039
+ dimByDefault: false,
3040
+ mutedPrescription: true
3041
+ },
3042
+ upcoming: {
3043
+ paddingVertical: 12,
3044
+ paddingHorizontal: 14,
3045
+ layout: "inline",
3046
+ dimOpacity: 0.6,
3047
+ dimByDefault: true,
3048
+ mutedPrescription: true
3049
+ }
3050
+ };
3051
+ function useRowInteraction() {
3052
+ const { hovered: rowHovered, hoverProps } = useHoverState();
3053
+ const [headingHovered, setHeadingHovered] = react.useState(false);
3054
+ const [pressed, setPressed] = react.useState(false);
3055
+ return {
3056
+ hoverProps,
3057
+ pressed,
3058
+ hovered: rowHovered || headingHovered,
3059
+ headingHandlers: {
3060
+ onHoverIn: () => setHeadingHovered(true),
3061
+ onHoverOut: () => setHeadingHovered(false),
3062
+ onPressIn: () => setPressed(true),
3063
+ onPressOut: () => setPressed(false)
3064
+ }
3065
+ };
3066
+ }
3067
+ function rowWash(mode, state) {
3068
+ if (state.pressed) return exerciseRowStateColor("pressed", mode);
3069
+ if (state.isSelected) return exerciseRowStateColor("selected", mode);
3070
+ if (state.hovered) return exerciseRowStateColor("hovered", mode);
3071
+ return void 0;
3072
+ }
3073
+ function prescriptionProps(p) {
3074
+ const base = { name: p.name };
3075
+ if (p.prescription !== void 0) return { ...base, prescription: p.prescription };
3076
+ if (p.sets === void 0) return base;
3077
+ return { ...base, sets: p.sets, reps: p.reps, load: p.load };
3078
+ }
3079
+ function ExerciseCardHeading(props) {
3080
+ const {
3081
+ density = "rail",
3082
+ setStates = [],
3083
+ stripHeight = 8,
3084
+ dimmed,
3085
+ isSelected = false,
3086
+ isLive = false,
3087
+ testID = "exercise-card",
3088
+ style
3089
+ } = props;
3090
+ const spec = DENSITY[density];
3091
+ const { hoverProps, pressed, hovered, headingHandlers } = useRowInteraction();
3092
+ const wash = rowWash(useSurfaceMode(), { pressed, isSelected, hovered });
3093
+ const rowIsButton = spec.layout === "inline";
3094
+ const Root = rowIsButton ? reactNative.Pressable : reactNative.View;
3095
+ const rootPress = rowIsButton ? {
3096
+ ...headingHandlers,
3097
+ onPress: props.onPress,
3098
+ accessibilityRole: "button",
3099
+ accessibilityLabel: exerciseHeadingLabel(props)
3100
+ } : {};
2943
3101
  return /* @__PURE__ */ jsxs(
2944
- reactNative.View,
3102
+ Root,
2945
3103
  {
2946
- style: { paddingVertical: 9, paddingHorizontal: 12, opacity: dimmed ? 0.55 : 1 },
3104
+ ...hoverProps,
3105
+ ...rootPress,
3106
+ style: [
3107
+ {
3108
+ paddingVertical: spec.paddingVertical,
3109
+ paddingHorizontal: spec.paddingHorizontal,
3110
+ opacity: dimmed ?? spec.dimByDefault ? spec.dimOpacity : 1
3111
+ },
3112
+ wash ? { backgroundColor: wash } : null,
3113
+ style
3114
+ ],
2947
3115
  testID,
2948
3116
  children: [
2949
- /* @__PURE__ */ jsx(ExerciseHeading, { ...heading }),
3117
+ /* @__PURE__ */ jsx(
3118
+ ExerciseHeading,
3119
+ {
3120
+ ...prescriptionProps(props),
3121
+ ...rowIsButton ? { pressTarget: "ancestor" } : headingHandlers,
3122
+ unit: props.unit,
3123
+ tempo: props.tempo,
3124
+ indicator: props.indicator,
3125
+ previousBest: props.previousBest,
3126
+ onPress: rowIsButton ? void 0 : props.onPress,
3127
+ layout: spec.layout,
3128
+ mutedPrescription: spec.mutedPrescription,
3129
+ isLive
3130
+ }
3131
+ ),
2950
3132
  setStates.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { marginTop: 7 }, testID: "exercise-card-strip", children: /* @__PURE__ */ jsx(SetStrip, { sets: setStates, height: stripHeight }) })
2951
3133
  ]
2952
3134
  }
@@ -2992,103 +3174,58 @@ function getSupersetMargin(position) {
2992
3174
  }
2993
3175
  return {};
2994
3176
  }
2995
- function formatSummary(summary) {
2996
- if (!summary) return "";
2997
- return `${summary.sets}\xD7${summary.reps} @ ${roundWeight(summary.weight)} ${summary.unit}`;
3177
+ function supersetChrome(position) {
3178
+ return { ...getSupersetBorderRadius(position), ...getSupersetMargin(position) };
2998
3179
  }
2999
- function formatAccessibilityLabel(name, summary, prescription) {
3000
- if (summary) return `${name}, ${formatSummary(summary)}`;
3001
- if (prescription) return `${name}, ${prescription}`;
3002
- return name;
3180
+ function deriveCollapsedSetStates(setVelocities, totalPlannedSets, plannedReps2) {
3181
+ const done = (setVelocities ?? []).map(
3182
+ (velocities) => ({ status: "done", velocities })
3183
+ );
3184
+ const remaining = Math.max(0, (totalPlannedSets ?? 0) - done.length);
3185
+ return [
3186
+ ...done,
3187
+ ...Array.from(
3188
+ { length: remaining },
3189
+ () => ({ status: "todo", planned: plannedReps2 })
3190
+ )
3191
+ ];
3003
3192
  }
3004
3193
  function CollapsedCard({
3005
3194
  name,
3006
3195
  onToggle,
3007
3196
  summary,
3008
3197
  isPR,
3198
+ indicator,
3009
3199
  setVelocities,
3010
- velocityZones,
3200
+ setStates,
3011
3201
  totalPlannedSets,
3012
- supersetPosition
3202
+ stripHeight,
3203
+ supersetPosition,
3204
+ isSelected,
3205
+ isLive
3013
3206
  }) {
3014
- const [pressed, setPressed] = react.useState(false);
3015
- const completedSets = setVelocities?.length ?? 0;
3016
- const remaining = Math.max(0, (totalPlannedSets ?? 0) - completedSets);
3017
- const borderRadius = getSupersetBorderRadius(supersetPosition);
3018
- const supersetMargin = getSupersetMargin(supersetPosition);
3019
- return /* @__PURE__ */ jsx(
3020
- reactNative.Pressable,
3207
+ const plannedReps2 = typeof summary?.reps === "number" ? summary.reps : 0;
3208
+ const row = {
3209
+ density: "compact",
3210
+ name,
3211
+ indicator: indicator ?? (isPR ? "pr" : void 0),
3212
+ setStates: setStates ?? deriveCollapsedSetStates(setVelocities, totalPlannedSets, plannedReps2),
3213
+ stripHeight,
3214
+ isSelected,
3215
+ isLive,
3216
+ onPress: onToggle,
3217
+ style: supersetChrome(supersetPosition)
3218
+ };
3219
+ return summary ? /* @__PURE__ */ jsx(
3220
+ ExerciseCardHeading,
3021
3221
  {
3022
- onPress: onToggle,
3023
- onPressIn: () => setPressed(true),
3024
- onPressOut: () => setPressed(false),
3025
- accessibilityRole: "button",
3026
- accessibilityLabel: formatAccessibilityLabel(name, summary, void 0),
3027
- testID: "exercise-card",
3028
- children: /* @__PURE__ */ jsxs(
3029
- reactNative.View,
3030
- {
3031
- className: pressed ? "bg-surface-raised" : void 0,
3032
- style: {
3033
- padding: 12,
3034
- paddingHorizontal: 14,
3035
- cursor: "pointer",
3036
- ...borderRadius,
3037
- ...supersetMargin
3038
- },
3039
- children: [
3040
- /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", testID: "exercise-card-header", children: [
3041
- /* @__PURE__ */ jsx(
3042
- Typography,
3043
- {
3044
- variant: "h6",
3045
- color: "primary",
3046
- className: "text-sm font-bold leading-[normal]",
3047
- testID: "exercise-card-name",
3048
- children: name
3049
- }
3050
- ),
3051
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1" }),
3052
- summary && /* The demo's "body" face is Inter, which is the library's `font-sans`
3053
- (the mapping B1 established for PrBadge), not Typography's `font-body`. */
3054
- /* @__PURE__ */ jsx(
3055
- Typography,
3056
- {
3057
- variant: "caption",
3058
- color: "secondary",
3059
- className: "font-sans mr-2 leading-[normal]",
3060
- testID: "exercise-card-summary",
3061
- children: formatSummary(summary)
3062
- }
3063
- ),
3064
- isPR && /* @__PURE__ */ jsx(reactNative.View, { style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx(PrBadge, { type: "e1rm", compact: true, animate: false }) })
3065
- ] }),
3066
- (completedSets > 0 || remaining > 0) && /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row", style: { marginTop: 6, gap: 4 }, testID: "exercise-card-strips", children: [
3067
- setVelocities?.map((velocities, i) => /* @__PURE__ */ jsx(
3068
- VelocityStrip,
3069
- {
3070
- velocities,
3071
- zones: velocityZones,
3072
- variant: "compact",
3073
- height: 8,
3074
- hideBaseline: true,
3075
- testID: `exercise-card-velocity-strip-${i}`
3076
- },
3077
- i
3078
- )),
3079
- Array.from({ length: remaining }, (_, i) => /* @__PURE__ */ jsx(
3080
- PlaceholderStrip,
3081
- {
3082
- testID: `exercise-card-placeholder-${i}`
3083
- },
3084
- `placeholder-${i}`
3085
- ))
3086
- ] })
3087
- ]
3088
- }
3089
- )
3222
+ ...row,
3223
+ sets: summary.sets,
3224
+ reps: summary.reps,
3225
+ load: summary.weight,
3226
+ unit: summary.unit
3090
3227
  }
3091
- );
3228
+ ) : /* @__PURE__ */ jsx(ExerciseCardHeading, { ...row });
3092
3229
  }
3093
3230
  function deriveHeaderSetStates(sets) {
3094
3231
  return sets.map((set) => {
@@ -3113,10 +3250,9 @@ function ExpandedCard({
3113
3250
  setStates,
3114
3251
  stripHeight = 8,
3115
3252
  velocityZones,
3116
- supersetPosition
3253
+ supersetPosition,
3254
+ isLive
3117
3255
  }) {
3118
- const borderRadius = getSupersetBorderRadius(supersetPosition);
3119
- const supersetMargin = getSupersetMargin(supersetPosition);
3120
3256
  const unit = summary?.unit ?? sets?.[0]?.unit ?? "lbs";
3121
3257
  const headerStates = setStates ?? (sets ? deriveHeaderSetStates(sets) : []);
3122
3258
  const headerIndicator = indicator ?? (isPR ? "pr" : void 0);
@@ -3124,11 +3260,7 @@ function ExpandedCard({
3124
3260
  reactNative.View,
3125
3261
  {
3126
3262
  className: "bg-surface-elevated border-hairline",
3127
- style: {
3128
- borderWidth: 1,
3129
- ...borderRadius,
3130
- ...supersetMargin
3131
- },
3263
+ style: { borderWidth: 1, ...supersetChrome(supersetPosition) },
3132
3264
  testID: "exercise-card",
3133
3265
  children: [
3134
3266
  /* @__PURE__ */ jsx(
@@ -3143,6 +3275,7 @@ function ExpandedCard({
3143
3275
  indicator: headerIndicator,
3144
3276
  setStates: headerStates,
3145
3277
  stripHeight,
3278
+ isLive,
3146
3279
  onPress: onToggle,
3147
3280
  testID: "exercise-card-heading"
3148
3281
  }
@@ -3170,62 +3303,21 @@ function UpcomingCard({
3170
3303
  name,
3171
3304
  prescription,
3172
3305
  previousBest,
3306
+ indicator,
3173
3307
  supersetPosition,
3308
+ isSelected,
3174
3309
  onToggle
3175
3310
  }) {
3176
- const borderRadius = getSupersetBorderRadius(supersetPosition);
3177
- const supersetMargin = getSupersetMargin(supersetPosition);
3178
- return /* @__PURE__ */ jsx(reactNative.Pressable, { onPress: onToggle, accessibilityRole: "button", children: /* @__PURE__ */ jsx(
3179
- reactNative.View,
3180
- {
3181
- style: {
3182
- opacity: 0.6,
3183
- padding: 12,
3184
- paddingHorizontal: 14,
3185
- ...borderRadius,
3186
- ...supersetMargin
3187
- },
3188
- accessibilityLabel: formatAccessibilityLabel(name, void 0, prescription),
3189
- testID: "exercise-card",
3190
- children: /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", children: [
3191
- /* @__PURE__ */ jsx(
3192
- Typography,
3193
- {
3194
- variant: "h6",
3195
- color: "primary",
3196
- className: "shrink-0 text-sm font-bold leading-[normal]",
3197
- testID: "exercise-card-name",
3198
- children: name
3199
- }
3200
- ),
3201
- prescription && /* @__PURE__ */ jsx(
3202
- Typography,
3203
- {
3204
- variant: "caption",
3205
- color: "secondary",
3206
- maxLines: 1,
3207
- className: "font-sans shrink ml-2 leading-[normal]",
3208
- testID: "exercise-card-prescription",
3209
- children: prescription
3210
- }
3211
- ),
3212
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", style: { minWidth: 8 } }),
3213
- previousBest && /* 11px is off the type scale; `2xs` (10px) is the step below, as B2's
3214
- WorkoutCard date took for the same size. */
3215
- /* @__PURE__ */ jsx(
3216
- Typography,
3217
- {
3218
- variant: "caption",
3219
- color: "tertiary",
3220
- maxLines: 1,
3221
- className: "font-sans shrink text-2xs leading-[normal]",
3222
- testID: "exercise-card-previous-best",
3223
- children: previousBest
3224
- }
3225
- )
3226
- ] })
3227
- }
3228
- ) });
3311
+ const row = {
3312
+ density: "upcoming",
3313
+ name,
3314
+ previousBest,
3315
+ indicator,
3316
+ isSelected,
3317
+ onPress: onToggle,
3318
+ style: supersetChrome(supersetPosition)
3319
+ };
3320
+ return prescription !== void 0 ? /* @__PURE__ */ jsx(ExerciseCardHeading, { ...row, prescription }) : /* @__PURE__ */ jsx(ExerciseCardHeading, { ...row });
3229
3321
  }
3230
3322
  function ExerciseCard({
3231
3323
  upcoming,
@@ -3245,7 +3337,9 @@ function ExerciseCard({
3245
3337
  if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
3246
3338
  return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
3247
3339
  }
3248
- var BRAND_PRIMARY = getSemanticColors("dark")["brand-primary"];
3340
+ var t2 = getSemanticColors("dark");
3341
+ var BRAND_PRIMARY = t2["brand-primary"];
3342
+ var ON_BRAND_PRIMARY = t2["on-brand-primary"];
3249
3343
  var INPUT_CLASSNAME = "bg-surface-raised border-hairline-strong text-text-primary";
3250
3344
  var inputStyle = {
3251
3345
  fontSize: 14,
@@ -3390,7 +3484,7 @@ function InputBar({
3390
3484
  fontSize: 13,
3391
3485
  fontWeight: "700",
3392
3486
  fontFamily: "Inter, sans-serif",
3393
- color: "#FFFFFF"
3487
+ color: ON_BRAND_PRIMARY
3394
3488
  },
3395
3489
  children: "Record"
3396
3490
  }
@@ -3617,7 +3711,7 @@ function RestActions({ onAddTime, onSkip }) {
3617
3711
  {
3618
3712
  onPress: onAddTime,
3619
3713
  style: {
3620
- backgroundColor: "rgba(255,255,255,0.06)",
3714
+ backgroundColor: alpha(primitiveColors.white, 0.06),
3621
3715
  paddingVertical: 8,
3622
3716
  paddingHorizontal: 20,
3623
3717
  borderRadius: 8
@@ -3640,7 +3734,7 @@ function RestActions({ onAddTime, onSkip }) {
3640
3734
  {
3641
3735
  onPress: onSkip,
3642
3736
  style: {
3643
- backgroundColor: "rgba(255,121,0,0.12)",
3737
+ backgroundColor: alpha(BRAND_PRIMARY2, 0.12),
3644
3738
  paddingVertical: 8,
3645
3739
  paddingHorizontal: 20,
3646
3740
  borderRadius: 8
@@ -4171,128 +4265,6 @@ function ActiveWorkoutPage({
4171
4265
  }
4172
4266
  );
4173
4267
  }
4174
- var FLOATING_LIFT_MIN = 4;
4175
- var LIFT_RIM_ALPHA = { dark: 0.12, light: 0.9 };
4176
- var AMBIENT = {
4177
- 1: [
4178
- { y: 1, blur: 2, alpha: 0.35 },
4179
- { y: 2, blur: 6, alpha: 0.3 }
4180
- ],
4181
- 2: [
4182
- { y: 1, blur: 2, alpha: 0.35 },
4183
- { y: 4, blur: 12, alpha: 0.32 }
4184
- ],
4185
- 3: [
4186
- { y: 2, blur: 4, alpha: 0.35 },
4187
- { y: 8, blur: 22, alpha: 0.35 }
4188
- ],
4189
- 4: [
4190
- { y: 1, blur: 2, alpha: 0.35 },
4191
- { y: 10, blur: 24, alpha: 0.3 },
4192
- { y: 24, blur: 48, alpha: 0.2 }
4193
- ],
4194
- 5: [
4195
- { y: 2, blur: 4, alpha: 0.4 },
4196
- { y: 16, blur: 32, alpha: 0.35 },
4197
- { y: 32, blur: 64, alpha: 0.25 }
4198
- ]
4199
- };
4200
- var AMBIENT_ALPHA_SCALE = { dark: 1, light: 0.4 };
4201
- function ambientLayers(step, mode) {
4202
- const scale = AMBIENT_ALPHA_SCALE[mode];
4203
- return AMBIENT[step].map(
4204
- ({ y, blur, alpha: alpha2 }) => `0 ${y}px ${blur}px rgba(0,0,0,${(alpha2 * scale).toFixed(2)})`
4205
- );
4206
- }
4207
- function liftShadow(step, mode = "dark", opts = {}) {
4208
- const rim = opts.rim ?? LIFT_RIM_ALPHA[mode];
4209
- const layers = ambientLayers(step, mode);
4210
- if (rim > 0) layers.unshift(`inset 0 1px 0 rgba(255,255,255,${rim.toFixed(2)})`);
4211
- return layers.join(", ");
4212
- }
4213
- function liftStyle(step, mode = "dark", opts = {}) {
4214
- const deepest = AMBIENT[step][AMBIENT[step].length - 1];
4215
- return reactNative.Platform.select({
4216
- web: { boxShadow: liftShadow(step, mode, opts) },
4217
- default: {
4218
- shadowColor: "#000",
4219
- shadowOffset: { width: 0, height: Math.round(deepest.y / 2) },
4220
- shadowOpacity: deepest.alpha * AMBIENT_ALPHA_SCALE[mode],
4221
- shadowRadius: deepest.blur / 2,
4222
- elevation: step
4223
- }
4224
- });
4225
- }
4226
- var FLOATING_ELEVATION_MIN = FLOATING_LIFT_MIN;
4227
- function getPressedRecessShadow(fillColor, _mode = "dark") {
4228
- const { boxShadow } = insetWell(fillColor);
4229
- return reactNative.Platform.select({
4230
- web: { boxShadow },
4231
- default: {}
4232
- });
4233
- }
4234
- var glowConfig = {
4235
- tight: { blur: 4, spread: 0, opacity: 0.4 },
4236
- subtle: { blur: 12, spread: 0, opacity: 0.25 },
4237
- medium: { blur: 20, spread: 2, opacity: 0.4 },
4238
- strong: { blur: 30, spread: 4, opacity: 0.55 }
4239
- };
4240
- function getGlowShadow(color, intensity = "medium") {
4241
- const config = glowConfig[intensity];
4242
- const rgb = hexToRgb(color);
4243
- if (!rgb) return {};
4244
- return reactNative.Platform.select({
4245
- web: {
4246
- boxShadow: `0 0 ${config.blur}px ${config.spread}px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${config.opacity})`
4247
- },
4248
- default: {
4249
- shadowColor: color,
4250
- shadowOffset: { width: 0, height: 0 },
4251
- shadowOpacity: config.opacity,
4252
- shadowRadius: config.blur / 2,
4253
- elevation: 0
4254
- }
4255
- });
4256
- }
4257
- function recessDepth(inherited, planesDown, mode) {
4258
- let plane = inherited.level;
4259
- for (let i = 0; i < planesDown; i++) plane = pressedLevel(plane);
4260
- const backgroundColor = surfaceBackground(plane, mode);
4261
- return { plane, backgroundColor, depthStyle: getPressedRecessShadow(backgroundColor, mode) };
4262
- }
4263
- function resolveSurfaceDepth(inherited, props) {
4264
- const mode = props.theme ?? inherited.mode;
4265
- const elevation = props.elevation ?? 0;
4266
- if (props.pressed || elevation < 0) {
4267
- const down = props.pressed ? 1 : -elevation;
4268
- return { mode, step: 0, lifted: false, ...recessDepth(inherited, down, mode) };
4269
- }
4270
- const step = props.level != null ? 0 : props.raise ?? elevation;
4271
- const floating = step >= FLOATING_ELEVATION_MIN;
4272
- const plane = props.level ?? (floating ? "overlay" : raisedLevel(inherited.level, step));
4273
- const lifted = props.lift ?? step > 0;
4274
- const liftStep = Math.max(1, Math.min(5, step));
4275
- const depthStyle = lifted ? liftStyle(liftStep, mode, props.liftOptions) : {};
4276
- return { mode, plane, backgroundColor: surfaceBackground(plane, mode), depthStyle, step, lifted };
4277
- }
4278
- function useResolvedSurface(props) {
4279
- const inherited = useSurface();
4280
- const { level, raise, elevation, pressed, lift, liftOptions, theme } = props;
4281
- return react.useMemo(
4282
- () => resolveSurfaceDepth(inherited, {
4283
- level,
4284
- raise,
4285
- elevation,
4286
- pressed,
4287
- lift,
4288
- liftOptions,
4289
- theme
4290
- }),
4291
- [inherited, level, raise, elevation, pressed, lift, liftOptions, theme]
4292
- );
4293
- }
4294
-
4295
- // src/components/ui/card/Card.tsx
4296
4268
  var EDGE_CLASS = {
4297
4269
  elevated: "",
4298
4270
  accent: "",
@@ -4572,11 +4544,11 @@ var WARNING = t3["status-warning"];
4572
4544
  var ERROR = t3["status-error"];
4573
4545
  var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY4, BRAND_PRIMARY_LIGHT];
4574
4546
  var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
4575
- 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%)";
4547
+ var GAUGE_GRADIENT = `linear-gradient(90deg, ${alpha(SUCCESS, 0.25)} 0%, ${alpha(WARNING, 0.25)} 50%, ${alpha(ERROR, 0.25)} 100%)`;
4576
4548
  var STATUS_VARIANTS = {
4577
- success: { bg: "rgba(46,213,115,0.15)", border: "rgba(46,213,115,0.3)", text: SUCCESS },
4578
- warning: { bg: "rgba(249,180,21,0.15)", border: "rgba(249,180,21,0.3)", text: WARNING },
4579
- error: { bg: "rgba(209,67,67,0.15)", border: "rgba(209,67,67,0.25)", text: ERROR }
4549
+ success: { bg: alpha(SUCCESS, 0.15), border: alpha(SUCCESS, 0.3), text: SUCCESS },
4550
+ warning: { bg: alpha(WARNING, 0.15), border: alpha(WARNING, 0.3), text: WARNING },
4551
+ error: { bg: alpha(ERROR, 0.15), border: alpha(ERROR, 0.25), text: ERROR }
4580
4552
  };
4581
4553
  function clamp01(value) {
4582
4554
  if (!Number.isFinite(value)) return 0;
@@ -4729,7 +4701,7 @@ function Gauge({ gauge }) {
4729
4701
  bottom: 0,
4730
4702
  width: 1,
4731
4703
  marginLeft: -0.5,
4732
- backgroundColor: "rgba(255,255,255,0.3)"
4704
+ backgroundColor: alpha(primitiveColors.white, 0.3)
4733
4705
  },
4734
4706
  testID: "meso-status-card-gauge-center"
4735
4707
  }
@@ -4838,9 +4810,9 @@ function MesoStatusCard({
4838
4810
  reactNative.View,
4839
4811
  {
4840
4812
  style: {
4841
- backgroundColor: "rgba(249,180,21,0.06)",
4813
+ backgroundColor: alpha(WARNING, 0.06),
4842
4814
  borderWidth: 1,
4843
- borderColor: "rgba(249,180,21,0.15)",
4815
+ borderColor: alpha(WARNING, 0.15),
4844
4816
  borderRadius: 8,
4845
4817
  paddingVertical: 10,
4846
4818
  paddingHorizontal: 12
@@ -4866,9 +4838,9 @@ function MesoStatusCard({
4866
4838
  className: "flex-row items-center",
4867
4839
  style: {
4868
4840
  gap: 8,
4869
- backgroundColor: "rgba(46,213,115,0.06)",
4841
+ backgroundColor: alpha(SUCCESS, 0.06),
4870
4842
  borderWidth: 1,
4871
- borderColor: "rgba(46,213,115,0.2)",
4843
+ borderColor: alpha(SUCCESS, 0.2),
4872
4844
  borderRadius: 8,
4873
4845
  paddingVertical: 10,
4874
4846
  paddingHorizontal: 12
@@ -4903,11 +4875,11 @@ var PROJECTION_LINE_COLOR = resolveColor("text-tertiary");
4903
4875
  var STATUS_SUCCESS2 = sem["status-success"];
4904
4876
  var STATUS_ERROR2 = sem["status-error"];
4905
4877
  var STATUS_WARNING2 = sem["status-warning"];
4906
- var GRID_LINE = "rgba(255,255,255,0.06)";
4907
- var SUCCESS_PILL_BG = "rgba(46,213,115,0.10)";
4908
- var SUCCESS_PILL_BORDER = "rgba(46,213,115,0.20)";
4909
- var ERROR_PILL_BG = "rgba(209,67,67,0.10)";
4910
- var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
4878
+ var GRID_LINE = alpha(primitiveColors.white, 0.06);
4879
+ var SUCCESS_PILL_BG = alpha(STATUS_SUCCESS2, 0.1);
4880
+ var SUCCESS_PILL_BORDER = alpha(STATUS_SUCCESS2, 0.2);
4881
+ var ERROR_PILL_BG = alpha(STATUS_ERROR2, 0.1);
4882
+ var ERROR_PILL_BORDER = alpha(STATUS_ERROR2, 0.2);
4911
4883
  var PLOT_LEFT = 26;
4912
4884
  var TOOLTIP_WIDTH = 124;
4913
4885
  function timeOf(dateStr) {
@@ -5152,7 +5124,7 @@ function StrengthTrendChart({
5152
5124
  height,
5153
5125
  borderLeftWidth: 1,
5154
5126
  borderStyle: "dashed",
5155
- borderLeftColor: "rgba(255,255,255,0.10)"
5127
+ borderLeftColor: alpha(primitiveColors.white, 0.1)
5156
5128
  }
5157
5129
  },
5158
5130
  `boundary-${i}`
@@ -5425,9 +5397,9 @@ var STATUS_SUCCESS3 = t4["status-success"];
5425
5397
  var STATUS_WARNING3 = t4["status-warning"];
5426
5398
  var STATUS_INFO = t4["status-info"];
5427
5399
  var DOT_BORDER = "#F3F4F6";
5428
- var BAND_FILL = "rgba(46,213,115,0.1)";
5429
- var BAND_EDGE = "rgba(46,213,115,0.45)";
5430
- var PROJECTION_FILL = "rgba(46,213,115,0.05)";
5400
+ var BAND_FILL = alpha(STATUS_SUCCESS3, 0.1);
5401
+ var BAND_EDGE = alpha(STATUS_SUCCESS3, 0.45);
5402
+ var PROJECTION_FILL = alpha(STATUS_SUCCESS3, 0.05);
5431
5403
  var PADDING_LEFT = 28;
5432
5404
  var PADDING_RIGHT = 10;
5433
5405
  var PADDING_TOP = 8;
@@ -6201,9 +6173,9 @@ function ExerciseDetailPage({
6201
6173
  paddingVertical: 4,
6202
6174
  paddingHorizontal: 10,
6203
6175
  borderRadius: 6,
6204
- backgroundColor: "rgba(255,121,0,0.10)",
6176
+ backgroundColor: alpha(BRAND_PRIMARY6, 0.1),
6205
6177
  borderWidth: 1,
6206
- borderColor: "rgba(255,121,0,0.25)"
6178
+ borderColor: alpha(BRAND_PRIMARY6, 0.25)
6207
6179
  },
6208
6180
  testID: "exercise-detail-page-e1rm",
6209
6181
  children: [
@@ -6506,13 +6478,16 @@ var toneStyles = {
6506
6478
  // host is, and an alpha fill composites by the same amount on all of them.
6507
6479
  // A `surface-*` fill would vanish on the plane it names. Borderless like the
6508
6480
  // six coloured tones — the fill carries the capsule, not a ring.
6481
+ // Label comes from `on-*-subtle`, not the base tone token: the base is tuned to
6482
+ // carry a white label as a solid fill, which left error and accent unreadable
6483
+ // here (AW-133). The on-subtle tokens level the family at one weight.
6509
6484
  neutral: "bg-hairline-subtle border-transparent text-text-secondary",
6510
- brand: "bg-brand-primary-subtle border-transparent text-brand-primary",
6511
- "brand-secondary": "bg-brand-secondary-subtle border-transparent text-brand-secondary",
6512
- success: "bg-status-success-subtle border-transparent text-status-success",
6513
- warning: "bg-status-warning-subtle border-transparent text-status-warning",
6514
- error: "bg-status-error-subtle border-transparent text-status-error",
6515
- info: "bg-status-info-subtle border-transparent text-status-info"
6485
+ brand: "bg-brand-primary-subtle border-transparent text-on-brand-primary-subtle",
6486
+ "brand-secondary": "bg-brand-secondary-subtle border-transparent text-on-brand-secondary-subtle",
6487
+ success: "bg-status-success-subtle border-transparent text-on-status-success-subtle",
6488
+ warning: "bg-status-warning-subtle border-transparent text-on-status-warning-subtle",
6489
+ error: "bg-status-error-subtle border-transparent text-on-status-error-subtle",
6490
+ info: "bg-status-info-subtle border-transparent text-on-status-info-subtle"
6516
6491
  },
6517
6492
  outline: {
6518
6493
  neutral: "border-hairline text-text-secondary",
@@ -6826,7 +6801,7 @@ function WorkoutPill({
6826
6801
  var t6 = getSemanticColors("dark");
6827
6802
  var TRACK_BG = "#333333";
6828
6803
  var LABEL_COLOR = "#6B7280";
6829
- var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
6804
+ var TARGET_LINE_COLOR = alpha(t6["status-info"], 0.5);
6830
6805
  var AT_TARGET_GLOW = getGlowShadow(t6["status-info"], "subtle");
6831
6806
  var ZONE = {
6832
6807
  building: t6["status-success"],
@@ -7108,7 +7083,7 @@ var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY7, BRAND_PRIMARY_LIGHT2];
7108
7083
  function heatmapColor(percentage) {
7109
7084
  const clamped = Math.max(0, Math.min(100, percentage));
7110
7085
  const opacity = 0.12 + clamped / 100 * 0.78;
7111
- return `rgba(255,121,0,${opacity.toFixed(3)})`;
7086
+ return alpha(BRAND_PRIMARY7, opacity);
7112
7087
  }
7113
7088
  function MesoCard({
7114
7089
  name,