@riverbankcms/sdk 0.139.0 → 0.140.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.
Files changed (196) hide show
  1. package/README.md +1 -1
  2. package/dist/{PreviewEditorSidebar-BDeenhi3.mjs → PreviewEditorSidebar-CUyTzd5z.mjs} +4 -4
  3. package/dist/{PreviewEditorUI-DSRhgjF5.mjs → PreviewEditorUI-Agkw4v5i.mjs} +3 -3
  4. package/dist/{PreviewQueryProvider-D2K8GLUG.mjs → PreviewQueryProvider-CN0ewic5.mjs} +1 -1
  5. package/dist/{SdkPreviewModeRuntime-CL7wSnYu.mjs → SdkPreviewModeRuntime-DdbeAdS8.mjs} +22 -8
  6. package/dist/{SiteChromeCustomizeContext-BhDn-5ir.mjs → SiteChromeCustomizeContext-CqGOz-Kv.mjs} +2053 -1110
  7. package/dist/_dts/ai/src/contracts/proposals.d.ts +165 -165
  8. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +212 -212
  9. package/dist/_dts/api/src/accessAdmin.d.ts +74 -104
  10. package/dist/_dts/api/src/aiEndpoints.d.ts +5 -1
  11. package/dist/_dts/api/src/authSubmission.d.ts +46 -0
  12. package/dist/_dts/api/src/billing.d.ts +8 -4
  13. package/dist/_dts/api/src/billingEndpointDefinitions.d.ts +23 -0
  14. package/dist/_dts/api/src/billingEndpointTypes.d.ts +26 -0
  15. package/dist/_dts/api/src/blocks.d.ts +2 -0
  16. package/dist/_dts/api/src/bookingRecords.d.ts +1 -2
  17. package/dist/_dts/api/src/commercePortalEndpoints.d.ts +8 -1
  18. package/dist/_dts/api/src/communications.d.ts +0 -1
  19. package/dist/_dts/api/src/contentRuntime.d.ts +8 -3
  20. package/dist/_dts/api/src/endpoints.d.ts +93 -64
  21. package/dist/_dts/api/src/event-destination-policy.d.ts +2 -1
  22. package/dist/_dts/api/src/footerSettings.d.ts +9 -6
  23. package/dist/_dts/api/src/index.d.ts +5 -6
  24. package/dist/_dts/api/src/indexCoreTypes.d.ts +1 -1
  25. package/dist/_dts/api/src/indexExtendedTypes.d.ts +1 -1
  26. package/dist/_dts/api/src/mailingListIntegration.d.ts +8 -2
  27. package/dist/_dts/api/src/navigation/dashboardSettingsNav.d.ts +1 -1
  28. package/dist/_dts/api/src/navigation/types.d.ts +1 -1
  29. package/dist/_dts/api/src/onboarding/confirmationResend.d.ts +25 -0
  30. package/dist/_dts/api/src/onboarding/surfaceData.d.ts +32 -32
  31. package/dist/_dts/api/src/publicBookingAvailabilityEndpointDefinitions.d.ts +8 -0
  32. package/dist/_dts/api/src/publicExperience/commerceEndpoints.d.ts +1 -0
  33. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +11 -3
  34. package/dist/_dts/api/src/publicExperience/sharedTypes.d.ts +3 -29
  35. package/dist/_dts/api/src/publicOnboardingAttemptEndpointDefinitions.d.ts +65 -0
  36. package/dist/_dts/api/src/saveOutcome.d.ts +20 -0
  37. package/dist/_dts/api/src/sdkContracts.d.ts +3 -22
  38. package/dist/_dts/api/src/sdkPushContracts.d.ts +18 -0
  39. package/dist/_dts/api/src/sdkThemeWire.d.ts +44 -44
  40. package/dist/_dts/api/src/settings/permissions.d.ts +12 -0
  41. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +9 -5
  42. package/dist/_dts/api/src/siteOnboarding.d.ts +2 -9
  43. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +2 -4
  44. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +3 -1
  45. package/dist/_dts/api/src/siteSettings.d.ts +20 -7
  46. package/dist/_dts/api/src/types.d.ts +77 -77
  47. package/dist/_dts/api/src/wholeSeriesRuns.d.ts +2 -37
  48. package/dist/_dts/billing/src/membership/index.d.ts +3 -0
  49. package/dist/_dts/billing/src/plans/types.d.ts +3 -0
  50. package/dist/_dts/billing/src/site-arrangements.d.ts +14 -3
  51. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +16 -4
  52. package/dist/_dts/blocks/src/pageRenderAdmission.d.ts +2 -0
  53. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +5 -0
  54. package/dist/_dts/blocks/src/portal/types.d.ts +2 -4
  55. package/dist/_dts/blocks/src/portal/waitlist-types.d.ts +35 -0
  56. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +2 -0
  57. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +42 -0
  58. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +4 -0
  59. package/dist/_dts/blocks/src/system/data/index.d.ts +2 -2
  60. package/dist/_dts/blocks/src/system/data/prefetchBlockData.d.ts +2 -1
  61. package/dist/_dts/blocks/src/system/data/types.d.ts +9 -0
  62. package/dist/_dts/blocks/src/system/registry.d.ts +1 -0
  63. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +0 -28
  64. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  65. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +7 -3
  66. package/dist/_dts/blocks/src/system/runtime/api/types/portal.d.ts +1 -1
  67. package/dist/_dts/blocks/src/system/runtime/api/types/waitlist-claims.d.ts +7 -2
  68. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +30 -6
  69. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +15 -3
  70. package/dist/_dts/blocks/src/system/runtime/nodes/appointment-services.d.ts +43 -0
  71. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationSurface.d.ts +1 -2
  72. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +2 -0
  73. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/checkoutRecovery.d.ts +14 -0
  74. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.d.ts +56 -0
  75. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +6 -8
  76. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useCheckoutRecovery.d.ts +8 -0
  77. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +8 -7
  78. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +10 -2
  79. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventSourceRecovery.d.ts +18 -0
  80. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.client.d.ts +1 -1
  81. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +24 -6
  82. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.server.d.ts +4 -2
  83. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.interactive.d.ts +6 -0
  84. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.types.d.ts +1 -0
  85. package/dist/_dts/blocks/src/system/runtime/resolver.d.ts +1 -0
  86. package/dist/_dts/blocks/src/system/runtime/shared/components/index.d.ts +2 -6
  87. package/dist/_dts/blocks/src/system/runtime/shared/index.d.ts +2 -2
  88. package/dist/_dts/blocks/src/system/runtime/shared/useWaitlistClaim.d.ts +2 -0
  89. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +4 -0
  90. package/dist/_dts/commerce/src/purchase/orderItemMetadata.d.ts +27 -4
  91. package/dist/_dts/content-editor/src/controller/EntryControllerProvider.d.ts +2 -1
  92. package/dist/_dts/content-editor/src/hooks/useBlockAutosave.d.ts +3 -1
  93. package/dist/_dts/content-editor/src/useFooterComposerState.d.ts +3 -0
  94. package/dist/_dts/core/src/branded.d.ts +0 -8
  95. package/dist/_dts/core/src/index.d.ts +1 -0
  96. package/dist/_dts/core/src/mailing-list-setup-parser.d.ts +1 -1
  97. package/dist/_dts/core/src/mailing-list-setup.d.ts +14 -1
  98. package/dist/_dts/core/src/mailing-list-submission.d.ts +7 -27
  99. package/dist/_dts/core/src/participants.d.ts +2 -29
  100. package/dist/_dts/core/src/public-experience.d.ts +4 -0
  101. package/dist/_dts/core/src/public-profile.d.ts +27 -1
  102. package/dist/_dts/core/src/site-route-target.d.ts +9 -2
  103. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +293 -606
  104. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/MailingListGlobalSettingsSummary.d.ts +10 -0
  105. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/MailingListTargetingField.d.ts +3 -2
  106. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/context.d.ts +3 -2
  107. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/loaders.d.ts +3 -2
  108. package/dist/_dts/editor-blocks/src/widgets/mailing-list-targeting/targetingState.d.ts +19 -0
  109. package/dist/_dts/sdk/src/public-api/contracts.d.ts +14 -20
  110. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +2 -0
  111. package/dist/_dts/sdk/src/rendering/components/pageShared.d.ts +2 -0
  112. package/dist/_dts/sdk/src/rendering/helpers/pageRuntimeSupport.d.ts +7 -1
  113. package/dist/_dts/sdk/src/rendering/helpers/templateContent.d.ts +1 -0
  114. package/dist/_dts/sdk/src/version.d.ts +1 -1
  115. package/dist/_dts/theme-core/src/accordions/types.d.ts +3 -3
  116. package/dist/_dts/theme-core/src/cards/types.d.ts +21 -21
  117. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  118. package/dist/_dts/theme-core/src/header/interactionPalette.d.ts +8 -2
  119. package/dist/_dts/theme-core/src/header/legacyHeaderVariant.d.ts +2 -2
  120. package/dist/_dts/theme-core/src/inputs/types.d.ts +2 -2
  121. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +54 -54
  122. package/dist/_dts/theme-core/src/runtime/buildThemeRuntimeFromBridge.d.ts +1 -4
  123. package/dist/_dts/theme-core/src/schema.d.ts +84 -84
  124. package/dist/_dts/theme-core/src/shared/componentStyles.d.ts +1 -1
  125. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  126. package/dist/_dts/theme-core/src/surfaces/foregroundInteraction.d.ts +77 -0
  127. package/dist/_dts/theme-core/src/surfaces/index.d.ts +1 -0
  128. package/dist/_dts/theme-core/src/typography/generateTypographyCss.d.ts +2 -2
  129. package/dist/_dts/ui/src/lib/use-delayed-flag.d.ts +9 -0
  130. package/dist/_dts/ui/src/pending-button.d.ts +10 -0
  131. package/dist/client/{BookingConsentAgreement-CZvTDtsK.mjs → BookingConsentAgreement-CuKa82V4.mjs} +4 -4
  132. package/dist/client/{DiscountCodeDisclosure-D-DsLCMU.mjs → DiscountCodeDisclosure-BbYieKWB.mjs} +1 -1
  133. package/dist/client/{EventCombined.client-dWqgd5ZO.mjs → EventCombined.client-LTfR1Aby.mjs} +6 -6
  134. package/dist/client/{EventListing.client-BMRa9IYp.mjs → EventListing.client-DUHpYSuo.mjs} +2 -2
  135. package/dist/client/{LeafletStaticMap-D21W9kBZ.mjs → LeafletStaticMap-CHRupL6m.mjs} +1 -1
  136. package/dist/client/{accordion-FxF_ta47.mjs → accordion-6HiIFxNL.mjs} +1 -1
  137. package/dist/client/{bookingForm-YnwW4TGw.mjs → bookingForm-Dlmid43H.mjs} +7 -7
  138. package/dist/client/bookings.mjs +325 -140
  139. package/dist/client/{carousel-vng7fFT5.mjs → carousel-DmkZRRq_.mjs} +2 -2
  140. package/dist/client/{carousel.client-59G_WMr4.mjs → carousel.client-DQTjCVQT.mjs} +1 -1
  141. package/dist/client/{cart-DEPDsjNk.mjs → cart-CIC24330.mjs} +3 -3
  142. package/dist/client/{checkout-CZRWu9rW.mjs → checkout-D1Zw9imo.mjs} +3 -3
  143. package/dist/client/{checkoutUrls-BuseL0dw.mjs → checkoutUrls-FL6lRkrt.mjs} +1 -1
  144. package/dist/client/client.mjs +1717 -882
  145. package/dist/client/{colorStyles-CrxJrPjJ.mjs → colorStyles-3013aRAY.mjs} +3 -1
  146. package/dist/client/{context-DvU_WUmd.mjs → context-Cy1OayHx.mjs} +6 -19
  147. package/dist/client/{dates-wnPMiRl5.mjs → dates-aDcfoLHr.mjs} +2 -2
  148. package/dist/client/{eventCalendar-BA3Q5L15.mjs → eventCalendar-FQxMPAjf.mjs} +8 -8
  149. package/dist/client/{eventCombined-51wgR-1R.mjs → eventCombined-Dr3ac6Hb.mjs} +3 -3
  150. package/dist/client/{eventDetails-modaetpm.mjs → eventDetails-D-AzUHFQ.mjs} +4 -4
  151. package/dist/client/{eventListing-D8t2J3Kv.mjs → eventListing-B3XxBf3i.mjs} +3 -3
  152. package/dist/client/{eventRegistration-CYqtkCE_.mjs → eventRegistration-I6EaokFD.mjs} +500 -152
  153. package/dist/client/{form-CBKXN5In.mjs → form-BbWckVWY.mjs} +3 -3
  154. package/dist/client/{headerSection-DylDqNrs.mjs → headerSection-C9qU4Y7x.mjs} +2 -2
  155. package/dist/client/hooks.mjs +445 -180
  156. package/dist/client/{locationMap-CvWrG0vZ.mjs → locationMap-CLCShvlG.mjs} +3 -3
  157. package/dist/client/{media-BOHuueS5.mjs → media-DHpfFwd3.mjs} +1 -1
  158. package/dist/client/{newsletterForm-Btwk70zw.mjs → newsletterForm-Briukxzi.mjs} +130 -87
  159. package/dist/client/{pricing-Uzw-GQ6Y.mjs → pricing-Copadu-2.mjs} +1 -1
  160. package/dist/client/{productDetail-JLcUYKnd.mjs → productDetail-oujlRd-r.mjs} +3 -3
  161. package/dist/client/{productList-FM0cbUzj.mjs → productList-BRiIRUcd.mjs} +3 -3
  162. package/dist/client/{propsDecoder-DYzafZb2.mjs → propsDecoder-CB2KS_ko.mjs} +130 -24
  163. package/dist/client/rendering/client.mjs +911 -400
  164. package/dist/client/rendering/islands.mjs +19 -19
  165. package/dist/client/rendering.mjs +1696 -818
  166. package/dist/client/{scrollToBlock-CvXszexS.mjs → scrollToBlock-B1IS1_ua.mjs} +1 -1
  167. package/dist/client/{shop-BIcjvy5a.mjs → shop-Vn6yNFED.mjs} +6 -6
  168. package/dist/client/{shop-commerce-BZQsy7xw.mjs → shop-commerce-BVTqipYp.mjs} +7 -7
  169. package/dist/client/{shop-commerce-store-D5V7XAb5.mjs → shop-commerce-store-BVnJVy7v.mjs} +1 -1
  170. package/dist/client/{shopShared-DowkXevB.mjs → shopShared-COIeK77T.mjs} +4 -4
  171. package/dist/client/{useEventFilters-oMoTw_JE.mjs → useEventFilters-DzIUcS9n.mjs} +7 -7
  172. package/dist/preview-next/before-render.mjs +119 -64
  173. package/dist/preview-next/client/runtime.mjs +4 -4
  174. package/dist/preview-next/keepAlive.mjs +119 -64
  175. package/dist/preview-next/middleware.mjs +119 -64
  176. package/dist/{previewAuthStore-COUyAKaX.mjs → previewAuthStore-Gjb6r6vz.mjs} +119 -64
  177. package/dist/{sdk-runtime-D4k2LSGF.mjs → sdk-runtime-CnCLvZRv.mjs} +2 -2
  178. package/dist/{separator-UGtpag4Z.mjs → separator-DKDRJ1oH.mjs} +1 -1
  179. package/dist/server/components.mjs +787 -323
  180. package/dist/server/config-validation.mjs +328 -79
  181. package/dist/server/config.mjs +332 -83
  182. package/dist/server/data.mjs +441 -180
  183. package/dist/server/index.mjs +126 -66
  184. package/dist/server/next.mjs +1031 -595
  185. package/dist/server/page-converter.mjs +183 -74
  186. package/dist/server/prebuild.mjs +1 -1
  187. package/dist/server/rendering/server.mjs +1068 -592
  188. package/dist/server/rendering.mjs +802 -324
  189. package/dist/server/routing.mjs +506 -214
  190. package/dist/server/server.mjs +468 -244
  191. package/dist/server/theme-bridge.mjs +433 -100
  192. package/dist/server/theme.mjs +8 -2
  193. package/dist/styles/index.css +237 -43
  194. package/package.json +1 -1
  195. package/dist/_dts/blocks/src/system/runtime/shared/components/BookingOptionCompactRow.d.ts +0 -29
  196. package/dist/_dts/blocks/src/system/runtime/shared/components/BookingOptionList.d.ts +0 -13
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { d as unwrapResponse, l as ApiEnvelopeError, s as ApiRequestError, u as extractApiEnvelopeErrorDetails } from "./previewAuthStore-COUyAKaX.mjs";
2
+ import { d as unwrapResponse, l as ApiEnvelopeError, s as ApiRequestError, u as extractApiEnvelopeErrorDetails } from "./previewAuthStore-Gjb6r6vz.mjs";
3
3
  import * as React$1 from "react";
