@riverbankcms/sdk 0.140.0 → 0.142.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 (145) hide show
  1. package/dist/{PreviewEditorSidebar-CUyTzd5z.mjs → PreviewEditorSidebar-Dk6Dt11f.mjs} +24 -15
  2. package/dist/{PreviewEditorUI-Agkw4v5i.mjs → PreviewEditorUI-D6EQTsCA.mjs} +3 -3
  3. package/dist/{PreviewQueryProvider-CN0ewic5.mjs → PreviewQueryProvider-CC-EPkyQ.mjs} +1 -1
  4. package/dist/{SdkPreviewModeRuntime-DdbeAdS8.mjs → SdkPreviewModeRuntime-DH1n4ytY.mjs} +85 -54
  5. package/dist/{SiteChromeCustomizeContext-CqGOz-Kv.mjs → SiteChromeCustomizeContext-DunIzY0K.mjs} +797 -216
  6. package/dist/_dts/api/src/billing.d.ts +6 -1
  7. package/dist/_dts/api/src/bookingConfig.d.ts +1 -1
  8. package/dist/_dts/api/src/commercePortalEndpoints.d.ts +2 -2
  9. package/dist/_dts/api/src/contentRuntime.d.ts +12 -31
  10. package/dist/_dts/api/src/domainConnection.d.ts +4 -0
  11. package/dist/_dts/api/src/endpoints.d.ts +0 -27
  12. package/dist/_dts/api/src/footerCompositionContracts.d.ts +25 -0
  13. package/dist/_dts/api/src/index.d.ts +2 -2
  14. package/dist/_dts/api/src/launchReadiness.d.ts +1 -0
  15. package/dist/_dts/api/src/navigation/dashboardSiteNav.d.ts +2 -1
  16. package/dist/_dts/api/src/publicBookingService.d.ts +1 -0
  17. package/dist/_dts/api/src/sdkConfig/layout.d.ts +13 -0
  18. package/dist/_dts/api/src/sdkContracts.d.ts +3 -8
  19. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +3 -26
  20. package/dist/_dts/api/src/siteOnboarding.d.ts +7 -0
  21. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +1 -31
  22. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +6 -1
  23. package/dist/_dts/billing/src/site-arrangements.d.ts +18 -5
  24. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +4 -0
  25. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +2 -0
  26. package/dist/_dts/blocks/src/system/blocks/events/event-calendar.d.ts +2 -0
  27. package/dist/_dts/blocks/src/system/blocks/events/eventSelectionFields.d.ts +122 -0
  28. package/dist/_dts/blocks/src/system/blocks/events/shared/calendarWindow.d.ts +2 -1
  29. package/dist/_dts/blocks/src/system/blocks/events/shared/eventSelection.d.ts +11 -0
  30. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +56 -0
  31. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +8 -0
  32. package/dist/_dts/blocks/src/system/blocks/location-map.d.ts +2 -2
  33. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +47 -47
  34. package/dist/_dts/blocks/src/system/data/prefetchBlockData.d.ts +5 -0
  35. package/dist/_dts/blocks/src/system/data/utils.d.ts +1 -1
  36. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +27 -1
  37. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +12 -180
  38. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +6 -90
  39. package/dist/_dts/blocks/src/system/runtime/hooks/useEventFilters.d.ts +2 -1
  40. package/dist/_dts/blocks/src/system/runtime/nodes/appointment-services.d.ts +5 -1
  41. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +4 -0
  42. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.state.d.ts +5 -0
  43. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useDiscountCodeUi.d.ts +3 -0
  44. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useDiscountCodeUi.state.d.ts +10 -3
  45. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventCheckoutHandoffRestore.d.ts +9 -0
  46. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +4 -5
  47. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useSettledCommerceView.d.ts +37 -0
  48. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +4 -3
  49. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.interactive.d.ts +3 -0
  50. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventListing.interactive.d.ts +11 -101
  51. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +63 -2
  52. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listOptions.d.ts +2 -0
  53. package/dist/_dts/blocks/src/system/transforms/registry/eventCalendar.d.ts +12 -0
  54. package/dist/_dts/commerce/src/purchase/bookingInstalmentSchedule.d.ts +4 -0
  55. package/dist/_dts/commerce/src/purchase/stripeCommissionMetadata.d.ts +1 -2
  56. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +3102 -3122
  57. package/dist/_dts/editor-blocks/src/block-editor/ColourFieldOptionsProvider.d.ts +7 -0
  58. package/dist/_dts/editor-blocks/src/block-editor/colourFieldVisibility.d.ts +7 -0
  59. package/dist/_dts/editor-blocks/src/block-editor/preparedManifest.d.ts +2 -0
  60. package/dist/_dts/editor-blocks/src/index.d.ts +1 -0
  61. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/loaders.d.ts +27 -1
  62. package/dist/_dts/preview-next/src/client/preview/PreviewEditorSidebar.d.ts +2 -2
  63. package/dist/_dts/preview-next/src/client/preview/PreviewFooterEditorPanel.d.ts +3 -6
  64. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +2 -2
  65. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +2 -2
  66. package/dist/_dts/preview-next/src/client/preview/footerInspection.d.ts +16 -0
  67. package/dist/_dts/preview-next/src/client/preview/sdkConfig.d.ts +6 -0
  68. package/dist/_dts/sdk/src/config/types.d.ts +2 -18
  69. package/dist/_dts/sdk/src/config/validation.d.ts +6 -0
  70. package/dist/_dts/sdk/src/public-api/contracts.d.ts +4 -1
  71. package/dist/_dts/sdk/src/version.d.ts +1 -1
  72. package/dist/_dts/theme-core/src/ThemeFontLoader.d.ts +17 -1
  73. package/dist/_dts/theme-core/src/buttons/affordanceSuppression.d.ts +17 -0
  74. package/dist/_dts/theme-core/src/buttons/buttonColourContract.d.ts +1 -1
  75. package/dist/_dts/theme-core/src/buttons/classNames.d.ts +16 -0
  76. package/dist/_dts/theme-core/src/buttons/generateButtonCss.d.ts +0 -6
  77. package/dist/_dts/theme-core/src/buttons/resolvedButtonCss.d.ts +4 -4
  78. package/dist/_dts/theme-core/src/footer/mode.d.ts +10 -1
  79. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  80. package/dist/_dts/theme-core/src/hosted-booking-appearance.d.ts +54 -12
  81. package/dist/_dts/theme-core/src/index.d.ts +1 -1
  82. package/dist/_dts/theme-core/src/schema.d.ts +1 -7
  83. package/dist/_dts/theme-core/src/site-styles/effectivePageDesign.d.ts +32 -0
  84. package/dist/_dts/theme-core/src/site-styles/index.d.ts +1 -0
  85. package/dist/client/{BookingConsentAgreement-CuKa82V4.mjs → BookingConsentAgreement-CnJeq3_r.mjs} +2 -2
  86. package/dist/client/{DiscountCodeDisclosure-BbYieKWB.mjs → DiscountCodeDisclosure-BINEO6J9.mjs} +1 -1
  87. package/dist/client/{EventCombined.client-LTfR1Aby.mjs → EventCombined.client-srxNQ6sZ.mjs} +11 -15
  88. package/dist/client/{EventListing.client-DUHpYSuo.mjs → EventListing.client-DbyVyGkX.mjs} +4 -17
  89. package/dist/client/{LeafletStaticMap-CHRupL6m.mjs → LeafletStaticMap-B-wpijzL.mjs} +1 -1
  90. package/dist/client/{accordion-6HiIFxNL.mjs → accordion-B-iUXU7n.mjs} +1 -1
  91. package/dist/client/{bookingForm-Dlmid43H.mjs → bookingForm-BXYPke5q.mjs} +5 -5
  92. package/dist/client/bookings.mjs +194 -102
  93. package/dist/client/{carousel-DmkZRRq_.mjs → carousel-Cr4aj63H.mjs} +1 -1
  94. package/dist/client/{cart-CIC24330.mjs → cart-DibP9WWa.mjs} +3 -3
  95. package/dist/client/{checkout-D1Zw9imo.mjs → checkout-CrSBVnL8.mjs} +3 -3
  96. package/dist/client/client.mjs +643 -192
  97. package/dist/client/{context-Cy1OayHx.mjs → context-Ces-AgdR.mjs} +1 -1
  98. package/dist/client/{dates-aDcfoLHr.mjs → dates-BXyNeNhB.mjs} +2 -2
  99. package/dist/client/{eventCalendar-FQxMPAjf.mjs → eventCalendar-HwpPeqrH.mjs} +11 -7
  100. package/dist/client/{eventCombined-Dr3ac6Hb.mjs → eventCombined-BpFUkuEI.mjs} +3 -3
  101. package/dist/client/{eventDetails-D-AzUHFQ.mjs → eventDetails-DK_agiWy.mjs} +4 -4
  102. package/dist/client/{eventListing-B3XxBf3i.mjs → eventListing-DhzHqP1c.mjs} +3 -3
  103. package/dist/client/{eventRegistration-I6EaokFD.mjs → eventRegistration-BdQ_Dn2d.mjs} +124 -32
  104. package/dist/client/{form-BbWckVWY.mjs → form-B1frcuzy.mjs} +2 -2
  105. package/dist/client/{headerSection-C9qU4Y7x.mjs → headerSection-CT5z460s.mjs} +2 -2
  106. package/dist/client/hooks.mjs +274 -45
  107. package/dist/client/{locationMap-CLCShvlG.mjs → locationMap-CIQXB9i2.mjs} +2 -2
  108. package/dist/client/{newsletterForm-Briukxzi.mjs → newsletterForm-BYNYZSes.mjs} +23 -10
  109. package/dist/client/{pricing-Copadu-2.mjs → pricing-CbTlAsmt.mjs} +1 -1
  110. package/dist/client/{productDetail-oujlRd-r.mjs → productDetail-CR7Z3yjS.mjs} +3 -3
  111. package/dist/client/{productList-BRiIRUcd.mjs → productList-DXj00vzR.mjs} +3 -3
  112. package/dist/client/{propsDecoder-CB2KS_ko.mjs → propsDecoder-DiLiBdek.mjs} +57 -16
  113. package/dist/client/rendering/client.mjs +476 -132
  114. package/dist/client/rendering/islands.mjs +17 -17
  115. package/dist/client/rendering.mjs +686 -195
  116. package/dist/client/{scrollToBlock-B1IS1_ua.mjs → scrollToBlock-CrcbBf0h.mjs} +28 -11
  117. package/dist/client/{shop-Vn6yNFED.mjs → shop-DgqiB_1b.mjs} +6 -6
  118. package/dist/client/{shop-commerce-BVTqipYp.mjs → shop-commerce-D_J781in.mjs} +5 -5
  119. package/dist/client/{shop-commerce-store-BVnJVy7v.mjs → shop-commerce-store-PGUbDwqe.mjs} +1 -1
  120. package/dist/client/{shopShared-COIeK77T.mjs → shopShared-C_Bb2zwG.mjs} +3 -3
  121. package/dist/client/{useEventFilters-DzIUcS9n.mjs → useEventFilters-CM17Xpr1.mjs} +17 -11
  122. package/dist/preview-next/before-render.mjs +0 -37
  123. package/dist/preview-next/client/runtime.mjs +4 -4
  124. package/dist/preview-next/editor-chrome.css +4 -8
  125. package/dist/preview-next/keepAlive.mjs +0 -37
  126. package/dist/preview-next/middleware.mjs +0 -37
  127. package/dist/{previewAuthStore-Gjb6r6vz.mjs → previewAuthStore-oVttoRDO.mjs} +0 -37
  128. package/dist/{sdk-runtime-CnCLvZRv.mjs → sdk-runtime-DhmuRhIX.mjs} +2 -2
  129. package/dist/{separator-DKDRJ1oH.mjs → separator-C3hlg9LH.mjs} +1 -1
  130. package/dist/server/components.mjs +452 -111
  131. package/dist/server/config-validation.mjs +263 -40
  132. package/dist/server/config.mjs +263 -40
  133. package/dist/server/data.mjs +274 -45
  134. package/dist/server/index.mjs +1 -38
  135. package/dist/server/next.mjs +427 -119
  136. package/dist/server/page-converter.mjs +208 -33
  137. package/dist/server/prebuild.mjs +1 -1
  138. package/dist/server/rendering/server.mjs +467 -119
  139. package/dist/server/rendering.mjs +467 -117
  140. package/dist/server/routing.mjs +301 -50
  141. package/dist/server/server.mjs +275 -83
  142. package/dist/server/theme-bridge.mjs +57 -23
  143. package/dist/server/theme.mjs +11 -9
  144. package/dist/styles/index.css +28 -2
  145. package/package.json +1 -1
