@lime-bundles/widget 4.6.3 → 4.6.5

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
@@ -257,6 +257,7 @@ function recalcPricing(container, products, formatMoney) {
257
257
  savingsPercentEl.textContent = "(" + pct + "%)";
258
258
  }
259
259
  savingsBar.style.display = "";
260
+ savingsBar.classList.remove("lb-savings-pending");
260
261
  } else {
261
262
  savingsBar.style.display = "none";
262
263
  }
@@ -2458,7 +2459,7 @@ function seedSelection(eligibleProducts, rulesMap, requiredQty, courtesy) {
2458
2459
  required.push(pick(p, v2, Math.max(1, rule.min)));
2459
2460
  }
2460
2461
  if (required.length || !courtesy?.enabled) return required;
2461
- const qualifies = (p) => p.available && ruleFor(p.id, rulesMap).min <= requiredQty;
2462
+ const qualifies = (p) => p.available && ruleFor(p.id, rulesMap).min <= requiredQty && firstAvailable(p) !== null;
2462
2463
  const seed = (courtesy.currentProductId != null ? eligibleProducts.find(
2463
2464
  (p) => p.id === courtesy.currentProductId && qualifies(p)
2464
2465
  ) : null) ?? eligibleProducts.find(qualifies);
@@ -2496,6 +2497,7 @@ function el(tag, className, attrs = {}) {
2496
2497
  }
