@nok-integration/storefront-react 0.18.0 → 0.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3580,9 +3580,14 @@ export declare interface OrderConfirmationViewProps {
3580
3580
  /**
3581
3581
  * The frame's "Done". Omit it and no button renders.
3582
3582
  *
3583
- * Where it goes is the host's: in the embed this is the end of a checkout DAZN own, so
3584
- * only they know whether it closes the webview, returns to the shop, or goes somewhere
3585
- * else entirely.
3583
+ * **It leaves the shop.** DAZN settled this on 21 Aug 2026: "Done = DAZN home, X = DAZN
3584
+ * Shop home". The two are not the same destination and wiring both to the shop home
3585
+ * which is what the first integration did — loses the only way out of the shop at the
3586
+ * end of a purchase.
3587
+ *
3588
+ * We cannot make that trip ourselves: the DAZN app's own home is not a route on our
3589
+ * `/v1` surface and not something a `navigate('/')` can reach. So this stays the host's
3590
+ * callback, and the header's `onClose` is the one that goes to the shop home.
3586
3591
  */
3587
3592
  onDone?: () => void;
3588
3593
  }
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
- import { useRef, useCallback, useMemo, useEffect, useContext, createContext, forwardRef, useState } from "react";
3
+ import { useContext, useEffect, useState, useRef, useCallback, useMemo, createContext, forwardRef } from "react";
4
4
  var client = {};
5
5
  var catalog = {};
6
6
  var zod = {};
@@ -5542,6 +5542,38 @@ function withAuthorization(init, token) {
5542
5542
  headers.set("authorization", `Bearer ${token}`);
5543
5543
  return { ...init, headers };
5544
5544
  }
5545
+ const ScreenTitleContext = createContext({ title: void 0, claim: () => () => {
5546
+ } });
5547
+ const SuppressedContext = createContext(false);
5548
+ function ScreenTitleProvider({ children }) {
5549
+ const [title2, setTitle] = useState(void 0);
5550
+ const held = useRef(void 0);
5551
+ const claim = useCallback((next) => {
5552
+ held.current = next;
5553
+ setTitle(next);
5554
+ return () => {
5555
+ if (held.current !== next) return;
5556
+ held.current = void 0;
5557
+ setTitle(void 0);
5558
+ };
5559
+ }, []);
5560
+ const value2 = useMemo(() => ({ title: title2, claim }), [title2, claim]);
5561
+ return /* @__PURE__ */ jsx(ScreenTitleContext.Provider, { value: value2, children });
5562
+ }
5563
+ function useScreenTitle() {
5564
+ return useContext(ScreenTitleContext).title;
5565
+ }
5566
+ function useSetScreenTitle(title2) {
5567
+ const { claim } = useContext(ScreenTitleContext);
5568
+ const suppressed = useContext(SuppressedContext);
5569
+ useEffect(() => {
5570
+ if (suppressed || title2 === void 0) return;
5571
+ return claim(title2);
5572
+ }, [claim, title2, suppressed]);
5573
+ }
5574
+ function NoScreenTitle({ children }) {
5575
+ return /* @__PURE__ */ jsx(SuppressedContext.Provider, { value: true, children });
5576
+ }
5545
5577
  const ShopContext = createContext(null);
5546
5578
  function DaznShopProvider({
5547
5579
  children,
@@ -5631,7 +5663,7 @@ function DaznShopProvider({
5631
5663
  }),
5632
5664
  [api, market, theme, notifications, locale, assetBaseUrl, emit, subscribe, navigate]
5633
5665
  );
5634
- return /* @__PURE__ */ jsx(ShopContext.Provider, { value: value2, children });
5666
+ return /* @__PURE__ */ jsx(ShopContext.Provider, { value: value2, children: /* @__PURE__ */ jsx(ScreenTitleProvider, { children }) });
5635
5667
  }