@@ -2003,6 +2003,7 @@ function isDisplayMediaKind(kind) {
2003
2003
  }
2004
2004
  const siteOptionSources = [
2005
2005
  "eventCategories",
2006
+ "eventSeries",
2006
2007
  "venues",
2007
2008
  "staffMembers"
2008
2009
  ];
@@ -6687,14 +6688,12 @@ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme) {
6687
6688
  const shadowValue = generateShadowValue$1(shadow);
6688
6689
  rules.push(`box-shadow: ${shadowValue}`);
6689
6690
  }
6690
- rules.push(`display: flex`);
6691
- rules.push(`flex-direction: column`);
6692
- rules.push(`position: relative`);
6693
- rules.push(`overflow: visible`);
6691
+ const structureSelector = cardClasses.map((cardClass) => `:where([data-theme-scope="${themeId}"]) :where(.${cardClass})`).join(",\n");
6694
6692
  const mediaRadius = MEDIA_CORNER_MAP[corners];
6695
6693
  const mediaRules = [`border-top-left-radius: ${mediaRadius}`, `border-top-right-radius: ${mediaRadius}`];
6696
6694
  const contentRules = [`padding: ${resolveContentPadding(variant)}`];
6697
6695
  return [
6696
+ `${structureSelector} {\n display: flex;\n flex-direction: column;\n position: relative;\n overflow: visible;\n}`,
6698
6697
  `${containerSelector} {\n ${rules.join(";\n ")};\n}`,
6699
6698
  ...cardDecorationPseudoRules(containerSelectors, variant.decorations, resolveColor),
6700
6699
  `${fallbackBackgroundSelector} {\n background: transparent;\n}`,
@@ -12138,6 +12137,54 @@ function defaultTypographyForRecipe(recipe) {
12138
12137
  }
12139
12138
  }
12140
12139
  //#endregion
12140
+ //#region ../theme-core/src/footer/mode.ts
12141
+ const footerModeSchema = z.enum([
12142
+ "default",
12143
+ "blocks",
12144
+ "default+blocks",
12145
+ "none"
12146
+ ]);
12147
+ /** Reads only SDK footer intent; unrelated config and theme fields are not authorities. */
12148
+ function resolveSdkFooterMode(config) {
12149
+ const parsed = z.object({ layout: z.object({ footerMode: footerModeSchema.optional() }).optional() }).safeParse(config);
12150
+ return parsed.success ? parsed.data.layout?.footerMode ?? "default" : "default";
12151
+ }
12152
+ /**
12153
+ * Resolve what to render for the footer given the current mode and whether the
12154
+ * site is SDK-managed.
12155
+ *
12156
+ * Backwards compatible: missing/undefined mode is treated as 'default'.
12157
+ */
12158
+ function resolveFooterRenderPlan(params) {
12159
+ if (!params.isSdkSite) return {
12160
+ mode: "default",
12161
+ renderDefaultFooter: true,
12162
+ renderFooterBlocks: false
12163
+ };
12164
+ switch (params.mode ?? "default") {
12165
+ case "blocks": return {
12166
+ mode: "blocks",
12167
+ renderDefaultFooter: false,
12168
+ renderFooterBlocks: true
12169
+ };
12170
+ case "default+blocks": return {
12171
+ mode: "default+blocks",
12172
+ renderDefaultFooter: true,
12173
+ renderFooterBlocks: true
12174
+ };
12175
+ case "none": return {
12176
+ mode: "none",
12177
+ renderDefaultFooter: false,
12178
+ renderFooterBlocks: false
12179
+ };
12180
+ default: return {
12181
+ mode: "default",
12182
+ renderDefaultFooter: true,
12183
+ renderFooterBlocks: false
12184
+ };
12185
+ }
12186
+ }
12187
+ //#endregion
12141
12188
  //#region ../theme-core/src/lib/media.ts
