@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
@@ -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}`,
@@ -12700,7 +12699,7 @@ const BUTTON_CONTRAST_POLICY = {
12700
12699
  nonTextMinimumRatio: 3,
12701
12700
  gradientSamplesPerSegment: 20,
12702
12701
  alphaBackgrounds: "reject-without-known-surface",
12703
- disabled: "retain-full-colour-contract"
12702
+ disabled: "validated-colour-pair-with-muted-control-opacity"
12704
12703
  };
12705
12704
  function formatButtonColourContractError(error) {
12706
12705
  switch (error.tag) {
@@ -13288,6 +13287,25 @@ function parseThemeForPersistence(value) {
13288
13287
  };
13289
13288
  }
13290
13289
  //#endregion
13290
+ //#region ../theme-core/src/buttons/affordanceSuppression.ts
13291
+ /**
13292
+ * Internal: the opt-out the generated affordance rule is written against.
13293
+ *
13294
+ * A button style may declare a trailing `affordance`, which the theme renders as
13295
+ * a CSS `::after` on every button of that variant. An authored button that
13296
+ * renders its own trailing icon must not also receive the theme's, or it shows
13297
+ * two.
13298
+ *
13299
+ * Deliberately not exported from `./index`. This used to be public, and two
13300
+ * block components reached past the theme to draw arrows of their own — one of
13301
+ * them doubling up on the styles that declare an affordance, both of them
13302
+ * overriding the twelve styles that deliberately declare none. Whether a button
13303
+ * carries a trailing affordance is the style's decision; the only caller that
13304
+ * legitimately opts out is the authored-button renderer, which reaches it
13305
+ * through `themeButtonClassNameWithTrailingIcon`.
13306
+ */
13307
+ const AFFORDANCE_SUPPRESS_CLASS = "rb-affordance-off";
13308
+ //#endregion
13291
13309
  //#region ../theme-core/src/buttons/classNames.ts
13292
13310
  /**
13293
13311
  * Parse content/runtime variant identifiers into the closed theme-button class
@@ -13320,6 +13338,27 @@ function themeButtonClassName(spec) {
13320
13338
  spec.extraClassName
13321
13339
  ].filter(Boolean).join(" ");
13322
13340
  }
13341
+ /**
13342
+ * Button classes for an authored button that renders its own trailing icon.
13343
+ *
13344
+ * A separate function rather than a flag on `themeButtonClassName`, because
13345
+ * there is exactly one caller and it should stay visible in a diff. The name
13346
+ * states what the caller knows to be true of its own markup — that it renders a
13347
+ * trailing icon — rather than what the theme does about it. The consequence is
13348
+ * the theme's to decide and to change; a caller naming the consequence would be
13349
+ * asserting a policy it does not own, and would quietly become a lie the moment
13350
+ * that policy moved.
13351
+ *
13352
+ * Do not reach for this to remove an arrow you simply do not want. A trailing
13353
+ * affordance is declared by the button style, and a block overriding that is the
13354
+ * defect this replaced.
13355
+ */
13356
+ function themeButtonClassNameWithTrailingIcon(spec) {
13357
+ return themeButtonClassName({
13358
+ ...spec,
13359
+ extraClassName: [spec.extraClassName, AFFORDANCE_SUPPRESS_CLASS].filter(Boolean).join(" ")
13360
+ });
13361
+ }
13323
13362
  //#endregion
13324
13363
  //#region ../theme-core/src/interactive/generateBaseCSS.ts
13325
13364
  /**
@@ -15232,26 +15271,19 @@ function resolvedButtonFocusDeclarations(indicator, role) {
15232
15271
  ] : [["outline", value("outline", `3px solid ${indicator.colorCss}`)], ["outline-offset", value("outline-offset", "3px")]];
15233
15272
  }
15234
15273
  /**
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.
15274
+ * Disabled buttons are deliberately muted as unavailable controls. Native
15275
+ * disabled controls are exempt from active-control contrast requirements;
15276
+ * opacity distinguishes the state without introducing a focus-like outline.
15277
+ * Retaining pointer events lets the not-allowed cursor remain observable.
15239
15278
  */
15240
15279
  const RESOLVED_BUTTON_DISABLED_DECLARATIONS = [
15241
15280
  ["cursor", "not-allowed"],
15242
15281
  ["transform", "none"],
15243
15282
  ["box-shadow", "none"],
15244
- ["outline", "2px dashed currentColor"],
15245
- ["outline-offset", "-4px"]
15283
+ ["opacity", "0.55"]
15246
15284
  ];
15247
15285
  //#endregion
15248
15286
  //#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
15287
  const BUTTON_SIZE_NAMES = buttonSizeNameSchema.options;
15256
15288
  /**
15257
15289
  * Letter spacing token → CSS value.
@@ -27332,6 +27364,7 @@ const calendarWindowQuerySchema = z.object({
27332
27364
  eventSurface: z.enum(eventSurfaceScopeValues).optional(),
27333
27365
  eventPresets: z.string().optional(),
27334
27366
  scheduleScope: z.enum(eventScheduleScopeValues).optional(),
27367
+ seriesIds: z.string().optional(),
27335
27368
  categoryIds: z.string().optional(),
27336
27369
  venueId: z.string().transform(normalizeCalendarFilterId).optional(),
27337
27370
  staffMemberId: z.string().transform(normalizeCalendarFilterId).optional()
@@ -27382,7 +27415,56 @@ const calendarWindowPageSchema = z.discriminatedUnion("kind", [z.object({
27382
27415
  kind: z.literal("partial"),
27383
27416
  nextCursor: calendarSourceCursorSchema
27384
27417
  })]);
27418
+ //#endregion
27419
+ //#region ../blocks/src/system/blocks/events/shared/eventSelection.ts
27420
+ /** Invalid saved selections fail closed rather than widening discovery. */
27421
+ function resolveEventSelection(mode, value) {
27422
+ if (mode !== "selected") return { kind: "matching" };
27423
+ const candidates = Array.isArray(value) ? value : typeof value === "string" ? value.split(",").filter(Boolean) : [];
27424
+ const ids = [];
27425
+ for (const candidate of candidates) {
27426
+ if (typeof candidate !== "string") return {
27427
+ kind: "selected",
27428
+ seriesIds: []
27429
+ };
27430
+ const parsed = parseEventSeriesId(candidate.trim());
27431
+ if (!parsed.ok) return {
27432
+ kind: "selected",
27433
+ seriesIds: []
27434
+ };
27435
+ ids.push(parsed.value);
27436
+ }
27437
+ return {
27438
+ kind: "selected",
27439
+ seriesIds: [...new Set(ids)].sort()
27440
+ };
27441
+ }
27442
+ /** Undefined means discovery; an empty string is an explicitly empty selection. */
27443
+ function eventSeriesScopeCsv(value) {
27444
+ if (value == null) return void 0;
27445
+ return typeof value === "string" ? value : value.join(",");
27446
+ }
27385
27447
  const defaultTransforms = createTransformRegistry([
27448
+ {
27449
+ id: "events.calendarSelectionParam",
27450
+ kind: "formatter",
27451
+ summary: "Resolve selected events instead of discovery filters",
27452
+ schema: z.object({ key: z.enum([
27453
+ "seriesIds",
27454
+ "eventSurface",
27455
+ "filterEventPresets",
27456
+ "scheduleScope",
27457
+ "filterCategoryIds",
27458
+ "filterStaffMemberId",
27459
+ "filterVenueId"
27460
+ ]) }),
27461
+ run(value, { key }) {
27462
+ const content = asPartialObject(value) ?? {};
27463
+ const selection = resolveEventSelection(content.eventSelection, content.selectedEventSeriesIds);
27464
+ if (selection.kind === "selected") return key === "seriesIds" ? selection.seriesIds : void 0;
27465
+ return key === "seriesIds" ? void 0 : content[key];
27466
+ }
27467
+ },
27386
27468
  {
27387
27469
  id: "events.calendarReadEnabled",
27388
27470
  kind: "formatter",
@@ -29138,17 +29220,16 @@ const ButtonNode = ({ value, children, href, variantId = "primary", size = "md",
29138
29220
  if (process.env.NODE_ENV === "development") console.warn("[ButtonNode] Missing href for link-required button");
29139
29221
  return null;
29140
29222
  }
29141
- const extraClassName = affordanceIcon ? [className, AFFORDANCE_SUPPRESS_CLASS].filter(Boolean).join(" ") : className;
29142
29223
  const resolvedVariantId = parseThemeButtonVariantId(variantId);
29143
29224
  if (variantId && !resolvedVariantId) {
29144
29225
  console.error("[ButtonNode] Invalid button variant", { variantId });
29145
29226
  return null;
29146
29227
  }
29147
- const combinedClassName = resolvedVariantId ? themeButtonClassName({
29228
+ const combinedClassName = resolvedVariantId ? (affordanceIcon ? themeButtonClassNameWithTrailingIcon : themeButtonClassName)({
29148
29229
  variant: resolvedVariantId,
29149
29230
  size,
29150
- extraClassName
29151
- }) : extraClassName;
29231
+ extraClassName: className
29232
+ }) : className;
29152
29233
  if (resolvedHref) return /* @__PURE__ */ jsxs("a", {
29153
29234
  href: resolvedHref,
29154
29235
  target,
@@ -30411,6 +30492,7 @@ const publicPricingSchema = z.object({
30411
30492
  });
30412
30493
  const publicAppointmentServiceSchema = z.object({
30413
30494
  id: z.string().min(1),
30495
+ slug: z.string().min(1).optional(),
30414
30496
  title: z.string(),
30415
30497
  description: z.string().nullable().optional(),
30416
30498
  durationMinutes: z.number().int().positive(),
@@ -30487,7 +30569,7 @@ function formatAmountOrFree(amount, currency) {
30487
30569
  currency
30488
30570
  }, { decimals: "auto" });
30489
30571
  }
30490
- function buildRows(services, pathname) {
30572
+ function buildRows(services, pathname, servicePathPrefix) {
30491
30573
  return services.map((service) => ({
30492
30574
  id: service.id,
30493
30575
  title: service.title,
@@ -30498,7 +30580,8 @@ function buildRows(services, pathname) {
30498
30580
  href: resolveDedicatedBookingHref(pathname, {
30499
30581
  kind: "appointment_service",
30500
30582
  serviceId: service.id
30501
- })
30583
+ }),
30584
+ detailsHref: servicePathPrefix && service.slug ? `${servicePathPrefix.replace(/\/$/, "")}/${encodeURIComponent(service.slug)}` : null
30502
30585
  }));
30503
30586
  }
30504
30587
  function buildAppointmentServicesView(input) {
@@ -30517,13 +30600,14 @@ function buildAppointmentServicesView(input) {
30517
30600
  };
30518
30601
  return {
30519
30602
  kind: "items",
30520
- rows: buildRows(parsed.services, input.pathname)
30603
+ rows: buildRows(parsed.services, input.pathname, input.servicePathPrefix ?? null)
30521
30604
  };
30522
30605
  }
30523
- function AppointmentServicesNode({ sectionHeading, services, pathname, className }) {
30606
+ function AppointmentServicesNode({ sectionHeading, services, pathname, servicePathPrefix, className }) {
30524
30607
  const view = buildAppointmentServicesView({
30525
30608
  services,
30526
- pathname: typeof pathname === "string" ? pathname : void 0
30609
+ pathname: typeof pathname === "string" ? pathname : void 0,
30610
+ servicePathPrefix: typeof servicePathPrefix === "string" ? servicePathPrefix : null
30527
30611
  });
30528
30612
  const rootClassName = `appointment-services ${className ?? ""}`.trim();
30529
30613
  const RowTitle = sectionHeading ? "h3" : "h2";
@@ -30554,7 +30638,10 @@ function AppointmentServicesNode({ sectionHeading, services, pathname, className
30554
30638
  children: [
30555
30639
  /* @__PURE__ */ jsx(RowTitle, {
30556
30640
  className: "appointment-services-row-title rb-text-lg rb-font-medium rb-leading-tight",
30557
- children: row.title
30641
+ children: row.detailsHref ? /* @__PURE__ */ jsx("a", {
30642
+ href: row.detailsHref,
30643
+ children: row.title
30644
+ }) : row.title
30558
30645
  }),
30559
30646
  row.description ? /* @__PURE__ */ jsx("p", {
30560
30647
  className: "appointment-services-row-description rb-text-sm rb-leading-6 status-muted",
@@ -33045,12 +33132,12 @@ const EventCompactRow = ({ event, buttonVariant = "primary", buttonText }) => {
33045
33132
  }), cta.hidden ? /* @__PURE__ */ jsx("span", {
33046
33133
  className: "event-compact-row-status",
33047
33134
  children: cta.label
33048
- }) : /* @__PURE__ */ jsxs("a", {
33135
+ }) : /* @__PURE__ */ jsx("a", {
33049
33136
  href: cta.href,
33050
33137
  target: cta.external ? "_blank" : void 0,
33051
33138
  rel: cta.external ? "noopener noreferrer" : void 0,
33052
33139
  className: `${buttonClass} event-compact-row-cta`,
33053
- children: [/* @__PURE__ */ jsx("span", { children: cta.label }), /* @__PURE__ */ jsx(ArrowRightIcon, { "aria-hidden": "true" })]
33140
+ children: /* @__PURE__ */ jsx("span", { children: cta.label })
33054
33141
  })]
33055
33142
  })]
33056
33143
  })]
@@ -34132,6 +34219,7 @@ const eventCollectionFilterSourceSchemaShape = {
34132
34219
  eventSurface: eventSurfaceScopeSchema$1.nullable().optional(),
34133
34220
  filterEventPresets: z.union([z.string(), z.array(z.string())]).nullable().optional(),
34134
34221
  scheduleScope: eventScheduleScopeSchema$1.nullable().optional(),
34222
+ seriesIds: z.union([z.string(), z.array(z.string())]).nullable().optional(),
34135
34223
  filterCategoryIds: z.union([z.string(), z.array(z.string())]).nullable().optional(),
34136
34224
  filterStaffMemberId: z.string().nullable().optional(),
34137
34225
  filterVenueId: z.string().nullable().optional()
@@ -34178,6 +34266,7 @@ const eventCollectionDisplaySchemaShape = {
34178
34266
  eventSurface: eventSurfaceScopeSchema$1,
34179
34267
  filterEventPresets: z.string().nullable(),
34180
34268
  scheduleScope: eventScheduleScopeSchema$1,
34269
+ seriesIds: z.string().nullable().optional(),
34181
34270
  filterCategoryIds: z.string().nullable(),
34182
34271
  filterStaffMemberId: z.string().nullable(),
34183
34272
  filterVenueId: z.string().nullable(),
@@ -34225,11 +34314,15 @@ function buildEventCollectionDisplay(source) {
34225
34314
  };
34226
34315
  }
34227
34316
  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,
34317
+ const selected = source.seriesIds != null;
34318
+ const matchingFilters = selected ? {
34319
+ eventSurface: "all",
34320
+ filterEventPresets: null,
34321
+ scheduleScope: "any",
34322
+ filterCategoryIds: null,
34323
+ filterStaffMemberId: null,
34324
+ filterVenueId: null
34325
+ } : {
34233
34326
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
34234
34327
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
34235
34328
  scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
@@ -34237,6 +34330,14 @@ function buildEventCollectionFilterDisplay(source) {
34237
34330
  filterStaffMemberId: normalizeEventStaffMemberId(source.filterStaffMemberId),
34238
34331
  filterVenueId: normalizeEventVenueId(source.filterVenueId)
34239
34332
  };
34333
+ return {
34334
+ seriesIds: eventSeriesScopeCsv(source.seriesIds),
34335
+ showFilters: selected ? false : source.showFilters ?? true,
34336
+ showCategoryFilter: source.showCategoryFilter ?? "auto",
34337
+ showVenueFilter: source.showVenueFilter ?? false,
34338
+ showStaffFilter: source.showStaffFilter ?? true,
34339
+ ...matchingFilters
34340
+ };
34240
34341
  }
34241
34342
  function buildEventCollectionPresentationDisplay(source) {
34242
34343
  return {
@@ -34251,14 +34352,15 @@ function buildEventCollectionPresentationDisplay(source) {
34251
34352
  }
34252
34353
  function buildEventCollectionHydration(source, now = /* @__PURE__ */ new Date()) {
34253
34354
  const timeZone = source.calendarWindow?.scope.timeZone ?? toBookingTimeZone(source.bookingTimeZone);
34355
+ const selected = source.seriesIds != null;
34254
34356
  return {
34255
34357
  calendarWindow: source.calendarWindow ?? void 0,
34256
34358
  siteId: source.siteId,
34257
34359
  stage: source.stage,
34258
34360
  prefetchedEvents: source.events != null,
34259
- eventSurface: normalizeEventSurfaceScope(source.eventSurface),
34260
- filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
34261
- scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
34361
+ eventSurface: selected ? "all" : normalizeEventSurfaceScope(source.eventSurface),
34362
+ filterEventPresets: selected ? null : normalizeEventSurfacePresetsCsv(source.filterEventPresets),
34363
+ scheduleScope: selected ? "any" : normalizeEventScheduleScope(source.scheduleScope),
34262
34364
  calendarContext: {
34263
34365
  timeZone,
34264
34366
  initialToday: source.calendarWindow?.initialToday ?? getLocalScheduleDate(now, timeZone)
@@ -34828,13 +34930,10 @@ function OfferingsPreviewContent(props) {
34828
34930
  }),
34829
34931
  groupCta ? /* @__PURE__ */ jsx("footer", {
34830
34932
  className: "offerings-preview-card-footer rb-mt-auto rb-flex",
34831
- children: /* @__PURE__ */ jsxs("a", {
34933
+ children: /* @__PURE__ */ jsx("a", {
34832
34934
  className: groupLinkClass,
34833
34935
  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
- })]
34936
+ children: /* @__PURE__ */ jsx("span", { children: groupCta.label })
34838
34937
  })
34839
34938
  }) : null
34840
34939
  ]
@@ -37781,6 +37880,65 @@ const formBlockDefinition = {
37781
37880
  runtime: formBlockRuntime
37782
37881
  };
37783
37882
  //#endregion
37883
+ //#region ../blocks/src/system/blocks/events/eventSelectionFields.ts
37884
+ const eventSelectionFields = [{
37885
+ id: "eventSelection",
37886
+ type: "select",
37887
+ label: "Events to show",
37888
+ required: false,
37889
+ multiple: false,
37890
+ defaultValue: "matching",
37891
+ options: [{
37892
+ value: "matching",
37893
+ label: "All matching events"
37894
+ }, {
37895
+ value: "selected",
37896
+ label: "Selected events"
37897
+ }],
37898
+ ui: { intent: "behavior" }
37899
+ }, createSiteOptionSelectField({
37900
+ id: "selectedEventSeriesIds",
37901
+ label: "Selected events",
37902
+ description: "Show upcoming dates for these events. Other event filters do not apply.",
37903
+ required: false,
37904
+ multiple: true,
37905
+ defaultValue: [],
37906
+ optionSource: "eventSeries",
37907
+ ui: {
37908
+ intent: "behavior",
37909
+ searchThreshold: 1,
37910
+ visibleWhen: {
37911
+ field: "eventSelection",
37912
+ equals: "selected"
37913
+ }
37914
+ }
37915
+ })];
37916
+ function withEventSelectionVisibility(field) {
37917
+ if (![
37918
+ "eventSurface",
37919
+ "filterEventPresets",
37920
+ "scheduleScope",
37921
+ "filterCategoryIds",
37922
+ "filterStaffMemberId",
37923
+ "filterVenueId",
37924
+ "showFilters",
37925
+ "showCategoryFilter",
37926
+ "showVenueFilter",
37927
+ "showStaffFilter"
37928
+ ].includes(field.id)) return field;
37929
+ const visibleWhen = field.ui?.visibleWhen;
37930
+ return {
37931
+ ...field,
37932
+ ui: {
37933
+ ...field.ui,
37934
+ visibleWhen: { allOf: [{
37935
+ field: "eventSelection",
37936
+ notEquals: "selected"
37937
+ }, ...visibleWhen ? [visibleWhen] : []] }
37938
+ }
37939
+ };
37940
+ }
37941
+ //#endregion
37784
37942
  //#region ../blocks/src/system/runtime/nodes/events/EventCalendar.interactive.ts
37785
37943
  const eventCalendarSourceSchema = z.object({
37786
37944
  ...eventCollectionSourceSchemaShape,
@@ -37878,19 +38036,59 @@ function buildEventCalendarInteractiveIslandProps(props) {
37878
38036
  //#region ../blocks/src/system/blocks/events/eventCalendarDataLoaders.ts
37879
38037
  const scopeParams = {
37880
38038
  siteId: { $bind: { from: "$root.siteId" } },
38039
+ seriesIds: { $bind: {
38040
+ from: "content",
38041
+ transforms: [{
38042
+ id: "events.calendarSelectionParam",
38043
+ options: { key: "seriesIds" }
38044
+ }]
38045
+ } },
37881
38046
  stage: { $bind: {
37882
38047
  from: "$root.previewStage",
37883
38048
  fallback: "published"
37884
38049
  } },
37885
- eventSurface: { $bind: { from: "content.eventSurface" } },
38050
+ eventSurface: { $bind: {
38051
+ from: "content",
38052
+ transforms: [{
38053
+ id: "events.calendarSelectionParam",
38054
+ options: { key: "eventSurface" }
38055
+ }, { id: "value.csv" }]
38056
+ } },
37886
38057
  eventPresets: { $bind: {
37887
- from: "content.filterEventPresets",
37888
- transforms: [{ id: "value.csv" }]
38058
+ from: "content",
38059
+ transforms: [{
38060
+ id: "events.calendarSelectionParam",
38061
+ options: { key: "filterEventPresets" }
38062
+ }, { id: "value.csv" }]
38063
+ } },
38064
+ scheduleScope: { $bind: {
38065
+ from: "content",
38066
+ transforms: [{
38067
+ id: "events.calendarSelectionParam",
38068
+ options: { key: "scheduleScope" }
38069
+ }, { id: "value.csv" }]
37889
38070
  } },
37890
- scheduleScope: { $bind: { from: "content.scheduleScope" } },
37891
- categoryIds: { $bind: { from: "content.filterCategoryIds" } },
37892
- staffMemberId: { $bind: { from: "content.filterStaffMemberId" } },
37893
- venueId: { $bind: { from: "content.filterVenueId" } }
38071
+ categoryIds: { $bind: {
38072
+ from: "content",
38073
+ transforms: [{
38074
+ id: "events.calendarSelectionParam",
38075
+ options: { key: "filterCategoryIds" }
38076
+ }, { id: "value.csv" }]
38077
+ } },
38078
+ staffMemberId: { $bind: {
38079
+ from: "content",
38080
+ transforms: [{
38081
+ id: "events.calendarSelectionParam",
38082
+ options: { key: "filterStaffMemberId" }
38083
+ }, { id: "value.csv" }]
38084
+ } },
38085
+ venueId: { $bind: {
38086
+ from: "content",
38087
+ transforms: [{
38088
+ id: "events.calendarSelectionParam",
38089
+ options: { key: "filterVenueId" }
38090
+ }, { id: "value.csv" }]
38091
+ } }
37894
38092
  };
37895
38093
  /** Combined renders two jobs; calendar-only variants never pay for the list seed. */
37896
38094
  const eventCalendarDataLoaders = {
@@ -38020,6 +38218,13 @@ const eventCalendarLayout = [styledSection({
38020
38218
  from: "content.defaultCalendarView",
38021
38219
  fallback: "month"
38022
38220
  } },
38221
+ seriesIds: { $bind: {
38222
+ from: "content",
38223
+ transforms: [{
38224
+ id: "events.calendarSelectionParam",
38225
+ options: { key: "seriesIds" }
38226
+ }]
38227
+ } },
38023
38228
  showFilters: { $bind: {
38024
38229
  from: "content.showFilters",
38025
38230
  fallback: true
@@ -38041,17 +38246,47 @@ const eventCalendarLayout = [styledSection({
38041
38246
  fallback: true
38042
38247
  } },
38043
38248
  eventSurface: { $bind: {
38044
- from: "content.eventSurface",
38045
- fallback: "all"
38249
+ from: "content",
38250
+ transforms: [{
38251
+ id: "events.calendarSelectionParam",
38252
+ options: { key: "eventSurface" }
38253
+ }]
38254
+ } },
38255
+ filterEventPresets: { $bind: {
38256
+ from: "content",
38257
+ transforms: [{
38258
+ id: "events.calendarSelectionParam",
38259
+ options: { key: "filterEventPresets" }
38260
+ }]
38046
38261
  } },
38047
- filterEventPresets: { $bind: { from: "content.filterEventPresets" } },
38048
38262
  scheduleScope: { $bind: {
38049
- from: "content.scheduleScope",
38050
- fallback: "any"
38263
+ from: "content",
38264
+ transforms: [{
38265
+ id: "events.calendarSelectionParam",
38266
+ options: { key: "scheduleScope" }
38267
+ }]
38268
+ } },
38269
+ filterCategoryIds: { $bind: {
38270
+ from: "content",
38271
+ transforms: [{
38272
+ id: "events.calendarSelectionParam",
38273
+ options: { key: "filterCategoryIds" }
38274
+ }]
38275
+ } },
38276
+ filterStaffMemberId: { $bind: {
38277
+ from: "content",
38278
+ transforms: [{
38279
+ id: "events.calendarSelectionParam",
38280
+ options: { key: "filterStaffMemberId" }
38281
+ }]
38282
+ } },
38283
+ filterVenueId: { $bind: {
38284
+ from: "content",
38285
+ transforms: [{
38286
+ id: "events.calendarSelectionParam",
38287
+ options: { key: "filterVenueId" }
38288
+ }]
38051
38289
  } },
38052
- filterCategoryIds: { $bind: { from: "content.filterCategoryIds" } },
38053
- filterStaffMemberId: { $bind: { from: "content.filterStaffMemberId" } },
38054
- filterVenueId: { $bind: { from: "content.filterVenueId" } },
38055
38290
  cardRole: { $bind: {
38056
38291
  from: "content._cardStyles.role",
38057
38292
  fallback: "default"
@@ -38555,6 +38790,7 @@ const eventCalendarAdditionalFields = [
38555
38790
  }
38556
38791
  }
38557
38792
  },
38793
+ ...eventSelectionFields,
38558
38794
  {
38559
38795
  id: "eventSurface",
38560
38796
  type: "select",
@@ -38764,7 +39000,7 @@ const eventCalendarBlockDefinition = {
38764
39000
  titleSource: "heading",
38765
39001
  description: "Display events with calendar, list, or combined views with filtering",
38766
39002
  skipSectionStyles: true,
38767
- additionalFields: eventCalendarAdditionalFields,
39003
+ additionalFields: eventCalendarAdditionalFields.map(withEventSelectionVisibility),
38768
39004
  styleTokens: {
38769
39005
  background: "surface",
38770
39006
  typography: "body",
@@ -38873,6 +39109,7 @@ const eventListingHydrationSchema = z.object({
38873
39109
  eventSurface: eventSurfaceScopeSchema,
38874
39110
  filterEventPresets: z.string().nullable(),
38875
39111
  scheduleScope: eventScheduleScopeSchema,
39112
+ seriesIds: z.string().nullable().optional(),
38876
39113
  filterCategoryIds: z.string().nullable().optional(),
38877
39114
  filterStaffMemberId: z.string().nullable().optional(),
38878
39115
  filterVenueId: z.string().nullable().optional(),
@@ -38933,6 +39170,7 @@ const eventListingBlockRuntime = defineProgressiveBlockRuntime({
38933
39170
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
38934
39171
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
38935
39172
  scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
39173
+ seriesIds: eventSeriesScopeCsv(source.seriesIds),
38936
39174
  filterCategoryIds: normalizeEventCategoryIdsCsv(source.filterCategoryIds),
38937
39175
  filterStaffMemberId: normalizeEventStaffMemberId(source.filterStaffMemberId),
38938
39176
  filterVenueId: normalizeEventVenueId(source.filterVenueId),
@@ -44301,9 +44539,56 @@ function generateNavLinkCSS(options) {
44301
44539
  if (baseRules.length > 0) cssChunks.unshift(`\n${selector} {\n ${baseRules.join("\n ")}\n}\n`);
44302
44540
  if (hoverRules.length > 0) cssChunks.push(`\n${hoverSelector} {\n ${hoverRules.join("\n ")}\n}\n`);
44303
44541
  if (activeRules.length > 0) cssChunks.push(`\n${activeSelector} {\n ${activeRules.join("\n ")}\n}\n`);
44542
+ cssChunks.push(navLinkFocusVisibleCss(selector, style, theme, tokens));
44304
44543
  cssChunks.push(generateInteractiveChildInheritanceCSS(selector));
44305
44544
  return cssChunks.join("");
44306
44545
  }
44546
+ /**
44547
+ * Keyboard focus indicator for nav links, in the header, the footer and header
44548
+ * dropdown menus — the three surfaces `generateNavLinkCSS` serves.
44549
+ *
44550
+ * Emitted unconditionally rather than as an effect, because focus visibility is
44551
+ * a floor rather than a style choice. Only the interaction-palette pill ever
44552
+ * wrote an `effects.focus` group, so every other nav — every `quiet` palette,
44553
+ * every footer, every dropdown — previously fell back to whatever outline the
44554
+ * browser drew.
44555
+ *
44556
+ * The outline takes the link's own resolved colour, so on a header that adapts
44557
+ * to the section it floats over it moves with the adaptation instead of
44558
+ * asserting a brand colour against an unknown backdrop.
44559
+ *
44560
+ * Where the style asks for `soft-glow`, the same halo goes on the outline. That
44561
+ * treatment exists because the theme has already conceded the resolved colour
44562
+ * alone is not reliable over an unpredictable backdrop — a hero photograph with
44563
+ * mixed luminance. Without it the text would stay legible by its halo while the
44564
+ * focus ring, which is the part with a contrast requirement of its own, would
44565
+ * not.
44566
+ *
44567
+ * A style that also paints a focus pill keeps it: this adds an outline around
44568
+ * that pill rather than replacing it.
44569
+ */
44570
+ function navLinkFocusVisibleCss(selector, style, theme, tokens) {
44571
+ const color = tokens.getColor(style.colorToken);
44572
+ const halo = buildTextContrastShadow({
44573
+ theme,
44574
+ colorToken: style.colorToken,
44575
+ treatment: style.contrastTreatment
44576
+ });
44577
+ return `
44578
+ ${selector}:focus-visible {
44579
+ outline: 2px solid ${color};
44580
+ outline-offset: ${focusOutlineOffset(selector)};${halo ? `\n box-shadow: ${halo};` : ""}
44581
+ }
44582
+ `;
44583
+ }
44584
+ /**
44585
+ * Dropdown items are full-width inside a panel with no horizontal padding, so
44586
+ * an outset outline would paint over and past the panel's own border. Keeping
44587
+ * it flush is the only way it reads as belonging to the item.
44588
+ */
44589
+ function focusOutlineOffset(selector) {
44590
+ return selector.includes("nav-dropdown-link") ? "-2px" : "3px";
44591
+ }
44307
44592
  function buildTextContrastShadow(input) {
44308
44593
  if (input.treatment !== "soft-glow") return null;
44309
44594
  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 +47721,7 @@ function pageBlockPosition(index, blockCount) {
47436
47721
  //#endregion
47437
47722
  //#region ../blocks/src/pageRendererContracts.ts
47438
47723
  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 ?? {};
47724
+ 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
47725
  return {
47441
47726
  siteId,
47442
47727
  pageId,
@@ -47446,6 +47731,7 @@ function buildPageRendererRootRuntime(dataContext) {
47446
47731
  apiBaseUrl,
47447
47732
  portalToken,
47448
47733
  bookingOfferingPresets,
47734
+ appointmentServicePathPrefix,
47449
47735
  mailingList,
47450
47736
  eventRegistrationOverlayEnabled,
47451
47737
  loaderFailures
@@ -52162,7 +52448,8 @@ const appointmentServicesBlockDefinition = {
52162
52448
  el("appointment-services", {
52163
52449
  sectionHeading: { $bind: { from: "content.heading" } },
52164
52450
  services: { $bind: { from: "data.services" } },
52165
- pathname: { $bind: { from: "$root.pathname" } }
52451
+ pathname: { $bind: { from: "$root.pathname" } },
52452
+ servicePathPrefix: { $bind: { from: "$root.appointmentServicePathPrefix" } }
52166
52453
  })
52167
52454
  ], { gap: "lg" })
52168
52455
  })]
@@ -58790,10 +59077,19 @@ const containerResponsiveThemeCss = `/*
58790
59077
  padding-top: 2.5rem;
58791
59078
  padding-bottom: 2.5rem;
58792
59079
  }
59080
+ /* Clearance for a surface that is not an authored block page.
59081
+ An authored page gets this through its opening block's container (see the
59082
+ overlay-clearance rule in styles.css). A reserved surface or a system
59083
+ destination page has no such block, so it consumes the same variable here.
59084
+ Marked with one class rather than per-surface selectors, because the last
59085
+ two additions to this family were both missed and shipped overlapping. */
58793
59086
  :where(.theme-scope)