4
4
  import React, { Component, Fragment, cloneElement, createContext, createElement, createRef, forwardRef, isValidElement, memo, startTransition, useCallback, useContext, useDebugValue, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -82,11 +82,13 @@ const unsafeAsPassId = (id) => id;
82
82
  const unsafeAsCustomerPassId = (id) => id;
83
83
  const unsafeAsAppointmentServiceId = (id) => id;
84
84
  const unsafeAsAppointmentResourceId = (id) => id;
85
+ const unsafeAsContentVersionId = (id) => id;
85
86
  const parseSiteId = (value) => parseUuidBrand(value, unsafeAsSiteId);
86
87
  const parseApiKeyId = (value) => parseUuidBrand(value, unsafeAsApiKeyId);
87
88
  const parseEventOfferingId = (value) => parseUuidBrand(value, unsafeAsEventOfferingId);
88
89
  const parseUserId = (value) => parseUuidBrand(value, unsafeAsUserId);
89
90
  const parseMediaAssetId = (value) => parseUuidBrand(value, unsafeAsMediaAssetId);
91
+ const parseContentVersionId = (value) => parseUuidBrand(value, unsafeAsContentVersionId);
90
92
  const parseFormId = (value) => parseUuidBrand(value, unsafeAsFormId);
91
93
  const parseEventSeriesId = (value) => parseUuidBrand(value, unsafeAsEventSeriesId);
92
94
  //#endregion
@@ -396,7 +398,7 @@ function parseEventPageSurfaceDestinationMode(value) {
396
398
  return value === "cms-managed" || value === "system-managed" ? value : void 0;
397
399
  }
398
400
  function parseEventPageSurfaceSubject(value) {
399
- if (!isRecord$28(value)) return err$1({ kind: "notObject" });
401
+ if (!isRecord$29(value)) return err$1({ kind: "notObject" });
400
402
  switch (value.kind) {
401
403
  case "eventSeries": {
402
404
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -451,7 +453,7 @@ function parseEventSeriesIdList(values) {
451
453
  }
452
454
  return ok$2(parsed);
453
455
  }
454
- function isRecord$28(value) {
456
+ function isRecord$29(value) {
455
457
  return typeof value === "object" && value !== null && !Array.isArray(value);
456
458
  }
457
459
  //#endregion
@@ -1101,7 +1103,7 @@ const knownSocialPlatforms = [
1101
1103
  "x",
1102
1104
  "youtube"
1103
1105
  ];
1104
- function isRecord$27(value) {
1106
+ function isRecord$28(value) {
1105
1107
  return typeof value === "object" && value !== null && !Array.isArray(value);
1106
1108
  }
1107
1109
  function trimmedOptionalString(value) {
@@ -1111,7 +1113,7 @@ function trimmedOptionalString(value) {
1111
1113
  return trimmed.length > 0 ? trimmed : null;
1112
1114
  }
1113
1115
  function parsePublicContactDetails(value) {
1114
- if (!isRecord$27(value)) return err$1({
1116
+ if (!isRecord$28(value)) return err$1({
1115
1117
  code: "invalid_shape",
1116
1118
  path: "contact",
1117
1119
  message: "Contact details must be an object."
@@ -1191,7 +1193,7 @@ function minutesSinceMidnight(value) {
1191
1193
  }
1192
1194
  function parseOpeningHoursDay(value, expectedWeekday, index) {
1193
1195
  const path = `openingHours.days.${index}`;
1194
- if (!isRecord$27(value) || value.weekday !== expectedWeekday) return err$1({
1196
+ if (!isRecord$28(value) || value.weekday !== expectedWeekday) return err$1({
1195
1197
  code: "invalid_weekdays",
1196
1198
  path,
1197
1199
  message: `Expected ${expectedWeekday} in calendar order.`
@@ -1208,7 +1210,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
1208
1210
  const intervals = [];
1209
1211
  for (const [intervalIndex, rawInterval] of value.intervals.entries()) {
1210
1212
  const intervalPath = `${path}.intervals.${intervalIndex}`;
1211
- if (!isRecord$27(rawInterval)) return err$1({
1213
+ if (!isRecord$28(rawInterval)) return err$1({
1212
1214
  code: "invalid_interval",
1213
1215
  path: intervalPath,
1214
1216
  message: "Opening interval is invalid."
@@ -1250,7 +1252,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
1250
1252
  });
1251
1253
  }
1252
1254
  function parseWeeklyOpeningHours(value) {
1253
- if (!isRecord$27(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) return err$1({
1255
+ if (!isRecord$28(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) return err$1({
1254
1256
  code: "invalid_weekdays",
1255
1257
  path: "openingHours.days",
1256
1258
  message: "Opening hours must include all seven weekdays."
@@ -1319,7 +1321,7 @@ function parsePublicSocialLinks(value) {
1319
1321
  const ids = /* @__PURE__ */ new Set();
1320
1322
  for (const [index, rawLink] of value.entries()) {
1321
1323
  const path = `socialLinks.${index}`;
1322
- if (!isRecord$27(rawLink)) return err$1({
1324
+ if (!isRecord$28(rawLink)) return err$1({
1323
1325
  code: "invalid_social_link",
1324
1326
  path,
1325
1327
  message: "Social link is invalid."
@@ -1362,7 +1364,7 @@ function parsePublicSocialLinks(value) {
1362
1364
  return ok$2(links);
1363
1365
  }
1364
1366
  function parsePublicProfile(value) {
1365
- if (!isRecord$27(value)) return err$1({
1367
+ if (!isRecord$28(value)) return err$1({
1366
1368
  code: "invalid_shape",
1367
1369
  path: "",
1368
1370
  message: "Public profile must be an object."
@@ -1375,11 +1377,54 @@ function parsePublicProfile(value) {
1375
1377
  if (!socialLinks.ok) return socialLinks;
1376
1378
  const addressVisibility = parseAddressVisibility(value.addressVisibility);
1377
1379
  if (!addressVisibility.ok) return addressVisibility;
1380
+ const contactVisibility = parseContactVisibility(value.contactVisibility);
1381
+ if (!contactVisibility.ok) return contactVisibility;
1382
+ const premises = parsePremises(value.premises);
1383
+ if (!premises.ok) return premises;
1378
1384
  return ok$2({
1379
1385
  contact: contact.value,
1386
+ contactVisibility: contactVisibility.value,
1380
1387
  openingHours: openingHours.value,
1381
1388
  socialLinks: socialLinks.value,
1382
- addressVisibility: addressVisibility.value
1389
+ addressVisibility: addressVisibility.value,
1390
+ premises: premises.value
1391
+ });
1392
+ }
1393
+ function parseFieldVisibility(value, path) {
1394
+ if (value === void 0 || value === null) return ok$2("shown");
1395
+ if (value === "shown" || value === "hidden") return ok$2(value);
1396
+ return err$1({
1397
+ code: "invalid_contact_visibility",
1398
+ path,
1399
+ message: "Contact visibility must be shown or hidden."
1400
+ });
1401
+ }
1402
+ function parseContactVisibility(value) {
1403
+ if (value === void 0 || value === null) return ok$2({
1404
+ phone: "shown",
1405
+ email: "shown"
1406
+ });
1407
+ if (!isRecord$28(value)) return err$1({
1408
+ code: "invalid_contact_visibility",
1409
+ path: "contactVisibility",
1410
+ message: "Contact visibility must be an object."
1411
+ });
1412
+ const phone = parseFieldVisibility(value.phone, "contactVisibility.phone");
1413
+ if (!phone.ok) return phone;
1414
+ const email = parseFieldVisibility(value.email, "contactVisibility.email");
1415
+ if (!email.ok) return email;
1416
+ return ok$2({
1417
+ phone: phone.value,
1418
+ email: email.value
1419
+ });
1420
+ }
1421
+ function parsePremises(value) {
1422
+ if (value === void 0 || value === null) return ok$2(null);
1423
+ if (typeof value === "boolean") return ok$2(value);
1424
+ return err$1({
1425
+ code: "invalid_premises",
1426
+ path: "premises",
1427
+ message: "Premises must be true or false."
1383
1428
  });
1384
1429
  }
1385
1430
  function parseAddressVisibility(value) {
@@ -1393,7 +1438,7 @@ function parseAddressVisibility(value) {
1393
1438
  }
1394
1439
  //#endregion
1395
1440
  //#region ../core/src/rich-text.ts
1396
- function isRecord$26(value) {
1441
+ function isRecord$27(value) {
1397
1442
  return typeof value === "object" && value !== null && !Array.isArray(value);
1398
1443
  }
1399
1444
  function hasOnlyKeys(value, allowed) {
@@ -1413,15 +1458,15 @@ function isDenseArrayOf(value, predicate) {
1413
1458
  return true;
1414
1459
  }
1415
1460
  function isTipTapMark(value) {
1416
- return isRecord$26(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === void 0 || isRecord$26(value.attrs));
1461
+ return isRecord$27(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === void 0 || isRecord$27(value.attrs));
1417
1462
  }
1418
1463
  function isTipTapNode(value) {
1419
- if (!isRecord$26(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== void 0 && !isRecord$26(value.attrs) || value.marks !== void 0 && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== void 0 && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) return false;
1464
+ if (!isRecord$27(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== void 0 && !isRecord$27(value.attrs) || value.marks !== void 0 && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== void 0 && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) return false;
1420
1465
  if (value.type === "text") return typeof value.text === "string" && value.text.length > 0 && value.content === void 0;
1421
1466
  return value.text === void 0;
1422
1467
  }
1423
1468
  function isCanonicalRichTextDoc(value) {
1424
- return isRecord$26(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
1469
+ return isRecord$27(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
1425
1470
  }
1426
1471
  //#endregion
1427
1472
  //#region ../core/src/mailing-list-identifiers.ts
@@ -1438,22 +1483,28 @@ function parsePositiveInteger$1(value) {
1438
1483
  function parseNewsletterListId(value) {
1439
1484
  return parseTrimmedMatchingString(value, UUID_PATTERN);
1440
1485
  }
1486
+ function parseMailchimpAccountId(value) {
1487
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
1488
+ }
1441
1489
  function parseMailchimpAudienceId(value) {
1442
1490
  return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
1443
1491
  }
1444
1492
  function parseMailchimpInterestId(value) {
1445
1493
  return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
1446
1494
  }
1495
+ function parseBrevoAccountId(value) {
1496
+ return parseTrimmedMatchingString(value, OPAQUE_ID_PATTERN);
1497
+ }
1447
1498
  function parseBrevoListId(value) {
1448
1499
  return parsePositiveInteger$1(value);
1449
1500
  }
1450
1501
  //#endregion
1451
1502
  //#region ../core/src/mailing-list-targeting-parser.ts
1452
1503
  function parseMailingListPlacementTargeting(value) {
1453
- if (!isRecord$25(value)) return targetingError("shape");
1504
+ if (!isRecord$26(value)) return targetingError("shape");
1454
1505
  if (value.mode === "default") return ok$2({ mode: "default" });
1455
1506
  if (value.mode !== "custom") return targetingError("mode");
1456
- if (!isRecord$25(value.selection)) return targetingError("selection");
1507
+ if (!isRecord$26(value.selection)) return targetingError("selection");
1457
1508
  switch (value.provider) {
1458
1509
  case "native": return parseNativeTargeting(value.selection);
1459
1510
  case "brevo": return parseBrevoTargeting(value.selection);
@@ -1462,7 +1513,7 @@ function parseMailingListPlacementTargeting(value) {
1462
1513
  }
1463
1514
  }
1464
1515
  function parseMailingListVisitorSelection(value) {
1465
- if (!isRecord$25(value)) return visitorSelectionError("shape");
1516
+ if (!isRecord$26(value)) return visitorSelectionError("shape");
1466
1517
  switch (value.provider) {
1467
1518
  case "native": {
1468
1519
  const topicIds = parseVisitorIds(value.topicIds, parseNewsletterListId);
@@ -1595,7 +1646,7 @@ function parseVisitorIds(value, parseId) {
1595
1646
  }
1596
1647
  return parsed;
1597
1648
  }
1598
- function isRecord$25(value) {
1649
+ function isRecord$26(value) {
1599
1650
  return typeof value === "object" && value !== null && !Array.isArray(value);
1600
1651
  }
1601
1652
  function targetingError(field) {
@@ -1612,7 +1663,7 @@ function visitorSelectionError(field) {
1612
1663
  }
1613
1664
  //#endregion
1614
1665
  //#region ../core/src/footer-engagement.ts
1615
- function isRecord$24(value) {
1666
+ function isRecord$25(value) {
1616
1667
  return typeof value === "object" && value !== null && !Array.isArray(value);
1617
1668
  }
1618
1669
  function invalid(path, message) {
@@ -1638,7 +1689,7 @@ function parseEngagementBase(value, base, path) {
1638
1689
  if (heading.value === null) return err$1(invalid(`${path}.heading`, "Footer engagement heading cannot be empty."));
1639
1690
  const treatment = parseTreatment(value.treatment, `${path}.treatment`);
1640
1691
  if (!treatment.ok) return treatment;
1641
- if (!isRecord$24(value.source)) return err$1(invalid(`${path}.source`, "Footer engagement source is invalid."));
1692
+ if (!isRecord$25(value.source)) return err$1(invalid(`${path}.source`, "Footer engagement source is invalid."));
1642
1693
  const intro = parseBoundedText(value.source.intro, `${path}.source.intro`, {
1643
1694
  maxLength: 240,
1644
1695
  optional: true
@@ -1681,7 +1732,7 @@ function parseFooterEngagementSectionValue(value, sectionBase, path) {
1681
1732
  const base = parseEngagementBase(value, sectionBase, path);
1682
1733
  if (!base.ok) return base;
1683
1734
  const source = value.source;
1684
- if (!isRecord$24(source)) return err$1(invalid(`${path}.source`, "Footer engagement source is invalid."));
1735
+ if (!isRecord$25(source)) return err$1(invalid(`${path}.source`, "Footer engagement source is invalid."));
1685
1736
  switch (source.kind) {
1686
1737
  case "standard-contact": return parseStandardContact(source, base.value, `${path}.source`);
1687
1738
  case "mailing-list": return parseMailingList(source, base.value, `${path}.source`);
@@ -1784,7 +1835,7 @@ function unsafeAsFooterModuleId(value) {
1784
1835
  function parseFooterModuleId(value) {
1785
1836
  return parseSectionId(value, "sectionId");
1786
1837
  }
1787
- function isRecord$23(value) {
1838
+ function isRecord$24(value) {
1788
1839
  return typeof value === "object" && value !== null && !Array.isArray(value);
1789
1840
  }
1790
1841
  function isRichText(value) {
@@ -1810,7 +1861,7 @@ function parseHeading(value, path) {
1810
1861
  }
1811
1862
  function parseFooterSection(value, index) {
1812
1863
  const path = `sections.${index}`;
1813
- if (!isRecord$23(value)) return err$1({
1864
+ if (!isRecord$24(value)) return err$1({
1814
1865
  code: "invalid_section",
1815
1866
  path,
1816
1867
  message: "Footer section must be an object."
@@ -1839,7 +1890,7 @@ function parseFooterSection(value, index) {
1839
1890
  });
1840
1891
  break;
1841
1892
  case "navigation":
1842
- if (isRecord$23(value.source) && value.source.kind === "menu-role" && value.source.role === "main") return ok$2({
1893
+ if (isRecord$24(value.source) && value.source.kind === "menu-role" && value.source.role === "main") return ok$2({
1843
1894
  ...base,
1844
1895
  kind: "navigation",
1845
1896
  heading: heading.value,
@@ -1850,7 +1901,7 @@ function parseFooterSection(value, index) {
1850
1901
  });
1851
1902
  break;
1852
1903
  case "legal-navigation":
1853
- if (isRecord$23(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") return ok$2({
1904
+ if (isRecord$24(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") return ok$2({
1854
1905
  ...base,
1855
1906
  kind: "legal-navigation",
1856
1907
  heading: heading.value,
@@ -1861,7 +1912,7 @@ function parseFooterSection(value, index) {
1861
1912
  });
1862
1913
  break;
1863
1914
  case "business-address":
1864
- if (isRecord$23(value.source) && value.source.kind === "business-address") return ok$2({
1915
+ if (isRecord$24(value.source) && value.source.kind === "business-address") return ok$2({
1865
1916
  ...base,
1866
1917
  kind: "business-address",
1867
1918
  heading: heading.value,
@@ -1871,7 +1922,7 @@ function parseFooterSection(value, index) {
1871
1922
  case "contact":
1872
1923
  case "opening-hours":
1873
1924
  case "social-links":
1874
- if (isRecord$23(value.source) && value.source.kind === "public-profile") return ok$2({
1925
+ if (isRecord$24(value.source) && value.source.kind === "public-profile") return ok$2({
1875
1926
  ...base,
1876
1927
  kind: value.kind,
1877
1928
  heading: heading.value,
@@ -2091,7 +2142,7 @@ const defaultSections = (legacy, options) => {
2091
2142
  ];
2092
2143
  };
2093
2144
  function normalizeLegacyFooterContentToV1(value, options = {}) {
2094
- if (!isRecord$23(value)) return err$1({
2145
+ if (!isRecord$24(value)) return err$1({
2095
2146
  code: "invalid_shape",
2096
2147
  path: "",
2097
2148
  message: "Legacy footer content must be an object."
@@ -2108,6 +2159,39 @@ function normalizeFooterNavigationLayout(value) {
2108
2159
  return value === "stack" ? "stack" : defaultFooterNavigationLayout;
2109
2160
  }
2110
2161
  //#endregion
2162
+ //#region ../core/src/mailing-list-setup.ts
2163
+ function isRecord$23(value) {
2164
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2165
+ }
2166
+ /** The name shown to people for each provider; the native one is the product brand. */
2167
+ function mailingListProviderName(provider) {
2168
+ switch (provider) {
2169
+ case "native": return "Macadamia";
2170
+ case "mailchimp": return "Mailchimp";
2171
+ case "brevo": return "Brevo";
2172
+ }
2173
+ }
2174
+ const DEFAULT_MAILING_LIST_SUCCESS_MESSAGE = "Thanks — you're on the list.";
2175
+ function parseMailingListProviderScope(value) {
2176
+ if (!isRecord$23(value)) return err$1("invalid_mailing_list_provider_scope");
2177
+ if (value.provider === "native") return ok$2({ provider: "native" });
2178
+ if (value.provider === "brevo") {
2179
+ const accountId = parseBrevoAccountId(value.accountId);
2180
+ return accountId !== null ? ok$2({
2181
+ provider: "brevo",
2182
+ accountId
2183
+ }) : err$1("invalid_mailing_list_provider_scope");
2184
+ }
2185
+ if (value.provider === "mailchimp") {
2186
+ const accountId = parseMailchimpAccountId(value.accountId);
2187
+ return accountId !== null ? ok$2({
2188
+ provider: "mailchimp",
2189
+ accountId
2190
+ }) : err$1("invalid_mailing_list_provider_scope");
2191
+ }
2192
+ return err$1("invalid_mailing_list_provider_scope");
2193
+ }
2194
+ //#endregion
2111
2195
  //#region ../core/src/mailing-list-setup-parser.ts
2112
2196
  function parseMailingListGlobalForm(value) {
2113
2197
  if (!isRecord$22(value)) return formError("shape");
@@ -2116,12 +2200,15 @@ function parseMailingListGlobalForm(value) {
2116
2200
  if (typeof value.includeOptionalPhone !== "boolean") return formError("includeOptionalPhone");
2117
2201
  const buttonLabel = parseRequiredString(value.buttonLabel);
2118
2202
  if (buttonLabel === null) return formError("buttonLabel");
2203
+ const successMessage = parseRequiredString(value.successMessage);
2204
+ if (successMessage === null) return formError("successMessage");
2119
2205
  const privacyNote = parseOptionalString(value.privacyNote);
2120
2206
  if (privacyNote === void 0) return formError("privacyNote");
2121
2207
  return ok$2({
2122
2208
  fields,
2123
2209
  includeOptionalPhone: value.includeOptionalPhone,
2124
2210
  buttonLabel,
2211
+ successMessage,
2125
2212
  privacyNote
2126
2213
  });
2127
2214
  }
@@ -2351,11 +2438,17 @@ function parseMailingListSignedSourceToken(value) {
2351
2438
  const parsed = typeof value === "string" ? value.trim() : "";
2352
2439
  return /^[A-Za-z0-9._~-]{16,1000}$/u.test(parsed) ? parsed : null;
2353
2440
  }
2354
- function buildMailingListSignupBlockSourceRef(blockId) {
2355
- const parsed = parseMailingListBlockId(blockId);
2356
- return parsed !== null ? {
2441
+ function parseMailingListContentVersionId(value) {
2442
+ const parsed = parseContentVersionId(value);
2443
+ return parsed.ok ? parsed.value : null;
2444
+ }
2445
+ function buildMailingListSignupBlockSourceRef(blockId, contentVersionId) {
2446
+ const parsedBlockId = parseMailingListBlockId(blockId);
2447
+ const parsedContentVersionId = parseMailingListContentVersionId(contentVersionId);
2448
+ return parsedBlockId !== null && parsedContentVersionId !== null ? {
2357
2449
  tag: "block_ref",
2358
- blockId: parsed
2450
+ blockId: parsedBlockId,
2451
+ contentVersionId: parsedContentVersionId
2359
2452
  } : null;
2360
2453
  }
2361
2454
  //#endregion
@@ -72308,7 +72401,7 @@ function labelNav() {
72308
72401
  }
72309
72402
  //#endregion
72310
72403
  //#region ../../node_modules/.pnpm/react-day-picker@9.13.0_react@19.2.7/node_modules/react-day-picker/dist/esm/labels/labelNext.js
72311
- const defaultLabel$1 = "Go to the Next Month";
72404
+ const defaultLabel = "Go to the Next Month";
72312
72405
  /**
72313
72406
  * Generates the ARIA label for the "next month" button.
72314
72407
  *
@@ -72320,7 +72413,7 @@ const defaultLabel$1 = "Go to the Next Month";
72320
72413
  * @see https://daypicker.dev/docs/translation#aria-labels
72321
72414
  */
72322
72415
  function labelNext(_month, _options) {
72323
- return defaultLabel$1;
72416
+ return defaultLabel;
72324
72417
  }
72325
72418
  //#endregion
72326
72419
  //#region ../../node_modules/.pnpm/react-day-picker@9.13.0_react@19.2.7/node_modules/react-day-picker/dist/esm/labels/labelPrevious.js
@@ -81197,6 +81290,442 @@ function themeButtonClassName(spec) {
81197
81290
  ].filter(Boolean).join(" ");
81198
81291
  }
81199
81292
  //#endregion
81293
+ //#region ../theme-core/src/tokens/tokens.ts
81294
+ const defaultThemeTokens = {
81295
+ color: {
81296
+ "background/base": "#ffffff",
81297
+ "background/elevated": "#f5f5f5",
81298
+ "surface/base": "#ffffff",
81299
+ "surface/subtle": "#f9f9fb",
81300
+ "surface/inverted": "#111111",
81301
+ "content/primary": "#111111",
81302
+ "content/secondary": "#3f3f46",
81303
+ "accent/primary": "#2563eb",
81304
+ "accent/secondary": "#1d4ed8",
81305
+ "border/base": "#e5e7eb"
81306
+ },
81307
+ typography: {
81308
+ "display/hero": {
81309
+ fontSize: "clamp(48px, 8vw, 96px)",
81310
+ lineHeight: "1.05",
81311
+ fontWeight: "700"
81312
+ },
81313
+ "display/section": {
81314
+ fontSize: "clamp(40px, 6vw, 72px)",
81315
+ lineHeight: "1.1",
81316
+ fontWeight: "700"
81317
+ },
81318
+ "heading/large": {
81319
+ fontSize: "clamp(32px, 4vw, 48px)",
81320
+ lineHeight: "1.2",
81321
+ fontWeight: "600"
81322
+ },
81323
+ "heading/medium": {
81324
+ fontSize: "clamp(24px, 3vw, 32px)",
81325
+ lineHeight: "1.25",
81326
+ fontWeight: "600"
81327
+ },
81328
+ "heading/small": {
81329
+ fontSize: "20px",
81330
+ lineHeight: "1.3",
81331
+ fontWeight: "600"
81332
+ },
81333
+ "body/normal": {
81334
+ fontSize: "16px",
81335
+ lineHeight: "1.6",
81336
+ fontWeight: "400"
81337
+ },
81338
+ "body/small": {
81339
+ fontSize: "14px",
81340
+ lineHeight: "1.5",
81341
+ fontWeight: "400"
81342
+ },
81343
+ caption: {
81344
+ fontSize: "12px",
81345
+ lineHeight: "1.4",
81346
+ fontWeight: "500"
81347
+ }
81348
+ },
81349
+ spacing: {
81350
+ none: "0px",
81351
+ "3xs": "4px",
81352
+ "2xs": "8px",
81353
+ xs: "12px",
81354
+ sm: "16px",
81355
+ md: "24px",
81356
+ lg: "32px",
81357
+ xl: "40px",
81358
+ "2xl": "56px",
81359
+ "3xl": "72px"
81360
+ },
81361
+ radius: {
81362
+ none: "0px",
81363
+ xs: "2px",
81364
+ sm: "4px",
81365
+ md: "8px",
81366
+ lg: "12px",
81367
+ pill: "999px",
81368
+ full: "9999px"
81369
+ },
81370
+ shadow: {
81371
+ none: "none",
81372
+ soft: "0px 2px 8px rgba(15, 23, 42, 0.08)",
81373
+ medium: "0px 6px 18px rgba(15, 23, 42, 0.12)",
81374
+ strong: "0px 12px 36px rgba(15, 23, 42, 0.16)"
81375
+ },
81376
+ palette: {
81377
+ surface: "#ffffff",
81378
+ "neutral-50": "#f9fafb",
81379
+ "neutral-900": "#111827",
81380
+ primary: "#2563eb",
81381
+ "primary-50": "#eff6ff",
81382
+ "primary-500": "#2563eb",
81383
+ "primary-900": "#1d4ed8",
81384
+ secondary: "#9333ea",
81385
+ "secondary-50": "#f3e8ff",
81386
+ "secondary-500": "#9333ea",
81387
+ "secondary-900": "#4c1d95",
81388
+ accent: "#f97316",
81389
+ "accent-50": "#fff7ed",
81390
+ "accent-500": "#f97316",
81391
+ "accent-900": "#7c2d12"
81392
+ }
81393
+ };
81394
+ /**
81395
+ * Project the expanded semantic palette into the token buckets consumed by
81396
+ * runtime transforms. Keep this mapping shared by full themes and the
81397
+ * tokens-only hosted-booking runtime so the two paths cannot drift.
81398
+ */
81399
+ function buildThemeTokens(palette) {
81400
+ const tokens = cloneThemeTokens(defaultThemeTokens);
81401
+ const paletteStrings = {};
81402
+ for (const [key, value] of Object.entries(palette)) if (typeof value === "string" && value.trim().length > 0) paletteStrings[key] = value;
81403
+ tokens.palette = paletteStrings;
81404
+ const resolveColor = (...keys) => {
81405
+ for (const key of keys) {
81406
+ const value = paletteStrings[key];
81407
+ if (value) return value;
81408
+ }
81409
+ };
81410
+ const assignColor = (token, ...keys) => {
81411
+ const value = resolveColor(...keys);
81412
+ if (value) tokens.color[token] = value;
81413
+ };
81414
+ assignColor("background/base", "background", "surface", "primary-50");
81415
+ assignColor("background/elevated", "surface", "background");
81416
+ assignColor("surface/base", "surface", "background");
81417
+ assignColor("surface/subtle", "neutral-100", "surface", "background");
81418
+ assignColor("surface/inverted", "neutral-900", "text", "primary-900");
81419
+ assignColor("content/primary", "text", "body", "neutral-900");
81420
+ assignColor("content/secondary", "mutedText", "secondary", "neutral-600");
81421
+ assignColor("accent/primary", "primary", "accent");
81422
+ assignColor("accent/secondary", "accent", "secondary", "primary-400");
81423
+ assignColor("border/base", "border", "neutral-300");
81424
+ return tokens;
81425
+ }
81426
+ function cloneThemeTokens(source) {
81427
+ return JSON.parse(JSON.stringify(source));
81428
+ }
81429
+ //#endregion
81430
+ //#region ../theme-core/src/palette/utils/opaqueSurfaceColor.ts
81431
+ /** Resolve an opaque colour, compositing alpha against an explicit underlay. */
81432
+ function resolveOpaqueSurfaceHex(reference, palette, underlayReference) {
81433
+ const opaque = resolveConcreteColorHex(reference, palette);
81434
+ if (opaque) return opaque;
81435
+ if (!underlayReference) return null;
81436
+ const underlayHex = resolveConcreteColorHex(underlayReference, palette);
81437
+ if (!underlayHex) return null;
81438
+ const parts = parseColorTokenParts(reference);
81439
+ if (parts.kind === "token" && parts.opacityPercent !== null) {
81440
+ const foregroundHex = normalizePaletteHex(palette[parts.token] ?? "");
81441
+ return foregroundHex ? compositeHex(foregroundHex, underlayHex, parts.opacityPercent / 100) : null;
81442
+ }
81443
+ const rgba = parseRgba(parts.kind === "raw" ? parts.value : reference);
81444
+ return rgba ? compositeHex(rgbToHex$1(rgba.r, rgba.g, rgba.b), underlayHex, rgba.alpha) : null;
81445
+ }
81446
+ /** Resolve a colour reference only when it is already fully opaque. */
81447
+ function resolveConcreteColorHex(reference, palette) {
81448
+ const literalHex = normalizePaletteHex(reference);
81449
+ if (literalHex) return literalHex;
81450
+ const parts = parseColorTokenParts(reference);
81451
+ if (parts.kind === "token") {
81452
+ if (parts.opacityPercent !== null) return null;
81453
+ return normalizePaletteHex(palette[parts.token] ?? "");
81454
+ }
81455
+ const literal = parts.kind === "raw" ? parts.value : reference;
81456
+ if (literal.toLowerCase() === "black") return "#000000";
81457
+ if (literal.toLowerCase() === "white") return "#ffffff";
81458
+ return normalizePaletteHex(literal);
81459
+ }
81460
+ function compositeHex(foregroundHex, underlayHex, alpha) {
81461
+ const foreground = hexToRgb(foregroundHex);
81462
+ const underlay = hexToRgb(underlayHex);
81463
+ const channel = (foregroundValue, underlayValue) => Math.round(foregroundValue * alpha + underlayValue * (1 - alpha));
81464
+ return rgbToHex$1(channel(foreground.r, underlay.r), channel(foreground.g, underlay.g), channel(foreground.b, underlay.b));
81465
+ }
81466
+ function parseRgba(value) {
81467
+ const match = value.trim().match(/^rgba?\(\s*([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*(?:,|\/)\s*([\d.]+%?))?\s*\)$/i);
81468
+ if (!match) return null;
81469
+ const [, rSource, gSource, bSource] = match;
81470
+ if (!rSource || !gSource || !bSource) return null;
81471
+ const r = Number(rSource);
81472
+ const g = Number(gSource);
81473
+ const b = Number(bSource);
81474
+ const alpha = parseAlpha(match[4]);
81475
+ if (alpha === null || !isRgbChannel(r) || !isRgbChannel(g) || !isRgbChannel(b)) return null;
81476
+ return {
81477
+ r,
81478
+ g,
81479
+ b,
81480
+ alpha
81481
+ };
81482
+ }
81483
+ function parseAlpha(source) {
81484
+ if (!source) return 1;
81485
+ const value = source.endsWith("%") ? Number(source.slice(0, -1)) / 100 : Number(source);
81486
+ return Number.isFinite(value) && value >= 0 && value <= 1 ? value : null;
81487
+ }
81488
+ function isRgbChannel(value) {
81489
+ return Number.isFinite(value) && value >= 0 && value <= 255;
81490
+ }
81491
+ //#endregion
81492
+ //#region ../theme-core/src/surfaces/resolveSurfaceForeground.ts
81493
+ const FALLBACK_TEXT_TOKENS = [
81494
+ "text",
81495
+ "primaryForeground",
81496
+ "secondaryForeground",
81497
+ "accentForeground",
81498
+ "neutral-950",
81499
+ "neutral-50"
81500
+ ];
81501
+ const CANONICAL_EQUIVALENT_TOKEN_ORDER = [
81502
+ "text",
81503
+ "primaryForeground",
81504
+ "secondaryForeground",
81505
+ "accentForeground",
81506
+ "onPrimary",
81507
+ "onSecondary",
81508
+ "onAccent",
81509
+ "onSurface",
81510
+ "neutral-950",
81511
+ "neutral-50"
81512
+ ];
81513
+ /**
81514
+ * Resolve the complete foreground contract for an effective page/section or
81515
+ * site-chrome background.
81516
+ *
81517
+ * Deterministic colour backgrounds are validated at ordinary-text WCAG AA.
81518
+ * Existing image/video/gradient treatments are trusted only when they already
81519
+ * carry a usable foreground token; this function deliberately does not claim
81520
+ * pixel-level contrast proof for media.
81521
+ */
81522
+ function resolveSurfaceForeground(background, palette, candidates = {}, options = {}) {
81523
+ const authored = foregroundCandidates(background, candidates);
81524
+ if (background === null) return resolveDeterministicForeground("background", {
81525
+ ...authored,
81526
+ text: authored.text ?? "text"
81527
+ }, palette);
81528
+ if (background.type === "color") {
81529
+ const backgroundReference = normalizedReference(background.color);
81530
+ if (!backgroundReference || backgroundReference === "transparent") return { kind: "unknown" };
81531
+ return resolveDeterministicForeground(backgroundReference, authored, palette, options.compositeBase);
81532
+ }
81533
+ return resolveTrustedTreatmentForeground(authored, palette);
81534
+ }
81535
+ function foregroundCandidates(background, overrides) {
81536
+ return {
81537
+ text: overrides.text ?? background?.textColor ?? null,
81538
+ heading: overrides.heading ?? background?.headingColor ?? null,
81539
+ link: overrides.link ?? background?.linkColor ?? null,
81540
+ supporting: overrides.supporting ?? background?.supportingColor ?? null
81541
+ };
81542
+ }
81543
+ function resolveDeterministicForeground(backgroundReference, candidates, palette, compositeBase) {
81544
+ const backgroundHex = resolveOpaqueSurfaceHex(backgroundReference, palette, compositeBase);
81545
+ if (!backgroundHex) return resolveTrustedTreatmentForeground(candidates, palette);
81546
+ const backgroundToken = paletteToken(backgroundReference);
81547
+ const derivedOnToken = backgroundToken ? onColorTokenName(backgroundToken) : null;
81548
+ const text = firstSafeToken([
81549
+ candidates.text,
81550
+ derivedOnToken,
81551
+ ...FALLBACK_TEXT_TOKENS
81552
+ ], backgroundHex, palette, derivedOnToken ? [derivedOnToken] : []);
81553
+ if (!text) return { kind: "unknown" };
81554
+ return {
81555
+ kind: "known",
81556
+ roles: {
81557
+ text,
81558
+ heading: safeTokenOr(candidates.heading, text, backgroundHex, palette),
81559
+ link: safeTokenOr(candidates.link, text, backgroundHex, palette),
81560
+ supporting: safeTokenOr(candidates.supporting, text, backgroundHex, palette)
81561
+ }
81562
+ };
81563
+ }
81564
+ function resolveTrustedTreatmentForeground(candidates, palette) {
81565
+ const text = canonicalToken(candidates.text, palette);
81566
+ if (!text) return { kind: "unknown" };
81567
+ return {
81568
+ kind: "known",
81569
+ roles: {
81570
+ text,
81571
+ heading: canonicalToken(candidates.heading, palette) ?? text,
81572
+ link: canonicalToken(candidates.link, palette) ?? text,
81573
+ supporting: canonicalToken(candidates.supporting, palette) ?? text
81574
+ }
81575
+ };
81576
+ }
81577
+ function safeTokenOr(candidate, fallback, backgroundHex, palette) {
81578
+ return firstSafeToken([candidate], backgroundHex, palette) ?? fallback;
81579
+ }
81580
+ function firstSafeToken(candidates, backgroundHex, palette, preferredEquivalentTokens = []) {
81581
+ const seen = /* @__PURE__ */ new Set();
81582
+ for (const candidate of candidates) {
81583
+ const token = canonicalToken(candidate, palette, preferredEquivalentTokens);
81584
+ if (!token || seen.has(token)) continue;
81585
+ seen.add(token);
81586
+ const foregroundHex = resolveConcreteColorHex(token, palette);
81587
+ if (foregroundHex && contrastRatio$1(backgroundHex, foregroundHex) >= 4.5) return token;
81588
+ }
81589
+ return null;
81590
+ }
81591
+ function canonicalToken(reference, palette, preferredEquivalentTokens = []) {
81592
+ const normalized = normalizedReference(reference);
81593
+ if (!normalized || normalized === "transparent") return null;
81594
+ const direct = parseThemeColorToken(normalized);
81595
+ if (direct && resolveConcreteColorHex(direct, palette)) return direct;
81596
+ const referenceHex = resolveConcreteColorHex(normalized, palette);
81597
+ if (!referenceHex) return null;
81598
+ const equivalent = [
81599
+ ...preferredEquivalentTokens,
81600
+ ...CANONICAL_EQUIVALENT_TOKEN_ORDER,
81601
+ ...Object.keys(palette).sort()
81602
+ ].find((token) => {
81603
+ const parsed = parseThemeColorToken(token);
81604
+ return parsed !== null && resolveConcreteColorHex(parsed, palette)?.toLowerCase() === referenceHex.toLowerCase();
81605
+ });
81606
+ return equivalent ? parseThemeColorToken(equivalent) : null;
81607
+ }
81608
+ function paletteToken(reference) {
81609
+ const parts = parseColorTokenParts(reference);
81610
+ return parts.kind === "token" && parts.opacityPercent === null ? parts.token : null;
81611
+ }
81612
+ function normalizedReference(value) {
81613
+ if (typeof value !== "string") return null;
81614
+ const normalized = value.trim();
81615
+ return normalized.length > 0 ? normalized : null;
81616
+ }
81617
+ //#endregion
81618
+ //#region ../theme-core/src/utils/colorStyles.ts
81619
+ const COLOR_VAR_PREFIX = "--tb-";
81620
+ function parseToken(source) {
81621
+ const parts = parseColorTokenParts(source);
81622
+ if (parts.kind === "raw") return {
81623
+ token: source,
81624
+ raw: parts.value
81625
+ };
81626
+ if (parts.kind === "invalid") return { token: source };
81627
+ return {
81628
+ token: parts.token,
81629
+ opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
81630
+ };
81631
+ }
81632
+ const SURFACE_TEXT_ROLE_BY_TOKEN = {
81633
+ text: "text",
81634
+ heading: "heading",
81635
+ mutedText: "supporting"
81636
+ };
81637
+ function surfaceRoleForTextToken(token) {
81638
+ return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
81639
+ }
81640
+ /**
81641
+ * Converts a theme color token to a CSS rgb value.
81642
+ * This is the primary function for applying theme colors to CSS properties.
81643
+ *
81644
+ * @param token - Theme color token (semantic, with opacity, or raw)
81645
+ * @returns CSS color value (rgb with optional alpha, or raw CSS value)
81646
+ *
81647
+ * @example
81648
+ * rgbColorValue('primary') // => 'rgb(var(--tb-primary))'
81649
+ * rgbColorValue('surface/95') // => 'rgb(var(--tb-surface) / 0.95)'
81650
+ * rgbColorValue('raw:transparent') // => 'transparent'
81651
+ */
81652
+ function rgbColorValue(token) {
81653
+ const { token: baseToken, opacityPercent, raw } = parseToken(token);
81654
+ if (raw) return raw;
81655
+ const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
81656
+ if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
81657
+ return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
81658
+ }
81659
+ /**
81660
+ * Converts a theme color token to a CSS variable reference.
81661
+ * Note: This returns the base variable without rgb() wrapper, ignoring opacity.
81662
+ *
81663
+ * @param token - Theme color token
81664
+ * @returns CSS variable reference
81665
+ *
81666
+ * @example
81667
+ * cssVarValue('primary') // => 'var(--tb-primary)'
81668
+ * cssVarValue('surface/95') // => 'var(--tb-surface)' (opacity ignored)
81669
+ */
81670
+ function cssVarValue(token) {
81671
+ const { token: baseToken } = parseToken(token);
81672
+ return `var(${COLOR_VAR_PREFIX}${baseToken})`;
81673
+ }
81674
+ /**
81675
+ * Creates a backgroundColor style object from a theme token.
81676
+ *
81677
+ * @param token - Theme color token
81678
+ * @returns CSSProperties with backgroundColor
81679
+ */
81680
+ function backgroundColorStyle(token) {
81681
+ return { backgroundColor: rgbColorValue(token) };
81682
+ }
81683
+ /**
81684
+ * Creates a text color style object from a theme token.
81685
+ *
81686
+ * @param token - Theme color token
81687
+ * @returns CSSProperties with color
81688
+ */
81689
+ function textColorStyle(token) {
81690
+ const parsed = parseToken(token);
81691
+ const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
81692
+ if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
81693
+ return { color: rgbColorValue(token) };
81694
+ }
81695
+ /**
81696
+ * Creates a border color style object from a theme token.
81697
+ *
81698
+ * @param token - Theme color token
81699
+ * @returns CSSProperties with borderColor
81700
+ */
81701
+ function borderColorStyle(token) {
81702
+ return { borderColor: rgbColorValue(token) };
81703
+ }
81704
+ /**
81705
+ * Merges multiple CSSProperties objects into a single object.
81706
+ * Later styles override earlier ones. Undefined values are filtered out.
81707
+ * Returns undefined if the merged result is empty.
81708
+ *
81709
+ * @param styles - Variable number of CSSProperties objects (can be undefined)
81710
+ * @returns Merged CSSProperties object, or undefined if empty
81711
+ *
81712
+ * @example
81713
+ * mergeStyles(
81714
+ * backgroundColorStyle('surface'),
81715
+ * textColorStyle('text'),
81716
+ * borderColorStyle('border')
81717
+ * )
81718
+ * // => { backgroundColor: '...', color: '...', borderColor: '...' }
81719
+ */
81720
+ function mergeStyles(...styles) {
81721
+ const merged = {};
81722
+ for (const style of styles) {
81723
+ if (!style) continue;
81724
+ Object.assign(merged, style);
81725
+ }
81726
+ return Object.keys(merged).length ? merged : void 0;
81727
+ }
81728
+ //#endregion
81200
81729
  //#region ../theme-core/src/tokens/resolver.ts
81201
81730
  /**
81202
81731
  * Resolves theme tokens to CSS variables
@@ -81223,7 +81752,7 @@ var TokenResolver = class {
81223
81752
  */
81224
81753
  getColor(token) {
81225
81754
  if (!this.tokenExists(token)) console.warn(`[TokenResolver] Color token "${token}" not found in theme palette. Button may render incorrectly.`);
81226
- return `rgb(var(--tb-${token}))`;
81755
+ return rgbColorValue(token);
81227
81756
  }
81228
81757
  /**
81229
81758
  * Adjust a color token's shade by a given amount
@@ -82695,305 +83224,6 @@ function affordanceIconMaskUrl(iconId) {
82695
83224
  return `data:image/svg+xml,${`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'>${paths.map((d) => `<path d='${d}'/>`).join("")}</svg>`.replace(/"/g, "'").replace(/#/g, "%23").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ")}`;
82696
83225
  }
82697
83226
  //#endregion
82698
- //#region ../theme-core/src/palette/utils/opaqueSurfaceColor.ts
82699
- /** Resolve an opaque colour, compositing alpha against an explicit underlay. */
82700
- function resolveOpaqueSurfaceHex(reference, palette, underlayReference) {
82701
- const opaque = resolveConcreteColorHex(reference, palette);
82702
- if (opaque) return opaque;
82703
- if (!underlayReference) return null;
82704
- const underlayHex = resolveConcreteColorHex(underlayReference, palette);
82705
- if (!underlayHex) return null;
82706
- const parts = parseColorTokenParts(reference);
82707
- if (parts.kind === "token" && parts.opacityPercent !== null) {
82708
- const foregroundHex = normalizePaletteHex(palette[parts.token] ?? "");
82709
- return foregroundHex ? compositeHex(foregroundHex, underlayHex, parts.opacityPercent / 100) : null;
82710
- }
82711
- const rgba = parseRgba(parts.kind === "raw" ? parts.value : reference);
82712
- return rgba ? compositeHex(rgbToHex$1(rgba.r, rgba.g, rgba.b), underlayHex, rgba.alpha) : null;
82713
- }
82714
- /** Resolve a colour reference only when it is already fully opaque. */
82715
- function resolveConcreteColorHex(reference, palette) {
82716
- const literalHex = normalizePaletteHex(reference);
82717
- if (literalHex) return literalHex;
82718
- const parts = parseColorTokenParts(reference);
82719
- if (parts.kind === "token") {
82720
- if (parts.opacityPercent !== null) return null;
82721
- return normalizePaletteHex(palette[parts.token] ?? "");
82722
- }
82723
- const literal = parts.kind === "raw" ? parts.value : reference;
82724
- if (literal.toLowerCase() === "black") return "#000000";
82725
- if (literal.toLowerCase() === "white") return "#ffffff";
82726
- return normalizePaletteHex(literal);
82727
- }
82728
- function compositeHex(foregroundHex, underlayHex, alpha) {
82729
- const foreground = hexToRgb(foregroundHex);
82730
- const underlay = hexToRgb(underlayHex);
82731
- const channel = (foregroundValue, underlayValue) => Math.round(foregroundValue * alpha + underlayValue * (1 - alpha));
82732
- return rgbToHex$1(channel(foreground.r, underlay.r), channel(foreground.g, underlay.g), channel(foreground.b, underlay.b));
82733
- }
82734
- function parseRgba(value) {
82735
- const match = value.trim().match(/^rgba?\(\s*([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*(?:,|\/)\s*([\d.]+%?))?\s*\)$/i);
82736
- if (!match) return null;
82737
- const [, rSource, gSource, bSource] = match;
82738
- if (!rSource || !gSource || !bSource) return null;
82739
- const r = Number(rSource);
82740
- const g = Number(gSource);
82741
- const b = Number(bSource);
82742
- const alpha = parseAlpha(match[4]);
82743
- if (alpha === null || !isRgbChannel(r) || !isRgbChannel(g) || !isRgbChannel(b)) return null;
82744
- return {
82745
- r,
82746
- g,
82747
- b,
82748
- alpha
82749
- };
82750
- }
82751
- function parseAlpha(source) {
82752
- if (!source) return 1;
82753
- const value = source.endsWith("%") ? Number(source.slice(0, -1)) / 100 : Number(source);
82754
- return Number.isFinite(value) && value >= 0 && value <= 1 ? value : null;
82755
- }
82756
- function isRgbChannel(value) {
82757
- return Number.isFinite(value) && value >= 0 && value <= 255;
82758
- }
82759
- //#endregion
82760
- //#region ../theme-core/src/surfaces/resolveSurfaceForeground.ts
82761
- const FALLBACK_TEXT_TOKENS = [
82762
- "text",
82763
- "primaryForeground",
82764
- "secondaryForeground",
82765
- "accentForeground",
82766
- "neutral-950",
82767
- "neutral-50"
82768
- ];
82769
- const CANONICAL_EQUIVALENT_TOKEN_ORDER = [
82770
- "text",
82771
- "primaryForeground",
82772
- "secondaryForeground",
82773
- "accentForeground",
82774
- "onPrimary",
82775
- "onSecondary",
82776
- "onAccent",
82777
- "onSurface",
82778
- "neutral-950",
82779
- "neutral-50"
82780
- ];
82781
- /**
82782
- * Resolve the complete foreground contract for an effective page/section or
82783
- * site-chrome background.
82784
- *
82785
- * Deterministic colour backgrounds are validated at ordinary-text WCAG AA.
82786
- * Existing image/video/gradient treatments are trusted only when they already
82787
- * carry a usable foreground token; this function deliberately does not claim
82788
- * pixel-level contrast proof for media.
82789
- */
82790
- function resolveSurfaceForeground(background, palette, candidates = {}, options = {}) {
82791
- const authored = foregroundCandidates(background, candidates);
82792
- if (background === null) return resolveDeterministicForeground("background", {
82793
- ...authored,
82794
- text: authored.text ?? "text"
82795
- }, palette);
82796
- if (background.type === "color") {
82797
- const backgroundReference = normalizedReference(background.color);
82798
- if (!backgroundReference || backgroundReference === "transparent") return { kind: "unknown" };
82799
- return resolveDeterministicForeground(backgroundReference, authored, palette, options.compositeBase);
82800
- }
82801
- return resolveTrustedTreatmentForeground(authored, palette);
82802
- }
82803
- function foregroundCandidates(background, overrides) {
82804
- return {
82805
- text: overrides.text ?? background?.textColor ?? null,
82806
- heading: overrides.heading ?? background?.headingColor ?? null,
82807
- link: overrides.link ?? background?.linkColor ?? null,
82808
- supporting: overrides.supporting ?? background?.supportingColor ?? null
82809
- };
82810
- }
82811
- function resolveDeterministicForeground(backgroundReference, candidates, palette, compositeBase) {
82812
- const backgroundHex = resolveOpaqueSurfaceHex(backgroundReference, palette, compositeBase);
82813
- if (!backgroundHex) return resolveTrustedTreatmentForeground(candidates, palette);
82814
- const backgroundToken = paletteToken(backgroundReference);
82815
- const derivedOnToken = backgroundToken ? onColorTokenName(backgroundToken) : null;
82816
- const text = firstSafeToken([
82817
- candidates.text,
82818
- derivedOnToken,
82819
- ...FALLBACK_TEXT_TOKENS
82820
- ], backgroundHex, palette, derivedOnToken ? [derivedOnToken] : []);
82821
- if (!text) return { kind: "unknown" };
82822
- return {
82823
- kind: "known",
82824
- roles: {
82825
- text,
82826
- heading: safeTokenOr(candidates.heading, text, backgroundHex, palette),
82827
- link: safeTokenOr(candidates.link, text, backgroundHex, palette),
82828
- supporting: safeTokenOr(candidates.supporting, text, backgroundHex, palette)
82829
- }
82830
- };
82831
- }
82832
- function resolveTrustedTreatmentForeground(candidates, palette) {
82833
- const text = canonicalToken(candidates.text, palette);
82834
- if (!text) return { kind: "unknown" };
82835
- return {
82836
- kind: "known",
82837
- roles: {
82838
- text,
82839
- heading: canonicalToken(candidates.heading, palette) ?? text,
82840
- link: canonicalToken(candidates.link, palette) ?? text,
82841
- supporting: canonicalToken(candidates.supporting, palette) ?? text
82842
- }
82843
- };
82844
- }
82845
- function safeTokenOr(candidate, fallback, backgroundHex, palette) {
82846
- return firstSafeToken([candidate], backgroundHex, palette) ?? fallback;
82847
- }
82848
- function firstSafeToken(candidates, backgroundHex, palette, preferredEquivalentTokens = []) {
82849
- const seen = /* @__PURE__ */ new Set();
82850
- for (const candidate of candidates) {
82851
- const token = canonicalToken(candidate, palette, preferredEquivalentTokens);
82852
- if (!token || seen.has(token)) continue;
82853
- seen.add(token);
82854
- const foregroundHex = resolveConcreteColorHex(token, palette);
82855
- if (foregroundHex && contrastRatio$1(backgroundHex, foregroundHex) >= 4.5) return token;
82856
- }
82857
- return null;
82858
- }
82859
- function canonicalToken(reference, palette, preferredEquivalentTokens = []) {
82860
- const normalized = normalizedReference(reference);
82861
- if (!normalized || normalized === "transparent") return null;
82862
- const direct = parseThemeColorToken(normalized);
82863
- if (direct && resolveConcreteColorHex(direct, palette)) return direct;
82864
- const referenceHex = resolveConcreteColorHex(normalized, palette);
82865
- if (!referenceHex) return null;
82866
- const equivalent = [
82867
- ...preferredEquivalentTokens,
82868
- ...CANONICAL_EQUIVALENT_TOKEN_ORDER,
82869
- ...Object.keys(palette).sort()
82870
- ].find((token) => {
82871
- const parsed = parseThemeColorToken(token);
82872
- return parsed !== null && resolveConcreteColorHex(parsed, palette)?.toLowerCase() === referenceHex.toLowerCase();
82873
- });
82874
- return equivalent ? parseThemeColorToken(equivalent) : null;
82875
- }
82876
- function paletteToken(reference) {
82877
- const parts = parseColorTokenParts(reference);
82878
- return parts.kind === "token" && parts.opacityPercent === null ? parts.token : null;
82879
- }
82880
- function normalizedReference(value) {
82881
- if (typeof value !== "string") return null;
82882
- const normalized = value.trim();
82883
- return normalized.length > 0 ? normalized : null;
82884
- }
82885
- //#endregion
82886
- //#region ../theme-core/src/utils/colorStyles.ts
82887
- const COLOR_VAR_PREFIX = "--tb-";
82888
- function parseToken(source) {
82889
- const parts = parseColorTokenParts(source);
82890
- if (parts.kind === "raw") return {
82891
- token: source,
82892
- raw: parts.value
82893
- };
82894
- if (parts.kind === "invalid") return { token: source };
82895
- return {
82896
- token: parts.token,
82897
- opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
82898
- };
82899
- }
82900
- const SURFACE_TEXT_ROLE_BY_TOKEN = {
82901
- text: "text",
82902
- heading: "heading",
82903
- mutedText: "supporting"
82904
- };
82905
- function surfaceRoleForTextToken(token) {
82906
- return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
82907
- }
82908
- /**
82909
- * Converts a theme color token to a CSS rgb value.
82910
- * This is the primary function for applying theme colors to CSS properties.
82911
- *
82912
- * @param token - Theme color token (semantic, with opacity, or raw)
82913
- * @returns CSS color value (rgb with optional alpha, or raw CSS value)
82914
- *
82915
- * @example
82916
- * rgbColorValue('primary') // => 'rgb(var(--tb-primary))'
82917
- * rgbColorValue('surface/95') // => 'rgb(var(--tb-surface) / 0.95)'
82918
- * rgbColorValue('raw:transparent') // => 'transparent'
82919
- */
82920
- function rgbColorValue(token) {
82921
- const { token: baseToken, opacityPercent, raw } = parseToken(token);
82922
- if (raw) return raw;
82923
- const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
82924
- if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
82925
- return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
82926
- }
82927
- /**
82928
- * Converts a theme color token to a CSS variable reference.
82929
- * Note: This returns the base variable without rgb() wrapper, ignoring opacity.
82930
- *
82931
- * @param token - Theme color token
82932
- * @returns CSS variable reference
82933
- *
82934
- * @example
82935
- * cssVarValue('primary') // => 'var(--tb-primary)'
82936
- * cssVarValue('surface/95') // => 'var(--tb-surface)' (opacity ignored)
82937
- */
82938
- function cssVarValue(token) {
82939
- const { token: baseToken } = parseToken(token);
82940
- return `var(${COLOR_VAR_PREFIX}${baseToken})`;
82941
- }
82942
- /**
82943
- * Creates a backgroundColor style object from a theme token.
82944
- *
82945
- * @param token - Theme color token
82946
- * @returns CSSProperties with backgroundColor
82947
- */
82948
- function backgroundColorStyle(token) {
82949
- return { backgroundColor: rgbColorValue(token) };
82950
- }
82951
- /**
82952
- * Creates a text color style object from a theme token.
82953
- *
82954
- * @param token - Theme color token
82955
- * @returns CSSProperties with color
82956
- */
82957
- function textColorStyle(token) {
82958
- const parsed = parseToken(token);
82959
- const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
82960
- if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
82961
- return { color: rgbColorValue(token) };
82962
- }
82963
- /**
82964
- * Creates a border color style object from a theme token.
82965
- *
82966
- * @param token - Theme color token
82967
- * @returns CSSProperties with borderColor
82968
- */
82969
- function borderColorStyle(token) {
82970
- return { borderColor: rgbColorValue(token) };
82971
- }
82972
- /**
82973
- * Merges multiple CSSProperties objects into a single object.
82974
- * Later styles override earlier ones. Undefined values are filtered out.
82975
- * Returns undefined if the merged result is empty.
82976
- *
82977
- * @param styles - Variable number of CSSProperties objects (can be undefined)
82978
- * @returns Merged CSSProperties object, or undefined if empty
82979
- *
82980
- * @example
82981
- * mergeStyles(
82982
- * backgroundColorStyle('surface'),
82983
- * textColorStyle('text'),
82984
- * borderColorStyle('border')
82985
- * )
82986
- * // => { backgroundColor: '...', color: '...', borderColor: '...' }
82987
- */
82988
- function mergeStyles(...styles) {
82989
- const merged = {};
82990
- for (const style of styles) {
82991
- if (!style) continue;
82992
- Object.assign(merged, style);
82993
- }
82994
- return Object.keys(merged).length ? merged : void 0;
82995
- }
82996
- //#endregion
82997
83227
  //#region ../theme-core/src/utils/tokenOrColor.ts
82998
83228
  /**
82999
83229
  * Runtime color-reference fields intentionally accept any string, including
@@ -87863,8 +88093,14 @@ const headerInteractionPillPaletteValues = [
87863
88093
  "accent"
87864
88094
  ];
87865
88095
  /**
87866
- * "No pill": the nav link keeps whatever hover treatment its recipe compiled,
87867
- * and the palette step writes nothing at all (#1318 Phase 4c decision 2).
88096
+ * "No pill": a colour change on hover rather than a filled background.
88097
+ *
88098
+ * The palette step writes nothing (#1318 Phase 4c decision 2), but on a
88099
+ * foreground-adaptive style that is not the end of the story: the hover colour
88100
+ * is derived from the resolved opening foreground at render time, paired with
88101
+ * the resting colour, so `quiet` resolves to a definite colour rather than to
88102
+ * whatever brand token the recipe happened to name. See
88103
+ * `applyOpeningForeground`.
87868
88104
  *
87869
88105
  * It exists because "a colour change on hover, no filled background" was not
87870
88106
  * expressible in the vocabulary, and it is what
@@ -88944,6 +89180,28 @@ const bookingRequirementConsentFieldSchema = z.object({
88944
89180
  const bookingConsentCarrierSchema = z.union([blockBookingConsentSchema, bookingRequirementConsentFieldSchema]);
88945
89181
  //#endregion
88946
89182
  //#region ../blocks/src/system/blocks/events/shared/schemas.ts
89183
+ const publicEventLoaderQuerySchemaShape = {
89184
+ limit: z.string().optional(),
89185
+ from: z.string().optional(),
89186
+ to: z.string().optional(),
89187
+ afterStartsAt: z.string().optional(),
89188
+ afterId: z.string().optional(),
89189
+ categoryIds: z.string().optional(),
89190
+ venueId: z.string().optional(),
89191
+ staffMemberId: z.string().optional(),
89192
+ eventSurface: z.string().optional(),
89193
+ eventPresets: z.string().optional(),
89194
+ scheduleScope: z.string().optional(),
89195
+ seriesId: z.string().optional(),
89196
+ seriesIds: z.string().optional(),
89197
+ runSeriesId: z.string().optional(),
89198
+ seriesSlug: z.string().optional(),
89199
+ includeOccurrenceId: z.string().optional(),
89200
+ readIntent: z.string().optional(),
89201
+ detailLevel: z.string().optional(),
89202
+ readProfile: z.string().optional()
89203
+ };
89204
+ z.object(publicEventLoaderQuerySchemaShape);
88947
89205
  const eventPresetSchema = z.enum([
88948
89206
  "event",
88949
89207
  "class",
@@ -89454,7 +89712,7 @@ function planCalendarMonthRanges(months) {
89454
89712
  const sorted = [...new Set(months)].sort();
89455
89713
  const ranges = [];
89456
89714
  for (const month of sorted) {
89457
- const previous = ranges.at(-1);
89715
+ const previous = ranges[ranges.length - 1];
89458
89716
  const end = calendarMonthKey$1(addMonths(calendarMonthDate(month), 1));
89459
89717
  if (previous?.endMonthExclusive === month && monthNumber(end) - monthNumber(previous.startMonth) <= 3) previous.endMonthExclusive = end;
89460
89718
  else ranges.push({
@@ -89973,6 +90231,9 @@ function isApiSuccessResponse(json) {
89973
90231
  * });
89974
90232
  * ```
89975
90233
  */
90234
+ function publicEventsQuery(params) {
90235
+ return { ...params ?? {} };
90236
+ }
89976
90237
  function createBlockApi(config) {
89977
90238
  const { baseUrl, auth, authOverrides, siteId, isPreview } = config;
89978
90239
  const dedup = createDeduplicationCache({ ttl: 100 });
@@ -90125,23 +90386,7 @@ function createBlockApi(config) {
90125
90386
  const path = buildPath(EVENTS.list.path, { siteId: sid });
90126
90387
  return request(EVENTS.list, {
90127
90388
  path,
90128
- query: {
90129
- from: params?.from,
90130
- to: params?.to,
90131
- afterStartsAt: params?.afterStartsAt,
90132
- afterId: params?.afterId,
90133
- limit: params?.limit,
90134
- stage: params?.stage,
90135
- eventSurface: params?.eventSurface,
90136
- eventPresets: params?.eventPresets,
90137
- scheduleScope: params?.scheduleScope,
90138
- categoryIds: params?.categoryIds,
90139
- venueId: params?.venueId,
90140
- staffMemberId: params?.staffMemberId,
90141
- seriesId: params?.seriesId,
90142
- includeOccurrenceId: params?.includeOccurrenceId,
90143
- detailLevel: params?.detailLevel
90144
- }
90389
+ query: publicEventsQuery(params)
90145
90390
  });
90146
90391
  },
90147
90392
  async getEventFilters(params) {
@@ -90205,7 +90450,7 @@ function createBlockApi(config) {
90205
90450
  const path = buildPath(WHOLE_SERIES.waitlistClaimResolve.path, { siteId: sid });
90206
90451
  return request(WHOLE_SERIES.waitlistClaimResolve, {
90207
90452
  path,
90208
- body: { claimToken: data.claimToken }
90453
+ body: data
90209
90454
  });
90210
90455
  },
90211
90456
  async getDiscountsAvailable(data) {
@@ -95384,7 +95629,7 @@ const publicPricingSchema = z.object({
95384
95629
  singlePriceAmount: z.number().nullable(),
95385
95630
  slidingMinAmount: z.number().nullable(),
95386
95631
  slidingMaxAmount: z.number().nullable(),
95387
- options: z.array(publicPricingOptionSchema)
95632
+ options: z.array(publicPricingOptionSchema).readonly()
95388
95633
  });
95389
95634
  const publicAppointmentServiceSchema = z.object({
95390
95635
  id: z.string().min(1),
@@ -103283,143 +103528,6 @@ function assertUniqueLayoutProfileKeys(profiles) {
103283
103528
  if (duplicates.length > 0) throw new Error(`Duplicate block layout design profiles: ${duplicates.join(", ")}`);
103284
103529
  }
103285
103530
  //#endregion
103286
- //#region ../theme-core/src/tokens/tokens.ts
103287
- const defaultThemeTokens = {
103288
- color: {
103289
- "background/base": "#ffffff",
103290
- "background/elevated": "#f5f5f5",
103291
- "surface/base": "#ffffff",
103292
- "surface/subtle": "#f9f9fb",
103293
- "surface/inverted": "#111111",
103294
- "content/primary": "#111111",
103295
- "content/secondary": "#3f3f46",
103296
- "accent/primary": "#2563eb",
103297
- "accent/secondary": "#1d4ed8",
103298
- "border/base": "#e5e7eb"
103299
- },
103300
- typography: {
103301
- "display/hero": {
103302
- fontSize: "clamp(48px, 8vw, 96px)",
103303
- lineHeight: "1.05",
103304
- fontWeight: "700"
103305
- },
103306
- "display/section": {
103307
- fontSize: "clamp(40px, 6vw, 72px)",
103308
- lineHeight: "1.1",
103309
- fontWeight: "700"
103310
- },
103311
- "heading/large": {
103312
- fontSize: "clamp(32px, 4vw, 48px)",
103313
- lineHeight: "1.2",
103314
- fontWeight: "600"
103315
- },
103316
- "heading/medium": {
103317
- fontSize: "clamp(24px, 3vw, 32px)",
103318
- lineHeight: "1.25",
103319
- fontWeight: "600"
103320
- },
103321
- "heading/small": {
103322
- fontSize: "20px",
103323
- lineHeight: "1.3",
103324
- fontWeight: "600"
103325
- },
103326
- "body/normal": {
103327
- fontSize: "16px",
103328
- lineHeight: "1.6",
103329
- fontWeight: "400"
103330
- },
103331
- "body/small": {
103332
- fontSize: "14px",
103333
- lineHeight: "1.5",
103334
- fontWeight: "400"
103335
- },
103336
- caption: {
103337
- fontSize: "12px",
103338
- lineHeight: "1.4",
103339
- fontWeight: "500"
103340
- }
103341
- },
103342
- spacing: {
103343
- none: "0px",
103344
- "3xs": "4px",
103345
- "2xs": "8px",
103346
- xs: "12px",
103347
- sm: "16px",
103348
- md: "24px",
103349
- lg: "32px",
103350
- xl: "40px",
103351
- "2xl": "56px",
103352
- "3xl": "72px"
103353
- },
103354
- radius: {
103355
- none: "0px",
103356
- xs: "2px",
103357
- sm: "4px",
103358
- md: "8px",
103359
- lg: "12px",
103360
- pill: "999px",
103361
- full: "9999px"
103362
- },
103363
- shadow: {
103364
- none: "none",
103365
- soft: "0px 2px 8px rgba(15, 23, 42, 0.08)",
103366
- medium: "0px 6px 18px rgba(15, 23, 42, 0.12)",
103367
- strong: "0px 12px 36px rgba(15, 23, 42, 0.16)"
103368
- },
103369
- palette: {
103370
- surface: "#ffffff",
103371
- "neutral-50": "#f9fafb",
103372
- "neutral-900": "#111827",
103373
- primary: "#2563eb",
103374
- "primary-50": "#eff6ff",
103375
- "primary-500": "#2563eb",
103376
- "primary-900": "#1d4ed8",
103377
- secondary: "#9333ea",
103378
- "secondary-50": "#f3e8ff",
103379
- "secondary-500": "#9333ea",
103380
- "secondary-900": "#4c1d95",
103381
- accent: "#f97316",
103382
- "accent-50": "#fff7ed",
103383
- "accent-500": "#f97316",
103384
- "accent-900": "#7c2d12"
103385
- }
103386
- };
103387
- /**
103388
- * Project the expanded semantic palette into the token buckets consumed by
103389
- * runtime transforms. Keep this mapping shared by full themes and the
103390
- * tokens-only hosted-booking runtime so the two paths cannot drift.
103391
- */
103392
- function buildThemeTokens(palette) {
103393
- const tokens = cloneThemeTokens(defaultThemeTokens);
103394
- const paletteStrings = {};
103395
- for (const [key, value] of Object.entries(palette)) if (typeof value === "string" && value.trim().length > 0) paletteStrings[key] = value;
103396
- tokens.palette = paletteStrings;
103397
- const resolveColor = (...keys) => {
103398
- for (const key of keys) {
103399
- const value = paletteStrings[key];
103400
- if (value) return value;
103401
- }
103402
- };
103403
- const assignColor = (token, ...keys) => {
103404
- const value = resolveColor(...keys);
103405
- if (value) tokens.color[token] = value;
103406
- };
103407
- assignColor("background/base", "background", "surface", "primary-50");
103408
- assignColor("background/elevated", "surface", "background");
103409
- assignColor("surface/base", "surface", "background");
103410
- assignColor("surface/subtle", "neutral-100", "surface", "background");
103411
- assignColor("surface/inverted", "neutral-900", "text", "primary-900");
103412
- assignColor("content/primary", "text", "body", "neutral-900");
103413
- assignColor("content/secondary", "mutedText", "secondary", "neutral-600");
103414
- assignColor("accent/primary", "primary", "accent");
103415
- assignColor("accent/secondary", "accent", "secondary", "primary-400");
103416
- assignColor("border/base", "border", "neutral-300");
103417
- return tokens;
103418
- }
103419
- function cloneThemeTokens(source) {
103420
- return JSON.parse(JSON.stringify(source));
103421
- }
103422
- //#endregion
103423
103531
  //#region ../theme-core/src/site-styles/backgroundAppearance.ts
103424
103532
  const backgroundAppearanceQuickColorTokenNames = [
103425
103533
  "background",
@@ -118805,6 +118913,44 @@ function getRegistrationStartAt(event, occurrenceContext) {
118805
118913
  return event?.registrationSubject.kind === "wholeSeries" ? event.startsAt : occurrenceContext?.startsAt ?? event?.startsAt;
118806
118914
  }
118807
118915
  //#endregion
118916
+ //#region ../blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.ts
118917
+ const { limit: _rawLimit, eventSurface: _rawEventSurface, scheduleScope: _rawScheduleScope, detailLevel: _rawDetailLevel, ...stringEventQueryShape } = publicEventLoaderQuerySchemaShape;
118918
+ const eventRecoveryRequestSchema = z.object({
118919
+ ...stringEventQueryShape,
118920
+ limit: z.coerce.number().int().positive().optional(),
118921
+ eventSurface: z.enum(eventSurfaceScopeValues).optional(),
118922
+ scheduleScope: z.enum(eventScheduleScopeValues).optional(),
118923
+ detailLevel: z.enum(["full", "listing"]).optional(),
118924
+ stage: z.enum(["preview", "published"]).optional()
118925
+ });
118926
+ function resolveEventRegistrationEventSource(input) {
118927
+ if (input.wholeSeriesRunEvents != null) return {
118928
+ kind: "loaded",
118929
+ events: input.wholeSeriesRunEvents
118930
+ };
118931
+ if (input.events != null) return {
118932
+ kind: "loaded",
118933
+ events: input.events
118934
+ };
118935
+ return {
118936
+ kind: "unresolved",
118937
+ request: parseEventLoaderRecoveryRequest(input.failures?.wholeSeriesRunEvents ?? input.failures?.events)
118938
+ };
118939
+ }
118940
+ const eventLoaderFailureSchema = z.object({
118941
+ endpoint: z.literal("listPublicEvents"),
118942
+ params: eventRecoveryRequestSchema.extend({ siteId: z.string().optional() })
118943
+ });
118944
+ function parseEventLoaderRecoveryRequest(value) {
118945
+ const parsed = eventLoaderFailureSchema.safeParse(value);
118946
+ if (!parsed.success) return null;
118947
+ const { siteId: _siteId, stage, ...params } = parsed.data.params;
118948
+ return {
118949
+ ...params,
118950
+ stage: stage ?? "published"
118951
+ };
118952
+ }
118953
+ //#endregion
118808
118954
  //#region ../blocks/src/system/runtime/nodes/event-registration.interactive.ts
118809
118955
  const EVENT_REGISTRATION_GENERIC_SHELL_STEP_LABELS = [
118810
118956
  "Date",
@@ -118822,8 +118968,6 @@ const eventRegistrationPublicEventSchema = z.preprocess(normalizeEventRegistrati
118822
118968
  const eventRegistrationHydrationSchemaShape = {
118823
118969
  occurrenceContext: occurrenceContextSchema.optional(),
118824
118970
  subject: eventRegistrationPageSurfaceSubjectSchema.nullable().optional(),
118825
- events: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
118826
- wholeSeriesRunEvents: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
118827
118971
  maxTickets: z.string().optional(),
118828
118972
  showVenue: z.boolean().optional(),
118829
118973
  showCapacity: z.boolean().optional(),
@@ -118835,12 +118979,22 @@ const eventRegistrationHydrationSchemaShape = {
118835
118979
  sitePasses: publicPassProductCollectionSchema.nullable().optional(),
118836
118980
  siteMemberships: publicMembershipProductCollectionSchema.nullable().optional()
118837
118981
  };
118982
+ const eventRegistrationEventSourceSchema = z.discriminatedUnion("kind", [z.object({
118983
+ kind: z.literal("loaded"),
118984
+ events: z.array(eventRegistrationPublicEventSchema)
118985
+ }), z.object({
118986
+ kind: z.literal("unresolved"),
118987
+ request: eventRecoveryRequestSchema.nullable()
118988
+ })]);
118838
118989
  const eventRegistrationSourceSchema = z.object({
118839
118990
  siteId: z.string().optional(),
118840
118991
  apiBaseUrl: z.string().optional(),
118841
118992
  portalToken: z.string().nullable().optional(),
118842
118993
  previewToken: z.string().nullable().optional(),
118843
118994
  ...eventRegistrationHydrationSchemaShape,
118995
+ events: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
118996
+ wholeSeriesRunEvents: z.array(eventRegistrationPublicEventSchema).nullable().optional(),
118997
+ eventLoaderFailures: z.record(z.string(), z.unknown()).nullable().optional(),
118844
118998
  className: z.string().nullable().optional(),
118845
118999
  buttonText: z.string().nullable().optional(),
118846
119000
  maxTickets: z.string().nullable().optional(),
@@ -118861,7 +119015,10 @@ const eventRegistrationDisplaySchema = z.discriminatedUnion("state", [z.object({
118861
119015
  titleText: z.string(),
118862
119016
  bodyText: z.string()
118863
119017
  })]);
118864
- const eventRegistrationHydrationSchema = z.object(eventRegistrationHydrationSchemaShape);
119018
+ const eventRegistrationHydrationSchema = z.object({
119019
+ ...eventRegistrationHydrationSchemaShape,
119020
+ eventSource: eventRegistrationEventSourceSchema
119021
+ });
118865
119022
  const eventRegistrationRenderSchema = z.object({
118866
119023
  display: eventRegistrationDisplaySchema,
118867
119024
  hydration: eventRegistrationHydrationSchema
@@ -118920,16 +119077,12 @@ function buildEventRegistrationShellStepLabels(source, scopedSourceEvents) {
118920
119077
  function toScopedEventRegistrationEvents(scopedSourceEvents) {
118921
119078
  return [...toRegistrationEvents(scopedSourceEvents), ...toWholeSeriesRegistrationEvents(scopedSourceEvents)];
118922
119079
  }
118923
- function scopeEventRegistrationSourceEvents(source) {
118924
- return resolveEventRegistrationScopedEvents(source.wholeSeriesRunEvents ?? source.events ?? [], {
119080
+ function scopeEventRegistrationSourceEvents(source, eventSource) {
119081
+ return resolveEventRegistrationScopedEvents(eventSource.kind === "loaded" ? eventSource.events : [], {
118925
119082
  occurrenceContext: source.occurrenceContext,
118926
119083
  subject: source.subject
118927
119084
  });
118928
119085
  }
118929
- function resolveLoadedEventRegistrationSourceEvents(source, scopedSourceEvents) {
118930
- if (source.events == null && source.wholeSeriesRunEvents == null) return null;
118931
- return [...scopedSourceEvents];
118932
- }
118933
119086
  /**
118934
119087
  * The one run this surface is registering for, when the scoped events describe
118935
119088
  * exactly one. More than one run means the visitor still has to choose.
@@ -118969,14 +119122,21 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
118969
119122
  render: createZodCodec(eventRegistrationRenderSchema)
118970
119123
  },
118971
119124
  buildRender: (source) => {
118972
- const scopedSourceEvents = scopeEventRegistrationSourceEvents(source);
118973
- const events = resolveLoadedEventRegistrationSourceEvents(source, scopedSourceEvents);
119125
+ const eventSource = resolveEventRegistrationEventSource({
119126
+ events: source.events,
119127
+ wholeSeriesRunEvents: source.wholeSeriesRunEvents,
119128
+ failures: source.eventLoaderFailures
119129
+ });
119130
+ const scopedSourceEvents = scopeEventRegistrationSourceEvents(source, eventSource);
118974
119131
  return {
118975
119132
  display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents),
118976
119133
  hydration: {
118977
119134
  occurrenceContext: source.occurrenceContext ?? null,
118978
119135
  subject: source.subject ?? null,
118979
- events,
119136
+ eventSource: eventSource.kind === "loaded" ? {
119137
+ ...eventSource,
119138
+ events: [...scopedSourceEvents]
119139
+ } : eventSource,
118980
119140
  maxTickets: source.maxTickets ?? void 0,
118981
119141
  showVenue: source.showVenue,
118982
119142
  showCapacity: source.showCapacity,
@@ -119166,6 +119326,7 @@ const eventRegistrationBlockDefinition = {
119166
119326
  } },
119167
119327
  events: { $bind: { from: "data.events" } },
119168
119328
  wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
119329
+ loaderFailures: { $bind: { from: "$root.loaderFailures" } },
119169
119330
  sitePasses: { $bind: { from: "data.sitePasses" } },
119170
119331
  siteMemberships: { $bind: { from: "data.siteMemberships" } },
119171
119332
  stage: { $bind: {
@@ -121172,6 +121333,15 @@ const newsletterFieldSchema = z.object({
121172
121333
  placeholder: z.string().optional()
121173
121334
  });
121174
121335
  const mailingListSignupBlockIdSchema = z.string().trim().min(1).transform(parseRequiredMailingListSignupBlockId);
121336
+ const mailingListContentVersionIdSchema = z.string().transform((value, ctx) => {
121337
+ const parsed = parseMailingListContentVersionId(value);
121338
+ if (parsed) return parsed;
121339
+ ctx.addIssue({
121340
+ code: z.ZodIssueCode.custom,
121341
+ message: "Invalid mailing-list content version ID"
121342
+ });
121343
+ return z.NEVER;
121344
+ });
121175
121345
  const signedMailingListSourceTokenSchema = z.string().trim().min(1).transform(parseRequiredMailingListSignedSourceToken);
121176
121346
  const newsletterSourceCommonSchema = z.object({
121177
121347
  siteId: z.string().optional(),
@@ -121185,7 +121355,8 @@ const newsletterSourceCommonSchema = z.object({
121185
121355
  const newsletterSourceSchema = z.discriminatedUnion("source", [
121186
121356
  newsletterSourceCommonSchema.extend({
121187
121357
  source: z.literal("block"),
121188
- blockId: z.string().nullable().optional()
121358
+ blockId: z.string().nullable().optional(),
121359
+ contentVersionId: z.string().nullable().optional()
121189
121360
  }),
121190
121361
  newsletterSourceCommonSchema.extend({
121191
121362
  source: z.literal("signed-footer"),
@@ -121201,6 +121372,7 @@ const newsletterDisplaySchema = z.object({
121201
121372
  availability: z.enum(["ready", "unavailable"]),
121202
121373
  className: z.string().nullable(),
121203
121374
  buttonLabel: z.string(),
121375
+ successMessage: z.string(),
121204
121376
  buttonVariant: newsletterButtonVariantSchema,
121205
121377
  privacyNote: z.string().nullable(),
121206
121378
  fields: z.array(newsletterFieldSchema),
@@ -121215,7 +121387,8 @@ const newsletterHydrationSchema = z.object({
121215
121387
  tag: z.literal("block-source"),
121216
121388
  sourceRef: z.object({
121217
121389
  tag: z.literal("block_ref"),
121218
- blockId: mailingListSignupBlockIdSchema
121390
+ blockId: mailingListSignupBlockIdSchema,
121391
+ contentVersionId: mailingListContentVersionIdSchema
121219
121392
  }).nullable()
121220
121393
  }),
121221
121394
  z.object({
@@ -121254,6 +121427,7 @@ function buildNewsletterDisplay(source) {
121254
121427
  fields: { preset: "emailOnly" },
121255
121428
  includeOptionalPhone: false,
121256
121429
  buttonLabel: "Subscribe",
121430
+ successMessage: DEFAULT_MAILING_LIST_SUCCESS_MESSAGE,
121257
121431
  privacyNote: null
121258
121432
  };
121259
121433
  const targeting = source.targeting ?? { mode: "default" };
@@ -121263,6 +121437,7 @@ function buildNewsletterDisplay(source) {
121263
121437
  availability: runtimeTargetingIsAvailable(context, targeting, choices) ? "ready" : "unavailable",
121264
121438
  className: source.className ?? null,
121265
121439
  buttonLabel: form.buttonLabel,
121440
+ successMessage: form.successMessage,
121266
121441
  buttonVariant: source.buttonVariant ?? "primary",
121267
121442
  privacyNote: form.privacyNote,
121268
121443
  fields: getGlobalFormFields(form),
@@ -121293,7 +121468,7 @@ const newsletterFormBlockRuntime = defineProgressiveBlockRuntime({
121293
121468
  function projectNewsletterFormHydrationTarget(source) {
121294
121469
  switch (source.source) {
121295
121470
  case "block": {
121296
- const sourceRef = buildMailingListSignupBlockSourceRef(source.blockId);
121471
+ const sourceRef = buildMailingListSignupBlockSourceRef(source.blockId, source.contentVersionId);
121297
121472
  if (sourceRef?.tag === "block_ref") return {
121298
121473
  tag: "block-source",
121299
121474
  sourceRef
@@ -123705,9 +123880,10 @@ function cx$1(...classes) {
123705
123880
  * Server-safe placeholder for event registration.
123706
123881
  * Provides a static container for island hydration without importing client hooks.
123707
123882
  */
123708
- function EventRegistrationSSR({ blockId, subject, children, ...sourceProps }) {
123883
+ function EventRegistrationSSR({ blockId, subject, loaderFailures, children, ...sourceProps }) {
123709
123884
  const islandProps = buildEventRegistrationInteractiveIslandProps({
123710
123885
  ...sourceProps,
123886
+ eventLoaderFailures: blockId ? loaderFailures?.[blockId] : void 0,
123711
123887
  subject: normalizeEventRegistrationPageSurfaceSubject(subject)
123712
123888
  });
123713
123889
  const display = islandProps.render.display;
@@ -124040,7 +124216,7 @@ function NewsletterFormSSR(props) {
124040
124216
  className: clsx("newsletter-form-ssr", className),
124041
124217
  children: /* @__PURE__ */ jsx("p", {
124042
124218
  role: "status",
124043
- children: "Mailing-list signup is not available right now."
124219
+ children: "We can’t accept signups right now. Please try again later."
124044
124220
  })
124045
124221
  });
124046
124222
  const renderField = (field) => {
@@ -124086,14 +124262,18 @@ function NewsletterFormSSR(props) {
124086
124262
  children: [
124087
124263
  fields.map(renderField),
124088
124264
  islandProps.render.hydration.choicePresentation === "visitor_choice" ? /* @__PURE__ */ jsxs("fieldset", {
124089
- className: "newsletter-topics",
124090
- children: [/* @__PURE__ */ jsx("legend", { children: "What would you like to hear about?" }), display.choices.map((choice) => /* @__PURE__ */ jsxs("label", {
124091
- className: "newsletter-topic-choice",
124265
+ className: "fb-fieldset newsletter-topics",
124266
+ children: [/* @__PURE__ */ jsx("legend", {
124267
+ className: "form-label",
124268
+ children: "What would you like to hear about?"
124269
+ }), display.choices.map((choice) => /* @__PURE__ */ jsxs("label", {
124270
+ className: "fb-inline form-label newsletter-topic-choice",
124092
124271
  children: [/* @__PURE__ */ jsx("input", {
124093
124272
  type: "checkbox",
124094
124273
  name: "mailingListChoice",
124095
- value: choice.id
124096
- }), choice.label]
124274
+ value: choice.id,
124275
+ className: "form-checkbox"
124276
+ }), /* @__PURE__ */ jsx("span", { children: choice.label })]
124097
124277
  }, choice.id))]
124098
124278
  }) : null,
124099
124279
  /* @__PURE__ */ jsx("div", {
@@ -128965,6 +129145,90 @@ function missingPaymentSelection() {
128965
129145
  };
128966
129146
  }
128967
129147
  //#endregion
129148
+ //#region ../blocks/src/system/runtime/nodes/event-registration/checkoutRecovery.ts
129149
+ /** Recovery may select held places; checkout still owns admission and payment validation. */
129150
+ function checkoutRecoveryOptionAvailability(availability) {
129151
+ if (availability.kind === "available" || availability.reason === "sold_out" || availability.reason === "overallCapacityUnavailable") return {
129152
+ kind: "available",
129153
+ remainingPlaces: null
129154
+ };
129155
+ return availability;
129156
+ }
129157
+ /**
129158
+ * A form-only projection for the explicit existing-checkout path. It permits
129159
+ * collecting the original selection without claiming a new place is available.
129160
+ * The public checkout service identifies the customer and remains the admission
129161
+ * authority; no reservation/customer data is exposed through this projection.
129162
+ */
129163
+ function getCheckoutRecoveryFormEligibility(eligibility) {
129164
+ if (!eligibility || eligibility.registration.kind !== "none" && eligibility.registration.kind !== "pending_payment" || eligibility.availability.kind === "unavailable" && eligibility.availability.reason !== "full" && eligibility.availability.reason !== "selectionRequired") return eligibility;
129165
+ const amountCents = eligibility.price ?? 0;
129166
+ if (amountCents <= 0 || !eligibility.currency || eligibility.availability.kind === "unavailable" && eligibility.availability.reason === "selectionRequired") return eligibility.registration.kind === "pending_payment" ? {
129167
+ ...eligibility,
129168
+ registration: { kind: "none" },
129169
+ ticketTypes: eligibility.ticketTypes.map((option) => ({
129170
+ ...option,
129171
+ availability: checkoutRecoveryOptionAvailability(option.availability)
129172
+ }))
129173
+ } : eligibility;
129174
+ const currency = eligibility.currency;
129175
+ const paymentOptions = eligibility.paymentTerms.allowedOptions.map((option) => {
129176
+ switch (option.kind) {
129177
+ case "upfront": return {
129178
+ kind: "payInFull",
129179
+ amountCents,
129180
+ currency
129181
+ };
129182
+ case "deferred_manual": return {
129183
+ kind: "deferredManual",
129184
+ amountCents,
129185
+ currency
129186
+ };
129187
+ case "instalment_plan": return {
129188
+ kind: "instalmentPlan",
129189
+ amountCents,
129190
+ currency
129191
+ };
129192
+ case "flexible_balance": return {
129193
+ kind: "flexibleBalance",
129194
+ amountCents,
129195
+ currency
129196
+ };
129197
+ }
129198
+ });
129199
+ return {
129200
+ ...eligibility,
129201
+ registration: { kind: "none" },
129202
+ availability: {
129203
+ kind: "available",
129204
+ remainingPlaces: null,
129205
+ waitlistEnabled: eligibility.waitlistEnabled
129206
+ },
129207
+ eligible: paymentOptions.length > 0,
129208
+ isFull: false,
129209
+ canJoinWaitlist: false,
129210
+ isFree: false,
129211
+ requiresPayment: true,
129212
+ canPayDirectly: paymentOptions.length > 0,
129213
+ paymentOptions,
129214
+ eligiblePasses: [],
129215
+ eligibleMemberships: [],
129216
+ ticketTypes: eligibility.ticketTypes.map((option) => ({
129217
+ ...option,
129218
+ availability: checkoutRecoveryOptionAvailability(option.availability)
129219
+ }))
129220
+ };
129221
+ }
129222
+ function isSoldOutEventSelectable(event, waitlistAvailable) {
129223
+ return waitlistAvailable || canRecoverEventCheckout(event);
129224
+ }
129225
+ function canJoinWaitlistOutsideRecovery(event, attendanceMode, checkoutRecovery) {
129226
+ return !checkoutRecovery && event !== null && canJoinEventWaitlist(event, { attendanceMode });
129227
+ }
129228
+ function canRecoverEventCheckout(event) {
129229
+ return event !== null && (event.registrationSubject.kind === "wholeSeries" || event.pricing.kind !== "free" && event.pricing.kind !== "external");
129230
+ }
129231
+ //#endregion
128968
129232
  //#region ../blocks/src/system/runtime/nodes/event-registration/utils.ts
128969
129233
  function getPortalSubjectEmail(profile) {
128970
129234
  return profile?.email ?? null;
@@ -129150,7 +129414,7 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
129150
129414
  const remaining = getRemainingPlaces(selectMostOpenCapacitySource(event));
129151
129415
  const isSoldOut = isEventFull(event);
129152
129416
  const canJoinWaitlist = canJoinEventWaitlistOnAnyMode(event);
129153
- const isUnavailable = isSoldOut && !canJoinWaitlist;
129417
+ const isUnavailable = isSoldOut && !isSoldOutEventSelectable(event, canJoinWaitlist);
129154
129418
  const isLowCapacity = remaining.kind === "low";
129155
129419
  const capacityLabel = formatEventRemainingPlaces(event, { full: "Sold out" });
129156
129420
  const timeZone = event.timeZone ?? null;
@@ -129195,6 +129459,33 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
129195
129459
  });
129196
129460
  };
129197
129461
  //#endregion
129462
+ //#region ../blocks/src/system/runtime/nodes/event-registration/useCheckoutRecovery.ts
129463
+ /** Recovery belongs to the selected booking subject; changing events resets its scope. */
129464
+ function useCheckoutRecovery(event, setStep) {
129465
+ const [recoveryEventId, setRecoveryEventId] = useState(null);
129466
+ return {
129467
+ checkoutRecovery: event !== null && recoveryEventId === event.selectionId,
129468
+ startCheckoutRecovery: useCallback(() => {
129469
+ if (!event) return;
129470
+ setRecoveryEventId(event.selectionId);
129471
+ setStep("select-tickets");
129472
+ }, [event, setStep])
129473
+ };
129474
+ }
129475
+ /** Authenticated pending bookings enter the same form-only recovery path as guests. */
129476
+ function useCheckoutRecoveryEligibility(eligibility, step, checkoutRecovery, startCheckoutRecovery) {
129477
+ const registrationKind = eligibility?.registration.kind;
129478
+ useEffect(() => {
129479
+ if ((step === "select-event" || step === "select-tickets" || step === "enter-details" || step === "select-payment") && registrationKind === "pending_payment" && !checkoutRecovery) startCheckoutRecovery();
129480
+ }, [
129481
+ registrationKind,
129482
+ step,
129483
+ checkoutRecovery,
129484
+ startCheckoutRecovery
129485
+ ]);
129486
+ return useMemo(() => checkoutRecovery ? getCheckoutRecoveryFormEligibility(eligibility) : eligibility, [checkoutRecovery, eligibility]);
129487
+ }
129488
+ //#endregion
129198
129489
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.ts
129199
129490
  function resolveRegistrationRecoveryAction(step, action) {
129200
129491
  if (step === "payment-failed") return "payment-options";
@@ -129835,6 +130126,10 @@ const eventRegistrationErrors = {
129835
130126
  message: "We’re checking your booking and payment. Please don’t try to book again yet. If you’ve already paid, you don’t need to pay again.",
129836
130127
  action: "payment-status"
129837
130128
  },
130129
+ paymentProviderUnavailable: {
130130
+ message: "We couldn’t reach the payment provider. Please try again in a minute.",
130131
+ action: "retry"
130132
+ },
129838
130133
  paymentPageUnavailable: {
129839
130134
  message: "We couldn’t open the payment page. Please refresh this page and try again. If this keeps happening, contact the organiser for help.",
129840
130135
  action: "retry"
@@ -129957,11 +130252,6 @@ function ensureWaitlistClaimLocationTracking() {
129957
130252
  } catch {}
129958
130253
  window.addEventListener("popstate", emitWaitlistClaimLocationChange);
129959
130254
  }
129960
- function getWaitlistClaimTokenFromUrl() {
129961
- if (typeof window === "undefined") return null;
129962
- const token = new URLSearchParams(window.location.search).get("waitlist_claim")?.trim() ?? "";
129963
- return token.length > 0 ? token : null;
129964
- }
129965
130255
  const useWaitlistClaimDeps = { useBlockApi };
129966
130256
  function subscribeToWaitlistClaimToken(callback) {
129967
130257
  if (typeof window === "undefined") return () => {};
@@ -129971,28 +130261,42 @@ function subscribeToWaitlistClaimToken(callback) {
129971
130261
  window.removeEventListener(WAITLIST_CLAIM_LOCATION_EVENT, callback);
129972
130262
  };
129973
130263
  }
129974
- function getWaitlistClaimTokenSnapshot() {
129975
- return getWaitlistClaimTokenFromUrl();
130264
+ function getWaitlistClaimLocationSnapshot() {
130265
+ return typeof window === "undefined" ? "" : window.location.search;
130266
+ }
130267
+ function parseWaitlistClaimCredential(search) {
130268
+ const params = new URLSearchParams(search);
130269
+ const token = params.get("waitlist_claim")?.trim();
130270
+ if (token) return {
130271
+ kind: "emailedToken",
130272
+ token
130273
+ };
130274
+ const entryId = params.get("waitlistEntry")?.trim();
130275
+ return entryId ? {
130276
+ kind: "portalOwnedEntry",
130277
+ entryId
130278
+ } : null;
129976
130279
  }
129977
130280
  function useWaitlistClaim() {
129978
130281
  return useWaitlistClaimWithDeps(useWaitlistClaimDeps);
129979
130282
  }
129980
130283
  function useWaitlistClaimWithDeps(deps) {
129981
- return useResolvedWaitlistClaim(deps, useSyncExternalStore(subscribeToWaitlistClaimToken, getWaitlistClaimTokenSnapshot, () => null));
130284
+ const locationSearch = useSyncExternalStore(subscribeToWaitlistClaimToken, getWaitlistClaimLocationSnapshot, () => "");
130285
+ return useResolvedWaitlistClaim(deps, useMemo(() => parseWaitlistClaimCredential(locationSearch), [locationSearch]));
129982
130286
  }
129983
- function useResolvedWaitlistClaim(deps, token) {
130287
+ function useResolvedWaitlistClaim(deps, credential) {
129984
130288
  const api = deps.useBlockApi();
129985
- const [state, dispatch] = useReducer(waitlistClaimReducer, Boolean(token), createInitialWaitlistClaimState);
130289
+ const [state, dispatch] = useReducer(waitlistClaimReducer, Boolean(credential), createInitialWaitlistClaimState);
129986
130290
  useEffect(() => {
129987
130291
  let cancelled = false;
129988
130292
  async function resolveClaim() {
129989
- if (!token) {
130293
+ if (!credential) {
129990
130294
  dispatch({ type: "cleared" });
129991
130295
  return;
129992
130296
  }
129993
130297
  dispatch({ type: "started" });
129994
130298
  try {
129995
- const result = await api.resolveWaitlistClaim({ claimToken: token });
130299
+ const result = credential.kind === "portalOwnedEntry" ? await api.resolveWaitlistClaim({ waitlistEntryId: credential.entryId }) : await api.resolveWaitlistClaim({ claimToken: credential.token });
129996
130300
  if (cancelled) return;
129997
130301
  dispatch({
129998
130302
  type: "resolved",
@@ -130010,9 +130314,10 @@ function useResolvedWaitlistClaim(deps, token) {
130010
130314
  return () => {
130011
130315
  cancelled = true;
130012
130316
  };
130013
- }, [api, token]);
130317
+ }, [api, credential]);
130014
130318
  return {
130015
- token,
130319
+ token: credential?.kind === "emailedToken" ? credential.token : null,
130320
+ entryId: credential?.kind === "portalOwnedEntry" ? credential.entryId : null,
130016
130321
  claim: getWaitlistClaim(state),
130017
130322
  error: getWaitlistClaimError(state),
130018
130323
  loginMessage: getWaitlistClaimLoginMessage(state),
@@ -130490,15 +130795,12 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
130490
130795
  * landed — a success, or a hand-off to checkout — because until then nobody
130491
130796
  * knows whether the first one committed.
130492
130797
  *
130493
- * Reusing it after a failure is safe even if the customer edits their
130494
- * details. The server consults the command record before it re-checks
130495
- * anything, so a submission that already booked is answered with the booking
130496
- * that exists — not refused for spending a credit or a claim that this very
130497
- * command already spent, and not treated as a second booking because a name
130498
- * or a note changed in between.
130798
+ * Identical retries retain the command after a lost response. Changed form
130799
+ * details get a new command so the server applies the new booking intent.
130499
130800
  */
130500
- const commandIdRef = useRef(null);
130501
- return async (method, passId, membershipId, selection, submitOptions) => {
130801
+ const commandRef = useRef(null);
130802
+ const inFlightRef = useRef(null);
130803
+ const submit = async (method, passId, membershipId, selection, submitOptions) => {
130502
130804
  const target = options.registrationTarget;
130503
130805
  if (!target) return {
130504
130806
  ok: false,
@@ -130506,44 +130808,34 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
130506
130808
  message: "Select an event to continue."
130507
130809
  };
130508
130810
  options.setStep("submitting");
130509
- const registrationCommandId = commandIdRef.current ?? deps.createClientCommandId();
130510
- commandIdRef.current = registrationCommandId;
130811
+ const registrationCommandId = commandRef.current?.id ?? deps.createClientCommandId();
130511
130812
  try {
130512
- const prepared = prepareSubmitContext(options, registrationCommandId, target, method, passId, membershipId, selection);
130813
+ let prepared = prepareSubmitContext(options, registrationCommandId, target, method, passId, membershipId, selection);
130513
130814
  if (prepared.subject.kind === "wholeSeries" && method === "stripe") {
130514
- commandIdRef.current = null;
130815
+ commandRef.current = null;
130515
130816
  return await submitWholeSeriesCheckout(options, deps, {
130516
130817
  ...prepared,
130517
130818
  subject: prepared.subject
130518
130819
  }, submitOptions);
130519
130820
  }
130821
+ const request = buildOccurrenceRegistrationRequest(options, prepared, method, passId, membershipId);
130822
+ const intentKey = registrationRequestIntentKey(request);
130823
+ if (commandRef.current !== null && commandRef.current.intentKey !== intentKey) prepared = {
130824
+ ...prepared,
130825
+ registrationCommandId: deps.createClientCommandId()
130826
+ };
130827
+ commandRef.current = {
130828
+ id: prepared.registrationCommandId,
130829
+ intentKey
130830
+ };
130520
130831
  const result = await options.api.registerForEvent({
130521
- registrationCommandId: prepared.registrationCommandId,
130522
- subject: prepared.subject,
130523
- ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
130524
- selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
130525
- email: options.formData.email.trim().toLowerCase(),
130526
- name: options.formData.name.trim(),
130527
- phone: options.formData.phone.trim() || void 0,
130528
- notes: options.formData.notes.trim() || void 0,
130529
- consentAccepted: options.formData.consentAccepted,
130530
- altchaSolution: options.altchaSolution,
130531
- paymentMethod: method,
130532
- customerPassId: method === "pass" ? passId : void 0,
130533
- customerMembershipId: method === "membership" ? membershipId : void 0,
130534
- bundleProductId: prepared.bundleProductId,
130535
- claimToken: options.claimToken ?? void 0,
130536
- guests: prepared.guestsPayload
130832
+ ...request,
130833
+ registrationCommandId: prepared.registrationCommandId
130537
130834
  });
130538
- commandIdRef.current = null;
130539
- if (needsOccurrenceCheckout(result, method)) return await submitOccurrenceCheckout(options, deps, prepared, result, method, submitOptions);
130540
- options.setRegistrationResult(toRegistrationResult(result));
130541
- options.setStep("success");
130542
- return {
130543
- ok: true,
130544
- outcome: { kind: "completed" }
130545
- };
130835
+ commandRef.current = null;
130836
+ return await completeRegistrationSubmission(options, deps, prepared, result, method, submitOptions);
130546
130837
  } catch (error) {
130838
+ if (error instanceof BlockApiError && error.tag === "registrationPreparationChanged") commandRef.current = null;
130547
130839
  const { message, action } = registrationErrorPresentation(error);
130548
130840
  options.setErrorAction?.(action);
130549
130841
  options.setErrorMessage(message);
@@ -130555,6 +130847,24 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
130555
130847
  };
130556
130848
  }
130557
130849
  };
130850
+ return (...args) => {
130851
+ if (inFlightRef.current) return inFlightRef.current;
130852
+ const pending = Promise.resolve().then(() => submit(...args));
130853
+ inFlightRef.current = pending;
130854
+ pending.finally(() => {
130855
+ if (inFlightRef.current === pending) inFlightRef.current = null;
130856
+ });
130857
+ return pending;
130858
+ };
130859
+ }
130860
+ async function completeRegistrationSubmission(options, deps, prepared, result, method, submitOptions) {
130861
+ if (needsOccurrenceCheckout(result, method)) return await submitOccurrenceCheckout(options, deps, prepared, result, method, submitOptions);
130862
+ options.setRegistrationResult(toRegistrationResult(result));
130863
+ options.setStep("success");
130864
+ return {
130865
+ ok: true,
130866
+ outcome: { kind: "completed" }
130867
+ };
130558
130868
  }
130559
130869
  function toTicketSelectionRequests(lines) {
130560
130870
  return lines.map((line) => ({
@@ -130629,6 +130939,7 @@ async function submitWholeSeriesCheckout(options, deps, prepared, submitOptions)
130629
130939
  altchaSolution: options.altchaSolution,
130630
130940
  guests: prepared.guestsPayload,
130631
130941
  claimToken: options.claimToken ?? void 0,
130942
+ waitlistEntryId: options.waitlistEntryId ?? void 0,
130632
130943
  discountCode: options.discountCode.trim() || void 0,
130633
130944
  collectionOptionKind: prepared.selectionSnapshot.collectionOptionKind,
130634
130945
  checkoutAttemptId,
@@ -130650,14 +130961,14 @@ async function submitWholeSeriesCheckout(options, deps, prepared, submitOptions)
130650
130961
  }
130651
130962
  if (result.bypassed) return completeBypassedCheckout(options);
130652
130963
  if (!result.checkoutUrl) throw new Error("Checkout URL missing");
130653
- savePendingCheckout(options, deps, prepared, checkoutAttemptId, {
130964
+ savePendingCheckout(options, deps, prepared, result.checkoutAttemptId, {
130654
130965
  scope: "wholeSeries",
130655
130966
  registrationId: result.registrationId
130656
130967
  });
130657
130968
  return redirectToCheckout(result.checkoutUrl, submitOptions);
130658
130969
  }
130659
130970
  function needsOccurrenceCheckout(result, method) {
130660
- return result.scope === "occurrence" && !result.alreadyRegistered && result.status === "confirmed" && result.pendingPayment && [
130971
+ return result.scope === "occurrence" && result.status === "confirmed" && result.pendingPayment && [
130661
130972
  "stripe",
130662
130973
  "pass_bundle",
130663
130974
  "membership_bundle"
@@ -130677,6 +130988,7 @@ async function submitOccurrenceCheckout(options, deps, prepared, registration, m
130677
130988
  kind: "occurrence",
130678
130989
  attendeeId: registration.attendeeId
130679
130990
  },
130991
+ registrationCommandId: prepared.registrationCommandId,
130680
130992
  checkoutType: method === "pass_bundle" ? "pass_bundle" : method === "membership_bundle" ? "membership_bundle" : "event",
130681
130993
  productId: prepared.bundleProductId,
130682
130994
  discountCode: options.discountCode.trim() || void 0,
@@ -130719,6 +131031,31 @@ function redirectToCheckout(checkoutUrl, submitOptions) {
130719
131031
  }
130720
131032
  };
130721
131033
  }
131034
+ function registrationRequestIntentKey(request) {
131035
+ const { registrationCommandId: _command, altchaSolution: _proof, ...intent } = request;
131036
+ return JSON.stringify(intent);
131037
+ }
131038
+ function buildOccurrenceRegistrationRequest(options, prepared, method, passId, membershipId) {
131039
+ return {
131040
+ registrationCommandId: prepared.registrationCommandId,
131041
+ subject: prepared.subject,
131042
+ ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
131043
+ selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
131044
+ email: options.formData.email.trim().toLowerCase(),
131045
+ name: options.formData.name.trim(),
131046
+ phone: options.formData.phone.trim() || void 0,
131047
+ notes: options.formData.notes.trim() || void 0,
131048
+ consentAccepted: options.formData.consentAccepted,
131049
+ altchaSolution: options.altchaSolution,
131050
+ paymentMethod: method,
131051
+ customerPassId: method === "pass" ? passId : void 0,
131052
+ customerMembershipId: method === "membership" ? membershipId : void 0,
131053
+ bundleProductId: prepared.bundleProductId,
131054
+ claimToken: options.claimToken ?? void 0,
131055
+ waitlistEntryId: options.waitlistEntryId ?? void 0,
131056
+ guests: prepared.guestsPayload
131057
+ };
131058
+ }
130722
131059
  //#endregion
130723
131060
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.ts
130724
131061
  function getEventSelectionDefaults(event) {
@@ -130957,14 +131294,28 @@ function useEventRegistrationWizardWithDeps(options, deps) {
130957
131294
  }), [options.occurrenceContext, selectedEvent]);
130958
131295
  const registrationOccurrenceId = getRegistrationOccurrenceId(registrationTarget);
130959
131296
  const currentEvent = selectedEvent ?? preSelectedEvent;
131297
+ const setStep = useCallback((nextStep) => {
131298
+ dispatchFlow({
131299
+ type: "set-step",
131300
+ step: nextStep
131301
+ });
131302
+ }, []);
131303
+ const { checkoutRecovery, startCheckoutRecovery } = useCheckoutRecovery(currentEvent, setStep);
130960
131304
  const currentEventUsesTicketTypes = usesTieredPricing(currentEvent);
130961
- const currentCanJoinWaitlist = currentEvent ? canJoinEventWaitlist(currentEvent, { attendanceMode: selectedAttendanceMode }) : false;
131305
+ const currentCanJoinWaitlist = canJoinWaitlistOutsideRecovery(currentEvent, selectedAttendanceMode, checkoutRecovery);
130962
131306
  const usesTicketTypeSelections = currentEventUsesTicketTypes && !currentCanJoinWaitlist;
130963
131307
  const eventTicketTypesForSelection = useMemo(() => getVisibleTicketTypes({
130964
131308
  event: currentEvent,
130965
131309
  eligibility: null,
130966
131310
  selectedAttendanceMode
130967
- }), [currentEvent, selectedAttendanceMode]);
131311
+ }).map((option) => checkoutRecovery ? {
131312
+ ...option,
131313
+ availability: checkoutRecoveryOptionAvailability(option.availability)
131314
+ } : option), [
131315
+ currentEvent,
131316
+ selectedAttendanceMode,
131317
+ checkoutRecovery
131318
+ ]);
130968
131319
  const normalizedSelectedTicketLines = useMemo(() => normalizeTicketSelectionLines(selectedTicketLines), [selectedTicketLines]);
130969
131320
  const ticketTypeSelectionTotal = useMemo(() => getTicketSelectionTotal(normalizedSelectedTicketLines), [normalizedSelectedTicketLines]);
130970
131321
  const ticketsCount = usesTicketTypeSelections ? ticketTypeSelectionTotal : flowState.ticketsCount;
@@ -131002,17 +131353,11 @@ function useEventRegistrationWizardWithDeps(options, deps) {
131002
131353
  isAuthenticated: portalAuthState === "authenticated",
131003
131354
  occurrenceId: registrationOccurrenceId
131004
131355
  });
131005
- const eligibility = eligibilityResult.eligibility;
131356
+ const eligibility = useCheckoutRecoveryEligibility(eligibilityResult.eligibility, step, checkoutRecovery, startCheckoutRecovery);
131006
131357
  const eligibilityLoading = eligibilityResult.isLoading;
131007
131358
  const eligibilityError = eligibilityResult.error;
131008
131359
  const portalCreditState = eligibilityResult.creditState;
131009
131360
  const registrationState = eligibility?.registration ?? { kind: "none" };
131010
- const setStep = useCallback((nextStep) => {
131011
- dispatchFlow({
131012
- type: "set-step",
131013
- step: nextStep
131014
- });
131015
- }, []);
131016
131361
  const setTicketsCount = useCallback((nextTicketsCount) => {
131017
131362
  dispatchFlow({
131018
131363
  type: "set-tickets-count",
@@ -131120,10 +131465,14 @@ function useEventRegistrationWizardWithDeps(options, deps) {
131120
131465
  event: currentEvent,
131121
131466
  eligibility,
131122
131467
  selectedAttendanceMode
131123
- }), [
131468
+ }).map((option) => checkoutRecovery ? {
131469
+ ...option,
131470
+ availability: checkoutRecoveryOptionAvailability(option.availability)
131471
+ } : option), [
131124
131472
  currentEvent,
131125
131473
  selectedAttendanceMode,
131126
- eligibility
131474
+ eligibility,
131475
+ checkoutRecovery
131127
131476
  ]);
131128
131477
  const singleSelectedPricingOptionId = getSingleSelectedPricingOptionId(normalizedSelectedTicketLines);
131129
131478
  const selectedTicketType = useMemo(() => findSelectedTicketType({
@@ -131147,8 +131496,12 @@ function useEventRegistrationWizardWithDeps(options, deps) {
131147
131496
  const event = currentEvent;
131148
131497
  if (!usesTieredPricing(event)) return false;
131149
131498
  if ((event.deliveryMode ?? "in_person") === "hybrid" && !selectedAttendanceMode) return false;
131150
- return !canJoinEventWaitlist(event, { attendanceMode: selectedAttendanceMode });
131151
- }, [currentEvent, selectedAttendanceMode]);
131499
+ return checkoutRecovery || !canJoinEventWaitlist(event, { attendanceMode: selectedAttendanceMode });
131500
+ }, [
131501
+ currentEvent,
131502
+ selectedAttendanceMode,
131503
+ checkoutRecovery
131504
+ ]);
131152
131505
  const hasValidTicketSelection = hasValidPricingOptionSelection({
131153
131506
  selectionRequired: ticketTypeSelectionRequired,
131154
131507
  lines: normalizedSelectedTicketLines,
@@ -131352,6 +131705,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
131352
131705
  guests,
131353
131706
  discountCode: discountUi.discountCode,
131354
131707
  claimToken: waitlistClaim.token,
131708
+ waitlistEntryId: waitlistClaim.entryId,
131355
131709
  setPendingRegistrationReference,
131356
131710
  setStep,
131357
131711
  setRegistrationResult: (result) => setRegistrationResult(result),
@@ -131414,6 +131768,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
131414
131768
  scopedEvents,
131415
131769
  preSelectedEvent,
131416
131770
  hasPreSelectedContext,
131771
+ checkoutRecovery,
131772
+ startCheckoutRecovery,
131417
131773
  step,
131418
131774
  setStep,
131419
131775
  selectedEvent,
@@ -131617,6 +131973,51 @@ function getMagicLinkErrorMessage(error) {
131617
131973
  return error instanceof Error ? error.message : "Failed to send. Please try again.";
131618
131974
  }
131619
131975
  //#endregion
131976
+ //#region ../blocks/src/system/runtime/nodes/event-registration/ErrorStep.tsx
131977
+ /**
131978
+ * Error Step
131979
+ *
131980
+ * Displays an error message with a retry button when registration fails.
131981
+ */
131982
+ const labels = {
131983
+ none: "",
131984
+ retry: "Try again",
131985
+ places: "Choose places",
131986
+ start: "Start again",
131987
+ details: "Check my details",
131988
+ "payment-options": "View payment options",
131989
+ "payment-status": "Check payment status",
131990
+ portal: "View my bookings",
131991
+ event: "View event"
131992
+ };
131993
+ const ErrorStep = ({ message, onRetry, action = "retry", canJoinWaitlist = false }) => {
131994
+ const offerWaitlist = action === "places" && canJoinWaitlist;
131995
+ return /* @__PURE__ */ jsx(TerminalPanel, {
131996
+ tone: "error",
131997
+ className: "er-center",
131998
+ titleClassName: "er-title--lg er-message",
131999
+ icon: /* @__PURE__ */ jsx(StateIcon, {
132000
+ variant: "error",
132001
+ children: /* @__PURE__ */ jsx(CrossIcon, {})
132002
+ }),
132003
+ title: "We couldn’t complete your booking",
132004
+ body: /* @__PURE__ */ jsx("p", {
132005
+ className: "er-text--muted er-message",
132006
+ children: offerWaitlist ? "Sorry, this event is now full. You can join the waiting list, and we’ll email you if a place becomes available." : message
132007
+ }),
132008
+ actions: action === "none" ? void 0 : action === "portal" || action === "payment-status" ? /* @__PURE__ */ jsx("a", {
132009
+ href: "/portal",
132010
+ className: "er-retry-btn",
132011
+ children: labels[action]
132012
+ }) : /* @__PURE__ */ jsx("button", {
132013
+ type: "button",
132014
+ onClick: onRetry,
132015
+ className: "er-retry-btn",
132016
+ children: offerWaitlist ? "Join the waiting list" : labels[action]
132017
+ })
132018
+ });
132019
+ };
132020
+ //#endregion
131620
132021
  //#region ../blocks/src/system/runtime/nodes/event-registration/EventRegistrationSummaryBar.tsx
131621
132022
  function getScheduleSummary(wizard, summaryEvent) {
131622
132023
  if (summaryEvent.registrationSubject.kind !== "wholeSeries") {
@@ -131900,7 +132301,7 @@ function SelectTicketsActions(props) {
131900
132301
  const currentEvent = wizard.selectedEvent ?? wizard.preSelectedEvent;
131901
132302
  const registrationOptionsPending = wizard.registrationOptionsState.tag === "idle" || wizard.registrationOptionsState.tag === "loading";
131902
132303
  const registrationOptionsFailed = wizard.registrationOptionsState.tag === "failed";
131903
- const isSoldOutWithoutWaitlist = !hasWaitlistClaim && isFullWithNoWaitlist(currentEvent, wizard.selectedAttendanceMode);
132304
+ const isSoldOutWithoutWaitlist = !wizard.checkoutRecovery && !hasWaitlistClaim && isFullWithNoWaitlist(currentEvent, wizard.selectedAttendanceMode);
131904
132305
  const isCheckingExistingRegistration = wizard.portalAuthState === "authenticated" && wizard.existingRegistrationLoading;
131905
132306
  const hasExistingRegistration = isExistingRegistrationBlocking$1(wizard);
131906
132307
  const ticketTypeSelectionBlocked = wizard.ticketTypeSelectionRequired && !wizard.hasValidTicketSelection;
@@ -132490,6 +132891,31 @@ function resolveTicketSelectionPricing(event) {
132490
132891
  currency: event ? publicEventPricingCurrency(event.pricing) : null
132491
132892
  };
132492
132893
  }
132894
+ function CheckoutRecoveryEntry(props) {
132895
+ return props.onContinue ? /* @__PURE__ */ jsx("button", {
132896
+ type: "button",
132897
+ className: "er-back-btn",
132898
+ onClick: props.onContinue,
132899
+ children: "Continue an existing booking"
132900
+ }) : null;
132901
+ }
132902
+ function getTicketSelectionCapacity(event, attendanceMode, checkoutRecovery) {
132903
+ if (checkoutRecovery) return {
132904
+ availableSpots: null,
132905
+ isSoldOut: false,
132906
+ canJoinWaitlist: false,
132907
+ eventAvailabilityLabel: null
132908
+ };
132909
+ const source = event ? selectEventCapacitySource(event, attendanceMode) : null;
132910
+ const availability = source ? getEventAvailability(source) : null;
132911
+ const canJoinWaitlist = source ? canJoinEventWaitlist(source) : false;
132912
+ return {
132913
+ availableSpots: availability?.available,
132914
+ isSoldOut: availability?.isFull ?? false,
132915
+ canJoinWaitlist,
132916
+ eventAvailabilityLabel: !canJoinWaitlist && source ? formatRemainingPlaces(source) : null
132917
+ };
132918
+ }
132493
132919
  /**
132494
132920
  * Ticket Selection Step
132495
132921
  *
@@ -132498,15 +132924,11 @@ function resolveTicketSelectionPricing(event) {
132498
132924
  *
132499
132925
  * Can use either a full PublicEvent or just occurrence context (when on event page).
132500
132926
  */
132501
- const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, selectedAttendanceMode = null, onAttendanceModeChange, ticketsCount, maxTickets, visibleTicketTypes: visibleTicketTypesOverride, ticketSelections = [], onTicketSelectionsChange, slidingScale = null, selectedSlidingAmountInput = "", slidingScaleSelectionMessage = null, onSlidingAmountChange, onTicketsChange, bookingUnit = { kind: "ticket" } }) => {
132927
+ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, selectedAttendanceMode = null, onAttendanceModeChange, ticketsCount, maxTickets, visibleTicketTypes: visibleTicketTypesOverride, ticketSelections = [], onTicketSelectionsChange, slidingScale = null, selectedSlidingAmountInput = "", slidingScaleSelectionMessage = null, onSlidingAmountChange, onTicketsChange, bookingUnit = { kind: "ticket" }, checkoutRecovery = false, onCheckoutRecovery }) => {
132502
132928
  const unit = bookingUnit.kind;
132503
132929
  const deliveryMode = event?.deliveryMode ?? "in_person";
132504
132930
  const attendanceModes = event?.attendanceModes ?? [];
132505
- const capacitySource = event ? selectEventCapacitySource(event, selectedAttendanceMode) : null;
132506
- const availability = capacitySource ? getEventAvailability(capacitySource) : null;
132507
- const availableSpots = availability?.available;
132508
- const isSoldOut = availability?.isFull ?? false;
132509
- const canJoinWaitlist = capacitySource ? canJoinEventWaitlist(capacitySource) : false;
132931
+ const { availableSpots, isSoldOut, canJoinWaitlist, eventAvailabilityLabel } = getTicketSelectionCapacity(event, selectedAttendanceMode, checkoutRecovery);
132510
132932
  const { mode: eventPricingMode, currency: eventPricingCurrency } = resolveTicketSelectionPricing(event);
132511
132933
  const usesTicketTypes = eventPricingMode === "tiered" && !canJoinWaitlist;
132512
132934
  const visibleTicketTypes = usesTicketTypes ? visibleTicketTypesOverride ?? getVisibleTicketTypes({
@@ -132525,7 +132947,6 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
132525
132947
  selectedTicketTypeRemainingCapacity: implicitTicketType?.availability.remainingPlaces ?? null,
132526
132948
  hasSelectedTicketType: implicitTicketType !== null
132527
132949
  });
132528
- const eventAvailabilityLabel = !canJoinWaitlist && capacitySource ? formatRemainingPlaces(capacitySource) : null;
132529
132950
  if (deliveryMode === "hybrid" && !selectedAttendanceMode) return /* @__PURE__ */ jsxs("div", {
132530
132951
  className: "rb-reg-stack er-stack",
132531
132952
  children: [
@@ -132546,13 +132967,17 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
132546
132967
  });
132547
132968
  if (isSoldOut && !canJoinWaitlist) return /* @__PURE__ */ jsxs("div", {
132548
132969
  className: "rb-reg-stack er-stack",
132549
- children: [/* @__PURE__ */ jsx("h3", {
132550
- className: "er-title",
132551
- children: "Sold out"
132552
- }), /* @__PURE__ */ jsx("p", {
132553
- className: "er-text er-text--muted",
132554
- children: "Sorry you missed out! This event is fully booked."
132555
- })]
132970
+ children: [
132971
+ /* @__PURE__ */ jsx("h3", {
132972
+ className: "er-title",
132973
+ children: "Sold out"
132974
+ }),
132975
+ /* @__PURE__ */ jsx("p", {
132976
+ className: "er-text er-text--muted",
132977
+ children: "Sorry you missed out! This event is fully booked."
132978
+ }),
132979
+ /* @__PURE__ */ jsx(CheckoutRecoveryEntry, { onContinue: onCheckoutRecovery })
132980
+ ]
132556
132981
  });
132557
132982
  if (!eventPricingCurrency) return /* @__PURE__ */ jsx("div", {
132558
132983
  className: "rb-reg-stack er-stack",
@@ -132620,9 +133045,13 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
132620
133045
  })]
132621
133046
  })
132622
133047
  ] }),
132623
- canJoinWaitlist && /* @__PURE__ */ jsx("p", {
133048
+ canJoinWaitlist && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
132624
133049
  className: "er-text er-text--muted",
132625
133050
  children: "This event is currently full. Continue to join the waitlist if a place opens up."
133051
+ }), /* @__PURE__ */ jsx(CheckoutRecoveryEntry, { onContinue: onCheckoutRecovery })] }),
133052
+ checkoutRecovery && /* @__PURE__ */ jsx("p", {
133053
+ className: "er-text er-text--muted",
133054
+ children: "Enter the details from your earlier booking. We’ll check your checkout before continuing to payment."
132626
133055
  })
132627
133056
  ]
132628
133057
  });
@@ -133530,12 +133959,14 @@ function EventRegistrationWizardContent(props) {
133530
133959
  onBookForSomeoneElse: wizard.handleBookForSomeoneElse
133531
133960
  }), /* @__PURE__ */ jsx(TicketSelectionStep, {
133532
133961
  event: wizard.selectedEvent,
133962
+ checkoutRecovery: wizard.checkoutRecovery,
133963
+ onCheckoutRecovery: canRecoverEventCheckout(wizard.selectedEvent) ? wizard.startCheckoutRecovery : void 0,
133533
133964
  occurrenceContext: wizard.occurrenceContext,
133534
133965
  selectedAttendanceMode: wizard.selectedAttendanceMode,
133535
133966
  onAttendanceModeChange: wizard.setSelectedAttendanceMode,
133536
133967
  ticketsCount: wizard.ticketsCount,
133537
133968
  maxTickets: wizard.maxTicketsNum,
133538
- visibleTicketTypes: wizard.eligibility ? wizard.visibleTicketTypes : void 0,
133969
+ visibleTicketTypes: wizard.visibleTicketTypes,
133539
133970
  ticketSelections: wizard.selectedTicketLines,
133540
133971
  onTicketSelectionsChange: wizard.setSelectedTicketLines,
133541
133972
  slidingScale: wizard.selectedEventSlidingScale,
@@ -133554,66 +133985,6 @@ function EventRegistrationWizardContent(props) {
133554
133985
  }
133555
133986
  }
133556
133987
  //#endregion
133557
- //#region ../blocks/src/system/runtime/nodes/event-registration/StepIndicator.tsx
133558
- /**
133559
- * Step indicator component for the event registration wizard.
133560
- * Thin wrapper around the shared StepIndicator: styled by the shared
133561
- * registration-surface classes, with 'er'-prefixed classes kept in the
133562
- * markup as the stable hook surface for per-site theme CSS.
133563
- */
133564
- function StepIndicator(props) {
133565
- return /* @__PURE__ */ jsx(StepIndicator$1, {
133566
- ...props,
133567
- classPrefix: "er",
133568
- emitSharedClasses: true
133569
- });
133570
- }
133571
- //#endregion
133572
- //#region ../blocks/src/system/runtime/nodes/event-registration/ErrorStep.tsx
133573
- /**
133574
- * Error Step
133575
- *
133576
- * Displays an error message with a retry button when registration fails.
133577
- */
133578
- const labels = {
133579
- none: "",
133580
- retry: "Try again",
133581
- places: "Choose places",
133582
- start: "Start again",
133583
- details: "Check my details",
133584
- "payment-options": "View payment options",
133585
- "payment-status": "Check payment status",
133586
- portal: "View my bookings",
133587
- event: "View event"
133588
- };
133589
- const ErrorStep = ({ message, onRetry, action = "retry", canJoinWaitlist = false }) => {
133590
- const offerWaitlist = action === "places" && canJoinWaitlist;
133591
- return /* @__PURE__ */ jsx(TerminalPanel, {
133592
- tone: "error",
133593
- className: "er-center",
133594
- titleClassName: "er-title--lg er-message",
133595
- icon: /* @__PURE__ */ jsx(StateIcon, {
133596
- variant: "error",
133597
- children: /* @__PURE__ */ jsx(CrossIcon, {})
133598
- }),
133599
- title: "We couldn’t complete your booking",
133600
- body: /* @__PURE__ */ jsx("p", {
133601
- className: "er-text--muted er-message",
133602
- children: offerWaitlist ? "Sorry, this event is now full. You can join the waiting list, and we’ll email you if a place becomes available." : message
133603
- }),
133604
- actions: action === "none" ? void 0 : action === "portal" || action === "payment-status" ? /* @__PURE__ */ jsx("a", {
133605
- href: "/portal",
133606
- className: "er-retry-btn",
133607
- children: labels[action]
133608
- }) : /* @__PURE__ */ jsx("button", {
133609
- type: "button",
133610
- onClick: onRetry,
133611
- className: "er-retry-btn",
133612
- children: offerWaitlist ? "Join the waiting list" : labels[action]
133613
- })
133614
- });
133615
- };
133616
- //#endregion
133617
133988
  //#region ../blocks/src/system/runtime/nodes/event-registration/PaymentFailedStep.tsx
133618
133989
  /**
133619
133990
  * Payment Failed Step
@@ -133644,6 +134015,21 @@ const PaymentFailedStep = ({ onRetry }) => {
133644
134015
  });
133645
134016
  };
133646
134017
  //#endregion
134018
+ //#region ../blocks/src/system/runtime/nodes/event-registration/StepIndicator.tsx
134019
+ /**
134020
+ * Step indicator component for the event registration wizard.
134021
+ * Thin wrapper around the shared StepIndicator: styled by the shared
134022
+ * registration-surface classes, with 'er'-prefixed classes kept in the
134023
+ * markup as the stable hook surface for per-site theme CSS.
134024
+ */
134025
+ function StepIndicator(props) {
134026
+ return /* @__PURE__ */ jsx(StepIndicator$1, {
134027
+ ...props,
134028
+ classPrefix: "er",
134029
+ emitSharedClasses: true
134030
+ });
134031
+ }
134032
+ //#endregion
133647
134033
  //#region ../blocks/src/system/runtime/nodes/event-registration/SuccessStep.tsx
133648
134034
  /**
133649
134035
  * Success Step
@@ -133916,7 +134302,8 @@ function EventRegistrationSurface(props) {
133916
134302
  }), /* @__PURE__ */ jsx(EventRegistrationTerminalState, {
133917
134303
  wizard,
133918
134304
  supportEmail: props.supportEmail,
133919
- successMessage: props.successMessage
134305
+ successMessage: props.successMessage,
134306
+ buttonClass: props.buttonClass
133920
134307
  })]
133921
134308
  });
133922
134309
  }
@@ -134389,6 +134776,84 @@ function PastOccurrenceNotice(props) {
134389
134776
  });
134390
134777
  }
134391
134778
  //#endregion
134779
+ //#region ../blocks/src/system/runtime/nodes/event-registration/useEventSourceRecovery.ts
134780
+ const RECOVERY_DELAYS_MS = [
134781
+ 0,
134782
+ 250,
134783
+ 750
134784
+ ];
134785
+ const defaultRecoveryDeps = {
134786
+ useApi: useBlockApi,
134787
+ wait: waitForRetry
134788
+ };
134789
+ function useEventSourceRecovery(source, deps = defaultRecoveryDeps) {
134790
+ const api = deps.useApi();
134791
+ const wait = deps.wait;
134792
+ const [cycle, setCycle] = useState(0);
134793
+ const [state, setState] = useState(() => source.kind === "loaded" ? {
134794
+ kind: "ready",
134795
+ events: source.events
134796
+ } : { kind: "loading" });
134797
+ const retry = useCallback(() => {
134798
+ setState({ kind: "loading" });
134799
+ setCycle((value) => value + 1);
134800
+ }, []);
134801
+ useEffect(() => {
134802
+ if (source.kind === "loaded") {
134803
+ setState({
134804
+ kind: "ready",
134805
+ events: source.events
134806
+ });
134807
+ return;
134808
+ }
134809
+ if (!source.request) {
134810
+ setState({
134811
+ kind: "exhausted",
134812
+ retry
134813
+ });
134814
+ return;
134815
+ }
134816
+ const controller = new AbortController();
134817
+ const isCurrent = () => !controller.signal.aborted;
134818
+ (async () => {
134819
+ for (const delayMs of RECOVERY_DELAYS_MS) try {
134820
+ await wait(delayMs, controller.signal);
134821
+ const events = await api.getPublicEvents(source.request ?? void 0);
134822
+ if (isCurrent()) setState({
134823
+ kind: "ready",
134824
+ events
134825
+ });
134826
+ return;
134827
+ } catch {
134828
+ if (!isCurrent()) return;
134829
+ }
134830
+ if (isCurrent()) setState({
134831
+ kind: "exhausted",
134832
+ retry
134833
+ });
134834
+ })();
134835
+ return () => controller.abort();
134836
+ }, [
134837
+ api,
134838
+ cycle,
134839
+ retry,
134840
+ source,
134841
+ wait
134842
+ ]);
134843
+ return state;
134844
+ }
134845
+ function waitForRetry(milliseconds, signal) {
134846
+ if (signal.aborted) return Promise.reject(/* @__PURE__ */ new Error("Recovery aborted"));
134847
+ if (milliseconds === 0) return Promise.resolve();
134848
+ return new Promise((resolve, reject) => {
134849
+ const timeout = setTimeout(resolve, milliseconds);
134850
+ signal.addEventListener("abort", () => {
134851
+ clearTimeout(timeout);
134852
+ reject(/* @__PURE__ */ new Error("Recovery aborted"));
134853
+ }, { once: true });
134854
+ });
134855
+ }
134856
+ //#endregion
134392
134857
  //#region ../blocks/src/system/runtime/nodes/event-registration.client.tsx
134393
134858
  function toEventRegistrationClientProps(props, display) {
134394
134859
  return {
@@ -134422,8 +134887,71 @@ const EventRegistrationIslandClient = (props) => {
134422
134887
  title: props.render.display.titleText,
134423
134888
  body: props.render.display.bodyText
134424
134889
  });
134425
- return /* @__PURE__ */ jsx(EventRegistrationClient, { ...toEventRegistrationClientProps(props, props.render.display) });
134890
+ return /* @__PURE__ */ jsx(RecoverableEventRegistrationClient, {
134891
+ props,
134892
+ display: props.render.display
134893
+ });
134426
134894
  };
134895
+ function RecoverableEventRegistrationClient({ props, display }) {
134896
+ const recovery = useEventSourceRecovery(props.render.hydration.eventSource);
134897
+ if (recovery.kind === "loading") return /* @__PURE__ */ jsxs("div", {
134898
+ id: EVENT_REGISTRATION_ANCHOR_ID,
134899
+ className: "rb-reg-surface er-container er-registration-shell",
134900
+ "aria-busy": "true",
134901
+ role: "status",
134902
+ children: [
134903
+ /* @__PURE__ */ jsx("p", {
134904
+ className: "er-registration-shell__eyebrow",
134905
+ children: "Registration"
134906
+ }),
134907
+ /* @__PURE__ */ jsx("h3", {
134908
+ className: "er-title er-registration-shell__title",
134909
+ children: display.titleText
134910
+ }),
134911
+ /* @__PURE__ */ jsx("p", {
134912
+ className: "er-text er-text--muted",
134913
+ children: "Loading event dates…"
134914
+ }),
134915
+ /* @__PURE__ */ jsxs("div", {
134916
+ className: "er-event-grid er-registration-shell__cards",
134917
+ "aria-hidden": "true",
134918
+ children: [/* @__PURE__ */ jsx("div", { className: "er-event-card er-registration-shell__card" }), /* @__PURE__ */ jsx("div", { className: "er-event-card er-registration-shell__card" })]
134919
+ })
134920
+ ]
134921
+ });
134922
+ if (recovery.kind === "exhausted") return /* @__PURE__ */ jsxs("div", {
134923
+ id: EVENT_REGISTRATION_ANCHOR_ID,
134924
+ className: "rb-reg-surface er-container er-registration-shell",
134925
+ role: "alert",
134926
+ children: [/* @__PURE__ */ jsx("p", { children: "We couldn’t load the event dates. Please try again." }), /* @__PURE__ */ jsx("button", {
134927
+ type: "button",
134928
+ className: themeButtonClassName({
134929
+ variant: props.render.hydration.buttonVariant ?? "primary",
134930
+ size: "md"
134931
+ }),
134932
+ onClick: recovery.retry,
134933
+ children: "Try again"
134934
+ })]
134935
+ });
134936
+ const events = resolveEventRegistrationScopedEvents(recovery.events, {
134937
+ occurrenceContext: props.render.hydration.occurrenceContext,
134938
+ subject: props.render.hydration.subject
134939
+ });
134940
+ const recoveredDisplay = buildEventRegistrationDisplayFromScopedEvents({
134941
+ ...props.render.hydration,
134942
+ events,
134943
+ className: display.className,
134944
+ buttonText: display.summaryButtonText
134945
+ }, events);
134946
+ if (recoveredDisplay.state === "registration-closed") return /* @__PURE__ */ jsx(RegistrationClosedState, {
134947
+ title: recoveredDisplay.titleText,
134948
+ body: recoveredDisplay.bodyText
134949
+ });
134950
+ return /* @__PURE__ */ jsx(EventRegistrationClient, {
134951
+ ...toEventRegistrationClientProps(props, display),
134952
+ events
134953
+ });
134954
+ }
134427
134955
  //#endregion
134428
134956
  //#region ../blocks/src/system/runtime/nodes/shared/LeafletStaticMap.tsx
134429
134957
  /**
@@ -142273,6 +142801,11 @@ function NewsletterFormClient({ render }) {
142273
142801
  const api = useBlockApi();
142274
142802
  const [submissionState, dispatchSubmission] = useReducer(newsletterFormSubmissionReducer, initialNewsletterFormSubmissionState);
142275
142803
  const [altchaSolution, setAltchaSolution] = useState(null);
142804
+ const successRef = useRef(null);
142805
+ const succeeded = isNewsletterFormSuccess(submissionState);
142806
+ useEffect(() => {
142807
+ if (succeeded) successRef.current?.focus();
142808
+ }, [succeeded]);
142276
142809
  const showSpamProtection = render.hydration.spamProtectionEnabled ?? isSpamProtectionEnabled();
142277
142810
  const fields = render.display.fields;
142278
142811
  const handleSubmit = async (e) => {
@@ -142312,7 +142845,7 @@ function NewsletterFormClient({ render }) {
142312
142845
  if (render.hydration.setupRevision === null || render.display.availability === "unavailable") {
142313
142846
  dispatchSubmission({
142314
142847
  type: "failed",
142315
- error: newsletterFormError("Mailing-list signup is not available right now.")
142848
+ error: newsletterFormError(SIGNUPS_PAUSED_MESSAGE)
142316
142849
  });
142317
142850
  return;
142318
142851
  }
@@ -142320,7 +142853,7 @@ function NewsletterFormClient({ render }) {
142320
142853
  if (visitorSelection === null) {
142321
142854
  dispatchSubmission({
142322
142855
  type: "failed",
142323
- error: newsletterFormError("Choose at least one email topic.")
142856
+ error: newsletterFieldError(TOPIC_CHOICE_FIELD_ID, "Choose at least one email topic.")
142324
142857
  });
142325
142858
  return;
142326
142859
  }
@@ -142352,71 +142885,27 @@ function NewsletterFormClient({ render }) {
142352
142885
  } catch (err) {
142353
142886
  dispatchSubmission({
142354
142887
  type: "failed",
142355
- error: newsletterFormError(err instanceof Error ? err.message : "Subscription failed. Please try again.")
142888
+ error: newsletterFormError(transportFailureMessage(err))
142356
142889
  });
142357
142890
  }
142358
142891
  };
142359
- const renderField = (field) => {
142360
- const inputId = `newsletter_${field.id}`;
142361
- const fieldError = getNewsletterSubmissionFieldError(submissionState, field.id);
142362
- const fieldErrorId = `${inputId}-error`;
142363
- const validationProps = getFieldValidationControlProps({
142364
- message: fieldError,
142365
- messageId: fieldErrorId
142366
- });
142367
- return /* @__PURE__ */ jsxs("div", {
142368
- className: "fb-field newsletter-field",
142369
- children: [
142370
- /* @__PURE__ */ jsxs("label", {
142371
- htmlFor: inputId,
142372
- className: "form-label",
142373
- children: [field.label, field.required && /* @__PURE__ */ jsx("span", {
142374
- className: "newsletter-required",
142375
- "aria-hidden": "true",
142376
- children: "*"
142377
- })]
142378
- }),
142379
- /* @__PURE__ */ jsx("input", {
142380
- id: inputId,
142381
- name: field.id,
142382
- type: field.type,
142383
- required: field.required,
142384
- placeholder: field.placeholder,
142385
- className: "form-input",
142386
- autoComplete: field.type === "email" ? "email" : void 0,
142387
- disabled: isNewsletterFormSubmitting(submissionState),
142388
- ...validationProps
142389
- }),
142390
- fieldError && /* @__PURE__ */ jsx(FieldValidationMessage, {
142391
- id: fieldErrorId,
142392
- className: "fb-error",
142393
- children: fieldError
142394
- })
142395
- ]
142396
- }, field.id);
142397
- };
142892
+ const submitting = isNewsletterFormSubmitting(submissionState);
142398
142893
  const formError = getNewsletterSubmissionFormError(submissionState);
142399
- if (isNewsletterFormSuccess(submissionState)) {
142400
- const outcome = getNewsletterSuccessOutcome(submissionState);
142401
- return /* @__PURE__ */ jsx("div", {
142402
- className: clsx("newsletter-form", "newsletter-success", render.display.className ?? void 0),
142403
- children: /* @__PURE__ */ jsx(FeedbackMessage, {
142404
- tone: "success",
142405
- className: "fb-success",
142406
- role: "status",
142407
- children: outcome === "confirmation_required" ? "Please check your inbox to confirm your signup." : "Thanks — you’re on the list."
142408
- })
142409
- });
142410
- }
142894
+ if (succeeded) return /* @__PURE__ */ jsx("div", {
142895
+ ref: successRef,
142896
+ tabIndex: -1,
142897
+ className: clsx("newsletter-form", "newsletter-success", render.display.className ?? void 0),
142898
+ children: /* @__PURE__ */ jsx(FeedbackMessage, {
142899
+ tone: "success",
142900
+ className: "fb-success",
142901
+ role: "status",
142902
+ children: getNewsletterSuccessOutcome(submissionState) === "confirmation_required" ? "Please check your inbox to confirm your signup." : render.display.successMessage
142903
+ })
142904
+ });
142411
142905
  if (render.display.availability === "unavailable") return /* @__PURE__ */ jsx(UnavailableNewsletterForm, { className: render.display.className });
142412
- return /* @__PURE__ */ jsxs("div", {
142906
+ return /* @__PURE__ */ jsx("div", {
142413
142907
  className: clsx("newsletter-form", render.display.className ?? void 0),
142414
- children: [formError && /* @__PURE__ */ jsx(FeedbackMessage, {
142415
- tone: "error",
142416
- className: "fb-error",
142417
- role: "alert",
142418
- children: formError
142419
- }), /* @__PURE__ */ jsxs("form", {
142908
+ children: /* @__PURE__ */ jsxs("form", {
142420
142909
  onSubmit: handleSubmit,
142421
142910
  className: "fb-form",
142422
142911
  children: [
@@ -142431,18 +142920,15 @@ function NewsletterFormClient({ render }) {
142431
142920
  /* @__PURE__ */ jsxs("div", {
142432
142921
  className: "newsletter-form-fields",
142433
142922
  children: [
142434
- fields.map(renderField),
142435
- render.hydration.choicePresentation === "visitor_choice" ? /* @__PURE__ */ jsxs("fieldset", {
142436
- className: "newsletter-topics",
142437
- children: [/* @__PURE__ */ jsx("legend", { children: "What would you like to hear about?" }), render.display.choices.map((choice) => /* @__PURE__ */ jsxs("label", {
142438
- className: "newsletter-topic-choice",
142439
- children: [/* @__PURE__ */ jsx("input", {
142440
- type: "checkbox",
142441
- name: "mailingListChoice",
142442
- value: choice.id,
142443
- disabled: isNewsletterFormSubmitting(submissionState)
142444
- }), choice.label]
142445
- }, choice.id))]
142923
+ fields.map((field) => /* @__PURE__ */ jsx(NewsletterField, {
142924
+ field,
142925
+ submitting,
142926
+ error: getNewsletterSubmissionFieldError(submissionState, field.id)
142927
+ }, field.id)),
142928
+ render.hydration.choicePresentation === "visitor_choice" ? /* @__PURE__ */ jsx(NewsletterTopicChoices, {
142929
+ choices: render.display.choices,
142930
+ submitting,
142931
+ error: getNewsletterSubmissionFieldError(submissionState, TOPIC_CHOICE_FIELD_ID)
142446
142932
  }) : null,
142447
142933
  showSpamProtection && /* @__PURE__ */ jsx("div", {
142448
142934
  className: "newsletter-altcha",
@@ -142451,6 +142937,12 @@ function NewsletterFormClient({ render }) {
142451
142937
  onSolutionChange: setAltchaSolution
142452
142938
  })
142453
142939
  }),
142940
+ formError && /* @__PURE__ */ jsx(FeedbackMessage, {
142941
+ tone: "error",
142942
+ className: "fb-error",
142943
+ role: "alert",
142944
+ children: formError
142945
+ }),
142454
142946
  /* @__PURE__ */ jsx("div", {
142455
142947
  className: "newsletter-submit",
142456
142948
  children: /* @__PURE__ */ jsx("button", {
@@ -142459,8 +142951,8 @@ function NewsletterFormClient({ render }) {
142459
142951
  variant: render.display.buttonVariant,
142460
142952
  size: "md"
142461
142953
  }),
142462
- disabled: isNewsletterFormSubmitting(submissionState),
142463
- children: isNewsletterFormSubmitting(submissionState) ? "Subscribing..." : render.display.buttonLabel
142954
+ disabled: submitting,
142955
+ children: submitting ? "Subscribing..." : render.display.buttonLabel
142464
142956
  })
142465
142957
  })
142466
142958
  ]
@@ -142470,9 +142962,70 @@ function NewsletterFormClient({ render }) {
142470
142962
  children: render.display.privacyNote
142471
142963
  })
142472
142964
  ]
142473
- })]
142965
+ })
142474
142966
  });
142475
142967
  }
142968
+ function NewsletterField({ field, submitting, error }) {
142969
+ const inputId = `newsletter_${field.id}`;
142970
+ const errorId = `${inputId}-error`;
142971
+ return /* @__PURE__ */ jsxs("div", {
142972
+ className: "fb-field newsletter-field",
142973
+ children: [
142974
+ /* @__PURE__ */ jsxs("label", {
142975
+ htmlFor: inputId,
142976
+ className: "form-label",
142977
+ children: [field.label, field.required && /* @__PURE__ */ jsx("span", {
142978
+ className: "newsletter-required",
142979
+ "aria-hidden": "true",
142980
+ children: "*"
142981
+ })]
142982
+ }),
142983
+ /* @__PURE__ */ jsx("input", {
142984
+ id: inputId,
142985
+ name: field.id,
142986
+ type: field.type,
142987
+ required: field.required,
142988
+ placeholder: field.placeholder,
142989
+ className: "form-input",
142990
+ autoComplete: field.type === "email" ? "email" : void 0,
142991
+ disabled: submitting,
142992
+ ...getFieldValidationControlProps({
142993
+ message: error,
142994
+ messageId: errorId
142995
+ })
142996
+ }),
142997
+ error && /* @__PURE__ */ jsx(FieldValidationMessage, {
142998
+ id: errorId,
142999
+ className: "fb-error",
143000
+ children: error
143001
+ })
143002
+ ]
143003
+ });
143004
+ }
143005
+ function NewsletterTopicChoices({ choices, submitting, error }) {
143006
+ const errorId = `newsletter_${TOPIC_CHOICE_FIELD_ID}-error`;
143007
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("fieldset", {
143008
+ className: "fb-fieldset newsletter-topics",
143009
+ "aria-describedby": error ? errorId : void 0,
143010
+ children: [/* @__PURE__ */ jsx("legend", {
143011
+ className: "form-label",
143012
+ children: "What would you like to hear about?"
143013
+ }), choices.map((choice) => /* @__PURE__ */ jsxs("label", {
143014
+ className: "fb-inline form-label newsletter-topic-choice",
143015
+ children: [/* @__PURE__ */ jsx("input", {
143016
+ type: "checkbox",
143017
+ name: TOPIC_CHOICE_FIELD_ID,
143018
+ value: choice.id,
143019
+ className: "form-checkbox",
143020
+ disabled: submitting
143021
+ }), /* @__PURE__ */ jsx("span", { children: choice.label })]
143022
+ }, choice.id))]
143023
+ }), error && /* @__PURE__ */ jsx(FieldValidationMessage, {
143024
+ id: errorId,
143025
+ className: "fb-error",
143026
+ children: error
143027
+ })] });
143028
+ }
142476
143029
  function UnavailableNewsletterForm({ className }) {
142477
143030
  return /* @__PURE__ */ jsx("div", {
142478
143031
  className: clsx("newsletter-form", className ?? void 0),
@@ -142480,13 +143033,32 @@ function UnavailableNewsletterForm({ className }) {
142480
143033
  tone: "error",
142481
143034
  className: "fb-error",
142482
143035
  role: "status",
142483
- children: "Mailing-list signup is not available right now."
143036
+ children: SIGNUPS_PAUSED_MESSAGE
142484
143037
  })
142485
143038
  });
142486
143039
  }
143040
+ const TOPIC_CHOICE_FIELD_ID = "mailingListChoice";
143041
+ /** The site's setup is broken or missing: nothing the visitor does will help. */
143042
+ const SIGNUPS_PAUSED_MESSAGE = "We can’t accept signups right now. Please try again later.";
143043
+ /** The provider did not answer in time or at all: worth another go shortly. */
143044
+ const RETRY_SHORTLY_MESSAGE = "We couldn’t confirm your signup. Please try again in a little while.";
143045
+ const TOO_MANY_ATTEMPTS_MESSAGE = "There have been too many signup attempts. Please wait a moment and try again.";
143046
+ /**
143047
+ * Vetted copy for a request that never produced a signup response. The raw
143048
+ * error text ("Failed to fetch", "received invalid JSON") is for logs, not
143049
+ * visitors.
143050
+ */
143051
+ function transportFailureMessage(err) {
143052
+ if (err instanceof BlockApiError) {
143053
+ if (err.status === 429) return TOO_MANY_ATTEMPTS_MESSAGE;
143054
+ if (err.status === 403) return "We couldn’t verify this submission. Please try again.";
143055
+ if (err.status === 400 || err.status === 422) return "Please check your details and try again.";
143056
+ }
143057
+ return RETRY_SHORTLY_MESSAGE;
143058
+ }
142487
143059
  function readVisitorSelection(formData, hydration) {
142488
143060
  if (hydration.choicePresentation !== "visitor_choice") return void 0;
142489
- const ids = formData.getAll("mailingListChoice").flatMap((value) => typeof value === "string" ? [value] : []);
143061
+ const ids = formData.getAll(TOPIC_CHOICE_FIELD_ID).flatMap((value) => typeof value === "string" ? [value] : []);
142490
143062
  if (ids.length === 0) return null;
142491
143063
  const raw = hydration.choiceProvider === "native" ? {
142492
143064
  provider: "native",
@@ -142508,11 +143080,10 @@ function publicSignupError(outcome) {
142508
143080
  case "unavailable": switch (outcome.reason) {
142509
143081
  case "configuration_unavailable":
142510
143082
  case "authentication_failed":
142511
- case "target_unavailable": return "Mailing-list signup is not available right now.";
143083
+ case "target_unavailable": return SIGNUPS_PAUSED_MESSAGE;
142512
143084
  case "stale_configuration": return "This signup form has changed. Refresh the page, then try again.";
142513
- case "subscriber_suppressed": return "This address cannot be added. Please contact the site if you would like help.";
142514
- case "rate_limited": return "There have been too many signup attempts. Please wait a moment and try again.";
142515
- case "temporarily_unavailable": return "We could not sign you up just now. Please try again shortly.";
143085
+ case "rate_limited": return TOO_MANY_ATTEMPTS_MESSAGE;
143086
+ case "temporarily_unavailable": return RETRY_SHORTLY_MESSAGE;
142516
143087
  }
142517
143088
  case "uncertain": return "We could not confirm whether your signup completed. Please wait a moment, then try again.";
142518
143089
  }
@@ -146749,6 +147320,18 @@ function resolveChildren(nodes, context) {
146749
147320
  function isReactNode(value) {
146750
147321
  return value !== null && value !== void 0;
146751
147322
  }
147323
+ function resolveNodeMetadata(nodeType, context) {
147324
+ return {
147325
+ pathBase: context.pathBase,
147326
+ blockId: context.blockId,
147327
+ blockKind: context.blockKind,
147328
+ blockContent: context.blockContent,
147329
+ fragmentRegistry: context.fragmentRegistry,
147330
+ previewAuthoring: context.previewAuthoring,
147331
+ ...nodeType.toLowerCase() === "newsletter-form" ? { contentVersionId: context.contentVersionId } : {},
147332
+ ...nodeType === "media" && context.imageResolveContext ? { imageResolveContext: context.imageResolveContext } : {}
147333
+ };
147334
+ }
146752
147335
  /**
146753
147336
  * Resolves a node definition into a React element.
146754
147337
  * Handles conditionals ($when), scopes ($scopes), repeaters ($repeat), bindings ($bind), and children.
@@ -146784,13 +147367,7 @@ function resolveNode(node, context, indexFallback) {
146784
147367
  const elementProps = {
146785
147368
  ...props,
146786
147369
  key: node.key ?? indexFallback,
146787
- pathBase: context.pathBase,
146788
- blockId: context.blockId,
146789
- blockKind: context.blockKind,
146790
- blockContent: context.blockContent,
146791
- ...node.type === "media" && context.imageResolveContext ? { imageResolveContext: context.imageResolveContext } : {},
146792
- fragmentRegistry: context.fragmentRegistry,
146793
- previewAuthoring: context.previewAuthoring
147370
+ ...resolveNodeMetadata(node.type, context)
146794
147371
  };
146795
147372
  const element = React.createElement(component, elementProps, ...children);
146796
147373
  if (headingDecorationPlan.kind === "skip") return element;
@@ -158952,7 +159529,7 @@ function collectNestedPreviewFieldConstraintEntries(field, path) {
158952
159529
  }
158953
159530
  function makeDefaultBlockComponent(args) {
158954
159531
  const { manifest } = args;
158955
- return function DefaultBlockComponent({ content, data, registry, theme, themeConfig, transforms, viewModelOverrides, pathBase, blockId, blockKind, blockBindings, editableRouteMetadataFieldIds, fragmentRegistry, imageResolveContext, previewAuthoring }) {
159532
+ return function DefaultBlockComponent({ content, data, registry, theme, themeConfig, transforms, viewModelOverrides, pathBase, blockId, contentVersionId, blockKind, blockBindings, editableRouteMetadataFieldIds, fragmentRegistry, imageResolveContext, previewAuthoring }) {
158956
159533
  const perf = beginBlockRenderMeasurement();
158957
159534
  if (!theme) throw new Error("Block rendering requires theme tokens; none were provided to makeDefaultBlockComponent");
158958
159535
  if (!registry) throw new Error("Block rendering requires a component registry; none was provided");
@@ -158972,6 +159549,7 @@ function makeDefaultBlockComponent(args) {
158972
159549
  theme: withButtonSystem(theme, themeConfig),
158973
159550
  pathBase,
158974
159551
  blockId,
159552
+ contentVersionId,
158975
159553
  blockKind,
158976
159554
  blockContent: mergedContent,
158977
159555
  blockBindings,
@@ -170076,7 +170654,9 @@ const containerResponsiveThemeCss = `/*
170076
170654
  text-align: left;
170077
170655
  }
170078
170656
 
170079
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-login__subtitle {
170657
+ :where(.theme-scope)
170658
+ :where([data-block="customer-portal"])
170659
+ .cp-login__subtitle {
170080
170660
  color: var(--cp-muted);
170081
170661
  font-size: 0.9375rem;
170082
170662
  text-align: left;
@@ -170103,7 +170683,9 @@ const containerResponsiveThemeCss = `/*
170103
170683
  text-align: left;
170104
170684
  }
170105
170685
 
170106
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-login__reset-link {
170686
+ :where(.theme-scope)
170687
+ :where([data-block="customer-portal"])
170688
+ .cp-login__reset-link {
170107
170689
  appearance: none;
170108
170690
  background: none;
170109
170691
  border: 0;
@@ -170117,11 +170699,15 @@ const containerResponsiveThemeCss = `/*
170117
170699
  text-underline-offset: 0.15em;
170118
170700
  }
170119
170701
 
170120
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-login__reset-link:hover {
170702
+ :where(.theme-scope)
170703
+ :where([data-block="customer-portal"])
170704
+ .cp-login__reset-link:hover {
170121
170705
  color: var(--cp-text);
170122
170706
  }
170123
170707
 
170124
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-login__reset-link:focus-visible {
170708
+ :where(.theme-scope)
170709
+ :where([data-block="customer-portal"])
170710
+ .cp-login__reset-link:focus-visible {
170125
170711
  outline: 2px solid currentColor;
170126
170712
  outline-offset: 3px;
170127
170713
  }
@@ -170166,7 +170752,9 @@ const containerResponsiveThemeCss = `/*
170166
170752
  border-bottom: none;
170167
170753
  }
170168
170754
 
170169
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-header__welcome {
170755
+ :where(.theme-scope)
170756
+ :where([data-block="customer-portal"])
170757
+ .cp-header__welcome {
170170
170758
  font-size: 1.125rem;
170171
170759
  font-weight: 500;
170172
170760
  }
@@ -170182,8 +170770,14 @@ const containerResponsiveThemeCss = `/*
170182
170770
  }
170183
170771
 
170184
170772
  @keyframes cp-fade-in {
170185
- from { opacity: 0; transform: translateY(4px); }
170186
- to { opacity: 1; transform: translateY(0); }
170773
+ from {
170774
+ opacity: 0;
170775
+ transform: translateY(4px);
170776
+ }
170777
+ to {
170778
+ opacity: 1;
170779
+ transform: translateY(0);
170780
+ }
170187
170781
  }
170188
170782
 
170189
170783
  /* Empty state */
@@ -170272,6 +170866,77 @@ const containerResponsiveThemeCss = `/*
170272
170866
  margin-top: 1rem;
170273
170867
  }
170274
170868
 
170869
+ /* Event waitlists */
170870
+ :where(.theme-scope) :where([data-block="customer-portal"]) .cp-waitlist-list {
170871
+ display: grid;
170872
+ gap: 1rem;
170873
+ }
170874
+
170875
+ :where(.theme-scope)
170876
+ :where([data-block="customer-portal"])
170877
+ .cp-waitlists
170878
+ > .cp-alert {
170879
+ margin-bottom: 1rem;
170880
+ }
170881
+
170882
+ :where(.theme-scope) :where([data-block="customer-portal"]) .cp-waitlist-card {
170883
+ transition:
170884
+ border-color 180ms ease,
170885
+ box-shadow 180ms ease;
170886
+ }
170887
+
170888
+ :where(.theme-scope)
170889
+ :where([data-block="customer-portal"])
170890
+ .cp-waitlist-card--focused {
170891
+ border-color: var(--cp-primary);
170892
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--cp-primary) 22%, transparent);
170893
+ }
170894
+
170895
+ :where(.theme-scope)
170896
+ :where([data-block="customer-portal"])
170897
+ .cp-waitlist-card__summary,
170898
+ :where(.theme-scope)
170899
+ :where([data-block="customer-portal"])
170900
+ .cp-waitlist-card__position {
170901
+ font-weight: 600;
170902
+ }
170903
+
170904
+ :where(.theme-scope)
170905
+ :where([data-block="customer-portal"])
170906
+ .cp-waitlist-card
170907
+ .cp-card-body
170908
+ p {
170909
+ margin: 0.4rem 0;
170910
+ }
170911
+
170912
+ @container rb-site (max-width: 640px) {
170913
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
170914
+ :where(.theme-scope)
170915
+ :where([data-block="customer-portal"])
170916
+ .cp-waitlist-card
170917
+ .cp-card-header {
170918
+ flex-direction: column-reverse;
170919
+ }
170920
+
170921
+ :where(.theme-scope)
170922
+ :where([data-block="customer-portal"])
170923
+ .cp-waitlist-card
170924
+ .cp-card-actions {
170925
+ align-items: stretch;
170926
+ flex-direction: column;
170927
+ }
170928
+
170929
+ :where(.theme-scope)
170930
+ :where([data-block="customer-portal"])
170931
+ .cp-waitlist-card
170932
+ .cp-card-actions
170933
+ > * {
170934
+ justify-content: center;
170935
+ text-align: center;
170936
+ }
170937
+ }
170938
+ }
170939
+
170275
170940
  /* Badge */
170276
170941
  :where(.theme-scope) :where([data-block="customer-portal"]) .cp-badge {
170277
170942
  display: inline-flex;
@@ -170408,11 +171073,18 @@ const containerResponsiveThemeCss = `/*
170408
171073
  background: rgba(var(--tb-border), 0.2);
170409
171074
  }
170410
171075
 
170411
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-table tbody tr:last-child td {
171076
+ :where(.theme-scope)
171077
+ :where([data-block="customer-portal"])
171078
+ .cp-table
171079
+ tbody
171080
+ tr:last-child
171081
+ td {
170412
171082
  border-bottom: none;
170413
171083
  }
170414
171084
 
170415
- :where(.theme-scope) :where([data-block="customer-portal"]) :is(.cp-row--past, .cp-row--muted) {
171085
+ :where(.theme-scope)
171086
+ :where([data-block="customer-portal"])
171087
+ :is(.cp-row--past, .cp-row--muted) {
170416
171088
  opacity: 0.65;
170417
171089
  }
170418
171090
 
@@ -170425,7 +171097,9 @@ const containerResponsiveThemeCss = `/*
170425
171097
  line-height: 1.2;
170426
171098
  }
170427
171099
 
170428
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-row-title__badges {
171100
+ :where(.theme-scope)
171101
+ :where([data-block="customer-portal"])
171102
+ .cp-row-title__badges {
170429
171103
  margin-left: auto;
170430
171104
  display: flex;
170431
171105
  flex-wrap: wrap;
@@ -170433,7 +171107,9 @@ const containerResponsiveThemeCss = `/*
170433
171107
  gap: 0.375rem;
170434
171108
  }
170435
171109
 
170436
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-row-title__text {
171110
+ :where(.theme-scope)
171111
+ :where([data-block="customer-portal"])
171112
+ .cp-row-title__text {
170437
171113
  min-width: 0;
170438
171114
  }
170439
171115
 
@@ -170453,12 +171129,16 @@ const containerResponsiveThemeCss = `/*
170453
171129
  line-height: 1.35;
170454
171130
  }
170455
171131
 
170456
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-row-note--success {
171132
+ :where(.theme-scope)
171133
+ :where([data-block="customer-portal"])
171134
+ .cp-row-note--success {
170457
171135
  border-left-color: rgb(var(--tb-success));
170458
171136
  color: rgb(var(--tb-success));
170459
171137
  }
170460
171138
 
170461
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-row-note--error {
171139
+ :where(.theme-scope)
171140
+ :where([data-block="customer-portal"])
171141
+ .cp-row-note--error {
170462
171142
  border-left-color: rgb(var(--tb-destructive));
170463
171143
  color: rgb(var(--tb-destructive));
170464
171144
  }
@@ -170477,16 +171157,23 @@ const containerResponsiveThemeCss = `/*
170477
171157
  white-space: nowrap;
170478
171158
  }
170479
171159
 
170480
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-col-actions .btn-sm {
171160
+ :where(.theme-scope)
171161
+ :where([data-block="customer-portal"])
171162
+ .cp-col-actions
171163
+ .btn-sm {
170481
171164
  white-space: nowrap;
170482
171165
  }
170483
171166
 
170484
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-row-title__link {
171167
+ :where(.theme-scope)
171168
+ :where([data-block="customer-portal"])
171169
+ .cp-row-title__link {
170485
171170
  color: inherit;
170486
171171
  text-decoration: none;
170487
171172
  }
170488
171173
 
170489
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-row-title__link:hover {
171174
+ :where(.theme-scope)
171175
+ :where([data-block="customer-portal"])
171176
+ .cp-row-title__link:hover {
170490
171177
  text-decoration: underline;
170491
171178
  text-decoration-thickness: 2px;
170492
171179
  text-underline-offset: 2px;
@@ -170508,7 +171195,9 @@ const containerResponsiveThemeCss = `/*
170508
171195
  }
170509
171196
 
170510
171197
  /* Profile actions */
170511
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-profile__actions {
171198
+ :where(.theme-scope)
171199
+ :where([data-block="customer-portal"])
171200
+ .cp-profile__actions {
170512
171201
  display: flex;
170513
171202
  gap: 0.75rem;
170514
171203
  margin-top: 1rem;
@@ -170575,28 +171264,39 @@ const containerResponsiveThemeCss = `/*
170575
171264
  gap: 0.375rem;
170576
171265
  }
170577
171266
 
170578
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-guest-edit-row__index {
171267
+ :where(.theme-scope)
171268
+ :where([data-block="customer-portal"])
171269
+ .cp-guest-edit-row__index {
170579
171270
  font-size: 0.75rem;
170580
171271
  font-weight: 500;
170581
171272
  color: var(--cp-muted);
170582
171273
  }
170583
171274
 
170584
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-guest-edit-row__fields {
171275
+ :where(.theme-scope)
171276
+ :where([data-block="customer-portal"])
171277
+ .cp-guest-edit-row__fields {
170585
171278
  display: flex;
170586
171279
  gap: 0.5rem;
170587
171280
  }
170588
171281
 
170589
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-guest-edit-row__fields input {
171282
+ :where(.theme-scope)
171283
+ :where([data-block="customer-portal"])
171284
+ .cp-guest-edit-row__fields
171285
+ input {
170590
171286
  flex: 1;
170591
171287
  min-width: 0;
170592
171288
  }
170593
171289
 
170594
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-guest-edit-row__error {
171290
+ :where(.theme-scope)
171291
+ :where([data-block="customer-portal"])
171292
+ .cp-guest-edit-row__error {
170595
171293
  font-size: 0.75rem;
170596
171294
  color: var(--cp-error);
170597
171295
  }
170598
171296
 
170599
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-guests__actions {
171297
+ :where(.theme-scope)
171298
+ :where([data-block="customer-portal"])
171299
+ .cp-guests__actions {
170600
171300
  display: flex;
170601
171301
  gap: 0.5rem;
170602
171302
  margin-top: 0.5rem;
@@ -170610,13 +171310,18 @@ const containerResponsiveThemeCss = `/*
170610
171310
  }
170611
171311
 
170612
171312
  /* In the actions modal, tiles sit on a surface modal, so use "background" for contrast. */
170613
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-actions-list :where(.rb-tile) {
171313
+ :where(.theme-scope)
171314
+ :where([data-block="customer-portal"])
171315
+ .cp-actions-list
171316
+ :where(.rb-tile) {
170614
171317
  --rb-event-tile-bg: rgb(var(--tb-background));
170615
171318
  --rb-event-tile-border: rgb(var(--tb-border));
170616
171319
  }
170617
171320
 
170618
171321
  /* Actions trigger: icon-only kebab button */
170619
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-actions-trigger.btn-sm {
171322
+ :where(.theme-scope)
171323
+ :where([data-block="customer-portal"])
171324
+ .cp-actions-trigger.btn-sm {
170620
171325
  padding: 0.35rem !important;
170621
171326
  line-height: 1 !important;
170622
171327
  min-width: 32px;
@@ -170640,17 +171345,24 @@ const containerResponsiveThemeCss = `/*
170640
171345
  gap: 0.25rem;
170641
171346
  }
170642
171347
 
170643
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-action-tile__title {
171348
+ :where(.theme-scope)
171349
+ :where([data-block="customer-portal"])
171350
+ .cp-action-tile__title {
170644
171351
  font-weight: 600;
170645
171352
  font-size: 0.9375rem;
170646
171353
  }
170647
171354
 
170648
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-action-tile__desc {
171355
+ :where(.theme-scope)
171356
+ :where([data-block="customer-portal"])
171357
+ .cp-action-tile__desc {
170649
171358
  font-size: 0.875rem;
170650
171359
  line-height: 1.35;
170651
171360
  }
170652
171361
 
170653
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-action-tile--danger .cp-action-tile__title {
171362
+ :where(.theme-scope)
171363
+ :where([data-block="customer-portal"])
171364
+ .cp-action-tile--danger
171365
+ .cp-action-tile__title {
170654
171366
  color: rgb(var(--tb-destructive));
170655
171367
  }
170656
171368
 
@@ -170672,7 +171384,9 @@ const containerResponsiveThemeCss = `/*
170672
171384
  text-decoration: underline;
170673
171385
  }
170674
171386
 
170675
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-btn--link:hover {
171387
+ :where(.theme-scope)
171388
+ :where([data-block="customer-portal"])
171389
+ .cp-btn--link:hover {
170676
171390
  color: var(--cp-primary-hover);
170677
171391
  }
170678
171392
 
@@ -170711,34 +171425,48 @@ const containerResponsiveThemeCss = `/*
170711
171425
  margin-top: 0.75rem;
170712
171426
  }
170713
171427
 
170714
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-cancel-confirm__title {
171428
+ :where(.theme-scope)
171429
+ :where([data-block="customer-portal"])
171430
+ .cp-cancel-confirm__title {
170715
171431
  font-weight: 600;
170716
171432
  margin-bottom: 0.5rem;
170717
171433
  }
170718
171434
 
170719
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-cancel-confirm__message {
171435
+ :where(.theme-scope)
171436
+ :where([data-block="customer-portal"])
171437
+ .cp-cancel-confirm__message {
170720
171438
  margin-bottom: 0.25rem;
170721
171439
  }
170722
171440
 
170723
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-cancel-confirm__warning {
171441
+ :where(.theme-scope)
171442
+ :where([data-block="customer-portal"])
171443
+ .cp-cancel-confirm__warning {
170724
171444
  margin-bottom: 0.75rem;
170725
171445
  }
170726
171446
 
170727
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-cancel-confirm__actions {
171447
+ :where(.theme-scope)
171448
+ :where([data-block="customer-portal"])
171449
+ .cp-cancel-confirm__actions {
170728
171450
  display: flex;
170729
171451
  gap: 0.75rem;
170730
171452
  }
170731
171453
 
170732
171454
  /* Membership actions */
170733
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-membership-actions {
171455
+ :where(.theme-scope)
171456
+ :where([data-block="customer-portal"])
171457
+ .cp-membership-actions {
170734
171458
  margin-top: 0.75rem;
170735
171459
  }
170736
171460
 
170737
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-section-actions {
171461
+ :where(.theme-scope)
171462
+ :where([data-block="customer-portal"])
171463
+ .cp-section-actions {
170738
171464
  margin-top: 1rem;
170739
171465
  }
170740
171466
 
170741
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-card-subtitle--spaced {
171467
+ :where(.theme-scope)
171468
+ :where([data-block="customer-portal"])
171469
+ .cp-card-subtitle--spaced {
170742
171470
  margin-top: 0.75rem;
170743
171471
  }
170744
171472
 
@@ -170762,9 +171490,18 @@ const containerResponsiveThemeCss = `/*
170762
171490
  display: none;
170763
171491
  }
170764
171492
 
170765
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-table :is(th, td).cp-col-guests,
170766
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-table :is(th, td).cp-col-tickets,
170767
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-table :is(th, td).cp-col-actions {
171493
+ :where(.theme-scope)
171494
+ :where([data-block="customer-portal"])
171495
+ .cp-table
171496
+ :is(th, td).cp-col-guests,
171497
+ :where(.theme-scope)
171498
+ :where([data-block="customer-portal"])
171499
+ .cp-table
171500
+ :is(th, td).cp-col-tickets,
171501
+ :where(.theme-scope)
171502
+ :where([data-block="customer-portal"])
171503
+ .cp-table
171504
+ :is(th, td).cp-col-actions {
170768
171505
  display: none;
170769
171506
  }
170770
171507
 
@@ -170773,24 +171510,34 @@ const containerResponsiveThemeCss = `/*
170773
171510
  }
170774
171511
 
170775
171512
  /* Make the cell a positioning context for the kebab */
170776
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-row > td:first-child {
171513
+ :where(.theme-scope)
171514
+ :where([data-block="customer-portal"])
171515
+ .cp-row
171516
+ > td:first-child {
170777
171517
  position: relative;
170778
171518
  padding-right: 2.5rem;
170779
171519
  }
170780
171520
 
170781
171521
  /* Position kebab in top-right of the cell */
170782
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-row-mobile-actions {
171522
+ :where(.theme-scope)
171523
+ :where([data-block="customer-portal"])
171524
+ .cp-row-mobile-actions {
170783
171525
  position: absolute;
170784
171526
  top: 0.5rem;
170785
171527
  right: 0.5rem;
170786
171528
  margin-top: 0;
170787
171529
  }
170788
171530
 
170789
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-row-mobile-actions .btn-sm {
171531
+ :where(.theme-scope)
171532
+ :where([data-block="customer-portal"])
171533
+ .cp-row-mobile-actions
171534
+ .btn-sm {
170790
171535
  width: auto;
170791
171536
  }
170792
171537
 
170793
- :where(.theme-scope) :where([data-block="customer-portal"]) .cp-profile__edit {
171538
+ :where(.theme-scope)
171539
+ :where([data-block="customer-portal"])
171540
+ .cp-profile__edit {
170794
171541
  align-self: stretch;
170795
171542
  }
170796
171543
  }
@@ -171238,6 +171985,30 @@ const containerResponsiveThemeCss = `/*
171238
171985
  gap: 0.25rem;
171239
171986
  }
171240
171987
 
171988
+ /* Topic choices (visitor picks what to hear about). Same treatment as the
171989
+ form block's .fb-fieldset / .fb-inline, defined here because the newsletter
171990
+ form also renders inside contact-newsletter and the site footer. */
171991
+ :where(.theme-scope) .newsletter-topics {
171992
+ display: flex;
171993
+ flex-direction: column;
171994
+ gap: 0.5rem;
171995
+ border: none;
171996
+ padding: 0;
171997
+ margin: 0;
171998
+ min-width: 0;
171999
+ }
172000
+
172001
+ :where(.theme-scope) .newsletter-topics > legend {
172002
+ padding: 0;
172003
+ margin-bottom: 0.25rem;
172004
+ }
172005
+
172006
+ :where(.theme-scope) .newsletter-topic-choice {
172007
+ display: flex;
172008
+ align-items: center;
172009
+ gap: 0.5rem;
172010
+ }
172011
+
171241
172012
  /* Required indicator */
171242
172013
  :where(.theme-scope) .newsletter-required {
171243
172014
  color: rgb(var(--tb-primary));
@@ -171287,6 +172058,10 @@ const containerResponsiveThemeCss = `/*
171287
172058
  flex-shrink: 0;
171288
172059
  }
171289
172060
 
172061
+ :where(.theme-scope) .newsletter-inline .newsletter-topics {
172062
+ flex-basis: 100%;
172063
+ }
172064
+
171290
172065
  /* Mobile fallback for inline variant */
171291
172066
  @container rb-site (max-width: 640px) {
171292
172067
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
@@ -184879,13 +185654,16 @@ async function listFooterForms(apiClient, siteId) {
184879
185654
  }));
184880
185655
  }
184881
185656
  async function getFooterEngagementAuthoringOptions(apiClient, siteId) {
184882
- const [forms, mailingList] = await Promise.all([listFooterForms(apiClient, siteId), apiClient({
185657
+ const [forms, authoringContext] = await Promise.all([listFooterForms(apiClient, siteId), apiClient({
184883
185658
  endpoint: "getMailingListAuthoringContext",
184884
185659
  params: { siteId }
184885
185660
  }).then(unwrapResponse)]);
184886
185661
  return {
184887
185662
  forms,
184888
- mailingList
185663
+ mailingList: authoringContext.runtime,
185664
+ expectedMailingListProviderScope: authoringContext.expectedProviderScope,
185665
+ confirmation: authoringContext.confirmation,
185666
+ canManageMailingListSetup: authoringContext.canManageSetup
184889
185667
  };
184890
185668
  }
184891
185669
  async function updateFooterComposition(apiClient, siteId, body) {
@@ -185341,7 +186119,17 @@ const riverbankSiteConfigInputSchema = z.object({
185341
186119
  z.object({
185342
186120
  config: riverbankSiteConfigInputSchema,
185343
186121
  forceContentTypes: z.boolean().optional(),
185344
- mode: z.enum(["apply", "preview"]).optional()
186122
+ mode: z.enum(["apply", "preview"]).optional(),
186123
+ expectedMailingListProviderScope: z.unknown().transform((value, ctx) => {
186124
+ if (value === void 0) return void 0;
186125
+ const parsed = parseMailingListProviderScope(value);
186126
+ if (parsed.ok) return parsed.value;
186127
+ ctx.addIssue({
186128
+ code: "custom",
186129
+ message: "Invalid mailing-list provider scope"
186130
+ });
186131
+ return z.NEVER;
186132
+ }).optional()
185345
186133
  }).strict();
185346
186134
  //#endregion
185347
186135
  //#region ../api/src/sdkThemeWire.ts
@@ -186504,7 +187292,8 @@ async function mutateBlockContent(apiClient, args) {
186504
187292
  if ("data" in args) {
186505
187293
  const payload = {
186506
187294
  data: args.data,
186507
- optimistic_hash: args.optimisticHash ?? void 0
187295
+ optimistic_hash: args.optimisticHash ?? void 0,
187296
+ expected_mailing_list_provider_scope: args.expectedMailingListProviderScope
186508
187297
  };
186509
187298
  return unwrapResponse(await apiClient({
186510
187299
  endpoint: "saveBlockContent",
@@ -186516,7 +187305,8 @@ async function mutateBlockContent(apiClient, args) {
186516
187305
  const payload = {
186517
187306
  path: args.path,
186518
187307
  value: args.value,
186519
- optimistic_hash: args.optimisticHash ?? void 0
187308
+ optimistic_hash: args.optimisticHash ?? void 0,
187309
+ expected_mailing_list_provider_scope: args.expectedMailingListProviderScope
186520
187310
  };
186521
187311
  return unwrapResponse(await apiClient({
186522
187312
  endpoint: "saveBlockContent",
@@ -192124,6 +192914,9 @@ function planBlockSaveRevisionForOwner(owner, optimisticHash) {
192124
192914
  default: return assertNever$10(owner);
192125
192915
  }
192126
192916
  }
192917
+ function isPendingWithCurrent(snapshot) {
192918
+ return snapshot.syncState.kind === "pending" && Boolean(snapshot.current);
192919
+ }
192127
192920
  function optimisticHashForSave(revision) {
192128
192921
  switch (revision.kind) {
192129
192922
  case "bestEffort":
@@ -192138,9 +192931,8 @@ const MISSING_REQUIRED_BLOCK_REVISION = "This Page Content block is missing its
192138
192931
  *
192139
192932
  * Only enabled for pages (hasRoute=true), as content entries don't have blocks.
192140
192933
  */
192141
- function useBlockAutosave({ uiStore, apiClient, siteId, hasRoute, owner }) {
192934
+ function useBlockAutosave({ uiStore, apiClient, siteId, hasRoute, owner, getExpectedMailingListProviderScope }) {
192142
192935
  const ownerKind = owner.kind;
192143
- const isPendingWithCurrent = (snapshot) => snapshot.syncState.kind === "pending" && Boolean(snapshot.current);
192144
192936
  return useSimpleAutosave({
192145
192937
  saveFn: useCallback(async (pendingBlocks) => {
192146
192938
  const results = await Promise.allSettled(pendingBlocks.map(async ({ blockId, data, revision }) => {
@@ -192149,7 +192941,8 @@ function useBlockAutosave({ uiStore, apiClient, siteId, hasRoute, owner }) {
192149
192941
  siteId,
192150
192942
  blockId,
192151
192943
  data,
192152
- optimisticHash: optimisticHashForSave(revision) ?? void 0
192944
+ optimisticHash: optimisticHashForSave(revision) ?? void 0,
192945
+ expectedMailingListProviderScope: getExpectedMailingListProviderScope?.() ?? void 0
192153
192946
  });
192154
192947
  }));
192155
192948
  const drafts = uiStore.getState().drafts;
@@ -192171,6 +192964,7 @@ function useBlockAutosave({ uiStore, apiClient, siteId, hasRoute, owner }) {
192171
192964
  return results;
192172
192965
  }, [
192173
192966
  apiClient,
192967
+ getExpectedMailingListProviderScope,
192174
192968
  siteId,
192175
192969
  uiStore
192176
192970
  ]),
@@ -222980,314 +223774,34 @@ async function loadMailingListAuthoringContext(input) {
222980
223774
  }));
222981
223775
  }
222982
223776
  //#endregion
222983
- //#region ../editor-blocks/src/widgets/mailing-list-targeting/MailingListTargetingField.tsx
222984
- const inputClass$2 = "w-full rounded-md border bg-background px-3 py-2 text-sm";
222985
- function MailingListTargetingField({ path, backend, config }) {
222986
- const { value, error, setValue } = useField(path, backend);
222987
- const { siteId, load, manageHref } = useMailingListTargetingContext();
222988
- const [loadState, setLoadState] = useState({ tag: "loading" });
222989
- const [loadError, setLoadError] = useState(null);
222990
- const requestVersion = useRef(0);
222991
- const refresh = useCallback(async () => {
222992
- const version = requestVersion.current + 1;
222993
- requestVersion.current = version;
222994
- setLoadState({ tag: "loading" });
222995
- setLoadError(null);
222996
- try {
222997
- const context = await load(siteId);
222998
- if (requestVersion.current === version) setLoadState({
222999
- tag: "loaded",
223000
- context
223001
- });
223002
- } catch {
223003
- if (requestVersion.current === version) {
223004
- setLoadState({ tag: "failed" });
223005
- setLoadError("We could not load the mailing-list choices. Try again.");
223006
- }
223007
- }
223008
- }, [load, siteId]);
223009
- useEffect(() => {
223010
- refresh();
223011
- }, [refresh]);
223012
- const targeting = value ?? { mode: "default" };
223013
- return /* @__PURE__ */ jsx(FieldFrame, {
223014
- fieldPath: path,
223015
- label: config.label,
223016
- description: config.description,
223017
- error: error?.message ?? loadError,
223018
- children: () => loadState.tag === "loaded" && loadState.context.tag === "configured" && providerDefaultIsAvailable(loadState.context) ? /* @__PURE__ */ jsxs("div", {
223019
- className: "space-y-3",
223020
- children: [/* @__PURE__ */ jsx(MailingListTargetingControl, {
223021
- context: loadState.context,
223022
- targeting,
223023
- onChange: setValue,
223024
- name: path
223025
- }), /* @__PURE__ */ jsxs("div", {
223026
- className: "flex gap-3 text-xs font-medium",
223027
- children: [manageHref ? /* @__PURE__ */ jsx("a", {
223028
- className: "text-primary hover:underline",
223029
- href: manageHref,
223030
- children: "Manage mailing list"
223031
- }) : null, /* @__PURE__ */ jsx("button", {
223032
- type: "button",
223033
- className: "text-primary hover:underline",
223034
- onClick: () => void refresh(),
223035
- children: "Refresh choices"
223036
- })]
223037
- })]
223038
- }) : /* @__PURE__ */ jsxs("div", {
223039
- className: "rounded-md border border-dashed p-3 text-sm text-muted-foreground",
223040
- children: [loadState.tag === "loading" ? "Loading mailing-list setup…" : loadState.tag === "failed" ? "We could not load the mailing-list setup." : loadState.context.tag === "configured" ? "The provider connection or default choice needs attention. Check Mailing list setup, then refresh choices." : "Set up a mailing list before configuring this form.", loadState.tag === "failed" ? /* @__PURE__ */ jsx("button", {
223041
- type: "button",
223042
- className: "ml-2 underline",
223043
- onClick: () => void refresh(),
223044
- children: "Try again"
223045
- }) : null]
223046
- })
223047
- });
223048
- }
223049
- function MailingListTargetingControl({ context, targeting, onChange, name }) {
223050
- if (!hasConfigurableTargetingChoices(context)) return /* @__PURE__ */ jsxs("div", {
223051
- className: "space-y-1 text-sm",
223052
- children: [
223053
- /* @__PURE__ */ jsx("p", {
223054
- className: "font-medium",
223055
- children: "Subscribers join"
223056
- }),
223057
- /* @__PURE__ */ jsx("p", { children: defaultLabel(context).replace("Use the site default: ", "") }),
223058
- /* @__PURE__ */ jsx("p", {
223059
- className: "text-xs text-muted-foreground",
223060
- children: "This is the only available choice, so it is used automatically."
223061
- })
223062
- ]
223063
- });
223064
- return /* @__PURE__ */ jsxs("div", {
223065
- className: "space-y-3",
223066
- children: [
223067
- /* @__PURE__ */ jsx(Choice, {
223068
- checked: targeting.mode === "default",
223069
- label: defaultLabel(context),
223070
- name: `${name}-mode`,
223071
- onChange: () => onChange({ mode: "default" })
223072
- }),
223073
- /* @__PURE__ */ jsx(Choice, {
223074
- checked: targeting.mode === "custom",
223075
- label: "Choose what this form signs people up to",
223076
- name: `${name}-mode`,
223077
- onChange: () => onChange(createInitialCustom(context))
223078
- }),
223079
- targeting.mode === "custom" ? /* @__PURE__ */ jsx(CustomTargeting, {
223080
- context,
223081
- targeting,
223082
- setValue: onChange,
223083
- name
223084
- }) : null
223085
- ]
223086
- });
223087
- }
223088
- function hasConfigurableTargetingChoices(context) {
223777
+ //#region ../editor-blocks/src/widgets/mailing-list-targeting/targetingState.ts
223778
+ /** The name of the site's default topic/list/audience, or null when it no longer exists. */
223779
+ function defaultItemName(context) {
223089
223780
  const provider = context.provider;
223090
- switch (provider.provider) {
223091
- case "native": return provider.topics.length > 1;
223092
- case "brevo": return provider.lists.length > 1;
223093
- case "mailchimp": return provider.audiences.length > 1 || provider.audiences.some((audience) => audience.interests.length > 0);
223094
- }
223781
+ return (provider.provider === "native" ? provider.topics.find((value) => value.id === provider.defaultTopicId) : provider.provider === "brevo" ? provider.lists.find((value) => value.id === provider.defaultListId) : provider.audiences.find((value) => value.id === provider.defaultAudienceId))?.name ?? null;
223095
223782
  }
223096
- function CustomTargeting({ context, targeting, setValue, name }) {
223097
- if (context.provider.provider !== targeting.provider) return /* @__PURE__ */ jsxs("div", {
223098
- className: "space-y-2 text-sm text-destructive",
223099
- children: [/* @__PURE__ */ jsx("p", { children: "This choice belongs to a different provider." }), /* @__PURE__ */ jsx("button", {
223100
- type: "button",
223101
- className: "underline",
223102
- onClick: () => setValue({ mode: "default" }),
223103
- children: "Use the site default"
223104
- })]
223105
- });
223106
- const selectionMode = targeting.selection.tag;
223107
- const setSelectionMode = (mode) => setValue(changeSelectionMode(context, targeting, mode));
223108
- return /* @__PURE__ */ jsxs("div", {
223109
- className: "ml-6 space-y-3 border-l pl-4",
223110
- children: [
223111
- /* @__PURE__ */ jsx(Choice, {
223112
- checked: selectionMode === "fixed",
223113
- label: "Automatically add people to",
223114
- name: `${name}-selection-mode`,
223115
- onChange: () => setSelectionMode("fixed")
223116
- }),
223117
- /* @__PURE__ */ jsx(Choice, {
223118
- checked: selectionMode === "visitor_choice",
223119
- disabled: !canOfferVisitorChoice(context, targeting),
223120
- label: "Let people choose",
223121
- name: `${name}-selection-mode`,
223122
- onChange: () => setSelectionMode("visitor_choice")
223123
- }),
223124
- targeting.provider === "mailchimp" ? /* @__PURE__ */ jsx(MailchimpChoices, {
223125
- context,
223126
- targeting,
223127
- setValue,
223128
- name
223129
- }) : /* @__PURE__ */ jsx(ListChoices, {
223130
- context,
223131
- targeting,
223132
- setValue,
223133
- name
223134
- })
223135
- ]
223136
- });
223137
- }
223138
- function ListChoices({ context, targeting, setValue }) {
223139
- const options = context.provider.provider === "native" ? context.provider.topics : context.provider.provider === "brevo" ? context.provider.lists : [];
223140
- const selected = targeting.provider === "native" ? targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds : targeting.provider === "brevo" ? targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds : [];
223141
- if (options.length === 0) return /* @__PURE__ */ jsx("p", {
223142
- className: "text-sm text-muted-foreground",
223143
- children: "No choices are available yet. Add one in Mailing list, then refresh choices."
223144
- });
223145
- return /* @__PURE__ */ jsx(Checkboxes, {
223146
- options,
223147
- selected: selected.map(String),
223148
- requireOne: true,
223149
- onToggle: (id) => {
223150
- const ids = toggleRequired(selected.map(String), id);
223151
- if (targeting.provider === "native") {
223152
- const parsed = ids.flatMap((item) => {
223153
- const value = parseNewsletterListId(item);
223154
- return value ? [value] : [];
223155
- });
223156
- if (!parsed[0]) return;
223157
- setValue({
223158
- mode: "custom",
223159
- provider: "native",
223160
- selection: targeting.selection.tag === "fixed" ? {
223161
- tag: "fixed",
223162
- topicIds: [parsed[0], ...parsed.slice(1)]
223163
- } : {
223164
- tag: "visitor_choice",
223165
- offeredTopicIds: [parsed[0], ...parsed.slice(1)]
223166
- }
223167
- });
223168
- } else if (targeting.provider === "brevo") {
223169
- const parsed = ids.flatMap((item) => {
223170
- const value = parseBrevoListId(Number(item));
223171
- return value ? [value] : [];
223172
- });
223173
- if (!parsed[0]) return;
223174
- setValue({
223175
- mode: "custom",
223176
- provider: "brevo",
223177
- selection: targeting.selection.tag === "fixed" ? {
223178
- tag: "fixed",
223179
- listIds: [parsed[0], ...parsed.slice(1)]
223180
- } : {
223181
- tag: "visitor_choice",
223182
- offeredListIds: [parsed[0], ...parsed.slice(1)]
223183
- }
223184
- });
223185
- }
223186
- }
223187
- });
223188
- }
223189
- function MailchimpChoices({ context, targeting, setValue }) {
223190
- if (context.provider.provider !== "mailchimp" || targeting.provider !== "mailchimp") return null;
223191
- const audience = context.provider.audiences.find((item) => item.id === targeting.audienceId);
223192
- const selected = targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
223193
- return /* @__PURE__ */ jsxs("div", {
223194
- className: "space-y-3",
223195
- children: [
223196
- /* @__PURE__ */ jsx("select", {
223197
- className: inputClass$2,
223198
- "aria-label": "Mailchimp audience",
223199
- value: targeting.audienceId,
223200
- onChange: (event) => {
223201
- const audienceId = parseMailchimpAudienceId(event.target.value);
223202
- if (!audienceId) return;
223203
- setValue({
223204
- mode: "custom",
223205
- provider: "mailchimp",
223206
- audienceId,
223207
- selection: {
223208
- tag: "fixed",
223209
- interestIds: []
223210
- }
223211
- });
223212
- },
223213
- children: context.provider.audiences.map((item) => /* @__PURE__ */ jsx("option", {
223214
- value: item.id,
223215
- children: item.name
223216
- }, item.id))
223217
- }),
223218
- /* @__PURE__ */ jsx(Checkboxes, {
223219
- options: audience?.interests ?? [],
223220
- selected: selected.map(String),
223221
- requireOne: targeting.selection.tag === "visitor_choice",
223222
- onToggle: (id) => {
223223
- const parsed = toggle(selected.map(String), id).flatMap((item) => {
223224
- const value = parseMailchimpInterestId(item);
223225
- return value ? [value] : [];
223226
- });
223227
- setValue({
223228
- ...targeting,
223229
- selection: targeting.selection.tag === "fixed" ? {
223230
- tag: "fixed",
223231
- interestIds: parsed
223232
- } : parsed[0] ? {
223233
- tag: "visitor_choice",
223234
- offeredInterestIds: [parsed[0], ...parsed.slice(1)]
223235
- } : targeting.selection
223236
- });
223237
- }
223238
- }),
223239
- targeting.selection.tag === "fixed" && selected.length === 0 ? /* @__PURE__ */ jsx("p", {
223240
- className: "text-xs text-muted-foreground",
223241
- children: "No interests selected: people join the audience without an interest."
223242
- }) : null
223243
- ]
223244
- });
223245
- }
223246
- function Checkboxes({ options, selected, onToggle, requireOne = false }) {
223247
- return /* @__PURE__ */ jsxs("div", {
223248
- className: "space-y-2",
223249
- children: [options.map((item) => {
223250
- const id = String(item.id);
223251
- return /* @__PURE__ */ jsxs("label", {
223252
- className: "flex items-center gap-2 text-sm",
223253
- children: [/* @__PURE__ */ jsx("input", {
223254
- type: "checkbox",
223255
- checked: selected.includes(id),
223256
- disabled: requireOne && selected.length === 1 && selected.includes(id),
223257
- onChange: () => onToggle(id)
223258
- }), item.name]
223259
- }, id);
223260
- }), requireOne && selected.length === 1 ? /* @__PURE__ */ jsx("p", {
223261
- className: "text-xs text-muted-foreground",
223262
- children: "At least one choice is required."
223263
- }) : null]
223264
- });
223265
- }
223266
- function Choice({ checked, disabled, label, name, onChange }) {
223267
- return /* @__PURE__ */ jsxs("label", {
223268
- className: "flex items-center gap-2 text-sm",
223269
- children: [/* @__PURE__ */ jsx("input", {
223270
- type: "radio",
223271
- checked,
223272
- disabled,
223273
- name,
223274
- onChange
223275
- }), label]
223276
- });
223783
+ /** What the editor is choosing between, in the provider's own words. */
223784
+ function choiceNoun(context, form = "plural") {
223785
+ switch (context.provider.provider) {
223786
+ case "native": return form === "plural" ? "topics" : "topic";
223787
+ case "brevo": return form === "plural" ? "lists" : "list";
223788
+ case "mailchimp": return form === "plural" ? "interests" : "audience";
223789
+ }
223277
223790
  }
223278
- function defaultLabel(context) {
223279
- const provider = context.provider;
223280
- const item = provider.provider === "native" ? provider.topics.find((value) => value.id === provider.defaultTopicId) : provider.provider === "brevo" ? provider.lists.find((value) => value.id === provider.defaultListId) : provider.audiences.find((value) => value.id === provider.defaultAudienceId);
223281
- return `Use the site default${item ? `: ${item.name}` : ""}`;
223791
+ function providerDisplayName(context) {
223792
+ return mailingListProviderName(context.provider.provider);
223282
223793
  }
223283
223794
  function providerDefaultIsAvailable(context) {
223284
223795
  const provider = context.provider;
223285
223796
  switch (provider.provider) {
223286
- case "native": return provider.topics.some((item) => item.id === provider.defaultTopicId);
223287
- case "brevo": return provider.lists.some((item) => item.id === provider.defaultListId);
223288
- case "mailchimp": return provider.audiences.some((item) => item.id === provider.defaultAudienceId);
223797
+ case "native": return includesId(provider.topics, provider.defaultTopicId);
223798
+ case "brevo": return includesId(provider.lists, provider.defaultListId);
223799
+ case "mailchimp": return includesId(provider.audiences, provider.defaultAudienceId);
223289
223800
  }
223290
223801
  }
223802
+ function includesId(items, id) {
223803
+ return items.some((item) => item.id === id);
223804
+ }
223291
223805
  function createInitialCustom(context) {
223292
223806
  switch (context.provider.provider) {
223293
223807
  case "native": return {
@@ -223374,6 +223888,425 @@ function toggleRequired(values, id) {
223374
223888
  return next.length > 0 ? next : [...values];
223375
223889
  }
223376
223890
  //#endregion
223891
+ //#region ../editor-blocks/src/widgets/mailing-list-targeting/MailingListGlobalSettingsSummary.tsx
223892
+ /**
223893
+ * The read-only "what every signup on this site shares" card. Editors only
223894
+ * choose targeting; fields, confirmation and copy are set once by an
223895
+ * administrator, so this tells them what they are working inside of.
223896
+ */
223897
+ function MailingListGlobalSettingsSummary({ context }) {
223898
+ if (context.runtime.tag !== "configured") return null;
223899
+ const confirmation = context.confirmation?.tag === "on" ? "Email confirmation is on" : "Email confirmation is off";
223900
+ return /* @__PURE__ */ jsxs("div", {
223901
+ className: "rounded-md bg-muted/50 p-3 text-xs text-muted-foreground",
223902
+ children: [/* @__PURE__ */ jsxs("p", {
223903
+ className: "font-medium text-foreground",
223904
+ children: [providerDisplayName(context.runtime), /* @__PURE__ */ jsx("span", {
223905
+ className: "ml-2 font-normal text-muted-foreground",
223906
+ children: "Shared form settings"
223907
+ })]
223908
+ }), /* @__PURE__ */ jsxs("p", { children: [
223909
+ "Shared across your site: ",
223910
+ globalFieldsSummary(context.runtime.form),
223911
+ ". ",
223912
+ confirmation,
223913
+ "."
223914
+ ] })]
223915
+ });
223916
+ }
223917
+ /** Mid-sentence phrasing, so it reads on from "Shared across your site:". */
223918
+ function globalFieldsSummary(form) {
223919
+ const phone = form.includeOptionalPhone ? ", plus optional phone" : "";
223920
+ switch (form.fields.preset) {
223921
+ case "emailOnly": return `email only${phone}`;
223922
+ case "emailName": return `email and ${form.fields.name} name${phone}`;
223923
+ case "emailFirstLast": return `email, ${form.fields.firstName} first name and ${form.fields.lastName} last name${phone}`;
223924
+ case "emailNameCompany": return `email, ${form.fields.name} name and ${form.fields.company} company${phone}`;
223925
+ }
223926
+ }
223927
+ //#endregion
223928
+ //#region ../editor-blocks/src/widgets/mailing-list-targeting/MailingListTargetingField.tsx
223929
+ const inputClass$2 = "w-full rounded-md border bg-background px-3 py-2 text-sm";
223930
+ function MailingListTargetingField({ path, backend, config }) {
223931
+ const { value, error, setValue } = useField(path, backend);
223932
+ const { siteId, load, manageHref } = useMailingListTargetingContext();
223933
+ const [loadState, setLoadState] = useState({ tag: "loading" });
223934
+ const [loadError, setLoadError] = useState(null);
223935
+ const requestVersion = useRef(0);
223936
+ const refresh = useCallback(async () => {
223937
+ const version = requestVersion.current + 1;
223938
+ requestVersion.current = version;
223939
+ setLoadState({ tag: "loading" });
223940
+ setLoadError(null);
223941
+ try {
223942
+ const context = await load(siteId);
223943
+ if (requestVersion.current === version) setLoadState({
223944
+ tag: "loaded",
223945
+ context
223946
+ });
223947
+ } catch {
223948
+ if (requestVersion.current === version) {
223949
+ setLoadState({ tag: "failed" });
223950
+ setLoadError("We could not load the mailing-list choices. Try again.");
223951
+ }
223952
+ }
223953
+ }, [load, siteId]);
223954
+ useEffect(() => {
223955
+ refresh();
223956
+ }, [refresh]);
223957
+ const targeting = value ?? { mode: "default" };
223958
+ return /* @__PURE__ */ jsx(FieldFrame, {
223959
+ fieldPath: path,
223960
+ label: config.label,
223961
+ description: config.description,
223962
+ error: error?.message ?? loadError,
223963
+ children: () => loadState.tag === "loaded" && loadState.context.runtime.tag === "configured" && providerDefaultIsAvailable(loadState.context.runtime) ? /* @__PURE__ */ jsxs("div", {
223964
+ className: "space-y-3",
223965
+ children: [
223966
+ /* @__PURE__ */ jsx(MailingListGlobalSettingsSummary, { context: loadState.context }),
223967
+ /* @__PURE__ */ jsx(MailingListTargetingControl, {
223968
+ context: loadState.context.runtime,
223969
+ targeting,
223970
+ onChange: setValue,
223971
+ name: path
223972
+ }),
223973
+ /* @__PURE__ */ jsxs("div", {
223974
+ className: "flex gap-3 text-xs font-medium",
223975
+ children: [manageHref && loadState.context.canManageSetup ? /* @__PURE__ */ jsx("a", {
223976
+ className: "text-primary hover:underline",
223977
+ href: manageHref,
223978
+ children: "Manage mailing list"
223979
+ }) : /* @__PURE__ */ jsx("span", {
223980
+ className: "text-muted-foreground",
223981
+ children: "Ask an administrator to change these settings."
223982
+ }), /* @__PURE__ */ jsx("button", {
223983
+ type: "button",
223984
+ className: "text-primary hover:underline",
223985
+ onClick: () => void refresh(),
223986
+ children: "Refresh choices"
223987
+ })]
223988
+ })
223989
+ ]
223990
+ }) : /* @__PURE__ */ jsxs("div", {
223991
+ className: "rounded-md border border-dashed p-3 text-sm text-muted-foreground",
223992
+ children: [loadState.tag === "loading" ? "Loading mailing-list setup…" : loadState.tag === "failed" ? "We could not load the mailing-list setup." : loadState.context.runtime.tag === "configured" ? "The provider connection or default choice needs attention. Check Mailing list setup, then refresh choices." : "Set up a mailing list before configuring this form.", loadState.tag === "failed" ? /* @__PURE__ */ jsx("button", {
223993
+ type: "button",
223994
+ className: "ml-2 underline",
223995
+ onClick: () => void refresh(),
223996
+ children: "Try again"
223997
+ }) : null]
223998
+ })
223999
+ });
224000
+ }
224001
+ function MailingListTargetingControl({ context, targeting, onChange, name }) {
224002
+ if (!hasConfigurableTargetingChoices(context)) return /* @__PURE__ */ jsxs("div", {
224003
+ className: "space-y-1 text-sm",
224004
+ children: [
224005
+ /* @__PURE__ */ jsx("p", {
224006
+ className: "font-medium",
224007
+ children: "Subscribers join"
224008
+ }),
224009
+ /* @__PURE__ */ jsx("p", { children: defaultItemName(context) ?? "the site default" }),
224010
+ /* @__PURE__ */ jsxs("p", {
224011
+ className: "text-xs text-muted-foreground",
224012
+ children: [
224013
+ "This is your only ",
224014
+ choiceNoun(context, "singular"),
224015
+ ", so it is used automatically."
224016
+ ]
224017
+ })
224018
+ ]
224019
+ });
224020
+ return /* @__PURE__ */ jsxs("div", {
224021
+ className: "space-y-3",
224022
+ children: [
224023
+ /* @__PURE__ */ jsxs("fieldset", {
224024
+ className: "space-y-2",
224025
+ children: [
224026
+ /* @__PURE__ */ jsx("legend", {
224027
+ className: legendClass,
224028
+ children: "Signup destination"
224029
+ }),
224030
+ /* @__PURE__ */ jsx(Choice, {
224031
+ checked: targeting.mode === "default",
224032
+ label: "Use site default",
224033
+ description: defaultItemName(context) ?? void 0,
224034
+ name: `${name}-mode`,
224035
+ onChange: () => onChange({ mode: "default" })
224036
+ }),
224037
+ /* @__PURE__ */ jsx(Choice, {
224038
+ checked: targeting.mode === "custom",
224039
+ label: "Customise this signup",
224040
+ name: `${name}-mode`,
224041
+ onChange: () => onChange(createInitialCustom(context))
224042
+ })
224043
+ ]
224044
+ }),
224045
+ targeting.mode === "custom" ? /* @__PURE__ */ jsx(CustomTargeting, {
224046
+ context,
224047
+ targeting,
224048
+ setValue: onChange,
224049
+ name
224050
+ }) : null,
224051
+ /* @__PURE__ */ jsx("p", {
224052
+ className: "text-xs text-muted-foreground",
224053
+ children: "Subscription choices should match what your form promises."
224054
+ })
224055
+ ]
224056
+ });
224057
+ }
224058
+ const legendClass = "mb-1 text-sm font-medium";
224059
+ function hasConfigurableTargetingChoices(context) {
224060
+ const provider = context.provider;
224061
+ switch (provider.provider) {
224062
+ case "native": return provider.topics.length > 1;
224063
+ case "brevo": return provider.lists.length > 1;
224064
+ case "mailchimp": return provider.audiences.length > 1 || provider.audiences.some((audience) => audience.interests.length > 0);
224065
+ }
224066
+ }
224067
+ function CustomTargeting({ context, targeting, setValue, name }) {
224068
+ if (context.provider.provider !== targeting.provider) return /* @__PURE__ */ jsxs("div", {
224069
+ className: "space-y-2 rounded-md border border-dashed p-3 text-sm text-muted-foreground",
224070
+ children: [/* @__PURE__ */ jsx("p", { children: "The mailing-list setup changed, so this form's previous choices no longer apply. Reset it to the site default to keep editing." }), /* @__PURE__ */ jsx("button", {
224071
+ type: "button",
224072
+ className: "font-medium text-primary underline",
224073
+ onClick: () => setValue({ mode: "default" }),
224074
+ children: "Use the site default"
224075
+ })]
224076
+ });
224077
+ const selectionMode = targeting.selection.tag;
224078
+ const setSelectionMode = (mode) => setValue(changeSelectionMode(context, targeting, mode));
224079
+ const noun = choiceNoun(context);
224080
+ const visitorChoiceAllowed = canOfferVisitorChoice(context, targeting);
224081
+ return /* @__PURE__ */ jsxs("div", {
224082
+ className: "ml-6 space-y-3 border-l pl-4",
224083
+ children: [
224084
+ targeting.provider === "mailchimp" ? /* @__PURE__ */ jsx(MailchimpAudience, {
224085
+ context,
224086
+ targeting,
224087
+ setValue,
224088
+ name
224089
+ }) : null,
224090
+ /* @__PURE__ */ jsxs("fieldset", {
224091
+ className: "space-y-2",
224092
+ children: [
224093
+ /* @__PURE__ */ jsxs("legend", {
224094
+ className: legendClass,
224095
+ children: [
224096
+ "How are ",
224097
+ noun,
224098
+ " chosen?"
224099
+ ]
224100
+ }),
224101
+ /* @__PURE__ */ jsx(Choice, {
224102
+ checked: selectionMode === "fixed",
224103
+ label: "I choose",
224104
+ description: `Automatically subscribe to selected ${noun}.`,
224105
+ name: `${name}-selection-mode`,
224106
+ onChange: () => setSelectionMode("fixed")
224107
+ }),
224108
+ /* @__PURE__ */ jsx(Choice, {
224109
+ checked: selectionMode === "visitor_choice",
224110
+ disabled: !visitorChoiceAllowed,
224111
+ label: "Let visitors choose",
224112
+ description: visitorChoiceAllowed ? "Show these options on the form." : "Pick an audience with interests to offer visitors a choice.",
224113
+ name: `${name}-selection-mode`,
224114
+ onChange: () => setSelectionMode("visitor_choice")
224115
+ })
224116
+ ]
224117
+ }),
224118
+ targeting.provider === "mailchimp" ? /* @__PURE__ */ jsx(MailchimpChoices, {
224119
+ context,
224120
+ targeting,
224121
+ setValue,
224122
+ name
224123
+ }) : /* @__PURE__ */ jsx(ListChoices, {
224124
+ context,
224125
+ targeting,
224126
+ setValue,
224127
+ name
224128
+ }),
224129
+ /* @__PURE__ */ jsx("p", {
224130
+ className: "text-xs text-muted-foreground",
224131
+ children: selectionMode === "visitor_choice" ? "Offer at least one choice; visitors choose what they want to hear about." : targeting.provider === "mailchimp" ? "People join the audience, with any interests you tick applied to them." : `Visitors join the selected ${noun} without a picker.`
224132
+ })
224133
+ ]
224134
+ });
224135
+ }
224136
+ function ListChoices({ context, targeting, setValue }) {
224137
+ const options = context.provider.provider === "native" ? context.provider.topics : context.provider.provider === "brevo" ? context.provider.lists : [];
224138
+ const selected = targeting.provider === "native" ? targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds : targeting.provider === "brevo" ? targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds : [];
224139
+ if (options.length === 0) return /* @__PURE__ */ jsx("p", {
224140
+ className: "text-sm text-muted-foreground",
224141
+ children: "No choices are available yet. Add one in Mailing list, then refresh choices."
224142
+ });
224143
+ return /* @__PURE__ */ jsx(Checkboxes, {
224144
+ options,
224145
+ selected: selected.map(String),
224146
+ requireOne: true,
224147
+ onToggle: (id) => {
224148
+ const ids = toggleRequired(selected.map(String), id);
224149
+ if (targeting.provider === "native") {
224150
+ const parsed = ids.flatMap((item) => {
224151
+ const value = parseNewsletterListId(item);
224152
+ return value ? [value] : [];
224153
+ });
224154
+ if (!parsed[0]) return;
224155
+ setValue({
224156
+ mode: "custom",
224157
+ provider: "native",
224158
+ selection: targeting.selection.tag === "fixed" ? {
224159
+ tag: "fixed",
224160
+ topicIds: [parsed[0], ...parsed.slice(1)]
224161
+ } : {
224162
+ tag: "visitor_choice",
224163
+ offeredTopicIds: [parsed[0], ...parsed.slice(1)]
224164
+ }
224165
+ });
224166
+ } else if (targeting.provider === "brevo") {
224167
+ const parsed = ids.flatMap((item) => {
224168
+ const value = parseBrevoListId(Number(item));
224169
+ return value ? [value] : [];
224170
+ });
224171
+ if (!parsed[0]) return;
224172
+ setValue({
224173
+ mode: "custom",
224174
+ provider: "brevo",
224175
+ selection: targeting.selection.tag === "fixed" ? {
224176
+ tag: "fixed",
224177
+ listIds: [parsed[0], ...parsed.slice(1)]
224178
+ } : {
224179
+ tag: "visitor_choice",
224180
+ offeredListIds: [parsed[0], ...parsed.slice(1)]
224181
+ }
224182
+ });
224183
+ }
224184
+ }
224185
+ });
224186
+ }
224187
+ function MailchimpAudience({ context, targeting, setValue, name }) {
224188
+ if (context.provider.provider !== "mailchimp" || targeting.provider !== "mailchimp") return null;
224189
+ const selectId = `${name}-audience`;
224190
+ return /* @__PURE__ */ jsxs("div", {
224191
+ className: "space-y-1",
224192
+ children: [/* @__PURE__ */ jsx("label", {
224193
+ htmlFor: selectId,
224194
+ className: "text-sm font-medium",
224195
+ children: "Audience"
224196
+ }), /* @__PURE__ */ jsx("select", {
224197
+ id: selectId,
224198
+ className: inputClass$2,
224199
+ value: targeting.audienceId,
224200
+ onChange: (event) => {
224201
+ const audienceId = parseMailchimpAudienceId(event.target.value);
224202
+ if (!audienceId) return;
224203
+ setValue({
224204
+ mode: "custom",
224205
+ provider: "mailchimp",
224206
+ audienceId,
224207
+ selection: {
224208
+ tag: "fixed",
224209
+ interestIds: []
224210
+ }
224211
+ });
224212
+ },
224213
+ children: context.provider.audiences.map((item) => /* @__PURE__ */ jsx("option", {
224214
+ value: item.id,
224215
+ children: item.name
224216
+ }, item.id))
224217
+ })]
224218
+ });
224219
+ }
224220
+ function MailchimpChoices({ context, targeting, setValue }) {
224221
+ if (context.provider.provider !== "mailchimp" || targeting.provider !== "mailchimp") return null;
224222
+ const audience = context.provider.audiences.find((item) => item.id === targeting.audienceId);
224223
+ const selected = targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
224224
+ return /* @__PURE__ */ jsxs("div", {
224225
+ className: "space-y-3",
224226
+ children: [/* @__PURE__ */ jsx(Checkboxes, {
224227
+ options: audience?.interests ?? [],
224228
+ selected: selected.map(String),
224229
+ requireOne: targeting.selection.tag === "visitor_choice",
224230
+ onToggle: (id) => {
224231
+ const parsed = toggle(selected.map(String), id).flatMap((item) => {
224232
+ const value = parseMailchimpInterestId(item);
224233
+ return value ? [value] : [];
224234
+ });
224235
+ setValue({
224236
+ ...targeting,
224237
+ selection: targeting.selection.tag === "fixed" ? {
224238
+ tag: "fixed",
224239
+ interestIds: parsed
224240
+ } : parsed[0] ? {
224241
+ tag: "visitor_choice",
224242
+ offeredInterestIds: [parsed[0], ...parsed.slice(1)]
224243
+ } : targeting.selection
224244
+ });
224245
+ }
224246
+ }), targeting.selection.tag === "fixed" && selected.length === 0 ? /* @__PURE__ */ jsx("p", {
224247
+ className: "text-xs text-muted-foreground",
224248
+ children: "No interests selected: people join the audience without an interest."
224249
+ }) : null]
224250
+ });
224251
+ }
224252
+ function Checkboxes({ options, selected, onToggle, requireOne = false }) {
224253
+ const [refusedLast, setRefusedLast] = useState(false);
224254
+ const refusedId = useId();
224255
+ useEffect(() => {
224256
+ setRefusedLast(false);
224257
+ }, [selected.join("\0")]);
224258
+ return /* @__PURE__ */ jsxs("fieldset", {
224259
+ className: "space-y-2",
224260
+ children: [
224261
+ /* @__PURE__ */ jsx("legend", {
224262
+ className: "sr-only",
224263
+ children: "Available mailing-list choices"
224264
+ }),
224265
+ options.map((item) => {
224266
+ const id = String(item.id);
224267
+ const isLastSelected = requireOne && selected.length === 1 && selected.includes(id);
224268
+ return /* @__PURE__ */ jsxs("label", {
224269
+ className: "flex items-center gap-2 text-sm",
224270
+ children: [/* @__PURE__ */ jsx("input", {
224271
+ type: "checkbox",
224272
+ checked: selected.includes(id),
224273
+ "aria-describedby": isLastSelected && refusedLast ? refusedId : void 0,
224274
+ onChange: () => {
224275
+ if (isLastSelected) {
224276
+ setRefusedLast(true);
224277
+ return;
224278
+ }
224279
+ onToggle(id);
224280
+ }
224281
+ }), item.name]
224282
+ }, id);
224283
+ }),
224284
+ refusedLast && requireOne && selected.length === 1 ? /* @__PURE__ */ jsx("p", {
224285
+ id: refusedId,
224286
+ role: "status",
224287
+ className: "text-xs text-muted-foreground",
224288
+ children: "Keep at least one selected."
224289
+ }) : null
224290
+ ]
224291
+ });
224292
+ }
224293
+ function Choice({ checked, disabled, label, description, name, onChange }) {
224294
+ return /* @__PURE__ */ jsxs("label", {
224295
+ className: "flex items-start gap-2 text-sm",
224296
+ children: [/* @__PURE__ */ jsx("input", {
224297
+ type: "radio",
224298
+ className: "mt-1",
224299
+ checked,
224300
+ disabled,
224301
+ name,
224302
+ onChange
224303
+ }), /* @__PURE__ */ jsxs("span", { children: [label, description ? /* @__PURE__ */ jsx("span", {
224304
+ className: "block text-xs text-muted-foreground",
224305
+ children: description
224306
+ }) : null] })]
224307
+ });
224308
+ }
224309
+ //#endregion
223377
224310
  //#region ../editor-blocks/src/widgets/offerings-preview/OfferingsPreviewWidgetProvider.tsx
223378
224311
  const OfferingsPreviewWidgetContext = createContext(null);
223379
224312
  function OfferingsPreviewWidgetProvider({ bookingOfferingPresets, children }) {
@@ -224497,9 +225430,7 @@ function useInternalValue(props) {
224497
225430
  };
224498
225431
  }, [uiStore]);
224499
225432
  const setEntry = (next) => {
224500
- setEntryRaw((prev) => {
224501
- return typeof next === "function" ? next(prev) : next;
224502
- });
225433
+ setEntryRaw((prev) => typeof next === "function" ? next(prev) : next);
224503
225434
  };
224504
225435
  useEffect(() => {
224505
225436
  if (initialEntry) setEntry((prev) => {
@@ -224707,7 +225638,8 @@ function useInternalValue(props) {
224707
225638
  apiClient,
224708
225639
  siteId,
224709
225640
  hasRoute,
224710
- owner
225641
+ owner,
225642
+ getExpectedMailingListProviderScope: props.getExpectedMailingListProviderScope
224711
225643
  });
224712
225644
  const linkedEntryAutosave = useLinkedEntryAutosave({
224713
225645
  uiStore,
@@ -225687,7 +226619,12 @@ function MailingListChoice({ dispatch, draft, manageNewsletterHref, options }) {
225687
226619
  return /* @__PURE__ */ jsxs("div", {
225688
226620
  className: "space-y-3",
225689
226621
  children: [
225690
- options.mailingList.tag === "configured" ? /* @__PURE__ */ jsx(MailingListTargetingControl, {
226622
+ options.mailingList.tag === "configured" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(MailingListGlobalSettingsSummary, { context: {
226623
+ runtime: options.mailingList,
226624
+ expectedProviderScope: options.expectedMailingListProviderScope,
226625
+ confirmation: options.confirmation,
226626
+ canManageSetup: options.canManageMailingListSetup
226627
+ } }), /* @__PURE__ */ jsx(MailingListTargetingControl, {
225691
226628
  context: options.mailingList,
225692
226629
  targeting: draft.targeting,
225693
226630
  name: "footer-mailing-list-targeting",
@@ -225695,14 +226632,17 @@ function MailingListChoice({ dispatch, draft, manageNewsletterHref, options }) {
225695
226632
  type: "set-mailing-targeting",
225696
226633
  targeting
225697
226634
  })
225698
- }) : /* @__PURE__ */ jsx("p", {
226635
+ })] }) : /* @__PURE__ */ jsx("p", {
225699
226636
  className: "rounded-md border border-dashed p-3 text-sm text-muted-foreground",
225700
226637
  children: "Set up a mailing list before adding this footer form."
225701
226638
  }),
225702
- /* @__PURE__ */ jsx("a", {
226639
+ options.canManageMailingListSetup ? /* @__PURE__ */ jsx("a", {
225703
226640
  className: buttonClass$3,
225704
226641
  href: manageNewsletterHref,
225705
226642
  children: "Manage mailing list"
226643
+ }) : /* @__PURE__ */ jsx("p", {
226644
+ className: "text-xs text-muted-foreground",
226645
+ children: "Ask an administrator to change shared form settings."
225706
226646
  }),
225707
226647
  /* @__PURE__ */ jsx("div", {
225708
226648
  className: "text-xs text-muted-foreground",
@@ -226568,6 +227508,7 @@ function useFooterComposerState({ apiClient, canEdit, initialTarget, initialTarg
226568
227508
  apiClient,
226569
227509
  canEdit,
226570
227510
  compositionSnapshot: loaded.compositionSnapshot,
227511
+ engagementOptions: loaded.engagementOptions,
226571
227512
  onSaved,
226572
227513
  setCompositionSnapshot: loaded.setCompositionSnapshot,
226573
227514
  setMessage,
@@ -226637,7 +227578,7 @@ function useFooterLoad(apiClient, rawSiteId, siteId) {
226637
227578
  setProfileSnapshot
226638
227579
  };
226639
227580
  }
226640
- function useCompositionSaver({ apiClient, canEdit, compositionSnapshot, onSaved, setCompositionSnapshot, setMessage, setSaveState, siteId }) {
227581
+ function useCompositionSaver({ apiClient, canEdit, compositionSnapshot, engagementOptions, onSaved, setCompositionSnapshot, setMessage, setSaveState, siteId }) {
226641
227582
  return useCallback(async (next, optimistic = true) => {
226642
227583
  if (!canEdit || compositionSnapshot?.kind !== "ready") return "failed";
226643
227584
  if (optimistic) setCompositionSnapshot({
@@ -226650,7 +227591,8 @@ function useCompositionSaver({ apiClient, canEdit, compositionSnapshot, onSaved,
226650
227591
  try {
226651
227592
  const result = await updateFooterComposition(apiClient, siteId, {
226652
227593
  composition: next,
226653
- baseRevision: compositionSnapshot.revision
227594
+ baseRevision: compositionSnapshot.revision,
227595
+ expectedMailingListProviderScope: engagementOptions?.expectedMailingListProviderScope ?? void 0
226654
227596
  });
226655
227597
  if (result.kind === "conflict") {
226656
227598
  setCompositionSnapshot(result.authoritative);
@@ -226671,6 +227613,7 @@ function useCompositionSaver({ apiClient, canEdit, compositionSnapshot, onSaved,
226671
227613
  apiClient,
226672
227614
  canEdit,
226673
227615
  compositionSnapshot,
227616
+ engagementOptions,
226674
227617
  onSaved,
226675
227618
  setCompositionSnapshot,
226676
227619
  setMessage,
@@ -230364,4 +231307,4 @@ function useSiteChromeCustomizeOptional() {
230364
231307
  return useContext(SiteChromeCustomizeContext);
230365
231308
  }
230366
231309
  //#endregion
230367
- export { FooterComposerPanel as $, Monitor as $a, FieldFrame as $i, coerceLinkedContentEntriesPayload as $n, parseBackgroundContent as $r, useMultiAutosave as $t, markApplySaving as A, DropdownMenu as Aa, buttonStyleHoverPreferenceValue as Ai, containerResponsiveThemeCss as An, compileTransitionRuntime as Ar, RichTextField as At, setPaletteColors as B, X as Ba, ORDINARY_TEXT_MIN_CONTRAST_RATIO as Bi, compileManifestContentContract as Bn, curatedPalettes as Br, getBlockManifestFromSdkConfig as Bt, cancelPreview as C, DialogFooter as Ca, isObjectRecord$1 as Ci, deriveLaunchReadiness as Cn, retargetCompiledRuntimeFieldsForContentAdjacency as Cr, ButtonVariantField as Ct, isPreviewingChange as D, CollapsibleContent as Da, asDesignBlockId as Di, useApiMediaUploader as Dn, curatedSiteStyles as Dr, ReferenceField as Dt, dismissWarnings as E, Collapsible as Ea, pageDesignEditorBackgroundOverrideOwner as Ei, useMediaEditorOptional as En, componentRuntimeRulesForTheme as Er, MediaField as Et, previewFooterLook as F, DropdownMenuTrigger as Fa, updateButtonStyleSelectionChoice as Fi, SITE_CHROME_VISIBLE_HEADER_SELECTOR as Fn, gradientToCss as Fr, LinkWidgetProvider as Ft, confirmAutosaveBatch as G, Sparkles as Ga, getFieldIntent as Gi, escapeStyleContent as Gn, backgroundAppearanceColorTokenNames as Gr, canRedoHistory as Gt, syncStyleConfiguratorState as H, Undo2 as Ha, paletteColorEntries as Hi, buildThemeRuntime as Hn, sectionTransitionFamilies as Hr, useEditorUiStore as Ht, previewHeaderLayout as I, Primitive as Ia, buttonStyleSelectionKey as Ii, SITE_OVERLAY_HEADER_CLEARANCE_CSS_VAR as In, GRADIENT_PRESETS as Ir, BlockEditor as It, idleEditorTransactionQueueState as J, RotateCcw as Ja, useSdkConfig as Ji, defaultTransforms as Jn, defaultThemeTokens as Jr, pushHistoryEntry as Jt, deriveTransactionCapabilities as K, Smartphone as Ka, isHeaderField as Ki, readBlockComposition as Kn, backgroundAppearanceQuickColorTokenNames as Kr, canUndoHistory as Kt, previewHeaderStyle as L, Button$1 as La, textColorStyle as Li, planSiteOverlayHeaderClearance as Ln, GRADIENT_PRESET_IDS as Lr, hideThemeV2RawStyleFields as Lt, palettePreviewProjection as M, DropdownMenuItem as Ma, findButtonStyleRecipe as Mi, makeDefaultBlockComponent as Mn, findFooterLook as Mr, EditorProvider as Mt, previewButtonStyle as N, DropdownMenuLabel as Na, resolveButtonStyleSelection as Ni, getBlockDefinition as Nn, getHeaderLayout as Nr, useEditor$1 as Nt, markApplyFailed as O, CollapsibleTrigger as Oa, assertNever$7 as Oi, isSupportedLoaderEndpoint as On, designRuntimeRulesForTheme as Or, ReferenceWidgetProvider as Ot, previewCuratedPalette as P, DropdownMenuSeparator as Pa, updateButtonStyleHoverPreference as Pi, projectRecurringSchedule as Pn, getHeaderStyle as Pr, LinkField as Pt, resolvePreviewSidebarTab as Q, Palette as Qa, unmapValueFromSelect as Qi, LINKED_CONTENT_ENTRY_META_KEY as Qn, planSiteChromePlacement as Qr, recordsEqual as Qt, previewSiteStyle as R, cva as Ra, themeButtonClassName as Ri, visibleHeaderClearancePx as Rn, getGradientPreset as Rr, prepareBlockEditorManifest as Rt, applyTypographyDraftPatch as S, DialogDescription as Sa, bookingConsentCarrierSchema as Si, DEFAULT_LAUNCH_READINESS_INPUT as Sn, retargetCompiledBoundaryRuntimeForContentAdjacency as Sr, EntryPickerWidgetProvider as St, deriveStyleConfiguratorCapabilities as T, DialogTitle as Ta, isThemeV2NormalEditorRawStyleFieldName as Ti, MediaEditorContextProvider as Tn, planPageDesignLayoutLookCompatibility as Tr, FileField as Tt, toggleRow as U, TriangleAlert as Ua, paletteColorsSchema as Ui, DEFAULT_THEME_RESPONSIVE_MODE as Un, sectionTransitionFamilyForBoundaryOptionId as Ur, useEditorUiStoreInstance as Ut, styleConfiguratorHistoryEntryForPersistedSelection as V, Upload as Va, VARIANT_ROLES as Vi, validateManifestDraft as Vn, defaultSectionTransitionVariantForFamily as Vr, classifyHistoryShortcut as Vt, beginAutosaveBatch as W, Tablet as Wa, attachRenderableMediaAssetFields as Wi, getResponsiveScopeSpec as Wn, sectionTransitionVariantForBoundaryOptionId as Wr, useOptionalEditorUiStoreInstance as Wt, resolveStyleConfiguratorInitialSnapshot as X, Pencil as Xa, mapCurrentValueForSelect as Xi, resolvePageRouteTarget as Xn, bindingSchema as Xr, undoHistory as Xt, rejectAutosaveBatch as Y, Redo2 as Ya, hasEmptyOption as Yi, resolveEntryRouteTarget as Yn, ctaButton as Yr, redoHistory as Yt, createOptimisticActionRunner as Z, PenLine as Za, mapOptionValueForSelect as Zi, resolveRouteTarget as Zn, EDITABLE_SURFACE_FORWARDING_PROP as Zr, unsafeAsLinkedContentEntryId as Zt, reportPreviewDesignError as _, TabsContent as _a, siblingPath$1 as _i, MediaEditor as _n, __exportAll as _o, heroCopyFragment as _r, loadMailingListAuthoringContext as _t, previewDesignHistoryDomainForCommand as a, SelectContent as aa, logger$1 as ai, parseReferenceKind as an, EllipsisVertical as ao, cardFragment as ar, useUISelector as at, planSiteChromeSelection as b, Dialog as ba, eventCalendarListLayoutCompatibleDisplayModeValues as bi, listEntryPageContentSlots as bn, applyCompiledRuntimeFieldsToContent as br, IconPickerField as bt, selectionSnapshotFromApplySiteStyleSelectionResponse as c, SelectValue as ca, isInlineTextEmpty as ci, resolveContentTitle as cn, ChevronDown as co, blogFeaturedPostFragment as cr, useEntryController as ct, PreviewDesignCoordinatorProvider as d, SearchInput as da, buildResolveImageUrlContext as di, ManifestForm as dn, unsafeAsEditorBlockId as do, formCopyFragment as dr, createSiteOptionWidgetAdapters as dt, useField as ea, runTransforms as ei, useDebouncedLatestSave as en, Maximize2 as eo, getLinkedContentEntryMeta as er, renderSiteChromeBlock as et, usePreviewDesignCoordinator as f, Popover as fa, getCurrentResolveImageUrlContext as fi, createManifestFormAdapter as fn, unsafeAsEditorBlockValues as fo, testimonialsCarouselFragment as fr, SiteOptionPickerField as ft, PREVIEW_DESIGN_SAVE_ERROR_MESSAGE as g, Tabs as ga, getAtPath as gi, createStoreBackend as gn, parseSiteId as go, ctaRowFragment as gr, MailingListTargetingField as gt, PREVIEW_DESIGN_LOAD_ERROR_MESSAGE as h, Input as ha, SECTION_BACKGROUND_MIN_SOURCE_WIDTH as hi, evaluateVisibility as hn, parseMediaAssetId as ho, heroCtaRowFragment as hr, OfferingsPreviewWidgetProvider as ht, usePreviewDesignHistoryOrder as i, Select$1 as ia, renderInlineText as ii, isAdapterReferenceKind as in, Eye as io, headingFragment as ir, ContentFieldsForm as it, openRow as j, DropdownMenuContent as ja, buttonStyleSelectionParameterValue as ji, createCustomBlockRegistry as jn, surfaceResolutionContextFromTheme as jr, RichTextWidgetProvider as jt, markApplySaved as k, Surface as ka, makeBoundaryId as ki, ThemeScope as kn, getCuratedSiteStyle as kr, useRichTextImagePicker as kt, deriveSiteStylePreviewUpdate as l, Callout as la, Text$2 as li, PaletteProvider as ln, Check as lo, singleButtonFragment as lr, useEntryEditorController as lt, usePreviewThemeOverlay as m, PopoverTrigger as ma, setContextResolveImageUrlContext as mi, FormRenderer as mn, expectParsedBrandedId as mo, ctaCopyFragment as mr, OfferingsPreviewGroupOrderField as mt, useSiteChromeCustomizeOptional as n, TooltipContent as na, parseGeneratedIconAssetId as ni, createSnapshotFromPageRecord as nn, Image as no, ImageResolveContextProvider as nr, PreviewInlineTextEditor as nt, selectPreviewDesignHistoryDomain as o, SelectItem as oa, coerceInlineTextWithFallback as oi, deriveRouteMetadataPresentation as on, ChevronRight as oo, faqAccordionFragment as or, EditableBlockProvider as ot, PreviewThemeOverlayProvider as p, PopoverContent as pa, resolveImageUrlWithContext as pi, createManifestValidation as pn, assertNever$10 as po, testimonialsHeadingFragment as pr, SiteOptionWidgetProvider as pt, enqueueTransaction as q, SlidersHorizontal as qa, SdkConfigProvider as qi, getDefaultComponentRegistry as qn, parseBackgroundAppearanceColorToken as qr, createHistoryState as qt, PreviewDesignHistoryOrderProvider as r, TooltipTrigger as ra, parseGeneratedIconSetId as ri, BUILT_IN_REFERENCE_KINDS as rn, GripVertical as ro, richTextFragment as rr, PreviewTextEditor as rt, buildApplySiteStyleSelectionBody as s, SelectTrigger as sa, inlineTextToPlainText as si, resolvePageRouteSlug as sn, ChevronLeft as so, faqHeadingFragment as sr, EntryControllerProvider as st, SiteChromeCustomizeProvider as t, Tooltip as ta, parseBlockIconValue as ti, createRoutingHelpers as tn, LoaderCircle as to, clientComponentRegistry as tr, PreviewRichTextEditor as tt, planConfiguratorPreview as u, CalloutDescription as ua, RichText as ui, usePalette as un, parseFooterModuleId as uo, formEmbedFragment as ur, createSiteOptionApiLoader as ut, buildSiteChromeSelectionModels as v, TabsList as va, BlockApiProvider as vi, applyPageDesignEditorAction as vn, bodyCopyFragment as vr, MailingListTargetingProvider as vt, createStyleConfiguratorState as w, DialogHeader as wa, isRecord$17 as wi, AMBIENT_BACKGROUND_ROLES as wn, formatSiteChromeBackgroundTokenLabel as wr, ButtonWidgetProvider as wt, applyResolvedSelections as x, DialogContent as xa, eventCalendarListLayoutOptions as xi, decodePublicProductCategorySelectorQuery as xn, compileEditorModelRuntime as xr, EntryPickerField as xt, cycleSiteChromeSelectionId as y, TabsTrigger as ya, calendarWindowQuerySchema as yi, getPageDesignEditorState as yn, defineFragment as yr, InlineTextField as yt, resetTypographyToSaved as z, cn$2 as za, parseThemeForPersistence as zi, resolveVariantKey as zn, parseGradientPresetId as zr, stripTopLevelRoutingUi as zt };
231310
+ export { FooterComposerPanel as $, Monitor as $a, FieldFrame as $i, coerceLinkedContentEntriesPayload as $n, runTransforms as $r, useMultiAutosave as $t, markApplySaving as A, DropdownMenu as Aa, buttonStyleSelectionParameterValue as Ai, containerResponsiveThemeCss as An, compileTransitionRuntime as Ar, RichTextField as At, setPaletteColors as B, X as Ba, ORDINARY_TEXT_MIN_CONTRAST_RATIO as Bi, compileManifestContentContract as Bn, curatedPalettes as Br, getBlockManifestFromSdkConfig as Bt, cancelPreview as C, DialogFooter as Ca, isRecord$17 as Ci, deriveLaunchReadiness as Cn, retargetCompiledRuntimeFieldsForContentAdjacency as Cr, ButtonVariantField as Ct, isPreviewingChange as D, CollapsibleContent as Da, assertNever$7 as Di, useApiMediaUploader as Dn, curatedSiteStyles as Dr, ReferenceField as Dt, dismissWarnings as E, Collapsible as Ea, asDesignBlockId as Ei, useMediaEditorOptional as En, componentRuntimeRulesForTheme as Er, MediaField as Et, previewFooterLook as F, DropdownMenuTrigger as Fa, buttonStyleSelectionKey as Fi, SITE_CHROME_VISIBLE_HEADER_SELECTOR as Fn, gradientToCss as Fr, LinkWidgetProvider as Ft, confirmAutosaveBatch as G, Sparkles as Ga, getFieldIntent as Gi, escapeStyleContent as Gn, backgroundAppearanceColorTokenNames as Gr, canRedoHistory as Gt, syncStyleConfiguratorState as H, Undo2 as Ha, paletteColorEntries as Hi, buildThemeRuntime as Hn, sectionTransitionFamilies as Hr, useEditorUiStore as Ht, previewHeaderLayout as I, Primitive as Ia, textColorStyle as Ii, SITE_OVERLAY_HEADER_CLEARANCE_CSS_VAR as In, GRADIENT_PRESETS as Ir, BlockEditor as It, idleEditorTransactionQueueState as J, RotateCcw as Ja, useSdkConfig as Ji, defaultTransforms as Jn, ctaButton as Jr, pushHistoryEntry as Jt, deriveTransactionCapabilities as K, Smartphone as Ka, isHeaderField as Ki, readBlockComposition as Kn, backgroundAppearanceQuickColorTokenNames as Kr, canUndoHistory as Kt, previewHeaderStyle as L, Button$1 as La, defaultThemeTokens as Li, planSiteOverlayHeaderClearance as Ln, GRADIENT_PRESET_IDS as Lr, hideThemeV2RawStyleFields as Lt, palettePreviewProjection as M, DropdownMenuItem as Ma, resolveButtonStyleSelection as Mi, makeDefaultBlockComponent as Mn, findFooterLook as Mr, EditorProvider as Mt, previewButtonStyle as N, DropdownMenuLabel as Na, updateButtonStyleHoverPreference as Ni, getBlockDefinition as Nn, getHeaderLayout as Nr, useEditor$1 as Nt, markApplyFailed as O, CollapsibleTrigger as Oa, makeBoundaryId as Oi, isSupportedLoaderEndpoint as On, designRuntimeRulesForTheme as Or, ReferenceWidgetProvider as Ot, previewCuratedPalette as P, DropdownMenuSeparator as Pa, updateButtonStyleSelectionChoice as Pi, projectRecurringSchedule as Pn, getHeaderStyle as Pr, LinkField as Pt, resolvePreviewSidebarTab as Q, Palette as Qa, unmapValueFromSelect as Qi, LINKED_CONTENT_ENTRY_META_KEY as Qn, parseBackgroundContent as Qr, recordsEqual as Qt, previewSiteStyle as R, cva as Ra, themeButtonClassName as Ri, visibleHeaderClearancePx as Rn, getGradientPreset as Rr, prepareBlockEditorManifest as Rt, applyTypographyDraftPatch as S, DialogDescription as Sa, isObjectRecord$1 as Si, DEFAULT_LAUNCH_READINESS_INPUT as Sn, retargetCompiledBoundaryRuntimeForContentAdjacency as Sr, EntryPickerWidgetProvider as St, deriveStyleConfiguratorCapabilities as T, DialogTitle as Ta, pageDesignEditorBackgroundOverrideOwner as Ti, MediaEditorContextProvider as Tn, planPageDesignLayoutLookCompatibility as Tr, FileField as Tt, toggleRow as U, TriangleAlert as Ua, paletteColorsSchema as Ui, DEFAULT_THEME_RESPONSIVE_MODE as Un, sectionTransitionFamilyForBoundaryOptionId as Ur, useEditorUiStoreInstance as Ut, styleConfiguratorHistoryEntryForPersistedSelection as V, Upload as Va, VARIANT_ROLES as Vi, validateManifestDraft as Vn, defaultSectionTransitionVariantForFamily as Vr, classifyHistoryShortcut as Vt, beginAutosaveBatch as W, Tablet as Wa, attachRenderableMediaAssetFields as Wi, getResponsiveScopeSpec as Wn, sectionTransitionVariantForBoundaryOptionId as Wr, useOptionalEditorUiStoreInstance as Wt, resolveStyleConfiguratorInitialSnapshot as X, Pencil as Xa, mapCurrentValueForSelect as Xi, resolvePageRouteTarget as Xn, EDITABLE_SURFACE_FORWARDING_PROP as Xr, undoHistory as Xt, rejectAutosaveBatch as Y, Redo2 as Ya, hasEmptyOption as Yi, resolveEntryRouteTarget as Yn, bindingSchema as Yr, redoHistory as Yt, createOptimisticActionRunner as Z, PenLine as Za, mapOptionValueForSelect as Zi, resolveRouteTarget as Zn, planSiteChromePlacement as Zr, unsafeAsLinkedContentEntryId as Zt, reportPreviewDesignError as _, TabsContent as _a, BlockApiProvider as _i, MediaEditor as _n, __exportAll as _o, heroCopyFragment as _r, loadMailingListAuthoringContext as _t, previewDesignHistoryDomainForCommand as a, SelectContent as aa, coerceInlineTextWithFallback as ai, parseReferenceKind as an, EllipsisVertical as ao, cardFragment as ar, useUISelector as at, planSiteChromeSelection as b, Dialog as ba, eventCalendarListLayoutOptions as bi, listEntryPageContentSlots as bn, applyCompiledRuntimeFieldsToContent as br, IconPickerField as bt, selectionSnapshotFromApplySiteStyleSelectionResponse as c, SelectValue as ca, Text$2 as ci, resolveContentTitle as cn, ChevronDown as co, blogFeaturedPostFragment as cr, useEntryController as ct, PreviewDesignCoordinatorProvider as d, SearchInput as da, getCurrentResolveImageUrlContext as di, ManifestForm as dn, unsafeAsEditorBlockId as do, formCopyFragment as dr, createSiteOptionWidgetAdapters as dt, useField as ea, parseBlockIconValue as ei, useDebouncedLatestSave as en, Maximize2 as eo, getLinkedContentEntryMeta as er, renderSiteChromeBlock as et, usePreviewDesignCoordinator as f, Popover as fa, resolveImageUrlWithContext as fi, createManifestFormAdapter as fn, unsafeAsEditorBlockValues as fo, testimonialsCarouselFragment as fr, SiteOptionPickerField as ft, PREVIEW_DESIGN_SAVE_ERROR_MESSAGE as g, Tabs as ga, siblingPath$1 as gi, createStoreBackend as gn, parseSiteId as go, ctaRowFragment as gr, MailingListTargetingField as gt, PREVIEW_DESIGN_LOAD_ERROR_MESSAGE as h, Input as ha, getAtPath as hi, evaluateVisibility as hn, parseMediaAssetId as ho, heroCtaRowFragment as hr, OfferingsPreviewWidgetProvider as ht, usePreviewDesignHistoryOrder as i, Select$1 as ia, logger$1 as ii, isAdapterReferenceKind as in, Eye as io, headingFragment as ir, ContentFieldsForm as it, openRow as j, DropdownMenuContent as ja, findButtonStyleRecipe as ji, createCustomBlockRegistry as jn, surfaceResolutionContextFromTheme as jr, RichTextWidgetProvider as jt, markApplySaved as k, Surface as ka, buttonStyleHoverPreferenceValue as ki, ThemeScope as kn, getCuratedSiteStyle as kr, useRichTextImagePicker as kt, deriveSiteStylePreviewUpdate as l, Callout as la, RichText as li, PaletteProvider as ln, Check as lo, singleButtonFragment as lr, useEntryEditorController as lt, usePreviewThemeOverlay as m, PopoverTrigger as ma, SECTION_BACKGROUND_MIN_SOURCE_WIDTH as mi, FormRenderer as mn, expectParsedBrandedId as mo, ctaCopyFragment as mr, OfferingsPreviewGroupOrderField as mt, useSiteChromeCustomizeOptional as n, TooltipContent as na, parseGeneratedIconSetId as ni, createSnapshotFromPageRecord as nn, Image as no, ImageResolveContextProvider as nr, PreviewInlineTextEditor as nt, selectPreviewDesignHistoryDomain as o, SelectItem as oa, inlineTextToPlainText as oi, deriveRouteMetadataPresentation as on, ChevronRight as oo, faqAccordionFragment as or, EditableBlockProvider as ot, PreviewThemeOverlayProvider as p, PopoverContent as pa, setContextResolveImageUrlContext as pi, createManifestValidation as pn, assertNever$10 as po, testimonialsHeadingFragment as pr, SiteOptionWidgetProvider as pt, enqueueTransaction as q, SlidersHorizontal as qa, SdkConfigProvider as qi, getDefaultComponentRegistry as qn, parseBackgroundAppearanceColorToken as qr, createHistoryState as qt, PreviewDesignHistoryOrderProvider as r, TooltipTrigger as ra, renderInlineText as ri, BUILT_IN_REFERENCE_KINDS as rn, GripVertical as ro, richTextFragment as rr, PreviewTextEditor as rt, buildApplySiteStyleSelectionBody as s, SelectTrigger as sa, isInlineTextEmpty as si, resolvePageRouteSlug as sn, ChevronLeft as so, faqHeadingFragment as sr, EntryControllerProvider as st, SiteChromeCustomizeProvider as t, Tooltip as ta, parseGeneratedIconAssetId as ti, createRoutingHelpers as tn, LoaderCircle as to, clientComponentRegistry as tr, PreviewRichTextEditor as tt, planConfiguratorPreview as u, CalloutDescription as ua, buildResolveImageUrlContext as ui, usePalette as un, parseFooterModuleId as uo, formEmbedFragment as ur, createSiteOptionApiLoader as ut, buildSiteChromeSelectionModels as v, TabsList as va, calendarWindowQuerySchema as vi, applyPageDesignEditorAction as vn, bodyCopyFragment as vr, MailingListTargetingProvider as vt, createStyleConfiguratorState as w, DialogHeader as wa, isThemeV2NormalEditorRawStyleFieldName as wi, AMBIENT_BACKGROUND_ROLES as wn, formatSiteChromeBackgroundTokenLabel as wr, ButtonWidgetProvider as wt, applyResolvedSelections as x, DialogContent as xa, bookingConsentCarrierSchema as xi, decodePublicProductCategorySelectorQuery as xn, compileEditorModelRuntime as xr, EntryPickerField as xt, cycleSiteChromeSelectionId as y, TabsTrigger as ya, eventCalendarListLayoutCompatibleDisplayModeValues as yi, getPageDesignEditorState as yn, defineFragment as yr, InlineTextField as yt, resetTypographyToSaved as z, cn$2 as za, parseThemeForPersistence as zi, resolveVariantKey as zn, parseGradientPresetId as zr, stripTopLevelRoutingUi as zt };