@lime-bundles/react 6.0.0 → 6.2.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.
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  BUNDLES_FOR_PRODUCT_QUERY: () => import_core18.BUNDLES_FOR_PRODUCT_QUERY,
24
24
  BUNDLE_METAOBJECT_QUERY: () => import_core18.BUNDLE_METAOBJECT_QUERY,
25
25
  BogoBundle: () => BogoBundle,
26
+ BundleCountdown: () => BundleCountdown,
26
27
  BundleParseError: () => import_core17.BundleParseError,
27
28
  DEFAULT_OUT_OF_STOCK_BEHAVIOR: () => import_core18.DEFAULT_OUT_OF_STOCK_BEHAVIOR,
28
29
  FixedBundle: () => FixedBundle,
@@ -75,7 +76,7 @@ __export(index_exports, {
75
76
  module.exports = __toCommonJS(index_exports);
76
77
 
77
78
  // src/components/FixedBundle.tsx
78
- var import_react7 = require("react");
79
+ var import_react8 = require("react");
79
80
  var import_core8 = require("@lime-bundles/core");
80
81
 
81
82
  // src/hooks/useBundleData.ts
@@ -702,8 +703,48 @@ function VariantDropdown({
702
703
  );
703
704
  }
704
705
 
705
- // src/components/FixedBundle.tsx
706
+ // src/components/BundleCountdown.tsx
707
+ var import_react7 = require("react");
706
708
  var import_jsx_runtime2 = require("react/jsx-runtime");
709
+ function pad(n) {
710
+ return n < 10 ? "0" + n : String(n);
711
+ }
712
+ function formatRemaining(remaining) {
713
+ const days = Math.floor(remaining / 864e5);
714
+ const hours = Math.floor(remaining % 864e5 / 36e5);
715
+ const mins = Math.floor(remaining % 36e5 / 6e4);
716
+ const secs = Math.floor(remaining % 6e4 / 1e3);
717
+ return days > 0 ? days + "d " + pad(hours) + "h " + pad(mins) + "m " + pad(secs) + "s" : pad(hours) + "h " + pad(mins) + "m " + pad(secs) + "s";
718
+ }
719
+ function BundleCountdown({
720
+ endsAt,
721
+ widgetConfig,
722
+ label = "Limited time offer"
723
+ }) {
724
+ const enabled = !!endsAt && widgetConfig?.countdown?.showCountdown !== false;
725
+ const endTime = enabled ? new Date(endsAt).getTime() : NaN;
726
+ const [remaining, setRemaining] = (0, import_react7.useState)(null);
727
+ (0, import_react7.useEffect)(() => {
728
+ if (!enabled || isNaN(endTime)) {
729
+ setRemaining(null);
730
+ return;
731
+ }
732
+ const update = () => setRemaining(endTime - Date.now());
733
+ update();
734
+ const interval = setInterval(update, 1e3);
735
+ return () => clearInterval(interval);
736
+ }, [enabled, endTime]);
737
+ if (!enabled || isNaN(endTime) || remaining === null || remaining <= 0) {
738
+ return null;
739
+ }
740
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lb-bundle-countdown", "data-countdown": true, children: [
741
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-bundle-countdown__label", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: label }) }),
742
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "lb-bundle-countdown__timer", "data-countdown-timer": true, children: formatRemaining(remaining) })
743
+ ] });
744
+ }
745
+
746
+ // src/components/FixedBundle.tsx
747
+ var import_jsx_runtime3 = require("react/jsx-runtime");
707
748
  function FixedBundle(props) {
708
749
  const {
709
750
  shopDomain,
@@ -727,10 +768,10 @@ function FixedBundle(props) {
727
768
  bundleType: "fixed",
728
769
  enabled: analyticsEnabled !== false
729
770
  });
730
- const [addingToCart, setAddingToCart] = (0, import_react7.useState)(false);
731
- const [cartError, setCartError] = (0, import_react7.useState)(null);
732
- const [selectedVariants, setSelectedVariants] = (0, import_react7.useState)({});
733
- const handleVariantChange = (0, import_react7.useCallback)(
771
+ const [addingToCart, setAddingToCart] = (0, import_react8.useState)(false);
772
+ const [cartError, setCartError] = (0, import_react8.useState)(null);
773
+ const [selectedVariants, setSelectedVariants] = (0, import_react8.useState)({});
774
+ const handleVariantChange = (0, import_react8.useCallback)(
734
775
  (productId, variant) => {
735
776
  setSelectedVariants((prev) => {
736
777
  if (prev[productId] === variant) return prev;
@@ -742,7 +783,7 @@ function FixedBundle(props) {
742
783
  const bundle = result.status === "success" && result.bundle.bundleType === "fixed" ? result.bundle : null;
743
784
  const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
744
785
  const productsEdgeFadeRef = useEdgeFade();
745
- (0, import_react7.useEffect)(() => {
786
+ (0, import_react8.useEffect)(() => {
746
787
  if (result.status === "error") {
747
788
  onError?.(result.error);
748
789
  return;
@@ -755,7 +796,7 @@ function FixedBundle(props) {
755
796
  );
756
797
  }
757
798
  }, [result, onError]);
758
- const oosProductIds = (0, import_react7.useMemo)(() => {
799
+ const oosProductIds = (0, import_react8.useMemo)(() => {
759
800
  if (!bundle) return /* @__PURE__ */ new Set();
760
801
  return new Set(
761
802
  bundle.products.filter(
@@ -765,7 +806,7 @@ function FixedBundle(props) {
765
806
  ).map((p) => p.id)
766
807
  );
767
808
  }, [bundle]);
768
- const handleAddToCart = (0, import_react7.useCallback)(async () => {
809
+ const handleAddToCart = (0, import_react8.useCallback)(async () => {
769
810
  if (!bundle) return;
770
811
  const bundleLines = bundle.products.filter(
771
812
  (p) => p.variants.nodes.some(
@@ -820,9 +861,9 @@ function FixedBundle(props) {
820
861
  }
821
862
  }, [bundle, onAddToCart, onError, trackAddToCart, selectedVariants]);
822
863
  if (result.status === "loading") {
823
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
824
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
825
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
864
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
865
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
866
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
826
867
  ] });
827
868
  }
828
869
  if (result.status === "error") return null;
@@ -849,7 +890,7 @@ function FixedBundle(props) {
849
890
  const savingsPercent = comparePrice > 0 && savings > 0 ? Math.round(savings / comparePrice * 100) : 0;
850
891
  const showCompare = bundle.widgetConfig.pricing.showComparePrice && savings > 0;
851
892
  const showSavings = bundle.widgetConfig.savingsBar.visible && savings > 0;
852
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
893
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
853
894
  "div",
854
895
  {
855
896
  ref: (el) => {
@@ -860,11 +901,18 @@ function FixedBundle(props) {
860
901
  role: "region",
861
902
  "aria-label": bundle.title,
862
903
  children: [
863
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lb-bundle-header__content", children: [
864
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
865
- bundle.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
904
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-bundle-header__content", children: [
905
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
906
+ bundle.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
866
907
  ] }) }),
867
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-bundle__products lb-edge-fade", ref: productsEdgeFadeRef, children: bundle.products.map((product) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
908
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
909
+ BundleCountdown,
910
+ {
911
+ endsAt: bundle.endsAt,
912
+ widgetConfig: bundle.widgetConfig
913
+ }
914
+ ),
915
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-bundle__products lb-edge-fade", ref: productsEdgeFadeRef, children: bundle.products.map((product) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
868
916
  FixedProductRow,
869
917
  {
870
918
  bundle,
@@ -875,29 +923,29 @@ function FixedBundle(props) {
875
923
  },
876
924
  product.id
877
925
  )) }),
878
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-bundle-divider" }),
879
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lb-bundle-summary", children: [
880
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lb-bundle-summary__text", children: [
881
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "lb-bundle-summary__label", children: "Bundle price" }),
882
- showSavings && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-bundle-divider" }),
927
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-bundle-summary", children: [
928
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-bundle-summary__text", children: [
929
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-bundle-summary__label", children: "Bundle total" }),
930
+ showSavings && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
883
931
  "You save",
884
932
  " ",
885
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "data-savings-amount": true, children: (0, import_core8.formatMoney)(savings, currency) }),
933
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "data-savings-amount": true, children: (0, import_core8.formatMoney)(savings, currency) }),
886
934
  " ",
887
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { "data-savings-percent": true, children: [
935
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { "data-savings-percent": true, children: [
888
936
  "(",
889
937
  savingsPercent,
890
938
  "%)"
891
939
  ] })
892
940
  ] })
893
941
  ] }),
894
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
895
- showCompare && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core8.formatMoney)(comparePrice, currency) }),
896
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core8.formatMoney)(salePrice, currency) })
942
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
943
+ showCompare && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core8.formatMoney)(comparePrice, currency) }),
944
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core8.formatMoney)(salePrice, currency) })
897
945
  ] })
898
946
  ] }),
899
- cartError && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
900
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
947
+ cartError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
948
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
901
949
  "button",
902
950
  {
903
951
  className: "lb-bundle__cta",
@@ -913,7 +961,7 @@ function FixedBundle(props) {
913
961
  }
914
962
  function FixedProductRow({ bundle, product, currency, isOos, onVariantChange }) {
915
963
  const variants = product.variants.nodes;
916
- const optionNames = (0, import_react7.useMemo)(() => {
964
+ const optionNames = (0, import_react8.useMemo)(() => {
917
965
  const first = variants[0];
918
966
  return first ? first.selectedOptions.map((o) => o.name) : [];
919
967
  }, [variants]);
@@ -922,7 +970,7 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
922
970
  variants,
923
971
  optionNames
924
972
  });
925
- (0, import_react7.useEffect)(() => {
973
+ (0, import_react8.useEffect)(() => {
926
974
  onVariantChange(product.id, selectedVariant ?? null);
927
975
  }, [selectedVariant, product.id, onVariantChange]);
928
976
  const displayVariant = selectedVariant ?? variants.find(
@@ -943,15 +991,15 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
943
991
  bundle.widgetConfig.lowStockThreshold,
944
992
  bundle.widgetConfig.showLowStockBadge
945
993
  );
946
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
994
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
947
995
  "div",
948
996
  {
949
997
  className: isOos ? "lb-bundle-product-row lb-bundle-product-row--oos" : "lb-bundle-product-row",
950
998
  "aria-disabled": isOos || void 0,
951
999
  part: "product",
952
1000
  children: [
953
- isOos && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "lb-bundle-product-row__oos", children: "Out of stock" }),
954
- thumbImage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1001
+ isOos && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-bundle-product-row__oos", children: "Out of stock" }),
1002
+ thumbImage && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
955
1003
  "img",
956
1004
  {
957
1005
  src: thumbImage.url,
@@ -960,11 +1008,11 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
960
1008
  loading: "lazy"
961
1009
  }
962
1010
  ),
963
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "lb-bundle__product-info", children: [
964
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
965
- !showPicker && displayVariant && variants.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "lb-bundle-variant-badge", children: displayVariant.title }),
966
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "lb-bundle-product-prices", children: [
967
- compareAt && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1011
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-bundle__product-info", children: [
1012
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
1013
+ !showPicker && displayVariant && variants.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-bundle-variant-badge", children: displayVariant.title }),
1014
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "lb-bundle-product-prices", children: [
1015
+ compareAt && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
968
1016
  "span",
969
1017
  {
970
1018
  className: "lb-bundle-product-compare-price",
@@ -972,9 +1020,9 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
972
1020
  children: compareAt
973
1021
  }
974
1022
  ),
975
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "lb-bundle__product-price", "data-product-price": true, children: priceText })
1023
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-bundle__product-price", "data-product-price": true, children: priceText })
976
1024
  ] }),
977
- unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1025
+ unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
978
1026
  "span",
979
1027
  {
980
1028
  className: "lb-bundle__product-unit-price",
@@ -982,18 +1030,18 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
982
1030
  children: unitPriceText
983
1031
  }
984
1032
  ),
985
- showLowStock && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "lb-bundle-low-stock-badge", children: [
1033
+ showLowStock && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "lb-bundle-low-stock-badge", children: [
986
1034
  "Only ",
987
1035
  displayVariant.quantityAvailable,
988
1036
  " left"
989
1037
  ] }),
990
- showPicker && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
1038
+ showPicker && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
991
1039
  const dropdownOptions = optionsFor(optionIndex).map((o) => ({
992
1040
  value: o.value,
993
1041
  label: o.value,
994
1042
  disabled: o.disabled
995
1043
  }));
996
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1044
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
997
1045
  VariantDropdown,
998
1046
  {
999
1047
  options: dropdownOptions,
@@ -1005,7 +1053,7 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
1005
1053
  );
1006
1054
  }) })
1007
1055
  ] }),
1008
- displayVariant && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "lb-bundle-qty-chip", "data-qty-inline": true, children: [
1056
+ displayVariant && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "lb-bundle-qty-chip", "data-qty-inline": true, children: [
1009
1057
  "\xD7",
1010
1058
  displayQty
1011
1059
  ] })
@@ -1015,16 +1063,16 @@ function FixedProductRow({ bundle, product, currency, isOos, onVariantChange })
1015
1063
  }
1016
1064
 
1017
1065
  // src/components/MixMatchBundle.tsx
1018
- var import_react12 = require("react");
1066
+ var import_react13 = require("react");
1019
1067
  var import_core11 = require("@lime-bundles/core");
1020
1068
 
1021
1069
  // src/hooks/useShopSettings.ts
1022
- var import_react8 = require("react");
1070
+ var import_react9 = require("react");
1023
1071
  var import_core9 = require("@lime-bundles/core");
