@primestyleai/tryon 3.13.0 → 3.15.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.
@@ -302,6 +302,10 @@ const en = {
302
302
  "Equivalent Sizes": "Equivalent Sizes",
303
303
  "Analyzing your size...": "Analyzing your size...",
304
304
  "Your size:": "Your size:",
305
+ "Size Chart": "Size Chart",
306
+ "tap to compare": "tap to compare",
307
+ "Comparing size": "Comparing size",
308
+ "Fit Analysis": "Fit Analysis",
305
309
  "Done": "Done",
306
310
  // ── Try-on result ───────────────────────────────────
307
311
  "Try-on result": "Try-on result",
@@ -1,5 +1,5 @@
1
- import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-25Bm_pob.js";
2
- import { P, b, T, d, r } from "./index-25Bm_pob.js";
1
+ import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-B8Dg-nJ7.js";
2
+ import { P, b, T, d, r } from "./index-B8Dg-nJ7.js";
3
3
  function detectProductImage() {
4
4
  const ogImage = document.querySelector(
5
5
  'meta[property="og:image"]'
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useMemo, useRef, useCallback } from "react";
4
- import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage, P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES } from "../index-25Bm_pob.js";
4
+ import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage, P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES } from "../index-B8Dg-nJ7.js";
5
5
  const HEADER_ALIASES = {
6
6
  // ── Size label columns (skipped during field derivation) ──
7
7
  size: "__size__",
@@ -406,6 +406,20 @@ const FALLBACK_FIELDS_MALE = [
406
406
  { key: "inseam", label: "Inseam", required: false, unit: "cm", placeholder: "e.g. 81", category: "body" },
407
407
  { key: "footLengthCm", label: "Foot length", required: false, unit: "cm", placeholder: "e.g. 27", category: "shoe" }
408
408
  ];
409
+ const SIZING_COUNTRIES = [
410
+ { code: "US", label: "United States" },
411
+ { code: "UK", label: "United Kingdom" },
412
+ { code: "EU", label: "Europe (EU)" },
413
+ { code: "FR", label: "France" },
414
+ { code: "IT", label: "Italy" },
415
+ { code: "DE", label: "Germany" },
416
+ { code: "ES", label: "Spain" },
417
+ { code: "JP", label: "Japan" },
418
+ { code: "CN", label: "China" },
419
+ { code: "KR", label: "South Korea" },
420
+ { code: "AU", label: "Australia" },
421
+ { code: "BR", label: "Brazil" }
422
+ ];
409
423
  const STEP_LABELS = ["", "Welcome", "Size", "Your Fit", "Try On"];
410
424
  const TOTAL_STEPS = 4;
411
425
  function detectLocale() {
@@ -424,6 +438,9 @@ function detectLocale() {
424
438
  function isImperial(locale) {
425
439
  return locale === "US" || locale === "UK";
426
440
  }
441
+ function cmToIn(cm) {
442
+ return +(cm / 2.54).toFixed(1);
443
+ }
427
444
  function inToCm(inches) {
428
445
  return +(inches * 2.54).toFixed(1);
429
446
  }
@@ -1322,23 +1339,21 @@ function PrimeStyleTryonInner({
1322
1339
  ")"
1323
1340
  ] }, p.id))
1324
1341
  ] }) }),
1325
- sizingMethod === "exact" && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-unit-switch", children: [
1326
- /* @__PURE__ */ jsxs("button", { className: `ps-tryon-unit-switch-btn${isCm ? " ps-active" : ""}`, onClick: () => {
1342
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
1343
+ /* @__PURE__ */ jsx("label", { children: t("Sizing region") }),
1344
+ /* @__PURE__ */ jsx("select", { className: "ps-tryon-country-select", value: sizingCountry, onChange: (e) => setSizingCountry(e.target.value), children: SIZING_COUNTRIES.map((c) => /* @__PURE__ */ jsx("option", { value: c.code, children: t(c.label) }, c.code)) })
1345
+ ] }),
1346
+ sizingMethod === "exact" && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-unit-tabs", children: [
1347
+ /* @__PURE__ */ jsx("button", { className: `ps-tryon-unit-tab${isCm ? " ps-active" : ""}`, onClick: () => {
1327
1348
  setSizingUnit("cm");
1328
1349
  setHeightUnit("cm");
1329
1350
  setWeightUnit("kg");
1330
- }, children: [
1331
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-unit-switch-label", children: t("cm") }),
1332
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-unit-switch-sub", children: "cm / kg" })
1333
- ] }),
1334
- /* @__PURE__ */ jsxs("button", { className: `ps-tryon-unit-switch-btn${!isCm ? " ps-active" : ""}`, onClick: () => {
1351
+ }, children: "CM" }),
1352
+ /* @__PURE__ */ jsx("button", { className: `ps-tryon-unit-tab${!isCm ? " ps-active" : ""}`, onClick: () => {
1335
1353
  setSizingUnit("in");
1336
1354
  setHeightUnit("ft");
1337
1355
  setWeightUnit("lbs");
1338
- }, children: [
1339
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-unit-switch-label", children: t("in") }),
1340
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-unit-switch-sub", children: "in / lbs" })
1341
- ] })
1356
+ }, children: "Inches" })
1342
1357
  ] }),
