riverbankcms 0.19.0 → 0.20.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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as parseUtcIsoDateTimeString, $t as unsafeAsBookingSessionId, A as parseFooterCompositionV1, Cn as unsafeAsStoragePath, Dn as ok, Dt as fromArray, En as err, Gt as parseMediaAssetId, I as isCanonicalRichTextDoc, L as parsePublicContactDetails, Ot as mapNonEmptyArray, Qt as unsafeAsBookingRequirementId, Rt as assertNever, Sn as unsafeAsStorageBucketName, Tn as unsafeAsUserId, Yt as parseSiteId, _n as unsafeAsSafeDownloadFilename, bn as unsafeAsSiteScopedStoragePath, c as resolveSiteVerticalBookingConfig, cn as unsafeAsFileChecksumSha256, d as ORDINARY_EVENT_PRESET_VALUES, dn as unsafeAsOriginalFilename, gn as unsafeAsProductId, in as unsafeAsEntitlementId, it as validateBookingPaymentOptionKinds, j as footerNavigationLayouts, ln as unsafeAsMediaAssetId, mn as unsafeAsPositiveSeconds, pn as unsafeAsPositiveByteCount, r as NAVIGATION_LOCATIONS, rn as unsafeAsDetectedMimeType, rt as parseBookingTimeZone, tn as unsafeAsDeclaredMimeType, u as EVENT_PRESET_VALUES, vn as unsafeAsSiteFontId, wn as unsafeAsUploadSessionId, x as stableJsonStringify, yn as unsafeAsSiteId, z as isPresent } from "./src-yL0DBH7S.mjs";
2
+ import { An as unsafeAsStoragePath, B as parsePublicContactDetails, C as stableJsonStringify, Cn as unsafeAsProductId, Dn as unsafeAsSiteScopedStoragePath, En as unsafeAsSiteId, H as isPresent, Mn as unsafeAsUserId, Mt as fromArray, N as parseFooterCompositionV1, Nn as err, Nt as mapNonEmptyArray, P as footerNavigationLayouts, Pn as ok, Pt as mapReadonlyNonEmptyArray, Tn as unsafeAsSiteFontId, Wt as assertNever, Zt as parseMediaAssetId, an as unsafeAsBookingRequirementId, bn as unsafeAsPositiveByteCount, c as resolveSiteVerticalBookingConfig, cn as unsafeAsDeclaredMimeType, ct as validateBookingPaymentOptionKinds, d as ORDINARY_EVENT_PRESET_VALUES, dn as unsafeAsEntitlementId, gn as unsafeAsMediaAssetId, hn as unsafeAsFileChecksumSha256, jn as unsafeAsUploadSessionId, kn as unsafeAsStorageBucketName, nt as localScheduleTimePattern, on as unsafeAsBookingSessionId, r as NAVIGATION_LOCATIONS, rt as parseUtcIsoDateTimeString, st as parseBookingTimeZone, tn as parseSiteId, u as EVENT_PRESET_VALUES, un as unsafeAsDetectedMimeType, vn as unsafeAsOriginalFilename, wn as unsafeAsSafeDownloadFilename, xn as unsafeAsPositiveSeconds, z as isCanonicalRichTextDoc } from "./src-Dixcv4PB.mjs";
3
3
  import "node:module";
4
4
  import { z } from "zod";
5
5
 
@@ -4038,6 +4038,7 @@ function canonicalHoverToken(reference, palette) {
4038
4038
  }
4039
4039
  return canonicalToken(reference, palette);
4040
4040
  }
4041
+ /** Keep a candidate only when it meets ordinary-text contrast on the ground. */
4041
4042
  function safeTokenOr(candidate$1, fallback, backgroundHex, palette) {
4042
4043
  return firstSafeToken([candidate$1], backgroundHex, palette) ?? fallback;
4043
4044
  }
@@ -4459,7 +4460,11 @@ function resolveSurface(spec, context) {
4459
4460
  token: spec.token
4460
4461
  }, {
4461
4462
  ...context,
4462
- buttonContext: spec.buttonContext ?? context.buttonContext
4463
+ buttonContext: spec.buttonContext ?? context.buttonContext,
4464
+ ...spec.foreground ? { foreground: {
4465
+ ...context.foreground,
4466
+ ...spec.foreground
4467
+ } } : {}
4463
4468
  });
4464
4469
  case "tint": return resolveTintSurface(spec, context);
4465
4470
  }
