@riverbankcms/sdk 0.145.0 → 0.146.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 (159) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/{PreviewEditorSidebar-O-V6Pl5a.mjs → PreviewEditorSidebar-kg2r2Fyi.mjs} +4 -4
  3. package/dist/{PreviewEditorUI-1UlX_9cw.mjs → PreviewEditorUI-sWMLp5dT.mjs} +3 -3
  4. package/dist/{PreviewQueryProvider-BxGwdOCV.mjs → PreviewQueryProvider-Cg51xq1w.mjs} +1 -1
  5. package/dist/{SdkPreviewModeRuntime-DYbA_4iv.mjs → SdkPreviewModeRuntime-h2ti3Hch.mjs} +280 -182
  6. package/dist/{SiteChromeCustomizeContext-CNTKQ9nx.mjs → SiteChromeCustomizeContext-DEACFH9m.mjs} +1330 -734
  7. package/dist/_dts/ai/src/contracts/proposals.d.ts +924 -0
  8. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +84 -0
  9. package/dist/_dts/api/src/bookingConfig.d.ts +8 -1
  10. package/dist/_dts/api/src/bookingManagementEndpoints.d.ts +8 -14
  11. package/dist/_dts/api/src/bookingManagementEventEndpointTypes.d.ts +45 -1
  12. package/dist/_dts/api/src/clientErrorReporting.d.ts +50 -0
  13. package/dist/_dts/api/src/communications.d.ts +20 -4
  14. package/dist/_dts/api/src/contentRuntime.d.ts +3 -7
  15. package/dist/_dts/api/src/endpoints.d.ts +223 -88
  16. package/dist/_dts/api/src/event-visibility.d.ts +20 -0
  17. package/dist/_dts/api/src/eventManagementContracts.d.ts +56 -3
  18. package/dist/_dts/api/src/eventManagementEndpointDefinitions.d.ts +24 -0
  19. package/dist/_dts/api/src/eventPageWords.d.ts +19 -0
  20. package/dist/_dts/api/src/eventSeriesRegistrationEndpointDefinitions.d.ts +14 -0
  21. package/dist/_dts/api/src/index.d.ts +7 -1
  22. package/dist/_dts/api/src/indexCoreTypes.d.ts +1 -1
  23. package/dist/_dts/api/src/indexExtendedTypes.d.ts +1 -1
  24. package/dist/_dts/api/src/launchReadiness.d.ts +20 -2
  25. package/dist/_dts/api/src/launchRequirementPriority.d.ts +3 -0
  26. package/dist/_dts/api/src/newsletterCampaignCommandEndpoints.d.ts +13 -0
  27. package/dist/_dts/api/src/newsletterEndpointDefinitions.d.ts +42 -0
  28. package/dist/_dts/api/src/occurrenceScheduling.d.ts +10 -0
  29. package/dist/_dts/api/src/pageSurfaceEndpoints.d.ts +44 -0
  30. package/dist/_dts/api/src/participantMessaging.d.ts +11 -1
  31. package/dist/_dts/api/src/sdk-event-upsert.d.ts +40 -1
  32. package/dist/_dts/api/src/sdkContracts.d.ts +3 -42
  33. package/dist/_dts/api/src/sdkCourseBooking.d.ts +30 -0
  34. package/dist/_dts/api/src/sdkEventEndpointDefinitions.d.ts +76 -0
  35. package/dist/_dts/api/src/sdkEventLifecycle.d.ts +37 -0
  36. package/dist/_dts/api/src/sdkManagedEventContracts.d.ts +18 -0
  37. package/dist/_dts/api/src/sdkPageDesign.d.ts +388 -0
  38. package/dist/_dts/api/src/sdkPageDesignEndpointDefinitions.d.ts +22 -0
  39. package/dist/_dts/api/src/sdkPullEventContracts.d.ts +46 -0
  40. package/dist/_dts/api/src/siteAddressEndpoints.d.ts +29 -0
  41. package/dist/_dts/api/src/siteManagementEndpointDefinitions.d.ts +49 -0
  42. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +2 -32
  43. package/dist/_dts/api/src/siteOnboarding.d.ts +75 -9
  44. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +1 -1
  45. package/dist/_dts/api/src/types.d.ts +7 -1
  46. package/dist/_dts/api/src/wholeSeriesRuns.d.ts +14 -0
  47. package/dist/_dts/blocks/src/index.d.ts +2 -0
  48. package/dist/_dts/blocks/src/pageRenderAdmission.d.ts +10 -0
  49. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +3 -1
  50. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +27 -5
  51. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +14 -1
  52. package/dist/_dts/blocks/src/system/icons/social.d.ts +1 -1
  53. package/dist/_dts/blocks/src/system/registry.d.ts +1 -1
  54. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.d.ts +1 -0
  55. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +20 -4
  56. package/dist/_dts/blocks/src/system/runtime/nodes/form.fields.d.ts +5 -0
  57. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.block-submission.d.ts +36 -0
  58. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.client.d.ts +1 -1
  59. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.interactive.d.ts +6 -1
  60. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.parts.d.ts +46 -0
  61. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.types.d.ts +2 -1
  62. package/dist/_dts/blocks/src/system/runtime/resolver.d.ts +2 -1
  63. package/dist/_dts/blocks/src/system/runtime/shared/components/CheckboxChoice.d.ts +18 -0
  64. package/dist/_dts/blocks/src/system/runtime/shared/components/index.d.ts +1 -0
  65. package/dist/_dts/content-editor/src/index.d.ts +1 -0
  66. package/dist/_dts/content-editor/src/primitives/previewEditableOutline.d.ts +2 -0
  67. package/dist/_dts/content-editor/src/ui/editorBlockEquality.d.ts +3 -0
  68. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +3 -5
  69. package/dist/_dts/core/src/block-content-identity.d.ts +5 -0
  70. package/dist/_dts/core/src/editor-state.d.ts +2 -7
  71. package/dist/_dts/core/src/index.d.ts +1 -0
  72. package/dist/_dts/core/src/mailing-list-submission.d.ts +3 -1
  73. package/dist/_dts/core/src/public-event-booking-availability.d.ts +20 -1
  74. package/dist/_dts/core/src/public-profile.d.ts +3 -1
  75. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +49 -2
  76. package/dist/_dts/editor-blocks/src/BlockEditor.d.ts +3 -0
  77. package/dist/_dts/editor-blocks/src/block-editor/EmptyState.d.ts +13 -1
  78. package/dist/_dts/editor-blocks/src/block-editor/types.d.ts +2 -8
  79. package/dist/_dts/editor-blocks/src/index.d.ts +1 -0
  80. package/dist/_dts/preview-next/src/client/blocks/composer/types.d.ts +3 -4
  81. package/dist/_dts/preview-next/src/client/preview/PreviewEntryControllerProvider.d.ts +1 -2
  82. package/dist/_dts/sdk/src/next/renderSdkContent.d.ts +0 -1
  83. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +2 -2
  84. package/dist/_dts/sdk/src/rendering/components/pageShared.d.ts +2 -2
  85. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +6 -1
  86. package/dist/_dts/sdk/src/rendering/helpers/loadPage.d.ts +5 -2
  87. package/dist/_dts/sdk/src/rendering/helpers/pageRuntimeSupport.d.ts +6 -1
  88. package/dist/_dts/sdk/src/rendering/helpers/templateContent.d.ts +2 -0
  89. package/dist/_dts/sdk/src/rendering/hooks/useContent.d.ts +5 -1
  90. package/dist/_dts/sdk/src/rendering/hooks/usePage.d.ts +5 -1
  91. package/dist/_dts/sdk/src/version.d.ts +1 -1
  92. package/dist/_dts/surfaces/src/index.d.ts +9 -0
  93. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  94. package/dist/_dts/theme-core/src/inputs/checkable.d.ts +42 -0
  95. package/dist/_dts/theme-core/src/inputs/inputGroup.d.ts +19 -0
  96. package/dist/_dts/ui/src/navigation-tabs.d.ts +2 -1
  97. package/dist/client/{BookingConsentAgreement-DlVOmpW4.mjs → BookingConsentAgreement-BQKL045f.mjs} +2 -2
  98. package/dist/client/CheckboxChoice--W2n6yKs.mjs +24 -0
  99. package/dist/client/{DiscountCodeDisclosure-T9q623PQ.mjs → DiscountCodeDisclosure-CsOtjFY7.mjs} +1 -1
  100. package/dist/client/{EventCombined.client-DnsUeONl.mjs → EventCombined.client-BdRjVbhH.mjs} +6 -6
  101. package/dist/client/{EventListing.client-CNBFqJS4.mjs → EventListing.client-DBxC6xOU.mjs} +2 -2
  102. package/dist/client/{LeafletStaticMap-C7higg52.mjs → LeafletStaticMap-mHEJKrTs.mjs} +1 -1
  103. package/dist/client/{ThemedButton-LAJTNrS8.mjs → ThemedButton-CE5KYT6z.mjs} +1 -1
  104. package/dist/client/{accordion-BcNF9TjX.mjs → accordion-BEQNSptH.mjs} +1 -1
  105. package/dist/client/{bookingForm-BUaQ3S03.mjs → bookingForm-AMr-s5vw.mjs} +33 -52
  106. package/dist/client/bookings.mjs +48 -46
  107. package/dist/client/{carousel-CTdF6e0V.mjs → carousel-Bbt_QgJy.mjs} +1 -1
  108. package/dist/client/{cart-pGtyf_XU.mjs → cart-CnWi30zJ.mjs} +3 -3
  109. package/dist/client/{checkout-Da9WSn7Y.mjs → checkout-f3nkWgd4.mjs} +3 -3
  110. package/dist/client/client.mjs +1611 -815
  111. package/dist/client/{context-BFeY_Wbm.mjs → context-Bc8T6pCe.mjs} +1 -1
  112. package/dist/client/{dates-BhwVSxDg.mjs → dates-DUsCEzLb.mjs} +2 -3
  113. package/dist/client/{eventCalendar-BAtdnR4s.mjs → eventCalendar-CKwgXui5.mjs} +9 -9
  114. package/dist/client/{eventCombined-RksA3tRC.mjs → eventCombined-CBAOSTGR.mjs} +4 -4
  115. package/dist/client/{eventDetails-BB9hB4wR.mjs → eventDetails-Dh-_WiBD.mjs} +4 -4
  116. package/dist/client/{eventListing-BpJBkviM.mjs → eventListing-CA3frIH5.mjs} +4 -4
  117. package/dist/client/{eventRegistration-B18jFq_o.mjs → eventRegistration-BHtsgdcg.mjs} +21 -22
  118. package/dist/client/{form-Dodt8Y8t.mjs → form-C2h3YAcg.mjs} +135 -104
  119. package/dist/client/{headerSection-B0qj9ttg.mjs → headerSection-lwMuMTH9.mjs} +3 -3
  120. package/dist/client/hooks.mjs +489 -65
  121. package/dist/client/{locationMap-gn-iMk7a.mjs → locationMap-DJ2bLODV.mjs} +3 -3
  122. package/dist/client/{newsletterForm-oByLdFkr.mjs → newsletterForm-8Pu5K32g.mjs} +203 -101
  123. package/dist/client/{pricing-BOU0_Hai.mjs → pricing-HHx3IgBz.mjs} +1 -1
  124. package/dist/client/{productDetail-D9v1zH2j.mjs → productDetail-C_1odnAf.mjs} +3 -3
  125. package/dist/client/{productList-D-mItx1n.mjs → productList-CtCv-Nmx.mjs} +3 -3
  126. package/dist/client/{propsDecoder-C3TP_z4x.mjs → propsDecoder-Dcgsncxb.mjs} +40 -23
  127. package/dist/client/rendering/client.mjs +490 -457
  128. package/dist/client/rendering/islands.mjs +17 -17
  129. package/dist/client/rendering.mjs +1515 -628
  130. package/dist/client/{routeIslandApiConfig-DzN1W0sq.mjs → routeIslandApiConfig-CWLP7wu3.mjs} +1 -1
  131. package/dist/client/{scrollToBlock-CWs8EIdo.mjs → scrollToBlock-BNgaEUpo.mjs} +1 -1
  132. package/dist/client/{shop-Dufw7N7o.mjs → shop-DQXrQ8kJ.mjs} +7 -7
  133. package/dist/client/{shop-commerce-gklw_hLF.mjs → shop-commerce-B88zmWWo.mjs} +6 -6
  134. package/dist/client/{shop-commerce-store-Sf1eyTaZ.mjs → shop-commerce-store-DtSfkKuX.mjs} +1 -1
  135. package/dist/client/{shopShared-B5nyA41S.mjs → shopShared-cuaONpkJ.mjs} +4 -4
  136. package/dist/client/{useEventFilters-CLL829vy.mjs → useEventFilters-Bukew6Gb.mjs} +6 -6
  137. package/dist/preview-next/before-render.mjs +368 -103
  138. package/dist/preview-next/client/runtime.mjs +4 -4
  139. package/dist/preview-next/editor-base.css +11 -0
  140. package/dist/preview-next/keepAlive.mjs +328 -103
  141. package/dist/preview-next/middleware.mjs +329 -103
  142. package/dist/{previewAuthStore-BxAFlu-j.mjs → previewAuthStore-h5IgbSp4.mjs} +353 -103
  143. package/dist/{sdk-runtime-D0uZFev7.mjs → sdk-runtime-DTc1uIXn.mjs} +2 -2
  144. package/dist/{separator-EvdCAn4V.mjs → separator-BpHS1KZK.mjs} +1 -1
  145. package/dist/server/components.mjs +1376 -331
  146. package/dist/server/config-validation.mjs +405 -27
  147. package/dist/server/config.mjs +405 -27
  148. package/dist/server/data.mjs +405 -27
  149. package/dist/server/index.mjs +370 -104
  150. package/dist/server/next.mjs +1430 -463
  151. package/dist/server/page-converter.mjs +36 -25
  152. package/dist/server/prebuild.mjs +1 -1
  153. package/dist/server/rendering/server.mjs +1374 -331
  154. package/dist/server/rendering.mjs +1436 -364
  155. package/dist/server/routing.mjs +476 -43
  156. package/dist/server/server.mjs +504 -169
  157. package/dist/server/theme-bridge.mjs +467 -111
  158. package/dist/styles/index.css +186 -65
  159. package/package.json +1 -1
@@ -805,6 +805,25 @@ function formError(field) {
805
805
  }
806
806
  //#endregion
807
807
  //#region ../core/src/mailing-list-targeting.ts
808
+ function sameMailingListPlacementTargeting(left, right) {
809
+ if (left.mode !== right.mode) return false;
810
+ if (left.mode === "default" || right.mode === "default") return true;
811
+ if (left.provider !== right.provider || left.selection.tag !== right.selection.tag) return false;
812
+ if (left.provider === "mailchimp" && (right.provider !== "mailchimp" || left.audienceId !== right.audienceId)) return false;
813
+ return sameIds(targetingSelectionIds(left), targetingSelectionIds(right));
814
+ }
815
+ function targetingSelectionIds(targeting) {
816
+ switch (targeting.provider) {
817
+ case "native": return targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
818
+ case "brevo": return targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
819
+ case "mailchimp": return targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
820
+ }
821
+ }
822
+ function sameIds(left, right) {
823
+ const leftIds = new Set(left);
824
+ const rightIds = new Set(right);
825
+ return leftIds.size === left.length && rightIds.size === right.length && leftIds.size === rightIds.size && [...leftIds].every((id) => rightIds.has(id));
826
+ }
808
827
  function planMailingListChoicePresentation(targeting) {
809
828
  if (targeting.mode === "default" || targeting.selection.tag === "fixed") return { tag: "hidden" };
810
829
  switch (targeting.provider) {
@@ -27627,6 +27646,7 @@ const publicEventLoaderQuerySchemaShape = {
27627
27646
  seriesSlug: z.string().optional(),
27628
27647
  includeOccurrenceId: z.string().optional(),
27629
27648
  readIntent: z.string().optional(),
27649
+ detailAdmission: z.string().optional(),
27630
27650
  detailLevel: z.string().optional(),
27631
27651
  readProfile: z.string().optional()
27632
27652
  };
@@ -27793,11 +27813,11 @@ const publicEventBookingAvailabilitySchema = z.discriminatedUnion("kind", [z.obj
27793
27813
  "series_archived",
27794
27814
  "parent_archived",
27795
27815
  "parent_inconsistent",
27796
- "not_public",
27797
27816
  "already_started",
27798
27817
  "enrollment_closed"
27799
27818
  ])
27800
27819
  })]);
