@riverbankcms/sdk 0.140.0 → 0.141.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 (112) hide show
  1. package/dist/{PreviewEditorSidebar-CUyTzd5z.mjs → PreviewEditorSidebar-Q-fy9GYw.mjs} +3 -3
  2. package/dist/{PreviewEditorUI-Agkw4v5i.mjs → PreviewEditorUI-LYGzgk85.mjs} +2 -2
  3. package/dist/{SdkPreviewModeRuntime-DdbeAdS8.mjs → SdkPreviewModeRuntime-CoPYI5Jq.mjs} +72 -46
  4. package/dist/{SiteChromeCustomizeContext-CqGOz-Kv.mjs → SiteChromeCustomizeContext-D8LePX8x.mjs} +681 -196
  5. package/dist/_dts/api/src/contentRuntime.d.ts +12 -31
  6. package/dist/_dts/api/src/domainConnection.d.ts +4 -0
  7. package/dist/_dts/api/src/footerCompositionContracts.d.ts +25 -0
  8. package/dist/_dts/api/src/index.d.ts +1 -1
  9. package/dist/_dts/api/src/launchReadiness.d.ts +1 -0
  10. package/dist/_dts/api/src/navigation/dashboardSiteNav.d.ts +2 -1
  11. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +3 -26
  12. package/dist/_dts/api/src/siteOnboarding.d.ts +7 -0
  13. package/dist/_dts/billing/src/site-arrangements.d.ts +12 -3
  14. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +4 -0
  15. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +2 -0
  16. package/dist/_dts/blocks/src/system/blocks/events/event-calendar.d.ts +2 -0
  17. package/dist/_dts/blocks/src/system/blocks/events/eventSelectionFields.d.ts +122 -0
  18. package/dist/_dts/blocks/src/system/blocks/events/shared/calendarWindow.d.ts +2 -1
  19. package/dist/_dts/blocks/src/system/blocks/events/shared/eventSelection.d.ts +11 -0
  20. package/dist/_dts/blocks/src/system/blocks/location-map.d.ts +2 -2
  21. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +47 -47
  22. package/dist/_dts/blocks/src/system/data/prefetchBlockData.d.ts +5 -0
  23. package/dist/_dts/blocks/src/system/data/utils.d.ts +1 -1
  24. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +27 -1
  25. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +12 -180
  26. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +6 -90
  27. package/dist/_dts/blocks/src/system/runtime/hooks/useEventFilters.d.ts +2 -1
  28. package/dist/_dts/blocks/src/system/runtime/nodes/appointment-services.d.ts +5 -1
  29. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +4 -0
  30. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.state.d.ts +5 -0
  31. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useDiscountCodeUi.d.ts +3 -0
  32. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useDiscountCodeUi.state.d.ts +10 -3
  33. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventCheckoutHandoffRestore.d.ts +9 -0
  34. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +4 -5
  35. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useSettledCommerceView.d.ts +37 -0
  36. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +4 -3
  37. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.interactive.d.ts +3 -0
  38. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventListing.interactive.d.ts +11 -101
  39. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +7 -2
  40. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listOptions.d.ts +2 -0
  41. package/dist/_dts/blocks/src/system/transforms/registry/eventCalendar.d.ts +12 -0
  42. package/dist/_dts/commerce/src/purchase/bookingInstalmentSchedule.d.ts +4 -0
  43. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +3 -0
  44. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/loaders.d.ts +27 -1
  45. package/dist/_dts/sdk/src/public-api/contracts.d.ts +1 -1
  46. package/dist/_dts/sdk/src/version.d.ts +1 -1
  47. package/dist/_dts/theme-core/src/ThemeFontLoader.d.ts +17 -1
  48. package/dist/_dts/theme-core/src/buttons/affordanceSuppression.d.ts +17 -0
  49. package/dist/_dts/theme-core/src/buttons/buttonColourContract.d.ts +1 -1
  50. package/dist/_dts/theme-core/src/buttons/classNames.d.ts +16 -0
  51. package/dist/_dts/theme-core/src/buttons/generateButtonCss.d.ts +0 -6
  52. package/dist/_dts/theme-core/src/buttons/resolvedButtonCss.d.ts +4 -4
  53. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  54. package/dist/_dts/theme-core/src/hosted-booking-appearance.d.ts +54 -12
  55. package/dist/_dts/theme-core/src/index.d.ts +1 -1
  56. package/dist/client/{BookingConsentAgreement-CuKa82V4.mjs → BookingConsentAgreement-xJeYNdnb.mjs} +2 -2
  57. package/dist/client/{DiscountCodeDisclosure-BbYieKWB.mjs → DiscountCodeDisclosure-DdT6uzl9.mjs} +1 -1
  58. package/dist/client/{EventCombined.client-LTfR1Aby.mjs → EventCombined.client-Cp28uYuM.mjs} +11 -15
  59. package/dist/client/{EventListing.client-DUHpYSuo.mjs → EventListing.client-CD6AJsMr.mjs} +4 -17
  60. package/dist/client/{LeafletStaticMap-CHRupL6m.mjs → LeafletStaticMap-DBsAi9-e.mjs} +1 -1
  61. package/dist/client/{accordion-6HiIFxNL.mjs → accordion-6dwbOKYR.mjs} +1 -1
  62. package/dist/client/{bookingForm-Dlmid43H.mjs → bookingForm-CZE9_qPQ.mjs} +5 -5
  63. package/dist/client/bookings.mjs +175 -93
  64. package/dist/client/{carousel-DmkZRRq_.mjs → carousel-7VHGQARz.mjs} +1 -1
  65. package/dist/client/{cart-CIC24330.mjs → cart-Qg_mvQhh.mjs} +3 -3
  66. package/dist/client/{checkout-D1Zw9imo.mjs → checkout-D84rpgdr.mjs} +3 -3
  67. package/dist/client/client.mjs +602 -139
  68. package/dist/client/{context-Cy1OayHx.mjs → context-Ctp3T0Wr.mjs} +1 -1
  69. package/dist/client/{dates-aDcfoLHr.mjs → dates-DJKFcI_N.mjs} +2 -2
  70. package/dist/client/{eventCalendar-FQxMPAjf.mjs → eventCalendar-CK31z3vs.mjs} +11 -7
  71. package/dist/client/{eventCombined-Dr3ac6Hb.mjs → eventCombined-DiXd-ewn.mjs} +3 -3
  72. package/dist/client/{eventDetails-D-AzUHFQ.mjs → eventDetails-CTRgt2DK.mjs} +4 -4
  73. package/dist/client/{eventListing-B3XxBf3i.mjs → eventListing-Dy9vi_j_.mjs} +3 -3
  74. package/dist/client/{eventRegistration-I6EaokFD.mjs → eventRegistration-CnalYMr2.mjs} +124 -31
  75. package/dist/client/{form-BbWckVWY.mjs → form-eUiMveTT.mjs} +2 -2
  76. package/dist/client/{headerSection-C9qU4Y7x.mjs → headerSection-Dy9kQpB1.mjs} +2 -2
  77. package/dist/client/hooks.mjs +247 -32
  78. package/dist/client/{locationMap-CLCShvlG.mjs → locationMap-DalCmWU0.mjs} +2 -2
  79. package/dist/client/{newsletterForm-Briukxzi.mjs → newsletterForm-SDDPvhS0.mjs} +23 -10
  80. package/dist/client/{pricing-Copadu-2.mjs → pricing-OcMz4SnX.mjs} +1 -1
  81. package/dist/client/{productDetail-oujlRd-r.mjs → productDetail-BQrgHElM.mjs} +3 -3
  82. package/dist/client/{productList-BRiIRUcd.mjs → productList-OnPn_UJj.mjs} +3 -3
  83. package/dist/client/{propsDecoder-CB2KS_ko.mjs → propsDecoder-zBdsnS-N.mjs} +38 -8
  84. package/dist/client/rendering/client.mjs +446 -121
  85. package/dist/client/rendering/islands.mjs +17 -17
  86. package/dist/client/rendering.mjs +601 -138
  87. package/dist/client/{scrollToBlock-B1IS1_ua.mjs → scrollToBlock-BNM4Ob2Y.mjs} +28 -11
  88. package/dist/client/{shop-Vn6yNFED.mjs → shop-Bv1fPTwQ.mjs} +6 -6
  89. package/dist/client/{shop-commerce-BVTqipYp.mjs → shop-commerce-D4KNGaW8.mjs} +5 -5
  90. package/dist/client/{shop-commerce-store-BVnJVy7v.mjs → shop-commerce-store-2N5EV0Rc.mjs} +1 -1
  91. package/dist/client/{shopShared-COIeK77T.mjs → shopShared-DvJGdHTV.mjs} +3 -3
  92. package/dist/client/{useEventFilters-DzIUcS9n.mjs → useEventFilters-BBBvGDHQ.mjs} +17 -11
  93. package/dist/preview-next/client/runtime.mjs +3 -3
  94. package/dist/preview-next/editor-chrome.css +4 -8
  95. package/dist/{sdk-runtime-CnCLvZRv.mjs → sdk-runtime-Defnbfzw.mjs} +1 -1
  96. package/dist/{separator-DKDRJ1oH.mjs → separator-CZXO3Sb0.mjs} +1 -1
  97. package/dist/server/components.mjs +384 -62
  98. package/dist/server/config-validation.mjs +232 -26
  99. package/dist/server/config.mjs +232 -26
  100. package/dist/server/data.mjs +247 -32
  101. package/dist/server/index.mjs +1 -1
  102. package/dist/server/next.mjs +400 -69
  103. package/dist/server/page-converter.mjs +189 -25
  104. package/dist/server/prebuild.mjs +1 -1
  105. package/dist/server/rendering/server.mjs +399 -68
  106. package/dist/server/rendering.mjs +399 -68
  107. package/dist/server/routing.mjs +261 -33
  108. package/dist/server/server.mjs +248 -33
  109. package/dist/server/theme-bridge.mjs +47 -15
  110. package/dist/server/theme.mjs +1 -1
  111. package/dist/styles/index.css +28 -2
  112. package/package.json +1 -1
