@riverbankcms/sdk 0.135.1 → 0.136.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 (152) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +4 -8
  3. package/dist/{PreviewEditorSidebar-D4p_5TdY.mjs → PreviewEditorSidebar-BU5ZlhQR.mjs} +4 -4
  4. package/dist/{PreviewEditorUI-D0cRoTX1.mjs → PreviewEditorUI-9XkHL-bC.mjs} +3 -3
  5. package/dist/{PreviewQueryProvider-DcgaESkm.mjs → PreviewQueryProvider-wbEpXryy.mjs} +1 -1
  6. package/dist/{SdkPreviewModeRuntime-BKLtM3Nz.mjs → SdkPreviewModeRuntime-CnWmj-2B.mjs} +50 -99
  7. package/dist/{SiteChromeCustomizeContext-61X2m7QV.mjs → SiteChromeCustomizeContext-B6d5zkog.mjs} +2299 -1149
  8. package/dist/_dts/ai/src/contracts/proposals.d.ts +8 -554
  9. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +401 -2312
  10. package/dist/_dts/api/src/contentRuntime.d.ts +6 -0
  11. package/dist/_dts/api/src/endpoints.d.ts +7 -0
  12. package/dist/_dts/api/src/index.d.ts +1 -0
  13. package/dist/_dts/api/src/publicEventEndpointDefinitions.d.ts +7 -0
  14. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +26 -0
  15. package/dist/_dts/api/src/sdkBlockContentAdmission.d.ts +5 -0
  16. package/dist/_dts/api/src/sdkThemeWire.d.ts +6 -432
  17. package/dist/_dts/block-form/src/blockDraftSaveability.d.ts +2 -3
  18. package/dist/_dts/block-form/src/utils/slug.d.ts +6 -0
  19. package/dist/_dts/blocks/src/PageRenderer.d.ts +12 -6
  20. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +48 -0
  21. package/dist/_dts/blocks/src/definitions.d.ts +14 -0
  22. package/dist/_dts/blocks/src/index.d.ts +4 -3
  23. package/dist/_dts/blocks/src/pageRenderAdmission.d.ts +40 -0
  24. package/dist/_dts/blocks/src/server.d.ts +2 -2
  25. package/dist/_dts/blocks/src/system/data/index.d.ts +2 -0
  26. package/dist/_dts/blocks/src/system/data/publicLoaderCachePolicy.d.ts +78 -0
  27. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +2 -18
  28. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +103 -16
  29. package/dist/_dts/blocks/src/system/node/validation-constants.d.ts +1 -1
  30. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +2 -1
  31. package/dist/_dts/blocks/src/system/runtime/api/endpoints.d.ts +8 -0
  32. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  33. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +19 -0
  34. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +52 -0
  35. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +24 -0
  36. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +44 -18
  37. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.state.d.ts +20 -3
  38. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/VerifyingTimeoutStep.d.ts +3 -1
  39. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +1 -0
  40. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.client.d.ts +2 -2
  41. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.interactive.d.ts +40 -0
  42. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +2 -0
  43. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.interactive.d.ts +43 -0
  44. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarDayCell.d.ts +3 -1
  45. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarNavigationChevron.d.ts +4 -0
  46. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/DayEventsModal.d.ts +2 -1
  47. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventModalContext.d.ts +3 -2
  48. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +3 -0
  49. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +31 -3
  50. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekTimetableView.d.ts +3 -1
  51. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +9 -1
  52. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/timetableModel.d.ts +4 -3
  53. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +3 -3
  54. package/dist/_dts/blocks/src/system/transforms/registry/layout.d.ts +0 -1
  55. package/dist/_dts/calendar/src/grid.d.ts +24 -66
  56. package/dist/_dts/calendar/src/index.d.ts +2 -2
  57. package/dist/_dts/calendar/src/timetable.d.ts +5 -9
  58. package/dist/_dts/commerce/src/purchase/allocation.d.ts +11 -0
  59. package/dist/_dts/commerce/src/purchase/index.d.ts +2 -0
  60. package/dist/_dts/commerce/src/purchase/settlement.d.ts +33 -0
  61. package/dist/_dts/commerce/src/purchase/stripeCheckoutReturnUrl.d.ts +8 -0
  62. package/dist/_dts/content-model/src/templates/defaults.d.ts +6 -0
  63. package/dist/_dts/core/src/slug.d.ts +20 -5
  64. package/dist/_dts/preview-next/src/client/blocks/EditablePageRenderer.d.ts +1 -1
  65. package/dist/_dts/preview-next/src/client/preview/headerInteractionPaletteSurfaceState.d.ts +2 -1
  66. package/dist/_dts/sdk/src/client/publicLoaderCacheAccess.d.ts +6 -0
  67. package/dist/_dts/sdk/src/public-api/contracts.d.ts +5 -1
  68. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +10 -6
  69. package/dist/_dts/sdk/src/rendering/components/Block.shared.d.ts +0 -1
  70. package/dist/_dts/sdk/src/rendering/components/pageShared.d.ts +4 -4
  71. package/dist/_dts/sdk/src/rendering/helpers/footerPageOutline.d.ts +3 -0
  72. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +3 -0
  73. package/dist/_dts/sdk/src/rendering/helpers/loadPage.d.ts +1 -1
  74. package/dist/_dts/sdk/src/rendering/helpers/pageRuntimeSupport.d.ts +17 -0
  75. package/dist/_dts/sdk/src/rendering/helpers/templateContent.d.ts +9 -3
  76. package/dist/_dts/sdk/src/rendering/hooks/usePage.d.ts +1 -1
  77. package/dist/_dts/sdk/src/rendering/index.d.ts +2 -2
  78. package/dist/_dts/sdk/src/rendering/overrideResolution.d.ts +7 -4
  79. package/dist/_dts/sdk/src/version.d.ts +1 -1
  80. package/dist/_dts/theme-core/src/cards/types.d.ts +8 -8
  81. package/dist/_dts/theme-core/src/data.d.ts +12 -0
  82. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  83. package/dist/_dts/theme-core/src/header/legacyHeaderVariant.d.ts +2 -6
  84. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +6 -807
  85. package/dist/_dts/theme-core/src/schema.d.ts +452 -1278
  86. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +213 -1
  87. package/dist/_dts/theme-core/src/site-styles/headerLayoutCatalog.d.ts +0 -29
  88. package/dist/_dts/theme-core/src/site-styles/headerLayoutStyles.d.ts +10 -0
  89. package/dist/_dts/theme-core/src/site-styles/headerLayoutTypes.d.ts +8 -5
  90. package/dist/_dts/theme-core/src/site-styles/headerOpeningForeground.d.ts +4 -4
  91. package/dist/_dts/theme-core/src/site-styles/headerStyleCompiler.d.ts +16 -2
  92. package/dist/_dts/theme-core/src/site-styles/headerStyleDefaults.d.ts +465 -0
  93. package/dist/_dts/theme-core/src/site-styles/headerStyleRecipe.d.ts +161 -0
  94. package/dist/client/{BookingConsentAgreement-Bz9znbpn.mjs → BookingConsentAgreement-DCch21a6.mjs} +2 -2
  95. package/dist/client/{DiscountCodeDisclosure-35Ru8yB9.mjs → DiscountCodeDisclosure-DIj1T59K.mjs} +1 -1
  96. package/dist/client/{EventCombined.client-BKB0ZxRR.mjs → EventCombined.client-BWVNlXTx.mjs} +226 -206
  97. package/dist/client/{EventListing.client-Dx8I21ok.mjs → EventListing.client-DVFCDe3i.mjs} +2 -2
  98. package/dist/client/{LeafletStaticMap-CEWx-Bhe.mjs → LeafletStaticMap-BY5mIHw9.mjs} +1 -1
  99. package/dist/client/{accordion-KIDsKTwS.mjs → accordion-CK29E8rG.mjs} +1 -1
  100. package/dist/client/{bookingForm-D96Rmp-5.mjs → bookingForm-DFvnet-N.mjs} +5 -5
  101. package/dist/client/bookings.mjs +86 -47
  102. package/dist/client/{carousel-YwUgyL66.mjs → carousel-BVU5NQlb.mjs} +1 -1
  103. package/dist/client/{cart-BbwuNrUT.mjs → cart-BNItcZVa.mjs} +3 -3
  104. package/dist/client/{checkout-hp1ucmEY.mjs → checkout-Bu6iCdkP.mjs} +3 -3
  105. package/dist/client/client.mjs +1950 -1031
  106. package/dist/client/{context-DljQn8L5.mjs → context-BImarOAq.mjs} +12 -0
  107. package/dist/client/{eventCalendar-DU4iNLpo.mjs → eventCalendar-DS2h-UXD.mjs} +256 -114
  108. package/dist/client/{eventCombined-CZuupW8m.mjs → eventCombined-Drt4Oq9p.mjs} +3 -3
  109. package/dist/client/{eventDetails-DPQWwCIi.mjs → eventDetails-Bl2vWgTX.mjs} +4 -3
  110. package/dist/client/{eventListing-C7oMidwS.mjs → eventListing-CS7PHY7v.mjs} +3 -3
  111. package/dist/client/{eventRegistration-zEiunlUs.mjs → eventRegistration-D-j8eNy2.mjs} +85 -41
  112. package/dist/client/{form-D2KIyCVJ.mjs → form-RMpoLTyr.mjs} +2 -2
  113. package/dist/client/{headerSection-CK5uChUK.mjs → headerSection-BYE3Lb3k.mjs} +2 -2
  114. package/dist/client/hooks.mjs +2225 -276
  115. package/dist/client/{locationMap-DLQNH1ln.mjs → locationMap-BxSr7Kkh.mjs} +2 -2
  116. package/dist/client/{newsletterForm-ZuhIG7fZ.mjs → newsletterForm-DerbXwFC.mjs} +2 -2
  117. package/dist/client/{productDetail-7hIcGeae.mjs → productDetail-4SZCLhAf.mjs} +3 -3
  118. package/dist/client/{productList-T4Cii1ot.mjs → productList-D7AP4GNq.mjs} +3 -3
  119. package/dist/client/{propsDecoder-mB0aC_Fi.mjs → propsDecoder-pzBeCh14.mjs} +94 -24
  120. package/dist/client/rendering/client.mjs +758 -464
  121. package/dist/client/rendering/islands.mjs +17 -17
  122. package/dist/client/rendering.mjs +2126 -1244
  123. package/dist/client/{scrollToBlock-Du5mHgvL.mjs → scrollToBlock-CduAKf8T.mjs} +1 -1
  124. package/dist/client/{shop-B5zAcKZn.mjs → shop-Cp5p_kGe.mjs} +6 -6
  125. package/dist/client/{shop-commerce-BKBZB6l5.mjs → shop-commerce-DicXYaYX.mjs} +5 -5
  126. package/dist/client/{shop-commerce-store-Cq4qlNtQ.mjs → shop-commerce-store-CGWnpY4K.mjs} +1 -1
  127. package/dist/client/{shopShared-DYGzEs9D.mjs → shopShared-BDf9ljkm.mjs} +3 -3
  128. package/dist/client/{useEventFilters-Bphkda8m.mjs → useEventFilters-DeeyGYuk.mjs} +3 -3
  129. package/dist/preview-next/before-render.mjs +7 -0
  130. package/dist/preview-next/client/runtime.mjs +4 -4
  131. package/dist/preview-next/keepAlive.mjs +7 -0
  132. package/dist/preview-next/middleware.mjs +7 -0
  133. package/dist/{previewAuthStore-C7ksxt5b.mjs → previewAuthStore-DbqBol0V.mjs} +7 -0
  134. package/dist/{sdk-runtime-BbIC8G4J.mjs → sdk-runtime-19MO27F0.mjs} +2 -2
  135. package/dist/{separator-CadhAtI7.mjs → separator-DaPLtJ1f.mjs} +1 -1
  136. package/dist/server/components.mjs +1527 -893
  137. package/dist/server/config-validation.mjs +65 -113
  138. package/dist/server/config.mjs +65 -113
  139. package/dist/server/data.mjs +65 -112
  140. package/dist/server/index.mjs +122 -4
  141. package/dist/server/manifest.mjs +0 -1
  142. package/dist/server/next.mjs +1432 -708
  143. package/dist/server/page-converter.mjs +58 -91
  144. package/dist/server/prebuild.mjs +1 -1
  145. package/dist/server/rendering/server.mjs +1682 -915
  146. package/dist/server/rendering.mjs +1694 -1020
  147. package/dist/server/routing.mjs +3230 -1843
  148. package/dist/server/server.mjs +21710 -19779
  149. package/dist/server/theme-bridge.mjs +656 -264
  150. package/dist/server/theme.mjs +2 -17
  151. package/dist/styles/index.css +609 -181
  152. package/package.json +1 -1
@@ -8479,12 +8479,8 @@ const legacyHeaderVariantValues = Object.keys(legacyHeaderVariants);
8479
8479
  * `@riverbankcms/api` is `z.input<typeof themeSchema>`, so a widened input here
8480
8480
  * would leak straight into a public type.
8481
8481
  *
8482
- * Not every read of a persisted theme goes through the schema:
8483
- * `resolveRuntimeTheme` in
8484
- * `apps/dashboard/src/server/services/sites/runtimeSiteResponse.ts` returns the
8485
- * raw database JSON asserted as `Theme` with no parse, so a theme still
8486
- * carrying a legacy spelling reaches the theme editor un-aliased. That gap
8487
- * predates this alias and is harmless while persisted usage is zero.
8482
+ * Persisted runtime themes are admitted through `parseThemeForPersistence`
8483
+ * before reaching consumers, so legacy spellings normalize at that boundary.
8488
8484
  */
8489
8485
  const headerVariantWithLegacyAliasSchema = z$1.enum([...headerVariantValues, ...legacyHeaderVariantValues]).transform((value) => value in legacyHeaderVariants ? legacyHeaderVariants[value] : value);
8490
8486
  const BREAKPOINT_LG = 1024;
@@ -9075,7 +9071,6 @@ const navFontWeight = z$1.enum([
9075
9071
  "bold"
9076
9072
  ]);
9077
9073
  const headerMaxWidth = z$1.enum(["container", "full"]);
