@primestyleai/tryon 5.9.1 → 5.10.1

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.
@@ -4,3 +4,9 @@ export declare function kgToLbs(kg: number): number;
4
4
  export declare function lbsToKg(lbs: number): number;
5
5
  export declare function ftInToCm(ft: number, inch: number): number;
6
6
  export declare function isImperial(locale: string): boolean;
7
+ /**
8
+ * Human-readable label for a unit token. Used to append a system label to
9
+ * primary CTAs ("Find My Best Fit (Metric)") so the user knows whether their
10
+ * values will be interpreted as cm/kg or in/lbs before they submit.
11
+ */
12
+ export declare function getUnitLabel(unit: string | undefined | null): string;
@@ -10080,7 +10080,8 @@ async function recommendForProduct(input) {
10080
10080
  sections: sectionsMap,
10081
10081
  profileId: profile.id,
10082
10082
  fromCache: false,
10083
- raw: result
10083
+ raw: result,
10084
+ found: result.found
10084
10085
  };
10085
10086
  }
10086
10087
  async function estimateFullMeasurements(args) {
@@ -10139,6 +10140,12 @@ async function estimateFullMeasurements(args) {
10139
10140
  function isImperial(locale) {
10140
10141
  return ["US", "UK", "AU"].includes(locale);
10141
10142
  }
10143
+ function getUnitLabel(unit) {
10144
+ if (unit === "in" || unit === "inches" || unit === "lbs") return "Imperial";
10145
+ if (unit === "cm" || unit === "kg") return "Metric";
10146
+ if (unit === "mm") return "mm";
10147
+ return "";
10148
+ }
10142
10149
  function cx(base, override) {
10143
10150
  return override ? `${base} ${override}` : base;
10144
10151
  }
@@ -10258,12 +10265,15 @@ const STYLES$1 = `
10258
10265
  .ps-tryon-logo-img { height: var(--ps-logo-height); width: auto; }
10259
10266
  .ps-tryon-header-actions { display: flex; align-items: center; gap: 0.42vw; }
10260
10267
  .ps-tryon-header-icon {
10261
- width: 2.2vw; height: 2.2vw; display: flex; align-items: center; justify-content: center;
10262
- border: 1.5px solid var(--ps-border-color); border-radius: 0.52vw; background: transparent;
10268
+ /* Pure vw sizing collapsed to ~8 px on 375 px mobile. Clamp so the icon
10269
+ stays finger-tappable (min 30 px) while scaling up on large screens. */
10270
+ width: clamp(30px, 2.2vw, 34px); height: clamp(30px, 2.2vw, 34px);
10271
+ display: flex; align-items: center; justify-content: center;
10272
+ border: 1.5px solid var(--ps-border-color); border-radius: clamp(6px, 0.52vw, 10px); background: transparent;
10263
10273
  cursor: pointer; color: var(--ps-text-secondary); transition: all 0.2s;
10264
10274
  }
10265
10275
  .ps-tryon-header-icon:hover { border-color: var(--ps-accent); color: var(--ps-accent); }
10266
- .ps-tryon-header-icon svg { stroke: currentColor; fill: none; width: 0.9vw; height: 0.9vw; }
10276
+ .ps-tryon-header-icon svg { stroke: currentColor; fill: none; width: clamp(14px, 0.9vw, 16px); height: clamp(14px, 0.9vw, 16px); }
10267
10277
  .ps-tryon-close {
10268
10278
  width: 2.2vw; height: 2.2vw; display: flex; align-items: center; justify-content: center;
10269
10279
  background: none; border: none; color: var(--ps-modal-close-color, #999);
@@ -10311,7 +10321,10 @@ const STYLES$1 = `
10311
10321
  }
10312
10322
 
10313
10323
  .ps-tryon-lang-list {
10314
- max-height: min(18vw, 280px); overflow-y: auto; padding: clamp(3px, 0.31vw, 5px);
10324
+ /* max(...) picks the larger of the two values so the dropdown is tall
10325
+ enough to scroll through a handful of languages on any viewport.
10326
+ Pure min(18vw, 280px) collapsed to ~67 px on 375 px mobile. */
10327
+ max-height: max(260px, min(18vw, 280px)); overflow-y: auto; padding: clamp(3px, 0.31vw, 5px);
10315
10328
  scrollbar-width: thin; scrollbar-color: rgba(0,0,0,0.15) transparent;
10316
10329
  }
10317
10330
  .ps-tryon-lang-item {
@@ -11524,21 +11537,21 @@ const STYLES$1 = `
11524
11537
  /* Shared progress layout used inside StageCycler (desktop) and
11525
11538
  MobileScanningView — row of ring + bar + percent, same tokens. */
11526
11539
  .ps-tryon-progress-wrap {
11527
- display: flex; align-items: center; gap: 10px;
11528
- width: 100%; max-width: 320px; margin-top: 16px;
11540
+ display: flex; align-items: center; gap: 12px;
11541
+ width: 100%; max-width: 360px; margin-top: 18px;
11529
11542
  }
11530
11543
  .ps-tryon-progress-wrap .ps-tryon-progress-bar-wrap {
11531
- flex: 1; height: 4px; border-radius: 3px; overflow: hidden;
11544
+ flex: 1; height: 6px; border-radius: 4px; overflow: hidden;
11532
11545
  position: relative; background: var(--ps-border-color);
11533
11546
  }
11534
11547
  .ps-tryon-progress-wrap .ps-tryon-progress-bar-fill {
11535
11548
  height: 100%; width: 0%;
11536
11549
  background: linear-gradient(90deg, var(--ps-accent), var(--ps-accent-light));
11537
- border-radius: 3px; transition: width 0.3s ease;
11550
+ border-radius: 4px; transition: width 0.3s ease;
11538
11551
  }
11539
11552
  .ps-tryon-progress-wrap .ps-tryon-progress-pct {
11540
- font-size: 11px; font-weight: 700; color: var(--ps-accent);
11541
- min-width: 30px; text-align: right;
11553
+ font-size: 13px; font-weight: 700; color: var(--ps-accent);
11554
+ min-width: 36px; text-align: right;
11542
11555
  font-variant-numeric: tabular-nums;
11543
11556
  }
11544
11557
  .ps-tryon-progress-bar-wrap {
@@ -11566,25 +11579,25 @@ const STYLES$1 = `
11566
11579
  font-variant-numeric: tabular-nums;
11567
11580
  }
11568
11581
 
11569
- /* Circular ETA ring — 48×48 px SVG with a track + progress circle; ETA
11582
+ /* Circular ETA ring — 64×64 px SVG with a track + progress circle; ETA
11570
11583
  text centered. strokeDashoffset is driven by the ticker in
11571
11584
  PrimeStyleTryonInner, so CSS only styles the appearance. */
11572
11585
  .ps-tryon-progress-ring {
11573
- position: relative; width: 48px; height: 48px; flex: 0 0 48px;
11586
+ position: relative; width: 64px; height: 64px; flex: 0 0 64px;
11574
11587
  display: flex; align-items: center; justify-content: center;
11575
11588
  }
11576
11589
  .ps-tryon-progress-ring svg { transform: rotate(-90deg); }
11577
11590
  .ps-tryon-progress-ring-track {
11578
- fill: none; stroke: var(--ps-border-color); stroke-width: 3.5;
11591
+ fill: none; stroke: var(--ps-border-color); stroke-width: 5;
11579
11592
  }
11580
11593
  .ps-tryon-progress-ring-fill {
11581
- fill: none; stroke: var(--ps-accent); stroke-width: 3.5;
11594
+ fill: none; stroke: var(--ps-accent); stroke-width: 5;
11582
11595
  stroke-linecap: round;
11583
11596
  transition: stroke-dashoffset 0.3s ease;
11584
11597
  }
11585
11598
  .ps-tryon-progress-eta {
11586
11599
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
11587
- font-size: 10px; font-weight: 700; color: var(--ps-accent);
11600
+ font-size: 13px; font-weight: 700; color: var(--ps-accent);
11588
11601
  font-variant-numeric: tabular-nums; letter-spacing: 0.01em;
11589
11602
  pointer-events: none;
11590
11603
  }
@@ -12704,6 +12717,33 @@ const STYLES$1 = `
12704
12717
 
12705
12718
  /* ── Preserve existing previews and modal-wide overrides ── */
12706
12719
  .ps-tryon-preview { height: 320px; }
12720
+
12721
+ /* ── Drawer list (history / profiles / settings) ── */
12722
+ /* Pure vw gap (0.52vw = ~2 px on mobile) crushed list rows together. */
12723
+ .ps-tryon-drawer-list { gap: 12px !important; padding: 0 !important; }
12724
+ .ps-tryon-drawer { padding: 16px !important; }
12725
+
12726
+ /* ── Profile cards inside the sizing-profiles drawer ── */
12727
+ .ps-msp-card {
12728
+ padding: 16px !important;
12729
+ border-radius: 14px !important;
12730
+ }
12731
+ .ps-msp-card-tag { font-size: 11px !important; padding: 4px 8px !important; border-radius: 999px !important; }
12732
+ .ps-msp-card-circle { width: 72px !important; height: 72px !important; margin: 8px auto 12px !important; }
12733
+ .ps-msp-card-name { font-size: 16px !important; margin-bottom: 6px !important; }
12734
+ .ps-msp-meta-row { padding: 6px 0 !important; gap: 8px !important; }
12735
+ .ps-msp-card-meta { font-size: 12px !important; }
12736
+ .ps-msp-card-actions { gap: 8px !important; margin-top: 10px !important; }
12737
+ .ps-msp-card-select { font-size: 13px !important; padding: 10px 12px !important; border-radius: 8px !important; }
12738
+ .ps-msp-card-edit, .ps-msp-card-delete { width: 36px !important; height: 36px !important; border-radius: 8px !important; }
12739
+ .ps-msp-card-create { min-height: 120px !important; font-size: 14px !important; }
12740
+
12741
+ /* ── Language switcher dropdown ── */
12742
+ /* Default min(18vw, 280px) collapsed to ~67 px on mobile — unusable. */
12743
+ .ps-tryon-lang-list { max-height: 320px !important; }
12744
+ .ps-tryon-lang-item { padding: 10px 14px !important; gap: 10px !important; }
12745
+ .ps-tryon-lang-name { font-size: 14px !important; }
12746
+ .ps-tryon-lang-code { font-size: 11px !important; }
12707
12747
  }
12708
12748
 
12709
12749
  @keyframes ps-mobile-slide-up {
@@ -16671,6 +16711,92 @@ function ProfileDetailModal({
16671
16711
  document.body
16672
16712
  );
16673
16713
  }
16714
+ function ConfirmMeasurementsModal({
16715
+ profile,
16716
+ onProceed,
16717
+ onEdit,
16718
+ t: t2
16719
+ }) {
16720
+ const heightUnit = profile.heightUnit === "in" || profile.heightUnit === "ft" ? "in" : "cm";
16721
+ const weightUnit = profile.weightUnit === "lbs" ? "lbs" : "kg";
16722
+ const systemLabel = getUnitLabel(heightUnit);
16723
+ const formatHeight = (h) => {
16724
+ if (!h) return "—";
16725
+ if (heightUnit === "in") {
16726
+ const ft = Math.floor(h / 12);
16727
+ const inches = Math.round(h % 12);
16728
+ return `${ft}'${inches}"`;
16729
+ }
16730
+ return `${Math.round(h)} cm`;
16731
+ };
16732
+ const formatWeight = (w2) => {
16733
+ if (!w2) return "—";
16734
+ return `${Math.round(w2)} ${weightUnit}`;
16735
+ };
16736
+ const height = profile.height ?? profile.heightCm;
16737
+ const weight = profile.weight ?? profile.weightKg;
16738
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-confirm-overlay", onClick: onEdit, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-confirm-modal", onClick: (e) => e.stopPropagation(), children: [
16739
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
16740
+ "button",
16741
+ {
16742
+ type: "button",
16743
+ "aria-label": t2("Close"),
16744
+ onClick: onEdit,
16745
+ style: {
16746
+ position: "absolute",
16747
+ top: "0.75vw",
16748
+ right: "0.75vw",
16749
+ width: "1.8vw",
16750
+ height: "1.8vw",
16751
+ borderRadius: "50%",
16752
+ background: "transparent",
16753
+ border: "none",
16754
+ cursor: "pointer",
16755
+ display: "flex",
16756
+ alignItems: "center",
16757
+ justifyContent: "center",
16758
+ color: "var(--ps-text-muted)"
16759
+ },
16760
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", width: "16", height: "16", children: [
16761
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
16762
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
16763
+ ] })
16764
+ }
16765
+ ),
16766
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { style: { fontWeight: 700, marginBottom: "0.4vw" }, children: t2("Confirm your measurements") }),
16767
+ /* @__PURE__ */ jsxRuntimeExports.jsx("small", { style: { color: "var(--ps-text-muted)" }, children: systemLabel ? t2("You chose") + " " + systemLabel + ". " + t2("Review before continuing.") : t2("Review before continuing.") }),
16768
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { style: {
16769
+ listStyle: "none",
16770
+ padding: 0,
16771
+ margin: "0.8vw 0 0.2vw 0",
16772
+ width: "100%",
16773
+ textAlign: "left",
16774
+ fontSize: "0.78vw",
16775
+ lineHeight: 1.6
16776
+ }, children: [
16777
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { style: { display: "flex", justifyContent: "space-between", gap: "1vw" }, children: [
16778
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--ps-text-muted)" }, children: t2("Height") }),
16779
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600 }, children: formatHeight(height) })
16780
+ ] }),
16781
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { style: { display: "flex", justifyContent: "space-between", gap: "1vw" }, children: [
16782
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--ps-text-muted)" }, children: t2("Weight") }),
16783
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600 }, children: formatWeight(weight) })
16784
+ ] }),
16785
+ profile.age ? /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { style: { display: "flex", justifyContent: "space-between", gap: "1vw" }, children: [
16786
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--ps-text-muted)" }, children: t2("Age") }),
16787
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600 }, children: profile.age })
16788
+ ] }) : null,
16789
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { style: { display: "flex", justifyContent: "space-between", gap: "1vw" }, children: [
16790
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--ps-text-muted)" }, children: t2("Gender") }),
16791
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600 }, children: profile.gender === "female" ? t2("Female") : t2("Male") })
16792
+ ] })
16793
+ ] }),
16794
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-confirm-actions", children: [
16795
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-confirm-cancel", onClick: onEdit, children: t2("Edit") }),
16796
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-confirm-delete", onClick: onProceed, children: t2("Proceed") })
16797
+ ] })
16798
+ ] }) });
16799
+ }
16674
16800
  function WelcomeView({
16675
16801
  productImage,
16676
16802
  setView,
@@ -16907,7 +17033,7 @@ function MobileSkeleton({ landmarks, w: w2, h }) {
16907
17033
  );
16908
17034
  }
16909
17035
  const MSC_TRYON_TARGET_SECONDS = 22;
16910
- const MSC_RING_RADIUS = 20;
17036
+ const MSC_RING_RADIUS = 27;
16911
17037
  const MSC_RING_CIRC = 2 * Math.PI * MSC_RING_RADIUS;
16912
17038
  function MscTryOnProgress({ t: t2 }) {
16913
17039
  const startRef = reactExports.useRef(Date.now());
@@ -16930,17 +17056,17 @@ function MscTryOnProgress({ t: t2 }) {
16930
17056
  }
16931
17057
  }, 200);
