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