@aforoai/storefront-widgets 1.0.3 → 1.0.5

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.
@@ -28,7 +28,7 @@ var React7__namespace = /*#__PURE__*/_interopNamespace(React7);
28
28
  // src/vue/index.ts
29
29
 
30
30
  // src/core/version.ts
31
- var VERSION = "1.0.3";
31
+ var VERSION = "1.0.5";
32
32
 
33
33
  // src/core/AforoEmbed.ts
34
34
  var mountedElements = /* @__PURE__ */ new WeakMap();
@@ -548,7 +548,8 @@ var _BridgeClient = class _BridgeClient {
548
548
  throw await this.parseError(resp);
549
549
  }
550
550
  const body = await resp.json();
551
- if (!body.sessionJwt || typeof body.expiresAt !== "number") {
551
+ const data = body?.data ?? body;
552
+ if (!data || !data.sessionJwt || typeof data.expiresAt !== "number") {
552
553
  throw new BridgeClientError(
553
554
  "Malformed bridge exchange response",
554
555
  500,
@@ -557,9 +558,9 @@ var _BridgeClient = class _BridgeClient {
557
558
  );
558
559
  }
559
560
  return {
560
- sessionJwt: body.sessionJwt,
561
- expiresAt: body.expiresAt,
562
- customerId: body.customerId ?? null
561
+ sessionJwt: data.sessionJwt,
562
+ expiresAt: data.expiresAt,
563
+ customerId: data.customerId ?? null
563
564
  };
564
565
  }
565
566
  /**
@@ -600,10 +601,23 @@ var _BridgeClient = class _BridgeClient {
600
601
  /**
601
602
  * Headless config — the PricingCard widget's primary read path.
602
603
  *
603
- * Mirrors `GET /api/v1/portal/headless/config` which returns
604
- * `{branding, offerings, ratePlans, themeTokens, customPages, ...}`
605
- * denormalized JSON. The endpoint is 60s Caffeine-cached server-side; we
606
- * cache once per widget instance on the client.
604
+ * Hits `GET /api/v1/portal/embed/tenant-config/{slug}` the SAME
605
+ * embed-key-authenticated endpoint {@link getTenantBrandKit} uses, now
606
+ * extended server-side with `offerings` + `embeddableWidget`. This was
607
+ * previously wired to `GET /api/v1/portal/headless/config`, an unrelated
608
+ * T6 Headless-tier endpoint that requires `X-Storefront-Key`/
609
+ * `X-Tenant-Id` auth — headers the embed key flow never sends. Every
610
+ * embed-tier fetch against that URL failed 400 "Missing tenant
611
+ * identification" by construction; this endpoint is scoped to the same
612
+ * `EmbedKeyAuthFilter` gate as the rest of the embed plane, so the
613
+ * `Authorization: Bearer <embed_key>` header `buildHeaders()` already
614
+ * sends is sufficient.
615
+ *
616
+ * The backend response is still a flat record (`tenantSlug,
617
+ * primaryColor, secondaryColor, logoUrl, fontFamily, offerings,
618
+ * embeddableWidget`) — kept flat so `getTenantBrandKit()`'s existing
619
+ * consumers (the ThemeReader cascade) are unaffected. This method nests
620
+ * the branding fields into the shape `HeadlessConfigResponse` expects.
607
621
  *
608
622
  * Pattern #18 fail-soft — returns `null` on transport failure, 404, or
609
623
  * malformed JSON. Widgets fall through to their empty / error state and
@@ -618,7 +632,7 @@ var _BridgeClient = class _BridgeClient {
618
632
  }
619
633
  const target = slug || this.tenantSlug;
620
634
  if (!target) return null;
621
- const url = `${this.baseUrl}/api/v1/portal/headless/config?tenantSlug=${encodeURIComponent(target)}`;
635
+ const url = `${this.baseUrl}/api/v1/portal/embed/tenant-config/${encodeURIComponent(target)}`;
622
636
  let resp;
623
637
  try {
624
638
  resp = await this.send(url, {
@@ -631,7 +645,19 @@ var _BridgeClient = class _BridgeClient {
631
645
  if (!resp.ok) return null;
632
646
  try {
633
647
  const body = await resp.json();
634
- return body && typeof body === "object" ? body : null;
648
+ if (!body || typeof body !== "object") return null;
649
+ const data = body.data ?? body;
650
+ return {
651
+ tenantSlug: data.tenantSlug,
652
+ branding: {
653
+ primaryColor: data.primaryColor ?? null,
654
+ secondaryColor: data.secondaryColor ?? null,
655
+ logoUrl: data.logoUrl ?? null,
656
+ fontFamily: data.fontFamily ?? null
657
+ },
658
+ offerings: Array.isArray(data.offerings) ? data.offerings : [],
659
+ embeddableWidget: data.embeddableWidget ?? null
660
+ };
635
661
  } catch {
636
662
  return null;
637
663
  }
@@ -775,7 +801,8 @@ var _BridgeClient = class _BridgeClient {
775
801
  false
776
802
  );
777
803
  }
778
- if (!body.checkoutUrl || !body.checkoutSessionId || !body.expiresAt) {
804
+ const data = body?.data ?? body ?? {};
805
+ if (!data.checkoutUrl || !data.checkoutSessionId || !data.expiresAt) {
779
806
  throw new BridgeClientError(
780
807
  "Malformed checkout-initiate response (missing required fields)",
781
808
  500,
@@ -784,9 +811,9 @@ var _BridgeClient = class _BridgeClient {
784
811
  );
785
812
  }
786
813
  return {
787
- checkoutUrl: body.checkoutUrl,
788
- checkoutSessionId: body.checkoutSessionId,
789
- expiresAt: body.expiresAt
814
+ checkoutUrl: data.checkoutUrl,
815
+ checkoutSessionId: data.checkoutSessionId,
816
+ expiresAt: data.expiresAt
790
817
  };
791
818
  }
792
819
  /* ────────────────────────────────────────────────────────────────────
@@ -1594,13 +1621,16 @@ var _BridgeClient = class _BridgeClient {
1594
1621
  if (!resp.ok) return emptyPage;
1595
1622
  try {
1596
1623
  const body = await resp.json();
1597
- if (!body || typeof body !== "object") return emptyPage;
1624
+ if (!body || typeof body !== "object") {
1625
+ return emptyPage;
1626
+ }
1627
+ const data = body.data ?? body;
1598
1628
  return {
1599
- content: Array.isArray(body.content) ? body.content : [],
1600
- totalElements: typeof body.totalElements === "number" && Number.isFinite(body.totalElements) ? body.totalElements : 0,
1601
- totalPages: typeof body.totalPages === "number" && Number.isFinite(body.totalPages) ? body.totalPages : 0,
1602
- page: typeof body.page === "number" && Number.isFinite(body.page) ? body.page : filter.page ?? 0,
1603
- size: typeof body.size === "number" && Number.isFinite(body.size) ? body.size : filter.size ?? 10
1629
+ content: Array.isArray(data.content) ? data.content : [],
1630
+ totalElements: typeof data.totalElements === "number" && Number.isFinite(data.totalElements) ? data.totalElements : 0,
1631
+ totalPages: typeof data.totalPages === "number" && Number.isFinite(data.totalPages) ? data.totalPages : 0,
1632
+ page: typeof data.page === "number" && Number.isFinite(data.page) ? data.page : filter.page ?? 0,
1633
+ size: typeof data.size === "number" && Number.isFinite(data.size) ? data.size : filter.size ?? 10
1604
1634
  };
1605
1635
  } catch {
1606
1636
  return emptyPage;
@@ -3104,6 +3134,20 @@ function safeFormatCurrency(priceCents, currency, locale, intlOverride) {
3104
3134
  return `${currency} ${amount.toFixed(2)}`;
3105
3135
  }
3106
3136
  }
3137
+ function resolveDisplayPrice(offering, locale, intlOverride) {
3138
+ const flat = safeFormatCurrency(offering.priceCents, offering.currency, locale, intlOverride);
3139
+ if (flat !== "\u2014") return { text: flat, unitSuffix: null };
3140
+ const perUnitPlan = offering.ratePlans?.find(
3141
+ (rp) => rp.pricingModel === "PER_UNIT" && rp.perUnitPriceCents != null
3142
+ );
3143
+ if (perUnitPlan) {
3144
+ const unitPrice = safeFormatCurrency(perUnitPlan.perUnitPriceCents, offering.currency, locale, intlOverride);
3145
+ if (unitPrice !== "\u2014") {
3146
+ return { text: unitPrice, unitSuffix: perUnitPlan.unitLabel || "unit" };
3147
+ }
3148
+ }
3149
+ return { text: "\u2014", unitSuffix: null };
3150
+ }
3107
3151
  function localizedName(o) {
3108
3152
  return o.localizedDisplayName || o.name || "(unnamed plan)";
3109
3153
  }
@@ -3507,12 +3551,7 @@ function PlanCard({
3507
3551
  onCtaClick,
3508
3552
  intlOverride
3509
3553
  }) {
3510
- const price = safeFormatCurrency(
3511
- offering.priceCents,
3512
- offering.currency,
3513
- locale,
3514
- intlOverride
3515
- );
3554
+ const { text: price, unitSuffix } = resolveDisplayPrice(offering, locale, intlOverride);
3516
3555
  const isContactSales = price === "\u2014";
3517
3556
  const planName = localizedName(offering);
3518
3557
  const description = localizedDescription(offering);
@@ -3533,7 +3572,7 @@ function PlanCard({
3533
3572
  "div",
3534
3573
  {
3535
3574
  role: "group",
3536
- "aria-label": `${planName}, ${price} per ${offering.billingCycle}`,
3575
+ "aria-label": `${planName}, ${price} per ${unitSuffix ?? offering.billingCycle}`,
3537
3576
  className: `aforo-w-pc-card${featured ? " aforo-w-pc-card-featured" : ""}`,
3538
3577
  style: cardStyle2,
3539
3578
  children: [
@@ -3598,14 +3637,14 @@ function PlanCard({
3598
3637
  children: price
3599
3638
  }
3600
3639
  ),
3601
- !isContactSales && offering.billingCycle ? /* @__PURE__ */ jsxRuntime.jsxs(
3640
+ !isContactSales && (unitSuffix || offering.billingCycle) ? /* @__PURE__ */ jsxRuntime.jsxs(
3602
3641
  "span",
3603
3642
  {
3604
3643
  className: "aforo-w-pc-cycle",
3605
3644
  style: { fontSize: 13, color: tokens.textMuted },
3606
3645
  children: [
3607
3646
  "/ ",
3608
- humanCycle(offering.billingCycle)
3647
+ unitSuffix ?? humanCycle(offering.billingCycle)
3609
3648
  ]
3610
3649
  }
3611
3650
  ) : null
@@ -3763,6 +3802,7 @@ function TableLayout({
3763
3802
  /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", style: { ...headerCellStyle, color: tokens.textMuted, fontSize: 12 }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "aforo-w-pc-sr-only", style: srOnlyStyle, children: "Feature" }) }),
3764
3803
  offerings.map((o) => {
3765
3804
  const featured = offeringIsFeatured(o, featuredOverride);
3805
+ const { text: tablePrice, unitSuffix: tableUnitSuffix } = resolveDisplayPrice(o, locale, intlOverride);
3766
3806
  return /* @__PURE__ */ jsxRuntime.jsx(
3767
3807
  "th",
3768
3808
  {
@@ -3792,13 +3832,20 @@ function TableLayout({
3792
3832
  children: "Most popular"
3793
3833
  }
3794
3834
  ) : null,
3795
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 22, fontWeight: 700 }, children: safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) }),
3835
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 22, fontWeight: 700 }, children: [
3836
+ tablePrice,
3837
+ tableUnitSuffix ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: 12, fontWeight: 400, color: tokens.textMuted }, children: [
3838
+ " ",
3839
+ "/ ",
3840
+ tableUnitSuffix
3841
+ ] }) : null
3842
+ ] }),
3796
3843
  /* @__PURE__ */ jsxRuntime.jsx(
3797
3844
  "button",
3798
3845
  {
3799
3846
  type: "button",
3800
3847
  onClick: () => onCtaClick(o),
3801
- "aria-label": `${ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")} for ${localizedName(o)}`,
3848
+ "aria-label": `${ctaLabel(o, ctaTextProp, tablePrice === "\u2014")} for ${localizedName(o)}`,
3802
3849
  style: {
3803
3850
  inlineSize: "100%",
3804
3851
  padding: "8px 12px",
@@ -3811,7 +3858,7 @@ function TableLayout({
3811
3858
  cursor: "pointer",
3812
3859
  fontFamily: tokens.fontFamily
3813
3860
  },
3814
- children: ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")
3861
+ children: ctaLabel(o, ctaTextProp, tablePrice === "\u2014")
3815
3862
  }
3816
3863
  )
3817
3864
  ] })