16932
17058
  return () => clearInterval(id2);
16933
- }, [t2]);
17059
+ }, []);
16934
17060
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-wrap", children: [
16935
17061
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-ring", children: [
16936
- /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "48", height: "48", viewBox: "0 0 48 48", "aria-hidden": "true", children: [
16937
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: MSC_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
17062
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "64", height: "64", viewBox: "0 0 64 64", "aria-hidden": "true", children: [
17063
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "32", cy: "32", r: MSC_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
16938
17064
  /* @__PURE__ */ jsxRuntimeExports.jsx(
16939
17065
  "circle",
16940
17066
  {
16941
17067
  ref: ringRef,
16942
- cx: "24",
16943
- cy: "24",
17068
+ cx: "32",
17069
+ cy: "32",
16944
17070
  r: MSC_RING_RADIUS,
16945
17071
  className: "ps-tryon-progress-ring-fill",
16946
17072
  strokeDasharray: MSC_RING_CIRC,
@@ -16989,11 +17115,12 @@ function MobileScanningView({
16989
17115
  };
16990
17116
  const [stageIdx, setStageIdx] = reactExports.useState(0);
16991
17117
  reactExports.useEffect(() => {
17118
+ const intervalMs = tryOnProcessing ? 2200 : 1500;
16992
17119
  const id2 = setInterval(() => {
16993
17120
  setStageIdx((i) => (i + 1) % stages.length);
16994
- }, 1500);
17121
+ }, intervalMs);
16995
17122
  return () => clearInterval(id2);
16996
- }, [stages.length]);
17123
+ }, [stages.length, tryOnProcessing]);
16997
17124
  reactExports.useEffect(() => {
16998
17125
  if (isPhotoMode && bodyLandmarks && stageIdx === 0) {
16999
17126
  setStageIdx(1);
@@ -17150,7 +17277,7 @@ function MultiSectionMobile({
17150
17277
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msr-sections", children: sectionEntries.map(({ name, secResult }) => {
17151
17278
  const cleanName = name.replace(/\s*[—–-]\s*.*/g, "");
17152
17279
  const sec = secResult;
17153
- const sizeValue = sec.size || secResult.recommendedSize;
17280
+ const sizeValue = sec.found === false ? t2("No fit") : sec.size || secResult.recommendedSize;
17154
17281
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
17155
17282
  "button",
17156
17283
  {
@@ -17185,7 +17312,19 @@ function MultiSectionMobile({
17185
17312
  children: t2("Continue Shopping")
17186
17313
  }
17187
17314
  )
17188
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
17315
+ ] }) : sizingResult?.found === false ? (
17316
+ // Backend couldn't find a size that fits — Try-On is meaningless
17317
+ // without a recommendation, so surface a clear terminal action.
17318
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
17319
+ "button",
17320
+ {
17321
+ type: "button",
17322
+ className: "ps-msr-tryon-cta",
17323
+ onClick: onClose,
17324
+ children: t2("Continue Shopping")
17325
+ }
17326
+ )
17327
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
17189
17328
  "button",
17190
17329
  {
17191
17330
  type: "button",
@@ -17218,7 +17357,7 @@ const SKELETON_CONNECTIONS = [
17218
17357
  ["rightKnee", "rightAnkle"]
17219
17358
  ];
17220
17359
  const TRYON_TARGET_SECONDS = 22;
17221
- const TRYON_RING_RADIUS = 20;
17360
+ const TRYON_RING_RADIUS = 27;
17222
17361
  const TRYON_RING_CIRC = 2 * Math.PI * TRYON_RING_RADIUS;
17223
17362
  function TryOnProgress({ t: t2, isActive }) {
17224
17363
  const startRef = reactExports.useRef(null);
@@ -17248,18 +17387,18 @@ function TryOnProgress({ t: t2, isActive }) {
17248
17387
  }
17249
17388
  }, 200);
17250
17389
  return () => clearInterval(id2);
17251
- }, [isActive, t2]);
17390
+ }, [isActive]);
17252
17391
  if (!isActive) return null;
