@riverbankcms/sdk 0.144.0 → 0.145.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 (162) hide show
  1. package/dist/{PreviewEditorSidebar-CQZNnFiM.mjs → PreviewEditorSidebar-O-V6Pl5a.mjs} +4 -4
  2. package/dist/{PreviewEditorUI-DCNxnmI0.mjs → PreviewEditorUI-1UlX_9cw.mjs} +3 -3
  3. package/dist/{PreviewQueryProvider-BCYICImX.mjs → PreviewQueryProvider-BxGwdOCV.mjs} +1 -1
  4. package/dist/{SdkPreviewModeRuntime-BJolUbr1.mjs → SdkPreviewModeRuntime-DYbA_4iv.mjs} +419 -348
  5. package/dist/{SiteChromeCustomizeContext-CKgEpoxi.mjs → SiteChromeCustomizeContext-CNTKQ9nx.mjs} +2081 -1313
  6. package/dist/_dts/ai/src/contracts/proposals.d.ts +4719 -66
  7. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +429 -6
  8. package/dist/_dts/api/src/appointmentEndpointDefinitions.d.ts +49 -0
  9. package/dist/_dts/api/src/appointmentServiceCategoryEndpoints.d.ts +75 -0
  10. package/dist/_dts/api/src/appointmentServiceContracts.d.ts +80 -0
  11. package/dist/_dts/api/src/appointmentSetup.d.ts +8 -1
  12. package/dist/_dts/api/src/bookingManagementEndpoints.d.ts +2 -12
  13. package/dist/_dts/api/src/communications.d.ts +14 -22
  14. package/dist/_dts/api/src/contentRuntime.d.ts +2 -0
  15. package/dist/_dts/api/src/endpoints.d.ts +105 -58
  16. package/dist/_dts/api/src/indexExtendedTypes.d.ts +1 -1
  17. package/dist/_dts/api/src/launchReadiness.d.ts +2 -0
  18. package/dist/_dts/api/src/newsletterEndpointDefinitions.d.ts +0 -23
  19. package/dist/_dts/api/src/onboarding/confirmationResend.d.ts +3 -0
  20. package/dist/_dts/api/src/publicBookingService.d.ts +35 -1
  21. package/dist/_dts/api/src/publicExperience/newsletterEndpoints.d.ts +13 -0
  22. package/dist/_dts/api/src/publicNewsletterEndpointDefinitions.d.ts +58 -0
  23. package/dist/_dts/api/src/schemas/admin-observability-logs.d.ts +3 -3
  24. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +2 -6
  25. package/dist/_dts/api/src/types.d.ts +2 -1
  26. package/dist/_dts/blocks/src/PageRenderer.d.ts +4 -4
  27. package/dist/_dts/blocks/src/bindings/index.d.ts +2 -2
  28. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +24 -0
  29. package/dist/_dts/blocks/src/index.d.ts +1 -1
  30. package/dist/_dts/blocks/src/pageRenderJoins.d.ts +68 -0
  31. package/dist/_dts/blocks/src/pageRenderPlan.d.ts +8 -27
  32. package/dist/_dts/blocks/src/pageRenderPreparation.d.ts +7 -4
  33. package/dist/_dts/blocks/src/pageRendererBoundaries.d.ts +40 -23
  34. package/dist/_dts/blocks/src/server.d.ts +1 -1
  35. package/dist/_dts/blocks/src/system/blocks/appointment-services.d.ts +2 -0
  36. package/dist/_dts/blocks/src/system/blocks/events/eventSelectionFields.d.ts +2 -2
  37. package/dist/_dts/blocks/src/system/blocks/events/shared/calendarWindow.d.ts +1 -1
  38. package/dist/_dts/blocks/src/system/blocks/location-map.d.ts +2 -2
  39. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +47 -47
  40. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +29 -3
  41. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.d.ts +8 -1
  42. package/dist/_dts/blocks/src/system/runtime/nodes/appointment-services.d.ts +22 -2
  43. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +3 -1
  44. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.interactive.d.ts +57 -2
  45. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.service-categories.d.ts +29 -0
  46. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.service-choices.d.ts +13 -0
  47. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.state.d.ts +3 -3
  48. package/dist/_dts/blocks/src/system/runtime/nodes/booking-runtime-sources.d.ts +13 -1
  49. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventSourceRecovery.d.ts +1 -1
  50. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +3 -3
  51. package/dist/_dts/blocks/src/system/runtime/nodes/newsletter-form.submission.d.ts +20 -1
  52. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +2 -0
  53. package/dist/_dts/content-model/src/content-types/schema.d.ts +1 -1
  54. package/dist/_dts/core/src/appointment-service-categories.d.ts +108 -0
  55. package/dist/_dts/core/src/editor-state.d.ts +6 -0
  56. package/dist/_dts/core/src/index.d.ts +1 -0
  57. package/dist/_dts/core/src/public-profile.d.ts +9 -0
  58. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +663 -241
  59. package/dist/_dts/editor-blocks/src/block-editor/types.d.ts +6 -0
  60. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/loaders.d.ts +16 -1
  61. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/types.d.ts +13 -0
  62. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/utils.d.ts +29 -0
  63. package/dist/_dts/preview-next/src/client/blocks/BoundaryControl.d.ts +7 -2
  64. package/dist/_dts/preview-next/src/client/blocks/composer/mergeDrafts.d.ts +1 -1
  65. package/dist/_dts/preview-next/src/client/blocks/composer/types.d.ts +1 -0
  66. package/dist/_dts/preview-next/src/client/blocks/previewProjection/applyPreviewProjections.d.ts +1 -3
  67. package/dist/_dts/preview-next/src/client/blocks/previewProjection/runtimeContentDecoration.d.ts +2 -2
  68. package/dist/_dts/preview-next/src/client/blocks/previewProjection/types.d.ts +2 -2
  69. package/dist/_dts/preview-next/src/client/preview/PreviewEntryControllerProvider.d.ts +1 -0
  70. package/dist/_dts/sdk/src/client/runtimeCacheKeys.d.ts +8 -0
  71. package/dist/_dts/sdk/src/client/types.d.ts +1 -1
  72. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  73. package/dist/_dts/sdk/src/contracts/page.d.ts +7 -2
  74. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +5 -0
  75. package/dist/_dts/sdk/src/manifest/passes/pass1-fields.d.ts +2 -2
  76. package/dist/_dts/sdk/src/public-api/contracts.d.ts +6 -8
  77. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +2 -2
  78. package/dist/_dts/sdk/src/rendering/components/pageShared.d.ts +2 -2
  79. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +1 -1
  80. package/dist/_dts/sdk/src/rendering/hooks/useContent.d.ts +1 -1
  81. package/dist/_dts/sdk/src/version.d.ts +1 -1
  82. package/dist/_dts/surfaces/src/index.d.ts +1 -0
  83. package/dist/_dts/theme-core/src/layout/boundaryCss.d.ts +3 -0
  84. package/dist/_dts/theme-core/src/site-styles/compositionBudget.d.ts +8 -0
  85. package/dist/_dts/theme-core/src/site-styles/contentTemplateRuntimeCompiler.d.ts +3 -2
  86. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +1 -3
  87. package/dist/_dts/theme-core/src/site-styles/generatedDesignPipeline.d.ts +3 -0
  88. package/dist/_dts/theme-core/src/site-styles/index.d.ts +3 -0
  89. package/dist/_dts/theme-core/src/site-styles/joinTreatment.d.ts +132 -0
  90. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorBoundaries.d.ts +31 -0
  91. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorModel.d.ts +33 -29
  92. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +4 -4
  93. package/dist/_dts/theme-core/src/site-styles/pageFlowPresets.d.ts +36 -0
  94. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +4 -10
  95. package/dist/_dts/theme-core/src/site-styles/rawStyleFields.d.ts +17 -4
  96. package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +20 -74
  97. package/dist/_dts/theme-core/src/site-styles/sectionBoundaryComposition.d.ts +95 -0
  98. package/dist/_dts/theme-core/src/site-styles/sectionTransitions.d.ts +80 -518
  99. package/dist/_dts/theme-core/src/site-styles/siteStyleBlockPolicies.d.ts +9 -4
  100. package/dist/_dts/theme-core/src/site-styles/transitionCatalogue.d.ts +320 -0
  101. package/dist/_dts/theme-core/src/site-styles/types.d.ts +3 -18
  102. package/dist/client/{BookingConsentAgreement-DoAILrkN.mjs → BookingConsentAgreement-DlVOmpW4.mjs} +2 -2
  103. package/dist/client/{DiscountCodeDisclosure-X528hUTf.mjs → DiscountCodeDisclosure-T9q623PQ.mjs} +1 -1
  104. package/dist/client/{EventCombined.client-DSBVwq5m.mjs → EventCombined.client-DnsUeONl.mjs} +6 -6
  105. package/dist/client/{EventListing.client-DmFAX7Vb.mjs → EventListing.client-CNBFqJS4.mjs} +2 -2
  106. package/dist/client/{LeafletStaticMap-BAqXdfhq.mjs → LeafletStaticMap-C7higg52.mjs} +1 -1
  107. package/dist/client/{ThemedButton-BwNFVwi4.mjs → ThemedButton-LAJTNrS8.mjs} +1 -1
  108. package/dist/client/{accordion-DRhMdkbd.mjs → accordion-BcNF9TjX.mjs} +1 -1
  109. package/dist/client/{bookingForm-iJ_nCsNF.mjs → bookingForm-BUaQ3S03.mjs} +142 -50
  110. package/dist/client/bookings.mjs +191 -64
  111. package/dist/client/{carousel-D8zuffZr.mjs → carousel-CTdF6e0V.mjs} +1 -1
  112. package/dist/client/{cart-DeMGJViN.mjs → cart-pGtyf_XU.mjs} +3 -3
  113. package/dist/client/{checkout-DXc06Km2.mjs → checkout-Da9WSn7Y.mjs} +3 -3
  114. package/dist/client/client.mjs +1630 -816
  115. package/dist/client/{context-CDLCfDep.mjs → context-BFeY_Wbm.mjs} +1 -1
  116. package/dist/client/{dates-ClFx7Epy.mjs → dates-BhwVSxDg.mjs} +2 -2
  117. package/dist/client/{eventCalendar-BaGpnOGN.mjs → eventCalendar-BAtdnR4s.mjs} +9 -9
  118. package/dist/client/{eventCombined-ywONOx2m.mjs → eventCombined-RksA3tRC.mjs} +4 -4
  119. package/dist/client/{eventDetails-B2LlOTYY.mjs → eventDetails-BB9hB4wR.mjs} +4 -4
  120. package/dist/client/{eventListing-CMgxWpqP.mjs → eventListing-BpJBkviM.mjs} +4 -4
  121. package/dist/client/{eventRegistration-BarLDNHR.mjs → eventRegistration-B18jFq_o.mjs} +8 -8
  122. package/dist/client/{form-BjqKaBJ7.mjs → form-Dodt8Y8t.mjs} +3 -3
  123. package/dist/client/{headerSection-CP4XDwz1.mjs → headerSection-B0qj9ttg.mjs} +3 -3
  124. package/dist/client/hooks.mjs +550 -99
  125. package/dist/client/{locationMap-C5gW-d-w.mjs → locationMap-gn-iMk7a.mjs} +3 -3
  126. package/dist/client/{newsletterForm-afXlVSA0.mjs → newsletterForm-oByLdFkr.mjs} +26 -5
  127. package/dist/client/{pricing-Bl1CAl28.mjs → pricing-BOU0_Hai.mjs} +1 -1
  128. package/dist/client/{productDetail-CvQ3jWQe.mjs → productDetail-D9v1zH2j.mjs} +3 -3
  129. package/dist/client/{productList-U874PGO7.mjs → productList-D-mItx1n.mjs} +3 -3
  130. package/dist/client/{propsDecoder-DPXtCZWP.mjs → propsDecoder-C3TP_z4x.mjs} +72 -33
  131. package/dist/client/rendering/client.mjs +900 -266
  132. package/dist/client/rendering/islands.mjs +17 -17
  133. package/dist/client/rendering.mjs +1637 -788
  134. package/dist/client/{routeIslandApiConfig-NGYiUWXu.mjs → routeIslandApiConfig-DzN1W0sq.mjs} +1 -1
  135. package/dist/client/{scrollToBlock-0-C3DVhK.mjs → scrollToBlock-CWs8EIdo.mjs} +1 -1
  136. package/dist/client/{shop-BXjXhM9P.mjs → shop-Dufw7N7o.mjs} +7 -7
  137. package/dist/client/{shop-commerce-BZ6UlWJ7.mjs → shop-commerce-gklw_hLF.mjs} +6 -6
  138. package/dist/client/{shop-commerce-store-DlSBXb3D.mjs → shop-commerce-store-Sf1eyTaZ.mjs} +1 -1
  139. package/dist/client/{shopShared-kkEOiLPA.mjs → shopShared-B5nyA41S.mjs} +4 -4
  140. package/dist/client/{useEventFilters-zvnnE1ro.mjs → useEventFilters-CLL829vy.mjs} +6 -6
  141. package/dist/preview-next/before-render.mjs +147 -77
  142. package/dist/preview-next/client/runtime.mjs +4 -4
  143. package/dist/preview-next/keepAlive.mjs +147 -77
  144. package/dist/preview-next/middleware.mjs +147 -77
  145. package/dist/{previewAuthStore-JA6-I_sL.mjs → previewAuthStore-BxAFlu-j.mjs} +147 -77
  146. package/dist/{sdk-runtime-DFJlQt3q.mjs → sdk-runtime-D0uZFev7.mjs} +2 -2
  147. package/dist/{separator-o8GHb1xh.mjs → separator-EvdCAn4V.mjs} +1 -1
  148. package/dist/server/components.mjs +1727 -991
  149. package/dist/server/config-validation.mjs +537 -97
  150. package/dist/server/config.mjs +537 -97
  151. package/dist/server/data.mjs +549 -98
  152. package/dist/server/index.mjs +159 -81
  153. package/dist/server/next.mjs +1460 -748
  154. package/dist/server/page-converter.mjs +384 -54
  155. package/dist/server/prebuild.mjs +1 -1
  156. package/dist/server/rendering/server.mjs +1712 -973
  157. package/dist/server/rendering.mjs +1748 -1001
  158. package/dist/server/routing.mjs +624 -131
  159. package/dist/server/server.mjs +554 -138
  160. package/dist/server/theme-bridge.mjs +1192 -27
  161. package/dist/server/theme.mjs +6 -4
  162. 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((() => {
@@ -1579,7 +1648,7 @@ var init_public_profile = __esmMin((() => {
1579
1648
  var init_public_experience = __esmMin((() => {}));
1580
1649
  //#endregion
1581
1650
  //#region ../core/src/rich-text.ts
1582
- function isRecord$27(value) {
1651
+ function isRecord$28(value) {
1583
1652
  return typeof value === "object" && value !== null && !Array.isArray(value);
1584
1653
  }
1585
1654
  function hasOnlyKeys(value, allowed) {
@@ -1590,10 +1659,10 @@ function isDenseArrayOf(value, predicate) {
1590
1659
  return true;
1591
1660
  }
1592
1661
  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));
1662
+ return isRecord$28(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === void 0 || isRecord$28(value.attrs));
1594
1663
  }
1595
1664
  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;
1665
+ 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
1666
  if (value.type === "text") return typeof value.text === "string" && value.text.length > 0 && value.content === void 0;
1598
1667
  return value.text === void 0;
1599
1668
  }
@@ -1638,10 +1707,10 @@ var init_mailing_list_identifiers = __esmMin((() => {
1638
1707
  //#endregion
1639
1708
  //#region ../core/src/mailing-list-targeting-parser.ts
1640
1709
  function parseMailingListPlacementTargeting(value) {
1641
- if (!isRecord$26(value)) return targetingError("shape");
1710
+ if (!isRecord$27(value)) return targetingError("shape");
1642
1711
  if (value.mode === "default") return ok$2({ mode: "default" });
1643
1712
  if (value.mode !== "custom") return targetingError("mode");
1644
- if (!isRecord$26(value.selection)) return targetingError("selection");
1713
+ if (!isRecord$27(value.selection)) return targetingError("selection");
1645
1714
  switch (value.provider) {
1646
1715
  case "native": return parseNativeTargeting(value.selection);
1647
1716
  case "brevo": return parseBrevoTargeting(value.selection);
@@ -1650,7 +1719,7 @@ function parseMailingListPlacementTargeting(value) {
1650
1719
  }
1651
1720
  }
1652
1721
  function parseMailingListVisitorSelection(value) {
1653
- if (!isRecord$26(value)) return visitorSelectionError$1("shape");
1722
+ if (!isRecord$27(value)) return visitorSelectionError$1("shape");
1654
1723
  switch (value.provider) {
1655
1724
  case "native": {
1656
1725
  const topicIds = parseVisitorIds(value.topicIds, parseNewsletterListId);
@@ -1783,7 +1852,7 @@ function parseVisitorIds(value, parseId) {
1783
1852
  }
1784
1853
  return parsed;
1785
1854
  }
1786
- function isRecord$26(value) {
1855
+ function isRecord$27(value) {
1787
1856
  return typeof value === "object" && value !== null && !Array.isArray(value);
1788
1857
  }
1789
1858
  function targetingError(field) {
@@ -1840,7 +1909,7 @@ var init_mailing_list_setup = __esmMin((() => {
1840
1909
  //#endregion
1841
1910
  //#region ../core/src/mailing-list-setup-parser.ts
1842
1911
  function parseMailingListGlobalForm(value) {
1843
- if (!isRecord$22(value)) return formError("shape");
1912
+ if (!isRecord$23(value)) return formError("shape");
1844
1913
  const fields = parseGlobalFields(value.fields);
1845
1914
  if (fields === null) return formError("fields");
1846
1915
  if (typeof value.includeOptionalPhone !== "boolean") return formError("includeOptionalPhone");
@@ -1859,7 +1928,7 @@ function parseMailingListGlobalForm(value) {
1859
1928
  });
1860
1929
  }
1861
1930
  function parseGlobalFields(value) {
1862
- if (!isRecord$22(value) || typeof value.preset !== "string") return null;
1931
+ if (!isRecord$23(value) || typeof value.preset !== "string") return null;
1863
1932
  switch (value.preset) {
1864
1933
  case "emailOnly": return { preset: "emailOnly" };
1865
1934
  case "emailName": return isRequirement(value.name) ? {
@@ -1896,7 +1965,7 @@ function parseOptionalString(value) {
1896
1965
  function isRequirement(value) {
1897
1966
  return value === "required" || value === "optional";
1898
1967
  }
1899
- function isRecord$22(value) {
1968
+ function isRecord$23(value) {
1900
1969
  return typeof value === "object" && value !== null && !Array.isArray(value);
1901
1970
  }
1902
1971
  function formError(field) {
@@ -1954,7 +2023,7 @@ var init_mailing_list_targeting = __esmMin((() => {
1954
2023
  //#endregion
1955
2024
  //#region ../core/src/mailing-list-runtime.ts
1956
2025
  function parseMailingListRuntimeContext(value) {
1957
- if (!isRecord$21(value)) return invalidRuntimeContext();
2026
+ if (!isRecord$22(value)) return invalidRuntimeContext();
1958
2027
  if (value.tag === "not_configured") return ok$2({ tag: "not_configured" });
1959
2028
  if (value.tag !== "configured") return invalidRuntimeContext();
1960
2029
  const revision = parseMailingListSetupRevision(value.revision);
@@ -1969,7 +2038,7 @@ function parseMailingListRuntimeContext(value) {
1969
2038
  });
1970
2039
  }
1971
2040
  function parseRuntimeProvider(value) {
1972
- if (!isRecord$21(value)) return null;
2041
+ if (!isRecord$22(value)) return null;
1973
2042
  switch (value.provider) {
1974
2043
  case "native": return parseNativeRuntimeProvider(value);
1975
2044
  case "brevo": return parseBrevoRuntimeProvider(value);
@@ -2001,7 +2070,7 @@ function parseMailchimpRuntimeProvider(value) {
2001
2070
  if (defaultAudienceId === null) return null;
2002
2071
  const audiences = [];
2003
2072
  for (const audience of value.audiences) {
2004
- if (!isRecord$21(audience)) return null;
2073
+ if (!isRecord$22(audience)) return null;
2005
2074
  const id = parseMailchimpAudienceId(audience.id);
2006
2075
  const name = parseName(audience.name);
2007
2076
  if (id === null || name === null || !Array.isArray(audience.interests)) return null;
@@ -2022,7 +2091,7 @@ function parseMailchimpRuntimeProvider(value) {
2022
2091
  function parseMailchimpInterests(value) {
2023
2092
  const interests = [];
2024
2093
  for (const interest of value) {
2025
- if (!isRecord$21(interest)) return null;
2094
+ if (!isRecord$22(interest)) return null;
2026
2095
  const id = parseMailchimpInterestId(interest.id);
2027
2096
  const name = parseName(interest.name);
2028
2097
  const categoryName = parseName(interest.categoryName);
@@ -2039,7 +2108,7 @@ function parseNamedItems(value, parseId) {
2039
2108
  if (!Array.isArray(value)) return null;
2040
2109
  const items = [];
2041
2110
  for (const item of value) {
2042
- if (!isRecord$21(item)) return null;
2111
+ if (!isRecord$22(item)) return null;
2043
2112
  const id = parseId(item.id);
2044
2113
  const name = parseName(item.name);
2045
2114
  if (id === null || name === null) return null;
@@ -2055,7 +2124,7 @@ function parseName(value) {
2055
2124
  const name = value.trim();
2056
2125
  return name.length > 0 ? name : null;
2057
2126
  }
2058
- function isRecord$21(value) {
2127
+ function isRecord$22(value) {
2059
2128
  return typeof value === "object" && value !== null && !Array.isArray(value);
2060
2129
  }
2061
2130
  function invalidRuntimeContext() {
@@ -2496,6 +2565,7 @@ var init_src$2 = __esmMin((() => {
2496
2565
  init_booking_instalments();
2497
2566
  init_booking_date_time();
2498
2567
  init_booking_consent();
2568
+ init_appointment_service_categories();
2499
2569
  init_dated_offering_settlement();
2500
2570
  init_person_records();
2501
2571
  init_rectification();
@@ -2680,11 +2750,11 @@ var init_storagePathDomain = __esmMin((() => {}));
2680
2750
  var init_fileAssetLegacyProjection = __esmMin((() => {}));
2681
2751
  //#endregion
2682
2752
  //#region ../media-core/src/typeGuards.ts
2683
- function isRecord$19(value) {
2753
+ function isRecord$20(value) {
2684
2754
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2685
2755
  }
2686
2756
  function asRecord$1(value) {
2687
- return isRecord$19(value) ? value : void 0;
2757
+ return isRecord$20(value) ? value : void 0;
2688
2758
  }
2689
2759
  var init_typeGuards$1 = __esmMin((() => {}));
2690
2760
  function isFileJsonObject(value) {
@@ -2694,7 +2764,7 @@ function isFileJsonValue(value) {
2694
2764
  if (value === null || typeof value === "string" || typeof value === "boolean") return true;
2695
2765
  if (typeof value === "number") return Number.isFinite(value);
2696
2766
  if (Array.isArray(value)) return value.every(isFileJsonValue);
2697
- return isRecord$19(value) && isFileJsonObject(value);
2767
+ return isRecord$20(value) && isFileJsonObject(value);
2698
2768
  }
2699
2769
  var init_filePersistence = __esmMin((() => {
2700
2770
  init_src$2();
@@ -3002,7 +3072,7 @@ function isCmsMediaObjectType(value) {
3002
3072
  return typeof value === "string" && CMS_MEDIA_OBJECT_TYPES.includes(value);
3003
3073
  }
3004
3074
  function isCmsMediaObject(value) {
3005
- if (!isRecord$19(value)) return false;
3075
+ if (!isRecord$20(value)) return false;
3006
3076
  const type = value.type;
3007
3077
  if (!isCmsMediaObjectType(type)) return false;
3008
3078
  if ("attrs" in value) return false;
@@ -3013,7 +3083,7 @@ function collectMediaObjects(value, out, location = "") {
3013
3083
  value.forEach((item, index) => collectMediaObjects(item, out, `${location}[${index}]`));
3014
3084
  return;
3015
3085
  }
3016
- if (!isRecord$19(value)) return;
3086
+ if (!isRecord$20(value)) return;
3017
3087
  if (isCmsMediaObject(value)) out.push({
3018
3088
  obj: value,
3019
3089
  location
@@ -3047,8 +3117,8 @@ function walkAndCollectImageAttrs(value, out, location = "") {
3047
3117
  value.forEach((item, index) => walkAndCollectImageAttrs(item, out, `${location}[${index}]`));
3048
3118
  return;
3049
3119
  }
3050
- if (!isRecord$19(value)) return;
3051
- if (value.type === "image" && isRecord$19(value.attrs)) out.push({
3120
+ if (!isRecord$20(value)) return;
3121
+ if (value.type === "image" && isRecord$20(value.attrs)) out.push({
3052
3122
  attrs: value.attrs,
3053
3123
  location: location ? `${location}.attrs` : "attrs"
3054
3124
  });
@@ -3108,7 +3178,7 @@ function mediaIdentifierOrFallback(current, fallback) {
3108
3178
  var init_renderableMediaAsset = __esmMin((() => {}));
3109
3179
  //#endregion
3110
3180
  //#region ../media-core/src/contentMediaAudit.ts
3111
- function isRecord$18(value) {
3181
+ function isRecord$19(value) {
3112
3182
  return typeof value === "object" && value !== null && !Array.isArray(value);
3113
3183
  }
3114
3184
  function summarizeContentMediaValue(value) {
@@ -3119,7 +3189,7 @@ function summarizeContentMediaValue(value) {
3119
3189
  }
3120
3190
  if (value === null) return "null";
3121
3191
  if (Array.isArray(value)) return `array(${value.length})`;
3122
- if (isRecord$18(value)) return `object(${Object.keys(value).sort().join(",")})`;
3192
+ if (isRecord$19(value)) return `object(${Object.keys(value).sort().join(",")})`;
3123
3193
  return typeof value;
3124
3194
  }
3125
3195
  function auditMediaValue(context, path, allowedMediaTypes, value) {
@@ -3143,9 +3213,9 @@ function auditMediaValue(context, path, allowedMediaTypes, value) {
3143
3213
  allowedMediaTypes
3144
3214
  }];
3145
3215
  }
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));
3216
+ const hasValidIdentifier = isRecord$19(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0;
3217
+ const hasValidExplicitType = isRecord$19(value) && (!("type" in value) || typeof value.type === "string" && allowedMediaTypes.includes(value.type));
3218
+ const hasRetiredEnvironmentField = isRecord$19(value) && ("assetId" in value || "storagePath" in value || "storageBucket" in value || "src" in value || "mimeType" in value || looksLikeUuid(value.id));
3149
3219
  if (hasValidIdentifier && hasValidExplicitType && !hasRetiredEnvironmentField) return [];
3150
3220
  return [{
3151
3221
  tag: "malformed-object",
@@ -3156,7 +3226,7 @@ function auditMediaValue(context, path, allowedMediaTypes, value) {
3156
3226
  }];
3157
3227
  }
3158
3228
  function auditFields(context, fields, value, parentPath) {
3159
- if (!isRecord$18(value)) return [];
3229
+ if (!isRecord$19(value)) return [];
3160
3230
  const findings = [];
3161
3231
  for (const field of fields) {
3162
3232
  if (!(field.id in value)) continue;
@@ -3173,7 +3243,7 @@ function auditFields(context, fields, value, parentPath) {
3173
3243
  if (!Array.isArray(fieldValue)) break;
3174
3244
  for (const [index, item] of fieldValue.entries()) {
3175
3245
  const itemPath = [...fieldPath, index];
3176
- if (!isRecord$18(item)) {
3246
+ if (!isRecord$19(item)) {
3177
3247
  findings.push({
3178
3248
  tag: "missing-schema",
3179
3249
  context,
@@ -3207,7 +3277,7 @@ var init_contentMediaAudit = __esmMin((() => {
3207
3277
  }));
3208
3278
  //#endregion
3209
3279
  //#region ../media-core/src/contentMediaAuditFieldAdapter.ts
3210
- function isRecord$17(value) {
3280
+ function isRecord$18(value) {
3211
3281
  return typeof value === "object" && value !== null && !Array.isArray(value);
3212
3282
  }
3213
3283
  function unknownArray(value) {
@@ -3216,7 +3286,7 @@ function unknownArray(value) {
3216
3286
  function fieldsArray(value) {
3217
3287
  const directFields = unknownArray(value);
3218
3288
  if (directFields) return directFields;
3219
- if (!isRecord$17(value)) return null;
3289
+ if (!isRecord$18(value)) return null;
3220
3290
  return unknownArray(value.fields);
3221
3291
  }
3222
3292
  function nestedFields(field) {
@@ -3281,7 +3351,7 @@ function mergeFields(fields) {
3281
3351
  }
3282
3352
  function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds = ["document"]) {
3283
3353
  return mergeFields(rawFields.flatMap((rawField) => {
3284
- if (!isRecord$17(rawField)) return [];
3354
+ if (!isRecord$18(rawField)) return [];
3285
3355
  const { id, type } = rawField;
3286
3356
  if (typeof id !== "string" || !id || typeof type !== "string") return [];
3287
3357
  if (type === "media" || type === "file") return [{
@@ -3289,7 +3359,7 @@ function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds
3289
3359
  id,
3290
3360
  allowedMediaTypes: allowedMediaTypes(rawField, type, defaultFileKinds)
3291
3361
  }];
3292
- if (flattenRepeaterGroups && type === "group" && isRecord$17(rawField.ui) && rawField.ui.flattenInRepeater === true) return adaptFields(nestedFields(rawField), false, defaultFileKinds);
3362
+ if (flattenRepeaterGroups && type === "group" && isRecord$18(rawField.ui) && rawField.ui.flattenInRepeater === true) return adaptFields(nestedFields(rawField), false, defaultFileKinds);
3293
3363
  if (type === "group" || type === "modal") return [{
3294
3364
  kind: "object",
3295
3365
  id,
@@ -3298,14 +3368,14 @@ function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds
3298
3368
  if (type === "tabGroup") return [{
3299
3369
  kind: "object",
3300
3370
  id,
3301
- fields: adaptFields((unknownArray(rawField.tabs) ?? []).flatMap((tab) => isRecord$17(tab) ? unknownArray(tab.fields) ?? [] : []), false, defaultFileKinds)
3371
+ fields: adaptFields((unknownArray(rawField.tabs) ?? []).flatMap((tab) => isRecord$18(tab) ? unknownArray(tab.fields) ?? [] : []), false, defaultFileKinds)
3302
3372
  }];
3303
3373
  if (type !== "repeater") return [];
3304
- if (isRecord$17(rawField.itemTypes)) return [{
3374
+ if (isRecord$18(rawField.itemTypes)) return [{
3305
3375
  kind: "repeater",
3306
3376
  id,
3307
3377
  mode: "variants",
3308
- variants: Object.entries(rawField.itemTypes).flatMap(([typeId, itemType]) => isRecord$17(itemType) && Array.isArray(itemType.fields) ? [{
3378
+ variants: Object.entries(rawField.itemTypes).flatMap(([typeId, itemType]) => isRecord$18(itemType) && Array.isArray(itemType.fields) ? [{
3309
3379
  typeId,
3310
3380
  fields: adaptFields(itemType.fields, true, defaultFileKinds)
3311
3381
  }] : []),
@@ -7715,6 +7785,15 @@ function asSiteStyleId(value) {
7715
7785
  function asDesignSystemTemplateId(value) {
7716
7786
  return brandNonEmptyString(value, "DesignSystemTemplateId");
7717
7787
  }
7788
+ function toNonEmptyReadonlyArray(values) {
7789
+ const [first, ...rest] = values;
7790
+ return first === void 0 ? null : [first, ...rest];
7791
+ }
7792
+ function requireNonEmptyReadonlyArray(values, message = "Expected a non-empty array.") {
7793
+ const result = toNonEmptyReadonlyArray(values);
7794
+ if (!result) throw new Error(message);
7795
+ return result;
7796
+ }
7718
7797
  function makeBoundaryId(previousBlockId, nextBlockId) {
7719
7798
  return asDesignBoundaryId(`boundary:${encodeURIComponent(previousBlockId)}:${encodeURIComponent(nextBlockId)}`);
7720
7799
  }
@@ -7888,16 +7967,18 @@ var init_rawStyleFields = __esmMin((() => {
7888
7967
  "_frameStyles",
7889
7968
  "_sectionStyles",
7890
7969
  "_containerStyles",
7891
- "_cardStyles",
7892
- "_transitionStyles",
7893
- "styleGroup"
7970
+ "_cardStyles"
7894
7971
  ];
7895
7972
  themeV2BlockSpecificRawStyleFieldNames = [
7896
7973
  "_contentCardStyles",
7897
7974
  "_heroLayoutStyles",
7898
7975
  "_textCardStyles"
7899
7976
  ];
7900
- themeV2NormalEditorRawStyleFieldNames = [...themeV2RuntimeStyleFieldNames, ...themeV2BlockSpecificRawStyleFieldNames];
7977
+ themeV2NormalEditorRawStyleFieldNames = [
7978
+ ...themeV2RuntimeStyleFieldNames,
7979
+ ...themeV2BlockSpecificRawStyleFieldNames,
7980
+ "styleGroup"
7981
+ ];
7901
7982
  new Set(themeV2NormalEditorRawStyleFieldNames);
7902
7983
  [...themeV2RuntimeStyleFieldNames, ...themeV2BlockSpecificRawStyleFieldNames];
7903
7984
  pageDesignEditorBackgroundOverrideOwner = "page-design-editor";
@@ -7989,7 +8070,7 @@ var init_interactionPalette = __esmMin((() => {
7989
8070
  //#endregion
7990
8071
  //#region ../theme-core/src/boxBackgroundSchema.ts
7991
8072
  function normalizeAuthoringOpacity(value) {
7992
- if (!isRecord$16(value)) return value;
8073
+ if (!isRecord$17(value)) return value;
7993
8074
  return {
7994
8075
  ...value,
7995
8076
  opacity: normalizeOpacity(value.opacity)
@@ -7998,7 +8079,7 @@ function normalizeAuthoringOpacity(value) {
7998
8079
  function normalizeOpacity(value) {
7999
8080
  return typeof value === "number" && value > 1 && value <= 100 ? value / 100 : value;
8000
8081
  }
8001
- function isRecord$16(value) {
8082
+ function isRecord$17(value) {
8002
8083
  return typeof value === "object" && value !== null && !Array.isArray(value);
8003
8084
  }
8004
8085
  var boxBackgroundOverlaySchema, commonSchema, mediaPresentationSchema, boxBackgroundSchema;
@@ -8817,7 +8898,7 @@ var init_media$1 = __esmMin((() => {
8817
8898
  /**
8818
8899
  * Strict record guard: excludes null, primitives, and arrays.
8819
8900
  */
8820
- function isRecord$15(value) {
8901
+ function isRecord$16(value) {
8821
8902
  return typeof value === "object" && value !== null && !Array.isArray(value);
8822
8903
  }
8823
8904
  var init_typeGuards = __esmMin((() => {}));
@@ -9922,7 +10003,7 @@ function encodePublicProductCategorySelector(selector) {
9922
10003
  };
9923
10004
  }
9924
10005
  }
9925
- function isRecord$14(value) {
10006
+ function isRecord$15(value) {
9926
10007
  return typeof value === "object" && value !== null;
9927
10008
  }
9928
10009
  function isJsonResponseContentType(contentType) {
@@ -9946,15 +10027,15 @@ async function parseJsonBody(response) {
9946
10027
  }
9947
10028
  }
9948
10029
  function isFieldError(value) {
9949
- return isRecord$14(value) && !Array.isArray(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string";
10030
+ return isRecord$15(value) && !Array.isArray(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string";
9950
10031
  }
9951
10032
  function fieldErrorsFromUnknown(value) {
9952
10033
  if (!Array.isArray(value)) return void 0;
9953
10034
  return value.every(isFieldError) ? value : void 0;
9954
10035
  }
9955
10036
  function getBlockApiErrorDetails(data) {
9956
- if (!isRecord$14(data)) return {};
9957
- const nestedError = isRecord$14(data.error) ? data.error : void 0;
10037
+ if (!isRecord$15(data)) return {};
10038
+ const nestedError = isRecord$15(data.error) ? data.error : void 0;
9958
10039
  const message = typeof nestedError?.message === "string" ? nestedError.message : typeof data.message === "string" ? data.message : void 0;
9959
10040
  const code = typeof nestedError?.code === "string" ? nestedError.code : typeof data.code === "string" ? data.code : void 0;
9960
10041
  const fieldErrors = fieldErrorsFromUnknown(nestedError?.fieldErrors) ?? fieldErrorsFromUnknown(data.fieldErrors);
@@ -10968,11 +11049,11 @@ var UnsafePathSegmentError = class extends Error {
10968
11049
  function assertSafePathSegments(segments) {
10969
11050
  for (const segment of segments) if (typeof segment === "string" && UNSAFE_PATH_SEGMENTS.has(segment)) throw new UnsafePathSegmentError(segment);
10970
11051
  }
10971
- function isRecord$13(value) {
11052
+ function isRecord$14(value) {
10972
11053
  return typeof value === "object" && value !== null && !Array.isArray(value);
10973
11054
  }
10974
11055
  function isPathContainer(value) {
10975
- return Array.isArray(value) || isRecord$13(value);
11056
+ return Array.isArray(value) || isRecord$14(value);
10976
11057
  }
10977
11058
  function getContainerValue(container, segment) {
10978
11059
  if (typeof segment === "number") return Array.isArray(container) ? container[segment] : void 0;
@@ -13957,7 +14038,7 @@ function parseSiteIconStyleGuideVersionId(value) {
13957
14038
  //#region ../blocks/src/system/icons/value.ts
13958
14039
  init_semantic();
13959
14040
  const NO_BLOCK_ICON_VALUE = { kind: "none" };
13960
- function isRecord$12(value) {
14041
+ function isRecord$13(value) {
13961
14042
  return typeof value === "object" && value !== null && !Array.isArray(value);
13962
14043
  }
13963
14044
  function parseBlockIconValue(value) {
@@ -13965,7 +14046,7 @@ function parseBlockIconValue(value) {
13965
14046
  kind: "semantic",
13966
14047
  iconId: value
13967
14048
  };
13968
- if (!isRecord$12(value)) return NO_BLOCK_ICON_VALUE;
14049
+ if (!isRecord$13(value)) return NO_BLOCK_ICON_VALUE;
13969
14050
  switch (value.kind) {
13970
14051
  case "none": return NO_BLOCK_ICON_VALUE;
13971
14052
  case "semantic": return isSemanticIconId(value.iconId) ? {
@@ -14038,7 +14119,7 @@ function buildGeneratedBlockIconRenderAssetLookupFromUnknown(value) {
14038
14119
  if (!Array.isArray(value)) return { kind: "invalid_asset_list" };
14039
14120
  const inputs = [];
14040
14121
  for (const [index, item] of value.entries()) {
14041
- if (!isRecord$12(item)) return {
14122
+ if (!isRecord$13(item)) return {
14042
14123
  kind: "invalid_asset_entry",
14043
14124
  index,
14044
14125
  reason: "not_object"
@@ -14158,11 +14239,11 @@ function resolveFeatureCardGeneratedIconSizePx(value) {
14158
14239
  const parsed = parseFeatureCardGeneratedIconSizePx(value);
14159
14240
  return parsed.ok ? parsed.value : unsafeAsFeatureCardGeneratedIconSizePx(FEATURE_CARD_GENERATED_ICON_SIZE_PX.default);
14160
14241
  }
14161
- function isRecord$11(value) {
14242
+ function isRecord$12(value) {
14162
14243
  return typeof value === "object" && value !== null && !Array.isArray(value);
14163
14244
  }
14164
14245
  function toPlainTextFromTipTap(value) {
14165
- if (!isRecord$11(value)) return "";
14246
+ if (!isRecord$12(value)) return "";
14166
14247
  if (typeof value.text === "string") return value.text;
14167
14248
  const content = value.content;
14168
14249
  if (!Array.isArray(content)) return "";
@@ -15453,18 +15534,43 @@ const publicAppointmentServiceSchema = z.object({
15453
15534
  pricing: publicPricingSchema.optional(),
15454
15535
  priceAmount: z.number().nullable(),
15455
15536
  priceCurrency: z.string().nullable(),
15456
- location: publicLocationSchema
15537
+ location: publicLocationSchema,
15538
+ categoryId: z.string().nullable().optional().transform((value) => value ?? null)
15539
+ });
15540
+ const publicServiceCategorySchema = z.object({
15541
+ id: z.string().min(1),
15542
+ name: z.string(),
15543
+ description: z.string().nullable().optional().transform((value) => value ?? null)
15457
15544
  });
15458
- const publicAppointmentServicesSchema = z.array(publicAppointmentServiceSchema);
15545
+ /**
15546
+ * The loader hands over the catalogue `{ services, categories }` (issue 1403).
15547
+ * A bare services array is still accepted and read as a site with no
15548
+ * categories, so callers from before categories keep the flat list.
15549
+ */
15550
+ const publicAppointmentServicesSchema = z.union([z.array(publicAppointmentServiceSchema).transform((services) => ({
15551
+ services,
15552
+ categories: []
15553
+ })), z.object({
15554
+ services: z.array(publicAppointmentServiceSchema),
15555
+ categories: z.array(publicServiceCategorySchema)
15556
+ })]);
15459
15557
  const EMPTY_MESSAGE = "No appointments are available right now.";
15460
15558
  const ERROR_MESSAGE = "Appointments are temporarily unavailable. Please try again shortly.";
15559
+ const OTHER_GROUP_KEY = "other";
15560
+ const OTHER_GROUP_HEADING = "Other";
15461
15561
  function parseServices(value) {
15462
15562
  const result = publicAppointmentServicesSchema.safeParse(value);
15463
15563
  return result.success ? {
15464
15564
  kind: "valid",
15465
- services: result.data
15565
+ services: result.data.services,
15566
+ categories: result.data.categories
15466
15567
  } : { kind: "invalid" };
15467
15568
  }
15569
+ function readCategoryFilter(value) {
15570
+ if (typeof value !== "string") return null;
15571
+ const trimmed = value.trim();
15572
+ return trimmed.length > 0 ? trimmed : null;
15573
+ }
15468
15574
  function formatAppointmentDuration(durationMinutes) {
15469
15575
  return `${durationMinutes} min`;
15470
15576
  }
@@ -15538,8 +15644,44 @@ function buildRows(services, pathname, servicePathPrefix) {
15538
15644
  detailsHref: servicePathPrefix && service.slug ? `${servicePathPrefix.replace(/\/$/, "")}/${encodeURIComponent(service.slug)}` : null
15539
15645
  }));
15540
15646
  }
15647
+ const EMPTY_VIEW = {
15648
+ kind: "empty",
15649
+ message: EMPTY_MESSAGE
15650
+ };
15651
+ function categoryGroup(category, services, toRows) {
15652
+ return {
15653
+ key: category.id,
15654
+ heading: category.name,
15655
+ intro: category.description,
15656
+ rows: toRows(services)
15657
+ };
15658
+ }
15659
+ function viewFromLayout(layout, toRows) {
15660
+ switch (layout.kind) {
15661
+ case "uncategorized-site": return layout.services.length === 0 ? EMPTY_VIEW : {
15662
+ kind: "items",
15663
+ rows: toRows(layout.services)
15664
+ };
15665
+ case "categorized-site": {
15666
+ const groups = layout.groups.map((group) => categoryGroup(group.category, group.services, toRows));
15667
+ const other = layout.other.length === 0 ? [] : [{
15668
+ key: OTHER_GROUP_KEY,
15669
+ heading: OTHER_GROUP_HEADING,
15670
+ intro: null,
15671
+ rows: toRows(layout.other)
15672
+ }];
15673
+ const allGroups = [...groups, ...other];
15674
+ return allGroups.length === 0 ? EMPTY_VIEW : {
15675
+ kind: "groups",
15676
+ groups: allGroups
15677
+ };
15678
+ }
15679
+ default: return assertNever$22(layout);
15680
+ }
15681
+ }
15541
15682
  function buildAppointmentServicesView(input) {
15542
- if (!input.pathname) return {
15683
+ const pathname = input.pathname;
15684
+ if (!pathname) return {
15543
15685
  kind: "error",
15544
15686
  message: ERROR_MESSAGE
15545
15687
  };
@@ -15548,76 +15690,128 @@ function buildAppointmentServicesView(input) {
15548
15690
  kind: "error",
15549
15691
  message: ERROR_MESSAGE
15550
15692
  };
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
- };
15693
+ const toRows = (services) => buildRows(services, pathname, input.servicePathPrefix ?? null);
15694
+ const view = selectCategoryView(parsed.categories, parsed.services, readCategoryFilter(input.categoryId));
15695
+ switch (view.kind) {
15696
+ case "single": return view.group.services.length === 0 ? EMPTY_VIEW : {
15697
+ kind: "groups",
15698
+ groups: [categoryGroup(view.group.category, view.group.services, toRows)]
15699
+ };
15700
+ case "all":
15701
+ case "fallback-all": return viewFromLayout(view.layout, toRows);
15702
+ default: return assertNever$22(view);
15703
+ }
15704
+ }
15705
+ function AppointmentServiceRows({ rows, RowTitle }) {
15706
+ return /* @__PURE__ */ jsx("div", {
15707
+ className: "appointment-services-rows rb-flex rb-flex-col",
15708
+ children: rows.map((row) => /* @__PURE__ */ jsxs("article", {
15709
+ className: "appointment-services-row rb-border-b rb-border-border rb-py-4",
15710
+ children: [
15711
+ /* @__PURE__ */ jsxs("div", {
15712
+ className: "appointment-services-row-main rb-min-w-0",
15713
+ children: [
15714
+ /* @__PURE__ */ jsx(RowTitle, {
15715
+ className: "appointment-services-row-title rb-text-lg rb-font-medium rb-leading-tight",
15716
+ children: row.detailsHref ? /* @__PURE__ */ jsx("a", {
15717
+ href: row.detailsHref,
15718
+ children: row.title
15719
+ }) : row.title
15720
+ }),
15721
+ row.description ? /* @__PURE__ */ jsx("p", {
15722
+ className: "appointment-services-row-description rb-text-sm rb-leading-6 status-muted",
15723
+ children: row.description
15724
+ }) : null,
15725
+ /* @__PURE__ */ jsxs("p", {
15726
+ className: "appointment-services-row-meta rb-caption status-muted",
15727
+ children: [row.durationLabel, row.locationLabel ? ` · ${row.locationLabel}` : ""]
15728
+ })
15729
+ ]
15730
+ }),
15731
+ row.priceLabel ? /* @__PURE__ */ jsx("span", {
15732
+ className: "appointment-services-row-price rb-text-lg",
15733
+ children: row.priceLabel
15734
+ }) : null,
15735
+ /* @__PURE__ */ jsx(ThemedButtonLink, {
15736
+ variant: "secondary",
15737
+ size: "sm",
15738
+ extraClassName: "appointment-services-row-action",
15739
+ href: row.href,
15740
+ label: "Check times",
15741
+ screenReaderSuffix: ` for ${row.title}`
15742
+ })
15743
+ ]
15744
+ }, row.id))
15745
+ });
15746
+ }
15747
+ function AppointmentServiceGroups({ groups, hasSectionHeading, idPrefix }) {
15748
+ const GroupHeading = hasSectionHeading ? "h3" : "h2";
15749
+ const RowTitle = hasSectionHeading ? "h4" : "h3";
15750
+ return /* @__PURE__ */ jsx("div", {
15751
+ className: "appointment-services-groups rb-flex rb-flex-col rb-gap-10",
15752
+ children: groups.map((group) => {
15753
+ const headingId = `${idPrefix}-group-${group.key}`;
15754
+ return /* @__PURE__ */ jsxs("section", {
15755
+ className: "appointment-services-group rb-flex rb-flex-col rb-gap-2",
15756
+ "aria-labelledby": headingId,
15757
+ "data-appointment-services-group": group.key,
15758
+ children: [/* @__PURE__ */ jsxs("div", {
15759
+ className: "appointment-services-group-heading",
15760
+ children: [/* @__PURE__ */ jsx(GroupHeading, {
15761
+ id: headingId,
15762
+ className: "appointment-services-group-title rb-text-sm rb-font-semibold rb-uppercase rb-tracking-wide",
15763
+ children: group.heading
15764
+ }), group.intro ? /* @__PURE__ */ jsx("p", {
15765
+ className: "appointment-services-group-intro rb-text-sm rb-leading-6 rb-max-w-prose status-muted",
15766
+ children: group.intro
15767
+ }) : null]
15768
+ }), /* @__PURE__ */ jsx(AppointmentServiceRows, {
15769
+ rows: group.rows,
15770
+ RowTitle
15771
+ })]
15772
+ }, group.key);
15773
+ })
15774
+ });
15559
15775
  }
15560
- function AppointmentServicesNode({ sectionHeading, services, pathname, servicePathPrefix, className }) {
15776
+ function AppointmentServicesNode({ sectionHeading, categoryId, services, pathname, servicePathPrefix, blockId, className }) {
15561
15777
  const view = buildAppointmentServicesView({
15562
15778
  services,
15779
+ categoryId,
15563
15780
  pathname: typeof pathname === "string" ? pathname : void 0,
15564
15781
  servicePathPrefix: typeof servicePathPrefix === "string" ? servicePathPrefix : null
15565
15782
  });
15566
15783
  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
- });
15784
+ const hasSectionHeading = Boolean(sectionHeading);
15785
+ switch (view.kind) {
15786
+ case "error":
15787
+ case "empty": return /* @__PURE__ */ jsx("div", {
15788
+ className: rootClassName,
15789
+ "data-appointment-services-state": view.kind,
15790
+ children: /* @__PURE__ */ jsx("p", {
15791
+ className: "rb-caption status-muted",
15792
+ role: "status",
15793
+ children: view.message
15794
+ })
15795
+ });
15796
+ case "items": return /* @__PURE__ */ jsx("div", {
15797
+ className: `${rootClassName} rb-flex rb-flex-col`,
15798
+ "data-appointment-services-state": "populated",
15799
+ children: /* @__PURE__ */ jsx(AppointmentServiceRows, {
15800
+ rows: view.rows,
15801
+ RowTitle: hasSectionHeading ? "h3" : "h2"
15802
+ })
15803
+ });
15804
+ case "groups": return /* @__PURE__ */ jsx("div", {
15805
+ className: `${rootClassName} rb-flex rb-flex-col`,
15806
+ "data-appointment-services-state": "populated",
15807
+ children: /* @__PURE__ */ jsx(AppointmentServiceGroups, {
15808
+ groups: view.groups,
15809
+ hasSectionHeading,
15810
+ idPrefix: `appointment-services-${blockId ?? "block"}`
15811
+ })
15812
+ });
15813
+ default: return assertNever$22(view);
15814
+ }
15621
15815
  }
15622
15816
  //#endregion
15623
15817
  //#region ../blocks/src/contracts/runtime.ts
@@ -15786,7 +15980,7 @@ function successSubmissionFeedback(message) {
15786
15980
  message
15787
15981
  };
15788
15982
  }
15789
- function isRecord$9(value) {
15983
+ function isRecord$10(value) {
15790
15984
  return typeof value === "object" && value !== null && !Array.isArray(value);
15791
15985
  }
15792
15986
  function deepEqualJsonValue(left, right) {
@@ -15796,8 +15990,8 @@ function deepEqualJsonValue(left, right) {
15796
15990
  if (left.length !== right.length) return false;
15797
15991
  return left.every((item, index) => deepEqualJsonValue(item, right[index]));
15798
15992
  }
15799
- if (isRecord$9(left) || isRecord$9(right)) {
15800
- if (!isRecord$9(left) || !isRecord$9(right)) return false;
15993
+ if (isRecord$10(left) || isRecord$10(right)) {
15994
+ if (!isRecord$10(left) || !isRecord$10(right)) return false;
15801
15995
  const leftKeys = Object.keys(left).sort();
15802
15996
  if (!deepEqualJsonValue(leftKeys, Object.keys(right).sort())) return false;
15803
15997
  return leftKeys.every((key) => deepEqualJsonValue(left[key], right[key]));
@@ -15979,12 +16173,12 @@ function resolveBookingSiteId(siteId, form) {
15979
16173
  return siteId || form?.siteId || "";
15980
16174
  }
15981
16175
  function normalizeBookingFormConfig(form) {
15982
- if (!isRecord$8(form)) return null;
16176
+ if (!isRecord$9(form)) return null;
15983
16177
  const id = readNonEmptyString(form.id);
15984
16178
  const name = readNonEmptyString(form.name);
15985
16179
  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;
16180
+ const schemaSource = isRecord$9(form.schema) ? form.schema : isRecord$9(form.schemaJson) ? form.schemaJson : null;
16181
+ const settingsSource = isRecord$9(form.settings) ? form.settings : isRecord$9(form.settingsJson) ? form.settingsJson : null;
15988
16182
  return {
15989
16183
  id,
15990
16184
  name,
@@ -16004,8 +16198,8 @@ function normalizeBookingServices(services) {
16004
16198
  };
16005
16199
  if (!services) return [];
16006
16200
  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) : [];
16201
+ if (isRecord$9(services) && "services" in services) return Array.isArray(services.services) ? services.services.filter(isBookingFormService).map(normalizeService) : [];
16202
+ return isRecord$9(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
16009
16203
  }
16010
16204
  function normalizeBookingSchema(value) {
16011
16205
  if (!value) return void 0;
@@ -16016,7 +16210,7 @@ function normalizeBookingSchema(value) {
16016
16210
  };
16017
16211
  }
16018
16212
  function readBookingFormField(value) {
16019
- if (!isRecord$8(value)) return null;
16213
+ if (!isRecord$9(value)) return null;
16020
16214
  const id = readNonEmptyString(value.id);
16021
16215
  const label = readNonEmptyString(value.label);
16022
16216
  const type = readBookingFieldType(value.type);
@@ -16059,7 +16253,7 @@ function readBookingFieldOptions(value) {
16059
16253
  return options.length > 0 ? options : void 0;
16060
16254
  }
16061
16255
  function readBookingFieldOption(value) {
16062
- if (!isRecord$8(value)) return null;
16256
+ if (!isRecord$9(value)) return null;
16063
16257
  const optionValue = readNonEmptyString(value.value);
16064
16258
  const label = readNonEmptyString(value.label);
16065
16259
  return optionValue && label ? {
@@ -16079,7 +16273,7 @@ function normalizeBookingSettings(value) {
16079
16273
  };
16080
16274
  }
16081
16275
  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);
16276
+ 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
16277
  }
16084
16278
  function collectBookingCustomFields(data) {
16085
16279
  const customFields = {};
@@ -16129,7 +16323,7 @@ function readStringArray(value) {
16129
16323
  const strings = value.filter((item) => typeof item === "string");
16130
16324
  return strings.length > 0 ? strings : void 0;
16131
16325
  }
16132
- function isRecord$8(value) {
16326
+ function isRecord$9(value) {
16133
16327
  return typeof value === "object" && value !== null;
16134
16328
  }
16135
16329
  function getBookingServicePaymentSelectionMode(service) {
@@ -16329,6 +16523,32 @@ var init_booking_form_state = __esmMin((() => {
16329
16523
  init_booking_form_reserved_fields();
16330
16524
  init_booking_form_consent();
16331
16525
  }));
16526
+ //#endregion
16527
+ //#region ../blocks/src/system/runtime/nodes/booking-form.service-categories.ts
16528
+ function isRecord$8(value) {
16529
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16530
+ }
16531
+ function readCategory(value) {
16532
+ if (!isRecord$8(value)) return [];
16533
+ const { id, name, description } = value;
16534
+ if (typeof id !== "string" || id.trim() === "" || typeof name !== "string") return [];
16535
+ return [{
16536
+ id,
16537
+ name,
16538
+ description: typeof description === "string" ? description : null
16539
+ }];
16540
+ }
16541
+ /**
16542
+ * The ordered service categories carried by a catalogue-shaped services
16543
+ * source (`{ services, categories }`). Every other shape has none, which
16544
+ * reads as a site without categories. Order is preserved: the database owns
16545
+ * it.
16546
+ */
16547
+ function normalizeBookingServiceCategories(services) {
16548
+ if (!isRecord$8(services) || !Array.isArray(services.categories)) return [];
16549
+ return services.categories.flatMap(readCategory);
16550
+ }
16551
+ var init_booking_form_service_categories = __esmMin((() => {}));
16332
16552
  var bookingFieldOptionSchema, standardBookingFieldRules, standardBookingFieldSchema, bookingFieldSchema, bookingSettingsSchema, bookingSourceFormSchema;
16333
16553
  var init_booking_runtime_sources = __esmMin((() => {
16334
16554
  init_noop();
@@ -16413,11 +16633,12 @@ function buildBookingFormInteractiveIslandProps(props) {
16413
16633
  source: props
16414
16634
  });
16415
16635
  }
16416
- var blockBookingPaymentTermsAvailabilitySchema, bookingServiceSchema, bookingServicesSourceSchema, bookingConfigSchema, bookingSourceSchema, bookingDisplaySchema, bookingHydrationSchema, bookingRenderSchema, bookingFormIslandMetaCodec, bookingFormIslandMetaKeys, bookingFormBlockRuntime, bookingFormInteractiveIslandPropsCodec;
16636
+ var blockBookingPaymentTermsAvailabilitySchema, bookingServiceSchema, bookingServiceCategorySchema, bookingServicesSourceSchema, bookingConfigSchema, bookingSourceSchema, bookingDisplaySchema, bookingHydrationSchema, bookingRenderSchema, bookingFormIslandMetaCodec, bookingFormIslandMetaKeys, bookingFormBlockRuntime, bookingFormInteractiveIslandPropsCodec;
16417
16637
  var init_booking_form_interactive = __esmMin((() => {
16418
16638
  init_islands();
16419
16639
  init_runtime();
16420
16640
  init_booking_form_state();
16641
+ init_booking_form_service_categories();
16421
16642
  init_booking_runtime_sources();
16422
16643
  init_bookingPaymentTerms();
16423
16644
  init_bookingConsent();
@@ -16440,11 +16661,20 @@ var init_booking_form_interactive = __esmMin((() => {
16440
16661
  priceCurrency: z.string().nullable().optional(),
16441
16662
  paymentTerms: blockBookingPaymentTermsSchema.optional(),
16442
16663
  paymentTermsAvailability: blockBookingPaymentTermsAvailabilitySchema.optional(),
16443
- consent: blockBookingConsentSchema.optional()
16664
+ consent: blockBookingConsentSchema.optional(),
16665
+ categoryId: z.string().nullable().optional()
16666
+ });
16667
+ bookingServiceCategorySchema = z.object({
16668
+ id: z.string(),
16669
+ name: z.string(),
16670
+ description: z.string().nullable()
16444
16671
  });
16445
16672
  bookingServicesSourceSchema = z.union([
16446
16673
  z.array(bookingServiceSchema),
16447
- z.object({ services: z.array(bookingServiceSchema).optional() }),
16674
+ z.object({
16675
+ services: z.array(bookingServiceSchema).optional(),
16676
+ categories: z.array(bookingServiceCategorySchema).optional()
16677
+ }),
16448
16678
  z.record(z.string(), bookingServiceSchema)
16449
16679
  ]);
16450
16680
  bookingConfigSchema = z.object({
@@ -16497,6 +16727,7 @@ var init_booking_form_interactive = __esmMin((() => {
16497
16727
  rebookContext: z.object({ kind: z.literal("portal_rebook") }).nullable().optional(),
16498
16728
  form: bookingConfigSchema.nullable(),
16499
16729
  services: z.array(bookingServiceSchema),
16730
+ serviceCategories: z.array(bookingServiceCategorySchema).optional(),
16500
16731
  spamProtectionEnabled: z.boolean().optional()
16501
16732
  });
16502
16733
  bookingRenderSchema = z.object({
@@ -16532,6 +16763,7 @@ var init_booking_form_interactive = __esmMin((() => {
16532
16763
  rebookContext: source.rebookContext ?? null,
16533
16764
  form,
16534
16765
  services,
16766
+ serviceCategories: normalizeBookingServiceCategories(source.services),
16535
16767
  spamProtectionEnabled: source.spamProtectionEnabled
16536
16768
  }
16537
16769
  };
@@ -23071,17 +23303,256 @@ var init_cardRoleVocabulary = __esmMin((() => {
23071
23303
  Object.keys(legacyItemSurfaceCardRoles);
23072
23304
  cardRoleWithLegacyAliasSchema = z.preprocess((value) => cardRoleFromLegacyVocabulary(value) ?? value, cardRoleSchema);
23073
23305
  }));
23074
- var init_compositionBudget = __esmMin((() => {
23075
- init_types$8();
23306
+ var init_runtimeBackgroundResolution = __esmMin((() => {
23307
+ init_boxBackgroundSchema();
23308
+ init_surfaces();
23309
+ init_rawStyleFields();
23076
23310
  }));
23077
- var defaultHeaderBrandPreferences;
23078
- var init_compilationPlan = __esmMin((() => {
23079
- defaultHeaderBrandPreferences = {
23080
- brandDisplay: "auto",
23081
- logoSize: "auto"
23082
- };
23311
+ var transitionCatalogueFamilies, mediumHeight, compactHeight, largeHeight, transitionCatalogueEntries;
23312
+ var init_transitionCatalogue = __esmMin((() => {
23313
+ init_types$8();
23314
+ transitionCatalogueFamilies = [
23315
+ {
23316
+ id: "gradient",
23317
+ label: "Gradient"
23318
+ },
23319
+ {
23320
+ id: "wave",
23321
+ label: "Wave"
23322
+ },
23323
+ {
23324
+ id: "angle",
23325
+ label: "Angle"
23326
+ }
23327
+ ];
23328
+ mediumHeight = "6rem + 2px";
23329
+ compactHeight = "4.5rem + 2px";
23330
+ largeHeight = "7rem + 2px";
23331
+ transitionCatalogueEntries = defineExactCatalog()([
23332
+ {
23333
+ intent: "soft-fade-short",
23334
+ optionId: asBoundaryOptionId("break:soft"),
23335
+ family: "gradient",
23336
+ variantId: "soft",
23337
+ label: "Soft",
23338
+ variantLabel: "Soft",
23339
+ treatment: {
23340
+ kind: "soft-fade",
23341
+ height: "medium",
23342
+ curve: "soft"
23343
+ },
23344
+ size: "medium",
23345
+ heightExpression: mediumHeight,
23346
+ shape: { kind: "fade" }
23347
+ },
23348
+ {
23349
+ intent: "soft-fade-long",
23350
+ optionId: asBoundaryOptionId("break:spacious"),
23351
+ family: "gradient",
23352
+ variantId: "spacious",
23353
+ label: "Spacious",
23354
+ variantLabel: "Spacious",
23355
+ treatment: {
23356
+ kind: "soft-fade",
23357
+ height: "large",
23358
+ curve: "spacious"
23359
+ },
23360
+ size: "spacious",
23361
+ heightExpression: "10rem",
23362
+ shape: { kind: "fade" }
23363
+ },
23364
+ {
23365
+ intent: "soft-fade-expansive",
23366
+ optionId: asBoundaryOptionId("break:expansive"),
23367
+ family: "gradient",
23368
+ variantId: "expansive",
23369
+ label: "Expansive",
23370
+ variantLabel: "Expansive",
23371
+ treatment: {
23372
+ kind: "soft-fade",
23373
+ height: "expansive",
23374
+ curve: "expansive"
23375
+ },
23376
+ size: "expansive",
23377
+ heightExpression: "14rem",
23378
+ shape: { kind: "fade" }
23379
+ },
23380
+ {
23381
+ intent: "wave-edge-balanced",
23382
+ optionId: asBoundaryOptionId("break:wave-balanced"),
23383
+ family: "wave",
23384
+ variantId: "balanced",
23385
+ label: "Balanced wave",
23386
+ variantLabel: "Balanced",
23387
+ treatment: {
23388
+ kind: "wave-edge",
23389
+ height: "medium",
23390
+ amplitude: "balanced",
23391
+ rhythm: "double",
23392
+ symmetry: "symmetrical"
23393
+ },
23394
+ size: "medium",
23395
+ heightExpression: mediumHeight,
23396
+ shape: {
23397
+ kind: "wave",
23398
+ mask: "balanced"
23399
+ }
23400
+ },
23401
+ {
23402
+ intent: "wave-edge",
23403
+ optionId: asBoundaryOptionId("break:wave"),
23404
+ family: "wave",
23405
+ variantId: "classic",
23406
+ label: "Classic wave",
23407
+ variantLabel: "Classic",
23408
+ treatment: {
23409
+ kind: "wave-edge",
23410
+ height: "medium",
23411
+ amplitude: "balanced",
23412
+ rhythm: "double",
23413
+ symmetry: "asymmetrical"
23414
+ },
23415
+ size: "medium",
23416
+ heightExpression: mediumHeight,
23417
+ shape: {
23418
+ kind: "wave",
23419
+ mask: "classic"
23420
+ }
23421
+ },
23422
+ {
23423
+ intent: "wave-edge-gentle",
23424
+ optionId: asBoundaryOptionId("break:wave-gentle"),
23425
+ family: "wave",
23426
+ variantId: "gentle",
23427
+ label: "Gentle wave",
23428
+ variantLabel: "Gentle",
23429
+ treatment: {
23430
+ kind: "wave-edge",
23431
+ height: "compact",
23432
+ amplitude: "subtle",
23433
+ rhythm: "single",
23434
+ symmetry: "symmetrical"
23435
+ },
23436
+ size: "compact",
23437
+ heightExpression: compactHeight,
23438
+ shape: {
23439
+ kind: "wave",
23440
+ mask: "gentle"
23441
+ }
23442
+ },
23443
+ {
23444
+ intent: "wave-edge-bold",
23445
+ optionId: asBoundaryOptionId("break:wave-bold"),
23446
+ family: "wave",
23447
+ variantId: "bold",
23448
+ label: "Bold wave",
23449
+ variantLabel: "Bold",
23450
+ treatment: {
23451
+ kind: "wave-edge",
23452
+ height: "large",
23453
+ amplitude: "bold",
23454
+ rhythm: "double",
23455
+ symmetry: "symmetrical"
23456
+ },
23457
+ size: "large",
23458
+ heightExpression: largeHeight,
23459
+ shape: {
23460
+ kind: "wave",
23461
+ mask: "bold"
23462
+ }
23463
+ },
23464
+ {
23465
+ intent: "wave-edge-drift",
23466
+ optionId: asBoundaryOptionId("break:wave-drift"),
23467
+ family: "wave",
23468
+ variantId: "drift",
23469
+ label: "Drift wave",
23470
+ variantLabel: "Drift",
23471
+ treatment: {
23472
+ kind: "wave-edge",
23473
+ height: "medium",
23474
+ amplitude: "balanced",
23475
+ rhythm: "double",
23476
+ symmetry: "drift"
23477
+ },
23478
+ size: "medium",
23479
+ heightExpression: mediumHeight,
23480
+ shape: {
23481
+ kind: "wave",
23482
+ mask: "drift"
23483
+ }
23484
+ },
23485
+ {
23486
+ intent: "angle-edge-subtle",
23487
+ optionId: asBoundaryOptionId("break:angle-subtle"),
23488
+ family: "angle",
23489
+ variantId: "subtle",
23490
+ label: "Subtle angle",
23491
+ variantLabel: "Subtle",
23492
+ treatment: {
23493
+ kind: "angle-edge",
23494
+ height: "compact",
23495
+ slope: "subtle",
23496
+ direction: "forward"
23497
+ },
23498
+ size: "compact",
23499
+ heightExpression: compactHeight,
23500
+ shape: {
23501
+ kind: "angle",
23502
+ polygon: {
23503
+ startTopPercent: 34,
23504
+ endTopPercent: 18
23505
+ }
23506
+ }
23507
+ },
23508
+ {
23509
+ intent: "angle-edge",
23510
+ optionId: asBoundaryOptionId("break:angle-balanced"),
23511
+ family: "angle",
23512
+ variantId: "balanced",
23513
+ label: "Balanced angle",
23514
+ variantLabel: "Balanced",
23515
+ treatment: {
23516
+ kind: "angle-edge",
23517
+ height: "medium",
23518
+ slope: "balanced",
23519
+ direction: "forward"
23520
+ },
23521
+ size: "medium",
23522
+ heightExpression: mediumHeight,
23523
+ shape: {
23524
+ kind: "angle",
23525
+ polygon: {
23526
+ startTopPercent: 42,
23527
+ endTopPercent: 8
23528
+ }
23529
+ }
23530
+ },
23531
+ {
23532
+ intent: "angle-edge-steep",
23533
+ optionId: asBoundaryOptionId("break:angle-steep"),
23534
+ family: "angle",
23535
+ variantId: "steep",
23536
+ label: "Steep angle",
23537
+ variantLabel: "Steep",
23538
+ treatment: {
23539
+ kind: "angle-edge",
23540
+ height: "large",
23541
+ slope: "steep",
23542
+ direction: "forward"
23543
+ },
23544
+ size: "large",
23545
+ heightExpression: largeHeight,
23546
+ shape: {
23547
+ kind: "angle",
23548
+ polygon: {
23549
+ startTopPercent: 54,
23550
+ endTopPercent: 0
23551
+ }
23552
+ }
23553
+ }
23554
+ ]);
23083
23555
  }));
23084
- var init_schemaVersionedJson = __esmMin((() => {}));
23085
23556
  //#endregion
23086
23557
  //#region ../theme-core/src/site-styles/sectionTransitions.ts
23087
23558
  function boundaryRuntimeMetrics(family, size, heightExpression) {
@@ -23092,46 +23563,120 @@ function boundaryRuntimeMetrics(family, size, heightExpression) {
23092
23563
  reserveCss: `calc((${heightExpression}) / 2)`
23093
23564
  };
23094
23565
  }
23095
- var noneBoundaryOption, softBoundaryOption, spaciousBoundaryOption;
23566
+ /**
23567
+ * The catalogue's compile-time coverage check guarantees an entry per
23568
+ * decorative intent, so a miss here is a broken invariant, not bad input.
23569
+ */
23570
+ function requireCatalogueValue(values, intent) {
23571
+ const value = values.get(intent);
23572
+ if (value === void 0) throw new Error(`Transition catalogue has no entry for ${intent}`);
23573
+ return value;
23574
+ }
23575
+ function boundaryOptionForTransitionIntent(intent) {
23576
+ return requireCatalogueValue(boundaryOptionByIntent, intent);
23577
+ }
23578
+ function transitionVariantForEntry(entry) {
23579
+ return {
23580
+ familyId: entry.family,
23581
+ variantId: entry.variantId,
23582
+ label: entry.variantLabel,
23583
+ boundaryOption: boundaryOptionForTransitionIntent(entry.intent)
23584
+ };
23585
+ }
23586
+ function stableTreatmentKey(treatment) {
23587
+ switch (treatment.kind) {
23588
+ case "none": return "none";
23589
+ case "soft-fade": return `${treatment.kind}:${treatment.height}:${treatment.curve}`;
23590
+ case "wave-edge": return [
23591
+ treatment.kind,
23592
+ treatment.height,
23593
+ treatment.amplitude,
23594
+ treatment.rhythm,
23595
+ treatment.symmetry
23596
+ ].join(":");
23597
+ case "angle-edge": return [
23598
+ treatment.kind,
23599
+ treatment.height,
23600
+ treatment.slope,
23601
+ treatment.direction
23602
+ ].join(":");
23603
+ case "legacy-unsupported": return `${treatment.kind}:${treatment.boundaryOptionId}`;
23604
+ }
23605
+ }
23606
+ var noneBoundaryOption, boundaryOptionByIntent, softBoundaryOption, spaciousBoundaryOption, boundaryOptions, noneTransitionVariant, sectionTransitionFamilies;
23096
23607
  var init_sectionTransitions = __esmMin((() => {
23097
23608
  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");
23609
+ init_transitionCatalogue();
23610
+ new Map(transitionCatalogueEntries.map((entry) => [entry.intent, entry]));
23611
+ new Map(transitionCatalogueEntries.map((entry) => [entry.intent, boundaryRuntimeMetrics(entry.family, entry.size, entry.heightExpression)]));
23099
23612
  noneBoundaryOption = {
23100
23613
  id: asBoundaryOptionId("break:none"),
23101
23614
  label: "None",
23102
23615
  transitionAfter: "none",
23103
23616
  treatment: { kind: "none" }
23104
23617
  };
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
- }
23618
+ boundaryOptionByIntent = new Map(transitionCatalogueEntries.map((entry) => [entry.intent, {
23619
+ id: entry.optionId,
23620
+ label: entry.label,
23621
+ transitionAfter: entry.intent,
23622
+ treatment: entry.treatment
23623
+ }]));
23624
+ softBoundaryOption = boundaryOptionForTransitionIntent("soft-fade-short");
23625
+ spaciousBoundaryOption = boundaryOptionForTransitionIntent("soft-fade-long");
23626
+ boundaryOptionForTransitionIntent("soft-fade-expansive");
23627
+ boundaryOptionForTransitionIntent("wave-edge");
23628
+ boundaryOptionForTransitionIntent("wave-edge-balanced");
23629
+ boundaryOptionForTransitionIntent("wave-edge-gentle");
23630
+ boundaryOptionForTransitionIntent("wave-edge-bold");
23631
+ boundaryOptionForTransitionIntent("wave-edge-drift");
23632
+ boundaryOptionForTransitionIntent("angle-edge-subtle");
23633
+ boundaryOptionForTransitionIntent("angle-edge");
23634
+ boundaryOptionForTransitionIntent("angle-edge-steep");
23635
+ boundaryOptions = [noneBoundaryOption, ...transitionCatalogueEntries.map((entry) => boundaryOptionForTransitionIntent(entry.intent))];
23636
+ noneTransitionVariant = {
23637
+ familyId: "none",
23638
+ variantId: "none",
23639
+ label: "None",
23640
+ boundaryOption: noneBoundaryOption
23114
23641
  };
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
- }
23642
+ sectionTransitionFamilies = [{
23643
+ id: "none",
23644
+ label: "None",
23645
+ variants: [noneTransitionVariant]
23646
+ }, ...transitionCatalogueFamilies.map((family) => ({
23647
+ id: family.id,
23648
+ label: family.label,
23649
+ variants: requireNonEmptyReadonlyArray(transitionCatalogueEntries.filter((entry) => entry.family === family.id).map(transitionVariantForEntry), `Transition family ${family.id} has no catalogue entries.`)
23650
+ }))];
23651
+ new Map(boundaryOptions.map((option) => [option.id, option]));
23652
+ new Map(boundaryOptions.map((option) => [stableTreatmentKey(option.treatment), option]));
23653
+ new Map(sectionTransitionFamilies.flatMap((family) => family.variants.map((variant) => [variant.boundaryOption.id, variant])));
23654
+ new Map(sectionTransitionFamilies.map((family) => [family.id, family]));
23655
+ }));
23656
+ function requireThemeColorToken$1(value) {
23657
+ const token = parseThemeColorToken(value);
23658
+ if (!token) throw new Error(`Expected a theme color token. Received: ${value}`);
23659
+ return token;
23660
+ }
23661
+ var init_sectionBoundaryComposition = __esmMin((() => {
23662
+ init_themeColorTokens();
23663
+ init_runtimeBackgroundResolution();
23664
+ init_sectionTransitions();
23665
+ init_types$8();
23666
+ requireThemeColorToken$1("background");
23667
+ }));
23668
+ var init_compositionBudget = __esmMin((() => {
23669
+ init_types$8();
23670
+ init_sectionBoundaryComposition();
23671
+ }));
23672
+ var defaultHeaderBrandPreferences;
23673
+ var init_compilationPlan = __esmMin((() => {
23674
+ defaultHeaderBrandPreferences = {
23675
+ brandDisplay: "auto",
23676
+ logoSize: "auto"
23124
23677
  };
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
23678
  }));
23679
+ var init_schemaVersionedJson = __esmMin((() => {}));
23135
23680
  function addDuplicateStringIssues(ctx, input) {
23136
23681
  const seen = /* @__PURE__ */ new Set();
23137
23682
  const duplicates = /* @__PURE__ */ new Set();
@@ -23160,14 +23705,11 @@ function validateUniquePageDesignPins(intent, ctx) {
23160
23705
  label: "block Look pin"
23161
23706
  });
23162
23707
  addDuplicateStringIssues(ctx, {
23163
- values: intent.boundaryPins.map((pin) => boundaryKey(pin.previousBlockId, pin.nextBlockId)),
23708
+ values: intent.boundaryPins.map((pin) => makeBoundaryId(pin.previousBlockId, pin.nextBlockId)),
23164
23709
  path: ["boundaryPins"],
23165
23710
  label: "boundary pin"
23166
23711
  });
23167
23712
  }
23168
- function boundaryKey(previousBlockId, nextBlockId) {
23169
- return makeBoundaryId(previousBlockId, nextBlockId);
23170
- }
23171
23713
  var pageDesignUserPinnedAppearanceChoiceSchema, pageDesignUserPinnedBoundaryChoiceSchema, pageDesignBoundaryTreatmentSchema, pageDesignUserPinnedBoundaryTreatmentChoiceSchema, pageDesignBlockLookPinSchema, pageDesignBoundaryPinSchema, legacyPageDesignBoundaryPinSchema;
23172
23714
  var init_pageDesignIntent = __esmMin((() => {
23173
23715
  init_types$8();
@@ -26207,6 +26749,33 @@ var init_designState = __esmMin((() => {
26207
26749
  authority: styleAuthoritySchema
26208
26750
  }).strict();
26209
26751
  }));
26752
+ var init_pageFlowPresets = __esmMin((() => {
26753
+ init_sectionTransitions();
26754
+ init_types$8();
26755
+ asPageFlowPresetId("flow:quiet"), noneBoundaryOption.id, asPageFlowPresetId("flow:balanced"), softBoundaryOption.id, asPageFlowPresetId("flow:expressive"), spaciousBoundaryOption.id;
26756
+ })), init_lookResolution = __esmMin((() => {
26757
+ init_appearancePresets();
26758
+ init_types$8();
26759
+ }));
26760
+ var init_siteStyleBlockPolicies = __esmMin((() => {
26761
+ init_lookResolution();
26762
+ init_appearancePresets();
26763
+ init_pageDesignIntent();
26764
+ init_types$8();
26765
+ asPageFlowPresetId("generated-default");
26766
+ asSiteStyleId("site-style:south-west-twerk-school");
26767
+ asAppearancePresetId("hero-brand-scrim-fixed-frame");
26768
+ asLayoutVariantKey("classic");
26769
+ asLayoutVariantKey("micro");
26770
+ asBoundaryOptionId("break:wave");
26771
+ }));
26772
+ var init_joinTreatment = __esmMin((() => {
26773
+ init_pageFlowPresets();
26774
+ init_pageDesignIntent();
26775
+ init_sectionTransitions();
26776
+ init_siteStyleBlockPolicies();
26777
+ init_types$8();
26778
+ }));
26210
26779
  var init_imageSurfaceBackground = __esmMin((() => {
26211
26780
  init_types$8();
26212
26781
  }));
@@ -26247,8 +26816,9 @@ var init_runtimeCompiler = __esmMin((() => {
26247
26816
  init_styles();
26248
26817
  init_designState();
26249
26818
  init_types$8();
26819
+ init_joinTreatment();
26820
+ init_rawStyleFields();
26250
26821
  init_accessibleSurfaces();
26251
- init_sectionTransitions();
26252
26822
  init_tokens();
26253
26823
  init_gradients();
26254
26824
  init_imageSurfaceBackground();
@@ -26285,8 +26855,7 @@ var init_runtimeCompiler = __esmMin((() => {
26285
26855
  color: "surface",
26286
26856
  opacity: .88
26287
26857
  }
26288
- },
26289
- transitionClassName: (transition, fromSurface, toSurface) => transition === "none" ? null : `site-style-transition-${transition}-from-${fromSurface}-to-${toSurface}`
26858
+ }
26290
26859
  });
26291
26860
  defaultComponentRuntimeRules = defineComponentRuntimeRules({
26292
26861
  panelFrames: {
@@ -28022,22 +28591,12 @@ var init_curatedSiteStyles = __esmMin((() => {
28022
28591
  ];
28023
28592
  new Map(curatedSiteStyles.map((style) => [style.id, style]));
28024
28593
  }));
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();
28594
+ var init_pageDesignEditorCapabilities = __esmMin((() => {}));
28595
+ var init_pageDesignEditorBoundaries = __esmMin((() => {
28032
28596
  init_pageDesignIntent();
28033
- init_types$8();
28597
+ init_joinTreatment();
28034
28598
  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");
28599
+ init_types$8();
28041
28600
  }));
28042
28601
  var init_pageDesignEditorModel = __esmMin((() => {
28043
28602
  init_curatedSiteStyles();
@@ -28046,8 +28605,9 @@ var init_pageDesignEditorModel = __esmMin((() => {
28046
28605
  init_siteStyleBlockPolicies();
28047
28606
  init_pageDesignIntent();
28048
28607
  init_types$8();
28608
+ init_pageFlowPresets();
28609
+ init_pageDesignEditorBoundaries();
28049
28610
  init_sectionTransitions();
28050
- asPageFlowPresetId("flow:quiet"), noneBoundaryOption.id, asPageFlowPresetId("flow:balanced"), softBoundaryOption.id, asPageFlowPresetId("flow:expressive"), spaciousBoundaryOption.id;
28051
28611
  })), init_concretePalette = __esmMin((() => {
28052
28612
  init_colors();
28053
28613
  })), blockDesignFallback;
@@ -28056,7 +28616,6 @@ var init_resolver = __esmMin((() => {
28056
28616
  blockDesignFallback = {
28057
28617
  sectionSurface: "base",
28058
28618
  contentFrame: plainFrame,
28059
- transitionAfter: "none",
28060
28619
  sectionHeight: "default",
28061
28620
  heroLegibility: "none",
28062
28621
  imageSurfaceTreatment: { kind: "none" },
@@ -28067,7 +28626,7 @@ var init_resolver = __esmMin((() => {
28067
28626
  accordionIconTreatment: null,
28068
28627
  composition: null
28069
28628
  };
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;
28629
+ blockDesignFallback.sectionSurface, blockDesignFallback.contentFrame, blockDesignFallback.sectionHeight, blockDesignFallback.heroLegibility, blockDesignFallback.imageSurfaceTreatment, blockDesignFallback.imageSurfaceMotion, blockDesignFallback.tileTreatment, blockDesignFallback.eventListTreatment, blockDesignFallback.eyebrowTreatment, blockDesignFallback.accordionIconTreatment, blockDesignFallback.composition;
28071
28630
  }));
28072
28631
  function parseSiteChromeBackgroundColorToken(value) {
28073
28632
  const token = parseBackgroundAppearanceColorToken(value);
@@ -28156,11 +28715,6 @@ var init_headerRecompile = __esmMin((() => {
28156
28715
  init_headerBrandPresentation();
28157
28716
  init_siteChromeNavPreferences();
28158
28717
  }));
28159
- var init_runtimeBackgroundResolution = __esmMin((() => {
28160
- init_boxBackgroundSchema();
28161
- init_surfaces();
28162
- init_rawStyleFields();
28163
- }));
28164
28718
  var init_previewRuntimeCapabilities = __esmMin((() => {
28165
28719
  init_types$8();
28166
28720
  }));
@@ -28180,8 +28734,8 @@ var init_previewCustomization = __esmMin((() => {
28180
28734
  init_types$8();
28181
28735
  }));
28182
28736
  var init_previewCompiler = __esmMin((() => {
28737
+ init_sectionTransitions();
28183
28738
  init_schema$2();
28184
- init_tokens();
28185
28739
  init_curated();
28186
28740
  init_concretePalette();
28187
28741
  init_curatedSiteStyles();
@@ -28191,7 +28745,6 @@ var init_previewCompiler = __esmMin((() => {
28191
28745
  init_headerRecompile();
28192
28746
  init_siteChromeNavPreferences();
28193
28747
  init_runtimeCompiler();
28194
- init_rawStyleFields();
28195
28748
  init_runtimeBackgroundResolution();
28196
28749
  init_previewRuntimeCapabilities();
28197
28750
  init_siteStyleThemeCompiler();
@@ -28201,6 +28754,7 @@ var init_previewCompiler = __esmMin((() => {
28201
28754
  init_pageDesignIntent();
28202
28755
  init_previewCustomization();
28203
28756
  init_curatedChoices();
28757
+ init_joinTreatment();
28204
28758
  init_types$8();
28205
28759
  }));
28206
28760
  var init_contentTemplateRuntimeCompiler = __esmMin((() => {
@@ -28302,6 +28856,7 @@ var init_generatedDesignPipeline = __esmMin((() => {
28302
28856
  init_curatedSiteStyles();
28303
28857
  init_lookResolution();
28304
28858
  init_headerOpeningSection();
28859
+ init_joinTreatment();
28305
28860
  init_resolver();
28306
28861
  init_runtimeCompiler();
28307
28862
  init_types$8();
@@ -28670,6 +29225,7 @@ var init_site_styles = __esmMin((() => {
28670
29225
  init_headerRecompile();
28671
29226
  init_headerOpeningForeground();
28672
29227
  init_headerOpeningSection();
29228
+ init_joinTreatment();
28673
29229
  init_lookResolution();
28674
29230
  init_lookOptionGroups();
28675
29231
  init_logoBranding();
@@ -28689,6 +29245,7 @@ var init_site_styles = __esmMin((() => {
28689
29245
  init_runtimeCompiler();
28690
29246
  init_runtimeBackgroundResolution();
28691
29247
  init_sectionTransitions();
29248
+ init_sectionBoundaryComposition();
28692
29249
  init_siteChromeBackgrounds();
28693
29250
  init_siteChromeNavPreferences();
28694
29251
  init_siteStyleThemeCompiler();
@@ -28696,6 +29253,7 @@ var init_site_styles = __esmMin((() => {
28696
29253
  init_sourceCatalogs();
28697
29254
  init_styleTags();
28698
29255
  init_tokenRecipes();
29256
+ init_transitionCatalogue();
28699
29257
  init_siteStyleTypography();
28700
29258
  init_themeParity();
28701
29259
  init_types$8();
@@ -29177,7 +29735,8 @@ var init_schema = __esmMin((() => {
29177
29735
  "eventCategories",
29178
29736
  "eventSeries",
29179
29737
  "venues",
29180
- "staffMembers"
29738
+ "staffMembers",
29739
+ "appointmentServiceCategories"
29181
29740
  ];
29182
29741
  mediaDeliverySchema = z.object({
29183
29742
  sizes: z.string().optional(),
@@ -44117,6 +44676,7 @@ var init_booking_form_headless_state = __esmMin((() => {
44117
44676
  init_src$2();
44118
44677
  init_dist$1();
44119
44678
  init_booking_form_state();
44679
+ init_booking_form_service_categories();
44120
44680
  init_booking_form_validationErrors();
44121
44681
  init_booking_form_field_values();
44122
44682
  unsafeAsBookingFieldId = (id) => id;
@@ -44681,6 +45241,7 @@ function useHeadlessBookingFlow(options) {
44681
45241
  data,
44682
45242
  services: {
44683
45243
  items: options.services,
45244
+ categories: readServiceCategories(options),
44684
45245
  selected: selectedService,
44685
45246
  defaultServiceId,
44686
45247
  requiresSelection: Boolean(options.requiresServiceSelection)
@@ -44745,6 +45306,9 @@ function useHeadlessBookingFlow(options) {
44745
45306
  actions
44746
45307
  };
44747
45308
  }
45309
+ function readServiceCategories(options) {
45310
+ return options.serviceCategories ?? EMPTY_SERVICE_CATEGORIES;
45311
+ }
44748
45312
  function navigateToCheckout(checkoutUrl) {
44749
45313
  window.location.href = checkoutUrl;
44750
45314
  }
@@ -44758,6 +45322,7 @@ function useLatestRef(value) {
44758
45322
  }, [value]);
44759
45323
  return ref;
44760
45324
  }
45325
+ var EMPTY_SERVICE_CATEGORIES;
44761
45326
  var init_useHeadlessBookingFlow = __esmMin((() => {
44762
45327
  init_client();
44763
45328
  init_src();
@@ -44773,6 +45338,7 @@ var init_useHeadlessBookingFlow = __esmMin((() => {
44773
45338
  init_bookingPaymentOptions();
44774
45339
  init_useHeadlessBookingFlow_state();
44775
45340
  init_booking_form_validationErrors();
45341
+ EMPTY_SERVICE_CATEGORIES = [];
44776
45342
  }));
44777
45343
  //#endregion
44778
45344
  //#region ../blocks/src/system/runtime/hooks/usePolling.state.ts
@@ -45457,10 +46023,102 @@ var init_BookingConsentAgreement = __esmMin((() => {
45457
46023
  init_RichText();
45458
46024
  }));
45459
46025
  //#endregion
45460
- //#region ../blocks/src/system/runtime/nodes/booking-form.default-ui.tsx
46026
+ //#region ../blocks/src/system/runtime/nodes/booking-form.service-choices.tsx
45461
46027
  function optionCardClassName(isSelected) {
45462
46028
  return `rb-option-card${isSelected ? " rb-option-card--selected" : ""}`;
45463
46029
  }
46030
+ function SelectedCheck() {
46031
+ return /* @__PURE__ */ jsx("div", {
46032
+ className: "rb-option-check",
46033
+ children: /* @__PURE__ */ jsx(CheckIcon, {})
46034
+ });
46035
+ }
46036
+ /**
46037
+ * The service buttons. A site with no categories keeps its single grid; a
46038
+ * categorised site gets a heading per category, then "Other", above the same
46039
+ * grid (issue 1403). Empty categories are skipped, and there are no intros
46040
+ * here, to keep the step compact.
46041
+ */
46042
+ function DefaultServiceChoices({ booking }) {
46043
+ const layout = layoutServicesByCategory(booking.services.categories, booking.services.items.map((service) => ({
46044
+ service,
46045
+ categoryId: service.categoryId ?? null
46046
+ })), { keepEmptyGroups: false });
46047
+ switch (layout.kind) {
46048
+ case "uncategorized-site": return /* @__PURE__ */ jsxs("div", {
46049
+ className: "rb-reg-stack rb-reg-stack--sm",
46050
+ children: [/* @__PURE__ */ jsx("span", {
46051
+ className: "ab-subtitle",
46052
+ children: "Service"
46053
+ }), /* @__PURE__ */ jsx(ServiceChoiceGrid, {
46054
+ booking,
46055
+ choices: layout.services
46056
+ })]
46057
+ });
46058
+ case "categorized-site": return /* @__PURE__ */ jsx("div", {
46059
+ className: "rb-reg-stack",
46060
+ children: [...layout.groups.map((group) => ({
46061
+ key: group.category.id,
46062
+ heading: group.category.name,
46063
+ choices: group.services
46064
+ })), ...layout.other.length > 0 ? [{
46065
+ key: "other",
46066
+ heading: "Other",
46067
+ choices: layout.other
46068
+ }] : []].map((group) => /* @__PURE__ */ jsxs("div", {
46069
+ className: "rb-reg-stack rb-reg-stack--sm",
46070
+ "data-booking-service-group": group.key,
46071
+ children: [/* @__PURE__ */ jsx("h3", {
46072
+ className: "ab-subtitle",
46073
+ children: group.heading
46074
+ }), /* @__PURE__ */ jsx(ServiceChoiceGrid, {
46075
+ booking,
46076
+ choices: group.choices
46077
+ })]
46078
+ }, group.key))
46079
+ });
46080
+ default: return assertNever$11(layout);
46081
+ }
46082
+ }
46083
+ function ServiceChoiceGrid({ booking, choices }) {
46084
+ const selectedServiceId = booking.services.selected?.id;
46085
+ return /* @__PURE__ */ jsx("div", {
46086
+ className: "rb-reg-grid",
46087
+ children: choices.map(({ service }) => {
46088
+ const isSelected = selectedServiceId === service.id;
46089
+ return /* @__PURE__ */ jsxs("button", {
46090
+ type: "button",
46091
+ onClick: () => booking.actions.selectService(service.id),
46092
+ className: optionCardClassName(isSelected),
46093
+ "aria-pressed": isSelected,
46094
+ children: [/* @__PURE__ */ jsxs("div", {
46095
+ className: "rb-flex-1",
46096
+ children: [
46097
+ /* @__PURE__ */ jsx("div", {
46098
+ className: "ab-subtitle",
46099
+ children: service.title
46100
+ }),
46101
+ service.description ? /* @__PURE__ */ jsx("div", {
46102
+ className: "ab-text rb-on-surface--muted",
46103
+ children: service.description
46104
+ }) : null,
46105
+ service.durationMinutes ? /* @__PURE__ */ jsxs("div", {
46106
+ className: "ab-text rb-on-surface--muted",
46107
+ children: [service.durationMinutes, " minutes"]
46108
+ }) : null
46109
+ ]
46110
+ }), isSelected ? /* @__PURE__ */ jsx(SelectedCheck, {}) : null]
46111
+ }, service.id);
46112
+ })
46113
+ });
46114
+ }
46115
+ var init_booking_form_service_choices = __esmMin((() => {
46116
+ init_src$2();
46117
+ init_dist$1();
46118
+ init_CheckIcon$1();
46119
+ }));
46120
+ //#endregion
46121
+ //#region ../blocks/src/system/runtime/nodes/booking-form.default-ui.tsx
45464
46122
  function formatBookingAmount(amount, currency) {
45465
46123
  return formatCurrencyAmount(amount, currency, { decimals: "auto" });
45466
46124
  }
@@ -45551,7 +46209,6 @@ function DefaultBookingStepContent({ booking, errorSummaryId }) {
45551
46209
  function DefaultServiceSelectionStep({ booking }) {
45552
46210
  const services = booking.services.items;
45553
46211
  const resources = booking.resources.items;
45554
- const selectedServiceId = booking.services.selected?.id;
45555
46212
  const selectedResourceId = booking.resources.selectedResourceId;
45556
46213
  const showServiceSelect = booking.services.requiresSelection || services.length > 1;
45557
46214
  const showResourceSelect = booking.resources.requiresSelection || resources.length > 1;
@@ -45566,41 +46223,7 @@ function DefaultServiceSelectionStep({ booking }) {
45566
46223
  className: "ab-text rb-on-surface--muted",
45567
46224
  children: "Choose the service you'd like to book"
45568
46225
  })] }),
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,
46226
+ showServiceSelect ? /* @__PURE__ */ jsx(DefaultServiceChoices, { booking }) : null,
45604
46227
  booking.resources.state.tag === "loading" ? /* @__PURE__ */ jsxs("div", {
45605
46228
  className: "ab-center",
45606
46229
  children: [/* @__PURE__ */ jsx(SpinnerNode, {
@@ -46179,12 +46802,6 @@ function DefaultBookingErrorSummary({ errors, id }) {
46179
46802
  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
46803
  });
46181
46804
  }
46182
- function SelectedCheck() {
46183
- return /* @__PURE__ */ jsx("div", {
46184
- className: "rb-option-check",
46185
- children: /* @__PURE__ */ jsx(CheckIcon, {})
46186
- });
46187
- }
46188
46805
  var init_booking_form_default_ui = __esmMin((() => {
46189
46806
  init_src$2();
46190
46807
  init_ThemedButton();
@@ -46194,12 +46811,12 @@ var init_booking_form_default_ui = __esmMin((() => {
46194
46811
  init_DatePicker();
46195
46812
  init_TimeSlotSelector();
46196
46813
  init_spinner$1();
46197
- init_CheckIcon$1();
46198
46814
  init_StepIndicator$1();
46199
46815
  init_FeedbackMessage();
46200
46816
  init_DiscountCodeDisclosure();
46201
46817
  init_FieldValidationMessage();
46202
46818
  init_BookingConsentAgreement();
46819
+ init_booking_form_service_choices();
46203
46820
  }));
46204
46821
  //#endregion
46205
46822
  //#region ../blocks/src/system/runtime/shared/utils/checkoutUrls.ts
@@ -46758,6 +47375,7 @@ var init_booking_form_client = __esmMin((() => {
46758
47375
  formId: render.hydration.formId ?? form?.id ?? "booking-form",
46759
47376
  form,
46760
47377
  services,
47378
+ serviceCategories: render.hydration.serviceCategories,
46761
47379
  initialServiceId,
46762
47380
  initialResourceId,
46763
47381
  initialData: render.hydration.initialData ?? void 0,
@@ -107840,7 +108458,7 @@ var init_event_registration_client = __esmMin((() => {
107840
108458
  * Loads Leaflet CSS/JS from CDN to avoid bundling in the blocks package.
107841
108459
  */
107842
108460
  function isLeafletApi(value) {
107843
- if (!isRecord$15(value)) return false;
108461
+ if (!isRecord$16(value)) return false;
107844
108462
  return typeof value.map === "function" && typeof value.tileLayer === "function" && typeof value.marker === "function";
107845
108463
  }
107846
108464
  function loadLeaflet() {
@@ -116478,6 +117096,16 @@ var init_newsletter_form_client_state = __esmMin((() => {
116478
117096
  }));
116479
117097
  //#endregion
116480
117098
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.submission.ts
117099
+ /**
117100
+ * Logs a refusal with its reason so a QA session can tell the cases apart in
117101
+ * the console, since the visitor-facing copy deliberately does not.
117102
+ */
117103
+ function reportNewsletterSignupRefusal(reason, detail = {}) {
117104
+ console.warn("[newsletter-form] Signup refused before sending", {
117105
+ reason,
117106
+ ...detail
117107
+ });
117108
+ }
116481
117109
  function mapNewsletterFormFieldsToSubscriberInput(fields) {
116482
117110
  return {
116483
117111
  email: readSubmittedText(fields.email),
@@ -116498,7 +117126,7 @@ function resolveNewsletterSubmitTarget(input) {
116498
117126
  case "signed-footer-source": return publishedSourceTarget(input.sourceRef);
116499
117127
  case "preview": return {
116500
117128
  tag: "unavailable",
116501
- reason: "missing_block_source"
117129
+ reason: "preview_only_source"
116502
117130
  };
116503
117131
  default: return assertNever$22(input);
116504
117132
  }
@@ -116560,13 +117188,23 @@ function NewsletterFormClient({ render }) {
116560
117188
  }
116561
117189
  const submitTarget = resolveNewsletterSubmitTarget(render.hydration.target);
116562
117190
  if (submitTarget.tag === "unavailable") {
117191
+ reportNewsletterSignupRefusal(submitTarget.reason, { target: render.hydration.target });
116563
117192
  dispatchSubmission({
116564
117193
  type: "failed",
116565
117194
  error: newsletterFormError(SIGNUPS_PAUSED_MESSAGE)
116566
117195
  });
116567
117196
  return;
116568
117197
  }
116569
- if (render.hydration.setupRevision === null || render.display.availability === "unavailable") {
117198
+ if (render.hydration.setupRevision === null) {
117199
+ reportNewsletterSignupRefusal("setup_not_configured");
117200
+ dispatchSubmission({
117201
+ type: "failed",
117202
+ error: newsletterFormError(SIGNUPS_PAUSED_MESSAGE)
117203
+ });
117204
+ return;
117205
+ }
117206
+ if (render.display.availability === "unavailable") {
117207
+ reportNewsletterSignupRefusal("destination_unavailable", { setupRevision: render.hydration.setupRevision });
116570
117208
  dispatchSubmission({
116571
117209
  type: "failed",
116572
117210
  error: newsletterFormError(SIGNUPS_PAUSED_MESSAGE)
@@ -116576,6 +117214,7 @@ function NewsletterFormClient({ render }) {
116576
117214
  const selection = readVisitorSelection(formData, render.hydration);
116577
117215
  const selectionError = visitorSelectionError(selection);
116578
117216
  if (selectionError) {
117217
+ if (selection.tag === "unusable") reportNewsletterSignupRefusal("choices_unusable", { choiceProvider: render.hydration.choiceProvider });
116579
117218
  dispatchSubmission({
116580
117219
  type: "failed",
116581
117220
  error: selectionError
@@ -117086,17 +117725,12 @@ init_paintTargets();
117086
117725
  //#region ../theme-core/src/footer/generateFooterCss.ts
117087
117726
  init_resolver$1();
117088
117727
  //#endregion
117728
+ //#region ../theme-core/src/layout/boundaryCss.ts
117729
+ init_sectionTransitions();
117730
+ init_transitionCatalogue();
117731
+ //#endregion
117089
117732
  //#region ../theme-core/src/layout/generateLayoutCss.ts
117090
117733
  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
117734
  //#endregion
117101
117735
  //#region ../theme-core/src/status/generateStatusCss.ts
117102
117736
  init_surfaces();