@riverbankcms/sdk 0.144.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 (228) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/{PreviewEditorSidebar-CQZNnFiM.mjs → PreviewEditorSidebar-kg2r2Fyi.mjs} +4 -4
  3. package/dist/{PreviewEditorUI-DCNxnmI0.mjs → PreviewEditorUI-sWMLp5dT.mjs} +3 -3
  4. package/dist/{PreviewQueryProvider-BCYICImX.mjs → PreviewQueryProvider-Cg51xq1w.mjs} +1 -1
  5. package/dist/{SdkPreviewModeRuntime-BJolUbr1.mjs → SdkPreviewModeRuntime-h2ti3Hch.mjs} +688 -519
  6. package/dist/{SiteChromeCustomizeContext-CKgEpoxi.mjs → SiteChromeCustomizeContext-DEACFH9m.mjs} +3381 -2017
  7. package/dist/_dts/ai/src/contracts/proposals.d.ts +5643 -66
  8. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +513 -6
  9. package/dist/_dts/api/src/appointmentEndpointDefinitions.d.ts +49 -0
  10. package/dist/_dts/api/src/appointmentServiceCategoryEndpoints.d.ts +75 -0
  11. package/dist/_dts/api/src/appointmentServiceContracts.d.ts +80 -0
  12. package/dist/_dts/api/src/appointmentSetup.d.ts +8 -1
  13. package/dist/_dts/api/src/bookingConfig.d.ts +8 -1
  14. package/dist/_dts/api/src/bookingManagementEndpoints.d.ts +10 -26
  15. package/dist/_dts/api/src/bookingManagementEventEndpointTypes.d.ts +45 -1
  16. package/dist/_dts/api/src/clientErrorReporting.d.ts +50 -0
  17. package/dist/_dts/api/src/communications.d.ts +32 -24
  18. package/dist/_dts/api/src/contentRuntime.d.ts +3 -5
  19. package/dist/_dts/api/src/endpoints.d.ts +316 -134
  20. package/dist/_dts/api/src/event-visibility.d.ts +20 -0
  21. package/dist/_dts/api/src/eventManagementContracts.d.ts +56 -3
  22. package/dist/_dts/api/src/eventManagementEndpointDefinitions.d.ts +24 -0
  23. package/dist/_dts/api/src/eventPageWords.d.ts +19 -0
  24. package/dist/_dts/api/src/eventSeriesRegistrationEndpointDefinitions.d.ts +14 -0
  25. package/dist/_dts/api/src/index.d.ts +7 -1
  26. package/dist/_dts/api/src/indexCoreTypes.d.ts +1 -1
  27. package/dist/_dts/api/src/indexExtendedTypes.d.ts +1 -1
  28. package/dist/_dts/api/src/launchReadiness.d.ts +22 -2
  29. package/dist/_dts/api/src/launchRequirementPriority.d.ts +3 -0
  30. package/dist/_dts/api/src/newsletterCampaignCommandEndpoints.d.ts +13 -0
  31. package/dist/_dts/api/src/newsletterEndpointDefinitions.d.ts +30 -11
  32. package/dist/_dts/api/src/occurrenceScheduling.d.ts +10 -0
  33. package/dist/_dts/api/src/onboarding/confirmationResend.d.ts +3 -0
  34. package/dist/_dts/api/src/pageSurfaceEndpoints.d.ts +44 -0
  35. package/dist/_dts/api/src/participantMessaging.d.ts +11 -1
  36. package/dist/_dts/api/src/publicBookingService.d.ts +35 -1
  37. package/dist/_dts/api/src/publicExperience/newsletterEndpoints.d.ts +13 -0
  38. package/dist/_dts/api/src/publicNewsletterEndpointDefinitions.d.ts +58 -0
  39. package/dist/_dts/api/src/schemas/admin-observability-logs.d.ts +3 -3
  40. package/dist/_dts/api/src/sdk-event-upsert.d.ts +40 -1
  41. package/dist/_dts/api/src/sdkContracts.d.ts +3 -42
  42. package/dist/_dts/api/src/sdkCourseBooking.d.ts +30 -0
  43. package/dist/_dts/api/src/sdkEventEndpointDefinitions.d.ts +76 -0
  44. package/dist/_dts/api/src/sdkEventLifecycle.d.ts +37 -0
  45. package/dist/_dts/api/src/sdkManagedEventContracts.d.ts +18 -0
  46. package/dist/_dts/api/src/sdkPageDesign.d.ts +388 -0
  47. package/dist/_dts/api/src/sdkPageDesignEndpointDefinitions.d.ts +22 -0
  48. package/dist/_dts/api/src/sdkPullEventContracts.d.ts +46 -0
  49. package/dist/_dts/api/src/siteAddressEndpoints.d.ts +29 -0
  50. package/dist/_dts/api/src/siteManagementEndpointDefinitions.d.ts +49 -0
  51. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +4 -38
  52. package/dist/_dts/api/src/siteOnboarding.d.ts +75 -9
  53. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +1 -1
  54. package/dist/_dts/api/src/types.d.ts +8 -1
  55. package/dist/_dts/api/src/wholeSeriesRuns.d.ts +14 -0
  56. package/dist/_dts/blocks/src/PageRenderer.d.ts +4 -4
  57. package/dist/_dts/blocks/src/bindings/index.d.ts +2 -2
  58. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +24 -0
  59. package/dist/_dts/blocks/src/index.d.ts +3 -1
  60. package/dist/_dts/blocks/src/pageRenderAdmission.d.ts +10 -0
  61. package/dist/_dts/blocks/src/pageRenderJoins.d.ts +68 -0
  62. package/dist/_dts/blocks/src/pageRenderPlan.d.ts +8 -27
  63. package/dist/_dts/blocks/src/pageRenderPreparation.d.ts +7 -4
  64. package/dist/_dts/blocks/src/pageRendererBoundaries.d.ts +40 -23
  65. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +3 -1
  66. package/dist/_dts/blocks/src/server.d.ts +1 -1
  67. package/dist/_dts/blocks/src/system/blocks/appointment-services.d.ts +2 -0
  68. package/dist/_dts/blocks/src/system/blocks/events/eventSelectionFields.d.ts +2 -2
  69. package/dist/_dts/blocks/src/system/blocks/events/shared/calendarWindow.d.ts +1 -1
  70. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +27 -5
  71. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +14 -1
  72. package/dist/_dts/blocks/src/system/blocks/location-map.d.ts +2 -2
  73. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +47 -47
  74. package/dist/_dts/blocks/src/system/icons/social.d.ts +1 -1
  75. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +29 -3
  76. package/dist/_dts/blocks/src/system/registry.d.ts +1 -1
  77. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.d.ts +8 -1
  78. package/dist/_dts/blocks/src/system/runtime/nodes/appointment-services.d.ts +22 -2
  79. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +3 -1
  80. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.interactive.d.ts +57 -2
  81. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.service-categories.d.ts +29 -0
  82. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.service-choices.d.ts +13 -0
  83. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.state.d.ts +3 -3
  84. package/dist/_dts/blocks/src/system/runtime/nodes/booking-runtime-sources.d.ts +13 -1
  85. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.d.ts +2 -1
  86. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +23 -7
  87. package/dist/_dts/blocks/src/system/runtime/nodes/form.fields.d.ts +5 -0
  88. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.block-submission.d.ts +36 -0
  89. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.client.d.ts +1 -1
  90. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.interactive.d.ts +6 -1
  91. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.parts.d.ts +46 -0
  92. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.submission.d.ts +20 -1
  93. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.types.d.ts +2 -1
  94. package/dist/_dts/blocks/src/system/runtime/resolver.d.ts +2 -1
  95. package/dist/_dts/blocks/src/system/runtime/shared/components/CheckboxChoice.d.ts +18 -0
  96. package/dist/_dts/blocks/src/system/runtime/shared/components/index.d.ts +1 -0
  97. package/dist/_dts/content-editor/src/index.d.ts +1 -0
  98. package/dist/_dts/content-editor/src/primitives/previewEditableOutline.d.ts +2 -0
  99. package/dist/_dts/content-editor/src/ui/editorBlockEquality.d.ts +3 -0
  100. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +3 -3
  101. package/dist/_dts/content-model/src/content-types/schema.d.ts +1 -1
  102. package/dist/_dts/core/src/appointment-service-categories.d.ts +108 -0
  103. package/dist/_dts/core/src/block-content-identity.d.ts +5 -0
  104. package/dist/_dts/core/src/editor-state.d.ts +2 -1
  105. package/dist/_dts/core/src/index.d.ts +2 -0
  106. package/dist/_dts/core/src/mailing-list-submission.d.ts +3 -1
  107. package/dist/_dts/core/src/public-event-booking-availability.d.ts +20 -1
  108. package/dist/_dts/core/src/public-profile.d.ts +12 -1
  109. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +711 -242
  110. package/dist/_dts/editor-blocks/src/BlockEditor.d.ts +3 -0
  111. package/dist/_dts/editor-blocks/src/block-editor/EmptyState.d.ts +13 -1
  112. package/dist/_dts/editor-blocks/src/block-editor/types.d.ts +2 -2
  113. package/dist/_dts/editor-blocks/src/index.d.ts +1 -0
  114. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/loaders.d.ts +16 -1
  115. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/types.d.ts +13 -0
  116. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/utils.d.ts +29 -0
  117. package/dist/_dts/preview-next/src/client/blocks/BoundaryControl.d.ts +7 -2
  118. package/dist/_dts/preview-next/src/client/blocks/composer/mergeDrafts.d.ts +1 -1
  119. package/dist/_dts/preview-next/src/client/blocks/composer/types.d.ts +3 -3
  120. package/dist/_dts/preview-next/src/client/blocks/previewProjection/applyPreviewProjections.d.ts +1 -3
  121. package/dist/_dts/preview-next/src/client/blocks/previewProjection/runtimeContentDecoration.d.ts +2 -2
  122. package/dist/_dts/preview-next/src/client/blocks/previewProjection/types.d.ts +2 -2
  123. package/dist/_dts/preview-next/src/client/preview/PreviewEntryControllerProvider.d.ts +1 -1
  124. package/dist/_dts/sdk/src/client/runtimeCacheKeys.d.ts +8 -0
  125. package/dist/_dts/sdk/src/client/types.d.ts +1 -1
  126. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  127. package/dist/_dts/sdk/src/contracts/page.d.ts +7 -2
  128. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +5 -0
  129. package/dist/_dts/sdk/src/manifest/passes/pass1-fields.d.ts +2 -2
  130. package/dist/_dts/sdk/src/next/renderSdkContent.d.ts +0 -1
  131. package/dist/_dts/sdk/src/public-api/contracts.d.ts +6 -8
  132. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +4 -4
  133. package/dist/_dts/sdk/src/rendering/components/pageShared.d.ts +4 -4
  134. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +7 -2
  135. package/dist/_dts/sdk/src/rendering/helpers/loadPage.d.ts +5 -2
  136. package/dist/_dts/sdk/src/rendering/helpers/pageRuntimeSupport.d.ts +6 -1
  137. package/dist/_dts/sdk/src/rendering/helpers/templateContent.d.ts +2 -0
  138. package/dist/_dts/sdk/src/rendering/hooks/useContent.d.ts +6 -2
  139. package/dist/_dts/sdk/src/rendering/hooks/usePage.d.ts +5 -1
  140. package/dist/_dts/sdk/src/version.d.ts +1 -1
  141. package/dist/_dts/surfaces/src/index.d.ts +10 -0
  142. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  143. package/dist/_dts/theme-core/src/inputs/checkable.d.ts +42 -0
  144. package/dist/_dts/theme-core/src/inputs/inputGroup.d.ts +19 -0
  145. package/dist/_dts/theme-core/src/layout/boundaryCss.d.ts +3 -0
  146. package/dist/_dts/theme-core/src/site-styles/compositionBudget.d.ts +8 -0
  147. package/dist/_dts/theme-core/src/site-styles/contentTemplateRuntimeCompiler.d.ts +3 -2
  148. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +1 -3
  149. package/dist/_dts/theme-core/src/site-styles/generatedDesignPipeline.d.ts +3 -0
  150. package/dist/_dts/theme-core/src/site-styles/index.d.ts +3 -0
  151. package/dist/_dts/theme-core/src/site-styles/joinTreatment.d.ts +132 -0
  152. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorBoundaries.d.ts +31 -0
  153. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorModel.d.ts +33 -29
  154. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +4 -4
  155. package/dist/_dts/theme-core/src/site-styles/pageFlowPresets.d.ts +36 -0
  156. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +4 -10
  157. package/dist/_dts/theme-core/src/site-styles/rawStyleFields.d.ts +17 -4
  158. package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +20 -74
  159. package/dist/_dts/theme-core/src/site-styles/sectionBoundaryComposition.d.ts +95 -0
  160. package/dist/_dts/theme-core/src/site-styles/sectionTransitions.d.ts +80 -518
  161. package/dist/_dts/theme-core/src/site-styles/siteStyleBlockPolicies.d.ts +9 -4
  162. package/dist/_dts/theme-core/src/site-styles/transitionCatalogue.d.ts +320 -0
  163. package/dist/_dts/theme-core/src/site-styles/types.d.ts +3 -18
  164. package/dist/_dts/ui/src/navigation-tabs.d.ts +2 -1
  165. package/dist/client/{BookingConsentAgreement-DoAILrkN.mjs → BookingConsentAgreement-BQKL045f.mjs} +2 -2
  166. package/dist/client/CheckboxChoice--W2n6yKs.mjs +24 -0
  167. package/dist/client/{DiscountCodeDisclosure-X528hUTf.mjs → DiscountCodeDisclosure-CsOtjFY7.mjs} +1 -1
  168. package/dist/client/{EventCombined.client-DSBVwq5m.mjs → EventCombined.client-BdRjVbhH.mjs} +6 -6
  169. package/dist/client/{EventListing.client-DmFAX7Vb.mjs → EventListing.client-DBxC6xOU.mjs} +2 -2
  170. package/dist/client/{LeafletStaticMap-BAqXdfhq.mjs → LeafletStaticMap-mHEJKrTs.mjs} +1 -1
  171. package/dist/client/{ThemedButton-BwNFVwi4.mjs → ThemedButton-CE5KYT6z.mjs} +1 -1
  172. package/dist/client/{accordion-DRhMdkbd.mjs → accordion-BEQNSptH.mjs} +1 -1
  173. package/dist/client/{bookingForm-iJ_nCsNF.mjs → bookingForm-AMr-s5vw.mjs} +168 -95
  174. package/dist/client/bookings.mjs +239 -110
  175. package/dist/client/{carousel-D8zuffZr.mjs → carousel-Bbt_QgJy.mjs} +1 -1
  176. package/dist/client/{cart-DeMGJViN.mjs → cart-CnWi30zJ.mjs} +3 -3
  177. package/dist/client/{checkout-DXc06Km2.mjs → checkout-f3nkWgd4.mjs} +3 -3
  178. package/dist/client/client.mjs +3222 -1612
  179. package/dist/client/{context-CDLCfDep.mjs → context-Bc8T6pCe.mjs} +1 -1
  180. package/dist/client/{dates-ClFx7Epy.mjs → dates-DUsCEzLb.mjs} +2 -3
  181. package/dist/client/{eventCalendar-BaGpnOGN.mjs → eventCalendar-CKwgXui5.mjs} +9 -9
  182. package/dist/client/{eventCombined-ywONOx2m.mjs → eventCombined-CBAOSTGR.mjs} +4 -4
  183. package/dist/client/{eventDetails-B2LlOTYY.mjs → eventDetails-Dh-_WiBD.mjs} +4 -4
  184. package/dist/client/{eventListing-CMgxWpqP.mjs → eventListing-CA3frIH5.mjs} +4 -4
  185. package/dist/client/{eventRegistration-BarLDNHR.mjs → eventRegistration-BHtsgdcg.mjs} +21 -22
  186. package/dist/client/{form-BjqKaBJ7.mjs → form-C2h3YAcg.mjs} +135 -104
  187. package/dist/client/{headerSection-CP4XDwz1.mjs → headerSection-lwMuMTH9.mjs} +3 -3
  188. package/dist/client/hooks.mjs +1023 -148
  189. package/dist/client/{locationMap-C5gW-d-w.mjs → locationMap-DJ2bLODV.mjs} +3 -3
  190. package/dist/client/{newsletterForm-afXlVSA0.mjs → newsletterForm-8Pu5K32g.mjs} +225 -102
  191. package/dist/client/{pricing-Bl1CAl28.mjs → pricing-HHx3IgBz.mjs} +1 -1
  192. package/dist/client/{productDetail-CvQ3jWQe.mjs → productDetail-C_1odnAf.mjs} +3 -3
  193. package/dist/client/{productList-U874PGO7.mjs → productList-CtCv-Nmx.mjs} +3 -3
  194. package/dist/client/{propsDecoder-DPXtCZWP.mjs → propsDecoder-Dcgsncxb.mjs} +111 -55
  195. package/dist/client/rendering/client.mjs +1389 -722
  196. package/dist/client/rendering/islands.mjs +17 -17
  197. package/dist/client/rendering.mjs +3136 -1400
  198. package/dist/client/{routeIslandApiConfig-NGYiUWXu.mjs → routeIslandApiConfig-CWLP7wu3.mjs} +1 -1
  199. package/dist/client/{scrollToBlock-0-C3DVhK.mjs → scrollToBlock-BNgaEUpo.mjs} +1 -1
  200. package/dist/client/{shop-BXjXhM9P.mjs → shop-DQXrQ8kJ.mjs} +7 -7
  201. package/dist/client/{shop-commerce-BZ6UlWJ7.mjs → shop-commerce-B88zmWWo.mjs} +6 -6
  202. package/dist/client/{shop-commerce-store-DlSBXb3D.mjs → shop-commerce-store-DtSfkKuX.mjs} +1 -1
  203. package/dist/client/{shopShared-kkEOiLPA.mjs → shopShared-cuaONpkJ.mjs} +4 -4
  204. package/dist/client/{useEventFilters-zvnnE1ro.mjs → useEventFilters-Bukew6Gb.mjs} +6 -6
  205. package/dist/preview-next/before-render.mjs +500 -165
  206. package/dist/preview-next/client/runtime.mjs +4 -4
  207. package/dist/preview-next/editor-base.css +11 -0
  208. package/dist/preview-next/keepAlive.mjs +460 -165
  209. package/dist/preview-next/middleware.mjs +461 -165
  210. package/dist/{previewAuthStore-JA6-I_sL.mjs → previewAuthStore-h5IgbSp4.mjs} +485 -165
  211. package/dist/{sdk-runtime-DFJlQt3q.mjs → sdk-runtime-DTc1uIXn.mjs} +2 -2
  212. package/dist/{separator-o8GHb1xh.mjs → separator-BpHS1KZK.mjs} +1 -1
  213. package/dist/server/components.mjs +3088 -1307
  214. package/dist/server/config-validation.mjs +927 -109
  215. package/dist/server/config.mjs +927 -109
  216. package/dist/server/data.mjs +939 -110
  217. package/dist/server/index.mjs +513 -169
  218. package/dist/server/next.mjs +2874 -1195
  219. package/dist/server/page-converter.mjs +420 -79
  220. package/dist/server/prebuild.mjs +1 -1
  221. package/dist/server/rendering/server.mjs +3071 -1289
  222. package/dist/server/rendering.mjs +3169 -1350
  223. package/dist/server/routing.mjs +1085 -159
  224. package/dist/server/server.mjs +1042 -291
  225. package/dist/server/theme-bridge.mjs +1658 -137
  226. package/dist/server/theme.mjs +6 -4
  227. package/dist/styles/index.css +186 -65
  228. package/package.json +2 -1
@@ -1018,7 +1018,7 @@ function parseEventPageSurfaceDestinationMode(value) {
1018
1018
  return value === "cms-managed" || value === "system-managed" ? value : void 0;
1019
1019
  }