58794
59087
  [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);
59088
+ .rb-overlay-clearance-surface {
59089
+ padding-top: calc(
59090
+ var(--site-overlay-header-clearance, 5.5rem) +
59091
+ var(--site-overlay-opening-content-extra-pad, clamp(1rem, 2vw, 1.75rem))
59092
+ );
58797
59093
  }
58798
59094
  .rb-preview-editor-offset {
58799
59095
  padding-bottom: calc(
@@ -69150,6 +69446,23 @@ const containerResponsiveThemeCss = `/*
69150
69446
  gap: 0.5rem;
69151
69447
  }
69152
69448
 
69449
+ /* Error panel and field notes. The form block scopes these to its own
69450
+ data-block, so the newsletter form (which also renders in the footer)
69451
+ needs its own copy of the treatment. */
69452
+ :where(.theme-scope) .newsletter-form .fb-error:not([data-field-validation-message]) {
69453
+ background-color: var(--fb-error-bg);
69454
+ color: var(--fb-error-text);
69455
+ border: 1px solid var(--fb-error-border);
69456
+ border-radius: var(--rb-block-radius);
69457
+ padding: 1rem;
69458
+ font-size: 0.875rem;
69459
+ }
69460
+
69461
+ :where(.theme-scope) .newsletter-form [data-field-validation-message] {
69462
+ font-size: 0.875rem;
69463
+ color: var(--fb-error-text);
69464
+ }
69465
+
69153
69466
  /* Required indicator */
69154
69467
  :where(.theme-scope) .newsletter-required {
69155
69468
  color: rgb(var(--tb-primary));
@@ -71483,6 +71796,15 @@ z.object({
71483
71796
  }).optional(),
71484
71797
  overrides: z.record(z.string(), z.string()).optional()
71485
71798
  }).passthrough();
71799
+ z.object({
71800
+ palette: paletteColorsSchema,
71801
+ paletteMode: paletteModeSchema,
71802
+ typography: z.object({
71803
+ headingFamily: z.string().trim().min(1),
71804
+ bodyFamily: z.string().trim().min(1)
71805
+ }).strict(),
71806
+ corners
71807
+ }).strict();
71486
71808
  //#endregion
71487
71809
  //#region src/rendering/blocks-theme-runtime.ts
71488
71810
  function parseBlocksTheme(theme) {
@@ -72322,12 +72644,12 @@ function normalizeContent(block) {
72322
72644
  }
72323
72645
  /**
72324
72646
  * Normalize params for API calls.
72325
- * Converts all values to strings.
72647
+ * Converts supplied values to strings, preserving absent versus explicitly empty params.
72326
72648
  */
72327
72649
  function normalizeParams(input) {
72328
72650
  if (!input || typeof input !== "object") return {};
72329
72651
  const output = {};
72330
- for (const [key, value] of Object.entries(input)) output[key] = coerceToString(value);
72652
+ for (const [key, value] of Object.entries(input)) if (value !== void 0 && value !== null) output[key] = coerceToString(value);
72331
72653
  return output;
72332
72654
  }
72333
72655
  function isNonArrayObject(value) {
@@ -72391,6 +72713,14 @@ function coercePayload(payload) {
72391
72713
  * - Frontend: provide internal API client
72392
72714
  * - SDK: provide SDK client with endpoint mapping
72393
72715
  */
72716
+ function projectDataLoaders(loaders, shouldLoad, blockId, blockKind) {
72717
+ if (!shouldLoad) return [...loaders];
72718
+ return loaders.filter(({ key }) => shouldLoad({
72719
+ blockId,
72720
+ blockKind,
72721
+ loaderKey: key
72722
+ }));
72723
+ }
72394
72724
  function shouldSkipResolvedLoader(input) {
72395
72725
  if (input.loader.requiredParams?.some((param) => !input.params[param]?.trim())) return true;
72396
72726
  return Boolean(input.isValidEndpoint && !input.isValidEndpoint(input.endpoint));
@@ -72419,7 +72749,7 @@ async function prefetchBlockData$1(page, context, options) {
72419
72749
  return (await prefetchBlockDataWithFailures(page, context, options)).resolvedData;
72420
72750
  }
72421
72751
  async function prefetchBlockDataWithFailures(page, context, options) {
72422
- const { apiClient, isValidEndpoint, onError, getCustomBlockLoaders, getBlockDefinition: getBlockDefinition$1 = getBlockDefinition, resolveParamsBindings: resolveParamsBindings$1 = resolveParamsBindings } = options;
72752
+ const { apiClient, isValidEndpoint, onError, getCustomBlockLoaders, getBlockDefinition: getBlockDefinition$1 = getBlockDefinition, resolveParamsBindings: resolveParamsBindings$1 = resolveParamsBindings, shouldLoad } = options;
72423
72753
  const results = {};
72424
72754
  const loaderFailures = {};
72425
72755
  const tasks = [];
@@ -72436,6 +72766,7 @@ async function prefetchBlockDataWithFailures(page, context, options) {
72436
72766
  loader
72437
72767
  })) : [];
72438
72768
  } else loaders = [];
72769
+ loaders = projectDataLoaders(loaders, shouldLoad, blockId, block.kind);
72439
72770
  if (loaders.length === 0) continue;
72440
72771
  const root = {
72441
72772
  ...context.root ?? {},
@@ -72514,8 +72845,8 @@ function toStringParams(value) {
72514
72845
  for (const [k, v] of Object.entries(value)) if (typeof v === "string") out[k] = v;
72515
72846
  return out;
72516
72847
  }
72517
- function stripEmptyStringValues(params) {
72518
- for (const [key, value] of Object.entries(params)) if (value === "") delete params[key];
72848
+ function stripEmptyStringValues(params, endpoint) {
72849
+ for (const [key, value] of Object.entries(params)) if (value === "" && !(key === "seriesIds" && (endpoint === "listPublicEvents" || endpoint === "listPublicEventCalendarWindow"))) delete params[key];
72519
72850
  }
72520
72851
  function ensureSiteId(params, context) {
72521
72852
  if (!params.siteId && typeof context.siteId === "string" && context.siteId) params.siteId = context.siteId;
@@ -72528,7 +72859,7 @@ function ensureSiteId(params, context) {
72528
72859
  */
72529
72860
  function normalizeLoaderParams(endpoint, params, context) {
72530
72861
  const out = toStringParams(params);
72531
- stripEmptyStringValues(out);
72862
+ stripEmptyStringValues(out, endpoint);
72532
72863
  if (endpoint === "getPublicFormById") {
72533
72864
  ensureSiteId(out, context);
72534
72865
  if (!out.formId && out.formSlug) {