riverbankcms 0.21.0 → 0.22.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.
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { $ as createNavigationRef, $n as cardVariantRoleValues, $t as headerRootPaint, A as eventManagementTiming, An as buildRenderableMediaAssetIndex, Ar as borderColorStyle, At as BREAKPOINT_XL, B as bookingPaymentCollectionOptionSchema, Bn as resolveVideoSelectionIntentForMediaField, Br as selectLogoVariant, Bt as themeButtonVariantClassNames, C as stableStringify, Cn as getDirectImageUrlForSiteScopedPath, Cr as toNonEmptyReadonlyArray, Ct as generateHeaderScrollTreatmentStyles, Dn as mediaSchema, Dr as resolveSurfaceForeground, Dt as BREAKPOINT_LG, E as mapEventCommandContent, En as mediaPosterSchema, Er as surfaceDecorationColors, Et as renderHeaderPaintTargetRules, F as pageIdentifierForCommand, Fn as parseCanonicalMediaIdentifier, Fr as isCssColorLiteral, Ft as ICON_CONFIG, G as createBlockRef, Gn as parseRelativeStoragePath, Gr as surfaceForegroundStyle, Gt as generateNavLinkCSS, H as require_jsx_runtime, Hn as getMimeType, Hr as defaultThemeTokens, I as pagePathForCommand, In as parseMediaIdentifier, Ir as resolveOnColorForThemeToken, It as generateDefaultButtonSystem, J as createContentEntryRef, Jn as classifyKnownMimeType, Jr as getTextColorForBackground, Jt as resolveItemSurface, K as createBlocksRef, Kn as FILE_SIZE_LIMITS, Kr as surfaceForegroundValue, Kt as arrangementLookHooks, L as hasConfiguredBookingVertical, Lr as resolveThemeColorReferenceValue, Lt as parseThemeButtonVariantId, M as blockOrderIdentifiersForCommand, Mn as mediaTypeDiscriminants, Mr as rgbColorValue, Mt as headerVariantValues, N as entryIdentifierForCommand, Nn as mediaIdentifierSchema, Nr as textColorStyle, Nt as getComponentBorderWidthPx, On as persistableMediaSchema, Or as TokenResolver, Ot as BREAKPOINT_MD, P as pageIdentifierForBlockCommand, Pn as buildMediaIdentifierFromFilename, Pr as opaqueSurfaceDeclarations, Pt as getComponentShadowVar, Q as createFormRef, Qn as cardRoleSchema, Qr as __toESM, R as eventCommandScheduleSchema, Rr as resolveTokenOrCssColorValue, Rt as themeButtonExtraClassesWithTrailingIcon, S as sortRecord, Sn as getDirectImageUrlForRelativePath, Sr as tileTreatmentValues, St as themeSchema, Tn as isSiteScopedDirectImageStoragePath, Tr as resolveSurface, Tt as getHeaderPaintTargets, U as require_react, Un as asRecord, Ur as surfaceForegroundDeclarations, Ut as footerForegroundCandidates, V as bookingPaymentTermsSchema, Vr as surfaceToneFromForegroundToken, Vt as parseThemeForPersistence, W as ianaTimezoneSchema, Wr as surfaceForegroundInputFromResolved, Wt as buildTextContrastShadow, X as createEventSeriesRef, Xn as parseSafeDownloadFilename, Xr as parseBlockColorToken, Y as createEventCategoryRef, Yn as managementMediaLibraryUploadLimitForMimeType, Yr as parseThemeColorToken, Yt as resolveLookSlotSurfaces, Z as createFooterRef, Zn as renderCssDeclarations, Zr as __commonJSMin, _n as rotateNormalizedPoint, _r as headingDecorationColorTokenValues, _t as requireCuratedSiteStyle, an as resolveSectionBackground, at as effectiveHeaderNavLinkStyle, b as isEventVisibility, bn as parseAspectRatioValue, br as offeringsPreviewCompositionTreatmentValues, bt as parseGradientPresetId, c as customLinkSchema$1, cn as boxBackgroundSchema, ct as transparentHeaderForegroundInput, d as internalLinkSchema, dn as stripStaleTransitionStyleGroup, dr as defineBlockDesignCapability, dt as readHeaderNavFontSizePreference, en as boundaryLayout, er as normalizeCardRole, et as createPageRef, f as internalResolvedLinkSchema, fn as collectPortableMediaReferencesFromContent, fr as defineFrameCapability, ft as arrangementManifestFromVariants, g as linkSchema, gn as looksLikeUuid, gr as featureCardCompositionTreatmentValues, gt as resolvePublishedLook, h as isResolvedInternalLinkEntityType, hn as deleteEnvSpecificKeys, hr as eyebrowTreatmentValues, ht as resolveModeArrangement, in as parseCanonicalRuntimeBackground, ir as cardListKnobBlocks, j as blockIdentifierForCommand, jn as resolveManagedMediaReference, jr as mergeStyles, jt as MQ_MD_DOWN, kn as runtimeMediaSchema, kr as backgroundColorStyle, kt as BREAKPOINT_SM, l as entryLinkSchema$1, ln as runtimeBoxBackgroundSchema, lr as asSiteStyleId, lt as readOpeningForegroundProjection, mn as walkAndCollectImageAttrs, mr as eventListTreatmentValues, mt as resolveEffectiveArrangement, nn as sectionFactsForContent, nr as resolveEffectiveKnobs, nt as createVenueRef, o as classifySdkEventBookingOffer, on as resolveSectionPhotoPlacement, or as asDesignBlockId, ot as headerForegroundCandidates, p as internalRouteOnlyLinkSchema, pn as summarizePortableMediaReferences, pr as defineNoBlockDesignCapability, pt as readStoredMode, q as createCommandRef, qr as expandPalette, qt as arrangementSlotNames, rn as deriveSectionGround, rr as cardListFamilyBlocks, rt as refSlug, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, sn as boxBackgroundOverlaySchema, sr as asLayoutVariantKey, st as headerLeafTextColor, t as classifySdkEventTiming, tn as composeBoundary, tr as defineBlockDesignMetadata, u as externalLinkSchema$1, un as siteChromeBackgroundOverrideOwner, ur as assertNever$11, ut as mergeHeaderNavTypographyPreference, v as pageLinkSchema$1, vn as normalizeWidthCandidates, vr as introCompositionTreatmentValues, vt as gradientToCss, wn as getImageUrl, wr as visibleHeadingDecorationFamilyValues, wt as generateMobileOverlayBackdropFilterFix, x as venueManagementFields, xn as resolveMediaTransformForAspect, xr as testimonialsCompositionTreatmentValues, xt as deriveSiteStyleBlockPolicy, yn as isExplicitRectCrop, yr as makeBoundaryId, yt as getGradientPreset, zn as deriveVideoSlotPolicy, zr as PUBLIC_BUTTON_ROLES, zt as themeButtonSizeClassName } from "../sdk-event-upsert-CrOuJrxo.mjs";
3
- import { $ as formatLocalScheduleTime, $t as parseEventSeriesId, A as DEFAULT_MAILING_LIST_SUCCESS_MESSAGE, At as publicEventPricingDisplay, Bt as parsePositiveCents, Cn as unsafeAsPositiveByteCount, Ct as resolveFlexibleBalanceInitialPaymentCents, D as parseMailingListSignedSourceToken, Dt as formatEventPricing, E as parseMailingListContentVersionId, Et as formatBookingPriceDisplay, F as DEFAULT_MAILING_LIST_TARGETING, Ft as fromArray, G as formatDateRange, Gt as formatCurrencyAmount, H as toLocationCoordinates, Ht as toNonNegativeCents, I as planMailingListChoicePresentation, In as err, J as buildBookingUtcDateWindow, Jt as formatDurationUnitLabel, K as formatDateTimeRange, Kt as parseCurrencyCode, L as isCanonicalRichTextDoc, Ln as ok, Lt as mapReadonlyNonEmptyArray, M as parseFooterCompositionV1, Mt as publicEventPricingType, Nt as resolveExactOrFreePriceDisplay, O as parseMailingListRuntimeContext, Ot as isPublicEventPricingOptionDisplayable, P as parseMailingListPlacementTargeting, Pt as resolvePricingOptionsPriceDisplay, Q as formatLocalScheduleDate, Qt as parseDatedOfferingCapacityHoldId, Rt as isNonNegativeWholeCents$1, S as parseManagementKey, Sn as unsafeAsPassId, St as resolveFlexibleBalanceDueAt, T as parseMailingListBlockId, Tn as unsafeAsPricingOptionId, Tt as parseEventPageSurfaceSubject, U as selectCategoryView, Ut as unsafeAsNonNegativeCents, V as normalizeLocationAddressText, Vt as toCents, W as formatDate$1, Wt as unsafeAsPositiveCents, X as formatBookingDateParts, Xt as parseCustomerId, Y as formatBookingDate, Yt as parseApiKeyId, Z as formatBookingTime, Zt as parseDatedOfferingCapacityHoldAuditRef, _ as orderedLiveRunSessions, _t as getBookingInstalmentDepositRefundability, an as parseSiteTermsVersionId, at as resolveBookingBusinessTimeZone, b as createSecret, bn as unsafeAsOccurrenceId, bt as omitZeroAmountFutureRows, c as resolveSiteVerticalBookingConfig, ct as toScheduledBookingDateTime, dt as validateBookingPaymentOptionKinds, et as getBookingLocalDateKey, f as SURFACE_EVENT_PRESET_VALUES, fn as unsafeAsDemoCapability, ft as isLiveOrderInstalmentPlanStatus, g as nextLiveRunSessionStartMs, gt as clampConfiguredAmount, h as CANCELLED_SESSION_STATUS, hn as unsafeAsEventInstanceId, ht as addIntervalPreservingMonthAnchor, in as parseSiteId, j as parseMailingListProviderScope, jt as publicEventPricingOptions, k as assessMailingListTargeting, kt as publicEventPricingCurrency, lt as toUtcIsoDateTimeString, mt as isSettleableOrderInstalmentPlanStatus, n as EVENT_PRESET_VOCABULARY, nt as getLocalScheduleTime, on as parseUserId, ot as toBookingTimeZone, p as WHOLE_SERIES_EVENT_PRESET_VALUES, pt as isSettleableFlexibleBalanceContractStatus, q as inferSiteDateLocale, qt as assertNever, r as NAVIGATION_LOCATIONS, rn as parseRetiredCourseId, st as toScheduledBookingDateRange, tn as parseMembershipId, tt as getLocalScheduleDate, u as EVENT_PRESET_VALUES, un as unsafeAsCheckoutLineId, v as resolveWholeSeriesBookingPhase, vt as getBookingInstalmentPlanIntervalCount, w as slugify, wt as deriveTotalTicketQuantity, x as exposeSecret, xt as splitAmountWithRemainderInFirst, y as resolveWholeSeriesRunLifecycle, yn as unsafeAsMediaAssetId, yt as getBookingInstalmentProtectedDepositAmountCents, z as assertAllowedPublicUrl, zt as parseNonNegativeCents } from "../src-BENi4Q5k.mjs";
4
- import { $ as buildStorageUrl, $t as createClientForEnv, A as formatDiffDetail, At as convertPulledFooterToLocal, B as formatFieldPath, Bn as linkPayloadSchema, Bt as createPushConfigCommand, C as reportFromOutcomes, Cn as planEnvironmentWriteAuthorization, Ct as readJsonFile, D as normalizeRemoteHomepageReference, Dn as formatError, Dt as duplicateNavigationIdentityMessage, E as createEmptySyncResult, En as ErrorCodes, Et as isMediaValue, F as calculatePullDiff, Fn as loadSdkEnvironment, Ft as normalizeNavigationConfigItemForCommand, G as prepareLocalMediaForPush, Gt as createBearerAPIClient, Hn as sdkConfiguredBookingVerticalConfigSchema, Ht as preparePushConfigTarget, I as buildJsonDiff, In as createManagementClient, It as extractApiErrorMessage, J as writeBinaryFileAtomic, Jt as deriveCmsApiUrlFromDashboardUrl, K as admitBinaryWriteDestination, Kt as ApiEnvelopeError, Ln as ManagementApiError, Lt as normalizeApiError, M as hasPendingChanges, Mn as getEnvironmentDebugInfo, Mt as convertPulledPagesToLocal, N as findChangedFieldsWithValues, Nn as isLocalConfigured, Nt as APPOINTMENT_SERVICE_VISIBILITY_VALUES, O as calculateDiff, On as transportSuggestion, Ot as navigationIdentity, P as formatHumanDiff, Pn as isRemoteConfigured, Pt as navigationCommandItemToManagementInput, Q as walkLocalContentMedia, Qt as confirmAction, R as buildFieldChanges, Rn as sdkEventScheduleSchema, Rt as isRecord$2, S as planCliCommandBatchForBindings, Sn as planEnvironmentExecution, St as listFiles, T as executeResidualSettingsSyncPlan, Tn as withRiverbankEnv, Tt as writeJsonFileAtomic, U as syncLocalMediaForPull, Ut as revalidatePushConfigPlan, V as revalidateLocalMediaPushPlan, Vn as portablePageDesignSchema, Vt as loadPushConfigTarget, W as applyLocalMediaPush, Wt as ApiRequestError, X as normalizeMediaIdentifierInput, Xt as mapEnvironmentExecutionOutcomesJson, Y as sha256, Yt as API_ENDPOINTS, Z as normalizeMediaValueToIdentifierOnly, Zt as bindRequestTimeout, _ as commandExecutionToReport, _n as withConfirmation, _t as readMetadataFile, a as executeEntryCommandPlanAsReport, an as formatDateShort, at as readEventCategoriesMeta, b as executeCompiledCliCommandPlanAsReport, bn as executeEnvironmentPlan, bt as ensureDir, c as titleFromEntryData, cn as handleCommandError, ct as readFormsMeta, d as unsafeAsCliEntryIdentifier, dn as parseJsonArray, dt as readSettingsMeta, en as createCommandContext, et as downloadMedia, f as unsafeAsCliFormSlug, fn as parseJsonData, ft as readVenuesMeta, g as commandCompileFailureToReport, gn as withCommandContext, gt as metadataTargetFromEnvironment, h as mutationCounterForEffect, hn as runWithInvocation, ht as metadataFilePath, i as entrySiteCommandTypes, in as currentInvocationEnvelope, it as readAllMeta, j as formatDiffSummary, jn as getEnvPrefix, jt as convertPulledNavigationToLocal, k as calculateFooterDiff, kn as CliExit, kt as navigationIdentityMap, l as unsafeAsCliBlockIdentifier, ln as managementClientConfigFor, lt as readNavigationMeta, m as commandReportToSyncResult, mn as resolveCommandPath, mt as ensureMetadataDir, n as entryCommandCompileErrorToReport, nn as createListCommand, nt as findLegacyEntryFiles, o as executeEntryCommandPlanAsSyncResult, on as getOutputContext, ot as readEventsMeta, p as unsafeAsCliPageIdentifier, pn as parsePaginationOptions, pt as writeMediaManifest, q as readStableBinaryFile, qt as unwrapResponse, r as entryCommandReportToSyncResult, rn as createOutputForCommand, rt as readEntries, s as syncResultForEntryCommandCompileError, sn as getOutputOnlyContext, st as readFooterMeta, t as compileEntryPushCommands, tn as createGetCommand, tt as extractMediaIdentifiers, u as unsafeAsCliContentTypeKey, un as mapEntryForOutput, ut as readPagesMeta, v as commandReportHasFailures, vn as withErrorHandling, vt as writeMetadataFile, w as mergeManySyncResults, wn as requireEnvironmentWriteAuthorization, wt as writeJsonFile, x as formatManagementError, xn as parseEnvironmentSelection, xt as fileExists, y as executeCliCommandPlan, yn as withOutputOnlyErrorHandling, yt as writeMetadataRecordMap, z as formatFieldChange, zn as sdkSettingsUpdateSchema, zt as applyPushConfigPlan } from "../entryCommands-DSQZqznu.mjs";
5
- import { r as CLI_VERSION, t as loadConfigFile } from "../load-config-B68gtN45.mjs";
6
- import { a as RichTextDocSchema, c as isRecord$18, i as findUnfilledSentinels, n as buildEntryScaffold, o as isRichTextDoc, r as validateEntryInstance, s as isObjectRecord, t as eventUpsertInputSchema } from "../eventUpsertInputSchema-zCTx0fdF.mjs";
7
- import { a as prepareScaffoldExecution, c as LOCAL_PACKAGE_VENDOR_DIR, d as loadGeneratedPackageRanges, i as preflightScaffoldTargets, n as createScaffoldCompletion, s as deriveScaffoldLocalManifest, t as deriveScaffoldPlan } from "../plan-CkMANumv.mjs";
2
+ import { $ as createNavigationRef, $n as cardRoleSchema, $r as __toESM, A as eventManagementTiming, An as runtimeMediaSchema, Ar as backgroundColorStyle, At as BREAKPOINT_SM, B as bookingPaymentCollectionOptionSchema, Bn as deriveVideoSlotPolicy, Br as PUBLIC_BUTTON_ROLES, Bt as themeButtonSizeClassName, C as stableStringify, Cn as getDirectImageUrlForRelativePath, Cr as tileTreatmentValues, Ct as themeSchema, Dn as mediaPosterSchema, Dr as surfaceDecorationColors, Dt as renderHeaderPaintTargetRules, E as mapEventCommandContent, En as isSiteScopedDirectImageStoragePath, Er as resolveSurface, Et as getHeaderPaintTargets, F as pageIdentifierForCommand, Fn as buildMediaIdentifierFromFilename, Fr as opaqueSurfaceDeclarations, Ft as getComponentShadowVar, G as createBlockRef, Gr as surfaceForegroundInputFromResolved, Gt as buildTextContrastShadow, H as require_jsx_runtime, Hr as surfaceToneFromForegroundToken, Ht as parseThemeForPersistence, I as pagePathForCommand, In as parseCanonicalMediaIdentifier, Ir as isCssColorLiteral, It as ICON_CONFIG, J as createContentEntryRef, Jr as expandPalette, Jt as arrangementSlotNames, K as createBlocksRef, Kn as parseRelativeStoragePath, Kr as surfaceForegroundStyle, Kt as generateNavLinkCSS, L as hasConfiguredBookingVertical, Ln as parseMediaIdentifier, Lr as resolveOnColorForThemeToken, Lt as generateDefaultButtonSystem, M as blockOrderIdentifiersForCommand, Mn as resolveManagedMediaReference, Mr as mergeStyles, Mt as MQ_MD_DOWN, N as entryIdentifierForCommand, Nn as mediaTypeDiscriminants, Nr as rgbColorValue, Nt as headerVariantValues, On as mediaSchema, Or as resolveSurfaceForeground, Ot as BREAKPOINT_LG, P as pageIdentifierForBlockCommand, Pn as mediaIdentifierSchema, Pr as textColorStyle, Pt as getComponentBorderWidthPx, Q as createFormRef, Qn as renderCssDeclarations, Qr as __commonJSMin, R as eventCommandScheduleSchema, Rr as resolveThemeColorReferenceValue, Rt as parseThemeButtonVariantId, S as sortRecord, Sn as resolveMediaTransformForAspect, Sr as testimonialsCompositionTreatmentValues, St as deriveSiteStyleBlockPolicy, Tn as getImageUrl, Tr as visibleHeadingDecorationFamilyValues, Tt as generateMobileOverlayBackdropFilterFix, U as require_react, Un as getMimeType, Ur as defaultThemeTokens, V as bookingPaymentTermsSchema, Vn as resolveVideoSelectionIntentForMediaField, Vr as selectLogoVariant, Vt as themeButtonVariantClassNames, W as ianaTimezoneSchema, Wn as asRecord, Wr as surfaceForegroundDeclarations, Wt as footerForegroundCandidates, X as createEventSeriesRef, Xn as managementMediaLibraryUploadLimitForMimeType, Xr as parseThemeColorToken, Xt as resolveLookSlotSurfaces, Y as createEventCategoryRef, Yn as classifyKnownMimeType, Yr as getTextColorForBackground, Yt as resolveItemSurface, Z as createFooterRef, Zn as parseSafeDownloadFilename, Zr as parseBlockColorToken, _n as looksLikeUuid, _r as featureCardCompositionTreatmentValues, _t as requireCuratedSiteStyle, an as parseCanonicalRuntimeBackground, ar as cardListKnobBlocks, at as effectiveHeaderNavLinkStyle, b as isEventVisibility, bn as isExplicitRectCrop, br as makeBoundaryId, bt as getGradientPreset, c as customLinkSchema$1, cn as boxBackgroundOverlaySchema, cr as asLayoutVariantKey, ct as transparentHeaderForegroundInput, d as internalLinkSchema, dn as siteChromeBackgroundOverrideOwner, dr as assertNever$11, dt as readHeaderNavFontSizePreference, en as headerRootPaint, er as cardVariantRoleValues, et as createPageRef, f as internalResolvedLinkSchema, fn as stripStaleTransitionStyleGroup, fr as defineBlockDesignCapability, ft as arrangementManifestFromVariants, g as linkSchema, gn as deleteEnvSpecificKeys, gr as eyebrowTreatmentValues, gt as resolvePublishedLook, h as isResolvedInternalLinkEntityType, hn as walkAndCollectImageAttrs, hr as eventListTreatmentValues, ht as resolveModeArrangement, in as deriveSectionGround, ir as cardListFamilyBlocks, j as blockIdentifierForCommand, jn as buildRenderableMediaAssetIndex, jr as borderColorStyle, jt as BREAKPOINT_XL, kn as persistableMediaSchema, kr as TokenResolver, kt as BREAKPOINT_MD, l as entryLinkSchema$1, ln as boxBackgroundSchema, lt as readOpeningForegroundProjection, mn as summarizePortableMediaReferences, mr as defineNoBlockDesignCapability, mt as resolveEffectiveArrangement, nn as composeBoundary, nr as defineBlockDesignMetadata, nt as createVenueRef, o as classifySdkEventBookingOffer, on as resolveSectionBackground, ot as headerForegroundCandidates, p as internalRouteOnlyLinkSchema, pn as collectPortableMediaReferencesFromContent, pr as defineFrameCapability, pt as readStoredMode, q as createCommandRef, qn as FILE_SIZE_LIMITS, qr as surfaceForegroundValue, qt as arrangementLookHooks, rn as sectionFactsForContent, rr as resolveEffectiveKnobs, rt as refSlug, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, sn as resolveSectionPhotoPlacement, sr as asDesignBlockId, st as headerLeafTextColor, t as classifySdkEventTiming, tn as boundaryLayout, tr as normalizeCardRole, u as externalLinkSchema$1, un as runtimeBoxBackgroundSchema, ur as asSiteStyleId, ut as mergeHeaderNavTypographyPreference, v as pageLinkSchema$1, vn as rotateNormalizedPoint, vr as headingDecorationColorTokenValues, vt as compiledRuntimeStyleFieldNames, wn as getDirectImageUrlForSiteScopedPath, wr as toNonEmptyReadonlyArray, wt as generateHeaderScrollTreatmentStyles, x as venueManagementFields, xn as parseAspectRatioValue, xr as offeringsPreviewCompositionTreatmentValues, xt as parseGradientPresetId, yn as normalizeWidthCandidates, yr as introCompositionTreatmentValues, yt as gradientToCss, zr as resolveTokenOrCssColorValue, zt as themeButtonExtraClassesWithTrailingIcon } from "../sdk-event-upsert-BqjzK2zT.mjs";
3
+ import { $ as compareLocalScheduleDates, $t as parseCustomerId, A as DEFAULT_MAILING_LIST_SUCCESS_MESSAGE, At as formatEventPricing, Bn as ok, Bt as mapReadonlyNonEmptyArray, Cn as unsafeAsOccurrenceId, Ct as omitZeroAmountFutureRows, D as parseMailingListSignedSourceToken, Dt as deriveTotalTicketQuantity, E as parseMailingListContentVersionId, En as unsafeAsPositiveByteCount, Et as resolveFlexibleBalanceInitialPaymentCents, F as DEFAULT_MAILING_LIST_TARGETING, Ft as publicEventPricingType, G as formatDateRange, Gt as toNonNegativeCents, H as toLocationCoordinates, Ht as parseNonNegativeCents, I as planMailingListChoicePresentation, It as resolveExactOrFreePriceDisplay, J as formatTime$1, Jt as formatCurrencyAmount, K as formatDateTime, Kt as unsafeAsNonNegativeCents, L as isCanonicalRichTextDoc, Lt as resolvePricingOptionsPriceDisplay, M as parseFooterCompositionV1, Mt as publicEventPricingCurrency, Nt as publicEventPricingDisplay, O as parseMailingListRuntimeContext, On as unsafeAsPricingOptionId, Ot as parseEventPageSurfaceSubject, P as parseMailingListPlacementTargeting, Pt as publicEventPricingOptions, Q as buildBookingUtcDateWindow, Qt as parseApiKeyId, Rt as fromArray, S as parseManagementKey, Sn as unsafeAsMediaAssetId, St as getBookingInstalmentProtectedDepositAmountCents, T as parseMailingListBlockId, Tn as unsafeAsPassId, Tt as resolveFlexibleBalanceDueAt, U as selectCategoryView, Ut as parsePositiveCents, V as normalizeLocationAddressText, Vt as isNonNegativeWholeCents$1, W as formatDate, Wt as toCents, X as inferSiteDateLocale, Xt as assertNever, Y as formatTimeRange, Yt as parseCurrencyCode, Z as resolveBookingDisplayContext, Zt as formatDurationUnitLabel, _ as orderedLiveRunSessions, _t as isSettleableOrderInstalmentPlanStatus, b as createSecret, bt as getBookingInstalmentDepositRefundability, c as resolveSiteVerticalBookingConfig, cn as parseSiteTermsVersionId, ct as resolveBookingBusinessTimeZone, dt as toScheduledBookingDateTime, en as parseDatedOfferingCapacityHoldAuditRef, et as formatLocalScheduleDate, f as SURFACE_EVENT_PRESET_VALUES, ft as toUtcIsoDateTimeString, g as nextLiveRunSessionStartMs, gt as isSettleableFlexibleBalanceContractStatus, h as CANCELLED_SESSION_STATUS, hn as unsafeAsDemoCapability, ht as isLiveOrderInstalmentPlanStatus, in as parseMembershipId, it as getLocalScheduleTime, j as parseMailingListProviderScope, jt as isPublicEventPricingOptionDisplayable, k as assessMailingListTargeting, kt as formatBookingPriceDisplay, ln as parseUserId, lt as resolveBookingTimeZoneId, mt as validateBookingPaymentOptionKinds, n as EVENT_PRESET_VOCABULARY, nn as parseEventSeriesId, nt as getBookingLocalDateKey, on as parseRetiredCourseId, ot as parseBookingLocalDate, p as WHOLE_SERIES_EVENT_PRESET_VALUES, pn as unsafeAsCheckoutLineId, q as formatDateTimeRange, qt as unsafeAsPositiveCents, r as NAVIGATION_LOCATIONS, rt as getLocalScheduleDate, sn as parseSiteId, st as parseUtcIsoDateTimeString, tn as parseDatedOfferingCapacityHoldId, tt as formatLocalScheduleTime, u as EVENT_PRESET_VALUES, ut as toBookingTimeZone, v as resolveWholeSeriesBookingPhase, vn as unsafeAsEventInstanceId, vt as addIntervalPreservingMonthAnchor, w as slugify, wt as splitAmountWithRemainderInFirst, x as exposeSecret, xt as getBookingInstalmentPlanIntervalCount, y as resolveWholeSeriesRunLifecycle, yt as clampConfiguredAmount, z as assertAllowedPublicUrl, zn as err } from "../src-zzwJUef5.mjs";
4
+ import { $ as buildStorageUrl, $t as createClientForEnv, A as formatDiffDetail, At as convertPulledFooterToLocal, B as formatFieldPath, Bn as linkPayloadSchema, Bt as extractApiErrorMessage, C as reportFromOutcomes, Cn as planEnvironmentWriteAuthorization, Ct as readJsonFile, D as normalizeRemoteHomepageReference, Dn as formatError, Dt as duplicateNavigationIdentityMessage, E as createEmptySyncResult, En as ErrorCodes, Et as isMediaValue, F as calculatePullDiff, Fn as loadSdkEnvironment, Ft as applyPushConfigPlan, G as prepareLocalMediaForPush, Gt as createBearerAPIClient, Hn as sdkConfiguredBookingVerticalConfigSchema, Ht as isRecord$2, I as buildJsonDiff, In as createManagementClient, It as createPushConfigCommand, J as writeBinaryFileAtomic, Jt as deriveCmsApiUrlFromDashboardUrl, K as admitBinaryWriteDestination, Kt as ApiEnvelopeError, Ln as ManagementApiError, Lt as loadPushConfigTarget, M as hasPendingChanges, Mn as getEnvironmentDebugInfo, Mt as convertPulledPagesToLocal, N as findChangedFieldsWithValues, Nn as isLocalConfigured, Nt as navigationCommandItemToManagementInput, O as calculateDiff, On as transportSuggestion, Ot as navigationIdentity, P as formatHumanDiff, Pn as isRemoteConfigured, Pt as normalizeNavigationConfigItemForCommand, Q as walkLocalContentMedia, Qt as confirmAction, R as buildFieldChanges, Rn as sdkEventScheduleSchema, Rt as preparePushConfigTarget, S as planCliCommandBatchForBindings, Sn as planEnvironmentExecution, St as listFiles, T as executeResidualSettingsSyncPlan, Tn as withRiverbankEnv, Tt as writeJsonFileAtomic, U as syncLocalMediaForPull, Ut as APPOINTMENT_SERVICE_VISIBILITY_VALUES, V as revalidateLocalMediaPushPlan, Vn as portablePageDesignSchema, Vt as normalizeApiError, W as applyLocalMediaPush, Wt as ApiRequestError, X as normalizeMediaIdentifierInput, Xt as mapEnvironmentExecutionOutcomesJson, Y as sha256, Yt as API_ENDPOINTS, Z as normalizeMediaValueToIdentifierOnly, Zt as bindRequestTimeout, _ as commandExecutionToReport, _n as withConfirmation, _t as readMetadataFile, a as executeEntryCommandPlanAsReport, an as formatDateShort, at as readEventCategoriesMeta, b as executeCompiledCliCommandPlanAsReport, bn as executeEnvironmentPlan, bt as ensureDir, c as titleFromEntryData, cn as handleCommandError, ct as readFormsMeta, d as unsafeAsCliEntryIdentifier, dn as parseJsonArray, dt as readSettingsMeta, en as createCommandContext, et as downloadMedia, f as unsafeAsCliFormSlug, fn as parseJsonData, ft as readVenuesMeta, g as commandCompileFailureToReport, gn as withCommandContext, gt as metadataTargetFromEnvironment, h as mutationCounterForEffect, hn as runWithInvocation, ht as metadataFilePath, i as entrySiteCommandTypes, in as currentInvocationEnvelope, it as readAllMeta, j as formatDiffSummary, jn as getEnvPrefix, jt as convertPulledNavigationToLocal, k as calculateFooterDiff, kn as CliExit, kt as navigationIdentityMap, l as unsafeAsCliBlockIdentifier, ln as managementClientConfigFor, lt as readNavigationMeta, m as commandReportToSyncResult, mn as resolveCommandPath, mt as ensureMetadataDir, n as entryCommandCompileErrorToReport, nn as createListCommand, nt as findLegacyEntryFiles, o as executeEntryCommandPlanAsSyncResult, on as getOutputContext, ot as readEventsMeta, p as unsafeAsCliPageIdentifier, pn as parsePaginationOptions, pt as writeMediaManifest, q as readStableBinaryFile, qt as unwrapResponse, r as entryCommandReportToSyncResult, rn as createOutputForCommand, rt as readEntries, s as syncResultForEntryCommandCompileError, sn as getOutputOnlyContext, st as readFooterMeta, t as compileEntryPushCommands, tn as createGetCommand, tt as extractMediaIdentifiers, u as unsafeAsCliContentTypeKey, un as mapEntryForOutput, ut as readPagesMeta, v as commandReportHasFailures, vn as withErrorHandling, vt as writeMetadataFile, w as mergeManySyncResults, wn as requireEnvironmentWriteAuthorization, wt as writeJsonFile, x as formatManagementError, xn as parseEnvironmentSelection, xt as fileExists, y as executeCliCommandPlan, yn as withOutputOnlyErrorHandling, yt as writeMetadataRecordMap, z as formatFieldChange, zn as sdkSettingsUpdateSchema, zt as revalidatePushConfigPlan } from "../entryCommands-CoGu0G2R.mjs";
5
+ import { r as CLI_VERSION, t as loadConfigFile } from "../load-config-j58KSqxl.mjs";
6
+ import { a as RichTextDocSchema, c as isRecord$18, i as findUnfilledSentinels, n as buildEntryScaffold, o as isRichTextDoc, r as validateEntryInstance, s as isObjectRecord, t as eventUpsertInputSchema } from "../eventUpsertInputSchema-B0ubhUu5.mjs";
7
+ import { a as prepareScaffoldExecution, c as LOCAL_PACKAGE_VENDOR_DIR, d as loadGeneratedPackageRanges, i as preflightScaffoldTargets, n as createScaffoldCompletion, s as deriveScaffoldLocalManifest, t as deriveScaffoldPlan } from "../plan-C4IphkPc.mjs";
8
8
  import { config } from "dotenv";