17253
17392
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-wrap", children: [
17254
17393
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-ring", children: [
17255
- /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "48", height: "48", viewBox: "0 0 48 48", "aria-hidden": "true", children: [
17256
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: TRYON_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
17394
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "64", height: "64", viewBox: "0 0 64 64", "aria-hidden": "true", children: [
17395
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "32", cy: "32", r: TRYON_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
17257
17396
  /* @__PURE__ */ jsxRuntimeExports.jsx(
17258
17397
  "circle",
17259
17398
  {
17260
17399
  ref: ringRef,
17261
- cx: "24",
17262
- cy: "24",
17400
+ cx: "32",
17401
+ cy: "32",
17263
17402
  r: TRYON_RING_RADIUS,
17264
17403
  className: "ps-tryon-progress-ring-fill",
17265
17404
  strokeDasharray: TRYON_RING_CIRC,
@@ -17410,11 +17549,12 @@ function StageCycler({
17410
17549
  }, [tryOnProcessing]);
17411
17550
  reactExports.useEffect(() => {
17412
17551
  if (isDone) return;
17552
+ const intervalMs = tryOnProcessing ? 2200 : 900;
17413
17553
  const id2 = setInterval(() => {
17414
17554
  setIdx((i) => Math.min(i + 1, active.length - 1));
17415
- }, 900);
17555
+ }, intervalMs);
17416
17556
  return () => clearInterval(id2);
17417
- }, [isDone, active.length]);
17557
+ }, [isDone, active.length, tryOnProcessing]);
17418
17558
  const current = active[idx] ?? active[0];
