@lime-bundles/widget 4.6.4 → 4.6.6

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
  }
@@ -380,6 +381,55 @@ function buildOptionGroups(container, product, selected) {
380
381
  }
381
382
  }
382
383
 
384
+ // ../render/src/product/placeholder.ts
385
+ var SVG_NS = "http://www.w3.org/2000/svg";
386
+ var PLACEHOLDER_ATTR = "data-lb-placeholder";
387
+ var PLACEHOLDER_CLASS = "lb-thumb--placeholder";
388
+ function builtInMark() {
389
+ const svg = document.createElementNS(SVG_NS, "svg");
390
+ svg.setAttribute("class", "lb-bundle-placeholder-icon");
391
+ svg.setAttribute("viewBox", "0 0 48 48");
392
+ const rect = document.createElementNS(SVG_NS, "rect");
393
+ rect.setAttribute("x", "15");
394
+ rect.setAttribute("y", "15");
395
+ rect.setAttribute("width", "18");
396
+ rect.setAttribute("height", "18");
397
+ rect.setAttribute("rx", "2");
398
+ svg.appendChild(rect);
399
+ const dot = document.createElementNS(SVG_NS, "circle");
400
+ dot.setAttribute("cx", "20.5");
401
+ dot.setAttribute("cy", "20.5");
402
+ dot.setAttribute("r", "1.5");
403
+ svg.appendChild(dot);
404
+ const ridge = document.createElementNS(SVG_NS, "path");
405
+ ridge.setAttribute("d", "M33 27l-5-5L17 33");
406
+ svg.appendChild(ridge);
407
+ return svg;
408
+ }
409
+ var make = builtInMark;
410
+ function mark() {
411
+ const node = make();
412
+ const el2 = node.nodeType === 1 ? node : null;
413
+ if (el2) {
414
+ el2.setAttribute(PLACEHOLDER_ATTR, "");
415
+ el2.setAttribute("aria-hidden", "true");
416
+ el2.setAttribute("focusable", "false");
417
+ }
418
+ return node;
419
+ }
420
+ function showThumbnailPlaceholder(box2) {
421
+ const img = box2.querySelector("img");
422
+ if (img) img.remove();
423
+ box2.classList.add(PLACEHOLDER_CLASS);
424
+ if (box2.querySelector("[" + PLACEHOLDER_ATTR + "]")) return;
425
+ box2.appendChild(mark());
426
+ }
427
+ function hideThumbnailPlaceholder(box2) {
428
+ const existing = box2.querySelector("[" + PLACEHOLDER_ATTR + "]");
429
+ if (existing) existing.remove();
430
+ box2.classList.remove(PLACEHOLDER_CLASS);
431
+ }
432
+
383
433
  // ../render/src/product/row.ts
384
434
  var productImgSrc = /* @__PURE__ */ new WeakMap();
