@titan-design/react-ui 0.7.0 → 0.9.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 (52) hide show
  1. package/dist/index.d.mts +112 -55
  2. package/dist/index.d.ts +112 -55
  3. package/dist/index.js +795 -501
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +795 -498
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/{pages-8u_4WbL-.d.mts → pages-D7hOD4Vj.d.mts} +17 -8
  8. package/dist/{pages-DaWiBOGa.d.ts → pages-DyEx-lBf.d.ts} +17 -8
  9. package/dist/pages.d.mts +1 -1
  10. package/dist/pages.d.ts +1 -1
  11. package/dist/pages.js +691 -250
  12. package/dist/pages.js.map +1 -1
  13. package/dist/pages.mjs +692 -251
  14. package/dist/pages.mjs.map +1 -1
  15. package/package.json +1 -1
  16. package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +90 -0
  17. package/src/components/custom/CircularTimer/CircularTimer.test.tsx +62 -0
  18. package/src/components/custom/CircularTimer/CircularTimer.tsx +112 -0
  19. package/src/components/custom/CircularTimer/index.ts +1 -0
  20. package/src/components/custom/Workout/ExerciseIndicator.tsx +1 -1
  21. package/src/components/custom/Workout/FatigueMeter.stories.tsx +2 -2
  22. package/src/components/custom/Workout/FatigueMeter.test.tsx +12 -0
  23. package/src/components/custom/Workout/FatigueMeter.tsx +7 -3
  24. package/src/components/custom/Workout/README.md +70 -3
  25. package/src/components/custom/Workout/RestTimer.stories.tsx +132 -2
  26. package/src/components/custom/Workout/RestTimer.test.tsx +73 -0
  27. package/src/components/custom/Workout/RestTimer.tsx +141 -50
  28. package/src/components/custom/Workout/SetsRepsLoad.test.tsx +6 -0
  29. package/src/components/custom/Workout/SetsRepsLoad.tsx +15 -6
  30. package/src/components/custom/Workout/TempoDisplay.stories.tsx +240 -65
  31. package/src/components/custom/Workout/TempoDisplay.test.tsx +61 -16
  32. package/src/components/custom/Workout/TempoDisplay.tsx +173 -60
  33. package/src/components/custom/Workout/VelocityStrip.stories.tsx +152 -9
  34. package/src/components/custom/Workout/VelocityStrip.test.tsx +67 -3
  35. package/src/components/custom/Workout/VelocityStrip.tsx +323 -42
  36. package/src/components/custom/Workout/ZoneTrack.stories.tsx +2 -3
  37. package/src/components/custom/Workout/ZoneTrack.test.tsx +34 -0
  38. package/src/components/custom/Workout/ZoneTrack.tsx +91 -19
  39. package/src/components/custom/Workout/index.ts +1 -9
  40. package/src/components/custom/index.ts +1 -0
  41. package/src/components/ui/alert/Alert.stories.tsx +85 -2
  42. package/src/components/ui/alert/Alert.test.tsx +52 -0
  43. package/src/components/ui/alert/Alert.tsx +54 -20
  44. package/src/components/ui/progress/Progress.tsx +47 -22
  45. package/src/lab/north-star/LivePage.tsx +101 -0
  46. package/src/lab/north-star/LiveView.tsx +409 -0
  47. package/src/lab/north-star/LiveWallDashboard.stories.tsx +129 -0
  48. package/src/lab/north-star/RestView.tsx +140 -0
  49. package/src/lab/north-star/fixtures.ts +224 -0
  50. package/src/components/custom/Workout/TempoBar.stories.tsx +0 -95
  51. package/src/components/custom/Workout/TempoBar.test.tsx +0 -103
  52. package/src/components/custom/Workout/TempoBar.tsx +0 -215
package/dist/index.js CHANGED
@@ -158,6 +158,7 @@ var statusColors = {
158
158
  subtle: "bg-status-success-subtle",
159
159
  outline: "border-2 border-status-success bg-transparent",
160
160
  solid: "bg-status-success",
161
+ border: "border-status-success",
161
162
  icon: "text-status-success",
162
163
  text: "text-status-success"
163
164
  },
@@ -165,6 +166,7 @@ var statusColors = {
165
166
  subtle: "bg-status-info-subtle",
166
167
  outline: "border-2 border-status-info bg-transparent",
167
168
  solid: "bg-status-info",
169
+ border: "border-status-info",
168
170
  icon: "text-status-info",
169
171
  text: "text-status-info"
170
172
  },
@@ -172,6 +174,7 @@ var statusColors = {
172
174
  subtle: "bg-status-warning-subtle",
173
175
  outline: "border-2 border-status-warning bg-transparent",
174
176
  solid: "bg-status-warning",
177
+ border: "border-status-warning",
175
178
  icon: "text-status-warning",
176
179
  text: "text-status-warning"
177
180
  },
@@ -179,6 +182,7 @@ var statusColors = {
179
182
  subtle: "bg-status-error-subtle",
180
183
  outline: "border-2 border-status-error bg-transparent",
181
184
  solid: "bg-status-error",
185
+ border: "border-status-error",
182
186
  icon: "text-status-error",
183
187
  text: "text-status-error"
184
188
  }
