@titan-design/react-ui 0.4.0 → 0.6.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 (150) hide show
  1. package/dist/MesoStatusCard-CQk71hSi.d.mts +77 -0
  2. package/dist/MesoStatusCard-CQk71hSi.d.ts +77 -0
  3. package/dist/bodymap.d.mts +246 -3
  4. package/dist/bodymap.d.ts +246 -3
  5. package/dist/bodymap.js +232 -185
  6. package/dist/bodymap.js.map +1 -1
  7. package/dist/bodymap.mjs +1963 -11
  8. package/dist/bodymap.mjs.map +1 -1
  9. package/dist/index.d.mts +193 -744
  10. package/dist/index.d.ts +193 -744
  11. package/dist/index.js +1551 -1856
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +10770 -8213
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/pages-D7Jlssvp.d.ts +791 -0
  16. package/dist/pages-MO0JCySL.d.mts +791 -0
  17. package/dist/pages.d.mts +4 -0
  18. package/dist/pages.d.ts +4 -0
  19. package/dist/pages.js +6694 -0
  20. package/dist/pages.js.map +1 -0
  21. package/dist/pages.mjs +6674 -0
  22. package/dist/pages.mjs.map +1 -0
  23. package/dist/theme/index.d.mts +473 -211
  24. package/dist/theme/index.d.ts +473 -211
  25. package/dist/theme/index.js +341 -127
  26. package/dist/theme/index.js.map +1 -1
  27. package/dist/theme/index.mjs +2085 -2
  28. package/dist/theme/index.mjs.map +1 -1
  29. package/dist/theme/tokens-css.js +148 -139
  30. package/dist/theme/tokens-css.js.map +1 -1
  31. package/dist/theme/tokens-css.mjs +582 -1
  32. package/dist/theme/tokens-css.mjs.map +1 -1
  33. package/package.json +11 -7
  34. package/src/components/custom/DateTime/DateTime.stories.tsx +82 -6
  35. package/src/components/custom/DateTime/DateTime.test.tsx +29 -3
  36. package/src/components/custom/DateTime/DateTime.tsx +55 -15
  37. package/src/components/custom/Gauge/Gauge.test.tsx +2 -2
  38. package/src/components/custom/Gauge/Gauge.tsx +6 -3
  39. package/src/components/custom/Scatter/Scatter.tsx +3 -5
  40. package/src/components/custom/Treemap/Treemap.tsx +3 -5
  41. package/src/components/custom/Typography/Typography.stories.tsx +45 -27
  42. package/src/components/custom/Typography/Typography.test.tsx +13 -3
  43. package/src/components/custom/Typography/Typography.tsx +5 -0
  44. package/src/components/custom/Workout/ActiveWorkoutPage.tsx +2 -1
  45. package/src/components/custom/Workout/BaseBadge.stories.tsx +3 -3
  46. package/src/components/custom/Workout/BaseBadge.test.tsx +1 -1
  47. package/src/components/custom/Workout/BaseBadge.tsx +1 -1
  48. package/src/components/custom/Workout/BodyMap.tsx +6 -3
  49. package/src/components/custom/Workout/BodyMapDetailPanel.tsx +6 -3
  50. package/src/components/custom/Workout/CapacityBandChart.test.tsx +2 -2
  51. package/src/components/custom/Workout/CapacityBandChart.tsx +9 -6
  52. package/src/components/custom/Workout/DeviationBar.test.tsx +1 -1
  53. package/src/components/custom/Workout/DeviationBar.tsx +7 -4
  54. package/src/components/custom/Workout/ExerciseCard.tsx +5 -1
  55. package/src/components/custom/Workout/ExerciseDetailPage.tsx +19 -5
  56. package/src/components/custom/Workout/InputBar.tsx +2 -1
  57. package/src/components/custom/Workout/IntensityBar.test.tsx +112 -60
  58. package/src/components/custom/Workout/IntensityBar.tsx +113 -58
  59. package/src/components/custom/Workout/MesoCard.tsx +8 -3
  60. package/src/components/custom/Workout/MesoProgressBar.test.tsx +1 -1
  61. package/src/components/custom/Workout/MesoProgressBar.tsx +3 -2
  62. package/src/components/custom/Workout/MesoStatusCard.tsx +20 -13
  63. package/src/components/custom/Workout/MuscleGroupChip.tsx +7 -4
  64. package/src/components/custom/Workout/PlaceholderStrip.tsx +5 -9
  65. package/src/components/custom/Workout/PrBadge.tsx +7 -4
  66. package/src/components/custom/Workout/PrHistoryModal.tsx +6 -5
  67. package/src/components/custom/Workout/ProgramPlanningPage.tsx +2 -1
  68. package/src/components/custom/Workout/README.md +50 -0
  69. package/src/components/custom/Workout/ReadinessCheck.tsx +7 -4
  70. package/src/components/custom/Workout/RestTimer.test.tsx +41 -10
  71. package/src/components/custom/Workout/RestTimer.tsx +50 -44
  72. package/src/components/custom/Workout/SetRow.tsx +12 -2
  73. package/src/components/custom/Workout/Sparkline.tsx +2 -1
  74. package/src/components/custom/Workout/StatusDot.test.tsx +2 -2
  75. package/src/components/custom/Workout/StatusDot.tsx +12 -9
  76. package/src/components/custom/Workout/StrengthTrendChart.tsx +9 -6
  77. package/src/components/custom/Workout/SupersetWrapper.tsx +2 -1
  78. package/src/components/custom/Workout/TempoDisplay.stories.tsx +69 -2
  79. package/src/components/custom/Workout/TempoDisplay.test.tsx +35 -0
  80. package/src/components/custom/Workout/TempoDisplay.tsx +109 -3
  81. package/src/components/custom/Workout/VelocityStrip.test.tsx +89 -2
  82. package/src/components/custom/Workout/VelocityStrip.tsx +203 -38
  83. package/src/components/custom/Workout/WeekRow.test.tsx +2 -2
  84. package/src/components/custom/Workout/WeekRow.tsx +3 -2
  85. package/src/components/custom/Workout/WeightBadge.test.tsx +2 -2
  86. package/src/components/custom/Workout/WeightBadge.tsx +8 -4
  87. package/src/components/custom/Workout/WorkoutCard.tsx +5 -2
  88. package/src/components/custom/Workout/WorkoutPill.tsx +12 -8
  89. package/src/components/custom/Workout/icons.tsx +7 -0
  90. package/src/components/custom/Workout/index.ts +38 -47
  91. package/src/components/icons/SvgIcon.tsx +50 -0
  92. package/src/components/icons/icons.stories.tsx +57 -0
  93. package/src/components/icons/icons.test.tsx +43 -0
  94. package/src/components/icons/icons.tsx +45 -0
  95. package/src/components/icons/index.ts +4 -0
  96. package/src/components/index.ts +6 -0
  97. package/src/components/shell/BrandLockup.stories.tsx +27 -0
  98. package/src/components/shell/BrandLockup.test.tsx +23 -0
  99. package/src/components/shell/BrandLockup.tsx +47 -0
  100. package/src/components/shell/DeviceIndicator.stories.tsx +28 -0
  101. package/src/components/shell/DeviceIndicator.test.tsx +26 -0
  102. package/src/components/shell/DeviceIndicator.tsx +52 -0
  103. package/src/components/shell/DeviceMenu.stories.tsx +47 -0
  104. package/src/components/shell/DeviceMenu.test.tsx +40 -0
  105. package/src/components/shell/DeviceMenu.tsx +65 -0
  106. package/src/components/shell/DeviceRow.stories.tsx +38 -0
  107. package/src/components/shell/DeviceRow.test.tsx +37 -0
  108. package/src/components/shell/DeviceRow.tsx +58 -0
  109. package/src/components/shell/README.md +86 -0
  110. package/src/components/shell/SessionStatePill.stories.tsx +27 -0
  111. package/src/components/shell/SessionStatePill.test.tsx +25 -0
  112. package/src/components/shell/SessionStatePill.tsx +46 -0
  113. package/src/components/shell/TopBar.stories.tsx +49 -0
  114. package/src/components/shell/TopBar.test.tsx +36 -0
  115. package/src/components/shell/TopBar.tsx +89 -0
  116. package/src/components/shell/index.ts +8 -0
  117. package/src/components/ui/indicator/Indicator.stories.tsx +37 -1
  118. package/src/components/ui/indicator/Indicator.test.tsx +19 -1
  119. package/src/components/ui/indicator/Indicator.tsx +45 -6
  120. package/src/components/ui/indicator/index.ts +1 -1
  121. package/src/components/ui/spinner/Spinner.tsx +3 -2
  122. package/src/components/ui/toolbar-button/ToolbarButton.tsx +2 -1
  123. package/src/index.ts +3 -2
  124. package/src/pages.ts +61 -0
  125. package/src/test/stories-smoke.test.tsx +50 -0
  126. package/src/theme/ColorSystem.stories.tsx +323 -0
  127. package/src/theme/Colors.stories.tsx +69 -1
  128. package/src/theme/barrel.ts +54 -0
  129. package/src/theme/config.ts +16 -3
  130. package/src/theme/extracted-colors-dataviz.ts +21 -0
  131. package/src/theme/extracted-colors-ui.ts +20 -0
  132. package/src/theme/global.css +64 -52
  133. package/src/theme/gradients.test.ts +25 -0
  134. package/src/theme/gradients.ts +36 -0
  135. package/src/theme/index.ts +8 -43
  136. package/src/theme/native-theming-guard.test.ts +117 -0
  137. package/src/theme/tokens/primitives.test.ts +232 -0
  138. package/src/theme/tokens/primitives.ts +247 -0
  139. package/src/theme/tokens/semantic.ts +93 -81
  140. package/src/theme/workout-tokens.ts +22 -21
  141. package/src/utils/avatar-color.ts +3 -3
  142. package/tailwind.config.js +10 -3
  143. package/dist/bodymap-BhG55xHM.d.mts +0 -318
  144. package/dist/bodymap-BhG55xHM.d.ts +0 -318
  145. package/dist/chunk-2SISKTWM.mjs +0 -1027
  146. package/dist/chunk-2SISKTWM.mjs.map +0 -1
  147. package/dist/chunk-7XPB4NAO.mjs +0 -1013
  148. package/dist/chunk-7XPB4NAO.mjs.map +0 -1
  149. package/dist/chunk-SNVKL5WZ.mjs +0 -883
  150. package/dist/chunk-SNVKL5WZ.mjs.map +0 -1
package/dist/index.js CHANGED
@@ -1,12 +1,10 @@
1
1
  'use strict';
2
2
 
3
+ var jsxRuntime = require('react/jsx-runtime');
3
4
  var reactNative = require('react-native');
4
5
  var clsx = require('clsx');
5
6
  var tailwindMerge = require('tailwind-merge');
6
- var jsxRuntime = require('react/jsx-runtime');
7
7
  var React24 = require('react');
8
- var nativewind = require('nativewind');
9
- var lucideReact = require('lucide-react');
10
8
 
11
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
10
 
@@ -18,9 +16,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
18
16
  if (typeof require !== "undefined") return require.apply(this, arguments);
19
17
  throw Error('Dynamic require of "' + x + '" is not supported');
20
18
  });