1024
1072
  function useShopSettings(options) {
1025
- const [outOfStockBehavior, setOutOfStockBehavior] = (0, import_react8.useState)(import_core9.DEFAULT_OUT_OF_STOCK_BEHAVIOR);
1073
+ const [outOfStockBehavior, setOutOfStockBehavior] = (0, import_react9.useState)(import_core9.DEFAULT_OUT_OF_STOCK_BEHAVIOR);
1026
1074
  const { shopDomain, storefrontAccessToken } = options;
1027
- (0, import_react8.useEffect)(() => {
1075
+ (0, import_react9.useEffect)(() => {
1028
1076
  const controller = new AbortController();
1029
1077
  let active = true;
1030
1078
  fetchShopSettingsOrDefault({
@@ -1044,12 +1092,12 @@ function useShopSettings(options) {
1044
1092
  }
1045
1093
 
1046
1094
  // src/components/MixMatchPicker.tsx
1047
- var import_react11 = require("react");
1095
+ var import_react12 = require("react");
1048
1096
  var import_react_dom = require("react-dom");
1049
1097
  var import_core10 = require("@lime-bundles/core");
1050
1098
 
1051
1099
  // src/hooks/useScrollLock.ts
1052
- var import_react9 = require("react");
1100
+ var import_react10 = require("react");
1053
1101
  var lockCount = 0;
1054
1102
  var savedY = 0;
1055
1103
  function lock() {
@@ -1078,7 +1126,7 @@ function unlock() {
1078
1126
  window.scrollTo(0, savedY);
1079
1127
  }
1080
1128
  function useScrollLock(active) {
1081
- (0, import_react9.useEffect)(() => {
1129
+ (0, import_react10.useEffect)(() => {
1082
1130
  if (!active) return;
1083
1131
  lock();
1084
1132
  return unlock;
@@ -1086,7 +1134,7 @@ function useScrollLock(active) {
1086
1134
  }
1087
1135
 
1088
1136
  // src/hooks/useFocusTrap.ts
1089
- var import_react10 = require("react");
1137
+ var import_react11 = require("react");
1090
1138
  var FOCUSABLE_SELECTOR = [
1091
1139
  "a[href]",
1092
1140
  "button:not([disabled])",
@@ -1111,8 +1159,8 @@ function useFocusTrap({
1111
1159
  onEscape,
1112
1160
  returnFocusRef
1113
1161
  }) {
1114
- const previouslyFocused = (0, import_react10.useRef)(null);
1115
- (0, import_react10.useEffect)(() => {
1162
+ const previouslyFocused = (0, import_react11.useRef)(null);
1163
+ (0, import_react11.useEffect)(() => {
1116
1164
  if (!active) return;
1117
1165
  const container = containerRef.current;
1118
1166
  previouslyFocused.current = document.activeElement ?? null;
@@ -1154,10 +1202,17 @@ function useFocusTrap({
1154
1202
  }
1155
1203
 
1156
1204
  // src/components/MixMatchPicker.tsx
1157
- var import_jsx_runtime3 = require("react/jsx-runtime");
1158
- function ruleFor(bundle, productId) {
1205
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1206
+ function ruleFor(bundle, productId, variantId) {
1207
+ if (variantId) {
1208
+ const vRule = bundle.variantRules[variantId];
1209
+ if (vRule) return vRule;
1210
+ }
1159
1211
  return bundle.productRules[productId] ?? import_core10.DEFAULT_PRODUCT_RULE;
1160
1212
  }
1213
+ function variantRuleFor(bundle, variantId) {
1214
+ return variantId ? bundle.variantRules[variantId] : void 0;
1215
+ }
1161
1216
  function sanitizeId(gid) {
1162
1217
  return gid.replace(/[^a-zA-Z0-9_-]/g, "-");
1163
1218
  }
@@ -1203,17 +1258,17 @@ function MixMatchPicker({
1203
1258
  styleVarsRef
1204
1259
  }) {
1205
1260
  const wc = bundle.widgetConfig;
1206
- const overlayRef = (0, import_react11.useRef)(null);
1207
- const dialogRef = (0, import_react11.useRef)(null);
1208
- const mouseDownTarget = (0, import_react11.useRef)(null);
1209
- const [open, setOpen] = (0, import_react11.useState)(false);
1210
- const [query, setQuery] = (0, import_react11.useState)("");
1211
- const [activeType, setActiveType] = (0, import_react11.useState)(FILTERS_ALL);
1212
- const eligible = (0, import_react11.useMemo)(
1261
+ const overlayRef = (0, import_react12.useRef)(null);
1262
+ const dialogRef = (0, import_react12.useRef)(null);
1263
+ const mouseDownTarget = (0, import_react12.useRef)(null);
1264
+ const [open, setOpen] = (0, import_react12.useState)(false);
1265
+ const [query, setQuery] = (0, import_react12.useState)("");
1266
+ const [activeType, setActiveType] = (0, import_react12.useState)(FILTERS_ALL);
1267
+ const eligible = (0, import_react12.useMemo)(
1213
1268
  () => buildEligibleProducts(bundle, outOfStockBehavior),
1214
1269
  [bundle, outOfStockBehavior]
1215
1270
  );
1216
- const productTypes = (0, import_react11.useMemo)(() => {
1271
+ const productTypes = (0, import_react12.useMemo)(() => {
1217
1272
  const types = [];
1218
1273
  const seen = /* @__PURE__ */ new Set();
1219
1274
  for (const ep of eligible) {
@@ -1229,7 +1284,7 @@ function MixMatchPicker({
1229
1284
  const totalUnits = selections.reduce((sum, s) => sum + s.quantity, 0);
1230
1285
  const atCapacity = totalUnits >= requiredQty;
1231
1286
  const shownUnits = Math.min(totalUnits, requiredQty);
1232
- (0, import_react11.useEffect)(() => {
1287
+ (0, import_react12.useEffect)(() => {
1233
1288
  const id = requestAnimationFrame(() => setOpen(true));
1234
1289
  return () => cancelAnimationFrame(id);
1235
1290
  }, []);
@@ -1245,7 +1300,7 @@ function MixMatchPicker({
1245
1300
  const pct = bundle.discountConfig.discountType === "percentage" && bundle.discountConfig.discountValue ? Math.round(bundle.discountConfig.discountValue) : 0;
1246
1301
  return pct > 0 ? `Choose ${remaining} more to unlock ${pct}% off` : `Choose ${remaining} more to complete your bundle`;
1247
1302
  })();
1248
- const filtered = (0, import_react11.useMemo)(() => {
1303
+ const filtered = (0, import_react12.useMemo)(() => {
1249
1304
  const nq = normalizeText(query.trim());
1250
1305
  return eligible.filter((ep) => {
1251
1306
  const matchesQuery = !nq || normalizeText(ep.product.title).includes(nq);
@@ -1270,7 +1325,7 @@ function MixMatchPicker({
1270
1325
  // inside carries the interactive role + focus trap. So the static-element
1271
1326
  // interaction lint does not apply to this backdrop.
1272
1327
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
1273
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1328
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1274
1329
  "div",
1275
1330
  {
1276
1331
  ref: (el) => {
@@ -1282,7 +1337,7 @@ function MixMatchPicker({
1282
1337
  "data-bundle-gid": bundle.id,
1283
1338
  onMouseDown: onOverlayMouseDown,
1284
1339
  onMouseUp: onOverlayMouseUp,
1285
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1340
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1286
1341
  "div",
1287
1342
  {
1288
1343
  ref: dialogRef,
@@ -1292,13 +1347,13 @@ function MixMatchPicker({
1292
1347
  "aria-labelledby": titleId,
1293
1348
  tabIndex: -1,
1294
1349
  children: [
1295
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-mix-match__modal-header", children: [
1296
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-mix-match__modal-header-top", children: [
1297
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-mix-match__modal-heading", children: [
1298
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h4", { className: "lb-mix-match__modal-title", id: titleId, children: "Add to your bundle" }),
1299
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-mix-match__modal-subtitle", "data-modal-subtitle": true, children: subtitle })
1350
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__modal-header", children: [
1351
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__modal-header-top", children: [
1352
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__modal-heading", children: [
1353
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h4", { className: "lb-mix-match__modal-title", id: titleId, children: "Add to your bundle" }),
1354
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "lb-mix-match__modal-subtitle", "data-modal-subtitle": true, children: subtitle })
1300
1355
  ] }),
1301
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1356
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1302
1357
  "button",
1303
1358
  {
1304
1359
  type: "button",
@@ -1306,16 +1361,16 @@ function MixMatchPicker({
1306
1361
  "data-modal-close": true,
1307
1362
  "aria-label": "Close",
1308
1363
  onClick: onClose,
1309
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {})
1364
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CloseIcon, {})
1310
1365
  }
1311
1366
  )
1312
1367
  ] }),
1313
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1368
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1314
1369
  "div",
1315
1370
  {
1316
1371
  className: "lb-mix-match__progress lb-mix-match__modal-progress",
1317
1372
  "data-progress": true,
1318
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1373
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1319
1374
  "div",
1320
1375
  {
1321
1376
  className: "lb-mix-match__progress-segments",
@@ -1323,7 +1378,7 @@ function MixMatchPicker({
1323
1378
  "aria-valuenow": shownUnits,
1324
1379
  "aria-valuemin": 0,
1325
1380
  "aria-valuemax": requiredQty,
1326
- children: Array.from({ length: requiredQty }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1381
+ children: Array.from({ length: requiredQty }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1327
1382
  "span",
1328
1383
  {
1329
1384
  className: `lb-mix-match__progress-segment${i < shownUnits ? " lb-mix-match__progress-segment--filled" : ""}`,
@@ -1336,8 +1391,8 @@ function MixMatchPicker({
1336
1391
  }
1337
1392
  )
1338
1393
  ] }),
1339
- wc.showSearch && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-mix-match__modal-search", children: [
1340
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1394
+ wc.showSearch && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__modal-search", children: [
1395
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1341
1396
  "input",
1342
1397
  {
1343
1398
  type: "text",
@@ -1351,7 +1406,7 @@ function MixMatchPicker({
1351
1406
  onChange: (e) => setQuery(e.target.value)
1352
1407
  }
1353
1408
  ),
1354
- query.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1409
+ query.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1355
1410
  "button",
1356
1411
  {
1357
1412
  type: "button",
@@ -1359,11 +1414,11 @@ function MixMatchPicker({
1359
1414
  "data-modal-search-clear": true,
1360
1415
  "aria-label": "Clear search",
1361
1416
  onClick: () => setQuery(""),
1362
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SearchClearIcon, {})
1417
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SearchClearIcon, {})
1363
1418
  }
1364
1419
  )
1365
1420
  ] }),
1366
- showFilters && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1421
+ showFilters && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1367
1422
  "div",
1368
1423
  {
1369
1424
  className: "lb-mix-match__filters",
@@ -1372,7 +1427,7 @@ function MixMatchPicker({
1372
1427
  "aria-label": "Filter by product type",
1373
1428
  children: [FILTERS_ALL, ...productTypes].map((value) => {
1374
1429
  const isActive = value === activeType;
1375
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1430
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1376
1431
  "button",
1377
1432
  {
1378
1433
  type: "button",
@@ -1387,7 +1442,7 @@ function MixMatchPicker({
1387
1442
  })
1388
1443
  }
1389
1444
  ),
1390
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-mix-match__modal-list", "data-modal-list": true, children: filtered.map((ep) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1445
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-mix-match__modal-list", "data-modal-list": true, children: filtered.map((ep) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1391
1446
  MixMatchPickerRow,
1392
1447
  {
1393
1448
  bundle,
@@ -1406,10 +1461,10 @@ function MixMatchPicker({
1406
1461
  },
1407
1462
  ep.product.id
1408
1463
  )) }),
1409
- showEmpty && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-mix-match__modal-empty", "data-modal-empty": true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: "No products match your search" }) }),
1410
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "data-modal-live": true, "aria-live": "polite", className: "lb-visually-hidden", children: `${filtered.length} products shown` }),
1411
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-mix-match__modal-footer", children: [
1412
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1464
+ showEmpty && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-mix-match__modal-empty", "data-modal-empty": true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: "No products match your search" }) }),
1465
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-modal-live": true, "aria-live": "polite", className: "lb-visually-hidden", children: `${filtered.length} products shown` }),
1466
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__modal-footer", children: [
1467
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1413
1468
  "span",
1414
1469
  {
1415
1470
  className: "lb-mix-match__modal-footer-count",
@@ -1418,7 +1473,7 @@ function MixMatchPicker({
1418
1473
  children: `${shownUnits} of ${requiredQty} added`
1419
1474
  }
1420
1475
  ),
1421
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1476
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1422
1477
  "button",
1423
1478
  {
1424
1479
  type: "button",
@@ -1455,14 +1510,16 @@ function MixMatchPickerRow({
1455
1510
  }) {
1456
1511
  const { product, variants, isOos } = eligible;
1457
1512
  const rule = ruleFor(bundle, product.id);
1458
- const optionNames = (0, import_react11.useMemo)(() => {
1513
+ const optionNames = (0, import_react12.useMemo)(() => {
1459
1514
  const first = variants[0];
1460
1515
  return first ? first.selectedOptions.map((o) => o.name) : [];
1461
1516
  }, [variants]);
1462
1517
  const showPicker = variants.length > 1 && optionNames.length > 0;
1463
1518
  const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({ variants, optionNames });
1464
1519
  const currentVariant = selectedVariant ?? variants.find((v) => (0, import_core10.isVariantFulfillable)(v, rule.min)) ?? eligible.firstAvailableVariant ?? variants[0] ?? null;
1465
- const alreadyInBundle = (0, import_react11.useMemo)(() => {
1520
+ const vRule = ruleFor(bundle, product.id, currentVariant?.id);
1521
+ const variantMax = variantRuleFor(bundle, currentVariant?.id)?.max;
1522
+ const alreadyInBundle = (0, import_react12.useMemo)(() => {
1466
1523
  if (!currentVariant) return 0;
1467
1524
  let sum = 0;
1468
1525
  for (const s of selections) {
@@ -1472,13 +1529,13 @@ function MixMatchPickerRow({
1472
1529
  }
1473
1530
  return sum;
1474
1531
  }, [selections, product.id, currentVariant]);
1475
- const committedSelection = (0, import_react11.useMemo)(
1532
+ const committedSelection = (0, import_react12.useMemo)(
1476
1533
  () => currentVariant ? selections.find((s) => s.variantId === currentVariant.id) ?? null : null,
1477
1534
  [selections, currentVariant]
1478
1535
  );
1479
1536
  const variantInBundle = committedSelection !== null;
1480
1537
  const committedQty = committedSelection?.quantity ?? 0;
1481
- const productOtherUnits = (0, import_react11.useMemo)(() => {
1538
+ const productOtherUnits = (0, import_react12.useMemo)(() => {
1482
1539
  if (!currentVariant) return 0;
1483
1540
  let sum = 0;
1484
1541
  for (const s of selections) {
@@ -1489,22 +1546,26 @@ function MixMatchPickerRow({
1489
1546
  return sum;
1490
1547
  }, [selections, product.id, currentVariant]);
1491
1548
  const swapUnits = currentVariant ? swapUnitsFor?.(product.id, currentVariant.id) ?? 0 : 0;
1492
- const inSwapState = swapUnits >= rule.min;
1493
- const spotCeiling = inSwapState ? swapUnits : Math.min(rule.max - productOtherUnits, committedQty + remainingUnits);
1549
+ const inSwapState = swapUnits >= vRule.min;
1550
+ const spotCeiling = inSwapState ? swapUnits : Math.min(
1551
+ rule.max - productOtherUnits,
1552
+ committedQty + remainingUnits,
1553
+ variantMax ?? Infinity
1554
+ );
1494
1555
  const cap = currentVariant ? (0, import_core10.maxAddableQuantity)(
1495
1556
  currentVariant,
1496
1557
  spotCeiling,
1497
1558
  Math.max(0, alreadyInBundle - committedQty)
1498
1559
  ) : 0;
1499
- const stepperMax = Math.max(rule.min, cap);
1500
- const [qty, setQty] = (0, import_react11.useState)(rule.min);
1501
- (0, import_react11.useEffect)(() => {
1502
- setQty((prev) => Math.max(rule.min, Math.min(prev, stepperMax)));
1503
- }, [stepperMax, rule.min]);
1560
+ const stepperMax = Math.max(vRule.min, cap);
1561
+ const [qty, setQty] = (0, import_react12.useState)(vRule.min);
1562
+ (0, import_react12.useEffect)(() => {
1563
+ setQty((prev) => Math.max(vRule.min, Math.min(prev, stepperMax)));
1564
+ }, [stepperMax, vRule.min]);
1504
1565
  const currentVariantId = currentVariant?.id ?? null;
1505
- (0, import_react11.useEffect)(() => {
1506
- if (!variantInBundle) setQty(rule.min);
1507
- }, [variantInBundle, currentVariantId, rule.min]);
1566
+ (0, import_react12.useEffect)(() => {
1567
+ if (!variantInBundle) setQty(vRule.min);
1568
+ }, [variantInBundle, currentVariantId, vRule.min]);
1508
1569
  if (!currentVariant) return null;
1509
1570
  const thumbImage = currentVariant.image ?? product.featuredImage ?? null;
1510
1571
  const unitPriceText = (0, import_core10.formatUnitPrice)(
@@ -1512,8 +1573,8 @@ function MixMatchPickerRow({
1512
1573
  currentVariant.unitPriceMeasurement,
1513
1574
  currency
1514
1575
  );
1515
- const noStock = cap < rule.min;
1516
- const needsMoreSpots = !variantInBundle && !inSwapState && rule.min > remainingUnits;
1576
+ const noStock = cap < vRule.min;
1577
+ const needsMoreSpots = !variantInBundle && !inSwapState && vRule.min > remainingUnits;
1517
1578
  const addDisabled = !variantInBundle && (inSwapState ? noStock : atCapacity || noStock || needsMoreSpots);
1518
1579
  const lockedRequired = variantInBundle && currentVariant != null && !(canRemoveVariant?.(currentVariant.id) ?? true);
1519
1580
  const stepperDisabled = isOos || !variantInBundle && addDisabled;
@@ -1524,7 +1585,7 @@ function MixMatchPickerRow({
1524
1585
  return;
1525
1586
  }
1526
1587
  if (!currentVariant || noStock) return;
1527
- const pickedQty = showStepper ? qty : rule.min;
1588
+ const pickedQty = showStepper ? qty : vRule.min;
1528
1589
  const item = {
1529
1590
  productId: product.id,
1530
1591
  variantId: currentVariant.id,
@@ -1546,7 +1607,7 @@ function MixMatchPickerRow({
1546
1607
  }
1547
1608
  const priceCents = parseFloat(currentVariant.price.amount);
1548
1609
  const compareCents = currentVariant.compareAtPrice ? parseFloat(currentVariant.compareAtPrice.amount) : null;
1549
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1610
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1550
1611
  "div",
1551
1612
  {
1552
1613
  className: `lb-mix-match__modal-product${isOos ? " lb-mix-match__modal-product--sold-out" : ""}${variantInBundle ? " lb-mix-match__modal-product--in-bundle" : ""}`,
@@ -1555,8 +1616,8 @@ function MixMatchPickerRow({
1555
1616
  "data-type": product.productType ?? "",
1556
1617
  "aria-disabled": isOos || void 0,
1557
1618
  children: [
1558
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-mix-match__modal-product-thumb", children: [
1559
- thumbImage && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1619
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__modal-product-thumb", children: [
1620
+ thumbImage && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1560
1621
  "img",
1561
1622
  {
1562
1623
  src: thumbImage.url,
@@ -1564,10 +1625,10 @@ function MixMatchPickerRow({
1564
1625
  loading: "lazy"
1565
1626
  }
1566
1627
  ),
1567
- variantInBundle && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-mix-match__modal-added-badge", children: "Added" })
1628
+ variantInBundle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-mix-match__modal-added-badge", children: "Added" })
1568
1629
  ] }),
1569
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-mix-match__modal-product-info", children: [
1570
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-mix-match__modal-product-title", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1630
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__modal-product-info", children: [
1631
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "lb-mix-match__modal-product-title", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1571
1632
  "a",
1572
1633
  {
1573
1634
  href: `/products/${product.handle}`,
@@ -1576,24 +1637,24 @@ function MixMatchPickerRow({
1576
1637
  children: product.title
1577
1638
  }
1578
1639
  ) }),
1579
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "lb-mix-match__modal-product-price", children: [
1580
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "data-row-price-sale": true, children: (0, import_core10.formatMoney)(priceCents, currency) }),
1581
- compareCents !== null && compareCents > priceCents && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("s", { className: "lb-mix-match__modal-product-compare", "data-row-compare": true, children: (0, import_core10.formatMoney)(compareCents, currency) })
1640
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "lb-mix-match__modal-product-price", children: [
1641
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-row-price-sale": true, children: (0, import_core10.formatMoney)(priceCents, currency) }),
1642
+ compareCents !== null && compareCents > priceCents && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("s", { className: "lb-mix-match__modal-product-compare", "data-row-compare": true, children: (0, import_core10.formatMoney)(compareCents, currency) })
1582
1643
  ] }),
1583
- unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price", children: unitPriceText }),
1584
- showPicker && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-bundle-variant-option-groups", children: optionNames.map((optionName, optionIndex) => {
1644
+ unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price", children: unitPriceText }),
1645
+ showPicker && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-bundle-variant-option-groups", children: optionNames.map((optionName, optionIndex) => {
1585
1646
  const dropdownOptions = optionsFor(optionIndex).map((o) => ({
1586
1647
  value: o.value,
1587
1648
  label: o.value,
1588
1649
  disabled: o.disabled
1589
1650
  }));
1590
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1651
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1591
1652
  "div",
1592
1653
  {
1593
1654
  className: "lb-bundle-variant-option-group",
1594
1655
  children: [
1595
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-bundle-variant-option-label", children: optionName }),
1596
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1656
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-bundle-variant-option-label", children: optionName }),
1657
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1597
1658
  VariantDropdown,
1598
1659
  {
1599
1660
  className: "lb-mix-match__variant-select-dropdown",
@@ -1608,17 +1669,17 @@ function MixMatchPickerRow({
1608
1669
  optionName
1609
1670
  );
1610
1671
  }) }),
1611
- !showPicker && variants.length === 1 && currentVariant.title !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-mix-match__filled-variant", children: currentVariant.title }),
1612
- !isOos && needsMoreSpots && remainingUnits > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-mix-match__modal-needs-spots", children: rule.min === 1 ? "Needs 1 spot" : `Needs ${rule.min} spots` }),
1613
- isOos ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "lb-mix-match__modal-sold-out-label", children: "Sold out" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "lb-mix-match__modal-product-actions", children: [
1614
- showStepper && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "lb-bundle-variant-option-group lb-mix-match__qty-stepper-group", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1672
+ !showPicker && variants.length === 1 && currentVariant.title !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-mix-match__filled-variant", children: currentVariant.title }),
1673
+ !isOos && needsMoreSpots && remainingUnits > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-mix-match__modal-needs-spots", children: rule.min === 1 ? "Needs 1 spot" : `Needs ${rule.min} spots` }),
1674
+ isOos ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-mix-match__modal-sold-out-label", children: "Sold out" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__modal-product-actions", children: [
1675
+ showStepper && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-bundle-variant-option-group lb-mix-match__qty-stepper-group", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1615
1676
  "div",
1616
1677
  {
1617
1678
  className: "lb-mix-match__qty-stepper",
1618
1679
  role: "group",
1619
1680
  "aria-label": "Quantity",
1620
1681
  children: [
1621
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1682
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1622
1683
  "button",
1623
1684
  {
1624
1685
  type: "button",
@@ -1639,7 +1700,7 @@ function MixMatchPickerRow({
1639
1700
  children: "\u2212"
1640
1701
  }
1641
1702
  ),
1642
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1703
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1643
1704
  "span",
1644
1705
  {
1645
1706
  className: "lb-mix-match__qty-stepper-value",
@@ -1648,7 +1709,7 @@ function MixMatchPickerRow({
1648
1709
  children: shownQty
1649
1710
  }
1650
1711
  ),
1651
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1712
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1652
1713
  "button",
1653
1714
  {
1654
1715
  type: "button",
@@ -1672,7 +1733,7 @@ function MixMatchPickerRow({
1672
1733
  ]
1673
1734
  }
1674
1735
  ) }),
1675
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1736
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1676
1737
  "button",
1677
1738
  {
1678
1739
  type: "button",
@@ -1691,7 +1752,7 @@ function MixMatchPickerRow({
1691
1752
  );
1692
1753
  }
1693
1754
  function CloseIcon() {
1694
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1755
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1695
1756
  "svg",
1696
1757
  {
1697
1758
  width: "20",
@@ -1701,14 +1762,14 @@ function CloseIcon() {
1701
1762
  xmlns: "http://www.w3.org/2000/svg",
1702
1763
  "aria-hidden": "true",
1703
1764
  children: [
1704
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "5", y1: "5", x2: "15", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
1705
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "15", y1: "5", x2: "5", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
1765
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "5", y1: "5", x2: "15", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
1766
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "15", y1: "5", x2: "5", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
1706
1767
  ]
1707
1768
  }
1708
1769
  );
1709
1770
  }
1710
1771
  function SearchClearIcon() {
1711
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1772
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1712
1773
  "svg",
1713
1774
  {
1714
1775
  width: "16",
@@ -1717,17 +1778,25 @@ function SearchClearIcon() {
1717
1778
  fill: "currentColor",
1718
1779
  xmlns: "http://www.w3.org/2000/svg",
1719
1780
  "aria-hidden": "true",
1720
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M14.348 5.652a.5.5 0 0 0-.707 0L10 9.293 6.36 5.652a.5.5 0 1 0-.708.707L9.293 10l-3.641 3.641a.5.5 0 0 0 .708.707L10 10.707l3.641 3.641a.5.5 0 0 0 .707-.707L10.707 10l3.641-3.641a.5.5 0 0 0 0-.707z" })
1781
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M14.348 5.652a.5.5 0 0 0-.707 0L10 9.293 6.36 5.652a.5.5 0 1 0-.708.707L9.293 10l-3.641 3.641a.5.5 0 0 0 .708.707L10 10.707l3.641 3.641a.5.5 0 0 0 .707-.707L10.707 10l3.641-3.641a.5.5 0 0 0 0-.707z" })
1721
1782
  }
1722
1783
  );
1723
1784
  }
1724
1785
 
1725
1786
  // src/components/MixMatchBundle.tsx
1726
- var import_jsx_runtime4 = require("react/jsx-runtime");
1727
- function ruleFor2(bundle, productId) {
1787
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1788
+ function ruleFor2(bundle, productId, variantId) {
1789
+ if (variantId) {
1790
+ const vRule = bundle.variantRules[variantId];
1791
+ if (vRule) return vRule;
1792
+ }
1728
1793
  return bundle.productRules[productId] ?? import_core11.DEFAULT_PRODUCT_RULE;
1729
1794
  }
1795
+ function variantRuleFor2(bundle, variantId) {
1796
+ return variantId ? bundle.variantRules[variantId] : void 0;
1797
+ }
1730
1798
  function canRemoveItem(bundle, selections, item) {
1799
+ if (variantRuleFor2(bundle, item.variantId)?.required) return false;
1731
1800
  const rule = ruleFor2(bundle, item.productId);
1732
1801
  if (!rule.required) return true;
1733
1802
  const unitsElsewhere = selections.filter((s) => s.productId === item.productId && s !== item).reduce((sum, s) => sum + s.quantity, 0);
@@ -1748,7 +1817,8 @@ function MixMatchBundle(props) {
1748
1817
  analyticsEnabled,
1749
1818
  onAddToCart,
1750
1819
  onError,
1751
- className
1820
+ className,
1821
+ productHandle
1752
1822
  } = props;
1753
1823
  const result = useBundleData({
1754
1824
  shopDomain,
@@ -1766,16 +1836,16 @@ function MixMatchBundle(props) {
1766
1836
  bundleType: "mix_match",
1767
1837
  enabled: analyticsEnabled !== false
1768
1838
  });
1769
- const [selections, setSelections] = (0, import_react12.useState)([]);
1770
- const [seededFor, setSeededFor] = (0, import_react12.useState)(null);
1771
- const [pickerOpen, setPickerOpen] = (0, import_react12.useState)(false);
1772
- const [addingToCart, setAddingToCart] = (0, import_react12.useState)(false);
1773
- const [cartError, setCartError] = (0, import_react12.useState)(null);
1839
+ const [selections, setSelections] = (0, import_react13.useState)([]);
1840
+ const [seededFor, setSeededFor] = (0, import_react13.useState)(null);
1841
+ const [pickerOpen, setPickerOpen] = (0, import_react13.useState)(false);
1842
+ const [addingToCart, setAddingToCart] = (0, import_react13.useState)(false);
1843
+ const [cartError, setCartError] = (0, import_react13.useState)(null);
1774
1844
  const bundle = result.status === "success" && result.bundle.bundleType === "mix_match" ? result.bundle : null;
1775
1845
  const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
1776
1846
  const setModalConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
1777
1847
  const slotsEdgeFadeRef = useEdgeFade();
1778
- (0, import_react12.useEffect)(() => {
1848
+ (0, import_react13.useEffect)(() => {
1779
1849
  if (result.status === "error") {
1780
1850
  onError?.(result.error);
1781
1851
  return;
@@ -1788,40 +1858,67 @@ function MixMatchBundle(props) {
1788
1858
  );
1789
1859
  }
1790
1860
  }, [result, onError]);
1791
- (0, import_react12.useEffect)(() => {
1861
+ (0, import_react13.useEffect)(() => {
1792
1862
  if (!bundle || seededFor === bundle.id) return;
1793
1863
  setSeededFor(bundle.id);
1794
1864
  const seedCurrency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
1865
+ const pick = (product, variant, quantity) => ({
1866
+ productId: product.id,
1867
+ variantId: variant.id,
1868
+ title: product.title,
1869
+ url: `/products/${product.handle}`,
1870
+ variantTitle: variant.title,
1871
+ featuredImage: variant.image?.url ?? product.featuredImage?.url ?? null,
1872
+ price: parseFloat(variant.price.amount),
1873
+ compareAtPrice: variant.compareAtPrice ? parseFloat(variant.compareAtPrice.amount) : null,
1874
+ unitPrice: (0, import_core11.formatUnitPrice)(
1875
+ variant.unitPrice,
1876
+ variant.unitPriceMeasurement,
1877
+ seedCurrency
1878
+ ),
1879
+ quantity
1880
+ });
1795
1881
  const seeds = [];
1796
- for (const [pid, rule] of Object.entries(bundle.productRules)) {
1797
- if (!rule.required) continue;
1798
- const product = bundle.products.find((p) => p.id === pid);
1799
- const variant = product?.variants.nodes.find(
1882
+ for (const product of bundle.products) {
1883
+ let seededVariant = false;
1884
+ for (const variant2 of product.variants.nodes) {
1885
+ const vRule = bundle.variantRules[variant2.id];
1886
+ if (!vRule?.required) continue;
1887
+ if (!(0, import_core11.isVariantFulfillable)(variant2, Math.max(1, vRule.min))) continue;
1888
+ seeds.push(pick(product, variant2, Math.max(1, vRule.min)));
1889
+ seededVariant = true;
1890
+ }
1891
+ if (seededVariant) continue;
1892
+ const rule = bundle.productRules[product.id];
1893
+ if (!rule?.required) continue;
1894
+ const variant = product.variants.nodes.find(
1800
1895
  (v) => (0, import_core11.isVariantFulfillable)(v, rule.min)
1801
1896
  );
1802
- if (!product || !variant) continue;
1803
- seeds.push({
1804
- productId: product.id,
1805
- variantId: variant.id,
1806
- title: product.title,
1807
- url: `/products/${product.handle}`,
1808
- variantTitle: variant.title,
1809
- featuredImage: variant.image?.url ?? product.featuredImage?.url ?? null,
1810
- price: parseFloat(variant.price.amount),
1811
- compareAtPrice: variant.compareAtPrice ? parseFloat(variant.compareAtPrice.amount) : null,
1812
- unitPrice: (0, import_core11.formatUnitPrice)(
1813
- variant.unitPrice,
1814
- variant.unitPriceMeasurement,
1815
- seedCurrency
1816
- ),
1817
- quantity: rule.min
1818
- });
1897
+ if (!variant) continue;
1898
+ seeds.push(pick(product, variant, rule.min));
1899
+ }
1900
+ if (seeds.length === 0 && productHandle) {
1901
+ const requiredUnits2 = bundle.minQuantity ?? 0;
1902
+ const qualifies = (p) => {
1903
+ const min = bundle.productRules[p.id]?.min ?? 1;
1904
+ return min <= requiredUnits2 && p.variants.nodes.some((v) => (0, import_core11.isVariantFulfillable)(v, min));
1905
+ };
1906
+ const seed = bundle.products.find(
1907
+ (p) => p.handle === productHandle && qualifies(p)
1908
+ ) ?? bundle.products.find(qualifies);
1909
+ if (seed) {
1910
+ const min = bundle.productRules[seed.id]?.min ?? 1;
1911
+ const variant = seed.variants.nodes.find(
1912
+ (v) => (0, import_core11.isVariantFulfillable)(v, min)
1913
+ );
1914
+ if (variant) seeds.push(pick(seed, variant, min));
1915
+ }
1819
1916
  }
1820
1917
  if (seeds.length > 0) setSelections(seeds);
1821
- }, [bundle, seededFor]);
1918
+ }, [bundle, seededFor, productHandle]);
1822
1919
  const showStepper = bundle?.widgetConfig.mixMatchShowQuantitySelector !== false;
1823
1920
  const requiredUnits = bundle?.minQuantity ?? 0;
1824
- const totalQuantity = (0, import_react12.useMemo)(
1921
+ const totalQuantity = (0, import_react13.useMemo)(
1825
1922
  () => selections.reduce((sum, s) => sum + s.quantity, 0),
1826
1923
  [selections]
1827
1924
  );
@@ -1829,7 +1926,7 @@ function MixMatchBundle(props) {
1829
1926
  const valid = !!bundle && meetsMinUnits;
1830
1927
  const shownUnits = Math.min(totalQuantity, requiredUnits);
1831
1928
  const remaining = Math.max(0, requiredUnits - shownUnits);
1832
- const summary = (0, import_react12.useMemo)(() => {
1929
+ const summary = (0, import_react13.useMemo)(() => {
1833
1930
  let comparePrice = 0;
1834
1931
  let salePrice = 0;
1835
1932
  for (const sel of selections) {
@@ -1847,7 +1944,7 @@ function MixMatchBundle(props) {
1847
1944
  const savingsPercent = comparePrice > 0 && savings > 0 ? Math.round(savings / comparePrice * 100) : 0;
1848
1945
  return { comparePrice, salePrice, savings, savingsPercent };
1849
1946
  }, [selections, bundle]);
1850
- const addSelection = (0, import_react12.useCallback)(
1947
+ const addSelection = (0, import_react13.useCallback)(
1851
1948
  (item) => {
1852
1949
  setSelections((prev) => {
1853
1950
  const units = prev.reduce((sum, s) => sum + s.quantity, 0);
@@ -1858,7 +1955,7 @@ function MixMatchBundle(props) {
1858
1955
  },
1859
1956
  [requiredUnits]
1860
1957
  );
1861
- const updateQuantity = (0, import_react12.useCallback)(
1958
+ const updateQuantity = (0, import_react13.useCallback)(
1862
1959
  (productId, variantId, quantity) => {
1863
1960
  setSelections((prev) => {
1864
1961
  const idx = prev.findIndex(
@@ -1881,7 +1978,7 @@ function MixMatchBundle(props) {
1881
1978
  },
1882
1979
  [requiredUnits]
1883
1980
  );
1884
- const swapSelection = (0, import_react12.useCallback)(
1981
+ const swapSelection = (0, import_react13.useCallback)(
1885
1982
  (item) => {
1886
1983
  setSelections((prev) => {
1887
1984
  if (!bundle) return prev;
@@ -1913,7 +2010,7 @@ function MixMatchBundle(props) {
1913
2010
  },
1914
2011
  [bundle]
1915
2012
  );
1916
- const removeVariant = (0, import_react12.useCallback)(
2013
+ const removeVariant = (0, import_react13.useCallback)(
1917
2014
  (variantId) => {
1918
2015
  setSelections((prev) => {
1919
2016
  const idx = prev.findIndex((s) => s.variantId === variantId);
@@ -1926,7 +2023,7 @@ function MixMatchBundle(props) {
1926
2023
  },
1927
2024
  [bundle]
1928
2025
  );
1929
- const removeSlot = (0, import_react12.useCallback)(
2026
+ const removeSlot = (0, import_react13.useCallback)(
1930
2027
  (index) => {
1931
2028
  setSelections((prev) => {
1932
2029
  if (index < 0 || index >= prev.length) return prev;
@@ -1938,7 +2035,7 @@ function MixMatchBundle(props) {
1938
2035
  },
1939
2036
  [bundle]
1940
2037
  );
1941
- const handleAddToCart = (0, import_react12.useCallback)(async () => {
2038
+ const handleAddToCart = (0, import_react13.useCallback)(async () => {
1942
2039
  if (!bundle || !valid) return;
1943
2040
  const grouped = /* @__PURE__ */ new Map();
1944
2041
  for (const sel of selections) {
@@ -1978,9 +2075,9 @@ function MixMatchBundle(props) {
1978
2075
  }
1979
2076
  }, [bundle, selections, valid, onAddToCart, onError, trackAddToCart, totalQuantity]);
1980
2077
  if (result.status === "loading") {
1981
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
1982
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
1983
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
2078
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
2079
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
2080
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
1984
2081
  ] });
1985
2082
  }
1986
2083
  if (result.status === "error") return null;
@@ -1999,7 +2096,7 @@ function MixMatchBundle(props) {
1999
2096
  if (requiredBlocked) return null;
2000
2097
  const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
2001
2098
  const ctaLabel = addingToCart ? "Adding..." : bundle.widgetConfig.cta.ctaText || "Add to cart";
2002
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2099
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2003
2100
  "div",
2004
2101
  {
2005
2102
  ref: (el) => {
@@ -2011,12 +2108,19 @@ function MixMatchBundle(props) {
2011
2108
  "aria-label": bundle.title,
2012
2109
  "data-required-quantity": requiredUnits,
2013
2110
  children: [
2014
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-bundle-header__content", children: [
2015
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
2016
- bundle.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
2111
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle-header__content", children: [
2112
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
2113
+ bundle.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
2017
2114
  ] }) }),
2018
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__progress", "data-progress": true, children: [
2019
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2115
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2116
+ BundleCountdown,
2117
+ {
2118
+ endsAt: bundle.endsAt,
2119
+ widgetConfig: bundle.widgetConfig
2120
+ }
2121
+ ),
2122
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-mix-match__progress", "data-progress": true, children: [
2123
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2020
2124
  "div",
2021
2125
  {
2022
2126
  className: "lb-mix-match__progress-segments",
@@ -2024,7 +2128,7 @@ function MixMatchBundle(props) {
2024
2128
  "aria-valuenow": shownUnits,
2025
2129
  "aria-valuemin": 0,
2026
2130
  "aria-valuemax": requiredUnits,
2027
- children: Array.from({ length: requiredUnits }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2131
+ children: Array.from({ length: requiredUnits }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2028
2132
  "span",
2029
2133
  {
2030
2134
  className: `lb-mix-match__progress-segment${i < shownUnits ? " lb-mix-match__progress-segment--filled" : ""}`,
@@ -2034,8 +2138,8 @@ function MixMatchBundle(props) {
2034
2138
  ))
2035
2139
  }
2036
2140
  ),
2037
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__progress-labels", children: [
2038
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2141
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-mix-match__progress-labels", children: [
2142
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2039
2143
  "span",
2040
2144
  {
2041
2145
  className: "lb-mix-match__progress-count",
@@ -2044,7 +2148,7 @@ function MixMatchBundle(props) {
2044
2148
  children: `${shownUnits} of ${requiredUnits} added`
2045
2149
  }
2046
2150
  ),
2047
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2151
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2048
2152
  "span",
2049
2153
  {
2050
2154
  className: "lb-mix-match__progress-remaining",
@@ -2055,7 +2159,7 @@ function MixMatchBundle(props) {
2055
2159
  )
2056
2160
  ] })
2057
2161
  ] }),
2058
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2162
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2059
2163
  "div",
2060
2164
  {
2061
2165
  className: "lb-mix-match__slots lb-edge-fade",
@@ -2066,15 +2170,15 @@ function MixMatchBundle(props) {
2066
2170
  button provides the keyboard/AT path (its activation bubbles
2067
2171
  here); × stops propagation. */
2068
2172
  // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- keyboard path is the inner "Edit selection" title button whose activation bubbles to this handler
2069
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2173
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2070
2174
  "div",
2071
2175
  {
2072
2176
  className: "lb-mix-match__slot lb-mix-match__slot--filled",
2073
2177
  onClick: () => setPickerOpen(true),
2074
2178
  children: [
2075
- item.featuredImage && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-bundle-thumbnail", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: item.featuredImage, alt: item.title, loading: "lazy" }) }),
2076
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-mix-match__filled-info", children: [
2077
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2179
+ item.featuredImage && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-bundle-thumbnail", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: item.featuredImage, alt: item.title, loading: "lazy" }) }),
2180
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-mix-match__filled-info", children: [
2181
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2078
2182
  "button",
2079
2183
  {
2080
2184
  type: "button",
@@ -2083,15 +2187,15 @@ function MixMatchBundle(props) {
2083
2187
  children: item.title
2084
2188
  }
2085
2189
  ),
2086
- item.variantTitle && item.variantTitle !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-mix-match__filled-variant", children: item.variantTitle }),
2087
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "lb-mix-match__filled-price", children: [
2088
- item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("s", { className: "lb-mix-match__filled-compare", children: (0, import_core11.formatMoney)(item.compareAtPrice, currency) }),
2089
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-bundle-product-price", children: (0, import_core11.formatMoney)(item.price, currency) }),
2090
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-bundle-qty-inline", children: `\xD7${item.quantity}` })
2190
+ item.variantTitle && item.variantTitle !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-mix-match__filled-variant", children: item.variantTitle }),
2191
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-mix-match__filled-price", children: [
2192
+ item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("s", { className: "lb-mix-match__filled-compare", children: (0, import_core11.formatMoney)(item.compareAtPrice, currency) }),
2193
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-product-price", children: (0, import_core11.formatMoney)(item.price, currency) }),
2194
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-qty-inline", children: `\xD7${item.quantity}` })
2091
2195
  ] }),
2092
- item.unitPrice && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-bundle-product-unit-price", children: item.unitPrice })
2196
+ item.unitPrice && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-product-unit-price", children: item.unitPrice })
2093
2197
  ] }),
2094
- canRemoveItem(bundle, selections, item) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2198
+ canRemoveItem(bundle, selections, item) ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2095
2199
  "button",
2096
2200
  {
2097
2201
  type: "button",
@@ -2101,13 +2205,13 @@ function MixMatchBundle(props) {
2101
2205
  e.stopPropagation();
2102
2206
  removeSlot(index);
2103
2207
  },
2104
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CloseIcon2, {})
2208
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon2, {})
2105
2209
  }
2106
2210
  ) : (
2107
2211
  /* Required pick at its floor — quiet state chip instead of
2108
2212
  the ×; removal returns once another slot covers the
2109
2213
  product's minimum (variant swap flow). */
2110
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-mix-match__slot-required", children: "Required" })
2214
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-mix-match__slot-required", children: "Required" })
2111
2215
  )
2112
2216
  ]
2113
2217
  },
@@ -2116,7 +2220,7 @@ function MixMatchBundle(props) {
2116
2220
  ))
2117
2221
  }
2118
2222
  ),
2119
- totalQuantity < requiredUnits && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2223
+ totalQuantity < requiredUnits && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2120
2224
  "button",
2121
2225
  {
2122
2226
  type: "button",
@@ -2124,34 +2228,34 @@ function MixMatchBundle(props) {
2124
2228
  "data-add-product-trigger": true,
2125
2229
  onClick: () => setPickerOpen(true),
2126
2230
  children: [
2127
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-mix-match__add-product-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PlusIcon, {}) }),
2128
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-mix-match__add-product-label", children: "Add a product" })
2231
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-mix-match__add-product-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PlusIcon, {}) }),
2232
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-mix-match__add-product-label", children: "Add a product" })
2129
2233
  ]
2130
2234
  }
2131
2235
  ),
2132
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "lb-bundle-divider" }),
2133
- selections.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-bundle-summary", "data-pricing-section": true, children: [
2134
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lb-bundle-summary__text", children: [
2135
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-bundle-summary__label", children: "Bundle total" }),
2136
- bundle.widgetConfig.savingsBar.visible && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
2236
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-bundle-divider" }),
2237
+ selections.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle-summary", "data-pricing-section": true, children: [
2238
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle-summary__text", children: [
2239
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-summary__label", children: "Bundle total" }),
2240
+ bundle.widgetConfig.savingsBar.visible && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
2137
2241
  "You save",
2138
2242
  " ",
2139
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-savings-amount": true, children: (0, import_core11.formatMoney)(summary.savings, currency) }),
2243
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "data-savings-amount": true, children: (0, import_core11.formatMoney)(summary.savings, currency) }),
2140
2244
  " ",
2141
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { "data-savings-percent": true, children: [
2245
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { "data-savings-percent": true, children: [
2142
2246
  "(",
2143
2247
  summary.savingsPercent,
2144
2248
  "%)"
2145
2249
  ] })
2146
2250
  ] })
2147
2251
  ] }),
2148
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
2149
- bundle.widgetConfig.pricing.showComparePrice && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core11.formatMoney)(summary.comparePrice, currency) }),
2150
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core11.formatMoney)(summary.salePrice, currency) })
2252
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
2253
+ bundle.widgetConfig.pricing.showComparePrice && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core11.formatMoney)(summary.comparePrice, currency) }),
2254
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core11.formatMoney)(summary.salePrice, currency) })
2151
2255
  ] })
2152
2256
  ] }),
2153
- cartError && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
2154
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2257
+ cartError && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
2258
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2155
2259
  "button",
2156
2260
  {
2157
2261
  className: "lb-bundle__cta",
@@ -2161,7 +2265,7 @@ function MixMatchBundle(props) {
2161
2265
  children: ctaLabel
2162
2266
  }
2163
2267
  ),
2164
- pickerOpen && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2268
+ pickerOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2165
2269
  MixMatchPicker,
2166
2270
  {
2167
2271
  outOfStockBehavior,
@@ -2202,7 +2306,7 @@ function countInStockProducts(bundle) {
2202
2306
  return count;
2203
2307
  }
2204
2308
  function PlusIcon() {
2205
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2309
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2206
2310
  "svg",
2207
2311
  {
2208
2312
  width: "18",
@@ -2212,14 +2316,14 @@ function PlusIcon() {
2212
2316
  xmlns: "http://www.w3.org/2000/svg",
2213
2317
  "aria-hidden": "true",
2214
2318
  children: [
2215
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "9", y1: "3", x2: "9", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2216
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "3", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
2319
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "9", y1: "3", x2: "9", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2320
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "3", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
2217
2321
  ]
2218
2322
  }
2219
2323
  );
2220
2324
  }
2221
2325
  function CloseIcon2() {
2222
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2326
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2223
2327
  "svg",
2224
2328
  {
2225
2329
  width: "16",
@@ -2229,17 +2333,17 @@ function CloseIcon2() {
2229
2333
  xmlns: "http://www.w3.org/2000/svg",
2230
2334
  "aria-hidden": "true",
2231
2335
  children: [
2232
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "5", y1: "5", x2: "15", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2233
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "15", y1: "5", x2: "5", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
2336
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "5", y1: "5", x2: "15", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2337
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "15", y1: "5", x2: "5", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
2234
2338
  ]
2235
2339
  }
2236
2340
  );
2237
2341
  }
2238
2342
 
2239
2343
  // src/components/VolumeBundle.tsx
2240
- var import_react13 = require("react");
2344
+ var import_react14 = require("react");
2241
2345
  var import_core12 = require("@lime-bundles/core");
2242
- var import_jsx_runtime5 = require("react/jsx-runtime");
2346
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2243
2347
  function VolumeBundle(props) {
2244
2348
  const {
2245
2349
  shopDomain,
@@ -2263,13 +2367,30 @@ function VolumeBundle(props) {
2263
2367
  bundleType: "volume",
2264
2368
  enabled: analyticsEnabled !== false
2265
2369
  });
2266
- const [quantity, setQuantity] = (0, import_react13.useState)(1);
2267
- const [addingToCart, setAddingToCart] = (0, import_react13.useState)(false);
2268
- const [cartError, setCartError] = (0, import_react13.useState)(null);
2370
+ const [quantity, setQuantity] = (0, import_react14.useState)(1);
2371
+ const [addingToCart, setAddingToCart] = (0, import_react14.useState)(false);
2372
+ const [cartError, setCartError] = (0, import_react14.useState)(null);
2373
+ const [seededFor, setSeededFor] = (0, import_react14.useState)(null);
2269
2374
  const bundle = result.status === "success" && result.bundle.bundleType === "volume" ? result.bundle : null;
2375
+ (0, import_react14.useEffect)(() => {
2376
+ if (!bundle || seededFor === bundle.id) return;
2377
+ setSeededFor(bundle.id);
2378
+ const tiers = bundle.volumeTiers;
2379
+ if (tiers.length === 0) return;
2380
+ const best = (0, import_core12.bestValueTierIndex)(
2381
+ tiers,
2382
+ bundle.discountConfig.discountType
2383
+ );
2384
+ const idx = (0, import_core12.resolveDefaultTierIndex)(
2385
+ bundle.widgetConfig.defaultTier,
2386
+ tiers.length,
2387
+ best
2388
+ );
2389
+ setQuantity(tiers[idx].minQuantity);
2390
+ }, [bundle, seededFor]);
2270
2391
  const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
2271
2392
  const tiersEdgeFadeRef = useEdgeFade();
2272
- (0, import_react13.useEffect)(() => {
2393
+ (0, import_react14.useEffect)(() => {
2273
2394
  if (result.status === "error") {
2274
2395
  onError?.(result.error);
2275
2396
  return;
@@ -2283,8 +2404,8 @@ function VolumeBundle(props) {
2283
2404
  }
2284
2405
  }, [result, onError]);
2285
2406
  const product = bundle?.products[0];
2286
- const variants = (0, import_react13.useMemo)(() => product?.variants.nodes ?? [], [product]);
2287
- const optionNames = (0, import_react13.useMemo)(() => {
2407
+ const variants = (0, import_react14.useMemo)(() => product?.variants.nodes ?? [], [product]);
2408
+ const optionNames = (0, import_react14.useMemo)(() => {
2288
2409
  const first = variants[0];
2289
2410
  return first ? first.selectedOptions.map((o) => o.name) : [];
2290
2411
  }, [variants]);
@@ -2301,7 +2422,7 @@ function VolumeBundle(props) {
2301
2422
  displayVariant.unitPriceMeasurement,
2302
2423
  currency
2303
2424
  ) : null;
2304
- const tierSavings = (0, import_react13.useMemo)(
2425
+ const tierSavings = (0, import_react14.useMemo)(
2305
2426
  () => bundle ? (0, import_core12.calculateTierSavings)(
2306
2427
  bundle.volumeTiers,
2307
2428
  basePrice,
@@ -2311,26 +2432,23 @@ function VolumeBundle(props) {
2311
2432
  [bundle, basePrice, quantity]
2312
2433
  );
2313
2434
  const activeTier = bundle ? (0, import_core12.getActiveTier)(bundle.volumeTiers, quantity) : null;
2314
- const popularTierIndex = (0, import_react13.useMemo)(() => {
2315
- if (!bundle?.widgetConfig.popularBadge.visible || tierSavings.length === 0) {
2316
- return -1;
2317
- }
2318
- const pinned = bundle.widgetConfig.popularBadge.tierIndex;
2319
- if (pinned !== void 0) {
2320
- return Math.min(Math.max(pinned, 0), tierSavings.length - 1);
2321
- }
2322
- let best = 0;
2323
- for (let i = 1; i < tierSavings.length; i++) {
2324
- if (tierSavings[i].savings > tierSavings[best].savings) best = i;
2325
- }
2326
- return best;
2327
- }, [bundle, tierSavings]);
2435
+ const popularTierIndex = (0, import_react14.useMemo)(() => {
2436
+ if (!bundle || bundle.volumeTiers.length === 0) return -1;
2437
+ return (0, import_core12.resolvePopularTierIndex)(
2438
+ bundle.widgetConfig.popularBadge.tierIndex,
2439
+ bundle.widgetConfig.popularBadge.visible !== false,
2440
+ (0, import_core12.bestValueTierIndex)(
2441
+ bundle.volumeTiers,
2442
+ bundle.discountConfig.discountType
2443
+ )
2444
+ );
2445
+ }, [bundle]);
2328
2446
  const activeUnitPrice = tierSavings.find((ts) => ts.tier === activeTier)?.unitPrice ?? basePrice;
2329
2447
  const undiscountedTotal = basePrice * quantity;
2330
2448
  const volumeTotal = activeUnitPrice * quantity;
2331
2449
  const volumeSavings = Math.max(0, undiscountedTotal - volumeTotal);
2332
2450
  const volumeSavingsPercent = undiscountedTotal > 0 && volumeSavings > 0 ? Math.round(volumeSavings / undiscountedTotal * 100) : 0;
2333
- const handleAddToCart = (0, import_react13.useCallback)(async () => {
2451
+ const handleAddToCart = (0, import_react14.useCallback)(async () => {
2334
2452
  if (!bundle || !product) return;
2335
2453
  const variant = selectedVariant ?? product.variants.nodes.find(
2336
2454
  (v) => (0, import_core12.isVariantFulfillable)(v, quantity)
@@ -2376,15 +2494,15 @@ function VolumeBundle(props) {
2376
2494
  tierSavings
2377
2495
  ]);
2378
2496
  if (result.status === "loading") {
2379
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
2380
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
2381
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-skeleton lb-skeleton--tiers" })
2497
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
2498
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
2499
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-skeleton lb-skeleton--tiers" })
2382
2500
  ] });
2383
2501
  }
2384
2502
  if (result.status === "error") return null;
2385
2503
  if (!bundle) return null;
2386
2504
  if (!product) return null;
2387
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2505
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2388
2506
  "div",
2389
2507
  {
2390
2508
  ref: (el) => {
@@ -2395,12 +2513,19 @@ function VolumeBundle(props) {
2395
2513
  role: "region",
2396
2514
  "aria-label": bundle.title,
2397
2515
  children: [
2398
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle-header__content", children: [
2399
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
2400
- bundle.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
2516
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle-header__content", children: [
2517
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
2518
+ bundle.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
2401
2519
  ] }) }),
2402
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle-product-row lb-bundle-product-row--volume", children: [
2403
- thumbImage && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2520
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2521
+ BundleCountdown,
2522
+ {
2523
+ endsAt: bundle.endsAt,
2524
+ widgetConfig: bundle.widgetConfig
2525
+ }
2526
+ ),
2527
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle-product-row lb-bundle-product-row--volume", children: [
2528
+ thumbImage && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2404
2529
  "img",
2405
2530
  {
2406
2531
  src: thumbImage.url,
@@ -2409,20 +2534,20 @@ function VolumeBundle(props) {
2409
2534
  loading: "lazy"
2410
2535
  }
2411
2536
  ),
2412
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle__product-info", children: [
2413
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
2414
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "lb-bundle__product-price", children: [
2537
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle__product-info", children: [
2538
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
2539
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "lb-bundle__product-price", children: [
2415
2540
  (0, import_core12.formatMoney)(basePrice, currency),
2416
2541
  " each"
2417
2542
  ] }),
2418
- unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }),
2419
- showPicker && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
2543
+ unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle__product-unit-price", children: unitPriceText }),
2544
+ showPicker && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
2420
2545
  const dropdownOptions = optionsFor(optionIndex).map((o) => ({
2421
2546
  value: o.value,
2422
2547
  label: o.value,
2423
2548
  disabled: o.disabled
2424
2549
  }));
2425
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2550
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2426
2551
  VariantDropdown,
2427
2552
  {
2428
2553
  options: dropdownOptions,
@@ -2435,37 +2560,47 @@ function VolumeBundle(props) {
2435
2560
  }) })
2436
2561
  ] })
2437
2562
  ] }),
2438
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2563
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2439
2564
  "div",
2440
2565
  {
2441
2566
  className: "lb-bundle__tiers lb-edge-fade",
2442
- role: "table",
2443
- "aria-label": "Volume discounts",
2567
+ role: "radiogroup",
2568
+ "aria-label": "Select quantity",
2444
2569
  ref: tiersEdgeFadeRef,
2445
2570
  children: tierSavings.map((ts, tierIndex) => {
2446
2571
  const savingsPercent = Math.round(ts.savingsPercent);
2447
2572
  const showCompare = bundle.widgetConfig.pricing.showComparePrice && ts.savings > 0;
2448
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2573
+ const isChecked = activeTier === ts.tier;
2574
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2449
2575
  "div",
2450
2576
  {
2451
2577
  className: `lb-bundle__tier ${ts.isActive ? "lb-bundle__tier--active" : ""}`,
2452
- role: "row",
2578
+ role: "radio",
2579
+ "aria-checked": isChecked,
2580
+ tabIndex: isChecked ? 0 : -1,
2581
+ onClick: () => setQuantity(ts.tier.minQuantity),
2582
+ onKeyDown: (e) => {
2583
+ if (e.key === "Enter" || e.key === " ") {
2584
+ e.preventDefault();
2585
+ setQuantity(ts.tier.minQuantity);
2586
+ }
2587
+ },
2453
2588
  children: [
2454
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle__tier-radio", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle__tier-radio-dot" }) }),
2455
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle__tier-info", role: "cell", children: [
2456
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle__tier-label", children: [
2589
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle__tier-radio", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle__tier-radio-dot" }) }),
2590
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle__tier-info", children: [
2591
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle__tier-label", children: [
2457
2592
  "Buy ",
2458
2593
  ts.tier.minQuantity
2459
2594
  ] }),
2460
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle__tier-price", children: [
2461
- showCompare && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle__tier-compare", children: (0, import_core12.formatMoney)(basePrice, currency) }),
2462
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: (0, import_core12.formatMoney)(ts.unitPrice, currency) }),
2463
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle__tier-unit", children: "each" })
2595
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle__tier-price", children: [
2596
+ showCompare && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle__tier-compare", children: (0, import_core12.formatMoney)(basePrice, currency) }),
2597
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: (0, import_core12.formatMoney)(ts.unitPrice, currency) }),
2598
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle__tier-unit", children: "each" })
2464
2599
  ] })
2465
2600
  ] }),
2466
- (tierIndex === popularTierIndex || savingsPercent > 0) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle__tier-right", role: "cell", children: [
2467
- tierIndex === popularTierIndex && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle__tier-badge", children: bundle.widgetConfig.popularBadge.text }),
2468
- savingsPercent > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle__tier-savings", children: [
2601
+ (tierIndex === popularTierIndex || savingsPercent > 0) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle__tier-right", children: [
2602
+ tierIndex === popularTierIndex && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle__tier-badge", children: bundle.widgetConfig.popularBadge.text }),
2603
+ savingsPercent > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle__tier-savings", children: [
2469
2604
  "Save ",
2470
2605
  savingsPercent,
2471
2606
  "%"
@@ -2478,10 +2613,10 @@ function VolumeBundle(props) {
2478
2613
  })
2479
2614
  }
2480
2615
  ),
2481
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle__quantity-selector", children: [
2482
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: `lb-qty-${bundle.id}`, children: "Quantity" }),
2483
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle__quantity-control", children: [
2484
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2616
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle__quantity-selector", children: [
2617
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: `lb-qty-${bundle.id}`, children: "Quantity" }),
2618
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle__quantity-control", children: [
2619
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2485
2620
  "button",
2486
2621
  {
2487
2622
  "aria-label": "Decrease quantity",
@@ -2489,7 +2624,7 @@ function VolumeBundle(props) {
2489
2624
  children: "\u2212"
2490
2625
  }
2491
2626
  ),
2492
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2627
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2493
2628
  "input",
2494
2629
  {
2495
2630
  id: `lb-qty-${bundle.id}`,
@@ -2503,7 +2638,7 @@ function VolumeBundle(props) {
2503
2638
  className: "lb-bundle__quantity-input"
2504
2639
  }
2505
2640
  ),
2506
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2641
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2507
2642
  "button",
2508
2643
  {
2509
2644
  "aria-label": "Increase quantity",
@@ -2513,23 +2648,23 @@ function VolumeBundle(props) {
2513
2648
  )
2514
2649
  ] })
2515
2650
  ] }),
2516
- cartError && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
2651
+ cartError && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
2517
2652
  bundle && displayVariant && (0, import_core12.shouldShowLowStockBadge)(
2518
2653
  displayVariant,
2519
2654
  minTierQty,
2520
2655
  bundle.widgetConfig.lowStockThreshold,
2521
2656
  bundle.widgetConfig.showLowStockBadge
2522
- ) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle-low-stock-badge", children: [
2657
+ ) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle-low-stock-badge", children: [
2523
2658
  "Only ",
2524
2659
  displayVariant.quantityAvailable,
2525
2660
  " left"
2526
2661
  ] }),
2527
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "lb-bundle-divider" }),
2528
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle-summary", children: [
2529
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "lb-bundle-summary__text", children: [
2530
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle-summary__label", "data-total-label": true, children: [
2662
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-bundle-divider" }),
2663
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle-summary", children: [
2664
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle-summary__text", children: [
2665
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle-summary__label", "data-total-label": true, children: [
2531
2666
  "Total",
2532
- bundle.widgetConfig.pricing.showItemCount && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { "data-item-count": true, children: [
2667
+ bundle.widgetConfig.pricing.showItemCount && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { "data-item-count": true, children: [
2533
2668
  " ",
2534
2669
  "(",
2535
2670
  quantity,
@@ -2538,24 +2673,24 @@ function VolumeBundle(props) {
2538
2673
  ")"
2539
2674
  ] })
2540
2675
  ] }),
2541
- bundle.widgetConfig.savingsBar.visible && volumeSavings > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
2676
+ bundle.widgetConfig.savingsBar.visible && volumeSavings > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
2542
2677
  "You save",
2543
2678
  " ",
2544
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "data-savings-amount": true, children: (0, import_core12.formatMoney)(volumeSavings, currency) }),
2679
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "data-savings-amount": true, children: (0, import_core12.formatMoney)(volumeSavings, currency) }),
2545
2680
  " ",
2546
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { "data-savings-percent": true, children: [
2681
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { "data-savings-percent": true, children: [
2547
2682
  "(",
2548
2683
  volumeSavingsPercent,
2549
2684
  "%)"
2550
2685
  ] })
2551
2686
  ] })
2552
2687
  ] }),
2553
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
2554
- bundle.widgetConfig.pricing.showComparePrice && volumeSavings > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core12.formatMoney)(undiscountedTotal, currency) }),
2555
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "lb-bundle-sale-price", "data-total-price": true, children: (0, import_core12.formatMoney)(volumeTotal, currency) })
2688
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
2689
+ bundle.widgetConfig.pricing.showComparePrice && volumeSavings > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core12.formatMoney)(undiscountedTotal, currency) }),
2690
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-sale-price", "data-total-price": true, children: (0, import_core12.formatMoney)(volumeTotal, currency) })
2556
2691
  ] })
