@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
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { d as unwrapResponse, l as ApiEnvelopeError, s as ApiRequestError, u as extractApiEnvelopeErrorDetails } from "./previewAuthStore-JA6-I_sL.mjs";
2
+ import { d as unwrapResponse, l as ApiEnvelopeError, s as ApiRequestError, u as extractApiEnvelopeErrorDetails } from "./previewAuthStore-BxAFlu-j.mjs";
3
3
  import * as React$1 from "react";
4
4
  import React, { Component, Fragment, cloneElement, createContext, createElement, createRef, forwardRef, isValidElement, memo, startTransition, useCallback, useContext, useDebugValue, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -398,7 +398,7 @@ function parseEventPageSurfaceDestinationMode(value) {
398
398
  return value === "cms-managed" || value === "system-managed" ? value : void 0;
399
399
  }
400
400
  function parseEventPageSurfaceSubject(value) {
401
- if (!isRecord$29(value)) return err$1({ kind: "notObject" });
401
+ if (!isRecord$30(value)) return err$1({ kind: "notObject" });
402
402
  switch (value.kind) {
403
403
  case "eventSeries": {
404
404
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -453,7 +453,7 @@ function parseEventSeriesIdList(values) {
453
453
  }
454
454
  return ok$2(parsed);
455
455
  }
456
- function isRecord$29(value) {
456
+ function isRecord$30(value) {
457
457
  return typeof value === "object" && value !== null && !Array.isArray(value);
458
458
  }
459
459
  //#endregion
@@ -892,6 +892,74 @@ function localScheduleDateTimeToUtcDate(localDate, localTime, timeZone) {
892
892
  return new Date(guess);
893
893
  }
894
894
  //#endregion
895
+ //#region ../core/src/appointment-service-categories.ts
896
+ /**
897
+ * Groups services under their categories.
898
+ *
899
+ * A service whose `categoryId` names no category in `categories` lands in
900
+ * Other. The composite foreign key keeps that from happening with stored data,
901
+ * so this is only the total, safe answer for a caller holding a stale list.
902
+ */
903
+ function layoutServicesByCategory(categories, services, options) {
904
+ if (categories.length === 0) return {
905
+ kind: "uncategorized-site",
906
+ services: [...services]
907
+ };
908
+ const buckets = new Map(categories.map((category) => [category.id, []]));
909
+ const other = [];
910
+ for (const service of services) {
911
+ const bucket = service.categoryId === null ? void 0 : buckets.get(service.categoryId);
912
+ if (bucket !== void 0) bucket.push(service);
913
+ else other.push(service);
914
+ }
915
+ const groups = [];
916
+ for (const category of categories) {
917
+ const groupServices = buckets.get(category.id) ?? [];
918
+ if (groupServices.length === 0 && !options.keepEmptyGroups) continue;
919
+ groups.push({
920
+ category,
921
+ services: groupServices
922
+ });
923
+ }
924
+ if (groups.length === 0 && !options.keepEmptyGroups) return {
925
+ kind: "uncategorized-site",
926
+ services: other
927
+ };
928
+ return {
929
+ kind: "categorized-site",
930
+ groups,
931
+ other
932
+ };
933
+ }
934
+ /**
935
+ * Selects the public view for an optional category filter.
936
+ *
937
+ * It takes the category and service lists rather than a prebuilt layout
938
+ * because the public layout has already dropped empty categories, and then an
939
+ * empty category (`single` with no services) could not be told apart from a
940
+ * deleted one (`fallback-all`).
941
+ */
942
+ function selectCategoryView(categories, services, filterCategoryId) {
943
+ const publicLayout = () => layoutServicesByCategory(categories, services, { keepEmptyGroups: false });
944
+ if (filterCategoryId === null || filterCategoryId === void 0) return {
945
+ kind: "all",
946
+ layout: publicLayout()
947
+ };
948
+ const category = categories.find((candidate) => candidate.id === filterCategoryId);
949
+ if (category === void 0) return {
950
+ kind: "fallback-all",
951
+ missingCategoryId: filterCategoryId,
952
+ layout: publicLayout()
953
+ };
954
+ return {
955
+ kind: "single",
956
+ group: {
957
+ category,
958
+ services: services.filter((service) => service.categoryId === filterCategoryId)
959
+ }
960
+ };
961
+ }
962
+ //#endregion
895
963
  //#region ../core/src/location.ts
896
964
  function normalizeLocationAddressText(value) {
897
965
  if (typeof value !== "string") return null;
@@ -1103,7 +1171,7 @@ const knownSocialPlatforms = [
1103
1171
  "x",
1104
1172
  "youtube"
1105
1173
  ];
1106
- function isRecord$28(value) {
1174
+ function isRecord$29(value) {
1107
1175
  return typeof value === "object" && value !== null && !Array.isArray(value);
1108
1176
  }
1109
1177
  function trimmedOptionalString(value) {
@@ -1113,7 +1181,7 @@ function trimmedOptionalString(value) {
1113
1181
  return trimmed.length > 0 ? trimmed : null;
1114
1182
  }
1115
1183
  function parsePublicContactDetails(value) {
1116
- if (!isRecord$28(value)) return err$1({
1184
+ if (!isRecord$29(value)) return err$1({
1117
1185
  code: "invalid_shape",
1118
1186
  path: "contact",
1119
1187
  message: "Contact details must be an object."
@@ -1193,7 +1261,7 @@ function minutesSinceMidnight(value) {
1193
1261
  }
1194
1262
  function parseOpeningHoursDay(value, expectedWeekday, index) {
1195
1263
  const path = `openingHours.days.${index}`;
1196
- if (!isRecord$28(value) || value.weekday !== expectedWeekday) return err$1({
1264
+ if (!isRecord$29(value) || value.weekday !== expectedWeekday) return err$1({
1197
1265
  code: "invalid_weekdays",
1198
1266
  path,
1199
1267
  message: `Expected ${expectedWeekday} in calendar order.`
@@ -1210,7 +1278,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
1210
1278
  const intervals = [];
1211
1279
  for (const [intervalIndex, rawInterval] of value.intervals.entries()) {
1212
1280
  const intervalPath = `${path}.intervals.${intervalIndex}`;
1213
- if (!isRecord$28(rawInterval)) return err$1({
1281
+ if (!isRecord$29(rawInterval)) return err$1({
1214
1282
  code: "invalid_interval",
1215
1283
  path: intervalPath,
1216
1284
  message: "Opening interval is invalid."
@@ -1252,7 +1320,7 @@ function parseOpeningHoursDay(value, expectedWeekday, index) {
1252
1320
  });
1253
1321
  }
1254
1322
  function parseWeeklyOpeningHours(value) {
1255
- if (!isRecord$28(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) return err$1({
1323
+ if (!isRecord$29(value) || !Array.isArray(value.days) || value.days.length !== publicProfileWeekdays.length) return err$1({
1256
1324
  code: "invalid_weekdays",
1257
1325
  path: "openingHours.days",
1258
1326
  message: "Opening hours must include all seven weekdays."
@@ -1321,7 +1389,7 @@ function parsePublicSocialLinks(value) {
1321
1389
  const ids = /* @__PURE__ */ new Set();
1322
1390
  for (const [index, rawLink] of value.entries()) {
1323
1391
  const path = `socialLinks.${index}`;
1324
- if (!isRecord$28(rawLink)) return err$1({
1392
+ if (!isRecord$29(rawLink)) return err$1({
1325
1393
  code: "invalid_social_link",
1326
1394
  path,
1327
1395
  message: "Social link is invalid."
@@ -1364,7 +1432,7 @@ function parsePublicSocialLinks(value) {
1364
1432
  return ok$2(links);
1365
1433
  }
1366
1434
  function parsePublicProfile(value) {
1367
- if (!isRecord$28(value)) return err$1({
1435
+ if (!isRecord$29(value)) return err$1({
1368
1436
  code: "invalid_shape",
1369
1437
  path: "",
1370
1438
  message: "Public profile must be an object."
@@ -1404,7 +1472,7 @@ function parseContactVisibility(value) {
1404
1472
  phone: "shown",
1405
1473
  email: "shown"
1406
1474
  });
1407
- if (!isRecord$28(value)) return err$1({
1475
+ if (!isRecord$29(value)) return err$1({
1408
1476
  code: "invalid_contact_visibility",
1409
1477
  path: "contactVisibility",
1410
1478
  message: "Contact visibility must be an object."
@@ -1427,6 +1495,36 @@ function parsePremises(value) {
1427
1495
  message: "Premises must be true or false."
1428
1496
  });
1429
1497
  }
1498
+ /**
1499
+ * The contact details the site may publish: the stored contact with every
1500
+ * hidden method removed, or null when nothing is left to show.
1501
+ *
1502
+ * Every public reader goes through this. The privacy policy identity is the
1503
+ * one exception and reads the stored email directly, because a hidden email
1504
+ * can still be the policy's contact.
1505
+ */
1506
+ function publishedContactDetails(profile) {
1507
+ const { contact, contactVisibility } = profile;
1508
+ if (contact === null) return null;
1509
+ const phone = contactVisibility.phone === "shown" ? contact.phone : null;
1510
+ const email = contactVisibility.email === "shown" ? contact.email : null;
1511
+ if (phone !== null && email !== null) return {
1512
+ kind: "phone-and-email",
1513
+ phone,
1514
+ email
1515
+ };
1516
+ if (phone !== null) return {
1517
+ kind: "phone",
1518
+ phone,
1519
+ email: null
1520
+ };
1521
+ if (email !== null) return {
1522
+ kind: "email",
1523
+ phone: null,
1524
+ email
1525
+ };
1526
+ return null;
1527
+ }
1430
1528
  function parseAddressVisibility(value) {
1431
1529
  if (value === void 0 || value === null) return ok$2("shown");
1432
1530
  if (value === "shown" || value === "hidden") return ok$2(value);
@@ -1438,7 +1536,7 @@ function parseAddressVisibility(value) {
1438
1536
  }
1439
1537
  //#endregion
1440
1538
  //#region ../core/src/rich-text.ts
1441
- function isRecord$27(value) {
1539
+ function isRecord$28(value) {
1442
1540
  return typeof value === "object" && value !== null && !Array.isArray(value);
1443
1541
  }
1444
1542
  function hasOnlyKeys(value, allowed) {
@@ -1458,15 +1556,15 @@ function isDenseArrayOf(value, predicate) {
1458
1556
  return true;
1459
1557
  }
1460
1558
  function isTipTapMark(value) {
1461
- return isRecord$27(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === void 0 || isRecord$27(value.attrs));
1559
+ return isRecord$28(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === void 0 || isRecord$28(value.attrs));
1462
1560
  }
1463
1561
  function isTipTapNode(value) {
1464
- if (!isRecord$27(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== void 0 && !isRecord$27(value.attrs) || value.marks !== void 0 && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== void 0 && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) return false;
1562
+ 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;
1465
1563
  if (value.type === "text") return typeof value.text === "string" && value.text.length > 0 && value.content === void 0;
1466
1564
  return value.text === void 0;
1467
1565
  }
1468
1566
  function isCanonicalRichTextDoc(value) {
1469
- return isRecord$27(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
1567
+ return isRecord$28(value) && hasOnlyKeys(value, rootKeys) && value.type === "doc" && Array.isArray(value.content) && isDenseArrayOf(value.content, isTipTapNode);
1470
1568
  }
1471
1569
  //#endregion
1472
1570
  //#region ../core/src/mailing-list-identifiers.ts
@@ -1501,10 +1599,10 @@ function parseBrevoListId(value) {
1501
1599
  //#endregion
1502
1600
  //#region ../core/src/mailing-list-targeting-parser.ts
1503
1601
  function parseMailingListPlacementTargeting(value) {
1504
- if (!isRecord$26(value)) return targetingError("shape");
1602
+ if (!isRecord$27(value)) return targetingError("shape");
1505
1603
  if (value.mode === "default") return ok$2({ mode: "default" });
1506
1604
  if (value.mode !== "custom") return targetingError("mode");
1507
- if (!isRecord$26(value.selection)) return targetingError("selection");
1605
+ if (!isRecord$27(value.selection)) return targetingError("selection");
1508
1606
  switch (value.provider) {
1509
1607
  case "native": return parseNativeTargeting(value.selection);
1510
1608
  case "brevo": return parseBrevoTargeting(value.selection);
@@ -1513,7 +1611,7 @@ function parseMailingListPlacementTargeting(value) {
1513
1611
  }
1514
1612
  }
1515
1613
  function parseMailingListVisitorSelection(value) {
1516
- if (!isRecord$26(value)) return visitorSelectionError$1("shape");
1614
+ if (!isRecord$27(value)) return visitorSelectionError$1("shape");
1517
1615
  switch (value.provider) {
1518
1616
  case "native": {
1519
1617
  const topicIds = parseVisitorIds(value.topicIds, parseNewsletterListId);
@@ -1646,7 +1744,7 @@ function parseVisitorIds(value, parseId) {
1646
1744
  }
1647
1745
  return parsed;
1648
1746
  }
1649
- function isRecord$26(value) {
1747
+ function isRecord$27(value) {
1650
1748
  return typeof value === "object" && value !== null && !Array.isArray(value);
1651
1749
  }
1652
1750
  function targetingError(field) {
@@ -1663,7 +1761,7 @@ function visitorSelectionError$1(field) {
1663
1761
  }
1664
1762
  //#endregion
1665
1763
  //#region ../core/src/footer-engagement.ts
1666
- function isRecord$25(value) {
1764
+ function isRecord$26(value) {
1667
1765
  return typeof value === "object" && value !== null && !Array.isArray(value);
1668
1766
  }
1669
1767
  function invalid(path, message) {
@@ -1689,7 +1787,7 @@ function parseEngagementBase(value, base, path) {
1689
1787
  if (heading.value === null) return err$1(invalid(`${path}.heading`, "Footer engagement heading cannot be empty."));
1690
1788
  const treatment = parseTreatment(value.treatment, `${path}.treatment`);
1691
1789
  if (!treatment.ok) return treatment;
1692
- if (!isRecord$25(value.source)) return err$1(invalid(`${path}.source`, "Footer engagement source is invalid."));
1790
+ if (!isRecord$26(value.source)) return err$1(invalid(`${path}.source`, "Footer engagement source is invalid."));
1693
1791
  const intro = parseBoundedText(value.source.intro, `${path}.source.intro`, {
1694
1792
  maxLength: 240,
1695
1793
  optional: true
@@ -1732,7 +1830,7 @@ function parseFooterEngagementSectionValue(value, sectionBase, path) {
1732
1830
  const base = parseEngagementBase(value, sectionBase, path);
1733
1831
  if (!base.ok) return base;
1734
1832
  const source = value.source;
1735
- if (!isRecord$25(source)) return err$1(invalid(`${path}.source`, "Footer engagement source is invalid."));
1833
+ if (!isRecord$26(source)) return err$1(invalid(`${path}.source`, "Footer engagement source is invalid."));
1736
1834
  switch (source.kind) {
1737
1835
  case "standard-contact": return parseStandardContact(source, base.value, `${path}.source`);
1738
1836
  case "mailing-list": return parseMailingList(source, base.value, `${path}.source`);
@@ -1835,7 +1933,7 @@ function unsafeAsFooterModuleId(value) {
1835
1933
  function parseFooterModuleId(value) {
1836
1934
  return parseSectionId(value, "sectionId");
1837
1935
  }
1838
- function isRecord$24(value) {
1936
+ function isRecord$25(value) {
1839
1937
  return typeof value === "object" && value !== null && !Array.isArray(value);
1840
1938
  }
1841
1939
  function isRichText(value) {
@@ -1861,7 +1959,7 @@ function parseHeading(value, path) {
1861
1959
  }
1862
1960
  function parseFooterSection(value, index) {
1863
1961
  const path = `sections.${index}`;
1864
- if (!isRecord$24(value)) return err$1({
1962
+ if (!isRecord$25(value)) return err$1({
1865
1963
  code: "invalid_section",
1866
1964
  path,
1867
1965
  message: "Footer section must be an object."
@@ -1890,7 +1988,7 @@ function parseFooterSection(value, index) {
1890
1988
  });
1891
1989
  break;
1892
1990
  case "navigation":
1893
- if (isRecord$24(value.source) && value.source.kind === "menu-role" && value.source.role === "main") return ok$2({
1991
+ if (isRecord$25(value.source) && value.source.kind === "menu-role" && value.source.role === "main") return ok$2({
1894
1992
  ...base,
1895
1993
  kind: "navigation",
1896
1994
  heading: heading.value,
@@ -1901,7 +1999,7 @@ function parseFooterSection(value, index) {
1901
1999
  });
1902
2000
  break;
1903
2001
  case "legal-navigation":
1904
- if (isRecord$24(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") return ok$2({
2002
+ if (isRecord$25(value.source) && value.source.kind === "menu-role" && value.source.role === "legal-policy") return ok$2({
1905
2003
  ...base,
1906
2004
  kind: "legal-navigation",
1907
2005
  heading: heading.value,
@@ -1912,7 +2010,7 @@ function parseFooterSection(value, index) {
1912
2010
  });
1913
2011
  break;
1914
2012
  case "business-address":
1915
- if (isRecord$24(value.source) && value.source.kind === "business-address") return ok$2({
2013
+ if (isRecord$25(value.source) && value.source.kind === "business-address") return ok$2({
1916
2014
  ...base,
1917
2015
  kind: "business-address",
1918
2016
  heading: heading.value,
@@ -1922,7 +2020,7 @@ function parseFooterSection(value, index) {
1922
2020
  case "contact":
1923
2021
  case "opening-hours":
1924
2022
  case "social-links":
1925
- if (isRecord$24(value.source) && value.source.kind === "public-profile") return ok$2({
2023
+ if (isRecord$25(value.source) && value.source.kind === "public-profile") return ok$2({
1926
2024
  ...base,
1927
2025
  kind: value.kind,
1928
2026
  heading: heading.value,
@@ -2142,7 +2240,7 @@ const defaultSections = (legacy, options) => {
2142
2240
  ];
2143
2241
  };
2144
2242
  function normalizeLegacyFooterContentToV1(value, options = {}) {
2145
- if (!isRecord$24(value)) return err$1({
2243
+ if (!isRecord$25(value)) return err$1({
2146
2244
  code: "invalid_shape",
2147
2245
  path: "",
2148
2246
  message: "Legacy footer content must be an object."
@@ -2160,7 +2258,7 @@ function normalizeFooterNavigationLayout(value) {
2160
2258
  }
2161
2259
  //#endregion
2162
2260
  //#region ../core/src/mailing-list-setup.ts
2163
- function isRecord$23(value) {
2261
+ function isRecord$24(value) {
2164
2262
  return typeof value === "object" && value !== null && !Array.isArray(value);
2165
2263
  }
2166
2264
  /** The name shown to people for each provider; the native one is the product brand. */
@@ -2173,7 +2271,7 @@ function mailingListProviderName(provider) {
2173
2271
  }
2174
2272
  const DEFAULT_MAILING_LIST_SUCCESS_MESSAGE = "Thanks — you're on the list.";
2175
2273
  function parseMailingListProviderScope(value) {
2176
- if (!isRecord$23(value)) return err$1("invalid_mailing_list_provider_scope");
2274
+ if (!isRecord$24(value)) return err$1("invalid_mailing_list_provider_scope");
2177
2275
  if (value.provider === "native") return ok$2({ provider: "native" });
2178
2276
  if (value.provider === "brevo") {
2179
2277
  const accountId = parseBrevoAccountId(value.accountId);
@@ -2194,7 +2292,7 @@ function parseMailingListProviderScope(value) {
2194
2292
  //#endregion
2195
2293
  //#region ../core/src/mailing-list-setup-parser.ts
2196
2294
  function parseMailingListGlobalForm(value) {
2197
- if (!isRecord$22(value)) return formError("shape");
2295
+ if (!isRecord$23(value)) return formError("shape");
2198
2296
  const fields = parseGlobalFields(value.fields);
2199
2297
  if (fields === null) return formError("fields");
2200
2298
  if (typeof value.includeOptionalPhone !== "boolean") return formError("includeOptionalPhone");
@@ -2213,7 +2311,7 @@ function parseMailingListGlobalForm(value) {
2213
2311
  });
2214
2312
  }
2215
2313
  function parseGlobalFields(value) {
2216
- if (!isRecord$22(value) || typeof value.preset !== "string") return null;
2314
+ if (!isRecord$23(value) || typeof value.preset !== "string") return null;
2217
2315
  switch (value.preset) {
2218
2316
  case "emailOnly": return { preset: "emailOnly" };
2219
2317
  case "emailName": return isRequirement(value.name) ? {
@@ -2250,7 +2348,7 @@ function parseOptionalString(value) {
2250
2348
  function isRequirement(value) {
2251
2349
  return value === "required" || value === "optional";
2252
2350
  }
2253
- function isRecord$22(value) {
2351
+ function isRecord$23(value) {
2254
2352
  return typeof value === "object" && value !== null && !Array.isArray(value);
2255
2353
  }
2256
2354
  function formError(field) {
@@ -2321,7 +2419,7 @@ function planMailingListChoicePresentation(targeting) {
2321
2419
  //#endregion
2322
2420
  //#region ../core/src/mailing-list-runtime.ts
2323
2421
  function parseMailingListRuntimeContext(value) {
2324
- if (!isRecord$21(value)) return invalidRuntimeContext();
2422
+ if (!isRecord$22(value)) return invalidRuntimeContext();
2325
2423
  if (value.tag === "not_configured") return ok$2({ tag: "not_configured" });
2326
2424
  if (value.tag !== "configured") return invalidRuntimeContext();
2327
2425
  const revision = parseMailingListSetupRevision(value.revision);
@@ -2336,7 +2434,7 @@ function parseMailingListRuntimeContext(value) {
2336
2434
  });
2337
2435
  }
2338
2436
  function parseRuntimeProvider(value) {
2339
- if (!isRecord$21(value)) return null;
2437
+ if (!isRecord$22(value)) return null;
2340
2438
  switch (value.provider) {
2341
2439
  case "native": return parseNativeRuntimeProvider(value);
2342
2440
  case "brevo": return parseBrevoRuntimeProvider(value);
@@ -2368,7 +2466,7 @@ function parseMailchimpRuntimeProvider(value) {
2368
2466
  if (defaultAudienceId === null) return null;
2369
2467
  const audiences = [];
2370
2468
  for (const audience of value.audiences) {
2371
- if (!isRecord$21(audience)) return null;
2469
+ if (!isRecord$22(audience)) return null;
2372
2470
  const id = parseMailchimpAudienceId(audience.id);
2373
2471
  const name = parseName(audience.name);
2374
2472
  if (id === null || name === null || !Array.isArray(audience.interests)) return null;
@@ -2389,7 +2487,7 @@ function parseMailchimpRuntimeProvider(value) {
2389
2487
  function parseMailchimpInterests(value) {
2390
2488
  const interests = [];
2391
2489
  for (const interest of value) {
2392
- if (!isRecord$21(interest)) return null;
2490
+ if (!isRecord$22(interest)) return null;
2393
2491
  const id = parseMailchimpInterestId(interest.id);
2394
2492
  const name = parseName(interest.name);
2395
2493
  const categoryName = parseName(interest.categoryName);
@@ -2406,7 +2504,7 @@ function parseNamedItems(value, parseId) {
2406
2504
  if (!Array.isArray(value)) return null;
2407
2505
  const items = [];
2408
2506
  for (const item of value) {
2409
- if (!isRecord$21(item)) return null;
2507
+ if (!isRecord$22(item)) return null;
2410
2508
  const id = parseId(item.id);
2411
2509
  const name = parseName(item.name);
2412
2510
  if (id === null || name === null) return null;
@@ -2422,7 +2520,7 @@ function parseName(value) {
2422
2520
  const name = value.trim();
2423
2521
  return name.length > 0 ? name : null;
2424
2522
  }
2425
- function isRecord$21(value) {
2523
+ function isRecord$22(value) {
2426
2524
  return typeof value === "object" && value !== null && !Array.isArray(value);
2427
2525
  }
2428
2526
  function invalidRuntimeContext() {
@@ -75020,7 +75118,7 @@ function BooleanField({ path, backend, config }) {
75020
75118
  }
75021
75119
  //#endregion
75022
75120
  //#region ../block-form/src/utils/typeGuards.ts
75023
- function isRecord$20(value) {
75121
+ function isRecord$21(value) {
75024
75122
  return typeof value === "object" && value !== null && !Array.isArray(value);
75025
75123
  }
75026
75124
  function isObjectRecord$2(value) {
@@ -78036,7 +78134,7 @@ function useRepeaterRowErrors(field, index, backend, basePath) {
78036
78134
  const rowErrorNode = getErrorAtPath(errorsRoot, rowPath);
78037
78135
  if (!rowErrorNode) return [];
78038
78136
  const rowValues = backend.getValue(rowPath);
78039
- const valueContext = isRecord$20(rowValues) ? rowValues : {};
78137
+ const valueContext = isRecord$21(rowValues) ? rowValues : {};
78040
78138
  const baseSegments = rowPath.split(".").filter((s) => s.length > 0);
78041
78139
  const results = [];
78042
78140
  let fieldsToTraverse = [];
@@ -78071,7 +78169,7 @@ function useRepeaterRowErrors(field, index, backend, basePath) {
78071
78169
  const itemError = errorArray[itemIndex];
78072
78170
  let nestedFields = [];
78073
78171
  if (childRepeater.polymorphic && childRepeater.itemTypes) {
78074
- const itemType = isRecord$20(itemValue) && typeof itemValue._type === "string" ? itemValue._type : Object.keys(childRepeater.itemTypes)[0] ?? "";
78172
+ const itemType = isRecord$21(itemValue) && typeof itemValue._type === "string" ? itemValue._type : Object.keys(childRepeater.itemTypes)[0] ?? "";
78075
78173
  const typeSchema = itemType ? childRepeater.itemTypes[itemType] : void 0;
78076
78174
  if (typeSchema) nestedFields = typeSchema.fields;
78077
78175
  } else if (!childRepeater.polymorphic && childRepeater.fields) nestedFields = childRepeater.fields;
@@ -78106,13 +78204,13 @@ function getErrorAtPath(errors, path) {
78106
78204
  const segments = path.split(".").filter((s) => s.length > 0);
78107
78205
  let current = errors;
78108
78206
  for (const segment of segments) {
78109
- if (!isRecord$20(current)) return void 0;
78207
+ if (!isRecord$21(current)) return void 0;
78110
78208
  current = current[segment];
78111
78209
  }
78112
78210
  return current;
78113
78211
  }
78114
78212
  function getChildValue(node, key) {
78115
- if (isRecord$20(node)) return node[key];
78213
+ if (isRecord$21(node)) return node[key];
78116
78214
  }
78117
78215
  function extractMessage$1(error) {
78118
78216
  if (!error) return void 0;
@@ -78124,7 +78222,7 @@ function extractMessage$1(error) {
78124
78222
  }
78125
78223
  return;
78126
78224
  }
78127
- if (isRecord$20(error) && typeof error.message === "string") return error.message;
78225
+ if (isRecord$21(error) && typeof error.message === "string") return error.message;
78128
78226
  }
78129
78227
  //#endregion
78130
78228
  //#region ../block-form/src/context/DisplayLabelResolverContext.tsx
@@ -79135,11 +79233,11 @@ function isSameHotspot(a, b) {
79135
79233
  }
79136
79234
  //#endregion
79137
79235
  //#region ../media-core/src/typeGuards.ts
79138
- function isRecord$19(value) {
79236
+ function isRecord$20(value) {
79139
79237
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
79140
79238
  }
79141
79239
  function asRecord(value) {
79142
- return isRecord$19(value) ? value : void 0;
79240
+ return isRecord$20(value) ? value : void 0;
79143
79241
  }
79144
79242
  //#endregion
79145
79243
  //#region ../media-core/src/cropDebug.ts
@@ -79321,8 +79419,8 @@ function walkAndCollectImageAttrs(value, out, location = "") {
79321
79419
  value.forEach((item, index) => walkAndCollectImageAttrs(item, out, `${location}[${index}]`));
79322
79420
  return;
79323
79421
  }
79324
- if (!isRecord$19(value)) return;
79325
- if (value.type === "image" && isRecord$19(value.attrs)) out.push({
79422
+ if (!isRecord$20(value)) return;
79423
+ if (value.type === "image" && isRecord$20(value.attrs)) out.push({
79326
79424
  attrs: value.attrs,
79327
79425
  location: location ? `${location}.attrs` : "attrs"
79328
79426
  });
@@ -84021,7 +84119,7 @@ const LETTER_SPACING_MAP = {
84021
84119
  normal: "0",
84022
84120
  loose: "0.05em"
84023
84121
  };
84024
- function rule(selector, declarations) {
84122
+ function rule$1(selector, declarations) {
84025
84123
  return {
84026
84124
  selectors: typeof selector === "string" ? [selector] : selector,
84027
84125
  declarations
@@ -84204,7 +84302,7 @@ function variantAffordanceRule(variant, selector) {
84204
84302
  const maskUrl = affordanceIconMaskUrl(affordance.iconId);
84205
84303
  if (!maskUrl) return null;
84206
84304
  const mask = `url("${maskUrl}") no-repeat center / contain`;
84207
- return rule(`${selector}:not(.${AFFORDANCE_SUPPRESS_CLASS})::after`, [
84305
+ return rule$1(`${selector}:not(.${AFFORDANCE_SUPPRESS_CLASS})::after`, [
84208
84306
  ["content", "\"\""],
84209
84307
  ["display", "inline-block"],
84210
84308
  ["width", "1em"],
@@ -84243,7 +84341,7 @@ function variantBaseRule(variant, selector, global, tokens, theme, colour) {
84243
84341
  const borderWidth = BORDER_WIDTH_MAP[variant.border.widthClass] || "1px";
84244
84342
  decls.push(["border", `${borderWidth} solid currentColor`]);
84245
84343
  }
84246
- return rule(selector, decls);
84344
+ return rule$1(selector, decls);
84247
84345
  }
84248
84346
  /**
84249
84347
  * Resolve a colour for a button that does not own its own background.
@@ -84254,13 +84352,13 @@ function variantBaseRule(variant, selector, global, tokens, theme, colour) {
84254
84352
  */
84255
84353
  function variantStateColourRule(variantId, themeId, state, colour) {
84256
84354
  const baseSelector = `:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}`;
84257
- return rule(state === "hover" ? [`${baseSelector}:hover`, `${baseSelector}[data-force-hover="true"]`] : `${baseSelector}:${state}`, resolvedButtonColourDeclarations(colour, {
84355
+ return rule$1(state === "hover" ? [`${baseSelector}:hover`, `${baseSelector}[data-force-hover="true"]`] : `${baseSelector}:${state}`, resolvedButtonColourDeclarations(colour, {
84258
84356
  role: variantId,
84259
84357
  state
84260
84358
  }));
84261
84359
  }
84262
84360
  function variantFocusVisibleRule(variantId, themeId, focusIndicator) {
84263
- return rule(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:focus-visible`, resolvedButtonFocusDeclarations(focusIndicator, variantId));
84361
+ return rule$1(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:focus-visible`, resolvedButtonFocusDeclarations(focusIndicator, variantId));
84264
84362
  }
84265
84363
  function variantEffectsCss(variant, effects, themeId, tokens, theme) {
84266
84364
  if (!effects) return "";
@@ -84286,10 +84384,10 @@ function variantDisabledRules(variantId, themeId, colour) {
84286
84384
  `${base}:focus`
84287
84385
  ];
84288
84386
  const pseudoSelectors = [`${base}::before`, `${base}::after`];
84289
- return [rule(disabledSelectors, [...resolvedButtonColourDeclarations(colour, {
84387
+ return [rule$1(disabledSelectors, [...resolvedButtonColourDeclarations(colour, {
84290
84388
  role: variantId,
84291
84389
  state: "disabled"
84292
- }), ...RESOLVED_BUTTON_DISABLED_DECLARATIONS]), rule(pseudoSelectors, [
84390
+ }), ...RESOLVED_BUTTON_DISABLED_DECLARATIONS]), rule$1(pseudoSelectors, [
84293
84391
  ["opacity", "0"],
84294
84392
  ["animation", "none"],
84295
84393
  ["pointer-events", "none"]
@@ -84304,7 +84402,7 @@ function variantSizeRules(variant, variantId, themeSizes, themeId) {
84304
84402
  const decls = [];
84305
84403
  for (const decl of paddingDeclarations(config.padding, borderWidth)) decls.push(decl);
84306
84404
  if (config.fontSize) decls.push(["font-size", FONT_SIZE_MAP[config.fontSize] || "1rem"]);
84307
- rules.push(rule(selector, decls));
84405
+ rules.push(rule$1(selector, decls));
84308
84406
  }
84309
84407
  return rules;
84310
84408
  }
@@ -87801,6 +87899,15 @@ function asSiteStyleId(value) {
87801
87899
  function asDesignSystemTemplateId(value) {
87802
87900
  return brandNonEmptyString(value, "DesignSystemTemplateId");
87803
87901
  }
87902
+ function toNonEmptyReadonlyArray(values) {
87903
+ const [first, ...rest] = values;
87904
+ return first === void 0 ? null : [first, ...rest];
87905
+ }
87906
+ function requireNonEmptyReadonlyArray(values, message = "Expected a non-empty array.") {
87907
+ const result = toNonEmptyReadonlyArray(values);
87908
+ if (!result) throw new Error(message);
87909
+ return result;
87910
+ }
87804
87911
  function makeBoundaryId(previousBlockId, nextBlockId) {
87805
87912
  return asDesignBoundaryId(`boundary:${encodeURIComponent(previousBlockId)}:${encodeURIComponent(nextBlockId)}`);
87806
87913
  }
@@ -88113,16 +88220,18 @@ const themeV2RuntimeStyleFieldNames = [
88113
88220
  "_frameStyles",
88114
88221
  "_sectionStyles",
88115
88222
  "_containerStyles",
88116
- "_cardStyles",
88117
- "_transitionStyles",
88118
- "styleGroup"
88223
+ "_cardStyles"
88119
88224
  ];
88120
88225
  const themeV2BlockSpecificRawStyleFieldNames = [
88121
88226
  "_contentCardStyles",
88122
88227
  "_heroLayoutStyles",
88123
88228
  "_textCardStyles"
88124
88229
  ];
88125
- const themeV2NormalEditorRawStyleFieldNames = [...themeV2RuntimeStyleFieldNames, ...themeV2BlockSpecificRawStyleFieldNames];
88230
+ const themeV2NormalEditorRawStyleFieldNames = [
88231
+ ...themeV2RuntimeStyleFieldNames,
88232
+ ...themeV2BlockSpecificRawStyleFieldNames,
88233
+ "styleGroup"
88234
+ ];
88126
88235
  const themeV2NormalEditorRawStyleFieldNameSet = new Set(themeV2NormalEditorRawStyleFieldNames);
88127
88236
  [...themeV2RuntimeStyleFieldNames, ...themeV2BlockSpecificRawStyleFieldNames];
88128
88237
  const pageDesignEditorBackgroundOverrideOwner = "page-design-editor";
@@ -88133,6 +88242,21 @@ function isThemeV2NormalEditorRawStyleFieldName(fieldName) {
88133
88242
  function isThemeV2NormalEditorRawStylePath(fieldPath) {
88134
88243
  return isThemeV2NormalEditorRawStyleFieldName(fieldPath.split(".")[0] ?? "");
88135
88244
  }
88245
+ const staleTransitionStyleGroupPrefix = "site-style-transition-";
88246
+ /**
88247
+ * A `styleGroup` in the retired `site-style-transition-*` namespace is residue
88248
+ * from the old per-block transition path (issue 1422). Any other value is
88249
+ * authored and must survive.
88250
+ */
88251
+ function isStaleTransitionStyleGroup(value) {
88252
+ return typeof value === "string" && value.startsWith(staleTransitionStyleGroupPrefix);
88253
+ }
88254
+ /** Drops a stale transition `styleGroup`, leaving authored values untouched. */
88255
+ function stripStaleTransitionStyleGroup(content) {
88256
+ if (!isStaleTransitionStyleGroup(content.styleGroup)) return { ...content };
88257
+ const { styleGroup: _stale, ...rest } = content;
88258
+ return rest;
88259
+ }
88136
88260
  //#endregion
88137
88261
  //#region ../theme-core/src/header/legacyHeaderVariant.ts
88138
88262
  /**
@@ -88471,7 +88595,7 @@ const boxBackgroundSchema = z.discriminatedUnion("type", [
88471
88595
  })
88472
88596
  ]);
88473
88597
  function normalizeAuthoringOpacity(value) {
88474
- if (!isRecord$18(value)) return value;
88598
+ if (!isRecord$19(value)) return value;
88475
88599
  return {
88476
88600
  ...value,
88477
88601
  opacity: normalizeOpacity(value.opacity)
@@ -88480,7 +88604,7 @@ function normalizeAuthoringOpacity(value) {
88480
88604
  function normalizeOpacity(value) {
88481
88605
  return typeof value === "number" && value > 1 && value <= 100 ? value / 100 : value;
88482
88606
  }
88483
- function isRecord$18(value) {
88607
+ function isRecord$19(value) {
88484
88608
  return typeof value === "object" && value !== null && !Array.isArray(value);
88485
88609
  }
88486
88610
  //#endregion
@@ -89225,7 +89349,7 @@ const PUBLIC_DOWNLOAD_MEDIA_TYPES = [
89225
89349
  /**
89226
89350
  * Strict record guard: excludes null, primitives, and arrays.
89227
89351
  */
89228
- function isRecord$17(value) {
89352
+ function isRecord$18(value) {
89229
89353
  return typeof value === "object" && value !== null && !Array.isArray(value);
89230
89354
  }
89231
89355
  /**
@@ -89350,7 +89474,7 @@ function buildRichTextSchema(options) {
89350
89474
  const content = val.content;
89351
89475
  if (!Array.isArray(content) || content.length === 0) return false;
89352
89476
  const first = content[0];
89353
- if (content.length === 1 && isRecord$17(first) && first.type === "paragraph" && (!("content" in first) || first.content == null)) return false;
89477
+ if (content.length === 1 && isRecord$18(first) && first.type === "paragraph" && (!("content" in first) || first.content == null)) return false;
89354
89478
  return true;
89355
89479
  }, { message: `${label} is required` });
89356
89480
  }
@@ -90429,7 +90553,7 @@ var BlockApiError = class extends Error {
90429
90553
  this.name = "BlockApiError";
90430
90554
  }
90431
90555
  };
90432
- function isRecord$16(value) {
90556
+ function isRecord$17(value) {
90433
90557
  return typeof value === "object" && value !== null;
90434
90558
  }
90435
90559
  function isJsonResponseContentType(contentType) {
@@ -90453,15 +90577,15 @@ async function parseJsonBody(response) {
90453
90577
  }
90454
90578
  }
90455
90579
  function isFieldError(value) {
90456
- return isRecord$16(value) && !Array.isArray(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string";
90580
+ return isRecord$17(value) && !Array.isArray(value) && typeof value.field === "string" && typeof value.code === "string" && typeof value.message === "string";
90457
90581
  }
90458
90582
  function fieldErrorsFromUnknown(value) {
90459
90583
  if (!Array.isArray(value)) return void 0;
90460
90584
  return value.every(isFieldError) ? value : void 0;
90461
90585
  }
90462
90586
  function getBlockApiErrorDetails(data) {
90463
- if (!isRecord$16(data)) return {};
90464
- const nestedError = isRecord$16(data.error) ? data.error : void 0;
90587
+ if (!isRecord$17(data)) return {};
90588
+ const nestedError = isRecord$17(data.error) ? data.error : void 0;
90465
90589
  const message = typeof nestedError?.message === "string" ? nestedError.message : typeof data.message === "string" ? data.message : void 0;
90466
90590
  const code = typeof nestedError?.code === "string" ? nestedError.code : typeof data.code === "string" ? data.code : void 0;
90467
90591
  const fieldErrors = fieldErrorsFromUnknown(nestedError?.fieldErrors) ?? fieldErrorsFromUnknown(data.fieldErrors);
@@ -91297,11 +91421,11 @@ var UnsafePathSegmentError = class extends Error {
91297
91421
  function assertSafePathSegments(segments) {
91298
91422
  for (const segment of segments) if (typeof segment === "string" && UNSAFE_PATH_SEGMENTS.has(segment)) throw new UnsafePathSegmentError(segment);
91299
91423
  }
91300
- function isRecord$15(value) {
91424
+ function isRecord$16(value) {
91301
91425
  return typeof value === "object" && value !== null && !Array.isArray(value);
91302
91426
  }
91303
91427
  function isPathContainer(value) {
91304
- return Array.isArray(value) || isRecord$15(value);
91428
+ return Array.isArray(value) || isRecord$16(value);
91305
91429
  }
91306
91430
  function createPathContainer(nextIsIndex) {
91307
91431
  return nextIsIndex ? [] : {};
@@ -94414,7 +94538,7 @@ function parseSiteIconStyleGuideVersionId(value) {
94414
94538
  //#endregion
94415
94539
  //#region ../blocks/src/system/icons/value.ts
94416
94540
  const NO_BLOCK_ICON_VALUE = { kind: "none" };
94417
- function isRecord$14(value) {
94541
+ function isRecord$15(value) {
94418
94542
  return typeof value === "object" && value !== null && !Array.isArray(value);
94419
94543
  }
94420
94544
  function parseBlockIconValue(value) {
@@ -94422,7 +94546,7 @@ function parseBlockIconValue(value) {
94422
94546
  kind: "semantic",
94423
94547
  iconId: value
94424
94548
  };
94425
- if (!isRecord$14(value)) return NO_BLOCK_ICON_VALUE;
94549
+ if (!isRecord$15(value)) return NO_BLOCK_ICON_VALUE;
94426
94550
  switch (value.kind) {
94427
94551
  case "none": return NO_BLOCK_ICON_VALUE;
94428
94552
  case "semantic": return isSemanticIconId(value.iconId) ? {
@@ -94503,7 +94627,7 @@ function buildGeneratedBlockIconRenderAssetLookupFromUnknown(value) {
94503
94627
  if (!Array.isArray(value)) return { kind: "invalid_asset_list" };
94504
94628
  const inputs = [];
94505
94629
  for (const [index, item] of value.entries()) {
94506
- if (!isRecord$14(item)) return {
94630
+ if (!isRecord$15(item)) return {
94507
94631
  kind: "invalid_asset_entry",
94508
94632
  index,
94509
94633
  reason: "not_object"
@@ -94563,7 +94687,7 @@ function resolveBlockIconRenderValue(value, generatedAssets = /* @__PURE__ */ ne
94563
94687
  }
94564
94688
  }
94565
94689
  function getGeneratedBlockIconStyleState(value, currentStyleGuideVersionId) {
94566
- if (isRecord$14(value) && value.kind === "generated" && parseBlockIconValue(value).kind !== "generated") return { kind: "invalid_generated" };
94690
+ if (isRecord$15(value) && value.kind === "generated" && parseBlockIconValue(value).kind !== "generated") return { kind: "invalid_generated" };
94567
94691
  const parsed = parseBlockIconValue(value);
94568
94692
  if (parsed.kind !== "generated") return { kind: "not_generated" };
94569
94693
  if (parsed.styleGuideVersionId === currentStyleGuideVersionId) return {
@@ -94634,11 +94758,11 @@ function resolveFeatureCardGeneratedIconSizePx(value) {
94634
94758
  const parsed = parseFeatureCardGeneratedIconSizePx(value);
94635
94759
  return parsed.ok ? parsed.value : unsafeAsFeatureCardGeneratedIconSizePx(FEATURE_CARD_GENERATED_ICON_SIZE_PX.default);
94636
94760
  }
94637
- function isRecord$13(value) {
94761
+ function isRecord$14(value) {
94638
94762
  return typeof value === "object" && value !== null && !Array.isArray(value);
94639
94763
  }
94640
94764
  function toPlainTextFromTipTap(value) {
94641
- if (!isRecord$13(value)) return "";
94765
+ if (!isRecord$14(value)) return "";
94642
94766
  if (typeof value.text === "string") return value.text;
94643
94767
  const content = value.content;
94644
94768
  if (!Array.isArray(content)) return "";
@@ -96087,18 +96211,43 @@ const publicAppointmentServiceSchema = z.object({
96087
96211
  pricing: publicPricingSchema.optional(),
96088
96212
  priceAmount: z.number().nullable(),
96089
96213
  priceCurrency: z.string().nullable(),
96090
- location: publicLocationSchema
96214
+ location: publicLocationSchema,
96215
+ categoryId: z.string().nullable().optional().transform((value) => value ?? null)
96091
96216
  });
96092
- const publicAppointmentServicesSchema = z.array(publicAppointmentServiceSchema);
96217
+ const publicServiceCategorySchema = z.object({
96218
+ id: z.string().min(1),
96219
+ name: z.string(),
96220
+ description: z.string().nullable().optional().transform((value) => value ?? null)
96221
+ });
96222
+ /**
96223
+ * The loader hands over the catalogue `{ services, categories }` (issue 1403).
96224
+ * A bare services array is still accepted and read as a site with no
96225
+ * categories, so callers from before categories keep the flat list.
96226
+ */
96227
+ const publicAppointmentServicesSchema = z.union([z.array(publicAppointmentServiceSchema).transform((services) => ({
96228
+ services,
96229
+ categories: []
96230
+ })), z.object({
96231
+ services: z.array(publicAppointmentServiceSchema),
96232
+ categories: z.array(publicServiceCategorySchema)
96233
+ })]);
96093
96234
  const EMPTY_MESSAGE = "No appointments are available right now.";
96094
96235
  const ERROR_MESSAGE = "Appointments are temporarily unavailable. Please try again shortly.";
96236
+ const OTHER_GROUP_KEY = "other";
96237
+ const OTHER_GROUP_HEADING = "Other";
96095
96238
  function parseServices(value) {
96096
96239
  const result = publicAppointmentServicesSchema.safeParse(value);
96097
96240
  return result.success ? {
96098
96241
  kind: "valid",
96099
- services: result.data
96242
+ services: result.data.services,
96243
+ categories: result.data.categories
96100
96244
  } : { kind: "invalid" };
96101
96245
  }
96246
+ function readCategoryFilter(value) {
96247
+ if (typeof value !== "string") return null;
96248
+ const trimmed = value.trim();
96249
+ return trimmed.length > 0 ? trimmed : null;
96250
+ }
96102
96251
  function formatAppointmentDuration(durationMinutes) {
96103
96252
  return `${durationMinutes} min`;
96104
96253
  }
@@ -96172,8 +96321,44 @@ function buildRows(services, pathname, servicePathPrefix) {
96172
96321
  detailsHref: servicePathPrefix && service.slug ? `${servicePathPrefix.replace(/\/$/, "")}/${encodeURIComponent(service.slug)}` : null
96173
96322
  }));
96174
96323
  }
96324
+ const EMPTY_VIEW = {
96325
+ kind: "empty",
96326
+ message: EMPTY_MESSAGE
96327
+ };
96328
+ function categoryGroup(category, services, toRows) {
96329
+ return {
96330
+ key: category.id,
96331
+ heading: category.name,
96332
+ intro: category.description,
96333
+ rows: toRows(services)
96334
+ };
96335
+ }
96336
+ function viewFromLayout(layout, toRows) {
96337
+ switch (layout.kind) {
96338
+ case "uncategorized-site": return layout.services.length === 0 ? EMPTY_VIEW : {
96339
+ kind: "items",
96340
+ rows: toRows(layout.services)
96341
+ };
96342
+ case "categorized-site": {
96343
+ const groups = layout.groups.map((group) => categoryGroup(group.category, group.services, toRows));
96344
+ const other = layout.other.length === 0 ? [] : [{
96345
+ key: OTHER_GROUP_KEY,
96346
+ heading: OTHER_GROUP_HEADING,
96347
+ intro: null,
96348
+ rows: toRows(layout.other)
96349
+ }];
96350
+ const allGroups = [...groups, ...other];
96351
+ return allGroups.length === 0 ? EMPTY_VIEW : {
96352
+ kind: "groups",
96353
+ groups: allGroups
96354
+ };
96355
+ }
96356
+ default: return assertNever$10(layout);
96357
+ }
96358
+ }
96175
96359
  function buildAppointmentServicesView(input) {
96176
- if (!input.pathname) return {
96360
+ const pathname = input.pathname;
96361
+ if (!pathname) return {
96177
96362
  kind: "error",
96178
96363
  message: ERROR_MESSAGE
96179
96364
  };
@@ -96182,76 +96367,128 @@ function buildAppointmentServicesView(input) {
96182
96367
  kind: "error",
96183
96368
  message: ERROR_MESSAGE
96184
96369
  };
96185
- if (parsed.services.length === 0) return {
96186
- kind: "empty",
96187
- message: EMPTY_MESSAGE
96188
- };
96189
- return {
96190
- kind: "items",
96191
- rows: buildRows(parsed.services, input.pathname, input.servicePathPrefix ?? null)
96192
- };
96370
+ const toRows = (services) => buildRows(services, pathname, input.servicePathPrefix ?? null);
96371
+ const view = selectCategoryView(parsed.categories, parsed.services, readCategoryFilter(input.categoryId));
96372
+ switch (view.kind) {
96373
+ case "single": return view.group.services.length === 0 ? EMPTY_VIEW : {
96374
+ kind: "groups",
96375
+ groups: [categoryGroup(view.group.category, view.group.services, toRows)]
96376
+ };
96377
+ case "all":
96378
+ case "fallback-all": return viewFromLayout(view.layout, toRows);
96379
+ default: return assertNever$10(view);
96380
+ }
96381
+ }
96382
+ function AppointmentServiceRows({ rows, RowTitle }) {
96383
+ return /* @__PURE__ */ jsx("div", {
96384
+ className: "appointment-services-rows rb-flex rb-flex-col",
96385
+ children: rows.map((row) => /* @__PURE__ */ jsxs("article", {
96386
+ className: "appointment-services-row rb-border-b rb-border-border rb-py-4",
96387
+ children: [
96388
+ /* @__PURE__ */ jsxs("div", {
96389
+ className: "appointment-services-row-main rb-min-w-0",
96390
+ children: [
96391
+ /* @__PURE__ */ jsx(RowTitle, {
96392
+ className: "appointment-services-row-title rb-text-lg rb-font-medium rb-leading-tight",
96393
+ children: row.detailsHref ? /* @__PURE__ */ jsx("a", {
96394
+ href: row.detailsHref,
96395
+ children: row.title
96396
+ }) : row.title
96397
+ }),
96398
+ row.description ? /* @__PURE__ */ jsx("p", {
96399
+ className: "appointment-services-row-description rb-text-sm rb-leading-6 status-muted",
96400
+ children: row.description
96401
+ }) : null,
96402
+ /* @__PURE__ */ jsxs("p", {
96403
+ className: "appointment-services-row-meta rb-caption status-muted",
96404
+ children: [row.durationLabel, row.locationLabel ? ` · ${row.locationLabel}` : ""]
96405
+ })
96406
+ ]
96407
+ }),
96408
+ row.priceLabel ? /* @__PURE__ */ jsx("span", {
96409
+ className: "appointment-services-row-price rb-text-lg",
96410
+ children: row.priceLabel
96411
+ }) : null,
96412
+ /* @__PURE__ */ jsx(ThemedButtonLink, {
96413
+ variant: "secondary",
96414
+ size: "sm",
96415
+ extraClassName: "appointment-services-row-action",
96416
+ href: row.href,
96417
+ label: "Check times",
96418
+ screenReaderSuffix: ` for ${row.title}`
96419
+ })
96420
+ ]
96421
+ }, row.id))
96422
+ });
96193
96423
  }
96194
- function AppointmentServicesNode({ sectionHeading, services, pathname, servicePathPrefix, className }) {
96424
+ function AppointmentServiceGroups({ groups, hasSectionHeading, idPrefix }) {
96425
+ const GroupHeading = hasSectionHeading ? "h3" : "h2";
96426
+ const RowTitle = hasSectionHeading ? "h4" : "h3";
96427
+ return /* @__PURE__ */ jsx("div", {
96428
+ className: "appointment-services-groups rb-flex rb-flex-col rb-gap-10",
96429
+ children: groups.map((group) => {
96430
+ const headingId = `${idPrefix}-group-${group.key}`;
96431
+ return /* @__PURE__ */ jsxs("section", {
96432
+ className: "appointment-services-group rb-flex rb-flex-col rb-gap-2",
96433
+ "aria-labelledby": headingId,
96434
+ "data-appointment-services-group": group.key,
96435
+ children: [/* @__PURE__ */ jsxs("div", {
96436
+ className: "appointment-services-group-heading",
96437
+ children: [/* @__PURE__ */ jsx(GroupHeading, {
96438
+ id: headingId,
96439
+ className: "appointment-services-group-title rb-text-sm rb-font-semibold rb-uppercase rb-tracking-wide",
96440
+ children: group.heading
96441
+ }), group.intro ? /* @__PURE__ */ jsx("p", {
96442
+ className: "appointment-services-group-intro rb-text-sm rb-leading-6 rb-max-w-prose status-muted",
96443
+ children: group.intro
96444
+ }) : null]
96445
+ }), /* @__PURE__ */ jsx(AppointmentServiceRows, {
96446
+ rows: group.rows,
96447
+ RowTitle
96448
+ })]
96449
+ }, group.key);
96450
+ })
96451
+ });
96452
+ }
96453
+ function AppointmentServicesNode({ sectionHeading, categoryId, services, pathname, servicePathPrefix, blockId, className }) {
96195
96454
  const view = buildAppointmentServicesView({
96196
96455
  services,
96456
+ categoryId,
96197
96457
  pathname: typeof pathname === "string" ? pathname : void 0,
96198
96458
  servicePathPrefix: typeof servicePathPrefix === "string" ? servicePathPrefix : null
96199
96459
  });
96200
96460
  const rootClassName = `appointment-services ${className ?? ""}`.trim();
96201
- const RowTitle = sectionHeading ? "h3" : "h2";
96202
- if (view.kind === "error" || view.kind === "empty") return /* @__PURE__ */ jsx("div", {
96203
- className: rootClassName,
96204
- "data-appointment-services-state": view.kind,
96205
- children: /* @__PURE__ */ jsx("p", {
96206
- className: "rb-caption status-muted",
96207
- role: "status",
96208
- children: view.message
96209
- })
96210
- });
96211
- return /* @__PURE__ */ jsx("div", {
96212
- className: `${rootClassName} rb-flex rb-flex-col`,
96213
- "data-appointment-services-state": "populated",
96214
- children: /* @__PURE__ */ jsx("div", {
96215
- className: "appointment-services-rows rb-flex rb-flex-col",
96216
- children: view.rows.map((row) => /* @__PURE__ */ jsxs("article", {
96217
- className: "appointment-services-row rb-border-b rb-border-border rb-py-4",
96218
- children: [
96219
- /* @__PURE__ */ jsxs("div", {
96220
- className: "appointment-services-row-main rb-min-w-0",
96221
- children: [
96222
- /* @__PURE__ */ jsx(RowTitle, {
96223
- className: "appointment-services-row-title rb-text-lg rb-font-medium rb-leading-tight",
96224
- children: row.detailsHref ? /* @__PURE__ */ jsx("a", {
96225
- href: row.detailsHref,
96226
- children: row.title
96227
- }) : row.title
96228
- }),
96229
- row.description ? /* @__PURE__ */ jsx("p", {
96230
- className: "appointment-services-row-description rb-text-sm rb-leading-6 status-muted",
96231
- children: row.description
96232
- }) : null,
96233
- /* @__PURE__ */ jsxs("p", {
96234
- className: "appointment-services-row-meta rb-caption status-muted",
96235
- children: [row.durationLabel, row.locationLabel ? ` · ${row.locationLabel}` : ""]
96236
- })
96237
- ]
96238
- }),
96239
- row.priceLabel ? /* @__PURE__ */ jsx("span", {
96240
- className: "appointment-services-row-price rb-text-lg",
96241
- children: row.priceLabel
96242
- }) : null,
96243
- /* @__PURE__ */ jsx(ThemedButtonLink, {
96244
- variant: "secondary",
96245
- size: "sm",
96246
- extraClassName: "appointment-services-row-action",
96247
- href: row.href,
96248
- label: "Check times",
96249
- screenReaderSuffix: ` for ${row.title}`
96250
- })
96251
- ]
96252
- }, row.id))
96253
- })
96254
- });
96461
+ const hasSectionHeading = Boolean(sectionHeading);
96462
+ switch (view.kind) {
96463
+ case "error":
96464
+ case "empty": return /* @__PURE__ */ jsx("div", {
96465
+ className: rootClassName,
96466
+ "data-appointment-services-state": view.kind,
96467
+ children: /* @__PURE__ */ jsx("p", {
96468
+ className: "rb-caption status-muted",
96469
+ role: "status",
96470
+ children: view.message
96471
+ })
96472
+ });
96473
+ case "items": return /* @__PURE__ */ jsx("div", {
96474
+ className: `${rootClassName} rb-flex rb-flex-col`,
96475
+ "data-appointment-services-state": "populated",
96476
+ children: /* @__PURE__ */ jsx(AppointmentServiceRows, {
96477
+ rows: view.rows,
96478
+ RowTitle: hasSectionHeading ? "h3" : "h2"
96479
+ })
96480
+ });
96481
+ case "groups": return /* @__PURE__ */ jsx("div", {
96482
+ className: `${rootClassName} rb-flex rb-flex-col`,
96483
+ "data-appointment-services-state": "populated",
96484
+ children: /* @__PURE__ */ jsx(AppointmentServiceGroups, {
96485
+ groups: view.groups,
96486
+ hasSectionHeading,
96487
+ idPrefix: `appointment-services-${blockId ?? "block"}`
96488
+ })
96489
+ });
96490
+ default: return assertNever$10(view);
96491
+ }
96255
96492
  }
96256
96493
  //#endregion
96257
96494
  //#region ../blocks/src/contracts/runtime.ts
@@ -96506,12 +96743,12 @@ function resolveBookingSiteId(siteId, form) {
96506
96743
  return siteId || form?.siteId || "";
96507
96744
  }
96508
96745
  function normalizeBookingFormConfig(form) {
96509
- if (!isRecord$12(form)) return null;
96746
+ if (!isRecord$13(form)) return null;
96510
96747
  const id = readNonEmptyString(form.id);
96511
96748
  const name = readNonEmptyString(form.name);
96512
96749
  if (!id || !name) return null;
96513
- const schemaSource = isRecord$12(form.schema) ? form.schema : isRecord$12(form.schemaJson) ? form.schemaJson : null;
96514
- const settingsSource = isRecord$12(form.settings) ? form.settings : isRecord$12(form.settingsJson) ? form.settingsJson : null;
96750
+ const schemaSource = isRecord$13(form.schema) ? form.schema : isRecord$13(form.schemaJson) ? form.schemaJson : null;
96751
+ const settingsSource = isRecord$13(form.settings) ? form.settings : isRecord$13(form.settingsJson) ? form.settingsJson : null;
96515
96752
  return {
96516
96753
  id,
96517
96754
  name,
@@ -96531,8 +96768,8 @@ function normalizeBookingServices(services) {
96531
96768
  };
96532
96769
  if (!services) return [];
96533
96770
  if (Array.isArray(services)) return services.filter(isBookingFormService).map(normalizeService);
96534
- if (isRecord$12(services) && "services" in services) return Array.isArray(services.services) ? services.services.filter(isBookingFormService).map(normalizeService) : [];
96535
- return isRecord$12(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
96771
+ if (isRecord$13(services) && "services" in services) return Array.isArray(services.services) ? services.services.filter(isBookingFormService).map(normalizeService) : [];
96772
+ return isRecord$13(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
96536
96773
  }
96537
96774
  function normalizeBookingSchema(value) {
96538
96775
  if (!value) return void 0;
@@ -96543,7 +96780,7 @@ function normalizeBookingSchema(value) {
96543
96780
  };
96544
96781
  }
96545
96782
  function readBookingFormField(value) {
96546
- if (!isRecord$12(value)) return null;
96783
+ if (!isRecord$13(value)) return null;
96547
96784
  const id = readNonEmptyString(value.id);
96548
96785
  const label = readNonEmptyString(value.label);
96549
96786
  const type = readBookingFieldType(value.type);
@@ -96586,7 +96823,7 @@ function readBookingFieldOptions(value) {
96586
96823
  return options.length > 0 ? options : void 0;
96587
96824
  }
96588
96825
  function readBookingFieldOption(value) {
96589
- if (!isRecord$12(value)) return null;
96826
+ if (!isRecord$13(value)) return null;
96590
96827
  const optionValue = readNonEmptyString(value.value);
96591
96828
  const label = readNonEmptyString(value.label);
96592
96829
  return optionValue && label ? {
@@ -96606,7 +96843,7 @@ function normalizeBookingSettings(value) {
96606
96843
  };
96607
96844
  }
96608
96845
  function isBookingFormService(value) {
96609
- return isRecord$12(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string" && (value.consent === void 0 || blockBookingConsentSchema.safeParse(value.consent).success);
96846
+ return isRecord$13(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string" && (value.consent === void 0 || blockBookingConsentSchema.safeParse(value.consent).success);
96610
96847
  }
96611
96848
  function collectBookingCustomFields(data) {
96612
96849
  const customFields = {};
@@ -96656,7 +96893,7 @@ function readStringArray(value) {
96656
96893
  const strings = value.filter((item) => typeof item === "string");
96657
96894
  return strings.length > 0 ? strings : void 0;
96658
96895
  }
96659
- function isRecord$12(value) {
96896
+ function isRecord$13(value) {
96660
96897
  return typeof value === "object" && value !== null;
96661
96898
  }
96662
96899
  function getBookingServicePaymentSelectionMode(service) {
@@ -96850,6 +97087,31 @@ function getBookingSuccessMessage(successMessage) {
96850
97087
  return successMessage?.trim() || "Your appointment has been booked! Check your email for confirmation.";
96851
97088
  }
96852
97089
  //#endregion
97090
+ //#region ../blocks/src/system/runtime/nodes/booking-form.service-categories.ts
97091
+ function isRecord$12(value) {
97092
+ return typeof value === "object" && value !== null && !Array.isArray(value);
97093
+ }
97094
+ function readCategory(value) {
97095
+ if (!isRecord$12(value)) return [];
97096
+ const { id, name, description } = value;
97097
+ if (typeof id !== "string" || id.trim() === "" || typeof name !== "string") return [];
97098
+ return [{
97099
+ id,
97100
+ name,
97101
+ description: typeof description === "string" ? description : null
97102
+ }];
97103
+ }
97104
+ /**
97105
+ * The ordered service categories carried by a catalogue-shaped services
97106
+ * source (`{ services, categories }`). Every other shape has none, which
97107
+ * reads as a site without categories. Order is preserved: the database owns
97108
+ * it.
97109
+ */
97110
+ function normalizeBookingServiceCategories(services) {
97111
+ if (!isRecord$12(services) || !Array.isArray(services.categories)) return [];
97112
+ return services.categories.flatMap(readCategory);
97113
+ }
97114
+ //#endregion
96853
97115
  //#region ../blocks/src/system/runtime/nodes/booking-runtime-sources.ts
96854
97116
  const bookingFieldOptionSchema = z.object({
96855
97117
  value: z.string(),
@@ -96927,11 +97189,20 @@ const bookingServiceSchema = z.object({
96927
97189
  priceCurrency: z.string().nullable().optional(),
96928
97190
  paymentTerms: blockBookingPaymentTermsSchema.optional(),
96929
97191
  paymentTermsAvailability: blockBookingPaymentTermsAvailabilitySchema.optional(),
96930
- consent: blockBookingConsentSchema.optional()
97192
+ consent: blockBookingConsentSchema.optional(),
97193
+ categoryId: z.string().nullable().optional()
97194
+ });
97195
+ const bookingServiceCategorySchema = z.object({
97196
+ id: z.string(),
97197
+ name: z.string(),
97198
+ description: z.string().nullable()
96931
97199
  });
96932
97200
  const bookingServicesSourceSchema = z.union([
96933
97201
  z.array(bookingServiceSchema),
96934
- z.object({ services: z.array(bookingServiceSchema).optional() }),
97202
+ z.object({
97203
+ services: z.array(bookingServiceSchema).optional(),
97204
+ categories: z.array(bookingServiceCategorySchema).optional()
97205
+ }),
96935
97206
  z.record(z.string(), bookingServiceSchema)
96936
97207
  ]);
96937
97208
  const bookingConfigSchema = z.object({
@@ -96984,6 +97255,7 @@ const bookingHydrationSchema = z.object({
96984
97255
  rebookContext: z.object({ kind: z.literal("portal_rebook") }).nullable().optional(),
96985
97256
  form: bookingConfigSchema.nullable(),
96986
97257
  services: z.array(bookingServiceSchema),
97258
+ serviceCategories: z.array(bookingServiceCategorySchema).optional(),
96987
97259
  spamProtectionEnabled: z.boolean().optional()
96988
97260
  });
96989
97261
  const bookingRenderSchema = z.object({
@@ -97028,6 +97300,7 @@ const bookingFormBlockRuntime = defineClientOwnedBlockRuntime({
97028
97300
  rebookContext: source.rebookContext ?? null,
97029
97301
  form,
97030
97302
  services,
97303
+ serviceCategories: normalizeBookingServiceCategories(source.services),
97031
97304
  spamProtectionEnabled: source.spamProtectionEnabled
97032
97305
  }
97033
97306
  };
@@ -104079,6 +104352,607 @@ function cardRoleFromLegacyVocabulary(value) {
104079
104352
  */
104080
104353
  const cardRoleWithLegacyAliasSchema = z.preprocess((value) => cardRoleFromLegacyVocabulary(value) ?? value, cardRoleSchema);
104081
104354
  //#endregion
104355
+ //#region ../theme-core/src/site-styles/runtimeBackgroundResolution.ts
104356
+ function parseCanonicalRuntimeBackground(content) {
104357
+ const background = readRecord(readRecord(content._sectionStyles)?.background);
104358
+ if (!background) return { kind: "none" };
104359
+ const parsed = boxBackgroundSchema.safeParse(background);
104360
+ if (!parsed.success) return { kind: "none" };
104361
+ return parsed.data.contentOwner === "page-design-editor" ? {
104362
+ kind: "page-design-override",
104363
+ background: parsed.data
104364
+ } : {
104365
+ kind: "block-authored",
104366
+ background: parsed.data
104367
+ };
104368
+ }
104369
+ function resolveSectionBackground(source, lookDefault) {
104370
+ if (source.kind === "page-design-override") return {
104371
+ kind: "page-design-override",
104372
+ background: source.background
104373
+ };
104374
+ if (lookDefault) {
104375
+ if (source.kind === "block-authored" && (source.background.type === "image" || source.background.type === "video")) return {
104376
+ kind: "block-authored",
104377
+ background: mergeLookForegroundWithAuthoredMedia(lookDefault, source.background)
104378
+ };
104379
+ return {
104380
+ kind: "look-default",
104381
+ background: lookDefault
104382
+ };
104383
+ }
104384
+ if (source.kind === "block-authored" && (source.background.type === "image" || source.background.type === "video")) return {
104385
+ kind: "block-authored",
104386
+ background: source.background
104387
+ };
104388
+ return source.kind === "block-authored" ? {
104389
+ kind: "block-authored",
104390
+ background: source.background
104391
+ } : { kind: "none" };
104392
+ }
104393
+ function mergeLookForegroundWithAuthoredMedia(lookDefault, authored) {
104394
+ const merged = boxBackgroundSchema.safeParse({
104395
+ ...lookDefault,
104396
+ type: authored.type,
104397
+ image: authored.image,
104398
+ objectFit: authored.objectFit,
104399
+ scale: authored.scale,
104400
+ position: authored.position
104401
+ });
104402
+ return merged.success ? merged.data : authored;
104403
+ }
104404
+ function readRecord(value) {
104405
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
104406
+ }
104407
+ //#endregion
104408
+ //#region ../theme-core/src/site-styles/transitionCatalogue.ts
104409
+ /**
104410
+ * Family order for pickers. Entries below are listed family by family in this
104411
+ * same order, so the flat option list and the grouped families agree.
104412
+ */
104413
+ const transitionCatalogueFamilies = [
104414
+ {
104415
+ id: "gradient",
104416
+ label: "Gradient"
104417
+ },
104418
+ {
104419
+ id: "wave",
104420
+ label: "Wave"
104421
+ },
104422
+ {
104423
+ id: "angle",
104424
+ label: "Angle"
104425
+ }
104426
+ ];
104427
+ const mediumHeight = "6rem + 2px";
104428
+ const compactHeight = "4.5rem + 2px";
104429
+ const largeHeight = "7rem + 2px";
104430
+ /**
104431
+ * The single source of transition vocabulary. Order is significant: it is the
104432
+ * order of `boundaryOptions` (after `none`) and of each family's variants,
104433
+ * which persisted pins, page-flow presets and the editor picker all rely on.
104434
+ *
104435
+ * Shapes match what `generateLayoutCss` draws today. Structured
104436
+ * amplitude/frequency treatments (issue 826) extend this list later.
104437
+ */
104438
+ const transitionCatalogueEntries = defineExactCatalog()([
104439
+ {
104440
+ intent: "soft-fade-short",
104441
+ optionId: asBoundaryOptionId("break:soft"),
104442
+ family: "gradient",
104443
+ variantId: "soft",
104444
+ label: "Soft",
104445
+ variantLabel: "Soft",
104446
+ treatment: {
104447
+ kind: "soft-fade",
104448
+ height: "medium",
104449
+ curve: "soft"
104450
+ },
104451
+ size: "medium",
104452
+ heightExpression: mediumHeight,
104453
+ shape: { kind: "fade" }
104454
+ },
104455
+ {
104456
+ intent: "soft-fade-long",
104457
+ optionId: asBoundaryOptionId("break:spacious"),
104458
+ family: "gradient",
104459
+ variantId: "spacious",
104460
+ label: "Spacious",
104461
+ variantLabel: "Spacious",
104462
+ treatment: {
104463
+ kind: "soft-fade",
104464
+ height: "large",
104465
+ curve: "spacious"
104466
+ },
104467
+ size: "spacious",
104468
+ heightExpression: "10rem",
104469
+ shape: { kind: "fade" }
104470
+ },
104471
+ {
104472
+ intent: "soft-fade-expansive",
104473
+ optionId: asBoundaryOptionId("break:expansive"),
104474
+ family: "gradient",
104475
+ variantId: "expansive",
104476
+ label: "Expansive",
104477
+ variantLabel: "Expansive",
104478
+ treatment: {
104479
+ kind: "soft-fade",
104480
+ height: "expansive",
104481
+ curve: "expansive"
104482
+ },
104483
+ size: "expansive",
104484
+ heightExpression: "14rem",
104485
+ shape: { kind: "fade" }
104486
+ },
104487
+ {
104488
+ intent: "wave-edge-balanced",
104489
+ optionId: asBoundaryOptionId("break:wave-balanced"),
104490
+ family: "wave",
104491
+ variantId: "balanced",
104492
+ label: "Balanced wave",
104493
+ variantLabel: "Balanced",
104494
+ treatment: {
104495
+ kind: "wave-edge",
104496
+ height: "medium",
104497
+ amplitude: "balanced",
104498
+ rhythm: "double",
104499
+ symmetry: "symmetrical"
104500
+ },
104501
+ size: "medium",
104502
+ heightExpression: mediumHeight,
104503
+ shape: {
104504
+ kind: "wave",
104505
+ mask: "balanced"
104506
+ }
104507
+ },
104508
+ {
104509
+ intent: "wave-edge",
104510
+ optionId: asBoundaryOptionId("break:wave"),
104511
+ family: "wave",
104512
+ variantId: "classic",
104513
+ label: "Classic wave",
104514
+ variantLabel: "Classic",
104515
+ treatment: {
104516
+ kind: "wave-edge",
104517
+ height: "medium",
104518
+ amplitude: "balanced",
104519
+ rhythm: "double",
104520
+ symmetry: "asymmetrical"
104521
+ },
104522
+ size: "medium",
104523
+ heightExpression: mediumHeight,
104524
+ shape: {
104525
+ kind: "wave",
104526
+ mask: "classic"
104527
+ }
104528
+ },
104529
+ {
104530
+ intent: "wave-edge-gentle",
104531
+ optionId: asBoundaryOptionId("break:wave-gentle"),
104532
+ family: "wave",
104533
+ variantId: "gentle",
104534
+ label: "Gentle wave",
104535
+ variantLabel: "Gentle",
104536
+ treatment: {
104537
+ kind: "wave-edge",
104538
+ height: "compact",
104539
+ amplitude: "subtle",
104540
+ rhythm: "single",
104541
+ symmetry: "symmetrical"
104542
+ },
104543
+ size: "compact",
104544
+ heightExpression: compactHeight,
104545
+ shape: {
104546
+ kind: "wave",
104547
+ mask: "gentle"
104548
+ }
104549
+ },
104550
+ {
104551
+ intent: "wave-edge-bold",
104552
+ optionId: asBoundaryOptionId("break:wave-bold"),
104553
+ family: "wave",
104554
+ variantId: "bold",
104555
+ label: "Bold wave",
104556
+ variantLabel: "Bold",
104557
+ treatment: {
104558
+ kind: "wave-edge",
104559
+ height: "large",
104560
+ amplitude: "bold",
104561
+ rhythm: "double",
104562
+ symmetry: "symmetrical"
104563
+ },
104564
+ size: "large",
104565
+ heightExpression: largeHeight,
104566
+ shape: {
104567
+ kind: "wave",
104568
+ mask: "bold"
104569
+ }
104570
+ },
104571
+ {
104572
+ intent: "wave-edge-drift",
104573
+ optionId: asBoundaryOptionId("break:wave-drift"),
104574
+ family: "wave",
104575
+ variantId: "drift",
104576
+ label: "Drift wave",
104577
+ variantLabel: "Drift",
104578
+ treatment: {
104579
+ kind: "wave-edge",
104580
+ height: "medium",
104581
+ amplitude: "balanced",
104582
+ rhythm: "double",
104583
+ symmetry: "drift"
104584
+ },
104585
+ size: "medium",
104586
+ heightExpression: mediumHeight,
104587
+ shape: {
104588
+ kind: "wave",
104589
+ mask: "drift"
104590
+ }
104591
+ },
104592
+ {
104593
+ intent: "angle-edge-subtle",
104594
+ optionId: asBoundaryOptionId("break:angle-subtle"),
104595
+ family: "angle",
104596
+ variantId: "subtle",
104597
+ label: "Subtle angle",
104598
+ variantLabel: "Subtle",
104599
+ treatment: {
104600
+ kind: "angle-edge",
104601
+ height: "compact",
104602
+ slope: "subtle",
104603
+ direction: "forward"
104604
+ },
104605
+ size: "compact",
104606
+ heightExpression: compactHeight,
104607
+ shape: {
104608
+ kind: "angle",
104609
+ polygon: {
104610
+ startTopPercent: 34,
104611
+ endTopPercent: 18
104612
+ }
104613
+ }
104614
+ },
104615
+ {
104616
+ intent: "angle-edge",
104617
+ optionId: asBoundaryOptionId("break:angle-balanced"),
104618
+ family: "angle",
104619
+ variantId: "balanced",
104620
+ label: "Balanced angle",
104621
+ variantLabel: "Balanced",
104622
+ treatment: {
104623
+ kind: "angle-edge",
104624
+ height: "medium",
104625
+ slope: "balanced",
104626
+ direction: "forward"
104627
+ },
104628
+ size: "medium",
104629
+ heightExpression: mediumHeight,
104630
+ shape: {
104631
+ kind: "angle",
104632
+ polygon: {
104633
+ startTopPercent: 42,
104634
+ endTopPercent: 8
104635
+ }
104636
+ }
104637
+ },
104638
+ {
104639
+ intent: "angle-edge-steep",
104640
+ optionId: asBoundaryOptionId("break:angle-steep"),
104641
+ family: "angle",
104642
+ variantId: "steep",
104643
+ label: "Steep angle",
104644
+ variantLabel: "Steep",
104645
+ treatment: {
104646
+ kind: "angle-edge",
104647
+ height: "large",
104648
+ slope: "steep",
104649
+ direction: "forward"
104650
+ },
104651
+ size: "large",
104652
+ heightExpression: largeHeight,
104653
+ shape: {
104654
+ kind: "angle",
104655
+ polygon: {
104656
+ startTopPercent: 54,
104657
+ endTopPercent: 0
104658
+ }
104659
+ }
104660
+ }
104661
+ ]);
104662
+ /** CSS `clip-path` value for an angle shape, as `generateLayoutCss` draws it. */
104663
+ function anglePolygonCss(polygon) {
104664
+ return `polygon(0 ${polygon.startTopPercent}%, 100% ${polygon.endTopPercent}%, 100% 100%, 0 100%)`;
104665
+ }
104666
+ //#endregion
104667
+ //#region ../theme-core/src/site-styles/sectionTransitions.ts
104668
+ function boundaryRuntimeMetrics(family, size, heightExpression) {
104669
+ return {
104670
+ family,
104671
+ size,
104672
+ heightCss: heightExpression.includes(" ") ? `calc(${heightExpression})` : heightExpression,
104673
+ reserveCss: `calc((${heightExpression}) / 2)`
104674
+ };
104675
+ }
104676
+ new Map(transitionCatalogueEntries.map((entry) => [entry.intent, entry]));
104677
+ const metricsByIntent = new Map(transitionCatalogueEntries.map((entry) => [entry.intent, boundaryRuntimeMetrics(entry.family, entry.size, entry.heightExpression)]));
104678
+ /**
104679
+ * The catalogue's compile-time coverage check guarantees an entry per
104680
+ * decorative intent, so a miss here is a broken invariant, not bad input.
104681
+ */
104682
+ function requireCatalogueValue(values, intent) {
104683
+ const value = values.get(intent);
104684
+ if (value === void 0) throw new Error(`Transition catalogue has no entry for ${intent}`);
104685
+ return value;
104686
+ }
104687
+ function isDecorativeTransitionIntent(transition) {
104688
+ return transition !== "none";
104689
+ }
104690
+ function boundaryRuntimeMetricsForDecorativeTransition(transition) {
104691
+ return requireCatalogueValue(metricsByIntent, transition);
104692
+ }
104693
+ const noneBoundaryOption = {
104694
+ id: asBoundaryOptionId("break:none"),
104695
+ label: "None",
104696
+ transitionAfter: "none",
104697
+ treatment: { kind: "none" }
104698
+ };
104699
+ const boundaryOptionByIntent = new Map(transitionCatalogueEntries.map((entry) => [entry.intent, {
104700
+ id: entry.optionId,
104701
+ label: entry.label,
104702
+ transitionAfter: entry.intent,
104703
+ treatment: entry.treatment
104704
+ }]));
104705
+ function boundaryOptionForTransitionIntent(intent) {
104706
+ return requireCatalogueValue(boundaryOptionByIntent, intent);
104707
+ }
104708
+ const softBoundaryOption = boundaryOptionForTransitionIntent("soft-fade-short");
104709
+ const spaciousBoundaryOption = boundaryOptionForTransitionIntent("soft-fade-long");
104710
+ boundaryOptionForTransitionIntent("soft-fade-expansive");
104711
+ boundaryOptionForTransitionIntent("wave-edge");
104712
+ boundaryOptionForTransitionIntent("wave-edge-balanced");
104713
+ boundaryOptionForTransitionIntent("wave-edge-gentle");
104714
+ boundaryOptionForTransitionIntent("wave-edge-bold");
104715
+ boundaryOptionForTransitionIntent("wave-edge-drift");
104716
+ boundaryOptionForTransitionIntent("angle-edge-subtle");
104717
+ boundaryOptionForTransitionIntent("angle-edge");
104718
+ boundaryOptionForTransitionIntent("angle-edge-steep");
104719
+ /**
104720
+ * Must remain non-empty; downstream code indexes `[0]` as the documented default.
104721
+ */
104722
+ const boundaryOptions = [noneBoundaryOption, ...transitionCatalogueEntries.map((entry) => boundaryOptionForTransitionIntent(entry.intent))];
104723
+ const noneTransitionVariant = {
104724
+ familyId: "none",
104725
+ variantId: "none",
104726
+ label: "None",
104727
+ boundaryOption: noneBoundaryOption
104728
+ };
104729
+ function transitionVariantForEntry(entry) {
104730
+ return {
104731
+ familyId: entry.family,
104732
+ variantId: entry.variantId,
104733
+ label: entry.variantLabel,
104734
+ boundaryOption: boundaryOptionForTransitionIntent(entry.intent)
104735
+ };
104736
+ }
104737
+ const sectionTransitionFamilies = [{
104738
+ id: "none",
104739
+ label: "None",
104740
+ variants: [noneTransitionVariant]
104741
+ }, ...transitionCatalogueFamilies.map((family) => ({
104742
+ id: family.id,
104743
+ label: family.label,
104744
+ variants: requireNonEmptyReadonlyArray(transitionCatalogueEntries.filter((entry) => entry.family === family.id).map(transitionVariantForEntry), `Transition family ${family.id} has no catalogue entries.`)
104745
+ }))];
104746
+ const boundaryOptionById = new Map(boundaryOptions.map((option) => [option.id, option]));
104747
+ const boundaryOptionByTreatmentKey = new Map(boundaryOptions.map((option) => [stableTreatmentKey(option.treatment), option]));
104748
+ const transitionVariantByBoundaryOptionId = new Map(sectionTransitionFamilies.flatMap((family) => family.variants.map((variant) => [variant.boundaryOption.id, variant])));
104749
+ const transitionFamilyById = new Map(sectionTransitionFamilies.map((family) => [family.id, family]));
104750
+ function sectionTransitionVariantForBoundaryOptionId(boundaryOptionId) {
104751
+ if (!boundaryOptionId) return null;
104752
+ return transitionVariantByBoundaryOptionId.get(boundaryOptionId) ?? null;
104753
+ }
104754
+ function boundaryTreatmentForBoundaryOptionId(boundaryOptionId) {
104755
+ return boundaryOptionById.get(boundaryOptionId)?.treatment ?? {
104756
+ kind: "legacy-unsupported",
104757
+ boundaryOptionId
104758
+ };
104759
+ }
104760
+ function boundaryOptionForTreatment(treatment) {
104761
+ if (treatment.kind === "legacy-unsupported") return null;
104762
+ return boundaryOptionByTreatmentKey.get(stableTreatmentKey(treatment)) ?? null;
104763
+ }
104764
+ function boundaryOptionIdForTreatment(treatment) {
104765
+ return boundaryOptionForTreatment(treatment)?.id ?? (treatment.kind === "legacy-unsupported" ? treatment.boundaryOptionId : null);
104766
+ }
104767
+ /**
104768
+ * Supported decorative intent for a structured treatment, or null for `none`,
104769
+ * legacy-unsupported pins and structured treatments the catalogue lacks.
104770
+ */
104771
+ function transitionIntentForTreatment(treatment) {
104772
+ const transition = boundaryOptionForTreatment(treatment)?.transitionAfter;
104773
+ return transition && isDecorativeTransitionIntent(transition) ? transition : null;
104774
+ }
104775
+ function stableTreatmentKey(treatment) {
104776
+ switch (treatment.kind) {
104777
+ case "none": return "none";
104778
+ case "soft-fade": return `${treatment.kind}:${treatment.height}:${treatment.curve}`;
104779
+ case "wave-edge": return [
104780
+ treatment.kind,
104781
+ treatment.height,
104782
+ treatment.amplitude,
104783
+ treatment.rhythm,
104784
+ treatment.symmetry
104785
+ ].join(":");
104786
+ case "angle-edge": return [
104787
+ treatment.kind,
104788
+ treatment.height,
104789
+ treatment.slope,
104790
+ treatment.direction
104791
+ ].join(":");
104792
+ case "legacy-unsupported": return `${treatment.kind}:${treatment.boundaryOptionId}`;
104793
+ }
104794
+ }
104795
+ function sectionTransitionFamilyForBoundaryOptionId(boundaryOptionId) {
104796
+ const variant = sectionTransitionVariantForBoundaryOptionId(boundaryOptionId);
104797
+ if (!variant) return null;
104798
+ return transitionFamilyById.get(variant.familyId) ?? null;
104799
+ }
104800
+ function defaultSectionTransitionVariantForFamily(familyId) {
104801
+ const family = transitionFamilyById.get(familyId);
104802
+ if (!family) throw new Error(`Unknown section transition family: ${familyId}`);
104803
+ return family.variants[0];
104804
+ }
104805
+ //#endregion
104806
+ //#region ../theme-core/src/site-styles/sectionBoundaryComposition.ts
104807
+ const backgroundToken = requireThemeColorToken$1("background");
104808
+ /**
104809
+ * Classifies a section from its final render content. `_sectionStyles`
104810
+ * already holds the effective background (Look default, page-design override
104811
+ * and block-authored media merged by `resolveSectionBackground`), so this only
104812
+ * reads it and never re-decides precedence. A missing or unparseable
104813
+ * background is the page background, as it is for the renderer.
104814
+ */
104815
+ function boundaryEndpointForSectionContent(content) {
104816
+ const background = parseCanonicalRuntimeBackground(content);
104817
+ if (background.kind === "none") return {
104818
+ kind: "paint",
104819
+ token: backgroundToken
104820
+ };
104821
+ const effective = background.background;
104822
+ switch (effective.type) {
104823
+ case "color": {
104824
+ if (effective.color === null || effective.color === void 0) return {
104825
+ kind: "paint",
104826
+ token: backgroundToken
104827
+ };
104828
+ const token = parseThemeColorToken(effective.color);
104829
+ return token ? {
104830
+ kind: "paint",
104831
+ token
104832
+ } : {
104833
+ kind: "unpainted",
104834
+ reason: "literal-color"
104835
+ };
104836
+ }
104837
+ case "gradient": return {
104838
+ kind: "unpainted",
104839
+ reason: "gradient"
104840
+ };
104841
+ case "image":
104842
+ case "video": return {
104843
+ kind: "unpainted",
104844
+ reason: "media"
104845
+ };
104846
+ default: return assertNever$7(effective, "Unhandled section background type");
104847
+ }
104848
+ }
104849
+ /**
104850
+ * Reads both facts from final render content in one place. `_frameStyles` is
104851
+ * the compiled frame; content without one (no page design, an SDK block)
104852
+ * draws a plain frame.
104853
+ */
104854
+ function sectionFactsForContent(content) {
104855
+ return {
104856
+ endpoint: boundaryEndpointForSectionContent(content),
104857
+ frame: frameKindForContent(content)
104858
+ };
104859
+ }
104860
+ function frameKindForContent(content) {
104861
+ const frameStyles = content._frameStyles;
104862
+ const kind = typeof frameStyles === "object" && frameStyles !== null && "kind" in frameStyles ? frameStyles.kind : null;
104863
+ return kind === "panel" || kind === "flush-panel" ? kind : "plain";
104864
+ }
104865
+ /**
104866
+ * The frame rule: a flush panel owns the edge it runs to, so no break is
104867
+ * drawn next to one. The single home for this rule; the generated
104868
+ * pipeline's budget and the site-style lab ask it with their plan frames.
104869
+ */
104870
+ function frameNotShownReason(previous, next) {
104871
+ return previous === "flush-panel" || next === "flush-panel" ? "flush-panel" : null;
104872
+ }
104873
+ /**
104874
+ * Decides whether and how a join draws. The frame rule wins before paint is
104875
+ * considered; two unpainted sides show nothing.
104876
+ */
104877
+ function composeBoundary(previous, next) {
104878
+ const frameReason = frameNotShownReason(previous.frame, next.frame);
104879
+ if (frameReason) return {
104880
+ kind: "not-shown",
104881
+ reason: frameReason
104882
+ };
104883
+ const from = previous.endpoint;
104884
+ const to = next.endpoint;
104885
+ if (from.kind === "paint") return {
104886
+ kind: "shown",
104887
+ composition: to.kind === "paint" ? {
104888
+ kind: "paint-to-paint",
104889
+ from: from.token,
104890
+ to: to.token
104891
+ } : {
104892
+ kind: "paint-to-unpainted",
104893
+ from: from.token
104894
+ }
104895
+ };
104896
+ return to.kind === "paint" ? {
104897
+ kind: "shown",
104898
+ composition: {
104899
+ kind: "unpainted-to-paint",
104900
+ to: to.token
104901
+ }
104902
+ } : {
104903
+ kind: "not-shown",
104904
+ reason: "both-unpainted"
104905
+ };
104906
+ }
104907
+ /**
104908
+ * Placement, reserves and paint for a shown boundary, derived from the
104909
+ * composition and the catalogue's metrics for the treatment.
104910
+ */
104911
+ function boundaryLayout(intent, composition) {
104912
+ const metrics = boundaryRuntimeMetricsForDecorativeTransition(intent);
104913
+ switch (composition.kind) {
104914
+ case "paint-to-paint": return {
104915
+ placement: "centred",
104916
+ reserves: {
104917
+ before: metrics.reserveCss,
104918
+ after: metrics.reserveCss
104919
+ },
104920
+ paint: {
104921
+ band: composition.from,
104922
+ shape: composition.to
104923
+ }
104924
+ };
104925
+ case "unpainted-to-paint": return {
104926
+ placement: "previous",
104927
+ reserves: {
104928
+ before: metrics.heightCss,
104929
+ after: null
104930
+ },
104931
+ paint: {
104932
+ band: null,
104933
+ shape: composition.to
104934
+ }
104935
+ };
104936
+ case "paint-to-unpainted": return {
104937
+ placement: "next",
104938
+ reserves: {
104939
+ before: null,
104940
+ after: metrics.heightCss
104941
+ },
104942
+ paint: {
104943
+ band: null,
104944
+ shape: composition.from
104945
+ }
104946
+ };
104947
+ default: return assertNever$7(composition, "Unhandled boundary composition");
104948
+ }
104949
+ }
104950
+ function requireThemeColorToken$1(value) {
104951
+ const token = parseThemeColorToken(value);
104952
+ if (!token) throw new Error(`Expected a theme color token. Received: ${value}`);
104953
+ return token;
104954
+ }
104955
+ //#endregion
104082
104956
  //#region ../theme-core/src/site-styles/compilationPlan.ts
104083
104957
  const defaultHeaderBrandPreferences = {
104084
104958
  brandDisplay: "auto",
@@ -104133,276 +105007,6 @@ function withHeaderBrandThemePreferences(theme, preferences) {
104133
105007
  }
104134
105008
  };
104135
105009
  }
104136
- //#endregion
104137
- //#region ../theme-core/src/site-styles/sectionTransitions.ts
104138
- function boundaryRuntimeMetrics(family, size, heightExpression) {
104139
- return {
104140
- family,
104141
- size,
104142
- heightCss: heightExpression.includes(" ") ? `calc(${heightExpression})` : heightExpression,
104143
- reserveCss: `calc((${heightExpression}) / 2)`
104144
- };
104145
- }
104146
- const sectionBoundaryRuntimeMetricsByTransition = {
104147
- none: null,
104148
- "soft-fade-short": boundaryRuntimeMetrics("gradient", "medium", "6rem + 2px"),
104149
- "soft-fade-long": boundaryRuntimeMetrics("gradient", "spacious", "10rem"),
104150
- "soft-fade-expansive": boundaryRuntimeMetrics("gradient", "expansive", "14rem"),
104151
- "wave-edge": boundaryRuntimeMetrics("wave", "medium", "6rem + 2px"),
104152
- "wave-edge-balanced": boundaryRuntimeMetrics("wave", "medium", "6rem + 2px"),
104153
- "wave-edge-gentle": boundaryRuntimeMetrics("wave", "compact", "4.5rem + 2px"),
104154
- "wave-edge-bold": boundaryRuntimeMetrics("wave", "large", "7rem + 2px"),
104155
- "wave-edge-drift": boundaryRuntimeMetrics("wave", "medium", "6rem + 2px"),
104156
- "angle-edge": boundaryRuntimeMetrics("angle", "medium", "6rem + 2px"),
104157
- "angle-edge-subtle": boundaryRuntimeMetrics("angle", "compact", "4.5rem + 2px"),
104158
- "angle-edge-steep": boundaryRuntimeMetrics("angle", "large", "7rem + 2px")
104159
- };
104160
- function boundaryRuntimeMetricsForTransition(transition) {
104161
- return sectionBoundaryRuntimeMetricsByTransition[transition];
104162
- }
104163
- const noneBoundaryOption = {
104164
- id: asBoundaryOptionId("break:none"),
104165
- label: "None",
104166
- transitionAfter: "none",
104167
- treatment: { kind: "none" }
104168
- };
104169
- const softBoundaryOption = {
104170
- id: asBoundaryOptionId("break:soft"),
104171
- label: "Soft",
104172
- transitionAfter: "soft-fade-short",
104173
- treatment: {
104174
- kind: "soft-fade",
104175
- height: "medium",
104176
- curve: "soft"
104177
- }
104178
- };
104179
- const spaciousBoundaryOption = {
104180
- id: asBoundaryOptionId("break:spacious"),
104181
- label: "Spacious",
104182
- transitionAfter: "soft-fade-long",
104183
- treatment: {
104184
- kind: "soft-fade",
104185
- height: "large",
104186
- curve: "spacious"
104187
- }
104188
- };
104189
- const expansiveBoundaryOption = {
104190
- id: asBoundaryOptionId("break:expansive"),
104191
- label: "Expansive",
104192
- transitionAfter: "soft-fade-expansive",
104193
- treatment: {
104194
- kind: "soft-fade",
104195
- height: "expansive",
104196
- curve: "expansive"
104197
- }
104198
- };
104199
- const waveBoundaryOption = {
104200
- id: asBoundaryOptionId("break:wave"),
104201
- label: "Classic wave",
104202
- transitionAfter: "wave-edge",
104203
- treatment: {
104204
- kind: "wave-edge",
104205
- height: "medium",
104206
- amplitude: "balanced",
104207
- rhythm: "double",
104208
- symmetry: "asymmetrical"
104209
- }
104210
- };
104211
- const balancedWaveBoundaryOption = {
104212
- id: asBoundaryOptionId("break:wave-balanced"),
104213
- label: "Balanced wave",
104214
- transitionAfter: "wave-edge-balanced",
104215
- treatment: {
104216
- kind: "wave-edge",
104217
- height: "medium",
104218
- amplitude: "balanced",
104219
- rhythm: "double",
104220
- symmetry: "symmetrical"
104221
- }
104222
- };
104223
- const gentleWaveBoundaryOption = {
104224
- id: asBoundaryOptionId("break:wave-gentle"),
104225
- label: "Gentle wave",
104226
- transitionAfter: "wave-edge-gentle",
104227
- treatment: {
104228
- kind: "wave-edge",
104229
- height: "compact",
104230
- amplitude: "subtle",
104231
- rhythm: "single",
104232
- symmetry: "symmetrical"
104233
- }
104234
- };
104235
- const boldWaveBoundaryOption = {
104236
- id: asBoundaryOptionId("break:wave-bold"),
104237
- label: "Bold wave",
104238
- transitionAfter: "wave-edge-bold",
104239
- treatment: {
104240
- kind: "wave-edge",
104241
- height: "large",
104242
- amplitude: "bold",
104243
- rhythm: "double",
104244
- symmetry: "symmetrical"
104245
- }
104246
- };
104247
- const driftWaveBoundaryOption = {
104248
- id: asBoundaryOptionId("break:wave-drift"),
104249
- label: "Drift wave",
104250
- transitionAfter: "wave-edge-drift",
104251
- treatment: {
104252
- kind: "wave-edge",
104253
- height: "medium",
104254
- amplitude: "balanced",
104255
- rhythm: "double",
104256
- symmetry: "drift"
104257
- }
104258
- };
104259
- const subtleAngleBoundaryOption = {
104260
- id: asBoundaryOptionId("break:angle-subtle"),
104261
- label: "Subtle angle",
104262
- transitionAfter: "angle-edge-subtle",
104263
- treatment: {
104264
- kind: "angle-edge",
104265
- height: "compact",
104266
- slope: "subtle",
104267
- direction: "forward"
104268
- }
104269
- };
104270
- const balancedAngleBoundaryOption = {
104271
- id: asBoundaryOptionId("break:angle-balanced"),
104272
- label: "Balanced angle",
104273
- transitionAfter: "angle-edge",
104274
- treatment: {
104275
- kind: "angle-edge",
104276
- height: "medium",
104277
- slope: "balanced",
104278
- direction: "forward"
104279
- }
104280
- };
104281
- const steepAngleBoundaryOption = {
104282
- id: asBoundaryOptionId("break:angle-steep"),
104283
- label: "Steep angle",
104284
- transitionAfter: "angle-edge-steep",
104285
- treatment: {
104286
- kind: "angle-edge",
104287
- height: "large",
104288
- slope: "steep",
104289
- direction: "forward"
104290
- }
104291
- };
104292
- const sectionTransitionFamilies = [
104293
- {
104294
- id: "none",
104295
- label: "None",
104296
- variants: [{
104297
- familyId: "none",
104298
- variantId: "none",
104299
- label: "None",
104300
- boundaryOption: noneBoundaryOption
104301
- }]
104302
- },
104303
- {
104304
- id: "gradient",
104305
- label: "Gradient",
104306
- variants: [
104307
- {
104308
- familyId: "gradient",
104309
- variantId: "soft",
104310
- label: "Soft",
104311
- boundaryOption: softBoundaryOption
104312
- },
104313
- {
104314
- familyId: "gradient",
104315
- variantId: "spacious",
104316
- label: "Spacious",
104317
- boundaryOption: spaciousBoundaryOption
104318
- },
104319
- {
104320
- familyId: "gradient",
104321
- variantId: "expansive",
104322
- label: "Expansive",
104323
- boundaryOption: expansiveBoundaryOption
104324
- }
104325
- ]
104326
- },
104327
- {
104328
- id: "wave",
104329
- label: "Wave",
104330
- variants: [
104331
- {
104332
- familyId: "wave",
104333
- variantId: "balanced",
104334
- label: "Balanced",
104335
- boundaryOption: balancedWaveBoundaryOption
104336
- },
104337
- {
104338
- familyId: "wave",
104339
- variantId: "classic",
104340
- label: "Classic",
104341
- boundaryOption: waveBoundaryOption
104342
- },
104343
- {
104344
- familyId: "wave",
104345
- variantId: "gentle",
104346
- label: "Gentle",
104347
- boundaryOption: gentleWaveBoundaryOption
104348
- },
104349
- {
104350
- familyId: "wave",
104351
- variantId: "bold",
104352
- label: "Bold",
104353
- boundaryOption: boldWaveBoundaryOption
104354
- },
104355
- {
104356
- familyId: "wave",
104357
- variantId: "drift",
104358
- label: "Drift",
104359
- boundaryOption: driftWaveBoundaryOption
104360
- }
104361
- ]
104362
- },
104363
- {
104364
- id: "angle",
104365
- label: "Angle",
104366
- variants: [
104367
- {
104368
- familyId: "angle",
104369
- variantId: "subtle",
104370
- label: "Subtle",
104371
- boundaryOption: subtleAngleBoundaryOption
104372
- },
104373
- {
104374
- familyId: "angle",
104375
- variantId: "balanced",
104376
- label: "Balanced",
104377
- boundaryOption: balancedAngleBoundaryOption
104378
- },
104379
- {
104380
- familyId: "angle",
104381
- variantId: "steep",
104382
- label: "Steep",
104383
- boundaryOption: steepAngleBoundaryOption
104384
- }
104385
- ]
104386
- }
104387
- ];
104388
- function sectionTransitionVariantForBoundaryOptionId(boundaryOptionId) {
104389
- if (!boundaryOptionId) return null;
104390
- for (const family of sectionTransitionFamilies) {
104391
- const variant = family.variants.find((candidate) => candidate.boundaryOption.id === boundaryOptionId);
104392
- if (variant) return variant;
104393
- }
104394
- return null;
104395
- }
104396
- function sectionTransitionFamilyForBoundaryOptionId(boundaryOptionId) {
104397
- const variant = sectionTransitionVariantForBoundaryOptionId(boundaryOptionId);
104398
- if (!variant) return null;
104399
- return sectionTransitionFamilies.find((family) => family.id === variant.familyId) ?? null;
104400
- }
104401
- function defaultSectionTransitionVariantForFamily(familyId) {
104402
- const family = sectionTransitionFamilies.find((candidate) => candidate.id === familyId);
104403
- if (!family) throw new Error(`Unknown section transition family: ${familyId}`);
104404
- return family.variants[0];
104405
- }
104406
105010
  asBoundaryOptionId("break:unknown");
104407
105011
  const pageDesignUserPinnedAppearanceChoiceSchema = z.object({
104408
105012
  kind: z.literal("user-pinned"),
@@ -104501,6 +105105,15 @@ z.object({
104501
105105
  blockLookPins: z.array(pageDesignBlockLookPinSchema),
104502
105106
  boundaryPins: z.array(pageDesignBoundaryPinSchema)
104503
105107
  }).strict().superRefine(validateUniquePageDesignPins);
105108
+ function autoDesignChoice() {
105109
+ return { kind: "auto" };
105110
+ }
105111
+ function userPinnedDesignChoice(value) {
105112
+ return {
105113
+ kind: "user-pinned",
105114
+ value
105115
+ };
105116
+ }
104504
105117
  function addDuplicateStringIssues(ctx, input) {
104505
105118
  const seen = /* @__PURE__ */ new Set();
104506
105119
  const duplicates = /* @__PURE__ */ new Set();
@@ -104529,14 +105142,11 @@ function validateUniquePageDesignPins(intent, ctx) {
104529
105142
  label: "block Look pin"
104530
105143
  });
104531
105144
  addDuplicateStringIssues(ctx, {
104532
- values: intent.boundaryPins.map((pin) => boundaryKey(pin.previousBlockId, pin.nextBlockId)),
105145
+ values: intent.boundaryPins.map((pin) => makeBoundaryId(pin.previousBlockId, pin.nextBlockId)),
104533
105146
  path: ["boundaryPins"],
104534
105147
  label: "boundary pin"
104535
105148
  });
104536
105149
  }
104537
- function boundaryKey(previousBlockId, nextBlockId) {
104538
- return makeBoundaryId(previousBlockId, nextBlockId);
104539
- }
104540
105150
  //#endregion
104541
105151
  //#region ../theme-core/src/site-styles/styleTags.ts
104542
105152
  /**
@@ -108815,6 +109425,200 @@ z.object({
108815
109425
  authority: styleAuthoritySchema
108816
109426
  }).strict();
108817
109427
  //#endregion
109428
+ //#region ../theme-core/src/site-styles/pageFlowPresets.ts
109429
+ /**
109430
+ * Must remain non-empty; downstream code indexes `[0]` as the documented default.
109431
+ */
109432
+ const pageFlowPresets = [
109433
+ {
109434
+ id: asPageFlowPresetId("flow:quiet"),
109435
+ label: "Quiet",
109436
+ boundaryStrategy: "quiet",
109437
+ defaultBoundaryOptionId: noneBoundaryOption.id
109438
+ },
109439
+ {
109440
+ id: asPageFlowPresetId("flow:balanced"),
109441
+ label: "Balanced",
109442
+ boundaryStrategy: "balanced",
109443
+ defaultBoundaryOptionId: softBoundaryOption.id
109444
+ },
109445
+ {
109446
+ id: asPageFlowPresetId("flow:expressive"),
109447
+ label: "Expressive",
109448
+ boundaryStrategy: "expressive",
109449
+ defaultBoundaryOptionId: spaciousBoundaryOption.id
109450
+ }
109451
+ ];
109452
+ /**
109453
+ * The break a join gets when nothing more specific applies. An unknown preset
109454
+ * id (a stale or policy-owned one) falls back to the first boundary option,
109455
+ * and `preset` is null so callers can tell the two apart.
109456
+ */
109457
+ function pageFlowDefaultBoundary(pageFlowPresetId) {
109458
+ const preset = pageFlowPresets.find((candidate) => candidate.id === pageFlowPresetId) ?? null;
109459
+ return {
109460
+ preset,
109461
+ boundaryOptionId: preset?.defaultBoundaryOptionId ?? boundaryOptions[0].id
109462
+ };
109463
+ }
109464
+ asPageFlowPresetId("generated-default");
109465
+ const southWestTwerkSchoolSiteStyleId = asSiteStyleId("site-style:south-west-twerk-school");
109466
+ const heroBrandScrimFixedFrameLookId = asAppearancePresetId("hero-brand-scrim-fixed-frame");
109467
+ const classicHeroLayoutVariantKey = asLayoutVariantKey("classic");
109468
+ const microHeroLayoutVariantKey = asLayoutVariantKey("micro");
109469
+ const waveBoundaryOptionId = asBoundaryOptionId("break:wave");
109470
+ const registeredSiteStyleBlockPolicies = [{
109471
+ siteStyleId: southWestTwerkSchoolSiteStyleId,
109472
+ kind: "locked-look-and-layouts",
109473
+ blockKind: "block.hero",
109474
+ lookId: heroBrandScrimFixedFrameLookId,
109475
+ allowedLayoutVariantKeys: [classicHeroLayoutVariantKey, microHeroLayoutVariantKey],
109476
+ defaultLayoutVariantKey: classicHeroLayoutVariantKey,
109477
+ defaultBoundaryAfter: {
109478
+ optionId: waveBoundaryOptionId,
109479
+ mode: "default-only"
109480
+ },
109481
+ customize: {
109482
+ hideDisallowedLooks: true,
109483
+ hideDisallowedLayouts: true
109484
+ }
109485
+ }];
109486
+ function deriveSiteStyleBlockPolicy(input) {
109487
+ return registeredSiteStyleBlockPolicies.find((policy) => policy.siteStyleId === input.siteStyleId && policy.blockKind === input.blockKind) ?? { kind: "open" };
109488
+ }
109489
+ //#endregion
109490
+ //#region ../theme-core/src/site-styles/joinTreatment.ts
109491
+ /** Narrows any join (a resolved intent included) to its public, intent-only form. */
109492
+ function toPageRuntimeJoin(join) {
109493
+ return {
109494
+ previousBlockId: join.previousBlockId,
109495
+ nextBlockId: join.nextBlockId,
109496
+ treatment: join.treatment
109497
+ };
109498
+ }
109499
+ /**
109500
+ * The single rule set for which treatment a join gets. Candidates arrive in
109501
+ * priority order; the first supported one wins.
109502
+ *
109503
+ * - `none` ends the search and yields no join.
109504
+ * - An unsupported candidate (a legacy option id, or a structured treatment the
109505
+ * catalogue lacks) falls through to the next one. When it was the user's
109506
+ * pin, the pin is reported unsupported and the existing
109507
+ * `pinned-boundary-option-unavailable` issue is raised.
109508
+ */
109509
+ function resolveJoinTreatment(input) {
109510
+ const boundaryId = makeBoundaryId(input.previousBlockId, input.nextBlockId);
109511
+ const selection = selectCandidate(input.candidates);
109512
+ const selectedOutcome = selection.kind === "selected" ? selection.outcome : { kind: "none" };
109513
+ const pin = pinStatus(input.candidates, selectedOutcome);
109514
+ const issues = pin.kind === "unsupported" ? [{
109515
+ kind: "pinned-boundary-option-unavailable",
109516
+ boundaryId,
109517
+ presetId: pin.optionId,
109518
+ feedback: "user-facing"
109519
+ }] : [];
109520
+ if (selection.kind === "exhausted") return {
109521
+ join: null,
109522
+ selectedSource: null,
109523
+ pin,
109524
+ issues
109525
+ };
109526
+ if (selection.outcome.kind === "none") return {
109527
+ join: null,
109528
+ selectedSource: selection.candidate.source,
109529
+ pin,
109530
+ issues
109531
+ };
109532
+ return {
109533
+ selectedSource: selection.candidate.source,
109534
+ join: {
109535
+ boundaryId,
109536
+ previousBlockId: input.previousBlockId,
109537
+ nextBlockId: input.nextBlockId,
109538
+ treatment: selection.outcome.treatment,
109539
+ source: selection.candidate.source
109540
+ },
109541
+ pin,
109542
+ issues
109543
+ };
109544
+ }
109545
+ function pinStatus(candidates, selectedOutcome) {
109546
+ const pinned = candidates.find((candidate) => candidate.source.kind === "user-pinned");
109547
+ if (!pinned) return { kind: "none" };
109548
+ const outcome = candidateOutcome(pinned.treatment);
109549
+ return outcome.kind === "unsupported" ? {
109550
+ kind: "unsupported",
109551
+ optionId: outcome.boundaryOptionId ?? fallbackOptionId(selectedOutcome)
109552
+ } : { kind: "supported" };
109553
+ }
109554
+ function selectCandidate(candidates) {
109555
+ for (const candidate of candidates) {
109556
+ const outcome = candidateOutcome(candidate.treatment);
109557
+ switch (outcome.kind) {
109558
+ case "decorative":
109559
+ case "none": return {
109560
+ kind: "selected",
109561
+ candidate,
109562
+ outcome
109563
+ };
109564
+ case "unsupported": continue;
109565
+ default: return assertNever$7(outcome, "Unhandled join candidate outcome");
109566
+ }
109567
+ }
109568
+ return { kind: "exhausted" };
109569
+ }
109570
+ function candidateOutcome(treatment) {
109571
+ if (treatment.kind === "none") return { kind: "none" };
109572
+ const intent = transitionIntentForTreatment(treatment);
109573
+ return intent ? {
109574
+ kind: "decorative",
109575
+ treatment: intent
109576
+ } : {
109577
+ kind: "unsupported",
109578
+ boundaryOptionId: boundaryOptionIdForTreatment(treatment)
109579
+ };
109580
+ }
109581
+ /**
109582
+ * Matches the editor's existing issue payload: the pin's own option id when
109583
+ * it has one, otherwise the option the join fell back to.
109584
+ */
109585
+ function fallbackOptionId(outcome) {
109586
+ switch (outcome.kind) {
109587
+ case "decorative": return boundaryOptionForTransitionIntent(outcome.treatment).id;
109588
+ case "none": return noneBoundaryOption.id;
109589
+ default: return assertNever$7(outcome, "Unhandled join fallback outcome");
109590
+ }
109591
+ }
109592
+ /**
109593
+ * The page-design defaults for a join, in priority order: the site style's
109594
+ * default break after the previous block, then the page-flow default, which
109595
+ * is always last.
109596
+ */
109597
+ function pageDesignDefaultJoinCandidates(input) {
109598
+ const policy = deriveSiteStyleBlockPolicy({
109599
+ siteStyleId: input.siteStyleId,
109600
+ blockKind: input.previousBlockKind
109601
+ });
109602
+ return [...policy.kind === "locked-look-and-layouts" && policy.defaultBoundaryAfter ? [{
109603
+ source: {
109604
+ kind: "site-style-default",
109605
+ siteStyleId: input.siteStyleId
109606
+ },
109607
+ treatment: boundaryTreatmentForBoundaryOptionId(policy.defaultBoundaryAfter.optionId)
109608
+ }] : [], pageFlowDefaultJoinCandidate(input.pageFlowPresetId)];
109609
+ }
109610
+ /** The last-resort page-design default, also the source of a fully unsupported join. */
109611
+ function pageFlowDefaultJoinCandidate(pageFlowPresetId) {
109612
+ const pageFlow = pageFlowDefaultBoundary(pageFlowPresetId);
109613
+ return {
109614
+ source: {
109615
+ kind: "page-flow-default",
109616
+ pageFlowPresetId: pageFlow.preset?.id ?? null
109617
+ },
109618
+ treatment: boundaryTreatmentForBoundaryOptionId(pageFlow.boundaryOptionId)
109619
+ };
109620
+ }
109621
+ //#endregion
108818
109622
  //#region ../theme-core/src/site-styles/imageSurfaceBackground.ts
108819
109623
  function withImageSurfaceMotion(background, motion) {
108820
109624
  return motion === "static" ? background : {
@@ -108923,13 +109727,15 @@ const compiledRuntimeStyleFieldNames = [
108923
109727
  "_contentCardStyles",
108924
109728
  "_heroLayoutStyles",
108925
109729
  "_cardStyles",
108926
- "_transitionStyles",
108927
109730
  "blockLook",
108928
- "blockComposition",
108929
- "styleGroup"
109731
+ "blockComposition"
108930
109732
  ];
109733
+ /**
109734
+ * Removes every compiler-owned field, plus a stale transition `styleGroup`.
109735
+ * An authored `styleGroup` is content and survives.
109736
+ */
108931
109737
  function stripCompiledRuntimeStyleFields(content) {
108932
- return Object.fromEntries(Object.entries(content).filter(([fieldName]) => !compiledRuntimeStyleFieldNames.includes(fieldName)));
109738
+ return Object.fromEntries(Object.entries(stripStaleTransitionStyleGroup(content)).filter(([fieldName]) => !compiledRuntimeStyleFieldNames.includes(fieldName)));
108933
109739
  }
108934
109740
  function surfaceResolutionContextFromPalette(palette) {
108935
109741
  return {
@@ -108948,7 +109754,7 @@ function defineComponentRuntimeRules(rules) {
108948
109754
  }
108949
109755
  /**
108950
109756
  * Default design rules translate semantic page-level choices into the current
108951
- * runtime's section background and transition style-group vocabulary.
109757
+ * runtime's section background vocabulary.
108952
109758
  */
108953
109759
  const defaultDesignRuntimeRules = defineDesignRuntimeRules({
108954
109760
  sectionSurfaces: {
@@ -108983,8 +109789,7 @@ const defaultDesignRuntimeRules = defineDesignRuntimeRules({
108983
109789
  color: "surface",
108984
109790
  opacity: .88
108985
109791
  }
108986
- },
108987
- transitionClassName: (transition, fromSurface, toSurface) => transition === "none" ? null : `site-style-transition-${transition}-from-${fromSurface}-to-${toSurface}`
109792
+ }
108988
109793
  });
108989
109794
  /**
108990
109795
  * Default component rules translate frame and item-surface semantics into the
@@ -109194,22 +109999,6 @@ function compileCardStyles(input) {
109194
109999
  if (!role) return void 0;
109195
110000
  return cardStylesOverrideSchema.parse({ role });
109196
110001
  }
109197
- function sectionAdjacencyForBlock(input) {
109198
- if (input.sectionAdjacency) return input.sectionAdjacency;
109199
- if (input.nextSectionSurface) return {
109200
- kind: "next-section",
109201
- surface: input.nextSectionSurface
109202
- };
109203
- return { kind: "page-end" };
109204
- }
109205
- function transitionTargetSurfaceForAdjacency(adjacency) {
109206
- switch (adjacency.kind) {
109207
- case "next-section":
109208
- case "terminal-surface": return adjacency.surface;
109209
- case "page-end": return null;
109210
- default: return assertNever$7(adjacency);
109211
- }
109212
- }
109213
110002
  function resolveEyebrowTreatmentForRuntime(block, rules) {
109214
110003
  return block.eyebrowTreatment ?? rules.defaultEyebrowTreatment ?? block.appliedEyebrowTreatment;
109215
110004
  }
@@ -109227,19 +110016,6 @@ function compileBlockRuntimeFields(input) {
109227
110016
  });
109228
110017
  const shouldSuppressContainerFrame = shouldMoveFrameToHeroContentCard(input.block);
109229
110018
  const cardStyles = compileCardStyles({ cardRole: input.block.appliedCardRole });
109230
- const transitionTargetSurface = transitionTargetSurfaceForAdjacency(sectionAdjacencyForBlock(input));
109231
- const transitionClassName = transitionTargetSurface ? designRules.transitionClassName(input.block.appliedTransitionAfter, visualSectionSurface, transitionTargetSurface) : null;
109232
- const transitionRuntime = transitionTargetSurface && transitionClassName ? compileTransitionRuntime({
109233
- treatment: input.block.appliedTransitionAfter,
109234
- from: {
109235
- kind: "surface",
109236
- surface: visualSectionSurface
109237
- },
109238
- to: {
109239
- kind: "surface",
109240
- surface: transitionTargetSurface
109241
- }
109242
- }) : null;
109243
110019
  return {
109244
110020
  blockId: input.block.blockId,
109245
110021
  visualSectionSurface,
@@ -109264,117 +110040,33 @@ function compileBlockRuntimeFields(input) {
109264
110040
  ...cardStyles ? { _cardStyles: cardStyles } : {},
109265
110041
  ...heroRuntimeFields,
109266
110042
  ...input.block.appearancePresetId ? { blockLook: input.block.appearancePresetId } : {},
109267
- ...input.block.appliedComposition ? { blockComposition: input.block.appliedComposition } : {},
109268
- ...transitionRuntime ? { _transitionStyles: transitionRuntime } : {},
109269
- ...transitionClassName ? { styleGroup: transitionClassName } : {}
109270
- }
109271
- };
109272
- }
109273
- function compileTransitionRuntime(input) {
109274
- return {
109275
- treatment: input.treatment,
109276
- from: input.from,
109277
- to: input.to,
109278
- cssVars: {
109279
- from: input.from.kind === "surface" && input.from.surface === "hero-image" ? "transparent" : transitionEndpointCssValue(input.from),
109280
- to: transitionEndpointCssValue(input.to)
110043
+ ...input.block.appliedComposition ? { blockComposition: input.block.appliedComposition } : {}
109281
110044
  }
109282
110045
  };
109283
110046
  }
109284
- function compileBoundaryRuntime(input) {
109285
- const styleGroup = (input.rules ?? defaultDesignRuntimeRules).transitionClassName(input.treatment, input.fromSurface, input.toSurface);
109286
- if (!styleGroup) return null;
109287
- const metrics = boundaryRuntimeMetricsForTransition(input.treatment);
109288
- if (!metrics) return null;
109289
- const transition = compileTransitionRuntime({
109290
- treatment: input.treatment,
109291
- from: {
109292
- kind: "surface",
109293
- surface: input.fromSurface
109294
- },
109295
- to: {
109296
- kind: "surface",
109297
- surface: input.toSurface
109298
- }
109299
- });
109300
- return {
109301
- boundaryId: input.boundaryId,
109302
- previousBlockId: input.previousBlockId,
109303
- nextBlockId: input.nextBlockId,
109304
- treatment: transition.treatment,
109305
- metrics,
109306
- from: transition.from,
109307
- to: transition.to,
109308
- cssVars: transition.cssVars,
109309
- styleGroup
109310
- };
109311
- }
109312
- function transitionEndpointCssValue(endpoint) {
109313
- switch (endpoint.kind) {
109314
- case "color-token": return themeColorTokenCssValue(endpoint.token);
109315
- case "surface": return themeColorTokenCssValue(tokenForTransitionSurface(endpoint.surface));
109316
- default: return assertNever$7(endpoint);
109317
- }
109318
- }
109319
- function tokenForTransitionSurface(surface) {
109320
- switch (surface) {
109321
- case "base":
109322
- case "hero-image": return "background";
109323
- case "muted-band": return "muted";
109324
- case "accent-band": return "accent";
109325
- case "brand-depth-band": return "primary-800";
109326
- default: return assertNever$7(surface);
109327
- }
109328
- }
109329
- function themeColorTokenCssValue(token) {
109330
- return `rgb(var(--tb-${token}))`;
109331
- }
109332
- /** Compiles a resolved page plan into legacy runtime fields for each resolved block. */
110047
+ /**
110048
+ * Compiles a resolved page plan into legacy runtime fields for each resolved
110049
+ * block, plus the public runtime joins between them.
110050
+ *
110051
+ * Joins come from `resolveJoinTreatment`; plans no longer carry a treatment.
110052
+ * Only joins whose two blocks were compiled are kept, and they leave as intent
110053
+ * only: the blocks renderer composes each one from final content after
110054
+ * omission, including whether it draws at all (issue 1422).
110055
+ */
109333
110056
  function compilePageRuntimeFields(input) {
109334
- const visualSurfaces = input.resolved.blocks.map(resolveVisualSectionSurfaceForBlock);
109335
- const terminalSectionAdjacency = input.terminalSectionAdjacency ?? { kind: "page-end" };
109336
- const designRules = input.designRules ?? defaultDesignRuntimeRules;
110057
+ const blocks = input.resolved.blocks.map((block) => compileBlockRuntimeFields({
110058
+ block,
110059
+ surfaceResolutionContext: input.surfaceResolutionContext,
110060
+ designRules: input.designRules,
110061
+ componentRules: input.componentRules,
110062
+ sectionSpacing: input.sectionSpacing
110063
+ }));
110064
+ const compiledIds = new Set(blocks.map((block) => block.blockId));
109337
110065
  return {
109338
- blocks: input.resolved.blocks.map((block, index) => {
109339
- const nextSurface = visualSurfaces[index + 1];
109340
- const compiled = compileBlockRuntimeFields({
109341
- block,
109342
- surfaceResolutionContext: input.surfaceResolutionContext,
109343
- designRules: input.designRules,
109344
- componentRules: input.componentRules,
109345
- sectionSpacing: input.sectionSpacing,
109346
- sectionAdjacency: nextSurface ? {
109347
- kind: "next-section",
109348
- surface: nextSurface
109349
- } : terminalSectionAdjacency
109350
- });
109351
- return {
109352
- ...compiled,
109353
- fields: stripPageLevelTransitionFields(compiled.fields)
109354
- };
109355
- }),
109356
- boundaries: input.resolved.blocks.flatMap((block, index) => {
109357
- const nextBlock = input.resolved.blocks[index + 1];
109358
- const fromSurface = visualSurfaces[index];
109359
- const toSurface = visualSurfaces[index + 1];
109360
- if (!nextBlock || !fromSurface || !toSurface) return [];
109361
- const boundary = compileBoundaryRuntime({
109362
- boundaryId: `boundary:${block.blockId}:${nextBlock.blockId}`,
109363
- previousBlockId: block.blockId,
109364
- nextBlockId: nextBlock.blockId,
109365
- treatment: block.appliedTransitionAfter,
109366
- fromSurface,
109367
- toSurface,
109368
- rules: designRules
109369
- });
109370
- return boundary ? [boundary] : [];
109371
- })
110066
+ blocks,
110067
+ joins: input.joins.filter((join) => compiledIds.has(join.previousBlockId) && compiledIds.has(join.nextBlockId)).map(toPageRuntimeJoin)
109372
110068
  };
109373
110069
  }
109374
- function stripPageLevelTransitionFields(fields) {
109375
- const { _transitionStyles, styleGroup, ...rest } = fields;
109376
- return rest;
109377
- }
109378
110070
  const paletteByRecipe = {
109379
110071
  "brand-led": [
109380
110072
  {
@@ -111437,12 +112129,97 @@ function componentRuntimeRulesForTheme(theme) {
111437
112129
  if (!siteStyleId) return defaultComponentRuntimeRules;
111438
112130
  return componentRuntimeRulesForSiteStyle(curatedSiteStylesById.get(asSiteStyleId(siteStyleId)) ?? null);
111439
112131
  }
111440
- asPageFlowPresetId("generated-default");
111441
- asSiteStyleId("site-style:south-west-twerk-school");
111442
- asAppearancePresetId("hero-brand-scrim-fixed-frame");
111443
- asLayoutVariantKey("classic");
111444
- asLayoutVariantKey("micro");
111445
- asBoundaryOptionId("break:wave");
112132
+ //#endregion
112133
+ //#region ../theme-core/src/site-styles/pageDesignEditorBoundaries.ts
112134
+ function buildBoundaryReadModel(input) {
112135
+ const previousBlockId = input.previousBlock.blockId;
112136
+ const nextBlockId = input.nextBlock.blockId;
112137
+ const defaults = pageDesignDefaultJoinCandidates({
112138
+ siteStyleId: input.siteStyleId,
112139
+ pageFlowPresetId: input.pageFlowPresetId,
112140
+ previousBlockKind: input.previousBlock.blockKind
112141
+ });
112142
+ const projection = input.pin.kind === "user-pinned" ? pinnedBreakChoice(resolveJoinTreatment({
112143
+ candidates: [{
112144
+ source: { kind: "user-pinned" },
112145
+ treatment: input.pin.value
112146
+ }, ...defaults],
112147
+ previousBlockId,
112148
+ nextBlockId
112149
+ })) : autoBreakChoice(resolveJoinTreatment({
112150
+ candidates: defaults,
112151
+ previousBlockId,
112152
+ nextBlockId
112153
+ }), input.pageFlowPresetId);
112154
+ return {
112155
+ boundary: {
112156
+ boundaryId: makeBoundaryId(previousBlockId, nextBlockId),
112157
+ previousBlockId,
112158
+ nextBlockId,
112159
+ breakChoice: projection.breakChoice,
112160
+ availableBreaks: boundaryOptions
112161
+ },
112162
+ issues: projection.issues
112163
+ };
112164
+ }
112165
+ function autoBreakChoice(resolution, pageFlowPresetId) {
112166
+ return {
112167
+ breakChoice: {
112168
+ kind: "auto",
112169
+ source: resolution.selectedSource ?? pageFlowDefaultJoinCandidate(pageFlowPresetId).source,
112170
+ effectiveValue: effectiveBreak(resolution)
112171
+ },
112172
+ issues: resolution.issues
112173
+ };
112174
+ }
112175
+ function pinnedBreakChoice(resolution) {
112176
+ const effectiveValue = effectiveBreak(resolution);
112177
+ switch (resolution.pin.kind) {
112178
+ case "unsupported": return {
112179
+ breakChoice: {
112180
+ kind: "user-pinned",
112181
+ value: resolution.pin.optionId,
112182
+ status: "unsupported",
112183
+ effectiveValue
112184
+ },
112185
+ issues: resolution.issues
112186
+ };
112187
+ case "supported":
112188
+ case "none": return {
112189
+ breakChoice: {
112190
+ kind: "user-pinned",
112191
+ value: effectiveValue ?? noneBoundaryOption.id,
112192
+ status: "supported",
112193
+ effectiveValue
112194
+ },
112195
+ issues: resolution.issues
112196
+ };
112197
+ default: return assertNever$7(resolution.pin, "Unhandled join pin status");
112198
+ }
112199
+ }
112200
+ /** The effective break as the option the editor shows; null when nothing was usable. */
112201
+ function effectiveBreak(resolution) {
112202
+ if (resolution.join) return boundaryOptionForTransitionIntent(resolution.join.treatment).id;
112203
+ return resolution.selectedSource ? noneBoundaryOption.id : null;
112204
+ }
112205
+ /**
112206
+ * Re-resolves one boundary of an editor model for a changed break choice,
112207
+ * through the same rules the model was built with. The preview uses it for
112208
+ * optimistic updates, so a reset lands on whichever default the rules pick
112209
+ * (site style or Page Flow). Null when either block is not in the model.
112210
+ */
112211
+ function resolvePageDesignEditorBoundaryForChoice(input) {
112212
+ const previousBlock = input.editorModel.blocks.find((block) => block.blockId === input.previousBlockId);
112213
+ const nextBlock = input.editorModel.blocks.find((block) => block.blockId === input.nextBlockId);
112214
+ if (!previousBlock || !nextBlock) return null;
112215
+ return buildBoundaryReadModel({
112216
+ siteStyleId: input.editorModel.siteStyleId,
112217
+ pageFlowPresetId: input.editorModel.pageFlow.value,
112218
+ pin: input.choice,
112219
+ previousBlock,
112220
+ nextBlock
112221
+ }).boundary;
112222
+ }
111446
112223
  //#endregion
111447
112224
  //#region ../theme-core/src/site-styles/pageDesignEditorModel.ts
111448
112225
  function autoEditorChoice(effectiveValue) {
@@ -111466,7 +112243,6 @@ function userPinnedEditorChoice(value, input = {}) {
111466
112243
  * Look/Layout semantics support them.
111467
112244
  */
111468
112245
  const normalEditorCuratedPromotedFieldPaths = ["_sectionStyles.background.image"];
111469
- asPageFlowPresetId("flow:quiet"), noneBoundaryOption.id, asPageFlowPresetId("flow:balanced"), softBoundaryOption.id, asPageFlowPresetId("flow:expressive"), spaciousBoundaryOption.id;
111470
112246
  function availableLooksForLayout(looksByLayout, layoutVariantKey) {
111471
112247
  if (!layoutVariantKey) return [];
111472
112248
  return looksByLayout.find((entry) => entry.layoutVariantKey === layoutVariantKey)?.availableLooks ?? [];
@@ -111587,7 +112363,6 @@ const repairActionsByIssueKind = {
111587
112363
  "image-surface-treatment-without-image-surface": ["fallback-image-surface-treatment"],
111588
112364
  "image-surface-motion-without-image-surface": ["fallback-image-surface-motion"],
111589
112365
  "hero-legibility-overridden-by-image-surface-treatment": ["remove-hero-legibility"],
111590
- "removed-transition-for-flush-frame": ["remove-transition"],
111591
112366
  "hero-image-without-legibility": ["fallback-hero-legibility"]
111592
112367
  };
111593
112368
  function repairActionsForIssue(issueKind) {
@@ -111627,13 +112402,9 @@ function supportsBlockComposition(capability, composition) {
111627
112402
  default: return assertNever$7(composition);
111628
112403
  }
111629
112404
  }
111630
- function isDecorativeTransition(transition) {
111631
- return transition !== "none";
111632
- }
111633
112405
  const blockDesignFallback = {
111634
112406
  sectionSurface: "base",
111635
112407
  contentFrame: plainFrame,
111636
- transitionAfter: "none",
111637
112408
  sectionHeight: "default",
111638
112409
  heroLegibility: "none",
111639
112410
  imageSurfaceTreatment: { kind: "none" },
@@ -111649,7 +112420,6 @@ const appliedBlockDesignFallback = {
111649
112420
  appliedSectionSurface: blockDesignFallback.sectionSurface,
111650
112421
  appliedContentFrame: blockDesignFallback.contentFrame,
111651
112422
  appliedCardRole: null,
111652
- appliedTransitionAfter: blockDesignFallback.transitionAfter,
111653
112423
  appliedSectionHeight: blockDesignFallback.sectionHeight,
111654
112424
  appliedHeroLegibility: blockDesignFallback.heroLegibility,
111655
112425
  appliedImageSurfaceTreatment: blockDesignFallback.imageSurfaceTreatment,
@@ -111675,7 +112445,6 @@ function blockDesignRequest(block) {
111675
112445
  sectionSurface: block.sectionSurface,
111676
112446
  contentFrame: block.contentFrame,
111677
112447
  ...block.cardRole ? { cardRole: block.cardRole } : {},
111678
- transitionAfter: block.transitionAfter,
111679
112448
  sectionHeight: block.sectionHeight,
111680
112449
  heroLegibility: block.heroLegibility,
111681
112450
  imageSurfaceTreatment: block.imageSurfaceTreatment,
@@ -111689,304 +112458,283 @@ function blockDesignRequest(block) {
111689
112458
  }
111690
112459
  function resolveBlockDesignPlan(input) {
111691
112460
  const issues = [];
111692
- const blocks = input.blocks.map((block) => {
111693
- const capability = input.capabilities[block.blockKind];
111694
- let appliedSectionSurface = block.sectionSurface;
111695
- let appliedContentFrame = block.contentFrame;
111696
- const appliedCardRole = appliedCardRoleFor(block, capability);
111697
- let appliedTransitionAfter = block.transitionAfter;
111698
- let appliedSectionHeight = block.sectionHeight ?? "default";
111699
- let appliedHeroLegibility = block.heroLegibility ?? "none";
111700
- let appliedImageSurfaceTreatment = block.imageSurfaceTreatment ?? { kind: "none" };
111701
- let appliedImageSurfaceMotion = block.imageSurfaceMotion ?? "static";
111702
- let appliedTileTreatment = block.tileTreatment ?? "default";
111703
- let appliedEventListTreatment = block.eventListTreatment ?? "plain";
111704
- let appliedEyebrowTreatment = block.eyebrowTreatment ?? "plain";
111705
- let appliedAccordionIconTreatment = block.accordionIconTreatment ?? null;
111706
- let appliedComposition = block.composition ?? null;
111707
- if (!capability) {
111708
- issues.push({
111709
- kind: "missing-block-design-capability",
111710
- blockId: block.blockId,
111711
- blockKind: block.blockKind,
111712
- purpose: block.purpose,
111713
- requested: blockDesignRequest(block),
111714
- fallback: blockDesignFallback,
111715
- policy: input.policy,
111716
- severity: designIssueSeverityForPolicy(input.policy),
111717
- allowedRepairActions: repairActionsForIssue("missing-block-design-capability")
111718
- });
111719
- return {
111720
- ...block,
111721
- ...appliedBlockDesignFallback
111722
- };
111723
- }
111724
- if (capability.kind === "not-designed") {
111725
- issues.push({
111726
- kind: "unsupported-block-design-capability",
111727
- blockId: block.blockId,
111728
- blockKind: block.blockKind,
111729
- purpose: block.purpose,
111730
- reason: capability.reason,
111731
- requested: blockDesignRequest(block),
111732
- fallback: blockDesignFallback,
111733
- policy: input.policy,
111734
- severity: designIssueSeverityForPolicy(input.policy),
111735
- allowedRepairActions: repairActionsForIssue("unsupported-block-design-capability")
111736
- });
111737
- return {
111738
- ...block,
111739
- ...appliedBlockDesignFallback
111740
- };
111741
- }
111742
- if (capability.sectionSurfaces === "unsupported") {
111743
- appliedSectionSurface = "base";
111744
- issues.push({
111745
- kind: "unsupported-section-surface",
111746
- blockId: block.blockId,
111747
- blockKind: block.blockKind,
111748
- purpose: block.purpose,
111749
- requested: block.sectionSurface,
111750
- fallback: appliedSectionSurface,
111751
- policy: input.policy,
111752
- severity: designIssueSeverityForPolicy(input.policy),
111753
- allowedRepairActions: repairActionsForIssue("unsupported-section-surface")
111754
- });
111755
- }
111756
- if (!supportsFrame(capability, block.contentFrame.kind)) {
111757
- appliedContentFrame = plainFrame;
111758
- issues.push({
111759
- kind: "unsupported-content-frame",
111760
- blockId: block.blockId,
111761
- blockKind: block.blockKind,
111762
- purpose: block.purpose,
111763
- requested: block.contentFrame,
111764
- fallback: appliedContentFrame,
111765
- policy: input.policy,
111766
- severity: designIssueSeverityForPolicy(input.policy),
111767
- allowedRepairActions: repairActionsForIssue("unsupported-content-frame")
111768
- });
111769
- }
111770
- const requestedCardRole = requestedCardRoleOf(block);
111771
- if (requestedCardRole && capability.cardRoles === "unsupported") issues.push({
111772
- kind: "unsupported-card-role",
111773
- blockId: block.blockId,
111774
- blockKind: block.blockKind,
111775
- purpose: block.purpose,
111776
- requested: requestedCardRole,
111777
- fallback: "none",
111778
- policy: input.policy,
111779
- severity: designIssueSeverityForPolicy(input.policy),
111780
- allowedRepairActions: repairActionsForIssue("unsupported-card-role")
111781
- });
111782
- if (!supportsSectionHeight(capability, appliedSectionHeight)) {
111783
- appliedSectionHeight = "default";
111784
- issues.push({
111785
- kind: "unsupported-section-height",
111786
- blockId: block.blockId,
111787
- blockKind: block.blockKind,
111788
- purpose: block.purpose,
111789
- requested: block.sectionHeight ?? "default",
111790
- fallback: appliedSectionHeight,
111791
- policy: input.policy,
111792
- severity: designIssueSeverityForPolicy(input.policy),
111793
- allowedRepairActions: repairActionsForIssue("unsupported-section-height")
111794
- });
111795
- }
111796
- if (!supportsImageSurfaceTreatment(capability, appliedImageSurfaceTreatment)) {
111797
- appliedImageSurfaceTreatment = { kind: "none" };
111798
- issues.push({
111799
- kind: "unsupported-image-surface-treatment",
111800
- blockId: block.blockId,
111801
- blockKind: block.blockKind,
111802
- purpose: block.purpose,
111803
- requested: block.imageSurfaceTreatment ?? { kind: "none" },
111804
- fallback: appliedImageSurfaceTreatment,
111805
- policy: input.policy,
111806
- severity: designIssueSeverityForPolicy(input.policy),
111807
- allowedRepairActions: repairActionsForIssue("unsupported-image-surface-treatment")
111808
- });
111809
- }
111810
- if (!supportsImageSurfaceMotion(capability, appliedImageSurfaceMotion)) {
111811
- appliedImageSurfaceMotion = "static";
111812
- issues.push({
111813
- kind: "unsupported-image-surface-motion",
111814
- blockId: block.blockId,
111815
- blockKind: block.blockKind,
111816
- purpose: block.purpose,
111817
- requested: block.imageSurfaceMotion ?? "static",
111818
- fallback: appliedImageSurfaceMotion,
111819
- policy: input.policy,
111820
- severity: designIssueSeverityForPolicy(input.policy),
111821
- allowedRepairActions: repairActionsForIssue("unsupported-image-surface-motion")
111822
- });
111823
- }
111824
- if (!supportsTileTreatment(capability, appliedTileTreatment)) {
111825
- appliedTileTreatment = "default";
111826
- issues.push({
111827
- kind: "unsupported-tile-treatment",
111828
- blockId: block.blockId,
111829
- blockKind: block.blockKind,
111830
- purpose: block.purpose,
111831
- requested: block.tileTreatment ?? "default",
111832
- fallback: appliedTileTreatment,
111833
- policy: input.policy,
111834
- severity: designIssueSeverityForPolicy(input.policy),
111835
- allowedRepairActions: repairActionsForIssue("unsupported-tile-treatment")
111836
- });
111837
- }
111838
- if (!supportsEventListTreatment(capability, appliedEventListTreatment)) {
111839
- appliedEventListTreatment = "plain";
111840
- issues.push({
111841
- kind: "unsupported-event-list-treatment",
111842
- blockId: block.blockId,
111843
- blockKind: block.blockKind,
111844
- purpose: block.purpose,
111845
- requested: block.eventListTreatment ?? "plain",
111846
- fallback: appliedEventListTreatment,
111847
- policy: input.policy,
111848
- severity: designIssueSeverityForPolicy(input.policy),
111849
- allowedRepairActions: repairActionsForIssue("unsupported-event-list-treatment")
111850
- });
111851
- }
111852
- if (!supportsEyebrowTreatment(capability, appliedEyebrowTreatment)) {
111853
- appliedEyebrowTreatment = "plain";
111854
- issues.push({
111855
- kind: "unsupported-eyebrow-treatment",
111856
- blockId: block.blockId,
111857
- blockKind: block.blockKind,
111858
- purpose: block.purpose,
111859
- requested: block.eyebrowTreatment ?? "plain",
111860
- fallback: appliedEyebrowTreatment,
111861
- policy: input.policy,
111862
- severity: designIssueSeverityForPolicy(input.policy),
111863
- allowedRepairActions: repairActionsForIssue("unsupported-eyebrow-treatment")
111864
- });
111865
- }
111866
- if (appliedAccordionIconTreatment && !supportsAccordionIconTreatment(capability, appliedAccordionIconTreatment)) {
111867
- const requested = appliedAccordionIconTreatment;
111868
- appliedAccordionIconTreatment = null;
111869
- issues.push({
111870
- kind: "unsupported-accordion-icon-treatment",
111871
- blockId: block.blockId,
111872
- blockKind: block.blockKind,
111873
- purpose: block.purpose,
111874
- requested,
111875
- fallback: appliedAccordionIconTreatment,
111876
- policy: input.policy,
111877
- severity: designIssueSeverityForPolicy(input.policy),
111878
- allowedRepairActions: repairActionsForIssue("unsupported-accordion-icon-treatment")
111879
- });
111880
- }
111881
- if (appliedComposition && !supportsBlockComposition(capability, appliedComposition)) {
111882
- const requested = appliedComposition;
111883
- appliedComposition = null;
111884
- issues.push({
111885
- kind: "unsupported-block-composition",
111886
- blockId: block.blockId,
111887
- blockKind: block.blockKind,
111888
- purpose: block.purpose,
111889
- requested,
111890
- fallback: null,
111891
- policy: input.policy,
111892
- severity: designIssueSeverityForPolicy(input.policy),
111893
- allowedRepairActions: repairActionsForIssue("unsupported-block-composition")
111894
- });
111895
- }
111896
- if (appliedSectionSurface !== "hero-image" && appliedImageSurfaceTreatment.kind !== "none") {
111897
- const requested = appliedImageSurfaceTreatment;
111898
- appliedImageSurfaceTreatment = { kind: "none" };
111899
- issues.push({
111900
- kind: "image-surface-treatment-without-image-surface",
111901
- blockId: block.blockId,
111902
- blockKind: block.blockKind,
111903
- purpose: block.purpose,
111904
- requested,
111905
- fallback: appliedImageSurfaceTreatment,
111906
- policy: input.policy,
111907
- severity: designIssueSeverityForPolicy(input.policy),
111908
- allowedRepairActions: repairActionsForIssue("image-surface-treatment-without-image-surface")
111909
- });
111910
- }
111911
- if (appliedSectionSurface !== "hero-image" && appliedImageSurfaceMotion !== "static") {
111912
- const requested = appliedImageSurfaceMotion;
111913
- appliedImageSurfaceMotion = "static";
111914
- issues.push({
111915
- kind: "image-surface-motion-without-image-surface",
111916
- blockId: block.blockId,
111917
- blockKind: block.blockKind,
111918
- purpose: block.purpose,
111919
- requested,
111920
- fallback: appliedImageSurfaceMotion,
111921
- policy: input.policy,
111922
- severity: designIssueSeverityForPolicy(input.policy),
111923
- allowedRepairActions: repairActionsForIssue("image-surface-motion-without-image-surface")
111924
- });
111925
- }
111926
- if (appliedSectionSurface === "hero-image" && appliedImageSurfaceTreatment.kind !== "none" && appliedHeroLegibility !== "none") {
111927
- const requested = appliedHeroLegibility;
111928
- appliedHeroLegibility = "none";
111929
- issues.push({
111930
- kind: "hero-legibility-overridden-by-image-surface-treatment",
111931
- blockId: block.blockId,
111932
- blockKind: block.blockKind,
111933
- purpose: block.purpose,
111934
- requested,
111935
- fallback: appliedHeroLegibility,
111936
- policy: input.policy,
111937
- severity: designIssueSeverityForPolicy(input.policy),
111938
- allowedRepairActions: repairActionsForIssue("hero-legibility-overridden-by-image-surface-treatment")
111939
- });
111940
- }
111941
- if (appliedSectionSurface === "hero-image" && appliedImageSurfaceTreatment.kind === "none" && (block.heroLegibility === void 0 || appliedHeroLegibility === "none")) {
111942
- appliedHeroLegibility = "scrim-gradient";
111943
- issues.push({
111944
- kind: "hero-image-without-legibility",
111945
- blockId: block.blockId,
111946
- blockKind: block.blockKind,
111947
- purpose: block.purpose,
111948
- requested: block.heroLegibility,
111949
- fallback: appliedHeroLegibility,
111950
- policy: input.policy,
111951
- severity: designIssueSeverityForPolicy(input.policy),
111952
- allowedRepairActions: repairActionsForIssue("hero-image-without-legibility")
111953
- });
111954
- }
111955
- return {
111956
- ...block,
111957
- appliedSectionSurface,
111958
- appliedContentFrame,
111959
- appliedCardRole,
111960
- appliedTransitionAfter,
111961
- appliedSectionHeight,
111962
- appliedHeroLegibility,
111963
- appliedImageSurfaceTreatment,
111964
- appliedImageSurfaceMotion,
111965
- appliedTileTreatment,
111966
- appliedEventListTreatment,
111967
- appliedEyebrowTreatment,
111968
- appliedAccordionIconTreatment,
111969
- appliedComposition
111970
- };
111971
- });
111972
- return {
111973
- blocks: blocks.map((block, index) => {
111974
- const nextBlock = blocks[index + 1];
111975
- if (!nextBlock || !isDecorativeTransition(block.appliedTransitionAfter) || block.appliedContentFrame.kind !== "flush-panel" && nextBlock.appliedContentFrame.kind !== "flush-panel") return block;
111976
- issues.push({
111977
- kind: "removed-transition-for-flush-frame",
112461
+ return {
112462
+ blocks: input.blocks.map((block) => {
112463
+ const capability = input.capabilities[block.blockKind];
112464
+ let appliedSectionSurface = block.sectionSurface;
112465
+ let appliedContentFrame = block.contentFrame;
112466
+ const appliedCardRole = appliedCardRoleFor(block, capability);
112467
+ let appliedSectionHeight = block.sectionHeight ?? "default";
112468
+ let appliedHeroLegibility = block.heroLegibility ?? "none";
112469
+ let appliedImageSurfaceTreatment = block.imageSurfaceTreatment ?? { kind: "none" };
112470
+ let appliedImageSurfaceMotion = block.imageSurfaceMotion ?? "static";
112471
+ let appliedTileTreatment = block.tileTreatment ?? "default";
112472
+ let appliedEventListTreatment = block.eventListTreatment ?? "plain";
112473
+ let appliedEyebrowTreatment = block.eyebrowTreatment ?? "plain";
112474
+ let appliedAccordionIconTreatment = block.accordionIconTreatment ?? null;
112475
+ let appliedComposition = block.composition ?? null;
112476
+ if (!capability) {
112477
+ issues.push({
112478
+ kind: "missing-block-design-capability",
112479
+ blockId: block.blockId,
112480
+ blockKind: block.blockKind,
112481
+ purpose: block.purpose,
112482
+ requested: blockDesignRequest(block),
112483
+ fallback: blockDesignFallback,
112484
+ policy: input.policy,
112485
+ severity: designIssueSeverityForPolicy(input.policy),
112486
+ allowedRepairActions: repairActionsForIssue("missing-block-design-capability")
112487
+ });
112488
+ return {
112489
+ ...block,
112490
+ ...appliedBlockDesignFallback
112491
+ };
112492
+ }
112493
+ if (capability.kind === "not-designed") {
112494
+ issues.push({
112495
+ kind: "unsupported-block-design-capability",
112496
+ blockId: block.blockId,
112497
+ blockKind: block.blockKind,
112498
+ purpose: block.purpose,
112499
+ reason: capability.reason,
112500
+ requested: blockDesignRequest(block),
112501
+ fallback: blockDesignFallback,
112502
+ policy: input.policy,
112503
+ severity: designIssueSeverityForPolicy(input.policy),
112504
+ allowedRepairActions: repairActionsForIssue("unsupported-block-design-capability")
112505
+ });
112506
+ return {
112507
+ ...block,
112508
+ ...appliedBlockDesignFallback
112509
+ };
112510
+ }
112511
+ if (capability.sectionSurfaces === "unsupported") {
112512
+ appliedSectionSurface = "base";
112513
+ issues.push({
112514
+ kind: "unsupported-section-surface",
112515
+ blockId: block.blockId,
112516
+ blockKind: block.blockKind,
112517
+ purpose: block.purpose,
112518
+ requested: block.sectionSurface,
112519
+ fallback: appliedSectionSurface,
112520
+ policy: input.policy,
112521
+ severity: designIssueSeverityForPolicy(input.policy),
112522
+ allowedRepairActions: repairActionsForIssue("unsupported-section-surface")
112523
+ });
112524
+ }
112525
+ if (!supportsFrame(capability, block.contentFrame.kind)) {
112526
+ appliedContentFrame = plainFrame;
112527
+ issues.push({
112528
+ kind: "unsupported-content-frame",
112529
+ blockId: block.blockId,
112530
+ blockKind: block.blockKind,
112531
+ purpose: block.purpose,
112532
+ requested: block.contentFrame,
112533
+ fallback: appliedContentFrame,
112534
+ policy: input.policy,
112535
+ severity: designIssueSeverityForPolicy(input.policy),
112536
+ allowedRepairActions: repairActionsForIssue("unsupported-content-frame")
112537
+ });
112538
+ }
112539
+ const requestedCardRole = requestedCardRoleOf(block);
112540
+ if (requestedCardRole && capability.cardRoles === "unsupported") issues.push({
112541
+ kind: "unsupported-card-role",
111978
112542
  blockId: block.blockId,
111979
112543
  blockKind: block.blockKind,
111980
112544
  purpose: block.purpose,
111981
- requested: block.appliedTransitionAfter,
112545
+ requested: requestedCardRole,
111982
112546
  fallback: "none",
111983
112547
  policy: input.policy,
111984
112548
  severity: designIssueSeverityForPolicy(input.policy),
111985
- allowedRepairActions: repairActionsForIssue("removed-transition-for-flush-frame")
112549
+ allowedRepairActions: repairActionsForIssue("unsupported-card-role")
111986
112550
  });
112551
+ if (!supportsSectionHeight(capability, appliedSectionHeight)) {
112552
+ appliedSectionHeight = "default";
112553
+ issues.push({
112554
+ kind: "unsupported-section-height",
112555
+ blockId: block.blockId,
112556
+ blockKind: block.blockKind,
112557
+ purpose: block.purpose,
112558
+ requested: block.sectionHeight ?? "default",
112559
+ fallback: appliedSectionHeight,
112560
+ policy: input.policy,
112561
+ severity: designIssueSeverityForPolicy(input.policy),
112562
+ allowedRepairActions: repairActionsForIssue("unsupported-section-height")
112563
+ });
112564
+ }
112565
+ if (!supportsImageSurfaceTreatment(capability, appliedImageSurfaceTreatment)) {
112566
+ appliedImageSurfaceTreatment = { kind: "none" };
112567
+ issues.push({
112568
+ kind: "unsupported-image-surface-treatment",
112569
+ blockId: block.blockId,
112570
+ blockKind: block.blockKind,
112571
+ purpose: block.purpose,
112572
+ requested: block.imageSurfaceTreatment ?? { kind: "none" },
112573
+ fallback: appliedImageSurfaceTreatment,
112574
+ policy: input.policy,
112575
+ severity: designIssueSeverityForPolicy(input.policy),
112576
+ allowedRepairActions: repairActionsForIssue("unsupported-image-surface-treatment")
112577
+ });
112578
+ }
112579
+ if (!supportsImageSurfaceMotion(capability, appliedImageSurfaceMotion)) {
112580
+ appliedImageSurfaceMotion = "static";
112581
+ issues.push({
112582
+ kind: "unsupported-image-surface-motion",
112583
+ blockId: block.blockId,
112584
+ blockKind: block.blockKind,
112585
+ purpose: block.purpose,
112586
+ requested: block.imageSurfaceMotion ?? "static",
112587
+ fallback: appliedImageSurfaceMotion,
112588
+ policy: input.policy,
112589
+ severity: designIssueSeverityForPolicy(input.policy),
112590
+ allowedRepairActions: repairActionsForIssue("unsupported-image-surface-motion")
112591
+ });
112592
+ }
112593
+ if (!supportsTileTreatment(capability, appliedTileTreatment)) {
112594
+ appliedTileTreatment = "default";
112595
+ issues.push({
112596
+ kind: "unsupported-tile-treatment",
112597
+ blockId: block.blockId,
112598
+ blockKind: block.blockKind,
112599
+ purpose: block.purpose,
112600
+ requested: block.tileTreatment ?? "default",
112601
+ fallback: appliedTileTreatment,
112602
+ policy: input.policy,
112603
+ severity: designIssueSeverityForPolicy(input.policy),
112604
+ allowedRepairActions: repairActionsForIssue("unsupported-tile-treatment")
112605
+ });
112606
+ }
112607
+ if (!supportsEventListTreatment(capability, appliedEventListTreatment)) {
112608
+ appliedEventListTreatment = "plain";
112609
+ issues.push({
112610
+ kind: "unsupported-event-list-treatment",
112611
+ blockId: block.blockId,
112612
+ blockKind: block.blockKind,
112613
+ purpose: block.purpose,
112614
+ requested: block.eventListTreatment ?? "plain",
112615
+ fallback: appliedEventListTreatment,
112616
+ policy: input.policy,
112617
+ severity: designIssueSeverityForPolicy(input.policy),
112618
+ allowedRepairActions: repairActionsForIssue("unsupported-event-list-treatment")
112619
+ });
112620
+ }
112621
+ if (!supportsEyebrowTreatment(capability, appliedEyebrowTreatment)) {
112622
+ appliedEyebrowTreatment = "plain";
112623
+ issues.push({
112624
+ kind: "unsupported-eyebrow-treatment",
112625
+ blockId: block.blockId,
112626
+ blockKind: block.blockKind,
112627
+ purpose: block.purpose,
112628
+ requested: block.eyebrowTreatment ?? "plain",
112629
+ fallback: appliedEyebrowTreatment,
112630
+ policy: input.policy,
112631
+ severity: designIssueSeverityForPolicy(input.policy),
112632
+ allowedRepairActions: repairActionsForIssue("unsupported-eyebrow-treatment")
112633
+ });
112634
+ }
112635
+ if (appliedAccordionIconTreatment && !supportsAccordionIconTreatment(capability, appliedAccordionIconTreatment)) {
112636
+ const requested = appliedAccordionIconTreatment;
112637
+ appliedAccordionIconTreatment = null;
112638
+ issues.push({
112639
+ kind: "unsupported-accordion-icon-treatment",
112640
+ blockId: block.blockId,
112641
+ blockKind: block.blockKind,
112642
+ purpose: block.purpose,
112643
+ requested,
112644
+ fallback: appliedAccordionIconTreatment,
112645
+ policy: input.policy,
112646
+ severity: designIssueSeverityForPolicy(input.policy),
112647
+ allowedRepairActions: repairActionsForIssue("unsupported-accordion-icon-treatment")
112648
+ });
112649
+ }
112650
+ if (appliedComposition && !supportsBlockComposition(capability, appliedComposition)) {
112651
+ const requested = appliedComposition;
112652
+ appliedComposition = null;
112653
+ issues.push({
112654
+ kind: "unsupported-block-composition",
112655
+ blockId: block.blockId,
112656
+ blockKind: block.blockKind,
112657
+ purpose: block.purpose,
112658
+ requested,
112659
+ fallback: null,
112660
+ policy: input.policy,
112661
+ severity: designIssueSeverityForPolicy(input.policy),
112662
+ allowedRepairActions: repairActionsForIssue("unsupported-block-composition")
112663
+ });
112664
+ }
112665
+ if (appliedSectionSurface !== "hero-image" && appliedImageSurfaceTreatment.kind !== "none") {
112666
+ const requested = appliedImageSurfaceTreatment;
112667
+ appliedImageSurfaceTreatment = { kind: "none" };
112668
+ issues.push({
112669
+ kind: "image-surface-treatment-without-image-surface",
112670
+ blockId: block.blockId,
112671
+ blockKind: block.blockKind,
112672
+ purpose: block.purpose,
112673
+ requested,
112674
+ fallback: appliedImageSurfaceTreatment,
112675
+ policy: input.policy,
112676
+ severity: designIssueSeverityForPolicy(input.policy),
112677
+ allowedRepairActions: repairActionsForIssue("image-surface-treatment-without-image-surface")
112678
+ });
112679
+ }
112680
+ if (appliedSectionSurface !== "hero-image" && appliedImageSurfaceMotion !== "static") {
112681
+ const requested = appliedImageSurfaceMotion;
112682
+ appliedImageSurfaceMotion = "static";
112683
+ issues.push({
112684
+ kind: "image-surface-motion-without-image-surface",
112685
+ blockId: block.blockId,
112686
+ blockKind: block.blockKind,
112687
+ purpose: block.purpose,
112688
+ requested,
112689
+ fallback: appliedImageSurfaceMotion,
112690
+ policy: input.policy,
112691
+ severity: designIssueSeverityForPolicy(input.policy),
112692
+ allowedRepairActions: repairActionsForIssue("image-surface-motion-without-image-surface")
112693
+ });
112694
+ }
112695
+ if (appliedSectionSurface === "hero-image" && appliedImageSurfaceTreatment.kind !== "none" && appliedHeroLegibility !== "none") {
112696
+ const requested = appliedHeroLegibility;
112697
+ appliedHeroLegibility = "none";
112698
+ issues.push({
112699
+ kind: "hero-legibility-overridden-by-image-surface-treatment",
112700
+ blockId: block.blockId,
112701
+ blockKind: block.blockKind,
112702
+ purpose: block.purpose,
112703
+ requested,
112704
+ fallback: appliedHeroLegibility,
112705
+ policy: input.policy,
112706
+ severity: designIssueSeverityForPolicy(input.policy),
112707
+ allowedRepairActions: repairActionsForIssue("hero-legibility-overridden-by-image-surface-treatment")
112708
+ });
112709
+ }
112710
+ if (appliedSectionSurface === "hero-image" && appliedImageSurfaceTreatment.kind === "none" && (block.heroLegibility === void 0 || appliedHeroLegibility === "none")) {
112711
+ appliedHeroLegibility = "scrim-gradient";
112712
+ issues.push({
112713
+ kind: "hero-image-without-legibility",
112714
+ blockId: block.blockId,
112715
+ blockKind: block.blockKind,
112716
+ purpose: block.purpose,
112717
+ requested: block.heroLegibility,
112718
+ fallback: appliedHeroLegibility,
112719
+ policy: input.policy,
112720
+ severity: designIssueSeverityForPolicy(input.policy),
112721
+ allowedRepairActions: repairActionsForIssue("hero-image-without-legibility")
112722
+ });
112723
+ }
111987
112724
  return {
111988
112725
  ...block,
111989
- appliedTransitionAfter: "none"
112726
+ appliedSectionSurface,
112727
+ appliedContentFrame,
112728
+ appliedCardRole,
112729
+ appliedSectionHeight,
112730
+ appliedHeroLegibility,
112731
+ appliedImageSurfaceTreatment,
112732
+ appliedImageSurfaceMotion,
112733
+ appliedTileTreatment,
112734
+ appliedEventListTreatment,
112735
+ appliedEyebrowTreatment,
112736
+ appliedAccordionIconTreatment,
112737
+ appliedComposition
111990
112738
  };
111991
112739
  }),
111992
112740
  issues
@@ -112826,59 +113574,6 @@ function recompileSiteChromeHeaderFromPersistedIntent(theme, siteStyle, requeste
112826
113574
  };
112827
113575
  }
112828
113576
  //#endregion
112829
- //#region ../theme-core/src/site-styles/runtimeBackgroundResolution.ts
112830
- function parseCanonicalRuntimeBackground(content) {
112831
- const background = readRecord$1(readRecord$1(content._sectionStyles)?.background);
112832
- if (!background) return { kind: "none" };
112833
- const parsed = boxBackgroundSchema.safeParse(background);
112834
- if (!parsed.success) return { kind: "none" };
112835
- return parsed.data.contentOwner === "page-design-editor" ? {
112836
- kind: "page-design-override",
112837
- background: parsed.data
112838
- } : {
112839
- kind: "block-authored",
112840
- background: parsed.data
112841
- };
112842
- }
112843
- function resolveSectionBackground(source, lookDefault) {
112844
- if (source.kind === "page-design-override") return {
112845
- kind: "page-design-override",
112846
- background: source.background
112847
- };
112848
- if (lookDefault) {
112849
- if (source.kind === "block-authored" && (source.background.type === "image" || source.background.type === "video")) return {
112850
- kind: "block-authored",
112851
- background: mergeLookForegroundWithAuthoredMedia(lookDefault, source.background)
112852
- };
112853
- return {
112854
- kind: "look-default",
112855
- background: lookDefault
112856
- };
112857
- }
112858
- if (source.kind === "block-authored" && (source.background.type === "image" || source.background.type === "video")) return {
112859
- kind: "block-authored",
112860
- background: source.background
112861
- };
112862
- return source.kind === "block-authored" ? {
112863
- kind: "block-authored",
112864
- background: source.background
112865
- } : { kind: "none" };
112866
- }
112867
- function mergeLookForegroundWithAuthoredMedia(lookDefault, authored) {
112868
- const merged = boxBackgroundSchema.safeParse({
112869
- ...lookDefault,
112870
- type: authored.type,
112871
- image: authored.image,
112872
- objectFit: authored.objectFit,
112873
- scale: authored.scale,
112874
- position: authored.position
112875
- });
112876
- return merged.success ? merged.data : authored;
112877
- }
112878
- function readRecord$1(value) {
112879
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
112880
- }
112881
- //#endregion
112882
113577
  //#region ../theme-core/src/site-styles/previewRuntimeCapabilities.ts
112883
113578
  function cardRoleCapabilitiesByBlockKind(blocks) {
112884
113579
  return new Map(blocks.map((block) => [block.blockKind, block.cardRoles]));
@@ -113243,7 +113938,6 @@ function compileEditorModelRuntime(input) {
113243
113938
  blockKind: block.blockKind,
113244
113939
  purpose: previewCustomizePurpose,
113245
113940
  ...look ? { appearancePresetId: look.id } : {},
113246
- transitionAfter: "none",
113247
113941
  emphasis: "medium",
113248
113942
  sectionSurface: "base",
113249
113943
  contentFrame: { kind: "plain" },
@@ -113252,51 +113946,29 @@ function compileEditorModelRuntime(input) {
113252
113946
  });
113253
113947
  if (blocks.length === 0) return {
113254
113948
  blocks: [],
113255
- boundaries: []
113949
+ joins: []
113256
113950
  };
113257
- const fallbackTransitionAfterByBlockId = new Map(blocks.map((block) => [block.blockId, block.transitionAfter]));
113258
- const compiled = compilePageRuntimeFields({
113951
+ return compilePageRuntimeFields({
113259
113952
  resolved: resolveBlockDesignPlan({
113260
113953
  policy: "user",
113261
113954
  blocks,
113262
113955
  capabilities: runtimeCapabilitiesForBlocks(blocks, cardRoleCapabilitiesByBlockKind(input.editorModel.blocks))
113263
113956
  }),
113957
+ joins: input.editorModel.boundaries.flatMap(runtimeJoinForEditorBoundary),
113264
113958
  surfaceResolutionContext: input.surfaceResolutionContext,
113265
113959
  designRules: input.designRules,
113266
113960
  componentRules: input.componentRules,
113267
113961
  sectionSpacing: input.sectionSpacing
113268
113962
  });
113269
- return {
113270
- blocks: compiled.blocks,
113271
- boundaries: compileEditorBoundaryRuntime({
113272
- boundaries: input.editorModel.boundaries,
113273
- blocks: compiled.blocks,
113274
- fallbackTransitionAfterByPreviousBlockId: fallbackTransitionAfterByBlockId,
113275
- designRules: input.designRules
113276
- })
113277
- };
113278
113963
  }
113279
- function compileEditorBoundaryRuntime(input) {
113280
- const blockById = new Map(input.blocks.map((block) => [block.blockId, block]));
113281
- return input.boundaries.flatMap((boundary) => {
113282
- const previousBlock = blockById.get(boundary.previousBlockId);
113283
- const nextBlock = blockById.get(boundary.nextBlockId);
113284
- const transitionAfter = transitionAfterForEditorBoundary(boundary) ?? input.fallbackTransitionAfterByPreviousBlockId.get(boundary.previousBlockId) ?? null;
113285
- if (!previousBlock || !nextBlock || !transitionAfter) return [];
113286
- const compiled = compileBoundaryRuntime({
113287
- boundaryId: boundary.boundaryId,
113288
- previousBlockId: boundary.previousBlockId,
113289
- nextBlockId: boundary.nextBlockId,
113290
- treatment: transitionAfter,
113291
- fromSurface: previousBlock.visualSectionSurface,
113292
- toSurface: nextBlock.visualSectionSurface,
113293
- rules: input.designRules
113294
- });
113295
- return compiled ? [compiled] : [];
113296
- });
113297
- }
113298
- function transitionAfterForEditorBoundary(boundary) {
113299
- return boundary.availableBreaks.find((candidate) => candidate.id === boundary.breakChoice.effectiveValue)?.transitionAfter ?? null;
113964
+ /** The editor boundary's effective break as an intent-only join; none for no break. */
113965
+ function runtimeJoinForEditorBoundary(boundary) {
113966
+ const treatment = boundary.availableBreaks.find((candidate) => candidate.id === boundary.breakChoice.effectiveValue)?.transitionAfter;
113967
+ return treatment && isDecorativeTransitionIntent(treatment) ? [{
113968
+ previousBlockId: boundary.previousBlockId,
113969
+ nextBlockId: boundary.nextBlockId,
113970
+ treatment
113971
+ }] : [];
113300
113972
  }
113301
113973
  function applyCompiledRuntimeFieldsToContent(content, fields) {
113302
113974
  const runtimeFields = fields ? preserveContentOwnedBackgroundFields(content, fields) : fields;
@@ -113305,47 +113977,6 @@ function applyCompiledRuntimeFieldsToContent(content, fields) {
113305
113977
  ...runtimeFields
113306
113978
  };
113307
113979
  }
113308
- function retargetCompiledRuntimeFieldsForContentAdjacency(fields, adjacency) {
113309
- if (!fields?._transitionStyles) return fields;
113310
- const transition = fields._transitionStyles;
113311
- const fromSurface = contentOwnedBackgroundEndpoint(adjacency.currentContent) ?? transition.from;
113312
- const toSurface = contentOwnedBackgroundEndpoint(adjacency.nextContent) ?? transition.to;
113313
- if (fromSurface === transition.from && toSurface === transition.to) return fields;
113314
- return {
113315
- ...fields,
113316
- _transitionStyles: compileTransitionRuntime({
113317
- treatment: transition.treatment,
113318
- from: fromSurface,
113319
- to: toSurface
113320
- })
113321
- };
113322
- }
113323
- function retargetCompiledBoundaryRuntimeForContentAdjacency(boundary, adjacency) {
113324
- const fromSurface = contentOwnedBackgroundEndpoint(adjacency.currentContent) ?? boundary.from;
113325
- const toSurface = contentOwnedBackgroundEndpoint(adjacency.nextContent) ?? boundary.to;
113326
- if (fromSurface === boundary.from && toSurface === boundary.to) return boundary;
113327
- const transition = compileTransitionRuntime({
113328
- treatment: boundary.treatment,
113329
- from: fromSurface,
113330
- to: toSurface
113331
- });
113332
- return {
113333
- ...boundary,
113334
- from: transition.from,
113335
- to: transition.to,
113336
- cssVars: transition.cssVars
113337
- };
113338
- }
113339
- function contentOwnedBackgroundEndpoint(content) {
113340
- const background = readRecord(readRecord(content?._sectionStyles)?.background);
113341
- if (background?.contentOwner !== "page-design-editor" || background.type !== "color") return null;
113342
- const token = parseThemeColorToken(background.color);
113343
- if (!token) return null;
113344
- return {
113345
- kind: "color-token",
113346
- token
113347
- };
113348
- }
113349
113980
  function preserveContentOwnedBackgroundFields(content, fields) {
113350
113981
  const resolved = resolveSectionBackground(parseCanonicalRuntimeBackground(content), fields._sectionStyles?.background);
113351
113982
  if (resolved.kind === "look-default" || resolved.kind === "none") return fields;
@@ -113357,9 +113988,6 @@ function preserveContentOwnedBackgroundFields(content, fields) {
113357
113988
  }
113358
113989
  };
113359
113990
  }
113360
- function readRecord(value) {
113361
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
113362
- }
113363
113991
  function lookForEditorRuntimeBlock(block, override) {
113364
113992
  if (override?.kind === "pinned") return block.availableLooks.find((look) => look.id === override.appearancePresetId) ?? null;
113365
113993
  if (override?.kind === "auto") return block.availableLooks.find((look) => look.id === block.autoLookId) ?? null;
@@ -114748,7 +115376,8 @@ const siteOptionSources = [
114748
115376
  "eventCategories",
114749
115377
  "eventSeries",
114750
115378
  "venues",
114751
- "staffMembers"
115379
+ "staffMembers",
115380
+ "appointmentServiceCategories"
114752
115381
  ];
114753
115382
  const mediaDeliverySchema = z.object({
114754
115383
  sizes: z.string().optional(),
@@ -127549,6 +128178,7 @@ function useHeadlessBookingFlow(options) {
127549
128178
  data,
127550
128179
  services: {
127551
128180
  items: options.services,
128181
+ categories: readServiceCategories(options),
127552
128182
  selected: selectedService,
127553
128183
  defaultServiceId,
127554
128184
  requiresSelection: Boolean(options.requiresServiceSelection)
@@ -127613,6 +128243,10 @@ function useHeadlessBookingFlow(options) {
127613
128243
  actions
127614
128244
  };
127615
128245
  }
128246
+ const EMPTY_SERVICE_CATEGORIES = [];
128247
+ function readServiceCategories(options) {
128248
+ return options.serviceCategories ?? EMPTY_SERVICE_CATEGORIES;
128249
+ }
127616
128250
  function navigateToCheckout(checkoutUrl) {
127617
128251
  window.location.href = checkoutUrl;
127618
128252
  }
@@ -128289,10 +128923,97 @@ function BookingConsentAgreement({ agreement, showTitle = true, title, titleId }
128289
128923
  })] });
128290
128924
  }
128291
128925
  //#endregion
128292
- //#region ../blocks/src/system/runtime/nodes/booking-form.default-ui.tsx
128926
+ //#region ../blocks/src/system/runtime/nodes/booking-form.service-choices.tsx
128293
128927
  function optionCardClassName(isSelected) {
128294
128928
  return `rb-option-card${isSelected ? " rb-option-card--selected" : ""}`;
128295
128929
  }
128930
+ function SelectedCheck() {
128931
+ return /* @__PURE__ */ jsx("div", {
128932
+ className: "rb-option-check",
128933
+ children: /* @__PURE__ */ jsx(CheckIcon, {})
128934
+ });
128935
+ }
128936
+ /**
128937
+ * The service buttons. A site with no categories keeps its single grid; a
128938
+ * categorised site gets a heading per category, then "Other", above the same
128939
+ * grid (issue 1403). Empty categories are skipped, and there are no intros
128940
+ * here, to keep the step compact.
128941
+ */
128942
+ function DefaultServiceChoices({ booking }) {
128943
+ const layout = layoutServicesByCategory(booking.services.categories, booking.services.items.map((service) => ({
128944
+ service,
128945
+ categoryId: service.categoryId ?? null
128946
+ })), { keepEmptyGroups: false });
128947
+ switch (layout.kind) {
128948
+ case "uncategorized-site": return /* @__PURE__ */ jsxs("div", {
128949
+ className: "rb-reg-stack rb-reg-stack--sm",
128950
+ children: [/* @__PURE__ */ jsx("span", {
128951
+ className: "ab-subtitle",
128952
+ children: "Service"
128953
+ }), /* @__PURE__ */ jsx(ServiceChoiceGrid, {
128954
+ booking,
128955
+ choices: layout.services
128956
+ })]
128957
+ });
128958
+ case "categorized-site": return /* @__PURE__ */ jsx("div", {
128959
+ className: "rb-reg-stack",
128960
+ children: [...layout.groups.map((group) => ({
128961
+ key: group.category.id,
128962
+ heading: group.category.name,
128963
+ choices: group.services
128964
+ })), ...layout.other.length > 0 ? [{
128965
+ key: "other",
128966
+ heading: "Other",
128967
+ choices: layout.other
128968
+ }] : []].map((group) => /* @__PURE__ */ jsxs("div", {
128969
+ className: "rb-reg-stack rb-reg-stack--sm",
128970
+ "data-booking-service-group": group.key,
128971
+ children: [/* @__PURE__ */ jsx("h3", {
128972
+ className: "ab-subtitle",
128973
+ children: group.heading
128974
+ }), /* @__PURE__ */ jsx(ServiceChoiceGrid, {
128975
+ booking,
128976
+ choices: group.choices
128977
+ })]
128978
+ }, group.key))
128979
+ });
128980
+ default: return assertNever$5(layout);
128981
+ }
128982
+ }
128983
+ function ServiceChoiceGrid({ booking, choices }) {
128984
+ const selectedServiceId = booking.services.selected?.id;
128985
+ return /* @__PURE__ */ jsx("div", {
128986
+ className: "rb-reg-grid",
128987
+ children: choices.map(({ service }) => {
128988
+ const isSelected = selectedServiceId === service.id;
128989
+ return /* @__PURE__ */ jsxs("button", {
128990
+ type: "button",
128991
+ onClick: () => booking.actions.selectService(service.id),
128992
+ className: optionCardClassName(isSelected),
128993
+ "aria-pressed": isSelected,
128994
+ children: [/* @__PURE__ */ jsxs("div", {
128995
+ className: "rb-flex-1",
128996
+ children: [
128997
+ /* @__PURE__ */ jsx("div", {
128998
+ className: "ab-subtitle",
128999
+ children: service.title
129000
+ }),
129001
+ service.description ? /* @__PURE__ */ jsx("div", {
129002
+ className: "ab-text rb-on-surface--muted",
129003
+ children: service.description
129004
+ }) : null,
129005
+ service.durationMinutes ? /* @__PURE__ */ jsxs("div", {
129006
+ className: "ab-text rb-on-surface--muted",
129007
+ children: [service.durationMinutes, " minutes"]
129008
+ }) : null
129009
+ ]
129010
+ }), isSelected ? /* @__PURE__ */ jsx(SelectedCheck, {}) : null]
129011
+ }, service.id);
129012
+ })
129013
+ });
129014
+ }
129015
+ //#endregion
129016
+ //#region ../blocks/src/system/runtime/nodes/booking-form.default-ui.tsx
128296
129017
  function formatBookingAmount(amount, currency) {
128297
129018
  return formatCurrencyAmount(amount, currency, { decimals: "auto" });
128298
129019
  }
@@ -128383,7 +129104,6 @@ function DefaultBookingStepContent({ booking, errorSummaryId }) {
128383
129104
  function DefaultServiceSelectionStep({ booking }) {
128384
129105
  const services = booking.services.items;
128385
129106
  const resources = booking.resources.items;
128386
- const selectedServiceId = booking.services.selected?.id;
128387
129107
  const selectedResourceId = booking.resources.selectedResourceId;
128388
129108
  const showServiceSelect = booking.services.requiresSelection || services.length > 1;
128389
129109
  const showResourceSelect = booking.resources.requiresSelection || resources.length > 1;
@@ -128398,41 +129118,7 @@ function DefaultServiceSelectionStep({ booking }) {
128398
129118
  className: "ab-text rb-on-surface--muted",
128399
129119
  children: "Choose the service you'd like to book"
128400
129120
  })] }),
128401
- showServiceSelect ? /* @__PURE__ */ jsxs("div", {
128402
- className: "rb-reg-stack rb-reg-stack--sm",
128403
- children: [/* @__PURE__ */ jsx("span", {
128404
- className: "ab-subtitle",
128405
- children: "Service"
128406
- }), /* @__PURE__ */ jsx("div", {
128407
- className: "rb-reg-grid",
128408
- children: services.map((service) => {
128409
- const isSelected = selectedServiceId === service.id;
128410
- return /* @__PURE__ */ jsxs("button", {
128411
- type: "button",
128412
- onClick: () => booking.actions.selectService(service.id),
128413
- className: optionCardClassName(isSelected),
128414
- "aria-pressed": isSelected,
128415
- children: [/* @__PURE__ */ jsxs("div", {
128416
- className: "rb-flex-1",
128417
- children: [
128418
- /* @__PURE__ */ jsx("div", {
128419
- className: "ab-subtitle",
128420
- children: service.title
128421
- }),
128422
- service.description ? /* @__PURE__ */ jsx("div", {
128423
- className: "ab-text rb-on-surface--muted",
128424
- children: service.description
128425
- }) : null,
128426
- service.durationMinutes ? /* @__PURE__ */ jsxs("div", {
128427
- className: "ab-text rb-on-surface--muted",
128428
- children: [service.durationMinutes, " minutes"]
128429
- }) : null
128430
- ]
128431
- }), isSelected ? /* @__PURE__ */ jsx(SelectedCheck, {}) : null]
128432
- }, service.id);
128433
- })
128434
- })]
128435
- }) : null,
129121
+ showServiceSelect ? /* @__PURE__ */ jsx(DefaultServiceChoices, { booking }) : null,
128436
129122
  booking.resources.state.tag === "loading" ? /* @__PURE__ */ jsxs("div", {
128437
129123
  className: "ab-center",
128438
129124
  children: [/* @__PURE__ */ jsx(SpinnerNode, {
@@ -129011,12 +129697,6 @@ function DefaultBookingErrorSummary({ errors, id }) {
129011
129697
  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))]
129012
129698
  });
129013
129699
  }
129014
- function SelectedCheck() {
129015
- return /* @__PURE__ */ jsx("div", {
129016
- className: "rb-option-check",
129017
- children: /* @__PURE__ */ jsx(CheckIcon, {})
129018
- });
129019
- }
129020
129700
  //#endregion
129021
129701
  //#region ../blocks/src/system/runtime/shared/utils/checkoutUrls.ts
129022
129702
  /**
@@ -129272,6 +129952,7 @@ const BookingFormClient = ({ siteId: siteIdProp, render }) => {
129272
129952
  formId: render.hydration.formId ?? form?.id ?? "booking-form",
129273
129953
  form,
129274
129954
  services,
129955
+ serviceCategories: render.hydration.serviceCategories,
129275
129956
  initialServiceId,
129276
129957
  initialResourceId,
129277
129958
  initialData: render.hydration.initialData ?? void 0,
@@ -135607,7 +136288,7 @@ const LEAFLET_JS_INTEGRITY = "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo
135607
136288
  let leafletLoaded = false;
135608
136289
  let leafletLoadPromise = null;
135609
136290
  function isLeafletApi(value) {
135610
- if (!isRecord$17(value)) return false;
136291
+ if (!isRecord$18(value)) return false;
135611
136292
  return typeof value.map === "function" && typeof value.tileLayer === "function" && typeof value.marker === "function";
135612
136293
  }
135613
136294
  function loadLeaflet() {
@@ -143368,6 +144049,16 @@ function getNewsletterSubmissionFormError(state) {
143368
144049
  }
143369
144050
  //#endregion
143370
144051
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.submission.ts
144052
+ /**
144053
+ * Logs a refusal with its reason so a QA session can tell the cases apart in
144054
+ * the console, since the visitor-facing copy deliberately does not.
144055
+ */
144056
+ function reportNewsletterSignupRefusal(reason, detail = {}) {
144057
+ console.warn("[newsletter-form] Signup refused before sending", {
144058
+ reason,
144059
+ ...detail
144060
+ });
144061
+ }
143371
144062
  function mapNewsletterFormFieldsToSubscriberInput(fields) {
143372
144063
  return {
143373
144064
  email: readSubmittedText(fields.email),
@@ -143388,7 +144079,7 @@ function resolveNewsletterSubmitTarget(input) {
143388
144079
  case "signed-footer-source": return publishedSourceTarget(input.sourceRef);
143389
144080
  case "preview": return {
143390
144081
  tag: "unavailable",
143391
- reason: "missing_block_source"
144082
+ reason: "preview_only_source"
143392
144083
  };
143393
144084
  default: return assertNever$10(input);
143394
144085
  }
@@ -143447,13 +144138,23 @@ function NewsletterFormClient({ render }) {
143447
144138
  }
143448
144139
  const submitTarget = resolveNewsletterSubmitTarget(render.hydration.target);
143449
144140
  if (submitTarget.tag === "unavailable") {
144141
+ reportNewsletterSignupRefusal(submitTarget.reason, { target: render.hydration.target });
144142
+ dispatchSubmission({
144143
+ type: "failed",
144144
+ error: newsletterFormError(SIGNUPS_PAUSED_MESSAGE)
144145
+ });
144146
+ return;
144147
+ }
144148
+ if (render.hydration.setupRevision === null) {
144149
+ reportNewsletterSignupRefusal("setup_not_configured");
143450
144150
  dispatchSubmission({
143451
144151
  type: "failed",
143452
144152
  error: newsletterFormError(SIGNUPS_PAUSED_MESSAGE)
143453
144153
  });
143454
144154
  return;
143455
144155
  }
143456
- if (render.hydration.setupRevision === null || render.display.availability === "unavailable") {
144156
+ if (render.display.availability === "unavailable") {
144157
+ reportNewsletterSignupRefusal("destination_unavailable", { setupRevision: render.hydration.setupRevision });
143457
144158
  dispatchSubmission({
143458
144159
  type: "failed",
143459
144160
  error: newsletterFormError(SIGNUPS_PAUSED_MESSAGE)
@@ -143463,6 +144164,7 @@ function NewsletterFormClient({ render }) {
143463
144164
  const selection = readVisitorSelection(formData, render.hydration);
143464
144165
  const selectionError = visitorSelectionError(selection);
143465
144166
  if (selectionError) {
144167
+ if (selection.tag === "unusable") reportNewsletterSignupRefusal("choices_unusable", { choiceProvider: render.hydration.choiceProvider });
143466
144168
  dispatchSubmission({
143467
144169
  type: "failed",
143468
144170
  error: selectionError
@@ -147086,16 +147788,16 @@ const interpolateRichTextTokensOptionsSchema = z.object({ tokens: z.array(suppor
147086
147788
  const richTextTokenPattern = /\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g;
147087
147789
  function getSiteName(context) {
147088
147790
  const viewModel = context.viewModel;
147089
- if (!isRecord$17(viewModel)) return null;
147791
+ if (!isRecord$18(viewModel)) return null;
147090
147792
  const site = viewModel.site;
147091
- if (isRecord$17(site) && typeof site.title === "string") {
147793
+ if (isRecord$18(site) && typeof site.title === "string") {
147092
147794
  const title = site.title.trim();
147093
147795
  return title.length > 0 ? title : null;
147094
147796
  }
147095
147797
  const root = viewModel.$root;
147096
- if (isRecord$17(root)) {
147798
+ if (isRecord$18(root)) {
147097
147799
  const rootSite = root.site;
147098
- if (isRecord$17(rootSite) && typeof rootSite.title === "string") {
147800
+ if (isRecord$18(rootSite) && typeof rootSite.title === "string") {
147099
147801
  const title = rootSite.title.trim();
147100
147802
  if (title.length > 0) return title;
147101
147803
  }
@@ -147117,7 +147819,7 @@ function replaceTokenText(input, allowedTokens, tokenValues) {
147117
147819
  });
147118
147820
  }
147119
147821
  function visitRichTextNode(node, allowedTokens, tokenValues) {
147120
- if (!isRecord$17(node)) return node;
147822
+ if (!isRecord$18(node)) return node;
147121
147823
  let changed = false;
147122
147824
  const nextNode = { ...node };
147123
147825
  if (nextNode.type === "text" && typeof nextNode.text === "string") {
@@ -147138,7 +147840,7 @@ function visitRichTextNode(node, allowedTokens, tokenValues) {
147138
147840
  return changed ? nextNode : node;
147139
147841
  }
147140
147842
  function interpolateRichTextTokens(value, options, context) {
147141
- if (!isRecord$17(value)) return value;
147843
+ if (!isRecord$18(value)) return value;
147142
147844
  return visitRichTextNode(value, new Set(options.tokens), {
147143
147845
  year: String((/* @__PURE__ */ new Date()).getFullYear()),
147144
147846
  site_name: getSiteName(context)
@@ -149849,6 +150551,123 @@ function generateFooterBottomTextLinkStyles(footer, themeId, tokens) {
149849
150551
  return cssChunks.join("\n");
149850
150552
  }
149851
150553
  //#endregion
150554
+ //#region ../theme-core/src/layout/boundaryCss.ts
150555
+ /**
150556
+ * Section-break CSS, generated from the transition catalogue (issue 1422).
150557
+ *
150558
+ * The renderer emits one element per shown join:
150559
+ *
150560
+ * <div data-block-boundary data-boundary-treatment="wave-edge-gentle"
150561
+ * data-boundary-placement="previous"
150562
+ * style="--boundary-shape-paint: …; --boundary-band-paint: …">
150563
+ *
150564
+ * Every selector matches an exact attribute value, so there is no reliance on
150565
+ * prefix ordering in the cascade and nothing reads colours out of a string.
150566
+ * The shape layer is `::after`; the band layer (paint → paint only) is
150567
+ * `::before`.
150568
+ */
150569
+ const waveMaskSvgPaths = {
150570
+ classic: "M0 72V46C130 55 260 24 420 30C590 36 690 62 860 42C1035 21 1160 47 1305 36C1365 31 1415 33 1440 38V72H0Z",
150571
+ balanced: "M0 72V42C120 24 240 24 360 42C480 60 600 60 720 42C840 24 960 24 1080 42C1200 60 1320 60 1440 42V72H0Z",
150572
+ gentle: "M0 72V43C180 32 300 32 480 43C660 54 780 54 960 43C1140 32 1260 32 1440 43V72H0Z",
150573
+ bold: "M0 72V44C90 14 270 14 360 44C450 74 630 74 720 44C810 14 990 14 1080 44C1170 74 1350 74 1440 44V72H0Z",
150574
+ drift: "M0 72V39C115 31 205 54 340 48C505 41 610 16 760 33C920 51 1015 64 1160 44C1280 27 1375 35 1440 50V72H0Z"
150575
+ };
150576
+ function waveMaskDataUrl(mask) {
150577
+ return svgDataUrl(`<svg viewBox='0 0 1440 72' xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none'><path d='${waveMaskSvgPaths[mask]}' fill='white'/></svg>`);
150578
+ }
150579
+ /**
150580
+ * How each placement anchors the layers against the zero-height boundary
150581
+ * element that sits exactly on the join.
150582
+ *
150583
+ * - `centred` straddles the join, band under shape.
150584
+ * - `previous` sits above the join, inside the previous section.
150585
+ * - `next` sits below the join, inside the next section, with the shape
150586
+ * flipped so it reads as the mirror of `previous`.
150587
+ */
150588
+ const placementRules = {
150589
+ centred: {
150590
+ shape: ["top: 0;", "transform: translateY(-50%);"],
150591
+ band: ["top: 0;", "transform: translateY(-50%);"]
150592
+ },
150593
+ previous: {
150594
+ shape: ["bottom: 0;"],
150595
+ band: null
150596
+ },
150597
+ next: {
150598
+ shape: ["top: 0;", "transform: scaleY(-1);"],
150599
+ band: null
150600
+ }
150601
+ };
150602
+ function generateBoundaryCss(selector) {
150603
+ const boundary = `${selector} [data-block-boundary]`;
150604
+ return [
150605
+ rule(boundary, [
150606
+ "position: relative;",
150607
+ "z-index: 3;",
150608
+ "height: 0;",
150609
+ "pointer-events: none;"
150610
+ ]),
150611
+ rule(`${selector} [data-block][data-site-style-boundary-before] > :where([data-slot='section'])`, ["padding-top: calc(var(--rb-section-pad-y, 0rem) + var(--site-style-boundary-before-reserve, 0rem));"]),
150612
+ rule(`${selector} [data-block][data-site-style-boundary-after] > :where([data-slot='section'])`, ["padding-bottom: calc(var(--rb-section-pad-y, 0rem) + var(--site-style-boundary-after-reserve, 0rem));"]),
150613
+ rule(`${boundary}::before,\n${boundary}::after`, [
150614
+ "content: '';",
150615
+ "position: absolute;",
150616
+ "left: 0;",
150617
+ "right: 0;",
150618
+ "height: var(--boundary-height);",
150619
+ "pointer-events: none;"
150620
+ ]),
150621
+ rule(`${boundary}::before`, ["z-index: 0;", "background: var(--boundary-band-paint, transparent);"]),
150622
+ rule(`${boundary}::after`, ["z-index: 1;"]),
150623
+ ...placementCss(boundary),
150624
+ ...transitionCatalogueEntries.flatMap((entry) => treatmentCss(boundary, entry))
150625
+ ].join("\n\n");
150626
+ }
150627
+ function placementCss(boundary) {
150628
+ return Object.keys(placementRules).flatMap((placement) => {
150629
+ const placed = `${boundary}[data-boundary-placement='${placement}']`;
150630
+ const rules = placementRules[placement];
150631
+ return [rule(`${placed}::after`, rules.shape), rules.band ? rule(`${placed}::before`, rules.band) : rule(`${placed}::before`, ["content: none;"])];
150632
+ });
150633
+ }
150634
+ function treatmentCss(boundary, entry) {
150635
+ const selected = `${boundary}[data-boundary-treatment='${entry.intent}']`;
150636
+ return [rule(selected, [`--boundary-height: ${boundaryRuntimeMetricsForDecorativeTransition(entry.intent).heightCss};`]), rule(`${selected}::after`, shapeDeclarations(entry))];
150637
+ }
150638
+ function shapeDeclarations(entry) {
150639
+ switch (entry.shape.kind) {
150640
+ case "fade": return [
150641
+ "/* Fallback for browsers without color-mix(). */",
150642
+ "background: linear-gradient(to bottom, transparent 0%, var(--boundary-shape-paint) 100%);",
150643
+ "background: linear-gradient(to bottom, transparent 0%, color-mix(in srgb, var(--boundary-shape-paint) 16%, transparent) 34%, color-mix(in srgb, var(--boundary-shape-paint) 68%, transparent) 78%, var(--boundary-shape-paint) 100%);"
150644
+ ];
150645
+ case "wave": {
150646
+ const mask = waveMaskDataUrl(entry.shape.mask);
150647
+ return [
150648
+ "background: var(--boundary-shape-paint);",
150649
+ `mask-image: url("${mask}");`,
150650
+ "mask-repeat: no-repeat;",
150651
+ "mask-size: 100% 100%;",
150652
+ `-webkit-mask-image: url("${mask}");`,
150653
+ "-webkit-mask-repeat: no-repeat;",
150654
+ "-webkit-mask-size: 100% 100%;"
150655
+ ];
150656
+ }
150657
+ case "angle": return [
150658
+ "height: calc(var(--boundary-height) + 2px);",
150659
+ "background: var(--boundary-shape-paint);",
150660
+ `clip-path: ${anglePolygonCss(entry.shape.polygon)};`
150661
+ ];
150662
+ }
150663
+ }
150664
+ function rule(selector, declarations) {
150665
+ return `${selector} {\n ${declarations.join("\n ")}\n}`;
150666
+ }
150667
+ function svgDataUrl(svg) {
150668
+ return `data:image/svg+xml,${encodeURIComponent(svg)}`;
150669
+ }
150670
+ //#endregion
149852
150671
  //#region ../theme-core/src/layout/generateLayoutCss.ts
149853
150672
  /**
149854
150673
  * Generate layout CSS for theme scope (background, text color)
@@ -149873,7 +150692,7 @@ function generateLayoutCss(options) {
149873
150692
  if (heroTypographyCss) cssChunks.push(heroTypographyCss);
149874
150693
  const bodyTextTypographyCss = generateBodyTextTypographyCss(themeId, theme);
149875
150694
  if (bodyTextTypographyCss) cssChunks.push(bodyTextTypographyCss);
149876
- cssChunks.push(generateSiteStyleTransitionCss(themeId));
150695
+ cssChunks.push(generateBoundaryCss(selector));
149877
150696
  return cssChunks.join("\n\n");
149878
150697
  }
149879
150698
  function toPaddingValue(preset) {
@@ -149884,193 +150703,6 @@ function toPaddingValue(preset) {
149884
150703
  relaxed: "1.5rem"
149885
150704
  }[preset];
149886
150705
  }
149887
- const classicWaveTransitionMask = 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>");
149888
- const balancedWaveTransitionMask = 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>");
149889
- const gentleWaveTransitionMask = 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>");
149890
- const boldWaveTransitionMask = 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>");
149891
- const driftWaveTransitionMask = 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>");
149892
- function generateSiteStyleTransitionCss(themeId) {
149893
- const selector = `:where([data-theme-scope="${themeId}"])`;
149894
- const compactBoundaryHeight = requiredBoundaryHeightCss("wave-edge-gentle");
149895
- const mediumBoundaryHeight = requiredBoundaryHeightCss("wave-edge");
149896
- const largeBoundaryHeight = requiredBoundaryHeightCss("wave-edge-bold");
149897
- return `${selector} [data-style-group^='site-style-transition-'] {
149898
- position: relative;
149899
- z-index: 2;
149900
- --site-style-transition-from: transparent;
149901
- --site-style-transition-to: rgb(var(--tb-background));
149902
- }
149903
-
149904
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-'] {
149905
- position: relative;
149906
- z-index: 3;
149907
- height: 0;
149908
- pointer-events: none;
149909
- --site-style-boundary-height: ${mediumBoundaryHeight};
149910
- }
149911
-
149912
- ${selector} [data-block][data-site-style-boundary-before] > :where([data-slot='section']) {
149913
- padding-top: calc(var(--rb-section-pad-y, 0rem) + var(--site-style-boundary-before-reserve, 0rem));
149914
- }
149915
-
149916
- ${selector} [data-block][data-site-style-boundary-after] > :where([data-slot='section']) {
149917
- padding-bottom: calc(var(--rb-section-pad-y, 0rem) + var(--site-style-boundary-after-reserve, 0rem));
149918
- }
149919
-
149920
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-wave-edge-']::before,
149921
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-angle-edge-']::before,
149922
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-soft-fade']::before {
149923
- content: '';
149924
- position: absolute;
149925
- left: 0;
149926
- right: 0;
149927
- top: 0;
149928
- transform: translateY(-50%);
149929
- z-index: 0;
149930
- height: var(--site-style-boundary-height);
149931
- background: var(--site-style-transition-from);
149932
- pointer-events: none;
149933
- }
149934
-
149935
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-wave-edge-']::after,
149936
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-angle-edge-']::after,
149937
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-soft-fade']::after {
149938
- content: '';
149939
- position: absolute;
149940
- left: 0;
149941
- right: 0;
149942
- top: 0;
149943
- transform: translateY(-50%);
149944
- z-index: 1;
149945
- height: var(--site-style-boundary-height);
149946
- pointer-events: none;
149947
- }
149948
-
149949
- ${selector} [data-style-group*='-from-base-'] {
149950
- --site-style-transition-from: rgb(var(--tb-background));
149951
- }
149952
-
149953
- ${selector} [data-style-group*='-from-muted-band-'] {
149954
- --site-style-transition-from: rgb(var(--tb-muted));
149955
- }
149956
-
149957
- ${selector} [data-style-group*='-from-accent-band-'] {
149958
- --site-style-transition-from: rgb(var(--tb-accent));
149959
- }
149960
-
149961
- ${selector} [data-style-group*='-from-brand-depth-band-'] {
149962
- --site-style-transition-from: rgb(var(--tb-primary-800));
149963
- }
149964
-
149965
- ${selector} [data-style-group$='-to-base'],
149966
- ${selector} [data-style-group$='-to-hero-image'] {
149967
- --site-style-transition-to: rgb(var(--tb-background));
149968
- }
149969
-
149970
- ${selector} [data-style-group$='-to-muted-band'] {
149971
- --site-style-transition-to: rgb(var(--tb-muted));
149972
- }
149973
-
149974
- ${selector} [data-style-group$='-to-accent-band'] {
149975
- --site-style-transition-to: rgb(var(--tb-accent));
149976
- }
149977
-
149978
- ${selector} [data-style-group$='-to-brand-depth-band'] {
149979
- --site-style-transition-to: rgb(var(--tb-primary-800));
149980
- }
149981
-
149982
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-soft-fade']::after {
149983
- /* Fallback for browsers without color-mix(). */
149984
- background: linear-gradient(
149985
- to bottom,
149986
- transparent 0%,
149987
- var(--site-style-transition-to) 100%
149988
- );
149989
- background: linear-gradient(
149990
- to bottom,
149991
- transparent 0%,
149992
- color-mix(in srgb, var(--site-style-transition-to) 16%, transparent) 34%,
149993
- color-mix(in srgb, var(--site-style-transition-to) 68%, transparent) 78%,
149994
- var(--site-style-transition-to) 100%
149995
- );
149996
- }
149997
-
149998
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-soft-fade-long-'] {
149999
- --site-style-boundary-height: ${requiredBoundaryHeightCss("soft-fade-long")};
150000
- }
150001
-
150002
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-soft-fade-expansive-'] {
150003
- --site-style-boundary-height: ${requiredBoundaryHeightCss("soft-fade-expansive")};
150004
- }
150005
-
150006
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-angle-edge-']::after {
150007
- height: calc(var(--site-style-boundary-height) + 2px);
150008
- background: var(--site-style-transition-to);
150009
- clip-path: polygon(0 42%, 100% 8%, 100% 100%, 0 100%);
150010
- }
150011
-
150012
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-angle-edge-subtle-'] {
150013
- --site-style-boundary-height: ${compactBoundaryHeight};
150014
- }
150015
-
150016
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-angle-edge-subtle-']::after {
150017
- clip-path: polygon(0 34%, 100% 18%, 100% 100%, 0 100%);
150018
- }
150019
-
150020
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-angle-edge-steep-'] {
150021
- --site-style-boundary-height: ${largeBoundaryHeight};
150022
- }
150023
-
150024
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-angle-edge-steep-']::after {
150025
- clip-path: polygon(0 54%, 100% 0%, 100% 100%, 0 100%);
150026
- }
150027
-
150028
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-wave-edge-']::after {
150029
- background: var(--site-style-transition-to);
150030
- mask-image: url("${classicWaveTransitionMask}");
150031
- mask-repeat: no-repeat;
150032
- mask-size: 100% 100%;
150033
- -webkit-mask-image: url("${classicWaveTransitionMask}");
150034
- -webkit-mask-repeat: no-repeat;
150035
- -webkit-mask-size: 100% 100%;
150036
- }
150037
-
150038
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-wave-edge-balanced-']::after {
150039
- mask-image: url("${balancedWaveTransitionMask}");
150040
- -webkit-mask-image: url("${balancedWaveTransitionMask}");
150041
- }
150042
-
150043
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-wave-edge-gentle-'] {
150044
- --site-style-boundary-height: ${compactBoundaryHeight};
150045
- }
150046
-
150047
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-wave-edge-gentle-']::after {
150048
- mask-image: url("${gentleWaveTransitionMask}");
150049
- -webkit-mask-image: url("${gentleWaveTransitionMask}");
150050
- }
150051
-
150052
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-wave-edge-bold-'] {
150053
- --site-style-boundary-height: ${largeBoundaryHeight};
150054
- }
150055
-
150056
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-wave-edge-bold-']::after {
150057
- mask-image: url("${boldWaveTransitionMask}");
150058
- -webkit-mask-image: url("${boldWaveTransitionMask}");
150059
- }
150060
-
150061
- ${selector} [data-block-boundary][data-style-group^='site-style-transition-wave-edge-drift-']::after {
150062
- mask-image: url("${driftWaveTransitionMask}");
150063
- -webkit-mask-image: url("${driftWaveTransitionMask}");
150064
- }`;
150065
- }
150066
- function requiredBoundaryHeightCss(transition) {
150067
- const metrics = boundaryRuntimeMetricsForTransition(transition);
150068
- if (!metrics) throw new Error(`Missing boundary runtime metrics for ${transition}`);
150069
- return metrics.heightCss;
150070
- }
150071
- function svgDataUrl(svg) {
150072
- return `data:image/svg+xml,${encodeURIComponent(svg)}`;
150073
- }
150074
150706
  function generateHeroTypographyCss(themeId, theme) {
150075
150707
  const typography = theme.hero?.typography;
150076
150708
  if (!typography) return "";
@@ -150089,24 +150721,23 @@ function generateHeroTypographyCss(themeId, theme) {
150089
150721
  if (microHeadlineRules.length > 0) chunks.push(`${microHeadlineSelector} {\n ${microHeadlineRules.join("\n ")}\n}`);
150090
150722
  const microSubheadlineRules = buildTypographyRules("subheadline", typography.microSubheadlineSize, typography.microSubheadlineLineHeight);
150091
150723
  if (microSubheadlineRules.length > 0) chunks.push(`${microSubheadlineSelector} {\n ${microSubheadlineRules.join("\n ")}\n}`);
150092
- if (typography.responsive?.tablet) {
150093
- const tabletHeadlineRules = buildTypographyRules("headline", typography.responsive.tablet.headlineSize, typography.responsive.tablet.headlineLineHeight);
150094
- const tabletSubheadlineRules = buildTypographyRules("subheadline", typography.responsive.tablet.subheadlineSize, typography.responsive.tablet.subheadlineLineHeight);
150095
- const tabletChunks = [];
150096
- if (tabletHeadlineRules.length > 0) tabletChunks.push(`${headlineSelector} {\n ${tabletHeadlineRules.join("\n ")}\n }`);
150097
- if (tabletSubheadlineRules.length > 0) tabletChunks.push(`${subheadlineSelector} {\n ${tabletSubheadlineRules.join("\n ")}\n }`);
150098
- if (tabletChunks.length > 0) chunks.push(`@media ${MQ_TABLET_ONLY} {\n ${tabletChunks.join("\n\n ")}\n}`);
150099
- }
150100
- if (typography.responsive?.mobile) {
150101
- const mobileHeadlineRules = buildTypographyRules("headline", typography.responsive.mobile.headlineSize, typography.responsive.mobile.headlineLineHeight);
150102
- const mobileSubheadlineRules = buildTypographyRules("subheadline", typography.responsive.mobile.subheadlineSize, typography.responsive.mobile.subheadlineLineHeight);
150103
- const mobileChunks = [];
150104
- if (mobileHeadlineRules.length > 0) mobileChunks.push(`${headlineSelector} {\n ${mobileHeadlineRules.join("\n ")}\n }`);
150105
- if (mobileSubheadlineRules.length > 0) mobileChunks.push(`${subheadlineSelector} {\n ${mobileSubheadlineRules.join("\n ")}\n }`);
150106
- if (mobileChunks.length > 0) chunks.push(`@media ${MQ_SM_DOWN} {\n ${mobileChunks.join("\n\n ")}\n}`);
150107
- }
150724
+ const selectors = {
150725
+ headline: headlineSelector,
150726
+ subheadline: subheadlineSelector
150727
+ };
150728
+ const responsiveChunks = [responsiveHeroTypographyCss(MQ_TABLET_ONLY, selectors, typography.responsive?.tablet), responsiveHeroTypographyCss(MQ_SM_DOWN, selectors, typography.responsive?.mobile)];
150729
+ for (const chunk of responsiveChunks) if (chunk) chunks.push(chunk);
150108
150730
  return chunks.join("\n\n");
150109
150731
  }
150732
+ function responsiveHeroTypographyCss(media, selectors, overrides) {
150733
+ if (!overrides) return null;
150734
+ const headlineRules = buildTypographyRules("headline", overrides.headlineSize, overrides.headlineLineHeight);
150735
+ const subheadlineRules = buildTypographyRules("subheadline", overrides.subheadlineSize, overrides.subheadlineLineHeight);
150736
+ const mediaChunks = [];
150737
+ if (headlineRules.length > 0) mediaChunks.push(`${selectors.headline} {\n ${headlineRules.join("\n ")}\n }`);
150738
+ if (subheadlineRules.length > 0) mediaChunks.push(`${selectors.subheadline} {\n ${subheadlineRules.join("\n ")}\n }`);
150739
+ return mediaChunks.length > 0 ? `@media ${media} {\n ${mediaChunks.join("\n\n ")}\n}` : null;
150740
+ }
150110
150741
  function buildTypographyRules(role, size, lineHeight) {
150111
150742
  const rules = [];
150112
150743
  if (size) rules.push(`font-size: ${toHeroSize(role, size)};`);
@@ -155730,6 +156361,15 @@ const appointmentServicesBlockDefinition = {
155730
156361
  required: false,
155731
156362
  ui: { intent: "content" }
155732
156363
  },
156364
+ createSiteOptionSelectField({
156365
+ id: "categoryId",
156366
+ label: "Category",
156367
+ description: "Show one category (with its heading and intro), or leave it on All categories to show everything grouped.",
156368
+ required: false,
156369
+ multiple: false,
156370
+ optionSource: "appointmentServiceCategories",
156371
+ ui: { intent: "content" }
156372
+ }),
155733
156373
  sectionStylesField({
155734
156374
  id: "_sectionStyles",
155735
156375
  label: "Section styles",
@@ -155764,6 +156404,7 @@ const appointmentServicesBlockDefinition = {
155764
156404
  richText({ className: "rb-prose rb-max-w-none" }, bind$1("content.description"), when("content.description")),
155765
156405
  el("appointment-services", {
155766
156406
  sectionHeading: { $bind: { from: "content.heading" } },
156407
+ categoryId: { $bind: { from: "content.categoryId" } },
155767
156408
  services: { $bind: { from: "data.services" } },
155768
156409
  pathname: { $bind: { from: "$root.pathname" } },
155769
156410
  servicePathPrefix: { $bind: { from: "$root.appointmentServicePathPrefix" } }
@@ -187100,25 +187741,10 @@ function deriveLaunchDomainRequirement(siteId, domainState) {
187100
187741
  } : evidenceFromDerivedState(state)
187101
187742
  });
187102
187743
  }
187103
- function deriveLaunchReadiness(input) {
187104
- const siteId = input.siteId ?? null;
187105
- const overrides = input.overrides ?? [];
187106
- const appointmentOverrideDefinition = LAUNCH_REQUIREMENT_OVERRIDE_DEFINITIONS.configure_appointments;
187744
+ function generatedSiteReviewRequirements(input) {
187745
+ if (input.publicExperience === "hosted_booking") return [];
187107
187746
  const isGeneratedSiteReviewed = input.reviewGeneratedSite.kind === "reviewed";
187108
- const requirements = [];
187109
- const confirmEmailState = input.email.kind === "confirmed" ? "complete" : "pending";
187110
- requirements.push(requirement({
187111
- id: "confirm_email",
187112
- packId: "core",
187113
- priority: "required",
187114
- state: confirmEmailState,
187115
- owner: "either",
187116
- title: "Confirm your email",
187117
- description: confirmEmailState === "complete" ? "Email confirmed. Your dashboard and launch path are unlocked." : "Confirm your email before the site can go live.",
187118
- action: informationalAction("Confirm your email"),
187119
- evidence: evidenceFromDerivedState(confirmEmailState)
187120
- }));
187121
- requirements.push(requirement({
187747
+ return [requirement({
187122
187748
  id: "review_generated_site",
187123
187749
  packId: "core",
187124
187750
  priority: "required",
@@ -187140,7 +187766,26 @@ function deriveLaunchReadiness(input) {
187140
187766
  kind: "deferred",
187141
187767
  reason: "pending_acknowledgement"
187142
187768
  }
187769
+ })];
187770
+ }
187771
+ function deriveLaunchReadiness(input) {
187772
+ const siteId = input.siteId ?? null;
187773
+ const overrides = input.overrides ?? [];
187774
+ const appointmentOverrideDefinition = LAUNCH_REQUIREMENT_OVERRIDE_DEFINITIONS.configure_appointments;
187775
+ const requirements = [];
187776
+ const confirmEmailState = input.email.kind === "confirmed" ? "complete" : "pending";
187777
+ requirements.push(requirement({
187778
+ id: "confirm_email",
187779
+ packId: "core",
187780
+ priority: "required",
187781
+ state: confirmEmailState,
187782
+ owner: "either",
187783
+ title: "Confirm your email",
187784
+ description: confirmEmailState === "complete" ? "Email confirmed. Your dashboard and launch path are unlocked." : "Confirm your email before the site can go live.",
187785
+ action: informationalAction("Confirm your email"),
187786
+ evidence: evidenceFromDerivedState(confirmEmailState)
187143
187787
  }));
187788
+ requirements.push(...generatedSiteReviewRequirements(input));
187144
187789
  if (input.appointments.kind !== "disabled") {
187145
187790
  const appointmentsOverride = input.appointments.kind === "pending_setup" ? findLaunchOverride(overrides, appointmentOverrideDefinition.requirementId) : null;
187146
187791
  requirements.push(requirement({
@@ -187636,7 +188281,12 @@ const appointmentSetupServiceDraftSchema = z.object({
187636
188281
  venue: appointmentSetupVenueChoiceSchema,
187637
188282
  timezone: z.string().trim().min(1).nullable(),
187638
188283
  payment: appointmentSetupPaymentDraftSchema,
187639
- assignment: appointmentSetupServiceAssignmentDraftSchema
188284
+ assignment: appointmentSetupServiceAssignmentDraftSchema,
188285
+ /**
188286
+ * The service's category, or null for "Other" (issue 1403). Omitted leaves
188287
+ * an existing service's category alone and puts a new one in "Other".
188288
+ */
188289
+ categoryId: z.string().uuid().nullable().optional()
187640
188290
  }).superRefine((service, ctx) => {
187641
188291
  if (service.payment.kind !== "free" && (service.priceAmount === null || service.priceAmount <= 0)) ctx.addIssue({
187642
188292
  code: "custom",
@@ -191239,9 +191889,9 @@ function createStoreBackend(options) {
191239
191889
  };
191240
191890
  },
191241
191891
  reset: (values) => {
191242
- if (scope === "blocks") getBlocksSlice().updateBlockBaseline?.(getBlockId(), isRecord$20(values) ? values : null);
191892
+ if (scope === "blocks") getBlocksSlice().updateBlockBaseline?.(getBlockId(), isRecord$21(values) ? values : null);
191243
191893
  else {
191244
- const recordValues = isRecord$20(values) ? values : null;
191894
+ const recordValues = isRecord$21(values) ? values : null;
191245
191895
  getRootSlice().hydrate?.(recordValues, recordValues);
191246
191896
  }
191247
191897
  },
@@ -192146,7 +192796,7 @@ function createPromotedFieldBackend(backend, promoted) {
192146
192796
  }
192147
192797
  const parentPath = outermost.parentPath;
192148
192798
  const current = backend.getValue(parentPath);
192149
- if (current != null && !isRecord$20(current)) throw new Error(`Expected an object at promoted field ancestor "${parentPath}"`);
192799
+ if (current != null && !isRecord$21(current)) throw new Error(`Expected an object at promoted field ancestor "${parentPath}"`);
192150
192800
  const next = current == null ? {} : structuredClone(current);
192151
192801
  const relative = (absolute) => absolute.slice(parentPath.length + 1);
192152
192802
  setAtPath(next, relative(path), value);
@@ -195297,6 +195947,7 @@ function normalizeEditorBlockId(value) {
195297
195947
  function normalizeEditorBlock(block) {
195298
195948
  return {
195299
195949
  id: normalizeEditorBlockId(block.id),
195950
+ contentVersionId: block.contentVersionId ?? null,
195300
195951
  kind: block.kind,
195301
195952
  content: cloneBlockValues(block.content ?? null),
195302
195953
  orderIndex: block.orderIndex ?? null,
@@ -195317,6 +195968,7 @@ function normalizedBlocksEqual(left, right) {
195317
195968
  const rightBlock = right[index];
195318
195969
  if (!leftBlock || !rightBlock) return false;
195319
195970
  if (leftBlock.id !== rightBlock.id) return false;
195971
+ if (leftBlock.contentVersionId !== rightBlock.contentVersionId) return false;
195320
195972
  if (leftBlock.kind !== rightBlock.kind) return false;
195321
195973
  if (leftBlock.orderIndex !== rightBlock.orderIndex) return false;
195322
195974
  if (leftBlock.purpose !== rightBlock.purpose) return false;
@@ -225569,6 +226221,21 @@ function normalizeSiteOptionMultiValue(value) {
225569
226221
  }
225570
226222
  return [];
225571
226223
  }
226224
+ function resolveSiteOptionSingleSelection(input) {
226225
+ const value = typeof input.value === "string" ? input.value.trim() : "";
226226
+ if (!value) return { kind: "default" };
226227
+ if (!input.loaded) return {
226228
+ kind: "unresolved",
226229
+ value
226230
+ };
226231
+ return input.items.some((item) => item.value === value) ? {
226232
+ kind: "selected",
226233
+ value
226234
+ } : {
226235
+ kind: "missing",
226236
+ value
226237
+ };
226238
+ }
225572
226239
  function getSiteOptionSource(config) {
225573
226240
  const raw = (config.ui ?? {}).siteOptionSource;
225574
226241
  if (typeof raw !== "string") return null;
@@ -225577,11 +226244,14 @@ function getSiteOptionSource(config) {
225577
226244
  }
225578
226245
  //#endregion
225579
226246
  //#region ../editor-blocks/src/widgets/site-option-picker/SiteOptionPickerField.tsx
226247
+ /** The Combobox id standing in for the empty value's named default option. */
226248
+ const DEFAULT_OPTION_ID = "__site-option-default__";
225580
226249
  function getSearchThreshold(config) {
225581
226250
  return normalizePickerSearchThreshold((config.ui ?? {}).searchThreshold);
225582
226251
  }
225583
226252
  function SiteOptionPickerField({ path, backend, config }) {
225584
226253
  const { value, error, setValue } = useField(path, backend);
226254
+ const errorMessage = error?.message;
225585
226255
  const { siteId, adapters } = useSiteOptionWidgetContext();
225586
226256
  const source = getSiteOptionSource(config);
225587
226257
  const searchThreshold = getSearchThreshold(config);
@@ -225609,14 +226279,14 @@ function SiteOptionPickerField({ path, backend, config }) {
225609
226279
  siteId,
225610
226280
  source
225611
226281
  ]);
225612
- const { options: items, isLoading, errorMessage: loadError } = useAsyncPickerOptions({
226282
+ const { options: items, state: loadState, isLoading, errorMessage: loadError } = useAsyncPickerOptions({
225613
226283
  enabled: Boolean(adapter && siteId && source),
225614
226284
  loadOptions: loadItems
225615
226285
  });
225616
226286
  if (!adapter || !siteId || !source) return /* @__PURE__ */ jsx(FieldFrame, {
225617
226287
  label: config.label,
225618
226288
  description: config.description,
225619
- error: error?.message,
226289
+ error: errorMessage,
225620
226290
  children: () => /* @__PURE__ */ jsx("div", {
225621
226291
  className: "rounded-lg border border-dashed bg-muted/10 p-4 text-sm text-muted-foreground",
225622
226292
  children: "This field type is not supported yet."
@@ -225633,7 +226303,7 @@ function SiteOptionPickerField({ path, backend, config }) {
225633
226303
  return /* @__PURE__ */ jsx(FieldFrame, {
225634
226304
  label: config.label,
225635
226305
  description: config.description,
225636
- error: error?.message,
226306
+ error: errorMessage,
225637
226307
  hint: helperText,
225638
226308
  children: () => /* @__PURE__ */ jsxs("div", {
225639
226309
  className: "space-y-2",
@@ -225660,6 +226330,21 @@ function SiteOptionPickerField({ path, backend, config }) {
225660
226330
  })
225661
226331
  });
225662
226332
  }
226333
+ if (adapter.singleSelection) return /* @__PURE__ */ jsx(SingleSelectionWithDefaultField, {
226334
+ config,
226335
+ error: errorMessage,
226336
+ helperText,
226337
+ items,
226338
+ loaded: loadState.status === "loaded",
226339
+ isLoading,
226340
+ loadError,
226341
+ labels,
226342
+ manageLink,
226343
+ onChange: (nextValue) => setValue(nextValue),
226344
+ selection: adapter.singleSelection,
226345
+ showSearch,
226346
+ value
226347
+ });
225663
226348
  const currentValue = typeof value === "string" ? value : "";
225664
226349
  const selectionLabel = resolvePickerSelectionLabel(currentValue || null, items, ({ value, options }) => adapter.formatSelectionLabel?.({
225665
226350
  value,
@@ -225668,7 +226353,7 @@ function SiteOptionPickerField({ path, backend, config }) {
225668
226353
  return /* @__PURE__ */ jsx(FieldFrame, {
225669
226354
  label: config.label,
225670
226355
  description: config.description,
225671
- error: error?.message,
226356
+ error: errorMessage,
225672
226357
  hint: helperText ?? (selectionLabel ? `Selected: ${selectionLabel}` : labels.emptySelection),
225673
226358
  children: ({ id, messageId }) => /* @__PURE__ */ jsxs("div", {
225674
226359
  className: "space-y-2",
@@ -225695,6 +226380,62 @@ function SiteOptionPickerField({ path, backend, config }) {
225695
226380
  })
225696
226381
  });
225697
226382
  }
226383
+ /**
226384
+ * A single select whose empty value is a named default option ("All
226385
+ * categories"), and which warns when the stored value has been deleted
226386
+ * rather than showing a raw id. Used only by sources that opt in through
226387
+ * `singleSelection` (issue 1403).
226388
+ */
226389
+ function SingleSelectionWithDefaultField({ config, error, helperText, items, loaded, isLoading, loadError, labels, manageLink, onChange, selection, showSearch, value }) {
226390
+ const state = resolveSiteOptionSingleSelection({
226391
+ value,
226392
+ items,
226393
+ loaded
226394
+ });
226395
+ const comboboxValue = state.kind === "default" ? DEFAULT_OPTION_ID : state.value;
226396
+ const placeholder = isLoading ? labels.loading : state.kind === "missing" ? selection.missingSelection.label : labels.select;
226397
+ return /* @__PURE__ */ jsx(FieldFrame, {
226398
+ label: config.label,
226399
+ description: config.description,
226400
+ error,
226401
+ hint: helperText,
226402
+ children: ({ id, messageId }) => /* @__PURE__ */ jsxs("div", {
226403
+ className: "space-y-2",
226404
+ children: [
226405
+ /* @__PURE__ */ jsx(Combobox, {
226406
+ id,
226407
+ "aria-describedby": messageId || void 0,
226408
+ items: [{
226409
+ id: DEFAULT_OPTION_ID,
226410
+ label: selection.defaultOptionLabel,
226411
+ item: null
226412
+ }, ...items.map((item) => ({
226413
+ id: item.value,
226414
+ label: item.label,
226415
+ item
226416
+ }))],
226417
+ value: comboboxValue,
226418
+ onSelect: (entry) => onChange(!entry || entry.id === DEFAULT_OPTION_ID ? "" : entry.id),
226419
+ placeholder,
226420
+ emptyMessage: labels.empty,
226421
+ showClear: false,
226422
+ search: showSearch,
226423
+ searchInputAriaLabel: `Search ${config.label}`,
226424
+ disabled: Boolean(isLoading || loadError)
226425
+ }),
226426
+ state.kind === "missing" ? /* @__PURE__ */ jsx(Callout, {
226427
+ tone: "warning",
226428
+ className: "text-sm",
226429
+ children: selection.missingSelection.warning
226430
+ }) : null,
226431
+ manageLink ? /* @__PURE__ */ jsx("div", {
226432
+ className: "flex justify-end",
226433
+ children: manageLink
226434
+ }) : null
226435
+ ]
226436
+ })
226437
+ });
226438
+ }
225698
226439
  //#endregion
225699
226440
  //#region ../editor-blocks/src/widgets/site-option-picker/loaders.ts
225700
226441
  const siteOptionWidgetLabels = {
@@ -225721,6 +226462,12 @@ const siteOptionWidgetLabels = {
225721
226462
  select: "Select a staff member",
225722
226463
  empty: "No staff yet",
225723
226464
  emptySelection: "Choose a staff member to restrict this block."
226465
+ },
226466
+ appointmentServiceCategories: {
226467
+ loading: "Loading categories...",
226468
+ select: "All categories",
226469
+ empty: "No categories yet",
226470
+ emptySelection: "All categories"
225724
226471
  }
225725
226472
  };
225726
226473
  function mapEventCategoriesToSiteOptionItems(payload) {
@@ -225754,6 +226501,24 @@ function mapStaffMembersToSiteOptionItems(payload) {
225754
226501
  subtitle: staffMember.bio ?? void 0
225755
226502
  }));
225756
226503
  }
226504
+ function mapAppointmentServiceCategoriesToSiteOptionItems(payload) {
226505
+ return payload.categories.map((category) => ({
226506
+ value: category.id,
226507
+ label: category.name
226508
+ }));
226509
+ }
226510
+ /**
226511
+ * Sources whose single selection has an explicit default option and can go
226512
+ * stale (issue 1403). Only appointment-service categories opt in: the event
226513
+ * calendar pickers keep their existing behaviour.
226514
+ */
226515
+ const siteOptionSingleSelection = { appointmentServiceCategories: {
226516
+ defaultOptionLabel: "All categories",
226517
+ missingSelection: {
226518
+ label: "Deleted category",
226519
+ warning: "The category this list was showing has been deleted, so it now shows all your services. Choose another category, or pick All categories."
226520
+ }
226521
+ } };
225757
226522
  async function loadSiteOptionItemsFromApi({ apiClient, siteId, source }) {
225758
226523
  switch (source) {
225759
226524
  case "eventSeries": return mapEventsToSiteOptionItems(unwrapResponse(await apiClient({
@@ -225776,6 +226541,10 @@ async function loadSiteOptionItemsFromApi({ apiClient, siteId, source }) {
225776
226541
  endpoint: "listAppointmentResources",
225777
226542
  params: { siteId }
225778
226543
  })));
226544
+ case "appointmentServiceCategories": return mapAppointmentServiceCategoriesToSiteOptionItems(unwrapResponse(await apiClient({
226545
+ endpoint: "listAppointmentServiceCategories",
226546
+ params: { siteId }
226547
+ })));
225779
226548
  default: return assertNever$10(source);
225780
226549
  }
225781
226550
  }
@@ -225790,12 +226559,14 @@ function createSiteOptionWidgetAdapters({ loadItems, mapLoadError, renderManageL
225790
226559
  eventSeries: createSiteOptionWidgetAdapter("eventSeries", loadItems, mapLoadError, renderManageLink),
225791
226560
  eventCategories: createSiteOptionWidgetAdapter("eventCategories", loadItems, mapLoadError, renderManageLink),
225792
226561
  venues: createSiteOptionWidgetAdapter("venues", loadItems, mapLoadError, renderManageLink),
225793
- staffMembers: createSiteOptionWidgetAdapter("staffMembers", loadItems, mapLoadError, renderManageLink)
226562
+ staffMembers: createSiteOptionWidgetAdapter("staffMembers", loadItems, mapLoadError, renderManageLink),
226563
+ appointmentServiceCategories: createSiteOptionWidgetAdapter("appointmentServiceCategories", loadItems, mapLoadError, renderManageLink)
225794
226564
  };
225795
226565
  }
225796
226566
  function createSiteOptionWidgetAdapter(source, loadItems, mapLoadError, renderManageLink) {
225797
226567
  return {
225798
226568
  labels: siteOptionWidgetLabels[source],
226569
+ singleSelection: siteOptionSingleSelection[source],
225799
226570
  renderManageLink: renderManageLink?.[source],
225800
226571
  loadItems: async ({ siteId }) => {
225801
226572
  if (!siteId) return [];
@@ -227348,7 +228119,7 @@ function sourceAvailability(input) {
227348
228119
  hasMainNavigation: input.hasMainNavigation ?? true,
227349
228120
  hasLegalPolicyNavigation: input.hasLegalPolicyNavigation ?? false,
227350
228121
  hasBusinessAddress: input.hasBusinessAddress,
227351
- hasContact: input.publicProfile.contact !== null,
228122
+ hasContact: publishedContactDetails(input.publicProfile) !== null,
227352
228123
  hasOpeningHours: input.publicProfile.openingHours !== null,
227353
228124
  hasSocialLinks: input.publicProfile.socialLinks.length > 0,
227354
228125
  hasEngagementSource: input.hasEngagementSource ?? false
@@ -228214,25 +228985,20 @@ function projectNavigationLayout(module, input, compactSplitRegion) {
228214
228985
  navigationGroupsClassName: inline ? "rb-grid rb-gap-8 rb-md-grid-cols-3" : "rb-grid rb-grid-cols-1 rb-gap-6"
228215
228986
  };
228216
228987
  }
228217
- function contactLinks(profile) {
228218
- if (profile.contact === null) return [];
228219
- switch (profile.contact.kind) {
228220
- case "phone": return [{
228221
- href: `tel:${profile.contact.phone.replace(/[^+\d]/gu, "")}`,
228222
- label: profile.contact.phone
228223
- }];
228224
- case "email": return [{
228225
- href: `mailto:${profile.contact.email}`,
228226
- label: profile.contact.email
228227
- }];
228228
- case "phone-and-email": return [{
228229
- href: `tel:${profile.contact.phone.replace(/[^+\d]/gu, "")}`,
228230
- label: profile.contact.phone
228231
- }, {
228232
- href: `mailto:${profile.contact.email}`,
228233
- label: profile.contact.email
228234
- }];
228235
- default: return assertNever$10(profile.contact);
228988
+ function contactLinks(contact) {
228989
+ const phone = (value) => ({
228990
+ href: `tel:${value.replace(/[^+\d]/gu, "")}`,
228991
+ label: value
228992
+ });
228993
+ const email = (value) => ({
228994
+ href: `mailto:${value}`,
228995
+ label: value
228996
+ });
228997
+ switch (contact.kind) {
228998
+ case "phone": return [phone(contact.phone)];
228999
+ case "email": return [email(contact.email)];
229000
+ case "phone-and-email": return [phone(contact.phone), email(contact.email)];
229001
+ default: return assertNever$10(contact);
228236
229002
  }
228237
229003
  }
228238
229004
  const socialPlatformIcons = {
@@ -228286,16 +229052,18 @@ function projectFooterRuntimeSection(input) {
228286
229052
  kind: section.kind,
228287
229053
  address: input.businessAddress
228288
229054
  });
228289
- case "contact":
228290
- if (input.publicProfile.contact === null) return err$1({
229055
+ case "contact": {
229056
+ const contact = publishedContactDetails(input.publicProfile);
229057
+ if (contact === null) return err$1({
228291
229058
  kind: "missing-source",
228292
229059
  sectionKind: section.kind
228293
229060
  });
228294
229061
  return ok$2({
228295
229062
  ...base,
228296
229063
  kind: section.kind,
228297
- contactLinks: contactLinks(input.publicProfile)
229064
+ contactLinks: contactLinks(contact)
228298
229065
  });
229066
+ }
228299
229067
  case "opening-hours": {
228300
229068
  const hours = openingHours(input.publicProfile);
228301
229069
  if (hours === null) return err$1({
@@ -228353,7 +229121,7 @@ function buildFooterRuntimeViewModel(input) {
228353
229121
  hasMainNavigation: input.hasFooterNavigation,
228354
229122
  hasLegalPolicyNavigation: input.hasLegalPolicyNavigation ?? false,
228355
229123
  hasBusinessAddress: input.businessAddress !== null && input.publicProfile.addressVisibility === "shown",
228356
- hasContact: input.publicProfile.contact !== null,
229124
+ hasContact: publishedContactDetails(input.publicProfile) !== null,
228357
229125
  hasOpeningHours: input.publicProfile.openingHours !== null,
228358
229126
  hasSocialLinks: input.publicProfile.socialLinks.length > 0,
228359
229127
  hasEngagementSource: hasResolvedEngagement
@@ -232306,4 +233074,4 @@ function useSiteChromeCustomizeOptional() {
232306
233074
  return useContext(SiteChromeCustomizeContext);
232307
233075
  }
232308
233076
  //#endregion
232309
- export { FooterComposerPanel as $, Pencil as $a, mapCurrentValueForSelect as $i, coerceLinkedContentEntriesPayload as $n, runTransforms as $r, useMultiAutosave as $t, markApplySaving as A, CollapsibleContent as Aa, makeBoundaryId as Ai, containerResponsiveThemeCss as An, compileTransitionRuntime as Ar, RichTextField as At, setPaletteColors as B, Button$1 as Ba, parseThemeForPersistence as Bi, compileManifestContentContract as Bn, curatedPalettes as Br, getBlockManifestFromSdkConfig as Bt, cancelPreview as C, Dialog as Ca, isObjectRecord$1 as Ci, deriveLaunchReadiness as Cn, retargetCompiledRuntimeFieldsForContentAdjacency as Cr, ButtonVariantField as Ct, isPreviewingChange as D, DialogHeader as Da, pageDesignEditorBackgroundOverrideOwner as Di, useApiMediaUploader as Dn, curatedSiteStyles as Dr, ReferenceField as Dt, dismissWarnings as E, DialogFooter as Ea, isThemeV2NormalEditorRawStyleFieldName as Ei, useMediaEditorOptional as En, componentRuntimeRulesForTheme as Er, MediaField as Et, previewFooterLook as F, DropdownMenuItem as Fa, updateButtonStyleHoverPreference as Fi, SITE_CHROME_VISIBLE_HEADER_SELECTOR as Fn, gradientToCss as Fr, LinkWidgetProvider as Ft, confirmAutosaveBatch as G, Undo2 as Ga, paletteModeSchema as Gi, escapeStyleContent as Gn, backgroundAppearanceColorTokenNames as Gr, canRedoHistory as Gt, syncStyleConfiguratorState as H, cn$2 as Ha, VARIANT_ROLES as Hi, buildThemeRuntime as Hn, sectionTransitionFamilies as Hr, useEditorUiStore as Ht, previewHeaderLayout as I, DropdownMenuLabel as Ia, updateButtonStyleSelectionChoice as Ii, SITE_OVERLAY_HEADER_CLEARANCE_CSS_VAR as In, GRADIENT_PRESETS as Ir, BlockEditor as It, idleEditorTransactionQueueState as J, Sparkles as Ja, getFieldIntent as Ji, defaultTransforms as Jn, ctaButton as Jr, pushHistoryEntry as Jt, deriveTransactionCapabilities as K, TriangleAlert as Ka, resolveSdkFooterMode as Ki, readBlockComposition as Kn, backgroundAppearanceQuickColorTokenNames as Kr, canUndoHistory as Kt, previewHeaderStyle as L, DropdownMenuSeparator as La, buttonStyleSelectionKey as Li, planSiteOverlayHeaderClearance as Ln, GRADIENT_PRESET_IDS as Lr, hideThemeV2RawStyleFields as Lt, palettePreviewProjection as M, Surface as Ma, buttonStyleSelectionParameterValue as Mi, makeDefaultBlockComponent as Mn, findFooterLook as Mr, EditorProvider as Mt, previewButtonStyle as N, DropdownMenu as Na, findButtonStyleRecipe as Ni, getBlockDefinition as Nn, getHeaderLayout as Nr, useEditor$1 as Nt, markApplyFailed as O, DialogTitle as Oa, asDesignBlockId as Oi, isSupportedLoaderEndpoint as On, designRuntimeRulesForTheme as Or, ReferenceWidgetProvider as Ot, previewCuratedPalette as P, DropdownMenuContent as Pa, resolveButtonStyleSelection as Pi, projectRecurringSchedule as Pn, getHeaderStyle as Pr, LinkField as Pt, resolvePreviewSidebarTab as Q, Redo2 as Qa, hasEmptyOption as Qi, LINKED_CONTENT_ENTRY_META_KEY as Qn, parseBackgroundContent as Qr, recordsEqual as Qt, previewSiteStyle as R, DropdownMenuTrigger as Ra, textColorStyle as Ri, visibleHeaderClearancePx as Rn, getGradientPreset as Rr, prepareBlockEditorManifest as Rt, applyTypographyDraftPatch as S, TabsTrigger as Sa, bookingConsentCarrierSchema as Si, DEFAULT_LAUNCH_READINESS_INPUT as Sn, retargetCompiledBoundaryRuntimeForContentAdjacency as Sr, EntryPickerWidgetProvider as St, deriveStyleConfiguratorCapabilities as T, DialogDescription as Ta, corners as Ti, MediaEditorContextProvider as Tn, planPageDesignLayoutLookCompatibility as Tr, FileField as Tt, toggleRow as U, X as Ua, paletteColorEntries as Ui, DEFAULT_THEME_RESPONSIVE_MODE as Un, sectionTransitionFamilyForBoundaryOptionId as Ur, useEditorUiStoreInstance as Ut, styleConfiguratorHistoryEntryForPersistedSelection as V, cva as Va, ORDINARY_TEXT_MIN_CONTRAST_RATIO as Vi, validateManifestDraft as Vn, defaultSectionTransitionVariantForFamily as Vr, classifyHistoryShortcut as Vt, beginAutosaveBatch as W, Upload as Wa, paletteColorsSchema as Wi, getResponsiveScopeSpec as Wn, sectionTransitionVariantForBoundaryOptionId as Wr, useOptionalEditorUiStoreInstance as Wt, resolveStyleConfiguratorInitialSnapshot as X, SlidersHorizontal as Xa, SdkConfigProvider as Xi, resolvePageRouteTarget as Xn, EDITABLE_SURFACE_FORWARDING_PROP as Xr, undoHistory as Xt, rejectAutosaveBatch as Y, Smartphone as Ya, isHeaderField as Yi, resolveEntryRouteTarget as Yn, bindingSchema as Yr, redoHistory as Yt, createOptimisticActionRunner as Z, RotateCcw as Za, useSdkConfig as Zi, resolveRouteTarget as Zn, planSiteChromePlacement as Zr, unsafeAsLinkedContentEntryId as Zt, reportPreviewDesignError as _, PopoverTrigger as _a, siblingPath$1 as _i, MediaEditor as _n, expectParsedBrandedId as _o, heroCopyFragment as _r, loadMailingListAuthoringContext as _t, previewDesignHistoryDomainForCommand as a, TooltipContent as aa, logger$1 as ai, parseReferenceKind as an, Image as ao, cardFragment as ar, useUISelector as at, planSiteChromeSelection as b, TabsContent as ba, eventCalendarListLayoutCompatibleDisplayModeValues as bi, listEntryPageContentSlots as bn, __exportAll as bo, applyCompiledRuntimeFieldsToContent as br, IconPickerField as bt, selectionSnapshotFromApplySiteStyleSelectionResponse as c, SelectContent as ca, isInlineTextEmpty as ci, resolveContentTitle as cn, EllipsisVertical as co, blogFeaturedPostFragment as cr, useEntryController as ct, PreviewDesignCoordinatorProvider as d, SelectValue as da, buildResolveImageUrlContext as di, ManifestForm as dn, ChevronDown as do, formCopyFragment as dr, createSiteOptionWidgetAdapters as dt, mapOptionValueForSelect as ea, parseBlockIconValue as ei, useDebouncedLatestSave as en, PenLine as eo, getLinkedContentEntryMeta as er, renderSiteChromeBlock as et, usePreviewDesignCoordinator as f, Callout as fa, getCurrentResolveImageUrlContext as fi, createManifestFormAdapter as fn, Check as fo, testimonialsCarouselFragment as fr, SiteOptionPickerField as ft, PREVIEW_DESIGN_SAVE_ERROR_MESSAGE as g, PopoverContent as ga, getAtPath as gi, createStoreBackend as gn, assertNever$10 as go, ctaRowFragment as gr, MailingListTargetingField as gt, PREVIEW_DESIGN_LOAD_ERROR_MESSAGE as h, Popover as ha, SECTION_BACKGROUND_MIN_SOURCE_WIDTH as hi, evaluateVisibility as hn, unsafeAsEditorBlockValues as ho, heroCtaRowFragment as hr, OfferingsPreviewWidgetProvider as ht, usePreviewDesignHistoryOrder as i, Tooltip as ia, renderInlineText as ii, isAdapterReferenceKind as in, LoaderCircle as io, headingFragment as ir, ContentFieldsForm as it, openRow as j, CollapsibleTrigger as ja, buttonStyleHoverPreferenceValue as ji, createCustomBlockRegistry as jn, surfaceResolutionContextFromTheme as jr, RichTextWidgetProvider as jt, markApplySaved as k, Collapsible as ka, assertNever$7 as ki, ThemeScope as kn, getCuratedSiteStyle as kr, useRichTextImagePicker as kt, deriveSiteStylePreviewUpdate as l, SelectItem as la, Text$2 as li, PaletteProvider as ln, ChevronRight as lo, singleButtonFragment as lr, useEntryEditorController as lt, usePreviewThemeOverlay as m, SearchInput as ma, setContextResolveImageUrlContext as mi, FormRenderer as mn, unsafeAsEditorBlockId as mo, ctaCopyFragment as mr, OfferingsPreviewGroupOrderField as mt, useSiteChromeCustomizeOptional as n, FieldFrame as na, parseGeneratedIconSetId as ni, createSnapshotFromPageRecord as nn, Monitor as no, ImageResolveContextProvider as nr, PreviewInlineTextEditor as nt, selectPreviewDesignHistoryDomain as o, TooltipTrigger as oa, coerceInlineTextWithFallback as oi, deriveRouteMetadataPresentation as on, GripVertical as oo, faqAccordionFragment as or, EditableBlockProvider as ot, PreviewThemeOverlayProvider as p, CalloutDescription as pa, resolveImageUrlWithContext as pi, createManifestValidation as pn, parseFooterModuleId as po, testimonialsHeadingFragment as pr, SiteOptionWidgetProvider as pt, enqueueTransaction as q, Tablet as qa, attachRenderableMediaAssetFields as qi, getDefaultComponentRegistry as qn, parseBackgroundAppearanceColorToken as qr, createHistoryState as qt, PreviewDesignHistoryOrderProvider as r, useField as ra, ThemedButtonSpecimen as ri, BUILT_IN_REFERENCE_KINDS as rn, Maximize2 as ro, richTextFragment as rr, PreviewTextEditor as rt, buildApplySiteStyleSelectionBody as s, Select$1 as sa, inlineTextToPlainText as si, resolvePageRouteSlug as sn, Eye as so, faqHeadingFragment as sr, EntryControllerProvider as st, SiteChromeCustomizeProvider as t, unmapValueFromSelect as ta, parseGeneratedIconAssetId as ti, createRoutingHelpers as tn, Palette as to, clientComponentRegistry as tr, PreviewRichTextEditor as tt, planConfiguratorPreview as u, SelectTrigger as ua, RichText as ui, usePalette as un, ChevronLeft as uo, formEmbedFragment as ur, createSiteOptionApiLoader as ut, buildSiteChromeSelectionModels as v, Input as va, BlockApiProvider as vi, applyPageDesignEditorAction as vn, parseMediaAssetId as vo, bodyCopyFragment as vr, MailingListTargetingProvider as vt, createStyleConfiguratorState as w, DialogContent as wa, isRecord$17 as wi, AMBIENT_BACKGROUND_ROLES as wn, formatSiteChromeBackgroundTokenLabel as wr, ButtonWidgetProvider as wt, applyResolvedSelections as x, TabsList as xa, eventCalendarListLayoutOptions as xi, decodePublicProductCategorySelectorQuery as xn, compileEditorModelRuntime as xr, EntryPickerField as xt, cycleSiteChromeSelectionId as y, Tabs as ya, calendarWindowQuerySchema as yi, getPageDesignEditorState as yn, parseSiteId as yo, defineFragment as yr, InlineTextField as yt, resetTypographyToSaved as z, Primitive as za, defaultThemeTokens as zi, resolveVariantKey as zn, parseGradientPresetId as zr, stripTopLevelRoutingUi as zt };
233077
+ export { FooterComposerPanel as $, Tablet as $a, attachRenderableMediaAssetFields as $i, LINKED_CONTENT_ENTRY_META_KEY as $n, ctaButton as $r, useMultiAutosave as $t, markApplySaving as A, DialogContent as Aa, corners as Ai, containerResponsiveThemeCss as An, surfaceResolutionContextFromTheme as Ar, RichTextField as At, setPaletteColors as B, DropdownMenuContent as Ba, resolveButtonStyleSelection as Bi, compileManifestContentContract as Bn, autoDesignChoice as Br, getBlockManifestFromSdkConfig as Bt, cancelPreview as C, PopoverTrigger as Ca, BlockApiProvider as Ci, deriveLaunchReadiness as Cn, expectParsedBrandedId as Co, formatSiteChromeBackgroundTokenLabel as Cr, ButtonVariantField as Ct, isPreviewingChange as D, TabsList as Da, bookingConsentCarrierSchema as Di, useApiMediaUploader as Dn, curatedSiteStyles as Dr, ReferenceField as Dt, dismissWarnings as E, TabsContent as Ea, eventCalendarListLayoutOptions as Ei, useMediaEditorOptional as En, __exportAll as Eo, componentRuntimeRulesForTheme as Er, MediaField as Et, previewFooterLook as F, Collapsible as Fa, assertNever$7 as Fi, SITE_CHROME_VISIBLE_HEADER_SELECTOR as Fn, GRADIENT_PRESETS as Fr, LinkWidgetProvider as Ft, confirmAutosaveBatch as G, Primitive as Ga, defaultThemeTokens as Gi, escapeStyleContent as Gn, boundaryTreatmentForBoundaryOptionId as Gr, canRedoHistory as Gt, syncStyleConfiguratorState as H, DropdownMenuLabel as Ha, updateButtonStyleSelectionChoice as Hi, buildThemeRuntime as Hn, boundaryLayout as Hr, useEditorUiStore as Ht, previewHeaderLayout as I, CollapsibleContent as Ia, makeBoundaryId as Ii, SITE_OVERLAY_HEADER_CLEARANCE_CSS_VAR as In, GRADIENT_PRESET_IDS as Ir, BlockEditor as It, idleEditorTransactionQueueState as J, cn$2 as Ja, VARIANT_ROLES as Ji, getDefaultComponentRegistry as Jn, sectionTransitionFamilyForBoundaryOptionId as Jr, pushHistoryEntry as Jt, deriveTransactionCapabilities as K, Button$1 as Ka, parseThemeForPersistence as Ki, mergeContentWithBlockStyleOverrides as Kn, defaultSectionTransitionVariantForFamily as Kr, canUndoHistory as Kt, previewHeaderStyle as L, CollapsibleTrigger as La, buttonStyleHoverPreferenceValue as Li, planSiteOverlayHeaderClearance as Ln, getGradientPreset as Lr, hideThemeV2RawStyleFields as Lt, palettePreviewProjection as M, DialogFooter as Ma, pageDesignEditorBackgroundOverrideOwner as Mi, makeDefaultBlockComponent as Mn, getHeaderLayout as Mr, EditorProvider as Mt, previewButtonStyle as N, DialogHeader as Na, stripStaleTransitionStyleGroup as Ni, getBlockDefinition as Nn, getHeaderStyle as Nr, useEditor$1 as Nt, markApplyFailed as O, TabsTrigger as Oa, isObjectRecord$1 as Oi, isSupportedLoaderEndpoint as On, designRuntimeRulesForTheme as Or, ReferenceWidgetProvider as Ot, previewCuratedPalette as P, DialogTitle as Pa, asDesignBlockId as Pi, projectRecurringSchedule as Pn, gradientToCss as Pr, LinkField as Pt, resolvePreviewSidebarTab as Q, TriangleAlert as Qa, resolveSdkFooterMode as Qi, resolveRouteTarget as Qn, parseBackgroundAppearanceColorToken as Qr, recordsEqual as Qt, previewSiteStyle as R, Surface as Ra, buttonStyleSelectionParameterValue as Ri, visibleHeaderClearancePx as Rn, parseGradientPresetId as Rr, prepareBlockEditorManifest as Rt, applyTypographyDraftPatch as S, PopoverContent as Sa, siblingPath$1 as Si, DEFAULT_LAUNCH_READINESS_INPUT as Sn, assertNever$10 as So, compileEditorModelRuntime as Sr, EntryPickerWidgetProvider as St, deriveStyleConfiguratorCapabilities as T, Tabs as Ta, eventCalendarListLayoutCompatibleDisplayModeValues as Ti, MediaEditorContextProvider as Tn, parseSiteId as To, resolvePageDesignEditorBoundaryForChoice as Tr, FileField as Tt, toggleRow as U, DropdownMenuSeparator as Ua, buttonStyleSelectionKey as Ui, DEFAULT_THEME_RESPONSIVE_MODE as Un, composeBoundary as Ur, useEditorUiStoreInstance as Ut, styleConfiguratorHistoryEntryForPersistedSelection as V, DropdownMenuItem as Va, updateButtonStyleHoverPreference as Vi, validateManifestDraft as Vn, userPinnedDesignChoice as Vr, classifyHistoryShortcut as Vt, beginAutosaveBatch as W, DropdownMenuTrigger as Wa, textColorStyle as Wi, getResponsiveScopeSpec as Wn, sectionFactsForContent as Wr, useOptionalEditorUiStoreInstance as Wt, resolveStyleConfiguratorInitialSnapshot as X, Upload as Xa, paletteColorsSchema as Xi, resolveEntryRouteTarget as Xn, backgroundAppearanceColorTokenNames as Xr, undoHistory as Xt, rejectAutosaveBatch as Y, X as Ya, paletteColorEntries as Yi, defaultTransforms as Yn, sectionTransitionVariantForBoundaryOptionId as Yr, redoHistory as Yt, createOptimisticActionRunner as Z, Undo2 as Za, paletteModeSchema as Zi, resolvePageRouteTarget as Zn, backgroundAppearanceQuickColorTokenNames as Zr, unsafeAsLinkedContentEntryId as Zt, reportPreviewDesignError as _, SelectValue as _a, getCurrentResolveImageUrlContext as _i, MediaEditor as _n, ChevronDown as _o, ctaRowFragment as _r, loadMailingListAuthoringContext as _t, previewDesignHistoryDomainForCommand as a, mapCurrentValueForSelect as aa, parseBlockIconValue as ai, parseReferenceKind as an, Pencil as ao, headingFragment as ar, useUISelector as at, planSiteChromeSelection as b, SearchInput as ba, SECTION_BACKGROUND_MIN_SOURCE_WIDTH as bi, listEntryPageContentSlots as bn, unsafeAsEditorBlockId as bo, defineFragment as br, IconPickerField as bt, selectionSnapshotFromApplySiteStyleSelectionResponse as c, FieldFrame as ca, ThemedButtonSpecimen as ci, resolveContentTitle as cn, Monitor as co, faqHeadingFragment as cr, useEntryController as ct, PreviewDesignCoordinatorProvider as d, TooltipContent as da, coerceInlineTextWithFallback as di, ManifestForm as dn, Image as do, formEmbedFragment as dr, createSiteOptionWidgetAdapters as dt, getFieldIntent as ea, bindingSchema as ei, useDebouncedLatestSave as en, Sparkles as eo, coerceLinkedContentEntriesPayload as er, renderSiteChromeBlock as et, usePreviewDesignCoordinator as f, TooltipTrigger as fa, inlineTextToPlainText as fi, createManifestFormAdapter as fn, GripVertical as fo, formCopyFragment as fr, SiteOptionPickerField as ft, PREVIEW_DESIGN_SAVE_ERROR_MESSAGE as g, SelectTrigger as ga, buildResolveImageUrlContext as gi, createStoreBackend as gn, ChevronLeft as go, heroCtaRowFragment as gr, MailingListTargetingField as gt, PREVIEW_DESIGN_LOAD_ERROR_MESSAGE as h, SelectItem as ha, RichText as hi, evaluateVisibility as hn, ChevronRight as ho, ctaCopyFragment as hr, OfferingsPreviewWidgetProvider as ht, usePreviewDesignHistoryOrder as i, hasEmptyOption as ia, runTransforms as ii, isAdapterReferenceKind as in, Redo2 as io, richTextFragment as ir, ContentFieldsForm as it, openRow as j, DialogDescription as ja, isThemeV2NormalEditorRawStyleFieldName as ji, createCustomBlockRegistry as jn, findFooterLook as jr, RichTextWidgetProvider as jt, markApplySaved as k, Dialog as ka, isRecord$18 as ki, ThemeScope as kn, getCuratedSiteStyle as kr, useRichTextImagePicker as kt, deriveSiteStylePreviewUpdate as l, useField as la, renderInlineText as li, PaletteProvider as ln, Maximize2 as lo, blogFeaturedPostFragment as lr, useEntryEditorController as lt, usePreviewThemeOverlay as m, SelectContent as ma, Text$2 as mi, FormRenderer as mn, EllipsisVertical as mo, testimonialsHeadingFragment as mr, OfferingsPreviewGroupOrderField as mt, useSiteChromeCustomizeOptional as n, SdkConfigProvider as na, planSiteChromePlacement as ni, createSnapshotFromPageRecord as nn, SlidersHorizontal as no, clientComponentRegistry as nr, PreviewInlineTextEditor as nt, selectPreviewDesignHistoryDomain as o, mapOptionValueForSelect as oa, parseGeneratedIconAssetId as oi, deriveRouteMetadataPresentation as on, PenLine as oo, cardFragment as or, EditableBlockProvider as ot, PreviewThemeOverlayProvider as p, Select$1 as pa, isInlineTextEmpty as pi, createManifestValidation as pn, Eye as po, testimonialsCarouselFragment as pr, SiteOptionWidgetProvider as pt, enqueueTransaction as q, cva as qa, ORDINARY_TEXT_MIN_CONTRAST_RATIO as qi, readBlockComposition as qn, sectionTransitionFamilies as qr, createHistoryState as qt, PreviewDesignHistoryOrderProvider as r, useSdkConfig as ra, parseBackgroundContent as ri, BUILT_IN_REFERENCE_KINDS as rn, RotateCcw as ro, ImageResolveContextProvider as rr, PreviewTextEditor as rt, buildApplySiteStyleSelectionBody as s, unmapValueFromSelect as sa, parseGeneratedIconSetId as si, resolvePageRouteSlug as sn, Palette as so, faqAccordionFragment as sr, EntryControllerProvider as st, SiteChromeCustomizeProvider as t, isHeaderField as ta, EDITABLE_SURFACE_FORWARDING_PROP as ti, createRoutingHelpers as tn, Smartphone as to, getLinkedContentEntryMeta as tr, PreviewRichTextEditor as tt, planConfiguratorPreview as u, Tooltip as ua, logger$1 as ui, usePalette as un, LoaderCircle as uo, singleButtonFragment as ur, createSiteOptionApiLoader as ut, buildSiteChromeSelectionModels as v, Callout as va, resolveImageUrlWithContext as vi, applyPageDesignEditorAction as vn, Check as vo, heroCopyFragment as vr, MailingListTargetingProvider as vt, createStyleConfiguratorState as w, Input as wa, calendarWindowQuerySchema as wi, AMBIENT_BACKGROUND_ROLES as wn, parseMediaAssetId as wo, planPageDesignLayoutLookCompatibility as wr, ButtonWidgetProvider as wt, applyResolvedSelections as x, Popover as xa, getAtPath as xi, decodePublicProductCategorySelectorQuery as xn, unsafeAsEditorBlockValues as xo, applyCompiledRuntimeFieldsToContent as xr, EntryPickerField as xt, cycleSiteChromeSelectionId as y, CalloutDescription as ya, setContextResolveImageUrlContext as yi, getPageDesignEditorState as yn, parseFooterModuleId as yo, bodyCopyFragment as yr, InlineTextField as yt, resetTypographyToSaved as z, DropdownMenu as za, findButtonStyleRecipe as zi, resolveVariantKey as zn, curatedPalettes as zr, stripTopLevelRoutingUi as zt };