27820
+ const publicEventBookingClosedDetailSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("opensSoon") }), z.object({ kind: z.literal("paused") })]);
27801
27821
  const publicRunSessionSchema = z.object({
27802
27822
  occurrenceId: z.string(),
27803
27823
  startsAt: z.string(),
@@ -27842,6 +27862,7 @@ const publicEventBaseSchema = z.object({
27842
27862
  destination: publicEventDestinationSchema,
27843
27863
  requiresRegistration: z.boolean(),
27844
27864
  bookingAvailability: publicEventBookingAvailabilitySchema,
27865
+ bookingClosedDetail: publicEventBookingClosedDetailSchema.optional(),
27845
27866
  deliveryMode: z.enum([
27846
27867
  "in_person",
27847
27868
  "online",
@@ -29779,6 +29800,7 @@ const SOCIAL_ICON_NAMES = [
29779
29800
  "SocialInstagram",
29780
29801
  "SocialLinkedIn",
29781
29802
  "SocialTikTok",
29803
+ "SocialWhatsApp",
29782
29804
  "SocialX",
29783
29805
  "SocialYouTube"
29784
29806
  ];
@@ -29815,6 +29837,7 @@ const socialIcons = {
29815
29837
  /* @__PURE__ */ jsx("path", { d: "M10.5 9v10" })
29816
29838
  ] })),
29817
29839
  SocialTikTok: createIcon("SocialTikTokIcon", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M14 4v11.5a4.5 4.5 0 1 1-4-4.47" }), /* @__PURE__ */ jsx("path", { d: "M14 4c.7 3 2.4 4.5 5 4.5" })] })),
29840
+ SocialWhatsApp: createIcon("SocialWhatsAppIcon", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M3.5 20.5 4.8 16.4A8.5 8.5 0 1 1 8 19.3Z" }), /* @__PURE__ */ jsx("path", { d: "M9.2 8.2c.2-.5.9-.6 1.2-.1l.8 1.4c.2.3.1.7-.2 1l-.5.5c.5 1.1 1.4 2 2.5 2.5l.5-.5c.3-.3.7-.4 1-.2l1.4.8c.5.3.4 1-.1 1.2-2.9 1.1-7.7-3.7-6.6-6.6Z" })] })),
29818
29841
  SocialX: createIcon("SocialXIcon", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "m5 4 14 16" }), /* @__PURE__ */ jsx("path", { d: "M19 4 5 20" })] })),
29819
29842
  SocialYouTube: createIcon("SocialYouTubeIcon", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M21 12c0 3.5-.5 5.5-1.3 6.3S16.5 19.5 12 19.5s-6.9-.4-7.7-1.2S3 15.5 3 12s.5-5.5 1.3-6.3S7.5 4.5 12 4.5s6.9.4 7.7 1.2S21 8.5 21 12Z" }), /* @__PURE__ */ jsx("path", { d: "m10 9 5 3-5 3Z" })] }))
29820
29843
  };