2557
2692
  ] }),
2558
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2693
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2559
2694
  "button",
2560
2695
  {
2561
2696
  className: "lb-bundle__cta",
@@ -2571,9 +2706,9 @@ function VolumeBundle(props) {
2571
2706
  }
2572
2707
 
2573
2708
  // src/components/BogoBundle.tsx
2574
- var import_react14 = require("react");
2709
+ var import_react15 = require("react");
2575
2710
  var import_core13 = require("@lime-bundles/core");
2576
- var import_jsx_runtime6 = require("react/jsx-runtime");
2711
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2577
2712
  function discountedUnit(price, percent) {
2578
2713
  const cents = Math.round(price * 100);
2579
2714
  const discounted = cents - Math.floor(cents * percent / 100);
@@ -2602,10 +2737,10 @@ function BogoBundle(props) {
2602
2737
  bundleType: "bogo",
2603
2738
  enabled: analyticsEnabled !== false
2604
2739
  });
2605
- const [addingToCart, setAddingToCart] = (0, import_react14.useState)(false);
2606
- const [cartError, setCartError] = (0, import_react14.useState)(null);
2607
- const [selectedVariants, setSelectedVariants] = (0, import_react14.useState)({ buy: null, get: null });
2608
- const handleVariantChange = (0, import_react14.useCallback)(
2740
+ const [addingToCart, setAddingToCart] = (0, import_react15.useState)(false);
2741
+ const [cartError, setCartError] = (0, import_react15.useState)(null);
2742
+ const [selectedVariants, setSelectedVariants] = (0, import_react15.useState)({ buy: null, get: null });
2743
+ const handleVariantChange = (0, import_react15.useCallback)(
2609
2744
  (role, variant) => {
2610
2745
  setSelectedVariants((prev) => {
2611
2746
  if (prev[role] === variant) return prev;
@@ -2616,7 +2751,7 @@ function BogoBundle(props) {
2616
2751
  );
2617
2752
  const bundle = result.status === "success" && result.bundle.bundleType === "bogo" ? result.bundle : null;
2618
2753
  const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
2619
- (0, import_react14.useEffect)(() => {
2754
+ (0, import_react15.useEffect)(() => {
2620
2755
  if (result.status === "error") {
2621
2756
  onError?.(result.error);
2622
2757
  return;
@@ -2629,28 +2764,28 @@ function BogoBundle(props) {
2629
2764
  );
2630
2765
  }
2631
2766
  }, [result, onError]);
2632
- const buyProduct = (0, import_react14.useMemo)(
2767
+ const buyProduct = (0, import_react15.useMemo)(
2633
2768
  () => bundle?.products.find((p) => p.id === bundle.buyProductId) ?? null,
2634
2769
  [bundle]
2635
2770
  );
2636
- const getProduct = (0, import_react14.useMemo)(
2771
+ const getProduct = (0, import_react15.useMemo)(
2637
2772
  () => bundle?.products.find((p) => p.id === bundle.getProductId) ?? null,
2638
2773
  [bundle]
2639
2774
  );
2640
2775
  const percent = bundle ? Math.min(100, Math.max(0, bundle.discountConfig.discountValue)) : 0;
2641
- const resolveVariant = (0, import_react14.useCallback)(
2776
+ const resolveVariant = (0, import_react15.useCallback)(
2642
2777
  (product, selected, qty) => {
2643
2778
  if (!product) return null;
2644
2779
  return selected ?? product.variants.nodes.find((v) => (0, import_core13.isVariantFulfillable)(v, qty)) ?? product.variants.nodes[0] ?? null;
2645
2780
  },
2646
2781
  []
2647
2782
  );
2648
- const oosSides = (0, import_react14.useMemo)(() => {
2783
+ const oosSides = (0, import_react15.useMemo)(() => {
2649
2784
  if (!bundle) return 0;
2650
2785
  const short = (product, qty) => !!product && !product.variants.nodes.some((v) => (0, import_core13.isVariantFulfillable)(v, qty));
2651
2786
  return (short(buyProduct, bundle.buyQuantity) ? 1 : 0) + (short(getProduct, bundle.getQuantity) ? 1 : 0);
2652
2787
  }, [bundle, buyProduct, getProduct]);
2653
- const handleAddToCart = (0, import_react14.useCallback)(async () => {
2788
+ const handleAddToCart = (0, import_react15.useCallback)(async () => {
2654
2789
  if (!bundle || !buyProduct || !getProduct) return;
2655
2790
  const buyVariant2 = resolveVariant(buyProduct, selectedVariants.buy, bundle.buyQuantity);
2656
2791
  const getVariant2 = resolveVariant(getProduct, selectedVariants.get, bundle.getQuantity);
@@ -2697,9 +2832,9 @@ function BogoBundle(props) {
2697
2832
  }
2698
2833
  }, [bundle, buyProduct, getProduct, selectedVariants, resolveVariant, onAddToCart, onError, trackAddToCart, percent]);
2699
2834
  if (result.status === "loading") {
2700
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
2701
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
2702
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
2835
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
2836
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
2837
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
2703
2838
  ] });
2704
2839
  }
2705
2840
  if (result.status === "error") return null;
@@ -2716,7 +2851,7 @@ function BogoBundle(props) {
2716
2851
  const showCompare = bundle.widgetConfig.pricing.showComparePrice && savings > 0;
2717
2852
  const showSavings = bundle.widgetConfig.savingsBar.visible && savings > 0;
2718
2853
  const badge = percent === 100 ? "Free" : `${percent}% off`;
2719
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2854
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2720
2855
  "div",
2721
2856
  {
2722
2857
  ref: (el) => {
@@ -2727,12 +2862,19 @@ function BogoBundle(props) {
2727
2862
  role: "region",
2728
2863
  "aria-label": bundle.title,
2729
2864
  children: [
2730
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle-header__content", children: [
2731
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
2732
- bundle.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
2865
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-bundle-header__content", children: [
2866
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
2867
+ bundle.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
2733
2868
  ] }) }),
2734
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle__products", children: [
2735
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2869
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2870
+ BundleCountdown,
2871
+ {
2872
+ endsAt: bundle.endsAt,
2873
+ widgetConfig: bundle.widgetConfig
2874
+ }
2875
+ ),
2876
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-bundle__products", children: [
2877
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2736
2878
  BogoProductRow,
2737
2879
  {
2738
2880
  side: "buy",
@@ -2744,11 +2886,11 @@ function BogoBundle(props) {
2744
2886
  onVariantChange: handleVariantChange
2745
2887
  }
2746
2888
  ),
2747
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-bogo__plus", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "12", height: "12", viewBox: "0 0 18 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2748
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "9", y1: "3", x2: "9", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2749
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "3", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
2889
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-bogo__plus", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "12", height: "12", viewBox: "0 0 18 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2890
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "9", y1: "3", x2: "9", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2891
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "3", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
2750
2892
  ] }) }),
2751
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2893
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2752
2894
  BogoProductRow,
2753
2895
  {
2754
2896
  side: "get",
@@ -2761,29 +2903,29 @@ function BogoBundle(props) {
2761
2903
  }
2762
2904
  )
2763
2905
  ] }),
2764
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-bundle-divider" }),
2765
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle-summary", children: [
2766
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle-summary__text", children: [
2767
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-summary__label", children: "Bundle price" }),
2768
- showSavings && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
2906
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-bundle-divider" }),
2907
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-bundle-summary", children: [
2908
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-bundle-summary__text", children: [
2909
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle-summary__label", children: "Bundle total" }),
2910
+ showSavings && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
2769
2911
  "You save",
2770
2912
  " ",
2771
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "data-savings-amount": true, children: (0, import_core13.formatMoney)(savings, currency) }),
2913
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-savings-amount": true, children: (0, import_core13.formatMoney)(savings, currency) }),
2772
2914
  " ",
2773
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { "data-savings-percent": true, children: [
2915
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { "data-savings-percent": true, children: [
2774
2916
  "(",
2775
2917
  savingsPercent,
2776
2918
  "%)"
2777
2919
  ] })
2778
2920
  ] })
