@nok-integration/storefront-react 0.6.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,2277 @@
1
+ /**
2
+ * DAZN storefront design tokens — canonical names from the design spec, implemented
3
+ * as CSS custom properties. Mobile-first, 375pt base width, embedded in the DAZN app.
4
+ * Values are display tokens only; money is always server-authoritative.
5
+ */
6
+ :root {
7
+ /* ── Typeface ─────────────────────────────────────────────────────────── */
8
+ --font-family-base:
9
+ 'DAZN Oscine', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
10
+ sans-serif;
11
+ --font-weight-regular: 400;
12
+ --font-weight-bold: 700;
13
+
14
+ /* ── Type scale (size / line-height) ──────────────────────────────────── */
15
+ --type-heading-lg-size: 24px;
16
+ --type-heading-lg-line: 1.2;
17
+ --type-heading-md-size: 20px;
18
+ --type-heading-sm-size: 20px;
19
+ --type-heading-md-line: 1.25;
20
+ --type-paragraph-size: 16px;
21
+ --type-paragraph-line: 1.55;
22
+ --type-trimmed-md-size: 16px;
23
+ --type-trimmed-md-line: 1;
24
+ --type-trimmed-sm-size: 14px;
25
+ --type-trimmed-sm-line: 1;
26
+ --type-caption-sm-size: 14px;
27
+ --type-caption-sm-line: 1.5;
28
+ --type-trimmed-xs-size: 12px;
29
+ --type-trimmed-xs-line: 1;
30
+
31
+ /* ── Colour ───────────────────────────────────────────────────────────── */
32
+ --color-text-default: #0c161c;
33
+ --color-text-subtle: #3d4549;
34
+ --color-text-disabled: #9ea2a4;
35
+ --color-text-strong: #f9fafb;
36
+ --color-surface-default: #f9fafa;
37
+ --color-surface-soft: #f0f2f2;
38
+ --color-surface-max: #080e12;
39
+ --color-border-default: #dddedf;
40
+ --color-border-subtle: #ced0d2;
41
+ --color-state-text-success: #147e4a;
42
+ --color-state-surface-success-soft: #def1e9;
43
+ --color-state-surface-critical-default: #d12424;
44
+ --color-state-text-warning: #ff9600;
45
+ --color-state-surface-warning-soft: #fcf1e1;
46
+
47
+ /* ── Spacing scale ────────────────────────────────────────────────────── */
48
+ --space-2: 2px;
49
+ --space-4: 4px;
50
+ --space-6: 6px;
51
+ --space-8: 8px;
52
+ --space-12: 12px;
53
+ --space-16: 16px;
54
+ --space-20: 20px;
55
+ --space-24: 24px;
56
+ --space-32: 32px;
57
+
58
+ /* ── Radius ───────────────────────────────────────────────────────────── */
59
+ --radius-2: 2px;
60
+ --radius-4: 4px;
61
+ --radius-6: 6px;
62
+ --radius-8: 8px;
63
+ --radius-12: 12px;
64
+ --radius-18: 18px;
65
+ --radius-full: 9999px;
66
+
67
+ /* ── Interaction ──────────────────────────────────────────────────────── */
68
+ --tap-target-min: 44px;
69
+ --focus-ring: 0 0 0 2px #ffffff, 0 0 0 4px var(--color-surface-max);
70
+ --safe-area-bottom: env(safe-area-inset-bottom, 0px);
71
+ }
72
+
73
+ /*
74
+ * Dark theme (PCP + homepage; the PDP stays light). Remaps the semantic tokens so the
75
+ * header, body and reused components (cards, price, rails) all adapt automatically.
76
+ * Elements that sit OVER product images (e.g. the TOP PICK card badge) use fixed colours
77
+ * so they don't invert.
78
+ *
79
+ * Two selectors, one set of values:
80
+ *
81
+ * - `:root[data-theme='dark']` is set on <html> by our own app's ThemeController, which
82
+ * also themes the host chrome around the screen (header, tab bar) — those live outside
83
+ * this package, so the switch has to be reachable from the document root.
84
+ * - `[data-shop-theme='dark']` is set by `ShopSurface` on the screen itself. This is the
85
+ * one that matters to an embedding host: the palette is a property of the design, so a
86
+ * mounted view must not depend on the host having set anything at all.
87
+ */
88
+ :root[data-theme='dark'],
89
+ [data-shop-theme='dark'] {
90
+ --color-text-default: #f9fafa;
91
+ --color-text-subtle: #aeb6bb;
92
+ --color-text-strong: #0c161c;
93
+ --color-text-disabled: #6b7378;
94
+ --color-surface-default: #080e12;
95
+ --color-surface-soft: #171e23;
96
+ --color-surface-max: #f9fafa;
97
+ --color-border-default: #2a3238;
98
+ --color-border-subtle: #2a3238;
99
+ }
100
+
101
+ /*
102
+ * The light values restated, because custom properties INHERIT. A light surface nested in
103
+ * a page that already went dark — our own app on /category, or a host that themed its
104
+ * document — would otherwise keep the inherited dark values and only the `:root` defaults
105
+ * would be shadowed. A surface has to be able to state its palette without reference to
106
+ * what it happens to be sitting inside.
107
+ */
108
+ [data-shop-theme='light'] {
109
+ --color-text-default: #0c161c;
110
+ --color-text-subtle: #3d4549;
111
+ --color-text-strong: #f9fafb;
112
+ --color-text-disabled: #9ea2a4;
113
+ --color-surface-default: #f9fafa;
114
+ --color-surface-soft: #f0f2f2;
115
+ --color-surface-max: #080e12;
116
+ --color-border-default: #dddedf;
117
+ --color-border-subtle: #ced0d2;
118
+ }
119
+ ._badge_1bagf_1 {
120
+ display: inline-flex;
121
+ align-items: center;
122
+ padding: var(--space-2) var(--space-8);
123
+ border-radius: var(--radius-full);
124
+ font-size: var(--type-trimmed-xs-size);
125
+ line-height: 1.4;
126
+ font-weight: var(--font-weight-bold);
127
+ white-space: nowrap;
128
+ }
129
+
130
+ ._neutral_1bagf_12 {
131
+ background: var(--color-surface-soft);
132
+ color: var(--color-text-subtle);
133
+ }
134
+
135
+ ._success_1bagf_17 {
136
+ background: var(--color-state-surface-success-soft);
137
+ color: var(--color-state-text-success);
138
+ }
139
+
140
+ ._critical_1bagf_22 {
141
+ background: var(--color-state-surface-critical-default);
142
+ color: var(--color-text-strong);
143
+ }
144
+
145
+ ._promo_1bagf_27 {
146
+ background: var(--color-surface-max);
147
+ color: var(--color-text-strong);
148
+ }
149
+ ._button_vvcmd_1 {
150
+ display: inline-flex;
151
+ align-items: center;
152
+ justify-content: center;
153
+ gap: var(--space-8);
154
+ min-height: var(--tap-target-min);
155
+ min-width: var(--tap-target-min);
156
+ padding: 0 var(--space-20);
157
+ /* DS button corners are radius-8 (not a pill), per the Figma mobile/button. */
158
+ border-radius: var(--radius-8);
159
+ border: 1px solid transparent;
160
+ font-family: inherit;
161
+ font-size: var(--type-trimmed-md-size);
162
+ font-weight: var(--font-weight-bold);
163
+ line-height: 1;
164
+ cursor: pointer;
165
+ transition:
166
+ background-color 120ms ease,
167
+ opacity 120ms ease;
168
+ -webkit-tap-highlight-color: transparent;
169
+ }
170
+
171
+ ._fullWidth_vvcmd_23 {
172
+ width: 100%;
173
+ }
174
+
175
+ ._primary_vvcmd_27 {
176
+ background: var(--color-surface-max);
177
+ color: var(--color-text-strong);
178
+ }
179
+
180
+ ._secondary_vvcmd_32 {
181
+ background: transparent;
182
+ color: var(--color-text-default);
183
+ border-color: var(--color-border-default);
184
+ }
185
+
186
+ ._ghost_vvcmd_38 {
187
+ background: transparent;
188
+ color: var(--color-text-default);
189
+ border-color: transparent;
190
+ }
191
+
192
+ ._button_vvcmd_1:disabled {
193
+ cursor: not-allowed;
194
+ opacity: 0.5;
195
+ }
196
+
197
+ ._button_vvcmd_1:focus-visible {
198
+ outline: 2px solid var(--color-surface-max);
199
+ outline-offset: 2px;
200
+ }
201
+
202
+ ._spinner_vvcmd_54 {
203
+ width: 16px;
204
+ height: 16px;
205
+ border-radius: var(--radius-full);
206
+ border: 2px solid currentColor;
207
+ border-top-color: transparent;
208
+ animation: _spin_vvcmd_54 700ms linear infinite;
209
+ }
210
+
211
+ ._labelLoading_vvcmd_63 {
212
+ opacity: 0.85;
213
+ }
214
+
215
+ @keyframes _spin_vvcmd_54 {
216
+ to {
217
+ transform: rotate(360deg);
218
+ }
219
+ }
220
+ ._skeleton_1nxz7_1 {
221
+ display: block;
222
+ background: linear-gradient(
223
+ 90deg,
224
+ var(--color-surface-soft) 25%,
225
+ var(--color-border-default) 37%,
226
+ var(--color-surface-soft) 63%
227
+ );
228
+ background-size: 400% 100%;
229
+ border-radius: var(--radius-8);
230
+ animation: _shimmer_1nxz7_1 1.4s ease infinite;
231
+ }
232
+
233
+ @keyframes _shimmer_1nxz7_1 {
234
+ 0% {
235
+ background-position: 100% 50%;
236
+ }
237
+ 100% {
238
+ background-position: 0 50%;
239
+ }
240
+ }
241
+ /*
242
+ * Base typography and element defaults for everything the package renders.
243
+ *
244
+ * These rules used to live in the Next app's `globals.css`, applied to `<body>`. Every
245
+ * component in this package was written against them and none of them travelled with the
246
+ * components when they moved here — so a host that mounts a view gets its own body font
247
+ * (serif, in a page that never set one) and its own background.
248
+ */
249
+
250
+ ._surface_168jv_10 {
251
+ font-family: var(--font-family-base);
252
+ font-weight: var(--font-weight-regular);
253
+ font-size: var(--type-paragraph-size);
254
+ line-height: var(--type-paragraph-line);
255
+ color: var(--color-text-default);
256
+ background: var(--color-surface-default);
257
+ -webkit-font-smoothing: antialiased;
258
+ text-rendering: optimizeLegibility;
259
+ }
260
+
261
+ ._surface_168jv_10 *,
262
+ ._surface_168jv_10 *::before,
263
+ ._surface_168jv_10 *::after {
264
+ box-sizing: border-box;
265
+ }
266
+
267
+ /*
268
+ * A screen paints the whole viewport, not just its content.
269
+ *
270
+ * A host embedding a view in a smaller region can turn this off with `fill={false}` — but
271
+ * the default has to be on, since the failure it prevents (the host's background showing
272
+ * through a screen of the opposite palette) is invisible in our own app, where the body
273
+ * already matches, and so goes unnoticed until a partner reports it.
274
+ */
275
+ ._fill_168jv_35 {
276
+ /* Declared twice on purpose, and the order matters.
277
+ `dvh` landed in Chrome 108 / iOS 15.4, and this package supports React 16.14 because
278
+ that is what DAZN's app runs — an app that old can be paired with a WebView that old.
279
+ A browser that does not understand `dvh` drops the second declaration and keeps the
280
+ first, so the worst case is `vh`'s URL-bar inaccuracy rather than no fill at all. */
281
+ min-height: 100vh;
282
+ min-height: 100dvh;
283
+ }
284
+
285
+ ._surface_168jv_10 :is(h1, h2, h3) {
286
+ margin: 0;
287
+ font-weight: var(--font-weight-bold);
288
+ }
289
+
290
+ ._surface_168jv_10 h1 {
291
+ font-size: var(--type-heading-lg-size);
292
+ line-height: var(--type-heading-lg-line);
293
+ }
294
+
295
+ ._surface_168jv_10 h2 {
296
+ font-size: var(--type-heading-md-size);
297
+ line-height: var(--type-heading-md-line);
298
+ }
299
+
300
+ /* Links are chrome-coloured by the component that owns them, never browser-blue.
301
+ *
302
+ * `:not([class])` is load-bearing, and its absence was a real bug. `.surface a` is
303
+ * specificity (0,1,1) and a CSS-module class is (0,1,0), so this rule beat every component
304
+ * that set its own link colour — `.cta { color: #f9fafa }` on the hero button asked for
305
+ * white and was overruled into `inherit`.
306
+ *
307
+ * On the dark screens inherit happened to BE white, so nothing looked wrong and the bug
308
+ * sat here unseen. It surfaced the moment those tokens were repainted: the button kept its
309
+ * white border, which nothing competed for, and took near-black text from the surface.
310
+ *
311
+ * Restricting the rule to unclassed anchors keeps what it is for — a bare `<a>` must never
312
+ * render browser-blue — while leaving any anchor that carries a class in charge of itself.
313
+ */
314
+ ._surface_168jv_10 a:not([class]) {
315
+ color: inherit;
316
+ }
317
+
318
+ ._surface_168jv_10 img {
319
+ max-width: 100%;
320
+ height: auto;
321
+ }
322
+
323
+ /* Visible focus for keyboard users — a WCAG 2.1 AA launch gate, so it cannot be left
324
+ to the host to remember. */
325
+ ._surface_168jv_10 :focus-visible {
326
+ outline: 2px solid var(--color-surface-max);
327
+ outline-offset: 2px;
328
+ border-radius: var(--radius-4);
329
+ }
330
+
331
+ @media (prefers-reduced-motion: reduce) {
332
+ ._surface_168jv_10 *,
333
+ ._surface_168jv_10 *::before,
334
+ ._surface_168jv_10 *::after {
335
+ animation-duration: 0.001ms !important;
336
+ animation-iteration-count: 1 !important;
337
+ transition-duration: 0.001ms !important;
338
+ scroll-behavior: auto !important;
339
+ }
340
+ }
341
+ ._root_ney0g_1 {
342
+ display: flex;
343
+ flex-direction: column;
344
+ }
345
+
346
+ ._inner_ney0g_6 {
347
+ display: flex;
348
+ flex-direction: column;
349
+ gap: var(--space-16);
350
+ padding-bottom: var(--space-24);
351
+ }
352
+
353
+ /*
354
+ * The media block carries the screen on its own.
355
+ *
356
+ * `aspect-ratio` rather than a fixed height so it reserves the same space the real image
357
+ * will take at any width — the point of a skeleton is that nothing moves when the content
358
+ * arrives. 4/5 is the PDP gallery's ratio.
359
+ */
360
+ ._media_ney0g_20 {
361
+ width: 100%;
362
+ /* The floor is not decoration. `aspect-ratio` is Chrome 88 / iOS 15, and this package
363
+ targets WebViews old enough to run React 16.14 — where an unsupported `aspect-ratio`
364
+ on a span with no content computes to zero height, which is the exact failure this
365
+ component exists to fix. `min-height` keeps a visible block either way; where
366
+ `aspect-ratio` works it resolves taller and wins. */
367
+ min-height: 320px;
368
+ aspect-ratio: 4 / 5;
369
+ border-radius: 0;
370
+ }
371
+
372
+ ._lines_ney0g_32 {
373
+ display: flex;
374
+ flex-direction: column;
375
+ gap: var(--space-12);
376
+ padding: 0 var(--space-16);
377
+ }
378
+
379
+ /* Uneven widths on purpose. Three identical bars read as a table; ragged ones read as
380
+ prose, which is what is actually coming. */
381
+ ._lineWide_ney0g_41 {
382
+ width: 70%;
383
+ }
384
+
385
+ ._lineMid_ney0g_45 {
386
+ width: 45%;
387
+ }
388
+
389
+ ._lineNarrow_ney0g_49 {
390
+ width: 88%;
391
+ }
392
+
393
+ ._row_ney0g_53 {
394
+ width: 100%;
395
+ }
396
+
397
+ ._action_ney0g_57 {
398
+ margin: var(--space-8) var(--space-16) 0;
399
+ }
400
+ ._root_f9nel_1 {
401
+ display: flex;
402
+ flex-direction: column;
403
+ align-items: center;
404
+ text-align: center;
405
+ gap: var(--space-8);
406
+ padding: var(--space-32) var(--space-24);
407
+ }
408
+
409
+ ._icon_f9nel_10 {
410
+ font-size: 32px;
411
+ line-height: 1;
412
+ }
413
+
414
+ ._title_f9nel_15 {
415
+ font-size: var(--type-heading-md-size);
416
+ line-height: var(--type-heading-md-line);
417
+ }
418
+
419
+ ._body_f9nel_20 {
420
+ margin: 0;
421
+ color: var(--color-text-subtle);
422
+ font-size: var(--type-paragraph-size);
423
+ max-width: 34ch;
424
+ }
425
+
426
+ ._action_f9nel_27 {
427
+ margin-top: var(--space-8);
428
+ }
429
+ ._root_1lbpx_1 {
430
+ display: flex;
431
+ flex-direction: column;
432
+ align-items: center;
433
+ text-align: center;
434
+ gap: var(--space-12);
435
+ padding: var(--space-32) var(--space-24);
436
+ }
437
+
438
+ ._title_1lbpx_10 {
439
+ font-size: var(--type-heading-md-size);
440
+ line-height: var(--type-heading-md-line);
441
+ }
442
+
443
+ ._body_1lbpx_15 {
444
+ margin: 0;
445
+ color: var(--color-text-subtle);
446
+ max-width: 34ch;
447
+ }
448
+ ._viewport_mxa9g_1 {
449
+ position: fixed;
450
+ left: 0;
451
+ right: 0;
452
+ bottom: calc(var(--space-16) + var(--safe-area-bottom));
453
+ display: flex;
454
+ flex-direction: column;
455
+ align-items: center;
456
+ gap: var(--space-8);
457
+ padding: 0 var(--space-16);
458
+ pointer-events: none;
459
+ z-index: 1000;
460
+ }
461
+
462
+ ._toast_mxa9g_15 {
463
+ display: flex;
464
+ align-items: center;
465
+ justify-content: space-between;
466
+ gap: var(--space-12);
467
+ width: 100%;
468
+ max-width: 420px;
469
+ padding: var(--space-12) var(--space-16);
470
+ border-radius: var(--radius-12);
471
+ font-size: var(--type-trimmed-sm-size);
472
+ font-weight: var(--font-weight-bold);
473
+ box-shadow: 0 6px 24px rgba(8, 14, 18, 0.24);
474
+ /* The viewport is click-through so it never blocks the page beneath; the toast
475
+ itself must not be, or an actionable toast's button would be unclickable. */
476
+ pointer-events: auto;
477
+ }
478
+
479
+ ._message_mxa9g_32 {
480
+ min-width: 0;
481
+ }
482
+
483
+ /* Meets the 44px tap target via padding while staying visually inline with the text. */
484
+ ._action_mxa9g_37 {
485
+ flex-shrink: 0;
486
+ min-height: var(--tap-target-min);
487
+ padding: 0 var(--space-12);
488
+ margin: calc(var(--space-12) * -1) calc(var(--space-8) * -1);
489
+ border: none;
490
+ background: none;
491
+ color: inherit;
492
+ font: inherit;
493
+ text-decoration: underline;
494
+ cursor: pointer;
495
+ }
496
+
497
+ ._action_mxa9g_37:focus-visible {
498
+ outline: 2px solid currentColor;
499
+ outline-offset: 2px;
500
+ border-radius: var(--radius-4);
501
+ }
502
+
503
+ ._neutral_mxa9g_56 {
504
+ background: var(--color-surface-max);
505
+ color: var(--color-text-strong);
506
+ }
507
+
508
+ ._success_mxa9g_61 {
509
+ background: var(--color-state-text-success);
510
+ color: var(--color-text-strong);
511
+ }
512
+
513
+ ._critical_mxa9g_66 {
514
+ background: var(--color-state-surface-critical-default);
515
+ color: var(--color-text-strong);
516
+ }
517
+ /* Figma mobile/badge/sale (sm): success-soft bg, success text, icon + -NN%, radius-4. */
518
+ ._badge_156ya_2 {
519
+ display: inline-flex;
520
+ align-items: center;
521
+ gap: var(--space-4);
522
+ height: 20px;
523
+ padding: var(--space-4);
524
+ border-radius: var(--radius-4);
525
+ background: var(--color-state-surface-success-soft);
526
+ color: var(--color-state-text-success);
527
+ font-size: var(--type-trimmed-xs-size);
528
+ line-height: var(--type-trimmed-xs-line);
529
+ font-weight: var(--font-weight-bold);
530
+ letter-spacing: 0.12px;
531
+ white-space: nowrap;
532
+ }
533
+
534
+ ._icon_156ya_18 {
535
+ display: block;
536
+ flex-shrink: 0;
537
+ }
538
+ ._root_l5uj5_1 {
539
+ display: inline-flex;
540
+ align-items: baseline;
541
+ gap: var(--space-8);
542
+ flex-wrap: wrap;
543
+ }
544
+
545
+ /* Alignment variants. Cards align on the price baseline row with a tight 4px gap; the
546
+ PDP price row centres the price against its sale badge (6px). */
547
+ ._center_l5uj5_10 {
548
+ align-items: center;
549
+ gap: var(--space-6);
550
+ }
551
+ ._end_l5uj5_14 {
552
+ align-items: flex-end;
553
+ gap: var(--space-4);
554
+ }
555
+
556
+ /* Size sets the row's font-size so the price and its struck was-price match (per the
557
+ product-card, where both are 16px). */
558
+ ._sm_l5uj5_21 {
559
+ font-size: var(--type-trimmed-sm-size);
560
+ }
561
+ ._md_l5uj5_24 {
562
+ font-size: var(--type-trimmed-md-size);
563
+ }
564
+ ._lg_l5uj5_27 {
565
+ font-size: var(--type-heading-md-size);
566
+ }
567
+
568
+ ._price_l5uj5_31 {
569
+ font-weight: var(--font-weight-bold);
570
+ color: var(--color-text-default);
571
+ }
572
+
573
+ ._compareAt_l5uj5_36 {
574
+ color: var(--color-text-disabled);
575
+ text-decoration: line-through;
576
+ font-weight: var(--font-weight-regular);
577
+ }
578
+ ._tile_1dy20_1 {
579
+ display: flex;
580
+ flex-direction: column;
581
+ gap: var(--space-12);
582
+ text-decoration: none;
583
+ color: inherit;
584
+ }
585
+
586
+ ._media_1dy20_9 {
587
+ position: relative;
588
+ width: 100%;
589
+ /* Product-card image is 165:220 (3:4), radius-6 (Figma), sharp not pill-round. */
590
+ aspect-ratio: 165 / 220;
591
+ border-radius: var(--radius-6);
592
+ overflow: hidden;
593
+ background: var(--color-surface-soft);
594
+ }
595
+
596
+ ._image_1dy20_19 {
597
+ object-fit: cover;
598
+ }
599
+
600
+ ._placeholder_1dy20_23 {
601
+ width: 100%;
602
+ height: 100%;
603
+ background: var(--color-surface-soft);
604
+ }
605
+
606
+ /* TOP PICK flag — top-left over the image, white surface, radius-2, uppercase. Fixed
607
+ colours (not theme tokens): it always sits over a light product image. */
608
+ ._topBadge_1dy20_31 {
609
+ position: absolute;
610
+ top: var(--space-8);
611
+ left: var(--space-8);
612
+ /* The badge string comes from product data, not from a fixed vocabulary. The design
613
+ only ever shows short flags (TOP PICK, FEW LEFT), so nothing constrained the width —
614
+ and a longer one ("99% HAPPY CUSTOMERS") ran past the tile and was sliced mid-word by
615
+ the media box's own overflow. Truncating inside the tile is the honest failure. */
616
+ max-width: calc(100% - var(--space-8) * 2);
617
+ overflow: hidden;
618
+ text-overflow: ellipsis;
619
+ padding: var(--space-4) var(--space-6);
620
+ border-radius: var(--radius-2);
621
+ background: #f9fafa;
622
+ color: #0c161c;
623
+ font-size: var(--type-trimmed-xs-size);
624
+ line-height: var(--type-trimmed-xs-line);
625
+ font-weight: var(--font-weight-bold);
626
+ letter-spacing: 0.12px;
627
+ text-transform: uppercase;
628
+ white-space: nowrap;
629
+ }
630
+
631
+ ._saleBadge_1dy20_54 {
632
+ position: absolute;
633
+ bottom: var(--space-8);
634
+ left: var(--space-8);
635
+ }
636
+
637
+ ._oos_1dy20_60 {
638
+ position: absolute;
639
+ top: var(--space-8);
640
+ left: var(--space-8);
641
+ }
642
+
643
+ ._bottom_1dy20_66 {
644
+ display: flex;
645
+ flex-direction: column;
646
+ gap: var(--space-16);
647
+ }
648
+
649
+ ._titlePrice_1dy20_72 {
650
+ display: flex;
651
+ flex-direction: column;
652
+ gap: var(--space-8);
653
+ }
654
+
655
+ ._title_1dy20_72 {
656
+ font-size: var(--type-paragraph-size);
657
+ line-height: 1.3;
658
+ font-weight: var(--font-weight-regular);
659
+ color: var(--color-text-default);
660
+ display: -webkit-box;
661
+ -webkit-line-clamp: 2;
662
+ line-clamp: 2;
663
+ -webkit-box-orient: vertical;
664
+ overflow: hidden;
665
+ /* Reserve both lines rather than only capping at two. The clamp stops a long title
666
+ overflowing but lets a short one collapse to one line, and in a two-column grid that
667
+ pulls its price up level with the neighbour's title — a ragged price baseline across
668
+ the row, which reads as broken alignment rather than as varying title lengths. */
669
+ min-height: calc(2 * 1.3em);
670
+ }
671
+
672
+ /* Colour swatches (Figma product-card): 0.5px-bordered boxes with a 14px colour square;
673
+ the first reads as selected (dark border). */
674
+ ._swatches_1dy20_97 {
675
+ display: flex;
676
+ gap: var(--space-4);
677
+ align-items: center;
678
+ }
679
+
680
+ ._swatch_1dy20_97 {
681
+ display: inline-flex;
682
+ padding: 2px;
683
+ border: 0.5px solid var(--color-border-subtle);
684
+ border-radius: var(--radius-2);
685
+ }
686
+
687
+ ._swatchActive_1dy20_110 {
688
+ border-color: var(--color-surface-max);
689
+ }
690
+
691
+ ._swatchDot_1dy20_114 {
692
+ display: block;
693
+ width: 14px;
694
+ height: 14px;
695
+ border-radius: var(--radius-2);
696
+ }
697
+ ._bar_fn0g9_1 {
698
+ display: flex;
699
+ align-items: center;
700
+ gap: var(--space-8);
701
+ padding: var(--space-8) var(--space-16);
702
+ }
703
+
704
+ ._chips_fn0g9_8 {
705
+ display: flex;
706
+ gap: var(--space-8);
707
+ overflow-x: auto;
708
+ flex: 1 1 auto;
709
+ -webkit-overflow-scrolling: touch;
710
+ }
711
+
712
+ ._chip_fn0g9_8 {
713
+ flex: 0 0 auto;
714
+ min-height: var(--tap-target-min);
715
+ padding: 0 var(--space-16);
716
+ border-radius: var(--radius-full);
717
+ border: 1px solid var(--color-border-default);
718
+ background: var(--color-surface-default);
719
+ color: var(--color-text-default);
720
+ font-family: inherit;
721
+ font-size: var(--type-trimmed-sm-size);
722
+ cursor: pointer;
723
+ }
724
+
725
+ ._on_fn0g9_29 {
726
+ background: var(--color-surface-max);
727
+ color: var(--color-text-strong);
728
+ border-color: var(--color-surface-max);
729
+ }
730
+
731
+ ._clear_fn0g9_35 {
732
+ flex: 0 0 auto;
733
+ min-height: var(--tap-target-min);
734
+ padding: 0 var(--space-8);
735
+ border: none;
736
+ background: transparent;
737
+ color: var(--color-text-subtle);
738
+ font-family: inherit;
739
+ font-size: var(--type-trimmed-sm-size);
740
+ text-decoration: underline;
741
+ cursor: pointer;
742
+ }
743
+ ._wrap_15ntf_1 {
744
+ display: flex;
745
+ flex-direction: column;
746
+ gap: var(--space-12);
747
+ }
748
+
749
+ ._grid_15ntf_7 {
750
+ list-style: none;
751
+ margin: 0;
752
+ padding: 0 var(--space-16);
753
+ display: grid;
754
+ grid-template-columns: repeat(2, 1fr);
755
+ gap: var(--space-16);
756
+ }
757
+ ._rail_6ckcw_1 {
758
+ display: flex;
759
+ flex-direction: column;
760
+ gap: var(--space-12);
761
+ }
762
+
763
+ ._header_6ckcw_7 {
764
+ display: flex;
765
+ align-items: baseline;
766
+ justify-content: space-between;
767
+ padding: 0 var(--space-16);
768
+ }
769
+
770
+ ._title_6ckcw_14 {
771
+ font-size: var(--type-heading-md-size);
772
+ /* Figma rail header: 20px, line-height 1.38. */
773
+ line-height: 1.38;
774
+ }
775
+
776
+ ._seeAll_6ckcw_20 {
777
+ font-size: var(--type-trimmed-sm-size);
778
+ color: var(--color-text-subtle);
779
+ text-decoration: none;
780
+ }
781
+
782
+ ._track_6ckcw_26 {
783
+ display: flex;
784
+ gap: var(--space-12);
785
+ list-style: none;
786
+ margin: 0;
787
+ padding: 0 var(--space-16) var(--space-8);
788
+ overflow-x: auto;
789
+ scroll-snap-type: x mandatory;
790
+ /* Keep the 16px leading/trailing inset under mandatory snap: without this the
791
+ snapport start sits at the border edge, so the first item snaps to x=0 and the
792
+ left padding is scrolled away — misaligning tiles from the section heading. */
793
+ scroll-padding-inline: var(--space-16);
794
+ -webkit-overflow-scrolling: touch;
795
+ }
796
+
797
+ ._item_6ckcw_41 {
798
+ flex: 0 0 auto;
799
+ width: 45vw;
800
+ max-width: 180px;
801
+ scroll-snap-align: start;
802
+ }
803
+
804
+ /*
805
+ * Inset variant — the rail lives INSIDE an already-padded column (the PDP content
806
+ * block, 16px each side) rather than bleeding edge-to-edge like the home rails. The
807
+ * parent supplies the 16px inset, so the header/track add none of their own, and tiles
808
+ * size to show exactly two across the content width (per the PDP design: rail x16 w343).
809
+ */
810
+ ._inset_6ckcw_54 ._header_6ckcw_7 {
811
+ padding: 0;
812
+ }
813
+
814
+ ._inset_6ckcw_54 ._track_6ckcw_26 {
815
+ padding-left: 0;
816
+ padding-right: 0;
817
+ scroll-padding-inline: 0;
818
+ }
819
+
820
+ ._inset_6ckcw_54 ._item_6ckcw_41 {
821
+ /* Fixed 165px cards (Figma product-card size=default). At 375 two fit the 343 content
822
+ width exactly; more scroll horizontally. */
823
+ width: 165px;
824
+ max-width: none;
825
+ }
826
+ ._gallery_h8m00_1 {
827
+ position: relative;
828
+ display: flex;
829
+ flex-direction: column;
830
+ gap: var(--space-12);
831
+ }
832
+
833
+ ._track_h8m00_8 {
834
+ display: flex;
835
+ overflow-x: auto;
836
+ scroll-snap-type: x mandatory;
837
+ -webkit-overflow-scrolling: touch;
838
+ /* Hero media is 375×485 (portrait) and fully sharp (no rounded corners) — it sits
839
+ edge-to-edge and flush under the header. */
840
+ aspect-ratio: 375 / 485;
841
+ background: var(--color-surface-soft);
842
+ }
843
+
844
+ ._slide_h8m00_19 {
845
+ position: relative;
846
+ flex: 0 0 100%;
847
+ scroll-snap-align: center;
848
+ aspect-ratio: 375 / 485;
849
+ }
850
+
851
+ ._image_h8m00_26 {
852
+ object-fit: cover;
853
+ }
854
+
855
+ ._placeholder_h8m00_30 {
856
+ width: 100%;
857
+ aspect-ratio: 375 / 485;
858
+ background: var(--color-surface-soft);
859
+ }
860
+
861
+ /* Share button — top-right chrome over the image (mobile/button: 32px, soft, radius-8). */
862
+ ._share_h8m00_37 {
863
+ position: absolute;
864
+ top: var(--space-16);
865
+ right: var(--space-16);
866
+ display: inline-flex;
867
+ align-items: center;
868
+ justify-content: center;
869
+ width: 32px;
870
+ height: 32px;
871
+ padding: 0;
872
+ border: none;
873
+ border-radius: var(--radius-8);
874
+ background: var(--color-surface-soft);
875
+ color: var(--color-text-default);
876
+ cursor: pointer;
877
+ }
878
+
879
+ ._dots_h8m00_54 {
880
+ display: flex;
881
+ justify-content: center;
882
+ gap: var(--space-6);
883
+ }
884
+
885
+ ._dot_h8m00_54 {
886
+ width: 8px;
887
+ height: 8px;
888
+ padding: 0;
889
+ border: none;
890
+ border-radius: var(--radius-full);
891
+ background: var(--color-border-default);
892
+ cursor: pointer;
893
+ /* Enlarge the hit area to >=44px without growing the visual dot. */
894
+ position: relative;
895
+ }
896
+
897
+ ._dot_h8m00_54::after {
898
+ content: '';
899
+ position: absolute;
900
+ inset: -18px;
901
+ }
902
+
903
+ ._dotActive_h8m00_78 {
904
+ background: var(--color-surface-max);
905
+ }
906
+ /*
907
+ * Size-guide bottom sheet. Uses the same scrim + rounded-top surface as the other PDP
908
+ * overlays so it reads as one system. Fixed light surface: the PDP is light in every
909
+ * theme, and an inverted sheet over a light page would be jarring.
910
+ */
911
+ ._scrim_8wz6r_6 {
912
+ position: fixed;
913
+ inset: 0;
914
+ z-index: 60;
915
+ display: flex;
916
+ align-items: flex-end;
917
+ justify-content: center;
918
+ background: rgba(8, 14, 18, 0.5);
919
+ animation: _fade_8wz6r_1 160ms ease-out;
920
+ }
921
+
922
+ ._sheet_8wz6r_17 {
923
+ width: 100%;
924
+ max-width: 480px;
925
+ max-height: 80vh;
926
+ overflow-y: auto;
927
+ padding: var(--space-8) var(--space-16) var(--space-24);
928
+ border-radius: var(--radius-18) var(--radius-18) 0 0;
929
+ background: #ffffff;
930
+ color: #16181a;
931
+ animation: _rise_8wz6r_1 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
932
+ }
933
+
934
+ ._grabber_8wz6r_29 {
935
+ width: 40px;
936
+ height: 4px;
937
+ margin: 0 auto var(--space-12);
938
+ border-radius: 2px;
939
+ background: #d8dcde;
940
+ }
941
+
942
+ ._header_8wz6r_37 {
943
+ display: flex;
944
+ align-items: center;
945
+ justify-content: space-between;
946
+ gap: var(--space-12);
947
+ margin-bottom: var(--space-16);
948
+ }
949
+
950
+ ._title_8wz6r_45 {
951
+ margin: 0;
952
+ font-size: var(--type-heading-sm-size, 20px);
953
+ font-weight: var(--font-weight-bold);
954
+ }
955
+
956
+ ._close_8wz6r_51 {
957
+ display: inline-flex;
958
+ align-items: center;
959
+ justify-content: center;
960
+ width: 32px;
961
+ height: 32px;
962
+ flex-shrink: 0;
963
+ border: none;
964
+ border-radius: 50%;
965
+ background: #f2f4f5;
966
+ color: #16181a;
967
+ cursor: pointer;
968
+ }
969
+ ._close_8wz6r_51:focus-visible {
970
+ outline: 2px solid #16181a;
971
+ outline-offset: 2px;
972
+ }
973
+
974
+ /* The chart can be wider than the sheet, so it scrolls inside its own container
975
+ rather than making the sheet scroll sideways. */
976
+ ._tableWrap_8wz6r_71 {
977
+ overflow-x: auto;
978
+ margin-bottom: var(--space-16);
979
+ }
980
+
981
+ ._table_8wz6r_71 {
982
+ width: 100%;
983
+ border-collapse: collapse;
984
+ font-size: var(--type-trimmed-sm-size, 14px);
985
+ }
986
+
987
+ ._table_8wz6r_71 th,
988
+ ._table_8wz6r_71 td {
989
+ padding: var(--space-8) var(--space-12);
990
+ text-align: left;
991
+ border-bottom: 1px solid #e2e6e8;
992
+ white-space: nowrap;
993
+ }
994
+
995
+ ._table_8wz6r_71 thead th {
996
+ font-weight: var(--font-weight-bold);
997
+ background: #f7f8f9;
998
+ }
999
+
1000
+ ._notes_8wz6r_95 {
1001
+ margin: 0;
1002
+ padding-left: var(--space-16);
1003
+ display: flex;
1004
+ flex-direction: column;
1005
+ gap: var(--space-6);
1006
+ font-size: var(--type-trimmed-sm-size, 14px);
1007
+ color: #4a5257;
1008
+ }
1009
+
1010
+ @keyframes _fade_8wz6r_1 {
1011
+ from { opacity: 0; }
1012
+ to { opacity: 1; }
1013
+ }
1014
+ @keyframes _rise_8wz6r_1 {
1015
+ from { transform: translateY(12%); }
1016
+ to { transform: translateY(0); }
1017
+ }
1018
+
1019
+ @media (prefers-reduced-motion: reduce) {
1020
+ ._scrim_8wz6r_6,
1021
+ ._sheet_8wz6r_17 {
1022
+ animation: none;
1023
+ }
1024
+ }
1025
+ ._root_mx6g7_1 {
1026
+ display: flex;
1027
+ flex-direction: column;
1028
+ gap: var(--space-12);
1029
+ }
1030
+
1031
+ ._header_mx6g7_7 {
1032
+ display: flex;
1033
+ align-items: center;
1034
+ }
1035
+
1036
+ ._title_mx6g7_12 {
1037
+ margin: 0;
1038
+ font-size: var(--type-trimmed-md-size);
1039
+ line-height: var(--type-trimmed-md-line);
1040
+ font-weight: var(--font-weight-bold);
1041
+ color: var(--color-text-default);
1042
+ }
1043
+
1044
+ ._chips_mx6g7_20 {
1045
+ display: flex;
1046
+ flex-wrap: wrap;
1047
+ gap: var(--space-8);
1048
+ }
1049
+
1050
+ /* 48px square chips per the mobile/pdp/size component. */
1051
+ ._chip_mx6g7_20 {
1052
+ display: inline-flex;
1053
+ align-items: center;
1054
+ justify-content: center;
1055
+ width: 48px;
1056
+ height: 48px;
1057
+ padding: 0;
1058
+ border: 1px solid var(--color-border-default);
1059
+ border-radius: var(--radius-8);
1060
+ background: var(--color-surface-default);
1061
+ color: var(--color-text-subtle);
1062
+ font-family: inherit;
1063
+ font-size: var(--type-trimmed-sm-size);
1064
+ font-weight: var(--font-weight-bold);
1065
+ cursor: pointer;
1066
+ }
1067
+
1068
+ ._selected_mx6g7_44 {
1069
+ border-color: var(--color-surface-max);
1070
+ background: var(--color-surface-max);
1071
+ color: var(--color-text-strong);
1072
+ }
1073
+
1074
+ ._disabled_mx6g7_50 {
1075
+ cursor: not-allowed;
1076
+ color: var(--color-text-disabled);
1077
+ text-decoration: line-through;
1078
+ background: var(--color-surface-soft);
1079
+ }
1080
+
1081
+ ._error_mx6g7_57 {
1082
+ margin: 0;
1083
+ font-size: var(--type-trimmed-sm-size);
1084
+ font-weight: var(--font-weight-bold);
1085
+ color: var(--color-state-surface-critical-default);
1086
+ }
1087
+
1088
+ ._sizeGuide_mx6g7_64 {
1089
+ display: inline-flex;
1090
+ align-items: center;
1091
+ gap: var(--space-2);
1092
+ align-self: flex-start;
1093
+ padding: 0;
1094
+ border: none;
1095
+ background: none;
1096
+ color: var(--color-text-subtle);
1097
+ font-family: inherit;
1098
+ font-size: var(--type-trimmed-sm-size);
1099
+ cursor: pointer;
1100
+ }
1101
+
1102
+ ._infoIcon_mx6g7_78 {
1103
+ display: block;
1104
+ }
1105
+ ._root_5qbaw_1 {
1106
+ display: flex;
1107
+ flex-direction: column;
1108
+ gap: var(--space-12);
1109
+ }
1110
+
1111
+ ._header_5qbaw_7 {
1112
+ margin: 0;
1113
+ display: flex;
1114
+ gap: var(--space-4);
1115
+ font-size: var(--type-trimmed-md-size);
1116
+ }
1117
+
1118
+ ._title_5qbaw_14 {
1119
+ font-weight: var(--font-weight-bold);
1120
+ color: var(--color-text-default);
1121
+ }
1122
+
1123
+ ._value_5qbaw_19 {
1124
+ font-weight: var(--font-weight-regular);
1125
+ color: var(--color-text-subtle);
1126
+ }
1127
+
1128
+ ._tiles_5qbaw_24 {
1129
+ display: flex;
1130
+ flex-wrap: wrap;
1131
+ gap: var(--space-8);
1132
+ }
1133
+
1134
+ /* 79×105 image tile, radius-8. Selected gets a dark 2px border (Figma color/tile). */
1135
+ ._tile_5qbaw_24 {
1136
+ position: relative;
1137
+ width: 79px;
1138
+ height: 105px;
1139
+ padding: 0;
1140
+ /* Figma color/tile: 1px dark border on the selected tile, none otherwise. */
1141
+ border: 1px solid transparent;
1142
+ border-radius: var(--radius-8);
1143
+ overflow: hidden;
1144
+ background: var(--color-surface-soft);
1145
+ cursor: pointer;
1146
+ }
1147
+
1148
+ ._selected_5qbaw_44 {
1149
+ border-color: var(--color-surface-max);
1150
+ }
1151
+
1152
+ ._image_5qbaw_48 {
1153
+ object-fit: cover;
1154
+ }
1155
+
1156
+ ._placeholder_5qbaw_52 {
1157
+ position: absolute;
1158
+ inset: 0;
1159
+ background: var(--color-surface-soft);
1160
+ }
1161
+
1162
+ /* "N left" low-stock badge (Figma badge/sale/size2): warning tones, top-right of tile. */
1163
+ ._stockBadge_5qbaw_59 {
1164
+ position: absolute;
1165
+ top: var(--space-4);
1166
+ right: var(--space-4);
1167
+ display: inline-flex;
1168
+ align-items: center;
1169
+ height: 20px;
1170
+ padding: var(--space-4);
1171
+ border-radius: var(--radius-4);
1172
+ background: var(--color-state-surface-warning-soft);
1173
+ color: var(--color-state-text-warning);
1174
+ font-size: var(--type-trimmed-xs-size);
1175
+ line-height: var(--type-trimmed-xs-line);
1176
+ font-weight: var(--font-weight-bold);
1177
+ white-space: nowrap;
1178
+ }
1179
+
1180
+ ._soldOut_5qbaw_76 {
1181
+ cursor: not-allowed;
1182
+ }
1183
+
1184
+ ._soldOutLabel_5qbaw_80 {
1185
+ position: absolute;
1186
+ inset: 0;
1187
+ display: flex;
1188
+ align-items: center;
1189
+ justify-content: center;
1190
+ background: rgba(249, 250, 250, 0.72);
1191
+ color: var(--color-text-subtle);
1192
+ font-size: var(--type-trimmed-xs-size);
1193
+ font-weight: var(--font-weight-bold);
1194
+ text-transform: uppercase;
1195
+ }
1196
+ ._fieldset_1mnz1_1 {
1197
+ border: none;
1198
+ margin: 0;
1199
+ padding: 0;
1200
+ min-width: 0;
1201
+ }
1202
+
1203
+ ._legend_1mnz1_8 {
1204
+ font-size: var(--type-trimmed-sm-size);
1205
+ font-weight: var(--font-weight-bold);
1206
+ color: var(--color-text-subtle);
1207
+ margin-bottom: var(--space-8);
1208
+ padding: 0;
1209
+ }
1210
+
1211
+ ._options_1mnz1_16 {
1212
+ display: flex;
1213
+ flex-wrap: wrap;
1214
+ gap: var(--space-8);
1215
+ }
1216
+
1217
+ ._option_1mnz1_16 {
1218
+ min-height: var(--tap-target-min);
1219
+ min-width: var(--tap-target-min);
1220
+ padding: 0 var(--space-16);
1221
+ border-radius: var(--radius-8);
1222
+ border: 1px solid var(--color-border-default);
1223
+ background: var(--color-surface-default);
1224
+ color: var(--color-text-default);
1225
+ font-family: inherit;
1226
+ font-size: var(--type-trimmed-sm-size);
1227
+ font-weight: var(--font-weight-bold);
1228
+ cursor: pointer;
1229
+ }
1230
+
1231
+ ._selected_1mnz1_36 {
1232
+ border-color: var(--color-surface-max);
1233
+ background: var(--color-surface-max);
1234
+ color: var(--color-text-strong);
1235
+ }
1236
+
1237
+ ._disabled_1mnz1_42 {
1238
+ cursor: not-allowed;
1239
+ color: var(--color-text-disabled);
1240
+ text-decoration: line-through;
1241
+ background: var(--color-surface-soft);
1242
+ }
1243
+ ._root_9s1n4_1 {
1244
+ display: flex;
1245
+ flex-direction: column;
1246
+ gap: var(--space-4);
1247
+ }
1248
+
1249
+ ._title_9s1n4_7 {
1250
+ margin: 0 0 var(--space-8);
1251
+ font-size: var(--type-trimmed-md-size);
1252
+ line-height: var(--type-trimmed-md-line);
1253
+ font-weight: var(--font-weight-bold);
1254
+ color: var(--color-text-default);
1255
+ }
1256
+
1257
+ ._body_9s1n4_15 {
1258
+ margin: 0;
1259
+ font-size: var(--type-paragraph-size);
1260
+ line-height: var(--type-paragraph-line);
1261
+ color: var(--color-text-subtle);
1262
+ white-space: pre-line;
1263
+ }
1264
+
1265
+ /* Collapsed: clamp to three lines. */
1266
+ ._clamped_9s1n4_24 {
1267
+ display: -webkit-box;
1268
+ -webkit-line-clamp: 3;
1269
+ -webkit-box-orient: vertical;
1270
+ overflow: hidden;
1271
+ }
1272
+
1273
+ ._toggle_9s1n4_31 {
1274
+ align-self: flex-start;
1275
+ padding: 0;
1276
+ border: none;
1277
+ background: none;
1278
+ color: var(--color-text-subtle);
1279
+ font-family: inherit;
1280
+ font-size: var(--type-paragraph-size);
1281
+ /* Figma: "more"/"Less" is a regular-weight subtle link, not bold. */
1282
+ font-weight: var(--font-weight-regular);
1283
+ cursor: pointer;
1284
+ }
1285
+ ._box_1cf1a_1 {
1286
+ display: flex;
1287
+ flex-direction: column;
1288
+ gap: var(--space-12);
1289
+ padding: var(--space-16);
1290
+ border-radius: var(--radius-8);
1291
+ background: var(--color-surface-soft);
1292
+ }
1293
+
1294
+ ._row_1cf1a_10 {
1295
+ display: flex;
1296
+ align-items: center;
1297
+ gap: var(--space-12);
1298
+ }
1299
+
1300
+ ._icon_1cf1a_16 {
1301
+ display: block;
1302
+ flex-shrink: 0;
1303
+ color: var(--color-text-default);
1304
+ }
1305
+
1306
+ ._label_1cf1a_22 {
1307
+ margin: 0;
1308
+ font-size: var(--type-trimmed-sm-size);
1309
+ font-weight: var(--font-weight-bold);
1310
+ color: var(--color-text-default);
1311
+ }
1312
+
1313
+ ._divider_1cf1a_29 {
1314
+ height: 1px;
1315
+ background: var(--color-border-default);
1316
+ }
1317
+ ._row_l4edh_1 {
1318
+ display: flex;
1319
+ flex-wrap: wrap;
1320
+ gap: var(--space-4) var(--space-6);
1321
+ margin: 0;
1322
+ padding: 0;
1323
+ list-style: none;
1324
+ }
1325
+
1326
+ /* mobile/badge/top-pick: soft surface, dark text, uppercase, radius-min. */
1327
+ ._badge_l4edh_11 {
1328
+ display: inline-flex;
1329
+ align-items: center;
1330
+ justify-content: center;
1331
+ height: 20px;
1332
+ padding: var(--space-4) var(--space-6);
1333
+ border-radius: var(--radius-2);
1334
+ background: var(--color-surface-soft);
1335
+ color: var(--color-text-default);
1336
+ font-size: var(--type-trimmed-xs-size);
1337
+ line-height: var(--type-trimmed-xs-line);
1338
+ font-weight: var(--font-weight-bold);
1339
+ letter-spacing: 0.12px;
1340
+ text-transform: uppercase;
1341
+ white-space: nowrap;
1342
+ }
1343
+ /* Figma mobile/pdp/promo: success-soft surface, success text, radius-8, p16, h48. */
1344
+ ._banner_1xvw4_2 {
1345
+ display: flex;
1346
+ align-items: center;
1347
+ justify-content: space-between;
1348
+ gap: var(--space-12);
1349
+ min-height: 48px;
1350
+ padding: var(--space-16);
1351
+ border-radius: var(--radius-8);
1352
+ background: var(--color-state-surface-success-soft);
1353
+ color: var(--color-state-text-success);
1354
+ }
1355
+
1356
+ ._message_1xvw4_14 {
1357
+ display: inline-flex;
1358
+ align-items: center;
1359
+ gap: var(--space-6);
1360
+ font-size: var(--type-trimmed-sm-size);
1361
+ font-weight: var(--font-weight-bold);
1362
+ }
1363
+
1364
+ ._icon_1xvw4_22 {
1365
+ display: block;
1366
+ flex-shrink: 0;
1367
+ }
1368
+
1369
+ ._timer_1xvw4_27 {
1370
+ display: inline-flex;
1371
+ align-items: center;
1372
+ gap: var(--space-4);
1373
+ flex-shrink: 0;
1374
+ font-size: var(--type-trimmed-sm-size);
1375
+ font-weight: var(--font-weight-bold);
1376
+ font-variant-numeric: tabular-nums;
1377
+ }
1378
+ ._bar_xe5ej_1 {
1379
+ position: sticky;
1380
+ bottom: 0;
1381
+ left: 0;
1382
+ right: 0;
1383
+ display: flex;
1384
+ flex-direction: column;
1385
+ gap: var(--space-6);
1386
+ /* Figma mobile/pdp/cta-bar: rounded top, drop-shadow (no border), pt-16 / pb-24. */
1387
+ padding: var(--space-16) var(--space-16) calc(var(--space-24) + var(--safe-area-bottom));
1388
+ background: var(--color-surface-default);
1389
+ border-radius: var(--radius-12) var(--radius-12) 0 0;
1390
+ box-shadow: 0 -4px 12px rgba(206, 206, 206, 0.48);
1391
+ z-index: 50;
1392
+ }
1393
+
1394
+ ._info_xe5ej_17 {
1395
+ display: flex;
1396
+ align-items: center;
1397
+ justify-content: space-between;
1398
+ font-weight: var(--font-weight-bold);
1399
+ }
1400
+
1401
+ ._actions_xe5ej_24 {
1402
+ display: flex;
1403
+ gap: var(--space-8);
1404
+ }
1405
+
1406
+ ._actions_xe5ej_24 > * {
1407
+ flex: 1 1 0;
1408
+ }
1409
+ ._page_pdsy4_1 {
1410
+ display: flex;
1411
+ flex-direction: column;
1412
+ gap: var(--space-16);
1413
+ /* No bottom padding to clear the CTA bar, and that is deliberate.
1414
+ `StickyCtaBar` is `position: sticky`, not `fixed`, so it stays in normal flow and
1415
+ already reserves its own height at the end of this column. Padding here does not
1416
+ clear anything — it adds an empty band *below* the bar that is only visible once the
1417
+ fan scrolls to the very end, which is exactly where it looks like a rendering fault.
1418
+ The bar carries its own `--safe-area-bottom` in its padding. */
1419
+ }
1420
+
1421
+ /* Sections below the full-bleed image, padded and spaced per the PDP content block. */
1422
+ ._content_pdsy4_14 {
1423
+ display: flex;
1424
+ flex-direction: column;
1425
+ gap: var(--space-32);
1426
+ padding: 0 var(--space-16);
1427
+ }
1428
+
1429
+ ._intro_pdsy4_21 {
1430
+ display: flex;
1431
+ flex-direction: column;
1432
+ gap: var(--space-8);
1433
+ }
1434
+
1435
+ /* Figma product-info: title+price group and the promo banner are spaced 16px apart. */
1436
+ ._productInfo_pdsy4_28 {
1437
+ display: flex;
1438
+ flex-direction: column;
1439
+ gap: var(--space-16);
1440
+ }
1441
+
1442
+ ._titlePrice_pdsy4_34 {
1443
+ display: flex;
1444
+ flex-direction: column;
1445
+ gap: var(--space-8);
1446
+ }
1447
+
1448
+ ._title_pdsy4_34 {
1449
+ margin: 0;
1450
+ font-size: var(--type-heading-lg-size);
1451
+ line-height: 1.1;
1452
+ font-weight: var(--font-weight-bold);
1453
+ color: var(--color-text-default);
1454
+ /* Reserve two lines and truncate, keeping the price position stable. */
1455
+ display: -webkit-box;
1456
+ -webkit-line-clamp: 2;
1457
+ -webkit-box-orient: vertical;
1458
+ overflow: hidden;
1459
+ }
1460
+
1461
+ /* Stock notice — shown ONLY when low or out of stock (Figma has no "in stock" line). */
1462
+ ._availability_pdsy4_54 {
1463
+ margin: 0;
1464
+ font-size: var(--type-trimmed-sm-size);
1465
+ font-weight: var(--font-weight-bold);
1466
+ }
1467
+
1468
+ ._low_pdsy4_60 {
1469
+ color: var(--color-state-text-warning);
1470
+ }
1471
+
1472
+ ._oos_pdsy4_64 {
1473
+ color: var(--color-state-surface-critical-default);
1474
+ }
1475
+ ._hero_czprq_1 {
1476
+ display: flex;
1477
+ flex-direction: column;
1478
+ gap: var(--space-16);
1479
+ }
1480
+
1481
+ ._text_czprq_7 {
1482
+ display: flex;
1483
+ flex-direction: column;
1484
+ align-items: center;
1485
+ gap: var(--space-4);
1486
+ padding: var(--space-24) var(--space-16) 0;
1487
+ text-align: center;
1488
+ }
1489
+
1490
+ ._title_czprq_16 {
1491
+ font-size: var(--type-heading-lg-size);
1492
+ line-height: var(--type-heading-lg-line);
1493
+ font-weight: var(--font-weight-bold);
1494
+ color: var(--color-text-default);
1495
+ }
1496
+
1497
+ ._subtitle_czprq_23 {
1498
+ margin: 0;
1499
+ font-size: var(--type-trimmed-sm-size);
1500
+ color: var(--color-text-subtle);
1501
+ }
1502
+
1503
+ ._image_czprq_29 {
1504
+ position: relative;
1505
+ width: 100%;
1506
+ aspect-ratio: 375 / 360;
1507
+ }
1508
+
1509
+ ._image_czprq_29 img {
1510
+ object-fit: cover;
1511
+ }
1512
+ ._text_g7ajl_1 {
1513
+ margin: 0;
1514
+ padding: var(--space-8) var(--space-16);
1515
+ font-size: var(--type-heading-md-size);
1516
+ line-height: var(--type-heading-md-line);
1517
+ font-weight: var(--font-weight-bold);
1518
+ color: var(--color-text-default);
1519
+ }
1520
+
1521
+ ._center_g7ajl_10 {
1522
+ text-align: center;
1523
+ }
1524
+
1525
+ ._left_g7ajl_14 {
1526
+ text-align: left;
1527
+ }
1528
+ ._wrap_88jzo_1 {
1529
+ padding: 0 var(--space-16);
1530
+ }
1531
+
1532
+ ._box_88jzo_5 {
1533
+ position: relative;
1534
+ width: 100%;
1535
+ aspect-ratio: 343 / 420;
1536
+ border-radius: var(--radius-12);
1537
+ overflow: hidden;
1538
+ background: var(--color-surface-soft);
1539
+ }
1540
+
1541
+ ._image_88jzo_14 {
1542
+ object-fit: cover;
1543
+ }
1544
+ ._section_yfu87_1 {
1545
+ display: flex;
1546
+ flex-direction: column;
1547
+ gap: var(--space-12);
1548
+ }
1549
+
1550
+ ._track_yfu87_7 {
1551
+ display: flex;
1552
+ gap: var(--space-12);
1553
+ list-style: none;
1554
+ margin: 0;
1555
+ padding: 0 var(--space-16);
1556
+ overflow-x: auto;
1557
+ scroll-snap-type: x mandatory;
1558
+ scroll-padding-inline: var(--space-16);
1559
+ -webkit-overflow-scrolling: touch;
1560
+ }
1561
+
1562
+ /* Light card island — fixed colours so it stays light on the dark PCP. */
1563
+ ._card_yfu87_20 {
1564
+ flex: 0 0 auto;
1565
+ width: calc(100% - var(--space-32));
1566
+ scroll-snap-align: start;
1567
+ display: flex;
1568
+ flex-direction: column;
1569
+ gap: var(--space-12);
1570
+ padding: var(--space-24);
1571
+ border-radius: var(--radius-12);
1572
+ background: #f9fafa;
1573
+ }
1574
+
1575
+ ._quote_yfu87_32 {
1576
+ margin: 0;
1577
+ font-size: var(--type-heading-md-size);
1578
+ line-height: 1.25;
1579
+ font-weight: var(--font-weight-bold);
1580
+ color: #0c161c;
1581
+ }
1582
+
1583
+ ._author_yfu87_40 {
1584
+ margin: 0;
1585
+ font-size: var(--type-trimmed-sm-size);
1586
+ color: #3d4549;
1587
+ }
1588
+
1589
+ ._dots_yfu87_46 {
1590
+ display: flex;
1591
+ justify-content: center;
1592
+ gap: var(--space-6);
1593
+ }
1594
+
1595
+ ._dot_yfu87_46 {
1596
+ width: 8px;
1597
+ height: 8px;
1598
+ border-radius: var(--radius-full);
1599
+ background: var(--color-border-default);
1600
+ }
1601
+
1602
+ ._dotActive_yfu87_59 {
1603
+ background: var(--color-text-default);
1604
+ }
1605
+ ._section_ubkar_1 {
1606
+ display: flex;
1607
+ flex-direction: column;
1608
+ gap: var(--space-24);
1609
+ padding: var(--space-24) var(--space-16);
1610
+ }
1611
+
1612
+ ._row_ubkar_8 {
1613
+ display: flex;
1614
+ align-items: flex-start;
1615
+ gap: var(--space-12);
1616
+ }
1617
+
1618
+ ._icon_ubkar_14 {
1619
+ flex-shrink: 0;
1620
+ color: var(--color-text-default);
1621
+ }
1622
+
1623
+ ._textCol_ubkar_19 {
1624
+ display: flex;
1625
+ flex-direction: column;
1626
+ gap: var(--space-4);
1627
+ }
1628
+
1629
+ ._title_ubkar_25 {
1630
+ margin: 0;
1631
+ font-size: var(--type-trimmed-sm-size);
1632
+ font-weight: var(--font-weight-bold);
1633
+ color: var(--color-text-default);
1634
+ }
1635
+
1636
+ ._body_ubkar_32 {
1637
+ margin: 0;
1638
+ font-size: var(--type-trimmed-sm-size);
1639
+ color: var(--color-text-subtle);
1640
+ }
1641
+ ._section_3mufa_1 {
1642
+ display: flex;
1643
+ flex-direction: column;
1644
+ gap: var(--space-8);
1645
+ padding: var(--space-16);
1646
+ }
1647
+
1648
+ ._title_3mufa_8 {
1649
+ font-size: var(--type-heading-md-size);
1650
+ line-height: var(--type-heading-md-line);
1651
+ font-weight: var(--font-weight-bold);
1652
+ color: var(--color-text-default);
1653
+ }
1654
+
1655
+ ._list_3mufa_15 {
1656
+ list-style: none;
1657
+ margin: 0;
1658
+ padding: 0;
1659
+ }
1660
+
1661
+ ._row_3mufa_21 {
1662
+ display: flex;
1663
+ align-items: center;
1664
+ justify-content: space-between;
1665
+ min-height: var(--tap-target-min);
1666
+ padding: var(--space-12) 0;
1667
+ border-bottom: 1px solid var(--color-border-default);
1668
+ text-decoration: none;
1669
+ color: var(--color-text-default);
1670
+ font-size: var(--type-trimmed-md-size);
1671
+ font-weight: var(--font-weight-bold);
1672
+ }
1673
+
1674
+ ._row_3mufa_21 svg {
1675
+ color: var(--color-text-subtle);
1676
+ }
1677
+ /* 4.2 Category (PCP) page layout. Moved here from the Next app's route stylesheet so the
1678
+ SDK and the app render the same screen from the same rules — see CategoryContent.tsx. */
1679
+
1680
+ /* Background and base typography come from ShopSurface, which this class is applied
1681
+ alongside — one owner for the surface, one for the layout. */
1682
+ ._pcp_pi5vp_6 {
1683
+ display: flex;
1684
+ flex-direction: column;
1685
+ gap: var(--space-32);
1686
+ padding-bottom: var(--space-32);
1687
+ /* Filling the screen now belongs to `ShopSurface`'s `fill`, which every screen sets and
1688
+ which uses `100dvh`. This rule used to be `min-height: 100%` here, with a comment
1689
+ arguing against viewport units precisely because a host may render chrome around us
1690
+ and a viewport unit overshoots by its height.
1691
+
1692
+ That argument is right, and it lost to a worse failure. `100%` resolves against the
1693
+ parent, so it does nothing at all unless the host's mount chain has a height — and
1694
+ DAZN's does not, which is why their cart rendered as a short white card on black.
1695
+ Overshooting by the height of a header costs a few pixels of scroll on a short page.
1696
+ Not filling costs a screen that looks broken. Hosts that mount a view inside their
1697
+ own sized container can still say `fill={false}`. */
1698
+ }
1699
+
1700
+ ._featured_pi5vp_24 {
1701
+ padding: 0 var(--space-16);
1702
+ }
1703
+
1704
+ ._gridBlock_pi5vp_28 {
1705
+ display: flex;
1706
+ flex-direction: column;
1707
+ gap: var(--space-12);
1708
+ }
1709
+
1710
+ ._gridTitle_pi5vp_34 {
1711
+ padding: 0 var(--space-16);
1712
+ font-size: var(--type-heading-md-size);
1713
+ line-height: var(--type-heading-md-line);
1714
+ font-weight: var(--font-weight-bold);
1715
+ color: var(--color-text-default);
1716
+ }
1717
+
1718
+ ._grid_pi5vp_28 {
1719
+ list-style: none;
1720
+ margin: 0;
1721
+ padding: 0 var(--space-16);
1722
+ display: grid;
1723
+ grid-template-columns: repeat(2, 1fr);
1724
+ gap: var(--space-16);
1725
+ }
1726
+ /*
1727
+ * Figma "homepage v2" (node 2795:98786): a full-screen, snap-scrolling editorial
1728
+ * showcase. Each panel is a full-bleed photo with a bottom overlay (title + subtitle +
1729
+ * outline CTA). Always dark (#080e12), independent of theme. Measurements are taken
1730
+ * from Figma: overlay pad 24, gap 16, title 32, subtitle 16/1.55, button h48 radius 8.
1731
+ */
1732
+ ._showcase_1osag_7 {
1733
+ height: calc(100dvh - 56px); /* below the 56px webview header */
1734
+ overflow-y: auto;
1735
+ scroll-snap-type: y mandatory;
1736
+ -webkit-overflow-scrolling: touch;
1737
+ background: #080e12;
1738
+ scrollbar-width: none;
1739
+ }
1740
+ ._showcase_1osag_7::-webkit-scrollbar {
1741
+ display: none;
1742
+ }
1743
+
1744
+ ._panel_1osag_19 {
1745
+ position: relative;
1746
+ height: calc(100dvh - 56px);
1747
+ scroll-snap-align: start;
1748
+ scroll-snap-stop: always;
1749
+ overflow: hidden;
1750
+ background: #080e12;
1751
+ }
1752
+
1753
+ ._image_1osag_28 {
1754
+ object-fit: cover;
1755
+ object-position: center;
1756
+ }
1757
+
1758
+ /* Bottom scrim so overlay text stays legible over any photo. */
1759
+ ._panel_1osag_19::after {
1760
+ content: '';
1761
+ position: absolute;
1762
+ inset: 0;
1763
+ background: linear-gradient(to bottom, rgba(8, 14, 18, 0) 45%, rgba(8, 14, 18, 0.85) 100%);
1764
+ pointer-events: none;
1765
+ }
1766
+
1767
+ /* ── Hero ── */
1768
+ /*
1769
+ * Unlike the category panels (`edit_6 1`, full-bleed 375x812), the hero photo in Figma
1770
+ * (`edit_9 1`) is 375x674 and stops where the opaque "swipe down" bar starts. Giving it
1771
+ * its own box keeps the shot at its native aspect instead of cover-cropping the sides.
1772
+ */
1773
+ ._heroImageBox_1osag_48 {
1774
+ position: absolute;
1775
+ inset: 0 0 82px;
1776
+ overflow: hidden;
1777
+ }
1778
+ ._hero_1osag_48 ._heroContent_1osag_53 {
1779
+ position: absolute;
1780
+ bottom: 15px;
1781
+ left: 0;
1782
+ right: 0;
1783
+ z-index: 1;
1784
+ display: flex;
1785
+ flex-direction: column;
1786
+ gap: 24px;
1787
+ align-items: stretch;
1788
+ }
1789
+ ._heroText_1osag_64 {
1790
+ display: flex;
1791
+ flex-direction: column;
1792
+ gap: 8px;
1793
+ align-items: center;
1794
+ padding: 0 24px;
1795
+ text-align: center;
1796
+ }
1797
+ ._logo_1osag_72 {
1798
+ width: 48px;
1799
+ height: 48px;
1800
+ }
1801
+ ._heroTitle_1osag_76 {
1802
+ margin: 0;
1803
+ font-size: 56px;
1804
+ line-height: 1.3;
1805
+ font-weight: 700;
1806
+ letter-spacing: -1.12px;
1807
+ color: #f9fafa;
1808
+ }
1809
+ ._heroSub_1osag_84 {
1810
+ margin: 0;
1811
+ font-size: 16px;
1812
+ line-height: 1.55;
1813
+ color: #f9fafa;
1814
+ max-width: 327px;
1815
+ }
1816
+ ._swipe_1osag_91 {
1817
+ display: flex;
1818
+ flex-direction: column;
1819
+ align-items: center;
1820
+ gap: 6px;
1821
+ height: 82px;
1822
+ justify-content: center;
1823
+ background: #080e12;
1824
+ }
1825
+ ._swipeLabel_1osag_100 {
1826
+ margin: 0;
1827
+ font-size: 14px;
1828
+ line-height: 1;
1829
+ color: rgba(255, 255, 255, 0.54);
1830
+ }
1831
+ ._chevron_1osag_106 {
1832
+ width: 24px;
1833
+ height: 24px;
1834
+ color: rgba(255, 255, 255, 0.54);
1835
+ animation: _bob_1osag_1 1.8s ease-in-out infinite;
1836
+ }
1837
+ @keyframes _bob_1osag_1 {
1838
+ 0%, 100% { transform: translateY(0); }
1839
+ 50% { transform: translateY(4px); }
1840
+ }
1841
+
1842
+ /* ── Category panels ── */
1843
+ ._overlay_1osag_118 {
1844
+ position: absolute;
1845
+ bottom: 0;
1846
+ left: 0;
1847
+ right: 0;
1848
+ z-index: 1;
1849
+ display: flex;
1850
+ flex-direction: column;
1851
+ gap: 16px;
1852
+ align-items: flex-start;
1853
+ padding: 0 24px 24px;
1854
+ }
1855
+ ._copy_1osag_130 {
1856
+ display: flex;
1857
+ flex-direction: column;
1858
+ gap: 8px;
1859
+ }
1860
+ ._title_1osag_135 {
1861
+ margin: 0;
1862
+ font-size: 32px;
1863
+ line-height: 1.2;
1864
+ font-weight: 700;
1865
+ color: #f9fafa;
1866
+ }
1867
+ ._subtitle_1osag_142 {
1868
+ margin: 0;
1869
+ font-size: 16px;
1870
+ line-height: 1.55;
1871
+ color: #f9fafa;
1872
+ }
1873
+ ._cta_1osag_148 {
1874
+ display: inline-flex;
1875
+ align-items: center;
1876
+ justify-content: center;
1877
+ min-height: 48px;
1878
+ padding: 8px 12px;
1879
+ border: 1px solid #f9fafa;
1880
+ border-radius: 8px;
1881
+ background: transparent;
1882
+ color: #f9fafa;
1883
+ font-size: 16px;
1884
+ font-weight: 700;
1885
+ text-decoration: none;
1886
+ }
1887
+ ._cta_1osag_148:active {
1888
+ background: rgba(249, 250, 250, 0.12);
1889
+ }
1890
+
1891
+ /* ── Mosaic gallery panel ── */
1892
+ ._mosaicPanel_1osag_167 {
1893
+ display: flex;
1894
+ align-items: center;
1895
+ justify-content: center;
1896
+ }
1897
+ ._mosaicPanel_1osag_167::after {
1898
+ display: none;
1899
+ }
1900
+ ._mosaic_1osag_167 {
1901
+ display: flex;
1902
+ flex-direction: column;
1903
+ gap: 12px;
1904
+ width: 343px;
1905
+ }
1906
+ ._mRow_1osag_181 {
1907
+ display: flex;
1908
+ gap: 12px;
1909
+ align-items: center;
1910
+ }
1911
+ ._mBetween_1osag_186 {
1912
+ justify-content: space-between;
1913
+ }
1914
+ ._mCol_1osag_189 {
1915
+ display: flex;
1916
+ flex-direction: column;
1917
+ gap: 10px;
1918
+ }
1919
+ ._tile_1osag_194 {
1920
+ position: relative;
1921
+ border-radius: 6px;
1922
+ overflow: hidden;
1923
+ flex-shrink: 0;
1924
+ }
1925
+ ._solid_1osag_200 {
1926
+ background: #171e23;
1927
+ }
1928
+ ._overlay_s4eai_1 {
1929
+ position: fixed;
1930
+ inset: 0;
1931
+ z-index: 900;
1932
+ display: flex;
1933
+ align-items: flex-end;
1934
+ justify-content: center;
1935
+ }
1936
+
1937
+ ._scrim_s4eai_10 {
1938
+ position: absolute;
1939
+ inset: 0;
1940
+ background: rgba(8, 14, 18, 0.5);
1941
+ }
1942
+
1943
+ ._sheet_s4eai_16 {
1944
+ position: relative;
1945
+ width: 100%;
1946
+ max-width: 480px;
1947
+ max-height: 90dvh;
1948
+ background: var(--color-surface-default);
1949
+ border-radius: var(--radius-18) var(--radius-18) 0 0;
1950
+ display: flex;
1951
+ flex-direction: column;
1952
+ animation: _slide-up_s4eai_1 200ms ease;
1953
+ padding-bottom: var(--safe-area-bottom);
1954
+ }
1955
+
1956
+ @keyframes _slide-up_s4eai_1 {
1957
+ from {
1958
+ transform: translateY(100%);
1959
+ }
1960
+ to {
1961
+ transform: translateY(0);
1962
+ }
1963
+ }
1964
+
1965
+ /* Drag handle strip. `touch-action: none` stops the browser claiming the vertical
1966
+ gesture as a page scroll before the pointer handlers see it. Confined to this strip
1967
+ so drags starting on the scrollable body still scroll the body. */
1968
+ ._grabArea_s4eai_41 {
1969
+ padding: var(--space-8) 0 var(--space-4);
1970
+ touch-action: none;
1971
+ cursor: grab;
1972
+ }
1973
+
1974
+ ._grabArea_s4eai_41:active {
1975
+ cursor: grabbing;
1976
+ }
1977
+
1978
+ ._grabber_s4eai_51 {
1979
+ width: 40px;
1980
+ height: 4px;
1981
+ margin: 0 auto;
1982
+ border-radius: 2px;
1983
+ background: var(--color-border-default);
1984
+ }
1985
+
1986
+ ._header_s4eai_59 {
1987
+ display: flex;
1988
+ align-items: center;
1989
+ justify-content: space-between;
1990
+ padding: var(--space-16);
1991
+ border-bottom: 1px solid var(--color-border-default);
1992
+ }
1993
+
1994
+ ._title_s4eai_67 {
1995
+ font-size: var(--type-heading-md-size);
1996
+ }
1997
+
1998
+ ._close_s4eai_71 {
1999
+ min-width: var(--tap-target-min);
2000
+ min-height: var(--tap-target-min);
2001
+ display: inline-flex;
2002
+ align-items: center;
2003
+ justify-content: center;
2004
+ border: none;
2005
+ background: transparent;
2006
+ font-size: var(--type-trimmed-md-size);
2007
+ cursor: pointer;
2008
+ color: var(--color-text-subtle);
2009
+ }
2010
+
2011
+ ._body_s4eai_84 {
2012
+ overflow-y: auto;
2013
+ padding: var(--space-16);
2014
+ flex: 1 1 auto;
2015
+ }
2016
+
2017
+ ._footer_s4eai_90 {
2018
+ border-top: 1px solid var(--color-border-default);
2019
+ padding: var(--space-16);
2020
+ }
2021
+ ._wrap_v9onk_1 {
2022
+ display: inline-flex;
2023
+ flex-direction: column;
2024
+ align-items: flex-start;
2025
+ gap: var(--space-4);
2026
+ }
2027
+
2028
+ ._stepper_v9onk_8 {
2029
+ display: inline-flex;
2030
+ align-items: center;
2031
+ gap: var(--space-4);
2032
+ border: 1px solid var(--color-border-default);
2033
+ border-radius: var(--radius-full);
2034
+ padding: var(--space-2);
2035
+ }
2036
+
2037
+ /* Why the increment stopped. Subtle, but present — never a silently dead control. */
2038
+ ._cap_v9onk_18 {
2039
+ font-size: var(--type-trimmed-xs-size, 12px);
2040
+ font-weight: var(--font-weight-regular, 400);
2041
+ color: var(--color-text-subtle);
2042
+ }
2043
+
2044
+ ._button_v9onk_24 {
2045
+ min-width: var(--tap-target-min);
2046
+ min-height: var(--tap-target-min);
2047
+ /* Centres the icon in the 44px target — without this the svg sits on the text
2048
+ baseline and the row reads as misaligned. */
2049
+ display: inline-flex;
2050
+ align-items: center;
2051
+ justify-content: center;
2052
+ border: none;
2053
+ background: transparent;
2054
+ border-radius: var(--radius-full);
2055
+ font-size: var(--type-trimmed-md-size);
2056
+ font-weight: var(--font-weight-bold);
2057
+ color: var(--color-text-default);
2058
+ cursor: pointer;
2059
+ }
2060
+
2061
+ ._button_v9onk_24:disabled {
2062
+ color: var(--color-text-disabled);
2063
+ cursor: not-allowed;
2064
+ }
2065
+
2066
+ ._value_v9onk_46 {
2067
+ min-width: 2ch;
2068
+ text-align: center;
2069
+ font-size: var(--type-trimmed-md-size);
2070
+ font-weight: var(--font-weight-bold);
2071
+ }
2072
+ ._row_stxj2_1 {
2073
+ display: grid;
2074
+ grid-template-columns: 72px 1fr auto;
2075
+ gap: var(--space-12);
2076
+ align-items: start;
2077
+ padding: var(--space-12) 0;
2078
+ border-bottom: 1px solid var(--color-border-default);
2079
+ }
2080
+
2081
+ ._pending_stxj2_10 {
2082
+ opacity: 0.6;
2083
+ }
2084
+
2085
+ ._media_stxj2_14 {
2086
+ position: relative;
2087
+ width: 72px;
2088
+ height: 72px;
2089
+ border-radius: var(--radius-8);
2090
+ overflow: hidden;
2091
+ background: var(--color-surface-soft);
2092
+ }
2093
+
2094
+ ._image_stxj2_23 {
2095
+ object-fit: cover;
2096
+ }
2097
+
2098
+ ._placeholder_stxj2_27 {
2099
+ width: 100%;
2100
+ height: 100%;
2101
+ }
2102
+
2103
+ ._info_stxj2_32 {
2104
+ display: flex;
2105
+ flex-direction: column;
2106
+ gap: var(--space-8);
2107
+ min-width: 0;
2108
+ }
2109
+
2110
+ ._text_stxj2_39 {
2111
+ display: flex;
2112
+ flex-direction: column;
2113
+ gap: var(--space-4);
2114
+ min-width: 0;
2115
+ }
2116
+
2117
+ ._title_stxj2_46 {
2118
+ font-size: var(--type-trimmed-sm-size);
2119
+ font-weight: var(--font-weight-bold);
2120
+ /* Two lines, then ellipsis. Product titles are catalogue copy and can run long; an
2121
+ unbounded title pushes the stepper and the line total out of alignment down the
2122
+ list. */
2123
+ display: -webkit-box;
2124
+ -webkit-line-clamp: 2;
2125
+ -webkit-box-orient: vertical;
2126
+ overflow: hidden;
2127
+ }
2128
+
2129
+ /* "Black · M" — which variant this line is. Without it two sizes of one product are
2130
+ the same line twice. */
2131
+ ._variant_stxj2_60 {
2132
+ font-size: var(--type-trimmed-xs-size);
2133
+ color: var(--color-text-subtle);
2134
+ }
2135
+
2136
+ ._unit_stxj2_65 {
2137
+ font-size: var(--type-trimmed-xs-size);
2138
+ color: var(--color-text-subtle);
2139
+ }
2140
+
2141
+ ._lineTotal_stxj2_70 {
2142
+ font-size: var(--type-trimmed-md-size);
2143
+ font-weight: var(--font-weight-bold);
2144
+ white-space: nowrap;
2145
+ }
2146
+ ._summary_dmv1d_1 {
2147
+ margin: 0;
2148
+ display: flex;
2149
+ flex-direction: column;
2150
+ gap: var(--space-8);
2151
+ }
2152
+
2153
+ ._row_dmv1d_8 {
2154
+ display: flex;
2155
+ align-items: baseline;
2156
+ justify-content: space-between;
2157
+ font-size: var(--type-trimmed-sm-size);
2158
+ color: var(--color-text-subtle);
2159
+ }
2160
+
2161
+ ._row_dmv1d_8 dt,
2162
+ ._row_dmv1d_8 dd {
2163
+ margin: 0;
2164
+ }
2165
+
2166
+ ._total_dmv1d_21 {
2167
+ padding-top: var(--space-8);
2168
+ border-top: 1px solid var(--color-border-default);
2169
+ font-size: var(--type-trimmed-md-size);
2170
+ font-weight: var(--font-weight-bold);
2171
+ color: var(--color-text-default);
2172
+ }
2173
+
2174
+ /* "Free" is a merchandising signal, not a number — success tone, per the design. */
2175
+ ._free_dmv1d_30 {
2176
+ color: var(--color-state-text-success);
2177
+ font-weight: var(--font-weight-bold);
2178
+ }
2179
+
2180
+ /* "Calculated at checkout". Set quieter than a real figure so it never reads as one. */
2181
+ ._pending_dmv1d_36 {
2182
+ font-size: var(--type-trimmed-xs-size);
2183
+ color: var(--color-text-subtle);
2184
+ }
2185
+ /*
2186
+ * Shown while the cart is locked for checkout (contracts 2.11.0 `Cart.locked`).
2187
+ *
2188
+ * Warning tones rather than critical: nothing has gone wrong, the fan simply cannot edit
2189
+ * right now. Critical red here would read as a failure and push people to abandon a
2190
+ * checkout that is working.
2191
+ */
2192
+ ._lockNotice_1f0ae_8 {
2193
+ margin: 0;
2194
+ padding: var(--space-12) var(--space-16);
2195
+ background: var(--color-state-surface-warning-soft);
2196
+ color: var(--color-text-default);
2197
+ border-bottom: 1px solid var(--color-border-subtle);
2198
+ font-size: 14px;
2199
+ line-height: 1.4;
2200
+ }
2201
+ /* Figma mobile/header top.navigation (context=shop): 56px bar, 16px inset, 24px icons. */
2202
+ ._header_nbe8a_2 {
2203
+ display: flex;
2204
+ align-items: center;
2205
+ justify-content: space-between;
2206
+ height: 56px;
2207
+ padding: 0 var(--space-16);
2208
+ background: var(--color-surface-default);
2209
+ }
2210
+
2211
+ ._right_nbe8a_11 {
2212
+ display: flex;
2213
+ align-items: center;
2214
+ gap: var(--space-16);
2215
+ }
2216
+
2217
+ /*
2218
+ * Holds the left slot open when there is no back control.
2219
+ *
2220
+ * `justify-content: space-between` with one child pushes the icons to the left edge, so a
2221
+ * header without a back button would silently re-lay-out rather than simply losing one
2222
+ * icon. 24px is the icon box it stands in for.
2223
+ */
2224
+ ._spacer_nbe8a_24 {
2225
+ width: 24px;
2226
+ height: 24px;
2227
+ }
2228
+
2229
+ ._iconBtn_nbe8a_29 {
2230
+ position: relative;
2231
+ display: inline-flex;
2232
+ align-items: center;
2233
+ justify-content: center;
2234
+ width: 24px;
2235
+ height: 24px;
2236
+ padding: 0;
2237
+ border: none;
2238
+ background: none;
2239
+ color: var(--color-text-default);
2240
+ cursor: pointer;
2241
+ text-decoration: none;
2242
+ }
2243
+
2244
+ /* Expand the hit area to >=44px without moving the 24px icon off the 16px inset. */
2245
+ ._iconBtn_nbe8a_29::after {
2246
+ content: '';
2247
+ position: absolute;
2248
+ inset: -10px;
2249
+ }
2250
+
2251
+ /*
2252
+ * Cart count. Anchored to the icon's top-right corner and allowed to grow leftwards for
2253
+ * a two- or three-character value ("99+"), which a fixed-width circle would clip.
2254
+ *
2255
+ * Sits above the ::after hit area so the number is never painted underneath it.
2256
+ */
2257
+ ._badge_nbe8a_57 {
2258
+ position: absolute;
2259
+ top: -4px;
2260
+ left: 50%;
2261
+ z-index: 1;
2262
+ min-width: 16px;
2263
+ height: 16px;
2264
+ padding: 0 4px;
2265
+ box-sizing: border-box;
2266
+ display: inline-flex;
2267
+ align-items: center;
2268
+ justify-content: center;
2269
+ border-radius: var(--radius-full);
2270
+ background: var(--color-state-surface-critical-default);
2271
+ color: #ffffff;
2272
+ font-size: 10px;
2273
+ line-height: 1;
2274
+ font-weight: var(--font-weight-bold);
2275
+ font-variant-numeric: tabular-nums;
2276
+ pointer-events: none;
2277
+ }