@@ -4928,8 +4933,12 @@ const cardListKnobs = [
4928
4933
  const lookKnobBlocks = {
4929
4934
  actionStyle: cardListKnobBlocks,
4930
4935
  titleSize: "block.hero",
4931
- imagePosition: ["block.hero", "block.media-text"],
4932
- imageShape: "block.media-text",
4936
+ imagePosition: [
4937
+ "block.hero",
4938
+ "block.intro",
4939
+ "block.media-text"
4940
+ ],
4941
+ imageShape: ["block.intro", "block.media-text"],
4933
4942
  aspectRatio: "block.media-text",
4934
4943
  stickerLine: "block.hero",
4935
4944
  smallCaption: "block.hero",
@@ -4992,6 +5001,7 @@ function cardListKnobDefaults(block, arrangement, knobs, defaults) {
4992
5001
  }
4993
5002
  const arrangementLookTargets = {
4994
5003
  hero: "hero",
5004
+ intro: "intro",
4995
5005
  "feature-cards": "feature-cards",
4996
5006
  "event-calendar": "events",
4997
5007
  testimonials: "testimonials",
@@ -5953,14 +5963,20 @@ const blockAppearancePresets = [
5953
5963
  }
5954
5964
  }),
5955
5965
  defineArrangementLook("body-text-reading-column", "body-text", "Reading column", "prose", []),
5956
- defineArrangementLook("form-beside-introduction-arched-panel", "form", "Introduction beside an arched form", "two-column", [], { slotRoles: { panel: "card" } }),
5957
- defineArrangementLook("newsletter-signup-in-arch", "newsletter-signup", "Signup in an arch", "arch", []),
5966
+ defineArrangementLook("form-beside-introduction-arched-panel", "form", "Introduction beside an arched form", "two-column", [], {
5967
+ slotRoles: { panel: "card" },
5968
+ styleFallback: [id$1("form-below-introduction")]
5969
+ }),
5970
+ defineArrangementLook("newsletter-signup-in-arch", "newsletter-signup", "Signup in an arch", "arch", [], { styleFallback: [id$1("newsletter-signup-text-beside")] }),
5958
5971
  defineArrangementLook("image-gallery-square-tiles", "image-gallery", "Square tiles", "tiles", []),
5959
5972
  defineArrangementLook("image-gallery-arched-captions", "image-gallery", "Arched photos with captions", "tiles", [], { styleFallback: [id$1("image-gallery-square-tiles")] }),
5960
- defineArrangementLook("media-text-circle-beside-text", "media-text", "Circle beside the text", "circle-beside", ["imagePosition"], { forcedKnobs: {
5961
- imageShape: "circle",
5962
- aspectRatio: "1/1"
5963
- } }),
5973
+ defineArrangementLook("media-text-circle-beside-text", "media-text", "Circle beside the text", "circle-beside", ["imagePosition"], {
5974
+ forcedKnobs: {
5975
+ imageShape: "circle",
5976
+ aspectRatio: "1/1"
5977
+ },
5978
+ styleFallback: [id$1("media-text-photo-beside-text")]
5979
+ }),
5964
5980
  defineArrangementLook("hero-photo-beside-text", "hero", "Photo beside the text", "split", heroKnobs, { slotRoles: { sticker: "highlight" } }),
5965
5981
  defineArrangementLook("hero-headline-row", "hero", "Big headline, then text and photo", "headline-row", heroKnobs, {
5966
5982
  slotRoles: { sticker: "highlight" },
@@ -5981,6 +5997,15 @@ const blockAppearancePresets = [
5981
5997
  "text",
5982
5998
  "action"
5983
5999
  ], ["showPhotos", "cardMarker"], { itemRoles: { items: "card" } }),
6000
+ defineCardListLook("card-list-accent-edged-cards", "Accent-edged cards", "cards", [
6001
+ "media",
6002
+ "title",
6003
+ "text",
6004
+ "action"
6005
+ ], ["showPhotos", "cardMarker"], {
6006
+ itemRoles: { items: "card" },
6007
+ styleFallback: [id$1("feature-cards-cards")]
6008
+ }),
5984
6009
  defineCardListLook("feature-cards-open-columns", "Open columns", "cards", [
5985
6010
  "media",
5986
6011
  "title",
@@ -6091,6 +6116,52 @@ const blockAppearancePresets = [
6091
6116
  defineArrangementLook("event-details-panel", "event-details", "Details panel", "details-panel", [], {
6092
6117
  slotRoles: { panel: "card" },
6093
6118
  styleFallback: [id$1("event-details-header")]
6119
+ }),
6120
+ defineArrangementLook("intro-statement-beside-text", "intro", "Big statement beside the text", "statement-copy", [], { styleFallback: [id$1("intro-photo-beside-text")] }),
6121
+ defineArrangementLook("intro-photo-beside-text", "intro", "Small photo beside the text", "media-copy", ["imagePosition", "imageShape"], { whenMissing: [{
6122
+ fact: "photo",
6123
+ look: id$1("intro-statement-beside-text")
6124
+ }] }),
6125
+ defineArrangementLook("media-text-photo-beside-text", "media-text", "Photo beside the text", "default", ["imagePosition", "imageShape"]),
6126
+ defineArrangementLook("newsletter-signup-one-row", "newsletter-signup", "Signup in one row", "inline", [], {
6127
+ slotRoles: { panel: "card" },
6128
+ styleFallback: [id$1("newsletter-signup-text-beside")]
6129
+ }),
6130
+ defineArrangementLook("newsletter-signup-text-beside", "newsletter-signup", "Text beside the signup", "editorial-split", [], {
6131
+ slotRoles: { panel: "card" },
6132
+ styleFallback: [id$1("newsletter-signup-in-arch")]
6133
+ }),
6134
+ defineArrangementLook("form-below-introduction", "form", "Form below its introduction", "default", [], {
6135
+ slotRoles: { panel: "card" },
6136
+ styleFallback: [id$1("form-beside-introduction-arched-panel")]
6137
+ }),
6138
+ defineCardListLook("card-list-wide-slides", "Wide cards, one at a time", "slides", [
6139
+ "media",
6140
+ "title",
6141
+ "text",
6142
+ "action"
6143
+ ], ["showPhotos"], {
6144
+ itemRoles: { items: "card" },
6145
+ slotRoles: { date: "highlight" },
6146
+ knobDefaults: {
6147
+ actionStyle: "button",
6148
+ dates: "summary"
6149
+ },
6150
+ styleFallback: [id$1("card-list-plates"), id$1("feature-cards-cards")]
6151
+ }),
6152
+ defineCardListLook("card-list-scroll-row", "A row of cards you can scroll", "track", [
6153
+ "media",
6154
+ "title",
6155
+ "text",
6156
+ "action"
6157
+ ], ["showPhotos"], {
6158
+ itemRoles: { items: "card" },
6159
+ slotRoles: { date: "highlight" },
6160
+ knobDefaults: {
6161
+ actionStyle: "button",
6162
+ dates: "summary"
6163
+ },
6164
+ styleFallback: [id$1("feature-cards-cards")]
6094
6165
  })
6095
6166
  ];
6096
6167
 
@@ -8692,11 +8763,11 @@ function isVideoPosterFileAssetSource(source) {
8692
8763
 
8693
8764
  //#endregion
8694
8765
  //#region ../media-core/src/typeGuards.ts
8695
- function isRecord$3(value) {
8766
+ function isRecord$4(value) {
8696
8767
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8697
8768
  }
8698
- function asRecord(value) {
8699
- return isRecord$3(value) ? value : undefined;
8769
+ function asRecord$1(value) {
8770
+ return isRecord$4(value) ? value : undefined;
8700
8771
  }
8701
8772
 
8702
8773
  //#endregion
@@ -9627,7 +9698,7 @@ function parseTaggedObject(value, allowed) {
9627
9698
  return parseOneOf(record.value.tag, allowed);
9628
9699
  }
9629
9700
  function requireRecord(value) {
9630
- if (!isRecord$3(value)) return err("expected-object");
9701
+ if (!isRecord$4(value)) return err("expected-object");
9631
9702
  return ok(value);
9632
9703
  }
9633
9704
  function requireString(value) {
@@ -9838,7 +9909,7 @@ function parseOptionalRowExif(value) {
9838
9909
  if (value === null || value === undefined) {
9839
9910
  return ok(undefined);
9840
9911
  }
9841
- if (!isRecord$3(value) || !isFileJsonObject(value)) {
9912
+ if (!isRecord$4(value) || !isFileJsonObject(value)) {
9842
9913
  return err({
9843
9914
  tag: "invalid-exif",
9844
9915
  error: "expected-object"
@@ -9912,7 +9983,7 @@ function isFileJsonValue(value) {
9912
9983
  if (Array.isArray(value)) {
9913
9984
  return value.every(isFileJsonValue);
9914
9985
  }
9915
- return isRecord$3(value) && isFileJsonObject(value);
9986
+ return isRecord$4(value) && isFileJsonObject(value);
9916
9987
  }
9917
9988
  function emptyStatePatch(patch) {
9918
9989
  return {
@@ -12491,7 +12562,7 @@ function isCmsMediaObjectType(value) {
12491
12562
  return mediaTypeDiscriminants.some((type) => type === value);
12492
12563
  }
12493
12564
  function isCmsMediaObject(value) {
12494
- if (!isRecord$3(value)) return false;
12565
+ if (!isRecord$4(value)) return false;
12495
12566
  if (value.tag === "media") return true;
12496
12567
  const type = value.type;
12497
12568
  if (!isCmsMediaObjectType(type)) return false;
@@ -12510,7 +12581,7 @@ function collectMediaObjects(value, out, location = "") {
12510
12581
  value.forEach((item, index) => collectMediaObjects(item, out, `${location}[${index}]`));
12511
12582
  return;
12512
12583
  }
12513
- if (!isRecord$3(value)) return;
12584
+ if (!isRecord$4(value)) return;
12514
12585
  if (isCmsMediaObject(value)) {
12515
12586
  out.push({
12516
12587
  obj: value,
@@ -12570,7 +12641,7 @@ function scanForMediaAssetUsage(value, tokens) {
12570
12641
  for (const item of node) visit(item);
12571
12642
  return;
12572
12643
  }
12573
- if (!isRecord$3(node)) return;
12644
+ if (!isRecord$4(node)) return;
12574
12645
  for (const [key, child] of Object.entries(node)) {
12575
12646
  if (typeof child === "string") {
12576
12647
  switch (key) {
@@ -12682,8 +12753,8 @@ function walkAndCollectImageAttrs(value, out, location = "") {
12682
12753
  value.forEach((item, index) => walkAndCollectImageAttrs(item, out, `${location}[${index}]`));
12683
12754
  return;
12684
12755
  }
12685
- if (!isRecord$3(value)) return;
12686
- if (value.type === "image" && isRecord$3(value.attrs)) {
12756
+ if (!isRecord$4(value)) return;
12757
+ if (value.type === "image" && isRecord$4(value.attrs)) {
12687
12758
  out.push({
12688
12759
  attrs: value.attrs,
12689
12760
  location: location ? `${location}.attrs` : "attrs"
@@ -12880,7 +12951,7 @@ function resolvePortableMediaReferences(references, lookup) {
12880
12951
 
12881
12952
  //#endregion
12882
12953
  //#region ../media-core/src/contentMediaAudit.ts
12883
- function isRecord$5(value) {
12954
+ function isRecord$6(value) {
12884
12955
  return typeof value === "object" && value !== null && !Array.isArray(value);
12885
12956
  }
12886
12957
  function formatContentFieldPath(path) {
@@ -12894,11 +12965,11 @@ function summarizeContentMediaValue(value) {
12894
12965
  }
12895
12966
  if (value === null) return "null";
12896
12967
  if (Array.isArray(value)) return `array(${value.length})`;
12897
- if (isRecord$5(value)) return `object(${Object.keys(value).sort().join(",")})`;
12968
+ if (isRecord$6(value)) return `object(${Object.keys(value).sort().join(",")})`;
12898
12969
  return typeof value;
12899
12970
  }
12900
12971
  function isPortableMediaValue(value, allowedMediaTypes$1) {
12901
- if (!isRecord$5(value) || !parseCanonicalMediaIdentifier(value.identifier).ok) return false;
12972
+ if (!isRecord$6(value) || !parseCanonicalMediaIdentifier(value.identifier).ok) return false;
12902
12973
  const type = value.type ?? allowedMediaTypes$1[0];
12903
12974
  const validType = allowedMediaTypes$1.some((allowed) => allowed === type);
12904
12975
  const hasEnvironmentFields = [
@@ -12949,7 +13020,7 @@ function auditMediaValue(context, path, allowedMediaTypes$1, value) {
12949
13020
  }];
12950
13021
  }
12951
13022
  function auditFields(context, fields, value, parentPath) {
12952
- if (!isRecord$5(value)) return [];
13023
+ if (!isRecord$6(value)) return [];
12953
13024
  const findings = [];
12954
13025
  for (const field of fields) {
12955
13026
  if (!(field.id in value)) continue;
@@ -12966,7 +13037,7 @@ function auditFields(context, fields, value, parentPath) {
12966
13037
  if (!Array.isArray(fieldValue)) break;
12967
13038
  for (const [index, item] of fieldValue.entries()) {
12968
13039
  const itemPath = [...fieldPath, index];
12969
- if (!isRecord$5(item)) {
13040
+ if (!isRecord$6(item)) {
12970
13041
  findings.push({
12971
13042
  tag: "missing-schema",
12972
13043
  context,
@@ -13068,7 +13139,7 @@ function resolveLegacyMediaFindings(findings, assets) {
13068
13139
  //#endregion
13069
13140
  //#region ../media-core/src/contentMediaAuditFieldAdapter.ts
13070
13141
  const contentMediaTypes = new Set(mediaTypeDiscriminants);
13071
- function isRecord$4(value) {
13142
+ function isRecord$5(value) {
13072
13143
  return typeof value === "object" && value !== null && !Array.isArray(value);
13073
13144
  }
13074
13145
  function unknownArray(value) {
@@ -13077,7 +13148,7 @@ function unknownArray(value) {
13077
13148
  function fieldsArray(value) {
13078
13149
  const directFields = unknownArray(value);
13079
13150
  if (directFields) return directFields;
13080
- if (!isRecord$4(value)) return null;
13151
+ if (!isRecord$5(value)) return null;
13081
13152
  return unknownArray(value.fields);
13082
13153
  }
13083
13154
  function nestedFields(field) {
@@ -13146,7 +13217,7 @@ function mergeFields(fields) {
13146
13217
  }
13147
13218
  function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds = ["document"]) {
13148
13219
  return mergeFields(rawFields.flatMap((rawField) => {
13149
- if (!isRecord$4(rawField)) return [];
13220
+ if (!isRecord$5(rawField)) return [];
13150
13221
  const { id: id$2, type } = rawField;
13151
13222
  if (typeof id$2 !== "string" || !id$2 || typeof type !== "string") return [];
13152
13223
  if (type === "media" || type === "file") {
@@ -13156,7 +13227,7 @@ function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds
13156
13227
  allowedMediaTypes: allowedMediaTypes(rawField, type, defaultFileKinds)
13157
13228
  }];
13158
13229
  }
13159
- if (flattenRepeaterGroups && type === "group" && isRecord$4(rawField.ui) && rawField.ui.flattenInRepeater === true) {
13230
+ if (flattenRepeaterGroups && type === "group" && isRecord$5(rawField.ui) && rawField.ui.flattenInRepeater === true) {
13160
13231
  return adaptFields(nestedFields(rawField), false, defaultFileKinds);
13161
13232
  }
13162
13233
  if (type === "group" || type === "modal") {
@@ -13171,12 +13242,12 @@ function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds
13171
13242
  return [{
13172
13243
  kind: "object",
13173
13244
  id: id$2,
13174
- fields: adaptFields(tabs.flatMap((tab) => isRecord$4(tab) ? unknownArray(tab.fields) ?? [] : []), false, defaultFileKinds)
13245
+ fields: adaptFields(tabs.flatMap((tab) => isRecord$5(tab) ? unknownArray(tab.fields) ?? [] : []), false, defaultFileKinds)
13175
13246
  }];
13176
13247
  }
13177
13248
  if (type !== "repeater") return [];
13178
- if (isRecord$4(rawField.itemTypes)) {
13179
- const variants = Object.entries(rawField.itemTypes).flatMap(([typeId, itemType]) => isRecord$4(itemType) && Array.isArray(itemType.fields) ? [{
13249
+ if (isRecord$5(rawField.itemTypes)) {
13250
+ const variants = Object.entries(rawField.itemTypes).flatMap(([typeId, itemType]) => isRecord$5(itemType) && Array.isArray(itemType.fields) ? [{
13180
13251
  typeId,
13181
13252
  fields: adaptFields(itemType.fields, true, defaultFileKinds)
13182
13253
  }] : []);
@@ -13675,7 +13746,7 @@ const runtimeBoxBackgroundSchema = z.discriminatedUnion("type", [
13675
13746
  ]).nullable().optional() })
13676
13747
  ]);
13677
13748
  function normalizeAuthoringOpacity(value) {
13678
- if (!isRecord$2(value)) return value;
13749
+ if (!isRecord$3(value)) return value;
13679
13750
  return {
13680
13751
  ...value,
13681
13752
  opacity: normalizeOpacity(value.opacity)
@@ -13684,7 +13755,7 @@ function normalizeAuthoringOpacity(value) {
13684
13755
  function normalizeOpacity(value) {
13685
13756
  return typeof value === "number" && value > 1 && value <= 100 ? value / 100 : value;
13686
13757
  }
13687
- function isRecord$2(value) {
13758
+ function isRecord$3(value) {
13688
13759
  return typeof value === "object" && value !== null && !Array.isArray(value);
13689
13760
  }
13690
13761
 
@@ -13730,6 +13801,10 @@ function resolveSectionBackground(source, lookDefault, rhythm, arrangementHeroPh
13730
13801
  }
13731
13802
  return rhythmBackground(rhythm);
13732
13803
  }
13804
+ function resolveSectionPhotoPlacement(background, heroPhoto) {
13805
+ if (heroPhoto?.paintsGround === false) return "none";
13806
+ return background.kind !== "base" && background.background.type === "image" && background.background.image !== undefined ? "behind-text" : "none";
13807
+ }
13733
13808
  function resolveSectionForeground(background, palette) {
13734
13809
  return resolveSurfaceForeground(background.kind === "base" ? null : background.background, palette, {}, { compositeBase: "background" });
13735
13810
  }
@@ -14553,7 +14628,7 @@ function validateCompositionBudget(input) {
14553
14628
 
14554
14629
  //#endregion
14555
14630
  //#region ../theme-core/src/site-styles/records.ts
14556
- function isRecord$1(value) {
14631
+ function isRecord$2(value) {
14557
14632
  return typeof value === "object" && value !== null && !Array.isArray(value);
14558
14633
  }
14559
14634
 
@@ -14755,15 +14830,15 @@ function parsePersistedFooterPreferences(value) {
14755
14830
  }
14756
14831
  /** One-time backfill rule, also exercised against the migration SQL. Not a runtime fallback. */
14757
14832
  function readFooterPreferences(theme) {
14758
- const footer = isRecord$1(theme) && isRecord$1(theme.footer) ? theme.footer : {};
14759
- const background = isRecord$1(footer.background) ? footer.background : {};
14833
+ const footer = isRecord$2(theme) && isRecord$2(theme.footer) ? theme.footer : {};
14834
+ const background = isRecord$2(footer.background) ? footer.background : {};
14760
14835
  const token = background.type === "color" && background.contentOwner === siteChromeBackgroundOverrideOwner ? parseSiteChromeBackgroundColorToken(background.color) : null;
14761
14836
  return {
14762
14837
  background: token === null ? { kind: "default" } : {
14763
14838
  kind: "color",
14764
14839
  token
14765
14840
  },
14766
- navigationLayout: isRecord$1(footer.navLayout) && footer.navLayout.layout === "stack" ? "stack" : "inline-wrap"
14841
+ navigationLayout: isRecord$2(footer.navLayout) && footer.navLayout.layout === "stack" ? "stack" : "inline-wrap"
14767
14842
  };
14768
14843
  }
14769
14844
  function applyFooterPreferences(theme, footer, preferences) {
@@ -15642,6 +15717,9 @@ const arrangementSlotNames = [
15642
15717
  "social",
15643
15718
  "base",
15644
15719
  "section",
15720
+ "background",
15721
+ "schedule-section",
15722
+ "shop-section",
15645
15723
  "stack",
15646
15724
  "inline",
15647
15725
  "grid",
@@ -19297,7 +19375,11 @@ const BUTTON_STYLE_IDS = [
19297
19375
  "hairline-tab",
19298
19376
  "marquee"
19299
19377
  ];
19300
- const quietArrowParametersSchema = z.object({ secondaryTreatment: z.enum(["outlined", "solid-accent"]) }).strict();
19378
+ const quietArrowParametersSchema = z.object({
19379
+ secondaryTreatment: z.enum(["outlined", "solid-accent"]),
19380
+ size: z.enum(["comfortable"]).optional(),
19381
+ hoverTreatment: z.enum(["deep-and-sheet"]).optional()
19382
+ }).strict();
19301
19383
  const FLAT_INK_HOVER_TREATMENT_VALUES = [
19302
19384
  "contextual",
19303
19385
  "secondary",
@@ -19410,15 +19492,35 @@ function admitKnownStyleParameters(styleId, value) {
19410
19492
  repairs
19411
19493
  };
19412
19494
  }
19495
+ /** Repair optional recipe choices independently so one bad field cannot discard another. */
19496
+ function admitQuietArrowOptionalParameters(parameters) {
19497
+ const size = quietArrowParametersSchema.shape.size.safeParse(parameters?.size);
19498
+ const hover = quietArrowParametersSchema.shape.hoverTreatment.safeParse(parameters?.hoverTreatment);
19499
+ return {
19500
+ values: {
19501
+ ...size.success && size.data ? { size: size.data } : {},
19502
+ ...hover.success && hover.data ? { hoverTreatment: hover.data } : {}
19503
+ },
19504
+ supportedKeys: [
19505
+ "secondaryTreatment",
19506
+ ...size.success ? ["size"] : [],
19507
+ ...hover.success ? ["hoverTreatment"] : []
19508
+ ]
19509
+ };
19510
+ }
19413
19511
  function admitQuietArrowParameters(value) {
19414
19512
  const parameters = objectRecord(value);
19415
19513
  const treatment = parameters?.secondaryTreatment;
19416
19514
  const valid = treatment === "outlined" || treatment === "solid-accent";
19417
- const extraRepairs = Object.keys(parameters ?? {}).filter((key) => key !== "secondaryTreatment").map((key) => repair("quiet-arrow", key, null, "unsupported-option"));
19515
+ const optional = admitQuietArrowOptionalParameters(parameters);
19516
+ const extraRepairs = Object.keys(parameters ?? {}).filter((key) => !optional.supportedKeys.includes(key)).map((key) => repair("quiet-arrow", key, null, "unsupported-option"));
19418
19517
  return {
19419
19518
  selection: buttonStyleSelectionSchema.parse({
19420
19519
  styleId: "quiet-arrow",
19421
- parameters: { secondaryTreatment: valid ? treatment : "outlined" }
19520
+ parameters: {
19521
+ secondaryTreatment: valid ? treatment : "outlined",
19522
+ ...optional.values
19523
+ }
19422
19524
  }),
19423
19525
  repairs: [...extraRepairs, ...valid ? [] : [repair("quiet-arrow", "secondaryTreatment", null, parameters ? "unsupported-option" : "invalid-option-shape")]]
19424
19526
  };
@@ -19920,6 +20022,63 @@ const solidAccentSecondary = {
19920
20022
  },
19921
20023
  affordance: { iconId: "ArrowRight" }
19922
20024
  };
20025
+ /** Selection-owned sizing leaves historical recipe output unchanged when absent. */
20026
+ function sizedQuietArrowRoles(roles, size) {
20027
+ if (size === undefined) return roles;
20028
+ return mapButtonRoleAppearances(roles, (appearance) => ({
20029
+ ...appearance,
20030
+ states: mapButtonVisualAppearances(appearance.states, (state) => ({
20031
+ ...state,
20032
+ sizes: {
20033
+ sm: {
20034
+ padding: "0.875rem 1.125rem",
20035
+ fontSize: "text-xs"
20036
+ },
20037
+ md: {
20038
+ padding: "0.75rem 1.375rem",
20039
+ fontSize: "text-sm"
20040
+ },
20041
+ lg: {
20042
+ padding: "0.875rem 1.75rem",
20043
+ fontSize: "text-sm"
20044
+ },
20045
+ xl: {
20046
+ padding: "1rem 2rem",
20047
+ fontSize: "text-base"
20048
+ }
20049
+ }
20050
+ }))
20051
+ }));
20052
+ }
20053
+ /** Forward sheet interaction is explicit selection data; absent preserves the captured recipe. */
20054
+ function quietArrowHoverRoles(roles, treatment) {
20055
+ if (treatment === undefined) return roles;
20056
+ const hover = (role, colour) => ({
20057
+ ...roles[role],
20058
+ states: {
20059
+ ...roles[role].states,
20060
+ hover: {
20061
+ ...roles[role].states.hover,
20062
+ background: {
20063
+ type: "solid",
20064
+ colorToken: unsafeAsOpaqueButtonColourToken(colour)
20065
+ },
20066
+ text: "treatment",
20067
+ effects: [],
20068
+ ...role === "secondary" ? { border: {
20069
+ widthClass: "border",
20070
+ marksBoundary: true,
20071
+ boundaryInk: unsafeAsOpaqueButtonColourToken("text")
20072
+ } } : {}
20073
+ }
20074
+ }
20075
+ });
20076
+ return {
20077
+ ...roles,
20078
+ primary: hover("primary", "primary-800"),
20079
+ secondary: hover("secondary", "surface")
20080
+ };
20081
+ }
19923
20082
  const quietArrowRecipe = defineButtonStyleRecipe({
19924
20083
  id: "quiet-arrow",
19925
20084
  accessibilityLabel: "Wide uppercase buttons with trailing arrows",
@@ -19943,10 +20102,10 @@ const quietArrowRecipe = defineButtonStyleRecipe({
19943
20102
  label: "Solid accent"
19944
20103
  }]
19945
20104
  }],
19946
- defineSystem: (parameters) => ({ roles: {
20105
+ defineSystem: (parameters) => ({ roles: quietArrowHoverRoles(sizedQuietArrowRoles({
19947
20106
  ...quietArrowSystem.roles,
19948
20107
  secondary: parameters.secondaryTreatment === "solid-accent" ? solidAccentSecondary : quietArrowSystem.roles.secondary
19949
- } })
20108
+ }, parameters.size), parameters.hoverTreatment) })
19950
20109
  });
19951
20110
 
19952
20111
  //#endregion
@@ -27505,10 +27664,11 @@ function recipeFrame(frame) {
27505
27664
  case "container": return { container: {
27506
27665
  rounded: frame.rounded,
27507
27666
  border: true,
27508
- shadow: true,
27667
+ shadow: frame.shadow ?? true,
27509
27668
  padding: frame.padding,
27510
27669
  tint: frame.tint,
27511
- opacity: frame.opacity
27670
+ opacity: frame.opacity,
27671
+ ...frame.mobileSheet ? { mobileSheet: frame.mobileSheet } : {}
27512
27672
  } };
27513
27673
  case "nav-container": return { navContainer: {
27514
27674
  type: frame.type,
@@ -28015,6 +28175,36 @@ const groundedSerifTypography = {
28015
28175
  },
28016
28176
  scale: "balanced"
28017
28177
  };
28178
+ /** Folio keeps the practitioner pairing, with a larger serif and working sans names. */
28179
+ const folioTypography = {
28180
+ ...groundedSerifTypography,
28181
+ body: {
28182
+ ...groundedSerifTypography.body,
28183
+ family: "Open Sans, system-ui, sans-serif",
28184
+ size: "lg"
28185
+ },
28186
+ smallHeading: {
28187
+ family: "Open Sans, system-ui, sans-serif",
28188
+ weight: "semibold"
28189
+ },
28190
+ headings: {
28191
+ ...groundedSerifTypography.headings,
28192
+ h1: {
28193
+ ...groundedSerifTypography.headings.h1,
28194
+ lineHeight: "tight",
28195
+ letterSpacing: "tight"
28196
+ },
28197
+ h2: {
28198
+ ...groundedSerifTypography.headings.h2,
28199
+ lineHeight: "tight",
28200
+ letterSpacing: "tight"
28201
+ },
28202
+ h3: {
28203
+ ...groundedSerifTypography.headings.h3,
28204
+ lineHeight: "tight"
28205
+ }
28206
+ }
28207
+ };
28018
28208
  const kirtanOrnateTypography = {
28019
28209
  body: {
28020
28210
  family: "Jost, system-ui, sans-serif",
@@ -28669,6 +28859,15 @@ const eyebrowTypographyStyleSchema = z.object({
28669
28859
  });
28670
28860
  const typographySchema = z.object({
28671
28861
  body: bodyTypographyStyleSchema,
28862
+ smallHeading: z.object({
28863
+ family: z.string().min(1),
28864
+ weight: z.enum([
28865
+ "regular",
28866
+ "medium",
28867
+ "semibold",
28868
+ "bold"
28869
+ ])
28870
+ }).optional(),
28672
28871
  headings: z.object({
28673
28872
  default: headingTypographyStyleSchema,
28674
28873
  h1: headingTypographyOverridesSchema,
@@ -30536,6 +30735,7 @@ const headerContainerSchema = z.object({
30536
30735
  "lg"
30537
30736
  ]).default("md"),
30538
30737
  tint: z.string().nullable().optional(),
30738
+ mobileSheet: z.literal("inset").optional(),
30539
30739
  opacity: z.number().min(0).max(1).default(.12)
30540
30740
  }).optional();
30541
30741
 
@@ -33032,15 +33232,17 @@ const curatedSiteStyleIdValues = [
33032
33232
  "site-style:grounded-practice",
33033
33233
  "site-style:poster",
33034
33234
  "site-style:lantern",
33035
- "site-style:measure"
33235
+ "site-style:measure",
33236
+ "site-style:folio"
33036
33237
  ];
33037
33238
  /** The same eligibility decision governs offering and the apply write boundary. */
33038
33239
  function siteStyleOfferingDecision(offering, target) {
33039
33240
  if (target.audience === "lab") return { kind: "offered" };
33241
+ const localDemo = target.audience === "local-demo" && target.environment === "local-development";
33040
33242
  switch (offering.kind) {
33041
33243
  case "public": return { kind: "offered" };
33042
- case "lab": return { kind: "lab" };
33043
- case "client": return target.audience === "site-picker" && target.siteSlug != null && offering.siteSlugs.includes(target.siteSlug) ? { kind: "offered" } : { kind: "client-site-mismatch" };
33244
+ case "lab": return localDemo ? { kind: "offered" } : { kind: "lab" };
33245
+ case "client": return localDemo || (target.audience === "site-picker" || target.audience === "local-demo") && target.siteSlug != null && offering.siteSlugs.includes(target.siteSlug) ? { kind: "offered" } : { kind: "client-site-mismatch" };
33044
33246
  }
33045
33247
  }
33046
33248
  function offeredSiteStyles(target, catalogue = curatedSiteStyles) {
@@ -33380,6 +33582,30 @@ const headerCurationBySiteStyle = {
33380
33582
  "fine-line"
33381
33583
  ]
33382
33584
  }]),
33585
+ "site-style:folio": defineHeaderCuration([
33586
+ {
33587
+ layoutId: "floating",
33588
+ styleIds: ["minimal-surface", "glass"]
33589
+ },
33590
+ {
33591
+ layoutId: "classic",
33592
+ styleIds: [
33593
+ "minimal",
33594
+ "serif-line",
33595
+ "fine-line",
33596
+ "soft-bar"
33597
+ ]
33598
+ },
33599
+ {
33600
+ layoutId: "centered",
33601
+ styleIds: [
33602
+ "serif-line",
33603
+ "fine-line",
33604
+ "minimal",
33605
+ "soft-bar"
33606
+ ]
33607
+ }
33608
+ ]),
33383
33609
  "site-style:measure": defineHeaderCuration([{
33384
33610
  layoutId: "classic",
33385
33611
  styleIds: ["fine-line", "minimal"]
@@ -33409,6 +33635,7 @@ function defineCuratedSiteStyle(input) {
33409
33635
  recommendedAppearancePresetIds
33410
33636
  },
33411
33637
  headerCuration: headerCurationBySiteStyle[input.id],
33638
+ headerStyleRecipes: input.headerStyleRecipes,
33412
33639
  footerLookRecipes: input.footerLookRecipes,
33413
33640
  recommendedFooterLookIds: (input.recommendedFooterLookIds ?? []).map(asFooterLookId),
33414
33641
  buttonStyleChoices: input.buttonStyleChoices,
@@ -33478,6 +33705,233 @@ function paletteRecommendation(authoringKey, note) {
33478
33705
  const id = asAppearancePresetId;
33479
33706
  const quieterOrStandard = { titleSize: ["quiet", "auto"] };
33480
33707
  /** Fulcrum: cards are white sheets, the statement is the brand band, stickers are sheets too. */
33708
+ const folioRoles = {
33709
+ card: "sheet",
33710
+ statement: {
33711
+ default: "band",
33712
+ band: "sheet"
33713
+ },
33714
+ highlight: "band"
33715
+ };
33716
+ const folioLinks = { link: "primary" };
33717
+ const folioLooks = {
33718
+ kind: "offers",
33719
+ skin: "folio",
33720
+ skinTokens: {
33721
+ radius: "10px",
33722
+ rule: "1px",
33723
+ "mark-length": "44px",
33724
+ "mark-weight": "2px",
33725
+ gutter: "32px",
33726
+ "copy-gap": "1.25rem",
33727
+ "stack-gap": "2.5rem"
33728
+ },
33729
+ fontLoading: {
33730
+ extraHeadingFontWeights: [600],
33731
+ italicHeadingFontWeights: [500]
33732
+ },
33733
+ surfaces: {
33734
+ base: {
33735
+ kind: "token",
33736
+ token: "background",
33737
+ foreground: folioLinks
33738
+ },
33739
+ sheet: {
33740
+ kind: "token",
33741
+ token: "surface",
33742
+ roleOnly: true,
33743
+ foreground: folioLinks
33744
+ },
33745
+ band: {
33746
+ kind: "token",
33747
+ token: "muted",
33748
+ foreground: folioLinks
33749
+ },
33750
+ brand: {
33751
+ kind: "token",
33752
+ token: "primary",
33753
+ foreground: folioLinks
33754
+ },
33755
+ deep: {
33756
+ kind: "token",
33757
+ token: "primary-800",
33758
+ foreground: folioLinks
33759
+ }
33760
+ },
33761
+ rhythm: {
33762
+ "flow:quiet": [
33763
+ "base",
33764
+ "base",
33765
+ "band",
33766
+ "base",
33767
+ "band",
33768
+ "base"
33769
+ ],
33770
+ "flow:balanced": [
33771
+ "base",
33772
+ "band",
33773
+ "base",
33774
+ "deep",
33775
+ "base",
33776
+ "band"
33777
+ ],
33778
+ "flow:expressive": [
33779
+ "base",
33780
+ "band",
33781
+ "deep",
33782
+ "base",
33783
+ "band",
33784
+ "brand"
33785
+ ]
33786
+ },
33787
+ offers: [
33788
+ {
33789
+ id: id("hero-photo-beside-text"),
33790
+ treatment: "hero-framed",
33791
+ knobValues: quieterOrStandard,
33792
+ roleMap: folioRoles
33793
+ },
33794
+ {
33795
+ id: id("hero-photo-behind-text"),
33796
+ treatment: "hero-panel",
33797
+ knobValues: quieterOrStandard,
33798
+ roleMap: folioRoles
33799
+ },
33800
+ {
33801
+ id: id("hero-page-head"),
33802
+ treatment: "hero-ruled",
33803
+ knobValues: quieterOrStandard,
33804
+ roleMap: folioRoles
33805
+ },
33806
+ {
33807
+ id: id("intro-photo-beside-text"),
33808
+ treatment: "intro-keyline",
33809
+ roleMap: folioRoles
33810
+ },
33811
+ {
33812
+ id: id("intro-statement-beside-text"),
33813
+ treatment: "intro-ruled-split",
33814
+ roleMap: folioRoles
33815
+ },
33816
+ {
33817
+ id: id("media-text-photo-beside-text"),
33818
+ treatment: "media-mount",
33819
+ roleMap: folioRoles
33820
+ },
33821
+ {
33822
+ id: id("media-text-circle-beside-text"),
33823
+ treatment: "media-circle",
33824
+ roleMap: folioRoles
33825
+ },
33826
+ {
33827
+ id: id("feature-cards-cards"),
33828
+ treatment: "cards-sheet",
33829
+ roleMap: folioRoles
33830
+ },
33831
+ {
33832
+ id: id("feature-cards-open-columns"),
33833
+ treatment: "cards-open",
33834
+ roleMap: folioRoles
33835
+ },
33836
+ {
33837
+ id: id("feature-cards-index"),
33838
+ treatment: "cards-register",
33839
+ roleMap: folioRoles
33840
+ },
33841
+ {
33842
+ id: id("body-text-reading-column"),
33843
+ treatment: "body-reading",
33844
+ roleMap: folioRoles
33845
+ },
33846
+ {
33847
+ id: id("event-calendar-ruled-rows"),
33848
+ treatment: "calendar-card",
33849
+ itemRoles: { container: "card" },
33850
+ surfaceTints: { date: {
33851
+ source: "text",
33852
+ opacity: 8
33853
+ } },
33854
+ roleMap: folioRoles
33855
+ },
33856
+ {
33857
+ id: id("event-calendar-session-cards"),
33858
+ treatment: "calendar-sheets",
33859
+ roleMap: folioRoles
33860
+ },
33861
+ {
33862
+ id: id("event-calendar-next-three"),
33863
+ treatment: "calendar-compact",
33864
+ roleMap: folioRoles
33865
+ },
33866
+ {
33867
+ id: id("event-calendar-month-grid"),
33868
+ treatment: "calendar-month",
33869
+ roleMap: folioRoles
33870
+ },
33871
+ {
33872
+ id: id("event-details-panel"),
33873
+ treatment: "details-sheet",
33874
+ roleMap: {
33875
+ ...folioRoles,
33876
+ card: "section"
33877
+ }
33878
+ },
33879
+ {
33880
+ id: id("event-details-header"),
33881
+ treatment: "details-ruled",
33882
+ roleMap: folioRoles
33883
+ },
33884
+ {
33885
+ id: id("newsletter-signup-text-beside"),
33886
+ treatment: "signup-sheet",
33887
+ roleMap: {
33888
+ ...folioRoles,
33889
+ card: "section"
33890
+ }
33891
+ },
33892
+ {
33893
+ id: id("newsletter-signup-one-row"),
33894
+ treatment: "signup-row",
33895
+ roleMap: {
33896
+ ...folioRoles,
33897
+ card: "section"
33898
+ }
33899
+ },
33900
+ {
33901
+ id: id("form-below-introduction"),
33902
+ treatment: "form-sheet-below",
33903
+ roleMap: {
33904
+ ...folioRoles,
33905
+ card: "section"
33906
+ }
33907
+ },
33908
+ {
33909
+ id: id("testimonials-lead-quote"),
33910
+ treatment: "quote-lead",
33911
+ roleMap: folioRoles
33912
+ },
33913
+ {
33914
+ id: id("testimonials-quote-cards"),
33915
+ treatment: "quote-sheets",
33916
+ roleMap: folioRoles
33917
+ },
33918
+ {
33919
+ id: id("card-list-accent-edged-cards"),
33920
+ treatment: "cards-accent-edged",
33921
+ roleMap: folioRoles
33922
+ },
33923
+ {
33924
+ id: id("card-list-wide-slides"),
33925
+ treatment: "carousel-wide-sheet",
33926
+ roleMap: folioRoles
33927
+ },
33928
+ {
33929
+ id: id("card-list-scroll-row"),
33930
+ treatment: "carousel-row-sheet",
33931
+ roleMap: folioRoles
33932
+ }
33933
+ ]
33934
+ };
33481
33935
  const fulcrumRoles = {
33482
33936
  card: "sheet",
33483
33937
  statement: {
@@ -33614,6 +34068,55 @@ const fulcrumOffers = [
33614
34068
  id: id("testimonials-scroll-row"),
33615
34069
  treatment: "cards",
33616
34070
  roleMap: fulcrumRoles
34071
+ },
34072
+ {
34073
+ id: id("intro-photo-beside-text"),
34074
+ treatment: "intro-plate",
34075
+ roleMap: fulcrumRoles
34076
+ },
34077
+ {
34078
+ id: id("intro-statement-beside-text"),
34079
+ treatment: "lintel",
34080
+ roleMap: fulcrumRoles
34081
+ },
34082
+ {
34083
+ id: id("media-text-photo-beside-text"),
34084
+ treatment: "beside-plate",
34085
+ roleMap: fulcrumRoles
34086
+ },
34087
+ {
34088
+ id: id("newsletter-signup-one-row"),
34089
+ treatment: "signup-strip",
34090
+ roleMap: {
34091
+ ...fulcrumRoles,
34092
+ card: "section"
34093
+ }
34094
+ },
34095
+ {
34096
+ id: id("newsletter-signup-text-beside"),
34097
+ treatment: "signup-beside-panel",
34098
+ roleMap: fulcrumRoles
34099
+ },
34100
+ {
34101
+ id: id("form-below-introduction"),
34102
+ treatment: "form-sheet",
34103
+ roleMap: fulcrumRoles
34104
+ },
34105
+ {
34106
+ id: id("card-list-wide-slides"),
34107
+ treatment: "carousel-wide-plate",
34108
+ roleMap: fulcrumRoles,
34109
+ knobValues: { actionStyle: ["link"] }
34110
+ },
34111
+ {
34112
+ id: id("card-list-scroll-row"),
34113
+ treatment: "carousel-row-cards",
34114
+ roleMap: fulcrumRoles
34115
+ },
34116
+ {
34117
+ id: id("card-list-accent-edged-cards"),
34118
+ treatment: "cards-accent-edged",
34119
+ roleMap: fulcrumRoles
33617
34120
  }
33618
34121
  ];
33619
34122
  const fulcrumLooks = {
@@ -33816,6 +34319,49 @@ const posterOffers = [
33816
34319
  id: id("testimonials-scroll-row"),
33817
34320
  treatment: "punchy",
33818
34321
  roleMap: posterRoles
34322
+ },
34323
+ {
34324
+ id: id("intro-photo-beside-text"),
34325
+ treatment: "ink-edge",
34326
+ roleMap: posterRoles
34327
+ },
34328
+ {
34329
+ id: id("intro-statement-beside-text"),
34330
+ treatment: "headline",
34331
+ roleMap: posterRoles
34332
+ },
34333
+ {
34334
+ id: id("media-text-photo-beside-text"),
34335
+ treatment: "beside-slab",
34336
+ roleMap: posterRoles
34337
+ },
34338
+ {
34339
+ id: id("newsletter-signup-one-row"),
34340
+ treatment: "signup-ticket",
34341
+ roleMap: posterRoles
34342
+ },
34343
+ {
34344
+ id: id("newsletter-signup-text-beside"),
34345
+ treatment: "signup-block",
34346
+ roleMap: {
34347
+ ...posterRoles,
34348
+ card: "pop"
34349
+ }
34350
+ },
34351
+ {
34352
+ id: id("form-below-introduction"),
34353
+ treatment: "form-heavy",
34354
+ roleMap: posterRoles
34355
+ },
34356
+ {
34357
+ id: id("card-list-wide-slides"),
34358
+ treatment: "carousel-wide-stamp",
34359
+ roleMap: posterRoles
34360
+ },
34361
+ {
34362
+ id: id("card-list-scroll-row"),
34363
+ treatment: "carousel-row-punchy",
34364
+ roleMap: posterRoles
33819
34365
  }
33820
34366
  ];
33821
34367
  const posterLooks = {
@@ -34092,6 +34638,48 @@ const lanternOffers = [
34092
34638
  treatment: "quote-arch",
34093
34639
  addKnobs: ["archFrame"],
34094
34640
  roleMap: lanternRoles
34641
+ },
34642
+ {
34643
+ id: id("testimonials-scroll-row"),
34644
+ treatment: "quote-row-cards",
34645
+ addKnobs: ["archFrame"],
34646
+ roleMap: lanternRoles
34647
+ },
34648
+ {
34649
+ id: id("intro-photo-beside-text"),
34650
+ treatment: "ringed",
34651
+ roleMap: lanternRoles
34652
+ },
34653
+ {
34654
+ id: id("intro-statement-beside-text"),
34655
+ treatment: "divided",
34656
+ roleMap: lanternRoles
34657
+ },
34658
+ {
34659
+ id: id("media-text-photo-beside-text"),
34660
+ treatment: "outlined",
34661
+ roleMap: lanternRoles
34662
+ },
34663
+ {
34664
+ id: id("newsletter-signup-text-beside"),
34665
+ treatment: "signup-beside-arch",
34666
+ addKnobs: ["archFrame"],
34667
+ roleMap: lanternRoles
34668
+ },
34669
+ {
34670
+ id: id("form-below-introduction"),
34671
+ treatment: "form-arch-below",
34672
+ roleMap: lanternRoles
34673
+ },
34674
+ {
34675
+ id: id("card-list-wide-slides"),
34676
+ treatment: "carousel-wide-arch",
34677
+ roleMap: lanternRoles
34678
+ },
34679
+ {
34680
+ id: id("card-list-scroll-row"),
34681
+ treatment: "carousel-row-cards",
34682
+ roleMap: lanternRoles
34095
34683
  }
34096
34684
  ];
34097
34685
  const lanternLooks = {
@@ -34286,6 +34874,51 @@ const measureOffers = [
34286
34874
  id: id("testimonials-one-at-a-time"),
34287
34875
  treatment: "counter",
34288
34876
  roleMap: measureRoles
34877
+ },
34878
+ {
34879
+ id: id("testimonials-scroll-row"),
34880
+ treatment: "quote-row-hairline",
34881
+ roleMap: measureRuledPanelRoles
34882
+ },
34883
+ {
34884
+ id: id("intro-photo-beside-text"),
34885
+ treatment: "rail-photo",
34886
+ roleMap: measureRoles
34887
+ },
34888
+ {
34889
+ id: id("intro-statement-beside-text"),
34890
+ treatment: "rule-split",
34891
+ roleMap: measureRoles
34892
+ },
34893
+ {
34894
+ id: id("media-text-photo-beside-text"),
34895
+ treatment: "beside-grid",
34896
+ roleMap: measureRoles
34897
+ },
34898
+ {
34899
+ id: id("newsletter-signup-one-row"),
34900
+ treatment: "signup-rail-row",
34901
+ roleMap: measureRuledPanelRoles
34902
+ },
34903
+ {
34904
+ id: id("newsletter-signup-text-beside"),
34905
+ treatment: "signup-split",
34906
+ roleMap: measureRoles
34907
+ },
34908
+ {
34909
+ id: id("form-below-introduction"),
34910
+ treatment: "form-rail",
34911
+ roleMap: measureRuledPanelRoles
34912
+ },
34913
+ {
34914
+ id: id("card-list-wide-slides"),
34915
+ treatment: "carousel-wide-sheet",
34916
+ roleMap: measureRoles
34917
+ },
34918
+ {
34919
+ id: id("card-list-scroll-row"),
34920
+ treatment: "carousel-row-sheets",
34921
+ roleMap: measureRoles
34289
34922
  }
34290
34923
  ];
34291
34924
  const measureLooks = {
@@ -34813,7 +35446,10 @@ const curatedSiteStyles = [
34813
35446
  headingDecoration: noHeadingDecoration,
34814
35447
  description: "Calm and editorial: Newsreader over IBM Plex Sans, hairlines instead of boxes, flat ink actions, and photos mounted on an offset plate in the brand tone.",
34815
35448
  generationBrief: "Use a calm, well-run voice: precise, reassuring and specific. Lead with what happens at a first visit, keep proof concrete (registrations, same-week appointments, outcomes), use sentence case throughout with no all-caps labels, and let photographs carry the boldness. Avoid spa or hard-sell wellness language and medical claims.",
34816
- offering: { kind: "lab" },
35449
+ offering: {
35450
+ kind: "public",
35451
+ generation: { kind: "explicit_only" }
35452
+ },
34817
35453
  tokenRecipes: {
34818
35454
  palette: "brand-led",
34819
35455
  contrast: "standard",
@@ -34948,7 +35584,10 @@ const curatedSiteStyles = [
34948
35584
  headingDecoration: noHeadingDecoration,
34949
35585
  description: "Loud but controlled: huge condensed type, flat colour blocks, hard offset shadows on anything you can press, and one sticker for the news.",
34950
35586
  generationBrief: "Write like a gig poster: short, direct lines, one fact set big (a time, a price, an age range), sentence case throughout and no all-caps labels. Keep body copy plain and friendly, and save the sticker line for one piece of real news.",
34951
- offering: { kind: "lab" },
35587
+ offering: {
35588
+ kind: "public",
35589
+ generation: { kind: "explicit_only" }
35590
+ },
34952
35591
  tokenRecipes: {
34953
35592
  palette: "brand-led",
34954
35593
  contrast: "strong",
@@ -35000,8 +35639,8 @@ const curatedSiteStyles = [
35000
35639
  description: "Dark, candlelit and symmetrical: Gloock display over Alegreya Sans, every photo seen through an arched window, one small lozenge rule above each heading, and a lit panel for the one thing you would pick.",
35001
35640
  generationBrief: "Warm and ceremonial without being religious. Write plain invitations (\"Come along this Friday\") with real times and prices, sentence case throughout and no all-caps labels. Let the quiet, candlelit mood come from the photographs rather than from spiritual language.",
35002
35641
  offering: {
35003
- kind: "client",
35004
- siteSlugs: ["kirtan-lucy"]
35642
+ kind: "public",
35643
+ generation: { kind: "explicit_only" }
35005
35644
  },
35006
35645
  tokenRecipes: {
35007
35646
  palette: "brand-led",
@@ -35059,7 +35698,10 @@ const curatedSiteStyles = [
35059
35698
  headingDecoration: noHeadingDecoration,
35060
35699
  description: "Swiss and warm: one grotesque through weight and size, a visible twelve-column grid, a heavy rule and a small hanging title at the top of every section, square corners and no shadows. The accent is only ever a small square.",
35061
35700
  generationBrief: "Calm and exact, for one-to-one practices. Number real sequences and nothing else, give times and fees as plain figures, keep sentence case throughout, and include a crisis line where a therapy site needs one. Avoid wellness cliches and promises of outcomes.",
35062
- offering: { kind: "lab" },
35701
+ offering: {
35702
+ kind: "public",
35703
+ generation: { kind: "explicit_only" }
35704
+ },
35063
35705
  tokenRecipes: {
35064
35706
  palette: "brand-led",
35065
35707
  contrast: "strong",
@@ -35088,6 +35730,104 @@ const curatedSiteStyles = [
35088
35730
  paletteRecommendation("measure-plaster"),
35089
35731
  paletteRecommendation("measure-night")
35090
35732
  ]
35733
+ }),
35734
+ defineCuratedSiteStyle({
35735
+ id: "site-style:folio",
35736
+ name: "Folio",
35737
+ offering: {
35738
+ kind: "public",
35739
+ generation: { kind: "explicit_only" }
35740
+ },
35741
+ description: "Calm and editorial: a classic serif over a plain sans, soft corners, thin rules and photos with room around them. For practitioners who teach classes, see clients and run workshops.",
35742
+ generationBrief: "Use restrained serif headings, clear sans-serif body copy, natural photography, practical calls to action and concrete class and workshop details.",
35743
+ headingDecoration: noHeadingDecoration,
35744
+ tokenRecipes: {
35745
+ palette: "warm-neutral",
35746
+ contrast: "standard",
35747
+ radius: "soft",
35748
+ shadow: "none",
35749
+ typography: "classic-serif",
35750
+ spacing: "airy",
35751
+ motion: "subtle"
35752
+ },
35753
+ compositionBudget: accentLedQuietBudget,
35754
+ typography: folioTypography,
35755
+ heroTypography: groundedSerifHeroTypography,
35756
+ looks: folioLooks,
35757
+ contentTemplateDefaults: { event: "event-header" },
35758
+ themeOverrides: {
35759
+ accordions: editorialDividedAccordionSystem,
35760
+ testimonialQuote: serifDisplayQuote
35761
+ },
35762
+ headerStyleRecipes: { "minimal-surface": {
35763
+ tone: "neutral",
35764
+ background: { kind: "transparent" },
35765
+ nav: {
35766
+ kind: "minimal",
35767
+ hoverColorToken: "primary"
35768
+ },
35769
+ navWeight: "medium",
35770
+ navLinkWeight: "medium",
35771
+ navTypography: "plain",
35772
+ border: "none",
35773
+ shadow: { kind: "absent" },
35774
+ frame: {
35775
+ kind: "container",
35776
+ rounded: "lg",
35777
+ padding: "sm",
35778
+ tint: "background",
35779
+ opacity: 1,
35780
+ shadow: false,
35781
+ mobileSheet: "inset"
35782
+ },
35783
+ cta: { treatment: "default" },
35784
+ openingPlacement: "photo-behind-text"
35785
+ } },
35786
+ footerLookRecipes: { "footer-links-row": {
35787
+ arrangement: "simple",
35788
+ ground: {
35789
+ kind: "token",
35790
+ token: "surface"
35791
+ },
35792
+ nav: {
35793
+ weight: "regular",
35794
+ colorToken: "text",
35795
+ hoverColorToken: "primary",
35796
+ typography: "body"
35797
+ },
35798
+ legal: {
35799
+ kind: "separate",
35800
+ bar: {
35801
+ fullBleed: false,
35802
+ background: {
35803
+ kind: "token",
35804
+ token: "surface"
35805
+ },
35806
+ textColor: "text",
35807
+ textAlign: "left",
35808
+ paddingY: "tight",
35809
+ paddingX: "none",
35810
+ borderTop: { width: "none" }
35811
+ }
35812
+ },
35813
+ brandAlign: "start",
35814
+ brandLogoSize: "md"
35815
+ } },
35816
+ recommendedFooterLookIds: ["footer-links-row"],
35817
+ buttonStyleChoices: [
35818
+ buttonChoice("quiet-arrow", undefined, {
35819
+ secondaryTreatment: "solid-accent",
35820
+ size: "comfortable",
35821
+ hoverTreatment: "deep-and-sheet"
35822
+ }),
35823
+ buttonChoice("flat-ink"),
35824
+ buttonChoice("editorial-link")
35825
+ ],
35826
+ paletteRecommendations: [
35827
+ paletteRecommendation("warm-neutral-forest"),
35828
+ paletteRecommendation("soft-natural-sage"),
35829
+ paletteRecommendation("soft-natural-stone")
35830
+ ]
35091
35831
  })
35092
35832
  ];
35093
35833
  const curatedSiteStylesById = new Map(curatedSiteStyles.map((style) => [style.id, style]));
@@ -37928,29 +38668,29 @@ function planGeneratedHeaderOpeningSection(input) {
37928
38668
  }
37929
38669
  function hasPersistedImageBackground(content) {
37930
38670
  const sectionStyles = content._sectionStyles;
37931
- if (!isRecord$1(sectionStyles)) return false;
38671
+ if (!isRecord$2(sectionStyles)) return false;
37932
38672
  const background = sectionStyles.background;
37933
- return isRecord$1(background) && background.type === "image" && background.image !== undefined;
38673
+ return isRecord$2(background) && background.type === "image" && background.image !== undefined;
37934
38674
  }
37935
38675
  function openingBackground(content) {
37936
38676
  const sectionStyles = content ? content._sectionStyles : null;
37937
- if (!isRecord$1(sectionStyles)) return null;
38677
+ if (!isRecord$2(sectionStyles)) return null;
37938
38678
  const background = sectionStyles.background;
37939
- return isRecord$1(background) ? background : null;
38679
+ return isRecord$2(background) ? background : null;
37940
38680
  }
37941
38681
  function contentHasContrastOverlay(content) {
37942
38682
  if (!content) return false;
37943
38683
  const imageSurfaceTreatment = content.imageSurfaceTreatment;
37944
- const imageSurfaceTreatmentKind = isRecord$1(imageSurfaceTreatment) ? imageSurfaceTreatment.kind : null;
38684
+ const imageSurfaceTreatmentKind = isRecord$2(imageSurfaceTreatment) ? imageSurfaceTreatment.kind : null;
37945
38685
  const background = openingBackground(content);
37946
38686
  const backgroundOverlay = background?.overlay;
37947
- const hasBackgroundOverlay = isRecord$1(backgroundOverlay) && backgroundOverlay.type !== undefined && backgroundOverlay.type !== null && backgroundOverlay.type !== "none";
38687
+ const hasBackgroundOverlay = isRecord$2(backgroundOverlay) && backgroundOverlay.type !== undefined && backgroundOverlay.type !== null && backgroundOverlay.type !== "none";
37948
38688
  return content.heroLegibility === "scrim-gradient" || imageSurfaceTreatmentKind === "brand-scrim" || imageSurfaceTreatmentKind === "neutral-scrim" || hasBackgroundOverlay;
37949
38689
  }
37950
38690
  function contentHasOpeningTopSpacing(content) {
37951
38691
  if (!content) return false;
37952
38692
  const sectionStyles = content._sectionStyles;
37953
- const minHeight = isRecord$1(sectionStyles) ? sectionStyles.minHeight : null;
38693
+ const minHeight = isRecord$2(sectionStyles) ? sectionStyles.minHeight : null;
37954
38694
  return content.sectionHeight === "hero" || content.sectionHeight === "immersive" || minHeight === "hero" || minHeight === "immersive";
37955
38695
  }
37956
38696
  function runtimeHasContrastOverlay(block) {
@@ -38855,10 +39595,28 @@ function planUnknownOpeningNavHover(theme, style, restingToken) {
38855
39595
  });
38856
39596
  }
38857
39597
  function applyPageOpeningForegroundToTheme(theme, recipe, context) {
38858
- if (!recipe?.openingForeground) return theme;
39598
+ if (!recipe || !headerReadsOpening(recipe)) return theme;
39599
+ const header = applyOpeningPlacement(theme.header, recipe, context);
38859
39600
  return {
38860
39601
  ...theme,
38861
- header: applyOpeningForeground(theme, theme.header, recipe, context)
39602
+ header: applyOpeningForeground(theme, header, recipe, context)
39603
+ };
39604
+ }
39605
+ /** Whether the page's opening section changes how this header compiles. */
39606
+ function headerReadsOpening(recipe) {
39607
+ return Boolean(recipe.openingForeground || recipe.openingPlacement);
39608
+ }
39609
+ /**
39610
+ * A header that floats only over a photo becomes an in-flow bar elsewhere:
39611
+ * the classic variant without the floating frame, so content starts below it.
39612
+ */
39613
+ function applyOpeningPlacement(header, recipe, context) {
39614
+ if (recipe.openingPlacement !== "photo-behind-text") return header;
39615
+ if (header.variant !== "floating" || context?.openingPhotoBehindText === true) return header;
39616
+ const { container: _floatingFrame, ...inFlow } = header;
39617
+ return {
39618
+ ...inFlow,
39619
+ variant: "classic"
38862
39620
  };
38863
39621
  }
38864
39622
 
@@ -43305,6 +44063,7 @@ const courseBookingSettingsSchema = z.object({
43305
44063
  visibility: z.enum(["public", "unlisted"]),
43306
44064
  acceptsPasses: z.boolean(),
43307
44065
  acceptsMemberships: z.boolean(),
44066
+ siteTermsMode: z.enum(["site", "off"]).optional(),
43308
44067
  ticketingMode: z.enum([
43309
44068
  "inherit",
43310
44069
  "enabled",
@@ -43542,13 +44301,44 @@ const eventBookingOfferIntentSchema = z.discriminatedUnion("bookingRequirement",
43542
44301
  pricing: eventPricingIntentSchema,
43543
44302
  paymentTerms: eventPaymentTermsIntentSchema
43544
44303
  }).strict()]);
44304
+ /** A booked pricing change carries its target beside the offer it selects from. */
44305
+ const eventRunBookingCommandFields = {
44306
+ bookingOffer: eventBookingOfferIntentSchema,
44307
+ attendeeMigration: z.object({ pricingOptionIdentifier: z.string().trim().min(1) }).strict().optional()
44308
+ };
43545
44309
 
43546
44310
  //#endregion
43547
44311
  //#region ../site-commands/src/eventSeriesCommandRules.ts
44312
+ /** The timezone belongs to the command; a schedule authors local wall time. */
44313
+ const eventCommandScheduleSchema = z.object({
44314
+ recurrenceRule: z.string().min(1),
44315
+ startDate: isoLocalDateSchema,
44316
+ defaultStartTime: z.string().regex(localScheduleTimePattern),
44317
+ durationMinutes: positiveIntegerSchema
44318
+ }).strict();
44319
+ function withEventSeriesTiming(fields, occurrence) {
44320
+ return z.union([withEventSeriesBookingScope({
44321
+ ...fields,
44322
+ occurrences: z.tuple([occurrence]).rest(occurrence).readonly(),
44323
+ schedule: z.never().optional()
44324
+ }), withEventSeriesBookingScope({
44325
+ ...fields,
44326
+ schedule: eventCommandScheduleSchema,
44327
+ occurrences: z.never().optional()
44328
+ })]);
44329
+ }
43548
44330
  /**
43549
44331
  * Cross-field rules for `upsertEventSeries`, kept together so the schema in
43550
44332
  * `commands.ts` reads as a shape and these read as the rules about it.
43551
44333
  */
44334
+ /** Ordinary runs inherit the parent's name; whole-series runs may author their own. */
44335
+ function withEventSeriesBookingScope(fields) {
44336
+ const common = z.object(fields);
44337
+ return z.discriminatedUnion("bookingScope", [common.extend({ bookingScope: z.literal("single_occurrence") }).strict(), common.extend({
44338
+ bookingScope: z.literal("whole_series"),
44339
+ runName: z.string().nullable().optional()
44340
+ }).strict()]);
44341
+ }
43552
44342
  function checkOccurrenceInterval(occurrence, ctx) {
43553
44343
  validateChronologicalFields({
43554
44344
  start: occurrence.startAt,
@@ -44321,12 +45111,11 @@ const eventOccurrenceSchema = z.object({
44321
45111
  const upsertEventSeriesCommandSchema = z.object({
44322
45112
  type: z.literal("upsertEventSeries"),
44323
45113
  ref: eventSeriesRefSchema,
44324
- input: z.object({
45114
+ input: withEventSeriesTiming({
44325
45115
  slug: z.string().min(1),
44326
45116
  path: z.string().startsWith("/").nullable().optional(),
44327
45117
  title: z.string().min(1),
44328
45118
  preset: z.enum(EVENT_PRESET_VALUES),
44329
- bookingScope: z.enum(["single_occurrence", "whole_series"]),
44330
45119
  status: z.enum([
44331
45120
  "draft",
44332
45121
  "active",
@@ -44336,19 +45125,18 @@ const upsertEventSeriesCommandSchema = z.object({
44336
45125
  ...offeringCommonInputFields,
44337
45126
  courseBooking: courseBookingSettingsSchema.optional(),
44338
45127
  waitlist: waitlistIntentSchema.optional(),
44339
- bookingOffer: eventBookingOfferIntentSchema,
45128
+ ...eventRunBookingCommandFields,
44340
45129
  deliveryMode: z.enum([
44341
45130
  "in_person",
44342
45131
  "online",
44343
45132
  "hybrid"
44344
45133
  ]).optional(),
44345
- occurrences: z.array(eventOccurrenceSchema).min(1).readonly(),
44346
45134
  content: contentSummarySchema.optional(),
44347
45135
  settings: unknownRecordSchema.optional(),
44348
45136
  tags: z.array(z.string()).readonly().optional(),
44349
45137
  blocks: entrySlotBlocksSchema.optional(),
44350
45138
  staffRefs: z.array(staffRefSchema).readonly().optional()
44351
- }).strict()
45139
+ }, eventOccurrenceSchema)
44352
45140
  }).strict().superRefine(checkDraftListingMatchesStatus);
44353
45141
  const deleteEventSeriesCommandSchema = z.object({
44354
45142
  type: z.literal("deleteEventSeries"),
@@ -44589,6 +45377,17 @@ const unsafeAsSiteCommandResourceId = (value) => value;
44589
45377
 
44590
45378
  //#endregion
44591
45379
  //#region ../site-commands/src/datedOfferingMapping.ts
45380
+ /** One timing authority goes to the SDK: generated dates never override a schedule. */
45381
+ function eventManagementTiming(input) {
45382
+ return input.schedule !== undefined ? { schedule: {
45383
+ ...input.schedule,
45384
+ timezone: input.timezone
45385
+ } } : { occurrences: mapReadonlyNonEmptyArray(input.occurrences, (item) => ({
45386
+ startsAt: item.startAt,
45387
+ endsAt: item.endAt,
45388
+ timeZone: input.timezone
45389
+ })) };
45390
+ }
44592
45391
  function eventManagementContent(content, operations) {
44593
45392
  if (content === undefined) return undefined;
44594
45393
  if (content.body !== undefined && typeof content.body !== "string" && !operations.isCanonicalBody(content.body)) {
@@ -45342,7 +46141,7 @@ function planCommandOrderingDependencies(command, commands, providerByRef, requi
45342
46141
 
45343
46142
  //#endregion
45344
46143
  //#region ../site-commands/src/guards.ts
45345
- function isRecord(value) {
46144
+ function isRecord$1(value) {
45346
46145
  return typeof value === "object" && value !== null;
45347
46146
  }
45348
46147
 
@@ -45689,7 +46488,7 @@ function collectVenueRefsFromIntervals(intervals, path) {
45689
46488
  }
45690
46489
  function collectNavigationRefs(items, path) {
45691
46490
  return items.flatMap((item, index) => {
45692
- if (!isRecord(item)) return [];
46491
+ if (!isRecord$1(item)) return [];
45693
46492
  const itemPath = `${path}[${index}]`;
45694
46493
  const refs = [];
45695
46494
  if (typeof item.pageRef === "string") {
@@ -45712,7 +46511,7 @@ function collectNavigationRefs(items, path) {
45712
46511
  }
45713
46512
  function collectBlockRefs(blocks, path) {
45714
46513
  return blocks.flatMap((block, index) => {
45715
- if (!isRecord(block)) return [];
46514
+ if (!isRecord$1(block)) return [];
45716
46515
  const blockPath = `${path}[${index}]`;
45717
46516
  return [...collectNestedRefs(block.props, `${blockPath}.props`), ...collectNestedRefs(block.children, `${blockPath}.children`)];
45718
46517
  });
@@ -45728,7 +46527,7 @@ function collectNestedRefs(value, path) {
45728
46527
  if (Array.isArray(value)) {
45729
46528
  return value.flatMap((item, index) => collectNestedRefs(item, `${path}[${index}]`));
45730
46529
  }
45731
- if (!isRecord(value)) return [];
46530
+ if (!isRecord$1(value)) return [];
45732
46531
  return Object.entries(value).flatMap(([key, child]) => collectNestedRefs(child, `${path}.${key}`));
45733
46532
  }
45734
46533
 
@@ -45750,7 +46549,7 @@ function nativeWholeSeriesStaticExecutionGaps(command) {
45750
46549
  ].filter(isPresent);
45751
46550
  }
45752
46551
  function wholeSeriesOccurrenceVenueGap(input) {
45753
- return input.occurrences.some((occurrence) => occurrence.venueRef !== undefined) ? staticGap("event_occurrence_venue_mapping_unsupported", "Whole-series occurrence venue overrides need native session-level venue mapping before mutation.") : null;
46552
+ return (input.occurrences ?? []).some((occurrence) => occurrence.venueRef !== undefined) ? staticGap("event_occurrence_venue_mapping_unsupported", "Whole-series occurrence venue overrides need native session-level venue mapping before mutation.") : null;
45754
46553
  }
45755
46554
  function wholeSeriesPricingGap(input) {
45756
46555
  switch (input.bookingOffer.pricing.kind) {
@@ -45803,7 +46602,7 @@ function occurrenceVenueMappingGap(command) {
45803
46602
  }
45804
46603
  function resolveEventSeriesVenueMapping(command) {
45805
46604
  const seriesVenue = command.input.venueRef !== undefined ? refSlug(command.input.venueRef) : command.input.venueIdentifier ?? null;
45806
- const effectiveVenues = command.input.occurrences.map((occurrence) => occurrence.venueRef !== undefined ? refSlug(occurrence.venueRef) : seriesVenue);
46605
+ const effectiveVenues = (command.input.occurrences ?? []).map((occurrence) => occurrence.venueRef !== undefined ? refSlug(occurrence.venueRef) : seriesVenue);
45807
46606
  const uniqueEffectiveVenues = [...new Set(effectiveVenues)];
45808
46607
  return uniqueEffectiveVenues.length <= 1 ? {
45809
46608
  kind: "resolved",
@@ -45963,9 +46762,9 @@ function deriveIdempotencyKey(input) {
45963
46762
  ].join("|"));
45964
46763
  }
45965
46764
  function findUnknownCommandTypes(input) {
45966
- if (!isRecord(input) || !Array.isArray(input.commands)) return [];
46765
+ if (!isRecord$1(input) || !Array.isArray(input.commands)) return [];
45967
46766
  return input.commands.flatMap((command, commandIndex) => {
45968
- if (!isRecord(command) || typeof command.type !== "string") return [];
46767
+ if (!isRecord$1(command) || typeof command.type !== "string") return [];
45969
46768
  if (commandTypeSet.has(command.type)) return [];
45970
46769
  return [{
45971
46770
  code: "unknown_command_type",
@@ -45991,9 +46790,9 @@ function commandIndexForIssuePath(path) {
45991
46790
  return path[0] === "commands" && typeof path[1] === "number" ? path[1] : -1;
45992
46791
  }
45993
46792
  function commandTypeForIssue(input, commandIndex) {
45994
- if (!isRecord(input) || !isUnknownArray(input.commands)) return null;
46793
+ if (!isRecord$1(input) || !isUnknownArray(input.commands)) return null;
45995
46794
  const command = input.commands[commandIndex];
45996
- return isRecord(command) && typeof command.type === "string" ? command.type : null;
46795
+ return isRecord$1(command) && typeof command.type === "string" ? command.type : null;
45997
46796
  }
45998
46797
  function isUnknownArray(value) {
45999
46798
  return Array.isArray(value);
@@ -46445,6 +47244,103 @@ function isEventVisibility(value) {
46445
47244
  }
46446
47245
  const eventVisibilitySchema = z.enum(EVENT_VISIBILITY_VALUES, { error: EVENT_VISIBILITY_ERROR_MESSAGE });
46447
47246
 
47247
+ //#endregion
47248
+ //#region ../blocks/src/system/types/link.ts
47249
+ const nullableString = z.union([z.string().min(1), z.null()]);
47250
+ const RESOLVED_INTERNAL_LINK_ENTITY_TYPES = [
47251
+ "page",
47252
+ "content",
47253
+ "pageSurface"
47254
+ ];
47255
+ function isResolvedInternalLinkEntityType(value) {
47256
+ return typeof value === "string" && RESOLVED_INTERNAL_LINK_ENTITY_TYPES.includes(value);
47257
+ }
47258
+ const resolvedInternalLinkKeys = [
47259
+ "entityId",
47260
+ "entityType",
47261
+ "href",
47262
+ "title",
47263
+ "typeLabel"
47264
+ ];
47265
+ const internalRouteOnlyLinkSchema = z.object({
47266
+ kind: z.literal("internal"),
47267
+ routeId: z.string().min(1)
47268
+ }).passthrough().superRefine((value, ctx) => {
47269
+ const hasResolvedMetadata = resolvedInternalLinkKeys.some((key) => value[key] != null);
47270
+ if (!hasResolvedMetadata) return;
47271
+ ctx.addIssue({
47272
+ code: "custom",
47273
+ message: "Internal links with resolved metadata must include entityId, entityType, href, title, and typeLabel"
47274
+ });
47275
+ });
47276
+ const internalResolvedLinkSchema = z.object({
47277
+ kind: z.literal("internal"),
47278
+ routeId: z.string().min(1),
47279
+ entityId: z.string().min(1),
47280
+ entityType: z.enum(RESOLVED_INTERNAL_LINK_ENTITY_TYPES),
47281
+ href: z.string().min(1),
47282
+ title: z.string().min(1),
47283
+ typeLabel: z.string().min(1),
47284
+ contentTypeKey: nullableString.optional(),
47285
+ contentTypeName: nullableString.optional(),
47286
+ updatedAt: nullableString.optional()
47287
+ }).passthrough();
47288
+ const internalLinkSchema = z.union([internalResolvedLinkSchema, internalRouteOnlyLinkSchema]);
47289
+ const externalLinkSchema = z.object({
47290
+ kind: z.literal("external"),
47291
+ href: z.string().min(1)
47292
+ }).passthrough();
47293
+ const customLinkSchema = z.object({
47294
+ kind: z.literal("url"),
47295
+ href: z.string().min(1)
47296
+ }).passthrough();
47297
+ const pageLinkSchema = z.object({
47298
+ kind: z.literal("page"),
47299
+ identifier: z.string().min(1)
47300
+ }).passthrough();
47301
+ const entryLinkSchema = z.object({
47302
+ kind: z.literal("entry"),
47303
+ contentType: z.string().min(1),
47304
+ identifier: z.string().min(1)
47305
+ }).passthrough();
47306
+ /** Convert legacy URL strings without inventing page or entry references. */
47307
+ function normalizeLinkValue(value) {
47308
+ return typeof value === "string" && value.trim().length > 0 ? {
47309
+ kind: "url",
47310
+ href: value.trim()
47311
+ } : value;
47312
+ }
47313
+ const linkSchema = z.union([
47314
+ internalLinkSchema,
47315
+ externalLinkSchema,
47316
+ customLinkSchema,
47317
+ pageLinkSchema,
47318
+ entryLinkSchema
47319
+ ]);
47320
+ function visitLinkValueReferences(value, visitor) {
47321
+ const parsed = linkSchema.safeParse(value);
47322
+ if (!parsed.success) {
47323
+ return;
47324
+ }
47325
+ const link = parsed.data;
47326
+ switch (link.kind) {
47327
+ case "internal":
47328
+ visitor.routeIds.add(link.routeId);
47329
+ return;
47330
+ case "page":
47331
+ visitor.pageIdentifiers.add(link.identifier);
47332
+ return;
47333
+ case "entry":
47334
+ visitor.entryIdentifiers.add(link.contentType, link.identifier);
47335
+ return;
47336
+ case "external":
47337
+ case "url": return;
47338
+ }
47339
+ }
47340
+ function isInternalResolvedLinkValue(value) {
47341
+ return value?.kind === "internal" && "entityId" in value && "entityType" in value && "href" in value && "title" in value && "typeLabel" in value && typeof value.entityId === "string" && isResolvedInternalLinkEntityType(value.entityType) && typeof value.href === "string" && typeof value.title === "string" && typeof value.typeLabel === "string";
47342
+ }
47343
+
46448
47344
  //#endregion
46449
47345
  //#region ../api/src/sdk-event-booking-offer.ts
46450
47346
  function classifySdkEventBookingOffer(input) {
@@ -46468,7 +47364,6 @@ function classifySdkEventBookingOffer(input) {
46468
47364
 
46469
47365
  //#endregion
46470
47366
  //#region ../api/src/sdk-event-schedule.ts
46471
- const timeRegex = /^([01]\d|2[0-3]):([0-5]\d)(:([0-5]\d))?$/;
46472
47367
  const explicitOffsetDateTimeRegex = /^(\d{4})-(\d{2})-(\d{2})T([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?(\.\d{1,3})?(Z|[+-]([01]\d|2[0-3]):[0-5]\d)$/;
46473
47368
  const localDateTimeRegex = /^(\d{4})-(\d{2})-(\d{2})T([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?(\.\d{1,3})?$/;
46474
47369
  function issue(path, message) {
@@ -46541,7 +47436,7 @@ function classifyCanonicalSchedule(input, options) {
46541
47436
  if (options.requireCanonicalFields && input.recurrenceRule === undefined) {
46542
47437
  issues.push(issue(["recurrenceRule"], "recurrenceRule is required"));
46543
47438
  }
46544
- if (input.defaultStartTime !== undefined && input.defaultStartTime !== null && !timeRegex.test(input.defaultStartTime)) {
47439
+ if (input.defaultStartTime !== undefined && input.defaultStartTime !== null && !localScheduleTimePattern.test(input.defaultStartTime)) {
46545
47440
  issues.push(issue(["defaultStartTime"], "Invalid time format (HH:MM or HH:MM:SS)"));
46546
47441
  }
46547
47442
  if (options.requireCanonicalFields && !input.defaultStartTime) {
@@ -46699,7 +47594,8 @@ const sdkEventUpsertOptionalFieldSchemas = {
46699
47594
  ]).optional(),
46700
47595
  bookingScope: z.enum(["single_occurrence", "whole_series"]).optional(),
46701
47596
  occurrences: z.array(sdkEventOccurrenceWireSchema).optional(),
46702
- capacity: z.number().int().positive().nullable().optional()
47597
+ capacity: z.number().int().positive().nullable().optional(),
47598
+ attendeeMigration: z.object({ pricingOptionIdentifier: z.string().trim().min(1) }).strict().optional()
46703
47599
  };
46704
47600
  function timingIssue(path, message) {
46705
47601
  return {
@@ -46807,73 +47703,66 @@ function classifySdkEventTiming(input, options = {}) {
46807
47703
  issues: schedule.issues.map((issue$1) => timingIssue(["schedule", ...issue$1.path], issue$1.message))
46808
47704
  };
46809
47705
  }
46810
- function parseSdkEventUpsertInput(input, options = {}) {
47706
+ function invalidEventGraph(path, message) {
47707
+ return {
47708
+ ok: false,
47709
+ issues: [{
47710
+ source: "graph",
47711
+ path,
47712
+ message,
47713
+ issue: { kind: "incoherentParentRunGraph" }
47714
+ }]
47715
+ };
47716
+ }
47717
+ function orderSdkEventRuns(input) {
46811
47718
  const runRefs = new Set(input.runRefs);
46812
47719
  const runsByIdentifier = new Map(input.runs.map((run) => [run.identifier, run]));
46813
47720
  if (input.runRefs.length !== runRefs.size || runsByIdentifier.size !== input.runs.length || input.runRefs.length !== input.runs.length || input.runRefs.some((ref) => !runsByIdentifier.has(ref)) || input.runs.some((run) => run.eventRef !== input.identifier)) {
46814
- return {
46815
- ok: false,
46816
- issues: [{
46817
- source: "graph",
46818
- path: ["runRefs"],
46819
- message: "Event runRefs and runs must form one complete parent graph",
46820
- issue: { kind: "incoherentParentRunGraph" }
46821
- }]
46822
- };
47721
+ return invalidEventGraph(["runRefs"], "Event runRefs and runs must form one complete parent graph");
46823
47722
  }
46824
- const runForRef = (ref) => {
47723
+ const resolveRun = (ref) => {
46825
47724
  const run = runsByIdentifier.get(ref);
46826
47725
  if (!run) throw new Error("Unreachable missing Event run after graph validation");
46827
47726
  return run;
46828
47727
  };
46829
- const firstRun = runForRef(input.runRefs[0]);
46830
- const remainingRuns = input.runRefs.slice(1).map(runForRef);
46831
- if (firstRun.bookingScope === "single_occurrence" && remainingRuns.length > 0 || remainingRuns.some((run) => run.bookingScope !== firstRun.bookingScope)) {
46832
- return {
46833
- ok: false,
46834
- issues: [{
46835
- source: "graph",
46836
- path: ["runs"],
46837
- message: "An Event must contain one ordinary run or one or more whole-series runs",
46838
- issue: { kind: "incoherentParentRunGraph" }
46839
- }]
46840
- };
46841
- }
46842
- const firstResult = parseSdkEventRunUpsertInput(firstRun, 0, options);
46843
- const remainingResults = remainingRuns.map((run, index) => parseSdkEventRunUpsertInput(run, index + 1, options));
46844
- const issues = [firstResult, ...remainingResults].flatMap((result) => result.ok ? [] : result.issues);
46845
- if (issues.length > 0) return {
46846
- ok: false,
46847
- issues
46848
- };
46849
- if (!firstResult.ok) return {
46850
- ok: false,
46851
- issues: firstResult.issues
47728
+ return {
47729
+ ok: true,
47730
+ runs: [resolveRun(input.runRefs[0]), ...input.runRefs.slice(1).map(resolveRun)]
46852
47731
  };
46853
- const parsedRemaining = remainingResults.flatMap((result) => result.ok ? [result.value] : []);
47732
+ }
47733
+ function admitSdkEventRunGraph(input, runs) {
47734
+ const [first, ...rest] = runs;
47735
+ if (first.bookingScope === "single_occurrence" && rest.length > 0 || rest.some((run) => run.bookingScope !== first.bookingScope)) {
47736
+ return invalidEventGraph(["runs"], "An Event must contain one ordinary run or one or more whole-series runs");
47737
+ }
47738
+ if (first.bookingScope === "single_occurrence" && first.identifier !== input.slug) {
47739
+ return invalidEventGraph([
47740
+ "runs",
47741
+ "0",
47742
+ "identifier"
47743
+ ], "identifier must match slug");
47744
+ }
46854
47745
  return {
46855
47746
  ok: true,
46856
47747
  value: {
46857
47748
  ...input,
46858
- runs: [firstResult.value, ...parsedRemaining]
47749
+ runs
46859
47750
  }
46860
47751
  };
46861
47752
  }
46862
- function toSdkEventRunUpsertBody(input) {
46863
- const { timingAuthority, ...common } = input;
46864
- switch (timingAuthority.kind) {
46865
- case "authored_schedule": return {
46866
- ...common,
46867
- ...timingAuthority.bookingScope === undefined ? {} : { bookingScope: timingAuthority.bookingScope },
46868
- schedule: timingAuthority.schedule
46869
- };
46870
- case "explicit_occurrences": return {
46871
- ...common,
46872
- bookingScope: timingAuthority.bookingScope,
46873
- occurrences: timingAuthority.occurrences
46874
- };
46875
- default: return timingAuthority;
46876
- }
47753
+ function parseSdkEventUpsertInput(input, options = {}) {
47754
+ const graph = orderSdkEventRuns(input);
47755
+ if (!graph.ok) return graph;
47756
+ const [firstRun, ...remainingRuns] = graph.runs;
47757
+ const first = parseSdkEventRunUpsertInput(firstRun, 0, options);
47758
+ const remaining = remainingRuns.map((run, index) => parseSdkEventRunUpsertInput(run, index + 1, options));
47759
+ const issues = [first, ...remaining].flatMap((result) => result.ok ? [] : result.issues);
47760
+ if (!first.ok || issues.length > 0) return {
47761
+ ok: false,
47762
+ issues
47763
+ };
47764
+ const parsedRemaining = remaining.flatMap((result) => result.ok ? [result.value] : []);
47765
+ return admitSdkEventRunGraph(input, [first.value, ...parsedRemaining]);
46877
47766
  }
46878
47767
  function toSdkEventUpsertBody(input) {
46879
47768
  const [first, ...rest] = input.runs;
@@ -46886,50 +47775,296 @@ function toSdkEventUpsertBody(input) {
46886
47775
  runs: [toBody(first), ...rest.map(toBody)]
46887
47776
  };
46888
47777
  }
47778
+ function toValidatedSdkEventRun(input, timingAuthority, bookingOffer, runIndex) {
47779
+ if (timingAuthority.bookingScope === "single_occurrence" && "name" in input) {
47780
+ return invalidEventGraph([
47781
+ "runs",
47782
+ String(runIndex),
47783
+ "name"
47784
+ ], "Unrecognized key: \"name\"");
47785
+ }
47786
+ const { schedule: _schedule, occurrences: _occurrences, bookingScope: _bookingScope, name, bookingOffer: _bookingOffer, ...common } = input;
47787
+ const fields = {
47788
+ ...common,
47789
+ bookingOffer,
47790
+ timingAuthority,
47791
+ ...timingAuthority.kind === "authored_schedule" ? { schedule: timingAuthority.schedule } : { occurrences: timingAuthority.occurrences }
47792
+ };
47793
+ return {
47794
+ ok: true,
47795
+ value: timingAuthority.bookingScope === "whole_series" ? {
47796
+ ...fields,
47797
+ bookingScope: "whole_series",
47798
+ ...name === undefined ? {} : { name }
47799
+ } : {
47800
+ ...fields,
47801
+ bookingScope: "single_occurrence"
47802
+ }
47803
+ };
47804
+ }
46889
47805
  function parseSdkEventRunUpsertInput(input, runIndex, options) {
46890
- const timingResult = classifySdkEventTiming(input, options.schedule);
46891
- const bookingOfferResult = classifySdkEventBookingOffer(input.bookingOffer);
46892
- const issues = [];
46893
- if (timingResult.ok && bookingOfferResult.ok) {
46894
- const { schedule: _schedule, occurrences: _occurrences, bookingScope: _bookingScope, bookingOffer: _bookingOffer, ...common } = input;
47806
+ if (input.bookingScope === undefined) {
47807
+ const issue$1 = timingIssue(["bookingScope"], "bookingScope is required");
46895
47808
  return {
46896
- ok: true,
46897
- value: (() => {
46898
- const parsed = {
46899
- ...common,
46900
- timingAuthority: timingResult.timingAuthority,
46901
- bookingOffer: bookingOfferResult.bookingOffer
46902
- };
46903
- return {
46904
- ...toSdkEventRunUpsertBody(parsed),
46905
- timingAuthority: parsed.timingAuthority
46906
- };
46907
- })()
47809
+ ok: false,
47810
+ issues: [{
47811
+ runIndex,
47812
+ source: "timing",
47813
+ ...issue$1,
47814
+ issue: issue$1
47815
+ }]
46908
47816
  };
46909
47817
  }
46910
- if (!timingResult.ok) {
46911
- issues.push(...timingResult.issues.map((timing) => ({
47818
+ const timing = classifySdkEventTiming(input, options.schedule);
47819
+ const offer = classifySdkEventBookingOffer(input.bookingOffer);
47820
+ if (timing.ok && offer.ok) {
47821
+ return toValidatedSdkEventRun(input, {
47822
+ ...timing.timingAuthority,
47823
+ bookingScope: input.bookingScope
47824
+ }, offer.bookingOffer, runIndex);
47825
+ }
47826
+ return {
47827
+ ok: false,
47828
+ issues: [...timing.ok ? [] : timing.issues.map((issue$1) => ({
46912
47829
  runIndex,
46913
47830
  source: "timing",
46914
- path: timing.path,
46915
- message: timing.message,
46916
- issue: timing
46917
- })));
46918
- }
46919
- if (!bookingOfferResult.ok) {
46920
- issues.push(...bookingOfferResult.issues.map((bookingOfferIssue) => ({
47831
+ path: issue$1.path,
47832
+ message: issue$1.message,
47833
+ issue: issue$1
47834
+ })), ...offer.ok ? [] : offer.issues.map((issue$1) => ({
46921
47835
  runIndex,
46922
47836
  source: "bookingOffer",
46923
- path: bookingOfferIssue.path,
46924
- message: bookingOfferIssue.message,
46925
- issue: bookingOfferIssue
46926
- })));
47837
+ path: issue$1.path,
47838
+ message: issue$1.message,
47839
+ issue: issue$1
47840
+ }))]
47841
+ };
47842
+ }
47843
+
47844
+ //#endregion
47845
+ //#region ../blocks/src/lib/typeGuards.ts
47846
+ /**
47847
+ * Strict record guard: excludes null, primitives, and arrays.
47848
+ */
47849
+ function isRecord(value) {
47850
+ return typeof value === "object" && value !== null && !Array.isArray(value);
47851
+ }
47852
+ /**
47853
+ * Non-null object guard: includes arrays for behavior parity with legacy helpers.
47854
+ *
47855
+ * NOTE: This intentionally narrows to UnknownRecord for compatibility with existing
47856
+ * callsites even though arrays pass at runtime. Follow-up cleanup should replace
47857
+ * this API with a more precise predicate type and migrate consumers.
47858
+ */
47859
+ function isObjectRecord(value) {
47860
+ return typeof value === "object" && value !== null;
47861
+ }
47862
+ /**
47863
+ * Coerces unknown input into a strict record, returning an empty object fallback.
47864
+ */
47865
+ function asRecord(value) {
47866
+ return isRecord(value) ? value : {};
47867
+ }
47868
+
47869
+ //#endregion
47870
+ //#region ../blocks/src/system/manifest/richTextSchema.ts
47871
+ /**
47872
+ * Shared Rich Text Schema
47873
+ *
47874
+ * This module provides a single source of truth for TipTap rich text validation
47875
+ * across both the block-form editor and the block rendering system.
47876
+ *
47877
+ * Rich-text fields use one canonical root shape:
47878
+ * `{ type: 'doc', content: [...] }`.
47879
+ */
47880
+ /**
47881
+ * TipTap document node schema (recursive structure)
47882
+ *
47883
+ * Supports the full TipTap document structure including:
47884
+ * - type: Node type (e.g., 'doc', 'paragraph', 'text')
47885
+ * - content: Nested array of child nodes
47886
+ * - text: Text content for text nodes
47887
+ * - attrs: Node attributes (e.g., alignment, color)
47888
+ * - marks: Text formatting marks (e.g., bold, italic)
47889
+ */
47890
+ const TipTapMarkSchema = z.object({
47891
+ type: z.string().min(1),
47892
+ attrs: z.record(z.string(), z.unknown()).optional()
47893
+ }).strict();
47894
+ const TipTapNodeSchema = z.lazy(() => z.object({
47895
+ type: z.string().min(1),
47896
+ content: z.array(TipTapNodeSchema).optional(),
47897
+ text: z.string().optional(),
47898
+ attrs: z.record(z.string(), z.unknown()).optional(),
47899
+ marks: z.array(TipTapMarkSchema).optional()
47900
+ }).strict().superRefine((node, ctx) => {
47901
+ if (node.type === "doc") {
47902
+ ctx.addIssue({
47903
+ code: "custom",
47904
+ path: ["type"],
47905
+ message: "Document nodes are only valid at the rich-text root"
47906
+ });
47907
+ }
47908
+ if (node.type === "text") {
47909
+ if (node.text === undefined || node.text.length === 0) {
47910
+ ctx.addIssue({
47911
+ code: "custom",
47912
+ path: ["text"],
47913
+ message: "Text nodes require a non-empty text value"
47914
+ });
47915
+ }
47916
+ if (node.content !== undefined) {
47917
+ ctx.addIssue({
47918
+ code: "custom",
47919
+ path: ["content"],
47920
+ message: "Text nodes cannot contain child nodes"
47921
+ });
47922
+ }
47923
+ return;
47924
+ }
47925
+ if (node.text !== undefined) {
47926
+ ctx.addIssue({
47927
+ code: "custom",
47928
+ path: ["text"],
47929
+ message: "Only text nodes can contain a text value"
47930
+ });
47931
+ }
47932
+ }));
47933
+ /**
47934
+ * Canonical schema for a TipTap rich-text document root.
47935
+ *
47936
+ * Stricter than {@link TipTapNodeSchema}: `type` is literally `'doc'` and
47937
+ * `content` is required (possibly empty). This is the shape every producer
47938
+ * (dummy content, editor output, migrations) must emit, and the shape every
47939
+ * non-legacy consumer can rely on.
47940
+ *
47941
+ */
47942
+ const RichTextDocSchema = z.object({
47943
+ type: z.literal("doc"),
47944
+ content: z.array(TipTapNodeSchema)
47945
+ }).strict();
47946
+ /**
47947
+ * Type guard for {@link RichTextDoc}.
47948
+ *
47949
+ * Returns `true` only for the canonical unwrapped form. The shared structural
47950
+ * contract lives in `@riverbankcms/core`; parity tests keep its dependency-free
47951
+ * guard aligned with this detailed Zod validator.
47952
+ */
47953
+ function isRichTextDoc(value) {
47954
+ return RichTextDocSchema.safeParse(value).success;
47955
+ }
47956
+ /**
47957
+ * Build a canonical {@link RichTextDoc} from plain-text paragraphs.
47958
+ *
47959
+ * Single source of truth for constructing a TipTap doc from strings.
47960
+ * Used by:
47961
+ * - `@riverbankcms/dummy-content` (fixture body generators)
47962
+ * - `@riverbankcms/content-model` (default template `cta-full` content)
47963
+ *
47964
+ * Empty strings produce a paragraph with no `content` array — matching
47965
+ * the TipTap convention for empty paragraphs. The shape is exactly what
47966
+ * {@link RichTextDocSchema} validates, so the type and runtime check
47967
+ * never drift from the constructor.
47968
+ */
47969
+ function createRichTextDoc(paragraphs) {
47970
+ return {
47971
+ type: "doc",
47972
+ content: paragraphs.map((text) => text ? {
47973
+ type: "paragraph",
47974
+ content: [{
47975
+ type: "text",
47976
+ text
47977
+ }]
47978
+ } : { type: "paragraph" })
47979
+ };
47980
+ }
47981
+ function createHardBreakNode() {
47982
+ return { type: "hardBreak" };
47983
+ }
47984
+ /**
47985
+ * Parse a small markdown subset into TipTap inline nodes.
47986
+ *
47987
+ * This intentionally supports only bold (`**text**`) because the SDK/content
47988
+ * import paths use it for lightweight default content, not full markdown.
47989
+ */
47990
+ function parseRichTextInlineMarkdown(text) {
47991
+ const out = [];
47992
+ const pushText = (value, marks) => {
47993
+ if (!value) return;
47994
+ out.push(marks && marks.length > 0 ? {
47995
+ type: "text",
47996
+ text: value,
47997
+ marks
47998
+ } : {
47999
+ type: "text",
48000
+ text: value
48001
+ });
48002
+ };
48003
+ let rest = text;
48004
+ while (rest.length > 0) {
48005
+ const start = rest.indexOf("**");
48006
+ if (start === -1) {
48007
+ pushText(rest);
48008
+ break;
48009
+ }
48010
+ const end = rest.indexOf("**", start + 2);
48011
+ if (end === -1) {
48012
+ pushText(rest);
48013
+ break;
48014
+ }
48015
+ pushText(rest.slice(0, start));
48016
+ pushText(rest.slice(start + 2, end), [{ type: "bold" }]);
48017
+ rest = rest.slice(end + 2);
46927
48018
  }
48019
+ return out;
48020
+ }
48021
+ function parseRichTextParagraphInlineMarkdown(text) {
48022
+ return text.split("\n").flatMap((line, index) => {
48023
+ const lineNodes = parseRichTextInlineMarkdown(line.trim());
48024
+ return index === 0 ? lineNodes : [createHardBreakNode(), ...lineNodes];
48025
+ });
48026
+ }
48027
+ /**
48028
+ * Convert a plain string with a tiny markdown subset to canonical TipTap JSON.
48029
+ *
48030
+ * Double newlines create semantic paragraphs. Single newlines remain inside the
48031
+ * current paragraph as TipTap `hardBreak` nodes for line-oriented content such
48032
+ * as addresses, opening hours, and contact details.
48033
+ */
48034
+ function createRichTextDocFromMarkdown(markdown) {
48035
+ const normalized = markdown.replace(/\r\n/g, "\n").trim();
48036
+ if (!normalized) return {
48037
+ type: "doc",
48038
+ content: []
48039
+ };
48040
+ const paragraphs = normalized.split(/\n{2,}/).map((paragraph) => paragraph.trim()).filter(Boolean);
46928
48041
  return {
46929
- ok: false,
46930
- issues
48042
+ type: "doc",
48043
+ content: paragraphs.map((paragraph) => {
48044
+ const inline = parseRichTextParagraphInlineMarkdown(paragraph);
48045
+ return inline.length > 0 ? {
48046
+ type: "paragraph",
48047
+ content: inline
48048
+ } : { type: "paragraph" };
48049
+ })
46931
48050
  };
46932
48051
  }
48052
+ function buildRichTextSchema(options) {
48053
+ let schema = RichTextDocSchema;
48054
+ if (options?.required) {
48055
+ const label = options.label ?? "This field";
48056
+ schema = schema.refine((val) => {
48057
+ const content = val.content;
48058
+ if (!Array.isArray(content) || content.length === 0) return false;
48059
+ const first = content[0];
48060
+ if (content.length === 1 && isRecord(first) && first.type === "paragraph" && (!("content" in first) || first.content == null)) {
48061
+ return false;
48062
+ }
48063
+ return true;
48064
+ }, { message: `${label} is required` });
48065
+ }
48066
+ return schema;
48067
+ }
46933
48068
 
46934
48069
  //#endregion
46935
- export { arrangementManifestFromVariants as $, parseCanonicalMediaIdentifier as $n, backgroundColorStyle as $r, getFontWidthRange as $t, pickShadeTowardBackground as A, getTextColorForBackground as Ai, walkAndCollectImageAttrs as An, asAppearancePresetId as Ar, INPUT_FONT_SIZE_MAP as At, createNavigationRef as B, __toESM as Bi, getImageUrl as Bn, eyebrowTreatmentValues as Br, generateButtonCss as Bt, pagePathForCommand as C, surfaceForegroundDeclarations as Ci, boxBackgroundSchema as Cn, backgroundAppearanceColorTokenNames as Cr, MQ_TABLET_ONLY as Ct, bookingPaymentTermsSchema as D, expandPalette as Di, stripStaleTransitionStyleGroup as Dn, resolveEffectiveKnobs as Dr, parseBlockCustomCssRules as Dt, bookingPaymentCollectionOptionSchema as E, surfaceForegroundValue as Ei, siteChromeBackgroundOverrideOwner as En, defineBlockDesignMetadata as Er, getComponentShadowVar as Et, createContentEntryRef as F, parseColorTokenParts as Fi, isExplicitRectCrop as Fn, assertNever$1 as Fr, ACCORDION_TRIGGER_FONT_SCALE as Ft, slugPart as G, persistableMediaSchema as Gn, offeringsPreviewCompositionTreatmentValues as Gr, parseThemeButtonVariantId as Gt, createSiteCommandSiteRef as H, imageMediaSchema as Hn, headingDecorationColorTokenValues as Hr, resolvedButtonColourDeclarations as Ht, createEventCategoryRef as I, featuresFromHex as Ii, parseAspectRatioValue as In, defineBlockDesignCapability as Ir, ACCORDION_TRIGGER_LETTER_SPACING as It, headerLeafTextColor as J, indexRenderableMediaAssets as Jn, visibleHeadingDecorationFamilyValues as Jr, themeButtonVariantClassNames as Jt, effectiveHeaderNavLinkStyle as K, runtimeMediaSchema as Kn, testimonialsCompositionTreatmentValues as Kr, themeButtonExtraClassesWithTrailingIcon as Kt, createEventSeriesRef as L, hexToRgb as Li, resolveMediaTransformForAspect as Ln, defineFrameCapability as Lr, ICON_CONFIG as Lt, createBlockRef as M, contrastRatio as Mi, looksLikeUuid as Mn, asLayoutVariantKey as Mr, LABEL_FONT_SIZE_MAP as Mt, createBlocksRef as N, parseThemeColorToken as Ni, rotateNormalizedPoint as Nn, asPageFlowPresetId as Nr, ACCORDION_OPEN_ACCENT_THICKNESS as Nt, require_jsx_runtime as O, isOnColorBackgroundCandidateToken as Oi, collectPortableMediaReferencesFromContent as On, cardListFamilyBlocks as Or, parseCustomCssRules as Ot, createCommandRef as P, parseBlockColorToken as Pi, normalizeWidthCandidates as Pn, asSiteStyleId as Pr, ACCORDION_SPACING_MAP as Pt, readHeaderNavFontSizePreference as Q, buildMediaIdentifierFromFilename as Qn, TokenResolver as Qr, generateFontFaceCss as Qt, createFooterRef as R, oklchToHexGamut as Ri, getDirectImageUrlForRelativePath as Rn, defineNoBlockDesignCapability as Rr, ICON_SIZE_PX as Rt, pageIdentifierForCommand as S, DEFAULT_SURFACE_TEXT_COLOR as Si, boxBackgroundOverlaySchema as Sn, normalizeCardRole as Sr, MQ_SM_UP as St, eventBookingOfferIntentSchema as T, surfaceForegroundStyle as Ti, pageDesignEditorBackgroundOverrideOwner as Tn, parseBackgroundAppearanceColorToken as Tr, getComponentBorderWidthPx as Tt, createVenueRef as U, mediaPosterSchema as Un, introCompositionTreatmentValues as Ur, resolvedButtonFocusDeclarations as Ut, createPageRef as V, isSiteScopedDirectImageStoragePath as Vn, featureCardCompositionTreatmentValues as Vr, RESOLVED_BUTTON_DISABLED_DECLARATIONS as Vt, refSlug as W, mediaSchema as Wn, makeBoundaryId as Wr, generateDefaultButtonSystem as Wt, readOpeningForegroundProjection as X, mediaTypeDiscriminants as Xn, surfaceDecorationColors as Xr, skinTokenCssVars as Xt, transparentHeaderForegroundInput as Y, resolveManagedMediaReference as Yn, resolveSurface as Yr, parseThemeForPersistence as Yt, mergeHeaderNavTypographyPreference as Z, mediaIdentifierSchema as Zn, resolveSurfaceForeground as Zr, buildGoogleFontFamilyQuery as Zt, getSiteCommandMetadata as _, unsafeAsOpaqueButtonColourToken as _i, anglePolygonCss as _n, generateCardCss as _r, BREAKPOINT_SM as _t, classifySdkEventSchedule as a, textColorValue as ai, arrangementLookHooks as an, getExtensionForMimeType as ar, gradientToCss as at, entryIdentifierForCommand as b, buildThemeTokens as bi, parseCanonicalRuntimeBackground as bn, cardRoleSchema as br, MQ_MD_DOWN as bt, isEventVisibility as c, compileButtonColourStates as ci, resolveLookSlotSurfaces as cn, normalizeAmbiguousStoragePathForSite as cr, parseGradientPresetId as ct, stableStringify as d, resolveOnColorForThemeToken as di, footerPreferencesSchema as dn, MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES as dr, generateHeaderScrollTreatmentStyles as dt, borderColorStyle as ei, buttonStyleSelectionSchema as en, parseMediaIdentifier as er, readStoredMode as et, createDryRunReport as f, resolveThemeColorReferenceValue as fi, headerRootPaint as fn, classifyKnownMimeType as fr, generateMobileOverlayBackdropFilterFix as ft, evaluateSiteCommandExposure as g, buttonVariantIdSchema as gi, boundaryRuntimeMetricsForDecorativeTransition as gn, getDefaultCardVariants as gr, BREAKPOINT_MD as gt, evaluateSiteCommandBatchExposure as h, PUBLIC_BUTTON_ROLES as hi, sectionFactsForContent as hn, generateCardCoreCss as hr, BREAKPOINT_LG as ht, sdkEventUpsertOptionalFieldSchemas as i, textColorStyle as ii, generateNavLinkCSS as in, resolveVideoSelectionIntentForMediaField as ir, requireCuratedSiteStyle as it, generateCssVars as j, ORDINARY_TEXT_MIN_CONTRAST_RATIO as ji, deleteEnvSpecificKeys as jn, asDesignBlockId as jr, INPUT_SPACING_MAP as jt, require_react as k, onColorTokenName as ki, summarizePortableMediaReferences as kn, cardListKnobBlocks as kr, FONT_WEIGHT_MAP as kt, venueManagementFields as l, formatButtonColourContractError as li, pageDesignBoundaryTreatmentSchema as ln, parseRelativeStoragePath as lr, deriveSiteStyleBlockPolicy as lt, mapEventCommandContent as m, generateShadowValue$1 as mi, composeBoundary as mn, parseSafeDownloadFilename as mr, renderHeaderPaintTargetRules as mt, sdkCourseBookingSettingsSchema as n, mergeStyles as ni, footerForegroundCandidates as nn, slugifyMediaIdentifier as nr, resolveModeArrangement as nt, classifySdkEventBookingOffer as o, opaqueSurfaceContextFromTheme as oi, arrangementSlotNames as on, getMimeType as or, GRADIENT_PRESET_IDS as ot, parseAndPlanCommandBatchForTypes as p, resolveTokenOrCssColorValue as pi, boundaryLayout as pn, managementMediaLibraryUploadLimitForMimeType as pr, getHeaderPaintTargets as pt, headerForegroundCandidates as q, buildRenderableMediaAssetIndex as qn, tileTreatmentValues as qr, themeButtonSizeClassName as qt, sdkEventOccurrenceInputSchema as r, rgbColorValue as ri, buildTextContrastShadow as rn, deriveVideoSlotPolicy as rr, resolvePublishedLook as rt, eventVisibilitySchema as s, opaqueSurfaceDeclarations as si, resolveItemSurface as sn, asRecord as sr, getGradientPreset as st, classifySdkEventTiming as t, cssVarValue as ti, generateFooterCss as tn, resolveMediaIdentifier as tr, resolveEffectiveArrangement as tt, sortRecord as u, isCssColorLiteral as ui, pageDesignIntentSchemaVersion as un, FILE_SIZE_LIMITS as ur, themeSchema as ut, blockIdentifierForCommand as v, selectLogoVariant as vi, transitionCatalogueEntries as vn, renderCssDeclarations as vr, BREAKPOINT_XL as vt, hasConfiguredBookingVertical as w, surfaceForegroundInputFromResolved as wi, runtimeBoxBackgroundSchema as wn, backgroundAppearanceQuickColorTokenNames as wr, headerVariantValues as wt, pageIdentifierForBlockCommand as x, defaultThemeTokens as xi, resolveSectionBackground as xn, cardVariantRoleValues as xr, MQ_SM_DOWN as xt, blockOrderIdentifiersForCommand as y, surfaceToneFromForegroundToken as yi, deriveSectionGround as yn, generateShadowValue as yr, MQ_LG_UP as yt, createFormRef as z, __commonJSMin as zi, getDirectImageUrlForSiteScopedPath as zn, eventListTreatmentValues as zr, generateButtonActionLayoutCss as zt };
48070
+ export { createBlocksRef as $, isOnColorBackgroundCandidateToken as $i, looksLikeUuid as $n, asLayoutVariantKey as $r, ACCORDION_OPEN_ACCENT_THICKNESS as $t, parseAndPlanCommandBatchForTypes as A, opaqueSurfaceDeclarations as Ai, pageDesignIntentSchemaVersion as An, parseRelativeStoragePath as Ar, themeSchema as At, pageIdentifierForCommand as B, unsafeAsOpaqueButtonColourToken as Bi, parseCanonicalRuntimeBackground as Bn, generateShadowValue as Br, MQ_MD_DOWN as Bt, pageLinkSchema as C, borderColorStyle as Ci, buildTextContrastShadow as Cn, slugifyMediaIdentifier as Cr, resolvePublishedLook as Ct, sortRecord as D, textColorStyle as Di, resolveItemSurface as Dn, getMimeType as Dr, getGradientPreset as Dt, venueManagementFields as E, rgbColorValue as Ei, arrangementSlotNames as En, getExtensionForMimeType as Er, GRADIENT_PRESET_IDS as Et, eventManagementTiming as F, resolveThemeColorReferenceValue as Fi, sectionFactsForContent as Fn, parseSafeDownloadFilename as Fr, BREAKPOINT_LG as Ft, bookingPaymentCollectionOptionSchema as G, DEFAULT_SURFACE_TEXT_COLOR as Gi, runtimeBoxBackgroundSchema as Gn, backgroundAppearanceQuickColorTokenNames as Gr, getComponentBorderWidthPx as Gt, hasConfiguredBookingVertical as H, surfaceToneFromForegroundToken as Hi, resolveSectionPhotoPlacement as Hn, cardVariantRoleValues as Hr, MQ_SM_UP as Ht, blockIdentifierForCommand as I, resolveTokenOrCssColorValue as Ii, boundaryRuntimeMetricsForDecorativeTransition as In, generateCardCoreCss as Ir, BREAKPOINT_MD as It, require_react as J, surfaceForegroundInputFromResolved as Ji, stripStaleTransitionStyleGroup as Jn, resolveEffectiveKnobs as Jr, parseCustomCssRules as Jt, bookingPaymentTermsSchema as K, SURFACE_FOREGROUND_CSS_VARS as Ki, pageDesignEditorBackgroundOverrideOwner as Kn, parseBackgroundAppearanceColorToken as Kr, getComponentShadowVar as Kt, blockOrderIdentifiersForCommand as L, generateShadowValue$1 as Li, anglePolygonCss as Ln, getDefaultCardVariants as Lr, BREAKPOINT_SM as Lt, evaluateSiteCommandBatchExposure as M, formatButtonColourContractError as Mi, headerRootPaint as Mn, MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES as Mr, generateMobileOverlayBackdropFilterFix as Mt, evaluateSiteCommandExposure as N, isCssColorLiteral as Ni, boundaryLayout as Nn, classifyKnownMimeType as Nr, getHeaderPaintTargets as Nt, stableStringify as O, textColorValue as Oi, resolveLookSlotSurfaces as On, asRecord$1 as Or, parseGradientPresetId as Ot, getSiteCommandMetadata as P, resolveOnColorForThemeToken as Pi, composeBoundary as Pn, managementMediaLibraryUploadLimitForMimeType as Pr, renderHeaderPaintTargetRules as Pt, createBlockRef as Q, expandPalette as Qi, deleteEnvSpecificKeys as Qn, asDesignBlockId as Qr, LABEL_FONT_SIZE_MAP as Qt, entryIdentifierForCommand as R, PUBLIC_BUTTON_ROLES as Ri, transitionCatalogueEntries as Rn, generateCardCss as Rr, BREAKPOINT_XL as Rt, normalizeLinkValue as S, backgroundColorStyle as Si, footerForegroundCandidates as Sn, resolveMediaIdentifier as Sr, resolveModeArrangement as St, isEventVisibility as T, mergeStyles as Ti, arrangementLookHooks as Tn, resolveVideoSelectionIntentForMediaField as Tr, gradientToCss as Tt, eventCommandScheduleSchema as U, buildThemeTokens as Ui, boxBackgroundOverlaySchema as Un, normalizeCardRole as Ur, MQ_TABLET_ONLY as Ut, pagePathForCommand as V, selectLogoVariant as Vi, resolveSectionBackground as Vn, cardRoleSchema as Vr, MQ_SM_DOWN as Vt, eventBookingOfferIntentSchema as W, defaultThemeTokens as Wi, boxBackgroundSchema as Wn, backgroundAppearanceColorTokenNames as Wr, headerVariantValues as Wt, generateCssVars as X, surfaceForegroundValue as Xi, summarizePortableMediaReferences as Xn, cardListKnobBlocks as Xr, INPUT_FONT_SIZE_MAP as Xt, pickShadeTowardBackground as Y, surfaceForegroundStyle as Yi, collectPortableMediaReferencesFromContent as Yn, cardListFamilyBlocks as Yr, FONT_WEIGHT_MAP as Yt, ianaTimezoneSchema as Z, surfaceForegroundValueWithOpacity as Zi, walkAndCollectImageAttrs as Zn, asAppearancePresetId as Zr, INPUT_SPACING_MAP as Zt, internalResolvedLinkSchema as _, resolveSurface as _i, buildGoogleFontFamilyQuery as _n, mediaTypeDiscriminants as _r, mergeHeaderNavTypographyPreference as _t, isRecord as a, parseThemeColorToken as aa, defineNoBlockDesignCapability as ai, generateButtonActionLayoutCss as an, getDirectImageUrlForRelativePath as ar, createFormRef as at, isResolvedInternalLinkEntityType as b, safeTokenOr as bi, buttonStyleSelectionSchema as bn, parseCanonicalMediaIdentifier as br, readStoredMode as bt, sdkEventOccurrenceInputSchema as c, featuresFromHex as ca, featureCardCompositionTreatmentValues as ci, resolvedButtonColourDeclarations as cn, isSiteScopedDirectImageStoragePath as cr, createSiteCommandSiteRef as ct, classifySdkEventBookingOffer as d, __commonJSMin as da, makeBoundaryId as di, parseThemeButtonVariantId as dn, mediaSchema as dr, slugPart as dt, onColorTokenName as ea, asPageFlowPresetId as ei, ACCORDION_SPACING_MAP as en, rotateNormalizedPoint as er, createCommandRef as et, RESOLVED_INTERNAL_LINK_ENTITY_TYPES as f, __toESM as fa, offeringsPreviewCompositionTreatmentValues as fi, themeButtonExtraClassesWithTrailingIcon as fn, persistableMediaSchema as fr, effectiveHeaderNavLinkStyle as ft, internalLinkSchema as g, visibleHeadingDecorationFamilyValues as gi, skinTokenCssVars as gn, resolveManagedMediaReference as gr, readOpeningForegroundProjection as gt, externalLinkSchema as h, toNonEmptyReadonlyArray as hi, parseThemeForPersistence as hn, indexRenderableMediaAssets as hr, transparentHeaderForegroundInput as ht, isObjectRecord as i, resolveOpaqueSurfaceHex as ia, defineFrameCapability as ii, ICON_SIZE_PX as in, resolveMediaTransformForAspect as ir, createFooterRef as it, mapEventCommandContent as j, compileButtonColourStates as ji, footerPreferencesSchema as jn, FILE_SIZE_LIMITS as jr, generateHeaderScrollTreatmentStyles as jt, createDryRunReport as k, opaqueSurfaceContextFromTheme as ki, pageDesignBoundaryTreatmentSchema as kn, normalizeAmbiguousStoragePathForSite as kr, deriveSiteStyleBlockPolicy as kt, sdkEventUpsertOptionalFieldSchemas as l, hexToRgb as la, headingDecorationColorTokenValues as li, resolvedButtonFocusDeclarations as ln, imageMediaSchema as lr, createVenueRef as lt, entryLinkSchema as m, tileTreatmentValues as mi, themeButtonVariantClassNames as mn, buildRenderableMediaAssetIndex as mr, headerLeafTextColor as mt, createRichTextDoc as n, ORDINARY_TEXT_MIN_CONTRAST_RATIO as na, assertNever$1 as ni, ACCORDION_TRIGGER_LETTER_SPACING as nn, isExplicitRectCrop as nr, createEventCategoryRef as nt, classifySdkEventTiming as o, parseBlockColorToken as oa, eventListTreatmentValues as oi, generateButtonCss as on, getDirectImageUrlForSiteScopedPath as or, createNavigationRef as ot, customLinkSchema as p, testimonialsCompositionTreatmentValues as pi, themeButtonSizeClassName as pn, runtimeMediaSchema as pr, headerForegroundCandidates as pt, require_jsx_runtime as q, surfaceForegroundDeclarations as qi, siteChromeBackgroundOverrideOwner as qn, defineBlockDesignMetadata as qr, parseBlockCustomCssRules as qt, isRichTextDoc as r, contrastRatio as ra, defineBlockDesignCapability as ri, ICON_CONFIG as rn, parseAspectRatioValue as rr, createEventSeriesRef as rt, sdkCourseBookingSettingsSchema as s, parseColorTokenParts as sa, eyebrowTreatmentValues as si, RESOLVED_BUTTON_DISABLED_DECLARATIONS as sn, getImageUrl as sr, createPageRef as st, RichTextDocSchema as t, getTextColorForBackground as ta, asSiteStyleId as ti, ACCORDION_TRIGGER_FONT_SCALE as tn, normalizeWidthCandidates as tr, createContentEntryRef as tt, classifySdkEventSchedule as u, oklchToHexGamut as ua, introCompositionTreatmentValues as ui, generateDefaultButtonSystem as un, mediaPosterSchema as ur, refSlug as ut, internalRouteOnlyLinkSchema as v, surfaceDecorationColors as vi, generateFontFaceCss as vn, mediaIdentifierSchema as vr, readHeaderNavFontSizePreference as vt, eventVisibilitySchema as w, cssVarValue as wi, generateNavLinkCSS as wn, deriveVideoSlotPolicy as wr, requireCuratedSiteStyle as wt, linkSchema as x, TokenResolver as xi, generateFooterCss as xn, parseMediaIdentifier as xr, resolveEffectiveArrangement as xt, isInternalResolvedLinkValue as y, resolveSurfaceForeground as yi, getFontWidthRange as yn, buildMediaIdentifierFromFilename as yr, arrangementManifestFromVariants as yt, pageIdentifierForBlockCommand as z, buttonVariantIdSchema as zi, deriveSectionGround as zn, renderCssDeclarations as zr, MQ_LG_UP as zt };