2779
2921
  ] }),
2780
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
2781
- showCompare && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core13.formatMoney)(comparePrice, currency) }),
2782
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core13.formatMoney)(salePrice, currency) })
2922
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
2923
+ showCompare && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core13.formatMoney)(comparePrice, currency) }),
2924
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core13.formatMoney)(salePrice, currency) })
2783
2925
  ] })
2784
2926
  ] }),
2785
- cartError && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
2786
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2927
+ cartError && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
2928
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2787
2929
  "button",
2788
2930
  {
2789
2931
  className: "lb-bundle__cta",
@@ -2799,7 +2941,7 @@ function BogoBundle(props) {
2799
2941
  }
2800
2942
  function BogoProductRow({ side, product, quantity, percent, badge, currency, onVariantChange }) {
2801
2943
  const variants = product.variants.nodes;
2802
- const optionNames = (0, import_react14.useMemo)(() => {
2944
+ const optionNames = (0, import_react15.useMemo)(() => {
2803
2945
  const first = variants[0];
2804
2946
  return first ? first.selectedOptions.map((o) => o.name) : [];
2805
2947
  }, [variants]);
@@ -2808,7 +2950,7 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
2808
2950
  variants,
2809
2951
  optionNames
2810
2952
  });
2811
- (0, import_react14.useEffect)(() => {
2953
+ (0, import_react15.useEffect)(() => {
2812
2954
  onVariantChange(side, selectedVariant ?? null);
2813
2955
  }, [selectedVariant, side, onVariantChange]);
2814
2956
  const displayVariant = selectedVariant ?? variants.find((v) => (0, import_core13.isVariantFulfillable)(v, quantity)) ?? variants[0];
@@ -2822,8 +2964,8 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
2822
2964
  currency
2823
2965
  ) : null;
2824
2966
  const thumbImage = displayVariant?.image ?? product.featuredImage;
2825
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle-product-row", part: "product", "data-bogo-role": side, children: [
2826
- thumbImage && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2967
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-bundle-product-row", part: "product", "data-bogo-role": side, children: [
2968
+ thumbImage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2827
2969
  "img",
2828
2970
  {
2829
2971
  src: thumbImage.url,
@@ -2832,11 +2974,11 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
2832
2974
  loading: "lazy"
2833
2975
  }
2834
2976
  ),
2835
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lb-bundle__product-info", children: [
2836
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
2837
- !showPicker && displayVariant && variants.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle-variant-badge", children: displayVariant.title }),
2838
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle-product-prices", children: [
2839
- compareAt && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2977
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-bundle__product-info", children: [
2978
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "lb-bundle__product-title", children: product.title }),
2979
+ !showPicker && displayVariant && variants.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle-variant-badge", children: displayVariant.title }),
2980
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "lb-bundle-product-prices", children: [
2981
+ compareAt && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2840
2982
  "span",
2841
2983
  {
2842
2984
  className: "lb-bundle-product-compare-price",
@@ -2844,13 +2986,13 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
2844
2986
  children: compareAt
2845
2987
  }
2846
2988
  ),