1343
1358
  sizingMethod === "exact" ? /* @__PURE__ */ jsx(Fragment, { children: (() => {
1344
1359
  const reqFields = dynamicFields.filter((f) => f.required);
@@ -1462,13 +1477,13 @@ function PrimeStyleTryonInner({
1462
1477
  }
1463
1478
  )
1464
1479
  ] }),
1465
- /* @__PURE__ */ jsxs("button", { className: "ps-tryon-submit", onClick: () => {
1480
+ /* @__PURE__ */ jsxs("button", { className: "ps-tryon-submit", onClick: async () => {
1466
1481
  if (saveToggle && saveFormName.trim()) {
1467
1482
  saveProfile(saveFormName.trim());
1468
1483
  }
1469
1484
  setSizingLoading(true);
1470
- submitSizing();
1471
1485
  setView("size-result");
1486
+ await submitSizing();
1472
1487
  }, children: [
1473
1488
  t("Get My Size"),
1474
1489
  " ",
@@ -1477,63 +1492,191 @@ function PrimeStyleTryonInner({
1477
1492
  ] }, `form-${formGender}-${sizingUnit}-${heightUnit}-${sizingCountry}-${formKey}`);
1478
1493
  }
1479
1494
  function SizeResultView() {
1480
- const [showFitDetails, setShowFitDetails] = useState(false);
1481
- const confidenceLabel = sizingResult?.confidence === "high" ? t("High Confidence") : sizingResult?.confidence === "medium" ? t("Medium Confidence") : sizingResult?.confidence === "low" ? t("Low Confidence") : "";
1482
- return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-size-result-view", children: [
1483
- sizingLoading && !sizingResult && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-size-recommend ps-tryon-sizing-loading", children: [
1495
+ const [activeSize, setActiveSize] = useState(sizingResult?.recommendedSize || "");
1496
+ const [editedValues, setEditedValues] = useState({});
1497
+ const isCmResult = sizingUnit === "cm";
1498
+ const chartData = useMemo(() => {
1499
+ if (!sizeGuide?.found || !sizeGuide.headers || !sizeGuide.rows) return null;
1500
+ const INTL = /* @__PURE__ */ new Set(["eu", "us", "uk", "it", "fr", "de", "jp", "cn", "kr", "au", "br", "eur"]);
1501
+ const MEAS_MAP = {
1502
+ chest: "chest",
1503
+ bust: "bust",
1504
+ waist: "waist",
1505
+ hips: "hips",
1506
+ hip: "hips",
1507
+ shoulder: "shoulderWidth",
1508
+ shoulders: "shoulderWidth",
1509
+ "shoulder width": "shoulderWidth",
1510
+ sleeve: "sleeveLength",
1511
+ "sleeve length": "sleeveLength",
1512
+ inseam: "inseam",
1513
+ neck: "neckCircumference",
1514
+ "neck circumference": "neckCircumference",
1515
+ foot: "footLengthCm",
1516
+ "foot length": "footLengthCm"
1517
+ };
1518
+ const sizeColIdx = sizeGuide.headers.findIndex((h) => /^size$/i.test(h.trim()));
1519
+ const idx = sizeColIdx >= 0 ? sizeColIdx : 0;
1520
+ const measCols = [];
1521
+ const intlCols = [];
1522
+ sizeGuide.headers.forEach((h, i) => {
1523
+ if (i === idx) return;
1524
+ const lower = h.toLowerCase().trim().replace(/\s*\(.*\)/, "");
1525
+ const clean = lower.replace(/\s*size\s*/gi, "").trim();
1526
+ if (INTL.has(clean)) {
1527
+ intlCols.push({ headerIdx: i, code: clean.toUpperCase() });
1528
+ } else if (MEAS_MAP[clean]) {
1529
+ measCols.push({ headerIdx: i, label: h.trim(), formKey: MEAS_MAP[clean] });
1530
+ } else if (lower !== "size") {
1531
+ measCols.push({ headerIdx: i, label: h.trim(), formKey: lower.replace(/\s+/g, "") });
1532
+ }
1533
+ });
1534
+ const sizes = sizeGuide.rows.map((row) => {
1535
+ const label = row[idx] || "";
1536
+ const measurements = {};
1537
+ for (const col of measCols) {
1538
+ const cell = row[col.headerIdx] || "";
1539
+ const nums = cell.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map(Number).filter((n) => !isNaN(n));
1540
+ if (nums.length > 0) {
1541
+ measurements[col.formKey] = { raw: cell, min: Math.min(...nums), max: Math.max(...nums) };
1542
+ }
1543
+ }
1544
+ const intl = {};
1545
+ for (const col of intlCols) {
1546
+ if (row[col.headerIdx]) intl[col.code] = row[col.headerIdx];
1547
+ }
1548
+ return { label, measurements, intl };
1549
+ });
1550
+ return { sizes, measCols, intlCols };
1551
+ }, [sizeGuide]);
1552
+ const getUserVal = useCallback((formKey2) => {
1553
+ const edited = editedValues[formKey2];
1554
+ if (edited !== void 0 && edited !== "") {
1555
+ const v2 = parseFloat(edited);
1556
+ return isNaN(v2) ? null : isCmResult ? v2 : inToCm(v2);
1557
+ }
1558
+ const raw = formRef.current[formKey2];
1559
+ if (!raw) return null;
1560
+ const v = parseFloat(raw);
1561
+ return isNaN(v) ? null : isCmResult ? v : inToCm(v);
1562
+ }, [editedValues, isCmResult]);
1563
+ const activeFit = useMemo(() => {
1564
+ if (!chartData) return null;
1565
+ const sizeRow = chartData.sizes.find((s) => s.label === activeSize);
1566
+ if (!sizeRow) return null;
1567
+ const details = [];
1568
+ for (const col of chartData.measCols) {
1569
+ const range = sizeRow.measurements[col.formKey];
1570
+ if (!range) continue;
1571
+ const userVal = getUserVal(col.formKey);
1572
+ if (userVal === null) continue;
1573
+ const fit = userVal >= range.min && userVal <= range.max ? "good" : userVal < range.min ? "tight" : "loose";
1574
+ details.push({ label: col.label, formKey: col.formKey, userVal, chartMin: range.min, chartMax: range.max, chartRaw: range.raw, fit });
1575
+ }
1576
+ return details;
1577
+ }, [chartData, activeSize, getUserVal]);
1578
+ const activeIntl = useMemo(() => {
1579
+ if (!chartData) return sizingResult?.internationalSizes || {};
1580
+ const sizeRow = chartData.sizes.find((s) => s.label === activeSize);
1581
+ return sizeRow?.intl || sizingResult?.internationalSizes || {};
1582
+ }, [chartData, activeSize, sizingResult]);
1583
+ const fmtRange = (min, max) => {
1584
+ if (isCmResult) return min === max ? `${Math.round(min)}` : `${Math.round(min)}–${Math.round(max)}`;
1585
+ return min === max ? `${cmToIn(min)}` : `${cmToIn(min)}–${cmToIn(max)}`;
1586
+ };
1587
+ const unitLabel = isCmResult ? t("cm") : t("in");
1588
+ const isRecommended = activeSize === sizingResult?.recommendedSize;
1589
+ return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr", children: [
1590
+ sizingLoading && !sizingResult && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-loading", children: [
1484
1591
  /* @__PURE__ */ jsx("div", { className: "ps-tryon-size-loading-spinner" }),
1485
- /* @__PURE__ */ jsx("p", { className: "ps-tryon-size-reasoning", children: t("Analyzing your size...") })
1592
+ /* @__PURE__ */ jsx("p", { children: t("Analyzing your size...") })
1486
1593
  ] }),
1487
1594
  sizingResult && /* @__PURE__ */ jsxs(Fragment, { children: [
1488
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-size-recommend", children: [
1489
- /* @__PURE__ */ jsx("h3", { className: "ps-tryon-size-title", children: t("Your Size") }),
1490
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-size-hero-row", children: [
1491
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-size-badge", children: sizingResult.recommendedSize }),
1492
- /* @__PURE__ */ jsx("span", { className: `ps-tryon-size-conf-label ps-conf-${sizingResult.confidence}`, children: confidenceLabel })
1493
- ] }),
1494
- sizingResult.sections && Object.keys(sizingResult.sections).length > 1 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-multi-section", children: [
1495
- /* @__PURE__ */ jsx("h4", { className: "ps-tryon-fit-summary-title", children: t("Sizing by Garment") }),
1496
- Object.entries(sizingResult.sections).map(([name, sec]) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-section-row", children: [
1497
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-section-name", children: name }),
1498
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-size-badge ps-tryon-section-badge", children: sec.recommendedSize })
1499
- ] }, name))
1500
- ] }),
1501
- sizingResult.matchDetails && sizingResult.matchDetails.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-fit-summary", children: [
1502
- /* @__PURE__ */ jsx("h4", { className: "ps-tryon-fit-summary-title", children: t("Fit Summary") }),
1503
- sizingResult.matchDetails.map((m, i) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-fit-row", children: [
1504
- /* @__PURE__ */ jsx("span", { className: `ps-tryon-fit-icon ps-fit-icon-${m.fit}`, children: m.fit === "good" ? "✓" : m.fit === "tight" ? "↑" : "↓" }),
1505
- /* @__PURE__ */ jsxs("span", { className: "ps-tryon-fit-text", children: [
1506
- /* @__PURE__ */ jsx("strong", { children: m.measurement }),
1507
- " ",
1508
- m.fit === "good" ? t("within range") : m.fit === "tight" ? t("may be snug") : t("may be loose")
1509
- ] })
1510
- ] }, i)),
1511
- /* @__PURE__ */ jsx("button", { className: "ps-tryon-fit-details-toggle", onClick: () => setShowFitDetails(!showFitDetails), children: showFitDetails ? `${t("Hide details")} ↑` : `${t("See details")} ↓` }),
1512
- showFitDetails && /* @__PURE__ */ jsxs("table", { className: "ps-tryon-fit-table", children: [
1513
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
1514
- /* @__PURE__ */ jsx("th", { children: t("Area") }),
1515
- /* @__PURE__ */ jsx("th", { children: t("You") }),
1516
- /* @__PURE__ */ jsx("th", { children: t("Chart") }),
1517
- /* @__PURE__ */ jsx("th", { children: t("Fit") })
1518
- ] }) }),
1519
- /* @__PURE__ */ jsx("tbody", { children: sizingResult.matchDetails.map((m, i) => /* @__PURE__ */ jsxs("tr", { children: [
1520
- /* @__PURE__ */ jsx("td", { children: m.measurement }),
1521
- /* @__PURE__ */ jsx("td", { children: m.userValue }),
1522
- /* @__PURE__ */ jsx("td", { children: m.chartRange }),
1523
- /* @__PURE__ */ jsx("td", { className: `ps-fit-${m.fit}`, children: m.fit })
1524
- ] }, i)) })
1595
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero", children: [
1596
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-badge", children: sizingResult.recommendedSize }),
1597
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero-info", children: [
1598
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-title", children: t("Your Size") }),
1599
+ /* @__PURE__ */ jsx("div", { className: `ps-tryon-sr-hero-conf ps-conf-${sizingResult.confidence}`, children: sizingResult.confidence === "high" ? t("High Confidence") : sizingResult.confidence === "medium" ? t("Medium Confidence") : t("Low Confidence") })
1600
+ ] })
1601
+ ] }),
1602
+ Object.keys(activeIntl).length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl", children: [
1603
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Equivalent Sizes") }),
1604
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-intl-grid", children: Object.entries(activeIntl).map(([code, val]) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl-item", children: [
1605
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-code", children: code }),
1606
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-val", children: val })
1607
+ ] }, code)) })
1608
+ ] }),
1609
+ chartData && chartData.sizes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-sizes", children: [
1610
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-label", children: [
1611
+ t("Size Chart"),
1612
+ " ",
1613
+ /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-label-hint", children: [
1614
+ " ",
1615
+ t("tap to compare")
1525
1616
  ] })
