@lime-bundles/widget 4.3.0 → 4.5.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.js CHANGED
@@ -60,6 +60,14 @@ function toCents(amount) {
60
60
  const n = parseFloat(amount);
61
61
  return Number.isFinite(n) ? Math.round(n * 100) : 0;
62
62
  }
63
+ function imageAspectRatio(image) {
64
+ const w = image?.width;
65
+ const h = image?.height;
66
+ if (typeof w !== "number" || typeof h !== "number" || w <= 0 || h <= 0) {
67
+ return null;
68
+ }
69
+ return w / h;
70
+ }
63
71
  function adaptVariant(v, requiredQty, fallbackCurrency) {
64
72
  return {
65
73
  id: numericId(v.id),
@@ -101,6 +109,7 @@ function adaptProduct(product, bundleId, opts) {
101
109
  title: product.title,
102
110
  url: product.handle ? "/products/" + product.handle : null,
103
111
  featuredImage: product.featuredImage?.url ?? null,
112
+ featuredImageRatio: imageAspectRatio(product.featuredImage),
104
113
  quantity: productQty,
105
114
  selectedVariantId: initial ? initial.id : "",
106
115
  // Option names come off a variant, since the Storefront product query
@@ -445,7 +454,12 @@ function buildRowSkeleton(product, t, opts) {
445
454
  }
446
455
  row.setAttribute("data-product-id", String(product.productId));
447
456
  if (opts.bogoRole) row.setAttribute("data-bogo-role", opts.bogoRole);
448
- row.appendChild(box("div", "lb-bundle-thumbnail", { "data-thumbnail": "" }));
457
+ const thumb = box("div", "lb-bundle-thumbnail", { "data-thumbnail": "" });
458
+ const ratio = product.featuredImageRatio;
459
+ if (opts.thumbnailRatio === "original" && ratio != null && ratio > 0) {
460
+ thumb.style.setProperty("--lb-row-thumb-ratio", String(ratio));
461
+ }
462
+ row.appendChild(thumb);
449
463
  const info = box("div", "lb-bundle-product-info");
450
464
  const title = document.createElement(opts.href ? "a" : "span");
451
465
  title.className = "lb-bundle-product-name";
@@ -754,6 +768,16 @@ var DEFAULT_STRINGS = {
754
768
  chooseMoreComplete: "Choose __COUNT__ more to complete your bundle",
755
769
  stepOf: "Step __STEP__ of __TOTAL__",
756
770
  stepsCompleted: "__COUNT__ of __TOTAL__ steps completed",
771
+ doorChoose: "Choose your products",
772
+ doorContinue: "Continue building",
773
+ doorQuickSteps: {
774
+ one: "__COUNT__ quick step",
775
+ other: "__COUNT__ quick steps"
776
+ },
777
+ doorStepsToGo: {
778
+ one: "__COUNT__ step to go",
779
+ other: "__COUNT__ steps to go"
780
+ },
757
781
  each: " each",
758
782
  buyQty: "Buy __COUNT__",
759
783
  itemCount: {
@@ -1211,7 +1235,8 @@ function renderFixedBundle(container, bundle, onAddToCart, onCleanup) {
1211
1235
  const isOos = !product.variants.some((v) => v.available);
1212
1236
  const row = buildRowSkeleton(product, t, {
1213
1237
  isOos,
1214
- href: product.url
1238
+ href: product.url,
1239
+ thumbnailRatio: wc.productList?.thumbnailRatio
1215
1240
  });
1216
1241
  shell.products.appendChild(row);
1217
1242
  if (isOos) continue;
@@ -3217,7 +3242,8 @@ function renderBogoBundle(container, bundle, onAddToCart, onCleanup) {
3217
3242
  href: side.url,
3218
3243
  bogoRole: side.role,
3219
3244
  inlineQuantity: isGet ? getQty : buyQty,
3220
- badgeText: isGet && !isOos ? badgeText : null
3245
+ badgeText: isGet && !isOos ? badgeText : null,
3246
+ thumbnailRatio: wc.productList?.thumbnailRatio
3221
3247
  });
3222
3248
  shell.products.appendChild(row);
3223
3249
  if (isOos) return;
@@ -3286,6 +3312,32 @@ function stepsCompletedLabel(completed, total, t) {
3286
3312
  function moreToGoLabel(remaining, t) {
3287
3313
  return remaining > 0 ? fill(t.moreToGo || "__COUNT__ more to go", { COUNT: remaining }) : t.complete || "Complete";
3288
3314
  }
3315
+ function fillPlural(map, count, locale) {
3316
+ const form = new Intl.PluralRules(locale || "en").select(count);
3317
+ return fill(map[form] || map.other || "__COUNT__", { COUNT: count });
3318
+ }
3319
+ function doorLabel(anyPicks, t) {
3320
+ return anyPicks ? t.doorContinue || "Continue building" : t.doorChoose || "Choose your products";
3321
+ }
3322
+ function doorSubLabel(anyPicks, remaining, total, t) {
3323
+ if (!anyPicks) {
3324
+ const quick = t.doorQuickSteps;
3325
+ if (typeof quick === "string") return fill(quick, { COUNT: total });
3326
+ return fillPlural(
3327
+ quick || { one: "__COUNT__ quick step", other: "__COUNT__ quick steps" },
3328
+ total,
3329
+ t.locale
3330
+ );
3331
+ }
3332
+ return fillPlural(
3333
+ t.doorStepsToGo || {
3334
+ one: "__COUNT__ step to go",
3335
+ other: "__COUNT__ steps to go"
3336
+ },
3337
+ remaining,
3338
+ t.locale
3339
+ );
3340
+ }
3289
3341
 
3290
3342
  // ../render/src/multi-step/rules.ts
3291
3343
  function unitsInStep(selections, i) {
@@ -3347,20 +3399,28 @@ function updateStepGroups(deps) {
3347
3399
  const { container, steps, selections, t } = deps;
3348
3400
  for (let i = 0; i < steps.length; i++) {
3349
3401
  const min = steps[i].minQuantity || 1;
3350
- const countEl = container.querySelector(
3351
- '[data-step-count="' + i + '"]'
3402
+ const section = container.querySelector(
3403
+ '[data-step-group="' + i + '"]'
3352
3404
  );
3353
- if (countEl) {
3354
- countEl.textContent = ofSelectedLabel(
3355
- Math.min(unitsInStep(selections, i), min),
3356
- min,
3357
- t
3358
- );
3359
- countEl.classList.toggle(
3360
- "lb-multi-step__group-count--met",
3361
- stepSatisfied(selections, steps, i)
3405
+ if (section) {
3406
+ section.setAttribute(
3407
+ "data-step-mode",
3408
+ selections[i].length > 0 ? "open" : "row"
3362
3409
  );
3363
3410
  }
3411
+ const countLabel = ofSelectedLabel(
3412
+ Math.min(unitsInStep(selections, i), min),
3413
+ min,
3414
+ t
3415
+ );
3416
+ const met = stepSatisfied(selections, steps, i);
3417
+ const countEls = container.querySelectorAll(
3418
+ '[data-step-count="' + i + '"]'
3419
+ );
3420
+ for (let c = 0; c < countEls.length; c++) {
3421
+ countEls[c].textContent = countLabel;
3422
+ countEls[c].classList.toggle("lb-multi-step__group-count--met", met);
3423
+ }
3364
3424
  const slotsWrap = container.querySelector(
3365
3425
  '[data-step-slots="' + i + '"]'
3366
3426
  );
@@ -3382,14 +3442,50 @@ function updateStepGroups(deps) {
3382
3442
  );
3383
3443
  }
3384
3444
  }
3385
- for (let ti = 0; ti < steps.length; ti++) {
3386
- const trigger = container.querySelector(
3387
- '[data-step-trigger="' + ti + '"]'
3388
- );
3389
- if (trigger) {
3390
- trigger.style.display = stepHeadroom(selections, steps, ti) === 0 ? "none" : "";
3445
+ }
3446
+ function updateDoor(deps) {
3447
+ const { container, steps, selections, t } = deps;
3448
+ const door = container.querySelector("[data-step-door]");
3449
+ if (!door) return;
3450
+ let firstUnsatisfied = -1;
3451
+ let remaining = 0;
3452
+ let anyPicks = false;
3453
+ for (let i = 0; i < steps.length; i++) {
3454
+ if (selections[i].length > 0) anyPicks = true;
3455
+ if (!stepSatisfied(selections, steps, i)) {
3456
+ remaining += 1;
3457
+ if (firstUnsatisfied === -1) firstUnsatisfied = i;
3391
3458
  }
3392
3459
  }
3460
+ if (firstUnsatisfied === -1) {
3461
+ door.style.display = "none";
3462
+ door.classList.add("lb-multi-step__door--hidden");
3463
+ const parent = door.parentElement;
3464
+ if (parent && parent.firstElementChild !== door) {
3465
+ parent.insertBefore(door, parent.firstElementChild);
3466
+ }
3467
+ return;
3468
+ }
3469
+ door.style.display = "";
3470
+ door.classList.remove("lb-multi-step__door--hidden");
3471
+ door.setAttribute("data-step-door", String(firstUnsatisfied));
3472
+ const label = doorLabel(anyPicks, t);
3473
+ const labelEl = door.querySelector(
3474
+ ".lb-mix-match__add-product-label"
3475
+ );
3476
+ if (labelEl) labelEl.textContent = label;
3477
+ const subEl = door.querySelector(".lb-multi-step__door-sub");
3478
+ if (subEl) {
3479
+ subEl.textContent = doorSubLabel(anyPicks, remaining, steps.length, t);
3480
+ }
3481
+ const stepName = steps[firstUnsatisfied].name || "";
3482
+ door.setAttribute("aria-label", stepName ? label + ": " + stepName : label);
3483
+ const target = container.querySelector(
3484
+ '[data-step-group="' + firstUnsatisfied + '"]'
3485
+ );
3486
+ if (target && target.parentElement && target.previousElementSibling !== door) {
3487
+ target.parentElement.insertBefore(door, target);
3488
+ }
3393
3489
  }
3394
3490
  function updatePricing3(deps, data, calcDiscount, paint) {
3395
3491
  const { container, steps, selections } = deps;
@@ -3606,6 +3702,12 @@ function createWizard(deps) {
3606
3702
  for (const r of rows) r.refresh();
3607
3703
  updateModalMeta();
3608
3704
  }
3705
+ function maybeAutoAdvance() {
3706
+ if (!deps.autoAdvance) return;
3707
+ if (stepIndex >= steps.length - 1) return;
3708
+ if (stepHeadroom(selections, steps, stepIndex) !== 0) return;
3709
+ goToStep(stepIndex + 1);
3710
+ }
3609
3711
  function buildProductList(forStep) {
3610
3712
  if (!modalList) return;
3611
3713
  deps.dropdown?.unbindAll(modalList);
@@ -3622,6 +3724,7 @@ function createWizard(deps) {
3622
3724
  onCommittedQtyChanged: () => {
3623
3725
  deps.onSelectionChanged();
3624
3726
  refresh();
3727
+ maybeAutoAdvance();
3625
3728
  }
3626
3729
  });
3627
3730
  rows.push(row);
@@ -3701,6 +3804,7 @@ function createWizard(deps) {
3701
3804
  selections[stepIndex].push(newItem);
3702
3805
  deps.onSelectionChanged();
3703
3806
  refresh();
3807
+ maybeAutoAdvance();
3704
3808
  }
3705
3809
  if (modalList) modalList.addEventListener("click", onListClick);
3706
3810
  function buildFilters(forStep) {
@@ -3833,10 +3937,12 @@ function createWizard(deps) {
3833
3937
  setTimeout(() => {
3834
3938
  if (!modalOpen) overlay.style.display = "none";
3835
3939
  }, CLOSE_TRANSITION_MS2);
3836
- const stepTrigger = container.querySelector(
3837
- '[data-step-trigger="' + stepIndex + '"]'
3838
- );
3839
- const elToFocus = stepTrigger && stepTrigger.offsetParent !== null ? stepTrigger : deps.ctaButton;
3940
+ const visible = (el2) => el2 && el2.offsetParent !== null ? el2 : null;
3941
+ const elToFocus = visible(
3942
+ container.querySelector(
3943
+ '[data-step-row="' + stepIndex + '"]'
3944
+ )
3945
+ ) || visible(container.querySelector("[data-step-door]")) || deps.ctaButton;
3840
3946
  if (elToFocus && typeof elToFocus.focus === "function") {
3841
3947
  setTimeout(() => elToFocus.focus(), 0);
3842
3948
  }
@@ -3889,6 +3995,29 @@ function createWizard(deps) {
3889
3995
  }
3890
3996
 
3891
3997
  // src/renderers/multi-step.ts
3998
+ function buildPlusIcon() {
3999
+ const NS = "http://www.w3.org/2000/svg";
4000
+ const svg = document.createElementNS(NS, "svg");
4001
+ svg.setAttribute("width", "18");
4002
+ svg.setAttribute("height", "18");
4003
+ svg.setAttribute("viewBox", "0 0 18 18");
4004
+ svg.setAttribute("fill", "none");
4005
+ for (const [x1, y1, x2, y2] of [
4006
+ ["9", "3", "9", "15"],
4007
+ ["3", "9", "15", "9"]
4008
+ ]) {
4009
+ const line = document.createElementNS(NS, "line");
4010
+ line.setAttribute("x1", x1);
4011
+ line.setAttribute("y1", y1);
4012
+ line.setAttribute("x2", x2);
4013
+ line.setAttribute("y2", y2);
4014
+ line.setAttribute("stroke", "currentColor");
4015
+ line.setAttribute("stroke-width", "2");
4016
+ line.setAttribute("stroke-linecap", "round");
4017
+ svg.appendChild(line);
4018
+ }
4019
+ return svg;
4020
+ }
3892
4021
  function renderMultiStepBundle(container, bundle, onAddToCart, onCleanup) {
3893
4022
  const wc = bundle.widgetConfig;
3894
4023
  const t = DEFAULT_STRINGS;
@@ -3943,31 +4072,65 @@ function renderMultiStepBundle(container, bundle, onAddToCart, onCleanup) {
3943
4072
  shell.products.before(buildProgressBar(steps.length));
3944
4073
  const groups = document.createElement("div");
3945
4074
  groups.className = "lb-multi-step__groups";
4075
+ groups.setAttribute("data-step-groups", "");
4076
+ const door = document.createElement("button");
4077
+ door.type = "button";
4078
+ door.className = "lb-mix-match__add-product lb-multi-step__door";
4079
+ door.setAttribute("data-step-door", "0");
4080
+ const doorIcon = document.createElement("span");
4081
+ doorIcon.className = "lb-mix-match__add-product-icon";
4082
+ doorIcon.setAttribute("aria-hidden", "true");
4083
+ doorIcon.appendChild(buildPlusIcon());
4084
+ door.appendChild(doorIcon);
4085
+ const doorCopy = document.createElement("span");
4086
+ doorCopy.className = "lb-multi-step__door-copy";
4087
+ const doorLabel2 = document.createElement("span");
4088
+ doorLabel2.className = "lb-mix-match__add-product-label";
4089
+ doorCopy.appendChild(doorLabel2);
4090
+ const doorSub = document.createElement("span");
4091
+ doorSub.className = "lb-multi-step__door-sub";
4092
+ doorCopy.appendChild(doorSub);
4093
+ door.appendChild(doorCopy);
4094
+ groups.appendChild(door);
3946
4095
  steps.forEach((step, i) => {
3947
- const group = document.createElement("div");
4096
+ const group = document.createElement("section");
3948
4097
  group.className = "lb-multi-step__group";
3949
4098
  group.setAttribute("data-step-group", String(i));
4099
+ group.setAttribute("data-step-mode", "row");
4100
+ const buildCount = () => {
4101
+ const count = document.createElement("span");
4102
+ count.className = "lb-multi-step__group-count";
4103
+ count.setAttribute("data-step-count", String(i));
4104
+ return count;
4105
+ };
4106
+ const row = document.createElement("button");
4107
+ row.type = "button";
4108
+ row.className = "lb-multi-step__step-row";
4109
+ row.setAttribute("data-step-row", String(i));
4110
+ row.setAttribute("aria-label", "Choose products: " + step.name);
4111
+ const ix = document.createElement("span");
4112
+ ix.className = "lb-multi-step__step-ix";
4113
+ ix.setAttribute("aria-hidden", "true");
4114
+ ix.textContent = String(i + 1);
4115
+ row.appendChild(ix);
4116
+ const rowName = document.createElement("span");
4117
+ rowName.className = "lb-multi-step__group-name";
4118
+ rowName.textContent = step.name;
4119
+ row.appendChild(rowName);
4120
+ row.appendChild(buildCount());
4121
+ group.appendChild(row);
3950
4122
  const heading = document.createElement("div");
3951
4123
  heading.className = "lb-multi-step__group-heading";
3952
4124
  const name = document.createElement("span");
3953
4125
  name.className = "lb-multi-step__group-name";
3954
4126
  name.textContent = step.name;
3955
4127
  heading.appendChild(name);
3956
- const count = document.createElement("span");
3957
- count.className = "lb-multi-step__group-count";
3958
- count.setAttribute("data-step-count", String(i));
3959
- heading.appendChild(count);
4128
+ heading.appendChild(buildCount());
3960
4129
  group.appendChild(heading);
3961
4130
  const slots = document.createElement("div");
3962
- slots.className = "lb-mix-match__slots lb-edge-fade";
4131
+ slots.className = "lb-mix-match__slots lb-edge-fade lb-multi-step__group-slots";
3963
4132
  slots.setAttribute("data-step-slots", String(i));
3964
4133
  group.appendChild(slots);
3965
- const trigger = document.createElement("button");
3966
- trigger.type = "button";
3967
- trigger.className = "lb-mix-match__add-product";
3968
- trigger.setAttribute("data-step-trigger", String(i));
3969
- trigger.textContent = "Add a product";
3970
- group.appendChild(trigger);
3971
4134
  groups.appendChild(group);
3972
4135
  });
3973
4136
  shell.products.replaceWith(groups);
@@ -4004,6 +4167,7 @@ function renderMultiStepBundle(container, bundle, onAddToCart, onCleanup) {
4004
4167
  };
4005
4168
  function updateAll() {
4006
4169
  updateStepGroups(bodyDeps);
4170
+ updateDoor(bodyDeps);
4007
4171
  updatePricing3(
4008
4172
  bodyDeps,
4009
4173
  {
@@ -4024,6 +4188,8 @@ function renderMultiStepBundle(container, bundle, onAddToCart, onCleanup) {
4024
4188
  rulesMap,
4025
4189
  showQtySelector: wc.mixMatchShowQuantitySelector !== false,
4026
4190
  showTypeFilters: wc.mixMatchShowTypeFilters !== false,
4191
+ // Off by default: a stored config predating the key must not advance.
4192
+ autoAdvance: wc.multiStepAutoAdvance === true,
4027
4193
  formatMoney,
4028
4194
  dropdown: void 0,
4029
4195
  portalTarget: null,
@@ -4050,10 +4216,14 @@ function renderMultiStepBundle(container, bundle, onAddToCart, onCleanup) {
4050
4216
  },
4051
4217
  canRemove: (item) => canRemovePick2(selections, item, rulesMap)
4052
4218
  });
4053
- shell.root.querySelectorAll("[data-step-trigger]").forEach((el2) => {
4219
+ door.addEventListener("click", () => {
4220
+ if (wizard.isOpen()) return;
4221
+ wizard.open(parseInt(door.getAttribute("data-step-door") ?? "", 10) || 0);
4222
+ });
4223
+ shell.root.querySelectorAll("[data-step-row]").forEach((el2) => {
4054
4224
  el2.addEventListener("click", () => {
4055
4225
  if (wizard.isOpen()) return;
4056
- wizard.open(parseInt(el2.getAttribute("data-step-trigger") ?? "", 10) || 0);
4226
+ wizard.open(parseInt(el2.getAttribute("data-step-row") ?? "", 10) || 0);
4057
4227
  });
4058
4228
  });
4059
4229
  shell.cta.addEventListener("click", () => {
@@ -6247,12 +6417,19 @@ var BUNDLE_MULTI_STEP_CSS = `/**
6247
6417
  * Canon: WIDGET-DESIGN.md (spacing scale, radius system, chip family).
6248
6418
  */
6249
6419
 
6250
- /* \u2500\u2500 Step groups (widget body) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
6420
+ /* \u2500\u2500 Step groups (widget body) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
6421
+ One dashed door + one section per step. A step without picks shows a
6422
+ slim checklist row; a step with picks shows the heading + chosen
6423
+ cards. data-step-mode ("row" | "open") on the section picks the
6424
+ representation \u2014 both are in the markup, CSS hides the other, so the
6425
+ server-rendered untouched state IS the hydrated no-selection state.
6426
+ Spacing is per-pair rather than a flat gap: consecutive rows sit
6427
+ flush (hairline-separated), everything else breathes 16px. */
6251
6428
 
6252
6429
  .lb-multi-step__groups {
6253
6430
  display: flex;
6254
6431
  flex-direction: column;
6255
- gap: 16px;
6432
+ gap: 0;
6256
6433
  }
6257
6434
 
6258
6435
  .lb-multi-step__group {
@@ -6261,8 +6438,117 @@ var BUNDLE_MULTI_STEP_CSS = `/**
6261
6438
  gap: 8px;
6262
6439
  }
6263
6440
 
6441
+ .lb-multi-step__group + .lb-multi-step__group {
6442
+ margin-top: 16px;
6443
+ }
6444
+
6445
+ /* A run of empty-step rows reads as one checklist: flush, hairlines
6446
+ between (the first row of a run keeps no border). */
6447
+ .lb-multi-step__group[data-step-mode="row"] + .lb-multi-step__group[data-step-mode="row"] {
6448
+ margin-top: 0;
6449
+ }
6450
+
6451
+ .lb-multi-step__group[data-step-mode="row"]
6452
+ + .lb-multi-step__group[data-step-mode="row"]
6453
+ .lb-multi-step__step-row {
6454
+ border-top: var(--lb-border-width) solid var(--lb-border);
6455
+ }
6456
+
6457
+ /* Mode switch: a row-mode step hides its open chrome and vice versa. */
6458
+ .lb-multi-step__group[data-step-mode="row"] .lb-multi-step__group-heading,
6459
+ .lb-multi-step__group[data-step-mode="row"] .lb-multi-step__group-slots {
6460
+ display: none;
6461
+ }
6462
+
6463
+ .lb-multi-step__group[data-step-mode="open"] .lb-multi-step__step-row {
6464
+ display: none;
6465
+ }
6466
+
6467
+ /* Checklist row \u2014 the empty step's whole representation, and a tap
6468
+ target that deep-links the wizard to that step. Quiet by design: the
6469
+ door above it is the primary action. */
6470
+ .lb-multi-step__step-row {
6471
+ display: flex;
6472
+ align-items: center;
6473
+ gap: 12px;
6474
+ width: 100%;
6475
+ min-height: 48px;
6476
+ padding: 10px 2px;
6477
+ background: none;
6478
+ border: none;
6479
+ font-family: inherit;
6480
+ text-align: left;
6481
+ cursor: pointer;
6482
+ box-sizing: border-box;
6483
+ transition: background-color 0.25s ease;
6484
+ }
6485
+
6486
+ .lb-multi-step__step-row:hover {
6487
+ background: color-mix(in srgb, var(--lb-text) 3%, transparent);
6488
+ }
6489
+
6490
+ .lb-multi-step__step-row:focus-visible {
6491
+ outline: 2px solid var(--lb-primary-color);
6492
+ outline-offset: -2px;
6493
+ border-radius: var(--lb-radius-sm);
6494
+ }
6495
+
6496
+ .lb-multi-step__step-row .lb-multi-step__group-count {
6497
+ margin-left: auto;
6498
+ }
6499
+
6500
+ /* Step number \u2014 quiet index, tabular so multi-digit lists align. */
6501
+ .lb-multi-step__step-ix {
6502
+ width: 18px;
6503
+ min-width: 18px;
6504
+ font-size: 12px;
6505
+ font-weight: 500;
6506
+ color: color-mix(in srgb, var(--lb-text) 45%, transparent);
6507
+ font-variant-numeric: tabular-nums;
6508
+ }
6509
+
6510
+ /* The door \u2014 the single wizard entry point, reusing the dashed
6511
+ add-product recipe with a two-line label. JS moves it directly above
6512
+ the first unsatisfied step and hides it once every step is met. The
6513
+ add-product recipe's own margin-top is for its mix & match position
6514
+ (below the slot list); here the pair rules own all spacing. */
6515
+ .lb-multi-step__door {
6516
+ margin-top: 0;
6517
+ }
6518
+
6519
+ .lb-multi-step__door + .lb-multi-step__group {
6520
+ margin-top: 12px;
6521
+ }
6522
+
6523
+ .lb-multi-step__door + .lb-multi-step__group[data-step-mode="row"] {
6524
+ margin-top: 4px;
6525
+ }
6526
+
6527
+ .lb-multi-step__group + .lb-multi-step__door {
6528
+ margin-top: 16px;
6529
+ }
6530
+
6531
+ /* A hidden door is parked as the container's first child (updateDoor), so
6532
+ the group after it is really the first visible element \u2014 no door gap. */
6533
+ .lb-multi-step__door--hidden:first-child + .lb-multi-step__group {
6534
+ margin-top: 0;
6535
+ }
6536
+
6537
+ .lb-multi-step__door-copy {
6538
+ display: flex;
6539
+ flex-direction: column;
6540
+ gap: 2px;
6541
+ }
6542
+
6543
+ .lb-multi-step__door-sub {
6544
+ font-size: 13px;
6545
+ font-weight: 400;
6546
+ line-height: 1.2;
6547
+ color: color-mix(in srgb, var(--lb-text) 55%, transparent);
6548
+ }
6549
+
6264
6550
  /* Heading is a quiet, non-interactive row: step name left, live count
6265
- chip right. The step's dashed trigger below is the click target. */
6551
+ chip right. The step's chosen cards below are the edit targets. */
6266
6552
  .lb-multi-step__group-heading {
6267
6553
  display: flex;
6268
6554
  align-items: center;