@@ -6,7 +6,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  // src/vue/index.ts
7
7
 
8
8
  // src/core/version.ts
9
- var VERSION = "1.0.3";
9
+ var VERSION = "1.0.5";
10
10
 
11
11
  // src/core/AforoEmbed.ts
12
12
  var mountedElements = /* @__PURE__ */ new WeakMap();
@@ -526,7 +526,8 @@ var _BridgeClient = class _BridgeClient {
526
526
  throw await this.parseError(resp);
527
527
  }
528
528
  const body = await resp.json();
529
- if (!body.sessionJwt || typeof body.expiresAt !== "number") {
529
+ const data = body?.data ?? body;
530
+ if (!data || !data.sessionJwt || typeof data.expiresAt !== "number") {
530
531
  throw new BridgeClientError(
531
532
  "Malformed bridge exchange response",
532
533
  500,
@@ -535,9 +536,9 @@ var _BridgeClient = class _BridgeClient {
535
536
  );
536
537
  }
537
538
  return {
538
- sessionJwt: body.sessionJwt,
539
- expiresAt: body.expiresAt,
540
- customerId: body.customerId ?? null
539
+ sessionJwt: data.sessionJwt,
540
+ expiresAt: data.expiresAt,
541
+ customerId: data.customerId ?? null
541
542
  };
542
543
  }
543
544
  /**
@@ -578,10 +579,23 @@ var _BridgeClient = class _BridgeClient {
578
579
  /**
579
580
  * Headless config — the PricingCard widget's primary read path.
580
581
  *
581
- * Mirrors `GET /api/v1/portal/headless/config` which returns
582
- * `{branding, offerings, ratePlans, themeTokens, customPages, ...}`
583
- * denormalized JSON. The endpoint is 60s Caffeine-cached server-side; we
584
- * cache once per widget instance on the client.
582
+ * Hits `GET /api/v1/portal/embed/tenant-config/{slug}` the SAME
583
+ * embed-key-authenticated endpoint {@link getTenantBrandKit} uses, now
584
+ * extended server-side with `offerings` + `embeddableWidget`. This was
585
+ * previously wired to `GET /api/v1/portal/headless/config`, an unrelated
586
+ * T6 Headless-tier endpoint that requires `X-Storefront-Key`/
587
+ * `X-Tenant-Id` auth — headers the embed key flow never sends. Every
588
+ * embed-tier fetch against that URL failed 400 "Missing tenant
589
+ * identification" by construction; this endpoint is scoped to the same
590
+ * `EmbedKeyAuthFilter` gate as the rest of the embed plane, so the
591
+ * `Authorization: Bearer <embed_key>` header `buildHeaders()` already
592
+ * sends is sufficient.
593
+ *
594
+ * The backend response is still a flat record (`tenantSlug,
595
+ * primaryColor, secondaryColor, logoUrl, fontFamily, offerings,
596
+ * embeddableWidget`) — kept flat so `getTenantBrandKit()`'s existing
597
+ * consumers (the ThemeReader cascade) are unaffected. This method nests
598
+ * the branding fields into the shape `HeadlessConfigResponse` expects.
585
599
  *
586
600
  * Pattern #18 fail-soft — returns `null` on transport failure, 404, or
587
601
  * malformed JSON. Widgets fall through to their empty / error state and
@@ -596,7 +610,7 @@ var _BridgeClient = class _BridgeClient {
596
610
  }
597
611
  const target = slug || this.tenantSlug;
598
612
  if (!target) return null;
599
- const url = `${this.baseUrl}/api/v1/portal/headless/config?tenantSlug=${encodeURIComponent(target)}`;
613
+ const url = `${this.baseUrl}/api/v1/portal/embed/tenant-config/${encodeURIComponent(target)}`;
600
614
  let resp;
601
615
  try {
602
616
  resp = await this.send(url, {
@@ -609,7 +623,19 @@ var _BridgeClient = class _BridgeClient {
609
623
  if (!resp.ok) return null;
610
624
  try {
611
625
  const body = await resp.json();
612
- return body && typeof body === "object" ? body : null;
626
+ if (!body || typeof body !== "object") return null;
627
+ const data = body.data ?? body;
628
+ return {
629
+ tenantSlug: data.tenantSlug,
630
+ branding: {
631
+ primaryColor: data.primaryColor ?? null,
632
+ secondaryColor: data.secondaryColor ?? null,
633
+ logoUrl: data.logoUrl ?? null,
634
+ fontFamily: data.fontFamily ?? null
635
+ },
636
+ offerings: Array.isArray(data.offerings) ? data.offerings : [],
637
+ embeddableWidget: data.embeddableWidget ?? null
638
+ };
613
639
  } catch {
614
640
  return null;
615
641
  }
@@ -753,7 +779,8 @@ var _BridgeClient = class _BridgeClient {
753
779
  false
754
780
  );
755
781
  }
756
- if (!body.checkoutUrl || !body.checkoutSessionId || !body.expiresAt) {
782
+ const data = body?.data ?? body ?? {};
783
+ if (!data.checkoutUrl || !data.checkoutSessionId || !data.expiresAt) {
757
784
  throw new BridgeClientError(
758
785
  "Malformed checkout-initiate response (missing required fields)",
759
786
  500,
@@ -762,9 +789,9 @@ var _BridgeClient = class _BridgeClient {
762
789
  );
763
790
  }
764
791
  return {
765
- checkoutUrl: body.checkoutUrl,
766
- checkoutSessionId: body.checkoutSessionId,
767
- expiresAt: body.expiresAt
792
+ checkoutUrl: data.checkoutUrl,
793
+ checkoutSessionId: data.checkoutSessionId,
794
+ expiresAt: data.expiresAt
768
795
  };
769
796
  }
770
797
  /* ────────────────────────────────────────────────────────────────────
@@ -1572,13 +1599,16 @@ var _BridgeClient = class _BridgeClient {
1572
1599
  if (!resp.ok) return emptyPage;
1573
1600
  try {
1574
1601
  const body = await resp.json();
1575
- if (!body || typeof body !== "object") return emptyPage;
1602
+ if (!body || typeof body !== "object") {
1603
+ return emptyPage;
1604
+ }
1605
+ const data = body.data ?? body;
1576
1606
  return {
1577
- content: Array.isArray(body.content) ? body.content : [],
1578
- totalElements: typeof body.totalElements === "number" && Number.isFinite(body.totalElements) ? body.totalElements : 0,
1579
- totalPages: typeof body.totalPages === "number" && Number.isFinite(body.totalPages) ? body.totalPages : 0,
1580
- page: typeof body.page === "number" && Number.isFinite(body.page) ? body.page : filter.page ?? 0,
1581
- size: typeof body.size === "number" && Number.isFinite(body.size) ? body.size : filter.size ?? 10
1607
+ content: Array.isArray(data.content) ? data.content : [],
1608
+ totalElements: typeof data.totalElements === "number" && Number.isFinite(data.totalElements) ? data.totalElements : 0,
1609
+ totalPages: typeof data.totalPages === "number" && Number.isFinite(data.totalPages) ? data.totalPages : 0,
1610
+ page: typeof data.page === "number" && Number.isFinite(data.page) ? data.page : filter.page ?? 0,
1611
+ size: typeof data.size === "number" && Number.isFinite(data.size) ? data.size : filter.size ?? 10
1582
1612
  };
1583
1613
  } catch {
1584
1614
  return emptyPage;
@@ -3082,6 +3112,20 @@ function safeFormatCurrency(priceCents, currency, locale, intlOverride) {
3082
3112
  return `${currency} ${amount.toFixed(2)}`;
3083
3113
  }
3084
3114
  }
3115
+ function resolveDisplayPrice(offering, locale, intlOverride) {
3116
+ const flat = safeFormatCurrency(offering.priceCents, offering.currency, locale, intlOverride);
3117
+ if (flat !== "\u2014") return { text: flat, unitSuffix: null };
3118
+ const perUnitPlan = offering.ratePlans?.find(
3119
+ (rp) => rp.pricingModel === "PER_UNIT" && rp.perUnitPriceCents != null
3120
+ );
3121
+ if (perUnitPlan) {
3122
+ const unitPrice = safeFormatCurrency(perUnitPlan.perUnitPriceCents, offering.currency, locale, intlOverride);
3123
+ if (unitPrice !== "\u2014") {
3124
+ return { text: unitPrice, unitSuffix: perUnitPlan.unitLabel || "unit" };
3125
+ }
3126
+ }
3127
+ return { text: "\u2014", unitSuffix: null };
3128
+ }
3085
3129
  function localizedName(o) {
3086
3130
  return o.localizedDisplayName || o.name || "(unnamed plan)";
3087
3131
  }
@@ -3485,12 +3529,7 @@ function PlanCard({
3485
3529
  onCtaClick,
3486
3530
  intlOverride
3487
3531
  }) {
3488
- const price = safeFormatCurrency(
3489
- offering.priceCents,
3490
- offering.currency,
3491
- locale,
3492
- intlOverride
3493
- );
3532
+ const { text: price, unitSuffix } = resolveDisplayPrice(offering, locale, intlOverride);
3494
3533
  const isContactSales = price === "\u2014";
3495
3534
  const planName = localizedName(offering);
3496
3535
  const description = localizedDescription(offering);
@@ -3511,7 +3550,7 @@ function PlanCard({
3511
3550
  "div",
3512
3551
  {
3513
3552
  role: "group",
3514
- "aria-label": `${planName}, ${price} per ${offering.billingCycle}`,
3553
+ "aria-label": `${planName}, ${price} per ${unitSuffix ?? offering.billingCycle}`,
3515
3554
  className: `aforo-w-pc-card${featured ? " aforo-w-pc-card-featured" : ""}`,
3516
3555
  style: cardStyle2,
3517
3556
  children: [
@@ -3576,14 +3615,14 @@ function PlanCard({
3576
3615
  children: price
3577
3616
  }
3578
3617
  ),
3579
- !isContactSales && offering.billingCycle ? /* @__PURE__ */ jsxs(
3618
+ !isContactSales && (unitSuffix || offering.billingCycle) ? /* @__PURE__ */ jsxs(
3580
3619
  "span",
3581
3620
  {
3582
3621
  className: "aforo-w-pc-cycle",
3583
3622
  style: { fontSize: 13, color: tokens.textMuted },
3584
3623
  children: [
3585
3624
  "/ ",
3586
- humanCycle(offering.billingCycle)
3625
+ unitSuffix ?? humanCycle(offering.billingCycle)
3587
3626
  ]
3588
3627
  }
3589
3628
  ) : null
@@ -3741,6 +3780,7 @@ function TableLayout({
3741
3780
  /* @__PURE__ */ jsx("th", { scope: "col", style: { ...headerCellStyle, color: tokens.textMuted, fontSize: 12 }, children: /* @__PURE__ */ jsx("span", { className: "aforo-w-pc-sr-only", style: srOnlyStyle, children: "Feature" }) }),
3742
3781
  offerings.map((o) => {
3743
3782
  const featured = offeringIsFeatured(o, featuredOverride);
3783
+ const { text: tablePrice, unitSuffix: tableUnitSuffix } = resolveDisplayPrice(o, locale, intlOverride);
3744
3784
  return /* @__PURE__ */ jsx(
3745
3785
  "th",
3746
3786
  {
@@ -3770,13 +3810,20 @@ function TableLayout({
3770
3810
  children: "Most popular"
3771
3811
  }
3772
3812
  ) : null,
3773
- /* @__PURE__ */ jsx("div", { style: { fontSize: 22, fontWeight: 700 }, children: safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) }),
3813
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 22, fontWeight: 700 }, children: [
3814
+ tablePrice,
3815
+ tableUnitSuffix ? /* @__PURE__ */ jsxs("span", { style: { fontSize: 12, fontWeight: 400, color: tokens.textMuted }, children: [
3816
+ " ",
3817
+ "/ ",
3818
+ tableUnitSuffix
3819
+ ] }) : null
3820
+ ] }),
3774
3821
  /* @__PURE__ */ jsx(
3775
3822
  "button",
3776
3823
  {
3777
3824
  type: "button",
3778
3825
  onClick: () => onCtaClick(o),
3779
- "aria-label": `${ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")} for ${localizedName(o)}`,
3826
+ "aria-label": `${ctaLabel(o, ctaTextProp, tablePrice === "\u2014")} for ${localizedName(o)}`,
3780
3827
  style: {
3781
3828
  inlineSize: "100%",
3782
3829
  padding: "8px 12px",
@@ -3789,7 +3836,7 @@ function TableLayout({
3789
3836
  cursor: "pointer",
3790
3837
  fontFamily: tokens.fontFamily
3791
3838
  },
3792
- children: ctaLabel(o, ctaTextProp, safeFormatCurrency(o.priceCents, o.currency, locale, intlOverride) === "\u2014")
3839
+ children: ctaLabel(o, ctaTextProp, tablePrice === "\u2014")
3793
3840
  }
3794
3841
  )
3795
3842
  ] })