@riverbankcms/sdk 0.111.0 → 0.119.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/dist/{PreviewEditorSidebar-CTR6LUhN.mjs → PreviewEditorSidebar-C7TXFitV.mjs} +4 -4
  2. package/dist/{PreviewEditorUI-DZg15Zip.mjs → PreviewEditorUI-_1JIHTXS.mjs} +3 -3
  3. package/dist/{PreviewQueryProvider-kkmjklLG.mjs → PreviewQueryProvider-nKUT4TL-.mjs} +1 -1
  4. package/dist/{SdkPreviewModeRuntime-DeOTAFvR.mjs → SdkPreviewModeRuntime-CvJTVgWa.mjs} +2579 -34325
  5. package/dist/{SiteChromeCustomizeContext-DOSDLCiJ.mjs → SiteChromeCustomizeContext-66QfCbAS.mjs} +64625 -32791
  6. package/dist/_dts/ai/src/contracts/proposals.d.ts +5211 -639
  7. package/dist/_dts/ai/src/designer/themePatch.d.ts +1 -1
  8. package/dist/_dts/api/src/bookingAdjustments.d.ts +30 -36
  9. package/dist/_dts/api/src/bookingConfig.d.ts +26 -1
  10. package/dist/_dts/api/src/bookingRecords.d.ts +6 -74
  11. package/dist/_dts/api/src/bookingRefundOutcomes.d.ts +3 -3
  12. package/dist/_dts/api/src/contentRuntime.d.ts +5 -1
  13. package/dist/_dts/api/src/customerMembershipOutcomes.d.ts +4 -4
  14. package/dist/_dts/api/src/endpoints.d.ts +35 -0
  15. package/dist/_dts/api/src/event-presentation.d.ts +4 -0
  16. package/dist/_dts/api/src/index.d.ts +4 -4
  17. package/dist/_dts/api/src/navigation/dashboardBookingsNav.d.ts +4 -1
  18. package/dist/_dts/api/src/navigation/dashboardSettingsNav.d.ts +2 -2
  19. package/dist/_dts/api/src/sdk-event-upsert.d.ts +2 -2
  20. package/dist/_dts/api/src/sdkContracts.d.ts +85 -7
  21. package/dist/_dts/api/src/siteBanner.d.ts +8 -8
  22. package/dist/_dts/api/src/siteOperations.d.ts +1 -1
  23. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +32 -4
  24. package/dist/_dts/block-form/src/widgets/RichTextField/richTextFieldDefaults.d.ts +3 -12
  25. package/dist/_dts/block-form/src/widgets/RichTextField/richTextFieldSchema.d.ts +1 -5
  26. package/dist/_dts/blocks/src/bindings/index.d.ts +1 -10
  27. package/dist/_dts/blocks/src/index.d.ts +3 -6
  28. package/dist/_dts/blocks/src/primitives/content/primitives.d.ts +2 -2
  29. package/dist/_dts/blocks/src/system/blocks/body-text.d.ts +2 -2
  30. package/dist/_dts/blocks/src/system/blocks/events/recurring-schedule.d.ts +2 -2
  31. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +2 -8
  32. package/dist/_dts/blocks/src/system/blocks/feature-card-icons.d.ts +2 -2
  33. package/dist/_dts/blocks/src/system/blocks/feature-cards.d.ts +3 -3
  34. package/dist/_dts/blocks/src/system/blocks/form.d.ts +2 -15
  35. package/dist/_dts/blocks/src/system/blocks/intro.d.ts +2 -2
  36. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +66 -66
  37. package/dist/_dts/blocks/src/system/blocks/newsletter-signup.d.ts +2 -16
  38. package/dist/_dts/blocks/src/system/blocks/site-footer.d.ts +0 -3
  39. package/dist/_dts/blocks/src/system/fragments/index.d.ts +0 -1
  40. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +19 -2
  41. package/dist/_dts/blocks/src/system/manifest/richTextSchema.d.ts +8 -70
  42. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +1 -0
  43. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +6 -0
  44. package/dist/_dts/blocks/src/system/node/schema.d.ts +4 -4
  45. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +1 -10
  46. package/dist/_dts/blocks/src/system/runtime/nodes/buttonAffordance.d.ts +5 -21
  47. package/dist/_dts/blocks/src/system/runtime/nodes/richText-coerce.d.ts +14 -2
  48. package/dist/_dts/blocks/src/system/transforms/typed.d.ts +1 -1
  49. package/dist/_dts/content-editor/src/controller/seedDraftBaselinesFromBlocks.d.ts +1 -1
  50. package/dist/_dts/content-editor/src/hooks/useBlockAutosave.d.ts +17 -1
  51. package/dist/_dts/content-editor/src/hooks/useDebouncedLatestSave.d.ts +1 -1
  52. package/dist/_dts/content-editor/src/hooks/useSimpleAutosave.d.ts +1 -1
  53. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +3 -2
  54. package/dist/_dts/content-editor/src/ui/editorUiStore.d.ts +2 -2
  55. package/dist/_dts/content-editor/src/ui/stores/autosaveStore.d.ts +1 -1
  56. package/dist/_dts/content-editor/src/ui/stores/types.d.ts +1 -1
  57. package/dist/_dts/content-model/src/content-types/builtins.d.ts +3 -15
  58. package/dist/_dts/content-model/src/content-types/entryValidation.d.ts +2 -0
  59. package/dist/_dts/content-model/src/content-types/schema.d.ts +1 -1
  60. package/dist/_dts/content-model/src/templates/defaults.d.ts +1 -21
  61. package/dist/_dts/core/src/editor-state.d.ts +14 -1
  62. package/dist/_dts/core/src/footer-composition.d.ts +2 -1
  63. package/dist/_dts/core/src/index.d.ts +1 -0
  64. package/dist/_dts/core/src/rich-text.d.ts +16 -0
  65. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +189 -86
  66. package/dist/_dts/editor/src/index.d.ts +1 -1
  67. package/dist/_dts/editor/src/primitives/rich-text/utils.d.ts +3 -5
  68. package/dist/_dts/editor-blocks/src/BlockEditor.d.ts +1 -1
  69. package/dist/_dts/editor-blocks/src/block-editor/BlockCard.d.ts +2 -1
  70. package/dist/_dts/editor-blocks/src/block-editor/BlockCardHeader.d.ts +2 -1
  71. package/dist/_dts/editor-blocks/src/block-editor/blockDraftBaselineSeed.d.ts +15 -0
  72. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockBaselineSync.d.ts +3 -7
  73. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockDuplication.d.ts +12 -0
  74. package/dist/_dts/editor-blocks/src/block-editor/types.d.ts +1 -0
  75. package/dist/_dts/editor-blocks/src/block-editor/utils.d.ts +1 -1
  76. package/dist/_dts/editor-blocks/src/index.d.ts +6 -5
  77. package/dist/_dts/editor-blocks/src/widgets/rich-text/RichTextField.d.ts +2 -2
  78. package/dist/_dts/media-core/src/contentMediaAudit.d.ts +104 -0
  79. package/dist/_dts/media-core/src/contentMediaAuditFieldAdapter.d.ts +8 -0
  80. package/dist/_dts/media-core/src/contentMediaSnapshotAudit.d.ts +22 -0
  81. package/dist/_dts/media-core/src/index.d.ts +3 -1
  82. package/dist/_dts/sdk/src/cli/commands/event.d.ts +18 -1
  83. package/dist/_dts/sdk/src/cli/commands/push/execute/theme.d.ts +2 -16
  84. package/dist/_dts/sdk/src/cli/content/reader.d.ts +8 -5
  85. package/dist/_dts/sdk/src/client/management/events.d.ts +2 -1
  86. package/dist/_dts/sdk/src/client/management/index.d.ts +1 -1
  87. package/dist/_dts/sdk/src/client/management/types.d.ts +36 -9
  88. package/dist/_dts/sdk/src/config/typed-entries.d.ts +7 -7
  89. package/dist/_dts/sdk/src/config/validation.d.ts +7 -7
  90. package/dist/_dts/sdk/src/contracts/content.d.ts +5 -15
  91. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  92. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +12 -25
  93. package/dist/_dts/sdk/src/contracts/theme.d.ts +2 -18
  94. package/dist/_dts/sdk/src/manifest/passes/pass1-fields.d.ts +1 -1
  95. package/dist/_dts/sdk/src/manifest/three-pass-converter.d.ts +4 -4
  96. package/dist/_dts/sdk/src/rendering/helpers/bindings.d.ts +1 -2
  97. package/dist/_dts/sdk/src/rendering/helpers/index.d.ts +1 -1
  98. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +2 -13
  99. package/dist/_dts/sdk/src/theme/index.d.ts +0 -29
  100. package/dist/_dts/sdk/src/version.d.ts +1 -1
  101. package/dist/_dts/site-commands/src/commands.d.ts +31 -18
  102. package/dist/_dts/theme-core/src/buttons/affordanceIcons.d.ts +2 -2
  103. package/dist/_dts/theme-core/src/buttons/core.d.ts +0 -1
  104. package/dist/_dts/theme-core/src/buttons/effectClassification.d.ts +22 -9
  105. package/dist/_dts/theme-core/src/buttons/effects/presets/background.d.ts +1 -2
  106. package/dist/_dts/theme-core/src/buttons/effects/presets/border.d.ts +1 -2
  107. package/dist/_dts/theme-core/src/buttons/effects/presets/overlay.d.ts +3 -1
  108. package/dist/_dts/theme-core/src/buttons/effects/presets/shadow.d.ts +2 -2
  109. package/dist/_dts/theme-core/src/buttons/effects/types.d.ts +8 -18
  110. package/dist/_dts/theme-core/src/buttons/generateButtonCss.d.ts +2 -2
  111. package/dist/_dts/theme-core/src/buttons/generateDefaultButtonSystem.d.ts +5 -5
  112. package/dist/_dts/theme-core/src/buttons/index.d.ts +1 -4
  113. package/dist/_dts/theme-core/src/buttons/parseThemeForPersistence.d.ts +36 -0
  114. package/dist/_dts/theme-core/src/buttons/types.d.ts +1638 -93
  115. package/dist/_dts/theme-core/src/cards/types.d.ts +13 -13
  116. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +969 -69
  117. package/dist/_dts/theme-core/src/navigation/types.d.ts +2 -2
  118. package/dist/_dts/theme-core/src/recommend/generateThemes.d.ts +0 -1
  119. package/dist/_dts/theme-core/src/schema.d.ts +1100 -116
  120. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  121. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +14 -14
  122. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +2 -2
  123. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +4 -4
  124. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +6 -0
  125. package/dist/cli/index.mjs +5706 -4852
  126. package/dist/cli/init-docs/content/cli-reference.md +40 -1
  127. package/dist/cli/init-docs/content/content-management.md +6 -0
  128. package/dist/client/{DiscountCodeDisclosure-C4xvTBux.mjs → DiscountCodeDisclosure-DIWj-P9v.mjs} +1 -1
  129. package/dist/client/{EventCombined.client-Cr7G131j.mjs → EventCombined.client-De8YWBSr.mjs} +2 -2
  130. package/dist/client/{EventListing.client-CAjbseNy.mjs → EventListing.client-seHTjimY.mjs} +2 -2
  131. package/dist/client/{LeafletStaticMap-BMlWfwz3.mjs → LeafletStaticMap-BmQxFcJW.mjs} +1 -1
  132. package/dist/client/{MagicLinkForm-ChdFz96y.mjs → MagicLinkForm-OoJAIUIj.mjs} +1 -1
  133. package/dist/client/{StepIndicator-7D3GaBeM.mjs → StepIndicator-CiZ8kdPk.mjs} +2 -2
  134. package/dist/client/{accordion-D8JTs2Si.mjs → accordion-6lnN__XJ.mjs} +1 -1
  135. package/dist/client/{bookingForm-H3DCRBHs.mjs → bookingForm-5uyqhONU.mjs} +6 -6
  136. package/dist/client/bookings.mjs +2219 -527
  137. package/dist/client/{carousel-DMr_QWlz.mjs → carousel-Dtnifil_.mjs} +1 -1
  138. package/dist/client/{cart-DWMbfBdR.mjs → cart-5Tc5S7NV.mjs} +3 -3
  139. package/dist/client/{checkout-zmfdkXLx.mjs → checkout-CYKKN5Hr.mjs} +3 -3
  140. package/dist/client/client.mjs +2498 -2384
  141. package/dist/client/{colorTokenParts-FSS3al6l.mjs → colorStyles-BWpj_h4C.mjs} +88 -1
  142. package/dist/client/{courseRegistration-Z_mn18jy.mjs → courseRegistration-CxCBybB4.mjs} +6 -6
  143. package/dist/client/{eventCalendar-CYp0YJGF.mjs → eventCalendar-Xj2jZS9X.mjs} +4 -4
  144. package/dist/client/{eventCombined-rKyYZUa9.mjs → eventCombined-CgcMoOgM.mjs} +2 -2
  145. package/dist/client/{eventDetails-X0ZaUuMj.mjs → eventDetails-CEAH8Edc.mjs} +3 -3
  146. package/dist/client/{eventListing-W1SuL7EP.mjs → eventListing-qyPVQ0wP.mjs} +2 -2
  147. package/dist/client/{eventRegistration-ClWricui.mjs → eventRegistration-DDu79CtL.mjs} +6 -6
  148. package/dist/client/{form--eKPwAAD.mjs → form-Bc7JKI7I.mjs} +1 -1
  149. package/dist/client/{headerSection-BSeGr-FS.mjs → headerSection-vTku_LEl.mjs} +2 -2
  150. package/dist/client/hooks.mjs +373 -555
  151. package/dist/client/{locationMap-BpOrtNYn.mjs → locationMap-Bg7X4Jna.mjs} +3 -3
  152. package/dist/client/{media-DoF0dcVx.mjs → media-CNE1Z-wb.mjs} +1 -2
  153. package/dist/client/{newsletterForm-Bt7it7Dy.mjs → newsletterForm-lpylVdp5.mjs} +1 -1
  154. package/dist/client/{pricing-BTjSDDr4.mjs → pricing-DE8fjJiT.mjs} +1 -1
  155. package/dist/client/{productDetail-ICnVTcCg.mjs → productDetail-D7Bo2RMH.mjs} +3 -3
  156. package/dist/client/{productList-ZHr6D604.mjs → productList-VjnZqiqN.mjs} +3 -3
  157. package/dist/client/{propsDecoder-wWN0tBDF.mjs → propsDecoder-DreFXjNP.mjs} +2212 -621
  158. package/dist/client/rendering/client.mjs +21612 -21646
  159. package/dist/client/rendering/islands.mjs +20 -20
  160. package/dist/client/rendering.mjs +1571 -1547
  161. package/dist/client/{shop-C1u_BUn0.mjs → shop-IRsHHbf7.mjs} +5 -5
  162. package/dist/client/{shop-commerce-CleZdRha.mjs → shop-commerce-gnI8PieZ.mjs} +27 -10
  163. package/dist/client/{shop-commerce-store-ycnoB2Ce.mjs → shop-commerce-store-BTbrhObn.mjs} +1 -1
  164. package/dist/client/{shopShared-CPhByt2a.mjs → shopShared-D6KS2_qk.mjs} +3 -3
  165. package/dist/client/{useDiscountCodeUi-cStSQI6C.mjs → useDiscountCodeUi-DCccGCxV.mjs} +1 -1
  166. package/dist/client/{useEventFilters-DKPN7hSY.mjs → useEventFilters-eGs3rPZQ.mjs} +3 -104
  167. package/dist/client/{usePolling-BKAd6pkZ.mjs → usePolling-CvSU2sur.mjs} +2 -2
  168. package/dist/preview-next/before-render.mjs +57 -0
  169. package/dist/preview-next/client/runtime.mjs +4 -4
  170. package/dist/preview-next/middleware.mjs +57 -0
  171. package/dist/{previewAuthStore-BkT1D06A.mjs → previewAuthStore-DN7vcRsv.mjs} +57 -0
  172. package/dist/{sdk-runtime-hu3wr3ng.mjs → sdk-runtime-DaOiA7ct.mjs} +2 -2
  173. package/dist/{separator-Bln8izQ9.mjs → separator-CqRhcCxD.mjs} +1 -1
  174. package/dist/server/components.mjs +4092 -4108
  175. package/dist/server/config-template-validation.mjs +4 -23
  176. package/dist/server/config-validation.mjs +373 -555
  177. package/dist/server/config.mjs +410 -558
  178. package/dist/server/data.mjs +373 -555
  179. package/dist/server/index.mjs +58 -1
  180. package/dist/server/next.mjs +32958 -32926
  181. package/dist/server/page-converter.mjs +369 -526
  182. package/dist/server/prebuild.mjs +1 -1
  183. package/dist/server/rendering/server.mjs +5371 -3174
  184. package/dist/server/rendering.mjs +4099 -4124
  185. package/dist/server/routing.mjs +758 -966
  186. package/dist/server/server.mjs +448 -632
  187. package/dist/server/theme-bridge.mjs +472 -525
  188. package/dist/server/theme.mjs +1074 -392
  189. package/package.json +1 -1
  190. package/dist/_dts/blocks/src/primitives/content/components/Button.d.ts +0 -40
  191. package/dist/_dts/blocks/src/system/fragments/library/footerBottomText.d.ts +0 -19
  192. package/dist/_dts/media-core/src/legacyMediaValue.d.ts +0 -11
  193. package/dist/_dts/theme-core/src/styles/buttons.d.ts +0 -27
  194. package/dist/_dts/theme-core/src/styles/buttonsLibrary.d.ts +0 -2
  195. package/dist/_dts/theme-core/src/styles/glow.d.ts +0 -3
  196. package/dist/client/colorStyles-gcTh5yZT.mjs +0 -89
@@ -168,7 +168,7 @@ var init_shop_shared = __esmMin((() => {
168
168
  });
169
169
  publicMembershipProductCollectionSchema = z$1.object({ memberships: z$1.array(publicMembershipProductSchema) });
170
170
  }));
171
- var init_types$17 = __esmMin((() => {}));
171
+ var init_types$16 = __esmMin((() => {}));
172
172
  //#endregion
173
173
  //#region ../core/src/result.ts