2847
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bundle__product-price", "data-product-price": true, children: priceText }),
2848
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "lb-bundle-qty-inline", "data-qty-inline": true, children: [
2989
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle__product-price", "data-product-price": true, children: priceText }),
2990
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "lb-bundle-qty-inline", "data-qty-inline": true, children: [
2849
2991
  "\xD7",
2850
2992
  quantity
2851
2993
  ] })
2852
2994
  ] }),
2853
- unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2995
+ unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2854
2996
  "span",
2855
2997
  {
2856
2998
  className: "lb-bundle__product-unit-price",
@@ -2858,13 +3000,13 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
2858
3000
  children: unitPriceText
2859
3001
  }
2860
3002
  ),
2861
- showPicker && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
3003
+ showPicker && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-bundle__product-variant-pickers", children: optionNames.map((optionName, optionIndex) => {
2862
3004
  const dropdownOptions = optionsFor(optionIndex).map((o) => ({
2863
3005
  value: o.value,
2864
3006
  label: o.value,
2865
3007
  disabled: o.disabled
2866
3008
  }));
2867
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3009
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2868
3010
  VariantDropdown,
2869
3011
  {
2870
3012
  options: dropdownOptions,
@@ -2876,22 +3018,29 @@ function BogoProductRow({ side, product, quantity, percent, badge, currency, onV
2876
3018
  );
2877
3019
  }) })