1526
1617
  ] }),
1527
- sizingResult.internationalSizes && Object.keys(sizingResult.internationalSizes).length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-equiv-section", children: [
1528
- /* @__PURE__ */ jsx("h4", { className: "ps-tryon-equiv-title", children: t("Equivalent Sizes") }),
1529
- /* @__PURE__ */ jsx("div", { className: "ps-tryon-equiv-chips", children: Object.entries(sizingResult.internationalSizes).map(([k, v]) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-equiv-chip", children: [
1530
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-equiv-region", children: k }),
1531
- /* @__PURE__ */ jsx("span", { className: "ps-tryon-equiv-value", children: v })
1532
- ] }, k)) })
1533
- ] }),
1534
- (!sizingResult.matchDetails || sizingResult.matchDetails.length === 0) && sizingResult.reasoning && /* @__PURE__ */ jsx("p", { className: "ps-tryon-size-reasoning", children: sizingResult.reasoning })
1618
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-size-row", children: chartData.sizes.map((s) => /* @__PURE__ */ jsxs(
1619
+ "button",
1620
+ {
1621
+ onClick: () => setActiveSize(s.label),
1622
+ className: `ps-tryon-sr-size-chip${s.label === activeSize ? " ps-active" : ""}${s.label === sizingResult.recommendedSize ? " ps-recommended" : ""}`,
1623
+ children: [
1624
+ s.label,
1625
+ s.label === sizingResult.recommendedSize && /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-rec-dot" })
1626
+ ]
1627
+ },
1628
+ s.label
1629
+ )) }),
1630
+ !isRecommended && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-comparing", children: [
1631
+ t("Comparing size"),
1632
+ " ",
1633
+ /* @__PURE__ */ jsx("strong", { children: activeSize })
1634
+ ] })
1535
1635
  ] }),
