riverbankcms 0.28.1 → 0.29.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 createEventSeriesRef, $n as isSiteScopedDirectImageStoragePath, $r as surfaceDecorationColors, $t as themeButtonSizeClassName, An as resolveCompiledSectionContextFromContent, Ar as asDesignBlockId, At as deriveSiteStyleBlockPolicy, B as eventCommandScheduleSchema, Bn as collectPortableMediaReferencesFromContent, Br as featureCardCompositionTreatmentValues, Bt as renderHeaderPaintTargetRules, C as sortRecord, Ci as surfaceInkDeclarations, Cn as sectionFactsForContent, Cr as parseSafeDownloadFilename, Ct as arrangementLookHooks, D as resolveEventSeriesWaitlistMapping, Di as parseBlockColorToken, Dn as applyLookPhotoTreatment, Dr as cardListKnobBlocks, Dt as resolveEffectiveArrangement, Ei as parseThemeColorToken, En as sectionShapeHook, Er as cardListFamilyBlocks, Et as readStoredMode, F as entryIdentifierForCommand, Fn as boxBackgroundSchema, Fr as defineBlockDesignCapability, Ft as parseGradientPresetId, G as require_react, Gn as rotateNormalizedPoint, Gr as testimonialsCompositionTreatmentValues, Gt as MQ_MD_DOWN, H as bookingPaymentCollectionOptionSchema, Hn as walkAndCollectImageAttrs, Hr as introCompositionTreatmentValues, I as pageIdentifierForBlockCommand, In as runtimeBoxBackgroundSchema, Ir as defineFrameCapability, It as themeSchema, J as createBlockRef, Jn as parseAspectRatioValue, Jr as visibleHeadingDecorationFamilyValues, Jt as getComponentShadowVar, Kn as normalizeWidthCandidates, Kr as tileTreatmentValues, Kt as headerVariantValues, L as pageIdentifierForCommand, Ln as siteChromeBackgroundOverrideOwner, Lr as defineNoBlockDesignCapability, Lt as generateHeaderScrollTreatmentStyles, M as eventManagementTiming, Mn as resolveSectionPhotoPlacement, Mt as compiledRuntimeStyleFieldNames, N as blockIdentifierForCommand, Nn as sectionForegroundBackground, Nr as asSiteStyleId, Nt as gradientToCss, O as mapEventCommandContent, Oi as __commonJSMin, On as deriveSectionGround, Or as arrangementModes, Ot as resolveModeArrangement, P as blockOrderIdentifiersForCommand, Pn as boxBackgroundOverlaySchema, Pr as assertNever$10, Pt as getGradientPreset, Q as createEventCategoryRef, Qn as getImageUrl, Qr as serializeSurfaceTree, Qt as themeButtonExtraClassesWithTrailingIcon, R as pagePathForCommand, Rn as stripStaleTransitionStyleGroup, Rr as eventListTreatmentValues, Rt as generateMobileOverlayBackdropFilterFix, S as venueManagementFields, Si as surfaceForegroundValue, Sn as composeBoundary, Sr as managementMediaLibraryUploadLimitForMimeType, St as arrangementAppearancePresets, Ti as PUBLIC_BUTTON_ROLES, Tn as sectionShapeFromContent, Tr as resolveEffectiveKnobs, Tt as arrangementManifestFromVariants, U as bookingPaymentTermsSchema, Un as deleteEnvSpecificKeys, Ur as makeBoundaryId, Vn as summarizePortableMediaReferences, Vr as headingDecorationColorTokenValues, W as require_jsx_runtime, Wn as looksLikeUuid, Wr as offeringsPreviewCompositionTreatmentValues, X as createCommandRef, Xn as getDirectImageUrlForRelativePath, Xr as parseSurfaceTree, Xt as generateDefaultButtonSystem, Y as createBlocksRef, Yn as resolveMediaTransformForAspect, Yr as parseResolvedSurface, Yt as ICON_CONFIG, Z as createContentEntryRef, Zn as getDirectImageUrlForSiteScopedPath, Zr as resolveSurface, Zt as parseThemeButtonVariantId, _i as TokenResolver, _t as readOpeningToneProjection, ai as borderColorStyle, an as normalizeCardRole, ar as mediaTypeDiscriminants, at as createVenueRef, b as isEventVisibility, bi as surfaceForegroundInputFromResolved, bn as headerRootPaint, bt as readHeaderNavFontSizePreference, c as customLinkSchema$1, ci as textColorStyle, cr as parseCanonicalMediaIdentifier, d as internalLinkSchema, di as selectLogoVariant, dt as heroPhotoPaintsGround, ei as isCssColorLiteral, en as themeButtonVariantClassNames, er as mediaSchema, et as createFooterRef, f as internalResolvedLinkSchema, fi as surfaceToneFromForegroundToken, fr as deriveVideoSlotPolicy, ft as effectiveHeaderNavLinkStyle, g as linkSchema, gi as getTextColorForBackground, gr as asRecord, gt as readOpeningForegroundProjection, h as isResolvedInternalLinkEntityType, hi as expandPalette, hn as generateNavLinkCSS, hr as getMimeType, ht as transparentHeaderForegroundInput, i as sdkEventUpsertOptionalFieldSchemas, ii as backgroundColorStyle, in as cardVariantRoleValues, ir as resolveManagedMediaReference, jn as resolveSectionBackground, jr as asLayoutVariantKey, jt as requireCuratedSiteStyle, ki as __toESM, kn as parseCanonicalRuntimeBackground, kt as resolvePublishedLook, l as entryLinkSchema$1, li as mapDrawingStyle, lr as parseMediaIdentifier, lt as resolveLookSurfacePresentation, mi as surfaceInkPublication, mn as buildTextContrastShadow, mt as headerLeafTextColor, ni as resolveThemeColorReferenceValue, nn as renderCssDeclarations, nr as runtimeMediaSchema, nt as createNavigationRef, o as classifySdkEventBookingOffer, oi as mergeStyles, or as mediaIdentifierSchema, ot as refSlug, p as internalRouteOnlyLinkSchema, pi as resolveSurfaceForeground, pn as footerForegroundCandidates, pr as resolveVideoSelectionIntentForMediaField, pt as headerForegroundCandidates, q as ianaTimezoneSchema, qn as isExplicitRectCrop, qr as toNonEmptyReadonlyArray, qt as getComponentBorderWidthPx, ri as resolveTokenOrCssColorValue, rn as cardRoleSchema, rr as buildRenderableMediaAssetIndex, rt as createPageRef, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, si as rgbColorValue, sr as buildMediaIdentifierFromFilename, t as classifySdkEventTiming, ti as resolveOnColorForThemeToken, tn as parseThemeForPersistence, tr as persistableMediaSchema, tt as createFormRef, u as externalLinkSchema$1, ui as opaqueSurfaceDeclarations, ut as selectItemSurfacePresentation, v as pageLinkSchema$1, vi as defaultThemeTokens, vr as parseRelativeStoragePath, w as stableStringify, wi as surfaceInkStyle, wn as sectionShapeForGround, wr as defineBlockDesignMetadata, wt as arrangementSlotNames, xi as surfaceForegroundStyle, xn as boundaryLayout, xr as classifyKnownMimeType, xt as storedSectionPhoto, yi as surfaceForegroundDeclarations, yn as headerNeedsPhotoShade, yr as FILE_SIZE_LIMITS, yt as mergeHeaderNavTypographyPreference, z as hasConfiguredBookingVertical, zn as themeV2CompilerOwnedFieldNames, zr as eyebrowTreatmentValues, zt as getHeaderPaintTargets } from "../sdk-event-upsert-4Dn75Ui9.mjs";
3
- import { $ as locationValueSchema, $n as parseEventSeriesId, $t as publicEventPricingDisplay, A as parseMailingListContentVersionId, Bt as getBookingInstalmentDepositRefundability, C as resolveWholeSeriesBookingPhase, Cn as parseSiteTermsVersionId, Ct as getLocalScheduleTime, D as parseManagementKey, Dn as parseDatedOfferingCapacityHoldAuditRef, Dt as resolveBookingBusinessTimeZone, E as exposeSecret, En as unsafeAsPassId, Et as parseLocalScheduleTime, Ft as isLiveOrderInstalmentPlanStatus, G as formatOpeningHoursRows, Gt as resolveFlexibleBalanceDueAt, H as DEFAULT_MAILING_LIST_TARGETING, Ht as getBookingInstalmentProtectedDepositAmountCents, I as DEFAULT_MAILING_LIST_PRIVACY_NOTE, In as unsafeAsPositiveByteCount, It as isSettleableFlexibleBalanceContractStatus, Jn as parseApiKeyId, Jt as parseEventPageSurfaceSubject, Kt as resolveFlexibleBalanceInitialPaymentCents, L as DEFAULT_MAILING_LIST_SUCCESS_MESSAGE, Lt as isSettleableOrderInstalmentPlanStatus, M as parseMailingListRuntimeContext, Mt as toUtcIsoDateTimeString, N as assessMailingListTargeting, O as slugify, On as parseDatedOfferingCapacityHoldId, Ot as resolveBookingTimeZoneId, Pn as unsafeAsMediaAssetId, Pt as validateBookingPaymentOptionKinds, Q as locationMapLinks, Qt as publicEventPricingCurrency, Rt as addIntervalPreservingMonthAnchor, S as orderedLiveRunSessions, Sn as parseCustomerId, St as getLocalScheduleDate, T as createSecret, Tt as parseBookingLocalDate, U as planMailingListChoicePresentation, Ut as omitZeroAmountFutureRows, V as parseMailingListPlacementTargeting, Vt as getBookingInstalmentPlanIntervalCount, W as isCanonicalRichTextDoc, Wn as unsafeAsCheckoutLineId, Wt as splitAmountWithRemainderInFirst, X as decideLocationSave, Xn as parseUserId, Xt as formatEventPricing, Yn as parseSiteId, Yt as formatBookingPriceDisplay, Zt as isPublicEventPricingOptionDisplayable, _ as WHOLE_SERIES_EVENT_PRESET_VALUES, _n as formatDurationUnitLabel, _t as buildBookingUtcDateWindow, ar as unsafeAsPricingOptionId, b as CANCELLED_SESSION_STATUS, bt as formatLocalScheduleTime, cn as parseNonNegativeCents, cr as blockInteractionContextSchema, ct as formatDate, dn as toNonNegativeCents, dt as formatDateTimeRange, en as publicEventPricingOptions, er as parseRetiredCourseId, et as normalizeLocationAddressText, f as resolveSiteVerticalBookingConfig, fn as unsafeAsNonNegativeCents, ft as formatTime$1, g as SURFACE_EVENT_PRESET_VALUES, gn as assertNever, gt as toScheduledBookingDateTime, hn as parseCurrencyCode, ht as resolveBookingDisplayContext, in as fromArray, ir as unsafeAsOccurrenceId, it as toLocationCoordinates, j as parseMailingListSignedSourceToken, jt as parseUtcIsoDateTimeString, k as parseMailingListBlockId, kt as toBookingTimeZone, ln as parsePositiveCents, lt as formatDateRange, m as EVENT_PRESET_VALUES, mn as formatCurrencyAmount, mt as inferSiteDateLocale, nn as resolveExactOrFreePriceDisplay, nt as projectPublicLocation, o as EVENT_PRESET_VOCABULARY, on as mapReadonlyNonEmptyArray, or as err, ot as mapDrawingSchema, pn as unsafeAsPositiveCents, pt as formatTimeRange, q as parseWeeklyOpeningHours, qt as deriveTotalTicketQuantity, rn as resolvePricingOptionsPriceDisplay, rt as publicLocationSchema$1, s as NAVIGATION_LOCATIONS, sn as isNonNegativeWholeCents$1, sr as ok, st as selectCategoryView, tn as publicEventPricingType, tr as unsafeAsEventInstanceId, tt as parseLocationValue, un as toCents, ut as formatDateTime, vt as compareLocalScheduleDates, w as resolveWholeSeriesRunLifecycle, wn as parseMembershipId, x as nextLiveRunSessionStartMs, xt as getBookingLocalDateKey, yn as unsafeAsDemoCapability, yt as formatLocalScheduleDate, z as parseFooterCompositionV1, zt as clampConfiguredAmount } from "../src-nzVzuMR1.mjs";
4
- import { $ as normalizeMediaValueToIdentifierOnly, $n as normalizeApiError, $t as formatDateShort, A as formatDiffDetail, An as createManagementClient, At as navigationIdentity, B as buildFieldChanges, Bn as planRepeaterGridColumnChange, Bt as preparePushConfigTarget, C as reportFromOutcomes, Cn as CliExit, Ct as fileExists, D as normalizeRemoteHomepageReference, Dn as isLocalConfigured, Dt as writeJsonFileAtomic, E as createEmptySyncResult, En as getEnvironmentDebugInfo, Et as writeJsonFile, F as calculatePullDiff, Fn as fieldSchema, Ft as navigationCommandItemToManagementInput, G as syncLocalMediaForPull, Gn as resolveRepeaterGridColumnsForBreakpoint, Gt as bindRequestTimeout, H as formatFieldPath, Hn as buildRepeaterGridLayout, Ht as createBearerAPIClient, I as buildJsonDiff, In as normalizeSelectSource, It as normalizeNavigationConfigItemForCommand, J as admitBinaryWriteDestination, Jn as typeBasedLayout, Jt as createCommandContext, K as applyLocalMediaPush, Kn as resolveRepeaterGridRowFieldId, Kt as confirmAction, L as hasPageOperations, Ln as parseFieldDefinition, Lt as applyPushConfigPlan, M as hasPendingChanges, Mn as PUBLIC_FILE_FIELD_KINDS, Mt as convertPulledFooterToLocal, N as findChangedFieldsWithValues, Nn as createSdkSelectField, Nt as convertPulledNavigationToLocal, O as calculateDiff, On as isRemoteConfigured, Ot as isMediaValue, P as formatHumanDiff, Pn as createSiteOptionSelectField, Pt as convertPulledPagesToLocal, Q as normalizeMediaIdentifierInput, Qn as extractApiErrorMessage, Qt as currentInvocationEnvelope, R as pageMetadataForUpsert, Rn as parseFieldDefinitions, Rt as createPushConfigCommand, S as planCliCommandBatchForBindings, Sn as transportSuggestion, St as ensureDir, T as executeResidualSettingsSyncPlan, Tn as getEnvPrefix, Tt as readJsonFile, U as revalidateLocalMediaPushPlan, Un as parseRepeaterGridInteger, Ut as deriveCmsApiUrlFromDashboardUrl, V as formatFieldChange, Vn as projectRepeaterGridLayoutForRendering, Vt as revalidatePushConfigPlan, Wn as resolveRepeaterGridColumnFieldId, Wt as mapEnvironmentExecutionOutcomesJson, X as writeBinaryFileAtomic, Xn as bindingSchema, Xt as createListCommand, Y as readStableBinaryFile, Yn as NodeSchema, Yt as createGetCommand, Z as sha256, Zn as ManagementApiError, Zt as createOutputForCommand, _ as commandExecutionToReport, _n as planEnvironmentWriteAuthorization, _t as metadataFilePath, a as executeEntryCommandPlanAsReport, an as parseJsonArray, ar as portablePageDesignSchema, at as readEntries, b as executeCompiledCliCommandPlanAsReport, bn as ErrorCodes, bt as writeMetadataFile, c as titleFromEntryData, cn as resolveCommandPath, cr as ApiEnvelopeError, ct as readEventsMeta, d as unsafeAsCliEntryIdentifier, dn as withConfirmation, dt as readNavigationMeta, en as getOutputContext, er as isRecord$2, et as walkLocalContentMedia, f as unsafeAsCliFormSlug, fn as withErrorHandling, ft as readPagesMeta, g as commandCompileFailureToReport, gn as planEnvironmentExecution, gt as ensureMetadataDir, h as mutationCounterForEffect, hn as parseEnvironmentSelection, ht as writeMediaManifest, i as entrySiteCommandTypes, in as mapEntryForOutput, ir as linkPayloadSchema, it as findLegacyEntryFiles, j as formatDiffSummary, jn as DEFAULT_MEDIA_FIELD_KINDS, jt as navigationIdentityMap, k as calculateFooterDiff, kn as loadSdkEnvironment, kt as duplicateNavigationIdentityMessage, l as unsafeAsCliBlockIdentifier, ln as runWithInvocation, lr as unwrapResponse, lt as readFooterMeta, m as commandReportToSyncResult, mn as executeEnvironmentPlan, mt as readVenuesMeta, n as entryCommandCompileErrorToReport, nn as handleCommandError, nr as sdkEventScheduleSchema, nt as downloadMedia, o as executeEntryCommandPlanAsSyncResult, on as parseJsonData, or as sdkConfiguredBookingVerticalConfigSchema, ot as readAllMeta, p as unsafeAsCliPageIdentifier, pn as withOutputOnlyErrorHandling, pt as readSettingsMeta, q as prepareLocalMediaForPush, qn as resolveRepeaterGridSpanFieldId, qt as createClientForEnv, r as entryCommandReportToSyncResult, rn as managementClientConfigFor, rr as sdkSettingsUpdateSchema, rt as extractMediaIdentifiers, s as syncResultForEntryCommandCompileError, sn as parsePaginationOptions, sr as API_ENDPOINTS, st as readEventCategoriesMeta, t as compileEntryPushCommands, tn as getOutputOnlyContext, tr as ApiRequestError, tt as buildStorageUrl, u as unsafeAsCliContentTypeKey, un as withCommandContext, ut as readFormsMeta, v as commandReportHasFailures, vn as requireEnvironmentWriteAuthorization, vt as metadataTargetFromEnvironment, w as mergeManySyncResults, wt as listFiles, x as formatManagementError, xn as formatError, xt as writeMetadataRecordMap, y as executeCliCommandPlan, yn as withRiverbankEnv, yt as readMetadataFile, zn as resolveIconPickerPreset, zt as loadPushConfigTarget } from "../entryCommands-CrfobcW7.mjs";
5
- import { r as CLI_VERSION, t as loadConfigFile } from "../load-config-DTbiJyCf.mjs";
6
- import { a as RichTextDocSchema, c as isObjectRecord, i as findUnfilledSentinels, l as isRecord$17, n as buildEntryScaffold, o as isRichTextDoc, r as validateEntryInstance, s as sanitizeHref, t as eventUpsertInputSchema } from "../eventUpsertInputSchema-R2GbOsj2.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-DJfyedxU.mjs";
2
+ import { $ as createEventSeriesRef, $r as surfaceToneFromForegroundToken, An as walkAndCollectImageAttrs, Ar as introCompositionTreatmentValues, At as resolvePublishedLook, B as eventCommandScheduleSchema, Bn as getImageUrl, Br as surfaceDecorationColors, C as sortRecord, Cn as boxBackgroundOverlaySchema, Cr as defineBlockDesignCapability, Ct as arrangementAppearancePresets, D as resolveEventSeriesWaitlistMapping, Dn as themeV2CompilerOwnedFieldNames, Dr as eyebrowTreatmentValues, Dt as readStoredMode, En as stripStaleTransitionStyleGroup, Er as eventListTreatmentValues, Et as arrangementManifestFromVariants, F as entryIdentifierForCommand, Fn as isExplicitRectCrop, Fr as toNonEmptyReadonlyArray, Ft as parseGradientPresetId, G as require_react, Gn as buildRenderableMediaAssetIndex, Gr as backgroundColorStyle, Gt as themeButtonExtraClassesWithTrailingIcon, H as bookingPaymentCollectionOptionSchema, Hn as mediaSchema, Hr as resolveOnColorForThemeToken, Ht as ICON_CONFIG, I as pageIdentifierForBlockCommand, In as parseAspectRatioValue, Ir as visibleHeadingDecorationFamilyValues, It as themeSchema, J as createBlockRef, Jn as mediaIdentifierSchema, Jr as rgbColorValue, Jt as parseThemeForPersistence, Kn as resolveManagedMediaReference, Kr as borderColorStyle, Kt as themeButtonSizeClassName, L as pageIdentifierForCommand, Ln as resolveMediaTransformForAspect, Lr as parseSurfaceTree, M as eventManagementTiming, Mn as looksLikeUuid, Mr as offeringsPreviewCompositionTreatmentValues, Mt as compiledRuntimeStyleFieldNames, N as blockIdentifierForCommand, Nn as rotateNormalizedPoint, Nr as testimonialsCompositionTreatmentValues, Nt as gradientToCss, O as mapEventCommandContent, On as collectPortableMediaReferencesFromContent, Or as featureCardCompositionTreatmentValues, Ot as resolveEffectiveArrangement, P as blockOrderIdentifiersForCommand, Pn as normalizeWidthCandidates, Pr as tileTreatmentValues, Pt as getGradientPreset, Q as createEventCategoryRef, Qr as selectLogoVariant, R as pagePathForCommand, Rn as getDirectImageUrlForRelativePath, Rr as resolveSurface, S as venueManagementFields, Sn as sectionSurfaceBase, Sr as assertNever$10, St as storedSectionPhoto, Tn as runtimeBoxBackgroundSchema, Tr as defineNoBlockDesignCapability, Tt as arrangementSlotNames, U as bookingPaymentTermsSchema, Un as persistableMediaSchema, Ur as resolveThemeColorReferenceValue, Ut as generateDefaultButtonSystem, Vn as isSiteScopedDirectImageStoragePath, Vr as isCssColorLiteral, Vt as headerVariantValues, W as require_jsx_runtime, Wn as runtimeMediaSchema, Wr as resolveTokenOrCssColorValue, Wt as parseThemeButtonVariantId, X as createCommandRef, Xn as parseCanonicalMediaIdentifier, Xr as mapDrawingStyle, Xt as cardVariantRoleValues, Y as createBlocksRef, Yn as buildMediaIdentifierFromFilename, Yr as textColorStyle, Yt as cardRoleSchema, Z as createContentEntryRef, Zn as parseMediaIdentifier, Zr as opaqueSurfaceDeclarations, Zt as normalizeCardRole, _n as parseCanonicalRuntimeBackground, _t as selectItemSurfacePresentation, ai as surfaceForegroundStyle, at as createVenueRef, b as isEventVisibility, bn as resolveSectionPhotoPlacement, bt as transparentHeaderForegroundInput, c as customLinkSchema$1, ci as parseThemeColorToken, cn as headerRootPaint, d as internalLinkSchema, di as __toESM, dn as sectionFactsForContent, dr as parseSafeDownloadFilename, dt as resolveFooterBottomBarPaint, ei as surfaceInkPublication, er as deriveVideoSlotPolicy, et as createFooterRef, f as internalResolvedLinkSchema, fn as sectionShapeForGround, fr as defineBlockDesignMetadata, ft as resolveFooterRootForeground, g as linkSchema, gn as deriveSectionGround, gr as arrangementModes, gt as resolveLookSurfacePresentation, h as isResolvedInternalLinkEntityType, hn as applyLookPhotoTreatment, hr as cardListKnobBlocks, ht as parseArrangementSlotSurface, i as sdkEventUpsertOptionalFieldSchemas, ii as surfaceForegroundInputFromResolved, ir as asRecord, jn as deleteEnvSpecificKeys, jr as makeBoundaryId, jt as deriveSiteStyleBlockPolicy, kn as summarizePortableMediaReferences, kr as headingDecorationColorTokenValues, kt as resolveModeArrangement, l as entryLinkSchema$1, li as parseBlockColorToken, ln as boundaryLayout, lr as classifyKnownMimeType, lt as mergeContentWithBlockStyleOverrides, mn as sectionShapeHook, mr as cardListFamilyBlocks, mt as lookPresentationOnPalette, ni as getTextColorForBackground, nt as createNavigationRef, o as classifySdkEventBookingOffer, oi as surfaceInkStyle, or as parseRelativeStoragePath, ot as refSlug, p as internalRouteOnlyLinkSchema, pn as sectionShapeFromContent, pr as resolveEffectiveKnobs, pt as generateDefaultAccordionSystem, q as ianaTimezoneSchema, qn as mediaTypeDiscriminants, qr as mergeStyles, qt as themeButtonVariantClassNames, ri as defaultThemeTokens, rr as getMimeType, rt as createPageRef, s as RESOLVED_INTERNAL_LINK_ENTITY_TYPES, si as PUBLIC_BUTTON_ROLES, sr as FILE_SIZE_LIMITS, t as classifySdkEventTiming, ti as expandPalette, tr as resolveVideoSelectionIntentForMediaField, tt as createFormRef, u as externalLinkSchema$1, ui as __commonJSMin, un as composeBoundary, ur as managementMediaLibraryUploadLimitForMimeType, ut as resolveHeaderRootForeground, v as pageLinkSchema$1, vn as resolveCompiledSectionContextFromContent, vr as asDesignBlockId, vt as heroPhotoPaintsGround, w as stableStringify, wn as boxBackgroundSchema, wr as defineFrameCapability, wt as arrangementLookHooks, xn as sectionForegroundBackground, xr as asSiteStyleId, yn as resolveSectionBackground, yr as asLayoutVariantKey, yt as headerLeafTextColor, z as hasConfiguredBookingVertical, zn as getDirectImageUrlForSiteScopedPath, zr as serializeSurfaceTree } from "../sdk-event-upsert-CXc13KPT.mjs";
3
+ import { $ as locationValueSchema, $n as parseEventSeriesId, $t as publicEventPricingDisplay, A as parseMailingListContentVersionId, Bt as getBookingInstalmentDepositRefundability, C as resolveWholeSeriesBookingPhase, Cn as parseSiteTermsVersionId, Ct as getLocalScheduleTime, D as parseManagementKey, Dn as parseDatedOfferingCapacityHoldAuditRef, Dt as resolveBookingBusinessTimeZone, E as exposeSecret, En as unsafeAsPassId, Et as parseLocalScheduleTime, Ft as isLiveOrderInstalmentPlanStatus, G as formatOpeningHoursRows, Gt as resolveFlexibleBalanceDueAt, H as DEFAULT_MAILING_LIST_TARGETING, Ht as getBookingInstalmentProtectedDepositAmountCents, I as DEFAULT_MAILING_LIST_PRIVACY_NOTE, In as unsafeAsPositiveByteCount, It as isSettleableFlexibleBalanceContractStatus, Jn as parseApiKeyId, Jt as parseEventPageSurfaceSubject, Kt as resolveFlexibleBalanceInitialPaymentCents, L as DEFAULT_MAILING_LIST_SUCCESS_MESSAGE, Lt as isSettleableOrderInstalmentPlanStatus, M as parseMailingListRuntimeContext, Mt as toUtcIsoDateTimeString, N as assessMailingListTargeting, O as slugify, On as parseDatedOfferingCapacityHoldId, Ot as resolveBookingTimeZoneId, Pn as unsafeAsMediaAssetId, Pt as validateBookingPaymentOptionKinds, Q as locationMapLinks, Qt as publicEventPricingCurrency, Rt as addIntervalPreservingMonthAnchor, S as orderedLiveRunSessions, Sn as parseCustomerId, St as getLocalScheduleDate, T as createSecret, Tt as parseBookingLocalDate, U as planMailingListChoicePresentation, Ut as omitZeroAmountFutureRows, V as parseMailingListPlacementTargeting, Vt as getBookingInstalmentPlanIntervalCount, W as isCanonicalRichTextDoc, Wn as unsafeAsCheckoutLineId, Wt as splitAmountWithRemainderInFirst, X as decideLocationSave, Xn as parseUserId, Xt as formatEventPricing, Yn as parseSiteId, Yt as formatBookingPriceDisplay, Zt as isPublicEventPricingOptionDisplayable, _ as WHOLE_SERIES_EVENT_PRESET_VALUES, _n as formatDurationUnitLabel, _t as buildBookingUtcDateWindow, ar as unsafeAsPricingOptionId, b as CANCELLED_SESSION_STATUS, bt as formatLocalScheduleTime, cn as parseNonNegativeCents, cr as blockInteractionContextSchema, ct as formatDate, dn as toNonNegativeCents, dt as formatDateTimeRange, en as publicEventPricingOptions, er as parseRetiredCourseId, et as normalizeLocationAddressText, f as resolveSiteVerticalBookingConfig, fn as unsafeAsNonNegativeCents, ft as formatTime$1, g as SURFACE_EVENT_PRESET_VALUES, gn as assertNever, gt as toScheduledBookingDateTime, hn as parseCurrencyCode, ht as resolveBookingDisplayContext, in as fromArray, ir as unsafeAsOccurrenceId, it as toLocationCoordinates, j as parseMailingListSignedSourceToken, jt as parseUtcIsoDateTimeString, k as parseMailingListBlockId, kt as toBookingTimeZone, ln as parsePositiveCents, lt as formatDateRange, m as EVENT_PRESET_VALUES, mn as formatCurrencyAmount, mt as inferSiteDateLocale, nn as resolveExactOrFreePriceDisplay, nt as projectPublicLocation, o as EVENT_PRESET_VOCABULARY, on as mapReadonlyNonEmptyArray, or as err, ot as mapDrawingSchema, pn as unsafeAsPositiveCents, pt as formatTimeRange, q as parseWeeklyOpeningHours, qt as deriveTotalTicketQuantity, rn as resolvePricingOptionsPriceDisplay, rt as publicLocationSchema$1, s as NAVIGATION_LOCATIONS, sn as isNonNegativeWholeCents$1, sr as ok, st as selectCategoryView, tn as publicEventPricingType, tr as unsafeAsEventInstanceId, tt as parseLocationValue, un as toCents, ut as formatDateTime, vt as compareLocalScheduleDates, w as resolveWholeSeriesRunLifecycle, wn as parseMembershipId, x as nextLiveRunSessionStartMs, xt as getBookingLocalDateKey, yn as unsafeAsDemoCapability, yt as formatLocalScheduleDate, z as parseFooterCompositionV1, zt as clampConfiguredAmount } from "../src-4CBCWovR.mjs";
4
+ import { $ as normalizeMediaValueToIdentifierOnly, $n as normalizeApiError, $t as formatDateShort, A as formatDiffDetail, An as createManagementClient, At as navigationIdentity, B as buildFieldChanges, Bn as planRepeaterGridColumnChange, Bt as preparePushConfigTarget, C as reportFromOutcomes, Cn as CliExit, Ct as fileExists, D as normalizeRemoteHomepageReference, Dn as isLocalConfigured, Dt as writeJsonFileAtomic, E as createEmptySyncResult, En as getEnvironmentDebugInfo, Et as writeJsonFile, F as calculatePullDiff, Fn as fieldSchema, Ft as navigationCommandItemToManagementInput, G as syncLocalMediaForPull, Gn as resolveRepeaterGridColumnsForBreakpoint, Gt as bindRequestTimeout, H as formatFieldPath, Hn as buildRepeaterGridLayout, Ht as createBearerAPIClient, I as buildJsonDiff, In as normalizeSelectSource, It as normalizeNavigationConfigItemForCommand, J as admitBinaryWriteDestination, Jn as typeBasedLayout, Jt as createCommandContext, K as applyLocalMediaPush, Kn as resolveRepeaterGridRowFieldId, Kt as confirmAction, L as hasPageOperations, Ln as parseFieldDefinition, Lt as applyPushConfigPlan, M as hasPendingChanges, Mn as PUBLIC_FILE_FIELD_KINDS, Mt as convertPulledFooterToLocal, N as findChangedFieldsWithValues, Nn as createSdkSelectField, Nt as convertPulledNavigationToLocal, O as calculateDiff, On as isRemoteConfigured, Ot as isMediaValue, P as formatHumanDiff, Pn as createSiteOptionSelectField, Pt as convertPulledPagesToLocal, Q as normalizeMediaIdentifierInput, Qn as extractApiErrorMessage, Qt as currentInvocationEnvelope, R as pageMetadataForUpsert, Rn as parseFieldDefinitions, Rt as createPushConfigCommand, S as planCliCommandBatchForBindings, Sn as transportSuggestion, St as ensureDir, T as executeResidualSettingsSyncPlan, Tn as getEnvPrefix, Tt as readJsonFile, U as revalidateLocalMediaPushPlan, Un as parseRepeaterGridInteger, Ut as deriveCmsApiUrlFromDashboardUrl, V as formatFieldChange, Vn as projectRepeaterGridLayoutForRendering, Vt as revalidatePushConfigPlan, Wn as resolveRepeaterGridColumnFieldId, Wt as mapEnvironmentExecutionOutcomesJson, X as writeBinaryFileAtomic, Xn as bindingSchema, Xt as createListCommand, Y as readStableBinaryFile, Yn as NodeSchema, Yt as createGetCommand, Z as sha256, Zn as ManagementApiError, Zt as createOutputForCommand, _ as commandExecutionToReport, _n as planEnvironmentWriteAuthorization, _t as metadataFilePath, a as executeEntryCommandPlanAsReport, an as parseJsonArray, ar as portablePageDesignSchema, at as readEntries, b as executeCompiledCliCommandPlanAsReport, bn as ErrorCodes, bt as writeMetadataFile, c as titleFromEntryData, cn as resolveCommandPath, cr as ApiEnvelopeError, ct as readEventsMeta, d as unsafeAsCliEntryIdentifier, dn as withConfirmation, dt as readNavigationMeta, en as getOutputContext, er as isRecord$2, et as walkLocalContentMedia, f as unsafeAsCliFormSlug, fn as withErrorHandling, ft as readPagesMeta, g as commandCompileFailureToReport, gn as planEnvironmentExecution, gt as ensureMetadataDir, h as mutationCounterForEffect, hn as parseEnvironmentSelection, ht as writeMediaManifest, i as entrySiteCommandTypes, in as mapEntryForOutput, ir as linkPayloadSchema, it as findLegacyEntryFiles, j as formatDiffSummary, jn as DEFAULT_MEDIA_FIELD_KINDS, jt as navigationIdentityMap, k as calculateFooterDiff, kn as loadSdkEnvironment, kt as duplicateNavigationIdentityMessage, l as unsafeAsCliBlockIdentifier, ln as runWithInvocation, lr as unwrapResponse, lt as readFooterMeta, m as commandReportToSyncResult, mn as executeEnvironmentPlan, mt as readVenuesMeta, n as entryCommandCompileErrorToReport, nn as handleCommandError, nr as sdkEventScheduleSchema, nt as downloadMedia, o as executeEntryCommandPlanAsSyncResult, on as parseJsonData, or as sdkConfiguredBookingVerticalConfigSchema, ot as readAllMeta, p as unsafeAsCliPageIdentifier, pn as withOutputOnlyErrorHandling, pt as readSettingsMeta, q as prepareLocalMediaForPush, qn as resolveRepeaterGridSpanFieldId, qt as createClientForEnv, r as entryCommandReportToSyncResult, rn as managementClientConfigFor, rr as sdkSettingsUpdateSchema, rt as extractMediaIdentifiers, s as syncResultForEntryCommandCompileError, sn as parsePaginationOptions, sr as API_ENDPOINTS, st as readEventCategoriesMeta, t as compileEntryPushCommands, tn as getOutputOnlyContext, tr as ApiRequestError, tt as buildStorageUrl, u as unsafeAsCliContentTypeKey, un as withCommandContext, ut as readFormsMeta, v as commandReportHasFailures, vn as requireEnvironmentWriteAuthorization, vt as metadataTargetFromEnvironment, w as mergeManySyncResults, wt as listFiles, x as formatManagementError, xn as formatError, xt as writeMetadataRecordMap, y as executeCliCommandPlan, yn as withRiverbankEnv, yt as readMetadataFile, zn as resolveIconPickerPreset, zt as loadPushConfigTarget } from "../entryCommands-5kz7nIFG.mjs";
5
+ import { r as CLI_VERSION, t as loadConfigFile } from "../load-config-JcidEUjc.mjs";
6
+ import { a as RichTextDocSchema, c as isObjectRecord, i as findUnfilledSentinels, l as isRecord$17, n as buildEntryScaffold, o as isRichTextDoc, r as validateEntryInstance, s as sanitizeHref, t as eventUpsertInputSchema } from "../eventUpsertInputSchema-DEN6k1oM.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-BppGb0gS.mjs";
8
8
  import { config } from "dotenv";
9
9
  import { Command, CommanderError } from "commander";
10
10
  import * as fs$2 from "fs/promises";
