@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,59 @@ 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
+ /** Validates raw editor state without granting a stored-content proof. */
14304
+ function validateManifestDraft(contract, data) {
14305
+ const parsed = contract[compiledManifestContentContract].draftSchema.safeParse(data);
14306
+ if (parsed.success) return {
14307
+ kind: "previewable",
14308
+ data,
14309
+ issues: []
14310
+ };
14311
+ const [firstIssue, ...remainingIssues] = zodAdmissionIssues(parsed.error);
14312
+ if (!firstIssue) throw new Error("Draft manifest validation failed without an issue");
14313
+ return {
14314
+ kind: "invalid",
14315
+ data,
14316
+ issues: [firstIssue, ...remainingIssues]
14317
+ };
14319
14318
  }
14320
- function createManifestValidatorFromPlans(plans, options, normalize) {
14319
+ /**
14320
+ * Re-admits renderable content after a transport boundary has erased its
14321
+ * compile-time proof. Unlike write admission, this never canonicalizes or
14322
+ * grants a stored proof, so hydrated media cannot flow back to persistence.
14323
+ */
14324
+ function admitTransportedRenderableManifestContent(contract, content) {
14325
+ const parsed = contract[compiledManifestContentContract].renderSchema.safeParse(content);
14326
+ return parsed.success ? {
14327
+ ok: true,
14328
+ value: content
14329
+ } : {
14330
+ ok: false,
14331
+ error: zodAdmissionError(parsed.error)
14332
+ };
14333
+ }
14334
+ function deriveNormalizedContentZodFromPlans(plans, options, normalize) {
14321
14335
  const isDraft = options.mode === "draft";
14322
14336
  const baseSchema = deriveContentZodFromPlans(plans, options);
14323
14337
  if (isDraft) return z$1.preprocess((content) => preprocessEmptyValues(normalize(plans, content, options)), baseSchema);
@@ -14328,9 +14342,81 @@ function deriveContentZodFromPlans(plans, options) {
14328
14342
  for (const plan of plans) shape[plan.fieldId] = fieldPlanToObjectPropertyZod(plan, options);
14329
14343
  return z$1.object(shape).catchall(z$1.unknown()).passthrough();
14330
14344
  }
14345
+ function deriveStrictStoredZodFromPlans(plans) {
14346
+ const shape = {};
14347
+ for (const plan of plans) shape[plan.fieldId] = fieldPlanToStoredObjectPropertyZod(plan);
14348
+ return z$1.object(shape).catchall(z$1.unknown()).passthrough();
14349
+ }
14331
14350
  function derivePlansFromManifestOrFields(manifestOrFields) {
14332
14351
  return deriveFieldValidationPlans(Array.isArray(manifestOrFields) ? manifestOrFields : manifestOrFields.fields ?? []);
14333
14352
  }
14353
+ function zodAdmissionError(error) {
14354
+ return {
14355
+ kind: "invalid_shape",
14356
+ issues: zodAdmissionIssues(error)
14357
+ };
14358
+ }
14359
+ function zodAdmissionIssues(error) {
14360
+ return error.issues.map((issue) => ({
14361
+ code: issue.code,
14362
+ path: issue.path.map((segment) => typeof segment === "number" ? segment : String(segment)),
14363
+ message: issue.message
14364
+ }));
14365
+ }
14366
+ function findNonPortableMediaPaths(plans, content) {
14367
+ const paths = /* @__PURE__ */ new Set();
14368
+ collectNonPortablePlannedMedia(plans, content, [], paths);
14369
+ return [...paths].sort();
14370
+ }
14371
+ function collectNonPortablePlannedMedia(plans, content, parentPath, paths) {
14372
+ if (!isPlainRecord$1(content)) return;
14373
+ for (const plan of plans) {
14374
+ if (!(plan.fieldId in content)) continue;
14375
+ const value = content[plan.fieldId];
14376
+ const path = [...parentPath, plan.fieldId];
14377
+ switch (plan.kind) {
14378
+ case "media":
14379
+ if (isPlainRecord$1(value) && hasEnvironmentSpecificMediaField(value)) paths.add(formatFieldPath(path));
14380
+ break;
14381
+ case "richText":
14382
+ collectNonPortableRichTextImages(value, path, paths);
14383
+ break;
14384
+ case "group":
14385
+ collectNonPortablePlannedMedia(selectedGroupChildPlans(plan, value), value, path, paths);
14386
+ break;
14387
+ case "repeater":
14388
+ collectNonPortableRepeaterMedia(plan, value, path, paths);
14389
+ break;
14390
+ case "string":
14391
+ case "number":
14392
+ case "boolean":
14393
+ case "inlineText":
14394
+ case "link":
14395
+ case "select":
14396
+ case "passthrough": break;
14397
+ }
14398
+ }
14399
+ }
14400
+ function collectNonPortableRepeaterMedia(plan, value, path, paths) {
14401
+ if (!Array.isArray(value)) return;
14402
+ value.forEach((item, index) => {
14403
+ if (!isPlainRecord$1(item)) return;
14404
+ const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
14405
+ if (childPlans) collectNonPortablePlannedMedia(childPlans, item, [...path, index], paths);
14406
+ });
14407
+ }
14408
+ function collectNonPortableRichTextImages(value, path, paths) {
14409
+ const images = [];
14410
+ walkAndCollectImageAttrs(value, images);
14411
+ const prefix = formatFieldPath(path);
14412
+ for (const reference of images) if (hasEnvironmentSpecificMediaField(reference.attrs)) paths.add(reference.location ? `${prefix}.${reference.location}` : prefix);
14413
+ }
14414
+ function isPlainRecord$1(value) {
14415
+ return typeof value === "object" && value !== null && !Array.isArray(value);
14416
+ }
14417
+ function hasEnvironmentSpecificMediaField(value) {
14418
+ return "assetId" in value || "storageBucket" in value || "storagePath" in value || "src" in value || "mimeType" in value || looksLikeUuid(value.id);
14419
+ }
14334
14420
  //#endregion
14335
14421
  //#region ../blocks/src/system/manifest/variantKey.ts
14336
14422
  /**
@@ -17538,27 +17624,6 @@ function asHeaderStyleId(value) {
17538
17624
  return value;
17539
17625
  }
17540
17626
  //#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
17627
  //#region ../theme-core/src/site-styles/headerLayoutCatalog.ts
17563
17628
  const titleOnlyLogoAccommodation = {
17564
17629
  pattern: "title-only",
@@ -17718,43 +17783,37 @@ const headerStyleCatalog = defineExactCatalog()([
17718
17783
  id: asHeaderStyleId("minimal"),
17719
17784
  label: "Minimal",
17720
17785
  description: "Quiet surface with simple color-change navigation.",
17721
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17722
- navTypography: "plain"
17786
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17723
17787
  },
17724
17788
  {
17725
17789
  id: asHeaderStyleId("fine-line"),
17726
17790
  label: "Seamless",
17727
17791
  description: "Borderless surface with underline hover for a quieter page transition.",
17728
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17729
- navTypography: "plain"
17792
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17730
17793
  },
17731
17794
  {
17732
17795
  id: asHeaderStyleId("soft-bar"),
17733
17796
  label: "Soft Bar",
17734
17797
  description: "Soft tinted surface with gentle pill hover states.",
17735
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17736
- navTypography: "plain"
17798
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17737
17799
  },
17738
17800
  {
17739
17801
  id: asHeaderStyleId("pill-nav"),
17740
17802
  label: "Pill Nav",
17741
17803
  description: "A calm pill navigation rail over a neutral header.",
17742
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17743
- navTypography: "plain"
17804
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17744
17805
  },
17745
17806
  {
17746
17807
  id: asHeaderStyleId("brand-bar"),
17747
17808
  label: "Brand Bar",
17748
17809
  description: "Solid brand surface with high-contrast navigation.",
17749
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17750
- navTypography: "brand-caps"
17810
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17751
17811
  },
17752
17812
  {
17753
17813
  id: asHeaderStyleId("brand-underline"),
17754
17814
  label: "Brand Underline",
17755
17815
  description: "Brand surface with underline-grow navigation.",
17756
- supportedLayoutIds: supportedLayouts(["classic"]),
17757
- navTypography: "brand-caps"
17816
+ supportedLayoutIds: supportedLayouts(["classic"])
17758
17817
  },
17759
17818
  {
17760
17819
  id: asHeaderStyleId("brand-pill"),
@@ -17764,73 +17823,78 @@ const headerStyleCatalog = defineExactCatalog()([
17764
17823
  "classic",
17765
17824
  "centered",
17766
17825
  "floating"
17767
- ]),
17768
- navTypography: "brand-caps"
17826
+ ])
17769
17827
  },
17770
17828
  {
17771
17829
  id: asHeaderStyleId("brand-capsule"),
17772
17830
  label: "Brand Capsule",
17773
17831
  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"
17832
+ supportedLayoutIds: supportedLayouts(["classic"])
17776
17833
  },
17777
17834
  {
17778
17835
  id: asHeaderStyleId("serif-line"),
17779
17836
  label: "Serif Line",
17780
17837
  description: "Typographic logo treatment with understated underline navigation.",
17781
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17782
- navTypography: "heading"
17838
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17783
17839
  },
17784
17840
  {
17785
17841
  id: asHeaderStyleId("flat-tabs"),
17786
17842
  label: "Flat Tabs",
17787
17843
  description: "Flat block hover and active states with a Swiss utility feel.",
17788
- supportedLayoutIds: supportedLayouts(["classic", "centered"]),
17789
- navTypography: "quiet-caps"
17844
+ supportedLayoutIds: supportedLayouts(["classic", "centered"])
17790
17845
  },
17791
17846
  {
17792
17847
  id: asHeaderStyleId("glass"),
17793
17848
  label: "Glass",
17794
17849
  description: "Translucent chrome with frosted navigation backing.",
17795
- supportedLayoutIds: supportedLayouts(["floating"]),
17796
- navTypography: "plain"
17850
+ supportedLayoutIds: supportedLayouts(["floating"])
17797
17851
  },
17798
17852
  {
17799
17853
  id: asHeaderStyleId("minimal-surface"),
17800
17854
  label: "Clear Split",
17801
17855
  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
17856
+ supportedLayoutIds: supportedLayouts(["floating"])
17807
17857
  },
17808
17858
  {
17809
17859
  id: asHeaderStyleId("soft-shell"),
17810
17860
  label: "Soft Shell",
17811
17861
  description: "Floating rounded shell with soft pill navigation states.",
17812
- supportedLayoutIds: supportedLayouts(["floating"]),
17813
- navTypography: "plain"
17862
+ supportedLayoutIds: supportedLayouts(["floating"])
17814
17863
  },
17815
17864
  {
17816
17865
  id: asHeaderStyleId("split-glass"),
17817
17866
  label: "Split Glass",
17818
17867
  description: "Logo floats independently while the navigation sits in a glass rail.",
17819
- supportedLayoutIds: supportedLayouts(["floating"]),
17820
- navTypography: "quiet-caps",
17821
- openingForegroundTarget: "brand-only"
17868
+ supportedLayoutIds: supportedLayouts(["floating"])
17822
17869
  },
17823
17870
  {
17824
17871
  id: asHeaderStyleId("split-pill"),
17825
17872
  label: "Split Pill",
17826
17873
  description: "Logo floats independently with a soft pill-backed navigation rail.",
17827
- supportedLayoutIds: supportedLayouts(["floating"]),
17828
- navTypography: "heading",
17829
- openingForegroundTarget: "brand-only"
17874
+ supportedLayoutIds: supportedLayouts(["floating"])
17830
17875
  }
17831
17876
  ]);
17832
17877
  new Map(headerLayoutCatalog.map((layout) => [layout.id, layout]));
17833
17878
  new Map(headerStyleCatalog.map((style) => [style.id, style]));
17879
+ defineLookOptionGroup({
17880
+ id: "interaction-palette",
17881
+ label: "Hover colour",
17882
+ defaultOptionId: "accent",
17883
+ options: [
17884
+ {
17885
+ id: "accent",
17886
+ label: "Accent"
17887
+ },
17888
+ {
17889
+ id: "primary",
17890
+ label: "Primary"
17891
+ },
17892
+ {
17893
+ id: "secondary",
17894
+ label: "Secondary"
17895
+ }
17896
+ ]
17897
+ });
17834
17898
  function asFooterLookId(value) {
17835
17899
  return value;
17836
17900
  }
@@ -28785,26 +28849,6 @@ const headerLogoTextVisibleTransform = {
28785
28849
  return resolveHeaderBrandDisplay(asPartialObject(value)) !== "logo-only";
28786
28850
  }
28787
28851
  };
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
28852
  const maxWidthClassTransform = {
28809
28853
  id: "layout.maxWidthClass",
28810
28854
  kind: "string",
@@ -28910,7 +28954,6 @@ const headerRootClassTransform = {
28910
28954
  if (isHeaderScrollRuntimeEnabled(content)) classes.push(config.shrink);
28911
28955
  const variant = header?.variant;
28912
28956
  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
28957
  return joinClasses$2(classes);
28915
28958
  }
28916
28959
  };
@@ -29118,7 +29161,6 @@ const layoutTransforms = [
29118
29161
  headerLogoClassTransform,
29119
29162
  headerLogoMediaClassTransform,
29120
29163
  headerLogoTextVisibleTransform,
29121
- classicNavModeTransform,
29122
29164
  footerRootClassTransform,
29123
29165
  headerRootStyleTransform,
29124
29166
  headerSectionBackgroundTransform,
@@ -29521,18 +29563,7 @@ const classicLayout = inline({
29521
29563
  logoRow,
29522
29564
  createNavRow(`rb-ml-auto ${desktopOnlyDisplay("flex")} rb-gap-6`),
29523
29565
  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"));
29566
+ ], when("$root.theme.header.variant", { equals: "classic" }));
29536
29567
  const centeredLayout = stack({
29537
29568
  gap: "md",
29538
29569
  align: "center",
@@ -29597,7 +29628,6 @@ const siteHeaderManifest = createBlockManifest({
29597
29628
  }, [
29598
29629
  mobileHeaderBar,
29599
29630
  classicLayout,
29600
- classicCenteredNavLayout,
29601
29631
  centeredLayout,
29602
29632
  floatingLayout,
29603
29633
  mobileOverlay
@@ -33931,67 +33961,49 @@ const MONTH_NAMES = [
33931
33961
  "November",
33932
33962
  "December"
33933
33963
  ];
33934
- /** Format a date as YYYY-MM-DD using the runtime's local timezone. */
33935
33964
  function formatDateKey(date) {
33936
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
33965
+ return `${String(date.year).padStart(4, "0")}-${String(date.month).padStart(2, "0")}-${String(date.day).padStart(2, "0")}`;
33937
33966
  }
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);
33967
+ function isToday(date, today) {
33968
+ return isSameCalendarDay(date, today);
33945
33969
  }
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();
33970
+ function isSameCalendarDay(left, right) {
33971
+ return left.year === right.year && left.month === right.month && left.day === right.day;
33972
+ }
33973
+ function addDays(date, days) {
33974
+ const result = new Date(Date.UTC(date.year, date.month - 1, date.day + days, 12));
33975
+ return {
33976
+ year: result.getUTCFullYear(),
33977
+ month: result.getUTCMonth() + 1,
33978
+ day: result.getUTCDate()
33979
+ };
33949
33980
  }
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
33981
  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;
33982
+ const firstDay = {
33983
+ year,
33984
+ month,
33985
+ day: 1
33986
+ };
33987
+ const dayOfWeek = getDayOfWeek(firstDay);
33988
+ const gridStart = addDays(firstDay, -(startOfWeek === "monday" ? dayOfWeek === 0 ? 6 : dayOfWeek - 1 : dayOfWeek));
33989
+ return Array.from({ length: 42 }, (_, index) => addDays(gridStart, index));
33967
33990
  }
33968
- /** Build the 7 dates of the week containing `baseDate`. */
33969
33991
  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;
33992
+ const dayOfWeek = getDayOfWeek(baseDate);
33993
+ const weekStart = addDays(baseDate, -(startOfWeek === "monday" ? dayOfWeek === 0 ? 6 : dayOfWeek - 1 : dayOfWeek));
33994
+ return Array.from({ length: 7 }, (_, index) => addDays(weekStart, index));
33995
+ }
33996
+ function getDayOfWeek(date) {
33997
+ return new Date(Date.UTC(date.year, date.month - 1, date.day, 12)).getUTCDay();
33981
33998
  }
33982
- /** Build the 7 dates of the week containing `baseDate` as a fixed-width tuple. */
33983
33999
  function getWeekCalendarDayTuple(baseDate, startOfWeek) {
33984
34000
  const days = getWeekCalendarDays(baseDate, startOfWeek);
33985
34001
  assertWeekCalendarDays(days);
33986
34002
  return days;
33987
34003
  }