9078
- const headerBarFrame = z$1.enum(["full-bleed", "inset-shell"]);
9079
9074
  const headerBorderSchema = componentBorderSchema.extend({ position: z$1.enum([
9080
9075
  "bottom",
9081
9076
  "top",
@@ -9104,12 +9099,6 @@ const headerLogoTextSchema = z$1.object({
9104
9099
  ]).optional(),
9105
9100
  hideOnShrink: z$1.boolean().optional()
9106
9101
  }).optional();
9107
- const navEffectsSchema = z$1.object({
9108
- underlineGradient: z$1.boolean().default(false),
9109
- glow: z$1.boolean().default(false),
9110
- glowColor: z$1.string().optional(),
9111
- neumorphic: z$1.boolean().default(false)
9112
- }).optional();
9113
9102
  const dropdownStyleSchema = z$1.object({
9114
9103
  background: themeColorTokenSchema.default("surface"),
9115
9104
  textColor: themeColorTokenSchema.default("text"),
@@ -9201,9 +9190,7 @@ const headerSchema = z$1.object({
9201
9190
  positioning: headerPositioning,
9202
9191
  shrinkOnScroll: z$1.boolean(),
9203
9192
  maxWidth: headerMaxWidth,
9204
- barFrame: headerBarFrame.optional(),
9205
9193
  scrollTreatment: headerScrollTreatmentSchema,
9206
- logoOverride: mediaSchema.nullable().optional(),
9207
9194
  background: headerBackgroundSchema.default({
9208
9195
  type: "color",
9209
9196
  color: "surface"
@@ -9215,13 +9202,11 @@ const headerSchema = z$1.object({
9215
9202
  navFontRole: z$1.enum(["body", "heading"]).optional(),
9216
9203
  navTextSize: navLinkStyleSchema.shape.textSize,
9217
9204
  interactionPalette: headerInteractionPaletteSchema.optional(),
9218
- navPlacement: z$1.enum(["end", "center"]).optional(),
9219
9205
  container: headerContainerSchema,
9220
9206
  border: headerBorderSchema,
9221
9207
  shadow: componentShadowSchema.optional(),
9222
9208
  logo: logoStyleSchema,
9223
9209
  logoText: headerLogoTextSchema,
9224
- navEffects: navEffectsSchema,
9225
9210
  navContainer: navContainerSchema,
9226
9211
  navLinkStyle: navLinkStyleSchema.optional(),
9227
9212
  ctaGap: headerCtaGapSchema.optional(),
@@ -11234,7 +11219,6 @@ const ALLOWED_TRANSFORM_IDS = [
11234
11219
  "layout.footerRootClass",
11235
11220
  "layout.headerRootStyle",
11236
11221
  "layout.headerSectionBackground",
11237
- "layout.classicNavMode",
11238
11222
  "layout.footerRootStyle",
11239
11223
  "ui.headerNavLinkClass",
11240
11224
  "ui.headingClassFromAlignment",
@@ -13722,6 +13706,15 @@ function normalizeStoredContentValues(plans, content, options = {}) {
13722
13706
  normalizeInlineTextFallbacks(plans, normalized, ctx);
13723
13707
  return normalized;
13724
13708
  }
13709
+ /**
13710
+ * Produces the portable representation accepted by content persistence.
13711
+ * Runtime-hydrated media fields are stripped only when a canonical identifier
13712
+ * is already present; unresolved environment-specific references remain
13713
+ * invalid and must be handled by the site-scoped media write normalizer first.
13714
+ */
13715
+ function normalizeManifestContentForWrite(plans, content, options = {}) {
13716
+ return canonicalizeObjectForWrite(plans, normalizeStoredContentValues(plans, content, options));
13717
+ }
13725
13718
  function normalizeStoredFieldValueWithContext(plan, value, ctx) {
13726
13719
  if (value === null && !plan.required) return;
13727
13720
  switch (plan.kind) {
@@ -13756,10 +13749,6 @@ function normalizeStoredRepeaterItems(plan, value, ctx) {
13756
13749
  return normalizeStoredObjectChildren(fields, itemRecord, ctx);
13757
13750
  });
13758
13751
  }
13759
- function validateFieldValue(plan, value, options = {}) {
13760
- const ctx = strictValidationContext(options);
13761
- return validateNormalizedFieldValue(plan, normalizeFieldValue(plan, value, ctx), ctx);
13762
- }
13763
13752
  function normalizeFieldValue(plan, value, ctx) {
13764
13753
  switch (plan.kind) {
13765
13754
  case "number": return normalizeNumberInput(value, ctx.allowNull);
@@ -14059,6 +14048,14 @@ function fieldPlanToObjectPropertyZod(plan, options = {}) {
14059
14048
  default: return assertNever$15(presence);
14060
14049
  }
14061
14050
  }
14051
+ /** Validate stored field values without applying editor/write normalization. */
14052
+ function fieldPlanToStoredObjectPropertyZod(plan) {
14053
+ const ctx = strictValidationContext({ allowNull: false });
14054
+ const schema = z$1.any().superRefine((value, zodCtx) => {
14055
+ for (const validationIssue of validateNormalizedFieldValue(plan, value, ctx)) addFieldIssueToZodContext(zodCtx, validationIssue, plan.path);
14056
+ });
14057
+ return plan.required ? schema : schema.optional();
14058
+ }
14062
14059
  function addFieldIssueToZodContext(zodCtx, validationIssue, rootPath) {
14063
14060
  const message = fieldIssueToMessage(validationIssue);
14064
14061
  const path = relativeZodIssuePath(validationIssue.path, rootPath);
@@ -14164,36 +14161,6 @@ function findNestedFieldValidationPlan(plan, path) {
14164
14161
  function sameFieldPath(left, right) {
14165
14162
  return left.length === right.length && left.every((segment, index) => segment === right[index]);
14166
14163
  }
14167
- function validateExplicitNullsForPush(plans, content, ctx) {
14168
- if (!isPlainObject$5(content)) return [];
14169
- return plans.flatMap((plan) => validateExplicitNullForPushPlan(plan, content[plan.fieldId], ctx));
14170
- }
14171
- function validateExplicitNullForPushPlan(plan, value, ctx) {
14172
- if (value === null) return !plan.required && !ctx.allowNull && !acceptsOptionalNullValue(plan) ? validateFieldValue(plan, value, { allowNull: false }) : [];
14173
- switch (plan.kind) {
14174
- case "group": return validateExplicitNullsForPush(selectedGroupChildPlans(plan, value), value, childValidationContext(plan, ctx));
14175
- case "repeater": return validateExplicitNullsForPushRepeater(plan, value, ctx);
14176
- case "string":
14177
- case "number":
14178
- case "boolean":
14179
- case "richText":
14180
- case "inlineText":
14181
- case "media":
14182
- case "link":
14183
- case "select":
14184
- case "passthrough": return [];
14185
- default: return assertNever$15(plan);
14186
- }
14187
- }
14188
- function validateExplicitNullsForPushRepeater(plan, value, ctx) {
14189
- if (!isUnknownArray$2(value)) return [];
14190
- return value.flatMap((item, index) => {
14191
- if (!isPlainObject$5(item)) return [];
14192
- const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
14193
- if (!childPlans) return [];
14194
- return childPlans.flatMap((childPlan) => validateExplicitNullForPushPlan(materializeRepeaterItemPlan(childPlan, plan.path, index), item[childPlan.fieldId], ctx));
14195
- });
14196
- }
14197
14164
  function compilePattern(pattern) {
14198
14165
  try {
14199
14166
  return new RegExp(pattern);
@@ -14301,6 +14268,7 @@ function registerManifest(manifest) {
14301
14268
  }
14302
14269
  //#endregion
14303
14270
  //#region ../blocks/src/system/manifest/validation.ts
14271
+ const compiledManifestContentContract = Symbol("compiledManifestContentContract");
14304
14272
  /**
14305
14273
  * Preprocess content by converting empty values to undefined.
14306
14274
  * Used in draft mode to allow required fields to be temporarily empty.
@@ -14311,13 +14279,94 @@ function preprocessEmptyValues(content) {
14311
14279
  for (const [key, value] of Object.entries(content)) result[key] = isEmptyValue$1(value) ? void 0 : value;
14312
14280
  return result;
14313
14281
  }
14314
- function createManifestValidator(manifestOrFields, options = {}) {
14315
- return createManifestValidatorWithNormalizer(manifestOrFields, options, normalizeStoredContentValues);
14282
+ /**
14283
+ * Compiles editor-facing manifest structure into the presentation-independent
14284
+ * validation contract used by every manifest-content trust transition.
14285
+ */
14286
+ function compileManifestContentContract(manifestOrFields) {
14287
+ const plans = derivePlansFromManifestOrFields(manifestOrFields);
14288
+ const strictOptions = { allowNull: false };
14289
+ const strictStoredSchema = deriveStrictStoredZodFromPlans(plans);
14290
+ return Object.freeze({ [compiledManifestContentContract]: Object.freeze({
14291
+ plans,
14292
+ strictStoredSchema,
14293
+ writeSchema: deriveNormalizedContentZodFromPlans(plans, strictOptions, normalizeManifestContentForWrite).transform((value) => canonicalizeObjectForWrite(plans, value)),
14294
+ draftSchema: deriveNormalizedContentZodFromPlans(plans, {
14295
+ allowNull: true,
14296
+ allowIncomplete: true,
14297
+ mode: "draft"
14298
+ }, normalizeStoredContentValues),
14299
+ renderSchema: strictStoredSchema,
14300
+ findNonPortableMediaPaths: (content) => findNonPortableMediaPaths(plans, content)
14301
+ }) });
14316
14302
  }
14317
- function createManifestValidatorWithNormalizer(manifestOrFields, options, normalize) {
14318
- return createManifestValidatorFromPlans(derivePlansFromManifestOrFields(manifestOrFields), options, normalize);
14303
+ /** Strictly admits already-persisted content without repairing it. */
14304
+ function admitStoredManifestContent(contract, content) {
14305
+ const state = contract[compiledManifestContentContract];
14306
+ const nonPortablePaths = state.findNonPortableMediaPaths(content);
14307
+ if (nonPortablePaths.length > 0) return {
14308
+ ok: false,
14309
+ error: {
14310
+ kind: "non_portable_media",
14311
+ paths: nonPortablePaths
14312
+ }
14313
+ };
14314
+ const parsed = state.strictStoredSchema.safeParse(content);
14315
+ return parsed.success ? {
14316
+ ok: true,
14317
+ value: content
14318
+ } : {
14319
+ ok: false,
14320
+ error: zodAdmissionError(parsed.error)
14321
+ };
14322
+ }
14323
+ /** Validates raw editor state without granting a stored-content proof. */
14324
+ function validateManifestDraft(contract, data) {
14325
+ const parsed = contract[compiledManifestContentContract].draftSchema.safeParse(data);
14326
+ if (parsed.success) return {
14327
+ kind: "previewable",
14328
+ data,
14329
+ issues: []
14330
+ };
14331
+ const [firstIssue, ...remainingIssues] = zodAdmissionIssues(parsed.error);
14332
+ if (!firstIssue) throw new Error("Draft manifest validation failed without an issue");
14333
+ return {
14334
+ kind: "invalid",
14335
+ data,
14336
+ issues: [firstIssue, ...remainingIssues]
14337
+ };
14319
14338
  }
14320
- function createManifestValidatorFromPlans(plans, options, normalize) {
14339
+ /**
14340
+ * Applies a pure runtime projection to admitted stored content and validates
14341
+ * the resulting render shape before granting the renderable proof.
14342
+ */
14343
+ function deriveRenderableManifestContent(contract, stored, project) {
14344
+ const projected = project(stored);
14345
+ const parsed = contract[compiledManifestContentContract].renderSchema.safeParse(projected);
14346
+ return parsed.success ? {
14347
+ ok: true,
14348
+ value: projected
14349
+ } : {
14350
+ ok: false,
14351
+ error: zodAdmissionError(parsed.error)
14352
+ };
14353
+ }
14354
+ /**
14355
+ * Re-admits renderable content after a transport boundary has erased its
14356
+ * compile-time proof. Unlike write admission, this never canonicalizes or
14357
+ * grants a stored proof, so hydrated media cannot flow back to persistence.
14358
+ */
14359
+ function admitTransportedRenderableManifestContent(contract, content) {
14360
+ const parsed = contract[compiledManifestContentContract].renderSchema.safeParse(content);
14361
+ return parsed.success ? {
14362
+ ok: true,
14363
+ value: content
14364
+ } : {
14365
+ ok: false,
14366
+ error: zodAdmissionError(parsed.error)
14367
+ };
14368
+ }
14369
+ function deriveNormalizedContentZodFromPlans(plans, options, normalize) {
14321
14370
  const isDraft = options.mode === "draft";
14322
14371
  const baseSchema = deriveContentZodFromPlans(plans, options);
14323
14372
  if (isDraft) return z$1.preprocess((content) => preprocessEmptyValues(normalize(plans, content, options)), baseSchema);
@@ -14328,9 +14377,81 @@ function deriveContentZodFromPlans(plans, options) {
14328
14377
  for (const plan of plans) shape[plan.fieldId] = fieldPlanToObjectPropertyZod(plan, options);
14329
14378
  return z$1.object(shape).catchall(z$1.unknown()).passthrough();
14330
14379
  }
14380
+ function deriveStrictStoredZodFromPlans(plans) {
14381
+ const shape = {};
14382
+ for (const plan of plans) shape[plan.fieldId] = fieldPlanToStoredObjectPropertyZod(plan);
14383
+ return z$1.object(shape).catchall(z$1.unknown()).passthrough();
14384
+ }
14331
14385
  function derivePlansFromManifestOrFields(manifestOrFields) {
14332
14386
  return deriveFieldValidationPlans(Array.isArray(manifestOrFields) ? manifestOrFields : manifestOrFields.fields ?? []);
14333
14387
  }
14388
+ function zodAdmissionError(error) {
14389
+ return {
14390
+ kind: "invalid_shape",
14391
+ issues: zodAdmissionIssues(error)
14392
+ };
14393
+ }
14394
+ function zodAdmissionIssues(error) {
14395
+ return error.issues.map((issue) => ({
14396
+ code: issue.code,
14397
+ path: issue.path.map((segment) => typeof segment === "number" ? segment : String(segment)),
14398
+ message: issue.message
14399
+ }));
14400
+ }
14401
+ function findNonPortableMediaPaths(plans, content) {
14402
+ const paths = /* @__PURE__ */ new Set();
14403
+ collectNonPortablePlannedMedia(plans, content, [], paths);
14404
+ return [...paths].sort();
14405
+ }
14406
+ function collectNonPortablePlannedMedia(plans, content, parentPath, paths) {
14407
+ if (!isPlainRecord$1(content)) return;
14408
+ for (const plan of plans) {
14409
+ if (!(plan.fieldId in content)) continue;
14410
+ const value = content[plan.fieldId];
14411
+ const path = [...parentPath, plan.fieldId];
14412
+ switch (plan.kind) {
14413
+ case "media":
14414
+ if (isPlainRecord$1(value) && hasEnvironmentSpecificMediaField(value)) paths.add(formatFieldPath(path));
14415
+ break;
14416
+ case "richText":
14417
+ collectNonPortableRichTextImages(value, path, paths);
14418
+ break;
14419
+ case "group":
14420
+ collectNonPortablePlannedMedia(selectedGroupChildPlans(plan, value), value, path, paths);
14421
+ break;
14422
+ case "repeater":
14423
+ collectNonPortableRepeaterMedia(plan, value, path, paths);
14424
+ break;
14425
+ case "string":
14426
+ case "number":
14427
+ case "boolean":
14428
+ case "inlineText":
14429
+ case "link":
14430
+ case "select":
14431
+ case "passthrough": break;
14432
+ }
14433
+ }
14434
+ }
14435
+ function collectNonPortableRepeaterMedia(plan, value, path, paths) {
14436
+ if (!Array.isArray(value)) return;
14437
+ value.forEach((item, index) => {
14438
+ if (!isPlainRecord$1(item)) return;
14439
+ const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
14440
+ if (childPlans) collectNonPortablePlannedMedia(childPlans, item, [...path, index], paths);
14441
+ });
14442
+ }
14443
+ function collectNonPortableRichTextImages(value, path, paths) {
14444
+ const images = [];
14445
+ walkAndCollectImageAttrs(value, images);
14446
+ const prefix = formatFieldPath(path);
14447
+ for (const reference of images) if (hasEnvironmentSpecificMediaField(reference.attrs)) paths.add(reference.location ? `${prefix}.${reference.location}` : prefix);
14448
+ }
14449
+ function isPlainRecord$1(value) {
14450
+ return typeof value === "object" && value !== null && !Array.isArray(value);
14451
+ }
14452
+ function hasEnvironmentSpecificMediaField(value) {
14453
+ return "assetId" in value || "storageBucket" in value || "storagePath" in value || "src" in value || "mimeType" in value || looksLikeUuid(value.id);
14454
+ }
14334
14455
  //#endregion
14335
14456
  //#region ../blocks/src/system/manifest/variantKey.ts
14336
14457
  /**
@@ -17538,27 +17659,6 @@ function asHeaderStyleId(value) {
17538
17659
  return value;
17539
17660
  }
17540
17661
  //#endregion
17541
- //#region ../theme-core/src/site-styles/headerInteractionPalette.ts
17542
- const clearSplitInteractionPalette = defineLookOptionGroup({
17543
- id: "interaction-palette",
17544
- label: "Hover colour",
17545
- defaultOptionId: "accent",
17546
- options: [
17547
- {
17548
- id: "accent",
17549
- label: "Accent"
17550
- },
17551
- {
17552
- id: "primary",
17553
- label: "Primary"
17554
- },
17555
- {
17556
- id: "secondary",
17557
- label: "Secondary"
17558
- }
17559
- ]
17560
- });
17561
- //#endregion
17562
17662
  //#region ../theme-core/src/site-styles/headerLayoutCatalog.ts
17563
17663
  const titleOnlyLogoAccommodation = {
17564
17664
  pattern: "title-only",
@@ -17718,43 +17818,37 @@ const headerStyleCatalog = defineExactCatalog()([
17718
17818
  id: asHeaderStyleId("minimal"),
17719
17819
  label: "Minimal",
17720
17820
  description: "Quiet surface with simple color-change navigation.",
17721
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17722
- navTypography: "plain"
17821
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17723
17822
  },
17724
17823
  {
17725
17824
  id: asHeaderStyleId("fine-line"),
17726
17825
  label: "Seamless",
17727
17826
  description: "Borderless surface with underline hover for a quieter page transition.",
17728
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17729
- navTypography: "plain"
17827
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17730
17828
  },
17731
17829
  {
17732
17830
  id: asHeaderStyleId("soft-bar"),
17733
17831
  label: "Soft Bar",
17734
17832
  description: "Soft tinted surface with gentle pill hover states.",
17735
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17736
- navTypography: "plain"
17833
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17737
17834
  },
17738
17835
  {
17739
17836
  id: asHeaderStyleId("pill-nav"),
17740
17837
  label: "Pill Nav",
17741
17838
  description: "A calm pill navigation rail over a neutral header.",
17742
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17743
- navTypography: "plain"
17839
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17744
17840
  },
17745
17841
  {
17746
17842
  id: asHeaderStyleId("brand-bar"),
17747
17843
  label: "Brand Bar",
17748
17844
  description: "Solid brand surface with high-contrast navigation.",
17749
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17750
- navTypography: "brand-caps"
17845
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17751
17846
  },
17752
17847
  {
17753
17848
  id: asHeaderStyleId("brand-underline"),
17754
17849
  label: "Brand Underline",
17755
17850
  description: "Brand surface with underline-grow navigation.",
17756
- supportedLayoutIds: supportedLayouts(["classic"]),
17757
- navTypography: "brand-caps"
17851
+ supportedLayoutIds: supportedLayouts(["classic"])
17758
17852
  },
17759
17853
  {
17760
17854
  id: asHeaderStyleId("brand-pill"),
@@ -17764,73 +17858,78 @@ const headerStyleCatalog = defineExactCatalog()([
17764
17858
  "classic",
17765
17859
  "centered",
17766
17860
  "floating"
17767
- ]),
17768
- navTypography: "brand-caps"
17861
+ ])
17769
17862
  },
17770
17863
  {
17771
17864
  id: asHeaderStyleId("brand-capsule"),
17772
17865
  label: "Brand Capsule",
17773
17866
  description: "Solid brand chrome designed as a floating capsule, with quiet-caps nav, a soft pill active state, and an inverted CTA pill.",
17774
- supportedLayoutIds: supportedLayouts(["classic"]),
17775
- navTypography: "quiet-caps"
17867
+ supportedLayoutIds: supportedLayouts(["classic"])
17776
17868
  },
17777
17869
  {
17778
17870
  id: asHeaderStyleId("serif-line"),
17779
17871
  label: "Serif Line",
17780
17872
  description: "Typographic logo treatment with understated underline navigation.",
17781
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17782
- navTypography: "heading"
17873
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17783
17874
  },
17784
17875
  {
17785
17876
  id: asHeaderStyleId("flat-tabs"),
17786
17877
  label: "Flat Tabs",
17787
17878
  description: "Flat block hover and active states with a Swiss utility feel.",
17788
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17789
- navTypography: "quiet-caps"
17879
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17790
17880
  },
17791
17881
  {
17792
17882
  id: asHeaderStyleId("glass"),
17793
17883
  label: "Glass",
17794
17884
  description: "Translucent chrome with frosted navigation backing.",
17795
- supportedLayoutIds: supportedLayouts(["floating"]),
17796
- navTypography: "plain"
17885
+ supportedLayoutIds: supportedLayouts(["floating"])
17797
17886
  },
17798
17887
  {
17799
17888
  id: asHeaderStyleId("minimal-surface"),
17800
17889
  label: "Clear Split",
17801
17890
  description: "Floating logo with transparent navigation and no shared shell.",
17802
- supportedLayoutIds: supportedLayouts(["floating"]),
17803
- navTypography: "plain",
17804
- openingForegroundTarget: "all",
17805
- scrollTreatment: "contrast-glass",
17806
- interactionPalette: clearSplitInteractionPalette
17891
+ supportedLayoutIds: supportedLayouts(["floating"])
17807
17892
  },
17808
17893
  {
17809
17894
  id: asHeaderStyleId("soft-shell"),
17810
17895
  label: "Soft Shell",
17811
17896
  description: "Floating rounded shell with soft pill navigation states.",
17812
- supportedLayoutIds: supportedLayouts(["floating"]),
17813
- navTypography: "plain"
17897
+ supportedLayoutIds: supportedLayouts(["floating"])
17814
17898
  },
17815
17899
  {
17816
17900
  id: asHeaderStyleId("split-glass"),
17817
17901
  label: "Split Glass",
17818
17902
  description: "Logo floats independently while the navigation sits in a glass rail.",
17819
- supportedLayoutIds: supportedLayouts(["floating"]),
17820
- navTypography: "quiet-caps",
17821
- openingForegroundTarget: "brand-only"
17903
+ supportedLayoutIds: supportedLayouts(["floating"])
17822
17904
  },
17823
17905
  {
17824
17906
  id: asHeaderStyleId("split-pill"),
17825
17907
  label: "Split Pill",
17826
17908
  description: "Logo floats independently with a soft pill-backed navigation rail.",
17827
- supportedLayoutIds: supportedLayouts(["floating"]),
17828
- navTypography: "heading",
17829
- openingForegroundTarget: "brand-only"
17909
+ supportedLayoutIds: supportedLayouts(["floating"])
17830
17910
  }
17831
17911
  ]);
17832
17912
  new Map(headerLayoutCatalog.map((layout) => [layout.id, layout]));
17833
17913
  new Map(headerStyleCatalog.map((style) => [style.id, style]));
17914
+ defineLookOptionGroup({
17915
+ id: "interaction-palette",
17916
+ label: "Hover colour",
17917
+ defaultOptionId: "accent",
17918
+ options: [
17919
+ {
17920
+ id: "accent",
17921
+ label: "Accent"
17922
+ },
17923
+ {
17924
+ id: "primary",
17925
+ label: "Primary"
17926
+ },
17927
+ {
17928
+ id: "secondary",
17929
+ label: "Secondary"
17930
+ }
17931
+ ]
17932
+ });
17834
17933
  function asFooterLookId(value) {
17835
17934
  return value;
17836
17935
  }
@@ -28785,26 +28884,6 @@ const headerLogoTextVisibleTransform = {
28785
28884
  return resolveHeaderBrandDisplay(asPartialObject(value)) !== "logo-only";
28786
28885
  }
28787
28886
  };
28788
- /**
28789
- * Selects which classic-layout nav row should render.
28790
- *
28791
- * - `'inline'` → classic variant with the historical logo-left / nav-right row
28792
- * - `'centered'`→ classic variant with a centred (flex-1) nav and a right CTA
28793
- * - `'other'` → not a classic variant (centered/floating handle it)
28794
- *
28795
- * Consumed by the site-header block's `$when` gates so the two classic rows are
28796
- * mutually exclusive without introducing a new top-level header variant.
28797
- */
28798
- const classicNavModeTransform = {
28799
- id: "layout.classicNavMode",
28800
- kind: "formatter",
28801
- summary: "Resolve which classic-layout nav row to render",
28802
- run: (value) => {
28803
- const header = asPartialObject(value);
28804
- if (header?.variant !== "classic") return "other";
28805
- return header?.navPlacement === "center" ? "centered" : "inline";
28806
- }
28807
- };
28808
28887
  const maxWidthClassTransform = {
28809
28888
  id: "layout.maxWidthClass",
28810
28889
  kind: "string",
@@ -28910,7 +28989,6 @@ const headerRootClassTransform = {
28910
28989
  if (isHeaderScrollRuntimeEnabled(content)) classes.push(config.shrink);
28911
28990
  const variant = header?.variant;
28912
28991
  if (variant && headerVariantValues.includes(variant)) classes.push(`header-variant-${variant}`);
28913
- if (header?.barFrame === "inset-shell" && variant === "classic") classes.push("header-bar-frame-inset-shell");
28914
28992
  return joinClasses$2(classes);
28915
28993
  }
28916
28994
  };
@@ -29118,7 +29196,6 @@ const layoutTransforms = [
29118
29196
  headerLogoClassTransform,
29119
29197
  headerLogoMediaClassTransform,
29120
29198
  headerLogoTextVisibleTransform,
29121
- classicNavModeTransform,
29122
29199
  footerRootClassTransform,
29123
29200
  headerRootStyleTransform,
29124
29201
  headerSectionBackgroundTransform,
@@ -29521,18 +29598,7 @@ const classicLayout = inline({
29521
29598
  logoRow,
29522
29599
  createNavRow(`rb-ml-auto ${desktopOnlyDisplay("flex")} rb-gap-6`),
29523
29600
  headerCta
29524
- ], whenTransformed("$root.theme.header", "layout.classicNavMode", "inline"));
29525
- const classicCenteredNavLayout = inline({
29526
- className: bindProp("$root.theme.header.maxWidth", {
29527
- transforms: pipe(tx("layout.maxWidthClass", { base: `rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6` })),
29528
- fallback: `rb-container rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6`
29529
- }),
29530
- align: "center"
29531
- }, [
29532
- logoRow,
29533
- createNavRow(`rb-flex-1 rb-justify-center ${desktopOnlyDisplay("flex")} rb-gap-6`, "center"),
29534
- headerCta
29535
- ], whenTransformed("$root.theme.header", "layout.classicNavMode", "centered"));
29601
+ ], when("$root.theme.header.variant", { equals: "classic" }));
29536
29602
  const centeredLayout = stack({
29537
29603
  gap: "md",
29538
29604
  align: "center",
@@ -29597,7 +29663,6 @@ const siteHeaderManifest = createBlockManifest({
29597
29663
  }, [
29598
29664
  mobileHeaderBar,
29599
29665
  classicLayout,
29600
- classicCenteredNavLayout,
29601
29666
  centeredLayout,
29602
29667
  floatingLayout,
29603
29668
  mobileOverlay
@@ -33931,67 +33996,49 @@ const MONTH_NAMES = [
33931
33996
  "November",
33932
33997
  "December"
33933
33998
  ];
33934
- /** Format a date as YYYY-MM-DD using the runtime's local timezone. */
33935
33999
  function formatDateKey(date) {
33936
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
34000
+ return `${String(date.year).padStart(4, "0")}-${String(date.month).padStart(2, "0")}-${String(date.day).padStart(2, "0")}`;
33937
34001
  }
33938
- /**
33939
- * True when `date` and `now` are the same local calendar day.
33940
- * `now` defaults to `new Date()` so casual callers don't need to thread it,
33941
- * but tests and snapshot-rendering should pass an explicit value.
33942
- */
33943
- function isToday(date, now = /* @__PURE__ */ new Date()) {
33944
- return isSameLocalDay(date, now);
34002
+ function isToday(date, today) {
34003
+ return isSameCalendarDay(date, today);
33945
34004
  }
33946
- /** True when two dates fall on the same local calendar day. */
33947
- function isSameLocalDay(left, right) {
33948
- return left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate();
34005
+ function isSameCalendarDay(left, right) {
34006
+ return left.year === right.year && left.month === right.month && left.day === right.day;
34007
+ }
34008
+ function addDays(date, days) {
34009
+ const result = new Date(Date.UTC(date.year, date.month - 1, date.day + days, 12));
34010
+ return {
34011
+ year: result.getUTCFullYear(),
34012
+ month: result.getUTCMonth() + 1,
34013
+ day: result.getUTCDate()
34014
+ };
33949
34015
  }
33950
- /**
33951
- * Build the calendar days for a month view. Returns 42 dates (6 rows × 7
33952
- * days) including padding from prior/next months for a stable grid.
33953
- */
33954
34016
  function getMonthCalendarDays(year, month, startOfWeek) {
33955
- const firstDayOfMonth = new Date(year, month, 1);
33956
- const lastDayOfMonth = new Date(year, month + 1, 0);
33957
- let startOffset = firstDayOfMonth.getDay();
33958
- if (startOfWeek === "monday") startOffset = startOffset === 0 ? 6 : startOffset - 1;
33959
- const days = [];
33960
- for (let i = startOffset - 1; i >= 0; i--) days.push(new Date(year, month, -i));
33961
- for (let day = 1; day <= lastDayOfMonth.getDate(); day++) days.push(new Date(year, month, day));
33962
- while (days.length < 42) {
33963
- const nextDay = days.length - startOffset - lastDayOfMonth.getDate() + 1;
33964
- days.push(new Date(year, month + 1, nextDay));
33965
- }
33966
- return days;
34017
+ const firstDay = {
34018
+ year,
34019
+ month,
34020
+ day: 1
34021
+ };
34022
+ const dayOfWeek = getDayOfWeek(firstDay);
34023
+ const gridStart = addDays(firstDay, -(startOfWeek === "monday" ? dayOfWeek === 0 ? 6 : dayOfWeek - 1 : dayOfWeek));
34024
+ return Array.from({ length: 42 }, (_, index) => addDays(gridStart, index));
33967
34025
  }
33968
- /** Build the 7 dates of the week containing `baseDate`. */
33969
34026
  function getWeekCalendarDays(baseDate, startOfWeek) {
33970
- const days = [];
33971
- const dayOfWeek = baseDate.getDay();
33972
- const daysToStart = startOfWeek === "monday" ? dayOfWeek === 0 ? 6 : dayOfWeek - 1 : dayOfWeek;
33973
- const weekStart = new Date(baseDate);
33974
- weekStart.setDate(baseDate.getDate() - daysToStart);
33975
- for (let i = 0; i < 7; i++) {
33976
- const date = new Date(weekStart);
33977
- date.setDate(weekStart.getDate() + i);
33978
- days.push(date);
33979
- }
33980
- return days;
34027
+ const dayOfWeek = getDayOfWeek(baseDate);
34028
+ const weekStart = addDays(baseDate, -(startOfWeek === "monday" ? dayOfWeek === 0 ? 6 : dayOfWeek - 1 : dayOfWeek));
34029
+ return Array.from({ length: 7 }, (_, index) => addDays(weekStart, index));
34030
+ }
34031
+ function getDayOfWeek(date) {
34032
+ return new Date(Date.UTC(date.year, date.month - 1, date.day, 12)).getUTCDay();
33981
34033
  }
33982
- /** Build the 7 dates of the week containing `baseDate` as a fixed-width tuple. */
33983
34034
  function getWeekCalendarDayTuple(baseDate, startOfWeek) {
33984
34035
  const days = getWeekCalendarDays(baseDate, startOfWeek);
33985
34036
  assertWeekCalendarDays(days);
33986
34037
  return days;
33987
34038
  }
33988
34039
  function assertWeekCalendarDays(days) {
33989
- if (days.length !== 7) throw new Error("Expected week calendar helper to return exactly 7 days");
34040
+ if (days.length !== 7) throw new Error("Expected exactly 7 calendar days");
33990
34041
  }
33991
- /**
33992
- * Day-name labels reordered for the chosen week start. Pass a custom
33993
- * `dayNames` array (length 7, Sunday-first) to localize.
33994
- */
33995
34042
  function getOrderedDayNames(startOfWeek, dayNames = DAY_NAMES) {
33996
34043
  if (startOfWeek === "monday") {
33997
34044
  const sunday = dayNames[0] ?? "Sun";
@@ -33999,22 +34046,17 @@ function getOrderedDayNames(startOfWeek, dayNames = DAY_NAMES) {
33999
34046
  }
34000
34047
  return [...dayNames];
34001
34048
  }
34002
- /**
34003
- * Header title for a calendar view.
34004
- * - month: "January 2025"
34005
- * - week: "January 5 - 11, 2025" or "December 30 - January 5, 2025"
34006
- */
34007
34049
  function formatCalendarTitle(viewDate, view, days, monthNames = MONTH_NAMES) {
34008
34050
  if (view === "week") {
34009
34051
  const weekStart = days[0];
34010
34052
  const weekEnd = days[6];
34011
- if (weekStart === void 0 || weekEnd === void 0) return `${monthNames[viewDate.getMonth()] ?? ""} ${viewDate.getFullYear()}`;
34012
- const startMonth = monthNames[weekStart.getMonth()] ?? "";
34013
- const endMonth = monthNames[weekEnd.getMonth()] ?? "";
34014
- if (weekStart.getMonth() === weekEnd.getMonth()) return `${startMonth} ${weekStart.getDate()} - ${weekEnd.getDate()}, ${weekEnd.getFullYear()}`;
34015
- return `${startMonth} ${weekStart.getDate()} - ${endMonth} ${weekEnd.getDate()}, ${weekEnd.getFullYear()}`;
34053
+ if (weekStart === void 0 || weekEnd === void 0) return `${monthNames[viewDate.month - 1] ?? ""} ${viewDate.year}`;
34054
+ const startMonth = monthNames[weekStart.month - 1] ?? "";
34055
+ const endMonth = monthNames[weekEnd.month - 1] ?? "";
34056
+ if (weekStart.month === weekEnd.month) return `${startMonth} ${weekStart.day} - ${weekEnd.day}, ${weekEnd.year}`;
34057
+ return `${startMonth} ${weekStart.day} - ${endMonth} ${weekEnd.day}, ${weekEnd.year}`;
34016
34058
  }
34017
- return `${monthNames[viewDate.getMonth()] ?? ""} ${viewDate.getFullYear()}`;
34059
+ return `${monthNames[viewDate.month - 1] ?? ""} ${viewDate.year}`;
34018
34060
  }
34019
34061
  function buildCalendarViewport(input) {
34020
34062
  const monthNames = input.monthNames ?? MONTH_NAMES;
@@ -34024,10 +34066,10 @@ function buildCalendarViewport(input) {
34024
34066
  kind: "week",
34025
34067
  anchorDate: input.anchorDate,
34026
34068
  days,
34027
- title: formatCalendarTitle(input.anchorDate, "week", [...days], monthNames)
34069
+ title: formatCalendarTitle(input.anchorDate, "week", days, monthNames)
34028
34070
  };
34029
34071
  }
34030
- const days = getMonthCalendarDays(input.anchorDate.getFullYear(), input.anchorDate.getMonth(), input.startOfWeek);
34072
+ const days = getMonthCalendarDays(input.anchorDate.year, input.anchorDate.month, input.startOfWeek);
34031
34073
  return {
34032
34074
  kind: "month",
34033
34075
  anchorDate: input.anchorDate,
@@ -34175,13 +34217,12 @@ function buildTimetableModel$1(input) {
34175
34217
  const bounds = resolveBounds(input.bounds);
34176
34218
  const viewportDays = getTimetableDays(input.viewDate, input.view ?? "week", input.startOfWeek);
34177
34219
  const viewportKeys = new Set(viewportDays.map(formatDateKey));
34178
- const now = input.now ?? /* @__PURE__ */ new Date();
34179
34220
  const parsed = parseAllItems(input.items, input);
34180
34221
  const inViewport = deriveVisibleSegments(parsed.drafts, viewportKeys);
34181
34222
  const hourRange = resolveHourRange(inViewport, bounds);
34182
34223
  const laid = layoutCellsByDate(inViewport, hourRange, bounds);
34183
34224
  return {
34184
- days: assembleDays(viewportDays, laid.cellsByDate, now),
34225
+ days: assembleDays(viewportDays, laid.cellsByDate, input.today),
34185
34226
  hourTicks: buildHourTicks(hourRange.startHour, hourRange.endHour),
34186
34227
  startHour: hourRange.startHour,
34187
34228
  endHour: hourRange.endHour,
@@ -34238,13 +34279,13 @@ function layoutCellsByDate(segments, hourRange, bounds) {
34238
34279
  diagnostics
34239
34280
  };
34240
34281
  }
34241
- function assembleDays(viewportDays, cellsByDate, now) {
34282
+ function assembleDays(viewportDays, cellsByDate, today) {
34242
34283
  return viewportDays.map((date) => {
34243
34284
  const dateKey = formatDateKey(date);
34244
34285
  return {
34245
34286
  date,
34246
34287
  dateKey,
34247
- isToday: isSameLocalDay(date, now),
34288
+ isToday: isSameCalendarDay(date, today),
34248
34289
  cells: cellsByDate.get(dateKey) ?? []
34249
34290
  };
34250
34291
  });
@@ -34396,13 +34437,12 @@ function getTimetableDays(viewDate, view, startOfWeek) {
34396
34437
  function enumerateDateKeys(startDateKey, endDateKey) {
34397
34438
  if (startDateKey > endDateKey) throw new Error(`Invalid timetable date range: ${startDateKey} is after ${endDateKey}`);
34398
34439
  const start = parseDateKey(startDateKey);
34399
- const end = parseDateKey(endDateKey);
34440
+ parseDateKey(endDateKey);
34400
34441
  const keys = [];
34401
- const cursor = new Date(start.year, start.month - 1, start.day);
34402
- const endDate = new Date(end.year, end.month - 1, end.day);
34403
- while (cursor.getTime() <= endDate.getTime()) {
34442
+ let cursor = start;
34443
+ while (formatDateKey(cursor) <= endDateKey) {
34404
34444
  keys.push(formatDateKey(cursor));
34405
- cursor.setDate(cursor.getDate() + 1);
34445
+ cursor = addDays(cursor, 1);
34406
34446
  }
34407
34447
  return keys;
34408
34448
  }
@@ -34627,10 +34667,10 @@ function formatEventTeacherLine(event) {
34627
34667
  * timezone). Thin domain wrapper over `groupItemsByDate` from the calendar
34628
34668
  * package; preserves the historical `Map<string, PublicEvent[]>` shape.
34629
34669
  */
34630
- function groupEventsByDate(events) {
34670
+ function groupEventsByDate(events, calendarTimeZone) {
34631
34671
  const grouped = groupItemsByDate(events, {
34632
34672
  getStartsAt: (event) => event.startsAt,
34633
- getTimeZone: (event) => event.timeZone ?? "UTC"
34673
+ getTimeZone: () => calendarTimeZone
34634
34674
  });
34635
34675
  const map = /* @__PURE__ */ new Map();
34636
34676
  for (const [dateKey, bucket] of grouped) map.set(dateKey, [...bucket]);
@@ -36416,7 +36456,8 @@ const eventCollectionSourceSchemaShape = {
36416
36456
  buttonVariant: eventButtonVariantSchema.optional(),
36417
36457
  buttonText: z$1.string().optional(),
36418
36458
  emptyMessage: z$1.string().optional(),
36419
- className: z$1.string().optional()
36459
+ className: z$1.string().optional(),
36460
+ bookingTimeZone: z$1.string().optional()
36420
36461
  };
36421
36462
  const eventCollectionDisplaySchemaShape = {
36422
36463
  events: z$1.array(publicEventSchema),
@@ -36451,7 +36492,15 @@ const eventCollectionHydrationSchema = z$1.object({
36451
36492
  prefetchedEvents: z$1.boolean(),
36452
36493
  eventSurface: eventSurfaceScopeSchema$1,
36453
36494
  filterEventPresets: z$1.string().nullable(),
36454
- scheduleScope: eventScheduleScopeSchema$1
36495
+ scheduleScope: eventScheduleScopeSchema$1,
36496
+ calendarContext: z$1.object({
36497
+ timeZone: z$1.string().transform(toBookingTimeZone),
36498
+ initialToday: z$1.object({
36499
+ year: z$1.number().int(),
36500
+ month: z$1.number().int().min(1).max(12),
36501
+ day: z$1.number().int().min(1).max(31)
36502
+ })
36503
+ })
36455
36504
  });
36456
36505
  function buildEventCollectionDisplay(source) {
36457
36506
  return {
@@ -36496,14 +36545,19 @@ function buildEventCollectionPresentationDisplay(source) {
36496
36545
  emptyMessage: source.emptyMessage ?? "No upcoming events scheduled."
36497
36546
  };
36498
36547
  }
36499
- function buildEventCollectionHydration(source) {
36548
+ function buildEventCollectionHydration(source, now = /* @__PURE__ */ new Date()) {
36549
+ const timeZone = toBookingTimeZone(source.bookingTimeZone);
36500
36550
  return {
36501
36551
  siteId: source.siteId,
36502
36552
  stage: source.stage,
36503
36553
  prefetchedEvents: source.events != null,
36504
36554
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
36505
36555
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
36506
- scheduleScope: normalizeEventScheduleScope(source.scheduleScope)
36556
+ scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
36557
+ calendarContext: {
36558
+ timeZone,
36559
+ initialToday: getLocalScheduleDate(now, timeZone)
36560
+ }
36507
36561
  };
36508
36562
  }
36509
36563
  function buildEventCollectionListingDisplay(display, prefetchedEvents, className) {
@@ -47157,7 +47211,8 @@ function isOrdinaryEvent(event) {
47157
47211
  function deriveWholeSeriesPhase(sessions, nowIso) {
47158
47212
  const now = Date.parse(nowIso);
47159
47213
  if (now < Date.parse(sessions[0].startsAt)) return "upcoming";
47160
- return now <= Date.parse(sessions.at(-1)?.endsAt ?? sessions[0].endsAt) ? "inProgress" : "completed";
47214
+ const finalSession = sessions[sessions.length - 1] ?? sessions[0];
47215
+ return now <= Date.parse(finalSession.endsAt) ? "inProgress" : "completed";
47161
47216
  }
47162
47217
  function selectRelatedDates(input) {
47163
47218
  if (!input.seriesId) return [];
@@ -49256,9 +49311,9 @@ const FULL_DAY_NAMES$1 = [
49256
49311
  * @returns "Monday, January 13"
49257
49312
  */
49258
49313
  function formatAgendaDayHeader(date) {
49259
- const dayName = FULL_DAY_NAMES$1[date.getDay()];
49260
- const monthName = MONTH_NAMES[date.getMonth()];
49261
- return `${dayName ?? "Day"}, ${monthName ?? "Month"} ${date.getDate()}`;
49314
+ const dayName = FULL_DAY_NAMES$1[getDayOfWeek(date)];
49315
+ const monthName = MONTH_NAMES[date.month - 1];
49316
+ return `${dayName ?? "Day"}, ${monthName ?? "Month"} ${date.day}`;
49262
49317
  }
49263
49318
  /**
49264
49319
  * WeekAgendaView Component
@@ -49266,14 +49321,14 @@ function formatAgendaDayHeader(date) {
49266
49321
  * Displays a week of events in an agenda/list style with rich event details.
49267
49322
  * Shows all 7 days with date headers, empty days show "No events scheduled".
49268
49323
  */
49269
- const WeekAgendaView = ({ days, events, showCapacity, emptyMessage = "No events scheduled" }) => {
49270
- const eventsByDate = groupEventsByDate(events);
49324
+ const WeekAgendaView = ({ days, events, showCapacity, emptyMessage = "No events scheduled", calendarTimeZone, today: currentDate }) => {
49325
+ const eventsByDate = groupEventsByDate(events, calendarTimeZone);
49271
49326
  return /* @__PURE__ */ jsx("div", {
49272
49327
  className: "event-agenda-week",
49273
49328
  children: days.map((date) => {
49274
49329
  const dateKey = formatDateKey(date);
49275
49330
  const dayEvents = eventsByDate.get(dateKey) || [];
49276
- const today = isToday(date);
49331
+ const today = isToday(date, currentDate);
49277
49332
  return /* @__PURE__ */ jsxs("div", {
49278
49333
  className: `event-agenda-day ${today ? "event-agenda-day--today" : ""}`,
49279
49334
  children: [/* @__PURE__ */ jsx("div", {
@@ -49302,6 +49357,50 @@ const WeekAgendaView = ({ days, events, showCapacity, emptyMessage = "No events
49302
49357
  })
49303
49358
  });
49304
49359
  };
49360
+ //#endregion
49361
+ //#region ../blocks/src/system/runtime/nodes/events/shared/CalendarNavigationChevron.tsx
49362
+ function CalendarNavigationChevron({ direction }) {
49363
+ return /* @__PURE__ */ jsx("svg", {
49364
+ width: "20",
49365
+ height: "20",
49366
+ fill: "none",
49367
+ stroke: "currentColor",
49368
+ strokeWidth: 2,
49369
+ viewBox: "0 0 24 24",
49370
+ "aria-hidden": "true",
49371
+ children: /* @__PURE__ */ jsx("path", {
49372
+ strokeLinecap: "round",
49373
+ strokeLinejoin: "round",
49374
+ d: direction === "previous" ? "M15 19l-7-7 7-7" : "M9 5l7 7-7 7"
49375
+ })
49376
+ });
49377
+ }
49378
+ //#endregion
49379
+ //#region ../blocks/src/system/runtime/nodes/events/shared/WeekBoardView.tsx
49380
+ function buildWeekBoardNavigationState(viewport, today) {
49381
+ return {
49382
+ rangeLabel: viewport.title,
49383
+ isCurrentWeek: viewport.days.some((day) => isToday(day, today))
49384
+ };
49385
+ }
49386
+ function buildWeekBoardViewProps(display, days, navigation, events = display.events) {
49387
+ return {
49388
+ days,
49389
+ events: [...events],
49390
+ navigation,
49391
+ showCapacity: display.showCapacity,
49392
+ emptyMessage: display.emptyMessage,
49393
+ title: display.boardTitle,
49394
+ footerNote: display.footerNote,
49395
+ ctaLabel: display.loadMoreText ?? display.buttonText,
49396
+ ctaHref: display.seeAllUrl,
49397
+ buttonVariant: display.buttonVariant,
49398
+ elevated: display.elevated,
49399
+ banded: display.banded,
49400
+ calendarTimeZone: display.calendarTimeZone,
49401
+ today: display.today
49402
+ };
49403
+ }
49305
49404
  const SHORT_DAY_NAMES = [
49306
49405
  "Sun",
49307
49406
  "Mon",
@@ -49363,6 +49462,114 @@ const WeekBoardCard = ({ event, showCapacity }) => {
49363
49462
  ]
49364
49463
  });
49365
49464
  };
49465
+ const WeekBoardNavigationControls = ({ navigation, todayClassName }) => {
49466
+ const handlers = navigation.kind === "interactive" ? navigation : {
49467
+ onPrevious: void 0,
49468
+ onNext: void 0,
49469
+ onToday: void 0
49470
+ };
49471
+ const isLoading = navigation.kind === "interactive" && navigation.loadState.kind === "loading";
49472
+ return /* @__PURE__ */ jsxs("nav", {
49473
+ className: "event-week-board-navigation",
49474
+ "aria-label": "Choose week",
49475
+ children: [
49476
+ /* @__PURE__ */ jsx("button", {
49477
+ type: "button",
49478
+ className: "event-week-board-nav-button",
49479
+ "aria-label": "Previous week",
49480
+ onClick: handlers.onPrevious,
49481
+ children: /* @__PURE__ */ jsx(CalendarNavigationChevron, { direction: "previous" })
49482
+ }),
49483
+ /* @__PURE__ */ jsxs("h3", {
49484
+ className: "event-week-board-range",
49485
+ "aria-live": "polite",
49486
+ children: [navigation.rangeLabel, isLoading ? /* @__PURE__ */ jsx("span", {
49487
+ className: "event-week-board-loading",
49488
+ children: " Loading…"
49489
+ }) : null]
49490
+ }),
49491
+ /* @__PURE__ */ jsx("button", {
49492
+ type: "button",
49493
+ className: "event-week-board-nav-button",
49494
+ "aria-label": "Next week",
49495
+ onClick: handlers.onNext,
49496
+ children: /* @__PURE__ */ jsx(CalendarNavigationChevron, { direction: "next" })
49497
+ }),
49498
+ /* @__PURE__ */ jsx("button", {
49499
+ type: "button",
49500
+ className: todayClassName,
49501
+ disabled: navigation.isCurrentWeek,
49502
+ onClick: handlers.onToday,
49503
+ children: "Today"
49504
+ })
49505
+ ]
49506
+ });
49507
+ };
49508
+ const WeekBoardLegend = () => /* @__PURE__ */ jsx("div", {
49509
+ className: "event-week-board-legend",
49510
+ "aria-hidden": "true",
49511
+ children: LEGEND_ITEMS.map((item) => /* @__PURE__ */ jsxs("span", {
49512
+ className: "event-week-board-legend-item",
49513
+ children: [/* @__PURE__ */ jsx("i", {
49514
+ className: "event-week-board-legend-dot",
49515
+ "data-status": item.status
49516
+ }), item.label]
49517
+ }, item.status))
49518
+ });
49519
+ const WeekBoardLoadError = ({ navigation, buttonClassName }) => {
49520
+ if (navigation.kind !== "interactive" || navigation.loadState.kind !== "error") return null;
49521
+ return /* @__PURE__ */ jsxs("div", {
49522
+ className: "event-week-board-error",
49523
+ role: "status",
49524
+ children: [/* @__PURE__ */ jsx("p", { children: "Failed to load events" }), /* @__PURE__ */ jsx("button", {
49525
+ type: "button",
49526
+ className: buttonClassName,
49527
+ onClick: navigation.loadState.onRetry,
49528
+ children: "Try again"
49529
+ })]
49530
+ });
49531
+ };
49532
+ const WeekBoardGrid = ({ days, eventsByDate, showCapacity, today: currentDate }) => /* @__PURE__ */ jsx("div", {
49533
+ className: "event-week-board-grid",
49534
+ children: days.map((date) => {
49535
+ const dateKey = formatDateKey(date);
49536
+ const dayEvents = eventsByDate.get(dateKey) ?? [];
49537
+ const today = isToday(date, currentDate);
49538
+ return /* @__PURE__ */ jsxs("div", {
49539
+ className: clsx("event-week-board-day", dayEvents.length === 0 && "event-week-board-day--empty", today && "event-week-board-day--today"),
49540
+ children: [/* @__PURE__ */ jsxs("div", {
49541
+ className: "event-week-board-day-head",
49542
+ children: [/* @__PURE__ */ jsx("span", {
49543
+ className: "event-week-board-day-abbr",
49544
+ children: SHORT_DAY_NAMES[getDayOfWeek(date)]
49545
+ }), /* @__PURE__ */ jsx("span", {
49546
+ className: "event-week-board-day-num",
49547
+ children: date.day
49548
+ })]
49549
+ }), /* @__PURE__ */ jsx("div", {
49550
+ className: "event-week-board-day-body",
49551
+ children: dayEvents.map((event) => /* @__PURE__ */ jsx(WeekBoardCard, {
49552
+ event,
49553
+ showCapacity
49554
+ }, event.occurrenceId || event.id))
49555
+ })]
49556
+ }, dateKey);
49557
+ })
49558
+ });
49559
+ const WeekBoardFooter = ({ note, ctaHref, ctaLabel, ctaClassName }) => {
49560
+ if (note === null && ctaHref === null) return null;
49561
+ return /* @__PURE__ */ jsxs("div", {
49562
+ className: "event-week-board-foot",
49563
+ children: [note === null ? null : /* @__PURE__ */ jsx("p", {
49564
+ className: "event-week-board-foot-note",
49565
+ children: note
49566
+ }), ctaHref === null ? null : /* @__PURE__ */ jsx("a", {
49567
+ className: ctaClassName,
49568
+ href: ctaHref,
49569
+ children: ctaLabel
49570
+ })]
49571
+ });
49572
+ };
49366
49573
  /**
49367
49574
  * WeekBoardView
49368
49575
  *
@@ -49371,18 +49578,21 @@ const WeekBoardCard = ({ event, showCapacity }) => {
49371
49578
  * to a stacked list on mobile. Repurposes the week-grouped agenda data as a
49372
49579
  * board rather than an hour-positioned timetable grid.
49373
49580
  */
49374
- const WeekBoardView = ({ days, events, showCapacity, emptyMessage = "No classes scheduled this week", title, footerNote, ctaLabel = "Reserve a place", ctaHref = null, buttonVariant = "outline", elevated = false, banded = false }) => {
49375
- const eventsByDate = groupEventsByDate(events);
49581
+ const WeekBoardView = ({ days, events, navigation, showCapacity, emptyMessage = "No classes scheduled this week", title, footerNote, ctaLabel = "Reserve a place", ctaHref = null, buttonVariant = "outline", elevated = false, banded = false, calendarTimeZone, today }) => {
49582
+ const eventsByDate = groupEventsByDate(events, calendarTimeZone);
49376
49583
  const hasAnyEvents = days.some((date) => (eventsByDate.get(formatDateKey(date)) ?? []).length > 0);
49377
49584
  const ctaClassName = themeButtonClassName({
49378
49585
  variant: buttonVariant,
49379
49586
  size: "sm"
49380
49587
  });
49588
+ const todayClassName = themeButtonClassName({
49589
+ variant: buttonVariant,
49590
+ size: "sm"
49591
+ });
49381
49592
  const hasTitle = typeof title === "string" && title.length > 0;
49382
49593
  const hasFooterNote = typeof footerNote === "string" && footerNote.length > 0;
49383
- const hasCta = typeof ctaHref === "string" && ctaHref.length > 0;
49384
49594
  const footerNoteText = hasAnyEvents ? hasFooterNote ? footerNote : null : emptyMessage;
49385
- const showFooter = hasCta || footerNoteText !== null;
49595
+ const resolvedCtaHref = typeof ctaHref === "string" && ctaHref.length > 0 ? ctaHref : null;
49386
49596
  return /* @__PURE__ */ jsx("div", {
49387
49597
  className: "event-calendar event-calendar--week-board",
49388
49598
  "data-block": "event-calendar",
@@ -49394,59 +49604,34 @@ const WeekBoardView = ({ days, events, showCapacity, emptyMessage = "No classes
49394
49604
  children: [
49395
49605
  /* @__PURE__ */ jsxs("div", {
49396
49606
  className: "event-week-board-top",
49397
- children: [hasTitle ? /* @__PURE__ */ jsx("span", {
49398
- className: "event-week-board-title",
49399
- children: title
49400
- }) : null, /* @__PURE__ */ jsx("div", {
49401
- className: "event-week-board-legend",
49402
- "aria-hidden": "true",
49403
- children: LEGEND_ITEMS.map((item) => /* @__PURE__ */ jsxs("span", {
49404
- className: "event-week-board-legend-item",
49405
- children: [/* @__PURE__ */ jsx("i", {
49406
- className: "event-week-board-legend-dot",
49407
- "data-status": item.status
49408
- }), item.label]
49409
- }, item.status))
49410
- })]
49607
+ children: [
49608
+ hasTitle ? /* @__PURE__ */ jsx("span", {
49609
+ className: "event-week-board-title",
49610
+ children: title
49611
+ }) : null,
49612
+ /* @__PURE__ */ jsx(WeekBoardNavigationControls, {
49613
+ navigation,
49614
+ todayClassName
49615
+ }),
49616
+ /* @__PURE__ */ jsx(WeekBoardLegend, {})
49617
+ ]
49411
49618
  }),
49412
- /* @__PURE__ */ jsx("div", {
49413
- className: "event-week-board-grid",
49414
- children: days.map((date) => {
49415
- const dateKey = formatDateKey(date);
49416
- const dayEvents = eventsByDate.get(dateKey) ?? [];
49417
- const today = isToday(date);
49418
- return /* @__PURE__ */ jsxs("div", {
49419
- className: clsx("event-week-board-day", dayEvents.length === 0 && "event-week-board-day--empty", today && "event-week-board-day--today"),
49420
- children: [/* @__PURE__ */ jsxs("div", {
49421
- className: "event-week-board-day-head",
49422
- children: [/* @__PURE__ */ jsx("span", {
49423
- className: "event-week-board-day-abbr",
49424
- children: SHORT_DAY_NAMES[date.getDay()]
49425
- }), /* @__PURE__ */ jsx("span", {
49426
- className: "event-week-board-day-num",
49427
- children: date.getDate()
49428
- })]
49429
- }), /* @__PURE__ */ jsx("div", {
49430
- className: "event-week-board-day-body",
49431
- children: dayEvents.map((event) => /* @__PURE__ */ jsx(WeekBoardCard, {
49432
- event,
49433
- showCapacity
49434
- }, event.occurrenceId || event.id))
49435
- })]
49436
- }, dateKey);
49437
- })
49619
+ /* @__PURE__ */ jsx(WeekBoardLoadError, {
49620
+ navigation,
49621
+ buttonClassName: todayClassName
49438
49622
  }),
49439
- showFooter ? /* @__PURE__ */ jsxs("div", {
49440
- className: "event-week-board-foot",
49441
- children: [footerNoteText !== null ? /* @__PURE__ */ jsx("p", {
49442
- className: "event-week-board-foot-note",
49443
- children: footerNoteText
49444
- }) : null, hasCta ? /* @__PURE__ */ jsx("a", {
49445
- className: ctaClassName,
49446
- href: ctaHref ?? void 0,
49447
- children: ctaLabel
49448
- }) : null]
49449
- }) : null
49623
+ /* @__PURE__ */ jsx(WeekBoardGrid, {
49624
+ days,
49625
+ eventsByDate,
49626
+ showCapacity,
49627
+ today
49628
+ }),
49629
+ /* @__PURE__ */ jsx(WeekBoardFooter, {
49630
+ note: footerNoteText,
49631
+ ctaHref: resolvedCtaHref,
49632
+ ctaLabel,
49633
+ ctaClassName
49634
+ })
49450
49635
  ]
49451
49636
  })
49452
49637
  });
@@ -49477,6 +49662,7 @@ function buildTimetableModel(input) {
49477
49662
  items: input.events,
49478
49663
  viewDate: input.viewDate,
49479
49664
  startOfWeek: input.startOfWeek,
49665
+ today: input.today,
49480
49666
  getId: (event) => event.id,
49481
49667
  getStartsAt: (event) => event.startsAt,
49482
49668
  getEndsAt: (event) => event.endsAt,
@@ -49538,10 +49724,10 @@ function formatEventTimeRange(event) {
49538
49724
  return `${formatTime(event.startsAt, { timeZone })} - ${formatTime(event.endsAt, { timeZone })}`;
49539
49725
  }
49540
49726
  function formatDayLabel(date) {
49541
- return `${FULL_DAY_NAMES[date.getDay()] ?? ""}, ${MONTH_NAMES[date.getMonth()] ?? ""} ${date.getDate()}`;
49727
+ return `${FULL_DAY_NAMES[getDayOfWeek(date)] ?? ""}, ${MONTH_NAMES[date.month - 1] ?? ""} ${date.day}`;
49542
49728
  }
49543
49729
  function formatShortDayLabel(date) {
49544
- return `${(FULL_DAY_NAMES[date.getDay()] ?? "").slice(0, 3)} ${date.getDate()}`;
49730
+ return `${(FULL_DAY_NAMES[getDayOfWeek(date)] ?? "").slice(0, 3)} ${date.day}`;
49545
49731
  }
49546
49732
  //#endregion
49547
49733
  //#region ../blocks/src/system/runtime/nodes/events/EventCalendar.server.tsx
@@ -49588,15 +49774,15 @@ function renderListSsr(display, prefetchedEvents) {
49588
49774
  renderEventCard: (event) => renderEventCard(event, listDisplay)
49589
49775
  });
49590
49776
  }
49591
- function renderCalendarSsr(display) {
49777
+ function renderCalendarSsr(display, hydration) {
49592
49778
  const calendarView = display.defaultCalendarView;
49593
- const viewDate = /* @__PURE__ */ new Date();
49779
+ const viewDate = hydration.calendarContext.initialToday;
49594
49780
  const viewport = buildCalendarViewport({
49595
49781
  view: calendarView,
49596
49782
  anchorDate: viewDate,
49597
49783
  startOfWeek: display.startOfWeek
49598
49784
  });
49599
- const eventsByDate = groupEventsByDate([...display.events]);
49785
+ const eventsByDate = groupEventsByDate([...display.events], hydration.calendarContext.timeZone);
49600
49786
  const dayNames = getOrderedDayNames(display.startOfWeek);
49601
49787
  return /* @__PURE__ */ jsxs("div", {
49602
49788
  className: clsx("event-calendar", display.className ?? void 0),
@@ -49663,7 +49849,9 @@ function renderCalendarSsr(display) {
49663
49849
  days: viewport.days,
49664
49850
  events: [...display.events],
49665
49851
  emptyMessage: display.emptyMessage,
49666
- showCapacity: display.showCapacity
49852
+ showCapacity: display.showCapacity,
49853
+ calendarTimeZone: hydration.calendarContext.timeZone,
49854
+ today: hydration.calendarContext.initialToday
49667
49855
  }) : /* @__PURE__ */ jsxs("div", {
49668
49856
  className: "event-calendar-container",
49669
49857
  children: [/* @__PURE__ */ jsx("div", {
@@ -49677,13 +49865,13 @@ function renderCalendarSsr(display) {
49677
49865
  children: viewport.days.map((date) => {
49678
49866
  const dateKey = formatDateKey(date);
49679
49867
  const dayEvents = eventsByDate.get(dateKey) || [];
49680
- const isCurrentMonth = date.getMonth() === viewDate.getMonth();
49681
- const today = isToday(date);
49868
+ const isCurrentMonth = date.month === viewDate.month;
49869
+ const today = isToday(date, hydration.calendarContext.initialToday);
49682
49870
  return /* @__PURE__ */ jsxs("div", {
49683
49871
  className: clsx("event-calendar-day", !isCurrentMonth && "event-calendar-day--other-month", today && "event-calendar-day--today"),
49684
49872
  children: [/* @__PURE__ */ jsx("div", {
49685
49873
  className: "event-calendar-day-number",
49686
- children: date.getDate()
49874
+ children: date.day
49687
49875
  }), /* @__PURE__ */ jsxs("div", {
49688
49876
  className: "event-calendar-events",
49689
49877
  children: [dayEvents.slice(0, 3).map((event) => /* @__PURE__ */ jsxs("a", {
@@ -49721,8 +49909,8 @@ function renderCalendarSsr(display) {
49721
49909
  ]
49722
49910
  });
49723
49911
  }
49724
- function renderTimetableSsr(display) {
49725
- const viewDate = /* @__PURE__ */ new Date();
49912
+ function renderTimetableSsr(display, hydration) {
49913
+ const viewDate = hydration.calendarContext.initialToday;
49726
49914
  const viewport = buildCalendarViewport({
49727
49915
  view: "week",
49728
49916
  anchorDate: viewDate,
@@ -49731,7 +49919,8 @@ function renderTimetableSsr(display) {
49731
49919
  const model = buildTimetableModel({
49732
49920
  events: [...display.events],
49733
49921
  viewDate,
49734
- startOfWeek: display.startOfWeek
49922
+ startOfWeek: display.startOfWeek,
49923
+ today: hydration.calendarContext.initialToday
49735
49924
  });
49736
49925
  const selectedDayKey = model.days.find((day) => day.isToday)?.dateKey ?? model.days[0]?.dateKey ?? "";
49737
49926
  return /* @__PURE__ */ jsxs("div", {
@@ -49886,20 +50075,21 @@ function renderTimetableSsr(display) {
49886
50075
  })]
49887
50076
  });
49888
50077
  }
49889
- function renderWeekBoardSsr(display) {
49890
- return /* @__PURE__ */ jsx(WeekBoardView, {
49891
- days: getWeekCalendarDayTuple(/* @__PURE__ */ new Date(), display.startOfWeek),
49892
- events: [...display.events],
49893
- showCapacity: display.showCapacity,
49894
- emptyMessage: display.emptyMessage,
49895
- title: display.boardTitle,
49896
- footerNote: display.footerNote,
49897
- ctaLabel: display.loadMoreText ?? display.buttonText,
49898
- ctaHref: display.seeAllUrl,
49899
- buttonVariant: display.buttonVariant,
49900
- elevated: display.elevated,
49901
- banded: display.banded
49902
- });
50078
+ function renderWeekBoardSsr(display, hydration) {
50079
+ const today = hydration.calendarContext.initialToday;
50080
+ const days = getWeekCalendarDayTuple(today, display.startOfWeek);
50081
+ const navigation = {
50082
+ kind: "static",
50083
+ ...buildWeekBoardNavigationState({
50084
+ days,
50085
+ title: formatCalendarTitle(today, "week", [...days])
50086
+ }, today)
50087
+ };
50088
+ return /* @__PURE__ */ jsx(WeekBoardView, { ...buildWeekBoardViewProps({
50089
+ ...display,
50090
+ calendarTimeZone: hydration.calendarContext.timeZone,
50091
+ today
50092
+ }, days, navigation) });
49903
50093
  }
49904
50094
  function formatHourLabel(hour) {
49905
50095
  if (hour === 0) return "12 AM";
@@ -49925,13 +50115,13 @@ const EventCalendarSSR = (props) => {
49925
50115
  content = renderListSsr(display, hydration.prefetchedEvents);
49926
50116
  break;
49927
50117
  case "timetable":
49928
- content = renderTimetableSsr(display);
50118
+ content = renderTimetableSsr(display, hydration);
49929
50119
  break;
49930
50120
  case "week-board":
49931
- content = renderWeekBoardSsr(display);
50121
+ content = renderWeekBoardSsr(display, hydration);
49932
50122
  break;
49933
50123
  case "calendar":
49934
- content = renderCalendarSsr(display);
50124
+ content = renderCalendarSsr(display, hydration);
49935
50125
  break;
49936
50126
  default: content = assertNeverDisplayMode(display.displayMode);
49937
50127
  }
@@ -53160,36 +53350,9 @@ function buildLegacyHeaderNavLinkStyle(header, contrastTreatment, navTextSize) {
53160
53350
  ...navTextSize ? { textSize: navTextSize } : {}
53161
53351
  };
53162
53352
  if (header.navWeight === "bold" && navStyle === "solid") style.textTransform = "uppercase";
53163
- applyLegacyNavEffects(style, header);
53164
53353
  applyLegacyGlassTreatment(style, header, navStyle);
53165
53354
  return style;
53166
53355
  }
53167
- function applyLegacyNavEffects(style, header) {
53168
- if (!header.navEffects) return;
53169
- const effects = style.effects = {
53170
- ...style.effects,
53171
- hover: [...style.effects?.hover ?? []],
53172
- active: [...style.effects?.active ?? []]
53173
- };
53174
- if (header.navEffects.glow) effects.hover.push({
53175
- effectId: "outer-glow",
53176
- options: { spread: 5 }
53177
- });
53178
- if (header.navEffects.neumorphic) {
53179
- effects.hover.push({
53180
- effectId: "neumorphic-shadow",
53181
- options: { mode: "pressed" }
53182
- });
53183
- effects.hover.push({
53184
- effectId: "text-color-change",
53185
- options: { hoverColorToken: "primary" }
53186
- });
53187
- }
53188
- if (header.navEffects.underlineGradient) effects.hover.push({
53189
- effectId: "border-bottom-grow",
53190
- options: { origin: "center" }
53191
- });
53192
- }
53193
53356
  function applyLegacyGlassTreatment(style, header, navStyle) {
53194
53357
  if (header.navContainer?.type !== "glass" || navStyle !== "minimal") return;
53195
53358
  style.borderRadius = "full";
@@ -53215,19 +53378,6 @@ function glassStateEffects(opacity) {
53215
53378
  //#region ../theme-core/src/header/generateHeaderCss.ts
53216
53379
  const HEADER_BLUR = "blur(12px)";
53217
53380
  const FLOATING_BLUR = "blur(16px)";
53218
- const INSET_SHELL_MARKER_CLASS = "header-bar-frame-inset-shell";
53219
- const INSET_SHELL_EDGE = "1rem";
53220
- const INSET_SHELL_INSET_BLOCK = "0.875rem";
53221
- const INSET_SHELL_RADIUS = "9999px";
53222
- const INSET_SHELL_CONTAINER_MAX = "80rem";
53223
- const INSET_SHELL_INTERIOR_PADDING_X = "1.75rem";
53224
- /**
53225
- * Whether the classic header should render as an inset "brand capsule" shell.
53226
- * The facet is only meaningful for the classic layout; other variants ignore it.
53227
- */
53228
- function usesInsetShell(header) {
53229
- return header.barFrame === "inset-shell" && header.variant === "classic";
53230
- }
53231
53381
  /**
53232
53382
  * Generate complete CSS for header system from theme config
53233
53383
  *
@@ -53305,36 +53455,7 @@ function generateHeaderRootStyles(header, themeId, tokens) {
53305
53455
  const overlayFix = `\n${`${selector}:has([data-nav-mobile-overlay][aria-hidden="false"])`} {\n backdrop-filter: none !important;\n -webkit-backdrop-filter: none !important;\n}\n`;
53306
53456
  const scrollTreatmentStyles = generateHeaderScrollTreatmentStyles(header.scrollTreatment, themeId, header.variant, scrolledPaintRules);
53307
53457
  if (isContainedNav || basePaintRules.length === 0) return `${baseBlock}\n${overlayFix}${scrollTreatmentStyles}`;
53308
- return `${baseBlock}\n${renderHeaderPaintTargetRules(getHeaderPaintTargets(header.variant, themeId), basePaintRules)}\n${overlayFix}${usesInsetShell(header) ? generateInsetShellStyles(header, themeId) : ""}${scrollTreatmentStyles}`;
53309
- }
53310
- /**
53311
- * Reshapes the full-bleed header paint layer into a contained brand capsule at
53312
- * the desktop breakpoint. The capsule is centred, capped to the page container
53313
- * width, inset from the top, fully rounded, and carries the header depth so it
53314
- * reads as a floating pill. Root border/shadow are neutralised on desktop so a
53315
- * bordered style does not leave a full-width hairline around the capsule. Sticky
53316
- * positioning is unaffected — the root stays full width and sticky while the
53317
- * ::before paint provides the capsule.
53318
- */
53319
- function generateInsetShellStyles(header, themeId) {
53320
- const scope = `:where([data-theme-scope="${themeId}"])`;
53321
- const shellRootSelector = `${scope} .header-root.${INSET_SHELL_MARKER_CLASS}`;
53322
- const shellBeforeSelector = `${scope} .header-root.${INSET_SHELL_MARKER_CLASS}::before`;
53323
- const layoutSelector = `${scope} .header-root.${INSET_SHELL_MARKER_CLASS} .rb-header-layout`;
53324
- const widthValue = header.maxWidth === "full" ? `calc(100% - (${INSET_SHELL_EDGE} * 2))` : `min(${INSET_SHELL_CONTAINER_MAX}, calc(100% - (${INSET_SHELL_EDGE} * 2)))`;
53325
- const shadowVar = getComponentShadowVar(header.shadow?.elevation ?? "none");
53326
- const rootRules = ["border: none;", "box-shadow: none;"];
53327
- const beforeRules = [
53328
- `inset: ${INSET_SHELL_INSET_BLOCK} auto;`,
53329
- "left: 50%;",
53330
- "right: auto;",
53331
- "transform: translateX(-50%);",
53332
- `width: ${widthValue};`,
53333
- `border-radius: ${INSET_SHELL_RADIUS};`,
53334
- ...shadowVar ? [`box-shadow: ${shadowVar};`] : []
53335
- ];
53336
- const layoutRules = [`padding-inline: ${INSET_SHELL_INTERIOR_PADDING_X};`];
53337
- return `\n@media ${MQ_MD_UP} {\n${shellRootSelector} {\n ${rootRules.join("\n ")}\n}\n${shellBeforeSelector} {\n ${beforeRules.join("\n ")}\n}\n${layoutSelector} {\n ${layoutRules.join("\n ")}\n}\n}\n`;
53458
+ return `${baseBlock}\n${renderHeaderPaintTargetRules(getHeaderPaintTargets(header.variant, themeId), basePaintRules)}\n${overlayFix}${scrollTreatmentStyles}`;
53338
53459
  }
53339
53460
  /**
53340
53461
  * Generate logo typography styles
@@ -53408,7 +53529,7 @@ function generateLogoTextBehaviorStyles(logoText, themeId) {
53408
53529
  * Generate navigation link styles based on navStyle and effects
53409
53530
  *
53410
53531
  * Uses the composable NavLinkStyle system with backward compatibility for
53411
- * legacy navStyle, navWeight, navColor, and navEffects settings.
53532
+ * legacy navStyle, navWeight and navColor settings.
53412
53533
  */
53413
53534
  function generateNavLinkStyles(header, themeId, tokens, theme, contrastTreatment, navTextSize) {
53414
53535
  const navPreferences = {
@@ -56045,9 +56166,60 @@ function pageBlockPosition(index, blockCount) {
56045
56166
  return "middle";
56046
56167
  }
56047
56168
  //#endregion
56169
+ //#region ../blocks/src/pageRenderAdmission.ts
56170
+ function toPresentationPage(page) {
56171
+ if (!page) return null;
56172
+ return {
56173
+ ...page,
56174
+ blocks: page.blocks.map((block) => {
56175
+ const presentationMetadata = {
56176
+ id: block.id,
56177
+ kind: block.kind,
56178
+ purpose: block.purpose,
56179
+ ...block.bindings ? { bindings: block.bindings } : {}
56180
+ };
56181
+ return block.admission.kind === "renderable" ? {
56182
+ ...presentationMetadata,
56183
+ content: block.admission.content,
56184
+ presentationAdmission: { kind: "renderable" }
56185
+ } : {
56186
+ ...presentationMetadata,
56187
+ presentationAdmission: {
56188
+ kind: "invalid",
56189
+ error: block.admission.error
56190
+ }
56191
+ };
56192
+ })
56193
+ };
56194
+ }
56195
+ function toEditorPresentationPage(page) {
56196
+ if (!page) return null;
56197
+ return {
56198
+ ...page,
56199
+ blocks: page.blocks.map((block) => ({
56200
+ ...block,
56201
+ presentationAdmission: { kind: "editor" }
56202
+ }))
56203
+ };
56204
+ }
56205
+ //#endregion
56048
56206
  //#region ../blocks/src/PageRenderer.tsx
56049
56207
  function PageRenderer$1(props) {
56050
- const { page, runtimeBoundaries, usePlaceholders = false, dataContext, wrapBlock, wrapBoundary, registry, routeMap, blockOverrides, sdkConfig, customBlocks, previewAuthoring } = props;
56208
+ const page = React$1.useMemo(() => toPresentationPage(props.page), [props.page]);
56209
+ return /* @__PURE__ */ jsx(PageRendererCore, {
56210
+ ...props,
56211
+ page
56212
+ });
56213
+ }
56214
+ function EditorPageRenderer$1(props) {
56215
+ const page = React$1.useMemo(() => toEditorPresentationPage(props.page), [props.page]);
56216
+ return /* @__PURE__ */ jsx(PageRendererCore, {
56217
+ ...props,
56218
+ page
56219
+ });
56220
+ }
56221
+ function PageRendererCore(props) {
56222
+ const { page, runtimeBoundaries, dataContext, wrapBlock, wrapBoundary, registry } = props;
56051
56223
  const imageResolveContext = React$1.useMemo(() => resolvePageRendererImageResolveContext(dataContext), [
56052
56224
  dataContext?.imageResolveContext,
56053
56225
  dataContext?.mediaAssets,
@@ -56146,11 +56318,11 @@ function resolvePageRendererImageResolveContext(dataContext) {
56146
56318
  mediaAssets: input?.mediaAssets ?? dataContext?.mediaAssets
56147
56319
  });
56148
56320
  }
56321
+ const resolveBookingTimeZone = (dataContext) => dataContext?.bookingTimeZone;
56149
56322
  function buildBlockSectionProps(input) {
56150
56323
  return {
56151
56324
  themeResources: input.themeResources,
56152
56325
  block: input.block,
56153
- usePlaceholders: input.props.usePlaceholders ?? false,
56154
56326
  dataContext: input.props.dataContext,
56155
56327
  blockKey: input.blockKey,
56156
56328
  registry: input.registry,
@@ -56174,7 +56346,6 @@ function getBlockOverride(blockKind, overrides) {
56174
56346
  if (overrides[shortKind]) return overrides[shortKind];
56175
56347
  }
56176
56348
  const blockComponentCache = /* @__PURE__ */ new WeakMap();
56177
- const manifestValidatorCache = /* @__PURE__ */ new WeakMap();
56178
56349
  const DEFAULT_PERF_SLOW_BLOCK_RENDER_THRESHOLD_MS = 25;
56179
56350
  let cachedPerfSlowBlockRenderThresholdMs = null;
56180
56351
  function getPerfSlowBlockRenderThresholdMs() {
@@ -56192,44 +56363,13 @@ function getCachedBlockComponent(manifest) {
56192
56363
  return created;
56193
56364
  }
56194
56365
  /**
56195
- * Render-time manifest validator — see `.claude/plans/dapper-jingling-emerson.md`
56196
- * Phase 10. One function, two flavours:
56197
- *
56198
- * - `strict` (default): the contract every render surface tries first. No
56199
- * null tolerance, no missing-required tolerance. Production frontends and
56200
- * the dashboard preview both go through this path; after Phases 6-9 the
56201
- * preview satisfies strict structurally.
56202
- * - `relaxed`: editor-mid-edit fallback. Reached via `usePlaceholders` when
56203
- * strict fails. Built on `mode: 'draft'` so empty required fields parse
56204
- * as `undefined` and the user's valid in-flight fields flow through.
56205
- *
56206
- * Per-manifest, per-flavour cached. Adding a third flavour means adding a
56207
- * branch to the `createManifestValidator` call below and a member to
56208
- * `ValidationFlavour` — no per-flavour helper duplication.
56209
- */
56210
- function getCachedValidator(manifest, flavour) {
56211
- let perFlavour = manifestValidatorCache.get(manifest);
56212
- if (!perFlavour) {
56213
- perFlavour = /* @__PURE__ */ new Map();
56214
- manifestValidatorCache.set(manifest, perFlavour);
56215
- }
56216
- const cached = perFlavour.get(flavour);
56217
- if (cached) return cached;
56218
- const created = createManifestValidator(manifest, {
56219
- allowNull: false,
56220
- ...flavour === "relaxed" ? { mode: "draft" } : {}
56221
- });
56222
- perFlavour.set(flavour, created);
56223
- return created;
56224
- }
56225
- /**
56226
56366
  * Memoized block section component.
56227
56367
  *
56228
56368
  * Custom memoization prevents render loops during editing by explicitly checking
56229
56369
  * referential equality of all props that affect rendering. Standard React.memo
56230
56370
  * wouldn't catch all the render triggers from the editor store.
56231
56371
  */
56232
- const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources, block, usePlaceholders, dataContext, registry, routeMap, wrapBlock, blockOverrides, sdkConfig, customBlocks, imageResolveContext, previewAuthoring }) {
56372
+ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources, block, dataContext, registry, routeMap, wrapBlock, blockOverrides, sdkConfig, customBlocks, imageResolveContext, previewAuthoring }) {
56233
56373
  const perfStart = globalThis.process?.env?.DEBUG_PERF === "1" && typeof window === "undefined" ? Date.now() : 0;
56234
56374
  const fallbackImageResolveContext = getCurrentResolveImageUrlContext();
56235
56375
  const resolvedImageResolveContext = imageResolveContext ?? fallbackImageResolveContext;
@@ -56249,6 +56389,19 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
56249
56389
  } catch {}
56250
56390
  return node;
56251
56391
  }
56392
+ if (block.presentationAdmission.kind === "invalid") {
56393
+ try {
56394
+ if (globalThis.process?.env?.NODE_ENV !== "production") console.warn("[blocks:PageRenderer] block admission failed", {
56395
+ blockId: block.id ?? null,
56396
+ blockKind: block.kind,
56397
+ error: block.presentationAdmission.error
56398
+ });
56399
+ } catch {}
56400
+ return finish(fallback(`Invalid content for block kind "${block.kind}".`), {
56401
+ outcome: "invalidContent",
56402
+ override: false
56403
+ });
56404
+ }
56252
56405
  const OverrideComponent = getBlockOverride(block.kind, blockOverrides);
56253
56406
  requireBlockOverride(themeResources, OverrideComponent, block.kind);
56254
56407
  let definition = getBlockDefinition(block.kind);
@@ -56318,25 +56471,10 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
56318
56471
  });
56319
56472
  }
56320
56473
  const content = getRenderableContent(block, dataContext?.previewStage);
56321
- const parsed = getCachedValidator(definition.manifest, "strict").safeParse(content);
56322
- try {
56323
- if (globalThis.process?.env?.NODE_ENV !== "production" && !parsed.success && !usePlaceholders) console.warn("[blocks:PageRenderer] manifest validation failed", {
56324
- blockId: block.id ?? null,
56325
- blockKind: block.kind,
56326
- content: summarizeValue(content),
56327
- expectedFields: (definition.manifest.fields ?? []).map((f) => ({
56328
- id: f.id,
56329
- type: f.type,
56330
- required: !!f.required
56331
- })),
56332
- issues: parsed.error?.issues ?? null
56333
- });
56334
- } catch {}
56335
- let data;
56336
- if (parsed.success) data = parsed.data;
56337
- else if (usePlaceholders) {
56338
- const relaxed = getCachedValidator(definition.manifest, "relaxed").safeParse(content);
56339
- if (relaxed.success) data = relaxed.data;
56474
+ let data = content;
56475
+ if (block.presentationAdmission.kind === "editor") {
56476
+ const draft = validateManifestDraft(compileManifestContentContract(definition.manifest), content);
56477
+ if (draft.kind === "previewable") data = draft.data;
56340
56478
  else {
56341
56479
  const rendered = fallback(`Unable to render block kind "${block.kind}".`);
56342
56480
  return finish(wrapBlock && block.id ? /* @__PURE__ */ jsx(Fragment, { children: wrapBlock(block.id, rendered) }) : /* @__PURE__ */ jsx(Fragment, { children: rendered }), {
@@ -56344,12 +56482,6 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
56344
56482
  override: false
56345
56483
  });
56346
56484
  }
56347
- } else {
56348
- const rendered = fallback(`Invalid content for block kind "${block.kind}".`);
56349
- return finish(wrapBlock && block.id ? /* @__PURE__ */ jsx(Fragment, { children: wrapBlock(block.id, rendered) }) : /* @__PURE__ */ jsx(Fragment, { children: rendered }), {
56350
- outcome: "invalidContent",
56351
- override: false
56352
- });
56353
56485
  }
56354
56486
  try {
56355
56487
  const routes = routeMap ?? dataContext?.routes;
@@ -56375,6 +56507,7 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
56375
56507
  blockId: block.id ?? null,
56376
56508
  blockKind: block.kind
56377
56509
  };
56510
+ baseProps.bookingTimeZone = resolveBookingTimeZone(dataContext);
56378
56511
  let rendered;
56379
56512
  if (OverrideComponent) rendered = /* @__PURE__ */ jsx(OverrideComponent, { ...baseProps });
56380
56513
  else {
@@ -56421,14 +56554,14 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
56421
56554
  hasResolvedData
56422
56555
  });
56423
56556
  } catch {
56424
- const rendered = usePlaceholders ? fallback(`Unable to render block kind "${block.kind}".`) : fallback(`Invalid content for block kind "${block.kind}".`);
56557
+ const rendered = block.presentationAdmission.kind === "editor" ? fallback(`Unable to render block kind "${block.kind}".`) : fallback(`Invalid content for block kind "${block.kind}".`);
56425
56558
  return finish(wrapBlock && block.id ? /* @__PURE__ */ jsx(Fragment, { children: wrapBlock(block.id, rendered) }) : /* @__PURE__ */ jsx(Fragment, { children: rendered }), {
56426
56559
  outcome: "exception",
56427
56560
  override: false
56428
56561
  });
56429
56562
  }
56430
56563
  }, (prev, next) => {
56431
- return prev.block === next.block && prev.registry === next.registry && prev.dataContext === next.dataContext && prev.themeResources === next.themeResources && prev.routeMap === next.routeMap && prev.usePlaceholders === next.usePlaceholders && prev.wrapBlock === next.wrapBlock && prev.blockOverrides === next.blockOverrides && prev.sdkConfig === next.sdkConfig && prev.customBlocks === next.customBlocks && prev.imageResolveContext === next.imageResolveContext && prev.previewAuthoring === next.previewAuthoring;
56564
+ return prev.block === next.block && prev.registry === next.registry && prev.dataContext === next.dataContext && prev.themeResources === next.themeResources && prev.routeMap === next.routeMap && prev.block.presentationAdmission === next.block.presentationAdmission && prev.wrapBlock === next.wrapBlock && prev.blockOverrides === next.blockOverrides && prev.sdkConfig === next.sdkConfig && prev.customBlocks === next.customBlocks && prev.imageResolveContext === next.imageResolveContext && prev.previewAuthoring === next.previewAuthoring;
56432
56565
  });
56433
56566
  function fallback(message) {
56434
56567
  return /* @__PURE__ */ jsx("div", {
@@ -56455,32 +56588,6 @@ function getRenderableContent(block, previewStage = "published") {
56455
56588
  if (!isObjectRecord$1(source)) return {};
56456
56589
  return source;
56457
56590
  }
56458
- function summarizeValue(value) {
56459
- try {
56460
- if (value === null || value === void 0) return value;
56461
- if (typeof value === "string") return value.length > 160 ? `${value.slice(0, 160)}…` : value;
56462
- if (typeof value === "number" || typeof value === "boolean") return value;
56463
- if (Array.isArray(value)) return `[array(${value.length})]`;
56464
- if (typeof value === "object") {
56465
- const rec = value;
56466
- const keys = Object.keys(rec);
56467
- const preview = {};
56468
- for (const k of keys.slice(0, 8)) {
56469
- const v = rec[k];
56470
- if (k === "body" || k === "content") preview[k] = !!(v && typeof v === "object" && isObjectRecord$1(v) && v.type === "doc") ? "{type:doc}" : summarizeValue(v);
56471
- else preview[k] = summarizeValue(v);
56472
- }
56473
- return {
56474
- keys,
56475
- preview
56476
- };
56477
- }
56478
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol") return String(value);
56479
- return "[uninspectable]";
56480
- } catch {
56481
- return "[uninspectable]";
56482
- }
56483
- }
56484
56591
  //#endregion
56485
56592
  //#region ../theme-core/src/generated/containerResponsiveThemeCss.ts
56486
56593
  /**
@@ -63077,31 +63184,37 @@ const containerResponsiveThemeCss = `/*
63077
63184
  */
63078
63185
 
63079
63186
  /* Main container */
63080
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar {
63187
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-calendar {
63081
63188
  width: 100%;
63082
63189
  }
63083
63190
 
63084
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-container {
63191
+ :where(.theme-scope)
63192
+ :where([data-block='event-calendar'])
63193
+ .event-calendar-container {
63085
63194
  border: 1px solid var(--ec-border);
63086
63195
  border-radius: var(--rb-block-radius);
63087
63196
  overflow: hidden;
63088
63197
  }
63089
63198
 
63090
63199
  /* Calendar navigation */
63091
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-header {
63200
+ :where(.theme-scope)
63201
+ :where([data-block='event-calendar'])
63202
+ .event-calendar-header {
63092
63203
  display: flex;
63093
63204
  align-items: center;
63094
63205
  justify-content: space-between;
63095
63206
  margin-bottom: 1rem;
63096
63207
  }
63097
63208
 
63098
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-nav {
63209
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-calendar-nav {
63099
63210
  display: flex;
63100
63211
  align-items: center;
63101
63212
  gap: 0.5rem;
63102
63213
  }
63103
63214
 
63104
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-nav-btn {
63215
+ :where(.theme-scope)
63216
+ :where([data-block='event-calendar'])
63217
+ .event-calendar-nav-btn {
63105
63218
  display: flex;
63106
63219
  align-items: center;
63107
63220
  justify-content: center;
@@ -63116,11 +63229,15 @@ const containerResponsiveThemeCss = `/*
63116
63229
  transition: background-color var(--rb-block-transition) ease;
63117
63230
  }
63118
63231
 
63119
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-nav-btn:hover {
63232
+ :where(.theme-scope)
63233
+ :where([data-block='event-calendar'])
63234
+ .event-calendar-nav-btn:hover {
63120
63235
  background-color: var(--ec-nav-hover-bg);
63121
63236
  }
63122
63237
 
63123
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-title {
63238
+ :where(.theme-scope)
63239
+ :where([data-block='event-calendar'])
63240
+ .event-calendar-title {
63124
63241
  font-size: 1.125rem;
63125
63242
  font-weight: 600;
63126
63243
  min-width: 200px;
@@ -63128,13 +63245,17 @@ const containerResponsiveThemeCss = `/*
63128
63245
  }
63129
63246
 
63130
63247
  /* Day headers */
63131
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-headers {
63248
+ :where(.theme-scope)
63249
+ :where([data-block='event-calendar'])
63250
+ .event-calendar-day-headers {
63132
63251
  display: grid;
63133
63252
  grid-template-columns: repeat(7, minmax(0, 1fr));
63134
63253
  background-color: var(--ec-header-bg);
63135
63254
  }
63136
63255
 
63137
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-header {
63256
+ :where(.theme-scope)
63257
+ :where([data-block='event-calendar'])
63258
+ .event-calendar-day-header {
63138
63259
  text-align: center;
63139
63260
  padding: 0.5rem;
63140
63261
  font-size: 0.875rem;
@@ -63144,33 +63265,43 @@ const containerResponsiveThemeCss = `/*
63144
63265
  }
63145
63266
 
63146
63267
  /* Calendar grid */
63147
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-grid {
63268
+ :where(.theme-scope)
63269
+ :where([data-block='event-calendar'])
63270
+ .event-calendar-grid {
63148
63271
  display: grid;
63149
63272
  grid-template-columns: repeat(7, minmax(0, 1fr));
63150
63273
  }
63151
63274
 
63152
63275
  /* Day cells */
63153
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day {
63276
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-calendar-day {
63154
63277
  min-height: 100px;
63155
63278
  border-bottom: 1px solid var(--ec-border-subtle);
63156
63279
  border-right: 1px solid var(--ec-border-subtle);
63157
63280
  padding: 0.25rem;
63158
63281
  }
63159
63282
 
63160
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day:nth-child(7n) {
63283
+ :where(.theme-scope)
63284
+ :where([data-block='event-calendar'])
63285
+ .event-calendar-day:nth-child(7n) {
63161
63286
  border-right: none;
63162
63287
  }
63163
63288
 
63164
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day--today {
63289
+ :where(.theme-scope)
63290
+ :where([data-block='event-calendar'])
63291
+ .event-calendar-day--today {
63165
63292
  background-color: var(--ec-today-bg);
63166
63293
  }
63167
63294
 
63168
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day--other-month {
63295
+ :where(.theme-scope)
63296
+ :where([data-block='event-calendar'])
63297
+ .event-calendar-day--other-month {
63169
63298
  opacity: var(--ec-other-month-opacity);
63170
63299
  }
63171
63300
 
63172
63301
  /* Day number */
63173
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-number {
63302
+ :where(.theme-scope)
63303
+ :where([data-block='event-calendar'])
63304
+ .event-calendar-day-number {
63174
63305
  font-size: 0.875rem;
63175
63306
  font-weight: 500;
63176
63307
  margin-bottom: 0.25rem;
@@ -63178,21 +63309,28 @@ const containerResponsiveThemeCss = `/*
63178
63309
  opacity: var(--ec-day-number-opacity);
63179
63310
  }
63180
63311
 
63181
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day--today .event-calendar-day-number {
63312
+ :where(.theme-scope)
63313
+ :where([data-block='event-calendar'])
63314
+ .event-calendar-day--today
63315
+ .event-calendar-day-number {
63182
63316
  font-weight: 700;
63183
63317
  opacity: 1;
63184
63318
  color: var(--ec-today-text);
63185
63319
  }
63186
63320
 
63187
63321
  /* Events list */
63188
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-events {
63322
+ :where(.theme-scope)
63323
+ :where([data-block='event-calendar'])
63324
+ .event-calendar-events {
63189
63325
  display: flex;
63190
63326
  flex-direction: column;
63191
63327
  gap: 0.25rem;
63192
63328
  }
63193
63329
 
63194
63330
  /* Event item - solid primary background */
63195
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event {
63331
+ :where(.theme-scope)
63332
+ :where([data-block='event-calendar'])
63333
+ .event-calendar-event {
63196
63334
  display: flex;
63197
63335
  align-items: center;
63198
63336
  gap: 0.375rem;
@@ -63213,24 +63351,32 @@ const containerResponsiveThemeCss = `/*
63213
63351
  transform var(--rb-block-transition) ease;
63214
63352
  }
63215
63353
 
63216
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event:hover {
63354
+ :where(.theme-scope)
63355
+ :where([data-block='event-calendar'])
63356
+ .event-calendar-event:hover {
63217
63357
  background-color: var(--ec-event-solid-hover-bg);
63218
63358
  transform: translateY(-1px);
63219
63359
  }
63220
63360
 
63221
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event:focus-visible {
63361
+ :where(.theme-scope)
63362
+ :where([data-block='event-calendar'])
63363
+ .event-calendar-event:focus-visible {
63222
63364
  outline: 2px solid var(--ec-event-solid-bg);
63223
63365
  outline-offset: 2px;
63224
63366
  }
63225
63367
 
63226
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event-time {
63368
+ :where(.theme-scope)
63369
+ :where([data-block='event-calendar'])
63370
+ .event-calendar-event-time {
63227
63371
  flex-shrink: 0;
63228
63372
  white-space: nowrap;
63229
63373
  font-weight: 600;
63230
63374
  opacity: 0.9;
63231
63375
  }
63232
63376
 
63233
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event-title {
63377
+ :where(.theme-scope)
63378
+ :where([data-block='event-calendar'])
63379
+ .event-calendar-event-title {
63234
63380
  min-width: 0;
63235
63381
  overflow: hidden;
63236
63382
  text-overflow: ellipsis;
@@ -63238,7 +63384,9 @@ const containerResponsiveThemeCss = `/*
63238
63384
  }
63239
63385
 
63240
63386
  /* More indicator - clickable button style */
63241
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-more {
63387
+ :where(.theme-scope)
63388
+ :where([data-block='event-calendar'])
63389
+ .event-calendar-more {
63242
63390
  font-size: 0.75rem;
63243
63391
  padding: 0.25rem 0.5rem;
63244
63392
  color: rgb(var(--tb-primary));
@@ -63250,16 +63398,22 @@ const containerResponsiveThemeCss = `/*
63250
63398
  transition: background-color var(--rb-block-transition) ease;
63251
63399
  }
63252
63400
 
63253
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-more:hover {
63401
+ :where(.theme-scope)
63402
+ :where([data-block='event-calendar'])
63403
+ .event-calendar-more:hover {
63254
63404
  background-color: rgba(var(--tb-primary), 0.1);
63255
63405
  }
63256
63406
 
63257
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-more:focus-visible {
63407
+ :where(.theme-scope)
63408
+ :where([data-block='event-calendar'])
63409
+ .event-calendar-more:focus-visible {
63258
63410
  outline: 2px solid rgb(var(--tb-primary));
63259
63411
  outline-offset: 2px;
63260
63412
  }
63261
63413
 
63262
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-empty {
63414
+ :where(.theme-scope)
63415
+ :where([data-block='event-calendar'])
63416
+ .event-calendar-empty {
63263
63417
  text-align: center;
63264
63418
  font-size: 0.875rem;
63265
63419
  opacity: var(--ec-empty-opacity);
@@ -63267,21 +63421,21 @@ const containerResponsiveThemeCss = `/*
63267
63421
  }
63268
63422
 
63269
63423
  /* Filters container */
63270
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filters {
63424
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-filters {
63271
63425
  display: flex;
63272
63426
  flex-wrap: wrap;
63273
63427
  gap: 1rem;
63274
63428
  margin-bottom: 1.5rem;
63275
63429
  }
63276
63430
 
63277
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filter {
63431
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-filter {
63278
63432
  display: flex;
63279
63433
  flex-direction: column;
63280
63434
  gap: 0.375rem;
63281
63435
  min-width: 180px;
63282
63436
  }
63283
63437
 
63284
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filter-label {
63438
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-filter-label {
63285
63439
  font-size: 0.75rem;
63286
63440
  font-weight: 500;
63287
63441
  text-transform: uppercase;
@@ -63290,19 +63444,23 @@ const containerResponsiveThemeCss = `/*
63290
63444
  }
63291
63445
 
63292
63446
  /* Event filter select inherits base styling from .form-select, only block-specific overrides here */
63293
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filter-select {
63447
+ :where(.theme-scope)
63448
+ :where([data-block='event-calendar'])
63449
+ .event-filter-select {
63294
63450
  /* Use theme input radius instead of block radius for consistency with other form inputs */
63295
63451
  border-radius: var(--rb-input-radius, var(--rb-block-radius));
63296
63452
  }
63297
63453
 
63298
63454
  /* Category filter buttons */
63299
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-buttons {
63455
+ :where(.theme-scope)
63456
+ :where([data-block='event-calendar'])
63457
+ .event-category-buttons {
63300
63458
  display: flex;
63301
63459
  flex-wrap: wrap;
63302
63460
  gap: 0.5rem;
63303
63461
  }
63304
63462
 
63305
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn {
63463
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-category-btn {
63306
63464
  padding: 0.5rem 1rem;
63307
63465
  font-size: 0.875rem;
63308
63466
  font-weight: 500;
@@ -63314,50 +63472,66 @@ const containerResponsiveThemeCss = `/*
63314
63472
  transition: all var(--rb-block-transition) ease;
63315
63473
  }
63316
63474
 
63317
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn:hover:not(:disabled) {
63475
+ :where(.theme-scope)
63476
+ :where([data-block='event-calendar'])
63477
+ .event-category-btn:hover:not(:disabled) {
63318
63478
  border-color: rgba(var(--tb-primary), 0.5);
63319
63479
  }
63320
63480
 
63321
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn:focus-visible {
63481
+ :where(.theme-scope)
63482
+ :where([data-block='event-calendar'])
63483
+ .event-category-btn:focus-visible {
63322
63484
  outline: 2px solid rgb(var(--tb-primary));
63323
63485
  outline-offset: 2px;
63324
63486
  }
63325
63487
 
63326
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn--active {
63488
+ :where(.theme-scope)
63489
+ :where([data-block='event-calendar'])
63490
+ .event-category-btn--active {
63327
63491
  background-color: rgb(var(--tb-primary));
63328
63492
  border-color: rgb(var(--tb-primary));
63329
63493
  color: var(--ec-event-solid-text);
63330
63494
  }
63331
63495
 
63332
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn:disabled {
63496
+ :where(.theme-scope)
63497
+ :where([data-block='event-calendar'])
63498
+ .event-category-btn:disabled {
63333
63499
  opacity: 0.5;
63334
63500
  cursor: not-allowed;
63335
63501
  }
63336
63502
 
63337
63503
  /* Allow category filter to be wider for button group */
63338
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filter:has(.event-category-buttons) {
63504
+ :where(.theme-scope)
63505
+ :where([data-block='event-calendar'])
63506
+ .event-filter:has(.event-category-buttons) {
63339
63507
  min-width: auto;
63340
63508
  flex: 1 1 auto;
63341
63509
  }
63342
63510
 
63343
63511
  /* Combined mode container */
63344
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-combined {
63512
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-combined {
63345
63513
  width: 100%;
63346
63514
  }
63347
63515
 
63348
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-combined-content {
63516
+ :where(.theme-scope)
63517
+ :where([data-block='event-calendar'])
63518
+ .event-combined-content {
63349
63519
  display: flex;
63350
63520
  flex-direction: column;
63351
63521
  }
63352
63522
 
63353
63523
  /* Calendar view toggle (Month/Week) in Combined mode */
63354
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-view-toggle {
63524
+ :where(.theme-scope)
63525
+ :where([data-block='event-calendar'])
63526
+ .event-calendar-view-toggle {
63355
63527
  display: flex;
63356
63528
  gap: 0.25rem;
63357
63529
  margin-bottom: 1rem;
63358
63530
  }
63359
63531
 
63360
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-view-btn {
63532
+ :where(.theme-scope)
63533
+ :where([data-block='event-calendar'])
63534
+ .event-calendar-view-btn {
63361
63535
  padding: 0.5rem 1rem;
63362
63536
  font-size: 0.875rem;
63363
63537
  font-weight: 500;
@@ -63370,12 +63544,16 @@ const containerResponsiveThemeCss = `/*
63370
63544
  transition: all var(--rb-block-transition) ease;
63371
63545
  }
63372
63546
 
63373
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-view-btn:hover {
63547
+ :where(.theme-scope)
63548
+ :where([data-block='event-calendar'])
63549
+ .event-calendar-view-btn:hover {
63374
63550
  opacity: 1;
63375
63551
  border-color: rgba(var(--tb-primary), 0.5);
63376
63552
  }
63377
63553
 
63378
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-view-btn--active {
63554
+ :where(.theme-scope)
63555
+ :where([data-block='event-calendar'])
63556
+ .event-calendar-view-btn--active {
63379
63557
  opacity: 1;
63380
63558
  background-color: rgb(var(--tb-primary));
63381
63559
  border-color: rgb(var(--tb-primary));
@@ -63388,29 +63566,35 @@ const containerResponsiveThemeCss = `/*
63388
63566
  * ----------------------------------------------------------------------------- */
63389
63567
 
63390
63568
  /* Agenda week container */
63391
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-week {
63569
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-agenda-week {
63392
63570
  display: flex;
63393
63571
  flex-direction: column;
63394
63572
  gap: 0;
63395
63573
  }
63396
63574
 
63397
63575
  /* Individual day section */
63398
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day {
63576
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-agenda-day {
63399
63577
  border-bottom: 1px solid var(--ec-border-subtle);
63400
63578
  }
63401
63579
 
63402
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day:last-child {
63580
+ :where(.theme-scope)
63581
+ :where([data-block='event-calendar'])
63582
+ .event-agenda-day:last-child {
63403
63583
  border-bottom: none;
63404
63584
  }
63405
63585
 
63406
63586
  /* Day header */
63407
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-header {
63587
+ :where(.theme-scope)
63588
+ :where([data-block='event-calendar'])
63589
+ .event-agenda-day-header {
63408
63590
  padding: 0.75rem 0;
63409
63591
  background-color: var(--ec-header-bg);
63410
63592
  border-bottom: 1px solid var(--ec-border-subtle);
63411
63593
  }
63412
63594
 
63413
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-title {
63595
+ :where(.theme-scope)
63596
+ :where([data-block='event-calendar'])
63597
+ .event-agenda-day-title {
63414
63598
  font-size: 0.875rem;
63415
63599
  font-weight: 600;
63416
63600
  margin: 0;
@@ -63420,7 +63604,9 @@ const containerResponsiveThemeCss = `/*
63420
63604
  }
63421
63605
 
63422
63606
  /* Today badge */
63423
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-today-badge {
63607
+ :where(.theme-scope)
63608
+ :where([data-block='event-calendar'])
63609
+ .event-agenda-day-today-badge {
63424
63610
  display: inline-flex;
63425
63611
  align-items: center;
63426
63612
  padding: 0.125rem 0.5rem;
@@ -63434,24 +63620,33 @@ const containerResponsiveThemeCss = `/*
63434
63620
  }
63435
63621
 
63436
63622
  /* Today highlight for entire day section */
63437
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day--today .event-agenda-day-header {
63623
+ :where(.theme-scope)
63624
+ :where([data-block='event-calendar'])
63625
+ .event-agenda-day--today
63626
+ .event-agenda-day-header {
63438
63627
  background-color: var(--ec-today-bg);
63439
63628
  }
63440
63629
 
63441
63630
  /* Day content area */
63442
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-content {
63631
+ :where(.theme-scope)
63632
+ :where([data-block='event-calendar'])
63633
+ .event-agenda-day-content {
63443
63634
  padding: 0.75rem 0;
63444
63635
  }
63445
63636
 
63446
63637
  /* Events list within a day */
63447
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-events {
63638
+ :where(.theme-scope)
63639
+ :where([data-block='event-calendar'])
63640
+ .event-agenda-day-events {
63448
63641
  display: flex;
63449
63642
  flex-direction: column;
63450
63643
  gap: 0.5rem;
63451
63644
  }
63452
63645
 
63453
63646
  /* Empty day message */
63454
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-empty {
63647
+ :where(.theme-scope)
63648
+ :where([data-block='event-calendar'])
63649
+ .event-agenda-day-empty {
63455
63650
  font-size: 0.875rem;
63456
63651
  opacity: var(--ec-empty-opacity);
63457
63652
  margin: 0;
@@ -63463,7 +63658,7 @@ const containerResponsiveThemeCss = `/*
63463
63658
  * Rich event card for agenda view
63464
63659
  * ----------------------------------------------------------------------------- */
63465
63660
 
63466
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card {
63661
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-agenda-card {
63467
63662
  display: flex;
63468
63663
  align-items: flex-start;
63469
63664
  gap: 0.75rem;
@@ -63476,13 +63671,17 @@ const containerResponsiveThemeCss = `/*
63476
63671
  transition: all var(--rb-block-transition) ease;
63477
63672
  }
63478
63673
 
63479
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card:hover {
63674
+ :where(.theme-scope)
63675
+ :where([data-block='event-calendar'])
63676
+ .event-agenda-card:hover {
63480
63677
  border-color: rgba(var(--tb-primary), 0.5);
63481
63678
  background-color: var(--ec-event-bg);
63482
63679
  }
63483
63680
 
63484
63681
  /* Category color indicator */
63485
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-indicator {
63682
+ :where(.theme-scope)
63683
+ :where([data-block='event-calendar'])
63684
+ .event-agenda-card-indicator {
63486
63685
  flex-shrink: 0;
63487
63686
  width: 4px;
63488
63687
  align-self: stretch;
@@ -63491,7 +63690,9 @@ const containerResponsiveThemeCss = `/*
63491
63690
  }
63492
63691
 
63493
63692
  /* Card content wrapper */
63494
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-content {
63693
+ :where(.theme-scope)
63694
+ :where([data-block='event-calendar'])
63695
+ .event-agenda-card-content {
63495
63696
  flex: 1;
63496
63697
  min-width: 0;
63497
63698
  display: flex;
@@ -63500,28 +63701,36 @@ const containerResponsiveThemeCss = `/*
63500
63701
  }
63501
63702
 
63502
63703
  /* Header row: time + title */
63503
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-header {
63704
+ :where(.theme-scope)
63705
+ :where([data-block='event-calendar'])
63706
+ .event-agenda-card-header {
63504
63707
  display: flex;
63505
63708
  align-items: baseline;
63506
63709
  gap: 0.5rem;
63507
63710
  flex-wrap: wrap;
63508
63711
  }
63509
63712
 
63510
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-time {
63713
+ :where(.theme-scope)
63714
+ :where([data-block='event-calendar'])
63715
+ .event-agenda-card-time {
63511
63716
  font-size: 0.75rem;
63512
63717
  font-weight: 600;
63513
63718
  opacity: var(--ec-time-opacity);
63514
63719
  white-space: nowrap;
63515
63720
  }
63516
63721
 
63517
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-title {
63722
+ :where(.theme-scope)
63723
+ :where([data-block='event-calendar'])
63724
+ .event-agenda-card-title {
63518
63725
  font-size: 0.875rem;
63519
63726
  font-weight: 500;
63520
63727
  line-height: 1.3;
63521
63728
  }
63522
63729
 
63523
63730
  /* Details row: venue, price, capacity */
63524
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-details {
63731
+ :where(.theme-scope)
63732
+ :where([data-block='event-calendar'])
63733
+ .event-agenda-card-details {
63525
63734
  display: flex;
63526
63735
  align-items: center;
63527
63736
  gap: 0.75rem;
@@ -63530,26 +63739,36 @@ const containerResponsiveThemeCss = `/*
63530
63739
  opacity: 0.7;
63531
63740
  }
63532
63741
 
63533
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-venue {
63742
+ :where(.theme-scope)
63743
+ :where([data-block='event-calendar'])
63744
+ .event-agenda-card-venue {
63534
63745
  display: inline-flex;
63535
63746
  align-items: center;
63536
63747
  gap: 0.25rem;
63537
63748
  }
63538
63749
 
63539
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-icon {
63750
+ :where(.theme-scope)
63751
+ :where([data-block='event-calendar'])
63752
+ .event-agenda-card-icon {
63540
63753
  flex-shrink: 0;
63541
63754
  opacity: 0.7;
63542
63755
  }
63543
63756
 
63544
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-price {
63757
+ :where(.theme-scope)
63758
+ :where([data-block='event-calendar'])
63759
+ .event-agenda-card-price {
63545
63760
  font-weight: 500;
63546
63761
  }
63547
63762
 
63548
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-capacity {
63763
+ :where(.theme-scope)
63764
+ :where([data-block='event-calendar'])
63765
+ .event-agenda-card-capacity {
63549
63766
  font-weight: 500;
63550
63767
  }
63551
63768
 
63552
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-capacity--full {
63769
+ :where(.theme-scope)
63770
+ :where([data-block='event-calendar'])
63771
+ .event-agenda-card-capacity--full {
63553
63772
  color: rgb(var(--tb-error, 239 68 68));
63554
63773
  opacity: 1;
63555
63774
  }
@@ -63559,20 +63778,24 @@ const containerResponsiveThemeCss = `/*
63559
63778
  * Time-grid layout for recurring class schedules
63560
63779
  * ----------------------------------------------------------------------------- */
63561
63780
 
63562
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable {
63781
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-timetable {
63563
63782
  display: flex;
63564
63783
  flex-direction: column;
63565
63784
  gap: 0.75rem;
63566
63785
  }
63567
63786
 
63568
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-selector {
63787
+ :where(.theme-scope)
63788
+ :where([data-block='event-calendar'])
63789
+ .event-timetable-day-selector {
63569
63790
  display: none;
63570
63791
  gap: 0.375rem;
63571
63792
  overflow-x: auto;
63572
63793
  padding-bottom: 0.125rem;
63573
63794
  }
63574
63795
 
63575
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-tab {
63796
+ :where(.theme-scope)
63797
+ :where([data-block='event-calendar'])
63798
+ .event-timetable-day-tab {
63576
63799
  flex: 0 0 auto;
63577
63800
  padding: 0.5rem 0.75rem;
63578
63801
  border: 1px solid var(--ec-border);
@@ -63587,35 +63810,49 @@ const containerResponsiveThemeCss = `/*
63587
63810
  border-color var(--rb-block-transition) ease;
63588
63811
  }
63589
63812
 
63590
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-tab--active {
63813
+ :where(.theme-scope)
63814
+ :where([data-block='event-calendar'])
63815
+ .event-timetable-day-tab--active {
63591
63816
  background-color: rgb(var(--tb-primary));
63592
63817
  border-color: rgb(var(--tb-primary));
63593
63818
  color: rgb(var(--tb-primaryText, 255 255 255));
63594
63819
  }
63595
63820
 
63596
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-container {
63821
+ :where(.theme-scope)
63822
+ :where([data-block='event-calendar'])
63823
+ .event-timetable-container {
63597
63824
  border: 1px solid var(--ec-border);
63598
63825
  border-radius: var(--rb-block-radius);
63599
63826
  overflow-x: auto;
63600
63827
  overflow-y: hidden;
63601
63828
  }
63602
63829
 
63603
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-grid {
63830
+ :where(.theme-scope)
63831
+ :where([data-block='event-calendar'])
63832
+ .event-timetable-grid {
63604
63833
  display: grid;
63605
63834
  grid-template-columns: 4.5rem repeat(7, minmax(8rem, 1fr));
63606
63835
  min-width: 62rem;
63607
63836
  }
63608
63837
 
63609
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-rail,
63610
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day {
63838
+ :where(.theme-scope)
63839
+ :where([data-block='event-calendar'])
63840
+ .event-timetable-time-rail,
63841
+ :where(.theme-scope)
63842
+ :where([data-block='event-calendar'])
63843
+ .event-timetable-day {
63611
63844
  border-right: 1px solid var(--ec-border-subtle);
63612
63845
  }
63613
63846
 
63614
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day:last-child {
63847
+ :where(.theme-scope)
63848
+ :where([data-block='event-calendar'])
63849
+ .event-timetable-day:last-child {
63615
63850
  border-right: none;
63616
63851
  }
63617
63852
 
63618
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-heading {
63853
+ :where(.theme-scope)
63854
+ :where([data-block='event-calendar'])
63855
+ .event-timetable-day-heading {
63619
63856
  min-height: 3.25rem;
63620
63857
  margin: 0;
63621
63858
  padding: 0.75rem 0.5rem;
@@ -63630,12 +63867,17 @@ const containerResponsiveThemeCss = `/*
63630
63867
  font-weight: 600;
63631
63868
  }
63632
63869
 
63633
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day--today .event-timetable-day-heading {
63870
+ :where(.theme-scope)
63871
+ :where([data-block='event-calendar'])
63872
+ .event-timetable-day--today
63873
+ .event-timetable-day-heading {
63634
63874
  background-color: var(--ec-today-bg);
63635
63875
  color: var(--ec-today-text);
63636
63876
  }
63637
63877
 
63638
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-today-badge {
63878
+ :where(.theme-scope)
63879
+ :where([data-block='event-calendar'])
63880
+ .event-timetable-today-badge {
63639
63881
  padding: 0.125rem 0.375rem;
63640
63882
  border-radius: var(--rb-button-radius);
63641
63883
  background-color: rgb(var(--tb-primary));
@@ -63645,17 +63887,29 @@ const containerResponsiveThemeCss = `/*
63645
63887
  text-transform: uppercase;
63646
63888
  }
63647
63889
 
63648
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-body,
63649
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-body {
63890
+ :where(.theme-scope)
63891
+ :where([data-block='event-calendar'])
63892
+ .event-timetable-time-body,
63893
+ :where(.theme-scope)
63894
+ :where([data-block='event-calendar'])
63895
+ .event-timetable-day-body {
63650
63896
  position: relative;
63651
- height: clamp(34rem, calc(var(--event-timetable-range-minutes, 900) * 0.055rem), 56rem);
63897
+ height: clamp(
63898
+ 34rem,
63899
+ calc(var(--event-timetable-range-minutes, 900) * 0.055rem),
63900
+ 56rem
63901
+ );
63652
63902
  }
63653
63903
 
63654
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-body {
63904
+ :where(.theme-scope)
63905
+ :where([data-block='event-calendar'])
63906
+ .event-timetable-time-body {
63655
63907
  background-color: var(--ec-header-bg);
63656
63908
  }
63657
63909
 
63658
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-label {
63910
+ :where(.theme-scope)
63911
+ :where([data-block='event-calendar'])
63912
+ .event-timetable-time-label {
63659
63913
  position: absolute;
63660
63914
  left: 0.625rem;
63661
63915
  transform: translateY(-50%);
@@ -63664,14 +63918,18 @@ const containerResponsiveThemeCss = `/*
63664
63918
  white-space: nowrap;
63665
63919
  }
63666
63920
 
63667
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-hour-line {
63921
+ :where(.theme-scope)
63922
+ :where([data-block='event-calendar'])
63923
+ .event-timetable-hour-line {
63668
63924
  position: absolute;
63669
63925
  left: 0;
63670
63926
  right: 0;
63671
63927
  border-top: 1px solid var(--ec-border-subtle);
63672
63928
  }
63673
63929
 
63674
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event {
63930
+ :where(.theme-scope)
63931
+ :where([data-block='event-calendar'])
63932
+ .event-timetable-event {
63675
63933
  position: absolute;
63676
63934
  min-height: 2.75rem;
63677
63935
  padding: 0.375rem 0.5rem;
@@ -63691,21 +63949,35 @@ const containerResponsiveThemeCss = `/*
63691
63949
  transform var(--rb-block-transition) ease;
63692
63950
  }
63693
63951
 
63694
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event:hover {
63952
+ :where(.theme-scope)
63953
+ :where([data-block='event-calendar'])
63954
+ .event-timetable-event:hover {
63695
63955
  box-shadow: 0 4px 12px rgba(var(--tb-text), 0.12);
63696
63956
  transform: translateY(-1px);
63697
63957
  }
63698
63958
 
63699
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event:focus-visible {
63959
+ :where(.theme-scope)
63960
+ :where([data-block='event-calendar'])
63961
+ .event-timetable-event:focus-visible {
63700
63962
  outline: 2px solid rgb(var(--tb-primary));
63701
63963
  outline-offset: 2px;
63702
63964
  }
63703
63965
 
63704
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-time,
63705
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-title,
63706
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-teacher,
63707
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-meta,
63708
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-capacity {
63966
+ :where(.theme-scope)
63967
+ :where([data-block='event-calendar'])
63968
+ .event-timetable-event-time,
63969
+ :where(.theme-scope)
63970
+ :where([data-block='event-calendar'])
63971
+ .event-timetable-event-title,
63972
+ :where(.theme-scope)
63973
+ :where([data-block='event-calendar'])
63974
+ .event-timetable-event-teacher,
63975
+ :where(.theme-scope)
63976
+ :where([data-block='event-calendar'])
63977
+ .event-timetable-event-meta,
63978
+ :where(.theme-scope)
63979
+ :where([data-block='event-calendar'])
63980
+ .event-timetable-event-capacity {
63709
63981
  display: block;
63710
63982
  min-width: 0;
63711
63983
  overflow: hidden;
@@ -63713,36 +63985,50 @@ const containerResponsiveThemeCss = `/*
63713
63985
  white-space: nowrap;
63714
63986
  }
63715
63987
 
63716
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-time {
63988
+ :where(.theme-scope)
63989
+ :where([data-block='event-calendar'])
63990
+ .event-timetable-event-time {
63717
63991
  font-size: 0.6875rem;
63718
63992
  font-weight: 700;
63719
63993
  opacity: var(--ec-time-opacity);
63720
63994
  }
63721
63995
 
63722
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-title {
63996
+ :where(.theme-scope)
63997
+ :where([data-block='event-calendar'])
63998
+ .event-timetable-event-title {
63723
63999
  font-size: 0.8125rem;
63724
64000
  font-weight: 700;
63725
64001
  line-height: 1.25;
63726
64002
  }
63727
64003
 
63728
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-teacher,
63729
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-meta {
64004
+ :where(.theme-scope)
64005
+ :where([data-block='event-calendar'])
64006
+ .event-timetable-event-teacher,
64007
+ :where(.theme-scope)
64008
+ :where([data-block='event-calendar'])
64009
+ .event-timetable-event-meta {
63730
64010
  font-size: 0.6875rem;
63731
64011
  opacity: 0.72;
63732
64012
  }
63733
64013
 
63734
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-capacity {
64014
+ :where(.theme-scope)
64015
+ :where([data-block='event-calendar'])
64016
+ .event-timetable-event-capacity {
63735
64017
  font-size: 0.6875rem;
63736
64018
  font-weight: 600;
63737
64019
  opacity: 0.85;
63738
64020
  }
63739
64021
 
63740
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-capacity--full {
64022
+ :where(.theme-scope)
64023
+ :where([data-block='event-calendar'])
64024
+ .event-timetable-event-capacity--full {
63741
64025
  color: rgb(var(--tb-danger, 220 38 38));
63742
64026
  opacity: 1;
63743
64027
  }
63744
64028
 
63745
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-empty {
64029
+ :where(.theme-scope)
64030
+ :where([data-block='event-calendar'])
64031
+ .event-timetable-day-empty {
63746
64032
  margin: 0;
63747
64033
  padding: 1rem 0.5rem;
63748
64034
  text-align: center;
@@ -63750,7 +64036,9 @@ const containerResponsiveThemeCss = `/*
63750
64036
  opacity: var(--ec-empty-opacity);
63751
64037
  }
63752
64038
 
63753
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-empty {
64039
+ :where(.theme-scope)
64040
+ :where([data-block='event-calendar'])
64041
+ .event-timetable-empty {
63754
64042
  margin: 0.25rem 0 0;
63755
64043
  text-align: center;
63756
64044
  font-size: 0.875rem;
@@ -63759,32 +64047,46 @@ const containerResponsiveThemeCss = `/*
63759
64047
 
63760
64048
  @container rb-site (max-width: 767px) {
63761
64049
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
63762
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-selector {
64050
+ :where(.theme-scope)
64051
+ :where([data-block='event-calendar'])
64052
+ .event-timetable-day-selector {
63763
64053
  display: flex;
63764
64054
  }
63765
64055
 
63766
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-container {
64056
+ :where(.theme-scope)
64057
+ :where([data-block='event-calendar'])
64058
+ .event-timetable-container {
63767
64059
  overflow: hidden;
63768
64060
  }
63769
64061
 
63770
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-grid {
64062
+ :where(.theme-scope)
64063
+ :where([data-block='event-calendar'])
64064
+ .event-timetable-grid {
63771
64065
  grid-template-columns: 4rem minmax(0, 1fr);
63772
64066
  min-width: 0;
63773
64067
  }
63774
64068
 
63775
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day[data-selected="false"] {
64069
+ :where(.theme-scope)
64070
+ :where([data-block='event-calendar'])
64071
+ .event-timetable-day[data-selected='false'] {
63776
64072
  display: none;
63777
64073
  }
63778
64074
 
63779
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day {
64075
+ :where(.theme-scope)
64076
+ :where([data-block='event-calendar'])
64077
+ .event-timetable-day {
63780
64078
  border-right: none;
63781
64079
  }
63782
64080
 
63783
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-rail {
64081
+ :where(.theme-scope)
64082
+ :where([data-block='event-calendar'])
64083
+ .event-timetable-time-rail {
63784
64084
  border-right: 1px solid var(--ec-border-subtle);
63785
64085
  }
63786
64086
 
63787
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-heading {
64087
+ :where(.theme-scope)
64088
+ :where([data-block='event-calendar'])
64089
+ .event-timetable-day-heading {
63788
64090
  justify-content: flex-start;
63789
64091
  padding-inline: 0.75rem;
63790
64092
  }
@@ -63797,7 +64099,7 @@ const containerResponsiveThemeCss = `/*
63797
64099
  * three-state availability (available / filling / waitlist), and a footer CTA.
63798
64100
  * ----------------------------------------------------------------------------- */
63799
64101
 
63800
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board {
64102
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-week-board {
63801
64103
  /* Status accent tokens — map the three availability states onto theme colours.
63802
64104
  * available -> secondary, filling -> accent, waitlist -> muted text. */
63803
64105
  --ec-status-available: rgb(var(--tb-secondary));
@@ -63814,17 +64116,20 @@ const containerResponsiveThemeCss = `/*
63814
64116
 
63815
64117
  /* Opt-in raised look: a soft drop shadow so the board lifts off the page.
63816
64118
  * Generic default (no [data-elevated]) stays flat. */
63817
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board[data-elevated] {
64119
+ :where(.theme-scope)
64120
+ :where([data-block='event-calendar'])
64121
+ .event-week-board[data-elevated] {
63818
64122
  box-shadow: 0 14px 34px -24px rgba(var(--tb-text), 0.35);
63819
64123
  }
63820
64124
 
63821
- /* Top bar: title + legend */
63822
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-top {
63823
- display: flex;
64125
+ /* Top bar: authored title + week navigation + legend */
64126
+ :where(.theme-scope)
64127
+ :where([data-block='event-calendar'])
64128
+ .event-week-board-top {
64129
+ display: grid;
64130
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
63824
64131
  align-items: center;
63825
- justify-content: space-between;
63826
64132
  gap: 1rem;
63827
- flex-wrap: wrap;
63828
64133
  padding: 1.1rem 1.5rem;
63829
64134
  /* surfaceAlt gives the header/footer a legible tint against the white day-grid
63830
64135
  * body — the faint \`--ec-header-bg\` (5% text) is invisible on light palettes. */
@@ -63832,7 +64137,10 @@ const containerResponsiveThemeCss = `/*
63832
64137
  border-bottom: 1px solid var(--ec-border-subtle);
63833
64138
  }
63834
64139
 
63835
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-title {
64140
+ :where(.theme-scope)
64141
+ :where([data-block='event-calendar'])
64142
+ .event-week-board-title {
64143
+ grid-column: 1;
63836
64144
  font-size: 0.8125rem;
63837
64145
  font-weight: 600;
63838
64146
  letter-spacing: 0.12em;
@@ -63840,7 +64148,67 @@ const containerResponsiveThemeCss = `/*
63840
64148
  color: rgb(var(--tb-primary));
63841
64149
  }
63842
64150
 
63843
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend {
64151
+ :where(.theme-scope)
64152
+ :where([data-block='event-calendar'])
64153
+ .event-week-board-navigation {
64154
+ grid-column: 2;
64155
+ display: flex;
64156
+ align-items: center;
64157
+ justify-content: center;
64158
+ gap: 0.65rem;
64159
+ }
64160
+
64161
+ :where(.theme-scope)
64162
+ :where([data-block='event-calendar'])
64163
+ .event-week-board-nav-button {
64164
+ display: inline-flex;
64165
+ align-items: center;
64166
+ justify-content: center;
64167
+ width: 2.25rem;
64168
+ height: 2.25rem;
64169
+ padding: 0;
64170
+ border: 1px solid var(--ec-border);
64171
+ border-radius: 999px;
64172
+ background: transparent;
64173
+ color: inherit;
64174
+ cursor: pointer;
64175
+ }
64176
+
64177
+ :where(.theme-scope)
64178
+ :where([data-block='event-calendar'])
64179
+ .event-week-board-nav-button:hover {
64180
+ background-color: var(--ec-hover-bg);
64181
+ }
64182
+
64183
+ :where(.theme-scope)
64184
+ :where([data-block='event-calendar'])
64185
+ .event-week-board-nav-button:focus-visible {
64186
+ outline: 2px solid rgb(var(--tb-primary));
64187
+ outline-offset: 2px;
64188
+ }
64189
+
64190
+ :where(.theme-scope)
64191
+ :where([data-block='event-calendar'])
64192
+ .event-week-board-range {
64193
+ min-width: 12rem;
64194
+ margin: 0;
64195
+ text-align: center;
64196
+ font-size: 0.875rem;
64197
+ font-weight: 600;
64198
+ }
64199
+
64200
+ :where(.theme-scope)
64201
+ :where([data-block='event-calendar'])
64202
+ .event-week-board-loading {
64203
+ font-weight: 400;
64204
+ opacity: 0.65;
64205
+ }
64206
+
64207
+ :where(.theme-scope)
64208
+ :where([data-block='event-calendar'])
64209
+ .event-week-board-legend {
64210
+ grid-column: 3;
64211
+ justify-self: end;
63844
64212
  display: flex;
63845
64213
  align-items: center;
63846
64214
  gap: 1.1rem;
@@ -63850,38 +64218,72 @@ const containerResponsiveThemeCss = `/*
63850
64218
  opacity: 0.75;
63851
64219
  }
63852
64220
 
63853
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-item {
64221
+ :where(.theme-scope)
64222
+ :where([data-block='event-calendar'])
64223
+ .event-week-board-error {
64224
+ display: flex;
64225
+ align-items: center;
64226
+ justify-content: center;
64227
+ gap: 0.75rem;
64228
+ padding: 0.75rem 1.5rem;
64229
+ border-bottom: 1px solid var(--ec-border-subtle);
64230
+ color: rgb(var(--tb-error, var(--tb-text)));
64231
+ }
64232
+
64233
+ :where(.theme-scope)
64234
+ :where([data-block='event-calendar'])
64235
+ .event-week-board-error
64236
+ p {
64237
+ margin: 0;
64238
+ font-size: 0.8125rem;
64239
+ }
64240
+
64241
+ :where(.theme-scope)
64242
+ :where([data-block='event-calendar'])
64243
+ .event-week-board-legend-item {
63854
64244
  display: inline-flex;
63855
64245
  align-items: center;
63856
64246
  gap: 0.4rem;
63857
64247
  }
63858
64248
 
63859
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot {
64249
+ :where(.theme-scope)
64250
+ :where([data-block='event-calendar'])
64251
+ .event-week-board-legend-dot {
63860
64252
  width: 0.55rem;
63861
64253
  height: 0.55rem;
63862
64254
  border-radius: 50%;
63863
64255
  display: inline-block;
63864
64256
  }
63865
64257
 
63866
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot[data-status="available"] {
64258
+ :where(.theme-scope)
64259
+ :where([data-block='event-calendar'])
64260
+ .event-week-board-legend-dot[data-status='available'] {
63867
64261
  background-color: var(--ec-status-available);
63868
64262
  }
63869
64263
 
63870
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot[data-status="filling"] {
64264
+ :where(.theme-scope)
64265
+ :where([data-block='event-calendar'])
64266
+ .event-week-board-legend-dot[data-status='filling'] {
63871
64267
  background-color: var(--ec-status-filling);
63872
64268
  }
63873
64269
 
63874
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot[data-status="waitlist"] {
64270
+ :where(.theme-scope)
64271
+ :where([data-block='event-calendar'])
64272
+ .event-week-board-legend-dot[data-status='waitlist'] {
63875
64273
  background-color: var(--ec-status-waitlist);
63876
64274
  }
63877
64275
 
63878
64276
  /* Day columns grid */
63879
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-grid {
64277
+ :where(.theme-scope)
64278
+ :where([data-block='event-calendar'])
64279
+ .event-week-board-grid {
63880
64280
  display: grid;
63881
64281
  grid-template-columns: repeat(var(--event-week-board-cols), minmax(0, 1fr));
63882
64282
  }
63883
64283
 
63884
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day {
64284
+ :where(.theme-scope)
64285
+ :where([data-block='event-calendar'])
64286
+ .event-week-board-day {
63885
64287
  display: flex;
63886
64288
  flex-direction: column;
63887
64289
  min-height: 15rem;
@@ -63891,11 +64293,15 @@ const containerResponsiveThemeCss = `/*
63891
64293
  border-right: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
63892
64294
  }
63893
64295
 
63894
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day:last-child {
64296
+ :where(.theme-scope)
64297
+ :where([data-block='event-calendar'])
64298
+ .event-week-board-day:last-child {
63895
64299
  border-right: none;
63896
64300
  }
63897
64301
 
63898
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-head {
64302
+ :where(.theme-scope)
64303
+ :where([data-block='event-calendar'])
64304
+ .event-week-board-day-head {
63899
64305
  display: flex;
63900
64306
  align-items: baseline;
63901
64307
  gap: 0.4rem;
@@ -63904,11 +64310,16 @@ const containerResponsiveThemeCss = `/*
63904
64310
  border-bottom: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
63905
64311
  }
63906
64312
 
63907
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day--today .event-week-board-day-head {
64313
+ :where(.theme-scope)
64314
+ :where([data-block='event-calendar'])
64315
+ .event-week-board-day--today
64316
+ .event-week-board-day-head {
63908
64317
  background-color: var(--ec-today-bg);
63909
64318
  }
63910
64319
 
63911
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-abbr {
64320
+ :where(.theme-scope)
64321
+ :where([data-block='event-calendar'])
64322
+ .event-week-board-day-abbr {
63912
64323
  font-size: 0.6875rem;
63913
64324
  font-weight: 600;
63914
64325
  letter-spacing: 0.1em;
@@ -63916,18 +64327,25 @@ const containerResponsiveThemeCss = `/*
63916
64327
  opacity: 0.65;
63917
64328
  }
63918
64329
 
63919
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day--today .event-week-board-day-abbr {
64330
+ :where(.theme-scope)
64331
+ :where([data-block='event-calendar'])
64332
+ .event-week-board-day--today
64333
+ .event-week-board-day-abbr {
63920
64334
  opacity: 1;
63921
64335
  color: var(--ec-today-text);
63922
64336
  }
63923
64337
 
63924
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-num {
64338
+ :where(.theme-scope)
64339
+ :where([data-block='event-calendar'])
64340
+ .event-week-board-day-num {
63925
64341
  font-size: 0.8125rem;
63926
64342
  font-weight: 600;
63927
64343
  opacity: 0.5;
63928
64344
  }
63929
64345
 
63930
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-body {
64346
+ :where(.theme-scope)
64347
+ :where([data-block='event-calendar'])
64348
+ .event-week-board-day-body {
63931
64349
  display: flex;
63932
64350
  flex-direction: column;
63933
64351
  gap: 0.65rem;
@@ -63936,7 +64354,9 @@ const containerResponsiveThemeCss = `/*
63936
64354
  }
63937
64355
 
63938
64356
  /* Class card */
63939
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card {
64357
+ :where(.theme-scope)
64358
+ :where([data-block='event-calendar'])
64359
+ .event-week-board-card {
63940
64360
  display: flex;
63941
64361
  flex-direction: column;
63942
64362
  gap: 0.45rem;
@@ -63956,40 +64376,54 @@ const containerResponsiveThemeCss = `/*
63956
64376
  border-color var(--rb-block-transition) ease;
63957
64377
  }
63958
64378
 
63959
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card[data-status="filling"] {
64379
+ :where(.theme-scope)
64380
+ :where([data-block='event-calendar'])
64381
+ .event-week-board-card[data-status='filling'] {
63960
64382
  border-left-color: var(--ec-status-filling);
63961
64383
  }
63962
64384
 
63963
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card[data-status="waitlist"] {
64385
+ :where(.theme-scope)
64386
+ :where([data-block='event-calendar'])
64387
+ .event-week-board-card[data-status='waitlist'] {
63964
64388
  border-left-color: var(--ec-status-waitlist);
63965
64389
  }
63966
64390
 
63967
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card:hover {
64391
+ :where(.theme-scope)
64392
+ :where([data-block='event-calendar'])
64393
+ .event-week-board-card:hover {
63968
64394
  transform: translateY(-2px);
63969
64395
  box-shadow: 0 6px 16px -8px rgba(var(--tb-text), 0.28);
63970
64396
  border-color: rgba(var(--tb-primary), 0.4);
63971
64397
  }
63972
64398
 
63973
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card:focus-visible {
64399
+ :where(.theme-scope)
64400
+ :where([data-block='event-calendar'])
64401
+ .event-week-board-card:focus-visible {
63974
64402
  outline: 2px solid rgb(var(--tb-primary));
63975
64403
  outline-offset: 2px;
63976
64404
  }
63977
64405
 
63978
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-title {
64406
+ :where(.theme-scope)
64407
+ :where([data-block='event-calendar'])
64408
+ .event-week-board-card-title {
63979
64409
  font-family: var(--font-heading, inherit);
63980
64410
  font-size: 0.9375rem;
63981
64411
  font-weight: 600;
63982
64412
  line-height: 1.2;
63983
64413
  }
63984
64414
 
63985
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-time {
64415
+ :where(.theme-scope)
64416
+ :where([data-block='event-calendar'])
64417
+ .event-week-board-card-time {
63986
64418
  font-size: 0.75rem;
63987
64419
  letter-spacing: 0.01em;
63988
64420
  opacity: 0.7;
63989
64421
  }
63990
64422
 
63991
64423
  /* Capacity pill */
63992
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-pill {
64424
+ :where(.theme-scope)
64425
+ :where([data-block='event-calendar'])
64426
+ .event-week-board-card-pill {
63993
64427
  align-self: flex-start;
63994
64428
  display: inline-flex;
63995
64429
  align-items: center;
@@ -64007,20 +64441,26 @@ const containerResponsiveThemeCss = `/*
64007
64441
  border-color: var(--ec-border);
64008
64442
  }
64009
64443
 
64010
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-pill[data-status="filling"] {
64444
+ :where(.theme-scope)
64445
+ :where([data-block='event-calendar'])
64446
+ .event-week-board-card-pill[data-status='filling'] {
64011
64447
  background-color: rgba(var(--tb-accent), 0.14);
64012
64448
  color: var(--ec-status-filling);
64013
64449
  border-color: rgba(var(--tb-accent), 0.32);
64014
64450
  }
64015
64451
 
64016
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-pill[data-status="waitlist"] {
64452
+ :where(.theme-scope)
64453
+ :where([data-block='event-calendar'])
64454
+ .event-week-board-card-pill[data-status='waitlist'] {
64017
64455
  background-color: transparent;
64018
64456
  color: rgba(var(--tb-text), 0.6);
64019
64457
  border-color: var(--ec-border);
64020
64458
  }
64021
64459
 
64022
64460
  /* Footer */
64023
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-foot {
64461
+ :where(.theme-scope)
64462
+ :where([data-block='event-calendar'])
64463
+ .event-week-board-foot {
64024
64464
  display: flex;
64025
64465
  align-items: center;
64026
64466
  justify-content: space-between;
@@ -64032,7 +64472,9 @@ const containerResponsiveThemeCss = `/*
64032
64472
  border-top: 1px solid var(--ec-border-subtle);
64033
64473
  }
64034
64474
 
64035
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-foot-note {
64475
+ :where(.theme-scope)
64476
+ :where([data-block='event-calendar'])
64477
+ .event-week-board-foot-note {
64036
64478
  margin: 0;
64037
64479
  font-size: 0.875rem;
64038
64480
  opacity: 0.75;
@@ -64041,36 +64483,69 @@ const containerResponsiveThemeCss = `/*
64041
64483
  /* Responsive: collapse day columns to a stacked list on narrow viewports */
64042
64484
  @container rb-site (max-width: 1023px) {
64043
64485
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
64044
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-grid {
64486
+ :where(.theme-scope)
64487
+ :where([data-block='event-calendar'])
64488
+ .event-week-board-top {
64045
64489
  grid-template-columns: 1fr;
64046
64490
  }
64047
64491
 
64048
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day {
64492
+ :where(.theme-scope)
64493
+ :where([data-block='event-calendar'])
64494
+ .event-week-board-title,
64495
+ :where(.theme-scope)
64496
+ :where([data-block='event-calendar'])
64497
+ .event-week-board-navigation,
64498
+ :where(.theme-scope)
64499
+ :where([data-block='event-calendar'])
64500
+ .event-week-board-legend {
64501
+ grid-column: 1;
64502
+ justify-self: center;
64503
+ }
64504
+
64505
+ :where(.theme-scope)
64506
+ :where([data-block='event-calendar'])
64507
+ .event-week-board-grid {
64508
+ grid-template-columns: 1fr;
64509
+ }
64510
+
64511
+ :where(.theme-scope)
64512
+ :where([data-block='event-calendar'])
64513
+ .event-week-board-day {
64049
64514
  min-height: 0;
64050
64515
  border-right: none;
64051
64516
  border-bottom: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
64052
64517
  }
64053
64518
 
64054
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day:last-child {
64519
+ :where(.theme-scope)
64520
+ :where([data-block='event-calendar'])
64521
+ .event-week-board-day:last-child {
64055
64522
  border-bottom: none;
64056
64523
  }
64057
64524
 
64058
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day--empty {
64525
+ :where(.theme-scope)
64526
+ :where([data-block='event-calendar'])
64527
+ .event-week-board-day--empty {
64059
64528
  display: none;
64060
64529
  }
64061
64530
 
64062
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-head {
64531
+ :where(.theme-scope)
64532
+ :where([data-block='event-calendar'])
64533
+ .event-week-board-day-head {
64063
64534
  border-bottom: none;
64064
64535
  padding-bottom: 0.25rem;
64065
64536
  }
64066
64537
 
64067
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-body {
64538
+ :where(.theme-scope)
64539
+ :where([data-block='event-calendar'])
64540
+ .event-week-board-day-body {
64068
64541
  flex-direction: row;
64069
64542
  flex-wrap: wrap;
64070
64543
  padding-top: 0.35rem;
64071
64544
  }
64072
64545
 
64073
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card {
64546
+ :where(.theme-scope)
64547
+ :where([data-block='event-calendar'])
64548
+ .event-week-board-card {
64074
64549
  flex: 1 1 14rem;
64075
64550
  }
64076
64551
  }
@@ -64078,11 +64553,37 @@ const containerResponsiveThemeCss = `/*
64078
64553
 
64079
64554
  @container rb-site (max-width: 639px) {
64080
64555
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
64081
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend {
64556
+ :where(.theme-scope)
64557
+ :where([data-block='event-calendar'])
64558
+ .event-week-board-navigation {
64559
+ display: grid;
64560
+ grid-template-columns: 2.25rem minmax(0, 1fr) 2.25rem;
64561
+ width: 100%;
64562
+ }
64563
+
64564
+ :where(.theme-scope)
64565
+ :where([data-block='event-calendar'])
64566
+ .event-week-board-navigation
64567
+ > :last-child {
64568
+ grid-column: 1 / -1;
64569
+ justify-self: center;
64570
+ }
64571
+
64572
+ :where(.theme-scope)
64573
+ :where([data-block='event-calendar'])
64574
+ .event-week-board-range {
64575
+ min-width: 0;
64576
+ }
64577
+
64578
+ :where(.theme-scope)
64579
+ :where([data-block='event-calendar'])
64580
+ .event-week-board-legend {
64082
64581
  display: none;
64083
64582
  }
64084
64583
 
64085
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card {
64584
+ :where(.theme-scope)
64585
+ :where([data-block='event-calendar'])
64586
+ .event-week-board-card {
64086
64587
  flex: 1 1 100%;
64087
64588
  }
64088
64589
  }
@@ -64099,8 +64600,9 @@ const containerResponsiveThemeCss = `/*
64099
64600
  * Opt-in only: the band paints when the board root carries [data-banded];
64100
64601
  * the generic default is bandless.
64101
64602
  * ----------------------------------------------------------------------------- */
64102
- :where(.theme-scope) [data-slot="section"]:has(.event-calendar--week-board[data-banded])::before {
64103
- content: "";
64603
+ :where(.theme-scope)
64604
+ [data-slot='section']:has(.event-calendar--week-board[data-banded])::before {
64605
+ content: '';
64104
64606
  position: absolute;
64105
64607
  inset: 0;
64106
64608
  z-index: -9;
@@ -64154,7 +64656,7 @@ const containerResponsiveThemeCss = `/*
64154
64656
  * ----------------------------------------------------------------------------- */
64155
64657
 
64156
64658
  /* Modal overlay */
64157
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-overlay {
64659
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-overlay {
64158
64660
  position: fixed;
64159
64661
  inset: 0;
64160
64662
  background: var(--ec-modal-overlay-bg);
@@ -64168,12 +64670,16 @@ const containerResponsiveThemeCss = `/*
64168
64670
  }
64169
64671
 
64170
64672
  @keyframes ec-modal-fade-in {
64171
- from { opacity: 0; }
64172
- to { opacity: 1; }
64673
+ from {
64674
+ opacity: 0;
64675
+ }
64676
+ to {
64677
+ opacity: 1;
64678
+ }
64173
64679
  }
64174
64680
 
64175
64681
  /* Modal dialog */
64176
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal {
64682
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal {
64177
64683
  position: relative;
64178
64684
  background: var(--ec-modal-bg);
64179
64685
  border-radius: var(--rb-block-radius);
@@ -64197,17 +64703,17 @@ const containerResponsiveThemeCss = `/*
64197
64703
  }
64198
64704
 
64199
64705
  /* Event modal - constrained width for card */
64200
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal--event {
64706
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal--event {
64201
64707
  max-width: 26rem;
64202
64708
  }
64203
64709
 
64204
64710
  /* Day modal */
64205
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal--day {
64711
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal--day {
64206
64712
  max-width: 28rem;
64207
64713
  }
64208
64714
 
64209
64715
  /* Close button */
64210
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-close {
64716
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-close {
64211
64717
  position: absolute;
64212
64718
  top: 0.75rem;
64213
64719
  right: 0.75rem;
@@ -64228,31 +64734,35 @@ const containerResponsiveThemeCss = `/*
64228
64734
  z-index: 10;
64229
64735
  }
64230
64736
 
64231
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-close:hover {
64737
+ :where(.theme-scope)
64738
+ :where([data-block='event-calendar'])
64739
+ .ec-modal-close:hover {
64232
64740
  opacity: 1;
64233
64741
  background-color: rgba(var(--tb-text), 0.1);
64234
64742
  }
64235
64743
 
64236
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-close:focus-visible {
64744
+ :where(.theme-scope)
64745
+ :where([data-block='event-calendar'])
64746
+ .ec-modal-close:focus-visible {
64237
64747
  outline: 2px solid rgb(var(--tb-primary));
64238
64748
  outline-offset: 2px;
64239
64749
  }
64240
64750
 
64241
64751
  /* Modal header (for day modal) */
64242
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-header {
64752
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-header {
64243
64753
  padding: 1.5rem 1.5rem 1rem;
64244
64754
  padding-right: 3rem; /* Space for close button */
64245
64755
  border-bottom: 1px solid var(--ec-border);
64246
64756
  }
64247
64757
 
64248
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-title {
64758
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-title {
64249
64759
  font-size: 1.125rem;
64250
64760
  font-weight: 600;
64251
64761
  color: var(--ec-text);
64252
64762
  margin: 0;
64253
64763
  }
64254
64764
 
64255
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-subtitle {
64765
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-subtitle {
64256
64766
  font-size: 0.875rem;
64257
64767
  color: var(--ec-text);
64258
64768
  opacity: 0.7;
@@ -64260,17 +64770,20 @@ const containerResponsiveThemeCss = `/*
64260
64770
  }
64261
64771
 
64262
64772
  /* Modal content */
64263
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-content {
64773
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-content {
64264
64774
  padding: 1rem;
64265
64775
  }
64266
64776
 
64267
64777
  /* Event modal: tighter padding for card */
64268
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal--event .ec-modal-content {
64778
+ :where(.theme-scope)
64779
+ :where([data-block='event-calendar'])
64780
+ .ec-modal--event
64781
+ .ec-modal-content {
64269
64782
  padding: 0.5rem;
64270
64783
  }
64271
64784
 
64272
64785
  /* Day events list */
64273
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-day-events-list {
64786
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-day-events-list {
64274
64787
  display: flex;
64275
64788
  flex-direction: column;
64276
64789
  gap: 0.5rem;
@@ -64278,7 +64791,9 @@ const containerResponsiveThemeCss = `/*
64278
64791
  }
64279
64792
 
64280
64793
  /* AgendaEventCard in button mode inherits most styles, just needs cursor */
64281
- :where(.theme-scope) :where([data-block="event-calendar"]) button.event-agenda-card {
64794
+ :where(.theme-scope)
64795
+ :where([data-block='event-calendar'])
64796
+ button.event-agenda-card {
64282
64797
  cursor: pointer;
64283
64798
  text-align: left;
64284
64799
  font-family: inherit;
@@ -64287,7 +64802,9 @@ const containerResponsiveThemeCss = `/*
64287
64802
  /* Responsive adjustments */
64288
64803
  @container rb-site (max-width: 640px) {
64289
64804
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
64290
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-container {
64805
+ :where(.theme-scope)
64806
+ :where([data-block='event-calendar'])
64807
+ .event-calendar-container {
64291
64808
  margin-left: -1.5rem;
64292
64809
  margin-right: -1.5rem;
64293
64810
  border-left: none;
@@ -64295,28 +64812,38 @@ const containerResponsiveThemeCss = `/*
64295
64812
  border-radius: 0;
64296
64813
  }
64297
64814
 
64298
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-header {
64815
+ :where(.theme-scope)
64816
+ :where([data-block='event-calendar'])
64817
+ .event-calendar-day-header {
64299
64818
  font-size: 0.75rem;
64300
64819
  padding: 0.375rem 0.125rem;
64301
64820
  }
64302
64821
 
64303
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day {
64822
+ :where(.theme-scope)
64823
+ :where([data-block='event-calendar'])
64824
+ .event-calendar-day {
64304
64825
  min-height: 88px;
64305
64826
  padding: 0.125rem;
64306
64827
  }
64307
64828
 
64308
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-title {
64829
+ :where(.theme-scope)
64830
+ :where([data-block='event-calendar'])
64831
+ .event-calendar-title {
64309
64832
  font-size: 0.9375rem;
64310
64833
  min-width: 0;
64311
64834
  }
64312
64835
 
64313
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-number {
64836
+ :where(.theme-scope)
64837
+ :where([data-block='event-calendar'])
64838
+ .event-calendar-day-number {
64314
64839
  font-size: 0.75rem;
64315
64840
  margin-bottom: 0.125rem;
64316
64841
  padding: 0 0.125rem;
64317
64842
  }
64318
64843
 
64319
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event {
64844
+ :where(.theme-scope)
64845
+ :where([data-block='event-calendar'])
64846
+ .event-calendar-event {
64320
64847
  justify-content: center;
64321
64848
  gap: 0.25rem;
64322
64849
  padding: 0.1875rem 0.25rem;
@@ -64324,25 +64851,34 @@ const containerResponsiveThemeCss = `/*
64324
64851
  border-radius: calc(var(--rb-block-radius) / 3);
64325
64852
  }
64326
64853
 
64327
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event-time {
64854
+ :where(.theme-scope)
64855
+ :where([data-block='event-calendar'])
64856
+ .event-calendar-event-time {
64328
64857
  text-align: center;
64329
64858
  font-size: 0.625rem;
64330
64859
  }
64331
64860
 
64332
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event-title {
64861
+ :where(.theme-scope)
64862
+ :where([data-block='event-calendar'])
64863
+ .event-calendar-event-title {
64333
64864
  display: none;
64334
64865
  }
64335
64866
 
64336
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-more {
64867
+ :where(.theme-scope)
64868
+ :where([data-block='event-calendar'])
64869
+ .event-calendar-more {
64337
64870
  padding: 0.125rem 0.25rem;
64338
64871
  font-size: 0.6875rem;
64339
64872
  }
64340
64873
 
64341
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-header > button {
64874
+ :where(.theme-scope)
64875
+ :where([data-block='event-calendar'])
64876
+ .event-calendar-header
64877
+ > button {
64342
64878
  margin-right: 0.25rem;
64343
64879
  }
64344
64880
 
64345
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal {
64881
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal {
64346
64882
  max-width: 100%;
64347
64883
  max-height: 85vh;
64348
64884
  margin: 0 0.5rem;
@@ -69119,6 +69655,9 @@ const MissingBlockOverrideError = MissingBlockOverrideError$1;
69119
69655
  function isOverrideOnlyPageRenderer(props) {
69120
69656
  return props.rendering?.kind === "overrides-only";
69121
69657
  }
69658
+ function isOverrideOnlyEditorPageRenderer(props) {
69659
+ return props.rendering?.kind === "overrides-only";
69660
+ }
69122
69661
  function normalizeSdkCustomBlocks(customBlocks) {
69123
69662
  if (!customBlocks) return void 0;
69124
69663
  return customBlocks.map((block) => {
@@ -69147,16 +69686,21 @@ function toBlocksPageRendererDataContext(dataContext) {
69147
69686
  imageResolveContext: toBlocksImageResolveContextInput(dataContext.imageResolveContext)
69148
69687
  };
69149
69688
  }
69150
- function PageRenderer(props) {
69151
- const blocksSdkConfig = props.sdkConfig ? { customBlocks: normalizeSdkCustomBlocks(props.sdkConfig.customBlocks) } : props.sdkConfig;
69152
- const { rendering: _rendering, theme: _theme, themeTokens: _themeTokens, ...commonProps } = props;
69153
- const commonBlocksProps = {
69154
- ...commonProps,
69689
+ function toBlocksCommonProps(props) {
69690
+ return {
69691
+ page: props.page,
69692
+ runtimeBoundaries: props.runtimeBoundaries,
69155
69693
  dataContext: props.dataContext ? toBlocksPageRendererDataContext(props.dataContext) : void 0,
69694
+ routeMap: props.routeMap,
69695
+ wrapBlock: props.wrapBlock,
69696
+ registry: props.registry,
69156
69697
  blockOverrides: toBlocksBlockOverrides(props.blockOverrides),
69157
- sdkConfig: blocksSdkConfig,
69698
+ sdkConfig: props.sdkConfig ? { customBlocks: normalizeSdkCustomBlocks(props.sdkConfig.customBlocks) } : props.sdkConfig,
69158
69699
  customBlocks: normalizeSdkCustomBlocks(props.customBlocks)
69159
69700
  };
69701
+ }
69702
+ function PageRenderer(props) {
69703
+ const commonBlocksProps = toBlocksCommonProps(props);
69160
69704
  const blocksProps = isOverrideOnlyPageRenderer(props) ? {
69161
69705
  ...commonBlocksProps,
69162
69706
  rendering: { kind: "overrides-only" },
@@ -69168,6 +69712,19 @@ function PageRenderer(props) {
69168
69712
  };
69169
69713
  return React$1.createElement(PageRenderer$1, blocksProps);
69170
69714
  }
69715
+ function EditorPageRenderer(props) {
69716
+ const commonBlocksProps = toBlocksCommonProps(props);
69717
+ const blocksProps = isOverrideOnlyEditorPageRenderer(props) ? {
69718
+ ...commonBlocksProps,
69719
+ rendering: { kind: "overrides-only" },
69720
+ blockOverrides: toBlocksBlockOverrides(props.blockOverrides) ?? {}
69721
+ } : {
69722
+ ...commonBlocksProps,
69723
+ theme: parseBlocksTheme(props.theme),
69724
+ themeTokens: props.themeTokens
69725
+ };
69726
+ return React$1.createElement(EditorPageRenderer$1, blocksProps);
69727
+ }
69171
69728
  //#endregion
69172
69729
  //#region src/rendering/blocks-theme-scope.ts
69173
69730
  function ThemeScope(props) {
@@ -69239,12 +69796,14 @@ function toPageHydrationBootstrapConfig(props) {
69239
69796
  mediaAssets: props.mediaAssets
69240
69797
  };
69241
69798
  }
69799
+ function resolveSdkBookingTimeZone(timeZone) {
69800
+ return timeZone ?? "UTC";
69801
+ }
69242
69802
  function PageContent(props) {
69243
- const { page, runtimeBoundaries, siteId, resolvedData, bookingOfferingPresets, routeMap, pagesByIdentifier, entriesByIdentifier, wrapBlock, registry, usePlaceholders = false, blockOverrides, sdkConfig, customBlocks, supabaseUrl, mediaAssets, apiBaseUrl, portalToken, previewToken, dataContext } = props;
69803
+ const { page, runtimeBoundaries, siteId, resolvedData, bookingOfferingPresets, bookingTimeZone, routeMap, pagesByIdentifier, entriesByIdentifier, wrapBlock, registry, blockOverrides, sdkConfig, customBlocks, supabaseUrl, mediaAssets, apiBaseUrl, portalToken, previewToken, dataContext } = props;
69244
69804
  const rendererCommonProps = {
69245
69805
  page,
69246
69806
  runtimeBoundaries,
69247
- usePlaceholders,
69248
69807
  dataContext: {
69249
69808
  siteId,
69250
69809
  apiBaseUrl,
@@ -69252,6 +69811,7 @@ function PageContent(props) {
69252
69811
  previewToken,
69253
69812
  resolvedData,
69254
69813
  bookingOfferingPresets: bookingOfferingPresets ?? [],
69814
+ bookingTimeZone: resolveSdkBookingTimeZone(bookingTimeZone),
69255
69815
  routes: routeMap,
69256
69816
  pagesByIdentifier,
69257
69817
  entriesByIdentifier,
@@ -69835,233 +70395,6 @@ function requireSdkTheme(theme, siteId, reason = "cms_theme_absent") {
69835
70395
  //#region src/rendering/renderingMode.ts
69836
70396
  const defaultOverrideOnlyRuntime = { kind: "responsive-only" };
69837
70397
  //#endregion
69838
- //#region src/rendering/components/Layout.tsx
69839
- /**
69840
- * Layout component with header and footer
69841
- *
69842
- * Renders site header and footer around content. Fetches site data if not provided.
69843
- */
69844
- function isOverrideOnlyLayout(props) {
69845
- return props.rendering?.kind === "overrides-only";
69846
- }
69847
- var UnsupportedOverrideOnlyLayoutChromeError = class extends Error {
69848
- constructor(surface) {
69849
- super(`Override-only Layout requires a custom or disabled ${surface}; SDK default chrome is unavailable.`);
69850
- this.surface = surface;
69851
- this.code = "unsupported_override_only_layout_chrome";
69852
- this.name = "UnsupportedOverrideOnlyLayoutChromeError";
69853
- }
69854
- };
69855
- async function resolveLayoutSiteData(props) {
69856
- if (props.siteData) return props.siteData;
69857
- if (!props.client) throw new Error("Layout: must provide either siteData or client");
69858
- if (!props.siteId && !props.slug && !props.domain) throw new Error("Layout: must provide siteId, slug, or domain when using client");
69859
- return props.client.getSite({
69860
- id: props.siteId,
69861
- slug: props.slug,
69862
- domain: props.domain
69863
- });
69864
- }
69865
- function renderOverrideOnlyLayout(props, data) {
69866
- if (data.header === true) throw new UnsupportedOverrideOnlyLayoutChromeError("header");
69867
- if (data.footer === true) throw new UnsupportedOverrideOnlyLayoutChromeError("footer");
69868
- return renderWithOverrideOnlyRuntime({
69869
- runtime: props.rendering.runtime ?? defaultOverrideOnlyRuntime,
69870
- children: /* @__PURE__ */ jsxs(Fragment, { children: [
69871
- typeof props.header === "function" ? props.header(data.headerData) : null,
69872
- props.children,
69873
- typeof props.footer === "function" ? props.footer(data.footerData) : null
69874
- ] })
69875
- });
69876
- }
69877
- /**
69878
- * Layout component that wraps content with site header and footer.
69879
- *
69880
- * This is a server/RSC-safe component. The default `@riverbankcms/sdk/rendering`
69881
- * `Page` and `Block` surfaces mount CMS island hydration automatically, while
69882
- * the strict `@riverbankcms/sdk/rendering/server` surface stays bootstrap-free.
69883
- * If a custom host renders hydratable CMS islands through the strict server
69884
- * surface, mount `BlocksIslandsHydrator` from
69885
- * `@riverbankcms/sdk/rendering/islands` explicitly.
69886
- *
69887
- * @example With pre-fetched site data (recommended)
69888
- * ```tsx
69889
- * import { Layout } from '@riverbankcms/sdk/rendering/server';
69890
- * import { Page } from '@riverbankcms/sdk/rendering';
69891
- *
69892
- * const site = await client.getSite({ slug: 'my-site' });
69893
- *
69894
- * <Layout siteData={site}>
69895
- * <Page {...pageData} />
69896
- * </Layout>
69897
- * ```
69898
- *
69899
- * @example With strict server rendering
69900
- * ```tsx
69901
- * import { Layout, Page } from '@riverbankcms/sdk/rendering/server';
69902
- * import { BlocksIslandsHydrator } from '@riverbankcms/sdk/rendering/islands';
69903
- *
69904
- * const site = await client.getSite({ slug: 'my-site' });
69905
- *
69906
- * <Layout siteData={site}>
69907
- * <BlocksIslandsHydrator siteId={site.site.id} supabaseUrl={site.supabaseUrl} />
69908
- * <Page {...pageData} />
69909
- * </Layout>
69910
- * ```
69911
- */
69912
- async function Layout(props) {
69913
- const { children, hasPortalSession = false, previewStage = "published" } = props;
69914
- const header = props.header ?? true;
69915
- const footer = props.footer ?? true;
69916
- const siteData = await resolveLayoutSiteData(props);
69917
- const { site, navigation, layout, routes } = siteData;
69918
- const footerBlocks = siteData.footerBlocks ?? [];
69919
- const navigationOptions = {
69920
- hasPortalSession,
69921
- previewStage,
69922
- pagesByIdentifier: siteData.pagesByIdentifier,
69923
- entriesByIdentifier: siteData.entriesByIdentifier,
69924
- portalEnabled: siteData.portalEnabled ?? false,
69925
- commerceEnabled: siteData.commerceEnabled ?? false,
69926
- checkoutHref: "/checkout"
69927
- };
69928
- const menuViewModel = buildMenu(navigation, routes, navigationOptions);
69929
- const footerMenuViewModel = buildFooterMenu(navigation, routes, navigationOptions);
69930
- const legalFooterMenuViewModel = buildFooterMenuForRole(navigation, routes, "legal-policy", navigationOptions);
69931
- const logoViewModel = buildLogo(layout.logo ?? null, site.title);
69932
- const customHeaderData = {
69933
- menu: menuViewModel,
69934
- logo: logoViewModel,
69935
- site,
69936
- routes
69937
- };
69938
- const customFooterData = {
69939
- menu: menuViewModel,
69940
- footerMenu: footerMenuViewModel,
69941
- legalFooterMenu: legalFooterMenuViewModel,
69942
- logo: logoViewModel,
69943
- site,
69944
- routes
69945
- };
69946
- if (isOverrideOnlyLayout(props)) return renderOverrideOnlyLayout(props, {
69947
- header: props.header ?? true,
69948
- footer: props.footer ?? true,
69949
- headerData: customHeaderData,
69950
- footerData: customFooterData
69951
- });
69952
- const theme = requireSdkTheme(siteData.theme, site.id);
69953
- const builtThemeRuntime = buildThemeRuntime(theme);
69954
- const headerVariant = props.headerVariant;
69955
- const themeRuntime = props.themeRuntime;
69956
- const headerData = {
69957
- ...customHeaderData,
69958
- theme
69959
- };
69960
- const footerData = {
69961
- ...customFooterData,
69962
- theme
69963
- };
69964
- const headerContent = headerVariant ? {
69965
- ...layout.header,
69966
- variant: headerVariant
69967
- } : layout.header;
69968
- const themeWithVariant = headerVariant ? {
69969
- ...theme,
69970
- header: {
69971
- ...theme.header,
69972
- variant: headerVariant
69973
- }
69974
- } : theme;
69975
- const blocksThemeWithVariant = themeWithVariant;
69976
- const blocksTheme = theme;
69977
- let apiBaseUrl;
69978
- try {
69979
- apiBaseUrl = getRiverbankEnv().baseUrl;
69980
- } catch {}
69981
- const sharedViewModelOverrides = {
69982
- $root: {
69983
- siteId: site.id,
69984
- apiBaseUrl,
69985
- previewStage,
69986
- routes,
69987
- pagesByIdentifier: siteData.pagesByIdentifier,
69988
- entriesByIdentifier: siteData.entriesByIdentifier,
69989
- theme: themeWithVariant
69990
- },
69991
- site,
69992
- menu: menuViewModel,
69993
- footerMenu: footerMenuViewModel,
69994
- legalFooterMenu: legalFooterMenuViewModel
69995
- };
69996
- const headerViewModelOverrides = {
69997
- ...sharedViewModelOverrides,
69998
- content: { logo: logoViewModel }
69999
- };
70000
- const footerViewModelOverrides = {
70001
- ...sharedViewModelOverrides,
70002
- content: {
70003
- ...layout.footer,
70004
- logo: logoViewModel
70005
- }
70006
- };
70007
- let headerElement = null;
70008
- if (header === true) headerElement = renderBlock(siteHeaderManifest, headerContent, {
70009
- theme: builtThemeRuntime.tokens,
70010
- themeConfig: blocksThemeWithVariant,
70011
- viewModelOverrides: headerViewModelOverrides
70012
- });
70013
- else if (typeof header === "function") headerElement = header(headerData);
70014
- let footerElement = null;
70015
- if (footer === true) {
70016
- const footerPlan = resolveFooterRenderPlan({
70017
- isSdkSite: Boolean(site.is_sdk_site),
70018
- mode: theme.footer?.mode
70019
- });
70020
- const footerBlocksElement = footerPlan.renderFooterBlocks && footerBlocks.length > 0 ? /* @__PURE__ */ jsx(PageRenderer, {
70021
- theme,
70022
- page: {
70023
- name: "Footer",
70024
- path: "/__footer",
70025
- purpose: "footer",
70026
- blocks: footerBlocks
70027
- },
70028
- routeMap: routes,
70029
- dataContext: {
70030
- previewStage,
70031
- siteId: site.id,
70032
- apiBaseUrl,
70033
- routes,
70034
- pagesByIdentifier: siteData.pagesByIdentifier,
70035
- entriesByIdentifier: siteData.entriesByIdentifier
70036
- },
70037
- sdkConfig: siteData.sdkConfig ?? null
70038
- }) : null;
70039
- if (footerPlan.renderFooterBlocks && !footerBlocksElement && process.env.NODE_ENV !== "production") console.debug("[sdk] Footer mode renders blocks, but footerBlocks is empty/missing", {
70040
- siteId: site.id,
70041
- mode: footerPlan.mode
70042
- });
70043
- const defaultFooterElement = footerPlan.renderDefaultFooter ? renderBlock(siteFooterManifest, layout.footer, {
70044
- theme: builtThemeRuntime.tokens,
70045
- themeConfig: blocksTheme,
70046
- viewModelOverrides: footerViewModelOverrides
70047
- }) : null;
70048
- footerElement = footerPlan.mode === "default+blocks" ? /* @__PURE__ */ jsxs(Fragment, { children: [footerBlocksElement, defaultFooterElement] }) : footerPlan.mode === "blocks" ? footerBlocksElement : footerPlan.mode === "none" ? null : defaultFooterElement;
70049
- } else if (typeof footer === "function") footerElement = footer(footerData);
70050
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
70051
- headerElement,
70052
- children,
70053
- footerElement
70054
- ] });
70055
- return renderWithSdkThemeRuntime({
70056
- theme,
70057
- themeRuntime: planLayoutThemeRuntime({
70058
- themeRuntime,
70059
- usesSdkChrome: header === true || footer === true
70060
- }),
70061
- children: content
70062
- });
70063
- }
70064
- //#endregion
70065
70398
  //#region ../blocks/src/structure/schema.ts
70066
70399
  const BlockKind = z.string();
70067
70400
  const blockSchema = z.object({
@@ -71545,6 +71878,379 @@ function parseBackgroundColorToken(value) {
71545
71878
  if (!token) return null;
71546
71879
  return token ? token : null;
71547
71880
  }
71881
+ //#endregion
71882
+ //#region src/rendering/helpers/siteCustomBlocks.ts
71883
+ /**
71884
+ * Prefer the DB-backed custom block manifest list (`site.customBlocks`) when available,
71885
+ * but fall back to SDK config custom blocks (`site.sdkConfig.customBlocks`) for SDK-mode sites.
71886
+ *
71887
+ * This avoids subtle DX bugs where one source is populated but the renderer/prefetch layer
71888
+ * is looking at the other.
71889
+ */
71890
+ function resolveSiteCustomBlocks(site) {
71891
+ const dbBlocks = site.customBlocks;
71892
+ if (Array.isArray(dbBlocks) && dbBlocks.length > 0) return dbBlocks;
71893
+ const sdkBlocks = site.sdkConfig?.customBlocks;
71894
+ if (Array.isArray(sdkBlocks) && sdkBlocks.length > 0) return sdkBlocks;
71895
+ }
71896
+ //#endregion
71897
+ //#region src/rendering/helpers/pageRuntimeSupport.ts
71898
+ function buildSiteRenderSupport(site, mediaAssets) {
71899
+ const customBlocks = resolveSiteCustomBlocks(site);
71900
+ return {
71901
+ theme: site.theme,
71902
+ site: site.site,
71903
+ sdkConfig: site.sdkConfig ?? null,
71904
+ bookingOfferingPresets: site.bookingOfferingPresets ?? [],
71905
+ bookingTimeZone: site.bookingTimeZone ?? "UTC",
71906
+ ...customBlocks ? { customBlocks } : {},
71907
+ supabaseUrl: site.supabaseUrl,
71908
+ routeMap: site.routes,
71909
+ pagesByIdentifier: site.pagesByIdentifier,
71910
+ entriesByIdentifier: site.entriesByIdentifier,
71911
+ ...mediaAssets ? { mediaAssets } : {}
71912
+ };
71913
+ }
71914
+ function collectMediaAssetSidecar(mediaAssets) {
71915
+ return Array.isArray(mediaAssets) && mediaAssets.length > 0 ? mediaAssets : void 0;
71916
+ }
71917
+ function convertPageResponseBlock(block) {
71918
+ if (!isRecord$8(block)) throw new Error("Invalid block format in page API response");
71919
+ if (typeof block.id !== "string" && block.id !== null) throw new Error(`Invalid block id in page: expected string or null, got ${typeof block.id}`);
71920
+ if (typeof block.kind !== "string") throw new Error(`Invalid block kind in page: expected string, got ${typeof block.kind}`);
71921
+ if (typeof block.purpose !== "string") throw new Error(`Invalid block purpose in page: expected string, got ${typeof block.purpose}`);
71922
+ const admission = readInvalidBlockAdmission(block.admission);
71923
+ return {
71924
+ id: block.id,
71925
+ kind: block.kind,
71926
+ purpose: block.purpose,
71927
+ content: sanitizeSdkRuntimeBlockContent(block.kind, readContentRecord(block.content) ?? {}),
71928
+ draftContent: sanitizeNullableSdkRuntimeBlockContent(block.kind, readNestedContentRecord(block.draftContent)),
71929
+ ...admission ? { admission } : {}
71930
+ };
71931
+ }
71932
+ function readInvalidBlockAdmission(value) {
71933
+ if (!isRecord$8(value) || value.kind !== "invalid" || !isRecord$8(value.error)) return null;
71934
+ if (value.error.kind !== "invalid_manifest_content" && value.error.kind !== "manifest_unavailable") return null;
71935
+ return value;
71936
+ }
71937
+ function buildPageOutline(input) {
71938
+ return {
71939
+ name: input.name,
71940
+ path: input.path ?? input.fallbackPath,
71941
+ purpose: input.purpose,
71942
+ blocks: input.blocks.map(convertPageResponseBlock)
71943
+ };
71944
+ }
71945
+ function buildRenderablePageOutline(input) {
71946
+ const page = buildPageOutline(input);
71947
+ return {
71948
+ ...page,
71949
+ blocks: page.blocks.map((block) => admitSdkRuntimePageBlock(block, input.customBlocks))
71950
+ };
71951
+ }
71952
+ function projectRenderablePageOutline(page) {
71953
+ return {
71954
+ ...page,
71955
+ blocks: page.blocks.flatMap((block) => block.admission.kind === "renderable" ? [{
71956
+ id: block.id,
71957
+ kind: block.kind,
71958
+ purpose: block.purpose,
71959
+ content: block.admission.content,
71960
+ ...block.bindings ? { bindings: block.bindings } : {}
71961
+ }] : [])
71962
+ };
71963
+ }
71964
+ function admitSdkRuntimePageBlock(block, customBlocks) {
71965
+ const base = {
71966
+ id: block.id,
71967
+ kind: block.kind,
71968
+ purpose: block.purpose,
71969
+ ...block.bindings ? { bindings: block.bindings } : {}
71970
+ };
71971
+ const transportedAdmission = readInvalidBlockAdmission(block.admission);
71972
+ if (transportedAdmission) return invalidRenderableBlock(base, transportedAdmission.error);
71973
+ const manifest = resolveSdkBlockManifest(block.kind, customBlocks);
71974
+ if (!manifest) return invalidRenderableBlock(base, { kind: "manifest_unavailable" });
71975
+ const content = block.content ?? {};
71976
+ const renderable = admitTransportedRenderableManifestContent(compileManifestContentContract(manifest), content);
71977
+ if (!renderable.ok) return invalidRenderableBlock(base, {
71978
+ kind: "invalid_manifest_content",
71979
+ error: renderable.error
71980
+ });
71981
+ return {
71982
+ ...base,
71983
+ admission: {
71984
+ kind: "renderable",
71985
+ content: renderable.value
71986
+ }
71987
+ };
71988
+ }
71989
+ function resolveSdkBlockManifest(blockKind, customBlocks) {
71990
+ const customBlock = customBlocks?.find((candidate) => candidate.id === blockKind);
71991
+ return customBlock ? parseFieldDefinitions(customBlock.fields) : getBlockDefinition(blockKind)?.manifest ?? null;
71992
+ }
71993
+ function invalidRenderableBlock(block, error) {
71994
+ return {
71995
+ ...block,
71996
+ admission: {
71997
+ kind: "invalid",
71998
+ error
71999
+ }
72000
+ };
72001
+ }
72002
+ function isRecord$8(value) {
72003
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
72004
+ }
72005
+ function readContentRecord(value) {
72006
+ return isRecord$8(value) ? value : void 0;
72007
+ }
72008
+ function readNestedContentRecord(value) {
72009
+ if (!isRecord$8(value)) return null;
72010
+ return readContentRecord(value.data) ?? null;
72011
+ }
72012
+ function sanitizeSdkRuntimeBlockContent(blockKind, content) {
72013
+ if (blockKind !== "block.newsletter-signup") return content;
72014
+ const sanitized = sanitizeNewsletterSignupPublicContent(content);
72015
+ return isRecord$8(sanitized) ? sanitized : {};
72016
+ }
72017
+ function sanitizeNullableSdkRuntimeBlockContent(blockKind, content) {
72018
+ return content ? sanitizeSdkRuntimeBlockContent(blockKind, content) : null;
72019
+ }
72020
+ //#endregion
72021
+ //#region src/rendering/helpers/footerPageOutline.ts
72022
+ function buildFooterRenderablePageOutline(siteData, blocks) {
72023
+ return buildRenderablePageOutline({
72024
+ name: "Footer",
72025
+ path: "/__footer",
72026
+ fallbackPath: "/__footer",
72027
+ purpose: "footer",
72028
+ blocks,
72029
+ customBlocks: resolveSiteCustomBlocks(siteData)
72030
+ });
72031
+ }
72032
+ //#endregion
72033
+ //#region src/rendering/components/Layout.tsx
72034
+ /**
72035
+ * Layout component with header and footer
72036
+ *
72037
+ * Renders site header and footer around content. Fetches site data if not provided.
72038
+ */
72039
+ function isOverrideOnlyLayout(props) {
72040
+ return props.rendering?.kind === "overrides-only";
72041
+ }
72042
+ var UnsupportedOverrideOnlyLayoutChromeError = class extends Error {
72043
+ constructor(surface) {
72044
+ super(`Override-only Layout requires a custom or disabled ${surface}; SDK default chrome is unavailable.`);
72045
+ this.surface = surface;
72046
+ this.code = "unsupported_override_only_layout_chrome";
72047
+ this.name = "UnsupportedOverrideOnlyLayoutChromeError";
72048
+ }
72049
+ };
72050
+ async function resolveLayoutSiteData(props) {
72051
+ if (props.siteData) return props.siteData;
72052
+ if (!props.client) throw new Error("Layout: must provide either siteData or client");
72053
+ if (!props.siteId && !props.slug && !props.domain) throw new Error("Layout: must provide siteId, slug, or domain when using client");
72054
+ return props.client.getSite({
72055
+ id: props.siteId,
72056
+ slug: props.slug,
72057
+ domain: props.domain
72058
+ });
72059
+ }
72060
+ function renderOverrideOnlyLayout(props, data) {
72061
+ if (data.header === true) throw new UnsupportedOverrideOnlyLayoutChromeError("header");
72062
+ if (data.footer === true) throw new UnsupportedOverrideOnlyLayoutChromeError("footer");
72063
+ return renderWithOverrideOnlyRuntime({
72064
+ runtime: props.rendering.runtime ?? defaultOverrideOnlyRuntime,
72065
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
72066
+ typeof props.header === "function" ? props.header(data.headerData) : null,
72067
+ props.children,
72068
+ typeof props.footer === "function" ? props.footer(data.footerData) : null
72069
+ ] })
72070
+ });
72071
+ }
72072
+ /**
72073
+ * Layout component that wraps content with site header and footer.
72074
+ *
72075
+ * This is a server/RSC-safe component. The default `@riverbankcms/sdk/rendering`
72076
+ * `Page` and `Block` surfaces mount CMS island hydration automatically, while
72077
+ * the strict `@riverbankcms/sdk/rendering/server` surface stays bootstrap-free.
72078
+ * If a custom host renders hydratable CMS islands through the strict server
72079
+ * surface, mount `BlocksIslandsHydrator` from
72080
+ * `@riverbankcms/sdk/rendering/islands` explicitly.
72081
+ *
72082
+ * @example With pre-fetched site data (recommended)
72083
+ * ```tsx
72084
+ * import { Layout } from '@riverbankcms/sdk/rendering/server';
72085
+ * import { Page } from '@riverbankcms/sdk/rendering';
72086
+ *
72087
+ * const site = await client.getSite({ slug: 'my-site' });
72088
+ *
72089
+ * <Layout siteData={site}>
72090
+ * <Page {...pageData} />
72091
+ * </Layout>
72092
+ * ```
72093
+ *
72094
+ * @example With strict server rendering
72095
+ * ```tsx
72096
+ * import { Layout, Page } from '@riverbankcms/sdk/rendering/server';
72097
+ * import { BlocksIslandsHydrator } from '@riverbankcms/sdk/rendering/islands';
72098
+ *
72099
+ * const site = await client.getSite({ slug: 'my-site' });
72100
+ *
72101
+ * <Layout siteData={site}>
72102
+ * <BlocksIslandsHydrator siteId={site.site.id} supabaseUrl={site.supabaseUrl} />
72103
+ * <Page {...pageData} />
72104
+ * </Layout>
72105
+ * ```
72106
+ */
72107
+ async function Layout(props) {
72108
+ const { children, hasPortalSession = false, previewStage = "published" } = props;
72109
+ const header = props.header ?? true;
72110
+ const footer = props.footer ?? true;
72111
+ const siteData = await resolveLayoutSiteData(props);
72112
+ const { site, navigation, layout, routes } = siteData;
72113
+ const footerBlocks = siteData.footerBlocks ?? [];
72114
+ const navigationOptions = {
72115
+ hasPortalSession,
72116
+ previewStage,
72117
+ pagesByIdentifier: siteData.pagesByIdentifier,
72118
+ entriesByIdentifier: siteData.entriesByIdentifier,
72119
+ portalEnabled: siteData.portalEnabled ?? false,
72120
+ commerceEnabled: siteData.commerceEnabled ?? false,
72121
+ checkoutHref: "/checkout"
72122
+ };
72123
+ const menuViewModel = buildMenu(navigation, routes, navigationOptions);
72124
+ const footerMenuViewModel = buildFooterMenu(navigation, routes, navigationOptions);
72125
+ const legalFooterMenuViewModel = buildFooterMenuForRole(navigation, routes, "legal-policy", navigationOptions);
72126
+ const logoViewModel = buildLogo(layout.logo ?? null, site.title);
72127
+ const customHeaderData = {
72128
+ menu: menuViewModel,
72129
+ logo: logoViewModel,
72130
+ site,
72131
+ routes
72132
+ };
72133
+ const customFooterData = {
72134
+ menu: menuViewModel,
72135
+ footerMenu: footerMenuViewModel,
72136
+ legalFooterMenu: legalFooterMenuViewModel,
72137
+ logo: logoViewModel,
72138
+ site,
72139
+ routes
72140
+ };
72141
+ if (isOverrideOnlyLayout(props)) return renderOverrideOnlyLayout(props, {
72142
+ header: props.header ?? true,
72143
+ footer: props.footer ?? true,
72144
+ headerData: customHeaderData,
72145
+ footerData: customFooterData
72146
+ });
72147
+ const theme = requireSdkTheme(siteData.theme, site.id);
72148
+ const builtThemeRuntime = buildThemeRuntime(theme);
72149
+ const headerVariant = props.headerVariant;
72150
+ const themeRuntime = props.themeRuntime;
72151
+ const headerData = {
72152
+ ...customHeaderData,
72153
+ theme
72154
+ };
72155
+ const footerData = {
72156
+ ...customFooterData,
72157
+ theme
72158
+ };
72159
+ const headerContent = headerVariant ? {
72160
+ ...layout.header,
72161
+ variant: headerVariant
72162
+ } : layout.header;
72163
+ const themeWithVariant = headerVariant ? {
72164
+ ...theme,
72165
+ header: {
72166
+ ...theme.header,
72167
+ variant: headerVariant
72168
+ }
72169
+ } : theme;
72170
+ const blocksThemeWithVariant = themeWithVariant;
72171
+ const blocksTheme = theme;
72172
+ let apiBaseUrl;
72173
+ try {
72174
+ apiBaseUrl = getRiverbankEnv().baseUrl;
72175
+ } catch {}
72176
+ const sharedViewModelOverrides = {
72177
+ $root: {
72178
+ siteId: site.id,
72179
+ apiBaseUrl,
72180
+ previewStage,
72181
+ routes,
72182
+ pagesByIdentifier: siteData.pagesByIdentifier,
72183
+ entriesByIdentifier: siteData.entriesByIdentifier,
72184
+ theme: themeWithVariant
72185
+ },
72186
+ site,
72187
+ menu: menuViewModel,
72188
+ footerMenu: footerMenuViewModel,
72189
+ legalFooterMenu: legalFooterMenuViewModel
72190
+ };
72191
+ const headerViewModelOverrides = {
72192
+ ...sharedViewModelOverrides,
72193
+ content: { logo: logoViewModel }
72194
+ };
72195
+ const footerViewModelOverrides = {
72196
+ ...sharedViewModelOverrides,
72197
+ content: {
72198
+ ...layout.footer,
72199
+ logo: logoViewModel
72200
+ }
72201
+ };
72202
+ let headerElement = null;
72203
+ if (header === true) headerElement = renderBlock(siteHeaderManifest, headerContent, {
72204
+ theme: builtThemeRuntime.tokens,
72205
+ themeConfig: blocksThemeWithVariant,
72206
+ viewModelOverrides: headerViewModelOverrides
72207
+ });
72208
+ else if (typeof header === "function") headerElement = header(headerData);
72209
+ let footerElement = null;
72210
+ if (footer === true) {
72211
+ const footerPlan = resolveFooterRenderPlan({
72212
+ isSdkSite: Boolean(site.is_sdk_site),
72213
+ mode: theme.footer?.mode
72214
+ });
72215
+ const footerBlocksElement = footerPlan.renderFooterBlocks && footerBlocks.length > 0 ? /* @__PURE__ */ jsx(PageRenderer, {
72216
+ theme,
72217
+ page: buildFooterRenderablePageOutline(siteData, footerBlocks),
72218
+ routeMap: routes,
72219
+ dataContext: {
72220
+ previewStage,
72221
+ siteId: site.id,
72222
+ apiBaseUrl,
72223
+ routes,
72224
+ pagesByIdentifier: siteData.pagesByIdentifier,
72225
+ entriesByIdentifier: siteData.entriesByIdentifier
72226
+ },
72227
+ sdkConfig: siteData.sdkConfig ?? null
72228
+ }) : null;
72229
+ if (footerPlan.renderFooterBlocks && !footerBlocksElement && process.env.NODE_ENV !== "production") console.debug("[sdk] Footer mode renders blocks, but footerBlocks is empty/missing", {
72230
+ siteId: site.id,
72231
+ mode: footerPlan.mode
72232
+ });
72233
+ const defaultFooterElement = footerPlan.renderDefaultFooter ? renderBlock(siteFooterManifest, layout.footer, {
72234
+ theme: builtThemeRuntime.tokens,
72235
+ themeConfig: blocksTheme,
72236
+ viewModelOverrides: footerViewModelOverrides
72237
+ }) : null;
72238
+ footerElement = footerPlan.mode === "default+blocks" ? /* @__PURE__ */ jsxs(Fragment, { children: [footerBlocksElement, defaultFooterElement] }) : footerPlan.mode === "blocks" ? footerBlocksElement : footerPlan.mode === "none" ? null : defaultFooterElement;
72239
+ } else if (typeof footer === "function") footerElement = footer(footerData);
72240
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
72241
+ headerElement,
72242
+ children,
72243
+ footerElement
72244
+ ] });
72245
+ return renderWithSdkThemeRuntime({
72246
+ theme,
72247
+ themeRuntime: planLayoutThemeRuntime({
72248
+ themeRuntime,
72249
+ usesSdkChrome: header === true || footer === true
72250
+ }),
72251
+ children: content
72252
+ });
72253
+ }
71548
72254
  function blockedDemoMutationPolicy(classification, publicMessage) {
71549
72255
  if (publicMessage.trim() === "") throw new Error(`Demo mutation policy ${classification} requires publicMessage.`);
71550
72256
  return {
@@ -72741,6 +73447,13 @@ const PUBLIC_WHOLE_SERIES_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
72741
73447
  //#endregion
72742
73448
  //#region ../api/src/publicEventEndpointDefinitions.ts
72743
73449
  const PUBLIC_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
73450
+ resolvePublicEventCheckoutReturn: {
73451
+ path: "/public/sites/{siteId}/events/checkout-return",
73452
+ method: "POST",
73453
+ tags: [TAG_TEMPLATES.publicEvents, "event-checkout"],
73454
+ auth: "public",
73455
+ responseKind: "json"
73456
+ },
72744
73457
  getPublicEventRegistrationOptions: {
72745
73458
  path: "/public/sites/{siteId}/events/registration-options",
72746
73459
  method: "POST",
@@ -74002,7 +74715,7 @@ z$1.object({
74002
74715
  });
74003
74716
  //#endregion
74004
74717
  //#region ../api/src/utils/isRecord.ts
74005
- function isRecord$6(value) {
74718
+ function isRecord$5(value) {
74006
74719
  return typeof value === "object" && value !== null && !Array.isArray(value);
74007
74720
  }
74008
74721
  //#endregion
@@ -74249,7 +74962,7 @@ function sanitizeMarks(marks) {
74249
74962
  if (!Array.isArray(marks) || marks.length === 0) return;
74250
74963
  const sanitized = marks.flatMap((mark) => {
74251
74964
  if (mark.type === "bold" || mark.type === "strong" || mark.type === "italic" || mark.type === "em") return [{ type: mark.type }];
74252
- if (mark.type === "link" && isRecord$6(mark.attrs) && typeof mark.attrs.href === "string") {
74965
+ if (mark.type === "link" && isRecord$5(mark.attrs) && typeof mark.attrs.href === "string") {
74253
74966
  const attrs = { href: mark.attrs.href };
74254
74967
  if (typeof mark.attrs.target === "string" && mark.attrs.target.trim().length > 0) attrs.target = mark.attrs.target;
74255
74968
  if (typeof mark.attrs.rel === "string" && mark.attrs.rel.trim().length > 0) attrs.rel = mark.attrs.rel;
@@ -74274,7 +74987,7 @@ function collectText(node) {
74274
74987
  }
74275
74988
  function stableSerialize(value) {
74276
74989
  if (Array.isArray(value)) return `[${value.map(stableSerialize).join(",")}]`;
74277
- if (isRecord$6(value)) return `{${Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableSerialize(entryValue)}`).join(",")}}`;
74990
+ if (isRecord$5(value)) return `{${Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableSerialize(entryValue)}`).join(",")}}`;
74278
74991
  return JSON.stringify(value);
74279
74992
  }
74280
74993
  //#endregion
@@ -74438,7 +75151,7 @@ function collectCartLinkStats(items, depth = 0) {
74438
75151
  return result;
74439
75152
  }
74440
75153
  function isNavigationMenuValidationItem(value) {
74441
- if (!isRecord$6(value)) return false;
75154
+ if (!isRecord$5(value)) return false;
74442
75155
  return typeof value.label === "string";
74443
75156
  }
74444
75157
  //#endregion
@@ -74456,7 +75169,7 @@ function normalizeIdList(value) {
74456
75169
  return result.length > 0 ? result : null;
74457
75170
  }
74458
75171
  function extractNavVisibilityConfig(raw) {
74459
- if (!isRecord$6(raw)) return null;
75172
+ if (!isRecord$5(raw)) return null;
74460
75173
  const modeRaw = raw.mode;
74461
75174
  const mode = modeRaw === "augment" || modeRaw === "restrict" ? modeRaw : null;
74462
75175
  const allowIds = normalizeIdList(raw.allowIds);
@@ -74469,7 +75182,7 @@ function extractNavVisibilityConfig(raw) {
74469
75182
  };
74470
75183
  }
74471
75184
  function asRecord(value) {
74472
- return isRecord$6(value) ? value : null;
75185
+ return isRecord$5(value) ? value : null;
74473
75186
  }
74474
75187
  /**
74475
75188
  * Check if a raw config object has any nav visibility values defined.
@@ -74572,7 +75285,7 @@ const ENTRY_REF_KEYS = [
74572
75285
  "entryId",
74573
75286
  "slug"
74574
75287
  ];
74575
- function isRecord$5(value) {
75288
+ function isRecord$4(value) {
74576
75289
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
74577
75290
  }
74578
75291
  function extractManualEntryRef(item) {
@@ -74580,7 +75293,7 @@ function extractManualEntryRef(item) {
74580
75293
  const trimmed = item.trim();
74581
75294
  return trimmed.length > 0 ? trimmed : null;
74582
75295
  }
74583
- if (!isRecord$5(item)) return null;
75296
+ if (!isRecord$4(item)) return null;
74584
75297
  for (const key of ENTRY_REF_KEYS) {
74585
75298
  const raw = item[key];
74586
75299
  if (typeof raw === "string" && raw.trim().length > 0) return raw.trim();
@@ -75540,80 +76253,6 @@ async function resolveBlockDataWithDeps(deps, input) {
75540
76253
  return deps.mergeLoaderResults(configData, hostResult.data);
75541
76254
  }
75542
76255
  //#endregion
75543
- //#region src/rendering/helpers/siteCustomBlocks.ts
75544
- /**
75545
- * Prefer the DB-backed custom block manifest list (`site.customBlocks`) when available,
75546
- * but fall back to SDK config custom blocks (`site.sdkConfig.customBlocks`) for SDK-mode sites.
75547
- *
75548
- * This avoids subtle DX bugs where one source is populated but the renderer/prefetch layer
75549
- * is looking at the other.
75550
- */
75551
- function resolveSiteCustomBlocks(site) {
75552
- const dbBlocks = site.customBlocks;
75553
- if (Array.isArray(dbBlocks) && dbBlocks.length > 0) return dbBlocks;
75554
- const sdkBlocks = site.sdkConfig?.customBlocks;
75555
- if (Array.isArray(sdkBlocks) && sdkBlocks.length > 0) return sdkBlocks;
75556
- }
75557
- //#endregion
75558
- //#region src/rendering/helpers/pageRuntimeSupport.ts
75559
- function buildSiteRenderSupport(site, mediaAssets) {
75560
- const customBlocks = resolveSiteCustomBlocks(site);
75561
- return {
75562
- theme: site.theme,
75563
- site: site.site,
75564
- sdkConfig: site.sdkConfig ?? null,
75565
- bookingOfferingPresets: site.bookingOfferingPresets ?? [],
75566
- ...customBlocks ? { customBlocks } : {},
75567
- supabaseUrl: site.supabaseUrl,
75568
- routeMap: site.routes,
75569
- pagesByIdentifier: site.pagesByIdentifier,
75570
- entriesByIdentifier: site.entriesByIdentifier,
75571
- ...mediaAssets ? { mediaAssets } : {}
75572
- };
75573
- }
75574
- function collectMediaAssetSidecar(mediaAssets) {
75575
- return Array.isArray(mediaAssets) && mediaAssets.length > 0 ? mediaAssets : void 0;
75576
- }
75577
- function convertPageResponseBlock(block) {
75578
- if (!isRecord$4(block)) throw new Error("Invalid block format in page API response");
75579
- if (typeof block.id !== "string" && block.id !== null) throw new Error(`Invalid block id in page: expected string or null, got ${typeof block.id}`);
75580
- if (typeof block.kind !== "string") throw new Error(`Invalid block kind in page: expected string, got ${typeof block.kind}`);
75581
- if (typeof block.purpose !== "string") throw new Error(`Invalid block purpose in page: expected string, got ${typeof block.purpose}`);
75582
- return {
75583
- id: block.id,
75584
- kind: block.kind,
75585
- purpose: block.purpose,
75586
- content: sanitizeSdkRuntimeBlockContent(block.kind, readContentRecord(block.content) ?? {}),
75587
- draftContent: sanitizeNullableSdkRuntimeBlockContent(block.kind, readNestedContentRecord(block.draftContent))
75588
- };
75589
- }
75590
- function buildPageOutline(input) {
75591
- return {
75592
- name: input.name,
75593
- path: input.path ?? input.fallbackPath,
75594
- purpose: input.purpose,
75595
- blocks: input.blocks.map(convertPageResponseBlock)
75596
- };
75597
- }
75598
- function isRecord$4(value) {
75599
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
75600
- }
75601
- function readContentRecord(value) {
75602
- return isRecord$4(value) ? value : void 0;
75603
- }
75604
- function readNestedContentRecord(value) {
75605
- if (!isRecord$4(value)) return null;
75606
- return readContentRecord(value.data) ?? null;
75607
- }
75608
- function sanitizeSdkRuntimeBlockContent(blockKind, content) {
75609
- if (blockKind !== "block.newsletter-signup") return content;
75610
- const sanitized = sanitizeNewsletterSignupPublicContent(content);
75611
- return isRecord$4(sanitized) ? sanitized : {};
75612
- }
75613
- function sanitizeNullableSdkRuntimeBlockContent(blockKind, content) {
75614
- return content ? sanitizeSdkRuntimeBlockContent(blockKind, content) : null;
75615
- }
75616
- //#endregion
75617
76256
  //#region src/rendering/helpers/loadPage.ts
75618
76257
  /**
75619
76258
  * Server-side helper to fetch all data needed for <Page> component.
@@ -75689,21 +76328,23 @@ async function loadPageWithDeps(deps, params) {
75689
76328
  if ("pageSurface" in pageResponse) throw new Error("This path resolves to a page surface, not a page. Use loadContent() instead, which handles pages, entries, and page surfaces.");
75690
76329
  const { page: pageData } = pageResponse;
75691
76330
  const support = buildSiteRenderSupport(site, collectMediaAssetSidecar(pageResponse.mediaAssets));
75692
- const pageOutline = buildPageOutline({
76331
+ const pageOutline = buildRenderablePageOutline({
75693
76332
  name: pageData.name,
75694
76333
  path: pageData.path,
75695
76334
  fallbackPath: path,
75696
76335
  purpose: pageData.purpose,
75697
- blocks: pageData.blocks
76336
+ blocks: pageData.blocks,
76337
+ customBlocks: support.customBlocks
75698
76338
  });
75699
76339
  const previewStage = preview ? "preview" : "published";
76340
+ const renderTarget = {
76341
+ kind: "page",
76342
+ pageId: pageId ?? pageData.id
76343
+ };
75700
76344
  const resolvedData = await resolveBlockDataWithDeps(deps, {
75701
- page: pageOutline,
76345
+ page: projectRenderablePageOutline(pageOutline),
75702
76346
  siteId,
75703
- renderTarget: {
75704
- kind: "page",
75705
- pageId: pageId ?? pageData.id
75706
- },
76347
+ renderTarget,
75707
76348
  previewStage,
75708
76349
  policy: createBlockDataLoadPolicy(dataLoaderOverrides, normalizeDataLoaderSearchParams(searchParams)),
75709
76350
  client,
@@ -75717,6 +76358,7 @@ async function loadPageWithDeps(deps, params) {
75717
76358
  siteId,
75718
76359
  resolvedData,
75719
76360
  bookingOfferingPresets: support.bookingOfferingPresets,
76361
+ bookingTimeZone: support.bookingTimeZone,
75720
76362
  supabaseUrl: support.supabaseUrl,
75721
76363
  ...support.mediaAssets ? { mediaAssets: support.mediaAssets } : {},
75722
76364
  ...support.customBlocks ? { customBlocks: support.customBlocks } : {},
@@ -77008,6 +77650,7 @@ const DEFAULT_TEMPLATE_DEFINITIONS = {
77008
77650
  {
77009
77651
  blockKind: "block.body-text",
77010
77652
  scope: "entry",
77653
+ content: { body: createRichTextDoc([""]) },
77011
77654
  bindings: EVENT_BODY_TEXT_BINDINGS
77012
77655
  },
77013
77656
  {
@@ -77054,6 +77697,7 @@ const DEFAULT_TEMPLATE_DEFINITIONS = {
77054
77697
  }, {
77055
77698
  blockKind: "block.body-text",
77056
77699
  scope: "entry",
77700
+ content: { body: createRichTextDoc([""]) },
77057
77701
  bindings: TEAM_MEMBER_BODY_TEXT_BINDINGS
77058
77702
  }]
77059
77703
  },
@@ -78212,6 +78856,14 @@ const isRecord = isRecord$27;
78212
78856
  const applyBindingsToContent = applyBindingsToContent$1;
78213
78857
  //#endregion
78214
78858
  //#region src/rendering/helpers/templateContent.ts
78859
+ function admitMaterializedSdkTemplateBlockContent(input) {
78860
+ const baseContent = sanitizeSdkRuntimeBlockContent(input.block.blockKind, input.block.content ?? {});
78861
+ const bindings = input.block.bindings ?? {};
78862
+ const materialized = input.block.scope === "entry" && Object.keys(bindings).length > 0 ? applyBindingsToContent(baseContent, bindings, input.bindingContext) : baseContent;
78863
+ const contract = compileManifestContentContract(input.manifestOrFields);
78864
+ const stored = admitStoredManifestContent(contract, materialized);
78865
+ return stored.ok ? deriveRenderableManifestContent(contract, stored.value, (content) => content) : stored;
78866
+ }
78215
78867
  function createTemplateRenderTarget(source, templateId) {
78216
78868
  switch (source.kind) {
78217
78869
  case "entry-template": return {
@@ -78262,7 +78914,12 @@ async function processTemplatePage(input) {
78262
78914
  templatePage: null,
78263
78915
  resolvedData: {}
78264
78916
  };
78265
- const blocks = template.blocks.flatMap((block) => resolveTemplateBlockSequence(block, input.bindingContext, input.slotContent));
78917
+ const blocks = template.blocks.flatMap((block) => resolveTemplateBlockSequence({
78918
+ block,
78919
+ bindingContext: input.bindingContext,
78920
+ slotContent: input.slotContent,
78921
+ customBlocks: input.customBlocks
78922
+ }));
78266
78923
  if (blocks.length === 0) return {
78267
78924
  templatePage: null,
78268
78925
  resolvedData: {}
@@ -78276,7 +78933,7 @@ async function processTemplatePage(input) {
78276
78933
  return {
78277
78934
  templatePage,
78278
78935
  resolvedData: await resolveBlockDataWithDeps(input.deps, {
78279
- page: templatePage,
78936
+ page: projectRenderablePageOutline(templatePage),
78280
78937
  siteId: input.siteId,
78281
78938
  renderTarget: createTemplateRenderTarget(input.renderTargetSource, template.id),
78282
78939
  previewStage: input.previewStage,
@@ -78286,58 +78943,74 @@ async function processTemplatePage(input) {
78286
78943
  })
78287
78944
  };
78288
78945
  }
78289
- function resolveTemplateBlockSequence(block, bindingContext, slotContent) {
78946
+ function resolveTemplateBlockSequence(input) {
78947
+ const { block } = input;
78290
78948
  const slotKey = readEntrySlotKey(block);
78291
78949
  if (slotKey) return selectBlocksForDeclaredEntrySlot({
78292
78950
  declaredSlotKey: slotKey,
78293
- entrySlots: slotContent ?? []
78294
- }).map(mapEntrySlotBlock);
78951
+ entrySlots: input.slotContent ?? []
78952
+ }).map((slotBlock) => admitEntrySlotBlock(slotBlock, input.customBlocks));
78295
78953
  if (block.blockKind === "system.entry-slot") return [];
78296
- return [resolveTemplateBlock(block, bindingContext)];
78954
+ return [resolveTemplateBlock(block, input.bindingContext, input.customBlocks)];
78297
78955
  }
78298
- function resolveTemplateBlock(block, bindingContext) {
78299
- const baseContent = sanitizeSdkRuntimeBlockContent(block.blockKind, block.content ?? {});
78300
- if (block.scope === "template") return {
78301
- id: block.id,
78302
- kind: block.blockKind,
78303
- purpose: "template-layout",
78304
- content: baseContent
78305
- };
78306
- const bindings = block.bindings ?? {};
78307
- const resolvedContent = Object.keys(bindings).length > 0 ? applyBindingsToContent(baseContent, bindings, bindingContext) : baseContent;
78308
- return {
78956
+ function resolveTemplateBlock(block, bindingContext, customBlocks) {
78957
+ const base = {
78309
78958
  id: block.id,
78310
78959
  kind: block.blockKind,
78311
- purpose: "entry-content",
78312
- content: resolvedContent
78313
- };
78960
+ purpose: block.scope === "template" ? "template-layout" : "entry-content",
78961
+ ...isRecord(block.bindings) ? { bindings: block.bindings } : {}
78962
+ };
78963
+ const manifest = resolveSdkBlockManifest(block.blockKind, customBlocks);
78964
+ if (manifest === null) return invalidRenderableBlock(base, { kind: "manifest_unavailable" });
78965
+ const admission = admitMaterializedSdkTemplateBlockContent({
78966
+ block,
78967
+ bindingContext,
78968
+ manifestOrFields: manifest
78969
+ });
78970
+ return admission.ok ? {
78971
+ ...base,
78972
+ admission: {
78973
+ kind: "renderable",
78974
+ content: admission.value
78975
+ }
78976
+ } : invalidRenderableBlock(base, {
78977
+ kind: "invalid_manifest_content",
78978
+ error: admission.error
78979
+ });
78314
78980
  }
78315
78981
  function readEntrySlotKey(block) {
78316
78982
  if (block.blockKind !== "system.entry-slot" || block.scope !== "template") return null;
78317
78983
  const parsed = parseSlotKey(block.content?.slotKey);
78318
78984
  return parsed.ok ? parsed.value : null;
78319
78985
  }
78320
- function mapEntrySlotBlock(block) {
78321
- const mapped = {
78986
+ function admitEntrySlotBlock(block, customBlocks) {
78987
+ const base = {
78322
78988
  id: block.id ?? block.blockId ?? null,
78323
78989
  kind: block.kind,
78324
78990
  purpose: block.purpose,
78325
- orderIndex: block.orderIndex ?? null,
78326
- content: normalizeEntrySlotBlockContent(block.content),
78327
- draftContent: normalizeNullableEntrySlotContent(block.draftContent),
78328
- publishedContent: normalizeNullableEntrySlotContent(block.publishedContent)
78329
- };
78330
- return block.bindings == null ? mapped : {
78331
- ...mapped,
78332
- bindings: block.bindings
78333
- };
78334
- }
78335
- function normalizeNullableEntrySlotContent(content) {
78336
- return content == null ? null : normalizeEntrySlotBlockContent(content);
78337
- }
78338
- function normalizeEntrySlotBlockContent(content) {
78339
- const value = content ?? {};
78340
- return isRecord(value) ? value : {};
78991
+ ...isRecord(block.bindings) ? { bindings: block.bindings } : {}
78992
+ };
78993
+ const transportedAdmission = readInvalidBlockAdmission(block.admission);
78994
+ if (transportedAdmission) return invalidRenderableBlock(base, transportedAdmission.error);
78995
+ const manifest = resolveSdkBlockManifest(block.kind, customBlocks);
78996
+ if (manifest === null) return invalidRenderableBlock(base, { kind: "manifest_unavailable" });
78997
+ const contract = compileManifestContentContract(manifest);
78998
+ const stored = admitStoredManifestContent(contract, block.content ?? {});
78999
+ if (!stored.ok) return invalidRenderableBlock(base, {
79000
+ kind: "invalid_manifest_content",
79001
+ error: stored.error
79002
+ });
79003
+ const renderable = deriveRenderableManifestContent(contract, stored.value, (content) => content);
79004
+ return renderable.ok ? {
79005
+ ...base,
79006
+ admission: {
79007
+ kind: "renderable",
79008
+ content: renderable.value
79009
+ }
79010
+ } : invalidRenderableBlock(base, {
79011
+ kind: "invalid_manifest_content",
79012
+ error: renderable.error
79013
+ });
78341
79014
  }
78342
79015
  //#endregion
78343
79016
  //#region src/rendering/helpers/loadContent.ts
@@ -78366,6 +79039,7 @@ function getRejectedReason(result) {
78366
79039
  function toTemplateContentRenderSupport(support, siteId) {
78367
79040
  return {
78368
79041
  bookingOfferingPresets: support.bookingOfferingPresets,
79042
+ bookingTimeZone: support.bookingTimeZone,
78369
79043
  ...support.customBlocks ? { customBlocks: support.customBlocks } : {},
78370
79044
  entriesByIdentifier: support.entriesByIdentifier,
78371
79045
  ...support.mediaAssets ? { mediaAssets: support.mediaAssets } : {},
@@ -78508,7 +79182,6 @@ async function loadContentFromContentResponseWithDeps(deps, params) {
78508
79182
  entry,
78509
79183
  templatePage,
78510
79184
  resolvedData,
78511
- bookingOfferingPresets: support.bookingOfferingPresets,
78512
79185
  dataContext: { contentEntry: {
78513
79186
  ...entry.content ?? {},
78514
79187
  id: entry.id,
@@ -78577,7 +79250,6 @@ async function loadContentFromContentResponseWithDeps(deps, params) {
78577
79250
  pageSurface,
78578
79251
  templatePage,
78579
79252
  resolvedData,
78580
- bookingOfferingPresets: support.bookingOfferingPresets,
78581
79253
  dataContext: {
78582
79254
  pageSurface: {
78583
79255
  ...pageSurface.content ?? {},
@@ -78596,15 +79268,16 @@ async function loadContentFromContentResponseWithDeps(deps, params) {
78596
79268
  const pageRecord = pageData;
78597
79269
  const canonicalPagePath = pageData.path ?? pageRecord.path ?? "/";
78598
79270
  const draftPagePath = pageRecord.draftPath ?? pageRecord.path ?? canonicalPagePath;
78599
- const pageOutline = buildPageOutline({
79271
+ const pageOutline = buildRenderablePageOutline({
78600
79272
  name: pageData.name,
78601
79273
  path: canonicalPagePath,
78602
79274
  fallbackPath: "/",
78603
79275
  purpose: pageData.purpose,
78604
- blocks: pageData.blocks
79276
+ blocks: pageData.blocks,
79277
+ customBlocks: support.customBlocks
78605
79278
  });
78606
79279
  const resolvedData = await resolveBlockDataWithDeps(deps, {
78607
- page: pageOutline,
79280
+ page: projectRenderablePageOutline(pageOutline),
78608
79281
  siteId,
78609
79282
  renderTarget: {
78610
79283
  kind: "page",
@@ -78640,6 +79313,7 @@ async function loadContentFromContentResponseWithDeps(deps, params) {
78640
79313
  ...support.customBlocks ? { customBlocks: support.customBlocks } : {},
78641
79314
  resolvedData,
78642
79315
  bookingOfferingPresets: support.bookingOfferingPresets,
79316
+ bookingTimeZone: support.bookingTimeZone,
78643
79317
  site: support.site,
78644
79318
  sdkConfig: support.sdkConfig,
78645
79319
  supabaseUrl: support.supabaseUrl,
@@ -78743,4 +79417,4 @@ function defineBlockOverrideRegistrations(registrations) {
78743
79417
  * ```
78744
79418
  */
78745
79419
  //#endregion
78746
- export { Block, ImagePresets, Layout, MISSING_SDK_THEME_ERROR_CODE, MissingBlockOverrideError, MissingSdkThemeError, Page, PageRenderer, RichText, ThemeScope, UnsupportedOverrideOnlyLayoutChromeError, buildThemeRuntime, defineBlockOverrideRegistrations, defineBlockOverrides, defineLazyBlockOverride, isEntryContent, isPageContent, isPageSurfaceContent, loadContent, loadPage, resolveImageSourceSetWithContext, resolveImageUrl, resolveImageUrlWithContext, toBlocksImageResolveContextInput, toBlocksMediaAssets, toBlocksPageRendererDataContext };
79420
+ export { Block, EditorPageRenderer, ImagePresets, Layout, MISSING_SDK_THEME_ERROR_CODE, MissingBlockOverrideError, MissingSdkThemeError, Page, PageRenderer, RichText, ThemeScope, UnsupportedOverrideOnlyLayoutChromeError, buildThemeRuntime, defineBlockOverrideRegistrations, defineBlockOverrides, defineLazyBlockOverride, isEntryContent, isPageContent, isPageSurfaceContent, loadContent, loadPage, resolveImageSourceSetWithContext, resolveImageUrl, resolveImageUrlWithContext, toBlocksImageResolveContextInput, toBlocksMediaAssets, toBlocksPageRendererDataContext };