@@ -386,10 +386,8 @@ async function writeVenues(contentDir, pulledVenues, options) {
386
386
  description: venue.description,
387
387
  timezone: venue.timezone,
388
388
  capacity: venue.capacity,
389
- ...venue.locationValue !== undefined ? { locationValue: venue.locationValue } : {
390
- address: venue.address,
391
- location: venue.location
392
- },
389
+ ...venue.addressVisibility ? { addressVisibility: venue.addressVisibility } : {},
390
+ ...venue.locationValue !== undefined ? { locationValue: venue.locationValue } : {},
393
391
  mapEmbed: venue.mapEmbed
394
392
  });
395
393
  filePaths.push(filePath);
@@ -2148,10 +2146,8 @@ function mapLocalVenueToComparable(v$1) {
2148
2146
  description: v$1.description,
2149
2147
  timezone: v$1.timezone,
2150
2148
  capacity: v$1.capacity,
2151
- ...v$1.locationValue !== undefined ? { locationValue: v$1.locationValue } : {
2152
- address: v$1.address,
2153
- location: v$1.location
2154
- },
2149
+ ...v$1.addressVisibility ? { addressVisibility: v$1.addressVisibility } : {},
2150
+ ...v$1.locationValue !== undefined ? { locationValue: v$1.locationValue } : {},
2155
2151
  mapEmbed: v$1.mapEmbed
2156
2152
  };
2157
2153
  }
@@ -2200,7 +2196,6 @@ function mapEventRunToComparable(run) {
2200
2196
  waitlistMode: run.waitlistMode,
2201
2197
  bookingOffer: run.bookingOffer,
2202
2198
  venueRef: run.venueRef,
2203
- formRef: run.formRef,
2204
2199
  settings: run.settings,
2205
2200
  tags: run.tags
2206
2201
  };
@@ -4100,7 +4095,6 @@ function normalizeRemoteRun(run) {
4100
4095
  waitlistMode: nullishToNull(run.waitlistMode),
4101
4096
  bookingOffer: normalizeCanonicalBookingOffer(run.bookingOffer),
4102
4097
  venueRef: run.venueRef,
4103
- formRef: run.formRef,
4104
4098
  settings: run.settings,
4105
4099
  tags: [...run.tags]
4106
4100
  };
@@ -4122,7 +4116,6 @@ function normalizeLocalRun(local, remote) {
4122
4116
  waitlistMode: local.waitlistMode === undefined ? base.waitlistMode : local.waitlistMode,
4123
4117
  bookingOffer: overrideWhenDefined(local.bookingOffer, base.bookingOffer, normalizeCanonicalBookingOffer),
4124
4118
  venueRef: overrideWhenDefined(local.venueRef, base.venueRef, normalizeNullableString),
4125
- formRef: overrideWhenDefined(local.formRef, base.formRef, normalizeNullableString),
4126
4119
  settings: overrideWhenDefined(local.settings, base.settings, (value) => isRecord$26(value) ? value : {}),
4127
4120
  tags: overrideWhenDefined(local.tags, base.tags, (value) => Array.isArray(value) ? value : [])
4128
4121
  };
@@ -4161,7 +4154,6 @@ function normalizeNewLocalRun(run) {
4161
4154
  waitlistMode: nullishToNull(run.waitlistMode),
4162
4155
  bookingOffer: normalizeCanonicalBookingOffer(run.bookingOffer),
4163
4156
  venueRef: run.venueRef ?? null,
4164
- formRef: run.formRef ?? null,
4165
4157
  settings: run.settings ?? {},
4166
4158
  tags: run.tags ?? []
4167
4159
  };
@@ -4188,28 +4180,6 @@ function normalizeLocalEventGraph(graph, remote) {
4188
4180
  })
4189
4181
  };
4190
4182
  }