33988
34004
  function assertWeekCalendarDays(days) {
33989
- if (days.length !== 7) throw new Error("Expected week calendar helper to return exactly 7 days");
34005
+ if (days.length !== 7) throw new Error("Expected exactly 7 calendar days");
33990
34006
  }
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
34007
  function getOrderedDayNames(startOfWeek, dayNames = DAY_NAMES) {
33996
34008
  if (startOfWeek === "monday") {
33997
34009
  const sunday = dayNames[0] ?? "Sun";
@@ -33999,22 +34011,17 @@ function getOrderedDayNames(startOfWeek, dayNames = DAY_NAMES) {
33999
34011
  }
34000
34012
  return [...dayNames];
34001
34013
  }
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
34014
  function formatCalendarTitle(viewDate, view, days, monthNames = MONTH_NAMES) {
34008
34015
  if (view === "week") {
34009
34016
  const weekStart = days[0];
34010
34017
  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()}`;
34018
+ if (weekStart === void 0 || weekEnd === void 0) return `${monthNames[viewDate.month - 1] ?? ""} ${viewDate.year}`;
34019
+ const startMonth = monthNames[weekStart.month - 1] ?? "";
34020
+ const endMonth = monthNames[weekEnd.month - 1] ?? "";
34021
+ if (weekStart.month === weekEnd.month) return `${startMonth} ${weekStart.day} - ${weekEnd.day}, ${weekEnd.year}`;
34022
+ return `${startMonth} ${weekStart.day} - ${endMonth} ${weekEnd.day}, ${weekEnd.year}`;
34016
34023
  }
34017
- return `${monthNames[viewDate.getMonth()] ?? ""} ${viewDate.getFullYear()}`;
34024
+ return `${monthNames[viewDate.month - 1] ?? ""} ${viewDate.year}`;
34018
34025
  }
34019
34026
  function buildCalendarViewport(input) {
34020
34027
  const monthNames = input.monthNames ?? MONTH_NAMES;
@@ -34024,10 +34031,10 @@ function buildCalendarViewport(input) {
34024
34031
  kind: "week",
34025
34032
  anchorDate: input.anchorDate,
34026
34033
  days,
34027
- title: formatCalendarTitle(input.anchorDate, "week", [...days], monthNames)
34034
+ title: formatCalendarTitle(input.anchorDate, "week", days, monthNames)
34028
34035
  };
34029
34036
  }
34030
- const days = getMonthCalendarDays(input.anchorDate.getFullYear(), input.anchorDate.getMonth(), input.startOfWeek);
34037
+ const days = getMonthCalendarDays(input.anchorDate.year, input.anchorDate.month, input.startOfWeek);
34031
34038
  return {
34032
34039
  kind: "month",
34033
34040
  anchorDate: input.anchorDate,
@@ -34175,13 +34182,12 @@ function buildTimetableModel$1(input) {
34175
34182
  const bounds = resolveBounds(input.bounds);
34176
34183
  const viewportDays = getTimetableDays(input.viewDate, input.view ?? "week", input.startOfWeek);
34177
34184
  const viewportKeys = new Set(viewportDays.map(formatDateKey));
34178
- const now = input.now ?? /* @__PURE__ */ new Date();
34179
34185
  const parsed = parseAllItems(input.items, input);
34180
34186
  const inViewport = deriveVisibleSegments(parsed.drafts, viewportKeys);
34181
34187
  const hourRange = resolveHourRange(inViewport, bounds);
34182
34188
  const laid = layoutCellsByDate(inViewport, hourRange, bounds);
34183
34189
  return {
34184
- days: assembleDays(viewportDays, laid.cellsByDate, now),
34190
+ days: assembleDays(viewportDays, laid.cellsByDate, input.today),
34185
34191
  hourTicks: buildHourTicks(hourRange.startHour, hourRange.endHour),
34186
34192
  startHour: hourRange.startHour,
34187
34193
  endHour: hourRange.endHour,
@@ -34238,13 +34244,13 @@ function layoutCellsByDate(segments, hourRange, bounds) {
34238
34244
  diagnostics
34239
34245
  };
34240
34246
  }
34241
- function assembleDays(viewportDays, cellsByDate, now) {
34247
+ function assembleDays(viewportDays, cellsByDate, today) {
34242
34248
  return viewportDays.map((date) => {
34243
34249
  const dateKey = formatDateKey(date);
34244
34250
  return {
34245
34251
  date,
34246
34252
  dateKey,
34247
- isToday: isSameLocalDay(date, now),
34253
+ isToday: isSameCalendarDay(date, today),
34248
34254
  cells: cellsByDate.get(dateKey) ?? []
34249
34255
  };
34250
34256
  });
@@ -34396,13 +34402,12 @@ function getTimetableDays(viewDate, view, startOfWeek) {
34396
34402
  function enumerateDateKeys(startDateKey, endDateKey) {
34397
34403
  if (startDateKey > endDateKey) throw new Error(`Invalid timetable date range: ${startDateKey} is after ${endDateKey}`);
34398
34404
  const start = parseDateKey(startDateKey);
34399
- const end = parseDateKey(endDateKey);
34405
+ parseDateKey(endDateKey);
34400
34406
  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()) {
34407
+ let cursor = start;
34408
+ while (formatDateKey(cursor) <= endDateKey) {
34404
34409
  keys.push(formatDateKey(cursor));
34405
- cursor.setDate(cursor.getDate() + 1);
34410
+ cursor = addDays(cursor, 1);
34406
34411
  }
34407
34412
  return keys;
34408
34413
  }
@@ -34627,10 +34632,10 @@ function formatEventTeacherLine(event) {
34627
34632
  * timezone). Thin domain wrapper over `groupItemsByDate` from the calendar
34628
34633
  * package; preserves the historical `Map<string, PublicEvent[]>` shape.
34629
34634
  */
34630
- function groupEventsByDate(events) {
34635
+ function groupEventsByDate(events, calendarTimeZone) {
34631
34636
  const grouped = groupItemsByDate(events, {
34632
34637
  getStartsAt: (event) => event.startsAt,
34633
- getTimeZone: (event) => event.timeZone ?? "UTC"
34638
+ getTimeZone: () => calendarTimeZone
34634
34639
  });
34635
34640
  const map = /* @__PURE__ */ new Map();
34636
34641
  for (const [dateKey, bucket] of grouped) map.set(dateKey, [...bucket]);
@@ -36416,7 +36421,8 @@ const eventCollectionSourceSchemaShape = {
36416
36421
  buttonVariant: eventButtonVariantSchema.optional(),
36417
36422
  buttonText: z$1.string().optional(),
36418
36423
  emptyMessage: z$1.string().optional(),
36419
- className: z$1.string().optional()
36424
+ className: z$1.string().optional(),
36425
+ bookingTimeZone: z$1.string().optional()
36420
36426
  };
36421
36427
  const eventCollectionDisplaySchemaShape = {
36422
36428
  events: z$1.array(publicEventSchema),
@@ -36451,7 +36457,15 @@ const eventCollectionHydrationSchema = z$1.object({
36451
36457
  prefetchedEvents: z$1.boolean(),
36452
36458
  eventSurface: eventSurfaceScopeSchema$1,
36453
36459
  filterEventPresets: z$1.string().nullable(),
36454
- scheduleScope: eventScheduleScopeSchema$1
36460
+ scheduleScope: eventScheduleScopeSchema$1,
36461
+ calendarContext: z$1.object({
36462
+ timeZone: z$1.string().transform(toBookingTimeZone),
36463
+ initialToday: z$1.object({
36464
+ year: z$1.number().int(),
36465
+ month: z$1.number().int().min(1).max(12),
36466
+ day: z$1.number().int().min(1).max(31)
36467
+ })
36468
+ })
36455
36469
  });
36456
36470
  function buildEventCollectionDisplay(source) {
36457
36471
  return {
@@ -36496,14 +36510,19 @@ function buildEventCollectionPresentationDisplay(source) {
36496
36510
  emptyMessage: source.emptyMessage ?? "No upcoming events scheduled."
36497
36511
  };
36498
36512
  }
36499
- function buildEventCollectionHydration(source) {
36513
+ function buildEventCollectionHydration(source, now = /* @__PURE__ */ new Date()) {
36514
+ const timeZone = toBookingTimeZone(source.bookingTimeZone);
36500
36515
  return {
36501
36516
  siteId: source.siteId,
36502
36517
  stage: source.stage,
36503
36518
  prefetchedEvents: source.events != null,
36504
36519
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
36505
36520
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
36506
- scheduleScope: normalizeEventScheduleScope(source.scheduleScope)
36521
+ scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
36522
+ calendarContext: {
36523
+ timeZone,
36524
+ initialToday: getLocalScheduleDate(now, timeZone)
36525
+ }
36507
36526
  };
36508
36527
  }
36509
36528
  function buildEventCollectionListingDisplay(display, prefetchedEvents, className) {
@@ -42267,6 +42286,23 @@ function deriveNewsletterSignupRuntimeSource(values) {
42267
42286
  tags: parseTrimmedString$1(normalized.tags)
42268
42287
  };
42269
42288
  }
42289
+ function sanitizeNewsletterSignupPublicContent(content) {
42290
+ if (!isRecord$18(content)) return content;
42291
+ const provider = parseNewsletterSignupDestinationProvider(content.destinationProvider) ?? getProviderFromExplicitDestination(content.destination);
42292
+ const { destination: _destination, destinationProvider: _destinationProvider, mailchimpAudienceId: _mailchimpAudienceId, mailchimpTags: _mailchimpTags, listId, tags, ...publicContent } = content;
42293
+ const mutablePublicContent = { ...publicContent };
42294
+ if (provider !== "mailchimp") {
42295
+ if ("listId" in content) mutablePublicContent.listId = listId;
42296
+ if ("tags" in content) mutablePublicContent.tags = tags;
42297
+ }
42298
+ return mutablePublicContent;
42299
+ }
42300
+ function getProviderFromExplicitDestination(destination) {
42301
+ if (!isRecord$18(destination) || typeof destination.tag !== "string") return null;
42302
+ if (destination.tag === "riverbank_list") return "riverbank";
42303
+ if (destination.tag === "mailchimp_audience") return "mailchimp";
42304
+ return null;
42305
+ }
42270
42306
  const BLOCK_ID_PATTERN = /^[A-Za-z0-9._:-]{1,200}$/;
42271
42307
  const SIGNED_DESTINATION_REF_PATTERN = /^[A-Za-z0-9._~-]{16,1000}$/;
42272
42308
  function parseTrimmedString$1(value) {
@@ -42274,6 +42310,9 @@ function parseTrimmedString$1(value) {
42274
42310
  const trimmed = value.trim();
42275
42311
  return trimmed.length > 0 ? trimmed : null;
42276
42312
  }
42313
+ function isRecord$18(value) {
42314
+ return typeof value === "object" && value !== null;
42315
+ }
42277
42316
  //#endregion
42278
42317
  //#region ../blocks/src/system/runtime/nodes/newsletter-form.types.ts
42279
42318
  /**
@@ -46777,7 +46816,8 @@ function isOrdinaryEvent(event) {
46777
46816
  function deriveWholeSeriesPhase(sessions, nowIso) {
46778
46817
  const now = Date.parse(nowIso);
46779
46818
  if (now < Date.parse(sessions[0].startsAt)) return "upcoming";
46780
- return now <= Date.parse(sessions.at(-1)?.endsAt ?? sessions[0].endsAt) ? "inProgress" : "completed";
46819
+ const finalSession = sessions[sessions.length - 1] ?? sessions[0];
46820
+ return now <= Date.parse(finalSession.endsAt) ? "inProgress" : "completed";
46781
46821
  }
46782
46822
  function selectRelatedDates(input) {
46783
46823
  if (!input.seriesId) return [];
@@ -48876,9 +48916,9 @@ const FULL_DAY_NAMES$1 = [
48876
48916
  * @returns "Monday, January 13"
48877
48917
  */
48878
48918
  function formatAgendaDayHeader(date) {
48879
- const dayName = FULL_DAY_NAMES$1[date.getDay()];
48880
- const monthName = MONTH_NAMES[date.getMonth()];
48881
- return `${dayName ?? "Day"}, ${monthName ?? "Month"} ${date.getDate()}`;
48919
+ const dayName = FULL_DAY_NAMES$1[getDayOfWeek(date)];
48920
+ const monthName = MONTH_NAMES[date.month - 1];
48921
+ return `${dayName ?? "Day"}, ${monthName ?? "Month"} ${date.day}`;
48882
48922
  }
48883
48923
  /**
48884
48924
  * WeekAgendaView Component
@@ -48886,14 +48926,14 @@ function formatAgendaDayHeader(date) {
48886
48926
  * Displays a week of events in an agenda/list style with rich event details.
48887
48927
  * Shows all 7 days with date headers, empty days show "No events scheduled".
48888
48928
  */
48889
- const WeekAgendaView = ({ days, events, showCapacity, emptyMessage = "No events scheduled" }) => {
48890
- const eventsByDate = groupEventsByDate(events);
48929
+ const WeekAgendaView = ({ days, events, showCapacity, emptyMessage = "No events scheduled", calendarTimeZone, today: currentDate }) => {
48930
+ const eventsByDate = groupEventsByDate(events, calendarTimeZone);
48891
48931
  return /* @__PURE__ */ jsx("div", {
48892
48932
  className: "event-agenda-week",
48893
48933
  children: days.map((date) => {
48894
48934
  const dateKey = formatDateKey(date);
48895
48935
  const dayEvents = eventsByDate.get(dateKey) || [];
48896
- const today = isToday(date);
48936
+ const today = isToday(date, currentDate);
48897
48937
  return /* @__PURE__ */ jsxs("div", {
48898
48938
  className: `event-agenda-day ${today ? "event-agenda-day--today" : ""}`,
48899
48939
  children: [/* @__PURE__ */ jsx("div", {
@@ -48922,6 +48962,50 @@ const WeekAgendaView = ({ days, events, showCapacity, emptyMessage = "No events
48922
48962
  })
48923
48963
  });
48924
48964
  };
48965
+ //#endregion
48966
+ //#region ../blocks/src/system/runtime/nodes/events/shared/CalendarNavigationChevron.tsx
48967
+ function CalendarNavigationChevron({ direction }) {
48968
+ return /* @__PURE__ */ jsx("svg", {
48969
+ width: "20",
48970
+ height: "20",
48971
+ fill: "none",
48972
+ stroke: "currentColor",
48973
+ strokeWidth: 2,
48974
+ viewBox: "0 0 24 24",
48975
+ "aria-hidden": "true",
48976
+ children: /* @__PURE__ */ jsx("path", {
48977
+ strokeLinecap: "round",
48978
+ strokeLinejoin: "round",
48979
+ d: direction === "previous" ? "M15 19l-7-7 7-7" : "M9 5l7 7-7 7"
48980
+ })
48981
+ });
48982
+ }
48983
+ //#endregion
48984
+ //#region ../blocks/src/system/runtime/nodes/events/shared/WeekBoardView.tsx
48985
+ function buildWeekBoardNavigationState(viewport, today) {
48986
+ return {
48987
+ rangeLabel: viewport.title,
48988
+ isCurrentWeek: viewport.days.some((day) => isToday(day, today))
48989
+ };
48990
+ }
48991
+ function buildWeekBoardViewProps(display, days, navigation, events = display.events) {
48992
+ return {
48993
+ days,
48994
+ events: [...events],
48995
+ navigation,
48996
+ showCapacity: display.showCapacity,
48997
+ emptyMessage: display.emptyMessage,
48998
+ title: display.boardTitle,
48999
+ footerNote: display.footerNote,
49000
+ ctaLabel: display.loadMoreText ?? display.buttonText,
49001
+ ctaHref: display.seeAllUrl,
49002
+ buttonVariant: display.buttonVariant,
49003
+ elevated: display.elevated,
49004
+ banded: display.banded,
49005
+ calendarTimeZone: display.calendarTimeZone,
49006
+ today: display.today
49007
+ };
49008
+ }
48925
49009
  const SHORT_DAY_NAMES = [
48926
49010
  "Sun",
48927
49011
  "Mon",
@@ -48983,6 +49067,114 @@ const WeekBoardCard = ({ event, showCapacity }) => {
48983
49067
  ]
48984
49068
  });
48985
49069
  };
49070
+ const WeekBoardNavigationControls = ({ navigation, todayClassName }) => {
49071
+ const handlers = navigation.kind === "interactive" ? navigation : {
49072
+ onPrevious: void 0,
49073
+ onNext: void 0,
49074
+ onToday: void 0
49075
+ };
49076
+ const isLoading = navigation.kind === "interactive" && navigation.loadState.kind === "loading";
49077
+ return /* @__PURE__ */ jsxs("nav", {
49078
+ className: "event-week-board-navigation",
49079
+ "aria-label": "Choose week",
49080
+ children: [
49081
+ /* @__PURE__ */ jsx("button", {
49082
+ type: "button",
49083
+ className: "event-week-board-nav-button",
49084
+ "aria-label": "Previous week",
49085
+ onClick: handlers.onPrevious,
49086
+ children: /* @__PURE__ */ jsx(CalendarNavigationChevron, { direction: "previous" })
49087
+ }),
49088
+ /* @__PURE__ */ jsxs("h3", {
49089
+ className: "event-week-board-range",
49090
+ "aria-live": "polite",
49091
+ children: [navigation.rangeLabel, isLoading ? /* @__PURE__ */ jsx("span", {
49092
+ className: "event-week-board-loading",
49093
+ children: " Loading…"
49094
+ }) : null]
49095
+ }),
49096
+ /* @__PURE__ */ jsx("button", {
49097
+ type: "button",
49098
+ className: "event-week-board-nav-button",
49099
+ "aria-label": "Next week",
49100
+ onClick: handlers.onNext,
49101
+ children: /* @__PURE__ */ jsx(CalendarNavigationChevron, { direction: "next" })
49102
+ }),
49103
+ /* @__PURE__ */ jsx("button", {
49104
+ type: "button",
49105
+ className: todayClassName,
49106
+ disabled: navigation.isCurrentWeek,
49107
+ onClick: handlers.onToday,
49108
+ children: "Today"
49109
+ })
49110
+ ]
49111
+ });
49112
+ };
49113
+ const WeekBoardLegend = () => /* @__PURE__ */ jsx("div", {
49114
+ className: "event-week-board-legend",
49115
+ "aria-hidden": "true",
49116
+ children: LEGEND_ITEMS.map((item) => /* @__PURE__ */ jsxs("span", {
49117
+ className: "event-week-board-legend-item",
49118
+ children: [/* @__PURE__ */ jsx("i", {
49119
+ className: "event-week-board-legend-dot",
49120
+ "data-status": item.status
49121
+ }), item.label]
49122
+ }, item.status))
49123
+ });
49124
+ const WeekBoardLoadError = ({ navigation, buttonClassName }) => {
49125
+ if (navigation.kind !== "interactive" || navigation.loadState.kind !== "error") return null;
49126
+ return /* @__PURE__ */ jsxs("div", {
49127
+ className: "event-week-board-error",
49128
+ role: "status",
49129
+ children: [/* @__PURE__ */ jsx("p", { children: "Failed to load events" }), /* @__PURE__ */ jsx("button", {
49130
+ type: "button",
49131
+ className: buttonClassName,
49132
+ onClick: navigation.loadState.onRetry,
49133
+ children: "Try again"
49134
+ })]
49135
+ });
49136
+ };
49137
+ const WeekBoardGrid = ({ days, eventsByDate, showCapacity, today: currentDate }) => /* @__PURE__ */ jsx("div", {
49138
+ className: "event-week-board-grid",
49139
+ children: days.map((date) => {
49140
+ const dateKey = formatDateKey(date);
49141
+ const dayEvents = eventsByDate.get(dateKey) ?? [];
49142
+ const today = isToday(date, currentDate);
49143
+ return /* @__PURE__ */ jsxs("div", {
49144
+ className: clsx("event-week-board-day", dayEvents.length === 0 && "event-week-board-day--empty", today && "event-week-board-day--today"),
49145
+ children: [/* @__PURE__ */ jsxs("div", {
49146
+ className: "event-week-board-day-head",
49147
+ children: [/* @__PURE__ */ jsx("span", {
49148
+ className: "event-week-board-day-abbr",
49149
+ children: SHORT_DAY_NAMES[getDayOfWeek(date)]
49150
+ }), /* @__PURE__ */ jsx("span", {
49151
+ className: "event-week-board-day-num",
49152
+ children: date.day
49153
+ })]
49154
+ }), /* @__PURE__ */ jsx("div", {
49155
+ className: "event-week-board-day-body",
49156
+ children: dayEvents.map((event) => /* @__PURE__ */ jsx(WeekBoardCard, {
49157
+ event,
49158
+ showCapacity
49159
+ }, event.occurrenceId || event.id))
49160
+ })]
49161
+ }, dateKey);
49162
+ })
49163
+ });
49164
+ const WeekBoardFooter = ({ note, ctaHref, ctaLabel, ctaClassName }) => {
49165
+ if (note === null && ctaHref === null) return null;
49166
+ return /* @__PURE__ */ jsxs("div", {
49167
+ className: "event-week-board-foot",
49168
+ children: [note === null ? null : /* @__PURE__ */ jsx("p", {
49169
+ className: "event-week-board-foot-note",
49170
+ children: note
49171
+ }), ctaHref === null ? null : /* @__PURE__ */ jsx("a", {
49172
+ className: ctaClassName,
49173
+ href: ctaHref,
49174
+ children: ctaLabel
49175
+ })]
49176
+ });
49177
+ };
48986
49178
  /**
48987
49179
  * WeekBoardView
48988
49180
  *
@@ -48991,18 +49183,21 @@ const WeekBoardCard = ({ event, showCapacity }) => {
48991
49183
  * to a stacked list on mobile. Repurposes the week-grouped agenda data as a
48992
49184
  * board rather than an hour-positioned timetable grid.
48993
49185
  */
48994
- 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 }) => {
48995
- const eventsByDate = groupEventsByDate(events);
49186
+ 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 }) => {
49187
+ const eventsByDate = groupEventsByDate(events, calendarTimeZone);
48996
49188
  const hasAnyEvents = days.some((date) => (eventsByDate.get(formatDateKey(date)) ?? []).length > 0);
48997
49189
  const ctaClassName = themeButtonClassName({
48998
49190
  variant: buttonVariant,
48999
49191
  size: "sm"
49000
49192
  });
49193
+ const todayClassName = themeButtonClassName({
49194
+ variant: buttonVariant,
49195
+ size: "sm"
49196
+ });
49001
49197
  const hasTitle = typeof title === "string" && title.length > 0;
49002
49198
  const hasFooterNote = typeof footerNote === "string" && footerNote.length > 0;
49003
- const hasCta = typeof ctaHref === "string" && ctaHref.length > 0;
49004
49199
  const footerNoteText = hasAnyEvents ? hasFooterNote ? footerNote : null : emptyMessage;
49005
- const showFooter = hasCta || footerNoteText !== null;
49200
+ const resolvedCtaHref = typeof ctaHref === "string" && ctaHref.length > 0 ? ctaHref : null;
49006
49201
  return /* @__PURE__ */ jsx("div", {
49007
49202
  className: "event-calendar event-calendar--week-board",
49008
49203
  "data-block": "event-calendar",
@@ -49014,59 +49209,34 @@ const WeekBoardView = ({ days, events, showCapacity, emptyMessage = "No classes
49014
49209
  children: [
49015
49210
  /* @__PURE__ */ jsxs("div", {
49016
49211
  className: "event-week-board-top",
49017
- children: [hasTitle ? /* @__PURE__ */ jsx("span", {
49018
- className: "event-week-board-title",
49019
- children: title
49020
- }) : null, /* @__PURE__ */ jsx("div", {
49021
- className: "event-week-board-legend",
49022
- "aria-hidden": "true",
49023
- children: LEGEND_ITEMS.map((item) => /* @__PURE__ */ jsxs("span", {
49024
- className: "event-week-board-legend-item",
49025
- children: [/* @__PURE__ */ jsx("i", {
49026
- className: "event-week-board-legend-dot",
49027
- "data-status": item.status
49028
- }), item.label]
49029
- }, item.status))
49030
- })]
49212
+ children: [
49213
+ hasTitle ? /* @__PURE__ */ jsx("span", {
49214
+ className: "event-week-board-title",
49215
+ children: title
49216
+ }) : null,
49217
+ /* @__PURE__ */ jsx(WeekBoardNavigationControls, {
49218
+ navigation,
49219
+ todayClassName
49220
+ }),
49221
+ /* @__PURE__ */ jsx(WeekBoardLegend, {})
49222
+ ]
49031
49223
  }),
49032
- /* @__PURE__ */ jsx("div", {
49033
- className: "event-week-board-grid",
49034
- children: days.map((date) => {
49035
- const dateKey = formatDateKey(date);
49036
- const dayEvents = eventsByDate.get(dateKey) ?? [];
49037
- const today = isToday(date);
49038
- return /* @__PURE__ */ jsxs("div", {
49039
- className: clsx("event-week-board-day", dayEvents.length === 0 && "event-week-board-day--empty", today && "event-week-board-day--today"),
49040
- children: [/* @__PURE__ */ jsxs("div", {
49041
- className: "event-week-board-day-head",
49042
- children: [/* @__PURE__ */ jsx("span", {
49043
- className: "event-week-board-day-abbr",
49044
- children: SHORT_DAY_NAMES[date.getDay()]
49045
- }), /* @__PURE__ */ jsx("span", {
49046
- className: "event-week-board-day-num",
49047
- children: date.getDate()
49048
- })]
49049
- }), /* @__PURE__ */ jsx("div", {
49050
- className: "event-week-board-day-body",
49051
- children: dayEvents.map((event) => /* @__PURE__ */ jsx(WeekBoardCard, {
49052
- event,
49053
- showCapacity
49054
- }, event.occurrenceId || event.id))
49055
- })]
49056
- }, dateKey);
49057
- })
49224
+ /* @__PURE__ */ jsx(WeekBoardLoadError, {
49225
+ navigation,
49226
+ buttonClassName: todayClassName
49058
49227
  }),
49059
- showFooter ? /* @__PURE__ */ jsxs("div", {
49060
- className: "event-week-board-foot",
49061
- children: [footerNoteText !== null ? /* @__PURE__ */ jsx("p", {
49062
- className: "event-week-board-foot-note",
49063
- children: footerNoteText
49064
- }) : null, hasCta ? /* @__PURE__ */ jsx("a", {
49065
- className: ctaClassName,
49066
- href: ctaHref ?? void 0,
49067
- children: ctaLabel
49068
- }) : null]
49069
- }) : null
49228
+ /* @__PURE__ */ jsx(WeekBoardGrid, {
49229
+ days,
49230
+ eventsByDate,
49231
+ showCapacity,
49232
+ today
49233
+ }),
49234
+ /* @__PURE__ */ jsx(WeekBoardFooter, {
49235
+ note: footerNoteText,
49236
+ ctaHref: resolvedCtaHref,
49237
+ ctaLabel,
49238
+ ctaClassName
49239
+ })
49070
49240
  ]
49071
49241
  })
49072
49242
  });
@@ -49097,6 +49267,7 @@ function buildTimetableModel(input) {
49097
49267
  items: input.events,
49098
49268
  viewDate: input.viewDate,
49099
49269
  startOfWeek: input.startOfWeek,
49270
+ today: input.today,
49100
49271
  getId: (event) => event.id,
49101
49272
  getStartsAt: (event) => event.startsAt,
49102
49273
  getEndsAt: (event) => event.endsAt,
@@ -49158,10 +49329,10 @@ function formatEventTimeRange(event) {
49158
49329
  return `${formatTime(event.startsAt, { timeZone })} - ${formatTime(event.endsAt, { timeZone })}`;
49159
49330
  }
49160
49331
  function formatDayLabel(date) {
49161
- return `${FULL_DAY_NAMES[date.getDay()] ?? ""}, ${MONTH_NAMES[date.getMonth()] ?? ""} ${date.getDate()}`;
49332
+ return `${FULL_DAY_NAMES[getDayOfWeek(date)] ?? ""}, ${MONTH_NAMES[date.month - 1] ?? ""} ${date.day}`;
49162
49333
  }
49163
49334
  function formatShortDayLabel(date) {
49164
- return `${(FULL_DAY_NAMES[date.getDay()] ?? "").slice(0, 3)} ${date.getDate()}`;
49335
+ return `${(FULL_DAY_NAMES[getDayOfWeek(date)] ?? "").slice(0, 3)} ${date.day}`;
49165
49336
  }
49166
49337
  //#endregion
49167
49338
  //#region ../blocks/src/system/runtime/nodes/events/EventCalendar.server.tsx
@@ -49208,15 +49379,15 @@ function renderListSsr(display, prefetchedEvents) {
49208
49379
  renderEventCard: (event) => renderEventCard(event, listDisplay)
49209
49380
  });
49210
49381
  }
49211
- function renderCalendarSsr(display) {
49382
+ function renderCalendarSsr(display, hydration) {
49212
49383
  const calendarView = display.defaultCalendarView;
49213
- const viewDate = /* @__PURE__ */ new Date();
49384
+ const viewDate = hydration.calendarContext.initialToday;
49214
49385
  const viewport = buildCalendarViewport({
49215
49386
  view: calendarView,
49216
49387
  anchorDate: viewDate,
49217
49388
  startOfWeek: display.startOfWeek
49218
49389
  });
49219
- const eventsByDate = groupEventsByDate([...display.events]);
49390
+ const eventsByDate = groupEventsByDate([...display.events], hydration.calendarContext.timeZone);
49220
49391
  const dayNames = getOrderedDayNames(display.startOfWeek);
49221
49392
  return /* @__PURE__ */ jsxs("div", {
49222
49393
  className: clsx("event-calendar", display.className ?? void 0),
@@ -49283,7 +49454,9 @@ function renderCalendarSsr(display) {
49283
49454
  days: viewport.days,
49284
49455
  events: [...display.events],
49285
49456
  emptyMessage: display.emptyMessage,
49286
- showCapacity: display.showCapacity
49457
+ showCapacity: display.showCapacity,
49458
+ calendarTimeZone: hydration.calendarContext.timeZone,
49459
+ today: hydration.calendarContext.initialToday
49287
49460
  }) : /* @__PURE__ */ jsxs("div", {
49288
49461
  className: "event-calendar-container",
49289
49462
  children: [/* @__PURE__ */ jsx("div", {
@@ -49297,13 +49470,13 @@ function renderCalendarSsr(display) {
49297
49470
  children: viewport.days.map((date) => {
49298
49471
  const dateKey = formatDateKey(date);
49299
49472
  const dayEvents = eventsByDate.get(dateKey) || [];
49300
- const isCurrentMonth = date.getMonth() === viewDate.getMonth();
49301
- const today = isToday(date);
49473
+ const isCurrentMonth = date.month === viewDate.month;
49474
+ const today = isToday(date, hydration.calendarContext.initialToday);
49302
49475
  return /* @__PURE__ */ jsxs("div", {
49303
49476
  className: clsx("event-calendar-day", !isCurrentMonth && "event-calendar-day--other-month", today && "event-calendar-day--today"),
49304
49477
  children: [/* @__PURE__ */ jsx("div", {
49305
49478
  className: "event-calendar-day-number",
49306
- children: date.getDate()
49479
+ children: date.day
49307
49480
  }), /* @__PURE__ */ jsxs("div", {
49308
49481
  className: "event-calendar-events",
49309
49482
  children: [dayEvents.slice(0, 3).map((event) => /* @__PURE__ */ jsxs("a", {
@@ -49341,8 +49514,8 @@ function renderCalendarSsr(display) {
49341
49514
  ]
49342
49515
  });
49343
49516
  }
49344
- function renderTimetableSsr(display) {
49345
- const viewDate = /* @__PURE__ */ new Date();
49517
+ function renderTimetableSsr(display, hydration) {
49518
+ const viewDate = hydration.calendarContext.initialToday;
49346
49519
  const viewport = buildCalendarViewport({
49347
49520
  view: "week",
49348
49521
  anchorDate: viewDate,
@@ -49351,7 +49524,8 @@ function renderTimetableSsr(display) {
49351
49524
  const model = buildTimetableModel({
49352
49525
  events: [...display.events],
49353
49526
  viewDate,
49354
- startOfWeek: display.startOfWeek
49527
+ startOfWeek: display.startOfWeek,
49528
+ today: hydration.calendarContext.initialToday
49355
49529
  });
49356
49530
  const selectedDayKey = model.days.find((day) => day.isToday)?.dateKey ?? model.days[0]?.dateKey ?? "";
49357
49531
  return /* @__PURE__ */ jsxs("div", {
@@ -49506,20 +49680,21 @@ function renderTimetableSsr(display) {
49506
49680
  })]
49507
49681
  });
49508
49682
  }
49509
- function renderWeekBoardSsr(display) {
49510
- return /* @__PURE__ */ jsx(WeekBoardView, {
49511
- days: getWeekCalendarDayTuple(/* @__PURE__ */ new Date(), display.startOfWeek),
49512
- events: [...display.events],
49513
- showCapacity: display.showCapacity,
49514
- emptyMessage: display.emptyMessage,
49515
- title: display.boardTitle,
49516
- footerNote: display.footerNote,
49517
- ctaLabel: display.loadMoreText ?? display.buttonText,
49518
- ctaHref: display.seeAllUrl,
49519
- buttonVariant: display.buttonVariant,
49520
- elevated: display.elevated,
49521
- banded: display.banded
49522
- });
49683
+ function renderWeekBoardSsr(display, hydration) {
49684
+ const today = hydration.calendarContext.initialToday;
49685
+ const days = getWeekCalendarDayTuple(today, display.startOfWeek);
49686
+ const navigation = {
49687
+ kind: "static",
49688
+ ...buildWeekBoardNavigationState({
49689
+ days,
49690
+ title: formatCalendarTitle(today, "week", [...days])
49691
+ }, today)
49692
+ };
49693
+ return /* @__PURE__ */ jsx(WeekBoardView, { ...buildWeekBoardViewProps({
49694
+ ...display,
49695
+ calendarTimeZone: hydration.calendarContext.timeZone,
49696
+ today
49697
+ }, days, navigation) });
49523
49698
  }
49524
49699
  function formatHourLabel(hour) {
49525
49700
  if (hour === 0) return "12 AM";
@@ -49545,13 +49720,13 @@ const EventCalendarSSR = (props) => {
49545
49720
  content = renderListSsr(display, hydration.prefetchedEvents);
49546
49721
  break;
49547
49722
  case "timetable":
49548
- content = renderTimetableSsr(display);
49723
+ content = renderTimetableSsr(display, hydration);
49549
49724
  break;
49550
49725
  case "week-board":
49551
- content = renderWeekBoardSsr(display);
49726
+ content = renderWeekBoardSsr(display, hydration);
49552
49727
  break;
49553
49728
  case "calendar":
49554
- content = renderCalendarSsr(display);
49729
+ content = renderCalendarSsr(display, hydration);
49555
49730
  break;
49556
49731
  default: content = assertNeverDisplayMode(display.displayMode);
49557
49732
  }
@@ -52780,36 +52955,9 @@ function buildLegacyHeaderNavLinkStyle(header, contrastTreatment, navTextSize) {
52780
52955
  ...navTextSize ? { textSize: navTextSize } : {}
52781
52956
  };
52782
52957
  if (header.navWeight === "bold" && navStyle === "solid") style.textTransform = "uppercase";
52783
- applyLegacyNavEffects(style, header);
52784
52958
  applyLegacyGlassTreatment(style, header, navStyle);
52785
52959
  return style;
52786
52960
  }
52787
- function applyLegacyNavEffects(style, header) {
52788
- if (!header.navEffects) return;
52789
- const effects = style.effects = {
52790
- ...style.effects,
52791
- hover: [...style.effects?.hover ?? []],
52792
- active: [...style.effects?.active ?? []]
52793
- };
52794
- if (header.navEffects.glow) effects.hover.push({
52795
- effectId: "outer-glow",
52796
- options: { spread: 5 }
52797
- });
52798
- if (header.navEffects.neumorphic) {
52799
- effects.hover.push({
52800
- effectId: "neumorphic-shadow",
52801
- options: { mode: "pressed" }
52802
- });
52803
- effects.hover.push({
52804
- effectId: "text-color-change",
52805
- options: { hoverColorToken: "primary" }
52806
- });
52807
- }
52808
- if (header.navEffects.underlineGradient) effects.hover.push({
52809
- effectId: "border-bottom-grow",
52810
- options: { origin: "center" }
52811
- });
52812
- }
52813
52961
  function applyLegacyGlassTreatment(style, header, navStyle) {
52814
52962
  if (header.navContainer?.type !== "glass" || navStyle !== "minimal") return;
52815
52963
  style.borderRadius = "full";
@@ -52835,19 +52983,6 @@ function glassStateEffects(opacity) {
52835
52983
  //#region ../theme-core/src/header/generateHeaderCss.ts
52836
52984
  const HEADER_BLUR = "blur(12px)";
52837
52985
  const FLOATING_BLUR = "blur(16px)";
52838
- const INSET_SHELL_MARKER_CLASS = "header-bar-frame-inset-shell";
52839
- const INSET_SHELL_EDGE = "1rem";
52840
- const INSET_SHELL_INSET_BLOCK = "0.875rem";
52841
- const INSET_SHELL_RADIUS = "9999px";
52842
- const INSET_SHELL_CONTAINER_MAX = "80rem";
52843
- const INSET_SHELL_INTERIOR_PADDING_X = "1.75rem";
52844
- /**
52845
- * Whether the classic header should render as an inset "brand capsule" shell.
52846
- * The facet is only meaningful for the classic layout; other variants ignore it.
52847
- */
52848
- function usesInsetShell(header) {
52849
- return header.barFrame === "inset-shell" && header.variant === "classic";
52850
- }
52851
52986
  /**
52852
52987
  * Generate complete CSS for header system from theme config
52853
52988
  *
@@ -52925,36 +53060,7 @@ function generateHeaderRootStyles(header, themeId, tokens) {
52925
53060
  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`;
52926
53061
  const scrollTreatmentStyles = generateHeaderScrollTreatmentStyles(header.scrollTreatment, themeId, header.variant, scrolledPaintRules);
52927
53062
  if (isContainedNav || basePaintRules.length === 0) return `${baseBlock}\n${overlayFix}${scrollTreatmentStyles}`;
52928
- return `${baseBlock}\n${renderHeaderPaintTargetRules(getHeaderPaintTargets(header.variant, themeId), basePaintRules)}\n${overlayFix}${usesInsetShell(header) ? generateInsetShellStyles(header, themeId) : ""}${scrollTreatmentStyles}`;
52929
- }
52930
- /**
52931
- * Reshapes the full-bleed header paint layer into a contained brand capsule at
52932
- * the desktop breakpoint. The capsule is centred, capped to the page container
52933
- * width, inset from the top, fully rounded, and carries the header depth so it
52934
- * reads as a floating pill. Root border/shadow are neutralised on desktop so a
52935
- * bordered style does not leave a full-width hairline around the capsule. Sticky
52936
- * positioning is unaffected — the root stays full width and sticky while the
52937
- * ::before paint provides the capsule.
52938
- */
52939
- function generateInsetShellStyles(header, themeId) {
52940
- const scope = `:where([data-theme-scope="${themeId}"])`;
52941
- const shellRootSelector = `${scope} .header-root.${INSET_SHELL_MARKER_CLASS}`;
52942
- const shellBeforeSelector = `${scope} .header-root.${INSET_SHELL_MARKER_CLASS}::before`;
52943
- const layoutSelector = `${scope} .header-root.${INSET_SHELL_MARKER_CLASS} .rb-header-layout`;
52944
- const widthValue = header.maxWidth === "full" ? `calc(100% - (${INSET_SHELL_EDGE} * 2))` : `min(${INSET_SHELL_CONTAINER_MAX}, calc(100% - (${INSET_SHELL_EDGE} * 2)))`;
52945
- const shadowVar = getComponentShadowVar(header.shadow?.elevation ?? "none");
52946
- const rootRules = ["border: none;", "box-shadow: none;"];
52947
- const beforeRules = [
52948
- `inset: ${INSET_SHELL_INSET_BLOCK} auto;`,
52949
- "left: 50%;",
52950
- "right: auto;",
52951
- "transform: translateX(-50%);",
52952
- `width: ${widthValue};`,
52953
- `border-radius: ${INSET_SHELL_RADIUS};`,
52954
- ...shadowVar ? [`box-shadow: ${shadowVar};`] : []
52955
- ];
52956
- const layoutRules = [`padding-inline: ${INSET_SHELL_INTERIOR_PADDING_X};`];
52957
- 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`;
53063
+ return `${baseBlock}\n${renderHeaderPaintTargetRules(getHeaderPaintTargets(header.variant, themeId), basePaintRules)}\n${overlayFix}${scrollTreatmentStyles}`;
52958
53064
  }
52959
53065
  /**
52960
53066
  * Generate logo typography styles
@@ -53028,7 +53134,7 @@ function generateLogoTextBehaviorStyles(logoText, themeId) {
53028
53134
  * Generate navigation link styles based on navStyle and effects
53029
53135
  *
53030
53136
  * Uses the composable NavLinkStyle system with backward compatibility for
53031
- * legacy navStyle, navWeight, navColor, and navEffects settings.
53137
+ * legacy navStyle, navWeight and navColor settings.
53032
53138
  */
53033
53139
  function generateNavLinkStyles(header, themeId, tokens, theme, contrastTreatment, navTextSize) {
53034
53140
  const navPreferences = {
@@ -55665,9 +55771,43 @@ function pageBlockPosition(index, blockCount) {
55665
55771
  return "middle";
55666
55772
  }
55667
55773
  //#endregion
55774
+ //#region ../blocks/src/pageRenderAdmission.ts
55775
+ function toPresentationPage(page) {
55776
+ if (!page) return null;
55777
+ return {
55778
+ ...page,
55779
+ blocks: page.blocks.map((block) => {
55780
+ const presentationMetadata = {
55781
+ id: block.id,
55782
+ kind: block.kind,
55783
+ purpose: block.purpose,
55784
+ ...block.bindings ? { bindings: block.bindings } : {}
55785
+ };
55786
+ return block.admission.kind === "renderable" ? {
55787
+ ...presentationMetadata,
55788
+ content: block.admission.content,
55789
+ presentationAdmission: { kind: "renderable" }
55790
+ } : {
55791
+ ...presentationMetadata,
55792
+ presentationAdmission: {
55793
+ kind: "invalid",
55794
+ error: block.admission.error
55795
+ }
55796
+ };
55797
+ })
55798
+ };
55799
+ }
55800
+ //#endregion
55668
55801
  //#region ../blocks/src/PageRenderer.tsx
55669
55802
  function PageRenderer$1(props) {
55670
- const { page, runtimeBoundaries, usePlaceholders = false, dataContext, wrapBlock, wrapBoundary, registry, routeMap, blockOverrides, sdkConfig, customBlocks, previewAuthoring } = props;
55803
+ const page = React$1.useMemo(() => toPresentationPage(props.page), [props.page]);
55804
+ return /* @__PURE__ */ jsx(PageRendererCore, {
55805
+ ...props,
55806
+ page
55807
+ });
55808
+ }
55809
+ function PageRendererCore(props) {
55810
+ const { page, runtimeBoundaries, dataContext, wrapBlock, wrapBoundary, registry } = props;
55671
55811
  const imageResolveContext = React$1.useMemo(() => resolvePageRendererImageResolveContext(dataContext), [
55672
55812
  dataContext?.imageResolveContext,
55673
55813
  dataContext?.mediaAssets,
@@ -55766,11 +55906,11 @@ function resolvePageRendererImageResolveContext(dataContext) {
55766
55906
  mediaAssets: input?.mediaAssets ?? dataContext?.mediaAssets
55767
55907
  });
55768
55908
  }
55909
+ const resolveBookingTimeZone = (dataContext) => dataContext?.bookingTimeZone;
55769
55910
  function buildBlockSectionProps(input) {
55770
55911
  return {
55771
55912
  themeResources: input.themeResources,
55772
55913
  block: input.block,
55773
- usePlaceholders: input.props.usePlaceholders ?? false,
55774
55914
  dataContext: input.props.dataContext,
55775
55915
  blockKey: input.blockKey,
55776
55916
  registry: input.registry,
@@ -55794,7 +55934,6 @@ function getBlockOverride(blockKind, overrides) {
55794
55934
  if (overrides[shortKind]) return overrides[shortKind];
55795
55935
  }
55796
55936
  const blockComponentCache = /* @__PURE__ */ new WeakMap();
55797
- const manifestValidatorCache = /* @__PURE__ */ new WeakMap();
55798
55937
  const DEFAULT_PERF_SLOW_BLOCK_RENDER_THRESHOLD_MS = 25;
55799
55938
  let cachedPerfSlowBlockRenderThresholdMs = null;
55800
55939
  function getPerfSlowBlockRenderThresholdMs() {
@@ -55812,44 +55951,13 @@ function getCachedBlockComponent(manifest) {
55812
55951
  return created;
55813
55952
  }
55814
55953
  /**
55815
- * Render-time manifest validator — see `.claude/plans/dapper-jingling-emerson.md`
55816
- * Phase 10. One function, two flavours:
55817
- *
55818
- * - `strict` (default): the contract every render surface tries first. No
55819
- * null tolerance, no missing-required tolerance. Production frontends and
55820
- * the dashboard preview both go through this path; after Phases 6-9 the
55821
- * preview satisfies strict structurally.
55822
- * - `relaxed`: editor-mid-edit fallback. Reached via `usePlaceholders` when
55823
- * strict fails. Built on `mode: 'draft'` so empty required fields parse
55824
- * as `undefined` and the user's valid in-flight fields flow through.
55825
- *
55826
- * Per-manifest, per-flavour cached. Adding a third flavour means adding a
55827
- * branch to the `createManifestValidator` call below and a member to
55828
- * `ValidationFlavour` — no per-flavour helper duplication.
55829
- */
55830
- function getCachedValidator(manifest, flavour) {
55831
- let perFlavour = manifestValidatorCache.get(manifest);
55832
- if (!perFlavour) {
55833
- perFlavour = /* @__PURE__ */ new Map();
55834
- manifestValidatorCache.set(manifest, perFlavour);
55835
- }
55836
- const cached = perFlavour.get(flavour);
55837
- if (cached) return cached;
55838
- const created = createManifestValidator(manifest, {
55839
- allowNull: false,
55840
- ...flavour === "relaxed" ? { mode: "draft" } : {}
55841
- });
55842
- perFlavour.set(flavour, created);
55843
- return created;
55844
- }
55845
- /**
55846
55954
  * Memoized block section component.
55847
55955
  *
55848
55956
  * Custom memoization prevents render loops during editing by explicitly checking
55849
55957
  * referential equality of all props that affect rendering. Standard React.memo
55850
55958
  * wouldn't catch all the render triggers from the editor store.
55851
55959
  */
55852
- const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources, block, usePlaceholders, dataContext, registry, routeMap, wrapBlock, blockOverrides, sdkConfig, customBlocks, imageResolveContext, previewAuthoring }) {
55960
+ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources, block, dataContext, registry, routeMap, wrapBlock, blockOverrides, sdkConfig, customBlocks, imageResolveContext, previewAuthoring }) {
55853
55961
  const perfStart = globalThis.process?.env?.DEBUG_PERF === "1" && typeof window === "undefined" ? Date.now() : 0;
55854
55962
  const fallbackImageResolveContext = getCurrentResolveImageUrlContext();
55855
55963
  const resolvedImageResolveContext = imageResolveContext ?? fallbackImageResolveContext;
@@ -55869,6 +55977,19 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
55869
55977
  } catch {}
55870
55978
  return node;
55871
55979
  }
55980
+ if (block.presentationAdmission.kind === "invalid") {
55981
+ try {
55982
+ if (globalThis.process?.env?.NODE_ENV !== "production") console.warn("[blocks:PageRenderer] block admission failed", {
55983
+ blockId: block.id ?? null,
55984
+ blockKind: block.kind,
55985
+ error: block.presentationAdmission.error
55986
+ });
55987
+ } catch {}
55988
+ return finish(fallback(`Invalid content for block kind "${block.kind}".`), {
55989
+ outcome: "invalidContent",
55990
+ override: false
55991
+ });
55992
+ }
55872
55993
  const OverrideComponent = getBlockOverride(block.kind, blockOverrides);
55873
55994
  requireBlockOverride(themeResources, OverrideComponent, block.kind);
55874
55995
  let definition = getBlockDefinition(block.kind);
@@ -55938,25 +56059,10 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
55938
56059
  });
55939
56060
  }
55940
56061
  const content = getRenderableContent(block, dataContext?.previewStage);
55941
- const parsed = getCachedValidator(definition.manifest, "strict").safeParse(content);
55942
- try {
55943
- if (globalThis.process?.env?.NODE_ENV !== "production" && !parsed.success && !usePlaceholders) console.warn("[blocks:PageRenderer] manifest validation failed", {
55944
- blockId: block.id ?? null,
55945
- blockKind: block.kind,
55946
- content: summarizeValue(content),
55947
- expectedFields: (definition.manifest.fields ?? []).map((f) => ({
55948
- id: f.id,
55949
- type: f.type,
55950
- required: !!f.required
55951
- })),
55952
- issues: parsed.error?.issues ?? null
55953
- });
55954
- } catch {}
55955
- let data;
55956
- if (parsed.success) data = parsed.data;
55957
- else if (usePlaceholders) {
55958
- const relaxed = getCachedValidator(definition.manifest, "relaxed").safeParse(content);
55959
- if (relaxed.success) data = relaxed.data;
56062
+ let data = content;
56063
+ if (block.presentationAdmission.kind === "editor") {
56064
+ const draft = validateManifestDraft(compileManifestContentContract(definition.manifest), content);
56065
+ if (draft.kind === "previewable") data = draft.data;
55960
56066
  else {
55961
56067
  const rendered = fallback(`Unable to render block kind "${block.kind}".`);
55962
56068
  return finish(wrapBlock && block.id ? /* @__PURE__ */ jsx(Fragment, { children: wrapBlock(block.id, rendered) }) : /* @__PURE__ */ jsx(Fragment, { children: rendered }), {
@@ -55964,12 +56070,6 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
55964
56070
  override: false
55965
56071
  });
55966
56072
  }
55967
- } else {
55968
- const rendered = fallback(`Invalid content for block kind "${block.kind}".`);
55969
- return finish(wrapBlock && block.id ? /* @__PURE__ */ jsx(Fragment, { children: wrapBlock(block.id, rendered) }) : /* @__PURE__ */ jsx(Fragment, { children: rendered }), {
55970
- outcome: "invalidContent",
55971
- override: false
55972
- });
55973
56073
  }
55974
56074
  try {
55975
56075
  const routes = routeMap ?? dataContext?.routes;
@@ -55995,6 +56095,7 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
55995
56095
  blockId: block.id ?? null,
55996
56096
  blockKind: block.kind
55997
56097
  };
56098
+ baseProps.bookingTimeZone = resolveBookingTimeZone(dataContext);
55998
56099
  let rendered;
55999
56100
  if (OverrideComponent) rendered = /* @__PURE__ */ jsx(OverrideComponent, { ...baseProps });
56000
56101
  else {
@@ -56041,14 +56142,14 @@ const MemoBlockSection = React$1.memo(function MemoBlockSection({ themeResources
56041
56142
  hasResolvedData
56042
56143
  });
56043
56144
  } catch {
56044
- const rendered = usePlaceholders ? fallback(`Unable to render block kind "${block.kind}".`) : fallback(`Invalid content for block kind "${block.kind}".`);
56145
+ const rendered = block.presentationAdmission.kind === "editor" ? fallback(`Unable to render block kind "${block.kind}".`) : fallback(`Invalid content for block kind "${block.kind}".`);
56045
56146
  return finish(wrapBlock && block.id ? /* @__PURE__ */ jsx(Fragment, { children: wrapBlock(block.id, rendered) }) : /* @__PURE__ */ jsx(Fragment, { children: rendered }), {
56046
56147
  outcome: "exception",
56047
56148
  override: false
56048
56149
  });
56049
56150
  }
56050
56151
  }, (prev, next) => {
56051
- 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;
56152
+ 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;
56052
56153
  });
56053
56154
  function fallback(message) {
56054
56155
  return /* @__PURE__ */ jsx("div", {
@@ -56075,32 +56176,6 @@ function getRenderableContent(block, previewStage = "published") {
56075
56176
  if (!isObjectRecord$1(source)) return {};
56076
56177
  return source;
56077
56178
  }
56078
- function summarizeValue(value) {
56079
- try {
56080
- if (value === null || value === void 0) return value;
56081
- if (typeof value === "string") return value.length > 160 ? `${value.slice(0, 160)}…` : value;
56082
- if (typeof value === "number" || typeof value === "boolean") return value;
56083
- if (Array.isArray(value)) return `[array(${value.length})]`;
56084
- if (typeof value === "object") {
56085
- const rec = value;
56086
- const keys = Object.keys(rec);
56087
- const preview = {};
56088
- for (const k of keys.slice(0, 8)) {
56089
- const v = rec[k];
56090
- if (k === "body" || k === "content") preview[k] = !!(v && typeof v === "object" && isObjectRecord$1(v) && v.type === "doc") ? "{type:doc}" : summarizeValue(v);
56091
- else preview[k] = summarizeValue(v);
56092
- }
56093
- return {
56094
- keys,
56095
- preview
56096
- };
56097
- }
56098
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol") return String(value);
56099
- return "[uninspectable]";
56100
- } catch {
56101
- return "[uninspectable]";
56102
- }
56103
- }
56104
56179
  //#endregion
56105
56180
  //#region ../theme-core/src/generated/containerResponsiveThemeCss.ts
56106
56181
  /**
@@ -62697,31 +62772,37 @@ const containerResponsiveThemeCss = `/*
62697
62772
  */
62698
62773
 
62699
62774
  /* Main container */
62700
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar {
62775
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-calendar {
62701
62776
  width: 100%;
62702
62777
  }
62703
62778
 
62704
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-container {
62779
+ :where(.theme-scope)
62780
+ :where([data-block='event-calendar'])
62781
+ .event-calendar-container {
62705
62782
  border: 1px solid var(--ec-border);
62706
62783
  border-radius: var(--rb-block-radius);
62707
62784
  overflow: hidden;
62708
62785
  }
62709
62786
 
62710
62787
  /* Calendar navigation */
62711
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-header {
62788
+ :where(.theme-scope)
62789
+ :where([data-block='event-calendar'])
62790
+ .event-calendar-header {
62712
62791
  display: flex;
62713
62792
  align-items: center;
62714
62793
  justify-content: space-between;
62715
62794
  margin-bottom: 1rem;
62716
62795
  }
62717
62796
 
62718
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-nav {
62797
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-calendar-nav {
62719
62798
  display: flex;
62720
62799
  align-items: center;
62721
62800
  gap: 0.5rem;
62722
62801
  }
62723
62802
 
62724
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-nav-btn {
62803
+ :where(.theme-scope)
62804
+ :where([data-block='event-calendar'])
62805
+ .event-calendar-nav-btn {
62725
62806
  display: flex;
62726
62807
  align-items: center;
62727
62808
  justify-content: center;
@@ -62736,11 +62817,15 @@ const containerResponsiveThemeCss = `/*
62736
62817
  transition: background-color var(--rb-block-transition) ease;
62737
62818
  }
62738
62819
 
62739
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-nav-btn:hover {
62820
+ :where(.theme-scope)
62821
+ :where([data-block='event-calendar'])
62822
+ .event-calendar-nav-btn:hover {
62740
62823
  background-color: var(--ec-nav-hover-bg);
62741
62824
  }
62742
62825
 
62743
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-title {
62826
+ :where(.theme-scope)
62827
+ :where([data-block='event-calendar'])
62828
+ .event-calendar-title {
62744
62829
  font-size: 1.125rem;
62745
62830
  font-weight: 600;
62746
62831
  min-width: 200px;
@@ -62748,13 +62833,17 @@ const containerResponsiveThemeCss = `/*
62748
62833
  }
62749
62834
 
62750
62835
  /* Day headers */
62751
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-headers {
62836
+ :where(.theme-scope)
62837
+ :where([data-block='event-calendar'])
62838
+ .event-calendar-day-headers {
62752
62839
  display: grid;
62753
62840
  grid-template-columns: repeat(7, minmax(0, 1fr));
62754
62841
  background-color: var(--ec-header-bg);
62755
62842
  }
62756
62843
 
62757
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-header {
62844
+ :where(.theme-scope)
62845
+ :where([data-block='event-calendar'])
62846
+ .event-calendar-day-header {
62758
62847
  text-align: center;
62759
62848
  padding: 0.5rem;
62760
62849
  font-size: 0.875rem;
@@ -62764,33 +62853,43 @@ const containerResponsiveThemeCss = `/*
62764
62853
  }
62765
62854
 
62766
62855
  /* Calendar grid */
62767
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-grid {
62856
+ :where(.theme-scope)
62857
+ :where([data-block='event-calendar'])
62858
+ .event-calendar-grid {
62768
62859
  display: grid;
62769
62860
  grid-template-columns: repeat(7, minmax(0, 1fr));
62770
62861
  }
62771
62862
 
62772
62863
  /* Day cells */
62773
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day {
62864
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-calendar-day {
62774
62865
  min-height: 100px;
62775
62866
  border-bottom: 1px solid var(--ec-border-subtle);
62776
62867
  border-right: 1px solid var(--ec-border-subtle);
62777
62868
  padding: 0.25rem;
62778
62869
  }
62779
62870
 
62780
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day:nth-child(7n) {
62871
+ :where(.theme-scope)
62872
+ :where([data-block='event-calendar'])
62873
+ .event-calendar-day:nth-child(7n) {
62781
62874
  border-right: none;
62782
62875
  }
62783
62876
 
62784
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day--today {
62877
+ :where(.theme-scope)
62878
+ :where([data-block='event-calendar'])
62879
+ .event-calendar-day--today {
62785
62880
  background-color: var(--ec-today-bg);
62786
62881
  }
62787
62882
 
62788
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day--other-month {
62883
+ :where(.theme-scope)
62884
+ :where([data-block='event-calendar'])
62885
+ .event-calendar-day--other-month {
62789
62886
  opacity: var(--ec-other-month-opacity);
62790
62887
  }
62791
62888
 
62792
62889
  /* Day number */
62793
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-number {
62890
+ :where(.theme-scope)
62891
+ :where([data-block='event-calendar'])
62892
+ .event-calendar-day-number {
62794
62893
  font-size: 0.875rem;
62795
62894
  font-weight: 500;
62796
62895
  margin-bottom: 0.25rem;
@@ -62798,21 +62897,28 @@ const containerResponsiveThemeCss = `/*
62798
62897
  opacity: var(--ec-day-number-opacity);
62799
62898
  }
62800
62899
 
62801
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day--today .event-calendar-day-number {
62900
+ :where(.theme-scope)
62901
+ :where([data-block='event-calendar'])
62902
+ .event-calendar-day--today
62903
+ .event-calendar-day-number {
62802
62904
  font-weight: 700;
62803
62905
  opacity: 1;
62804
62906
  color: var(--ec-today-text);
62805
62907
  }
62806
62908
 
62807
62909
  /* Events list */
62808
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-events {
62910
+ :where(.theme-scope)
62911
+ :where([data-block='event-calendar'])
62912
+ .event-calendar-events {
62809
62913
  display: flex;
62810
62914
  flex-direction: column;
62811
62915
  gap: 0.25rem;
62812
62916
  }
62813
62917
 
62814
62918
  /* Event item - solid primary background */
62815
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event {
62919
+ :where(.theme-scope)
62920
+ :where([data-block='event-calendar'])
62921
+ .event-calendar-event {
62816
62922
  display: flex;
62817
62923
  align-items: center;
62818
62924
  gap: 0.375rem;
@@ -62833,24 +62939,32 @@ const containerResponsiveThemeCss = `/*
62833
62939
  transform var(--rb-block-transition) ease;
62834
62940
  }
62835
62941
 
62836
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event:hover {
62942
+ :where(.theme-scope)
62943
+ :where([data-block='event-calendar'])
62944
+ .event-calendar-event:hover {
62837
62945
  background-color: var(--ec-event-solid-hover-bg);
62838
62946
  transform: translateY(-1px);
62839
62947
  }
62840
62948
 
62841
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event:focus-visible {
62949
+ :where(.theme-scope)
62950
+ :where([data-block='event-calendar'])
62951
+ .event-calendar-event:focus-visible {
62842
62952
  outline: 2px solid var(--ec-event-solid-bg);
62843
62953
  outline-offset: 2px;
62844
62954
  }
62845
62955
 
62846
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event-time {
62956
+ :where(.theme-scope)
62957
+ :where([data-block='event-calendar'])
62958
+ .event-calendar-event-time {
62847
62959
  flex-shrink: 0;
62848
62960
  white-space: nowrap;
62849
62961
  font-weight: 600;
62850
62962
  opacity: 0.9;
62851
62963
  }
62852
62964
 
62853
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event-title {
62965
+ :where(.theme-scope)
62966
+ :where([data-block='event-calendar'])
62967
+ .event-calendar-event-title {
62854
62968
  min-width: 0;
62855
62969
  overflow: hidden;
62856
62970
  text-overflow: ellipsis;
@@ -62858,7 +62972,9 @@ const containerResponsiveThemeCss = `/*
62858
62972
  }
62859
62973
 
62860
62974
  /* More indicator - clickable button style */
62861
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-more {
62975
+ :where(.theme-scope)
62976
+ :where([data-block='event-calendar'])
62977
+ .event-calendar-more {
62862
62978
  font-size: 0.75rem;
62863
62979
  padding: 0.25rem 0.5rem;
62864
62980
  color: rgb(var(--tb-primary));
@@ -62870,16 +62986,22 @@ const containerResponsiveThemeCss = `/*
62870
62986
  transition: background-color var(--rb-block-transition) ease;
62871
62987
  }
62872
62988
 
62873
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-more:hover {
62989
+ :where(.theme-scope)
62990
+ :where([data-block='event-calendar'])
62991
+ .event-calendar-more:hover {
62874
62992
  background-color: rgba(var(--tb-primary), 0.1);
62875
62993
  }
62876
62994
 
62877
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-more:focus-visible {
62995
+ :where(.theme-scope)
62996
+ :where([data-block='event-calendar'])
62997
+ .event-calendar-more:focus-visible {
62878
62998
  outline: 2px solid rgb(var(--tb-primary));
62879
62999
  outline-offset: 2px;
62880
63000
  }
62881
63001
 
62882
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-empty {
63002
+ :where(.theme-scope)
63003
+ :where([data-block='event-calendar'])
63004
+ .event-calendar-empty {
62883
63005
  text-align: center;
62884
63006
  font-size: 0.875rem;
62885
63007
  opacity: var(--ec-empty-opacity);
@@ -62887,21 +63009,21 @@ const containerResponsiveThemeCss = `/*
62887
63009
  }
62888
63010
 
62889
63011
  /* Filters container */
62890
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filters {
63012
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-filters {
62891
63013
  display: flex;
62892
63014
  flex-wrap: wrap;
62893
63015
  gap: 1rem;
62894
63016
  margin-bottom: 1.5rem;
62895
63017
  }
62896
63018
 
62897
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filter {
63019
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-filter {
62898
63020
  display: flex;
62899
63021
  flex-direction: column;
62900
63022
  gap: 0.375rem;
62901
63023
  min-width: 180px;
62902
63024
  }
62903
63025
 
62904
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filter-label {
63026
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-filter-label {
62905
63027
  font-size: 0.75rem;
62906
63028
  font-weight: 500;
62907
63029
  text-transform: uppercase;
@@ -62910,19 +63032,23 @@ const containerResponsiveThemeCss = `/*
62910
63032
  }
62911
63033
 
62912
63034
  /* Event filter select inherits base styling from .form-select, only block-specific overrides here */
62913
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filter-select {
63035
+ :where(.theme-scope)
63036
+ :where([data-block='event-calendar'])
63037
+ .event-filter-select {
62914
63038
  /* Use theme input radius instead of block radius for consistency with other form inputs */
62915
63039
  border-radius: var(--rb-input-radius, var(--rb-block-radius));
62916
63040
  }
62917
63041
 
62918
63042
  /* Category filter buttons */
62919
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-buttons {
63043
+ :where(.theme-scope)
63044
+ :where([data-block='event-calendar'])
63045
+ .event-category-buttons {
62920
63046
  display: flex;
62921
63047
  flex-wrap: wrap;
62922
63048
  gap: 0.5rem;
62923
63049
  }
62924
63050
 
62925
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn {
63051
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-category-btn {
62926
63052
  padding: 0.5rem 1rem;
62927
63053
  font-size: 0.875rem;
62928
63054
  font-weight: 500;
@@ -62934,50 +63060,66 @@ const containerResponsiveThemeCss = `/*
62934
63060
  transition: all var(--rb-block-transition) ease;
62935
63061
  }
62936
63062
 
62937
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn:hover:not(:disabled) {
63063
+ :where(.theme-scope)
63064
+ :where([data-block='event-calendar'])
63065
+ .event-category-btn:hover:not(:disabled) {
62938
63066
  border-color: rgba(var(--tb-primary), 0.5);
62939
63067
  }
62940
63068
 
62941
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn:focus-visible {
63069
+ :where(.theme-scope)
63070
+ :where([data-block='event-calendar'])
63071
+ .event-category-btn:focus-visible {
62942
63072
  outline: 2px solid rgb(var(--tb-primary));
62943
63073
  outline-offset: 2px;
62944
63074
  }
62945
63075
 
62946
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn--active {
63076
+ :where(.theme-scope)
63077
+ :where([data-block='event-calendar'])
63078
+ .event-category-btn--active {
62947
63079
  background-color: rgb(var(--tb-primary));
62948
63080
  border-color: rgb(var(--tb-primary));
62949
63081
  color: var(--ec-event-solid-text);
62950
63082
  }
62951
63083
 
62952
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-category-btn:disabled {
63084
+ :where(.theme-scope)
63085
+ :where([data-block='event-calendar'])
63086
+ .event-category-btn:disabled {
62953
63087
  opacity: 0.5;
62954
63088
  cursor: not-allowed;
62955
63089
  }
62956
63090
 
62957
63091
  /* Allow category filter to be wider for button group */
62958
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-filter:has(.event-category-buttons) {
63092
+ :where(.theme-scope)
63093
+ :where([data-block='event-calendar'])
63094
+ .event-filter:has(.event-category-buttons) {
62959
63095
  min-width: auto;
62960
63096
  flex: 1 1 auto;
62961
63097
  }
62962
63098
 
62963
63099
  /* Combined mode container */
62964
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-combined {
63100
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-combined {
62965
63101
  width: 100%;
62966
63102
  }
62967
63103
 
62968
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-combined-content {
63104
+ :where(.theme-scope)
63105
+ :where([data-block='event-calendar'])
63106
+ .event-combined-content {
62969
63107
  display: flex;
62970
63108
  flex-direction: column;
62971
63109
  }
62972
63110
 
62973
63111
  /* Calendar view toggle (Month/Week) in Combined mode */
62974
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-view-toggle {
63112
+ :where(.theme-scope)
63113
+ :where([data-block='event-calendar'])
63114
+ .event-calendar-view-toggle {
62975
63115
  display: flex;
62976
63116
  gap: 0.25rem;
62977
63117
  margin-bottom: 1rem;
62978
63118
  }
62979
63119
 
62980
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-view-btn {
63120
+ :where(.theme-scope)
63121
+ :where([data-block='event-calendar'])
63122
+ .event-calendar-view-btn {
62981
63123
  padding: 0.5rem 1rem;
62982
63124
  font-size: 0.875rem;
62983
63125
  font-weight: 500;
@@ -62990,12 +63132,16 @@ const containerResponsiveThemeCss = `/*
62990
63132
  transition: all var(--rb-block-transition) ease;
62991
63133
  }
62992
63134
 
62993
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-view-btn:hover {
63135
+ :where(.theme-scope)
63136
+ :where([data-block='event-calendar'])
63137
+ .event-calendar-view-btn:hover {
62994
63138
  opacity: 1;
62995
63139
  border-color: rgba(var(--tb-primary), 0.5);
62996
63140
  }
62997
63141
 
62998
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-view-btn--active {
63142
+ :where(.theme-scope)
63143
+ :where([data-block='event-calendar'])
63144
+ .event-calendar-view-btn--active {
62999
63145
  opacity: 1;
63000
63146
  background-color: rgb(var(--tb-primary));
63001
63147
  border-color: rgb(var(--tb-primary));
@@ -63008,29 +63154,35 @@ const containerResponsiveThemeCss = `/*
63008
63154
  * ----------------------------------------------------------------------------- */
63009
63155
 
63010
63156
  /* Agenda week container */
63011
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-week {
63157
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-agenda-week {
63012
63158
  display: flex;
63013
63159
  flex-direction: column;
63014
63160
  gap: 0;
63015
63161
  }
63016
63162
 
63017
63163
  /* Individual day section */
63018
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day {
63164
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-agenda-day {
63019
63165
  border-bottom: 1px solid var(--ec-border-subtle);
63020
63166
  }
63021
63167
 
63022
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day:last-child {
63168
+ :where(.theme-scope)
63169
+ :where([data-block='event-calendar'])
63170
+ .event-agenda-day:last-child {
63023
63171
  border-bottom: none;
63024
63172
  }
63025
63173
 
63026
63174
  /* Day header */
63027
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-header {
63175
+ :where(.theme-scope)
63176
+ :where([data-block='event-calendar'])
63177
+ .event-agenda-day-header {
63028
63178
  padding: 0.75rem 0;
63029
63179
  background-color: var(--ec-header-bg);
63030
63180
  border-bottom: 1px solid var(--ec-border-subtle);
63031
63181
  }
63032
63182
 
63033
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-title {
63183
+ :where(.theme-scope)
63184
+ :where([data-block='event-calendar'])
63185
+ .event-agenda-day-title {
63034
63186
  font-size: 0.875rem;
63035
63187
  font-weight: 600;
63036
63188
  margin: 0;
@@ -63040,7 +63192,9 @@ const containerResponsiveThemeCss = `/*
63040
63192
  }
63041
63193
 
63042
63194
  /* Today badge */
63043
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-today-badge {
63195
+ :where(.theme-scope)
63196
+ :where([data-block='event-calendar'])
63197
+ .event-agenda-day-today-badge {
63044
63198
  display: inline-flex;
63045
63199
  align-items: center;
63046
63200
  padding: 0.125rem 0.5rem;
@@ -63054,24 +63208,33 @@ const containerResponsiveThemeCss = `/*
63054
63208
  }
63055
63209
 
63056
63210
  /* Today highlight for entire day section */
63057
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day--today .event-agenda-day-header {
63211
+ :where(.theme-scope)
63212
+ :where([data-block='event-calendar'])
63213
+ .event-agenda-day--today
63214
+ .event-agenda-day-header {
63058
63215
  background-color: var(--ec-today-bg);
63059
63216
  }
63060
63217
 
63061
63218
  /* Day content area */
63062
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-content {
63219
+ :where(.theme-scope)
63220
+ :where([data-block='event-calendar'])
63221
+ .event-agenda-day-content {
63063
63222
  padding: 0.75rem 0;
63064
63223
  }
63065
63224
 
63066
63225
  /* Events list within a day */
63067
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-events {
63226
+ :where(.theme-scope)
63227
+ :where([data-block='event-calendar'])
63228
+ .event-agenda-day-events {
63068
63229
  display: flex;
63069
63230
  flex-direction: column;
63070
63231
  gap: 0.5rem;
63071
63232
  }
63072
63233
 
63073
63234
  /* Empty day message */
63074
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-day-empty {
63235
+ :where(.theme-scope)
63236
+ :where([data-block='event-calendar'])
63237
+ .event-agenda-day-empty {
63075
63238
  font-size: 0.875rem;
63076
63239
  opacity: var(--ec-empty-opacity);
63077
63240
  margin: 0;
@@ -63083,7 +63246,7 @@ const containerResponsiveThemeCss = `/*
63083
63246
  * Rich event card for agenda view
63084
63247
  * ----------------------------------------------------------------------------- */
63085
63248
 
63086
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card {
63249
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-agenda-card {
63087
63250
  display: flex;
63088
63251
  align-items: flex-start;
63089
63252
  gap: 0.75rem;
@@ -63096,13 +63259,17 @@ const containerResponsiveThemeCss = `/*
63096
63259
  transition: all var(--rb-block-transition) ease;
63097
63260
  }
63098
63261
 
63099
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card:hover {
63262
+ :where(.theme-scope)
63263
+ :where([data-block='event-calendar'])
63264
+ .event-agenda-card:hover {
63100
63265
  border-color: rgba(var(--tb-primary), 0.5);
63101
63266
  background-color: var(--ec-event-bg);
63102
63267
  }
63103
63268
 
63104
63269
  /* Category color indicator */
63105
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-indicator {
63270
+ :where(.theme-scope)
63271
+ :where([data-block='event-calendar'])
63272
+ .event-agenda-card-indicator {
63106
63273
  flex-shrink: 0;
63107
63274
  width: 4px;
63108
63275
  align-self: stretch;
@@ -63111,7 +63278,9 @@ const containerResponsiveThemeCss = `/*
63111
63278
  }
63112
63279
 
63113
63280
  /* Card content wrapper */
63114
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-content {
63281
+ :where(.theme-scope)
63282
+ :where([data-block='event-calendar'])
63283
+ .event-agenda-card-content {
63115
63284
  flex: 1;
63116
63285
  min-width: 0;
63117
63286
  display: flex;
@@ -63120,28 +63289,36 @@ const containerResponsiveThemeCss = `/*
63120
63289
  }
63121
63290
 
63122
63291
  /* Header row: time + title */
63123
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-header {
63292
+ :where(.theme-scope)
63293
+ :where([data-block='event-calendar'])
63294
+ .event-agenda-card-header {
63124
63295
  display: flex;
63125
63296
  align-items: baseline;
63126
63297
  gap: 0.5rem;
63127
63298
  flex-wrap: wrap;
63128
63299
  }
63129
63300
 
63130
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-time {
63301
+ :where(.theme-scope)
63302
+ :where([data-block='event-calendar'])
63303
+ .event-agenda-card-time {
63131
63304
  font-size: 0.75rem;
63132
63305
  font-weight: 600;
63133
63306
  opacity: var(--ec-time-opacity);
63134
63307
  white-space: nowrap;
63135
63308
  }
63136
63309
 
63137
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-title {
63310
+ :where(.theme-scope)
63311
+ :where([data-block='event-calendar'])
63312
+ .event-agenda-card-title {
63138
63313
  font-size: 0.875rem;
63139
63314
  font-weight: 500;
63140
63315
  line-height: 1.3;
63141
63316
  }
63142
63317
 
63143
63318
  /* Details row: venue, price, capacity */
63144
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-details {
63319
+ :where(.theme-scope)
63320
+ :where([data-block='event-calendar'])
63321
+ .event-agenda-card-details {
63145
63322
  display: flex;
63146
63323
  align-items: center;
63147
63324
  gap: 0.75rem;
@@ -63150,26 +63327,36 @@ const containerResponsiveThemeCss = `/*
63150
63327
  opacity: 0.7;
63151
63328
  }
63152
63329
 
63153
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-venue {
63330
+ :where(.theme-scope)
63331
+ :where([data-block='event-calendar'])
63332
+ .event-agenda-card-venue {
63154
63333
  display: inline-flex;
63155
63334
  align-items: center;
63156
63335
  gap: 0.25rem;
63157
63336
  }
63158
63337
 
63159
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-icon {
63338
+ :where(.theme-scope)
63339
+ :where([data-block='event-calendar'])
63340
+ .event-agenda-card-icon {
63160
63341
  flex-shrink: 0;
63161
63342
  opacity: 0.7;
63162
63343
  }
63163
63344
 
63164
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-price {
63345
+ :where(.theme-scope)
63346
+ :where([data-block='event-calendar'])
63347
+ .event-agenda-card-price {
63165
63348
  font-weight: 500;
63166
63349
  }
63167
63350
 
63168
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-capacity {
63351
+ :where(.theme-scope)
63352
+ :where([data-block='event-calendar'])
63353
+ .event-agenda-card-capacity {
63169
63354
  font-weight: 500;
63170
63355
  }
63171
63356
 
63172
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-agenda-card-capacity--full {
63357
+ :where(.theme-scope)
63358
+ :where([data-block='event-calendar'])
63359
+ .event-agenda-card-capacity--full {
63173
63360
  color: rgb(var(--tb-error, 239 68 68));
63174
63361
  opacity: 1;
63175
63362
  }
@@ -63179,20 +63366,24 @@ const containerResponsiveThemeCss = `/*
63179
63366
  * Time-grid layout for recurring class schedules
63180
63367
  * ----------------------------------------------------------------------------- */
63181
63368
 
63182
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable {
63369
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-timetable {
63183
63370
  display: flex;
63184
63371
  flex-direction: column;
63185
63372
  gap: 0.75rem;
63186
63373
  }
63187
63374
 
63188
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-selector {
63375
+ :where(.theme-scope)
63376
+ :where([data-block='event-calendar'])
63377
+ .event-timetable-day-selector {
63189
63378
  display: none;
63190
63379
  gap: 0.375rem;
63191
63380
  overflow-x: auto;
63192
63381
  padding-bottom: 0.125rem;
63193
63382
  }
63194
63383
 
63195
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-tab {
63384
+ :where(.theme-scope)
63385
+ :where([data-block='event-calendar'])
63386
+ .event-timetable-day-tab {
63196
63387
  flex: 0 0 auto;
63197
63388
  padding: 0.5rem 0.75rem;
63198
63389
  border: 1px solid var(--ec-border);
@@ -63207,35 +63398,49 @@ const containerResponsiveThemeCss = `/*
63207
63398
  border-color var(--rb-block-transition) ease;
63208
63399
  }
63209
63400
 
63210
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-tab--active {
63401
+ :where(.theme-scope)
63402
+ :where([data-block='event-calendar'])
63403
+ .event-timetable-day-tab--active {
63211
63404
  background-color: rgb(var(--tb-primary));
63212
63405
  border-color: rgb(var(--tb-primary));
63213
63406
  color: rgb(var(--tb-primaryText, 255 255 255));
63214
63407
  }
63215
63408
 
63216
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-container {
63409
+ :where(.theme-scope)
63410
+ :where([data-block='event-calendar'])
63411
+ .event-timetable-container {
63217
63412
  border: 1px solid var(--ec-border);
63218
63413
  border-radius: var(--rb-block-radius);
63219
63414
  overflow-x: auto;
63220
63415
  overflow-y: hidden;
63221
63416
  }
63222
63417
 
63223
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-grid {
63418
+ :where(.theme-scope)
63419
+ :where([data-block='event-calendar'])
63420
+ .event-timetable-grid {
63224
63421
  display: grid;
63225
63422
  grid-template-columns: 4.5rem repeat(7, minmax(8rem, 1fr));
63226
63423
  min-width: 62rem;
63227
63424
  }
63228
63425
 
63229
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-rail,
63230
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day {
63426
+ :where(.theme-scope)
63427
+ :where([data-block='event-calendar'])
63428
+ .event-timetable-time-rail,
63429
+ :where(.theme-scope)
63430
+ :where([data-block='event-calendar'])
63431
+ .event-timetable-day {
63231
63432
  border-right: 1px solid var(--ec-border-subtle);
63232
63433
  }
63233
63434
 
63234
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day:last-child {
63435
+ :where(.theme-scope)
63436
+ :where([data-block='event-calendar'])
63437
+ .event-timetable-day:last-child {
63235
63438
  border-right: none;
63236
63439
  }
63237
63440
 
63238
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-heading {
63441
+ :where(.theme-scope)
63442
+ :where([data-block='event-calendar'])
63443
+ .event-timetable-day-heading {
63239
63444
  min-height: 3.25rem;
63240
63445
  margin: 0;
63241
63446
  padding: 0.75rem 0.5rem;
@@ -63250,12 +63455,17 @@ const containerResponsiveThemeCss = `/*
63250
63455
  font-weight: 600;
63251
63456
  }
63252
63457
 
63253
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day--today .event-timetable-day-heading {
63458
+ :where(.theme-scope)
63459
+ :where([data-block='event-calendar'])
63460
+ .event-timetable-day--today
63461
+ .event-timetable-day-heading {
63254
63462
  background-color: var(--ec-today-bg);
63255
63463
  color: var(--ec-today-text);
63256
63464
  }
63257
63465
 
63258
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-today-badge {
63466
+ :where(.theme-scope)
63467
+ :where([data-block='event-calendar'])
63468
+ .event-timetable-today-badge {
63259
63469
  padding: 0.125rem 0.375rem;
63260
63470
  border-radius: var(--rb-button-radius);
63261
63471
  background-color: rgb(var(--tb-primary));
@@ -63265,17 +63475,29 @@ const containerResponsiveThemeCss = `/*
63265
63475
  text-transform: uppercase;
63266
63476
  }
63267
63477
 
63268
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-body,
63269
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-body {
63478
+ :where(.theme-scope)
63479
+ :where([data-block='event-calendar'])
63480
+ .event-timetable-time-body,
63481
+ :where(.theme-scope)
63482
+ :where([data-block='event-calendar'])
63483
+ .event-timetable-day-body {
63270
63484
  position: relative;
63271
- height: clamp(34rem, calc(var(--event-timetable-range-minutes, 900) * 0.055rem), 56rem);
63485
+ height: clamp(
63486
+ 34rem,
63487
+ calc(var(--event-timetable-range-minutes, 900) * 0.055rem),
63488
+ 56rem
63489
+ );
63272
63490
  }
63273
63491
 
63274
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-body {
63492
+ :where(.theme-scope)
63493
+ :where([data-block='event-calendar'])
63494
+ .event-timetable-time-body {
63275
63495
  background-color: var(--ec-header-bg);
63276
63496
  }
63277
63497
 
63278
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-label {
63498
+ :where(.theme-scope)
63499
+ :where([data-block='event-calendar'])
63500
+ .event-timetable-time-label {
63279
63501
  position: absolute;
63280
63502
  left: 0.625rem;
63281
63503
  transform: translateY(-50%);
@@ -63284,14 +63506,18 @@ const containerResponsiveThemeCss = `/*
63284
63506
  white-space: nowrap;
63285
63507
  }
63286
63508
 
63287
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-hour-line {
63509
+ :where(.theme-scope)
63510
+ :where([data-block='event-calendar'])
63511
+ .event-timetable-hour-line {
63288
63512
  position: absolute;
63289
63513
  left: 0;
63290
63514
  right: 0;
63291
63515
  border-top: 1px solid var(--ec-border-subtle);
63292
63516
  }
63293
63517
 
63294
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event {
63518
+ :where(.theme-scope)
63519
+ :where([data-block='event-calendar'])
63520
+ .event-timetable-event {
63295
63521
  position: absolute;
63296
63522
  min-height: 2.75rem;
63297
63523
  padding: 0.375rem 0.5rem;
@@ -63311,21 +63537,35 @@ const containerResponsiveThemeCss = `/*
63311
63537
  transform var(--rb-block-transition) ease;
63312
63538
  }
63313
63539
 
63314
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event:hover {
63540
+ :where(.theme-scope)
63541
+ :where([data-block='event-calendar'])
63542
+ .event-timetable-event:hover {
63315
63543
  box-shadow: 0 4px 12px rgba(var(--tb-text), 0.12);
63316
63544
  transform: translateY(-1px);
63317
63545
  }
63318
63546
 
63319
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event:focus-visible {
63547
+ :where(.theme-scope)
63548
+ :where([data-block='event-calendar'])
63549
+ .event-timetable-event:focus-visible {
63320
63550
  outline: 2px solid rgb(var(--tb-primary));
63321
63551
  outline-offset: 2px;
63322
63552
  }
63323
63553
 
63324
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-time,
63325
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-title,
63326
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-teacher,
63327
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-meta,
63328
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-capacity {
63554
+ :where(.theme-scope)
63555
+ :where([data-block='event-calendar'])
63556
+ .event-timetable-event-time,
63557
+ :where(.theme-scope)
63558
+ :where([data-block='event-calendar'])
63559
+ .event-timetable-event-title,
63560
+ :where(.theme-scope)
63561
+ :where([data-block='event-calendar'])
63562
+ .event-timetable-event-teacher,
63563
+ :where(.theme-scope)
63564
+ :where([data-block='event-calendar'])
63565
+ .event-timetable-event-meta,
63566
+ :where(.theme-scope)
63567
+ :where([data-block='event-calendar'])
63568
+ .event-timetable-event-capacity {
63329
63569
  display: block;
63330
63570
  min-width: 0;
63331
63571
  overflow: hidden;
@@ -63333,36 +63573,50 @@ const containerResponsiveThemeCss = `/*
63333
63573
  white-space: nowrap;
63334
63574
  }
63335
63575
 
63336
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-time {
63576
+ :where(.theme-scope)
63577
+ :where([data-block='event-calendar'])
63578
+ .event-timetable-event-time {
63337
63579
  font-size: 0.6875rem;
63338
63580
  font-weight: 700;
63339
63581
  opacity: var(--ec-time-opacity);
63340
63582
  }
63341
63583
 
63342
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-title {
63584
+ :where(.theme-scope)
63585
+ :where([data-block='event-calendar'])
63586
+ .event-timetable-event-title {
63343
63587
  font-size: 0.8125rem;
63344
63588
  font-weight: 700;
63345
63589
  line-height: 1.25;
63346
63590
  }
63347
63591
 
63348
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-teacher,
63349
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-meta {
63592
+ :where(.theme-scope)
63593
+ :where([data-block='event-calendar'])
63594
+ .event-timetable-event-teacher,
63595
+ :where(.theme-scope)
63596
+ :where([data-block='event-calendar'])
63597
+ .event-timetable-event-meta {
63350
63598
  font-size: 0.6875rem;
63351
63599
  opacity: 0.72;
63352
63600
  }
63353
63601
 
63354
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-capacity {
63602
+ :where(.theme-scope)
63603
+ :where([data-block='event-calendar'])
63604
+ .event-timetable-event-capacity {
63355
63605
  font-size: 0.6875rem;
63356
63606
  font-weight: 600;
63357
63607
  opacity: 0.85;
63358
63608
  }
63359
63609
 
63360
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-event-capacity--full {
63610
+ :where(.theme-scope)
63611
+ :where([data-block='event-calendar'])
63612
+ .event-timetable-event-capacity--full {
63361
63613
  color: rgb(var(--tb-danger, 220 38 38));
63362
63614
  opacity: 1;
63363
63615
  }
63364
63616
 
63365
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-empty {
63617
+ :where(.theme-scope)
63618
+ :where([data-block='event-calendar'])
63619
+ .event-timetable-day-empty {
63366
63620
  margin: 0;
63367
63621
  padding: 1rem 0.5rem;
63368
63622
  text-align: center;
@@ -63370,7 +63624,9 @@ const containerResponsiveThemeCss = `/*
63370
63624
  opacity: var(--ec-empty-opacity);
63371
63625
  }
63372
63626
 
63373
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-empty {
63627
+ :where(.theme-scope)
63628
+ :where([data-block='event-calendar'])
63629
+ .event-timetable-empty {
63374
63630
  margin: 0.25rem 0 0;
63375
63631
  text-align: center;
63376
63632
  font-size: 0.875rem;
@@ -63379,32 +63635,46 @@ const containerResponsiveThemeCss = `/*
63379
63635
 
63380
63636
  @container rb-site (max-width: 767px) {
63381
63637
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
63382
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-selector {
63638
+ :where(.theme-scope)
63639
+ :where([data-block='event-calendar'])
63640
+ .event-timetable-day-selector {
63383
63641
  display: flex;
63384
63642
  }
63385
63643
 
63386
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-container {
63644
+ :where(.theme-scope)
63645
+ :where([data-block='event-calendar'])
63646
+ .event-timetable-container {
63387
63647
  overflow: hidden;
63388
63648
  }
63389
63649
 
63390
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-grid {
63650
+ :where(.theme-scope)
63651
+ :where([data-block='event-calendar'])
63652
+ .event-timetable-grid {
63391
63653
  grid-template-columns: 4rem minmax(0, 1fr);
63392
63654
  min-width: 0;
63393
63655
  }
63394
63656
 
63395
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day[data-selected="false"] {
63657
+ :where(.theme-scope)
63658
+ :where([data-block='event-calendar'])
63659
+ .event-timetable-day[data-selected='false'] {
63396
63660
  display: none;
63397
63661
  }
63398
63662
 
63399
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day {
63663
+ :where(.theme-scope)
63664
+ :where([data-block='event-calendar'])
63665
+ .event-timetable-day {
63400
63666
  border-right: none;
63401
63667
  }
63402
63668
 
63403
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-time-rail {
63669
+ :where(.theme-scope)
63670
+ :where([data-block='event-calendar'])
63671
+ .event-timetable-time-rail {
63404
63672
  border-right: 1px solid var(--ec-border-subtle);
63405
63673
  }
63406
63674
 
63407
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-timetable-day-heading {
63675
+ :where(.theme-scope)
63676
+ :where([data-block='event-calendar'])
63677
+ .event-timetable-day-heading {
63408
63678
  justify-content: flex-start;
63409
63679
  padding-inline: 0.75rem;
63410
63680
  }
@@ -63417,7 +63687,7 @@ const containerResponsiveThemeCss = `/*
63417
63687
  * three-state availability (available / filling / waitlist), and a footer CTA.
63418
63688
  * ----------------------------------------------------------------------------- */
63419
63689
 
63420
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board {
63690
+ :where(.theme-scope) :where([data-block='event-calendar']) .event-week-board {
63421
63691
  /* Status accent tokens — map the three availability states onto theme colours.
63422
63692
  * available -> secondary, filling -> accent, waitlist -> muted text. */
63423
63693
  --ec-status-available: rgb(var(--tb-secondary));
@@ -63434,17 +63704,20 @@ const containerResponsiveThemeCss = `/*
63434
63704
 
63435
63705
  /* Opt-in raised look: a soft drop shadow so the board lifts off the page.
63436
63706
  * Generic default (no [data-elevated]) stays flat. */
63437
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board[data-elevated] {
63707
+ :where(.theme-scope)
63708
+ :where([data-block='event-calendar'])
63709
+ .event-week-board[data-elevated] {
63438
63710
  box-shadow: 0 14px 34px -24px rgba(var(--tb-text), 0.35);
63439
63711
  }
63440
63712
 
63441
- /* Top bar: title + legend */
63442
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-top {
63443
- display: flex;
63713
+ /* Top bar: authored title + week navigation + legend */
63714
+ :where(.theme-scope)
63715
+ :where([data-block='event-calendar'])
63716
+ .event-week-board-top {
63717
+ display: grid;
63718
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
63444
63719
  align-items: center;
63445
- justify-content: space-between;
63446
63720
  gap: 1rem;
63447
- flex-wrap: wrap;
63448
63721
  padding: 1.1rem 1.5rem;
63449
63722
  /* surfaceAlt gives the header/footer a legible tint against the white day-grid
63450
63723
  * body — the faint \`--ec-header-bg\` (5% text) is invisible on light palettes. */
@@ -63452,7 +63725,10 @@ const containerResponsiveThemeCss = `/*
63452
63725
  border-bottom: 1px solid var(--ec-border-subtle);
63453
63726
  }
63454
63727
 
63455
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-title {
63728
+ :where(.theme-scope)
63729
+ :where([data-block='event-calendar'])
63730
+ .event-week-board-title {
63731
+ grid-column: 1;
63456
63732
  font-size: 0.8125rem;
63457
63733
  font-weight: 600;
63458
63734
  letter-spacing: 0.12em;
@@ -63460,7 +63736,67 @@ const containerResponsiveThemeCss = `/*
63460
63736
  color: rgb(var(--tb-primary));
63461
63737
  }
63462
63738
 
63463
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend {
63739
+ :where(.theme-scope)
63740
+ :where([data-block='event-calendar'])
63741
+ .event-week-board-navigation {
63742
+ grid-column: 2;
63743
+ display: flex;
63744
+ align-items: center;
63745
+ justify-content: center;
63746
+ gap: 0.65rem;
63747
+ }
63748
+
63749
+ :where(.theme-scope)
63750
+ :where([data-block='event-calendar'])
63751
+ .event-week-board-nav-button {
63752
+ display: inline-flex;
63753
+ align-items: center;
63754
+ justify-content: center;
63755
+ width: 2.25rem;
63756
+ height: 2.25rem;
63757
+ padding: 0;
63758
+ border: 1px solid var(--ec-border);
63759
+ border-radius: 999px;
63760
+ background: transparent;
63761
+ color: inherit;
63762
+ cursor: pointer;
63763
+ }
63764
+
63765
+ :where(.theme-scope)
63766
+ :where([data-block='event-calendar'])
63767
+ .event-week-board-nav-button:hover {
63768
+ background-color: var(--ec-hover-bg);
63769
+ }
63770
+
63771
+ :where(.theme-scope)
63772
+ :where([data-block='event-calendar'])
63773
+ .event-week-board-nav-button:focus-visible {
63774
+ outline: 2px solid rgb(var(--tb-primary));
63775
+ outline-offset: 2px;
63776
+ }
63777
+
63778
+ :where(.theme-scope)
63779
+ :where([data-block='event-calendar'])
63780
+ .event-week-board-range {
63781
+ min-width: 12rem;
63782
+ margin: 0;
63783
+ text-align: center;
63784
+ font-size: 0.875rem;
63785
+ font-weight: 600;
63786
+ }
63787
+
63788
+ :where(.theme-scope)
63789
+ :where([data-block='event-calendar'])
63790
+ .event-week-board-loading {
63791
+ font-weight: 400;
63792
+ opacity: 0.65;
63793
+ }
63794
+
63795
+ :where(.theme-scope)
63796
+ :where([data-block='event-calendar'])
63797
+ .event-week-board-legend {
63798
+ grid-column: 3;
63799
+ justify-self: end;
63464
63800
  display: flex;
63465
63801
  align-items: center;
63466
63802
  gap: 1.1rem;
@@ -63470,38 +63806,72 @@ const containerResponsiveThemeCss = `/*
63470
63806
  opacity: 0.75;
63471
63807
  }
63472
63808
 
63473
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-item {
63809
+ :where(.theme-scope)
63810
+ :where([data-block='event-calendar'])
63811
+ .event-week-board-error {
63812
+ display: flex;
63813
+ align-items: center;
63814
+ justify-content: center;
63815
+ gap: 0.75rem;
63816
+ padding: 0.75rem 1.5rem;
63817
+ border-bottom: 1px solid var(--ec-border-subtle);
63818
+ color: rgb(var(--tb-error, var(--tb-text)));
63819
+ }
63820
+
63821
+ :where(.theme-scope)
63822
+ :where([data-block='event-calendar'])
63823
+ .event-week-board-error
63824
+ p {
63825
+ margin: 0;
63826
+ font-size: 0.8125rem;
63827
+ }
63828
+
63829
+ :where(.theme-scope)
63830
+ :where([data-block='event-calendar'])
63831
+ .event-week-board-legend-item {
63474
63832
  display: inline-flex;
63475
63833
  align-items: center;
63476
63834
  gap: 0.4rem;
63477
63835
  }
63478
63836
 
63479
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot {
63837
+ :where(.theme-scope)
63838
+ :where([data-block='event-calendar'])
63839
+ .event-week-board-legend-dot {
63480
63840
  width: 0.55rem;
63481
63841
  height: 0.55rem;
63482
63842
  border-radius: 50%;
63483
63843
  display: inline-block;
63484
63844
  }
63485
63845
 
63486
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot[data-status="available"] {
63846
+ :where(.theme-scope)
63847
+ :where([data-block='event-calendar'])
63848
+ .event-week-board-legend-dot[data-status='available'] {
63487
63849
  background-color: var(--ec-status-available);
63488
63850
  }
63489
63851
 
63490
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot[data-status="filling"] {
63852
+ :where(.theme-scope)
63853
+ :where([data-block='event-calendar'])
63854
+ .event-week-board-legend-dot[data-status='filling'] {
63491
63855
  background-color: var(--ec-status-filling);
63492
63856
  }
63493
63857
 
63494
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot[data-status="waitlist"] {
63858
+ :where(.theme-scope)
63859
+ :where([data-block='event-calendar'])
63860
+ .event-week-board-legend-dot[data-status='waitlist'] {
63495
63861
  background-color: var(--ec-status-waitlist);
63496
63862
  }
63497
63863
 
63498
63864
  /* Day columns grid */
63499
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-grid {
63865
+ :where(.theme-scope)
63866
+ :where([data-block='event-calendar'])
63867
+ .event-week-board-grid {
63500
63868
  display: grid;
63501
63869
  grid-template-columns: repeat(var(--event-week-board-cols), minmax(0, 1fr));
63502
63870
  }
63503
63871
 
63504
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day {
63872
+ :where(.theme-scope)
63873
+ :where([data-block='event-calendar'])
63874
+ .event-week-board-day {
63505
63875
  display: flex;
63506
63876
  flex-direction: column;
63507
63877
  min-height: 15rem;
@@ -63511,11 +63881,15 @@ const containerResponsiveThemeCss = `/*
63511
63881
  border-right: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
63512
63882
  }
63513
63883
 
63514
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day:last-child {
63884
+ :where(.theme-scope)
63885
+ :where([data-block='event-calendar'])
63886
+ .event-week-board-day:last-child {
63515
63887
  border-right: none;
63516
63888
  }
63517
63889
 
63518
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-head {
63890
+ :where(.theme-scope)
63891
+ :where([data-block='event-calendar'])
63892
+ .event-week-board-day-head {
63519
63893
  display: flex;
63520
63894
  align-items: baseline;
63521
63895
  gap: 0.4rem;
@@ -63524,11 +63898,16 @@ const containerResponsiveThemeCss = `/*
63524
63898
  border-bottom: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
63525
63899
  }
63526
63900
 
63527
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day--today .event-week-board-day-head {
63901
+ :where(.theme-scope)
63902
+ :where([data-block='event-calendar'])
63903
+ .event-week-board-day--today
63904
+ .event-week-board-day-head {
63528
63905
  background-color: var(--ec-today-bg);
63529
63906
  }
63530
63907
 
63531
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-abbr {
63908
+ :where(.theme-scope)
63909
+ :where([data-block='event-calendar'])
63910
+ .event-week-board-day-abbr {
63532
63911
  font-size: 0.6875rem;
63533
63912
  font-weight: 600;
63534
63913
  letter-spacing: 0.1em;
@@ -63536,18 +63915,25 @@ const containerResponsiveThemeCss = `/*
63536
63915
  opacity: 0.65;
63537
63916
  }
63538
63917
 
63539
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day--today .event-week-board-day-abbr {
63918
+ :where(.theme-scope)
63919
+ :where([data-block='event-calendar'])
63920
+ .event-week-board-day--today
63921
+ .event-week-board-day-abbr {
63540
63922
  opacity: 1;
63541
63923
  color: var(--ec-today-text);
63542
63924
  }
63543
63925
 
63544
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-num {
63926
+ :where(.theme-scope)
63927
+ :where([data-block='event-calendar'])
63928
+ .event-week-board-day-num {
63545
63929
  font-size: 0.8125rem;
63546
63930
  font-weight: 600;
63547
63931
  opacity: 0.5;
63548
63932
  }
63549
63933
 
63550
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-body {
63934
+ :where(.theme-scope)
63935
+ :where([data-block='event-calendar'])
63936
+ .event-week-board-day-body {
63551
63937
  display: flex;
63552
63938
  flex-direction: column;
63553
63939
  gap: 0.65rem;
@@ -63556,7 +63942,9 @@ const containerResponsiveThemeCss = `/*
63556
63942
  }
63557
63943
 
63558
63944
  /* Class card */
63559
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card {
63945
+ :where(.theme-scope)
63946
+ :where([data-block='event-calendar'])
63947
+ .event-week-board-card {
63560
63948
  display: flex;
63561
63949
  flex-direction: column;
63562
63950
  gap: 0.45rem;
@@ -63576,40 +63964,54 @@ const containerResponsiveThemeCss = `/*
63576
63964
  border-color var(--rb-block-transition) ease;
63577
63965
  }
63578
63966
 
63579
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card[data-status="filling"] {
63967
+ :where(.theme-scope)
63968
+ :where([data-block='event-calendar'])
63969
+ .event-week-board-card[data-status='filling'] {
63580
63970
  border-left-color: var(--ec-status-filling);
63581
63971
  }
63582
63972
 
63583
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card[data-status="waitlist"] {
63973
+ :where(.theme-scope)
63974
+ :where([data-block='event-calendar'])
63975
+ .event-week-board-card[data-status='waitlist'] {
63584
63976
  border-left-color: var(--ec-status-waitlist);
63585
63977
  }
63586
63978
 
63587
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card:hover {
63979
+ :where(.theme-scope)
63980
+ :where([data-block='event-calendar'])
63981
+ .event-week-board-card:hover {
63588
63982
  transform: translateY(-2px);
63589
63983
  box-shadow: 0 6px 16px -8px rgba(var(--tb-text), 0.28);
63590
63984
  border-color: rgba(var(--tb-primary), 0.4);
63591
63985
  }
63592
63986
 
63593
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card:focus-visible {
63987
+ :where(.theme-scope)
63988
+ :where([data-block='event-calendar'])
63989
+ .event-week-board-card:focus-visible {
63594
63990
  outline: 2px solid rgb(var(--tb-primary));
63595
63991
  outline-offset: 2px;
63596
63992
  }
63597
63993
 
63598
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-title {
63994
+ :where(.theme-scope)
63995
+ :where([data-block='event-calendar'])
63996
+ .event-week-board-card-title {
63599
63997
  font-family: var(--font-heading, inherit);
63600
63998
  font-size: 0.9375rem;
63601
63999
  font-weight: 600;
63602
64000
  line-height: 1.2;
63603
64001
  }
63604
64002
 
63605
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-time {
64003
+ :where(.theme-scope)
64004
+ :where([data-block='event-calendar'])
64005
+ .event-week-board-card-time {
63606
64006
  font-size: 0.75rem;
63607
64007
  letter-spacing: 0.01em;
63608
64008
  opacity: 0.7;
63609
64009
  }
63610
64010
 
63611
64011
  /* Capacity pill */
63612
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-pill {
64012
+ :where(.theme-scope)
64013
+ :where([data-block='event-calendar'])
64014
+ .event-week-board-card-pill {
63613
64015
  align-self: flex-start;
63614
64016
  display: inline-flex;
63615
64017
  align-items: center;
@@ -63627,20 +64029,26 @@ const containerResponsiveThemeCss = `/*
63627
64029
  border-color: var(--ec-border);
63628
64030
  }
63629
64031
 
63630
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-pill[data-status="filling"] {
64032
+ :where(.theme-scope)
64033
+ :where([data-block='event-calendar'])
64034
+ .event-week-board-card-pill[data-status='filling'] {
63631
64035
  background-color: rgba(var(--tb-accent), 0.14);
63632
64036
  color: var(--ec-status-filling);
63633
64037
  border-color: rgba(var(--tb-accent), 0.32);
63634
64038
  }
63635
64039
 
63636
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-pill[data-status="waitlist"] {
64040
+ :where(.theme-scope)
64041
+ :where([data-block='event-calendar'])
64042
+ .event-week-board-card-pill[data-status='waitlist'] {
63637
64043
  background-color: transparent;
63638
64044
  color: rgba(var(--tb-text), 0.6);
63639
64045
  border-color: var(--ec-border);
63640
64046
  }
63641
64047
 
63642
64048
  /* Footer */
63643
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-foot {
64049
+ :where(.theme-scope)
64050
+ :where([data-block='event-calendar'])
64051
+ .event-week-board-foot {
63644
64052
  display: flex;
63645
64053
  align-items: center;
63646
64054
  justify-content: space-between;
@@ -63652,7 +64060,9 @@ const containerResponsiveThemeCss = `/*
63652
64060
  border-top: 1px solid var(--ec-border-subtle);
63653
64061
  }
63654
64062
 
63655
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-foot-note {
64063
+ :where(.theme-scope)
64064
+ :where([data-block='event-calendar'])
64065
+ .event-week-board-foot-note {
63656
64066
  margin: 0;
63657
64067
  font-size: 0.875rem;
63658
64068
  opacity: 0.75;
@@ -63661,36 +64071,69 @@ const containerResponsiveThemeCss = `/*
63661
64071
  /* Responsive: collapse day columns to a stacked list on narrow viewports */
63662
64072
  @container rb-site (max-width: 1023px) {
63663
64073
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
63664
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-grid {
64074
+ :where(.theme-scope)
64075
+ :where([data-block='event-calendar'])
64076
+ .event-week-board-top {
64077
+ grid-template-columns: 1fr;
64078
+ }
64079
+
64080
+ :where(.theme-scope)
64081
+ :where([data-block='event-calendar'])
64082
+ .event-week-board-title,
64083
+ :where(.theme-scope)
64084
+ :where([data-block='event-calendar'])
64085
+ .event-week-board-navigation,
64086
+ :where(.theme-scope)
64087
+ :where([data-block='event-calendar'])
64088
+ .event-week-board-legend {
64089
+ grid-column: 1;
64090
+ justify-self: center;
64091
+ }
64092
+
64093
+ :where(.theme-scope)
64094
+ :where([data-block='event-calendar'])
64095
+ .event-week-board-grid {
63665
64096
  grid-template-columns: 1fr;
63666
64097
  }
63667
64098
 
63668
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day {
64099
+ :where(.theme-scope)
64100
+ :where([data-block='event-calendar'])
64101
+ .event-week-board-day {
63669
64102
  min-height: 0;
63670
64103
  border-right: none;
63671
64104
  border-bottom: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
63672
64105
  }
63673
64106
 
63674
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day:last-child {
64107
+ :where(.theme-scope)
64108
+ :where([data-block='event-calendar'])
64109
+ .event-week-board-day:last-child {
63675
64110
  border-bottom: none;
63676
64111
  }
63677
64112
 
63678
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day--empty {
64113
+ :where(.theme-scope)
64114
+ :where([data-block='event-calendar'])
64115
+ .event-week-board-day--empty {
63679
64116
  display: none;
63680
64117
  }
63681
64118
 
63682
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-head {
64119
+ :where(.theme-scope)
64120
+ :where([data-block='event-calendar'])
64121
+ .event-week-board-day-head {
63683
64122
  border-bottom: none;
63684
64123
  padding-bottom: 0.25rem;
63685
64124
  }
63686
64125
 
63687
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-body {
64126
+ :where(.theme-scope)
64127
+ :where([data-block='event-calendar'])
64128
+ .event-week-board-day-body {
63688
64129
  flex-direction: row;
63689
64130
  flex-wrap: wrap;
63690
64131
  padding-top: 0.35rem;
63691
64132
  }
63692
64133
 
63693
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card {
64134
+ :where(.theme-scope)
64135
+ :where([data-block='event-calendar'])
64136
+ .event-week-board-card {
63694
64137
  flex: 1 1 14rem;
63695
64138
  }
63696
64139
  }
@@ -63698,11 +64141,37 @@ const containerResponsiveThemeCss = `/*
63698
64141
 
63699
64142
  @container rb-site (max-width: 639px) {
63700
64143
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
63701
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend {
64144
+ :where(.theme-scope)
64145
+ :where([data-block='event-calendar'])
64146
+ .event-week-board-navigation {
64147
+ display: grid;
64148
+ grid-template-columns: 2.25rem minmax(0, 1fr) 2.25rem;
64149
+ width: 100%;
64150
+ }
64151
+
64152
+ :where(.theme-scope)
64153
+ :where([data-block='event-calendar'])
64154
+ .event-week-board-navigation
64155
+ > :last-child {
64156
+ grid-column: 1 / -1;
64157
+ justify-self: center;
64158
+ }
64159
+
64160
+ :where(.theme-scope)
64161
+ :where([data-block='event-calendar'])
64162
+ .event-week-board-range {
64163
+ min-width: 0;
64164
+ }
64165
+
64166
+ :where(.theme-scope)
64167
+ :where([data-block='event-calendar'])
64168
+ .event-week-board-legend {
63702
64169
  display: none;
63703
64170
  }
63704
64171
 
63705
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card {
64172
+ :where(.theme-scope)
64173
+ :where([data-block='event-calendar'])
64174
+ .event-week-board-card {
63706
64175
  flex: 1 1 100%;
63707
64176
  }
63708
64177
  }
@@ -63719,8 +64188,9 @@ const containerResponsiveThemeCss = `/*
63719
64188
  * Opt-in only: the band paints when the board root carries [data-banded];
63720
64189
  * the generic default is bandless.
63721
64190
  * ----------------------------------------------------------------------------- */
63722
- :where(.theme-scope) [data-slot="section"]:has(.event-calendar--week-board[data-banded])::before {
63723
- content: "";
64191
+ :where(.theme-scope)
64192
+ [data-slot='section']:has(.event-calendar--week-board[data-banded])::before {
64193
+ content: '';
63724
64194
  position: absolute;
63725
64195
  inset: 0;
63726
64196
  z-index: -9;
@@ -63774,7 +64244,7 @@ const containerResponsiveThemeCss = `/*
63774
64244
  * ----------------------------------------------------------------------------- */
63775
64245
 
63776
64246
  /* Modal overlay */
63777
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-overlay {
64247
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-overlay {
63778
64248
  position: fixed;
63779
64249
  inset: 0;
63780
64250
  background: var(--ec-modal-overlay-bg);
@@ -63788,12 +64258,16 @@ const containerResponsiveThemeCss = `/*
63788
64258
  }
63789
64259
 
63790
64260
  @keyframes ec-modal-fade-in {
63791
- from { opacity: 0; }
63792
- to { opacity: 1; }
64261
+ from {
64262
+ opacity: 0;
64263
+ }
64264
+ to {
64265
+ opacity: 1;
64266
+ }
63793
64267
  }
63794
64268
 
63795
64269
  /* Modal dialog */
63796
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal {
64270
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal {
63797
64271
  position: relative;
63798
64272
  background: var(--ec-modal-bg);
63799
64273
  border-radius: var(--rb-block-radius);
@@ -63817,17 +64291,17 @@ const containerResponsiveThemeCss = `/*
63817
64291
  }
63818
64292
 
63819
64293
  /* Event modal - constrained width for card */
63820
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal--event {
64294
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal--event {
63821
64295
  max-width: 26rem;
63822
64296
  }
63823
64297
 
63824
64298
  /* Day modal */
63825
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal--day {
64299
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal--day {
63826
64300
  max-width: 28rem;
63827
64301
  }
63828
64302
 
63829
64303
  /* Close button */
63830
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-close {
64304
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-close {
63831
64305
  position: absolute;
63832
64306
  top: 0.75rem;
63833
64307
  right: 0.75rem;
@@ -63848,31 +64322,35 @@ const containerResponsiveThemeCss = `/*
63848
64322
  z-index: 10;
63849
64323
  }
63850
64324
 
63851
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-close:hover {
64325
+ :where(.theme-scope)
64326
+ :where([data-block='event-calendar'])
64327
+ .ec-modal-close:hover {
63852
64328
  opacity: 1;
63853
64329
  background-color: rgba(var(--tb-text), 0.1);
63854
64330
  }
63855
64331
 
63856
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-close:focus-visible {
64332
+ :where(.theme-scope)
64333
+ :where([data-block='event-calendar'])
64334
+ .ec-modal-close:focus-visible {
63857
64335
  outline: 2px solid rgb(var(--tb-primary));
63858
64336
  outline-offset: 2px;
63859
64337
  }
63860
64338
 
63861
64339
  /* Modal header (for day modal) */
63862
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-header {
64340
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-header {
63863
64341
  padding: 1.5rem 1.5rem 1rem;
63864
64342
  padding-right: 3rem; /* Space for close button */
63865
64343
  border-bottom: 1px solid var(--ec-border);
63866
64344
  }
63867
64345
 
63868
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-title {
64346
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-title {
63869
64347
  font-size: 1.125rem;
63870
64348
  font-weight: 600;
63871
64349
  color: var(--ec-text);
63872
64350
  margin: 0;
63873
64351
  }
63874
64352
 
63875
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-subtitle {
64353
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-subtitle {
63876
64354
  font-size: 0.875rem;
63877
64355
  color: var(--ec-text);
63878
64356
  opacity: 0.7;
@@ -63880,17 +64358,20 @@ const containerResponsiveThemeCss = `/*
63880
64358
  }
63881
64359
 
63882
64360
  /* Modal content */
63883
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal-content {
64361
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal-content {
63884
64362
  padding: 1rem;
63885
64363
  }
63886
64364
 
63887
64365
  /* Event modal: tighter padding for card */
63888
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal--event .ec-modal-content {
64366
+ :where(.theme-scope)
64367
+ :where([data-block='event-calendar'])
64368
+ .ec-modal--event
64369
+ .ec-modal-content {
63889
64370
  padding: 0.5rem;
63890
64371
  }
63891
64372
 
63892
64373
  /* Day events list */
63893
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-day-events-list {
64374
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-day-events-list {
63894
64375
  display: flex;
63895
64376
  flex-direction: column;
63896
64377
  gap: 0.5rem;
@@ -63898,7 +64379,9 @@ const containerResponsiveThemeCss = `/*
63898
64379
  }
63899
64380
 
63900
64381
  /* AgendaEventCard in button mode inherits most styles, just needs cursor */
63901
- :where(.theme-scope) :where([data-block="event-calendar"]) button.event-agenda-card {
64382
+ :where(.theme-scope)
64383
+ :where([data-block='event-calendar'])
64384
+ button.event-agenda-card {
63902
64385
  cursor: pointer;
63903
64386
  text-align: left;
63904
64387
  font-family: inherit;
@@ -63907,7 +64390,9 @@ const containerResponsiveThemeCss = `/*
63907
64390
  /* Responsive adjustments */
63908
64391
  @container rb-site (max-width: 640px) {
63909
64392
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
63910
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-container {
64393
+ :where(.theme-scope)
64394
+ :where([data-block='event-calendar'])
64395
+ .event-calendar-container {
63911
64396
  margin-left: -1.5rem;
63912
64397
  margin-right: -1.5rem;
63913
64398
  border-left: none;
@@ -63915,28 +64400,38 @@ const containerResponsiveThemeCss = `/*
63915
64400
  border-radius: 0;
63916
64401
  }
63917
64402
 
63918
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-header {
64403
+ :where(.theme-scope)
64404
+ :where([data-block='event-calendar'])
64405
+ .event-calendar-day-header {
63919
64406
  font-size: 0.75rem;
63920
64407
  padding: 0.375rem 0.125rem;
63921
64408
  }
63922
64409
 
63923
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day {
64410
+ :where(.theme-scope)
64411
+ :where([data-block='event-calendar'])
64412
+ .event-calendar-day {
63924
64413
  min-height: 88px;
63925
64414
  padding: 0.125rem;
63926
64415
  }
63927
64416
 
63928
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-title {
64417
+ :where(.theme-scope)
64418
+ :where([data-block='event-calendar'])
64419
+ .event-calendar-title {
63929
64420
  font-size: 0.9375rem;
63930
64421
  min-width: 0;
63931
64422
  }
63932
64423
 
63933
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-day-number {
64424
+ :where(.theme-scope)
64425
+ :where([data-block='event-calendar'])
64426
+ .event-calendar-day-number {
63934
64427
  font-size: 0.75rem;
63935
64428
  margin-bottom: 0.125rem;
63936
64429
  padding: 0 0.125rem;
63937
64430
  }
63938
64431
 
63939
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event {
64432
+ :where(.theme-scope)
64433
+ :where([data-block='event-calendar'])
64434
+ .event-calendar-event {
63940
64435
  justify-content: center;
63941
64436
  gap: 0.25rem;
63942
64437
  padding: 0.1875rem 0.25rem;
@@ -63944,25 +64439,34 @@ const containerResponsiveThemeCss = `/*
63944
64439
  border-radius: calc(var(--rb-block-radius) / 3);
63945
64440
  }
63946
64441
 
63947
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event-time {
64442
+ :where(.theme-scope)
64443
+ :where([data-block='event-calendar'])
64444
+ .event-calendar-event-time {
63948
64445
  text-align: center;
63949
64446
  font-size: 0.625rem;
63950
64447
  }
63951
64448
 
63952
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-event-title {
64449
+ :where(.theme-scope)
64450
+ :where([data-block='event-calendar'])
64451
+ .event-calendar-event-title {
63953
64452
  display: none;
63954
64453
  }
63955
64454
 
63956
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-more {
64455
+ :where(.theme-scope)
64456
+ :where([data-block='event-calendar'])
64457
+ .event-calendar-more {
63957
64458
  padding: 0.125rem 0.25rem;
63958
64459
  font-size: 0.6875rem;
63959
64460
  }
63960
64461
 
63961
- :where(.theme-scope) :where([data-block="event-calendar"]) .event-calendar-header > button {
64462
+ :where(.theme-scope)
64463
+ :where([data-block='event-calendar'])
64464
+ .event-calendar-header
64465
+ > button {
63962
64466
  margin-right: 0.25rem;
63963
64467
  }
63964
64468
 
63965
- :where(.theme-scope) :where([data-block="event-calendar"]) .ec-modal {
64469
+ :where(.theme-scope) :where([data-block='event-calendar']) .ec-modal {
63966
64470
  max-width: 100%;
63967
64471
  max-height: 85vh;
63968
64472
  margin: 0 0.5rem;
@@ -68764,16 +69268,21 @@ function toBlocksPageRendererDataContext(dataContext) {
68764
69268
  imageResolveContext: toBlocksImageResolveContextInput(dataContext.imageResolveContext)
68765
69269
  };
68766
69270
  }
68767
- function PageRenderer(props) {
68768
- const blocksSdkConfig = props.sdkConfig ? { customBlocks: normalizeSdkCustomBlocks(props.sdkConfig.customBlocks) } : props.sdkConfig;
68769
- const { rendering: _rendering, theme: _theme, themeTokens: _themeTokens, ...commonProps } = props;
68770
- const commonBlocksProps = {
68771
- ...commonProps,
69271
+ function toBlocksCommonProps(props) {
69272
+ return {
69273
+ page: props.page,
69274
+ runtimeBoundaries: props.runtimeBoundaries,
68772
69275
  dataContext: props.dataContext ? toBlocksPageRendererDataContext(props.dataContext) : void 0,
69276
+ routeMap: props.routeMap,
69277
+ wrapBlock: props.wrapBlock,
69278
+ registry: props.registry,
68773
69279
  blockOverrides: toBlocksBlockOverrides(props.blockOverrides),
68774
- sdkConfig: blocksSdkConfig,
69280
+ sdkConfig: props.sdkConfig ? { customBlocks: normalizeSdkCustomBlocks(props.sdkConfig.customBlocks) } : props.sdkConfig,
68775
69281
  customBlocks: normalizeSdkCustomBlocks(props.customBlocks)
68776
69282
  };
69283
+ }
69284
+ function PageRenderer(props) {
69285
+ const commonBlocksProps = toBlocksCommonProps(props);
68777
69286
  const blocksProps = isOverrideOnlyPageRenderer(props) ? {
68778
69287
  ...commonBlocksProps,
68779
69288
  rendering: { kind: "overrides-only" },
@@ -68856,12 +69365,14 @@ function toPageHydrationBootstrapConfig(props) {
68856
69365
  mediaAssets: props.mediaAssets
68857
69366
  };
68858
69367
  }
69368
+ function resolveSdkBookingTimeZone(timeZone) {
69369
+ return timeZone ?? "UTC";
69370
+ }
68859
69371
  function PageContent(props) {
68860
- const { page, runtimeBoundaries, siteId, resolvedData, bookingOfferingPresets, routeMap, pagesByIdentifier, entriesByIdentifier, wrapBlock, registry, usePlaceholders = false, blockOverrides, sdkConfig, customBlocks, supabaseUrl, mediaAssets, apiBaseUrl, portalToken, previewToken, dataContext } = props;
69372
+ const { page, runtimeBoundaries, siteId, resolvedData, bookingOfferingPresets, bookingTimeZone, routeMap, pagesByIdentifier, entriesByIdentifier, wrapBlock, registry, blockOverrides, sdkConfig, customBlocks, supabaseUrl, mediaAssets, apiBaseUrl, portalToken, previewToken, dataContext } = props;
68861
69373
  const rendererCommonProps = {
68862
69374
  page,
68863
69375
  runtimeBoundaries,
68864
- usePlaceholders,
68865
69376
  dataContext: {
68866
69377
  siteId,
68867
69378
  apiBaseUrl,
@@ -68869,6 +69380,7 @@ function PageContent(props) {
68869
69380
  previewToken,
68870
69381
  resolvedData,
68871
69382
  bookingOfferingPresets: bookingOfferingPresets ?? [],
69383
+ bookingTimeZone: resolveSdkBookingTimeZone(bookingTimeZone),
68872
69384
  routes: routeMap,
68873
69385
  pagesByIdentifier,
68874
69386
  entriesByIdentifier,
@@ -69452,233 +69964,6 @@ function requireSdkTheme(theme, siteId, reason = "cms_theme_absent") {
69452
69964
  //#region src/rendering/renderingMode.ts
69453
69965
  const defaultOverrideOnlyRuntime = { kind: "responsive-only" };
69454
69966
  //#endregion
69455
- //#region src/rendering/components/Layout.tsx
69456
- /**
69457
- * Layout component with header and footer
69458
- *
69459
- * Renders site header and footer around content. Fetches site data if not provided.
69460
- */
69461
- function isOverrideOnlyLayout(props) {
69462
- return props.rendering?.kind === "overrides-only";
69463
- }
69464
- var UnsupportedOverrideOnlyLayoutChromeError = class extends Error {
69465
- constructor(surface) {
69466
- super(`Override-only Layout requires a custom or disabled ${surface}; SDK default chrome is unavailable.`);
69467
- this.surface = surface;
69468
- this.code = "unsupported_override_only_layout_chrome";
69469
- this.name = "UnsupportedOverrideOnlyLayoutChromeError";
69470
- }
69471
- };
69472
- async function resolveLayoutSiteData(props) {
69473
- if (props.siteData) return props.siteData;
69474
- if (!props.client) throw new Error("Layout: must provide either siteData or client");
69475
- if (!props.siteId && !props.slug && !props.domain) throw new Error("Layout: must provide siteId, slug, or domain when using client");
69476
- return props.client.getSite({
69477
- id: props.siteId,
69478
- slug: props.slug,
69479
- domain: props.domain
69480
- });
69481
- }
69482
- function renderOverrideOnlyLayout(props, data) {
69483
- if (data.header === true) throw new UnsupportedOverrideOnlyLayoutChromeError("header");
69484
- if (data.footer === true) throw new UnsupportedOverrideOnlyLayoutChromeError("footer");
69485
- return renderWithOverrideOnlyRuntime({
69486
- runtime: props.rendering.runtime ?? defaultOverrideOnlyRuntime,
69487
- children: /* @__PURE__ */ jsxs(Fragment, { children: [
69488
- typeof props.header === "function" ? props.header(data.headerData) : null,
69489
- props.children,
69490
- typeof props.footer === "function" ? props.footer(data.footerData) : null
69491
- ] })
69492
- });
69493
- }
69494
- /**
69495
- * Layout component that wraps content with site header and footer.
69496
- *
69497
- * This is a server/RSC-safe component. The default `@riverbankcms/sdk/rendering`
69498
- * `Page` and `Block` surfaces mount CMS island hydration automatically, while
69499
- * the strict `@riverbankcms/sdk/rendering/server` surface stays bootstrap-free.
69500
- * If a custom host renders hydratable CMS islands through the strict server
69501
- * surface, mount `BlocksIslandsHydrator` from
69502
- * `@riverbankcms/sdk/rendering/islands` explicitly.
69503
- *
69504
- * @example With pre-fetched site data (recommended)
69505
- * ```tsx
69506
- * import { Layout } from '@riverbankcms/sdk/rendering/server';
69507
- * import { Page } from '@riverbankcms/sdk/rendering';
69508
- *
69509
- * const site = await client.getSite({ slug: 'my-site' });
69510
- *
69511
- * <Layout siteData={site}>
69512
- * <Page {...pageData} />
69513
- * </Layout>
69514
- * ```
69515
- *
69516
- * @example With strict server rendering
69517
- * ```tsx
69518
- * import { Layout, Page } from '@riverbankcms/sdk/rendering/server';
69519
- * import { BlocksIslandsHydrator } from '@riverbankcms/sdk/rendering/islands';
69520
- *
69521
- * const site = await client.getSite({ slug: 'my-site' });
69522
- *
69523
- * <Layout siteData={site}>
69524
- * <BlocksIslandsHydrator siteId={site.site.id} supabaseUrl={site.supabaseUrl} />
69525
- * <Page {...pageData} />
69526
- * </Layout>
69527
- * ```
69528
- */
69529
- async function Layout(props) {
69530
- const { children, hasPortalSession = false, previewStage = "published" } = props;
69531
- const header = props.header ?? true;
69532
- const footer = props.footer ?? true;
69533
- const siteData = await resolveLayoutSiteData(props);
69534
- const { site, navigation, layout, routes } = siteData;
69535
- const footerBlocks = siteData.footerBlocks ?? [];
69536
- const navigationOptions = {
69537
- hasPortalSession,
69538
- previewStage,
69539
- pagesByIdentifier: siteData.pagesByIdentifier,
69540
- entriesByIdentifier: siteData.entriesByIdentifier,
69541
- portalEnabled: siteData.portalEnabled ?? false,
69542
- commerceEnabled: siteData.commerceEnabled ?? false,
69543
- checkoutHref: "/checkout"
69544
- };
69545
- const menuViewModel = buildMenu(navigation, routes, navigationOptions);
69546
- const footerMenuViewModel = buildFooterMenu(navigation, routes, navigationOptions);
69547
- const legalFooterMenuViewModel = buildFooterMenuForRole(navigation, routes, "legal-policy", navigationOptions);
69548
- const logoViewModel = buildLogo(layout.logo ?? null, site.title);
69549
- const customHeaderData = {
69550
- menu: menuViewModel,
69551
- logo: logoViewModel,
69552
- site,
69553
- routes
69554
- };
69555
- const customFooterData = {
69556
- menu: menuViewModel,
69557
- footerMenu: footerMenuViewModel,
69558
- legalFooterMenu: legalFooterMenuViewModel,
69559
- logo: logoViewModel,
69560
- site,
69561
- routes
69562
- };
69563
- if (isOverrideOnlyLayout(props)) return renderOverrideOnlyLayout(props, {
69564
- header: props.header ?? true,
69565
- footer: props.footer ?? true,
69566
- headerData: customHeaderData,
69567
- footerData: customFooterData
69568
- });
69569
- const theme = requireSdkTheme(siteData.theme, site.id);
69570
- const builtThemeRuntime = buildThemeRuntime(theme);
69571
- const headerVariant = props.headerVariant;
69572
- const themeRuntime = props.themeRuntime;
69573
- const headerData = {
69574
- ...customHeaderData,
69575
- theme
69576
- };
69577
- const footerData = {
69578
- ...customFooterData,
69579
- theme
69580
- };
69581
- const headerContent = headerVariant ? {
69582
- ...layout.header,
69583
- variant: headerVariant
69584
- } : layout.header;
69585
- const themeWithVariant = headerVariant ? {
69586
- ...theme,
69587
- header: {
69588
- ...theme.header,
69589
- variant: headerVariant
69590
- }
69591
- } : theme;
69592
- const blocksThemeWithVariant = themeWithVariant;
69593
- const blocksTheme = theme;
69594
- let apiBaseUrl;
69595
- try {
69596
- apiBaseUrl = getRiverbankEnv().baseUrl;
69597
- } catch {}
69598
- const sharedViewModelOverrides = {
69599
- $root: {
69600
- siteId: site.id,
69601
- apiBaseUrl,
69602
- previewStage,
69603
- routes,
69604
- pagesByIdentifier: siteData.pagesByIdentifier,
69605
- entriesByIdentifier: siteData.entriesByIdentifier,
69606
- theme: themeWithVariant
69607
- },
69608
- site,
69609
- menu: menuViewModel,
69610
- footerMenu: footerMenuViewModel,
69611
- legalFooterMenu: legalFooterMenuViewModel
69612
- };
69613
- const headerViewModelOverrides = {
69614
- ...sharedViewModelOverrides,
69615
- content: { logo: logoViewModel }
69616
- };
69617
- const footerViewModelOverrides = {
69618
- ...sharedViewModelOverrides,
69619
- content: {
69620
- ...layout.footer,
69621
- logo: logoViewModel
69622
- }
69623
- };
69624
- let headerElement = null;
69625
- if (header === true) headerElement = renderBlock(siteHeaderManifest, headerContent, {
69626
- theme: builtThemeRuntime.tokens,
69627
- themeConfig: blocksThemeWithVariant,
69628
- viewModelOverrides: headerViewModelOverrides
69629
- });
69630
- else if (typeof header === "function") headerElement = header(headerData);
69631
- let footerElement = null;
69632
- if (footer === true) {
69633
- const footerPlan = resolveFooterRenderPlan({
69634
- isSdkSite: Boolean(site.is_sdk_site),
69635
- mode: theme.footer?.mode
69636
- });
69637
- const footerBlocksElement = footerPlan.renderFooterBlocks && footerBlocks.length > 0 ? /* @__PURE__ */ jsx(PageRenderer, {
69638
- theme,
69639
- page: {
69640
- name: "Footer",
69641
- path: "/__footer",
69642
- purpose: "footer",
69643
- blocks: footerBlocks
69644
- },
69645
- routeMap: routes,
69646
- dataContext: {
69647
- previewStage,
69648
- siteId: site.id,
69649
- apiBaseUrl,
69650
- routes,
69651
- pagesByIdentifier: siteData.pagesByIdentifier,
69652
- entriesByIdentifier: siteData.entriesByIdentifier
69653
- },
69654
- sdkConfig: siteData.sdkConfig ?? null
69655
- }) : null;
69656
- if (footerPlan.renderFooterBlocks && !footerBlocksElement && process.env.NODE_ENV !== "production") console.debug("[sdk] Footer mode renders blocks, but footerBlocks is empty/missing", {
69657
- siteId: site.id,
69658
- mode: footerPlan.mode
69659
- });
69660
- const defaultFooterElement = footerPlan.renderDefaultFooter ? renderBlock(siteFooterManifest, layout.footer, {
69661
- theme: builtThemeRuntime.tokens,
69662
- themeConfig: blocksTheme,
69663
- viewModelOverrides: footerViewModelOverrides
69664
- }) : null;
69665
- footerElement = footerPlan.mode === "default+blocks" ? /* @__PURE__ */ jsxs(Fragment, { children: [footerBlocksElement, defaultFooterElement] }) : footerPlan.mode === "blocks" ? footerBlocksElement : footerPlan.mode === "none" ? null : defaultFooterElement;
69666
- } else if (typeof footer === "function") footerElement = footer(footerData);
69667
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
69668
- headerElement,
69669
- children,
69670
- footerElement
69671
- ] });
69672
- return renderWithSdkThemeRuntime({
69673
- theme,
69674
- themeRuntime: planLayoutThemeRuntime({
69675
- themeRuntime,
69676
- usesSdkChrome: header === true || footer === true
69677
- }),
69678
- children: content
69679
- });
69680
- }
69681
- //#endregion
69682
69967
  //#region ../blocks/src/structure/schema.ts
69683
69968
  const BlockKind = z.string();
69684
69969
  const blockSchema = z.object({
@@ -70943,6 +71228,346 @@ function parseBackgroundColorToken(value) {
70943
71228
  if (!token) return null;
70944
71229
  return token ? token : null;
70945
71230
  }
71231
+ //#endregion
71232
+ //#region src/rendering/helpers/siteCustomBlocks.ts
71233
+ /**
71234
+ * Prefer the DB-backed custom block manifest list (`site.customBlocks`) when available,
71235
+ * but fall back to SDK config custom blocks (`site.sdkConfig.customBlocks`) for SDK-mode sites.
71236
+ *
71237
+ * This avoids subtle DX bugs where one source is populated but the renderer/prefetch layer
71238
+ * is looking at the other.
71239
+ */
71240
+ function resolveSiteCustomBlocks(site) {
71241
+ const dbBlocks = site.customBlocks;
71242
+ if (Array.isArray(dbBlocks) && dbBlocks.length > 0) return dbBlocks;
71243
+ const sdkBlocks = site.sdkConfig?.customBlocks;
71244
+ if (Array.isArray(sdkBlocks) && sdkBlocks.length > 0) return sdkBlocks;
71245
+ }
71246
+ function convertPageResponseBlock(block) {
71247
+ if (!isRecord$8(block)) throw new Error("Invalid block format in page API response");
71248
+ if (typeof block.id !== "string" && block.id !== null) throw new Error(`Invalid block id in page: expected string or null, got ${typeof block.id}`);
71249
+ if (typeof block.kind !== "string") throw new Error(`Invalid block kind in page: expected string, got ${typeof block.kind}`);
71250
+ if (typeof block.purpose !== "string") throw new Error(`Invalid block purpose in page: expected string, got ${typeof block.purpose}`);
71251
+ const admission = readInvalidBlockAdmission(block.admission);
71252
+ return {
71253
+ id: block.id,
71254
+ kind: block.kind,
71255
+ purpose: block.purpose,
71256
+ content: sanitizeSdkRuntimeBlockContent(block.kind, readContentRecord(block.content) ?? {}),
71257
+ draftContent: sanitizeNullableSdkRuntimeBlockContent(block.kind, readNestedContentRecord(block.draftContent)),
71258
+ ...admission ? { admission } : {}
71259
+ };
71260
+ }
71261
+ function readInvalidBlockAdmission(value) {
71262
+ if (!isRecord$8(value) || value.kind !== "invalid" || !isRecord$8(value.error)) return null;
71263
+ if (value.error.kind !== "invalid_manifest_content" && value.error.kind !== "manifest_unavailable") return null;
71264
+ return value;
71265
+ }
71266
+ function buildPageOutline(input) {
71267
+ return {
71268
+ name: input.name,
71269
+ path: input.path ?? input.fallbackPath,
71270
+ purpose: input.purpose,
71271
+ blocks: input.blocks.map(convertPageResponseBlock)
71272
+ };
71273
+ }
71274
+ function buildRenderablePageOutline(input) {
71275
+ const page = buildPageOutline(input);
71276
+ return {
71277
+ ...page,
71278
+ blocks: page.blocks.map((block) => admitSdkRuntimePageBlock(block, input.customBlocks))
71279
+ };
71280
+ }
71281
+ function admitSdkRuntimePageBlock(block, customBlocks) {
71282
+ const base = {
71283
+ id: block.id,
71284
+ kind: block.kind,
71285
+ purpose: block.purpose,
71286
+ ...block.bindings ? { bindings: block.bindings } : {}
71287
+ };
71288
+ const transportedAdmission = readInvalidBlockAdmission(block.admission);
71289
+ if (transportedAdmission) return invalidRenderableBlock(base, transportedAdmission.error);
71290
+ const manifest = resolveSdkBlockManifest(block.kind, customBlocks);
71291
+ if (!manifest) return invalidRenderableBlock(base, { kind: "manifest_unavailable" });
71292
+ const content = block.content ?? {};
71293
+ const renderable = admitTransportedRenderableManifestContent(compileManifestContentContract(manifest), content);
71294
+ if (!renderable.ok) return invalidRenderableBlock(base, {
71295
+ kind: "invalid_manifest_content",
71296
+ error: renderable.error
71297
+ });
71298
+ return {
71299
+ ...base,
71300
+ admission: {
71301
+ kind: "renderable",
71302
+ content: renderable.value
71303
+ }
71304
+ };
71305
+ }
71306
+ function resolveSdkBlockManifest(blockKind, customBlocks) {
71307
+ const customBlock = customBlocks?.find((candidate) => candidate.id === blockKind);
71308
+ return customBlock ? parseFieldDefinitions(customBlock.fields) : getBlockDefinition(blockKind)?.manifest ?? null;
71309
+ }
71310
+ function invalidRenderableBlock(block, error) {
71311
+ return {
71312
+ ...block,
71313
+ admission: {
71314
+ kind: "invalid",
71315
+ error
71316
+ }
71317
+ };
71318
+ }
71319
+ function isRecord$8(value) {
71320
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
71321
+ }
71322
+ function readContentRecord(value) {
71323
+ return isRecord$8(value) ? value : void 0;
71324
+ }
71325
+ function readNestedContentRecord(value) {
71326
+ if (!isRecord$8(value)) return null;
71327
+ return readContentRecord(value.data) ?? null;
71328
+ }
71329
+ function sanitizeSdkRuntimeBlockContent(blockKind, content) {
71330
+ if (blockKind !== "block.newsletter-signup") return content;
71331
+ const sanitized = sanitizeNewsletterSignupPublicContent(content);
71332
+ return isRecord$8(sanitized) ? sanitized : {};
71333
+ }
71334
+ function sanitizeNullableSdkRuntimeBlockContent(blockKind, content) {
71335
+ return content ? sanitizeSdkRuntimeBlockContent(blockKind, content) : null;
71336
+ }
71337
+ //#endregion
71338
+ //#region src/rendering/helpers/footerPageOutline.ts
71339
+ function buildFooterRenderablePageOutline(siteData, blocks) {
71340
+ return buildRenderablePageOutline({
71341
+ name: "Footer",
71342
+ path: "/__footer",
71343
+ fallbackPath: "/__footer",
71344
+ purpose: "footer",
71345
+ blocks,
71346
+ customBlocks: resolveSiteCustomBlocks(siteData)
71347
+ });
71348
+ }
71349
+ //#endregion
71350
+ //#region src/rendering/components/Layout.tsx
71351
+ /**
71352
+ * Layout component with header and footer
71353
+ *
71354
+ * Renders site header and footer around content. Fetches site data if not provided.
71355
+ */
71356
+ function isOverrideOnlyLayout(props) {
71357
+ return props.rendering?.kind === "overrides-only";
71358
+ }
71359
+ var UnsupportedOverrideOnlyLayoutChromeError = class extends Error {
71360
+ constructor(surface) {
71361
+ super(`Override-only Layout requires a custom or disabled ${surface}; SDK default chrome is unavailable.`);
71362
+ this.surface = surface;
71363
+ this.code = "unsupported_override_only_layout_chrome";
71364
+ this.name = "UnsupportedOverrideOnlyLayoutChromeError";
71365
+ }
71366
+ };
71367
+ async function resolveLayoutSiteData(props) {
71368
+ if (props.siteData) return props.siteData;
71369
+ if (!props.client) throw new Error("Layout: must provide either siteData or client");
71370
+ if (!props.siteId && !props.slug && !props.domain) throw new Error("Layout: must provide siteId, slug, or domain when using client");
71371
+ return props.client.getSite({
71372
+ id: props.siteId,
71373
+ slug: props.slug,
71374
+ domain: props.domain
71375
+ });
71376
+ }
71377
+ function renderOverrideOnlyLayout(props, data) {
71378
+ if (data.header === true) throw new UnsupportedOverrideOnlyLayoutChromeError("header");
71379
+ if (data.footer === true) throw new UnsupportedOverrideOnlyLayoutChromeError("footer");
71380
+ return renderWithOverrideOnlyRuntime({
71381
+ runtime: props.rendering.runtime ?? defaultOverrideOnlyRuntime,
71382
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
71383
+ typeof props.header === "function" ? props.header(data.headerData) : null,
71384
+ props.children,
71385
+ typeof props.footer === "function" ? props.footer(data.footerData) : null
71386
+ ] })
71387
+ });
71388
+ }
71389
+ /**
71390
+ * Layout component that wraps content with site header and footer.
71391
+ *
71392
+ * This is a server/RSC-safe component. The default `@riverbankcms/sdk/rendering`
71393
+ * `Page` and `Block` surfaces mount CMS island hydration automatically, while
71394
+ * the strict `@riverbankcms/sdk/rendering/server` surface stays bootstrap-free.
71395
+ * If a custom host renders hydratable CMS islands through the strict server
71396
+ * surface, mount `BlocksIslandsHydrator` from
71397
+ * `@riverbankcms/sdk/rendering/islands` explicitly.
71398
+ *
71399
+ * @example With pre-fetched site data (recommended)
71400
+ * ```tsx
71401
+ * import { Layout } from '@riverbankcms/sdk/rendering/server';
71402
+ * import { Page } from '@riverbankcms/sdk/rendering';
71403
+ *
71404
+ * const site = await client.getSite({ slug: 'my-site' });
71405
+ *
71406
+ * <Layout siteData={site}>
71407
+ * <Page {...pageData} />
71408
+ * </Layout>
71409
+ * ```
71410
+ *
71411
+ * @example With strict server rendering
71412
+ * ```tsx
71413
+ * import { Layout, Page } from '@riverbankcms/sdk/rendering/server';
71414
+ * import { BlocksIslandsHydrator } from '@riverbankcms/sdk/rendering/islands';
71415
+ *
71416
+ * const site = await client.getSite({ slug: 'my-site' });
71417
+ *
71418
+ * <Layout siteData={site}>
71419
+ * <BlocksIslandsHydrator siteId={site.site.id} supabaseUrl={site.supabaseUrl} />
71420
+ * <Page {...pageData} />
71421
+ * </Layout>
71422
+ * ```
71423
+ */
71424
+ async function Layout(props) {
71425
+ const { children, hasPortalSession = false, previewStage = "published" } = props;
71426
+ const header = props.header ?? true;
71427
+ const footer = props.footer ?? true;
71428
+ const siteData = await resolveLayoutSiteData(props);
71429
+ const { site, navigation, layout, routes } = siteData;
71430
+ const footerBlocks = siteData.footerBlocks ?? [];
71431
+ const navigationOptions = {
71432
+ hasPortalSession,
71433
+ previewStage,
71434
+ pagesByIdentifier: siteData.pagesByIdentifier,
71435
+ entriesByIdentifier: siteData.entriesByIdentifier,
71436
+ portalEnabled: siteData.portalEnabled ?? false,
71437
+ commerceEnabled: siteData.commerceEnabled ?? false,
71438
+ checkoutHref: "/checkout"
71439
+ };
71440
+ const menuViewModel = buildMenu(navigation, routes, navigationOptions);
71441
+ const footerMenuViewModel = buildFooterMenu(navigation, routes, navigationOptions);
71442
+ const legalFooterMenuViewModel = buildFooterMenuForRole(navigation, routes, "legal-policy", navigationOptions);
71443
+ const logoViewModel = buildLogo(layout.logo ?? null, site.title);
71444
+ const customHeaderData = {
71445
+ menu: menuViewModel,
71446
+ logo: logoViewModel,
71447
+ site,
71448
+ routes
71449
+ };
71450
+ const customFooterData = {
71451
+ menu: menuViewModel,
71452
+ footerMenu: footerMenuViewModel,
71453
+ legalFooterMenu: legalFooterMenuViewModel,
71454
+ logo: logoViewModel,
71455
+ site,
71456
+ routes
71457
+ };
71458
+ if (isOverrideOnlyLayout(props)) return renderOverrideOnlyLayout(props, {
71459
+ header: props.header ?? true,
71460
+ footer: props.footer ?? true,
71461
+ headerData: customHeaderData,
71462
+ footerData: customFooterData
71463
+ });
71464
+ const theme = requireSdkTheme(siteData.theme, site.id);
71465
+ const builtThemeRuntime = buildThemeRuntime(theme);
71466
+ const headerVariant = props.headerVariant;
71467
+ const themeRuntime = props.themeRuntime;
71468
+ const headerData = {
71469
+ ...customHeaderData,
71470
+ theme
71471
+ };
71472
+ const footerData = {
71473
+ ...customFooterData,
71474
+ theme
71475
+ };
71476
+ const headerContent = headerVariant ? {
71477
+ ...layout.header,
71478
+ variant: headerVariant
71479
+ } : layout.header;
71480
+ const themeWithVariant = headerVariant ? {
71481
+ ...theme,
71482
+ header: {
71483
+ ...theme.header,
71484
+ variant: headerVariant
71485
+ }
71486
+ } : theme;
71487
+ const blocksThemeWithVariant = themeWithVariant;
71488
+ const blocksTheme = theme;
71489
+ let apiBaseUrl;
71490
+ try {
71491
+ apiBaseUrl = getRiverbankEnv().baseUrl;
71492
+ } catch {}
71493
+ const sharedViewModelOverrides = {
71494
+ $root: {
71495
+ siteId: site.id,
71496
+ apiBaseUrl,
71497
+ previewStage,
71498
+ routes,
71499
+ pagesByIdentifier: siteData.pagesByIdentifier,
71500
+ entriesByIdentifier: siteData.entriesByIdentifier,
71501
+ theme: themeWithVariant
71502
+ },
71503
+ site,
71504
+ menu: menuViewModel,
71505
+ footerMenu: footerMenuViewModel,
71506
+ legalFooterMenu: legalFooterMenuViewModel
71507
+ };
71508
+ const headerViewModelOverrides = {
71509
+ ...sharedViewModelOverrides,
71510
+ content: { logo: logoViewModel }
71511
+ };
71512
+ const footerViewModelOverrides = {
71513
+ ...sharedViewModelOverrides,
71514
+ content: {
71515
+ ...layout.footer,
71516
+ logo: logoViewModel
71517
+ }
71518
+ };
71519
+ let headerElement = null;
71520
+ if (header === true) headerElement = renderBlock(siteHeaderManifest, headerContent, {
71521
+ theme: builtThemeRuntime.tokens,
71522
+ themeConfig: blocksThemeWithVariant,
71523
+ viewModelOverrides: headerViewModelOverrides
71524
+ });
71525
+ else if (typeof header === "function") headerElement = header(headerData);
71526
+ let footerElement = null;
71527
+ if (footer === true) {
71528
+ const footerPlan = resolveFooterRenderPlan({
71529
+ isSdkSite: Boolean(site.is_sdk_site),
71530
+ mode: theme.footer?.mode
71531
+ });
71532
+ const footerBlocksElement = footerPlan.renderFooterBlocks && footerBlocks.length > 0 ? /* @__PURE__ */ jsx(PageRenderer, {
71533
+ theme,
71534
+ page: buildFooterRenderablePageOutline(siteData, footerBlocks),
71535
+ routeMap: routes,
71536
+ dataContext: {
71537
+ previewStage,
71538
+ siteId: site.id,
71539
+ apiBaseUrl,
71540
+ routes,
71541
+ pagesByIdentifier: siteData.pagesByIdentifier,
71542
+ entriesByIdentifier: siteData.entriesByIdentifier
71543
+ },
71544
+ sdkConfig: siteData.sdkConfig ?? null
71545
+ }) : null;
71546
+ if (footerPlan.renderFooterBlocks && !footerBlocksElement && process.env.NODE_ENV !== "production") console.debug("[sdk] Footer mode renders blocks, but footerBlocks is empty/missing", {
71547
+ siteId: site.id,
71548
+ mode: footerPlan.mode
71549
+ });
71550
+ const defaultFooterElement = footerPlan.renderDefaultFooter ? renderBlock(siteFooterManifest, layout.footer, {
71551
+ theme: builtThemeRuntime.tokens,
71552
+ themeConfig: blocksTheme,
71553
+ viewModelOverrides: footerViewModelOverrides
71554
+ }) : null;
71555
+ footerElement = footerPlan.mode === "default+blocks" ? /* @__PURE__ */ jsxs(Fragment, { children: [footerBlocksElement, defaultFooterElement] }) : footerPlan.mode === "blocks" ? footerBlocksElement : footerPlan.mode === "none" ? null : defaultFooterElement;
71556
+ } else if (typeof footer === "function") footerElement = footer(footerData);
71557
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
71558
+ headerElement,
71559
+ children,
71560
+ footerElement
71561
+ ] });
71562
+ return renderWithSdkThemeRuntime({
71563
+ theme,
71564
+ themeRuntime: planLayoutThemeRuntime({
71565
+ themeRuntime,
71566
+ usesSdkChrome: header === true || footer === true
71567
+ }),
71568
+ children: content
71569
+ });
71570
+ }
70946
71571
  function blockedDemoMutationPolicy(classification, publicMessage) {
70947
71572
  if (publicMessage.trim() === "") throw new Error(`Demo mutation policy ${classification} requires publicMessage.`);
70948
71573
  return {
@@ -72139,6 +72764,13 @@ const PUBLIC_WHOLE_SERIES_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
72139
72764
  //#endregion
72140
72765
  //#region ../api/src/publicEventEndpointDefinitions.ts
72141
72766
  const PUBLIC_EVENT_ENDPOINT_DEFINITIONS = defineEndpointDefinitions({
72767
+ resolvePublicEventCheckoutReturn: {
72768
+ path: "/public/sites/{siteId}/events/checkout-return",
72769
+ method: "POST",
72770
+ tags: [TAG_TEMPLATES.publicEvents, "event-checkout"],
72771
+ auth: "public",
72772
+ responseKind: "json"
72773
+ },
72142
72774
  getPublicEventRegistrationOptions: {
72143
72775
  path: "/public/sites/{siteId}/events/registration-options",
72144
72776
  method: "POST",
@@ -73400,7 +74032,7 @@ z$1.object({
73400
74032
  });
73401
74033
  //#endregion
73402
74034
  //#region ../api/src/utils/isRecord.ts
73403
- function isRecord$6(value) {
74035
+ function isRecord$5(value) {
73404
74036
  return typeof value === "object" && value !== null && !Array.isArray(value);
73405
74037
  }
73406
74038
  //#endregion
@@ -73647,7 +74279,7 @@ function sanitizeMarks(marks) {
73647
74279
  if (!Array.isArray(marks) || marks.length === 0) return;
73648
74280
  const sanitized = marks.flatMap((mark) => {
73649
74281
  if (mark.type === "bold" || mark.type === "strong" || mark.type === "italic" || mark.type === "em") return [{ type: mark.type }];
73650
- if (mark.type === "link" && isRecord$6(mark.attrs) && typeof mark.attrs.href === "string") {
74282
+ if (mark.type === "link" && isRecord$5(mark.attrs) && typeof mark.attrs.href === "string") {
73651
74283
  const attrs = { href: mark.attrs.href };
73652
74284
  if (typeof mark.attrs.target === "string" && mark.attrs.target.trim().length > 0) attrs.target = mark.attrs.target;
73653
74285
  if (typeof mark.attrs.rel === "string" && mark.attrs.rel.trim().length > 0) attrs.rel = mark.attrs.rel;
@@ -73672,7 +74304,7 @@ function collectText(node) {
73672
74304
  }
73673
74305
  function stableSerialize(value) {
73674
74306
  if (Array.isArray(value)) return `[${value.map(stableSerialize).join(",")}]`;
73675
- 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(",")}}`;
74307
+ 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(",")}}`;
73676
74308
  return JSON.stringify(value);
73677
74309
  }
73678
74310
  //#endregion
@@ -73809,7 +74441,7 @@ function collectCartLinkStats(items, depth = 0) {
73809
74441
  return result;
73810
74442
  }
73811
74443
  function isNavigationMenuValidationItem(value) {
73812
- if (!isRecord$6(value)) return false;
74444
+ if (!isRecord$5(value)) return false;
73813
74445
  return typeof value.label === "string";
73814
74446
  }
73815
74447
  //#endregion
@@ -73827,7 +74459,7 @@ function normalizeIdList(value) {
73827
74459
  return result.length > 0 ? result : null;
73828
74460
  }
73829
74461
  function extractNavVisibilityConfig(raw) {
73830
- if (!isRecord$6(raw)) return null;
74462
+ if (!isRecord$5(raw)) return null;
73831
74463
  const modeRaw = raw.mode;
73832
74464
  const mode = modeRaw === "augment" || modeRaw === "restrict" ? modeRaw : null;
73833
74465
  const allowIds = normalizeIdList(raw.allowIds);
@@ -73840,7 +74472,7 @@ function extractNavVisibilityConfig(raw) {
73840
74472
  };
73841
74473
  }
73842
74474
  function asRecord(value) {
73843
- return isRecord$6(value) ? value : null;
74475
+ return isRecord$5(value) ? value : null;
73844
74476
  }
73845
74477
  /**
73846
74478
  * Check if a raw config object has any nav visibility values defined.
@@ -75180,6 +75812,7 @@ const DEFAULT_TEMPLATE_DEFINITIONS = {
75180
75812
  {
75181
75813
  blockKind: "block.body-text",
75182
75814
  scope: "entry",
75815
+ content: { body: createRichTextDoc([""]) },
75183
75816
  bindings: EVENT_BODY_TEXT_BINDINGS
75184
75817
  },
75185
75818
  {
@@ -75226,6 +75859,7 @@ const DEFAULT_TEMPLATE_DEFINITIONS = {
75226
75859
  }, {
75227
75860
  blockKind: "block.body-text",
75228
75861
  scope: "entry",
75862
+ content: { body: createRichTextDoc([""]) },
75229
75863
  bindings: TEAM_MEMBER_BODY_TEXT_BINDINGS
75230
75864
  }]
75231
75865
  },