@@ -192,6 +196,8 @@ var defaultIcons = {
192
196
  function Alert({
193
197
  status = "info",
194
198
  variant = "subtle",
199
+ size = "default",
200
+ message,
195
201
  icon,
196
202
  showIcon = true,
197
203
  onClose,
@@ -201,28 +207,43 @@ function Alert({
201
207
  }) {
202
208
  const colors = statusColors[status];
203
209
  const isSolid = variant === "solid";
210
+ const isCompact = size === "compact";
204
211
  return /* @__PURE__ */ jsxs(
205
212
  reactNative.View,
206
213
  {
207
214
  accessibilityRole: "alert",
208
215
  className: cn(
209
- "flex-row items-start p-4 rounded-lg",
216
+ isCompact ? "flex-row items-center px-3 py-2 rounded-lg" : "flex-row items-start p-4 rounded-lg",
210
217
  colors[variant],
218
+ // A compact `subtle` pill gets a hairline status border so it reads as a
219
+ // defined cue on a dark wall (the CueFlag look); other variants carry their own.
220
+ isCompact && variant === "subtle" && cn("border", colors.border),
211
221
  className
212
222
  ),
213
223
  ...props,
214
224
  children: [
215
- showIcon && /* @__PURE__ */ jsx(reactNative.View, { className: "mr-3 mt-0.5", children: icon || /* @__PURE__ */ jsx(
225
+ showIcon && /* @__PURE__ */ jsx(reactNative.View, { className: isCompact ? "mr-2" : "mr-3 mt-0.5", children: icon || /* @__PURE__ */ jsx(
216
226
  reactNative.Text,
217
227
  {
218
228
  className: cn(
219
- "text-lg font-bold",
229
+ "font-bold",
230
+ isCompact ? "text-base" : "text-lg",
220
231
  isSolid ? "text-white" : colors.icon
221
232
  ),
222
233
  children: defaultIcons[status]
223
234
  }
224
235
  ) }),
225
- /* @__PURE__ */ jsx(reactNative.View, { className: "flex-1", children }),
236
+ /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-1", children: [
237
+ message != null && /* @__PURE__ */ jsx(
238
+ reactNative.Text,
239
+ {
240
+ className: cn("text-sm font-semibold", isSolid ? "text-white" : colors.text),
241
+ testID: "alert-message",
242
+ children: message
243
+ }
244
+ ),
245
+ children
246
+ ] }),
226
247
  onClose && /* @__PURE__ */ jsx(
227
248
  reactNative.Pressable,
228
249
  {
@@ -5072,6 +5093,8 @@ function CircularProgress({
5072
5093
  color = "primary",
5073
5094
  showValue = false,
5074
5095
  formatValue: formatValue3 = (v, m) => `${Math.round(v / m * 100)}%`,
5096
+ children,
5097
+ fill = false,
5075
5098
  className,
5076
5099
  ...props
5077
5100
  }) {
@@ -5080,11 +5103,12 @@ function CircularProgress({
5080
5103
  const circumference = 2 * Math.PI * radius;
5081
5104
  const strokeDashoffset = circumference - percentage / 100 * circumference;
5082
5105
  const center = size / 2;
5106
+ const boxStyle = fill ? { width: "100%", aspectRatio: 1 } : { width: size, height: size };
5083
5107
  return /* @__PURE__ */ jsxs(
5084
5108
  reactNative.View,
5085
5109
  {
5086
5110
  className: cn("items-center justify-center", className),
5087
- style: { width: size, height: size },
5111
+ style: boxStyle,
5088
5112
  accessibilityRole: "progressbar",
5089
5113
  accessibilityValue: {
5090
5114
  min: 0,
@@ -5093,52 +5117,56 @@ function CircularProgress({
5093
5117
  },
5094
5118
  ...props,
5095
5119
  children: [
5096
- /* @__PURE__ */ jsx(
5120
+ /* @__PURE__ */ jsx(reactNative.View, { className: cn(isIndeterminate && "animate-spin"), style: boxStyle, children: /* @__PURE__ */ jsxs(
5121
+ "svg",
5122
+ {
5123
+ width: "100%",
5124
+ height: "100%",
5125
+ viewBox: `0 0 ${size} ${size}`,
5126
+ style: { position: "absolute" },
5127
+ children: [
5128
+ /* @__PURE__ */ jsx(
5129
+ "circle",
5130
+ {
5131
+ cx: center,
5132
+ cy: center,
5133
+ r: radius,
5134
+ fill: "none",
5135
+ stroke: "var(--color-border-default)",
5136
+ strokeWidth
5137
+ }
5138
+ ),
5139
+ /* @__PURE__ */ jsx(
5140
+ "circle",
5141
+ {
5142
+ cx: center,
5143
+ cy: center,
5144
+ r: radius,
5145
+ fill: "none",
5146
+ stroke: colorVarMap[color],
5147
+ strokeWidth,
5148
+ strokeLinecap: "round",
5149
+ strokeDasharray: circumference,
5150
+ strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
5151
+ transform: `rotate(-90 ${center} ${center})`,
5152
+ style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
5153
+ }
5154
+ )
5155
+ ]
5156
+ }
5157
+ ) }),
5158
+ children != null ? /* @__PURE__ */ jsx(
5097
5159
  reactNative.View,
5098
5160
  {
5099
- className: cn(isIndeterminate && "animate-spin"),
5100
- style: { width: size, height: size },
5101
- children: /* @__PURE__ */ jsxs(
5102
- "svg",
5103
- {
5104
- width: size,
5105
- height: size,
5106
- viewBox: `0 0 ${size} ${size}`,
5107
- style: { position: "absolute" },
5108
- children: [
5109
- /* @__PURE__ */ jsx(
5110
- "circle",
5111
- {
5112
- cx: center,
5113
- cy: center,
5114
- r: radius,
5115
- fill: "none",
5116
- stroke: "var(--color-border-default)",
5117
- strokeWidth
5118
- }
5119
- ),
5120
- /* @__PURE__ */ jsx(
5121
- "circle",
5122
- {
5123
- cx: center,
5124
- cy: center,
5125
- r: radius,
5126
- fill: "none",
5127
- stroke: colorVarMap[color],
5128
- strokeWidth,
5129
- strokeLinecap: "round",
5130
- strokeDasharray: circumference,
5131
- strokeDashoffset: isIndeterminate ? circumference * 0.75 : strokeDashoffset,
5132
- transform: `rotate(-90 ${center} ${center})`,
5133
- style: { transition: "stroke-dashoffset 300ms cubic-bezier(0.22, 1, 0.36, 1)" }
5134
- }
5135
- )
5136
- ]
5137
- }
5138
- )
5161
+ style: {
5162
+ position: "absolute",
5163
+ alignItems: "center",
5164
+ justifyContent: "center",
5165
+ pointerEvents: "none"
5166
+ },
5167
+ children
5139
5168
  }
5140
- ),
5141
- showValue && !isIndeterminate && /* @__PURE__ */ jsx(
5169
+ ) : showValue && !isIndeterminate && /* @__PURE__ */ jsx(
5142
5170
  reactNative.Text,
5143
5171
  {
5144
5172
  className: "text-xs font-semibold text-text-primary absolute",
@@ -5234,7 +5262,7 @@ function ToastProvider({
5234
5262
  return id;
5235
5263
  }, [defaultDuration, maxToasts]);
5236
5264
  const removeToast = React24.useCallback((id) => {
5237
- setToasts((prev) => prev.filter((t18) => t18.id !== id));
5265
+ setToasts((prev) => prev.filter((t17) => t17.id !== id));
5238
5266
  }, []);
5239
5267
  const removeAllToasts = React24.useCallback(() => {
5240
5268
  setToasts([]);
@@ -7079,6 +7107,70 @@ function TimerReadout({
7079
7107
  }
7080
7108
  );
7081
7109
  }
7110
+ function CircularTimer({
7111
+ durationMs,
7112
+ elapsedMs,
7113
+ mode = "down",
7114
+ size = 160,
7115
+ strokeWidth = 8,
7116
+ fill = false,
7117
+ color = "primary",
7118
+ doneLabel,
7119
+ doneColor = "success",
7120
+ controls,
7121
+ accessibilityLabel: accessibilityLabel2,
7122
+ testID
7123
+ }) {
7124
+ const { remainingMs, progress, label, done } = useTimer({ mode, durationMs, elapsedMs });
7125
+ const isDone = done && mode === "down";
7126
+ const showDone = isDone && doneLabel != null;
7127
+ const remainingFrac = durationMs > 0 ? Math.max(0, Math.min(1, remainingMs / durationMs)) : 0;
7128
+ const arc = mode === "down" ? showDone ? 1 : remainingFrac : progress;
7129
+ const ringColor = showDone ? doneColor : color;
7130
+ const remainingSec = Math.ceil(Math.max(0, remainingMs) / 1e3);
7131
+ const a11y = accessibilityLabel2 ?? (isDone ? "Timer complete" : `${remainingSec} seconds remaining`);
7132
+ return /* @__PURE__ */ jsxs(
7133
+ reactNative.View,
7134
+ {
7135
+ style: { alignItems: "center", gap: 16 },
7136
+ accessibilityRole: "timer",
7137
+ accessibilityLabel: a11y,
7138
+ testID: testID ?? "circular-timer",
7139
+ children: [
7140
+ /* @__PURE__ */ jsx(
7141
+ CircularProgress,
7142
+ {
7143
+ value: arc,
7144
+ max: 1,
7145
+ size,
7146
+ strokeWidth,
7147
+ fill,
7148
+ color: ringColor,
7149
+ accessibilityLabel: "Timer ring",
7150
+ testID: "circular-timer-ring",
7151
+ children: /* @__PURE__ */ jsx(
7152
+ reactNative.Text,
7153
+ {
7154
+ className: showDone ? void 0 : "text-text-primary",
7155
+ style: {
7156
+ fontSize: Math.round(size * (showDone ? 0.28 : 0.24)),
7157
+ fontFamily: '"Space Grotesk", sans-serif',
7158
+ fontWeight: "800",
7159
+ fontVariant: ["tabular-nums"],
7160
+ letterSpacing: showDone ? 1 : -1,
7161
+ ...showDone ? { color: colorVarMap[doneColor] } : null
7162
+ },
7163
+ testID: "circular-timer-label",
7164
+ children: showDone ? doneLabel : label
7165
+ }
7166
+ )
7167
+ }
7168
+ ),
7169
+ controls
7170
+ ]
7171
+ }
7172
+ );
7173
+ }
7082
7174
  var sizeConfig2 = {
7083
7175
  sm: { value: "text-lg font-bold", label: "text-xs", unit: "text-xs" },
7084
7176
  md: { value: "text-2xl font-bold", label: "text-xs", unit: "text-sm" },
@@ -7724,137 +7816,6 @@ function getLuminance(color) {
7724
7816
  function getContrastText(backgroundColor) {
7725
7817
  return getLuminance(backgroundColor) === "light" ? "#000000" : "#FFFFFF";
7726
7818
  }
7727
- var t2 = getSemanticColors("dark");
7728
- var TEMPO_PACING = {
7729
- behindThresholdPct: 0.15,
7730
- minPhaseDurationMs: 500
7731
- };
7732
- function getTempoPacingState(elapsedMs, targetMs) {
7733
- if (!targetMs || targetMs < TEMPO_PACING.minPhaseDurationMs) return "none";
7734
- const ratio = elapsedMs / targetMs;
7735
- if (ratio > 1 + TEMPO_PACING.behindThresholdPct) return "behind";
7736
- return "on-pace";
7737
- }
7738
- function getTempoFillPct(elapsedMs, targetMs) {
7739
- if (!targetMs) return 100;
7740
- return Math.min(100, elapsedMs / targetMs * 100);
7741
- }
7742
- var PHASE_ORDER = ["concentric", "hold", "eccentric"];
7743
- var PHASE_CONFIG = {
7744
- concentric: { label: "Con", color: t2["status-success"], flex: 2 },
7745
- hold: { label: "Hold", color: t2["brand-primary"], flex: 1 },
7746
- eccentric: { label: "Ecc", color: t2["status-warning"], flex: 3 }
7747
- };
7748
- function TempoBar({
7749
- activePhase,
7750
- phaseElapsedMs,
7751
- completed,
7752
- target,
7753
- className,
7754
- ...props
7755
- }) {
7756
- return /* @__PURE__ */ jsxs(reactNative.View, { className, testID: "tempo-bar", ...props, children: [
7757
- /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", marginBottom: 4 }, children: PHASE_ORDER.map((phase) => {
7758
- const config = PHASE_CONFIG[phase];
7759
- 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);
7760
- }) }),
7761
- /* @__PURE__ */ jsx(reactNative.View, { style: { flexDirection: "row", gap: 2, height: 20 }, children: PHASE_ORDER.map((phase) => {
7762
- const config = PHASE_CONFIG[phase];
7763
- const isActive = activePhase === phase;
7764
- const completedMs = completed?.[phase];
7765
- const targetSec = target?.[phase];
7766
- const targetMs = targetSec != null ? targetSec * 1e3 : null;
7767
- return /* @__PURE__ */ jsx(
7768
- reactNative.View,
7769
- {
7770
- style: {
7771
- flex: config.flex,
7772
- backgroundColor: alpha("#ffffff", 0.06),
7773
- borderRadius: 4,
7774
- overflow: "hidden"
7775
- },
7776
- children: isActive ? /* @__PURE__ */ jsx(
7777
- ActiveSegment,
7778
- {
7779
- config,
7780
- phaseElapsedMs,
7781
- targetMs
7782
- }
7783
- ) : completedMs != null ? /* @__PURE__ */ jsx(CompletedSegment, { config, completedMs, targetMs }) : null
7784
- },
7785
- phase
7786
- );
7787
- }) })
7788
- ] });
7789
- }
7790
- function ActiveSegment({
7791
- config,
7792
- phaseElapsedMs,
7793
- targetMs
7794
- }) {
7795
- const pacing = getTempoPacingState(phaseElapsedMs, targetMs);
7796
- const barColor = pacing === "behind" ? t2["status-error"] : config.color;
7797
- const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
7798
- const label = targetMs ? `${formatDuration2(phaseElapsedMs)} / ${formatDuration2(targetMs)}` : formatDuration2(phaseElapsedMs);
7799
- return /* @__PURE__ */ jsxs(reactNative.View, { style: { height: "100%", position: "relative" }, testID: `tempo-segment-active-${config.label}`, children: [
7800
- /* @__PURE__ */ jsx(
7801
- reactNative.View,
7802
- {
7803
- style: {
7804
- position: "absolute",
7805
- left: 0,
7806
- top: 0,
7807
- bottom: 0,
7808
- width: `${fillPct}%`,
7809
- backgroundColor: alpha(barColor, 0.3),
7810
- borderRadius: 4
7811
- }
7812
- }
7813
- ),
7814
- /* @__PURE__ */ jsx(
7815
- reactNative.View,
7816
- {
7817
- style: {
7818
- height: "100%",
7819
- flexDirection: "row",
7820
- alignItems: "center",
7821
- justifyContent: "center"
7822
- },
7823
- children: /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, fontWeight: "700", color: barColor }, children: label })
7824
- }
7825
- )
7826
- ] });
7827
- }
7828
- function CompletedSegment({
7829
- config,
7830
- completedMs,
7831
- targetMs
7832
- }) {
7833
- const pacing = getTempoPacingState(completedMs, targetMs);
7834
- const hitTarget = pacing !== "behind";
7835
- const indicator = targetMs && targetMs >= TEMPO_PACING.minPhaseDurationMs ? hitTarget ? " \u2713" : " \u2717" : "";
7836
- return /* @__PURE__ */ jsx(
7837
- reactNative.View,
7838
- {
7839
- style: {
7840
- height: "100%",
7841
- flexDirection: "row",
7842
- alignItems: "center",
7843
- justifyContent: "center",
7844
- backgroundColor: alpha(config.color, 0.15)
7845
- },
7846
- testID: `tempo-segment-completed-${config.label}`,
7847
- children: /* @__PURE__ */ jsxs(reactNative.Text, { style: { fontSize: 12, fontWeight: "500", color: alpha(config.color, 0.7) }, children: [
7848
- formatDuration2(completedMs),
7849
- indicator
7850
- ] })
7851
- }
7852
- );
7853
- }
7854
- function formatDuration2(ms) {
7855
- const s = ms / 1e3;
7856
- return s < 10 ? s.toFixed(1) : Math.round(s).toString();
7857
- }
7858
7819
  var METRIC_FONT = "Inter, sans-serif";
7859
7820
  function MetricCell({ children, color, fontSize = 11 }) {
7860
7821
  return /* @__PURE__ */ jsx(
@@ -7873,14 +7834,32 @@ function MetricCell({ children, color, fontSize = 11 }) {
7873
7834
  }
7874
7835
 
7875
7836
  // src/components/custom/Workout/TempoDisplay.tsx
7876
- var t3 = getSemanticColors("dark");
7837
+ var t2 = getSemanticColors("dark");
7877
7838
  var INTER = "Inter, sans-serif";
7878
7839
  var TEXT_TERTIARY = "#6B7280";
7879
- var STATUS_ERROR = t3["status-error"];
7840
+ var STATUS_ERROR = t2["status-error"];
7841
+ var STATUS_SUCCESS = t2["status-success"];
7842
+ var STATUS_WARNING = t2["status-warning"];
7843
+ var ON_TARGET_MS = 100;
7844
+ function getTempoFillPct(elapsedMs, targetMs) {
7845
+ if (!targetMs) return 100;
7846
+ return Math.min(100, elapsedMs / targetMs * 100);
7847
+ }
7848
+ function activeNumberTone(elapsedMs, targetMs) {
7849
+ if (targetMs == null) return t2["text-primary"];
7850
+ const remainingMs = targetMs - elapsedMs;
7851
+ if (remainingMs > ON_TARGET_MS) return STATUS_WARNING;
7852
+ if (remainingMs >= -ON_TARGET_MS) return STATUS_SUCCESS;
7853
+ return STATUS_ERROR;
7854
+ }
7855
+ function liveReadoutText(elapsedMs, targetMs, readout) {
7856
+ const seconds = readout === "countup" ? elapsedMs / 1e3 : (targetMs - elapsedMs) / 1e3;
7857
+ return seconds.toFixed(1);
7858
+ }
7880
7859
  var phaseColors = {
7881
- eccentric: t3["status-warning"],
7860
+ eccentric: primitiveRamps.magenta[400],
7882
7861
  pauseBottom: TEXT_TERTIARY,
7883
- concentric: t3["status-success"],
7862
+ concentric: primitiveRamps.cyan[300],
7884
7863
  pauseTop: TEXT_TERTIARY,
7885
7864
  dash: TEXT_TERTIARY
7886
7865
  };
@@ -7900,63 +7879,98 @@ var LIVE_PHASES = [
7900
7879
  { key: "concentric", color: phaseColors.concentric },
7901
7880
  { key: "pauseTop", color: phaseColors.pauseTop }
7902
7881
  ];
7882
+ function CellFill({ color, pct: pct2 }) {
7883
+ return /* @__PURE__ */ jsx(
7884
+ reactNative.View,
7885
+ {
7886
+ style: {
7887
+ position: "absolute",
7888
+ left: 2,
7889
+ right: 2,
7890
+ bottom: 0,
7891
+ height: `${pct2}%`,
7892
+ backgroundColor: alpha(color, 0.28),
7893
+ borderRadius: 3
7894
+ }
7895
+ }
7896
+ );
7897
+ }
7898
+ var MONO = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
7899
+ var READOUT_MAX_CHARS = 5;
7903
7900
  function LiveTempoCell({
7904
7901
  value,
7905
7902
  color,
7906
- isActive,
7903
+ status,
7907
7904
  phaseElapsedMs,
7908
- fontSize
7905
+ completedMs,
7906
+ fontSize,
7907
+ readout
7909
7908
  }) {
7910
- if (!isActive) {
7911
- return /* @__PURE__ */ jsx(TempoValue, { value, color: alpha(color, 0.35), fontSize });
7912
- }
7913
7909
  const targetMs = value > 0 ? value * 1e3 : null;
7914
- const barColor = getTempoPacingState(phaseElapsedMs, targetMs) === "behind" ? STATUS_ERROR : color;
7910
+ const cellW = Math.ceil(fontSize * 0.62 * READOUT_MAX_CHARS);
7911
+ const wrap = {
7912
+ position: "relative",
7913
+ width: cellW,
7914
+ minHeight: Math.round(fontSize * 1.25),
7915
+ alignItems: "center",
7916
+ justifyContent: "center"
7917
+ };
7918
+ const num = (c, text) => /* @__PURE__ */ jsx(reactNative.Text, { style: { fontFamily: MONO, fontSize, color: c, fontWeight: "700" }, children: text });
7919
+ if (status === "upcoming") {
7920
+ const targetText = targetMs != null ? (targetMs / 1e3).toFixed(1) : String(value);
7921
+ return /* @__PURE__ */ jsx(reactNative.View, { style: wrap, children: num(color, targetText) });
7922
+ }
7923
+ if (status === "done") {
7924
+ const finalMs = completedMs ?? targetMs ?? 0;
7925
+ const finalText = targetMs != null ? liveReadoutText(finalMs, targetMs, readout) : String(value);
7926
+ const finalTone = targetMs != null ? activeNumberTone(finalMs, targetMs) : color;
7927
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: wrap, testID: "tempo-live-done", children: [
7928
+ /* @__PURE__ */ jsx(CellFill, { color, pct: 100 }),
7929
+ num(finalTone, finalText)
7930
+ ] });
7931
+ }
7932
+ const numberTone = activeNumberTone(phaseElapsedMs, targetMs);
7915
7933
  const fillPct = getTempoFillPct(phaseElapsedMs, targetMs);
7916
- return /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "relative" }, testID: "tempo-live-active", children: [
7917
- /* @__PURE__ */ jsx(
7918
- reactNative.View,
7919
- {
7920
- style: {
7921
- position: "absolute",
7922
- left: 0,
7923
- top: 0,
7924
- bottom: 0,
7925
- width: `${fillPct}%`,
7926
- backgroundColor: alpha(barColor, 0.3),
7927
- borderRadius: 2
7928
- }
7929
- }
7930
- ),
7931
- /* @__PURE__ */ jsx(TempoValue, { value, color: barColor, fontSize })
7934
+ const activeText = targetMs != null ? liveReadoutText(phaseElapsedMs, targetMs, readout) : String(value);
7935
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: wrap, testID: "tempo-live-active", children: [
7936
+ /* @__PURE__ */ jsx(CellFill, { color, pct: fillPct }),
7937
+ num(numberTone, activeText)
7932
7938
  ] });
7933
7939
  }
7934
7940
  function LiveTempoRow({
7935
7941
  values,
7936
7942
  live,
7937
- fontSize
7938
- }) {
7939
- return /* @__PURE__ */ jsx(jsxRuntime.Fragment, { children: LIVE_PHASES.map((phase, i) => /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row" }, children: [
7940
- i > 0 && /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
7941
- /* @__PURE__ */ jsx(
7942
- LiveTempoCell,
7943
- {
7944
- value: values[i],
7945
- color: phase.color,
7946
- isActive: live.activePhase === phase.key,
7947
- phaseElapsedMs: live.phaseElapsedMs,
7948
- fontSize
7949
- }
7950
- )
7951
- ] }, phase.key)) });
7943
+ fontSize,
7944
+ readout
7945
+ }) {
7946
+ const activeIndex = live.activePhase ? LIVE_PHASES.findIndex((p) => p.key === live.activePhase) : -1;
7947
+ return /* @__PURE__ */ jsx(jsxRuntime.Fragment, { children: LIVE_PHASES.map((phase, i) => {
7948
+ const status = activeIndex < 0 || i > activeIndex ? "upcoming" : i < activeIndex ? "done" : "active";
7949
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
7950
+ i > 0 && /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
7951
+ /* @__PURE__ */ jsx(
7952
+ LiveTempoCell,
7953
+ {
7954
+ value: values[i],
7955
+ color: phase.color,
7956
+ status,
7957
+ phaseElapsedMs: live.phaseElapsedMs,
7958
+ completedMs: live.completed?.[phase.key],
7959
+ fontSize,
7960
+ readout
7961
+ }
7962
+ )
7963
+ ] }, phase.key);
7964
+ }) });
7952
7965
  }
7953
7966
  function TempoDisplay({
7954
7967
  tempo,
7955
7968
  size = "md",
7956
- colored = true,
7969
+ fontSize: fontSizeProp,
7957
7970
  showLabel = true,
7958
7971
  showInfo = true,
7959
7972
  live,
7973
+ liveReadout = "countdown",
7960
7974
  onPress,
7961
7975
  className,
7962
7976
  ...props
@@ -7964,8 +7978,11 @@ function TempoDisplay({
7964
7978
  const [showTooltip, setShowTooltip] = React24.useState(false);
7965
7979
  const [eccentric, pauseBottom, concentric, pauseTop] = roundTempo(tempo);
7966
7980
  const isSm = size === "sm";
7967
- const fontSize = isSm ? 9 : 11;
7968
- const monoColor = TEXT_TERTIARY;
7981
+ const fontSize = fontSizeProp ?? (isSm ? 9 : 11);
7982
+ const chromePadX = Math.round(fontSize * 0.6);
7983
+ const chromePadY = Math.round(fontSize * 0.3);
7984
+ const chromeRadius = Math.round(fontSize * 0.4);
7985
+ const labelFont = Math.max(9, Math.round(fontSize * 0.5));
7969
7986
  const handlePress = React24.useCallback(() => {
7970
7987
  if (onPress) {
7971
7988
  onPress();
@@ -7982,9 +7999,9 @@ function TempoDisplay({
7982
7999
  flexDirection: "row",
7983
8000
  alignItems: "center",
7984
8001
  backgroundColor: "#1C1C1C",
7985
- paddingHorizontal: isSm ? 6 : 8,
7986
- paddingVertical: 3,
7987
- borderRadius: 4
8002
+ paddingHorizontal: chromePadX,
8003
+ paddingVertical: chromePadY,
8004
+ borderRadius: chromeRadius
7988
8005
  },
7989
8006
  ...props,
7990
8007
  children: [
@@ -7993,12 +8010,13 @@ function TempoDisplay({
7993
8010
  {
7994
8011
  style: {
7995
8012
  fontFamily: INTER,
7996
- fontSize: 9,
8013
+ fontSize: labelFont,
7997
8014
  fontWeight: "500",
7998
- color: TEXT_TERTIARY,
8015
+ // Live-muted green while a rep is running, tertiary at rest.
8016
+ color: live ? t2["status-live-muted"] : TEXT_TERTIARY,
7999
8017
  letterSpacing: 0.5,
8000
8018
  textTransform: "uppercase",
8001
- marginRight: 6
8019
+ marginRight: Math.round(fontSize * 0.5)
8002
8020
  },
8003
8021
  children: "TEMPO"
8004
8022
  }
@@ -8008,36 +8026,20 @@ function TempoDisplay({
8008
8026
  {
8009
8027
  values: [eccentric, pauseBottom, concentric, pauseTop],
8010
8028
  live,
8011
- fontSize
8012
- }
8013
- ) : colored ? /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
8014
- /* @__PURE__ */ jsx(TempoValue, { value: eccentric, color: phaseColors.eccentric, fontSize }),
8015
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8016
- /* @__PURE__ */ jsx(TempoValue, { value: pauseBottom, color: phaseColors.pauseBottom, fontSize }),
8017
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8018
- /* @__PURE__ */ jsx(TempoValue, { value: concentric, color: phaseColors.concentric, fontSize }),
8019
- /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8020
- /* @__PURE__ */ jsx(TempoValue, { value: pauseTop, color: phaseColors.pauseTop, fontSize })
8021
- ] }) : /* @__PURE__ */ jsxs(
8022
- reactNative.Text,
8023
- {
8024
- style: {
8025
- fontFamily: INTER,
8026
- fontSize,
8027
- color: monoColor,
8028
- fontWeight: "600",
8029
- letterSpacing: 1
8030
- },
8031
- children: [
8032
- eccentric,
8033
- "-",
8034
- pauseBottom,
8035
- "-",
8036
- concentric,
8037
- "-",
8038
- pauseTop
8039
- ]
8029
+ fontSize,
8030
+ readout: liveReadout
8040
8031
  }
8032
+ ) : (
8033
+ // Static prescription: the phase-coloured lockup (the same colours the live row rests at).
8034
+ /* @__PURE__ */ jsxs(jsxRuntime.Fragment, { children: [
8035
+ /* @__PURE__ */ jsx(TempoValue, { value: eccentric, color: phaseColors.eccentric, fontSize }),
8036
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8037
+ /* @__PURE__ */ jsx(TempoValue, { value: pauseBottom, color: phaseColors.pauseBottom, fontSize }),
8038
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8039
+ /* @__PURE__ */ jsx(TempoValue, { value: concentric, color: phaseColors.concentric, fontSize }),
8040
+ /* @__PURE__ */ jsx(TempoSeparator, { color: phaseColors.dash, fontSize }),
8041
+ /* @__PURE__ */ jsx(TempoValue, { value: pauseTop, color: phaseColors.pauseTop, fontSize })
8042
+ ] })
8041
8043
  ) })
8042
8044
  ]
8043
8045
  }
@@ -8122,13 +8124,13 @@ function TempoDisplay({
8122
8124
  }
8123
8125
  );
8124
8126
  }
8125
- var t4 = getSemanticColors("dark");
8127
+ var t3 = getSemanticColors("dark");
8126
8128
  function getDotColor(deviation) {
8127
8129
  const abs = Math.abs(deviation);
8128
- if (deviation < -0.3) return t4["status-success"];
8130
+ if (deviation < -0.3) return t3["status-success"];
8129
8131
  if (abs <= 0.3) return "#6B7280";
8130
- if (abs <= 0.7) return t4["status-warning"];
8131
- return t4["status-error"];
8132
+ if (abs <= 0.7) return t3["status-warning"];
8133
+ return t3["status-error"];
8132
8134
  }
8133
8135
  function getDeviationDescription(deviation) {
8134
8136
  if (deviation < -0.3) return "lighter than planned";
@@ -8196,19 +8198,19 @@ function DeviationBar({
8196
8198
  }
8197
8199
  );
8198
8200
  }
8199
- var t5 = getSemanticColors("dark");
8201
+ var t4 = getSemanticColors("dark");
8200
8202
  var TRACK_BG = "#333333";
8201
8203
  var LABEL_COLOR = "#6B7280";
8202
8204
  var TARGET_LINE_COLOR = "rgba(33, 150, 243, 0.5)";
8203
8205
  var AT_TARGET_GLOW = "0 0 5px 1px rgba(33, 150, 243, 0.35), 0 0 10px 3px rgba(33, 150, 243, 0.15)";
8204
8206
  var ZONE = {
8205
- building: t5["status-success"],
8207
+ building: t4["status-success"],
8206
8208
  // below target ramp-up
8207
- approaching: t5["status-warning"],
8209
+ approaching: t4["status-warning"],
8208
8210
  // nearing target
8209
- target: t5["brand-primary"],
8211
+ target: t4["brand-primary"],
8210
8212
  // exactly at target
8211
- over1: t5["status-error"],
8213
+ over1: t4["status-error"],
8212
8214
  // mild over-target
8213
8215
  over2: WORKOUT_TOKENS.intensity.over2,
8214
8216
  // moderate over-target
@@ -8377,7 +8379,7 @@ var MESO_ACCENT_GRADIENT_LIGHT = primitiveRamps.orange[300];
8377
8379
  var WORKOUT_PILL_DELOAD = primitiveRamps.magenta[600];
8378
8380
 
8379
8381
  // src/components/custom/Workout/WorkoutPill.tsx
8380
- var t6 = getSemanticColors("dark");
8382
+ var t5 = getSemanticColors("dark");
8381
8383
  var statusBgColors = {
8382
8384
  completed: "rgba(46,213,115,0.15)",
8383
8385
  current: "rgba(255,121,0,0.15)",
@@ -8395,9 +8397,9 @@ var statusBorderStyles = {
8395
8397
  deload: { borderWidth: 1, borderColor: "rgba(186,41,150,0.3)" }
8396
8398
  };
8397
8399
  var textColors = {
8398
- completed: t6["status-success"],
8399
- current: t6["brand-primary"],
8400
- next: t6["brand-primary"],
8400
+ completed: t5["status-success"],
8401
+ current: t5["brand-primary"],
8402
+ next: t5["brand-primary"],
8401
8403
  upcoming: "#6B7280",
8402
8404
  missed: "rgba(209,67,67,0.7)",
8403
8405
  deload: WORKOUT_PILL_DELOAD
@@ -8937,8 +8939,18 @@ var EXPANDED_ENCODED_PCT = 45;
8937
8939
  var EXPANDED_HEIGHT = 60;
8938
8940
  var FIXED_MAX_VELOCITY = 1.15;
8939
8941
  var BARE_STUB_HEIGHT = 3;
8942
+ var HERO_HEIGHT = 220;
8943
+ var HERO_LABEL_HEADROOM = 20;
8944
+ var HERO_BAR_GAP = 8;
8945
+ var HERO_BAR_MAX_WIDTH = 120;
8946
+ var HERO_LABEL_MIN_BAR_WIDTH = 30;
8947
+ var HERO_BAR_RADIUS = 5;
8948
+ var HERO_MIN_BAR_HEIGHT = 4;
8949
+ var HERO_PENDING_COLOR = primitiveColors.charcoal[100];
8950
+ var HERO_REFERENCE_COLOR = primitiveColors.charcoal[0];
8951
+ var PEAK_HEADROOM = 1.06;
8940
8952
  function scaleDenominator(scale, maxVelocity) {
8941
- return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * 1.15;
8953
+ return scale === "fixed" ? FIXED_MAX_VELOCITY : maxVelocity * PEAK_HEADROOM;
8942
8954
  }
8943
8955
  function bareSlotHeight(slot, height, denom) {
8944
8956
  if (slot.kind !== "rep" || denom <= 0) return BARE_STUB_HEIGHT;
@@ -8964,6 +8976,195 @@ function usePrefersReducedMotion() {
8964
8976
  var ANIMATION_DURATION = 400;
8965
8977
  var ANIMATION_EASING = reactNative.Easing.bezier(0.22, 1, 0.36, 1);
8966
8978
  var POP_EASING = reactNative.Easing.bezier(0.34, 1.56, 0.64, 1);
8979
+ function useLiveRepPop(active, liveRepIndex, liveVelocity, isNewPeak) {
8980
+ const prefersReducedMotion = usePrefersReducedMotion();
8981
+ const [liveScale] = React24.useState(() => new reactNative.Animated.Value(1));
8982
+ React24.useEffect(() => {
8983
+ if (!active || liveRepIndex == null || liveVelocity == null) return;
8984
+ if (prefersReducedMotion) {
8985
+ liveScale.setValue(1);
8986
+ return;
8987
+ }
8988
+ if (isNewPeak) {
8989
+ liveScale.setValue(1);
8990
+ reactNative.Animated.sequence([
8991
+ reactNative.Animated.timing(liveScale, {
8992
+ toValue: 1.25,
8993
+ duration: 150,
8994
+ easing: reactNative.Easing.out(reactNative.Easing.quad),
8995
+ useNativeDriver: true
8996
+ }),
8997
+ reactNative.Animated.spring(liveScale, {
8998
+ toValue: 1,
8999
+ friction: 3,
9000
+ tension: 140,
9001
+ useNativeDriver: true
9002
+ })
9003
+ ]).start();
9004
+ } else {
9005
+ liveScale.setValue(0.8);
9006
+ reactNative.Animated.timing(liveScale, {
9007
+ toValue: 1,
9008
+ duration: 300,
9009
+ easing: POP_EASING,
9010
+ useNativeDriver: true
9011
+ }).start();
9012
+ }
9013
+ }, [active, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
9014
+ return liveScale;
9015
+ }
9016
+ function HeroVelocityChart({
9017
+ doneVelocities,
9018
+ barColorFor,
9019
+ scaleDenom,
9020
+ referenceVelocity,
9021
+ liveRepIndex,
9022
+ isNewPeak,
9023
+ targetReps,
9024
+ height,
9025
+ className,
9026
+ viewProps
9027
+ }) {
9028
+ const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
9029
+ const liveScale = useLiveRepPop(true, liveRepIndex, liveVelocity, isNewPeak);
9030
+ const [plotW, setPlotW] = React24.useState(0);
9031
+ const onPlotLayout = (e) => setPlotW(e.nativeEvent.layout.width);
9032
+ const plotHeight = Math.max(0, height - HERO_LABEL_HEADROOM);
9033
+ const barHeight = (velocity) => scaleDenom > 0 ? Math.max(HERO_MIN_BAR_HEIGHT, Math.min(1, velocity / scaleDenom) * plotHeight) : HERO_MIN_BAR_HEIGHT;
9034
+ const pendingCount = Math.max(0, (targetReps ?? 0) - doneVelocities.length);
9035
+ const referencePx = referenceVelocity > 0 && scaleDenom > 0 ? Math.min(plotHeight, referenceVelocity / scaleDenom * plotHeight) : 0;
9036
+ const totalSlots = doneVelocities.length + pendingCount;
9037
+ const barWidth = plotW > 0 && totalSlots > 0 ? Math.min(HERO_BAR_MAX_WIDTH, (plotW - HERO_BAR_GAP * (totalSlots - 1)) / totalSlots) : HERO_BAR_MAX_WIDTH;
9038
+ const labelsCrowded = plotW > 0 && barWidth < HERO_LABEL_MIN_BAR_WIDTH;
9039
+ const heroGap = plotW === 0 ? HERO_BAR_GAP : barWidth < 16 ? 2 : barWidth < 28 ? 4 : HERO_BAR_GAP;
9040
+ const peakIndex = doneVelocities.reduce(
9041
+ (best, v, i) => v > doneVelocities[best] ? i : best,
9042
+ 0
9043
+ );
9044
+ const showBarLabel = (i) => !labelsCrowded || i === peakIndex || i === liveRepIndex;
9045
+ const repCount = doneVelocities.length;
9046
+ const total = Math.max(repCount, targetReps ?? repCount);
9047
+ const label = referenceVelocity > 0 ? `Velocity chart, ${repCount} of ${total} reps, best ${formatVelocity(referenceVelocity)} meters per second` : `Velocity chart, ${repCount} of ${total} reps`;
9048
+ const { style: externalStyle, ...restProps } = viewProps;
9049
+ return /* @__PURE__ */ jsx(
9050
+ reactNative.View,
9051
+ {
9052
+ className,
9053
+ style: [{ height }, externalStyle],
9054
+ accessibilityRole: "image",
9055
+ accessibilityLabel: label,
9056
+ testID: "velocity-strip-hero",
9057
+ ...restProps,
9058
+ children: /* @__PURE__ */ jsxs(
9059
+ reactNative.View,
9060
+ {
9061
+ onLayout: onPlotLayout,
9062
+ style: {
9063
+ flex: 1,
9064
+ flexDirection: "row",
9065
+ alignItems: "flex-end",
9066
+ gap: heroGap,
9067
+ position: "relative",
9068
+ borderBottomWidth: 2,
9069
+ borderBottomColor: HERO_PENDING_COLOR
9070
+ },
9071
+ children: [
9072
+ referencePx > 0 && /* @__PURE__ */ jsx(
9073
+ reactNative.View,
9074
+ {
9075
+ accessibilityElementsHidden: true,
9076
+ style: {
9077
+ position: "absolute",
9078
+ left: 0,
9079
+ right: 0,
9080
+ bottom: referencePx,
9081
+ borderTopWidth: 1,
9082
+ borderStyle: "dashed",
9083
+ borderColor: HERO_REFERENCE_COLOR,
9084
+ pointerEvents: "none"
9085
+ },
9086
+ testID: "velocity-hero-reference"
9087
+ }
9088
+ ),
9089
+ doneVelocities.map((velocity, i) => {
9090
+ const isLive = liveRepIndex === i;
9091
+ return /* @__PURE__ */ jsxs(
9092
+ reactNative.View,
9093
+ {
9094
+ accessibilityElementsHidden: true,
9095
+ style: {
9096
+ flex: 1,
9097
+ maxWidth: HERO_BAR_MAX_WIDTH,
9098
+ height: "100%",
9099
+ alignItems: "center",
9100
+ justifyContent: "flex-end"
9101
+ },
9102
+ children: [
9103
+ showBarLabel(i) && /* @__PURE__ */ jsx(
9104
+ reactNative.Text,
9105
+ {
9106
+ className: "text-text-primary",
9107
+ style: { fontSize: 12, fontWeight: "800", marginBottom: 4 },
9108
+ testID: `velocity-label-${i}`,
9109
+ children: formatVelocity(velocity)
9110
+ }
9111
+ ),
9112
+ /* @__PURE__ */ jsx(
9113
+ reactNative.Animated.View,
9114
+ {
9115
+ style: [
9116
+ {
9117
+ width: "100%",
9118
+ height: barHeight(velocity),
9119
+ borderTopLeftRadius: HERO_BAR_RADIUS,
9120
+ borderTopRightRadius: HERO_BAR_RADIUS,
9121
+ backgroundColor: barColorFor(velocity)
9122
+ },
9123
+ isLive ? { transform: [{ scale: liveScale }] } : null
9124
+ ],
9125
+ testID: `velocity-bar-${i}`
9126
+ }
9127
+ )
9128
+ ]
9129
+ },
9130
+ i
9131
+ );
9132
+ }),
9133
+ Array.from({ length: pendingCount }, (_, i) => /* @__PURE__ */ jsx(
9134
+ reactNative.View,
9135
+ {
9136
+ accessibilityElementsHidden: true,
9137
+ style: {
9138
+ flex: 1,
9139
+ maxWidth: HERO_BAR_MAX_WIDTH,
9140
+ height: "100%",
9141
+ alignItems: "center",
9142
+ justifyContent: "flex-end"
9143
+ },
9144
+ children: /* @__PURE__ */ jsx(
9145
+ reactNative.View,
9146
+ {
9147
+ style: {
9148
+ width: "100%",
9149
+ height: HERO_MIN_BAR_HEIGHT * 3,
9150
+ borderWidth: 1,
9151
+ borderStyle: "dashed",
9152
+ borderColor: HERO_PENDING_COLOR,
9153
+ borderTopLeftRadius: HERO_BAR_RADIUS,
9154
+ borderTopRightRadius: HERO_BAR_RADIUS
9155
+ },
9156
+ testID: "velocity-slot-todo"
9157
+ }
9158
+ )
9159
+ },
9160
+ `pending-${i}`
9161
+ ))
9162
+ ]
9163
+ }
9164
+ )
9165
+ }
9166
+ );
9167
+ }
8967
9168
  function VelocityStrip({
8968
9169
  velocities,
8969
9170
  set,
@@ -8974,6 +9175,7 @@ function VelocityStrip({
8974
9175
  onRepPress,
8975
9176
  variant = "expanded",
8976
9177
  height = EXPANDED_HEIGHT,
9178
+ targetReps,
8977
9179
  scale = "peak",
8978
9180
  showNumbers = true,
8979
9181
  showInfo = true,
@@ -8999,45 +9201,17 @@ function VelocityStrip({
8999
9201
  return SET_STRIP_VARIABLE_COLOR;
9000
9202
  };
9001
9203
  const meanZone = hasZones ? classifyBand(meanVelocity, zones)?.label ?? "" : getVelocityZoneName(meanVelocity);
9002
- const prefersReducedMotion = usePrefersReducedMotion();
9003
9204
  const [heightAnim] = React24.useState(() => new reactNative.Animated.Value(expanded ? height : 3));
9004
9205
  const [labelOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
9005
9206
  const [infoOpacity] = React24.useState(() => new reactNative.Animated.Value(expanded ? 1 : 0));
9006
- const [liveScale] = React24.useState(() => new reactNative.Animated.Value(1));
9007
9207
  const liveVelocity = liveRepIndex != null ? doneVelocities[liveRepIndex] : void 0;
9008
9208
  const isNewPeak = liveVelocity != null && maxVelocity > 0 && liveVelocity === maxVelocity;
9009
- React24.useEffect(() => {
9010
- if (variant !== "expanded" || !framed || liveRepIndex == null || liveVelocity == null) return;
9011
- if (prefersReducedMotion) {
9012
- liveScale.setValue(1);
9013
- return;
9014
- }
9015
- if (isNewPeak) {
9016
- liveScale.setValue(1);
9017
- reactNative.Animated.sequence([
9018
- reactNative.Animated.timing(liveScale, {
9019
- toValue: 1.25,
9020
- duration: 150,
9021
- easing: reactNative.Easing.out(reactNative.Easing.quad),
9022
- useNativeDriver: true
9023
- }),
9024
- reactNative.Animated.spring(liveScale, {
9025
- toValue: 1,
9026
- friction: 3,
9027
- tension: 140,
9028
- useNativeDriver: true
9029
- })
9030
- ]).start();
9031
- } else {
9032
- liveScale.setValue(0.8);
9033
- reactNative.Animated.timing(liveScale, {
9034
- toValue: 1,
9035
- duration: 300,
9036
- easing: POP_EASING,
9037
- useNativeDriver: true
9038
- }).start();
9039
- }
9040
- }, [variant, framed, liveRepIndex, liveVelocity, isNewPeak, prefersReducedMotion, liveScale]);
9209
+ const liveScale = useLiveRepPop(
9210
+ variant === "expanded" && framed,
9211
+ liveRepIndex,
9212
+ liveVelocity,
9213
+ isNewPeak
9214
+ );
9041
9215
  React24.useEffect(() => {
9042
9216
  if (variant !== "expanded" || !framed) return;
9043
9217
  reactNative.Animated.parallel([
@@ -9064,6 +9238,24 @@ function VelocityStrip({
9064
9238
  if (set == null && velocities == null) return null;
9065
9239
  const repCount = doneVelocities.length;
9066
9240
  const miniLabel = set ? setAccessibilityLabel(set, repCount) : `Velocity strip, ${repCount} reps`;
9241
+ if (variant === "hero") {
9242
+ const heroHeight = height === EXPANDED_HEIGHT ? HERO_HEIGHT : height;
9243
+ return /* @__PURE__ */ jsx(
9244
+ HeroVelocityChart,
9245
+ {
9246
+ doneVelocities,
9247
+ barColorFor,
9248
+ scaleDenom,
9249
+ referenceVelocity: maxVelocity,
9250
+ liveRepIndex,
9251
+ isNewPeak,
9252
+ targetReps,
9253
+ height: heroHeight,
9254
+ className,
9255
+ viewProps: props
9256
+ }
9257
+ );
9258
+ }
9067
9259
  if (variant === "mini") {
9068
9260
  const { style: externalStyle, ...restProps } = props;
9069
9261
  return /* @__PURE__ */ jsx(
@@ -9354,13 +9546,13 @@ function VelocityStrip({
9354
9546
  }
9355
9547
  return stripContent;
9356
9548
  }
9357
- var t7 = getSemanticColors("dark");
9549
+ var t6 = getSemanticColors("dark");
9358
9550
  var dotColorMap = {
9359
9551
  untrained: "#2C2C2C",
9360
- behind: t7["brand-secondary"],
9361
- ontrack: t7["status-success"],
9362
- target: t7["brand-primary"],
9363
- over: t7["status-error"]
9552
+ behind: t6["brand-secondary"],
9553
+ ontrack: t6["status-success"],
9554
+ target: t6["brand-primary"],
9555
+ over: t6["status-error"]
9364
9556
  };
9365
9557
  function MuscleGroupChip({
9366
9558
  name,
@@ -9828,6 +10020,99 @@ function InputBar({
9828
10020
  );
9829
10021
  }
9830
10022
  var BRAND_PRIMARY4 = getSemanticColors("dark")["brand-primary"];
10023
+ var RING_DEFAULT_SIZE = 180;
10024
+ function RestActions({ onAddTime, onSkip }) {
10025
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", gap: 8 }, children: [
10026
+ /* @__PURE__ */ jsx(
10027
+ reactNative.Pressable,
10028
+ {
10029
+ onPress: onAddTime,
10030
+ style: {
10031
+ backgroundColor: "rgba(255,255,255,0.06)",
10032
+ paddingVertical: 8,
10033
+ paddingHorizontal: 20,
10034
+ borderRadius: 8
10035
+ },
10036
+ accessibilityRole: "button",
10037
+ accessibilityLabel: "Add 30 seconds",
10038
+ testID: "rest-timer-add-time",
10039
+ children: /* @__PURE__ */ jsx(
10040
+ reactNative.Text,
10041
+ {
10042
+ className: "text-text-secondary",
10043
+ style: { fontSize: 11, fontFamily: "Inter, sans-serif", fontWeight: "600" },
10044
+ children: "+30s"
10045
+ }
10046
+ )
10047
+ }
10048
+ ),
10049
+ /* @__PURE__ */ jsx(
10050
+ reactNative.Pressable,
10051
+ {
10052
+ onPress: onSkip,
10053
+ style: {
10054
+ backgroundColor: "rgba(255,121,0,0.12)",
10055
+ paddingVertical: 8,
10056
+ paddingHorizontal: 20,
10057
+ borderRadius: 8
10058
+ },
10059
+ accessibilityRole: "button",
10060
+ accessibilityLabel: "Skip rest",
10061
+ testID: "rest-timer-skip",
10062
+ children: /* @__PURE__ */ jsx(
10063
+ reactNative.Text,
10064
+ {
10065
+ style: {
10066
+ fontSize: 11,
10067
+ fontFamily: "Inter, sans-serif",
10068
+ fontWeight: "600",
10069
+ color: BRAND_PRIMARY4
10070
+ },
10071
+ children: "Skip"
10072
+ }
10073
+ )
10074
+ }
10075
+ )
10076
+ ] });
10077
+ }
10078
+ function RestTimerRing({
10079
+ totalSeconds,
10080
+ elapsedMs,
10081
+ remainingSec,
10082
+ done,
10083
+ size,
10084
+ nextSetInfo,
10085
+ displayOnly,
10086
+ onSkip,
10087
+ onAddTime
10088
+ }) {
10089
+ const a11yLabel = done ? "Rest complete, next set ready" : `Rest timer, ${Math.max(0, remainingSec)} seconds remaining`;
10090
+ return /* @__PURE__ */ jsxs(reactNative.View, { style: { alignItems: "center", gap: 16 }, testID: "rest-timer", children: [
10091
+ /* @__PURE__ */ jsx(
10092
+ CircularTimer,
10093
+ {
10094
+ durationMs: totalSeconds * 1e3,
10095
+ elapsedMs,
10096
+ mode: "down",
10097
+ size,
10098
+ doneLabel: "GO",
10099
+ doneColor: "success",
10100
+ accessibilityLabel: a11yLabel,
10101
+ controls: !displayOnly ? /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip }) : void 0,
10102
+ testID: "rest-timer-ring"
10103
+ }
10104
+ ),
10105
+ nextSetInfo != null && /* @__PURE__ */ jsx(
10106
+ reactNative.Text,
10107
+ {
10108
+ className: "text-text-tertiary",
10109
+ style: { fontSize: 13, fontFamily: "Inter, sans-serif" },
10110
+ testID: "rest-timer-next-set",
10111
+ children: nextSetInfo
10112
+ }
10113
+ )
10114
+ ] });
10115
+ }
9831
10116
  function RestTimer({
9832
10117
  totalSeconds,
9833
10118
  elapsedMs,
@@ -9835,12 +10120,15 @@ function RestTimer({
9835
10120
  onAddTime,
9836
10121
  nextSetInfo,
9837
10122
  visible,
9838
- displayOnly = false
10123
+ displayOnly = false,
10124
+ variant = "bar",
10125
+ size = RING_DEFAULT_SIZE
9839
10126
  }) {
9840
10127
  const {
9841
10128
  remainingMs,
9842
10129
  progress,
9843
- label: timeDisplay
10130
+ label: timeDisplay,
10131
+ done
9844
10132
  } = useTimer({
9845
10133
  mode: "down",
9846
10134
  durationMs: totalSeconds * 1e3,
@@ -9849,6 +10137,22 @@ function RestTimer({
9849
10137
  const remainingSec = Math.ceil(remainingMs / 1e3);
9850
10138
  const progressPct = progress * 100;
9851
10139
  if (!visible) return null;
10140
+ if (variant === "ring") {
10141
+ return /* @__PURE__ */ jsx(
10142
+ RestTimerRing,
10143
+ {
10144
+ totalSeconds,
10145
+ elapsedMs,
10146
+ remainingSec,
10147
+ done,
10148
+ size,
10149
+ nextSetInfo,
10150
+ displayOnly,
10151
+ onSkip,
10152
+ onAddTime
10153
+ }
10154
+ );
10155
+ }
9852
10156
  return /* @__PURE__ */ jsxs(
9853
10157
  reactNative.View,
9854
10158
  {
@@ -9946,67 +10250,12 @@ function RestTimer({
9946
10250
  )
9947
10251
  }
9948
10252
  ),
9949
- !displayOnly && /* @__PURE__ */ jsxs(reactNative.View, { style: { flexDirection: "row", gap: 8 }, children: [
9950
- /* @__PURE__ */ jsx(
9951
- reactNative.Pressable,
9952
- {
9953
- onPress: onAddTime,
9954
- style: {
9955
- backgroundColor: "rgba(255,255,255,0.06)",
9956
- paddingVertical: 8,
9957
- paddingHorizontal: 20,
9958
- borderRadius: 8
9959
- },
9960
- accessibilityRole: "button",
9961
- accessibilityLabel: "Add 30 seconds",
9962
- testID: "rest-timer-add-time",
9963
- children: /* @__PURE__ */ jsx(
9964
- reactNative.Text,
9965
- {
9966
- className: "text-text-secondary",
9967
- style: {
9968
- fontSize: 11,
9969
- fontFamily: "Inter, sans-serif",
9970
- fontWeight: "600"
9971
- },
9972
- children: "+30s"
9973
- }
9974
- )
9975
- }
9976
- ),
9977
- /* @__PURE__ */ jsx(
9978
- reactNative.Pressable,
9979
- {
9980
- onPress: onSkip,
9981
- style: {
9982
- backgroundColor: "rgba(255,121,0,0.12)",
9983
- paddingVertical: 8,
9984
- paddingHorizontal: 20,
9985
- borderRadius: 8
9986
- },
9987
- accessibilityRole: "button",
9988
- accessibilityLabel: "Skip rest",
9989
- testID: "rest-timer-skip",
9990
- children: /* @__PURE__ */ jsx(
9991
- reactNative.Text,
9992
- {
9993
- style: {
9994
- fontSize: 11,
9995
- fontFamily: "Inter, sans-serif",
9996
- fontWeight: "600",
9997
- color: BRAND_PRIMARY4
9998
- },
9999
- children: "Skip"
10000
- }
10001
- )
10002
- }
10003
- )
10004
- ] })
10253
+ !displayOnly && /* @__PURE__ */ jsx(RestActions, { onAddTime, onSkip })
10005
10254
  ]
10006
10255
  }
10007
10256
  );
10008
10257
  }
10009
- var t8 = getSemanticColors("dark");
10258
+ var t7 = getSemanticColors("dark");
10010
10259
  var TIMES = "\xD7";
10011
10260
  var AT = "@";
10012
10261
  function SetsRepsLoad({
@@ -10014,11 +10263,12 @@ function SetsRepsLoad({
10014
10263
  reps,
10015
10264
  load,
10016
10265
  unit = "lb",
10266
+ fontSize,
10017
10267
  className,
10018
10268
  ...props
10019
10269
  }) {
10020
- const value = t8["text-primary"];
10021
- const sep = t8["text-tertiary"];
10270
+ const value = t7["text-primary"];
10271
+ const sep = t7["text-tertiary"];
10022
10272
  return /* @__PURE__ */ jsxs(
10023
10273
  reactNative.View,
10024
10274
  {
@@ -10028,12 +10278,12 @@ function SetsRepsLoad({
10028
10278
  testID: "sets-reps-load",
10029
10279
  ...props,
10030
10280
  children: [
10031
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: sets }),
10032
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${TIMES} ` }),
10033
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: reps }),
10034
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${AT} ` }),
10035
- /* @__PURE__ */ jsx(MetricCell, { color: value, children: load }),
10036
- /* @__PURE__ */ jsx(MetricCell, { color: sep, children: ` ${unit}` })
10281
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: sets }),
10282
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${TIMES} ` }),
10283
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: reps }),
10284
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${AT} ` }),
10285
+ /* @__PURE__ */ jsx(MetricCell, { color: value, fontSize, children: load }),
10286
+ /* @__PURE__ */ jsx(MetricCell, { color: sep, fontSize, children: ` ${unit}` })
10037
10287
  ]
10038
10288
  }
10039
10289
  );
@@ -10131,14 +10381,14 @@ function ScheduleTiles({ when, now, gap = 1, ...props }) {
10131
10381
  )
10132
10382
  ] });
10133
10383
  }
10134
- var t9 = getSemanticColors("dark");
10384
+ var t8 = getSemanticColors("dark");
10135
10385
  var INDICATOR_CONFIG = {
10136
- imbalance: { Icon: ScaleIcon, color: t9["status-error"], label: "Left/right imbalance" },
10137
- overshoot: { Icon: AlertTriangleIcon, color: t9["status-error"], label: "Load overshoot" },
10138
- "velocity-loss": { Icon: TrendingDownIcon, color: t9["status-warning"], label: "Velocity loss" },
10139
- "missed-reps": { Icon: CircleSlashIcon, color: t9["status-warning"], label: "Missed reps" },
10140
- pr: { Icon: AwardIcon, color: t9["status-success"], label: "Personal record" },
10141
- info: { Icon: InfoIcon, color: t9["status-info"], label: "More info" }
10386
+ imbalance: { Icon: ScaleIcon, color: t8["status-error"], label: "Left/right imbalance" },
10387
+ overshoot: { Icon: AlertTriangleIcon, color: t8["status-error"], label: "Load overshoot" },
10388
+ "velocity-loss": { Icon: TrendingDownIcon, color: t8["status-warning"], label: "Velocity loss" },
10389
+ "missed-reps": { Icon: CircleSlashIcon, color: t8["status-warning"], label: "Missed reps" },
10390
+ pr: { Icon: AwardIcon, color: t8["status-success"], label: "Personal record" },
10391
+ info: { Icon: InfoIcon, color: t8["status-info"], label: "More info" }
10142
10392
  };
10143
10393
  var INDICATOR_PRECEDENCE = [
10144
10394
  "imbalance",
@@ -10613,7 +10863,7 @@ function ExerciseCard({
10613
10863
  if (upcoming) return /* @__PURE__ */ jsx(UpcomingCard, { ...rest, onToggle });
10614
10864
  return isExpanded ? /* @__PURE__ */ jsx(ExpandedCard, { ...rest, onToggle }) : /* @__PURE__ */ jsx(CollapsedCard, { ...rest, onToggle });
10615
10865
  }
10616
- var t10 = getSemanticColors("dark");
10866
+ var t9 = getSemanticColors("dark");
10617
10867
  var RAISED = primitiveColors.charcoal[400];
10618
10868
  var BAR_HEIGHT = 9;
10619
10869
  var BAR_GAP = 3;
@@ -10635,7 +10885,7 @@ function SessionHeader({
10635
10885
  const hasPace = !upcoming && budgetMs != null && elapsedMs != null;
10636
10886
  const target = hasPace ? elapsedMs / budgetMs : void 0;
10637
10887
  const setsLabel = upcoming ? `${totalSets} sets` : `${Math.floor(setsDone)}/${totalSets} sets`;
10638
- const setsLabelColor = upcoming ? t10["text-secondary"] : paceToneColor(paceTone(totalSets > 0 ? setsDone / totalSets : 0, target));
10888
+ const setsLabelColor = upcoming ? t9["text-secondary"] : paceToneColor(paceTone(totalSets > 0 ? setsDone / totalSets : 0, target));
10639
10889
  return /* @__PURE__ */ jsxs(
10640
10890
  reactNative.View,
10641
10891
  {
@@ -10663,7 +10913,7 @@ function SessionHeader({
10663
10913
  lineHeight: 18,
10664
10914
  fontWeight: "700",
10665
10915
  fontFamily: '"Space Grotesk", sans-serif',
10666
- color: t10["text-primary"],
10916
+ color: t9["text-primary"],
10667
10917
  marginBottom: 10
10668
10918
  },
10669
10919
  testID: "session-rail-title",
@@ -11058,10 +11308,10 @@ function WeekRow({
11058
11308
  }
11059
11309
  );
11060
11310
  }
11061
- var t11 = getSemanticColors("dark");
11311
+ var t10 = getSemanticColors("dark");
11062
11312
  var COLORS = {
11063
- statusSuccess: t11["status-success"],
11064
- brandPrimary: t11["brand-primary"],
11313
+ statusSuccess: t10["status-success"],
11314
+ brandPrimary: t10["brand-primary"],
11065
11315
  borderDefault: "#1F1F1F",
11066
11316
  brandPrimarySubtle: "rgba(255,121,0,0.06)"
11067
11317
  };
@@ -11545,12 +11795,12 @@ function PrHistoryModal({
11545
11795
  }
11546
11796
  );
11547
11797
  }
11548
- var t12 = getSemanticColors("dark");
11549
- var BRAND_PRIMARY9 = t12["brand-primary"];
11798
+ var t11 = getSemanticColors("dark");
11799
+ var BRAND_PRIMARY9 = t11["brand-primary"];
11550
11800
  var BRAND_PRIMARY_SUBTLE = "rgba(255,121,0,0.12)";
11551
- var STATUS_SUCCESS = t12["status-success"];
11552
- var STATUS_WARNING = t12["status-warning"];
11553
- var STATUS_ERROR2 = t12["status-error"];
11801
+ var STATUS_SUCCESS2 = t11["status-success"];
11802
+ var STATUS_WARNING2 = t11["status-warning"];
11803
+ var STATUS_ERROR2 = t11["status-error"];
11554
11804
  var EMOJI_SETS = {
11555
11805
  sleep: ["\u{1F634}", "\u{1F610}", "\u{1F642}", "\u{1F60A}", "\u{1F929}"],
11556
11806
  soreness: ["\u{1F635}", "\u{1F623}", "\u{1F610}", "\u{1F642}", "\u{1F4AA}"],
@@ -11560,8 +11810,8 @@ var EMOJI_SETS = {
11560
11810
  var DEFAULT_EMOJI_SET = ["\u{1F61E}", "\u{1F615}", "\u{1F610}", "\u{1F642}", "\u{1F604}"];
11561
11811
  function scoreColor(score) {
11562
11812
  if (score < 40) return STATUS_ERROR2;
11563
- if (score < 70) return STATUS_WARNING;
11564
- return STATUS_SUCCESS;
11813
+ if (score < 70) return STATUS_WARNING2;
11814
+ return STATUS_SUCCESS2;
11565
11815
  }
11566
11816
  var WARMUP_BADGE = {
11567
11817
  good: { color: "success", label: "Good" },
@@ -11740,13 +11990,13 @@ function ReadinessCheck({
11740
11990
  )
11741
11991
  ] }) });
11742
11992
  }
11743
- var t13 = getSemanticColors("dark");
11744
- var BRAND_PRIMARY10 = t13["brand-primary"];
11993
+ var t12 = getSemanticColors("dark");
11994
+ var BRAND_PRIMARY10 = t12["brand-primary"];
11745
11995
  var BRAND_PRIMARY_DARK2 = MESO_ACCENT_GRADIENT_DARK;
11746
11996
  var BRAND_PRIMARY_LIGHT2 = MESO_ACCENT_GRADIENT_LIGHT;
11747
- var SUCCESS = t13["status-success"];
11748
- var WARNING = t13["status-warning"];
11749
- var ERROR = t13["status-error"];
11997
+ var SUCCESS = t12["status-success"];
11998
+ var WARNING = t12["status-warning"];
11999
+ var ERROR = t12["status-error"];
11750
12000
  var ACCENT_STOPS2 = [BRAND_PRIMARY_DARK2, BRAND_PRIMARY10, BRAND_PRIMARY_LIGHT2];
11751
12001
  var CARD_GRADIENT = `linear-gradient(135deg, ${resolveColor("surface-elevated")} 0%, ${resolveColor("surface-raised")} 100%)`;
11752
12002
  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%)";
@@ -12106,9 +12356,9 @@ function MesoStatusCard({
12106
12356
  var sem = getSemanticColors("dark");
12107
12357
  var BRAND_PRIMARY11 = sem["brand-primary"];
12108
12358
  var PROJECTION_LINE_COLOR = resolveColor("text-tertiary");
12109
- var STATUS_SUCCESS2 = sem["status-success"];
12359
+ var STATUS_SUCCESS3 = sem["status-success"];
12110
12360
  var STATUS_ERROR3 = sem["status-error"];
12111
- var STATUS_WARNING2 = sem["status-warning"];
12361
+ var STATUS_WARNING3 = sem["status-warning"];
12112
12362
  var GRID_LINE = "rgba(255,255,255,0.06)";
12113
12363
  var SUCCESS_PILL_BG = "rgba(46,213,115,0.10)";
12114
12364
  var SUCCESS_PILL_BORDER = "rgba(46,213,115,0.20)";
@@ -12117,8 +12367,8 @@ var ERROR_PILL_BORDER = "rgba(209,67,67,0.20)";
12117
12367
  var PLOT_LEFT = 26;
12118
12368
  var TOOLTIP_WIDTH = 124;
12119
12369
  function timeOf(dateStr) {
12120
- const t18 = Date.parse(dateStr);
12121
- return Number.isNaN(t18) ? 0 : t18;
12370
+ const t17 = Date.parse(dateStr);
12371
+ return Number.isNaN(t17) ? 0 : t17;
12122
12372
  }
12123
12373
  function formatValue(value) {
12124
12374
  return `${Math.round(value)}`;
@@ -12414,7 +12664,7 @@ function StrengthTrendChart({
12414
12664
  left: c.x - 7,
12415
12665
  top: c.y - 20,
12416
12666
  fontSize: 12,
12417
- color: STATUS_WARNING2
12667
+ color: STATUS_WARNING3
12418
12668
  },
12419
12669
  children: "\u2605"
12420
12670
  },
@@ -12507,7 +12757,7 @@ function StrengthTrendChart({
12507
12757
  marginTop: 1,
12508
12758
  fontSize: 10,
12509
12759
  fontFamily: "Inter, sans-serif",
12510
- color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS2 : STATUS_ERROR3
12760
+ color: selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? STATUS_SUCCESS3 : STATUS_ERROR3
12511
12761
  },
12512
12762
  children: `${selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm >= 0 ? "+" : "-"}${Math.abs(
12513
12763
  selectedCoord.point.e1rm - geometry.actual[selected - 1].point.e1rm
@@ -12563,7 +12813,7 @@ function StrengthTrendChart({
12563
12813
  fontSize: 11,
12564
12814
  fontFamily: "Inter, sans-serif",
12565
12815
  fontWeight: "600",
12566
- color: trend.positive ? STATUS_SUCCESS2 : STATUS_ERROR3
12816
+ color: trend.positive ? STATUS_SUCCESS3 : STATUS_ERROR3
12567
12817
  },
12568
12818
  children: trendText
12569
12819
  }
@@ -12597,7 +12847,7 @@ function StrengthTrendChart({
12597
12847
  /* @__PURE__ */ jsx(reactNative.Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "Projected" })
12598
12848
  ] }),
12599
12849
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-center", style: { gap: 5 }, children: [
12600
- /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, color: STATUS_WARNING2 }, children: "\u2605" }),
12850
+ /* @__PURE__ */ jsx(reactNative.Text, { style: { fontSize: 12, color: STATUS_WARNING3 }, children: "\u2605" }),
12601
12851
  /* @__PURE__ */ jsx(reactNative.Text, { className: "text-text-secondary", style: { fontSize: 10, fontFamily: "Inter, sans-serif" }, children: "PR" })
12602
12852
  ] })
12603
12853
  ]
@@ -12607,10 +12857,10 @@ function StrengthTrendChart({
12607
12857
  }
12608
12858
  );
12609
12859
  }
12610
- var t14 = getSemanticColors("dark");
12611
- var STATUS_SUCCESS3 = t14["status-success"];
12612
- var STATUS_WARNING3 = t14["status-warning"];
12613
- var STATUS_INFO = t14["status-info"];
12860
+ var t13 = getSemanticColors("dark");
12861
+ var STATUS_SUCCESS4 = t13["status-success"];
12862
+ var STATUS_WARNING4 = t13["status-warning"];
12863
+ var STATUS_INFO = t13["status-info"];
12614
12864
  var DOT_BORDER = "#F3F4F6";
12615
12865
  var BAND_FILL = "rgba(46,213,115,0.1)";
12616
12866
  var BAND_EDGE = "rgba(46,213,115,0.45)";
@@ -12622,8 +12872,8 @@ var PADDING_BOTTOM = 16;
12622
12872
  var COLUMN_STEP = 4;
12623
12873
  var DOT_SIZE = 8;
12624
12874
  var DOT_COLORS = {
12625
- within: STATUS_SUCCESS3,
12626
- above: STATUS_WARNING3,
12875
+ within: STATUS_SUCCESS4,
12876
+ above: STATUS_WARNING4,
12627
12877
  below: STATUS_INFO
12628
12878
  };
12629
12879
  var STATUS_PHRASES = {
@@ -12653,8 +12903,8 @@ function interpEdge(pixels, x, key) {
12653
12903
  const a = pixels[i];
12654
12904
  const b = pixels[i + 1];
12655
12905
  if (x >= a.x && x <= b.x) {
12656
- const t18 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
12657
- return a[key] + t18 * (b[key] - a[key]);
12906
+ const t17 = b.x === a.x ? 0 : (x - a.x) / (b.x - a.x);
12907
+ return a[key] + t17 * (b[key] - a[key]);
12658
12908
  }
12659
12909
  }
12660
12910
  return pixels[pixels.length - 1][key];
@@ -12848,8 +13098,8 @@ function CapacityBandChart({
12848
13098
  projection && /* @__PURE__ */ jsxs(reactNative.View, { testID: "capacity-band-chart-projection", children: [
12849
13099
  renderColumns(buildColumns2(trainingPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12850
13100
  renderColumns(buildColumns2(restPixels, COLUMN_STEP), PROJECTION_FILL, "capacity-band-chart-projection-fill"),
12851
- renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
12852
- renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS3, true, "capacity-band-chart-projection-training"),
13101
+ renderEdges(buildEdges(trainingPixels, "yHigh"), STATUS_SUCCESS4, true, "capacity-band-chart-projection-training"),
13102
+ renderEdges(buildEdges(trainingPixels, "yLow"), STATUS_SUCCESS4, true, "capacity-band-chart-projection-training"),
12853
13103
  renderEdges(buildEdges(restPixels, "yHigh"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
12854
13104
  renderEdges(buildEdges(restPixels, "yLow"), STATUS_INFO, true, "capacity-band-chart-projection-rest"),
12855
13105
  /* @__PURE__ */ jsx(
@@ -12861,7 +13111,7 @@ function CapacityBandChart({
12861
13111
  top: trainingPixels[trainingPixels.length - 1]?.yHigh - 12,
12862
13112
  fontSize: 9,
12863
13113
  fontFamily: "Inter, sans-serif",
12864
- color: STATUS_SUCCESS3
13114
+ color: STATUS_SUCCESS4
12865
13115
  },
12866
13116
  testID: "capacity-band-chart-projection-training-label",
12867
13117
  children: "Training"
@@ -12984,16 +13234,43 @@ function CapacityBandChart({
12984
13234
  }
12985
13235
  );
12986
13236
  }
12987
- var t15 = getSemanticColors("dark");
13237
+ var t14 = getSemanticColors("dark");
12988
13238
  function glowShadow(color) {
12989
13239
  return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`;
12990
13240
  }
12991
13241
  var DEFAULT_TRACK_COLOR = primitiveColors.charcoal[200];
12992
13242
  var DEFAULT_MARKER_COLOR2 = primitiveColors.white;
12993
- var TICK_COLOR = t15["text-tertiary"];
12994
- var DEFAULT_TRACK_HEIGHT = 14;
12995
- var DEFAULT_NEEDLE_OVERHANG = 6;
12996
- var NEEDLE_WIDTH = 4;
13243
+ var TICK_COLOR = t14["text-tertiary"];
13244
+ var ZONE_SIZES = {
13245
+ default: {
13246
+ trackHeight: 14,
13247
+ needleOverhang: 6,
13248
+ needleWidth: 4,
13249
+ tickLineNormal: 1.5,
13250
+ tickLineEmphasized: 2,
13251
+ tickLineHeightPad: 4,
13252
+ tickLineTopPad: 2,
13253
+ labelRowHeight: 16,
13254
+ labelMarginTop: 5,
13255
+ tickFont: 9,
13256
+ tickLetterSpacing: 0.5,
13257
+ tickCellWidth: 28
13258
+ },
13259
+ wall: {
13260
+ trackHeight: 24,
13261
+ needleOverhang: 9,
13262
+ needleWidth: 7,
13263
+ tickLineNormal: 2.5,
13264
+ tickLineEmphasized: 3.5,
13265
+ tickLineHeightPad: 6,
13266
+ tickLineTopPad: 3,
13267
+ labelRowHeight: 24,
13268
+ labelMarginTop: 8,
13269
+ tickFont: 14,
13270
+ tickLetterSpacing: 0.5,
13271
+ tickCellWidth: 48
13272
+ }
13273
+ };
12997
13274
  function fraction(value, min, max) {
12998
13275
  if (max <= min) return 0;
12999
13276
  return Math.max(0, Math.min(1, (value - min) / (max - min)));
@@ -13008,14 +13285,24 @@ function ZoneTrack({
13008
13285
  marker = null,
13009
13286
  ticks,
13010
13287
  band = null,
13011
- trackHeight = DEFAULT_TRACK_HEIGHT,
13012
- needleOverhang = DEFAULT_NEEDLE_OVERHANG,
13288
+ size = "default",
13289
+ trackHeight: trackHeightProp,
13290
+ needleOverhang: needleOverhangProp,
13013
13291
  trackColor = DEFAULT_TRACK_COLOR,
13014
13292
  className,
13015
13293
  style,
13016
13294
  accessibilityLabel: accessibilityLabel2,
13017
13295
  ...props
13018
13296
  }) {
13297
+ const s = ZONE_SIZES[size];
13298
+ const trackHeight = trackHeightProp ?? s.trackHeight;
13299
+ const needleOverhang = needleOverhangProp ?? s.needleOverhang;
13300
+ const [trackW, setTrackW] = React24.useState(0);
13301
+ const onTrackLayout = (e) => setTrackW(e.nativeEvent.layout.width);
13302
+ const tickCount = ticks?.length ?? 0;
13303
+ const tickSpacing = trackW > 0 && tickCount > 0 ? trackW / tickCount : Infinity;
13304
+ const tickStep = tickSpacing < s.tickCellWidth ? Math.ceil(s.tickCellWidth / tickSpacing) : 1;
13305
+ const showTickLabel = (i, emphasized) => tickStep <= 1 || i === 0 || i === tickCount - 1 || i % tickStep === 0 || emphasized;
13019
13306
  const isNeedle = marker?.type === "needle";
13020
13307
  const markerHeight = isNeedle ? trackHeight + needleOverhang * 2 : trackHeight;
13021
13308
  const markerFrac = marker != null ? fraction(marker.value, min, max) : 0;
@@ -13034,6 +13321,7 @@ function ZoneTrack({
13034
13321
  accessibilityValue: marker != null ? { min, max, now: marker.value } : { min, max, now: min },
13035
13322
  accessibilityLabel: accessibilityLabel2 ?? `Zone track: ${marker?.value ?? min}`,
13036
13323
  testID: "zone-track",
13324
+ onLayout: onTrackLayout,
13037
13325
  ...props,
13038
13326
  children: [
13039
13327
  /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "relative", height: markerHeight }, children: [
@@ -13125,18 +13413,18 @@ function ZoneTrack({
13125
13413
  position: "absolute",
13126
13414
  top: 0,
13127
13415
  left: pct(markerFrac),
13128
- width: NEEDLE_WIDTH,
13416
+ width: s.needleWidth,
13129
13417
  height: markerHeight,
13130
- borderRadius: NEEDLE_WIDTH / 2,
13418
+ borderRadius: s.needleWidth / 2,
13131
13419
  backgroundColor: marker.color ?? DEFAULT_MARKER_COLOR2,
13132
- transform: [{ translateX: -NEEDLE_WIDTH / 2 }]
13420
+ transform: [{ translateX: -s.needleWidth / 2 }]
13133
13421
  }
13134
13422
  }
13135
13423
  ),
13136
13424
  ticks?.map((tick, i) => {
13137
13425
  const emphasized = tick.emphasized === true;
13138
- const lineColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
13139
- const lineWidth = emphasized ? 2 : 1.5;
13426
+ const lineColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
13427
+ const lineWidth = emphasized ? s.tickLineEmphasized : s.tickLineNormal;
13140
13428
  return /* @__PURE__ */ jsx(
13141
13429
  reactNative.View,
13142
13430
  {
@@ -13144,10 +13432,10 @@ function ZoneTrack({
13144
13432
  accessibilityElementsHidden: true,
13145
13433
  style: {
13146
13434
  position: "absolute",
13147
- top: (markerHeight - trackHeight) / 2 - 2,
13435
+ top: (markerHeight - trackHeight) / 2 - s.tickLineTopPad,
13148
13436
  left: pct(fraction(tick.value, min, max)),
13149
13437
  width: lineWidth,
13150
- height: trackHeight + 4,
13438
+ height: trackHeight + s.tickLineHeightPad,
13151
13439
  borderRadius: lineWidth / 2,
13152
13440
  backgroundColor: lineColor,
13153
13441
  transform: [{ translateX: -lineWidth / 2 }],
@@ -13158,40 +13446,47 @@ function ZoneTrack({
13158
13446
  );
13159
13447
  })
13160
13448
  ] }),
13161
- ticks != null && ticks.length > 0 && /* @__PURE__ */ jsx(reactNative.View, { style: { position: "relative", height: 16, marginTop: 5 }, children: ticks.map((tick, i) => {
13162
- if (tick.label == null) return null;
13163
- const emphasized = tick.emphasized === true;
13164
- const labelColor = tick.color ?? (emphasized ? t15["brand-primary"] : TICK_COLOR);
13165
- const labelNode = /* @__PURE__ */ jsx(
13166
- reactNative.Text,
13167
- {
13168
- testID: "zone-track-tick-label",
13169
- style: {
13170
- fontSize: 9,
13171
- letterSpacing: 0.5,
13172
- color: labelColor,
13173
- fontFamily: "monospace",
13174
- fontWeight: emphasized ? "700" : "400"
13175
- },
13176
- children: tick.label
13177
- }
13178
- );
13179
- return /* @__PURE__ */ jsx(
13180
- reactNative.View,
13181
- {
13182
- testID: "zone-track-tick",
13183
- style: {
13184
- position: "absolute",
13185
- left: pct(fraction(tick.value, min, max)),
13186
- transform: [{ translateX: -14 }],
13187
- width: 28,
13188
- alignItems: "center"
13189
- },
13190
- children: tick.tooltip != null ? /* @__PURE__ */ jsx(Tooltip, { label: tick.tooltip, placement: "top", children: labelNode }) : labelNode
13191
- },
13192
- `label-${i}`
13193
- );
13194
- }) })
13449
+ ticks != null && ticks.length > 0 && /* @__PURE__ */ jsx(
13450
+ reactNative.View,
13451
+ {
13452
+ style: { position: "relative", height: s.labelRowHeight, marginTop: s.labelMarginTop },
13453
+ children: ticks.map((tick, i) => {
13454
+ if (tick.label == null) return null;
13455
+ const emphasized = tick.emphasized === true;
13456
+ if (!showTickLabel(i, emphasized)) return null;
13457
+ const labelColor = tick.color ?? (emphasized ? t14["brand-primary"] : TICK_COLOR);
13458
+ const labelNode = /* @__PURE__ */ jsx(
13459
+ reactNative.Text,
13460
+ {
13461
+ testID: "zone-track-tick-label",
13462
+ style: {
13463
+ fontSize: s.tickFont,
13464
+ letterSpacing: s.tickLetterSpacing,
13465
+ color: labelColor,
13466
+ fontFamily: "monospace",
13467
+ fontWeight: emphasized ? "700" : "400"
13468
+ },
13469
+ children: tick.label
13470
+ }
13471
+ );
13472
+ return /* @__PURE__ */ jsx(
13473
+ reactNative.View,
13474
+ {
13475
+ testID: "zone-track-tick",
13476
+ style: {
13477
+ position: "absolute",
13478
+ left: pct(fraction(tick.value, min, max)),
13479
+ transform: [{ translateX: -s.tickCellWidth / 2 }],
13480
+ width: s.tickCellWidth,
13481
+ alignItems: "center"
13482
+ },
13483
+ children: tick.tooltip != null ? /* @__PURE__ */ jsx(Tooltip, { label: tick.tooltip, placement: "top", children: labelNode }) : labelNode
13484
+ },
13485
+ `label-${i}`
13486
+ );
13487
+ })
13488
+ }
13489
+ )
13195
13490
  ]
13196
13491
  }
13197
13492
  );
@@ -13209,7 +13504,8 @@ function FatigueMeter({
13209
13504
  thresholds = DEFAULT_THRESHOLDS,
13210
13505
  zoneColors = DEFAULT_ZONE_COLORS,
13211
13506
  labels = DEFAULT_LABELS,
13212
- trackHeight = 14,
13507
+ size = "default",
13508
+ trackHeight,
13213
13509
  needleColor,
13214
13510
  className,
13215
13511
  ...props
@@ -13230,6 +13526,7 @@ function FatigueMeter({
13230
13526
  zones,
13231
13527
  min: 0,
13232
13528
  max,
13529
+ size,
13233
13530
  trackHeight,
13234
13531
  ticks,
13235
13532
  marker: { type: "needle", value, color: needleColor },
@@ -13241,7 +13538,7 @@ function FatigueMeter({
13241
13538
  }
13242
13539
  var HEAT = WORKOUT_TOKENS.heatmap;
13243
13540
  var NEUTRAL_TRACK = primitiveColors.charcoal[200];
13244
- var MONO = "monospace";
13541
+ var MONO2 = "monospace";
13245
13542
  var HEAT_BY_ZONE = {
13246
13543
  under: HEAT.under,
13247
13544
  maintenance: HEAT.maintenance,
@@ -13300,7 +13597,7 @@ function VolumeLandmarkBar({
13300
13597
  reactNative.Text,
13301
13598
  {
13302
13599
  testID: "volume-landmark-pct",
13303
- style: { fontSize: 14, fontWeight: "700", fontFamily: MONO, color: fillColor },
13600
+ style: { fontSize: 14, fontWeight: "700", fontFamily: MONO2, color: fillColor },
13304
13601
  children: [
13305
13602
  pct2,
13306
13603
  "%"
@@ -13343,7 +13640,7 @@ function VolumeLandmarkBar({
13343
13640
  }
13344
13641
  );
13345
13642
  }
13346
- var t16 = getSemanticColors("dark");
13643
+ var t15 = getSemanticColors("dark");
13347
13644
  var statusToken = {
13348
13645
  productive: "status-success",
13349
13646
  threshold: "status-warning",
@@ -13360,7 +13657,7 @@ var defaultLabel = {
13360
13657
  stop: "Stop"
13361
13658
  };
13362
13659
  function statusPillColor(status) {
13363
- return t16[statusToken[status]];
13660
+ return t15[statusToken[status]];
13364
13661
  }
13365
13662
  function StatusPill2({ status, label, className, style, ...props }) {
13366
13663
  const color = statusPillColor(status);
@@ -13393,7 +13690,7 @@ function StatusPill2({ status, label, className, style, ...props }) {
13393
13690
  }
13394
13691
  );
13395
13692
  }
13396
- var t17 = getSemanticColors("dark");
13693
+ var t16 = getSemanticColors("dark");
13397
13694
  var categoryToken = {
13398
13695
  productive: null,
13399
13696
  threshold: "status-warning",
@@ -13406,7 +13703,7 @@ var floodOpacity = {
13406
13703
  };
13407
13704
  function liveAuraColor(category) {
13408
13705
  const token = categoryToken[category];
13409
- return token ? t17[token] : null;
13706
+ return token ? t16[token] : null;
13410
13707
  }
13411
13708
  function LiveAuraFrame({
13412
13709
  category,
@@ -13435,9 +13732,9 @@ function LiveAuraFrame({
13435
13732
  position: "relative",
13436
13733
  overflow: "hidden",
13437
13734
  borderRadius: 10,
13438
- backgroundColor: t17["background-base"],
13735
+ backgroundColor: t16["background-base"],
13439
13736
  borderWidth: 1,
13440
- borderColor: t17["border-default"]
13737
+ borderColor: t16["border-default"]
13441
13738
  },
13442
13739
  style
13443
13740
  ],
@@ -13664,28 +13961,28 @@ function Scatter({
13664
13961
  testID: "scatter-canvas",
13665
13962
  style: { position: "absolute", top: 0, left: 0, width, height, overflow: "hidden" },
13666
13963
  children: [
13667
- ticksOf(yd, TICK_COUNT).map((t18, i) => {
13668
- const y = toY(t18);
13964
+ ticksOf(yd, TICK_COUNT).map((t17, i) => {
13965
+ const y = toY(t17);
13669
13966
  return /* @__PURE__ */ jsx(
13670
13967
  reactNative.View,
13671
13968
  {
13672
13969
  accessibilityElementsHidden: true,
13673
13970
  testID: "scatter-gridline-y",
13674
13971
  style: { position: "absolute", left: PLOT_LEFT2, width: innerW, top: y, height: 1, backgroundColor: GRID_LINE2 },
13675
- 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(t18) })
13972
+ 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(t17) })
13676
13973
  },
13677
13974
  `y-${i}`
13678
13975
  );
13679
13976
  }),
13680
- ticksOf(xd, TICK_COUNT).map((t18, i) => {
13681
- const x = toX(t18);
13977
+ ticksOf(xd, TICK_COUNT).map((t17, i) => {
13978
+ const x = toX(t17);
13682
13979
  return /* @__PURE__ */ jsx(
13683
13980
  reactNative.View,
13684
13981
  {
13685
13982
  accessibilityElementsHidden: true,
13686
13983
  testID: "scatter-gridline-x",
13687
13984
  style: { position: "absolute", top: PLOT_TOP, height: innerH, left: x, width: 1, backgroundColor: GRID_LINE2 },
13688
- 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(t18) })
13985
+ 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(t17) })
13689
13986
  },
13690
13987
  `x-${i}`
13691
13988
  );
@@ -13746,14 +14043,14 @@ function Scatter({
13746
14043
  ] });
13747
14044
  }
13748
14045
  var sem2 = getSemanticColors("dark");
13749
- var STATUS_SUCCESS4 = sem2["status-success"];
13750
- var STATUS_WARNING4 = sem2["status-warning"];
14046
+ var STATUS_SUCCESS5 = sem2["status-success"];
14047
+ var STATUS_WARNING5 = sem2["status-warning"];
13751
14048
  var STATUS_ERROR4 = sem2["status-error"];
13752
14049
  var TRACK = "rgba(255,255,255,0.08)";
13753
14050
  var DEFAULT_THRESHOLDS2 = [
13754
14051
  { value: 0, color: STATUS_ERROR4 },
13755
- { value: 60, color: STATUS_WARNING4 },
13756
- { value: 80, color: STATUS_SUCCESS4 }
14052
+ { value: 60, color: STATUS_WARNING5 },
14053
+ { value: 80, color: STATUS_SUCCESS5 }
13757
14054
  ];
13758
14055
  var ARC_START = 135;
13759
14056
  var ARC_SWEEP = 270;
@@ -13768,7 +14065,7 @@ function formatValue2(v) {
13768
14065
  function bandColor2(fraction2, bands, min, max) {
13769
14066
  const value = min + fraction2 * (max - min);
13770
14067
  const sorted = [...bands].sort((a, b) => a.value - b.value);
13771
- let color = sorted[0]?.color ?? STATUS_SUCCESS4;
14068
+ let color = sorted[0]?.color ?? STATUS_SUCCESS5;
13772
14069
  for (const band of sorted) {
13773
14070
  if (value >= band.value) color = band.color;
13774
14071
  }
@@ -13820,23 +14117,23 @@ function Gauge2({
13820
14117
  testID: "gauge",
13821
14118
  ...props,
13822
14119
  children: [
13823
- ticks.map((t18) => /* @__PURE__ */ jsx(
14120
+ ticks.map((t17) => /* @__PURE__ */ jsx(
13824
14121
  reactNative.View,
13825
14122
  {
13826
14123
  accessibilityElementsHidden: true,
13827
14124
  testID: "gauge-segment",
13828
14125
  style: {
13829
14126
  position: "absolute",
13830
- left: t18.left - tickThickness / 2,
13831
- top: t18.top - tickLength / 2,
14127
+ left: t17.left - tickThickness / 2,
14128
+ top: t17.top - tickLength / 2,
13832
14129
  width: tickThickness,
13833
14130
  height: tickLength,
13834
14131
  borderRadius: tickThickness / 2,
13835
- backgroundColor: t18.color,
13836
- transform: [{ rotate: t18.rotate }]
14132
+ backgroundColor: t17.color,
14133
+ transform: [{ rotate: t17.rotate }]
13837
14134
  }
13838
14135
  },
13839
- t18.key
14136
+ t17.key
13840
14137
  )),
13841
14138
  /* @__PURE__ */ jsxs(reactNative.View, { style: { position: "absolute", top: 0, left: 0, width: size, height: size }, className: "items-center justify-center", children: [
13842
14139
  /* @__PURE__ */ jsxs(reactNative.View, { className: "flex-row items-baseline gap-0.5", children: [
@@ -14296,6 +14593,7 @@ exports.CheckboxGroup = CheckboxGroup;
14296
14593
  exports.Chip = Chip;
14297
14594
  exports.CircleSlashIcon = CircleSlashIcon;
14298
14595
  exports.CircularProgress = CircularProgress;
14596
+ exports.CircularTimer = CircularTimer;
14299
14597
  exports.Collapse = Collapse;
14300
14598
  exports.CollapseButton = CollapseButton;
14301
14599
  exports.CollapseContent = CollapseContent;
@@ -14434,7 +14732,6 @@ exports.SupersetWrapper = SupersetWrapper;
14434
14732
  exports.Surface = Surface;
14435
14733
  exports.SvgIcon = SvgIcon;
14436
14734
  exports.Switch = Switch;
14437
- exports.TEMPO_PACING = TEMPO_PACING;
14438
14735
  exports.Tab = Tab;
14439
14736
  exports.TabList = TabList;
14440
14737
  exports.TabPanel = TabPanel;
@@ -14447,7 +14744,6 @@ exports.TableHeaderCell = TableHeaderCell;
14447
14744
  exports.TablePagination = TablePagination;
14448
14745
  exports.TableRow = TableRow;
14449
14746
  exports.Tabs = Tabs;
14450
- exports.TempoBar = TempoBar;
14451
14747
  exports.TempoDisplay = TempoDisplay;
14452
14748
  exports.Tile = Tile;
14453
14749
  exports.TimerReadout = TimerReadout;
@@ -14513,8 +14809,6 @@ exports.getLuminance = getLuminance;
14513
14809
  exports.getResultColor = getResultColor;
14514
14810
  exports.getSemanticColors = getSemanticColors;
14515
14811
  exports.getStatusColor = getStatusColor;
14516
- exports.getTempoFillPct = getTempoFillPct;
14517
- exports.getTempoPacingState = getTempoPacingState;
14518
14812
  exports.getThemeCSSVars = getThemeCSSVars;
14519
14813
  exports.getValidatedElevation = getValidatedElevation;
14520
14814
  exports.getVelocityZoneColor = getVelocityZoneColor;