@lime-bundles/react 6.1.0 → 7.0.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.
@@ -0,0 +1,1929 @@
1
+ /* Lime Bundles — Default styles with CSS custom properties for theming.
2
+ * Import: import '@lime-bundles/react/styles.css'
3
+ * Override: set --lb-* variables on any ancestor element.
4
+ *
5
+ * Mirrors the storefront theme widget's "manifest + receipt" redesign
6
+ * (extensions/bundle-theme/assets/bundle-base.css + bundle-fixed.css):
7
+ * a quiet title block, hairline-separated product rows with a right-aligned
8
+ * price column, and a receipt-style summary whose sale price is the heaviest
9
+ * element. No colored header band, no save-badge pill, no savings bar — the
10
+ * single savings signal is a quiet "You save" line under the total.
11
+ */
12
+
13
+ :root {
14
+ --lb-primary-color: #1a1a1a;
15
+ --lb-secondary-color: #666;
16
+ --lb-accent-color: #2563eb;
17
+ --lb-background: #fff;
18
+ --lb-border-color: #e5e7eb;
19
+ --lb-border-radius: 8px;
20
+ --lb-font-family: inherit;
21
+ --lb-font-size: 16px;
22
+ --lb-spacing-sm: 8px;
23
+ --lb-spacing-md: 16px;
24
+ --lb-spacing-lg: 24px;
25
+ --lb-button-bg: var(--lb-primary-color);
26
+ --lb-button-text: #fff;
27
+ --lb-button-radius: var(--lb-border-radius);
28
+ --lb-error-color: #92400e;
29
+ /* Cap on the per-bundle product/slot/tier list height — keeps long
30
+ bundles from pushing the CTA off-screen. The list scrolls
31
+ internally (with the .lb-edge-fade treatment) when content exceeds
32
+ this. */
33
+ --lb-list-max-height: 360px;
34
+ /* Depth of the soft fade at a scroll list's clipped edge. */
35
+ --lb-edge-fade-size: 20px;
36
+ }
37
+
38
+ /* Derived "small" corner radius for inner controls (thumbnails, variant
39
+ selects, the qty chip). One step tighter than the global --lb-radius so
40
+ small elements stay proportionate. Both come from the global radius preset
41
+ (emitted as --lb-radius by useWidgetConfigVars); nothing else sets a radius.
42
+ Falls back to --lb-border-radius until the bundle config has loaded, matching
43
+ the rest of this stylesheet. */
44
+ .lb-bundle {
45
+ --lb-radius-sm: max(0px, calc(var(--lb-radius, var(--lb-border-radius)) - 4px));
46
+ }
47
+
48
+ /* Edge fade — the shared treatment for inset scroll lists (fixed products,
49
+ mix & match slots, volume tiers). The native scrollbar channel is hidden;
50
+ instead the clipped edge of the list softly fades so content melts away
51
+ rather than ending in a hard cut against an app-chrome scrollbar. The
52
+ useEdgeFade hook toggles data-edge-fade so an edge fades ONLY when content
53
+ is actually scrolled past it — never at rest — which keeps the first/last
54
+ card and the volume "Most popular" badge from ever being dimmed. Without JS
55
+ the list simply scrolls with no bar and no fade (a safe, quiet fallback). */
56
+ .lb-edge-fade {
57
+ scrollbar-width: none;
58
+ }
59
+ .lb-edge-fade::-webkit-scrollbar {
60
+ width: 0;
61
+ height: 0;
62
+ }
63
+ .lb-edge-fade[data-edge-fade="top"] {
64
+ -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lb-edge-fade-size));
65
+ mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lb-edge-fade-size));
66
+ }
67
+ .lb-edge-fade[data-edge-fade="bottom"] {
68
+ -webkit-mask-image: linear-gradient(to top, transparent 0, #000 var(--lb-edge-fade-size));
69
+ mask-image: linear-gradient(to top, transparent 0, #000 var(--lb-edge-fade-size));
70
+ }
71
+ .lb-edge-fade[data-edge-fade="both"] {
72
+ -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lb-edge-fade-size), #000 calc(100% - var(--lb-edge-fade-size)), transparent 100%);
73
+ mask-image: linear-gradient(to bottom, transparent 0, #000 var(--lb-edge-fade-size), #000 calc(100% - var(--lb-edge-fade-size)), transparent 100%);
74
+ }
75
+
76
+ .lb-bundle {
77
+ font-family: var(--lb-font-family);
78
+ font-size: var(--lb-font-size);
79
+ /* `--lb-text` is the merchant-driven body colour emitted by
80
+ useWidgetConfigVars; fall back to --lb-primary-color when the bundle
81
+ hasn't loaded yet. */
82
+ color: var(--lb-text, var(--lb-primary-color));
83
+ background: var(--lb-bg, var(--lb-background));
84
+ border: var(--lb-border-width, 1px) solid var(--lb-border, var(--lb-border-color));
85
+ border-radius: var(--lb-radius, var(--lb-border-radius));
86
+ padding: 20px;
87
+ box-sizing: border-box;
88
+ max-width: 440px;
89
+ }
90
+
91
+ /* General sibling combinator (`~`) rather than adjacent (`+`) so the
92
+ spacing still applies when other elements sit between rendered
93
+ <Bundle>s (e.g. wrapper divs or breadcrumb markup in the consumer
94
+ layout). Single-bundle pages stay unaffected. */
95
+ .lb-bundle ~ .lb-bundle {
96
+ margin-top: var(--lb-spacing-lg);
97
+ }
98
+
99
+ /* ───────────────────────────────────────────────────────────────────
100
+ * Header — a plain section-heading title block in the host theme's own
101
+ * type. No colored band, no full-bleed, no save-badge pill.
102
+ * ─────────────────────────────────────────────────────────────────── */
103
+
104
+ .lb-bundle-header {
105
+ margin: 0 0 16px;
106
+ }
107
+
108
+ .lb-bundle-header__content {
109
+ min-width: 0;
110
+ }
111
+
112
+ .lb-bundle-title {
113
+ /* Weight 600 (not 700): the receipt's sale price is the widget's heaviest,
114
+ largest element, so the title steps down to clear the hierarchy. */
115
+ font-size: 18px;
116
+ font-weight: 600;
117
+ line-height: 24px;
118
+ letter-spacing: -0.01em;
119
+ color: var(--lb-text, var(--lb-primary-color));
120
+ margin: 0;
121
+ }
122
+
123
+ .lb-bundle-subtitle {
124
+ font-size: 14px;
125
+ font-weight: 400;
126
+ line-height: 20px;
127
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
128
+ margin: 4px 0 0;
129
+ }
130
+
131
+ .lb-bundle__instructions {
132
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
133
+ margin: 0 0 var(--lb-spacing-md);
134
+ }
135
+
136
+ /* Divider */
137
+ .lb-bundle-divider {
138
+ height: var(--lb-border-width, 1px);
139
+ background: var(--lb-border, var(--lb-border-color));
140
+ margin: 16px 0;
141
+ }
142
+
143
+ /* ───────────────────────────────────────────────────────────────────
144
+ * Product list — manifest rows.
145
+ * ─────────────────────────────────────────────────────────────────── */
146
+
147
+ .lb-bundle__products {
148
+ display: flex;
149
+ flex-direction: column;
150
+ gap: 12px;
151
+ margin: 0;
152
+ max-height: var(--lb-list-max-height);
153
+ overflow-y: auto;
154
+ /* Scrollbar hidden + edge fade — see .lb-edge-fade above. */
155
+ }
156
+
157
+ /* Both bundle types use the same card layout: a bordered row with a
158
+ thumbnail, an info column (name → variant → price → unit → pickers on the
159
+ inherited type), and a right-slot element. Fixed bundles can't remove items,
160
+ so their right slot is the quiet .lb-bundle-qty-chip (×N); mix & match puts a
161
+ remove × there instead. Mirrors the storefront theme widget
162
+ (extensions/bundle-theme/assets/bundle-fixed.css + bundle-mix-match.css). */
163
+ .lb-bundle-product-row {
164
+ display: flex;
165
+ gap: 12px;
166
+ align-items: flex-start;
167
+ padding: 8px;
168
+ border: var(--lb-border-width, 1px) solid var(--lb-border, var(--lb-border-color));
169
+ border-radius: var(--lb-radius, var(--lb-border-radius));
170
+ box-sizing: border-box;
171
+ }
172
+
173
+ /* Out-of-stock product row */
174
+ .lb-bundle-product-row--oos {
175
+ opacity: 0.5;
176
+ }
177
+
178
+ .lb-bundle__product-image {
179
+ width: 60px;
180
+ min-width: 60px;
181
+ /* Aspect-ratio + object-fit derived from the merchant's
182
+ `productList.thumbnailRatio` enum, applied via useWidgetConfigVars().
183
+ Defaults preserve a square cover thumbnail. */
184
+ aspect-ratio: var(--lb-thumbnail-aspect-ratio, 1 / 1);
185
+ object-fit: var(--lb-thumbnail-img-fit, cover);
186
+ /* Thumbnails carry only the derived corner radius — no border. */
187
+ border-radius: var(--lb-radius-sm);
188
+ box-sizing: border-box;
189
+ flex-shrink: 0;
190
+ }
191
+
192
+ /* Info column flows name → variant badge → prices → unit price → pickers on
193
+ the inherited type. */
194
+ .lb-bundle__product-info {
195
+ flex: 1;
196
+ min-width: 0;
197
+ }
198
+
199
+ .lb-bundle__product-title {
200
+ margin: 0;
201
+ font-size: 16px;
202
+ font-weight: 600;
203
+ line-height: 20px;
204
+ color: var(--lb-text, var(--lb-primary-color));
205
+ }
206
+
207
+ /* Read-only variant text under the product name. */
208
+ .lb-bundle-variant-badge {
209
+ display: block;
210
+ font-size: 14px;
211
+ line-height: 20px;
212
+ margin-top: 2px;
213
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
214
+ }
215
+
216
+ /* Price row — compare, price and inline "×N" share one line, unit price below. */
217
+ .lb-bundle-product-prices {
218
+ display: flex;
219
+ align-items: baseline;
220
+ gap: 6px;
221
+ flex-wrap: wrap;
222
+ margin-top: 4px;
223
+ }
224
+
225
+ /* Quantity chip — the fixed card's right slot. A quiet, non-interactive count
226
+ (×N), deliberately border-less so it never reads as the mix & match remove
227
+ button. */
228
+ .lb-bundle-qty-chip {
229
+ flex-shrink: 0;
230
+ min-width: 34px;
231
+ height: 28px;
232
+ padding: 0 9px;
233
+ display: inline-flex;
234
+ align-items: center;
235
+ justify-content: center;
236
+ border-radius: var(--lb-radius-sm);
237
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 7%, transparent);
238
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 75%, transparent);
239
+ font-size: 14px;
240
+ font-weight: 500;
241
+ line-height: 1;
242
+ font-variant-numeric: tabular-nums;
243
+ white-space: nowrap;
244
+ }
245
+
246
+ .lb-bundle__product-price {
247
+ display: var(--lb-product-price-display, block);
248
+ font-size: 14px;
249
+ font-weight: 400;
250
+ line-height: 20px;
251
+ color: var(--lb-text, var(--lb-primary-color));
252
+ font-variant-numeric: tabular-nums;
253
+ margin: 0;
254
+ }
255
+
256
+ .lb-bundle-product-compare-price {
257
+ display: var(--lb-product-compare-display, inline);
258
+ font-size: 14px;
259
+ font-weight: 400;
260
+ line-height: 20px;
261
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
262
+ text-decoration: line-through;
263
+ font-variant-numeric: tabular-nums;
264
+ }
265
+
266
+ .lb-bundle__product-unit-price {
267
+ display: block;
268
+ font-size: 12px;
269
+ line-height: 16px;
270
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
271
+ margin: 2px 0 0;
272
+ }
273
+
274
+ /* Inline quantity — "×2" beside the product price (mix & match chosen-product
275
+ cards). Quiet, theme-inherited; the fixed card uses .lb-bundle-qty-chip in the
276
+ card's right slot instead. */
277
+ .lb-bundle-qty-inline {
278
+ font-size: 13px;
279
+ font-weight: 400;
280
+ margin-left: 5px;
281
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 55%, transparent);
282
+ font-variant-numeric: tabular-nums;
283
+ white-space: nowrap;
284
+ }
285
+
286
+ .lb-bundle-oos-label {
287
+ font-size: 12px;
288
+ font-weight: 500;
289
+ color: var(--lb-low-stock-text, var(--lb-error-color));
290
+ white-space: nowrap;
291
+ margin-left: auto;
292
+ }
293
+
294
+ .lb-bundle__product-variant-pickers {
295
+ display: flex;
296
+ flex-direction: column;
297
+ gap: 8px;
298
+ margin-top: 8px;
299
+ }
300
+
301
+ /* "Only X left" low-stock badge */
302
+ .lb-bundle-low-stock-badge {
303
+ display: inline-flex;
304
+ align-items: center;
305
+ gap: 4px;
306
+ margin-top: 4px;
307
+ padding: 2px 8px;
308
+ font-size: 11px;
309
+ font-weight: 600;
310
+ line-height: 1.4;
311
+ color: var(--lb-low-stock-text, var(--lb-error-color));
312
+ background-color: var(--lb-low-stock-bg, #fef3c7);
313
+ border-radius: 4px;
314
+ white-space: nowrap;
315
+ }
316
+
317
+ /* ───────────────────────────────────────────────────────────────────
318
+ * Mix-and-match — modal-based picker UX.
319
+ *
320
+ * Ported one-for-one from the storefront theme widget
321
+ * (extensions/bundle-theme/assets/bundle-mix-match.css). The shopper picks
322
+ * distinct products from a portaled modal; chosen products render as cards
323
+ * with a segmented progress bar above and a receipt summary below.
324
+ *
325
+ * Var convention follows this stylesheet: merchant-configurable colours use
326
+ * `var(--lb-text, var(--lb-primary-color))`; `--lb-border` / `--lb-radius`
327
+ * fall back to `--lb-border-color` / `--lb-border-radius`. The picker
328
+ * (`--lb-picker-*`) vars are emitted by useWidgetConfigVars onto both the
329
+ * widget root and the portaled modal overlay.
330
+ * ─────────────────────────────────────────────────────────────────── */
331
+
332
+ /* Screen-reader-only live regions inside the modal. */
333
+ .lb-visually-hidden {
334
+ position: absolute;
335
+ width: 1px;
336
+ height: 1px;
337
+ padding: 0;
338
+ margin: -1px;
339
+ overflow: hidden;
340
+ clip: rect(0, 0, 0, 0);
341
+ white-space: nowrap;
342
+ border: 0;
343
+ }
344
+
345
+ /* === Chosen-product card list ============================================ */
346
+ .lb-mix-match__slots {
347
+ display: flex;
348
+ flex-direction: column;
349
+ gap: 12px;
350
+ max-height: var(--lb-list-max-height);
351
+ overflow-y: auto;
352
+ /* Scrollbar hidden + edge fade — see .lb-edge-fade above. */
353
+ }
354
+
355
+ .lb-mix-match__slots:empty {
356
+ display: none;
357
+ }
358
+
359
+ /* === Segmented progress bar ============================================== */
360
+ .lb-mix-match__progress {
361
+ margin-bottom: 16px;
362
+ }
363
+
364
+ .lb-mix-match__progress-segments {
365
+ display: flex;
366
+ gap: 6px;
367
+ width: 100%;
368
+ }
369
+
370
+ .lb-mix-match__progress-segment {
371
+ flex: 1;
372
+ height: 6px;
373
+ /* Follows the global radius (like the widget's other small elements); the
374
+ modal override below switches it to the picker radius. Capped by the 6px
375
+ height, so it reads as a pill until the merchant picks a tighter radius. */
376
+ border-radius: var(--lb-radius-sm);
377
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 12%, transparent);
378
+ transition: background 0.25s ease;
379
+ }
380
+
381
+ .lb-mix-match__progress-segment--filled {
382
+ background: var(--lb-text, var(--lb-primary-color));
383
+ }
384
+
385
+ .lb-mix-match__progress-labels {
386
+ display: flex;
387
+ justify-content: space-between;
388
+ margin-top: 8px;
389
+ }
390
+
391
+ .lb-mix-match__progress-count {
392
+ font-size: 12px;
393
+ font-weight: 500;
394
+ line-height: 16px;
395
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
396
+ }
397
+
398
+ .lb-mix-match__progress-remaining {
399
+ font-size: 12px;
400
+ font-weight: 500;
401
+ line-height: 16px;
402
+ color: var(--lb-text, var(--lb-primary-color));
403
+ }
404
+
405
+ .lb-mix-match__modal-progress {
406
+ margin: 0;
407
+ padding: 0;
408
+ }
409
+
410
+ /* The modal's progress segments follow the picker radius, not the global one. */
411
+ .lb-mix-match__modal-progress .lb-mix-match__progress-segment {
412
+ border-radius: var(--lb-picker-radius-sm);
413
+ }
414
+
415
+ /* === Chosen-product cards =============================================== */
416
+ .lb-mix-match__slot--filled {
417
+ display: flex;
418
+ align-items: center;
419
+ gap: 12px;
420
+ padding: 8px;
421
+ border: var(--lb-border-width, 1px) solid var(--lb-border, var(--lb-border-color));
422
+ border-radius: var(--lb-radius, var(--lb-border-radius));
423
+ box-sizing: border-box;
424
+ }
425
+
426
+ .lb-mix-match .lb-bundle-thumbnail {
427
+ position: relative;
428
+ width: 60px;
429
+ min-width: 60px;
430
+ /* Thumbnails carry only the derived corner radius — no border. */
431
+ border-radius: var(--lb-radius-sm);
432
+ box-sizing: border-box;
433
+ flex-shrink: 0;
434
+ }
435
+
436
+ .lb-mix-match .lb-bundle-thumbnail img {
437
+ width: 100%;
438
+ aspect-ratio: var(--lb-thumbnail-aspect-ratio, 1 / 1);
439
+ object-fit: var(--lb-thumbnail-img-fit, cover);
440
+ border-radius: var(--lb-radius-sm);
441
+ border: none;
442
+ display: block;
443
+ }
444
+
445
+ .lb-mix-match__slot--filled .lb-mix-match__filled-info {
446
+ flex: 1;
447
+ min-width: 0;
448
+ display: flex;
449
+ flex-direction: column;
450
+ }
451
+
452
+ .lb-mix-match__slot--filled .lb-mix-match__filled-title {
453
+ font-size: 16px;
454
+ font-weight: 600;
455
+ line-height: 20px;
456
+ color: var(--lb-text, var(--lb-primary-color));
457
+ text-decoration: none;
458
+ overflow-wrap: break-word;
459
+ }
460
+
461
+ .lb-mix-match__slot--filled a.lb-mix-match__filled-title:hover {
462
+ text-decoration: underline;
463
+ }
464
+
465
+ .lb-mix-match__slot--filled a.lb-mix-match__filled-title:focus-visible {
466
+ outline: 2px solid var(--lb-primary-color);
467
+ outline-offset: 2px;
468
+ box-shadow: none;
469
+ border-radius: 2px;
470
+ }
471
+
472
+ .lb-mix-match__filled-variant {
473
+ display: block;
474
+ font-size: 14px;
475
+ line-height: 20px;
476
+ margin-top: 2px;
477
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
478
+ }
479
+
480
+ .lb-mix-match__slot--filled .lb-mix-match__filled-price {
481
+ display: var(--lb-product-price-display, block);
482
+ font-size: 14px;
483
+ line-height: 20px;
484
+ margin-top: 2px;
485
+ color: var(--lb-text, var(--lb-primary-color));
486
+ font-weight: 500;
487
+ }
488
+
489
+ .lb-mix-match__filled-compare {
490
+ display: var(--lb-product-compare-display, inline);
491
+ font-size: 14px;
492
+ font-weight: 400;
493
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
494
+ text-decoration: line-through;
495
+ margin-right: 4px;
496
+ }
497
+
498
+ .lb-mix-match__slot--filled .lb-bundle-product-unit-price {
499
+ display: block;
500
+ font-size: 12px;
501
+ line-height: 16px;
502
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
503
+ margin-top: 2px;
504
+ }
505
+
506
+ .lb-mix-match__slot-remove {
507
+ flex-shrink: 0;
508
+ width: 36px;
509
+ height: 36px;
510
+ min-width: 36px;
511
+ display: flex;
512
+ align-items: center;
513
+ justify-content: center;
514
+ background: none;
515
+ border: var(--lb-border-width, 1px) solid var(--lb-border, var(--lb-border-color));
516
+ border-radius: var(--lb-radius, var(--lb-border-radius));
517
+ cursor: pointer;
518
+ color: var(--lb-text, var(--lb-primary-color));
519
+ padding: 0;
520
+ margin-left: auto;
521
+ }
522
+
523
+ .lb-mix-match__slot-remove:hover {
524
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 6%, transparent);
525
+ }
526
+
527
+ .lb-mix-match__slot-remove:focus-visible {
528
+ outline: 2px solid var(--lb-primary-color);
529
+ outline-offset: -2px;
530
+ box-shadow: none;
531
+ }
532
+
533
+ /* === Dashed "Add a product" slot ======================================= */
534
+ .lb-mix-match__add-product {
535
+ display: flex;
536
+ align-items: center;
537
+ gap: 12px;
538
+ width: 100%;
539
+ margin-top: 12px;
540
+ padding: 8px;
541
+ background: none;
542
+ border: 1px dashed color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 28%, transparent);
543
+ border-radius: var(--lb-radius, var(--lb-border-radius));
544
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 75%, transparent);
545
+ font-family: inherit;
546
+ font-size: 15px;
547
+ font-weight: 500;
548
+ line-height: 1.2;
549
+ text-align: left;
550
+ cursor: pointer;
551
+ box-sizing: border-box;
552
+ transition: border-color 0.15s ease, color 0.15s ease;
553
+ }
554
+
555
+ .lb-mix-match__add-product:hover {
556
+ border-color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 45%, transparent);
557
+ color: var(--lb-text, var(--lb-primary-color));
558
+ }
559
+
560
+ .lb-mix-match__add-product:focus-visible {
561
+ outline: 2px solid var(--lb-primary-color);
562
+ outline-offset: 2px;
563
+ box-shadow: none;
564
+ }
565
+
566
+ .lb-mix-match__add-product-icon {
567
+ display: flex;
568
+ align-items: center;
569
+ justify-content: center;
570
+ width: 60px;
571
+ min-width: 60px;
572
+ height: 40px;
573
+ flex-shrink: 0;
574
+ color: inherit;
575
+ }
576
+
577
+ /* === Variant option groups (shared with picker rows) =================== */
578
+ .lb-bundle-variant-option-groups {
579
+ display: flex;
580
+ flex-direction: column;
581
+ gap: 8px;
582
+ margin-top: 8px;
583
+ }
584
+
585
+ .lb-bundle-variant-option-group {
586
+ display: flex;
587
+ flex-direction: column;
588
+ gap: 2px;
589
+ }
590
+
591
+ .lb-bundle-variant-option-label {
592
+ font-size: 12px;
593
+ font-weight: 500;
594
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 70%, transparent);
595
+ }
596
+
597
+ /* Derived "small" radius for the picker's inner controls (search, thumbnails,
598
+ variant select, qty stepper, Add/Done buttons), one step tighter than the
599
+ modal's --lb-picker-radius — the picker-scoped mirror of --lb-radius-sm in
600
+ the main widget. Defined on the overlay (which carries data-modal-overlay)
601
+ so the modal AND any portaled child inherit it; --lb-picker-radius is
602
+ emitted onto this element by useWidgetConfigVars. The modal shell and
603
+ product tiles keep the full --lb-picker-radius. Falls back to 16px to match
604
+ the rest of the picker rules until the bundle config has loaded. */
605
+ [data-modal-overlay] {
606
+ --lb-picker-radius-sm: max(0px, calc(var(--lb-picker-radius, 16px) - 4px));
607
+ }
608
+
609
+ /* === Modal overlay ===================================================== */
610
+ .lb-mix-match__modal-overlay {
611
+ position: fixed;
612
+ top: 0;
613
+ left: 0;
614
+ right: 0;
615
+ bottom: 0;
616
+ background: rgba(0, 0, 0, 0.5);
617
+ z-index: 9999;
618
+ display: flex;
619
+ align-items: center;
620
+ justify-content: center;
621
+ opacity: 0;
622
+ transition: opacity 0.2s ease-out;
623
+ font-family: var(--lb-font-family);
624
+ }
625
+
626
+ .lb-mix-match__modal-overlay--open {
627
+ opacity: 1;
628
+ }
629
+
630
+ /* === Modal panel ====================================================== */
631
+ .lb-mix-match__modal {
632
+ background: var(--lb-picker-bg, var(--lb-bg, var(--lb-background)));
633
+ color: var(--lb-picker-text, var(--lb-text, var(--lb-primary-color)));
634
+ border-radius: var(--lb-picker-radius, 16px);
635
+ border: var(--lb-picker-border-width, 0px) solid var(--lb-picker-border-color, var(--lb-border-color));
636
+ box-sizing: border-box;
637
+ width: 100%;
638
+ max-width: 520px;
639
+ max-height: 70vh;
640
+ display: flex;
641
+ flex-direction: column;
642
+ overflow: hidden;
643
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);
644
+ transform: translateY(24px);
645
+ transition: transform 0.25s ease-out;
646
+ will-change: transform;
647
+ }
648
+
649
+ .lb-mix-match__modal-overlay--open .lb-mix-match__modal {
650
+ transform: translateY(0);
651
+ }
652
+
653
+ /* === Modal header ===================================================== */
654
+ .lb-mix-match__modal-header {
655
+ display: flex;
656
+ flex-direction: column;
657
+ gap: 16px;
658
+ padding: 20px;
659
+ border-bottom: 1px solid color-mix(in srgb, var(--lb-picker-text, var(--lb-text, var(--lb-primary-color))) 15%, transparent);
660
+ flex-shrink: 0;
661
+ }
662
+
663
+ .lb-mix-match__modal-header-top {
664
+ display: flex;
665
+ align-items: flex-start;
666
+ justify-content: space-between;
667
+ gap: 12px;
668
+ }
669
+
670
+ .lb-mix-match__modal-heading {
671
+ min-width: 0;
672
+ }
673
+
674
+ .lb-mix-match__modal-title {
675
+ font-size: 20px;
676
+ font-weight: 600;
677
+ line-height: 24px;
678
+ margin: 0;
679
+ color: inherit;
680
+ }
681
+
682
+ .lb-mix-match__modal-subtitle {
683
+ margin: 4px 0 0;
684
+ font-size: 13px;
685
+ line-height: 18px;
686
+ color: color-mix(in srgb, var(--lb-picker-text, var(--lb-text, var(--lb-primary-color))) 60%, transparent);
687
+ }
688
+
689
+ .lb-mix-match__modal-subtitle:empty {
690
+ display: none;
691
+ }
692
+
693
+ .lb-mix-match__modal-close {
694
+ min-width: 44px;
695
+ min-height: 44px;
696
+ display: flex;
697
+ align-items: center;
698
+ justify-content: center;
699
+ background: none;
700
+ border: none;
701
+ cursor: pointer;
702
+ color: var(--lb-picker-text, var(--lb-text, var(--lb-primary-color)));
703
+ border-radius: 8px;
704
+ padding: 0;
705
+ margin: -12px -12px 0 0;
706
+ }
707
+
708
+ .lb-mix-match__modal-close:focus-visible {
709
+ outline: 2px solid var(--lb-primary-color);
710
+ outline-offset: 0;
711
+ box-shadow: none;
712
+ }
713
+
714
+ /* === Filter pills ===================================================== */
715
+ .lb-mix-match__filters {
716
+ display: flex;
717
+ flex-wrap: wrap;
718
+ gap: 8px;
719
+ padding: 16px 20px 16px;
720
+ flex-shrink: 0;
721
+ }
722
+
723
+ .lb-mix-match__filter {
724
+ padding: 6px 14px;
725
+ border: var(--lb-picker-border-width, 0px) solid var(--lb-picker-border-color, var(--lb-border-color));
726
+ border-radius: 999px;
727
+ background: transparent;
728
+ color: var(--lb-picker-text, var(--lb-text, var(--lb-primary-color)));
729
+ font-family: inherit;
730
+ font-size: 13px;
731
+ font-weight: 500;
732
+ line-height: 1;
733
+ cursor: pointer;
734
+ white-space: nowrap;
735
+ }
736
+
737
+ .lb-mix-match__filter:focus-visible {
738
+ outline: 2px solid var(--lb-primary-color);
739
+ outline-offset: 2px;
740
+ box-shadow: none;
741
+ }
742
+
743
+ .lb-mix-match__filter--active {
744
+ background: #1a1a1a;
745
+ border-color: #1a1a1a;
746
+ color: #ffffff;
747
+ }
748
+
749
+ /* === Modal search ===================================================== */
750
+ .lb-mix-match__modal-search {
751
+ padding: 16px 20px 0;
752
+ position: relative;
753
+ flex-shrink: 0;
754
+ }
755
+
756
+ .lb-mix-match__modal-search::before {
757
+ content: "";
758
+ position: absolute;
759
+ left: 36px;
760
+ top: 16px;
761
+ bottom: 0;
762
+ width: 18px;
763
+ background-color: color-mix(in srgb, var(--lb-picker-text, var(--lb-text, var(--lb-primary-color))) 60%, transparent);
764
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0'/%3E%3Cpath d='M21 21l-6 -6'/%3E%3C/svg%3E") center / 18px no-repeat;
765
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0'/%3E%3Cpath d='M21 21l-6 -6'/%3E%3C/svg%3E") center / 18px no-repeat;
766
+ pointer-events: none;
767
+ }
768
+
769
+ .lb-mix-match__modal-search-input {
770
+ width: 100%;
771
+ padding: 12px 40px 12px 44px;
772
+ border: var(--lb-picker-border-width, 0px) solid var(--lb-picker-border-color, var(--lb-border-color));
773
+ border-radius: var(--lb-picker-radius-sm);
774
+ font-size: 16px;
775
+ line-height: 20px;
776
+ color: var(--lb-picker-text, var(--lb-text, var(--lb-primary-color)));
777
+ background-color: var(--lb-picker-bg, var(--lb-bg, var(--lb-background)));
778
+ box-sizing: border-box;
779
+ -webkit-appearance: none;
780
+ appearance: none;
781
+ font-family: inherit;
782
+ }
783
+
784
+ .lb-mix-match__modal-search-input::placeholder {
785
+ color: color-mix(in srgb, var(--lb-picker-text, var(--lb-text, var(--lb-primary-color))) 50%, transparent);
786
+ }
787
+
788
+ .lb-mix-match__modal-search-input:focus-visible {
789
+ outline: 2px solid var(--lb-primary-color);
790
+ outline-offset: 2px;
791
+ box-shadow: none;
792
+ }
793
+
794
+ .lb-mix-match__modal-search-clear {
795
+ position: absolute;
796
+ right: 32px;
797
+ top: 16px;
798
+ bottom: 0;
799
+ min-width: 32px;
800
+ min-height: 32px;
801
+ display: flex;
802
+ align-items: center;
803
+ justify-content: center;
804
+ background: none;
805
+ border: none;
806
+ cursor: pointer;
807
+ color: color-mix(in srgb, var(--lb-picker-text, var(--lb-text, var(--lb-primary-color))) 60%, transparent);
808
+ padding: 0;
809
+ }
810
+
811
+ .lb-mix-match__modal--filters-hidden .lb-mix-match__modal-search {
812
+ padding-bottom: 16px;
813
+ }
814
+
815
+ .lb-mix-match__modal--filters-hidden .lb-mix-match__modal-search::before,
816
+ .lb-mix-match__modal--filters-hidden .lb-mix-match__modal-search-clear {
817
+ bottom: 16px;
818
+ }
819
+
820
+ /* === Modal product grid =============================================== */
821
+ .lb-mix-match__modal-list {
822
+ overflow-y: auto;
823
+ flex: 1;
824
+ padding: 8px 20px 20px;
825
+ -webkit-overflow-scrolling: touch;
826
+ display: grid;
827
+ grid-template-columns: repeat(2, minmax(0, 1fr));
828
+ column-gap: 24px;
829
+ row-gap: 28px;
830
+ align-content: start;
831
+ }
832
+
833
+ .lb-mix-match__modal-header + .lb-mix-match__modal-list {
834
+ padding-top: 16px;
835
+ }
836
+
837
+ .lb-mix-match__modal-product {
838
+ display: flex;
839
+ flex-direction: column;
840
+ padding: 8px;
841
+ border: var(--lb-picker-border-width, 0px) solid var(--lb-picker-border-color, var(--lb-border-color));
842
+ border-radius: var(--lb-picker-radius, 16px);
843
+ box-sizing: border-box;
844
+ transition: border-color 0.15s ease, background 0.15s ease;
845
+ }
846
+
847
+ .lb-mix-match__modal-product-thumb {
848
+ width: 100%;
849
+ aspect-ratio: var(--lb-picker-thumbnail-aspect-ratio, 1 / 1);
850
+ /* Thumbnails carry only the picker corner radius — no border. */
851
+ border-radius: var(--lb-picker-radius-sm);
852
+ box-sizing: border-box;
853
+ overflow: hidden;
854
+ background: var(--lb-thumbnail-bg, transparent);
855
+ margin-bottom: 10px;
856
+ }
857
+
858
+ .lb-mix-match__modal-product-thumb img {
859
+ width: 100%;
860
+ height: var(--lb-picker-thumbnail-img-height, 100%);
861
+ object-fit: var(--lb-picker-thumbnail-img-fit, cover);
862
+ border-radius: var(--lb-picker-radius-sm);
863
+ display: block;
864
+ }
865
+
866
+ .lb-mix-match__modal-product-info {
867
+ flex: 1;
868
+ min-width: 0;
869
+ }
870
+
871
+ .lb-mix-match__modal-product-title {
872
+ font-size: 16px;
873
+ font-weight: 600;
874
+ line-height: 20px;
875
+ color: inherit;
876
+ margin: 0;
877
+ }
878
+
879
+ .lb-mix-match__modal-product-price {
880
+ display: flex;
881
+ align-items: baseline;
882
+ gap: 6px;
883
+ font-size: 14px;
884
+ line-height: 20px;
885
+ color: inherit;
886
+ margin: 2px 0 0;
887
+ }
888
+
889
+ .lb-mix-match__modal-product-compare {
890
+ display: var(--lb-product-compare-display, inline);
891
+ font-size: 13px;
892
+ font-weight: 400;
893
+ color: color-mix(in srgb, var(--lb-picker-text, var(--lb-text, var(--lb-primary-color))) 55%, transparent);
894
+ text-decoration: line-through;
895
+ }
896
+
897
+ .lb-mix-match__modal-product-unit-price {
898
+ font-size: 12px;
899
+ line-height: 16px;
900
+ color: color-mix(in srgb, var(--lb-picker-text, var(--lb-text, var(--lb-primary-color))) 60%, transparent);
901
+ margin: 2px 0 0;
902
+ }
903
+
904
+ .lb-mix-match__variant-select-dropdown {
905
+ font-size: 12px;
906
+ }
907
+
908
+ /* Per-pick quantity stepper. */
909
+ .lb-mix-match__qty-stepper-group {
910
+ flex-direction: row;
911
+ width: fit-content;
912
+ }
913
+
914
+ .lb-mix-match__qty-stepper {
915
+ display: inline-flex;
916
+ align-items: center;
917
+ justify-content: space-between;
918
+ width: 100%;
919
+ flex-shrink: 0;
920
+ border: var(--lb-picker-border-width, 0px) solid var(--lb-picker-border-color, var(--lb-border-color));
921
+ border-radius: var(--lb-picker-radius-sm);
922
+ background-color: var(--lb-picker-bg, var(--lb-bg, var(--lb-background)));
923
+ overflow: hidden;
924
+ box-sizing: border-box;
925
+ min-height: 32px;
926
+ max-height: 32px;
927
+ }
928
+
929
+ .lb-mix-match__qty-stepper-button {
930
+ appearance: none;
931
+ -webkit-appearance: none;
932
+ background: transparent;
933
+ border: none;
934
+ margin: 0;
935
+ padding: 0 4px;
936
+ width: 24px;
937
+ height: 24px;
938
+ box-sizing: border-box;
939
+ font-family: inherit;
940
+ font-size: 16px;
941
+ font-weight: 500;
942
+ line-height: 1;
943
+ color: var(--lb-picker-text, var(--lb-text, var(--lb-primary-color)));
944
+ cursor: pointer;
945
+ display: inline-flex;
946
+ align-items: center;
947
+ justify-content: center;
948
+ }
949
+
950
+ .lb-mix-match__qty-stepper-button:focus-visible {
951
+ outline: 2px solid var(--lb-primary-color);
952
+ outline-offset: -2px;
953
+ box-shadow: none;
954
+ }
955
+
956
+ .lb-mix-match__qty-stepper-button:disabled {
957
+ opacity: 0.4;
958
+ cursor: not-allowed;
959
+ }
960
+
961
+ .lb-mix-match__qty-stepper-value {
962
+ display: inline-flex;
963
+ align-items: center;
964
+ justify-content: center;
965
+ width: 24px;
966
+ height: 24px;
967
+ font-family: inherit;
968
+ font-size: 13px;
969
+ font-weight: 500;
970
+ color: var(--lb-picker-text, var(--lb-text, var(--lb-primary-color)));
971
+ box-sizing: border-box;
972
+ font-variant-numeric: tabular-nums;
973
+ }
974
+
975
+ .lb-mix-match__modal-product-actions {
976
+ display: flex;
977
+ flex-wrap: wrap;
978
+ align-items: stretch;
979
+ row-gap: 8px;
980
+ column-gap: 8px;
981
+ margin-top: 12px;
982
+ }
983
+
984
+ .lb-mix-match__modal-add {
985
+ flex: 1;
986
+ padding: 8px 16px;
987
+ background: var(--lb-picker-add-bg, var(--lb-primary-color));
988
+ color: var(--lb-picker-add-label, #fff);
989
+ border: var(--lb-picker-add-border-width, 0px) solid var(--lb-picker-add-border-color, var(--lb-primary-color));
990
+ border-radius: var(--lb-picker-radius-sm);
991
+ box-sizing: border-box;
992
+ font-family: inherit;
993
+ font-size: 12px;
994
+ font-weight: 600;
995
+ cursor: pointer;
996
+ min-height: 32px;
997
+ max-height: 32px;
998
+ }
999
+
1000
+ .lb-mix-match__modal-add:hover:not(:disabled) {
1001
+ opacity: 0.9;
1002
+ }
1003
+
1004
+ .lb-mix-match__modal-add:focus-visible {
1005
+ outline: 2px solid var(--lb-primary-color);
1006
+ outline-offset: 2px;
1007
+ box-shadow: none;
1008
+ }
1009
+
1010
+ .lb-mix-match__modal-add:disabled {
1011
+ background: color-mix(in srgb, var(--lb-picker-add-bg, var(--lb-primary-color)) 35%, var(--lb-picker-bg, var(--lb-bg, var(--lb-background))));
1012
+ color: color-mix(in srgb, var(--lb-picker-add-label, #fff) 85%, transparent);
1013
+ cursor: not-allowed;
1014
+ }
1015
+
1016
+ /* "Added" toggle — quiet neutral outlined treatment with a leading check, so an
1017
+ in-bundle product reads as selected (click removes it). Lime stays on the
1018
+ un-added "Add" button. */
1019
+ .lb-mix-match__modal-add--added {
1020
+ background: transparent;
1021
+ color: var(--lb-picker-text, var(--lb-text, var(--lb-primary-color)));
1022
+ border-color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 25%, transparent);
1023
+ display: inline-flex;
1024
+ align-items: center;
1025
+ justify-content: center;
1026
+ gap: 5px;
1027
+ }
1028
+
1029
+ .lb-mix-match__modal-add--added:hover {
1030
+ opacity: 1;
1031
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 6%, transparent);
1032
+ }
1033
+
1034
+ .lb-mix-match__modal-add--added svg {
1035
+ width: 14px;
1036
+ height: 14px;
1037
+ flex-shrink: 0;
1038
+ }
1039
+
1040
+ /* In-bundle card — a quiet neutral tint + stronger border marks the selected
1041
+ products without spending lime on a non-button element. */
1042
+ .lb-mix-match__modal-product--in-bundle {
1043
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 4%, transparent);
1044
+ border-color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 22%, transparent);
1045
+ }
1046
+
1047
+ .lb-mix-match__modal-product--sold-out {
1048
+ opacity: 0.5;
1049
+ }
1050
+
1051
+ .lb-mix-match__modal-sold-out-label {
1052
+ font-size: 12px;
1053
+ color: inherit;
1054
+ font-weight: 500;
1055
+ white-space: nowrap;
1056
+ display: block;
1057
+ margin-top: 8px;
1058
+ }
1059
+
1060
+ /* "Needs N spots" hint — exact-size enforcement: the product's minimum
1061
+ pick exceeds the bundle's remaining open spots, so its Add and stepper
1062
+ are disabled. Same quiet label treatment as the sold-out state. */
1063
+ .lb-mix-match__modal-needs-spots {
1064
+ display: block;
1065
+ margin-top: 8px;
1066
+ font-size: 12px;
1067
+ color: inherit;
1068
+ font-weight: 500;
1069
+ white-space: nowrap;
1070
+ opacity: 0.7;
1071
+ }
1072
+
1073
+ /* === Modal empty state ================================================ */
1074
+ .lb-mix-match__modal-empty {
1075
+ padding: 32px 20px;
1076
+ text-align: center;
1077
+ }
1078
+
1079
+ .lb-mix-match__modal-empty p {
1080
+ margin: 0;
1081
+ font-size: 16px;
1082
+ color: var(--lb-picker-text, var(--lb-text, var(--lb-primary-color)));
1083
+ }
1084
+
1085
+ /* === Modal footer ===================================================== */
1086
+ .lb-mix-match__modal-footer {
1087
+ display: flex;
1088
+ align-items: center;
1089
+ justify-content: space-between;
1090
+ gap: 12px;
1091
+ padding: 16px 20px;
1092
+ border-top: 1px solid color-mix(in srgb, var(--lb-picker-text, var(--lb-text, var(--lb-primary-color))) 15%, transparent);
1093
+ flex-shrink: 0;
1094
+ }
1095
+
1096
+ .lb-mix-match__modal-footer-count {
1097
+ font-size: 13px;
1098
+ font-weight: 500;
1099
+ color: color-mix(in srgb, var(--lb-picker-text, var(--lb-text, var(--lb-primary-color))) 70%, transparent);
1100
+ }
1101
+
1102
+ /* The flex centring + min-height pin the footer-button silhouette: the wizard
1103
+ Back copies these metrics so the pair stays equal-height whatever border
1104
+ widths the merchant's picker tokens set. */
1105
+ .lb-mix-match__modal-done {
1106
+ display: inline-flex;
1107
+ align-items: center;
1108
+ justify-content: center;
1109
+ min-height: 40px;
1110
+ padding: 10px 24px;
1111
+ background: var(--lb-picker-add-bg, var(--lb-primary-color));
1112
+ color: var(--lb-picker-add-label, #fff);
1113
+ border: var(--lb-picker-add-border-width, 0px) solid var(--lb-picker-add-border-color, var(--lb-primary-color));
1114
+ border-radius: var(--lb-picker-radius-sm);
1115
+ box-sizing: border-box;
1116
+ font-family: inherit;
1117
+ font-size: 14px;
1118
+ font-weight: 600;
1119
+ cursor: pointer;
1120
+ }
1121
+
1122
+ .lb-mix-match__modal-done:hover:not(:disabled) {
1123
+ opacity: 0.9;
1124
+ }
1125
+
1126
+ /* Disabled Next/Done (wizard step minimum not met) — same washed-out
1127
+ treatment as a disabled row Add, no hover reaction; the solid button
1128
+ returns the moment the requirement is met. */
1129
+ .lb-mix-match__modal-done:disabled {
1130
+ background: color-mix(in srgb, var(--lb-picker-add-bg, var(--lb-primary-color)) 35%, var(--lb-picker-bg, var(--lb-bg, var(--lb-background))));
1131
+ color: color-mix(in srgb, var(--lb-picker-add-label, #fff) 85%, transparent);
1132
+ cursor: not-allowed;
1133
+ }
1134
+
1135
+ .lb-mix-match__modal-done:focus-visible {
1136
+ outline: 2px solid var(--lb-primary-color);
1137
+ outline-offset: 2px;
1138
+ box-shadow: none;
1139
+ }
1140
+
1141
+ .lb-hidden {
1142
+ display: none !important;
1143
+ }
1144
+
1145
+ /* Desktop: stepper group + Add share the action row equally. */
1146
+ @media (min-width: 768px) {
1147
+ .lb-mix-match__qty-stepper-group {
1148
+ flex: 1;
1149
+ }
1150
+ }
1151
+
1152
+ /* Mobile full-screen bottom-sheet modal. */
1153
+ @media (max-width: 767px) {
1154
+ .lb-mix-match__modal-overlay {
1155
+ align-items: flex-end;
1156
+ }
1157
+
1158
+ .lb-mix-match__modal {
1159
+ max-width: 100%;
1160
+ max-height: 90vh;
1161
+ border-radius: 16px 16px 0 0;
1162
+ transform: translateY(100%);
1163
+ }
1164
+
1165
+ .lb-mix-match__modal-overlay--open .lb-mix-match__modal {
1166
+ transform: translateY(0);
1167
+ }
1168
+
1169
+ .lb-mix-match__qty-stepper {
1170
+ min-height: 44px;
1171
+ max-height: 44px;
1172
+ }
1173
+
1174
+ .lb-mix-match__qty-stepper-button {
1175
+ width: 44px;
1176
+ height: 44px;
1177
+ }
1178
+
1179
+ .lb-mix-match__modal-add {
1180
+ min-height: 44px;
1181
+ max-height: 44px;
1182
+ }
1183
+
1184
+ .lb-mix-match__qty-stepper-group {
1185
+ width: 100%;
1186
+ }
1187
+ }
1188
+
1189
+ @media (max-width: 430px) {
1190
+ .lb-mix-match__modal-list {
1191
+ grid-template-columns: 1fr;
1192
+ }
1193
+ }
1194
+
1195
+ @media (prefers-reduced-motion: reduce) {
1196
+ .lb-mix-match__modal-overlay,
1197
+ .lb-mix-match__modal,
1198
+ .lb-mix-match__progress-segment {
1199
+ transition: none;
1200
+ }
1201
+ }
1202
+
1203
+ /* ───────────────────────────────────────────────────────────────────
1204
+ * Volume bundle — quantity selector.
1205
+ * ─────────────────────────────────────────────────────────────────── */
1206
+
1207
+ .lb-bundle__quantity-selector {
1208
+ margin: var(--lb-spacing-lg) 0;
1209
+ }
1210
+
1211
+ .lb-bundle__quantity-selector label {
1212
+ display: block;
1213
+ margin-bottom: var(--lb-spacing-sm);
1214
+ font-weight: 500;
1215
+ }
1216
+
1217
+ .lb-bundle__quantity-control {
1218
+ display: inline-flex;
1219
+ align-items: center;
1220
+ gap: 0;
1221
+ border: 1px solid var(--lb-border, var(--lb-border-color));
1222
+ border-radius: var(--lb-border-radius);
1223
+ }
1224
+
1225
+ .lb-bundle__quantity-control button {
1226
+ width: 32px;
1227
+ height: 32px;
1228
+ border: none;
1229
+ background: transparent;
1230
+ cursor: pointer;
1231
+ font-size: 1.1em;
1232
+ display: flex;
1233
+ align-items: center;
1234
+ justify-content: center;
1235
+ color: inherit;
1236
+ }
1237
+
1238
+ .lb-bundle__quantity-control span,
1239
+ .lb-bundle__quantity-input {
1240
+ width: 40px;
1241
+ text-align: center;
1242
+ border: none;
1243
+ border-left: 1px solid var(--lb-border, var(--lb-border-color));
1244
+ border-right: 1px solid var(--lb-border, var(--lb-border-color));
1245
+ height: 32px;
1246
+ line-height: 32px;
1247
+ font-size: var(--lb-font-size);
1248
+ background: transparent;
1249
+ color: inherit;
1250
+ }
1251
+
1252
+ .lb-bundle__quantity-input {
1253
+ -moz-appearance: textfield;
1254
+ }
1255
+ .lb-bundle__quantity-input::-webkit-outer-spin-button,
1256
+ .lb-bundle__quantity-input::-webkit-inner-spin-button {
1257
+ -webkit-appearance: none;
1258
+ }
1259
+
1260
+ /* ───────────────────────────────────────────────────────────────────
1261
+ * Volume tier cards — mirror the fixed / mix & match bundle card so volume
1262
+ * reads as the same app: a bordered card with matching geometry, a price row,
1263
+ * and a right-slot savings chip. The radio sits where those cards put the
1264
+ * thumbnail, since a volume tier is the same product at a different quantity
1265
+ * (no thumbnail). Ported from extensions/bundle-theme/assets/bundle-volume.css.
1266
+ * ─────────────────────────────────────────────────────────────────── */
1267
+
1268
+ .lb-bundle__tiers {
1269
+ display: flex;
1270
+ flex-direction: column;
1271
+ gap: 16px;
1272
+ margin: var(--lb-spacing-lg) 0 0;
1273
+ max-height: var(--lb-list-max-height);
1274
+ overflow-y: auto;
1275
+ /* Scrollbar hidden + edge fade — see .lb-edge-fade above. */
1276
+ }
1277
+
1278
+ /* Match the theme: when the first tier is the popular one, reserve room above
1279
+ for the floating "Most popular" badge so it has the same breathing space. */
1280
+ .lb-bundle__tiers:has(> .lb-bundle__tier:first-child .lb-bundle__tier-badge) {
1281
+ padding-top: 12px;
1282
+ }
1283
+
1284
+ .lb-bundle__tier {
1285
+ position: relative;
1286
+ display: flex;
1287
+ align-items: center;
1288
+ gap: 12px;
1289
+ border: var(--lb-border-width, 1px) solid var(--lb-border, var(--lb-border-color));
1290
+ border-radius: var(--lb-radius, var(--lb-border-radius));
1291
+ padding: 12px;
1292
+ box-sizing: border-box;
1293
+ }
1294
+
1295
+ /* Selected (active) tier — the same quiet neutral highlight as the mix & match
1296
+ in-bundle / Added card (soft tint + stronger border), plus the filled radio,
1297
+ so "selected" looks the same everywhere in the app. The old
1298
+ --lb-tier-selected-* tokens are gone. */
1299
+ .lb-bundle__tier--active {
1300
+ border-color: var(--lb-text, var(--lb-primary-color));
1301
+ border-width: 2px;
1302
+ }
1303
+
1304
+ /* Tier the selected variant can't cover — same greyed treatment as an
1305
+ out-of-stock product row, and not selectable. */
1306
+ .lb-bundle__tier--oos {
1307
+ opacity: 0.5;
1308
+ cursor: not-allowed;
1309
+ }
1310
+
1311
+ .lb-bundle__tier-radio {
1312
+ width: 20px;
1313
+ height: 20px;
1314
+ min-width: 20px;
1315
+ flex: none;
1316
+ box-sizing: border-box;
1317
+ border: 2px solid color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 45%, transparent);
1318
+ border-radius: 50%;
1319
+ display: flex;
1320
+ align-items: center;
1321
+ justify-content: center;
1322
+ }
1323
+
1324
+ /* Ring and dot follow the SELECTED tier (aria-checked), matching the
1325
+ single-selection radio the theme renders — `--active` marks every tier
1326
+ the current quantity reaches and keeps the row highlight only. */
1327
+ .lb-bundle__tier[aria-checked="true"] .lb-bundle__tier-radio {
1328
+ border-color: var(--lb-text, var(--lb-primary-color));
1329
+ }
1330
+
1331
+ .lb-bundle__tier-radio-dot {
1332
+ width: 10px;
1333
+ height: 10px;
1334
+ border-radius: 50%;
1335
+ background: transparent;
1336
+ }
1337
+
1338
+ .lb-bundle__tier[aria-checked="true"] .lb-bundle__tier-radio-dot {
1339
+ background: var(--lb-text, var(--lb-primary-color));
1340
+ }
1341
+
1342
+ /* Info column: the quantity over the price row. */
1343
+ .lb-bundle__tier-info {
1344
+ flex: 1;
1345
+ min-width: 0;
1346
+ display: flex;
1347
+ flex-direction: column;
1348
+ }
1349
+
1350
+ .lb-bundle__tier-label {
1351
+ font-size: 16px;
1352
+ font-weight: 600;
1353
+ line-height: 20px;
1354
+ color: var(--lb-text, var(--lb-primary-color));
1355
+ }
1356
+
1357
+ /* Right column — stacks the "Most popular" badge (when present) over the
1358
+ savings chip, so the markers group on the right and the quantity row stays
1359
+ clean. */
1360
+ .lb-bundle__tier-right {
1361
+ flex: none;
1362
+ display: flex;
1363
+ flex-direction: column;
1364
+ align-items: flex-end;
1365
+ gap: 6px;
1366
+ }
1367
+
1368
+ /* "Most popular" pill — a floating crown straddling the card's top-right edge.
1369
+ The 16px gap on .lb-bundle__tiers leaves room for it above each card. */
1370
+ .lb-bundle__tier-badge {
1371
+ position: absolute;
1372
+ top: -12px;
1373
+ right: 12px;
1374
+ z-index: 1;
1375
+ font-size: 12px;
1376
+ font-weight: 500;
1377
+ line-height: 1;
1378
+ color: var(--lb-popular-badge-text);
1379
+ background: var(--lb-popular-badge-bg);
1380
+ border: var(--lb-popular-badge-border-width) solid var(--lb-popular-badge-border-color);
1381
+ /* The small floating badge reads better one step softer than --lb-radius-sm —
1382
+ ~3px at the "subtle" preset — while still scaling with the radius setting. */
1383
+ border-radius: max(0px, calc(var(--lb-radius, var(--lb-border-radius)) - 3px));
1384
+ padding: 4px 9px;
1385
+ }
1386
+
1387
+ /* Price row — the same struck → per-unit → "each" treatment the product cards
1388
+ use (compare-at muted and struck, per-unit price, tabular figures). */
1389
+ .lb-bundle__tier-price {
1390
+ display: flex;
1391
+ align-items: baseline;
1392
+ gap: 6px;
1393
+ flex-wrap: wrap;
1394
+ margin-top: 3px;
1395
+ font-size: 14px;
1396
+ color: var(--lb-text, var(--lb-primary-color));
1397
+ font-variant-numeric: tabular-nums;
1398
+ }
1399
+
1400
+ .lb-bundle__tier-compare {
1401
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 55%, transparent);
1402
+ text-decoration: line-through;
1403
+ }
1404
+
1405
+ .lb-bundle__tier-unit {
1406
+ font-size: 12px;
1407
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
1408
+ }
1409
+
1410
+ /* Savings chip — the same soft neutral chip as the fixed card's quantity chip,
1411
+ here carrying the per-tier discount so "buy more, save more" is scannable. */
1412
+ .lb-bundle__tier-savings {
1413
+ flex-shrink: 0;
1414
+ display: inline-flex;
1415
+ align-items: center;
1416
+ height: 28px;
1417
+ padding: 0 10px;
1418
+ border-radius: max(0px, calc(var(--lb-radius, var(--lb-border-radius)) - 2px));
1419
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 7%, transparent);
1420
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 80%, transparent);
1421
+ font-size: 13px;
1422
+ font-weight: 500;
1423
+ line-height: 1;
1424
+ white-space: nowrap;
1425
+ font-variant-numeric: tabular-nums;
1426
+ }
1427
+
1428
+ /* ───────────────────────────────────────────────────────────────────
1429
+ * Receipt summary — a two-column row: the label (+ savings line) on the
1430
+ * left, the price stack on the right, vertically centred. The sale price is
1431
+ * the widget's #1 element (largest, heaviest), so the eye lands on what the
1432
+ * bundle costs. Inherits the widget's global text color.
1433
+ * ─────────────────────────────────────────────────────────────────── */
1434
+
1435
+ .lb-bundle-summary {
1436
+ display: flex;
1437
+ align-items: center;
1438
+ justify-content: space-between;
1439
+ gap: 12px;
1440
+ margin-bottom: 16px;
1441
+ }
1442
+
1443
+ /* Left column — the label over the savings line. Paired with the price stack
1444
+ on the right and vertically centred against it, so the summary stays
1445
+ balanced whether or not a savings line / compare price is present (e.g. an
1446
+ undiscounted bundle reads as a clean "label … total" on one centred line). */
1447
+ .lb-bundle-summary__text {
1448
+ display: flex;
1449
+ flex-direction: column;
1450
+ gap: 2px;
1451
+ min-width: 0;
1452
+ }
1453
+
1454
+ .lb-bundle-summary__label {
1455
+ font-size: 15px;
1456
+ font-weight: 400;
1457
+ line-height: 1.4;
1458
+ text-transform: uppercase;
1459
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 70%, transparent);
1460
+ }
1461
+
1462
+ /* Savings line — the one savings signal, quiet text under the label. No pill,
1463
+ no lime: lime is reserved for the CTA. */
1464
+ .lb-bundle-savings-line {
1465
+ font-size: 13px;
1466
+ font-weight: 400;
1467
+ line-height: 1.4;
1468
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 60%, transparent);
1469
+ margin: 0;
1470
+ }
1471
+
1472
+ .lb-bundle-savings-line [data-savings-amount] {
1473
+ color: var(--lb-text, var(--lb-primary-color));
1474
+ font-variant-numeric: tabular-nums;
1475
+ }
1476
+
1477
+ /* Right column — the struck original over the bundle total ("was / now"),
1478
+ vertically centred against the label/savings column on the left. */
1479
+ .lb-bundle-summary__prices {
1480
+ display: flex;
1481
+ flex-direction: column;
1482
+ align-items: flex-end;
1483
+ gap: 1px;
1484
+ flex-shrink: 0;
1485
+ text-align: right;
1486
+ }
1487
+
1488
+ .lb-bundle-sale-price {
1489
+ font-size: 24px;
1490
+ font-weight: 600;
1491
+ line-height: 1.1;
1492
+ letter-spacing: -0.02em;
1493
+ color: var(--lb-text, var(--lb-primary-color));
1494
+ font-variant-numeric: tabular-nums;
1495
+ }
1496
+
1497
+ .lb-bundle-compare-price {
1498
+ font-size: 14px;
1499
+ font-weight: 400;
1500
+ line-height: 1.3;
1501
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 55%, transparent);
1502
+ text-decoration: line-through;
1503
+ font-variant-numeric: tabular-nums;
1504
+ }
1505
+
1506
+ /* ───────────────────────────────────────────────────────────────────
1507
+ * CTA button.
1508
+ * ─────────────────────────────────────────────────────────────────── */
1509
+
1510
+ .lb-bundle__cta {
1511
+ width: 100%;
1512
+ padding: 12px 16px;
1513
+ border: var(--lb-cta-border-width, 0px) solid var(--lb-cta-border-color, var(--lb-primary-color));
1514
+ border-radius: var(--lb-radius, var(--lb-border-radius));
1515
+ background: var(--lb-primary-color, var(--lb-button-bg));
1516
+ color: var(--lb-btn-text, var(--lb-button-text));
1517
+ font-size: 16px;
1518
+ font-weight: 600;
1519
+ line-height: 20px;
1520
+ cursor: pointer;
1521
+ text-align: center;
1522
+ font-family: inherit;
1523
+ transition: opacity 0.15s;
1524
+ }
1525
+
1526
+ .lb-bundle__cta:hover:not(:disabled) {
1527
+ opacity: 0.9;
1528
+ }
1529
+
1530
+ .lb-bundle__cta:disabled {
1531
+ background: color-mix(in srgb, var(--lb-primary-color) 35%, var(--lb-bg, var(--lb-background)));
1532
+ color: color-mix(in srgb, var(--lb-btn-text, var(--lb-button-text)) 85%, transparent);
1533
+ cursor: not-allowed;
1534
+ }
1535
+
1536
+ .lb-bundle__error {
1537
+ color: var(--lb-low-stock-text, var(--lb-error-color));
1538
+ margin: var(--lb-spacing-sm) 0;
1539
+ font-size: 0.9em;
1540
+ }
1541
+
1542
+ /* Skeleton loading */
1543
+ .lb-skeleton {
1544
+ background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
1545
+ background-size: 200% 100%;
1546
+ animation: lb-shimmer 1.5s infinite;
1547
+ border-radius: var(--lb-border-radius);
1548
+ }
1549
+
1550
+ .lb-skeleton--title {
1551
+ height: 24px;
1552
+ width: 60%;
1553
+ margin-bottom: var(--lb-spacing-md);
1554
+ }
1555
+
1556
+ .lb-skeleton--products {
1557
+ height: 200px;
1558
+ }
1559
+
1560
+ .lb-skeleton--tiers {
1561
+ height: 120px;
1562
+ }
1563
+
1564
+ @keyframes lb-shimmer {
1565
+ 0% { background-position: 200% 0; }
1566
+ 100% { background-position: -200% 0; }
1567
+ }
1568
+
1569
+ /* ───────────────────────────────────────────────────────────────────
1570
+ * Variant-picker dropdown — accessible custom combobox.
1571
+ * Reuses the widget vars (--lb-bg, --lb-text, the global --lb-border /
1572
+ * --lb-border-width, and the derived --lb-radius-sm) emitted by
1573
+ * useWidgetConfigVars on the bundle component root, so values match the
1574
+ * merchant's WidgetConfig without separate plumbing.
1575
+ * ─────────────────────────────────────────────────────────────────── */
1576
+
1577
+ .lb-dropdown {
1578
+ position: relative;
1579
+ display: inline-block;
1580
+ width: 100%;
1581
+ max-width: 100%;
1582
+ font-family: inherit;
1583
+ }
1584
+
1585
+ .lb-dropdown-trigger {
1586
+ display: inline-flex;
1587
+ align-items: center;
1588
+ justify-content: space-between;
1589
+ gap: 8px;
1590
+ width: 100%;
1591
+ border: var(--lb-border-width, 1px) solid var(--lb-border, var(--lb-border-color));
1592
+ border-radius: var(--lb-radius-sm);
1593
+ padding: 8px 12px;
1594
+ font-size: 12px;
1595
+ line-height: 16px;
1596
+ color: var(--lb-text, var(--lb-primary-color));
1597
+ background: var(--lb-bg, var(--lb-background));
1598
+ font-family: inherit;
1599
+ cursor: pointer;
1600
+ appearance: none;
1601
+ -webkit-appearance: none;
1602
+ text-align: start;
1603
+ transition: border-color 120ms ease;
1604
+ }
1605
+
1606
+ .lb-dropdown-trigger:focus-visible {
1607
+ outline: 2px solid var(--lb-primary-color);
1608
+ outline-offset: 2px;
1609
+ }
1610
+
1611
+ .lb-dropdown-trigger[aria-expanded="true"] {
1612
+ border-color: var(--lb-text, var(--lb-primary-color));
1613
+ }
1614
+
1615
+ .lb-dropdown-trigger-value {
1616
+ flex: 1 1 auto;
1617
+ white-space: nowrap;
1618
+ overflow: hidden;
1619
+ text-overflow: ellipsis;
1620
+ text-align: start;
1621
+ }
1622
+
1623
+ .lb-dropdown-chevron {
1624
+ flex: 0 0 auto;
1625
+ width: 12px;
1626
+ height: 12px;
1627
+ background: var(--lb-variant-chevron) center / contain no-repeat;
1628
+ transition: transform 120ms ease;
1629
+ }
1630
+
1631
+ .lb-dropdown-trigger[aria-expanded="true"] .lb-dropdown-chevron {
1632
+ transform: rotate(180deg);
1633
+ }
1634
+
1635
+ .lb-dropdown-listbox {
1636
+ position: absolute;
1637
+ left: 0;
1638
+ /* Default to below-trigger placement so the panel doesn't overlap the
1639
+ trigger if data-placement is missing for any reason. The explicit
1640
+ [data-placement="down"|"up"] rules below override this. */
1641
+ top: calc(100% + 4px);
1642
+ width: 100%;
1643
+ z-index: 9999;
1644
+ margin: 0;
1645
+ padding: 4px 0;
1646
+ list-style: none;
1647
+ background: var(--lb-bg, var(--lb-background));
1648
+ color: var(--lb-text, var(--lb-primary-color));
1649
+ border: var(--lb-border-width, 1px) solid var(--lb-border, var(--lb-border-color));
1650
+ border-radius: var(--lb-radius-sm);
1651
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
1652
+ overflow-y: auto;
1653
+ overflow-x: hidden;
1654
+ /* Quiet overlay scrollbar — transparent track, soft rounded thumb. The
1655
+ popover has a shadow + border, so it keeps a thumb rather than the inset
1656
+ lists' edge fade (a mask would clip the shadow). */
1657
+ scrollbar-width: thin;
1658
+ scrollbar-color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 18%, transparent) transparent;
1659
+ animation: lb-dropdown-in-down 120ms ease-out;
1660
+ transform-origin: top center;
1661
+ }
1662
+
1663
+ .lb-dropdown-listbox[data-placement="down"] {
1664
+ top: calc(100% + 4px);
1665
+ }
1666
+
1667
+ .lb-dropdown-listbox[data-placement="up"] {
1668
+ top: auto;
1669
+ bottom: calc(100% + 4px);
1670
+ animation-name: lb-dropdown-in-up;
1671
+ transform-origin: bottom center;
1672
+ }
1673
+
1674
+ /* Quiet overlay scrollbar — Webkit/Blink: transparent track, soft rounded
1675
+ thumb that darkens on hover. */
1676
+ .lb-dropdown-listbox::-webkit-scrollbar {
1677
+ width: 6px;
1678
+ }
1679
+ .lb-dropdown-listbox::-webkit-scrollbar-track {
1680
+ background: transparent;
1681
+ }
1682
+ .lb-dropdown-listbox::-webkit-scrollbar-thumb {
1683
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 16%, transparent);
1684
+ border-radius: 999px;
1685
+ }
1686
+ .lb-dropdown-listbox::-webkit-scrollbar-thumb:hover {
1687
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 30%, transparent);
1688
+ }
1689
+
1690
+ .lb-dropdown-option {
1691
+ padding: 8px 12px;
1692
+ font-size: 12px;
1693
+ line-height: 16px;
1694
+ cursor: pointer;
1695
+ white-space: nowrap;
1696
+ overflow: hidden;
1697
+ text-overflow: ellipsis;
1698
+ color: var(--lb-text, var(--lb-primary-color));
1699
+ }
1700
+
1701
+ .lb-dropdown-option[aria-selected="true"] {
1702
+ font-weight: 600;
1703
+ }
1704
+
1705
+ .lb-dropdown-option.is-active,
1706
+ .lb-dropdown-option:hover:not([aria-disabled="true"]) {
1707
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 2%, transparent);
1708
+ }
1709
+
1710
+ .lb-dropdown-option[aria-disabled="true"] {
1711
+ opacity: 0.4;
1712
+ cursor: not-allowed;
1713
+ }
1714
+
1715
+ @keyframes lb-dropdown-in-down {
1716
+ from { opacity: 0; transform: translateY(-4px) scale(0.98); }
1717
+ to { opacity: 1; transform: translateY(0) scale(1); }
1718
+ }
1719
+
1720
+ @keyframes lb-dropdown-in-up {
1721
+ from { opacity: 0; transform: translateY(4px) scale(0.98); }
1722
+ to { opacity: 1; transform: translateY(0) scale(1); }
1723
+ }
1724
+
1725
+ @media (prefers-reduced-motion: reduce) {
1726
+ .lb-dropdown-listbox { animation: none; }
1727
+ .lb-dropdown-chevron { transition: none; }
1728
+ .lb-dropdown-trigger { transition: none; }
1729
+ }
1730
+
1731
+ @media (forced-colors: active) {
1732
+ .lb-dropdown-trigger {
1733
+ border-color: ButtonBorder;
1734
+ color: ButtonText;
1735
+ background: ButtonFace;
1736
+ }
1737
+ .lb-dropdown-listbox {
1738
+ border-color: ButtonBorder;
1739
+ background: Canvas;
1740
+ color: CanvasText;
1741
+ }
1742
+ .lb-dropdown-option.is-active {
1743
+ background: Highlight;
1744
+ color: HighlightText;
1745
+ }
1746
+ }
1747
+
1748
+ /* === BOGO (Buy X Get Y) ===
1749
+ * Mirrors extensions/bundle-theme/assets/bundle-bogo.css translated to this
1750
+ * stylesheet's var-fallback convention. */
1751
+
1752
+ /* The "+" divider between the buy and get cards — a small accent medallion
1753
+ so the offer's hinge stands out. */
1754
+ .lb-bogo__plus {
1755
+ align-self: center;
1756
+ display: inline-flex;
1757
+ align-items: center;
1758
+ justify-content: center;
1759
+ width: 24px;
1760
+ height: 24px;
1761
+ border-radius: 50%;
1762
+ background: var(--lb-primary-color, var(--lb-button-bg));
1763
+ color: var(--lb-btn-text, var(--lb-button-text));
1764
+ }
1765
+
1766
+ /* Reward badge on the get card — a right-slot chip family member
1767
+ (WIDGET-DESIGN.md): 28px neutral silhouette, small radius, 13/600. */
1768
+ .lb-bogo__badge {
1769
+ display: inline-flex;
1770
+ align-items: center;
1771
+ align-self: flex-start;
1772
+ flex-shrink: 0;
1773
+ height: 28px;
1774
+ padding: 0 8px;
1775
+ border-radius: var(--lb-radius-sm, 4px);
1776
+ background: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 7%, transparent);
1777
+ color: color-mix(in srgb, var(--lb-text, var(--lb-primary-color)) 75%, transparent);
1778
+ font-size: 13px;
1779
+ font-weight: 600;
1780
+ line-height: 1;
1781
+ white-space: nowrap;
1782
+ }
1783
+
1784
+ /* ───────────────────────────────────────────────────────────────────
1785
+ Countdown timer bar — ported from the theme's bundle-base.css. The
1786
+ theme insets with --lb-widget-pad; this stylesheet's root uses a fixed
1787
+ 20px pad, so the full-bleed margins use the same constant.
1788
+ ─────────────────────────────────────────────────────────────────── */
1789
+
1790
+ .lb-bundle-countdown {
1791
+ margin: 0 -20px 20px;
1792
+ padding: 12px 20px;
1793
+ background: var(--lb-countdown-bg);
1794
+ display: flex;
1795
+ align-items: center;
1796
+ justify-content: space-between;
1797
+ }
1798
+
1799
+ .lb-bundle-countdown__label {
1800
+ display: flex;
1801
+ align-items: center;
1802
+ gap: 8px;
1803
+ }
1804
+
1805
+ .lb-bundle-countdown__label span {
1806
+ font-size: 12px;
1807
+ font-weight: 500;
1808
+ line-height: 1;
1809
+ color: var(--lb-countdown-text);
1810
+ }
1811
+
1812
+ .lb-bundle-countdown__timer {
1813
+ font-family: 'SF Mono', 'Roboto Mono', ui-monospace, monospace;
1814
+ font-size: 12px;
1815
+ font-weight: 600;
1816
+ line-height: 1;
1817
+ color: var(--lb-countdown-text);
1818
+ letter-spacing: 0.02em;
1819
+ }
1820
+
1821
+ /* ───────────────────────────────────────────────────────────────────
1822
+ Multi-step groups — ported from the theme's bundle-multi-step.css.
1823
+ The step-group markup shares the theme's class names; without these
1824
+ rules the step name and count chip render as unstyled run-on text.
1825
+ ─────────────────────────────────────────────────────────────────── */
1826
+
1827
+ .lb-multi-step__groups {
1828
+ display: flex;
1829
+ flex-direction: column;
1830
+ gap: 16px;
1831
+ }
1832
+
1833
+ .lb-multi-step__group {
1834
+ display: flex;
1835
+ flex-direction: column;
1836
+ gap: 8px;
1837
+ }
1838
+
1839
+ /* Heading is a quiet, non-interactive row: step name left, live count
1840
+ chip right. */
1841
+ .lb-multi-step__group-heading {
1842
+ display: flex;
1843
+ align-items: center;
1844
+ justify-content: space-between;
1845
+ gap: 12px;
1846
+ color: var(--lb-text);
1847
+ }
1848
+
1849
+ .lb-multi-step__group-name {
1850
+ font-size: 14px;
1851
+ font-weight: 600;
1852
+ line-height: 1.4;
1853
+ }
1854
+
1855
+ /* Count chip — right-slot chip family (28px, 0 8px, radius-sm, 13/500,
1856
+ neutral 7%/75% fill). Weight-only emphasis when the step is met. */
1857
+ .lb-multi-step__group-count {
1858
+ display: inline-flex;
1859
+ align-items: center;
1860
+ height: 28px;
1861
+ padding: 0 8px;
1862
+ border-radius: var(--lb-radius-sm);
1863
+ background: color-mix(in srgb, var(--lb-text) 7%, transparent);
1864
+ color: color-mix(in srgb, var(--lb-text) 75%, transparent);
1865
+ font-size: 13px;
1866
+ font-weight: 500;
1867
+ white-space: nowrap;
1868
+ }
1869
+
1870
+ .lb-multi-step__group-count--met {
1871
+ font-weight: 600;
1872
+ }
1873
+
1874
+ /* Empty step group: the slots container collapses so the heading rows
1875
+ read as a compact checklist until picks land. */
1876
+ .lb-multi-step__group-slots:empty {
1877
+ display: none;
1878
+ }
1879
+
1880
+ /* ── Wizard modal footer: [Back] [count] [Next|Done] ─────────── */
1881
+
1882
+ .lb-multi-step__modal-footer {
1883
+ display: grid;
1884
+ grid-template-columns: 1fr auto 1fr;
1885
+ align-items: center;
1886
+ gap: 12px;
1887
+ }
1888
+
1889
+ .lb-multi-step__modal-footer > .lb-multi-step__modal-back {
1890
+ grid-area: 1 / 1;
1891
+ justify-self: start;
1892
+ }
1893
+
1894
+ .lb-multi-step__modal-footer > .lb-mix-match__modal-footer-count {
1895
+ grid-area: 1 / 2;
1896
+ text-align: center;
1897
+ }
1898
+
1899
+ .lb-multi-step__modal-footer > .lb-mix-match__modal-done {
1900
+ grid-area: 1 / 3;
1901
+ justify-self: end;
1902
+ }
1903
+
1904
+ .lb-multi-step__modal-back {
1905
+ display: inline-flex;
1906
+ align-items: center;
1907
+ justify-content: center;
1908
+ min-height: 40px;
1909
+ padding: 10px 24px;
1910
+ background: none;
1911
+ border: var(--lb-picker-border-width) solid var(--lb-picker-border-color);
1912
+ border-radius: var(--lb-picker-radius-sm);
1913
+ box-sizing: border-box;
1914
+ color: var(--lb-picker-text);
1915
+ font-family: inherit;
1916
+ font-size: 14px;
1917
+ font-weight: 600;
1918
+ cursor: pointer;
1919
+ transition: background-color 0.25s ease;
1920
+ }
1921
+
1922
+ .lb-multi-step__modal-back:hover {
1923
+ background: color-mix(in srgb, var(--lb-picker-text) 5%, transparent);
1924
+ }
1925
+
1926
+ .lb-multi-step__modal-back:focus-visible {
1927
+ outline: 2px solid var(--lb-primary-color);
1928
+ outline-offset: 2px;
1929
+ }