17419
17559
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage", style: { alignSelf: "center", marginTop: "auto", marginBottom: "auto" }, children: [
17420
17560
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage-slot", children: [
@@ -17661,7 +17801,8 @@ function SectionDetailView({
17661
17801
  backLabel,
17662
17802
  internationalSizes,
17663
17803
  continueLabel,
17664
- renderRaw = false
17804
+ renderRaw = false,
17805
+ sectionFound
17665
17806
  }) {
17666
17807
  const recSize = sectionResult?.recommendedSize || "";
17667
17808
  const [selectedSize, setSelectedSize] = reactExports.useState(null);
@@ -17706,7 +17847,8 @@ function SectionDetailView({
17706
17847
  const hasBadFit = details.some((d) => BAD_FIT.test(d.fit || ""));
17707
17848
  return hasBadFit ? t2("Not Recommended") : t2("Your Selection");
17708
17849
  }, [isRecommended, sectionResult, t2]);
17709
- const displaySizeLabel = selectedCountry && isRecommended && internationalSizes && internationalSizes[selectedCountry] ? internationalSizes[selectedCountry] : displaySize;
17850
+ const noFitMessage = t2("We couldn't find a size that fits for this product");
17851
+ const displaySizeLabel = sectionFound === false ? noFitMessage : selectedCountry && isRecommended && internationalSizes && internationalSizes[selectedCountry] ? internationalSizes[selectedCountry] : displaySize;
17710
17852
  const columnUnits = reactExports.useMemo(() => {
17711
17853
  const units = [];
17712
17854
  for (let i = 0; i < section.headers.length; i++) {
@@ -18560,6 +18702,7 @@ function SizeResultView({
18560
18702
  const allDone = hasPhoto ? sizingDone && tryOnDone : sizingDone;
18561
18703
  const isMobile = useIsMobile();
18562
18704
  const isAccessory = measurementType === "face" || measurementType === "head";
18705
+ const noFit = sizingResult?.found === false;
18563
18706
  const vtoExcluded = measurementType === "foot";
18564
18707
  console.log("[PS-SDK] SizeResultView render:", {
18565
18708
  hasPhoto,
@@ -18644,6 +18787,7 @@ function SizeResultView({
18644
18787
  sectionName: entry.name,
18645
18788
  section: entry.section,
18646
18789
  sectionResult: entry.secResult,
18790
+ sectionFound: entry.secResult?.found,
18647
18791
  userMeasurements: entry.userMeasurements,
18648
18792
  unitLbl,
18649
18793
  chartUnit: resultUnit,
@@ -18693,6 +18837,7 @@ function SizeResultView({
18693
18837
  sectionName: entry.name,
18694
18838
  section: entry.section,
18695
18839
  sectionResult: entry.secResult,
18840
+ sectionFound: entry.secResult?.found,
18696
18841
  userMeasurements: entry.userMeasurements,
18697
18842
  unitLbl,
18698
18843
  chartUnit: resultUnit,
@@ -18814,7 +18959,7 @@ function SizeResultView({
18814
18959
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: `ps-tryon-sr-card-v2${isLast ? " ps-full" : ""}`, onClick: () => setActiveSection(name), style: { animationDelay: `${idx * 0.07}s` }, children: [
18815
18960
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-sr-card-v2-text", children: [
18816
18961
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-sr-card-v2-label", children: name.replace(/\s*[—–-]\s*.*/g, "") }),
18817
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-sr-card-v2-value", children: sec.size || secResult.recommendedSize }),
18962
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-sr-card-v2-value", children: sec.found === false ? t2("No fit") : sec.size || secResult.recommendedSize }),
18818
18963
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-sr-card-v2-rec", children: t2("recommended") })
18819
18964
  ] }),
18820
18965
  sectionImg && /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: sectionImg, alt: name, className: "ps-tryon-sr-card-v2-img" }),
@@ -18838,7 +18983,7 @@ function SizeResultView({
18838
18983
  " →"
18839
18984
  ]
18840
18985
  }
18841
- ) : vtoExcluded ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
18986
+ ) : vtoExcluded || noFit ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
18842
18987
  "button",
18843
18988
  {
18844
18989
  className: "ps-tryon-v2-cta",
@@ -18891,6 +19036,7 @@ function SizeResultView({
18891
19036
  sectionName,
18892
19037
  section: singleSection,
18893
19038
  sectionResult: singleResult,
19039
+ sectionFound: sizingResult?.found,
18894
19040
  userMeasurements: singleUserMeasurements,
18895
19041
  unitLbl,
18896
19042
  chartUnit: resultUnit,
@@ -18898,7 +19044,7 @@ function SizeResultView({
18898
19044
  onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
18899
19045
  backLabel: t2("Back"),
18900
19046
  internationalSizes: sizingResult?.internationalSizes,
18901
- onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
19047
+ onTryOn: resultImageUrl || vtoExcluded || noFit ? void 0 : handleSingleTryOn,
18902
19048
  continueLabel: resultImageUrl ? t2("Continue Shopping") : void 0,
18903
19049
  tryOnProcessing,
18904
19050
  productImage: resultImageUrl || productImage,
@@ -18938,6 +19084,7 @@ function SizeResultView({
18938
19084
  sectionName,
18939
19085
  section: singleSection,
18940
19086
  sectionResult: singleResult,
19087
+ sectionFound: sizingResult?.found,
18941
19088
  userMeasurements: singleUserMeasurements,
18942
19089
  unitLbl,
18943
19090
  chartUnit: resultUnit,
@@ -18945,7 +19092,7 @@ function SizeResultView({
18945
19092
  onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
18946
19093
  backLabel: t2("Back"),
18947
19094
  internationalSizes: sizingResult?.internationalSizes,
18948
- onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
19095
+ onTryOn: resultImageUrl || vtoExcluded || noFit ? void 0 : handleSingleTryOn,
18949
19096
  continueLabel: resultImageUrl ? t2("Continue Shopping") : void 0,
18950
19097
  tryOnProcessing,
18951
19098
  t: t2,
@@ -19321,7 +19468,7 @@ function UploadView({
19321
19468
  }
19322
19469
  ) });
19323
19470
  }
19324
- const RING_RADIUS = 20;
19471
+ const RING_RADIUS = 27;
19325
19472
  const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
19326
19473
  function ProcessingView({
19327
19474
  previewUrl,
@@ -19366,12 +19513,12 @@ function ProcessingView({
19366
19513
  ] }),
19367
19514
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-section", children: [
19368
19515
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-ring", children: [
19369
- /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 48 48", width: "48", height: "48", "aria-hidden": "true", children: [
19516
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 64 64", width: "64", height: "64", "aria-hidden": "true", children: [
19370
19517
  /* @__PURE__ */ jsxRuntimeExports.jsx(
19371
19518
  "circle",
19372
19519
  {
19373
- cx: "24",
19374
- cy: "24",
19520
+ cx: "32",
19521
+ cy: "32",
19375
19522
  r: RING_RADIUS,
19376
19523
  className: "ps-tryon-progress-ring-track"
19377
19524
  }
@@ -19380,8 +19527,8 @@ function ProcessingView({
19380
19527
  "circle",
19381
19528
  {
19382
19529
  ref: ringCb,
19383
- cx: "24",
19384
- cy: "24",
19530
+ cx: "32",
19531
+ cy: "32",
19385
19532
  r: RING_RADIUS,
19386
19533
  className: "ps-tryon-progress-ring-fill",
19387
19534
  strokeDasharray: RING_CIRCUMFERENCE,
@@ -21951,7 +22098,7 @@ function BodyProfileView({
21951
22098
  hidePhotoOptions: hasActiveProfileWithMeasurements,
21952
22099
  onNext: hasActiveProfileWithMeasurements && onUseActiveProfile ? onUseActiveProfile : handleNext,
21953
22100
  canProceed: true,
21954
- fastPathLabel: hasActiveProfileWithMeasurements ? t2("Find My Best Fit") : void 0,
22101
+ fastPathLabel: hasActiveProfileWithMeasurements ? t2("Find My Best Fit") + (getUnitLabel(hUnit) ? ` (${getUnitLabel(hUnit)})` : "") : void 0,
21955
22102
  activeProfileName: hasActiveProfileWithMeasurements ? activeProfileName : null,
21956
22103
  onStartFresh,
21957
22104
  error,
@@ -22206,7 +22353,8 @@ function BodyProfileView({
22206
22353
  !(isMobile && step === "basics") && (() => {
22207
22354
  const useProfileFast = step === "basics" && hasActiveProfileWithMeasurements && !!onUseActiveProfile;
22208
22355
  const handleClick = useProfileFast ? onUseActiveProfile : handleNext;
22209
- const label = useProfileFast ? t2("Find My Best Fit") : isLastStep ? t2("Find My Size") : t2("Next");
22356
+ const unitSuffix = getUnitLabel(hUnit) ? ` (${getUnitLabel(hUnit)})` : "";
22357
+ const label = useProfileFast ? t2("Find My Best Fit") + unitSuffix : isLastStep ? t2("Find My Size") + unitSuffix : t2("Next");
22210
22358
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-nav", children: [
22211
22359
  step !== "basics" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ps-bp-back-btn", onClick: handleBackStep, type: "button", children: [
22212
22360
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-bp-back-arrow", children: "←" }),
@@ -22396,6 +22544,7 @@ function AccessorySizeView({
22396
22544
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bpm-bottom", children: [
22397
22545
  /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "ps-bpm-next-btn", onClick: handleManualSubmit, children: [
22398
22546
  t2("Find My Size"),
22547
+ getUnitLabel(sizingUnit) ? ` (${getUnitLabel(sizingUnit)})` : "",
22399
22548
  " ",
22400
22549
  /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRightIcon, {})
22401
22550
  ] }),
@@ -22730,6 +22879,7 @@ function AccessorySizeView({
22730
22879
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
22731
22880
  /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ps-bp-next-btn", onClick: handleManualSubmit, type: "button", children: [
22732
22881
  t2("Find My Size"),
22882
+ getUnitLabel(sizingUnit) ? ` (${getUnitLabel(sizingUnit)})` : "",
22733
22883
  " ",
22734
22884
  /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRightIcon, {})
22735
22885
  ] })
@@ -22963,6 +23113,9 @@ function PrimeStyleTryonInner({
22963
23113
  const [sizingUnit, setSizingUnit] = reactExports.useState(imperial ? "in" : "cm");
22964
23114
  const [heightUnit, setHeightUnit] = reactExports.useState(imperial ? "in" : "cm");
22965
23115
  const [weightUnit, setWeightUnit] = reactExports.useState(imperial ? "lbs" : "kg");
23116
+ reactExports.useEffect(() => {
23117
+ if (detectMeasurementType(productTitle) === "foot") setSizingUnit("cm");
23118
+ }, [productTitle]);
22966
23119
  const formRef = reactExports.useRef({});
22967
23120
  const [formGender, setFormGender] = reactExports.useState("male");
22968
23121
  const [formKey, setFormKey] = reactExports.useState(0);
@@ -23037,7 +23190,7 @@ function PrimeStyleTryonInner({
23037
23190
  { at: 75, text: t2("Refining details...") },
23038
23191
  { at: 90, text: t2("Almost there...") }
23039
23192
  ];
23040
- const RING_CIRCUMFERENCE2 = 2 * Math.PI * 20;
23193
+ const RING_CIRCUMFERENCE2 = 2 * Math.PI * 27;
23041
23194
  progressIntervalRef.current = setInterval(() => {
23042
23195
  if (completedRef.current) return;
23043
23196
  const startTs = progressStartTsRef.current || Date.now();
@@ -23215,13 +23368,10 @@ function PrimeStyleTryonInner({
23215
23368
  [activeProfileId, profiles, apiUrl, productImage, productTitle, effectiveProductId, setActiveProfileId$1]
23216
23369
  );
23217
23370
  const snapSubmitRef = reactExports.useRef(null);
23218
- const handleUseActiveProfile = reactExports.useCallback(async () => {
23219
- const p2 = profiles.find((x2) => x2.id === activeProfileId);
23220
- if (!p2) return;
23371
+ const [confirmProfile, setConfirmProfile] = reactExports.useState(null);
23372
+ const runRecommendWithProfile = reactExports.useCallback(async (p2) => {
23221
23373
  const profileHeight = p2.height ?? p2.heightCm ?? 0;
23222
23374
  const profileWeight = p2.weight ?? p2.weightKg ?? 0;
23223
- const hasIdentity = profileHeight > 0 && profileWeight > 0;
23224
- if (!hasIdentity) return;
23225
23375
  const hasStored = !!p2.measurements && Object.keys(p2.measurements).length > 0;
23226
23376
  const storedPhoto = p2.photoBase64;
23227
23377
  if (!hasStored && storedPhoto && profileHeight > 0 && snapSubmitRef.current) {
@@ -23245,9 +23395,8 @@ function PrimeStyleTryonInner({
23245
23395
  }
23246
23396
  setSizingResult(null);
23247
23397
  setSizingLoading(true);
23248
- const hasStoredMeasurements = !!p2.measurements && Object.keys(p2.measurements).length > 0;
23249
- setEstimationDone(hasStoredMeasurements);
23250
- if (hasStoredMeasurements) {
23398
+ setEstimationDone(hasStored);
23399
+ if (hasStored) {
23251
23400
  setPreviewUrl(null);
23252
23401
  setBodyLandmarks(null);
23253
23402
  }
@@ -23280,7 +23429,27 @@ function PrimeStyleTryonInner({
23280
23429
  setEstimationDone(true);
23281
23430
  }).catch(() => {
23282
23431
  }).finally(() => setSizingLoading(false));
23283
- }, [profiles, activeProfileId, effectiveProductId, productTitle, productImage, sizeGuideData, apiUrl]);
23432
+ }, [effectiveProductId, productTitle, productImage, sizeGuideData, apiUrl, previewUrl]);
23433
+ const handleUseActiveProfile = reactExports.useCallback(async () => {
23434
+ const p2 = profiles.find((x2) => x2.id === activeProfileId);
23435
+ if (!p2) return;
23436
+ const profileHeight = p2.height ?? p2.heightCm ?? 0;
23437
+ const profileWeight = p2.weight ?? p2.weightKg ?? 0;
23438
+ const hasIdentity = profileHeight > 0 && profileWeight > 0;
23439
+ if (!hasIdentity) return;
23440
+ setConfirmProfile(p2);
23441
+ }, [profiles, activeProfileId]);
23442
+ const proceedFromConfirmProfile = reactExports.useCallback(() => {
23443
+ if (!confirmProfile) return;
23444
+ const p2 = confirmProfile;
23445
+ setConfirmProfile(null);
23446
+ void runRecommendWithProfile(p2);
23447
+ }, [confirmProfile, runRecommendWithProfile]);
23448
+ const cancelFromConfirmProfile = reactExports.useCallback(() => {
23449
+ setConfirmProfile(null);
23450
+ setFormKey((k2) => k2 + 1);
23451
+ setView("body-profile");
23452
+ }, []);
23284
23453
  const applyProfileRef = reactExports.useRef(() => {
23285
23454
  });
23286
23455
  const handleOpen = reactExports.useCallback(() => {
@@ -23619,7 +23788,14 @@ function PrimeStyleTryonInner({
23619
23788
  }
23620
23789
  }, [apiUrl, sizingMethod, sizingCountry, heightUnit, weightUnit, sizingUnit, sizeGuide, productTitle, dynamicFields, persistResultToProfile]);
23621
23790
  const handleQuickEstimate = reactExports.useCallback(async (height, weight, heightUnit2, weightUnit2, gender, age, bodyType, chestProfile, midsectionProfile, hipProfile, bodyImage) => {
23622
- if (!apiRef.current) return;
23791
+ if (!apiRef.current) {
23792
+ const msg = t2("SDK not configured. Please refresh and try again.");
23793
+ console.warn("[ps-sdk] handleQuickEstimate BAILED — apiRef is null. API key not loaded.");
23794
+ setErrorMessage(msg);
23795
+ setView("error");
23796
+ onError?.({ message: msg, code: "SDK_NOT_CONFIGURED" });
23797
+ return;
23798
+ }
23623
23799
  getApiUrl(apiUrl);
23624
23800
  getApiKey();
23625
23801
  const SKIP_ESTIMATE_KEYS = /* @__PURE__ */ new Set(["weight", "weightKg", "height", "heightCm"]);
@@ -23676,7 +23852,11 @@ function PrimeStyleTryonInner({
23676
23852
  apiUrl
23677
23853
  });
23678
23854
  if (!apiRef.current || !sseRef.current) {
23855
+ const msg = t2("SDK not configured. Please refresh and try again.");
23679
23856
  console.warn("[ps-sdk] handleSnapSubmit BAILED — apiRef or sseRef is null. Check api init.");
23857
+ setErrorMessage(msg);
23858
+ setView("error");
23859
+ onError?.({ message: msg, code: "SDK_NOT_CONFIGURED" });
23680
23860
  return;
23681
23861
  }
23682
23862
  const baseUrl = getApiUrl(apiUrl);
@@ -24655,6 +24835,15 @@ function PrimeStyleTryonInner({
24655
24835
  onCancel: () => setDeleteConfirmId(null),
24656
24836
  t: t2
24657
24837
  }
24838
+ ),
24839
+ confirmProfile && /* @__PURE__ */ jsxRuntimeExports.jsx(
24840
+ ConfirmMeasurementsModal,
24841
+ {
24842
+ profile: confirmProfile,
24843
+ onProceed: proceedFromConfirmProfile,
24844
+ onEdit: cancelFromConfirmProfile,
24845
+ t: t2
24846
+ }
24658
24847
  )
24659
24848
  ] }) }),
24660
24849
  document.body