9
9
  import { Command, CommanderError } from "commander";
10
10
  import * as fs$2 from "fs/promises";
@@ -11972,19 +11972,23 @@ function eyebrowClassName(baseClass) {
11972
11972
  //#endregion
11973
11973
  //#region ../blocks/src/system/node/fragments/headingGroup.ts
11974
11974
  function headingGroup(opts) {
11975
- const { eyebrowPath, titlePath, containerClass = "text-center", className, eyebrowClass = "rb-eyebrow heading-eyebrow", titleClass = "heading-title", eyebrowStyle, titleStyle } = opts;
11975
+ const { eyebrowPath, titlePath, containerClass = "text-center", className, eyebrowClass = "rb-eyebrow heading-eyebrow", titleClass = "heading-title", eyebrowStyle, titleStyle, lookSlots = false } = opts;
11976
+ const slot = (name) => lookSlots ? lookSlot(name) : {};
11976
11977
  const finalContainerClass = className ? `${containerClass} ${className}` : containerClass;
11977
11978
  return stack({
11978
11979
  gap: "sm",
11979
- className: finalContainerClass
11980
+ className: finalContainerClass,
11981
+ ...slot("head")
11980
11982
  }, [eyebrowPath ? text({
11981
11983
  as: "p",
11982
11984
  className: eyebrowClassName(eyebrowClass),
11983
- style: eyebrowStyle
11985
+ style: eyebrowStyle,
11986
+ ...slot("eyebrow")
11984
11987
  }, when(eyebrowPath), bind(eyebrowPath)) : null, text({
11985
11988
  as: "h2",
11986
11989
  className: titleClass,
11987
- style: titleStyle
11990
+ style: titleStyle,
11991
+ ...slot("heading")
11988
11992
  }, bind(titlePath))]);
11989
11993
  }
11990
11994
 
@@ -12379,12 +12383,13 @@ function ctaContent(paths) {
12379
12383
  //#endregion
12380
12384
  //#region ../blocks/src/system/node/fragments/ctaRow.ts
12381
12385
  function ctaRow(opts) {
12382
- const { collectionPath = "content.ctas", itemName: item = "cta", gap = "sm", justify = "center", containerClassName: extraClass, itemAction } = opts ?? {};
12386
+ const { collectionPath = "content.ctas", itemName: item = "cta", gap = "sm", justify = "center", containerClassName: extraClass, itemAction, lookSlots = false } = opts ?? {};
12383
12387
  const containerClassName = ["cta-row", extraClass].filter(Boolean).join(" ");
12384
12388
  return inline({
12385
12389
  gap,
12386
12390
  justify,
12387
- className: containerClassName
12391
+ className: containerClassName,
12392
+ ...lookSlots ? lookSlot("actions") : {}
12388
12393
  }, [ctaButton({
12389
12394
  itemAction,
12390
12395
  basePath: item,
@@ -15289,7 +15294,8 @@ const ctaRowFragment = defineFragment({
15289
15294
  layout: [ctaRow({
15290
15295
  collectionPath: "content.ctas",
15291
15296
  itemName: "cta",
15292
- justify: "center"
15297
+ justify: "center",
15298
+ lookSlots: true
15293
15299
  })]
15294
15300
  });
15295
15301
 
@@ -15386,8 +15392,12 @@ const ctaCopyFragment = defineFragment({
15386
15392
  layout: [headingGroup({
15387
15393
  eyebrowPath: "content.eyebrow",
15388
15394
  titlePath: "content.title",
15389
- className: "cta-heading"
15390
- }), richText({ className: "cta-content rb-prose rb-mx-auto rb-max-w-none" }, when("content"), bind("content"))]
15395
+ className: "cta-heading",
15396
+ lookSlots: true
15397
+ }), richText({
15398
+ ...lookSlot("intro"),
15399
+ className: "cta-content rb-prose rb-mx-auto rb-max-w-none"
15400
+ }, when("content"), bind("content"))]
15391
15401
  });
15392
15402
 
15393
15403
  //#endregion
@@ -15983,12 +15993,16 @@ const formCopyFragment = defineFragment({
15983
15993
  * Standard API bindings that all interactive blocks need.
15984
15994
  */
15985
15995
  const apiBindings = {
15996
+ now: { $bind: { from: "$root.now" } },
15986
15997
  siteId: { $bind: { from: "$root.siteId" } },
15987
15998
  apiBaseUrl: { $bind: { from: "$root.apiBaseUrl" } }
15988
15999
  };
15989
16000
  function withApiBindings(props$1, options) {
15990
16001
  const includeApiBaseUrl = options?.includeApiBaseUrl ?? true;
15991
- const bindings = includeApiBaseUrl ? apiBindings : { siteId: apiBindings.siteId };
16002
+ const bindings = includeApiBaseUrl ? apiBindings : {
16003
+ siteId: apiBindings.siteId,
16004
+ now: apiBindings.now
16005
+ };
15992
16006
  return {
15993
16007
  ...bindings,
15994
16008
  ...props$1
@@ -16207,17 +16221,21 @@ const blogFeaturedPostFragment = defineFragment({
16207
16221
  function faqHeadingLayout() {
16208
16222
  return stack({
16209
16223
  gap: "sm",
16210
- className: "rb-mx-auto rb-max-w-3xl rb-text-center rb-mb-4"
16224
+ className: "rb-mx-auto rb-max-w-3xl rb-text-center rb-mb-4",
16225
+ ...lookSlot("head")
16211
16226
  }, [
16212
16227
  text({
16213
16228
  as: "p",
16229
+ ...lookSlot("eyebrow"),
16214
16230
  className: eyebrowClassName("faq-eyebrow rb-eyebrow heading-eyebrow")
16215
16231
  }, when("content.eyebrow"), bind("content.eyebrow")),
16216
16232
  text({
16217
16233
  as: "h2",
16234
+ ...lookSlot("heading"),
16218
16235
  className: "faq-title"
16219
16236
  }, when("content.title"), bind("content.title")),
16220
16237
  richText({
16238
+ ...lookSlot("intro"),
16221
16239
  className: "faq-description rb-prose rb-mx-auto rb-max-w-none",
16222
16240
  style: textColorStyle("mutedText")
16223
16241
  }, when("content.description"), bind("content.description"))
@@ -16263,11 +16281,13 @@ function createFaqAccordionLayout(options = {}) {
16263
16281
  itemName: "faqItem",
16264
16282
  indexName: "faqIndex",
16265
16283
  accordionProps: {
16284
+ ...lookSlot("items"),
16266
16285
  className: options.rootClassName ?? "accordion-root",
16267
16286
  type: "single",
16268
16287
  collapsible: true
16269
16288
  },
16270
16289
  itemProps: {
16290
+ ...lookSlot("item"),
16271
16291
  className: {
16272
16292
  $bind: {
16273
16293
  from: "content._cardStyles",
@@ -18915,10 +18935,10 @@ const ctaFullConfig = {
18915
18935
  fieldPriority: 2
18916
18936
  }],
18917
18937
  layout: styledSection({
18918
- children: sectionContainer(ctaComposition.layout, {
18938
+ children: props(lookSlot("copy"))(sectionContainer(ctaComposition.layout, {
18919
18939
  gap: "md",
18920
18940
  className: "rb-relative rb-text-center"
18921
- }),
18941
+ })),
18922
18942
  baseClass: ""
18923
18943
  }),
18924
18944
  description: "Centered call to action with optional background image.",
@@ -18973,7 +18993,7 @@ const singleButtonBlockDefinition = { manifest: singleButtonManifest };
18973
18993
  //#endregion
18974
18994
  //#region ../blocks/src/pageRenderItemRoles.ts
18975
18995
  /** Zero-based index; 1-based parity. Last > first > odd-count middle > parity.
18976
- * The reserved featured flag selects statement ahead of every position.
18996
+ * The declared featured role takes precedence over every position.
18977
18997
  */
18978
18998
  function itemRole(index, count, roles, featured = false) {
18979
18999
  const at = roles.at ?? {};
@@ -18983,7 +19003,7 @@ function itemRole(index, count, roles, featured = false) {
18983
19003
  [count % 2 === 1 && index === Math.floor(count / 2), "middle"],
18984
19004
  [true, index % 2 === 0 ? "odd" : "even"]
18985
19005
  ];
18986
- return featured ? "statement" : positions.filter(([applies]) => applies).map(([, position]) => at[position]).find((role) => role !== undefined) ?? roles.items;
19006
+ return featured && roles.featured ? roles.featured : positions.filter(([applies]) => applies).map(([, position]) => at[position]).find((role) => role !== undefined) ?? roles.items;
18987
19007
  }
18988
19008
 
18989
19009
  //#endregion
@@ -19027,6 +19047,11 @@ const lookListPresentationSchemaShape = {
19027
19047
  "statement",
19028
19048
  "highlight"
19029
19049
  ]).optional(),
19050
+ featured: z.enum([
19051
+ "card",
19052
+ "statement",
19053
+ "highlight"
19054
+ ]).optional(),
19030
19055
  at: z.partialRecord(z.enum([
19031
19056
  "first",
19032
19057
  "last",
@@ -19067,8 +19092,8 @@ function resolvedSurfaceBackground(surface, token) {
19067
19092
  return base ? `radial-gradient(ellipse at center, ${color}, ${base.startsWith("#") ? base : `rgb(var(--tb-${base}))`} 70%)` : color;
19068
19093
  }
19069
19094
  /** Pagination uses the displayed collection, just as declarative repeaters do. */
19070
- function listItemPresentation(presentation, index, count) {
19071
- const role = presentation.itemRoles ? itemRole(index, count, presentation.itemRoles) : undefined;
19095
+ function listItemPresentation(presentation, index, count, featured = false) {
19096
+ const role = presentation.itemRoles ? itemRole(index, count, presentation.itemRoles, featured) : undefined;
19072
19097
  const surface = role ? presentation.roleSurfaces?.[role] : undefined;
19073
19098
  return surface ? {
19074
19099
  ...presentation,
@@ -19727,7 +19752,8 @@ const defaultLayout = styledSection({
19727
19752
  baseClassName: "rb-relative rb-isolate",
19728
19753
  children: stack({
19729
19754
  gap: "lg",
19730
- className: "rb-w-full rb-max-w-4xl rb-mx-auto"
19755
+ className: "rb-w-full rb-max-w-4xl rb-mx-auto",
19756
+ ...lookSlot("layout")
19731
19757
  }, [headingLayout, accordionLayout])
19732
19758
  }),
19733
19759
  baseClass: ""
@@ -19735,7 +19761,10 @@ const defaultLayout = styledSection({
19735
19761
  const twoColumnLayout = styledSection({
19736
19762
  children: styledContainer({
19737
19763
  baseClassName: "rb-relative rb-isolate",
19738
- children: el("div", { className: "rb-faq-split-heading rb-grid rb-grid-cols-1 rb-gap-8 rb-lg-grid-cols-2 rb-items-start" }, [el("div", { className: "rb-faq-split-heading-copy" }, [headingLayout]), el("div", { className: "rb-faq-split-heading-accordion" }, [accordionLayout])])
19764
+ children: el("div", {
19765
+ className: "rb-faq-split-heading rb-grid rb-grid-cols-1 rb-gap-8 rb-lg-grid-cols-2 rb-items-start",
19766
+ ...lookSlot("layout")
19767
+ }, [el("div", { className: "rb-faq-split-heading-copy" }, [headingLayout]), el("div", { className: "rb-faq-split-heading-accordion" }, [accordionLayout])])
19739
19768
  }),
19740
19769
  baseClass: ""
19741
19770
  });
@@ -19744,7 +19773,8 @@ const questionColumnsLayout = styledSection({
19744
19773
  baseClassName: "rb-relative rb-isolate",
19745
19774
  children: stack({
19746
19775
  gap: "lg",
19747
- className: "rb-w-full"
19776
+ className: "rb-w-full",
19777
+ ...lookSlot("layout")
19748
19778
  }, [headingLayout, questionColumnsAccordionLayout])
19749
19779
  }),
19750
19780
  baseClass: ""
@@ -19819,6 +19849,32 @@ const faqBlockDefinition = { manifest: faqManifest };
19819
19849
  //#region ../blocks/src/contracts/site-chrome.ts
19820
19850
  const SITE_HEADER_MODES = ["default", "hidden"];
19821
19851
 
19852
+ //#endregion
19853
+ //#region ../blocks/src/bindings/formatDate.ts
19854
+ /** Admit stored civil dates or offset-aware instants before core projection. */
19855
+ function formatDate$3(value, context, options) {
19856
+ if (typeof value !== "string" || !value) return null;
19857
+ if (/^\d{4}-\d{2}-\d{2}$/u.test(value)) {
19858
+ try {
19859
+ return formatDate({
19860
+ kind: "civil",
19861
+ value: parseBookingLocalDate(value)
19862
+ }, context, options);
19863
+ } catch {
19864
+ return null;
19865
+ }
19866
+ }
19867
+ const instant = parseUtcIsoDateTimeString(value);
19868
+ if (!instant.ok) return null;
19869
+ return options.includeTime ? formatDateTime(instant.value, context, {
19870
+ date: options.date,
19871
+ zone: "omit"
19872
+ }) : formatDate({
19873
+ kind: "instant",
19874
+ value: instant.value
19875
+ }, context, options);
19876
+ }
19877
+
19822
19878
  //#endregion
19823
19879
  //#region ../blocks/src/system/transforms/registry/formatting.ts
19824
19880
  const uppercaseTransform = {
@@ -19838,20 +19894,7 @@ const dateFormatShort = {
19838
19894
  id: "date.formatShort",
19839
19895
  kind: "formatter",
19840
19896
  summary: "Format ISO date string into short human-readable label",
19841
- run: (value) => {
19842
- if (typeof value !== "string" || !value) return null;
19843
- const date = new Date(value);
19844
- if (Number.isNaN(date.getTime())) return null;
19845
- try {
19846
- return new Intl.DateTimeFormat(undefined, {
19847
- month: "short",
19848
- day: "numeric",
19849
- year: "numeric"
19850
- }).format(date);
19851
- } catch {
19852
- return null;
19853
- }
19854
- }
19897
+ run: (value, _options, context) => formatDate$3(value, context.dateContext, { date: "short" })
19855
19898
  };
19856
19899
  const arrayLengthTransform = {
19857
19900
  id: "array.length",
@@ -21165,6 +21208,24 @@ const mediaFromUrl = {
21165
21208
  };
21166
21209
  const mediaTransforms = [mediaFromUrl];
21167
21210
 
21211
+ //#endregion
21212
+ //#region ../blocks/src/bindings/dateContext.ts
21213
+ /** Wire admission: retained payloads may have missing or invalid zone values. */
21214
+ function resolveDateRuntimeContext(timeZone) {
21215
+ const businessTimeZone = resolveBookingBusinessTimeZone(timeZone);
21216
+ return {
21217
+ locale: inferSiteDateLocale(businessTimeZone),
21218
+ timeZone: businessTimeZone,
21219
+ viewerTimeZonePolicy: "business"
21220
+ };
21221
+ }
21222
+ /** Required clock facts are admitted at the outer runtime boundary. */
21223
+ function readRuntimeNow(value) {
21224
+ const parsed = typeof value === "string" ? parseUtcIsoDateTimeString(value) : null;
21225
+ if (!parsed?.ok) throw new Error("Block runtime requires an offset-aware clock snapshot");
21226
+ return parsed.value;
21227
+ }
21228
+
21168
21229
  //#endregion
21169
21230
  //#region ../blocks/src/system/transforms/registry/content.ts
21170
21231
  const supportedRichTextTokenSchema = z.enum(["year", "site_name"]);
@@ -21197,7 +21258,7 @@ function replaceTokenText(input, allowedTokens, tokenValues) {
21197
21258
  return input.replace(richTextTokenPattern, (fullMatch, rawToken) => {
21198
21259
  const token = String(rawToken ?? "").trim();
21199
21260
  if (!allowedTokens.has(token)) return fullMatch;
21200
- const resolved = tokenValues[token];
21261
+ const resolved = tokenValues[token]?.();
21201
21262
  if (resolved === null || resolved === undefined) return fullMatch;
21202
21263
  return resolved;
21203
21264
  });
@@ -21228,8 +21289,14 @@ function interpolateRichTextTokens(value, options, context) {
21228
21289
  if (!isRecord$18(value)) return value;
21229
21290
  const allowedTokens = new Set(options.tokens);
21230
21291
  const tokenValues = {
21231
- year: String(new Date().getFullYear()),
21232
- site_name: getSiteName(context)
21292
+ year: () => formatDate({
21293
+ kind: "instant",
21294
+ value: readRuntimeNow(context.now)
21295
+ }, context.dateContext, {
21296
+ date: "omit",
21297
+ year: "numeric"
21298
+ }),
21299
+ site_name: () => getSiteName(context)
21233
21300
  };
21234
21301
  return visitRichTextNode(value, allowedTokens, tokenValues);
21235
21302
  }
@@ -23961,6 +24028,7 @@ const bookingDescription = richText({}, bind("content.description"), when("conte
23961
24028
  * that hydration never rewrites.
23962
24029
  */
23963
24030
  const bookingWizard = bookingForm(withApiBindings({
24031
+ dateContext: { $bind: { from: "$root.dateContext" } },
23964
24032
  demoEmailSuppressed: { $bind: {
23965
24033
  from: "$root.demoEmailSuppressed",
23966
24034
  fallback: false
@@ -24745,7 +24813,7 @@ const publicEventBaseSchema = z.object({
24745
24813
  "archived"
24746
24814
  ]).optional(),
24747
24815
  timeZone: z.string(),
24748
- siteTimeZone: z.string().optional(),
24816
+ siteTimeZone: z.string().nullish().transform(resolveBookingBusinessTimeZone),
24749
24817
  pricing: publicEventPricingSchema,
24750
24818
  paymentTerms: blockBookingPaymentTermsSchema,
24751
24819
  cancellationPolicy: z.object({ refunds: z.object({
@@ -25715,7 +25783,7 @@ function getOrderedDayNames(startOfWeek, dayNames = DAY_NAMES) {
25715
25783
  }
25716
25784
  return [...dayNames];
25717
25785
  }
25718
- function formatCalendarTitle(viewDate, view, days, monthNames = MONTH_NAMES) {
25786
+ function formatCalendarTitle$1(viewDate, view, days, monthNames = MONTH_NAMES) {
25719
25787
  if (view === "week") {
25720
25788
  const weekStart = days[0];
25721
25789
  const weekEnd = days[6];
@@ -25729,7 +25797,7 @@ function formatCalendarTitle(viewDate, view, days, monthNames = MONTH_NAMES) {
25729
25797
  }
25730
25798
  return `${monthNames[viewDate.month - 1] ?? ""} ${viewDate.year}`;
25731
25799
  }
25732
- function buildCalendarViewport(input) {
25800
+ function buildCalendarViewport$1(input) {
25733
25801
  const monthNames = input.monthNames ?? MONTH_NAMES;
25734
25802
  if (input.view === "week") {
25735
25803
  const days$1 = getWeekCalendarDayTuple(input.anchorDate, input.startOfWeek);
@@ -25737,7 +25805,7 @@ function buildCalendarViewport(input) {
25737
25805
  kind: "week",
25738
25806
  anchorDate: input.anchorDate,
25739
25807
  days: days$1,
25740
- title: formatCalendarTitle(input.anchorDate, "week", days$1, monthNames)
25808
+ title: formatCalendarTitle$1(input.anchorDate, "week", days$1, monthNames)
25741
25809
  };
25742
25810
  }
25743
25811
  const days = getMonthCalendarDays(input.anchorDate.year, input.anchorDate.month, input.startOfWeek);
@@ -25745,7 +25813,7 @@ function buildCalendarViewport(input) {
25745
25813
  kind: "month",
25746
25814
  anchorDate: input.anchorDate,
25747
25815
  days,
25748
- title: formatCalendarTitle(input.anchorDate, "month", days, monthNames)
25816
+ title: formatCalendarTitle$1(input.anchorDate, "month", days, monthNames)
25749
25817
  };
25750
25818
  }
25751
25819
 
@@ -25908,7 +25976,7 @@ function buildTimetableModel$1(input) {
25908
25976
  const viewportKeys = new Set(viewportDays.map(formatDateKey));
25909
25977
  const parsed = parseAllItems(input.items, input);
25910
25978
  const inViewport = deriveVisibleSegments(parsed.drafts, viewportKeys);
25911
- const hourRange = resolveHourRange(inViewport, bounds);
25979
+ const hourRange = input.hours === undefined ? resolveHourRange(inViewport, bounds) : fixedHourRange(input.hours);
25912
25980
  const laid = layoutCellsByDate(inViewport, hourRange, bounds);
25913
25981
  const days = assembleDays(viewportDays, laid.cellsByDate, input.today);
25914
25982
  return {
@@ -26125,6 +26193,13 @@ function resolveHourRange(segments, bounds) {
26125
26193
  endHour: Math.max(startHour + 1, endHour)
26126
26194
  };
26127
26195
  }
26196
+ function fixedHourRange(hours$1) {
26197
+ const startHour = Math.min(23, Math.max(0, Math.floor(hours$1.startHour)));
26198
+ return {
26199
+ startHour,
26200
+ endHour: Math.min(24, Math.max(startHour + 1, Math.ceil(hours$1.endHour)))
26201
+ };
26202
+ }
26128
26203
  function buildHourTicks(startHour, endHour) {
26129
26204
  const ticks = [];
26130
26205
  for (let hour = startHour; hour <= endHour; hour += 1) {
@@ -26176,6 +26251,138 @@ function parseDateKey(dateKey) {
26176
26251
  };
26177
26252
  }
26178
26253
 
26254
+ //#endregion
26255
+ //#region ../calendar/dist/month.js
26256
+ /**
26257
+ * Month model — a month's weeks, with each item placed on the days it covers.
26258
+ *
26259
+ * Generic over `T`, like the timetable model. An item that starts and ends on
26260
+ * one local day is listed on that day. An item that runs across days becomes
26261
+ * one span per week row it touches, packed into lanes so spans in the same
26262
+ * week never overlap. Separate items stay separate, whatever they're called.
26263
+ *
26264
+ * Only the month's own days carry items. The leading and trailing days that
26265
+ * fill out the first and last weeks are padding: callers usually load just the
26266
+ * month, so those days have nothing reliable to say.
26267
+ */
26268
+ /** Whole weeks from the one holding the 1st to the one holding the last day: four to six of them. */
26269
+ function getMonthGridWeeks(viewDate, startOfWeek) {
26270
+ const first = getWeekCalendarDays(getStartOfMonth(viewDate), startOfWeek)[0] ?? getStartOfMonth(viewDate);
26271
+ const lastKey = formatDateKey(getEndOfMonth(viewDate));
26272
+ const weekStarts = Array.from({ length: 6 }, (_$1, index) => addDays(first, index * 7)).filter((start) => formatDateKey(start) <= lastKey);
26273
+ return weekStarts.map((start) => Array.from({ length: 7 }, (_$1, index) => addDays(start, index)));
26274
+ }
26275
+ function parseItem(item, input) {
26276
+ const startsAtIso = input.getStartsAt(item);
26277
+ const startsAt = Date.parse(startsAtIso);
26278
+ const endsAt = Date.parse(input.getEndsAt(item));
26279
+ if (Number.isNaN(startsAt)) return {
26280
+ ok: false,
26281
+ diagnostic: {
26282
+ item,
26283
+ reason: "invalid-start"
26284
+ }
26285
+ };
26286
+ if (Number.isNaN(endsAt)) return {
26287
+ ok: false,
26288
+ diagnostic: {
26289
+ item,
26290
+ reason: "invalid-end"
26291
+ }
26292
+ };
26293
+ if (endsAt <= startsAt) return {
26294
+ ok: false,
26295
+ diagnostic: {
26296
+ item,
26297
+ reason: "non-positive-duration"
26298
+ }
26299
+ };
26300
+ const timeZone = input.getTimeZone(item);
26301
+ const firstKey = getLocalDateKey(startsAtIso, timeZone);
26302
+ const lastKey = getLocalDateKey(new Date(endsAt - 1).toISOString(), timeZone);
26303
+ if (firstKey === null || lastKey === null) return {
26304
+ ok: false,
26305
+ diagnostic: {
26306
+ item,
26307
+ reason: "invalid-start"
26308
+ }
26309
+ };
26310
+ return {
26311
+ ok: true,
26312
+ placed: {
26313
+ item,
26314
+ firstKey,
26315
+ lastKey,
26316
+ startsAt,
26317
+ orderKey: input.getId(item)
26318
+ }
26319
+ };
26320
+ }
26321
+ function byStart$1(left, right) {
26322
+ return left.startsAt - right.startsAt || left.orderKey.localeCompare(right.orderKey);
26323
+ }
26324
+ function spanInWeek(placed, monthKeys) {
26325
+ const columns = monthKeys.map((key, column) => ({
26326
+ key,
26327
+ column
26328
+ })).filter((day) => day.key !== null && day.key >= placed.firstKey && day.key <= placed.lastKey);
26329
+ const first = columns[0];
26330
+ const last = columns[columns.length - 1];
26331
+ if (first === undefined || last === undefined) return null;
26332
+ return {
26333
+ item: placed.item,
26334
+ firstColumn: first.column,
26335
+ lastColumn: last.column,
26336
+ continuesBefore: placed.firstKey < first.key,
26337
+ continuesAfter: placed.lastKey > last.key
26338
+ };
26339
+ }
26340
+ /** Longest first within a start column, then each span takes the lowest free lane. */
26341
+ function packSpans(drafts) {
26342
+ const ordered = [...drafts].sort((left, right) => left.firstColumn - right.firstColumn || right.lastColumn - right.firstColumn - (left.lastColumn - left.firstColumn));
26343
+ return ordered.reduce((packed, draft) => {
26344
+ const taken = new Set(packed.filter((span) => span.firstColumn <= draft.lastColumn && span.lastColumn >= draft.firstColumn).map((span) => span.lane));
26345
+ const lane = Array.from({ length: taken.size + 1 }, (_$1, index) => index).find((index) => !taken.has(index)) ?? taken.size;
26346
+ return [...packed, {
26347
+ ...draft,
26348
+ lane
26349
+ }];
26350
+ }, []);
26351
+ }
26352
+ function buildMonthModel(input) {
26353
+ const parsed = input.items.map((item) => parseItem(item, input));
26354
+ const placed = parsed.flatMap((result) => result.ok ? [result.placed] : []).sort(byStart$1);
26355
+ const diagnostics = parsed.flatMap((result) => result.ok ? [] : [result.diagnostic]);
26356
+ const singleDay = placed.filter((item) => item.firstKey === item.lastKey);
26357
+ const multiDay = placed.filter((item) => item.firstKey !== item.lastKey);
26358
+ const month = input.viewDate.month;
26359
+ const weeks = getMonthGridWeeks(input.viewDate, input.startOfWeek).map((dates) => {
26360
+ const monthKeys = dates.map((date) => date.month === month ? formatDateKey(date) : null);
26361
+ const spans = packSpans(multiDay.flatMap((item) => {
26362
+ const span = spanInWeek(item, monthKeys);
26363
+ return span === null ? [] : [span];
26364
+ }));
26365
+ return {
26366
+ days: dates.map((date, column) => {
26367
+ const key = monthKeys[column] ?? null;
26368
+ return {
26369
+ date,
26370
+ dateKey: formatDateKey(date),
26371
+ inMonth: key !== null,
26372
+ isToday: isSameCalendarDay(date, input.today),
26373
+ items: key === null ? [] : singleDay.filter((item) => item.firstKey === key).map((item) => item.item)
26374
+ };
26375
+ }),
26376
+ spans,
26377
+ laneCount: spans.reduce((count, span) => Math.max(count, span.lane + 1), 0)
26378
+ };
26379
+ });
26380
+ return {
26381
+ weeks,
26382
+ diagnostics
26383
+ };
26384
+ }
26385
+
26179
26386
  //#endregion
26180
26387
  //#region ../calendar/dist/group.js
26181
26388
  /**
@@ -26204,6 +26411,36 @@ function groupItemsByDate(items, accessors) {
26204
26411
  return grouped;
26205
26412
  }
26206
26413
 
26414
+ //#endregion
26415
+ //#region ../calendar/dist/calendarLinks.js
26416
+ function compactUtc(date) {
26417
+ return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
26418
+ }
26419
+ function calendarLinks(entry) {
26420
+ const google = new URL("https://calendar.google.com/calendar/render");
26421
+ google.search = new URLSearchParams({
26422
+ action: "TEMPLATE",
26423
+ text: entry.title,
26424
+ dates: `${compactUtc(entry.startsAt)}/${compactUtc(entry.endsAt)}`,
26425
+ details: entry.description ?? "",
26426
+ location: entry.location ?? ""
26427
+ }).toString();
26428
+ const outlook = new URL("https://outlook.live.com/calendar/0/deeplink/compose");
26429
+ outlook.search = new URLSearchParams({
26430
+ path: "/calendar/action/compose",
26431
+ rru: "addevent",
26432
+ subject: entry.title,
26433
+ startdt: entry.startsAt.toISOString(),
26434
+ enddt: entry.endsAt.toISOString(),
26435
+ body: entry.description ?? "",
26436
+ location: entry.location ?? ""
26437
+ }).toString();
26438
+ return {
26439
+ googleUrl: google.toString(),
26440
+ outlookUrl: outlook.toString()
26441
+ };
26442
+ }
26443
+
26207
26444
  //#endregion
26208
26445
  //#region ../../node_modules/.pnpm/@date-fns+tz@1.5.0/node_modules/@date-fns/tz/constants/index.js
26209
26446
  /**
@@ -27461,131 +27698,125 @@ function calendarEvent(entry, generatedAt) {
27461
27698
 
27462
27699
  //#endregion
27463
27700
  //#region ../blocks/src/system/runtime/nodes/events/domain/dates.ts
27464
- /**
27465
- * Date and time presentation for events: the single place an event's
27466
- * timestamps become strings or date buckets. Calendar-grid primitives are
27467
- * re-exported from @riverbankcms/calendar so block code has one import.
27468
- */
27469
- /**
27470
- * The English locale a site's public dates are written in, read from the
27471
- * site's own time zone (Will, 2026-09-26: "date style from site time zone").
27472
- *
27473
- * Sites don't store a locale, and every public word around a date is English,
27474
- * so the only open question is which English conventions to follow: day or
27475
- * month first. The site's time zone is the one
27476
- * fact every site carries about where it is. It is the site's, never an
27477
- * event's: a venue left at UTC must not turn a UK site's dates American.
27478
- * Anywhere this can't place (UTC, the Americas, Asia) keeps US English, which
27479
- * is what these helpers wrote before sites were placed at all.
27480
- */
27701
+ /** Event presentation adapters; core owns projection, clocks and ranges. */
27481
27702
  function eventDateLocale(siteTimeZone) {
27482
27703
  return inferSiteDateLocale(siteTimeZone);
27483
27704
  }
27484
- /**
27485
- * Formatting options for an event's dates: the times in the event's own zone,
27486
- * written in its site's locale. A record from an older server that doesn't
27487
- * name the site's zone falls back to the event's.
27488
- */
27489
- function eventDateOptions(place) {
27490
- return {
27491
- timeZone: place.timeZone ?? null,
27492
- locale: eventDateLocale(place.siteTimeZone ?? place.timeZone)
27493
- };
27705
+ function eventCalendarWeekStart(timeZone, override) {
27706
+ return override ?? (inferSiteDateLocale(resolveBookingBusinessTimeZone(timeZone)) === "en-US" ? "sunday" : "monday");
27494
27707
  }
27495
- /**
27496
- * Start and end times as cards write them, in the site's locale:
27497
- * "10:00 to 16:00" in the UK, "10:00 AM to 4:00 PM" in the US.
27498
- */
27499
- function formatEventTimeRange$1(times, options) {
27500
- return `${formatTime(times.startsAt, options)} to ${formatTime(times.endsAt, options)}`;
27708
+ /** Admit older wire zones once; the booking business fallback remains London. */
27709
+ function eventDateOptions(place) {
27710
+ const siteTimeZone = resolveBookingBusinessTimeZone(place.siteTimeZone);
27711
+ const resolved = resolveBookingDisplayContext({
27712
+ sessionTimeZone: resolveBookingTimeZoneId(place.timeZone, siteTimeZone),
27713
+ siteTimeZone,
27714
+ locale: inferSiteDateLocale(siteTimeZone),
27715
+ isOnline: place.isOnline === true || place.deliveryMode === "online" || place.deliveryMode === "hybrid",
27716
+ surface: "public"
27717
+ });
27718
+ return {
27719
+ ...resolved.context,
27720
+ zone: resolved.zone
27721
+ };
27722
+ }
27723
+ /** Overnight spans keep their end day; folds retain both distinct offsets. */
27724
+ function formatEventTimeRange(times, options) {
27725
+ const start = toUtcIsoDateTimeString(times.startsAt);
27726
+ const end = toUtcIsoDateTimeString(times.endsAt);
27727
+ const startDay = getLocalScheduleDate(new Date(start), options.timeZone);
27728
+ const endDay = getLocalScheduleDate(new Date(end), options.timeZone);
27729
+ return startDay.year === endDay.year && startDay.month === endDay.month && startDay.day === endDay.day ? formatTimeRange(start, end, options, { zone: options.zone }) : formatDateTimeRange({
27730
+ start,
27731
+ end
27732
+ }, options, {
27733
+ date: "short",
27734
+ zone: options.zone
27735
+ });
27501
27736
  }
27502
- /** Use the event timezone when present, otherwise the calendar/site timezone. */
27503
27737
  function resolveEventTimeZone(event, calendarTimeZone) {
27504
- return event.timeZone || calendarTimeZone;
27738
+ return resolveBookingTimeZoneId(event.timeZone, resolveBookingBusinessTimeZone(calendarTimeZone));
27505
27739
  }
27506
- /**
27507
- * Format date for display.
27508
- * Returns: "Monday, January 15, 2025"
27509
- */
27510
- function formatDate(isoTimestamp, options) {
27511
- return formatBookingDate(toScheduledBookingDateTime({
27512
- startsAt: isoTimestamp,
27513
- timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
27514
- locale: options?.locale ?? "en-US"
27515
- }), {
27516
- weekday: "long",
27517
- year: "numeric",
27518
- month: "long",
27519
- day: "numeric"
27740
+ function formatDate$1(isoTimestamp, options) {
27741
+ return formatDate({
27742
+ kind: "instant",
27743
+ value: toUtcIsoDateTimeString(isoTimestamp)
27744
+ }, options, {
27745
+ date: "short",
27746
+ weekday: "long"
27520
27747
  });
27521
27748
  }
27522
- /**
27523
- * Whether the locale reads a 24-hour clock, where hours are written with two
27524
- * digits ("09:00"), as the dashboard writes them.
27525
- */
27526
- function usesTwentyFourHourClock(locale) {
27527
- return new Intl.DateTimeFormat(locale, { hour: "numeric" }).resolvedOptions().hour12 === false;
27528
- }
27529
- /**
27530
- * Format time for display, on the locale's own clock.
27531
- * Returns: "9:00 AM" (en-US) or "09:00" (en-GB)
27532
- */
27533
27749
  function formatTime(isoTimestamp, options) {
27534
- const locale = options?.locale ?? "en-US";
27535
- return formatBookingTime(toScheduledBookingDateTime({
27536
- startsAt: isoTimestamp,
27537
- timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
27538
- locale
27539
- }), {
27540
- hour: usesTwentyFourHourClock(locale) ? "2-digit" : "numeric",
27541
- minute: "2-digit"
27542
- });
27750
+ return formatTime$1(toUtcIsoDateTimeString(isoTimestamp), options, { zone: options.zone });
27543
27751
  }
27544
27752
  function formatEventDateParts(args) {
27545
- const scheduledRange = toScheduledBookingDateRange({
27546
- startsAt: args.startsAt,
27547
- endsAt: args.endsAt,
27548
- timeZone: toBookingTimeZone(args.timeZone ?? "UTC"),
27549
- locale: args.locale ?? "en-US"
27550
- });
27551
- const { date, time: start } = formatBookingDateParts(scheduledRange, { dateFormat: {
27552
- weekday: "long",
27553
- year: "numeric",
27554
- month: "long",
27555
- day: "numeric"
27556
- } });
27557
- const end = formatBookingTime(toScheduledBookingDateTime({
27558
- startsAt: args.endsAt,
27559
- timeZone: scheduledRange.timeZone,
27560
- locale: args.locale ?? "en-US"
27561
- }));
27562
27753
  return {
27563
- date,
27564
- timeRange: args.locale ? `${formatTime(args.startsAt, args)} – ${formatTime(args.endsAt, args)}` : `${start} – ${end}`
27754
+ date: formatDate$1(args.startsAt, args),
27755
+ timeRange: formatEventTimeRange(args, args)
27565
27756
  };
27566
27757
  }
27567
- function formatEventCompactDateRail(isoTimestamp, options, letterCase = "capitals") {
27568
- const locale = options?.locale ?? "en-US";
27569
- const dateTime = toScheduledBookingDateTime({
27570
- startsAt: isoTimestamp,
27571
- timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
27572
- locale
27758
+ /** Civil calendar headings never acquire an instant's timezone shift. */
27759
+ function formatCalendarTitle(viewDate, view, days, timeZone) {
27760
+ const context = eventDateOptions({
27761
+ siteTimeZone: timeZone,
27762
+ timeZone
27573
27763
  });
27574
- const cased = (value) => letterCase === "capitals" ? value.toLocaleUpperCase(locale) : value;
27575
- const weekday = cased(formatBookingDate(dateTime, { weekday: "short" }));
27576
- const month = cased(formatBookingDate(dateTime, { month: "short" }).slice(0, 3));
27577
- const day = formatBookingDate(dateTime, { day: "numeric" });
27578
- const label = formatBookingDate(dateTime, {
27579
- weekday: "long",
27580
- year: "numeric",
27581
- month: "long",
27582
- day: "numeric"
27764
+ const first = days[0];
27765
+ const last = days[6];
27766
+ return view === "week" && first && last ? formatDateRange({
27767
+ kind: "civil",
27768
+ value: first
27769
+ }, {
27770
+ kind: "civil",
27771
+ value: last
27772
+ }, context, { date: "short" }) : formatDate({
27773
+ kind: "civil",
27774
+ value: viewDate
27775
+ }, context, {
27776
+ date: "long",
27777
+ day: "omit"
27583
27778
  });
27779
+ }
27780
+ function buildCalendarViewport(input) {
27781
+ const viewport = buildCalendarViewport$1(input);
27584
27782
  return {
27585
- weekday,
27586
- day,
27587
- month,
27588
- label
27783
+ ...viewport,
27784
+ title: formatCalendarTitle(input.anchorDate, input.view, viewport.days, input.timeZone)
27785
+ };
27786
+ }
27787
+ /** Tick digits are civil clock values, not session instants. */
27788
+ function formatEventHourLabel(hour, timeZone) {
27789
+ return formatTime$1(toUtcIsoDateTimeString(`2000-01-01T${String(hour % 24).padStart(2, "0")}:00:00Z`), {
27790
+ locale: eventDateLocale(timeZone),
27791
+ timeZone: resolveBookingBusinessTimeZone("UTC")
27792
+ }, { zone: "omit" });
27793
+ }
27794
+ function formatEventCompactDateRail(isoTimestamp, options, letterCase = "capitals") {
27795
+ const instant = {
27796
+ kind: "instant",
27797
+ value: toUtcIsoDateTimeString(isoTimestamp)
27798
+ };
27799
+ const cased = (value) => letterCase === "capitals" ? value.toLocaleUpperCase(options.locale) : value;
27800
+ return {
27801
+ weekday: cased(formatDate(instant, options, {
27802
+ date: "omit",
27803
+ weekday: "short",
27804
+ year: "omit"
27805
+ })),
27806
+ day: formatDate(instant, options, {
27807
+ date: "omit",
27808
+ day: "numeric",
27809
+ year: "omit"
27810
+ }),
27811
+ month: cased(formatDate(instant, options, {
27812
+ date: "short",
27813
+ day: "omit",
27814
+ year: "omit"
27815
+ }).slice(0, 3)),
27816
+ label: formatDate(instant, options, {
27817
+ date: "long",
27818
+ weekday: "long"
27819
+ })
27589
27820
  };
27590
27821
  }
27591
27822
  /**
@@ -27612,17 +27843,13 @@ function groupEventsByDate(events, calendarTimeZone) {
27612
27843
  getStartsAt: (event) => event.startsAt,
27613
27844
  getTimeZone: () => calendarTimeZone
27614
27845
  });
27615
- const map = new Map();
27616
- for (const [dateKey, bucket] of grouped) {
27617
- map.set(dateKey, [...bucket]);
27618
- }
27619
- return map;
27846
+ return new Map([...grouped].map(([dateKey, bucket]) => [dateKey, [...bucket]]));
27620
27847
  }
27621
27848
  /** Stable local-day identity shared by ledger continuations and day groups. */
27622
27849
  function eventLocalDateKey(event) {
27623
27850
  return getBookingLocalDateKey(toScheduledBookingDateTime({
27624
27851
  startsAt: event.startsAt,
27625
- timeZone: toBookingTimeZone(event.timeZone ?? "UTC"),
27852
+ timeZone: resolveBookingBusinessTimeZone(event.timeZone),
27626
27853
  locale: "en-GB"
27627
27854
  }));
27628
27855
  }
@@ -27741,11 +27968,11 @@ function orderedUniqueEventOccurrences(events) {
27741
27968
  return sorted.filter((event, index) => sorted.findIndex((candidate) => candidate.occurrenceId === event.occurrenceId) === index);
27742
27969
  }
27743
27970
  function formatEventQuickDate(event, style = "legacy") {
27744
- const options = style === "site" ? eventDateOptions(event) : { timeZone: event.timeZone };
27971
+ const options = eventDateOptions(event);
27745
27972
  const dateRail = formatEventCompactDateRail(event.startsAt, options, style === "site" ? "as-written" : "capitals");
27746
27973
  return {
27747
27974
  dateTime: event.startsAt,
27748
- label: `${dateRail.weekday} ${dateRail.day} ${dateRail.month} · ${formatTime(event.startsAt, options)}`
27975
+ label: `${dateRail.weekday} ${dateRail.day} ${dateRail.month}, ${formatTime(event.startsAt, options)}`
27749
27976
  };
27750
27977
  }
27751
27978
  function compareQuickDateEvents(left, right) {
@@ -27789,7 +28016,7 @@ function resolveMonthSelectedDay(input) {
27789
28016
  //#region ../blocks/src/system/runtime/nodes/events/domain/listWeek.ts
27790
28017
  /** Civil weeks use the site zone, including weeks containing a clock change. */
27791
28018
  function buildEventListWeek(anchor, startOfWeek, timeZone) {
27792
- const viewport = buildCalendarViewport({
28019
+ const viewport = buildCalendarViewport$1({
27793
28020
  view: "week",
27794
28021
  anchorDate: anchor,
27795
28022
  startOfWeek
@@ -27799,15 +28026,17 @@ function buildEventListWeek(anchor, startOfWeek, timeZone) {
27799
28026
  endDate: formatDateKey(viewport.days[6]),
27800
28027
  timeZone
27801
28028
  });
27802
- const formatter = new Intl.DateTimeFormat(eventDateLocale(timeZone), {
27803
- day: "numeric",
27804
- month: "short",
27805
- year: "numeric",
27806
- timeZone: "UTC"
27807
- });
27808
- const labels = [viewport.days[0], viewport.days[6]].map((date) => formatter.format(new Date(`${formatDateKey(date)}T12:00:00.000Z`)));
27809
28029
  return {
27810
- title: labels.join(" – "),
28030
+ title: formatDateRange({
28031
+ kind: "civil",
28032
+ value: viewport.days[0]
28033
+ }, {
28034
+ kind: "civil",
28035
+ value: viewport.days[6]
28036
+ }, {
28037
+ locale: eventDateLocale(timeZone),
28038
+ timeZone: resolveBookingBusinessTimeZone(timeZone)
28039
+ }, { date: "short" }),
27811
28040
  from: window$1.startAt.toISOString(),
27812
28041
  to: new Date(window$1.endExclusive.getTime() - 1).toISOString()
27813
28042
  };
@@ -27882,33 +28111,6 @@ function buildBookingInstalmentCadenceLabel(plan) {
27882
28111
 
27883
28112
  //#endregion
27884
28113
  //#region ../blocks/src/system/runtime/nodes/event-registration/bookingCalendar.ts
27885
- function compactUtc(date) {
27886
- return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
27887
- }
27888
- function calendarLinks(entry) {
27889
- const google = new URL("https://calendar.google.com/calendar/render");
27890
- google.search = new URLSearchParams({
27891
- action: "TEMPLATE",
27892
- text: entry.title,
27893
- dates: `${compactUtc(entry.startsAt)}/${compactUtc(entry.endsAt)}`,
27894
- details: entry.description ?? "",
27895
- location: entry.location ?? ""
27896
- }).toString();
27897
- const outlook = new URL("https://outlook.live.com/calendar/0/deeplink/compose");
27898
- outlook.search = new URLSearchParams({
27899
- path: "/calendar/action/compose",
27900
- rru: "addevent",
27901
- subject: entry.title,
27902
- startdt: entry.startsAt.toISOString(),
27903
- enddt: entry.endsAt.toISOString(),
27904
- body: entry.description ?? "",
27905
- location: entry.location ?? ""
27906
- }).toString();
27907
- return {
27908
- googleUrl: google.toString(),
27909
- outlookUrl: outlook.toString()
27910
- };
27911
- }
27912
28114
  /** Public schedule facts only; online joining instructions stay in email (issue 1580). */
27913
28115
  function bookingCalendar(event, identity) {
27914
28116
  if (!event) return null;
@@ -28234,6 +28436,74 @@ function scrollToBlock(anchorId) {
28234
28436
  });
28235
28437
  }
28236
28438
 
28439
+ //#endregion
28440
+ //#region ../theme-core/src/themeScopeUtils.ts
28441
+ /** Nearest-first scopes: visual identity and breakpoint context are independent. */
28442
+ function resolveThemeScopeContext(scopes$1) {
28443
+ return {
28444
+ visualScope: scopes$1.find((scope) => scope.themeId !== null) ?? null,
28445
+ responsiveScope: scopes$1.find((scope) => scope.responsiveMode !== null) ?? null,
28446
+ inheritanceScopes: scopes$1.filter((scope) => scope.themeId !== null || scope.responsiveMode !== null)
28447
+ };
28448
+ }
28449
+ /** DOM boundary for readers of the shared, pure scope decision. */
28450
+ function readThemeScopeContext(source) {
28451
+ const scopes$1 = readAncestorScopes(source);
28452
+ return resolveThemeScopeContext(scopes$1);
28453
+ }
28454
+ function readAncestorScopes(source) {
28455
+ if (!source) return [];
28456
+ const ancestors = readAncestorScopes(source.parentElement);
28457
+ if (!(source instanceof HTMLElement)) return ancestors;
28458
+ const themeId = source.getAttribute("data-theme-scope");
28459
+ const responsiveMode = source.getAttribute("data-rb-responsive-mode");
28460
+ return themeId !== null || responsiveMode !== null ? [{
28461
+ element: source,
28462
+ themeId,
28463
+ responsiveMode
28464
+ }, ...ancestors] : ancestors;
28465
+ }
28466
+ /**
28467
+ * Build data attributes object for theme axes.
28468
+ *
28469
+ * These attributes are used by CSS selectors to apply
28470
+ * axis-specific styles (tone, energy, density, etc.)
28471
+ */
28472
+ function buildThemeDataAttrs(axes, themeId, skin) {
28473
+ return {
28474
+ ...skin !== undefined ? { "data-skin": skin } : {},
28475
+ "data-tone": axes.tone,
28476
+ "data-energy": axes.energy,
28477
+ "data-density": axes.density,
28478
+ "data-media": axes.mediaBias,
28479
+ "data-conv": axes.conversionBias,
28480
+ "data-motion": axes.motion,
28481
+ "data-theme-scope": themeId
28482
+ };
28483
+ }
28484
+ /** One projection for the server and client scope's identical CSS channels. */
28485
+ function themeRuntimeScopeProps(runtime) {
28486
+ return {
28487
+ cssVars: runtime.cssVars,
28488
+ buttonCss: runtime.buttonCss,
28489
+ headerCss: runtime.headerCss,
28490
+ footerCss: runtime.footerCss,
28491
+ cardCss: runtime.cardCss,
28492
+ accordionCss: runtime.accordionCss,
28493
+ inputCss: runtime.inputCss,
28494
+ layoutCss: runtime.layoutCss,
28495
+ statusCss: runtime.statusCss,
28496
+ progressCss: runtime.progressCss,
28497
+ typographyCss: runtime.typographyCss,
28498
+ eventCalendarCss: runtime.eventCalendarCss,
28499
+ blockVarsCss: runtime.blockVarsCss,
28500
+ blockCustomVarsCss: runtime.blockCustomVarsCss,
28501
+ blockCustomCss: runtime.blockCustomCss,
28502
+ atRulesCss: runtime.atRulesCss,
28503
+ customCss: runtime.customCss
28504
+ };
28505
+ }
28506
+
28237
28507
  //#endregion
28238
28508
  //#region ../blocks/src/system/runtime/shared/utils/themePortalRoot.ts
28239
28509
  function readThemeAttributes(scope) {
@@ -28266,17 +28536,28 @@ function observeThemeAttributes(scope, root) {
28266
28536
  }
28267
28537
  function createBodyThemePortalRoot(sourceEl) {
28268
28538
  if (typeof document === "undefined") return null;
28269
- const scope = sourceEl.closest(".theme-scope");
28270
- if (!(scope instanceof HTMLElement)) return null;
28271
- const root = document.createElement("div");
28272
- root.className = "theme-scope rb-theme-portal-root";
28273
- const observer = observeThemeAttributes(scope, root);
28274
- document.body.appendChild(root);
28539
+ const context = readThemeScopeContext(sourceEl);
28540
+ if (!context.visualScope && !context.responsiveScope) return null;
28541
+ const layers = [...context.inheritanceScopes].reverse().map(({ element }) => {
28542
+ const root$1 = document.createElement("div");
28543
+ root$1.className = "theme-scope";
28544
+ return {
28545
+ root: root$1,
28546
+ observer: observeThemeAttributes(element, root$1)
28547
+ };
28548
+ });
28549
+ layers.forEach((layer, index) => {
28550
+ (layers[index - 1]?.root ?? document.body).appendChild(layer.root);
28551
+ });
28552
+ const root = layers.at(-1)?.root;
28553
+ const outerRoot = layers[0]?.root;
28554
+ if (!root || !outerRoot) return null;
28555
+ root.classList.add("rb-theme-portal-root");
28275
28556
  return {
28276
28557
  root,
28277
28558
  cleanup: () => {
28278
- observer.disconnect();
28279
- root.remove();
28559
+ layers.forEach(({ observer }) => observer.disconnect());
28560
+ outerRoot.remove();
28280
28561
  }
28281
28562
  };
28282
28563
  }
@@ -29142,7 +29423,7 @@ function MembershipHero({ membershipName, classPrefix, onBookForSomeoneElse }) {
29142
29423
  * LICENSE file in the root directory of this source tree.
29143
29424
  */
29144
29425
  var require_react_dom_production = /* @__PURE__ */ __commonJSMin(((exports) => {
29145
- var React$77 = require_react();
29426
+ var React$78 = require_react();
29146
29427
  function formatProdErrorMessage(code) {
29147
29428
  var url = "https://react.dev/errors/" + code;
29148
29429
  if (1 < arguments.length) {
@@ -29179,7 +29460,7 @@ var require_react_dom_production = /* @__PURE__ */ __commonJSMin(((exports) => {
29179
29460
  implementation
29180
29461
  };
29181
29462
  }
29182
- var ReactSharedInternals = React$77.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
29463
+ var ReactSharedInternals = React$78.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
29183
29464
  function getCrossOriginStringAs(as, input) {
29184
29465
  if ("font" === as) return "";
29185
29466
  if ("string" === typeof input) return "use-credentials" === input ? input : "";
@@ -29322,7 +29603,7 @@ var require_react_dom_development = /* @__PURE__ */ __commonJSMin(((exports) =>
29322
29603
  return dispatcher;
29323
29604
  }
29324
29605
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
29325
- var React$86 = require_react(), Internals$1 = {
29606
+ var React$87 = require_react(), Internals$1 = {
29326
29607
  d: {
29327
29608
  f: noop$2,
29328
29609
  r: function() {
@@ -29338,7 +29619,7 @@ var require_react_dom_development = /* @__PURE__ */ __commonJSMin(((exports) =>
29338
29619
  },
29339
29620
  p: 0,
29340
29621
  findDOMNode: null
29341
- }, REACT_PORTAL_TYPE$1 = Symbol.for("react.portal"), ReactSharedInternals$1 = React$86.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
29622
+ }, REACT_PORTAL_TYPE$1 = Symbol.for("react.portal"), ReactSharedInternals$1 = React$87.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
29342
29623
  "function" === typeof Map && null != Map.prototype && "function" === typeof Map.prototype.forEach && "function" === typeof Set && null != Set.prototype && "function" === typeof Set.prototype.clear && "function" === typeof Set.prototype.forEach || console.error("React depends on Map and Set built-in types. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills");
29343
29624
  exports.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = Internals$1;
29344
29625
  exports.createPortal = function(children, container) {
@@ -29867,100 +30148,128 @@ function StepIndicator$1({ currentStep, totalSteps, stepLabels, classPrefix, emi
29867
30148
  function getPortalSubjectEmail(profile) {
29868
30149
  return profile?.email ?? null;
29869
30150
  }
29870
- /**
29871
- * Format date for display
29872
- */
30151
+ /** Public registration boundary: recover missing/invalid zones through the business owner. */
30152
+ function bookingContext(input) {
30153
+ const { zone, ...context } = eventDateOptions({
30154
+ ...input,
30155
+ deliveryMode: input.deliveryMode ?? undefined
30156
+ });
30157
+ return {
30158
+ context,
30159
+ zone
30160
+ };
30161
+ }
29873
30162
  function formatDate$2(isoTimestamp, options) {
29874
- return formatBookingDate(toScheduledBookingDateTime({
29875
- startsAt: isoTimestamp,
29876
- timeZone: toBookingTimeZone(options?.timeZone),
29877
- locale: options?.locale
29878
- }));
30163
+ const instant = parseUtcIsoDateTimeString(isoTimestamp);
30164
+ return instant.ok ? formatDate({
30165
+ kind: "instant",
30166
+ value: instant.value
30167
+ }, bookingContext(options).context, {
30168
+ date: "short",
30169
+ weekday: "long"
30170
+ }) : isoTimestamp;
29879
30171
  }
29880
- /**
29881
- * Format time for display
29882
- */
29883
- function formatTime$1(isoTimestamp, options) {
29884
- return formatBookingTime(toScheduledBookingDateTime({
29885
- startsAt: isoTimestamp,
29886
- timeZone: toBookingTimeZone(options?.timeZone),
29887
- locale: options?.locale
29888
- }));
30172
+ function formatTime$2(isoTimestamp, options) {
30173
+ const instant = parseUtcIsoDateTimeString(isoTimestamp);
30174
+ const { context, zone } = bookingContext(options);
30175
+ return instant.ok ? formatTime$1(instant.value, context, { zone }) : isoTimestamp;
29889
30176
  }
29890
- /**
29891
- * The locale a site's booking dates are written in: the one the site's time
29892
- * zone places it in (`eventDateLocale`, shared with event cards), or British
29893
- * English when the site names no zone.
29894
- */
29895
30177
  function bookingDateLocale(siteTimeZone) {
29896
- return siteTimeZone ? eventDateLocale(siteTimeZone) : "en-GB";
29897
- }
29898
- function bookingInstant(startsAt, input) {
29899
- return toScheduledBookingDateTime({
29900
- startsAt,
29901
- timeZone: toBookingTimeZone(input.timeZone),
29902
- locale: bookingDateLocale(input.siteTimeZone)
29903
- });
29904
- }
29905
- /** "7:00pm": a twelve-hour clock with a lowercase, unspaced day period. */
29906
- function bookingClock(startsAt, input) {
29907
- const time = formatBookingTime(bookingInstant(startsAt, input), {
29908
- hour: "numeric",
29909
- minute: "2-digit",
29910
- hourCycle: "h12"
29911
- });
29912
- return time.replace(/\s*([ap])\.?\s?m\.?$/i, (_match, period) => `${period.toLowerCase()}m`);
29913
- }
29914
- /** "7:00pm – 8:15pm", or just the start when there's no distinct end. */
29915
- function clockRange(input) {
29916
- const start = bookingClock(input.startsAt, input);
29917
- const end = input.endsAt ? bookingClock(input.endsAt, input) : null;
29918
- return end && end !== start ? `${start} – ${end}` : start;
30178
+ return inferSiteDateLocale(resolveBookingBusinessTimeZone(siteTimeZone));
29919
30179
  }
29920
30180
  /** A stored local clock has no date or DST offset; UTC preserves its digits. */
29921
30181
  function formatBookingScheduleSummary(event) {
29922
30182
  const summary = event.scheduleSummary;
29923
30183
  if (!summary) return null;
29924
- const time = summary.startTime ? formatBookingWhen({
29925
- startsAt: `1970-01-01T${summary.startTime}:00Z`,
29926
- timeZone: "UTC",
29927
- siteTimeZone: event.siteTimeZone
29928
- }, "time") : null;
29929
- return [summary.recurrenceDescription, time].filter(Boolean).join(" · ");
30184
+ const start = summary.startTime ? parseUtcIsoDateTimeString(`1970-01-01T${summary.startTime}:00Z`) : null;
30185
+ const time = start?.ok ? formatTime$1(start.value, {
30186
+ timeZone: resolveBookingTimeZoneId("UTC"),
30187
+ locale: bookingDateLocale(event.siteTimeZone)
30188
+ }, { zone: "omit" }) : null;
30189
+ return [summary.recurrenceDescription, time].filter(Boolean).join(", ");
30190
+ }
30191
+ /** Variant meaning is closed here; core owns the resulting typography. */
30192
+ const BOOKING_DATE_PRESENTATIONS = {
30193
+ day: {
30194
+ kind: "date",
30195
+ options: {
30196
+ date: "short",
30197
+ weekday: "short",
30198
+ year: "omit"
30199
+ }
30200
+ },
30201
+ dayLong: {
30202
+ kind: "date",
30203
+ options: {
30204
+ date: "short",
30205
+ weekday: "long",
30206
+ year: "omit"
30207
+ }
30208
+ },
30209
+ month: {
30210
+ kind: "date",
30211
+ options: {
30212
+ date: "long",
30213
+ day: "omit",
30214
+ year: "omit"
30215
+ }
30216
+ },
30217
+ monthYear: {
30218
+ kind: "date",
30219
+ options: {
30220
+ date: "long",
30221
+ day: "omit"
30222
+ }
30223
+ },
30224
+ time: { kind: "time" },
30225
+ short: { kind: "dateTime" },
30226
+ timeRange: { kind: "timeRange" },
30227
+ long: { kind: "dateTimeRange" }
30228
+ };
30229
+ function bookingSpan(start, endsAt) {
30230
+ const end = endsAt ? parseUtcIsoDateTimeString(endsAt) : null;
30231
+ return end?.ok && end.value >= start ? {
30232
+ start,
30233
+ end: end.value
30234
+ } : null;
29930
30235
  }
29931
- /** The one booking date formatter; every variant derives from it. */
30236
+ /** The one booking date formatter; every variant delegates to core. */
29932
30237
  function formatBookingWhen(input, variant) {
29933
- const instant = bookingInstant(input.startsAt, input);
29934
- const day = formatBookingDate(instant, {
30238
+ const start = parseUtcIsoDateTimeString(input.startsAt);
30239
+ if (!start.ok) return input.startsAt;
30240
+ const span = bookingSpan(start.value, input.endsAt);
30241
+ const { context, zone } = bookingContext(input);
30242
+ const options = {
30243
+ date: "short",
29935
30244
  weekday: "short",
29936
- day: "numeric",
29937
- month: "short"
29938
- });
29939
- switch (variant) {
29940
- case "day": return day;
29941
- case "dayLong": return formatBookingDate(instant, {
29942
- weekday: "long",
29943
- day: "numeric",
29944
- month: "long"
29945
- });
29946
- case "month": return formatBookingDate(instant, { month: "long" });
29947
- case "monthYear": return formatBookingDate(instant, {
29948
- month: "long",
29949
- year: "numeric"
29950
- });
29951
- case "time": return bookingClock(input.startsAt, input);
29952
- case "timeRange": return clockRange(input);
29953
- case "short": return `${day}, ${bookingClock(input.startsAt, input)}`;
29954
- case "long": return `${day} · ${clockRange(input)}`;
29955
- }
29956
- }
29957
- /** A single instant in the short form, e.g. "Mon 5 Oct, 7:00pm". */
29958
- function formatEventDateTime(isoTimestamp, options) {
29959
- return formatBookingWhen({
29960
- startsAt: isoTimestamp,
29961
- timeZone: options?.timeZone,
29962
- siteTimeZone: options?.siteTimeZone
29963
- }, "short");
30245
+ year: "omit",
30246
+ zone
30247
+ };
30248
+ const presentation = BOOKING_DATE_PRESENTATIONS[variant];
30249
+ switch (presentation.kind) {
30250
+ case "date": return formatDate({
30251
+ kind: "instant",
30252
+ value: start.value
30253
+ }, context, presentation.options);
30254
+ case "time": return formatTime$1(start.value, context, { zone });
30255
+ case "dateTime": return formatDateTime(start.value, context, options);
30256
+ case "timeRange": return span ? formatRegistrationClockRange(span, context, options) : formatTime$1(start.value, context, { zone });
30257
+ case "dateTimeRange": return span ? formatDateTimeRange(span, context, options) : formatDateTime(start.value, context, options);
30258
+ }
30259
+ }
30260
+ function formatRegistrationClockRange(span, context, options) {
30261
+ const sameDay = compareLocalScheduleDates(getLocalScheduleDate(new Date(span.start), context.timeZone), getLocalScheduleDate(new Date(span.end), context.timeZone)) === 0;
30262
+ return sameDay ? formatTimeRange(span.start, span.end, context, options) : formatDateTimeRange(span, context, options);
30263
+ }
30264
+ function formatEventDateTime(isoTimestamp, options, date = "short") {
30265
+ const instant = parseUtcIsoDateTimeString(isoTimestamp);
30266
+ const { context, zone } = bookingContext(options);
30267
+ return instant.ok ? formatDateTime(instant.value, context, {
30268
+ date,
30269
+ weekday: "short",
30270
+ year: "omit",
30271
+ zone
30272
+ }) : isoTimestamp;
29964
30273
  }
29965
30274
  /** The title an occurrence was given on its own, when it overrides the event's. */
29966
30275
  function occurrenceTitleOverride(occurrence) {
@@ -29975,31 +30284,33 @@ function withOccurrenceTitle(event, occurrence) {
29975
30284
  title
29976
30285
  } : event;
29977
30286
  }
29978
- function courseSchedule(sessions, siteTimeZone) {
30287
+ function courseSchedule(sessions, siteTimeZone, deliveryMode) {
29979
30288
  const first = sessions[0];
29980
30289
  if (!first) return null;
29981
30290
  const last = sessions[sessions.length - 1] ?? first;
29982
30291
  const firstLabel = formatEventDateTime(first.startsAt, {
29983
30292
  timeZone: first.timeZone,
29984
- siteTimeZone
30293
+ siteTimeZone,
30294
+ deliveryMode
29985
30295
  });
29986
30296
  if (last.startsAt === first.startsAt) {
29987
30297
  return `${sessions.length} session${sessions.length === 1 ? "" : "s"} · ${firstLabel}`;
29988
30298
  }
29989
30299
  const lastLabel = formatEventDateTime(last.startsAt, {
29990
30300
  timeZone: last.timeZone,
29991
- siteTimeZone
30301
+ siteTimeZone,
30302
+ deliveryMode
29992
30303
  });
29993
30304
  return `${sessions.length} sessions · ${firstLabel} – ${lastLabel}`;
29994
30305
  }
29995
30306
  /**
29996
30307
  * When a booking happens, in one line: the occurrence's date and time, or a
29997
- * course's session count and first and last dates. `short` by default (bar,
29998
- * confirmation); `long` adds the end time (card, panel header).
30308
+ * course's session count and first and last dates. The default retains the
30309
+ * stored end time; an explicitly requested `short` variant shows the start.
29999
30310
  */
30000
- function formatRegistrationSchedule(event, occurrence, variant = "short") {
30311
+ function formatRegistrationSchedule(event, occurrence, variant = "long") {
30001
30312
  if (event.registrationSubject.kind === "wholeSeries") {
30002
- return courseSchedule(event.registrationSubject.sessions, event.siteTimeZone);
30313
+ return courseSchedule(event.registrationSubject.sessions, event.siteTimeZone, event.deliveryMode);
30003
30314
  }
30004
30315
  const input = scheduleInput(event, occurrence ?? null);
30005
30316
  return input ? formatBookingWhen(input, variant) : null;
@@ -30017,7 +30328,8 @@ function scheduleInput(event, occurrence) {
30017
30328
  return {
30018
30329
  ...times,
30019
30330
  timeZone: occurrence?.timeZone ?? event.timeZone ?? null,
30020
- siteTimeZone: event.siteTimeZone
30331
+ siteTimeZone: event.siteTimeZone,
30332
+ deliveryMode: event.deliveryMode
30021
30333
  };
30022
30334
  }
30023
30335
  /** Tooltip text shown when pass/membership redemptions are unavailable for multi-ticket bookings. */
@@ -37513,8 +37825,9 @@ function buildBookingPaymentSelectionViewModel(input) {
37513
37825
  options: allowedOptions.map((option) => ({
37514
37826
  ...buildBookingPaymentOptionViewModel({
37515
37827
  option,
37828
+ dateContext: input.dateContext,
37516
37829
  currency,
37517
- locale: input.locale
37830
+ locale: input.dateContext.locale
37518
37831
  }),
37519
37832
  dueNowCents: option.dueNowCents
37520
37833
  }))
@@ -37574,7 +37887,7 @@ function buildInstalmentOption(input) {
37574
37887
  label: "Deposit policy",
37575
37888
  value: depositPolicy(refundability)
37576
37889
  }] : [], ...futureRows.map((row$1) => ({
37577
- label: formatScheduleDate(row$1.dueAt, input.locale),
37890
+ label: formatScheduleDate(row$1.dueAt, input.dateContext),
37578
37891
  value: formatPrice(row$1.amountCents, input.currency, input.locale)
37579
37892
  }))]
37580
37893
  })
@@ -37583,12 +37896,12 @@ function buildInstalmentOption(input) {
37583
37896
  function buildFlexibleBalanceOption(input) {
37584
37897
  const balance = {
37585
37898
  remainingBalance: formatPrice(input.option.remainingBalanceCents, input.currency, input.locale),
37586
- balanceDueBy: formatScheduleDate(input.option.balanceDueAt, input.locale),
37899
+ balanceDueBy: formatScheduleDate(input.option.balanceDueAt, input.dateContext),
37587
37900
  depositPolicy: depositPolicy(input.option.config.depositRefundability)
37588
37901
  };
37589
37902
  const copy = buildFlexibleBalanceBookingPaymentOptionCopy({
37590
37903
  formattedDueToday: formatPrice(input.option.dueNowCents, input.currency, input.locale),
37591
- dueDateLabel: balance.balanceDueBy,
37904
+ dueDateLabel: formatScheduleDate(input.option.balanceDueAt, input.dateContext, "long"),
37592
37905
  depositRefundability: input.option.config.depositRefundability
37593
37906
  });
37594
37907
  return {
@@ -37618,12 +37931,12 @@ function buildFlexibleBalanceOption(input) {
37618
37931
  })
37619
37932
  };
37620
37933
  }
37621
- function formatScheduleDate(isoString, locale) {
37622
- const date = new Date(isoString);
37623
- if (!Number.isFinite(date.getTime())) {
37624
- return isoString;
37625
- }
37626
- return new Intl.DateTimeFormat(locale, { dateStyle: "medium" }).format(date);
37934
+ function formatScheduleDate(isoString, context, date = "short") {
37935
+ const instant = parseUtcIsoDateTimeString(isoString);
37936
+ return instant.ok ? formatDate({
37937
+ kind: "instant",
37938
+ value: instant.value
37939
+ }, context, { date }) : isoString;
37627
37940
  }
37628
37941
  /** Total, Due today and the terms after them, from the amounts themselves. */
37629
37942
  function summaryFor(input) {
@@ -37700,8 +38013,9 @@ function buildReviewedBookingPaymentViewModel(input) {
37700
38013
  return {
37701
38014
  ...buildBookingPaymentOptionViewModel({
37702
38015
  option,
38016
+ dateContext: input.dateContext,
37703
38017
  currency: input.terms.currency,
37704
- locale: input.locale
38018
+ locale: input.dateContext.locale
37705
38019
  }),
37706
38020
  dueNowCents: option.dueNowCents
37707
38021
  };
@@ -38336,6 +38650,7 @@ function getApprovedDirectPaymentCollectionOptionKinds(paymentOptions) {
38336
38650
  function buildEventRegistrationPaymentOptionsViewModel(input) {
38337
38651
  const approvedOptions = selectApprovedEventDirectPaymentOptions(input.approvedPaymentOptions, input.paymentTerms);
38338
38652
  const selection = buildBookingPaymentSelectionViewModel({
38653
+ dateContext: input.dateContext,
38339
38654
  serviceTitle: input.eventTitle,
38340
38655
  availableOptions: repriceBookingCollectionOptions({
38341
38656
  options: resolveAvailableBookingCollectionOptions({
@@ -38346,8 +38661,7 @@ function buildEventRegistrationPaymentOptionsViewModel(input) {
38346
38661
  }),
38347
38662
  totalCents: input.totalPriceCents
38348
38663
  }),
38349
- priceCurrency: input.currency,
38350
- locale: input.locale
38664
+ priceCurrency: input.currency
38351
38665
  });
38352
38666
  return {
38353
38667
  mode: selection.mode,
@@ -39085,7 +39399,7 @@ function seriesPriceLabel(events) {
39085
39399
  }
39086
39400
  function dateTile(event) {
39087
39401
  if (event.registrationSubject.kind === "wholeSeries" || !event.startsAt) return null;
39088
- const rail = formatEventCompactDateRail(event.startsAt, { timeZone: event.timeZone });
39402
+ const rail = formatEventCompactDateRail(event.startsAt, eventDateOptions(event));
39089
39403
  return {
39090
39404
  month: rail.month,
39091
39405
  day: rail.day
@@ -39323,7 +39637,8 @@ function offeringsListingFor(arrangement) {
39323
39637
  case "lead-support":
39324
39638
  case "index":
39325
39639
  case "sequence":
39326
- case "plates": return "by-type";
39640
+ case "plates":
39641
+ case "stack": return "by-type";
39327
39642
  default: return assertNever(arrangement);
39328
39643
  }
39329
39644
  }
@@ -39674,6 +39989,13 @@ function normalizeTextOverride(value) {
39674
39989
  const trimmed = value.trim();
39675
39990
  return trimmed.length > 0 ? trimmed : null;
39676
39991
  }
39992
+ function resolveOfferingsRenderInput(input) {
39993
+ if (input.renderInput) return input.renderInput;
39994
+ return {
39995
+ kind: "unprepared",
39996
+ now: new Date(input.now)
39997
+ };
39998
+ }
39677
39999
 
39678
40000
  //#endregion
39679
40001
  //#region ../blocks/src/system/runtime/nodes/event-registration/offeringNoun.ts
@@ -40082,6 +40404,7 @@ function eventCalendarListGroupingDecision(input) {
40082
40404
 
40083
40405
  //#endregion
40084
40406
  //#region ../blocks/src/system/runtime/nodes/shared/contract-schemas.ts
40407
+ const bookingNowSchema = z.string().datetime({ offset: true }).transform(readRuntimeNow);
40085
40408
  const previewStageSchema = z.enum(["preview", "published"]);
40086
40409
  const registrationButtonVariantSchema = blockButtonRoleSchema;
40087
40410
  const eventButtonVariantSchema = blockButtonRoleSchema;
@@ -40164,6 +40487,7 @@ function displayButtonText(display) {
40164
40487
  }
40165
40488
  const eventRegistrationPublicEventSchema = z.preprocess(normalizeEventRegistrationEventInput, publicEventSchema);
40166
40489
  const eventRegistrationCommonSchemaShape = {
40490
+ now: bookingNowSchema,
40167
40491
  demoEmailSuppressed: z.boolean().optional(),
40168
40492
  occurrenceContext: occurrenceContextSchema.optional(),
40169
40493
  subject: eventRegistrationPageSurfaceSubjectSchema.nullable().optional(),
@@ -40358,6 +40682,7 @@ const eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
40358
40682
  return {
40359
40683
  display: buildEventRegistrationDisplayFromScopedEvents(source, scopedSourceEvents, display),
40360
40684
  hydration: {
40685
+ now: source.now,
40361
40686
  occurrenceContext: source.occurrenceContext ?? null,
40362
40687
  subject: source.subject ?? null,
40363
40688
  eventSource: eventSource.kind === "loaded" ? {
@@ -41073,7 +41398,7 @@ const eventDetailsManifest = createBlockManifest({
41073
41398
  layout: [styledSection({
41074
41399
  baseClass: "",
41075
41400
  spacing: "spacious",
41076
- children: sectionContainer([text({ as: "h2" }, bind("content.heading"), when("content.heading")), el("event-details", {
41401
+ children: sectionContainer([text({ as: "h2" }, bind("content.heading"), when("content.heading")), el("event-details", withApiBindings({
41077
41402
  entryTitle: { $bind: {
41078
41403
  from: "$root.contentEntry.title",
41079
41404
  fallback: null
@@ -41123,7 +41448,7 @@ const eventDetailsManifest = createBlockManifest({
41123
41448
  from: "data.eventDetailsData.autoApplyDiscount",
41124
41449
  fallback: null
41125
41450
  } }
41126
- })], { gap: "lg" })
41451
+ }, { includeApiBaseUrl: false }))], { gap: "lg" })
41127
41452
  })]
41128
41453
  });
41129
41454
  const eventDetailsBlockDefinition = {
@@ -41405,6 +41730,63 @@ function getCardOrientation(layout) {
41405
41730
  }
41406
41731
  }
41407
41732
 
41733
+ //#endregion
41734
+ //#region ../blocks/src/primitives/content/utils/sanitizeHref.ts
41735
+ const SAFE_PROTOCOLS = new Set([
41736
+ "http:",
41737
+ "https:",
41738
+ "mailto:",
41739
+ "tel:"
41740
+ ]);
41741
+ const CONTROL_CHARS_PATTERN = /[\u0000-\u001F\u007F]+/g;
41742
+ function normalizeInput(value) {
41743
+ return value.replace(CONTROL_CHARS_PATTERN, "").trim();
41744
+ }
41745
+ function decodeForSchemeCheck(value) {
41746
+ try {
41747
+ return decodeURIComponent(value);
41748
+ } catch {
41749
+ return value;
41750
+ }
41751
+ }
41752
+ /**
41753
+ * Returns a safe href for rich-text anchors, or null when the value should not be rendered.
41754
+ *
41755
+ * Policy:
41756
+ * - Allow absolute URLs with protocols: http, https, mailto, tel.
41757
+ * - Allow root-relative ("/path") and hash-only ("#section") links.
41758
+ * - Reject protocol-relative URLs ("//example.com"), bare domains ("example.com"),
41759
+ * backslashes, executable schemes (javascript/data/vbscript), malformed URLs, and empty values.
41760
+ * - Preserve percent-encoding from the original normalized input.
41761
+ */
41762
+ function sanitizeHref(raw) {
41763
+ if (typeof raw !== "string") return null;
41764
+ const normalized = normalizeInput(raw);
41765
+ if (!normalized) return null;
41766
+ if (normalized.includes("\\") || normalized.startsWith("//")) return null;
41767
+ if (normalized.startsWith("/")) return normalized;
41768
+ if (normalized.startsWith("#")) return normalized;
41769
+ return sanitizeAbsoluteHref(normalized);
41770
+ }
41771
+ function sanitizeAbsoluteHref(normalized) {
41772
+ const decoded = normalizeInput(decodeForSchemeCheck(normalized));
41773
+ const schemeMatch = decoded.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);
41774
+ if (!schemeMatch) return null;
41775
+ const rawScheme = schemeMatch[1];
41776
+ if (!rawScheme) return null;
41777
+ const protocol = `${rawScheme.toLowerCase()}:`;
41778
+ if (!SAFE_PROTOCOLS.has(protocol)) return null;
41779
+ try {
41780
+ const parsed = new URL(normalized);
41781
+ if (!SAFE_PROTOCOLS.has(parsed.protocol.toLowerCase())) {
41782
+ return null;
41783
+ }
41784
+ } catch {
41785
+ return null;
41786
+ }
41787
+ return normalized;
41788
+ }
41789
+
41408
41790
  //#endregion
41409
41791
  //#region ../blocks/src/system/runtime/nodes/mediaContext.ts
41410
41792
  function buildMediaAssetIndex(assets) {
@@ -42802,6 +43184,171 @@ function mediaImageHostProps(props$1, style) {
42802
43184
  };
42803
43185
  }
42804
43186
 
43187
+ //#endregion
43188
+ //#region ../blocks/src/primitives/content/components/RichText.tsx
43189
+ function parseDimension(value) {
43190
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
43191
+ return Math.round(value);
43192
+ }
43193
+ if (typeof value === "string" && value.trim().length > 0) {
43194
+ const parsed = Number(value);
43195
+ if (Number.isFinite(parsed) && parsed > 0) {
43196
+ return Math.round(parsed);
43197
+ }
43198
+ }
43199
+ return undefined;
43200
+ }
43201
+ function normalizeRel(target, rel) {
43202
+ const normalizedRel = rel?.trim();
43203
+ if (target !== "_blank") {
43204
+ return normalizedRel && normalizedRel.length > 0 ? normalizedRel : undefined;
43205
+ }
43206
+ const tokens = new Set((normalizedRel ?? "").split(/\s+/).map((token) => token.trim().toLowerCase()).filter(Boolean));
43207
+ tokens.add("noopener");
43208
+ tokens.add("noreferrer");
43209
+ return Array.from(tokens).join(" ");
43210
+ }
43211
+ function renderMarks(text$1, marks, key, context) {
43212
+ if (!marks || marks.length === 0) return text$1;
43213
+ return marks.reduce((acc, mark) => {
43214
+ switch (mark.type) {
43215
+ case "bold":
43216
+ case "strong": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: acc }, `${key}-b`);
43217
+ case "italic":
43218
+ case "em": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("em", { children: acc }, `${key}-i`);
43219
+ case "link": return renderLinkMark(mark, acc, key, context);
43220
+ default: return acc;
43221
+ }
43222
+ }, text$1);
43223
+ }
43224
+ function richTextLinkAttributes(mark, mode) {
43225
+ const attrs = mark.attrs ?? {};
43226
+ const href = sanitizeHref(attrs.href);
43227
+ if (!href) return null;
43228
+ const storedTarget = typeof attrs.target === "string" && attrs.target.trim().length > 0 ? attrs.target.trim() : undefined;
43229
+ const target = mode === "new-tab" ? "_blank" : storedTarget;
43230
+ const rel = typeof attrs.rel === "string" ? attrs.rel : undefined;
43231
+ return {
43232
+ href,
43233
+ target,
43234
+ rel: normalizeRel(target, rel)
43235
+ };
43236
+ }
43237
+ function renderLinkMark(mark, text$1, key, context) {
43238
+ if (typeof text$1 === "string" && text$1.length === 0) return text$1;
43239
+ const attributes = richTextLinkAttributes(mark, context.linkTarget);
43240
+ return attributes ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
43241
+ ...attributes,
43242
+ ...listLookControl(context, "link"),
43243
+ children: text$1
43244
+ }, `${key}-l`) : text$1;
43245
+ }
43246
+ function renderDoc(node, key, context) {
43247
+ if (!node || !node.type) return null;
43248
+ const children = node.content?.map((child, index) => renderDoc(child, `${key}-${index}`, context));
43249
+ switch (node.type) {
43250
+ case "doc": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
43251
+ case "paragraph": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: children ?? renderMarks(node.text ?? "", node.marks, key, context) }, key);
43252
+ case "heading": return renderHeading(node, key, children, context);
43253
+ case "blockquote": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("blockquote", {
43254
+ ...listLookSlot(context, "quote"),
43255
+ children
43256
+ }, key);
43257
+ case "image": return renderRichTextImage(node, key, context);
43258
+ default: return renderSimpleNode(node, key, children, context);
43259
+ }
43260
+ }
43261
+ function renderHeading(node, key, children, context) {
43262
+ const level = Math.min(Math.max(Number(node.attrs?.level ?? 2), 1), 6);
43263
+ return import_react.createElement(`h${level}`, {
43264
+ key,
43265
+ ...level === 2 ? listLookSlot(context, "heading") : {}
43266
+ }, children);
43267
+ }
43268
+ function renderSimpleNode(node, key, children, context) {
43269
+ switch (node.type) {
43270
+ case "bulletList": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children }, key);
43271
+ case "orderedList": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { children }, key);
43272
+ case "listItem": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children }, key);
43273
+ case "horizontalRule": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", {}, key);
43274
+ case "hardBreak": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}, key);
43275
+ case "text": return renderMarks(node.text ?? "", node.marks, key, context);
43276
+ default: return null;
43277
+ }
43278
+ }
43279
+ function richTextImageOptions(width, height) {
43280
+ if (width) return {
43281
+ width,
43282
+ resize: "contain",
43283
+ quality: 85
43284
+ };
43285
+ if (height) return {
43286
+ height,
43287
+ resize: "contain",
43288
+ quality: 85
43289
+ };
43290
+ return ImagePresets.full;
43291
+ }
43292
+ function resolveRichTextImageSource(attrs, width, height, context) {
43293
+ if (attrs.identifier !== undefined && attrs.identifier !== null) {
43294
+ const parsed = parseCanonicalMediaIdentifier(attrs.identifier);
43295
+ if (!parsed.ok) return undefined;
43296
+ if (context?.mediaAssetIndex) {
43297
+ return resolveImageUrlWithContext({
43298
+ type: "image",
43299
+ identifier: parsed.value
43300
+ }, richTextImageOptions(width, height), context);
43301
+ }
43302
+ return typeof attrs.src === "string" ? attrs.src.trim() || undefined : undefined;
43303
+ }
43304
+ return typeof attrs.src === "string" ? attrs.src.trim() || undefined : undefined;
43305
+ }
43306
+ function renderRichTextImage(node, key, context) {
43307
+ const attrs = node.attrs ?? {};
43308
+ const width = parseDimension(attrs.width);
43309
+ const height = parseDimension(attrs.height);
43310
+ const src = resolveRichTextImageSource(attrs, width, height, context.imageResolveContext);
43311
+ if (!src) return null;
43312
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
43313
+ src,
43314
+ alt: typeof attrs.alt === "string" ? attrs.alt : "",
43315
+ title: typeof attrs.title === "string" && attrs.title.trim() ? attrs.title : undefined,
43316
+ width,
43317
+ height,
43318
+ loading: "lazy",
43319
+ decoding: "async",
43320
+ ...listLookSlot(context, "photo")
43321
+ }, key);
43322
+ }
43323
+ /** Mark meaning in the renderer, rather than choosing ink with a positional CSS rule. */
43324
+ function renderRichTextContent(doc, context, leadSlot) {
43325
+ if (!leadSlot) return renderDoc(doc, "doc", context);
43326
+ const leadIndex = doc.content?.findIndex((node) => node.type === "paragraph");
43327
+ return doc.content?.map((node, index) => {
43328
+ const rendered = renderDoc(node, `doc-${index}`, context);
43329
+ return index === leadIndex && import_react.isValidElement(rendered) ? import_react.cloneElement(rendered, { "data-slot": leadSlot }) : rendered;
43330
+ });
43331
+ }
43332
+ const DEFAULT_RICH_TEXT_CLASS = "rb-prose rb-max-w-none";
43333
+ function RichText({ as: Comp = "div", className, doc, path: path$3, style, "data-slot": dataSlot, "data-surface": dataSurface, linkTarget = "stored", leadParagraphSlot, lookSlots }) {
43334
+ const imageResolveContext = getCurrentResolveImageUrlContext() ?? null;
43335
+ const content = doc ? renderRichTextContent(doc, {
43336
+ imageResolveContext,
43337
+ linkTarget,
43338
+ lookSlots
43339
+ }, leadParagraphSlot) : null;
43340
+ const dataAttributes = path$3 ? { "data-builder-richtext": path$3 } : undefined;
43341
+ const resolvedClassName = className?.trim() || DEFAULT_RICH_TEXT_CLASS;
43342
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Comp, {
43343
+ "data-slot": dataSlot,
43344
+ "data-surface": dataSurface,
43345
+ style: dataSlot === undefined ? undefined : style,
43346
+ className: resolvedClassName,
43347
+ ...dataAttributes,
43348
+ children: content
43349
+ });
43350
+ }
43351
+
42805
43352
  //#endregion
42806
43353
  //#region ../blocks/src/system/runtime/nodes/media.server.tsx
42807
43354
  /**
@@ -42852,6 +43399,35 @@ const MediaNodeServer = ({ value, "data-slot": dataSlot, "data-surface": dataSur
42852
43399
 
42853
43400
  //#endregion
42854
43401
  //#region ../blocks/src/system/runtime/nodes/events/shared/CardListItem.tsx
43402
+ const cardListArrangements = [
43403
+ "cards",
43404
+ "stack",
43405
+ "lead-support",
43406
+ "index",
43407
+ "sequence",
43408
+ "plates",
43409
+ "slides",
43410
+ "track"
43411
+ ];
43412
+ const cardListPresentationSchemaShape = {
43413
+ ...lookListPresentationSchemaShape,
43414
+ cardArrangement: z.enum(cardListArrangements).optional(),
43415
+ showPhotos: z.boolean().optional(),
43416
+ datesMode: z.enum(["summary", "next-dates"]).optional(),
43417
+ cardMarker: z.string().optional()
43418
+ };
43419
+ const cardListPresentationKeys = z.object(cardListPresentationSchemaShape).keyof().options;
43420
+ /** Structural typing does not strip loader data or React children from block props. */
43421
+ function selectCardListPresentation(source) {
43422
+ return [
43423
+ ...cardListPresentationKeys,
43424
+ "buttonVariant",
43425
+ "bodyParts"
43426
+ ].reduce((presentation, key) => ({
43427
+ ...presentation,
43428
+ [key]: source[key]
43429
+ }), {});
43430
+ }
42855
43431
  /** Actual date rows own availability; a missing batch still uses the summary. */
42856
43432
  function resolveCardListSchedule(item, mode) {
42857
43433
  return mode === "next-dates" && item.dates?.length ? {
@@ -42879,7 +43455,11 @@ const partRenderers = {
42879
43455
  })
42880
43456
  }) : item.title
42881
43457
  }),
42882
- text: ({ item, presentation }) => item.text ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
43458
+ text: ({ item, presentation }) => item.textDocument ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RichText, {
43459
+ doc: item.textDocument,
43460
+ ...listLookSlot(presentation, "item-text"),
43461
+ lookSlots: presentation.lookSlots
43462
+ }) : item.text ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
42883
43463
  ...listLookSlot(presentation, "item-text"),
42884
43464
  children: item.text
42885
43465
  }) : null,
@@ -42947,7 +43527,27 @@ const partRenderers = {
42947
43527
  children: item.availability.label
42948
43528
  })
42949
43529
  }) : null,
42950
- action: (props$1) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListAction, { ...props$1 })
43530
+ price: ({ item, presentation }) => item.price ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
43531
+ ...listLookSlot(presentation, "price"),
43532
+ children: [
43533
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", {
43534
+ ...listLookSlot(presentation, "price-amount"),
43535
+ children: item.price.amount
43536
+ }),
43537
+ item.price.unit ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
43538
+ ...listLookSlot(presentation, "price-unit"),
43539
+ children: item.price.unit
43540
+ }) : null,
43541
+ item.price.note ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
43542
+ ...listLookSlot(presentation, "price-note"),
43543
+ children: item.price.note
43544
+ }) : null
43545
+ ]
43546
+ }) : null,
43547
+ action: (props$1) => props$1.actionNode === undefined ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListAction, { ...props$1 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
43548
+ ...listLookSlot(props$1.presentation, "item-actions"),
43549
+ children: props$1.actionNode
43550
+ })
42951
43551
  };
42952
43552
  function CardListContentPart({ part, ...props$1 }) {
42953
43553
  return partRenderers[part](props$1);
@@ -42977,6 +43577,7 @@ function CardListLeadDate({ item, presentation }) {
42977
43577
  }
42978
43578
  function CardListAction({ item, presentation }) {
42979
43579
  const action = item.action;
43580
+ if (!action) return null;
42980
43581
  const link$2 = cardListActionLink(action);
42981
43582
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
42982
43583
  ...listLookSlot(presentation, "item-actions"),
@@ -43074,9 +43675,9 @@ function eventItemFacts(props$1) {
43074
43675
  const availability = getEventAvailabilityStatus(props$1.event);
43075
43676
  return {
43076
43677
  date: formatEventCompactDateRail(props$1.event.startsAt, options, "as-written"),
43077
- time: formatEventTimeRange$1(props$1.event, options),
43678
+ time: formatEventTimeRange(props$1.event, options),
43078
43679
  price: formatEventPrice(props$1.event),
43079
- meta: (props$1.parts ?? eventItemParts[props$1.arrangement]).includes("time") ? formatEventTeacherLine(props$1.event) : [formatEventTimeRange$1(props$1.event, options), formatEventTeacherLine(props$1.event)].filter(Boolean).join(" · "),
43680
+ meta: (props$1.parts ?? eventItemParts[props$1.arrangement]).includes("time") ? formatEventTeacherLine(props$1.event) : [formatEventTimeRange(props$1.event, options), formatEventTeacherLine(props$1.event)].filter(Boolean).join(" · "),
43080
43681
  places: props$1.showCapacity ? formatEventRemainingPlaces(props$1.event, { full: "Sold out" }) : null,
43081
43682
  availability: availability.isFull ? "full" : availability.status === "filling" ? "low" : "available",
43082
43683
  description: props$1.event.presentation?.summary ?? props$1.event.description
@@ -43240,6 +43841,7 @@ const carouselBodyParts = [
43240
43841
  "chips",
43241
43842
  "numeral",
43242
43843
  "title",
43844
+ "price",
43243
43845
  "schedule",
43244
43846
  "dates",
43245
43847
  "text",
@@ -43251,6 +43853,7 @@ const bodyParts = [
43251
43853
  "chips",
43252
43854
  "numeral",
43253
43855
  "title",
43856
+ "price",
43254
43857
  "text",
43255
43858
  "facts",
43256
43859
  "schedule",
@@ -43258,37 +43861,66 @@ const bodyParts = [
43258
43861
  "availability",
43259
43862
  "action"
43260
43863
  ];
43261
- function renderCardItem({ item, presentation, index, Title }) {
43262
- const arrangement = presentation.cardArrangement ?? "cards";
43263
- const row$1 = arrangement === "index" || arrangement === "sequence";
43264
- const Tag = row$1 ? "li" : "article";
43265
- const slot = arrangement === "lead-support" && index === 0 ? "lead" : "item";
43266
- const schedule = resolveCardListSchedule(item, presentation.datesMode);
43267
- const orderedParts = arrangement === "slides" || arrangement === "track" ? carouselBodyParts : bodyParts;
43268
- const selectedParts = orderedParts.filter((part) => part !== "schedule" && part !== "dates" || part === schedule.part);
43269
- const parts = selectedParts.map((part) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListContentPart, {
43864
+ function renderCardItem({ item, presentation, index, Title, actionNode, feedback }) {
43865
+ const layout = cardItemLayout(presentation, index);
43866
+ const body = cardItemBody(item, presentation);
43867
+ const Tag = layout.tag;
43868
+ const parts = body.parts.map((part) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: CardListContentPart({
43270
43869
  part,
43271
- item: schedule.item,
43870
+ item: body.item,
43272
43871
  presentation,
43273
- Title
43274
- }, part));
43872
+ Title,
43873
+ actionNode
43874
+ }) }, part));
43275
43875
  const marker = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
43276
43876
  ...listLookSlot(presentation, "numeral"),
43277
43877
  "aria-hidden": "true"
43278
43878
  });
43279
43879
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, {
43280
- ...listLookSlot(presentation, slot),
43880
+ ...listLookSlot(presentation, layout.slot),
43281
43881
  "data-card-list-item": true,
43282
- children: row$1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [marker, parts] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardItemMedia, {
43882
+ "data-has-featured": item.featured ? true : undefined,
43883
+ children: layout.kind === "row" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
43884
+ marker,
43885
+ parts,
43886
+ feedback
43887
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [renderCardItemMedia({
43283
43888
  item,
43284
43889
  presentation
43285
43890
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
43286
43891
  ...listLookSlot(presentation, "item-body"),
43287
- children: [marker, parts]
43892
+ children: [
43893
+ marker,
43894
+ parts,
43895
+ feedback
43896
+ ]
43288
43897
  })] })
43289
43898
  });
43290
43899
  }
43291
- function CardItemMedia({ item, presentation }) {
43900
+ function cardItemLayout(presentation, index) {
43901
+ const arrangement = presentation.cardArrangement ?? "cards";
43902
+ if (arrangement === "index" || arrangement === "sequence") return {
43903
+ kind: "row",
43904
+ tag: "li",
43905
+ slot: "item"
43906
+ };
43907
+ return {
43908
+ kind: "card",
43909
+ tag: "article",
43910
+ slot: arrangement === "lead-support" && index === 0 ? "lead" : "item"
43911
+ };
43912
+ }
43913
+ function cardItemBody(item, presentation) {
43914
+ const schedule = resolveCardListSchedule(item, presentation.datesMode);
43915
+ const arrangement = presentation.cardArrangement ?? "cards";
43916
+ const orderedParts = presentation.bodyParts ?? (arrangement === "slides" || arrangement === "track" ? carouselBodyParts : bodyParts);
43917
+ const parts = orderedParts.filter((part) => part !== "schedule" && part !== "dates" || part === schedule.part);
43918
+ return {
43919
+ item: schedule.item,
43920
+ parts
43921
+ };
43922
+ }
43923
+ function renderCardItemMedia({ item, presentation }) {
43292
43924
  const carousel$1 = presentation.cardArrangement === "slides" || presentation.cardArrangement === "track";
43293
43925
  if (presentation.showPhotos === false) return null;
43294
43926
  if (!item.media) return carousel$1 && item.leadDate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -43311,14 +43943,26 @@ function CardItemMedia({ item, presentation }) {
43311
43943
  })
43312
43944
  }) : null] });
43313
43945
  }
43946
+ /** Pure and hook-free: select presentation before creating per-card RSC elements. */
43314
43947
  function CardListItems({ Carousel, items, presentation, carouselLabels, Title = "h3" }) {
43315
- const slides = cardListSlides(items, presentation, Title);
43316
- if (presentation.cardArrangement === "slides" || presentation.cardArrangement === "track") {
43948
+ const selectedPresentation = selectCardListPresentation(presentation);
43949
+ const slides = cardListSlides(items, selectedPresentation, Title);
43950
+ const photoCount = presentation.showPhotos === false ? 0 : items.filter((item) => item.media).length;
43951
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListCollection, {
43952
+ Carousel,
43953
+ presentation: selectedPresentation,
43954
+ slides,
43955
+ photoCount,
43956
+ labels: carouselLabels
43957
+ });
43958
+ }
43959
+ function CardListCollection({ Carousel, presentation, slides, photoCount = 0, labels }) {
43960
+ if (Carousel && (presentation.cardArrangement === "slides" || presentation.cardArrangement === "track")) {
43317
43961
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Carousel, {
43318
43962
  mode: "arrangement",
43319
43963
  arrangement: presentation.cardArrangement,
43320
43964
  cardList: true,
43321
- labels: carouselLabels ?? {
43965
+ labels: labels ?? {
43322
43966
  item: "card",
43323
43967
  region: "Cards"
43324
43968
  },
@@ -43327,7 +43971,6 @@ function CardListItems({ Carousel, items, presentation, carouselLabels, Title =
43327
43971
  });
43328
43972
  }
43329
43973
  const Tag = presentation.cardArrangement === "index" || presentation.cardArrangement === "sequence" ? "ol" : "div";
43330
- const photoCount = presentation.showPhotos === false ? 0 : items.filter((item) => item.media).length;
43331
43974
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, {
43332
43975
  ...listLookSlot(presentation, "items"),
43333
43976
  "data-card-list": true,
@@ -43642,8 +44285,8 @@ function resolveEventCardOptions(props$1) {
43642
44285
  function formatEventCardWhen(event) {
43643
44286
  const options = eventDateOptions(event);
43644
44287
  return {
43645
- formattedDate: formatDate(event.startsAt, options),
43646
- formattedTime: formatEventTimeRange$1(event, options)
44288
+ formattedDate: formatDate$1(event.startsAt, options),
44289
+ formattedTime: formatEventTimeRange(event, options)
43647
44290
  };
43648
44291
  }
43649
44292
  function resolveEventCardDateFact(props$1) {
@@ -43771,8 +44414,8 @@ const EventCompactRow = (props$1) => props$1.lookSlots === undefined ? /* @__PUR
43771
44414
  });
43772
44415
  const LegacyEventCompactRow = ({ event, buttonVariant = "primary", buttonText }) => {
43773
44416
  const timeZone = event.timeZone ?? null;
43774
- const dateRail = formatEventCompactDateRail(event.startsAt, { timeZone });
43775
- const formattedTime = `${formatTime(event.startsAt, { timeZone })} – ${formatTime(event.endsAt, { timeZone })}`;
44417
+ const dateRail = formatEventCompactDateRail(event.startsAt, eventDateOptions(event));
44418
+ const formattedTime = formatEventTimeRange(event, eventDateOptions(event));
43776
44419
  const price$1 = formatEventPrice(event);
43777
44420
  const teacherLine = formatEventTeacherLine(event);
43778
44421
  const cta = resolveEventCta(event, buttonText);
@@ -43977,13 +44620,8 @@ var EmptyState_default = EmptyState;
43977
44620
  * Shared tile content for displaying date and time range.
43978
44621
  * Used by event-details "Other dates".
43979
44622
  */
43980
- function DateTimeTile({ startsAt, endsAt, timeZone, locale }) {
43981
- const { date, timeRange } = formatEventDateParts({
43982
- startsAt,
43983
- endsAt,
43984
- timeZone,
43985
- locale
43986
- });
44623
+ function DateTimeTile(props$1) {
44624
+ const { date, timeRange } = formatEventDateParts(props$1);
43987
44625
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
43988
44626
  className: "rb-flex rb-flex-col rb-gap-1",
43989
44627
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", {
@@ -44377,6 +45015,7 @@ const eventCollectionSourceSchemaShape = {
44377
45015
  buttonText: z.string().optional(),
44378
45016
  emptyMessage: z.string().optional(),
44379
45017
  className: z.string().optional(),
45018
+ now: z.string().transform(toUtcIsoDateTimeString),
44380
45019
  bookingTimeZone: z.string().optional()
44381
45020
  };
44382
45021
  const eventCollectionDisplaySchemaShape = {
@@ -44416,7 +45055,7 @@ const eventCollectionHydrationSchema = z.object({
44416
45055
  filterEventPresets: z.string().nullable(),
44417
45056
  scheduleScope: eventScheduleScopeSchema$1,
44418
45057
  calendarContext: z.object({
44419
- timeZone: z.string().transform(toBookingTimeZone),
45058
+ timeZone: z.string().transform(resolveBookingBusinessTimeZone),
44420
45059
  initialToday: z.object({
44421
45060
  year: z.number().int(),
44422
45061
  month: z.number().int().min(1).max(12),
@@ -44444,7 +45083,7 @@ function buildEventCollectionDisplay(source) {
44444
45083
  seeAllUrl: source.seeAllUrl,
44445
45084
  loadMoreText: source.loadMoreText
44446
45085
  }),
44447
- startOfWeek: source.startOfWeek ?? "monday",
45086
+ startOfWeek: eventCalendarWeekStart(resolveBookingBusinessTimeZone(source.bookingTimeZone ?? source.calendarWindow?.scope.timeZone), source.startOfWeek),
44448
45087
  calendarTabViews: source.calendarTabViews ?? "both",
44449
45088
  defaultCalendarView: source.defaultCalendarView ?? "month"
44450
45089
  };
@@ -44486,8 +45125,8 @@ function buildEventCollectionPresentationDisplay(source) {
44486
45125
  emptyMessage: source.emptyMessage ?? "No upcoming events scheduled."
44487
45126
  };
44488
45127
  }
44489
- function buildEventCollectionHydration(source, now = new Date()) {
44490
- const timeZone = source.calendarWindow?.scope.timeZone ?? toBookingTimeZone(source.bookingTimeZone);
45128
+ function buildEventCollectionHydration(source) {
45129
+ const timeZone = source.calendarWindow?.scope.timeZone ?? resolveBookingBusinessTimeZone(source.bookingTimeZone);
44491
45130
  const selected = source.seriesIds != null;
44492
45131
  return {
44493
45132
  calendarWindow: source.calendarWindow ?? undefined,
@@ -44498,7 +45137,7 @@ function buildEventCollectionHydration(source, now = new Date()) {
44498
45137
  scheduleScope: selected ? "any" : normalizeEventScheduleScope(source.scheduleScope),
44499
45138
  calendarContext: {
44500
45139
  timeZone,
44501
- initialToday: source.calendarWindow?.initialToday ?? getLocalScheduleDate(now, timeZone)
45140
+ initialToday: source.calendarWindow?.initialToday ?? getLocalScheduleDate(new Date(source.now), timeZone)
44502
45141
  }
44503
45142
  };
44504
45143
  }
@@ -44532,7 +45171,7 @@ function buildEventCollectionListingDisplay(display, prefetchedEvents, className
44532
45171
  /** Existing pagination fields also supply the optional arrangement section action. */
44533
45172
  function eventSectionAction(source) {
44534
45173
  const href = source.seeAllUrl?.trim();
44535
- return source.lookSlots && href ? {
45174
+ return (source.lookSlots || source.paginationMode === "seeAllLink") && href ? {
44536
45175
  href,
44537
45176
  label: source.loadMoreText?.trim() || "See the full timetable"
44538
45177
  } : undefined;
@@ -45493,10 +46132,7 @@ const eventCalendarLayout = [styledSection({
45493
46132
  displayMode: { $bind: { from: "content.displayMode" } },
45494
46133
  listLayout: { $bind: { from: "content.listLayout" } },
45495
46134
  listBy: { $bind: { from: "content.listBy" } },
45496
- bookingTimeZone: { $bind: {
45497
- from: "$root.bookingTimeZone",
45498
- transforms: [{ id: "events.calendarLookTimeZone" }]
45499
- } },
46135
+ bookingTimeZone: { $bind: { from: "$root.dateContext.timeZone" } },
45500
46136
  monthArrangement: { $bind: { from: "content._lookMonthArrangement" } },
45501
46137
  lookSlots: { $bind: { from: "content._lookSlots" } },
45502
46138
  itemActionStyle: { $bind: { from: "content._effectiveKnobs.actionStyle.value" } },
@@ -45521,10 +46157,7 @@ const eventCalendarLayout = [styledSection({
45521
46157
  from: "content.loadMoreText",
45522
46158
  fallback: "Load more events"
45523
46159
  } },
45524
- startOfWeek: { $bind: {
45525
- from: "content.startOfWeek",
45526
- fallback: "monday"
45527
- } },
46160
+ startOfWeek: { $bind: { from: "content.startOfWeek" } },
45528
46161
  calendarTabViews: { $bind: {
45529
46162
  from: "content.calendarTabViews",
45530
46163
  fallback: "both"
@@ -45853,7 +46486,6 @@ const eventCalendarAdditionalFields = [
45853
46486
  description: "First day of the week",
45854
46487
  required: false,
45855
46488
  multiple: false,
45856
- defaultValue: "monday",
45857
46489
  options: [{
45858
46490
  value: "sunday",
45859
46491
  label: "Sunday"
@@ -47250,6 +47882,7 @@ const displayModeBinding = {
47250
47882
  };
47251
47883
  const catalogueMode = { $bind: {
47252
47884
  ...displayModeBinding,
47885
+ fallback: false,
47253
47886
  transforms: [{
47254
47887
  id: "value.equals",
47255
47888
  options: { value: "catalogue" }
@@ -47257,6 +47890,7 @@ const catalogueMode = { $bind: {
47257
47890
  } };
47258
47891
  const previewMode$1 = { $bind: {
47259
47892
  ...displayModeBinding,
47893
+ fallback: true,
47260
47894
  transforms: [{
47261
47895
  id: "value.equals",
47262
47896
  options: { value: "preview" }
@@ -47286,7 +47920,7 @@ function offeringsPreviewHeaderNodes() {
47286
47920
  }, bind("content.description"), when("content.description"))];
47287
47921
  }
47288
47922
  const offeringsPreviewLayout = [styledSection({
47289
- children: sectionContainer([el("offerings-preview", {
47923
+ children: sectionContainer([el("offerings-preview", withApiBindings({
47290
47924
  ...cardListPresentationBindings,
47291
47925
  carouselRegion: { $bind: { from: "content.heading" } },
47292
47926
  renderInput: { $bind: { from: "content._offeringsPreviewInput" } },
@@ -47327,7 +47961,7 @@ const offeringsPreviewLayout = [styledSection({
47327
47961
  routeCandidates: { $bind: { from: "data.routeCandidates" } },
47328
47962
  wholeSeriesRunEvents: { $bind: { from: "data.wholeSeriesRunEvents" } },
47329
47963
  ...offeringsPreviewEventDataBindings
47330
- }, offeringsPreviewHeaderNodes()), cardListSectionAction()], { gap: "lg" }),
47964
+ }, { includeApiBaseUrl: false }), offeringsPreviewHeaderNodes()), cardListSectionAction()], { gap: "lg" }),
47331
47965
  baseClass: "",
47332
47966
  spacing: "comfortable"
47333
47967
  })];
@@ -49535,32 +50169,40 @@ function buildProductDetailDisplay(product, options, selectedVariantId) {
49535
50169
  body: product.body
49536
50170
  };
49537
50171
  }
49538
- function buildPassesMembershipsDisplay(options) {
49539
- const passes = options.passes.map((pass) => {
49540
- const creditUnit = getCreditProductCreditUnit(pass.productUseCase);
49541
- return {
49542
- id: pass.id,
49543
- title: pass.name,
49544
- description: options.showDescriptions ? pass.description : null,
49545
- priceLabel: formatCommercePrice(pass.price, pass.currency),
49546
- creditsLabel: `${pass.creditsTotal} ${pass.creditsTotal === 1 ? creditUnit.singular : creditUnit.plural}`,
49547
- perCreditLabel: options.showPricePerCredit ? `${formatCommercePrice(pass.pricePerCredit, pass.currency)}/${creditUnit.perCreditSuffix}` : null,
49548
- validityLabel: options.showValidityPeriod && pass.validDays ? formatValidity(pass.validDays) : null,
49549
- action: pass.purchaseAvailability === "purchasable" ? {
49550
- kind: "purchase",
49551
- label: options.passActionLabel,
49552
- product: pass
49553
- } : {
49554
- kind: "unavailable",
49555
- label: "Online purchasing is unavailable."
49556
- }
49557
- };
49558
- });
49559
- const memberships = options.memberships.map((membership) => ({
50172
+ function passCardDisplay(pass, options) {
50173
+ const creditUnit = getCreditProductCreditUnit(pass.productUseCase);
50174
+ return {
50175
+ id: pass.id,
50176
+ title: pass.name,
50177
+ description: options.showDescriptions ? pass.description : null,
50178
+ priceLabel: formatCommercePrice(pass.price, pass.currency),
50179
+ ...options.cardList ? {
50180
+ priceAmountLabel: formatCurrencyAmount(pass.price, pass.currency, { decimals: "auto" }),
50181
+ perCreditAmountLabel: options.showPricePerCredit ? `${formatCurrencyAmount(pass.pricePerCredit, pass.currency, { decimals: "auto" })} a ${creditUnit.perCreditSuffix}` : null
50182
+ } : {},
50183
+ creditsLabel: `${pass.creditsTotal} ${pass.creditsTotal === 1 ? creditUnit.singular : creditUnit.plural}`,
50184
+ perCreditLabel: options.showPricePerCredit ? `${formatCommercePrice(pass.pricePerCredit, pass.currency)}/${creditUnit.perCreditSuffix}` : null,
50185
+ validityLabel: options.showValidityPeriod && pass.validDays ? formatValidity(pass.validDays) : null,
50186
+ action: pass.purchaseAvailability === "purchasable" ? {
50187
+ kind: "purchase",
50188
+ label: options.passActionLabel,
50189
+ product: pass
50190
+ } : {
50191
+ kind: "unavailable",
50192
+ label: "Online purchasing is unavailable."
50193
+ }
50194
+ };
50195
+ }
50196
+ function membershipCardDisplay(membership, options) {
50197
+ return {
49560
50198
  id: membership.id,
49561
50199
  title: membership.name,
49562
50200
  description: options.showDescriptions ? membership.description : null,
49563
50201
  priceLabel: formatCommercePrice(membership.price, membership.currency),
50202
+ ...options.cardList ? {
50203
+ priceAmountLabel: formatCurrencyAmount(membership.price, membership.currency, { decimals: "auto" }),
50204
+ priceUnitLabel: `a ${membership.billingInterval}`
50205
+ } : {},
49564
50206
  intervalLabel: formatInterval(membership.billingInterval),
49565
50207
  action: membership.purchaseAvailability === "purchasable" ? {
49566
50208
  kind: "purchase",
@@ -49570,7 +50212,11 @@ function buildPassesMembershipsDisplay(options) {
49570
50212
  kind: "unavailable",
49571
50213
  label: "Online purchasing is unavailable."
49572
50214
  }
49573
- }));
50215
+ };
50216
+ }
50217
+ function buildPassesMembershipsDisplay(options) {
50218
+ const passes = options.passes.map((pass) => passCardDisplay(pass, options));
50219
+ const memberships = options.memberships.map((membership) => membershipCardDisplay(membership, options));
49574
50220
  return {
49575
50221
  kind: "passes-memberships",
49576
50222
  state: passes.length === 0 && memberships.length === 0 ? "empty" : "ready",
@@ -49660,6 +50306,42 @@ function formatValidity(days) {
49660
50306
  if (days === 365) return "Valid for 1 year";
49661
50307
  return `Valid for ${Math.round(days / 30)} months`;
49662
50308
  }
50309
+ const membershipClassesLabel = "Unlimited classes";
50310
+ function passCardListContent(display) {
50311
+ return {
50312
+ id: display.id,
50313
+ title: display.title,
50314
+ text: display.description,
50315
+ price: {
50316
+ amount: display.priceAmountLabel ?? display.priceLabel,
50317
+ unit: display.creditsLabel
50318
+ },
50319
+ facts: [...display.validityLabel ? [{
50320
+ label: "Validity",
50321
+ value: display.validityLabel
50322
+ }] : [], ...display.perCreditLabel ? [{
50323
+ label: "Price per credit",
50324
+ value: display.perCreditAmountLabel ?? display.perCreditLabel
50325
+ }] : []],
50326
+ action: { label: display.action.label }
50327
+ };
50328
+ }
50329
+ function membershipCardListContent(display) {
50330
+ return {
50331
+ id: display.id,
50332
+ title: display.title,
50333
+ text: display.description,
50334
+ price: {
50335
+ amount: display.priceAmountLabel ?? display.priceLabel,
50336
+ unit: display.priceUnitLabel ?? display.intervalLabel
50337
+ },
50338
+ facts: [{
50339
+ label: "Classes",
50340
+ value: membershipClassesLabel
50341
+ }],
50342
+ action: { label: display.action.label }
50343
+ };
50344
+ }
49663
50345
 
49664
50346
  //#endregion
49665
50347
  //#region ../blocks/src/system/runtime/nodes/shop.pass-contract.ts
@@ -49668,6 +50350,8 @@ const passCardDisplaySchema = z.object({
49668
50350
  title: z.string(),
49669
50351
  description: z.string().nullable(),
49670
50352
  priceLabel: z.string(),
50353
+ priceAmountLabel: z.string().optional(),
50354
+ perCreditAmountLabel: z.string().nullable().optional(),
49671
50355
  creditsLabel: z.string(),
49672
50356
  perCreditLabel: z.string().nullable(),
49673
50357
  validityLabel: z.string().nullable(),
@@ -49695,6 +50379,8 @@ const contentEntrySchema = z.object({
49695
50379
  slug: z.string().nullable().optional()
49696
50380
  }).nullable();
49697
50381
  const shopSourceSchema = z.object({
50382
+ ...cardListPresentationSchemaShape,
50383
+ featured: z.string().nullable().optional(),
49698
50384
  className: z.string().nullable().optional(),
49699
50385
  mode: shopModeSchema.optional(),
49700
50386
  heading: z.string().nullable().optional(),
@@ -49793,6 +50479,8 @@ const productDetailDisplaySchema = z.discriminatedUnion("state", [z.object({
49793
50479
  body: z.unknown().nullable()
49794
50480
  })]);
49795
50481
  const membershipCardDisplaySchema = z.object({
50482
+ priceAmountLabel: z.string().optional(),
50483
+ priceUnitLabel: z.string().optional(),
49796
50484
  id: z.string(),
49797
50485
  title: z.string(),
49798
50486
  description: z.string().nullable(),
@@ -49855,6 +50543,8 @@ const checkoutDisplaySchema = z.object({
49855
50543
  submitButtonText: z.string()
49856
50544
  });
49857
50545
  const passesMembershipsHydrationSchema = z.object({
50546
+ presentation: z.object(cardListPresentationSchemaShape).optional(),
50547
+ featured: z.string().optional(),
49858
50548
  memberships: z.array(publicMembershipProductSchema),
49859
50549
  passPurchaseMode: z.enum(["add_to_cart", "buy_now"]),
49860
50550
  buttonVariant: blockButtonRoleSchema,
@@ -49932,8 +50622,10 @@ const shopBlockRuntime = defineProgressiveBlockRuntime({
49932
50622
  buildRender: (source) => {
49933
50623
  const passPurchaseMode = source.passPurchaseMode ?? "add_to_cart";
49934
50624
  const subscribeButtonText = source.subscribeButtonText ?? "Subscribe";
50625
+ const products = shopDisplayedProducts(source);
49935
50626
  return {
49936
50627
  display: buildPassesMembershipsDisplay({
50628
+ cardList: source.lookSlots !== undefined,
49937
50629
  className: source.className,
49938
50630
  layout: source.layout,
49939
50631
  columns: source.columns,
@@ -49942,14 +50634,16 @@ const shopBlockRuntime = defineProgressiveBlockRuntime({
49942
50634
  membershipsHeading: source.membershipsHeading,
49943
50635
  passActionLabel: resolvePassBuyButtonText(source.buyButtonText, passPurchaseMode),
49944
50636
  membershipActionLabel: subscribeButtonText,
49945
- passes: source.showPasses === false ? [] : source.passes ?? [],
49946
- memberships: source.showMemberships === false ? [] : source.memberships ?? [],
50637
+ passes: products.passes,
50638
+ memberships: products.memberships,
49947
50639
  showDescriptions: source.showDescriptions !== false,
49948
50640
  showValidityPeriod: source.showValidityPeriod !== false,
49949
50641
  showPricePerCredit: resolveShowPricePerCredit(source)
49950
50642
  }),
49951
50643
  hydration: {
49952
- memberships: source.showMemberships === false ? [] : source.memberships ?? [],
50644
+ ...source.lookSlots ? { presentation: shopCardListPresentation(source) } : {},
50645
+ ...shopFeaturedProduct(source, products),
50646
+ memberships: products.memberships,
49953
50647
  passPurchaseMode,
49954
50648
  buttonVariant: source.buttonVariant ?? "primary",
49955
50649
  siteTerms: source.siteTerms,
@@ -50190,6 +50884,32 @@ function getShopDisplayForItems(render, items) {
50190
50884
  submitButtonText: render.hydration.submitButtonText
50191
50885
  });
50192
50886
  }
50887
+ function shopCardListPresentation(source) {
50888
+ return cardListPresentationKeys.reduce((presentation, key) => ({
50889
+ ...presentation,
50890
+ [key]: source[key]
50891
+ }), {});
50892
+ }
50893
+ function shopDisplayedProducts(source) {
50894
+ return {
50895
+ passes: source.showPasses === false ? [] : source.passes ?? [],
50896
+ memberships: source.showMemberships === false ? [] : source.memberships ?? []
50897
+ };
50898
+ }
50899
+ function shopFeaturedProduct(source, products) {
50900
+ const selectedId = source.featured;
50901
+ const visible = [...products.passes, ...products.memberships].some(({ id }) => id === selectedId);
50902
+ return source.lookSlots && source.itemRoles?.featured && selectedId && visible ? { featured: selectedId } : {};
50903
+ }
50904
+ /** Server and hydrated cards share the same decided role and featured marker. */
50905
+ function shopProductPresentation(hydration, productId, index, count) {
50906
+ if (!hydration.presentation) return undefined;
50907
+ const featured = hydration.featured === productId;
50908
+ return {
50909
+ ...listItemPresentation(hydration.presentation, index, count, featured),
50910
+ ...featured ? { featured: true } : {}
50911
+ };
50912
+ }
50193
50913
 
50194
50914
  //#endregion
50195
50915
  //#region ../blocks/src/system/blocks/shop.ts
@@ -50210,7 +50930,17 @@ function getShopDisplayForItems(render, items) {
50210
50930
  * - Stripe handles payment security
50211
50931
  */
50212
50932
  const shopManifest = createBlockManifest({
50213
- variantMeaning: { kind: "arrangement" },
50933
+ variantMeaning: {
50934
+ kind: "arrangement",
50935
+ lookOnlyKeys: [
50936
+ "cards",
50937
+ "lead-support",
50938
+ "index",
50939
+ "sequence",
50940
+ "slides",
50941
+ "track"
50942
+ ]
50943
+ },
50214
50944
  id: "block.shop",
50215
50945
  version: "1.0.0",
50216
50946
  title: "Passes & Memberships",
@@ -50218,6 +50948,20 @@ const shopManifest = createBlockManifest({
50218
50948
  description: "Display passes and memberships for purchase",
50219
50949
  skipSectionStyles: true,
50220
50950
  additionalFields: [
50951
+ ...cardListKnobFields,
50952
+ ...cardListSectionActionFields,
50953
+ {
50954
+ id: "featured",
50955
+ type: "text",
50956
+ label: "Featured product",
50957
+ required: false,
50958
+ multiline: false,
50959
+ maxLength: 100,
50960
+ ui: {
50961
+ intent: "display",
50962
+ hidden: true
50963
+ }
50964
+ },
50221
50965
  {
50222
50966
  id: "showPasses",
50223
50967
  type: "boolean",
@@ -50419,6 +51163,8 @@ const shopManifest = createBlockManifest({
50419
51163
  baseClass: "",
50420
51164
  spacing: "spacious",
50421
51165
  children: sectionContainer([shop(withApiBindings({
51166
+ ...cardListPresentationBindings,
51167
+ featured: { $bind: { from: "content.featured" } },
50422
51168
  readAuthority: { $bind: { from: "$root.readAuthority" } },
50423
51169
  siteTerms: { $bind: { from: "$root.siteTerms" } },
50424
51170
  demoEmailSuppressed: { $bind: {
@@ -50481,7 +51227,7 @@ const shopManifest = createBlockManifest({
50481
51227
  } },
50482
51228
  passes: { $bind: { from: "data.passes.passes" } },
50483
51229
  memberships: { $bind: { from: "data.memberships.memberships" } }
50484
- }))], { gap: "lg" })
51230
+ })), cardListSectionAction()], { gap: "lg" })
50485
51231
  })]
50486
51232
  });
50487
51233
  const shopBlockDefinition = {
@@ -51761,7 +52507,7 @@ function sanitizeNewsletterSignupPublicContent(content) {
51761
52507
  return content;
51762
52508
  }
51763
52509
  const value = content;
51764
- return Object.fromEntries(newsletterPublicFieldIds.map((id) => [id, value[id]]));
52510
+ return Object.fromEntries([...newsletterPublicFieldIds.map((id) => [id, value[id]]), ...Object.entries(value).filter(([id]) => compiledRuntimeStyleFieldNames.includes(id))]);
51765
52511
  }
51766
52512
  const newsletterSignupBlockDefinition = {
51767
52513
  manifest: newsletterSignupManifest,
@@ -51807,7 +52553,9 @@ const teamMemberDataSchema = z.object({
51807
52553
  bio: z.string().nullable(),
51808
52554
  summary: z.string().nullable(),
51809
52555
  photo: teamMemberPhotoSchema,
51810
- profilePath: z.string().nullable()
52556
+ profilePath: z.string().nullable(),
52557
+ specialties: z.array(z.string()).optional(),
52558
+ profileBio: RichTextDocSchema.nullable().optional()
51811
52559
  });
51812
52560
  const teamMembersDataSchema = z.array(teamMemberDataSchema);
51813
52561
  const dynamicSourceWhen = { equals: "site_staff" };
@@ -51921,6 +52669,47 @@ const stackVariant = wrapVariant({
51921
52669
  } } },
51922
52670
  children: [manualStackMembersNode, dynamicStackMembersNode]
51923
52671
  });
52672
+ const cardListVariant = styledSection({
52673
+ children: sectionContainer([el("div", { ...lookSlot("layout") }, [
52674
+ el("div", { ...lookSlot("head") }, [text({
52675
+ as: "h2",
52676
+ ...lookSlot("heading")
52677
+ }, bind("content.heading"), when("content.heading"))], when("content.heading")),
52678
+ el("team-members", {
52679
+ ...cardListPresentationBindings,
52680
+ biographySource: { $bind: { from: "content._effectiveLook.look.structure.teamBiography" } },
52681
+ source: { $bind: {
52682
+ from: "content.source",
52683
+ fallback: "manual"
52684
+ } },
52685
+ members: { $bind: {
52686
+ from: "content.members",
52687
+ fallback: []
52688
+ } },
52689
+ siteMembers: { $bind: {
52690
+ from: "data.members",
52691
+ fallback: []
52692
+ } },
52693
+ carouselRegion: { $bind: {
52694
+ from: "content.heading",
52695
+ fallback: "Team members"
52696
+ } },
52697
+ sectionHeading: { $bind: { from: "content.heading" } }
52698
+ }),
52699
+ cardListSectionAction()
52700
+ ])], { gap: "md" }),
52701
+ baseClass: "",
52702
+ spacing: "comfortable"
52703
+ });
52704
+ const arrangementOnlyKeys = [
52705
+ "cards",
52706
+ "lead-support",
52707
+ "index",
52708
+ "sequence",
52709
+ "plates",
52710
+ "slides",
52711
+ "track"
52712
+ ];
51924
52713
  const teamMembersManifest = createBlockManifest({
51925
52714
  id: "block.team-members",
51926
52715
  title: "Team Members",
@@ -51928,6 +52717,9 @@ const teamMembersManifest = createBlockManifest({
51928
52717
  contentSource: { kind: "mixed" },
51929
52718
  titleSource: "heading",
51930
52719
  additionalFields: [
52720
+ ...cardListKnobFields,
52721
+ ...cardListPhotoNumberFields,
52722
+ ...cardListSectionActionFields,
51931
52723
  parseFieldDefinition({
51932
52724
  id: "heading",
51933
52725
  type: "text",
@@ -52096,9 +52888,13 @@ const teamMembersManifest = createBlockManifest({
52096
52888
  layout: gridVariant,
52097
52889
  variants: {
52098
52890
  grid: gridVariant,
52099
- stack: stackVariant
52891
+ stack: [when("content._effectiveLook", { not: true })(stackVariant), when("content._effectiveLook")(cardListVariant)],
52892
+ ...Object.fromEntries(arrangementOnlyKeys.map((key) => [key, cardListVariant]))
52893
+ },
52894
+ variantMeaning: {
52895
+ kind: "arrangement",
52896
+ lookOnlyKeys: [...arrangementOnlyKeys]
52100
52897
  },
52101
- variantMeaning: { kind: "arrangement" },
52102
52898
  defaultVariant: "grid",
52103
52899
  variantOptions: {
52104
52900
  grid: {
@@ -52323,10 +53119,9 @@ function normalizeLocationMapContent(content) {
52323
53119
  const locationSource = normalizeLocationMapSource(record.locationSource);
52324
53120
  const venueId = typeof record.venueId === "string" && record.venueId.trim() ? record.venueId.trim() : null;
52325
53121
  return {
52326
- heading: typeof record.heading === "string" ? record.heading : null,
53122
+ heading: typeof record.heading === "string" ? record.heading : undefined,
52327
53123
  locationSource,
52328
- venueId: locationSource === "venue" ? venueId : null,
52329
- location: locationSource === "custom" ? normalized.locationValue : null,
53124
+ ...authoredLocationMapSourceFields(locationSource, venueId, normalized.locationValue),
52330
53125
  showAddressText: normalized.showAddressText,
52331
53126
  showDirections: normalized.showDirections,
52332
53127
  directionsLabel: normalized.directionsLabel,
@@ -52334,6 +53129,23 @@ function normalizeLocationMapContent(content) {
52334
53129
  heightPx: normalized.heightPx
52335
53130
  };
52336
53131
  }
53132
+ /** Keep only the location fields owned by the selected source. */
53133
+ function authoredLocationMapSourceFields(source, venueId, location) {
53134
+ switch (source) {
53135
+ case "custom": return {
53136
+ venueId: null,
53137
+ location: location ?? undefined
53138
+ };
53139
+ case "venue": return {
53140
+ venueId,
53141
+ location: undefined
53142
+ };
53143
+ case "business_address": return {
53144
+ venueId: null,
53145
+ location: undefined
53146
+ };
53147
+ }
53148
+ }
52337
53149
  const locationMapManifest = createBlockManifest({
52338
53150
  variantMeaning: { kind: "arrangement" },
52339
53151
  id: "block.location-map",
@@ -52405,7 +53217,7 @@ const locationMapManifest = createBlockManifest({
52405
53217
  type: "group",
52406
53218
  label: "Location",
52407
53219
  description: "Enter the address and place the map pin.",
52408
- required: true,
53220
+ required: false,
52409
53221
  ui: {
52410
53222
  widget: "locationPicker",
52411
53223
  intent: "content",
@@ -53210,11 +54022,7 @@ const systemBlockDefinitionsRaw = [
53210
54022
  weight: "standard"
53211
54023
  })]
53212
54024
  })),
53213
- withDesignCapability("block.team-members", teamMembersBlockDefinition, declareBlockDesignCapability({
53214
- sectionSurfaces: "supported",
53215
- contentFrames: ["plain"],
53216
- cardRoles: "supported"
53217
- }), defineBlockDesignMetadata({
54025
+ withDesignCapability("block.team-members", teamMembersBlockDefinition, withLookCapability(plainItemsCapable()), defineBlockDesignMetadata({
53218
54026
  flow: proofFlow,
53219
54027
  layoutProfiles: [layoutProfile({
53220
54028
  variantKey: "grid",
@@ -53285,7 +54093,7 @@ const systemBlockDefinitionsRaw = [
53285
54093
  weight: "standard"
53286
54094
  })]
53287
54095
  })),
53288
- withDesignCapability("block.shop", shopBlockDefinition, sectionOnly(), defineBlockDesignMetadata({
54096
+ withDesignCapability("block.shop", shopBlockDefinition, withLookCapability(plainItemsCapable()), defineBlockDesignMetadata({
53289
54097
  flow: offeringsFlow,
53290
54098
  layoutProfiles: [layoutProfile({
53291
54099
  variantKey: "default",
@@ -54378,29 +55186,15 @@ const eventCalendarListWindow = {
54378
55186
  id: "events.calendarListWindow",
54379
55187
  kind: "formatter",
54380
55188
  schema: listWindowSchema,
54381
- run(value, { key }, context) {
55189
+ run(_value, { key }, context) {
54382
55190
  const content = context.data ?? {};
54383
55191
  if (eventCalendarListGroupingDecision({
54384
55192
  ...content,
54385
55193
  arrangementLooks: content._lookSlots !== undefined
54386
55194
  }).grouping !== "week") return undefined;
54387
- const root = asPartialObject(value);
54388
- const timeZone = toBookingTimeZone(root?.bookingTimeZone);
54389
- const startOfWeek = content.startOfWeek === "sunday" ? "sunday" : "monday";
54390
- return buildEventListWeek(getLocalScheduleDate(new Date(), timeZone), startOfWeek, timeZone)[key];
54391
- }
54392
- };
54393
- const eventCalendarLookTimeZone = {
54394
- id: "events.calendarLookTimeZone",
54395
- kind: "formatter",
54396
- run(value, _options, context) {
54397
- const content = context.data ?? {};
54398
- const monthLook = content._lookMonthArrangement === "month-grid" || content._lookMonthArrangement === "month-day";
54399
- const weekList = eventCalendarListGroupingDecision({
54400
- ...content,
54401
- arrangementLooks: content._lookSlots !== undefined
54402
- }).grouping === "week";
54403
- return monthLook || weekList ? value : undefined;
55195
+ const timeZone = context.dateContext.timeZone;
55196
+ const startOfWeek = eventCalendarWeekStart(timeZone, content.startOfWeek === "sunday" || content.startOfWeek === "monday" ? content.startOfWeek : undefined);
55197
+ return buildEventListWeek(getLocalScheduleDate(new Date(readRuntimeNow(context.now)), timeZone), startOfWeek, timeZone)[key];
54404
55198
  }
54405
55199
  };
54406
55200
 
@@ -54413,7 +55207,6 @@ const builtinTransformDefinitions = [
54413
55207
  lookCarouselRegionTransform,
54414
55208
  lookSkinSlotTransform,
54415
55209
  eventCalendarListWindow,
54416
- eventCalendarLookTimeZone,
54417
55210
  eventCalendarSelectionParam,
54418
55211
  eventCalendarReadEnabled,
54419
55212
  eventCalendarReadProfile,
@@ -55217,228 +56010,6 @@ function toAbsolutePath(base, path$3) {
55217
56010
  return `${baseStr}${trimmed}`;
55218
56011
  }
55219
56012
 
55220
- //#endregion
55221
- //#region ../blocks/src/primitives/content/utils/sanitizeHref.ts
55222
- const SAFE_PROTOCOLS = new Set([
55223
- "http:",
55224
- "https:",
55225
- "mailto:",
55226
- "tel:"
55227
- ]);
55228
- const CONTROL_CHARS_PATTERN = /[\u0000-\u001F\u007F]+/g;
55229
- function normalizeInput(value) {
55230
- return value.replace(CONTROL_CHARS_PATTERN, "").trim();
55231
- }
55232
- function decodeForSchemeCheck(value) {
55233
- try {
55234
- return decodeURIComponent(value);
55235
- } catch {
55236
- return value;
55237
- }
55238
- }
55239
- /**
55240
- * Returns a safe href for rich-text anchors, or null when the value should not be rendered.
55241
- *
55242
- * Policy:
55243
- * - Allow absolute URLs with protocols: http, https, mailto, tel.
55244
- * - Allow root-relative ("/path") and hash-only ("#section") links.
55245
- * - Reject protocol-relative URLs ("//example.com"), bare domains ("example.com"),
55246
- * backslashes, executable schemes (javascript/data/vbscript), malformed URLs, and empty values.
55247
- * - Preserve percent-encoding from the original normalized input.
55248
- */
55249
- function sanitizeHref(raw) {
55250
- if (typeof raw !== "string") return null;
55251
- const normalized = normalizeInput(raw);
55252
- if (!normalized) return null;
55253
- if (normalized.includes("\\") || normalized.startsWith("//")) return null;
55254
- if (normalized.startsWith("/")) return normalized;
55255
- if (normalized.startsWith("#")) return normalized;
55256
- return sanitizeAbsoluteHref(normalized);
55257
- }
55258
- function sanitizeAbsoluteHref(normalized) {
55259
- const decoded = normalizeInput(decodeForSchemeCheck(normalized));
55260
- const schemeMatch = decoded.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);
55261
- if (!schemeMatch) return null;
55262
- const rawScheme = schemeMatch[1];
55263
- if (!rawScheme) return null;
55264
- const protocol = `${rawScheme.toLowerCase()}:`;
55265
- if (!SAFE_PROTOCOLS.has(protocol)) return null;
55266
- try {
55267
- const parsed = new URL(normalized);
55268
- if (!SAFE_PROTOCOLS.has(parsed.protocol.toLowerCase())) {
55269
- return null;
55270
- }
55271
- } catch {
55272
- return null;
55273
- }
55274
- return normalized;
55275
- }
55276
-
55277
- //#endregion
55278
- //#region ../blocks/src/primitives/content/components/RichText.tsx
55279
- function parseDimension(value) {
55280
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
55281
- return Math.round(value);
55282
- }
55283
- if (typeof value === "string" && value.trim().length > 0) {
55284
- const parsed = Number(value);
55285
- if (Number.isFinite(parsed) && parsed > 0) {
55286
- return Math.round(parsed);
55287
- }
55288
- }
55289
- return undefined;
55290
- }
55291
- function normalizeRel(target, rel) {
55292
- const normalizedRel = rel?.trim();
55293
- if (target !== "_blank") {
55294
- return normalizedRel && normalizedRel.length > 0 ? normalizedRel : undefined;
55295
- }
55296
- const tokens = new Set((normalizedRel ?? "").split(/\s+/).map((token) => token.trim().toLowerCase()).filter(Boolean));
55297
- tokens.add("noopener");
55298
- tokens.add("noreferrer");
55299
- return Array.from(tokens).join(" ");
55300
- }
55301
- function renderMarks(text$1, marks, key, context) {
55302
- if (!marks || marks.length === 0) return text$1;
55303
- return marks.reduce((acc, mark) => {
55304
- switch (mark.type) {
55305
- case "bold":
55306
- case "strong": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: acc }, `${key}-b`);
55307
- case "italic":
55308
- case "em": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("em", { children: acc }, `${key}-i`);
55309
- case "link": return renderLinkMark(mark, acc, key, context);
55310
- default: return acc;
55311
- }
55312
- }, text$1);
55313
- }
55314
- function richTextLinkAttributes(mark, mode) {
55315
- const attrs = mark.attrs ?? {};
55316
- const href = sanitizeHref(attrs.href);
55317
- if (!href) return null;
55318
- const storedTarget = typeof attrs.target === "string" && attrs.target.trim().length > 0 ? attrs.target.trim() : undefined;
55319
- const target = mode === "new-tab" ? "_blank" : storedTarget;
55320
- const rel = typeof attrs.rel === "string" ? attrs.rel : undefined;
55321
- return {
55322
- href,
55323
- target,
55324
- rel: normalizeRel(target, rel)
55325
- };
55326
- }
55327
- function renderLinkMark(mark, text$1, key, context) {
55328
- if (typeof text$1 === "string" && text$1.length === 0) return text$1;
55329
- const attributes = richTextLinkAttributes(mark, context.linkTarget);
55330
- return attributes ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
55331
- ...attributes,
55332
- ...listLookControl(context, "link"),
55333
- children: text$1
55334
- }, `${key}-l`) : text$1;
55335
- }
55336
- function renderDoc(node, key, context) {
55337
- if (!node || !node.type) return null;
55338
- const children = node.content?.map((child, index) => renderDoc(child, `${key}-${index}`, context));
55339
- switch (node.type) {
55340
- case "doc": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
55341
- case "paragraph": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: children ?? renderMarks(node.text ?? "", node.marks, key, context) }, key);
55342
- case "heading": return renderHeading(node, key, children, context);
55343
- case "blockquote": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("blockquote", {
55344
- ...listLookSlot(context, "quote"),
55345
- children
55346
- }, key);
55347
- case "image": return renderRichTextImage(node, key, context);
55348
- default: return renderSimpleNode(node, key, children, context);
55349
- }
55350
- }
55351
- function renderHeading(node, key, children, context) {
55352
- const level = Math.min(Math.max(Number(node.attrs?.level ?? 2), 1), 6);
55353
- return import_react.createElement(`h${level}`, {
55354
- key,
55355
- ...level === 2 ? listLookSlot(context, "heading") : {}
55356
- }, children);
55357
- }
55358
- function renderSimpleNode(node, key, children, context) {
55359
- switch (node.type) {
55360
- case "bulletList": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children }, key);
55361
- case "orderedList": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { children }, key);
55362
- case "listItem": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children }, key);
55363
- case "horizontalRule": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", {}, key);
55364
- case "hardBreak": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}, key);
55365
- case "text": return renderMarks(node.text ?? "", node.marks, key, context);
55366
- default: return null;
55367
- }
55368
- }
55369
- function richTextImageOptions(width, height) {
55370
- if (width) return {
55371
- width,
55372
- resize: "contain",
55373
- quality: 85
55374
- };
55375
- if (height) return {
55376
- height,
55377
- resize: "contain",
55378
- quality: 85
55379
- };
55380
- return ImagePresets.full;
55381
- }
55382
- function resolveRichTextImageSource(attrs, width, height, context) {
55383
- if (attrs.identifier !== undefined && attrs.identifier !== null) {
55384
- const parsed = parseCanonicalMediaIdentifier(attrs.identifier);
55385
- if (!parsed.ok) return undefined;
55386
- if (context?.mediaAssetIndex) {
55387
- return resolveImageUrlWithContext({
55388
- type: "image",
55389
- identifier: parsed.value
55390
- }, richTextImageOptions(width, height), context);
55391
- }
55392
- return typeof attrs.src === "string" ? attrs.src.trim() || undefined : undefined;
55393
- }
55394
- return typeof attrs.src === "string" ? attrs.src.trim() || undefined : undefined;
55395
- }
55396
- function renderRichTextImage(node, key, context) {
55397
- const attrs = node.attrs ?? {};
55398
- const width = parseDimension(attrs.width);
55399
- const height = parseDimension(attrs.height);
55400
- const src = resolveRichTextImageSource(attrs, width, height, context.imageResolveContext);
55401
- if (!src) return null;
55402
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
55403
- src,
55404
- alt: typeof attrs.alt === "string" ? attrs.alt : "",
55405
- title: typeof attrs.title === "string" && attrs.title.trim() ? attrs.title : undefined,
55406
- width,
55407
- height,
55408
- loading: "lazy",
55409
- decoding: "async",
55410
- ...listLookSlot(context, "photo")
55411
- }, key);
55412
- }
55413
- /** Mark meaning in the renderer, rather than choosing ink with a positional CSS rule. */
55414
- function renderRichTextContent(doc, context, leadSlot) {
55415
- if (!leadSlot) return renderDoc(doc, "doc", context);
55416
- const leadIndex = doc.content?.findIndex((node) => node.type === "paragraph");
55417
- return doc.content?.map((node, index) => {
55418
- const rendered = renderDoc(node, `doc-${index}`, context);
55419
- return index === leadIndex && import_react.isValidElement(rendered) ? import_react.cloneElement(rendered, { "data-slot": leadSlot }) : rendered;
55420
- });
55421
- }
55422
- const DEFAULT_RICH_TEXT_CLASS = "rb-prose rb-max-w-none";
55423
- function RichText({ as: Comp = "div", className, doc, path: path$3, style, "data-slot": dataSlot, "data-surface": dataSurface, linkTarget = "stored", leadParagraphSlot, lookSlots }) {
55424
- const imageResolveContext = getCurrentResolveImageUrlContext() ?? null;
55425
- const content = doc ? renderRichTextContent(doc, {
55426
- imageResolveContext,
55427
- linkTarget,
55428
- lookSlots
55429
- }, leadParagraphSlot) : null;
55430
- const dataAttributes = path$3 ? { "data-builder-richtext": path$3 } : undefined;
55431
- const resolvedClassName = className?.trim() || DEFAULT_RICH_TEXT_CLASS;
55432
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Comp, {
55433
- "data-slot": dataSlot,
55434
- "data-surface": dataSurface,
55435
- style: dataSlot === undefined ? undefined : style,
55436
- className: resolvedClassName,
55437
- ...dataAttributes,
55438
- children: content
55439
- });
55440
- }
55441
-
55442
56013
  //#endregion
55443
56014
  //#region ../blocks/src/primitives/content/components/Text.tsx
55444
56015
  function Text({ as: Comp = "span", children, className = "", path: path$3, style, "data-slot": dataSlot, "data-surface": dataSurface, maxLength: _maxLength }) {
@@ -56328,6 +56899,29 @@ function assertNever$5(value) {
56328
56899
  throw new Error(`Unhandled accordion icon set: ${String(value)}`);
56329
56900
  }
56330
56901
 
56902
+ //#endregion
56903
+ //#region ../blocks/src/system/runtime/nodes/accordionLookSlots.ts
56904
+ /** An item with a slot names its parts too, so skins never reach for accordion class names. */
56905
+ function accordionItemPartSlots(itemSlot) {
56906
+ return itemSlot === undefined ? {
56907
+ title: {},
56908
+ icon: {},
56909
+ text: {}
56910
+ } : {
56911
+ title: { "data-slot": "item-title" },
56912
+ icon: { "data-slot": "icon" },
56913
+ text: { "data-slot": "item-text" }
56914
+ };
56915
+ }
56916
+ /** Only the Look attributes, so a legacy accordion's markup stays exactly as it was. */
56917
+ function accordionLookAttributes(props$1) {
56918
+ return props$1["data-slot"] === undefined ? {} : {
56919
+ "data-slot": props$1["data-slot"],
56920
+ "data-surface": props$1["data-surface"],
56921
+ style: props$1.style
56922
+ };
56923
+ }
56924
+
56331
56925
  //#endregion
56332
56926
  //#region ../blocks/src/system/runtime/nodes/accordion.server.tsx
56333
56927
  /**
@@ -56341,8 +56935,9 @@ const ACCORDION_ICONS = {
56341
56935
  CirclePlus: CirclePlusIcon,
56342
56936
  CircleMinus: CircleMinusIcon
56343
56937
  };
56344
- const AccordionNodeSSR = ({ blockId, children, className, type = "single", defaultValue, collapsible }) => {
56938
+ const AccordionNodeSSR = ({ blockId, children, className, type = "single", defaultValue, collapsible, ...look }) => {
56345
56939
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
56940
+ ...accordionLookAttributes(look),
56346
56941
  className: clsx_default("accordion-root", className),
56347
56942
  "data-accordion-type": type,
56348
56943
  "data-default-value": Array.isArray(defaultValue) ? defaultValue.join(",") : defaultValue,
@@ -56354,7 +56949,9 @@ const AccordionNodeSSR = ({ blockId, children, className, type = "single", defau
56354
56949
  } }), children]
56355
56950
  });
56356
56951
  };
56357
- const AccordionItemNodeSSR = ({ className, value, trigger, content, question, answer, iconStyle, iconTreatment }) => {
56952
+ const AccordionItemNodeSSR = ({ className, value, trigger, content, question, answer, iconStyle, iconTreatment, ...look }) => {
56953
+ const lookAttributes = accordionLookAttributes(look);
56954
+ const parts = accordionItemPartSlots(lookAttributes["data-slot"]);
56358
56955
  const resolvedValue = value !== undefined && value !== null ? String(value) : undefined;
56359
56956
  const triggerSource = trigger ?? question;
56360
56957
  const contentSource = content ?? answer;
@@ -56364,6 +56961,7 @@ const AccordionItemNodeSSR = ({ className, value, trigger, content, question, an
56364
56961
  const OpenIconComponent = iconConfig.openIcon ? ACCORDION_ICONS[iconConfig.openIcon] : null;
56365
56962
  const iconColorStyle = accordionIconColorStyle(iconTreatment);
56366
56963
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
56964
+ ...lookAttributes,
56367
56965
  className: clsx_default("accordion-item", className),
56368
56966
  "data-accordion-value": resolvedValue,
56369
56967
  "data-state": "closed",
@@ -56374,9 +56972,11 @@ const AccordionItemNodeSSR = ({ className, value, trigger, content, question, an
56374
56972
  "aria-expanded": "false",
56375
56973
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
56376
56974
  className: "accordion-trigger-text",
56975
+ ...parts.title,
56377
56976
  children: resolveNode$1(triggerSource)
56378
56977
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
56379
56978
  className: "accordion-icon",
56979
+ ...parts.icon,
56380
56980
  "aria-hidden": "true",
56381
56981
  "data-accordion-icon-set": iconTreatment?.iconSet,
56382
56982
  "data-accordion-icon-motion": iconTreatment?.motion,
@@ -56385,6 +56985,7 @@ const AccordionItemNodeSSR = ({ className, value, trigger, content, question, an
56385
56985
  })]
56386
56986
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
56387
56987
  className: "accordion-content",
56988
+ ...parts.text,
56388
56989
  "data-animation": "accordion",
56389
56990
  "data-state": "closed",
56390
56991
  hidden: true,
@@ -57000,6 +57601,72 @@ function appointmentServicesListing(view, arrangement) {
57000
57601
  } : view;
57001
57602
  }
57002
57603
 
57604
+ //#endregion
57605
+ //#region ../blocks/src/system/blocks/shared/teamMembers.ts
57606
+ const teamMemberSpecialtiesInputSchema = z.union([z.string(), z.array(z.string())]).nullish();
57607
+ function parseTeamMemberSpecialties(value) {
57608
+ const parsed = teamMemberSpecialtiesInputSchema.safeParse(value);
57609
+ if (!parsed.success) return [];
57610
+ const parts = typeof parsed.data === "string" ? parsed.data.split(/\r?\n|,/) : parsed.data ?? [];
57611
+ return [...new Set(parts.map((part) => part.trim()).filter(Boolean))];
57612
+ }
57613
+
57614
+ //#endregion
57615
+ //#region ../blocks/src/system/runtime/nodes/team-members.tsx
57616
+ const teamMemberCardSchema = z.object({
57617
+ id: z.string().optional(),
57618
+ name: z.string(),
57619
+ role: z.string().nullish(),
57620
+ bio: z.union([z.string(), RichTextDocSchema]).nullish(),
57621
+ profileBio: RichTextDocSchema.nullish(),
57622
+ photo: runtimeMediaSchema.refine((value) => value.type === "image").nullish(),
57623
+ profilePath: z.string().nullish(),
57624
+ specialties: z.unknown().transform(parseTeamMemberSpecialties)
57625
+ });
57626
+ /** Staff and manual members meet at one boundary; Look data chooses biography length. */
57627
+ function teamMemberCardContent(member, index, biographySource) {
57628
+ const biography = biographySource === "profile" && member.profileBio ? member.profileBio : member.bio;
57629
+ const href = sanitizeHref(member.profilePath);
57630
+ return {
57631
+ id: member.id ?? `member-${index}`,
57632
+ title: member.name,
57633
+ media: member.photo,
57634
+ ...typeof biography === "string" ? { text: biography } : { textDocument: biography ?? undefined },
57635
+ facts: member.role ? [{
57636
+ label: "Role",
57637
+ value: member.role
57638
+ }] : [],
57639
+ chips: member.specialties,
57640
+ ...href ? { action: {
57641
+ label: `About ${member.name}`,
57642
+ href
57643
+ } } : {}
57644
+ };
57645
+ }
57646
+ function TeamMembersNode(props$1) {
57647
+ const parsed = z.array(teamMemberCardSchema).safeParse(props$1.source === "site_staff" ? props$1.siteMembers : props$1.members);
57648
+ if (!parsed.success || parsed.data.length === 0) return null;
57649
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListItems, {
57650
+ Carousel: props$1.Carousel,
57651
+ items: parsed.data.map((member, index) => teamMemberCardContent(member, index, props$1.biographySource)),
57652
+ presentation: {
57653
+ ...props$1,
57654
+ bodyParts: [
57655
+ "title",
57656
+ "facts",
57657
+ "text",
57658
+ "chips",
57659
+ "action"
57660
+ ]
57661
+ },
57662
+ Title: props$1.sectionHeading?.trim() ? "h3" : "h2",
57663
+ carouselLabels: {
57664
+ item: "person",
57665
+ region: props$1.carouselRegion || "Team members"
57666
+ }
57667
+ });
57668
+ }
57669
+
57003
57670
  //#endregion
57004
57671
  //#region ../blocks/src/system/runtime/nodes/booking-form.reserved-fields.ts
57005
57672
  const APPOINTMENT_BOOKING_BLOCK_ID = "block.appointment-booking";
@@ -57849,7 +58516,8 @@ function normalizeAppointmentBookingServices(input) {
57849
58516
  paymentTermsAvailability: service.paymentTermsAvailability,
57850
58517
  asksSiteTerms: service.asksSiteTerms,
57851
58518
  consent: service.consent,
57852
- categoryId: service.categoryId ?? null
58519
+ categoryId: service.categoryId ?? null,
58520
+ location: service.location
57853
58521
  }));
57854
58522
  }
57855
58523
  function buildAppointmentBookingSourceProps(input) {
@@ -57858,6 +58526,8 @@ function buildAppointmentBookingSourceProps(input) {
57858
58526
  form: input.form
57859
58527
  });
57860
58528
  return {
58529
+ dateContext: input.dateContext,
58530
+ now: input.now,
57861
58531
  siteId: input.siteId,
57862
58532
  apiBaseUrl: input.apiBaseUrl,
57863
58533
  portalToken: input.portalToken,
@@ -57893,6 +58563,7 @@ function buildEventRegistrationRuntimeTarget(input) {
57893
58563
  }
57894
58564
  function buildEventRegistrationSourceProps(input) {
57895
58565
  return {
58566
+ now: input.now,
57896
58567
  siteId: input.siteId,
57897
58568
  apiBaseUrl: input.apiBaseUrl,
57898
58569
  portalToken: input.portalToken,
@@ -57927,6 +58598,11 @@ const blockBookingPaymentTermsAvailabilitySchema = z.union([z.object({
57927
58598
  reason: z.literal("online_payment_configured_before_launch")
57928
58599
  })]);
57929
58600
  const bookingServiceSchema = z.object({
58601
+ location: z.object({ mode: z.enum([
58602
+ "online",
58603
+ "in_person",
58604
+ "either"
58605
+ ]) }).optional(),
57930
58606
  id: z.string(),
57931
58607
  siteId: z.string(),
57932
58608
  title: z.string(),
@@ -57969,7 +58645,13 @@ const bookingMailingListContextSchema = z.unknown().transform((value) => {
57969
58645
  const parsed = parseMailingListRuntimeContext(value);
57970
58646
  return parsed.ok ? parsed.value : undefined;
57971
58647
  });
58648
+ const bookingDateContextSchema = z.object({
58649
+ locale: z.string(),
58650
+ timeZone: z.string().transform(resolveBookingBusinessTimeZone)
58651
+ });
57972
58652
  const bookingSourceSchema = z.object({
58653
+ dateContext: bookingDateContextSchema,
58654
+ now: bookingNowSchema,
57973
58655
  demoEmailSuppressed: z.boolean().optional(),
57974
58656
  mailingList: bookingMailingListContextSchema.optional(),
57975
58657
  siteTerms: blockSiteTermsSchema.optional(),
@@ -58002,6 +58684,8 @@ const bookingDisplaySchema = z.object({
58002
58684
  message: z.string().nullable()
58003
58685
  });
58004
58686
  const bookingHydrationSchema = z.object({
58687
+ now: bookingNowSchema,
58688
+ dateContext: bookingDateContextSchema,
58005
58689
  demoEmailSuppressed: z.boolean().optional(),
58006
58690
  mailingList: bookingMailingListContextSchema.optional(),
58007
58691
  siteTerms: blockSiteTermsSchema.optional(),
@@ -58052,6 +58736,8 @@ const bookingFormBlockRuntime = defineClientOwnedBlockRuntime({
58052
58736
  return {
58053
58737
  display: buildBookingFormDisplay(source, form$1),
58054
58738
  hydration: {
58739
+ now: source.now,
58740
+ dateContext: source.dateContext,
58055
58741
  demoEmailSuppressed: source.demoEmailSuppressed,
58056
58742
  mailingList: source.mailingList,
58057
58743
  siteTerms: source.siteTerms,
@@ -58373,7 +59059,7 @@ function resolveSelection(input) {
58373
59059
  events: input.events ?? null,
58374
59060
  occurrenceContext: input.occurrenceContext ?? null,
58375
59061
  subject: input.subject,
58376
- nowIso: input.nowIso ?? new Date().toISOString(),
59062
+ nowIso: input.nowIso,
58377
59063
  otherDatesLimit: input.otherDatesLimit
58378
59064
  });
58379
59065
  }
@@ -58582,20 +59268,19 @@ function offeringSchedule(event, now) {
58582
59268
  };
58583
59269
  }
58584
59270
  function scheduleLine(summary, event, now) {
58585
- const options = eventDateOptions(event);
58586
59271
  const first = "occurrence" in summary ? summary.occurrence : summary.first;
58587
59272
  const last = "occurrence" in summary ? summary.occurrence : summary.last;
58588
- const context = {
58589
- locale: options.locale,
58590
- timeZone: resolveBookingBusinessTimeZone(first.timeZone)
58591
- };
58592
- const zone = event.deliveryMode === "online" || context.timeZone !== resolveBookingBusinessTimeZone(event.siteTimeZone ?? event.timeZone) ? "always" : "omit";
59273
+ const context = eventDateOptions({
59274
+ ...event,
59275
+ timeZone: first.timeZone
59276
+ });
59277
+ const zone = context.zone;
58593
59278
  const endpoint$1 = (value) => ({
58594
59279
  kind: "instant",
58595
59280
  value: toUtcIsoDateTimeString(value)
58596
59281
  });
58597
59282
  const year = (value, timeZone = context.timeZone) => now && getLocalScheduleDate(new Date(value), timeZone).year === getLocalScheduleDate(now, timeZone).year ? "omit" : "numeric";
58598
- const date = (value) => formatDate$1(endpoint$1(value), context, {
59283
+ const date = (value) => formatDate(endpoint$1(value), context, {
58599
59284
  date: "short",
58600
59285
  year: now ? year(value) : "omit"
58601
59286
  });
@@ -58679,8 +59364,7 @@ function eventHeaderContent(selection, showCost) {
58679
59364
  const { date, timeRange } = formatEventDateParts({
58680
59365
  startsAt: event.startsAt,
58681
59366
  endsAt: event.endsAt,
58682
- timeZone: selection.timeZone,
58683
- locale: eventDateOptions(event).locale
59367
+ ...eventDateOptions(event)
58684
59368
  });
58685
59369
  return {
58686
59370
  ...item,
@@ -58863,12 +59547,11 @@ function EventPricingDetails({ isDropIn, wholeSeriesPricingDisplay, acceptedItem
58863
59547
  })
58864
59548
  }) : null;
58865
59549
  }
58866
- function EventFacts({ selection, selected, locale, venue, teamMembers, pricing, showCapacity, lookSlots }) {
59550
+ function EventFacts({ selection, selected, venue, teamMembers, pricing, showCapacity, lookSlots }) {
58867
59551
  const { date, timeRange } = formatEventDateParts({
58868
59552
  startsAt: selected.startsAt,
58869
59553
  endsAt: selected.endsAt,
58870
- timeZone: selection.timeZone,
58871
- locale
59554
+ ...eventDateOptions(selected)
58872
59555
  });
58873
59556
  const dateHeading = selection.kind === "wholeSeries" ? runLifecycleLabel(selection.lifecycle) : selection.mode === "next" ? "Next date" : "Date";
58874
59557
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -58989,7 +59672,7 @@ function bookingClosedReason(reason) {
58989
59672
  * the list and says so: dropping it would leave a visitor wondering whether
58990
59673
  * they had misremembered the dates.
58991
59674
  */
58992
- function SessionSchedule({ locale, sessions, timeZone }) {
59675
+ function SessionSchedule({ sessions, event }) {
58993
59676
  if (sessions.length === 0) return null;
58994
59677
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
58995
59678
  className: "rb-label",
@@ -59003,8 +59686,10 @@ function SessionSchedule({ locale, sessions, timeZone }) {
59003
59686
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DateTimeTile, {
59004
59687
  startsAt: session.startsAt,
59005
59688
  endsAt: session.endsAt,
59006
- timeZone,
59007
- locale
59689
+ ...eventDateOptions({
59690
+ ...event,
59691
+ timeZone: session.timeZone
59692
+ })
59008
59693
  }), cancelled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
59009
59694
  className: "rb-caption status-muted",
59010
59695
  children: "Cancelled"
@@ -59013,13 +59698,12 @@ function SessionSchedule({ locale, sessions, timeZone }) {
59013
59698
  })
59014
59699
  })] });
59015
59700
  }
59016
- function OtherEventDates({ selection, locale }) {
59701
+ function OtherEventDates({ selection }) {
59017
59702
  if (selection.kind === "empty") return null;
59018
59703
  if (selection.kind === "wholeSeries") {
59019
59704
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SessionSchedule, {
59020
59705
  sessions: selection.sessions,
59021
- timeZone: selection.timeZone,
59022
- locale
59706
+ event: selection.selected
59023
59707
  });
59024
59708
  }
59025
59709
  const dates = selection.otherDates;
@@ -59035,8 +59719,7 @@ function OtherEventDates({ selection, locale }) {
59035
59719
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DateTimeTile, {
59036
59720
  startsAt: event.startsAt,
59037
59721
  endsAt: event.endsAt,
59038
- timeZone: selection.timeZone,
59039
- locale
59722
+ ...eventDateOptions(event)
59040
59723
  })
59041
59724
  }, event.occurrenceId || event.id))
59042
59725
  })] });
@@ -59076,7 +59759,7 @@ function selectedEventPricing(event, showCost) {
59076
59759
  };
59077
59760
  }
59078
59761
  function SelectedEventDetailsView(props$1) {
59079
- const { selection, locale, showVenue, showMap, showOtherDates, showTitle, showDescription, showCapacity, mapHeight, mapClassName, renderVenueMap, acceptedItems } = props$1;
59762
+ const { selection, showVenue, showMap, showOtherDates, showTitle, showDescription, showCapacity, mapHeight, mapClassName, renderVenueMap, acceptedItems } = props$1;
59080
59763
  const selected = selection.selected;
59081
59764
  const venue = showVenue ? selected.venue : undefined;
59082
59765
  const pricing = selectedEventPricing(selected, props$1.showCost);
@@ -59095,7 +59778,6 @@ function SelectedEventDetailsView(props$1) {
59095
59778
  lookSlots: props$1.lookSlots,
59096
59779
  selection,
59097
59780
  selected,
59098
- locale,
59099
59781
  venue,
59100
59782
  showCapacity,
59101
59783
  teamMembers: selected.teamMembers ?? [],
@@ -59112,10 +59794,7 @@ function SelectedEventDetailsView(props$1) {
59112
59794
  renderVenueMap
59113
59795
  })]
59114
59796
  }),
59115
- showOtherDates ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OtherEventDates, {
59116
- selection,
59117
- locale
59118
- }) : null
59797
+ showOtherDates ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OtherEventDates, { selection }) : null
59119
59798
  ]
59120
59799
  });
59121
59800
  }
@@ -59128,7 +59807,6 @@ function LegacyEventDetailsView(props$1) {
59128
59807
  }
59129
59808
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectedEventDetailsView, {
59130
59809
  lookSlots: props$1.lookSlots,
59131
- locale: props$1.arrangement ? eventDateOptions(props$1.selection.selected).locale : undefined,
59132
59810
  selection: props$1.selection,
59133
59811
  showVenue: props$1.showVenue,
59134
59812
  showMap: props$1.showMap,
@@ -59229,7 +59907,7 @@ function EventDetailsHydration({ props: props$1, presentation, scopedEvents, sub
59229
59907
  }
59230
59908
  function EventDetailsSSR(props$1) {
59231
59909
  const { blockId, occurrenceContext = null, subject, events = null, className, children } = props$1;
59232
- const nowIso = new Date().toISOString();
59910
+ const nowIso = props$1.now;
59233
59911
  const normalizedSubject = normalizeEventRegistrationPageSurfaceSubject(subject);
59234
59912
  const scopedEvents = events === null ? null : resolveEventRegistrationScopedEvents(events, {
59235
59913
  occurrenceContext,
@@ -59620,7 +60298,7 @@ function offeringDateRow(session, event) {
59620
60298
  const status = getEventAvailabilityStatus(event);
59621
60299
  return {
59622
60300
  startsAt: session.startsAt,
59623
- label: `${date.weekday} ${date.day} ${date.month} · ${formatTime(session.startsAt, options)}`,
60301
+ label: `${date.weekday} ${date.day} ${date.month}, ${formatTime(session.startsAt, options)}`,
59624
60302
  places: capacity.available === null ? null : {
59625
60303
  label: capacity.isFull ? "Sold out" : `${capacity.available} place${capacity.available === 1 ? "" : "s"} left`,
59626
60304
  status: capacity.isFull ? "full" : status.status === "filling" ? "low" : "available"
@@ -59631,10 +60309,7 @@ function offeringDateRow(session, event) {
59631
60309
  //#endregion
59632
60310
  //#region ../blocks/src/system/runtime/nodes/offerings-preview-catalogue.tsx
59633
60311
  function OfferingsCatalogue(props$1) {
59634
- const input = props$1.renderInput ?? {
59635
- kind: "unprepared",
59636
- now: new Date()
59637
- };
60312
+ const input = resolveOfferingsRenderInput(props$1);
59638
60313
  const now = input.now;
59639
60314
  const state = input.kind === "prepared" ? input.listing : buildOfferingsCatalogueListing({
59640
60315
  now,
@@ -59643,15 +60318,15 @@ function OfferingsCatalogue(props$1) {
59643
60318
  events: props$1.catalogueEvents
59644
60319
  });
59645
60320
  switch (state.kind) {
59646
- case "no-groups": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CatalogueEmpty, {
60321
+ case "no-groups": return renderCatalogueEmpty({
59647
60322
  props: props$1,
59648
60323
  failed: false
59649
60324
  });
59650
- case "nothing-to-show": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CatalogueEmpty, {
60325
+ case "nothing-to-show": return renderCatalogueEmpty({
59651
60326
  props: props$1,
59652
60327
  failed: state.reason === "failed"
59653
60328
  });
59654
- case "by-date": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CatalogueMergedList, {
60329
+ case "by-date": return renderCatalogueMergedList({
59655
60330
  props: props$1,
59656
60331
  listing: state,
59657
60332
  now
@@ -59661,7 +60336,7 @@ function OfferingsCatalogue(props$1) {
59661
60336
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("header", {
59662
60337
  ...listLookSlot(props$1, "head"),
59663
60338
  children: props$1.children
59664
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OfferingsCatalogueGroups, {
60339
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: renderOfferingsCatalogueGroups({
59665
60340
  props: {
59666
60341
  ...props$1,
59667
60342
  now,
@@ -59669,7 +60344,7 @@ function OfferingsCatalogue(props$1) {
59669
60344
  },
59670
60345
  groups: state.groups
59671
60346
  }) })]
59672
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OfferingsCatalogueGroups, {
60347
+ }) : renderOfferingsCatalogueGroups({
59673
60348
  props: {
59674
60349
  ...props$1,
59675
60350
  now
@@ -59679,7 +60354,7 @@ function OfferingsCatalogue(props$1) {
59679
60354
  default: return assertNever(state);
59680
60355
  }
59681
60356
  }
59682
- function OfferingsCatalogueGroups({ props: props$1, groups }) {
60357
+ function renderOfferingsCatalogueGroups({ props: props$1, groups }) {
59683
60358
  const enabledSeriesIds = new Set(groups.flatMap((load) => load.kind === "items" ? load.items.map((item) => item.kind === "event" ? item.event.seriesId : item.run.eventSeriesId) : []));
59684
60359
  const quickDateEvents = selectQuickDateEvents((props$1.quickDateEvents ?? []).filter((event) => enabledSeriesIds.has(event.seriesId)));
59685
60360
  const populated = groups.flatMap(catalogueGroupItems);
@@ -59706,7 +60381,7 @@ function OfferingsCatalogueGroups({ props: props$1, groups }) {
59706
60381
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
59707
60382
  ...listLookSlot(props$1, "heading"),
59708
60383
  children: group.title
59709
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CatalogueItems, {
60384
+ }), renderCatalogueItems({
59710
60385
  props: props$1,
59711
60386
  items
59712
60387
  })]
@@ -59715,12 +60390,12 @@ function OfferingsCatalogueGroups({ props: props$1, groups }) {
59715
60390
  ]
59716
60391
  });
59717
60392
  }
59718
- function CatalogueItems({ props: props$1, items }) {
60393
+ function renderCatalogueItems({ props: props$1, items }) {
59719
60394
  const events = items.map((item) => item.kind === "event" ? item.event : item.run.representative);
59720
60395
  if (props$1.lookSlots) {
59721
60396
  const now = props$1.now;
59722
60397
  const dates = offeringDatesBySeries(props$1.quickDateEvents ?? [], now);
59723
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListItems, {
60398
+ return CardListItems({
59724
60399
  Carousel: props$1.Carousel,
59725
60400
  Title: "h4",
59726
60401
  presentation: props$1,
@@ -59748,7 +60423,7 @@ function catalogueCardContent(item, now, dates, typeLabel) {
59748
60423
  dates: event.bookingScope === "whole_series" ? offeringRunDates(event, now) : dates.get(event.seriesId)
59749
60424
  };
59750
60425
  }
59751
- function CatalogueEmpty({ props: props$1, failed }) {
60426
+ function renderCatalogueEmpty({ props: props$1, failed }) {
59752
60427
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
59753
60428
  className: "offerings-preview rb-flex rb-flex-col rb-gap-6",
59754
60429
  "data-block": "offerings-preview",
@@ -59759,7 +60434,7 @@ function CatalogueEmpty({ props: props$1, failed }) {
59759
60434
  })]
59760
60435
  });
59761
60436
  }
59762
- function CatalogueMergedList({ props: props$1, listing, now }) {
60437
+ function renderCatalogueMergedList({ props: props$1, listing, now }) {
59763
60438
  const dates = offeringDatesBySeries(props$1.quickDateEvents ?? [], now);
59764
60439
  const items = listing.items.map(({ item, group }) => catalogueCardContent(item, now, dates, group.singularLabel));
59765
60440
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -59767,7 +60442,7 @@ function CatalogueMergedList({ props: props$1, listing, now }) {
59767
60442
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("header", {
59768
60443
  ...listLookSlot(props$1, "head"),
59769
60444
  children: props$1.children
59770
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListItems, {
60445
+ }), CardListItems({
59771
60446
  Carousel: props$1.Carousel,
59772
60447
  carouselLabels: {
59773
60448
  item: "offering",
@@ -59856,20 +60531,17 @@ const OFFERINGS_PREVIEW_GRID_STYLE = {
59856
60531
  //#region ../blocks/src/system/runtime/nodes/offerings-preview.tsx
59857
60532
  const OFFERINGS_WHOLE_SERIES_CARD_CLASS = boxStyleClassNames({ role: "default" }, { baseClass: "group rb-flex rb-h-full rb-flex-col event-card offerings-preview-whole-series-card" });
59858
60533
  function OfferingsPreviewNode(props$1) {
59859
- if (props$1.displayMode === "catalogue") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OfferingsCatalogue, { ...props$1 });
60534
+ if (props$1.displayMode === "catalogue") return OfferingsCatalogue(props$1);
59860
60535
  return props$1.lookSlots ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
59861
60536
  ...listLookSlot(props$1, "layout"),
59862
60537
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("header", {
59863
60538
  ...listLookSlot(props$1, "head"),
59864
60539
  children: props$1.children
59865
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OfferingsPreview, { ...props$1 }) })]
59866
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [props$1.children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OfferingsPreview, { ...props$1 })] });
60540
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: renderOfferingsPreview(props$1) })]
60541
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [props$1.children, renderOfferingsPreview(props$1)] });
59867
60542
  }
59868
- function OfferingsPreview(props$1) {
59869
- const input = props$1.renderInput ?? {
59870
- kind: "unprepared",
59871
- now: new Date()
59872
- };
60543
+ function renderOfferingsPreview(props$1) {
60544
+ const input = resolveOfferingsRenderInput(props$1);
59873
60545
  const now = input.now;
59874
60546
  const listing = input.kind === "prepared" ? input.listing : buildLoadedOfferingsPreviewListing({
59875
60547
  ...props$1,
@@ -59885,7 +60557,7 @@ function OfferingsPreview(props$1) {
59885
60557
  case "nothing-to-show": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyState, { message: listingMessage(listing.reason, props$1) });
59886
60558
  case "by-date": {
59887
60559
  const dates = offeringDatesBySeries(props$1.quickDateEvents ?? [], now);
59888
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListItems, {
60560
+ return CardListItems({
59889
60561
  Carousel: props$1.Carousel,
59890
60562
  carouselLabels: {
59891
60563
  item: "offering",
@@ -59898,7 +60570,7 @@ function OfferingsPreview(props$1) {
59898
60570
  }, dates, group.singularLabel))
59899
60571
  });
59900
60572
  }
59901
- case "by-type": return props$1.lookSlots ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OfferingCardGroups, {
60573
+ case "by-type": return props$1.lookSlots ? renderOfferingCardGroups({
59902
60574
  ...props$1,
59903
60575
  states: listing.groups,
59904
60576
  now
@@ -59984,13 +60656,15 @@ function featuredSummaryDetails(item) {
59984
60656
  switch (item.kind) {
59985
60657
  case "event": return formatFeaturedSummary(deriveEventScheduleSummary(item.event), {
59986
60658
  venue: item.event.venue?.name ?? null,
59987
- siteTimeZone: item.event.siteTimeZone
60659
+ siteTimeZone: item.event.siteTimeZone,
60660
+ deliveryMode: item.event.deliveryMode
59988
60661
  });
59989
60662
  case "run": {
59990
60663
  const schedule = derivePreviewRunSchedule(item.run.run.sessions);
59991
60664
  return schedule.kind === "scheduled" ? formatFeaturedSummary(schedule.summary, {
59992
60665
  venue: item.run.representative.venue?.name ?? null,
59993
- siteTimeZone: item.run.representative.siteTimeZone
60666
+ siteTimeZone: item.run.representative.siteTimeZone,
60667
+ deliveryMode: item.run.representative.deliveryMode
59994
60668
  }) : null;
59995
60669
  }
59996
60670
  default: return assertNever(item, "Unhandled offerings preview item");
@@ -60029,7 +60703,8 @@ function detailsFromOccurrences(input) {
60029
60703
  const endDateTime = input.spanUsesEndDate ? first.endsAt : last?.startsAt ?? first.startsAt;
60030
60704
  const options = eventDateOptions({
60031
60705
  timeZone: first.timeZone,
60032
- siteTimeZone: place.siteTimeZone
60706
+ siteTimeZone: place.siteTimeZone,
60707
+ deliveryMode: place.deliveryMode
60033
60708
  });
60034
60709
  const summaryDate = (isoTimestamp, timeZone) => formatSummaryDate(isoTimestamp, {
60035
60710
  timeZone,
@@ -60040,20 +60715,23 @@ function detailsFromOccurrences(input) {
60040
60715
  startDateTime: first.startsAt,
60041
60716
  endDate: last ? summaryDate(endDateTime, last.timeZone) : null,
60042
60717
  endDateTime,
60043
- timeRange: formatEventTimeRange$1(first, options),
60718
+ timeRange: formatEventTimeRange(first, options),
60044
60719
  recurrence: input.recurrence ?? null,
60045
60720
  sessionCount: input.sessionCount ?? null,
60046
60721
  venue: place.venue
60047
60722
  };
60048
60723
  }
60049
60724
  function formatSummaryDate(isoTimestamp, options) {
60050
- return new Intl.DateTimeFormat(options.locale, {
60051
- timeZone: options.timeZone,
60052
- weekday: "long",
60053
- day: "numeric",
60054
- month: "long",
60055
- year: "numeric"
60056
- }).format(new Date(isoTimestamp));
60725
+ return formatDate({
60726
+ kind: "instant",
60727
+ value: toUtcIsoDateTimeString(isoTimestamp)
60728
+ }, {
60729
+ locale: options.locale,
60730
+ timeZone: resolveBookingBusinessTimeZone(options.timeZone)
60731
+ }, {
60732
+ date: "short",
60733
+ weekday: "long"
60734
+ });
60057
60735
  }
60058
60736
  function renderOfferingCompactItem(item, options) {
60059
60737
  switch (item.kind) {
@@ -60097,10 +60775,11 @@ function WholeSeriesRunCompactRow({ group, path: path$3, now }) {
60097
60775
  const { sessionCount: sessionsCount, firstSession } = schedule;
60098
60776
  const options = eventDateOptions({
60099
60777
  timeZone: firstSession.timeZone,
60100
- siteTimeZone: group.representative.siteTimeZone
60778
+ siteTimeZone: group.representative.siteTimeZone,
60779
+ deliveryMode: group.representative.deliveryMode
60101
60780
  });
60102
60781
  const dateRail = formatEventCompactDateRail(firstSession.startsAt, options);
60103
- const timeRange = formatEventTimeRange$1(firstSession, options);
60782
+ const timeRange = formatEventTimeRange(firstSession, options);
60104
60783
  const price$1 = formatEventPriceDisplay(group.representative.pricing);
60105
60784
  const statusLabel = resolveWholeSeriesBookingPhase(group.run, now).kind === "open" ? null : "Registration closed";
60106
60785
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", {
@@ -60182,10 +60861,11 @@ function WholeSeriesRunFeatureCard({ group, now, path: path$3, buttonVariant, bu
60182
60861
  const { firstSession } = schedule;
60183
60862
  const options = eventDateOptions({
60184
60863
  timeZone: firstSession.timeZone,
60185
- siteTimeZone: group.representative.siteTimeZone
60864
+ siteTimeZone: group.representative.siteTimeZone,
60865
+ deliveryMode: group.representative.deliveryMode
60186
60866
  });
60187
60867
  const formattedDate = formatEventCompactDateRail(firstSession.startsAt, options).label;
60188
- const timeRange = formatEventTimeRange$1(firstSession, options);
60868
+ const timeRange = formatEventTimeRange(firstSession, options);
60189
60869
  const price$1 = formatEventPriceDisplay(group.representative.pricing);
60190
60870
  const statusLabel = resolveWholeSeriesBookingPhase(group.run, now).kind === "open" ? null : "Registration closed";
60191
60871
  const description = group.representative.description;
@@ -60308,7 +60988,7 @@ function RunWithoutLiveDates({ group, path: path$3 }) {
60308
60988
  ]
60309
60989
  });
60310
60990
  }
60311
- function OfferingCardGroups(props$1) {
60991
+ function renderOfferingCardGroups(props$1) {
60312
60992
  const datesBySeries = offeringDatesBySeries(props$1.quickDateEvents ?? [], props$1.now);
60313
60993
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
60314
60994
  className: "rb-flex rb-flex-col rb-gap-10",
@@ -60375,7 +61055,7 @@ function nativeRunCardContent(item, content, props$1) {
60375
61055
  }
60376
61056
  function offeringGroupCards(state, props$1, dates) {
60377
61057
  switch (state.kind) {
60378
- case "items": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListItems, {
61058
+ case "items": return CardListItems({
60379
61059
  Carousel: props$1.Carousel,
60380
61060
  Title: "h4",
60381
61061
  presentation: props$1,
@@ -60737,6 +61417,17 @@ function ProductGalleryFrameView(props$1) {
60737
61417
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: renderCarousel(slides) });
60738
61418
  }
60739
61419
  function PassCardView(props$1) {
61420
+ if (props$1.presentation?.lookSlots) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventItem, {
61421
+ item: {
61422
+ ...passCardListContent(props$1.display),
61423
+ featured: props$1.presentation.featured
61424
+ },
61425
+ presentation: props$1.presentation,
61426
+ index: props$1.index ?? 0,
61427
+ Title: "h4",
61428
+ actionNode: props$1.action,
61429
+ feedback: props$1.feedback
61430
+ });
60740
61431
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", {
60741
61432
  className: "shop__card shop__card--pass",
60742
61433
  children: [
@@ -60787,6 +61478,16 @@ function PassPurchaseUnavailable(props$1) {
60787
61478
  });
60788
61479
  }
60789
61480
  function MembershipCardView(props$1) {
61481
+ if (props$1.presentation?.lookSlots) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventItem, {
61482
+ item: {
61483
+ ...membershipCardListContent(props$1.display),
61484
+ featured: props$1.presentation.featured
61485
+ },
61486
+ presentation: props$1.presentation,
61487
+ index: props$1.index ?? 0,
61488
+ Title: "h4",
61489
+ actionNode: props$1.action
61490
+ });
60790
61491
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", {
60791
61492
  className: "shop__card shop__card--membership",
60792
61493
  children: [
@@ -60811,7 +61512,7 @@ function MembershipCardView(props$1) {
60811
61512
  className: "shop__card-details",
60812
61513
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
60813
61514
  className: "shop__card-unlimited",
60814
- children: "Unlimited classes"
61515
+ children: membershipClassesLabel
60815
61516
  })
60816
61517
  }),
60817
61518
  props$1.action
@@ -60820,6 +61521,10 @@ function MembershipCardView(props$1) {
60820
61521
  }
60821
61522
  function PassesMembershipsSectionView(props$1) {
60822
61523
  const { display, beforeContent, passCards, membershipCards } = props$1;
61524
+ if (props$1.presentation?.lookSlots) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopCardListSections, {
61525
+ ...props$1,
61526
+ presentation: props$1.presentation
61527
+ });
60823
61528
  const productsClass = `shop__products shop__products--${display.layout}`;
60824
61529
  const gridStyle = display.state === "ready" ? { "--shop-columns": display.columns } : undefined;
60825
61530
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -60913,6 +61618,65 @@ function CheckoutShellView(props$1) {
60913
61618
  ]
60914
61619
  });
60915
61620
  }
61621
+ function ShopCardListSections(props$1) {
61622
+ const { display, presentation } = props$1;
61623
+ const groups = [{
61624
+ heading: display.passesHeading,
61625
+ items: display.passes,
61626
+ cards: props$1.passCards
61627
+ }, {
61628
+ heading: display.membershipsHeading,
61629
+ items: display.memberships,
61630
+ cards: props$1.membershipCards
61631
+ }];
61632
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
61633
+ id: "shop",
61634
+ "data-block": "shop",
61635
+ ...listLookSlot(presentation, "layout"),
61636
+ children: [
61637
+ props$1.beforeContent,
61638
+ display.state === "empty" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: display.emptyMessage }) : null,
61639
+ groups.filter(({ items }) => items.length > 0).map((group, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
61640
+ ...listLookSlot(presentation, "group"),
61641
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
61642
+ ...listLookSlot(presentation, "group-label"),
61643
+ children: group.heading
61644
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopCardListCollection, {
61645
+ presentation,
61646
+ Carousel: props$1.Carousel,
61647
+ items: group.items,
61648
+ cards: group.cards
61649
+ })]
61650
+ }, index))
61651
+ ]
61652
+ });
61653
+ }
61654
+ function ShopCardListCollection(props$1) {
61655
+ const cards = import_react.Children.toArray(props$1.cards);
61656
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListCollection, {
61657
+ presentation: props$1.presentation,
61658
+ Carousel: props$1.Carousel,
61659
+ labels: {
61660
+ item: "product",
61661
+ region: "Passes and memberships"
61662
+ },
61663
+ slides: props$1.items.map((item, index) => ({
61664
+ title: item.title,
61665
+ content: cards[index]
61666
+ }))
61667
+ });
61668
+ }
61669
+ function ShopPurchaseAction({ presentation, ...props$1 }) {
61670
+ if (presentation?.itemActionStyle !== "link") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedButton, { ...props$1 });
61671
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
61672
+ type: "button",
61673
+ className: "rb-item-text-link",
61674
+ onClick: props$1.onClick,
61675
+ disabled: props$1.disabled,
61676
+ "aria-disabled": props$1["aria-disabled"],
61677
+ children: props$1.label
61678
+ });
61679
+ }
60916
61680
 
60917
61681
  //#endregion
60918
61682
  //#region ../blocks/src/system/runtime/nodes/shop-commerce.server.tsx
@@ -61221,7 +61985,10 @@ function MonthEvent({ event, timeZone, locale }) {
61221
61985
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
61222
61986
  "data-slot": "time",
61223
61987
  children: formatTime(event.startsAt, {
61224
- timeZone,
61988
+ ...eventDateOptions({
61989
+ ...event,
61990
+ timeZone
61991
+ }),
61225
61992
  locale
61226
61993
  })
61227
61994
  }),
@@ -61233,10 +62000,16 @@ function MonthEvent({ event, timeZone, locale }) {
61233
62000
  ]
61234
62001
  });
61235
62002
  }
61236
- function dateLabel(key, timeZone) {
61237
- return formatDate(`${key}T12:00:00.000Z`, {
61238
- timeZone: "UTC",
61239
- locale: eventDateLocale(timeZone)
62003
+ function dateLabel(key, timeZone, date = "short") {
62004
+ return formatDate({
62005
+ kind: "civil",
62006
+ value: parseBookingLocalDate(key)
62007
+ }, eventDateOptions({
62008
+ siteTimeZone: timeZone,
62009
+ timeZone
62010
+ }), {
62011
+ date,
62012
+ weekday: "long"
61240
62013
  });
61241
62014
  }
61242
62015
  function MonthDay(props$1) {
@@ -61251,7 +62024,7 @@ function MonthDay(props$1) {
61251
62024
  children: [selection.arrangement === "month-day" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
61252
62025
  type: "button",
61253
62026
  "data-slot": "control",
61254
- "aria-label": `Show events on ${dateLabel(day.dateKey, selection.timeZone)}`,
62027
+ "aria-label": `Show events on ${dateLabel(day.dateKey, selection.timeZone, "long")}`,
61255
62028
  "aria-pressed": selected,
61256
62029
  onClick: controls ? () => controls.selectDay(day.dateKey) : undefined,
61257
62030
  children: day.date.day
@@ -61261,7 +62034,7 @@ function MonthDay(props$1) {
61261
62034
  }), selection.arrangement === "month-grid" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [day.visibleEntries.map(({ event, timeZone }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MonthEvent, {
61262
62035
  event,
61263
62036
  timeZone,
61264
- locale: eventDateLocale(selection.timeZone)
62037
+ locale: eventDateOptions({ siteTimeZone: selection.timeZone }).locale
61265
62038
  }, event.occurrenceId ?? event.id)), day.moreCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
61266
62039
  type: "button",
61267
62040
  className: "event-calendar-more",
@@ -61466,7 +62239,10 @@ function AgendaModalCard({ event, showCapacity, calendarTimeZone, onClick }) {
61466
62239
  className: "event-agenda-card-header",
61467
62240
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
61468
62241
  className: "event-agenda-card-time",
61469
- children: formatTime(event.startsAt, { timeZone })
62242
+ children: formatTime(event.startsAt, eventDateOptions({
62243
+ ...event,
62244
+ timeZone
62245
+ }))
61470
62246
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
61471
62247
  className: "event-agenda-card-title",
61472
62248
  children: event.title
@@ -61516,7 +62292,10 @@ function AgendaLinkCard({ event, showCapacity, showVenue, buttonVariant, buttonT
61516
62292
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("time", {
61517
62293
  className: "event-agenda-card-time",
61518
62294
  dateTime: event.startsAt,
61519
- children: formatTime(event.startsAt, { timeZone })
62295
+ children: formatTime(event.startsAt, eventDateOptions({
62296
+ ...event,
62297
+ timeZone
62298
+ }))
61520
62299
  }),
61521
62300
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
61522
62301
  className: "event-agenda-card-content",
@@ -61592,25 +62371,6 @@ var AgendaEventCard_default = AgendaEventCard;
61592
62371
 
61593
62372
  //#endregion
61594
62373
  //#region ../blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.tsx
61595
- /** Full day names for agenda headers */
61596
- const FULL_DAY_NAMES$1 = [
61597
- "Sunday",
61598
- "Monday",
61599
- "Tuesday",
61600
- "Wednesday",
61601
- "Thursday",
61602
- "Friday",
61603
- "Saturday"
61604
- ];
61605
- /**
61606
- * Format date for agenda day header
61607
- * @returns "Monday, January 13"
61608
- */
61609
- function formatAgendaDayHeader(date) {
61610
- const dayName = FULL_DAY_NAMES$1[getDayOfWeek(date)];
61611
- const monthName = MONTH_NAMES[date.month - 1];
61612
- return `${dayName ?? "Day"}, ${monthName ?? "Month"} ${date.day}`;
61613
- }
61614
62374
  /**
61615
62375
  * WeekAgendaView Component
61616
62376
  *
@@ -61633,7 +62393,14 @@ const WeekAgendaView = ({ days, events, showCapacity, showVenue = true, emptyMes
61633
62393
  className: "event-agenda-day-header",
61634
62394
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h4", {
61635
62395
  className: "event-agenda-day-title",
61636
- children: [formatAgendaDayHeader(date), today && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
62396
+ children: [formatDate({
62397
+ kind: "civil",
62398
+ value: date
62399
+ }, eventDateOptions({ siteTimeZone: calendarTimeZone }), {
62400
+ date: "short",
62401
+ weekday: "long",
62402
+ year: "omit"
62403
+ }), today && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
61637
62404
  className: "event-agenda-day-today-badge",
61638
62405
  children: "Today"
61639
62406
  })]
@@ -61734,15 +62501,10 @@ const LEGEND_ITEMS = [
61734
62501
  * "6:00 PM" + "7:00 PM" -> "6:00 – 7:00 PM"; "9:30 AM" + "10:15 AM" -> "9:30 – 10:15 AM".
61735
62502
  */
61736
62503
  function formatCompactTimeRange(event, calendarTimeZone) {
61737
- const timeZone = resolveEventTimeZone(event, calendarTimeZone);
61738
- const start = formatTime(event.startsAt, { timeZone });
61739
- const end = formatTime(event.endsAt, { timeZone });
61740
- const startMeridiem = start.slice(-2);
61741
- const endMeridiem = end.slice(-2);
61742
- if ((startMeridiem === "AM" || startMeridiem === "PM") && startMeridiem === endMeridiem) {
61743
- return `${start.slice(0, -3).trim()} – ${end}`;
61744
- }
61745
- return `${start} – ${end}`;
62504
+ return formatEventTimeRange(event, eventDateOptions({
62505
+ ...event,
62506
+ timeZone: resolveEventTimeZone(event, calendarTimeZone)
62507
+ }));
61746
62508
  }
61747
62509
  const WeekBoardCard = ({ event, showCapacity, calendarTimeZone }) => {
61748
62510
  const linkProps = resolveEventLinkProps(event);
@@ -61956,15 +62718,6 @@ var WeekBoardView_default = WeekBoardView;
61956
62718
  */
61957
62719
  /** Theme-default border colour for events without a category-supplied colour. */
61958
62720
  const DEFAULT_EVENT_CELL_COLOR = "rgb(var(--tb-accent))";
61959
- const FULL_DAY_NAMES = [
61960
- "Sunday",
61961
- "Monday",
61962
- "Tuesday",
61963
- "Wednesday",
61964
- "Thursday",
61965
- "Friday",
61966
- "Saturday"
61967
- ];
61968
62721
  function buildTimetableModel(input) {
61969
62722
  const calendarTimeZone = input.calendarTimeZone;
61970
62723
  const generic = buildTimetableModel$1({
@@ -61988,8 +62741,17 @@ function buildTimetableModel(input) {
61988
62741
  days: generic.days.map((day) => ({
61989
62742
  date: day.date,
61990
62743
  dateKey: day.dateKey,
61991
- label: formatDayLabel(day.date),
61992
- shortLabel: formatShortDayLabel(day.date),
62744
+ label: formatDayLabel(day.date, calendarTimeZone, {
62745
+ date: "short",
62746
+ weekday: "long",
62747
+ year: "omit"
62748
+ }),
62749
+ shortLabel: formatDayLabel(day.date, calendarTimeZone, {
62750
+ date: "omit",
62751
+ day: "numeric",
62752
+ weekday: "short",
62753
+ year: "omit"
62754
+ }),
61993
62755
  isToday: day.isToday,
61994
62756
  events: day.cells.map((cell) => toEventCell(cell, calendarTimeZone))
61995
62757
  }))
@@ -62007,7 +62769,10 @@ function toEventCell(cell, calendarTimeZone) {
62007
62769
  laneIndex: cell.laneIndex,
62008
62770
  laneCount: cell.laneCount,
62009
62771
  categoryColor: cell.color ?? DEFAULT_EVENT_CELL_COLOR,
62010
- timeLabel: formatEventTimeRange(event, calendarTimeZone),
62772
+ timeLabel: formatEventTimeRange(event, eventDateOptions({
62773
+ ...event,
62774
+ timeZone: resolveEventTimeZone(event, calendarTimeZone)
62775
+ })),
62011
62776
  teacherLine: formatEventTeacherLine(event),
62012
62777
  capacityLabel: capacity.label,
62013
62778
  isFull: capacity.isFull
@@ -62019,18 +62784,14 @@ function formatTimetableCapacity(event) {
62019
62784
  isFull: isEventFull(event)
62020
62785
  };
62021
62786
  }
62022
- function formatEventTimeRange(event, calendarTimeZone) {
62023
- const timeZone = resolveEventTimeZone(event, calendarTimeZone);
62024
- return `${formatTime(event.startsAt, { timeZone })} - ${formatTime(event.endsAt, { timeZone })}`;
62025
- }
62026
- function formatDayLabel(date) {
62027
- const dayName = FULL_DAY_NAMES[getDayOfWeek(date)] ?? "";
62028
- const monthName = MONTH_NAMES[date.month - 1] ?? "";
62029
- return `${dayName}, ${monthName} ${date.day}`;
62030
- }
62031
- function formatShortDayLabel(date) {
62032
- const dayName = FULL_DAY_NAMES[getDayOfWeek(date)] ?? "";
62033
- return `${dayName.slice(0, 3)} ${date.day}`;
62787
+ function formatDayLabel(date, timeZone, options) {
62788
+ return formatDate({
62789
+ kind: "civil",
62790
+ value: date
62791
+ }, eventDateOptions({
62792
+ siteTimeZone: timeZone,
62793
+ timeZone
62794
+ }), options);
62034
62795
  }
62035
62796
 
62036
62797
  //#endregion
@@ -62136,7 +62897,8 @@ function renderCalendarSsr(display, hydration) {
62136
62897
  const viewport = buildCalendarViewport({
62137
62898
  view: calendarView,
62138
62899
  anchorDate: viewDate,
62139
- startOfWeek: display.startOfWeek
62900
+ startOfWeek: display.startOfWeek,
62901
+ timeZone: hydration.calendarContext.timeZone
62140
62902
  });
62141
62903
  const eventsByDate = groupEventsByDate([...display.events], hydration.calendarContext.timeZone);
62142
62904
  const dayNames = getOrderedDayNames(display.startOfWeek);
@@ -62197,11 +62959,17 @@ function renderCalendarSsr(display, hydration) {
62197
62959
  children: [day.visibleEntries.map(({ event, timeZone }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("a", {
62198
62960
  ...resolveEventLinkProps(event),
62199
62961
  className: "event-calendar-event",
62200
- title: `${event.title} at ${formatTime(event.startsAt, { timeZone })}`,
62962
+ title: `${event.title} at ${formatTime(event.startsAt, eventDateOptions({
62963
+ ...event,
62964
+ timeZone
62965
+ }))}`,
62201
62966
  children: [
62202
62967
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
62203
62968
  className: "event-calendar-event-time",
62204
- children: formatTime(event.startsAt, { timeZone })
62969
+ children: formatTime(event.startsAt, eventDateOptions({
62970
+ ...event,
62971
+ timeZone
62972
+ }))
62205
62973
  }),
62206
62974
  " ",
62207
62975
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
@@ -62229,7 +62997,8 @@ function renderTimetableSsr(display, hydration) {
62229
62997
  const viewport = buildCalendarViewport({
62230
62998
  view: "week",
62231
62999
  anchorDate: viewDate,
62232
- startOfWeek: display.startOfWeek
63000
+ startOfWeek: display.startOfWeek,
63001
+ timeZone: hydration.calendarContext.timeZone
62233
63002
  });
62234
63003
  const model = buildTimetableModel({
62235
63004
  events: [...display.events],
@@ -62271,7 +63040,7 @@ function renderTimetableSsr(display, hydration) {
62271
63040
  children: model.hourTicks.map((hour) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
62272
63041
  className: "event-timetable-time-label",
62273
63042
  style: { top: `${hourToTickPercent(hour, model.hourTicks)}%` },
62274
- children: formatHourLabel(hour)
63043
+ children: formatEventHourLabel(hour, hydration.calendarContext.timeZone)
62275
63044
  }, hour))
62276
63045
  })]
62277
63046
  }), model.days.map((day) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
@@ -62346,7 +63115,7 @@ function renderWeekBoardSsr(display, hydration) {
62346
63115
  kind: "static",
62347
63116
  ...buildWeekBoardNavigationState({
62348
63117
  days,
62349
- title: formatCalendarTitle(today, "week", [...days])
63118
+ title: formatCalendarTitle(today, "week", [...days], hydration.calendarContext.timeZone)
62350
63119
  }, today)
62351
63120
  };
62352
63121
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WeekBoardView, { ...buildWeekBoardViewProps({
@@ -62355,11 +63124,6 @@ function renderWeekBoardSsr(display, hydration) {
62355
63124
  today
62356
63125
  }, days, navigation$1) });
62357
63126
  }
62358
- function formatHourLabel(hour) {
62359
- if (hour === 0) return "12 AM";
62360
- if (hour === 12) return "12 PM";
62361
- return hour > 12 ? `${hour - 12} PM` : `${hour} AM`;
62362
- }
62363
63127
  function hourToTickPercent(hour, hours$1) {
62364
63128
  const startHour = hours$1[0] ?? 0;
62365
63129
  const endHour = hours$1[hours$1.length - 1] ?? startHour + 1;
@@ -63528,36 +64292,56 @@ function ShopSSR({ blockId, siteId, apiBaseUrl, stage, children, checkoutEndpoin
63528
64292
  ...getIslandRootProps("shop", blockId),
63529
64293
  children: [
63530
64294
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IslandPropsScript, { value: islandProps }),
63531
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PassesMembershipsSectionView, {
63532
- display: render.display,
63533
- passCards: render.display.passes.map((pass) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PassCardView, {
63534
- display: pass,
63535
- action: pass.action.kind === "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PassPurchaseUnavailable, { label: pass.action.label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedButton, {
63536
- type: "button",
63537
- variant: render.hydration.buttonVariant,
63538
- size: "md",
63539
- label: pass.action.label,
63540
- disabled: true,
63541
- "aria-disabled": "true"
63542
- })
63543
- }, pass.id)),
63544
- membershipCards: render.display.memberships.map((membership) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MembershipCardView, {
63545
- display: membership,
63546
- action: membership.action.kind === "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PassPurchaseUnavailable, { label: membership.action.label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedButton, {
63547
- type: "button",
63548
- variant: render.hydration.buttonVariant,
63549
- size: "md",
63550
- label: membership.action.label,
63551
- disabled: true,
63552
- "aria-disabled": "true"
63553
- })
63554
- }, membership.id))
63555
- }),
64295
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopSsrProducts, { render }),
63556
64296
  children
63557
64297
  ]
63558
64298
  });
63559
64299
  }
63560
64300
  var shop_server_default = ShopSSR;
64301
+ function ShopSsrProducts({ render }) {
64302
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PassesMembershipsSectionView, {
64303
+ display: render.display,
64304
+ presentation: render.hydration.presentation,
64305
+ Carousel: CarouselNodeSSR,
64306
+ passCards: render.display.passes.map((pass, index) => {
64307
+ const presentation = shopProductPresentation(render.hydration, pass.id, index, render.display.passes.length);
64308
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PassCardView, {
64309
+ display: pass,
64310
+ index,
64311
+ presentation,
64312
+ action: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopSsrPurchaseAction, {
64313
+ action: pass.action,
64314
+ hydration: render.hydration,
64315
+ presentation
64316
+ })
64317
+ }, pass.id);
64318
+ }),
64319
+ membershipCards: render.display.memberships.map((membership, index) => {
64320
+ const presentation = shopProductPresentation(render.hydration, membership.id, index, render.display.memberships.length);
64321
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MembershipCardView, {
64322
+ display: membership,
64323
+ index,
64324
+ presentation,
64325
+ action: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopSsrPurchaseAction, {
64326
+ action: membership.action,
64327
+ hydration: render.hydration,
64328
+ presentation
64329
+ })
64330
+ }, membership.id);
64331
+ })
64332
+ });
64333
+ }
64334
+ function ShopSsrPurchaseAction({ action, hydration, presentation }) {
64335
+ return action.kind === "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PassPurchaseUnavailable, { label: action.label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopPurchaseAction, {
64336
+ presentation,
64337
+ type: "button",
64338
+ variant: hydration.buttonVariant,
64339
+ size: "md",
64340
+ label: action.label,
64341
+ disabled: true,
64342
+ "aria-disabled": "true"
64343
+ });
64344
+ }
63561
64345
 
63562
64346
  //#endregion
63563
64347
  //#region ../blocks/src/system/runtime/components/runtime-backed-blocks.ts
@@ -63639,6 +64423,7 @@ const serverComponentRegistry = {
63639
64423
  accordion: AccordionNodeSSR,
63640
64424
  accordionItem: AccordionItemNodeSSR,
63641
64425
  "appointment-services": appointment_services_default,
64426
+ "team-members": TeamMembersNode,
63642
64427
  "booking-form": booking_form_server_default,
63643
64428
  "event-details": event_details_server_default,
63644
64429
  "location-map": location_map_server_default,
@@ -63997,6 +64782,8 @@ function applyTransformsAndFallback(value, binding, context) {
63997
64782
  options: step.options
63998
64783
  })) ?? [], {
63999
64784
  theme: context.theme ?? defaultThemeTokens,
64785
+ dateContext: context.dateContext,
64786
+ now: context.now,
64000
64787
  proseHoverCandidate: context.proseHoverCandidate,
64001
64788
  stage,
64002
64789
  data: resolveTransformData(context, binding.from),
@@ -64799,6 +65586,7 @@ function resolveTree(node, options) {
64799
65586
  }
64800
65587
  const scopedContext$1 = {
64801
65588
  ...options,
65589
+ dateContext: options.dateContext,
64802
65590
  transforms: options.transforms ?? defaultTransforms,
64803
65591
  theme: options.theme ?? defaultThemeTokens,
64804
65592
  scopes: scopes$1
@@ -64849,7 +65637,7 @@ function arrangementPlaceholderFieldPath(from, context) {
64849
65637
  }
64850
65638
  /** Own the declarative-to-paired-slide boundary before invoking either renderer. */
64851
65639
  function resolvedCarouselProps(type, props$1, children, registry) {
64852
- if (type === "offerings-preview" || type === "appointment-services") return { Carousel: registry.carousel ?? serverComponentRegistry.carousel };
65640
+ if (type === "offerings-preview" || type === "appointment-services" || type === "team-members") return { Carousel: registry.carousel ?? serverComponentRegistry.carousel };
64853
65641
  return type === "carousel" && props$1.mode === "arrangement" ? { slides: declarativeCarouselSlides(children) } : {};
64854
65642
  }
64855
65643
 
@@ -65192,6 +65980,22 @@ function collectNestedPreviewFieldConstraintEntries(field, path$3) {
65192
65980
  default: return assertNever(field);
65193
65981
  }
65194
65982
  }
65983
+ /** Share the supplied snapshot with transforms and manifest bindings, including standalone components. */
65984
+ function suppliedBlockRuntime(props$1) {
65985
+ const candidate = props$1.viewModelOverrides?.$root;
65986
+ const root = isObjectRecord(candidate) ? candidate : {};
65987
+ const dateContext = props$1.dateContext ?? runtimeDateContext(props$1.viewModelOverrides ?? {});
65988
+ const now = root.now === undefined || root.now === props$1.now ? props$1.now : readRuntimeNow(root.now);
65989
+ return {
65990
+ dateContext,
65991
+ now,
65992
+ root: {
65993
+ dateContext,
65994
+ ...root,
65995
+ now
65996
+ }
65997
+ };
65998
+ }
65195
65999
  function prepareDefaultBlockRender(manifest, props$1) {
65196
66000
  if (!props$1.theme) {
65197
66001
  throw new Error("Block rendering requires theme tokens; none were provided to makeDefaultBlockComponent");
@@ -65204,6 +66008,7 @@ function prepareDefaultBlockRender(manifest, props$1) {
65204
66008
  if (!layout) {
65205
66009
  throw new Error(`Block manifest "${manifest.name}" requires a layout definition`);
65206
66010
  }
66011
+ const runtime = suppliedBlockRuntime(props$1);
65207
66012
  const prepared = prepareBlockRender({
65208
66013
  content,
65209
66014
  theme: props$1.theme,
@@ -65213,7 +66018,7 @@ function prepareDefaultBlockRender(manifest, props$1) {
65213
66018
  viewModelOverrides: {
65214
66019
  ...props$1.viewModelOverrides,
65215
66020
  $root: {
65216
- ...props$1.viewModelOverrides?.$root,
66021
+ ...runtime.root,
65217
66022
  effectiveListArrangementKey: props$1.effectiveListArrangementKey
65218
66023
  }
65219
66024
  }
@@ -65222,6 +66027,8 @@ function prepareDefaultBlockRender(manifest, props$1) {
65222
66027
  registry: props$1.registry,
65223
66028
  layout,
65224
66029
  ...prepared,
66030
+ dateContext: runtime.dateContext,
66031
+ now: runtime.now,
65225
66032
  fieldConstraints: buildPreviewFieldConstraints(manifest)
65226
66033
  };
65227
66034
  }
@@ -65232,6 +66039,8 @@ function makeDefaultBlockComponent(args) {
65232
66039
  const result = resolveTree(prepared.layout, {
65233
66040
  registry: prepared.registry,
65234
66041
  viewModel: prepared.viewModel,
66042
+ dateContext: prepared.dateContext,
66043
+ now: prepared.now,
65235
66044
  transforms: props$1.transforms,
65236
66045
  theme: prepared.themeForTransforms,
65237
66046
  proseHoverCandidate: prepared.proseHoverCandidate,
@@ -65350,6 +66159,8 @@ function renderBlock(manifest, content, options = {}) {
65350
66159
  const resolvedTheme = options.theme ?? defaultThemeTokens;
65351
66160
  const cleanKind = manifest.name.replace(/^block\./, "");
65352
66161
  const componentElement = import_react.createElement(Component, {
66162
+ now: options.now,
66163
+ dateContext: options.dateContext,
65353
66164
  content,
65354
66165
  data: options.data,
65355
66166
  registry: resolvedRegistry,
@@ -65384,6 +66195,11 @@ function renderBlock(manifest, content, options = {}) {
65384
66195
  ...styleGroup ? { "data-style-group": styleGroup } : {}
65385
66196
  }, componentElement);
65386
66197
  }
66198
+ /** Standalone blocks are a wire boundary; page roots already hold admitted context. */
66199
+ function runtimeDateContext(viewModel) {
66200
+ const root = isObjectRecord(viewModel.$root) ? viewModel.$root : {};
66201
+ return resolveDateRuntimeContext(typeof root.bookingTimeZone === "string" ? root.bookingTimeZone : undefined);
66202
+ }
65387
66203
 
65388
66204
  //#endregion
65389
66205
  //#region ../blocks/src/system/constants/blockKinds.ts
@@ -66328,7 +67144,7 @@ function planArrangementContent(blockKind, manifest, content, audience) {
66328
67144
  }
66329
67145
  /** Widget section hooks use the theme; hero background geometry never travels to list islands. */
66330
67146
  function arrangementContentSlots(blockKind) {
66331
- return Object.fromEntries(arrangementSlotNames.filter((slot) => slot !== "schedule-section" && slot !== "shop-section" && (slot !== "background" || blockKind === "block.hero")).map((slot) => [slot, slot]));
67147
+ return Object.fromEntries(arrangementSlotNames.filter((slot) => (!slot.startsWith("price-") || blockKind === "block.shop") && slot !== "schedule-section" && slot !== "shop-section" && (slot !== "background" || blockKind === "block.hero")).map((slot) => [slot, slot]));
66332
67148
  }
66333
67149
  /** C photo Looks render shape-specific crops without rewriting authored preferences. */
66334
67150
  function arrangementShapeContent(lookId, content) {
@@ -66366,12 +67182,13 @@ function arrangementPhotoContent(content, knobs) {
66366
67182
  }
66367
67183
  function arrangementContentFacts(content) {
66368
67184
  const photoCount = arrangementPhotoCount(content);
67185
+ const featured = typeof content.featured === "string" ? content.featured.trim().length > 0 : content.featured !== undefined && content.featured !== null;
66369
67186
  return {
66370
67187
  photoCount,
66371
67188
  facts: {
66372
67189
  photo: photoCount > 0,
66373
67190
  facts: Array.isArray(content.facts) && content.facts.length > 0,
66374
- featured: content.featured !== undefined && content.featured !== null,
67191
+ featured,
66375
67192
  outcome: typeof content.outcome === "string" && content.outcome.length > 0
66376
67193
  }
66377
67194
  };
@@ -66379,6 +67196,7 @@ function arrangementContentFacts(content) {
66379
67196
  /** The content stage owns media presence for every arrangement consumer. */
66380
67197
  function arrangementPhotoCount(content) {
66381
67198
  if (Array.isArray(content.cards)) return content.cards.filter((card) => isObjectRecord(card) && isObjectRecord(card.media)).length;
67199
+ if (Array.isArray(content.members)) return content.members.filter((member) => isObjectRecord(member) && isObjectRecord(member.photo)).length;
66382
67200
  if (Array.isArray(content.images)) return content.images.filter((item) => isObjectRecord(item) && isObjectRecord(item.image)).length;
66383
67201
  if (isObjectRecord(content.media)) return 1;
66384
67202
  const section$2 = isObjectRecord(content._sectionStyles) ? content._sectionStyles : {};
@@ -66743,7 +67561,7 @@ function isEntryIdentifierMap(value) {
66743
67561
  }
66744
67562
  return true;
66745
67563
  }
66746
- function buildTransformContext(vm) {
67564
+ function buildTransformContext(vm, dateContext, now) {
66747
67565
  const root = vm.$root;
66748
67566
  const stage = root && isPlainObject$1(root) && root.previewStage === "preview" ? "preview" : "published";
66749
67567
  const routes = root && isPlainObject$1(root) && isRouteMap(root.routes) ? root.routes : undefined;
@@ -66751,6 +67569,8 @@ function buildTransformContext(vm) {
66751
67569
  const entriesByIdentifier = root && isPlainObject$1(root) && isEntryIdentifierMap(root.entriesByIdentifier) ? root.entriesByIdentifier : undefined;
66752
67570
  return {
66753
67571
  theme: defaultThemeTokens,
67572
+ dateContext,
67573
+ now,
66754
67574
  stage,
66755
67575
  data: vm.content,
66756
67576
  routes,
@@ -66758,32 +67578,33 @@ function buildTransformContext(vm) {
66758
67578
  entriesByIdentifier
66759
67579
  };
66760
67580
  }
66761
- function resolveParamsBindings(input, vm, meta) {
67581
+ function resolveParamsBindings(input, vm, meta, dateContext = resolveDateRuntimeContext(typeof vm.$root?.bookingTimeZone === "string" ? vm.$root.bookingTimeZone : undefined), now = readRuntimeNow(vm.$root?.now)) {
67582
+ return resolveLoaderParam(input, {
67583
+ vm,
67584
+ meta,
67585
+ transforms: buildTransformContext(vm, dateContext, now)
67586
+ });
67587
+ }
67588
+ function resolveLoaderParam(input, runtime) {
66762
67589
  if (input === null || typeof input !== "object") return input;
66763
- if (Array.isArray(input)) return input.map((v$1) => resolveParamsBindings(v$1, vm, meta));
67590
+ if (Array.isArray(input)) return input.map((value) => resolveLoaderParam(value, runtime));
66764
67591
  const inputRecord = input;
66765
- const maybeBind = inputRecord.$bind;
66766
- if (maybeBind && typeof maybeBind === "object") {
66767
- try {
66768
- const binding = bindingSchema.parse(maybeBind);
66769
- const value = resolveBinding(binding.from, vm);
66770
- const resolved = value === undefined || value === null ? binding.fallback : runTransforms(value, binding.transforms, buildTransformContext(vm), defaultTransforms);
66771
- if (resolved === undefined || resolved === null || resolved === "") {
66772
- return binding.fallback;
66773
- }
66774
- return resolved;
66775
- } catch (err$3) {
66776
- if (shouldLogWarnings) {
66777
- console.warn(`bindings: failed to parse loader param binding for block "${meta?.blockKind ?? "unknown"}"`, err$3);
66778
- }
66779
- return undefined;
67592
+ const binding = inputRecord.$bind;
67593
+ if (binding && typeof binding === "object") return resolveBoundLoaderParam(binding, runtime);
67594
+ return Object.fromEntries(Object.entries(inputRecord).map(([key, value]) => [key, resolveLoaderParam(value, runtime)]));
67595
+ }
67596
+ function resolveBoundLoaderParam(input, runtime) {
67597
+ try {
67598
+ const binding = bindingSchema.parse(input);
67599
+ const value = resolveBinding(binding.from, runtime.vm);
67600
+ const resolved = value === undefined || value === null ? binding.fallback : runTransforms(value, binding.transforms, runtime.transforms, defaultTransforms);
67601
+ return resolved === undefined || resolved === null || resolved === "" ? binding.fallback : resolved;
67602
+ } catch (err$3) {
67603
+ if (shouldLogWarnings) {
67604
+ console.warn(`bindings: failed to parse loader param binding for block "${runtime.meta?.blockKind ?? "unknown"}"`, err$3);
66780
67605
  }
67606
+ return undefined;
66781
67607
  }
66782
- const out = {};
66783
- for (const [k$1, v$1] of Object.entries(input)) {
66784
- out[k$1] = resolveParamsBindings(v$1, vm, meta);
66785
- }
66786
- return out;
66787
67608
  }
66788
67609
  function resolveBinding(path$3, vm) {
66789
67610
  const segments = splitPath(path$3);
@@ -66905,6 +67726,7 @@ async function prefetchBlockData(page, context, options) {
66905
67726
  }
66906
67727
  async function prefetchBlockDataWithFailures(page, context, options) {
66907
67728
  const { apiClient, isValidEndpoint, onError, getCustomBlockLoaders, getBlockDefinition: getBlockDefinition$1 = getBlockDefinition, resolveParamsBindings: resolveParamsBindings$1 = resolveParamsBindings, shouldLoad } = options;
67729
+ const { now, dateContext } = admitLoaderDateRuntime(context);
66908
67730
  const results = {};
66909
67731
  const loaderFailures = {};
66910
67732
  const tasks = [];
@@ -66942,12 +67764,12 @@ async function prefetchBlockDataWithFailures(page, context, options) {
66942
67764
  continue;
66943
67765
  }
66944
67766
  if (typeof loader.when !== "undefined") {
66945
- const resolvedWhen = resolveParamsBindings$1(loader.when, vm, { blockKind: block.kind });
67767
+ const resolvedWhen = resolveParamsBindings$1(loader.when, vm, { blockKind: block.kind }, dateContext, now);
66946
67768
  if (resolvedWhen !== true) {
66947
67769
  continue;
66948
67770
  }
66949
67771
  }
66950
- const rawParams = resolveParamsBindings$1(loader.params, vm, { blockKind: block.kind });
67772
+ const rawParams = resolveParamsBindings$1(loader.params, vm, { blockKind: block.kind }, dateContext, now);
66951
67773
  const params = normalizeParams(rawParams);
66952
67774
  const endpoint$1 = loader.endpoint;
66953
67775
  if (shouldSkipResolvedLoader({
@@ -66993,6 +67815,13 @@ function plannedLoaderContent(block, definition$1, context) {
66993
67815
  if (!definition$1 || content._arrangementLooks === undefined) return content;
66994
67816
  return planArrangementContent(block.kind, definition$1.manifest, content, context.previewStage === "preview" ? "authoring" : "public");
66995
67817
  }
67818
+ function admitLoaderDateRuntime(context) {
67819
+ const root = context.root;
67820
+ return {
67821
+ now: root?.now === undefined ? toUtcIsoDateTimeString(new Date().toISOString()) : readRuntimeNow(root.now),
67822
+ dateContext: resolveDateRuntimeContext(typeof root?.bookingTimeZone === "string" ? root.bookingTimeZone : undefined)
67823
+ };
67824
+ }
66996
67825
 
66997
67826
  //#endregion
66998
67827
  //#region ../blocks/src/system/data/previewResolvedDataState.ts
@@ -67186,13 +68015,9 @@ function ensureSiteId(params, context) {
67186
68015
  * non-manual type would silently flip its default sort (all `manualOrder` values
67187
68016
  * are equal, so it would degrade to createdAt-asc) — a render regression.
67188
68017
  *
67189
- * The manual-ordering HONOR therefore lives server-side where the schema IS
67190
- * known: `listContentEntries` (content/entries/list.ts) applies `manualOrder ASC`
67191
- * as the default for manual-ordered types (plan 8.3). That is the path
67192
- * `block-loaders/content.ts` renders through. The embed/`listPublishedEntries`
67193
- * loader path (contentEntryLoaders) is not yet schema-aware for its default
67194
- * ordering; honoring manual order there for the default `'order'` value is a
67195
- * bounded follow-up (recorded in plan 8.3), not this change.
68018
+ * The content-type schema owns the default order. Both management lists and
68019
+ * public entry loaders resolve it with resolveContentEntryOrder; explicit
68020
+ * caller sorts still override that default.
67196
68021
  */
67197
68022
  function normalizeOrder(orderBy) {
67198
68023
  switch (orderBy) {
@@ -77224,7 +78049,7 @@ async function loadConfig(configPath, output) {
77224
78049
  return { errorMessage: "No config path provided." };
77225
78050
  }
77226
78051
  try {
77227
- const { loadConfigFile: loadConfigFile$1 } = await import("../load-config-ghkXBKim.mjs");
78052
+ const { loadConfigFile: loadConfigFile$1 } = await import("../load-config-Kg45qY-N.mjs");
77228
78053
  const rawConfig = await loadConfigFile$1(configPath);
77229
78054
  const parsed = riverbankSiteConfigSchema.safeParse(rawConfig);
77230
78055
  if (!parsed.success) {