5636
5668
  function useShopOptional() {
5637
5669
  return useContext(ShopContext);
@@ -7542,6 +7574,19 @@ const styles$w = {
7542
7574
  function OrderConfirmationView(props) {
7543
7575
  return /* @__PURE__ */ jsx(ShopSurface, { tone: "light", fill: true, children: /* @__PURE__ */ jsx(OrderConfirmationContent, { ...props }) });
7544
7576
  }
7577
+ const CONFIRMATION_LABELS = {
7578
+ packing: "Order placed",
7579
+ on_its_way: "On the way"
7580
+ };
7581
+ function confirmationProgress(progress2) {
7582
+ return progress2.map((entry) => {
7583
+ if (entry.step === "delivered" && !entry.at) {
7584
+ return { ...entry, label: "Estimated delivery" };
7585
+ }
7586
+ const relabelled = CONFIRMATION_LABELS[entry.step];
7587
+ return relabelled ? { ...entry, label: relabelled } : { ...entry };
7588
+ });
7589
+ }
7545
7590
  function OrderConfirmationContent({ orderRef, locale: localeProp, onDone }) {
7546
7591
  const { api } = useShop();
7547
7592
  const locale = useLocale(localeProp);
@@ -7581,7 +7626,7 @@ function OrderConfirmationContent({ orderRef, locale: localeProp, onDone }) {
7581
7626
  /* @__PURE__ */ jsx(
7582
7627
  OrderTimeline,
7583
7628
  {
7584
- progress: order.progress,
7629
+ progress: confirmationProgress(order.progress),
7585
7630
  ...order.estimated_delivery ? { estimatedDelivery: order.estimated_delivery } : {},
7586
7631
  locale
7587
7632
  }
@@ -8454,6 +8499,7 @@ const styles$j = {
8454
8499
  lines
8455
8500
  };
8456
8501
  function CartView(props = {}) {
8502
+ useSetScreenTitle("Cart");
8457
8503
  return /* @__PURE__ */ jsx(ShopSurface, { tone: "light", fill: true, className: styles$j.surface, children: /* @__PURE__ */ jsx(CartViewContent, { ...props }) });
8458
8504
  }
8459
8505
  function CartViewContent({
@@ -8644,7 +8690,7 @@ function CartSheetProvider({ children }) {
8644
8690
  const api = useMemo(() => ({ open, close: close2, isOpen }), [open, close2, isOpen]);
8645
8691
  return /* @__PURE__ */ jsxs(CartSheetContext.Provider, { value: api, children: [
8646
8692
  children,
8647
- isOpen && /* @__PURE__ */ jsx(BottomSheet, { open: true, onClose: close2, title: "Cart", children: /* @__PURE__ */ jsx(CartView, { onContinueShopping: close2 }) })
8693
+ isOpen && /* @__PURE__ */ jsx(BottomSheet, { open: true, onClose: close2, title: "Cart", children: /* @__PURE__ */ jsx(NoScreenTitle, { children: /* @__PURE__ */ jsx(CartView, { onContinueShopping: close2 }) }) })
8648
8694
  ] });
8649
8695
  }
8650
8696
  const gallery = "_gallery_10mch_1";
@@ -10005,17 +10051,17 @@ function useCartCount() {
10005
10051
  }, [subscribe]);
10006
10052
  return count2;
10007
10053
  }
10008
- const header$1 = "_header_oq0bd_25";
10009
- const floating = "_floating_oq0bd_44";
10010
- const scrolled = "_scrolled_oq0bd_87";
10011
- const title$1 = "_title_oq0bd_161";
10012
- const right = "_right_oq0bd_174";
10013
- const spacer = "_spacer_oq0bd_187";
10014
- const iconBtn = "_iconBtn_oq0bd_192";
10015
- const menuBtn = "_menuBtn_oq0bd_219";
10016
- const badge = "_badge_oq0bd_247";
10017
- const closeSlot = "_closeSlot_oq0bd_274";
10018
- const centred = "_centred_oq0bd_289";
10054
+ const header$1 = "_header_1yjrk_25";
10055
+ const floating = "_floating_1yjrk_44";
10056
+ const scrolled = "_scrolled_1yjrk_79";
10057
+ const title$1 = "_title_1yjrk_172";
10058
+ const right = "_right_1yjrk_185";
10059
+ const spacer = "_spacer_1yjrk_198";
10060
+ const iconBtn = "_iconBtn_1yjrk_203";
10061
+ const menuBtn = "_menuBtn_1yjrk_230";
10062
+ const badge = "_badge_1yjrk_258";
10063
+ const closeSlot = "_closeSlot_1yjrk_285";
10064
+ const centred = "_centred_1yjrk_300";
10019
10065
  const styles$1 = {
10020
10066
  header: header$1,
10021
10067
  floating,
@@ -10124,6 +10170,8 @@ function ShopBar({
10124
10170
  className
10125
10171
  }) {
10126
10172
  const cartCount = useCartCount();
10173
+ const screenTitle = useScreenTitle();
10174
+ const shownTitle = title2 ?? screenTitle;
10127
10175
  const atTop = useScrolledUnder(floating2 && scrolled2 === void 0);
10128
10176
  const isScrolled = scrolled2 ?? atTop;
10129
10177
  const cartLabel = cartCount === null ? "Cart" : cartCount === 0 ? "Cart, empty" : `Cart, ${cartCount} item${cartCount === 1 ? "" : "s"}`;
@@ -10135,7 +10183,7 @@ function ShopBar({
10135
10183
  ].filter(Boolean).join(" ");
10136
10184
  return /* @__PURE__ */ jsxs("header", { className: classes, children: [
10137
10185
  onBack ? /* @__PURE__ */ jsx("button", { type: "button", className: styles$1.iconBtn, "aria-label": backLabel, onClick: onBack, children: /* @__PURE__ */ jsx(ChevronLeft, {}) }) : /* @__PURE__ */ jsx("span", { className: styles$1.spacer, "aria-hidden": "true" }),
10138
- title2 ? /* @__PURE__ */ jsx("span", { className: styles$1.title, children: title2 }) : null,
10186
+ shownTitle ? /* @__PURE__ */ jsx("span", { className: styles$1.title, children: shownTitle }) : null,
10139
10187
  /* @__PURE__ */ jsxs("div", { className: styles$1.right, children: [
10140
10188
  showCart ? /* @__PURE__ */ jsxs(CartTrigger, { className: styles$1.iconBtn, "aria-label": cartLabel, children: [
10141
10189
  /* @__PURE__ */ jsx(CartIcon, {}),
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
- import { useRef, useCallback, useMemo, useEffect, useContext, createContext, forwardRef, useState } from "react";
3
+ import { useContext, useEffect, useState, useRef, useCallback, useMemo, createContext, forwardRef } from "react";
4
4
  var client = {};
5
5
  var catalog = {};
6
6
  var zod = {};
@@ -5542,6 +5542,38 @@ function withAuthorization(init, token) {
5542
5542
  headers.set("authorization", `Bearer ${token}`);
5543
5543
  return { ...init, headers };
5544
5544
  }
5545
+ const ScreenTitleContext = createContext({ title: void 0, claim: () => () => {
5546
+ } });
5547
+ const SuppressedContext = createContext(false);
5548
+ function ScreenTitleProvider({ children }) {
5549
+ const [title2, setTitle] = useState(void 0);
5550
+ const held = useRef(void 0);
5551
+ const claim = useCallback((next) => {
5552
+ held.current = next;
5553
+ setTitle(next);
5554
+ return () => {
5555
+ if (held.current !== next) return;
5556
+ held.current = void 0;
5557
+ setTitle(void 0);
5558
+ };
5559
+ }, []);
5560
+ const value2 = useMemo(() => ({ title: title2, claim }), [title2, claim]);
5561
+ return /* @__PURE__ */ jsx(ScreenTitleContext.Provider, { value: value2, children });
5562
+ }
5563
+ function useScreenTitle() {
5564
+ return useContext(ScreenTitleContext).title;
5565
+ }
5566
+ function useSetScreenTitle(title2) {
5567
+ const { claim } = useContext(ScreenTitleContext);
5568
+ const suppressed = useContext(SuppressedContext);
5569
+ useEffect(() => {
5570
+ if (suppressed || title2 === void 0) return;
5571
+ return claim(title2);
5572
+ }, [claim, title2, suppressed]);
5573
+ }
5574
+ function NoScreenTitle({ children }) {
5575
+ return /* @__PURE__ */ jsx(SuppressedContext.Provider, { value: true, children });
5576
+ }
5545
5577
  const ShopContext = createContext(null);
5546
5578
  function DaznShopProvider({
5547
5579
  children,
@@ -5631,7 +5663,7 @@ function DaznShopProvider({
5631
5663
  }),
5632
5664
  [api, market, theme, notifications, locale, assetBaseUrl, emit, subscribe, navigate]
5633
5665
  );
5634
- return /* @__PURE__ */ jsx(ShopContext.Provider, { value: value2, children });
5666
+ return /* @__PURE__ */ jsx(ShopContext.Provider, { value: value2, children: /* @__PURE__ */ jsx(ScreenTitleProvider, { children }) });
5635
5667
  }
5636
5668
  function useShopOptional() {
5637
5669
  return useContext(ShopContext);
@@ -7542,6 +7574,19 @@ const styles$w = {
7542
7574
  function OrderConfirmationView(props) {
7543
7575
  return /* @__PURE__ */ jsx(ShopSurface, { tone: "light", fill: true, children: /* @__PURE__ */ jsx(OrderConfirmationContent, { ...props }) });
7544
7576
  }
7577
+ const CONFIRMATION_LABELS = {
7578
+ packing: "Order placed",
7579
+ on_its_way: "On the way"
7580
+ };
7581
+ function confirmationProgress(progress2) {
7582
+ return progress2.map((entry) => {
7583
+ if (entry.step === "delivered" && !entry.at) {
7584
+ return { ...entry, label: "Estimated delivery" };
7585
+ }
7586
+ const relabelled = CONFIRMATION_LABELS[entry.step];
7587
+ return relabelled ? { ...entry, label: relabelled } : { ...entry };
7588
+ });
7589
+ }
7545
7590
  function OrderConfirmationContent({ orderRef, locale: localeProp, onDone }) {
7546
7591
  const { api } = useShop();
7547
7592
  const locale = useLocale(localeProp);
@@ -7581,7 +7626,7 @@ function OrderConfirmationContent({ orderRef, locale: localeProp, onDone }) {
7581
7626
  /* @__PURE__ */ jsx(
7582
7627
  OrderTimeline,
7583
7628
  {
7584
- progress: order.progress,
7629
+ progress: confirmationProgress(order.progress),
7585
7630
  ...order.estimated_delivery ? { estimatedDelivery: order.estimated_delivery } : {},
7586
7631
  locale
7587
7632
  }
@@ -8454,6 +8499,7 @@ const styles$j = {
8454
8499
  lines
8455
8500
  };
8456
8501
  function CartView(props = {}) {
8502
+ useSetScreenTitle("Cart");
8457
8503
  return /* @__PURE__ */ jsx(ShopSurface, { tone: "light", fill: true, className: styles$j.surface, children: /* @__PURE__ */ jsx(CartViewContent, { ...props }) });
8458
8504
  }
8459
8505
  function CartViewContent({
@@ -8644,7 +8690,7 @@ function CartSheetProvider({ children }) {
8644
8690
  const api = useMemo(() => ({ open, close: close2, isOpen }), [open, close2, isOpen]);
8645
8691
  return /* @__PURE__ */ jsxs(CartSheetContext.Provider, { value: api, children: [
8646
8692
  children,
8647
- isOpen && /* @__PURE__ */ jsx(BottomSheet, { open: true, onClose: close2, title: "Cart", children: /* @__PURE__ */ jsx(CartView, { onContinueShopping: close2 }) })
8693
+ isOpen && /* @__PURE__ */ jsx(BottomSheet, { open: true, onClose: close2, title: "Cart", children: /* @__PURE__ */ jsx(NoScreenTitle, { children: /* @__PURE__ */ jsx(CartView, { onContinueShopping: close2 }) }) })
8648
8694
  ] });
8649
8695
  }
8650
8696
  const gallery = "_gallery_10mch_1";
@@ -10005,17 +10051,17 @@ function useCartCount() {
10005
10051
  }, [subscribe]);
10006
10052
  return count2;
10007
10053
  }
10008
- const header$1 = "_header_oq0bd_25";
10009
- const floating = "_floating_oq0bd_44";
10010
- const scrolled = "_scrolled_oq0bd_87";
10011
- const title$1 = "_title_oq0bd_161";
10012
- const right = "_right_oq0bd_174";
10013
- const spacer = "_spacer_oq0bd_187";
10014
- const iconBtn = "_iconBtn_oq0bd_192";
10015
- const menuBtn = "_menuBtn_oq0bd_219";
10016
- const badge = "_badge_oq0bd_247";
10017
- const closeSlot = "_closeSlot_oq0bd_274";
10018
- const centred = "_centred_oq0bd_289";
10054
+ const header$1 = "_header_1yjrk_25";
10055
+ const floating = "_floating_1yjrk_44";
10056
+ const scrolled = "_scrolled_1yjrk_79";
10057
+ const title$1 = "_title_1yjrk_172";
10058
+ const right = "_right_1yjrk_185";
10059
+ const spacer = "_spacer_1yjrk_198";
10060
+ const iconBtn = "_iconBtn_1yjrk_203";
10061
+ const menuBtn = "_menuBtn_1yjrk_230";
10062
+ const badge = "_badge_1yjrk_258";
10063
+ const closeSlot = "_closeSlot_1yjrk_285";
10064
+ const centred = "_centred_1yjrk_300";
10019
10065
  const styles$1 = {
10020
10066
  header: header$1,
10021
10067
  floating,
@@ -10124,6 +10170,8 @@ function ShopBar({
10124
10170
  className
10125
10171
  }) {
10126
10172
  const cartCount = useCartCount();
10173
+ const screenTitle = useScreenTitle();
10174
+ const shownTitle = title2 ?? screenTitle;
10127
10175
  const atTop = useScrolledUnder(floating2 && scrolled2 === void 0);
10128
10176
  const isScrolled = scrolled2 ?? atTop;
10129
10177
  const cartLabel = cartCount === null ? "Cart" : cartCount === 0 ? "Cart, empty" : `Cart, ${cartCount} item${cartCount === 1 ? "" : "s"}`;
@@ -10135,7 +10183,7 @@ function ShopBar({
10135
10183
  ].filter(Boolean).join(" ");
10136
10184
  return /* @__PURE__ */ jsxs("header", { className: classes, children: [
10137
10185
  onBack ? /* @__PURE__ */ jsx("button", { type: "button", className: styles$1.iconBtn, "aria-label": backLabel, onClick: onBack, children: /* @__PURE__ */ jsx(ChevronLeft, {}) }) : /* @__PURE__ */ jsx("span", { className: styles$1.spacer, "aria-hidden": "true" }),
10138
- title2 ? /* @__PURE__ */ jsx("span", { className: styles$1.title, children: title2 }) : null,
10186
+ shownTitle ? /* @__PURE__ */ jsx("span", { className: styles$1.title, children: shownTitle }) : null,
10139
10187
  /* @__PURE__ */ jsxs("div", { className: styles$1.right, children: [
10140
10188
  showCart ? /* @__PURE__ */ jsxs(CartTrigger, { className: styles$1.iconBtn, "aria-label": cartLabel, children: [
10141
10189
  /* @__PURE__ */ jsx(CartIcon, {}),
@@ -5384,7 +5384,7 @@
5384
5384
  * the sticky CTA bar (50), the size-guide sheet (60), the bottom sheets (900) and the
5385
5385
  * toast (1000).
5386
5386
  */
5387
- ._header_oq0bd_25 {
5387
+ ._header_1yjrk_25 {
5388
5388
  position: sticky;
5389
5389
  top: 0;
5390
5390
  z-index: 40;
@@ -5426,24 +5426,35 @@
5426
5426
  /*
5427
5427
  * Overlaid rather than stacked, for screens whose artwork runs to the top of the display.
5428
5428
  *
5429
- * `absolute`, so this deliberately opts OUT of the sticky default above: the category
5430
- * hero scrolls away under its own title and a bar pinned over a photograph that is no
5431
- * longer there would be a bar floating on nothing.
5429
+ * Overlaid is about the ARTWORK, not about staying on screen. `mobile/hero` carries its
5430
+ * own `mobile/header` instance at y=0, hidden, and positions the category title at y=106
5431
+ * measured from the top of the SCREEN, not from under a bar. In flow the header pushes the
5432
+ * picture down and every measurement inside it lands a bar height low, which is how the
5433
+ * category page shipped.
5432
5434
  *
5433
- * `mobile/hero` carries its own `mobile/header` instance at y=0, hidden, and positions the
5434
- * category title at y=106 measured from the top of the SCREEN, not from under a bar. In
5435
- * flow the header pushes the picture down and every measurement inside it lands a bar
5436
- * height low,
5437
- * which is how the category page shipped.
5435
+ * So it is taken out of the flow with a negative margin rather than with `absolute`, and
5436
+ * it keeps the sticky default. This was `position: absolute` until 21 Aug 2026, and that
5437
+ * scrolled the bar off the top of a category page: the back chevron, the cart badge and
5438
+ * the menu were reachable only by scrolling back up. DAZN reported it.
5439
+ *
5440
+ * The give-away was in the component itself — a bar that had left the screen was still
5441
+ * switching to `.scrolled` and painting a scrim on nothing. That state only means anything
5442
+ * for a bar that is still there. It looked correct on the shop entry because the entry
5443
+ * scrolls its own snap scroller and never the document, so `absolute` never moved.
5444
+ *
5445
+ * `sticky` rather than `fixed`: we are mounted in someone else's page, under chrome of
5446
+ * theirs we cannot see. `fixed` pins to the viewport and would sit on top of it. Sticky
5447
+ * stays inside our own box, wherever the host has put it.
5438
5448
  *
5439
5449
  * Declared after `.header` so it wins the background: both are one class, so the later
5440
5450
  * rule takes it.
5441
5451
  */
5442
- ._floating_oq0bd_44 {
5443
- position: absolute;
5452
+ ._floating_1yjrk_44 {
5453
+ position: sticky;
5444
5454
  top: 0;
5445
- left: 0;
5446
- right: 0;
5455
+ /* Out of the flow without leaving it: the artwork below starts at y=0 and is drawn
5456
+ under the bar, which is what `absolute` was really buying. */
5457
+ margin-bottom: calc(-1 * var(--header-height));
5447
5458
  z-index: 5;
5448
5459
  /*
5449
5460
  * Clear at rest. The scrim belongs to `.scrolled` below.
@@ -5495,12 +5506,12 @@
5495
5506
  * Fixed rather than tokenised, like the hero's own palette: it is drawn over a photograph
5496
5507
  * and does not follow the surface.
5497
5508
  */
5498
- ._floating_oq0bd_44._scrolled_oq0bd_87 {
5509
+ ._floating_1yjrk_44._scrolled_1yjrk_79 {
5499
5510
  background: rgba(8, 14, 18, 0.6);
5500
5511
  }
5501
5512
 
5502
5513
  @media (prefers-reduced-motion: reduce) {
5503
- ._floating_oq0bd_44 {
5514
+ ._floating_1yjrk_44 {
5504
5515
  transition: none;
5505
5516
  }
5506
5517
  }
@@ -5520,7 +5531,7 @@
5520
5531
  * the dark screens, and the light PDP header (5295:75116) has an empty title slot. Written
5521
5532
  * as a static white it would be invisible the first time a host passed one on a light page.
5522
5533
  */
5523
- ._title_oq0bd_161 {
5534
+ ._title_1yjrk_172 {
5524
5535
  flex: 0 0 auto;
5525
5536
  width: 222px;
5526
5537
  min-width: 0;
@@ -5533,7 +5544,7 @@
5533
5544
  white-space: nowrap;
5534
5545
  }
5535
5546
 
5536
- ._right_oq0bd_174 {
5547
+ ._right_1yjrk_185 {
5537
5548
  display: flex;
5538
5549
  align-items: center;
5539
5550
  gap: var(--space-16);
@@ -5546,12 +5557,12 @@
5546
5557
  * header without a back button would silently re-lay-out rather than simply losing one
5547
5558
  * icon. 24px is the icon box it stands in for.
5548
5559
  */
5549
- ._spacer_oq0bd_187 {
5560
+ ._spacer_1yjrk_198 {
5550
5561
  width: 24px;
5551
5562
  height: 24px;
5552
5563
  }
5553
5564
 
5554
- ._iconBtn_oq0bd_192 {
5565
+ ._iconBtn_1yjrk_203 {
5555
5566
  position: relative;
5556
5567
  display: inline-flex;
5557
5568
  align-items: center;
@@ -5567,7 +5578,7 @@
5567
5578
  }
5568
5579
 
5569
5580
  /* Expand the hit area to >=44px without moving the 24px icon off the 16px inset. */
5570
- ._iconBtn_oq0bd_192::after {
5581
+ ._iconBtn_1yjrk_203::after {
5571
5582
  content: '';
5572
5583
  position: absolute;
5573
5584
  inset: -10px;
@@ -5578,12 +5589,12 @@
5578
5589
  * its glyph on the design's centre line — 16px from the right edge, not 12. Its own icon
5579
5590
  * stays 24px.
5580
5591
  */
5581
- ._menuBtn_oq0bd_219 {
5592
+ ._menuBtn_1yjrk_230 {
5582
5593
  width: 32px;
5583
5594
  height: 32px;
5584
5595
  }
5585
5596
 
5586
- ._menuBtn_oq0bd_219::after {
5597
+ ._menuBtn_1yjrk_230::after {
5587
5598
  inset: -6px;
5588
5599
  }
5589
5600
 
@@ -5606,7 +5617,7 @@
5606
5617
  *
5607
5618
  * Sits above the ::after hit area so the number is never painted underneath it.
5608
5619
  */
5609
- ._badge_oq0bd_247 {
5620
+ ._badge_1yjrk_258 {
5610
5621
  position: absolute;
5611
5622
  top: -6px;
5612
5623
  left: 14px;
@@ -5633,13 +5644,13 @@
5633
5644
  * `context=checkout` (5295:74075). Both side slots are the 32px box the frame draws — the
5634
5645
  * left one empty, so the title centres on the screen rather than on what is left over.
5635
5646
  */
5636
- ._closeSlot_oq0bd_274 {
5647
+ ._closeSlot_1yjrk_285 {
5637
5648
  flex: 0 0 auto;
5638
5649
  width: 32px;
5639
5650
  height: 32px;
5640
5651
  }
5641
5652
 
5642
- ._closeSlot_oq0bd_274._iconBtn_oq0bd_192::after {
5653
+ ._closeSlot_1yjrk_285._iconBtn_1yjrk_203::after {
5643
5654
  inset: -6px;
5644
5655
  }
5645
5656
 
@@ -5648,7 +5659,7 @@
5648
5659
  * centres "Order #2304" between two equal slots, so it is the free space, not a measured
5649
5660
  * width. Same size, weight and colour as the shop entry's — one text style, two positions.
5650
5661
  */
5651
- ._centred_oq0bd_289 {
5662
+ ._centred_1yjrk_300 {
5652
5663
  flex: 1 1 auto;
5653
5664
  width: auto;
5654
5665
  text-align: center;
package/dist/styles.css CHANGED
@@ -5384,7 +5384,7 @@
5384
5384
  * the sticky CTA bar (50), the size-guide sheet (60), the bottom sheets (900) and the
5385
5385
  * toast (1000).
5386
5386
  */
5387
- ._header_oq0bd_25 {
5387
+ ._header_1yjrk_25 {
5388
5388
  position: sticky;
5389
5389
  top: 0;
5390
5390
  z-index: 40;
@@ -5426,24 +5426,35 @@
5426
5426
  /*
5427
5427
  * Overlaid rather than stacked, for screens whose artwork runs to the top of the display.
5428
5428
  *
5429
- * `absolute`, so this deliberately opts OUT of the sticky default above: the category
5430
- * hero scrolls away under its own title and a bar pinned over a photograph that is no
5431
- * longer there would be a bar floating on nothing.
5429
+ * Overlaid is about the ARTWORK, not about staying on screen. `mobile/hero` carries its
5430
+ * own `mobile/header` instance at y=0, hidden, and positions the category title at y=106
5431
+ * measured from the top of the SCREEN, not from under a bar. In flow the header pushes the
5432
+ * picture down and every measurement inside it lands a bar height low, which is how the
5433
+ * category page shipped.
5432
5434
  *
5433
- * `mobile/hero` carries its own `mobile/header` instance at y=0, hidden, and positions the
5434
- * category title at y=106 measured from the top of the SCREEN, not from under a bar. In
5435
- * flow the header pushes the picture down and every measurement inside it lands a bar
5436
- * height low,
5437
- * which is how the category page shipped.
5435
+ * So it is taken out of the flow with a negative margin rather than with `absolute`, and
5436
+ * it keeps the sticky default. This was `position: absolute` until 21 Aug 2026, and that
5437
+ * scrolled the bar off the top of a category page: the back chevron, the cart badge and
5438
+ * the menu were reachable only by scrolling back up. DAZN reported it.
5439
+ *
5440
+ * The give-away was in the component itself — a bar that had left the screen was still
5441
+ * switching to `.scrolled` and painting a scrim on nothing. That state only means anything
5442
+ * for a bar that is still there. It looked correct on the shop entry because the entry
5443
+ * scrolls its own snap scroller and never the document, so `absolute` never moved.
5444
+ *
5445
+ * `sticky` rather than `fixed`: we are mounted in someone else's page, under chrome of
5446
+ * theirs we cannot see. `fixed` pins to the viewport and would sit on top of it. Sticky
5447
+ * stays inside our own box, wherever the host has put it.
5438
5448
  *
5439
5449
  * Declared after `.header` so it wins the background: both are one class, so the later
5440
5450
  * rule takes it.
5441
5451
  */
5442
- ._floating_oq0bd_44 {
5443
- position: absolute;
5452
+ ._floating_1yjrk_44 {
5453
+ position: sticky;
5444
5454
  top: 0;
5445
- left: 0;
5446
- right: 0;
5455
+ /* Out of the flow without leaving it: the artwork below starts at y=0 and is drawn
5456
+ under the bar, which is what `absolute` was really buying. */
5457
+ margin-bottom: calc(-1 * var(--header-height));
5447
5458
  z-index: 5;
5448
5459
  /*
5449
5460
  * Clear at rest. The scrim belongs to `.scrolled` below.
@@ -5495,12 +5506,12 @@
5495
5506
  * Fixed rather than tokenised, like the hero's own palette: it is drawn over a photograph
5496
5507
  * and does not follow the surface.
5497
5508
  */
5498
- ._floating_oq0bd_44._scrolled_oq0bd_87 {
5509
+ ._floating_1yjrk_44._scrolled_1yjrk_79 {
5499
5510
  background: rgba(8, 14, 18, 0.6);
5500
5511
  }
5501
5512
 
5502
5513
  @media (prefers-reduced-motion: reduce) {
5503
- ._floating_oq0bd_44 {
5514
+ ._floating_1yjrk_44 {
5504
5515
  transition: none;
5505
5516
  }
5506
5517
  }
@@ -5520,7 +5531,7 @@
5520
5531
  * the dark screens, and the light PDP header (5295:75116) has an empty title slot. Written
5521
5532
  * as a static white it would be invisible the first time a host passed one on a light page.
5522
5533
  */
5523
- ._title_oq0bd_161 {
5534
+ ._title_1yjrk_172 {
5524
5535
  flex: 0 0 auto;
5525
5536
  width: 222px;
5526
5537
  min-width: 0;
@@ -5533,7 +5544,7 @@
5533
5544
  white-space: nowrap;
5534
5545
  }
5535
5546
 
5536
- ._right_oq0bd_174 {
5547
+ ._right_1yjrk_185 {
5537
5548
  display: flex;
5538
5549
  align-items: center;
5539
5550
  gap: var(--space-16);
@@ -5546,12 +5557,12 @@
5546
5557
  * header without a back button would silently re-lay-out rather than simply losing one
5547
5558
  * icon. 24px is the icon box it stands in for.
5548
5559
  */
5549
- ._spacer_oq0bd_187 {
5560
+ ._spacer_1yjrk_198 {
5550
5561
  width: 24px;
5551
5562
  height: 24px;
5552
5563
  }
5553
5564
 
5554
- ._iconBtn_oq0bd_192 {
5565
+ ._iconBtn_1yjrk_203 {
5555
5566
  position: relative;
5556
5567
  display: inline-flex;
5557
5568
  align-items: center;
@@ -5567,7 +5578,7 @@
5567
5578
  }
5568
5579
 
5569
5580
  /* Expand the hit area to >=44px without moving the 24px icon off the 16px inset. */
5570
- ._iconBtn_oq0bd_192::after {
5581
+ ._iconBtn_1yjrk_203::after {
5571
5582
  content: '';
5572
5583
  position: absolute;
5573
5584
  inset: -10px;
@@ -5578,12 +5589,12 @@
5578
5589
  * its glyph on the design's centre line — 16px from the right edge, not 12. Its own icon
5579
5590
  * stays 24px.
5580
5591
  */
5581
- ._menuBtn_oq0bd_219 {
5592
+ ._menuBtn_1yjrk_230 {
5582
5593
  width: 32px;
5583
5594
  height: 32px;
5584
5595
  }
5585
5596
 
5586
- ._menuBtn_oq0bd_219::after {
5597
+ ._menuBtn_1yjrk_230::after {
5587
5598
  inset: -6px;
5588
5599
  }
5589
5600
 
@@ -5606,7 +5617,7 @@
5606
5617
  *
5607
5618
  * Sits above the ::after hit area so the number is never painted underneath it.
5608
5619
  */
5609
- ._badge_oq0bd_247 {
5620
+ ._badge_1yjrk_258 {
5610
5621
  position: absolute;
5611
5622
  top: -6px;
5612
5623
  left: 14px;
@@ -5633,13 +5644,13 @@
5633
5644
  * `context=checkout` (5295:74075). Both side slots are the 32px box the frame draws — the
5634
5645
  * left one empty, so the title centres on the screen rather than on what is left over.
5635
5646
  */
5636
- ._closeSlot_oq0bd_274 {
5647
+ ._closeSlot_1yjrk_285 {
5637
5648
  flex: 0 0 auto;
5638
5649
  width: 32px;
5639
5650
  height: 32px;
5640
5651
  }
5641
5652
 
5642
- ._closeSlot_oq0bd_274._iconBtn_oq0bd_192::after {
5653
+ ._closeSlot_1yjrk_285._iconBtn_1yjrk_203::after {
5643
5654
  inset: -6px;
5644
5655
  }
5645
5656
 
@@ -5648,7 +5659,7 @@
5648
5659
  * centres "Order #2304" between two equal slots, so it is the free space, not a measured
5649
5660
  * width. Same size, weight and colour as the shop entry's — one text style, two positions.
5650
5661
  */
5651
- ._centred_oq0bd_289 {
5662
+ ._centred_1yjrk_300 {
5652
5663
  flex: 1 1 auto;
5653
5664
  width: auto;
5654
5665
  text-align: center;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nok-integration/storefront-react",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "Mountable React storefront components: catalogue, product detail and cart, mounted directly into a host DOM tree. No iframe.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",