4191
- function normalizeVenueAddress(value) {
4192
- const obj = isRecord$26(value) ? value : {};
4193
- return {
4194
- line1: nullishToNull(typeof obj.line1 === "string" ? obj.line1 : undefined),
4195
- line2: nullishToNull(typeof obj.line2 === "string" ? obj.line2 : undefined),
4196
- city: nullishToNull(typeof obj.city === "string" ? obj.city : undefined),
4197
- region: nullishToNull(typeof obj.region === "string" ? obj.region : typeof obj.state === "string" ? obj.state : undefined),
4198
- postcode: nullishToNull(typeof obj.postcode === "string" ? obj.postcode : undefined),
4199
- country: nullishToNull(typeof obj.country === "string" ? obj.country : undefined)
4200
- };
4201
- }
4202
- function normalizeVenueLocation(value) {
4203
- if (!isRecord$26(value)) return null;
4204
- const obj = value;
4205
- const lat = typeof obj.lat === "number" ? obj.lat : null;
4206
- const lng = typeof obj.lng === "number" ? obj.lng : null;
4207
- if (lat === null && lng === null) return null;
4208
- return {
4209
- lat,
4210
- lng
4211
- };
4212
- }
4213
4183
  function normalizeVenueForCompare(venue) {
4214
4184
  const obj = isRecord$26(venue) ? venue : {};
4215
4185
  return {
@@ -4218,8 +4188,8 @@ function normalizeVenueForCompare(venue) {
4218
4188
  description: nullishToNull(typeof obj.description === "string" ? obj.description : undefined),
4219
4189
  timezone: nullishToNull(typeof obj.timezone === "string" ? obj.timezone : undefined),
4220
4190
  capacity: nullishToNull(typeof obj.capacity === "number" ? obj.capacity : undefined),
4221
- address: normalizeVenueAddress(obj.address),
4222
- location: normalizeVenueLocation(obj.location),
4191
+ ...obj.addressVisibility !== undefined ? { addressVisibility: obj.addressVisibility } : {},
4192
+ locationValue: obj.locationValue ?? null,
4223
4193
  mapEmbed: nullishToNull(typeof obj.mapEmbed === "string" ? obj.mapEmbed : undefined)
4224
4194
  };
4225
4195
  }
@@ -4770,46 +4740,21 @@ function venueCommand(venue) {
4770
4740
  if (!venue.timezone) {
4771
4741
  return unsupported(venue.slug, `Venue ${venue.slug} is missing timezone; command-backed venue pushes require an IANA timezone.`);
4772
4742
  }
4773
- const location = venueLocation(venue);
4774
- if (!location.ok) return location;
4775
4743
  return ok({
4776
4744
  type: "upsertVenue",
4777
4745
  ref: createVenueRef([venue.slug]),
4778
4746
  input: {
4747
+ ...venue.addressVisibility ? { addressVisibility: venue.addressVisibility } : {},
4779
4748
  ...venue.locationValue !== undefined ? { locationValue: venue.locationValue } : {},
4780
4749
  slug: venue.slug,
4781
4750
  title: venue.title,
4782
4751
  description: venue.description ?? undefined,
4783
4752
  timezone: venue.timezone,
4784
4753
  capacity: venue.capacity ?? undefined,
4785
- address: venueAddress(venue),
4786
- location: location.value,
4787
4754
  mapEmbedUrl: venue.mapEmbed ?? undefined
4788
4755
  }
4789
4756
  });
4790
4757
  }
4791
- function venueLocation(venue) {
4792
- if (!venue.location) return ok(undefined);
4793
- const { lat, lng } = venue.location;
4794
- if (typeof lat !== "number" || typeof lng !== "number") {
4795
- return unsupported(venue.slug, `Venue ${venue.slug} has an incomplete location; command-backed venue pushes require both lat and lng.`);
4796
- }
4797
- return ok({
4798
- latitude: lat,
4799
- longitude: lng
4800
- });
4801
- }
4802
- function venueAddress(venue) {
4803
- if (!venue.address) return undefined;
4804
- return {
4805
- line1: venue.address.line1 ?? undefined,
4806
- line2: venue.address.line2 ?? undefined,
4807
- city: venue.address.city ?? undefined,
4808
- region: venue.address.region ?? undefined,
4809
- postalCode: venue.address.postcode ?? undefined,
4810
- country: venue.address.country ?? undefined
4811
- };
4812
- }
4813
4758
  function eventCategoryCommand(category) {
4814
4759
  return {
4815
4760
  type: "upsertEventCategory",
@@ -4857,7 +4802,6 @@ function buildEventSeriesCommand(event, run, schedule, offer) {
4857
4802
  timezone: schedule.timeZone,
4858
4803
  venueIdentifier: run.venueRef ?? undefined,
4859
4804
  categoryIdentifier: event.categoryRef ?? undefined,
4860
- formIdentifier: run.formRef ?? undefined,
4861
4805
  bookingQuestions: run.bookingQuestions,
4862
4806
  capacity: eventCapacity(run.capacity),
4863
4807
  ...run.waitlistMode === undefined ? {} : { waitlist: { kind: run.waitlistMode === "on" ? "enabled_when_full" : run.waitlistMode === "off" ? "disabled" : "inherit" } },
@@ -4971,6 +4915,9 @@ async function executeDatedOfferingCommand(input, reported) {
4971
4915
  };
4972
4916
  }
4973
4917
  }
4918
+ function applicationWarnings(application) {
4919
+ return [...application.warnings ?? [], ..."warning" in application && application.warning ? [application.warning] : []];
4920
+ }
4974
4921
  /** Accepted cancellations remain applied writes, with their unfinished work intact. */
4975
4922
  function summarizeDatedOfferingExecution(input, executions) {
4976
4923
  const applications = executions.flatMap((execution) => execution.kind === "applied" ? [execution.application] : []);
@@ -4983,7 +4930,7 @@ function summarizeDatedOfferingExecution(input, executions) {
4983
4930
  error: execution.error
4984
4931
  }] : []);
4985
4932
  const unconfirmedEventUpdates = applications.flatMap((application) => "kind" in application ? [application] : []);
4986
- const eventWriteWarnings$1 = applications.flatMap((application) => application.warnings ?? []);
4933
+ const writeWarnings$1 = applications.flatMap(applicationWarnings);
4987
4934
  const cancellationResults = applications.flatMap((application) => application.cancellationResult ? [application.cancellationResult] : []);
4988
4935
  return {
4989
4936
  status: errors.length > 0 && created + updated$1 + deleted === 0 ? "failed" : "pushed",
@@ -4994,7 +4941,7 @@ function summarizeDatedOfferingExecution(input, executions) {
4994
4941
  deleted,
4995
4942
  failed: errors.length,
4996
4943
  errors,
4997
- ...eventWriteWarnings$1.length ? { eventWriteWarnings: eventWriteWarnings$1 } : {},
4944
+ ...writeWarnings$1.length ? { writeWarnings: writeWarnings$1 } : {},
4998
4945
  ...cancellationResults.length ? { cancellationResults } : {},
4999
4946
  ...unconfirmedEventUpdates.length ? { unconfirmedEventUpdates } : {}
5000
4947
  };
@@ -5114,7 +5061,6 @@ function eventRunUpsertInput(command, eventIdentifier) {
5114
5061
  bookingOffer: input.bookingOffer,
5115
5062
  attendeeMigration: input.attendeeMigration,
5116
5063
  venueRef: preferredRef(input.venueIdentifier, input.venueRef),
5117
- formRef: preferredRef(input.formIdentifier, input.formRef),
5118
5064
  settings: input.settings,
5119
5065
  tags: input.tags ? [...input.tags] : undefined
5120
5066
  };
@@ -5796,12 +5742,12 @@ function allGroupChildPlans(plan) {
5796
5742
  }
5797
5743
  function selectedGroupChildPlans(plan, value) {
5798
5744
  if (plan.childSelection.kind === "all") return plan.childSelection.fields;
5799
- if (!isPlainObject$7(value)) return [];
5745
+ if (!isPlainObject$6(value)) return [];
5800
5746
  const selectedType = value[plan.childSelection.activeTabField];
5801
5747
  return plan.childSelection.tabs.find((tab) => tab.typeId === selectedType)?.fields ?? [];
5802
5748
  }
5803
5749
  function canonicalizeObjectForWrite(plans, value) {
5804
- if (!isPlainObject$7(value)) return value;
5750
+ if (!isPlainObject$6(value)) return value;
5805
5751
  const canonical$1 = { ...value };
5806
5752
  for (const plan of plans) {
5807
5753
  if (!(plan.fieldId in canonical$1)) continue;
@@ -5827,7 +5773,7 @@ function canonicalizeFieldValue(plan, value) {
5827
5773
  }
5828
5774
  }
5829
5775
  function canonicalizeMedia(value) {
5830
- if (!isPlainObject$7(value)) return value;
5776
+ if (!isPlainObject$6(value)) return value;
5831
5777
  const canonical$1 = { ...value };
5832
5778
  if (mediaIdentifierSchema.safeParse(canonical$1.identifier).success) {
5833
5779
  deleteEnvSpecificKeys(canonical$1);
@@ -5835,15 +5781,15 @@ function canonicalizeMedia(value) {
5835
5781
  return canonical$1;
5836
5782
  }
5837
5783
  function canonicalizeRepeater(plan, value) {
5838
- if (!isUnknownArray$3(value)) return value;
5784
+ if (!isUnknownArray$2(value)) return value;
5839
5785
  return value.map((item) => {
5840
- if (!isPlainObject$7(item)) return item;
5786
+ if (!isPlainObject$6(item)) return item;
5841
5787
  const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
5842
5788
  return childPlans ? canonicalizeObjectForWrite(childPlans, item) : item;
5843
5789
  });
5844
5790
  }
5845
5791
  function canonicalizeDiscriminatedGroup(plan, value) {
5846
- if (!isPlainObject$7(value) || plan.childSelection.kind !== "discriminatedTabs") {
5792
+ if (!isPlainObject$6(value) || plan.childSelection.kind !== "discriminatedTabs") {
5847
5793
  return value;
5848
5794
  }
5849
5795
  const selection = plan.childSelection;
@@ -5860,14 +5806,14 @@ function rootFieldIds(plans) {
5860
5806
  return new Set(plans.map((child) => child.fieldId.split(".")[0] ?? child.fieldId));
5861
5807
  }
5862
5808
  function omitUndefined(value) {
5863
- if (isUnknownArray$3(value)) return value.map(omitUndefined);
5864
- if (!isPlainObject$7(value)) return value;
5809
+ if (isUnknownArray$2(value)) return value.map(omitUndefined);
5810
+ if (!isPlainObject$6(value)) return value;
5865
5811
  return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== undefined).map(([key, field]) => [key, omitUndefined(field)]));
5866
5812
  }
5867
- function isPlainObject$7(value) {
5813
+ function isPlainObject$6(value) {
5868
5814
  return typeof value === "object" && value !== null && !Array.isArray(value);
5869
5815
  }
5870
- function isUnknownArray$3(value) {
5816
+ function isUnknownArray$2(value) {
5871
5817
  return Array.isArray(value);
5872
5818
  }
5873
5819
 
@@ -7997,7 +7943,7 @@ function normalizeStoredObjectChildren(children, value, ctx) {
7997
7943
  return normalized;
7998
7944
  }
7999
7945
  function normalizeStoredRepeaterItems(plan, value, ctx) {
8000
- if (!isUnknownArray$2(value)) {
7946
+ if (!isUnknownArray$1(value)) {
8001
7947
  return value;
8002
7948
  }
8003
7949
  return value.map((item) => {
@@ -8061,7 +8007,7 @@ function getAtFieldPath(source, path$3) {
8061
8007
  }, source);
8062
8008
  }
8063
8009
  function normalizeRepeaterItems(plan, value, ctx) {
8064
- if (!isUnknownArray$2(value)) return value;
8010
+ if (!isUnknownArray$1(value)) return value;
8065
8011
  return value.map((item) => {
8066
8012
  if (!item || typeof item !== "object" || Array.isArray(item)) return item;
8067
8013
  const itemRecord = item;
@@ -8101,7 +8047,7 @@ function validateNormalizedFieldValue(plan, value, ctx) {
8101
8047
  }
8102
8048
  }
8103
8049
  function validateMediaPlan(plan, value, ctx) {
8104
- if (!isPlainObject$6(value)) return [issue(plan, "invalidMedia")];
8050
+ if (!isPlainObject$5(value)) return [issue(plan, "invalidMedia")];
8105
8051
  const allowedKind = value.type === undefined || typeof value.type === "string" && plan.allowedMediaTypes.includes(value.type);
8106
8052
  const admitted = ctx.mode === "render" ? runtimeMediaSchema.safeParse({
8107
8053
  ...value,
@@ -8246,7 +8192,7 @@ function selectRepeaterItemPlan(plan, item) {
8246
8192
  } : { kind: "untyped" };
8247
8193
  }
8248
8194
  function validateRepeaterItem(plan, item, index, ctx) {
8249
- if (!isPlainObject$6(item)) {
8195
+ if (!isPlainObject$5(item)) {
8250
8196
  return [{
8251
8197
  kind: "wrongType",
8252
8198
  path: [...plan.path, index],
@@ -8292,7 +8238,7 @@ function validateFlattenedGroupContainers(groupIds, item, plan, index, ctx) {
8292
8238
  }
8293
8239
  function validateGroupPlan(plan, value, ctx) {
8294
8240
  if (plan.valueContract === "location") return validateLocationGroupContract(plan, value, ctx);
8295
- if (!isPlainObject$6(value)) return [issue(plan, "wrongType", { expected: "an object" })];
8241
+ if (!isPlainObject$5(value)) return [issue(plan, "wrongType", { expected: "an object" })];
8296
8242
  if (plan.childSelection.kind === "discriminatedTabs") {
8297
8243
  const selectedType = value[plan.childSelection.activeTabField];
8298
8244
  if (selectedType !== undefined && selectedType !== null && !plan.childSelection.tabs.some((tab) => tab.typeId === selectedType)) {
@@ -8603,17 +8549,17 @@ function isEmptyValue$1(value) {
8603
8549
  if (isEmptyRichTextDoc(value)) return true;
8604
8550
  return false;
8605
8551
  }
8606
- function isPlainObject$6(value) {
8552
+ function isPlainObject$5(value) {
8607
8553
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
8608
8554
  }
8609
- function isUnknownArray$2(value) {
8555
+ function isUnknownArray$1(value) {
8610
8556
  return Array.isArray(value);
8611
8557
  }
8612
8558
  function isValidRichTextDoc(value) {
8613
8559
  return isRichTextDoc(value);
8614
8560
  }
8615
8561
  function isEmptyRichTextDoc(value) {
8616
- if (!isPlainObject$6(value)) return false;
8562
+ if (!isPlainObject$5(value)) return false;
8617
8563
  if (value.type !== "doc") return false;
8618
8564
  const content = value.content;
8619
8565
  if (!Array.isArray(content) || content.length === 0) return true;
@@ -9186,15 +9132,10 @@ function getDataKeys(data) {
9186
9132
  *
9187
9133
  * @param data - The block data to validate
9188
9134
  * @param manifest - The block manifest with field definitions
9189
- * @param customFields - Optional custom fields from config (blockFieldExtensions)
9190
9135
  * @returns Array of validation warnings for invalid content
9191
9136
  */
9192
- function validateBlockContent(data, manifest, customFields) {
9193
- const manifestWithCustomFields = customFields ? {
9194
- ...manifest,
9195
- fields: [...manifest.fields, ...customFields]
9196
- } : manifest;
9197
- const normalizedFields = parseFieldDefinitions([...manifestWithCustomFields.fields]);
9137
+ function validateBlockContent(data, manifest) {
9138
+ const normalizedFields = parseFieldDefinitions([...manifest.fields]);
9198
9139
  const admission = admitManifestContentForWrite(compileManifestContentContract(normalizedFields), data);
9199
9140
  if (admission.ok) return [];
9200
9141
  if (admission.error.kind === "non_portable_media") {
@@ -9225,12 +9166,11 @@ function validateBlockContent(data, manifest, customFields) {
9225
9166
  *
9226
9167
  * @param data - The block data to validate
9227
9168
  * @param manifest - The block manifest with field definitions
9228
- * @param customFields - Optional custom fields from config (blockFieldExtensions)
9229
9169
  */
9230
- function validateBlockData(data, manifest, customFields) {
9170
+ function validateBlockData(data, manifest) {
9231
9171
  const warnings = [];
9232
9172
  const errors = [];
9233
- const allFields = customFields ? [...manifest.fields, ...customFields] : manifest.fields;
9173
+ const allFields = manifest.fields;
9234
9174
  const validFieldIds = extractFieldIds(allFields);
9235
9175
  const requiredFieldIds = extractRequiredFieldIds(allFields);
9236
9176
  const dataKeys = getDataKeys(data);
@@ -9252,7 +9192,7 @@ function validateBlockData(data, manifest, customFields) {
9252
9192
  });
9253
9193
  }
9254
9194
  }
9255
- const contentWarnings = validateBlockContent(data, manifest, customFields);
9195
+ const contentWarnings = validateBlockContent(data, manifest);
9256
9196
  warnings.push(...contentWarnings);
9257
9197
  return {
9258
9198
  valid: errors.length === 0,
@@ -9275,13 +9215,6 @@ function formatValidationResult(pageIdentifier, blockIdentifier$1, blockKind, re
9275
9215
  return lines;
9276
9216
  }
9277
9217
 
9278
- //#endregion
9279
- //#region ../blocks/src/system/registryStore.ts
9280
- const REGISTRY_SYMBOL$1 = Symbol.for("@riverbankcms/blocks/system/definitions");
9281
- const isBlocksTestRuntime$1 = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.RIVERBANK_BLOCKS_TEST === "1";
9282
- const globalScope$1 = globalThis;
9283
- const blockStore = isBlocksTestRuntime$1 ? new Map() : globalScope$1[REGISTRY_SYMBOL$1] ??= new Map();
9284
-
9285
9218
  //#endregion
9286
9219
  //#region ../blocks/src/system/manifest/augmentManifest.ts
9287
9220
  /**
@@ -9407,6 +9340,166 @@ function augmentManifestWithStyleGroups(manifest, styleGroups, blockKind) {
9407
9340
  };
9408
9341
  }
9409
9342
 
9343
+ //#endregion
9344
+ //#region ../blocks/src/system/manifest/mergeManifestWithSdkOptions.ts
9345
+ /**
9346
+ * Merges SDK-provided field options AND field extensions into a block manifest.
9347
+ *
9348
+ * This creates a single source of truth for validation and rendering:
9349
+ * 1. Field options: Replaces select options for fields with `ui.widget: 'sdkSelect'`
9350
+ * 2. Field extensions: Appends additional fields to the end of the manifest
9351
+ *
9352
+ * Without this merge, the Zod validation schema is built from manifest's
9353
+ * hardcoded options, but SdkSelectField renders SDK options - causing
9354
+ * validation errors when users select SDK-provided values.
9355
+ *
9356
+ * @example
9357
+ * // Field options: Embed block manifest has options [list, grid, carousel]
9358
+ * // SDK config provides options [showcase, list]
9359
+ * // After merge, manifest.fields[layoutField].options = [showcase, list]
9360
+ *
9361
+ * @example
9362
+ * // Field extensions: Body text block has [body] field
9363
+ * // SDK config extends with [layout] field
9364
+ * // After merge, manifest.fields = [body, layout]
9365
+ */
9366
+ function mergeManifestWithSdkOptions(manifest, blockKind, sdkBlockFieldOptions, sdkBlockFieldExtensions) {
9367
+ const blockOptions = sdkBlockFieldOptions?.[blockKind];
9368
+ const extended = mergeFieldExtension(manifest, sdkBlockFieldExtensions?.[blockKind]);
9369
+ return blockOptions ? {
9370
+ ...extended,
9371
+ fields: extended.fields.map((field) => mergeFieldWithSdkOptions(field, blockOptions))
9372
+ } : extended;
9373
+ }
9374
+ /**
9375
+ * Recursively merges SDK options into a single field.
9376
+ * Handles nested fields in groups, repeaters, modals, and tab groups.
9377
+ */
9378
+ function mergeFieldWithSdkOptions(field, blockOptions) {
9379
+ const mergeFields = (fields$1) => fields$1.map((child) => mergeFieldWithSdkOptions(child, blockOptions));
9380
+ if (field.type === "select") return mergeSelectField(field, blockOptions);
9381
+ if (field.type === "group" || field.type === "modal") {
9382
+ return {
9383
+ ...field,
9384
+ schema: {
9385
+ ...field.schema,
9386
+ fields: mergeFields(field.schema.fields)
9387
+ }
9388
+ };
9389
+ }
9390
+ if (field.type === "repeater") {
9391
+ return {
9392
+ ...field,
9393
+ ...field.schema ? { schema: {
9394
+ ...field.schema,
9395
+ fields: mergeFields(field.schema.fields)
9396
+ } } : {},
9397
+ ...field.itemTypes ? { itemTypes: Object.fromEntries(Object.entries(field.itemTypes).map(([key, item]) => [key, {
9398
+ ...item,
9399
+ fields: mergeFields(item.fields)
9400
+ }])) } : {}
9401
+ };
9402
+ }
9403
+ if (field.type === "tabGroup") {
9404
+ return {
9405
+ ...field,
9406
+ tabs: field.tabs.map((tab) => ({
9407
+ ...tab,
9408
+ fields: mergeFields(tab.fields)
9409
+ }))
9410
+ };
9411
+ }
9412
+ return field;
9413
+ }
9414
+ /**
9415
+ * Merges SDK options into a select field if it uses the sdkSelect widget.
9416
+ * Returns the original field unchanged if it doesn't use sdkSelect or has no SDK options.
9417
+ */
9418
+ function mergeSelectField(field, blockOptions) {
9419
+ const ui = field.ui;
9420
+ if (ui?.widget !== "sdkSelect") return field;
9421
+ const sdkFieldConfig = blockOptions[field.id];
9422
+ if (!sdkFieldConfig?.options?.length) return field;
9423
+ return {
9424
+ ...field,
9425
+ options: sdkFieldConfig.options
9426
+ };
9427
+ }
9428
+ function mergeFieldExtension(manifest, extension) {
9429
+ if (!extension || !extension.fields.length && !extension.lookField && !extension.promotedFields?.length) return manifest;
9430
+ return {
9431
+ ...manifest,
9432
+ fields: [...manifest.fields, ...extension.fields],
9433
+ lookField: extension.lookField ?? manifest.lookField,
9434
+ promotedFields: [...manifest.promotedFields ?? [], ...extension.promotedFields ?? []]
9435
+ };
9436
+ }
9437
+ /** SDK declarations omit defaults that the compiled manifest field model requires. */
9438
+ function parseSdkBlockFieldExtensions(extensions) {
9439
+ return extensions ? Object.fromEntries(Object.entries(extensions).map(([kind, extension]) => [kind, {
9440
+ ...extension,
9441
+ fields: parseFieldDefinitions([...extension.fields])
9442
+ }])) : undefined;
9443
+ }
9444
+
9445
+ //#endregion
9446
+ //#region ../blocks/src/sdkBlockTransform.ts
9447
+ /**
9448
+ * Transforms an SDK custom block into a BlockManifest.
9449
+ *
9450
+ * If the block has a `layout`, it can be rendered declaratively without React code.
9451
+ * If no layout, the block MUST use blockOverrides for rendering.
9452
+ */
9453
+ function transformSdkBlockToManifest(sdkBlock) {
9454
+ const hasLayout = Boolean(sdkBlock.layout) || Boolean(sdkBlock.variants && Object.keys(sdkBlock.variants).length > 0);
9455
+ const manifest = {
9456
+ variantMeaning: { kind: "arrangement" },
9457
+ name: sdkBlock.id,
9458
+ version: "1.0.0",
9459
+ title: sdkBlock.title,
9460
+ titleSource: sdkBlock.titleSource,
9461
+ description: sdkBlock.description,
9462
+ component: sdkBlock.id,
9463
+ fields: sdkBlock.fields,
9464
+ slots: [],
9465
+ category: sdkBlock.category,
9466
+ tags: sdkBlock.tags,
9467
+ icon: sdkBlock.icon,
9468
+ layout: sdkBlock.layout,
9469
+ variants: sdkBlock.variants,
9470
+ defaultVariant: sdkBlock.defaultVariant,
9471
+ variantOptions: sdkBlock.variantOptions,
9472
+ promotedFields: sdkBlock.promotedFields,
9473
+ lookField: sdkBlock.lookField,
9474
+ behaviours: {
9475
+ supportsThemeSwitching: true,
9476
+ inlineEditing: hasLayout,
9477
+ animation: hasLayout,
9478
+ paletteHidden: false
9479
+ }
9480
+ };
9481
+ return augmentManifest(manifest);
9482
+ }
9483
+ /**
9484
+ * Transforms an SDK custom block into a SystemBlockDefinition.
9485
+ *
9486
+ * This creates a minimal definition with just the manifest - no data loaders
9487
+ * or content normalizers since SDK blocks handle their own rendering.
9488
+ */
9489
+ function transformSdkBlockToDefinition(sdkBlock) {
9490
+ return {
9491
+ manifest: transformSdkBlockToManifest(sdkBlock),
9492
+ dataLoaders: sdkBlock.dataLoaders
9493
+ };
9494
+ }
9495
+
9496
+ //#endregion
9497
+ //#region ../blocks/src/system/registryStore.ts
9498
+ const REGISTRY_SYMBOL$1 = Symbol.for("@riverbankcms/blocks/system/definitions");
9499
+ const isBlocksTestRuntime$1 = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.RIVERBANK_BLOCKS_TEST === "1";
9500
+ const globalScope$1 = globalThis;
9501
+ const blockStore = isBlocksTestRuntime$1 ? new Map() : globalScope$1[REGISTRY_SYMBOL$1] ??= new Map();
9502
+
9410
9503
  //#endregion
9411
9504
  //#region ../blocks/src/system/manifest/registry.ts
9412
9505
  const REGISTRY_SYMBOL = Symbol.for("@riverbankcms/blocks/manifest-registry");
@@ -11703,818 +11796,6 @@ function footerNav(opts) {
11703
11796
  return buildFooterNavAll(common);
11704
11797
  }
11705
11798
 
11706
- //#endregion
11707
- //#region ../theme-core/src/responsive/mediaQuery.ts
11708
- /**
11709
- * Convert familiar viewport media queries to container queries when safe.
11710
- * v1 supports simple width-based min/max clauses joined by `and`.
11711
- */
11712
- function convertMediaQueryToContainerQuery(query) {
11713
- const normalized = normalizeMediaQueryPrelude(query);
11714
- if (!normalized) return null;
11715
- if (normalized.includes(",")) return null;
11716
- if (/\bor\b/i.test(normalized)) return null;
11717
- const clauses = splitTopLevelAnd(normalized);
11718
- if (clauses.length === 0) return null;
11719
- const convertedClauses = clauses.map(convertWidthClause);
11720
- if (convertedClauses.some((clause) => clause === null)) return null;
11721
- return convertedClauses.join(" and ");
11722
- }
11723
- function normalizeMediaQueryPrelude(query) {
11724
- let normalized = query.trim();
11725
- if (!normalized) return "";
11726
- normalized = normalized.replace(/^only\s+/i, "").trim();
11727
- if (/^screen\b/i.test(normalized)) {
11728
- normalized = normalized.replace(/^screen\b/i, "").trim();
11729
- normalized = normalized.replace(/^and\b/i, "").trim();
11730
- }
11731
- if (/^all\b/i.test(normalized)) {
11732
- normalized = normalized.replace(/^all\b/i, "").trim();
11733
- normalized = normalized.replace(/^and\b/i, "").trim();
11734
- }
11735
- return normalized;
11736
- }
11737
- function splitTopLevelAnd(input) {
11738
- const parts = [];
11739
- let depth = 0;
11740
- let quote = null;
11741
- let current = "";
11742
- for (let i$1 = 0; i$1 < input.length; i$1++) {
11743
- const ch = input[i$1];
11744
- if (quote) {
11745
- current += ch;
11746
- if (ch === quote && input[i$1 - 1] !== "\\") {
11747
- quote = null;
11748
- }
11749
- continue;
11750
- }
11751
- if (ch === "\"" || ch === "'") {
11752
- quote = ch;
11753
- current += ch;
11754
- continue;
11755
- }
11756
- if (ch === "(") {
11757
- depth++;
11758
- current += ch;
11759
- continue;
11760
- }
11761
- if (ch === ")") {
11762
- depth = Math.max(0, depth - 1);
11763
- current += ch;
11764
- continue;
11765
- }
11766
- if (depth === 0 && isAndBoundary(input, i$1)) {
11767
- const trimmed$1 = current.trim();
11768
- if (trimmed$1) parts.push(trimmed$1);
11769
- current = "";
11770
- i$1 += 2;
11771
- continue;
11772
- }
11773
- current += ch;
11774
- }
11775
- const trimmed = current.trim();
11776
- if (trimmed) parts.push(trimmed);
11777
- return parts;
11778
- }
11779
- function isAndBoundary(input, index) {
11780
- if (!input.slice(index, index + 3).match(/^and$/i)) return false;
11781
- const prev = input[index - 1] ?? " ";
11782
- const next = input[index + 3] ?? " ";
11783
- return /\s/.test(prev) && /\s/.test(next);
11784
- }
11785
- function convertWidthClause(clause) {
11786
- const match = clause.match(/^\(\s*(min|max)-width\s*:\s*([^\)]+?)\s*\)$/i);
11787
- if (!match) return null;
11788
- const kindRaw = match[1];
11789
- const valueRaw = match[2];
11790
- if (!kindRaw || !valueRaw) return null;
11791
- const kind = kindRaw.toLowerCase();
11792
- const value = valueRaw.trim();
11793
- if (!value) return null;
11794
- return `(${kind}-width: ${value})`;
11795
- }
11796
-
11797
- //#endregion
11798
- //#region ../theme-core/src/responsiveMode.ts
11799
- const DEFAULT_THEME_RESPONSIVE_MODE = "viewport";
11800
- const THEME_RESPONSIVE_CONTAINER_NAME = "rb-site";
11801
- const THEME_RESPONSIVE_MODE_ATTR = "data-rb-responsive-mode";
11802
- function getResponsiveScopeSpec(mode = DEFAULT_THEME_RESPONSIVE_MODE) {
11803
- if (mode === "container") {
11804
- return {
11805
- kind: "container",
11806
- selector: `[${THEME_RESPONSIVE_MODE_ATTR}="container"]`,
11807
- containerName: THEME_RESPONSIVE_CONTAINER_NAME
11808
- };
11809
- }
11810
- return {
11811
- kind: "viewport",
11812
- selector: `[${THEME_RESPONSIVE_MODE_ATTR}="viewport"]`
11813
- };
11814
- }
11815
- function getContainerResponsiveScopeSpec() {
11816
- return {
11817
- kind: "container",
11818
- selector: `[${THEME_RESPONSIVE_MODE_ATTR}="container"]`,
11819
- containerName: THEME_RESPONSIVE_CONTAINER_NAME
11820
- };
11821
- }
11822
- function buildResponsiveScopeRule(spec, css) {
11823
- return `${buildResponsiveScopePrelude(spec)}\n${css.trim()}\n}`;
11824
- }
11825
- function buildResponsiveScopePrelude(spec) {
11826
- return `@scope (${spec.selector}) to (:scope [${THEME_RESPONSIVE_MODE_ATTR}]) {`;
11827
- }
11828
- function escapeRegExp(value) {
11829
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11830
- }
11831
- const RESPONSIVE_SCOPE_RULE_PATTERN = new RegExp(buildResponsiveScopeRulePatternSource(), "g");
11832
- const RESPONSIVE_SCOPE_RULE_AT_PATTERN = new RegExp(buildResponsiveScopeRulePatternSource(), "y");
11833
- function buildResponsiveScopeRulePatternSource() {
11834
- return [
11835
- "@scope",
11836
- "\\(\\[",
11837
- escapeRegExp(THEME_RESPONSIVE_MODE_ATTR),
11838
- "=\"(?:viewport|container)\"\\]\\)",
11839
- "to",
11840
- "\\((?::scope )?\\[",
11841
- escapeRegExp(THEME_RESPONSIVE_MODE_ATTR),
11842
- "\\]\\)",
11843
- "\\{"
11844
- ].join("\\s*");
11845
- }
11846
- function findResponsiveScopeRuleAt(css, index) {
11847
- RESPONSIVE_SCOPE_RULE_AT_PATTERN.lastIndex = index;
11848
- const match = RESPONSIVE_SCOPE_RULE_AT_PATTERN.exec(css);
11849
- if (!match) {
11850
- return null;
11851
- }
11852
- return { openBraceIndex: RESPONSIVE_SCOPE_RULE_AT_PATTERN.lastIndex - 1 };
11853
- }
11854
- function hasResponsiveScopeRule(css) {
11855
- RESPONSIVE_SCOPE_RULE_PATTERN.lastIndex = 0;
11856
- return RESPONSIVE_SCOPE_RULE_PATTERN.test(css);
11857
- }
11858
- function retargetResponsiveScopeRules(css, spec) {
11859
- RESPONSIVE_SCOPE_RULE_PATTERN.lastIndex = 0;
11860
- return css.replace(RESPONSIVE_SCOPE_RULE_PATTERN, buildResponsiveScopePrelude(spec));
11861
- }
11862
-
11863
- //#endregion
11864
- //#region ../theme-core/src/header/navDropdownSizing.ts
11865
- const NAV_DROPDOWN_MIN_WIDTH_REM = 12;
11866
- const NAV_DROPDOWN_MAX_WIDTH_REM = 22;
11867
- const NAV_DROPDOWN_VIEWPORT_GUTTER_REM = 1;
11868
- const NAV_DROPDOWN_VIEWPORT_GUTTER_PX = 16;
11869
- const NAV_DROPDOWN_SHIFT_PROPERTY = "--nav-dropdown-shift-x";
11870
- const NAV_DROPDOWN_MAX_WIDTH_CSS = `min(${NAV_DROPDOWN_MAX_WIDTH_REM}rem, calc(100vw - ${NAV_DROPDOWN_VIEWPORT_GUTTER_REM * 2}rem))`;
11871
- const NAV_DROPDOWN_CONTAINER_RAIL_CSS_RULES = [
11872
- `min-width: ${NAV_DROPDOWN_MIN_WIDTH_REM}rem;`,
11873
- `width: max-content;`,
11874
- `max-width: ${NAV_DROPDOWN_MAX_WIDTH_CSS};`,
11875
- `transform: translateX(var(${NAV_DROPDOWN_SHIFT_PROPERTY}, 0px));`
11876
- ];
11877
-
11878
- //#endregion
11879
- //#region ../theme-core/src/header/surfaceRules.ts
11880
- /** Compile the at-rest surface independently of scroll treatment and paint geometry. */
11881
- function buildHeaderSurfaceRules(header, tokens) {
11882
- const base = [];
11883
- const background = header.background;
11884
- if (background.type === "color") {
11885
- base.push(`background-color: ${tokens.getColor(background.color || "surface")};`);
11886
- } else if (background.type === "gradient" && background.gradient) {
11887
- const stops = background.gradient.stops.map((stop) => tokens.getColor(stop.color)).join(", ");
11888
- base.push(`background-image: linear-gradient(${background.gradient.angle ?? 180}deg, ${stops});`);
11889
- }
11890
- const borderWidth = getComponentBorderWidthPx(header.border?.width ?? "none");
11891
- if (!header.border || borderWidth === "0") {
11892
- return {
11893
- base,
11894
- border: []
11895
- };
11896
- }
11897
- const color = rgbColorValue(header.border.colorToken?.trim() || "border");
11898
- const style = header.border.style === "dashed" ? "dashed" : "solid";
11899
- const border = header.border.position === "both" ? [`border-top: ${borderWidth} ${style} ${color};`, `border-bottom: ${borderWidth} ${style} ${color};`] : [`border-${header.border.position}: ${borderWidth} ${style} ${color};`];
11900
- return {
11901
- base,
11902
- border
11903
- };
11904
- }
11905
-
11906
- //#endregion
11907
- //#region ../theme-core/src/header/generateHeaderNavLinkStyles.ts
11908
- function generateHeaderNavLinkStyles(header, themeId, tokens, theme, contrastTreatment, navTextSize) {
11909
- const style = effectiveHeaderNavLinkStyle(header, contrastTreatment, navTextSize);
11910
- if (!style) {
11911
- console.warn(`[generateHeaderCss] Nav link preset not found: ${header.navStyle}`);
11912
- return "";
11913
- }
11914
- const preferences = {
11915
- typography: header.navFontRole ?? "inherit",
11916
- fontSize: readHeaderNavFontSizePreference(header.navTextSize),
11917
- interactionPalette: header.interactionPalette ?? "recommended"
11918
- };
11919
- return generateNavLinkCSS({
11920
- themeId,
11921
- tokens,
11922
- theme,
11923
- style: mergeHeaderNavTypographyPreference(style, preferences),
11924
- className: "header-nav-link",
11925
- surfaceDependent: true,
11926
- surfaceRestingColor: header.background?.contentOwner === siteChromeBackgroundOverrideOwner
11927
- });
11928
- }
11929
-
11930
- //#endregion
11931
- //#region ../theme-core/src/header/generateHeaderLogoStyles.ts
11932
- function generateHeaderLogoStyles(input) {
11933
- const { logo, themeId, tokens } = input;
11934
- const selector = `:where([data-theme-scope="${themeId}"]) .header-logo-text`;
11935
- const rules = [
11936
- ...logoTextColorRules(input),
11937
- logo?.fontFamily === "body" ? "font-family: var(--font-body);" : "font-family: var(--font-heading);",
11938
- ...logoLetterSpacingRules(logo),
11939
- ...logoGradientRules(logo, tokens),
11940
- ...logoContrastRules(input)
11941
- ];
11942
- return `\n${selector} {\n ${rules.join("\n ")}\n}\n${mobilePanelLogoColor(input)}`;
11943
- }
11944
- function mobilePanelLogoColor(input) {
11945
- if (input.logo?.gradient) return "";
11946
- const { themeId, tokens, textColor, fallbackTextColor } = input;
11947
- const fallback = tokens.getColor(textColor ?? fallbackTextColor ?? "text");
11948
- const panelDescendant = `:where([data-theme-scope="${themeId}"]) .nav-mobile-panel .header-logo-text`;
11949
- const scopedPanel = `:where(.nav-mobile-panel[data-theme-scope="${themeId}"]) .header-logo-text`;
11950
- return `\n${panelDescendant},\n${scopedPanel} {\n color: ${surfaceForegroundValue("text", fallback)};\n}\n`;
11951
- }
11952
- function logoTextColorRules(input) {
11953
- const { logo, textColor, fallbackTextColor, tokens, useSurfaceTextColor } = input;
11954
- if (logo?.gradient || !textColor && !useSurfaceTextColor) return [];
11955
- const authoredColor = tokens.getColor(textColor ?? fallbackTextColor ?? "text");
11956
- return [`color: ${useSurfaceTextColor ? surfaceForegroundValue("text", authoredColor) : authoredColor};`];
11957
- }
11958
- function logoLetterSpacingRules(logo) {
11959
- const spacing = logo?.letterSpacing;
11960
- if (!spacing || spacing === "normal") return [];
11961
- const values = {
11962
- wide: "0.05em",
11963
- wider: "0.1em",
11964
- widest: "0.15em"
11965
- };
11966
- return [`letter-spacing: ${values[spacing]};`];
11967
- }
11968
- function logoGradientRules(logo, tokens) {
11969
- if (!logo?.gradient) return [];
11970
- return [
11971
- `background: linear-gradient(to right, ${tokens.getColor("primary")}, ${tokens.getColor("accent")});`,
11972
- "-webkit-background-clip: text;",
11973
- "-webkit-text-fill-color: transparent;",
11974
- "background-clip: text;",
11975
- "color: transparent;"
11976
- ];
11977
- }
11978
- function logoContrastRules(input) {
11979
- if (input.logo?.gradient || input.useSurfaceTextColor) return [];
11980
- const colorToken = input.textColor?.trim() || input.fallbackTextColor?.trim() || "text";
11981
- const shadow = buildTextContrastShadow({
11982
- theme: input.theme,
11983
- colorToken,
11984
- treatment: input.contrastTreatment
11985
- });
11986
- return shadow ? [`text-shadow: ${shadow};`] : [];
11987
- }
11988
-
11989
- //#endregion
11990
- //#region ../theme-core/src/header/generateHeaderCss.ts
11991
- const HEADER_BLUR = "blur(12px)";
11992
- const FLOATING_BLUR = "blur(16px)";
11993
- /**
11994
- * Generate complete CSS for header system from theme config
11995
- *
11996
- * Similar to button system - generates theme-scoped CSS using [data-theme-scope] selector
11997
- */
11998
- function generateHeaderCss(options) {
11999
- const { themeId, theme, responsiveMode = DEFAULT_THEME_RESPONSIVE_MODE, solvedInks = "withhold" } = options;
12000
- const tokens = new TokenResolver(theme);
12001
- const header = theme.header;
12002
- const cssChunks = [];
12003
- const headerContrastTreatment = resolveHeaderContrastTreatment(header);
12004
- const headerNavTextSize = resolveHeaderNavTextSize(header);
12005
- cssChunks.push(generateHeaderRootStyles(header, themeId, tokens, headerPaintInks(header, theme, solvedInks)));
12006
- cssChunks.push(generateOpeningPhotoShade(header, themeId));
12007
- cssChunks.push(generateMobileOverlayBackdropFilterFix(themeId, header.variant));
12008
- cssChunks.push(generateHeaderLogoStyles({
12009
- logo: header.logo,
12010
- themeId,
12011
- tokens,
12012
- theme,
12013
- textColor: header.textColor,
12014
- fallbackTextColor: header.navLinkStyle?.colorToken,
12015
- contrastTreatment: headerContrastTreatment ?? undefined,
12016
- useSurfaceTextColor: header.background?.contentOwner === siteChromeBackgroundOverrideOwner
12017
- }));
12018
- cssChunks.push(generateLogoTextBehaviorStyles(header.logoText, themeId));
12019
- cssChunks.push(generateHeaderNavLinkStyles(header, themeId, tokens, theme, headerContrastTreatment, headerNavTextSize));
12020
- const dropdown = resolveDropdownSurface(header, theme, tokens, solvedInks);
12021
- cssChunks.push(generateDropdownContainerStyles(themeId, tokens, dropdown));
12022
- cssChunks.push(generateDropdownLinkStyles(header, themeId, tokens, theme, dropdown));
12023
- cssChunks.push(generateDropdownCaretStyles(themeId));
12024
- cssChunks.push(generateDropdownTriggerOverrides(header, themeId));
12025
- cssChunks.push(generateMobilePanelStyles(themeId, dropdown));
12026
- if (header.variant === "floating" && header.container) {
12027
- cssChunks.push(generateFloatingContainerStyles(header, themeId, tokens, responsiveMode));
12028
- }
12029
- cssChunks.push(generateNavContainerStyles(header, themeId, tokens, theme, solvedInks));
12030
- cssChunks.push(generateHeaderCtaTreatmentStyles(header, themeId, tokens));
12031
- cssChunks.push(generateCtaGapStyles(header, themeId));
12032
- return cssChunks.filter(Boolean).join("\n\n");
12033
- }
12034
- const _testExports = { generateHeaderRootStyles };
12035
- /** Scope shading to the actual opening under overlay chrome, including hidden-header pages. */
12036
- function generateOpeningPhotoShade(header, themeId) {
12037
- const shade = headerNeedsPhotoShade(header) ? "linear-gradient(to bottom, rgba(0,0,0,0.78) 0px, rgba(0,0,0,0.64) 110px, transparent 220px)" : "linear-gradient(transparent, transparent)";
12038
- return `:where([data-theme-scope="${themeId}"]) [data-site-document-layout-mode="header-overlay"] :is([data-page-block-position="first"], [data-page-block-position="only"]) { --rb-opening-photo-shade: ${shade}; }`;
12039
- }
12040
- function resolveHeaderContrastTreatment(header) {
12041
- if (header.navLinkStyle?.contrastTreatment) {
12042
- return header.navLinkStyle.contrastTreatment;
12043
- }
12044
- return isClearSplitHeaderShape(header) ? "soft-glow" : null;
12045
- }
12046
- function resolveHeaderNavTextSize(header) {
12047
- if (header.navLinkStyle?.textSize) {
12048
- return header.navLinkStyle.textSize;
12049
- }
12050
- return isClearSplitHeaderShape(header) ? "lg" : null;
12051
- }
12052
- function isClearSplitHeaderShape(header) {
12053
- const hasSharedFloatingSurface = Boolean(header.container);
12054
- return header.variant === "floating" && (header.navStyle ?? "minimal") === "minimal" && !hasSharedFloatingSurface;
12055
- }
12056
- function isDetachedFloatingHeaderShape(header) {
12057
- return header.variant === "floating" && !header.container;
12058
- }
12059
- /**
12060
- * Generate root header container styles
12061
- */
12062
- function generateHeaderRootStyles(header, themeId, tokens, inks) {
12063
- const selector = `:where([data-theme-scope="${themeId}"]) .header-root`;
12064
- const rules = [];
12065
- const isContainedNav = headerRootPaint(header) === "contained-nav";
12066
- rules.push("position: relative;");
12067
- rules.push("isolation: isolate;");
12068
- const surfaceRules = isContainedNav ? {
12069
- base: [],
12070
- border: []
12071
- } : buildHeaderSurfaceRules(header, tokens);
12072
- const floatingSurfaceBorder = header.variant === "floating";
12073
- if (!floatingSurfaceBorder) rules.push(...surfaceRules.border);
12074
- const basePaintRules = floatingSurfaceBorder ? [
12075
- ...surfaceRules.base,
12076
- ...surfaceRules.border,
12077
- ...inks
12078
- ] : [...surfaceRules.base, ...inks];
12079
- const scrolledPaintRules = floatingSurfaceBorder ? surfaceRules.border : [];
12080
- if (header.positioning === "sticky" || header.positioning === "fixed") {
12081
- const shadowElevation = header.shadow?.elevation ?? "none";
12082
- const shadowVar = getComponentShadowVar(shadowElevation);
12083
- if (shadowVar) {
12084
- rules.push(`box-shadow: ${shadowVar};`);
12085
- }
12086
- }
12087
- const baseBlock = `\n${selector} {\n ${rules.join("\n ")}\n}\n`;
12088
- const overlaySelector = `${selector}:has([data-nav-mobile-overlay][aria-hidden="false"])`;
12089
- const overlayFix = `\n${overlaySelector} {\n backdrop-filter: none !important;\n -webkit-backdrop-filter: none !important;\n}\n`;
12090
- const scrollTreatmentStyles = generateHeaderScrollTreatmentStyles(header.scrollTreatment, themeId, header.variant, scrolledPaintRules);
12091
- if (isContainedNav || basePaintRules.length === 0) {
12092
- return `${baseBlock}\n${overlayFix}${scrollTreatmentStyles}`;
12093
- }
12094
- const paintTargets = getHeaderPaintTargets(header.variant, themeId);
12095
- const backgroundBlock = renderHeaderPaintTargetRules(paintTargets, basePaintRules);
12096
- return `${baseBlock}\n${backgroundBlock}\n${overlayFix}${scrollTreatmentStyles}`;
12097
- }
12098
- function generateLogoTextBehaviorStyles(logoText, themeId) {
12099
- if (!logoText) return "";
12100
- const selector = `:where([data-theme-scope="${themeId}"]) .header-logo-text`;
12101
- const cssChunks = [];
12102
- const mobileRules = [];
12103
- if (logoText.mobileWrapLines === 1) {
12104
- mobileRules.push("display: block;");
12105
- mobileRules.push("white-space: nowrap;");
12106
- mobileRules.push("overflow: hidden;");
12107
- mobileRules.push("text-overflow: ellipsis;");
12108
- }
12109
- if (logoText.mobileWrapLines === 2) {
12110
- mobileRules.push("display: -webkit-box;");
12111
- mobileRules.push("-webkit-box-orient: vertical;");
12112
- mobileRules.push("-webkit-line-clamp: 2;");
12113
- mobileRules.push("white-space: normal;");
12114
- mobileRules.push("overflow: hidden;");
12115
- mobileRules.push("text-overflow: ellipsis;");
12116
- }
12117
- if (logoText.mobileMaxWidth) {
12118
- const maxWidthMap = {
12119
- xs: "8rem",
12120
- sm: "10rem",
12121
- md: "12rem"
12122
- };
12123
- mobileRules.push(`max-width: ${maxWidthMap[logoText.mobileMaxWidth]};`);
12124
- }
12125
- if (mobileRules.length > 0) {
12126
- cssChunks.push(`@media ${MQ_MD_DOWN} {\n ${selector} {\n ${mobileRules.join("\n ")}\n }\n}`);
12127
- }
12128
- if (logoText.hideOnShrink) {
12129
- const shrinkSelector = `:where([data-theme-scope="${themeId}"]) .rb-header-shrink.header-scrolled .header-logo-text`;
12130
- cssChunks.push(`\n${shrinkSelector} {\n display: none;\n}\n`);
12131
- }
12132
- return cssChunks.join("\n\n");
12133
- }
12134
- /**
12135
- * Generate dropdown menu container styles
12136
- */
12137
- function generateDropdownContainerStyles(themeId, tokens, surface) {
12138
- const selector = `:where([data-theme-scope="${themeId}"]) .nav-dropdown-menu`;
12139
- const rules = [];
12140
- const dropdownStyle = surface.style;
12141
- rules.push(...NAV_DROPDOWN_CONTAINER_RAIL_CSS_RULES);
12142
- rules.push(`background-color: ${surface.backgroundColor};`);
12143
- rules.push(...surface.foregroundDeclarations);
12144
- const borderToken = dropdownStyle?.borderColor;
12145
- if (borderToken === null) {
12146
- rules.push(`border: none;`);
12147
- } else {
12148
- rules.push(`border: 1px solid ${tokens.getColor(borderToken ?? "border")};`);
12149
- }
12150
- const shadow = dropdownStyle?.shadow ?? "md";
12151
- const shadowVar = getComponentShadowVar(shadow);
12152
- if (shadowVar) {
12153
- rules.push(`box-shadow: ${shadowVar};`);
12154
- }
12155
- const borderRadius = dropdownStyle?.borderRadius ?? "md";
12156
- const radiusMap = {
12157
- none: "0",
12158
- sm: "0.25rem",
12159
- md: "0.5rem",
12160
- lg: "0.75rem"
12161
- };
12162
- rules.push(`border-radius: ${radiusMap[borderRadius]};`);
12163
- rules.push(`padding: 0.5rem 0;`);
12164
- return `\n${selector} {\n ${rules.join("\n ")}\n}\n` + generateDropdownFrosting(selector, surface);
12165
- }
12166
- function dropdownLinkEffects(style, textToken, opacity) {
12167
- const background = style?.hoverBackground === undefined ? textToken : style.hoverBackground;
12168
- return [...background === null ? [] : [{
12169
- effectId: "background-on-hover-alpha",
12170
- options: {
12171
- colorToken: background,
12172
- opacity
12173
- }
12174
- }], ...style?.hoverTextColor ? [{
12175
- effectId: "text-color-change",
12176
- options: { hoverColorToken: style.hoverTextColor }
12177
- }] : []];
12178
- }
12179
- function dropdownNavLinkStyle(header, surface) {
12180
- const style = surface.style;
12181
- const textToken = surface.textToken;
12182
- return {
12183
- name: "Dropdown",
12184
- typography: "body",
12185
- fontWeight: style?.fontWeight ?? header.navWeight ?? "medium",
12186
- textTransform: style?.textTransform ?? "none",
12187
- italic: false,
12188
- colorToken: textToken,
12189
- padding: "compact",
12190
- borderRadius: "sm",
12191
- effects: {
12192
- hover: dropdownLinkEffects(style, textToken, 12),
12193
- active: dropdownLinkEffects(style, textToken, 18)
12194
- }
12195
- };
12196
- }
12197
- function dropdownTypographyRules(style) {
12198
- const letterSpacing = style?.letterSpacing ?? "normal";
12199
- const spacing = {
12200
- normal: "0em",
12201
- wide: "0.05em",
12202
- wider: "0.1em"
12203
- };
12204
- const sizes = {
12205
- xs: "0.75rem",
12206
- sm: "0.875rem",
12207
- base: "1rem",
12208
- lg: "1.125rem"
12209
- };
12210
- return [...letterSpacing === "normal" ? [] : [`letter-spacing: ${spacing[letterSpacing]};`], `font-size: ${sizes[style?.textSize ?? "sm"]};`];
12211
- }
12212
- function generateDropdownLinkStyles(header, themeId, tokens, theme, surface) {
12213
- const baseCSS = generateNavLinkCSS({
12214
- themeId,
12215
- tokens,
12216
- theme,
12217
- style: dropdownNavLinkStyle(header, surface),
12218
- className: "nav-dropdown-link"
12219
- });
12220
- const selector = `:where([data-theme-scope="${themeId}"]) .nav-dropdown-link`;
12221
- return baseCSS + `\n${selector} {\n ${dropdownTypographyRules(surface.style).join("\n ")}\n}\n`;
12222
- }
12223
- function generateDropdownCaretStyles(themeId) {
12224
- const selector = `:where([data-theme-scope="${themeId}"]) .nav-caret`;
12225
- const openSelector = `:where([data-theme-scope="${themeId}"]) .nav-dropdown-trigger[aria-expanded="true"] .nav-caret,` + ` :where([data-theme-scope="${themeId}"]) .nav-mobile-trigger[aria-expanded="true"] .nav-caret`;
12226
- return `
12227
- ${selector} {
12228
- width: 0.45rem;
12229
- height: 0.45rem;
12230
- margin-left: 0.5rem;
12231
- border-right: 2px solid currentColor;
12232
- border-bottom: 2px solid currentColor;
12233
- transform: rotate(45deg) translateY(-1px);
12234
- transition: transform 150ms ease;
12235
- }
12236
-
12237
- ${openSelector} {
12238
- transform: rotate(-135deg) translateY(1px);
12239
- }
12240
- `.trim();
12241
- }
12242
- function generateDropdownTriggerOverrides(header, themeId) {
12243
- const navStyle = header.navStyle ?? "minimal";
12244
- if (navStyle !== "underline" && navStyle !== "underline-grow") return "";
12245
- const selector = `:where([data-theme-scope="${themeId}"]) .nav-dropdown-trigger.header-nav-link::after`;
12246
- return `
12247
- ${selector} {
12248
- content: none;
12249
- }
12250
- `.trim();
12251
- }
12252
- /** Reuse the header's decided opening tone; unknown openings retain opaque paint. */
12253
- function contextualDropdownStyle(header) {
12254
- const style = header.dropdownStyle;
12255
- const treatment = style?.surfaceTreatment;
12256
- if (!style || treatment?.kind !== "frosted" || treatment.tone !== "opening") return style;
12257
- const tone = readOpeningToneProjection(header);
12258
- if (!tone) return {
12259
- ...style,
12260
- surfaceTreatment: { kind: "solid" }
12261
- };
12262
- const textColor = tone === "dark" ? "neutral-50" : "neutral-950";
12263
- return {
12264
- ...style,
12265
- background: tone === "dark" ? "neutral-950" : "neutral-50",
12266
- textColor,
12267
- hoverBackground: textColor,
12268
- borderColor: null
12269
- };
12270
- }
12271
- /** Menu text wins; only an absent preference falls back to the header's ink. */
12272
- function dropdownTextCandidate(style, header) {
12273
- return style?.textColor ?? ((header.navColor ?? header.textColor)?.trim() || "text");
12274
- }
12275
- function resolveDropdownSurface(header, theme, tokens, solvedInks) {
12276
- const style = contextualDropdownStyle(header);
12277
- const backgroundToken = style?.background ?? "surface";
12278
- const candidate = dropdownTextCandidate(style, header);
12279
- const foreground = resolveSurfaceForeground({
12280
- type: "color",
12281
- color: backgroundToken,
12282
- textColor: candidate
12283
- }, expandPalette(theme.palette));
12284
- return {
12285
- backgroundColor: tokens.getColor(backgroundToken),
12286
- textToken: foreground.kind === "known" ? foreground.roles.text : candidate,
12287
- foregroundDeclarations: [...foreground.kind === "known" ? surfaceForegroundDeclarations(surfaceForegroundInputFromResolved(foreground, (token) => tokens.getColor(token))) : [`color: ${tokens.getColor(candidate)};`], ...surfaceInkDeclarations(surfaceInkPublication(foreground, solvedInks))],
12288
- treatment: style?.surfaceTreatment,
12289
- style
12290
- };
12291
- }
12292
- /** Solid paint is the fallback. Blur is enhancement, never the readability mechanism. */
12293
- function generateDropdownFrosting(selector, surface) {
12294
- if (surface.treatment?.kind !== "frosted") return "";
12295
- const blur = surface.treatment.blur === "sm" ? "8px" : "12px";
12296
- return `
12297
- @supports (background-color: color-mix(in srgb, white, transparent)) and ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
12298
- ${selector} {
12299
- background-color: color-mix(in srgb, ${surface.backgroundColor} ${surface.treatment.opacity * 100}%, transparent);
12300
- backdrop-filter: blur(${blur});
12301
- -webkit-backdrop-filter: blur(${blur});
12302
- ${surface.treatment.opacity < 1 ? surfaceInkDeclarations({ kind: "reset" }).join("\n ") : ""}
12303
- }
12304
- }
12305
- `;
12306
- }
12307
- /** Mobile shares the resolved opaque surface, never desktop translucency. */
12308
- function generateMobilePanelStyles(themeId, surface) {
12309
- const selector = `:where([data-theme-scope="${themeId}"]) .nav-mobile-panel`;
12310
- const selfSelector = `:where(.nav-mobile-panel[data-theme-scope="${themeId}"])`;
12311
- const body = renderCssDeclarations([`background-color: ${surface.backgroundColor};`, ...surface.foregroundDeclarations]);
12312
- return `\n${selector} {\n ${body}\n}\n\n${selfSelector} {\n ${body}\n}\n`;
12313
- }
12314
- /**
12315
- * Generate nav container styles (pill/glass)
12316
- */
12317
- function generateNavContainerStyles(header, themeId, tokens, theme, solvedInks) {
12318
- const navContainer = header.navContainer;
12319
- const navContainerType = navContainer?.type ?? "none";
12320
- if (!navContainer || navContainerType === "none") return "";
12321
- const selector = `:where([data-theme-scope="${themeId}"]) .header-nav-row`;
12322
- const rules = [];
12323
- if (navContainerType === "pill" || navContainerType === "glass") {
12324
- rules.push("padding: 0.5rem 0.75rem;");
12325
- rules.push("border-radius: 9999px;");
12326
- rules.push("display: inline-flex;");
12327
- rules.push("align-items: center;");
12328
- rules.push("gap: 1.5rem;");
12329
- if (isDetachedFloatingHeaderShape(header)) {
12330
- rules.push("align-self: flex-start;");
12331
- }
12332
- }
12333
- rules.push(...navContainerSurfaceRules(header, navContainer, tokens, theme, solvedInks));
12334
- return `${selector} { ${rules.join(" ")} }`;
12335
- }
12336
- /** Paint and publication are decided together, so translucent containers cannot borrow solid inks. */
12337
- function navContainerSurfaceRules(header, container, tokens, theme, policy) {
12338
- if (container.type === "glass") {
12339
- const opacity$1 = resolveFloatingGlassOpacity(header, container.opacity, .15);
12340
- const background$1 = container.tint ? buildTint(tokens.getColor(container.tint), opacity$1) : buildRgbWhite(opacity$1);
12341
- return [
12342
- `background-color: ${background$1};`,
12343
- `backdrop-filter: ${HEADER_BLUR};`,
12344
- `-webkit-backdrop-filter: ${HEADER_BLUR};`,
12345
- "border: 1px solid rgba(255, 255, 255, 0.2);",
12346
- "box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);",
12347
- ...surfaceInkDeclarations({ kind: "reset" })
12348
- ];
12349
- }
12350
- const opacity = clampOpacity(container.opacity, .15);
12351
- const background = container.tint ? buildTint(tokens.getColor(container.tint), opacity) : tokens.getColor("surface");
12352
- const solid = container.tint ? opacity === 1 ? container.tint : null : "surface";
12353
- return [
12354
- `background-color: ${background};`,
12355
- `border: 1px solid ${tokens.getColor("border")};`,
12356
- ...headerContainerInks(solid, theme, policy)
12357
- ];
12358
- }
12359
- /**
12360
- * Generate floating container styles
12361
- */
12362
- function generateFloatingContainerStyles(header, themeId, tokens, responsiveMode) {
12363
- const container = header.container;
12364
- if (!container) return "";
12365
- const selector = `:where([data-theme-scope="${themeId}"]) .header-floating-container`;
12366
- const rules = [];
12367
- const opacity = container.tint === "surface" ? resolveFloatingGlassOpacity(header, container.opacity, .12) : clampOpacity(container.opacity, .12);
12368
- if (container.tint) {
12369
- rules.push(`background-color: ${buildTint(tokens.getColor(container.tint), opacity)};`);
12370
- } else {
12371
- rules.push(`background-color: ${buildRgbWhite(opacity)};`);
12372
- }
12373
- rules.push(`backdrop-filter: ${FLOATING_BLUR};`);
12374
- rules.push(`-webkit-backdrop-filter: ${FLOATING_BLUR};`);
12375
- rules.push(...surfaceInkDeclarations({ kind: "reset" }));
12376
- if (container.border) {
12377
- rules.push(`border: 1px solid rgb(255 255 255 / 0.2);`);
12378
- }
12379
- const radiusMap = {
12380
- none: "0",
12381
- sm: "0.375rem",
12382
- md: "0.5rem",
12383
- lg: "0.75rem",
12384
- xl: "1rem",
12385
- "2xl": "1.5rem"
12386
- };
12387
- rules.push(`border-radius: ${radiusMap[container.rounded]};`);
12388
- if (container.shadow) {
12389
- rules.push(`box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);`);
12390
- }
12391
- const paddingMap = {
12392
- sm: "1rem 1.5rem",
12393
- md: "1.5rem 2rem",
12394
- lg: "2rem 2.5rem"
12395
- };
12396
- rules.push(`padding: ${paddingMap[container.padding]};`);
12397
- return `\n${selector} {\n ${rules.join("\n ")}\n}\n${generateFrameMobileSheetStyles(container, themeId, rules, responsiveMode)}`;
12398
- }
12399
- /** The frame owner explicitly opts into an inset sheet on phones. */
12400
- function generateFrameMobileSheetStyles(container, themeId, rules, responsiveMode) {
12401
- if (container.mobileSheet !== "inset") return "";
12402
- return `
12403
- ${phoneAtRule(responsiveMode)} {
12404
- :where([data-theme-scope="${themeId}"]) .header-mobile-bar {
12405
- ${rules.join("\n ")}
12406
- box-sizing: border-box; margin: 12px; width: calc(100% - 24px); min-height: 60px; padding: 10px 16px; align-items: center;
12407
- }
12408
- }`;
12409
- }
12410
- /** The phone breakpoint as a viewport media query, or the site container query in container mode. */
12411
- function phoneAtRule(responsiveMode) {
12412
- const containerQuery = responsiveMode === "container" ? convertMediaQueryToContainerQuery(MQ_MD_DOWN) : null;
12413
- return containerQuery ? `@container ${THEME_RESPONSIVE_CONTAINER_NAME} ${containerQuery}` : `@media ${MQ_MD_DOWN}`;
12414
- }
12415
- /**
12416
- * CTA gap value map — controls left margin on the desktop CTA button
12417
- */
12418
- const HEADER_CTA_GAP_MAP = {
12419
- none: "0",
12420
- tight: "0.5rem",
12421
- compact: "1rem",
12422
- default: "1.5rem",
12423
- relaxed: "2rem",
12424
- spacious: "3rem"
12425
- };
12426
- /**
12427
- * The inverted CTA treatment is contextual to branded header surfaces. It
12428
- * cannot be represented by the generic secondary button role alone because
12429
- * that role is transparent and inherits the surrounding surface foreground.
12430
- * Keep the shared button's structure/effects while making the header own the
12431
- * palette relationship for both desktop and mobile CTAs.
12432
- */
12433
- function generateHeaderCtaTreatmentStyles(header, themeId, tokens) {
12434
- if (header.ctaTreatment !== "inverted") return "";
12435
- const desktopSelector = `:where([data-theme-scope="${themeId}"]) .header-cta`;
12436
- const mobileSelector = `:where([data-theme-scope="${themeId}"]) .nav-mobile-cta`;
12437
- const selectors = `${desktopSelector},\n${mobileSelector}`;
12438
- const hoverSelectors = [
12439
- `${desktopSelector}:hover`,
12440
- `${desktopSelector}[data-force-hover="true"]`,
12441
- `${mobileSelector}:hover`,
12442
- `${mobileSelector}[data-force-hover="true"]`
12443
- ].join(",\n");
12444
- const focusSelectors = [`${desktopSelector}:focus-visible`, `${mobileSelector}:focus-visible`].join(",\n");
12445
- return `
12446
- ${selectors} {
12447
- background-color: ${tokens.getColor("background")};
12448
- background-image: none;
12449
- color: ${tokens.getColor("onBackground")};
12450
- border-color: currentColor;
12451
- }
12452
-
12453
- ${hoverSelectors} {
12454
- background-color: ${tokens.getColor("surface")};
12455
- background-image: none;
12456
- color: ${tokens.getColor("onSurface")};
12457
- }
12458
-
12459
- ${focusSelectors} {
12460
- outline: 2px solid ${tokens.getColor("onBackground")};
12461
- outline-offset: 2px;
12462
- box-shadow: 0 0 0 4px ${tokens.getColor("background")};
12463
- }
12464
- `;
12465
- }
12466
- /**
12467
- * Generate CTA button gap styles (left margin between nav links and CTA)
12468
- */
12469
- function generateCtaGapStyles(header, themeId) {
12470
- if (!header.ctaGap) return "";
12471
- const value = HEADER_CTA_GAP_MAP[header.ctaGap];
12472
- if (!value) return "";
12473
- const selector = `:where([data-theme-scope="${themeId}"]) .header-cta`;
12474
- return `\n${selector} {\n margin-left: ${value};\n}\n`;
12475
- }
12476
- function clampOpacity(value, fallback) {
12477
- if (typeof value !== "number" || Number.isNaN(value)) {
12478
- return fallback;
12479
- }
12480
- return Math.min(Math.max(value, 0), 1);
12481
- }
12482
- function resolveFloatingGlassOpacity(header, value, fallback) {
12483
- const opacity = clampOpacity(value, fallback);
12484
- return header.variant === "floating" ? Math.max(opacity, .4) : opacity;
12485
- }
12486
- function buildTint(color, opacity) {
12487
- return applyAlpha(color, opacity);
12488
- }
12489
- function buildRgbWhite(opacity) {
12490
- const clamped = clampOpacity(opacity, 1);
12491
- return `rgb(255 255 255 / ${clamped})`;
12492
- }
12493
- function applyAlpha(color, opacity) {
12494
- const clamped = clampOpacity(opacity, 1);
12495
- if (color.startsWith("rgb(") && color.endsWith(")")) {
12496
- const inner = color.slice(4, -1).trim();
12497
- const channels = inner.includes("/") ? (inner.split("/")[0] ?? inner).trim() : inner;
12498
- return `rgb(${channels} / ${clamped})`;
12499
- }
12500
- return color;
12501
- }
12502
- /** Container glass is deliberately unmeasured; an opaque pill owns its solid paint. */
12503
- function headerContainerInks(color, theme, policy) {
12504
- if (color === null) return surfaceInkDeclarations({ kind: "reset" });
12505
- const foreground = resolveSurfaceForeground({
12506
- type: "color",
12507
- color
12508
- }, expandPalette(theme.palette));
12509
- return surfaceInkDeclarations(surfaceInkPublication(foreground, policy));
12510
- }
12511
- /** Floating mobile bars paint the header background even when the root overlays the page. */
12512
- function headerPaintInks(header, theme, policy) {
12513
- if (headerRootPaint(header) === "contained-nav") return [];
12514
- const foreground = resolveSurfaceForeground(header.background, expandPalette(theme.palette));
12515
- return surfaceInkDeclarations(surfaceInkPublication(foreground, policy));
12516
- }
12517
-
12518
11799
  //#endregion
12519
11800
  //#region ../blocks/src/system/transforms/registry/_utils/themeColorResolution.ts
12520
11801
  function resolveBackgroundColorValue(value, context) {
@@ -12524,20 +11805,6 @@ function resolveBackgroundColorValue(value, context) {
12524
11805
  //#endregion
12525
11806
  //#region ../blocks/src/system/transforms/registry/_utils/surfaceForegroundStyles.ts
12526
11807
  const sectionSurfaceCache = new WeakMap();
12527
- function resolveHeaderRootForeground(header, palette, isTransparent) {
12528
- if (isTransparent) return readOpeningForegroundProjection(header) ?? { kind: "inherits" };
12529
- const background = header?.background;
12530
- return resolveSurfaceForeground(background ?? {
12531
- type: "color",
12532
- color: "surface"
12533
- }, palette, headerForegroundCandidates(header));
12534
- }
12535
- function resolveFooterRootForeground(footer, palette, proseHoverCandidate) {
12536
- return resolveSurfaceForeground(footer?.background ?? {
12537
- type: "color",
12538
- color: "surface"
12539
- }, palette, footerForegroundCandidates(footer, proseHoverCandidate), { compositeBase: "background" });
12540
- }
12541
11808
  function resolveSurfaceForegroundStyles(value, context, options) {
12542
11809
  const resolved = cachedSectionSurface(value, context, options);
12543
11810
  const foreground = resolved.foreground;
@@ -12630,18 +11897,18 @@ function replaceBackgroundWithImage(value, image, defaults = {}) {
12630
11897
  const current = parseBackgroundContent(value);
12631
11898
  let candidate;
12632
11899
  if (current?.type === "image") {
12633
- const { type: _type, image: _image, ...presentation } = current;
11900
+ const { type: _type, image: _image, ...presentation$1 } = current;
12634
11901
  candidate = {
12635
11902
  ...defaults,
12636
- ...presentation,
11903
+ ...presentation$1,
12637
11904
  type: "image",
12638
11905
  image
12639
11906
  };
12640
11907
  } else if (current?.type === "video") {
12641
- const { type: _type, image: _image, showVideoOnMobile: _mobile, ...presentation } = current;
11908
+ const { type: _type, image: _image, showVideoOnMobile: _mobile, ...presentation$1 } = current;
12642
11909
  candidate = {
12643
11910
  ...defaults,
12644
- ...presentation,
11911
+ ...presentation$1,
12645
11912
  type: "image",
12646
11913
  image
12647
11914
  };
@@ -16957,6 +16224,24 @@ const heroConfig = {
16957
16224
  renderInHeader: true,
16958
16225
  intent: "style"
16959
16226
  }),
16227
+ {
16228
+ id: "photoTint",
16229
+ type: "select",
16230
+ multiple: false,
16231
+ required: false,
16232
+ label: "Photo tint",
16233
+ options: [{
16234
+ label: "Dark",
16235
+ value: "dark"
16236
+ }, {
16237
+ label: "Brand colour",
16238
+ value: "brand"
16239
+ }],
16240
+ ui: {
16241
+ intent: "display",
16242
+ hidden: true
16243
+ }
16244
+ },
16960
16245
  {
16961
16246
  id: "candleGlow",
16962
16247
  type: "boolean",
@@ -18208,7 +17493,7 @@ function itemRole(index, count, roles, featured = false) {
18208
17493
 
18209
17494
  //#endregion
18210
17495
  //#region ../blocks/src/system/runtime/lookSurfaces.ts
18211
- const isPlainObject$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
17496
+ const isPlainObject$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
18212
17497
  function resolvedItemSurfaceProps(surface, slot, style = undefined) {
18213
17498
  const declarations = opaqueSurfaceDeclarations(surface, true);
18214
17499
  const styles = Object.fromEntries(declarations.map((declaration) => {
@@ -18219,7 +17504,7 @@ function resolvedItemSurfaceProps(surface, slot, style = undefined) {
18219
17504
  return {
18220
17505
  "data-surface": paint?.kind === "solid" ? paint.token : surface.kind === "inherit" ? "section" : slot,
18221
17506
  style: {
18222
- ...isPlainObject$5(style) ? style : {},
17507
+ ...isPlainObject$4(style) ? style : {},
18223
17508
  ...styles,
18224
17509
  ...paint?.kind === "solid" ? { background: resolvedSurfaceBackground(surface, paint.token) } : {}
18225
17510
  }
@@ -18227,7 +17512,7 @@ function resolvedItemSurfaceProps(surface, slot, style = undefined) {
18227
17512
  }
18228
17513
  const resolvedSurfaceSchema = z.unknown().transform((value, context) => {
18229
17514
  try {
18230
- return parseResolvedSurface(value);
17515
+ return parseArrangementSlotSurface(value);
18231
17516
  } catch {
18232
17517
  context.addIssue({
18233
17518
  code: "custom",
@@ -18288,18 +17573,18 @@ const lookListPresentationSchemaShape = {
18288
17573
  lookSlots: z.partialRecord(z.enum(arrangementSlotNames), z.enum(arrangementSlotNames)).optional(),
18289
17574
  surfacePresentation: surfacePresentationSchema.optional()
18290
17575
  };
18291
- function listLookSlot(presentation, slot, state) {
18292
- const name = presentation.lookSlots?.[slot];
17576
+ function listLookSlot(presentation$1, slot, state) {
17577
+ const name = presentation$1.lookSlots?.[slot];
18293
17578
  if (name === undefined) return {};
18294
- const surface = (state ? presentation.surfacePresentation?.states[slot]?.[state] : undefined) ?? presentation.surfacePresentation?.slots[slot];
17579
+ const surface = (state ? presentation$1.surfacePresentation?.states[slot]?.[state] : undefined) ?? presentation$1.surfacePresentation?.slots[slot];
18295
17580
  return {
18296
17581
  "data-slot": name,
18297
17582
  ...surface === undefined ? { "data-surface": "section" } : resolvedItemSurfaceProps(surface, slot)
18298
17583
  };
18299
17584
  }
18300
17585
  /** Control kind is semantic; legacy markup receives no extra attributes. */
18301
- function listLookControl(presentation, kind) {
18302
- return presentation.lookSlots === undefined ? {} : { "data-control": kind };
17586
+ function listLookControl(presentation$1, kind) {
17587
+ return presentation$1.lookSlots === undefined ? {} : { "data-control": kind };
18303
17588
  }
18304
17589
  function resolvedSurfaceBackground(surface, token) {
18305
17590
  const color = token.startsWith("#") ? token : `rgb(var(--tb-${token}))`;
@@ -18307,12 +17592,12 @@ function resolvedSurfaceBackground(surface, token) {
18307
17592
  return base ? `radial-gradient(ellipse at center, ${color}, ${base.startsWith("#") ? base : `rgb(var(--tb-${base}))`} 70%)` : color;
18308
17593
  }
18309
17594
  /** Pagination uses the displayed collection, just as declarative repeaters do. */
18310
- function listItemPresentation(presentation, index, count, featured = false) {
18311
- const role = presentation.itemRoles ? itemRole(index, count, presentation.itemRoles, featured) : undefined;
18312
- return presentation.surfacePresentation ? {
18313
- ...presentation,
18314
- surfacePresentation: selectItemSurfacePresentation(presentation.surfacePresentation, role)
18315
- } : presentation;
17595
+ function listItemPresentation(presentation$1, index, count, featured = false) {
17596
+ const role = presentation$1.itemRoles ? itemRole(index, count, presentation$1.itemRoles, featured) : undefined;
17597
+ return presentation$1.surfacePresentation ? {
17598
+ ...presentation$1,
17599
+ surfacePresentation: selectItemSurfacePresentation(presentation$1.surfacePresentation, role)
17600
+ } : presentation$1;
18316
17601
  }
18317
17602
 
18318
17603
  //#endregion
@@ -19386,7 +18671,7 @@ const heroTitleLengthThresholds = {
19386
18671
  maxWords: 11
19387
18672
  }
19388
18673
  };
19389
- function isPlainObject$4(value) {
18674
+ function isPlainObject$3(value) {
19390
18675
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
19391
18676
  }
19392
18677
  function readString$2(obj, key) {
@@ -19395,7 +18680,7 @@ function readString$2(obj, key) {
19395
18680
  }
19396
18681
  function resolveGeneratedHeroLayoutStyles(content) {
19397
18682
  const raw = content._heroLayoutStyles;
19398
- return isPlainObject$4(raw) ? raw : null;
18683
+ return isPlainObject$3(raw) ? raw : null;
19399
18684
  }
19400
18685
  function readHeroLayoutString(content, generatedKey, fallbackKey = generatedKey) {
19401
18686
  const explicitValue = readString$2(content, fallbackKey);
@@ -19459,7 +18744,7 @@ const heroContentWrapperClassTransform = {
19459
18744
  summary: "Resolve hero content wrapper className from alignment + max width controls",
19460
18745
  schema: heroContentWrapperClassSchema,
19461
18746
  run: (value, options, context) => {
19462
- const content = isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {};
18747
+ const content = isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {};
19463
18748
  const alignment = resolveHeroContentAlignment(content);
19464
18749
  const maxWidth = resolveHeroContentMaxWidth(content);
19465
18750
  const typographyScale = resolveHeroTypographyScale(content);
@@ -19481,7 +18766,7 @@ const heroCtaJustifyTransform = {
19481
18766
  kind: "string",
19482
18767
  summary: "Map hero contentAlignment to CTA row justify value",
19483
18768
  run: (value, _options, context) => {
19484
- const content = isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {};
18769
+ const content = isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {};
19485
18770
  const alignment = resolveHeroContentAlignment(content);
19486
18771
  return alignment === "left" ? "start" : alignment === "right" ? "end" : "center";
19487
18772
  }
@@ -19491,7 +18776,7 @@ const heroStackJustifyFromVerticalAlignmentTransform = {
19491
18776
  kind: "string",
19492
18777
  summary: "Map hero verticalAlignment to stack justify value",
19493
18778
  run: (value, _options, context) => {
19494
- const content = isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {};
18779
+ const content = isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {};
19495
18780
  const vertical = resolveHeroVerticalAlignment(content);
19496
18781
  return vertical === "top" ? "start" : vertical === "bottom" ? "end" : "center";
19497
18782
  }
@@ -19503,7 +18788,7 @@ const heroGridClassFromVerticalAlignmentTransform = {
19503
18788
  summary: "Append align-items class for hero split layouts based on verticalAlignment",
19504
18789
  schema: heroGridClassFromVerticalAlignmentSchema,
19505
18790
  run: (value, options, context) => {
19506
- const content = isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {};
18791
+ const content = isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {};
19507
18792
  const vertical = resolveHeroVerticalAlignment(content);
19508
18793
  const itemsClass = vertical === "top" ? "rb-items-start" : vertical === "bottom" ? "rb-items-end" : "rb-items-center";
19509
18794
  return joinClasses$5([options.base, itemsClass]);
@@ -20260,38 +19545,19 @@ const footerBottomBarStyleTransform = {
20260
19545
  run: (value, _options, context) => {
20261
19546
  const footer = asPartialObject(value);
20262
19547
  const bottomBar = footer?.bottomBar;
20263
- return mergeStyles(bottomBar?.background?.type === "color" ? backgroundColorStyle(bottomBar.background.color ?? "surface") : undefined, footerBottomBarBorderColorStyle(footer), footerBottomBarBorderWidthStyle(bottomBar?.borderTop?.width), footerBottomBarForegroundStyle(footer, context.theme.palette), footerBottomBarInkStyle(footer, context));
19548
+ return mergeStyles(bottomBar?.background?.type === "color" ? backgroundColorStyle(bottomBar.background.color ?? "surface") : undefined, footerBottomBarBorderColorStyle(footer), footerBottomBarBorderWidthStyle(bottomBar?.borderTop?.width), ...footerBottomBarForegroundStyles(footer, context));
20264
19549
  }
20265
19550
  };
20266
- function footerBottomBarForegroundStyle(footer, palette) {
20267
- if (!footer?.bottomBar) return undefined;
20268
- const foreground = footerBottomBarSurfaceForeground(footer, palette);
20269
- return foreground === null ? backgroundlessBottomBarForegroundStyle(footer) : resolvedForegroundStyle(foreground);
20270
- }
20271
- /** A bar without its own colour paints nothing, so the footer's inks carry on. */
20272
- function footerBottomBarInkStyle(footer, context) {
20273
- const foreground = footer ? footerBottomBarSurfaceForeground(footer, context.theme.palette) : null;
20274
- return foreground === null ? undefined : solvedInkStyle(foreground, context);
20275
- }
20276
- function footerBottomBarSurfaceForeground(footer, palette) {
20277
- const bar = footer.bottomBar;
20278
- if (bar?.background?.type !== "color") return null;
20279
- return resolveSurfaceForeground(bar.background, palette, {
20280
- text: bar.textColor,
20281
- link: footer.bottomTextLinkStyle?.colorToken ?? bar.textColor,
20282
- linkHover: footer.bottomTextLinkStyle?.hoverColorToken
20283
- }, { compositeBase: measuredFooterBackdrop(footer, palette) });
20284
- }
20285
- function backgroundlessBottomBarForegroundStyle(footer) {
20286
- if (footer.background?.contentOwner === siteChromeBackgroundOverrideOwner) {
20287
- return undefined;
19551
+ /** The bar's own certified contract and inks, its certified text on the footer's ground, or nothing of its own. */
19552
+ function footerBottomBarForegroundStyles(footer, context) {
19553
+ if (!footer) return [];
19554
+ const paint = resolveFooterBottomBarPaint(footer, context.theme.palette);
19555
+ switch (paint.kind) {
19556
+ case "inherits": return [];
19557
+ case "surface": return [resolvedForegroundStyle(paint.foreground), solvedInkStyle(paint.foreground, context)];
19558
+ case "unmeasured": return [textColorStyle(paint.declared)];
19559
+ case "ink": return [paint.paint.fallback ? { color: paint.paint.ink } : textColorStyle(paint.declared)];
20288
19560
  }
20289
- const textColor = footer.bottomBar?.textColor;
20290
- return textColor ? textColorStyle(textColor) : undefined;
20291
- }
20292
- function measuredFooterBackdrop(footer, palette) {
20293
- const foreground = resolveFooterRootForeground(footer, palette);
20294
- return foreground.kind === "known" && foreground.provenance.kind === "measured" ? foreground.provenance.backdropHex : null;
20295
19561
  }
20296
19562
  const footerLogoClassSchema = z.object({ base: z.string().optional() });
20297
19563
  const footerLogoClassTransform = {
@@ -20631,7 +19897,7 @@ function visitRichTextNode(node, allowedTokens, tokenValues) {
20631
19897
  return changed ? nextNode : node;
20632
19898
  }
20633
19899
  function interpolateRichTextTokens(value, options, context) {
20634
- if (!isRecord$17(value)) return value;
19900
+ if (typeof value !== "string" && !isRecord$17(value)) return value;
20635
19901
  const allowedTokens = new Set(options.tokens);
20636
19902
  const tokenValues = {
20637
19903
  year: () => formatDate({
@@ -20643,6 +19909,13 @@ function interpolateRichTextTokens(value, options, context) {
20643
19909
  }),
20644
19910
  site_name: () => getSiteName(context)
20645
19911
  };
19912
+ if (typeof value === "string") {
19913
+ const plainTextValues = {
19914
+ ...tokenValues,
19915
+ site_name: () => getSiteName(context) ?? ""
19916
+ };
19917
+ return replaceTokenText(value, allowedTokens, plainTextValues).trim();
19918
+ }
20646
19919
  return visitRichTextNode(value, allowedTokens, tokenValues);
20647
19920
  }
20648
19921
  const interpolateRichTextTokensTransform = {
@@ -20803,6 +20076,112 @@ function visibleHeaderClearancePx(box) {
20803
20076
  if (box.height <= 0 || box.bottom <= 0) return 0;
20804
20077
  return Math.ceil(box.bottom);
20805
20078
  }
20079
+ /**
20080
+ * How far a sticky or fixed site header (with any banner stuck alongside it)
20081
+ * reaches below the top of its scrollport. Each site document layout measures
20082
+ * its own and sets it on its root, so sticky page columns and anchor targets
20083
+ * inside it stay below the header.
20084
+ */
20085
+ const SITE_STICKY_CHROME_HEIGHT_CSS_VAR = "--site-sticky-chrome-height";
20086
+ /** Rendered on the server until the layout has measured its header. */
20087
+ const SITE_STICKY_CHROME_HEIGHT_FALLBACK = SITE_OVERLAY_HEADER_CLEARANCE_FALLBACK;
20088
+ const SITE_HEADER_SELECTOR = ".block-site-header";
20089
+ /** The site's announcement banner. The preview passes its own wrapper. */
20090
+ const SITE_BANNER_SELECTOR = "[data-site-banner=\"true\"]";
20091
+ /** Sub-pixel rounding between a header and the banner stacked under it. */
20092
+ const SITE_CHROME_STACK_TOLERANCE_PX = 2;
20093
+ /**
20094
+ * The header plus any announcement banner stacked against it, above or below.
20095
+ * A banner elsewhere on the page doesn't count, and nor does chrome scrolled
20096
+ * out of view (null).
20097
+ */
20098
+ function stackSiteTopChrome(input) {
20099
+ if (!input.header || !isVisibleChromeBox(input.header)) return null;
20100
+ return input.banners.filter(isVisibleChromeBox).slice().sort((left, right) => left.top - right.top).reduce((edges, banner) => banner.top <= edges.bottom + SITE_CHROME_STACK_TOLERANCE_PX && banner.bottom >= edges.top - SITE_CHROME_STACK_TOLERANCE_PX ? {
20101
+ top: Math.min(edges.top, banner.top),
20102
+ bottom: Math.max(edges.bottom, banner.bottom)
20103
+ } : edges, input.header);
20104
+ }
20105
+ /** How far down the viewport the site's top chrome reaches, in whole pixels. */
20106
+ function siteTopChromeBottomPx(input) {
20107
+ const stack$1 = stackSiteTopChrome(input);
20108
+ return stack$1 ? Math.ceil(stack$1.bottom) : 0;
20109
+ }
20110
+ /**
20111
+ * How far the top chrome reaches below its own top edge (chromeTop, the outer
20112
+ * header box, which sits at the top of the scrollport once stuck), in the
20113
+ * layout's CSS pixels. Measuring from the chrome rather than the viewport keeps
20114
+ * it right inside the editor preview, which sits below the editor's toolbar and
20115
+ * may be zoomed.
20116
+ */
20117
+ function siteStickyChromeHeightPx(input) {
20118
+ const stack$1 = stackSiteTopChrome(input);
20119
+ if (!stack$1) return 0;
20120
+ const zoom = Number.isFinite(input.zoom) && input.zoom > 0 ? input.zoom : 1;
20121
+ const top = Math.min(input.chromeTop, stack$1.top);
20122
+ return Math.ceil((stack$1.bottom - top) / zoom);
20123
+ }
20124
+ function isVisibleChromeBox(box) {
20125
+ return Number.isFinite(box.top) && Number.isFinite(box.bottom) && box.bottom > box.top && box.bottom > 0;
20126
+ }
20127
+ function findSiteTopChrome(scope, bannerSelector) {
20128
+ const outerHeader = scope.querySelector(SITE_HEADER_SELECTOR);
20129
+ const headerCandidates = outerHeader ? Array.from(outerHeader.querySelectorAll(SITE_CHROME_VISIBLE_HEADER_SELECTOR)) : [];
20130
+ return {
20131
+ outerHeader,
20132
+ header: outerHeader ? visibleHeaderPart(outerHeader, headerCandidates) : null,
20133
+ headerCandidates,
20134
+ banners: Array.from(scope.querySelectorAll(bannerSelector))
20135
+ };
20136
+ }
20137
+ function chromeBoxesOf(elements) {
20138
+ return {
20139
+ header: elements.header ? chromeBox(elements.header) : null,
20140
+ banners: elements.banners.map(chromeBox)
20141
+ };
20142
+ }
20143
+ /** One live reading of {@link siteTopChromeBottomPx} from the document. */
20144
+ function measureSiteTopChromeBottomPx(bannerSelector = SITE_BANNER_SELECTOR) {
20145
+ return siteTopChromeBottomPx(chromeBoxesOf(findSiteTopChrome(document, bannerSelector)));
20146
+ }
20147
+ /** One live reading of {@link siteStickyChromeHeightPx} for one site document layout. */
20148
+ function measureSiteStickyChromeHeightPx(layoutRoot) {
20149
+ const elements = findSiteTopChrome(layoutRoot, SITE_BANNER_SELECTOR);
20150
+ if (!elements.outerHeader) return 0;
20151
+ return siteStickyChromeHeightPx({
20152
+ ...chromeBoxesOf(elements),
20153
+ chromeTop: elements.outerHeader.getBoundingClientRect().top,
20154
+ zoom: cssZoomOf(layoutRoot)
20155
+ });
20156
+ }
20157
+ /** Clearance boxes and marked bars that can gain intrinsic height inside box-less wrappers. */
20158
+ function siteTopChromeMeasurementTargets(bannerSelector = SITE_BANNER_SELECTOR, scope = document) {
20159
+ const { header, headerCandidates, banners } = findSiteTopChrome(scope, bannerSelector);
20160
+ return [
20161
+ ...header ? [header] : [],
20162
+ ...headerCandidates,
20163
+ ...banners
20164
+ ];
20165
+ }
20166
+ /** The header's visible bar, when it marks one, rather than its whole box. */
20167
+ function visibleHeaderPart(header, candidates) {
20168
+ return candidates.find((candidate) => {
20169
+ const rect = candidate.getBoundingClientRect();
20170
+ return window.getComputedStyle(candidate).display !== "none" && rect.height > 0 && rect.bottom > 0;
20171
+ }) ?? header;
20172
+ }
20173
+ function chromeBox(element) {
20174
+ const rect = element.getBoundingClientRect();
20175
+ return {
20176
+ top: rect.top,
20177
+ bottom: rect.bottom
20178
+ };
20179
+ }
20180
+ /** The CSS zoom applied to an element (the editor preview zooms its document). */
20181
+ function cssZoomOf(element) {
20182
+ const zoom = Reflect.get(element, "currentCSSZoom");
20183
+ return typeof zoom === "number" ? zoom : 1;
20184
+ }
20806
20185
 
20807
20186
  //#endregion
20808
20187
  //#region ../blocks/src/system/blocks/site-header.ts
@@ -21124,9 +20503,9 @@ const brandLogo = link$1({
21124
20503
  from: "$root.theme.footer.logo",
21125
20504
  transforms: [{
21126
20505
  id: "layout.footerLogoClass",
21127
- options: { base: "footer-logo rb-inline-flex rb-items-center rb-gap-3 rb-no-underline" }
20506
+ options: { base: "footer-logo rb-inline-flex rb-flex-wrap rb-items-center rb-gap-3 rb-no-underline" }
21128
20507
  }],
21129
- fallback: "footer-logo rb-inline-flex rb-items-center rb-gap-3 rb-no-underline"
20508
+ fallback: "footer-logo rb-inline-flex rb-flex-wrap rb-items-center rb-gap-3 rb-no-underline"
21130
20509
  } }
21131
20510
  }, [brandMedia, brandTitle], (node) => ({
21132
20511
  ...node,
@@ -21233,7 +20612,8 @@ const authoredCopy = richText({
21233
20612
  }] }));
21234
20613
  const footerEngagement = stack({
21235
20614
  gap: "md",
21236
- className: "rb-w-full"
20615
+ className: "rb-w-full",
20616
+ "data-footer-engagement": ""
21237
20617
  }, [
21238
20618
  text({
21239
20619
  as: "p",
@@ -21248,10 +20628,21 @@ const footerEngagement = stack({
21248
20628
  ...footerSpamProtectionBinding
21249
20629
  }), [], when("module.engagement.kind", { equals: "mailing-list" }))
21250
20630
  ], when("module.kind", { equals: "engagement" }));
20631
+ const MACADAMIA_NUT_PATH = "M695.95 432.12C703.56 441.79 712.58 444.68 722.92 450.58C734.35 457.11 744.89 465 754.42 474.07C800.52 517.98 814.25 592.12 777.56 646.07C769.9 657.35 760.46 667.11 753.15 678.67C750.79 682.39 748.47 690.26 743.47 690.98C739.99 691.49 735.89 689.93 732.5 689.2C725.87 687.78 719.29 686.48 712.5 686.05C682.8 684.2 655.61 683.31 628.67 668.83C570.03 637.3 541.86 560.59 572.51 500C585.09 475.13 607.1 455 632.83 444.39C638.62 442 644.49 439.78 650.56 438.19C655.41 436.93 660.62 436.52 665.33 434.79C668.19 433.74 670.85 432.04 673.57 430.68C677.42 429.93 681.48 428 685.46 428.25C689.27 428.49 692.49 430.99 695.95 432.12Z " + "M684.5 466.17C723.47 503.72 749.19 550.28 748.93 605.5C748.88 617.14 748.67 629.96 745.5 641.21C743.7 631.07 742.83 620.81 740.66 610.71C734.59 582.42 725.07 554.31 713.82 527.66C707.95 513.78 701.99 499.91 694.94 486.57C692.46 481.89 684.79 470.62 684.5 466.17Z";
20632
+ const macadamiaNut = el("svg", {
20633
+ viewBox: "556 422 262 274",
20634
+ className: "rb-footer-credit-mark",
20635
+ shapes: [{
20636
+ kind: "path",
20637
+ d: MACADAMIA_NUT_PATH,
20638
+ fillRule: "evenodd",
20639
+ fill: "currentColor"
20640
+ }]
20641
+ });
21251
20642
  const platformAttribution = el("div", {
21252
- className: "rb-caption",
20643
+ className: "rb-caption rb-footer-credit",
21253
20644
  "data-site-attribution": "macadamia"
21254
- }, [
20645
+ }, [macadamiaNut, el("div", null, [
21255
20646
  text({ as: "span" }, bind("module.lead")),
21256
20647
  text({
21257
20648
  as: "span",
@@ -21261,7 +20652,14 @@ const platformAttribution = el("div", {
21261
20652
  href: { $bind: { from: "module.link.href" } },
21262
20653
  className: "footer-nav-link rb-caption transition-theme-standard"
21263
20654
  }, [text({ as: "span" }, bind("module.link.label"))])
21264
- ], when("module.kind", { equals: "attribution" }));
20655
+ ])], when("module.kind", { equals: "attribution" }));
20656
+ const copyright = text({
20657
+ as: "p",
20658
+ className: "rb-caption"
20659
+ }, bind("module.text", { transforms: [{
20660
+ id: "content.interpolateRichTextTokens",
20661
+ options: { tokens: ["year", "site_name"] }
20662
+ }] }), when("module.kind", { equals: "copyright" }));
21265
20663
  const footerModule = stack({
21266
20664
  gap: "md",
21267
20665
  align: { $bind: {
@@ -21269,6 +20667,7 @@ const footerModule = stack({
21269
20667
  fallback: "start"
21270
20668
  } },
21271
20669
  className: { $bind: { from: "module.className" } },
20670
+ "data-footer-module": { $bind: { from: "module.kind" } },
21272
20671
  "data-preview-footer-module-id": { $bind: { from: "module.id" } }
21273
20672
  }, [
21274
20673
  text({
@@ -21296,6 +20695,7 @@ const footerModule = stack({
21296
20695
  hours,
21297
20696
  socials,
21298
20697
  footerEngagement,
20698
+ copyright,
21299
20699
  platformAttribution,
21300
20700
  stack({ className: "rb-w-full" }, [authoredCopy], when("module.kind", { equals: "supporting-text" })),
21301
20701
  stack({ className: "rb-w-full rb-lg-col-span-3" }, [authoredCopy], when("module.kind", { equals: "legal" }))
@@ -23959,6 +23359,8 @@ const publicEventDestinationSchema = z.discriminatedUnion("kind", [z.object({
23959
23359
  * Zod schema for venue data
23960
23360
  */
23961
23361
  const eventVenueSchema = z.object({
23362
+ area: z.string().nullable().optional(),
23363
+ addressWithBooking: z.boolean().optional(),
23962
23364
  drawing: z.lazy(() => mapDrawingSchema).optional().catch(undefined),
23963
23365
  id: z.string(),
23964
23366
  name: z.string(),
@@ -27373,6 +26775,17 @@ function formatEventLocation(event) {
27373
26775
  case "in_person": return event.venue?.name ?? "In person";
27374
26776
  }
27375
26777
  }
26778
+ /** The line under the venue name. A booked-only venue shows its area, and the address follows the booking. */
26779
+ function eventVenuePlace(venue) {
26780
+ if (venue.addressWithBooking) return {
26781
+ place: venue.area ?? null,
26782
+ addressWithBooking: true
26783
+ };
26784
+ return {
26785
+ place: venue.address,
26786
+ addressWithBooking: false
26787
+ };
26788
+ }
27376
26789
 
27377
26790
  //#endregion
27378
26791
  //#region ../blocks/src/system/runtime/nodes/events/domain/people.ts
@@ -27662,9 +27075,10 @@ function buildBookingInstalmentCadenceLabel(plan) {
27662
27075
 
27663
27076
  //#endregion
27664
27077
  //#region ../blocks/src/system/runtime/nodes/event-registration/bookingCalendar.ts
27665
- /** Public schedule facts only; online joining instructions stay in email (issue 1580). */
27666
- function bookingCalendar(event, identity) {
27078
+ /** Public schedule plus the authorised outcome's arrival text; online access stays in email. */
27079
+ function bookingCalendar(event, outcome) {
27667
27080
  if (!event) return null;
27081
+ const { identity } = outcome;
27668
27082
  const sessions = event.registrationSubject.kind === "wholeSeries" ? event.registrationSubject.sessions : [{
27669
27083
  occurrenceId: event.registrationSubject.occurrenceId,
27670
27084
  startsAt: event.startsAt,
@@ -27679,7 +27093,7 @@ function bookingCalendar(event, identity) {
27679
27093
  endsAt: new Date(session.endsAt),
27680
27094
  timezone: "UTC",
27681
27095
  status: "confirmed",
27682
- location: identity.attendanceMode === "online" ? "Online" : event.venue?.name,
27096
+ location: identity.attendanceMode === "online" ? "Online" : outcome.arrivalVenues?.[session.occurrenceId] ?? event.venue?.name,
27683
27097
  description: identity.attendanceMode === "online" ? "See your confirmation email for joining instructions." : undefined
27684
27098
  }));
27685
27099
  const first = entries[0];
@@ -28235,11 +27649,11 @@ function getStepNumber(step, wizardSteps) {
28235
27649
  * theme's input CSS (`.form-check`, `.form-checkbox`), so it follows each
28236
27650
  * site's input colours, borders and corners.
28237
27651
  */
28238
- function CheckboxChoice({ label, labelLink, presentation = "row", className, ...inputProps }) {
27652
+ function CheckboxChoice({ label, labelLink, presentation: presentation$1 = "row", className, ...inputProps }) {
28239
27653
  const generatedId = import_react.useId();
28240
27654
  const inputId = inputProps.id ?? generatedId;
28241
27655
  if (labelLink) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
28242
- className: clsx_default("form-check", "form-check--linked", presentation === "tile" && "form-check--tile", className),
27656
+ className: clsx_default("form-check", "form-check--linked", presentation$1 === "tile" && "form-check--tile", className),
28243
27657
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
28244
27658
  ...inputProps,
28245
27659
  id: inputId,
@@ -28256,7 +27670,7 @@ function CheckboxChoice({ label, labelLink, presentation = "row", className, ...
28256
27670
  })]
28257
27671
  });
28258
27672
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
28259
- className: clsx_default("form-check", presentation === "tile" && "form-check--tile", className),
27673
+ className: clsx_default("form-check", presentation$1 === "tile" && "form-check--tile", className),
28260
27674
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
28261
27675
  ...inputProps,
28262
27676
  type: "checkbox",
@@ -29021,7 +28435,7 @@ function MembershipHero({ membershipName, classPrefix, onBookForSomeoneElse }) {
29021
28435
  * LICENSE file in the root directory of this source tree.
29022
28436
  */
29023
28437
  var require_react_dom_production = /* @__PURE__ */ __commonJSMin(((exports) => {
29024
- var React$82 = require_react();
28438
+ var React$83 = require_react();
29025
28439
  function formatProdErrorMessage(code) {
29026
28440
  var url = "https://react.dev/errors/" + code;
29027
28441
  if (1 < arguments.length) {
@@ -29058,7 +28472,7 @@ var require_react_dom_production = /* @__PURE__ */ __commonJSMin(((exports) => {
29058
28472
  implementation
29059
28473
  };
29060
28474
  }
29061
- var ReactSharedInternals = React$82.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
28475
+ var ReactSharedInternals = React$83.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
29062
28476
  function getCrossOriginStringAs(as, input) {
29063
28477
  if ("font" === as) return "";
29064
28478
  if ("string" === typeof input) return "use-credentials" === input ? input : "";
@@ -29201,7 +28615,7 @@ var require_react_dom_development = /* @__PURE__ */ __commonJSMin(((exports) =>
29201
28615
  return dispatcher;
29202
28616
  }
29203
28617
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
29204
- var React$91 = require_react(), Internals$1 = {
28618
+ var React$92 = require_react(), Internals$1 = {
29205
28619
  d: {
29206
28620
  f: noop$2,
29207
28621
  r: function() {
@@ -29217,7 +28631,7 @@ var require_react_dom_development = /* @__PURE__ */ __commonJSMin(((exports) =>
29217
28631
  },
29218
28632
  p: 0,
29219
28633
  findDOMNode: null
29220
- }, REACT_PORTAL_TYPE$1 = Symbol.for("react.portal"), ReactSharedInternals$1 = React$91.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
28634
+ }, REACT_PORTAL_TYPE$1 = Symbol.for("react.portal"), ReactSharedInternals$1 = React$92.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
29221
28635
  "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");
29222
28636
  exports.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = Internals$1;
29223
28637
  exports.createPortal = function(children, container) {
@@ -29615,44 +29029,43 @@ function clampIndex(idx, len) {
29615
29029
  *
29616
29030
  * Includes basic keyboard navigation (ArrowLeft/Right, Home/End) and focuses the next tab.
29617
29031
  */
29618
- function TabBarFragment({ tabs, activeTab, onTabChange, ariaLabel, className, tabClassName, activeTabClassName }) {
29032
+ function TabBarFragment({ tabs, activeTab, onTabChange, ariaLabel, listAttributes, tabAttributes, className, tabClassName, activeTabClassName }) {
29619
29033
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
29620
29034
  className,
29035
+ ...listAttributes,
29621
29036
  role: "tablist",
29622
29037
  "aria-label": ariaLabel,
29623
29038
  children: tabs.map((tab, idx) => {
29624
29039
  const isActive = tab.id === activeTab;
29625
29040
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
29626
29041
  type: "button",
29042
+ ...tabAttributes?.(isActive),
29627
29043
  role: "tab",
29628
29044
  "aria-selected": isActive,
29629
29045
  tabIndex: isActive ? 0 : -1,
29630
29046
  className: clsx_default(tabClassName, isActive && activeTabClassName),
29631
29047
  onClick: () => onTabChange(tab.id),
29632
- onKeyDown: (e$1) => {
29633
- if (tabs.length === 0) return;
29634
- if (!(e$1.currentTarget instanceof HTMLButtonElement)) return;
29635
- let nextIdx = null;
29636
- if (e$1.key === "ArrowRight") nextIdx = clampIndex(idx + 1, tabs.length);
29637
- else if (e$1.key === "ArrowLeft") nextIdx = clampIndex(idx - 1, tabs.length);
29638
- else if (e$1.key === "Home") nextIdx = 0;
29639
- else if (e$1.key === "End") nextIdx = tabs.length - 1;
29640
- if (nextIdx === null) return;
29641
- e$1.preventDefault();
29642
- const nextId = tabs[nextIdx]?.id;
29643
- if (!nextId) return;
29644
- onTabChange(nextId);
29645
- requestAnimationFrame(() => {
29646
- const root = e$1.currentTarget.closest("[role=\"tablist\"]") ?? null;
29647
- const btns = root?.querySelectorAll("[role=\"tab\"]") ?? null;
29648
- btns?.[nextIdx]?.focus();
29649
- });
29650
- },
29048
+ onKeyDown: (e$1) => handleTabKey(e$1, tabs, idx, onTabChange),
29651
29049
  children: tab.label
29652
29050
  }, tab.id);
29653
29051
  })
29654
29052
  });
29655
29053
  }
29054
+ function handleTabKey(e$1, tabs, idx, onTabChange) {
29055
+ if (tabs.length === 0) return;
29056
+ if (!(e$1.currentTarget instanceof HTMLButtonElement)) return;
29057
+ const nextIdx = e$1.key === "ArrowRight" ? clampIndex(idx + 1, tabs.length) : e$1.key === "ArrowLeft" ? clampIndex(idx - 1, tabs.length) : e$1.key === "Home" ? 0 : e$1.key === "End" ? tabs.length - 1 : null;
29058
+ if (nextIdx === null) return;
29059
+ e$1.preventDefault();
29060
+ const nextId = tabs[nextIdx]?.id;
29061
+ if (!nextId) return;
29062
+ onTabChange(nextId);
29063
+ const tabList = e$1.currentTarget.closest("[role=\"tablist\"]");
29064
+ requestAnimationFrame(() => {
29065
+ const btns = tabList?.querySelectorAll("[role=\"tab\"]") ?? null;
29066
+ btns?.[nextIdx]?.focus();
29067
+ });
29068
+ }
29656
29069
 
29657
29070
  //#endregion
29658
29071
  //#region ../blocks/src/system/runtime/shared/components/TerminalPanel.tsx
@@ -29829,12 +29242,12 @@ function formatBookingWhen(input, variant) {
29829
29242
  year: "omit",
29830
29243
  zone
29831
29244
  };
29832
- const presentation = BOOKING_DATE_PRESENTATIONS[variant];
29833
- switch (presentation.kind) {
29245
+ const presentation$1 = BOOKING_DATE_PRESENTATIONS[variant];
29246
+ switch (presentation$1.kind) {
29834
29247
  case "date": return formatDate({
29835
29248
  kind: "instant",
29836
29249
  value: start.value
29837
- }, context, presentation.options);
29250
+ }, context, presentation$1.options);
29838
29251
  case "time": return formatTime$1(start.value, context, { zone });
29839
29252
  case "dateTime": return formatDateTime(start.value, context, options);
29840
29253
  case "timeRange": return span ? formatRegistrationClockRange(span, context, options) : formatTime$1(start.value, context, { zone });
@@ -30190,15 +29603,20 @@ function bookedBooking(result, context) {
30190
29603
  const facts = outcomeFacts(result.outcome);
30191
29604
  const totalPlaces = completedBookingPlaces(context.completedOutcomes, identity);
30192
29605
  const where = identity.attendanceMode === "online" ? "none" : "venue";
29606
+ const arrival = identity.attendanceMode === "in_person" && context.display.venue === "shown" ? [...new Set(Object.values(result.outcome.arrivalVenues ?? {}))].join("\n\n") : "";
30193
29607
  return {
30194
29608
  entry: {
30195
29609
  kind: "booked",
30196
29610
  ...dates,
30197
29611
  places: totalPlaces === null ? null : placesLabel$1(totalPlaces)
30198
29612
  },
30199
- summary: [...scheduleRows(event, dates.shortWhen, where, context.display), ...facts.rows],
29613
+ summary: [
29614
+ ...scheduleRows(event, dates.shortWhen, arrival ? "none" : where, context.display),
29615
+ ...row("Where", arrival),
29616
+ ...facts.rows
29617
+ ],
30200
29618
  payment: facts.payment,
30201
- calendar: bookingCalendar(event, identity)
29619
+ calendar: bookingCalendar(event, result.outcome)
30202
29620
  };
30203
29621
  }
30204
29622
  function waitlistedBooking(context) {
@@ -30218,15 +29636,15 @@ function waitlistedBooking(context) {
30218
29636
  * The one place a finished booking is resolved: the confirmation, the
30219
29637
  * finished card and the bar all take this, never the raw result and events.
30220
29638
  */
30221
- function resolveFinishedPresentation(presentation, context) {
30222
- if (presentation.layout !== "whole") return presentation;
30223
- switch (presentation.screen) {
29639
+ function resolveFinishedPresentation(presentation$1, context) {
29640
+ if (presentation$1.layout !== "whole") return presentation$1;
29641
+ switch (presentation$1.screen) {
30224
29642
  case "confirmed": return {
30225
- ...presentation,
30226
- finished: bookedBooking(presentation.result, context)
29643
+ ...presentation$1,
29644
+ finished: bookedBooking(presentation$1.result, context)
30227
29645
  };
30228
29646
  case "waitlisted": return {
30229
- ...presentation,
29647
+ ...presentation$1,
30230
29648
  finished: waitlistedBooking(context)
30231
29649
  };
30232
29650
  case "processing":
@@ -30234,8 +29652,8 @@ function resolveFinishedPresentation(presentation, context) {
30234
29652
  case "verifyingTimeout":
30235
29653
  case "paymentStatus":
30236
29654
  case "paymentFailed":
30237
- case "error": return presentation;
30238
- default: return assertNever(presentation);
29655
+ case "error": return presentation$1;
29656
+ default: return assertNever(presentation$1);
30239
29657
  }
30240
29658
  }
30241
29659
 
@@ -32253,10 +31671,11 @@ function findPreSelectedEvent(input) {
32253
31671
  })) === contextDate) ?? null;
32254
31672
  }
32255
31673
  function resolveRegistrationTarget(input) {
32256
- const subject = input.occurrenceContext ? {
31674
+ const selectedSubject = input.selectedEvent?.registrationSubject ?? null;
31675
+ const subject = selectedSubject?.kind === "wholeSeries" ? selectedSubject : input.occurrenceContext ? {
32257
31676
  kind: "occurrence",
32258
31677
  occurrenceId: input.occurrenceContext.id
32259
- } : input.selectedEvent?.registrationSubject ?? null;
31678
+ } : selectedSubject;
32260
31679
  if (!subject) return null;
32261
31680
  return {
32262
31681
  subject,
@@ -36773,8 +36192,8 @@ function ensureStripeCheckoutSessionIdInReturnUrl(returnUrl) {
36773
36192
  * (hosted) or return (embedded) URL, with the session filled in where Stripe
36774
36193
  * would have put it.
36775
36194
  */
36776
- function completedStripeCheckoutReturnUrl(presentation, sessionId) {
36777
- const url = new URL(presentation.mode === "hosted" ? presentation.successUrl : presentation.returnUrl);
36195
+ function completedStripeCheckoutReturnUrl(presentation$1, sessionId) {
36196
+ const url = new URL(presentation$1.mode === "hosted" ? presentation$1.successUrl : presentation$1.returnUrl);
36778
36197
  url.searchParams.set("session_id", sessionId);
36779
36198
  return url.toString();
36780
36199
  }
@@ -36902,6 +36321,11 @@ function transitionOrderRefundStatusTo(from, to) {
36902
36321
  //#region ../commerce/src/purchase/event-registration-errors.ts
36903
36322
  const unfinishedBookingAssistanceMessage = "We couldn’t safely find your unfinished booking. Please contact the organiser before trying to book again.";
36904
36323
  const eventRegistrationErrors = {
36324
+ wholeCourseOnly: {
36325
+ endsCommand: false,
36326
+ message: "This course is booked as a whole course, so single dates can’t be booked on their own. Please go back to the event and book the full course.",
36327
+ action: "event"
36328
+ },
36905
36329
  registrationClosed: {
36906
36330
  endsCommand: false,
36907
36331
  message: "Booking is currently closed for this event. Please contact the organiser if you’d like to find out more.",
@@ -37917,31 +37341,31 @@ function selectApprovedEventDirectPaymentOptions(approvedPaymentOptions, payment
37917
37341
  return approvedKinds.has("upfront") && !configured.some((option) => option.kind === "upfront") ? [...configured, { kind: "upfront" }] : configured;
37918
37342
  }
37919
37343
  function toEventDirectPaymentOption(option, mode, totalCents) {
37920
- const { dueNowCents: configuredPaymentDueNowCents, ...presentation } = option;
37344
+ const { dueNowCents: configuredPaymentDueNowCents, ...presentation$1 } = option;
37921
37345
  switch (option.kind) {
37922
37346
  case "upfront": return {
37923
- ...presentation,
37347
+ ...presentation$1,
37924
37348
  kind: "upfront",
37925
37349
  label: mode === "choice_required" ? "Pay in full now" : "Pay by card",
37926
37350
  totalCents,
37927
37351
  configuredPaymentDueNowCents
37928
37352
  };
37929
37353
  case "deferred_manual": return {
37930
- ...presentation,
37354
+ ...presentation$1,
37931
37355
  kind: "deferred_manual",
37932
37356
  label: option.title,
37933
37357
  totalCents,
37934
37358
  configuredPaymentDueNowCents
37935
37359
  };
37936
37360
  case "instalment_plan": return {
37937
- ...presentation,
37361
+ ...presentation$1,
37938
37362
  kind: "instalment_plan",
37939
37363
  label: option.title,
37940
37364
  totalCents,
37941
37365
  configuredPaymentDueNowCents
37942
37366
  };
37943
37367
  case "flexible_balance": return {
37944
- ...presentation,
37368
+ ...presentation$1,
37945
37369
  kind: "flexible_balance",
37946
37370
  label: option.presentationKind === "upfront" ? option.title : "Pay deposit now",
37947
37371
  totalCents,
@@ -38726,19 +38150,19 @@ function bookingEntry(input) {
38726
38150
  * finished once it is confirmed or waitlisted.
38727
38151
  */
38728
38152
  function bookingEntryProgress(input) {
38729
- const { presentation } = input;
38730
- if (presentation.layout === "whole") {
38731
- return presentation.screen === "confirmed" || presentation.screen === "waitlisted" ? {
38153
+ const { presentation: presentation$1 } = input;
38154
+ if (presentation$1.layout === "whole") {
38155
+ return presentation$1.screen === "confirmed" || presentation$1.screen === "waitlisted" ? {
38732
38156
  underway: false,
38733
38157
  progress: null,
38734
- finished: presentation.finished
38158
+ finished: presentation$1.finished
38735
38159
  } : {
38736
38160
  underway: true,
38737
38161
  progress: null,
38738
38162
  finished: null
38739
38163
  };
38740
38164
  }
38741
- const progress = presentation.progress;
38165
+ const progress = presentation$1.progress;
38742
38166
  const step = progress ? progress.steps[progress.current - 1] : undefined;
38743
38167
  if (!progress || progress.current <= 1 || !step) {
38744
38168
  return {
@@ -40322,14 +39746,17 @@ const bookedTicketLineSchema = z.object({
40322
39746
  label: z.string().min(1),
40323
39747
  quantity: z.number().int().positive()
40324
39748
  }).strict();
39749
+ const arrivalFields = { arrivalVenues: z.record(uuid, z.string()).optional() };
40325
39750
  const eventBookingOutcomeSchema = z.discriminatedUnion("kind", [z.object({
40326
39751
  kind: z.literal("detailsAvailable"),
40327
39752
  identity: confirmedBookingIdentitySchema,
40328
39753
  ticketLines: z.tuple([bookedTicketLineSchema]).rest(bookedTicketLineSchema).readonly(),
40329
- payment: confirmationPaymentSchema
39754
+ payment: confirmationPaymentSchema,
39755
+ ...arrivalFields
40330
39756
  }).strict(), z.object({
40331
39757
  kind: z.literal("detailsUnavailable"),
40332
- identity: confirmedBookingIdentitySchema
39758
+ identity: confirmedBookingIdentitySchema,
39759
+ ...arrivalFields
40333
39760
  }).strict()]);
40334
39761
  const eventEmbeddedCheckoutSchema = z.object({
40335
39762
  kind: z.literal("embedded"),
@@ -40495,6 +39922,8 @@ const eventCheckoutResponseSchema = z.union([
40495
39922
 
40496
39923
  //#endregion
40497
39924
  //#region ../blocks/src/contracts/events.ts
39925
+ /** Release 1: older SDKs reject additional confirmed-outcome fields. */
39926
+ const EVENT_BOOKING_ARRIVAL_QUERY = "bookingArrival";
40498
39927
  const eventAutoApplyDiscountPreviewBaseSchema = z.object({
40499
39928
  id: z.string(),
40500
39929
  publicLabel: z.string(),
@@ -42674,8 +42103,8 @@ function selectCardListPresentation(source) {
42674
42103
  ...cardListPresentationKeys,
42675
42104
  "buttonVariant",
42676
42105
  "bodyParts"
42677
- ].reduce((presentation, key) => ({
42678
- ...presentation,
42106
+ ].reduce((presentation$1, key) => ({
42107
+ ...presentation$1,
42679
42108
  [key]: source[key]
42680
42109
  }), {});
42681
42110
  }
@@ -42696,93 +42125,93 @@ function resolveCardListSchedule(item, mode) {
42696
42125
  /** Each part owns optional-content collapse once, for calendar and catalogue items. */
42697
42126
  const partRenderers = {
42698
42127
  title: CardListTitle,
42699
- text: ({ item, presentation }) => item.textDocument ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RichText, {
42128
+ text: ({ item, presentation: presentation$1 }) => item.textDocument ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RichText, {
42700
42129
  doc: item.textDocument,
42701
- ...listLookSlot(presentation, "item-text"),
42702
- lookSlots: presentation.lookSlots
42130
+ ...listLookSlot(presentation$1, "item-text"),
42131
+ lookSlots: presentation$1.lookSlots
42703
42132
  }) : item.text ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
42704
- ...listLookSlot(presentation, "item-text"),
42133
+ ...listLookSlot(presentation$1, "item-text"),
42705
42134
  children: item.text
42706
42135
  }) : null,
42707
42136
  media: CardListMedia,
42708
- facts: ({ item, presentation }) => item.facts?.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dl", {
42709
- ...listLookSlot(presentation, "facts"),
42137
+ facts: ({ item, presentation: presentation$1 }) => item.facts?.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dl", {
42138
+ ...listLookSlot(presentation$1, "facts"),
42710
42139
  children: item.facts.map(({ label, value }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
42711
42140
  "data-fact": label.toLowerCase(),
42712
42141
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
42713
- ...listLookSlot(presentation, "fact-label"),
42142
+ ...listLookSlot(presentation$1, "fact-label"),
42714
42143
  children: label
42715
42144
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
42716
- ...listLookSlot(presentation, "fact-value"),
42145
+ ...listLookSlot(presentation$1, "fact-value"),
42717
42146
  children: value
42718
42147
  })]
42719
42148
  }, label))
42720
42149
  }) : null,
42721
- chips: ({ item, presentation }) => item.chips?.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
42722
- ...listLookSlot(presentation, "chips"),
42150
+ chips: ({ item, presentation: presentation$1 }) => item.chips?.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
42151
+ ...listLookSlot(presentation$1, "chips"),
42723
42152
  children: item.chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
42724
- ...listLookSlot(presentation, "chip"),
42153
+ ...listLookSlot(presentation$1, "chip"),
42725
42154
  children: chip
42726
42155
  }, chip))
42727
42156
  }) : null,
42728
- numeral: ({ item, presentation }) => item.numeral ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
42729
- ...listLookSlot(presentation, "figure"),
42157
+ numeral: ({ item, presentation: presentation$1 }) => item.numeral ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
42158
+ ...listLookSlot(presentation$1, "figure"),
42730
42159
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", {
42731
- ...listLookSlot(presentation, "figure-value"),
42160
+ ...listLookSlot(presentation$1, "figure-value"),
42732
42161
  children: item.numeral.value
42733
42162
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
42734
- ...listLookSlot(presentation, "figure-label"),
42163
+ ...listLookSlot(presentation$1, "figure-label"),
42735
42164
  children: item.numeral.label
42736
42165
  })]
42737
42166
  }) : null,
42738
- schedule: ({ item, presentation }) => item.schedule ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
42739
- ...listLookSlot(presentation, "schedule"),
42167
+ schedule: ({ item, presentation: presentation$1 }) => item.schedule ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
42168
+ ...listLookSlot(presentation$1, "schedule"),
42740
42169
  children: item.schedule
42741
42170
  }) : null,
42742
- dates: ({ item, presentation }) => item.dates?.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
42743
- ...listLookSlot(presentation, "dates"),
42171
+ dates: ({ item, presentation: presentation$1 }) => item.dates?.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
42172
+ ...listLookSlot(presentation$1, "dates"),
42744
42173
  children: item.dates.map((date, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", {
42745
- ...listLookSlot(presentation, "date"),
42174
+ ...listLookSlot(presentation$1, "date"),
42746
42175
  children: [
42747
42176
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("time", {
42748
42177
  dateTime: date.startsAt,
42749
42178
  children: date.label
42750
42179
  }),
42751
42180
  date.places ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
42752
- ...listLookSlot(presentation, "date-places"),
42181
+ ...listLookSlot(presentation$1, "date-places"),
42753
42182
  "data-availability": date.places.status,
42754
42183
  children: date.places.label
42755
42184
  }) : null,
42756
42185
  date.booking ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedButtonLink, {
42757
42186
  ...date.booking,
42758
- variant: presentation.buttonVariant ?? "primary",
42187
+ variant: presentation$1.buttonVariant ?? "primary",
42759
42188
  size: "sm",
42760
42189
  extraClassName: "rb-card-list-date-booking"
42761
42190
  }) : null
42762
42191
  ]
42763
42192
  }, `${date.startsAt}-${index}`))
42764
42193
  }) : null,
42765
- availability: ({ item, presentation }) => item.availability ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
42766
- ...listLookSlot(presentation, "availability"),
42194
+ availability: ({ item, presentation: presentation$1 }) => item.availability ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
42195
+ ...listLookSlot(presentation$1, "availability"),
42767
42196
  "data-availability": item.availability.status,
42768
42197
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
42769
42198
  "data-event-places": true,
42770
42199
  children: item.availability.label
42771
42200
  })
42772
42201
  }) : null,
42773
- price: ({ item, presentation }) => item.price ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
42774
- ...listLookSlot(presentation, "price"),
42202
+ price: ({ item, presentation: presentation$1 }) => item.price ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
42203
+ ...listLookSlot(presentation$1, "price"),
42775
42204
  children: [
42776
42205
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", {
42777
- ...listLookSlot(presentation, "price-amount"),
42206
+ ...listLookSlot(presentation$1, "price-amount"),
42778
42207
  children: item.price.amount
42779
42208
  }),
42780
42209
  item.price.unit ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
42781
- ...listLookSlot(presentation, "price-unit"),
42210
+ ...listLookSlot(presentation$1, "price-unit"),
42782
42211
  children: item.price.unit
42783
42212
  }) : null,
42784
42213
  item.price.note ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
42785
- ...listLookSlot(presentation, "price-note"),
42214
+ ...listLookSlot(presentation$1, "price-note"),
42786
42215
  children: item.price.note
42787
42216
  }) : null
42788
42217
  ]
@@ -42796,10 +42225,10 @@ const partRenderers = {
42796
42225
  function cardListItemLink(item) {
42797
42226
  return item.titleLink ?? (item.href ? { href: item.href } : null);
42798
42227
  }
42799
- function CardListTitle({ item, presentation, Title = "h3" }) {
42228
+ function CardListTitle({ item, presentation: presentation$1, Title = "h3" }) {
42800
42229
  const link$2 = cardListItemLink(item);
42801
42230
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Title, {
42802
- ...listLookSlot(presentation, "item-title"),
42231
+ ...listLookSlot(presentation$1, "item-title"),
42803
42232
  children: link$2 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
42804
42233
  ...link$2,
42805
42234
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkedLabel, {
@@ -42809,16 +42238,16 @@ function CardListTitle({ item, presentation, Title = "h3" }) {
42809
42238
  }) : item.title
42810
42239
  });
42811
42240
  }
42812
- function CardListMedia({ item, presentation }) {
42241
+ function CardListMedia({ item, presentation: presentation$1 }) {
42813
42242
  if (!item.media) return null;
42814
42243
  const link$2 = cardListItemLink(item);
42815
42244
  const photo = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MediaNodeServer, {
42816
42245
  value: item.media,
42817
42246
  size: ImagePresets.card,
42818
- ...listLookSlot(presentation, "photo")
42247
+ ...listLookSlot(presentation$1, "photo")
42819
42248
  });
42820
42249
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
42821
- ...listLookSlot(presentation, "item-media"),
42250
+ ...listLookSlot(presentation$1, "item-media"),
42822
42251
  children: link$2 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
42823
42252
  ...link$2,
42824
42253
  className: "rb-block rb-h-full",
@@ -42830,36 +42259,36 @@ function CardListMedia({ item, presentation }) {
42830
42259
  function CardListContentPart({ part, ...props$1 }) {
42831
42260
  return partRenderers[part](props$1);
42832
42261
  }
42833
- function CardListLeadDate({ item, presentation }) {
42262
+ function CardListLeadDate({ item, presentation: presentation$1 }) {
42834
42263
  const date = item.leadDate;
42835
42264
  return date ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("time", {
42836
- ...listLookSlot(presentation, "date"),
42265
+ ...listLookSlot(presentation$1, "date"),
42837
42266
  "data-lead-date": true,
42838
42267
  dateTime: date.startsAt,
42839
42268
  "aria-label": date.label,
42840
42269
  children: [
42841
42270
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
42842
- ...listLookSlot(presentation, "weekday"),
42271
+ ...listLookSlot(presentation$1, "weekday"),
42843
42272
  children: date.weekday
42844
42273
  }),
42845
42274
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", {
42846
- ...listLookSlot(presentation, "day"),
42275
+ ...listLookSlot(presentation$1, "day"),
42847
42276
  children: date.day
42848
42277
  }),
42849
42278
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
42850
- ...listLookSlot(presentation, "month"),
42279
+ ...listLookSlot(presentation$1, "month"),
42851
42280
  children: date.month
42852
42281
  })
42853
42282
  ]
42854
42283
  }) : null;
42855
42284
  }
42856
- function CardListAction({ item, presentation }) {
42285
+ function CardListAction({ item, presentation: presentation$1 }) {
42857
42286
  const action = item.action;
42858
42287
  if (!action) return null;
42859
42288
  const link$2 = cardListActionLink(action);
42860
42289
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
42861
- ...listLookSlot(presentation, "item-actions"),
42862
- children: !action.href ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: action.label }) : presentation.itemActionStyle === "link" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
42290
+ ...listLookSlot(presentation$1, "item-actions"),
42291
+ children: !action.href ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: action.label }) : presentation$1.itemActionStyle === "link" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
42863
42292
  ...link$2,
42864
42293
  className: "rb-item-text-link",
42865
42294
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkedLabel, {
@@ -42868,15 +42297,15 @@ function CardListAction({ item, presentation }) {
42868
42297
  })
42869
42298
  }) : item.newTabHint ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedAuthoredButton, {
42870
42299
  ...link$2,
42871
- variant: presentation.buttonVariant ?? "primary",
42872
- size: presentation.actionSize ?? "sm",
42300
+ variant: presentation$1.buttonVariant ?? "primary",
42301
+ size: presentation$1.actionSize ?? "sm",
42873
42302
  label: action.label,
42874
42303
  trailingIcon: "ExternalLink",
42875
42304
  ariaLabel: `${action.label} (opens in a new tab)`
42876
42305
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedButtonLink, {
42877
42306
  ...link$2,
42878
- variant: presentation.buttonVariant ?? "primary",
42879
- size: presentation.actionSize ?? "sm",
42307
+ variant: presentation$1.buttonVariant ?? "primary",
42308
+ size: presentation$1.actionSize ?? "sm",
42880
42309
  label: action.label
42881
42310
  })
42882
42311
  });
@@ -42957,6 +42386,38 @@ function CalendarIcon({ className, size = 16, strokeWidth = 2 }) {
42957
42386
  });
42958
42387
  }
42959
42388
 
42389
+ //#endregion
42390
+ //#region ../blocks/src/system/runtime/nodes/events/shared/eventCategoryBadge.ts
42391
+ /** The category's badge, when the event has a category: its name in its own colour, with readable text. */
42392
+ function eventCategoryBadge(category) {
42393
+ return category ? {
42394
+ label: category.name,
42395
+ style: categoryBadgeStyle(category.color ?? null)
42396
+ } : null;
42397
+ }
42398
+ function categoryBadgeStyle(background) {
42399
+ const normalized = background?.trim() ?? "";
42400
+ if (!normalized) return {
42401
+ backgroundColor: "rgb(var(--tb-accent, 0 0 0))",
42402
+ color: "rgb(var(--tb-onAccent, 255 255 255))"
42403
+ };
42404
+ if (isCssColorLiteral(normalized)) return {
42405
+ backgroundColor: normalized,
42406
+ color: readableTextColor(normalized)
42407
+ };
42408
+ return {
42409
+ backgroundColor: resolveTokenOrCssColorValue(normalized),
42410
+ color: resolveOnColorForThemeToken(normalized) ?? "rgb(var(--tb-onAccent, 255 255 255))"
42411
+ };
42412
+ }
42413
+ function readableTextColor(background) {
42414
+ try {
42415
+ return getTextColorForBackground(background);
42416
+ } catch {
42417
+ return "rgb(var(--tb-onAccent, 255 255 255))";
42418
+ }
42419
+ }
42420
+
42960
42421
  //#endregion
42961
42422
  //#region ../blocks/src/system/runtime/nodes/events/shared/EventItem.tsx
42962
42423
  /** Arrangement content choices are data, ready for other live offering lists to reuse. */
@@ -42986,6 +42447,7 @@ const eventItemParts = {
42986
42447
  ],
42987
42448
  grid: [
42988
42449
  "image",
42450
+ "chips",
42989
42451
  "date",
42990
42452
  "title",
42991
42453
  "meta",
@@ -43059,11 +42521,12 @@ function renderCalendarItem(props$1) {
43059
42521
  const facts = eventItemFacts(props$1);
43060
42522
  const Tag = props$1.as ?? eventItemTag(props$1.arrangement);
43061
42523
  const root = props$1.arrangement === "grid" ? "item" : props$1.continued ? "row-continued" : "row";
42524
+ const chipsOnPhoto = parts.includes("image") && Boolean(props$1.event.presentation?.image);
43062
42525
  const body = (part) => [
43063
42526
  "title",
43064
42527
  "meta",
43065
42528
  "description"
43066
- ].includes(part) || part === "action" && props$1.arrangement === "grid";
42529
+ ].includes(part) || part === "chips" && !chipsOnPhoto || part === "action" && props$1.arrangement === "grid";
43067
42530
  const renderPart = (part) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: renderEventItemContentPart({
43068
42531
  part,
43069
42532
  props: props$1,
@@ -43073,7 +42536,7 @@ function renderCalendarItem(props$1) {
43073
42536
  ...listLookSlot(props$1, root),
43074
42537
  "data-availability": facts.availability,
43075
42538
  children: [
43076
- parts.filter((part) => part === "image" || part === "date").map(renderPart),
42539
+ parts.filter((part) => part === "image" || part === "chips" && chipsOnPhoto || part === "date").map(renderPart),
43077
42540
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
43078
42541
  ...listLookSlot(props$1, props$1.arrangement === "grid" ? "item-body" : "what"),
43079
42542
  children: parts.filter(body).map(renderPart)
@@ -43100,6 +42563,7 @@ function eventItemTag(arrangement) {
43100
42563
  function renderEventItemContentPart({ part, props: props$1, facts }) {
43101
42564
  switch (part) {
43102
42565
  case "image": return renderEventItemImage(props$1, facts.link);
42566
+ case "chips": return renderEventItemCategory(props$1);
43103
42567
  case "date": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("time", {
43104
42568
  ...listLookSlot(props$1, "date"),
43105
42569
  dateTime: props$1.event.startsAt,
@@ -43185,6 +42649,18 @@ function renderEventItemImage(props$1, link$2) {
43185
42649
  presentation: props$1
43186
42650
  });
43187
42651
  }
42652
+ /** The same badge the older event cards show: the category's name in its own colour. */
42653
+ function renderEventItemCategory(props$1) {
42654
+ const badge = eventCategoryBadge(props$1.event.eventCategory);
42655
+ return badge ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
42656
+ ...listLookSlot(props$1, "chips"),
42657
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
42658
+ ...listLookSlot(props$1, "chip"),
42659
+ style: badge.style,
42660
+ children: badge.label
42661
+ })
42662
+ }) : null;
42663
+ }
43188
42664
  function renderEventItemAction(props$1) {
43189
42665
  const cta = resolveEventCta(props$1.event, props$1.buttonText);
43190
42666
  const slot = props$1.arrangement === "grid" ? "item-actions" : "action";
@@ -43231,23 +42707,23 @@ const bodyParts = [
43231
42707
  "availability",
43232
42708
  "action"
43233
42709
  ];
43234
- function renderCardItem({ item, presentation, index, Title, actionNode, feedback }) {
43235
- const layout = cardItemLayout(presentation, index);
43236
- const body = cardItemBody(item, presentation);
42710
+ function renderCardItem({ item, presentation: presentation$1, index, Title, actionNode, feedback }) {
42711
+ const layout = cardItemLayout(presentation$1, index);
42712
+ const body = cardItemBody(item, presentation$1);
43237
42713
  const Tag = layout.tag;
43238
42714
  const parts = body.parts.map((part) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: CardListContentPart({
43239
42715
  part,
43240
42716
  item: body.item,
43241
- presentation,
42717
+ presentation: presentation$1,
43242
42718
  Title,
43243
42719
  actionNode
43244
42720
  }) }, part));
43245
42721
  const marker = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
43246
- ...listLookSlot(presentation, "numeral"),
42722
+ ...listLookSlot(presentation$1, "numeral"),
43247
42723
  "aria-hidden": "true"
43248
42724
  });
43249
42725
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, {
43250
- ...listLookSlot(presentation, layout.slot),
42726
+ ...listLookSlot(presentation$1, layout.slot),
43251
42727
  "data-card-list-item": true,
43252
42728
  "data-has-featured": item.featured ? true : undefined,
43253
42729
  children: layout.kind === "row" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
@@ -43256,9 +42732,9 @@ function renderCardItem({ item, presentation, index, Title, actionNode, feedback
43256
42732
  feedback
43257
42733
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [renderCardItemMedia({
43258
42734
  item,
43259
- presentation
42735
+ presentation: presentation$1
43260
42736
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
43261
- ...listLookSlot(presentation, "item-body"),
42737
+ ...listLookSlot(presentation$1, "item-body"),
43262
42738
  children: [
43263
42739
  marker,
43264
42740
  parts,
@@ -43267,8 +42743,8 @@ function renderCardItem({ item, presentation, index, Title, actionNode, feedback
43267
42743
  })] })
43268
42744
  });
43269
42745
  }
43270
- function cardItemLayout(presentation, index) {
43271
- const arrangement = presentation.cardArrangement ?? "cards";
42746
+ function cardItemLayout(presentation$1, index) {
42747
+ const arrangement = presentation$1.cardArrangement ?? "cards";
43272
42748
  if (arrangement === "index" || arrangement === "sequence") return {
43273
42749
  kind: "row",
43274
42750
  tag: "li",
@@ -43280,44 +42756,44 @@ function cardItemLayout(presentation, index) {
43280
42756
  slot: arrangement === "lead-support" && index === 0 ? "lead" : "item"
43281
42757
  };
43282
42758
  }
43283
- function cardItemBody(item, presentation) {
43284
- const schedule = resolveCardListSchedule(item, presentation.datesMode);
43285
- const arrangement = presentation.cardArrangement ?? "cards";
43286
- const orderedParts = presentation.bodyParts ?? (arrangement === "slides" || arrangement === "track" ? carouselBodyParts : bodyParts);
42759
+ function cardItemBody(item, presentation$1) {
42760
+ const schedule = resolveCardListSchedule(item, presentation$1.datesMode);
42761
+ const arrangement = presentation$1.cardArrangement ?? "cards";
42762
+ const orderedParts = presentation$1.bodyParts ?? (arrangement === "slides" || arrangement === "track" ? carouselBodyParts : bodyParts);
43287
42763
  const parts = orderedParts.filter((part) => part !== "schedule" && part !== "dates" || part === schedule.part);
43288
42764
  return {
43289
42765
  item: schedule.item,
43290
42766
  parts
43291
42767
  };
43292
42768
  }
43293
- function renderCardItemMedia({ item, presentation }) {
43294
- const carousel$1 = presentation.cardArrangement === "slides" || presentation.cardArrangement === "track";
43295
- if (presentation.showPhotos === false) return null;
42769
+ function renderCardItemMedia({ item, presentation: presentation$1 }) {
42770
+ const carousel$1 = presentation$1.cardArrangement === "slides" || presentation$1.cardArrangement === "track";
42771
+ if (presentation$1.showPhotos === false) return null;
43296
42772
  if (!item.media) return carousel$1 && item.leadDate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
43297
- ...listLookSlot(presentation, "item-media"),
42773
+ ...listLookSlot(presentation$1, "item-media"),
43298
42774
  "data-has-date-plate": true,
43299
42775
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListLeadDate, {
43300
42776
  item,
43301
- presentation
42777
+ presentation: presentation$1
43302
42778
  })
43303
42779
  }) : null;
43304
42780
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListContentPart, {
43305
42781
  part: "media",
43306
42782
  item,
43307
- presentation
42783
+ presentation: presentation$1
43308
42784
  }), carousel$1 && item.leadDate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
43309
42785
  "data-has-date-stamp": true,
43310
42786
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListLeadDate, {
43311
42787
  item,
43312
- presentation
42788
+ presentation: presentation$1
43313
42789
  })
43314
42790
  }) : null] });
43315
42791
  }
43316
42792
  /** Pure and hook-free: select presentation before creating per-card RSC elements. */
43317
- function CardListItems({ Carousel, items, presentation, carouselLabels, Title = "h3" }) {
43318
- const selectedPresentation = selectCardListPresentation(presentation);
42793
+ function CardListItems({ Carousel, items, presentation: presentation$1, carouselLabels, Title = "h3" }) {
42794
+ const selectedPresentation = selectCardListPresentation(presentation$1);
43319
42795
  const slides = cardListSlides(items, selectedPresentation, Title);
43320
- const photoCount = presentation.showPhotos === false ? 0 : items.filter((item) => item.media).length;
42796
+ const photoCount = presentation$1.showPhotos === false ? 0 : items.filter((item) => item.media).length;
43321
42797
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListCollection, {
43322
42798
  Carousel,
43323
42799
  presentation: selectedPresentation,
@@ -43326,23 +42802,23 @@ function CardListItems({ Carousel, items, presentation, carouselLabels, Title =
43326
42802
  labels: carouselLabels
43327
42803
  });
43328
42804
  }
43329
- function CardListCollection({ Carousel, presentation, slides, photoCount = 0, labels }) {
43330
- if (Carousel && (presentation.cardArrangement === "slides" || presentation.cardArrangement === "track")) {
42805
+ function CardListCollection({ Carousel, presentation: presentation$1, slides, photoCount = 0, labels }) {
42806
+ if (Carousel && (presentation$1.cardArrangement === "slides" || presentation$1.cardArrangement === "track")) {
43331
42807
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Carousel, {
43332
42808
  mode: "arrangement",
43333
- arrangement: presentation.cardArrangement,
42809
+ arrangement: presentation$1.cardArrangement,
43334
42810
  cardList: true,
43335
42811
  labels: labels ?? {
43336
42812
  item: "card",
43337
42813
  region: "Cards"
43338
42814
  },
43339
- indicator: presentation.cardArrangement === "slides" ? "dots" : "none",
42815
+ indicator: presentation$1.cardArrangement === "slides" ? "dots" : "none",
43340
42816
  slides
43341
42817
  });
43342
42818
  }
43343
- const Tag = presentation.cardArrangement === "index" || presentation.cardArrangement === "sequence" ? "ol" : "div";
42819
+ const Tag = presentation$1.cardArrangement === "index" || presentation$1.cardArrangement === "sequence" ? "ol" : "div";
43344
42820
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, {
43345
- ...listLookSlot(presentation, "items"),
42821
+ ...listLookSlot(presentation$1, "items"),
43346
42822
  "data-card-list": true,
43347
42823
  "data-has-photo": String(photoCount > 0),
43348
42824
  "data-photo-count": String(photoCount),
@@ -43350,19 +42826,19 @@ function CardListCollection({ Carousel, presentation, slides, photoCount = 0, la
43350
42826
  });
43351
42827
  }
43352
42828
  /** A card remains one element; the carousel owns track and slide wrappers. */
43353
- function cardListSlides(items, presentation, Title = "h3") {
42829
+ function cardListSlides(items, presentation$1, Title = "h3") {
43354
42830
  return items.map((item, index) => ({
43355
42831
  title: item.title,
43356
42832
  content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventItem, {
43357
42833
  item,
43358
42834
  index,
43359
42835
  Title,
43360
- presentation: leadItemPresentation(presentation, index, items.length)
42836
+ presentation: leadItemPresentation(presentation$1, index, items.length)
43361
42837
  }, item.id)
43362
42838
  }));
43363
42839
  }
43364
- function leadItemPresentation(presentation, index, count) {
43365
- return listItemPresentation(presentation, index, count);
42840
+ function leadItemPresentation(presentation$1, index, count) {
42841
+ return listItemPresentation(presentation$1, index, count);
43366
42842
  }
43367
42843
  /** These parents describe the JSX above, independently of any Look or style. */
43368
42844
  function eventItemSlotParents(arrangement) {
@@ -43588,13 +43064,6 @@ function cropHeightStyle(height, heightAs) {
43588
43064
 
43589
43065
  //#endregion
43590
43066
  //#region ../blocks/src/system/runtime/nodes/events/shared/EventCard.tsx
43591
- /** The category's badge, when the event has a category. */
43592
- function eventCardCategoryBadge(category) {
43593
- return category ? {
43594
- label: category.name,
43595
- style: getCategoryBadgeStyle(category.color ?? null)
43596
- } : null;
43597
- }
43598
43067
  /**
43599
43068
  * EventCard Component
43600
43069
  *
@@ -43603,10 +43072,10 @@ function eventCardCategoryBadge(category) {
43603
43072
  *
43604
43073
  * This component is server-safe (uses no client APIs like Intl).
43605
43074
  */
43606
- const EventCard = ({ event, cardRole = "default", buttonVariant = "primary", buttonText, showVenue = true, showMap = true, mapSlot, allowMap = true, lookSlots, surfacePresentation, showCapacity = true, showImage = true, showCategory = true, showSummary = true, orientation = "vertical", presentation, headingLevel, imageResolveContext }) => {
43075
+ const EventCard = ({ event, cardRole = "default", buttonVariant = "primary", buttonText, showVenue = true, showMap = true, mapSlot, allowMap = true, lookSlots, surfacePresentation, showCapacity = true, showImage = true, showCategory = true, showSummary = true, orientation = "vertical", presentation: presentation$1, headingLevel, imageResolveContext }) => {
43607
43076
  const { presentationClass, cardHeadingLevel, formattedDate, formattedTime, cta, cardClass, title, summary, image, category, categoryBadge, hasImage, dateFact, priceText, teacherLine, capacityText, orientationClass } = eventCardFacts({
43608
43077
  event,
43609
- presentation,
43078
+ presentation: presentation$1,
43610
43079
  headingLevel,
43611
43080
  buttonText,
43612
43081
  cardRole,
@@ -43770,11 +43239,12 @@ function EventCardContent(props$1) {
43770
43239
  function EventCardVenue(props$1) {
43771
43240
  const venue = props$1.showVenue ? props$1.event.venue : null;
43772
43241
  if (!venue) return null;
43242
+ const { place } = eventVenuePlace(venue);
43773
43243
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
43774
43244
  className: "rb-mb-3",
43775
43245
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
43776
43246
  className: "rb-flex rb-items-center rb-gap-2 rb-caption",
43777
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MapPinIcon, { className: "rb-shrink-0" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: venue.name }), venue.address ? ` · ${venue.address}` : null] })]
43247
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MapPinIcon, { className: "rb-shrink-0" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: venue.name }), place ? ` · ${place}` : null] })]
43778
43248
  }), props$1.showMap && props$1.allowMap && venue.location && props$1.mapSlot && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
43779
43249
  className: "rb-mt-2",
43780
43250
  children: props$1.mapSlot
@@ -43812,10 +43282,10 @@ function EventCardFooter(props$1) {
43812
43282
  });
43813
43283
  }
43814
43284
  function resolveEventCardOptions(props$1) {
43815
- const presentation = props$1.presentation ?? "default";
43285
+ const presentation$1 = props$1.presentation ?? "default";
43816
43286
  return {
43817
- presentation,
43818
- presentationClass: presentation === "catalogue" ? " event-card--catalogue" : "",
43287
+ presentation: presentation$1,
43288
+ presentationClass: presentation$1 === "catalogue" ? " event-card--catalogue" : "",
43819
43289
  headingLevel: props$1.headingLevel ?? 3
43820
43290
  };
43821
43291
  }
@@ -43836,8 +43306,8 @@ function resolveEventCardDateFact(props$1) {
43836
43306
  }
43837
43307
  return formatEventNextDateFact(props$1.event);
43838
43308
  }
43839
- function resolveEventCardPrice(event, presentation) {
43840
- return presentation === "catalogue" ? formatEventPrice(event) : null;
43309
+ function resolveEventCardPrice(event, presentation$1) {
43310
+ return presentation$1 === "catalogue" ? formatEventPrice(event) : null;
43841
43311
  }
43842
43312
  function eventCardOrientationClass(orientation) {
43843
43313
  switch (orientation) {
@@ -43845,44 +43315,18 @@ function eventCardOrientationClass(orientation) {
43845
43315
  case "vertical": return "event-card--vertical";
43846
43316
  }
43847
43317
  }
43848
- function getCategoryBadgeStyle(background) {
43849
- const normalized = background?.trim() ?? "";
43850
- if (!normalized) {
43851
- return {
43852
- backgroundColor: "rgb(var(--tb-accent, 0 0 0))",
43853
- color: "rgb(var(--tb-onAccent, 255 255 255))"
43854
- };
43855
- }
43856
- if (isCssColorLiteral(normalized)) {
43857
- return {
43858
- backgroundColor: normalized,
43859
- color: pickReadableTextColor(normalized)
43860
- };
43861
- }
43862
- return {
43863
- backgroundColor: resolveTokenOrCssColorValue(normalized),
43864
- color: resolveOnColorForThemeToken(normalized) ?? "rgb(var(--tb-onAccent, 255 255 255))"
43865
- };
43866
- }
43867
- function pickReadableTextColor(background) {
43868
- try {
43869
- return getTextColorForBackground(background);
43870
- } catch {
43871
- return "rgb(var(--tb-onAccent, 255 255 255))";
43872
- }
43873
- }
43874
43318
  var EventCard_default = EventCard;
43875
- function EventLookDate({ event, ...presentation }) {
43319
+ function EventLookDate({ event, ...presentation$1 }) {
43876
43320
  const date = formatEventCompactDateRail(event.startsAt, eventDateOptions(event));
43877
43321
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("time", {
43878
- ...listLookSlot(presentation, "date"),
43322
+ ...listLookSlot(presentation$1, "date"),
43879
43323
  dateTime: event.startsAt,
43880
43324
  "aria-label": date.label,
43881
43325
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
43882
- ...listLookSlot(presentation, "numeral"),
43326
+ ...listLookSlot(presentation$1, "numeral"),
43883
43327
  children: date.day
43884
43328
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
43885
- ...listLookSlot(presentation, "date-label"),
43329
+ ...listLookSlot(presentation$1, "date-label"),
43886
43330
  children: [
43887
43331
  date.weekday,
43888
43332
  " ",
@@ -43891,20 +43335,20 @@ function EventLookDate({ event, ...presentation }) {
43891
43335
  })]
43892
43336
  });
43893
43337
  }
43894
- function eventCardFacts({ event, presentation, headingLevel, buttonText, cardRole, showCategory, showImage, showCapacity, orientation }) {
43338
+ function eventCardFacts({ event, presentation: presentation$1, headingLevel, buttonText, cardRole, showCategory, showImage, showCapacity, orientation }) {
43895
43339
  const { presentation: cardPresentation, presentationClass, headingLevel: cardHeadingLevel } = resolveEventCardOptions({
43896
- presentation,
43340
+ presentation: presentation$1,
43897
43341
  headingLevel
43898
43342
  });
43899
43343
  const { formattedDate, formattedTime } = formatEventCardWhen(event);
43900
- const booking = resolveEventCardBooking(event, presentation === "catalogue" ? "series" : "occurrence", buttonText);
43344
+ const booking = resolveEventCardBooking(event, presentation$1 === "catalogue" ? "series" : "occurrence", buttonText);
43901
43345
  const { cta } = booking;
43902
43346
  const cardClass = boxStyleClassNames({ role: cardRole }, { baseClass: "event-card" });
43903
43347
  const title = event.title;
43904
43348
  const summary = event.presentation?.summary ?? event.description;
43905
43349
  const image = event.presentation?.image ?? null;
43906
43350
  const category = event.eventCategory;
43907
- const categoryBadge = showCategory ? eventCardCategoryBadge(category) : null;
43351
+ const categoryBadge = showCategory ? eventCategoryBadge(category) : null;
43908
43352
  const hasImage = Boolean(showImage && image);
43909
43353
  const dateFact = resolveEventCardDateFact({
43910
43354
  event,
@@ -45928,6 +45372,17 @@ function withEventSelectionVisibility(field) {
45928
45372
  };
45929
45373
  }
45930
45374
 
45375
+ //#endregion
45376
+ //#region ../blocks/src/system/runtime/nodes/events/shared/combinedLook.ts
45377
+ const presentation = z.object({
45378
+ ...lookListPresentationSchemaShape,
45379
+ hooks: z.record(z.string(), z.string())
45380
+ });
45381
+ const combinedLookSchema = z.object({
45382
+ list: presentation.extend({ arrangement: z.enum(eventListArrangementValues) }),
45383
+ month: presentation.extend({ arrangement: z.enum(["month-grid", "month-day"]) })
45384
+ });
45385
+
45931
45386
  //#endregion
45932
45387
  //#region ../blocks/src/system/runtime/nodes/events/EventCalendar.interactive.ts
45933
45388
  const eventCalendarDateContextSchema = z.object({
@@ -45941,6 +45396,7 @@ const eventCalendarSourceSchema = z.object({
45941
45396
  seeAllButtonVariant: eventButtonVariantSchema.nullable().optional(),
45942
45397
  effectiveListArrangementKey: z.enum(eventListArrangementValues).nullable().optional(),
45943
45398
  listBy: z.enum(["upcoming", "week"]).optional(),
45399
+ combinedLook: combinedLookSchema.optional(),
45944
45400
  monthArrangement: z.enum(["month-grid", "month-day"]).optional(),
45945
45401
  variant: eventCalendarDisplayModeSchema.nullable().optional(),
45946
45402
  displayMode: eventCalendarDisplayModeSchema.nullable().optional(),
@@ -45956,6 +45412,7 @@ const eventCalendarDisplaySchema = z.object({
45956
45412
  schedule: recurringScheduleProjectionSchema.optional(),
45957
45413
  seeAllButtonVariant: eventButtonVariantSchema,
45958
45414
  listBy: z.literal("week").optional(),
45415
+ combinedLook: combinedLookSchema.optional(),
45959
45416
  monthArrangement: z.enum(["month-grid", "month-day"]).optional(),
45960
45417
  kind: z.literal("event-calendar"),
45961
45418
  displayMode: eventCalendarDisplayModeSchema,
@@ -45991,13 +45448,14 @@ function normalizeOptionalText(value) {
45991
45448
  return trimmed.length > 0 ? trimmed : undefined;
45992
45449
  }
45993
45450
  function buildEventCalendarDisplay(source, effectiveListArrangementKey = source.effectiveListArrangementKey ?? resolveCalendarListArrangement(source)) {
45994
- const listLayout = effectiveListArrangementKey;
45451
+ const presentation$1 = combinedCalendarPresentation(source, effectiveListArrangementKey);
45995
45452
  return {
45996
45453
  ...buildEventCollectionDisplay({
45997
45454
  ...source,
45998
- listLayout,
45455
+ ...presentation$1,
45999
45456
  listColumns: source.listColumns ?? source.columns
46000
45457
  }),
45458
+ ...presentation$1,
46001
45459
  ...source.monthArrangement === undefined ? {} : { monthArrangement: source.monthArrangement },
46002
45460
  ...calendarListGrouping(source),
46003
45461
  ...weekListPaging(source),
@@ -46123,6 +45581,17 @@ function calendarRecurringWeekPresentation(source) {
46123
45581
  seeAllButtonVariant: source.seeAllButtonVariant ?? "secondary"
46124
45582
  };
46125
45583
  }
45584
+ /** A resolved composition owns the layout and the allowed calendar view. */
45585
+ function combinedCalendarPresentation(source, listLayout) {
45586
+ const combinedLook = source.combinedLook;
45587
+ return combinedLook ? {
45588
+ ...combinedLook.list,
45589
+ combinedLook,
45590
+ listLayout: combinedLook.list.arrangement,
45591
+ calendarTabViews: "month",
45592
+ defaultCalendarView: "month"
45593
+ } : { listLayout };
45594
+ }
46126
45595
 
46127
45596
  //#endregion
46128
45597
  //#region ../blocks/src/system/blocks/events/eventCalendarDataLoaders.ts
@@ -46336,6 +45805,7 @@ const eventCalendarLayout = [styledSection({
46336
45805
  listBy: { $bind: { from: "content.listBy" } },
46337
45806
  bookingTimeZone: { $bind: { from: "$root.dateContext.timeZone" } },
46338
45807
  dateContext: { $bind: { from: "$root.dateContext" } },
45808
+ combinedLook: { $bind: { from: "content._combinedLook" } },
46339
45809
  monthArrangement: { $bind: { from: "content._lookMonthArrangement" } },
46340
45810
  lookSlots: { $bind: { from: "content._lookSlots" } },
46341
45811
  itemActionStyle: { $bind: { from: "content._effectiveKnobs.actionStyle.value" } },
@@ -50937,8 +50407,8 @@ function getShopDisplayForItems(render, items) {
50937
50407
  });
50938
50408
  }
50939
50409
  function shopCardListPresentation(source) {
50940
- return cardListPresentationKeys.reduce((presentation, key) => ({
50941
- ...presentation,
50410
+ return cardListPresentationKeys.reduce((presentation$1, key) => ({
50411
+ ...presentation$1,
50942
50412
  [key]: source[key]
50943
50413
  }), {});
50944
50414
  }
@@ -52097,9 +51567,9 @@ const giftingBlockDefinition = {
52097
51567
  /**
52098
51568
  * Get form fields based on preset configuration.
52099
51569
  */
52100
- function getGlobalFormFields(form$1, presentation = {}) {
51570
+ function getGlobalFormFields(form$1, presentation$1 = {}) {
52101
51571
  const fields$1 = withOptionalPhone(newsletterPresetFields(form$1.fields), form$1.includeOptionalPhone);
52102
- return presentation.lookSlots ? fields$1.map((field) => ({
51572
+ return presentation$1.lookSlots ? fields$1.map((field) => ({
52103
51573
  ...field,
52104
51574
  label: lookFieldLabel(field)
52105
51575
  })) : fields$1;
@@ -52362,14 +51832,14 @@ const newsletterFormBlockRuntime = defineProgressiveBlockRuntime({
52362
51832
  },
52363
51833
  buildRender: (source) => {
52364
51834
  const targeting = source.targeting ?? { mode: "default" };
52365
- const presentation = planMailingListChoicePresentation(targeting);
51835
+ const presentation$1 = planMailingListChoicePresentation(targeting);
52366
51836
  return {
52367
51837
  display: buildNewsletterDisplay(source),
52368
51838
  hydration: {
52369
51839
  target: projectNewsletterFormHydrationTarget(source),
52370
51840
  setupRevision: source.mailingList?.tag === "configured" ? source.mailingList.revision : null,
52371
51841
  choiceProvider: targeting.mode === "custom" ? targeting.provider : null,
52372
- choicePresentation: presentation.tag,
51842
+ choicePresentation: presentation$1.tag,
52373
51843
  spamProtectionEnabled: source.interactionContext?.kind === "demo" ? false : source.spamProtectionEnabled
52374
51844
  }
52375
51845
  };
@@ -53113,6 +52583,12 @@ const teamMembersBlockDefinition = {
53113
52583
 
53114
52584
  //#endregion
53115
52585
  //#region ../blocks/src/system/runtime/nodes/location-map.shared.ts
52586
+ /** Additive map DTO; hidden venues retain only their public name and area. */
52587
+ const publicVenueMapLocationSchema = publicLocationSchema$1.unwrap().extend({
52588
+ name: z.string().optional(),
52589
+ area: z.string().nullable().optional(),
52590
+ addressWithBooking: z.boolean().optional()
52591
+ }).readonly();
53116
52592
  const LOCATION_MAP_DEFAULTS = {
53117
52593
  zoom: 15,
53118
52594
  heightPx: 320,
@@ -53262,10 +52738,18 @@ function buildGoogleMapsDirectionsUrl(input) {
53262
52738
  return locationMapLinks(input).directions;
53263
52739
  }
53264
52740
  function readLocationMapProjection(value, siteCountry) {
52741
+ const venue = publicVenueMapLocationSchema.safeParse(value);
52742
+ if (venue.success) return venue.data.addressWithBooking ? {
52743
+ ...venue.data,
52744
+ address: null,
52745
+ displayAddress: venue.data.area ?? null,
52746
+ pin: null,
52747
+ doorNote: null,
52748
+ drawing: undefined
52749
+ } : venue.data;
53265
52750
  const canonical$1 = parseLocationValue(value);
53266
52751
  if (canonical$1.ok && canonical$1.value !== null) return projectPublicLocation(canonical$1.value, siteCountry);
53267
- const projected = publicLocationSchema$1.safeParse(value);
53268
- return projected.success ? projected.data : null;
52752
+ return null;
53269
52753
  }
53270
52754
 
53271
52755
  //#endregion
@@ -54697,6 +54181,13 @@ const systemCustomizeFacetDefinitions = [
54697
54181
  "block.form"
54698
54182
  ]
54699
54183
  },
54184
+ fixedKnob("block.hero", "photoTint", "Photo tint", [{
54185
+ value: "dark",
54186
+ label: "Dark"
54187
+ }, {
54188
+ value: "brand",
54189
+ label: "Brand colour"
54190
+ }], "dark"),
54700
54191
  fixedKnob("block.hero", "candleGlow", "Candle glow", onOff, "true", false, true),
54701
54192
  fixedKnob("block.hero", "columnGuides", "Column guides", onOff, "true", false, true),
54702
54193
  fixedKnob("block.hero", "titleSize", "Headline size", heroTitleSizeOptions, "auto", true),
@@ -54959,6 +54450,29 @@ const blocksWithLooks = new Set(arrangementAppearancePresets.flatMap((preset) =>
54959
54450
  function blockHasArrangementLooks(blockKind) {
54960
54451
  return blocksWithLooks.has(blockKind);
54961
54452
  }
54453
+ /**
54454
+ * The Looks a block can show and the knobs each one offers, for catalogues that
54455
+ * describe a block rather than one placed on a page. A knob's stored values are the
54456
+ * Look's own narrowing when it has one; free-text knobs have none.
54457
+ */
54458
+ function arrangementLooksForBlock(blockKind) {
54459
+ const facets = systemCustomizeFacetDefinitionsForBlock(blockKind);
54460
+ return arrangementAppearancePresets.filter((preset) => preset.blockKinds.some((kind) => kind === blockKind)).map(({ id, label, structure }) => ({
54461
+ id,
54462
+ label,
54463
+ knobs: structure.knobs.flatMap((knob) => {
54464
+ const facet = facets.find((definition$1) => definition$1.id === knob);
54465
+ if (!facet) return [];
54466
+ const allowed = structure.knobValues?.[knob];
54467
+ const values = facet.values.filter(({ value }) => !allowed || allowed.includes(value)).map(({ value }) => value);
54468
+ return [{
54469
+ id: knob,
54470
+ label: facet.label,
54471
+ values
54472
+ }];
54473
+ })
54474
+ }));
54475
+ }
54962
54476
  /** Schema paths retain flattened group names, as the form and SDK catalogue describe them. */
54963
54477
  function schemaFieldOwnerAt(ownership, path$3) {
54964
54478
  return ownership.kind === "no-look" ? "owner" : ownership.paths.get(path$3) ?? "owner";
@@ -55279,9 +54793,9 @@ function memoizePresentationBlock(present) {
55279
54793
  return (block) => {
55280
54794
  const cached = blocks.get(block);
55281
54795
  if (cached) return cached;
55282
- const presentation = present(block);
55283
- blocks.set(block, presentation);
55284
- return presentation;
54796
+ const presentation$1 = present(block);
54797
+ blocks.set(block, presentation$1);
54798
+ return presentation$1;
55285
54799
  };
55286
54800
  }
55287
54801
 
@@ -55418,10 +54932,11 @@ function hasArrangementPanel(display) {
55418
54932
  return display.state === "ready" && display.lookSlots !== undefined && display.listLayout === "compact";
55419
54933
  }
55420
54934
  /** Nearest slot parents in EventListBody and WeekColumns. */
55421
- function eventListSlotParents(arrangement) {
54935
+ /** `root` is the declared slot the listing renders inside (a Look may paint the block's layout), or null. */
54936
+ function eventListSlotParents(arrangement, root = null) {
55422
54937
  return {
55423
- list: arrangement === "compact" ? "panel" : null,
55424
- panel: null,
54938
+ list: arrangement === "compact" ? "panel" : root,
54939
+ panel: root,
55425
54940
  group: "list",
55426
54941
  "group-label": "group",
55427
54942
  "group-date": "group-label",
@@ -55433,13 +54948,13 @@ function eventListSlotParents(arrangement) {
55433
54948
 
55434
54949
  //#endregion
55435
54950
  //#region ../blocks/src/system/runtime/nodes/events/shared/MonthLookView.tsx
55436
- function MonthEvent({ event, timeZone, locale, presentation }) {
54951
+ function MonthEvent({ event, timeZone, locale, presentation: presentation$1 }) {
55437
54952
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("a", {
55438
54953
  ...resolveEventLinkProps(event),
55439
- ...listLookSlot(presentation, "item"),
54954
+ ...listLookSlot(presentation$1, "item"),
55440
54955
  children: [
55441
54956
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
55442
- ...listLookSlot(presentation, "time"),
54957
+ ...listLookSlot(presentation$1, "time"),
55443
54958
  children: formatTime(event.startsAt, {
55444
54959
  ...eventDateOptions({
55445
54960
  ...event,
@@ -55450,7 +54965,7 @@ function MonthEvent({ event, timeZone, locale, presentation }) {
55450
54965
  }),
55451
54966
  " ",
55452
54967
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
55453
- ...listLookSlot(presentation, "what"),
54968
+ ...listLookSlot(presentation$1, "what"),
55454
54969
  children: event.title
55455
54970
  })
55456
54971
  ]
@@ -55550,14 +55065,14 @@ function MonthLookView(props$1) {
55550
55065
  }) : null]
55551
55066
  });
55552
55067
  }
55553
- function MonthDayMarks({ count, presentation }) {
55068
+ function MonthDayMarks({ count, presentation: presentation$1 }) {
55554
55069
  if (count === 0) return null;
55555
55070
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
55556
- ...listLookSlot(presentation, "item-meta"),
55071
+ ...listLookSlot(presentation$1, "item-meta"),
55557
55072
  role: "img",
55558
55073
  "aria-label": `${count} ${count === 1 ? "event" : "events"}`,
55559
55074
  children: Array.from({ length: Math.min(count, 3) }, (_$1, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
55560
- ...listLookSlot(presentation, "mark"),
55075
+ ...listLookSlot(presentation$1, "mark"),
55561
55076
  "aria-hidden": "true"
55562
55077
  }, index))
55563
55078
  });
@@ -55810,7 +55325,7 @@ function headerAction(selection, action) {
55810
55325
 
55811
55326
  //#endregion
55812
55327
  //#region ../blocks/src/system/runtime/nodes/event-details/EventHeader.tsx
55813
- function EventHeader({ selection, presentation, showCost, showCapacity }) {
55328
+ function EventHeader({ selection, presentation: presentation$1, showCost, showCapacity }) {
55814
55329
  const item = eventHeaderContent(selection, {
55815
55330
  cost: showCost,
55816
55331
  capacity: showCapacity
@@ -55818,31 +55333,31 @@ function EventHeader({ selection, presentation, showCost, showCapacity }) {
55818
55333
  const part = (name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListContentPart, {
55819
55334
  part: name,
55820
55335
  item,
55821
- presentation
55336
+ presentation: presentation$1
55822
55337
  });
55823
55338
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
55824
- ...listLookSlot(presentation, "frame"),
55339
+ ...listLookSlot(presentation$1, "frame"),
55825
55340
  children: [
55826
55341
  item.media ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("figure", {
55827
- ...listLookSlot(presentation, "item-media"),
55342
+ ...listLookSlot(presentation$1, "item-media"),
55828
55343
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MediaNodeServer, {
55829
55344
  value: item.media,
55830
55345
  size: ImagePresets.full,
55831
55346
  loading: "eager",
55832
55347
  fetchPriority: "high",
55833
- ...listLookSlot(presentation, "photo")
55348
+ ...listLookSlot(presentation$1, "photo")
55834
55349
  })
55835
55350
  }) : null,
55836
55351
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
55837
- ...listLookSlot(presentation, "copy"),
55352
+ ...listLookSlot(presentation$1, "copy"),
55838
55353
  children: [
55839
55354
  part("chips"),
55840
55355
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
55841
- ...listLookSlot(presentation, "headline"),
55356
+ ...listLookSlot(presentation$1, "headline"),
55842
55357
  children: item.title
55843
55358
  }),
55844
55359
  item.text ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
55845
- ...listLookSlot(presentation, "lede"),
55360
+ ...listLookSlot(presentation$1, "lede"),
55846
55361
  children: item.text
55847
55362
  }) : null,
55848
55363
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -55897,7 +55412,7 @@ function arrangementSlotParents(input) {
55897
55412
  const { target, arrangement } = input;
55898
55413
  if (target === "events") return {
55899
55414
  ...derived,
55900
- ...eventListSlotParents(arrangement),
55415
+ ...eventListSlotParents(arrangement, "layout" in derived ? "layout" : null),
55901
55416
  ...eventItemSlotParents(arrangement),
55902
55417
  ...arrangement === "month-grid" || arrangement === "month-day" ? monthSlotParents(arrangement) : {}
55903
55418
  };
@@ -58861,7 +58376,7 @@ function AppointmentServiceGroups({ groups, hasSectionHeading, idPrefix }) {
58861
58376
  function appointmentSectionHeading(value) {
58862
58377
  return typeof value === "string" ? value : undefined;
58863
58378
  }
58864
- function AppointmentServicesNode({ Carousel, preparedView, sectionHeading, categoryId, services, pathname, servicePathPrefix, blockId, className, ...presentation }) {
58379
+ function AppointmentServicesNode({ Carousel, preparedView, sectionHeading, categoryId, services, pathname, servicePathPrefix, blockId, className, ...presentation$1 }) {
58865
58380
  const view = preparedView ?? buildAppointmentServicesView({
58866
58381
  ...appointmentReadInput({
58867
58382
  services,
@@ -58869,14 +58384,14 @@ function AppointmentServicesNode({ Carousel, preparedView, sectionHeading, categ
58869
58384
  pathname,
58870
58385
  servicePathPrefix
58871
58386
  }),
58872
- arrangement: presentation.cardArrangement
58387
+ arrangement: presentation$1.cardArrangement
58873
58388
  });
58874
58389
  const hasSectionHeading = Boolean(sectionHeading);
58875
- if (presentation.lookSlots && (view.kind === "items" || view.kind === "groups")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppointmentCardLists, {
58390
+ if (presentation$1.lookSlots && (view.kind === "items" || view.kind === "groups")) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppointmentCardLists, {
58876
58391
  sectionHeading: appointmentSectionHeading(sectionHeading),
58877
58392
  Carousel,
58878
58393
  view,
58879
- presentation,
58394
+ presentation: presentation$1,
58880
58395
  hasSectionHeading
58881
58396
  });
58882
58397
  const rootClassName = `appointment-services ${className ?? ""}`.trim();
@@ -58940,7 +58455,7 @@ function appointmentCardContent(row$1) {
58940
58455
  }
58941
58456
  };
58942
58457
  }
58943
- function AppointmentCardLists({ Carousel, view, presentation, hasSectionHeading, sectionHeading }) {
58458
+ function AppointmentCardLists({ Carousel, view, presentation: presentation$1, hasSectionHeading, sectionHeading }) {
58944
58459
  if (view.kind === "items") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListItems, {
58945
58460
  Carousel,
58946
58461
  carouselLabels: {
@@ -58948,25 +58463,25 @@ function AppointmentCardLists({ Carousel, view, presentation, hasSectionHeading,
58948
58463
  region: sectionHeading || "Services"
58949
58464
  },
58950
58465
  items: view.rows.map(appointmentCardContent),
58951
- presentation,
58466
+ presentation: presentation$1,
58952
58467
  Title: hasSectionHeading ? "h3" : "h2"
58953
58468
  });
58954
58469
  const Heading = hasSectionHeading ? "h3" : "h2";
58955
58470
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
58956
58471
  className: "rb-flex rb-flex-col rb-gap-10",
58957
58472
  children: view.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
58958
- ...listLookSlot(presentation, "head"),
58473
+ ...listLookSlot(presentation$1, "head"),
58959
58474
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, {
58960
- ...listLookSlot(presentation, "heading"),
58475
+ ...listLookSlot(presentation$1, "heading"),
58961
58476
  children: group.heading
58962
58477
  }), group.intro ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
58963
- ...listLookSlot(presentation, "intro"),
58478
+ ...listLookSlot(presentation$1, "intro"),
58964
58479
  children: group.intro
58965
58480
  }) : null]
58966
58481
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardListItems, {
58967
58482
  Carousel,
58968
58483
  items: group.rows.map(appointmentCardContent),
58969
- presentation,
58484
+ presentation: presentation$1,
58970
58485
  Title: hasSectionHeading ? "h4" : "h3"
58971
58486
  })] }, group.key))
58972
58487
  });
@@ -60683,7 +60198,9 @@ function EventFacts({ selection, selected, venue, teamMembers, pricing, showCapa
60683
60198
  });
60684
60199
  }
60685
60200
  function EventVenueFact({ venue }) {
60686
- return venue ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
60201
+ if (!venue) return null;
60202
+ const { place, addressWithBooking } = eventVenuePlace(venue);
60203
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
60687
60204
  className: "rb-mt-6",
60688
60205
  children: [
60689
60206
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -60691,16 +60208,19 @@ function EventVenueFact({ venue }) {
60691
60208
  children: "Venue"
60692
60209
  }),
60693
60210
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: venue.name }) }),
60694
- venue.address ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
60211
+ place ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
60695
60212
  className: "rb-caption",
60696
- children: venue.address
60213
+ children: place
60697
60214
  }) : null,
60698
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocationMapActions, {
60215
+ addressWithBooking ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
60216
+ className: "rb-caption rb-mt-1",
60217
+ children: "The full address and directions come with your booking confirmation."
60218
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocationMapActions, {
60699
60219
  location: venue.location,
60700
60220
  addressText: venue.address
60701
60221
  })
60702
60222
  ]
60703
- }) : null;
60223
+ });
60704
60224
  }
60705
60225
  function EventTeacherFacts({ teamMembers }) {
60706
60226
  return teamMembers.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -60972,7 +60492,7 @@ function withLookParts(props$1, arrangement) {
60972
60492
 
60973
60493
  //#endregion
60974
60494
  //#region ../blocks/src/system/runtime/nodes/event-details.server.tsx
60975
- function EventDetailsHydration({ props: props$1, presentation, scopedEvents, subject, nowIso }) {
60495
+ function EventDetailsHydration({ props: props$1, presentation: presentation$1, scopedEvents, subject, nowIso }) {
60976
60496
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IslandPropsScript, { value: {
60977
60497
  ...props$1.arrangement ? {
60978
60498
  arrangement: props$1.arrangement,
@@ -60986,19 +60506,19 @@ function EventDetailsHydration({ props: props$1, presentation, scopedEvents, sub
60986
60506
  subject,
60987
60507
  events: scopedEvents,
60988
60508
  venueDrawing: props$1.venueDrawing ?? null,
60989
- showVenue: presentation.showVenue,
60990
- showMap: presentation.showMap,
60991
- showOtherDates: presentation.showOtherDates,
60509
+ showVenue: presentation$1.showVenue,
60510
+ showMap: presentation$1.showMap,
60511
+ showOtherDates: presentation$1.showOtherDates,
60992
60512
  otherDatesLimit: props$1.otherDatesLimit,
60993
- showCost: presentation.showCost,
60994
- showTitle: presentation.showTitle,
60995
- showDescription: presentation.showDescription,
60996
- showCapacity: presentation.showCapacity,
60997
- showAcceptedPasses: presentation.showAcceptedPasses,
60998
- showAcceptedMemberships: presentation.showAcceptedMemberships,
60513
+ showCost: presentation$1.showCost,
60514
+ showTitle: presentation$1.showTitle,
60515
+ showDescription: presentation$1.showDescription,
60516
+ showCapacity: presentation$1.showCapacity,
60517
+ showAcceptedPasses: presentation$1.showAcceptedPasses,
60518
+ showAcceptedMemberships: presentation$1.showAcceptedMemberships,
60999
60519
  sitePasses: props$1.sitePasses,
61000
60520
  siteMemberships: props$1.siteMemberships,
61001
- autoApplyDiscount: presentation.autoApplyDiscount,
60521
+ autoApplyDiscount: presentation$1.autoApplyDiscount,
61002
60522
  className: props$1.className,
61003
60523
  nowIso
61004
60524
  } });
@@ -61011,7 +60531,7 @@ function EventDetailsSSR(props$1) {
61011
60531
  occurrenceContext,
61012
60532
  subject: normalizedSubject
61013
60533
  });
61014
- const presentation = resolveEventDetailsPresentation({
60534
+ const presentation$1 = resolveEventDetailsPresentation({
61015
60535
  ...props$1,
61016
60536
  events: scopedEvents,
61017
60537
  occurrenceContext,
@@ -61025,13 +60545,13 @@ function EventDetailsSSR(props$1) {
61025
60545
  children: [
61026
60546
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventDetailsHydration, {
61027
60547
  props: props$1,
61028
- presentation,
60548
+ presentation: presentation$1,
61029
60549
  scopedEvents,
61030
60550
  subject: normalizedSubject,
61031
60551
  nowIso
61032
60552
  }),
61033
60553
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventDetailsView, {
61034
- ...presentation,
60554
+ ...presentation$1,
61035
60555
  mapClassName: "venue-map event-details-map"
61036
60556
  }),
61037
60557
  children
@@ -61063,11 +60583,11 @@ function LocationMapSSR(props$1) {
61063
60583
  }
61064
60584
  /** Legacy and automatic-Look markup carries no slot; Look-only layouts mark the details as the panel. */
61065
60585
  function lookDetailsSlot(props$1) {
61066
- const presentation = lookPresentationSchema.safeParse({
60586
+ const presentation$1 = lookPresentationSchema.safeParse({
61067
60587
  lookSlots: props$1.lookSlots ?? undefined,
61068
60588
  surfacePresentation: props$1.surfacePresentation ?? undefined
61069
60589
  });
61070
- return presentation.success ? listLookSlot(presentation.data, "panel") : {};
60590
+ return presentation$1.success ? listLookSlot(presentation$1.data, "panel") : {};
61071
60591
  }
61072
60592
  var location_map_server_default = LocationMapSSR;
61073
60593
 
@@ -61671,8 +61191,8 @@ function LegacyOfferingsPreview({ props: props$1, states, now }) {
61671
61191
  renderGroupItems: renderOfferingGroupItems
61672
61192
  });
61673
61193
  }
61674
- function renderOfferingGroupItems(presentation, items, options) {
61675
- switch (presentation) {
61194
+ function renderOfferingGroupItems(presentation$1, items, options) {
61195
+ switch (presentation$1) {
61676
61196
  case "single-card": {
61677
61197
  const item = items[0];
61678
61198
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -61694,7 +61214,7 @@ function renderOfferingGroupItems(presentation, items, options) {
61694
61214
  children: items.map((item) => renderOfferingCompactItem(item, options))
61695
61215
  })
61696
61216
  });
61697
- default: return assertNever(presentation, "Unhandled offerings preview presentation");
61217
+ default: return assertNever(presentation$1, "Unhandled offerings preview presentation");
61698
61218
  }
61699
61219
  }
61700
61220
  function FeaturedOfferingSummary({ item }) {
@@ -62707,7 +62227,7 @@ function CheckoutShellView(props$1) {
62707
62227
  });
62708
62228
  }
62709
62229
  function ShopCardListSections(props$1) {
62710
- const { display, presentation } = props$1;
62230
+ const { display, presentation: presentation$1 } = props$1;
62711
62231
  const groups = [{
62712
62232
  heading: display.passesHeading,
62713
62233
  items: display.passes,
@@ -62720,17 +62240,17 @@ function ShopCardListSections(props$1) {
62720
62240
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
62721
62241
  id: "shop",
62722
62242
  "data-block": "shop",
62723
- ...listLookSlot(presentation, "layout"),
62243
+ ...listLookSlot(presentation$1, "layout"),
62724
62244
  children: [
62725
62245
  props$1.beforeContent,
62726
62246
  display.state === "empty" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: display.emptyMessage }) : null,
62727
62247
  groups.filter(({ items }) => items.length > 0).map((group, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
62728
- ...listLookSlot(presentation, "group"),
62248
+ ...listLookSlot(presentation$1, "group"),
62729
62249
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
62730
- ...listLookSlot(presentation, "group-label"),
62250
+ ...listLookSlot(presentation$1, "group-label"),
62731
62251
  children: group.heading
62732
62252
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopCardListCollection, {
62733
- presentation,
62253
+ presentation: presentation$1,
62734
62254
  Carousel: props$1.Carousel,
62735
62255
  items: group.items,
62736
62256
  cards: group.cards
@@ -62754,8 +62274,8 @@ function ShopCardListCollection(props$1) {
62754
62274
  }))
62755
62275
  });
62756
62276
  }
62757
- function ShopPurchaseAction({ presentation, ...props$1 }) {
62758
- if (presentation?.itemActionStyle !== "link") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedButton, { ...props$1 });
62277
+ function ShopPurchaseAction({ presentation: presentation$1, ...props$1 }) {
62278
+ if (presentation$1?.itemActionStyle !== "link") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemedButton, { ...props$1 });
62759
62279
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
62760
62280
  type: "button",
62761
62281
  className: "rb-item-text-link",
@@ -63085,6 +62605,19 @@ function EventListWeekEmptyState(props$1) {
63085
62605
  });
63086
62606
  }
63087
62607
 
62608
+ //#endregion
62609
+ //#region ../blocks/src/system/runtime/nodes/events/shared/CombinedLookFrame.tsx
62610
+ /** Recipe markup keeps its existing tree; arrangements carry the selected tab's hooks. */
62611
+ function CombinedLookFrame({ look, children }) {
62612
+ return look ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
62613
+ ...look.hooks,
62614
+ children
62615
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
62616
+ }
62617
+ function combinedClassName(look, className) {
62618
+ return look ? ["event-combined", className].filter(Boolean).join(" ") : `event-combined ${className || ""}`;
62619
+ }
62620
+
63088
62621
  //#endregion
63089
62622
  //#region ../blocks/src/system/runtime/nodes/events/shared/AgendaEventCard.tsx
63090
62623
  /**
@@ -63745,25 +63278,12 @@ function renderCombinedSsr(display, prefetchedEvents) {
63745
63278
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
63746
63279
  className: clsx_default("event-combined", display.className ?? undefined),
63747
63280
  "data-block": "event-combined",
63748
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
63749
- className: "nav-tabs",
63750
- role: "tablist",
63751
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
63752
- type: "button",
63753
- role: "tab",
63754
- "aria-selected": true,
63755
- className: "nav-tab nav-tab--active",
63756
- children: "List"
63757
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
63758
- type: "button",
63759
- role: "tab",
63760
- "aria-selected": false,
63761
- className: "nav-tab",
63762
- children: "Calendar"
63763
- })]
63764
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventListingFrameView, {
63765
- display: listDisplay,
63766
- renderEventCard: (event) => renderEventCard(event, listDisplay)
63281
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CombinedSsrTabs, { look: display.combinedLook }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CombinedLookFrame, {
63282
+ look: display.combinedLook?.list,
63283
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventListingFrameView, {
63284
+ display: listDisplay,
63285
+ renderEventCard: (event) => renderEventCard(event, listDisplay)
63286
+ })
63767
63287
  })]
63768
63288
  });
63769
63289
  }
@@ -64045,6 +63565,31 @@ const EventCalendarSSR = (props$1) => {
64045
63565
  });
64046
63566
  };
64047
63567
  var EventCalendar_server_default = EventCalendarSSR;
63568
+ function CombinedSsrTabs({ look }) {
63569
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
63570
+ className: "nav-tabs",
63571
+ role: "tablist",
63572
+ ...listLookSlot(look?.list ?? {}, "controls"),
63573
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
63574
+ type: "button",
63575
+ ...listLookSlot(look?.list ?? {}, "control"),
63576
+ role: "tab",
63577
+ "data-current": look ? "true" : undefined,
63578
+ tabIndex: look ? 0 : undefined,
63579
+ "aria-selected": true,
63580
+ className: "nav-tab nav-tab--active",
63581
+ children: "List"
63582
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
63583
+ type: "button",
63584
+ ...listLookSlot(look?.list ?? {}, "control"),
63585
+ role: "tab",
63586
+ tabIndex: look ? -1 : undefined,
63587
+ "aria-selected": false,
63588
+ className: "nav-tab",
63589
+ children: "Calendar"
63590
+ })]
63591
+ });
63592
+ }
64048
63593
 
64049
63594
  //#endregion
64050
63595
  //#region ../blocks/src/system/runtime/nodes/events/EventListing.server.tsx
@@ -65207,36 +64752,36 @@ function ShopSsrProducts({ render }) {
65207
64752
  presentation: render.hydration.presentation,
65208
64753
  Carousel: CarouselNodeSSR,
65209
64754
  passCards: render.display.passes.map((pass, index) => {
65210
- const presentation = shopProductPresentation(render.hydration, pass.id, index, render.display.passes.length);
64755
+ const presentation$1 = shopProductPresentation(render.hydration, pass.id, index, render.display.passes.length);
65211
64756
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PassCardView, {
65212
64757
  display: pass,
65213
64758
  index,
65214
- presentation,
64759
+ presentation: presentation$1,
65215
64760
  action: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopSsrPurchaseAction, {
65216
64761
  action: pass.action,
65217
64762
  hydration: render.hydration,
65218
- presentation
64763
+ presentation: presentation$1
65219
64764
  })
65220
64765
  }, pass.id);
65221
64766
  }),
65222
64767
  membershipCards: render.display.memberships.map((membership, index) => {
65223
- const presentation = shopProductPresentation(render.hydration, membership.id, index, render.display.memberships.length);
64768
+ const presentation$1 = shopProductPresentation(render.hydration, membership.id, index, render.display.memberships.length);
65224
64769
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MembershipCardView, {
65225
64770
  display: membership,
65226
64771
  index,
65227
- presentation,
64772
+ presentation: presentation$1,
65228
64773
  action: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopSsrPurchaseAction, {
65229
64774
  action: membership.action,
65230
64775
  hydration: render.hydration,
65231
- presentation
64776
+ presentation: presentation$1
65232
64777
  })
65233
64778
  }, membership.id);
65234
64779
  })
65235
64780
  });
65236
64781
  }
65237
- function ShopSsrPurchaseAction({ action, hydration, presentation }) {
64782
+ function ShopSsrPurchaseAction({ action, hydration, presentation: presentation$1 }) {
65238
64783
  return action.kind === "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PassPurchaseUnavailable, { label: action.label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShopPurchaseAction, {
65239
- presentation,
64784
+ presentation: presentation$1,
65240
64785
  type: "button",
65241
64786
  variant: hydration.buttonVariant,
65242
64787
  size: "md",
@@ -65557,7 +65102,7 @@ function stripNumericPathSegments(path$3) {
65557
65102
  }
65558
65103
  const lookOwnedPathCache = new WeakMap();
65559
65104
  function lookOwnedPathsOf(content) {
65560
- if (!isPlainObject$3(content)) return [];
65105
+ if (!isPlainObject$2(content)) return [];
65561
65106
  const cached = lookOwnedPathCache.get(content);
65562
65107
  if (cached) return cached;
65563
65108
  const value = content._lookOwnedPaths;
@@ -65588,7 +65133,7 @@ function contentFieldSegments(fromPath, context) {
65588
65133
  if (first === "content") return rest;
65589
65134
  const named = [...context.scopes].reverse().find((entry) => entry.name === first);
65590
65135
  if (named) return scopedContentSegments(named, rest);
65591
- const innermost = [...context.scopes].reverse().find((entry) => isPlainObject$3(entry.value) || Array.isArray(entry.value));
65136
+ const innermost = [...context.scopes].reverse().find((entry) => isPlainObject$2(entry.value) || Array.isArray(entry.value));
65592
65137
  if (!innermost || first in context.viewModel) return undefined;
65593
65138
  return innermost.name === "content" ? [first, ...rest] : scopedContentSegments(innermost, [first, ...rest]);
65594
65139
  }
@@ -65647,7 +65192,7 @@ function readFromSource$1(source, segments) {
65647
65192
  /**
65648
65193
  * Type guard for plain objects.
65649
65194
  */
65650
- function isPlainObject$3(value) {
65195
+ function isPlainObject$2(value) {
65651
65196
  return typeof value === "object" && value !== null && !Array.isArray(value);
65652
65197
  }
65653
65198
  /**
@@ -65660,7 +65205,7 @@ function parseRouteEntry(routeId, value) {
65660
65205
  if (typeof value === "string") {
65661
65206
  return [routeId, value];
65662
65207
  }
65663
- if (isPlainObject$3(value)) {
65208
+ if (isPlainObject$2(value)) {
65664
65209
  return [routeId, value];
65665
65210
  }
65666
65211
  return null;
@@ -65669,11 +65214,11 @@ function parseRouteEntry(routeId, value) {
65669
65214
  * Extracts route map from $root.routes in the viewModel.
65670
65215
  */
65671
65216
  function extractRouteMapFromViewModel(viewModel) {
65672
- if (!isPlainObject$3(viewModel)) return undefined;
65217
+ if (!isPlainObject$2(viewModel)) return undefined;
65673
65218
  const root = viewModel.$root;
65674
- if (!isPlainObject$3(root)) return undefined;
65219
+ if (!isPlainObject$2(root)) return undefined;
65675
65220
  const routes = root.routes;
65676
- if (!isPlainObject$3(routes)) return undefined;
65221
+ if (!isPlainObject$2(routes)) return undefined;
65677
65222
  const entries = Object.entries(routes).map(([routeId, value]) => parseRouteEntry(routeId, value)).filter((entry) => entry !== null);
65678
65223
  if (entries.length === 0) return undefined;
65679
65224
  return Object.fromEntries(entries);
@@ -65682,22 +65227,22 @@ function extractRouteMapFromViewModel(viewModel) {
65682
65227
  * Extracts pagesByIdentifier from $root.pagesByIdentifier in the viewModel.
65683
65228
  */
65684
65229
  function extractPagesByIdentifierFromViewModel(viewModel) {
65685
- if (!isPlainObject$3(viewModel)) return undefined;
65230
+ if (!isPlainObject$2(viewModel)) return undefined;
65686
65231
  const root = viewModel.$root;
65687
- if (!isPlainObject$3(root)) return undefined;
65232
+ if (!isPlainObject$2(root)) return undefined;
65688
65233
  const pagesByIdentifier = root.pagesByIdentifier;
65689
- if (!isPlainObject$3(pagesByIdentifier)) return undefined;
65234
+ if (!isPlainObject$2(pagesByIdentifier)) return undefined;
65690
65235
  return pagesByIdentifier;
65691
65236
  }
65692
65237
  /**
65693
65238
  * Extracts entriesByIdentifier from $root.entriesByIdentifier in the viewModel.
65694
65239
  */
65695
65240
  function extractEntriesByIdentifierFromViewModel(viewModel) {
65696
- if (!isPlainObject$3(viewModel)) return undefined;
65241
+ if (!isPlainObject$2(viewModel)) return undefined;
65697
65242
  const root = viewModel.$root;
65698
- if (!isPlainObject$3(root)) return undefined;
65243
+ if (!isPlainObject$2(root)) return undefined;
65699
65244
  const entriesByIdentifier = root.entriesByIdentifier;
65700
- if (!isPlainObject$3(entriesByIdentifier)) return undefined;
65245
+ if (!isPlainObject$2(entriesByIdentifier)) return undefined;
65701
65246
  return entriesByIdentifier;
65702
65247
  }
65703
65248
  /**
@@ -65719,7 +65264,7 @@ function applyTransformsAndFallback(value, binding, context) {
65719
65264
  const routeMap = context.routes ?? extractRouteMapFromViewModel(context.viewModel);
65720
65265
  const pagesByIdentifier = context.pagesByIdentifier ?? extractPagesByIdentifierFromViewModel(context.viewModel);
65721
65266
  const entriesByIdentifier = context.entriesByIdentifier ?? extractEntriesByIdentifierFromViewModel(context.viewModel);
65722
- const root = isPlainObject$3(context.viewModel.$root) ? context.viewModel.$root : null;
65267
+ const root = isPlainObject$2(context.viewModel.$root) ? context.viewModel.$root : null;
65723
65268
  const stage = root?.previewStage === "preview" ? "preview" : "published";
65724
65269
  const transformed = runTransforms(value, binding.transforms?.map((step) => ({
65725
65270
  id: step.id,
@@ -65760,21 +65305,21 @@ function resolveTransformData(context, bindingPath) {
65760
65305
  const [first] = segments;
65761
65306
  if (typeof first === "string" && first !== "$root") {
65762
65307
  const namedScope = context.scopes.find((entry) => entry.name === first);
65763
- if (namedScope && isPlainObject$3(namedScope.value)) {
65308
+ if (namedScope && isPlainObject$2(namedScope.value)) {
65764
65309
  return namedScope.value;
65765
65310
  }
65766
65311
  }
65767
65312
  for (let i$1 = context.scopes.length - 1; i$1 >= 0; i$1 -= 1) {
65768
65313
  const scope = context.scopes[i$1];
65769
- if (scope && isPlainObject$3(scope.value)) {
65314
+ if (scope && isPlainObject$2(scope.value)) {
65770
65315
  return scope.value;
65771
65316
  }
65772
65317
  }
65773
65318
  const content = context.viewModel.content;
65774
- if (isPlainObject$3(content)) {
65319
+ if (isPlainObject$2(content)) {
65775
65320
  return content;
65776
65321
  }
65777
- if (isPlainObject$3(context.viewModel)) {
65322
+ if (isPlainObject$2(context.viewModel)) {
65778
65323
  return context.viewModel;
65779
65324
  }
65780
65325
  return undefined;
@@ -65877,7 +65422,7 @@ function richTextSelfPresence(node) {
65877
65422
  }
65878
65423
  }
65879
65424
  function hasResolvableRichTextImageSource(node) {
65880
- const attrs = isPlainObject$3(node.attrs) ? node.attrs : {};
65425
+ const attrs = isPlainObject$2(node.attrs) ? node.attrs : {};
65881
65426
  return hasNonBlankStringAttr(attrs, "src") || hasNonBlankStringAttr(attrs, "identifier");
65882
65427
  }
65883
65428
  function hasNonBlankStringAttr(attrs, key) {
@@ -66103,7 +65648,7 @@ function getNestedBinding(bindings, path$3) {
66103
65648
  const segments = path$3.split(".");
66104
65649
  let current = bindings;
66105
65650
  for (const segment of segments) {
66106
- if (!isPlainObject$3(current)) return undefined;
65651
+ if (!isPlainObject$2(current)) return undefined;
66107
65652
  current = current[segment];
66108
65653
  }
66109
65654
  return current;
@@ -66126,7 +65671,7 @@ function resolveBindingToEditorPath(fieldName, bindings, editableRouteMetadataFi
66126
65671
  };
66127
65672
  const binding = bindings[fieldName] ?? getNestedBinding(bindings, fieldName);
66128
65673
  if (!binding) return null;
66129
- if (isPlainObject$3(binding) && "source" in binding) {
65674
+ if (isPlainObject$2(binding) && "source" in binding) {
66130
65675
  const source = binding.source;
66131
65676
  if (source === "field" && "path" in binding) {
66132
65677
  const path$3 = binding.path;
@@ -66145,7 +65690,7 @@ function resolveBindingToEditorPath(fieldName, bindings, editableRouteMetadataFi
66145
65690
  if (typeof option === "string") {
66146
65691
  return `content.${option}`;
66147
65692
  }
66148
- if (isPlainObject$3(option) && "source" in option) {
65693
+ if (isPlainObject$2(option) && "source" in option) {
66149
65694
  const optSource = option.source;
66150
65695
  if (optSource === "field" && "path" in option) {
66151
65696
  const path$3 = option.path;
@@ -66203,7 +65748,7 @@ function derivePreviewTextEditableTarget(parsedBinding, context) {
66203
65748
  }
66204
65749
  }
66205
65750
  const root = context.viewModel?.$root;
66206
- const isEntryTemplatePage = isPlainObject$3(root) && root.contentEntry != null;
65751
+ const isEntryTemplatePage = isPlainObject$2(root) && root.contentEntry != null;
66207
65752
  if (isEntryTemplatePage && context.pathBase) {
66208
65753
  return {
66209
65754
  kind: "read-only",
@@ -66270,7 +65815,7 @@ function injectMediaSlotPath(props$1, node, parsedBinding, context) {
66270
65815
  if (entryPath) return props$1;
66271
65816
  }
66272
65817
  const root = context.viewModel?.$root;
66273
- const isEntryTemplatePage = isPlainObject$3(root) && root.contentEntry != null;
65818
+ const isEntryTemplatePage = isPlainObject$2(root) && root.contentEntry != null;
66274
65819
  if (isEntryTemplatePage && context.pathBase) return props$1;
66275
65820
  return {
66276
65821
  ...props$1,
@@ -66511,7 +66056,7 @@ function getNodeProps(node) {
66511
66056
  if (key.startsWith("$")) continue;
66512
66057
  legacy[key] = value;
66513
66058
  }
66514
- const props$1 = isPlainObject$3(node.props) ? node.props : {};
66059
+ const props$1 = isPlainObject$2(node.props) ? node.props : {};
66515
66060
  return {
66516
66061
  ...legacy,
66517
66062
  ...props$1
@@ -66553,7 +66098,7 @@ function bindItemSurfacePresentation(node, context) {
66553
66098
  const slot = resolveProps({ "data-slot": declared }, context, undefined)["data-slot"];
66554
66099
  if (!["item", "lead"].includes(String(slot))) return context;
66555
66100
  const content = context.blockContent;
66556
- if (!isPlainObject$3(content) || !isPlainObject$3(content._effectiveLook)) return context;
66101
+ if (!isPlainObject$2(content) || !isPlainObject$2(content._effectiveLook)) return context;
66557
66102
  const look = content._effectiveLook;
66558
66103
  if (!look.surfacePresentation) return context;
66559
66104
  const repeater = context.scopes.reduce((found, scope) => scope.repeater ?? found, undefined);
@@ -66566,14 +66111,14 @@ function bindItemSurfacePresentation(node, context) {
66566
66111
  function arrangementSlotProps(props$1, context) {
66567
66112
  const content = context.blockContent;
66568
66113
  const slot = props$1["data-slot"];
66569
- if (typeof slot !== "string" || !isPlainObject$3(content) || !isPlainObject$3(content._effectiveLook)) return {};
66114
+ if (typeof slot !== "string" || !isPlainObject$2(content) || !isPlainObject$2(content._effectiveLook)) return {};
66570
66115
  const look = content._effectiveLook;
66571
66116
  const surface = (context.surfacePresentation ?? look.surfacePresentation)?.slots[slot];
66572
66117
  return surface ? resolvedItemSurfaceProps(surface, slot, props$1.style) : { "data-surface": "section" };
66573
66118
  }
66574
66119
  function arrangementMediaPlaceholder(node, context) {
66575
66120
  const content = context.blockContent;
66576
- if (!isPlainObject$3(content) || content._lookAudience !== "authoring" || content.showPhotos === false) return null;
66121
+ if (!isPlainObject$2(content) || content._lookAudience !== "authoring" || content.showPhotos === false) return null;
66577
66122
  const from = arrangementPlaceholderMediaPath(node);
66578
66123
  if (!from) return null;
66579
66124
  const fieldPath = arrangementPlaceholderFieldPath(from, context);
@@ -66653,218 +66198,6 @@ function blockLookFromContent(content) {
66653
66198
  return typeof content.blockLook === "string" ? content.blockLook : undefined;
66654
66199
  }
66655
66200
 
66656
- //#endregion
66657
- //#region ../theme-core/src/accordions/defaults.ts
66658
- /**
66659
- * Generate default accordion system based on theme configuration
66660
- *
66661
- * Creates a single accordion style that harmonizes with the theme's
66662
- * visual language (spacing, borders, shadows, colors).
66663
- */
66664
- function generateDefaultAccordionSystem(theme) {
66665
- const overrides = theme.siteStyleId ? requireCuratedSiteStyle(theme.siteStyleId).themeOverrides?.accordions : undefined;
66666
- if (overrides) return overrides;
66667
- const { axes, space } = theme;
66668
- return {
66669
- background: {
66670
- type: "solid",
66671
- colorToken: "surface"
66672
- },
66673
- spacing: space === "comfortable" ? "comfortable" : space === "dense" ? "compact" : "medium",
66674
- corners: null,
66675
- border: defaultAccordionBorder(theme),
66676
- shadow: defaultAccordionShadow(theme),
66677
- icon: {
66678
- style: axes.tone === "bold" ? "plus-minus" : "chevron",
66679
- position: "right",
66680
- size: axes.density === "compact" ? "sm" : "md"
66681
- },
66682
- trigger: {
66683
- textColorToken: "text",
66684
- fontWeight: axes.tone === "bold" ? "bold" : axes.tone === "minimal" ? "medium" : "semibold"
66685
- },
66686
- hover: generateHoverEffects(axes),
66687
- content: { textColorToken: "neutral-700" }
66688
- };
66689
- }
66690
- function defaultAccordionBorder({ axes, border }) {
66691
- return axes.conversionBias === "high" || border.width !== "none" ? {
66692
- widthClass: border.width === "thick" ? "border-2" : "border",
66693
- colorToken: "border"
66694
- } : null;
66695
- }
66696
- function defaultAccordionShadow({ axes, shadow }) {
66697
- return axes.conversionBias === "high" || shadow.elevation !== "none" ? {
66698
- elevation: shadow.elevation === "none" ? "low" : shadow.elevation,
66699
- softness: shadow.softness === "crisp" || shadow.softness === null ? "soft" : shadow.softness,
66700
- position: shadow.position
66701
- } : null;
66702
- }
66703
- /**
66704
- * Generate hover effects based on theme characteristics
66705
- *
66706
- * Strategy:
66707
- * - Minimal/calm themes: subtle text color change only
66708
- * - Bold/vibrant themes: background + text color
66709
- * - High conversion themes: background + shadow elevation
66710
- */
66711
- function generateHoverEffects(axes) {
66712
- const { tone, energy, conversionBias } = axes;
66713
- if (tone === "minimal" && energy === "calm") {
66714
- return { textColorToken: "primary" };
66715
- }
66716
- if (tone === "bold" || conversionBias === "high") {
66717
- return {
66718
- backgroundColorToken: "primary/10",
66719
- textColorToken: "primary"
66720
- };
66721
- }
66722
- if (energy === "vibrant") {
66723
- return {
66724
- backgroundColorToken: "primary/5",
66725
- textColorToken: "primary"
66726
- };
66727
- }
66728
- return {
66729
- backgroundColorToken: "primary/5",
66730
- textColorToken: "primary"
66731
- };
66732
- }
66733
-
66734
- //#endregion
66735
- //#region ../theme-core/src/blockOverrides.ts
66736
- const BLOCK_PREFIX = "block.";
66737
- function isPlainObject$2(value) {
66738
- return typeof value === "object" && value !== null && !Array.isArray(value);
66739
- }
66740
- function isUnknownArray$1(value) {
66741
- return Array.isArray(value);
66742
- }
66743
- function cloneValue(value) {
66744
- if (isUnknownArray$1(value)) {
66745
- return value.map((item) => cloneValue(item));
66746
- }
66747
- if (isPlainObject$2(value)) {
66748
- const cloned = {};
66749
- for (const [key, child] of Object.entries(value)) {
66750
- cloned[key] = cloneValue(child);
66751
- }
66752
- return cloned;
66753
- }
66754
- return value;
66755
- }
66756
- function mergeDeep(defaultValue, overrideValue) {
66757
- if (overrideValue === undefined) {
66758
- return cloneValue(defaultValue);
66759
- }
66760
- if (overrideValue === null) {
66761
- return null;
66762
- }
66763
- if (isPlainObject$2(defaultValue) && isPlainObject$2(overrideValue)) {
66764
- const merged = {};
66765
- const keys = new Set([...Object.keys(defaultValue), ...Object.keys(overrideValue)]);
66766
- for (const key of keys) {
66767
- merged[key] = mergeDeep(defaultValue[key], overrideValue[key]);
66768
- }
66769
- return merged;
66770
- }
66771
- return cloneValue(overrideValue);
66772
- }
66773
- function normalizeBlockKind(blockKind) {
66774
- return blockKind.replace(/^block\./, "");
66775
- }
66776
- function buildBlockKindCandidates(blockKind) {
66777
- const normalized = normalizeBlockKind(blockKind);
66778
- const prefixed = `${BLOCK_PREFIX}${normalized}`;
66779
- return Array.from(new Set([
66780
- blockKind,
66781
- normalized,
66782
- prefixed
66783
- ]));
66784
- }
66785
- function resolveEffectiveHeadingDecoration(themeDecoration, override) {
66786
- if (override === "off" || !themeDecoration || themeDecoration.family === "none") {
66787
- return { kind: "none" };
66788
- }
66789
- return {
66790
- kind: "visible",
66791
- decoration: themeDecoration
66792
- };
66793
- }
66794
- function resolveBlockThemeOverride(theme, blockKind) {
66795
- if (!theme?.blockOverrides || !blockKind) return undefined;
66796
- for (const candidate of buildBlockKindCandidates(blockKind)) {
66797
- const override = theme.blockOverrides[candidate];
66798
- if (override) return override;
66799
- }
66800
- return undefined;
66801
- }
66802
- function mergeContentWithBlockStyleOverrides(content, theme, blockKind) {
66803
- const blockOverride = resolveBlockThemeOverride(theme, blockKind);
66804
- const styleOverrides = blockOverride?.styles;
66805
- const mergedContent = { ...content };
66806
- if (styleOverrides) {
66807
- applyStyleOverride(mergedContent, "_sectionStyles", styleOverrides.sectionStyles);
66808
- applyStyleOverride(mergedContent, "_containerStyles", styleOverrides.containerStyles);
66809
- applyStyleOverride(mergedContent, "_cardStyles", styleOverrides.cardStyles);
66810
- }
66811
- applyHeadingDecorationSignature(mergedContent, theme?.headingDecoration);
66812
- return mergedContent;
66813
- }
66814
- /**
66815
- * Projects the site-wide heading decoration signature onto a transient
66816
- * `_sectionStyles.headingDecoration`, honouring the per-section override:
66817
- * - `off` strips the decoration (the section opts out).
66818
- * - `site-default` / absent use the theme signature.
66819
- *
66820
- * Clone-on-write so the caller's content is never mutated in place. Any value
66821
- * previously baked into content is intentionally overwritten — the theme is the
66822
- * single runtime source.
66823
- */
66824
- function applyHeadingDecorationSignature(target, themeDecoration) {
66825
- const existing = target["_sectionStyles"];
66826
- const existingObj = isPlainObject$2(existing) ? existing : undefined;
66827
- const rawOverride = existingObj?.["headingDecorationOverride"];
66828
- const override = rawOverride === "site-default" || rawOverride === "off" ? rawOverride : undefined;
66829
- const effective = resolveEffectiveHeadingDecoration(themeDecoration, override);
66830
- if (effective.kind === "none") {
66831
- if (existingObj && "headingDecoration" in existingObj) {
66832
- const next$1 = { ...existingObj };
66833
- delete next$1["headingDecoration"];
66834
- target["_sectionStyles"] = next$1;
66835
- }
66836
- return;
66837
- }
66838
- const next = existingObj ? { ...existingObj } : {};
66839
- next["headingDecoration"] = { ...effective.decoration };
66840
- target["_sectionStyles"] = next;
66841
- }
66842
- function applyStyleOverride(target, key, defaults) {
66843
- if (defaults === undefined) return;
66844
- target[key] = mergeStyleOverride(defaults, target[key]);
66845
- }
66846
- function mergeStyleOverride(defaultValue, contentValue) {
66847
- if (!isPlainObject$2(defaultValue) || !isPlainObject$2(contentValue)) {
66848
- return mergeDeep(defaultValue, contentValue);
66849
- }
66850
- const merged = mergeDeep(defaultValue, contentValue);
66851
- if (!isPlainObject$2(merged)) return merged;
66852
- return {
66853
- ...merged,
66854
- background: mergeBoxBackground(defaultValue.background, contentValue.background)
66855
- };
66856
- }
66857
- function mergeBoxBackground(defaultValue, contentValue) {
66858
- if (contentValue === undefined) return cloneValue(defaultValue);
66859
- if (contentValue === null) return null;
66860
- const defaults = boxBackgroundSchema.safeParse(defaultValue);
66861
- const content = boxBackgroundSchema.safeParse(contentValue);
66862
- if (!content.success) return cloneValue(contentValue);
66863
- if (!defaults.success || defaults.data.type !== content.data.type) return content.data;
66864
- const merged = boxBackgroundSchema.safeParse(mergeDeep(defaults.data, content.data));
66865
- return merged.success ? merged.data : content.data;
66866
- }
66867
-
66868
66201
  //#endregion
66869
66202
  //#region ../blocks/src/system/componentFactory.ts
66870
66203
  /**
@@ -67273,57 +66606,6 @@ function collectConstrainedVideoMediaValues(options) {
67273
66606
  return values;
67274
66607
  }
67275
66608
 
67276
- //#endregion
67277
- //#region ../blocks/src/sdkBlockTransform.ts
67278
- /**
67279
- * Transforms an SDK custom block into a BlockManifest.
67280
- *
67281
- * If the block has a `layout`, it can be rendered declaratively without React code.
67282
- * If no layout, the block MUST use blockOverrides for rendering.
67283
- */
67284
- function transformSdkBlockToManifest(sdkBlock) {
67285
- const hasLayout = Boolean(sdkBlock.layout) || Boolean(sdkBlock.variants && Object.keys(sdkBlock.variants).length > 0);
67286
- const manifest = {
67287
- variantMeaning: { kind: "arrangement" },
67288
- name: sdkBlock.id,
67289
- version: "1.0.0",
67290
- title: sdkBlock.title,
67291
- titleSource: sdkBlock.titleSource,
67292
- description: sdkBlock.description,
67293
- component: sdkBlock.id,
67294
- fields: sdkBlock.fields,
67295
- slots: [],
67296
- category: sdkBlock.category,
67297
- tags: sdkBlock.tags,
67298
- icon: sdkBlock.icon,
67299
- layout: sdkBlock.layout,
67300
- variants: sdkBlock.variants,
67301
- defaultVariant: sdkBlock.defaultVariant,
67302
- variantOptions: sdkBlock.variantOptions,
67303
- promotedFields: sdkBlock.promotedFields,
67304
- lookField: sdkBlock.lookField,
67305
- behaviours: {
67306
- supportsThemeSwitching: true,
67307
- inlineEditing: hasLayout,
67308
- animation: hasLayout,
67309
- paletteHidden: false
67310
- }
67311
- };
67312
- return augmentManifest(manifest);
67313
- }
67314
- /**
67315
- * Transforms an SDK custom block into a SystemBlockDefinition.
67316
- *
67317
- * This creates a minimal definition with just the manifest - no data loaders
67318
- * or content normalizers since SDK blocks handle their own rendering.
67319
- */
67320
- function transformSdkBlockToDefinition(sdkBlock) {
67321
- return {
67322
- manifest: transformSdkBlockToManifest(sdkBlock),
67323
- dataLoaders: sdkBlock.dataLoaders
67324
- };
67325
- }
67326
-
67327
66609
  //#endregion
67328
66610
  //#region ../blocks/src/customBlockRegistry.ts
67329
66611
  const defaultCustomBlockSourcePolicy = { kind: "canonical-db-fallback-sdk" };
@@ -67774,7 +67056,7 @@ function planArrangementContent(blockKind, manifest, content, audience) {
67774
67056
  });
67775
67057
  return {
67776
67058
  ...activeContent,
67777
- ...arrangementPhotoTreatmentContent(activeContent, effective.look),
67059
+ ...arrangementPhotoTreatmentContent(activeContent, effective.look, runtime.surfaceContext.palette, values.photoTint === "brand" ? "brand" : "dark"),
67778
67060
  ...mode !== null ? { variant: mode } : {},
67779
67061
  ...effectiveContent,
67780
67062
  ...photoContent,
@@ -67795,13 +67077,13 @@ function planArrangementContent(blockKind, manifest, content, audience) {
67795
67077
  };
67796
67078
  }
67797
67079
  /** Typed Look composition owns contrast treatment; parsing preserves authored paint. */
67798
- function arrangementPhotoTreatmentContent(content, look) {
67080
+ function arrangementPhotoTreatmentContent(content, look, palette, tint) {
67799
67081
  if (!look.structure.photoTreatment) return {};
67800
67082
  const authored = parseCanonicalRuntimeBackground(content);
67801
67083
  if (authored.kind === "none") return {};
67802
67084
  return { _sectionStyles: {
67803
67085
  ...isObjectRecord(content._sectionStyles) ? content._sectionStyles : {},
67804
- background: applyLookPhotoTreatment(authored.background, look.structure.photoTreatment)
67086
+ background: applyLookPhotoTreatment(authored.background, look.structure.photoTreatment, palette, tint)
67805
67087
  } };
67806
67088
  }
67807
67089
  /** Widget section hooks use the theme; hero background geometry never travels to list islands. */
@@ -67880,8 +67162,10 @@ function effectiveContentKnobValue(blockKind, key, value) {
67880
67162
  function arrangementContentHooks(effective, mode, length, facts, values, photoCount) {
67881
67163
  return {
67882
67164
  "data-look": effective.look.id,
67883
- "data-treat": effective.treatment,
67884
- "data-arrangement": effective.look.structure.byMode[mode ?? "default"],
67165
+ ...mode === "combined" ? { "data-arrangement": "combined" } : {
67166
+ "data-treat": effective.treatment,
67167
+ "data-arrangement": effective.look.structure.byMode[mode ?? "default"]
67168
+ },
67885
67169
  ...mode !== null ? { "data-mode": mode } : {},
67886
67170
  ...length !== null ? { "data-length": length } : {},
67887
67171
  ...Object.fromEntries(Object.entries(facts).map(([key, value]) => [`data-has-${key}`, String(value)])),
@@ -67900,11 +67184,13 @@ function applyArrangementRhythm(entry, position) {
67900
67184
  const surface = arrangementGroundToken(resolved);
67901
67185
  const grounded = resolveArrangementSurfaces(entry, look, runtime, resolved, updated$1);
67902
67186
  const shape = sectionShapeHook(sectionShapeForGround(runtime.fieldGrounds, surface));
67187
+ const combined = resolveCombinedLook(entry, grounded, runtime, resolved, updated$1);
67903
67188
  return {
67904
67189
  ...entry,
67905
67190
  styleBackground: arrangementGroundToken(resolveSectionBackground({ kind: "none" }, null, rhythm)),
67906
67191
  renderableContent: {
67907
67192
  ...updated$1,
67193
+ ...combined ? { _combinedLook: combined } : {},
67908
67194
  _effectiveLook: grounded,
67909
67195
  _lookHooks: {
67910
67196
  ...isObjectRecord(updated$1._lookHooks) ? updated$1._lookHooks : {},
@@ -67914,6 +67200,7 @@ function applyArrangementRhythm(entry, position) {
67914
67200
  }
67915
67201
  };
67916
67202
  }
67203
+ /** The section's name for hooks such as `data-surface`. Surfaces take sectionSurfaceBase, where media and gradients are never colours. */
67917
67204
  function arrangementGroundToken(resolved) {
67918
67205
  if (resolved.kind === "base") return "background";
67919
67206
  switch (resolved.background.type) {
@@ -67984,19 +67271,69 @@ function resolveArrangementSurfaces(entry, look, runtime, background, content) {
67984
67271
  context: runtime.surfaceContext
67985
67272
  };
67986
67273
  const parents = plannedSlotParents(entry, look);
67274
+ const base = sectionSurfaceBase(background, runtime.surfaceContext.palette);
67275
+ const surfacePresentation = resolveLookSurfacePresentation({
67276
+ look,
67277
+ parents,
67278
+ ground,
67279
+ base,
67280
+ style,
67281
+ content
67282
+ });
67283
+ if (!lookPresentationOnPalette(surfacePresentation, runtime.surfaceContext.palette)) throw new Error(`Look ${look.look.id} resolved its surfaces against a different palette from this page's theme`);
67987
67284
  return {
67988
67285
  ...look,
67989
67286
  ground,
67990
- surfacePresentation: resolveLookSurfacePresentation({
67991
- look,
67992
- parents,
67993
- ground,
67994
- base: arrangementGroundToken(background),
67995
- style,
67996
- content
67997
- })
67287
+ surfacePresentation
67998
67288
  };
67999
67289
  }
67290
+ /** The combined mode owns its resolved list and month, including independent paint. */
67291
+ function resolveCombinedLook(entry, list, runtime, background, content) {
67292
+ if (entry.block.kind !== "block.event-calendar" || content.variant !== "combined") return null;
67293
+ const month = runtime.byMode.calendar;
67294
+ if (!month) throw new Error("Combined calendar needs a month Look");
67295
+ const arrangement = month.look.structure.byMode.calendar;
67296
+ if (arrangement !== "month-grid" && arrangement !== "month-day") throw new Error("Invalid combined month arrangement");
67297
+ const ground = deriveSectionGround(background, runtime.surfaceContext.palette);
67298
+ const surfacePresentation = resolveLookSurfacePresentation({
67299
+ look: month,
67300
+ parents: arrangementSlotParentsForManifest(entry.kind === "renderable" ? entry.definition.manifest : undefined, month.look.target, arrangement),
67301
+ ground,
67302
+ base: sectionSurfaceBase(background, runtime.surfaceContext.palette),
67303
+ content,
67304
+ style: {
67305
+ roleMap: month.roleMap,
67306
+ surfaces: runtime.surfaces,
67307
+ context: runtime.surfaceContext
67308
+ }
67309
+ });
67310
+ const presentation$1 = (look, mode, arrangement$1) => ({
67311
+ lookSlots: arrangementContentSlots(entry.block.kind),
67312
+ itemRoles: look.itemRoles,
67313
+ surfacePresentation: look.surfacePresentation,
67314
+ hooks: {
67315
+ "data-look": look.look.id,
67316
+ "data-treat": look.treatment,
67317
+ "data-mode": mode,
67318
+ "data-arrangement": arrangement$1
67319
+ }
67320
+ });
67321
+ return combinedLookSchema.parse({
67322
+ list: {
67323
+ ...presentation$1(list, "list", list.look.structure.byMode.combined),
67324
+ arrangement: list.look.structure.byMode.combined,
67325
+ panelHeading: content._lookPanelHeading,
67326
+ itemActionStyle: content.actionStyle
67327
+ },
67328
+ month: {
67329
+ ...presentation$1({
67330
+ ...month,
67331
+ surfacePresentation
67332
+ }, "calendar", arrangement),
67333
+ arrangement
67334
+ }
67335
+ });
67336
+ }
68000
67337
  function plannedSlotParents(entry, look) {
68001
67338
  const fallback = Object.values(look.look.structure.byMode)[0] ?? "";
68002
67339
  if (entry.kind !== "renderable") return arrangementSlotParentsForManifest(undefined, look.look.target, fallback);
@@ -68014,10 +67351,10 @@ function pageOpeningPhotoPlacement(blockKind, content, arrangementKey) {
68014
67351
  }
68015
67352
  /** Header consumers receive the opening that the public render plan actually admits. */
68016
67353
  function resolvePageOpeningSection(page, options) {
68017
- const presentation = toPresentationPage(page);
68018
- if (!presentation) return null;
67354
+ const presentation$1 = toPresentationPage(page);
67355
+ if (!presentation$1) return null;
68019
67356
  const opening = buildPageRenderPlan({
68020
- page: presentation,
67357
+ page: presentation$1,
68021
67358
  ...options,
68022
67359
  customBlocks: options.customBlocks,
68023
67360
  audience: "public",
@@ -68775,16 +68112,20 @@ function resolveLocalBlockManifestFromSources(input) {
68775
68112
  if (input.builtInManifest) {
68776
68113
  return {
68777
68114
  kind: "builtin_found",
68778
- manifest: input.builtInManifest,
68779
- extensionFields
68115
+ manifest: mergeManifestWithSdkOptions({
68116
+ ...input.builtInManifest,
68117
+ fields: parseFieldDefinitions([...input.builtInManifest.fields])
68118
+ }, input.kind, undefined, parseSdkBlockFieldExtensions(input.blockFieldExtensions))
68780
68119
  };
68781
68120
  }
68782
68121
  const customBlock = input.customBlocks?.find((block) => block.id === input.kind);
68783
68122
  if (customBlock) {
68784
68123
  return {
68785
68124
  kind: "custom_found",
68786
- manifest: sdkCustomBlockManifest(customBlock),
68787
- extensionFields
68125
+ manifest: mergeManifestWithSdkOptions({
68126
+ ...sdkCustomBlockManifest(customBlock),
68127
+ fields: parseFieldDefinitions(customBlock.fields)
68128
+ }, input.kind, undefined, parseSdkBlockFieldExtensions(input.blockFieldExtensions))
68788
68129
  };
68789
68130
  }
68790
68131
  if (extensionFields.length > 0 && input.kind.startsWith("custom.")) {
@@ -68958,7 +68299,7 @@ function validateLocalBlock(input) {
68958
68299
  case "extension_only_custom_found": break;
68959
68300
  default: assertNever(resolution);
68960
68301
  }
68961
- const validation = validateBlockData(input.data, resolution.manifest, resolution.kind === "builtin_found" || resolution.kind === "custom_found" ? [...resolution.extensionFields] : undefined);
68302
+ const validation = validateBlockData(input.data, resolution.manifest);
68962
68303
  const warnings = formatValidationResult(input.pageIdentifier, input.blockIdentifier, input.kind, validation);
68963
68304
  const blockingIssues = [...validation.errors, ...input.invalidContentAsError ? validation.warnings.filter((warning) => warning.type === "invalid_content") : []];
68964
68305
  if (blockingIssues.length === 0) return ok(warnings);
@@ -74929,8 +74270,8 @@ function unconfirmedEventWarnings(result) {
74929
74270
  const updates = result.kind === "scoped" && result.result.ok ? result.result.result.unconfirmedEventUpdates ?? [] : result.kind === "all" && "operations" in result.dated ? result.dated.operations.flatMap((operation) => operation.kind === "executed" ? operation.result.unconfirmedEventUpdates ?? [] : []) : [];
74930
74271
  return updates.map(() => "The schedule was saved, but we couldn’t finish updating the event. Check it before making another change.");
74931
74272
  }
74932
- function eventWriteWarnings(result) {
74933
- const warnings = result.kind === "scoped" && result.result.ok ? result.result.result.eventWriteWarnings ?? [] : result.kind === "all" && "operations" in result.dated ? result.dated.operations.flatMap((operation) => operation.kind === "executed" ? operation.result.eventWriteWarnings ?? [] : []) : [];
74273
+ function writeWarnings(result) {
74274
+ const warnings = result.kind === "scoped" && result.result.ok ? result.result.result.writeWarnings ?? [] : result.kind === "all" && "operations" in result.dated ? result.dated.operations.flatMap((operation) => operation.kind === "executed" ? operation.result.writeWarnings ?? [] : []) : [];
74934
74275
  return warnings.map((warning) => `${warning.code}: ${warning.message}`);
74935
74276
  }
74936
74277
  function reportPreparedWarnings(output, prepared, result) {
@@ -74940,7 +74281,7 @@ function reportPreparedWarnings(output, prepared, result) {
74940
74281
  ...resultWarnings(result),
74941
74282
  ...cancellationWarnings(result),
74942
74283
  ...unconfirmedEventWarnings(result),
74943
- ...eventWriteWarnings(result)
74284
+ ...writeWarnings(result)
74944
74285
  ].forEach((warning) => output.warn(warning));
74945
74286
  }
74946
74287
  function reportPageDraftGuidance(output, prepared, result) {
@@ -78267,6 +77608,7 @@ function renderSystemBlockDefinition(definition$1) {
78267
77608
  lines.push(...renderFieldList(manifest.fields, 0));
78268
77609
  }
78269
77610
  lines.push(...renderLookOwnedFields(manifest));
77611
+ lines.push(...renderLookKnobs(manifest));
78270
77612
  lines.push("");
78271
77613
  return lines;
78272
77614
  }
@@ -78281,6 +77623,19 @@ function renderLookOwnedFields(manifest) {
78281
77623
  const paths = fieldPathsOwnedBy(ownership, ["look"]);
78282
77624
  return paths.length === 0 ? [] : ["", `On site styles with Looks, the Look sets these while it shows the block, and stored values have no effect: ${paths.join(", ")}`];
78283
77625
  }
77626
+ /** Which Looks a block can show, and the choices (knobs) each one leaves to the owner. */
77627
+ function renderLookKnobs(manifest) {
77628
+ const looks = arrangementLooksForBlock(manifest.name);
77629
+ if (looks.length === 0) return [];
77630
+ return [
77631
+ "",
77632
+ "Looks for this block, and the knobs each one offers:",
77633
+ ...looks.map(({ id, label, knobs }) => {
77634
+ const offered = knobs.length === 0 ? "no knobs" : knobs.map((knob) => knob.values.length === 0 ? `${knob.label} (\`${knob.id}\`, free text)` : `${knob.label} (\`${knob.id}\`: ${knob.values.join(", ")})`).join("; ");
77635
+ return `- ${label} (\`${id}\`): ${offered}`;
77636
+ })
77637
+ ];
77638
+ }
78284
77639
  function renderVariantDocumentation(manifest) {
78285
77640
  if (!manifest.variants || Object.keys(manifest.variants).length === 0) {
78286
77641
  return [];
@@ -78366,7 +77721,7 @@ function renderEffectiveSystemBlocksSection(systemBlockIds, config$1) {
78366
77721
  if (overriddenOptionFields.length > 0) {
78367
77722
  lines.push(`- Option overrides: ${overriddenOptionFields.join(", ")}`);
78368
77723
  }
78369
- const effectiveFields = applySiteFieldExtensionsAndOptions(base.fields, extensions, optionsMap);
77724
+ const effectiveFields = mergeManifestWithSdkOptions(base, blockId, config$1.blockFieldOptions, parseSdkBlockFieldExtensions(config$1.blockFieldExtensions)).fields;
78370
77725
  if (base.variants && Object.keys(base.variants).length > 0) {
78371
77726
  const keys = Object.keys(base.variants);
78372
77727
  lines.push(`- Variants: ${keys.join(", ")} (default: ${base.defaultVariant ?? keys[0] ?? "default"})`);
@@ -78378,21 +77733,6 @@ function renderEffectiveSystemBlocksSection(systemBlockIds, config$1) {
78378
77733
  }
78379
77734
  return lines.join("\n");
78380
77735
  }
78381
- function applySiteFieldExtensionsAndOptions(baseFields, extensionFields, optionsMap) {
78382
- const seen = new Set(baseFields.map((field) => field.id));
78383
- const withOptionOverrides = baseFields.map((field) => {
78384
- const override = optionsMap[field.id];
78385
- if (!override?.options || override.options.length === 0) return field;
78386
- if (!("options" in field)) return field;
78387
- if (!Array.isArray(field.options)) return field;
78388
- return {
78389
- ...field,
78390
- options: override.options
78391
- };
78392
- });
78393
- const safeExtensions = extensionFields.filter((field) => field?.id && !seen.has(field.id));
78394
- return [...withOptionOverrides, ...safeExtensions];
78395
- }
78396
77736
 
78397
77737
  //#endregion
78398
77738
  //#region src/init-docs/templates.ts
@@ -78766,7 +78106,7 @@ async function loadConfig(configPath, output) {
78766
78106
  return { errorMessage: "No config path provided." };
78767
78107
  }
78768
78108
  try {
78769
- const { loadConfigFile: loadConfigFile$1 } = await import("../load-config-BB5L2u1y.mjs");
78109
+ const { loadConfigFile: loadConfigFile$1 } = await import("../load-config-DkoaFO1t.mjs");
78770
78110
  const rawConfig = await loadConfigFile$1(configPath);
78771
78111
  const parsed = riverbankSiteConfigSchema.safeParse(rawConfig);
78772
78112
  if (!parsed.success) {
@@ -79778,9 +79118,7 @@ function buildAuditContext(localContent, remoteContent) {
79778
79118
  localVenueRefs: new Set(localContent.venues.map((venue) => venue.slug)),
79779
79119
  remoteVenueRefs: new Set((remoteContent.venues ?? []).map((venue) => venue.slug)),
79780
79120
  localCategoryRefs: new Set(localContent.eventCategories.map((category) => category.identifier)),
79781
- remoteCategoryRefs: new Set((remoteContent.eventCategories ?? []).map((category) => category.identifier)),
79782
- localFormRefs: new Set(localContent.forms.map((form$1) => form$1.slug)),
79783
- remoteFormRefs: new Set((remoteContent.forms ?? []).map((form$1) => form$1.slug))
79121
+ remoteCategoryRefs: new Set((remoteContent.eventCategories ?? []).map((category) => category.identifier))
79784
79122
  };
79785
79123
  }
79786
79124
  async function prepareAuditTarget(_target, loaded) {
@@ -80115,13 +79453,6 @@ function auditEntityReferences(entities, ctx, labels) {
80115
79453
  message: `${labels.entity} "${entity.identifier}" references unknown event category "${entity.categoryRef ?? "none"}"`
80116
79454
  });
80117
79455
  }
80118
- if (!validateRefExists(entity.formRef, ctx.localFormRefs, ctx.remoteFormRefs)) {
80119
- issues.push({
80120
- severity: "error",
80121
- type: labels.formIssueType,
80122
- message: `${labels.entity} "${entity.identifier}" references unknown form "${entity.formRef ?? "none"}"`
80123
- });
80124
- }
80125
79456
  }
80126
79457
  return issues;
80127
79458
  }
@@ -80129,8 +79460,7 @@ function auditEventReferences(events, ctx) {
80129
79460
  return auditEntityReferences(events, ctx, {
80130
79461
  entity: "Event",
80131
79462
  venueIssueType: "invalid-event-venue-ref",
80132
- categoryIssueType: "invalid-event-category-ref",
80133
- formIssueType: "invalid-event-form-ref"
79463
+ categoryIssueType: "invalid-event-category-ref"
80134
79464
  });
80135
79465
  }
80136
79466
  function auditResultPayload(result) {
@@ -81527,7 +80857,6 @@ function cancelledRunInput(run) {
81527
80857
  waitlistMode: run.waitlistMode,
81528
80858
  bookingOffer: run.bookingOffer,
81529
80859
  venueRef: run.venueRef,
81530
- formRef: run.formRef,
81531
80860
  settings: run.settings,
81532
80861
  tags: run.tags,
81533
80862
  ...timing,
@@ -84693,7 +84022,7 @@ function parseSuccessfulData(endpoint$1, result, schema) {
84693
84022
  if (!result.success) {
84694
84023
  throw new ApiEnvelopeError(result.error);
84695
84024
  }
84696
- const parsed = schema.safeParse(result.data);
84025
+ const parsed = z.safeParse(schema, result.data);
84697
84026
  if (!parsed.success) {
84698
84027
  throw new AccountApiMalformedResponseError(endpoint$1, parsed.error.issues);
84699
84028
  }
@@ -90114,6 +89443,17 @@ const previewFrameHeightMessageSchema = z.object({
90114
89443
  /** Sent by a framed request that has no preview authority, so the dashboard stops waiting. */
90115
89444
  const previewFrameFailedMessageSchema = z.object({ type: z.literal("riverbank:preview-failed") }).strict();
90116
89445
 
89446
+ //#endregion
89447
+ //#region ../api/src/footerCompositionContracts.ts
89448
+ /** The credit line the served footer ends with, or null when it shows none. */
89449
+ function footerCreditLine(setting) {
89450
+ switch (setting.kind) {
89451
+ case "not-shown": return null;
89452
+ case "required": return setting.line;
89453
+ case "optional": return setting.hidden ? null : setting.line;
89454
+ }
89455
+ }
89456
+
90117
89457
  //#endregion
90118
89458
  //#region ../api/src/footerSettings.ts
90119
89459
  async function getFooterComposition(apiClient, siteId) {
@@ -90165,6 +89505,13 @@ async function updatePublicProfile(apiClient, siteId, body) {
90165
89505
  body
90166
89506
  }));
90167
89507
  }
89508
+ async function updateFooterCredit(apiClient, siteId, body) {
89509
+ return unwrapResponse(await apiClient({
89510
+ endpoint: "updateFooterCredit",
89511
+ params: { siteId },
89512
+ body
89513
+ }));
89514
+ }
90168
89515
 
90169
89516
  //#endregion
90170
89517
  //#region ../api/src/demoDashboardMutationSurfaces.ts
@@ -91873,78 +91220,170 @@ function section(id, title, paragraphs) {
91873
91220
  paragraphs
91874
91221
  };
91875
91222
  }
91876
- function buildManagedPrivacyPolicyDocument(input) {
91877
- const { identity, capabilities, effectiveDate } = input;
91878
- const controller = normalizeEntityName(identity);
91879
- const privacyEmail = normalizePrivacyEmail(identity);
91880
- const jurisdictionLabel = buildJurisdictionLabel(identity.jurisdiction);
91881
- const sections = [section("controller", "Who controls your data", [
91882
- `${controller} is the data controller for personal data collected through this site.`,
91883
- `If you have privacy questions, contact ${privacyEmail}.`,
91884
- `This policy is written for visitors in ${jurisdictionLabel}.`
91885
- ]), section("data-collected", "Data we collect", ["We only collect data needed to run this site and provide requested services.", "Depending on how you use the site, this can include contact details, booking details, payment-related metadata, and technical usage data."])];
91886
- const cookiesParagraphs = ["This site uses necessary cookies to keep core functionality working (for example security, session continuity, and consent choices)."];
91887
- if (capabilities.usesAnalyticsCookies) {
91888
- cookiesParagraphs.push("This site also uses optional analytics cookies to understand traffic and improve performance. These are only used when you explicitly accept analytics cookies.");
91223
+ /**
91224
+ * The date of the generated policy's current wording. A published policy
91225
+ * shows whichever is later, this or its publication date, so a site that
91226
+ * published before a wording change doesn't show the old date on new words.
91227
+ * Move it forward whenever the wording below changes what a policy says.
91228
+ */
91229
+ const MANAGED_PRIVACY_POLICY_WORDING_DATE = "2026-10-22";
91230
+ /** The later of the publication date and the wording date, both `YYYY-MM-DD`. */
91231
+ function resolveManagedPrivacyPolicyEffectiveDate(publishedOn) {
91232
+ return publishedOn > MANAGED_PRIVACY_POLICY_WORDING_DATE ? publishedOn : MANAGED_PRIVACY_POLICY_WORDING_DATE;
91233
+ }
91234
+ /** The jurisdiction as a place in a sentence: "the United Kingdom", but "Canada". */
91235
+ function jurisdictionPlace(jurisdiction) {
91236
+ const label = buildJurisdictionLabel(jurisdiction);
91237
+ return jurisdiction === "UK" || jurisdiction === "US" || jurisdiction === "EU" ? `the ${label}` : label;
91238
+ }
91239
+ /** UK GDPR and EU GDPR give the full set of rights; elsewhere they depend on local law. */
91240
+ function hasGdprRights(jurisdiction) {
91241
+ return jurisdiction === "UK" || jurisdiction === "EU";
91242
+ }
91243
+ function controllerSection(context) {
91244
+ return section("controller", "Who we are", [
91245
+ `${context.controller} runs this website and is responsible for the personal information collected through it. In data protection law, that makes us the "data controller".`,
91246
+ `If you have a question about this policy or about your information, contact ${context.privacyEmail}.`,
91247
+ `This policy is written for people in ${jurisdictionPlace(context.jurisdiction)}.`
91248
+ ]);
91249
+ }
91250
+ function dataCollectedSection() {
91251
+ return section("data-collected", "What we collect and why", ["We only collect the information we need to run this site and provide the things you ask us for.", "The sections below explain what we collect for each part of the site, why we need it, and the legal reason we rely on to use it (sometimes called the \"lawful basis\")."]);
91252
+ }
91253
+ function cookiesSection(context) {
91254
+ const paragraphs = ["This site uses necessary cookies to keep it working, for example to keep it secure, keep you signed in and remember your cookie choice. The site can't work properly without them, so they don't need your consent. We rely on our legitimate interest in running a secure site that works."];
91255
+ if (context.capabilities.usesAnalyticsCookies) {
91256
+ paragraphs.push("With your permission, this site also uses analytics cookies to count visits and see which pages are used, so we can improve it. They're run by Riverbank, which provides this site, not by an advertising company. They're only set if you accept them in the cookie banner, and we rely on your consent to use them.");
91889
91257
  } else {
91890
- cookiesParagraphs.push("This site does not use optional analytics cookies at this time.");
91891
- }
91892
- sections.push(section("cookies", "Cookies", cookiesParagraphs));
91893
- if (capabilities.usesForms) {
91894
- sections.push(section("forms", "Forms and enquiries", [
91895
- "When you submit a form, we process the information you provide so we can respond to your enquiry or request.",
91896
- "We keep identifiable form submissions for as long as needed to handle the enquiry, maintain operational records, and meet any legal requirements that apply to the site.",
91897
- "Where the platform applies retention controls, older submissions may be anonymised so the record can still support reporting or operational history without keeping the full identifying content indefinitely."
91898
- ]));
91899
- }
91900
- if (capabilities.usesNewsletter) {
91901
- sections.push(section("newsletter", "Newsletter and marketing emails", [
91902
- "If you subscribe to updates, we use your email address to send the communications you request.",
91903
- "Our emails may let us see when they are opened and which links are clicked, using a small image and tracked links. We use this to understand which emails people find useful. Turning off images in your email app stops the open check.",
91904
- "You can unsubscribe at any time using the unsubscribe link in those emails.",
91905
- "We may keep limited suppression information after you unsubscribe so we can respect that preference and avoid sending unwanted emails."
91906
- ]));
91258
+ paragraphs.push("This site doesn't use analytics or advertising cookies.");
91907
91259
  }
91908
- if (capabilities.usesBookings) {
91909
- sections.push(section("bookings", "Bookings and customer records", [
91910
- "If you make a booking, we process details needed to manage attendance, scheduling, confirmations, and customer support.",
91911
- "We keep identifiable booking records for as long as needed to manage the booking, support customer service, and meet any legal or accounting requirements that apply to the site.",
91912
- "Where appropriate, older records may be reduced to a smaller history or ledger record so the site can keep the business history it needs without retaining the same level of personal detail forever."
91913
- ]));
91914
- }
91915
- if (capabilities.usesSensitiveClientRecords) {
91916
- sections.push(section("sensitive-client-records", "Sensitive client records", [
91917
- "This site may store sensitive client records, such as practitioner notes, appointment history, sensitive form responses, booking-question answers, and details shared before or during a session.",
91918
- `${controller} remains responsible for deciding the lawful basis and any special-category data condition that applies to these records.`,
91919
- "Sensitive client records are handled with protected access controls and are retained under controller-managed review rather than automatic platform deletion.",
91920
- "You can request access, correction, or deletion of these records, subject to legal, safeguarding, professional, and operational limits that may apply."
91921
- ]));
91922
- }
91923
- if (capabilities.usesCustomerPortal) {
91924
- sections.push(section("portal", "Customer portal sessions", [
91925
- "If this site offers a customer portal, we use secure session data so you can sign in and manage your own bookings or account details.",
91926
- "These session cookies are necessary for authentication and account security.",
91927
- "Short-lived portal sign-in and session artifacts are kept only for the period needed to operate and secure the portal, then removed under the platform’s security retention controls."
91928
- ]));
91929
- }
91930
- if (capabilities.usesPayments) {
91931
- sections.push(section("payments", "Payments", ["Payments are processed by our payment processor(s). We do not store full card numbers on this platform.", "Payment providers process payment data under their own security and compliance obligations."]));
91932
- }
91933
- sections.push(section("sharing", "Who we share data with", ["We use trusted service providers (for example hosting, email delivery, analytics, and payment processors) to operate the site.", "These providers process personal data only as needed to deliver their services to us."]));
91934
- sections.push(section("retention", "How long we keep personal data", [
91935
- "We keep personal data only for as long as it is needed for the purposes described in this policy, including service delivery, customer support, security, fraud prevention, and legal or accounting obligations.",
91936
- "Retention periods vary by data category. Some records are deleted after short security or operational windows, some are anonymised or minimised once full identity is no longer needed, and some are kept longer where accounting, dispute, or legal requirements apply.",
91937
- "If you want more detail about how long a specific category of data is kept for this site, contact the privacy contact listed in this policy."
91938
- ]));
91939
- sections.push(section("rights", "Your rights", [
91940
- "You can request access, correction, or deletion of your personal data, subject to legal and operational limits.",
91941
- `To make a privacy request, contact ${privacyEmail}.`,
91942
- buildComplaintAuthorityText(identity.jurisdiction)
91943
- ]));
91260
+ return section("cookies", "Cookies", paragraphs);
91261
+ }
91262
+ function formsSection() {
91263
+ return section("forms", "Forms and enquiries", [
91264
+ "When you send us a form, we use what you write so we can reply and deal with your enquiry or request.",
91265
+ "We rely on our legitimate interest in answering the people who contact us. If you're asking about something you'd like to book or buy, we're also taking steps you've asked for before entering into a contract with you.",
91266
+ "We keep enquiries for as long as we need them to deal with your request and keep a record of it. If you'd like us to delete an enquiry, contact us."
91267
+ ]);
91268
+ }
91269
+ function newsletterSection(context) {
91270
+ const { usesMailchimp, usesBrevo } = context.capabilities;
91271
+ const providers = [usesMailchimp ? "Mailchimp" : null, usesBrevo ? "Brevo" : null].filter((name) => name !== null);
91272
+ const paragraphs = ["If you sign up to our mailing list, we use your name and email address to send you the emails you asked for. We rely on your consent, and you can withdraw it at any time with the unsubscribe link in any of our emails.", "Our emails may let us see when they're opened and which links are clicked, using a small image and tracked links. We use this to understand which emails people find useful. Turning off images in your email app stops the open check."];
91273
+ if (providers.length > 0) {
91274
+ paragraphs.push(`Our mailing list is also kept with ${providers.join(" and ")}, which we use to send our mailing list emails.`);
91275
+ }
91276
+ paragraphs.push("If you unsubscribe, we keep a note of your email address so we don't email you again by mistake.");
91277
+ return section("newsletter", "Our mailing list", paragraphs);
91278
+ }
91279
+ function bookingsSection(context) {
91280
+ const paragraphs = ["When you book with us, we use your name, contact details and booking details to manage your booking, send confirmations and reminders, and help if something changes. We also keep any answers you give to our booking questions. We need this to provide what you've booked, so we rely on our contract with you."];
91281
+ if (context.capabilities.usesGoogleCalendar) {
91282
+ paragraphs.push("We use Google Calendar to manage our diary, so your booking, including your name, is added to our calendar.");
91283
+ }
91284
+ paragraphs.push(context.jurisdiction === "UK" ? "We keep records of bookings and payments for as long as we need them for our accounts and any tax or legal requirements, which in the UK can be up to six years." : "We keep records of bookings and payments for as long as we need them for our accounts and any tax or legal requirements.", "If you ask us to delete your details, we can remove your name, contact details, booking answers and any files you've uploaded from our records. We keep the bookings and payments themselves without your details, so our accounts stay correct.");
91285
+ return section("bookings", "Bookings", paragraphs);
91286
+ }
91287
+ function sensitiveRecordsSection(context) {
91288
+ return section("sensitive-client-records", "Client records and sensitive information", [
91289
+ "We may keep client records, such as notes about our sessions, your appointment history, and anything you tell us before or during a session. Some of this may be sensitive, for example information about your health.",
91290
+ "We only collect sensitive information where you've given your explicit consent, or where data protection law otherwise allows it, for example where we need it to provide care or treatment safely.",
91291
+ "Access to these records is restricted. We decide how long they're kept, and some professions have a duty to keep client records for a period after you stop seeing us.",
91292
+ "You can ask to see, correct or delete these records. If we have a professional or legal duty to keep them, we'll tell you."
91293
+ ]);
91294
+ }
91295
+ function portalSection() {
91296
+ return section("portal", "Your account on this site", ["If you sign in to manage your bookings or account, we use secure sign-in links and session cookies to keep your account safe. These are necessary for the site to work. We need them to provide your account, so we rely on our contract with you.", "Sign-in links and session details are only kept for as long as they're needed and are then deleted."]);
91297
+ }
91298
+ function paymentsSection() {
91299
+ return section("payments", "Payments", [
91300
+ "Payments on this site are handled by Stripe. Your card details go straight to Stripe, and we never see or store your full card number.",
91301
+ "Stripe uses your payment details to take the payment and prevent fraud. Stripe handles that under its own privacy policy. We keep a record of what you paid, for our accounts.",
91302
+ "We rely on our contract with you to take payment, and on our legal duty to keep accounts."
91303
+ ]);
91304
+ }
91305
+ function sharingSection(context) {
91306
+ const { usesPayments, usesGoogleCalendar, usesMailchimp, usesBrevo } = context.capabilities;
91307
+ const optional = [
91308
+ usesPayments ? "Stripe, for payments" : null,
91309
+ usesGoogleCalendar ? "Google, for our calendar" : null,
91310
+ usesMailchimp ? "Mailchimp, for our mailing list" : null,
91311
+ usesBrevo ? "Brevo, for our mailing list" : null
91312
+ ].filter((name) => name !== null);
91313
+ const paragraphs = ["This site runs on a platform provided by Riverbank Digital Limited, which stores and processes your information on our behalf under a data processing agreement. Riverbank uses its own providers to host the site (Vercel and Supabase), send emails (Resend and Mailgun), and monitor that the service is working (Axiom)."];
91314
+ if (optional.length > 0) {
91315
+ paragraphs.push(`We also use ${joinList(optional)}.`);
91316
+ }
91317
+ paragraphs.push("Riverbank and these providers only use your information to provide their services to us. We may also share information where the law requires it.", "We don't sell your personal information.");
91318
+ return section("sharing", "Who we share it with", paragraphs);
91319
+ }
91320
+ function joinList(items) {
91321
+ const last = items.at(-1);
91322
+ if (last === undefined || items.length === 1) return items.join("");
91323
+ return `${items.slice(0, -1).join("; ")}; and ${last}`;
91324
+ }
91325
+ function transfersSection(context) {
91326
+ const home = context.jurisdiction === "EU" ? "the European Economic Area" : context.jurisdiction === "UK" ? "the UK" : "your country";
91327
+ const safeguards = context.jurisdiction === "UK" ? "such as the UK International Data Transfer Addendum or a UK adequacy decision" : context.jurisdiction === "EU" ? "such as the EU Standard Contractual Clauses or an EU adequacy decision" : "such as contractual commitments from the provider";
91328
+ return section("transfers", "Information stored outside your country", [`Some of the providers above store or process information outside ${home}, mostly in the United States. Where they do, they use recognised safeguards to protect it, ${safeguards}.`]);
91329
+ }
91330
+ function retentionSection() {
91331
+ return section("retention", "How long we keep it", [
91332
+ "We keep personal information only for as long as we need it for the reasons in this policy. How long that is depends on what it is, and some of the sections above say more.",
91333
+ "Security records, such as sign-in links, are deleted after a short time. Records we need for our accounts are kept for as long as tax and accounting rules require.",
91334
+ "If you'd like to know how long we keep something in particular, contact us."
91335
+ ]);
91336
+ }
91337
+ function rightsSection(context) {
91338
+ const intro = hasGdprRights(context.jurisdiction) ? "You have the right to:" : "Depending on where you live, you may have the right to:";
91339
+ return section("rights", "Your rights", [
91340
+ `${intro} ask for a copy of the information we hold about you; ask us to correct it; ask us to delete it; ask us to limit how we use it, or object to how we use it; ask for your information in a format you can take elsewhere; and withdraw your consent wherever we rely on it.`,
91341
+ "Some of these rights have limits. For example, we may need to keep payment records for our accounts. If we can't do what you've asked, we'll explain why.",
91342
+ hasGdprRights(context.jurisdiction) ? `To make a request, contact ${context.privacyEmail}. We'll reply within one month.` : `To make a request, contact ${context.privacyEmail}.`,
91343
+ buildComplaintAuthorityText(context.jurisdiction)
91344
+ ]);
91345
+ }
91346
+ function changesSection() {
91347
+ return section("changes", "Changes to this policy", ["We may update this policy from time to time, for example when we start offering something new. The date at the top shows when it last changed."]);
91348
+ }
91349
+ function featureSections(context) {
91350
+ const { capabilities } = context;
91351
+ return [
91352
+ capabilities.usesForms ? formsSection() : null,
91353
+ capabilities.usesNewsletter ? newsletterSection(context) : null,
91354
+ capabilities.usesBookings ? bookingsSection(context) : null,
91355
+ capabilities.usesSensitiveClientRecords ? sensitiveRecordsSection(context) : null,
91356
+ capabilities.usesCustomerPortal ? portalSection() : null,
91357
+ capabilities.usesPayments ? paymentsSection() : null
91358
+ ].filter((entry) => entry !== null);
91359
+ }
91360
+ /**
91361
+ * The privacy policy a site serves, written in the site owner's voice. It
91362
+ * describes what the platform does with the features the site uses; the
91363
+ * owner remains responsible for checking it covers anything the platform
91364
+ * can't see.
91365
+ */
91366
+ function buildManagedPrivacyPolicyDocument(input) {
91367
+ const context = {
91368
+ controller: normalizeEntityName(input.identity),
91369
+ privacyEmail: normalizePrivacyEmail(input.identity),
91370
+ jurisdiction: input.identity.jurisdiction,
91371
+ capabilities: input.capabilities
91372
+ };
91944
91373
  return {
91945
91374
  title: "Privacy policy",
91946
- effectiveDate,
91947
- sections
91375
+ effectiveDate: input.effectiveDate,
91376
+ sections: [
91377
+ controllerSection(context),
91378
+ dataCollectedSection(),
91379
+ cookiesSection(context),
91380
+ ...featureSections(context),
91381
+ sharingSection(context),
91382
+ transfersSection(context),
91383
+ retentionSection(),
91384
+ rightsSection(context),
91385
+ changesSection()
91386
+ ]
91948
91387
  };
91949
91388
  }
91950
91389