12142
12189
  const hotspotSchema = z.object({
12143
12190
  x: z.number().min(0).max(1),
@@ -12700,7 +12747,7 @@ const BUTTON_CONTRAST_POLICY = {
12700
12747
  nonTextMinimumRatio: 3,
12701
12748
  gradientSamplesPerSegment: 20,
12702
12749
  alphaBackgrounds: "reject-without-known-surface",
12703
- disabled: "retain-full-colour-contract"
12750
+ disabled: "validated-colour-pair-with-muted-control-opacity"
12704
12751
  };
12705
12752
  function formatButtonColourContractError(error) {
12706
12753
  switch (error.tag) {
@@ -13288,6 +13335,25 @@ function parseThemeForPersistence(value) {
13288
13335
  };
13289
13336
  }
13290
13337
  //#endregion
13338
+ //#region ../theme-core/src/buttons/affordanceSuppression.ts
13339
+ /**
13340
+ * Internal: the opt-out the generated affordance rule is written against.
13341
+ *
13342
+ * A button style may declare a trailing `affordance`, which the theme renders as
13343
+ * a CSS `::after` on every button of that variant. An authored button that
13344
+ * renders its own trailing icon must not also receive the theme's, or it shows
13345
+ * two.
13346
+ *
13347
+ * Deliberately not exported from `./index`. This used to be public, and two
13348
+ * block components reached past the theme to draw arrows of their own — one of
13349
+ * them doubling up on the styles that declare an affordance, both of them
13350
+ * overriding the twelve styles that deliberately declare none. Whether a button
13351
+ * carries a trailing affordance is the style's decision; the only caller that
13352
+ * legitimately opts out is the authored-button renderer, which reaches it
13353
+ * through `themeButtonClassNameWithTrailingIcon`.
13354
+ */
13355
+ const AFFORDANCE_SUPPRESS_CLASS = "rb-affordance-off";
13356
+ //#endregion
13291
13357
  //#region ../theme-core/src/buttons/classNames.ts
13292
13358
  /**
13293
13359
  * Parse content/runtime variant identifiers into the closed theme-button class
@@ -13320,6 +13386,27 @@ function themeButtonClassName(spec) {
13320
13386
  spec.extraClassName
13321
13387
  ].filter(Boolean).join(" ");
13322
13388
  }
13389
+ /**
13390
+ * Button classes for an authored button that renders its own trailing icon.
13391
+ *
13392
+ * A separate function rather than a flag on `themeButtonClassName`, because
13393
+ * there is exactly one caller and it should stay visible in a diff. The name
13394
+ * states what the caller knows to be true of its own markup — that it renders a
13395
+ * trailing icon — rather than what the theme does about it. The consequence is
13396
+ * the theme's to decide and to change; a caller naming the consequence would be
13397
+ * asserting a policy it does not own, and would quietly become a lie the moment
13398
+ * that policy moved.
13399
+ *
13400
+ * Do not reach for this to remove an arrow you simply do not want. A trailing
13401
+ * affordance is declared by the button style, and a block overriding that is the
13402
+ * defect this replaced.
13403
+ */
13404
+ function themeButtonClassNameWithTrailingIcon(spec) {
13405
+ return themeButtonClassName({
13406
+ ...spec,
13407
+ extraClassName: [spec.extraClassName, AFFORDANCE_SUPPRESS_CLASS].filter(Boolean).join(" ")
13408
+ });
13409
+ }
13323
13410
  //#endregion
13324
13411
  //#region ../theme-core/src/interactive/generateBaseCSS.ts
13325
13412
  /**
@@ -15232,26 +15319,19 @@ function resolvedButtonFocusDeclarations(indicator, role) {
15232
15319
  ] : [["outline", value("outline", `3px solid ${indicator.colorCss}`)], ["outline-offset", value("outline-offset", "3px")]];
15233
15320
  }
15234
15321
  /**
15235
- * Disabled buttons retain their validated colour pair and add a non-layout
15236
- * visual affordance. `currentColor` keeps the cue inside the same contrast
15237
- * relationship, while retaining pointer events lets the not-allowed cursor
15238
- * remain observable.
15322
+ * Disabled buttons are deliberately muted as unavailable controls. Native
15323
+ * disabled controls are exempt from active-control contrast requirements;
15324
+ * opacity distinguishes the state without introducing a focus-like outline.
15325
+ * Retaining pointer events lets the not-allowed cursor remain observable.
15239
15326
  */
15240
15327
  const RESOLVED_BUTTON_DISABLED_DECLARATIONS = [
15241
15328
  ["cursor", "not-allowed"],
15242
15329
  ["transform", "none"],
15243
15330
  ["box-shadow", "none"],
15244
- ["outline", "2px dashed currentColor"],
15245
- ["outline-offset", "-4px"]
15331
+ ["opacity", "0.55"]
15246
15332
  ];
15247
15333
  //#endregion
15248
15334
  //#region ../theme-core/src/buttons/generateButtonCss.ts
15249
- /**
15250
- * Class the `button` runtime node adds when it renders its own trailing icon,
15251
- * so a node-level icon suppresses the theme's CSS `::after` affordance.
15252
- * Shared so generator and renderer can't drift.
15253
- */
15254
- const AFFORDANCE_SUPPRESS_CLASS = "rb-affordance-off";
15255
15335
  const BUTTON_SIZE_NAMES = buttonSizeNameSchema.options;
15256
15336
  /**
15257
15337
  * Letter spacing token → CSS value.
@@ -17224,12 +17304,6 @@ const footerVariant = z.enum([
17224
17304
  "split"
17225
17305
  ]);
17226
17306
  const footerMaxWidth = z.enum(["container", "full"]);
17227
- const footerMode = z.enum([
17228
- "default",
17229
- "blocks",
17230
- "default+blocks",
17231
- "none"
17232
- ]);
17233
17307
  const footerNavLayoutMode = z.enum([
17234
17308
  "stack",
17235
17309
  "inline",
@@ -17318,9 +17392,9 @@ const footerSchema = z.object({
17318
17392
  /**
17319
17393
  * SDK-only footer rendering mode.
17320
17394
  *
17321
- * Backwards compatible: missing/undefined behaves as 'default'.
17395
+ * Historical persisted field; SDK rendering mode now lives in layout.footerMode.
17322
17396
  */
17323
- mode: footerMode.optional(),
17397
+ mode: footerModeSchema.optional(),
17324
17398
  variant: footerVariant,
17325
17399
  maxWidth: footerMaxWidth,
17326
17400
  showLogoText: z.boolean().optional(),
@@ -27202,6 +27276,15 @@ const publicEventBaseSchema = z.object({
27202
27276
  timeZone: z.string(),
27203
27277
  pricing: publicEventPricingSchema,
27204
27278
  paymentTerms: blockBookingPaymentTermsSchema,
27279
+ cancellationPolicy: z.object({ refunds: z.object({
27280
+ mode: z.enum([
27281
+ "never",
27282
+ "always",
27283
+ "cutoff"
27284
+ ]),
27285
+ cutoffHours: z.number(),
27286
+ method: z.enum(["original_payment_method", "site_credit"])
27287
+ }) }),
27205
27288
  consent: blockBookingConsentSchema.nullable(),
27206
27289
  accessRestriction: accessRestrictionSchema,
27207
27290
  allowedCategoryIds: z.array(z.string()),
@@ -27332,6 +27415,7 @@ const calendarWindowQuerySchema = z.object({
27332
27415
  eventSurface: z.enum(eventSurfaceScopeValues).optional(),
27333
27416
  eventPresets: z.string().optional(),
27334
27417
  scheduleScope: z.enum(eventScheduleScopeValues).optional(),
27418
+ seriesIds: z.string().optional(),
27335
27419
  categoryIds: z.string().optional(),
27336
27420
  venueId: z.string().transform(normalizeCalendarFilterId).optional(),
27337
27421
  staffMemberId: z.string().transform(normalizeCalendarFilterId).optional()
@@ -27382,7 +27466,56 @@ const calendarWindowPageSchema = z.discriminatedUnion("kind", [z.object({
27382
27466
  kind: z.literal("partial"),
27383
27467
  nextCursor: calendarSourceCursorSchema
27384
27468
  })]);
27469
+ //#endregion
27470
+ //#region ../blocks/src/system/blocks/events/shared/eventSelection.ts
27471
+ /** Invalid saved selections fail closed rather than widening discovery. */
27472
+ function resolveEventSelection(mode, value) {
27473
+ if (mode !== "selected") return { kind: "matching" };
27474
+ const candidates = Array.isArray(value) ? value : typeof value === "string" ? value.split(",").filter(Boolean) : [];
27475
+ const ids = [];
27476
+ for (const candidate of candidates) {
27477
+ if (typeof candidate !== "string") return {
27478
+ kind: "selected",
27479
+ seriesIds: []
27480
+ };
27481
+ const parsed = parseEventSeriesId(candidate.trim());
27482
+ if (!parsed.ok) return {
27483
+ kind: "selected",
27484
+ seriesIds: []
27485
+ };
27486
+ ids.push(parsed.value);
27487
+ }
27488
+ return {
27489
+ kind: "selected",
27490
+ seriesIds: [...new Set(ids)].sort()
27491
+ };
27492
+ }
27493
+ /** Undefined means discovery; an empty string is an explicitly empty selection. */
27494
+ function eventSeriesScopeCsv(value) {
27495
+ if (value == null) return void 0;
27496
+ return typeof value === "string" ? value : value.join(",");
27497
+ }
27385
27498
  const defaultTransforms = createTransformRegistry([
27499
+ {
27500
+ id: "events.calendarSelectionParam",
27501
+ kind: "formatter",
27502
+ summary: "Resolve selected events instead of discovery filters",
27503
+ schema: z.object({ key: z.enum([
27504
+ "seriesIds",
27505
+ "eventSurface",
27506
+ "filterEventPresets",
27507
+ "scheduleScope",
27508
+ "filterCategoryIds",
27509
+ "filterStaffMemberId",
27510
+ "filterVenueId"
27511
+ ]) }),
27512
+ run(value, { key }) {
27513
+ const content = asPartialObject(value) ?? {};
27514
+ const selection = resolveEventSelection(content.eventSelection, content.selectedEventSeriesIds);
27515
+ if (selection.kind === "selected") return key === "seriesIds" ? selection.seriesIds : void 0;
27516
+ return key === "seriesIds" ? void 0 : content[key];
27517
+ }
27518
+ },
27386
27519
  {
27387
27520
  id: "events.calendarReadEnabled",
27388
27521
  kind: "formatter",
@@ -29138,17 +29271,16 @@ const ButtonNode = ({ value, children, href, variantId = "primary", size = "md",
29138
29271
  if (process.env.NODE_ENV === "development") console.warn("[ButtonNode] Missing href for link-required button");
29139
29272
  return null;
29140
29273
  }
29141
- const extraClassName = affordanceIcon ? [className, AFFORDANCE_SUPPRESS_CLASS].filter(Boolean).join(" ") : className;
29142
29274
  const resolvedVariantId = parseThemeButtonVariantId(variantId);
29143
29275
  if (variantId && !resolvedVariantId) {
29144
29276
  console.error("[ButtonNode] Invalid button variant", { variantId });
29145
29277
  return null;
29146
29278
  }
29147
- const combinedClassName = resolvedVariantId ? themeButtonClassName({
29279
+ const combinedClassName = resolvedVariantId ? (affordanceIcon ? themeButtonClassNameWithTrailingIcon : themeButtonClassName)({
29148
29280
  variant: resolvedVariantId,
29149
29281
  size,
29150
- extraClassName
29151
- }) : extraClassName;
29282
+ extraClassName: className
29283
+ }) : className;
29152
29284
  if (resolvedHref) return /* @__PURE__ */ jsxs("a", {
29153
29285
  href: resolvedHref,
29154
29286
  target,
@@ -30411,6 +30543,7 @@ const publicPricingSchema = z.object({
30411
30543
  });
30412
30544
  const publicAppointmentServiceSchema = z.object({
30413
30545
  id: z.string().min(1),
30546
+ slug: z.string().min(1).optional(),
30414
30547
  title: z.string(),
30415
30548
  description: z.string().nullable().optional(),
30416
30549
  durationMinutes: z.number().int().positive(),
@@ -30487,7 +30620,7 @@ function formatAmountOrFree(amount, currency) {
30487
30620
  currency
30488
30621
  }, { decimals: "auto" });
30489
30622
  }
30490
- function buildRows(services, pathname) {
30623
+ function buildRows(services, pathname, servicePathPrefix) {
30491
30624
  return services.map((service) => ({
30492
30625
  id: service.id,
30493
30626
  title: service.title,
@@ -30498,7 +30631,8 @@ function buildRows(services, pathname) {
30498
30631
  href: resolveDedicatedBookingHref(pathname, {
30499
30632
  kind: "appointment_service",
30500
30633
  serviceId: service.id
30501
- })
30634
+ }),
30635
+ detailsHref: servicePathPrefix && service.slug ? `${servicePathPrefix.replace(/\/$/, "")}/${encodeURIComponent(service.slug)}` : null
30502
30636
  }));
30503
30637
  }
30504
30638
  function buildAppointmentServicesView(input) {
@@ -30517,13 +30651,14 @@ function buildAppointmentServicesView(input) {
30517
30651
  };
30518
30652
  return {
30519
30653
  kind: "items",
30520
- rows: buildRows(parsed.services, input.pathname)
30654
+ rows: buildRows(parsed.services, input.pathname, input.servicePathPrefix ?? null)
30521
30655
  };
30522
30656
  }
30523
- function AppointmentServicesNode({ sectionHeading, services, pathname, className }) {
30657
+ function AppointmentServicesNode({ sectionHeading, services, pathname, servicePathPrefix, className }) {
30524
30658
  const view = buildAppointmentServicesView({
30525
30659
  services,
30526
- pathname: typeof pathname === "string" ? pathname : void 0
30660
+ pathname: typeof pathname === "string" ? pathname : void 0,
30661
+ servicePathPrefix: typeof servicePathPrefix === "string" ? servicePathPrefix : null
30527
30662
  });
30528
30663
  const rootClassName = `appointment-services ${className ?? ""}`.trim();
30529
30664
  const RowTitle = sectionHeading ? "h3" : "h2";
@@ -30554,7 +30689,10 @@ function AppointmentServicesNode({ sectionHeading, services, pathname, className
30554
30689
  children: [
30555
30690
  /* @__PURE__ */ jsx(RowTitle, {
30556
30691
  className: "appointment-services-row-title rb-text-lg rb-font-medium rb-leading-tight",
30557
- children: row.title
30692
+ children: row.detailsHref ? /* @__PURE__ */ jsx("a", {
30693
+ href: row.detailsHref,
30694
+ children: row.title
30695
+ }) : row.title
30558
30696
  }),
30559
30697
  row.description ? /* @__PURE__ */ jsx("p", {
30560
30698
  className: "appointment-services-row-description rb-text-sm rb-leading-6 status-muted",
@@ -33045,12 +33183,12 @@ const EventCompactRow = ({ event, buttonVariant = "primary", buttonText }) => {
33045
33183
  }), cta.hidden ? /* @__PURE__ */ jsx("span", {
33046
33184
  className: "event-compact-row-status",
33047
33185
  children: cta.label
33048
- }) : /* @__PURE__ */ jsxs("a", {
33186
+ }) : /* @__PURE__ */ jsx("a", {
33049
33187
  href: cta.href,
33050
33188
  target: cta.external ? "_blank" : void 0,
33051
33189
  rel: cta.external ? "noopener noreferrer" : void 0,
33052
33190
  className: `${buttonClass} event-compact-row-cta`,
33053
- children: [/* @__PURE__ */ jsx("span", { children: cta.label }), /* @__PURE__ */ jsx(ArrowRightIcon, { "aria-hidden": "true" })]
33191
+ children: /* @__PURE__ */ jsx("span", { children: cta.label })
33054
33192
  })]
33055
33193
  })]
33056
33194
  })]
@@ -34132,6 +34270,7 @@ const eventCollectionFilterSourceSchemaShape = {
34132
34270
  eventSurface: eventSurfaceScopeSchema$1.nullable().optional(),
34133
34271
  filterEventPresets: z.union([z.string(), z.array(z.string())]).nullable().optional(),
34134
34272
  scheduleScope: eventScheduleScopeSchema$1.nullable().optional(),
34273
+ seriesIds: z.union([z.string(), z.array(z.string())]).nullable().optional(),
34135
34274
  filterCategoryIds: z.union([z.string(), z.array(z.string())]).nullable().optional(),
34136
34275
  filterStaffMemberId: z.string().nullable().optional(),
34137
34276
  filterVenueId: z.string().nullable().optional()
@@ -34178,6 +34317,7 @@ const eventCollectionDisplaySchemaShape = {
34178
34317
  eventSurface: eventSurfaceScopeSchema$1,
34179
34318
  filterEventPresets: z.string().nullable(),
34180
34319
  scheduleScope: eventScheduleScopeSchema$1,
34320
+ seriesIds: z.string().nullable().optional(),
34181
34321
  filterCategoryIds: z.string().nullable(),
34182
34322
  filterStaffMemberId: z.string().nullable(),
34183
34323
  filterVenueId: z.string().nullable(),
@@ -34225,11 +34365,15 @@ function buildEventCollectionDisplay(source) {
34225
34365
  };
34226
34366
  }
34227
34367
  function buildEventCollectionFilterDisplay(source) {
34228
- return {
34229
- showFilters: source.showFilters ?? true,
34230
- showCategoryFilter: source.showCategoryFilter ?? "auto",
34231
- showVenueFilter: source.showVenueFilter ?? false,
34232
- showStaffFilter: source.showStaffFilter ?? true,
34368
+ const selected = source.seriesIds != null;
34369
+ const matchingFilters = selected ? {
34370
+ eventSurface: "all",
34371
+ filterEventPresets: null,
34372
+ scheduleScope: "any",
34373
+ filterCategoryIds: null,
34374
+ filterStaffMemberId: null,
34375
+ filterVenueId: null
34376
+ } : {
34233
34377
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
34234
34378
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
34235
34379
  scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
@@ -34237,6 +34381,14 @@ function buildEventCollectionFilterDisplay(source) {
34237
34381
  filterStaffMemberId: normalizeEventStaffMemberId(source.filterStaffMemberId),
34238
34382
  filterVenueId: normalizeEventVenueId(source.filterVenueId)
34239
34383
  };
34384
+ return {
34385
+ seriesIds: eventSeriesScopeCsv(source.seriesIds),
34386
+ showFilters: selected ? false : source.showFilters ?? true,
34387
+ showCategoryFilter: source.showCategoryFilter ?? "auto",
34388
+ showVenueFilter: source.showVenueFilter ?? false,
34389
+ showStaffFilter: source.showStaffFilter ?? true,
34390
+ ...matchingFilters
34391
+ };
34240
34392
  }
34241
34393
  function buildEventCollectionPresentationDisplay(source) {
34242
34394
  return {
@@ -34251,14 +34403,15 @@ function buildEventCollectionPresentationDisplay(source) {
34251
34403
  }
34252
34404
  function buildEventCollectionHydration(source, now = /* @__PURE__ */ new Date()) {
34253
34405
  const timeZone = source.calendarWindow?.scope.timeZone ?? toBookingTimeZone(source.bookingTimeZone);
34406
+ const selected = source.seriesIds != null;
34254
34407
  return {
34255
34408
  calendarWindow: source.calendarWindow ?? void 0,
34256
34409
  siteId: source.siteId,
34257
34410
  stage: source.stage,
34258
34411
  prefetchedEvents: source.events != null,
34259
- eventSurface: normalizeEventSurfaceScope(source.eventSurface),
34260
- filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
34261
- scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
34412
+ eventSurface: selected ? "all" : normalizeEventSurfaceScope(source.eventSurface),
34413
+ filterEventPresets: selected ? null : normalizeEventSurfacePresetsCsv(source.filterEventPresets),
34414
+ scheduleScope: selected ? "any" : normalizeEventScheduleScope(source.scheduleScope),
34262
34415
  calendarContext: {
34263
34416
  timeZone,
34264
34417
  initialToday: source.calendarWindow?.initialToday ?? getLocalScheduleDate(now, timeZone)
@@ -34828,13 +34981,10 @@ function OfferingsPreviewContent(props) {
34828
34981
  }),
34829
34982
  groupCta ? /* @__PURE__ */ jsx("footer", {
34830
34983
  className: "offerings-preview-card-footer rb-mt-auto rb-flex",
34831
- children: /* @__PURE__ */ jsxs("a", {
34984
+ children: /* @__PURE__ */ jsx("a", {
34832
34985
  className: groupLinkClass,
34833
34986
  href: groupCta.href,
34834
- children: [/* @__PURE__ */ jsx("span", { children: groupCta.label }), /* @__PURE__ */ jsx(ArrowRightIcon, {
34835
- "aria-hidden": "true",
34836
- className: "offerings-preview-link-icon rb-h-4 rb-w-4"
34837
- })]
34987
+ children: /* @__PURE__ */ jsx("span", { children: groupCta.label })
34838
34988
  })
34839
34989
  }) : null
34840
34990
  ]
@@ -37781,6 +37931,65 @@ const formBlockDefinition = {
37781
37931
  runtime: formBlockRuntime
37782
37932
  };
37783
37933
  //#endregion
37934
+ //#region ../blocks/src/system/blocks/events/eventSelectionFields.ts
37935
+ const eventSelectionFields = [{
37936
+ id: "eventSelection",
37937
+ type: "select",
37938
+ label: "Events to show",
37939
+ required: false,
37940
+ multiple: false,
37941
+ defaultValue: "matching",
37942
+ options: [{
37943
+ value: "matching",
37944
+ label: "All matching events"
37945
+ }, {
37946
+ value: "selected",
37947
+ label: "Selected events"
37948
+ }],
37949
+ ui: { intent: "behavior" }
37950
+ }, createSiteOptionSelectField({
37951
+ id: "selectedEventSeriesIds",
37952
+ label: "Selected events",
37953
+ description: "Show upcoming dates for these events. Other event filters do not apply.",
37954
+ required: false,
37955
+ multiple: true,
37956
+ defaultValue: [],
37957
+ optionSource: "eventSeries",
37958
+ ui: {
37959
+ intent: "behavior",
37960
+ searchThreshold: 1,
37961
+ visibleWhen: {
37962
+ field: "eventSelection",
37963
+ equals: "selected"
37964
+ }
37965
+ }
37966
+ })];
37967
+ function withEventSelectionVisibility(field) {
37968
+ if (![
37969
+ "eventSurface",
37970
+ "filterEventPresets",
37971
+ "scheduleScope",
37972
+ "filterCategoryIds",
37973
+ "filterStaffMemberId",
37974
+ "filterVenueId",
37975
+ "showFilters",
37976
+ "showCategoryFilter",
37977
+ "showVenueFilter",
37978
+ "showStaffFilter"
37979
+ ].includes(field.id)) return field;
37980
+ const visibleWhen = field.ui?.visibleWhen;
37981
+ return {
37982
+ ...field,
37983
+ ui: {
37984
+ ...field.ui,
37985
+ visibleWhen: { allOf: [{
37986
+ field: "eventSelection",
37987
+ notEquals: "selected"
37988
+ }, ...visibleWhen ? [visibleWhen] : []] }
37989
+ }
37990
+ };
37991
+ }
37992
+ //#endregion
37784
37993
  //#region ../blocks/src/system/runtime/nodes/events/EventCalendar.interactive.ts
37785
37994
  const eventCalendarSourceSchema = z.object({
37786
37995
  ...eventCollectionSourceSchemaShape,
@@ -37878,19 +38087,59 @@ function buildEventCalendarInteractiveIslandProps(props) {
37878
38087
  //#region ../blocks/src/system/blocks/events/eventCalendarDataLoaders.ts
37879
38088
  const scopeParams = {
37880
38089
  siteId: { $bind: { from: "$root.siteId" } },
38090
+ seriesIds: { $bind: {
38091
+ from: "content",
38092
+ transforms: [{
38093
+ id: "events.calendarSelectionParam",
38094
+ options: { key: "seriesIds" }
38095
+ }]
38096
+ } },
37881
38097
  stage: { $bind: {
37882
38098
  from: "$root.previewStage",
37883
38099
  fallback: "published"
37884
38100
  } },
37885
- eventSurface: { $bind: { from: "content.eventSurface" } },
38101
+ eventSurface: { $bind: {
38102
+ from: "content",
38103
+ transforms: [{
38104
+ id: "events.calendarSelectionParam",
38105
+ options: { key: "eventSurface" }
38106
+ }, { id: "value.csv" }]
38107
+ } },
37886
38108
  eventPresets: { $bind: {
37887
- from: "content.filterEventPresets",
37888
- transforms: [{ id: "value.csv" }]
38109
+ from: "content",
38110
+ transforms: [{
38111
+ id: "events.calendarSelectionParam",
38112
+ options: { key: "filterEventPresets" }
38113
+ }, { id: "value.csv" }]
38114
+ } },
38115
+ scheduleScope: { $bind: {
38116
+ from: "content",
38117
+ transforms: [{
38118
+ id: "events.calendarSelectionParam",
38119
+ options: { key: "scheduleScope" }
38120
+ }, { id: "value.csv" }]
38121
+ } },
38122
+ categoryIds: { $bind: {
38123
+ from: "content",
38124
+ transforms: [{
38125
+ id: "events.calendarSelectionParam",
38126
+ options: { key: "filterCategoryIds" }
38127
+ }, { id: "value.csv" }]
38128
+ } },
38129
+ staffMemberId: { $bind: {
38130
+ from: "content",
38131
+ transforms: [{
38132
+ id: "events.calendarSelectionParam",
38133
+ options: { key: "filterStaffMemberId" }
38134
+ }, { id: "value.csv" }]
37889
38135
  } },
37890
- scheduleScope: { $bind: { from: "content.scheduleScope" } },
37891
- categoryIds: { $bind: { from: "content.filterCategoryIds" } },
37892
- staffMemberId: { $bind: { from: "content.filterStaffMemberId" } },
37893
- venueId: { $bind: { from: "content.filterVenueId" } }
38136
+ venueId: { $bind: {
38137
+ from: "content",
38138
+ transforms: [{
38139
+ id: "events.calendarSelectionParam",
38140
+ options: { key: "filterVenueId" }
38141
+ }, { id: "value.csv" }]
38142
+ } }
37894
38143
  };
37895
38144
  /** Combined renders two jobs; calendar-only variants never pay for the list seed. */
37896
38145
  const eventCalendarDataLoaders = {
@@ -38020,6 +38269,13 @@ const eventCalendarLayout = [styledSection({
38020
38269
  from: "content.defaultCalendarView",
38021
38270
  fallback: "month"
38022
38271
  } },
38272
+ seriesIds: { $bind: {
38273
+ from: "content",
38274
+ transforms: [{
38275
+ id: "events.calendarSelectionParam",
38276
+ options: { key: "seriesIds" }
38277
+ }]
38278
+ } },
38023
38279
  showFilters: { $bind: {
38024
38280
  from: "content.showFilters",
38025
38281
  fallback: true
@@ -38041,17 +38297,47 @@ const eventCalendarLayout = [styledSection({
38041
38297
  fallback: true
38042
38298
  } },
38043
38299
  eventSurface: { $bind: {
38044
- from: "content.eventSurface",
38045
- fallback: "all"
38300
+ from: "content",
38301
+ transforms: [{
38302
+ id: "events.calendarSelectionParam",
38303
+ options: { key: "eventSurface" }
38304
+ }]
38305
+ } },
38306
+ filterEventPresets: { $bind: {
38307
+ from: "content",
38308
+ transforms: [{
38309
+ id: "events.calendarSelectionParam",
38310
+ options: { key: "filterEventPresets" }
38311
+ }]
38046
38312
  } },
38047
- filterEventPresets: { $bind: { from: "content.filterEventPresets" } },
38048
38313
  scheduleScope: { $bind: {
38049
- from: "content.scheduleScope",
38050
- fallback: "any"
38314
+ from: "content",
38315
+ transforms: [{
38316
+ id: "events.calendarSelectionParam",
38317
+ options: { key: "scheduleScope" }
38318
+ }]
38319
+ } },
38320
+ filterCategoryIds: { $bind: {
38321
+ from: "content",
38322
+ transforms: [{
38323
+ id: "events.calendarSelectionParam",
38324
+ options: { key: "filterCategoryIds" }
38325
+ }]
38326
+ } },
38327
+ filterStaffMemberId: { $bind: {
38328
+ from: "content",
38329
+ transforms: [{
38330
+ id: "events.calendarSelectionParam",
38331
+ options: { key: "filterStaffMemberId" }
38332
+ }]
38333
+ } },
38334
+ filterVenueId: { $bind: {
38335
+ from: "content",
38336
+ transforms: [{
38337
+ id: "events.calendarSelectionParam",
38338
+ options: { key: "filterVenueId" }
38339
+ }]
38051
38340
  } },
38052
- filterCategoryIds: { $bind: { from: "content.filterCategoryIds" } },
38053
- filterStaffMemberId: { $bind: { from: "content.filterStaffMemberId" } },
38054
- filterVenueId: { $bind: { from: "content.filterVenueId" } },
38055
38341
  cardRole: { $bind: {
38056
38342
  from: "content._cardStyles.role",
38057
38343
  fallback: "default"
@@ -38555,6 +38841,7 @@ const eventCalendarAdditionalFields = [
38555
38841
  }
38556
38842
  }
38557
38843
  },
38844
+ ...eventSelectionFields,
38558
38845
  {
38559
38846
  id: "eventSurface",
38560
38847
  type: "select",
@@ -38764,7 +39051,7 @@ const eventCalendarBlockDefinition = {
38764
39051
  titleSource: "heading",
38765
39052
  description: "Display events with calendar, list, or combined views with filtering",
38766
39053
  skipSectionStyles: true,
38767
- additionalFields: eventCalendarAdditionalFields,
39054
+ additionalFields: eventCalendarAdditionalFields.map(withEventSelectionVisibility),
38768
39055
  styleTokens: {
38769
39056
  background: "surface",
38770
39057
  typography: "body",
@@ -38873,6 +39160,7 @@ const eventListingHydrationSchema = z.object({
38873
39160
  eventSurface: eventSurfaceScopeSchema,
38874
39161
  filterEventPresets: z.string().nullable(),
38875
39162
  scheduleScope: eventScheduleScopeSchema,
39163
+ seriesIds: z.string().nullable().optional(),
38876
39164
  filterCategoryIds: z.string().nullable().optional(),
38877
39165
  filterStaffMemberId: z.string().nullable().optional(),
38878
39166
  filterVenueId: z.string().nullable().optional(),
@@ -38933,6 +39221,7 @@ const eventListingBlockRuntime = defineProgressiveBlockRuntime({
38933
39221
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
38934
39222
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
38935
39223
  scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
39224
+ seriesIds: eventSeriesScopeCsv(source.seriesIds),
38936
39225
  filterCategoryIds: normalizeEventCategoryIdsCsv(source.filterCategoryIds),
38937
39226
  filterStaffMemberId: normalizeEventStaffMemberId(source.filterStaffMemberId),
38938
39227
  filterVenueId: normalizeEventVenueId(source.filterVenueId),
@@ -44301,9 +44590,56 @@ function generateNavLinkCSS(options) {
44301
44590
  if (baseRules.length > 0) cssChunks.unshift(`\n${selector} {\n ${baseRules.join("\n ")}\n}\n`);
44302
44591
  if (hoverRules.length > 0) cssChunks.push(`\n${hoverSelector} {\n ${hoverRules.join("\n ")}\n}\n`);
44303
44592
  if (activeRules.length > 0) cssChunks.push(`\n${activeSelector} {\n ${activeRules.join("\n ")}\n}\n`);
44593
+ cssChunks.push(navLinkFocusVisibleCss(selector, style, theme, tokens));
44304
44594
  cssChunks.push(generateInteractiveChildInheritanceCSS(selector));
44305
44595
  return cssChunks.join("");
44306
44596
  }
44597
+ /**
44598
+ * Keyboard focus indicator for nav links, in the header, the footer and header
44599
+ * dropdown menus — the three surfaces `generateNavLinkCSS` serves.
44600
+ *
44601
+ * Emitted unconditionally rather than as an effect, because focus visibility is
44602
+ * a floor rather than a style choice. Only the interaction-palette pill ever
44603
+ * wrote an `effects.focus` group, so every other nav — every `quiet` palette,
44604
+ * every footer, every dropdown — previously fell back to whatever outline the
44605
+ * browser drew.
44606
+ *
44607
+ * The outline takes the link's own resolved colour, so on a header that adapts
44608
+ * to the section it floats over it moves with the adaptation instead of
44609
+ * asserting a brand colour against an unknown backdrop.
44610
+ *
44611
+ * Where the style asks for `soft-glow`, the same halo goes on the outline. That
44612
+ * treatment exists because the theme has already conceded the resolved colour
44613
+ * alone is not reliable over an unpredictable backdrop — a hero photograph with
44614
+ * mixed luminance. Without it the text would stay legible by its halo while the
44615
+ * focus ring, which is the part with a contrast requirement of its own, would
44616
+ * not.
44617
+ *
44618
+ * A style that also paints a focus pill keeps it: this adds an outline around
44619
+ * that pill rather than replacing it.
44620
+ */
44621
+ function navLinkFocusVisibleCss(selector, style, theme, tokens) {
44622
+ const color = tokens.getColor(style.colorToken);
44623
+ const halo = buildTextContrastShadow({
44624
+ theme,
44625
+ colorToken: style.colorToken,
44626
+ treatment: style.contrastTreatment
44627
+ });
44628
+ return `
44629
+ ${selector}:focus-visible {
44630
+ outline: 2px solid ${color};
44631
+ outline-offset: ${focusOutlineOffset(selector)};${halo ? `\n box-shadow: ${halo};` : ""}
44632
+ }
44633
+ `;
44634
+ }
44635
+ /**
44636
+ * Dropdown items are full-width inside a panel with no horizontal padding, so
44637
+ * an outset outline would paint over and past the panel's own border. Keeping
44638
+ * it flush is the only way it reads as belonging to the item.
44639
+ */
44640
+ function focusOutlineOffset(selector) {
44641
+ return selector.includes("nav-dropdown-link") ? "-2px" : "3px";
44642
+ }
44307
44643
  function buildTextContrastShadow(input) {
44308
44644
  if (input.treatment !== "soft-glow") return null;
44309
44645
  return isLikelyLightColorToken(input.theme, input.colorToken) ? "0 1px 3px rgb(0 0 0 / 0.55), 0 0 18px rgb(0 0 0 / 0.38)" : "0 1px 3px rgb(255 255 255 / 0.72), 0 0 18px rgb(255 255 255 / 0.5)";
@@ -47436,7 +47772,7 @@ function pageBlockPosition(index, blockCount) {
47436
47772
  //#endregion
47437
47773
  //#region ../blocks/src/pageRendererContracts.ts
47438
47774
  function buildPageRendererRootRuntime(dataContext) {
47439
- const { siteId = null, pageId = null, pathname = null, bookingTimeZone, previewStage = "published", apiBaseUrl = null, portalToken = null, bookingOfferingPresets = [], mailingList = { tag: "not_configured" }, eventRegistrationOverlayEnabled = false, loaderFailures = {} } = dataContext ?? {};
47775
+ const { siteId = null, pageId = null, pathname = null, bookingTimeZone, previewStage = "published", apiBaseUrl = null, portalToken = null, bookingOfferingPresets = [], appointmentServicePathPrefix = null, mailingList = { tag: "not_configured" }, eventRegistrationOverlayEnabled = false, loaderFailures = {} } = dataContext ?? {};
47440
47776
  return {
47441
47777
  siteId,
47442
47778
  pageId,
@@ -47446,6 +47782,7 @@ function buildPageRendererRootRuntime(dataContext) {
47446
47782
  apiBaseUrl,
47447
47783
  portalToken,
47448
47784
  bookingOfferingPresets,
47785
+ appointmentServicePathPrefix,
47449
47786
  mailingList,
47450
47787
  eventRegistrationOverlayEnabled,
47451
47788
  loaderFailures
@@ -52162,7 +52499,8 @@ const appointmentServicesBlockDefinition = {
52162
52499
  el("appointment-services", {
52163
52500
  sectionHeading: { $bind: { from: "content.heading" } },
52164
52501
  services: { $bind: { from: "data.services" } },
52165
- pathname: { $bind: { from: "$root.pathname" } }
52502
+ pathname: { $bind: { from: "$root.pathname" } },
52503
+ servicePathPrefix: { $bind: { from: "$root.appointmentServicePathPrefix" } }
52166
52504
  })
52167
52505
  ], { gap: "lg" })
52168
52506
  })]
@@ -57951,43 +58289,6 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
57951
58289
  return prev.entry === next.entry && prev.audience === next.audience && prev.registry === next.registry && prev.dataContext === next.dataContext && prev.themeResources === next.themeResources && prev.routeMap === next.routeMap && prev.wrapBlock === next.wrapBlock && prev.imageResolveContext === next.imageResolveContext && prev.previewAuthoring === next.previewAuthoring;
57952
58290
  });
57953
58291
  //#endregion
57954
- //#region ../theme-core/src/footer/mode.ts
57955
- /**
57956
- * Resolve what to render for the footer given the current mode and whether the
57957
- * site is SDK-managed.
57958
- *
57959
- * Backwards compatible: missing/undefined mode is treated as 'default'.
57960
- */
57961
- function resolveFooterRenderPlan(params) {
57962
- if (!params.isSdkSite) return {
57963
- mode: "default",
57964
- renderDefaultFooter: true,
57965
- renderFooterBlocks: false
57966
- };
57967
- switch (params.mode ?? "default") {
57968
- case "blocks": return {
57969
- mode: "blocks",
57970
- renderDefaultFooter: false,
57971
- renderFooterBlocks: true
57972
- };
57973
- case "default+blocks": return {
57974
- mode: "default+blocks",
57975
- renderDefaultFooter: true,
57976
- renderFooterBlocks: true
57977
- };
57978
- case "none": return {
57979
- mode: "none",
57980
- renderDefaultFooter: false,
57981
- renderFooterBlocks: false
57982
- };
57983
- default: return {
57984
- mode: "default",
57985
- renderDefaultFooter: true,
57986
- renderFooterBlocks: false
57987
- };
57988
- }
57989
- }
57990
- //#endregion
57991
58292
  //#region ../theme-core/src/generated/containerResponsiveThemeCss.ts
57992
58293
  /**
57993
58294
  * Auto-generated by scripts/generate-container-responsive-css.ts
@@ -58790,10 +59091,19 @@ const containerResponsiveThemeCss = `/*
58790
59091
  padding-top: 2.5rem;
58791
59092
  padding-bottom: 2.5rem;
58792
59093
  }
59094
+ /* Clearance for a surface that is not an authored block page.
59095
+ An authored page gets this through its opening block's container (see the
59096
+ overlay-clearance rule in styles.css). A reserved surface or a system
59097
+ destination page has no such block, so it consumes the same variable here.
59098
+ Marked with one class rather than per-surface selectors, because the last
59099
+ two additions to this family were both missed and shipped overlapping. */
58793
59100
  :where(.theme-scope)
58794
59101
  [data-site-document-layout-mode="header-overlay"]
58795
- [data-rb-reserved-surface].rb-reserved-surface-main {
58796
- padding-top: calc(var(--rb-reserved-surface-header-offset, 5.5rem) + 2.5rem);
59102
+ .rb-overlay-clearance-surface {
59103
+ padding-top: calc(
59104
+ var(--site-overlay-header-clearance, 5.5rem) +
59105
+ var(--site-overlay-opening-content-extra-pad, clamp(1rem, 2vw, 1.75rem))
59106
+ );
58797
59107
  }
58798
59108
  .rb-preview-editor-offset {
58799
59109
  padding-bottom: calc(
@@ -69150,6 +69460,23 @@ const containerResponsiveThemeCss = `/*
69150
69460
  gap: 0.5rem;
69151
69461
  }
69152
69462
 
69463
+ /* Error panel and field notes. The form block scopes these to its own
69464
+ data-block, so the newsletter form (which also renders in the footer)
69465
+ needs its own copy of the treatment. */
69466
+ :where(.theme-scope) .newsletter-form .fb-error:not([data-field-validation-message]) {
69467
+ background-color: var(--fb-error-bg);
69468
+ color: var(--fb-error-text);
69469
+ border: 1px solid var(--fb-error-border);
69470
+ border-radius: var(--rb-block-radius);
69471
+ padding: 1rem;
69472
+ font-size: 0.875rem;
69473
+ }
69474
+
69475
+ :where(.theme-scope) .newsletter-form [data-field-validation-message] {
69476
+ font-size: 0.875rem;
69477
+ color: var(--fb-error-text);
69478
+ }
69479
+
69153
69480
  /* Required indicator */
69154
69481
  :where(.theme-scope) .newsletter-required {
69155
69482
  color: rgb(var(--tb-primary));
@@ -71483,6 +71810,15 @@ z.object({
71483
71810
  }).optional(),
71484
71811
  overrides: z.record(z.string(), z.string()).optional()
71485
71812
  }).passthrough();
71813
+ z.object({
71814
+ palette: paletteColorsSchema,
71815
+ paletteMode: paletteModeSchema,
71816
+ typography: z.object({
71817
+ headingFamily: z.string().trim().min(1),
71818
+ bodyFamily: z.string().trim().min(1)
71819
+ }).strict(),
71820
+ corners
71821
+ }).strict();
71486
71822
  //#endregion
71487
71823
  //#region src/rendering/blocks-theme-runtime.ts
71488
71824
  function parseBlocksTheme(theme) {
@@ -72322,12 +72658,12 @@ function normalizeContent(block) {
72322
72658
  }
72323
72659
  /**
72324
72660
  * Normalize params for API calls.
72325
- * Converts all values to strings.
72661
+ * Converts supplied values to strings, preserving absent versus explicitly empty params.
72326
72662
  */
72327
72663
  function normalizeParams(input) {
72328
72664
  if (!input || typeof input !== "object") return {};
72329
72665
  const output = {};
72330
- for (const [key, value] of Object.entries(input)) output[key] = coerceToString(value);
72666
+ for (const [key, value] of Object.entries(input)) if (value !== void 0 && value !== null) output[key] = coerceToString(value);
72331
72667
  return output;
72332
72668
  }
72333
72669
  function isNonArrayObject(value) {
@@ -72391,6 +72727,14 @@ function coercePayload(payload) {
72391
72727
  * - Frontend: provide internal API client
72392
72728
  * - SDK: provide SDK client with endpoint mapping
72393
72729
  */
72730
+ function projectDataLoaders(loaders, shouldLoad, blockId, blockKind) {
72731
+ if (!shouldLoad) return [...loaders];
72732
+ return loaders.filter(({ key }) => shouldLoad({
72733
+ blockId,
72734
+ blockKind,
72735
+ loaderKey: key
72736
+ }));
72737
+ }
72394
72738
  function shouldSkipResolvedLoader(input) {
72395
72739
  if (input.loader.requiredParams?.some((param) => !input.params[param]?.trim())) return true;
72396
72740
  return Boolean(input.isValidEndpoint && !input.isValidEndpoint(input.endpoint));
@@ -72419,7 +72763,7 @@ async function prefetchBlockData$1(page, context, options) {
72419
72763
  return (await prefetchBlockDataWithFailures(page, context, options)).resolvedData;
72420
72764
  }
72421
72765
  async function prefetchBlockDataWithFailures(page, context, options) {
72422
- const { apiClient, isValidEndpoint, onError, getCustomBlockLoaders, getBlockDefinition: getBlockDefinition$1 = getBlockDefinition, resolveParamsBindings: resolveParamsBindings$1 = resolveParamsBindings } = options;
72766
+ const { apiClient, isValidEndpoint, onError, getCustomBlockLoaders, getBlockDefinition: getBlockDefinition$1 = getBlockDefinition, resolveParamsBindings: resolveParamsBindings$1 = resolveParamsBindings, shouldLoad } = options;
72423
72767
  const results = {};
72424
72768
  const loaderFailures = {};
72425
72769
  const tasks = [];
@@ -72436,6 +72780,7 @@ async function prefetchBlockDataWithFailures(page, context, options) {
72436
72780
  loader
72437
72781
  })) : [];
72438
72782
  } else loaders = [];
72783
+ loaders = projectDataLoaders(loaders, shouldLoad, blockId, block.kind);
72439
72784
  if (loaders.length === 0) continue;
72440
72785
  const root = {
72441
72786
  ...context.root ?? {},
@@ -72514,8 +72859,8 @@ function toStringParams(value) {
72514
72859
  for (const [k, v] of Object.entries(value)) if (typeof v === "string") out[k] = v;
72515
72860
  return out;
72516
72861
  }
72517
- function stripEmptyStringValues(params) {
72518
- for (const [key, value] of Object.entries(params)) if (value === "") delete params[key];
72862
+ function stripEmptyStringValues(params, endpoint) {
72863
+ for (const [key, value] of Object.entries(params)) if (value === "" && !(key === "seriesIds" && (endpoint === "listPublicEvents" || endpoint === "listPublicEventCalendarWindow"))) delete params[key];
72519
72864
  }
72520
72865
  function ensureSiteId(params, context) {
72521
72866
  if (!params.siteId && typeof context.siteId === "string" && context.siteId) params.siteId = context.siteId;
@@ -72528,7 +72873,7 @@ function ensureSiteId(params, context) {
72528
72873
  */
72529
72874
  function normalizeLoaderParams(endpoint, params, context) {
72530
72875
  const out = toStringParams(params);
72531
- stripEmptyStringValues(out);
72876
+ stripEmptyStringValues(out, endpoint);
72532
72877
  if (endpoint === "getPublicFormById") {
72533
72878
  ensureSiteId(out, context);
72534
72879
  if (!out.formId && out.formSlug) {
@@ -72883,7 +73228,7 @@ async function Layout(props) {
72883
73228
  if (footer === true) {
72884
73229
  const footerPlan = resolveFooterRenderPlan({
72885
73230
  isSdkSite: Boolean(site.is_sdk_site),
72886
- mode: theme.footer?.mode
73231
+ mode: resolveSdkFooterMode(siteData.sdkConfig)
72887
73232
  });
72888
73233
  const footerBlocksElement = footerPlan.renderFooterBlocks && footerBlocks.length > 0 ? /* @__PURE__ */ jsx(PageRenderer, {
72889
73234
  theme,
@@ -75308,11 +75653,14 @@ const sdkSiteStyleConfigSchema = z.object({
75308
75653
  sectionOptions: sdkSectionOptionsConfigSchema.optional(),
75309
75654
  containerOptions: sdkContainerOptionsConfigSchema.optional()
75310
75655
  });
75311
- const sdkSiteLayoutConfigSchema = z.object({ footerBlocks: z.array(z.object({
75312
- id: z.string().min(1).optional(),
75313
- kind: z.string().min(1),
75314
- content: z.record(z.string(), z.unknown()).optional()
75315
- })).optional() });
75656
+ const sdkSiteLayoutConfigSchema = z.object({
75657
+ footerMode: footerModeSchema.optional(),
75658
+ footerBlocks: z.array(z.object({
75659
+ id: z.string().min(1).optional(),
75660
+ kind: z.string().min(1),
75661
+ content: z.record(z.string(), z.unknown()).optional()
75662
+ })).optional()
75663
+ });
75316
75664
  const sdkFieldSelectOptionSchema = z.object({
75317
75665
  value: z.string(),
75318
75666
  label: z.string()