@@ -32681,7 +32704,6 @@ function closedEventBookingLabel(reason) {
32681
32704
  case "series_archived":
32682
32705
  case "parent_archived":
32683
32706
  case "parent_inconsistent":
32684
- case "not_public":
32685
32707
  case "already_started":
32686
32708
  case "enrollment_closed": return "Registration closed";
32687
32709
  default: return assertNever$4(reason);
@@ -40531,6 +40553,12 @@ function withOptionalPhone(fields, includePhone) {
40531
40553
  }
40532
40554
  //#endregion
40533
40555
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.interactive.ts
40556
+ const NEWSLETTER_SIGNUPS_PAUSED_MESSAGE = "We can’t accept signups right now. Please try again later.";
40557
+ function newsletterBlockSubmissionMessage(target) {
40558
+ if (target.tag === "block-source") return target.sourceRef ? null : NEWSLETTER_SIGNUPS_PAUSED_MESSAGE;
40559
+ if (target.tag !== "unpublished-preview") return null;
40560
+ return target.reason === "unpublished-form" ? "Publish this form to test a signup here." : "This form's signup settings are unpublished. Publish it to test a signup here.";
40561
+ }
40534
40562
  const newsletterButtonVariantSchema = blockButtonRoleSchema;
40535
40563
  const mailingListContextSchema = z.unknown().transform((value, ctx) => {
40536
40564
  const parsed = parseMailingListRuntimeContext(value);
@@ -40571,6 +40599,11 @@ const mailingListContentVersionIdSchema = z.string().transform((value, ctx) => {
40571
40599
  });
40572
40600
  return z.NEVER;
40573
40601
  });
40602
+ const mailingListSignupBlockSourceRefSchema = z.object({
40603
+ tag: z.literal("block_ref"),
40604
+ blockId: mailingListSignupBlockIdSchema,
40605
+ contentVersionId: mailingListContentVersionIdSchema
40606
+ });
40574
40607
  const signedMailingListSourceTokenSchema = z.string().trim().min(1).transform(parseRequiredMailingListSignedSourceToken);
40575
40608
  const newsletterSourceCommonSchema = z.object({
40576
40609
  siteId: z.string().optional(),
@@ -40585,7 +40618,15 @@ const newsletterSourceSchema = z.discriminatedUnion("source", [
40585
40618
  newsletterSourceCommonSchema.extend({
40586
40619
  source: z.literal("block"),
40587
40620
  blockId: z.string().nullable().optional(),
40588
- contentVersionId: z.string().nullable().optional()
40621
+ newsletterBlockSubmission: z.discriminatedUnion("tag", [
40622
+ z.object({
40623
+ tag: z.literal("ready"),
40624
+ sourceRef: mailingListSignupBlockSourceRefSchema
40625
+ }),
40626
+ z.object({ tag: z.literal("unpublished-form") }),
40627
+ z.object({ tag: z.literal("unpublished-settings") }),
40628
+ z.object({ tag: z.literal("unavailable") })
40629
+ ])
40589
40630
  }),
40590
40631
  newsletterSourceCommonSchema.extend({
40591
40632
  source: z.literal("signed-footer"),
@@ -40614,11 +40655,7 @@ const newsletterHydrationSchema = z.object({
40614
40655
  target: z.discriminatedUnion("tag", [
40615
40656
  z.object({
40616
40657
  tag: z.literal("block-source"),
40617
- sourceRef: z.object({
40618
- tag: z.literal("block_ref"),
40619
- blockId: mailingListSignupBlockIdSchema,
40620
- contentVersionId: mailingListContentVersionIdSchema
40621
- }).nullable()
40658
+ sourceRef: mailingListSignupBlockSourceRefSchema.nullable()
40622
40659
  }),
40623
40660
  z.object({
40624
40661
  tag: z.literal("signed-footer-source"),
@@ -40627,7 +40664,11 @@ const newsletterHydrationSchema = z.object({
40627
40664
  token: signedMailingListSourceTokenSchema
40628
40665
  })
40629
40666
  }),
40630
- z.object({ tag: z.literal("preview") })
40667
+ z.object({ tag: z.literal("preview") }),
40668
+ z.object({
40669
+ tag: z.literal("unpublished-preview"),
40670
+ reason: z.enum(["unpublished-form", "unpublished-settings"])
40671
+ })
40631
40672
  ]),
40632
40673
  setupRevision: z.number().int().positive().nullable(),
40633
40674
  choiceProvider: z.enum([
@@ -40696,16 +40737,21 @@ const newsletterFormBlockRuntime = defineProgressiveBlockRuntime({
40696
40737
  });
40697
40738
  function projectNewsletterFormHydrationTarget(source) {
40698
40739
  switch (source.source) {
40699
- case "block": {
40700
- const sourceRef = buildMailingListSignupBlockSourceRef(source.blockId, source.contentVersionId);
40701
- if (sourceRef?.tag === "block_ref") return {
40740
+ case "block": switch (source.newsletterBlockSubmission.tag) {
40741
+ case "unpublished-form":
40742
+ case "unpublished-settings": return {
40743
+ tag: "unpublished-preview",
40744
+ reason: source.newsletterBlockSubmission.tag
40745
+ };
40746
+ case "ready": return {
40702
40747
  tag: "block-source",
40703
- sourceRef
40748
+ sourceRef: source.newsletterBlockSubmission.sourceRef
40704
40749
  };
40705
- return {
40750
+ case "unavailable": return {
40706
40751
  tag: "block-source",
40707
40752
  sourceRef: null
40708
40753
  };
40754
+ default: return assertNever$4(source.newsletterBlockSubmission);
40709
40755
  }
40710
40756
  case "signed-footer": return {
40711
40757
  tag: "signed-footer-source",
@@ -40774,6 +40820,7 @@ function buildNewsletterFormInteractiveIslandProps(props) {
40774
40820
  const decoded = newsletterFormBlockRuntime.contracts.source.decode(props);
40775
40821
  const source = isDecodeSuccess(decoded) ? decoded.value : {
40776
40822
  ...props,
40823
+ ...props.source === "block" ? { newsletterBlockSubmission: { tag: "unavailable" } } : {},
40777
40824
  mailingList: void 0,
40778
40825
  targeting: { mode: "default" }
40779
40826
  };
@@ -43199,6 +43246,156 @@ function FeedbackMessage({ tone, treatment = "panel", children, className, id, t
43199
43246
  });
43200
43247
  }
43201
43248
  //#endregion
43249
+ //#region ../blocks/src/system/runtime/shared/components/CheckboxChoice.tsx
43250
+ /**
43251
+ * The customer-facing checkbox every block renders. Styling comes from the
43252
+ * theme's input CSS (`.form-check`, `.form-checkbox`), so it follows each
43253
+ * site's input colours, borders and corners.
43254
+ */
43255
+ function CheckboxChoice({ label, presentation = "row", className, ...inputProps }) {
43256
+ return /* @__PURE__ */ jsxs("label", {
43257
+ className: clsx("form-check", presentation === "tile" && "form-check--tile", className),
43258
+ children: [/* @__PURE__ */ jsx("input", {
43259
+ ...inputProps,
43260
+ type: "checkbox",
43261
+ className: "form-checkbox"
43262
+ }), /* @__PURE__ */ jsx("span", {
43263
+ className: "form-check-label",
43264
+ children: label
43265
+ })]
43266
+ });
43267
+ }
43268
+ //#endregion
43269
+ //#region ../blocks/src/system/runtime/nodes/form.fields.tsx
43270
+ function fieldBasics(field) {
43271
+ return {
43272
+ inputId: `f_${field.id}`,
43273
+ label: String(field.label ?? field.id),
43274
+ required: Boolean(field.required)
43275
+ };
43276
+ }
43277
+ function textInputType(type) {
43278
+ return type === "email" || type === "url" || type === "tel" || type === "number" ? type : "text";
43279
+ }
43280
+ function textInputConstraints(field, inputType) {
43281
+ if (inputType === "number") return {
43282
+ ...typeof field.min === "number" ? { min: field.min } : {},
43283
+ ...typeof field.max === "number" ? { max: field.max } : {}
43284
+ };
43285
+ return {
43286
+ ...typeof field.minLength === "number" ? { minLength: field.minLength } : {},
43287
+ ...typeof field.maxLength === "number" ? { maxLength: field.maxLength } : {},
43288
+ ...typeof field.pattern === "string" && field.pattern ? { pattern: field.pattern } : {}
43289
+ };
43290
+ }
43291
+ function LabelledField({ basics, children }) {
43292
+ return /* @__PURE__ */ jsxs("div", {
43293
+ className: "fb-field",
43294
+ children: [/* @__PURE__ */ jsx("label", {
43295
+ htmlFor: basics.inputId,
43296
+ className: "form-label",
43297
+ children: basics.label
43298
+ }), children]
43299
+ });
43300
+ }
43301
+ function TextareaField({ field }) {
43302
+ const basics = fieldBasics(field);
43303
+ return /* @__PURE__ */ jsx(LabelledField, {
43304
+ basics,
43305
+ children: /* @__PURE__ */ jsx("textarea", {
43306
+ id: basics.inputId,
43307
+ name: field.id,
43308
+ required: basics.required,
43309
+ placeholder: field.placeholder,
43310
+ rows: field.rows,
43311
+ className: "form-textarea"
43312
+ })
43313
+ });
43314
+ }
43315
+ function SelectField({ field }) {
43316
+ const basics = fieldBasics(field);
43317
+ const options = field.options ?? [];
43318
+ const multiple = Boolean(field.multiple);
43319
+ return /* @__PURE__ */ jsx(LabelledField, {
43320
+ basics,
43321
+ children: /* @__PURE__ */ jsxs("select", {
43322
+ id: basics.inputId,
43323
+ name: field.id,
43324
+ multiple,
43325
+ required: basics.required,
43326
+ className: "form-select",
43327
+ children: [!multiple && /* @__PURE__ */ jsx("option", {
43328
+ value: "",
43329
+ children: "Select…"
43330
+ }), options.map((opt) => /* @__PURE__ */ jsx("option", {
43331
+ value: opt.value,
43332
+ children: opt.label
43333
+ }, opt.value))]
43334
+ })
43335
+ });
43336
+ }
43337
+ function RadioField({ field }) {
43338
+ const basics = fieldBasics(field);
43339
+ const options = field.options ?? [];
43340
+ return /* @__PURE__ */ jsxs("fieldset", {
43341
+ className: "fb-fieldset",
43342
+ children: [/* @__PURE__ */ jsx("legend", {
43343
+ className: "form-label",
43344
+ children: basics.label
43345
+ }), options.map((opt) => /* @__PURE__ */ jsxs("label", {
43346
+ className: "form-check",
43347
+ children: [/* @__PURE__ */ jsx("input", {
43348
+ type: "radio",
43349
+ name: field.id,
43350
+ value: opt.value,
43351
+ required: basics.required,
43352
+ className: "form-radio"
43353
+ }), /* @__PURE__ */ jsx("span", {
43354
+ className: "form-check-label",
43355
+ children: opt.label
43356
+ })]
43357
+ }, opt.value))]
43358
+ });
43359
+ }
43360
+ function TextInputField({ field, type }) {
43361
+ const basics = fieldBasics(field);
43362
+ const inputType = textInputType(type);
43363
+ return /* @__PURE__ */ jsx(LabelledField, {
43364
+ basics,
43365
+ children: /* @__PURE__ */ jsx("input", {
43366
+ id: basics.inputId,
43367
+ name: field.id,
43368
+ type: inputType,
43369
+ required: basics.required,
43370
+ placeholder: field.placeholder,
43371
+ ...textInputConstraints(field, inputType),
43372
+ className: "form-input"
43373
+ })
43374
+ });
43375
+ }
43376
+ function FormFieldControl({ field }) {
43377
+ const type = String(field.type ?? "text");
43378
+ switch (type) {
43379
+ case "textarea": return /* @__PURE__ */ jsx(TextareaField, { field });
43380
+ case "select": return /* @__PURE__ */ jsx(SelectField, { field });
43381
+ case "radio": return /* @__PURE__ */ jsx(RadioField, { field });
43382
+ case "checkbox":
43383
+ case "consent": {
43384
+ const basics = fieldBasics(field);
43385
+ return /* @__PURE__ */ jsx(CheckboxChoice, {
43386
+ name: field.id,
43387
+ required: basics.required,
43388
+ label: basics.label,
43389
+ presentation: type === "consent" ? "tile" : "row"
43390
+ });
43391
+ }
43392
+ default: return /* @__PURE__ */ jsx(TextInputField, {
43393
+ field,
43394
+ type
43395
+ });
43396
+ }
43397
+ }
43398
+ //#endregion
43202
43399
  //#region ../blocks/src/system/runtime/nodes/form.server.tsx
43203
43400
  const FormNodeSSR = ({ blockId, value, submitLabel, successMessage, className, onSubmit, action = "/api/forms/submit", siteId, apiBaseUrl, spamProtectionEnabled }) => {
43204
43401
  const islandProps = buildFormInteractiveIslandProps({
@@ -43222,107 +43419,7 @@ const FormNodeSSR = ({ blockId, value, submitLabel, successMessage, className, o
43222
43419
  })]
43223
43420
  });
43224
43421
  const groups = display.groups;
43225
- const renderField = (field) => {
43226
- const inputId = `f_${field.id}`;
43227
- const label = String(field.label ?? field.id);
43228
- const required = Boolean(field.required);
43229
- const type = String(field.type ?? "text");
43230
- if (type === "textarea") return /* @__PURE__ */ jsxs("div", {
43231
- className: "fb-field",
43232
- children: [/* @__PURE__ */ jsx("label", {
43233
- htmlFor: inputId,
43234
- className: "form-label",
43235
- children: label
43236
- }), /* @__PURE__ */ jsx("textarea", {
43237
- id: inputId,
43238
- name: field.id,
43239
- required,
43240
- placeholder: field.placeholder,
43241
- rows: field.rows,
43242
- className: "form-textarea"
43243
- })]
43244
- }, field.id);
43245
- if (type === "select") {
43246
- const options = field.options ?? [];
43247
- const multiple = Boolean(field.multiple);
43248
- return /* @__PURE__ */ jsxs("div", {
43249
- className: "fb-field",
43250
- children: [/* @__PURE__ */ jsx("label", {
43251
- htmlFor: inputId,
43252
- className: "form-label",
43253
- children: label
43254
- }), /* @__PURE__ */ jsxs("select", {
43255
- id: inputId,
43256
- name: field.id,
43257
- multiple,
43258
- required,
43259
- className: "form-select",
43260
- children: [!multiple && /* @__PURE__ */ jsx("option", {
43261
- value: "",
43262
- children: "Select…"
43263
- }), options.map((opt) => /* @__PURE__ */ jsx("option", {
43264
- value: opt.value,
43265
- children: opt.label
43266
- }, opt.value))]
43267
- })]
43268
- }, field.id);
43269
- }
43270
- if (type === "radio") {
43271
- const options = field.options ?? [];
43272
- return /* @__PURE__ */ jsxs("fieldset", {
43273
- className: "fb-fieldset",
43274
- children: [/* @__PURE__ */ jsx("legend", {
43275
- className: "form-label",
43276
- children: label
43277
- }), options.map((opt) => /* @__PURE__ */ jsxs("label", {
43278
- className: "fb-inline form-label",
43279
- children: [/* @__PURE__ */ jsx("input", {
43280
- type: "radio",
43281
- name: field.id,
43282
- value: opt.value,
43283
- required,
43284
- className: "form-radio"
43285
- }), /* @__PURE__ */ jsx("span", { children: opt.label })]
43286
- }, opt.value))]
43287
- }, field.id);
43288
- }
43289
- if (type === "checkbox" || type === "consent") return /* @__PURE__ */ jsxs("label", {
43290
- className: "fb-inline form-label",
43291
- children: [/* @__PURE__ */ jsx("input", {
43292
- type: "checkbox",
43293
- name: field.id,
43294
- required,
43295
- className: "form-checkbox"
43296
- }), /* @__PURE__ */ jsx("span", { children: label })]
43297
- }, field.id);
43298
- const inputType = type === "email" || type === "url" || type === "tel" || type === "number" ? type : "text";
43299
- const extra = {};
43300
- if (inputType === "text" || inputType === "email" || inputType === "url" || inputType === "tel") {
43301
- if (typeof field.minLength === "number") extra.minLength = field.minLength;
43302
- if (typeof field.maxLength === "number") extra.maxLength = field.maxLength;
43303
- if (typeof field.pattern === "string" && field.pattern) extra.pattern = field.pattern;
43304
- }
43305
- if (inputType === "number") {
43306
- if (typeof field.min === "number") extra.min = field.min;
43307
- if (typeof field.max === "number") extra.max = field.max;
43308
- }
43309
- return /* @__PURE__ */ jsxs("div", {
43310
- className: "fb-field",
43311
- children: [/* @__PURE__ */ jsx("label", {
43312
- htmlFor: inputId,
43313
- className: "form-label",
43314
- children: label
43315
- }), /* @__PURE__ */ jsx("input", {
43316
- id: inputId,
43317
- name: field.id,
43318
- type: inputType,
43319
- required,
43320
- placeholder: field.placeholder,
43321
- ...extra,
43322
- className: "form-input"
43323
- })]
43324
- }, field.id);
43325
- };
43422
+ const renderField = (field) => /* @__PURE__ */ jsx(FormFieldControl, { field }, field.id);
43326
43423
  return /* @__PURE__ */ jsxs("div", {
43327
43424
  ...getIslandRootProps("form", blockId),
43328
43425
  children: [/* @__PURE__ */ jsx(IslandPropsScript, { value: islandProps }), /* @__PURE__ */ jsxs("form", {
@@ -43368,6 +43465,172 @@ const FormNodeSSR = ({ blockId, value, submitLabel, successMessage, className, o
43368
43465
  });
43369
43466
  };
43370
43467
  //#endregion
43468
+ //#region ../blocks/src/system/runtime/shared/components/FieldValidationMessage.tsx
43469
+ function getFieldValidationControlProps({ message, messageId, describedBy }) {
43470
+ const hasMessage = Boolean(message);
43471
+ const ariaDescribedBy = [describedBy, hasMessage ? messageId : void 0].filter((part) => typeof part === "string" && part.length > 0).join(" ");
43472
+ return {
43473
+ "aria-invalid": hasMessage ? "true" : void 0,
43474
+ "aria-describedby": ariaDescribedBy || void 0
43475
+ };
43476
+ }
43477
+ function FieldValidationMessage({ id, children, className, live = "assertive" }) {
43478
+ return /* @__PURE__ */ jsx("p", {
43479
+ id,
43480
+ className,
43481
+ "data-field-validation-message": true,
43482
+ "data-field-validation-tone": "error",
43483
+ role: "alert",
43484
+ "aria-live": live === "off" ? void 0 : live,
43485
+ children
43486
+ });
43487
+ }
43488
+ //#endregion
43489
+ //#region ../blocks/src/system/runtime/nodes/newsletter-form.parts.tsx
43490
+ /**
43491
+ * Newsletter form pieces shared by the SSR render and the hydrated island, so
43492
+ * both produce the same markup.
43493
+ *
43494
+ * Labels are visually hidden: every field has a placeholder, and a visible
43495
+ * "Email *" label over a single field reads like a generic form. Screen
43496
+ * readers still get the label.
43497
+ */
43498
+ const TOPIC_CHOICE_FIELD_ID = "mailingListChoice";
43499
+ const inputIdFor = (field) => `newsletter_${field.id}`;
43500
+ /** Splits the preset's fields into the email (joined with the button) and the rest. */
43501
+ function partitionNewsletterFields(fields) {
43502
+ const email = fields.find((field) => field.type === "email") ?? null;
43503
+ return {
43504
+ email,
43505
+ extra: fields.filter((field) => field !== email)
43506
+ };
43507
+ }
43508
+ function NewsletterExtraField({ field, disabled, error }) {
43509
+ const inputId = inputIdFor(field);
43510
+ const errorId = `${inputId}-error`;
43511
+ return /* @__PURE__ */ jsxs("div", {
43512
+ className: "fb-field newsletter-field",
43513
+ children: [
43514
+ /* @__PURE__ */ jsx("label", {
43515
+ htmlFor: inputId,
43516
+ className: "rb-sr-only",
43517
+ children: field.label
43518
+ }),
43519
+ /* @__PURE__ */ jsx("input", {
43520
+ id: inputId,
43521
+ name: field.id,
43522
+ type: field.type,
43523
+ required: field.required,
43524
+ placeholder: field.placeholder ?? field.label,
43525
+ className: "form-input",
43526
+ disabled,
43527
+ ...getFieldValidationControlProps({
43528
+ message: error,
43529
+ messageId: errorId
43530
+ })
43531
+ }),
43532
+ error ? /* @__PURE__ */ jsx(FieldValidationMessage, {
43533
+ id: errorId,
43534
+ className: "fb-error",
43535
+ children: error
43536
+ }) : null
43537
+ ]
43538
+ });
43539
+ }
43540
+ /** The email field and submit button sharing one input-styled shell. */
43541
+ function NewsletterEmailEntry({ field, buttonVariant, buttonLabel, disabled, error }) {
43542
+ const inputId = inputIdFor(field);
43543
+ const errorId = `${inputId}-error`;
43544
+ return /* @__PURE__ */ jsxs("div", {
43545
+ className: "newsletter-entry",
43546
+ children: [/* @__PURE__ */ jsxs("div", {
43547
+ className: "form-input-group newsletter-entry-group",
43548
+ children: [
43549
+ /* @__PURE__ */ jsx("label", {
43550
+ htmlFor: inputId,
43551
+ className: "rb-sr-only",
43552
+ children: field.label
43553
+ }),
43554
+ /* @__PURE__ */ jsx("input", {
43555
+ id: inputId,
43556
+ name: field.id,
43557
+ type: "email",
43558
+ required: field.required,
43559
+ placeholder: field.placeholder ?? field.label,
43560
+ className: "form-input-group-field",
43561
+ autoComplete: "email",
43562
+ disabled,
43563
+ ...getFieldValidationControlProps({
43564
+ message: error,
43565
+ messageId: errorId
43566
+ })
43567
+ }),
43568
+ /* @__PURE__ */ jsx(ThemedButton, {
43569
+ type: "submit",
43570
+ variant: buttonVariant,
43571
+ size: "md",
43572
+ disabled,
43573
+ label: buttonLabel
43574
+ })
43575
+ ]
43576
+ }), error ? /* @__PURE__ */ jsx(FieldValidationMessage, {
43577
+ id: errorId,
43578
+ className: "fb-error",
43579
+ children: error
43580
+ }) : null]
43581
+ });
43582
+ }
43583
+ /**
43584
+ * The submit action: joined to the email field when the preset has one
43585
+ * (every current preset does), otherwise a plain themed button.
43586
+ */
43587
+ function NewsletterSubmitRow({ emailField, buttonVariant, buttonLabel, disabled, error }) {
43588
+ if (emailField) return /* @__PURE__ */ jsx(NewsletterEmailEntry, {
43589
+ field: emailField,
43590
+ buttonVariant,
43591
+ buttonLabel,
43592
+ disabled,
43593
+ error
43594
+ });
43595
+ return /* @__PURE__ */ jsx("div", {
43596
+ className: "newsletter-submit",
43597
+ children: /* @__PURE__ */ jsx(ThemedButton, {
43598
+ type: "submit",
43599
+ variant: buttonVariant,
43600
+ size: "md",
43601
+ disabled,
43602
+ label: buttonLabel
43603
+ })
43604
+ });
43605
+ }
43606
+ /** Visitor-chosen topics as toggle chips. Real checkboxes underneath. */
43607
+ function NewsletterTopicChips({ choices, disabled, error }) {
43608
+ const errorId = `newsletter_${TOPIC_CHOICE_FIELD_ID}-error`;
43609
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("fieldset", {
43610
+ className: "newsletter-topics",
43611
+ "aria-describedby": error ? errorId : void 0,
43612
+ children: [/* @__PURE__ */ jsx("legend", {
43613
+ className: "newsletter-topics-legend",
43614
+ children: "What would you like to hear about?"
43615
+ }), /* @__PURE__ */ jsx("div", {
43616
+ className: "newsletter-topic-chips",
43617
+ children: choices.map((choice) => /* @__PURE__ */ jsxs("label", {
43618
+ className: "newsletter-topic-chip",
43619
+ children: [/* @__PURE__ */ jsx("input", {
43620
+ type: "checkbox",
43621
+ name: TOPIC_CHOICE_FIELD_ID,
43622
+ value: choice.id,
43623
+ disabled
43624
+ }), /* @__PURE__ */ jsx("span", { children: choice.label })]
43625
+ }, choice.id))
43626
+ })]
43627
+ }), error ? /* @__PURE__ */ jsx(FieldValidationMessage, {
43628
+ id: errorId,
43629
+ className: "fb-error",
43630
+ children: error
43631
+ }) : null] });
43632
+ }
43633
+ //#endregion
43371
43634
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.server.tsx
43372
43635
  /**
43373
43636
  * Newsletter Form Server Component
@@ -43388,36 +43651,15 @@ function NewsletterFormSSR(props) {
43388
43651
  });
43389
43652
  const display = islandProps.render.display;
43390
43653
  const fields = display.fields;
43654
+ const submissionMessage = newsletterBlockSubmissionMessage(islandProps.render.hydration.target);
43391
43655
  if (display.availability === "unavailable") return /* @__PURE__ */ jsx("div", {
43392
43656
  className: clsx("newsletter-form-ssr", className),
43393
43657
  children: /* @__PURE__ */ jsx("p", {
43394
43658
  role: "status",
43395
- children: "We can’t accept signups right now. Please try again later."
43659
+ children: NEWSLETTER_SIGNUPS_PAUSED_MESSAGE
43396
43660
  })
43397
43661
  });
43398
- const renderField = (field) => {
43399
- const inputId = `newsletter_${field.id}`;
43400
- return /* @__PURE__ */ jsxs("div", {
43401
- className: "fb-field newsletter-field",
43402
- children: [/* @__PURE__ */ jsxs("label", {
43403
- htmlFor: inputId,
43404
- className: "form-label",
43405
- children: [field.label, field.required && /* @__PURE__ */ jsx("span", {
43406
- className: "newsletter-required",
43407
- "aria-hidden": "true",
43408
- children: "*"
43409
- })]
43410
- }), /* @__PURE__ */ jsx("input", {
43411
- id: inputId,
43412
- name: field.id,
43413
- type: field.type,
43414
- required: field.required,
43415
- placeholder: field.placeholder,
43416
- className: "form-input",
43417
- autoComplete: field.type === "email" ? "email" : void 0
43418
- })]
43419
- }, field.id);
43420
- };
43662
+ const { email: emailField, extra: extraFields } = partitionNewsletterFields(fields);
43421
43663
  return /* @__PURE__ */ jsxs("div", {
43422
43664
  ...getIslandRootProps("newsletter-form", blockId),
43423
43665
  className: clsx("newsletter-form-ssr", className),
@@ -43436,30 +43678,20 @@ function NewsletterFormSSR(props) {
43436
43678
  /* @__PURE__ */ jsxs("div", {
43437
43679
  className: "newsletter-form-fields",
43438
43680
  children: [
43439
- fields.map(renderField),
43440
- islandProps.render.hydration.choicePresentation === "visitor_choice" ? /* @__PURE__ */ jsxs("fieldset", {
43441
- className: "fb-fieldset newsletter-topics",
43442
- children: [/* @__PURE__ */ jsx("legend", {
43443
- className: "form-label",
43444
- children: "What would you like to hear about?"
43445
- }), display.choices.map((choice) => /* @__PURE__ */ jsxs("label", {
43446
- className: "fb-inline form-label newsletter-topic-choice",
43447
- children: [/* @__PURE__ */ jsx("input", {
43448
- type: "checkbox",
43449
- name: "mailingListChoice",
43450
- value: choice.id,
43451
- className: "form-checkbox"
43452
- }), /* @__PURE__ */ jsx("span", { children: choice.label })]
43453
- }, choice.id))]
43681
+ islandProps.render.hydration.choicePresentation === "visitor_choice" ? /* @__PURE__ */ jsx(NewsletterTopicChips, { choices: display.choices }) : null,
43682
+ extraFields.length > 0 ? /* @__PURE__ */ jsx("div", {
43683
+ className: "newsletter-extra-fields",
43684
+ children: extraFields.map((field) => /* @__PURE__ */ jsx(NewsletterExtraField, { field }, field.id))
43454
43685
  }) : null,
43455
- /* @__PURE__ */ jsx("div", {
43456
- className: "newsletter-submit",
43457
- children: /* @__PURE__ */ jsx(ThemedButton, {
43458
- type: "submit",
43459
- variant: display.buttonVariant,
43460
- size: "md",
43461
- label: display.buttonLabel
43462
- })
43686
+ /* @__PURE__ */ jsx(NewsletterSubmitRow, {
43687
+ emailField,
43688
+ buttonVariant: display.buttonVariant,
43689
+ buttonLabel: display.buttonLabel,
43690
+ disabled: submissionMessage !== null
43691
+ }),
43692
+ submissionMessage && /* @__PURE__ */ jsx("p", {
43693
+ role: "status",
43694
+ children: submissionMessage
43463
43695
  })
43464
43696
  ]
43465
43697
  }),
@@ -44440,7 +44672,7 @@ const RENDERER_METADATA_PROP_KEYS = /* @__PURE__ */ new Set([
44440
44672
  "blockContent",
44441
44673
  "fragmentRegistry",
44442
44674
  "previewAuthoring",
44443
- "contentVersionId",
44675
+ "newsletterBlockSubmission",
44444
44676
  "imageResolveContext"
44445
44677
  ]);
44446
44678
  function omitRendererMetadataProps(props) {
@@ -44496,7 +44728,7 @@ function resolveNodeMetadata(nodeType, context) {
44496
44728
  pathBase: context.pathBase,
44497
44729
  fragmentRegistry: context.fragmentRegistry
44498
44730
  } : {},
44499
- ...normalizedType === "newsletter-form" ? { contentVersionId: context.contentVersionId } : {},
44731
+ ...normalizedType === "newsletter-form" ? { newsletterBlockSubmission: context.newsletterBlockSubmission } : {},
44500
44732
  ...normalizedType === "media" && context.imageResolveContext ? { imageResolveContext: context.imageResolveContext } : {}
44501
44733
  };
44502
44734
  }
@@ -45107,6 +45339,270 @@ function generateHoverEffects(axes, _space) {
45107
45339
  };
45108
45340
  }
45109
45341
  //#endregion
45342
+ //#region ../theme-core/src/inputs/checkable.ts
45343
+ /**
45344
+ * Checkbox and radio CSS shared by the full theme generator and the SDK
45345
+ * theme bridge, so both render the same customer-facing controls.
45346
+ *
45347
+ * Emits:
45348
+ * - .form-checkbox / .form-radio: the controls themselves. The tick and dot
45349
+ * are drawn with ::before so their colour comes from theme tokens rather
45350
+ * than a baked-in SVG.
45351
+ * - .form-check: a label row holding a control and its text. The control
45352
+ * lines up with the first line of text, so long labels wrap cleanly.
45353
+ * - .form-check--tile: a bordered, fully clickable panel for agreements
45354
+ * (booking consent, consent fields) that tints when ticked.
45355
+ */
45356
+ const CHECKABLE_SIZE = "1.375rem";
45357
+ /** Never fully round: a round checkbox reads as a radio. */
45358
+ const CHECKBOX_RADIUS = {
45359
+ square: "0",
45360
+ soft: "0.25rem",
45361
+ rounded: "0.375rem",
45362
+ pill: "0.375rem"
45363
+ };
45364
+ /** Tiles hold wrapping text, so pill themes get a generous radius, not a capsule. */
45365
+ const TILE_RADIUS = {
45366
+ square: "0",
45367
+ soft: "0.5rem",
45368
+ rounded: "0.75rem",
45369
+ pill: "1rem"
45370
+ };
45371
+ const ON_ACCENT_TOKENS = {
45372
+ primary: "onPrimary",
45373
+ secondary: "onSecondary",
45374
+ accent: "onAccent"
45375
+ };
45376
+ /**
45377
+ * Colour for the tick/dot drawn on top of the accent fill. Uses the palette's
45378
+ * matching "on" token where there is one, falling back to white.
45379
+ */
45380
+ /** The accent at full strength: `primary/70` becomes `primary`. */
45381
+ function solidAccentToken(accentToken) {
45382
+ const parsed = parseColorTokenParts(accentToken);
45383
+ return parsed.kind === "token" ? parsed.token : accentToken;
45384
+ }
45385
+ function checkableMarkColor(accentToken) {
45386
+ const parsed = parseColorTokenParts(accentToken);
45387
+ const onToken = parsed.kind === "token" ? ON_ACCENT_TOKENS[parsed.token] : void 0;
45388
+ return onToken ? `rgb(var(--tb-${onToken}, 255 255 255))` : "rgb(255 255 255)";
45389
+ }
45390
+ function checkableSelectors(root) {
45391
+ return {
45392
+ checkbox: `${root} .form-checkbox`,
45393
+ radio: `${root} .form-radio`,
45394
+ check: `${root} .form-check`,
45395
+ tile: `${root} .form-check--tile`
45396
+ };
45397
+ }
45398
+ /** The box and circle themselves, unchecked. */
45399
+ function controlBaseCss(sel, input) {
45400
+ const border = input.borderWidth === "0" ? "1px" : input.borderWidth;
45401
+ return [
45402
+ `${sel.checkbox},
45403
+ ${sel.radio} {
45404
+ appearance: none;
45405
+ display: inline-grid;
45406
+ place-content: center;
45407
+ width: ${CHECKABLE_SIZE};
45408
+ height: ${CHECKABLE_SIZE};
45409
+ margin: 0;
45410
+ flex-shrink: 0;
45411
+ border: ${border} solid ${input.borderColor};
45412
+ background-color: ${input.background};
45413
+ color: inherit;
45414
+ cursor: pointer;
45415
+ transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
45416
+ }`,
45417
+ `${sel.checkbox} {
45418
+ border-radius: ${CHECKBOX_RADIUS[input.corners]};
45419
+ }`,
45420
+ `${sel.radio} {
45421
+ border-radius: 9999px;
45422
+ }`
45423
+ ];
45424
+ }
45425
+ /** The tick and dot, drawn with ::before so their colour is a theme token. */
45426
+ function controlMarkCss(sel, accentToken) {
45427
+ return [
45428
+ `${sel.checkbox}::before,
45429
+ ${sel.radio}::before {
45430
+ content: "";
45431
+ background-color: ${checkableMarkColor(accentToken)};
45432
+ transform: scale(0);
45433
+ transition: transform 0.12s ease;
45434
+ }`,
45435
+ `${sel.checkbox}::before {
45436
+ width: 0.75rem;
45437
+ height: 0.75rem;
45438
+ clip-path: polygon(14% 52%, 0 67%, 38% 100%, 100% 22%, 85% 8%, 37% 70%);
45439
+ }`,
45440
+ `${sel.radio}::before {
45441
+ width: 0.5rem;
45442
+ height: 0.5rem;
45443
+ border-radius: 9999px;
45444
+ }`
45445
+ ];
45446
+ }
45447
+ function controlStateCss(sel, input) {
45448
+ const accent = rgbColorValue(solidAccentToken(input.accentToken));
45449
+ return [
45450
+ `${sel.checkbox}:hover,
45451
+ ${sel.radio}:hover,
45452
+ ${sel.check}:hover > .form-checkbox,
45453
+ ${sel.check}:hover > .form-radio {
45454
+ border-color: ${accent};
45455
+ }`,
45456
+ `${sel.checkbox}:checked,
45457
+ ${sel.radio}:checked {
45458
+ background-color: ${accent};
45459
+ border-color: ${accent};
45460
+ }`,
45461
+ `${sel.checkbox}:checked::before,
45462
+ ${sel.radio}:checked::before {
45463
+ transform: scale(1);
45464
+ }`,
45465
+ `${sel.checkbox}:focus-visible,
45466
+ ${sel.radio}:focus-visible {
45467
+ outline: 2px solid ${accent};
45468
+ outline-offset: 2px;
45469
+ }`,
45470
+ `${sel.checkbox}:disabled,
45471
+ ${sel.radio}:disabled {
45472
+ opacity: 0.5;
45473
+ cursor: not-allowed;
45474
+ }`
45475
+ ];
45476
+ }
45477
+ /** Label row: control lined up with the first line of wrapping text. */
45478
+ function checkRowCss(sel) {
45479
+ return [
45480
+ `${sel.check} {
45481
+ display: flex;
45482
+ align-items: flex-start;
45483
+ gap: 0.75rem;
45484
+ line-height: 1.5;
45485
+ cursor: pointer;
45486
+ }`,
45487
+ `${sel.check} > .form-checkbox,
45488
+ ${sel.check} > .form-radio {
45489
+ margin-top: 0.0625rem;
45490
+ margin-top: max(0px, calc((1lh - ${CHECKABLE_SIZE}) / 2));
45491
+ }`,
45492
+ `${sel.check}:has(> :disabled) {
45493
+ cursor: not-allowed;
45494
+ }`
45495
+ ];
45496
+ }
45497
+ function agreementTileCss(sel, input) {
45498
+ const accent = rgbColorValue(solidAccentToken(input.accentToken));
45499
+ return [
45500
+ `${sel.tile} {
45501
+ padding: 0.875rem 1rem;
45502
+ border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
45503
+ border-radius: ${TILE_RADIUS[input.corners]};
45504
+ transition: border-color 0.15s ease, background-color 0.15s ease;
45505
+ }`,
45506
+ `${sel.tile}:hover {
45507
+ border-color: color-mix(in srgb, ${accent} 55%, transparent);
45508
+ }`,
45509
+ `${sel.tile}:has(> .form-checkbox:checked) {
45510
+ border-color: ${accent};
45511
+ background-color: color-mix(in srgb, ${accent} 7%, transparent);
45512
+ }`,
45513
+ `${sel.tile}:has(> .form-checkbox[aria-invalid="true"]) {
45514
+ border-color: ${input.errorColor};
45515
+ }`
45516
+ ];
45517
+ }
45518
+ function checkableMediaCss(sel) {
45519
+ return [`@media (prefers-reduced-motion: reduce) {
45520
+ ${sel.checkbox}, ${sel.radio}, ${sel.checkbox}::before, ${sel.radio}::before, ${sel.tile} {
45521
+ transition: none;
45522
+ }
45523
+ }`, `@media (forced-colors: active) {
45524
+ ${sel.checkbox}::before,
45525
+ ${sel.radio}::before {
45526
+ background-color: CanvasText;
45527
+ }
45528
+ }`];
45529
+ }
45530
+ /**
45531
+ * Exposes the accent and mark colours so other choice controls (newsletter
45532
+ * topic chips) match the checkboxes without re-deriving the theme tokens.
45533
+ */
45534
+ function checkableVarsCss(input) {
45535
+ return [`${input.root} {
45536
+ --rb-check-accent: ${rgbColorValue(solidAccentToken(input.accentToken))};
45537
+ --rb-check-mark: ${checkableMarkColor(input.accentToken)};
45538
+ }`];
45539
+ }
45540
+ function generateCheckableCss(input) {
45541
+ const sel = checkableSelectors(input.root);
45542
+ return [
45543
+ ...checkableVarsCss(input),
45544
+ ...controlBaseCss(sel, input),
45545
+ ...controlMarkCss(sel, input.accentToken),
45546
+ ...controlStateCss(sel, input),
45547
+ ...checkRowCss(sel),
45548
+ ...agreementTileCss(sel, input),
45549
+ ...checkableMediaCss(sel)
45550
+ ].join("\n\n");
45551
+ }
45552
+ //#endregion
45553
+ //#region ../theme-core/src/inputs/inputGroup.ts
45554
+ /**
45555
+ * Input group: a field and an action (usually a themed button) sharing one
45556
+ * input-styled shell, e.g. the newsletter email + Sign up row.
45557
+ *
45558
+ * The shell `.form-input-group` takes the theme's `.form-input` rules (the
45559
+ * generators add it to that selector list, plus focus and error), so it
45560
+ * follows the site's input background, border, corners and shadow. This file
45561
+ * only adds the layout overrides and the borderless inner field.
45562
+ */
45563
+ const INPUT_GROUP_SELECTOR = ".form-input-group";
45564
+ /** Shell focus ring follows the field only, not the button beside it. */
45565
+ const INPUT_GROUP_FOCUS_SELECTOR = ".form-input-group:has(> .form-input-group-field:focus-visible)";
45566
+ const INPUT_GROUP_ERROR_SELECTOR = ".form-input-group:has(> .form-input-group-field[aria-invalid=\"true\"])";
45567
+ const GROUP_INSET = "0.25rem";
45568
+ /**
45569
+ * @param root - scoped root selector
45570
+ * @param padding - the theme's input padding shorthand (`block inline`), so the
45571
+ * inner field's text sits where a plain input's text would.
45572
+ */
45573
+ function generateInputGroupCss(root, padding) {
45574
+ const [block = padding, inline = block] = padding.trim().split(/\s+/);
45575
+ return `${root} .form-input-group {
45576
+ display: flex;
45577
+ align-items: stretch;
45578
+ gap: ${GROUP_INSET};
45579
+ padding: ${GROUP_INSET};
45580
+ }
45581
+
45582
+ ${root} .form-input-group-field {
45583
+ flex: 1 1 auto;
45584
+ min-width: 0;
45585
+ margin: 0;
45586
+ border: 0;
45587
+ border-radius: 0;
45588
+ background: transparent;
45589
+ box-shadow: none;
45590
+ outline: none;
45591
+ color: inherit;
45592
+ font: inherit;
45593
+ padding: calc(${block} - ${GROUP_INSET}) calc(${inline} - ${GROUP_INSET});
45594
+ }
45595
+
45596
+ ${root} .form-input-group-field::placeholder {
45597
+ color: inherit;
45598
+ opacity: 0.6;
45599
+ }
45600
+
45601
+ ${root} .form-input-group > :not(.form-input-group-field) {
45602
+ flex-shrink: 0;
45603
+ }`;
45604
+ }
45605
+ //#endregion
45110
45606
  //#region ../theme-core/src/inputs/generateInputCss.ts
45111
45607
  /**
45112
45608
  * Corner radius map (reused from other systems)
@@ -45117,6 +45613,9 @@ const CORNER_MAP$1 = {
45117
45613
  rounded: "1rem",
45118
45614
  pill: "9999px"
45119
45615
  };
45616
+ function inputBackgroundValue(background) {
45617
+ return background.type === "transparent" ? "transparent" : rgbColorValue(background.colorToken);
45618
+ }
45120
45619
  /**
45121
45620
  * Generate CSS for form input styling based on theme configuration
45122
45621
  *
@@ -45134,8 +45633,7 @@ const CORNER_MAP$1 = {
45134
45633
  function generateInputCss({ themeId, theme, inputSystem }) {
45135
45634
  const rootSelector = `:where([data-theme-scope="${themeId}"])`;
45136
45635
  const baseInputRules = [];
45137
- if (inputSystem.base.background.type === "transparent") baseInputRules.push("background-color: transparent");
45138
- else baseInputRules.push(`background-color: ${rgbColorValue(inputSystem.base.background.colorToken)}`);
45636
+ baseInputRules.push(`background-color: ${inputBackgroundValue(inputSystem.base.background)}`);
45139
45637
  baseInputRules.push(`color: ${rgbColorValue(inputSystem.base.textColorToken)}`);
45140
45638
  const borderPx = parseBorderWidth(inputSystem.base.border.widthClass);
45141
45639
  const borderStyle = inputSystem.base.border.style ?? "all";
@@ -45191,7 +45689,8 @@ function generateInputCss({ themeId, theme, inputSystem }) {
45191
45689
  const focusSelectors = [
45192
45690
  `${inputSelector}:focus-visible`,
45193
45691
  `${textareaSelector}:focus-visible`,
45194
- `${selectSelector}:focus-visible`
45692
+ `${selectSelector}:focus-visible`,
45693
+ `${rootSelector} ${INPUT_GROUP_FOCUS_SELECTOR}`
45195
45694
  ];
45196
45695
  const focusRules = [];
45197
45696
  focusRules.push("outline: none");
@@ -45200,7 +45699,8 @@ function generateInputCss({ themeId, theme, inputSystem }) {
45200
45699
  const errorSelectors = [
45201
45700
  `${inputSelector}[aria-invalid="true"]`,
45202
45701
  `${textareaSelector}[aria-invalid="true"]`,
45203
- `${selectSelector}[aria-invalid="true"]`
45702
+ `${selectSelector}[aria-invalid="true"]`,
45703
+ `${rootSelector} ${INPUT_GROUP_ERROR_SELECTOR}`
45204
45704
  ];
45205
45705
  const errorRules = [];
45206
45706
  errorRules.push(`border-color: ${rgbColorValue(inputSystem.error.borderColorToken)}`);
@@ -45215,53 +45715,25 @@ function generateInputCss({ themeId, theme, inputSystem }) {
45215
45715
  disabledRules.push(`color: ${rgbColorValue(inputSystem.disabled.textColorToken)}`);
45216
45716
  disabledRules.push(`opacity: ${inputSystem.disabled.opacity}`);
45217
45717
  disabledRules.push("cursor: not-allowed");
45218
- const checkboxSelector = `${rootSelector} .form-checkbox`;
45219
- const radioSelector = `${rootSelector} .form-radio`;
45220
- const checkableRules = [];
45221
- checkableRules.push("appearance: none");
45222
- checkableRules.push("width: 1.125rem");
45223
- checkableRules.push("height: 1.125rem");
45224
- checkableRules.push("flex-shrink: 0");
45225
- checkableRules.push(`border: ${borderPx} solid ${rgbColorValue(inputSystem.base.border.colorToken)}`);
45226
- if (inputSystem.base.background.type === "transparent") checkableRules.push("background-color: transparent");
45227
- else checkableRules.push(`background-color: ${rgbColorValue(inputSystem.base.background.colorToken)}`);
45228
- checkableRules.push("cursor: pointer");
45229
- checkableRules.push("transition: background-color 0.2s ease, border-color 0.2s ease");
45230
- const checkboxSpecificRules = [...checkableRules];
45231
- checkboxSpecificRules.push(`border-radius: ${CORNER_MAP$1.soft}`);
45232
- const radioSpecificRules = [...checkableRules];
45233
- radioSpecificRules.push("border-radius: 9999px");
45234
- const checkboxCheckedSelector = `${checkboxSelector}:checked`;
45235
- const checkboxCheckedRules = [];
45236
- checkboxCheckedRules.push(`background-color: ${rgbColorValue(inputSystem.focus.ringColorToken)}`);
45237
- checkboxCheckedRules.push(`border-color: ${rgbColorValue(inputSystem.focus.ringColorToken)}`);
45238
- checkboxCheckedRules.push("background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e\")");
45239
- checkboxCheckedRules.push("background-size: 100% 100%");
45240
- checkboxCheckedRules.push("background-position: center");
45241
- checkboxCheckedRules.push("background-repeat: no-repeat");
45242
- const radioCheckedSelector = `${radioSelector}:checked`;
45243
- const radioCheckedRules = [];
45244
- radioCheckedRules.push(`background-color: ${rgbColorValue(inputSystem.focus.ringColorToken)}`);
45245
- radioCheckedRules.push(`border-color: ${rgbColorValue(inputSystem.focus.ringColorToken)}`);
45246
- radioCheckedRules.push("background-image: radial-gradient(circle, white 40%, transparent 40%)");
45247
- const checkboxFocusSelector = `${checkboxSelector}:focus-visible`;
45248
- const radioFocusSelector = `${radioSelector}:focus-visible`;
45249
- const checkableFocusRules = [];
45250
- checkableFocusRules.push("outline: none");
45251
- checkableFocusRules.push(`box-shadow: 0 0 0 ${inputSystem.focus.ringWidth}px ${rgbColorValue(inputSystem.focus.ringColorToken)}`);
45718
+ const checkableCss = generateCheckableCss({
45719
+ root: rootSelector,
45720
+ borderWidth: borderPx,
45721
+ borderColor: rgbColorValue(inputSystem.base.border.colorToken),
45722
+ background: inputBackgroundValue(inputSystem.base.background),
45723
+ corners: inputSystem.base.corners ?? theme.corners,
45724
+ accentToken: inputSystem.focus.borderColorToken ?? inputSystem.focus.ringColorToken,
45725
+ errorColor: rgbColorValue(inputSystem.error.borderColorToken)
45726
+ });
45252
45727
  const cssBlocks = [];
45253
45728
  cssBlocks.push(`${labelSelector} {\n ${labelRules.join(";\n ")};\n}`);
45254
- cssBlocks.push(`${inputSelector} {\n ${inputRules.join(";\n ")};\n}`);
45729
+ cssBlocks.push(`${inputSelector},\n${rootSelector} ${INPUT_GROUP_SELECTOR} {\n ${inputRules.join(";\n ")};\n}`);
45255
45730
  cssBlocks.push(`${textareaSelector} {\n ${textareaRules.join(";\n ")};\n}`);
45256
45731
  cssBlocks.push(`${selectSelector} {\n ${selectRules.join(";\n ")};\n}`);
45257
45732
  cssBlocks.push(`${focusSelectors.join(",\n")} {\n ${focusRules.join(";\n ")};\n}`);
45258
45733
  cssBlocks.push(`${errorSelectors.join(",\n")} {\n ${errorRules.join(";\n ")};\n}`);
45259
45734
  cssBlocks.push(`${disabledSelectors.join(",\n")} {\n ${disabledRules.join(";\n ")};\n}`);
45260
- cssBlocks.push(`${checkboxSelector} {\n ${checkboxSpecificRules.join(";\n ")};\n}`);
45261
- cssBlocks.push(`${radioSelector} {\n ${radioSpecificRules.join(";\n ")};\n}`);
45262
- cssBlocks.push(`${checkboxCheckedSelector} {\n ${checkboxCheckedRules.join(";\n ")};\n}`);
45263
- cssBlocks.push(`${radioCheckedSelector} {\n ${radioCheckedRules.join(";\n ")};\n}`);
45264
- cssBlocks.push(`${checkboxFocusSelector},\n${radioFocusSelector} {\n ${checkableFocusRules.join(";\n ")};\n}`);
45735
+ cssBlocks.push(generateInputGroupCss(rootSelector, padding));
45736
+ cssBlocks.push(checkableCss);
45265
45737
  return cssBlocks.join("\n\n");
45266
45738
  }
45267
45739
  //#endregion
@@ -51675,13 +52147,13 @@ const hours = stack({
51675
52147
  as: "p",
51676
52148
  className: "rb-caption rb-opacity-70"
51677
52149
  }, bind("module.timeZone"), when("module.timeZone"))], when("module.kind", { equals: "opening-hours" }));
51678
- const socials = inline({ className: "rb-flex rb-flex-wrap rb-gap-x-5 rb-gap-y-3" }, [link({
52150
+ const socials = inline({ className: "rb-footer-socials" }, [link({
51679
52151
  href: { $bind: { from: "social.href" } },
51680
52152
  target: "_blank",
51681
52153
  rel: "noreferrer noopener",
51682
52154
  "aria-label": { $bind: { from: "social.label" } },
51683
- className: "rb-nav-text transition-theme-standard"
51684
- }, [icon({ className: "rb-h-5 rb-w-5" }, bind("social.icon"), when("social.icon")), text({ as: "span" }, bind("social.label"), when("social.icon", { equals: null }))], repeat("module.socialLinks", "social"))], when("module.kind", { equals: "social-links" }));
52155
+ className: "rb-footer-social-link rb-nav-text transition-theme-standard"
52156
+ }, [icon({ className: "rb-footer-social-icon" }, bind("social.icon"), when("social.icon")), text({ as: "span" }, bind("social.label"), when("social.icon", { equals: null }))], repeat("module.socialLinks", "social"))], when("module.kind", { equals: "social-links" }));
51685
52157
  const authoredCopy = richText({
51686
52158
  className: "rb-prose rb-prose-sm rb-max-w-none",
51687
52159
  style: textColorStyle("text/80")
@@ -57884,43 +58356,53 @@ function collectNestedPreviewFieldConstraintEntries(field, path) {
57884
58356
  default: return assertNever$4(field);
57885
58357
  }
57886
58358
  }
58359
+ function prepareDefaultBlockRender(manifest, props) {
58360
+ if (!props.theme) throw new Error("Block rendering requires theme tokens; none were provided to makeDefaultBlockComponent");
58361
+ if (!props.registry) throw new Error("Block rendering requires a component registry; none was provided");
58362
+ const content = props.content;
58363
+ const layout = resolveBlockLayout(manifest, content);
58364
+ if (!layout) throw new Error(`Block manifest "${manifest.name}" requires a layout definition`);
58365
+ const mergedContent = mergeContentWithBlockStyleOverrides(content, props.themeConfig, props.blockKind ?? manifest.name);
58366
+ return {
58367
+ registry: props.registry,
58368
+ layout,
58369
+ mergedContent,
58370
+ fieldConstraints: buildPreviewFieldConstraints(manifest),
58371
+ viewModel: buildBlockViewModel({
58372
+ mergedContent,
58373
+ data: props.data,
58374
+ resolvedThemeConfig: resolveThemeConfigForBindings(props.themeConfig),
58375
+ viewModelOverrides: props.viewModelOverrides
58376
+ }),
58377
+ themeForTransforms: withButtonSystem(props.theme, props.themeConfig)
58378
+ };
58379
+ }
57887
58380
  function makeDefaultBlockComponent(args) {
57888
- const { manifest } = args;
57889
- return function DefaultBlockComponent({ content, data, registry, theme, themeConfig, transforms, viewModelOverrides, pathBase, blockId, contentVersionId, blockKind, blockBindings, editableRouteMetadataFieldIds, fragmentRegistry, imageResolveContext, previewAuthoring }) {
58381
+ return function DefaultBlockComponent(props) {
57890
58382
  const perf = beginBlockRenderMeasurement();
57891
- if (!theme) throw new Error("Block rendering requires theme tokens; none were provided to makeDefaultBlockComponent");
57892
- if (!registry) throw new Error("Block rendering requires a component registry; none was provided");
57893
- const layout = resolveBlockLayout(manifest, content);
57894
- if (!layout) throw new Error(`Block manifest "${manifest.name}" requires a layout definition`);
57895
- const fieldConstraints = buildPreviewFieldConstraints(manifest);
57896
- const mergedContent = mergeContentWithBlockStyleOverrides(content, themeConfig, blockKind ?? manifest.name);
57897
- const result = resolveTree(layout, {
57898
- registry,
57899
- viewModel: buildBlockViewModel({
57900
- mergedContent,
57901
- data,
57902
- resolvedThemeConfig: resolveThemeConfigForBindings(themeConfig),
57903
- viewModelOverrides
57904
- }),
57905
- transforms,
57906
- theme: withButtonSystem(theme, themeConfig),
57907
- pathBase,
57908
- blockId,
57909
- contentVersionId,
57910
- blockKind,
57911
- blockContent: mergedContent,
57912
- blockBindings,
57913
- editableRouteMetadataFieldIds,
57914
- fragmentRegistry,
57915
- fieldConstraints,
57916
- imageResolveContext,
57917
- previewAuthoring
58383
+ const prepared = prepareDefaultBlockRender(args.manifest, props);
58384
+ const result = resolveTree(prepared.layout, {
58385
+ registry: prepared.registry,
58386
+ viewModel: prepared.viewModel,
58387
+ transforms: props.transforms,
58388
+ theme: prepared.themeForTransforms,
58389
+ pathBase: props.pathBase,
58390
+ blockId: props.blockId,
58391
+ newsletterBlockSubmission: props.newsletterBlockSubmission,
58392
+ blockKind: props.blockKind,
58393
+ blockContent: prepared.mergedContent,
58394
+ blockBindings: props.blockBindings,
58395
+ editableRouteMetadataFieldIds: props.editableRouteMetadataFieldIds,
58396
+ fragmentRegistry: props.fragmentRegistry,
58397
+ fieldConstraints: prepared.fieldConstraints,
58398
+ imageResolveContext: props.imageResolveContext,
58399
+ previewAuthoring: props.previewAuthoring
57918
58400
  });
57919
58401
  reportSlowBlockRender(perf, {
57920
- content,
57921
- blockId,
57922
- blockKind: blockKind ?? manifest.name,
57923
- manifestName: manifest.name
58402
+ content: props.content,
58403
+ blockId: props.blockId,
58404
+ blockKind: props.blockKind ?? args.manifest.name,
58405
+ manifestName: args.manifest.name
57924
58406
  });
57925
58407
  return result;
57926
58408
  };
@@ -58437,6 +58919,39 @@ function resolveBlockData(blockId, dataLoaders, dataContext, routes) {
58437
58919
  return dataLoaders && typeof dataLoaders === "object" && Object.keys(dataLoaders).length > 0 ? {} : void 0;
58438
58920
  }
58439
58921
  //#endregion
58922
+ //#region ../blocks/src/system/runtime/nodes/newsletter-form.block-submission.ts
58923
+ /** Authorise one block source for both public and preview rendering. */
58924
+ function planNewsletterBlockSubmission(placement) {
58925
+ if (placement.stage === "published") {
58926
+ const sourceRef = buildMailingListSignupBlockSourceRef(placement.blockId, placement.selectedContentVersionId);
58927
+ return sourceRef ? {
58928
+ tag: "ready",
58929
+ sourceRef
58930
+ } : { tag: "unavailable" };
58931
+ }
58932
+ if (placement.publishedPlacement) return planNewsletterPreviewSubmission({
58933
+ blockId: placement.blockId,
58934
+ publishedContentId: placement.publishedPlacement.id,
58935
+ renderedContent: placement.renderedContent,
58936
+ publishedContent: placement.publishedPlacement.content
58937
+ });
58938
+ if (placement.stage === "preview") return { tag: "unpublished-form" };
58939
+ return { tag: "unavailable" };
58940
+ }
58941
+ /** Decide whether the visible draft can safely cite the published placement. */
58942
+ function planNewsletterPreviewSubmission(placement) {
58943
+ if (!placement.publishedContentId) return { tag: "unpublished-form" };
58944
+ if (!placement.publishedContent) return { tag: "unpublished-settings" };
58945
+ const draft = parseMailingListPlacementTargeting(placement.renderedContent.targeting ?? { mode: "default" });
58946
+ const published = parseMailingListPlacementTargeting(placement.publishedContent.targeting);
58947
+ if (!draft.ok || !published.ok || !sameMailingListPlacementTargeting(draft.value, published.value)) return { tag: "unpublished-settings" };
58948
+ const sourceRef = buildMailingListSignupBlockSourceRef(placement.blockId, placement.publishedContentId);
58949
+ return sourceRef ? {
58950
+ tag: "ready",
58951
+ sourceRef
58952
+ } : { tag: "unpublished-settings" };
58953
+ }
58954
+ //#endregion
58440
58955
  //#region ../blocks/src/pageRenderPreparation.ts
58441
58956
  /**
58442
58957
  * Stage two of the render plan.
@@ -58565,9 +59080,20 @@ function buildBlockOverrideProps(input) {
58565
59080
  imageResolveContext: input.imageResolveContext,
58566
59081
  blockId: block.id ?? null,
58567
59082
  contentVersionId: block.contentVersionId ?? null,
59083
+ ...newsletterBlockProps(block, input.content, dataContext?.previewStage),
58568
59084
  blockKind: block.kind
58569
59085
  };
58570
59086
  }
59087
+ function newsletterBlockProps(block, renderedContent, stage) {
59088
+ if (block.kind !== "block.newsletter-signup") return {};
59089
+ return { newsletterBlockSubmission: planNewsletterBlockSubmission({
59090
+ blockId: block.id,
59091
+ selectedContentVersionId: block.contentVersionId ?? null,
59092
+ publishedPlacement: block.publishedPlacement ?? null,
59093
+ renderedContent,
59094
+ stage
59095
+ }) };
59096
+ }
58571
59097
  /** Site, transport and session context a block needs to call back to the API. */
58572
59098
  function blockSessionProps(dataContext) {
58573
59099
  return {
@@ -58657,6 +59183,15 @@ function createBlockErrorBoundary() {
58657
59183
  const BlockErrorBoundary = createBlockErrorBoundary();
58658
59184
  //#endregion
58659
59185
  //#region ../blocks/src/pageRenderAdmission.ts
59186
+ /** Pair a published version with the content it authorises at the render boundary. */
59187
+ function readPublishedBlockPlacement(id, content) {
59188
+ if (id == null && content == null) return null;
59189
+ if (id == null || content == null) throw new Error("Published block placement requires both an ID and content");
59190
+ return {
59191
+ id,
59192
+ content
59193
+ };
59194
+ }
58660
59195
  function toPresentationPage(page) {
58661
59196
  if (!page) return null;
58662
59197
  return {
@@ -58667,6 +59202,8 @@ function toPresentationPage(page) {
58667
59202
  kind: block.kind,
58668
59203
  purpose: block.purpose,
58669
59204
  ...block.contentVersionId ? { contentVersionId: block.contentVersionId } : {},
59205
+ ...block.draftContentId !== void 0 ? { draftContentId: block.draftContentId } : {},
59206
+ ...block.publishedPlacement !== void 0 ? { publishedPlacement: block.publishedPlacement } : {},
58670
59207
  ...block.bindings ? { bindings: block.bindings } : {}
58671
59208
  };
58672
59209
  return block.admission.kind === "renderable" ? {
@@ -59394,6 +59931,9 @@ const containerResponsiveThemeCss = `/*
59394
59931
  .rb-h-4 {
59395
59932
  height: 1rem;
59396
59933
  }
59934
+ .rb-h-5 {
59935
+ height: 1.25rem;
59936
+ }
59397
59937
  .rb-h-8 {
59398
59938
  height: 2rem;
59399
59939
  }
@@ -63359,6 +63899,10 @@ const containerResponsiveThemeCss = `/*
63359
63899
  margin-bottom: 0.25rem;
63360
63900
  }
63361
63901
 
63902
+ :where(.theme-scope) :where([data-block='event-registration']) .er-consent-check {
63903
+ margin-top: 0.75rem;
63904
+ }
63905
+
63362
63906
  :where(.theme-scope) :where([data-block='event-registration']) .er-required {
63363
63907
  color: var(--er-required);
63364
63908
  }
@@ -68078,13 +68622,6 @@ const containerResponsiveThemeCss = `/*
68078
68622
  margin: 0;
68079
68623
  }
68080
68624
 
68081
- /* Inline field (checkbox, radio with label) */
68082
- :where(.theme-scope) :where([data-block="form"]) .fb-inline {
68083
- display: flex;
68084
- align-items: center;
68085
- gap: 0.5rem;
68086
- }
68087
-
68088
68625
  /* Success state */
68089
68626
  :where(.theme-scope) :where([data-block="form"]) .fb-success {
68090
68627
  background-color: var(--fb-success-bg);
@@ -70201,9 +70738,11 @@ const containerResponsiveThemeCss = `/*
70201
70738
  */
70202
70739
 
70203
70740
  /* Newsletter copy styling */
70741
+ /* Size only: weight, family and colour come from the theme's heading styles */
70204
70742
  :where(.theme-scope) :where([data-block="newsletter-signup"]) .newsletter-copy-title {
70205
- font-size: 1.5rem;
70206
- font-weight: 600;
70743
+ font-size: clamp(1.75rem, 1.35rem + 1.6vw, 2.5rem);
70744
+ line-height: 1.15;
70745
+ text-wrap: balance;
70207
70746
  }
70208
70747
 
70209
70748
  :where(.theme-scope) :where([data-block="newsletter-signup"]) .newsletter-copy-subtitle {
@@ -70227,37 +70766,150 @@ const containerResponsiveThemeCss = `/*
70227
70766
  gap: var(--rb-gap-sm, 0.75rem);
70228
70767
  }
70229
70768
 
70230
- /* Individual field styling */
70769
+ /* Extra preset fields (name, company...). Labels are visually hidden, so
70770
+ these sit in a row above the email entry. */
70771
+ :where(.theme-scope) .newsletter-extra-fields {
70772
+ display: grid;
70773
+ grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
70774
+ gap: 0.5rem;
70775
+ }
70776
+
70231
70777
  :where(.theme-scope) .newsletter-field {
70232
70778
  display: flex;
70233
70779
  flex-direction: column;
70234
70780
  gap: 0.25rem;
70781
+ text-align: left;
70235
70782
  }
70236
70783
 
70237
- /* Topic choices (visitor picks what to hear about). Same treatment as the
70238
- form block's .fb-fieldset / .fb-inline, defined here because the newsletter
70239
- form also renders inside contact-newsletter and the site footer. */
70240
- :where(.theme-scope) .newsletter-topics {
70784
+ /* Email entry: the email field and the themed submit button share one shell.
70785
+ The shell's look (background, border, corners, focus ring) comes from the
70786
+ theme's input system via .form-input-group; this only handles layout. */
70787
+ :where(.theme-scope) .newsletter-entry {
70788
+ container-type: inline-size;
70241
70789
  display: flex;
70242
70790
  flex-direction: column;
70243
- gap: 0.5rem;
70791
+ gap: 0.375rem;
70792
+ text-align: left;
70793
+ }
70794
+
70795
+ :where(.theme-scope) .newsletter-entry-group > button {
70796
+ white-space: nowrap;
70797
+ }
70798
+
70799
+ /* Too narrow for field and button side by side (phones, footer columns):
70800
+ stack them inside the shell and keep pill themes from turning the taller
70801
+ shell into a capsule. */
70802
+ @container (max-width: 22rem) {
70803
+ :where(.theme-scope) .newsletter-entry .newsletter-entry-group {
70804
+ flex-direction: column;
70805
+ border-radius: min(var(--rb-input-radius, 0.5rem), 1rem);
70806
+ }
70807
+
70808
+ :where(.theme-scope) .newsletter-entry .newsletter-entry-group > button {
70809
+ width: 100%;
70810
+ justify-content: center;
70811
+ }
70812
+ }
70813
+
70814
+ /* Topic choices (visitor picks what to hear about) as toggle chips. Each chip
70815
+ is a real checkbox underneath; the chip is its visible face. */
70816
+ :where(.theme-scope) .newsletter-topics {
70244
70817
  border: none;
70245
70818
  padding: 0;
70246
70819
  margin: 0;
70247
70820
  min-width: 0;
70248
70821
  }
70249
70822
 
70250
- :where(.theme-scope) .newsletter-topics > legend {
70823
+ :where(.theme-scope) .newsletter-topics-legend {
70251
70824
  padding: 0;
70252
- margin-bottom: 0.25rem;
70825
+ margin-bottom: 0.625rem;
70826
+ font-size: 0.9375rem;
70827
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
70253
70828
  }
70254
70829
 
70255
- :where(.theme-scope) .newsletter-topic-choice {
70830
+ :where(.theme-scope) .newsletter-topic-chips {
70256
70831
  display: flex;
70257
- align-items: center;
70832
+ flex-wrap: wrap;
70258
70833
  gap: 0.5rem;
70259
70834
  }
70260
70835
 
70836
+ :where(.theme-scope) .newsletter-stacked .newsletter-topics-legend {
70837
+ width: 100%;
70838
+ }
70839
+
70840
+ :where(.theme-scope) .newsletter-stacked .newsletter-topic-chips {
70841
+ justify-content: center;
70842
+ }
70843
+
70844
+ :where(.theme-scope) .newsletter-topic-chip {
70845
+ position: relative;
70846
+ display: inline-flex;
70847
+ }
70848
+
70849
+ :where(.theme-scope) .newsletter-topic-chip > input {
70850
+ position: absolute;
70851
+ inset: 0;
70852
+ width: 100%;
70853
+ height: 100%;
70854
+ margin: 0;
70855
+ opacity: 0;
70856
+ cursor: pointer;
70857
+ }
70858
+
70859
+ :where(.theme-scope) .newsletter-topic-chip > span {
70860
+ display: inline-flex;
70861
+ align-items: center;
70862
+ gap: 0.375rem;
70863
+ padding: 0.4375rem 0.9375rem;
70864
+ border: 1px solid color-mix(in srgb, currentColor 35%, transparent);
70865
+ border-radius: var(--rb-button-radius, var(--rb-input-radius, 9999px));
70866
+ font-size: 0.9375rem;
70867
+ line-height: 1.3;
70868
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
70869
+ transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
70870
+ }
70871
+
70872
+ /* Tick that grows in when the chip is on */
70873
+ :where(.theme-scope) .newsletter-topic-chip > span::before {
70874
+ content: "";
70875
+ width: 0;
70876
+ height: 0.7em;
70877
+ background-color: currentColor;
70878
+ clip-path: polygon(14% 52%, 0 67%, 38% 100%, 100% 22%, 85% 8%, 37% 70%);
70879
+ transition: width 0.15s ease;
70880
+ }
70881
+
70882
+ :where(.theme-scope) .newsletter-topic-chip:hover > span {
70883
+ border-color: currentColor;
70884
+ }
70885
+
70886
+ /* Same accent and mark colours as the theme's checkboxes */
70887
+ :where(.theme-scope) .newsletter-topic-chip > input:checked + span {
70888
+ background-color: var(--rb-check-accent, rgb(var(--tb-primary)));
70889
+ border-color: var(--rb-check-accent, rgb(var(--tb-primary)));
70890
+ color: var(--rb-check-mark, rgb(var(--tb-onPrimary, 255 255 255)));
70891
+ }
70892
+
70893
+ :where(.theme-scope) .newsletter-topic-chip > input:checked + span::before {
70894
+ width: 0.7em;
70895
+ }
70896
+
70897
+ :where(.theme-scope) .newsletter-topic-chip > input:focus-visible + span {
70898
+ outline: 2px solid var(--rb-check-accent, rgb(var(--tb-primary)));
70899
+ outline-offset: 2px;
70900
+ }
70901
+
70902
+ :where(.theme-scope) .newsletter-topic-chip > input:disabled + span {
70903
+ opacity: 0.6;
70904
+ }
70905
+
70906
+ @media (prefers-reduced-motion: reduce) {
70907
+ :where(.theme-scope) .newsletter-topic-chip > span,
70908
+ :where(.theme-scope) .newsletter-topic-chip > span::before {
70909
+ transition: none;
70910
+ }
70911
+ }
70912
+
70261
70913
  /* Error panel and field notes. The form block scopes these to its own
70262
70914
  data-block, so the newsletter form (which also renders in the footer)
70263
70915
  needs its own copy of the treatment. */
@@ -70275,13 +70927,7 @@ const containerResponsiveThemeCss = `/*
70275
70927
  color: var(--fb-error-text);
70276
70928
  }
70277
70929
 
70278
- /* Required indicator */
70279
- :where(.theme-scope) .newsletter-required {
70280
- color: rgb(var(--tb-primary));
70281
- margin-left: 0.125rem;
70282
- }
70283
-
70284
- /* Submit button container */
70930
+ /* Submit button container (only when a preset has no email field) */
70285
70931
  :where(.theme-scope) .newsletter-submit {
70286
70932
  margin-top: 0.5rem;
70287
70933
  }
@@ -70293,52 +70939,29 @@ const containerResponsiveThemeCss = `/*
70293
70939
  justify-content: center;
70294
70940
  }
70295
70941
 
70296
- /* Privacy note */
70942
+ /* Privacy note: quieter than the intro so the entry row leads */
70297
70943
  :where(.theme-scope) .newsletter-privacy-note {
70298
70944
  margin-top: 0.75rem;
70299
- font-size: 1rem;
70300
- line-height: 1.6;
70301
- color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
70945
+ font-size: 0.8125rem;
70946
+ line-height: 1.5;
70947
+ color: var(--rb-surface-supporting-color, var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39))));
70948
+ opacity: 0.85;
70302
70949
  text-align: center;
70303
70950
  }
70304
70951
 
70305
70952
  /* =============================================================================
70306
- * Inline Variant - Horizontal layout
70953
+ * Inline Variant - left-aligned, wider entry row
70307
70954
  * =============================================================================
70308
70955
  */
70309
70956
 
70310
- :where(.theme-scope) .newsletter-inline .newsletter-form-fields {
70311
- flex-direction: row;
70312
- align-items: flex-end;
70313
- flex-wrap: wrap;
70314
- gap: var(--rb-gap-sm, 0.75rem);
70315
- }
70316
-
70317
- :where(.theme-scope) .newsletter-inline .newsletter-field {
70318
- flex: 1 1 auto;
70319
- min-width: 10rem;
70320
- }
70321
-
70322
- :where(.theme-scope) .newsletter-inline .newsletter-submit {
70323
- margin-top: 0;
70324
- flex-shrink: 0;
70325
- }
70326
-
70327
- :where(.theme-scope) .newsletter-inline .newsletter-topics {
70328
- flex-basis: 100%;
70957
+ :where(.theme-scope) .newsletter-inline .newsletter-form {
70958
+ max-width: 40rem;
70959
+ margin-left: 0;
70960
+ margin-right: 0;
70329
70961
  }
70330
70962
 
70331
- /* Mobile fallback for inline variant */
70332
- @container rb-site (max-width: 640px) {
70333
- @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
70334
- :where(.theme-scope) .newsletter-inline .newsletter-form-fields {
70335
- flex-direction: column;
70336
- }
70337
-
70338
- :where(.theme-scope) .newsletter-inline .newsletter-field {
70339
- min-width: 100%;
70340
- }
70341
- }
70963
+ :where(.theme-scope) .newsletter-inline .newsletter-privacy-note {
70964
+ text-align: left;
70342
70965
  }
70343
70966
 
70344
70967
  /* =============================================================================
@@ -70476,12 +71099,6 @@ const containerResponsiveThemeCss = `/*
70476
71099
  margin: 0;
70477
71100
  }
70478
71101
 
70479
- :where(.theme-scope) :where([data-block="contact-newsletter"]) .fb-inline {
70480
- display: flex;
70481
- align-items: center;
70482
- gap: 0.5rem;
70483
- }
70484
-
70485
71102
  :where(.theme-scope) :where([data-block="contact-newsletter"]) .fb-success,
70486
71103
  :where(.theme-scope) :where([data-block="contact-newsletter"]) .fb-error,
70487
71104
  :where(.theme-scope) :where([data-block="contact-newsletter"]) .fb-empty {
@@ -72378,6 +72995,44 @@ const containerResponsiveThemeCss = `/*
72378
72995
  flex: 0 0 auto;
72379
72996
  }
72380
72997
  }
72998
+ }
72999
+
73000
+ /* =============================================================================
73001
+ * Site footer: social links
73002
+ *
73003
+ * Icon links get a 40px tap target with a hairline ring drawn from the link's
73004
+ * own colour, so it follows the footer's text colour on any surface. Links
73005
+ * without an icon (unknown platforms) stay plain text.
73006
+ * =============================================================================
73007
+ */
73008
+
73009
+ :where(.theme-scope) .rb-footer-socials {
73010
+ display: flex;
73011
+ flex-wrap: wrap;
73012
+ align-items: center;
73013
+ gap: 0.75rem 1.25rem;
73014
+ }
73015
+
73016
+ :where(.theme-scope) .rb-footer-social-link:has(> .rb-footer-social-icon) {
73017
+ display: inline-grid;
73018
+ place-items: center;
73019
+ width: 2.5rem;
73020
+ height: 2.5rem;
73021
+ border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
73022
+ border-radius: 9999px;
73023
+ }
73024
+
73025
+ :where(.theme-scope) .rb-footer-socials:has(.rb-footer-social-icon) {
73026
+ gap: 0.5rem;
73027
+ }
73028
+
73029
+ :where(.theme-scope) .rb-footer-social-link:has(> .rb-footer-social-icon):hover {
73030
+ border-color: currentColor;
73031
+ }
73032
+
73033
+ :where(.theme-scope) .rb-footer-social-icon {
73034
+ width: 1.5rem;
73035
+ height: 1.5rem;
72381
73036
  }`;
72382
73037
  //#endregion
72383
73038
  //#region ../theme-core/src/ThemeScopeCore.tsx
@@ -72681,7 +73336,7 @@ function toBlocksCommonProps(props) {
72681
73336
  return {
72682
73337
  page: props.page,
72683
73338
  runtimeJoins: props.runtimeJoins,
72684
- dataContext: props.dataContext ? toBlocksPageRendererDataContext(props.dataContext) : void 0,
73339
+ dataContext: toBlocksPageRendererDataContext(props.dataContext),
72685
73340
  routeMap: props.routeMap,
72686
73341
  wrapBlock: props.wrapBlock,
72687
73342
  registry: props.registry,
@@ -72772,14 +73427,14 @@ function resolveSdkBookingTimeZone(timeZone) {
72772
73427
  }
72773
73428
  function buildPageRouteRenderContext(dataContext) {
72774
73429
  return {
72775
- pathname: dataContext?.pathname,
72776
- occurrenceContext: dataContext?.occurrenceContext ?? null,
72777
- entrySubrouteContext: dataContext?.entrySubrouteContext ?? null,
72778
- contentEntry: dataContext?.contentEntry ?? null,
72779
- pageSurface: dataContext?.pageSurface ?? null,
72780
- subject: dataContext?.subject ?? null,
72781
- editableRouteMetadataFieldIds: dataContext?.editableRouteMetadataFieldIds ?? null,
72782
- previewStage: dataContext?.previewStage
73430
+ pathname: dataContext.pathname,
73431
+ occurrenceContext: dataContext.occurrenceContext ?? null,
73432
+ entrySubrouteContext: dataContext.entrySubrouteContext ?? null,
73433
+ contentEntry: dataContext.contentEntry ?? null,
73434
+ pageSurface: dataContext.pageSurface ?? null,
73435
+ subject: dataContext.subject ?? null,
73436
+ editableRouteMetadataFieldIds: dataContext.editableRouteMetadataFieldIds ?? null,
73437
+ previewStage: dataContext.previewStage
72783
73438
  };
72784
73439
  }
72785
73440
  function PageContent(props) {
@@ -72793,10 +73448,10 @@ function PageContent(props) {
72793
73448
  apiBaseUrl,
72794
73449
  portalToken,
72795
73450
  previewToken,
72796
- readAuthority: readAuthority ?? dataContext?.readAuthority,
73451
+ readAuthority: readAuthority ?? dataContext.readAuthority,
72797
73452
  resolvedData,
72798
73453
  bookingOfferingPresets: bookingOfferingPresets ?? [],
72799
- mailingList: mailingList ?? dataContext?.mailingList,
73454
+ mailingList: mailingList ?? dataContext.mailingList,
72800
73455
  bookingTimeZone: resolveSdkBookingTimeZone(bookingTimeZone),
72801
73456
  routes: routeMap,
72802
73457
  pagesByIdentifier,
@@ -73771,21 +74426,39 @@ function resolveSiteCustomBlocks(site) {
73771
74426
  }
73772
74427
  //#endregion
73773
74428
  //#region src/rendering/helpers/pageRuntimeSupport.ts
73774
- function convertPageResponseBlock(block) {
74429
+ function readBlockContentId(value, name) {
74430
+ if (value === null || value === void 0) return null;
74431
+ if (typeof value === "string") return value;
74432
+ throw new Error(`Invalid ${name} in page: expected string or null`);
74433
+ }
74434
+ function readTransportedBlockIdentity(block, kind, stage) {
74435
+ return {
74436
+ draftContentId: readBlockContentId(block.draftContentId, "draft content id"),
74437
+ publishedPlacement: stage === "preview" ? readPublishedBlockPlacement(readBlockContentId(block.publishedContentId, "published content id"), sanitizeNullableSdkRuntimeBlockContent(kind, readNestedContentRecord(block.publishedContent))) : null
74438
+ };
74439
+ }
74440
+ function convertPageResponseBlock(block, stage) {
73775
74441
  if (!isRecord$3(block)) throw new Error("Invalid block format in page API response");
74442
+ const header = readTransportedBlockHeader(block);
74443
+ const admission = readInvalidBlockAdmission(block.admission);
74444
+ return {
74445
+ ...header,
74446
+ ...readTransportedBlockIdentity(block, header.kind, stage),
74447
+ content: sanitizeSdkRuntimeBlockContent(header.kind, readContentRecord(block.content) ?? {}),
74448
+ draftContent: sanitizeNullableSdkRuntimeBlockContent(header.kind, readNestedContentRecord(block.draftContent)),
74449
+ ...admission ? { admission } : {}
74450
+ };
74451
+ }
74452
+ function readTransportedBlockHeader(block) {
73776
74453
  if (typeof block.id !== "string" && block.id !== null) throw new Error(`Invalid block id in page: expected string or null, got ${typeof block.id}`);
73777
74454
  if (typeof block.kind !== "string") throw new Error(`Invalid block kind in page: expected string, got ${typeof block.kind}`);
73778
74455
  if (typeof block.purpose !== "string") throw new Error(`Invalid block purpose in page: expected string, got ${typeof block.purpose}`);
73779
74456
  if (block.contentVersionId !== void 0 && block.contentVersionId !== null && typeof block.contentVersionId !== "string") throw new Error(`Invalid content version id in page: expected string or null, got ${typeof block.contentVersionId}`);
73780
- const admission = readInvalidBlockAdmission(block.admission);
73781
74457
  return {
73782
74458
  id: block.id,
73783
74459
  ...block.contentVersionId !== void 0 ? { contentVersionId: block.contentVersionId } : {},
73784
74460
  kind: block.kind,
73785
- purpose: block.purpose,
73786
- content: sanitizeSdkRuntimeBlockContent(block.kind, readContentRecord(block.content) ?? {}),
73787
- draftContent: sanitizeNullableSdkRuntimeBlockContent(block.kind, readNestedContentRecord(block.draftContent)),
73788
- ...admission ? { admission } : {}
74461
+ purpose: block.purpose
73789
74462
  };
73790
74463
  }
73791
74464
  function readInvalidBlockAdmission(value) {
@@ -73798,7 +74471,7 @@ function buildPageOutline(input) {
73798
74471
  name: input.name,
73799
74472
  path: input.path ?? input.fallbackPath,
73800
74473
  purpose: input.purpose,
73801
- blocks: input.blocks.map(convertPageResponseBlock)
74474
+ blocks: input.blocks.map((block) => convertPageResponseBlock(block, input.previewStage))
73802
74475
  };
73803
74476
  }
73804
74477
  function buildRenderablePageOutline(input) {
@@ -73811,6 +74484,8 @@ function buildRenderablePageOutline(input) {
73811
74484
  function admitSdkRuntimePageBlock(block, customBlocks) {
73812
74485
  const base = {
73813
74486
  id: block.id,
74487
+ draftContentId: block.draftContentId,
74488
+ publishedPlacement: block.publishedPlacement,
73814
74489
  ...block.contentVersionId !== void 0 ? { contentVersionId: block.contentVersionId } : {},
73815
74490
  kind: block.kind,
73816
74491
  purpose: block.purpose,
@@ -73881,6 +74556,7 @@ function buildFooterRenderablePageOutline(siteData, blocks) {
73881
74556
  fallbackPath: "/__footer",
73882
74557
  purpose: "footer",
73883
74558
  blocks,
74559
+ previewStage: "published",
73884
74560
  customBlocks: resolveSiteCustomBlocks(siteData)
73885
74561
  });
73886
74562
  }
@@ -75056,6 +75732,13 @@ const EVENT_SERIES_REGISTRATION_ENDPOINT_DEFINITIONS = defineEndpointDefinitions
75056
75732
  errors: [],
75057
75733
  responseKind: "blob"
75058
75734
  },
75735
+ exportEventOfferingRegistrationsCsv: {
75736
+ path: "/sites/{siteId}/bookings/event-offerings/{eventOfferingId}/registrations/export.csv",
75737
+ method: "GET",
75738
+ auth: "user",
75739
+ errors: [],
75740
+ responseKind: "blob"
75741
+ },
75059
75742
  updateEventSeriesRegistration: {
75060
75743
  path: "/sites/{siteId}/bookings/event-series/{seriesId}/registrations/{registrationId}",
75061
75744
  method: "PATCH",
@@ -75066,6 +75749,19 @@ const EVENT_SERIES_REGISTRATION_ENDPOINT_DEFINITIONS = defineEndpointDefinitions
75066
75749
  "resource:not_found",
75067
75750
  "server:internal_error"
75068
75751
  ]
75752
+ },
75753
+ saveEventSeriesRegistrationAnswers: {
75754
+ path: "/sites/{siteId}/bookings/event-series/{seriesId}/registrations/{registrationId}/answers",
75755
+ method: "PUT",
75756
+ auth: "user",
75757
+ responseKind: "json",
75758
+ errors: [
75759
+ "validation:invalid_input",
75760
+ "auth:forbidden",
75761
+ "resource:not_found",
75762
+ "resource:conflict",
75763
+ "server:internal_error"
75764
+ ]
75069
75765
  }
75070
75766
  });
75071
75767
  //#endregion
@@ -75486,6 +76182,30 @@ const NEWSLETTER_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
75486
76182
  responseKind: "json",
75487
76183
  demoMutation: DEMO_MUTATION_BLOCKED_HIDDEN
75488
76184
  },
76185
+ scheduleNewsletterCampaign: {
76186
+ path: "/sites/{siteId}/newsletter/campaigns/{campaignId}/schedule",
76187
+ method: "POST",
76188
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
76189
+ auth: "user",
76190
+ responseKind: "json",
76191
+ demoMutation: DEMO_MUTATION_BLOCKED_HIDDEN
76192
+ },
76193
+ unscheduleNewsletterCampaign: {
76194
+ path: "/sites/{siteId}/newsletter/campaigns/{campaignId}/unschedule",
76195
+ method: "POST",
76196
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
76197
+ auth: "user",
76198
+ responseKind: "json",
76199
+ demoMutation: DEMO_MUTATION_BLOCKED_HIDDEN
76200
+ },
76201
+ stopNewsletterCampaign: {
76202
+ path: "/sites/{siteId}/newsletter/campaigns/{campaignId}/stop",
76203
+ method: "POST",
76204
+ tags: [TAG_TEMPLATES.site, TAG_TEMPLATES.siteNewsletter],
76205
+ auth: "user",
76206
+ responseKind: "json",
76207
+ demoMutation: DEMO_MUTATION_BLOCKED_HIDDEN
76208
+ },
75489
76209
  testSendNewsletterCampaign: {
75490
76210
  path: "/sites/{siteId}/newsletter/campaigns/{campaignId}/test-send",
75491
76211
  method: "POST",
@@ -75496,6 +76216,154 @@ const NEWSLETTER_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
75496
76216
  }
75497
76217
  });
75498
76218
  //#endregion
76219
+ //#region ../api/src/sdkEventEndpointDefinitions.ts
76220
+ /**
76221
+ * The SDK's Event graph: the Events themselves and the occurrences of one run.
76222
+ * Extracted from `endpoints.ts` so this family can be read, and added to, on
76223
+ * its own. New entries here must declare `errors`; the
76224
+ * require-api-endpoint-errors lint enforces that here as in endpoints.ts.
76225
+ */
76226
+ const SDK_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
76227
+ sdkListEvents: {
76228
+ path: "/sdk/{siteId}/events",
76229
+ method: "GET",
76230
+ auth: "service",
76231
+ responseKind: "json"
76232
+ },
76233
+ sdkUpsertEvent: {
76234
+ path: "/sdk/{siteId}/events",
76235
+ method: "POST",
76236
+ auth: "service",
76237
+ responseKind: "json",
76238
+ errors: [
76239
+ "validation:invalid_input",
76240
+ "resource:not_found",
76241
+ "resource:conflict",
76242
+ "billing:plan_limit_exceeded",
76243
+ "server:internal_error"
76244
+ ]
76245
+ },
76246
+ sdkGetEvent: {
76247
+ path: "/sdk/{siteId}/events/{identifier}",
76248
+ method: "GET",
76249
+ auth: "service",
76250
+ responseKind: "json"
76251
+ },
76252
+ sdkDeleteEvent: {
76253
+ path: "/sdk/{siteId}/events/{identifier}",
76254
+ method: "DELETE",
76255
+ auth: "service",
76256
+ responseKind: "json",
76257
+ errors: [
76258
+ "resource:not_found",
76259
+ "resource:conflict",
76260
+ "server:internal_error"
76261
+ ]
76262
+ },
76263
+ sdkTakeEventOffline: {
76264
+ path: "/sdk/{siteId}/events/{identifier}/lifecycle",
76265
+ method: "POST",
76266
+ auth: "service",
76267
+ responseKind: "json",
76268
+ errors: [
76269
+ "resource:not_found",
76270
+ "validation:invalid_input",
76271
+ "resource:conflict",
76272
+ "server:internal_error"
76273
+ ]
76274
+ },
76275
+ sdkListEventOccurrences: {
76276
+ path: "/sdk/{siteId}/events/{identifier}/runs/{runIdentifier}/occurrences",
76277
+ method: "GET",
76278
+ auth: "service",
76279
+ responseKind: "json",
76280
+ errors: [
76281
+ "resource:not_found",
76282
+ "validation:invalid_input",
76283
+ "server:internal_error"
76284
+ ]
76285
+ },
76286
+ sdkAddEventOccurrence: {
76287
+ path: "/sdk/{siteId}/events/{identifier}/runs/{runIdentifier}/occurrences",
76288
+ method: "POST",
76289
+ auth: "service",
76290
+ responseKind: "json",
76291
+ errors: [
76292
+ "resource:not_found",
76293
+ "validation:invalid_input",
76294
+ "resource:conflict",
76295
+ "server:internal_error"
76296
+ ]
76297
+ },
76298
+ sdkUpdateEventOccurrence: {
76299
+ path: "/sdk/{siteId}/events/{identifier}/runs/{runIdentifier}/occurrences/{occurrenceId}",
76300
+ method: "PATCH",
76301
+ auth: "service",
76302
+ responseKind: "json",
76303
+ errors: [
76304
+ "resource:not_found",
76305
+ "validation:invalid_input",
76306
+ "resource:conflict",
76307
+ "server:internal_error"
76308
+ ]
76309
+ },
76310
+ sdkCancelEventOccurrence: {
76311
+ path: "/sdk/{siteId}/events/{identifier}/runs/{runIdentifier}/occurrences/{occurrenceId}/cancel",
76312
+ method: "POST",
76313
+ auth: "service",
76314
+ responseKind: "json",
76315
+ errors: [
76316
+ "resource:not_found",
76317
+ "resource:conflict",
76318
+ "server:internal_error"
76319
+ ]
76320
+ },
76321
+ sdkDeleteEventOccurrence: {
76322
+ path: "/sdk/{siteId}/events/{identifier}/runs/{runIdentifier}/occurrences/{occurrenceId}",
76323
+ method: "DELETE",
76324
+ auth: "service",
76325
+ responseKind: "json",
76326
+ errors: [
76327
+ "resource:not_found",
76328
+ "resource:conflict",
76329
+ "server:internal_error"
76330
+ ]
76331
+ }
76332
+ });
76333
+ //#endregion
76334
+ //#region ../api/src/sdkPageDesignEndpointDefinitions.ts
76335
+ /**
76336
+ * SDK management endpoints for a page's draft design (issue 1497), spread into
76337
+ * `ENDPOINT_DEFINITIONS` like the other endpoint-definition modules. New
76338
+ * entries here must declare `errors`, which the require-api-endpoint-errors
76339
+ * lint enforces.
76340
+ */
76341
+ const SDK_PAGE_DESIGN_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
76342
+ sdkGetPageDesign: {
76343
+ path: "/sdk/{siteId}/pages/{identifier}/design",
76344
+ method: "GET",
76345
+ auth: "service",
76346
+ responseKind: "json",
76347
+ errors: [
76348
+ "resource:not_found",
76349
+ "validation:invalid_input",
76350
+ "server:internal_error"
76351
+ ]
76352
+ },
76353
+ sdkSavePageDesign: {
76354
+ path: "/sdk/{siteId}/pages/{identifier}/design",
76355
+ method: "POST",
76356
+ auth: "service",
76357
+ responseKind: "json",
76358
+ errors: [
76359
+ "resource:not_found",
76360
+ "validation:invalid_input",
76361
+ "resource:conflict",
76362
+ "server:internal_error"
76363
+ ]
76364
+ }
76365
+ });
76366
+ //#endregion
75499
76367
  //#region ../api/src/publicEventEndpointDefinitions.ts
75500
76368
  const PUBLIC_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
75501
76369
  checkPublicEventRegistrationNextStep: {
@@ -75740,6 +76608,45 @@ const SITE_API_KEY_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
75740
76608
  }
75741
76609
  });
75742
76610
  //#endregion
76611
+ //#region ../api/src/siteManagementEndpointDefinitions.ts
76612
+ const SITE_MANAGEMENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
76613
+ createSite: {
76614
+ path: "/sites/manage",
76615
+ method: "POST",
76616
+ tags: ["site"],
76617
+ auth: "user",
76618
+ responseKind: "json"
76619
+ },
76620
+ updateSite: {
76621
+ path: "/sites/manage",
76622
+ method: "PUT",
76623
+ tags: ["site"],
76624
+ auth: "user",
76625
+ responseKind: "json"
76626
+ },
76627
+ changeSiteAddress: {
76628
+ path: "/sites/{siteId}/address",
76629
+ method: "PUT",
76630
+ auth: "user",
76631
+ responseKind: "json",
76632
+ demoMutation: DEMO_MUTATION_BLOCKED_DISABLED
76633
+ },
76634
+ getSiteAddressAvailability: {
76635
+ path: "/sites/{siteId}/address/availability",
76636
+ method: "GET",
76637
+ auth: "user",
76638
+ responseKind: "json"
76639
+ },
76640
+ deleteSite: {
76641
+ path: "/sites/{siteId}",
76642
+ method: "DELETE",
76643
+ tags: ["site", "site-{siteId}"],
76644
+ auth: "user",
76645
+ responseKind: "json",
76646
+ demoMutation: DEMO_MUTATION_BLOCKED_HIDDEN
76647
+ }
76648
+ });
76649
+ //#endregion
75743
76650
  //#region ../api/src/eventManagementEndpointDefinitions.ts
75744
76651
  const EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
75745
76652
  listEvents: {
@@ -75787,6 +76694,28 @@ const EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
75787
76694
  "server:internal_error"
75788
76695
  ]
75789
76696
  },
76697
+ /**
76698
+ * The web address of an event's own page. A course page belongs to the
76699
+ * event itself, so its address is written here rather than through a run.
76700
+ */
76701
+ updateEventAddress: {
76702
+ path: "/sites/{siteId}/bookings/events/{eventId}/address",
76703
+ method: "PATCH",
76704
+ tags: [
76705
+ "site-{siteId}",
76706
+ "event-series-{siteId}",
76707
+ TAG_TEMPLATES.siteRoutableContent,
76708
+ TAG_TEMPLATES.siteRedirects
76709
+ ],
76710
+ auth: "user",
76711
+ responseKind: "json",
76712
+ errors: [
76713
+ "validation:invalid_input",
76714
+ "resource:not_found",
76715
+ "resource:conflict",
76716
+ "server:internal_error"
76717
+ ]
76718
+ },
75790
76719
  createWholeSeriesRun: {
75791
76720
  path: "/sites/{siteId}/bookings/events/{eventId}/runs",
75792
76721
  method: "POST",
@@ -75813,6 +76742,20 @@ const EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
75813
76742
  "server:internal_error"
75814
76743
  ]
75815
76744
  },
76745
+ updateCourseBookingSettings: {
76746
+ path: "/sites/{siteId}/bookings/events/{eventId}/booking-settings",
76747
+ method: "PATCH",
76748
+ tags: ["site-{siteId}", "event-series-{siteId}"],
76749
+ auth: "user",
76750
+ responseKind: "json",
76751
+ errors: [
76752
+ "validation:invalid_input",
76753
+ "auth:forbidden",
76754
+ "resource:not_found",
76755
+ "resource:conflict",
76756
+ "server:internal_error"
76757
+ ]
76758
+ },
75816
76759
  cancelWholeSeriesRun: {
75817
76760
  path: "/sites/{siteId}/bookings/events/{eventId}/runs/{runId}/cancellation",
75818
76761
  method: "POST",
@@ -75825,6 +76768,28 @@ const EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
75825
76768
  "resource:conflict",
75826
76769
  "server:internal_error"
75827
76770
  ]
76771
+ },
76772
+ /**
76773
+ * The Content tab's one write: the short introduction, image and
76774
+ * description of an event's page, published without its layout.
76775
+ */
76776
+ saveEventPageWords: {
76777
+ path: "/sites/{siteId}/page-surfaces/{pageSurfaceId}/words",
76778
+ method: "PUT",
76779
+ tags: [
76780
+ TAG_TEMPLATES.siteCollection,
76781
+ TAG_TEMPLATES.siteRoutableContent,
76782
+ TAG_TEMPLATES.pageSurfaceById
76783
+ ],
76784
+ auth: "user",
76785
+ responseKind: "json",
76786
+ errors: [
76787
+ "auth:forbidden",
76788
+ "validation:invalid_input",
76789
+ "resource:not_found",
76790
+ "resource:conflict",
76791
+ "server:internal_error"
76792
+ ]
75828
76793
  }
75829
76794
  });
75830
76795
  //#endregion
@@ -76257,19 +77222,56 @@ const BILLING_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
76257
77222
  responseKind: "json"
76258
77223
  }
76259
77224
  });
77225
+ //#endregion
77226
+ //#region ../api/src/clientErrorReporting.ts
77227
+ const CLIENT_ERROR_REPORT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({ reportClientError: {
77228
+ path: "/client/error-report",
77229
+ method: "POST",
77230
+ auth: "user",
77231
+ errors: [],
77232
+ responseKind: "json"
77233
+ } });
77234
+ const CLIENT_ERROR_REPORT_LIMITS = {
77235
+ pathname: 1024,
77236
+ name: 120,
77237
+ message: 2e3,
77238
+ stack: 8e3,
77239
+ digest: 128,
77240
+ componentStack: 8e3,
77241
+ sourceLocation: 1024
77242
+ };
77243
+ const clientErrorEventSchema = z.enum([
77244
+ "dashboard:route-error",
77245
+ "dashboard:app-error",
77246
+ "client:error-boundary",
77247
+ "client:window-error",
77248
+ "client:unhandled-rejection"
77249
+ ]);
77250
+ z.object({
77251
+ event: clientErrorEventSchema,
77252
+ pathname: z.string().startsWith("/").max(CLIENT_ERROR_REPORT_LIMITS.pathname),
77253
+ name: z.string().max(CLIENT_ERROR_REPORT_LIMITS.name),
77254
+ message: z.string().max(CLIENT_ERROR_REPORT_LIMITS.message),
77255
+ stack: z.string().max(CLIENT_ERROR_REPORT_LIMITS.stack).nullable(),
77256
+ digest: z.string().max(CLIENT_ERROR_REPORT_LIMITS.digest).nullable(),
77257
+ componentStack: z.string().max(CLIENT_ERROR_REPORT_LIMITS.componentStack).nullable(),
77258
+ sourceLocation: z.string().max(CLIENT_ERROR_REPORT_LIMITS.sourceLocation).nullable()
77259
+ }).strict();
76260
77260
  ({
77261
+ ...CLIENT_ERROR_REPORT_ENDPOINT_DEFINITIONS,
76261
77262
  ...PUBLIC_NEWSLETTER_ENDPOINT_DEFINITIONS,
76262
77263
  ...ACCOUNT_ENDPOINT_DEFINITIONS,
76263
77264
  ...PUBLIC_ONBOARDING_ATTEMPT_ENDPOINT_DEFINITIONS,
76264
77265
  ...PUBLIC_EVENT_ENDPOINT_DEFINITIONS,
76265
77266
  ...GIFTING_ENDPOINT_DEFINITIONS,
76266
77267
  ...PUBLIC_READ_ENDPOINT_DEFINITIONS,
76267
- ...BILLING_ENDPOINT_DEFINITIONS
77268
+ ...BILLING_ENDPOINT_DEFINITIONS,
77269
+ ...SITE_MANAGEMENT_ENDPOINT_DEFINITIONS
76268
77270
  }), TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, { ...SITE_API_KEY_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.site, TAG_TEMPLATES.siteBookingSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteBookingSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteEmailSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteEmailSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteEmailSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteEmailSettings, {
76269
77271
  ...NEWSLETTER_ENDPOINT_DEFINITIONS,
76270
77272
  ...APPOINTMENT_ENDPOINT_DEFINITIONS,
76271
77273
  ...EVENT_SERIES_REGISTRATION_ENDPOINT_DEFINITIONS
76272
- }, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.workflowRunById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.routeById, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicBookingServices, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, { ...PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicEventFilters, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicShop, TAG_TEMPLATES.publicCheckout, TAG_TEMPLATES.siteRoutableContent, { ...PUBLIC_EVENT_CHECKOUT_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, { ...EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS }, { ...SDK_MEDIA_ENDPOINT_DEFINITIONS }, { ...SDK_ENDPOINT_DEFINITIONS }, { ...SDK_ENTRY_READ_ENDPOINTS };
77274
+ }, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteById, TAG_TEMPLATES.siteBySlug, TAG_TEMPLATES.siteByDomain, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.workflowRunById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.routeById, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.site, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.site, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.navigationItemById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteNavigation, TAG_TEMPLATES.navigationMenuById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteSettings, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicBookingServices, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.siteTheme, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteCollection, TAG_TEMPLATES.pageSurfaceById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.pageById, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteRedirects, TAG_TEMPLATES.siteRouteOutcomes, TAG_TEMPLATES.siteRoutableContent, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, TAG_TEMPLATES.formSubmissionsBySlug, TAG_TEMPLATES.siteForms, { ...PUBLIC_BOOKING_AVAILABILITY_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicEventFilters, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicShop, TAG_TEMPLATES.publicCheckout, TAG_TEMPLATES.siteRoutableContent, { ...PUBLIC_EVENT_CHECKOUT_ENDPOINT_DEFINITIONS }, TAG_TEMPLATES.publicEvents, TAG_TEMPLATES.publicEvents, { ...EVENT_MANAGEMENT_ENDPOINT_DEFINITIONS }, { ...SDK_MEDIA_ENDPOINT_DEFINITIONS }, { ...SDK_ENDPOINT_DEFINITIONS }, { ...SDK_PAGE_DESIGN_ENDPOINT_DEFINITIONS }, { ...SDK_EVENT_ENDPOINT_DEFINITIONS }, { ...SDK_ENTRY_READ_ENDPOINTS };
76273
77275
  z.object({ refreshToken: z.string().min(1).max(4096) }).strict();
76274
77276
  z.object({
76275
77277
  accessToken: z.string().min(1).max(4096),
@@ -76746,6 +77748,46 @@ z.object({ acceptExtraSiteCharge: z.boolean().optional() });
76746
77748
  //#region ../surfaces/src/index.ts
76747
77749
  const SURFACE_IDS = ["core", "macadamia"];
76748
77750
  const DEFAULT_SURFACE_ID = "core";
77751
+ /**
77752
+ * Root hosts for each surface, keyed by deployment tier. The tier-keyed shape
77753
+ * makes it impossible to mis-order entries: callers like
77754
+ * `buildSurfaceAwareDashboardOriginForSurfaceId` look up by tier directly
77755
+ * rather than relying on positional alignment between arrays.
77756
+ */
77757
+ const SURFACE_ROOT_HOSTS = {
77758
+ core: {
77759
+ local: "localhost",
77760
+ prod: "riverbankcms.com"
77761
+ },
77762
+ macadamia: {
77763
+ local: "macadamia.localhost",
77764
+ prod: "macadamia.studio"
77765
+ }
77766
+ };
77767
+ const SURFACE_SITE_ROOT_HOSTS = {
77768
+ core: {
77769
+ local: "localhost",
77770
+ prod: "riverbanksites.com"
77771
+ },
77772
+ macadamia: {
77773
+ local: "macadamia.localhost",
77774
+ prod: "macadamia.studio"
77775
+ }
77776
+ };
77777
+ const SURFACE_SITE_ROOT_HOST_ALIASES = { core: { prod: ["preview.riverbanksites.com"] } };
77778
+ function surfaceHostTableValues(table) {
77779
+ return Object.values(table).flatMap((byTier) => Object.values(byTier).flat());
77780
+ }
77781
+ function siteSlugLabelUnderRoot(host, siteRootHost) {
77782
+ if (!host.endsWith(`.${siteRootHost}`)) return [];
77783
+ return host.slice(0, host.length - siteRootHost.length - 1).split(".").slice(-1);
77784
+ }
77785
+ function deriveSurfaceOwnedSiteSlugs() {
77786
+ const siteRootHosts = [...surfaceHostTableValues(SURFACE_SITE_ROOT_HOSTS), ...surfaceHostTableValues(SURFACE_SITE_ROOT_HOST_ALIASES)];
77787
+ const labels = [...surfaceHostTableValues(SURFACE_ROOT_HOSTS), ...siteRootHosts].flatMap((host) => siteRootHosts.flatMap((root) => siteSlugLabelUnderRoot(host, root)));
77788
+ return [...new Set(labels)].sort();
77789
+ }
77790
+ deriveSurfaceOwnedSiteSlugs();
76749
77791
  [...SURFACE_IDS.filter((surfaceId) => surfaceId !== DEFAULT_SURFACE_ID)];
76750
77792
  //#endregion
76751
77793
  //#region ../api/src/accountApi.ts
@@ -77374,6 +78416,7 @@ function parsePublicProductCategorySelector(value) {
77374
78416
  productCategoryEntryId: record.productCategoryEntryId
77375
78417
  };
77376
78418
  }
78419
+ z.enum(["public", "unlisted"], { error: "visibility must be 'public' or 'unlisted'" });
77377
78420
  //#endregion
77378
78421
  //#region ../api/src/bookingPaymentTermSchemas.ts
77379
78422
  const bookingDepositRefundPolicySchema = z.enum(["refundable", "non_refundable"]);