2497
2498
  function buildPickerModal(opts) {
2498
2499
  const titleId = "lb-modal-title-" + opts.domId;
2500
+ const s = opts.strings || {};
2499
2501
  const overlay = el("div", "lb-mix-match__modal-overlay", {
2500
2502
  "data-modal-overlay": "",
2501
2503
  "data-bundle-gid": opts.bundleGid,
@@ -2516,7 +2518,7 @@ function buildPickerModal(opts) {
2516
2518
  const heading = el("div", "lb-mix-match__modal-heading");
2517
2519
  const title = el("h4", "lb-mix-match__modal-title", { id: titleId });
2518
2520
  if (opts.wizard) title.setAttribute("tabindex", "-1");
2519
- title.textContent = "Add to your bundle";
2521
+ title.textContent = s.addToBundle || "Add to your bundle";
2520
2522
  heading.appendChild(title);
2521
2523
  heading.appendChild(
2522
2524
  el("p", "lb-mix-match__modal-subtitle", { "data-modal-subtitle": "" })
@@ -2525,7 +2527,7 @@ function buildPickerModal(opts) {
2525
2527
  const close = el("button", "lb-mix-match__modal-close", {
2526
2528
  type: "button",
2527
2529
  "data-modal-close": "",
2528
- "aria-label": "Close"
2530
+ "aria-label": s.closeModal || "Close"
2529
2531
  });
2530
2532
  close.appendChild(
2531
2533
  icon([
@@ -2566,15 +2568,15 @@ function buildPickerModal(opts) {
2566
2568
  type: "text",
2567
2569
  "data-modal-search": "",
2568
2570
  role: "searchbox",
2569
- "aria-label": "Search products",
2570
- placeholder: "Search products",
2571
+ "aria-label": s.searchProducts || "Search products",
2572
+ placeholder: s.searchProducts || "Search products",
2571
2573
  autocomplete: "off"
2572
2574
  });
2573
2575
  search.appendChild(input);
2574
2576
  const clear = el("button", "lb-mix-match__modal-search-clear", {
2575
2577
  type: "button",
2576
2578
  "data-modal-search-clear": "",
2577
- "aria-label": "Clear search",
2579
+ "aria-label": s.clearSearch || "Clear search",
2578
2580
  style: "display: none;"
2579
2581
  });
2580
2582
  clear.appendChild(
@@ -2603,7 +2605,7 @@ function buildPickerModal(opts) {
2603
2605
  style: "display: none;"
2604
2606
  });
2605
2607
  const emptyText = document.createElement("p");
2606
- emptyText.textContent = "No products match your search";
2608
+ emptyText.textContent = s.noSearchResults || "No products match your search";
2607
2609
  empty.appendChild(emptyText);
2608
2610
  dialog.appendChild(empty);
2609
2611
  dialog.appendChild(
@@ -2622,7 +2624,7 @@ function buildPickerModal(opts) {
2622
2624
  "data-modal-back": "",
2623
2625
  style: "display: none;"
2624
2626
  });
2625
- back.textContent = "Back";
2627
+ back.textContent = s.back || "Back";
2626
2628
  footer.appendChild(back);
2627
2629
  }
2628
2630
  footer.appendChild(
@@ -2637,7 +2639,7 @@ function buildPickerModal(opts) {
2637
2639
  "data-modal-next": "",
2638
2640
  style: "display: none;"
2639
2641
  });
2640
- next.textContent = "Next";
2642
+ next.textContent = s.next || "Next";
2641
2643
  footer.appendChild(next);
2642
2644
  }
2643
2645
  const done = el("button", "lb-mix-match__modal-done", {
@@ -2645,7 +2647,7 @@ function buildPickerModal(opts) {
2645
2647
  "data-modal-done": "",
2646
2648
  ...opts.wizard ? { style: "display: none;" } : {}
2647
2649
  });
2648
- done.textContent = "Done";
2650
+ done.textContent = s.done || "Done";
2649
2651
  footer.appendChild(done);
2650
2652
  dialog.appendChild(footer);
2651
2653
  overlay.appendChild(dialog);
@@ -2963,6 +2965,7 @@ function selectTier(container, allTiers, basePrice, discountType, index, transla
2963
2965
  savingsPercentEl.textContent = "(" + savingsPct + "%)";
2964
2966
  }
2965
2967
  savingsBar.style.display = "";
2968
+ savingsBar.classList.remove("lb-savings-pending");
2966
2969
  } else {
2967
2970
  savingsBar.style.display = "none";
2968
2971
  }
@@ -3146,6 +3149,7 @@ function recalcPricing2(container, sides, percent, buyQty, getQty, formatMoney)
3146
3149
  savingsPercentEl.textContent = "(" + pct + "%)";
3147
3150
  }
3148
3151
  savingsBar.style.display = "";
3152
+ savingsBar.classList.remove("lb-savings-pending");
3149
3153
  } else {
3150
3154
  savingsBar.style.display = "none";
3151
3155
  }
@@ -4698,6 +4702,43 @@ a.lb-bundle-product-name:focus-visible {
4698
4702
  gap: 4px;
4699
4703
  }
4700
4704
 
4705
+ /* Variant picker select \u2014 styled to match the variant badge aesthetic.
4706
+ Sits inside .lb-bundle-variant-option-group so vertical spacing is owned
4707
+ by the group/groups flex gap, not the select itself.
4708
+
4709
+ Lives here, not in bundle-fixed.css, because every widget that renders an
4710
+ option group needs it and only the fixed widget loads that stylesheet: a
4711
+ page carrying just a Buy X Get Y bundle rendered raw browser selects, and
4712
+ the option-group height reservation (sized for the styled control) was
4713
+ wrong by 14px a group, which shifted the row on hydration.
4714
+ The same split applies to the headless package, which injects per type. */
4715
+ .lb-bundle-variant-select {
4716
+ display: inline-block;
4717
+ border: var(--lb-border-width) solid var(--lb-border);
4718
+ border-radius: var(--lb-radius);
4719
+ padding: 8px 32px 8px 12px;
4720
+ font-size: 12px;
4721
+ line-height: 16px;
4722
+ color: var(--lb-text);
4723
+ background: var(--lb-bg);
4724
+ font-family: inherit;
4725
+ cursor: pointer;
4726
+ appearance: none;
4727
+ -webkit-appearance: none;
4728
+ background-image: var(--lb-variant-chevron);
4729
+ background-repeat: no-repeat;
4730
+ background-position: right 8px center;
4731
+ background-size: 12px;
4732
+ width: 50%;
4733
+ max-width: 50%;
4734
+ }
4735
+
4736
+ .lb-bundle-variant-select:focus-visible {
4737
+ outline: 2px solid var(--lb-primary-color);
4738
+ outline-offset: 2px;
4739
+ box-shadow: none;
4740
+ }
4741
+
4701
4742
  .lb-bundle-variant-option-label {
4702
4743
  display: block;
4703
4744
  margin: 0;
@@ -5019,6 +5060,16 @@ a.lb-bundle-product-name:focus-visible {
5019
5060
  white-space: nowrap;
5020
5061
  }
5021
5062
 
5063
+ /* The savings line is laid out from first paint and revealed on hydration,
5064
+ rather than being display:none until then.
5065
+ Its amounts are money strings the server cannot know, so filling them in
5066
+ place moves whatever shares the line; revealing an already-laid-out box
5067
+ moves nothing. The band around it is vertically centred, so reserving the
5068
+ line costs no height of its own. JS drops the class in updatePricing. */
5069
+ .lb-bundle-savings-line.lb-savings-pending {
5070
+ visibility: hidden;
5071
+ }
5072
+
5022
5073
  /* A/B test: hide save badge until JS swaps the label (prevents flash of default) */
5023
5074
  .lb-ab-pending {
5024
5075
  visibility: hidden;
@@ -5036,27 +5087,43 @@ a.lb-bundle-product-name:focus-visible {
5036
5087
  The title is the one box NOT reserved this way: text wrapping depends on the
5037
5088
  merchant's font, so it is server-rendered and its height is simply real. */
5038
5089
 
5090
+ /* A zero-width space, not a pixel height.
5091
+ These boxes are one line of text once JS fills them, and their height is
5092
+ whatever the merchant's font makes of \`line-height: normal\` \u2014 which the
5093
+ reset at the top of this file forces on every descendant, overriding the
5094
+ per-element line-heights above it. A hardcoded min-height therefore has to
5095
+ guess, and guessed 3px too tall: the row shrank on hydration in every theme.
5096
+ An empty line box measures exactly what the filled one will. */
5039
5097
  .lb-bundle-product-price:empty {
5040
5098
  display: inline-block;
5041
- min-height: 20px;
5042
5099
  }
5043
5100
 
5044
5101
  .lb-bundle-variant-badge:empty {
5045
5102
  display: block;
5046
- min-height: 20px;
5047
5103
  }
5048
5104
 
5049
5105
  .lb-bundle-product-unit-price:empty {
5050
5106
  display: block;
5051
- min-height: 16px;
5107
+ }
5108
+
5109
+ .lb-bundle-product-price:empty::before,
5110
+ .lb-bundle-variant-badge:empty::before,
5111
+ .lb-bundle-product-unit-price:empty::before,
5112
+ .lb-bundle-qty-inline:empty::before,
5113
+ .lb-bundle-sale-price:empty::before,
5114
+ .lb-bundle-compare-price:empty::before {
5115
+ content: "\\200B";
5052
5116
  }
5053
5117
 
5054
5118
  /* Label (16) + gap (4) + trigger (8 + 16 + 8 padding/line-height) per group,
5055
5119
  plus the 8px gap between groups. --lb-row-option-count is set per row by
5056
- Liquid, which knows how many options the product has. */
5120
+ Liquid, which knows how many options the product has.
5121
+
5122
+ The trigger is border-box, so its border is already inside that 52px; the
5123
+ calc used to add it again and reserved 2px per group too much. */
5057
5124
  .lb-bundle-variant-option-groups:empty {
5058
5125
  min-height: calc(
5059
- var(--lb-row-option-count, 1) * (52px + 2 * var(--lb-border-width)) +
5126
+ var(--lb-row-option-count, 1) * 52px +
5060
5127
  (var(--lb-row-option-count, 1) - 1) * 8px
5061
5128
  );
5062
5129
  }
@@ -5136,36 +5203,6 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
5136
5203
  font-variant-numeric: tabular-nums;
5137
5204
  white-space: nowrap;
5138
5205
  }
5139
-
5140
- /* Variant picker select \u2014 styled to match the variant badge aesthetic.
5141
- Sits inside .lb-bundle-variant-option-group so vertical spacing is owned
5142
- by the group/groups flex gap, not the select itself. */
5143
- .lb-bundle-variant-select {
5144
- display: inline-block;
5145
- border: var(--lb-border-width) solid var(--lb-border);
5146
- border-radius: var(--lb-radius);
5147
- padding: 8px 32px 8px 12px;
5148
- font-size: 12px;
5149
- line-height: 16px;
5150
- color: var(--lb-text);
5151
- background: var(--lb-bg);
5152
- font-family: inherit;
5153
- cursor: pointer;
5154
- appearance: none;
5155
- -webkit-appearance: none;
5156
- background-image: var(--lb-variant-chevron);
5157
- background-repeat: no-repeat;
5158
- background-position: right 8px center;
5159
- background-size: 12px;
5160
- width: 50%;
5161
- max-width: 50%;
5162
- }
5163
-
5164
- .lb-bundle-variant-select:focus-visible {
5165
- outline: 2px solid var(--lb-primary-color);
5166
- outline-offset: 2px;
5167
- box-shadow: none;
5168
- }
5169
5206
  `;
5170
5207
  var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
5171
5208
 
@@ -6488,6 +6525,15 @@ var BUNDLE_BOGO_CSS = `/* Lime Bundles \u2014 BOGO (Buy X Get Y) bundle styles *
6488
6525
  text-decoration: line-through;
6489
6526
  opacity: 0.6;
6490
6527
  }
6528
+
6529
+ /* The reward row's struck original is emitted empty and filled on hydration,
6530
+ so anything sharing its line moves when it appears. The count is the only
6531
+ such thing, and it is a count, not part of the price phrase: pinning it to
6532
+ the row's right edge makes its position independent of the price text \u2014 the
6533
+ same right-slot treatment the fixed widget gives its quantity chip. */
6534
+ .lb-bogo .lb-bundle-qty-inline {
6535
+ margin-left: auto;
6536
+ }
6491
6537
  `;
6492
6538
  var BUNDLE_MULTI_STEP_CSS = `/**
6493
6539
  * Lime Bundles \u2014 Multi-step bundle widget styles (wizard chrome ONLY).
package/dist/index.js CHANGED
@@ -256,6 +256,7 @@ function recalcPricing(container, products, formatMoney) {
256
256
  savingsPercentEl.textContent = "(" + pct + "%)";
257
257
  }
258
258
  savingsBar.style.display = "";
259
+ savingsBar.classList.remove("lb-savings-pending");
259
260
  } else {
260
261
  savingsBar.style.display = "none";
261
262
  }
@@ -2457,7 +2458,7 @@ function seedSelection(eligibleProducts, rulesMap, requiredQty, courtesy) {
2457
2458
  required.push(pick(p, v2, Math.max(1, rule.min)));
2458
2459
  }
2459
2460
  if (required.length || !courtesy?.enabled) return required;
2460
- const qualifies = (p) => p.available && ruleFor(p.id, rulesMap).min <= requiredQty;
2461
+ const qualifies = (p) => p.available && ruleFor(p.id, rulesMap).min <= requiredQty && firstAvailable(p) !== null;
2461
2462
  const seed = (courtesy.currentProductId != null ? eligibleProducts.find(
2462
2463
  (p) => p.id === courtesy.currentProductId && qualifies(p)
2463
2464
  ) : null) ?? eligibleProducts.find(qualifies);
@@ -2495,6 +2496,7 @@ function el(tag, className, attrs = {}) {
2495
2496
  }
2496
2497
  function buildPickerModal(opts) {
2497
2498
  const titleId = "lb-modal-title-" + opts.domId;
2499
+ const s = opts.strings || {};
2498
2500
  const overlay = el("div", "lb-mix-match__modal-overlay", {
2499
2501
  "data-modal-overlay": "",
2500
2502
  "data-bundle-gid": opts.bundleGid,
@@ -2515,7 +2517,7 @@ function buildPickerModal(opts) {
2515
2517
  const heading = el("div", "lb-mix-match__modal-heading");
2516
2518
  const title = el("h4", "lb-mix-match__modal-title", { id: titleId });
2517
2519
  if (opts.wizard) title.setAttribute("tabindex", "-1");
2518
- title.textContent = "Add to your bundle";
2520
+ title.textContent = s.addToBundle || "Add to your bundle";
2519
2521
  heading.appendChild(title);
2520
2522
  heading.appendChild(
2521
2523
  el("p", "lb-mix-match__modal-subtitle", { "data-modal-subtitle": "" })
@@ -2524,7 +2526,7 @@ function buildPickerModal(opts) {
2524
2526
  const close = el("button", "lb-mix-match__modal-close", {
2525
2527
  type: "button",
2526
2528
  "data-modal-close": "",
2527
- "aria-label": "Close"
2529
+ "aria-label": s.closeModal || "Close"
2528
2530
  });
2529
2531
  close.appendChild(
2530
2532
  icon([
@@ -2565,15 +2567,15 @@ function buildPickerModal(opts) {
2565
2567
  type: "text",
2566
2568
  "data-modal-search": "",
2567
2569
  role: "searchbox",
2568
- "aria-label": "Search products",
2569
- placeholder: "Search products",
2570
+ "aria-label": s.searchProducts || "Search products",
2571
+ placeholder: s.searchProducts || "Search products",
2570
2572
  autocomplete: "off"
2571
2573
  });
2572
2574
  search.appendChild(input);
2573
2575
  const clear = el("button", "lb-mix-match__modal-search-clear", {
2574
2576
  type: "button",
2575
2577
  "data-modal-search-clear": "",
2576
- "aria-label": "Clear search",
2578
+ "aria-label": s.clearSearch || "Clear search",
2577
2579
  style: "display: none;"
2578
2580
  });
2579
2581
  clear.appendChild(
@@ -2602,7 +2604,7 @@ function buildPickerModal(opts) {
2602
2604
  style: "display: none;"
2603
2605
  });
2604
2606
  const emptyText = document.createElement("p");
2605
- emptyText.textContent = "No products match your search";
2607
+ emptyText.textContent = s.noSearchResults || "No products match your search";
2606
2608
  empty.appendChild(emptyText);
2607
2609
  dialog.appendChild(empty);
2608
2610
  dialog.appendChild(
@@ -2621,7 +2623,7 @@ function buildPickerModal(opts) {
2621
2623
  "data-modal-back": "",
2622
2624
  style: "display: none;"
2623
2625
  });
2624
- back.textContent = "Back";
2626
+ back.textContent = s.back || "Back";
2625
2627
  footer.appendChild(back);
2626
2628
  }
2627
2629
  footer.appendChild(
@@ -2636,7 +2638,7 @@ function buildPickerModal(opts) {
2636
2638
  "data-modal-next": "",
2637
2639
  style: "display: none;"
2638
2640
  });
2639
- next.textContent = "Next";
2641
+ next.textContent = s.next || "Next";
2640
2642
  footer.appendChild(next);
2641
2643
  }
2642
2644
  const done = el("button", "lb-mix-match__modal-done", {
@@ -2644,7 +2646,7 @@ function buildPickerModal(opts) {
2644
2646
  "data-modal-done": "",
2645
2647
  ...opts.wizard ? { style: "display: none;" } : {}
2646
2648
  });
2647
- done.textContent = "Done";
2649
+ done.textContent = s.done || "Done";
2648
2650
  footer.appendChild(done);
2649
2651
  dialog.appendChild(footer);
2650
2652
  overlay.appendChild(dialog);
@@ -2969,6 +2971,7 @@ function selectTier(container, allTiers, basePrice, discountType, index, transla
2969
2971
  savingsPercentEl.textContent = "(" + savingsPct + "%)";
2970
2972
  }
2971
2973
  savingsBar.style.display = "";
2974
+ savingsBar.classList.remove("lb-savings-pending");
2972
2975
  } else {
2973
2976
  savingsBar.style.display = "none";
2974
2977
  }
@@ -3152,6 +3155,7 @@ function recalcPricing2(container, sides, percent, buyQty, getQty, formatMoney)
3152
3155
  savingsPercentEl.textContent = "(" + pct + "%)";
3153
3156
  }
3154
3157
  savingsBar.style.display = "";
3158
+ savingsBar.classList.remove("lb-savings-pending");
3155
3159
  } else {
3156
3160
  savingsBar.style.display = "none";
3157
3161
  }
@@ -4708,6 +4712,43 @@ a.lb-bundle-product-name:focus-visible {
4708
4712
  gap: 4px;
4709
4713
  }
4710
4714
 
4715
+ /* Variant picker select \u2014 styled to match the variant badge aesthetic.
4716
+ Sits inside .lb-bundle-variant-option-group so vertical spacing is owned
4717
+ by the group/groups flex gap, not the select itself.
4718
+
4719
+ Lives here, not in bundle-fixed.css, because every widget that renders an
4720
+ option group needs it and only the fixed widget loads that stylesheet: a
4721
+ page carrying just a Buy X Get Y bundle rendered raw browser selects, and
4722
+ the option-group height reservation (sized for the styled control) was
4723
+ wrong by 14px a group, which shifted the row on hydration.
4724
+ The same split applies to the headless package, which injects per type. */
4725
+ .lb-bundle-variant-select {
4726
+ display: inline-block;
4727
+ border: var(--lb-border-width) solid var(--lb-border);
4728
+ border-radius: var(--lb-radius);
4729
+ padding: 8px 32px 8px 12px;
4730
+ font-size: 12px;
4731
+ line-height: 16px;
4732
+ color: var(--lb-text);
4733
+ background: var(--lb-bg);
4734
+ font-family: inherit;
4735
+ cursor: pointer;
4736
+ appearance: none;
4737
+ -webkit-appearance: none;
4738
+ background-image: var(--lb-variant-chevron);
4739
+ background-repeat: no-repeat;
4740
+ background-position: right 8px center;
4741
+ background-size: 12px;
4742
+ width: 50%;
4743
+ max-width: 50%;
4744
+ }
4745
+
4746
+ .lb-bundle-variant-select:focus-visible {
4747
+ outline: 2px solid var(--lb-primary-color);
4748
+ outline-offset: 2px;
4749
+ box-shadow: none;
4750
+ }
4751
+
4711
4752
  .lb-bundle-variant-option-label {
4712
4753
  display: block;
4713
4754
  margin: 0;
@@ -5029,6 +5070,16 @@ a.lb-bundle-product-name:focus-visible {
5029
5070
  white-space: nowrap;
5030
5071
  }
5031
5072
 
5073
+ /* The savings line is laid out from first paint and revealed on hydration,
5074
+ rather than being display:none until then.
5075
+ Its amounts are money strings the server cannot know, so filling them in
5076
+ place moves whatever shares the line; revealing an already-laid-out box
5077
+ moves nothing. The band around it is vertically centred, so reserving the
5078
+ line costs no height of its own. JS drops the class in updatePricing. */
5079
+ .lb-bundle-savings-line.lb-savings-pending {
5080
+ visibility: hidden;
5081
+ }
5082
+
5032
5083
  /* A/B test: hide save badge until JS swaps the label (prevents flash of default) */
5033
5084
  .lb-ab-pending {
5034
5085
  visibility: hidden;
@@ -5046,27 +5097,43 @@ a.lb-bundle-product-name:focus-visible {
5046
5097
  The title is the one box NOT reserved this way: text wrapping depends on the
5047
5098
  merchant's font, so it is server-rendered and its height is simply real. */
5048
5099
 
5100
+ /* A zero-width space, not a pixel height.
5101
+ These boxes are one line of text once JS fills them, and their height is
5102
+ whatever the merchant's font makes of \`line-height: normal\` \u2014 which the
5103
+ reset at the top of this file forces on every descendant, overriding the
5104
+ per-element line-heights above it. A hardcoded min-height therefore has to
5105
+ guess, and guessed 3px too tall: the row shrank on hydration in every theme.
5106
+ An empty line box measures exactly what the filled one will. */
5049
5107
  .lb-bundle-product-price:empty {
5050
5108
  display: inline-block;
5051
- min-height: 20px;
5052
5109
  }
5053
5110
 
5054
5111
  .lb-bundle-variant-badge:empty {
5055
5112
  display: block;
5056
- min-height: 20px;
5057
5113
  }
5058
5114
 
5059
5115
  .lb-bundle-product-unit-price:empty {
5060
5116
  display: block;
5061
- min-height: 16px;
5117
+ }
5118
+
5119
+ .lb-bundle-product-price:empty::before,
5120
+ .lb-bundle-variant-badge:empty::before,
5121
+ .lb-bundle-product-unit-price:empty::before,
5122
+ .lb-bundle-qty-inline:empty::before,
5123
+ .lb-bundle-sale-price:empty::before,
5124
+ .lb-bundle-compare-price:empty::before {
5125
+ content: "\\200B";
5062
5126
  }
5063
5127
 
5064
5128
  /* Label (16) + gap (4) + trigger (8 + 16 + 8 padding/line-height) per group,
5065
5129
  plus the 8px gap between groups. --lb-row-option-count is set per row by
5066
- Liquid, which knows how many options the product has. */
5130
+ Liquid, which knows how many options the product has.
5131
+
5132
+ The trigger is border-box, so its border is already inside that 52px; the
5133
+ calc used to add it again and reserved 2px per group too much. */
5067
5134
  .lb-bundle-variant-option-groups:empty {
5068
5135
  min-height: calc(
5069
- var(--lb-row-option-count, 1) * (52px + 2 * var(--lb-border-width)) +
5136
+ var(--lb-row-option-count, 1) * 52px +
5070
5137
  (var(--lb-row-option-count, 1) - 1) * 8px
5071
5138
  );
5072
5139
  }
@@ -5146,36 +5213,6 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
5146
5213
  font-variant-numeric: tabular-nums;
5147
5214
  white-space: nowrap;
5148
5215
  }
5149
-
5150
- /* Variant picker select \u2014 styled to match the variant badge aesthetic.
5151
- Sits inside .lb-bundle-variant-option-group so vertical spacing is owned
5152
- by the group/groups flex gap, not the select itself. */
5153
- .lb-bundle-variant-select {
5154
- display: inline-block;
5155
- border: var(--lb-border-width) solid var(--lb-border);
5156
- border-radius: var(--lb-radius);
5157
- padding: 8px 32px 8px 12px;
5158
- font-size: 12px;
5159
- line-height: 16px;
5160
- color: var(--lb-text);
5161
- background: var(--lb-bg);
5162
- font-family: inherit;
5163
- cursor: pointer;
5164
- appearance: none;
5165
- -webkit-appearance: none;
5166
- background-image: var(--lb-variant-chevron);
5167
- background-repeat: no-repeat;
5168
- background-position: right 8px center;
5169
- background-size: 12px;
5170
- width: 50%;
5171
- max-width: 50%;
5172
- }
5173
-
5174
- .lb-bundle-variant-select:focus-visible {
5175
- outline: 2px solid var(--lb-primary-color);
5176
- outline-offset: 2px;
5177
- box-shadow: none;
5178
- }
5179
5216
  `;
5180
5217
  var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
5181
5218
 
@@ -6498,6 +6535,15 @@ var BUNDLE_BOGO_CSS = `/* Lime Bundles \u2014 BOGO (Buy X Get Y) bundle styles *
6498
6535
  text-decoration: line-through;
6499
6536
  opacity: 0.6;
6500
6537
  }
6538
+
6539
+ /* The reward row's struck original is emitted empty and filled on hydration,
6540
+ so anything sharing its line moves when it appears. The count is the only
6541
+ such thing, and it is a count, not part of the price phrase: pinning it to
6542
+ the row's right edge makes its position independent of the price text \u2014 the
6543
+ same right-slot treatment the fixed widget gives its quantity chip. */
6544
+ .lb-bogo .lb-bundle-qty-inline {
6545
+ margin-left: auto;
6546
+ }
6501
6547
  `;
6502
6548
  var BUNDLE_MULTI_STEP_CSS = `/**
6503
6549
  * Lime Bundles \u2014 Multi-step bundle widget styles (wizard chrome ONLY).