385
435
  function updateThumbnail(row, variant, productImage) {
@@ -390,8 +440,12 @@ function updateThumbnail(row, variant, productImage) {
390
440
  const seed = productImage || (img ? img.src : null);
391
441
  if (seed) productImgSrc.set(row, seed);
392
442
  }
393
- const nextSrc = variant.image || productImgSrc.get(row) || null;
394
- if (!nextSrc) return;
443
+ const nextSrc = variant && variant.image || productImgSrc.get(row) || null;
444
+ if (!nextSrc) {
445
+ showThumbnailPlaceholder(thumb);
446
+ return;
447
+ }
448
+ hideThumbnailPlaceholder(thumb);
395
449
  if (img) {
396
450
  img.src = nextSrc;
397
451
  img.srcset = "";
@@ -410,7 +464,10 @@ function updateInlineQuantity(row, qty) {
410
464
  el2.textContent = "\xD7" + qty;
411
465
  }
412
466
  function applyVariant(row, variant, product, formatMoney) {
413
- if (!variant) return;
467
+ if (!variant) {
468
+ updateThumbnail(row, null, product.featuredImage);
469
+ return;
470
+ }
414
471
  updateThumbnail(row, variant, product.featuredImage);
415
472
  updateInlineQuantity(row, variant.quantity || product.quantity || 1);
416
473
  const priceEl = row.querySelector("[data-product-price]");
@@ -460,8 +517,11 @@ function buildRowSkeleton(product, t, opts) {
460
517
  if (opts.bogoRole) row.setAttribute("data-bogo-role", opts.bogoRole);
461
518
  const thumb = box("div", "lb-bundle-thumbnail", { "data-thumbnail": "" });
462
519
  const ratio = product.featuredImageRatio;
463
- if (opts.thumbnailRatio === "original" && ratio != null && ratio > 0) {
464
- thumb.style.setProperty("--lb-row-thumb-ratio", String(ratio));
520
+ if (opts.thumbnailRatio === "original") {
521
+ thumb.style.setProperty(
522
+ "--lb-row-thumb-ratio",
523
+ String(ratio != null && ratio > 0 ? ratio : 1)
524
+ );
465
525
  }
466
526
  row.appendChild(thumb);
467
527
  const info = box("div", "lb-bundle-product-info");
@@ -1426,17 +1486,25 @@ function buildPickerRow(p, rowIndex, ctx) {
1426
1486
  addedBadge.textContent = t.addedItem || "Added";
1427
1487
  addedBadge.hidden = true;
1428
1488
  thumbDiv.appendChild(addedBadge);
1489
+ const paintThumb = (src) => {
1490
+ if (!src) {
1491
+ showThumbnailPlaceholder(thumbDiv);
1492
+ return;
1493
+ }
1494
+ hideThumbnailPlaceholder(thumbDiv);
1495
+ let img = thumbDiv.querySelector("img");
1496
+ if (!img) {
1497
+ img = document.createElement("img");
1498
+ img.sizes = PICKER_SIZES;
1499
+ img.alt = p.title;
1500
+ img.loading = "lazy";
1501
+ thumbDiv.appendChild(img);
1502
+ }
1503
+ img.src = imgWidth(src, PICKER_BASE_WIDTH);
1504
+ img.srcset = srcset(src, PICKER_WIDTHS);
1505
+ };
1429
1506
  const firstAvailable = p.variants.find((v) => v.available) ?? null;
1430
- const initialThumbSrc = firstAvailable && firstAvailable.image || p.featuredImage;
1431
- if (initialThumbSrc) {
1432
- const img = document.createElement("img");
1433
- img.src = imgWidth(initialThumbSrc, PICKER_BASE_WIDTH);
1434
- img.srcset = srcset(initialThumbSrc, PICKER_WIDTHS);
1435
- img.sizes = PICKER_SIZES;
1436
- img.alt = p.title;
1437
- img.loading = "lazy";
1438
- thumbDiv.appendChild(img);
1439
- }
1507
+ paintThumb(firstAvailable && firstAvailable.image || p.featuredImage);
1440
1508
  row.appendChild(thumbDiv);
1441
1509
  const infoDiv = document.createElement("div");
1442
1510
  infoDiv.className = "lb-mix-match__modal-product-info";
@@ -1647,12 +1715,7 @@ function buildPickerRow(p, rowIndex, ctx) {
1647
1715
  unitPriceEl.textContent = "";
1648
1716
  unitPriceEl.hidden = true;
1649
1717
  }
1650
- const rowImg = thumbDiv.querySelector("img");
1651
- const imgSrc = v.image || p.featuredImage;
1652
- if (rowImg && imgSrc) {
1653
- rowImg.src = imgWidth(imgSrc, PICKER_BASE_WIDTH);
1654
- rowImg.srcset = srcset(imgSrc, PICKER_WIDTHS);
1655
- }
1718
+ paintThumb(v.image || p.featuredImage);
1656
1719
  setBoundsForVariant?.(v, capacity.variantUnitsElsewhere(v.id));
1657
1720
  if (v.options) recomputeDisabled(v.options);
1658
1721
  refresh();
@@ -2230,6 +2293,8 @@ function buildSlotCard(item, trans, formatMoney, removable, onRemove, onEdit) {
2230
2293
  img.alt = item.title;
2231
2294
  img.loading = "lazy";
2232
2295
  thumb.appendChild(img);
2296
+ } else {
2297
+ showThumbnailPlaceholder(thumb);
2233
2298
  }
2234
2299
  card.appendChild(thumb);
2235
2300
  const info = document.createElement("div");
@@ -2458,7 +2523,7 @@ function seedSelection(eligibleProducts, rulesMap, requiredQty, courtesy) {
2458
2523
  required.push(pick(p, v2, Math.max(1, rule.min)));
2459
2524
  }
2460
2525
  if (required.length || !courtesy?.enabled) return required;
2461
- const qualifies = (p) => p.available && ruleFor(p.id, rulesMap).min <= requiredQty;
2526
+ const qualifies = (p) => p.available && ruleFor(p.id, rulesMap).min <= requiredQty && firstAvailable(p) !== null;
2462
2527
  const seed = (courtesy.currentProductId != null ? eligibleProducts.find(
2463
2528
  (p) => p.id === courtesy.currentProductId && qualifies(p)
2464
2529
  ) : null) ?? eligibleProducts.find(qualifies);
@@ -2496,6 +2561,7 @@ function el(tag, className, attrs = {}) {
2496
2561
  }
2497
2562
  function buildPickerModal(opts) {
2498
2563
  const titleId = "lb-modal-title-" + opts.domId;
2564
+ const s = opts.strings || {};
2499
2565
  const overlay = el("div", "lb-mix-match__modal-overlay", {
2500
2566
  "data-modal-overlay": "",
2501
2567
  "data-bundle-gid": opts.bundleGid,
@@ -2516,7 +2582,7 @@ function buildPickerModal(opts) {
2516
2582
  const heading = el("div", "lb-mix-match__modal-heading");
2517
2583
  const title = el("h4", "lb-mix-match__modal-title", { id: titleId });
2518
2584
  if (opts.wizard) title.setAttribute("tabindex", "-1");
2519
- title.textContent = "Add to your bundle";
2585
+ title.textContent = s.addToBundle || "Add to your bundle";
2520
2586
  heading.appendChild(title);
2521
2587
  heading.appendChild(
2522
2588
  el("p", "lb-mix-match__modal-subtitle", { "data-modal-subtitle": "" })
@@ -2525,7 +2591,7 @@ function buildPickerModal(opts) {
2525
2591
  const close = el("button", "lb-mix-match__modal-close", {
2526
2592
  type: "button",
2527
2593
  "data-modal-close": "",
2528
- "aria-label": "Close"
2594
+ "aria-label": s.closeModal || "Close"
2529
2595
  });
2530
2596
  close.appendChild(
2531
2597
  icon([
@@ -2566,15 +2632,15 @@ function buildPickerModal(opts) {
2566
2632
  type: "text",
2567
2633
  "data-modal-search": "",
2568
2634
  role: "searchbox",
2569
- "aria-label": "Search products",
2570
- placeholder: "Search products",
2635
+ "aria-label": s.searchProducts || "Search products",
2636
+ placeholder: s.searchProducts || "Search products",
2571
2637
  autocomplete: "off"
2572
2638
  });
2573
2639
  search.appendChild(input);
2574
2640
  const clear = el("button", "lb-mix-match__modal-search-clear", {
2575
2641
  type: "button",
2576
2642
  "data-modal-search-clear": "",
2577
- "aria-label": "Clear search",
2643
+ "aria-label": s.clearSearch || "Clear search",
2578
2644
  style: "display: none;"
2579
2645
  });
2580
2646
  clear.appendChild(
@@ -2603,7 +2669,7 @@ function buildPickerModal(opts) {
2603
2669
  style: "display: none;"
2604
2670
  });
2605
2671
  const emptyText = document.createElement("p");
2606
- emptyText.textContent = "No products match your search";
2672
+ emptyText.textContent = s.noSearchResults || "No products match your search";
2607
2673
  empty.appendChild(emptyText);
2608
2674
  dialog.appendChild(empty);
2609
2675
  dialog.appendChild(
@@ -2622,7 +2688,7 @@ function buildPickerModal(opts) {
2622
2688
  "data-modal-back": "",
2623
2689
  style: "display: none;"
2624
2690
  });
2625
- back.textContent = "Back";
2691
+ back.textContent = s.back || "Back";
2626
2692
  footer.appendChild(back);
2627
2693
  }
2628
2694
  footer.appendChild(
@@ -2637,7 +2703,7 @@ function buildPickerModal(opts) {
2637
2703
  "data-modal-next": "",
2638
2704
  style: "display: none;"
2639
2705
  });
2640
- next.textContent = "Next";
2706
+ next.textContent = s.next || "Next";
2641
2707
  footer.appendChild(next);
2642
2708
  }
2643
2709
  const done = el("button", "lb-mix-match__modal-done", {
@@ -2645,7 +2711,7 @@ function buildPickerModal(opts) {
2645
2711
  "data-modal-done": "",
2646
2712
  ...opts.wizard ? { style: "display: none;" } : {}
2647
2713
  });
2648
- done.textContent = "Done";
2714
+ done.textContent = s.done || "Done";
2649
2715
  footer.appendChild(done);
2650
2716
  dialog.appendChild(footer);
2651
2717
  overlay.appendChild(dialog);
@@ -2963,6 +3029,7 @@ function selectTier(container, allTiers, basePrice, discountType, index, transla
2963
3029
  savingsPercentEl.textContent = "(" + savingsPct + "%)";
2964
3030
  }
2965
3031
  savingsBar.style.display = "";
3032
+ savingsBar.classList.remove("lb-savings-pending");
2966
3033
  } else {
2967
3034
  savingsBar.style.display = "none";
2968
3035
  }
@@ -3146,6 +3213,7 @@ function recalcPricing2(container, sides, percent, buyQty, getQty, formatMoney)
3146
3213
  savingsPercentEl.textContent = "(" + pct + "%)";
3147
3214
  }
3148
3215
  savingsBar.style.display = "";
3216
+ savingsBar.classList.remove("lb-savings-pending");
3149
3217
  } else {
3150
3218
  savingsBar.style.display = "none";
3151
3219
  }
@@ -4532,6 +4600,15 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
4532
4600
  display: block;
4533
4601
  }
4534
4602
 
4603
+ /* Same reset, one more victim. A thumbnail is emitted empty and filled at
4604
+ hydration, so between the two it matched \`div:empty\` and vanished \u2014 taking
4605
+ the row's gap with it and starting that title 72px left of every other
4606
+ row's (issue #552). The box holds its place either way now: the product's
4607
+ image lands in it, or the placeholder mark does. */
4608
+ .lb-bundle-thumbnail:empty {
4609
+ display: flex;
4610
+ }
4611
+
4535
4612
  /* Divider */
4536
4613
  .lb-bundle-divider {
4537
4614
  height: var(--lb-border-width);
@@ -4570,11 +4647,6 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
4570
4647
  object-fit: var(--lb-thumbnail-img-fit, cover);
4571
4648
  }
4572
4649
 
4573
- .lb-bundle-thumbnail svg {
4574
- width: 28px;
4575
- height: 28px;
4576
- color: color-mix(in srgb, var(--lb-text) 35%, transparent);
4577
- }
4578
4650
 
4579
4651
  .lb-bundle-product-info {
4580
4652
  flex: 1;
@@ -4698,6 +4770,43 @@ a.lb-bundle-product-name:focus-visible {
4698
4770
  gap: 4px;
4699
4771
  }
4700
4772
 
4773
+ /* Variant picker select \u2014 styled to match the variant badge aesthetic.
4774
+ Sits inside .lb-bundle-variant-option-group so vertical spacing is owned
4775
+ by the group/groups flex gap, not the select itself.
4776
+
4777
+ Lives here, not in bundle-fixed.css, because every widget that renders an
4778
+ option group needs it and only the fixed widget loads that stylesheet: a
4779
+ page carrying just a Buy X Get Y bundle rendered raw browser selects, and
4780
+ the option-group height reservation (sized for the styled control) was
4781
+ wrong by 14px a group, which shifted the row on hydration.
4782
+ The same split applies to the headless package, which injects per type. */
4783
+ .lb-bundle-variant-select {
4784
+ display: inline-block;
4785
+ border: var(--lb-border-width) solid var(--lb-border);
4786
+ border-radius: var(--lb-radius);
4787
+ padding: 8px 32px 8px 12px;
4788
+ font-size: 12px;
4789
+ line-height: 16px;
4790
+ color: var(--lb-text);
4791
+ background: var(--lb-bg);
4792
+ font-family: inherit;
4793
+ cursor: pointer;
4794
+ appearance: none;
4795
+ -webkit-appearance: none;
4796
+ background-image: var(--lb-variant-chevron);
4797
+ background-repeat: no-repeat;
4798
+ background-position: right 8px center;
4799
+ background-size: 12px;
4800
+ width: 50%;
4801
+ max-width: 50%;
4802
+ }
4803
+
4804
+ .lb-bundle-variant-select:focus-visible {
4805
+ outline: 2px solid var(--lb-primary-color);
4806
+ outline-offset: 2px;
4807
+ box-shadow: none;
4808
+ }
4809
+
4701
4810
  .lb-bundle-variant-option-label {
4702
4811
  display: block;
4703
4812
  margin: 0;
@@ -4979,15 +5088,33 @@ a.lb-bundle-product-name:focus-visible {
4979
5088
  border: 0 !important;
4980
5089
  }
4981
5090
 
4982
- /* Placeholder SVG icon for missing images */
5091
+ /* Missing-image placeholder.
5092
+ Two marks share these rules: Shopify's own \`placeholder_svg_tag\`
5093
+ illustration, which the theme scripts clone out of the <template> in
5094
+ bundle-widget.liquid, and the line glyph the headless renderer falls back to
5095
+ when there is no Liquid to supply one. */
5096
+ .lb-thumb--placeholder > [data-lb-placeholder] {
5097
+ display: block;
5098
+ width: 100%;
5099
+ /* The mark carries the box's height, not the other way round: the
5100
+ "original" thumbnail ratio sizes a box to its image's proportions and an
5101
+ imageless row has no image, so without this the box would be 60px by
5102
+ nothing. A box that already has a ratio caps the mark instead. */
5103
+ height: auto;
5104
+ max-height: 100%;
5105
+ aspect-ratio: 1 / 1;
5106
+ }
5107
+
4983
5108
  .lb-bundle-placeholder-icon {
4984
- width: 28px;
4985
- height: 28px;
4986
5109
  stroke: color-mix(in srgb, var(--lb-text) 35%, transparent);
4987
5110
  stroke-width: 1.5;
4988
5111
  fill: none;
4989
5112
  }
4990
5113
 
5114
+ .lb-bundle-placeholder-svg {
5115
+ fill: color-mix(in srgb, var(--lb-text) 22%, transparent);
5116
+ }
5117
+
4991
5118
  /* Out-of-stock product row */
4992
5119
  .lb-bundle-product-row--oos {
4993
5120
  opacity: 0.5;
@@ -5019,6 +5146,16 @@ a.lb-bundle-product-name:focus-visible {
5019
5146
  white-space: nowrap;
5020
5147
  }
5021
5148
 
5149
+ /* The savings line is laid out from first paint and revealed on hydration,
5150
+ rather than being display:none until then.
5151
+ Its amounts are money strings the server cannot know, so filling them in
5152
+ place moves whatever shares the line; revealing an already-laid-out box
5153
+ moves nothing. The band around it is vertically centred, so reserving the
5154
+ line costs no height of its own. JS drops the class in updatePricing. */
5155
+ .lb-bundle-savings-line.lb-savings-pending {
5156
+ visibility: hidden;
5157
+ }
5158
+
5022
5159
  /* A/B test: hide save badge until JS swaps the label (prevents flash of default) */
5023
5160
  .lb-ab-pending {
5024
5161
  visibility: hidden;
@@ -5036,27 +5173,43 @@ a.lb-bundle-product-name:focus-visible {
5036
5173
  The title is the one box NOT reserved this way: text wrapping depends on the
5037
5174
  merchant's font, so it is server-rendered and its height is simply real. */
5038
5175
 
5176
+ /* A zero-width space, not a pixel height.
5177
+ These boxes are one line of text once JS fills them, and their height is
5178
+ whatever the merchant's font makes of \`line-height: normal\` \u2014 which the
5179
+ reset at the top of this file forces on every descendant, overriding the
5180
+ per-element line-heights above it. A hardcoded min-height therefore has to
5181
+ guess, and guessed 3px too tall: the row shrank on hydration in every theme.
5182
+ An empty line box measures exactly what the filled one will. */
5039
5183
  .lb-bundle-product-price:empty {
5040
5184
  display: inline-block;
5041
- min-height: 20px;
5042
5185
  }
5043
5186
 
5044
5187
  .lb-bundle-variant-badge:empty {
5045
5188
  display: block;
5046
- min-height: 20px;
5047
5189
  }
5048
5190
 
5049
5191
  .lb-bundle-product-unit-price:empty {
5050
5192
  display: block;
5051
- min-height: 16px;
5193
+ }
5194
+
5195
+ .lb-bundle-product-price:empty::before,
5196
+ .lb-bundle-variant-badge:empty::before,
5197
+ .lb-bundle-product-unit-price:empty::before,
5198
+ .lb-bundle-qty-inline:empty::before,
5199
+ .lb-bundle-sale-price:empty::before,
5200
+ .lb-bundle-compare-price:empty::before {
5201
+ content: "\\200B";
5052
5202
  }
5053
5203
 
5054
5204
  /* Label (16) + gap (4) + trigger (8 + 16 + 8 padding/line-height) per group,
5055
5205
  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. */
5206
+ Liquid, which knows how many options the product has.
5207
+
5208
+ The trigger is border-box, so its border is already inside that 52px; the
5209
+ calc used to add it again and reserved 2px per group too much. */
5057
5210
  .lb-bundle-variant-option-groups:empty {
5058
5211
  min-height: calc(
5059
- var(--lb-row-option-count, 1) * (52px + 2 * var(--lb-border-width)) +
5212
+ var(--lb-row-option-count, 1) * 52px +
5060
5213
  (var(--lb-row-option-count, 1) - 1) * 8px
5061
5214
  );
5062
5215
  }
@@ -5136,36 +5289,6 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
5136
5289
  font-variant-numeric: tabular-nums;
5137
5290
  white-space: nowrap;
5138
5291
  }
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
5292
  `;
5170
5293
  var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
5171
5294
 
@@ -6488,6 +6611,15 @@ var BUNDLE_BOGO_CSS = `/* Lime Bundles \u2014 BOGO (Buy X Get Y) bundle styles *
6488
6611
  text-decoration: line-through;
6489
6612
  opacity: 0.6;
6490
6613
  }
6614
+
6615
+ /* The reward row's struck original is emitted empty and filled on hydration,
6616
+ so anything sharing its line moves when it appears. The count is the only
6617
+ such thing, and it is a count, not part of the price phrase: pinning it to
6618
+ the row's right edge makes its position independent of the price text \u2014 the
6619
+ same right-slot treatment the fixed widget gives its quantity chip. */
6620
+ .lb-bogo .lb-bundle-qty-inline {
6621
+ margin-left: auto;
6622
+ }
6491
6623
  `;
6492
6624
  var BUNDLE_MULTI_STEP_CSS = `/**
6493
6625
  * Lime Bundles \u2014 Multi-step bundle widget styles (wizard chrome ONLY).