1020
1020
  function parseEventPageSurfaceSubject(value) {
1021
- if (!isRecord$29(value)) return err$2({ kind: "notObject" });
1021
+ if (!isRecord$30(value)) return err$2({ kind: "notObject" });
1022
1022
  switch (value.kind) {
1023
1023
  case "eventSeries": {
1024
1024
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -1073,7 +1073,7 @@ function parseEventSeriesIdList(values) {
1073
1073
  }
1074
1074
  return ok$2(parsed);
1075
1075
  }
1076
- function isRecord$29(value) {
1076
+ function isRecord$30(value) {
1077
1077
  return typeof value === "object" && value !== null && !Array.isArray(value);
1078
1078
  }
1079
1079
  var init_event_page_surface_subject = __esmMin((() => {
@@ -1525,7 +1525,76 @@ var init_booking_date_time = __esmMin((() => {
1525
1525
  }));
1526
1526
  //#endregion
1527
1527
  //#region ../core/src/booking-consent.ts
1528
- var init_booking_consent = __esmMin((() => {})), init_dated_offering_settlement = __esmMin((() => {
1528
+ var init_booking_consent = __esmMin((() => {}));
1529
+ //#endregion
1530
+ //#region ../core/src/appointment-service-categories.ts
1531
+ /**
1532
+ * Groups services under their categories.
1533
+ *
1534
+ * A service whose `categoryId` names no category in `categories` lands in
1535
+ * Other. The composite foreign key keeps that from happening with stored data,
1536
+ * so this is only the total, safe answer for a caller holding a stale list.
1537
+ */
1538
+ function layoutServicesByCategory(categories, services, options) {
1539
+ if (categories.length === 0) return {
1540
+ kind: "uncategorized-site",
1541
+ services: [...services]
1542
+ };
1543
+ const buckets = new Map(categories.map((category) => [category.id, []]));
1544
+ const other = [];
1545
+ for (const service of services) {
1546
+ const bucket = service.categoryId === null ? void 0 : buckets.get(service.categoryId);
1547
+ if (bucket !== void 0) bucket.push(service);
1548
+ else other.push(service);
1549
+ }
1550
+ const groups = [];
1551
+ for (const category of categories) {
1552
+ const groupServices = buckets.get(category.id) ?? [];
1553
+ if (groupServices.length === 0 && !options.keepEmptyGroups) continue;
1554
+ groups.push({
1555
+ category,
1556
+ services: groupServices
1557
+ });
1558
+ }
1559
+ if (groups.length === 0 && !options.keepEmptyGroups) return {
1560
+ kind: "uncategorized-site",
1561
+ services: other
1562
+ };
1563
+ return {
1564
+ kind: "categorized-site",
1565
+ groups,
1566
+ other
1567
+ };
1568
+ }
1569
+ /**
1570
+ * Selects the public view for an optional category filter.
1571
+ *
1572
+ * It takes the category and service lists rather than a prebuilt layout
1573
+ * because the public layout has already dropped empty categories, and then an
1574
+ * empty category (`single` with no services) could not be told apart from a
1575
+ * deleted one (`fallback-all`).
1576
+ */
1577
+ function selectCategoryView(categories, services, filterCategoryId) {
1578
+ const publicLayout = () => layoutServicesByCategory(categories, services, { keepEmptyGroups: false });
1579
+ if (filterCategoryId === null || filterCategoryId === void 0) return {
1580
+ kind: "all",
1581
+ layout: publicLayout()
1582
+ };
1583
+ const category = categories.find((candidate) => candidate.id === filterCategoryId);
1584
+ if (category === void 0) return {
1585
+ kind: "fallback-all",
1586
+ missingCategoryId: filterCategoryId,
1587
+ layout: publicLayout()
1588
+ };
1589
+ return {
1590
+ kind: "single",
1591
+ group: {
1592
+ category,
1593
+ services: services.filter((service) => service.categoryId === filterCategoryId)
1594
+ }
1595
+ };
1596
+ }
1597
+ var init_appointment_service_categories = __esmMin((() => {})), init_dated_offering_settlement = __esmMin((() => {
1529
1598
  init_result$1();
1530
1599
  }));
1531
1600
  var init_person_records = __esmMin((() => {
@@ -1557,7 +1626,8 @@ var init_business_address = __esmMin((() => {
1557
1626
  var init_try_async = __esmMin((() => {
1558
1627
  init_result$1();
1559
1628
  }));
1560
- var init_narrow_result = __esmMin((() => {})), init_editor_state = __esmMin((() => {}));
1629
+ var init_narrow_result = __esmMin((() => {}));
1630
+ var init_editor_state = __esmMin((() => {})), init_block_content_identity = __esmMin((() => {}));
1561
1631
  var init_sensitive_collection = __esmMin((() => {}));
1562
1632
  //#endregion
1563
1633
  //#region ../core/src/assertNever.ts
@@ -1579,7 +1649,7 @@ var init_public_profile = __esmMin((() => {
1579
1649
  var init_public_experience = __esmMin((() => {}));
1580
1650
  //#endregion
1581
1651
  //#region ../core/src/rich-text.ts
1582
- function isRecord$27(value) {
1652
+ function isRecord$28(value) {
1583
1653
  return typeof value === "object" && value !== null && !Array.isArray(value);
1584
1654
  }
1585
1655
  function hasOnlyKeys(value, allowed) {
@@ -1590,10 +1660,10 @@ function isDenseArrayOf(value, predicate) {
1590
1660
  return true;
1591
1661
  }
1592
1662
  function isTipTapMark(value) {
1593
- return isRecord$27(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === void 0 || isRecord$27(value.attrs));
1663
+ return isRecord$28(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === void 0 || isRecord$28(value.attrs));
1594
1664
  }
1595
1665
  function isTipTapNode(value) {
1596
- if (!isRecord$27(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== void 0 && !isRecord$27(value.attrs) || value.marks !== void 0 && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== void 0 && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) return false;
1666
+ if (!isRecord$28(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== void 0 && !isRecord$28(value.attrs) || value.marks !== void 0 && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== void 0 && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) return false;
1597
1667
  if (value.type === "text") return typeof value.text === "string" && value.text.length > 0 && value.content === void 0;
1598
1668
  return value.text === void 0;
1599
1669
  }
@@ -1638,10 +1708,10 @@ var init_mailing_list_identifiers = __esmMin((() => {
1638
1708
  //#endregion
1639
1709
  //#region ../core/src/mailing-list-targeting-parser.ts
1640
1710
  function parseMailingListPlacementTargeting(value) {
1641
- if (!isRecord$26(value)) return targetingError("shape");
1711
+ if (!isRecord$27(value)) return targetingError("shape");
1642
1712
  if (value.mode === "default") return ok$2({ mode: "default" });
1643
1713
  if (value.mode !== "custom") return targetingError("mode");
1644
- if (!isRecord$26(value.selection)) return targetingError("selection");
1714
+ if (!isRecord$27(value.selection)) return targetingError("selection");
1645
1715
  switch (value.provider) {
1646
1716
  case "native": return parseNativeTargeting(value.selection);
1647
1717
  case "brevo": return parseBrevoTargeting(value.selection);
@@ -1650,7 +1720,7 @@ function parseMailingListPlacementTargeting(value) {
1650
1720
  }
1651
1721
  }
1652
1722
  function parseMailingListVisitorSelection(value) {
1653
- if (!isRecord$26(value)) return visitorSelectionError$1("shape");
1723
+ if (!isRecord$27(value)) return visitorSelectionError$1("shape");
1654
1724
  switch (value.provider) {
1655
1725
  case "native": {
1656
1726
  const topicIds = parseVisitorIds(value.topicIds, parseNewsletterListId);
@@ -1783,7 +1853,7 @@ function parseVisitorIds(value, parseId) {
1783
1853
  }
1784
1854
  return parsed;
1785
1855
  }
1786
- function isRecord$26(value) {
1856
+ function isRecord$27(value) {
1787
1857
  return typeof value === "object" && value !== null && !Array.isArray(value);
1788
1858
  }
1789
1859
  function targetingError(field) {
@@ -1840,7 +1910,7 @@ var init_mailing_list_setup = __esmMin((() => {
1840
1910
  //#endregion
1841
1911
  //#region ../core/src/mailing-list-setup-parser.ts
1842
1912
  function parseMailingListGlobalForm(value) {
1843
- if (!isRecord$22(value)) return formError("shape");
1913
+ if (!isRecord$23(value)) return formError("shape");
1844
1914
  const fields = parseGlobalFields(value.fields);
1845
1915
  if (fields === null) return formError("fields");
1846
1916
  if (typeof value.includeOptionalPhone !== "boolean") return formError("includeOptionalPhone");
@@ -1859,7 +1929,7 @@ function parseMailingListGlobalForm(value) {
1859
1929
  });
1860
1930
  }
1861
1931
  function parseGlobalFields(value) {
1862
- if (!isRecord$22(value) || typeof value.preset !== "string") return null;
1932
+ if (!isRecord$23(value) || typeof value.preset !== "string") return null;
1863
1933
  switch (value.preset) {
1864
1934
  case "emailOnly": return { preset: "emailOnly" };
1865
1935
  case "emailName": return isRequirement(value.name) ? {
@@ -1896,7 +1966,7 @@ function parseOptionalString(value) {
1896
1966
  function isRequirement(value) {
1897
1967
  return value === "required" || value === "optional";
1898
1968
  }
1899
- function isRecord$22(value) {
1969
+ function isRecord$23(value) {
1900
1970
  return typeof value === "object" && value !== null && !Array.isArray(value);
1901
1971
  }
1902
1972
  function formError(field) {
@@ -1954,7 +2024,7 @@ var init_mailing_list_targeting = __esmMin((() => {
1954
2024
  //#endregion
1955
2025
  //#region ../core/src/mailing-list-runtime.ts
1956
2026
  function parseMailingListRuntimeContext(value) {
1957
- if (!isRecord$21(value)) return invalidRuntimeContext();
2027
+ if (!isRecord$22(value)) return invalidRuntimeContext();
1958
2028
  if (value.tag === "not_configured") return ok$2({ tag: "not_configured" });
1959
2029
  if (value.tag !== "configured") return invalidRuntimeContext();
1960
2030
  const revision = parseMailingListSetupRevision(value.revision);
@@ -1969,7 +2039,7 @@ function parseMailingListRuntimeContext(value) {
1969
2039
  });
1970
2040
  }
1971
2041
  function parseRuntimeProvider(value) {
1972
- if (!isRecord$21(value)) return null;
2042
+ if (!isRecord$22(value)) return null;
1973
2043
  switch (value.provider) {
1974
2044
  case "native": return parseNativeRuntimeProvider(value);
1975
2045
  case "brevo": return parseBrevoRuntimeProvider(value);
@@ -2001,7 +2071,7 @@ function parseMailchimpRuntimeProvider(value) {
2001
2071
  if (defaultAudienceId === null) return null;
2002
2072
  const audiences = [];
2003
2073
  for (const audience of value.audiences) {
2004
- if (!isRecord$21(audience)) return null;
2074
+ if (!isRecord$22(audience)) return null;
2005
2075
  const id = parseMailchimpAudienceId(audience.id);
2006
2076
  const name = parseName(audience.name);
2007
2077
  if (id === null || name === null || !Array.isArray(audience.interests)) return null;
@@ -2022,7 +2092,7 @@ function parseMailchimpRuntimeProvider(value) {
2022
2092
  function parseMailchimpInterests(value) {
2023
2093
  const interests = [];
2024
2094
  for (const interest of value) {
2025
- if (!isRecord$21(interest)) return null;
2095
+ if (!isRecord$22(interest)) return null;
2026
2096
  const id = parseMailchimpInterestId(interest.id);
2027
2097
  const name = parseName(interest.name);
2028
2098
  const categoryName = parseName(interest.categoryName);
@@ -2039,7 +2109,7 @@ function parseNamedItems(value, parseId) {
2039
2109
  if (!Array.isArray(value)) return null;
2040
2110
  const items = [];
2041
2111
  for (const item of value) {
2042
- if (!isRecord$21(item)) return null;
2112
+ if (!isRecord$22(item)) return null;
2043
2113
  const id = parseId(item.id);
2044
2114
  const name = parseName(item.name);
2045
2115
  if (id === null || name === null) return null;
@@ -2055,7 +2125,7 @@ function parseName(value) {
2055
2125
  const name = value.trim();
2056
2126
  return name.length > 0 ? name : null;
2057
2127
  }
2058
- function isRecord$21(value) {
2128
+ function isRecord$22(value) {
2059
2129
  return typeof value === "object" && value !== null && !Array.isArray(value);
2060
2130
  }
2061
2131
  function invalidRuntimeContext() {
@@ -2080,15 +2150,6 @@ function parseMailingListContentVersionId(value) {
2080
2150
  const parsed = parseContentVersionId(value);
2081
2151
  return parsed.ok ? parsed.value : null;
2082
2152
  }
2083
- function buildMailingListSignupBlockSourceRef(blockId, contentVersionId) {
2084
- const parsedBlockId = parseMailingListBlockId(blockId);
2085
- const parsedContentVersionId = parseMailingListContentVersionId(contentVersionId);
2086
- return parsedBlockId !== null && parsedContentVersionId !== null ? {
2087
- tag: "block_ref",
2088
- blockId: parsedBlockId,
2089
- contentVersionId: parsedContentVersionId
2090
- } : null;
2091
- }
2092
2153
  var init_mailing_list_submission = __esmMin((() => {
2093
2154
  init_branded();
2094
2155
  }));
@@ -2496,6 +2557,7 @@ var init_src$2 = __esmMin((() => {
2496
2557
  init_booking_instalments();
2497
2558
  init_booking_date_time();
2498
2559
  init_booking_consent();
2560
+ init_appointment_service_categories();
2499
2561
  init_dated_offering_settlement();
2500
2562
  init_person_records();
2501
2563
  init_rectification();
@@ -2507,6 +2569,7 @@ var init_src$2 = __esmMin((() => {
2507
2569
  init_narrow_result();
2508
2570
  init_non_empty_array();
2509
2571
  init_editor_state();
2572
+ init_block_content_identity();
2510
2573
  init_sensitive_collection();
2511
2574
  init_assertNever$1();
2512
2575
  init_participants();
@@ -2680,11 +2743,11 @@ var init_storagePathDomain = __esmMin((() => {}));
2680
2743
  var init_fileAssetLegacyProjection = __esmMin((() => {}));
2681
2744
  //#endregion
2682
2745
  //#region ../media-core/src/typeGuards.ts
2683
- function isRecord$19(value) {
2746
+ function isRecord$20(value) {
2684
2747
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2685
2748
  }
2686
2749
  function asRecord$1(value) {
2687
- return isRecord$19(value) ? value : void 0;
2750
+ return isRecord$20(value) ? value : void 0;
2688
2751
  }
2689
2752
  var init_typeGuards$1 = __esmMin((() => {}));
2690
2753
  function isFileJsonObject(value) {
@@ -2694,7 +2757,7 @@ function isFileJsonValue(value) {
2694
2757
  if (value === null || typeof value === "string" || typeof value === "boolean") return true;
2695
2758
  if (typeof value === "number") return Number.isFinite(value);
2696
2759
  if (Array.isArray(value)) return value.every(isFileJsonValue);
2697
- return isRecord$19(value) && isFileJsonObject(value);
2760
+ return isRecord$20(value) && isFileJsonObject(value);
2698
2761
  }
2699
2762
  var init_filePersistence = __esmMin((() => {
2700
2763
  init_src$2();
@@ -3002,7 +3065,7 @@ function isCmsMediaObjectType(value) {
3002
3065
  return typeof value === "string" && CMS_MEDIA_OBJECT_TYPES.includes(value);
3003
3066
  }
3004
3067
  function isCmsMediaObject(value) {
3005
- if (!isRecord$19(value)) return false;
3068
+ if (!isRecord$20(value)) return false;
3006
3069
  const type = value.type;
3007
3070
  if (!isCmsMediaObjectType(type)) return false;
3008
3071
  if ("attrs" in value) return false;
@@ -3013,7 +3076,7 @@ function collectMediaObjects(value, out, location = "") {
3013
3076
  value.forEach((item, index) => collectMediaObjects(item, out, `${location}[${index}]`));
3014
3077
  return;
3015
3078
  }
3016
- if (!isRecord$19(value)) return;
3079
+ if (!isRecord$20(value)) return;
3017
3080
  if (isCmsMediaObject(value)) out.push({
3018
3081
  obj: value,
3019
3082
  location
@@ -3047,8 +3110,8 @@ function walkAndCollectImageAttrs(value, out, location = "") {
3047
3110
  value.forEach((item, index) => walkAndCollectImageAttrs(item, out, `${location}[${index}]`));
3048
3111
  return;
3049
3112
  }
3050
- if (!isRecord$19(value)) return;
3051
- if (value.type === "image" && isRecord$19(value.attrs)) out.push({
3113
+ if (!isRecord$20(value)) return;
3114
+ if (value.type === "image" && isRecord$20(value.attrs)) out.push({
3052
3115
  attrs: value.attrs,
3053
3116
  location: location ? `${location}.attrs` : "attrs"
3054
3117
  });
@@ -3108,7 +3171,7 @@ function mediaIdentifierOrFallback(current, fallback) {
3108
3171
  var init_renderableMediaAsset = __esmMin((() => {}));
3109
3172
  //#endregion
3110
3173
  //#region ../media-core/src/contentMediaAudit.ts
3111
- function isRecord$18(value) {
3174
+ function isRecord$19(value) {
3112
3175
  return typeof value === "object" && value !== null && !Array.isArray(value);
3113
3176
  }
3114
3177
  function summarizeContentMediaValue(value) {
@@ -3119,7 +3182,7 @@ function summarizeContentMediaValue(value) {
3119
3182
  }
3120
3183
  if (value === null) return "null";
3121
3184
  if (Array.isArray(value)) return `array(${value.length})`;
3122
- if (isRecord$18(value)) return `object(${Object.keys(value).sort().join(",")})`;
3185
+ if (isRecord$19(value)) return `object(${Object.keys(value).sort().join(",")})`;
3123
3186
  return typeof value;
3124
3187
  }
3125
3188
  function auditMediaValue(context, path, allowedMediaTypes, value) {
@@ -3143,9 +3206,9 @@ function auditMediaValue(context, path, allowedMediaTypes, value) {
3143
3206
  allowedMediaTypes
3144
3207
  }];
3145
3208
  }
3146
- const hasValidIdentifier = isRecord$18(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0;
3147
- const hasValidExplicitType = isRecord$18(value) && (!("type" in value) || typeof value.type === "string" && allowedMediaTypes.includes(value.type));
3148
- const hasRetiredEnvironmentField = isRecord$18(value) && ("assetId" in value || "storagePath" in value || "storageBucket" in value || "src" in value || "mimeType" in value || looksLikeUuid(value.id));
3209
+ const hasValidIdentifier = isRecord$19(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0;
3210
+ const hasValidExplicitType = isRecord$19(value) && (!("type" in value) || typeof value.type === "string" && allowedMediaTypes.includes(value.type));
3211
+ const hasRetiredEnvironmentField = isRecord$19(value) && ("assetId" in value || "storagePath" in value || "storageBucket" in value || "src" in value || "mimeType" in value || looksLikeUuid(value.id));
3149
3212
  if (hasValidIdentifier && hasValidExplicitType && !hasRetiredEnvironmentField) return [];
3150
3213
  return [{
3151
3214
  tag: "malformed-object",
@@ -3156,7 +3219,7 @@ function auditMediaValue(context, path, allowedMediaTypes, value) {
3156
3219
  }];
3157
3220
  }
3158
3221
  function auditFields(context, fields, value, parentPath) {
3159
- if (!isRecord$18(value)) return [];
3222
+ if (!isRecord$19(value)) return [];
3160
3223
  const findings = [];
3161
3224
  for (const field of fields) {
3162
3225
  if (!(field.id in value)) continue;
@@ -3173,7 +3236,7 @@ function auditFields(context, fields, value, parentPath) {
3173
3236
  if (!Array.isArray(fieldValue)) break;
3174
3237
  for (const [index, item] of fieldValue.entries()) {
3175
3238
  const itemPath = [...fieldPath, index];
3176
- if (!isRecord$18(item)) {
3239
+ if (!isRecord$19(item)) {
3177
3240
  findings.push({
3178
3241
  tag: "missing-schema",
3179
3242
  context,
@@ -3207,7 +3270,7 @@ var init_contentMediaAudit = __esmMin((() => {
3207
3270
  }));
3208
3271
  //#endregion
3209
3272
  //#region ../media-core/src/contentMediaAuditFieldAdapter.ts
3210
- function isRecord$17(value) {
3273
+ function isRecord$18(value) {
3211
3274
  return typeof value === "object" && value !== null && !Array.isArray(value);
3212
3275
  }
3213
3276
  function unknownArray(value) {
@@ -3216,7 +3279,7 @@ function unknownArray(value) {
3216
3279
  function fieldsArray(value) {
3217
3280
  const directFields = unknownArray(value);
3218
3281
  if (directFields) return directFields;
3219
- if (!isRecord$17(value)) return null;
3282
+ if (!isRecord$18(value)) return null;
3220
3283
  return unknownArray(value.fields);
3221
3284
  }
3222
3285
  function nestedFields(field) {
@@ -3281,7 +3344,7 @@ function mergeFields(fields) {
3281
3344
  }
3282
3345
  function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds = ["document"]) {
3283
3346
  return mergeFields(rawFields.flatMap((rawField) => {
3284
- if (!isRecord$17(rawField)) return [];
3347
+ if (!isRecord$18(rawField)) return [];
3285
3348
  const { id, type } = rawField;
3286
3349
  if (typeof id !== "string" || !id || typeof type !== "string") return [];
3287
3350
  if (type === "media" || type === "file") return [{
@@ -3289,7 +3352,7 @@ function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds
3289
3352
  id,
3290
3353
  allowedMediaTypes: allowedMediaTypes(rawField, type, defaultFileKinds)
3291
3354
  }];
3292
- if (flattenRepeaterGroups && type === "group" && isRecord$17(rawField.ui) && rawField.ui.flattenInRepeater === true) return adaptFields(nestedFields(rawField), false, defaultFileKinds);
3355
+ if (flattenRepeaterGroups && type === "group" && isRecord$18(rawField.ui) && rawField.ui.flattenInRepeater === true) return adaptFields(nestedFields(rawField), false, defaultFileKinds);
3293
3356
  if (type === "group" || type === "modal") return [{
3294
3357
  kind: "object",
3295
3358
  id,
@@ -3298,14 +3361,14 @@ function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds
3298
3361
  if (type === "tabGroup") return [{
3299
3362
  kind: "object",
3300
3363
  id,
3301
- fields: adaptFields((unknownArray(rawField.tabs) ?? []).flatMap((tab) => isRecord$17(tab) ? unknownArray(tab.fields) ?? [] : []), false, defaultFileKinds)
3364
+ fields: adaptFields((unknownArray(rawField.tabs) ?? []).flatMap((tab) => isRecord$18(tab) ? unknownArray(tab.fields) ?? [] : []), false, defaultFileKinds)
3302
3365
  }];
3303
3366
  if (type !== "repeater") return [];
3304
- if (isRecord$17(rawField.itemTypes)) return [{
3367
+ if (isRecord$18(rawField.itemTypes)) return [{
3305
3368
  kind: "repeater",
3306
3369
  id,
3307
3370
  mode: "variants",
3308
- variants: Object.entries(rawField.itemTypes).flatMap(([typeId, itemType]) => isRecord$17(itemType) && Array.isArray(itemType.fields) ? [{
3371
+ variants: Object.entries(rawField.itemTypes).flatMap(([typeId, itemType]) => isRecord$18(itemType) && Array.isArray(itemType.fields) ? [{
3309
3372
  typeId,
3310
3373
  fields: adaptFields(itemType.fields, true, defaultFileKinds)
3311
3374
  }] : []),
@@ -7715,6 +7778,15 @@ function asSiteStyleId(value) {
7715
7778
  function asDesignSystemTemplateId(value) {
7716
7779
  return brandNonEmptyString(value, "DesignSystemTemplateId");
7717
7780
  }
7781
+ function toNonEmptyReadonlyArray(values) {
7782
+ const [first, ...rest] = values;
7783
+ return first === void 0 ? null : [first, ...rest];
7784
+ }
7785
+ function requireNonEmptyReadonlyArray(values, message = "Expected a non-empty array.") {
7786
+ const result = toNonEmptyReadonlyArray(values);
7787
+ if (!result) throw new Error(message);
7788
+ return result;
7789
+ }
7718
7790
  function makeBoundaryId(previousBlockId, nextBlockId) {
7719
7791
  return asDesignBoundaryId(`boundary:${encodeURIComponent(previousBlockId)}:${encodeURIComponent(nextBlockId)}`);
7720
7792
  }
@@ -7888,16 +7960,18 @@ var init_rawStyleFields = __esmMin((() => {
7888
7960
  "_frameStyles",
7889
7961
  "_sectionStyles",
7890
7962
  "_containerStyles",
7891
- "_cardStyles",
7892
- "_transitionStyles",
7893
- "styleGroup"
7963
+ "_cardStyles"
7894
7964
  ];
7895
7965
  themeV2BlockSpecificRawStyleFieldNames = [
7896
7966
  "_contentCardStyles",
7897
7967
  "_heroLayoutStyles",
7898
7968
  "_textCardStyles"
7899
7969
  ];
7900
- themeV2NormalEditorRawStyleFieldNames = [...themeV2RuntimeStyleFieldNames, ...themeV2BlockSpecificRawStyleFieldNames];
7970
+ themeV2NormalEditorRawStyleFieldNames = [
7971
+ ...themeV2RuntimeStyleFieldNames,
7972
+ ...themeV2BlockSpecificRawStyleFieldNames,
7973
+ "styleGroup"
7974
+ ];
7901
7975
  new Set(themeV2NormalEditorRawStyleFieldNames);
7902
7976
  [...themeV2RuntimeStyleFieldNames, ...themeV2BlockSpecificRawStyleFieldNames];
7903
7977
  pageDesignEditorBackgroundOverrideOwner = "page-design-editor";
@@ -7989,7 +8063,7 @@ var init_interactionPalette = __esmMin((() => {
7989
8063
  //#endregion
7990
8064
  //#region ../theme-core/src/boxBackgroundSchema.ts
7991
8065
  function normalizeAuthoringOpacity(value) {
7992
- if (!isRecord$16(value)) return value;
8066
+ if (!isRecord$17(value)) return value;
7993
8067
  return {
7994
8068
  ...value,
7995
8069
  opacity: normalizeOpacity(value.opacity)
@@ -7998,7 +8072,7 @@ function normalizeAuthoringOpacity(value) {
7998
8072
  function normalizeOpacity(value) {
7999
8073
  return typeof value === "number" && value > 1 && value <= 100 ? value / 100 : value;
8000
8074
  }
8001
- function isRecord$16(value) {
8075
+ function isRecord$17(value) {
8002
8076
  return typeof value === "object" && value !== null && !Array.isArray(value);
8003
8077
  }
8004
8078
  var boxBackgroundOverlaySchema, commonSchema, mediaPresentationSchema, boxBackgroundSchema;
@@ -8817,7 +8891,7 @@ var init_media$1 = __esmMin((() => {
8817
8891
  /**
8818
8892
  * Strict record guard: excludes null, primitives, and arrays.
8819
8893
  */
8820
- function isRecord$15(value) {
8894
+ function isRecord$16(value) {
8821
8895
  return typeof value === "object" && value !== null && !Array.isArray(value);
8822
8896
  }
8823
8897
  var init_typeGuards = __esmMin((() => {}));
@@ -8967,7 +9041,7 @@ var init_bookingConsent = __esmMin((() => {
8967
9041
  checkboxLabel: z.string().min(1)
8968
9042
  });
8969
9043
  z.union([blockBookingConsentSchema, bookingRequirementConsentFieldSchema]);
8970
- })), publicEventLoaderQuerySchemaShape, eventPresetSchema, eventScheduleScopeSchema$2, publicEventRecurrenceSchema, eventDestinationModeSchema, publicEventDestinationSchema, eventVenueSchema, eventCategorySchema, accessRestrictionSchema, publicCurrencySchema, publicCentsSchema, publicPositiveCentsSchema, publicIsoDateTimeSchema, publicEventAttendanceModeSchema, publicEventAttendanceModeAvailabilitySchema, publicEventPricingOptionAvailabilitySchema, publicEventPricingOptionSchema, publicEventPricingSchema, publicTeamMemberSummarySchema, publicEventBookingAvailabilitySchema, publicRunSessionSchema, publicWholeSeriesRunSchema, publicEventBaseSchema, publicEventSchema, publicEventsArraySchema;
9044
+ })), publicEventLoaderQuerySchemaShape, eventPresetSchema, eventScheduleScopeSchema$2, publicEventRecurrenceSchema, eventDestinationModeSchema, publicEventDestinationSchema, eventVenueSchema, eventCategorySchema, accessRestrictionSchema, publicCurrencySchema, publicCentsSchema, publicPositiveCentsSchema, publicIsoDateTimeSchema, publicEventAttendanceModeSchema, publicEventAttendanceModeAvailabilitySchema, publicEventPricingOptionAvailabilitySchema, publicEventPricingOptionSchema, publicEventPricingSchema, publicTeamMemberSummarySchema, publicEventBookingAvailabilitySchema, publicEventBookingClosedDetailSchema, publicRunSessionSchema, publicWholeSeriesRunSchema, publicEventBaseSchema, publicEventSchema, publicEventsArraySchema;
8971
9045
  var init_schemas = __esmMin((() => {
8972
9046
  init_media$1();
8973
9047
  init_richTextSchema();
@@ -8991,6 +9065,7 @@ var init_schemas = __esmMin((() => {
8991
9065
  seriesSlug: z.string().optional(),
8992
9066
  includeOccurrenceId: z.string().optional(),
8993
9067
  readIntent: z.string().optional(),
9068
+ detailAdmission: z.string().optional(),
8994
9069
  detailLevel: z.string().optional(),
8995
9070
  readProfile: z.string().optional()
8996
9071
  };
@@ -9148,11 +9223,11 @@ var init_schemas = __esmMin((() => {
9148
9223
  "series_archived",
9149
9224
  "parent_archived",
9150
9225
  "parent_inconsistent",
9151
- "not_public",
9152
9226
  "already_started",
9153
9227
  "enrollment_closed"
9154
9228
  ])
9155
9229
  })]);
9230
+ publicEventBookingClosedDetailSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("opensSoon") }), z.object({ kind: z.literal("paused") })]);
9156
9231
  publicRunSessionSchema = z.object({
9157
9232
  occurrenceId: z.string(),
9158
9233
  startsAt: z.string(),
@@ -9194,6 +9269,7 @@ var init_schemas = __esmMin((() => {
9194
9269
  destination: publicEventDestinationSchema,
9195
9270
  requiresRegistration: z.boolean(),
9196
9271
  bookingAvailability: publicEventBookingAvailabilitySchema,
9272
+ bookingClosedDetail: publicEventBookingClosedDetailSchema.optional(),
9197
9273
  deliveryMode: z.enum([
9198
9274
  "in_person",
9199
9275
  "online",
@@ -9922,7 +9998,7 @@ function encodePublicProductCategorySelector(selector) {
9922
9998
  };
9923
9999
  }
9924
10000
  }
9925
- function isRecord$14(value) {
10001
+ function isRecord$15(value) {
9926
10002
  return typeof value === "object" && value !== null;
9927
10003
  }
9928
10004
  function isJsonResponseContentType(contentType) {
@@ -9946,15 +10022,15 @@ async function parseJsonBody(response) {
9946
10022
  }
9947
10023
  }
9948
10024
  function isFieldError(value) {
9949
- return isRecord$14(value) && !Array.isArray(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string";
10025
+ return isRecord$15(value) && !Array.isArray(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string";
9950
10026
  }
9951
10027
  function fieldErrorsFromUnknown(value) {
9952
10028
  if (!Array.isArray(value)) return void 0;
9953
10029
  return value.every(isFieldError) ? value : void 0;
9954
10030
  }
9955
10031
  function getBlockApiErrorDetails(data) {
9956
- if (!isRecord$14(data)) return {};
9957
- const nestedError = isRecord$14(data.error) ? data.error : void 0;
10032
+ if (!isRecord$15(data)) return {};
10033
+ const nestedError = isRecord$15(data.error) ? data.error : void 0;
9958
10034
  const message = typeof nestedError?.message === "string" ? nestedError.message : typeof data.message === "string" ? data.message : void 0;
9959
10035
  const code = typeof nestedError?.code === "string" ? nestedError.code : typeof data.code === "string" ? data.code : void 0;
9960
10036
  const fieldErrors = fieldErrorsFromUnknown(nestedError?.fieldErrors) ?? fieldErrorsFromUnknown(data.fieldErrors);
@@ -10968,11 +11044,11 @@ var UnsafePathSegmentError = class extends Error {
10968
11044
  function assertSafePathSegments(segments) {
10969
11045
  for (const segment of segments) if (typeof segment === "string" && UNSAFE_PATH_SEGMENTS.has(segment)) throw new UnsafePathSegmentError(segment);
10970
11046
  }
10971
- function isRecord$13(value) {
11047
+ function isRecord$14(value) {
10972
11048
  return typeof value === "object" && value !== null && !Array.isArray(value);
10973
11049
  }
10974
11050
  function isPathContainer(value) {
10975
- return Array.isArray(value) || isRecord$13(value);
11051
+ return Array.isArray(value) || isRecord$14(value);
10976
11052
  }
10977
11053
  function getContainerValue(container, segment) {
10978
11054
  if (typeof segment === "number") return Array.isArray(container) ? container[segment] : void 0;
@@ -13652,6 +13728,7 @@ var init_social = __esmMin((() => {
13652
13728
  "SocialInstagram",
13653
13729
  "SocialLinkedIn",
13654
13730
  "SocialTikTok",
13731
+ "SocialWhatsApp",
13655
13732
  "SocialX",
13656
13733
  "SocialYouTube"
13657
13734
  ];
@@ -13688,6 +13765,7 @@ var init_social = __esmMin((() => {
13688
13765
  /* @__PURE__ */ jsx("path", { d: "M10.5 9v10" })
13689
13766
  ] })),
13690
13767
  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" })] })),
13768
+ 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" })] })),
13691
13769
  SocialX: createIcon("SocialXIcon", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "m5 4 14 16" }), /* @__PURE__ */ jsx("path", { d: "M19 4 5 20" })] })),
13692
13770
  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" })] }))
13693
13771
  };
@@ -13957,7 +14035,7 @@ function parseSiteIconStyleGuideVersionId(value) {
13957
14035
  //#region ../blocks/src/system/icons/value.ts
13958
14036
  init_semantic();
13959
14037
  const NO_BLOCK_ICON_VALUE = { kind: "none" };
13960
- function isRecord$12(value) {
14038
+ function isRecord$13(value) {
13961
14039
  return typeof value === "object" && value !== null && !Array.isArray(value);
13962
14040
  }
13963
14041
  function parseBlockIconValue(value) {
@@ -13965,7 +14043,7 @@ function parseBlockIconValue(value) {
13965
14043
  kind: "semantic",
13966
14044
  iconId: value
13967
14045
  };
13968
- if (!isRecord$12(value)) return NO_BLOCK_ICON_VALUE;
14046
+ if (!isRecord$13(value)) return NO_BLOCK_ICON_VALUE;
13969
14047
  switch (value.kind) {
13970
14048
  case "none": return NO_BLOCK_ICON_VALUE;
13971
14049
  case "semantic": return isSemanticIconId(value.iconId) ? {
@@ -14038,7 +14116,7 @@ function buildGeneratedBlockIconRenderAssetLookupFromUnknown(value) {
14038
14116
  if (!Array.isArray(value)) return { kind: "invalid_asset_list" };
14039
14117
  const inputs = [];
14040
14118
  for (const [index, item] of value.entries()) {
14041
- if (!isRecord$12(item)) return {
14119
+ if (!isRecord$13(item)) return {
14042
14120
  kind: "invalid_asset_entry",
14043
14121
  index,
14044
14122
  reason: "not_object"
@@ -14158,11 +14236,11 @@ function resolveFeatureCardGeneratedIconSizePx(value) {
14158
14236
  const parsed = parseFeatureCardGeneratedIconSizePx(value);
14159
14237
  return parsed.ok ? parsed.value : unsafeAsFeatureCardGeneratedIconSizePx(FEATURE_CARD_GENERATED_ICON_SIZE_PX.default);
14160
14238
  }
14161
- function isRecord$11(value) {
14239
+ function isRecord$12(value) {
14162
14240
  return typeof value === "object" && value !== null && !Array.isArray(value);
14163
14241
  }
14164
14242
  function toPlainTextFromTipTap(value) {
14165
- if (!isRecord$11(value)) return "";
14243
+ if (!isRecord$12(value)) return "";
14166
14244
  if (typeof value.text === "string") return value.text;
14167
14245
  const content = value.content;
14168
14246
  if (!Array.isArray(content)) return "";
@@ -15453,18 +15531,43 @@ const publicAppointmentServiceSchema = z.object({
15453
15531
  pricing: publicPricingSchema.optional(),
15454
15532
  priceAmount: z.number().nullable(),
15455
15533
  priceCurrency: z.string().nullable(),
15456
- location: publicLocationSchema
15534
+ location: publicLocationSchema,
15535
+ categoryId: z.string().nullable().optional().transform((value) => value ?? null)
15536
+ });
15537
+ const publicServiceCategorySchema = z.object({
15538
+ id: z.string().min(1),
15539
+ name: z.string(),
15540
+ description: z.string().nullable().optional().transform((value) => value ?? null)
15457
15541
  });
15458
- const publicAppointmentServicesSchema = z.array(publicAppointmentServiceSchema);
15542
+ /**
15543
+ * The loader hands over the catalogue `{ services, categories }` (issue 1403).
15544
+ * A bare services array is still accepted and read as a site with no
15545
+ * categories, so callers from before categories keep the flat list.
15546
+ */
15547
+ const publicAppointmentServicesSchema = z.union([z.array(publicAppointmentServiceSchema).transform((services) => ({
15548
+ services,
15549
+ categories: []
15550
+ })), z.object({
15551
+ services: z.array(publicAppointmentServiceSchema),
15552
+ categories: z.array(publicServiceCategorySchema)
15553
+ })]);
15459
15554
  const EMPTY_MESSAGE = "No appointments are available right now.";
15460
15555
  const ERROR_MESSAGE = "Appointments are temporarily unavailable. Please try again shortly.";
15556
+ const OTHER_GROUP_KEY = "other";
15557
+ const OTHER_GROUP_HEADING = "Other";
15461
15558
  function parseServices(value) {
15462
15559
  const result = publicAppointmentServicesSchema.safeParse(value);
15463
15560
  return result.success ? {
15464
15561
  kind: "valid",
15465
- services: result.data
15562
+ services: result.data.services,
15563
+ categories: result.data.categories
15466
15564
  } : { kind: "invalid" };
15467
15565
  }
15566
+ function readCategoryFilter(value) {
15567
+ if (typeof value !== "string") return null;
15568
+ const trimmed = value.trim();
15569
+ return trimmed.length > 0 ? trimmed : null;
15570
+ }
15468
15571
  function formatAppointmentDuration(durationMinutes) {
15469
15572
  return `${durationMinutes} min`;
15470
15573
  }
@@ -15538,8 +15641,44 @@ function buildRows(services, pathname, servicePathPrefix) {
15538
15641
  detailsHref: servicePathPrefix && service.slug ? `${servicePathPrefix.replace(/\/$/, "")}/${encodeURIComponent(service.slug)}` : null
15539
15642
  }));
15540
15643
  }
15644
+ const EMPTY_VIEW = {
15645
+ kind: "empty",
15646
+ message: EMPTY_MESSAGE
15647
+ };
15648
+ function categoryGroup(category, services, toRows) {
15649
+ return {
15650
+ key: category.id,
15651
+ heading: category.name,
15652
+ intro: category.description,
15653
+ rows: toRows(services)
15654
+ };
15655
+ }
15656
+ function viewFromLayout(layout, toRows) {
15657
+ switch (layout.kind) {
15658
+ case "uncategorized-site": return layout.services.length === 0 ? EMPTY_VIEW : {
15659
+ kind: "items",
15660
+ rows: toRows(layout.services)
15661
+ };
15662
+ case "categorized-site": {
15663
+ const groups = layout.groups.map((group) => categoryGroup(group.category, group.services, toRows));
15664
+ const other = layout.other.length === 0 ? [] : [{
15665
+ key: OTHER_GROUP_KEY,
15666
+ heading: OTHER_GROUP_HEADING,
15667
+ intro: null,
15668
+ rows: toRows(layout.other)
15669
+ }];
15670
+ const allGroups = [...groups, ...other];
15671
+ return allGroups.length === 0 ? EMPTY_VIEW : {
15672
+ kind: "groups",
15673
+ groups: allGroups
15674
+ };
15675
+ }
15676
+ default: return assertNever$22(layout);
15677
+ }
15678
+ }
15541
15679
  function buildAppointmentServicesView(input) {
15542
- if (!input.pathname) return {
15680
+ const pathname = input.pathname;
15681
+ if (!pathname) return {
15543
15682
  kind: "error",
15544
15683
  message: ERROR_MESSAGE
15545
15684
  };
@@ -15548,76 +15687,128 @@ function buildAppointmentServicesView(input) {
15548
15687
  kind: "error",
15549
15688
  message: ERROR_MESSAGE
15550
15689
  };
15551
- if (parsed.services.length === 0) return {
15552
- kind: "empty",
15553
- message: EMPTY_MESSAGE
15554
- };
15555
- return {
15556
- kind: "items",
15557
- rows: buildRows(parsed.services, input.pathname, input.servicePathPrefix ?? null)
15558
- };
15690
+ const toRows = (services) => buildRows(services, pathname, input.servicePathPrefix ?? null);
15691
+ const view = selectCategoryView(parsed.categories, parsed.services, readCategoryFilter(input.categoryId));
15692
+ switch (view.kind) {
15693
+ case "single": return view.group.services.length === 0 ? EMPTY_VIEW : {
15694
+ kind: "groups",
15695
+ groups: [categoryGroup(view.group.category, view.group.services, toRows)]
15696
+ };
15697
+ case "all":
15698
+ case "fallback-all": return viewFromLayout(view.layout, toRows);
15699
+ default: return assertNever$22(view);
15700
+ }
15701
+ }
15702
+ function AppointmentServiceRows({ rows, RowTitle }) {
15703
+ return /* @__PURE__ */ jsx("div", {
15704
+ className: "appointment-services-rows rb-flex rb-flex-col",
15705
+ children: rows.map((row) => /* @__PURE__ */ jsxs("article", {
15706
+ className: "appointment-services-row rb-border-b rb-border-border rb-py-4",
15707
+ children: [
15708
+ /* @__PURE__ */ jsxs("div", {
15709
+ className: "appointment-services-row-main rb-min-w-0",
15710
+ children: [
15711
+ /* @__PURE__ */ jsx(RowTitle, {
15712
+ className: "appointment-services-row-title rb-text-lg rb-font-medium rb-leading-tight",
15713
+ children: row.detailsHref ? /* @__PURE__ */ jsx("a", {
15714
+ href: row.detailsHref,
15715
+ children: row.title
15716
+ }) : row.title
15717
+ }),
15718
+ row.description ? /* @__PURE__ */ jsx("p", {
15719
+ className: "appointment-services-row-description rb-text-sm rb-leading-6 status-muted",
15720
+ children: row.description
15721
+ }) : null,
15722
+ /* @__PURE__ */ jsxs("p", {
15723
+ className: "appointment-services-row-meta rb-caption status-muted",
15724
+ children: [row.durationLabel, row.locationLabel ? ` · ${row.locationLabel}` : ""]
15725
+ })
15726
+ ]
15727
+ }),
15728
+ row.priceLabel ? /* @__PURE__ */ jsx("span", {
15729
+ className: "appointment-services-row-price rb-text-lg",
15730
+ children: row.priceLabel
15731
+ }) : null,
15732
+ /* @__PURE__ */ jsx(ThemedButtonLink, {
15733
+ variant: "secondary",
15734
+ size: "sm",
15735
+ extraClassName: "appointment-services-row-action",
15736
+ href: row.href,
15737
+ label: "Check times",
15738
+ screenReaderSuffix: ` for ${row.title}`
15739
+ })
15740
+ ]
15741
+ }, row.id))
15742
+ });
15743
+ }
15744
+ function AppointmentServiceGroups({ groups, hasSectionHeading, idPrefix }) {
15745
+ const GroupHeading = hasSectionHeading ? "h3" : "h2";
15746
+ const RowTitle = hasSectionHeading ? "h4" : "h3";
15747
+ return /* @__PURE__ */ jsx("div", {
15748
+ className: "appointment-services-groups rb-flex rb-flex-col rb-gap-10",
15749
+ children: groups.map((group) => {
15750
+ const headingId = `${idPrefix}-group-${group.key}`;
15751
+ return /* @__PURE__ */ jsxs("section", {
15752
+ className: "appointment-services-group rb-flex rb-flex-col rb-gap-2",
15753
+ "aria-labelledby": headingId,
15754
+ "data-appointment-services-group": group.key,
15755
+ children: [/* @__PURE__ */ jsxs("div", {
15756
+ className: "appointment-services-group-heading",
15757
+ children: [/* @__PURE__ */ jsx(GroupHeading, {
15758
+ id: headingId,
15759
+ className: "appointment-services-group-title rb-text-sm rb-font-semibold rb-uppercase rb-tracking-wide",
15760
+ children: group.heading
15761
+ }), group.intro ? /* @__PURE__ */ jsx("p", {
15762
+ className: "appointment-services-group-intro rb-text-sm rb-leading-6 rb-max-w-prose status-muted",
15763
+ children: group.intro
15764
+ }) : null]
15765
+ }), /* @__PURE__ */ jsx(AppointmentServiceRows, {
15766
+ rows: group.rows,
15767
+ RowTitle
15768
+ })]
15769
+ }, group.key);
15770
+ })
15771
+ });
15559
15772
  }
15560
- function AppointmentServicesNode({ sectionHeading, services, pathname, servicePathPrefix, className }) {
15773
+ function AppointmentServicesNode({ sectionHeading, categoryId, services, pathname, servicePathPrefix, blockId, className }) {
15561
15774
  const view = buildAppointmentServicesView({
15562
15775
  services,
15776
+ categoryId,
15563
15777
  pathname: typeof pathname === "string" ? pathname : void 0,
15564
15778
  servicePathPrefix: typeof servicePathPrefix === "string" ? servicePathPrefix : null
15565
15779
  });
15566
15780
  const rootClassName = `appointment-services ${className ?? ""}`.trim();
15567
- const RowTitle = sectionHeading ? "h3" : "h2";
15568
- if (view.kind === "error" || view.kind === "empty") return /* @__PURE__ */ jsx("div", {
15569
- className: rootClassName,
15570
- "data-appointment-services-state": view.kind,
15571
- children: /* @__PURE__ */ jsx("p", {
15572
- className: "rb-caption status-muted",
15573
- role: "status",
15574
- children: view.message
15575
- })
15576
- });
15577
- return /* @__PURE__ */ jsx("div", {
15578
- className: `${rootClassName} rb-flex rb-flex-col`,
15579
- "data-appointment-services-state": "populated",
15580
- children: /* @__PURE__ */ jsx("div", {
15581
- className: "appointment-services-rows rb-flex rb-flex-col",
15582
- children: view.rows.map((row) => /* @__PURE__ */ jsxs("article", {
15583
- className: "appointment-services-row rb-border-b rb-border-border rb-py-4",
15584
- children: [
15585
- /* @__PURE__ */ jsxs("div", {
15586
- className: "appointment-services-row-main rb-min-w-0",
15587
- children: [
15588
- /* @__PURE__ */ jsx(RowTitle, {
15589
- className: "appointment-services-row-title rb-text-lg rb-font-medium rb-leading-tight",
15590
- children: row.detailsHref ? /* @__PURE__ */ jsx("a", {
15591
- href: row.detailsHref,
15592
- children: row.title
15593
- }) : row.title
15594
- }),
15595
- row.description ? /* @__PURE__ */ jsx("p", {
15596
- className: "appointment-services-row-description rb-text-sm rb-leading-6 status-muted",
15597
- children: row.description
15598
- }) : null,
15599
- /* @__PURE__ */ jsxs("p", {
15600
- className: "appointment-services-row-meta rb-caption status-muted",
15601
- children: [row.durationLabel, row.locationLabel ? ` · ${row.locationLabel}` : ""]
15602
- })
15603
- ]
15604
- }),
15605
- row.priceLabel ? /* @__PURE__ */ jsx("span", {
15606
- className: "appointment-services-row-price rb-text-lg",
15607
- children: row.priceLabel
15608
- }) : null,
15609
- /* @__PURE__ */ jsx(ThemedButtonLink, {
15610
- variant: "secondary",
15611
- size: "sm",
15612
- extraClassName: "appointment-services-row-action",
15613
- href: row.href,
15614
- label: "Check times",
15615
- screenReaderSuffix: ` for ${row.title}`
15616
- })
15617
- ]
15618
- }, row.id))
15619
- })
15620
- });
15781
+ const hasSectionHeading = Boolean(sectionHeading);
15782
+ switch (view.kind) {
15783
+ case "error":
15784
+ case "empty": return /* @__PURE__ */ jsx("div", {
15785
+ className: rootClassName,
15786
+ "data-appointment-services-state": view.kind,
15787
+ children: /* @__PURE__ */ jsx("p", {
15788
+ className: "rb-caption status-muted",
15789
+ role: "status",
15790
+ children: view.message
15791
+ })
15792
+ });
15793
+ case "items": return /* @__PURE__ */ jsx("div", {
15794
+ className: `${rootClassName} rb-flex rb-flex-col`,
15795
+ "data-appointment-services-state": "populated",
15796
+ children: /* @__PURE__ */ jsx(AppointmentServiceRows, {
15797
+ rows: view.rows,
15798
+ RowTitle: hasSectionHeading ? "h3" : "h2"
15799
+ })
15800
+ });
15801
+ case "groups": return /* @__PURE__ */ jsx("div", {
15802
+ className: `${rootClassName} rb-flex rb-flex-col`,
15803
+ "data-appointment-services-state": "populated",
15804
+ children: /* @__PURE__ */ jsx(AppointmentServiceGroups, {
15805
+ groups: view.groups,
15806
+ hasSectionHeading,
15807
+ idPrefix: `appointment-services-${blockId ?? "block"}`
15808
+ })
15809
+ });
15810
+ default: return assertNever$22(view);
15811
+ }
15621
15812
  }
15622
15813
  //#endregion
15623
15814
  //#region ../blocks/src/contracts/runtime.ts
@@ -15786,7 +15977,7 @@ function successSubmissionFeedback(message) {
15786
15977
  message
15787
15978
  };
15788
15979
  }
15789
- function isRecord$9(value) {
15980
+ function isRecord$10(value) {
15790
15981
  return typeof value === "object" && value !== null && !Array.isArray(value);
15791
15982
  }
15792
15983
  function deepEqualJsonValue(left, right) {
@@ -15796,8 +15987,8 @@ function deepEqualJsonValue(left, right) {
15796
15987
  if (left.length !== right.length) return false;
15797
15988
  return left.every((item, index) => deepEqualJsonValue(item, right[index]));
15798
15989
  }
15799
- if (isRecord$9(left) || isRecord$9(right)) {
15800
- if (!isRecord$9(left) || !isRecord$9(right)) return false;
15990
+ if (isRecord$10(left) || isRecord$10(right)) {
15991
+ if (!isRecord$10(left) || !isRecord$10(right)) return false;
15801
15992
  const leftKeys = Object.keys(left).sort();
15802
15993
  if (!deepEqualJsonValue(leftKeys, Object.keys(right).sort())) return false;
15803
15994
  return leftKeys.every((key) => deepEqualJsonValue(left[key], right[key]));
@@ -15979,12 +16170,12 @@ function resolveBookingSiteId(siteId, form) {
15979
16170
  return siteId || form?.siteId || "";
15980
16171
  }
15981
16172
  function normalizeBookingFormConfig(form) {
15982
- if (!isRecord$8(form)) return null;
16173
+ if (!isRecord$9(form)) return null;
15983
16174
  const id = readNonEmptyString(form.id);
15984
16175
  const name = readNonEmptyString(form.name);
15985
16176
  if (!id || !name) return null;
15986
- const schemaSource = isRecord$8(form.schema) ? form.schema : isRecord$8(form.schemaJson) ? form.schemaJson : null;
15987
- const settingsSource = isRecord$8(form.settings) ? form.settings : isRecord$8(form.settingsJson) ? form.settingsJson : null;
16177
+ const schemaSource = isRecord$9(form.schema) ? form.schema : isRecord$9(form.schemaJson) ? form.schemaJson : null;
16178
+ const settingsSource = isRecord$9(form.settings) ? form.settings : isRecord$9(form.settingsJson) ? form.settingsJson : null;
15988
16179
  return {
15989
16180
  id,
15990
16181
  name,
@@ -16004,8 +16195,8 @@ function normalizeBookingServices(services) {
16004
16195
  };
16005
16196
  if (!services) return [];
16006
16197
  if (Array.isArray(services)) return services.filter(isBookingFormService).map(normalizeService);
16007
- if (isRecord$8(services) && "services" in services) return Array.isArray(services.services) ? services.services.filter(isBookingFormService).map(normalizeService) : [];
16008
- return isRecord$8(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
16198
+ if (isRecord$9(services) && "services" in services) return Array.isArray(services.services) ? services.services.filter(isBookingFormService).map(normalizeService) : [];
16199
+ return isRecord$9(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
16009
16200
  }
16010
16201
  function normalizeBookingSchema(value) {
16011
16202
  if (!value) return void 0;
@@ -16016,7 +16207,7 @@ function normalizeBookingSchema(value) {
16016
16207
  };
16017
16208
  }
16018
16209
  function readBookingFormField(value) {
16019
- if (!isRecord$8(value)) return null;
16210
+ if (!isRecord$9(value)) return null;
16020
16211
  const id = readNonEmptyString(value.id);
16021
16212
  const label = readNonEmptyString(value.label);
16022
16213
  const type = readBookingFieldType(value.type);
@@ -16059,7 +16250,7 @@ function readBookingFieldOptions(value) {
16059
16250
  return options.length > 0 ? options : void 0;
16060
16251
  }
16061
16252
  function readBookingFieldOption(value) {
16062
- if (!isRecord$8(value)) return null;
16253
+ if (!isRecord$9(value)) return null;
16063
16254
  const optionValue = readNonEmptyString(value.value);
16064
16255
  const label = readNonEmptyString(value.label);
16065
16256
  return optionValue && label ? {
@@ -16079,7 +16270,7 @@ function normalizeBookingSettings(value) {
16079
16270
  };
16080
16271
  }
16081
16272
  function isBookingFormService(value) {
16082
- return isRecord$8(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string" && (value.consent === void 0 || blockBookingConsentSchema.safeParse(value.consent).success);
16273
+ return isRecord$9(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string" && (value.consent === void 0 || blockBookingConsentSchema.safeParse(value.consent).success);
16083
16274
  }
16084
16275
  function collectBookingCustomFields(data) {
16085
16276
  const customFields = {};
@@ -16129,7 +16320,7 @@ function readStringArray(value) {
16129
16320
  const strings = value.filter((item) => typeof item === "string");
16130
16321
  return strings.length > 0 ? strings : void 0;
16131
16322
  }
16132
- function isRecord$8(value) {
16323
+ function isRecord$9(value) {
16133
16324
  return typeof value === "object" && value !== null;
16134
16325
  }
16135
16326
  function getBookingServicePaymentSelectionMode(service) {
@@ -16329,6 +16520,32 @@ var init_booking_form_state = __esmMin((() => {
16329
16520
  init_booking_form_reserved_fields();
16330
16521
  init_booking_form_consent();
16331
16522
  }));
16523
+ //#endregion
16524
+ //#region ../blocks/src/system/runtime/nodes/booking-form.service-categories.ts
16525
+ function isRecord$8(value) {
16526
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16527
+ }
16528
+ function readCategory(value) {
16529
+ if (!isRecord$8(value)) return [];
16530
+ const { id, name, description } = value;
16531
+ if (typeof id !== "string" || id.trim() === "" || typeof name !== "string") return [];
16532
+ return [{
16533
+ id,
16534
+ name,
16535
+ description: typeof description === "string" ? description : null
16536
+ }];
16537
+ }
16538
+ /**
16539
+ * The ordered service categories carried by a catalogue-shaped services
16540
+ * source (`{ services, categories }`). Every other shape has none, which
16541
+ * reads as a site without categories. Order is preserved: the database owns
16542
+ * it.
16543
+ */
16544
+ function normalizeBookingServiceCategories(services) {
16545
+ if (!isRecord$8(services) || !Array.isArray(services.categories)) return [];
16546
+ return services.categories.flatMap(readCategory);
16547
+ }
16548
+ var init_booking_form_service_categories = __esmMin((() => {}));
16332
16549
  var bookingFieldOptionSchema, standardBookingFieldRules, standardBookingFieldSchema, bookingFieldSchema, bookingSettingsSchema, bookingSourceFormSchema;
16333
16550
  var init_booking_runtime_sources = __esmMin((() => {
16334
16551
  init_noop();
@@ -16413,11 +16630,12 @@ function buildBookingFormInteractiveIslandProps(props) {
16413
16630
  source: props
16414
16631
  });
16415
16632
  }
16416
- var blockBookingPaymentTermsAvailabilitySchema, bookingServiceSchema, bookingServicesSourceSchema, bookingConfigSchema, bookingSourceSchema, bookingDisplaySchema, bookingHydrationSchema, bookingRenderSchema, bookingFormIslandMetaCodec, bookingFormIslandMetaKeys, bookingFormBlockRuntime, bookingFormInteractiveIslandPropsCodec;
16633
+ var blockBookingPaymentTermsAvailabilitySchema, bookingServiceSchema, bookingServiceCategorySchema, bookingServicesSourceSchema, bookingConfigSchema, bookingSourceSchema, bookingDisplaySchema, bookingHydrationSchema, bookingRenderSchema, bookingFormIslandMetaCodec, bookingFormIslandMetaKeys, bookingFormBlockRuntime, bookingFormInteractiveIslandPropsCodec;
16417
16634
  var init_booking_form_interactive = __esmMin((() => {
16418
16635
  init_islands();
16419
16636
  init_runtime();
16420
16637
  init_booking_form_state();
16638
+ init_booking_form_service_categories();
16421
16639
  init_booking_runtime_sources();
16422
16640
  init_bookingPaymentTerms();
16423
16641
  init_bookingConsent();
@@ -16440,11 +16658,20 @@ var init_booking_form_interactive = __esmMin((() => {
16440
16658
  priceCurrency: z.string().nullable().optional(),
16441
16659
  paymentTerms: blockBookingPaymentTermsSchema.optional(),
16442
16660
  paymentTermsAvailability: blockBookingPaymentTermsAvailabilitySchema.optional(),
16443
- consent: blockBookingConsentSchema.optional()
16661
+ consent: blockBookingConsentSchema.optional(),
16662
+ categoryId: z.string().nullable().optional()
16663
+ });
16664
+ bookingServiceCategorySchema = z.object({
16665
+ id: z.string(),
16666
+ name: z.string(),
16667
+ description: z.string().nullable()
16444
16668
  });
16445
16669
  bookingServicesSourceSchema = z.union([
16446
16670
  z.array(bookingServiceSchema),
16447
- z.object({ services: z.array(bookingServiceSchema).optional() }),
16671
+ z.object({
16672
+ services: z.array(bookingServiceSchema).optional(),
16673
+ categories: z.array(bookingServiceCategorySchema).optional()
16674
+ }),
16448
16675
  z.record(z.string(), bookingServiceSchema)
16449
16676
  ]);
16450
16677
  bookingConfigSchema = z.object({
@@ -16497,6 +16724,7 @@ var init_booking_form_interactive = __esmMin((() => {
16497
16724
  rebookContext: z.object({ kind: z.literal("portal_rebook") }).nullable().optional(),
16498
16725
  form: bookingConfigSchema.nullable(),
16499
16726
  services: z.array(bookingServiceSchema),
16727
+ serviceCategories: z.array(bookingServiceCategorySchema).optional(),
16500
16728
  spamProtectionEnabled: z.boolean().optional()
16501
16729
  });
16502
16730
  bookingRenderSchema = z.object({
@@ -16532,6 +16760,7 @@ var init_booking_form_interactive = __esmMin((() => {
16532
16760
  rebookContext: source.rebookContext ?? null,
16533
16761
  form,
16534
16762
  services,
16763
+ serviceCategories: normalizeBookingServiceCategories(source.services),
16535
16764
  spamProtectionEnabled: source.spamProtectionEnabled
16536
16765
  }
16537
16766
  };
@@ -17333,7 +17562,6 @@ function closedEventBookingLabel(reason) {
17333
17562
  case "series_archived":
17334
17563
  case "parent_archived":
17335
17564
  case "parent_inconsistent":
17336
- case "not_public":
17337
17565
  case "already_started":
17338
17566
  case "enrollment_closed": return "Registration closed";
17339
17567
  default: return assertNever$22(reason);
@@ -23071,17 +23299,256 @@ var init_cardRoleVocabulary = __esmMin((() => {
23071
23299
  Object.keys(legacyItemSurfaceCardRoles);
23072
23300
  cardRoleWithLegacyAliasSchema = z.preprocess((value) => cardRoleFromLegacyVocabulary(value) ?? value, cardRoleSchema);
23073
23301
  }));
23074
- var init_compositionBudget = __esmMin((() => {
23075
- init_types$8();
23302
+ var init_runtimeBackgroundResolution = __esmMin((() => {
23303
+ init_boxBackgroundSchema();
23304
+ init_surfaces();
23305
+ init_rawStyleFields();
23076
23306
  }));
23077
- var defaultHeaderBrandPreferences;
23078
- var init_compilationPlan = __esmMin((() => {
23079
- defaultHeaderBrandPreferences = {
23080
- brandDisplay: "auto",
23081
- logoSize: "auto"
23082
- };
23307
+ var transitionCatalogueFamilies, mediumHeight, compactHeight, largeHeight, transitionCatalogueEntries;
23308
+ var init_transitionCatalogue = __esmMin((() => {
23309
+ init_types$8();
23310
+ transitionCatalogueFamilies = [
23311
+ {
23312
+ id: "gradient",
23313
+ label: "Gradient"
23314
+ },
23315
+ {
23316
+ id: "wave",
23317
+ label: "Wave"
23318
+ },
23319
+ {
23320
+ id: "angle",
23321
+ label: "Angle"
23322
+ }
23323
+ ];
23324
+ mediumHeight = "6rem + 2px";
23325
+ compactHeight = "4.5rem + 2px";
23326
+ largeHeight = "7rem + 2px";
23327
+ transitionCatalogueEntries = defineExactCatalog()([
23328
+ {
23329
+ intent: "soft-fade-short",
23330
+ optionId: asBoundaryOptionId("break:soft"),
23331
+ family: "gradient",
23332
+ variantId: "soft",
23333
+ label: "Soft",
23334
+ variantLabel: "Soft",
23335
+ treatment: {
23336
+ kind: "soft-fade",
23337
+ height: "medium",
23338
+ curve: "soft"
23339
+ },
23340
+ size: "medium",
23341
+ heightExpression: mediumHeight,
23342
+ shape: { kind: "fade" }
23343
+ },
23344
+ {
23345
+ intent: "soft-fade-long",
23346
+ optionId: asBoundaryOptionId("break:spacious"),
23347
+ family: "gradient",
23348
+ variantId: "spacious",
23349
+ label: "Spacious",
23350
+ variantLabel: "Spacious",
23351
+ treatment: {
23352
+ kind: "soft-fade",
23353
+ height: "large",
23354
+ curve: "spacious"
23355
+ },
23356
+ size: "spacious",
23357
+ heightExpression: "10rem",
23358
+ shape: { kind: "fade" }
23359
+ },
23360
+ {
23361
+ intent: "soft-fade-expansive",
23362
+ optionId: asBoundaryOptionId("break:expansive"),
23363
+ family: "gradient",
23364
+ variantId: "expansive",
23365
+ label: "Expansive",
23366
+ variantLabel: "Expansive",
23367
+ treatment: {
23368
+ kind: "soft-fade",
23369
+ height: "expansive",
23370
+ curve: "expansive"
23371
+ },
23372
+ size: "expansive",
23373
+ heightExpression: "14rem",
23374
+ shape: { kind: "fade" }
23375
+ },
23376
+ {
23377
+ intent: "wave-edge-balanced",
23378
+ optionId: asBoundaryOptionId("break:wave-balanced"),
23379
+ family: "wave",
23380
+ variantId: "balanced",
23381
+ label: "Balanced wave",
23382
+ variantLabel: "Balanced",
23383
+ treatment: {
23384
+ kind: "wave-edge",
23385
+ height: "medium",
23386
+ amplitude: "balanced",
23387
+ rhythm: "double",
23388
+ symmetry: "symmetrical"
23389
+ },
23390
+ size: "medium",
23391
+ heightExpression: mediumHeight,
23392
+ shape: {
23393
+ kind: "wave",
23394
+ mask: "balanced"
23395
+ }
23396
+ },
23397
+ {
23398
+ intent: "wave-edge",
23399
+ optionId: asBoundaryOptionId("break:wave"),
23400
+ family: "wave",
23401
+ variantId: "classic",
23402
+ label: "Classic wave",
23403
+ variantLabel: "Classic",
23404
+ treatment: {
23405
+ kind: "wave-edge",
23406
+ height: "medium",
23407
+ amplitude: "balanced",
23408
+ rhythm: "double",
23409
+ symmetry: "asymmetrical"
23410
+ },
23411
+ size: "medium",
23412
+ heightExpression: mediumHeight,
23413
+ shape: {
23414
+ kind: "wave",
23415
+ mask: "classic"
23416
+ }
23417
+ },
23418
+ {
23419
+ intent: "wave-edge-gentle",
23420
+ optionId: asBoundaryOptionId("break:wave-gentle"),
23421
+ family: "wave",
23422
+ variantId: "gentle",
23423
+ label: "Gentle wave",
23424
+ variantLabel: "Gentle",
23425
+ treatment: {
23426
+ kind: "wave-edge",
23427
+ height: "compact",
23428
+ amplitude: "subtle",
23429
+ rhythm: "single",
23430
+ symmetry: "symmetrical"
23431
+ },
23432
+ size: "compact",
23433
+ heightExpression: compactHeight,
23434
+ shape: {
23435
+ kind: "wave",
23436
+ mask: "gentle"
23437
+ }
23438
+ },
23439
+ {
23440
+ intent: "wave-edge-bold",
23441
+ optionId: asBoundaryOptionId("break:wave-bold"),
23442
+ family: "wave",
23443
+ variantId: "bold",
23444
+ label: "Bold wave",
23445
+ variantLabel: "Bold",
23446
+ treatment: {
23447
+ kind: "wave-edge",
23448
+ height: "large",
23449
+ amplitude: "bold",
23450
+ rhythm: "double",
23451
+ symmetry: "symmetrical"
23452
+ },
23453
+ size: "large",
23454
+ heightExpression: largeHeight,
23455
+ shape: {
23456
+ kind: "wave",
23457
+ mask: "bold"
23458
+ }
23459
+ },
23460
+ {
23461
+ intent: "wave-edge-drift",
23462
+ optionId: asBoundaryOptionId("break:wave-drift"),
23463
+ family: "wave",
23464
+ variantId: "drift",
23465
+ label: "Drift wave",
23466
+ variantLabel: "Drift",
23467
+ treatment: {
23468
+ kind: "wave-edge",
23469
+ height: "medium",
23470
+ amplitude: "balanced",
23471
+ rhythm: "double",
23472
+ symmetry: "drift"
23473
+ },
23474
+ size: "medium",
23475
+ heightExpression: mediumHeight,
23476
+ shape: {
23477
+ kind: "wave",
23478
+ mask: "drift"
23479
+ }
23480
+ },
23481
+ {
23482
+ intent: "angle-edge-subtle",
23483
+ optionId: asBoundaryOptionId("break:angle-subtle"),
23484
+ family: "angle",
23485
+ variantId: "subtle",
23486
+ label: "Subtle angle",
23487
+ variantLabel: "Subtle",
23488
+ treatment: {
23489
+ kind: "angle-edge",
23490
+ height: "compact",
23491
+ slope: "subtle",
23492
+ direction: "forward"
23493
+ },
23494
+ size: "compact",
23495
+ heightExpression: compactHeight,
23496
+ shape: {
23497
+ kind: "angle",
23498
+ polygon: {
23499
+ startTopPercent: 34,
23500
+ endTopPercent: 18
23501
+ }
23502
+ }
23503
+ },
23504
+ {
23505
+ intent: "angle-edge",
23506
+ optionId: asBoundaryOptionId("break:angle-balanced"),
23507
+ family: "angle",
23508
+ variantId: "balanced",
23509
+ label: "Balanced angle",
23510
+ variantLabel: "Balanced",
23511
+ treatment: {
23512
+ kind: "angle-edge",
23513
+ height: "medium",
23514
+ slope: "balanced",
23515
+ direction: "forward"
23516
+ },
23517
+ size: "medium",
23518
+ heightExpression: mediumHeight,
23519
+ shape: {
23520
+ kind: "angle",
23521
+ polygon: {
23522
+ startTopPercent: 42,
23523
+ endTopPercent: 8
23524
+ }
23525
+ }
23526
+ },
23527
+ {
23528
+ intent: "angle-edge-steep",
23529
+ optionId: asBoundaryOptionId("break:angle-steep"),
23530
+ family: "angle",
23531
+ variantId: "steep",
23532
+ label: "Steep angle",
23533
+ variantLabel: "Steep",
23534
+ treatment: {
23535
+ kind: "angle-edge",
23536
+ height: "large",
23537
+ slope: "steep",
23538
+ direction: "forward"
23539
+ },
23540
+ size: "large",
23541
+ heightExpression: largeHeight,
23542
+ shape: {
23543
+ kind: "angle",
23544
+ polygon: {
23545
+ startTopPercent: 54,
23546
+ endTopPercent: 0
23547
+ }
23548
+ }
23549
+ }
23550
+ ]);
23083
23551
  }));
23084
- var init_schemaVersionedJson = __esmMin((() => {}));
23085
23552
  //#endregion
23086
23553
  //#region ../theme-core/src/site-styles/sectionTransitions.ts
23087
23554
  function boundaryRuntimeMetrics(family, size, heightExpression) {
@@ -23092,46 +23559,120 @@ function boundaryRuntimeMetrics(family, size, heightExpression) {
23092
23559
  reserveCss: `calc((${heightExpression}) / 2)`
23093
23560
  };
23094
23561
  }
23095
- var noneBoundaryOption, softBoundaryOption, spaciousBoundaryOption;
23562
+ /**
23563
+ * The catalogue's compile-time coverage check guarantees an entry per
23564
+ * decorative intent, so a miss here is a broken invariant, not bad input.
23565
+ */
23566
+ function requireCatalogueValue(values, intent) {
23567
+ const value = values.get(intent);
23568
+ if (value === void 0) throw new Error(`Transition catalogue has no entry for ${intent}`);
23569
+ return value;
23570
+ }
23571
+ function boundaryOptionForTransitionIntent(intent) {
23572
+ return requireCatalogueValue(boundaryOptionByIntent, intent);
23573
+ }
23574
+ function transitionVariantForEntry(entry) {
23575
+ return {
23576
+ familyId: entry.family,
23577
+ variantId: entry.variantId,
23578
+ label: entry.variantLabel,
23579
+ boundaryOption: boundaryOptionForTransitionIntent(entry.intent)
23580
+ };
23581
+ }
23582
+ function stableTreatmentKey(treatment) {
23583
+ switch (treatment.kind) {
23584
+ case "none": return "none";
23585
+ case "soft-fade": return `${treatment.kind}:${treatment.height}:${treatment.curve}`;
23586
+ case "wave-edge": return [
23587
+ treatment.kind,
23588
+ treatment.height,
23589
+ treatment.amplitude,
23590
+ treatment.rhythm,
23591
+ treatment.symmetry
23592
+ ].join(":");
23593
+ case "angle-edge": return [
23594
+ treatment.kind,
23595
+ treatment.height,
23596
+ treatment.slope,
23597
+ treatment.direction
23598
+ ].join(":");
23599
+ case "legacy-unsupported": return `${treatment.kind}:${treatment.boundaryOptionId}`;
23600
+ }
23601
+ }
23602
+ var noneBoundaryOption, boundaryOptionByIntent, softBoundaryOption, spaciousBoundaryOption, boundaryOptions, noneTransitionVariant, sectionTransitionFamilies;
23096
23603
  var init_sectionTransitions = __esmMin((() => {
23097
23604
  init_types$8();
23098
- boundaryRuntimeMetrics("gradient", "medium", "6rem + 2px"), boundaryRuntimeMetrics("gradient", "spacious", "10rem"), boundaryRuntimeMetrics("gradient", "expansive", "14rem"), boundaryRuntimeMetrics("wave", "medium", "6rem + 2px"), boundaryRuntimeMetrics("wave", "medium", "6rem + 2px"), boundaryRuntimeMetrics("wave", "compact", "4.5rem + 2px"), boundaryRuntimeMetrics("wave", "large", "7rem + 2px"), boundaryRuntimeMetrics("wave", "medium", "6rem + 2px"), boundaryRuntimeMetrics("angle", "medium", "6rem + 2px"), boundaryRuntimeMetrics("angle", "compact", "4.5rem + 2px"), boundaryRuntimeMetrics("angle", "large", "7rem + 2px");
23605
+ init_transitionCatalogue();
23606
+ new Map(transitionCatalogueEntries.map((entry) => [entry.intent, entry]));
23607
+ new Map(transitionCatalogueEntries.map((entry) => [entry.intent, boundaryRuntimeMetrics(entry.family, entry.size, entry.heightExpression)]));
23099
23608
  noneBoundaryOption = {
23100
23609
  id: asBoundaryOptionId("break:none"),
23101
23610
  label: "None",
23102
23611
  transitionAfter: "none",
23103
23612
  treatment: { kind: "none" }
23104
23613
  };
23105
- softBoundaryOption = {
23106
- id: asBoundaryOptionId("break:soft"),
23107
- label: "Soft",
23108
- transitionAfter: "soft-fade-short",
23109
- treatment: {
23110
- kind: "soft-fade",
23111
- height: "medium",
23112
- curve: "soft"
23113
- }
23614
+ boundaryOptionByIntent = new Map(transitionCatalogueEntries.map((entry) => [entry.intent, {
23615
+ id: entry.optionId,
23616
+ label: entry.label,
23617
+ transitionAfter: entry.intent,
23618
+ treatment: entry.treatment
23619
+ }]));
23620
+ softBoundaryOption = boundaryOptionForTransitionIntent("soft-fade-short");
23621
+ spaciousBoundaryOption = boundaryOptionForTransitionIntent("soft-fade-long");
23622
+ boundaryOptionForTransitionIntent("soft-fade-expansive");
23623
+ boundaryOptionForTransitionIntent("wave-edge");
23624
+ boundaryOptionForTransitionIntent("wave-edge-balanced");
23625
+ boundaryOptionForTransitionIntent("wave-edge-gentle");
23626
+ boundaryOptionForTransitionIntent("wave-edge-bold");
23627
+ boundaryOptionForTransitionIntent("wave-edge-drift");
23628
+ boundaryOptionForTransitionIntent("angle-edge-subtle");
23629
+ boundaryOptionForTransitionIntent("angle-edge");
23630
+ boundaryOptionForTransitionIntent("angle-edge-steep");
23631
+ boundaryOptions = [noneBoundaryOption, ...transitionCatalogueEntries.map((entry) => boundaryOptionForTransitionIntent(entry.intent))];
23632
+ noneTransitionVariant = {
23633
+ familyId: "none",
23634
+ variantId: "none",
23635
+ label: "None",
23636
+ boundaryOption: noneBoundaryOption
23114
23637
  };
23115
- spaciousBoundaryOption = {
23116
- id: asBoundaryOptionId("break:spacious"),
23117
- label: "Spacious",
23118
- transitionAfter: "soft-fade-long",
23119
- treatment: {
23120
- kind: "soft-fade",
23121
- height: "large",
23122
- curve: "spacious"
23123
- }
23638
+ sectionTransitionFamilies = [{
23639
+ id: "none",
23640
+ label: "None",
23641
+ variants: [noneTransitionVariant]
23642
+ }, ...transitionCatalogueFamilies.map((family) => ({
23643
+ id: family.id,
23644
+ label: family.label,
23645
+ variants: requireNonEmptyReadonlyArray(transitionCatalogueEntries.filter((entry) => entry.family === family.id).map(transitionVariantForEntry), `Transition family ${family.id} has no catalogue entries.`)
23646
+ }))];
23647
+ new Map(boundaryOptions.map((option) => [option.id, option]));
23648
+ new Map(boundaryOptions.map((option) => [stableTreatmentKey(option.treatment), option]));
23649
+ new Map(sectionTransitionFamilies.flatMap((family) => family.variants.map((variant) => [variant.boundaryOption.id, variant])));
23650
+ new Map(sectionTransitionFamilies.map((family) => [family.id, family]));
23651
+ }));
23652
+ function requireThemeColorToken$1(value) {
23653
+ const token = parseThemeColorToken(value);
23654
+ if (!token) throw new Error(`Expected a theme color token. Received: ${value}`);
23655
+ return token;
23656
+ }
23657
+ var init_sectionBoundaryComposition = __esmMin((() => {
23658
+ init_themeColorTokens();
23659
+ init_runtimeBackgroundResolution();
23660
+ init_sectionTransitions();
23661
+ init_types$8();
23662
+ requireThemeColorToken$1("background");
23663
+ }));
23664
+ var init_compositionBudget = __esmMin((() => {
23665
+ init_types$8();
23666
+ init_sectionBoundaryComposition();
23667
+ }));
23668
+ var defaultHeaderBrandPreferences;
23669
+ var init_compilationPlan = __esmMin((() => {
23670
+ defaultHeaderBrandPreferences = {
23671
+ brandDisplay: "auto",
23672
+ logoSize: "auto"
23124
23673
  };
23125
- asBoundaryOptionId("break:expansive");
23126
- asBoundaryOptionId("break:wave");
23127
- asBoundaryOptionId("break:wave-balanced");
23128
- asBoundaryOptionId("break:wave-gentle");
23129
- asBoundaryOptionId("break:wave-bold");
23130
- asBoundaryOptionId("break:wave-drift");
23131
- asBoundaryOptionId("break:angle-subtle");
23132
- asBoundaryOptionId("break:angle-balanced");
23133
- asBoundaryOptionId("break:angle-steep");
23134
23674
  }));
23675
+ var init_schemaVersionedJson = __esmMin((() => {}));
23135
23676
  function addDuplicateStringIssues(ctx, input) {
23136
23677
  const seen = /* @__PURE__ */ new Set();
23137
23678
  const duplicates = /* @__PURE__ */ new Set();
@@ -23160,14 +23701,11 @@ function validateUniquePageDesignPins(intent, ctx) {
23160
23701
  label: "block Look pin"
23161
23702
  });
23162
23703
  addDuplicateStringIssues(ctx, {
23163
- values: intent.boundaryPins.map((pin) => boundaryKey(pin.previousBlockId, pin.nextBlockId)),
23704
+ values: intent.boundaryPins.map((pin) => makeBoundaryId(pin.previousBlockId, pin.nextBlockId)),
23164
23705
  path: ["boundaryPins"],
23165
23706
  label: "boundary pin"
23166
23707
  });
23167
23708
  }
23168
- function boundaryKey(previousBlockId, nextBlockId) {
23169
- return makeBoundaryId(previousBlockId, nextBlockId);
23170
- }
23171
23709
  var pageDesignUserPinnedAppearanceChoiceSchema, pageDesignUserPinnedBoundaryChoiceSchema, pageDesignBoundaryTreatmentSchema, pageDesignUserPinnedBoundaryTreatmentChoiceSchema, pageDesignBlockLookPinSchema, pageDesignBoundaryPinSchema, legacyPageDesignBoundaryPinSchema;
23172
23710
  var init_pageDesignIntent = __esmMin((() => {
23173
23711
  init_types$8();
@@ -26207,6 +26745,33 @@ var init_designState = __esmMin((() => {
26207
26745
  authority: styleAuthoritySchema
26208
26746
  }).strict();
26209
26747
  }));
26748
+ var init_pageFlowPresets = __esmMin((() => {
26749
+ init_sectionTransitions();
26750
+ init_types$8();
26751
+ asPageFlowPresetId("flow:quiet"), noneBoundaryOption.id, asPageFlowPresetId("flow:balanced"), softBoundaryOption.id, asPageFlowPresetId("flow:expressive"), spaciousBoundaryOption.id;
26752
+ })), init_lookResolution = __esmMin((() => {
26753
+ init_appearancePresets();
26754
+ init_types$8();
26755
+ }));
26756
+ var init_siteStyleBlockPolicies = __esmMin((() => {
26757
+ init_lookResolution();
26758
+ init_appearancePresets();
26759
+ init_pageDesignIntent();
26760
+ init_types$8();
26761
+ asPageFlowPresetId("generated-default");
26762
+ asSiteStyleId("site-style:south-west-twerk-school");
26763
+ asAppearancePresetId("hero-brand-scrim-fixed-frame");
26764
+ asLayoutVariantKey("classic");
26765
+ asLayoutVariantKey("micro");
26766
+ asBoundaryOptionId("break:wave");
26767
+ }));
26768
+ var init_joinTreatment = __esmMin((() => {
26769
+ init_pageFlowPresets();
26770
+ init_pageDesignIntent();
26771
+ init_sectionTransitions();
26772
+ init_siteStyleBlockPolicies();
26773
+ init_types$8();
26774
+ }));
26210
26775
  var init_imageSurfaceBackground = __esmMin((() => {
26211
26776
  init_types$8();
26212
26777
  }));
@@ -26247,8 +26812,9 @@ var init_runtimeCompiler = __esmMin((() => {
26247
26812
  init_styles();
26248
26813
  init_designState();
26249
26814
  init_types$8();
26815
+ init_joinTreatment();
26816
+ init_rawStyleFields();
26250
26817
  init_accessibleSurfaces();
26251
- init_sectionTransitions();
26252
26818
  init_tokens();
26253
26819
  init_gradients();
26254
26820
  init_imageSurfaceBackground();
@@ -26285,8 +26851,7 @@ var init_runtimeCompiler = __esmMin((() => {
26285
26851
  color: "surface",
26286
26852
  opacity: .88
26287
26853
  }
26288
- },
26289
- transitionClassName: (transition, fromSurface, toSurface) => transition === "none" ? null : `site-style-transition-${transition}-from-${fromSurface}-to-${toSurface}`
26854
+ }
26290
26855
  });
26291
26856
  defaultComponentRuntimeRules = defineComponentRuntimeRules({
26292
26857
  panelFrames: {
@@ -28022,22 +28587,12 @@ var init_curatedSiteStyles = __esmMin((() => {
28022
28587
  ];
28023
28588
  new Map(curatedSiteStyles.map((style) => [style.id, style]));
28024
28589
  }));
28025
- var init_pageDesignEditorCapabilities = __esmMin((() => {})), init_lookResolution = __esmMin((() => {
28026
- init_appearancePresets();
28027
- init_types$8();
28028
- }));
28029
- var init_siteStyleBlockPolicies = __esmMin((() => {
28030
- init_lookResolution();
28031
- init_appearancePresets();
28590
+ var init_pageDesignEditorCapabilities = __esmMin((() => {}));
28591
+ var init_pageDesignEditorBoundaries = __esmMin((() => {
28032
28592
  init_pageDesignIntent();
28033
- init_types$8();
28593
+ init_joinTreatment();
28034
28594
  init_sectionTransitions();
28035
- asPageFlowPresetId("generated-default");
28036
- asSiteStyleId("site-style:south-west-twerk-school");
28037
- asAppearancePresetId("hero-brand-scrim-fixed-frame");
28038
- asLayoutVariantKey("classic");
28039
- asLayoutVariantKey("micro");
28040
- asBoundaryOptionId("break:wave");
28595
+ init_types$8();
28041
28596
  }));
28042
28597
  var init_pageDesignEditorModel = __esmMin((() => {
28043
28598
  init_curatedSiteStyles();
@@ -28046,8 +28601,9 @@ var init_pageDesignEditorModel = __esmMin((() => {
28046
28601
  init_siteStyleBlockPolicies();
28047
28602
  init_pageDesignIntent();
28048
28603
  init_types$8();
28604
+ init_pageFlowPresets();
28605
+ init_pageDesignEditorBoundaries();
28049
28606
  init_sectionTransitions();
28050
- asPageFlowPresetId("flow:quiet"), noneBoundaryOption.id, asPageFlowPresetId("flow:balanced"), softBoundaryOption.id, asPageFlowPresetId("flow:expressive"), spaciousBoundaryOption.id;
28051
28607
  })), init_concretePalette = __esmMin((() => {
28052
28608
  init_colors();
28053
28609
  })), blockDesignFallback;
@@ -28056,7 +28612,6 @@ var init_resolver = __esmMin((() => {
28056
28612
  blockDesignFallback = {
28057
28613
  sectionSurface: "base",
28058
28614
  contentFrame: plainFrame,
28059
- transitionAfter: "none",
28060
28615
  sectionHeight: "default",
28061
28616
  heroLegibility: "none",
28062
28617
  imageSurfaceTreatment: { kind: "none" },
@@ -28067,7 +28622,7 @@ var init_resolver = __esmMin((() => {
28067
28622
  accordionIconTreatment: null,
28068
28623
  composition: null
28069
28624
  };
28070
- blockDesignFallback.sectionSurface, blockDesignFallback.contentFrame, blockDesignFallback.transitionAfter, blockDesignFallback.sectionHeight, blockDesignFallback.heroLegibility, blockDesignFallback.imageSurfaceTreatment, blockDesignFallback.imageSurfaceMotion, blockDesignFallback.tileTreatment, blockDesignFallback.eventListTreatment, blockDesignFallback.eyebrowTreatment, blockDesignFallback.accordionIconTreatment, blockDesignFallback.composition;
28625
+ blockDesignFallback.sectionSurface, blockDesignFallback.contentFrame, blockDesignFallback.sectionHeight, blockDesignFallback.heroLegibility, blockDesignFallback.imageSurfaceTreatment, blockDesignFallback.imageSurfaceMotion, blockDesignFallback.tileTreatment, blockDesignFallback.eventListTreatment, blockDesignFallback.eyebrowTreatment, blockDesignFallback.accordionIconTreatment, blockDesignFallback.composition;
28071
28626
  }));
28072
28627
  function parseSiteChromeBackgroundColorToken(value) {
28073
28628
  const token = parseBackgroundAppearanceColorToken(value);
@@ -28156,11 +28711,6 @@ var init_headerRecompile = __esmMin((() => {
28156
28711
  init_headerBrandPresentation();
28157
28712
  init_siteChromeNavPreferences();
28158
28713
  }));
28159
- var init_runtimeBackgroundResolution = __esmMin((() => {
28160
- init_boxBackgroundSchema();
28161
- init_surfaces();
28162
- init_rawStyleFields();
28163
- }));
28164
28714
  var init_previewRuntimeCapabilities = __esmMin((() => {
28165
28715
  init_types$8();
28166
28716
  }));
@@ -28180,8 +28730,8 @@ var init_previewCustomization = __esmMin((() => {
28180
28730
  init_types$8();
28181
28731
  }));
28182
28732
  var init_previewCompiler = __esmMin((() => {
28733
+ init_sectionTransitions();
28183
28734
  init_schema$2();
28184
- init_tokens();
28185
28735
  init_curated();
28186
28736
  init_concretePalette();
28187
28737
  init_curatedSiteStyles();
@@ -28191,7 +28741,6 @@ var init_previewCompiler = __esmMin((() => {
28191
28741
  init_headerRecompile();
28192
28742
  init_siteChromeNavPreferences();
28193
28743
  init_runtimeCompiler();
28194
- init_rawStyleFields();
28195
28744
  init_runtimeBackgroundResolution();
28196
28745
  init_previewRuntimeCapabilities();
28197
28746
  init_siteStyleThemeCompiler();
@@ -28201,6 +28750,7 @@ var init_previewCompiler = __esmMin((() => {
28201
28750
  init_pageDesignIntent();
28202
28751
  init_previewCustomization();
28203
28752
  init_curatedChoices();
28753
+ init_joinTreatment();
28204
28754
  init_types$8();
28205
28755
  }));
28206
28756
  var init_contentTemplateRuntimeCompiler = __esmMin((() => {
@@ -28302,6 +28852,7 @@ var init_generatedDesignPipeline = __esmMin((() => {
28302
28852
  init_curatedSiteStyles();
28303
28853
  init_lookResolution();
28304
28854
  init_headerOpeningSection();
28855
+ init_joinTreatment();
28305
28856
  init_resolver();
28306
28857
  init_runtimeCompiler();
28307
28858
  init_types$8();
@@ -28670,6 +29221,7 @@ var init_site_styles = __esmMin((() => {
28670
29221
  init_headerRecompile();
28671
29222
  init_headerOpeningForeground();
28672
29223
  init_headerOpeningSection();
29224
+ init_joinTreatment();
28673
29225
  init_lookResolution();
28674
29226
  init_lookOptionGroups();
28675
29227
  init_logoBranding();
@@ -28689,6 +29241,7 @@ var init_site_styles = __esmMin((() => {
28689
29241
  init_runtimeCompiler();
28690
29242
  init_runtimeBackgroundResolution();
28691
29243
  init_sectionTransitions();
29244
+ init_sectionBoundaryComposition();
28692
29245
  init_siteChromeBackgrounds();
28693
29246
  init_siteChromeNavPreferences();
28694
29247
  init_siteStyleThemeCompiler();
@@ -28696,6 +29249,7 @@ var init_site_styles = __esmMin((() => {
28696
29249
  init_sourceCatalogs();
28697
29250
  init_styleTags();
28698
29251
  init_tokenRecipes();
29252
+ init_transitionCatalogue();
28699
29253
  init_siteStyleTypography();
28700
29254
  init_themeParity();
28701
29255
  init_types$8();
@@ -29177,7 +29731,8 @@ var init_schema = __esmMin((() => {
29177
29731
  "eventCategories",
29178
29732
  "eventSeries",
29179
29733
  "venues",
29180
- "staffMembers"
29734
+ "staffMembers",
29735
+ "appointmentServiceCategories"
29181
29736
  ];
29182
29737
  mediaDeliverySchema = z.object({
29183
29738
  sizes: z.string().optional(),
@@ -36634,6 +37189,11 @@ function withOptionalPhone(fields, includePhone) {
36634
37189
  var init_newsletter_form_types = __esmMin((() => {}));
36635
37190
  //#endregion
36636
37191
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.interactive.ts
37192
+ function newsletterBlockSubmissionMessage(target) {
37193
+ if (target.tag === "block-source") return target.sourceRef ? null : NEWSLETTER_SIGNUPS_PAUSED_MESSAGE;
37194
+ if (target.tag !== "unpublished-preview") return null;
37195
+ 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.";
37196
+ }
36637
37197
  function buildNewsletterDisplay(source) {
36638
37198
  const context = source.mailingList;
36639
37199
  const form = context?.tag === "configured" ? context.form : {
@@ -36659,16 +37219,21 @@ function buildNewsletterDisplay(source) {
36659
37219
  }
36660
37220
  function projectNewsletterFormHydrationTarget(source) {
36661
37221
  switch (source.source) {
36662
- case "block": {
36663
- const sourceRef = buildMailingListSignupBlockSourceRef(source.blockId, source.contentVersionId);
36664
- if (sourceRef?.tag === "block_ref") return {
37222
+ case "block": switch (source.newsletterBlockSubmission.tag) {
37223
+ case "unpublished-form":
37224
+ case "unpublished-settings": return {
37225
+ tag: "unpublished-preview",
37226
+ reason: source.newsletterBlockSubmission.tag
37227
+ };
37228
+ case "ready": return {
36665
37229
  tag: "block-source",
36666
- sourceRef
37230
+ sourceRef: source.newsletterBlockSubmission.sourceRef
36667
37231
  };
36668
- return {
37232
+ case "unavailable": return {
36669
37233
  tag: "block-source",
36670
37234
  sourceRef: null
36671
37235
  };
37236
+ default: return assertNever$22(source.newsletterBlockSubmission);
36672
37237
  }
36673
37238
  case "signed-footer": return {
36674
37239
  tag: "signed-footer-source",
@@ -36737,6 +37302,7 @@ function buildNewsletterFormInteractiveIslandProps(props) {
36737
37302
  const decoded = newsletterFormBlockRuntime.contracts.source.decode(props);
36738
37303
  const source = isDecodeSuccess(decoded) ? decoded.value : {
36739
37304
  ...props,
37305
+ ...props.source === "block" ? { newsletterBlockSubmission: { tag: "unavailable" } } : {},
36740
37306
  mailingList: void 0,
36741
37307
  targeting: { mode: "default" }
36742
37308
  };
@@ -36760,13 +37326,14 @@ function parseRequiredMailingListSignedSourceToken(value) {
36760
37326
  if (!parsed) throw new Error("Mailing-list signup signed source ref requires token");
36761
37327
  return parsed;
36762
37328
  }
36763
- var newsletterButtonVariantSchema, mailingListContextSchema, mailingListTargetingSchema, newsletterFieldSchema, mailingListSignupBlockIdSchema, mailingListContentVersionIdSchema, signedMailingListSourceTokenSchema, newsletterSourceCommonSchema, newsletterSourceSchema, newsletterDisplaySchema, newsletterHydrationSchema, newsletterRenderSchema, newsletterIslandMetaCodec, newsletterIslandMetaKeys, newsletterFormBlockRuntime, newsletterFormInteractiveIslandPropsCodec;
37329
+ var NEWSLETTER_SIGNUPS_PAUSED_MESSAGE, newsletterButtonVariantSchema, mailingListContextSchema, mailingListTargetingSchema, newsletterFieldSchema, mailingListSignupBlockIdSchema, mailingListContentVersionIdSchema, mailingListSignupBlockSourceRefSchema, signedMailingListSourceTokenSchema, newsletterSourceCommonSchema, newsletterSourceSchema, newsletterDisplaySchema, newsletterHydrationSchema, newsletterRenderSchema, newsletterIslandMetaCodec, newsletterIslandMetaKeys, newsletterFormBlockRuntime, newsletterFormInteractiveIslandPropsCodec;
36764
37330
  var init_newsletter_form_interactive = __esmMin((() => {
36765
37331
  init_src$2();
36766
37332
  init_islands();
36767
37333
  init_runtime();
36768
37334
  init_newsletter_form_types();
36769
37335
  init_buttonRoles();
37336
+ NEWSLETTER_SIGNUPS_PAUSED_MESSAGE = "We can’t accept signups right now. Please try again later.";
36770
37337
  newsletterButtonVariantSchema = blockButtonRoleSchema;
36771
37338
  mailingListContextSchema = z.unknown().transform((value, ctx) => {
36772
37339
  const parsed = parseMailingListRuntimeContext(value);
@@ -36807,6 +37374,11 @@ var init_newsletter_form_interactive = __esmMin((() => {
36807
37374
  });
36808
37375
  return z.NEVER;
36809
37376
  });
37377
+ mailingListSignupBlockSourceRefSchema = z.object({
37378
+ tag: z.literal("block_ref"),
37379
+ blockId: mailingListSignupBlockIdSchema,
37380
+ contentVersionId: mailingListContentVersionIdSchema
37381
+ });
36810
37382
  signedMailingListSourceTokenSchema = z.string().trim().min(1).transform(parseRequiredMailingListSignedSourceToken);
36811
37383
  newsletterSourceCommonSchema = z.object({
36812
37384
  siteId: z.string().optional(),
@@ -36821,7 +37393,15 @@ var init_newsletter_form_interactive = __esmMin((() => {
36821
37393
  newsletterSourceCommonSchema.extend({
36822
37394
  source: z.literal("block"),
36823
37395
  blockId: z.string().nullable().optional(),
36824
- contentVersionId: z.string().nullable().optional()
37396
+ newsletterBlockSubmission: z.discriminatedUnion("tag", [
37397
+ z.object({
37398
+ tag: z.literal("ready"),
37399
+ sourceRef: mailingListSignupBlockSourceRefSchema
37400
+ }),
37401
+ z.object({ tag: z.literal("unpublished-form") }),
37402
+ z.object({ tag: z.literal("unpublished-settings") }),
37403
+ z.object({ tag: z.literal("unavailable") })
37404
+ ])
36825
37405
  }),
36826
37406
  newsletterSourceCommonSchema.extend({
36827
37407
  source: z.literal("signed-footer"),
@@ -36850,11 +37430,7 @@ var init_newsletter_form_interactive = __esmMin((() => {
36850
37430
  target: z.discriminatedUnion("tag", [
36851
37431
  z.object({
36852
37432
  tag: z.literal("block-source"),
36853
- sourceRef: z.object({
36854
- tag: z.literal("block_ref"),
36855
- blockId: mailingListSignupBlockIdSchema,
36856
- contentVersionId: mailingListContentVersionIdSchema
36857
- }).nullable()
37433
+ sourceRef: mailingListSignupBlockSourceRefSchema.nullable()
36858
37434
  }),
36859
37435
  z.object({
36860
37436
  tag: z.literal("signed-footer-source"),
@@ -36863,7 +37439,11 @@ var init_newsletter_form_interactive = __esmMin((() => {
36863
37439
  token: signedMailingListSourceTokenSchema
36864
37440
  })
36865
37441
  }),
36866
- z.object({ tag: z.literal("preview") })
37442
+ z.object({ tag: z.literal("preview") }),
37443
+ z.object({
37444
+ tag: z.literal("unpublished-preview"),
37445
+ reason: z.enum(["unpublished-form", "unpublished-settings"])
37446
+ })
36867
37447
  ]),
36868
37448
  setupRevision: z.number().int().positive().nullable(),
36869
37449
  choiceProvider: z.enum([
@@ -39364,10 +39944,167 @@ function FeedbackMessage({ tone, treatment = "panel", children, className, id, t
39364
39944
  }
39365
39945
  var init_FeedbackMessage = __esmMin((() => {}));
39366
39946
  //#endregion
39947
+ //#region ../blocks/src/system/runtime/shared/components/CheckboxChoice.tsx
39948
+ /**
39949
+ * The customer-facing checkbox every block renders. Styling comes from the
39950
+ * theme's input CSS (`.form-check`, `.form-checkbox`), so it follows each
39951
+ * site's input colours, borders and corners.
39952
+ */
39953
+ function CheckboxChoice({ label, presentation = "row", className, ...inputProps }) {
39954
+ return /* @__PURE__ */ jsxs("label", {
39955
+ className: clsx("form-check", presentation === "tile" && "form-check--tile", className),
39956
+ children: [/* @__PURE__ */ jsx("input", {
39957
+ ...inputProps,
39958
+ type: "checkbox",
39959
+ className: "form-checkbox"
39960
+ }), /* @__PURE__ */ jsx("span", {
39961
+ className: "form-check-label",
39962
+ children: label
39963
+ })]
39964
+ });
39965
+ }
39966
+ var init_CheckboxChoice = __esmMin((() => {
39967
+ init_clsx();
39968
+ }));
39969
+ //#endregion
39970
+ //#region ../blocks/src/system/runtime/nodes/form.fields.tsx
39971
+ function fieldBasics(field) {
39972
+ return {
39973
+ inputId: `f_${field.id}`,
39974
+ label: String(field.label ?? field.id),
39975
+ required: Boolean(field.required)
39976
+ };
39977
+ }
39978
+ function textInputType(type) {
39979
+ return type === "email" || type === "url" || type === "tel" || type === "number" ? type : "text";
39980
+ }
39981
+ function textInputConstraints(field, inputType) {
39982
+ if (inputType === "number") return {
39983
+ ...typeof field.min === "number" ? { min: field.min } : {},
39984
+ ...typeof field.max === "number" ? { max: field.max } : {}
39985
+ };
39986
+ return {
39987
+ ...typeof field.minLength === "number" ? { minLength: field.minLength } : {},
39988
+ ...typeof field.maxLength === "number" ? { maxLength: field.maxLength } : {},
39989
+ ...typeof field.pattern === "string" && field.pattern ? { pattern: field.pattern } : {}
39990
+ };
39991
+ }
39992
+ function LabelledField({ basics, children }) {
39993
+ return /* @__PURE__ */ jsxs("div", {
39994
+ className: "fb-field",
39995
+ children: [/* @__PURE__ */ jsx("label", {
39996
+ htmlFor: basics.inputId,
39997
+ className: "form-label",
39998
+ children: basics.label
39999
+ }), children]
40000
+ });
40001
+ }
40002
+ function TextareaField({ field }) {
40003
+ const basics = fieldBasics(field);
40004
+ return /* @__PURE__ */ jsx(LabelledField, {
40005
+ basics,
40006
+ children: /* @__PURE__ */ jsx("textarea", {
40007
+ id: basics.inputId,
40008
+ name: field.id,
40009
+ required: basics.required,
40010
+ placeholder: field.placeholder,
40011
+ rows: field.rows,
40012
+ className: "form-textarea"
40013
+ })
40014
+ });
40015
+ }
40016
+ function SelectField({ field }) {
40017
+ const basics = fieldBasics(field);
40018
+ const options = field.options ?? [];
40019
+ const multiple = Boolean(field.multiple);
40020
+ return /* @__PURE__ */ jsx(LabelledField, {
40021
+ basics,
40022
+ children: /* @__PURE__ */ jsxs("select", {
40023
+ id: basics.inputId,
40024
+ name: field.id,
40025
+ multiple,
40026
+ required: basics.required,
40027
+ className: "form-select",
40028
+ children: [!multiple && /* @__PURE__ */ jsx("option", {
40029
+ value: "",
40030
+ children: "Select…"
40031
+ }), options.map((opt) => /* @__PURE__ */ jsx("option", {
40032
+ value: opt.value,
40033
+ children: opt.label
40034
+ }, opt.value))]
40035
+ })
40036
+ });
40037
+ }
40038
+ function RadioField({ field }) {
40039
+ const basics = fieldBasics(field);
40040
+ const options = field.options ?? [];
40041
+ return /* @__PURE__ */ jsxs("fieldset", {
40042
+ className: "fb-fieldset",
40043
+ children: [/* @__PURE__ */ jsx("legend", {
40044
+ className: "form-label",
40045
+ children: basics.label
40046
+ }), options.map((opt) => /* @__PURE__ */ jsxs("label", {
40047
+ className: "form-check",
40048
+ children: [/* @__PURE__ */ jsx("input", {
40049
+ type: "radio",
40050
+ name: field.id,
40051
+ value: opt.value,
40052
+ required: basics.required,
40053
+ className: "form-radio"
40054
+ }), /* @__PURE__ */ jsx("span", {
40055
+ className: "form-check-label",
40056
+ children: opt.label
40057
+ })]
40058
+ }, opt.value))]
40059
+ });
40060
+ }
40061
+ function TextInputField({ field, type }) {
40062
+ const basics = fieldBasics(field);
40063
+ const inputType = textInputType(type);
40064
+ return /* @__PURE__ */ jsx(LabelledField, {
40065
+ basics,
40066
+ children: /* @__PURE__ */ jsx("input", {
40067
+ id: basics.inputId,
40068
+ name: field.id,
40069
+ type: inputType,
40070
+ required: basics.required,
40071
+ placeholder: field.placeholder,
40072
+ ...textInputConstraints(field, inputType),
40073
+ className: "form-input"
40074
+ })
40075
+ });
40076
+ }
40077
+ function FormFieldControl({ field }) {
40078
+ const type = String(field.type ?? "text");
40079
+ switch (type) {
40080
+ case "textarea": return /* @__PURE__ */ jsx(TextareaField, { field });
40081
+ case "select": return /* @__PURE__ */ jsx(SelectField, { field });
40082
+ case "radio": return /* @__PURE__ */ jsx(RadioField, { field });
40083
+ case "checkbox":
40084
+ case "consent": {
40085
+ const basics = fieldBasics(field);
40086
+ return /* @__PURE__ */ jsx(CheckboxChoice, {
40087
+ name: field.id,
40088
+ required: basics.required,
40089
+ label: basics.label,
40090
+ presentation: type === "consent" ? "tile" : "row"
40091
+ });
40092
+ }
40093
+ default: return /* @__PURE__ */ jsx(TextInputField, {
40094
+ field,
40095
+ type
40096
+ });
40097
+ }
40098
+ }
40099
+ var init_form_fields = __esmMin((() => {
40100
+ init_CheckboxChoice();
40101
+ }));
40102
+ //#endregion
39367
40103
  //#region ../blocks/src/system/runtime/nodes/form.server.tsx
39368
40104
  init_clsx();
39369
40105
  init_ThemedButton();
39370
40106
  init_FeedbackMessage();
40107
+ init_form_fields();
39371
40108
  init_form_interactive();
39372
40109
  const FormNodeSSR = ({ blockId, value, submitLabel, successMessage, className, onSubmit, action = "/api/forms/submit", siteId, apiBaseUrl, spamProtectionEnabled }) => {
39373
40110
  const islandProps = buildFormInteractiveIslandProps({
@@ -39391,107 +40128,7 @@ const FormNodeSSR = ({ blockId, value, submitLabel, successMessage, className, o
39391
40128
  })]
39392
40129
  });
39393
40130
  const groups = display.groups;
39394
- const renderField = (field) => {
39395
- const inputId = `f_${field.id}`;
39396
- const label = String(field.label ?? field.id);
39397
- const required = Boolean(field.required);
39398
- const type = String(field.type ?? "text");
39399
- if (type === "textarea") return /* @__PURE__ */ jsxs("div", {
39400
- className: "fb-field",
39401
- children: [/* @__PURE__ */ jsx("label", {
39402
- htmlFor: inputId,
39403
- className: "form-label",
39404
- children: label
39405
- }), /* @__PURE__ */ jsx("textarea", {
39406
- id: inputId,
39407
- name: field.id,
39408
- required,
39409
- placeholder: field.placeholder,
39410
- rows: field.rows,
39411
- className: "form-textarea"
39412
- })]
39413
- }, field.id);
39414
- if (type === "select") {
39415
- const options = field.options ?? [];
39416
- const multiple = Boolean(field.multiple);
39417
- return /* @__PURE__ */ jsxs("div", {
39418
- className: "fb-field",
39419
- children: [/* @__PURE__ */ jsx("label", {
39420
- htmlFor: inputId,
39421
- className: "form-label",
39422
- children: label
39423
- }), /* @__PURE__ */ jsxs("select", {
39424
- id: inputId,
39425
- name: field.id,
39426
- multiple,
39427
- required,
39428
- className: "form-select",
39429
- children: [!multiple && /* @__PURE__ */ jsx("option", {
39430
- value: "",
39431
- children: "Select…"
39432
- }), options.map((opt) => /* @__PURE__ */ jsx("option", {
39433
- value: opt.value,
39434
- children: opt.label
39435
- }, opt.value))]
39436
- })]
39437
- }, field.id);
39438
- }
39439
- if (type === "radio") {
39440
- const options = field.options ?? [];
39441
- return /* @__PURE__ */ jsxs("fieldset", {
39442
- className: "fb-fieldset",
39443
- children: [/* @__PURE__ */ jsx("legend", {
39444
- className: "form-label",
39445
- children: label
39446
- }), options.map((opt) => /* @__PURE__ */ jsxs("label", {
39447
- className: "fb-inline form-label",
39448
- children: [/* @__PURE__ */ jsx("input", {
39449
- type: "radio",
39450
- name: field.id,
39451
- value: opt.value,
39452
- required,
39453
- className: "form-radio"
39454
- }), /* @__PURE__ */ jsx("span", { children: opt.label })]
39455
- }, opt.value))]
39456
- }, field.id);
39457
- }
39458
- if (type === "checkbox" || type === "consent") return /* @__PURE__ */ jsxs("label", {
39459
- className: "fb-inline form-label",
39460
- children: [/* @__PURE__ */ jsx("input", {
39461
- type: "checkbox",
39462
- name: field.id,
39463
- required,
39464
- className: "form-checkbox"
39465
- }), /* @__PURE__ */ jsx("span", { children: label })]
39466
- }, field.id);
39467
- const inputType = type === "email" || type === "url" || type === "tel" || type === "number" ? type : "text";
39468
- const extra = {};
39469
- if (inputType === "text" || inputType === "email" || inputType === "url" || inputType === "tel") {
39470
- if (typeof field.minLength === "number") extra.minLength = field.minLength;
39471
- if (typeof field.maxLength === "number") extra.maxLength = field.maxLength;
39472
- if (typeof field.pattern === "string" && field.pattern) extra.pattern = field.pattern;
39473
- }
39474
- if (inputType === "number") {
39475
- if (typeof field.min === "number") extra.min = field.min;
39476
- if (typeof field.max === "number") extra.max = field.max;
39477
- }
39478
- return /* @__PURE__ */ jsxs("div", {
39479
- className: "fb-field",
39480
- children: [/* @__PURE__ */ jsx("label", {
39481
- htmlFor: inputId,
39482
- className: "form-label",
39483
- children: label
39484
- }), /* @__PURE__ */ jsx("input", {
39485
- id: inputId,
39486
- name: field.id,
39487
- type: inputType,
39488
- required,
39489
- placeholder: field.placeholder,
39490
- ...extra,
39491
- className: "form-input"
39492
- })]
39493
- }, field.id);
39494
- };
40131
+ const renderField = (field) => /* @__PURE__ */ jsx(FormFieldControl, { field }, field.id);
39495
40132
  return /* @__PURE__ */ jsxs("div", {
39496
40133
  ...getIslandRootProps("form", blockId),
39497
40134
  children: [/* @__PURE__ */ jsx(IslandPropsScript, { value: islandProps }), /* @__PURE__ */ jsxs("form", {
@@ -39537,9 +40174,173 @@ const FormNodeSSR = ({ blockId, value, submitLabel, successMessage, className, o
39537
40174
  });
39538
40175
  };
39539
40176
  //#endregion
40177
+ //#region ../blocks/src/system/runtime/shared/components/FieldValidationMessage.tsx
40178
+ function getFieldValidationControlProps({ message, messageId, describedBy }) {
40179
+ const hasMessage = Boolean(message);
40180
+ const ariaDescribedBy = [describedBy, hasMessage ? messageId : void 0].filter((part) => typeof part === "string" && part.length > 0).join(" ");
40181
+ return {
40182
+ "aria-invalid": hasMessage ? "true" : void 0,
40183
+ "aria-describedby": ariaDescribedBy || void 0
40184
+ };
40185
+ }
40186
+ function FieldValidationMessage({ id, children, className, live = "assertive" }) {
40187
+ return /* @__PURE__ */ jsx("p", {
40188
+ id,
40189
+ className,
40190
+ "data-field-validation-message": true,
40191
+ "data-field-validation-tone": "error",
40192
+ role: "alert",
40193
+ "aria-live": live === "off" ? void 0 : live,
40194
+ children
40195
+ });
40196
+ }
40197
+ var init_FieldValidationMessage = __esmMin((() => {}));
40198
+ //#endregion
40199
+ //#region ../blocks/src/system/runtime/nodes/newsletter-form.parts.tsx
40200
+ /** Splits the preset's fields into the email (joined with the button) and the rest. */
40201
+ function partitionNewsletterFields(fields) {
40202
+ const email = fields.find((field) => field.type === "email") ?? null;
40203
+ return {
40204
+ email,
40205
+ extra: fields.filter((field) => field !== email)
40206
+ };
40207
+ }
40208
+ function NewsletterExtraField({ field, disabled, error }) {
40209
+ const inputId = inputIdFor(field);
40210
+ const errorId = `${inputId}-error`;
40211
+ return /* @__PURE__ */ jsxs("div", {
40212
+ className: "fb-field newsletter-field",
40213
+ children: [
40214
+ /* @__PURE__ */ jsx("label", {
40215
+ htmlFor: inputId,
40216
+ className: "rb-sr-only",
40217
+ children: field.label
40218
+ }),
40219
+ /* @__PURE__ */ jsx("input", {
40220
+ id: inputId,
40221
+ name: field.id,
40222
+ type: field.type,
40223
+ required: field.required,
40224
+ placeholder: field.placeholder ?? field.label,
40225
+ className: "form-input",
40226
+ disabled,
40227
+ ...getFieldValidationControlProps({
40228
+ message: error,
40229
+ messageId: errorId
40230
+ })
40231
+ }),
40232
+ error ? /* @__PURE__ */ jsx(FieldValidationMessage, {
40233
+ id: errorId,
40234
+ className: "fb-error",
40235
+ children: error
40236
+ }) : null
40237
+ ]
40238
+ });
40239
+ }
40240
+ /** The email field and submit button sharing one input-styled shell. */
40241
+ function NewsletterEmailEntry({ field, buttonVariant, buttonLabel, disabled, error }) {
40242
+ const inputId = inputIdFor(field);
40243
+ const errorId = `${inputId}-error`;
40244
+ return /* @__PURE__ */ jsxs("div", {
40245
+ className: "newsletter-entry",
40246
+ children: [/* @__PURE__ */ jsxs("div", {
40247
+ className: "form-input-group newsletter-entry-group",
40248
+ children: [
40249
+ /* @__PURE__ */ jsx("label", {
40250
+ htmlFor: inputId,
40251
+ className: "rb-sr-only",
40252
+ children: field.label
40253
+ }),
40254
+ /* @__PURE__ */ jsx("input", {
40255
+ id: inputId,
40256
+ name: field.id,
40257
+ type: "email",
40258
+ required: field.required,
40259
+ placeholder: field.placeholder ?? field.label,
40260
+ className: "form-input-group-field",
40261
+ autoComplete: "email",
40262
+ disabled,
40263
+ ...getFieldValidationControlProps({
40264
+ message: error,
40265
+ messageId: errorId
40266
+ })
40267
+ }),
40268
+ /* @__PURE__ */ jsx(ThemedButton, {
40269
+ type: "submit",
40270
+ variant: buttonVariant,
40271
+ size: "md",
40272
+ disabled,
40273
+ label: buttonLabel
40274
+ })
40275
+ ]
40276
+ }), error ? /* @__PURE__ */ jsx(FieldValidationMessage, {
40277
+ id: errorId,
40278
+ className: "fb-error",
40279
+ children: error
40280
+ }) : null]
40281
+ });
40282
+ }
40283
+ /**
40284
+ * The submit action: joined to the email field when the preset has one
40285
+ * (every current preset does), otherwise a plain themed button.
40286
+ */
40287
+ function NewsletterSubmitRow({ emailField, buttonVariant, buttonLabel, disabled, error }) {
40288
+ if (emailField) return /* @__PURE__ */ jsx(NewsletterEmailEntry, {
40289
+ field: emailField,
40290
+ buttonVariant,
40291
+ buttonLabel,
40292
+ disabled,
40293
+ error
40294
+ });
40295
+ return /* @__PURE__ */ jsx("div", {
40296
+ className: "newsletter-submit",
40297
+ children: /* @__PURE__ */ jsx(ThemedButton, {
40298
+ type: "submit",
40299
+ variant: buttonVariant,
40300
+ size: "md",
40301
+ disabled,
40302
+ label: buttonLabel
40303
+ })
40304
+ });
40305
+ }
40306
+ /** Visitor-chosen topics as toggle chips. Real checkboxes underneath. */
40307
+ function NewsletterTopicChips({ choices, disabled, error }) {
40308
+ const errorId = `newsletter_${TOPIC_CHOICE_FIELD_ID}-error`;
40309
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("fieldset", {
40310
+ className: "newsletter-topics",
40311
+ "aria-describedby": error ? errorId : void 0,
40312
+ children: [/* @__PURE__ */ jsx("legend", {
40313
+ className: "newsletter-topics-legend",
40314
+ children: "What would you like to hear about?"
40315
+ }), /* @__PURE__ */ jsx("div", {
40316
+ className: "newsletter-topic-chips",
40317
+ children: choices.map((choice) => /* @__PURE__ */ jsxs("label", {
40318
+ className: "newsletter-topic-chip",
40319
+ children: [/* @__PURE__ */ jsx("input", {
40320
+ type: "checkbox",
40321
+ name: TOPIC_CHOICE_FIELD_ID,
40322
+ value: choice.id,
40323
+ disabled
40324
+ }), /* @__PURE__ */ jsx("span", { children: choice.label })]
40325
+ }, choice.id))
40326
+ })]
40327
+ }), error ? /* @__PURE__ */ jsx(FieldValidationMessage, {
40328
+ id: errorId,
40329
+ className: "fb-error",
40330
+ children: error
40331
+ }) : null] });
40332
+ }
40333
+ var TOPIC_CHOICE_FIELD_ID, inputIdFor;
40334
+ var init_newsletter_form_parts = __esmMin((() => {
40335
+ init_ThemedButton();
40336
+ init_FieldValidationMessage();
40337
+ TOPIC_CHOICE_FIELD_ID = "mailingListChoice";
40338
+ inputIdFor = (field) => `newsletter_${field.id}`;
40339
+ }));
40340
+ //#endregion
39540
40341
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.server.tsx
39541
40342
  init_clsx();
39542
- init_ThemedButton();
40343
+ init_newsletter_form_parts();
39543
40344
  init_newsletter_form_interactive();
39544
40345
  /**
39545
40346
  * Newsletter Form Server Component
@@ -39560,36 +40361,15 @@ function NewsletterFormSSR(props) {
39560
40361
  });
39561
40362
  const display = islandProps.render.display;
39562
40363
  const fields = display.fields;
40364
+ const submissionMessage = newsletterBlockSubmissionMessage(islandProps.render.hydration.target);
39563
40365
  if (display.availability === "unavailable") return /* @__PURE__ */ jsx("div", {
39564
40366
  className: clsx("newsletter-form-ssr", className),
39565
40367
  children: /* @__PURE__ */ jsx("p", {
39566
40368
  role: "status",
39567
- children: "We can’t accept signups right now. Please try again later."
40369
+ children: NEWSLETTER_SIGNUPS_PAUSED_MESSAGE
39568
40370
  })
39569
40371
  });
39570
- const renderField = (field) => {
39571
- const inputId = `newsletter_${field.id}`;
39572
- return /* @__PURE__ */ jsxs("div", {
39573
- className: "fb-field newsletter-field",
39574
- children: [/* @__PURE__ */ jsxs("label", {
39575
- htmlFor: inputId,
39576
- className: "form-label",
39577
- children: [field.label, field.required && /* @__PURE__ */ jsx("span", {
39578
- className: "newsletter-required",
39579
- "aria-hidden": "true",
39580
- children: "*"
39581
- })]
39582
- }), /* @__PURE__ */ jsx("input", {
39583
- id: inputId,
39584
- name: field.id,
39585
- type: field.type,
39586
- required: field.required,
39587
- placeholder: field.placeholder,
39588
- className: "form-input",
39589
- autoComplete: field.type === "email" ? "email" : void 0
39590
- })]
39591
- }, field.id);
39592
- };
40372
+ const { email: emailField, extra: extraFields } = partitionNewsletterFields(fields);
39593
40373
  return /* @__PURE__ */ jsxs("div", {
39594
40374
  ...getIslandRootProps("newsletter-form", blockId),
39595
40375
  className: clsx("newsletter-form-ssr", className),
@@ -39608,30 +40388,20 @@ function NewsletterFormSSR(props) {
39608
40388
  /* @__PURE__ */ jsxs("div", {
39609
40389
  className: "newsletter-form-fields",
39610
40390
  children: [
39611
- fields.map(renderField),
39612
- islandProps.render.hydration.choicePresentation === "visitor_choice" ? /* @__PURE__ */ jsxs("fieldset", {
39613
- className: "fb-fieldset newsletter-topics",
39614
- children: [/* @__PURE__ */ jsx("legend", {
39615
- className: "form-label",
39616
- children: "What would you like to hear about?"
39617
- }), display.choices.map((choice) => /* @__PURE__ */ jsxs("label", {
39618
- className: "fb-inline form-label newsletter-topic-choice",
39619
- children: [/* @__PURE__ */ jsx("input", {
39620
- type: "checkbox",
39621
- name: "mailingListChoice",
39622
- value: choice.id,
39623
- className: "form-checkbox"
39624
- }), /* @__PURE__ */ jsx("span", { children: choice.label })]
39625
- }, choice.id))]
40391
+ islandProps.render.hydration.choicePresentation === "visitor_choice" ? /* @__PURE__ */ jsx(NewsletterTopicChips, { choices: display.choices }) : null,
40392
+ extraFields.length > 0 ? /* @__PURE__ */ jsx("div", {
40393
+ className: "newsletter-extra-fields",
40394
+ children: extraFields.map((field) => /* @__PURE__ */ jsx(NewsletterExtraField, { field }, field.id))
39626
40395
  }) : null,
39627
- /* @__PURE__ */ jsx("div", {
39628
- className: "newsletter-submit",
39629
- children: /* @__PURE__ */ jsx(ThemedButton, {
39630
- type: "submit",
39631
- variant: display.buttonVariant,
39632
- size: "md",
39633
- label: display.buttonLabel
39634
- })
40396
+ /* @__PURE__ */ jsx(NewsletterSubmitRow, {
40397
+ emailField,
40398
+ buttonVariant: display.buttonVariant,
40399
+ buttonLabel: display.buttonLabel,
40400
+ disabled: submissionMessage !== null
40401
+ }),
40402
+ submissionMessage && /* @__PURE__ */ jsx("p", {
40403
+ role: "status",
40404
+ children: submissionMessage
39635
40405
  })
39636
40406
  ]
39637
40407
  }),
@@ -44117,6 +44887,7 @@ var init_booking_form_headless_state = __esmMin((() => {
44117
44887
  init_src$2();
44118
44888
  init_dist$1();
44119
44889
  init_booking_form_state();
44890
+ init_booking_form_service_categories();
44120
44891
  init_booking_form_validationErrors();
44121
44892
  init_booking_form_field_values();
44122
44893
  unsafeAsBookingFieldId = (id) => id;
@@ -44681,6 +45452,7 @@ function useHeadlessBookingFlow(options) {
44681
45452
  data,
44682
45453
  services: {
44683
45454
  items: options.services,
45455
+ categories: readServiceCategories(options),
44684
45456
  selected: selectedService,
44685
45457
  defaultServiceId,
44686
45458
  requiresSelection: Boolean(options.requiresServiceSelection)
@@ -44745,6 +45517,9 @@ function useHeadlessBookingFlow(options) {
44745
45517
  actions
44746
45518
  };
44747
45519
  }
45520
+ function readServiceCategories(options) {
45521
+ return options.serviceCategories ?? EMPTY_SERVICE_CATEGORIES;
45522
+ }
44748
45523
  function navigateToCheckout(checkoutUrl) {
44749
45524
  window.location.href = checkoutUrl;
44750
45525
  }
@@ -44758,6 +45533,7 @@ function useLatestRef(value) {
44758
45533
  }, [value]);
44759
45534
  return ref;
44760
45535
  }
45536
+ var EMPTY_SERVICE_CATEGORIES;
44761
45537
  var init_useHeadlessBookingFlow = __esmMin((() => {
44762
45538
  init_client();
44763
45539
  init_src();
@@ -44773,6 +45549,7 @@ var init_useHeadlessBookingFlow = __esmMin((() => {
44773
45549
  init_bookingPaymentOptions();
44774
45550
  init_useHeadlessBookingFlow_state();
44775
45551
  init_booking_form_validationErrors();
45552
+ EMPTY_SERVICE_CATEGORIES = [];
44776
45553
  }));
44777
45554
  //#endregion
44778
45555
  //#region ../blocks/src/system/runtime/hooks/usePolling.state.ts
@@ -45411,28 +46188,6 @@ var init_DiscountCodeDisclosure = __esmMin((() => {
45411
46188
  };
45412
46189
  }));
45413
46190
  //#endregion
45414
- //#region ../blocks/src/system/runtime/shared/components/FieldValidationMessage.tsx
45415
- function getFieldValidationControlProps({ message, messageId, describedBy }) {
45416
- const hasMessage = Boolean(message);
45417
- const ariaDescribedBy = [describedBy, hasMessage ? messageId : void 0].filter((part) => typeof part === "string" && part.length > 0).join(" ");
45418
- return {
45419
- "aria-invalid": hasMessage ? "true" : void 0,
45420
- "aria-describedby": ariaDescribedBy || void 0
45421
- };
45422
- }
45423
- function FieldValidationMessage({ id, children, className, live = "assertive" }) {
45424
- return /* @__PURE__ */ jsx("p", {
45425
- id,
45426
- className,
45427
- "data-field-validation-message": true,
45428
- "data-field-validation-tone": "error",
45429
- role: "alert",
45430
- "aria-live": live === "off" ? void 0 : live,
45431
- children
45432
- });
45433
- }
45434
- var init_FieldValidationMessage = __esmMin((() => {}));
45435
- //#endregion
45436
46191
  //#region ../blocks/src/system/runtime/shared/components/BookingConsentAgreement.tsx
45437
46192
  function BookingConsentAgreement({ agreement, showTitle = true, title, titleId }) {
45438
46193
  return /* @__PURE__ */ jsxs(Fragment, { children: [showTitle ? /* @__PURE__ */ jsxs("div", {
@@ -45457,10 +46212,102 @@ var init_BookingConsentAgreement = __esmMin((() => {
45457
46212
  init_RichText();
45458
46213
  }));
45459
46214
  //#endregion
45460
- //#region ../blocks/src/system/runtime/nodes/booking-form.default-ui.tsx
46215
+ //#region ../blocks/src/system/runtime/nodes/booking-form.service-choices.tsx
45461
46216
  function optionCardClassName(isSelected) {
45462
46217
  return `rb-option-card${isSelected ? " rb-option-card--selected" : ""}`;
45463
46218
  }
46219
+ function SelectedCheck() {
46220
+ return /* @__PURE__ */ jsx("div", {
46221
+ className: "rb-option-check",
46222
+ children: /* @__PURE__ */ jsx(CheckIcon, {})
46223
+ });
46224
+ }
46225
+ /**
46226
+ * The service buttons. A site with no categories keeps its single grid; a
46227
+ * categorised site gets a heading per category, then "Other", above the same
46228
+ * grid (issue 1403). Empty categories are skipped, and there are no intros
46229
+ * here, to keep the step compact.
46230
+ */
46231
+ function DefaultServiceChoices({ booking }) {
46232
+ const layout = layoutServicesByCategory(booking.services.categories, booking.services.items.map((service) => ({
46233
+ service,
46234
+ categoryId: service.categoryId ?? null
46235
+ })), { keepEmptyGroups: false });
46236
+ switch (layout.kind) {
46237
+ case "uncategorized-site": return /* @__PURE__ */ jsxs("div", {
46238
+ className: "rb-reg-stack rb-reg-stack--sm",
46239
+ children: [/* @__PURE__ */ jsx("span", {
46240
+ className: "ab-subtitle",
46241
+ children: "Service"
46242
+ }), /* @__PURE__ */ jsx(ServiceChoiceGrid, {
46243
+ booking,
46244
+ choices: layout.services
46245
+ })]
46246
+ });
46247
+ case "categorized-site": return /* @__PURE__ */ jsx("div", {
46248
+ className: "rb-reg-stack",
46249
+ children: [...layout.groups.map((group) => ({
46250
+ key: group.category.id,
46251
+ heading: group.category.name,
46252
+ choices: group.services
46253
+ })), ...layout.other.length > 0 ? [{
46254
+ key: "other",
46255
+ heading: "Other",
46256
+ choices: layout.other
46257
+ }] : []].map((group) => /* @__PURE__ */ jsxs("div", {
46258
+ className: "rb-reg-stack rb-reg-stack--sm",
46259
+ "data-booking-service-group": group.key,
46260
+ children: [/* @__PURE__ */ jsx("h3", {
46261
+ className: "ab-subtitle",
46262
+ children: group.heading
46263
+ }), /* @__PURE__ */ jsx(ServiceChoiceGrid, {
46264
+ booking,
46265
+ choices: group.choices
46266
+ })]
46267
+ }, group.key))
46268
+ });
46269
+ default: return assertNever$11(layout);
46270
+ }
46271
+ }
46272
+ function ServiceChoiceGrid({ booking, choices }) {
46273
+ const selectedServiceId = booking.services.selected?.id;
46274
+ return /* @__PURE__ */ jsx("div", {
46275
+ className: "rb-reg-grid",
46276
+ children: choices.map(({ service }) => {
46277
+ const isSelected = selectedServiceId === service.id;
46278
+ return /* @__PURE__ */ jsxs("button", {
46279
+ type: "button",
46280
+ onClick: () => booking.actions.selectService(service.id),
46281
+ className: optionCardClassName(isSelected),
46282
+ "aria-pressed": isSelected,
46283
+ children: [/* @__PURE__ */ jsxs("div", {
46284
+ className: "rb-flex-1",
46285
+ children: [
46286
+ /* @__PURE__ */ jsx("div", {
46287
+ className: "ab-subtitle",
46288
+ children: service.title
46289
+ }),
46290
+ service.description ? /* @__PURE__ */ jsx("div", {
46291
+ className: "ab-text rb-on-surface--muted",
46292
+ children: service.description
46293
+ }) : null,
46294
+ service.durationMinutes ? /* @__PURE__ */ jsxs("div", {
46295
+ className: "ab-text rb-on-surface--muted",
46296
+ children: [service.durationMinutes, " minutes"]
46297
+ }) : null
46298
+ ]
46299
+ }), isSelected ? /* @__PURE__ */ jsx(SelectedCheck, {}) : null]
46300
+ }, service.id);
46301
+ })
46302
+ });
46303
+ }
46304
+ var init_booking_form_service_choices = __esmMin((() => {
46305
+ init_src$2();
46306
+ init_dist$1();
46307
+ init_CheckIcon$1();
46308
+ }));
46309
+ //#endregion
46310
+ //#region ../blocks/src/system/runtime/nodes/booking-form.default-ui.tsx
45464
46311
  function formatBookingAmount(amount, currency) {
45465
46312
  return formatCurrencyAmount(amount, currency, { decimals: "auto" });
45466
46313
  }
@@ -45551,7 +46398,6 @@ function DefaultBookingStepContent({ booking, errorSummaryId }) {
45551
46398
  function DefaultServiceSelectionStep({ booking }) {
45552
46399
  const services = booking.services.items;
45553
46400
  const resources = booking.resources.items;
45554
- const selectedServiceId = booking.services.selected?.id;
45555
46401
  const selectedResourceId = booking.resources.selectedResourceId;
45556
46402
  const showServiceSelect = booking.services.requiresSelection || services.length > 1;
45557
46403
  const showResourceSelect = booking.resources.requiresSelection || resources.length > 1;
@@ -45566,41 +46412,7 @@ function DefaultServiceSelectionStep({ booking }) {
45566
46412
  className: "ab-text rb-on-surface--muted",
45567
46413
  children: "Choose the service you'd like to book"
45568
46414
  })] }),
45569
- showServiceSelect ? /* @__PURE__ */ jsxs("div", {
45570
- className: "rb-reg-stack rb-reg-stack--sm",
45571
- children: [/* @__PURE__ */ jsx("span", {
45572
- className: "ab-subtitle",
45573
- children: "Service"
45574
- }), /* @__PURE__ */ jsx("div", {
45575
- className: "rb-reg-grid",
45576
- children: services.map((service) => {
45577
- const isSelected = selectedServiceId === service.id;
45578
- return /* @__PURE__ */ jsxs("button", {
45579
- type: "button",
45580
- onClick: () => booking.actions.selectService(service.id),
45581
- className: optionCardClassName(isSelected),
45582
- "aria-pressed": isSelected,
45583
- children: [/* @__PURE__ */ jsxs("div", {
45584
- className: "rb-flex-1",
45585
- children: [
45586
- /* @__PURE__ */ jsx("div", {
45587
- className: "ab-subtitle",
45588
- children: service.title
45589
- }),
45590
- service.description ? /* @__PURE__ */ jsx("div", {
45591
- className: "ab-text rb-on-surface--muted",
45592
- children: service.description
45593
- }) : null,
45594
- service.durationMinutes ? /* @__PURE__ */ jsxs("div", {
45595
- className: "ab-text rb-on-surface--muted",
45596
- children: [service.durationMinutes, " minutes"]
45597
- }) : null
45598
- ]
45599
- }), isSelected ? /* @__PURE__ */ jsx(SelectedCheck, {}) : null]
45600
- }, service.id);
45601
- })
45602
- })]
45603
- }) : null,
46415
+ showServiceSelect ? /* @__PURE__ */ jsx(DefaultServiceChoices, { booking }) : null,
45604
46416
  booking.resources.state.tag === "loading" ? /* @__PURE__ */ jsxs("div", {
45605
46417
  className: "ab-center",
45606
46418
  children: [/* @__PURE__ */ jsx(SpinnerNode, {
@@ -45990,7 +46802,7 @@ function DefaultFieldInput({ describedBy, error, errorId, field, onBlur, onChang
45990
46802
  case "radio": return /* @__PURE__ */ jsx("div", {
45991
46803
  className: "rb-space-y-2",
45992
46804
  children: field.field.options?.map((option) => /* @__PURE__ */ jsxs("label", {
45993
- className: "rb-flex rb-items-center rb-gap-2 rb-cursor-pointer",
46805
+ className: "form-check",
45994
46806
  children: [/* @__PURE__ */ jsx("input", {
45995
46807
  type: "radio",
45996
46808
  name: field.id,
@@ -46002,7 +46814,7 @@ function DefaultFieldInput({ describedBy, error, errorId, field, onBlur, onChang
46002
46814
  ...validationProps,
46003
46815
  className: "form-radio"
46004
46816
  }), /* @__PURE__ */ jsx("span", {
46005
- className: "rb-caption",
46817
+ className: "form-check-label",
46006
46818
  children: option.label
46007
46819
  })]
46008
46820
  }, option.value))
@@ -46012,47 +46824,20 @@ function DefaultFieldInput({ describedBy, error, errorId, field, onBlur, onChang
46012
46824
  const checkedValues = toStringArray(value);
46013
46825
  return /* @__PURE__ */ jsx("div", {
46014
46826
  className: "rb-space-y-2",
46015
- children: field.field.options.map((option) => /* @__PURE__ */ jsxs("label", {
46016
- className: "rb-flex rb-items-center rb-gap-2 rb-cursor-pointer",
46017
- children: [/* @__PURE__ */ jsx("input", {
46018
- type: "checkbox",
46019
- name: field.id,
46020
- value: option.value,
46021
- checked: checkedValues.includes(option.value),
46022
- onChange: (event) => {
46023
- onChange(event.target.checked ? [...checkedValues, option.value] : checkedValues.filter((item) => item !== option.value));
46024
- },
46025
- onBlur,
46026
- ...validationProps,
46027
- className: "form-checkbox"
46028
- }), /* @__PURE__ */ jsx("span", {
46029
- className: "rb-caption",
46030
- children: option.label
46031
- })]
46827
+ children: field.field.options.map((option) => /* @__PURE__ */ jsx(CheckboxChoice, {
46828
+ name: field.id,
46829
+ value: option.value,
46830
+ checked: checkedValues.includes(option.value),
46831
+ onChange: (event) => {
46832
+ onChange(event.target.checked ? [...checkedValues, option.value] : checkedValues.filter((item) => item !== option.value));
46833
+ },
46834
+ onBlur,
46835
+ ...validationProps,
46836
+ label: option.label
46032
46837
  }, option.value))
46033
46838
  });
46034
46839
  }
46035
- return /* @__PURE__ */ jsxs("label", {
46036
- className: "rb-flex rb-items-center rb-gap-2 rb-cursor-pointer",
46037
- children: [/* @__PURE__ */ jsx("input", {
46038
- type: "checkbox",
46039
- id: field.inputId,
46040
- name: field.id,
46041
- checked: Boolean(value),
46042
- onChange: (event) => onChange(event.target.checked),
46043
- onBlur,
46044
- required: field.required,
46045
- ...validationProps,
46046
- className: "form-checkbox"
46047
- }), /* @__PURE__ */ jsx("span", {
46048
- className: "rb-caption",
46049
- children: field.field.placeholder || field.label
46050
- })]
46051
- });
46052
- case "booking_requirement_consent": return /* @__PURE__ */ jsxs("label", {
46053
- className: "rb-flex rb-items-start rb-gap-2 rb-cursor-pointer",
46054
- children: [/* @__PURE__ */ jsx("input", {
46055
- type: "checkbox",
46840
+ return /* @__PURE__ */ jsx(CheckboxChoice, {
46056
46841
  id: field.inputId,
46057
46842
  name: field.id,
46058
46843
  checked: Boolean(value),
@@ -46060,11 +46845,18 @@ function DefaultFieldInput({ describedBy, error, errorId, field, onBlur, onChang
46060
46845
  onBlur,
46061
46846
  required: field.required,
46062
46847
  ...validationProps,
46063
- className: "form-checkbox rb-mt-0.5"
46064
- }), /* @__PURE__ */ jsx("span", {
46065
- className: "rb-caption",
46066
- children: field.field.checkboxLabel
46067
- })]
46848
+ label: field.field.placeholder || field.label
46849
+ });
46850
+ case "booking_requirement_consent": return /* @__PURE__ */ jsx(CheckboxChoice, {
46851
+ id: field.inputId,
46852
+ name: field.id,
46853
+ checked: Boolean(value),
46854
+ onChange: (event) => onChange(event.target.checked),
46855
+ onBlur,
46856
+ required: field.required,
46857
+ ...validationProps,
46858
+ label: field.field.checkboxLabel,
46859
+ presentation: "tile"
46068
46860
  });
46069
46861
  case "number": return /* @__PURE__ */ jsx("input", {
46070
46862
  ...commonProps,
@@ -46179,12 +46971,6 @@ function DefaultBookingErrorSummary({ errors, id }) {
46179
46971
  children: [errors.formError ? /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("strong", { children: errors.formError }) }) : null, Object.entries(errors.fieldErrors).map(([key, message]) => /* @__PURE__ */ jsx("p", { children: message }, key))]
46180
46972
  });
46181
46973
  }
46182
- function SelectedCheck() {
46183
- return /* @__PURE__ */ jsx("div", {
46184
- className: "rb-option-check",
46185
- children: /* @__PURE__ */ jsx(CheckIcon, {})
46186
- });
46187
- }
46188
46974
  var init_booking_form_default_ui = __esmMin((() => {
46189
46975
  init_src$2();
46190
46976
  init_ThemedButton();
@@ -46194,12 +46980,13 @@ var init_booking_form_default_ui = __esmMin((() => {
46194
46980
  init_DatePicker();
46195
46981
  init_TimeSlotSelector();
46196
46982
  init_spinner$1();
46197
- init_CheckIcon$1();
46198
46983
  init_StepIndicator$1();
46199
46984
  init_FeedbackMessage();
46200
46985
  init_DiscountCodeDisclosure();
46201
46986
  init_FieldValidationMessage();
46202
46987
  init_BookingConsentAgreement();
46988
+ init_CheckboxChoice();
46989
+ init_booking_form_service_choices();
46203
46990
  }));
46204
46991
  //#endregion
46205
46992
  //#region ../blocks/src/system/runtime/shared/utils/checkoutUrls.ts
@@ -46665,6 +47452,7 @@ var init_SelectableOptionCard = __esmMin((() => {}));
46665
47452
  //#endregion
46666
47453
  //#region ../blocks/src/system/runtime/shared/components/index.ts
46667
47454
  var init_components = __esmMin((() => {
47455
+ init_CheckboxChoice();
46668
47456
  init_CheckIcon$1();
46669
47457
  init_ClockIcon();
46670
47458
  init_CrossIcon();
@@ -46758,6 +47546,7 @@ var init_booking_form_client = __esmMin((() => {
46758
47546
  formId: render.hydration.formId ?? form?.id ?? "booking-form",
46759
47547
  form,
46760
47548
  services,
47549
+ serviceCategories: render.hydration.serviceCategories,
46761
47550
  initialServiceId,
46762
47551
  initialResourceId,
46763
47552
  initialData: render.hydration.initialData ?? void 0,
@@ -51666,26 +52455,24 @@ function EventConsentField({ consent, error, formData, idBase, onChange }) {
51666
52455
  title: consent.title,
51667
52456
  titleId: `${idBase}-consent-title`
51668
52457
  }),
51669
- /* @__PURE__ */ jsxs("label", {
51670
- className: "er-label rb-flex rb-items-start rb-gap-2 rb-cursor-pointer",
51671
- children: [
51672
- /* @__PURE__ */ jsx("input", {
51673
- "aria-describedby": error ? errorId : void 0,
51674
- className: "form-checkbox rb-mt-0.5",
51675
- checked: formData.consentAccepted === true,
51676
- onChange: (event) => onChange({
51677
- ...formData,
51678
- consentAccepted: event.target.checked
51679
- }),
51680
- type: "checkbox"
51681
- }),
52458
+ /* @__PURE__ */ jsx(CheckboxChoice, {
52459
+ "aria-describedby": error ? errorId : void 0,
52460
+ "aria-invalid": error ? true : void 0,
52461
+ checked: formData.consentAccepted === true,
52462
+ onChange: (event) => onChange({
52463
+ ...formData,
52464
+ consentAccepted: event.target.checked
52465
+ }),
52466
+ label: /* @__PURE__ */ jsxs(Fragment, { children: [
51682
52467
  consent.checkboxLabel,
51683
52468
  " ",
51684
52469
  /* @__PURE__ */ jsx("span", {
51685
52470
  className: "er-required",
51686
52471
  children: "*"
51687
52472
  })
51688
- ]
52473
+ ] }),
52474
+ presentation: "tile",
52475
+ className: "er-consent-check"
51689
52476
  }),
51690
52477
  error ? /* @__PURE__ */ jsx(FieldValidationMessage, {
51691
52478
  id: errorId,
@@ -51721,6 +52508,7 @@ var init_AttendeeDetailsStep = __esmMin((() => {
51721
52508
  init_LoggedInAsUser();
51722
52509
  init_FieldValidationMessage();
51723
52510
  init_BookingConsentAgreement();
52511
+ init_CheckboxChoice();
51724
52512
  AttendeeDetailsStep = ({ formData, onChange, errors, emailLocked = false, onLogout, ticketsCount = 1, requireGuestDetails = false, guests = [], onGuestsChange, guestErrors = {}, guestSectionShake = false, onGuestShakeEnd, consent }) => {
51725
52513
  const idBase = useId();
51726
52514
  const nameId = `${idBase}-name`;
@@ -107840,7 +108628,7 @@ var init_event_registration_client = __esmMin((() => {
107840
108628
  * Loads Leaflet CSS/JS from CDN to avoid bundling in the blocks package.
107841
108629
  */
107842
108630
  function isLeafletApi(value) {
107843
- if (!isRecord$15(value)) return false;
108631
+ if (!isRecord$16(value)) return false;
107844
108632
  return typeof value.map === "function" && typeof value.tileLayer === "function" && typeof value.marker === "function";
107845
108633
  }
107846
108634
  function loadLeaflet() {
@@ -116107,6 +116895,7 @@ var init_form_client = __esmMin((() => {
116107
116895
  init_useFormSubmission();
116108
116896
  init_ThemedButton();
116109
116897
  init_FeedbackMessage();
116898
+ init_form_fields();
116110
116899
  FormNodeClient = ({ render }) => {
116111
116900
  const form = render.display.form;
116112
116901
  const { submit, state } = useFormSubmission();
@@ -116119,107 +116908,7 @@ var init_form_client = __esmMin((() => {
116119
116908
  children: "Missing or unknown form."
116120
116909
  });
116121
116910
  const groups = render.display.groups;
116122
- const renderField = (field) => {
116123
- const inputId = `f_${field.id}`;
116124
- const label = String(field.label ?? field.id);
116125
- const required = Boolean(field.required);
116126
- const type = String(field.type ?? "text");
116127
- if (type === "textarea") return /* @__PURE__ */ jsxs("div", {
116128
- className: "fb-field",
116129
- children: [/* @__PURE__ */ jsx("label", {
116130
- htmlFor: inputId,
116131
- className: "form-label",
116132
- children: label
116133
- }), /* @__PURE__ */ jsx("textarea", {
116134
- id: inputId,
116135
- name: field.id,
116136
- required,
116137
- placeholder: field.placeholder,
116138
- rows: field.rows,
116139
- className: "form-textarea"
116140
- })]
116141
- }, field.id);
116142
- if (type === "select") {
116143
- const options = field.options ?? [];
116144
- const multiple = Boolean(field.multiple);
116145
- return /* @__PURE__ */ jsxs("div", {
116146
- className: "fb-field",
116147
- children: [/* @__PURE__ */ jsx("label", {
116148
- htmlFor: inputId,
116149
- className: "form-label",
116150
- children: label
116151
- }), /* @__PURE__ */ jsxs("select", {
116152
- id: inputId,
116153
- name: field.id,
116154
- multiple,
116155
- required,
116156
- className: "form-select",
116157
- children: [!multiple && /* @__PURE__ */ jsx("option", {
116158
- value: "",
116159
- children: "Select…"
116160
- }), options.map((opt) => /* @__PURE__ */ jsx("option", {
116161
- value: opt.value,
116162
- children: opt.label
116163
- }, opt.value))]
116164
- })]
116165
- }, field.id);
116166
- }
116167
- if (type === "radio") {
116168
- const options = field.options ?? [];
116169
- return /* @__PURE__ */ jsxs("fieldset", {
116170
- className: "fb-fieldset",
116171
- children: [/* @__PURE__ */ jsx("legend", {
116172
- className: "form-label",
116173
- children: label
116174
- }), options.map((opt) => /* @__PURE__ */ jsxs("label", {
116175
- className: "fb-inline form-label",
116176
- children: [/* @__PURE__ */ jsx("input", {
116177
- type: "radio",
116178
- name: field.id,
116179
- value: opt.value,
116180
- required,
116181
- className: "form-radio"
116182
- }), /* @__PURE__ */ jsx("span", { children: opt.label })]
116183
- }, opt.value))]
116184
- }, field.id);
116185
- }
116186
- if (type === "checkbox" || type === "consent") return /* @__PURE__ */ jsxs("label", {
116187
- className: "fb-inline form-label",
116188
- children: [/* @__PURE__ */ jsx("input", {
116189
- type: "checkbox",
116190
- name: field.id,
116191
- required,
116192
- className: "form-checkbox"
116193
- }), /* @__PURE__ */ jsx("span", { children: label })]
116194
- }, field.id);
116195
- const inputType = type === "email" || type === "url" || type === "tel" || type === "number" ? type : "text";
116196
- const extra = {};
116197
- if (inputType === "text" || inputType === "email" || inputType === "url" || inputType === "tel") {
116198
- if (typeof field.minLength === "number") extra.minLength = field.minLength;
116199
- if (typeof field.maxLength === "number") extra.maxLength = field.maxLength;
116200
- if (typeof field.pattern === "string" && field.pattern) extra.pattern = field.pattern;
116201
- }
116202
- if (inputType === "number") {
116203
- if (typeof field.min === "number") extra.min = field.min;
116204
- if (typeof field.max === "number") extra.max = field.max;
116205
- }
116206
- return /* @__PURE__ */ jsxs("div", {
116207
- className: "fb-field",
116208
- children: [/* @__PURE__ */ jsx("label", {
116209
- htmlFor: inputId,
116210
- className: "form-label",
116211
- children: label
116212
- }), /* @__PURE__ */ jsx("input", {
116213
- id: inputId,
116214
- name: field.id,
116215
- type: inputType,
116216
- required,
116217
- placeholder: field.placeholder,
116218
- ...extra,
116219
- className: "form-input"
116220
- })]
116221
- }, field.id);
116222
- };
116911
+ const renderField = (field) => /* @__PURE__ */ jsx(FormFieldControl, { field }, field.id);
116223
116912
  const handleSubmit = async (e) => {
116224
116913
  e.preventDefault();
116225
116914
  const formData = new FormData(e.currentTarget);
@@ -116478,6 +117167,16 @@ var init_newsletter_form_client_state = __esmMin((() => {
116478
117167
  }));
116479
117168
  //#endregion
116480
117169
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.submission.ts
117170
+ /**
117171
+ * Logs a refusal with its reason so a QA session can tell the cases apart in
117172
+ * the console, since the visitor-facing copy deliberately does not.
117173
+ */
117174
+ function reportNewsletterSignupRefusal(reason, detail = {}) {
117175
+ console.warn("[newsletter-form] Signup refused before sending", {
117176
+ reason,
117177
+ ...detail
117178
+ });
117179
+ }
116481
117180
  function mapNewsletterFormFieldsToSubscriberInput(fields) {
116482
117181
  return {
116483
117182
  email: readSubmittedText(fields.email),
@@ -116498,7 +117197,11 @@ function resolveNewsletterSubmitTarget(input) {
116498
117197
  case "signed-footer-source": return publishedSourceTarget(input.sourceRef);
116499
117198
  case "preview": return {
116500
117199
  tag: "unavailable",
116501
- reason: "missing_block_source"
117200
+ reason: "preview_only_source"
117201
+ };
117202
+ case "unpublished-preview": return {
117203
+ tag: "unavailable",
117204
+ reason: "preview_only_source"
116502
117205
  };
116503
117206
  default: return assertNever$22(input);
116504
117207
  }
@@ -116522,6 +117225,7 @@ var init_newsletter_form_submission = __esmMin((() => {
116522
117225
  //#endregion
116523
117226
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.client.tsx
116524
117227
  function NewsletterFormClient({ render }) {
117228
+ const submissionMessage = newsletterBlockSubmissionMessage(render.hydration.target);
116525
117229
  const api = useBlockApi();
116526
117230
  const [submissionState, dispatchSubmission] = useReducer(newsletterFormSubmissionReducer, initialNewsletterFormSubmissionState);
116527
117231
  const [altchaSolution, setAltchaSolution] = useState(null);
@@ -116531,7 +117235,6 @@ function NewsletterFormClient({ render }) {
116531
117235
  if (succeeded) successRef.current?.focus();
116532
117236
  }, [succeeded]);
116533
117237
  const showSpamProtection = render.hydration.spamProtectionEnabled ?? isSpamProtectionEnabled();
116534
- const fields = render.display.fields;
116535
117238
  const handleSubmit = async (e) => {
116536
117239
  e.preventDefault();
116537
117240
  const formData = new FormData(e.currentTarget);
@@ -116560,22 +117263,33 @@ function NewsletterFormClient({ render }) {
116560
117263
  }
116561
117264
  const submitTarget = resolveNewsletterSubmitTarget(render.hydration.target);
116562
117265
  if (submitTarget.tag === "unavailable") {
117266
+ reportNewsletterSignupRefusal(submitTarget.reason, { target: render.hydration.target });
117267
+ dispatchSubmission({
117268
+ type: "failed",
117269
+ error: newsletterFormError(NEWSLETTER_SIGNUPS_PAUSED_MESSAGE)
117270
+ });
117271
+ return;
117272
+ }
117273
+ if (render.hydration.setupRevision === null) {
117274
+ reportNewsletterSignupRefusal("setup_not_configured");
116563
117275
  dispatchSubmission({
116564
117276
  type: "failed",
116565
- error: newsletterFormError(SIGNUPS_PAUSED_MESSAGE)
117277
+ error: newsletterFormError(NEWSLETTER_SIGNUPS_PAUSED_MESSAGE)
116566
117278
  });
116567
117279
  return;
116568
117280
  }
116569
- if (render.hydration.setupRevision === null || render.display.availability === "unavailable") {
117281
+ if (render.display.availability === "unavailable") {
117282
+ reportNewsletterSignupRefusal("destination_unavailable", { setupRevision: render.hydration.setupRevision });
116570
117283
  dispatchSubmission({
116571
117284
  type: "failed",
116572
- error: newsletterFormError(SIGNUPS_PAUSED_MESSAGE)
117285
+ error: newsletterFormError(NEWSLETTER_SIGNUPS_PAUSED_MESSAGE)
116573
117286
  });
116574
117287
  return;
116575
117288
  }
116576
117289
  const selection = readVisitorSelection(formData, render.hydration);
116577
117290
  const selectionError = visitorSelectionError(selection);
116578
117291
  if (selectionError) {
117292
+ if (selection.tag === "unusable") reportNewsletterSignupRefusal("choices_unusable", { choiceProvider: render.hydration.choiceProvider });
116579
117293
  dispatchSubmission({
116580
117294
  type: "failed",
116581
117295
  error: selectionError
@@ -116643,43 +117357,14 @@ function NewsletterFormClient({ render }) {
116643
117357
  autoComplete: "off",
116644
117358
  "aria-hidden": "true"
116645
117359
  }),
116646
- /* @__PURE__ */ jsxs("div", {
116647
- className: "newsletter-form-fields",
116648
- children: [
116649
- fields.map((field) => /* @__PURE__ */ jsx(NewsletterField, {
116650
- field,
116651
- submitting,
116652
- error: getNewsletterSubmissionFieldError(submissionState, field.id)
116653
- }, field.id)),
116654
- render.hydration.choicePresentation === "visitor_choice" ? /* @__PURE__ */ jsx(NewsletterTopicChoices, {
116655
- choices: render.display.choices,
116656
- submitting,
116657
- error: getNewsletterSubmissionFieldError(submissionState, TOPIC_CHOICE_FIELD_ID)
116658
- }) : null,
116659
- showSpamProtection && /* @__PURE__ */ jsx("div", {
116660
- className: "newsletter-altcha",
116661
- children: /* @__PURE__ */ jsx(AltchaWidget, {
116662
- challengeUrl: getChallengeUrl(),
116663
- onSolutionChange: setAltchaSolution
116664
- })
116665
- }),
116666
- formError && /* @__PURE__ */ jsx(FeedbackMessage, {
116667
- tone: "error",
116668
- className: "fb-error",
116669
- role: "alert",
116670
- children: formError
116671
- }),
116672
- /* @__PURE__ */ jsx("div", {
116673
- className: "newsletter-submit",
116674
- children: /* @__PURE__ */ jsx(ThemedButton, {
116675
- type: "submit",
116676
- variant: render.display.buttonVariant,
116677
- size: "md",
116678
- disabled: submitting,
116679
- label: submitting ? "Subscribing..." : render.display.buttonLabel
116680
- })
116681
- })
116682
- ]
117360
+ /* @__PURE__ */ jsx(NewsletterFormFields, {
117361
+ render,
117362
+ submissionState,
117363
+ submitting,
117364
+ submissionMessage,
117365
+ formError,
117366
+ showSpamProtection,
117367
+ onAltchaSolution: setAltchaSolution
116683
117368
  }),
116684
117369
  render.display.privacyNote && /* @__PURE__ */ jsx("p", {
116685
117370
  className: "newsletter-privacy-note",
@@ -116689,67 +117374,52 @@ function NewsletterFormClient({ render }) {
116689
117374
  })
116690
117375
  });
116691
117376
  }
116692
- function NewsletterField({ field, submitting, error }) {
116693
- const inputId = `newsletter_${field.id}`;
116694
- const errorId = `${inputId}-error`;
117377
+ function NewsletterFormFields({ render, submissionState, submitting, submissionMessage, formError, showSpamProtection, onAltchaSolution }) {
117378
+ const { email: emailField, extra: extraFields } = partitionNewsletterFields(render.display.fields);
117379
+ const fieldError = (id) => getNewsletterSubmissionFieldError(submissionState, id);
116695
117380
  return /* @__PURE__ */ jsxs("div", {
116696
- className: "fb-field newsletter-field",
117381
+ className: "newsletter-form-fields",
116697
117382
  children: [
116698
- /* @__PURE__ */ jsxs("label", {
116699
- htmlFor: inputId,
116700
- className: "form-label",
116701
- children: [field.label, field.required && /* @__PURE__ */ jsx("span", {
116702
- className: "newsletter-required",
116703
- "aria-hidden": "true",
116704
- children: "*"
116705
- })]
116706
- }),
116707
- /* @__PURE__ */ jsx("input", {
116708
- id: inputId,
116709
- name: field.id,
116710
- type: field.type,
116711
- required: field.required,
116712
- placeholder: field.placeholder,
116713
- className: "form-input",
116714
- autoComplete: field.type === "email" ? "email" : void 0,
117383
+ render.hydration.choicePresentation === "visitor_choice" ? /* @__PURE__ */ jsx(NewsletterTopicChips, {
117384
+ choices: render.display.choices,
116715
117385
  disabled: submitting,
116716
- ...getFieldValidationControlProps({
116717
- message: error,
116718
- messageId: errorId
117386
+ error: fieldError(TOPIC_CHOICE_FIELD_ID)
117387
+ }) : null,
117388
+ extraFields.length > 0 ? /* @__PURE__ */ jsx("div", {
117389
+ className: "newsletter-extra-fields",
117390
+ children: extraFields.map((field) => /* @__PURE__ */ jsx(NewsletterExtraField, {
117391
+ field,
117392
+ disabled: submitting,
117393
+ error: fieldError(field.id)
117394
+ }, field.id))
117395
+ }) : null,
117396
+ showSpamProtection && /* @__PURE__ */ jsx("div", {
117397
+ className: "newsletter-altcha",
117398
+ children: /* @__PURE__ */ jsx(AltchaWidget, {
117399
+ challengeUrl: getChallengeUrl(),
117400
+ onSolutionChange: onAltchaSolution
116719
117401
  })
116720
117402
  }),
116721
- error && /* @__PURE__ */ jsx(FieldValidationMessage, {
116722
- id: errorId,
117403
+ formError && /* @__PURE__ */ jsx(FeedbackMessage, {
117404
+ tone: "error",
116723
117405
  className: "fb-error",
116724
- children: error
117406
+ role: "alert",
117407
+ children: formError
117408
+ }),
117409
+ /* @__PURE__ */ jsx(NewsletterSubmitRow, {
117410
+ emailField,
117411
+ buttonVariant: render.display.buttonVariant,
117412
+ buttonLabel: submitting ? "Subscribing..." : render.display.buttonLabel,
117413
+ disabled: submitting || submissionMessage !== null,
117414
+ error: emailField ? fieldError(emailField.id) : null
117415
+ }),
117416
+ submissionMessage && /* @__PURE__ */ jsx("p", {
117417
+ role: "status",
117418
+ children: submissionMessage
116725
117419
  })
116726
117420
  ]
116727
117421
  });
116728
117422
  }
116729
- function NewsletterTopicChoices({ choices, submitting, error }) {
116730
- const errorId = `newsletter_${TOPIC_CHOICE_FIELD_ID}-error`;
116731
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("fieldset", {
116732
- className: "fb-fieldset newsletter-topics",
116733
- "aria-describedby": error ? errorId : void 0,
116734
- children: [/* @__PURE__ */ jsx("legend", {
116735
- className: "form-label",
116736
- children: "What would you like to hear about?"
116737
- }), choices.map((choice) => /* @__PURE__ */ jsxs("label", {
116738
- className: "fb-inline form-label newsletter-topic-choice",
116739
- children: [/* @__PURE__ */ jsx("input", {
116740
- type: "checkbox",
116741
- name: TOPIC_CHOICE_FIELD_ID,
116742
- value: choice.id,
116743
- className: "form-checkbox",
116744
- disabled: submitting
116745
- }), /* @__PURE__ */ jsx("span", { children: choice.label })]
116746
- }, choice.id))]
116747
- }), error && /* @__PURE__ */ jsx(FieldValidationMessage, {
116748
- id: errorId,
116749
- className: "fb-error",
116750
- children: error
116751
- })] });
116752
- }
116753
117423
  function UnavailableNewsletterForm({ className }) {
116754
117424
  return /* @__PURE__ */ jsx("div", {
116755
117425
  className: clsx("newsletter-form", className ?? void 0),
@@ -116757,7 +117427,7 @@ function UnavailableNewsletterForm({ className }) {
116757
117427
  tone: "error",
116758
117428
  className: "fb-error",
116759
117429
  role: "status",
116760
- children: SIGNUPS_PAUSED_MESSAGE
117430
+ children: NEWSLETTER_SIGNUPS_PAUSED_MESSAGE
116761
117431
  })
116762
117432
  });
116763
117433
  }
@@ -116777,7 +117447,7 @@ function transportFailureMessage(err) {
116777
117447
  function visitorSelectionError(selection) {
116778
117448
  switch (selection.tag) {
116779
117449
  case "nothing_chosen": return newsletterFieldError(TOPIC_CHOICE_FIELD_ID, "Choose at least one email topic.");
116780
- case "unusable": return newsletterFormError(SIGNUPS_PAUSED_MESSAGE);
117450
+ case "unusable": return newsletterFormError(NEWSLETTER_SIGNUPS_PAUSED_MESSAGE);
116781
117451
  case "not_offered":
116782
117452
  case "chosen": return null;
116783
117453
  }
@@ -116809,7 +117479,7 @@ function publicSignupError(outcome) {
116809
117479
  case "unavailable": switch (outcome.reason) {
116810
117480
  case "configuration_unavailable":
116811
117481
  case "authentication_failed":
116812
- case "target_unavailable": return SIGNUPS_PAUSED_MESSAGE;
117482
+ case "target_unavailable": return NEWSLETTER_SIGNUPS_PAUSED_MESSAGE;
116813
117483
  case "stale_configuration": return "This signup form has changed. Refresh the page, then try again.";
116814
117484
  case "rate_limited": return TOO_MANY_ATTEMPTS_MESSAGE;
116815
117485
  case "temporarily_unavailable": return RETRY_SHORTLY_MESSAGE;
@@ -116817,20 +117487,18 @@ function publicSignupError(outcome) {
116817
117487
  case "uncertain": return "We could not confirm whether your signup completed. Please wait a moment, then try again.";
116818
117488
  }
116819
117489
  }
116820
- var TOPIC_CHOICE_FIELD_ID, SIGNUPS_PAUSED_MESSAGE, RETRY_SHORTLY_MESSAGE, TOO_MANY_ATTEMPTS_MESSAGE;
117490
+ var RETRY_SHORTLY_MESSAGE, TOO_MANY_ATTEMPTS_MESSAGE;
116821
117491
  var init_newsletter_form_client = __esmMin((() => {
116822
117492
  init_clsx();
116823
117493
  init_client();
116824
117494
  init_src();
116825
117495
  init_api$1();
116826
117496
  init_src$2();
116827
- init_ThemedButton();
116828
117497
  init_FeedbackMessage();
116829
- init_FieldValidationMessage();
117498
+ init_newsletter_form_parts();
116830
117499
  init_newsletter_form_client_state();
116831
117500
  init_newsletter_form_submission();
116832
- TOPIC_CHOICE_FIELD_ID = "mailingListChoice";
116833
- SIGNUPS_PAUSED_MESSAGE = "We can’t accept signups right now. Please try again later.";
117501
+ init_newsletter_form_interactive();
116834
117502
  RETRY_SHORTLY_MESSAGE = "We couldn’t confirm your signup. Please try again in a little while.";
116835
117503
  TOO_MANY_ATTEMPTS_MESSAGE = "There have been too many signup attempts. Please wait a moment and try again.";
116836
117504
  }));
@@ -117058,6 +117726,10 @@ init_surfaces();
117058
117726
  //#region ../theme-core/src/accordions/defaults.ts
117059
117727
  init_curatedSiteStyles();
117060
117728
  //#endregion
117729
+ //#region ../theme-core/src/inputs/checkable.ts
117730
+ init_colorTokenParts();
117731
+ init_colorStyles();
117732
+ //#endregion
117061
117733
  //#region ../theme-core/src/inputs/generateInputCss.ts
117062
117734
  init_types$11();
117063
117735
  init_constants$2();
@@ -117086,17 +117758,12 @@ init_paintTargets();
117086
117758
  //#region ../theme-core/src/footer/generateFooterCss.ts
117087
117759
  init_resolver$1();
117088
117760
  //#endregion
117761
+ //#region ../theme-core/src/layout/boundaryCss.ts
117762
+ init_sectionTransitions();
117763
+ init_transitionCatalogue();
117764
+ //#endregion
117089
117765
  //#region ../theme-core/src/layout/generateLayoutCss.ts
117090
117766
  init_breakpoints();
117091
- init_sectionTransitions();
117092
- svgDataUrl("<svg viewBox='0 0 1440 72' xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none'><path d='M0 72V46C130 55 260 24 420 30C590 36 690 62 860 42C1035 21 1160 47 1305 36C1365 31 1415 33 1440 38V72H0Z' fill='white'/></svg>");
117093
- svgDataUrl("<svg viewBox='0 0 1440 72' xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none'><path d='M0 72V42C120 24 240 24 360 42C480 60 600 60 720 42C840 24 960 24 1080 42C1200 60 1320 60 1440 42V72H0Z' fill='white'/></svg>");
117094
- svgDataUrl("<svg viewBox='0 0 1440 72' xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none'><path d='M0 72V43C180 32 300 32 480 43C660 54 780 54 960 43C1140 32 1260 32 1440 43V72H0Z' fill='white'/></svg>");
117095
- svgDataUrl("<svg viewBox='0 0 1440 72' xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none'><path d='M0 72V44C90 14 270 14 360 44C450 74 630 74 720 44C810 14 990 14 1080 44C1170 74 1350 74 1440 44V72H0Z' fill='white'/></svg>");
117096
- svgDataUrl("<svg viewBox='0 0 1440 72' xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none'><path d='M0 72V39C115 31 205 54 340 48C505 41 610 16 760 33C920 51 1015 64 1160 44C1280 27 1375 35 1440 50V72H0Z' fill='white'/></svg>");
117097
- function svgDataUrl(svg) {
117098
- return `data:image/svg+xml,${encodeURIComponent(svg)}`;
117099
- }
117100
117767
  //#endregion
117101
117768
  //#region ../theme-core/src/status/generateStatusCss.ts
117102
117769
  init_surfaces();