@@ -2461,6 +2461,7 @@ function isDisplayMediaKind$2(kind) {
2461
2461
  }
2462
2462
  const siteOptionSources = [
2463
2463
  "eventCategories",
2464
+ "eventSeries",
2464
2465
  "venues",
2465
2466
  "staffMembers"
2466
2467
  ];
@@ -7186,14 +7187,12 @@ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme) {
7186
7187
  const shadowValue = generateShadowValue$1(shadow);
7187
7188
  rules.push(`box-shadow: ${shadowValue}`);
7188
7189
  }
7189
- rules.push(`display: flex`);
7190
- rules.push(`flex-direction: column`);
7191
- rules.push(`position: relative`);
7192
- rules.push(`overflow: visible`);
7190
+ const structureSelector = cardClasses.map((cardClass) => `:where([data-theme-scope="${themeId}"]) :where(.${cardClass})`).join(",\n");
7193
7191
  const mediaRadius = MEDIA_CORNER_MAP[corners];
7194
7192
  const mediaRules = [`border-top-left-radius: ${mediaRadius}`, `border-top-right-radius: ${mediaRadius}`];
7195
7193
  const contentRules = [`padding: ${resolveContentPadding(variant)}`];
7196
7194
  return [
7195
+ `${structureSelector} {\n display: flex;\n flex-direction: column;\n position: relative;\n overflow: visible;\n}`,
7197
7196
  `${containerSelector} {\n ${rules.join(";\n ")};\n}`,
7198
7197
  ...cardDecorationPseudoRules(containerSelectors, variant.decorations, resolveColor),
7199
7198
  `${fallbackBackgroundSelector} {\n background: transparent;\n}`,
@@ -13224,7 +13223,7 @@ const BUTTON_CONTRAST_POLICY = {
13224
13223
  nonTextMinimumRatio: 3,
13225
13224
  gradientSamplesPerSegment: 20,
13226
13225
  alphaBackgrounds: "reject-without-known-surface",
13227
- disabled: "retain-full-colour-contract"
13226
+ disabled: "validated-colour-pair-with-muted-control-opacity"
13228
13227
  };
13229
13228
  function formatButtonColourContractError(error) {
13230
13229
  switch (error.tag) {
@@ -13812,6 +13811,25 @@ function parseThemeForPersistence(value) {
13812
13811
  };
13813
13812
  }
13814
13813
  //#endregion
13814
+ //#region ../theme-core/src/buttons/affordanceSuppression.ts
13815
+ /**
13816
+ * Internal: the opt-out the generated affordance rule is written against.
13817
+ *
13818
+ * A button style may declare a trailing `affordance`, which the theme renders as
13819
+ * a CSS `::after` on every button of that variant. An authored button that
13820
+ * renders its own trailing icon must not also receive the theme's, or it shows
13821
+ * two.
13822
+ *
13823
+ * Deliberately not exported from `./index`. This used to be public, and two
13824
+ * block components reached past the theme to draw arrows of their own — one of
13825
+ * them doubling up on the styles that declare an affordance, both of them
13826
+ * overriding the twelve styles that deliberately declare none. Whether a button
13827
+ * carries a trailing affordance is the style's decision; the only caller that
13828
+ * legitimately opts out is the authored-button renderer, which reaches it
13829
+ * through `themeButtonClassNameWithTrailingIcon`.
13830
+ */
13831
+ const AFFORDANCE_SUPPRESS_CLASS = "rb-affordance-off";
13832
+ //#endregion
13815
13833
  //#region ../theme-core/src/buttons/classNames.ts
13816
13834
  /**
13817
13835
  * Parse content/runtime variant identifiers into the closed theme-button class
@@ -13844,6 +13862,27 @@ function themeButtonClassName(spec) {
13844
13862
  spec.extraClassName
13845
13863
  ].filter(Boolean).join(" ");
13846
13864
  }
13865
+ /**
13866
+ * Button classes for an authored button that renders its own trailing icon.
13867
+ *
13868
+ * A separate function rather than a flag on `themeButtonClassName`, because
13869
+ * there is exactly one caller and it should stay visible in a diff. The name
13870
+ * states what the caller knows to be true of its own markup — that it renders a
13871
+ * trailing icon — rather than what the theme does about it. The consequence is
13872
+ * the theme's to decide and to change; a caller naming the consequence would be
13873
+ * asserting a policy it does not own, and would quietly become a lie the moment
13874
+ * that policy moved.
13875
+ *
13876
+ * Do not reach for this to remove an arrow you simply do not want. A trailing
13877
+ * affordance is declared by the button style, and a block overriding that is the
13878
+ * defect this replaced.
13879
+ */
13880
+ function themeButtonClassNameWithTrailingIcon(spec) {
13881
+ return themeButtonClassName({
13882
+ ...spec,
13883
+ extraClassName: [spec.extraClassName, AFFORDANCE_SUPPRESS_CLASS].filter(Boolean).join(" ")
13884
+ });
13885
+ }
13847
13886
  //#endregion
13848
13887
  //#region ../theme-core/src/interactive/generateBaseCSS.ts
13849
13888
  /**
@@ -15756,26 +15795,19 @@ function resolvedButtonFocusDeclarations(indicator, role) {
15756
15795
  ] : [["outline", value("outline", `3px solid ${indicator.colorCss}`)], ["outline-offset", value("outline-offset", "3px")]];
15757
15796
  }
15758
15797
  /**
15759
- * Disabled buttons retain their validated colour pair and add a non-layout
15760
- * visual affordance. `currentColor` keeps the cue inside the same contrast
15761
- * relationship, while retaining pointer events lets the not-allowed cursor
15762
- * remain observable.
15798
+ * Disabled buttons are deliberately muted as unavailable controls. Native
15799
+ * disabled controls are exempt from active-control contrast requirements;
15800
+ * opacity distinguishes the state without introducing a focus-like outline.
15801
+ * Retaining pointer events lets the not-allowed cursor remain observable.
15763
15802
  */
15764
15803
  const RESOLVED_BUTTON_DISABLED_DECLARATIONS = [
15765
15804
  ["cursor", "not-allowed"],
15766
15805
  ["transform", "none"],
15767
15806
  ["box-shadow", "none"],
15768
- ["outline", "2px dashed currentColor"],
15769
- ["outline-offset", "-4px"]
15807
+ ["opacity", "0.55"]
15770
15808
  ];
15771
15809
  //#endregion
15772
15810
  //#region ../theme-core/src/buttons/generateButtonCss.ts
15773
- /**
15774
- * Class the `button` runtime node adds when it renders its own trailing icon,
15775
- * so a node-level icon suppresses the theme's CSS `::after` affordance.
15776
- * Shared so generator and renderer can't drift.
15777
- */
15778
- const AFFORDANCE_SUPPRESS_CLASS = "rb-affordance-off";
15779
15811
  const BUTTON_SIZE_NAMES = buttonSizeNameSchema.options;
15780
15812
  /**
15781
15813
  * Letter spacing token → CSS value.
@@ -28208,6 +28240,7 @@ const calendarWindowQuerySchema = z$1.object({
28208
28240
  eventSurface: z$1.enum(eventSurfaceScopeValues).optional(),
28209
28241
  eventPresets: z$1.string().optional(),
28210
28242
  scheduleScope: z$1.enum(eventScheduleScopeValues).optional(),
28243
+ seriesIds: z$1.string().optional(),
28211
28244
  categoryIds: z$1.string().optional(),
28212
28245
  venueId: z$1.string().transform(normalizeCalendarFilterId).optional(),
28213
28246
  staffMemberId: z$1.string().transform(normalizeCalendarFilterId).optional()
@@ -28258,7 +28291,56 @@ const calendarWindowPageSchema = z$1.discriminatedUnion("kind", [z$1.object({
28258
28291
  kind: z$1.literal("partial"),
28259
28292
  nextCursor: calendarSourceCursorSchema
28260
28293
  })]);
28294
+ //#endregion
28295
+ //#region ../blocks/src/system/blocks/events/shared/eventSelection.ts
28296
+ /** Invalid saved selections fail closed rather than widening discovery. */
28297
+ function resolveEventSelection(mode, value) {
28298
+ if (mode !== "selected") return { kind: "matching" };
28299
+ const candidates = Array.isArray(value) ? value : typeof value === "string" ? value.split(",").filter(Boolean) : [];
28300
+ const ids = [];
28301
+ for (const candidate of candidates) {
28302
+ if (typeof candidate !== "string") return {
28303
+ kind: "selected",
28304
+ seriesIds: []
28305
+ };
28306
+ const parsed = parseEventSeriesId(candidate.trim());
28307
+ if (!parsed.ok) return {
28308
+ kind: "selected",
28309
+ seriesIds: []
28310
+ };
28311
+ ids.push(parsed.value);
28312
+ }
28313
+ return {
28314
+ kind: "selected",
28315
+ seriesIds: [...new Set(ids)].sort()
28316
+ };
28317
+ }
28318
+ /** Undefined means discovery; an empty string is an explicitly empty selection. */
28319
+ function eventSeriesScopeCsv(value) {
28320
+ if (value == null) return void 0;
28321
+ return typeof value === "string" ? value : value.join(",");
28322
+ }
28261
28323
  const defaultTransforms = createTransformRegistry([
28324
+ {
28325
+ id: "events.calendarSelectionParam",
28326
+ kind: "formatter",
28327
+ summary: "Resolve selected events instead of discovery filters",
28328
+ schema: z$1.object({ key: z$1.enum([
28329
+ "seriesIds",
28330
+ "eventSurface",
28331
+ "filterEventPresets",
28332
+ "scheduleScope",
28333
+ "filterCategoryIds",
28334
+ "filterStaffMemberId",
28335
+ "filterVenueId"
28336
+ ]) }),
28337
+ run(value, { key }) {
28338
+ const content = asPartialObject(value) ?? {};
28339
+ const selection = resolveEventSelection(content.eventSelection, content.selectedEventSeriesIds);
28340
+ if (selection.kind === "selected") return key === "seriesIds" ? selection.seriesIds : void 0;
28341
+ return key === "seriesIds" ? void 0 : content[key];
28342
+ }
28343
+ },
28262
28344
  {
28263
28345
  id: "events.calendarReadEnabled",
28264
28346
  kind: "formatter",
@@ -30012,17 +30094,16 @@ const ButtonNode = ({ value, children, href, variantId = "primary", size = "md",
30012
30094
  if (process.env.NODE_ENV === "development") console.warn("[ButtonNode] Missing href for link-required button");
30013
30095
  return null;
30014
30096
  }
30015
- const extraClassName = affordanceIcon ? [className, AFFORDANCE_SUPPRESS_CLASS].filter(Boolean).join(" ") : className;
30016
30097
  const resolvedVariantId = parseThemeButtonVariantId(variantId);
30017
30098
  if (variantId && !resolvedVariantId) {
30018
30099
  console.error("[ButtonNode] Invalid button variant", { variantId });
30019
30100
  return null;
30020
30101
  }
30021
- const combinedClassName = resolvedVariantId ? themeButtonClassName({
30102
+ const combinedClassName = resolvedVariantId ? (affordanceIcon ? themeButtonClassNameWithTrailingIcon : themeButtonClassName)({
30022
30103
  variant: resolvedVariantId,
30023
30104
  size,
30024
- extraClassName
30025
- }) : extraClassName;
30105
+ extraClassName: className
30106
+ }) : className;
30026
30107
  if (resolvedHref) return /* @__PURE__ */ jsxs("a", {
30027
30108
  href: resolvedHref,
30028
30109
  target,
@@ -31294,6 +31375,7 @@ const publicPricingSchema = z$1.object({
31294
31375
  });
31295
31376
  const publicAppointmentServiceSchema = z$1.object({
31296
31377
  id: z$1.string().min(1),
31378
+ slug: z$1.string().min(1).optional(),
31297
31379
  title: z$1.string(),
31298
31380
  description: z$1.string().nullable().optional(),
31299
31381
  durationMinutes: z$1.number().int().positive(),
@@ -31370,7 +31452,7 @@ function formatAmountOrFree(amount, currency) {
31370
31452
  currency
31371
31453
  }, { decimals: "auto" });
31372
31454
  }
31373
- function buildRows(services, pathname) {
31455
+ function buildRows(services, pathname, servicePathPrefix) {
31374
31456
  return services.map((service) => ({
31375
31457
  id: service.id,
31376
31458
  title: service.title,
@@ -31381,7 +31463,8 @@ function buildRows(services, pathname) {
31381
31463
  href: resolveDedicatedBookingHref(pathname, {
31382
31464
  kind: "appointment_service",
31383
31465
  serviceId: service.id
31384
- })
31466
+ }),
31467
+ detailsHref: servicePathPrefix && service.slug ? `${servicePathPrefix.replace(/\/$/, "")}/${encodeURIComponent(service.slug)}` : null
31385
31468
  }));
31386
31469
  }
31387
31470
  function buildAppointmentServicesView(input) {
@@ -31400,13 +31483,14 @@ function buildAppointmentServicesView(input) {
31400
31483
  };
31401
31484
  return {
31402
31485
  kind: "items",
31403
- rows: buildRows(parsed.services, input.pathname)
31486
+ rows: buildRows(parsed.services, input.pathname, input.servicePathPrefix ?? null)
31404
31487
  };
31405
31488
  }
31406
- function AppointmentServicesNode({ sectionHeading, services, pathname, className }) {
31489
+ function AppointmentServicesNode({ sectionHeading, services, pathname, servicePathPrefix, className }) {
31407
31490
  const view = buildAppointmentServicesView({
31408
31491
  services,
31409
- pathname: typeof pathname === "string" ? pathname : void 0
31492
+ pathname: typeof pathname === "string" ? pathname : void 0,
31493
+ servicePathPrefix: typeof servicePathPrefix === "string" ? servicePathPrefix : null
31410
31494
  });
31411
31495
  const rootClassName = `appointment-services ${className ?? ""}`.trim();
31412
31496
  const RowTitle = sectionHeading ? "h3" : "h2";
@@ -31437,7 +31521,10 @@ function AppointmentServicesNode({ sectionHeading, services, pathname, className
31437
31521
  children: [
31438
31522
  /* @__PURE__ */ jsx(RowTitle, {
31439
31523
  className: "appointment-services-row-title rb-text-lg rb-font-medium rb-leading-tight",
31440
- children: row.title
31524
+ children: row.detailsHref ? /* @__PURE__ */ jsx("a", {
31525
+ href: row.detailsHref,
31526
+ children: row.title
31527
+ }) : row.title
31441
31528
  }),
31442
31529
  row.description ? /* @__PURE__ */ jsx("p", {
31443
31530
  className: "appointment-services-row-description rb-text-sm rb-leading-6 status-muted",
@@ -33944,12 +34031,12 @@ const EventCompactRow = ({ event, buttonVariant = "primary", buttonText }) => {
33944
34031
  }), cta.hidden ? /* @__PURE__ */ jsx("span", {
33945
34032
  className: "event-compact-row-status",
33946
34033
  children: cta.label
33947
- }) : /* @__PURE__ */ jsxs("a", {
34034
+ }) : /* @__PURE__ */ jsx("a", {
33948
34035
  href: cta.href,
33949
34036
  target: cta.external ? "_blank" : void 0,
33950
34037
  rel: cta.external ? "noopener noreferrer" : void 0,
33951
34038
  className: `${buttonClass} event-compact-row-cta`,
33952
- children: [/* @__PURE__ */ jsx("span", { children: cta.label }), /* @__PURE__ */ jsx(ArrowRightIcon, { "aria-hidden": "true" })]
34039
+ children: /* @__PURE__ */ jsx("span", { children: cta.label })
33953
34040
  })]
33954
34041
  })]
33955
34042
  })]
@@ -35042,6 +35129,7 @@ const eventCollectionFilterSourceSchemaShape = {
35042
35129
  eventSurface: eventSurfaceScopeSchema$1.nullable().optional(),
35043
35130
  filterEventPresets: z$1.union([z$1.string(), z$1.array(z$1.string())]).nullable().optional(),
35044
35131
  scheduleScope: eventScheduleScopeSchema$1.nullable().optional(),
35132
+ seriesIds: z$1.union([z$1.string(), z$1.array(z$1.string())]).nullable().optional(),
35045
35133
  filterCategoryIds: z$1.union([z$1.string(), z$1.array(z$1.string())]).nullable().optional(),
35046
35134
  filterStaffMemberId: z$1.string().nullable().optional(),
35047
35135
  filterVenueId: z$1.string().nullable().optional()
@@ -35088,6 +35176,7 @@ const eventCollectionDisplaySchemaShape = {
35088
35176
  eventSurface: eventSurfaceScopeSchema$1,
35089
35177
  filterEventPresets: z$1.string().nullable(),
35090
35178
  scheduleScope: eventScheduleScopeSchema$1,
35179
+ seriesIds: z$1.string().nullable().optional(),
35091
35180
  filterCategoryIds: z$1.string().nullable(),
35092
35181
  filterStaffMemberId: z$1.string().nullable(),
35093
35182
  filterVenueId: z$1.string().nullable(),
@@ -35135,11 +35224,15 @@ function buildEventCollectionDisplay(source) {
35135
35224
  };
35136
35225
  }
35137
35226
  function buildEventCollectionFilterDisplay(source) {
35138
- return {
35139
- showFilters: source.showFilters ?? true,
35140
- showCategoryFilter: source.showCategoryFilter ?? "auto",
35141
- showVenueFilter: source.showVenueFilter ?? false,
35142
- showStaffFilter: source.showStaffFilter ?? true,
35227
+ const selected = source.seriesIds != null;
35228
+ const matchingFilters = selected ? {
35229
+ eventSurface: "all",
35230
+ filterEventPresets: null,
35231
+ scheduleScope: "any",
35232
+ filterCategoryIds: null,
35233
+ filterStaffMemberId: null,
35234
+ filterVenueId: null
35235
+ } : {
35143
35236
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
35144
35237
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
35145
35238
  scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
@@ -35147,6 +35240,14 @@ function buildEventCollectionFilterDisplay(source) {
35147
35240
  filterStaffMemberId: normalizeEventStaffMemberId(source.filterStaffMemberId),
35148
35241
  filterVenueId: normalizeEventVenueId(source.filterVenueId)
35149
35242
  };
35243
+ return {
35244
+ seriesIds: eventSeriesScopeCsv(source.seriesIds),
35245
+ showFilters: selected ? false : source.showFilters ?? true,
35246
+ showCategoryFilter: source.showCategoryFilter ?? "auto",
35247
+ showVenueFilter: source.showVenueFilter ?? false,
35248
+ showStaffFilter: source.showStaffFilter ?? true,
35249
+ ...matchingFilters
35250
+ };
35150
35251
  }
35151
35252
  function buildEventCollectionPresentationDisplay(source) {
35152
35253
  return {
@@ -35161,14 +35262,15 @@ function buildEventCollectionPresentationDisplay(source) {
35161
35262
  }
35162
35263
  function buildEventCollectionHydration(source, now = /* @__PURE__ */ new Date()) {
35163
35264
  const timeZone = source.calendarWindow?.scope.timeZone ?? toBookingTimeZone(source.bookingTimeZone);
35265
+ const selected = source.seriesIds != null;
35164
35266
  return {
35165
35267
  calendarWindow: source.calendarWindow ?? void 0,
35166
35268
  siteId: source.siteId,
35167
35269
  stage: source.stage,
35168
35270
  prefetchedEvents: source.events != null,
35169
- eventSurface: normalizeEventSurfaceScope(source.eventSurface),
35170
- filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
35171
- scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
35271
+ eventSurface: selected ? "all" : normalizeEventSurfaceScope(source.eventSurface),
35272
+ filterEventPresets: selected ? null : normalizeEventSurfacePresetsCsv(source.filterEventPresets),
35273
+ scheduleScope: selected ? "any" : normalizeEventScheduleScope(source.scheduleScope),
35172
35274
  calendarContext: {
35173
35275
  timeZone,
35174
35276
  initialToday: source.calendarWindow?.initialToday ?? getLocalScheduleDate(now, timeZone)
@@ -35738,13 +35840,10 @@ function OfferingsPreviewContent(props) {
35738
35840
  }),
35739
35841
  groupCta ? /* @__PURE__ */ jsx("footer", {
35740
35842
  className: "offerings-preview-card-footer rb-mt-auto rb-flex",
35741
- children: /* @__PURE__ */ jsxs("a", {
35843
+ children: /* @__PURE__ */ jsx("a", {
35742
35844
  className: groupLinkClass,
35743
35845
  href: groupCta.href,
35744
- children: [/* @__PURE__ */ jsx("span", { children: groupCta.label }), /* @__PURE__ */ jsx(ArrowRightIcon, {
35745
- "aria-hidden": "true",
35746
- className: "offerings-preview-link-icon rb-h-4 rb-w-4"
35747
- })]
35846
+ children: /* @__PURE__ */ jsx("span", { children: groupCta.label })
35748
35847
  })
35749
35848
  }) : null
35750
35849
  ]
@@ -38689,6 +38788,65 @@ const formBlockDefinition = {
38689
38788
  runtime: formBlockRuntime
38690
38789
  };
38691
38790
  //#endregion
38791
+ //#region ../blocks/src/system/blocks/events/eventSelectionFields.ts
38792
+ const eventSelectionFields = [{
38793
+ id: "eventSelection",
38794
+ type: "select",
38795
+ label: "Events to show",
38796
+ required: false,
38797
+ multiple: false,
38798
+ defaultValue: "matching",
38799
+ options: [{
38800
+ value: "matching",
38801
+ label: "All matching events"
38802
+ }, {
38803
+ value: "selected",
38804
+ label: "Selected events"
38805
+ }],
38806
+ ui: { intent: "behavior" }
38807
+ }, createSiteOptionSelectField({
38808
+ id: "selectedEventSeriesIds",
38809
+ label: "Selected events",
38810
+ description: "Show upcoming dates for these events. Other event filters do not apply.",
38811
+ required: false,
38812
+ multiple: true,
38813
+ defaultValue: [],
38814
+ optionSource: "eventSeries",
38815
+ ui: {
38816
+ intent: "behavior",
38817
+ searchThreshold: 1,
38818
+ visibleWhen: {
38819
+ field: "eventSelection",
38820
+ equals: "selected"
38821
+ }
38822
+ }
38823
+ })];
38824
+ function withEventSelectionVisibility(field) {
38825
+ if (![
38826
+ "eventSurface",
38827
+ "filterEventPresets",
38828
+ "scheduleScope",
38829
+ "filterCategoryIds",
38830
+ "filterStaffMemberId",
38831
+ "filterVenueId",
38832
+ "showFilters",
38833
+ "showCategoryFilter",
38834
+ "showVenueFilter",
38835
+ "showStaffFilter"
38836
+ ].includes(field.id)) return field;
38837
+ const visibleWhen = field.ui?.visibleWhen;
38838
+ return {
38839
+ ...field,
38840
+ ui: {
38841
+ ...field.ui,
38842
+ visibleWhen: { allOf: [{
38843
+ field: "eventSelection",
38844
+ notEquals: "selected"
38845
+ }, ...visibleWhen ? [visibleWhen] : []] }
38846
+ }
38847
+ };
38848
+ }
38849
+ //#endregion
38692
38850
  //#region ../blocks/src/system/runtime/nodes/events/EventCalendar.interactive.ts
38693
38851
  const eventCalendarSourceSchema = z$1.object({
38694
38852
  ...eventCollectionSourceSchemaShape,
@@ -38786,19 +38944,59 @@ function buildEventCalendarInteractiveIslandProps(props) {
38786
38944
  //#region ../blocks/src/system/blocks/events/eventCalendarDataLoaders.ts
38787
38945
  const scopeParams = {
38788
38946
  siteId: { $bind: { from: "$root.siteId" } },
38947
+ seriesIds: { $bind: {
38948
+ from: "content",
38949
+ transforms: [{
38950
+ id: "events.calendarSelectionParam",
38951
+ options: { key: "seriesIds" }
38952
+ }]
38953
+ } },
38789
38954
  stage: { $bind: {
38790
38955
  from: "$root.previewStage",
38791
38956
  fallback: "published"
38792
38957
  } },
38793
- eventSurface: { $bind: { from: "content.eventSurface" } },
38958
+ eventSurface: { $bind: {
38959
+ from: "content",
38960
+ transforms: [{
38961
+ id: "events.calendarSelectionParam",
38962
+ options: { key: "eventSurface" }
38963
+ }, { id: "value.csv" }]
38964
+ } },
38794
38965
  eventPresets: { $bind: {
38795
- from: "content.filterEventPresets",
38796
- transforms: [{ id: "value.csv" }]
38966
+ from: "content",
38967
+ transforms: [{
38968
+ id: "events.calendarSelectionParam",
38969
+ options: { key: "filterEventPresets" }
38970
+ }, { id: "value.csv" }]
38971
+ } },
38972
+ scheduleScope: { $bind: {
38973
+ from: "content",
38974
+ transforms: [{
38975
+ id: "events.calendarSelectionParam",
38976
+ options: { key: "scheduleScope" }
38977
+ }, { id: "value.csv" }]
38797
38978
  } },
38798
- scheduleScope: { $bind: { from: "content.scheduleScope" } },
38799
- categoryIds: { $bind: { from: "content.filterCategoryIds" } },
38800
- staffMemberId: { $bind: { from: "content.filterStaffMemberId" } },
38801
- venueId: { $bind: { from: "content.filterVenueId" } }
38979
+ categoryIds: { $bind: {
38980
+ from: "content",
38981
+ transforms: [{
38982
+ id: "events.calendarSelectionParam",
38983
+ options: { key: "filterCategoryIds" }
38984
+ }, { id: "value.csv" }]
38985
+ } },
38986
+ staffMemberId: { $bind: {
38987
+ from: "content",
38988
+ transforms: [{
38989
+ id: "events.calendarSelectionParam",
38990
+ options: { key: "filterStaffMemberId" }
38991
+ }, { id: "value.csv" }]
38992
+ } },
38993
+ venueId: { $bind: {
38994
+ from: "content",
38995
+ transforms: [{
38996
+ id: "events.calendarSelectionParam",
38997
+ options: { key: "filterVenueId" }
38998
+ }, { id: "value.csv" }]
38999
+ } }
38802
39000
  };
38803
39001
  /** Combined renders two jobs; calendar-only variants never pay for the list seed. */
38804
39002
  const eventCalendarDataLoaders = {
@@ -38928,6 +39126,13 @@ const eventCalendarLayout = [styledSection({
38928
39126
  from: "content.defaultCalendarView",
38929
39127
  fallback: "month"
38930
39128
  } },
39129
+ seriesIds: { $bind: {
39130
+ from: "content",
39131
+ transforms: [{
39132
+ id: "events.calendarSelectionParam",
39133
+ options: { key: "seriesIds" }
39134
+ }]
39135
+ } },
38931
39136
  showFilters: { $bind: {
38932
39137
  from: "content.showFilters",
38933
39138
  fallback: true
@@ -38949,17 +39154,47 @@ const eventCalendarLayout = [styledSection({
38949
39154
  fallback: true
38950
39155
  } },
38951
39156
  eventSurface: { $bind: {
38952
- from: "content.eventSurface",
38953
- fallback: "all"
39157
+ from: "content",
39158
+ transforms: [{
39159
+ id: "events.calendarSelectionParam",
39160
+ options: { key: "eventSurface" }
39161
+ }]
39162
+ } },
39163
+ filterEventPresets: { $bind: {
39164
+ from: "content",
39165
+ transforms: [{
39166
+ id: "events.calendarSelectionParam",
39167
+ options: { key: "filterEventPresets" }
39168
+ }]
38954
39169
  } },
38955
- filterEventPresets: { $bind: { from: "content.filterEventPresets" } },
38956
39170
  scheduleScope: { $bind: {
38957
- from: "content.scheduleScope",
38958
- fallback: "any"
39171
+ from: "content",
39172
+ transforms: [{
39173
+ id: "events.calendarSelectionParam",
39174
+ options: { key: "scheduleScope" }
39175
+ }]
39176
+ } },
39177
+ filterCategoryIds: { $bind: {
39178
+ from: "content",
39179
+ transforms: [{
39180
+ id: "events.calendarSelectionParam",
39181
+ options: { key: "filterCategoryIds" }
39182
+ }]
39183
+ } },
39184
+ filterStaffMemberId: { $bind: {
39185
+ from: "content",
39186
+ transforms: [{
39187
+ id: "events.calendarSelectionParam",
39188
+ options: { key: "filterStaffMemberId" }
39189
+ }]
39190
+ } },
39191
+ filterVenueId: { $bind: {
39192
+ from: "content",
39193
+ transforms: [{
39194
+ id: "events.calendarSelectionParam",
39195
+ options: { key: "filterVenueId" }
39196
+ }]
38959
39197
  } },
38960
- filterCategoryIds: { $bind: { from: "content.filterCategoryIds" } },
38961
- filterStaffMemberId: { $bind: { from: "content.filterStaffMemberId" } },
38962
- filterVenueId: { $bind: { from: "content.filterVenueId" } },
38963
39198
  cardRole: { $bind: {
38964
39199
  from: "content._cardStyles.role",
38965
39200
  fallback: "default"
@@ -39463,6 +39698,7 @@ const eventCalendarAdditionalFields = [
39463
39698
  }
39464
39699
  }
39465
39700
  },
39701
+ ...eventSelectionFields,
39466
39702
  {
39467
39703
  id: "eventSurface",
39468
39704
  type: "select",
@@ -39672,7 +39908,7 @@ const eventCalendarBlockDefinition = {
39672
39908
  titleSource: "heading",
39673
39909
  description: "Display events with calendar, list, or combined views with filtering",
39674
39910
  skipSectionStyles: true,
39675
- additionalFields: eventCalendarAdditionalFields,
39911
+ additionalFields: eventCalendarAdditionalFields.map(withEventSelectionVisibility),
39676
39912
  styleTokens: {
39677
39913
  background: "surface",
39678
39914
  typography: "body",
@@ -39781,6 +40017,7 @@ const eventListingHydrationSchema = z$1.object({
39781
40017
  eventSurface: eventSurfaceScopeSchema,
39782
40018
  filterEventPresets: z$1.string().nullable(),
39783
40019
  scheduleScope: eventScheduleScopeSchema,
40020
+ seriesIds: z$1.string().nullable().optional(),
39784
40021
  filterCategoryIds: z$1.string().nullable().optional(),
39785
40022
  filterStaffMemberId: z$1.string().nullable().optional(),
39786
40023
  filterVenueId: z$1.string().nullable().optional(),
@@ -39841,6 +40078,7 @@ const eventListingBlockRuntime = defineProgressiveBlockRuntime({
39841
40078
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
39842
40079
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
39843
40080
  scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
40081
+ seriesIds: eventSeriesScopeCsv(source.seriesIds),
39844
40082
  filterCategoryIds: normalizeEventCategoryIdsCsv(source.filterCategoryIds),
39845
40083
  filterStaffMemberId: normalizeEventStaffMemberId(source.filterStaffMemberId),
39846
40084
  filterVenueId: normalizeEventVenueId(source.filterVenueId),
@@ -45198,9 +45436,56 @@ function generateNavLinkCSS(options) {
45198
45436
  if (baseRules.length > 0) cssChunks.unshift(`\n${selector} {\n ${baseRules.join("\n ")}\n}\n`);
45199
45437
  if (hoverRules.length > 0) cssChunks.push(`\n${hoverSelector} {\n ${hoverRules.join("\n ")}\n}\n`);
45200
45438
  if (activeRules.length > 0) cssChunks.push(`\n${activeSelector} {\n ${activeRules.join("\n ")}\n}\n`);
45439
+ cssChunks.push(navLinkFocusVisibleCss(selector, style, theme, tokens));
45201
45440
  cssChunks.push(generateInteractiveChildInheritanceCSS(selector));
45202
45441
  return cssChunks.join("");
45203
45442
  }
45443
+ /**
45444
+ * Keyboard focus indicator for nav links, in the header, the footer and header
45445
+ * dropdown menus — the three surfaces `generateNavLinkCSS` serves.
45446
+ *
45447
+ * Emitted unconditionally rather than as an effect, because focus visibility is
45448
+ * a floor rather than a style choice. Only the interaction-palette pill ever
45449
+ * wrote an `effects.focus` group, so every other nav — every `quiet` palette,
45450
+ * every footer, every dropdown — previously fell back to whatever outline the
45451
+ * browser drew.
45452
+ *
45453
+ * The outline takes the link's own resolved colour, so on a header that adapts
45454
+ * to the section it floats over it moves with the adaptation instead of
45455
+ * asserting a brand colour against an unknown backdrop.
45456
+ *
45457
+ * Where the style asks for `soft-glow`, the same halo goes on the outline. That
45458
+ * treatment exists because the theme has already conceded the resolved colour
45459
+ * alone is not reliable over an unpredictable backdrop — a hero photograph with
45460
+ * mixed luminance. Without it the text would stay legible by its halo while the
45461
+ * focus ring, which is the part with a contrast requirement of its own, would
45462
+ * not.
45463
+ *
45464
+ * A style that also paints a focus pill keeps it: this adds an outline around
45465
+ * that pill rather than replacing it.
45466
+ */
45467
+ function navLinkFocusVisibleCss(selector, style, theme, tokens) {
45468
+ const color = tokens.getColor(style.colorToken);
45469
+ const halo = buildTextContrastShadow({
45470
+ theme,
45471
+ colorToken: style.colorToken,
45472
+ treatment: style.contrastTreatment
45473
+ });
45474
+ return `
45475
+ ${selector}:focus-visible {
45476
+ outline: 2px solid ${color};
45477
+ outline-offset: ${focusOutlineOffset(selector)};${halo ? `\n box-shadow: ${halo};` : ""}
45478
+ }
45479
+ `;
45480
+ }
45481
+ /**
45482
+ * Dropdown items are full-width inside a panel with no horizontal padding, so
45483
+ * an outset outline would paint over and past the panel's own border. Keeping
45484
+ * it flush is the only way it reads as belonging to the item.
45485
+ */
45486
+ function focusOutlineOffset(selector) {
45487
+ return selector.includes("nav-dropdown-link") ? "-2px" : "3px";
45488
+ }
45204
45489
  function buildTextContrastShadow(input) {
45205
45490
  if (input.treatment !== "soft-glow") return null;
45206
45491
  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)";
@@ -48333,7 +48618,7 @@ function pageBlockPosition(index, blockCount) {
48333
48618
  //#endregion
48334
48619
  //#region ../blocks/src/pageRendererContracts.ts
48335
48620
  function buildPageRendererRootRuntime(dataContext) {
48336
- const { siteId = null, pageId = null, pathname = null, bookingTimeZone, previewStage = "published", apiBaseUrl = null, portalToken = null, bookingOfferingPresets = [], mailingList = { tag: "not_configured" }, eventRegistrationOverlayEnabled = false, loaderFailures = {} } = dataContext ?? {};
48621
+ 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 ?? {};
48337
48622
  return {
48338
48623
  siteId,
48339
48624
  pageId,
@@ -48343,6 +48628,7 @@ function buildPageRendererRootRuntime(dataContext) {
48343
48628
  apiBaseUrl,
48344
48629
  portalToken,
48345
48630
  bookingOfferingPresets,
48631
+ appointmentServicePathPrefix,
48346
48632
  mailingList,
48347
48633
  eventRegistrationOverlayEnabled,
48348
48634
  loaderFailures
@@ -53085,7 +53371,8 @@ const appointmentServicesBlockDefinition = {
53085
53371
  el("appointment-services", {
53086
53372
  sectionHeading: { $bind: { from: "content.heading" } },
53087
53373
  services: { $bind: { from: "data.services" } },
53088
- pathname: { $bind: { from: "$root.pathname" } }
53374
+ pathname: { $bind: { from: "$root.pathname" } },
53375
+ servicePathPrefix: { $bind: { from: "$root.appointmentServicePathPrefix" } }
53089
53376
  })
53090
53377
  ], { gap: "lg" })
53091
53378
  })]
@@ -59779,10 +60066,19 @@ const containerResponsiveThemeCss = `/*
59779
60066
  padding-top: 2.5rem;
59780
60067
  padding-bottom: 2.5rem;
59781
60068
  }
60069
+ /* Clearance for a surface that is not an authored block page.
60070
+ An authored page gets this through its opening block's container (see the
60071
+ overlay-clearance rule in styles.css). A reserved surface or a system
60072
+ destination page has no such block, so it consumes the same variable here.
60073
+ Marked with one class rather than per-surface selectors, because the last
60074
+ two additions to this family were both missed and shipped overlapping. */
59782
60075
  :where(.theme-scope)
59783
60076
  [data-site-document-layout-mode="header-overlay"]
59784
- [data-rb-reserved-surface].rb-reserved-surface-main {
59785
- padding-top: calc(var(--rb-reserved-surface-header-offset, 5.5rem) + 2.5rem);
60077
+ .rb-overlay-clearance-surface {
60078
+ padding-top: calc(
60079
+ var(--site-overlay-header-clearance, 5.5rem) +
60080
+ var(--site-overlay-opening-content-extra-pad, clamp(1rem, 2vw, 1.75rem))
60081
+ );
59786
60082
  }
59787
60083
  .rb-preview-editor-offset {
59788
60084
  padding-bottom: calc(
@@ -70139,6 +70435,23 @@ const containerResponsiveThemeCss = `/*
70139
70435
  gap: 0.5rem;
70140
70436
  }
70141
70437
 
70438
+ /* Error panel and field notes. The form block scopes these to its own
70439
+ data-block, so the newsletter form (which also renders in the footer)
70440
+ needs its own copy of the treatment. */
70441
+ :where(.theme-scope) .newsletter-form .fb-error:not([data-field-validation-message]) {
70442
+ background-color: var(--fb-error-bg);
70443
+ color: var(--fb-error-text);
70444
+ border: 1px solid var(--fb-error-border);
70445
+ border-radius: var(--rb-block-radius);
70446
+ padding: 1rem;
70447
+ font-size: 0.875rem;
70448
+ }
70449
+
70450
+ :where(.theme-scope) .newsletter-form [data-field-validation-message] {
70451
+ font-size: 0.875rem;
70452
+ color: var(--fb-error-text);
70453
+ }
70454
+
70142
70455
  /* Required indicator */
70143
70456
  :where(.theme-scope) .newsletter-required {
70144
70457
  color: rgb(var(--tb-primary));
@@ -72507,6 +72820,15 @@ z$1.object({
72507
72820
  }).optional(),
72508
72821
  overrides: z$1.record(z$1.string(), z$1.string()).optional()
72509
72822
  }).passthrough();
72823
+ z$1.object({
72824
+ palette: paletteColorsSchema,
72825
+ paletteMode: paletteModeSchema,
72826
+ typography: z$1.object({
72827
+ headingFamily: z$1.string().trim().min(1),
72828
+ bodyFamily: z$1.string().trim().min(1)
72829
+ }).strict(),
72830
+ corners
72831
+ }).strict();
72510
72832
  //#endregion
72511
72833
  //#region src/rendering/blocks-theme-runtime.ts
72512
72834
  function parseBlocksTheme(theme) {
@@ -74461,12 +74783,12 @@ function normalizeContent(block) {
74461
74783
  }
74462
74784
  /**
74463
74785
  * Normalize params for API calls.
74464
- * Converts all values to strings.
74786
+ * Converts supplied values to strings, preserving absent versus explicitly empty params.
74465
74787
  */
74466
74788
  function normalizeParams(input) {
74467
74789
  if (!input || typeof input !== "object") return {};
74468
74790
  const output = {};
74469
- for (const [key, value] of Object.entries(input)) output[key] = coerceToString(value);
74791
+ for (const [key, value] of Object.entries(input)) if (value !== void 0 && value !== null) output[key] = coerceToString(value);
74470
74792
  return output;
74471
74793
  }
74472
74794
  function isNonArrayObject(value) {
@@ -74530,6 +74852,14 @@ function coercePayload(payload) {
74530
74852
  * - Frontend: provide internal API client
74531
74853
  * - SDK: provide SDK client with endpoint mapping
74532
74854
  */
74855
+ function projectDataLoaders(loaders, shouldLoad, blockId, blockKind) {
74856
+ if (!shouldLoad) return [...loaders];
74857
+ return loaders.filter(({ key }) => shouldLoad({
74858
+ blockId,
74859
+ blockKind,
74860
+ loaderKey: key
74861
+ }));
74862
+ }
74533
74863
  function shouldSkipResolvedLoader(input) {
74534
74864
  if (input.loader.requiredParams?.some((param) => !input.params[param]?.trim())) return true;
74535
74865
  return Boolean(input.isValidEndpoint && !input.isValidEndpoint(input.endpoint));
@@ -74558,7 +74888,7 @@ async function prefetchBlockData$1(page, context, options) {
74558
74888
  return (await prefetchBlockDataWithFailures(page, context, options)).resolvedData;
74559
74889
  }
74560
74890
  async function prefetchBlockDataWithFailures(page, context, options) {
74561
- const { apiClient, isValidEndpoint, onError, getCustomBlockLoaders, getBlockDefinition: getBlockDefinition$1 = getBlockDefinition, resolveParamsBindings: resolveParamsBindings$1 = resolveParamsBindings } = options;
74891
+ const { apiClient, isValidEndpoint, onError, getCustomBlockLoaders, getBlockDefinition: getBlockDefinition$1 = getBlockDefinition, resolveParamsBindings: resolveParamsBindings$1 = resolveParamsBindings, shouldLoad } = options;
74562
74892
  const results = {};
74563
74893
  const loaderFailures = {};
74564
74894
  const tasks = [];
@@ -74575,6 +74905,7 @@ async function prefetchBlockDataWithFailures(page, context, options) {
74575
74905
  loader
74576
74906
  })) : [];
74577
74907
  } else loaders = [];
74908
+ loaders = projectDataLoaders(loaders, shouldLoad, blockId, block.kind);
74578
74909
  if (loaders.length === 0) continue;
74579
74910
  const root = {
74580
74911
  ...context.root ?? {},
@@ -74672,8 +75003,8 @@ function toStringParams(value) {
74672
75003
  for (const [k, v] of Object.entries(value)) if (typeof v === "string") out[k] = v;
74673
75004
  return out;
74674
75005
  }
74675
- function stripEmptyStringValues(params) {
74676
- for (const [key, value] of Object.entries(params)) if (value === "") delete params[key];
75006
+ function stripEmptyStringValues(params, endpoint) {
75007
+ for (const [key, value] of Object.entries(params)) if (value === "" && !(key === "seriesIds" && (endpoint === "listPublicEvents" || endpoint === "listPublicEventCalendarWindow"))) delete params[key];
74677
75008
  }
74678
75009
  function ensureSiteId(params, context) {
74679
75010
  if (!params.siteId && typeof context.siteId === "string" && context.siteId) params.siteId = context.siteId;
@@ -74686,7 +75017,7 @@ function ensureSiteId(params, context) {
74686
75017
  */
74687
75018
  function normalizeLoaderParams(endpoint, params, context) {
74688
75019
  const out = toStringParams(params);
74689
- stripEmptyStringValues(out);
75020
+ stripEmptyStringValues(out, endpoint);
74690
75021
  if (endpoint === "getPublicFormById") {
74691
75022
  ensureSiteId(out, context);
74692
75023
  if (!out.formId && out.formSlug) {