174
174
  function ok$3(value) {
@@ -382,7 +382,7 @@ var init_event_pricing = __esmMin((() => {
382
382
  //#endregion
383
383
  //#region ../core/src/event-page-surface-subject.ts
384
384
  function parseEventPageSurfaceSubject(value) {
385
- if (!isRecord$37(value)) return err$3({ kind: "notObject" });
385
+ if (!isRecord$40(value)) return err$3({ kind: "notObject" });
386
386
  switch (value.kind) {
387
387
  case "eventSeries": {
388
388
  const eventSeriesId = parseEventSeriesId(value.eventSeriesId);
@@ -425,7 +425,7 @@ function parseEventPageSurfaceSubject(value) {
425
425
  });
426
426
  }
427
427
  }
428
- function isRecord$37(value) {
428
+ function isRecord$40(value) {
429
429
  return typeof value === "object" && value !== null && !Array.isArray(value);
430
430
  }
431
431
  var init_event_page_surface_subject = __esmMin((() => {
@@ -839,8 +839,40 @@ var init_public_url_validation = __esmMin((() => {}));
839
839
  var init_public_profile = __esmMin((() => {
840
840
  init_public_url_validation();
841
841
  init_result();
842
+ }));
843
+ //#endregion
844
+ //#region ../core/src/rich-text.ts
845
+ function isRecord$38(value) {
846
+ return typeof value === "object" && value !== null && !Array.isArray(value);
847
+ }
848
+ function hasOnlyKeys(value, allowed) {
849
+ return Object.keys(value).every((key) => allowed.has(key));
850
+ }
851
+ function isDenseArrayOf(value, predicate) {
852
+ for (let index = 0; index < value.length; index += 1) if (!(index in value) || !predicate(value[index])) return false;
853
+ return true;
854
+ }
855
+ function isTipTapMark(value) {
856
+ return isRecord$38(value) && hasOnlyKeys(value, markKeys) && typeof value.type === "string" && value.type.length > 0 && (value.attrs === void 0 || isRecord$38(value.attrs));
857
+ }
858
+ function isTipTapNode(value) {
859
+ if (!isRecord$38(value) || !hasOnlyKeys(value, nodeKeys) || typeof value.type !== "string" || value.type.length === 0 || value.type === "doc" || value.attrs !== void 0 && !isRecord$38(value.attrs) || value.marks !== void 0 && (!Array.isArray(value.marks) || !isDenseArrayOf(value.marks, isTipTapMark)) || value.content !== void 0 && (!Array.isArray(value.content) || !isDenseArrayOf(value.content, isTipTapNode))) return false;
860
+ if (value.type === "text") return typeof value.text === "string" && value.text.length > 0 && value.content === void 0;
861
+ return value.text === void 0;
862
+ }
863
+ var nodeKeys, markKeys;
864
+ var init_rich_text = __esmMin((() => {
865
+ nodeKeys = /* @__PURE__ */ new Set([
866
+ "type",
867
+ "content",
868
+ "text",
869
+ "attrs",
870
+ "marks"
871
+ ]);
872
+ markKeys = /* @__PURE__ */ new Set(["type", "attrs"]);
842
873
  })), sectionKinds;
843
874
  var init_footer_composition = __esmMin((() => {
875
+ init_rich_text();
844
876
  init_result();
845
877
  sectionKinds = [
846
878
  "brand",
@@ -953,6 +985,7 @@ var init_src$3 = __esmMin((() => {
953
985
  init_participant_identity();
954
986
  init_public_url_validation();
955
987
  init_public_profile();
988
+ init_rich_text();
956
989
  init_footer_composition();
957
990
  init_number_input();
958
991
  init_field_errors();
@@ -1096,11 +1129,11 @@ var init_storagePathDomain = __esmMin((() => {}));
1096
1129
  var init_fileAssetLegacyProjection = __esmMin((() => {}));
1097
1130
  //#endregion
1098
1131
  //#region ../media-core/src/typeGuards.ts
1099
- function isRecord$33(value) {
1132
+ function isRecord$35(value) {
1100
1133
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1101
1134
  }
1102
1135
  function asRecord$3(value) {
1103
- return isRecord$33(value) ? value : void 0;
1136
+ return isRecord$35(value) ? value : void 0;
1104
1137
  }
1105
1138
  var init_typeGuards$1 = __esmMin((() => {}));
1106
1139
  function isFileJsonObject(value) {
@@ -1110,7 +1143,7 @@ function isFileJsonValue(value) {
1110
1143
  if (value === null || typeof value === "string" || typeof value === "boolean") return true;
1111
1144
  if (typeof value === "number") return Number.isFinite(value);
1112
1145
  if (Array.isArray(value)) return value.every(isFileJsonValue);
1113
- return isRecord$33(value) && isFileJsonObject(value);
1146
+ return isRecord$35(value) && isFileJsonObject(value);
1114
1147
  }
1115
1148
  var init_filePersistence = __esmMin((() => {
1116
1149
  init_src$3();
@@ -1475,7 +1508,7 @@ function isCmsMediaObjectType(value) {
1475
1508
  return typeof value === "string" && CMS_MEDIA_OBJECT_TYPES.includes(value);
1476
1509
  }
1477
1510
  function isCmsMediaObject(value) {
1478
- if (!isRecord$33(value)) return false;
1511
+ if (!isRecord$35(value)) return false;
1479
1512
  const type = value.type;
1480
1513
  if (!isCmsMediaObjectType(type)) return false;
1481
1514
  if ("attrs" in value) return false;
@@ -1486,13 +1519,25 @@ function collectMediaObjects(value, out, location = "") {
1486
1519
  value.forEach((item, index) => collectMediaObjects(item, out, `${location}[${index}]`));
1487
1520
  return;
1488
1521
  }
1489
- if (!isRecord$33(value)) return;
1522
+ if (!isRecord$35(value)) return;
1490
1523
  if (isCmsMediaObject(value)) out.push({
1491
1524
  obj: value,
1492
1525
  location
1493
1526
  });
1494
1527
  for (const [key, child] of Object.entries(value)) collectMediaObjects(child, out, location ? `${location}.${key}` : key);
1495
1528
  }
1529
+ /**
1530
+ * Strip environment-specific keys from a CMS media object.
1531
+ * @mutates Mutates the passed object in-place.
1532
+ */
1533
+ function deleteEnvSpecificKeys(obj) {
1534
+ delete obj.assetId;
1535
+ delete obj.storageBucket;
1536
+ delete obj.storagePath;
1537
+ delete obj.src;
1538
+ delete obj.mimeType;
1539
+ if (looksLikeUuid(obj.id)) delete obj.id;
1540
+ }
1496
1541
  var CMS_MEDIA_OBJECT_TYPES;
1497
1542
  var init_mediaRefNormalizer = __esmMin((() => {
1498
1543
  init_typeGuards$1();
@@ -1520,8 +1565,8 @@ function walkAndCollectImageAttrs(value, out, location = "") {
1520
1565
  value.forEach((item, index) => walkAndCollectImageAttrs(item, out, `${location}[${index}]`));
1521
1566
  return;
1522
1567
  }
1523
- if (!isRecord$33(value)) return;
1524
- if (value.type === "image" && isRecord$33(value.attrs)) out.push({
1568
+ if (!isRecord$35(value)) return;
1569
+ if (value.type === "image" && isRecord$35(value.attrs)) out.push({
1525
1570
  attrs: value.attrs,
1526
1571
  location: location ? `${location}.attrs` : "attrs"
1527
1572
  });
@@ -1580,25 +1625,239 @@ function mediaIdentifierOrFallback(current, fallback) {
1580
1625
  }
1581
1626
  var init_renderableMediaAsset = __esmMin((() => {}));
1582
1627
  //#endregion
1583
- //#region ../media-core/src/legacyMediaValue.ts
1584
- function legacyStringMediaTypeForField(field) {
1585
- if (field.type === "file") return field.fileKinds?.[0] ?? "document";
1586
- return field.mediaKinds?.[0] ?? "image";
1628
+ //#region ../media-core/src/contentMediaAudit.ts
1629
+ function isRecord$34(value) {
1630
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1587
1631
  }
1588
- function normalizeLegacyStringMediaValueForField(value, field) {
1589
- if (typeof value !== "string") return value;
1590
- const src = value.trim();
1591
- if (!src) return value;
1592
- return {
1593
- type: legacyStringMediaTypeForField(field),
1594
- src
1595
- };
1632
+ function summarizeContentMediaValue(value) {
1633
+ if (typeof value === "string") {
1634
+ const trimmed = value.trim();
1635
+ if (!trimmed) return "blank string";
1636
+ return trimmed.length <= 160 ? trimmed : `${trimmed.slice(0, 157)}...`;
1637
+ }
1638
+ if (value === null) return "null";
1639
+ if (Array.isArray(value)) return `array(${value.length})`;
1640
+ if (isRecord$34(value)) return `object(${Object.keys(value).sort().join(",")})`;
1641
+ return typeof value;
1596
1642
  }
1597
- var init_legacyMediaValue = __esmMin((() => {}));
1643
+ function auditMediaValue(context, path, allowedMediaTypes, value) {
1644
+ const expectedMediaType = allowedMediaTypes[0];
1645
+ if (value === void 0 || value === null) return [];
1646
+ if (typeof value === "string") {
1647
+ if (!value.trim()) return [{
1648
+ tag: "blank-string",
1649
+ context,
1650
+ path,
1651
+ valueSummary: summarizeContentMediaValue(value),
1652
+ expectedMediaType
1653
+ }];
1654
+ return [{
1655
+ tag: "legacy-string",
1656
+ context,
1657
+ path,
1658
+ valueSummary: summarizeContentMediaValue(value),
1659
+ originalValue: value,
1660
+ expectedMediaType,
1661
+ allowedMediaTypes
1662
+ }];
1663
+ }
1664
+ const hasValidIdentifier = isRecord$34(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0;
1665
+ const hasValidExplicitType = isRecord$34(value) && (!("type" in value) || typeof value.type === "string" && allowedMediaTypes.includes(value.type));
1666
+ const hasRetiredEnvironmentField = isRecord$34(value) && ("assetId" in value || "storagePath" in value || "storageBucket" in value || "src" in value || "mimeType" in value || looksLikeUuid(value.id));
1667
+ if (hasValidIdentifier && hasValidExplicitType && !hasRetiredEnvironmentField) return [];
1668
+ return [{
1669
+ tag: "malformed-object",
1670
+ context,
1671
+ path,
1672
+ valueSummary: summarizeContentMediaValue(value),
1673
+ expectedMediaType
1674
+ }];
1675
+ }
1676
+ function auditFields(context, fields, value, parentPath) {
1677
+ if (!isRecord$34(value)) return [];
1678
+ const findings = [];
1679
+ for (const field of fields) {
1680
+ if (!(field.id in value)) continue;
1681
+ const fieldValue = value[field.id];
1682
+ const fieldPath = [...parentPath, field.id];
1683
+ switch (field.kind) {
1684
+ case "media":
1685
+ findings.push(...auditMediaValue(context, fieldPath, field.allowedMediaTypes, fieldValue));
1686
+ break;
1687
+ case "object":
1688
+ findings.push(...auditFields(context, field.fields, fieldValue, fieldPath));
1689
+ break;
1690
+ case "repeater":
1691
+ if (!Array.isArray(fieldValue)) break;
1692
+ for (const [index, item] of fieldValue.entries()) {
1693
+ const itemPath = [...fieldPath, index];
1694
+ if (!isRecord$34(item)) {
1695
+ findings.push({
1696
+ tag: "missing-schema",
1697
+ context,
1698
+ path: itemPath,
1699
+ valueSummary: summarizeContentMediaValue(item)
1700
+ });
1701
+ continue;
1702
+ }
1703
+ if (field.mode === "fields") {
1704
+ findings.push(...auditFields(context, field.fields, item, itemPath));
1705
+ continue;
1706
+ }
1707
+ const discriminator = field.discriminator ?? "_type";
1708
+ const typeId = item[discriminator];
1709
+ const variant = typeof typeId === "string" ? field.variants.find((candidate) => candidate.typeId === typeId) : void 0;
1710
+ if (variant) findings.push(...auditFields(context, variant.fields, item, itemPath));
1711
+ else findings.push({
1712
+ tag: "missing-schema",
1713
+ context,
1714
+ path: [...itemPath, discriminator],
1715
+ valueSummary: summarizeContentMediaValue(typeId)
1716
+ });
1717
+ }
1718
+ break;
1719
+ }
1720
+ }
1721
+ return findings;
1722
+ }
1723
+ var init_contentMediaAudit = __esmMin((() => {
1724
+ init_mediaRefNormalizer();
1725
+ }));
1726
+ //#endregion
1727
+ //#region ../media-core/src/contentMediaAuditFieldAdapter.ts
1728
+ function isRecord$33(value) {
1729
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1730
+ }
1731
+ function unknownArray(value) {
1732
+ return Array.isArray(value) ? value : null;
1733
+ }
1734
+ function fieldsArray(value) {
1735
+ const directFields = unknownArray(value);
1736
+ if (directFields) return directFields;
1737
+ if (!isRecord$33(value)) return null;
1738
+ return unknownArray(value.fields);
1739
+ }
1740
+ function nestedFields$1(field) {
1741
+ const directFields = unknownArray(field.fields);
1742
+ if (directFields) return directFields;
1743
+ return fieldsArray(field.schema) ?? [];
1744
+ }
1745
+ function allowedMediaTypes(field, fieldType, defaultFileKinds) {
1746
+ const configured = fieldType === "media" ? field.mediaKinds : field.fileKinds;
1747
+ const parsed = Array.isArray(configured) ? configured.filter((value) => typeof value === "string" && contentMediaTypes.has(value)) : [];
1748
+ if (parsed.length > 0) return parsed;
1749
+ return fieldType === "media" ? ["image"] : defaultFileKinds;
1750
+ }
1751
+ function mergeVariants(variants) {
1752
+ const merged = /* @__PURE__ */ new Map();
1753
+ for (const variant of variants) merged.set(variant.typeId, mergeFields([...merged.get(variant.typeId) ?? [], ...variant.fields]));
1754
+ return [...merged].map(([typeId, fields]) => ({
1755
+ typeId,
1756
+ fields
1757
+ }));
1758
+ }
1759
+ function mergeFields(fields) {
1760
+ const merged = [];
1761
+ for (const field of fields) {
1762
+ const index = merged.findIndex((candidate) => candidate.kind === field.kind && candidate.id === field.id);
1763
+ const existing = index === -1 ? void 0 : merged[index];
1764
+ if (!existing) {
1765
+ merged.push(field);
1766
+ continue;
1767
+ }
1768
+ if (existing.kind === "media" && field.kind === "media") {
1769
+ merged[index] = {
1770
+ ...existing,
1771
+ allowedMediaTypes: [.../* @__PURE__ */ new Set([...existing.allowedMediaTypes, ...field.allowedMediaTypes])]
1772
+ };
1773
+ continue;
1774
+ }
1775
+ if (existing.kind === "object" && field.kind === "object") {
1776
+ merged[index] = {
1777
+ ...existing,
1778
+ fields: mergeFields([...existing.fields, ...field.fields])
1779
+ };
1780
+ continue;
1781
+ }
1782
+ if (existing.kind === "repeater" && field.kind === "repeater" && existing.mode === "fields" && field.mode === "fields") {
1783
+ merged[index] = {
1784
+ ...existing,
1785
+ fields: mergeFields([...existing.fields, ...field.fields])
1786
+ };
1787
+ continue;
1788
+ }
1789
+ if (existing.kind === "repeater" && field.kind === "repeater" && existing.mode === "variants" && field.mode === "variants") {
1790
+ merged[index] = {
1791
+ ...existing,
1792
+ variants: mergeVariants([...existing.variants, ...field.variants])
1793
+ };
1794
+ continue;
1795
+ }
1796
+ merged.push(field);
1797
+ }
1798
+ return merged;
1799
+ }
1800
+ function adaptFields(rawFields, flattenRepeaterGroups = false, defaultFileKinds = ["document"]) {
1801
+ return mergeFields(rawFields.flatMap((rawField) => {
1802
+ if (!isRecord$33(rawField)) return [];
1803
+ const { id, type } = rawField;
1804
+ if (typeof id !== "string" || !id || typeof type !== "string") return [];
1805
+ if (type === "media" || type === "file") return [{
1806
+ kind: "media",
1807
+ id,
1808
+ allowedMediaTypes: allowedMediaTypes(rawField, type, defaultFileKinds)
1809
+ }];
1810
+ if (flattenRepeaterGroups && type === "group" && isRecord$33(rawField.ui) && rawField.ui.flattenInRepeater === true) return adaptFields(nestedFields$1(rawField), false, defaultFileKinds);
1811
+ if (type === "group" || type === "modal") return [{
1812
+ kind: "object",
1813
+ id,
1814
+ fields: adaptFields(nestedFields$1(rawField), false, defaultFileKinds)
1815
+ }];
1816
+ if (type === "tabGroup") return [{
1817
+ kind: "object",
1818
+ id,
1819
+ fields: adaptFields((unknownArray(rawField.tabs) ?? []).flatMap((tab) => isRecord$33(tab) ? unknownArray(tab.fields) ?? [] : []), false, defaultFileKinds)
1820
+ }];
1821
+ if (type !== "repeater") return [];
1822
+ if (isRecord$33(rawField.itemTypes)) return [{
1823
+ kind: "repeater",
1824
+ id,
1825
+ mode: "variants",
1826
+ variants: Object.entries(rawField.itemTypes).flatMap(([typeId, itemType]) => isRecord$33(itemType) && Array.isArray(itemType.fields) ? [{
1827
+ typeId,
1828
+ fields: adaptFields(itemType.fields, true, defaultFileKinds)
1829
+ }] : []),
1830
+ discriminator: "_type"
1831
+ }];
1832
+ return [{
1833
+ kind: "repeater",
1834
+ id,
1835
+ mode: "fields",
1836
+ fields: adaptFields(nestedFields$1(rawField), true, defaultFileKinds)
1837
+ }];
1838
+ }));
1839
+ }
1840
+ var contentMediaTypes;
1841
+ var init_contentMediaAuditFieldAdapter = __esmMin((() => {
1842
+ contentMediaTypes = /* @__PURE__ */ new Set([
1843
+ "image",
1844
+ "video",
1845
+ "audio",
1846
+ "document",
1847
+ "spreadsheet",
1848
+ "archive"
1849
+ ]);
1850
+ }));
1851
+ var init_contentMediaSnapshotAudit = __esmMin((() => {
1852
+ init_src$3();
1853
+ init_contentMediaAudit();
1854
+ init_contentMediaAuditFieldAdapter();
1855
+ init_identifiers();
1856
+ }));
1598
1857
  //#endregion
1599
1858
  //#region ../media-core/src/index.ts
1600
1859
  var init_src$2 = __esmMin((() => {
1601
- init_types$17();
1860
+ init_types$16();
1602
1861
  init_fileDomain();
1603
1862
  init_fileStorage();
1604
1863
  init_storagePathDomain();
@@ -1644,7 +1903,9 @@ var init_src$2 = __esmMin((() => {
1644
1903
  init_mediaUsageProjection();
1645
1904
  init_portableMediaReferences();
1646
1905
  init_renderableMediaAsset();
1647
- init_legacyMediaValue();
1906
+ init_contentMediaAudit();
1907
+ init_contentMediaAuditFieldAdapter();
1908
+ init_contentMediaSnapshotAudit();
1648
1909
  })), PUBLIC_DOWNLOAD_MEDIA_TYPES, hotspotSchema$1, rotationSchema$1, rectSchema$1, aspectCropSchema$1, transformSchema$1, mediaPosterSchema, mediaBaseSchema, imageMediaSchema, videoMediaSchema, audioMediaSchema, documentMediaSchema, spreadsheetMediaSchema, archiveMediaSchema, mediaSchema$1;
1649
1910
  var init_media$2 = __esmMin((() => {
1650
1911
  init_src$2();
@@ -1746,123 +2007,85 @@ function isObjectRecord$1(value) {
1746
2007
  return typeof value === "object" && value !== null;
1747
2008
  }
1748
2009
  var init_typeGuards = __esmMin((() => {}));
2010
+ //#endregion
2011
+ //#region ../blocks/src/system/manifest/richTextSchema.ts
1749
2012
  /**
1750
- * Permissive unwrap for a value known to be the root of a rich-text field.
2013
+ * Type guard for {@link RichTextDoc}.
1751
2014
  *
1752
- * Producers should emit the canonical unwrapped form `{ type: 'doc', content: [...] }`,
1753
- * but two legacy shapes still appear in older data and pre-Phase-1 fixtures:
1754
- *
1755
- * - `{ doc: <typed-doc> }` — pre-migration DB storage.
1756
- * - `{ doc: <typed-doc>, html, plain }` — pre-Phase-1 dummy-content output.
1757
- *
1758
- * This helper is the single right place to tolerate either shape at a known
1759
- * rich-text root (transforms, coercion, validators that operate on rich text).
1760
- * The strictness sits on the inner node's `type === 'doc'`, which prevents
1761
- * misidentifying random objects that happen to carry a field named `doc`.
1762
- *
1763
- * Returns the inner doc node when the input matches a legacy shape, otherwise
1764
- * returns the input unchanged.
1765
- *
1766
- * For deep structural traversal where any nested key might be named `doc`
1767
- * (and unwrapping would be incorrect), use the strict `isDocWrapper` private
1768
- * helper inside `@riverbankcms/blocks/bindings`.
2015
+ * Returns `true` only for the canonical unwrapped form. The shared structural
2016
+ * contract lives in `@riverbankcms/core`; parity tests keep its dependency-free
2017
+ * guard aligned with this detailed Zod validator.
1769
2018
  */
1770
- function unwrapRichTextRoot(value) {
1771
- if (!isRecord$32(value)) return value;
1772
- if (!("doc" in value)) return value;
1773
- const inner = value.doc;
1774
- if (isRecord$32(inner) && inner.type === "doc") return inner;
1775
- return value;
1776
- }
1777
- function createHardBreakNode() {
1778
- return { type: "hardBreak" };
2019
+ function isRichTextDoc(value) {
2020
+ return RichTextDocSchema.safeParse(value).success;
1779
2021
  }
1780
2022
  /**
1781
- * Parse a small markdown subset into TipTap inline nodes.
2023
+ * Build a canonical {@link RichTextDoc} from plain-text paragraphs.
1782
2024
  *
1783
- * This intentionally supports only bold (`**text**`) because the SDK/content
1784
- * import paths use it for lightweight default content, not full markdown.
1785
- */
1786
- function parseRichTextInlineMarkdown(text) {
1787
- const out = [];
1788
- const pushText = (value, marks) => {
1789
- if (!value) return;
1790
- out.push(marks && marks.length > 0 ? {
1791
- type: "text",
1792
- text: value,
1793
- marks
1794
- } : {
1795
- type: "text",
1796
- text: value
1797
- });
1798
- };
1799
- let rest = text;
1800
- while (rest.length > 0) {
1801
- const start = rest.indexOf("**");
1802
- if (start === -1) {
1803
- pushText(rest);
1804
- break;
1805
- }
1806
- const end = rest.indexOf("**", start + 2);
1807
- if (end === -1) {
1808
- pushText(rest);
1809
- break;
1810
- }
1811
- pushText(rest.slice(0, start));
1812
- pushText(rest.slice(start + 2, end), [{ type: "bold" }]);
1813
- rest = rest.slice(end + 2);
1814
- }
1815
- return out;
1816
- }
1817
- function parseRichTextParagraphInlineMarkdown(text) {
1818
- return text.split("\n").flatMap((line, index) => {
1819
- const lineNodes = parseRichTextInlineMarkdown(line.trim());
1820
- return index === 0 ? lineNodes : [createHardBreakNode(), ...lineNodes];
1821
- });
1822
- }
1823
- /**
1824
- * Convert a plain string with a tiny markdown subset to canonical TipTap JSON.
2025
+ * Single source of truth for constructing a TipTap doc from strings.
2026
+ * Used by:
2027
+ * - `@riverbankcms/dummy-content` (fixture body generators)
2028
+ * - `@riverbankcms/content-model` (default template `cta-full` content)
1825
2029
  *
1826
- * Double newlines create semantic paragraphs. Single newlines remain inside the
1827
- * current paragraph as TipTap `hardBreak` nodes for line-oriented content such
1828
- * as addresses, opening hours, and contact details.
2030
+ * Empty strings produce a paragraph with no `content` array — matching
2031
+ * the TipTap convention for empty paragraphs. The shape is exactly what
2032
+ * {@link RichTextDocSchema} validates, so the type and runtime check
2033
+ * never drift from the constructor.
1829
2034
  */
1830
- function createRichTextDocFromMarkdown(markdown) {
1831
- const normalized = markdown.replace(/\r\n/g, "\n").trim();
1832
- if (!normalized) return {
1833
- type: "doc",
1834
- content: []
1835
- };
2035
+ function createRichTextDoc(paragraphs) {
1836
2036
  return {
1837
2037
  type: "doc",
1838
- content: normalized.split(/\n{2,}/).map((paragraph) => paragraph.trim()).filter(Boolean).map((paragraph) => {
1839
- const inline = parseRichTextParagraphInlineMarkdown(paragraph);
1840
- return inline.length > 0 ? {
1841
- type: "paragraph",
1842
- content: inline
1843
- } : { type: "paragraph" };
1844
- })
2038
+ content: paragraphs.map((text) => text ? {
2039
+ type: "paragraph",
2040
+ content: [{
2041
+ type: "text",
2042
+ text
2043
+ }]
2044
+ } : { type: "paragraph" })
1845
2045
  };
1846
2046
  }
1847
2047
  var TipTapMarkSchema, TipTapNodeSchema, RichTextDocSchema;
1848
2048
  var init_richTextSchema = __esmMin((() => {
1849
2049
  init_typeGuards();
1850
2050
  TipTapMarkSchema = z$1.object({
1851
- type: z$1.string(),
2051
+ type: z$1.string().min(1),
1852
2052
  attrs: z$1.record(z$1.string(), z$1.unknown()).optional()
1853
- });
2053
+ }).strict();
1854
2054
  TipTapNodeSchema = z$1.lazy(() => z$1.object({
1855
- type: z$1.string(),
2055
+ type: z$1.string().min(1),
1856
2056
  content: z$1.array(TipTapNodeSchema).optional(),
1857
2057
  text: z$1.string().optional(),
1858
2058
  attrs: z$1.record(z$1.string(), z$1.unknown()).optional(),
1859
2059
  marks: z$1.array(TipTapMarkSchema).optional()
2060
+ }).strict().superRefine((node, ctx) => {
2061
+ if (node.type === "doc") ctx.addIssue({
2062
+ code: "custom",
2063
+ path: ["type"],
2064
+ message: "Document nodes are only valid at the rich-text root"
2065
+ });
2066
+ if (node.type === "text") {
2067
+ if (node.text === void 0 || node.text.length === 0) ctx.addIssue({
2068
+ code: "custom",
2069
+ path: ["text"],
2070
+ message: "Text nodes require a non-empty text value"
2071
+ });
2072
+ if (node.content !== void 0) ctx.addIssue({
2073
+ code: "custom",
2074
+ path: ["content"],
2075
+ message: "Text nodes cannot contain child nodes"
2076
+ });
2077
+ return;
2078
+ }
2079
+ if (node.text !== void 0) ctx.addIssue({
2080
+ code: "custom",
2081
+ path: ["text"],
2082
+ message: "Only text nodes can contain a text value"
2083
+ });
1860
2084
  }));
1861
- z$1.union([TipTapNodeSchema, z$1.object({ doc: TipTapNodeSchema })]);
1862
2085
  RichTextDocSchema = z$1.object({
1863
2086
  type: z$1.literal("doc"),
1864
2087
  content: z$1.array(TipTapNodeSchema)
1865
- });
2088
+ }).strict();
1866
2089
  }));
1867
2090
  //#endregion
1868
2091
  //#region ../blocks/src/system/blocks/shared/bookingPaymentTerms.ts
@@ -2450,7 +2673,7 @@ function validateProps(props, context = "node builder") {
2450
2673
  else console.error(message);
2451
2674
  }
2452
2675
  var RESERVED_KEYS;
2453
- var init_types$16 = __esmMin((() => {
2676
+ var init_types$15 = __esmMin((() => {
2454
2677
  init_env();
2455
2678
  RESERVED_KEYS = [
2456
2679
  "children",
@@ -2555,7 +2778,7 @@ function devValidate(node) {
2555
2778
  var section$1, headerSection, stack, inline, accordion, accordionItem, carousel, text, richText, icon, media, fileDownload, videoEmbed, spinner, button, link, form, bookingForm, eventRegistration, shop, productList, productDetail, cart, checkout, gifting, courseRegistration;
2556
2779
  var init_builder$2 = __esmMin((() => {
2557
2780
  init_schema$2();
2558
- init_types$16();
2781
+ init_types$15();
2559
2782
  init_env();
2560
2783
  section$1 = (props, children, ...mods) => el("section", props, children, ...mods);
2561
2784
  headerSection = (props, children, ...mods) => el("headerSection", props, children, ...mods);
@@ -3673,7 +3896,7 @@ function createTransformRegistry(definitions) {
3673
3896
  }
3674
3897
  return registry;
3675
3898
  }
3676
- var init_core$2 = __esmMin((() => {}));
3899
+ var init_core$1 = __esmMin((() => {}));
3677
3900
  //#endregion
3678
3901
  //#region ../theme-core/src/palette/utils/colorConversion.ts
3679
3902
  function hexToRgb$1(hex) {
@@ -3977,7 +4200,7 @@ function defineBlockDesignCapability(capability) {
3977
4200
  };
3978
4201
  }
3979
4202
  var plainFrame, tileTreatmentValues, eventListTreatmentValues, eyebrowTreatmentValues, visibleHeadingDecorationFamilyValues, headingDecorationOverrideValues, headingDecorationColorTokenValues, noHeadingDecoration, accordionIconSetValues, accordionIconColorTokenValues, accordionIconMotionValues;
3980
- var init_types$15 = __esmMin((() => {
4203
+ var init_types$14 = __esmMin((() => {
3981
4204
  plainFrame = { kind: "plain" };
3982
4205
  tileTreatmentValues = [
3983
4206
  "default",
@@ -4042,14 +4265,14 @@ function defineBlockAppearancePreset(preset) {
4042
4265
  return preset;
4043
4266
  }
4044
4267
  var init_appearancePresetResolution = __esmMin((() => {
4045
- init_types$15();
4268
+ init_types$14();
4046
4269
  }));
4047
4270
  //#endregion
4048
4271
  //#region ../theme-core/src/site-styles/blockAppearancePresets.ts
4049
4272
  var blockAppearancePresets;
4050
4273
  var init_blockAppearancePresets = __esmMin((() => {
4051
4274
  init_appearancePresetResolution();
4052
- init_types$15();
4275
+ init_types$14();
4053
4276
  blockAppearancePresets = [
4054
4277
  defineBlockAppearancePreset({
4055
4278
  kind: "block-design",
@@ -4862,168 +5085,10 @@ function assertUniqueLayoutProfileKeys(profiles) {
4862
5085
  if (duplicates.length > 0) throw new Error(`Duplicate block layout design profiles: ${duplicates.join(", ")}`);
4863
5086
  }
4864
5087
  var init_blockFlow = __esmMin((() => {
4865
- init_types$15();
5088
+ init_types$14();
4866
5089
  }));
4867
5090
  var init_classic_professional = __esmMin((() => {}));
4868
- //#endregion
4869
- //#region ../theme-core/src/mock-themes/modern-minimal.ts
4870
- var modernMinimalTheme;
4871
- var init_modern_minimal = __esmMin((() => {
4872
- modernMinimalTheme = {
4873
- name: "Modern Minimal",
4874
- description: "Clean, monochromatic design with subtle styling and modern typography",
4875
- rationale: "Perfect for professional services, portfolios, and content-focused sites that need a clean, trustworthy appearance",
4876
- axes: {
4877
- tone: "minimal",
4878
- energy: "calm",
4879
- density: "airy",
4880
- mediaBias: "text",
4881
- conversionBias: "low",
4882
- motion: "subtle"
4883
- },
4884
- palette: {
4885
- colors: [
4886
- {
4887
- name: "primary",
4888
- hex: "#1f2937"
4889
- },
4890
- {
4891
- name: "primaryForeground",
4892
- hex: "#ffffff"
4893
- },
4894
- {
4895
- name: "secondary",
4896
- hex: "#6b7280"
4897
- },
4898
- {
4899
- name: "accent",
4900
- hex: "#3b82f6"
4901
- },
4902
- {
4903
- name: "success",
4904
- hex: "#10b981"
4905
- },
4906
- {
4907
- name: "warning",
4908
- hex: "#f59e0b"
4909
- },
4910
- {
4911
- name: "destructive",
4912
- hex: "#ef4444"
4913
- },
4914
- {
4915
- name: "background",
4916
- hex: "#ffffff"
4917
- },
4918
- {
4919
- name: "surface",
4920
- hex: "#ffffff"
4921
- },
4922
- {
4923
- name: "surfaceAlt",
4924
- hex: "#f9fafb"
4925
- },
4926
- {
4927
- name: "muted",
4928
- hex: "#f3f4f6"
4929
- },
4930
- {
4931
- name: "text",
4932
- hex: "#1f2937"
4933
- },
4934
- {
4935
- name: "border",
4936
- hex: "#e5e7eb"
4937
- }
4938
- ],
4939
- meta: { mode: "light" }
4940
- },
4941
- typography: {
4942
- headings: {
4943
- default: {
4944
- family: "Inter, system-ui, sans-serif",
4945
- weight: "medium",
4946
- letterSpacing: "tight",
4947
- lineHeight: "normal",
4948
- case: "normal",
4949
- italic: null
4950
- },
4951
- h1: {
4952
- weight: "semibold",
4953
- letterSpacing: "tight",
4954
- size: "xl",
4955
- lineHeight: "normal",
4956
- italic: null
4957
- },
4958
- h2: {
4959
- weight: "medium",
4960
- letterSpacing: "normal",
4961
- size: "lg",
4962
- lineHeight: "normal",
4963
- italic: null
4964
- },
4965
- h3: {
4966
- weight: "medium",
4967
- letterSpacing: "normal",
4968
- size: "md",
4969
- lineHeight: "normal",
4970
- italic: null
4971
- }
4972
- },
4973
- body: {
4974
- family: "Inter, system-ui, sans-serif",
4975
- weight: "regular",
4976
- size: "md",
4977
- letterSpacing: "normal",
4978
- lineHeight: "relaxed"
4979
- },
4980
- scale: "minorThird"
4981
- },
4982
- space: "comfortable",
4983
- corners: "rounded",
4984
- border: {
4985
- width: "hairline",
4986
- style: "solid"
4987
- },
4988
- shadow: {
4989
- elevation: "low",
4990
- softness: "soft",
4991
- position: "bottom"
4992
- },
4993
- motion: {
4994
- level: "low",
4995
- easing: "standard"
4996
- },
4997
- header: {
4998
- variant: "classic",
4999
- positioning: "static",
5000
- shrinkOnScroll: false,
5001
- maxWidth: "container",
5002
- logoOverride: null,
5003
- background: {
5004
- type: "color",
5005
- color: "surface"
5006
- },
5007
- navStyle: "minimal",
5008
- navWeight: "medium",
5009
- navContainer: {
5010
- type: "none",
5011
- opacity: 0,
5012
- tint: null
5013
- }
5014
- },
5015
- footer: {
5016
- variant: "simple",
5017
- maxWidth: "container",
5018
- background: {
5019
- type: "color",
5020
- color: "surface"
5021
- },
5022
- navStyle: "minimal",
5023
- navWeight: "medium"
5024
- }
5025
- };
5026
- }));
5091
+ var init_modern_minimal = __esmMin((() => {}));
5027
5092
  var init_vibrant_creative = __esmMin((() => {}));
5028
5093
  //#endregion
5029
5094
  //#region ../theme-core/src/palette/generateCssVars.ts
@@ -5349,7 +5414,7 @@ var init_all = __esmMin((() => {
5349
5414
  }));
5350
5415
  //#endregion
5351
5416
  //#region ../theme-core/src/palette/types.ts
5352
- var init_types$14 = __esmMin((() => {}));
5417
+ var init_types$13 = __esmMin((() => {}));
5353
5418
  //#endregion
5354
5419
  //#region ../../node_modules/.pnpm/@vibrant+image@4.0.4/node_modules/@vibrant/image/dist/esm/histogram.js
5355
5420
  var Histogram;
@@ -6763,7 +6828,7 @@ function defineCuratedPalette(input) {
6763
6828
  };
6764
6829
  }
6765
6830
  var curatedPaletteAuthoringKeySchema;
6766
- var init_types$13 = __esmMin((() => {
6831
+ var init_types$12 = __esmMin((() => {
6767
6832
  init_styleTags();
6768
6833
  init_themeColorTokens();
6769
6834
  init_colors();
@@ -6804,7 +6869,7 @@ var init_types$13 = __esmMin((() => {
6804
6869
  //#region ../theme-core/src/palette/curated/brand-led.ts
6805
6870
  var brandLedCool, brandLedWarm, brandLedJewel, brandLedSpruce, brandLedBurgundyGold;
6806
6871
  var init_brand_led = __esmMin((() => {
6807
- init_types$13();
6872
+ init_types$12();
6808
6873
  brandLedCool = defineCuratedPalette({
6809
6874
  authoringKey: "brand-led-cool",
6810
6875
  name: "Brand-led — Cool",
@@ -7135,7 +7200,7 @@ var init_brand_led = __esmMin((() => {
7135
7200
  //#region ../theme-core/src/palette/curated/high-contrast.ts
7136
7201
  var highContrastInk, highContrastCharcoal, highContrastMonochrome;
7137
7202
  var init_high_contrast = __esmMin((() => {
7138
- init_types$13();
7203
+ init_types$12();
7139
7204
  highContrastInk = defineCuratedPalette({
7140
7205
  authoringKey: "high-contrast-ink",
7141
7206
  name: "High Contrast — Ink",
@@ -7336,7 +7401,7 @@ var init_high_contrast = __esmMin((() => {
7336
7401
  //#region ../theme-core/src/palette/curated/soft-natural.ts
7337
7402
  var softNaturalSage, softNaturalStone, softNaturalWatercolor, softNaturalLilac;
7338
7403
  var init_soft_natural = __esmMin((() => {
7339
- init_types$13();
7404
+ init_types$12();
7340
7405
  softNaturalSage = defineCuratedPalette({
7341
7406
  authoringKey: "soft-natural-sage",
7342
7407
  name: "Soft Natural — Sage",
@@ -7602,7 +7667,7 @@ var init_soft_natural = __esmMin((() => {
7602
7667
  //#region ../theme-core/src/palette/curated/warm-neutral.ts
7603
7668
  var warmNeutralClay, warmNeutralCream, warmNeutralSoftEarth, warmNeutralForest;
7604
7669
  var init_warm_neutral = __esmMin((() => {
7605
- init_types$13();
7670
+ init_types$12();
7606
7671
  warmNeutralClay = defineCuratedPalette({
7607
7672
  authoringKey: "warm-neutral-clay",
7608
7673
  name: "Warm Neutral — Clay",
@@ -7868,7 +7933,7 @@ var init_warm_neutral = __esmMin((() => {
7868
7933
  //#region ../theme-core/src/palette/curated/warm-dark.ts
7869
7934
  var warmDarkEmber, warmDarkKirtanGold;
7870
7935
  var init_warm_dark = __esmMin((() => {
7871
- init_types$13();
7936
+ init_types$12();
7872
7937
  warmDarkEmber = defineCuratedPalette({
7873
7938
  authoringKey: "warm-dark-ember",
7874
7939
  name: "Warm Dark — Ember",
@@ -8034,7 +8099,7 @@ var init_curated = __esmMin((() => {
8034
8099
  init_soft_natural();
8035
8100
  init_warm_neutral();
8036
8101
  init_warm_dark();
8037
- init_types$13();
8102
+ init_types$12();
8038
8103
  curatedPalettes = [
8039
8104
  admitCuratedPaletteDefinition(brandLedCool),
8040
8105
  admitCuratedPaletteDefinition(brandLedWarm),
@@ -8166,7 +8231,7 @@ var init_palette$1 = __esmMin((() => {
8166
8231
  init_generateCssVars();
8167
8232
  init_generateShades();
8168
8233
  init_neutralScale();
8169
- init_types$14();
8234
+ init_types$13();
8170
8235
  init_utils$6();
8171
8236
  init_paletteAccess();
8172
8237
  init_extractPalette();
@@ -8407,7 +8472,7 @@ var init_backgroundAppearance = __esmMin((() => {
8407
8472
  backgroundAppearanceColorTokenNameSet = new Set(backgroundAppearanceColorTokenNames);
8408
8473
  }));
8409
8474
  var init_compositionBudget = __esmMin((() => {
8410
- init_types$15();
8475
+ init_types$14();
8411
8476
  }));
8412
8477
  var init_schemaVersionedJson = __esmMin((() => {}));
8413
8478
  //#endregion
@@ -8425,7 +8490,7 @@ function boundaryRuntimeMetricsForTransition(transition) {
8425
8490
  }
8426
8491
  var sectionBoundaryRuntimeMetricsByTransition, noneBoundaryOption, softBoundaryOption, spaciousBoundaryOption;
8427
8492
  var init_sectionTransitions = __esmMin((() => {
8428
- init_types$15();
8493
+ init_types$14();
8429
8494
  sectionBoundaryRuntimeMetricsByTransition = {
8430
8495
  none: null,
8431
8496
  "soft-fade-short": boundaryRuntimeMetrics("gradient", "medium", "6rem + 2px"),
@@ -8514,7 +8579,7 @@ function boundaryKey$1(previousBlockId, nextBlockId) {
8514
8579
  }
8515
8580
  var pageDesignUserPinnedAppearanceChoiceSchema, pageDesignUserPinnedBoundaryChoiceSchema, pageDesignBoundaryTreatmentSchema, pageDesignUserPinnedBoundaryTreatmentChoiceSchema, pageDesignBlockLookPinSchema, pageDesignBoundaryPinSchema, legacyPageDesignBoundaryPinSchema;
8516
8581
  var init_pageDesignIntent = __esmMin((() => {
8517
- init_types$15();
8582
+ init_types$14();
8518
8583
  init_schemaVersionedJson();
8519
8584
  init_sectionTransitions();
8520
8585
  asBoundaryOptionId("break:unknown");
@@ -8619,109 +8684,7 @@ var init_pageDesignIntent = __esmMin((() => {
8619
8684
  var init_contentTemplateDesignIntent = __esmMin((() => {
8620
8685
  init_pageDesignIntent();
8621
8686
  init_sectionTransitions();
8622
- init_types$15();
8623
- }));
8624
- //#endregion
8625
- //#region ../theme-core/src/buttons/effectClassification.ts
8626
- function getButtonEffectPolicy(effectId) {
8627
- return Object.prototype.hasOwnProperty.call(BUTTON_EFFECT_POLICIES, effectId) ? BUTTON_EFFECT_POLICIES[effectId] : null;
8628
- }
8629
- function classifyButtonEffectId(effectId) {
8630
- return getButtonEffectPolicy(effectId)?.classification ?? "unknown";
8631
- }
8632
- var BUTTON_EFFECT_POLICIES;
8633
- var init_effectClassification = __esmMin((() => {
8634
- BUTTON_EFFECT_POLICIES = {
8635
- "darken-background": {
8636
- classification: "colour-transition",
8637
- transition: {
8638
- kind: "adjust-shade",
8639
- direction: "darker"
8640
- }
8641
- },
8642
- "lighten-background": {
8643
- classification: "colour-transition",
8644
- transition: {
8645
- kind: "adjust-shade",
8646
- direction: "lighter"
8647
- }
8648
- },
8649
- "background-on-hover": {
8650
- classification: "colour-transition",
8651
- transition: { kind: "replace-background" }
8652
- },
8653
- "background-on-hover-alpha": {
8654
- classification: "unsupported-colour",
8655
- reason: "alpha_requires_surface"
8656
- },
8657
- "fade-background": {
8658
- classification: "unsupported-colour",
8659
- reason: "changes_element_opacity"
8660
- },
8661
- "opacity-reduce": {
8662
- classification: "unsupported-colour",
8663
- reason: "changes_element_opacity"
8664
- },
8665
- "gradient-text": {
8666
- classification: "unsupported-colour",
8667
- reason: "changes_foreground"
8668
- },
8669
- "text-color-change": {
8670
- classification: "unsupported-colour",
8671
- reason: "changes_foreground"
8672
- },
8673
- "border-color-change": {
8674
- classification: "unsupported-colour",
8675
- reason: "changes_border"
8676
- },
8677
- "aurora-glow": {
8678
- classification: "unsupported-background-overlay",
8679
- reason: "unresolved_background_overlay"
8680
- },
8681
- "cosmic-stardust": {
8682
- classification: "unsupported-background-overlay",
8683
- reason: "unresolved_background_overlay"
8684
- },
8685
- "gradient-border-fill": {
8686
- classification: "unsupported-background-overlay",
8687
- reason: "unresolved_background_overlay"
8688
- },
8689
- "animated-gradient-border-fill": {
8690
- classification: "unsupported-background-overlay",
8691
- reason: "unresolved_background_overlay"
8692
- },
8693
- "nav-underline": {
8694
- classification: "not-button",
8695
- reason: "not_button_effect"
8696
- },
8697
- "nav-frosted-base": {
8698
- classification: "not-button",
8699
- reason: "not_button_effect"
8700
- },
8701
- "nav-frosted-hover": {
8702
- classification: "not-button",
8703
- reason: "not_button_effect"
8704
- },
8705
- "nav-frosted-active": {
8706
- classification: "not-button",
8707
- reason: "not_button_effect"
8708
- },
8709
- "animated-gradient": { classification: "non-colour" },
8710
- "border-glow": { classification: "non-colour" },
8711
- "drop-shadow": { classification: "non-colour" },
8712
- "inner-shadow": { classification: "non-colour" },
8713
- "neumorphic-shadow": { classification: "non-colour" },
8714
- "outer-glow": { classification: "non-colour" },
8715
- "retro-3d-shadow": { classification: "non-colour" },
8716
- "scale-up": { classification: "non-colour" },
8717
- "scale-down": { classification: "non-colour" },
8718
- lift: { classification: "non-colour" },
8719
- "press-down": { classification: "non-colour" },
8720
- pop: { classification: "non-colour" },
8721
- "border-bottom-grow": { classification: "non-colour" },
8722
- "sketchy-border": { classification: "non-colour" },
8723
- "pulse-animation": { classification: "non-colour" }
8724
- };
8687
+ init_types$14();
8725
8688
  }));
8726
8689
  //#endregion
8727
8690
  //#region ../theme-core/src/buttons/types.ts
@@ -8729,8 +8692,9 @@ function isVariantRole(value) {
8729
8692
  return VARIANT_ROLES.includes(value);
8730
8693
  }
8731
8694
  function asSpecialVariantId(value) {
8732
- if (value.length === 0) throw new Error("SpecialVariantId must be a non-empty string");
8733
- return value;
8695
+ const parsed = buttonVariantIdSchema.safeParse(value);
8696
+ if (!parsed.success) throw new Error("SpecialVariantId must be a CSS-safe button variant ID");
8697
+ return parsed.data;
8734
8698
  }
8735
8699
  function parseOpaqueButtonColourToken(value) {
8736
8700
  const token = parseThemeColorToken(value);
@@ -8741,144 +8705,8 @@ function parseOpaqueButtonColourToken(value) {
8741
8705
  function unsafeAsOpaqueButtonColourToken(value) {
8742
8706
  return value;
8743
8707
  }
8744
- function normalizeLegacyButtonVariantInput(input) {
8745
- if (!isUnknownRecord(input)) return input;
8746
- const background = input.background;
8747
- if (!isUnknownRecord(background)) return input;
8748
- if (!(background.type === "transparent" || Object.prototype.hasOwnProperty.call(input, "textColorToken") || isUnknownRecord(input.border) && Object.prototype.hasOwnProperty.call(input.border, "colorToken") || hasLegacyButtonEffects(input.effects) || hasLegacyAlphaToken(input, background))) return input;
8749
- const { textColorToken, ...withoutLegacyText } = input;
8750
- const normalizedBorder = isUnknownRecord(input.border) && typeof input.border.widthClass === "string" ? { widthClass: input.border.widthClass } : input.border;
8751
- const normalizedAffordance = isUnknownRecord(input.affordance) && typeof input.affordance.iconId === "string" ? { iconId: input.affordance.iconId } : input.affordance;
8752
- const normalizedEffects = normalizeLegacyButtonEffects(input.effects, background.type);
8753
- const hoverBackgroundToken = stripLegacyTokenAlpha(input.hoverBackgroundToken);
8754
- if (background.type === "transparent") return {
8755
- ...withoutLegacyText,
8756
- background: {
8757
- type: "surface",
8758
- role: "link"
8759
- },
8760
- border: normalizedBorder,
8761
- affordance: normalizedAffordance,
8762
- effects: normalizedEffects,
8763
- hoverBackgroundToken
8764
- };
8765
- if (background.type === "gradient") {
8766
- const stops = Array.isArray(background.stops) ? background.stops.map(stripLegacyTokenAlpha) : background.stops;
8767
- return {
8768
- ...withoutLegacyText,
8769
- background: {
8770
- ...background,
8771
- stops,
8772
- foregroundToken: stripLegacyTokenAlpha(background.foregroundToken ?? textColorToken)
8773
- },
8774
- border: normalizedBorder,
8775
- affordance: normalizedAffordance,
8776
- effects: normalizedEffects,
8777
- hoverBackgroundToken
8778
- };
8779
- }
8780
- return {
8781
- ...withoutLegacyText,
8782
- background: background.type === "solid" ? {
8783
- ...background,
8784
- colorToken: stripLegacyTokenAlpha(background.colorToken)
8785
- } : background,
8786
- border: normalizedBorder,
8787
- affordance: normalizedAffordance,
8788
- effects: normalizedEffects,
8789
- hoverBackgroundToken
8790
- };
8791
- }
8792
- function normalizeLegacyButtonGlobalInput(input) {
8793
- if (!isUnknownRecord(input)) return input;
8794
- const hoverColorToken = stripLegacyTokenAlpha(input.hoverColorToken);
8795
- return hoverColorToken === input.hoverColorToken ? input : {
8796
- ...input,
8797
- hoverColorToken
8798
- };
8799
- }
8800
- function hasLegacyAlphaToken(input, background) {
8801
- const stops = isUnknownArray$4(background.stops) ? background.stops : [];
8802
- return [
8803
- input.hoverBackgroundToken,
8804
- background.colorToken,
8805
- background.foregroundToken,
8806
- ...stops
8807
- ].some((token) => stripLegacyTokenAlpha(token) !== token);
8808
- }
8809
- function stripLegacyTokenAlpha(value) {
8810
- if (typeof value !== "string") return value;
8811
- const slashIndex = value.indexOf("/");
8812
- return slashIndex < 0 ? value : value.slice(0, slashIndex);
8813
- }
8814
- function hasLegacyButtonEffects(input) {
8815
- if (!isUnknownRecord(input)) return false;
8816
- return [
8817
- "base",
8818
- "hover",
8819
- "active",
8820
- "focus"
8821
- ].some((state) => {
8822
- const effects = input[state];
8823
- return Array.isArray(effects) && effects.some((effect) => {
8824
- if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return true;
8825
- const classification = classifyButtonEffectId(effect.effectId);
8826
- if (classification !== "colour-transition" && classification !== "non-colour") return true;
8827
- return effect.effectId === "background-on-hover" && isUnknownRecord(effect.options) && stripLegacyTokenAlpha(effect.options.colorToken) !== effect.options.colorToken;
8828
- });
8829
- });
8830
- }
8831
- function normalizeLegacyButtonEffects(input, backgroundType) {
8832
- if (!isUnknownRecord(input)) return input;
8833
- const normalized = {};
8834
- for (const state of [
8835
- "base",
8836
- "hover",
8837
- "active",
8838
- "focus"
8839
- ]) {
8840
- const effects = input[state];
8841
- if (!Array.isArray(effects)) continue;
8842
- const safeEffects = effects.flatMap((effect) => {
8843
- if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return [];
8844
- const classification = classifyButtonEffectId(effect.effectId);
8845
- if (classification === "non-colour") return [effect];
8846
- if (classification !== "colour-transition") return [];
8847
- if (effect.effectId === "background-on-hover") {
8848
- const options = isUnknownRecord(effect.options) ? effect.options : null;
8849
- const colorToken = options === null ? null : parseOpaqueButtonColourToken(stripLegacyTokenAlpha(options.colorToken));
8850
- return colorToken === null ? [] : [{
8851
- ...effect,
8852
- options: {
8853
- ...options,
8854
- colorToken
8855
- }
8856
- }];
8857
- }
8858
- if (backgroundType !== "solid") return [];
8859
- const rawAmount = isUnknownRecord(effect.options) ? effect.options.amount : void 0;
8860
- const amount = typeof rawAmount === "number" && rawAmount > 0 ? Math.max(100, Math.round(rawAmount / 100) * 100) : 100;
8861
- return [{
8862
- ...effect,
8863
- options: {
8864
- ...isUnknownRecord(effect.options) ? effect.options : {},
8865
- amount
8866
- }
8867
- }];
8868
- });
8869
- if (safeEffects.length > 0) normalized[state] = safeEffects;
8870
- }
8871
- return Object.keys(normalized).length === 0 ? void 0 : normalized;
8872
- }
8873
- function isUnknownRecord(value) {
8874
- return typeof value === "object" && value !== null && !Array.isArray(value);
8875
- }
8876
- function isUnknownArray$4(value) {
8877
- return Array.isArray(value);
8878
- }
8879
- var VARIANT_ROLES, DEFAULT_VARIANT_ALIAS_ID, opaqueButtonColourTokenSchema, cornerStyleSchema, shadowSizeSchema, textTransformSchema, fontWeightSchema, buttonTypographySchema, letterSpacingSchema, hoverTransformSchema, hoverColorSchema, buttonPaddingPresetSchema, gradientStyleSchema, gradientSharpnessSchema, prioritySchema, buttonSizeNameSchema, PADDING_TOKEN_PATTERN, paddingShorthandSchema, buttonSizeConfigSchema, buttonSizesSchema, canonicalButtonGlobalSettingsSchema, buttonGlobalSettingsSchema, gradientDirectionSchema$1, buttonColourTreatmentSchema, buttonBackgroundSchema, effectApplicationSchema$1, buttonBorderSchema, buttonVariantAffordanceSchema, variantShadowSchema, variantEffectsSchema, variantSizeOverridesSchema, canonicalButtonVariantSchema, buttonVariantSchema, buttonSystemSchema;
8880
- var init_types$12 = __esmMin((() => {
8881
- init_effectClassification();
8708
+ var VARIANT_ROLES, buttonVariantIdSchema, DEFAULT_VARIANT_ALIAS_ID, opaqueButtonColourTokenSchema, buttonShadeAmountSchema, buttonShadowIntensitySchema, buttonEffectApplicationSchema, cornerStyleSchema, shadowSizeSchema, textTransformSchema, fontWeightSchema, buttonTypographySchema, letterSpacingSchema, hoverTransformSchema, buttonPaddingPresetSchema, gradientStyleSchema, gradientSharpnessSchema, prioritySchema, buttonSizeNameSchema, PADDING_TOKEN_PATTERN, paddingShorthandSchema, buttonSizeConfigSchema, buttonSizesSchema, buttonGlobalSettingsBaseSchema, buttonGlobalSettingsSchema, gradientDirectionSchema$1, buttonColourTreatmentSchema, buttonBackgroundSchema, buttonBorderSchema, buttonVariantAffordanceSchema, variantShadowSchema, variantEffectsSchema, variantSizeOverridesSchema, buttonVariantSchema, buttonSystemSchema;
8709
+ var init_types$11 = __esmMin((() => {
8882
8710
  init_themeColorTokens();
8883
8711
  init_colorTokenParts();
8884
8712
  VARIANT_ROLES = [
@@ -8890,12 +8718,109 @@ var init_types$12 = __esmMin((() => {
8890
8718
  "tertiary",
8891
8719
  "accent"
8892
8720
  ];
8721
+ buttonVariantIdSchema = z$1.string().min(1).max(50).regex(/^[a-z0-9-]+$/, "Button variant IDs may contain lowercase letters, numbers, and hyphens only");
8893
8722
  DEFAULT_VARIANT_ALIAS_ID = "default";
8894
8723
  opaqueButtonColourTokenSchema = themeColorTokenSchema.refine((value) => parseOpaqueButtonColourToken(value) !== null, "Button colour tokens must be opaque").transform((value) => {
8895
8724
  const token = parseOpaqueButtonColourToken(value);
8896
8725
  if (token === null) throw new Error("Invariant: opaque button token passed its schema");
8897
8726
  return token;
8898
8727
  });
8728
+ buttonShadeAmountSchema = z$1.number().finite().min(0).max(900).multipleOf(100);
8729
+ buttonShadowIntensitySchema = z$1.enum([
8730
+ "subtle",
8731
+ "medium",
8732
+ "strong"
8733
+ ]);
8734
+ buttonEffectApplicationSchema = z$1.discriminatedUnion("effectId", [
8735
+ z$1.strictObject({
8736
+ effectId: z$1.literal("darken-background"),
8737
+ options: z$1.strictObject({ amount: buttonShadeAmountSchema.optional() }).optional()
8738
+ }),
8739
+ z$1.strictObject({
8740
+ effectId: z$1.literal("lighten-background"),
8741
+ options: z$1.strictObject({ amount: buttonShadeAmountSchema.optional() }).optional()
8742
+ }),
8743
+ z$1.strictObject({
8744
+ effectId: z$1.literal("background-on-hover"),
8745
+ options: z$1.strictObject({ colorToken: opaqueButtonColourTokenSchema.optional() }).optional()
8746
+ }),
8747
+ z$1.strictObject({
8748
+ effectId: z$1.literal("animated-gradient"),
8749
+ options: z$1.strictObject({ direction: z$1.enum(["horizontal", "vertical"]).optional() }).optional()
8750
+ }),
8751
+ z$1.strictObject({
8752
+ effectId: z$1.literal("drop-shadow"),
8753
+ options: z$1.strictObject({ intensity: buttonShadowIntensitySchema.optional() }).optional()
8754
+ }),
8755
+ z$1.strictObject({
8756
+ effectId: z$1.literal("inner-shadow"),
8757
+ options: z$1.strictObject({}).optional()
8758
+ }),
8759
+ z$1.strictObject({
8760
+ effectId: z$1.literal("neumorphic-shadow"),
8761
+ options: z$1.strictObject({
8762
+ mode: z$1.enum(["raised", "pressed"]).optional(),
8763
+ intensity: buttonShadowIntensitySchema.optional()
8764
+ }).optional()
8765
+ }),
8766
+ z$1.strictObject({
8767
+ effectId: z$1.literal("retro-3d-shadow"),
8768
+ options: z$1.strictObject({}).optional()
8769
+ }),
8770
+ z$1.strictObject({
8771
+ effectId: z$1.literal("scale-up"),
8772
+ options: z$1.strictObject({ scale: z$1.enum([
8773
+ "1.02",
8774
+ "1.03",
8775
+ "1.05",
8776
+ "1.10"
8777
+ ]).optional() }).optional()
8778
+ }),
8779
+ z$1.strictObject({
8780
+ effectId: z$1.literal("scale-down"),
8781
+ options: z$1.strictObject({ scale: z$1.enum([
8782
+ "0.90",
8783
+ "0.95",
8784
+ "0.97",
8785
+ "0.98"
8786
+ ]).optional() }).optional()
8787
+ }),
8788
+ z$1.strictObject({
8789
+ effectId: z$1.literal("lift"),
8790
+ options: z$1.strictObject({ amount: z$1.enum([
8791
+ "0.25",
8792
+ "0.5",
8793
+ "1"
8794
+ ]).optional() }).optional()
8795
+ }),
8796
+ z$1.strictObject({
8797
+ effectId: z$1.literal("press-down"),
8798
+ options: z$1.strictObject({}).optional()
8799
+ }),
8800
+ z$1.strictObject({
8801
+ effectId: z$1.literal("pop"),
8802
+ options: z$1.strictObject({ intensity: z$1.enum([
8803
+ "subtle",
8804
+ "medium",
8805
+ "dramatic"
8806
+ ]).optional() }).optional()
8807
+ }),
8808
+ z$1.strictObject({
8809
+ effectId: z$1.literal("border-bottom-grow"),
8810
+ options: z$1.strictObject({
8811
+ height: z$1.union([z$1.literal(1), z$1.literal(2)]).optional(),
8812
+ origin: z$1.enum([
8813
+ "left",
8814
+ "center",
8815
+ "right"
8816
+ ]).optional()
8817
+ }).optional()
8818
+ }),
8819
+ z$1.strictObject({
8820
+ effectId: z$1.literal("sketchy-border"),
8821
+ options: z$1.strictObject({ intensity: buttonShadowIntensitySchema.optional() }).optional()
8822
+ })
8823
+ ]);
8899
8824
  cornerStyleSchema = z$1.enum([
8900
8825
  "square",
8901
8826
  "rounded",
@@ -8930,13 +8855,7 @@ var init_types$12 = __esmMin((() => {
8930
8855
  "scale",
8931
8856
  "press"
8932
8857
  ]);
8933
- hoverColorSchema = z$1.enum([
8934
- "none",
8935
- "brighten",
8936
- "darken",
8937
- "saturate",
8938
- "token"
8939
- ]);
8858
+ z$1.enum(["none", "token"]);
8940
8859
  buttonPaddingPresetSchema = z$1.enum([
8941
8860
  "compact",
8942
8861
  "default",
@@ -8984,7 +8903,7 @@ var init_types$12 = __esmMin((() => {
8984
8903
  lg,
8985
8904
  xl: xl ?? lg
8986
8905
  }));
8987
- canonicalButtonGlobalSettingsSchema = z$1.object({
8906
+ buttonGlobalSettingsBaseSchema = z$1.strictObject({
8988
8907
  cornerStyle: cornerStyleSchema,
8989
8908
  cornerRadius: z$1.number().min(0).max(24).optional(),
8990
8909
  /**
@@ -8997,30 +8916,23 @@ var init_types$12 = __esmMin((() => {
8997
8916
  shadow: shadowSizeSchema.optional(),
8998
8917
  textTransform: textTransformSchema,
8999
8918
  fontWeight: fontWeightSchema,
9000
- /**
9001
- * @deprecated Set per-variant via `variant.border.widthClass` instead.
9002
- * Schema field is preserved so existing presets parse; the value is unread
9003
- * by the current generator and will be removed once the legacy
9004
- * `primitives/content/components/Button.tsx` slot exports are deleted.
9005
- */
9006
- borderWidth: z$1.union([
9007
- z$1.literal(1),
9008
- z$1.literal(2),
9009
- z$1.literal(3)
9010
- ]),
9011
8919
  typography: buttonTypographySchema.optional(),
9012
8920
  fontSize: z$1.string().optional(),
9013
8921
  letterSpacing: letterSpacingSchema.optional(),
9014
8922
  italic: z$1.boolean().optional(),
9015
8923
  paddingPreset: buttonPaddingPresetSchema.optional(),
9016
8924
  hoverTransform: hoverTransformSchema,
9017
- hoverColor: hoverColorSchema,
9018
- hoverColorToken: opaqueButtonColourTokenSchema.optional(),
9019
8925
  gradientStyle: gradientStyleSchema.optional(),
9020
8926
  gradientAngle: z$1.number().min(0).max(360).optional(),
9021
8927
  gradientSharpness: gradientSharpnessSchema.optional()
9022
8928
  });
9023
- buttonGlobalSettingsSchema = z$1.preprocess(normalizeLegacyButtonGlobalInput, canonicalButtonGlobalSettingsSchema);
8929
+ buttonGlobalSettingsSchema = z$1.discriminatedUnion("hoverColor", [buttonGlobalSettingsBaseSchema.extend({
8930
+ hoverColor: z$1.literal("none"),
8931
+ hoverColorToken: z$1.never().optional()
8932
+ }), buttonGlobalSettingsBaseSchema.extend({
8933
+ hoverColor: z$1.literal("token"),
8934
+ hoverColorToken: opaqueButtonColourTokenSchema
8935
+ })]);
9024
8936
  gradientDirectionSchema$1 = z$1.enum([
9025
8937
  "to-r",
9026
8938
  "to-l",
@@ -9032,11 +8944,11 @@ var init_types$12 = __esmMin((() => {
9032
8944
  "to-bl"
9033
8945
  ]);
9034
8946
  buttonColourTreatmentSchema = z$1.discriminatedUnion("type", [
9035
- z$1.object({
8947
+ z$1.strictObject({
9036
8948
  type: z$1.literal("solid"),
9037
8949
  colorToken: opaqueButtonColourTokenSchema
9038
8950
  }),
9039
- z$1.object({
8951
+ z$1.strictObject({
9040
8952
  type: z$1.literal("gradient"),
9041
8953
  stops: z$1.array(opaqueButtonColourTokenSchema).min(2).transform((stops) => {
9042
8954
  const [first, second, ...rest] = stops;
@@ -9050,19 +8962,15 @@ var init_types$12 = __esmMin((() => {
9050
8962
  direction: gradientDirectionSchema$1,
9051
8963
  foregroundToken: opaqueButtonColourTokenSchema
9052
8964
  }),
9053
- z$1.object({
8965
+ z$1.strictObject({
9054
8966
  type: z$1.literal("surface"),
9055
8967
  role: z$1.literal("link")
9056
8968
  })
9057
8969
  ]);
9058
8970
  buttonBackgroundSchema = buttonColourTreatmentSchema;
9059
- effectApplicationSchema$1 = z$1.object({
9060
- effectId: z$1.string(),
9061
- options: z$1.record(z$1.string(), z$1.json()).optional()
9062
- });
9063
- buttonBorderSchema = z$1.object({ widthClass: z$1.string() });
9064
- buttonVariantAffordanceSchema = z$1.object({ iconId: z$1.string().min(1) });
9065
- variantShadowSchema = z$1.object({
8971
+ buttonBorderSchema = z$1.strictObject({ widthClass: z$1.string() });
8972
+ buttonVariantAffordanceSchema = z$1.strictObject({ iconId: z$1.string().min(1) });
8973
+ variantShadowSchema = z$1.strictObject({
9066
8974
  elevation: z$1.enum([
9067
8975
  "none",
9068
8976
  "low",
@@ -9075,13 +8983,13 @@ var init_types$12 = __esmMin((() => {
9075
8983
  "hard"
9076
8984
  ]).nullable(),
9077
8985
  position: z$1.enum(["bottom", "bottom-right"]).default("bottom"),
9078
- colorToken: themeColorTokenSchema.optional()
8986
+ colorToken: opaqueButtonColourTokenSchema.optional()
9079
8987
  });
9080
- variantEffectsSchema = z$1.object({
9081
- base: z$1.array(effectApplicationSchema$1).optional(),
9082
- hover: z$1.array(effectApplicationSchema$1).optional(),
9083
- active: z$1.array(effectApplicationSchema$1).optional(),
9084
- focus: z$1.array(effectApplicationSchema$1).optional()
8988
+ variantEffectsSchema = z$1.strictObject({
8989
+ base: z$1.array(buttonEffectApplicationSchema).optional(),
8990
+ hover: z$1.array(buttonEffectApplicationSchema).optional(),
8991
+ active: z$1.array(buttonEffectApplicationSchema).optional(),
8992
+ focus: z$1.array(buttonEffectApplicationSchema).optional()
9085
8993
  });
9086
8994
  variantSizeOverridesSchema = z$1.object({
9087
8995
  sm: buttonSizeConfigSchema.optional(),
@@ -9089,8 +8997,8 @@ var init_types$12 = __esmMin((() => {
9089
8997
  lg: buttonSizeConfigSchema.optional(),
9090
8998
  xl: buttonSizeConfigSchema.optional()
9091
8999
  });
9092
- canonicalButtonVariantSchema = z$1.object({
9093
- id: z$1.string(),
9000
+ buttonVariantSchema = z$1.strictObject({
9001
+ id: buttonVariantIdSchema,
9094
9002
  name: z$1.string().min(1).max(20),
9095
9003
  enabled: z$1.boolean(),
9096
9004
  priority: prioritySchema,
@@ -9104,11 +9012,24 @@ var init_types$12 = __esmMin((() => {
9104
9012
  sizes: variantSizeOverridesSchema.optional(),
9105
9013
  affordance: buttonVariantAffordanceSchema.optional()
9106
9014
  });
9107
- buttonVariantSchema = z$1.preprocess(normalizeLegacyButtonVariantInput, canonicalButtonVariantSchema);
9108
- buttonSystemSchema = z$1.object({
9015
+ buttonSystemSchema = z$1.strictObject({
9109
9016
  global: buttonGlobalSettingsSchema,
9110
9017
  variants: z$1.array(buttonVariantSchema).min(1).max(6),
9111
9018
  sizes: buttonSizesSchema.optional()
9019
+ }).superRefine((system, ctx) => {
9020
+ const seen = /* @__PURE__ */ new Set();
9021
+ system.variants.forEach((variant, index) => {
9022
+ if (seen.has(variant.id)) ctx.addIssue({
9023
+ code: "custom",
9024
+ path: [
9025
+ "variants",
9026
+ index,
9027
+ "id"
9028
+ ],
9029
+ message: `Duplicate button variant ID: ${variant.id}`
9030
+ });
9031
+ seen.add(variant.id);
9032
+ });
9112
9033
  });
9113
9034
  }));
9114
9035
  //#endregion
@@ -9192,7 +9113,7 @@ function rolePriority(role) {
9192
9113
  }
9193
9114
  var PUBLIC_BUTTON_ROLES;
9194
9115
  var init_domain = __esmMin((() => {
9195
- init_types$12();
9116
+ init_types$11();
9196
9117
  PUBLIC_BUTTON_ROLES = [
9197
9118
  "primary",
9198
9119
  "secondary",
@@ -9248,7 +9169,7 @@ var init_selection = __esmMin((() => {
9248
9169
  var quietArrowRecipe;
9249
9170
  var init_quietArrow = __esmMin((() => {
9250
9171
  init_domain();
9251
- init_types$12();
9172
+ init_types$11();
9252
9173
  init_selection();
9253
9174
  quietArrowRecipe = defineButtonStyleRecipe({
9254
9175
  id: "quiet-arrow",
@@ -9278,7 +9199,6 @@ var init_quietArrow = __esmMin((() => {
9278
9199
  cornerStyle: "rounded",
9279
9200
  textTransform: "uppercase",
9280
9201
  fontWeight: 500,
9281
- borderWidth: 1,
9282
9202
  typography: "body",
9283
9203
  fontSize: "text-sm",
9284
9204
  letterSpacing: "loose",
@@ -9342,7 +9262,7 @@ var init_quietArrow = __esmMin((() => {
9342
9262
  affordance: { iconId: "ArrowRight" },
9343
9263
  effects: { hover: [{
9344
9264
  effectId: "background-on-hover",
9345
- options: { colorToken: "surfaceAlt" }
9265
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
9346
9266
  }] }
9347
9267
  }
9348
9268
  },
@@ -9438,7 +9358,7 @@ function normalizeAppearanceOverrides(overrides) {
9438
9358
  }
9439
9359
  var emptyParametersSchema;
9440
9360
  var init_defineStaticButtonStyleRecipe = __esmMin((() => {
9441
- init_types$12();
9361
+ init_types$11();
9442
9362
  init_domain();
9443
9363
  emptyParametersSchema = z$1.object({}).strict();
9444
9364
  }));
@@ -9447,6 +9367,7 @@ var init_defineStaticButtonStyleRecipe = __esmMin((() => {
9447
9367
  var softPillRecipe, earthPillRecipe, confidentChipRecipe, editorialSizes, editorialLinkRecipe, pebbleRecipe, brushedWashRecipe, inkStampRecipe, flatInkRecipe, showtimePillRecipe, joyStampRecipe, ornateGhostSizes, ornateRitualRecipe;
9448
9368
  var init_staticRecipes = __esmMin((() => {
9449
9369
  init_defineStaticButtonStyleRecipe();
9370
+ init_types$11();
9450
9371
  softPillRecipe = defineStaticButtonStyleRecipe({
9451
9372
  id: "soft-pill",
9452
9373
  accessibilityLabel: "Rounded pill buttons with a gentle hover lift",
@@ -9462,7 +9383,6 @@ var init_staticRecipes = __esmMin((() => {
9462
9383
  shadow: "low",
9463
9384
  textTransform: "none",
9464
9385
  fontWeight: 500,
9465
- borderWidth: 2,
9466
9386
  hoverTransform: "lift",
9467
9387
  hoverColor: "none"
9468
9388
  },
@@ -9481,10 +9401,7 @@ var init_staticRecipes = __esmMin((() => {
9481
9401
  position: "bottom"
9482
9402
  },
9483
9403
  effects: {
9484
- hover: [{
9485
- effectId: "lift",
9486
- options: { translateY: "-1px" }
9487
- }],
9404
+ hover: [{ effectId: "lift" }],
9488
9405
  active: [{
9489
9406
  effectId: "scale-down",
9490
9407
  options: { scale: "0.98" }
@@ -9503,7 +9420,7 @@ var init_staticRecipes = __esmMin((() => {
9503
9420
  border: { widthClass: "border-2" },
9504
9421
  effects: { hover: [{
9505
9422
  effectId: "background-on-hover",
9506
- options: { colorToken: "surface" }
9423
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
9507
9424
  }] }
9508
9425
  }
9509
9426
  },
@@ -9518,7 +9435,7 @@ var init_staticRecipes = __esmMin((() => {
9518
9435
  border: { widthClass: "border" },
9519
9436
  effects: { hover: [{
9520
9437
  effectId: "background-on-hover",
9521
- options: { colorToken: "surface" }
9438
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
9522
9439
  }] }
9523
9440
  }
9524
9441
  },
@@ -9532,7 +9449,7 @@ var init_staticRecipes = __esmMin((() => {
9532
9449
  borderRadius: "rounded-full",
9533
9450
  effects: { hover: [{
9534
9451
  effectId: "background-on-hover",
9535
- options: { colorToken: "surface" }
9452
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
9536
9453
  }] }
9537
9454
  }
9538
9455
  },
@@ -9562,7 +9479,6 @@ var init_staticRecipes = __esmMin((() => {
9562
9479
  shadow: "low",
9563
9480
  textTransform: "none",
9564
9481
  fontWeight: 500,
9565
- borderWidth: 1,
9566
9482
  hoverTransform: "lift",
9567
9483
  hoverColor: "none",
9568
9484
  typography: "body",
@@ -9598,12 +9514,9 @@ var init_staticRecipes = __esmMin((() => {
9598
9514
  position: "bottom"
9599
9515
  },
9600
9516
  effects: {
9601
- hover: [{
9602
- effectId: "lift",
9603
- options: { translateY: "-1px" }
9604
- }, {
9517
+ hover: [{ effectId: "lift" }, {
9605
9518
  effectId: "background-on-hover",
9606
- options: { colorToken: "accent" }
9519
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("accent") }
9607
9520
  }],
9608
9521
  active: [{
9609
9522
  effectId: "scale-down",
@@ -9623,7 +9536,7 @@ var init_staticRecipes = __esmMin((() => {
9623
9536
  border: { widthClass: "border" },
9624
9537
  effects: { hover: [{
9625
9538
  effectId: "background-on-hover",
9626
- options: { colorToken: "muted" }
9539
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("muted") }
9627
9540
  }] }
9628
9541
  }
9629
9542
  },
@@ -9638,7 +9551,7 @@ var init_staticRecipes = __esmMin((() => {
9638
9551
  border: { widthClass: "border" },
9639
9552
  effects: { hover: [{
9640
9553
  effectId: "background-on-hover",
9641
- options: { colorToken: "surfaceAlt" }
9554
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
9642
9555
  }] }
9643
9556
  }
9644
9557
  },
@@ -9698,7 +9611,6 @@ var init_staticRecipes = __esmMin((() => {
9698
9611
  shadow: "medium",
9699
9612
  textTransform: "none",
9700
9613
  fontWeight: 600,
9701
- borderWidth: 1,
9702
9614
  hoverTransform: "none",
9703
9615
  hoverColor: "none"
9704
9616
  },
@@ -9739,7 +9651,7 @@ var init_staticRecipes = __esmMin((() => {
9739
9651
  border: { widthClass: "border" },
9740
9652
  effects: { hover: [{
9741
9653
  effectId: "background-on-hover",
9742
- options: { colorToken: "background" }
9654
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("background") }
9743
9655
  }] }
9744
9656
  }
9745
9657
  },
@@ -9754,7 +9666,7 @@ var init_staticRecipes = __esmMin((() => {
9754
9666
  border: { widthClass: "border" },
9755
9667
  effects: { hover: [{
9756
9668
  effectId: "background-on-hover",
9757
- options: { colorToken: "surface" }
9669
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
9758
9670
  }] }
9759
9671
  }
9760
9672
  },
@@ -9768,7 +9680,7 @@ var init_staticRecipes = __esmMin((() => {
9768
9680
  borderRadius: "rounded-md",
9769
9681
  effects: { hover: [{
9770
9682
  effectId: "background-on-hover",
9771
- options: { colorToken: "surface" }
9683
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
9772
9684
  }] }
9773
9685
  }
9774
9686
  },
@@ -9803,7 +9715,6 @@ var init_staticRecipes = __esmMin((() => {
9803
9715
  cornerStyle: "square",
9804
9716
  textTransform: "none",
9805
9717
  fontWeight: 500,
9806
- borderWidth: 1,
9807
9718
  hoverTransform: "none",
9808
9719
  hoverColor: "none"
9809
9720
  },
@@ -9821,7 +9732,6 @@ var init_staticRecipes = __esmMin((() => {
9821
9732
  effectId: "border-bottom-grow",
9822
9733
  options: {
9823
9734
  height: 2,
9824
- colorToken: "text",
9825
9735
  origin: "left"
9826
9736
  }
9827
9737
  }] }
@@ -9840,7 +9750,6 @@ var init_staticRecipes = __esmMin((() => {
9840
9750
  effectId: "border-bottom-grow",
9841
9751
  options: {
9842
9752
  height: 1,
9843
- colorToken: "mutedText",
9844
9753
  origin: "left"
9845
9754
  }
9846
9755
  }] }
@@ -9859,7 +9768,6 @@ var init_staticRecipes = __esmMin((() => {
9859
9768
  effectId: "border-bottom-grow",
9860
9769
  options: {
9861
9770
  height: 1,
9862
- colorToken: "accent",
9863
9771
  origin: "left"
9864
9772
  }
9865
9773
  }] }
@@ -9883,7 +9791,6 @@ var init_staticRecipes = __esmMin((() => {
9883
9791
  effectId: "border-bottom-grow",
9884
9792
  options: {
9885
9793
  height: 2,
9886
- colorToken: "accent",
9887
9794
  origin: "left"
9888
9795
  }
9889
9796
  }] } }
@@ -9905,7 +9812,6 @@ var init_staticRecipes = __esmMin((() => {
9905
9812
  cornerStyle: "pill",
9906
9813
  textTransform: "none",
9907
9814
  fontWeight: 500,
9908
- borderWidth: 2,
9909
9815
  hoverTransform: "none",
9910
9816
  hoverColor: "none"
9911
9817
  },
@@ -9950,7 +9856,7 @@ var init_staticRecipes = __esmMin((() => {
9950
9856
  border: { widthClass: "border-2" },
9951
9857
  effects: { hover: [{
9952
9858
  effectId: "background-on-hover",
9953
- options: { colorToken: "accent" }
9859
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("accent") }
9954
9860
  }] }
9955
9861
  }
9956
9862
  },
@@ -9965,7 +9871,7 @@ var init_staticRecipes = __esmMin((() => {
9965
9871
  border: { widthClass: "border" },
9966
9872
  effects: { hover: [{
9967
9873
  effectId: "background-on-hover",
9968
- options: { colorToken: "surface" }
9874
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
9969
9875
  }] }
9970
9876
  }
9971
9877
  },
@@ -10020,7 +9926,6 @@ var init_staticRecipes = __esmMin((() => {
10020
9926
  shadow: "low",
10021
9927
  textTransform: "none",
10022
9928
  fontWeight: 500,
10023
- borderWidth: 1,
10024
9929
  hoverTransform: "none",
10025
9930
  hoverColor: "none"
10026
9931
  },
@@ -10085,7 +9990,7 @@ var init_staticRecipes = __esmMin((() => {
10085
9990
  border: { widthClass: "border" },
10086
9991
  effects: { hover: [{
10087
9992
  effectId: "background-on-hover",
10088
- options: { colorToken: "surface" }
9993
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
10089
9994
  }] }
10090
9995
  }
10091
9996
  },
@@ -10099,7 +10004,7 @@ var init_staticRecipes = __esmMin((() => {
10099
10004
  borderRadius: "rounded-lg",
10100
10005
  effects: { hover: [{
10101
10006
  effectId: "background-on-hover",
10102
- options: { colorToken: "surface" }
10007
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
10103
10008
  }] }
10104
10009
  }
10105
10010
  },
@@ -10128,7 +10033,6 @@ var init_staticRecipes = __esmMin((() => {
10128
10033
  cornerStyle: "rounded",
10129
10034
  textTransform: "uppercase",
10130
10035
  fontWeight: 600,
10131
- borderWidth: 1,
10132
10036
  hoverTransform: "none",
10133
10037
  hoverColor: "none",
10134
10038
  letterSpacing: "loose"
@@ -10186,7 +10090,7 @@ var init_staticRecipes = __esmMin((() => {
10186
10090
  border: { widthClass: "border-2" },
10187
10091
  effects: { hover: [{
10188
10092
  effectId: "background-on-hover",
10189
- options: { colorToken: "surface" }
10093
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
10190
10094
  }] }
10191
10095
  }
10192
10096
  },
@@ -10201,7 +10105,7 @@ var init_staticRecipes = __esmMin((() => {
10201
10105
  border: { widthClass: "border-2" },
10202
10106
  effects: { hover: [{
10203
10107
  effectId: "background-on-hover",
10204
- options: { colorToken: "surface" }
10108
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
10205
10109
  }] }
10206
10110
  }
10207
10111
  },
@@ -10235,7 +10139,6 @@ var init_staticRecipes = __esmMin((() => {
10235
10139
  effectId: "border-bottom-grow",
10236
10140
  options: {
10237
10141
  height: 1,
10238
- colorToken: "accent",
10239
10142
  origin: "left"
10240
10143
  }
10241
10144
  }] }
@@ -10266,7 +10169,6 @@ var init_staticRecipes = __esmMin((() => {
10266
10169
  cornerStyle: "rounded",
10267
10170
  textTransform: "none",
10268
10171
  fontWeight: 600,
10269
- borderWidth: 1,
10270
10172
  typography: "body",
10271
10173
  letterSpacing: "tight",
10272
10174
  paddingPreset: "default",
@@ -10298,12 +10200,9 @@ var init_staticRecipes = __esmMin((() => {
10298
10200
  },
10299
10201
  borderRadius: "rounded-lg",
10300
10202
  affordance: { iconId: "ArrowRight" },
10301
- effects: { hover: [{
10302
- effectId: "lift",
10303
- options: { translateY: "-2px" }
10304
- }, {
10203
+ effects: { hover: [{ effectId: "lift" }, {
10305
10204
  effectId: "background-on-hover",
10306
- options: { colorToken: "secondary" }
10205
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("secondary") }
10307
10206
  }] }
10308
10207
  }
10309
10208
  },
@@ -10316,12 +10215,9 @@ var init_staticRecipes = __esmMin((() => {
10316
10215
  },
10317
10216
  borderRadius: "rounded-lg",
10318
10217
  border: { widthClass: "border" },
10319
- effects: { hover: [{
10320
- effectId: "lift",
10321
- options: { translateY: "-2px" }
10322
- }, {
10218
+ effects: { hover: [{ effectId: "lift" }, {
10323
10219
  effectId: "background-on-hover",
10324
- options: { colorToken: "surfaceAlt" }
10220
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
10325
10221
  }] }
10326
10222
  }
10327
10223
  },
@@ -10334,12 +10230,9 @@ var init_staticRecipes = __esmMin((() => {
10334
10230
  },
10335
10231
  borderRadius: "rounded-lg",
10336
10232
  border: { widthClass: "border" },
10337
- effects: { hover: [{
10338
- effectId: "lift",
10339
- options: { translateY: "-2px" }
10340
- }, {
10233
+ effects: { hover: [{ effectId: "lift" }, {
10341
10234
  effectId: "background-on-hover",
10342
- options: { colorToken: "surfaceAlt" }
10235
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surfaceAlt") }
10343
10236
  }] }
10344
10237
  }
10345
10238
  },
@@ -10351,10 +10244,7 @@ var init_staticRecipes = __esmMin((() => {
10351
10244
  role: "link"
10352
10245
  },
10353
10246
  borderRadius: "rounded-lg",
10354
- effects: { hover: [{
10355
- effectId: "lift",
10356
- options: { translateY: "-2px" }
10357
- }] }
10247
+ effects: { hover: [{ effectId: "lift" }] }
10358
10248
  }
10359
10249
  },
10360
10250
  accent: {
@@ -10366,10 +10256,7 @@ var init_staticRecipes = __esmMin((() => {
10366
10256
  },
10367
10257
  borderRadius: "rounded-lg",
10368
10258
  affordance: { iconId: "ArrowRight" },
10369
- effects: { hover: [{
10370
- effectId: "lift",
10371
- options: { translateY: "-2px" }
10372
- }] }
10259
+ effects: { hover: [{ effectId: "lift" }] }
10373
10260
  }
10374
10261
  }
10375
10262
  }
@@ -10390,9 +10277,8 @@ var init_staticRecipes = __esmMin((() => {
10390
10277
  shadow: "low",
10391
10278
  textTransform: "uppercase",
10392
10279
  fontWeight: 700,
10393
- borderWidth: 2,
10394
10280
  hoverTransform: "lift",
10395
- hoverColor: "darken",
10281
+ hoverColor: "none",
10396
10282
  typography: "body",
10397
10283
  paddingPreset: "spacious",
10398
10284
  italic: false
@@ -10450,7 +10336,7 @@ var init_staticRecipes = __esmMin((() => {
10450
10336
  border: { widthClass: "border-2" },
10451
10337
  effects: { hover: [{
10452
10338
  effectId: "background-on-hover",
10453
- options: { colorToken: "background" }
10339
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("background") }
10454
10340
  }] }
10455
10341
  }
10456
10342
  },
@@ -10464,7 +10350,7 @@ var init_staticRecipes = __esmMin((() => {
10464
10350
  borderRadius: "rounded-full",
10465
10351
  effects: { hover: [{
10466
10352
  effectId: "background-on-hover",
10467
- options: { colorToken: "surface" }
10353
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
10468
10354
  }] }
10469
10355
  }
10470
10356
  },
@@ -10499,7 +10385,6 @@ var init_staticRecipes = __esmMin((() => {
10499
10385
  typography: "heading",
10500
10386
  fontWeight: 700,
10501
10387
  fontSize: "text-lg",
10502
- borderWidth: 2,
10503
10388
  hoverTransform: "none",
10504
10389
  hoverColor: "none"
10505
10390
  },
@@ -10534,7 +10419,7 @@ var init_staticRecipes = __esmMin((() => {
10534
10419
  elevation: "high",
10535
10420
  softness: "hard",
10536
10421
  position: "bottom-right",
10537
- colorToken: "text"
10422
+ colorToken: unsafeAsOpaqueButtonColourToken("text")
10538
10423
  },
10539
10424
  effects: { hover: [{ effectId: "press-down" }] }
10540
10425
  }
@@ -10552,7 +10437,7 @@ var init_staticRecipes = __esmMin((() => {
10552
10437
  elevation: "high",
10553
10438
  softness: "hard",
10554
10439
  position: "bottom-right",
10555
- colorToken: "secondary"
10440
+ colorToken: unsafeAsOpaqueButtonColourToken("secondary")
10556
10441
  },
10557
10442
  effects: { hover: [{ effectId: "press-down" }] }
10558
10443
  }
@@ -10615,7 +10500,6 @@ var init_staticRecipes = __esmMin((() => {
10615
10500
  cornerStyle: "rounded",
10616
10501
  textTransform: "uppercase",
10617
10502
  fontWeight: 600,
10618
- borderWidth: 1,
10619
10503
  hoverTransform: "none",
10620
10504
  hoverColor: "none",
10621
10505
  letterSpacing: "loose"
@@ -10651,7 +10535,7 @@ var init_staticRecipes = __esmMin((() => {
10651
10535
  effects: {
10652
10536
  hover: [{
10653
10537
  effectId: "background-on-hover",
10654
- options: { colorToken: "primary-100" }
10538
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("primary-100") }
10655
10539
  }],
10656
10540
  active: [{
10657
10541
  effectId: "scale-down",
@@ -10671,7 +10555,7 @@ var init_staticRecipes = __esmMin((() => {
10671
10555
  border: { widthClass: "border" },
10672
10556
  effects: { hover: [{
10673
10557
  effectId: "background-on-hover",
10674
- options: { colorToken: "surface" }
10558
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("surface") }
10675
10559
  }] }
10676
10560
  }
10677
10561
  },
@@ -10686,7 +10570,7 @@ var init_staticRecipes = __esmMin((() => {
10686
10570
  border: { widthClass: "border" },
10687
10571
  effects: { hover: [{
10688
10572
  effectId: "background-on-hover",
10689
- options: { colorToken: "muted" }
10573
+ options: { colorToken: unsafeAsOpaqueButtonColourToken("muted") }
10690
10574
  }] }
10691
10575
  }
10692
10576
  },
@@ -10703,7 +10587,6 @@ var init_staticRecipes = __esmMin((() => {
10703
10587
  effectId: "border-bottom-grow",
10704
10588
  options: {
10705
10589
  height: 1,
10706
- colorToken: "accent",
10707
10590
  origin: "left"
10708
10591
  }
10709
10592
  }] }
@@ -11104,7 +10987,7 @@ var init_testimonialQuotePresets = __esmMin((() => {
11104
10987
  * Gradient configuration types and schemas
11105
10988
  */
11106
10989
  var gradientStopSchema, radialGradientShapeSchema, gradientConfigSchema;
11107
- var init_types$11 = __esmMin((() => {
10990
+ var init_types$10 = __esmMin((() => {
11108
10991
  init_themeColorTokens();
11109
10992
  gradientStopSchema = z$1.object({
11110
10993
  color: themeColorTokenSchema,
@@ -11364,15 +11247,15 @@ var init_utils$4 = __esmMin((() => {}));
11364
11247
  //#endregion
11365
11248
  //#region ../theme-core/src/gradients/index.ts
11366
11249
  var init_gradients = __esmMin((() => {
11367
- init_types$11();
11250
+ init_types$10();
11368
11251
  init_presets$2();
11369
11252
  init_utils$4();
11370
11253
  }));
11371
11254
  var init_customizableSurfaces = __esmMin((() => {
11372
- init_types$15();
11255
+ init_types$14();
11373
11256
  })), logoTextConfidenceValues, logoAssetShapeValues, logoKindValues, logoDensityValues, logoContainsTextSchema, logoAnalysisSourceSchema, brandDisplayModeValues, logoAccommodationPatternValues, logoSizePolicyValues;
11374
11257
  var init_logoBranding = __esmMin((() => {
11375
- init_types$15();
11258
+ init_types$14();
11376
11259
  logoTextConfidenceValues = [
11377
11260
  "low",
11378
11261
  "medium",
@@ -11845,7 +11728,7 @@ var init_headerLooks = __esmMin((() => {
11845
11728
  init_headerCtaVariants();
11846
11729
  init_customizableSurfaces();
11847
11730
  init_logoBranding();
11848
- init_types$15();
11731
+ init_types$14();
11849
11732
  titleOnlyLogoAccommodation = {
11850
11733
  pattern: "title-only",
11851
11734
  supportedShapes: [
@@ -12521,7 +12404,7 @@ function emphasizedFooterLinks(theme) {
12521
12404
  }
12522
12405
  var footerLookCatalog;
12523
12406
  var init_footerLooks = __esmMin((() => {
12524
- init_types$15();
12407
+ init_types$14();
12525
12408
  footerLookCatalog = defineExactCatalog()([{
12526
12409
  id: asFooterLookId("footer-links-quiet"),
12527
12410
  label: "Quiet links",
@@ -12537,7 +12420,7 @@ var init_footerLooks = __esmMin((() => {
12537
12420
  }));
12538
12421
  var init_curatedChoices = __esmMin((() => {})), generatedContentFrames, userContentFrames, generatedDesignOptionCatalog, userDesignOptionCatalog;
12539
12422
  var init_sourceCatalogs = __esmMin((() => {
12540
- init_types$15();
12423
+ init_types$14();
12541
12424
  generatedContentFrames = [
12542
12425
  plainFrame,
12543
12426
  panelFrame("inset"),
@@ -13122,6 +13005,643 @@ var init_media$1 = __esmMin((() => {
13122
13005
  });
13123
13006
  }));
13124
13007
  //#endregion
13008
+ //#region ../theme-core/src/buttons/constants.ts
13009
+ /**
13010
+ * Generate shadow CSS value dynamically based on elevation, softness, and position
13011
+ *
13012
+ * This replaces the static shadow maps with a single dynamic function.
13013
+ * Benefits: single source of truth, easy to adjust algorithm, no redundancy.
13014
+ *
13015
+ * @param elevation - Shadow elevation level
13016
+ * @param softness - Shadow softness/blur style
13017
+ * @param position - Shadow position (bottom or bottom-right diagonal)
13018
+ * @returns CSS box-shadow value
13019
+ */
13020
+ function generateShadowValue$1(elevation, softness, position = "bottom", color) {
13021
+ if (elevation === "none") return "none";
13022
+ const config = {
13023
+ low: {
13024
+ blur: 4,
13025
+ spread: 0
13026
+ },
13027
+ medium: {
13028
+ blur: 8,
13029
+ spread: -1
13030
+ },
13031
+ high: {
13032
+ blur: 12,
13033
+ spread: -2
13034
+ }
13035
+ }[elevation];
13036
+ const baseOffset = ELEVATION_OFFSET[elevation];
13037
+ const xOffset = position === "bottom-right" ? baseOffset : 0;
13038
+ const yOffset = baseOffset;
13039
+ switch (softness) {
13040
+ case null:
13041
+ case "soft": return `${xOffset}px ${yOffset}px ${config.blur}px ${config.spread}px ${color ?? "rgb(0 0 0 / 0.3)"}`;
13042
+ case "hard": return `${xOffset}px ${yOffset}px 0 0 ${color ?? "rgb(0 0 0 / 1)"}`;
13043
+ case "crisp": return `${xOffset}px ${yOffset}px ${baseOffset}px ${config.spread}px ${color ?? "rgb(0 0 0 / 0.4)"}`;
13044
+ }
13045
+ }
13046
+ var GRADIENT_DIRECTION_MAP$2, BORDER_RADIUS_MAP, ELEVATION_OFFSET, BORDER_WIDTH_MAP$4, FONT_SIZE_MAP, DEFAULT_BUTTON_SIZES;
13047
+ var init_constants$2 = __esmMin((() => {
13048
+ GRADIENT_DIRECTION_MAP$2 = {
13049
+ "to-r": "to right",
13050
+ "to-l": "to left",
13051
+ "to-t": "to top",
13052
+ "to-b": "to bottom",
13053
+ "to-br": "to bottom right",
13054
+ "to-bl": "to bottom left",
13055
+ "to-tr": "to top right",
13056
+ "to-tl": "to top left"
13057
+ };
13058
+ BORDER_RADIUS_MAP = {
13059
+ "rounded-none": "0",
13060
+ "rounded-sm": "0.125rem",
13061
+ "rounded": "0.25rem",
13062
+ "rounded-md": "0.375rem",
13063
+ "rounded-lg": "0.5rem",
13064
+ "rounded-xl": "0.75rem",
13065
+ "rounded-2xl": "1rem",
13066
+ "rounded-3xl": "1.5rem",
13067
+ "rounded-full": "9999px"
13068
+ };
13069
+ ELEVATION_OFFSET = {
13070
+ none: 0,
13071
+ low: 2,
13072
+ medium: 4,
13073
+ high: 6
13074
+ };
13075
+ BORDER_WIDTH_MAP$4 = {
13076
+ "border-0": "0px",
13077
+ "border": "1px",
13078
+ "border-2": "2px",
13079
+ "border-4": "4px",
13080
+ "border-8": "8px"
13081
+ };
13082
+ FONT_SIZE_MAP = {
13083
+ "text-xs": "0.75rem",
13084
+ "text-sm": "0.875rem",
13085
+ "text-base": "1rem",
13086
+ "text-lg": "1.125rem",
13087
+ "text-xl": "1.25rem",
13088
+ "text-2xl": "1.5rem"
13089
+ };
13090
+ DEFAULT_BUTTON_SIZES = {
13091
+ sm: {
13092
+ padding: "0.5rem 1rem",
13093
+ fontSize: "text-sm"
13094
+ },
13095
+ md: { padding: "0.75rem 1.5rem" },
13096
+ lg: {
13097
+ padding: "1rem 2rem",
13098
+ fontSize: "text-lg"
13099
+ },
13100
+ xl: {
13101
+ padding: "1.25rem 2.5rem",
13102
+ fontSize: "text-xl"
13103
+ }
13104
+ };
13105
+ }));
13106
+ //#endregion
13107
+ //#region ../theme-core/src/buttons/effectClassification.ts
13108
+ function validateButtonEffectOptions(effect) {
13109
+ const parsed = buttonEffectApplicationSchema.safeParse(effect);
13110
+ if (parsed.success) return { ok: true };
13111
+ const firstIssue = parsed.error.issues[0];
13112
+ const firstPathPart = firstIssue?.path[firstIssue.path.length - 1];
13113
+ return {
13114
+ ok: false,
13115
+ option: (firstIssue?.code === "unrecognized_keys" ? firstIssue.keys[0] : void 0) ?? (typeof firstPathPart === "string" ? firstPathPart : "options")
13116
+ };
13117
+ }
13118
+ function getButtonEffectPolicy(effectId) {
13119
+ return Object.prototype.hasOwnProperty.call(BUTTON_EFFECT_POLICIES, effectId) ? BUTTON_EFFECT_POLICIES[effectId] : null;
13120
+ }
13121
+ var BUTTON_EFFECT_POLICIES;
13122
+ var init_effectClassification = __esmMin((() => {
13123
+ init_types$11();
13124
+ BUTTON_EFFECT_POLICIES = {
13125
+ "darken-background": {
13126
+ classification: "colour-transition",
13127
+ transition: {
13128
+ kind: "adjust-shade",
13129
+ direction: "darker"
13130
+ }
13131
+ },
13132
+ "lighten-background": {
13133
+ classification: "colour-transition",
13134
+ transition: {
13135
+ kind: "adjust-shade",
13136
+ direction: "lighter"
13137
+ }
13138
+ },
13139
+ "background-on-hover": {
13140
+ classification: "colour-transition",
13141
+ transition: { kind: "replace-background" }
13142
+ },
13143
+ "background-on-hover-alpha": {
13144
+ classification: "unsupported-colour",
13145
+ reason: "alpha_requires_surface"
13146
+ },
13147
+ "fade-background": {
13148
+ classification: "unsupported-colour",
13149
+ reason: "changes_element_opacity"
13150
+ },
13151
+ "opacity-reduce": {
13152
+ classification: "unsupported-colour",
13153
+ reason: "changes_element_opacity"
13154
+ },
13155
+ "gradient-text": {
13156
+ classification: "unsupported-colour",
13157
+ reason: "changes_foreground"
13158
+ },
13159
+ "text-color-change": {
13160
+ classification: "unsupported-colour",
13161
+ reason: "changes_foreground"
13162
+ },
13163
+ "border-color-change": {
13164
+ classification: "unsupported-colour",
13165
+ reason: "changes_border"
13166
+ },
13167
+ "aurora-glow": {
13168
+ classification: "unsupported-background-overlay",
13169
+ reason: "unresolved_background_overlay"
13170
+ },
13171
+ "cosmic-stardust": {
13172
+ classification: "unsupported-background-overlay",
13173
+ reason: "unresolved_background_overlay"
13174
+ },
13175
+ "gradient-border-fill": {
13176
+ classification: "unsupported-background-overlay",
13177
+ reason: "unresolved_background_overlay"
13178
+ },
13179
+ "animated-gradient-border-fill": {
13180
+ classification: "unsupported-background-overlay",
13181
+ reason: "unresolved_background_overlay"
13182
+ },
13183
+ "nav-underline": {
13184
+ classification: "not-button",
13185
+ reason: "not_button_effect"
13186
+ },
13187
+ "nav-frosted-base": {
13188
+ classification: "not-button",
13189
+ reason: "not_button_effect"
13190
+ },
13191
+ "nav-frosted-hover": {
13192
+ classification: "not-button",
13193
+ reason: "not_button_effect"
13194
+ },
13195
+ "nav-frosted-active": {
13196
+ classification: "not-button",
13197
+ reason: "not_button_effect"
13198
+ },
13199
+ "animated-gradient": { classification: "non-colour" },
13200
+ "border-glow": {
13201
+ classification: "unsupported-colour",
13202
+ reason: "changes_shadow_colour"
13203
+ },
13204
+ "drop-shadow": { classification: "decorative-colour" },
13205
+ "inner-shadow": { classification: "decorative-colour" },
13206
+ "neumorphic-shadow": { classification: "decorative-colour" },
13207
+ "outer-glow": {
13208
+ classification: "unsupported-colour",
13209
+ reason: "changes_shadow_colour"
13210
+ },
13211
+ "retro-3d-shadow": { classification: "decorative-colour" },
13212
+ "scale-up": { classification: "non-colour" },
13213
+ "scale-down": { classification: "non-colour" },
13214
+ lift: { classification: "non-colour" },
13215
+ "press-down": { classification: "non-colour" },
13216
+ pop: { classification: "non-colour" },
13217
+ "border-bottom-grow": { classification: "non-colour" },
13218
+ "sketchy-border": { classification: "decorative-colour" },
13219
+ "pulse-animation": {
13220
+ classification: "unsupported-colour",
13221
+ reason: "changes_element_opacity"
13222
+ }
13223
+ };
13224
+ }));
13225
+ //#endregion
13226
+ //#region ../theme-core/src/buttons/resolveButtonColourStates.ts
13227
+ function compileButtonColourStates(input) {
13228
+ const baseTransition = applyColourEffects({
13229
+ variantId: input.variant.id,
13230
+ state: "base",
13231
+ treatment: input.variant.background,
13232
+ effects: input.variant.effects?.base,
13233
+ palette: input.palette
13234
+ });
13235
+ if (!baseTransition.ok) return baseTransition;
13236
+ const stateTreatments = {
13237
+ base: baseTransition.treatment,
13238
+ hover: hoverTreatment(input.variant, input.global, baseTransition.treatment),
13239
+ focus: baseTransition.treatment,
13240
+ active: baseTransition.treatment,
13241
+ disabled: baseTransition.treatment
13242
+ };
13243
+ const nonColourEffects = { ...baseTransition.nonColourEffects.length > 0 ? { base: [...baseTransition.nonColourEffects] } : {} };
13244
+ for (const state of [
13245
+ "hover",
13246
+ "focus",
13247
+ "active"
13248
+ ]) {
13249
+ const transition = applyColourEffects({
13250
+ variantId: input.variant.id,
13251
+ state,
13252
+ treatment: stateTreatments[state],
13253
+ effects: input.variant.effects?.[state],
13254
+ palette: input.palette
13255
+ });
13256
+ if (!transition.ok) return transition;
13257
+ stateTreatments[state] = transition.treatment;
13258
+ if (transition.nonColourEffects.length > 0) nonColourEffects[state] = [...transition.nonColourEffects];
13259
+ }
13260
+ const resolved = {};
13261
+ for (const state of BUTTON_VISUAL_STATES) {
13262
+ const result = resolveTreatment$1({
13263
+ variantId: input.variant.id,
13264
+ state,
13265
+ treatment: stateTreatments[state],
13266
+ palette: input.palette
13267
+ });
13268
+ if (!result.ok) return result;
13269
+ resolved[state] = result.colour;
13270
+ }
13271
+ return {
13272
+ ok: true,
13273
+ states: resolved,
13274
+ focusIndicator: resolveFocusIndicator(resolved.focus),
13275
+ nonColourEffects
13276
+ };
13277
+ }
13278
+ function formatButtonColourContractError(error) {
13279
+ switch (error.tag) {
13280
+ case "unknown_palette_token": return `Button "${error.variantId}" ${error.state} state uses unknown palette token "${error.token}".`;
13281
+ case "missing_on_colour": return `Button "${error.variantId}" ${error.state} state cannot derive ${error.foregroundToken} from ${error.backgroundToken}.`;
13282
+ case "insufficient_contrast": return `Button "${error.variantId}" ${error.state} state has ${error.ratio.toFixed(2)}:1 contrast (${error.requiredRatio}:1 required).`;
13283
+ case "unsupported_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" is not contrast-safe (${error.reason}).`;
13284
+ case "invalid_colour_effect_options": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" has an invalid ${error.option} option.`;
13285
+ case "inapplicable_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" cannot apply to a ${error.treatment} treatment.`;
13286
+ case "unknown_button_effect": return `Button "${error.variantId}" ${error.state} uses unknown effect "${error.effectId}".`;
13287
+ }
13288
+ }
13289
+ function hoverTreatment(variant, global, fallback) {
13290
+ const token = variant.hoverBackgroundToken ?? (global.hoverColor === "token" ? global.hoverColorToken : void 0);
13291
+ return token === void 0 ? fallback : {
13292
+ type: "solid",
13293
+ colorToken: token
13294
+ };
13295
+ }
13296
+ function applyColourEffects(input) {
13297
+ let treatment = input.treatment;
13298
+ const nonColourEffects = [];
13299
+ for (const effect of input.effects ?? []) {
13300
+ const policy = getButtonEffectPolicy(effect.effectId);
13301
+ if (policy === null) return unknownEffect(input, effect);
13302
+ if (policy.classification === "unsupported-colour" || policy.classification === "unsupported-background-overlay" || policy.classification === "not-button") return unsupportedEffect(input, effect, policy.reason);
13303
+ const options = validateButtonEffectOptions(effect);
13304
+ if (!options.ok) return invalidEffectOption(input, effect, options.option);
13305
+ switch (policy.classification) {
13306
+ case "non-colour":
13307
+ case "decorative-colour":
13308
+ nonColourEffects.push(effect);
13309
+ break;
13310
+ case "colour-transition":
13311
+ if (policy.transition.kind === "adjust-shade") {
13312
+ if (effect.effectId !== "darken-background" && effect.effectId !== "lighten-background") return unknownEffect(input, effect);
13313
+ if (treatment.type !== "solid") return inapplicableEffect(input, effect, treatment);
13314
+ const amount = readShadeAmount(effect);
13315
+ if (amount === null) return invalidEffectOption(input, effect, "amount");
13316
+ const signedAmount = policy.transition.direction === "darker" ? amount : -amount;
13317
+ treatment = {
13318
+ type: "solid",
13319
+ colorToken: adjustShade(treatment.colorToken, signedAmount, input.palette)
13320
+ };
13321
+ break;
13322
+ }
13323
+ if (policy.transition.kind === "replace-background") {
13324
+ if (effect.effectId !== "background-on-hover") return unknownEffect(input, effect);
13325
+ const colorToken = readOpaqueToken(effect);
13326
+ if (colorToken === null) return invalidEffectOption(input, effect, "colorToken");
13327
+ treatment = {
13328
+ type: "solid",
13329
+ colorToken
13330
+ };
13331
+ break;
13332
+ }
13333
+ return unknownEffect(input, effect);
13334
+ }
13335
+ }
13336
+ return {
13337
+ ok: true,
13338
+ treatment,
13339
+ nonColourEffects
13340
+ };
13341
+ }
13342
+ function resolveTreatment$1(input) {
13343
+ const treatment = input.treatment;
13344
+ switch (treatment.type) {
13345
+ case "surface": return {
13346
+ ok: true,
13347
+ colour: {
13348
+ kind: "surface",
13349
+ role: treatment.role,
13350
+ backgroundCss: "transparent",
13351
+ foregroundCss: surfaceForegroundValue(treatment.role)
13352
+ }
13353
+ };
13354
+ case "solid": return resolveSolid(input, treatment.colorToken);
13355
+ case "gradient": return resolveGradient$2(input, treatment);
13356
+ }
13357
+ }
13358
+ function resolveFocusIndicator(colour) {
13359
+ switch (colour.kind) {
13360
+ case "solid": return {
13361
+ kind: "two-tone",
13362
+ innerCss: colour.foregroundCss,
13363
+ outerCss: colour.backgroundCss,
13364
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
13365
+ };
13366
+ case "gradient": return {
13367
+ kind: "two-tone",
13368
+ innerCss: colour.foregroundCss,
13369
+ outerCss: tokenCss(colour.backgroundTokens[0]),
13370
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
13371
+ };
13372
+ case "surface": return {
13373
+ kind: "surface-role",
13374
+ colorCss: colour.foregroundCss,
13375
+ minimumSurfaceContrast: BUTTON_CONTRAST_POLICY.nonTextMinimumRatio
13376
+ };
13377
+ }
13378
+ }
13379
+ function resolveSolid(input, backgroundToken) {
13380
+ const backgroundHex = paletteHex(input, backgroundToken);
13381
+ if (!backgroundHex.ok) return backgroundHex;
13382
+ const foregroundToken = unsafeAsOpaqueButtonColourToken(onColorTokenName(backgroundToken));
13383
+ const foregroundHex = input.palette[foregroundToken];
13384
+ if (typeof foregroundHex !== "string") return {
13385
+ ok: false,
13386
+ error: {
13387
+ tag: "missing_on_colour",
13388
+ variantId: input.variantId,
13389
+ state: input.state,
13390
+ backgroundToken,
13391
+ foregroundToken
13392
+ }
13393
+ };
13394
+ const contrast = validateContrast({
13395
+ ...input,
13396
+ background: backgroundToken,
13397
+ backgroundHex: backgroundHex.hex,
13398
+ foreground: foregroundToken,
13399
+ foregroundHex
13400
+ });
13401
+ if (!contrast.ok) return contrast;
13402
+ return {
13403
+ ok: true,
13404
+ colour: {
13405
+ kind: "solid",
13406
+ backgroundToken,
13407
+ foregroundToken,
13408
+ backgroundCss: tokenCss(backgroundToken),
13409
+ foregroundCss: tokenCss(foregroundToken)
13410
+ }
13411
+ };
13412
+ }
13413
+ function resolveGradient$2(input, treatment) {
13414
+ const foregroundHex = paletteHex(input, treatment.foregroundToken);
13415
+ if (!foregroundHex.ok) return foregroundHex;
13416
+ const stopHexes = [];
13417
+ for (const token of treatment.stops) {
13418
+ const stop = paletteHex(input, token);
13419
+ if (!stop.ok) return stop;
13420
+ stopHexes.push(stop.hex);
13421
+ }
13422
+ for (let segment = 0; segment < stopHexes.length - 1; segment += 1) {
13423
+ const start = stopHexes[segment];
13424
+ const end = stopHexes[segment + 1];
13425
+ const startToken = treatment.stops[segment];
13426
+ const endToken = treatment.stops[segment + 1];
13427
+ if (start === void 0 || end === void 0 || startToken === void 0 || endToken === void 0) continue;
13428
+ for (let sample = 0; sample <= BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment; sample += 1) {
13429
+ const position = sample / BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment;
13430
+ const sampleHex = interpolateHex(start, end, position);
13431
+ const contrast = validateContrast({
13432
+ ...input,
13433
+ background: `${startToken}→${endToken}@${position.toFixed(2)}`,
13434
+ backgroundHex: sampleHex,
13435
+ foreground: treatment.foregroundToken,
13436
+ foregroundHex: foregroundHex.hex
13437
+ });
13438
+ if (!contrast.ok) return contrast;
13439
+ }
13440
+ }
13441
+ const direction = GRADIENT_DIRECTION_MAP$2[treatment.direction] ?? "to right";
13442
+ return {
13443
+ ok: true,
13444
+ colour: {
13445
+ kind: "gradient",
13446
+ backgroundTokens: treatment.stops,
13447
+ foregroundToken: treatment.foregroundToken,
13448
+ backgroundCss: `linear-gradient(${direction}, ${treatment.stops.map(tokenCss).join(", ")})`,
13449
+ foregroundCss: tokenCss(treatment.foregroundToken)
13450
+ }
13451
+ };
13452
+ }
13453
+ function paletteHex(input, token) {
13454
+ const hex = input.palette[token];
13455
+ return typeof hex === "string" ? {
13456
+ ok: true,
13457
+ hex
13458
+ } : {
13459
+ ok: false,
13460
+ error: {
13461
+ tag: "unknown_palette_token",
13462
+ variantId: input.variantId,
13463
+ state: input.state,
13464
+ token
13465
+ }
13466
+ };
13467
+ }
13468
+ function validateContrast(input) {
13469
+ const ratio = contrastRatio(input.backgroundHex, input.foregroundHex);
13470
+ return ratio >= BUTTON_CONTRAST_POLICY.textMinimumRatio ? { ok: true } : {
13471
+ ok: false,
13472
+ error: {
13473
+ tag: "insufficient_contrast",
13474
+ variantId: input.variantId,
13475
+ state: input.state,
13476
+ background: input.background,
13477
+ foreground: input.foreground,
13478
+ ratio,
13479
+ requiredRatio: BUTTON_CONTRAST_POLICY.textMinimumRatio
13480
+ }
13481
+ };
13482
+ }
13483
+ function tokenCss(token) {
13484
+ return `rgb(var(--tb-${token}))`;
13485
+ }
13486
+ function adjustShade(token, amount, palette) {
13487
+ const match = token.match(/^(.*)-(\d{2,3})$/);
13488
+ const base = match?.[1] ?? token;
13489
+ const currentShade = match?.[2] === void 0 ? 500 : Number(match[2]);
13490
+ const availableShades = [
13491
+ 50,
13492
+ 100,
13493
+ 200,
13494
+ 300,
13495
+ 400,
13496
+ 500,
13497
+ 600,
13498
+ 700,
13499
+ 800,
13500
+ 900,
13501
+ 950
13502
+ ].filter((shade) => typeof palette[`${base}-${shade}`] === "string");
13503
+ if (availableShades.length === 0) return token;
13504
+ const firstShade = availableShades[0];
13505
+ if (firstShade === void 0) return token;
13506
+ const exactIndex = availableShades.indexOf(currentShade);
13507
+ const startingIndex = exactIndex >= 0 ? exactIndex : availableShades.indexOf(availableShades.reduce((closestShade, shade) => Math.abs(shade - currentShade) < Math.abs(closestShade - currentShade) ? shade : closestShade, firstShade));
13508
+ const stepCount = Math.abs(amount) / 100;
13509
+ const direction = amount < 0 ? -1 : 1;
13510
+ const targetShade = availableShades[Math.max(0, Math.min(availableShades.length - 1, startingIndex + direction * stepCount))];
13511
+ return targetShade === void 0 ? token : unsafeAsOpaqueButtonColourToken(`${base}-${targetShade}`);
13512
+ }
13513
+ function readShadeAmount(effect) {
13514
+ const value = effect.options?.amount ?? 100;
13515
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value % 100 === 0 ? value : null;
13516
+ }
13517
+ function readOpaqueToken(effect) {
13518
+ return parseOpaqueButtonColourToken(effect.options?.colorToken ?? "neutral-800");
13519
+ }
13520
+ function unsupportedEffect(input, effect, reason) {
13521
+ return {
13522
+ ok: false,
13523
+ error: {
13524
+ tag: "unsupported_colour_effect",
13525
+ variantId: input.variantId,
13526
+ state: input.state,
13527
+ effectId: effect.effectId,
13528
+ reason
13529
+ }
13530
+ };
13531
+ }
13532
+ function invalidEffectOption(input, effect, option) {
13533
+ return {
13534
+ ok: false,
13535
+ error: {
13536
+ tag: "invalid_colour_effect_options",
13537
+ variantId: input.variantId,
13538
+ state: input.state,
13539
+ effectId: effect.effectId,
13540
+ option
13541
+ }
13542
+ };
13543
+ }
13544
+ function inapplicableEffect(input, effect, treatment) {
13545
+ return {
13546
+ ok: false,
13547
+ error: {
13548
+ tag: "inapplicable_colour_effect",
13549
+ variantId: input.variantId,
13550
+ state: input.state,
13551
+ effectId: effect.effectId,
13552
+ treatment: treatment.type
13553
+ }
13554
+ };
13555
+ }
13556
+ function unknownEffect(input, effect) {
13557
+ return {
13558
+ ok: false,
13559
+ error: {
13560
+ tag: "unknown_button_effect",
13561
+ variantId: input.variantId,
13562
+ state: input.state,
13563
+ effectId: effect.effectId
13564
+ }
13565
+ };
13566
+ }
13567
+ function interpolateHex(startHex, endHex, position) {
13568
+ const start = hexToRgb$1(startHex);
13569
+ const end = hexToRgb$1(endHex);
13570
+ const channel = (from, to) => Math.round(from + (to - from) * position).toString(16).padStart(2, "0");
13571
+ return `#${channel(start.r, end.r)}${channel(start.g, end.g)}${channel(start.b, end.b)}`;
13572
+ }
13573
+ var BUTTON_VISUAL_STATES, BUTTON_CONTRAST_POLICY;
13574
+ var init_resolveButtonColourStates = __esmMin((() => {
13575
+ init_contrast$1();
13576
+ init_colorConversion();
13577
+ init_onColor();
13578
+ init_foregroundContract();
13579
+ init_types$11();
13580
+ init_constants$2();
13581
+ init_effectClassification();
13582
+ BUTTON_VISUAL_STATES = [
13583
+ "base",
13584
+ "hover",
13585
+ "focus",
13586
+ "active",
13587
+ "disabled"
13588
+ ];
13589
+ BUTTON_CONTRAST_POLICY = {
13590
+ textMinimumRatio: 4.5,
13591
+ nonTextMinimumRatio: 3,
13592
+ gradientSamplesPerSegment: 20,
13593
+ alphaBackgrounds: "reject-without-known-surface",
13594
+ disabled: "retain-full-colour-contract"
13595
+ };
13596
+ }));
13597
+ //#endregion
13598
+ //#region ../theme-core/src/buttons/parseThemeForPersistence.ts
13599
+ /**
13600
+ * Proves that every persisted button variant compiles against the theme's
13601
+ * concrete palette. Public rendering may still recover from malformed
13602
+ * external/runtime input, but persistence boundaries must reject it.
13603
+ */
13604
+ function parseThemeForPersistence(value) {
13605
+ const parsed = themeSchema.safeParse(value);
13606
+ if (!parsed.success) return {
13607
+ ok: false,
13608
+ error: {
13609
+ kind: "invalid-theme-schema",
13610
+ issues: parsed.error.issues
13611
+ }
13612
+ };
13613
+ const theme = parsed.data;
13614
+ if (!theme.buttons) return {
13615
+ ok: true,
13616
+ value: theme
13617
+ };
13618
+ const palette = expandPalette(theme.palette);
13619
+ for (const variant of theme.buttons.variants) {
13620
+ const result = compileButtonColourStates({
13621
+ variant,
13622
+ global: theme.buttons.global,
13623
+ palette
13624
+ });
13625
+ if (!result.ok) return {
13626
+ ok: false,
13627
+ error: {
13628
+ kind: "invalid-button-colour-contract",
13629
+ variantId: variant.id,
13630
+ cause: result.error
13631
+ }
13632
+ };
13633
+ }
13634
+ return {
13635
+ ok: true,
13636
+ value: theme
13637
+ };
13638
+ }
13639
+ var init_parseThemeForPersistence = __esmMin((() => {
13640
+ init_schema$1();
13641
+ init_expandPalette();
13642
+ init_resolveButtonColourStates();
13643
+ }));
13644
+ //#endregion
13125
13645
  //#region ../theme-core/src/buttons/classNames.ts
13126
13646
  /**
13127
13647
  * Parse content/runtime variant identifiers into the closed theme-button class
@@ -13131,6 +13651,7 @@ var init_media$1 = __esmMin((() => {
13131
13651
  function parseThemeButtonVariantId(value) {
13132
13652
  const normalized = value?.trim() ?? "";
13133
13653
  if (normalized.length === 0) return null;
13654
+ if (!buttonVariantIdSchema.safeParse(normalized).success) return null;
13134
13655
  if (normalized === "default") return DEFAULT_VARIANT_ALIAS_ID;
13135
13656
  return isVariantRole(normalized) ? normalized : asSpecialVariantId(normalized);
13136
13657
  }
@@ -13154,7 +13675,7 @@ function themeButtonClassName(spec) {
13154
13675
  ].filter(Boolean).join(" ");
13155
13676
  }
13156
13677
  var init_classNames = __esmMin((() => {
13157
- init_types$12();
13678
+ init_types$11();
13158
13679
  }));
13159
13680
  //#endregion
13160
13681
  //#region ../theme-core/src/interactive/generateBaseCSS.ts
@@ -13225,9 +13746,21 @@ var init_background$2 = __esmMin((() => {
13225
13746
  applicableTo: ["button"],
13226
13747
  customizableOption: {
13227
13748
  name: "amount",
13228
- type: "number",
13749
+ type: "select",
13229
13750
  label: "Darken Amount (shade step)",
13230
- default: 100
13751
+ default: 100,
13752
+ options: [
13753
+ 0,
13754
+ 100,
13755
+ 200,
13756
+ 300,
13757
+ 400,
13758
+ 500,
13759
+ 600,
13760
+ 700,
13761
+ 800,
13762
+ 900
13763
+ ]
13231
13764
  },
13232
13765
  isApplicable: (buttonConfig) => {
13233
13766
  return buttonConfig.background.type === "solid";
@@ -13254,9 +13787,21 @@ var init_background$2 = __esmMin((() => {
13254
13787
  applicableTo: ["button"],
13255
13788
  customizableOption: {
13256
13789
  name: "amount",
13257
- type: "number",
13790
+ type: "select",
13258
13791
  label: "Lighten Amount (shade step)",
13259
- default: 100
13792
+ default: 100,
13793
+ options: [
13794
+ 0,
13795
+ 100,
13796
+ 200,
13797
+ 300,
13798
+ 400,
13799
+ 500,
13800
+ 600,
13801
+ 700,
13802
+ 800,
13803
+ 900
13804
+ ]
13260
13805
  },
13261
13806
  isApplicable: (buttonConfig) => {
13262
13807
  return buttonConfig.background.type === "solid";
@@ -13280,7 +13825,7 @@ var init_background$2 = __esmMin((() => {
13280
13825
  description: "Reduces background opacity on hover",
13281
13826
  category: "background",
13282
13827
  pseudoElement: "none",
13283
- applicableTo: ["button", "nav-link"],
13828
+ applicableTo: ["nav-link"],
13284
13829
  customizableOption: {
13285
13830
  name: "opacity",
13286
13831
  type: "percentage",
@@ -13299,10 +13844,10 @@ var init_background$2 = __esmMin((() => {
13299
13844
  opacityReduceEffect = {
13300
13845
  id: "opacity-reduce",
13301
13846
  name: "Reduce Opacity",
13302
- description: "Reduces button opacity on hover by a percentage",
13847
+ description: "Reduces navigation-link opacity on hover by a percentage",
13303
13848
  category: "background",
13304
13849
  pseudoElement: "none",
13305
- applicableTo: ["button", "nav-link"],
13850
+ applicableTo: ["nav-link"],
13306
13851
  customizableOption: {
13307
13852
  name: "reduction",
13308
13853
  type: "percentage",
@@ -13390,7 +13935,7 @@ var init_background$2 = __esmMin((() => {
13390
13935
  description: "Adds semi-transparent background on hover",
13391
13936
  category: "background",
13392
13937
  pseudoElement: "none",
13393
- applicableTo: ["button", "nav-link"],
13938
+ applicableTo: ["nav-link"],
13394
13939
  customizableOption: {
13395
13940
  name: "opacity",
13396
13941
  type: "percentage",
@@ -13464,7 +14009,7 @@ var init_shadow = __esmMin((() => {
13464
14009
  description: "Adds a glowing shadow on hover",
13465
14010
  category: "shadow",
13466
14011
  pseudoElement: "none",
13467
- applicableTo: ["button", "nav-link"],
14012
+ applicableTo: ["nav-link"],
13468
14013
  customizableOption: {
13469
14014
  name: "glowColorToken",
13470
14015
  type: "colorToken",
@@ -13563,10 +14108,10 @@ var init_shadow = __esmMin((() => {
13563
14108
  outerGlowEffect = {
13564
14109
  id: "outer-glow",
13565
14110
  name: "Outer Glow",
13566
- description: "Creates an outer glow around the button",
14111
+ description: "Creates an outer glow around a navigation link",
13567
14112
  category: "shadow",
13568
14113
  pseudoElement: "none",
13569
- applicableTo: ["button", "nav-link"],
14114
+ applicableTo: ["nav-link"],
13570
14115
  customizableOption: {
13571
14116
  name: "spread",
13572
14117
  type: "number",
@@ -13622,105 +14167,6 @@ var init_shadow = __esmMin((() => {
13622
14167
  };
13623
14168
  }));
13624
14169
  //#endregion
13625
- //#region ../theme-core/src/buttons/constants.ts
13626
- /**
13627
- * Generate shadow CSS value dynamically based on elevation, softness, and position
13628
- *
13629
- * This replaces the static shadow maps with a single dynamic function.
13630
- * Benefits: single source of truth, easy to adjust algorithm, no redundancy.
13631
- *
13632
- * @param elevation - Shadow elevation level
13633
- * @param softness - Shadow softness/blur style
13634
- * @param position - Shadow position (bottom or bottom-right diagonal)
13635
- * @returns CSS box-shadow value
13636
- */
13637
- function generateShadowValue$1(elevation, softness, position = "bottom", color) {
13638
- if (elevation === "none") return "none";
13639
- const config = {
13640
- low: {
13641
- blur: 4,
13642
- spread: 0
13643
- },
13644
- medium: {
13645
- blur: 8,
13646
- spread: -1
13647
- },
13648
- high: {
13649
- blur: 12,
13650
- spread: -2
13651
- }
13652
- }[elevation];
13653
- const baseOffset = ELEVATION_OFFSET[elevation];
13654
- const xOffset = position === "bottom-right" ? baseOffset : 0;
13655
- const yOffset = baseOffset;
13656
- switch (softness) {
13657
- case null:
13658
- case "soft": return `${xOffset}px ${yOffset}px ${config.blur}px ${config.spread}px ${color ?? "rgb(0 0 0 / 0.3)"}`;
13659
- case "hard": return `${xOffset}px ${yOffset}px 0 0 ${color ?? "rgb(0 0 0 / 1)"}`;
13660
- case "crisp": return `${xOffset}px ${yOffset}px ${baseOffset}px ${config.spread}px ${color ?? "rgb(0 0 0 / 0.4)"}`;
13661
- }
13662
- }
13663
- var GRADIENT_DIRECTION_MAP$2, BORDER_RADIUS_MAP, ELEVATION_OFFSET, BORDER_WIDTH_MAP$4, FONT_SIZE_MAP, DEFAULT_BUTTON_SIZES;
13664
- var init_constants$2 = __esmMin((() => {
13665
- GRADIENT_DIRECTION_MAP$2 = {
13666
- "to-r": "to right",
13667
- "to-l": "to left",
13668
- "to-t": "to top",
13669
- "to-b": "to bottom",
13670
- "to-br": "to bottom right",
13671
- "to-bl": "to bottom left",
13672
- "to-tr": "to top right",
13673
- "to-tl": "to top left"
13674
- };
13675
- BORDER_RADIUS_MAP = {
13676
- "rounded-none": "0",
13677
- "rounded-sm": "0.125rem",
13678
- "rounded": "0.25rem",
13679
- "rounded-md": "0.375rem",
13680
- "rounded-lg": "0.5rem",
13681
- "rounded-xl": "0.75rem",
13682
- "rounded-2xl": "1rem",
13683
- "rounded-3xl": "1.5rem",
13684
- "rounded-full": "9999px"
13685
- };
13686
- ELEVATION_OFFSET = {
13687
- none: 0,
13688
- low: 2,
13689
- medium: 4,
13690
- high: 6
13691
- };
13692
- BORDER_WIDTH_MAP$4 = {
13693
- "border-0": "0px",
13694
- "border": "1px",
13695
- "border-2": "2px",
13696
- "border-4": "4px",
13697
- "border-8": "8px"
13698
- };
13699
- FONT_SIZE_MAP = {
13700
- "text-xs": "0.75rem",
13701
- "text-sm": "0.875rem",
13702
- "text-base": "1rem",
13703
- "text-lg": "1.125rem",
13704
- "text-xl": "1.25rem",
13705
- "text-2xl": "1.5rem"
13706
- };
13707
- DEFAULT_BUTTON_SIZES = {
13708
- sm: {
13709
- padding: "0.5rem 1rem",
13710
- fontSize: "text-sm"
13711
- },
13712
- md: { padding: "0.75rem 1.5rem" },
13713
- lg: {
13714
- padding: "1rem 2rem",
13715
- fontSize: "text-lg"
13716
- },
13717
- xl: {
13718
- padding: "1.25rem 2.5rem",
13719
- fontSize: "text-xl"
13720
- }
13721
- };
13722
- }));
13723
- //#endregion
13724
14170
  //#region ../theme-core/src/buttons/effects/presets/transform.ts
13725
14171
  var scaleUpEffect, scaleDownEffect, liftEffect, pressDownEffect, popEffect;
13726
14172
  var init_transform = __esmMin((() => {
@@ -13740,6 +14186,7 @@ var init_transform = __esmMin((() => {
13740
14186
  default: "1.05",
13741
14187
  options: [
13742
14188
  "1.02",
14189
+ "1.03",
13743
14190
  "1.05",
13744
14191
  "1.10"
13745
14192
  ]
@@ -13864,7 +14311,7 @@ var init_overlay = __esmMin((() => {
13864
14311
  description: "Blurred gradient glow that appears on hover",
13865
14312
  category: "overlay",
13866
14313
  pseudoElement: "before",
13867
- applicableTo: ["button"],
14314
+ applicableTo: [],
13868
14315
  customizableOption: {
13869
14316
  name: "intensity",
13870
14317
  type: "percentage",
@@ -13922,7 +14369,7 @@ var init_overlay = __esmMin((() => {
13922
14369
  description: "Rotating rainbow gradient glow (always visible, intensifies on hover)",
13923
14370
  category: "overlay",
13924
14371
  pseudoElement: "before",
13925
- applicableTo: ["button"],
14372
+ applicableTo: [],
13926
14373
  customizableOption: {
13927
14374
  name: "speed",
13928
14375
  type: "select",
@@ -13989,7 +14436,7 @@ var init_overlay = __esmMin((() => {
13989
14436
  description: "Gradient border that fills on hover using ::before pseudo-element",
13990
14437
  category: "overlay",
13991
14438
  pseudoElement: "before",
13992
- applicableTo: ["button"],
14439
+ applicableTo: [],
13993
14440
  customizableOption: {
13994
14441
  name: "borderWidth",
13995
14442
  type: "number",
@@ -14038,7 +14485,7 @@ var init_overlay = __esmMin((() => {
14038
14485
  description: "Rotating gradient border that fills on hover",
14039
14486
  category: "overlay",
14040
14487
  pseudoElement: "before",
14041
- applicableTo: ["button"],
14488
+ applicableTo: [],
14042
14489
  customizableOption: {
14043
14490
  name: "speed",
14044
14491
  type: "select",
@@ -14112,15 +14559,13 @@ var init_overlay = __esmMin((() => {
14112
14559
  applicableTo: ["button", "nav-link"],
14113
14560
  customizableOption: {
14114
14561
  name: "height",
14115
- type: "number",
14562
+ type: "select",
14116
14563
  label: "Border Height (px)",
14117
- default: 1
14564
+ default: 1,
14565
+ options: [1, 2]
14118
14566
  },
14119
14567
  generateCSS: (ctx) => {
14120
- const { themeId, variantId, options, tokens } = ctx;
14121
- const height = options.height || 1;
14122
- const colorToken = options.colorToken || "neutral-400";
14123
- const origin = options.origin || "left";
14568
+ const { themeId, variantId, options } = ctx;
14124
14569
  return `
14125
14570
  /* Setup button for pseudo-element */
14126
14571
  :where([data-theme-scope="${themeId}"]) .${variantId} {
@@ -14134,10 +14579,10 @@ var init_overlay = __esmMin((() => {
14134
14579
  bottom: 0;
14135
14580
  left: 0;
14136
14581
  width: 100%;
14137
- height: ${height}px;
14138
- background: ${tokens.getColor(colorToken)};
14582
+ height: ${options.height || 1}px;
14583
+ background: currentColor;
14139
14584
  transform: scaleX(0);
14140
- transform-origin: ${origin};
14585
+ transform-origin: ${options.origin || "left"};
14141
14586
  transition: transform 300ms ease-in-out;
14142
14587
  }
14143
14588
 
@@ -14159,7 +14604,7 @@ var init_text = __esmMin((() => {
14159
14604
  description: "Gradient text using background-clip",
14160
14605
  category: "text",
14161
14606
  pseudoElement: "none",
14162
- applicableTo: ["button"],
14607
+ applicableTo: [],
14163
14608
  customizableOption: {
14164
14609
  name: "solidOnHover",
14165
14610
  type: "select",
@@ -14202,7 +14647,7 @@ var init_text = __esmMin((() => {
14202
14647
  description: "Changes text color on hover",
14203
14648
  category: "text",
14204
14649
  pseudoElement: "none",
14205
- applicableTo: ["button", "nav-link"],
14650
+ applicableTo: ["nav-link"],
14206
14651
  customizableOption: {
14207
14652
  name: "hoverColorToken",
14208
14653
  type: "select",
@@ -14238,7 +14683,7 @@ var init_border = __esmMin((() => {
14238
14683
  description: "Changes border color on hover",
14239
14684
  category: "border",
14240
14685
  pseudoElement: "none",
14241
- applicableTo: ["button", "nav-link"],
14686
+ applicableTo: ["nav-link"],
14242
14687
  customizableOption: {
14243
14688
  name: "hoverColorToken",
14244
14689
  type: "select",
@@ -14292,8 +14737,7 @@ var init_border = __esmMin((() => {
14292
14737
  medium: "rotate(-1deg)",
14293
14738
  strong: "rotate(-1.5deg)"
14294
14739
  }[intensity]};
14295
- filter: contrast(0.95) brightness(1.02);
14296
- transition: transform 0.2s ease, filter 0.2s ease;
14740
+ transition: transform 0.2s ease;
14297
14741
  }
14298
14742
 
14299
14743
  :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
@@ -14302,7 +14746,6 @@ var init_border = __esmMin((() => {
14302
14746
  medium: "rotate(1deg)",
14303
14747
  strong: "rotate(1.5deg)"
14304
14748
  }[intensity]} scale(1.02);
14305
- filter: contrast(1) brightness(1);
14306
14749
  }
14307
14750
 
14308
14751
  :where([data-theme-scope="${themeId}"]) .${variantId}:active {
@@ -14322,7 +14765,7 @@ var init_animation = __esmMin((() => {
14322
14765
  description: "Continuous pulse animation (opacity fade)",
14323
14766
  category: "animation",
14324
14767
  pseudoElement: "none",
14325
- applicableTo: ["button", "nav-link"],
14768
+ applicableTo: ["nav-link"],
14326
14769
  customizableOption: {
14327
14770
  name: "speed",
14328
14771
  type: "select",
@@ -14518,6 +14961,7 @@ var init_registry$1 = __esmMin((() => {
14518
14961
  init_border();
14519
14962
  init_animation();
14520
14963
  init_effects();
14964
+ init_effectClassification();
14521
14965
  EFFECT_PRESETS = [
14522
14966
  darkenBackgroundEffect,
14523
14967
  lightenBackgroundEffect,
@@ -14619,11 +15063,11 @@ function narrowCornerStyle(corners) {
14619
15063
  /**
14620
15064
  * Generate default button system from theme settings.
14621
15065
  *
14622
- * Creates 4 sized variants when theme.buttons is not defined:
14623
- * 1. Primary - primary color background with opacity-reduce hover effect
14624
- * 2. Secondary - neutral-700 background with opacity-reduce hover effect
14625
- * 3. Outline - transparent background with primary border and opacity-reduce hover effect
14626
- * 4. Ghost - transparent background, no border, with opacity-reduce hover effect
15066
+ * Creates 4 sized canonical variants when theme.buttons is not defined:
15067
+ * 1. Primary - solid primary treatment
15068
+ * 2. Secondary - solid neutral treatment
15069
+ * 3. Outline - surface treatment with a border
15070
+ * 4. Ghost - surface treatment without a border
14627
15071
  *
14628
15072
  * All four are sized variants — they inherit padding/fontSize from the theme's
14629
15073
  * `sizes` config (or the system defaults) so they're consistent at any size.
@@ -14700,7 +15144,6 @@ function generateDefaultButtonSystem(theme) {
14700
15144
  cornerStyle: narrowCornerStyle(theme.corners),
14701
15145
  textTransform: "none",
14702
15146
  fontWeight: 500,
14703
- borderWidth: theme.border.width === "none" ? 1 : theme.border.width === "thick" ? 3 : 2,
14704
15147
  hoverTransform: "none",
14705
15148
  hoverColor: "none"
14706
15149
  },
@@ -14709,7 +15152,7 @@ function generateDefaultButtonSystem(theme) {
14709
15152
  }
14710
15153
  var BORDER_WIDTH_TO_CLASS_MAP, VALID_CORNER_STYLES;
14711
15154
  var init_generateDefaultButtonSystem = __esmMin((() => {
14712
- init_types$12();
15155
+ init_types$11();
14713
15156
  BORDER_WIDTH_TO_CLASS_MAP = {
14714
15157
  none: "border-0",
14715
15158
  hairline: "border",
@@ -14745,375 +15188,6 @@ var init_affordanceIcons = __esmMin((() => {
14745
15188
  };
14746
15189
  }));
14747
15190
  //#endregion
14748
- //#region ../theme-core/src/buttons/resolveButtonColourStates.ts
14749
- function compileButtonColourStates(input) {
14750
- const baseTransition = applyColourEffects({
14751
- variantId: input.variant.id,
14752
- state: "base",
14753
- treatment: input.variant.background,
14754
- effects: input.variant.effects?.base,
14755
- palette: input.palette
14756
- });
14757
- if (!baseTransition.ok) return baseTransition;
14758
- const stateTreatments = {
14759
- base: baseTransition.treatment,
14760
- hover: hoverTreatment(input.variant, input.global, baseTransition.treatment),
14761
- focus: baseTransition.treatment,
14762
- active: baseTransition.treatment,
14763
- disabled: baseTransition.treatment
14764
- };
14765
- const nonColourEffects = { ...baseTransition.nonColourEffects.length > 0 ? { base: [...baseTransition.nonColourEffects] } : {} };
14766
- for (const state of [
14767
- "hover",
14768
- "focus",
14769
- "active"
14770
- ]) {
14771
- const transition = applyColourEffects({
14772
- variantId: input.variant.id,
14773
- state,
14774
- treatment: stateTreatments[state],
14775
- effects: input.variant.effects?.[state],
14776
- palette: input.palette
14777
- });
14778
- if (!transition.ok) return transition;
14779
- stateTreatments[state] = transition.treatment;
14780
- if (transition.nonColourEffects.length > 0) nonColourEffects[state] = [...transition.nonColourEffects];
14781
- }
14782
- const resolved = {};
14783
- for (const state of BUTTON_VISUAL_STATES) {
14784
- const result = resolveTreatment$1({
14785
- variantId: input.variant.id,
14786
- state,
14787
- treatment: stateTreatments[state],
14788
- palette: input.palette
14789
- });
14790
- if (!result.ok) return result;
14791
- resolved[state] = result.colour;
14792
- }
14793
- return {
14794
- ok: true,
14795
- states: resolved,
14796
- focusIndicator: resolveFocusIndicator(resolved.focus),
14797
- nonColourEffects
14798
- };
14799
- }
14800
- function formatButtonColourContractError(error) {
14801
- switch (error.tag) {
14802
- case "unknown_palette_token": return `Button "${error.variantId}" ${error.state} state uses unknown palette token "${error.token}".`;
14803
- case "missing_on_colour": return `Button "${error.variantId}" ${error.state} state cannot derive ${error.foregroundToken} from ${error.backgroundToken}.`;
14804
- case "insufficient_contrast": return `Button "${error.variantId}" ${error.state} state has ${error.ratio.toFixed(2)}:1 contrast (${error.requiredRatio}:1 required).`;
14805
- case "unsupported_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" is not contrast-safe (${error.reason}).`;
14806
- case "invalid_colour_effect_options": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" has an invalid ${error.option} option.`;
14807
- case "inapplicable_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" cannot apply to a ${error.treatment} treatment.`;
14808
- case "unknown_button_effect": return `Button "${error.variantId}" ${error.state} uses unknown effect "${error.effectId}".`;
14809
- }
14810
- }
14811
- function hoverTreatment(variant, global, fallback) {
14812
- const token = variant.hoverBackgroundToken ?? (global.hoverColor === "token" ? global.hoverColorToken : void 0);
14813
- return token === void 0 ? fallback : {
14814
- type: "solid",
14815
- colorToken: token
14816
- };
14817
- }
14818
- function applyColourEffects(input) {
14819
- let treatment = input.treatment;
14820
- const nonColourEffects = [];
14821
- for (const effect of input.effects ?? []) {
14822
- const policy = getButtonEffectPolicy(effect.effectId);
14823
- if (policy === null) return unknownEffect(input, effect);
14824
- switch (policy.classification) {
14825
- case "non-colour":
14826
- nonColourEffects.push(effect);
14827
- break;
14828
- case "unsupported-colour":
14829
- case "unsupported-background-overlay":
14830
- case "not-button": return unsupportedEffect(input, effect, policy.reason);
14831
- case "colour-transition":
14832
- if (policy.transition.kind === "adjust-shade") {
14833
- if (treatment.type !== "solid") return inapplicableEffect(input, effect, treatment);
14834
- const amount = readShadeAmount(effect);
14835
- if (amount === null) return invalidEffectOption(input, effect, "amount");
14836
- const signedAmount = policy.transition.direction === "darker" ? amount : -amount;
14837
- treatment = {
14838
- type: "solid",
14839
- colorToken: adjustShade(treatment.colorToken, signedAmount, input.palette)
14840
- };
14841
- break;
14842
- }
14843
- if (policy.transition.kind === "replace-background") {
14844
- const colorToken = readOpaqueToken(effect, "colorToken", "neutral-800");
14845
- if (colorToken === null) return invalidEffectOption(input, effect, "colorToken");
14846
- treatment = {
14847
- type: "solid",
14848
- colorToken
14849
- };
14850
- break;
14851
- }
14852
- return unknownEffect(input, effect);
14853
- }
14854
- }
14855
- return {
14856
- ok: true,
14857
- treatment,
14858
- nonColourEffects
14859
- };
14860
- }
14861
- function resolveTreatment$1(input) {
14862
- const treatment = input.treatment;
14863
- switch (treatment.type) {
14864
- case "surface": return {
14865
- ok: true,
14866
- colour: {
14867
- kind: "surface",
14868
- role: treatment.role,
14869
- backgroundCss: "transparent",
14870
- foregroundCss: surfaceForegroundValue(treatment.role)
14871
- }
14872
- };
14873
- case "solid": return resolveSolid(input, treatment.colorToken);
14874
- case "gradient": return resolveGradient$2(input, treatment);
14875
- }
14876
- }
14877
- function resolveFocusIndicator(colour) {
14878
- switch (colour.kind) {
14879
- case "solid": return {
14880
- kind: "two-tone",
14881
- innerCss: colour.foregroundCss,
14882
- outerCss: colour.backgroundCss,
14883
- minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
14884
- };
14885
- case "gradient": return {
14886
- kind: "two-tone",
14887
- innerCss: colour.foregroundCss,
14888
- outerCss: tokenCss(colour.backgroundTokens[0]),
14889
- minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
14890
- };
14891
- case "surface": return {
14892
- kind: "surface-role",
14893
- colorCss: colour.foregroundCss,
14894
- minimumSurfaceContrast: BUTTON_CONTRAST_POLICY.nonTextMinimumRatio
14895
- };
14896
- }
14897
- }
14898
- function resolveSolid(input, backgroundToken) {
14899
- const backgroundHex = paletteHex(input, backgroundToken);
14900
- if (!backgroundHex.ok) return backgroundHex;
14901
- const foregroundToken = unsafeAsOpaqueButtonColourToken(onColorTokenName(backgroundToken));
14902
- const foregroundHex = input.palette[foregroundToken];
14903
- if (typeof foregroundHex !== "string") return {
14904
- ok: false,
14905
- error: {
14906
- tag: "missing_on_colour",
14907
- variantId: input.variantId,
14908
- state: input.state,
14909
- backgroundToken,
14910
- foregroundToken
14911
- }
14912
- };
14913
- const contrast = validateContrast({
14914
- ...input,
14915
- background: backgroundToken,
14916
- backgroundHex: backgroundHex.hex,
14917
- foreground: foregroundToken,
14918
- foregroundHex
14919
- });
14920
- if (!contrast.ok) return contrast;
14921
- return {
14922
- ok: true,
14923
- colour: {
14924
- kind: "solid",
14925
- backgroundToken,
14926
- foregroundToken,
14927
- backgroundCss: tokenCss(backgroundToken),
14928
- foregroundCss: tokenCss(foregroundToken)
14929
- }
14930
- };
14931
- }
14932
- function resolveGradient$2(input, treatment) {
14933
- const foregroundHex = paletteHex(input, treatment.foregroundToken);
14934
- if (!foregroundHex.ok) return foregroundHex;
14935
- const stopHexes = [];
14936
- for (const token of treatment.stops) {
14937
- const stop = paletteHex(input, token);
14938
- if (!stop.ok) return stop;
14939
- stopHexes.push(stop.hex);
14940
- }
14941
- for (let segment = 0; segment < stopHexes.length - 1; segment += 1) {
14942
- const start = stopHexes[segment];
14943
- const end = stopHexes[segment + 1];
14944
- const startToken = treatment.stops[segment];
14945
- const endToken = treatment.stops[segment + 1];
14946
- if (start === void 0 || end === void 0 || startToken === void 0 || endToken === void 0) continue;
14947
- for (let sample = 0; sample <= BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment; sample += 1) {
14948
- const position = sample / BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment;
14949
- const sampleHex = interpolateHex(start, end, position);
14950
- const contrast = validateContrast({
14951
- ...input,
14952
- background: `${startToken}→${endToken}@${position.toFixed(2)}`,
14953
- backgroundHex: sampleHex,
14954
- foreground: treatment.foregroundToken,
14955
- foregroundHex: foregroundHex.hex
14956
- });
14957
- if (!contrast.ok) return contrast;
14958
- }
14959
- }
14960
- const direction = GRADIENT_DIRECTION_MAP$2[treatment.direction] ?? "to right";
14961
- return {
14962
- ok: true,
14963
- colour: {
14964
- kind: "gradient",
14965
- backgroundTokens: treatment.stops,
14966
- foregroundToken: treatment.foregroundToken,
14967
- backgroundCss: `linear-gradient(${direction}, ${treatment.stops.map(tokenCss).join(", ")})`,
14968
- foregroundCss: tokenCss(treatment.foregroundToken)
14969
- }
14970
- };
14971
- }
14972
- function paletteHex(input, token) {
14973
- const hex = input.palette[token];
14974
- return typeof hex === "string" ? {
14975
- ok: true,
14976
- hex
14977
- } : {
14978
- ok: false,
14979
- error: {
14980
- tag: "unknown_palette_token",
14981
- variantId: input.variantId,
14982
- state: input.state,
14983
- token
14984
- }
14985
- };
14986
- }
14987
- function validateContrast(input) {
14988
- const ratio = contrastRatio(input.backgroundHex, input.foregroundHex);
14989
- return ratio >= BUTTON_CONTRAST_POLICY.textMinimumRatio ? { ok: true } : {
14990
- ok: false,
14991
- error: {
14992
- tag: "insufficient_contrast",
14993
- variantId: input.variantId,
14994
- state: input.state,
14995
- background: input.background,
14996
- foreground: input.foreground,
14997
- ratio,
14998
- requiredRatio: BUTTON_CONTRAST_POLICY.textMinimumRatio
14999
- }
15000
- };
15001
- }
15002
- function tokenCss(token) {
15003
- return `rgb(var(--tb-${token}))`;
15004
- }
15005
- function adjustShade(token, amount, palette) {
15006
- const match = token.match(/^(.*)-(\d{2,3})$/);
15007
- const base = match?.[1] ?? token;
15008
- const currentShade = match?.[2] === void 0 ? 500 : Number(match[2]);
15009
- const availableShades = [
15010
- 50,
15011
- 100,
15012
- 200,
15013
- 300,
15014
- 400,
15015
- 500,
15016
- 600,
15017
- 700,
15018
- 800,
15019
- 900,
15020
- 950
15021
- ].filter((shade) => typeof palette[`${base}-${shade}`] === "string");
15022
- if (availableShades.length === 0) return token;
15023
- const firstShade = availableShades[0];
15024
- if (firstShade === void 0) return token;
15025
- const exactIndex = availableShades.indexOf(currentShade);
15026
- const startingIndex = exactIndex >= 0 ? exactIndex : availableShades.indexOf(availableShades.reduce((closestShade, shade) => Math.abs(shade - currentShade) < Math.abs(closestShade - currentShade) ? shade : closestShade, firstShade));
15027
- const stepCount = Math.abs(amount) / 100;
15028
- const direction = amount < 0 ? -1 : 1;
15029
- const targetShade = availableShades[Math.max(0, Math.min(availableShades.length - 1, startingIndex + direction * stepCount))];
15030
- return targetShade === void 0 ? token : unsafeAsOpaqueButtonColourToken(`${base}-${targetShade}`);
15031
- }
15032
- function readShadeAmount(effect) {
15033
- const value = effect.options?.amount ?? 100;
15034
- return typeof value === "number" && Number.isFinite(value) && value >= 0 && value % 100 === 0 ? value : null;
15035
- }
15036
- function readOpaqueToken(effect, key, fallback) {
15037
- return parseOpaqueButtonColourToken(effect.options?.[key] ?? fallback);
15038
- }
15039
- function unsupportedEffect(input, effect, reason) {
15040
- return {
15041
- ok: false,
15042
- error: {
15043
- tag: "unsupported_colour_effect",
15044
- variantId: input.variantId,
15045
- state: input.state,
15046
- effectId: effect.effectId,
15047
- reason
15048
- }
15049
- };
15050
- }
15051
- function invalidEffectOption(input, effect, option) {
15052
- return {
15053
- ok: false,
15054
- error: {
15055
- tag: "invalid_colour_effect_options",
15056
- variantId: input.variantId,
15057
- state: input.state,
15058
- effectId: effect.effectId,
15059
- option
15060
- }
15061
- };
15062
- }
15063
- function inapplicableEffect(input, effect, treatment) {
15064
- return {
15065
- ok: false,
15066
- error: {
15067
- tag: "inapplicable_colour_effect",
15068
- variantId: input.variantId,
15069
- state: input.state,
15070
- effectId: effect.effectId,
15071
- treatment: treatment.type
15072
- }
15073
- };
15074
- }
15075
- function unknownEffect(input, effect) {
15076
- return {
15077
- ok: false,
15078
- error: {
15079
- tag: "unknown_button_effect",
15080
- variantId: input.variantId,
15081
- state: input.state,
15082
- effectId: effect.effectId
15083
- }
15084
- };
15085
- }
15086
- function interpolateHex(startHex, endHex, position) {
15087
- const start = hexToRgb$1(startHex);
15088
- const end = hexToRgb$1(endHex);
15089
- const channel = (from, to) => Math.round(from + (to - from) * position).toString(16).padStart(2, "0");
15090
- return `#${channel(start.r, end.r)}${channel(start.g, end.g)}${channel(start.b, end.b)}`;
15091
- }
15092
- var BUTTON_VISUAL_STATES, BUTTON_CONTRAST_POLICY;
15093
- var init_resolveButtonColourStates = __esmMin((() => {
15094
- init_contrast$1();
15095
- init_colorConversion();
15096
- init_onColor();
15097
- init_foregroundContract();
15098
- init_types$12();
15099
- init_constants$2();
15100
- init_effectClassification();
15101
- BUTTON_VISUAL_STATES = [
15102
- "base",
15103
- "hover",
15104
- "focus",
15105
- "active",
15106
- "disabled"
15107
- ];
15108
- BUTTON_CONTRAST_POLICY = {
15109
- textMinimumRatio: 4.5,
15110
- nonTextMinimumRatio: 3,
15111
- gradientSamplesPerSegment: 20,
15112
- alphaBackgrounds: "reject-without-known-surface",
15113
- disabled: "retain-full-colour-contract"
15114
- };
15115
- }));
15116
- //#endregion
15117
15191
  //#region ../theme-core/src/buttons/resolvedButtonCss.ts
15118
15192
  /**
15119
15193
  * Canonical colour declaration mapping shared by full themes and ThemeBridge.
@@ -15322,8 +15396,8 @@ function variantChunks(variant, global, themeSizes, themeId, tokens, theme, colo
15322
15396
  * Only variants that declare `affordance` with a known icon id produce a rule —
15323
15397
  * every other variant is byte-identical to before. The `:not(.rb-affordance-off)`
15324
15398
  * guard lets a `button` node that renders its own trailing icon suppress this
15325
- * one (node overrides personality). Colour comes from the affordance token, or
15326
- * `currentColor` (the button text colour) when no token is given.
15399
+ * one. The mask uses `currentColor`, so the icon always shares the canonical
15400
+ * button foreground.
15327
15401
  */
15328
15402
  function variantAffordanceRule(variant, selector) {
15329
15403
  const affordance = variant.affordance;
@@ -15483,7 +15557,7 @@ function resolveBoxShadow(variant, global, theme, tokens) {
15483
15557
  }
15484
15558
  var AFFORDANCE_SUPPRESS_CLASS, BUTTON_SIZE_NAMES, LETTER_SPACING_MAP, STRUCTURAL_DECLARATIONS;
15485
15559
  var init_generateButtonCss = __esmMin((() => {
15486
- init_types$12();
15560
+ init_types$11();
15487
15561
  init_classNames();
15488
15562
  init_resolver$1();
15489
15563
  init_generateBaseCSS();
@@ -15540,17 +15614,10 @@ var init_presets$1 = __esmMin((() => {
15540
15614
  });
15541
15615
  }));
15542
15616
  //#endregion
15543
- //#region ../theme-core/src/buttons/effects/types.ts
15544
- var init_types$10 = __esmMin((() => {}));
15545
- var init_core$1 = __esmMin((() => {
15546
- init_resolveButtonColourStates();
15547
- init_resolvedButtonCss();
15548
- init_types$12();
15549
- }));
15550
- //#endregion
15551
15617
  //#region ../theme-core/src/buttons/index.ts
15552
15618
  var init_buttons = __esmMin((() => {
15553
- init_types$12();
15619
+ init_types$11();
15620
+ init_parseThemeForPersistence();
15554
15621
  init_classNames();
15555
15622
  init_generateButtonCss();
15556
15623
  init_resolveButtonColourStates();
@@ -15559,10 +15626,6 @@ var init_buttons = __esmMin((() => {
15559
15626
  init_constants$2();
15560
15627
  init_presets$1();
15561
15628
  init_styles();
15562
- init_types$10();
15563
- init_registry$1();
15564
- init_transform();
15565
- init_core$1();
15566
15629
  }));
15567
15630
  //#endregion
15568
15631
  //#region ../theme-core/src/cards/types.ts
@@ -15711,9 +15774,9 @@ function generateShadowValue(shadow) {
15711
15774
  case "high": return soft ? `${xOffset}px 25px 50px rgba(0,0,0,.18)` : `${xOffset}px 20px 30px rgba(0,0,0,.28)`;
15712
15775
  }
15713
15776
  }
15714
- var CORNER_RADIUS_MAP$2, GRADIENT_DIRECTION_MAP$1;
15777
+ var CORNER_RADIUS_MAP$3, GRADIENT_DIRECTION_MAP$1;
15715
15778
  var init_constants$1 = __esmMin((() => {
15716
- CORNER_RADIUS_MAP$2 = {
15779
+ CORNER_RADIUS_MAP$3 = {
15717
15780
  square: "0px",
15718
15781
  soft: "8px",
15719
15782
  rounded: "16px",
@@ -15797,7 +15860,7 @@ var init_generateDefaultCardSystem = __esmMin((() => {}));
15797
15860
  //#endregion
15798
15861
  //#region ../theme-core/src/cards/generateCardCss.ts
15799
15862
  function resolveCornerRadiiCss$1(cornerRadii, fallback) {
15800
- const fallbackValue = CORNER_RADIUS_MAP$2[fallback];
15863
+ const fallbackValue = CORNER_RADIUS_MAP$3[fallback];
15801
15864
  const at = (value) => value ? PER_CORNER_RADIUS_MAP$1[value] : fallbackValue;
15802
15865
  return `${at(cornerRadii.topLeft)} ${at(cornerRadii.topRight)} ${at(cornerRadii.bottomRight)} ${at(cornerRadii.bottomLeft)}`;
15803
15866
  }
@@ -15855,7 +15918,7 @@ function generateVariantCSS(variant, themeId, tokens, theme) {
15855
15918
  rules.push(`border: ${borderWidth} solid ${borderColor}`);
15856
15919
  }
15857
15920
  const corners = variant.corners ?? theme.corners;
15858
- const radius = variant.cornerRadii ? resolveCornerRadiiCss$1(variant.cornerRadii, corners) : CORNER_RADIUS_MAP$2[corners];
15921
+ const radius = variant.cornerRadii ? resolveCornerRadiiCss$1(variant.cornerRadii, corners) : CORNER_RADIUS_MAP$3[corners];
15859
15922
  rules.push(`border-radius: ${radius}`);
15860
15923
  const shadow = variant.shadow ?? theme.shadow;
15861
15924
  if (shadow.elevation !== "none") {
@@ -16763,12 +16826,12 @@ var init_schema$1 = __esmMin((() => {
16763
16826
  init_cards();
16764
16827
  init_types$8();
16765
16828
  init_types$7();
16766
- init_types$11();
16829
+ init_types$10();
16767
16830
  init_types$6();
16768
16831
  init_validation$2();
16769
16832
  init_atRuleValidation();
16770
16833
  init_shared$5();
16771
- init_types$15();
16834
+ init_types$14();
16772
16835
  init_logoBranding();
16773
16836
  init_rawStyleFields();
16774
16837
  init_themeColorTokens();
@@ -17626,7 +17689,7 @@ function uniqueNonEmpty$1(values) {
17626
17689
  var designResolutionPolicySchema, runtimeStyleFieldNameSchema, nonEmptyRuntimeStyleFieldNamesSchema, heroLegibilityStrategySchema, heroCompositionIntentSchema, heroTypographyScaleIntentSchema, sectionHeightIntentSchema, imageSurfaceTreatmentIntensitySchema, imageSurfaceMotionSchema, imageSurfaceTreatmentSchema, testimonialCardTreatmentSchema, tileTreatmentSchema, eventListTreatmentSchema, eyebrowTreatmentSchema, userPanelTreatments, userFlushTreatments, userFlushBleeds, contentFrameIntentSchema, blockDesignPlanSchema, styleAuthoritySchema, blockRuntimeStyleAuthoritySchema;
17627
17690
  var init_designState = __esmMin((() => {
17628
17691
  init_sourceCatalogs();
17629
- init_types$15();
17692
+ init_types$14();
17630
17693
  init_schemaVersionedJson();
17631
17694
  init_rawStyleFields();
17632
17695
  designResolutionPolicySchema = z$1.enum([
@@ -17805,7 +17868,7 @@ var init_runtimeCompiler = __esmMin((() => {
17805
17868
  init_styles();
17806
17869
  init_colors();
17807
17870
  init_designState();
17808
- init_types$15();
17871
+ init_types$14();
17809
17872
  init_accessibleSurfaces();
17810
17873
  init_sectionTransitions();
17811
17874
  init_tokens();
@@ -18224,7 +18287,7 @@ var init_curatedSiteStyles = __esmMin((() => {
18224
18287
  init_footerLooks();
18225
18288
  init_curatedChoices();
18226
18289
  init_sourceCatalogs();
18227
- init_types$15();
18290
+ init_types$14();
18228
18291
  init_siteStyleTypography();
18229
18292
  init_runtimeCompiler();
18230
18293
  SOFT_EARTH_SITE_STYLE_ID_VALUE = "site-style:soft-earth";
@@ -19638,13 +19701,13 @@ var init_curatedSiteStyles = __esmMin((() => {
19638
19701
  curatedSiteStylesById = new Map(curatedSiteStyles.map((style) => [style.id, style]));
19639
19702
  })), init_lookResolution = __esmMin((() => {
19640
19703
  init_appearancePresets();
19641
- init_types$15();
19704
+ init_types$14();
19642
19705
  }));
19643
19706
  var init_siteStyleBlockPolicies = __esmMin((() => {
19644
19707
  init_lookResolution();
19645
19708
  init_appearancePresets();
19646
19709
  init_pageDesignIntent();
19647
- init_types$15();
19710
+ init_types$14();
19648
19711
  init_sectionTransitions();
19649
19712
  asPageFlowPresetId("generated-default");
19650
19713
  asSiteStyleId("site-style:south-west-twerk-school");
@@ -19657,12 +19720,12 @@ var init_siteStyleBlockPolicies = __esmMin((() => {
19657
19720
  init_lookResolution();
19658
19721
  init_siteStyleBlockPolicies();
19659
19722
  init_pageDesignIntent();
19660
- init_types$15();
19723
+ init_types$14();
19661
19724
  init_sectionTransitions();
19662
19725
  asPageFlowPresetId("flow:quiet"), noneBoundaryOption.id, asPageFlowPresetId("flow:balanced"), softBoundaryOption.id, asPageFlowPresetId("flow:expressive"), spaciousBoundaryOption.id;
19663
19726
  }));
19664
19727
  var init_resolver = __esmMin((() => {
19665
- init_types$15();
19728
+ init_types$14();
19666
19729
  }));
19667
19730
  var init_siteStyleThemeCompiler = __esmMin((() => {
19668
19731
  init_schema$1();
@@ -19672,7 +19735,7 @@ var init_siteStyleThemeCompiler = __esmMin((() => {
19672
19735
  init_resolveSelection();
19673
19736
  }));
19674
19737
  var init_previewCustomization = __esmMin((() => {
19675
- init_types$15();
19738
+ init_types$14();
19676
19739
  }));
19677
19740
  var init_previewCompiler = __esmMin((() => {
19678
19741
  init_schema$1();
@@ -19682,7 +19745,7 @@ var init_previewCompiler = __esmMin((() => {
19682
19745
  init_resolveSelection();
19683
19746
  init_curatedSiteStyles();
19684
19747
  init_resolver();
19685
- init_types$15();
19748
+ init_types$14();
19686
19749
  init_footerLooks();
19687
19750
  init_headerLooks();
19688
19751
  init_runtimeCompiler();
@@ -19721,7 +19784,7 @@ var generatedPanelTreatments, generatedFlushTreatments, generatedFlushBleeds, ge
19721
19784
  var init_generatedSchemas = __esmMin((() => {
19722
19785
  init_curatedSiteStyles();
19723
19786
  init_sourceCatalogs();
19724
- init_types$15();
19787
+ init_types$14();
19725
19788
  generatedPanelTreatments = uniqueNonEmpty(generatedDesignOptionCatalog.contentFrames.filter((frame) => frame.kind === "panel").map((frame) => frame.treatment));
19726
19789
  generatedFlushTreatments = uniqueNonEmpty(generatedDesignOptionCatalog.contentFrames.filter((frame) => frame.kind === "flush-panel").map((frame) => frame.treatment));
19727
19790
  generatedFlushBleeds = uniqueNonEmpty(generatedDesignOptionCatalog.contentFrames.filter((frame) => frame.kind === "flush-panel").map((frame) => frame.bleed));
@@ -19788,7 +19851,7 @@ var init_generatedDesignPipeline = __esmMin((() => {
19788
19851
  init_headerOpeningSection();
19789
19852
  init_resolver();
19790
19853
  init_runtimeCompiler();
19791
- init_types$15();
19854
+ init_types$14();
19792
19855
  }));
19793
19856
  function validateBlockSequence(snapshots, ctx) {
19794
19857
  for (const issue of collectBlockSequenceIssues(snapshots)) ctx.addIssue(issue);
@@ -19823,7 +19886,7 @@ function collectBlockSequenceIssues(snapshots) {
19823
19886
  }
19824
19887
  var nonEmptyStringSchema, blockContentVersionIdSchema, designBlockIdSchema, designPageIdSchema, designEntryIdSchema, blockOrderIndexSchema, publishedPageBlockSnapshotSchema, draftPageBlockSnapshotSchema, missingPageBlockSnapshotSchema, pageBlockSnapshotSchema, publishedPageBlockSequenceSchema, draftPageBlockSequenceSchema, publishedPageDesignVersionDataV1Schema, draftPageDesignVersionDataV1Schema;
19825
19888
  var init_pageDesignAuthority = __esmMin((() => {
19826
- init_types$15();
19889
+ init_types$14();
19827
19890
  init_pageDesignIntent();
19828
19891
  nonEmptyStringSchema = z$1.string().min(1);
19829
19892
  blockContentVersionIdSchema = nonEmptyStringSchema.transform(asBlockContentVersionId);
@@ -20119,7 +20182,7 @@ var init_site_styles = __esmMin((() => {
20119
20182
  init_tokenRecipes();
20120
20183
  init_siteStyleTypography();
20121
20184
  init_themeParity();
20122
- init_types$15();
20185
+ init_types$14();
20123
20186
  }));
20124
20187
  //#endregion
20125
20188
  //#region ../blocks/src/system/transforms/registry/_utils/themeColorResolution.ts
@@ -20186,7 +20249,7 @@ function isEventListTreatment(value) {
20186
20249
  }
20187
20250
  var spacingTransform, raisedTransform, roundedTransform, boxStylesCompositeTransform, cardVariantClassTransform, boxStylesBorderColorTransform, fixedFrameScopeClass, tileTreatmentClassMap, tileTreatmentSet, eventListTreatmentClassMap, eventListTreatmentSet, sectionClassNameTransform, containerStylesResolveClassName, containerStylesResolveBoxClassName, sectionColorsStyleTransform, GAP_CLASS_MAP, GAP_VALUE_MAP, semanticGapTransform, semanticGapValueTransform, boxStylesTransforms;
20188
20251
  var init_boxStyles$1 = __esmMin((() => {
20189
- init_core$2();
20252
+ init_core$1();
20190
20253
  init_spacing();
20191
20254
  init_site_styles();
20192
20255
  init_colorStyles();
@@ -21342,6 +21405,16 @@ function isDisplayMediaKind$2(kind) {
21342
21405
  function assertNeverSelectSource(value) {
21343
21406
  throw new Error(`Unhandled select source: ${JSON.stringify(value)}`);
21344
21407
  }
21408
+ function hasMediaDefaultIdentifier(value) {
21409
+ return value !== null && typeof value === "object" && !Array.isArray(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0;
21410
+ }
21411
+ function validateMediaFieldDefault(field, ctx) {
21412
+ if (field.defaultValue !== void 0 && field.defaultValue !== null && !hasMediaDefaultIdentifier(field.defaultValue)) ctx.addIssue({
21413
+ code: z$1.ZodIssueCode.custom,
21414
+ path: ["defaultValue"],
21415
+ message: `${field.type} defaultValue must be null or an object with a non-empty identifier`
21416
+ });
21417
+ }
21345
21418
  function getFieldSchemaInternal() {
21346
21419
  if (_fieldSchemaInternal) return _fieldSchemaInternal;
21347
21420
  _fieldSchemaInternal = z$1.discriminatedUnion("type", [
@@ -21418,7 +21491,7 @@ function createSdkSelectField(input) {
21418
21491
  }
21419
21492
  }));
21420
21493
  }
21421
- var visibilityLevels, MEDIA_KINDS, DISPLAY_MEDIA_KINDS$2, PUBLIC_FILE_FIELD_KINDS, siteOptionSources, mediaDeliverySchema, selectOptionSchema, selectSourceSchema, visibleWhenPredicateSchema, visibleWhenSchema, uiSchema, baseFieldSchema, textFieldSchema, richTextFieldSchema, inlineTextFieldSchema, mediaFieldSchema, fileFieldSchema, booleanFieldSchema, dateFieldSchema, timeFieldSchema, dateTimeFieldSchema, slugFieldSchema, urlFieldSchema, linkFieldSchema, selectFieldSchema, referenceFieldSchema, itemTypeSchema, repeaterFieldSchema, groupFieldSchema, modalFieldSchema, numberFieldSchema, tabDefinitionSchema, tabGroupFieldSchema, presetOptionSchema, presetOrCustomFieldSchema, contentTypeSelectFieldSchema, entryPickerFieldSchema, _fieldSchemaInternal, fieldSchema, slotSchema, typographyTokens, colorTokens, spacingTokens, radiusTokens, styleTokenSchema, behaviourSchema, quickFieldPrioritySchema, previewCustomizeManifestCurationSchema, blockCategoryEnum, blockContentSourceKinds, blockContentSourceSchema;
21494
+ var visibilityLevels, MEDIA_KINDS, DISPLAY_MEDIA_KINDS$2, DEFAULT_MEDIA_FIELD_KINDS, PUBLIC_FILE_FIELD_KINDS, siteOptionSources, mediaDeliverySchema, selectOptionSchema, selectSourceSchema, visibleWhenPredicateSchema, visibleWhenSchema, uiSchema, baseFieldSchema, textFieldSchema, richTextFieldSchema, inlineTextFieldSchema, mediaFieldSchema, fileFieldSchema, booleanFieldSchema, dateFieldSchema, timeFieldSchema, dateTimeFieldSchema, slugFieldSchema, urlFieldSchema, linkFieldSchema, selectFieldSchema, referenceFieldSchema, itemTypeSchema, repeaterFieldSchema, groupFieldSchema, modalFieldSchema, numberFieldSchema, tabDefinitionSchema, tabGroupFieldSchema, presetOptionSchema, presetOrCustomFieldSchema, contentTypeSelectFieldSchema, entryPickerFieldSchema, _fieldSchemaInternal, fieldSchema, slotSchema, typographyTokens, colorTokens, spacingTokens, radiusTokens, styleTokenSchema, behaviourSchema, quickFieldPrioritySchema, previewCustomizeManifestCurationSchema, blockCategoryEnum, blockContentSourceKinds, blockContentSourceSchema;
21422
21495
  var init_schema = __esmMin((() => {
21423
21496
  init_src$2();
21424
21497
  init_node();
@@ -21442,6 +21515,7 @@ var init_schema = __esmMin((() => {
21442
21515
  "video",
21443
21516
  "audio"
21444
21517
  ];
21518
+ DEFAULT_MEDIA_FIELD_KINDS = ["image"];
21445
21519
  PUBLIC_FILE_FIELD_KINDS = [
21446
21520
  "document",
21447
21521
  "spreadsheet",
@@ -21638,16 +21712,16 @@ var init_schema = __esmMin((() => {
21638
21712
  });
21639
21713
  mediaFieldSchema = baseFieldSchema.extend({
21640
21714
  type: z$1.literal("media"),
21641
- mediaKinds: z$1.array(z$1.enum(MEDIA_KINDS)).default(["image"]).transform((kinds) => {
21715
+ mediaKinds: z$1.array(z$1.enum(MEDIA_KINDS)).default([...DEFAULT_MEDIA_FIELD_KINDS]).transform((kinds) => {
21642
21716
  const displayKinds = kinds.filter(isDisplayMediaKind$2);
21643
- return displayKinds.length > 0 ? displayKinds : ["image"];
21717
+ return displayKinds.length > 0 ? displayKinds : [...DEFAULT_MEDIA_FIELD_KINDS];
21644
21718
  }),
21645
21719
  aspectRatio: z$1.string().optional()
21646
- });
21720
+ }).superRefine(validateMediaFieldDefault);
21647
21721
  fileFieldSchema = baseFieldSchema.extend({
21648
21722
  type: z$1.literal("file"),
21649
21723
  fileKinds: z$1.array(z$1.enum(PUBLIC_FILE_FIELD_KINDS)).default([...PUBLIC_FILE_FIELD_KINDS])
21650
- });
21724
+ }).superRefine(validateMediaFieldDefault);
21651
21725
  booleanFieldSchema = baseFieldSchema.extend({ type: z$1.literal("boolean") });
21652
21726
  dateFieldSchema = baseFieldSchema.extend({ type: z$1.literal("date") });
21653
21727
  timeFieldSchema = baseFieldSchema.extend({ type: z$1.literal("time") });
@@ -23124,45 +23198,6 @@ var init_formEmbed = __esmMin((() => {
23124
23198
  });
23125
23199
  }));
23126
23200
  //#endregion
23127
- //#region ../blocks/src/system/fragments/library/footerBottomText.ts
23128
- var footerBottomTextFragment;
23129
- var init_footerBottomText = __esmMin((() => {
23130
- init_types$4();
23131
- init_builder$2();
23132
- init_colorStyles();
23133
- footerBottomTextFragment = defineFragment({
23134
- id: "footerBottomText",
23135
- title: "Footer Text",
23136
- description: "Rich text content for the main footer body and optional bottom bar.",
23137
- fields: [{
23138
- id: "bodyText",
23139
- type: "richText",
23140
- label: "Body text",
23141
- description: "Appears in the main footer area.",
23142
- ui: { variant: "limited" }
23143
- }, {
23144
- id: "bottomText",
23145
- type: "richText",
23146
- label: "Bottom bar text",
23147
- description: "Appears in the optional footer bottom bar.",
23148
- ui: { variant: "limited" }
23149
- }],
23150
- layout: [richText({
23151
- className: "rb-prose rb-prose-sm rb-mx-auto rb-max-w-3xl rb-text-center",
23152
- style: textColorStyle("text/80")
23153
- }, when("bodyText"), bind("bodyText", { transforms: [{
23154
- id: "content.interpolateRichTextTokens",
23155
- options: { tokens: ["year", "site_name"] }
23156
- }] })), richText({
23157
- className: "rb-prose rb-prose-sm rb-mx-auto rb-max-w-3xl rb-text-center",
23158
- style: textColorStyle("text/80")
23159
- }, when("bottomText"), bind("bottomText", { transforms: [{
23160
- id: "content.interpolateRichTextTokens",
23161
- options: { tokens: ["year", "site_name"] }
23162
- }] }))]
23163
- });
23164
- }));
23165
- //#endregion
23166
23201
  //#region ../blocks/src/system/fragments/library/headerCta.ts
23167
23202
  var headerCtaFragment;
23168
23203
  var init_headerCta = __esmMin((() => {
@@ -23676,7 +23711,6 @@ var init_lazyInit = __esmMin((() => {
23676
23711
  init_testimonialsCarousel();
23677
23712
  init_formCopy();
23678
23713
  init_formEmbed();
23679
- init_footerBottomText();
23680
23714
  init_headerCta();
23681
23715
  init_singleButton();
23682
23716
  init_blogFeaturedPost();
@@ -23696,7 +23730,6 @@ var init_lazyInit = __esmMin((() => {
23696
23730
  testimonialsCarouselFragment,
23697
23731
  formCopyFragment,
23698
23732
  formEmbedFragment,
23699
- footerBottomTextFragment,
23700
23733
  headerCtaFragment,
23701
23734
  singleButtonFragment,
23702
23735
  blogFeaturedPostFragment,
@@ -24513,7 +24546,6 @@ function ensureFragmentsRegistered() {
24513
24546
  registerFragment(formCopyFragment);
24514
24547
  registerFragment(formEmbedFragment);
24515
24548
  registerFragment(bookingFormFragment);
24516
- registerFragment(footerBottomTextFragment);
24517
24549
  registerFragment(headerCtaFragment);
24518
24550
  registerFragment(singleButtonFragment);
24519
24551
  registerFragment(blogFeaturedPostFragment);
@@ -24543,7 +24575,6 @@ var init_init = __esmMin((() => {
24543
24575
  init_formCopy();
24544
24576
  init_formEmbed();
24545
24577
  init_bookingForm$1();
24546
- init_footerBottomText();
24547
24578
  init_headerCta();
24548
24579
  init_singleButton();
24549
24580
  init_blogFeaturedPost();
@@ -24836,7 +24867,6 @@ var init_fragments = __esmMin((() => {
24836
24867
  init_formCopy();
24837
24868
  init_formEmbed();
24838
24869
  init_bookingForm$1();
24839
- init_footerBottomText();
24840
24870
  init_headerCta();
24841
24871
  init_singleButton();
24842
24872
  init_blogFeaturedPost();
@@ -109050,15 +109080,32 @@ var init_RichText = __esmMin((() => {
109050
109080
  }));
109051
109081
  //#endregion
109052
109082
  //#region ../blocks/src/system/runtime/nodes/richText-coerce.ts
109053
- function coerceRichTextDoc(value) {
109054
- const raw = unwrapRichTextRoot(value);
109055
- if (typeof raw === "string") return createRichTextDocFromMarkdown(raw);
109056
- if (isRecord$32(raw) && typeof raw.type === "string") return raw;
109083
+ function parseRichTextDoc(value) {
109084
+ const parsed = RichTextDocSchema.safeParse(value);
109085
+ if (parsed.success) return {
109086
+ kind: "valid",
109087
+ doc: parsed.data
109088
+ };
109089
+ return {
109090
+ kind: "invalid",
109091
+ issues: parsed.error.issues.map((issue) => ({
109092
+ path: issue.path.map((segment) => String(segment)),
109093
+ message: issue.message
109094
+ }))
109095
+ };
109096
+ }
109097
+ function coerceRichTextDoc(value, source) {
109098
+ const parsed = parseRichTextDoc(value);
109099
+ if (parsed.kind === "valid") return parsed.doc;
109100
+ if (value !== null && value !== void 0) logger.warn("blocks:rich-text:invalid-document", {
109101
+ source,
109102
+ issues: parsed.issues.slice(0, 3)
109103
+ });
109057
109104
  return null;
109058
109105
  }
109059
109106
  var init_richText_coerce = __esmMin((() => {
109107
+ init_noop();
109060
109108
  init_richTextSchema();
109061
- init_typeGuards();
109062
109109
  }));
109063
109110
  //#endregion
109064
109111
  //#region ../blocks/src/system/runtime/nodes/commerce/shared/view.tsx
@@ -109859,7 +109906,7 @@ function ProductDetailController(props) {
109859
109906
  selectedProduct,
109860
109907
  variantId
109861
109908
  ]);
109862
- const bodyDoc = display.state === "ready" && props.render.hydration.showBody ? coerceRichTextDoc(display.body) : null;
109909
+ const bodyDoc = display.state === "ready" && props.render.hydration.showBody ? coerceRichTextDoc(display.body, "block-runtime:product-detail:body") : null;
109863
109910
  return /* @__PURE__ */ jsx(ProductDetailView, {
109864
109911
  display,
109865
109912
  gallery: display.state === "ready" ? /* @__PURE__ */ jsx(ProductGalleryFrameView, {
@@ -112455,6 +112502,7 @@ function resolveBlockIconRenderValue(value, generatedAssets = /* @__PURE__ */ ne
112455
112502
  //#region ../blocks/src/system/manifest/fieldValidation/index.ts
112456
112503
  init_src$2();
112457
112504
  init_schema();
112505
+ init_richTextSchema();
112458
112506
  const TEL_RE = /^[+()0-9\s-]{3,}$/;
112459
112507
  const SLUG_PATTERN = /^(?:[a-z0-9]+(?:-[a-z0-9]+)*)(?:\/(?:[a-z0-9]+(?:-[a-z0-9]+)*))*$/;
112460
112508
  const ALLOWED_URL_PROTOCOLS = ["http:", "https:"];
@@ -112511,7 +112559,7 @@ function fieldIssueToMessage(issue) {
112511
112559
  case "tooManyItems": return `${issue.label} must have at most ${issue.maximum} ${pluralize(issue.itemLabel, issue.maximum)}`;
112512
112560
  case "invalidRichText": return `${issue.label} must be valid rich text`;
112513
112561
  case "invalidInlineText": return `${issue.label} must be valid styled text`;
112514
- case "invalidMedia": return `${issue.label} must be a valid media item`;
112562
+ case "invalidMedia": return `${issue.label} must be a media object with an identifier`;
112515
112563
  case "invalidLink": return `${issue.label} must be a valid link`;
112516
112564
  case "invalidReference": return `${issue.label} must reference a valid item`;
112517
112565
  default: return assertNever$7(issue);
@@ -112599,7 +112647,7 @@ function deriveFieldValidationPlan(field, path = [field.id]) {
112599
112647
  case "media":
112600
112648
  case "file": return {
112601
112649
  ...makePlan("media", "object", field, path, required, commonConstraints(required, { kind: "mediaObject" })),
112602
- legacyStringType: legacyStringMediaTypeForField(field)
112650
+ allowedMediaTypes: field.type === "media" ? field.mediaKinds ?? DEFAULT_MEDIA_FIELD_KINDS : field.fileKinds ?? PUBLIC_FILE_FIELD_KINDS
112603
112651
  };
112604
112652
  case "link": return makePlan("link", "object", field, path, required, commonConstraints(required, { kind: "linkObject" }));
112605
112653
  case "boolean": return makePlan("boolean", "boolean", field, path, required, requiredConstraints(required));
@@ -112611,12 +112659,25 @@ function deriveFieldValidationPlan(field, path = [field.id]) {
112611
112659
  case "group":
112612
112660
  case "modal": return {
112613
112661
  ...makePlan("group", "object", field, path, required, requiredConstraints(required)),
112614
- children: deriveNestedFieldPlans(field.schema?.fields ?? [], path),
112662
+ childSelection: {
112663
+ kind: "all",
112664
+ fields: deriveNestedFieldPlans(field.schema?.fields ?? [], path)
112665
+ },
112615
112666
  allowNullChildren: field.type === "group" && field.ui?.widget === "locationPicker"
112616
112667
  };
112617
112668
  case "tabGroup": return {
112618
112669
  ...makePlan("group", "object", field, path, required, requiredConstraints(required)),
112619
- children: field.tabs.flatMap((tab) => deriveNestedFieldPlans(tab.fields, path))
112670
+ childSelection: field.activeTabField ? {
112671
+ kind: "discriminatedTabs",
112672
+ activeTabField: field.activeTabField,
112673
+ tabs: field.tabs.map((tab) => ({
112674
+ typeId: tab.id,
112675
+ fields: deriveNestedFieldPlans(tab.fields, path)
112676
+ }))
112677
+ } : {
112678
+ kind: "all",
112679
+ fields: field.tabs.flatMap((tab) => deriveNestedFieldPlans(tab.fields, path))
112680
+ }
112620
112681
  };
112621
112682
  case "presetOrCustom":
112622
112683
  case "contentTypeSelect":
@@ -112711,10 +112772,59 @@ function normalizeStoredContentValues(plans, content, options = {}) {
112711
112772
  normalizeInlineTextFallbacks(plans, normalized, ctx);
112712
112773
  return normalized;
112713
112774
  }
112775
+ function allGroupChildPlans(plan) {
112776
+ switch (plan.childSelection.kind) {
112777
+ case "all": return plan.childSelection.fields;
112778
+ case "discriminatedTabs": return plan.childSelection.tabs.flatMap((tab) => tab.fields);
112779
+ default: return assertNever$7(plan.childSelection);
112780
+ }
112781
+ }
112782
+ function selectedGroupChildPlans(plan, value) {
112783
+ if (plan.childSelection.kind === "all") return plan.childSelection.fields;
112784
+ if (!isPlainObject$5(value)) return [];
112785
+ const selectedType = value[plan.childSelection.activeTabField];
112786
+ return plan.childSelection.tabs.find((tab) => tab.typeId === selectedType)?.fields ?? [];
112787
+ }
112788
+ function canonicalizeFieldValueForWrite(plan, value) {
112789
+ switch (plan.kind) {
112790
+ case "media": {
112791
+ if (!isPlainObject$5(value)) return value;
112792
+ const canonical = { ...value };
112793
+ if (typeof canonical.identifier === "string" && canonical.identifier.trim().length > 0) deleteEnvSpecificKeys(canonical);
112794
+ return canonical;
112795
+ }
112796
+ case "group": return canonicalizeObjectForWrite(selectedGroupChildPlans(plan, value), value);
112797
+ case "repeater":
112798
+ if (!isUnknownArray$2(value)) return value;
112799
+ return value.map((item) => {
112800
+ if (!isPlainObject$5(item)) return item;
112801
+ const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
112802
+ return childPlans ? canonicalizeObjectForWrite(childPlans, item) : item;
112803
+ });
112804
+ case "string":
112805
+ case "number":
112806
+ case "boolean":
112807
+ case "richText":
112808
+ case "inlineText":
112809
+ case "link":
112810
+ case "select":
112811
+ case "passthrough": return value;
112812
+ default: return assertNever$7(plan);
112813
+ }
112814
+ }
112815
+ function canonicalizeObjectForWrite(plans, value) {
112816
+ if (!isPlainObject$5(value)) return value;
112817
+ const canonical = { ...value };
112818
+ for (const plan of plans) {
112819
+ if (!(plan.fieldId in canonical)) continue;
112820
+ canonical[plan.fieldId] = canonicalizeFieldValueForWrite(plan, canonical[plan.fieldId]);
112821
+ }
112822
+ return canonical;
112823
+ }
112714
112824
  function normalizeStoredFieldValueWithContext(plan, value, ctx) {
112715
112825
  if (value === null && !plan.required) return;
112716
112826
  switch (plan.kind) {
112717
- case "group": return normalizeStoredObjectChildren(plan.children, value, childValidationContext(plan, ctx));
112827
+ case "group": return normalizeStoredObjectChildren(selectedGroupChildPlans(plan, value), value, childValidationContext(plan, ctx));
112718
112828
  case "repeater": return normalizeStoredRepeaterItems(plan, value, ctx);
112719
112829
  case "string":
112720
112830
  case "number":
@@ -112753,7 +112863,7 @@ function normalizeFieldValue(plan, value, ctx) {
112753
112863
  switch (plan.kind) {
112754
112864
  case "number": return normalizeNumberInput(value, ctx.allowNull);
112755
112865
  case "string": return acceptsOptionalBlankAsMissing(plan, value) ? void 0 : value;
112756
- case "group": return normalizeObjectChildren(plan.children, value, childValidationContext(plan, ctx));
112866
+ case "group": return normalizeObjectChildren(selectedGroupChildPlans(plan, value), value, childValidationContext(plan, ctx));
112757
112867
  case "repeater": return normalizeRepeaterItems(plan, value, ctx);
112758
112868
  case "boolean":
112759
112869
  case "richText":
@@ -112761,17 +112871,10 @@ function normalizeFieldValue(plan, value, ctx) {
112761
112871
  case "link":
112762
112872
  case "select":
112763
112873
  case "passthrough": return value;
112764
- case "media": return normalizeMediaInput(plan, value);
112874
+ case "media": return value;
112765
112875
  default: return assertNever$7(plan);
112766
112876
  }
112767
112877
  }
112768
- function normalizeMediaInput(plan, value) {
112769
- return normalizeLegacyStringMediaValueForField(value, {
112770
- type: plan.fieldType,
112771
- mediaKinds: plan.fieldType === "media" ? [plan.legacyStringType] : void 0,
112772
- fileKinds: plan.fieldType === "file" ? [plan.legacyStringType] : void 0
112773
- });
112774
- }
112775
112878
  function normalizeObjectChildren(children, value, ctx) {
112776
112879
  if (!value || typeof value !== "object" || Array.isArray(value)) return value;
112777
112880
  const normalized = { ...value };
@@ -112823,7 +112926,7 @@ function validateNormalizedFieldValue(plan, value, ctx) {
112823
112926
  case "select": return validateSelectPlan(plan, value);
112824
112927
  case "richText": return isValidRichTextDoc(value) ? [] : [issue(plan, "invalidRichText")];
112825
112928
  case "inlineText": return validateInlineTextPlan(plan, value);
112826
- case "media": return isPlainObject$5(value) ? [] : [issue(plan, "invalidMedia")];
112929
+ case "media": return isPlainObject$5(value) && typeof value.identifier === "string" && value.identifier.trim().length > 0 && (value.type === void 0 || typeof value.type === "string" && plan.allowedMediaTypes.includes(value.type)) ? [] : [issue(plan, "invalidMedia")];
112827
112930
  case "link": return linkSchema.safeParse(value).success ? [] : [issue(plan, "invalidLink")];
112828
112931
  case "repeater": return validateRepeaterPlan(plan, value, ctx);
112829
112932
  case "group": return validateGroupPlan(plan, value, ctx);
@@ -112916,9 +113019,26 @@ function validateRepeaterPlan(plan, value, ctx) {
112916
113019
  }), ...value.flatMap((item, index) => validateRepeaterItem(plan, item, index, ctx))];
112917
113020
  }
112918
113021
  function validateRepeaterItem(plan, item, index, ctx) {
112919
- if (!isPlainObject$5(item)) return [];
113022
+ if (!isPlainObject$5(item)) return [{
113023
+ kind: "wrongType",
113024
+ path: [...plan.path, index],
113025
+ fieldId: plan.fieldId,
113026
+ label: plan.label,
113027
+ expected: "an object"
113028
+ }];
112920
113029
  const itemType = item._type;
112921
- const fields = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemType)?.fields : plan.repeatedItemPlan;
113030
+ const variant = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((candidate) => candidate.typeId === itemType) : void 0;
113031
+ if (plan.repeatedItemVariants && !variant) return [{
113032
+ kind: "invalidOption",
113033
+ path: [
113034
+ ...plan.path,
113035
+ index,
113036
+ "_type"
113037
+ ],
113038
+ fieldId: "_type",
113039
+ label: `${plan.label} item type`
113040
+ }];
113041
+ const fields = variant?.fields ?? plan.repeatedItemPlan;
112922
113042
  if (!fields) return [];
112923
113043
  return fields.flatMap((childPlan) => {
112924
113044
  return validateNormalizedFieldValue(materializeRepeaterItemPlan(childPlan, plan.path, index), item[childPlan.fieldId], ctx);
@@ -112926,8 +113046,17 @@ function validateRepeaterItem(plan, item, index, ctx) {
112926
113046
  }
112927
113047
  function validateGroupPlan(plan, value, ctx) {
112928
113048
  if (!isPlainObject$5(value)) return [issue(plan, "wrongType", { expected: "an object" })];
113049
+ if (plan.childSelection.kind === "discriminatedTabs") {
113050
+ const selectedType = value[plan.childSelection.activeTabField];
113051
+ if (selectedType !== void 0 && selectedType !== null && !plan.childSelection.tabs.some((tab) => tab.typeId === selectedType)) return [{
113052
+ kind: "invalidOption",
113053
+ path: [...plan.path, plan.childSelection.activeTabField],
113054
+ fieldId: plan.childSelection.activeTabField,
113055
+ label: `${plan.label} type`
113056
+ }];
113057
+ }
112929
113058
  const childCtx = childValidationContext(plan, ctx);
112930
- return plan.children.flatMap((childPlan) => validateNormalizedFieldValue(childPlan, value[childPlan.fieldId], childCtx));
113059
+ return selectedGroupChildPlans(plan, value).flatMap((childPlan) => validateNormalizedFieldValue(childPlan, value[childPlan.fieldId], childCtx));
112931
113060
  }
112932
113061
  function childValidationContext(plan, ctx) {
112933
113062
  return plan.allowNullChildren ? {
@@ -112944,7 +113073,16 @@ function rebaseFieldPlanPath(plan, fromPrefix, toPrefix) {
112944
113073
  case "group": return {
112945
113074
  ...plan,
112946
113075
  path,
112947
- children: plan.children.map((childPlan) => rebaseFieldPlanPath(childPlan, fromPrefix, toPrefix))
113076
+ childSelection: plan.childSelection.kind === "all" ? {
113077
+ kind: "all",
113078
+ fields: plan.childSelection.fields.map((childPlan) => rebaseFieldPlanPath(childPlan, fromPrefix, toPrefix))
113079
+ } : {
113080
+ ...plan.childSelection,
113081
+ tabs: plan.childSelection.tabs.map((tab) => ({
113082
+ ...tab,
113083
+ fields: tab.fields.map((childPlan) => rebaseFieldPlanPath(childPlan, fromPrefix, toPrefix))
113084
+ }))
113085
+ }
112948
113086
  };
112949
113087
  case "repeater": return {
112950
113088
  ...plan,
@@ -113100,7 +113238,7 @@ function findFieldValidationPlan(plans, path) {
113100
113238
  }
113101
113239
  function findNestedFieldValidationPlan(plan, path) {
113102
113240
  switch (plan.kind) {
113103
- case "group": return findFieldValidationPlan(plan.children, path);
113241
+ case "group": return findFieldValidationPlan(allGroupChildPlans(plan), path);
113104
113242
  case "repeater": {
113105
113243
  const index = path[plan.path.length];
113106
113244
  if (typeof index !== "number") return null;
@@ -113128,7 +113266,7 @@ function validateExplicitNullsForPush(plans, content, ctx) {
113128
113266
  function validateExplicitNullForPushPlan(plan, value, ctx) {
113129
113267
  if (value === null) return !plan.required && !ctx.allowNull && !acceptsOptionalNullValue(plan) ? validateFieldValue(plan, value, { allowNull: false }) : [];
113130
113268
  switch (plan.kind) {
113131
- case "group": return validateExplicitNullsForPush(plan.children, value, childValidationContext(plan, ctx));
113269
+ case "group": return validateExplicitNullsForPush(selectedGroupChildPlans(plan, value), value, childValidationContext(plan, ctx));
113132
113270
  case "repeater": return validateExplicitNullsForPushRepeater(plan, value, ctx);
113133
113271
  case "string":
113134
113272
  case "number":
@@ -113235,23 +113373,17 @@ function isUnknownArray$2(value) {
113235
113373
  return Array.isArray(value);
113236
113374
  }
113237
113375
  function isValidRichTextDoc(value) {
113238
- const doc = richTextDoc(value);
113239
- return Boolean(isPlainObject$5(doc) && doc.type === "doc");
113376
+ return isRichTextDoc(value);
113240
113377
  }
113241
113378
  function isEmptyRichTextDoc(value) {
113242
- const doc = richTextDoc(value);
113243
- if (!isPlainObject$5(doc)) return false;
113244
- if (doc.type !== "doc") return false;
113245
- const content = doc.content;
113379
+ if (!isPlainObject$5(value)) return false;
113380
+ if (value.type !== "doc") return false;
113381
+ const content = value.content;
113246
113382
  if (!Array.isArray(content) || content.length === 0) return true;
113247
113383
  if (content.length !== 1) return false;
113248
113384
  const first = content[0];
113249
113385
  return first?.type === "paragraph" && (!Array.isArray(first.content) || first.content.length === 0);
113250
113386
  }
113251
- function richTextDoc(value) {
113252
- if (!isPlainObject$5(value)) return value;
113253
- return isPlainObject$5(value.doc) ? value.doc : value;
113254
- }
113255
113387
  //#endregion
113256
113388
  //#region ../blocks/src/system/manifest/registry.ts
113257
113389
  const REGISTRY_SYMBOL$1 = Symbol.for("@riverbankcms/blocks/manifest-registry");
@@ -113275,11 +113407,14 @@ function preprocessEmptyValues(content) {
113275
113407
  return result;
113276
113408
  }
113277
113409
  function createManifestValidator(manifestOrFields, options = {}) {
113410
+ return createManifestValidatorWithNormalizer(manifestOrFields, options, normalizeStoredContentValues);
113411
+ }
113412
+ function createManifestValidatorWithNormalizer(manifestOrFields, options, normalize) {
113278
113413
  const isDraft = options.mode === "draft";
113279
113414
  const plans = derivePlansFromManifestOrFields(manifestOrFields);
113280
113415
  const baseSchema = deriveContentZodFromPlans(plans, options);
113281
- if (isDraft) return z$1.preprocess((content) => preprocessEmptyValues(normalizeStoredContentValues(plans, content, options)), baseSchema);
113282
- return z$1.preprocess((content) => normalizeStoredContentValues(plans, content, options), baseSchema);
113416
+ if (isDraft) return z$1.preprocess((content) => preprocessEmptyValues(normalize(plans, content, options)), baseSchema);
113417
+ return z$1.preprocess((content) => normalize(plans, content, options), baseSchema);
113283
113418
  }
113284
113419
  function deriveContentZodFromPlans(plans, options) {
113285
113420
  const shape = {};
@@ -114992,7 +115127,7 @@ const formattingTransforms = [
114992
115127
  ];
114993
115128
  //#endregion
114994
115129
  //#region ../blocks/src/system/transforms/registry/ui.ts
114995
- init_core$2();
115130
+ init_core$1();
114996
115131
  init_site_styles();
114997
115132
  init_colorStyles();
114998
115133
  init_blockColorTokens();
@@ -115508,7 +115643,7 @@ const uiTransforms = [
115508
115643
  ];
115509
115644
  //#endregion
115510
115645
  //#region ../theme-core/src/siteChromePlacement.ts
115511
- init_types$15();
115646
+ init_types$14();
115512
115647
  const SITE_CHROME_PLACEMENT_CLASSES = [
115513
115648
  "rb-sticky",
115514
115649
  "rb-fixed",
@@ -115570,7 +115705,7 @@ function splitSiteChromePlacementClasses(className) {
115570
115705
  //#endregion
115571
115706
  //#region ../blocks/src/system/transforms/registry/layout.ts
115572
115707
  init_colorConversion();
115573
- init_core$2();
115708
+ init_core$1();
115574
115709
  init_colorStyles();
115575
115710
  init_surfaces();
115576
115711
  const joinClasses$1 = (parts) => parts.filter((part) => typeof part === "string" && part.trim().length > 0).join(" ").replace(/\s+/g, " ").trim();
@@ -116210,20 +116345,10 @@ function visitRichTextNode(node, allowedTokens, tokenValues) {
116210
116345
  }
116211
116346
  function interpolateRichTextTokens(value, options, context) {
116212
116347
  if (!isRecord$32(value)) return value;
116213
- const allowedTokens = new Set(options.tokens);
116214
- const tokenValues = {
116348
+ return visitRichTextNode(value, new Set(options.tokens), {
116215
116349
  year: String((/* @__PURE__ */ new Date()).getFullYear()),
116216
116350
  site_name: getSiteName(context)
116217
- };
116218
- if (isRecord$32(value.doc)) {
116219
- const nextDoc = visitRichTextNode(value.doc, allowedTokens, tokenValues);
116220
- if (nextDoc === value.doc) return value;
116221
- return {
116222
- ...value,
116223
- doc: nextDoc
116224
- };
116225
- }
116226
- return visitRichTextNode(value, allowedTokens, tokenValues);
116351
+ });
116227
116352
  }
116228
116353
  const contentTransforms = [{
116229
116354
  id: "content.interpolateRichTextTokens",
@@ -126347,7 +126472,7 @@ const linksTransforms = [{
126347
126472
  }];
126348
126473
  //#endregion
126349
126474
  //#region ../blocks/src/system/transforms/registry/index.ts
126350
- init_core$2();
126475
+ init_core$1();
126351
126476
  init_boxStyles$1();
126352
126477
  const defaultTransforms = createTransformRegistry([
126353
126478
  ...formattingTransforms,
@@ -126361,7 +126486,7 @@ const defaultTransforms = createTransformRegistry([
126361
126486
  ]);
126362
126487
  //#endregion
126363
126488
  //#region ../blocks/src/system/transforms/index.ts
126364
- init_core$2();
126489
+ init_core$1();
126365
126490
  //#endregion
126366
126491
  //#region ../blocks/src/system/data/linkedContentEntries.ts
126367
126492
  init_src$3();
@@ -127009,29 +127134,11 @@ function normalize(value) {
127009
127134
  return trimmed.length > 0 ? trimmed : null;
127010
127135
  }
127011
127136
  /**
127012
- * Resolve the trailing affordance a button should render. Node overrides
127013
- * personality; returns `null` when nothing is configured.
127137
+ * Resolve the node-authored trailing affordance, or `null` when no icon exists.
127014
127138
  */
127015
127139
  function resolveButtonAffordance(input) {
127016
127140
  const nodeIcon = normalize(input.trailingIcon);
127017
- if (nodeIcon) {
127018
- const colorToken = normalize(input.trailingIconColorToken);
127019
- return {
127020
- iconId: nodeIcon,
127021
- source: "node",
127022
- ...colorToken ? { colorToken } : {}
127023
- };
127024
- }
127025
- const personality = input.personalityAffordance;
127026
- const personalityIcon = normalize(personality?.iconId);
127027
- if (personality && personalityIcon) {
127028
- const colorToken = normalize(personality.colorToken);
127029
- return {
127030
- iconId: personalityIcon,
127031
- source: "personality",
127032
- ...colorToken ? { colorToken } : {}
127033
- };
127034
- }
127141
+ if (nodeIcon) return { iconId: nodeIcon };
127035
127142
  return null;
127036
127143
  }
127037
127144
  //#endregion
@@ -127085,13 +127192,12 @@ function readStringPath(source, path) {
127085
127192
  }
127086
127193
  const RichTextNode = ({ value, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, ...rest }) => {
127087
127194
  return /* @__PURE__ */ jsx(RichText$2, {
127088
- doc: coerceRichTextDoc(value) ?? void 0,
127195
+ doc: coerceRichTextDoc(value, `block-runtime:${_blockKind ?? "unknown"}:${rest.path ?? "richText"}`) ?? void 0,
127089
127196
  ...rest
127090
127197
  });
127091
127198
  };
127092
127199
  /**
127093
- * Render the resolved trailing affordance icon. Colour is applied via a theme
127094
- * CSS var so it recolours with the palette; the icon stroke uses `currentColor`.
127200
+ * Render the resolved trailing affordance icon using the button's foreground.
127095
127201
  */
127096
127202
  function renderTrailingAffordanceIcon(affordance) {
127097
127203
  if (!affordance) return null;
@@ -127099,21 +127205,16 @@ function renderTrailingAffordanceIcon(affordance) {
127099
127205
  if (!Icon) return null;
127100
127206
  return /* @__PURE__ */ jsx(Icon, {
127101
127207
  "aria-hidden": true,
127102
- className: "rb-ml-2 rb-h-4 rb-w-4 rb-inline-block",
127103
- style: affordance.colorToken ? { color: `rgb(var(--tb-${affordance.colorToken}))` } : void 0
127208
+ className: "rb-ml-2 rb-h-4 rb-w-4 rb-inline-block"
127104
127209
  });
127105
127210
  }
127106
- const ButtonNode = ({ value, children, href, variantId = "primary", size = "md", disabled, type = "button", target, rel, linkRequired = false, trailingIcon, trailingIconColorToken, personalityAffordance, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, className, ...rest }) => {
127211
+ const ButtonNode = ({ value, children, href, variantId = "primary", size = "md", disabled, type = "button", target, rel, linkRequired = false, trailingIcon, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, className, ...rest }) => {
127107
127212
  const content = value ?? children;
127108
127213
  if (!content) {
127109
127214
  console.warn("[ButtonNode] No content to render - value and children are both empty");
127110
127215
  return null;
127111
127216
  }
127112
- const affordanceIcon = renderTrailingAffordanceIcon(resolveButtonAffordance({
127113
- trailingIcon,
127114
- trailingIconColorToken,
127115
- personalityAffordance
127116
- }));
127217
+ const affordanceIcon = renderTrailingAffordanceIcon(resolveButtonAffordance({ trailingIcon }));
127117
127218
  const resolvedHref = typeof href === "string" ? href.trim() : "";
127118
127219
  if (linkRequired && resolvedHref.length === 0) {
127119
127220
  console.warn("[ButtonNode] Missing href for link-required button");
@@ -127736,9 +127837,9 @@ const AccordionItemNodeSSR = ({ className, value, trigger, content, question, an
127736
127837
  };
127737
127838
  function resolveNode$1(value) {
127738
127839
  if (typeof value === "string" || typeof value === "number") return String(value);
127739
- const doc = coerceRichTextDoc(value);
127840
+ const doc = coerceRichTextDoc(value, "block-runtime:accordion:item-content");
127740
127841
  if (doc) return /* @__PURE__ */ jsx(RichText$2, { doc });
127741
- return value;
127842
+ return React.isValidElement(value) ? value : null;
127742
127843
  }
127743
127844
  //#endregion
127744
127845
  //#region ../blocks/src/system/runtime/nodes/header-section.server.tsx
@@ -128243,7 +128344,7 @@ function CourseDetailsView(props) {
128243
128344
  const sortedSessions = [...course.sessions ?? []].sort((a, b) => Date.parse(a.startsAt) - Date.parse(b.startsAt));
128244
128345
  const visibleSessions = sessionLimit === "all" ? sortedSessions : sortedSessions.slice(0, sessionLimit);
128245
128346
  const hasMoreSessions = sessionLimit !== "all" && sortedSessions.length > sessionLimit;
128246
- const descriptionDoc = showDescription ? coerceRichTextDoc(course.description) : null;
128347
+ const descriptionDoc = showDescription ? coerceRichTextDoc(course.description, "block-runtime:course-details:description") : null;
128247
128348
  const teamMembers = course.teamMembers ?? [];
128248
128349
  return /* @__PURE__ */ jsxs("div", {
128249
128350
  className: "rb-flex rb-flex-col rb-gap-6",
@@ -129416,7 +129517,7 @@ function ProductDetailServerContent(props) {
129416
129517
  actions: props.render.display.state === "ready" ? renderServerPendingAction(props.render.display.actionLabel, props.render.display.soldOut) : null,
129417
129518
  body: props.render.display.state === "ready" && props.render.display.body ? /* @__PURE__ */ jsx("div", {
129418
129519
  className: "rb-prose rb-max-w-none",
129419
- children: /* @__PURE__ */ jsx(RichText$2, { doc: coerceRichTextDoc(props.render.display.body) })
129520
+ children: /* @__PURE__ */ jsx(RichText$2, { doc: coerceRichTextDoc(props.render.display.body, "block-runtime:product-detail:body") })
129420
129521
  }) : null
129421
129522
  });
129422
129523
  }
@@ -130694,8 +130795,8 @@ function extractAlignmentClassName(className) {
130694
130795
  //#region ../blocks/src/system/runtime/resolver.ts
130695
130796
  init_src$3();
130696
130797
  init_node();
130697
- init_richTextSchema();
130698
130798
  init_headingDecoration();
130799
+ init_richText_coerce();
130699
130800
  /**
130700
130801
  * Maps meta binding paths to their actual store keys.
130701
130802
  * The metadata store uses 'draft' prefixed keys (e.g., draftTitle, draftSlug)
@@ -131019,13 +131120,9 @@ function isTextConditionValueEmpty(value) {
131019
131120
  return !value;
131020
131121
  }
131021
131122
  function isRichTextConditionValueEmpty(value) {
131022
- const root = unwrapRichTextRoot(value);
131023
- if (typeof root === "string") return root.trim().length === 0;
131024
- if (!isTipTapNode(root)) return root === null || root === void 0 || !root;
131025
- return richTextStructuralPresence(root).kind === "empty";
131026
- }
131027
- function isTipTapNode(value) {
131028
- return isPlainObject$3(value) && typeof value.type === "string";
131123
+ const doc = coerceRichTextDoc(value, "block-runtime:condition:richText");
131124
+ if (!doc) return true;
131125
+ return richTextStructuralPresence(doc).kind === "empty";
131029
131126
  }
131030
131127
  function richTextStructuralPresence(node) {
131031
131128
  if (typeof node.text === "string" && node.text.trim().length > 0) return {
@@ -132961,6 +133058,7 @@ function generateHeaderCss(options) {
132961
133058
  cssChunks.push(generateMobilePanelStyles(header, themeId, tokens));
132962
133059
  if (header.variant === "floating" && header.container) cssChunks.push(generateFloatingContainerStyles(header, themeId, tokens));
132963
133060
  cssChunks.push(generateNavContainerStyles(header, themeId, tokens));
133061
+ cssChunks.push(generateHeaderCtaTreatmentStyles(header, themeId, tokens));
132964
133062
  cssChunks.push(generateCtaGapStyles(header, themeId));
132965
133063
  return cssChunks.filter(Boolean).join("\n\n");
132966
133064
  }
@@ -133431,6 +133529,46 @@ const HEADER_CTA_GAP_MAP = {
133431
133529
  spacious: "3rem"
133432
133530
  };
133433
133531
  /**
133532
+ * The inverted CTA treatment is contextual to branded header surfaces. It
133533
+ * cannot be represented by the generic secondary button role alone because
133534
+ * that role is transparent and inherits the surrounding surface foreground.
133535
+ * Keep the shared button's structure/effects while making the header own the
133536
+ * palette relationship for both desktop and mobile CTAs.
133537
+ */
133538
+ function generateHeaderCtaTreatmentStyles(header, themeId, tokens) {
133539
+ if (header.ctaTreatment !== "inverted") return "";
133540
+ const desktopSelector = `:where([data-theme-scope="${themeId}"]) .header-cta`;
133541
+ const mobileSelector = `:where([data-theme-scope="${themeId}"]) .nav-mobile-cta`;
133542
+ const selectors = `${desktopSelector},\n${mobileSelector}`;
133543
+ const hoverSelectors = [
133544
+ `${desktopSelector}:hover`,
133545
+ `${desktopSelector}[data-force-hover="true"]`,
133546
+ `${mobileSelector}:hover`,
133547
+ `${mobileSelector}[data-force-hover="true"]`
133548
+ ].join(",\n");
133549
+ const focusSelectors = [`${desktopSelector}:focus-visible`, `${mobileSelector}:focus-visible`].join(",\n");
133550
+ return `
133551
+ ${selectors} {
133552
+ background-color: ${tokens.getColor("background")};
133553
+ background-image: none;
133554
+ color: ${tokens.getColor("onBackground")};
133555
+ border-color: currentColor;
133556
+ }
133557
+
133558
+ ${hoverSelectors} {
133559
+ background-color: ${tokens.getColor("surface")};
133560
+ background-image: none;
133561
+ color: ${tokens.getColor("onSurface")};
133562
+ }
133563
+
133564
+ ${focusSelectors} {
133565
+ outline: 2px solid ${tokens.getColor("onBackground")};
133566
+ outline-offset: 2px;
133567
+ box-shadow: 0 0 0 4px ${tokens.getColor("background")};
133568
+ }
133569
+ `;
133570
+ }
133571
+ /**
133434
133572
  * Generate CTA button gap styles (left margin between nav links and CTA)
133435
133573
  */
133436
133574
  function generateCtaGapStyles(header, themeId) {
@@ -133489,7 +133627,8 @@ ${`:where([data-theme-scope="${themeId}"])`} .footer-logo img {
133489
133627
  `;
133490
133628
  }
133491
133629
  function resolveFooterLogoSize(footer, headerLogo) {
133492
- return footerLogoSizeFromHeaderLogo(headerLogo) ?? footer.logo?.maxHeight ?? footer.logo?.size ?? "md";
133630
+ const brandDrivenSize = footerLogoSizeFromHeaderLogo(headerLogo);
133631
+ return footer.logo?.maxHeight ?? brandDrivenSize ?? footer.logo?.size ?? "md";
133493
133632
  }
133494
133633
  function footerLogoSizeFromHeaderLogo(headerLogo) {
133495
133634
  const size = headerLogo?.size;
@@ -136492,11 +136631,8 @@ function summarizeValue(value) {
136492
136631
  const preview = {};
136493
136632
  for (const k of keys.slice(0, 8)) {
136494
136633
  const v = rec[k];
136495
- if (k === "body" || k === "content") {
136496
- const isDocWrapper = !!(v && typeof v === "object" && isObjectRecord$1(v) && "doc" in v);
136497
- const isDoc = !!(v && typeof v === "object" && isObjectRecord$1(v) && v.type === "doc");
136498
- preview[k] = isDocWrapper ? "{doc}" : isDoc ? "{type:doc}" : summarizeValue(v);
136499
- } else preview[k] = summarizeValue(v);
136634
+ if (k === "body" || k === "content") preview[k] = !!(v && typeof v === "object" && isObjectRecord$1(v) && v.type === "doc") ? "{type:doc}" : summarizeValue(v);
136635
+ else preview[k] = summarizeValue(v);
136500
136636
  }
136501
136637
  return {
136502
136638
  keys,
@@ -149609,7 +149745,12 @@ function ResponsiveScope({ children, responsiveMode = DEFAULT_THEME_RESPONSIVE_M
149609
149745
  }
149610
149746
  //#endregion
149611
149747
  //#region ../theme-core/src/siteChrome.tsx
149612
- init_types$15();
149748
+ init_types$14();
149749
+ //#endregion
149750
+ //#region ../theme-core/src/buttons/core.ts
149751
+ init_resolveButtonColourStates();
149752
+ init_resolvedButtonCss();
149753
+ init_types$11();
149613
149754
  //#endregion
149614
149755
  //#region ../theme-core/src/runtime/buildThemeRuntimeFromBridge.ts
149615
149756
  /**
@@ -149622,7 +149763,6 @@ init_types$15();
149622
149763
  init_colorConversion();
149623
149764
  init_textColor();
149624
149765
  init_onColor();
149625
- init_core$1();
149626
149766
  init_core();
149627
149767
  z$1.object({
149628
149768
  tokens: z$1.record(z$1.string(), z$1.string()),
@@ -149675,41 +149815,11 @@ init_classic_professional();
149675
149815
  init_vibrant_creative();
149676
149816
  //#endregion
149677
149817
  //#region src/rendering/blocks-theme-runtime.ts
149678
- const AXES_TONE_OPTIONS = [
149679
- "minimal",
149680
- "classic",
149681
- "bold"
149682
- ];
149683
- const DEFAULT_AXES_TONE = AXES_TONE_OPTIONS[0];
149684
- const AXES_TONE_SET = new Set(AXES_TONE_OPTIONS);
149685
- const THEME_NAME_MAX_LENGTH = 30;
149686
- function normalizeThemeForBlocks(theme) {
149687
- if (!theme || typeof theme !== "object") return theme;
149688
- const cloned = JSON.parse(JSON.stringify(theme));
149689
- if (typeof cloned.name === "string" && cloned.name.length > THEME_NAME_MAX_LENGTH) cloned.name = cloned.name.slice(0, THEME_NAME_MAX_LENGTH);
149690
- if (cloned.axes && typeof cloned.axes === "object") {
149691
- const axes = cloned.axes;
149692
- const tone = axes.tone;
149693
- if (typeof tone === "string" && !AXES_TONE_SET.has(tone)) axes.tone = DEFAULT_AXES_TONE;
149694
- }
149695
- return cloned;
149696
- }
149818
+ init_buttons();
149697
149819
  function parseBlocksTheme(theme) {
149698
- const schema = themeSchema;
149699
- if (typeof schema.safeParse !== "function") {
149700
- console.warn("[sdk/rendering] themeSchema.safeParse is unavailable; skipping theme validation and using normalized theme data directly.");
149701
- return normalizeThemeForBlocks(theme);
149702
- }
149703
- const parsed = schema.safeParse(theme);
149704
- if (parsed.success) return parsed.data;
149705
- const normalized = normalizeThemeForBlocks(theme);
149706
- const reparsed = schema.safeParse(normalized);
149707
- if (reparsed.success) {
149708
- console.warn("[sdk/rendering] Repaired invalid theme to satisfy blocks schema:", parsed.error.issues);
149709
- return reparsed.data;
149710
- }
149711
- console.warn("[sdk/rendering] Failed to parse theme; falling back to modernMinimalTheme:", reparsed.error.issues);
149712
- return modernMinimalTheme;
149820
+ const parsed = parseThemeForPersistence(theme);
149821
+ if (!parsed.ok) throw new Error(`Theme does not satisfy the canonical runtime contract: ${parsed.error.kind}`);
149822
+ return parsed.value;
149713
149823
  }
149714
149824
  function buildThemeRuntime(theme) {
149715
149825
  return { tokens: buildThemeRuntime$1(parseBlocksTheme(theme)).tokens };
@@ -150550,16 +150660,6 @@ async function Layout({ siteData: providedSiteData, client, siteId, slug, domain
150550
150660
  });
150551
150661
  }
150552
150662
  //#endregion
150553
- //#region ../theme-core/src/styles/gradients.ts
150554
- init_paletteAccess();
150555
- init_colorConversion();
150556
- //#endregion
150557
- //#region ../theme-core/src/styles/glow.ts
150558
- init_palette$1();
150559
- //#endregion
150560
- //#region ../theme-core/src/styles/buttons.ts
150561
- init_colorStyles();
150562
- //#endregion
150563
150663
  //#region ../blocks/src/structure/schema.ts
150564
150664
  const BlockKind = z.string();
150565
150665
  const blockSchema = z.object({
@@ -150578,8 +150678,9 @@ z.object({
150578
150678
  blocks: z.array(blockSchema).min(1)
150579
150679
  });
150580
150680
  //#endregion
150581
- //#region ../theme-core/src/recommend/generateThemes.ts
150582
- init_expandPalette();
150681
+ //#region ../theme-core/src/styles/gradients.ts
150682
+ init_paletteAccess();
150683
+ init_colorConversion();
150583
150684
  //#endregion
150584
150685
  //#region ../blocks/src/system/runtime/bindings.ts
150585
150686
  init_node();
@@ -151607,6 +151708,7 @@ const CANONICAL_REFUND_PLANNER_ERROR_CODES = [
151607
151708
  "idempotency_amount_mismatch",
151608
151709
  "idempotency_method_mismatch",
151609
151710
  "idempotency_receipt_mismatch",
151711
+ "idempotency_scope_mismatch",
151610
151712
  "refund_in_progress"
151611
151713
  ],
151612
151714
  "invalid_amount",
@@ -151986,6 +152088,7 @@ BOOKING_QUESTION_ANSWER_EXPORT_SCOPES.site, BOOKING_QUESTION_ANSWER_EXPORT_SCOPE
151986
152088
  init_src$3();
151987
152089
  //#endregion
151988
152090
  //#region ../api/src/siteBanner.ts
152091
+ init_richTextSchema();
151989
152092
  const SITE_BANNER_SCOPE_VALUES = ["homepage", "all-pages"];
151990
152093
  const SITE_BANNER_TONE_VALUES = [
151991
152094
  "announcement",
@@ -151995,7 +152098,7 @@ const SITE_BANNER_TONE_VALUES = [
151995
152098
  ];
151996
152099
  const siteBannerScopeSchema = z$1.enum(SITE_BANNER_SCOPE_VALUES);
151997
152100
  const siteBannerToneSchema = z$1.enum(SITE_BANNER_TONE_VALUES);
151998
- const siteBannerBodySchema = z$1.unknown().transform(unwrapRichTextValue);
152101
+ const siteBannerBodySchema = RichTextDocSchema;
151999
152102
  const siteBannerCtaSchema = z$1.object({
152000
152103
  label: z$1.string().trim().min(1),
152001
152104
  link: linkPayloadSchema
@@ -152023,7 +152126,8 @@ const EMPTY_SITE_BANNER_BODY = {
152023
152126
  }]
152024
152127
  };
152025
152128
  function analyzeSiteBannerBody(body) {
152026
- const sanitizedBody = sanitizeSiteBannerBodyInternal(body, { limitParagraphs: false });
152129
+ const parsed = RichTextDocSchema.safeParse(body);
152130
+ const sanitizedBody = parsed.success ? sanitizeSiteBannerBodyInternal(parsed.data, { limitParagraphs: false }) : EMPTY_SITE_BANNER_BODY;
152027
152131
  const paragraphs = collectParagraphs(sanitizedBody);
152028
152132
  const textLength = paragraphs.reduce((total, paragraph) => total + paragraphTextLength(paragraph), 0);
152029
152133
  const issues = [];
@@ -152045,7 +152149,7 @@ function siteBannerBodyIssueMessage(issue) {
152045
152149
  }
152046
152150
  }
152047
152151
  function sanitizeSiteBannerBodyInternal(input, options) {
152048
- const content = normalizeTopLevelNodes(unwrapRichTextValue(input).content ?? [], options);
152152
+ const content = normalizeTopLevelNodes(input.content, options);
152049
152153
  return {
152050
152154
  type: "doc",
152051
152155
  content: content.length > 0 ? content : EMPTY_SITE_BANNER_BODY.content
@@ -152126,28 +152230,6 @@ function sanitizeMarks(marks) {
152126
152230
  });
152127
152231
  return sanitized.length > 0 ? sanitized : void 0;
152128
152232
  }
152129
- function unwrapRichTextValue(value) {
152130
- if (isRecord$6(value) && isRecord$6(value.doc)) return unwrapRichTextValue(value.doc);
152131
- if (isRecord$6(value) && value.type === "doc") return {
152132
- type: "doc",
152133
- content: Array.isArray(value.content) ? value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) : []
152134
- };
152135
- return EMPTY_SITE_BANNER_BODY;
152136
- }
152137
- function isTipTapNodeLike(value) {
152138
- return isRecord$6(value) && typeof value.type === "string";
152139
- }
152140
- function coerceTipTapNode(value) {
152141
- return {
152142
- type: value.type,
152143
- ...Array.isArray(value.content) ? { content: value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) } : {},
152144
- ...typeof value.text === "string" ? { text: value.text } : {},
152145
- ...Array.isArray(value.marks) ? { marks: value.marks.filter((mark) => isRecord$6(mark) && typeof mark.type === "string").map((mark) => ({
152146
- type: mark.type,
152147
- ...isRecord$6(mark.attrs) ? { attrs: mark.attrs } : {}
152148
- })) } : {}
152149
- };
152150
- }
152151
152233
  function paragraphTextLength(paragraph) {
152152
152234
  return collectText(paragraph).length;
152153
152235
  }
@@ -153760,20 +153842,14 @@ function textToInlineText(value) {
153760
153842
  /**
153761
153843
  * Extracts the first paragraph text from a TipTap/ProseMirror document.
153762
153844
  *
153763
- * Accepts either the canonical unwrapped form (`{ type: 'doc', content: [...] }`)
153764
- * or the legacy envelope (`{ doc: { type: 'doc', content: [...] } }`). New
153765
- * producers must emit the unwrapped form; this tolerance only exists to read
153766
- * data that pre-dates the unwrap migration.
153845
+ * Accepts the canonical unwrapped form (`{ type: 'doc', content: [...] }`).
153767
153846
  */
153768
153847
  function extractFirstParagraph$1(value) {
153769
- const unwrapped = unwrapRichTextRoot(value);
153770
- if (!isRecord$32(unwrapped)) return null;
153771
- const doc = unwrapped;
153772
- if (!Array.isArray(doc.content)) return null;
153773
- for (const node of doc.content) {
153848
+ if (!isRichTextDoc(value)) return null;
153849
+ for (const node of value.content) {
153774
153850
  if (!isRecord$32(node)) continue;
153775
153851
  if (node.type !== "paragraph" || !Array.isArray(node.content)) continue;
153776
- const text = node.content.filter((item) => isRecord$32(item) && item.type === "text").map((item) => typeof item.text === "string" ? item.text : "").join("").trim();
153852
+ const text = node.content.filter((item) => item.type === "text").map((item) => typeof item.text === "string" ? item.text : "").join("").trim();
153777
153853
  if (text.length > 0) return text;
153778
153854
  }
153779
153855
  return null;
@@ -153822,40 +153898,6 @@ function normalizeBindingInput$1(value) {
153822
153898
  };
153823
153899
  return null;
153824
153900
  }
153825
- /**
153826
- * Normalizes binding output by unwrapping doc wrappers.
153827
- * Rich text fields are sometimes wrapped as { doc: { type: 'doc', content: [...] } }
153828
- * This unwraps them to just the doc content.
153829
- */
153830
- function normalizeBindingOutput$1(value) {
153831
- return normalizeValue(value);
153832
- }
153833
- function normalizeValue(value) {
153834
- if (Array.isArray(value)) return value.map((item) => normalizeValue(item));
153835
- if (!isRecord$32(value)) return value;
153836
- if (isDocWrapper(value)) return normalizeValue(value.doc);
153837
- const entries = Object.entries(value).map(([key, val]) => [key, normalizeValue(val)]);
153838
- return Object.fromEntries(entries);
153839
- }
153840
- /**
153841
- * Strict doc-wrapper detector for recursive structure traversal.
153842
- *
153843
- * Matches ONLY the exact `{ doc: <typed-doc> }` shape with no sibling keys.
153844
- * The strictness is deliberate: `normalizeBindingOutput` (this file) walks
153845
- * every key of every nested object, and a permissive rule would corrupt
153846
- * legitimate structures that happen to carry a field named `doc` (e.g.
153847
- * `{ products: [{ doc: <node>, name: 'Widget' }] }`).
153848
- *
153849
- * For the *root* of a rich-text value — where we know any `doc` envelope
153850
- * is rich-text bookkeeping rather than content — use `unwrapRichTextRoot`
153851
- * (exported from `@riverbankcms/blocks/system/manifest/richTextSchema`).
153852
- */
153853
- function isDocWrapper(value) {
153854
- if (!("doc" in value)) return false;
153855
- if (Object.keys(value).length !== 1) return false;
153856
- const doc = value.doc;
153857
- return isRecord$32(doc) && typeof doc.type === "string" && doc.type === "doc";
153858
- }
153859
153901
  BINDING_TRANSFORM_VALUES$1.join(", ");
153860
153902
  //#endregion
153861
153903
  //#region src/contracts/rendering.ts
@@ -153882,7 +153924,6 @@ const ImagePresets = {
153882
153924
  }
153883
153925
  };
153884
153926
  const isRecord$3 = isRecord$32;
153885
- const normalizeBindingOutput = normalizeBindingOutput$1;
153886
153927
  const applyBindingsToContent = applyBindingsToContent$1;
153887
153928
  const builtInReferenceKindSet = /* @__PURE__ */ new Set([
153888
153929
  "post",
@@ -154333,6 +154374,11 @@ const ContentFieldSchema = z$1.object({
154333
154374
  ui: z$1.record(z$1.string(), z$1.unknown()).optional(),
154334
154375
  itemLabel: z$1.string().optional()
154335
154376
  }).superRefine((field, ctx) => {
154377
+ if ((field.type === "media" || field.type === "file") && field.defaultValue !== void 0 && field.defaultValue !== null && (typeof field.defaultValue !== "object" || Array.isArray(field.defaultValue) || typeof field.defaultValue.identifier !== "string" || !field.defaultValue.identifier.trim())) ctx.addIssue({
154378
+ code: z$1.ZodIssueCode.custom,
154379
+ path: ["defaultValue"],
154380
+ message: `${field.type} defaultValue must be null or an object with a non-empty identifier`
154381
+ });
154336
154382
  if (field.multiple !== void 0 && field.type !== "select") ctx.addIssue({
154337
154383
  code: z$1.ZodIssueCode.custom,
154338
154384
  path: ["multiple"],
@@ -154504,6 +154550,13 @@ function subfieldsOf$1(field) {
154504
154550
  if (direct && direct.length > 0) return direct;
154505
154551
  return field.schema?.fields ?? [];
154506
154552
  }
154553
+ function repeaterVariantFields(field, item) {
154554
+ const itemTypes = field.itemTypes;
154555
+ if (!itemTypes) return subfieldsOf$1(field);
154556
+ const typeId = item._type;
154557
+ if (typeof typeId !== "string") return null;
154558
+ return itemTypes[typeId]?.fields ?? null;
154559
+ }
154507
154560
  function selectOptionValues(field) {
154508
154561
  const options = field.options;
154509
154562
  return options ? options.map((o) => o.value) : [];
@@ -154511,19 +154564,23 @@ function selectOptionValues(field) {
154511
154564
  function isPlainObject(value) {
154512
154565
  return value !== null && typeof value === "object" && !Array.isArray(value);
154513
154566
  }
154514
- /** Field types whose value must be a string. */
154515
154567
  const STRING_TYPES = /* @__PURE__ */ new Set([
154516
154568
  "text",
154517
154569
  "textarea",
154518
154570
  "url",
154519
154571
  "link",
154520
- "media",
154521
- "file",
154522
154572
  "reference",
154523
154573
  "date",
154524
154574
  "datetime",
154525
154575
  "select"
154526
154576
  ]);
154577
+ function isAllowedIdentifierBearingMedia(field, value) {
154578
+ if (!isPlainObject(value) || typeof value.identifier !== "string" || value.identifier.trim().length === 0) return false;
154579
+ if (value.type === void 0) return true;
154580
+ const allowedKinds = field.type === "media" ? field.mediaKinds : field.fileKinds;
154581
+ if (typeof value.type !== "string") return false;
154582
+ return (allowedKinds ?? (field.type === "media" ? ["image"] : ["document"])).some((kind) => kind === value.type);
154583
+ }
154527
154584
  function checkLeafType(field, value, path, errors) {
154528
154585
  const push = (code, message) => {
154529
154586
  errors.push({
@@ -154545,6 +154602,14 @@ function checkLeafType(field, value, path, errors) {
154545
154602
  if (typeof value !== "string" && !isPlainObject(value)) push("wrong_type", `"${path}" must be rich-text (string or document object).`);
154546
154603
  return;
154547
154604
  }
154605
+ if (field.type === "media" || field.type === "file") {
154606
+ if (isPlainObject(value) && isPlaceholderSentinel(value.identifier)) {
154607
+ push("unfilled_placeholder", `"${path}" still contains an unfilled placeholder (${value.identifier}).`);
154608
+ return;
154609
+ }
154610
+ if (!isAllowedIdentifierBearingMedia(field, value)) push("wrong_type", `"${path}" must be a media object with an identifier.`);
154611
+ return;
154612
+ }
154548
154613
  if (field.type === "select") {
154549
154614
  if (typeof value !== "string") {
154550
154615
  push("wrong_type", `"${path}" must be a string.`);
@@ -154558,9 +154623,9 @@ function checkLeafType(field, value, path, errors) {
154558
154623
  if (typeof value !== "string") push("wrong_type", `"${path}" must be a string.`);
154559
154624
  }
154560
154625
  }
154561
- function validateField(field, value, path, errors) {
154626
+ function validateField(field, value, path, errors, allowIncomplete) {
154562
154627
  if (value === void 0 || value === null || value === "") {
154563
- if (field.required === true) errors.push({
154628
+ if (field.required === true && !allowIncomplete) errors.push({
154564
154629
  path,
154565
154630
  fieldType: field.type,
154566
154631
  code: "missing_required",
@@ -154587,7 +154652,7 @@ function validateField(field, value, path, errors) {
154587
154652
  });
154588
154653
  return;
154589
154654
  }
154590
- validateFields(subfieldsOf$1(field), value, path, errors);
154655
+ validateFields(subfieldsOf$1(field), value, path, errors, allowIncomplete);
154591
154656
  return;
154592
154657
  }
154593
154658
  if (field.type === "repeater") {
@@ -154600,7 +154665,6 @@ function validateField(field, value, path, errors) {
154600
154665
  });
154601
154666
  return;
154602
154667
  }
154603
- const itemFields = subfieldsOf$1(field);
154604
154668
  value.forEach((item, index) => {
154605
154669
  const itemPath = `${path}[${index}]`;
154606
154670
  if (!isPlainObject(item)) {
@@ -154612,16 +154676,26 @@ function validateField(field, value, path, errors) {
154612
154676
  });
154613
154677
  return;
154614
154678
  }
154615
- validateFields(itemFields, item, itemPath, errors);
154679
+ const itemFields = repeaterVariantFields(field, item);
154680
+ if (!itemFields) {
154681
+ errors.push({
154682
+ path: itemPath,
154683
+ fieldType: "repeater",
154684
+ code: "invalid_option",
154685
+ message: `"${itemPath}" has an unknown or missing repeater item type.`
154686
+ });
154687
+ return;
154688
+ }
154689
+ validateFields(itemFields, item, itemPath, errors, allowIncomplete);
154616
154690
  });
154617
154691
  return;
154618
154692
  }
154619
154693
  checkLeafType(field, value, path, errors);
154620
154694
  }
154621
- function validateFields(fields, data, pathPrefix, errors) {
154695
+ function validateFields(fields, data, pathPrefix, errors, allowIncomplete) {
154622
154696
  for (const field of fields) {
154623
154697
  const path = pathPrefix ? `${pathPrefix}.${field.id}` : field.id;
154624
- validateField(field, data[field.id], path, errors);
154698
+ validateField(field, data[field.id], path, errors, allowIncomplete);
154625
154699
  }
154626
154700
  }
154627
154701
  //#endregion
@@ -154653,6 +154727,7 @@ function buildFieldValue(field) {
154653
154727
  };
154654
154728
  return {};
154655
154729
  }
154730
+ if (field.type === "media" || field.type === "file") return { identifier: buildSentinel(field) };
154656
154731
  return buildSentinel(field);
154657
154732
  }
154658
154733
  /**
@@ -155062,39 +155137,7 @@ function selectBlocksForDeclaredEntrySlot(input) {
155062
155137
  }
155063
155138
  //#endregion
155064
155139
  //#region ../content-model/src/templates/defaults.ts
155065
- /**
155066
- * Build a canonical TipTap doc from plain-text paragraphs.
155067
- *
155068
- * Local mirror of `createRichTextDoc` from
155069
- * `@riverbankcms/blocks/system/manifest/richTextSchema` — content-model
155070
- * doesn't depend on blocks (blocks sits downstream), but the shape both
155071
- * functions produce is governed by the same Zod schema in blocks.
155072
- *
155073
- * Exported with `@internal` so the cross-package parity test
155074
- * (`packages/dummy-content/src/richTextDoc-parity.test.ts`) can import
155075
- * the *real* implementation rather than reimplement it — eliminates the
155076
- * "test verifies two copies of itself" failure mode.
155077
- *
155078
- * Returns `Record<string, unknown>` rather than the canonical
155079
- * `RichTextDoc` type because that type lives in blocks; downstream
155080
- * consumers narrow it via the canonical schema.
155081
- *
155082
- * @internal
155083
- */
155084
- function createRichTextDocInternal(paragraphs) {
155085
- return {
155086
- type: "doc",
155087
- content: paragraphs.map((text) => text ? {
155088
- type: "paragraph",
155089
- content: [{
155090
- type: "text",
155091
- text
155092
- }]
155093
- } : { type: "paragraph" })
155094
- };
155095
- }
155096
- /** Local alias used by template defaults. Equivalent to `createRichTextDocInternal`. */
155097
- const createRichTextDoc = createRichTextDocInternal;
155140
+ init_richTextSchema();
155098
155141
  const POST_EXCERPT_FALLBACK = fallbackBinding(fieldBinding("excerpt"), transformBinding(TEMPLATE_BINDING_TRANSFORMS.RICHTEXT_FIRST_PARAGRAPH, { input: fieldBinding("body") }));
155099
155142
  const EVENT_SUBHEADLINE_FALLBACK = fallbackBinding(fieldBinding("summary"), transformBinding(TEMPLATE_BINDING_TRANSFORMS.RICHTEXT_FIRST_PARAGRAPH, { input: fieldBinding("body") }));
155100
155143
  const COURSE_SUMMARY_FALLBACK = fallbackBinding(fieldBinding("summary"), transformBinding(TEMPLATE_BINDING_TRANSFORMS.RICHTEXT_FIRST_PARAGRAPH, { input: fieldBinding("body") }));
@@ -155488,13 +155531,9 @@ const META_FIELD_COMPATIBILITY = {
155488
155531
  ]
155489
155532
  };
155490
155533
  /**
155491
- * Nested path suffixes and their compatible types
155492
- * (e.g., richText.doc, media.url)
155534
+ * Nested path suffixes and their compatible types (for example media.url).
155493
155535
  */
155494
155536
  const NESTED_PATH_COMPATIBILITY = {
155495
- "richText.doc": ["richText"],
155496
- "richText.html": ["text", "inlineText"],
155497
- "richText.plain": ["text", "inlineText"],
155498
155537
  "media.url": [
155499
155538
  "text",
155500
155539
  "inlineText",
@@ -155576,21 +155615,6 @@ function contentFieldPaths(fields, parentPath = "", parentLabel = "") {
155576
155615
  label: baseLabel,
155577
155616
  type: "richText",
155578
155617
  category: "content"
155579
- }, {
155580
- path: `${basePath}.doc`,
155581
- label: `${baseLabel} (Document)`,
155582
- type: "richText.doc",
155583
- category: "content"
155584
- }, {
155585
- path: `${basePath}.html`,
155586
- label: `${baseLabel} (HTML)`,
155587
- type: "richText.html",
155588
- category: "content"
155589
- }, {
155590
- path: `${basePath}.plain`,
155591
- label: `${baseLabel} (Plain Text)`,
155592
- type: "richText.plain",
155593
- category: "content"
155594
155618
  });
155595
155619
  continue;
155596
155620
  }
@@ -155780,12 +155804,12 @@ function isBindingDescriptor(value) {
155780
155804
  return source === "field" || source === "meta" || source === "literal" || source === "transform" || source === "fallback";
155781
155805
  }
155782
155806
  /**
155783
- * Check compatibility between a resolved source type (e.g. `'text'`, `'richText'`,
155784
- * `'richText.doc'`) and a target block field type.
155807
+ * Check compatibility between a resolved source type (for example `'text'`,
155808
+ * `'richText'`, or `'media.url'`) and a target block field type.
155785
155809
  *
155786
155810
  * Handles three shapes of source type:
155787
155811
  * - plain content-field type (e.g. `'text'`)
155788
- * - dotted nested suffix (e.g. `'richText.doc'`)
155812
+ * - dotted nested suffix (e.g. `'media.url'`)
155789
155813
  * - meta-typed value (treated as plain content type, since meta types
155790
155814
  * already map cleanly into the matrix)
155791
155815
  */
@@ -156611,7 +156635,7 @@ async function processTemplatePage(input) {
156611
156635
  * - Entry-scoped blocks preserve defaults and resolve bindings over them
156612
156636
  */
156613
156637
  function resolveTemplateBlock(block, bindingContext) {
156614
- const baseContent = normalizeBindingOutput(sanitizeSdkRuntimeBlockContent(block.blockKind, block.content ?? {}));
156638
+ const baseContent = sanitizeSdkRuntimeBlockContent(block.blockKind, block.content ?? {});
156615
156639
  if (block.scope === "template") return {
156616
156640
  id: block.id,
156617
156641
  kind: block.blockKind,
@@ -156619,7 +156643,7 @@ function resolveTemplateBlock(block, bindingContext) {
156619
156643
  content: baseContent
156620
156644
  };
156621
156645
  const bindings = block.bindings ?? {};
156622
- const resolvedContent = typeof bindings === "object" && Object.keys(bindings).length > 0 ? normalizeBindingOutput(applyBindingsToContent(baseContent, bindings, bindingContext)) : baseContent;
156646
+ const resolvedContent = typeof bindings === "object" && Object.keys(bindings).length > 0 ? applyBindingsToContent(baseContent, bindings, bindingContext) : baseContent;
156623
156647
  return {
156624
156648
  id: block.id,
156625
156649
  kind: block.blockKind,
@@ -156663,8 +156687,8 @@ function normalizeNullableEntrySlotContent(content) {
156663
156687
  return content == null ? null : normalizeEntrySlotBlockContent(content);
156664
156688
  }
156665
156689
  function normalizeEntrySlotBlockContent(content) {
156666
- const normalized = normalizeBindingOutput(content ?? {});
156667
- return isRecord$3(normalized) ? normalized : {};
156690
+ const value = content ?? {};
156691
+ return isRecord$3(value) ? value : {};
156668
156692
  }
156669
156693
  //#endregion
156670
156694
  //#region src/rendering/primitives/RichText.tsx