21
- function cn(...inputs) {
22
- return tailwindMerge.twMerge(clsx.clsx(inputs));
23
- }
24
19
  function wrapJsx(fn) {
25
20
  return function(type, props, ...rest) {
26
21
  if (props && typeof props.className === "string" && props.className) {
@@ -39,6 +34,58 @@ function wrapJsx(fn) {
39
34
  var jsx = wrapJsx(jsxRuntime.jsx);
40
35
  var jsxs = wrapJsx(jsxRuntime.jsxs);
41
36
 
37
+ // src/components/icons/SvgIcon.tsx
38
+ function SvgIcon({
39
+ size = 24,
40
+ color = "currentColor",
41
+ strokeWidth = 2,
42
+ fill = "none",
43
+ title,
44
+ children
45
+ }) {
46
+ return /* @__PURE__ */ jsxs(
47
+ "svg",
48
+ {
49
+ width: size,
50
+ height: size,
51
+ viewBox: "0 0 24 24",
52
+ fill,
53
+ stroke: color,
54
+ strokeWidth,
55
+ strokeLinecap: "round",
56
+ strokeLinejoin: "round",
57
+ ...title ? { role: "img", "aria-label": title } : { "aria-hidden": true },
58
+ children: [
59
+ title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
60
+ children
61
+ ]
62
+ }
63
+ );
64
+ }
65
+
66
+ // src/components/icons/icons.tsx
67
+ function VoltrasMark({ size = 14, ...props }) {
68
+ return /* @__PURE__ */ jsx(SvgIcon, { size, ...props, children: /* @__PURE__ */ jsx("path", { d: "M12 2 22 12 12 22 2 12Z" }) });
69
+ }
70
+ function BluetoothIcon({ size = 18, ...props }) {
71
+ return /* @__PURE__ */ jsx(SvgIcon, { size, ...props, children: /* @__PURE__ */ jsx("path", { d: "M7 7 17 17 12 22 12 2 17 7 7 17" }) });
72
+ }
73
+ function DumbbellIcon(props) {
74
+ return /* @__PURE__ */ jsxs(SvgIcon, { ...props, children: [
75
+ /* @__PURE__ */ jsx("path", { d: "M14.4 14.4 9.6 9.6" }),
76
+ /* @__PURE__ */ jsx("path", { d: "M18.657 21.485a2 2 0 1 1-2.829-2.828l-1.767 1.768a2 2 0 1 1-2.829-2.829l6.364-6.364a2 2 0 1 1 2.829 2.829l-1.768 1.767a2 2 0 1 1 2.828 2.829z" }),
77
+ /* @__PURE__ */ jsx("path", { d: "m21.5 21.5-1.4-1.4" }),
78
+ /* @__PURE__ */ jsx("path", { d: "M3.9 3.9 2.5 2.5" }),
79
+ /* @__PURE__ */ jsx("path", { d: "M6.404 12.768a2 2 0 1 1-2.829-2.829l1.768-1.767a2 2 0 1 1-2.828-2.829l2.828-2.828a2 2 0 1 1 2.829 2.828l1.767-1.768a2 2 0 1 1 2.829 2.829z" })
80
+ ] });
81
+ }
82
+ function StarIcon(props) {
83
+ 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" }) });
84
+ }
85
+ function cn(...inputs) {
86
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
87
+ }
88
+
42
89
  // src/components/ui/alert/Alert.tsx
43
90
  var statusColors = {
44
91
  success: {
@@ -196,6 +243,38 @@ var primitiveColors = {
196
243
  800: "#991B1B",
197
244
  900: "#7F1D1D"
198
245
  },
246
+ // Vivid green scale (live/go success)
247
+ greenVivid: {
248
+ 50: "#E9FBF0",
249
+ 100: "#C8F7DA",
250
+ 200: "#9FF0BF",
251
+ 300: "#6BE79D",
252
+ 400: "#47DE84",
253
+ // Success vivid light
254
+ 500: "#2ED573",
255
+ // Success vivid main
256
+ 600: "#22B85F",
257
+ 700: "#1A9950",
258
+ // Success vivid dark
259
+ 800: "#157A40",
260
+ 900: "#105C30"
261
+ },
262
+ // Vivid red scale (alert)
263
+ redVivid: {
264
+ 50: "#FFECEE",
265
+ 100: "#FFD6DB",
266
+ 200: "#FFB3BC",
267
+ 300: "#FF8593",
268
+ 400: "#FF6070",
269
+ // Error vivid light
270
+ 500: "#FF4757",
271
+ // Error vivid main
272
+ 600: "#E63548",
273
+ 700: "#C42539",
274
+ // Error vivid dark
275
+ 800: "#9E1C2C",
276
+ 900: "#7A1520"
277
+ },
199
278
  // Amber scale (warning)
200
279
  amber: {
201
280
  50: "#FFFBEB",
@@ -285,6 +364,162 @@ var primitiveColors = {
285
364
  black: "#000000",
286
365
  transparent: "transparent"
287
366
  };
367
+ var primitiveRamps = {
368
+ red: {
369
+ 50: "#FFF4F4",
370
+ 100: "#FFE3E5",
371
+ 200: "#FFC2C5",
372
+ 300: "#FF9A9D",
373
+ 400: "#F77175",
374
+ 500: "#E05254",
375
+ 600: "#D14343",
376
+ // pin
377
+ 700: "#A4221C",
378
+ 800: "#7E1002",
379
+ 900: "#5A0900",
380
+ 950: "#3D0400"
381
+ },
382
+ orange: {
383
+ 50: "#FFF5ED",
384
+ 100: "#FFE6D4",
385
+ 200: "#FFC7A2",
386
+ 300: "#FFA063",
387
+ 400: "#FF7900",
388
+ // pin
389
+ 500: "#DA5F00",
390
+ 600: "#B94A00",
391
+ 700: "#983804",
392
+ 800: "#6F290F",
393
+ 900: "#4E1C0C",
394
+ 950: "#331107"
395
+ },
396
+ amber: {
397
+ 50: "#FFF7DD",
398
+ 100: "#FFEAA9",
399
+ 200: "#FFD352",
400
+ 300: "#F9B415",
401
+ // pin
402
+ 400: "#E08C00",
403
+ 500: "#C27400",
404
+ 600: "#A45E00",
405
+ 700: "#814D14",
406
+ 800: "#5B3A1A",
407
+ 900: "#442503",
408
+ 950: "#301500"
409
+ },
410
+ green: {
411
+ 50: "#E3FFEE",
412
+ 100: "#B5FFD2",
413
+ 200: "#58F69E",
414
+ 300: "#2ED573",
415
+ // pin
416
+ 400: "#21C05D",
417
+ 500: "#22A444",
418
+ 600: "#298732",
419
+ 700: "#2B6B25",
420
+ 800: "#264D1C",
421
+ 900: "#1B3515",
422
+ 950: "#11220D"
423
+ },
424
+ cyan: {
425
+ 50: "#E6FBFF",
426
+ 100: "#C2F6FF",
427
+ 200: "#62EAFF",
428
+ 300: "#22D3EE",
429
+ // pin
430
+ 400: "#01B5D1",
431
+ 500: "#2697B7",
432
+ 600: "#307B9B",
433
+ 700: "#2A617F",
434
+ 800: "#22465F",
435
+ 900: "#0B3149",
436
+ 950: "#001F36"
437
+ },
438
+ blue: {
439
+ 50: "#EFF8FF",
440
+ 100: "#D9EFFF",
441
+ 200: "#ACDBFF",
442
+ 300: "#78C2FF",
443
+ 400: "#3CA8FF",
444
+ 500: "#2196F3",
445
+ // pin
446
+ 600: "#1072CB",
447
+ 700: "#135AA8",
448
+ 800: "#14407E",
449
+ 900: "#112C5A",
450
+ 950: "#091B3C"
451
+ },
452
+ magenta: {
453
+ 50: "#FFF3F9",
454
+ 100: "#FFE2F1",
455
+ 200: "#FFBDE2",
456
+ 300: "#FF8FD5",
457
+ 400: "#ED69C3",
458
+ 500: "#D548AF",
459
+ 600: "#BA2996",
460
+ 700: "#9C0D7A",
461
+ // pin
462
+ 800: "#77005A",
463
+ 900: "#56003F",
464
+ 950: "#3B0029"
465
+ }
466
+ };
467
+ var categoricalPalette = {
468
+ default: [
469
+ primitiveRamps.blue[500],
470
+ primitiveRamps.magenta[500],
471
+ primitiveRamps.red[500],
472
+ primitiveRamps.orange[400],
473
+ primitiveRamps.green[300],
474
+ primitiveRamps.cyan[300],
475
+ primitiveRamps.amber[600]
476
+ // extended (7th) — pair with a legend
477
+ ],
478
+ dark: [
479
+ primitiveRamps.blue[600],
480
+ primitiveRamps.magenta[600],
481
+ primitiveRamps.red[500],
482
+ primitiveRamps.orange[600],
483
+ primitiveRamps.green[800],
484
+ primitiveRamps.cyan[800],
485
+ primitiveRamps.amber[500]
486
+ // extended (7th) — pair with a legend
487
+ ]
488
+ };
489
+ var CATEGORICAL_CVD_SAFE_MAX = 6;
490
+ function getCategoricalColor(index, variant = "default") {
491
+ const palette = categoricalPalette[variant];
492
+ index = Math.max(0, Math.floor(index));
493
+ return palette[index % palette.length];
494
+ }
495
+ var divergingScale = [
496
+ primitiveRamps.blue[500],
497
+ // under
498
+ primitiveRamps.cyan[300],
499
+ // maintenance
500
+ primitiveRamps.green[200],
501
+ // optimal (light center)
502
+ primitiveRamps.amber[300],
503
+ // approaching
504
+ primitiveRamps.red[600]
505
+ // over
506
+ ];
507
+ var sequentialEffort = [
508
+ primitiveRamps.green[300],
509
+ primitiveRamps.amber[200],
510
+ primitiveRamps.amber[300],
511
+ primitiveRamps.orange[400],
512
+ primitiveRamps.red[600],
513
+ primitiveRamps.red[700]
514
+ ];
515
+ function bestTextColor(hex) {
516
+ const [r, g, b] = [0, 2, 4].map((i) => {
517
+ const c = parseInt(hex.replace("#", "").slice(i, i + 2), 16) / 255;
518
+ return c >= 0.04045 ? ((c + 0.055) / 1.055) ** 2.4 : c / 12.92;
519
+ });
520
+ const l = 0.2126 * r + 0.7152 * g + 0.0722 * b;
521
+ return (l + 0.05) / 0.05 >= 1.05 / (l + 0.05) ? "#000000" : "#FFFFFF";
522
+ }
288
523
  var discreteRainbow = [
289
524
  "#E8ECFB",
290
525
  "#D9CCE3",
@@ -494,56 +729,43 @@ var primitiveZIndex = {
494
729
  // src/theme/tokens/semantic.ts
495
730
  var semanticColorsLight = {
496
731
  // Brand colors (brand-*)
497
- "brand-primary": primitiveColors.accent[400],
498
- // #FF7900
499
- "brand-primary-light": primitiveColors.accent[200],
500
- // #FF9630
501
- "brand-primary-dark": primitiveColors.accent[700],
502
- // #D0620C
732
+ "brand-primary": primitiveRamps.orange[400],
733
+ "brand-primary-light": primitiveRamps.orange[300],
734
+ "brand-primary-dark": primitiveRamps.orange[500],
503
735
  "brand-primary-subtle": "rgba(255, 121, 0, 0.08)",
504
- "brand-primary-hover": primitiveColors.accent[500],
505
- "brand-primary-active": primitiveColors.accent[600],
506
- "brand-secondary": primitiveColors.steel[500],
507
- // #406D87
508
- "brand-secondary-light": primitiveColors.steel[300],
509
- // #678498
510
- "brand-secondary-dark": primitiveColors.steel[700],
511
- // #32556D
512
- "brand-secondary-subtle": "rgba(64, 109, 135, 0.08)",
513
- "brand-secondary-hover": primitiveColors.steel[600],
514
- "brand-secondary-active": primitiveColors.steel[700],
736
+ "brand-primary-hover": primitiveRamps.orange[500],
737
+ "brand-primary-active": primitiveRamps.orange[600],
738
+ "brand-secondary": primitiveRamps.cyan[600],
739
+ "brand-secondary-light": primitiveRamps.cyan[500],
740
+ "brand-secondary-dark": primitiveRamps.cyan[700],
741
+ "brand-secondary-subtle": "rgba(48, 123, 155, 0.08)",
742
+ "brand-secondary-hover": primitiveRamps.cyan[700],
743
+ "brand-secondary-active": primitiveRamps.cyan[800],
515
744
  // Text on brand backgrounds (on-*)
516
745
  "on-brand-primary": primitiveColors.white,
517
746
  "on-brand-secondary": primitiveColors.white,
518
747
  // Status colors (status-*)
519
- "status-success": primitiveColors.teal[500],
520
- // #14B8A6
521
- "status-success-light": primitiveColors.teal[400],
522
- // #43C6B7
523
- "status-success-dark": primitiveColors.teal[700],
524
- // #0E8074
525
- "status-success-subtle": primitiveColors.teal[50],
526
- "status-error": primitiveColors.red[600],
527
- // #D14343
528
- "status-error-light": primitiveColors.red[400],
529
- // #DA6868
530
- "status-error-dark": primitiveColors.red[700],
531
- // #922E2E
532
- "status-error-subtle": primitiveColors.red[50],
533
- "status-warning": primitiveColors.amber[500],
534
- // #FFB020
535
- "status-warning-light": primitiveColors.amber[400],
536
- // #FFBF4C
537
- "status-warning-dark": primitiveColors.amber[600],
538
- // #B27B16
539
- "status-warning-subtle": primitiveColors.amber[50],
540
- "status-info": primitiveColors.sky[500],
541
- // #2196F3
542
- "status-info-light": primitiveColors.sky[400],
543
- // #64B6F7
544
- "status-info-dark": primitiveColors.sky[700],
545
- // #0B79D0
546
- "status-info-subtle": primitiveColors.sky[50],
748
+ "status-success": primitiveRamps.green[300],
749
+ "status-success-light": primitiveRamps.green[200],
750
+ "status-success-dark": primitiveRamps.green[600],
751
+ "status-success-subtle": primitiveRamps.green[50],
752
+ "status-error": primitiveRamps.red[600],
753
+ "status-error-light": primitiveRamps.red[500],
754
+ "status-error-dark": primitiveRamps.red[700],
755
+ "status-error-subtle": primitiveRamps.red[50],
756
+ "status-error-vivid": primitiveColors.redVivid[500],
757
+ // vivid alert red
758
+ "status-error-vivid-light": primitiveColors.redVivid[400],
759
+ "status-error-vivid-dark": primitiveColors.redVivid[700],
760
+ "status-error-vivid-subtle": "rgba(255, 71, 87, 0.12)",
761
+ "status-warning": primitiveRamps.amber[300],
762
+ "status-warning-light": primitiveRamps.amber[200],
763
+ "status-warning-dark": primitiveRamps.amber[500],
764
+ "status-warning-subtle": primitiveRamps.amber[50],
765
+ "status-info": primitiveRamps.blue[500],
766
+ "status-info-light": primitiveRamps.blue[300],
767
+ "status-info-dark": primitiveRamps.blue[600],
768
+ "status-info-subtle": primitiveRamps.blue[50],
547
769
  // Text on status backgrounds (on-status-*)
548
770
  "on-status-success": primitiveColors.white,
549
771
  "on-status-error": primitiveColors.white,
@@ -570,25 +792,25 @@ var semanticColorsLight = {
570
792
  // Data visualization colors (data-*)
571
793
  // First 10 colors from discrete rainbow optimized for charts
572
794
  "data-1": discreteRainbow[9],
573
- // #1965B0 - Blue
795
+ // Blue
574
796
  "data-2": discreteRainbow[14],
575
- // #4EB265 - Green
797
+ // Green
576
798
  "data-3": discreteRainbow[17],
577
- // #F7F056 - Yellow
799
+ // Yellow
578
800
  "data-4": discreteRainbow[25],
579
- // #DC050C - Red
801
+ // Red
580
802
  "data-5": discreteRainbow[8],
581
- // #882E72 - Purple
803
+ // Purple
582
804
  "data-6": discreteRainbow[20],
583
- // #F4A736 - Orange
805
+ // Orange
584
806
  "data-7": discreteRainbow[13],
585
- // #7BAFDE - Light Blue
807
+ // Light Blue
586
808
  "data-8": discreteRainbow[15],
587
- // #90C987 - Light Green
809
+ // Light Green
588
810
  "data-9": discreteRainbow[3],
589
- // #CAACCB - Lavender
811
+ // Lavender
590
812
  "data-10": discreteRainbow[22],
591
- // #EE8026 - Dark Orange
813
+ // Dark Orange
592
814
  // Text colors (text-*)
593
815
  "text-primary": "#121828",
594
816
  "text-secondary": "#65748B",
@@ -600,9 +822,9 @@ var semanticColorsLight = {
600
822
  // Surface colors (surface-*) - for elevated containers
601
823
  "surface-base": primitiveColors.white,
602
824
  "surface-elevated": primitiveColors.neutral[50],
603
- // #FAFAFA - slightly off-white for elevated cards
825
+ // slightly off-white for elevated cards
604
826
  "surface-raised": primitiveColors.neutral[100],
605
- // #F3F4F6 - light gray for raised cards
827
+ // light gray for raised cards
606
828
  "surface-overlay": primitiveColors.white,
607
829
  "surface-input": primitiveColors.neutral[50],
608
830
  // Input field background (filled variant)
@@ -614,6 +836,8 @@ var semanticColorsLight = {
614
836
  "border-default": "#E8E9EB",
615
837
  "border-subtle": primitiveColors.neutral[100],
616
838
  "border-strong": primitiveColors.neutral[300],
839
+ "border-prominent": primitiveColors.neutral[400],
840
+ // high-visibility divider
617
841
  "border-focus": primitiveColors.blue[600],
618
842
  "border-input": primitiveColors.neutral[300],
619
843
  // Input field border
@@ -638,37 +862,41 @@ var semanticColorsLight = {
638
862
  };
639
863
  var semanticColorsDark = {
640
864
  // Brand colors stay the same in dark mode
641
- "brand-primary": primitiveColors.accent[400],
642
- "brand-primary-light": primitiveColors.accent[200],
643
- "brand-primary-dark": primitiveColors.accent[700],
865
+ "brand-primary": primitiveRamps.orange[400],
866
+ "brand-primary-light": primitiveRamps.orange[300],
867
+ "brand-primary-dark": primitiveRamps.orange[500],
644
868
  "brand-primary-subtle": "rgba(255, 121, 0, 0.12)",
645
- "brand-primary-hover": primitiveColors.accent[300],
646
- "brand-primary-active": primitiveColors.accent[200],
647
- "brand-secondary": primitiveColors.steel[500],
648
- "brand-secondary-light": primitiveColors.steel[300],
649
- "brand-secondary-dark": primitiveColors.steel[700],
650
- "brand-secondary-subtle": "rgba(64, 109, 135, 0.12)",
651
- "brand-secondary-hover": primitiveColors.steel[400],
652
- "brand-secondary-active": primitiveColors.steel[300],
869
+ "brand-primary-hover": primitiveRamps.orange[300],
870
+ "brand-primary-active": primitiveRamps.orange[200],
871
+ "brand-secondary": primitiveRamps.cyan[600],
872
+ "brand-secondary-light": primitiveRamps.cyan[500],
873
+ "brand-secondary-dark": primitiveRamps.cyan[700],
874
+ "brand-secondary-subtle": "rgba(48, 123, 155, 0.12)",
875
+ "brand-secondary-hover": primitiveRamps.cyan[500],
876
+ "brand-secondary-active": primitiveRamps.cyan[400],
653
877
  // Text on brand backgrounds
654
878
  "on-brand-primary": primitiveColors.white,
655
879
  "on-brand-secondary": primitiveColors.white,
656
880
  // Status colors
657
- "status-success": primitiveColors.teal[500],
658
- "status-success-light": primitiveColors.teal[400],
659
- "status-success-dark": primitiveColors.teal[700],
660
- "status-success-subtle": "rgba(20, 184, 166, 0.12)",
661
- "status-error": primitiveColors.red[600],
662
- "status-error-light": primitiveColors.red[400],
663
- "status-error-dark": primitiveColors.red[700],
881
+ "status-success": primitiveRamps.green[300],
882
+ "status-success-light": primitiveRamps.green[200],
883
+ "status-success-dark": primitiveRamps.green[600],
884
+ "status-success-subtle": "rgba(46, 213, 115, 0.12)",
885
+ "status-error": primitiveRamps.red[600],
886
+ "status-error-light": primitiveRamps.red[500],
887
+ "status-error-dark": primitiveRamps.red[700],
664
888
  "status-error-subtle": "rgba(209, 67, 67, 0.12)",
665
- "status-warning": primitiveColors.amber[500],
666
- "status-warning-light": primitiveColors.amber[400],
667
- "status-warning-dark": primitiveColors.amber[600],
668
- "status-warning-subtle": "rgba(255, 176, 32, 0.12)",
669
- "status-info": primitiveColors.sky[500],
670
- "status-info-light": primitiveColors.sky[400],
671
- "status-info-dark": primitiveColors.sky[700],
889
+ "status-error-vivid": primitiveColors.redVivid[500],
890
+ "status-error-vivid-light": primitiveColors.redVivid[400],
891
+ "status-error-vivid-dark": primitiveColors.redVivid[700],
892
+ "status-error-vivid-subtle": "rgba(255, 71, 87, 0.12)",
893
+ "status-warning": primitiveRamps.amber[300],
894
+ "status-warning-light": primitiveRamps.amber[200],
895
+ "status-warning-dark": primitiveRamps.amber[500],
896
+ "status-warning-subtle": "rgba(249, 180, 21, 0.12)",
897
+ "status-info": primitiveRamps.blue[500],
898
+ "status-info-light": primitiveRamps.blue[300],
899
+ "status-info-dark": primitiveRamps.blue[600],
672
900
  "status-info-subtle": "rgba(33, 150, 243, 0.12)",
673
901
  // Text on status backgrounds
674
902
  "on-status-success": primitiveColors.white,
@@ -710,29 +938,31 @@ var semanticColorsDark = {
710
938
  "text-link-hover": primitiveColors.blue[400],
711
939
  // Surface colors - dark backgrounds
712
940
  "surface-base": primitiveColors.charcoal[700],
713
- // #161616 - main surface
941
+ // main surface
714
942
  "surface-elevated": primitiveColors.charcoal[600],
715
- // #191919 - elevated surface
943
+ // elevated surface
716
944
  "surface-raised": primitiveColors.charcoal[500],
717
- // #1C1C1C - raised surface
945
+ // raised surface
718
946
  "surface-overlay": primitiveColors.charcoal[600],
719
- // #191919 - overlay surface
947
+ // overlay surface
720
948
  "surface-input": primitiveColors.charcoal[600],
721
- // #191919 - input surface
949
+ // input surface
722
950
  // Background colors
723
951
  "background-base": primitiveColors.charcoal[900],
724
- // #101010 - darkest charcoal
952
+ // darkest charcoal
725
953
  "background-default": primitiveColors.charcoal[700],
726
- // #161616 - main background
954
+ // main background
727
955
  "background-subtle": primitiveColors.charcoal[500],
728
- // #1C1C1C - subtle background
956
+ // subtle background
729
957
  // Border colors
730
958
  "border-default": primitiveColors.charcoal[400],
731
- // #1F1F1F - default border
959
+ // default border
732
960
  "border-subtle": primitiveColors.charcoal[500],
733
- // #1C1C1C - subtle border
961
+ // subtle border
734
962
  "border-strong": primitiveColors.charcoal[300],
735
- // #2C2C2C - strong border
963
+ // strong border
964
+ "border-prominent": primitiveColors.charcoal[200],
965
+ // high-visibility divider
736
966
  "border-focus": "#828DF8",
737
967
  "border-input": primitiveColors.neutral[600],
738
968
  "border-input-hover": primitiveColors.neutral[500],
@@ -747,7 +977,7 @@ var semanticColorsDark = {
747
977
  "interactive-disabled-text": "rgba(255, 255, 255, 0.26)",
748
978
  // Divider
749
979
  "divider": primitiveColors.charcoal[400],
750
- // #1F1F1F - divider color
980
+ // divider color
751
981
  // Avatar default
752
982
  "avatar-background": primitiveColors.neutral[600],
753
983
  "avatar-text": primitiveColors.white
@@ -1213,10 +1443,11 @@ var PasswordInput = React24.forwardRef(function PasswordInput2({ showIcon, hideI
1213
1443
  var themeIndependentCSSVars = {
1214
1444
  // RGB decomposed values for glow shadows
1215
1445
  "--color-brand-primary-rgb": "255, 121, 0",
1216
- "--color-brand-secondary-rgb": "64, 109, 135",
1217
- "--color-status-success-rgb": "20, 184, 166",
1446
+ "--color-brand-secondary-rgb": "48, 123, 155",
1447
+ "--color-status-success-rgb": "46, 213, 115",
1218
1448
  "--color-status-error-rgb": "209, 67, 67",
1219
- "--color-status-warning-rgb": "255, 176, 32",
1449
+ "--color-status-error-vivid-rgb": "255, 71, 87",
1450
+ "--color-status-warning-rgb": "249, 180, 21",
1220
1451
  "--color-status-info-rgb": "33, 150, 243",
1221
1452
  "--color-background-base-rgb": "16, 16, 16",
1222
1453
  // Font families
@@ -1251,6 +1482,10 @@ var lightThemeCSSVars = {
1251
1482
  "--color-status-warning-subtle": semanticColorsLight["status-warning-subtle"],
1252
1483
  "--color-status-info": semanticColorsLight["status-info"],
1253
1484
  "--color-status-info-subtle": semanticColorsLight["status-info-subtle"],
1485
+ "--color-status-error-vivid": semanticColorsLight["status-error-vivid"],
1486
+ "--color-status-error-vivid-light": semanticColorsLight["status-error-vivid-light"],
1487
+ "--color-status-error-vivid-dark": semanticColorsLight["status-error-vivid-dark"],
1488
+ "--color-status-error-vivid-subtle": semanticColorsLight["status-error-vivid-subtle"],
1254
1489
  "--color-text-primary": semanticColorsLight["text-primary"],
1255
1490
  "--color-text-secondary": semanticColorsLight["text-secondary"],
1256
1491
  "--color-text-tertiary": semanticColorsLight["text-tertiary"],
@@ -1266,6 +1501,7 @@ var lightThemeCSSVars = {
1266
1501
  "--color-border-default": semanticColorsLight["border-default"],
1267
1502
  "--color-border-subtle": semanticColorsLight["border-subtle"],
1268
1503
  "--color-border-strong": semanticColorsLight["border-strong"],
1504
+ "--color-border-prominent": semanticColorsLight["border-prominent"],
1269
1505
  "--color-border-focus": semanticColorsLight["border-focus"],
1270
1506
  "--color-interactive-hover": semanticColorsLight["interactive-hover"],
1271
1507
  "--color-interactive-focus": semanticColorsLight["interactive-focus"],
@@ -1342,6 +1578,10 @@ var darkThemeCSSVars = {
1342
1578
  "--color-status-warning-subtle": semanticColorsDark["status-warning-subtle"],
1343
1579
  "--color-status-info": semanticColorsDark["status-info"],
1344
1580
  "--color-status-info-subtle": semanticColorsDark["status-info-subtle"],
1581
+ "--color-status-error-vivid": semanticColorsDark["status-error-vivid"],
1582
+ "--color-status-error-vivid-light": semanticColorsDark["status-error-vivid-light"],
1583
+ "--color-status-error-vivid-dark": semanticColorsDark["status-error-vivid-dark"],
1584
+ "--color-status-error-vivid-subtle": semanticColorsDark["status-error-vivid-subtle"],
1345
1585
  "--color-text-primary": semanticColorsDark["text-primary"],
1346
1586
  "--color-text-secondary": semanticColorsDark["text-secondary"],
1347
1587
  "--color-text-tertiary": semanticColorsDark["text-tertiary"],
@@ -1357,6 +1597,7 @@ var darkThemeCSSVars = {
1357
1597
  "--color-border-default": semanticColorsDark["border-default"],
1358
1598
  "--color-border-subtle": semanticColorsDark["border-subtle"],
1359
1599
  "--color-border-strong": semanticColorsDark["border-strong"],
1600
+ "--color-border-prominent": semanticColorsDark["border-prominent"],
1360
1601
  "--color-border-focus": semanticColorsDark["border-focus"],
1361
1602
  "--color-interactive-hover": semanticColorsDark["interactive-hover"],
1362
1603
  "--color-interactive-focus": semanticColorsDark["interactive-focus"],
@@ -2161,22 +2402,21 @@ function validateCssPropertyManifest(candidate) {
2161
2402
  function isCssPropertyManifest(candidate) {
2162
2403
  return validateCssPropertyManifest(candidate).valid;
2163
2404
  }
2164
- var MODE_VARS = {
2165
- dark: nativewind.vars(darkThemeCSSVars),
2166
- light: nativewind.vars(lightThemeCSSVars)
2167
- };
2168
- function ThemeProvider({
2169
- mode = "dark",
2170
- style,
2171
- children,
2172
- ...props
2173
- }) {
2174
- return /* @__PURE__ */ jsx(reactNative.View, { style: [MODE_VARS[mode], { flex: 1 }, style], ...props, children });
2175
- }
2176
2405
  function resolveColor(token, mode = "dark") {
2177
2406
  return reactNative.Platform.OS === "web" ? `var(--color-${token})` : getSemanticColors(mode)[token];
2178
2407
  }
2179
2408
 
2409
+ // src/theme/gradients.ts
2410
+ function linearGradient(from, to, angle = 180, mode = "dark") {
2411
+ return {
2412
+ backgroundImage: `linear-gradient(${angle}deg, ${resolveColor(from, mode)}, ${resolveColor(to, mode)})`
2413
+ };
2414
+ }
2415
+ var surfaceGradient = {
2416
+ /** Chrome bands (top bar, headers): elevated → base, a subtle dark wash. */
2417
+ chrome: (mode = "dark") => linearGradient("surface-elevated", "background-base", 180, mode)
2418
+ };
2419
+
2180
2420
  // src/theme/presets/apply.ts
2181
2421
  function colorKeyToVar(key) {
2182
2422
  return `--color-${key}`;
@@ -2551,7 +2791,8 @@ var colorStyles = {
2551
2791
  success: "bg-status-success",
2552
2792
  error: "bg-status-error",
2553
2793
  warning: "bg-status-warning",
2554
- info: "bg-status-info"
2794
+ info: "bg-status-info",
2795
+ "error-vivid": "bg-status-error-vivid"
2555
2796
  };
2556
2797
  function Indicator({
2557
2798
  size = "sm",
@@ -2559,14 +2800,40 @@ function Indicator({
2559
2800
  customColor,
2560
2801
  glow = false,
2561
2802
  ring = false,
2803
+ pulse = false,
2562
2804
  className,
2563
2805
  style,
2564
2806
  ...props
2565
2807
  }) {
2808
+ const pulseMode = pulse === true ? "opacity" : pulse || false;
2809
+ const colorClass = !customColor ? colorStyles[color] : void 0;
2810
+ const colorStyle = customColor ? { backgroundColor: customColor } : void 0;
2811
+ if (pulseMode === "ping") {
2812
+ return /* @__PURE__ */ jsxs(reactNative.View, { className: cn("relative", sizeStyles3[size], className), style, ...props, children: [
2813
+ /* @__PURE__ */ jsx(
2814
+ reactNative.View,
2815
+ {
2816
+ className: cn("absolute inset-0 rounded-full animate-ping", colorClass),
2817
+ style: colorStyle
2818
+ }
2819
+ ),
2820
+ /* @__PURE__ */ jsx(
2821
+ reactNative.View,
2822
+ {
2823
+ className: cn(
2824
+ "relative rounded-full",
2825
+ sizeStyles3[size],
2826
+ colorClass,
2827
+ ring && "border-2 border-background-base"
2828
+ ),
2829
+ style: colorStyle
2830
+ }
2831
+ )
2832
+ ] });
2833
+ }
2566
2834
  const dynamicStyle = {
2567
2835
  ...typeof style === "object" ? style : {},
2568
- ...customColor ? { backgroundColor: customColor } : {},
2569
- ...glow && !customColor ? {} : {},
2836
+ ...colorStyle ?? {},
2570
2837
  ...glow && customColor ? { boxShadow: `0 0 6px ${customColor}` } : {}
2571
2838
  };
2572
2839
  return /* @__PURE__ */ jsx(
@@ -2575,12 +2842,14 @@ function Indicator({
2575
2842
  className: cn(
2576
2843
  "rounded-full shrink-0",
2577
2844
  sizeStyles3[size],
2578
- !customColor && colorStyles[color],
2845
+ colorClass,
2846
+ pulseMode === "opacity" && "animate-pulse",
2579
2847
  ring && "border-2 border-background-base",
2580
- glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(20,184,166,0.6)]",
2848
+ glow && !customColor && color === "success" && "shadow-[0_0_6px_rgba(46,213,115,0.6)]",
2581
2849
  glow && !customColor && color === "error" && "shadow-[0_0_6px_rgba(239,68,68,0.6)]",
2582
2850
  glow && !customColor && color === "warning" && "shadow-[0_0_6px_rgba(245,158,11,0.6)]",
2583
2851
  glow && !customColor && color === "primary" && "shadow-[0_0_6px_rgba(255,121,0,0.6)]",
2852
+ glow && !customColor && color === "error-vivid" && "shadow-[0_0_6px_rgba(255,71,87,0.6)]",
2584
2853
  className
2585
2854
  ),
2586
2855
  style: Object.keys(dynamicStyle).length > 0 ? dynamicStyle : style,
@@ -2661,6 +2930,13 @@ function Badge({
2661
2930
  function BadgeText({ children, className }) {
2662
2931
  return /* @__PURE__ */ jsx(reactNative.Text, { className: cn("text-inherit font-medium", className), children });
2663
2932
  }
2933
+
2934
+ // src/theme/extracted-colors-ui.ts
2935
+ var SPINNER_PRIMARY = primitiveRamps.cyan[600];
2936
+ var SPINNER_SECONDARY = primitiveRamps.green[400];
2937
+ var AVATAR_CATEGORICAL_COLORS = categoricalPalette.default;
2938
+
2939
+ // src/components/ui/spinner/Spinner.tsx
2664
2940
  var sizeMap = {
2665
2941
  sm: "small",
2666
2942
  md: "small",
@@ -2668,8 +2944,8 @@ var sizeMap = {
2668
2944
  xl: "large"
2669
2945
  };
2670
2946
  var colorMap = {
2671
- primary: "#5048E5",
2672
- secondary: "#10B981",
2947
+ primary: SPINNER_PRIMARY,
2948
+ secondary: SPINNER_SECONDARY,
2673
2949
  white: "#FFFFFF",
2674
2950
  default: "#6B7280"
2675
2951
  };
@@ -2705,15 +2981,7 @@ function Spinner({
2705
2981
  }
2706
2982
 
2707
2983
  // src/utils/avatar-color.ts
2708
- var AVATAR_COLORS = [
2709
- "#FF6B6B",
2710
- "#4ECDC4",
2711
- "#45B7D1",
2712
- "#96CEB4",
2713
- "#FFEAA7",
2714
- "#DDA0DD",
2715
- "#98D8C8"
2716
- ];
2984
+ var AVATAR_COLORS = AVATAR_CATEGORICAL_COLORS;
2717
2985
  function avatarColor(name) {
2718
2986
  let hash = 0;
2719
2987
  for (let i = 0; i < name.length; i++) {
@@ -4358,7 +4626,7 @@ function ToolbarButton({
4358
4626
  const selected = isActive === void 0 ? isOpen : isActive;
4359
4627
  const showActive = isActive !== false;
4360
4628
  const getIconColor = () => {
4361
- if (isActive === true) return "#FF7900";
4629
+ if (isActive === true) return resolveColor("brand-primary");
4362
4630
  return showActive ? "#FFFFFF" : "#D1D1D1";
4363
4631
  };
4364
4632
  const getNeumorphicStyle = () => {
@@ -5001,7 +5269,7 @@ function ToastProvider({
5001
5269
  return id;
5002
5270
  }, [defaultDuration, maxToasts]);
5003
5271
  const removeToast = React24.useCallback((id) => {
5004
- setToasts((prev) => prev.filter((t2) => t2.id !== id));
5272
+ setToasts((prev) => prev.filter((t12) => t12.id !== id));
5005
5273
  }, []);
5006
5274
  const removeAllToasts = React24.useCallback(() => {
5007
5275
  setToasts([]);
@@ -5883,7 +6151,10 @@ var variantStyles4 = {
5883
6151
  subtitle2: "font-body text-sm font-medium leading-normal",
5884
6152
  caption: "font-body text-xs font-normal leading-loose",
5885
6153
  overline: "font-body text-xs font-semibold uppercase tracking-widest leading-relaxed",
5886
- button: "font-sans text-sm font-semibold leading-relaxed"
6154
+ button: "font-sans text-sm font-semibold leading-relaxed",
6155
+ // Monospace technical readouts (clocks, ids, metrics) and their all-caps label form.
6156
+ mono: "font-mono text-xs font-normal leading-normal",
6157
+ monoLabel: "font-mono text-xs font-bold uppercase tracking-wide leading-normal"
5887
6158
  };
5888
6159
  var colorStyles7 = {
5889
6160
  primary: "text-text-primary",
@@ -6572,7 +6843,7 @@ var colorStyles8 = {
6572
6843
  tertiary: "text-text-tertiary",
6573
6844
  inherit: ""
6574
6845
  };
6575
- function formatDate(value, format, isUTC) {
6846
+ function formatDate(value, format, isUTC, hour12, seconds) {
6576
6847
  const date = value instanceof Date ? value : new Date(value);
6577
6848
  if (isNaN(date.getTime())) {
6578
6849
  return "Invalid Date";
@@ -6622,6 +6893,12 @@ function formatDate(value, format, isUTC) {
6622
6893
  if (isUTC) {
6623
6894
  options.timeZone = "UTC";
6624
6895
  }
6896
+ if (hour12 !== void 0 && (options.hour !== void 0 || options.minute !== void 0)) {
6897
+ options.hour12 = hour12;
6898
+ }
6899
+ if (seconds && options.hour !== void 0) {
6900
+ options.second = "2-digit";
6901
+ }
6625
6902
  return new Intl.DateTimeFormat(void 0, options).format(date);
6626
6903
  }
6627
6904
  function getRelativeTime(date) {
@@ -6691,16 +6968,28 @@ function DateTime({
6691
6968
  format = "datetime",
6692
6969
  customFormat,
6693
6970
  isUTC = false,
6971
+ hour12,
6972
+ seconds,
6973
+ variant,
6974
+ live = false,
6975
+ refreshMs = 1e3,
6694
6976
  fallback = "-",
6695
6977
  color = "inherit",
6696
6978
  className,
6697
6979
  ...props
6698
6980
  }) {
6699
- if (value === null || value === void 0) {
6700
- return /* @__PURE__ */ jsx(reactNative.Text, { className: cn(colorStyles8[color], className), ...props, children: fallback });
6981
+ const [now, setNow] = React24.useState(() => Date.now());
6982
+ React24.useEffect(() => {
6983
+ if (!live) return;
6984
+ const id = setInterval(() => setNow(Date.now()), refreshMs);
6985
+ return () => clearInterval(id);
6986
+ }, [live, refreshMs]);
6987
+ const effectiveValue = live ? now : value;
6988
+ const text = effectiveValue === null || effectiveValue === void 0 ? fallback : formatDate(effectiveValue, format, isUTC, hour12, seconds);
6989
+ if (variant) {
6990
+ return /* @__PURE__ */ jsx(Typography, { variant, color, className, ...props, children: text });
6701
6991
  }
6702
- const formattedDate = formatDate(value, format, isUTC);
6703
- return /* @__PURE__ */ jsx(reactNative.Text, { className: cn(colorStyles8[color], className), ...props, children: formattedDate });
6992
+ return /* @__PURE__ */ jsx(reactNative.Text, { className: cn(colorStyles8[color], className), ...props, children: text });
6704
6993
  }
6705
6994
  function formatDateTime(value, format = "datetime", isUTC = false, fallback = "-") {
6706
6995
  if (value === null || value === void 0) {
@@ -6768,7 +7057,7 @@ var variantColors = {
6768
7057
  plain: { backgroundColor: "#1C1C1C", borderColor: "#1F1F1F" },
6769
7058
  pr: {
6770
7059
  backgroundColor: "rgba(255, 121, 0, 0.12)",
6771
- borderColor: "rgba(255, 121, 0, 0.12)"
7060
+ borderColor: "rgba(255, 121, 0, 0.3)"
6772
7061
  }
6773
7062
  };
6774
7063
  function BaseBadge({
@@ -6849,7 +7138,7 @@ function WeightBadge({
6849
7138
  ...props
6850
7139
  }) {
6851
7140
  const config = baseBadgeSizeConfig[size];
6852
- const textColor = isPr ? "#FF7900" : "#9CA3AF";
7141
+ const textColor = isPr ? getSemanticColors("dark")["brand-primary"] : "#9CA3AF";
6853
7142
  const iconColor = textColor;
6854
7143
  const repMaxClause = reps != null ? `${reps} rep max` : "Estimated one rep max";
6855
7144
  const deltaLabel = delta != null ? `, ${delta >= 0 ? "+" : ""}${delta}% change` : "";
@@ -6861,7 +7150,7 @@ function WeightBadge({
6861
7150
  size,
6862
7151
  onPress,
6863
7152
  className,
6864
- icon: showIcon ? /* @__PURE__ */ jsx(lucideReact.Dumbbell, { size: config.iconSize, color: iconColor, strokeWidth: 2 }) : void 0,
7153
+ icon: showIcon ? /* @__PURE__ */ jsx(DumbbellIcon, { size: config.iconSize, color: iconColor, strokeWidth: 2 }) : void 0,
6865
7154
  accessibilityLabel: fullLabel,
6866
7155
  testID: "weight-badge",
6867
7156
  ...props,
@@ -6908,7 +7197,7 @@ function WeightBadge({
6908
7197
  fontFamily: '"Space Grotesk", sans-serif',
6909
7198
  fontSize: config.fontSize,
6910
7199
  marginLeft: 4,
6911
- color: delta >= 0 ? "#4caf50" : "#ef5350"
7200
+ color: delta >= 0 ? getSemanticColors("dark")["result-improve"] : getSemanticColors("dark")["result-degrade"]
6912
7201
  },
6913
7202
  testID: "weight-badge-delta",
6914
7203
  children: [
@@ -6922,6 +7211,7 @@ function WeightBadge({
6922
7211
  }
6923
7212
  );
6924
7213
  }
7214
+ var BRAND_PRIMARY = getSemanticColors("dark")["brand-primary"];
6925
7215
  var typeLabels = {
6926
7216
  e1rm: "PR e1RM",
6927
7217
  weight: "PR Weight",
@@ -6965,7 +7255,7 @@ function PrBadge({
6965
7255
  accessibilityLabel: `Personal record: ${resolvedLabel}`,
6966
7256
  testID: "pr-badge-star",
6967
7257
  ...props,
6968
- children: /* @__PURE__ */ jsx(lucideReact.Star, { size: 14, color: "#FF7900", fill: "#FF7900", strokeWidth: 2 })
7258
+ children: /* @__PURE__ */ jsx(StarIcon, { size: 14, color: BRAND_PRIMARY, fill: BRAND_PRIMARY, strokeWidth: 2 })
6969
7259
  }
6970
7260
  ) : /* @__PURE__ */ jsx(
6971
7261
  BaseBadge,
@@ -6978,8 +7268,8 @@ function PrBadge({
6978
7268
  reactNative.Text,
6979
7269
  {
6980
7270
  style: {
6981
- fontSize: 10,
6982
- color: "#FF7900",
7271
+ fontSize: 12,
7272
+ color: BRAND_PRIMARY,
6983
7273
  fontWeight: "700",
6984
7274
  fontFamily: "Inter, sans-serif"
6985
7275
  },
@@ -7004,17 +7294,18 @@ function PrBadge({
7004
7294
  }
7005
7295
  return badge;
7006
7296
  }
7297
+ var t = getSemanticColors("dark");
7007
7298
  var solidVariantColors = {
7008
- success: "#14B8A6",
7009
- warning: "#FFB020",
7010
- error: "#D14343",
7299
+ success: t["status-success"],
7300
+ warning: t["status-warning"],
7301
+ error: t["status-error"],
7011
7302
  neutral: "#6B7280"
7012
7303
  };
7013
7304
  var ringVariantStyles = {
7014
7305
  "on-track": {
7015
- backgroundColor: "rgba(20,184,166,0.15)",
7306
+ backgroundColor: "rgba(46,213,115,0.15)",
7016
7307
  borderWidth: 1,
7017
- borderColor: "rgba(20,184,166,0.3)"
7308
+ borderColor: "rgba(46,213,115,0.3)"
7018
7309
  },
7019
7310
  deviation: {
7020
7311
  backgroundColor: "rgba(245,158,11,0.15)",
@@ -7029,11 +7320,11 @@ var ringVariantStyles = {
7029
7320
  }
7030
7321
  };
7031
7322
  var glowStyles = {
7032
- success: { boxShadow: "0 0 4px rgba(20,184,166,0.4)" },
7323
+ success: { boxShadow: "0 0 4px rgba(46,213,115,0.4)" },
7033
7324
  warning: { boxShadow: "0 0 4px rgba(245,158,11,0.4)" },
7034
7325
  error: { boxShadow: "0 0 4px rgba(239,68,68,0.4)" },
7035
7326
  neutral: { boxShadow: "0 0 4px rgba(107,114,128,0.4)" },
7036
- "on-track": { boxShadow: "0 0 4px rgba(20,184,166,0.4)" },
7327
+ "on-track": { boxShadow: "0 0 4px rgba(46,213,115,0.4)" },
7037
7328
  deviation: { boxShadow: "0 0 4px rgba(245,158,11,0.4)" },
7038
7329
  future: { boxShadow: "0 0 4px rgba(107,114,128,0.4)" }
7039
7330
  };
@@ -7047,8 +7338,8 @@ var iconColors2 = {
7047
7338
  warning: "#6B4000",
7048
7339
  error: "#5C1A1A",
7049
7340
  neutral: "#D1D5DB",
7050
- "on-track": "#14B8A6",
7051
- deviation: "#FFB020",
7341
+ "on-track": t["status-success"],
7342
+ deviation: t["status-warning"],
7052
7343
  future: "#6B7280"
7053
7344
  };
7054
7345
  var sizeDimensions = {
@@ -7129,6 +7420,53 @@ function StatusDot({
7129
7420
  }
7130
7421
  return /* @__PURE__ */ jsx(reactNative.View, { className, ...props, children: dot });
7131
7422
  }
7423
+
7424
+ // src/theme/workout-tokens.ts
7425
+ var WORKOUT_TOKENS = {
7426
+ // Canonical 4-band performance scale — the single source for BOTH the
7427
+ // VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
7428
+ // is intentionally inverted between the two consumers: for velocity, green =
7429
+ // fastest/best; for RPE, green = easiest. This is the [0,1,2,4] subsample of
7430
+ // the canonical `sequentialEffort` primitive. The `orange` key is the band
7431
+ // label. Consumed inline (RN).
7432
+ scale: {
7433
+ green: sequentialEffort[0],
7434
+ yellow: sequentialEffort[1],
7435
+ orange: sequentialEffort[2],
7436
+ // gold band
7437
+ red: sequentialEffort[4]
7438
+ },
7439
+ // BodyMap volume heatmap — the canonical `divergingScale` (under → optimal →
7440
+ // over): a true diverging shape with a light green center, cool-blue under-
7441
+ // trained end and warm-red over-reaching end (colorblind-robust in lightness).
7442
+ heatmap: {
7443
+ // no training data
7444
+ under: divergingScale[0],
7445
+ // below MEV
7446
+ maintenance: divergingScale[1],
7447
+ // MEV to MAV
7448
+ productive: divergingScale[2],
7449
+ // optimal center — MAV to MRV
7450
+ approaching: divergingScale[3],
7451
+ // near MRV
7452
+ over: divergingScale[4]
7453
+ // over MRV
7454
+ },
7455
+ // Surface and border colors were removed here: those flip with the theme, so
7456
+ // components use the CSS custom properties (var(--color-surface-*/border-*))
7457
+ // directly. Only theme-independent data-viz values remain below.
7458
+ // Intensity bar specific — over-target tiers deepen along the red ramp.
7459
+ intensity: {
7460
+ over1: primitiveRamps.red[600],
7461
+ // matches status-error
7462
+ over2: primitiveRamps.red[700],
7463
+ over3: primitiveRamps.red[800]},
7464
+ // Placeholder strip
7465
+ placeholder: {
7466
+ fill: "#3A3A3A"
7467
+ }};
7468
+
7469
+ // src/components/custom/Workout/PlaceholderStrip.tsx
7132
7470
  function SingleStrip({
7133
7471
  width,
7134
7472
  className,
@@ -7137,14 +7475,12 @@ function SingleStrip({
7137
7475
  return /* @__PURE__ */ jsx(
7138
7476
  reactNative.View,
7139
7477
  {
7140
- className: cn("border-border-strong", className),
7478
+ className,
7141
7479
  style: [
7142
7480
  {
7143
7481
  height: 3,
7144
- backgroundColor: "transparent",
7145
- borderWidth: 1,
7146
- borderStyle: "dashed",
7147
- borderRadius: 1,
7482
+ backgroundColor: WORKOUT_TOKENS.placeholder.fill,
7483
+ borderRadius: 2,
7148
7484
  opacity: 0.5
7149
7485
  },
7150
7486
  width != null ? { width } : { flex: 1 }
@@ -7177,13 +7513,10 @@ function SegmentedStrip({
7177
7513
  children: Array.from({ length: segments }, (_, i) => /* @__PURE__ */ jsx(
7178
7514
  reactNative.View,
7179
7515
  {
7180
- className: "border-border-strong",
7181
7516
  style: {
7182
7517
  flex: 1,
7183
7518
  height: 3,
7184
- backgroundColor: "transparent",
7185
- borderWidth: 1,
7186
- borderStyle: "dashed",
7519
+ backgroundColor: WORKOUT_TOKENS.placeholder.fill,
7187
7520
  borderRadius: 1,
7188
7521
  minWidth: 4
7189
7522
  },
@@ -7206,23 +7539,6 @@ function PlaceholderStrip({
7206
7539
  return /* @__PURE__ */ jsx(SingleStrip, { ...props });
7207
7540
  }
7208
7541
 
7209
- // src/theme/workout-tokens.ts
7210
- var WORKOUT_TOKENS = {
7211
- // Canonical 4-band performance scale — the single source for BOTH the
7212
- // VelocityStrip zone bars and the SetRow RPE color (TD-03.43). The direction
7213
- // is intentionally inverted between the two consumers: for velocity, green =
7214
- // fastest/best; for RPE, green = easiest. Each component maps its own
7215
- // thresholds onto these four colors. Kept as vivid data-viz values (distinct
7216
- // from the muted status tokens) and theme-independent, so — like the heatmap
7217
- // scale below — they are consumed inline as a JS import rather than CSS vars.
7218
- // A JS import also resolves on native RN, unlike var(--…) strings.
7219
- scale: {
7220
- green: "#2ed573",
7221
- yellow: "#ffd43b",
7222
- orange: "#ffa502",
7223
- red: "#ff4757"
7224
- }};
7225
-
7226
7542
  // src/utils/workout-format.ts
7227
7543
  function roundRpe(rpe) {
7228
7544
  return Math.round(rpe * 2) / 2;
@@ -7261,91 +7577,338 @@ function formatPrescription(p) {
7261
7577
  return full.length > 0 ? full : null;
7262
7578
  }
7263
7579
 
7264
- // src/components/custom/Workout/TempoDisplay.tsx
7265
- var INTER = "Inter, sans-serif";
7266
- var TEXT_TERTIARY = "#6B7280";
7267
- var phaseColors = {
7268
- eccentric: "#FFB020",
7269
- // status-warning (amber)
7270
- pauseBottom: TEXT_TERTIARY,
7271
- concentric: "#14B8A6",
7272
- // status-success (teal)
7273
- pauseTop: TEXT_TERTIARY,
7274
- dash: TEXT_TERTIARY
7275
- };
7276
- function TempoValue({
7277
- value,
7278
- color,
7279
- fontSize
7280
- }) {
7281
- return /* @__PURE__ */ jsx(
7282
- reactNative.Text,
7283
- {
7284
- style: {
7285
- fontFamily: INTER,
7286
- fontSize,
7287
- color,
7288
- fontWeight: "600",
7289
- letterSpacing: 1
7290
- },
7291
- children: value
7292
- }
7293
- );
7580
+ // src/utils/colors.ts
7581
+ function getStatusColor(status) {
7582
+ return `var(--color-status-${status})`;
7294
7583
  }
7295
- function TempoSeparator({ color, fontSize }) {
7296
- return /* @__PURE__ */ jsx(
7297
- reactNative.Text,
7298
- {
7299
- style: {
7300
- fontFamily: INTER,
7301
- fontSize,
7302
- color,
7303
- fontWeight: "600",
7304
- letterSpacing: 1
7305
- },
7306
- children: "-"
7584
+ function getResultColor(result) {
7585
+ return `var(--color-result-${result})`;
7586
+ }
7587
+ function alpha(color, opacity) {
7588
+ const clampedOpacity = Math.max(0, Math.min(1, opacity));
7589
+ if (color.startsWith("#")) {
7590
+ const hex = color.slice(1);
7591
+ let r, g, b;
7592
+ if (hex.length === 3) {
7593
+ r = parseInt(hex[0] + hex[0], 16);
7594
+ g = parseInt(hex[1] + hex[1], 16);
7595
+ b = parseInt(hex[2] + hex[2], 16);
7596
+ } else if (hex.length === 6) {
7597
+ r = parseInt(hex.slice(0, 2), 16);
7598
+ g = parseInt(hex.slice(2, 4), 16);
7599
+ b = parseInt(hex.slice(4, 6), 16);
7600
+ } else {
7601
+ return color;
7307
7602
  }
7308
- );
7603
+ return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
7604
+ }
7605
+ const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
7606
+ if (rgbMatch) {
7607
+ const [, r, g, b] = rgbMatch;
7608
+ return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
7609
+ }
7610
+ const rgbaMatch = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/);
7611
+ if (rgbaMatch) {
7612
+ const [, r, g, b] = rgbaMatch;
7613
+ return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
7614
+ }
7615
+ return color;
7309
7616
  }
7310
- function TempoDisplay({
7311
- tempo,
7312
- size = "md",
7313
- colored = true,
7314
- showInfo = true,
7315
- onPress,
7617
+ function getLuminance(color) {
7618
+ if (!color.startsWith("#")) return "dark";
7619
+ const hex = color.slice(1);
7620
+ let r, g, b;
7621
+ if (hex.length === 3) {
7622
+ r = parseInt(hex[0] + hex[0], 16);
7623
+ g = parseInt(hex[1] + hex[1], 16);
7624
+ b = parseInt(hex[2] + hex[2], 16);
7625
+ } else if (hex.length === 6) {
7626
+ r = parseInt(hex.slice(0, 2), 16);
7627
+ g = parseInt(hex.slice(2, 4), 16);
7628
+ b = parseInt(hex.slice(4, 6), 16);
7629
+ } else {
7630
+ return "dark";
7631
+ }
7632
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
7633
+ return luminance > 0.5 ? "light" : "dark";
7634
+ }
7635
+ function getContrastText(backgroundColor) {
7636
+ return getLuminance(backgroundColor) === "light" ? "#000000" : "#FFFFFF";
7637
+ }
7638
+ var t2 = getSemanticColors("dark");
7639
+ var TEMPO_PACING = {
7640
+ behindThresholdPct: 0.15,
7641
+ minPhaseDurationMs: 500
7642
+ };
7643
+ function getTempoPacingState(elapsedMs, targetMs) {
7644
+ if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
7645
+ const ratio = elapsedMs / targetMs;
7646
+ if (ratio > 1 + TEMPO_PACING.behindThresholdPct) return "behind";
7647
+ return "on-pace";
7648
+ }
7649
+ function getTempoFillPct(elapsedMs, targetMs) {
7650
+ if (!targetMs) return 100;
7651
+ return Math.min(100, elapsedMs / targetMs * 100);
7652
+ }
7653
+ var PHASE_ORDER = ["concentric", "hold", "eccentric"];
7654
+ var PHASE_CONFIG = {
7655
+ concentric: { label: "Con", color: t2["status-success"], flex: 2 },
7656
+ hold: { label: "Hold", color: t2["brand-primary"], flex: 1 },
7657
+ eccentric: { label: "Ecc", color: t2["status-warning"], flex: 3 }
7658
+ };
7659
+ function TempoBar({
7660
+ activePhase,
7661
+ phaseElapsedMs,
7662
+ completed,
7663
+ target,
7316
7664
  className,
7317
7665
  ...props
7318
7666
  }) {
7319
- const [showTooltip, setShowTooltip] = React24.useState(false);
7320
- const [eccentric, pauseBottom, concentric, pauseTop] = roundTempo(tempo);
7321
- const isSm = size === "sm";
7322
- const fontSize = isSm ? 9 : 11;
7323
- const monoColor = TEXT_TERTIARY;
7324
- const handlePress = React24.useCallback(() => {
7325
- if (onPress) {
7326
- onPress();
7327
- }
7328
- if (showInfo) {
7329
- setShowTooltip((prev) => !prev);
7330
- }
7331
- }, [onPress, showInfo]);
7332
- const content = /* @__PURE__ */ jsxs(
7333
- reactNative.View,
7334
- {
7335
- className,
7336
- style: {
7337
- flexDirection: "row",
7338
- alignItems: "center",
7339
- backgroundColor: "#1C1C1C",
7340
- paddingHorizontal: isSm ? 6 : 8,
7341
- paddingVertical: 3,
7342
- borderRadius: 4
7343
- },
7344
- ...props,
7345
- children: [
7346
- /* @__PURE__ */ jsx(
7347
- reactNative.Text,
7348
- {
7667
+ return /* @__PURE__ */ jsxs(reactNative.View, { className, testID: "tempo-bar", ...props, children: [
7668
+ /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", marginBottom: 4 }, children: PHASE_ORDER.map((phase) => {
7669
+ const config = PHASE_CONFIG[phase];
7670
+ return /* @__PURE__ */ jsx(reactNative.View, { style: { flex: config.flex, alignItems: "center" }, children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 10, color: t2["text-disabled"] }, children: config.label }) }, phase);
7671
+ }) }),
7672
+ /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", gap: 2, height: 20 }, children: PHASE_ORDER.map((phase) => {
7673
+ const config = PHASE_CONFIG[phase];
7674
+ const isActive = activePhase === phase;
7675
+ const completedMs = completed?.[phase];
7676
+ const targetSec = target?.[phase];
7677
+ const targetMs = targetSec != null ? targetSec * 1e3 : null;
7678
+ return /* @__PURE__ */ jsx(
7679
+ reactNative.View,
7680
+ {
7681
+ style: {
7682
+ flex: config.flex,
7683
+ backgroundColor: alpha("#ffffff", 0.06),
7684
+ borderRadius: 4,
7685
+ overflow: "hidden"
7686
+ },
7687
+ children: isActive ? /* @__PURE__ */ jsx(
7688
+ ActiveSegment,
7689
+ {
7690
+ config,
7691
+ phaseElapsedMs,
7692
+ targetMs
7693
+ }
7694
+ ) : completedMs != null ? /* @__PURE__ */ jsx(CompletedSegment, { config, completedMs, targetMs }) : null
7695
+ },
7696
+ phase
7697
+ );
7698
+ }) })
7699
+ ] });
7700
+ }
7701
+ function ActiveSegment({
7702
+ config,
7703
+ phaseElapsedMs,
7704
+ targetMs
7705
+ }) {
7706
+ const pacing = getTempoPacingState(phaseElapsedMs, targetMs);
7707
+ const barColor = pacing === "behind" ? t2["status-error"] : config.color;
7708
+ const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
7709
+ const label = targetMs ? `${formatDuration(phaseElapsedMs)} / ${formatDuration(targetMs)}` : formatDuration(phaseElapsedMs);
7710
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { height: "100%", position: "relative" }, testID: `tempo-segment-active-${config.label}`, children: [
7711
+ /* @__PURE__ */ jsx(
7712
+ reactNative.View,
7713
+ {
7714
+ style: {
7715
+ position: "absolute",
7716
+ left: 0,
7717
+ top: 0,
7718
+ bottom: 0,
7719
+ width: `${fillPct}%`,
7720
+ backgroundColor: alpha(barColor, 0.3),
7721
+ borderRadius: 4
7722
+ }
7723
+ }
7724
+ ),
7725
+ /* @__PURE__ */ jsx(
7726
+ reactNative.View,
7727
+ {
7728
+ style: {
7729
+ height: "100%",
7730
+ flexDirection: "row",
7731
+ alignItems: "center",
7732
+ justifyContent: "center"
7733
+ },
7734
+ children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, fontWeight: "700", color: barColor }, children: label })
7735
+ }
7736
+ )
7737
+ ] });
7738
+ }
7739
+ function CompletedSegment({
7740
+ config,
7741
+ completedMs,
7742
+ targetMs
7743
+ }) {
7744
+ const pacing = getTempoPacingState(completedMs, targetMs);
7745
+ const hitTarget = pacing !== "behind";
7746
+ const indicator = targetMs && targetMs >= TEMPO_PACING.minPhaseDurationMs ? hitTarget ? " \u2713" : " \u2717" : "";
7747
+ return /* @__PURE__ */ jsx(
7748
+ reactNative.View,
7749
+ {
7750
+ style: {
7751
+ height: "100%",
7752
+ flexDirection: "row",
7753
+ alignItems: "center",
7754
+ justifyContent: "center",
7755
+ backgroundColor: alpha(config.color, 0.15)
7756
+ },
7757
+ testID: `tempo-segment-completed-${config.label}`,
7758
+ children: /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 12, fontWeight: "500", color: alpha(config.color, 0.7) }, children: [
7759
+ formatDuration(completedMs),
7760
+ indicator
7761
+ ] })
7762
+ }
7763
+ );
7764
+ }
7765
+ function formatDuration(ms) {
7766
+ const s = ms / 1e3;
7767
+ return s < 10 ? s.toFixed(1) : Math.round(s).toString();
7768
+ }
7769
+
7770
+ // src/components/custom/Workout/TempoDisplay.tsx
7771
+ var t3 = getSemanticColors("dark");
7772
+ var INTER = "Inter, sans-serif";
7773
+ var TEXT_TERTIARY = "#6B7280";
7774
+ var STATUS_ERROR = t3["status-error"];
7775
+ var phaseColors = {
7776
+ eccentric: t3["status-warning"],
7777
+ pauseBottom: TEXT_TERTIARY,
7778
+ concentric: t3["status-success"],
7779
+ pauseTop: TEXT_TERTIARY,
7780
+ dash: TEXT_TERTIARY
7781
+ };
7782
+ function TempoValue({
7783
+ value,
7784
+ color,
7785
+ fontSize
7786
+ }) {
7787
+ return /* @__PURE__ */ jsx(
7788
+ reactNative.Text,
7789
+ {
7790
+ style: {
7791
+ fontFamily: INTER,
7792
+ fontSize,
7793
+ color,
7794
+ fontWeight: "600",
7795
+ letterSpacing: 1
7796
+ },
7797
+ children: value
7798
+ }
7799
+ );
7800
+ }
7801
+ function TempoSeparator({ color, fontSize }) {
7802
+ return /* @__PURE__ */ jsx(
7803
+ reactNative.Text,
7804
+ {
7805
+ style: {
7806
+ fontFamily: INTER,
7807
+ fontSize,
7808
+ color,
7809
+ fontWeight: "600",
7810
+ letterSpacing: 1
7811
+ },
7812
+ children: "-"
7813
+ }
7814
+ );
7815
+ }
7816
+ var LIVE_PHASES = [
7817
+ { key: "eccentric", color: phaseColors.eccentric },
7818
+ { key: "pauseBottom", color: phaseColors.pauseBottom },
7819
+ { key: "concentric", color: phaseColors.concentric },
7820
+ { key: "pauseTop", color: phaseColors.pauseTop }
7821
+ ];
7822
+ function LiveTempoCell({
7823
+ value,
7824
+ color,
7825
+ isActive,
7826
+ phaseElapsedMs,
7827
+ fontSize
7828
+ }) {
7829
+ if (!isActive) {
7830
+ return /* @__PURE__ */ jsx(TempoValue, { value, color: alpha(color, 0.35), fontSize });
7831
+ }
7832
+ const targetMs = value > 0 ? value * 1e3 : null;
7833
+ const barColor = getTempoPacingState(phaseElapsedMs, targetMs) === "behind" ? STATUS_ERROR : color;
7834
+ const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
7835
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "relative" }, testID: "tempo-live-active", children: [
7836
+ /* @__PURE__ */ jsx(
7837
+ reactNative.View,
7838
+ {
7839
+ style: {
7840
+ position: "absolute",
7841
+ left: 0,
7842
+ top: 0,
7843
+ bottom: 0,
7844
+ width: `${fillPct}%`,
7845
+ backgroundColor: alpha(barColor, 0.3),
7846
+ borderRadius: 2
7847
+ }
7848
+ }
7849
+ ),
7850
+ /* @__PURE__ */ jsx(TempoValue, { value, color: barColor, fontSize })
7851
+ ] });
7852
+ }
7853
+ function LiveTempoRow({
7854
+ values,
7855
+ live,
7856
+ fontSize
7857
+ }) {
7858
+ return /* @__PURE__ */ jsx(jsxRuntime.Fragment, { children: LIVE_PHASES.map((phase, i) => /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row" }, children: [
7859
+ i > 0 && /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
7860
+ /* @__PURE__ */ jsx(
7861
+ LiveTempoCell,
7862
+ {
7863
+ value: values[i],
7864
+ color: phase.color,
7865
+ isActive: live.activePhase === phase.key,
7866
+ phaseElapsedMs: live.phaseElapsedMs,
7867
+ fontSize
7868
+ }
7869
+ )
7870
+ ] }, phase.key)) });
7871
+ }
7872
+ function TempoDisplay({
7873
+ tempo,
7874
+ size = "md",
7875
+ colored = true,
7876
+ showInfo = true,
7877
+ live,
7878
+ onPress,
7879
+ className,
7880
+ ...props
7881
+ }) {
7882
+ const [showTooltip, setShowTooltip] = React24.useState(false);
7883
+ const [eccentric, pauseBottom, concentric, pauseTop] = roundTempo(tempo);
7884
+ const isSm = size === "sm";
7885
+ const fontSize = isSm ? 9 : 11;
7886
+ const monoColor = TEXT_TERTIARY;
7887
+ const handlePress = React24.useCallback(() => {
7888
+ if (onPress) {
7889
+ onPress();
7890
+ }
7891
+ if (showInfo) {
7892
+ setShowTooltip((prev) => !prev);
7893
+ }
7894
+ }, [onPress, showInfo]);
7895
+ const content = /* @__PURE__ */ jsxs(
7896
+ reactNative.View,
7897
+ {
7898
+ className,
7899
+ style: {
7900
+ flexDirection: "row",
7901
+ alignItems: "center",
7902
+ backgroundColor: "#1C1C1C",
7903
+ paddingHorizontal: isSm ? 6 : 8,
7904
+ paddingVertical: 3,
7905
+ borderRadius: 4
7906
+ },
7907
+ ...props,
7908
+ children: [
7909
+ /* @__PURE__ */ jsx(
7910
+ reactNative.Text,
7911
+ {
7349
7912
  style: {
7350
7913
  fontFamily: INTER,
7351
7914
  fontSize: 9,
@@ -7358,7 +7921,14 @@ function TempoDisplay({
7358
7921
  children: "TEMPO"
7359
7922
  }
7360
7923
  ),
7361
- /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row" }, testID: "tempo-value", children: colored ? /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
7924
+ /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row" }, testID: "tempo-value", children: live ? /* @__PURE__ */ jsx(
7925
+ LiveTempoRow,
7926
+ {
7927
+ values: [eccentric, pauseBottom, concentric, pauseTop],
7928
+ live,
7929
+ fontSize
7930
+ }
7931
+ ) : colored ? /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
7362
7932
  /* @__PURE__ */ jsx(TempoValue, { value: eccentric, color: phaseColors.eccentric, fontSize }),
7363
7933
  /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
7364
7934
  /* @__PURE__ */ jsx(TempoValue, { value: pauseBottom, color: phaseColors.pauseBottom, fontSize }),
@@ -7470,209 +8040,18 @@ function TempoDisplay({
7470
8040
  }
7471
8041
  );
7472
8042
  }
7473
-
7474
- // src/utils/colors.ts
7475
- function getStatusColor(status) {
7476
- return `var(--color-status-${status})`;
8043
+ var t4 = getSemanticColors("dark");
8044
+ function getDotColor(deviation) {
8045
+ const abs = Math.abs(deviation);
8046
+ if (deviation < -0.3) return t4["status-success"];
8047
+ if (abs <= 0.3) return "#6B7280";
8048
+ if (abs <= 0.7) return t4["status-warning"];
8049
+ return t4["status-error"];
7477
8050
  }
7478
- function getResultColor(result) {
7479
- return `var(--color-result-${result})`;
7480
- }
7481
- function alpha(color, opacity) {
7482
- const clampedOpacity = Math.max(0, Math.min(1, opacity));
7483
- if (color.startsWith("#")) {
7484
- const hex = color.slice(1);
7485
- let r, g, b;
7486
- if (hex.length === 3) {
7487
- r = parseInt(hex[0] + hex[0], 16);
7488
- g = parseInt(hex[1] + hex[1], 16);
7489
- b = parseInt(hex[2] + hex[2], 16);
7490
- } else if (hex.length === 6) {
7491
- r = parseInt(hex.slice(0, 2), 16);
7492
- g = parseInt(hex.slice(2, 4), 16);
7493
- b = parseInt(hex.slice(4, 6), 16);
7494
- } else {
7495
- return color;
7496
- }
7497
- return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
7498
- }
7499
- const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
7500
- if (rgbMatch) {
7501
- const [, r, g, b] = rgbMatch;
7502
- return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
7503
- }
7504
- const rgbaMatch = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/);
7505
- if (rgbaMatch) {
7506
- const [, r, g, b] = rgbaMatch;
7507
- return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`;
7508
- }
7509
- return color;
7510
- }
7511
- function getLuminance(color) {
7512
- if (!color.startsWith("#")) return "dark";
7513
- const hex = color.slice(1);
7514
- let r, g, b;
7515
- if (hex.length === 3) {
7516
- r = parseInt(hex[0] + hex[0], 16);
7517
- g = parseInt(hex[1] + hex[1], 16);
7518
- b = parseInt(hex[2] + hex[2], 16);
7519
- } else if (hex.length === 6) {
7520
- r = parseInt(hex.slice(0, 2), 16);
7521
- g = parseInt(hex.slice(2, 4), 16);
7522
- b = parseInt(hex.slice(4, 6), 16);
7523
- } else {
7524
- return "dark";
7525
- }
7526
- const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
7527
- return luminance > 0.5 ? "light" : "dark";
7528
- }
7529
- function getContrastText(backgroundColor) {
7530
- return getLuminance(backgroundColor) === "light" ? "#000000" : "#FFFFFF";
7531
- }
7532
-
7533
- // src/components/custom/Workout/TempoBar.tsx
7534
- var t = getSemanticColors("dark");
7535
- var TEMPO_PACING = {
7536
- behindThresholdPct: 0.15,
7537
- minPhaseDurationMs: 500
7538
- };
7539
- function getTempoPacingState(elapsedMs, targetMs) {
7540
- if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
7541
- const ratio = elapsedMs / targetMs;
7542
- if (ratio > 1 + TEMPO_PACING.behindThresholdPct) return "behind";
7543
- return "on-pace";
7544
- }
7545
- function getTempoFillPct(elapsedMs, targetMs) {
7546
- if (!targetMs) return 100;
7547
- return Math.min(100, elapsedMs / targetMs * 100);
7548
- }
7549
- var PHASE_ORDER = ["concentric", "hold", "eccentric"];
7550
- var PHASE_CONFIG = {
7551
- concentric: { label: "Con", color: t["status-success"], flex: 2 },
7552
- hold: { label: "Hold", color: t["brand-primary"], flex: 1 },
7553
- eccentric: { label: "Ecc", color: t["status-warning"], flex: 3 }
7554
- };
7555
- function TempoBar({
7556
- activePhase,
7557
- phaseElapsedMs,
7558
- completed,
7559
- target,
7560
- className,
7561
- ...props
7562
- }) {
7563
- return /* @__PURE__ */ jsxs(reactNative.View, { className, testID: "tempo-bar", ...props, children: [
7564
- /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", marginBottom: 4 }, children: PHASE_ORDER.map((phase) => {
7565
- const config = PHASE_CONFIG[phase];
7566
- return /* @__PURE__ */ jsx(reactNative.View, { style: { flex: config.flex, alignItems: "center" }, children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 10, color: t["text-disabled"] }, children: config.label }) }, phase);
7567
- }) }),
7568
- /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", gap: 2, height: 20 }, children: PHASE_ORDER.map((phase) => {
7569
- const config = PHASE_CONFIG[phase];
7570
- const isActive = activePhase === phase;
7571
- const completedMs = completed?.[phase];
7572
- const targetSec = target?.[phase];
7573
- const targetMs = targetSec != null ? targetSec * 1e3 : null;
7574
- return /* @__PURE__ */ jsx(
7575
- reactNative.View,
7576
- {
7577
- style: {
7578
- flex: config.flex,
7579
- backgroundColor: alpha("#ffffff", 0.06),
7580
- borderRadius: 4,
7581
- overflow: "hidden"
7582
- },
7583
- children: isActive ? /* @__PURE__ */ jsx(
7584
- ActiveSegment,
7585
- {
7586
- config,
7587
- phaseElapsedMs,
7588
- targetMs
7589
- }
7590
- ) : completedMs != null ? /* @__PURE__ */ jsx(CompletedSegment, { config, completedMs, targetMs }) : null
7591
- },
7592
- phase
7593
- );
7594
- }) })
7595
- ] });
7596
- }
7597
- function ActiveSegment({
7598
- config,
7599
- phaseElapsedMs,
7600
- targetMs
7601
- }) {
7602
- const pacing = getTempoPacingState(phaseElapsedMs, targetMs);
7603
- const barColor = pacing === "behind" ? t["status-error"] : config.color;
7604
- const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
7605
- const label = targetMs ? `${formatDuration(phaseElapsedMs)} / ${formatDuration(targetMs)}` : formatDuration(phaseElapsedMs);
7606
- return /* @__PURE__ */ jsxs(reactNative.View, { style: { height: "100%", position: "relative" }, testID: `tempo-segment-active-${config.label}`, children: [
7607
- /* @__PURE__ */ jsx(
7608
- reactNative.View,
7609
- {
7610
- style: {
7611
- position: "absolute",
7612
- left: 0,
7613
- top: 0,
7614
- bottom: 0,
7615
- width: `${fillPct}%`,
7616
- backgroundColor: alpha(barColor, 0.3),
7617
- borderRadius: 4
7618
- }
7619
- }
7620
- ),
7621
- /* @__PURE__ */ jsx(
7622
- reactNative.View,
7623
- {
7624
- style: {
7625
- height: "100%",
7626
- flexDirection: "row",
7627
- alignItems: "center",
7628
- justifyContent: "center"
7629
- },
7630
- children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, fontWeight: "700", color: barColor }, children: label })
7631
- }
7632
- )
7633
- ] });
7634
- }
7635
- function CompletedSegment({
7636
- config,
7637
- completedMs,
7638
- targetMs
7639
- }) {
7640
- const pacing = getTempoPacingState(completedMs, targetMs);
7641
- const hitTarget = pacing !== "behind";
7642
- const indicator = targetMs && targetMs >= TEMPO_PACING.minPhaseDurationMs ? hitTarget ? " \u2713" : " \u2717" : "";
7643
- return /* @__PURE__ */ jsx(
7644
- reactNative.View,
7645
- {
7646
- style: {
7647
- height: "100%",
7648
- flexDirection: "row",
7649
- alignItems: "center",
7650
- justifyContent: "center",
7651
- backgroundColor: alpha(config.color, 0.15)
7652
- },
7653
- testID: `tempo-segment-completed-${config.label}`,
7654
- children: /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 12, fontWeight: "500", color: alpha(config.color, 0.7) }, children: [
7655
- formatDuration(completedMs),
7656
- indicator
7657
- ] })
7658
- }
7659
- );
7660
- }
7661
- function formatDuration(ms) {
7662
- const s = ms / 1e3;
7663
- return s < 10 ? s.toFixed(1) : Math.round(s).toString();
7664
- }
7665
- function getDotColor(deviation) {
7666
- const abs = Math.abs(deviation);
7667
- if (deviation < -0.3) return "#14B8A6";
7668
- if (abs <= 0.3) return "#6B7280";
7669
- if (abs <= 0.7) return "#FFB020";
7670
- return "#D14343";
7671
- }
7672
- function getDeviationDescription(deviation) {
7673
- if (deviation < -0.3) return "lighter than planned";
7674
- if (deviation > 0.3) return "harder than planned";
7675
- return "on plan";
8051
+ function getDeviationDescription(deviation) {
8052
+ if (deviation < -0.3) return "lighter than planned";
8053
+ if (deviation > 0.3) return "harder than planned";
8054
+ return "on plan";
7676
8055
  }
7677
8056
  function DeviationBar({
7678
8057
  deviation,
@@ -7709,7 +8088,7 @@ function DeviationBar({
7709
8088
  width: resolvedWidth,
7710
8089
  borderRadius: 3,
7711
8090
  // react-native-web renders backgroundImage at runtime; not in RN ViewStyle types
7712
- backgroundImage: "linear-gradient(90deg, rgba(20,184,166,0.25) 0%, rgba(107,114,128,0.15) 50%, rgba(255,176,32,0.25) 100%)"
8091
+ backgroundImage: "linear-gradient(90deg, rgba(46,213,115,0.25) 0%, rgba(107,114,128,0.15) 50%, rgba(249,180,21,0.25) 100%)"
7713
8092
  }
7714
8093
  }
7715
8094
  ),
@@ -7735,19 +8114,43 @@ function DeviationBar({
7735
8114
  }
7736
8115
  );
7737
8116
  }
7738
- var TRACK_BG = "rgba(255,255,255,0.06)";
7739
- var THRESHOLD_COLOR = "#FFFFFF";
8117
+ var t5 = getSemanticColors("dark");
8118
+ var TRACK_BG = "#333333";
7740
8119
  var LABEL_COLOR = "#6B7280";
7741
- function getZoneColor(level) {
7742
- if (level >= 0.7) return "#D14343";
7743
- if (level >= 0.4) return "#FFB020";
7744
- return "#14B8A6";
7745
- }
7746
- function clamp01(value) {
8120
+ var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
8121
+ var AT_TARGET_GLOW = "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)";
8122
+ var ZONE = {
8123
+ building: t5["status-success"],
8124
+ // below target ramp-up
8125
+ approaching: t5["status-warning"],
8126
+ // nearing target
8127
+ target: t5["brand-primary"],
8128
+ // exactly at target
8129
+ over1: t5["status-error"],
8130
+ // mild over-target
8131
+ over2: WORKOUT_TOKENS.intensity.over2,
8132
+ // moderate over-target
8133
+ over3: WORKOUT_TOKENS.intensity.over3
8134
+ // severe over-target
8135
+ };
8136
+ var BULGE_SIZE = { 1: 8, 2: 10, 3: 11 };
8137
+ function zoneForPct(pct) {
8138
+ if (pct >= 125) return { color: ZONE.over3, over: 3 };
8139
+ if (pct >= 115) return { color: ZONE.over2, over: 2 };
8140
+ if (pct > 100) return { color: ZONE.over1, over: 1 };
8141
+ if (pct === 100) return { color: ZONE.target, over: 0 };
8142
+ if (pct >= 75) return { color: ZONE.approaching, over: 0 };
8143
+ return { color: ZONE.building, over: 0 };
8144
+ }
8145
+ function clampFill(value) {
7747
8146
  if (value < 0) return 0;
7748
8147
  if (value > 1) return 1;
7749
8148
  return value;
7750
8149
  }
8150
+ function isAtTarget(pct, threshold) {
8151
+ if (threshold == null) return false;
8152
+ return Math.abs(pct - Math.round(threshold * 100)) <= 5;
8153
+ }
7751
8154
  function IntensityBar({
7752
8155
  level,
7753
8156
  threshold,
@@ -7759,62 +8162,62 @@ function IntensityBar({
7759
8162
  ...props
7760
8163
  }) {
7761
8164
  const isVertical = orientation === "vertical";
7762
- const clampedLevel = clamp01(level);
7763
- const percentage = Math.round(clampedLevel * 100);
7764
- const zoneColor = getZoneColor(clampedLevel);
7765
- const [fillAnim] = React24.useState(() => new reactNative.Animated.Value(clampedLevel));
8165
+ const fillLevel = clampFill(level);
8166
+ const pct = Math.round(Math.max(0, level) * 100);
8167
+ const zone = zoneForPct(pct);
8168
+ const atTarget = isAtTarget(pct, threshold);
8169
+ const [fillAnim] = React24.useState(() => new reactNative.Animated.Value(fillLevel));
7766
8170
  React24.useEffect(() => {
7767
8171
  const animation = reactNative.Animated.timing(fillAnim, {
7768
- toValue: clampedLevel,
8172
+ toValue: fillLevel,
7769
8173
  duration: 250,
7770
8174
  useNativeDriver: false
7771
8175
  });
7772
8176
  animation.start();
7773
8177
  return () => animation.stop();
7774
- }, [clampedLevel, fillAnim]);
8178
+ }, [fillLevel, fillAnim]);
7775
8179
  const fillSizePercent = fillAnim.interpolate({
7776
8180
  inputRange: [0, 1],
7777
8181
  outputRange: ["0%", "100%"]
7778
8182
  });
7779
8183
  const trackStyle = isVertical ? { width: thickness, height: size } : { width: size, height: thickness };
7780
- const fillStyle = isVertical ? {
7781
- position: "absolute",
7782
- bottom: 0,
7783
- left: 0,
7784
- right: 0,
7785
- height: fillSizePercent
7786
- } : {
8184
+ const fillStyle = isVertical ? { position: "absolute", bottom: 0, left: 0, right: 0, height: fillSizePercent } : { position: "absolute", top: 0, bottom: 0, left: 0, width: fillSizePercent };
8185
+ const bulgeSize = zone.over === 0 ? 0 : BULGE_SIZE[zone.over];
8186
+ const bulgeStyle = {
7787
8187
  position: "absolute",
7788
- top: 0,
7789
- bottom: 0,
7790
- left: 0,
7791
- width: fillSizePercent
8188
+ width: bulgeSize,
8189
+ height: bulgeSize,
8190
+ borderRadius: bulgeSize / 2,
8191
+ backgroundColor: zone.color,
8192
+ zIndex: 1,
8193
+ ...isVertical ? { top: 0, left: "50%", transform: [{ translateX: -bulgeSize / 2 }] } : { right: 0, top: "50%", transform: [{ translateY: -bulgeSize / 2 }] }
7792
8194
  };
7793
- const thresholdLineStyle = threshold != null ? isVertical ? {
8195
+ const targetPct = `${clampFill(threshold ?? 0) * 100}%`;
8196
+ const targetLineStyle = isVertical ? {
7794
8197
  position: "absolute",
7795
- bottom: `${clamp01(threshold) * 100}%`,
7796
- left: -2,
7797
- right: -2,
7798
- height: 1,
7799
- backgroundColor: THRESHOLD_COLOR,
8198
+ bottom: targetPct,
8199
+ left: -3,
8200
+ right: -3,
8201
+ height: 1.5,
8202
+ backgroundColor: TARGET_LINE_COLOR,
7800
8203
  zIndex: 4
7801
8204
  } : {
7802
8205
  position: "absolute",
7803
- left: `${clamp01(threshold) * 100}%`,
7804
- top: -2,
7805
- bottom: -2,
7806
- width: 1,
7807
- backgroundColor: THRESHOLD_COLOR,
8206
+ left: targetPct,
8207
+ top: -3,
8208
+ bottom: -3,
8209
+ width: 1.5,
8210
+ backgroundColor: TARGET_LINE_COLOR,
7808
8211
  zIndex: 4
7809
- } : void 0;
8212
+ };
7810
8213
  return /* @__PURE__ */ jsxs(
7811
8214
  reactNative.View,
7812
8215
  {
7813
8216
  className,
7814
8217
  style: { alignItems: "center" },
7815
8218
  accessibilityRole: "progressbar",
7816
- accessibilityValue: { min: 0, max: 100, now: percentage },
7817
- accessibilityLabel: `Intensity level: ${percentage}%`,
8219
+ accessibilityValue: { min: 0, max: 100, now: pct },
8220
+ accessibilityLabel: `Intensity level: ${pct}%`,
7818
8221
  testID: "intensity-bar",
7819
8222
  ...props,
7820
8223
  children: [
@@ -7836,13 +8239,32 @@ function IntensityBar({
7836
8239
  style: {
7837
8240
  ...fillStyle,
7838
8241
  borderRadius: 3,
7839
- backgroundColor: zoneColor,
7840
- zIndex: 2
8242
+ backgroundColor: zone.color,
8243
+ zIndex: 2,
8244
+ ...atTarget ? { boxShadow: AT_TARGET_GLOW } : null
7841
8245
  },
7842
8246
  testID: "intensity-fill"
7843
8247
  }
7844
8248
  ),
7845
- threshold != null && /* @__PURE__ */ jsx(reactNative.View, { style: thresholdLineStyle, testID: "intensity-threshold" })
8249
+ zone.over !== 0 && /* @__PURE__ */ jsx(reactNative.View, { style: bulgeStyle, testID: "intensity-bulge" }),
8250
+ threshold != null && /* @__PURE__ */ jsx(reactNative.View, { style: targetLineStyle, testID: "intensity-target" })
8251
+ ]
8252
+ }
8253
+ ),
8254
+ /* @__PURE__ */ jsxs(
8255
+ reactNative.Text,
8256
+ {
8257
+ style: {
8258
+ fontSize: 8,
8259
+ color: LABEL_COLOR,
8260
+ fontFamily: '"Nunito Sans", sans-serif',
8261
+ marginTop: 6
8262
+ },
8263
+ accessibilityElementsHidden: true,
8264
+ testID: "intensity-label",
8265
+ children: [
8266
+ pct,
8267
+ "%"
7846
8268
  ]
7847
8269
  }
7848
8270
  ),
@@ -7854,7 +8276,7 @@ function IntensityBar({
7854
8276
  fontWeight: "500",
7855
8277
  color: LABEL_COLOR,
7856
8278
  fontFamily: "Inter, sans-serif",
7857
- marginTop: 6
8279
+ marginTop: 2
7858
8280
  },
7859
8281
  accessibilityElementsHidden: true,
7860
8282
  testID: "intensity-threshold-label",
@@ -7865,29 +8287,38 @@ function IntensityBar({
7865
8287
  }
7866
8288
  );
7867
8289
  }
8290
+
8291
+ // src/theme/extracted-colors-dataviz.ts
8292
+ var DATAVIZ_CATEGORICAL_PALETTE = categoricalPalette.default;
8293
+ var MESO_ACCENT_GRADIENT_DARK = primitiveRamps.orange[500];
8294
+ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
8295
+ var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
8296
+
8297
+ // src/components/custom/Workout/WorkoutPill.tsx
8298
+ var t6 = getSemanticColors("dark");
7868
8299
  var statusBgColors = {
7869
- completed: "rgba(20,184,166,0.15)",
8300
+ completed: "rgba(46,213,115,0.15)",
7870
8301
  current: "rgba(255,121,0,0.15)",
7871
8302
  next: "transparent",
7872
8303
  upcoming: "#1C1C1C",
7873
8304
  missed: "rgba(209,67,67,0.1)",
7874
- deload: "rgba(147,51,234,0.15)"
8305
+ deload: "rgba(186,41,150,0.15)"
7875
8306
  };
7876
8307
  var statusBorderStyles = {
7877
- completed: { borderWidth: 1, borderColor: "rgba(20,184,166,0.3)" },
8308
+ completed: { borderWidth: 1, borderColor: "rgba(46,213,115,0.3)" },
7878
8309
  current: { borderWidth: 1, borderColor: "rgba(255,121,0,0.3)" },
7879
8310
  next: { borderWidth: 1, borderColor: "rgba(255,121,0,0.4)" },
7880
8311
  upcoming: { borderWidth: 1, borderColor: "#1F1F1F" },
7881
8312
  missed: { borderWidth: 1, borderColor: "rgba(209,67,67,0.25)" },
7882
- deload: { borderWidth: 1, borderColor: "rgba(147,51,234,0.3)" }
8313
+ deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
7883
8314
  };
7884
8315
  var textColors = {
7885
- completed: "#14B8A6",
7886
- current: "#FF7900",
7887
- next: "#FF7900",
8316
+ completed: t6["status-success"],
8317
+ current: t6["brand-primary"],
8318
+ next: t6["brand-primary"],
7888
8319
  upcoming: "#6B7280",
7889
8320
  missed: "rgba(209,67,67,0.7)",
7890
- deload: "#9333ea"
8321
+ deload: WORKOUT_PILL_DELOAD
7891
8322
  };
7892
8323
  function usePulse(enabled) {
7893
8324
  const [opacity] = React24.useState(() => new reactNative.Animated.Value(1));
@@ -8018,6 +8449,13 @@ function WorkoutPill({
8018
8449
  return pill;
8019
8450
  }
8020
8451
  var VEL_COLORS = WORKOUT_TOKENS.scale;
8452
+ var zoneIdToScaleToken = {
8453
+ speed: "green",
8454
+ power: "yellow",
8455
+ strengthSpeed: "orange",
8456
+ maximalStrength: "red",
8457
+ grinding: "red"
8458
+ };
8021
8459
  function getVelocityZoneColor(velocity) {
8022
8460
  if (velocity >= 1) return "vel-green";
8023
8461
  if (velocity >= 0.75) return "vel-yellow";
@@ -8030,47 +8468,62 @@ function getVelocityZoneName(velocity) {
8030
8468
  if (velocity >= 0.5) return "Strength-Speed";
8031
8469
  return "Strength";
8032
8470
  }
8471
+ var zoneHexMap = {
8472
+ "vel-green": VEL_COLORS.green,
8473
+ "vel-yellow": VEL_COLORS.yellow,
8474
+ "vel-orange": VEL_COLORS.orange,
8475
+ "vel-red": VEL_COLORS.red
8476
+ };
8033
8477
  function calculateVelocityLoss(velocities) {
8034
8478
  if (velocities.length < 2) return 0;
8035
- const first = velocities[0];
8479
+ const best = Math.max(...velocities);
8480
+ if (best <= 0) return 0;
8036
8481
  const last = velocities[velocities.length - 1];
8037
- if (first === 0) return 0;
8038
- return Math.round((first - last) / first * 100);
8482
+ return Math.max(0, Math.round((best - last) / best * 100));
8039
8483
  }
8040
8484
  function calculateMeanVelocity(velocities) {
8041
8485
  if (velocities.length === 0) return 0;
8042
8486
  const sum = velocities.reduce((acc, v) => acc + v, 0);
8043
8487
  return sum / velocities.length;
8044
8488
  }
8045
- function getBarColor(zone) {
8046
- switch (zone) {
8047
- case "vel-green":
8048
- return VEL_COLORS.green;
8049
- case "vel-yellow":
8050
- return VEL_COLORS.yellow;
8051
- case "vel-orange":
8052
- return VEL_COLORS.orange;
8053
- case "vel-red":
8054
- return VEL_COLORS.red;
8055
- default:
8056
- return VEL_COLORS.green;
8489
+ function classifyBand(velocity, bands) {
8490
+ for (const band of bands) {
8491
+ if (band.max === null || velocity < band.max) return band;
8057
8492
  }
8493
+ return bands[bands.length - 1];
8494
+ }
8495
+ function bandColor(band) {
8496
+ const token = zoneIdToScaleToken[band.id];
8497
+ return token ? VEL_COLORS[token] : VEL_COLORS.green;
8058
8498
  }
8059
- var zoneHexMap = {
8060
- "vel-green": VEL_COLORS.green,
8061
- "vel-yellow": VEL_COLORS.yellow,
8062
- "vel-orange": VEL_COLORS.orange,
8063
- "vel-red": VEL_COLORS.red
8064
- };
8065
8499
  function getLossStyle(loss) {
8066
8500
  if (loss > 25) return { color: VEL_COLORS.red };
8067
8501
  if (loss > 20) return { color: VEL_COLORS.orange };
8068
8502
  return null;
8069
8503
  }
8504
+ function getReducedMotionPreference() {
8505
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
8506
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
8507
+ }
8508
+ function usePrefersReducedMotion() {
8509
+ const [reduced, setReduced] = React24.useState(getReducedMotionPreference);
8510
+ React24.useEffect(() => {
8511
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
8512
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
8513
+ const handler = () => setReduced(mq.matches);
8514
+ handler();
8515
+ mq.addEventListener?.("change", handler);
8516
+ return () => mq.removeEventListener?.("change", handler);
8517
+ }, []);
8518
+ return reduced;
8519
+ }
8070
8520
  var ANIMATION_DURATION = 400;
8071
8521
  var ANIMATION_EASING = reactNative.Easing.bezier(0.22, 1, 0.36, 1);
8522
+ var POP_EASING = reactNative.Easing.bezier(0.34, 1.56, 0.64, 1);
8072
8523
  function VelocityStrip({
8073
8524
  velocities,
8525
+ zones,
8526
+ liveRepIndex,
8074
8527
  expanded = false,
8075
8528
  onToggle,
8076
8529
  onRepPress,
@@ -8082,10 +8535,48 @@ function VelocityStrip({
8082
8535
  const maxVelocity = Math.max(...velocities, 0);
8083
8536
  const meanVelocity = calculateMeanVelocity(velocities);
8084
8537
  const loss = calculateVelocityLoss(velocities);
8085
- const meanZone = getVelocityZoneName(meanVelocity);
8538
+ const hasZones = zones != null && zones.length > 0;
8539
+ const barColorFor = (v) => hasZones ? bandColor(classifyBand(v, zones)) : zoneHexMap[getVelocityZoneColor(v)];
8540
+ const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
8541
+ const prefersReducedMotion = usePrefersReducedMotion();
8086
8542
  const [heightAnim] = React24.useState(() => new reactNative.Animated.Value(expanded ? 60 : 3));
8087
8543
  const [labelOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
8088
8544
  const [infoOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
8545
+ const [liveScale] = React24.useState(() => new reactNative.Animated.Value(1));
8546
+ const liveVelocity = liveRepIndex != null ? velocities[liveRepIndex] : void 0;
8547
+ const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
8548
+ React24.useEffect(() => {
8549
+ if (variant === "mini" || liveRepIndex == null || liveVelocity == null) return;
8550
+ if (prefersReducedMotion) {
8551
+ liveScale.setValue(1);
8552
+ return;
8553
+ }
8554
+ if (isNewPeak) {
8555
+ liveScale.setValue(1);
8556
+ reactNative.Animated.sequence([
8557
+ reactNative.Animated.timing(liveScale, {
8558
+ toValue: 1.25,
8559
+ duration: 150,
8560
+ easing: reactNative.Easing.out(reactNative.Easing.quad),
8561
+ useNativeDriver: true
8562
+ }),
8563
+ reactNative.Animated.spring(liveScale, {
8564
+ toValue: 1,
8565
+ friction: 3,
8566
+ tension: 140,
8567
+ useNativeDriver: true
8568
+ })
8569
+ ]).start();
8570
+ } else {
8571
+ liveScale.setValue(0.8);
8572
+ reactNative.Animated.timing(liveScale, {
8573
+ toValue: 1,
8574
+ duration: 300,
8575
+ easing: POP_EASING,
8576
+ useNativeDriver: true
8577
+ }).start();
8578
+ }
8579
+ }, [variant, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
8089
8580
  React24.useEffect(() => {
8090
8581
  if (variant === "mini") return;
8091
8582
  reactNative.Animated.parallel([
@@ -8125,7 +8616,7 @@ function VelocityStrip({
8125
8616
  {
8126
8617
  style: {
8127
8618
  flex: 1,
8128
- backgroundColor: getBarColor(getVelocityZoneColor(v)),
8619
+ backgroundColor: barColorFor(v),
8129
8620
  borderRadius: 1,
8130
8621
  minWidth: 4,
8131
8622
  height: "100%"
@@ -8168,8 +8659,35 @@ function VelocityStrip({
8168
8659
  {
8169
8660
  style: { flexDirection: "row", flex: 1, gap: 2, alignItems: "flex-end" },
8170
8661
  children: velocities.map((v, i) => {
8171
- const zone = getVelocityZoneColor(v);
8662
+ const barBackground = barColorFor(v);
8172
8663
  const barHeightPct = maxVelocity > 0 ? Math.round(v / (maxVelocity * 1.15) * 100) : 0;
8664
+ const isLive = liveRepIndex === i;
8665
+ const liveLabelSuffix = isLive ? isNewPeak ? ", latest rep, new set peak" : ", latest rep" : "";
8666
+ const barStyle = expanded ? { height: `${barHeightPct}%`, minHeight: 2, borderTopLeftRadius: 2, borderTopRightRadius: 2, backgroundColor: barBackground } : {
8667
+ minHeight: "100%",
8668
+ borderTopLeftRadius: 2,
8669
+ borderTopRightRadius: 2,
8670
+ borderBottomLeftRadius: 0,
8671
+ borderBottomRightRadius: 0,
8672
+ backgroundColor: barBackground
8673
+ };
8674
+ const barInner = isLive ? /* @__PURE__ */ jsx(
8675
+ reactNative.Animated.View,
8676
+ {
8677
+ style: [barStyle, { transform: [{ scale: liveScale }] }],
8678
+ testID: `velocity-bar-${i}`,
8679
+ accessibilityRole: "image",
8680
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second${liveLabelSuffix}`
8681
+ }
8682
+ ) : /* @__PURE__ */ jsx(
8683
+ reactNative.View,
8684
+ {
8685
+ style: barStyle,
8686
+ testID: `velocity-bar-${i}`,
8687
+ accessibilityRole: "image",
8688
+ accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
8689
+ }
8690
+ );
8173
8691
  const bar = /* @__PURE__ */ jsxs(
8174
8692
  reactNative.View,
8175
8693
  {
@@ -8184,22 +8702,7 @@ function VelocityStrip({
8184
8702
  children: formatVelocity(v)
8185
8703
  }
8186
8704
  ) }),
8187
- /* @__PURE__ */ jsx(
8188
- reactNative.View,
8189
- {
8190
- style: expanded ? { height: `${barHeightPct}%`, minHeight: 2, borderTopLeftRadius: 2, borderTopRightRadius: 2, backgroundColor: zoneHexMap[zone] } : {
8191
- minHeight: "100%",
8192
- borderTopLeftRadius: 2,
8193
- borderTopRightRadius: 2,
8194
- borderBottomLeftRadius: 0,
8195
- borderBottomRightRadius: 0,
8196
- backgroundColor: zoneHexMap[zone]
8197
- },
8198
- testID: `velocity-bar-${i}`,
8199
- accessibilityRole: "image",
8200
- accessibilityLabel: `Rep ${i + 1}: ${formatVelocity(v)} meters per second`
8201
- }
8202
- )
8705
+ barInner
8203
8706
  ]
8204
8707
  },
8205
8708
  i
@@ -8290,12 +8793,13 @@ function VelocityStrip({
8290
8793
  }
8291
8794
  return stripContent;
8292
8795
  }
8796
+ var t7 = getSemanticColors("dark");
8293
8797
  var dotColorMap = {
8294
8798
  untrained: "#2C2C2C",
8295
- behind: "#406D87",
8296
- ontrack: "#14B8A6",
8297
- target: "#FF7900",
8298
- over: "#D14343"
8799
+ behind: t7["brand-secondary"],
8800
+ ontrack: t7["status-success"],
8801
+ target: t7["brand-primary"],
8802
+ over: t7["status-error"]
8299
8803
  };
8300
8804
  function MuscleGroupChip({
8301
8805
  name,
@@ -8367,7 +8871,7 @@ function MuscleGroupChip({
8367
8871
  }
8368
8872
  return content;
8369
8873
  }
8370
- var BRAND_PRIMARY = "#FF7900";
8874
+ var BRAND_PRIMARY2 = getSemanticColors("dark")["brand-primary"];
8371
8875
  function normalizeData(data, height) {
8372
8876
  if (data.length === 0) return [];
8373
8877
  const min = Math.min(...data);
@@ -8404,7 +8908,7 @@ function Sparkline({
8404
8908
  }
8405
8909
  );
8406
8910
  }
8407
- const resolvedColor = color ?? BRAND_PRIMARY;
8911
+ const resolvedColor = color ?? BRAND_PRIMARY2;
8408
8912
  const normalized = normalizeData(data, height);
8409
8913
  const stepX = data.length > 1 ? width / (data.length - 1) : 0;
8410
8914
  const dotSize = 3;
@@ -8532,6 +9036,8 @@ function SetRow({
8532
9036
  rpe,
8533
9037
  unit,
8534
9038
  velocities,
9039
+ velocityZones,
9040
+ velocityLiveRepIndex,
8535
9041
  velocityExpanded,
8536
9042
  onVelocityToggle,
8537
9043
  setType,
@@ -8581,7 +9087,7 @@ function SetRow({
8581
9087
  fontSize: 10,
8582
9088
  fontWeight: "700",
8583
9089
  fontFamily: "Inter, sans-serif",
8584
- color: "#ffa502",
9090
+ color: WORKOUT_TOKENS.scale.orange,
8585
9091
  backgroundColor: "rgba(255,165,2,0.12)",
8586
9092
  paddingVertical: 1,
8587
9093
  paddingHorizontal: 5,
@@ -8734,6 +9240,8 @@ function SetRow({
8734
9240
  VelocityStrip,
8735
9241
  {
8736
9242
  velocities,
9243
+ zones: velocityZones,
9244
+ liveRepIndex: velocityLiveRepIndex,
8737
9245
  expanded: velocityExpanded,
8738
9246
  onToggle: onVelocityToggle,
8739
9247
  variant: onVelocityToggle ? "full" : "mini"
@@ -8745,7 +9253,7 @@ function SetRow({
8745
9253
  }
8746
9254
  );
8747
9255
  }
8748
- var BRAND_PRIMARY2 = "#FF7900";
9256
+ var BRAND_PRIMARY3 = getSemanticColors("dark")["brand-primary"];
8749
9257
  var INPUT_CLASSNAME = "bg-surface-raised border-border-strong text-text-primary";
8750
9258
  var inputStyle = {
8751
9259
  fontSize: 14,
@@ -8874,7 +9382,7 @@ function InputBar({
8874
9382
  onPress: onRecord,
8875
9383
  disabled: !canRecord,
8876
9384
  style: ({ pressed }) => ({
8877
- backgroundColor: BRAND_PRIMARY2,
9385
+ backgroundColor: BRAND_PRIMARY3,
8878
9386
  borderRadius: 8,
8879
9387
  paddingVertical: 10,
8880
9388
  paddingHorizontal: 16,
@@ -8901,14 +9409,15 @@ function InputBar({
8901
9409
  }
8902
9410
  );
8903
9411
  }
8904
- var BRAND_PRIMARY3 = "#FF7900";
9412
+ var BRAND_PRIMARY4 = getSemanticColors("dark")["brand-primary"];
8905
9413
  function RestTimer({
8906
9414
  totalSeconds,
8907
9415
  elapsedMs,
8908
9416
  onSkip,
8909
9417
  onAddTime,
8910
9418
  nextSetInfo,
8911
- visible
9419
+ visible,
9420
+ displayOnly = false
8912
9421
  }) {
8913
9422
  if (!visible) return null;
8914
9423
  const remainingMs = Math.max(0, totalSeconds * 1e3 - elapsedMs);
@@ -9006,7 +9515,7 @@ function RestTimer({
9006
9515
  {
9007
9516
  style: {
9008
9517
  height: "100%",
9009
- backgroundColor: BRAND_PRIMARY3,
9518
+ backgroundColor: BRAND_PRIMARY4,
9010
9519
  borderRadius: 2,
9011
9520
  width: `${progressPct}%`
9012
9521
  },
@@ -9015,7 +9524,7 @@ function RestTimer({
9015
9524
  )
9016
9525
  }
9017
9526
  ),
9018
- /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", gap: 8 }, children: [
9527
+ !displayOnly && /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", gap: 8 }, children: [
9019
9528
  /* @__PURE__ */ jsx(
9020
9529
  reactNative.Pressable,
9021
9530
  {
@@ -9063,7 +9572,7 @@ function RestTimer({
9063
9572
  fontSize: 11,
9064
9573
  fontFamily: "Inter, sans-serif",
9065
9574
  fontWeight: "600",
9066
- color: BRAND_PRIMARY3
9575
+ color: BRAND_PRIMARY4
9067
9576
  },
9068
9577
  children: "Skip"
9069
9578
  }
@@ -9132,6 +9641,7 @@ function CollapsedCard({
9132
9641
  e1rm,
9133
9642
  isPR,
9134
9643
  setVelocities,
9644
+ velocityZones,
9135
9645
  totalPlannedSets,
9136
9646
  supersetPosition
9137
9647
  }) {
@@ -9219,6 +9729,7 @@ function CollapsedCard({
9219
9729
  VelocityStrip,
9220
9730
  {
9221
9731
  velocities,
9732
+ zones: velocityZones,
9222
9733
  variant: "mini",
9223
9734
  testID: `exercise-card-velocity-strip-${i}`
9224
9735
  },
@@ -9454,7 +9965,7 @@ function ExerciseCard(props) {
9454
9965
  }
9455
9966
  var DEFAULTS = {
9456
9967
  label: "SS",
9457
- color: "#FF7900",
9968
+ color: getSemanticColors("dark")["brand-primary"],
9458
9969
  bgBase: "#101010"
9459
9970
  };
9460
9971
  function SupersetWrapper({
@@ -9505,9 +10016,9 @@ function SupersetWrapper({
9505
10016
  }
9506
10017
  );
9507
10018
  }
9508
- var BRAND_PRIMARY4 = "#FF7900";
10019
+ var BRAND_PRIMARY5 = getSemanticColors("dark")["brand-primary"];
9509
10020
  var segmentBgColors = {
9510
- completed: "rgba(20,184,166,0.3)",
10021
+ completed: "rgba(46,213,115,0.3)",
9511
10022
  current: "rgba(255,121,0,0.5)",
9512
10023
  upcoming: "rgba(255,255,255,0.06)"
9513
10024
  };
@@ -9557,7 +10068,7 @@ function MesoSegment({ meso, isActive, onPress }) {
9557
10068
  },
9558
10069
  isActive ? {
9559
10070
  borderWidth: 2,
9560
- borderColor: BRAND_PRIMARY4,
10071
+ borderColor: BRAND_PRIMARY5,
9561
10072
  transform: [{ scale: 1.02 }, { scale: pressScale }]
9562
10073
  } : void 0
9563
10074
  ],
@@ -9571,7 +10082,7 @@ function MesoSegment({ meso, isActive, onPress }) {
9571
10082
  top: 0,
9572
10083
  bottom: 0,
9573
10084
  width: `${progress * 100}%`,
9574
- backgroundColor: BRAND_PRIMARY4
10085
+ backgroundColor: BRAND_PRIMARY5
9575
10086
  },
9576
10087
  testID: `meso-segment-fill-${meso.id}`
9577
10088
  }
@@ -9612,7 +10123,7 @@ function MesoProgressBar({
9612
10123
  }
9613
10124
  );
9614
10125
  }
9615
- var BRAND_PRIMARY5 = "#FF7900";
10126
+ var BRAND_PRIMARY6 = getSemanticColors("dark")["brand-primary"];
9616
10127
  function WeekRow({
9617
10128
  weekNumber,
9618
10129
  totalWeeks,
@@ -9632,12 +10143,12 @@ function WeekRow({
9632
10143
  gap: 12
9633
10144
  };
9634
10145
  if (isDeload) {
9635
- rowStyle.backgroundColor = "rgba(147,51,234,0.06)";
10146
+ rowStyle.backgroundColor = "rgba(186,41,150,0.06)";
9636
10147
  }
9637
10148
  if (isCurrent) {
9638
10149
  rowStyle.backgroundColor = "rgba(255,121,0,0.04)";
9639
10150
  rowStyle.borderLeftWidth = 2;
9640
- rowStyle.borderLeftColor = BRAND_PRIMARY5;
10151
+ rowStyle.borderLeftColor = BRAND_PRIMARY6;
9641
10152
  }
9642
10153
  return /* @__PURE__ */ jsxs(
9643
10154
  reactNative.View,
@@ -9661,7 +10172,7 @@ function WeekRow({
9661
10172
  width: 6,
9662
10173
  height: 6,
9663
10174
  borderRadius: 3,
9664
- backgroundColor: BRAND_PRIMARY5
10175
+ backgroundColor: BRAND_PRIMARY6
9665
10176
  },
9666
10177
  accessibilityElementsHidden: true,
9667
10178
  testID: "week-row-current-dot"
@@ -9675,7 +10186,7 @@ function WeekRow({
9675
10186
  fontSize: 13,
9676
10187
  fontWeight: "700",
9677
10188
  fontFamily: "Inter, sans-serif",
9678
- color: isCurrent ? BRAND_PRIMARY5 : void 0
10189
+ color: isCurrent ? BRAND_PRIMARY6 : void 0
9679
10190
  },
9680
10191
  accessibilityElementsHidden: true,
9681
10192
  children: `W${weekNumber}`
@@ -9714,9 +10225,10 @@ function WeekRow({
9714
10225
  }
9715
10226
  );
9716
10227
  }
10228
+ var t8 = getSemanticColors("dark");
9717
10229
  var COLORS = {
9718
- statusSuccess: "#14B8A6",
9719
- brandPrimary: "#FF7900",
10230
+ statusSuccess: t8["status-success"],
10231
+ brandPrimary: t8["brand-primary"],
9720
10232
  borderDefault: "#1F1F1F",
9721
10233
  brandPrimarySubtle: "rgba(255,121,0,0.06)"
9722
10234
  };
@@ -9858,11 +10370,11 @@ function WorkoutCard({
9858
10370
  }
9859
10371
  );
9860
10372
  }
9861
- var BRAND_PRIMARY6 = "#FF7900";
9862
- var BRAND_PRIMARY_DARK = "#C45E00";
9863
- var BRAND_PRIMARY_LIGHT = "#FFB066";
10373
+ var BRAND_PRIMARY7 = getSemanticColors("dark")["brand-primary"];
10374
+ var BRAND_PRIMARY_DARK = MESO_ACCENT_GRADIENT_DARK;
10375
+ var BRAND_PRIMARY_LIGHT = MESO_ACCENT_GRADIENT_LIGHT;
9864
10376
  var BORDER_DEFAULT = "#1F1F1F";
9865
- var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY6, BRAND_PRIMARY_LIGHT];
10377
+ var ACCENT_STOPS = [BRAND_PRIMARY_DARK, BRAND_PRIMARY7, BRAND_PRIMARY_LIGHT];
9866
10378
  function heatmapColor(percentage) {
9867
10379
  const clamped = Math.max(0, Math.min(100, percentage));
9868
10380
  const opacity = 0.12 + clamped / 100 * 0.78;
@@ -9897,7 +10409,7 @@ function MesoCard({
9897
10409
  }, [highlighted, highlight]);
9898
10410
  const borderColor = highlight.interpolate({
9899
10411
  inputRange: [0, 1],
9900
- outputRange: [BORDER_DEFAULT, BRAND_PRIMARY6]
10412
+ outputRange: [BORDER_DEFAULT, BRAND_PRIMARY7]
9901
10413
  });
9902
10414
  const header = /* @__PURE__ */ jsxs(reactNative.View, { style: { paddingHorizontal: 14, paddingTop: 12, paddingBottom: 10 }, testID: "meso-card-body", children: [
9903
10415
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 8 }, testID: "meso-card-header", children: [
@@ -9957,7 +10469,7 @@ function MesoCard({
9957
10469
  {
9958
10470
  variant: "outline",
9959
10471
  elevation: 2,
9960
- borderColor: highlighted ? BRAND_PRIMARY6 : BORDER_DEFAULT,
10472
+ borderColor: highlighted ? BRAND_PRIMARY7 : BORDER_DEFAULT,
9961
10473
  className,
9962
10474
  style: [
9963
10475
  { borderColor },
@@ -10014,9 +10526,9 @@ function MesoCard({
10014
10526
  }
10015
10527
  ) });
10016
10528
  }
10017
- var BRAND_PRIMARY7 = "#FF7900";
10018
- var RECENT_BORDER = "rgba(255,176,32,0.3)";
10019
- var RECENT_GLOW = "0 0 12px rgba(255,176,32,0.06)";
10529
+ var BRAND_PRIMARY8 = getSemanticColors("dark")["brand-primary"];
10530
+ var RECENT_BORDER = "rgba(249,180,21,0.3)";
10531
+ var RECENT_GLOW = "0 0 12px rgba(249,180,21,0.06)";
10020
10532
  var BORDER_DEFAULT2 = resolveColor("border-default");
10021
10533
  var TYPE_LABELS = {
10022
10534
  e1rm: "e1RM",
@@ -10056,7 +10568,7 @@ function PrRecordRow({ record, index }) {
10056
10568
  },
10057
10569
  testID: `pr-history-modal-record-${index}`,
10058
10570
  children: [
10059
- /* @__PURE__ */ jsx(lucideReact.Star, { size: 16, color: BRAND_PRIMARY7, fill: BRAND_PRIMARY7, strokeWidth: 2 }),
10571
+ /* @__PURE__ */ jsx(StarIcon, { size: 16, color: BRAND_PRIMARY8, fill: BRAND_PRIMARY8, strokeWidth: 2 }),
10060
10572
  /* @__PURE__ */ jsx(
10061
10573
  reactNative.Text,
10062
10574
  {
@@ -10200,11 +10712,12 @@ function PrHistoryModal({
10200
10712
  }
10201
10713
  );
10202
10714
  }
10203
- var BRAND_PRIMARY8 = "#FF7900";
10715
+ var t9 = getSemanticColors("dark");
10716
+ var BRAND_PRIMARY9 = t9["brand-primary"];
10204
10717
  var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
10205
- var STATUS_SUCCESS = "#14B8A6";
10206
- var STATUS_WARNING = "#FFB020";
10207
- var STATUS_ERROR = "#D14343";
10718
+ var STATUS_SUCCESS = t9["status-success"];
10719
+ var STATUS_WARNING = t9["status-warning"];
10720
+ var STATUS_ERROR2 = t9["status-error"];
10208
10721
  var EMOJI_SETS = {
10209
10722
  sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
10210
10723
  soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
@@ -10213,7 +10726,7 @@ var EMOJI_SETS = {
10213
10726
  };
10214
10727
  var DEFAULT_EMOJI_SET = ["\u{1F61E}", "\u{1F615}", "\u{1F610}", "\u{1F642}", "\u{1F604}"];
10215
10728
  function scoreColor(score) {
10216
- if (score < 40) return STATUS_ERROR;
10729
+ if (score < 40) return STATUS_ERROR2;
10217
10730
  if (score < 70) return STATUS_WARNING;
10218
10731
  return STATUS_SUCCESS;
10219
10732
  }
@@ -10268,7 +10781,7 @@ function EmojiSlider({ factor }) {
10268
10781
  paddingVertical: 8,
10269
10782
  borderRadius: 8,
10270
10783
  borderWidth: 1,
10271
- borderColor: selected ? BRAND_PRIMARY8 : void 0,
10784
+ borderColor: selected ? BRAND_PRIMARY9 : void 0,
10272
10785
  backgroundColor: selected ? BRAND_PRIMARY_SUBTLE : void 0,
10273
10786
  opacity: selected ? 1 : pressed ? 0.8 : 0.55,
10274
10787
  transform: [{ scale: selected ? 1.06 : 1 }]
@@ -10384,7 +10897,7 @@ function ReadinessCheck({
10384
10897
  marginTop: 16,
10385
10898
  width: "100%",
10386
10899
  alignItems: "center",
10387
- backgroundColor: BRAND_PRIMARY8,
10900
+ backgroundColor: BRAND_PRIMARY9,
10388
10901
  borderRadius: 8,
10389
10902
  paddingVertical: 12,
10390
10903
  opacity: pressed ? 0.8 : 1
@@ -10394,21 +10907,22 @@ function ReadinessCheck({
10394
10907
  )
10395
10908
  ] }) });
10396
10909
  }
10397
- var BRAND_PRIMARY9 = "#FF7900";
10398
- var BRAND_PRIMARY_DARK2 = "#C45E00";
10399
- var BRAND_PRIMARY_LIGHT2 = "#FFB066";
10400
- var SUCCESS = "#14B8A6";
10401
- var WARNING = "#FFB020";
10402
- var ERROR = "#D14343";
10403
- var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY9, BRAND_PRIMARY_LIGHT2];
10910
+ var t10 = getSemanticColors("dark");
10911
+ var BRAND_PRIMARY10 = t10["brand-primary"];
10912
+ var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
10913
+ var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
10914
+ var SUCCESS = t10["status-success"];
10915
+ var WARNING = t10["status-warning"];
10916
+ var ERROR = t10["status-error"];
10917
+ var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
10404
10918
  var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
10405
- var GAUGE_GRADIENT = "linear-gradient(90deg, rgba(20,184,166,0.25) 0%, rgba(255,176,32,0.25) 50%, rgba(209,67,67,0.25) 100%)";
10919
+ 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%)";
10406
10920
  var STATUS_VARIANTS = {
10407
- success: { bg: "rgba(20,184,166,0.15)", border: "rgba(20,184,166,0.3)", text: SUCCESS },
10408
- warning: { bg: "rgba(255,176,32,0.15)", border: "rgba(255,176,32,0.3)", text: WARNING },
10921
+ success: { bg: "rgba(46,213,115,0.15)", border: "rgba(46,213,115,0.3)", text: SUCCESS },
10922
+ warning: { bg: "rgba(249,180,21,0.15)", border: "rgba(249,180,21,0.3)", text: WARNING },
10409
10923
  error: { bg: "rgba(209,67,67,0.15)", border: "rgba(209,67,67,0.25)", text: ERROR }
10410
10924
  };
10411
- function clamp012(value) {
10925
+ function clamp01(value) {
10412
10926
  if (!Number.isFinite(value)) return 0;
10413
10927
  if (value < 0) return 0;
10414
10928
  if (value > 1) return 1;
@@ -10503,7 +11017,7 @@ function MetricCell({ metric }) {
10503
11017
  ] });
10504
11018
  }
10505
11019
  function Gauge({ gauge }) {
10506
- const level = clamp012(gauge.level);
11020
+ const level = clamp01(gauge.level);
10507
11021
  const percentage = Math.round(level * 100);
10508
11022
  const markerColor = getGaugeZoneColor(level);
10509
11023
  return /* @__PURE__ */ jsxs(
@@ -10697,9 +11211,9 @@ function MesoStatusCard({
10697
11211
  reactNative.View,
10698
11212
  {
10699
11213
  style: {
10700
- backgroundColor: "rgba(255,176,32,0.06)",
11214
+ backgroundColor: "rgba(249,180,21,0.06)",
10701
11215
  borderWidth: 1,
10702
- borderColor: "rgba(255,176,32,0.15)",
11216
+ borderColor: "rgba(249,180,21,0.15)",
10703
11217
  borderRadius: 8,
10704
11218
  paddingVertical: 10,
10705
11219
  paddingHorizontal: 12
@@ -10725,9 +11239,9 @@ function MesoStatusCard({
10725
11239
  className: "flex-row items-center",
10726
11240
  style: {
10727
11241
  gap: 8,
10728
- backgroundColor: "rgba(20,184,166,0.06)",
11242
+ backgroundColor: "rgba(46,213,115,0.06)",
10729
11243
  borderWidth: 1,
10730
- borderColor: "rgba(20,184,166,0.2)",
11244
+ borderColor: "rgba(46,213,115,0.2)",
10731
11245
  borderRadius: 8,
10732
11246
  paddingVertical: 10,
10733
11247
  paddingHorizontal: 12
@@ -10756,21 +11270,22 @@ function MesoStatusCard({
10756
11270
  }
10757
11271
  );
10758
11272
  }
10759
- var BRAND_PRIMARY10 = "#FF7900";
11273
+ var sem = getSemanticColors("dark");
11274
+ var BRAND_PRIMARY11 = sem["brand-primary"];
10760
11275
  var PROJECTION_LINE_COLOR = resolveColor("text-tertiary");
10761
- var STATUS_SUCCESS2 = "#14B8A6";
10762
- var STATUS_ERROR2 = "#D14343";
10763
- var STATUS_WARNING2 = "#FFB020";
11276
+ var STATUS_SUCCESS2 = sem["status-success"];
11277
+ var STATUS_ERROR3 = sem["status-error"];
11278
+ var STATUS_WARNING2 = sem["status-warning"];
10764
11279
  var GRID_LINE = "rgba(255,255,255,0.06)";
10765
- var SUCCESS_PILL_BG = "rgba(20,184,166,0.10)";
10766
- var SUCCESS_PILL_BORDER = "rgba(20,184,166,0.20)";
11280
+ var SUCCESS_PILL_BG = "rgba(46,213,115,0.10)";
11281
+ var SUCCESS_PILL_BORDER = "rgba(46,213,115,0.20)";
10767
11282
  var ERROR_PILL_BG = "rgba(209,67,67,0.10)";
10768
11283
  var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
10769
11284
  var PLOT_LEFT = 26;
10770
11285
  var TOOLTIP_WIDTH = 124;
10771
11286
  function timeOf(dateStr) {
10772
- const t2 = Date.parse(dateStr);
10773
- return Number.isNaN(t2) ? 0 : t2;
11287
+ const t12 = Date.parse(dateStr);
11288
+ return Number.isNaN(t12) ? 0 : t12;
10774
11289
  }
10775
11290
  function formatValue(value) {
10776
11291
  return `${Math.round(value)}`;
@@ -11047,7 +11562,7 @@ function StrengthTrendChart({
11047
11562
  ChartLine,
11048
11563
  {
11049
11564
  coords: geometry.actual,
11050
- color: BRAND_PRIMARY10,
11565
+ color: BRAND_PRIMARY11,
11051
11566
  strokeWidth: 2.5,
11052
11567
  testID: "strength-trend-chart-actual-segment"
11053
11568
  }
@@ -11100,7 +11615,7 @@ function StrengthTrendChart({
11100
11615
  width: 8,
11101
11616
  height: 8,
11102
11617
  borderRadius: 1,
11103
- backgroundColor: BRAND_PRIMARY10
11618
+ backgroundColor: BRAND_PRIMARY11
11104
11619
  }
11105
11620
  },
11106
11621
  `point-${c.index}`
@@ -11159,7 +11674,7 @@ function StrengthTrendChart({
11159
11674
  marginTop: 1,
11160
11675
  fontSize: 10,
11161
11676
  fontFamily: "Inter, sans-serif",
11162
- color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS2 : STATUS_ERROR2
11677
+ color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS2 : STATUS_ERROR3
11163
11678
  },
11164
11679
  children: `${selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? "+" : "-"}${Math.abs(
11165
11680
  selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm
@@ -11215,7 +11730,7 @@ function StrengthTrendChart({
11215
11730
  fontSize: 11,
11216
11731
  fontFamily: "Inter, sans-serif",
11217
11732
  fontWeight: "600",
11218
- color: trend.positive ? STATUS_SUCCESS2 : STATUS_ERROR2
11733
+ color: trend.positive ? STATUS_SUCCESS2 : STATUS_ERROR3
11219
11734
  },
11220
11735
  children: trendText
11221
11736
  }
@@ -11230,7 +11745,7 @@ function StrengthTrendChart({
11230
11745
  testID: "strength-trend-chart-legend",
11231
11746
  children: [
11232
11747
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 5 }, children: [
11233
- /* @__PURE__ */ jsx(reactNative.View, { style: { width: 14, height: 2.5, borderRadius: 1.5, backgroundColor: BRAND_PRIMARY10 } }),
11748
+ /* @__PURE__ */ jsx(reactNative.View, { style: { width: 14, height: 2.5, borderRadius: 1.5, backgroundColor: BRAND_PRIMARY11 } }),
11234
11749
  /* @__PURE__ */ jsx(reactNative.Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "Actual" })
11235
11750
  ] }),
11236
11751
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 5 }, children: [
@@ -11259,13 +11774,14 @@ function StrengthTrendChart({
11259
11774
  }
11260
11775
  );
11261
11776
  }
11262
- var STATUS_SUCCESS3 = "#14B8A6";
11263
- var STATUS_WARNING3 = "#FFB020";
11264
- var STATUS_INFO = "#2196F3";
11777
+ var t11 = getSemanticColors("dark");
11778
+ var STATUS_SUCCESS3 = t11["status-success"];
11779
+ var STATUS_WARNING3 = t11["status-warning"];
11780
+ var STATUS_INFO = t11["status-info"];
11265
11781
  var DOT_BORDER = "#F3F4F6";
11266
- var BAND_FILL = "rgba(20,184,166,0.1)";
11267
- var BAND_EDGE = "rgba(20,184,166,0.45)";
11268
- var PROJECTION_FILL = "rgba(20,184,166,0.05)";
11782
+ var BAND_FILL = "rgba(46,213,115,0.1)";
11783
+ var BAND_EDGE = "rgba(46,213,115,0.45)";
11784
+ var PROJECTION_FILL = "rgba(46,213,115,0.05)";
11269
11785
  var PADDING_LEFT = 28;
11270
11786
  var PADDING_RIGHT = 10;
11271
11787
  var PADDING_TOP = 8;
@@ -11304,8 +11820,8 @@ function interpEdge(pixels, x, key) {
11304
11820
  const a = pixels[i];
11305
11821
  const b = pixels[i + 1];
11306
11822
  if (x >= a.x && x <= b.x) {
11307
- const t2 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
11308
- return a[key] + t2 * (b[key] - a[key]);
11823
+ const t12 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
11824
+ return a[key] + t12 * (b[key] - a[key]);
11309
11825
  }
11310
11826
  }
11311
11827
  return pixels[pixels.length - 1][key];
@@ -11464,1188 +11980,178 @@ function CapacityBandChart({
11464
11980
  transformOrigin: "0 0",
11465
11981
  transform: [{ rotate: `${seg.angle}deg` }]
11466
11982
  },
11467
- accessibilityElementsHidden: true,
11468
- importantForAccessibility: "no-hide-descendants",
11469
- testID: key
11470
- },
11471
- `${key}-${i}`
11472
- ));
11473
- return /* @__PURE__ */ jsxs(
11474
- reactNative.View,
11475
- {
11476
- style: { width, height, position: "relative" },
11477
- className,
11478
- testID: "capacity-band-chart-root",
11479
- ...props,
11480
- children: [
11481
- /* @__PURE__ */ jsxs(
11482
- reactNative.View,
11483
- {
11484
- style: { position: "absolute", top: 0, left: 0, width, height },
11485
- accessibilityRole: "image",
11486
- accessibilityLabel: `Training capacity band chart. Current capacity: ${currentStatus(
11487
- workouts
11488
- )}. ${workouts.length} workout${workouts.length === 1 ? "" : "s"} shown.`,
11489
- testID: "capacity-band-chart",
11490
- children: [
11491
- /* @__PURE__ */ jsx(
11492
- reactNative.Animated.View,
11493
- {
11494
- style: { position: "absolute", top: 0, left: 0, height, width: revealWidth, overflow: "hidden" },
11495
- accessibilityElementsHidden: true,
11496
- importantForAccessibility: "no-hide-descendants",
11497
- testID: "capacity-band-chart-reveal",
11498
- children: /* @__PURE__ */ jsxs(reactNative.View, { style: { width, height, position: "relative" }, children: [
11499
- projection && /* @__PURE__ */ jsxs(reactNative.View, { testID: "capacity-band-chart-projection", children: [
11500
- renderColumns(buildColumns(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
11501
- renderColumns(buildColumns(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
11502
- renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
11503
- renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
11504
- renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
11505
- renderEdges(buildEdges(restPixels, "yLow"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
11506
- /* @__PURE__ */ jsx(
11507
- reactNative.Text,
11508
- {
11509
- style: {
11510
- position: "absolute",
11511
- left: trainingPixels[trainingPixels.length - 1]?.x + 2,
11512
- top: trainingPixels[trainingPixels.length - 1]?.yHigh - 12,
11513
- fontSize: 9,
11514
- fontFamily: "Inter, sans-serif",
11515
- color: STATUS_SUCCESS3
11516
- },
11517
- testID: "capacity-band-chart-projection-training-label",
11518
- children: "Training"
11519
- }
11520
- ),
11521
- /* @__PURE__ */ jsx(
11522
- reactNative.Text,
11523
- {
11524
- style: {
11525
- position: "absolute",
11526
- left: restPixels[restPixels.length - 1]?.x + 2,
11527
- top: restPixels[restPixels.length - 1]?.yLow + 2,
11528
- fontSize: 9,
11529
- fontFamily: "Inter, sans-serif",
11530
- color: STATUS_INFO
11531
- },
11532
- testID: "capacity-band-chart-projection-rest-label",
11533
- children: "Rest"
11534
- }
11535
- )
11536
- ] }),
11537
- /* @__PURE__ */ jsx(reactNative.View, { testID: "capacity-band-chart-band", children: renderColumns(columns, BAND_FILL, "capacity-band-chart-band-cell") }),
11538
- renderEdges(topEdge, BAND_EDGE, false, "capacity-band-chart-edge-top"),
11539
- renderEdges(bottomEdge, BAND_EDGE, false, "capacity-band-chart-edge-bottom")
11540
- ] })
11541
- }
11542
- ),
11543
- /* @__PURE__ */ jsx(
11544
- reactNative.Text,
11545
- {
11546
- className: "text-text-tertiary",
11547
- style: {
11548
- position: "absolute",
11549
- left: PADDING_LEFT / 2 - 14,
11550
- top: PADDING_TOP + plotHeight / 2 - 7,
11551
- width: 32,
11552
- textAlign: "center",
11553
- fontSize: 9,
11554
- fontFamily: "Inter, sans-serif",
11555
- transform: [{ rotate: "-90deg" }]
11556
- },
11557
- accessibilityElementsHidden: true,
11558
- importantForAccessibility: "no-hide-descendants",
11559
- testID: "capacity-band-chart-y-axis-label",
11560
- children: "Load"
11561
- }
11562
- ),
11563
- band.map((point, i) => {
11564
- if (i % labelStride !== 0 && i !== band.length - 1) return null;
11565
- return /* @__PURE__ */ jsx(
11566
- reactNative.Text,
11567
- {
11568
- className: "text-text-tertiary",
11569
- style: {
11570
- position: "absolute",
11571
- left: toX(point.date) - 14,
11572
- top: height - PADDING_BOTTOM + 2,
11573
- width: 28,
11574
- textAlign: "center",
11575
- fontSize: 10,
11576
- fontFamily: "Inter, sans-serif"
11577
- },
11578
- accessibilityElementsHidden: true,
11579
- importantForAccessibility: "no-hide-descendants",
11580
- testID: "capacity-band-chart-x-label",
11581
- children: formatDate2(point.date)
11582
- },
11583
- `x-${point.date}`
11584
- );
11585
- })
11586
- ]
11587
- }
11588
- ),
11589
- workouts.map((workout, i) => {
11590
- const cx = toX(workout.date);
11591
- const cy = toY(workout.load);
11592
- const color = DOT_COLORS[workout.status];
11593
- const dotStyle = {
11594
- width: DOT_SIZE,
11595
- height: DOT_SIZE,
11596
- borderRadius: DOT_SIZE / 2,
11597
- backgroundColor: color,
11598
- borderWidth: 2,
11599
- borderColor: DOT_BORDER
11600
- };
11601
- const label = `Workout on ${formatDate2(workout.date)}, load ${workout.load}, ${STATUS_PHRASES[workout.status]}`;
11602
- return /* @__PURE__ */ jsx(
11603
- reactNative.Animated.View,
11604
- {
11605
- style: {
11606
- position: "absolute",
11607
- left: cx - DOT_SIZE / 2,
11608
- top: cy - DOT_SIZE / 2,
11609
- transform: [{ scale: dotAnims[i] ?? 1 }]
11610
- },
11611
- testID: "capacity-band-chart-dot-wrapper",
11612
- children: onWorkoutPress ? /* @__PURE__ */ jsx(
11613
- reactNative.Pressable,
11614
- {
11615
- onPress: () => onWorkoutPress(workout),
11616
- accessibilityRole: "button",
11617
- accessibilityLabel: label,
11618
- style: dotStyle,
11619
- testID: "capacity-band-chart-dot"
11620
- }
11621
- ) : /* @__PURE__ */ jsx(
11622
- reactNative.View,
11623
- {
11624
- accessibilityElementsHidden: true,
11625
- importantForAccessibility: "no-hide-descendants",
11626
- style: dotStyle,
11627
- testID: "capacity-band-chart-dot"
11628
- }
11629
- )
11630
- },
11631
- `dot-${workout.date}-${i}`
11632
- );
11633
- })
11634
- ]
11635
- }
11636
- );
11637
- }
11638
- var BRAND_PRIMARY11 = "#FF7900";
11639
- var WORKOUT_PILL_STATUS = {
11640
- completed: "completed",
11641
- today: "current",
11642
- upcoming: "upcoming"
11643
- };
11644
- function deriveNavLevel(selection) {
11645
- if (selection.workoutId != null) return "workout";
11646
- if (selection.weekNumber != null) return "week";
11647
- return "meso";
11648
- }
11649
- function toProgressBarMesos(mesos) {
11650
- return mesos.map(({ id, name, weekCount, status, currentWeek }) => ({
11651
- id,
11652
- name,
11653
- weekCount,
11654
- status,
11655
- currentWeek
11656
- }));
11657
- }
11658
- function findMeso(mesos, id) {
11659
- if (id == null) return null;
11660
- return mesos.find((meso) => meso.id === id) ?? null;
11661
- }
11662
- function findWeek(meso, weekNumber) {
11663
- if (meso == null || weekNumber == null) return null;
11664
- return meso.weeks.find((week) => week.weekNumber === weekNumber) ?? null;
11665
- }
11666
- function findWorkout(week, workoutId) {
11667
- if (week == null || workoutId == null) return null;
11668
- return week.workouts.find((workout) => workout.id === workoutId) ?? null;
11669
- }
11670
- function buildBreadcrumbs(meso, weekNumber, workout) {
11671
- const crumbs = [{ key: "meso", label: meso.name, level: "meso" }];
11672
- if (weekNumber != null) {
11673
- crumbs.push({ key: "week", label: `Week ${weekNumber}`, level: "week" });
11674
- }
11675
- if (workout != null) {
11676
- crumbs.push({ key: "workout", label: workout.name, level: "workout" });
11677
- }
11678
- return crumbs;
11679
- }
11680
- function Breadcrumbs2({ crumbs, onNavigate }) {
11681
- return /* @__PURE__ */ jsx(
11682
- reactNative.View,
11683
- {
11684
- className: "flex-row items-center flex-wrap",
11685
- style: { gap: 4 },
11686
- testID: "program-planning-page-breadcrumbs",
11687
- children: crumbs.map((crumb, index) => {
11688
- const isLast = index === crumbs.length - 1;
11689
- return /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 4 }, children: [
11690
- index > 0 && /* @__PURE__ */ jsx(reactNative.Text, { className: "text-text-tertiary", style: { fontSize: 12 }, children: "\u203A" }),
11691
- /* @__PURE__ */ jsx(
11692
- reactNative.Pressable,
11693
- {
11694
- onPress: () => onNavigate(crumb.level),
11695
- disabled: isLast,
11696
- accessibilityRole: "button",
11697
- testID: `program-planning-page-crumb-${crumb.key}`,
11698
- children: /* @__PURE__ */ jsx(
11699
- reactNative.Text,
11700
- {
11701
- className: isLast ? "text-text-primary" : void 0,
11702
- style: {
11703
- fontSize: 12,
11704
- fontFamily: "Inter, sans-serif",
11705
- fontWeight: isLast ? "700" : "500",
11706
- color: isLast ? void 0 : BRAND_PRIMARY11
11707
- },
11708
- children: crumb.label
11709
- }
11710
- )
11711
- }
11712
- )
11713
- ] }, crumb.key);
11714
- })
11715
- }
11716
- );
11717
- }
11718
- function toWeekRow(week, meso, onSelectWeek) {
11719
- const workouts = week.workouts.map((workout) => ({
11720
- name: workout.name,
11721
- status: WORKOUT_PILL_STATUS[workout.status],
11722
- onPress: () => onSelectWeek(week.weekNumber)
11723
- }));
11724
- return {
11725
- weekNumber: week.weekNumber,
11726
- totalWeeks: meso.weekCount,
11727
- workouts,
11728
- intensityLevel: week.intensityLevel,
11729
- intensityThreshold: week.intensityThreshold,
11730
- isCurrent: week.isCurrent ?? week.weekNumber === meso.currentWeek,
11731
- isDeload: week.isDeload
11732
- };
11733
- }
11734
- function MesoLevel({ mesos, activeMesoId, onSelectMeso, onSelectWeek }) {
11735
- return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 12 }, testID: "program-planning-page-meso-level", children: mesos.map((meso) => {
11736
- const expanded = meso.id === activeMesoId;
11737
- return /* @__PURE__ */ jsx(
11738
- MesoCard,
11739
- {
11740
- name: meso.name,
11741
- goal: meso.goal,
11742
- split: meso.split,
11743
- weekRange: meso.weekRange,
11744
- currentWeek: meso.currentWeek,
11745
- totalWeeks: meso.weekCount,
11746
- weeks: expanded ? meso.weeks.map((week) => toWeekRow(week, meso, onSelectWeek)) : [],
11747
- volumeHeatmap: meso.volumeHeatmap,
11748
- expanded,
11749
- onToggle: () => onSelectMeso(meso.id),
11750
- highlighted: expanded
11751
- },
11752
- meso.id
11753
- );
11754
- }) });
11755
- }
11756
- function WeekLevel({ week, onSelectWorkout }) {
11757
- return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 10 }, testID: "program-planning-page-week-level", children: week.workouts.map((workout) => /* @__PURE__ */ jsx(
11758
- WorkoutCard,
11759
- {
11760
- name: workout.name,
11761
- date: workout.date,
11762
- duration: workout.duration,
11763
- status: workout.status,
11764
- muscleGroups: workout.muscleGroups,
11765
- totalSets: workout.totalSets,
11766
- totalVolume: workout.totalVolume,
11767
- unit: workout.unit,
11768
- exercises: workout.exercises,
11769
- expanded: false,
11770
- onToggle: () => onSelectWorkout(workout.id)
11771
- },
11772
- workout.id
11773
- )) });
11774
- }
11775
- function WorkoutLevel({ workout, expandedExercise, onToggleExercise }) {
11776
- const exercises = workout.exercises.map((exercise, index) => ({
11777
- ...exercise,
11778
- state: exercise.state === "upcoming" ? exercise.state : index === expandedExercise ? "expanded" : "collapsed",
11779
- onToggle: () => onToggleExercise(index)
11780
- }));
11781
- return /* @__PURE__ */ jsx(reactNative.View, { testID: "program-planning-page-workout-level", children: /* @__PURE__ */ jsx(
11782
- WorkoutCard,
11783
- {
11784
- name: workout.name,
11785
- date: workout.date,
11786
- duration: workout.duration,
11787
- status: workout.status,
11788
- muscleGroups: workout.muscleGroups,
11789
- totalSets: workout.totalSets,
11790
- totalVolume: workout.totalVolume,
11791
- unit: workout.unit,
11792
- exercises,
11793
- expanded: true,
11794
- onToggle: () => void 0
11795
- }
11796
- ) });
11797
- }
11798
- function ProgramPlanningPage({
11799
- title = "Program Plan",
11800
- mesos,
11801
- className,
11802
- ...props
11803
- }) {
11804
- const [activeMesoId, setActiveMesoId] = React24.useState(mesos[0]?.id ?? null);
11805
- const [selectedWeek, setSelectedWeek] = React24.useState(null);
11806
- const [selectedWorkoutId, setSelectedWorkoutId] = React24.useState(null);
11807
- const [expandedExercise, setExpandedExercise] = React24.useState(null);
11808
- const progressMesos = React24.useMemo(() => toProgressBarMesos(mesos), [mesos]);
11809
- const activeMeso = findMeso(mesos, activeMesoId);
11810
- const level = deriveNavLevel({ weekNumber: selectedWeek, workoutId: selectedWorkoutId });
11811
- const activeWeek = findWeek(activeMeso, selectedWeek);
11812
- const activeWorkout = findWorkout(activeWeek, selectedWorkoutId);
11813
- const selectMeso = (id) => {
11814
- setActiveMesoId(id);
11815
- setSelectedWeek(null);
11816
- setSelectedWorkoutId(null);
11817
- };
11818
- const selectWeek = (weekNumber) => {
11819
- setSelectedWeek(weekNumber);
11820
- setSelectedWorkoutId(null);
11821
- setExpandedExercise(null);
11822
- };
11823
- const selectWorkout = (id) => {
11824
- setSelectedWorkoutId(id);
11825
- setExpandedExercise(null);
11826
- };
11827
- const toggleExercise = (index) => setExpandedExercise((current) => current === index ? null : index);
11828
- const navigateTo = (target) => {
11829
- if (target === "meso") selectMeso(activeMesoId ?? mesos[0]?.id);
11830
- else if (target === "week" && selectedWeek != null) selectWeek(selectedWeek);
11831
- };
11832
- return /* @__PURE__ */ jsx(
11833
- reactNative.View,
11834
- {
11835
- className: cn(className, "bg-background-base"),
11836
- style: { width: 390 },
11837
- accessibilityRole: "main",
11838
- "aria-label": title,
11839
- testID: "program-planning-page",
11840
- ...props,
11841
- children: /* @__PURE__ */ jsxs(reactNative.View, { style: { padding: 16, gap: 14 }, testID: "program-planning-page-content", children: [
11842
- /* @__PURE__ */ jsx(
11843
- reactNative.Text,
11844
- {
11845
- accessibilityRole: "header",
11846
- className: "text-text-primary",
11847
- style: {
11848
- fontSize: 20,
11849
- fontFamily: '"Space Grotesk", sans-serif',
11850
- fontWeight: "700"
11851
- },
11852
- testID: "program-planning-page-title",
11853
- children: title
11854
- }
11855
- ),
11856
- /* @__PURE__ */ jsx(reactNative.View, { style: { marginHorizontal: -16 }, children: /* @__PURE__ */ jsx(
11857
- MesoProgressBar,
11858
- {
11859
- mesos: progressMesos,
11860
- activeMesoId,
11861
- onMesoPress: selectMeso
11862
- }
11863
- ) }),
11864
- activeMeso != null && /* @__PURE__ */ jsx(
11865
- Breadcrumbs2,
11866
- {
11867
- crumbs: buildBreadcrumbs(activeMeso, selectedWeek, activeWorkout),
11868
- onNavigate: navigateTo
11869
- }
11870
- ),
11871
- /* @__PURE__ */ jsxs(
11872
- reactNative.View,
11873
- {
11874
- className: "bg-surface-elevated border-border",
11875
- style: {
11876
- borderWidth: 1,
11877
- borderRadius: 12,
11878
- padding: 12
11879
- },
11880
- testID: "program-planning-page-body",
11881
- children: [
11882
- level === "meso" && /* @__PURE__ */ jsx(
11883
- MesoLevel,
11884
- {
11885
- mesos,
11886
- activeMesoId,
11887
- onSelectMeso: selectMeso,
11888
- onSelectWeek: selectWeek
11889
- }
11890
- ),
11891
- level === "week" && activeWeek != null && /* @__PURE__ */ jsx(WeekLevel, { week: activeWeek, onSelectWorkout: selectWorkout }),
11892
- level === "workout" && activeWorkout != null && /* @__PURE__ */ jsx(
11893
- WorkoutLevel,
11894
- {
11895
- workout: activeWorkout,
11896
- expandedExercise,
11897
- onToggleExercise: toggleExercise
11898
- }
11899
- )
11900
- ]
11901
- }
11902
- )
11903
- ] })
11904
- }
11905
- );
11906
- }
11907
- var BRAND_PRIMARY12 = "#FF7900";
11908
- var CHART_WIDTH = 326;
11909
- var CHART_HEIGHT = 150;
11910
- var EXERCISE_DETAIL_TABS = [
11911
- { key: "progress", label: "Progress" },
11912
- { key: "history", label: "History" },
11913
- { key: "advanced", label: "Advanced" }
11914
- ];
11915
- function deriveExerciseStats(entries) {
11916
- let bestE1rm = null;
11917
- let prCount = 0;
11918
- for (const entry of entries) {
11919
- if (entry.e1rm != null && (bestE1rm == null || entry.e1rm.value > bestE1rm)) {
11920
- bestE1rm = entry.e1rm.value;
11921
- }
11922
- if (entry.isPR) prCount += 1;
11923
- }
11924
- return { sessions: entries.length, bestE1rm, prCount };
11925
- }
11926
- function toExerciseCardProps(entry, expanded, onToggle) {
11927
- return {
11928
- name: entry.title,
11929
- state: expanded ? "expanded" : "collapsed",
11930
- onToggle,
11931
- summary: entry.summary,
11932
- e1rm: entry.e1rm,
11933
- isPR: entry.isPR,
11934
- tempo: entry.tempo,
11935
- sets: expanded ? entry.sets : void 0
11936
- };
11937
- }
11938
- function summarizeVbt(sets) {
11939
- const allReps = sets.flatMap((set) => set.velocities);
11940
- const meanVelocity = calculateMeanVelocity(allReps);
11941
- let velocityLoss = 0;
11942
- for (const set of sets) {
11943
- velocityLoss = Math.max(velocityLoss, calculateVelocityLoss(set.velocities));
11944
- }
11945
- return { meanVelocity, velocityLoss };
11946
- }
11947
- function TabBar({ active, onSelect }) {
11948
- return /* @__PURE__ */ jsx(
11949
- reactNative.View,
11950
- {
11951
- className: "flex-row",
11952
- style: {
11953
- borderBottomWidth: 1,
11954
- borderBottomColor: resolveColor("border-default")
11955
- },
11956
- accessibilityRole: "tablist",
11957
- testID: "exercise-detail-page-tabs",
11958
- children: EXERCISE_DETAIL_TABS.map(({ key, label }) => {
11959
- const isActive = key === active;
11960
- return /* @__PURE__ */ jsx(
11961
- reactNative.Pressable,
11962
- {
11963
- onPress: () => onSelect(key),
11964
- accessibilityRole: "tab",
11965
- accessibilityState: { selected: isActive },
11966
- style: {
11967
- flex: 1,
11968
- alignItems: "center",
11969
- paddingVertical: 10,
11970
- borderBottomWidth: 2,
11971
- borderBottomColor: isActive ? BRAND_PRIMARY12 : "transparent"
11972
- },
11973
- testID: `exercise-detail-page-tab-${key}`,
11974
- children: /* @__PURE__ */ jsx(
11975
- reactNative.Text,
11976
- {
11977
- className: isActive ? "text-text-primary" : "text-text-tertiary",
11978
- style: {
11979
- fontSize: 13,
11980
- fontFamily: "Inter, sans-serif",
11981
- fontWeight: isActive ? "700" : "500"
11982
- },
11983
- children: label
11984
- }
11985
- )
11986
- },
11987
- key
11988
- );
11989
- })
11990
- }
11991
- );
11992
- }
11993
- var STAT_CARDS = [
11994
- { key: "sessions", label: "Sessions" },
11995
- { key: "bestE1rm", label: "Best e1RM" },
11996
- { key: "prCount", label: "PRs" }
11997
- ];
11998
- function StatStrip({ stats, unit }) {
11999
- return /* @__PURE__ */ jsx(reactNative.View, { className: "flex-row", style: { gap: 8 }, testID: "exercise-detail-page-stats", children: STAT_CARDS.map(({ key, label }) => {
12000
- const raw = stats[key];
12001
- const value = key === "bestE1rm" ? raw == null ? "\u2014" : `${raw} ${unit}` : `${raw}`;
12002
- return /* @__PURE__ */ jsxs(
12003
- reactNative.View,
12004
- {
12005
- className: "bg-surface-elevated border-border",
12006
- style: {
12007
- flex: 1,
12008
- padding: 12,
12009
- borderRadius: 10,
12010
- borderWidth: 1
12011
- },
12012
- testID: `exercise-detail-page-stat-${key}`,
12013
- children: [
12014
- /* @__PURE__ */ jsx(
12015
- reactNative.Text,
12016
- {
12017
- className: "text-text-primary",
12018
- style: {
12019
- fontSize: 18,
12020
- fontFamily: '"Space Grotesk", sans-serif',
12021
- fontWeight: "700"
12022
- },
12023
- children: value
12024
- }
12025
- ),
12026
- /* @__PURE__ */ jsx(
12027
- reactNative.Text,
12028
- {
12029
- className: "text-text-tertiary",
12030
- style: {
12031
- marginTop: 2,
12032
- fontSize: 10,
12033
- fontFamily: "Inter, sans-serif",
12034
- fontWeight: "600",
12035
- letterSpacing: 0.4,
12036
- textTransform: "uppercase"
12037
- },
12038
- children: label
12039
- }
12040
- )
12041
- ]
12042
- },
12043
- key
12044
- );
12045
- }) });
12046
- }
12047
- function SectionCard({
12048
- title,
12049
- children,
12050
- testID
12051
- }) {
12052
- return /* @__PURE__ */ jsxs(
12053
- reactNative.View,
12054
- {
12055
- className: "bg-surface-elevated border-border",
12056
- style: {
12057
- borderWidth: 1,
12058
- borderRadius: 12,
12059
- padding: 12,
12060
- gap: 10
12061
- },
12062
- testID,
12063
- children: [
12064
- /* @__PURE__ */ jsx(
12065
- reactNative.Text,
12066
- {
12067
- className: "text-text-tertiary",
12068
- style: {
12069
- fontSize: 11,
12070
- fontFamily: "Inter, sans-serif",
12071
- fontWeight: "600",
12072
- letterSpacing: 0.6,
12073
- textTransform: "uppercase"
12074
- },
12075
- children: title
12076
- }
12077
- ),
12078
- children
12079
- ]
12080
- }
12081
- );
12082
- }
12083
- function EntryList({ entries, expandedId, onToggle, emptyLabel, testID }) {
12084
- if (entries.length === 0) {
12085
- return /* @__PURE__ */ jsx(
12086
- reactNative.Text,
12087
- {
12088
- className: "text-text-tertiary",
12089
- style: { fontSize: 12, fontFamily: "Inter, sans-serif" },
12090
- testID: `${testID}-empty`,
12091
- children: emptyLabel
12092
- }
12093
- );
12094
- }
12095
- return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 8 }, testID, children: entries.map((entry) => /* @__PURE__ */ jsx(
12096
- ExerciseCard,
12097
- {
12098
- ...toExerciseCardProps(entry, entry.id === expandedId, () => onToggle(entry.id))
12099
- },
12100
- entry.id
12101
- )) });
12102
- }
12103
- function VbtBreakdown({ sets }) {
12104
- return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 10 }, testID: "exercise-detail-page-vbt-breakdown", children: sets.map((set) => {
12105
- const mean = calculateMeanVelocity(set.velocities);
12106
- const loss = calculateVelocityLoss(set.velocities);
12107
- return /* @__PURE__ */ jsxs(
12108
- reactNative.View,
12109
- {
12110
- className: "flex-row items-center",
12111
- style: { gap: 10 },
12112
- testID: "exercise-detail-page-vbt-set",
12113
- children: [
12114
- /* @__PURE__ */ jsx(
12115
- reactNative.Text,
12116
- {
12117
- className: "text-text-secondary",
12118
- style: {
12119
- width: 44,
12120
- fontSize: 11,
12121
- fontFamily: "Inter, sans-serif",
12122
- fontWeight: "600"
12123
- },
12124
- children: set.label
12125
- }
12126
- ),
12127
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", style: { height: 8, justifyContent: "center" }, children: /* @__PURE__ */ jsx(VelocityStrip, { velocities: set.velocities, variant: "mini" }) }),
12128
- /* @__PURE__ */ jsx(
12129
- reactNative.Text,
12130
- {
12131
- className: "text-text-tertiary",
12132
- style: {
12133
- fontSize: 11,
12134
- fontFamily: "Inter, sans-serif",
12135
- fontVariant: ["tabular-nums"]
12136
- },
12137
- children: `${mean.toFixed(2)} \xB7 -${loss}%`
12138
- }
12139
- )
12140
- ]
12141
- },
12142
- set.label
12143
- );
12144
- }) });
12145
- }
12146
- function VbtSummaryRow({ summary }) {
12147
- const cells = [
12148
- { label: "Mean Velocity", value: `${summary.meanVelocity.toFixed(2)} m/s` },
12149
- { label: "Peak Vel. Loss", value: `-${summary.velocityLoss}%` }
12150
- ];
12151
- return /* @__PURE__ */ jsx(reactNative.View, { className: "flex-row", style: { gap: 8 }, testID: "exercise-detail-page-vbt-summary", children: cells.map((cell) => /* @__PURE__ */ jsxs(
12152
- reactNative.View,
12153
- {
12154
- className: "bg-surface-elevated border-border",
12155
- style: {
12156
- flex: 1,
12157
- padding: 12,
12158
- borderRadius: 10,
12159
- borderWidth: 1
12160
- },
12161
- children: [
12162
- /* @__PURE__ */ jsx(
12163
- reactNative.Text,
12164
- {
12165
- className: "text-text-primary",
12166
- style: {
12167
- fontSize: 16,
12168
- fontFamily: '"Space Grotesk", sans-serif',
12169
- fontWeight: "700"
12170
- },
12171
- children: cell.value
12172
- }
12173
- ),
12174
- /* @__PURE__ */ jsx(
12175
- reactNative.Text,
12176
- {
12177
- className: "text-text-tertiary",
12178
- style: {
12179
- marginTop: 2,
12180
- fontSize: 10,
12181
- fontFamily: "Inter, sans-serif",
12182
- fontWeight: "600",
12183
- letterSpacing: 0.4,
12184
- textTransform: "uppercase"
12185
- },
12186
- children: cell.label
12187
- }
12188
- )
12189
- ]
12190
- },
12191
- cell.label
12192
- )) });
12193
- }
12194
- function ExerciseDetailPage({
12195
- exercise,
12196
- meso,
12197
- trend,
12198
- progression,
12199
- history,
12200
- vbt,
12201
- className,
12202
- ...props
12203
- }) {
12204
- const [tab, setTab] = React24.useState("progress");
12205
- const [expandedProgressId, setExpandedProgressId] = React24.useState(null);
12206
- const [expandedHistoryId, setExpandedHistoryId] = React24.useState(null);
12207
- const stats = React24.useMemo(() => deriveExerciseStats(history), [history]);
12208
- const vbtSummary = React24.useMemo(() => summarizeVbt(vbt.sets), [vbt.sets]);
12209
- const toggle = (setId) => (id) => setId((current) => current === id ? null : id);
12210
- return /* @__PURE__ */ jsx(
12211
- reactNative.View,
12212
- {
12213
- className: cn("bg-background-base", className),
12214
- style: { width: 390 },
12215
- accessibilityRole: "main",
12216
- "aria-label": exercise.name,
12217
- testID: "exercise-detail-page",
12218
- ...props,
12219
- children: /* @__PURE__ */ jsxs(reactNative.View, { style: { padding: 16, gap: 14 }, testID: "exercise-detail-page-content", children: [
12220
- /* @__PURE__ */ jsxs(
12221
- reactNative.View,
12222
- {
12223
- className: "flex-row items-start justify-between",
12224
- style: { gap: 8 },
12225
- testID: "exercise-detail-page-header",
12226
- children: [
12227
- /* @__PURE__ */ jsxs(reactNative.View, { style: { flexShrink: 1 }, children: [
12228
- /* @__PURE__ */ jsx(
12229
- reactNative.Text,
12230
- {
12231
- accessibilityRole: "header",
12232
- className: "text-text-primary",
12233
- style: {
12234
- fontSize: 20,
12235
- fontFamily: '"Space Grotesk", sans-serif',
12236
- fontWeight: "700"
12237
- },
12238
- testID: "exercise-detail-page-title",
12239
- children: exercise.name
12240
- }
12241
- ),
12242
- /* @__PURE__ */ jsx(
12243
- reactNative.Text,
12244
- {
12245
- className: "text-text-secondary",
12246
- style: {
12247
- marginTop: 2,
12248
- fontSize: 12,
12249
- fontFamily: "Inter, sans-serif"
12250
- },
12251
- testID: "exercise-detail-page-subtitle",
12252
- children: exercise.subtitle
12253
- }
12254
- )
12255
- ] }),
12256
- exercise.currentE1rm != null && /* @__PURE__ */ jsxs(
12257
- reactNative.View,
12258
- {
12259
- style: {
12260
- paddingVertical: 4,
12261
- paddingHorizontal: 10,
12262
- borderRadius: 6,
12263
- backgroundColor: "rgba(255,121,0,0.10)",
12264
- borderWidth: 1,
12265
- borderColor: "rgba(255,121,0,0.25)"
12266
- },
12267
- testID: "exercise-detail-page-e1rm",
12268
- children: [
12269
- /* @__PURE__ */ jsx(
12270
- reactNative.Text,
12271
- {
12272
- className: "text-text-tertiary",
12273
- style: {
12274
- fontSize: 9,
12275
- fontFamily: "Inter, sans-serif",
12276
- fontWeight: "600",
12277
- letterSpacing: 0.4,
12278
- textTransform: "uppercase"
12279
- },
12280
- children: "e1RM"
12281
- }
12282
- ),
12283
- /* @__PURE__ */ jsx(
12284
- reactNative.Text,
12285
- {
12286
- style: {
12287
- fontSize: 15,
12288
- fontFamily: '"Space Grotesk", sans-serif',
12289
- fontWeight: "700",
12290
- color: BRAND_PRIMARY12
12291
- },
12292
- children: `${exercise.currentE1rm} ${exercise.unit}`
12293
- }
12294
- )
12295
- ]
12296
- }
12297
- )
12298
- ]
12299
- }
12300
- ),
12301
- /* @__PURE__ */ jsx(TabBar, { active: tab, onSelect: setTab }),
12302
- tab === "progress" && /* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 14 }, testID: "exercise-detail-page-panel-progress", children: [
12303
- /* @__PURE__ */ jsx(MesoStatusCard, { ...meso }),
12304
- /* @__PURE__ */ jsx(StatStrip, { stats, unit: exercise.unit }),
12305
- /* @__PURE__ */ jsx(SectionCard, { title: "Strength Trend", testID: "exercise-detail-page-trend", children: /* @__PURE__ */ jsx(
12306
- StrengthTrendChart,
12307
- {
12308
- data: trend.data,
12309
- projection: trend.projection,
12310
- mesoBoundaries: trend.mesoBoundaries,
12311
- width: CHART_WIDTH,
12312
- height: CHART_HEIGHT,
12313
- unit: exercise.unit,
12314
- animateOnMount: false
12315
- }
12316
- ) }),
12317
- /* @__PURE__ */ jsx(SectionCard, { title: "Week-by-Week", testID: "exercise-detail-page-progression", children: /* @__PURE__ */ jsx(
12318
- EntryList,
12319
- {
12320
- entries: progression,
12321
- expandedId: expandedProgressId,
12322
- onToggle: toggle(setExpandedProgressId),
12323
- emptyLabel: "No progression logged yet",
12324
- testID: "exercise-detail-page-progression-list"
12325
- }
12326
- ) })
12327
- ] }),
12328
- tab === "history" && /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 14 }, testID: "exercise-detail-page-panel-history", children: /* @__PURE__ */ jsx(SectionCard, { title: "All Sessions", testID: "exercise-detail-page-history", children: /* @__PURE__ */ jsx(
12329
- EntryList,
12330
- {
12331
- entries: history,
12332
- expandedId: expandedHistoryId,
12333
- onToggle: toggle(setExpandedHistoryId),
12334
- emptyLabel: "No sessions logged yet",
12335
- testID: "exercise-detail-page-history-list"
12336
- }
12337
- ) }) }),
12338
- tab === "advanced" && /* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 14 }, testID: "exercise-detail-page-panel-advanced", children: [
12339
- /* @__PURE__ */ jsx(SectionCard, { title: "Capacity Band", testID: "exercise-detail-page-capacity", children: /* @__PURE__ */ jsx(
12340
- CapacityBandChart,
12341
- {
12342
- band: vbt.band,
12343
- workouts: vbt.workouts,
12344
- projection: vbt.projection,
12345
- width: CHART_WIDTH,
12346
- height: CHART_HEIGHT
12347
- }
12348
- ) }),
12349
- /* @__PURE__ */ jsx(VbtSummaryRow, { summary: vbtSummary }),
12350
- /* @__PURE__ */ jsx(SectionCard, { title: "Velocity Breakdown", testID: "exercise-detail-page-velocity", children: /* @__PURE__ */ jsx(VbtBreakdown, { sets: vbt.sets }) })
12351
- ] })
12352
- ] })
12353
- }
12354
- );
12355
- }
12356
- var BRAND_PRIMARY13 = "#FF7900";
12357
- function countCompletedSets(exercise) {
12358
- if (exercise.setVelocities != null) return exercise.setVelocities.length;
12359
- if (exercise.status === "completed") return exercise.totalPlannedSets;
12360
- return 0;
12361
- }
12362
- function deriveWorkoutProgress(exercises) {
12363
- let completedExercises = 0;
12364
- let completedSets = 0;
12365
- let totalSets = 0;
12366
- for (const exercise of exercises) {
12367
- if (exercise.status === "completed") completedExercises += 1;
12368
- completedSets += countCompletedSets(exercise);
12369
- totalSets += exercise.totalPlannedSets;
12370
- }
12371
- return {
12372
- completedExercises,
12373
- totalExercises: exercises.length,
12374
- completedSets,
12375
- totalSets,
12376
- fraction: totalSets === 0 ? 0 : completedSets / totalSets
12377
- };
12378
- }
12379
- function findActiveExercise(exercises) {
12380
- return exercises.find((exercise) => exercise.status === "active") ?? null;
12381
- }
12382
- function statusToCardState(status, focused) {
12383
- if (status === "upcoming") return "upcoming";
12384
- return focused ? "expanded" : "collapsed";
12385
- }
12386
- function toActiveCardProps(exercise, focused, onToggle, supersetPosition = null, supersetColor) {
12387
- const state = statusToCardState(exercise.status, focused);
12388
- return {
12389
- name: exercise.name,
12390
- state,
12391
- onToggle,
12392
- summary: exercise.summary,
12393
- e1rm: exercise.e1rm,
12394
- isPR: exercise.isPR,
12395
- tempo: exercise.tempo,
12396
- setVelocities: exercise.setVelocities,
12397
- totalPlannedSets: exercise.totalPlannedSets,
12398
- sets: state === "expanded" ? exercise.sets : void 0,
12399
- prescription: exercise.prescription,
12400
- previousBest: exercise.previousBest,
12401
- supersetPosition,
12402
- supersetColor
12403
- };
12404
- }
12405
- function groupExercises(exercises, supersets) {
12406
- const byId = new Map(supersets.map((superset) => [superset.id, superset]));
12407
- const groups = [];
12408
- for (const exercise of exercises) {
12409
- const superset = exercise.supersetId != null ? byId.get(exercise.supersetId) : void 0;
12410
- const last = groups[groups.length - 1];
12411
- if (superset != null && last?.type === "superset" && last.superset.id === superset.id) {
12412
- last.exercises.push(exercise);
12413
- } else if (superset != null) {
12414
- groups.push({ type: "superset", superset, exercises: [exercise] });
12415
- } else {
12416
- groups.push({ type: "single", exercise });
12417
- }
12418
- }
12419
- return groups;
12420
- }
12421
- function supersetPositionAt(index, length) {
12422
- if (length === 1) return null;
12423
- if (index === 0) return "first";
12424
- if (index === length - 1) return "last";
12425
- return "middle";
12426
- }
12427
- function WorkoutHeader({
12428
- title,
12429
- subtitle,
12430
- progress
12431
- }) {
12432
- return /* @__PURE__ */ jsxs(reactNative.View, { style: { gap: 10 }, testID: "active-workout-page-header", children: [
12433
- /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-start justify-between", style: { gap: 8 }, children: [
12434
- /* @__PURE__ */ jsxs(reactNative.View, { style: { flexShrink: 1 }, children: [
12435
- /* @__PURE__ */ jsx(
12436
- reactNative.Text,
12437
- {
12438
- accessibilityRole: "header",
12439
- className: "text-text-primary",
12440
- style: {
12441
- fontSize: 20,
12442
- fontFamily: '"Space Grotesk", sans-serif',
12443
- fontWeight: "700"
12444
- },
12445
- testID: "active-workout-page-title",
12446
- children: title
12447
- }
12448
- ),
12449
- subtitle != null && /* @__PURE__ */ jsx(
12450
- reactNative.Text,
12451
- {
12452
- className: "text-text-secondary",
12453
- style: {
12454
- marginTop: 2,
12455
- fontSize: 12,
12456
- fontFamily: "Inter, sans-serif"
12457
- },
12458
- testID: "active-workout-page-subtitle",
12459
- children: subtitle
12460
- }
12461
- )
12462
- ] }),
12463
- /* @__PURE__ */ jsxs(reactNative.View, { style: { alignItems: "flex-end" }, testID: "active-workout-page-progress-count", children: [
12464
- /* @__PURE__ */ jsx(
12465
- reactNative.Text,
12466
- {
12467
- style: {
12468
- fontSize: 15,
12469
- fontFamily: '"Space Grotesk", sans-serif',
12470
- fontWeight: "700",
12471
- color: BRAND_PRIMARY13,
12472
- fontVariant: ["tabular-nums"]
12473
- },
12474
- children: `${progress.completedExercises}/${progress.totalExercises}`
12475
- }
12476
- ),
12477
- /* @__PURE__ */ jsx(
12478
- reactNative.Text,
12479
- {
12480
- className: "text-text-tertiary",
12481
- style: {
12482
- fontSize: 10,
12483
- fontFamily: "Inter, sans-serif",
12484
- fontWeight: "600",
12485
- letterSpacing: 0.4,
12486
- textTransform: "uppercase"
12487
- },
12488
- children: `${progress.completedSets}/${progress.totalSets} sets`
12489
- }
12490
- )
12491
- ] })
12492
- ] }),
12493
- /* @__PURE__ */ jsx(
12494
- reactNative.View,
12495
- {
12496
- className: "bg-border",
12497
- style: { height: 4, borderRadius: 2 },
12498
- testID: "active-workout-page-progress-track",
12499
- children: /* @__PURE__ */ jsx(
12500
- reactNative.View,
12501
- {
12502
- style: {
12503
- height: "100%",
12504
- width: `${Math.round(progress.fraction * 100)}%`,
12505
- backgroundColor: BRAND_PRIMARY13,
12506
- borderRadius: 2
12507
- },
12508
- testID: "active-workout-page-progress-fill"
12509
- }
12510
- )
12511
- }
12512
- )
12513
- ] });
12514
- }
12515
- function ExerciseList({ groups, focusedId, onToggle }) {
12516
- return /* @__PURE__ */ jsx(reactNative.View, { style: { gap: 10 }, testID: "active-workout-page-exercises", children: groups.map((group) => {
12517
- if (group.type === "single") {
12518
- return /* @__PURE__ */ jsx(
12519
- reactNative.View,
12520
- {
12521
- className: "bg-surface-elevated border-border",
12522
- style: {
12523
- borderWidth: 1,
12524
- borderRadius: 12
12525
- },
12526
- children: /* @__PURE__ */ jsx(
12527
- ExerciseCard,
12528
- {
12529
- ...toActiveCardProps(
12530
- group.exercise,
12531
- group.exercise.id === focusedId,
12532
- () => onToggle(group.exercise.id)
12533
- )
12534
- }
12535
- )
12536
- },
12537
- group.exercise.id
12538
- );
12539
- }
12540
- return /* @__PURE__ */ jsx(
12541
- SupersetWrapper,
12542
- {
12543
- label: group.superset.label,
12544
- color: group.superset.color,
12545
- children: group.exercises.map((exercise, index) => /* @__PURE__ */ jsx(
12546
- ExerciseCard,
12547
- {
12548
- ...toActiveCardProps(
12549
- exercise,
12550
- exercise.id === focusedId,
12551
- () => onToggle(exercise.id),
12552
- supersetPositionAt(index, group.exercises.length),
12553
- group.superset.color
12554
- )
12555
- },
12556
- exercise.id
12557
- ))
12558
- },
12559
- group.superset.id
12560
- );
12561
- }) });
12562
- }
12563
- function ActiveWorkoutPage({
12564
- title = "Active Workout",
12565
- subtitle,
12566
- exercises,
12567
- supersets = [],
12568
- input,
12569
- rest,
12570
- initialResting = false,
12571
- className,
12572
- ...props
12573
- }) {
12574
- const active = findActiveExercise(exercises);
12575
- const [focusedId, setFocusedId] = React24.useState(active?.id ?? null);
12576
- const [resting, setResting] = React24.useState(initialResting);
12577
- const [reps, setReps] = React24.useState(input?.reps ?? "");
12578
- const [weight, setWeight] = React24.useState(input?.weight ?? "");
12579
- const progress = React24.useMemo(() => deriveWorkoutProgress(exercises), [exercises]);
12580
- const groups = React24.useMemo(() => groupExercises(exercises, supersets), [exercises, supersets]);
12581
- const toggleFocus = (id) => setFocusedId((current) => current === id ? null : id);
12582
- const nextSetNumber = active != null ? countCompletedSets(active) + 1 : 1;
12583
- const canRecord = reps.trim() !== "" && weight.trim() !== "";
12584
- const showRest = resting && rest != null;
12585
- const showInput = !showRest && active != null;
11983
+ accessibilityElementsHidden: true,
11984
+ importantForAccessibility: "no-hide-descendants",
11985
+ testID: key
11986
+ },
11987
+ `${key}-${i}`
11988
+ ));
12586
11989
  return /* @__PURE__ */ jsxs(
12587
11990
  reactNative.View,
12588
11991
  {
12589
- className: ["bg-background-base", className].filter(Boolean).join(" "),
12590
- style: { position: "relative", width: 390 },
12591
- accessibilityRole: "main",
12592
- "aria-label": title,
12593
- testID: "active-workout-page",
11992
+ style: { width, height, position: "relative" },
11993
+ className,
11994
+ testID: "capacity-band-chart-root",
12594
11995
  ...props,
12595
11996
  children: [
12596
- /* @__PURE__ */ jsxs(reactNative.View, { style: { padding: 16, gap: 14 }, testID: "active-workout-page-content", children: [
12597
- /* @__PURE__ */ jsx(WorkoutHeader, { title, subtitle, progress }),
12598
- /* @__PURE__ */ jsx(ExerciseList, { groups, focusedId, onToggle: toggleFocus })
12599
- ] }),
12600
- (showRest || showInput) && /* @__PURE__ */ jsx(
11997
+ /* @__PURE__ */ jsxs(
12601
11998
  reactNative.View,
12602
11999
  {
12603
- style: { borderTopWidth: 1, borderTopColor: resolveColor("border-default") },
12604
- testID: "active-workout-page-bottom-bar",
12605
- children: showRest && rest != null ? /* @__PURE__ */ jsx(
12606
- RestTimer,
12607
- {
12608
- totalSeconds: rest.totalSeconds,
12609
- elapsedMs: rest.elapsedMs,
12610
- nextSetInfo: rest.nextSetInfo,
12611
- onSkip: () => setResting(false),
12612
- onAddTime: () => void 0,
12613
- visible: true
12614
- }
12615
- ) : active != null && /* @__PURE__ */ jsx(
12616
- InputBar,
12617
- {
12618
- exerciseName: active.name,
12619
- setNumber: nextSetNumber,
12620
- totalSets: active.totalPlannedSets,
12621
- reps,
12622
- weight,
12623
- unit: active.unit,
12624
- onRepsChange: setReps,
12625
- onWeightChange: setWeight,
12626
- onRecord: () => {
12627
- if (rest != null) setResting(true);
12628
- },
12629
- canRecord,
12630
- visible: true
12631
- }
12632
- )
12000
+ style: { position: "absolute", top: 0, left: 0, width, height },
12001
+ accessibilityRole: "image",
12002
+ accessibilityLabel: `Training capacity band chart. Current capacity: ${currentStatus(
12003
+ workouts
12004
+ )}. ${workouts.length} workout${workouts.length === 1 ? "" : "s"} shown.`,
12005
+ testID: "capacity-band-chart",
12006
+ children: [
12007
+ /* @__PURE__ */ jsx(
12008
+ reactNative.Animated.View,
12009
+ {
12010
+ style: { position: "absolute", top: 0, left: 0, height, width: revealWidth, overflow: "hidden" },
12011
+ accessibilityElementsHidden: true,
12012
+ importantForAccessibility: "no-hide-descendants",
12013
+ testID: "capacity-band-chart-reveal",
12014
+ children: /* @__PURE__ */ jsxs(reactNative.View, { style: { width, height, position: "relative" }, children: [
12015
+ projection && /* @__PURE__ */ jsxs(reactNative.View, { testID: "capacity-band-chart-projection", children: [
12016
+ renderColumns(buildColumns(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12017
+ renderColumns(buildColumns(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12018
+ renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
12019
+ renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
12020
+ renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
12021
+ renderEdges(buildEdges(restPixels, "yLow"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
12022
+ /* @__PURE__ */ jsx(
12023
+ reactNative.Text,
12024
+ {
12025
+ style: {
12026
+ position: "absolute",
12027
+ left: trainingPixels[trainingPixels.length - 1]?.x + 2,
12028
+ top: trainingPixels[trainingPixels.length - 1]?.yHigh - 12,
12029
+ fontSize: 9,
12030
+ fontFamily: "Inter, sans-serif",
12031
+ color: STATUS_SUCCESS3
12032
+ },
12033
+ testID: "capacity-band-chart-projection-training-label",
12034
+ children: "Training"
12035
+ }
12036
+ ),
12037
+ /* @__PURE__ */ jsx(
12038
+ reactNative.Text,
12039
+ {
12040
+ style: {
12041
+ position: "absolute",
12042
+ left: restPixels[restPixels.length - 1]?.x + 2,
12043
+ top: restPixels[restPixels.length - 1]?.yLow + 2,
12044
+ fontSize: 9,
12045
+ fontFamily: "Inter, sans-serif",
12046
+ color: STATUS_INFO
12047
+ },
12048
+ testID: "capacity-band-chart-projection-rest-label",
12049
+ children: "Rest"
12050
+ }
12051
+ )
12052
+ ] }),
12053
+ /* @__PURE__ */ jsx(reactNative.View, { testID: "capacity-band-chart-band", children: renderColumns(columns, BAND_FILL, "capacity-band-chart-band-cell") }),
12054
+ renderEdges(topEdge, BAND_EDGE, false, "capacity-band-chart-edge-top"),
12055
+ renderEdges(bottomEdge, BAND_EDGE, false, "capacity-band-chart-edge-bottom")
12056
+ ] })
12057
+ }
12058
+ ),
12059
+ /* @__PURE__ */ jsx(
12060
+ reactNative.Text,
12061
+ {
12062
+ className: "text-text-tertiary",
12063
+ style: {
12064
+ position: "absolute",
12065
+ left: PADDING_LEFT / 2 - 14,
12066
+ top: PADDING_TOP + plotHeight / 2 - 7,
12067
+ width: 32,
12068
+ textAlign: "center",
12069
+ fontSize: 9,
12070
+ fontFamily: "Inter, sans-serif",
12071
+ transform: [{ rotate: "-90deg" }]
12072
+ },
12073
+ accessibilityElementsHidden: true,
12074
+ importantForAccessibility: "no-hide-descendants",
12075
+ testID: "capacity-band-chart-y-axis-label",
12076
+ children: "Load"
12077
+ }
12078
+ ),
12079
+ band.map((point, i) => {
12080
+ if (i % labelStride !== 0 && i !== band.length - 1) return null;
12081
+ return /* @__PURE__ */ jsx(
12082
+ reactNative.Text,
12083
+ {
12084
+ className: "text-text-tertiary",
12085
+ style: {
12086
+ position: "absolute",
12087
+ left: toX(point.date) - 14,
12088
+ top: height - PADDING_BOTTOM + 2,
12089
+ width: 28,
12090
+ textAlign: "center",
12091
+ fontSize: 10,
12092
+ fontFamily: "Inter, sans-serif"
12093
+ },
12094
+ accessibilityElementsHidden: true,
12095
+ importantForAccessibility: "no-hide-descendants",
12096
+ testID: "capacity-band-chart-x-label",
12097
+ children: formatDate2(point.date)
12098
+ },
12099
+ `x-${point.date}`
12100
+ );
12101
+ })
12102
+ ]
12633
12103
  }
12634
- )
12104
+ ),
12105
+ workouts.map((workout, i) => {
12106
+ const cx = toX(workout.date);
12107
+ const cy = toY(workout.load);
12108
+ const color = DOT_COLORS[workout.status];
12109
+ const dotStyle = {
12110
+ width: DOT_SIZE,
12111
+ height: DOT_SIZE,
12112
+ borderRadius: DOT_SIZE / 2,
12113
+ backgroundColor: color,
12114
+ borderWidth: 2,
12115
+ borderColor: DOT_BORDER
12116
+ };
12117
+ const label = `Workout on ${formatDate2(workout.date)}, load ${workout.load}, ${STATUS_PHRASES[workout.status]}`;
12118
+ return /* @__PURE__ */ jsx(
12119
+ reactNative.Animated.View,
12120
+ {
12121
+ style: {
12122
+ position: "absolute",
12123
+ left: cx - DOT_SIZE / 2,
12124
+ top: cy - DOT_SIZE / 2,
12125
+ transform: [{ scale: dotAnims[i] ?? 1 }]
12126
+ },
12127
+ testID: "capacity-band-chart-dot-wrapper",
12128
+ children: onWorkoutPress ? /* @__PURE__ */ jsx(
12129
+ reactNative.Pressable,
12130
+ {
12131
+ onPress: () => onWorkoutPress(workout),
12132
+ accessibilityRole: "button",
12133
+ accessibilityLabel: label,
12134
+ style: dotStyle,
12135
+ testID: "capacity-band-chart-dot"
12136
+ }
12137
+ ) : /* @__PURE__ */ jsx(
12138
+ reactNative.View,
12139
+ {
12140
+ accessibilityElementsHidden: true,
12141
+ importantForAccessibility: "no-hide-descendants",
12142
+ style: dotStyle,
12143
+ testID: "capacity-band-chart-dot"
12144
+ }
12145
+ )
12146
+ },
12147
+ `dot-${workout.date}-${i}`
12148
+ );
12149
+ })
12635
12150
  ]
12636
12151
  }
12637
12152
  );
12638
12153
  }
12639
- var PALETTE = [
12640
- "#5B9BD5",
12641
- "#14B8A6",
12642
- "#F4A736",
12643
- "#F83030",
12644
- "#823CA0",
12645
- "#D4A520",
12646
- "#406D87",
12647
- "#43C6B7"
12648
- ];
12154
+ var PALETTE = DATAVIZ_CATEGORICAL_PALETTE;
12649
12155
  function scaleValue(v, scale) {
12650
12156
  if (v <= 0) return 0;
12651
12157
  if (scale === "sqrt") return Math.sqrt(v);
@@ -12761,16 +12267,7 @@ function Treemap({
12761
12267
  );
12762
12268
  }) });
12763
12269
  }
12764
- var PALETTE2 = [
12765
- "#5B9BD5",
12766
- "#14B8A6",
12767
- "#F4A736",
12768
- "#F83030",
12769
- "#823CA0",
12770
- "#D4A520",
12771
- "#406D87",
12772
- "#43C6B7"
12773
- ];
12270
+ var PALETTE2 = DATAVIZ_CATEGORICAL_PALETTE;
12774
12271
  var GRID_LINE2 = "rgba(255,255,255,0.07)";
12775
12272
  var AXIS_LINE = "rgba(255,255,255,0.18)";
12776
12273
  var DIAGONAL_LINE = "rgba(255,255,255,0.28)";
@@ -12860,28 +12357,28 @@ function Scatter({
12860
12357
  testID: "scatter-canvas",
12861
12358
  style: { position: "absolute", top: 0, left: 0, width, height, overflow: "hidden" },
12862
12359
  children: [
12863
- ticksOf(yd, TICK_COUNT).map((t2, i) => {
12864
- const y = toY(t2);
12360
+ ticksOf(yd, TICK_COUNT).map((t12, i) => {
12361
+ const y = toY(t12);
12865
12362
  return /* @__PURE__ */ jsx(
12866
12363
  reactNative.View,
12867
12364
  {
12868
12365
  accessibilityElementsHidden: true,
12869
12366
  testID: "scatter-gridline-y",
12870
12367
  style: { position: "absolute", left: PLOT_LEFT2, width: innerW, top: y, height: 1, backgroundColor: GRID_LINE2 },
12871
- children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t2) })
12368
+ children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", left: -PLOT_LEFT2, top: -6, width: PLOT_LEFT2 - 6, textAlign: "right" }, children: formatTick(t12) })
12872
12369
  },
12873
12370
  `y-${i}`
12874
12371
  );
12875
12372
  }),
12876
- ticksOf(xd, TICK_COUNT).map((t2, i) => {
12877
- const x = toX(t2);
12373
+ ticksOf(xd, TICK_COUNT).map((t12, i) => {
12374
+ const x = toX(t12);
12878
12375
  return /* @__PURE__ */ jsx(
12879
12376
  reactNative.View,
12880
12377
  {
12881
12378
  accessibilityElementsHidden: true,
12882
12379
  testID: "scatter-gridline-x",
12883
12380
  style: { position: "absolute", top: PLOT_TOP, height: innerH, left: x, width: 1, backgroundColor: GRID_LINE2 },
12884
- children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t2) })
12381
+ children: /* @__PURE__ */ jsx(reactNative.Text, { className: "text-[9px] text-text-tertiary", style: { position: "absolute", top: innerH + 4, left: -16, width: 32, textAlign: "center" }, children: formatTick(t12) })
12885
12382
  },
12886
12383
  `x-${i}`
12887
12384
  );
@@ -12941,26 +12438,27 @@ function Scatter({
12941
12438
  }) })
12942
12439
  ] });
12943
12440
  }
12944
- var STATUS_SUCCESS4 = "#14B8A6";
12945
- var STATUS_WARNING4 = "#FFB020";
12946
- var STATUS_ERROR3 = "#D14343";
12441
+ var sem2 = getSemanticColors("dark");
12442
+ var STATUS_SUCCESS4 = sem2["status-success"];
12443
+ var STATUS_WARNING4 = sem2["status-warning"];
12444
+ var STATUS_ERROR4 = sem2["status-error"];
12947
12445
  var TRACK = "rgba(255,255,255,0.08)";
12948
12446
  var DEFAULT_THRESHOLDS = [
12949
- { value: 0, color: STATUS_ERROR3 },
12447
+ { value: 0, color: STATUS_ERROR4 },
12950
12448
  { value: 60, color: STATUS_WARNING4 },
12951
12449
  { value: 80, color: STATUS_SUCCESS4 }
12952
12450
  ];
12953
12451
  var ARC_START = 135;
12954
12452
  var ARC_SWEEP = 270;
12955
12453
  var SEGMENTS = 40;
12956
- function clamp013(n) {
12454
+ function clamp012(n) {
12957
12455
  if (Number.isNaN(n)) return 0;
12958
12456
  return Math.max(0, Math.min(1, n));
12959
12457
  }
12960
12458
  function formatValue2(v) {
12961
12459
  return Number.isInteger(v) ? `${v}` : v.toFixed(1);
12962
12460
  }
12963
- function bandColor(fraction, bands, min, max) {
12461
+ function bandColor2(fraction, bands, min, max) {
12964
12462
  const value = min + fraction * (max - min);
12965
12463
  const sorted = [...bands].sort((a, b) => a.value - b.value);
12966
12464
  let color = sorted[0]?.color ?? STATUS_SUCCESS4;
@@ -12998,10 +12496,10 @@ function Gauge2({
12998
12496
  ...props
12999
12497
  }) {
13000
12498
  const span = max - min || 1;
13001
- const fill = clamp013((value - min) / span);
13002
- const activeColor = (f) => color ?? bandColor(f, thresholds, min, max);
12499
+ const fill = clamp012((value - min) / span);
12500
+ const activeColor = (f) => color ?? bandColor2(f, thresholds, min, max);
13003
12501
  const ticks = buildTicks(fill, size, activeColor);
13004
- const displayColor = color ?? bandColor(fill, thresholds, min, max);
12502
+ const displayColor = color ?? bandColor2(fill, thresholds, min, max);
13005
12503
  const tickLength = size * 0.11;
13006
12504
  const tickThickness = Math.max(2, size * 0.9 / SEGMENTS);
13007
12505
  const ariaLabel = `${label ? `${label}: ` : ""}${formatValue2(value)}${unit ?? ""} of ${formatValue2(max)}`;
@@ -13015,23 +12513,23 @@ function Gauge2({
13015
12513
  testID: "gauge",
13016
12514
  ...props,
13017
12515
  children: [
13018
- ticks.map((t2) => /* @__PURE__ */ jsx(
12516
+ ticks.map((t12) => /* @__PURE__ */ jsx(
13019
12517
  reactNative.View,
13020
12518
  {
13021
12519
  accessibilityElementsHidden: true,
13022
12520
  testID: "gauge-segment",
13023
12521
  style: {
13024
12522
  position: "absolute",
13025
- left: t2.left - tickThickness / 2,
13026
- top: t2.top - tickLength / 2,
12523
+ left: t12.left - tickThickness / 2,
12524
+ top: t12.top - tickLength / 2,
13027
12525
  width: tickThickness,
13028
12526
  height: tickLength,
13029
12527
  borderRadius: tickThickness / 2,
13030
- backgroundColor: t2.color,
13031
- transform: [{ rotate: t2.rotate }]
12528
+ backgroundColor: t12.color,
12529
+ transform: [{ rotate: t12.rotate }]
13032
12530
  }
13033
12531
  },
13034
- t2.key
12532
+ t12.key
13035
12533
  )),
13036
12534
  /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "absolute", top: 0, left: 0, width: size, height: size }, className: "items-center justify-center", children: [
13037
12535
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-baseline gap-0.5", children: [
@@ -13044,6 +12542,203 @@ function Gauge2({
13044
12542
  }
13045
12543
  );
13046
12544
  }
12545
+ function BrandLockup({
12546
+ subtitle = "wall dashboard",
12547
+ showSubtitle = true,
12548
+ className,
12549
+ ...props
12550
+ }) {
12551
+ return /* @__PURE__ */ jsxs(reactNative.View, { className: cn("flex-row items-center gap-[7px]", className), ...props, children: [
12552
+ /* @__PURE__ */ jsx(reactNative.View, { className: "text-brand-primary", children: /* @__PURE__ */ jsx(VoltrasMark, { size: 14, color: "currentColor" }) }),
12553
+ /* @__PURE__ */ jsx(
12554
+ Typography,
12555
+ {
12556
+ variant: "button",
12557
+ color: "primary",
12558
+ className: "text-[12px] font-extrabold tracking-[1.5px]",
12559
+ children: "VOLTRAS"
12560
+ }
12561
+ ),
12562
+ showSubtitle && subtitle ? /* @__PURE__ */ jsxs(
12563
+ Typography,
12564
+ {
12565
+ variant: "button",
12566
+ color: "tertiary",
12567
+ className: "text-[10px] font-semibold tracking-[0.4px]",
12568
+ children: [
12569
+ "/ ",
12570
+ subtitle
12571
+ ]
12572
+ }
12573
+ ) : null
12574
+ ] });
12575
+ }
12576
+ var stateConfig = {
12577
+ // live = vivid green with an expanding ring; rest = solid amber (no pulse — operator); idle is dim.
12578
+ live: { label: "LIVE", color: "success", pulse: "ping", textColor: "primary" },
12579
+ rest: { label: "REST", color: "warning", pulse: false, textColor: "primary" },
12580
+ idle: { label: "IDLE", color: "default", pulse: false, textColor: "secondary" }
12581
+ };
12582
+ function SessionStatePill({ state, label, className, ...props }) {
12583
+ const cfg = stateConfig[state];
12584
+ return /* @__PURE__ */ jsxs(reactNative.View, { className: cn("flex-row items-center gap-2", className), ...props, children: [
12585
+ /* @__PURE__ */ jsx(Indicator, { size: "md", color: cfg.color, pulse: cfg.pulse }),
12586
+ /* @__PURE__ */ jsx(Typography, { variant: "monoLabel", color: cfg.textColor, className: "text-[11px]", children: label ?? cfg.label })
12587
+ ] });
12588
+ }
12589
+ var statusTextClass = {
12590
+ connected: "text-status-success",
12591
+ degraded: "text-status-warning",
12592
+ lost: "text-status-error-vivid"
12593
+ };
12594
+ function DeviceIndicator({
12595
+ status = "connected",
12596
+ onPress,
12597
+ label = "Devices",
12598
+ className,
12599
+ ...props
12600
+ }) {
12601
+ const Wrapper = onPress ? reactNative.Pressable : reactNative.View;
12602
+ return /* @__PURE__ */ jsx(
12603
+ Wrapper,
12604
+ {
12605
+ accessibilityLabel: label,
12606
+ ...onPress ? { onPress, accessibilityRole: "button" } : {},
12607
+ className: cn(
12608
+ "w-[26px] h-[28px] items-center justify-center rounded-[7px]",
12609
+ statusTextClass[status],
12610
+ className
12611
+ ),
12612
+ ...props,
12613
+ children: /* @__PURE__ */ jsx(BluetoothIcon, { size: 18, color: "currentColor", title: label })
12614
+ }
12615
+ );
12616
+ }
12617
+ var stateColor = {
12618
+ connected: "success",
12619
+ available: "info",
12620
+ degraded: "warning",
12621
+ lost: "error-vivid"
12622
+ };
12623
+ function DeviceRow({ device, onPress, className, ...props }) {
12624
+ const Wrapper = onPress ? reactNative.Pressable : reactNative.View;
12625
+ return /* @__PURE__ */ jsxs(
12626
+ Wrapper,
12627
+ {
12628
+ ...onPress ? { onPress, accessibilityRole: "button" } : {},
12629
+ className: cn("flex-row items-center gap-[10px] px-2 py-[9px] rounded-[7px]", className),
12630
+ ...props,
12631
+ children: [
12632
+ /* @__PURE__ */ jsx(Indicator, { size: "md", color: stateColor[device.state] }),
12633
+ /* @__PURE__ */ jsx(
12634
+ Typography,
12635
+ {
12636
+ variant: "body2",
12637
+ color: device.slot ? "primary" : "secondary",
12638
+ className: "flex-1 font-bold text-[12px]",
12639
+ children: device.nickname
12640
+ }
12641
+ ),
12642
+ /* @__PURE__ */ jsx(Typography, { variant: "mono", color: "tertiary", className: "text-[10px]", children: device.id })
12643
+ ]
12644
+ }
12645
+ );
12646
+ }
12647
+ function aggregateStatus(devices) {
12648
+ const bound = devices.filter((d) => d.slot);
12649
+ if (bound.some((d) => d.state === "lost")) return "lost";
12650
+ if (bound.some((d) => d.state === "degraded")) return "degraded";
12651
+ return "connected";
12652
+ }
12653
+ function DeviceMenu({
12654
+ devices,
12655
+ isOpen,
12656
+ onOpenChange,
12657
+ onSelectDevice,
12658
+ className
12659
+ }) {
12660
+ const status = aggregateStatus(devices);
12661
+ const boundCount = devices.filter((d) => d.slot).length;
12662
+ const availCount = devices.filter((d) => !d.slot).length;
12663
+ return /* @__PURE__ */ jsxs(Popover, { placement: "bottom", isOpen, onOpenChange, className, children: [
12664
+ /* @__PURE__ */ jsx(PopoverTrigger, { children: /* @__PURE__ */ jsx(DeviceIndicator, { status }) }),
12665
+ /* @__PURE__ */ jsxs(PopoverContent, { className: "right-0 left-auto mt-[10px] w-[326px] bg-surface-elevated border border-border-strong rounded-[10px] p-[7px]", children: [
12666
+ /* @__PURE__ */ jsxs(
12667
+ Typography,
12668
+ {
12669
+ variant: "monoLabel",
12670
+ color: "tertiary",
12671
+ className: "text-[9px] tracking-[0.8px] px-2 pt-[6px] pb-2",
12672
+ children: [
12673
+ "Devices \u2014 ",
12674
+ boundCount,
12675
+ " bound \xB7 ",
12676
+ availCount,
12677
+ " available"
12678
+ ]
12679
+ }
12680
+ ),
12681
+ devices.map((device, i) => /* @__PURE__ */ jsx(reactNative.View, { className: cn(i > 0 && "border-t border-border"), children: /* @__PURE__ */ jsx(
12682
+ DeviceRow,
12683
+ {
12684
+ device,
12685
+ onPress: onSelectDevice ? () => onSelectDevice(device) : void 0
12686
+ }
12687
+ ) }, device.id))
12688
+ ] })
12689
+ ] });
12690
+ }
12691
+ var SUBTITLE_MIN = 1024;
12692
+ var CLOCK_MIN = 720;
12693
+ function TopBar({
12694
+ state,
12695
+ devices,
12696
+ time,
12697
+ subtitle = "wall dashboard",
12698
+ showSubtitle,
12699
+ showClock,
12700
+ onSelectDevice,
12701
+ className
12702
+ }) {
12703
+ const [width, setWidth] = React24.useState(SUBTITLE_MIN);
12704
+ const onLayout = (e) => setWidth(e.nativeEvent.layout.width);
12705
+ const subtitleVisible = showSubtitle ?? width >= SUBTITLE_MIN;
12706
+ const clockVisible = showClock ?? width >= CLOCK_MIN;
12707
+ return /* @__PURE__ */ jsxs(
12708
+ reactNative.View,
12709
+ {
12710
+ onLayout,
12711
+ style: surfaceGradient.chrome(),
12712
+ className: cn(
12713
+ "h-[46px] flex-row items-center gap-[14px] px-4 bg-surface-elevated border-b border-border",
12714
+ className
12715
+ ),
12716
+ children: [
12717
+ /* @__PURE__ */ jsx(BrandLockup, { subtitle, showSubtitle: subtitleVisible }),
12718
+ /* @__PURE__ */ jsxs(reactNative.View, { className: "ml-auto flex-row items-center gap-[12px]", children: [
12719
+ /* @__PURE__ */ jsx(SessionStatePill, { state }),
12720
+ /* @__PURE__ */ jsx(Divider, { orientation: "vertical", className: "h-4 bg-border-prominent" }),
12721
+ /* @__PURE__ */ jsx(DeviceMenu, { devices, onSelectDevice }),
12722
+ clockVisible ? /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
12723
+ /* @__PURE__ */ jsx(Divider, { orientation: "vertical", className: "h-4 bg-border-prominent" }),
12724
+ /* @__PURE__ */ jsx(
12725
+ DateTime,
12726
+ {
12727
+ value: time,
12728
+ live: time == null,
12729
+ format: "time",
12730
+ hour12: false,
12731
+ variant: "mono",
12732
+ color: "secondary",
12733
+ className: "text-[11px] min-w-[38px] text-right"
12734
+ }
12735
+ )
12736
+ ] }) : null
12737
+ ] })
12738
+ ]
12739
+ }
12740
+ );
12741
+ }
13047
12742
 
13048
12743
  // src/utils/form.ts
13049
12744
  function getFieldValidationProps(state) {
@@ -13137,7 +12832,6 @@ exports.Accordion = Accordion;
13137
12832
  exports.AccordionButton = AccordionButton;
13138
12833
  exports.AccordionItem = AccordionItem;
13139
12834
  exports.AccordionPanel = AccordionPanel;
13140
- exports.ActiveWorkoutPage = ActiveWorkoutPage;
13141
12835
  exports.Alert = Alert;
13142
12836
  exports.AlertDescription = AlertDescription;
13143
12837
  exports.AlertTitle = AlertTitle;
@@ -13148,12 +12842,15 @@ exports.AvatarGroup = AvatarGroup;
13148
12842
  exports.Badge = Badge;
13149
12843
  exports.BadgeText = BadgeText;
13150
12844
  exports.BaseBadge = BaseBadge;
12845
+ exports.BluetoothIcon = BluetoothIcon;
12846
+ exports.BrandLockup = BrandLockup;
13151
12847
  exports.BreadcrumbItem = BreadcrumbItem;
13152
12848
  exports.Breadcrumbs = Breadcrumbs;
13153
12849
  exports.Button = Button;
13154
12850
  exports.ButtonIcon = ButtonIcon;
13155
12851
  exports.ButtonSpinner = ButtonSpinner;
13156
12852
  exports.ButtonText = ButtonText;
12853
+ exports.CATEGORICAL_CVD_SAFE_MAX = CATEGORICAL_CVD_SAFE_MAX;
13157
12854
  exports.CapacityBandChart = CapacityBandChart;
13158
12855
  exports.Caption = Caption;
13159
12856
  exports.Card = Card;
@@ -13173,15 +12870,17 @@ exports.CollapseContent = CollapseContent;
13173
12870
  exports.DataRow = DataRow;
13174
12871
  exports.DateTime = DateTime;
13175
12872
  exports.DeviationBar = DeviationBar;
12873
+ exports.DeviceIndicator = DeviceIndicator;
12874
+ exports.DeviceMenu = DeviceMenu;
12875
+ exports.DeviceRow = DeviceRow;
13176
12876
  exports.Divider = Divider;
13177
12877
  exports.Drawer = Drawer;
13178
12878
  exports.DrawerBody = DrawerBody;
13179
12879
  exports.DrawerFooter = DrawerFooter;
13180
12880
  exports.DrawerHeader = DrawerHeader;
13181
- exports.EXERCISE_DETAIL_TABS = EXERCISE_DETAIL_TABS;
12881
+ exports.DumbbellIcon = DumbbellIcon;
13182
12882
  exports.EmptyState = EmptyState;
13183
12883
  exports.ExerciseCard = ExerciseCard;
13184
- exports.ExerciseDetailPage = ExerciseDetailPage;
13185
12884
  exports.FormActions = FormActions;
13186
12885
  exports.FormField = FormField;
13187
12886
  exports.FormRow = FormRow;
@@ -13234,7 +12933,6 @@ exports.PopoverContent = PopoverContent;
13234
12933
  exports.PopoverTrigger = PopoverTrigger;
13235
12934
  exports.PrBadge = PrBadge;
13236
12935
  exports.PrHistoryModal = PrHistoryModal;
13237
- exports.ProgramPlanningPage = ProgramPlanningPage;
13238
12936
  exports.Progress = Progress;
13239
12937
  exports.ProgressSteps = ProgressSteps;
13240
12938
  exports.Radio = Radio;
@@ -13246,6 +12944,7 @@ exports.Section = Section;
13246
12944
  exports.SectionContent = SectionContent;
13247
12945
  exports.SectionHeader = SectionHeader;
13248
12946
  exports.Select = Select;
12947
+ exports.SessionStatePill = SessionStatePill;
13249
12948
  exports.SetRow = SetRow;
13250
12949
  exports.Sidebar = Sidebar;
13251
12950
  exports.SidebarContent = SidebarContent;
@@ -13262,6 +12961,7 @@ exports.SkeletonText = SkeletonText;
13262
12961
  exports.Sparkline = Sparkline;
13263
12962
  exports.Spinner = Spinner;
13264
12963
  exports.Stack = Stack;
12964
+ exports.StarIcon = StarIcon;
13265
12965
  exports.StatusDot = StatusDot;
13266
12966
  exports.StepContent = StepContent;
13267
12967
  exports.StepIndicator = StepIndicator;
@@ -13271,6 +12971,7 @@ exports.StepperStep = Step;
13271
12971
  exports.StrengthTrendChart = StrengthTrendChart;
13272
12972
  exports.SupersetWrapper = SupersetWrapper;
13273
12973
  exports.Surface = Surface;
12974
+ exports.SvgIcon = SvgIcon;
13274
12975
  exports.Switch = Switch;
13275
12976
  exports.TEMPO_PACING = TEMPO_PACING;
13276
12977
  exports.Tab = Tab;
@@ -13287,16 +12988,17 @@ exports.TableRow = TableRow;
13287
12988
  exports.Tabs = Tabs;
13288
12989
  exports.TempoBar = TempoBar;
13289
12990
  exports.TempoDisplay = TempoDisplay;
13290
- exports.ThemeProvider = ThemeProvider;
13291
12991
  exports.Toast = Toast;
13292
12992
  exports.ToastProvider = ToastProvider;
13293
12993
  exports.ToolbarButton = ToolbarButton;
13294
12994
  exports.ToolbarButtonGroup = ToolbarButtonGroup;
13295
12995
  exports.Tooltip = Tooltip;
12996
+ exports.TopBar = TopBar;
13296
12997
  exports.Treemap = Treemap;
13297
12998
  exports.Typography = Typography;
13298
12999
  exports.VStack = VStack;
13299
13000
  exports.VelocityStrip = VelocityStrip;
13001
+ exports.VoltrasMark = VoltrasMark;
13300
13002
  exports.WeekRow = WeekRow;
13301
13003
  exports.WeightBadge = WeightBadge;
13302
13004
  exports.WorkoutCard = WorkoutCard;
@@ -13304,14 +13006,14 @@ exports.WorkoutPill = WorkoutPill;
13304
13006
  exports.alpha = alpha;
13305
13007
  exports.applyThemePreset = applyThemePreset;
13306
13008
  exports.audiobookPreset = audiobookPreset;
13307
- exports.buildBreadcrumbs = buildBreadcrumbs;
13009
+ exports.bestTextColor = bestTextColor;
13308
13010
  exports.buttonSizes = buttonSizes;
13309
13011
  exports.calculateMeanVelocity = calculateMeanVelocity;
13310
13012
  exports.calculateVelocityLoss = calculateVelocityLoss;
13013
+ exports.categoricalPalette = categoricalPalette;
13311
13014
  exports.cn = cn;
13312
13015
  exports.componentElevationRanges = componentElevationRanges;
13313
13016
  exports.composeValidators = composeValidators;
13314
- exports.countCompletedSets = countCompletedSets;
13315
13017
  exports.createFieldId = createFieldId;
13316
13018
  exports.createFlatShadow = createFlatShadow;
13317
13019
  exports.createPressedHoverShadow = createPressedHoverShadow;
@@ -13321,20 +13023,15 @@ exports.createRaisedShadow = createRaisedShadow;
13321
13023
  exports.darkThemeCSSVars = darkThemeCSSVars;
13322
13024
  exports.darken = darken;
13323
13025
  exports.defaultPreset = defaultPreset;
13324
- exports.deriveExerciseStats = deriveExerciseStats;
13325
- exports.deriveNavLevel = deriveNavLevel;
13326
- exports.deriveWorkoutProgress = deriveWorkoutProgress;
13327
13026
  exports.discreteRainbow = discreteRainbow;
13027
+ exports.divergingScale = divergingScale;
13328
13028
  exports.elevationSystem = elevationSystem;
13329
- exports.findActiveExercise = findActiveExercise;
13330
- exports.findMeso = findMeso;
13331
- exports.findWeek = findWeek;
13332
- exports.findWorkout = findWorkout;
13333
13029
  exports.formatDateTime = formatDateTime;
13334
13030
  exports.formatPrescription = formatPrescription;
13335
13031
  exports.formatSignedPct = formatSignedPct;
13336
13032
  exports.formatVelocity = formatVelocity;
13337
13033
  exports.getBaseSurfaceColor = getBaseSurfaceColor;
13034
+ exports.getCategoricalColor = getCategoricalColor;
13338
13035
  exports.getContrastText = getContrastText;
13339
13036
  exports.getDiscreteRainbowColor = getDiscreteRainbowColor;
13340
13037
  exports.getElevationConfig = getElevationConfig;
@@ -13355,7 +13052,6 @@ exports.getValidatedElevation = getValidatedElevation;
13355
13052
  exports.getVelocityZoneColor = getVelocityZoneColor;
13356
13053
  exports.getVelocityZoneName = getVelocityZoneName;
13357
13054
  exports.gluestackConfig = gluestackConfig;
13358
- exports.groupExercises = groupExercises;
13359
13055
  exports.hasMaxLength = hasMaxLength;
13360
13056
  exports.hasMinLength = hasMinLength;
13361
13057
  exports.hexToRgb = hexToRgb;
@@ -13366,10 +13062,12 @@ exports.isEmpty = isEmpty;
13366
13062
  exports.isValidEmail = isValidEmail;
13367
13063
  exports.lightThemeCSSVars = lightThemeCSSVars;
13368
13064
  exports.lighten = lighten;
13065
+ exports.linearGradient = linearGradient;
13369
13066
  exports.neumorphicShadows = neumorphicShadows;
13370
13067
  exports.primitiveBorderRadius = primitiveBorderRadius;
13371
13068
  exports.primitiveBreakpoints = primitiveBreakpoints;
13372
13069
  exports.primitiveColors = primitiveColors;
13070
+ exports.primitiveRamps = primitiveRamps;
13373
13071
  exports.primitiveShadows = primitiveShadows;
13374
13072
  exports.primitiveSpacing = primitiveSpacing;
13375
13073
  exports.primitiveTypography = primitiveTypography;
@@ -13384,12 +13082,9 @@ exports.rpeColor = rpeColor;
13384
13082
  exports.semanticColorsDark = semanticColorsDark;
13385
13083
  exports.semanticColorsLight = semanticColorsLight;
13386
13084
  exports.semanticTypography = semanticTypography;
13085
+ exports.sequentialEffort = sequentialEffort;
13387
13086
  exports.shadowColors = shadowColors;
13388
- exports.statusToCardState = statusToCardState;
13389
- exports.summarizeVbt = summarizeVbt;
13390
- exports.toActiveCardProps = toActiveCardProps;
13391
- exports.toExerciseCardProps = toExerciseCardProps;
13392
- exports.toProgressBarMesos = toProgressBarMesos;
13087
+ exports.surfaceGradient = surfaceGradient;
13393
13088
  exports.useTable = useTable;
13394
13089
  exports.useToast = useToast;
13395
13090
  exports.useToolbarButton = useToolbarButton;