1536
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-tryon-cta", children: [
1636
+ activeFit && activeFit.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit", children: [
1637
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Fit Analysis") }),
1638
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-table", children: [
1639
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-header", children: [
1640
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: t("Area") }),
1641
+ /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-col-you", children: [
1642
+ t("You"),
1643
+ " (",
1644
+ unitLabel,
1645
+ ")"
1646
+ ] }),
1647
+ /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-col-chart", children: [
1648
+ t("Chart"),
1649
+ " (",
1650
+ unitLabel,
1651
+ ")"
1652
+ ] }),
1653
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: t("Fit") })
1654
+ ] }),
1655
+ activeFit.map((row, i) => /* @__PURE__ */ jsxs("div", { className: `ps-tryon-sr-fit-row ps-fit-${row.fit}`, children: [
1656
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-area", children: row.label }),
1657
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-you", children: /* @__PURE__ */ jsx(
1658
+ "input",
1659
+ {
1660
+ type: "number",
1661
+ className: "ps-tryon-sr-fit-input",
1662
+ defaultValue: isCmResult ? Math.round(row.userVal) : cmToIn(row.userVal),
1663
+ onBlur: (e) => {
1664
+ const val = e.target.value;
1665
+ setEditedValues((prev) => ({ ...prev, [row.formKey]: val }));
1666
+ }
1667
+ }
1668
+ ) }),
1669
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-chart", children: fmtRange(row.chartMin, row.chartMax) }),
1670
+ /* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: /* @__PURE__ */ jsxs("span", { className: `ps-tryon-sr-fit-badge ps-fit-${row.fit}`, children: [
1671
+ row.fit === "good" ? "✓" : row.fit === "tight" ? "↑" : "↓",
1672
+ " ",
1673
+ row.fit === "good" ? t("within range") : row.fit === "tight" ? t("may be snug") : t("may be loose")
1674
+ ] }) })
1675
+ ] }, i))
1676
+ ] })
1677
+ ] }),
1678
+ (!activeFit || activeFit.length === 0) && sizingResult.reasoning && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-reasoning", children: /* @__PURE__ */ jsx("p", { children: sizingResult.reasoning }) }),
1679
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-ctas", children: [
1537
1680
  /* @__PURE__ */ jsxs("button", { className: "ps-tryon-cta", onClick: () => setView("upload"), children: [
1538
1681
  t("See how it looks on you"),
1539
1682
  " ",
@@ -2093,27 +2236,21 @@ const STYLES = `
2093
2236
  }
2094
2237
  .ps-tryon-unit-btn.ps-active { background: #bb945c; color: #111; }
2095
2238
 
2096
- /* Prominent unit switch (cm/in) */
2097
- .ps-tryon-unit-switch {
2098
- display: flex; gap: 0.42vw; margin-bottom: 0.83vw;
2099
- }
2100
- .ps-tryon-unit-switch-btn {
2101
- flex: 1; display: flex; flex-direction: column; align-items: center; gap: 0.16vw;
2102
- padding: 0.73vw 0.57vw;
2103
- border: 2px solid #333; border-radius: 0.73vw;
2104
- background: transparent; color: #666; cursor: pointer;
2105
- transition: all 0.25s ease; font-family: inherit;
2106
- }
2107
- .ps-tryon-unit-switch-btn:hover { border-color: #555; color: #999; }
2108
- .ps-tryon-unit-switch-btn.ps-active {
2109
- border-color: #bb945c; background: rgba(187,148,92,0.08); color: #bb945c;
2110
- box-shadow: 0 0 0 0.21vw rgba(187,148,92,0.08);
2111
- }
2112
- .ps-tryon-unit-switch-label {
2113
- font-size: 1.04vw; font-weight: 700; letter-spacing: 0.02em;
2114
- }
2115
- .ps-tryon-unit-switch-sub {
2116
- font-size: 0.52vw; font-weight: 500; opacity: 0.6;
2239
+ /* Unit tabs (cm / inches) */
2240
+ .ps-tryon-unit-tabs {
2241
+ display: flex; gap: 0; background: #1a1b1a; border: 1.5px solid #333;
2242
+ border-radius: 0.63vw; overflow: hidden; margin-bottom: 0.83vw;
2243
+ }
2244
+ .ps-tryon-unit-tab {
2245
+ flex: 1; padding: 0.57vw 0.83vw; background: transparent; border: none;
2246
+ color: #666; font-size: 0.78vw; font-weight: 700; cursor: pointer;
2247
+ transition: all 0.2s ease; font-family: inherit; letter-spacing: 0.04em;
2248
+ position: relative;
2249
+ }
2250
+ .ps-tryon-unit-tab:first-child { border-right: 1px solid #333; }
2251
+ .ps-tryon-unit-tab:hover { color: #999; background: rgba(255,255,255,0.03); }
2252
+ .ps-tryon-unit-tab.ps-active {
2253
+ background: #bb945c; color: #111;
2117
2254
  }
2118
2255
  .ps-tryon-section-label { font-size: 0.57vw; font-weight: 700; color: #bb945c; text-transform: uppercase; letter-spacing: 0.08em; margin: 0.21vw 0 0.1vw; }
2119
2256
  .ps-tryon-optional-section { border-top: 1px solid #333; padding-top: 0.52vw; margin-top: 0.31vw; }
@@ -2207,79 +2344,120 @@ const STYLES = `
2207
2344
  .ps-tryon-btn-retry { background: rgba(255,255,255,0.08); color: #fff; border: 1px solid #333 !important; }
2208
2345
  .ps-tryon-btn-retry:hover { background: rgba(255,255,255,0.12); }
2209
2346
 
2210
- /* Size recommendation */
2211
- .ps-tryon-size-recommend { margin-bottom: 0.83vw; }
2212
- .ps-tryon-size-title { font-size: 0.94vw; font-weight: 700; color: #fff; margin: 0 0 0.73vw; }
2213
- .ps-tryon-size-hero-row {
2214
- display: flex; align-items: center; gap: 0.83vw; padding: 0.83vw 1.04vw;
2215
- border: 1.5px solid #333; border-radius: 0.73vw; margin-bottom: 0.94vw; background: rgba(255,255,255,0.02);
2216
- }
2217
- .ps-tryon-size-badge {
2218
- display: inline-flex; align-items: center; justify-content: center;
2219
- min-width: 2.92vw; height: 2.92vw; padding: 0 0.63vw; border-radius: 0.63vw;
2220
- background: linear-gradient(135deg, #bb945c, #d6ba7d);
2221
- color: #111; font-size: 1.25vw; font-weight: 800; letter-spacing: -0.02em;
2222
- }
2223
- .ps-tryon-size-conf-label { font-size: 0.78vw; font-weight: 600; }
2224
- .ps-conf-high { color: #4ade80; } .ps-conf-medium { color: #bb945c; } .ps-conf-low { color: #ef4444; }
2225
-
2226
- .ps-tryon-sizing-loading { text-align: center; padding: 1.04vw 0; }
2347
+ /* ── Size Result (redesigned) ── */
2348
+ .ps-tryon-sr { display: flex; flex-direction: column; gap: 1.1vw; }
2349
+ .ps-tryon-sr-loading { text-align: center; padding: 2vw 0; }
2350
+ .ps-tryon-sr-loading p { font-size: 0.83vw; color: #999; margin-top: 0.5vw; }
2227
2351
  .ps-tryon-size-loading-spinner {
2228
- width: 1.88vw; height: 1.88vw; border: 3px solid #333;
2352
+ width: 2vw; height: 2vw; border: 3px solid #333;
2229
2353
  border-top-color: #bb945c; border-radius: 50%;
2230
- animation: ps-spin 0.8s linear infinite; margin: 0 auto 0.63vw;
2354
+ animation: ps-spin 0.8s linear infinite; margin: 0 auto;
2231
2355
  }
2232
2356
  @keyframes ps-spin { to { transform: rotate(360deg); } }
2233
2357
 
2234
- /* Fit Summary */
2235
- .ps-tryon-fit-summary { margin-bottom: 0.83vw; }
2236
- .ps-tryon-fit-summary-title { font-size: 0.78vw; font-weight: 700; color: #fff; margin: 0 0 0.52vw; }
2237
- .ps-tryon-fit-row { display: flex; align-items: center; gap: 0.52vw; padding: 0.42vw 0; }
2238
- .ps-tryon-fit-icon {
2239
- width: 1.15vw; height: 1.15vw; border-radius: 50%; display: flex; align-items: center; justify-content: center;
2240
- font-size: 0.63vw; font-weight: 700; flex-shrink: 0;
2241
- }
2242
- .ps-fit-icon-good { background: rgba(74,222,128,0.15); color: #4ade80; }
2243
- .ps-fit-icon-tight { background: rgba(245,158,11,0.15); color: #f59e0b; }
2244
- .ps-fit-icon-loose { background: rgba(96,165,250,0.15); color: #60a5fa; }
2245
- .ps-tryon-fit-text { font-size: 0.73vw; color: #ccc; line-height: 1.4; }
2246
- .ps-tryon-fit-text strong { color: #fff; font-weight: 600; }
2247
- .ps-tryon-fit-details-toggle {
2248
- display: inline-block; margin-top: 0.42vw; font-size: 0.68vw; color: #bb945c; cursor: pointer;
2249
- font-weight: 600; background: none; border: none; padding: 0; font-family: inherit;
2250
- }
2251
- .ps-tryon-fit-details-toggle:hover { color: #d6ba7d; }
2252
- .ps-tryon-fit-table { width: 100%; border-collapse: collapse; margin-top: 0.52vw; font-size: 0.68vw; }
2253
- .ps-tryon-fit-table th { text-align: left; padding: 0.42vw 0.52vw; border-bottom: 1px solid #333; color: #999; font-weight: 600; }
2254
- .ps-tryon-fit-table td { padding: 0.42vw 0.52vw; border-bottom: 1px solid #222; color: #ccc; }
2255
- .ps-fit-good { color: #4ade80; } .ps-fit-tight { color: #f59e0b; } .ps-fit-loose { color: #60a5fa; }
2358
+ /* Hero */
2359
+ .ps-tryon-sr-hero {
2360
+ display: flex; align-items: center; gap: 1vw; padding: 1vw 1.2vw;
2361
+ background: linear-gradient(135deg, rgba(187,148,92,0.08), rgba(187,148,92,0.02));
2362
+ border: 1.5px solid rgba(187,148,92,0.25); border-radius: 0.83vw;
2363
+ }
2364
+ .ps-tryon-sr-hero-badge {
2365
+ min-width: 3.5vw; height: 3.5vw; display: flex; align-items: center; justify-content: center;
2366
+ background: linear-gradient(135deg, #bb945c, #d6ba7d); color: #111;
2367
+ font-size: 1.5vw; font-weight: 800; border-radius: 0.73vw; letter-spacing: -0.02em;
2368
+ padding: 0 0.8vw; box-shadow: 0 0.4vw 1.2vw rgba(187,148,92,0.25);
2369
+ }
2370
+ .ps-tryon-sr-hero-info { flex: 1; }
2371
+ .ps-tryon-sr-hero-title { font-size: 1.04vw; font-weight: 700; color: #fff; margin-bottom: 0.15vw; }
2372
+ .ps-tryon-sr-hero-conf { font-size: 0.78vw; font-weight: 600; }
2373
+ .ps-conf-high { color: #4ade80; } .ps-conf-medium { color: #bb945c; } .ps-conf-low { color: #ef4444; }
2256
2374
 
2257
- /* Equivalent Sizes */
2258
- .ps-tryon-equiv-section { margin-bottom: 0.83vw; }
2259
- .ps-tryon-equiv-title { font-size: 0.78vw; font-weight: 700; color: #fff; margin: 0 0 0.52vw; }
2260
- .ps-tryon-equiv-chips { display: flex; flex-wrap: wrap; gap: 0.42vw; }
2261
- .ps-tryon-equiv-chip {
2375
+ /* International sizes */
2376
+ .ps-tryon-sr-intl { }
2377
+ .ps-tryon-sr-label { font-size: 0.78vw; font-weight: 700; color: #999; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.52vw; }
2378
+ .ps-tryon-sr-label-hint { font-weight: 400; text-transform: none; letter-spacing: 0; color: #666; font-style: italic; }
2379
+ .ps-tryon-sr-intl-grid { display: flex; flex-wrap: wrap; gap: 0.42vw; }
2380
+ .ps-tryon-sr-intl-item {
2262
2381
  display: flex; align-items: center; border: 1.5px solid #333; border-radius: 0.52vw; overflow: hidden;
2263
2382
  }
2264
- .ps-tryon-equiv-region { padding: 0.36vw 0.52vw; font-size: 0.63vw; color: #999; font-weight: 600; background: rgba(255,255,255,0.03); }
2265
- .ps-tryon-equiv-value { padding: 0.36vw 0.63vw; font-size: 0.73vw; color: #fff; font-weight: 700; }
2383
+ .ps-tryon-sr-intl-code { padding: 0.42vw 0.57vw; font-size: 0.68vw; color: #999; font-weight: 700; background: rgba(255,255,255,0.03); letter-spacing: 0.04em; }
2384
+ .ps-tryon-sr-intl-val { padding: 0.42vw 0.68vw; font-size: 0.83vw; color: #fff; font-weight: 700; }
2266
2385
 
2267
- /* Multi-section garment sizing (tuxedo/set) */
2268
- .ps-tryon-multi-section { margin-bottom: 0.83vw; }
2269
- .ps-tryon-section-row { display: flex; align-items: center; justify-content: space-between; padding: 0.52vw 0; border-bottom: 1px solid #222; }
2270
- .ps-tryon-section-row:last-child { border-bottom: none; }
2271
- .ps-tryon-section-name { font-size: 0.78vw; color: #ccc; font-weight: 500; }
2272
- .ps-tryon-section-badge { font-size: 0.83vw; min-width: auto; padding: 0.26vw 0.83vw; }
2386
+ /* Size selector */
2387
+ .ps-tryon-sr-sizes { }
2388
+ .ps-tryon-sr-size-row { display: flex; flex-wrap: wrap; gap: 0.36vw; }
2389
+ .ps-tryon-sr-size-chip {
2390
+ position: relative; min-width: 2.6vw; height: 2.4vw; padding: 0 0.73vw;
2391
+ display: flex; align-items: center; justify-content: center;
2392
+ border: 1.5px solid #333; border-radius: 0.52vw; background: transparent;
2393
+ color: #999; font-size: 0.83vw; font-weight: 600; cursor: pointer;
2394
+ transition: all 0.2s; font-family: inherit;
2395
+ }
2396
+ .ps-tryon-sr-size-chip:hover { border-color: #555; color: #ccc; }
2397
+ .ps-tryon-sr-size-chip.ps-active {
2398
+ border-color: #bb945c; background: #bb945c; color: #111;
2399
+ box-shadow: 0 0.2vw 0.8vw rgba(187,148,92,0.2);
2400
+ }
2401
+ .ps-tryon-sr-size-chip.ps-recommended { border-color: rgba(187,148,92,0.4); }
2402
+ .ps-tryon-sr-rec-dot {
2403
+ position: absolute; bottom: -0.26vw; left: 50%; transform: translateX(-50%);
2404
+ width: 0.31vw; height: 0.31vw; border-radius: 50%; background: #bb945c;
2405
+ }
2406
+ .ps-tryon-sr-size-chip.ps-active .ps-tryon-sr-rec-dot { background: #111; }
2407
+ .ps-tryon-sr-comparing { font-size: 0.73vw; color: #bb945c; margin-top: 0.42vw; }
2408
+ .ps-tryon-sr-comparing strong { color: #d6ba7d; }
2273
2409
 
2274
- .ps-tryon-size-reasoning { font-size: 0.73vw; color: #ccc; line-height: 1.6; margin-bottom: 0.73vw; }
2410
+ /* Fit analysis table */
2411
+ .ps-tryon-sr-fit { }
2412
+ .ps-tryon-sr-fit-table { border: 1.5px solid #333; border-radius: 0.73vw; overflow: hidden; }
2413
+ .ps-tryon-sr-fit-header {
2414
+ display: flex; padding: 0.57vw 0.83vw; background: #1a1b1a; border-bottom: 1px solid #333;
2415
+ }
2416
+ .ps-tryon-sr-fit-header span {
2417
+ font-size: 0.68vw; font-weight: 700; color: #666; text-transform: uppercase; letter-spacing: 0.06em;
2418
+ }
2419
+ .ps-tryon-sr-fit-row {
2420
+ display: flex; align-items: center; padding: 0.57vw 0.83vw;
2421
+ border-bottom: 1px solid #222; transition: background 0.15s;
2422
+ }
2423
+ .ps-tryon-sr-fit-row:last-child { border-bottom: none; }
2424
+ .ps-tryon-sr-fit-row:hover { background: rgba(255,255,255,0.02); }
2425
+ .ps-tryon-sr-fit-row.ps-fit-good { border-left: 3px solid #4ade80; }
2426
+ .ps-tryon-sr-fit-row.ps-fit-tight { border-left: 3px solid #f59e0b; }
2427
+ .ps-tryon-sr-fit-row.ps-fit-loose { border-left: 3px solid #60a5fa; }
2275
2428
 
2276
- /* Size Result View (standalone sizing result before try-on) */
2277
- .ps-tryon-size-result-view { display: flex; flex-direction: column; gap: 1vw; }
2278
- .ps-tryon-tryon-cta { display: flex; flex-direction: column; gap: 0.7vw; margin-top: 0.7vw; }
2429
+ .ps-tryon-sr-fit-col-area { flex: 0 0 28%; font-size: 0.78vw; font-weight: 600; color: #fff; }
2430
+ .ps-tryon-sr-fit-col-you { flex: 0 0 22%; }
2431
+ .ps-tryon-sr-fit-col-chart { flex: 0 0 22%; font-size: 0.78vw; color: #999; font-weight: 500; }
2432
+ .ps-tryon-sr-fit-col-fit { flex: 1; text-align: right; }
2433
+
2434
+ .ps-tryon-sr-fit-input {
2435
+ width: 3.5vw; padding: 0.26vw 0.42vw; border: 1.5px solid #333; border-radius: 0.36vw;
2436
+ background: #111211; color: #fff; font-size: 0.78vw; font-weight: 600; font-family: inherit;
2437
+ outline: none; text-align: center; -moz-appearance: textfield;
2438
+ }
2439
+ .ps-tryon-sr-fit-input::-webkit-outer-spin-button,
2440
+ .ps-tryon-sr-fit-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
2441
+ .ps-tryon-sr-fit-input:focus { border-color: #bb945c; }
2442
+
2443
+ .ps-tryon-sr-fit-badge {
2444
+ display: inline-flex; align-items: center; gap: 0.26vw; padding: 0.21vw 0.52vw;
2445
+ border-radius: 0.36vw; font-size: 0.63vw; font-weight: 700; white-space: nowrap;
2446
+ }
2447
+ .ps-tryon-sr-fit-badge.ps-fit-good { background: rgba(74,222,128,0.1); color: #4ade80; }
2448
+ .ps-tryon-sr-fit-badge.ps-fit-tight { background: rgba(245,158,11,0.1); color: #f59e0b; }
2449
+ .ps-tryon-sr-fit-badge.ps-fit-loose { background: rgba(96,165,250,0.1); color: #60a5fa; }
2450
+
2451
+ /* Reasoning fallback */
2452
+ .ps-tryon-sr-reasoning { padding: 0.83vw; border: 1px solid #333; border-radius: 0.63vw; background: #1a1b1a; }
2453
+ .ps-tryon-sr-reasoning p { font-size: 0.78vw; color: #ccc; line-height: 1.6; margin: 0; }
2454
+
2455
+ /* CTAs */
2456
+ .ps-tryon-sr-ctas { display: flex; flex-direction: column; gap: 0.52vw; margin-top: 0.36vw; }
2279
2457
  .ps-tryon-btn-secondary {
2280
2458
  background: transparent; border: 1.5px solid #444; color: #999; font-size: 0.83vw;
2281
- font-weight: 600; padding: 0.7vw 1.3vw; border-radius: 0.52vw;
2282
- cursor: pointer; transition: all 0.2s;
2459
+ font-weight: 600; padding: 0.73vw 1.3vw; border-radius: 0.52vw;
2460
+ cursor: pointer; transition: all 0.2s; font-family: inherit;
2283
2461
  }
2284
2462
  .ps-tryon-btn-secondary:hover { border-color: #666; color: #ccc; }
2285
2463
  .ps-tryon-size-compact { padding: 0.5vw 0; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primestyleai/tryon",
3
- "version": "3.13.0",
3
+ "version": "3.15.0",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",