2878
3020
  ] }),
2879
- badge && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "lb-bogo__badge", children: badge })
3021
+ badge && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bogo__badge", children: badge })
2880
3022
  ] });
2881
3023
  }
2882
3024
 
2883
3025
  // src/components/MultiStepBundle.tsx
2884
- var import_react16 = require("react");
3026
+ var import_react17 = require("react");
2885
3027
  var import_core15 = require("@lime-bundles/core");
2886
3028
 
2887
3029
  // src/components/MultiStepPicker.tsx
2888
- var import_react15 = require("react");
3030
+ var import_react16 = require("react");
2889
3031
  var import_react_dom2 = require("react-dom");
2890
3032
  var import_core14 = require("@lime-bundles/core");
2891
- var import_jsx_runtime7 = require("react/jsx-runtime");
2892
- function ruleFor3(bundle, productId) {
3033
+ var import_jsx_runtime8 = require("react/jsx-runtime");
3034
+ function ruleFor3(bundle, productId, variantId) {
3035
+ if (variantId) {
3036
+ const vRule = bundle.variantRules[variantId];
3037
+ if (vRule) return vRule;
3038
+ }
2893
3039
  return bundle.productRules[productId] ?? import_core14.DEFAULT_PRODUCT_RULE;
2894
3040
  }
3041
+ function variantRuleFor3(bundle, variantId) {
3042
+ return variantId ? bundle.variantRules[variantId] : void 0;
3043
+ }
2895
3044
  function sanitizeId2(gid) {
2896
3045
  return gid.replace(/[^a-zA-Z0-9_-]/g, "-");
2897
3046
  }
@@ -2935,17 +3084,17 @@ function MultiStepPicker({
2935
3084
  }) {
2936
3085
  const wc = bundle.widgetConfig;
2937
3086
  const steps = bundle.steps;
2938
- const overlayRef = (0, import_react15.useRef)(null);
2939
- const dialogRef = (0, import_react15.useRef)(null);
2940
- const titleRef = (0, import_react15.useRef)(null);
2941
- const mouseDownTarget = (0, import_react15.useRef)(null);
2942
- const [open, setOpen] = (0, import_react15.useState)(false);
2943
- const [stepIndex, setStepIndex] = (0, import_react15.useState)(
3087
+ const overlayRef = (0, import_react16.useRef)(null);
3088
+ const dialogRef = (0, import_react16.useRef)(null);
3089
+ const titleRef = (0, import_react16.useRef)(null);
3090
+ const mouseDownTarget = (0, import_react16.useRef)(null);
3091
+ const [open, setOpen] = (0, import_react16.useState)(false);
3092
+ const [stepIndex, setStepIndex] = (0, import_react16.useState)(
2944
3093
  () => Math.max(0, Math.min(steps.length - 1, initialStep))
2945
3094
  );
2946
- const [query, setQuery] = (0, import_react15.useState)("");
2947
- const [activeType, setActiveType] = (0, import_react15.useState)(FILTERS_ALL2);
2948
- const [announcement, setAnnouncement] = (0, import_react15.useState)("");
3095
+ const [query, setQuery] = (0, import_react16.useState)("");
3096
+ const [activeType, setActiveType] = (0, import_react16.useState)(FILTERS_ALL2);
3097
+ const [announcement, setAnnouncement] = (0, import_react16.useState)("");
2949
3098
  const step = steps[stepIndex];
2950
3099
  const stepMin = step?.minQuantity ?? 1;
2951
3100
  const stepPicks = selections[stepIndex] ?? [];
@@ -2953,11 +3102,11 @@ function MultiStepPicker({
2953
3102
  const stepIsSatisfied = unitsInStep >= stepMin;
2954
3103
  const isLastStep = stepIndex === steps.length - 1;
2955
3104
  const shownUnits = Math.min(unitsInStep, stepMin);
2956
- const eligible = (0, import_react15.useMemo)(
3105
+ const eligible = (0, import_react16.useMemo)(
2957
3106
  () => buildEligibleProducts2(bundle, stepIndex, outOfStockBehavior),
2958
3107
  [bundle, stepIndex, outOfStockBehavior]
2959
3108
  );
2960
- const productTypes = (0, import_react15.useMemo)(() => {
3109
+ const productTypes = (0, import_react16.useMemo)(() => {
2961
3110
  const types = [];
2962
3111
  const seen = /* @__PURE__ */ new Set();
2963
3112
  for (const ep of eligible) {
@@ -2970,7 +3119,7 @@ function MultiStepPicker({
2970
3119
  return types;
2971
3120
  }, [eligible]);
2972
3121
  const showFilters = wc.mixMatchShowTypeFilters && productTypes.length >= 2;
2973
- (0, import_react15.useEffect)(() => {
3122
+ (0, import_react16.useEffect)(() => {
2974
3123
  const id = requestAnimationFrame(() => setOpen(true));
2975
3124
  return () => cancelAnimationFrame(id);
2976
3125
  }, []);
@@ -2994,7 +3143,7 @@ function MultiStepPicker({
2994
3143
  titleRef.current?.focus();
2995
3144
  }
2996
3145
  const subtitle = `Step ${stepIndex + 1} of ${steps.length} \xB7 ${step?.name ?? ""}`;
2997
- const filtered = (0, import_react15.useMemo)(() => {
3146
+ const filtered = (0, import_react16.useMemo)(() => {
2998
3147
  const nq = normalizeText2(query.trim());
2999
3148
  return eligible.filter((ep) => {
3000
3149
  const matchesQuery = !nq || normalizeText2(ep.product.title).includes(nq);
@@ -3019,7 +3168,7 @@ function MultiStepPicker({
3019
3168
  // inside carries the interactive role + focus trap. So the static-element
3020
3169
  // interaction lint does not apply to this backdrop.
3021
3170
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
3022
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3171
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3023
3172
  "div",
3024
3173
  {
3025
3174
  ref: (el) => {
@@ -3031,7 +3180,7 @@ function MultiStepPicker({
3031
3180
  "data-bundle-gid": bundle.id,
3032
3181
  onMouseDown: onOverlayMouseDown,
3033
3182
  onMouseUp: onOverlayMouseUp,
3034
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3183
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3035
3184
  "div",
3036
3185
  {
3037
3186
  ref: dialogRef,
@@ -3041,10 +3190,10 @@ function MultiStepPicker({
3041
3190
  "aria-labelledby": titleId,
3042
3191
  tabIndex: -1,
3043
3192
  children: [
3044
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-mix-match__modal-header", children: [
3045
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-mix-match__modal-header-top", children: [
3046
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-mix-match__modal-heading", children: [
3047
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3193
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__modal-header", children: [
3194
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__modal-header-top", children: [
3195
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__modal-heading", children: [
3196
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3048
3197
  "h4",
3049
3198
  {
3050
3199
  className: "lb-mix-match__modal-title",
@@ -3054,9 +3203,9 @@ function MultiStepPicker({
3054
3203
  children: "Add to your bundle"
3055
3204
  }
3056
3205
  ),
3057
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "lb-mix-match__modal-subtitle", "data-modal-subtitle": true, children: subtitle })
3206
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "lb-mix-match__modal-subtitle", "data-modal-subtitle": true, children: subtitle })
3058
3207
  ] }),
3059
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3208
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3060
3209
  "button",
3061
3210
  {
3062
3211
  type: "button",
@@ -3064,16 +3213,16 @@ function MultiStepPicker({
3064
3213
  "data-modal-close": true,
3065
3214
  "aria-label": "Close",
3066
3215
  onClick: onClose,
3067
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CloseIcon3, {})
3216
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CloseIcon3, {})
3068
3217
  }
3069
3218
  )
3070
3219
  ] }),
3071
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3220
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3072
3221
  "div",
3073
3222
  {
3074
3223
  className: "lb-mix-match__progress lb-mix-match__modal-progress",
3075
3224
  "data-progress": true,
3076
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3225
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3077
3226
  "div",
3078
3227
  {
3079
3228
  className: "lb-mix-match__progress-segments",
@@ -3082,7 +3231,7 @@ function MultiStepPicker({
3082
3231
  "aria-valuenow": shownUnits,
3083
3232
  "aria-valuemin": 0,
3084
3233
  "aria-valuemax": stepMin,
3085
- children: Array.from({ length: stepMin }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3234
+ children: Array.from({ length: stepMin }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3086
3235
  "span",
3087
3236
  {
3088
3237
  className: `lb-mix-match__progress-segment${i < shownUnits ? " lb-mix-match__progress-segment--filled" : ""}`,
@@ -3095,8 +3244,8 @@ function MultiStepPicker({
3095
3244
  }
3096
3245
  )
3097
3246
  ] }),
3098
- wc.showSearch && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-mix-match__modal-search", children: [
3099
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3247
+ wc.showSearch && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__modal-search", children: [
3248
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3100
3249
  "input",
3101
3250
  {
3102
3251
  type: "text",
@@ -3110,7 +3259,7 @@ function MultiStepPicker({
3110
3259
  onChange: (e) => setQuery(e.target.value)
3111
3260
  }
3112
3261
  ),
3113
- query.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3262
+ query.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3114
3263
  "button",
3115
3264
  {
3116
3265
  type: "button",
@@ -3118,11 +3267,11 @@ function MultiStepPicker({
3118
3267
  "data-modal-search-clear": true,
3119
3268
  "aria-label": "Clear search",
3120
3269
  onClick: () => setQuery(""),
3121
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SearchClearIcon2, {})
3270
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SearchClearIcon2, {})
3122
3271
  }
3123
3272
  )
3124
3273
  ] }),
3125
- showFilters && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3274
+ showFilters && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3126
3275
  "div",
3127
3276
  {
3128
3277
  className: "lb-mix-match__filters",
@@ -3131,7 +3280,7 @@ function MultiStepPicker({
3131
3280
  "aria-label": "Filter by product type",
3132
3281
  children: [FILTERS_ALL2, ...productTypes].map((value) => {
3133
3282
  const isActive = value === activeType;
3134
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3283
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3135
3284
  "button",
3136
3285
  {
3137
3286
  type: "button",
@@ -3146,7 +3295,7 @@ function MultiStepPicker({
3146
3295
  })
3147
3296
  }
3148
3297
  ),
3149
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-mix-match__modal-list", "data-modal-list": true, children: filtered.map((ep) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3298
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-mix-match__modal-list", "data-modal-list": true, children: filtered.map((ep) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3150
3299
  MultiStepPickerRow,
3151
3300
  {
3152
3301
  bundle,
@@ -3164,10 +3313,10 @@ function MultiStepPicker({
3164
3313
  },
3165
3314
  `${stepIndex}:${ep.product.id}`
3166
3315
  )) }),
3167
- showEmpty && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-mix-match__modal-empty", "data-modal-empty": true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: "No products match your search" }) }),
3168
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-modal-live": true, "aria-live": "polite", className: "lb-visually-hidden", children: announcement || (query.trim() ? `${filtered.length} products shown` : "") }),
3169
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-mix-match__modal-footer lb-multi-step__modal-footer", children: [
3170
- stepIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3316
+ showEmpty && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-mix-match__modal-empty", "data-modal-empty": true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "No products match your search" }) }),
3317
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "data-modal-live": true, "aria-live": "polite", className: "lb-visually-hidden", children: announcement || (query.trim() ? `${filtered.length} products shown` : "") }),
3318
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__modal-footer lb-multi-step__modal-footer", children: [
3319
+ stepIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3171
3320
  "button",
3172
3321
  {
3173
3322
  type: "button",
@@ -3177,7 +3326,7 @@ function MultiStepPicker({
3177
3326
  children: "Back"
3178
3327
  }
3179
3328
  ),
3180
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3329
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3181
3330
  "span",
3182
3331
  {
3183
3332
  className: "lb-mix-match__modal-footer-count",
@@ -3186,7 +3335,7 @@ function MultiStepPicker({
3186
3335
  children: `${shownUnits} of ${stepMin} added`
3187
3336
  }
3188
3337
  ),
3189
- !isLastStep && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3338
+ !isLastStep && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3190
3339
  "button",
3191
3340
  {
3192
3341
  type: "button",
@@ -3199,7 +3348,7 @@ function MultiStepPicker({
3199
3348
  children: "Next"
3200
3349
  }
3201
3350
  ),
3202
- isLastStep && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3351
+ isLastStep && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3203
3352
  "button",
3204
3353
  {
3205
3354
  type: "button",
@@ -3237,25 +3386,27 @@ function MultiStepPickerRow({
3237
3386
  const { product, variants, isOos } = eligible;
3238
3387
  const rule = ruleFor3(bundle, product.id);
3239
3388
  const step = bundle.steps[stepIndex];
3240
- const optionNames = (0, import_react15.useMemo)(() => {
3389
+ const optionNames = (0, import_react16.useMemo)(() => {
3241
3390
  const first = variants[0];
3242
3391
  return first ? first.selectedOptions.map((o) => o.name) : [];
3243
3392
  }, [variants]);
3244
3393
  const showPicker = variants.length > 1 && optionNames.length > 0;
3245
3394
  const { selectedValues, selectedVariant, setOptionValue, optionsFor } = useVariantSelection({ variants, optionNames });
3246
3395
  const currentVariant = selectedVariant ?? variants.find((v) => (0, import_core14.isVariantFulfillable)(v, rule.min)) ?? eligible.firstAvailableVariant ?? variants[0] ?? null;
3247
- const stepPicks = (0, import_react15.useMemo)(
3396
+ const vRule = ruleFor3(bundle, product.id, currentVariant?.id);
3397
+ const variantMax = variantRuleFor3(bundle, currentVariant?.id)?.max;
3398
+ const stepPicks = (0, import_react16.useMemo)(
3248
3399
  () => selections[stepIndex] ?? [],
3249
3400
  [selections, stepIndex]
3250
3401
  );
3251
3402
  const unitsInStep = stepPicks.reduce((sum, s) => sum + s.quantity, 0);
3252
- const committedSelection = (0, import_react15.useMemo)(
3403
+ const committedSelection = (0, import_react16.useMemo)(
3253
3404
  () => currentVariant ? stepPicks.find((s) => s.variantId === currentVariant.id) ?? null : null,
3254
3405
  [stepPicks, currentVariant]
3255
3406
  );
3256
3407
  const variantInStep = committedSelection !== null;
3257
3408
  const committedQty = committedSelection?.quantity ?? 0;
3258
- const productOtherUnits = (0, import_react15.useMemo)(() => {
3409
+ const productOtherUnits = (0, import_react16.useMemo)(() => {
3259
3410
  if (!currentVariant) return 0;
3260
3411
  let sum = 0;
3261
3412
  for (let i = 0; i < selections.length; i++) {
@@ -3267,7 +3418,7 @@ function MultiStepPickerRow({
3267
3418
  }
3268
3419
  return sum;
3269
3420
  }, [selections, product.id, currentVariant, stepIndex]);
3270
- const variantOtherUnits = (0, import_react15.useMemo)(() => {
3421
+ const variantOtherUnits = (0, import_react16.useMemo)(() => {
3271
3422
  if (!currentVariant) return 0;
3272
3423
  let sum = 0;
3273
3424
  for (const picks of selections) {
@@ -3278,24 +3429,27 @@ function MultiStepPickerRow({
3278
3429
  return Math.max(0, sum - committedQty);
3279
3430
  }, [selections, currentVariant, committedQty]);
3280
3431
  const swapUnits = currentVariant ? swapUnitsFor?.(stepIndex, product.id, currentVariant.id) ?? 0 : 0;
3281
- const inSwapState = swapUnits >= rule.min;
3432
+ const inSwapState = swapUnits >= vRule.min;
3282
3433
  const room = (0, import_core14.stepHeadroom)(step?.maxQuantity ?? null, unitsInStep);
3283
- const productHeadroom = rule.max - productOtherUnits;
3434
+ const productHeadroom = Math.min(
3435
+ rule.max - productOtherUnits,
3436
+ variantMax ?? Infinity
3437
+ );
3284
3438
  const spotCeiling = inSwapState ? swapUnits : Math.min(
3285
3439
  productHeadroom,
3286
3440
  room === Number.POSITIVE_INFINITY ? productHeadroom : committedQty + room
3287
3441
  );
3288
3442
  const stockRemaining = currentVariant ? (0, import_core14.maxAddableQuantity)(currentVariant, Number.MAX_SAFE_INTEGER, variantOtherUnits) : 0;
3289
3443
  const cap = Math.max(0, Math.min(spotCeiling, stockRemaining));
3290
- const stepperMax = Math.max(rule.min, cap);
3291
- const [qty, setQty] = (0, import_react15.useState)(rule.min);
3292
- (0, import_react15.useEffect)(() => {
3293
- setQty((prev) => Math.max(rule.min, Math.min(prev, stepperMax)));
3294
- }, [stepperMax, rule.min]);
3444
+ const stepperMax = Math.max(vRule.min, cap);
3445
+ const [qty, setQty] = (0, import_react16.useState)(vRule.min);
3446
+ (0, import_react16.useEffect)(() => {
3447
+ setQty((prev) => Math.max(vRule.min, Math.min(prev, stepperMax)));
3448
+ }, [stepperMax, vRule.min]);
3295
3449
  const currentVariantId = currentVariant?.id ?? null;
3296
- (0, import_react15.useEffect)(() => {
3297
- if (!variantInStep) setQty(rule.min);
3298
- }, [variantInStep, currentVariantId, rule.min]);
3450
+ (0, import_react16.useEffect)(() => {
3451
+ if (!variantInStep) setQty(vRule.min);
3452
+ }, [variantInStep, currentVariantId, vRule.min]);
3299
3453
  if (!currentVariant || !step) return null;
3300
3454
  const thumbImage = currentVariant.image ?? product.featuredImage ?? null;
3301
3455
  const unitPriceText = (0, import_core14.formatUnitPrice)(
@@ -3303,8 +3457,8 @@ function MultiStepPickerRow({
3303
3457
  currentVariant.unitPriceMeasurement,
3304
3458
  currency
3305
3459
  );
3306
- const noStock = cap < rule.min;
3307
- const needsMoreSpots = !variantInStep && !inSwapState && room !== Number.POSITIVE_INFINITY && rule.min > room;
3460
+ const noStock = cap < vRule.min;
3461
+ const needsMoreSpots = !variantInStep && !inSwapState && room !== Number.POSITIVE_INFINITY && vRule.min > room;
3308
3462
  const addDisabled = !variantInStep && (inSwapState ? noStock : noStock || needsMoreSpots);
3309
3463
  const lockedRequired = variantInStep && currentVariant != null && !(canRemoveVariant?.(currentVariant.id) ?? true);
3310
3464
  const stepperDisabled = isOos || !variantInStep && addDisabled;
@@ -3315,7 +3469,7 @@ function MultiStepPickerRow({
3315
3469
  return;
3316
3470
  }
3317
3471
  if (!currentVariant || noStock) return;
3318
- const pickedQty = showStepper ? qty : rule.min;
3472
+ const pickedQty = showStepper ? qty : vRule.min;
3319
3473
  const item = {
3320
3474
  productId: product.id,
3321
3475
  variantId: currentVariant.id,
@@ -3337,7 +3491,7 @@ function MultiStepPickerRow({
3337
3491
  }
3338
3492
  const priceCents = parseFloat(currentVariant.price.amount);
3339
3493
  const compareCents = currentVariant.compareAtPrice ? parseFloat(currentVariant.compareAtPrice.amount) : null;
3340
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3494
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3341
3495
  "div",
3342
3496
  {
3343
3497
  className: `lb-mix-match__modal-product${isOos ? " lb-mix-match__modal-product--sold-out" : ""}${variantInStep ? " lb-mix-match__modal-product--in-bundle" : ""}`,
@@ -3346,8 +3500,8 @@ function MultiStepPickerRow({
3346
3500
  "data-type": product.productType ?? "",
3347
3501
  "aria-disabled": isOos || void 0,
3348
3502
  children: [
3349
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-mix-match__modal-product-thumb", children: [
3350
- thumbImage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3503
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__modal-product-thumb", children: [
3504
+ thumbImage && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3351
3505
  "img",
3352
3506
  {
3353
3507
  src: thumbImage.url,
@@ -3355,10 +3509,10 @@ function MultiStepPickerRow({
3355
3509
  loading: "lazy"
3356
3510
  }
3357
3511
  ),
3358
- variantInStep && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-mix-match__modal-added-badge", children: "Added" })
3512
+ variantInStep && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-mix-match__modal-added-badge", children: "Added" })
3359
3513
  ] }),
3360
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-mix-match__modal-product-info", children: [
3361
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "lb-mix-match__modal-product-title", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3514
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__modal-product-info", children: [
3515
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "lb-mix-match__modal-product-title", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3362
3516
  "a",
3363
3517
  {
3364
3518
  href: `/products/${product.handle}`,
@@ -3367,24 +3521,24 @@ function MultiStepPickerRow({
3367
3521
  children: product.title
3368
3522
  }
3369
3523
  ) }),
3370
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "lb-mix-match__modal-product-price", children: [
3371
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-row-price-sale": true, children: (0, import_core14.formatMoney)(priceCents, currency) }),
3372
- compareCents !== null && compareCents > priceCents && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("s", { className: "lb-mix-match__modal-product-compare", "data-row-compare": true, children: (0, import_core14.formatMoney)(compareCents, currency) })
3524
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { className: "lb-mix-match__modal-product-price", children: [
3525
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "data-row-price-sale": true, children: (0, import_core14.formatMoney)(priceCents, currency) }),
3526
+ compareCents !== null && compareCents > priceCents && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("s", { className: "lb-mix-match__modal-product-compare", "data-row-compare": true, children: (0, import_core14.formatMoney)(compareCents, currency) })
3373
3527
  ] }),
3374
- unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price", children: unitPriceText }),
3375
- showPicker && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-bundle-variant-option-groups", children: optionNames.map((optionName, optionIndex) => {
3528
+ unitPriceText && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price", children: unitPriceText }),
3529
+ showPicker && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-bundle-variant-option-groups", children: optionNames.map((optionName, optionIndex) => {
3376
3530
  const dropdownOptions = optionsFor(optionIndex).map((o) => ({
3377
3531
  value: o.value,
3378
3532
  label: o.value,
3379
3533
  disabled: o.disabled
3380
3534
  }));
3381
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3535
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3382
3536
  "div",
3383
3537
  {
3384
3538
  className: "lb-bundle-variant-option-group",
3385
3539
  children: [
3386
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-bundle-variant-option-label", children: optionName }),
3387
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3540
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-bundle-variant-option-label", children: optionName }),
3541
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3388
3542
  VariantDropdown,
3389
3543
  {
3390
3544
  className: "lb-mix-match__variant-select-dropdown",
@@ -3399,39 +3553,39 @@ function MultiStepPickerRow({
3399
3553
  optionName
3400
3554
  );
3401
3555
  }) }),
3402
- !showPicker && variants.length === 1 && currentVariant.title !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-mix-match__filled-variant", children: currentVariant.title }),
3403
- !isOos && needsMoreSpots && room > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-mix-match__modal-needs-spots", children: rule.min === 1 ? "Needs 1 spot" : `Needs ${rule.min} spots` }),
3404
- isOos ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "lb-mix-match__modal-sold-out-label", children: "Sold out" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "lb-mix-match__modal-product-actions", children: [
3405
- showStepper && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "lb-bundle-variant-option-group lb-mix-match__qty-stepper-group", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3556
+ !showPicker && variants.length === 1 && currentVariant.title !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-mix-match__filled-variant", children: currentVariant.title }),
3557
+ !isOos && needsMoreSpots && room > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-mix-match__modal-needs-spots", children: vRule.min === 1 ? "Needs 1 spot" : `Needs ${vRule.min} spots` }),
3558
+ isOos ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-mix-match__modal-sold-out-label", children: "Sold out" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__modal-product-actions", children: [
3559
+ showStepper && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-bundle-variant-option-group lb-mix-match__qty-stepper-group", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3406
3560
  "div",
3407
3561
  {
3408
3562
  className: "lb-mix-match__qty-stepper",
3409
3563
  role: "group",
3410
3564
  "aria-label": "Quantity",
3411
3565
  children: [
3412
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3566
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3413
3567
  "button",
3414
3568
  {
3415
3569
  type: "button",
3416
3570
  className: "lb-mix-match__qty-stepper-button lb-mix-match__qty-stepper-button--minus",
3417
3571
  "aria-label": "Decrease quantity",
3418
- disabled: stepperDisabled || shownQty <= rule.min,
3572
+ disabled: stepperDisabled || shownQty <= vRule.min,
3419
3573
  onClick: () => {
3420
3574
  if (variantInStep && currentVariant) {
3421
3575
  onUpdateQuantity(
3422
3576
  stepIndex,
3423
3577
  product.id,
3424
3578
  currentVariant.id,
3425
- Math.max(rule.min, committedQty - 1)
3579
+ Math.max(vRule.min, committedQty - 1)
3426
3580
  );
3427
3581
  } else {
3428
- setQty((q) => Math.max(rule.min, q - 1));
3582
+ setQty((q) => Math.max(vRule.min, q - 1));
3429
3583
  }
3430
3584
  },
3431
3585
  children: "\u2212"
3432
3586
  }
3433
3587
  ),
3434
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3588
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3435
3589
  "span",
3436
3590
  {
3437
3591
  className: "lb-mix-match__qty-stepper-value",
@@ -3440,7 +3594,7 @@ function MultiStepPickerRow({
3440
3594
  children: shownQty
3441
3595
  }
3442
3596
  ),
3443
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3597
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3444
3598
  "button",
3445
3599
  {
3446
3600
  type: "button",
@@ -3465,7 +3619,7 @@ function MultiStepPickerRow({
3465
3619
  ]
3466
3620
  }
3467
3621
  ) }),
3468
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3622
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3469
3623
  "button",
3470
3624
  {
3471
3625
  type: "button",
@@ -3484,7 +3638,7 @@ function MultiStepPickerRow({
3484
3638
  );
3485
3639
  }
3486
3640
  function CloseIcon3() {
3487
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3641
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3488
3642
  "svg",
3489
3643
  {
3490
3644
  width: "20",
@@ -3494,14 +3648,14 @@ function CloseIcon3() {
3494
3648
  xmlns: "http://www.w3.org/2000/svg",
3495
3649
  "aria-hidden": "true",
3496
3650
  children: [
3497
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "5", y1: "5", x2: "15", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
3498
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "15", y1: "5", x2: "5", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
3651
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "5", y1: "5", x2: "15", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
3652
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "15", y1: "5", x2: "5", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
3499
3653
  ]
3500
3654
  }
3501
3655
  );
3502
3656
  }
3503
3657
  function SearchClearIcon2() {
3504
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3658
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3505
3659
  "svg",
3506
3660
  {
3507
3661
  width: "16",
@@ -3510,17 +3664,25 @@ function SearchClearIcon2() {
3510
3664
  fill: "currentColor",
3511
3665
  xmlns: "http://www.w3.org/2000/svg",
3512
3666
  "aria-hidden": "true",
3513
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M14.348 5.652a.5.5 0 0 0-.707 0L10 9.293 6.36 5.652a.5.5 0 1 0-.708.707L9.293 10l-3.641 3.641a.5.5 0 0 0 .708.707L10 10.707l3.641 3.641a.5.5 0 0 0 .707-.707L10.707 10l3.641-3.641a.5.5 0 0 0 0-.707z" })
3667
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M14.348 5.652a.5.5 0 0 0-.707 0L10 9.293 6.36 5.652a.5.5 0 1 0-.708.707L9.293 10l-3.641 3.641a.5.5 0 0 0 .708.707L10 10.707l3.641 3.641a.5.5 0 0 0 .707-.707L10.707 10l3.641-3.641a.5.5 0 0 0 0-.707z" })
3514
3668
  }
3515
3669
  );
3516
3670
  }
3517
3671
 
3518
3672
  // src/components/MultiStepBundle.tsx
3519
- var import_jsx_runtime8 = require("react/jsx-runtime");
3520
- function ruleFor4(bundle, productId) {
3673
+ var import_jsx_runtime9 = require("react/jsx-runtime");
3674
+ function ruleFor4(bundle, productId, variantId) {
3675
+ if (variantId) {
3676
+ const vRule = bundle.variantRules[variantId];
3677
+ if (vRule) return vRule;
3678
+ }
3521
3679
  return bundle.productRules[productId] ?? import_core15.DEFAULT_PRODUCT_RULE;
3522
3680
  }
3681
+ function variantRuleFor4(bundle, variantId) {
3682
+ return variantId ? bundle.variantRules[variantId] : void 0;
3683
+ }
3523
3684
  function canRemoveItem2(bundle, selections, item) {
3685
+ if (variantRuleFor4(bundle, item.variantId)?.required) return false;
3524
3686
  const rule = ruleFor4(bundle, item.productId);
3525
3687
  if (!rule.required) return true;
3526
3688
  let unitsElsewhere = 0;
@@ -3567,14 +3729,14 @@ function MultiStepBundle(props) {
3567
3729
  bundleType: "multi_step",
3568
3730
  enabled: analyticsEnabled !== false
3569
3731
  });
3570
- const [selections, setSelections] = (0, import_react16.useState)([]);
3571
- const [pickerOpenAt, setPickerOpenAt] = (0, import_react16.useState)(null);
3572
- const [addingToCart, setAddingToCart] = (0, import_react16.useState)(false);
3573
- const [cartError, setCartError] = (0, import_react16.useState)(null);
3732
+ const [selections, setSelections] = (0, import_react17.useState)([]);
3733
+ const [pickerOpenAt, setPickerOpenAt] = (0, import_react17.useState)(null);
3734
+ const [addingToCart, setAddingToCart] = (0, import_react17.useState)(false);
3735
+ const [cartError, setCartError] = (0, import_react17.useState)(null);
3574
3736
  const bundle = result.status === "success" && result.bundle.bundleType === "multi_step" ? result.bundle : null;
3575
3737
  const setConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
3576
3738
  const setModalConfigVarsRef = useWidgetConfigVars(bundle?.widgetConfig);
3577
- (0, import_react16.useEffect)(() => {
3739
+ (0, import_react17.useEffect)(() => {
3578
3740
  if (result.status === "error") {
3579
3741
  onError?.(result.error);
3580
3742
  return;
@@ -3587,8 +3749,8 @@ function MultiStepBundle(props) {
3587
3749
  );
3588
3750
  }
3589
3751
  }, [result, onError]);
3590
- const steps = (0, import_react16.useMemo)(() => bundle?.steps ?? [], [bundle]);
3591
- (0, import_react16.useEffect)(() => {
3752
+ const steps = (0, import_react17.useMemo)(() => bundle?.steps ?? [], [bundle]);
3753
+ (0, import_react17.useEffect)(() => {
3592
3754
  const seeded = steps.map(() => []);
3593
3755
  if (bundle) {
3594
3756
  const seedCurrency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
@@ -3623,26 +3785,26 @@ function MultiStepBundle(props) {
3623
3785
  setSelections(seeded);
3624
3786
  }, [bundle, steps]);
3625
3787
  const showStepper = bundle?.widgetConfig.mixMatchShowQuantitySelector !== false;
3626
- const totalMin = (0, import_react16.useMemo)(
3788
+ const totalMin = (0, import_react17.useMemo)(
3627
3789
  () => steps.reduce((sum, s) => sum + s.minQuantity, 0),
3628
3790
  [steps]
3629
3791
  );
3630
- const unitsByStep = (0, import_react16.useMemo)(
3792
+ const unitsByStep = (0, import_react17.useMemo)(
3631
3793
  () => selections.map((picks) => picks.reduce((sum, s) => sum + s.quantity, 0)),
3632
3794
  [selections]
3633
3795
  );
3634
- const totalQuantity = (0, import_react16.useMemo)(
3796
+ const totalQuantity = (0, import_react17.useMemo)(
3635
3797
  () => unitsByStep.reduce((sum, u) => sum + u, 0),
3636
3798
  [unitsByStep]
3637
3799
  );
3638
- const stepSatisfied = (0, import_react16.useCallback)(
3800
+ const stepSatisfied = (0, import_react17.useCallback)(
3639
3801
  (i) => (unitsByStep[i] ?? 0) >= (steps[i]?.minQuantity ?? 1),
3640
3802
  [unitsByStep, steps]
3641
3803
  );
3642
3804
  const allSatisfied = steps.length > 0 && steps.every((_, i) => stepSatisfied(i));
3643
3805
  const valid = !!bundle && allSatisfied;
3644
3806
  const completedSteps = steps.filter((_, i) => stepSatisfied(i)).length;
3645
- const summary = (0, import_react16.useMemo)(() => {
3807
+ const summary = (0, import_react17.useMemo)(() => {
3646
3808
  let comparePrice = 0;
3647
3809
  let salePrice = 0;
3648
3810
  for (const stepPicks of selections) {
@@ -3662,7 +3824,7 @@ function MultiStepBundle(props) {
3662
3824
  const savingsPercent = comparePrice > 0 && savings > 0 ? Math.round(savings / comparePrice * 100) : 0;
3663
3825
  return { comparePrice, salePrice, savings, savingsPercent };
3664
3826
  }, [selections, bundle]);
3665
- const addSelection = (0, import_react16.useCallback)((step, item) => {
3827
+ const addSelection = (0, import_react17.useCallback)((step, item) => {
3666
3828
  setSelections((prev) => {
3667
3829
  if (step < 0 || step >= prev.length) return prev;
3668
3830
  if (prev[step].some((s) => s.variantId === item.variantId)) return prev;
@@ -3671,7 +3833,7 @@ function MultiStepBundle(props) {
3671
3833
  return next;
3672
3834
  });
3673
3835
  }, []);
3674
- const swapSelection = (0, import_react16.useCallback)(
3836
+ const swapSelection = (0, import_react17.useCallback)(
3675
3837
  (step, item) => {
3676
3838
  setSelections((prev) => {
3677
3839
  if (!bundle || step < 0 || step >= prev.length) return prev;
@@ -3706,7 +3868,7 @@ function MultiStepBundle(props) {
3706
3868
  },
3707
3869
  [bundle]
3708
3870
  );
3709
- const updateQuantity = (0, import_react16.useCallback)(
3871
+ const updateQuantity = (0, import_react17.useCallback)(
3710
3872
  (step, productId, variantId, quantity) => {
3711
3873
  setSelections((prev) => {
3712
3874
  if (step < 0 || step >= prev.length) return prev;
@@ -3725,7 +3887,7 @@ function MultiStepBundle(props) {
3725
3887
  },
3726
3888
  []
3727
3889
  );
3728
- const removeVariant = (0, import_react16.useCallback)(
3890
+ const removeVariant = (0, import_react17.useCallback)(
3729
3891
  (step, variantId) => {
3730
3892
  setSelections((prev) => {
3731
3893
  if (step < 0 || step >= prev.length) return prev;
@@ -3741,7 +3903,7 @@ function MultiStepBundle(props) {
3741
3903
  },
3742
3904
  [bundle]
3743
3905
  );
3744
- const removeSlot = (0, import_react16.useCallback)(
3906
+ const removeSlot = (0, import_react17.useCallback)(
3745
3907
  (step, index) => {
3746
3908
  setSelections((prev) => {
3747
3909
  if (step < 0 || step >= prev.length) return prev;
@@ -3757,7 +3919,7 @@ function MultiStepBundle(props) {
3757
3919
  },
3758
3920
  [bundle]
3759
3921
  );
3760
- const handleAddToCart = (0, import_react16.useCallback)(async () => {
3922
+ const handleAddToCart = (0, import_react17.useCallback)(async () => {
3761
3923
  if (!bundle || !valid) return;
3762
3924
  const grouped = /* @__PURE__ */ new Map();
3763
3925
  for (const stepPicks of selections) {
@@ -3798,9 +3960,9 @@ function MultiStepBundle(props) {
3798
3960
  }
3799
3961
  }, [bundle, selections, valid, onAddToCart, onError, trackAddToCart, totalQuantity, steps]);
3800
3962
  if (result.status === "loading") {
3801
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
3802
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
3803
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
3963
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `lb-bundle lb-bundle--loading ${className ?? ""}`, children: [
3964
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "lb-skeleton lb-skeleton--title" }),
3965
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "lb-skeleton lb-skeleton--products" })
3804
3966
  ] });
3805
3967
  }
3806
3968
  if (result.status === "error") return null;
@@ -3821,7 +3983,7 @@ function MultiStepBundle(props) {
3821
3983
  if (requiredBlocked) return null;
3822
3984
  const currency = bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD";
3823
3985
  const ctaLabel = addingToCart ? "Adding..." : bundle.widgetConfig.cta.ctaText || "Add to cart";
3824
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3986
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3825
3987
  "div",
3826
3988
  {
3827
3989
  ref: (el) => {
@@ -3833,12 +3995,19 @@ function MultiStepBundle(props) {
3833
3995
  "aria-label": bundle.title,
3834
3996
  "data-required-quantity": totalMin,
3835
3997
  children: [
3836
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-bundle-header__content", children: [
3837
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
3838
- bundle.description && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
3998
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "lb-bundle-header", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "lb-bundle-header__content", children: [
3999
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "lb-bundle-title", children: bundle.title }),
4000
+ bundle.description && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "lb-bundle-subtitle", children: bundle.description })
3839
4001
  ] }) }),
3840
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__progress", "data-progress": true, children: [
3841
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4002
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4003
+ BundleCountdown,
4004
+ {
4005
+ endsAt: bundle.endsAt,
4006
+ widgetConfig: bundle.widgetConfig
4007
+ }
4008
+ ),
4009
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "lb-mix-match__progress", "data-progress": true, children: [
4010
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3842
4011
  "div",
3843
4012
  {
3844
4013
  className: "lb-mix-match__progress-segments",
@@ -3846,7 +4015,7 @@ function MultiStepBundle(props) {
3846
4015
  "aria-valuenow": completedSteps,
3847
4016
  "aria-valuemin": 0,
3848
4017
  "aria-valuemax": steps.length,
3849
- children: Array.from({ length: steps.length }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4018
+ children: Array.from({ length: steps.length }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3850
4019
  "span",
3851
4020
  {
3852
4021
  className: `lb-mix-match__progress-segment${i < completedSteps ? " lb-mix-match__progress-segment--filled" : ""}`,
@@ -3856,8 +4025,8 @@ function MultiStepBundle(props) {
3856
4025
  ))
3857
4026
  }
3858
4027
  ),
3859
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__progress-labels", children: [
3860
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4028
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "lb-mix-match__progress-labels", children: [
4029
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3861
4030
  "span",
3862
4031
  {
3863
4032
  className: "lb-mix-match__progress-count",
@@ -3866,7 +4035,7 @@ function MultiStepBundle(props) {
3866
4035
  children: `${completedSteps} of ${steps.length} steps completed`
3867
4036
  }
3868
4037
  ),
3869
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4038
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3870
4039
  "span",
3871
4040
  {
3872
4041
  className: "lb-mix-match__progress-remaining",
@@ -3877,15 +4046,15 @@ function MultiStepBundle(props) {
3877
4046
  )
3878
4047
  ] })
3879
4048
  ] }),
3880
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-multi-step__groups", "data-step-groups": true, children: steps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4049
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "lb-multi-step__groups", "data-step-groups": true, children: steps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3881
4050
  "section",
3882
4051
  {
3883
4052
  className: "lb-multi-step__group",
3884
4053
  "data-step-group": i,
3885
4054
  children: [
3886
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-multi-step__group-heading", children: [
3887
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-multi-step__group-name", children: step.name }),
3888
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4055
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "lb-multi-step__group-heading", children: [
4056
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-multi-step__group-name", children: step.name }),
4057
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3889
4058
  "span",
3890
4059
  {
3891
4060
  className: `lb-multi-step__group-count${stepSatisfied(i) ? " lb-multi-step__group-count--met" : ""}`,
@@ -3894,7 +4063,7 @@ function MultiStepBundle(props) {
3894
4063
  }
3895
4064
  )
3896
4065
  ] }),
3897
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4066
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3898
4067
  "div",
3899
4068
  {
3900
4069
  className: "lb-mix-match__slots lb-edge-fade lb-multi-step__group-slots",
@@ -3904,13 +4073,13 @@ function MultiStepBundle(props) {
3904
4073
  title button provides the keyboard/AT path (its
3905
4074
  activation bubbles here); × stops propagation. */
3906
4075
  // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- keyboard path is the inner "Edit selection" title button whose activation bubbles to this handler
3907
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4076
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3908
4077
  "div",
3909
4078
  {
3910
4079
  className: "lb-mix-match__slot lb-mix-match__slot--filled",
3911
4080
  onClick: () => setPickerOpenAt(i),
3912
4081
  children: [
3913
- item.featuredImage && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-bundle-thumbnail", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4082
+ item.featuredImage && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "lb-bundle-thumbnail", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3914
4083
  "img",
3915
4084
  {
3916
4085
  src: item.featuredImage,
@@ -3918,8 +4087,8 @@ function MultiStepBundle(props) {
3918
4087
  loading: "lazy"
3919
4088
  }
3920
4089
  ) }),
3921
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-mix-match__filled-info", children: [
3922
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4090
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "lb-mix-match__filled-info", children: [
4091
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3923
4092
  "button",
3924
4093
  {
3925
4094
  type: "button",
@@ -3928,15 +4097,15 @@ function MultiStepBundle(props) {
3928
4097
  children: item.title
3929
4098
  }
3930
4099
  ),
3931
- item.variantTitle && item.variantTitle !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-mix-match__filled-variant", children: item.variantTitle }),
3932
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "lb-mix-match__filled-price", children: [
3933
- item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("s", { className: "lb-mix-match__filled-compare", children: (0, import_core15.formatMoney)(item.compareAtPrice, currency) }),
3934
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-bundle-product-price", children: (0, import_core15.formatMoney)(item.price, currency) }),
3935
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-bundle-qty-inline", children: `\xD7${item.quantity}` })
4100
+ item.variantTitle && item.variantTitle !== "Default Title" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-mix-match__filled-variant", children: item.variantTitle }),
4101
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "lb-mix-match__filled-price", children: [
4102
+ item.compareAtPrice !== null && item.compareAtPrice > item.price && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("s", { className: "lb-mix-match__filled-compare", children: (0, import_core15.formatMoney)(item.compareAtPrice, currency) }),
4103
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-product-price", children: (0, import_core15.formatMoney)(item.price, currency) }),
4104
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-qty-inline", children: `\xD7${item.quantity}` })
3936
4105
  ] }),
3937
- item.unitPrice && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-bundle-product-unit-price", children: item.unitPrice })
4106
+ item.unitPrice && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-product-unit-price", children: item.unitPrice })
3938
4107
  ] }),
3939
- canRemoveItem2(bundle, selections, item) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4108
+ canRemoveItem2(bundle, selections, item) ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3940
4109
  "button",
3941
4110
  {
3942
4111
  type: "button",
@@ -3946,13 +4115,13 @@ function MultiStepBundle(props) {
3946
4115
  e.stopPropagation();
3947
4116
  removeSlot(i, index);
3948
4117
  },
3949
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CloseIcon4, {})
4118
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CloseIcon4, {})
3950
4119
  }
3951
4120
  ) : (
3952
4121
  /* Required pick at its floor — quiet state chip instead
3953
4122
  of the ×; removal returns once another slot covers
3954
4123
  the product's minimum (variant swap flow). */
3955
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-mix-match__slot-required", children: "Required" })
4124
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-mix-match__slot-required", children: "Required" })
3956
4125
  )
3957
4126
  ]
3958
4127
  },
@@ -3961,7 +4130,7 @@ function MultiStepBundle(props) {
3961
4130
  ))
3962
4131
  }
3963
4132
  ),
3964
- (0, import_core15.stepHeadroom)(step.maxQuantity, unitsByStep[i] ?? 0) !== 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4133
+ (0, import_core15.stepHeadroom)(step.maxQuantity, unitsByStep[i] ?? 0) !== 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3965
4134
  "button",
3966
4135
  {
3967
4136
  type: "button",
@@ -3970,15 +4139,15 @@ function MultiStepBundle(props) {
3970
4139
  "aria-label": `${step.minQuantity === 1 && step.maxQuantity === 1 ? "Choose a product" : "Choose products"}: ${step.name}`,
3971
4140
  onClick: () => setPickerOpenAt(i),
3972
4141
  children: [
3973
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4142
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3974
4143
  "span",
3975
4144
  {
3976
4145
  className: "lb-mix-match__add-product-icon",
3977
4146
  "aria-hidden": "true",
3978
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PlusIcon2, {})
4147
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PlusIcon2, {})
3979
4148
  }
3980
4149
  ),
3981
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-mix-match__add-product-label", children: step.minQuantity === 1 && step.maxQuantity === 1 ? "Choose a product" : "Choose products" })
4150
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-mix-match__add-product-label", children: step.minQuantity === 1 && step.maxQuantity === 1 ? "Choose a product" : "Choose products" })
3982
4151
  ]
3983
4152
  }
3984
4153
  )
@@ -3986,29 +4155,29 @@ function MultiStepBundle(props) {
3986
4155
  },
3987
4156
  i
3988
4157
  )) }),
3989
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lb-bundle-divider" }),
3990
- allSatisfied && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-bundle-summary", "data-pricing-section": true, children: [
3991
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lb-bundle-summary__text", children: [
3992
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-bundle-summary__label", children: "Bundle total" }),
3993
- bundle.widgetConfig.savingsBar.visible && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
4158
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "lb-bundle-divider" }),
4159
+ allSatisfied && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "lb-bundle-summary", "data-pricing-section": true, children: [
4160
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "lb-bundle-summary__text", children: [
4161
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-summary__label", children: "Bundle total" }),
4162
+ bundle.widgetConfig.savingsBar.visible && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { className: "lb-bundle-savings-line", "data-savings-bar": true, children: [
3994
4163
  "You save",
3995
4164
  " ",
3996
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "data-savings-amount": true, children: (0, import_core15.formatMoney)(summary.savings, currency) }),
4165
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-savings-amount": true, children: (0, import_core15.formatMoney)(summary.savings, currency) }),
3997
4166
  " ",
3998
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { "data-savings-percent": true, children: [
4167
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { "data-savings-percent": true, children: [
3999
4168
  "(",
4000
4169
  summary.savingsPercent,
4001
4170
  "%)"
4002
4171
  ] })
4003
4172
  ] })
4004
4173
  ] }),
4005
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
4006
- bundle.widgetConfig.pricing.showComparePrice && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core15.formatMoney)(summary.comparePrice, currency) }),
4007
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core15.formatMoney)(summary.salePrice, currency) })
4174
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "lb-bundle-summary__prices", children: [
4175
+ bundle.widgetConfig.pricing.showComparePrice && summary.savings > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-compare-price", "data-compare-price": true, children: (0, import_core15.formatMoney)(summary.comparePrice, currency) }),
4176
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "lb-bundle-sale-price", "data-sale-price": true, children: (0, import_core15.formatMoney)(summary.salePrice, currency) })
4008
4177
  ] })
4009
4178
  ] }),
4010
- cartError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
4011
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4179
+ cartError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "lb-bundle__error", role: "alert", children: cartError }),
4180
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4012
4181
  "button",
4013
4182
  {
4014
4183
  className: "lb-bundle__cta",
@@ -4018,7 +4187,7 @@ function MultiStepBundle(props) {
4018
4187
  children: ctaLabel
4019
4188
  }
4020
4189
  ),
4021
- pickerOpenAt !== null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4190
+ pickerOpenAt !== null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4022
4191
  MultiStepPicker,
4023
4192
  {
4024
4193
  outOfStockBehavior,
@@ -4054,7 +4223,7 @@ function stepHasInStockProduct(bundle, stepIndex) {
4054
4223
  });
4055
4224
  }
4056
4225
  function PlusIcon2() {
4057
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4226
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4058
4227
  "svg",
4059
4228
  {
4060
4229
  width: "18",
@@ -4064,14 +4233,14 @@ function PlusIcon2() {
4064
4233
  xmlns: "http://www.w3.org/2000/svg",
4065
4234
  "aria-hidden": "true",
4066
4235
  children: [
4067
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "9", y1: "3", x2: "9", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
4068
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "3", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
4236
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "9", y1: "3", x2: "9", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
4237
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "3", y1: "9", x2: "15", y2: "9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
4069
4238
  ]
4070
4239
  }
4071
4240
  );
4072
4241
  }
4073
4242
  function CloseIcon4() {
4074
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4243
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4075
4244
  "svg",
4076
4245
  {
4077
4246
  width: "16",
@@ -4081,8 +4250,8 @@ function CloseIcon4() {
4081
4250
  xmlns: "http://www.w3.org/2000/svg",
4082
4251
  "aria-hidden": "true",
4083
4252
  children: [
4084
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "5", y1: "5", x2: "15", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
4085
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "15", y1: "5", x2: "5", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
4253
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "5", y1: "5", x2: "15", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
4254
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "15", y1: "5", x2: "5", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
4086
4255
  ]
4087
4256
  }
4088
4257
  );
@@ -4095,7 +4264,7 @@ async function fetchShopCustomCss(options) {
4095
4264
  }
4096
4265
 
4097
4266
  // src/hooks/useBundlesForProduct.ts
4098
- var import_react17 = require("react");
4267
+ var import_react18 = require("react");
4099
4268
  var import_core16 = require("@lime-bundles/core");
4100
4269
  var INITIAL_STATE2 = {
4101
4270
  status: "loading",
@@ -4103,10 +4272,10 @@ var INITIAL_STATE2 = {
4103
4272
  error: null
4104
4273
  };
4105
4274
  function useBundlesForProduct(options) {
4106
- const [state, setState] = (0, import_react17.useState)(
4275
+ const [state, setState] = (0, import_react18.useState)(
4107
4276
  INITIAL_STATE2
4108
4277
  );
4109
- (0, import_react17.useEffect)(() => {
4278
+ (0, import_react18.useEffect)(() => {
4110
4279
  const controller = new AbortController();
4111
4280
  setState(INITIAL_STATE2);
4112
4281
  (0, import_core16.fetchBundlesForProduct)({
@@ -4155,6 +4324,7 @@ var import_core18 = require("@lime-bundles/core");
4155
4324
  BUNDLES_FOR_PRODUCT_QUERY,
4156
4325
  BUNDLE_METAOBJECT_QUERY,
4157
4326
  BogoBundle,
4327
+ BundleCountdown,
4158
4328
  BundleParseError,
4159
4329
  DEFAULT_OUT_OF_STOCK_BEHAVIOR,
4160
4330
  FixedBundle,