@riverbankcms/sdk 0.108.0 → 0.109.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 (189) hide show
  1. package/README.md +21 -1
  2. package/dist/{PreviewEditorSidebar-BBT-kZAM.mjs → PreviewEditorSidebar-DWqwfy3_.mjs} +7 -22
  3. package/dist/{PreviewEditorUI-D69xNKT2.mjs → PreviewEditorUI-B7OL4sfR.mjs} +3 -3
  4. package/dist/{PreviewQueryProvider-RRu7wyQ5.mjs → PreviewQueryProvider-D0y2l8JN.mjs} +1 -1
  5. package/dist/{SdkPreviewModeRuntime-CuDjXH6o.mjs → SdkPreviewModeRuntime-D8Gt9Gbe.mjs} +212 -153
  6. package/dist/{SiteChromeCustomizeContext-BQUY2dEk.mjs → SiteChromeCustomizeContext-DUpalEdL.mjs} +1919 -1032
  7. package/dist/_dts/ai/src/contracts/proposals.d.ts +11244 -10741
  8. package/dist/_dts/api/src/bookingAdjustments.d.ts +5 -0
  9. package/dist/_dts/api/src/bookingOperations.d.ts +4 -0
  10. package/dist/_dts/api/src/bookingRecords.d.ts +2 -17
  11. package/dist/_dts/api/src/bookingRefundOutcomes.d.ts +6 -6
  12. package/dist/_dts/api/src/common/envelope.d.ts +7 -0
  13. package/dist/_dts/api/src/courses.d.ts +1 -0
  14. package/dist/_dts/api/src/customerMembershipOutcomes.d.ts +5 -5
  15. package/dist/_dts/api/src/endpoints.d.ts +35 -0
  16. package/dist/_dts/api/src/index.d.ts +2 -2
  17. package/dist/_dts/api/src/participantMessaging.d.ts +18 -12
  18. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +1 -0
  19. package/dist/_dts/api/src/sdkContracts.d.ts +247 -1
  20. package/dist/_dts/api/src/siteInfrastructure.d.ts +13 -0
  21. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +1 -5
  22. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +29 -1
  23. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +33 -27
  24. package/dist/_dts/block-form/src/widgets/BackgroundGradientWidget/BackgroundGradientField.d.ts +9 -0
  25. package/dist/_dts/block-form/src/widgets/BackgroundGradientWidget/BackgroundGradientWidget.d.ts +3 -4
  26. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +4 -4
  27. package/dist/_dts/blocks/src/index.d.ts +1 -1
  28. package/dist/_dts/blocks/src/system/blocks/audio-player.d.ts +3 -0
  29. package/dist/_dts/blocks/src/system/blocks/body-text.d.ts +3 -0
  30. package/dist/_dts/blocks/src/system/blocks/contact-newsletter.d.ts +3 -0
  31. package/dist/_dts/blocks/src/system/blocks/course-registration.d.ts +1 -22
  32. package/dist/_dts/blocks/src/system/blocks/cta-full.d.ts +3 -0
  33. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +1 -22
  34. package/dist/_dts/blocks/src/system/blocks/file-download.d.ts +3 -0
  35. package/dist/_dts/blocks/src/system/blocks/form.d.ts +3 -0
  36. package/dist/_dts/blocks/src/system/blocks/image-gallery.d.ts +3 -0
  37. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +3 -0
  38. package/dist/_dts/blocks/src/system/blocks/newsletter-signup.d.ts +3 -0
  39. package/dist/_dts/blocks/src/system/blocks/site-header.d.ts +3 -0
  40. package/dist/_dts/blocks/src/system/blocks/team-members.d.ts +3 -0
  41. package/dist/_dts/blocks/src/system/blocks/testimonials.d.ts +3 -0
  42. package/dist/_dts/blocks/src/system/blocks/video-grid.d.ts +3 -0
  43. package/dist/_dts/blocks/src/system/constants/background.d.ts +1 -1
  44. package/dist/_dts/blocks/src/system/customize/backgrounds.d.ts +16 -6
  45. package/dist/_dts/blocks/src/system/defineBlock.d.ts +9 -1
  46. package/dist/_dts/blocks/src/system/fields/ctas.d.ts +8 -0
  47. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +31 -0
  48. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +6 -6
  49. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +3 -3
  50. package/dist/_dts/blocks/src/system/runtime/nodes/shared/contract-schemas.d.ts +1 -1
  51. package/dist/_dts/content-model/src/content-types/entryValidation.d.ts +31 -0
  52. package/dist/_dts/content-model/src/content-types/scaffold.d.ts +58 -0
  53. package/dist/_dts/content-model/src/content-types/sentinels.d.ts +36 -0
  54. package/dist/_dts/content-model/src/index.d.ts +3 -0
  55. package/dist/_dts/core/src/booking-date-time.d.ts +8 -0
  56. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +3071 -3157
  57. package/dist/_dts/media-core/src/fileMimeDetection.d.ts +16 -0
  58. package/dist/_dts/media-core/src/fileStorage.d.ts +8 -0
  59. package/dist/_dts/media-core/src/storagePathDomain.d.ts +2 -0
  60. package/dist/_dts/media-core/src/uploadVerificationContract.d.ts +3 -0
  61. package/dist/_dts/preview-next/src/client/blocks/BlockToolbar.d.ts +2 -0
  62. package/dist/_dts/preview-next/src/client/preview/StyleConfigurator.state.d.ts +1 -0
  63. package/dist/_dts/preview-next/src/client/preview/styleConfiguratorApplyPayload.d.ts +1 -1
  64. package/dist/_dts/sdk/src/cli/commands/audit.d.ts +1 -1
  65. package/dist/_dts/sdk/src/cli/commands/compare.d.ts +1 -1
  66. package/dist/_dts/sdk/src/cli/commands/deploy.d.ts +1 -1
  67. package/dist/_dts/sdk/src/cli/commands/drafts.d.ts +18 -0
  68. package/dist/_dts/sdk/src/cli/commands/entry.d.ts +15 -0
  69. package/dist/_dts/sdk/src/cli/commands/init-docs.d.ts +2 -2
  70. package/dist/_dts/sdk/src/cli/commands/publish-all.d.ts +1 -1
  71. package/dist/_dts/sdk/src/cli/commands/pull.d.ts +1 -0
  72. package/dist/_dts/sdk/src/cli/commands/push/execute/diff.d.ts +1 -1
  73. package/dist/_dts/sdk/src/cli/commands/push/options.d.ts +3 -0
  74. package/dist/_dts/sdk/src/cli/commands/push-execute.d.ts +12 -1
  75. package/dist/_dts/sdk/src/cli/commands/setup.d.ts +1 -0
  76. package/dist/_dts/sdk/src/cli/commands/verify.d.ts +1 -0
  77. package/dist/_dts/sdk/src/cli/helpers.d.ts +22 -1
  78. package/dist/_dts/sdk/src/cli/index.d.ts +4 -0
  79. package/dist/_dts/sdk/src/cli/output.d.ts +88 -0
  80. package/dist/_dts/sdk/src/cli/program.d.ts +29 -0
  81. package/dist/_dts/sdk/src/cli/push-config.d.ts +1 -1
  82. package/dist/_dts/sdk/src/client/management/catalog.d.ts +10 -0
  83. package/dist/_dts/sdk/src/client/management/http.d.ts +7 -1
  84. package/dist/_dts/sdk/src/client/management/index.d.ts +1 -1
  85. package/dist/_dts/sdk/src/client/management/types.d.ts +43 -19
  86. package/dist/_dts/sdk/src/date-time/index.d.ts +68 -0
  87. package/dist/_dts/sdk/src/public-api/contracts.d.ts +2 -0
  88. package/dist/_dts/sdk/src/version.d.ts +1 -1
  89. package/dist/_dts/site-commands/src/commands.d.ts +893 -823
  90. package/dist/_dts/theme-core/src/accordions/types.d.ts +1 -1
  91. package/dist/_dts/theme-core/src/buttons/core.d.ts +15 -46
  92. package/dist/_dts/theme-core/src/buttons/effectClassification.d.ts +147 -0
  93. package/dist/_dts/theme-core/src/buttons/effects/types.d.ts +2 -3
  94. package/dist/_dts/theme-core/src/buttons/generateDefaultButtonSystem.d.ts +1 -1
  95. package/dist/_dts/theme-core/src/buttons/index.d.ts +1 -0
  96. package/dist/_dts/theme-core/src/buttons/personalities/types.d.ts +44 -32
  97. package/dist/_dts/theme-core/src/buttons/presets.d.ts +4 -25
  98. package/dist/_dts/theme-core/src/buttons/resolveButtonColourStates.d.ts +100 -0
  99. package/dist/_dts/theme-core/src/buttons/resolvedButtonCss.d.ts +16 -0
  100. package/dist/_dts/theme-core/src/buttons/types.d.ts +101 -83
  101. package/dist/_dts/theme-core/src/cards/types.d.ts +16 -16
  102. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  103. package/dist/_dts/theme-core/src/gradients/presets.d.ts +200 -8
  104. package/dist/_dts/theme-core/src/gradients/utils.d.ts +6 -2
  105. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +87 -126
  106. package/dist/_dts/theme-core/src/mock-themes/modern-minimal.d.ts +1 -1
  107. package/dist/_dts/theme-core/src/navigation/types.d.ts +2 -2
  108. package/dist/_dts/theme-core/src/palette/curated/index.d.ts +2 -2
  109. package/dist/_dts/theme-core/src/palette/curated/types.d.ts +5 -8
  110. package/dist/_dts/theme-core/src/palette/index.d.ts +0 -1
  111. package/dist/_dts/theme-core/src/schema.d.ts +112 -138
  112. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  113. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +10 -10
  114. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorModel.d.ts +3 -2
  115. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +13 -6
  116. package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +29 -8
  117. package/dist/_dts/theme-core/src/site-styles/styleTags.d.ts +2 -2
  118. package/dist/cli/index.mjs +67956 -66745
  119. package/dist/cli/init-docs/content/cli-reference.md +50 -7
  120. package/dist/cli/init-docs/content/content-management.md +7 -4
  121. package/dist/cli/init-docs/content/workflow-publish.md +5 -1
  122. package/dist/client/{DiscountCodeDisclosure-D6TOf6Hi.mjs → DiscountCodeDisclosure-DU1ygxU2.mjs} +1 -1
  123. package/dist/client/{EventCombined.client-Dao3zxRk.mjs → EventCombined.client-DB5zagCo.mjs} +3 -3
  124. package/dist/client/{EventListing.client-B2NNt4y1.mjs → EventListing.client-CTX9JHBL.mjs} +2 -2
  125. package/dist/client/{LeafletStaticMap-BhwFo16w.mjs → LeafletStaticMap-BYImhgiB.mjs} +1 -1
  126. package/dist/client/{MagicLinkForm-BWnB7cMR.mjs → MagicLinkForm-CRkw_M3l.mjs} +2 -2
  127. package/dist/client/{StepIndicator-TzRiQ0BY.mjs → StepIndicator-BnmVBJJh.mjs} +2 -2
  128. package/dist/client/{accordion-BPy-AJ4_.mjs → accordion-CeGreumd.mjs} +1 -1
  129. package/dist/client/{bookingForm-BL4PGZds.mjs → bookingForm-CSzFNrpN.mjs} +6 -6
  130. package/dist/client/bookings.mjs +512 -252
  131. package/dist/client/{buttons-EBjyslaX.mjs → buttons-DoFV7KDL.mjs} +493 -230
  132. package/dist/client/{carousel-C2b7Cn9-.mjs → carousel-B2jDtogf.mjs} +1 -1
  133. package/dist/client/{cart-BQcbXIVD.mjs → cart-D-jZ-vaY.mjs} +3 -3
  134. package/dist/client/{checkout-DEmpGRJu.mjs → checkout-Dza-LA7N.mjs} +3 -3
  135. package/dist/client/client.mjs +1673 -785
  136. package/dist/client/{colorStyles-jZS6KfkP.mjs → colorStyles-CrgzoXsm.mjs} +1 -30
  137. package/dist/client/{courseRegistration-BqRZPmsq.mjs → courseRegistration-TYkNychb.mjs} +7 -7
  138. package/dist/client/{eventCalendar-B8N_tSl5.mjs → eventCalendar-BI_90SsT.mjs} +5 -5
  139. package/dist/client/{eventCombined-Gjn6gtq7.mjs → eventCombined-mNrpryE9.mjs} +2 -2
  140. package/dist/client/{eventDetails-M-VCxA-r.mjs → eventDetails-D4GVlCIY.mjs} +3 -3
  141. package/dist/client/{eventListing-DR19hqYi.mjs → eventListing-YhKN018y.mjs} +2 -2
  142. package/dist/client/{eventRegistration-BR02a0k3.mjs → eventRegistration-CdHGtilr.mjs} +7 -7
  143. package/dist/client/foregroundContract-xICPq59x.mjs +31 -0
  144. package/dist/client/{form-LY2xIbwg.mjs → form-AcAL45rA.mjs} +2 -2
  145. package/dist/client/{headerSection-CYwwXJfh.mjs → headerSection-uEC52ueD.mjs} +3 -3
  146. package/dist/client/hooks.mjs +890 -497
  147. package/dist/client/{locationMap-DWnBXrFq.mjs → locationMap-_wJDSUYe.mjs} +3 -3
  148. package/dist/client/{media-DHtSxS5V.mjs → media-BK0O3b-U.mjs} +3 -2
  149. package/dist/client/{newsletterForm-D9s-fQ2P.mjs → newsletterForm-DrArsb2B.mjs} +2 -2
  150. package/dist/client/{pricing-DDq80oYc.mjs → pricing-BSYVghwo.mjs} +1 -1
  151. package/dist/client/{productDetail-B0SbWd45.mjs → productDetail-Dyk90sMs.mjs} +3 -3
  152. package/dist/client/{productList-PwYlZn0y.mjs → productList-BCKThwAI.mjs} +3 -3
  153. package/dist/client/{propsDecoder-BRWBiH7i.mjs → propsDecoder-CkXxkdSR.mjs} +63 -24
  154. package/dist/client/rendering/client.mjs +1034 -486
  155. package/dist/client/rendering/islands.mjs +19 -19
  156. package/dist/client/rendering.mjs +1832 -760
  157. package/dist/client/{shop-CMwZPYME.mjs → shop-DeltVH10.mjs} +6 -6
  158. package/dist/client/{shop-commerce-BuxsFate.mjs → shop-commerce-BD9Jsm2Z.mjs} +6 -6
  159. package/dist/client/{shop-commerce-store-boYlEJ91.mjs → shop-commerce-store-CmXD6FDS.mjs} +1 -1
  160. package/dist/client/{shopShared-D2naaOes.mjs → shopShared-B8v-lH_F.mjs} +2 -2
  161. package/dist/client/{useDiscountCodeUi-D9RHpVoj.mjs → useDiscountCodeUi-DbWJN8ld.mjs} +1 -1
  162. package/dist/client/{useEventFilters-igGiK1re.mjs → useEventFilters-EJIMUnqT.mjs} +46 -69
  163. package/dist/client/{usePolling-CI7MbsFB.mjs → usePolling-Cmh14fc7.mjs} +2 -2
  164. package/dist/preview-next/before-render.mjs +35 -0
  165. package/dist/preview-next/client/runtime.mjs +4 -4
  166. package/dist/preview-next/middleware.mjs +35 -0
  167. package/dist/{previewAuthStore-a_cBD28m.mjs → previewAuthStore-CTO7CRPk.mjs} +35 -0
  168. package/dist/{sdk-runtime-MBzD-GmP.mjs → sdk-runtime-Dou0GAav.mjs} +2 -2
  169. package/dist/{separator-5ahRBC85.mjs → separator-CI5wZNXT.mjs} +1 -1
  170. package/dist/server/components.mjs +1832 -784
  171. package/dist/server/config-validation.mjs +890 -497
  172. package/dist/server/config.mjs +890 -497
  173. package/dist/server/data.mjs +890 -497
  174. package/dist/server/date-time.mjs +386 -0
  175. package/dist/server/index.mjs +36 -1
  176. package/dist/server/next.mjs +1868 -785
  177. package/dist/server/page-converter.mjs +849 -285
  178. package/dist/server/prebuild.mjs +1 -1
  179. package/dist/server/rendering/server.mjs +1553 -718
  180. package/dist/server/rendering.mjs +1832 -784
  181. package/dist/server/routing.mjs +933 -522
  182. package/dist/server/server.mjs +929 -501
  183. package/dist/server/theme-bridge.mjs +1408 -392
  184. package/dist/server/theme.mjs +471 -228
  185. package/dist/styles/index.css +5 -0
  186. package/package.json +7 -1
  187. package/dist/_dts/theme-core/src/buttons/presets/types.d.ts +0 -88
  188. package/dist/_dts/theme-core/src/buttons/utils/contrast.d.ts +0 -38
  189. package/dist/_dts/theme-core/src/palette/themePaletteCompatibility.d.ts +0 -57
@@ -6329,6 +6329,41 @@ const API_ENDPOINTS$1 = {
6329
6329
  auth: "service",
6330
6330
  responseKind: "json"
6331
6331
  },
6332
+ sdkCatalogTheme: {
6333
+ path: "/sdk/{siteId}/catalog/theme",
6334
+ method: "GET",
6335
+ errors: ["server:internal_error"],
6336
+ auth: "service",
6337
+ responseKind: "json"
6338
+ },
6339
+ sdkCatalogBlocks: {
6340
+ path: "/sdk/{siteId}/catalog/blocks",
6341
+ method: "GET",
6342
+ errors: ["server:internal_error"],
6343
+ auth: "service",
6344
+ responseKind: "json"
6345
+ },
6346
+ sdkCatalogContentTypes: {
6347
+ path: "/sdk/{siteId}/catalog/content-types",
6348
+ method: "GET",
6349
+ errors: ["server:internal_error"],
6350
+ auth: "service",
6351
+ responseKind: "json"
6352
+ },
6353
+ sdkCapabilities: {
6354
+ path: "/sdk/{siteId}/capabilities",
6355
+ method: "GET",
6356
+ errors: ["server:internal_error"],
6357
+ auth: "service",
6358
+ responseKind: "json"
6359
+ },
6360
+ sdkListDemoContent: {
6361
+ path: "/sdk/{siteId}/demo-content",
6362
+ method: "GET",
6363
+ errors: ["server:internal_error"],
6364
+ auth: "service",
6365
+ responseKind: "json"
6366
+ },
6332
6367
  sdkPullPages: {
6333
6368
  path: "/sdk/{siteId}/pull/pages",
6334
6369
  method: "GET",
@@ -7447,19 +7482,29 @@ function isRecord$33(value) {
7447
7482
  //#region ../core/src/booking-date-time.ts
7448
7483
  const DEFAULT_LOCALE = "en-US";
7449
7484
  const FALLBACK_TIMEZONE = "UTC";
7485
+ const DATE_TIME_FORMATTER_CACHE_LIMIT = 100;
7486
+ const dateTimeFormatterCache = /* @__PURE__ */ new Map();
7487
+ const timeZoneValidityCache = /* @__PURE__ */ new Map();
7450
7488
  function isValidTimeZone$1(timeZone) {
7489
+ const cached = timeZoneValidityCache.get(timeZone);
7490
+ if (cached !== void 0) return cached;
7491
+ let isValid;
7451
7492
  try {
7452
7493
  new Intl.DateTimeFormat(DEFAULT_LOCALE, { timeZone }).format(/* @__PURE__ */ new Date());
7453
- return true;
7494
+ isValid = true;
7454
7495
  } catch {
7455
- return false;
7496
+ isValid = false;
7456
7497
  }
7498
+ if (timeZoneValidityCache.size >= DATE_TIME_FORMATTER_CACHE_LIMIT) timeZoneValidityCache.clear();
7499
+ timeZoneValidityCache.set(timeZone, isValid);
7500
+ return isValid;
7457
7501
  }
7458
7502
  function getSafeTimeZone(timeZone) {
7459
7503
  const normalized = typeof timeZone === "string" ? timeZone.trim() : "";
7460
7504
  return normalized.length > 0 && isValidTimeZone$1(normalized) ? normalized : FALLBACK_TIMEZONE;
7461
7505
  }
7462
7506
  function parseStrictBookingTimeZone(timeZone) {
7507
+ if (typeof timeZone !== "string") return null;
7463
7508
  const normalized = timeZone.trim();
7464
7509
  if (normalized.length === 0 || !isValidTimeZone$1(normalized)) return null;
7465
7510
  return normalized;
@@ -7472,10 +7517,19 @@ function formatParseError(error) {
7472
7517
  }
7473
7518
  }
7474
7519
  function formatToPartsLookup(date, locale, timeZone, format) {
7475
- return new Map(new Intl.DateTimeFormat(locale, {
7520
+ return new Map(getCachedDateTimeFormatter(locale, timeZone, format).formatToParts(date).map((part) => [part.type, part.value]));
7521
+ }
7522
+ function getCachedDateTimeFormatter(locale, timeZone, format) {
7523
+ const key = `${locale}\u0000${timeZone}\u0000${JSON.stringify(Object.entries(format).sort(([left], [right]) => left.localeCompare(right)))}`;
7524
+ const cached = dateTimeFormatterCache.get(key);
7525
+ if (cached !== void 0) return cached;
7526
+ const formatter = new Intl.DateTimeFormat(locale, {
7476
7527
  ...format,
7477
7528
  timeZone
7478
- }).formatToParts(date).map((part) => [part.type, part.value]));
7529
+ });
7530
+ if (dateTimeFormatterCache.size >= DATE_TIME_FORMATTER_CACHE_LIMIT) dateTimeFormatterCache.clear();
7531
+ dateTimeFormatterCache.set(key, formatter);
7532
+ return formatter;
7479
7533
  }
7480
7534
  function getZonedDateTimeParts(date, timeZone) {
7481
7535
  const parts = formatToPartsLookup(date, "en-GB", timeZone, {
@@ -7521,23 +7575,9 @@ function toUtcIsoDateTimeString(value) {
7521
7575
  return parsed.value;
7522
7576
  }
7523
7577
  function parseScheduledBookingDateTime(input) {
7578
+ let startsAt;
7524
7579
  try {
7525
- const startsAt = toUtcIsoDateTimeString(input.startsAt);
7526
- const timeZone = parseStrictBookingTimeZone(input.timeZone);
7527
- if (timeZone === null) return {
7528
- ok: false,
7529
- field: "timeZone",
7530
- reason: "invalid_time_zone",
7531
- value: input.timeZone
7532
- };
7533
- return {
7534
- ok: true,
7535
- value: {
7536
- startsAt,
7537
- timeZone,
7538
- locale: input.locale
7539
- }
7540
- };
7580
+ startsAt = toUtcIsoDateTimeString(input.startsAt);
7541
7581
  } catch {
7542
7582
  return {
7543
7583
  ok: false,
@@ -7546,6 +7586,21 @@ function parseScheduledBookingDateTime(input) {
7546
7586
  value: input.startsAt
7547
7587
  };
7548
7588
  }
7589
+ const timeZone = parseStrictBookingTimeZone(input.timeZone);
7590
+ if (timeZone === null) return {
7591
+ ok: false,
7592
+ field: "timeZone",
7593
+ reason: "invalid_time_zone",
7594
+ value: typeof input.timeZone === "string" ? input.timeZone : ""
7595
+ };
7596
+ return {
7597
+ ok: true,
7598
+ value: {
7599
+ startsAt,
7600
+ timeZone,
7601
+ locale: input.locale
7602
+ }
7603
+ };
7549
7604
  }
7550
7605
  function parseScheduledBookingDateRange(input) {
7551
7606
  const start = parseScheduledBookingDateTime(input);
@@ -7584,10 +7639,7 @@ function toScheduledBookingDateRange(input) {
7584
7639
  function formatBookingInstant(isoString, options) {
7585
7640
  const date = new Date(isoString);
7586
7641
  if (Number.isNaN(date.getTime())) return isoString;
7587
- return new Intl.DateTimeFormat(options.locale ?? DEFAULT_LOCALE, {
7588
- ...options.format,
7589
- timeZone: getSafeTimeZone(options.timeZone)
7590
- }).format(date);
7642
+ return getCachedDateTimeFormatter(options.locale ?? DEFAULT_LOCALE, getSafeTimeZone(options.timeZone), options.format).format(date);
7591
7643
  }
7592
7644
  function formatBookingDate(input, format = {
7593
7645
  weekday: "long",
@@ -8025,7 +8077,8 @@ function validateRelativeStoragePath(value) {
8025
8077
  if (value.startsWith("sites/")) return fail({ tag: "SITE_SCOPED_PATH" });
8026
8078
  if (value.startsWith("/")) return fail({ tag: "LEADING_SLASH" });
8027
8079
  if (value.includes("//")) return fail({ tag: "DOUBLE_SLASH" });
8028
- if (value.split("/").some((segment) => segment === "..")) return fail({ tag: "TRAVERSAL" });
8080
+ if (value.includes("%")) return fail({ tag: "PERCENT_ENCODING" });
8081
+ if (value.split("/").some((segment) => segment === "." || segment === "..")) return fail({ tag: "TRAVERSAL" });
8029
8082
  return ok$2(value);
8030
8083
  }
8031
8084
  function brandRelativeStoragePath(value) {
@@ -9342,6 +9395,20 @@ const blockCategoryEnum = z$1.enum([
9342
9395
  "layout",
9343
9396
  "interactive"
9344
9397
  ]);
9398
+ const blockContentSourceSchema = z$1.object({
9399
+ kind: z$1.enum([
9400
+ "inline",
9401
+ "entries",
9402
+ "mixed"
9403
+ ]),
9404
+ contentType: z$1.string().min(1).optional()
9405
+ }).superRefine((value, ctx) => {
9406
+ if (value.kind === "inline" && value.contentType) ctx.addIssue({
9407
+ code: z$1.ZodIssueCode.custom,
9408
+ path: ["contentType"],
9409
+ message: "contentType is not allowed when contentSource.kind is \"inline\""
9410
+ });
9411
+ });
9345
9412
  z$1.object({
9346
9413
  name: z$1.string().min(1),
9347
9414
  version: z$1.string().min(1),
@@ -9372,6 +9439,12 @@ z$1.object({
9372
9439
  }).optional()).optional(),
9373
9440
  category: blockCategoryEnum.optional(),
9374
9441
  contentTypes: z$1.array(z$1.string()).optional(),
9442
+ /**
9443
+ * Explicit content-source classification (inline / entries / mixed). Optional at the
9444
+ * schema level for backward compatibility with SDK-defined custom blocks, but every
9445
+ * registered SYSTEM block must declare it (enforced by a registry-wide test).
9446
+ */
9447
+ contentSource: blockContentSourceSchema.optional(),
9375
9448
  tags: z$1.array(z$1.string()).optional(),
9376
9449
  icon: z$1.string().optional()
9377
9450
  });
@@ -10915,7 +10988,7 @@ function normalizeStoredObjectChildren(children, value, ctx) {
10915
10988
  return normalized;
10916
10989
  }
10917
10990
  function normalizeStoredRepeaterItems(plan, value, ctx) {
10918
- if (!isUnknownArray$2(value)) return value;
10991
+ if (!isUnknownArray$3(value)) return value;
10919
10992
  return value.map((item) => {
10920
10993
  if (!item || typeof item !== "object" || Array.isArray(item)) return item;
10921
10994
  const itemRecord = item;
@@ -10974,7 +11047,7 @@ function getAtFieldPath(source, path) {
10974
11047
  }, source);
10975
11048
  }
10976
11049
  function normalizeRepeaterItems(plan, value, ctx) {
10977
- if (!isUnknownArray$2(value)) return value;
11050
+ if (!isUnknownArray$3(value)) return value;
10978
11051
  return value.map((item) => {
10979
11052
  if (!item || typeof item !== "object" || Array.isArray(item)) return item;
10980
11053
  const itemRecord = item;
@@ -11002,7 +11075,7 @@ function validateNormalizedFieldValue(plan, value, ctx) {
11002
11075
  case "select": return validateSelectPlan(plan, value);
11003
11076
  case "richText": return isValidRichTextDoc(value) ? [] : [issue(plan, "invalidRichText")];
11004
11077
  case "inlineText": return validateInlineTextPlan(plan, value);
11005
- case "media": return isPlainObject$4(value) ? [] : [issue(plan, "invalidMedia")];
11078
+ case "media": return isPlainObject$5(value) ? [] : [issue(plan, "invalidMedia")];
11006
11079
  case "link": return linkSchema.safeParse(value).success ? [] : [issue(plan, "invalidLink")];
11007
11080
  case "repeater": return validateRepeaterPlan(plan, value, ctx);
11008
11081
  case "group": return validateGroupPlan(plan, value, ctx);
@@ -11095,7 +11168,7 @@ function validateRepeaterPlan(plan, value, ctx) {
11095
11168
  }), ...value.flatMap((item, index) => validateRepeaterItem(plan, item, index, ctx))];
11096
11169
  }
11097
11170
  function validateRepeaterItem(plan, item, index, ctx) {
11098
- if (!isPlainObject$4(item)) return [];
11171
+ if (!isPlainObject$5(item)) return [];
11099
11172
  const itemType = item._type;
11100
11173
  const fields = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemType)?.fields : plan.repeatedItemPlan;
11101
11174
  if (!fields) return [];
@@ -11104,7 +11177,7 @@ function validateRepeaterItem(plan, item, index, ctx) {
11104
11177
  });
11105
11178
  }
11106
11179
  function validateGroupPlan(plan, value, ctx) {
11107
- if (!isPlainObject$4(value)) return [issue(plan, "wrongType", { expected: "an object" })];
11180
+ if (!isPlainObject$5(value)) return [issue(plan, "wrongType", { expected: "an object" })];
11108
11181
  const childCtx = childValidationContext(plan, ctx);
11109
11182
  return plan.children.flatMap((childPlan) => validateNormalizedFieldValue(childPlan, value[childPlan.fieldId], childCtx));
11110
11183
  }
@@ -11301,7 +11374,7 @@ function sameFieldPath(left, right) {
11301
11374
  return left.length === right.length && left.every((segment, index) => segment === right[index]);
11302
11375
  }
11303
11376
  function validateExplicitNullsForPush(plans, content, ctx) {
11304
- if (!isPlainObject$4(content)) return [];
11377
+ if (!isPlainObject$5(content)) return [];
11305
11378
  return plans.flatMap((plan) => validateExplicitNullForPushPlan(plan, content[plan.fieldId], ctx));
11306
11379
  }
11307
11380
  function validateExplicitNullForPushPlan(plan, value, ctx) {
@@ -11322,9 +11395,9 @@ function validateExplicitNullForPushPlan(plan, value, ctx) {
11322
11395
  }
11323
11396
  }
11324
11397
  function validateExplicitNullsForPushRepeater(plan, value, ctx) {
11325
- if (!isUnknownArray$2(value)) return [];
11398
+ if (!isUnknownArray$3(value)) return [];
11326
11399
  return value.flatMap((item, index) => {
11327
- if (!isPlainObject$4(item)) return [];
11400
+ if (!isPlainObject$5(item)) return [];
11328
11401
  const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
11329
11402
  if (!childPlans) return [];
11330
11403
  return childPlans.flatMap((childPlan) => validateExplicitNullForPushPlan(materializeRepeaterItemPlan(childPlan, plan.path, index), item[childPlan.fieldId], ctx));
@@ -11407,19 +11480,19 @@ function isEmptyValue$1(value) {
11407
11480
  if (isEmptyRichTextDoc(value)) return true;
11408
11481
  return false;
11409
11482
  }
11410
- function isPlainObject$4(value) {
11483
+ function isPlainObject$5(value) {
11411
11484
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
11412
11485
  }
11413
- function isUnknownArray$2(value) {
11486
+ function isUnknownArray$3(value) {
11414
11487
  return Array.isArray(value);
11415
11488
  }
11416
11489
  function isValidRichTextDoc(value) {
11417
11490
  const doc = richTextDoc(value);
11418
- return Boolean(isPlainObject$4(doc) && doc.type === "doc");
11491
+ return Boolean(isPlainObject$5(doc) && doc.type === "doc");
11419
11492
  }
11420
11493
  function isEmptyRichTextDoc(value) {
11421
11494
  const doc = richTextDoc(value);
11422
- if (!isPlainObject$4(doc)) return false;
11495
+ if (!isPlainObject$5(doc)) return false;
11423
11496
  if (doc.type !== "doc") return false;
11424
11497
  const content = doc.content;
11425
11498
  if (!Array.isArray(content) || content.length === 0) return true;
@@ -11428,8 +11501,8 @@ function isEmptyRichTextDoc(value) {
11428
11501
  return first?.type === "paragraph" && (!Array.isArray(first.content) || first.content.length === 0);
11429
11502
  }
11430
11503
  function richTextDoc(value) {
11431
- if (!isPlainObject$4(value)) return value;
11432
- return isPlainObject$4(value.doc) ? value.doc : value;
11504
+ if (!isPlainObject$5(value)) return value;
11505
+ return isPlainObject$5(value.doc) ? value.doc : value;
11433
11506
  }
11434
11507
  //#endregion
11435
11508
  //#region ../blocks/src/system/manifest/registry.ts
@@ -11497,7 +11570,7 @@ function resolveVariantKey(manifest, content) {
11497
11570
  const clamp01$3 = (x) => Math.max(0, Math.min(1, x));
11498
11571
  const mod = (a, n) => (a % n + n) % n;
11499
11572
  const deg = (x) => mod(x, 360);
11500
- function hexToRgb$2(hex) {
11573
+ function hexToRgb$1(hex) {
11501
11574
  const h = hex.replace(/^#/, "");
11502
11575
  const m = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
11503
11576
  if (m.length !== 6) throw new Error("Bad hex");
@@ -11606,7 +11679,7 @@ function oklchToHexGamut(L, C, h) {
11606
11679
  return rgbToHex$1(r, g, bb);
11607
11680
  }
11608
11681
  function featuresFromHex(hex) {
11609
- const { r, g, b } = hexToRgb$2(hex);
11682
+ const { r, g, b } = hexToRgb$1(hex);
11610
11683
  const { h, s, l } = rgbToHsl$1(r, g, b);
11611
11684
  const { L, a, b: b2 } = srgbToOklab(r, g, b);
11612
11685
  const { L: okL, C: okC, h: okH } = oklabToOklch(L, a, b2);
@@ -11633,8 +11706,8 @@ function relativeLuminance(r, g, b) {
11633
11706
  return .2126 * R + .7152 * G + .0722 * B;
11634
11707
  }
11635
11708
  function contrastRatio(hex1, hex2) {
11636
- const { r: r1, g: g1, b: b1 } = hexToRgb$2(hex1);
11637
- const { r: r2, g: g2, b: b2 } = hexToRgb$2(hex2);
11709
+ const { r: r1, g: g1, b: b1 } = hexToRgb$1(hex1);
11710
+ const { r: r2, g: g2, b: b2 } = hexToRgb$1(hex2);
11638
11711
  const L1 = relativeLuminance(r1, g1, b1);
11639
11712
  const L2 = relativeLuminance(r2, g2, b2);
11640
11713
  const [hi, lo] = L1 >= L2 ? [L1, L2] : [L2, L1];
@@ -12656,6 +12729,10 @@ const modernMinimalTheme = {
12656
12729
  name: "primary",
12657
12730
  hex: "#1f2937"
12658
12731
  },
12732
+ {
12733
+ name: "primaryForeground",
12734
+ hex: "#ffffff"
12735
+ },
12659
12736
  {
12660
12737
  name: "secondary",
12661
12738
  hex: "#6b7280"
@@ -12684,6 +12761,14 @@ const modernMinimalTheme = {
12684
12761
  name: "surface",
12685
12762
  hex: "#ffffff"
12686
12763
  },
12764
+ {
12765
+ name: "surfaceAlt",
12766
+ hex: "#f9fafb"
12767
+ },
12768
+ {
12769
+ name: "muted",
12770
+ hex: "#f3f4f6"
12771
+ },
12687
12772
  {
12688
12773
  name: "text",
12689
12774
  hex: "#1f2937"
@@ -12792,7 +12877,7 @@ function generateCssVars(palette) {
12792
12877
  const cssVars = {};
12793
12878
  Object.entries(palette).forEach(([key, value]) => {
12794
12879
  if (key === "meta" || value === null || value === void 0 || typeof value !== "string") return;
12795
- const { r, g, b } = hexToRgb$2(value);
12880
+ const { r, g, b } = hexToRgb$1(value);
12796
12881
  cssVars[`--tb-${key}`] = `${r} ${g} ${b}`;
12797
12882
  });
12798
12883
  return cssVars;
@@ -12859,7 +12944,7 @@ function getTextColorForBackground(background) {
12859
12944
  }
12860
12945
  function parseColorToRgb(input) {
12861
12946
  const trimmed = input.trim();
12862
- if (trimmed.startsWith("#")) return hexToRgb$2(trimmed);
12947
+ if (trimmed.startsWith("#")) return hexToRgb$1(trimmed);
12863
12948
  const rgbMatch = trimmed.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i);
12864
12949
  if (rgbMatch?.[1] && rgbMatch[2] && rgbMatch[3]) return {
12865
12950
  r: clamp255(Number(rgbMatch[1])),
@@ -16083,6 +16168,111 @@ function boundaryKey$1(previousBlockId, nextBlockId) {
16083
16168
  return makeBoundaryId(previousBlockId, nextBlockId);
16084
16169
  }
16085
16170
  //#endregion
16171
+ //#region ../theme-core/src/buttons/effectClassification.ts
16172
+ /**
16173
+ * Exhaustive live policy for every registered effect id.
16174
+ *
16175
+ * The resolver consumes the transition payload directly, so adding an effect
16176
+ * cannot update classification without also defining its runtime behavior.
16177
+ */
16178
+ const BUTTON_EFFECT_POLICIES = {
16179
+ "darken-background": {
16180
+ classification: "colour-transition",
16181
+ transition: {
16182
+ kind: "adjust-shade",
16183
+ direction: "darker"
16184
+ }
16185
+ },
16186
+ "lighten-background": {
16187
+ classification: "colour-transition",
16188
+ transition: {
16189
+ kind: "adjust-shade",
16190
+ direction: "lighter"
16191
+ }
16192
+ },
16193
+ "background-on-hover": {
16194
+ classification: "colour-transition",
16195
+ transition: { kind: "replace-background" }
16196
+ },
16197
+ "background-on-hover-alpha": {
16198
+ classification: "unsupported-colour",
16199
+ reason: "alpha_requires_surface"
16200
+ },
16201
+ "fade-background": {
16202
+ classification: "unsupported-colour",
16203
+ reason: "changes_element_opacity"
16204
+ },
16205
+ "opacity-reduce": {
16206
+ classification: "unsupported-colour",
16207
+ reason: "changes_element_opacity"
16208
+ },
16209
+ "gradient-text": {
16210
+ classification: "unsupported-colour",
16211
+ reason: "changes_foreground"
16212
+ },
16213
+ "text-color-change": {
16214
+ classification: "unsupported-colour",
16215
+ reason: "changes_foreground"
16216
+ },
16217
+ "border-color-change": {
16218
+ classification: "unsupported-colour",
16219
+ reason: "changes_border"
16220
+ },
16221
+ "aurora-glow": {
16222
+ classification: "unsupported-background-overlay",
16223
+ reason: "unresolved_background_overlay"
16224
+ },
16225
+ "cosmic-stardust": {
16226
+ classification: "unsupported-background-overlay",
16227
+ reason: "unresolved_background_overlay"
16228
+ },
16229
+ "gradient-border-fill": {
16230
+ classification: "unsupported-background-overlay",
16231
+ reason: "unresolved_background_overlay"
16232
+ },
16233
+ "animated-gradient-border-fill": {
16234
+ classification: "unsupported-background-overlay",
16235
+ reason: "unresolved_background_overlay"
16236
+ },
16237
+ "nav-underline": {
16238
+ classification: "not-button",
16239
+ reason: "not_button_effect"
16240
+ },
16241
+ "nav-frosted-base": {
16242
+ classification: "not-button",
16243
+ reason: "not_button_effect"
16244
+ },
16245
+ "nav-frosted-hover": {
16246
+ classification: "not-button",
16247
+ reason: "not_button_effect"
16248
+ },
16249
+ "nav-frosted-active": {
16250
+ classification: "not-button",
16251
+ reason: "not_button_effect"
16252
+ },
16253
+ "animated-gradient": { classification: "non-colour" },
16254
+ "border-glow": { classification: "non-colour" },
16255
+ "drop-shadow": { classification: "non-colour" },
16256
+ "inner-shadow": { classification: "non-colour" },
16257
+ "neumorphic-shadow": { classification: "non-colour" },
16258
+ "outer-glow": { classification: "non-colour" },
16259
+ "retro-3d-shadow": { classification: "non-colour" },
16260
+ "scale-up": { classification: "non-colour" },
16261
+ "scale-down": { classification: "non-colour" },
16262
+ lift: { classification: "non-colour" },
16263
+ "press-down": { classification: "non-colour" },
16264
+ pop: { classification: "non-colour" },
16265
+ "border-bottom-grow": { classification: "non-colour" },
16266
+ "sketchy-border": { classification: "non-colour" },
16267
+ "pulse-animation": { classification: "non-colour" }
16268
+ };
16269
+ function getButtonEffectPolicy(effectId) {
16270
+ return Object.prototype.hasOwnProperty.call(BUTTON_EFFECT_POLICIES, effectId) ? BUTTON_EFFECT_POLICIES[effectId] : null;
16271
+ }
16272
+ function classifyButtonEffectId(effectId) {
16273
+ return getButtonEffectPolicy(effectId)?.classification ?? "unknown";
16274
+ }
16275
+ //#endregion
16086
16276
  //#region ../theme-core/src/buttons/types.ts
16087
16277
  /**
16088
16278
  * Standard semantic variant roles a theme exposes.
@@ -16112,6 +16302,20 @@ function asSpecialVariantId(value) {
16112
16302
  * Shared between generator and renderer so neither side hard-codes the string.
16113
16303
  */
16114
16304
  const DEFAULT_VARIANT_ALIAS_ID = "default";
16305
+ function parseOpaqueButtonColourToken(value) {
16306
+ const token = parseThemeColorToken(value);
16307
+ if (token === null) return null;
16308
+ const parts = parseColorTokenParts(token);
16309
+ return parts.kind === "token" && parts.opacityPercent === null ? token : null;
16310
+ }
16311
+ function unsafeAsOpaqueButtonColourToken(value) {
16312
+ return value;
16313
+ }
16314
+ const opaqueButtonColourTokenSchema = themeColorTokenSchema.refine((value) => parseOpaqueButtonColourToken(value) !== null, "Button colour tokens must be opaque").transform((value) => {
16315
+ const token = parseOpaqueButtonColourToken(value);
16316
+ if (token === null) throw new Error("Invariant: opaque button token passed its schema");
16317
+ return token;
16318
+ });
16115
16319
  const cornerStyleSchema = z$1.enum([
16116
16320
  "square",
16117
16321
  "rounded",
@@ -16213,7 +16417,7 @@ const buttonSizesSchema = z$1.object({
16213
16417
  lg,
16214
16418
  xl: xl ?? lg
16215
16419
  }));
16216
- const buttonGlobalSettingsSchema = z$1.object({
16420
+ const canonicalButtonGlobalSettingsSchema = z$1.object({
16217
16421
  cornerStyle: cornerStyleSchema,
16218
16422
  cornerRadius: z$1.number().min(0).max(24).optional(),
16219
16423
  /**
@@ -16244,11 +16448,12 @@ const buttonGlobalSettingsSchema = z$1.object({
16244
16448
  paddingPreset: buttonPaddingPresetSchema.optional(),
16245
16449
  hoverTransform: hoverTransformSchema,
16246
16450
  hoverColor: hoverColorSchema,
16247
- hoverColorToken: themeColorTokenSchema.optional(),
16451
+ hoverColorToken: opaqueButtonColourTokenSchema.optional(),
16248
16452
  gradientStyle: gradientStyleSchema.optional(),
16249
16453
  gradientAngle: z$1.number().min(0).max(360).optional(),
16250
16454
  gradientSharpness: gradientSharpnessSchema.optional()
16251
16455
  });
16456
+ const buttonGlobalSettingsSchema = z$1.preprocess(normalizeLegacyButtonGlobalInput, canonicalButtonGlobalSettingsSchema);
16252
16457
  const gradientDirectionSchema$1 = z$1.enum([
16253
16458
  "to-r",
16254
16459
  "to-l",
@@ -16262,27 +16467,33 @@ const gradientDirectionSchema$1 = z$1.enum([
16262
16467
  const buttonBackgroundSchema = z$1.discriminatedUnion("type", [
16263
16468
  z$1.object({
16264
16469
  type: z$1.literal("solid"),
16265
- colorToken: themeColorTokenSchema
16470
+ colorToken: opaqueButtonColourTokenSchema
16266
16471
  }),
16267
16472
  z$1.object({
16268
16473
  type: z$1.literal("gradient"),
16269
- stops: z$1.array(themeColorTokenSchema).min(2),
16270
- direction: gradientDirectionSchema$1
16474
+ stops: z$1.array(opaqueButtonColourTokenSchema).min(2).transform((stops) => {
16475
+ const [first, second, ...rest] = stops;
16476
+ if (first === void 0 || second === void 0) throw new Error("Invariant: gradient stops passed the minimum length schema");
16477
+ return [
16478
+ first,
16479
+ second,
16480
+ ...rest
16481
+ ];
16482
+ }),
16483
+ direction: gradientDirectionSchema$1,
16484
+ foregroundToken: opaqueButtonColourTokenSchema
16271
16485
  }),
16272
- z$1.object({ type: z$1.literal("transparent") })
16486
+ z$1.object({
16487
+ type: z$1.literal("surface"),
16488
+ role: z$1.literal("link")
16489
+ })
16273
16490
  ]);
16274
16491
  const effectApplicationSchema$1 = z$1.object({
16275
16492
  effectId: z$1.string(),
16276
- options: z$1.record(z$1.string(), z$1.any()).optional()
16277
- });
16278
- const buttonBorderSchema = z$1.object({
16279
- widthClass: z$1.string(),
16280
- colorToken: themeColorTokenSchema
16281
- });
16282
- const buttonVariantAffordanceSchema = z$1.object({
16283
- iconId: z$1.string().min(1),
16284
- colorToken: themeColorTokenSchema.optional()
16493
+ options: z$1.record(z$1.string(), z$1.json()).optional()
16285
16494
  });
16495
+ const buttonBorderSchema = z$1.object({ widthClass: z$1.string() });
16496
+ const buttonVariantAffordanceSchema = z$1.object({ iconId: z$1.string().min(1) });
16286
16497
  const variantShadowSchema = z$1.object({
16287
16498
  elevation: z$1.enum([
16288
16499
  "none",
@@ -16323,27 +16534,170 @@ const variantSizeOverridesSchema = z$1.object({
16323
16534
  * default (e.g. link-style variants declaring zero horizontal padding at
16324
16535
  * every size).
16325
16536
  */
16326
- const buttonVariantSchema = z$1.object({
16537
+ const canonicalButtonVariantSchema = z$1.object({
16327
16538
  id: z$1.string(),
16328
16539
  name: z$1.string().min(1).max(20),
16329
16540
  enabled: z$1.boolean(),
16330
16541
  priority: prioritySchema,
16331
16542
  background: buttonBackgroundSchema,
16332
- textColorToken: themeColorTokenSchema,
16333
16543
  /** Tailwind radius token (e.g. `'rounded-md'`). Omit to inherit from `global.cornerStyle`. */
16334
16544
  borderRadius: z$1.string().optional(),
16335
16545
  shadow: variantShadowSchema.optional(),
16336
16546
  border: buttonBorderSchema.optional(),
16337
- hoverBackgroundToken: z$1.string().optional(),
16547
+ hoverBackgroundToken: opaqueButtonColourTokenSchema.optional(),
16338
16548
  effects: variantEffectsSchema.optional(),
16339
16549
  sizes: variantSizeOverridesSchema.optional(),
16340
16550
  affordance: buttonVariantAffordanceSchema.optional()
16341
16551
  });
16552
+ /**
16553
+ * Temporary persisted-theme admission boundary for issue #1206.
16554
+ *
16555
+ * Production inventory on 2026-07-22 found 22 rows using the old independent
16556
+ * `textColorToken`/border colour shape. New source and all downstream code use
16557
+ * only the canonical relationship. Delete this preprocessor after the reviewed
16558
+ * production rewrite reports zero legacy rows.
16559
+ */
16560
+ const buttonVariantSchema = z$1.preprocess(normalizeLegacyButtonVariantInput, canonicalButtonVariantSchema);
16342
16561
  const buttonSystemSchema = z$1.object({
16343
16562
  global: buttonGlobalSettingsSchema,
16344
16563
  variants: z$1.array(buttonVariantSchema).min(1).max(6),
16345
16564
  sizes: buttonSizesSchema.optional()
16346
16565
  });
16566
+ function normalizeLegacyButtonVariantInput(input) {
16567
+ if (!isUnknownRecord(input)) return input;
16568
+ const background = input.background;
16569
+ if (!isUnknownRecord(background)) return input;
16570
+ 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;
16571
+ const { textColorToken, ...withoutLegacyText } = input;
16572
+ const normalizedBorder = isUnknownRecord(input.border) && typeof input.border.widthClass === "string" ? { widthClass: input.border.widthClass } : input.border;
16573
+ const normalizedAffordance = isUnknownRecord(input.affordance) && typeof input.affordance.iconId === "string" ? { iconId: input.affordance.iconId } : input.affordance;
16574
+ const normalizedEffects = normalizeLegacyButtonEffects(input.effects, background.type);
16575
+ const hoverBackgroundToken = stripLegacyTokenAlpha(input.hoverBackgroundToken);
16576
+ if (background.type === "transparent") return {
16577
+ ...withoutLegacyText,
16578
+ background: {
16579
+ type: "surface",
16580
+ role: "link"
16581
+ },
16582
+ border: normalizedBorder,
16583
+ affordance: normalizedAffordance,
16584
+ effects: normalizedEffects,
16585
+ hoverBackgroundToken
16586
+ };
16587
+ if (background.type === "gradient") {
16588
+ const stops = Array.isArray(background.stops) ? background.stops.map(stripLegacyTokenAlpha) : background.stops;
16589
+ return {
16590
+ ...withoutLegacyText,
16591
+ background: {
16592
+ ...background,
16593
+ stops,
16594
+ foregroundToken: stripLegacyTokenAlpha(background.foregroundToken ?? textColorToken)
16595
+ },
16596
+ border: normalizedBorder,
16597
+ affordance: normalizedAffordance,
16598
+ effects: normalizedEffects,
16599
+ hoverBackgroundToken
16600
+ };
16601
+ }
16602
+ return {
16603
+ ...withoutLegacyText,
16604
+ background: background.type === "solid" ? {
16605
+ ...background,
16606
+ colorToken: stripLegacyTokenAlpha(background.colorToken)
16607
+ } : background,
16608
+ border: normalizedBorder,
16609
+ affordance: normalizedAffordance,
16610
+ effects: normalizedEffects,
16611
+ hoverBackgroundToken
16612
+ };
16613
+ }
16614
+ function normalizeLegacyButtonGlobalInput(input) {
16615
+ if (!isUnknownRecord(input)) return input;
16616
+ const hoverColorToken = stripLegacyTokenAlpha(input.hoverColorToken);
16617
+ return hoverColorToken === input.hoverColorToken ? input : {
16618
+ ...input,
16619
+ hoverColorToken
16620
+ };
16621
+ }
16622
+ function hasLegacyAlphaToken(input, background) {
16623
+ const stops = isUnknownArray$2(background.stops) ? background.stops : [];
16624
+ return [
16625
+ input.hoverBackgroundToken,
16626
+ background.colorToken,
16627
+ background.foregroundToken,
16628
+ ...stops
16629
+ ].some((token) => stripLegacyTokenAlpha(token) !== token);
16630
+ }
16631
+ function stripLegacyTokenAlpha(value) {
16632
+ if (typeof value !== "string") return value;
16633
+ const slashIndex = value.indexOf("/");
16634
+ return slashIndex < 0 ? value : value.slice(0, slashIndex);
16635
+ }
16636
+ function hasLegacyButtonEffects(input) {
16637
+ if (!isUnknownRecord(input)) return false;
16638
+ return [
16639
+ "base",
16640
+ "hover",
16641
+ "active",
16642
+ "focus"
16643
+ ].some((state) => {
16644
+ const effects = input[state];
16645
+ return Array.isArray(effects) && effects.some((effect) => {
16646
+ if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return true;
16647
+ const classification = classifyButtonEffectId(effect.effectId);
16648
+ if (classification !== "colour-transition" && classification !== "non-colour") return true;
16649
+ return effect.effectId === "background-on-hover" && isUnknownRecord(effect.options) && stripLegacyTokenAlpha(effect.options.colorToken) !== effect.options.colorToken;
16650
+ });
16651
+ });
16652
+ }
16653
+ function normalizeLegacyButtonEffects(input, backgroundType) {
16654
+ if (!isUnknownRecord(input)) return input;
16655
+ const normalized = {};
16656
+ for (const state of [
16657
+ "base",
16658
+ "hover",
16659
+ "active",
16660
+ "focus"
16661
+ ]) {
16662
+ const effects = input[state];
16663
+ if (!Array.isArray(effects)) continue;
16664
+ const safeEffects = effects.flatMap((effect) => {
16665
+ if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return [];
16666
+ const classification = classifyButtonEffectId(effect.effectId);
16667
+ if (classification === "non-colour") return [effect];
16668
+ if (classification !== "colour-transition") return [];
16669
+ if (effect.effectId === "background-on-hover") {
16670
+ const options = isUnknownRecord(effect.options) ? effect.options : null;
16671
+ const colorToken = options === null ? null : parseOpaqueButtonColourToken(stripLegacyTokenAlpha(options.colorToken));
16672
+ return colorToken === null ? [] : [{
16673
+ ...effect,
16674
+ options: {
16675
+ ...options,
16676
+ colorToken
16677
+ }
16678
+ }];
16679
+ }
16680
+ if (backgroundType !== "solid") return [];
16681
+ const rawAmount = isUnknownRecord(effect.options) ? effect.options.amount : void 0;
16682
+ const amount = typeof rawAmount === "number" && rawAmount > 0 ? Math.max(100, Math.round(rawAmount / 100) * 100) : 100;
16683
+ return [{
16684
+ ...effect,
16685
+ options: {
16686
+ ...isUnknownRecord(effect.options) ? effect.options : {},
16687
+ amount
16688
+ }
16689
+ }];
16690
+ });
16691
+ if (safeEffects.length > 0) normalized[state] = safeEffects;
16692
+ }
16693
+ return Object.keys(normalized).length === 0 ? void 0 : normalized;
16694
+ }
16695
+ function isUnknownRecord(value) {
16696
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16697
+ }
16698
+ function isUnknownArray$2(value) {
16699
+ return Array.isArray(value);
16700
+ }
16347
16701
  //#endregion
16348
16702
  //#region ../theme-core/src/buttons/personalities/types.ts
16349
16703
  function asButtonPersonalityId(value) {
@@ -16369,7 +16723,7 @@ function defineButtonPersonality(input) {
16369
16723
  name: input.name,
16370
16724
  description: input.description,
16371
16725
  tags: input.tags,
16372
- buttonSystem: input.buttonSystem
16726
+ buttonSystem: buttonSystemSchema.parse(input.buttonSystem)
16373
16727
  };
16374
16728
  }
16375
16729
  //#endregion
@@ -16422,19 +16776,15 @@ const brushedWash = defineButtonPersonality({
16422
16776
  background: {
16423
16777
  type: "gradient",
16424
16778
  stops: ["primary", "accent"],
16425
- direction: "to-r"
16779
+ direction: "to-r",
16780
+ foregroundToken: "primaryForeground"
16426
16781
  },
16427
- textColorToken: "primaryForeground",
16428
16782
  borderRadius: "rounded-lg",
16429
16783
  shadow: {
16430
16784
  elevation: "low",
16431
16785
  softness: "soft",
16432
16786
  position: "bottom"
16433
- },
16434
- effects: { hover: [{
16435
- effectId: "opacity-reduce",
16436
- options: { reduction: -15 }
16437
- }] }
16787
+ }
16438
16788
  },
16439
16789
  {
16440
16790
  id: "secondary",
@@ -16444,32 +16794,27 @@ const brushedWash = defineButtonPersonality({
16444
16794
  background: {
16445
16795
  type: "gradient",
16446
16796
  stops: ["secondary", "accent"],
16447
- direction: "to-br"
16797
+ direction: "to-br",
16798
+ foregroundToken: "primaryForeground"
16448
16799
  },
16449
- textColorToken: "primaryForeground",
16450
16800
  borderRadius: "rounded-lg",
16451
16801
  shadow: {
16452
16802
  elevation: "low",
16453
16803
  softness: "soft",
16454
16804
  position: "bottom"
16455
- },
16456
- effects: { hover: [{
16457
- effectId: "opacity-reduce",
16458
- options: { reduction: -15 }
16459
- }] }
16805
+ }
16460
16806
  },
16461
16807
  {
16462
16808
  id: "outline",
16463
16809
  name: "Outline",
16464
16810
  enabled: true,
16465
16811
  priority: 3,
16466
- background: { type: "transparent" },
16467
- textColorToken: "primary",
16468
- borderRadius: "rounded-lg",
16469
- border: {
16470
- widthClass: "border",
16471
- colorToken: "primary"
16812
+ background: {
16813
+ type: "surface",
16814
+ role: "link"
16472
16815
  },
16816
+ borderRadius: "rounded-lg",
16817
+ border: { widthClass: "border" },
16473
16818
  effects: { hover: [{
16474
16819
  effectId: "background-on-hover",
16475
16820
  options: { colorToken: "surface" }
@@ -16480,8 +16825,10 @@ const brushedWash = defineButtonPersonality({
16480
16825
  name: "Ghost",
16481
16826
  enabled: true,
16482
16827
  priority: 3,
16483
- background: { type: "transparent" },
16484
- textColorToken: "text",
16828
+ background: {
16829
+ type: "surface",
16830
+ role: "link"
16831
+ },
16485
16832
  borderRadius: "rounded-lg",
16486
16833
  effects: { hover: [{
16487
16834
  effectId: "background-on-hover",
@@ -16527,7 +16874,6 @@ const confidentChip = defineButtonPersonality({
16527
16874
  type: "solid",
16528
16875
  colorToken: "primary"
16529
16876
  },
16530
- textColorToken: "primaryForeground",
16531
16877
  borderRadius: "rounded-md",
16532
16878
  shadow: {
16533
16879
  elevation: "medium",
@@ -16537,7 +16883,7 @@ const confidentChip = defineButtonPersonality({
16537
16883
  effects: {
16538
16884
  hover: [{
16539
16885
  effectId: "darken-background",
16540
- options: { amount: 60 }
16886
+ options: { amount: 100 }
16541
16887
  }],
16542
16888
  active: [{
16543
16889
  effectId: "scale-down",
@@ -16554,12 +16900,8 @@ const confidentChip = defineButtonPersonality({
16554
16900
  type: "solid",
16555
16901
  colorToken: "surface"
16556
16902
  },
16557
- textColorToken: "text",
16558
16903
  borderRadius: "rounded-md",
16559
- border: {
16560
- widthClass: "border",
16561
- colorToken: "border"
16562
- },
16904
+ border: { widthClass: "border" },
16563
16905
  effects: { hover: [{
16564
16906
  effectId: "background-on-hover",
16565
16907
  options: { colorToken: "background" }
@@ -16570,13 +16912,12 @@ const confidentChip = defineButtonPersonality({
16570
16912
  name: "Outline",
16571
16913
  enabled: true,
16572
16914
  priority: 3,
16573
- background: { type: "transparent" },
16574
- textColorToken: "primary",
16575
- borderRadius: "rounded-md",
16576
- border: {
16577
- widthClass: "border",
16578
- colorToken: "primary"
16915
+ background: {
16916
+ type: "surface",
16917
+ role: "link"
16579
16918
  },
16919
+ borderRadius: "rounded-md",
16920
+ border: { widthClass: "border" },
16580
16921
  effects: { hover: [{
16581
16922
  effectId: "background-on-hover",
16582
16923
  options: { colorToken: "surface" }
@@ -16587,8 +16928,10 @@ const confidentChip = defineButtonPersonality({
16587
16928
  name: "Ghost",
16588
16929
  enabled: true,
16589
16930
  priority: 3,
16590
- background: { type: "transparent" },
16591
- textColorToken: "primary",
16931
+ background: {
16932
+ type: "surface",
16933
+ role: "link"
16934
+ },
16592
16935
  borderRadius: "rounded-md",
16593
16936
  effects: { hover: [{
16594
16937
  effectId: "background-on-hover",
@@ -16651,7 +16994,6 @@ const earthPill = defineButtonPersonality({
16651
16994
  type: "solid",
16652
16995
  colorToken: "primary"
16653
16996
  },
16654
- textColorToken: "primaryForeground",
16655
16997
  borderRadius: "rounded-full",
16656
16998
  shadow: {
16657
16999
  elevation: "low",
@@ -16677,13 +17019,12 @@ const earthPill = defineButtonPersonality({
16677
17019
  name: "Secondary",
16678
17020
  enabled: true,
16679
17021
  priority: 2,
16680
- background: { type: "transparent" },
16681
- textColorToken: "primary",
16682
- borderRadius: "rounded-full",
16683
- border: {
16684
- widthClass: "border",
16685
- colorToken: "primary"
17022
+ background: {
17023
+ type: "surface",
17024
+ role: "link"
16686
17025
  },
17026
+ borderRadius: "rounded-full",
17027
+ border: { widthClass: "border" },
16687
17028
  effects: { hover: [{
16688
17029
  effectId: "background-on-hover",
16689
17030
  options: { colorToken: "muted" }
@@ -16694,13 +17035,12 @@ const earthPill = defineButtonPersonality({
16694
17035
  name: "Outline",
16695
17036
  enabled: true,
16696
17037
  priority: 3,
16697
- background: { type: "transparent" },
16698
- textColorToken: "text",
16699
- borderRadius: "rounded-full",
16700
- border: {
16701
- widthClass: "border",
16702
- colorToken: "border"
17038
+ background: {
17039
+ type: "surface",
17040
+ role: "link"
16703
17041
  },
17042
+ borderRadius: "rounded-full",
17043
+ border: { widthClass: "border" },
16704
17044
  effects: { hover: [{
16705
17045
  effectId: "background-on-hover",
16706
17046
  options: { colorToken: "surfaceAlt" }
@@ -16711,8 +17051,10 @@ const earthPill = defineButtonPersonality({
16711
17051
  name: "Ghost",
16712
17052
  enabled: true,
16713
17053
  priority: 3,
16714
- background: { type: "transparent" },
16715
- textColorToken: "primary",
17054
+ background: {
17055
+ type: "surface",
17056
+ role: "link"
17057
+ },
16716
17058
  borderRadius: "rounded-full",
16717
17059
  shadow: {
16718
17060
  elevation: "none",
@@ -16733,11 +17075,7 @@ const earthPill = defineButtonPersonality({
16733
17075
  padding: "0.625rem 0",
16734
17076
  fontSize: "text-xl"
16735
17077
  }
16736
- },
16737
- effects: { hover: [{
16738
- effectId: "opacity-reduce",
16739
- options: { reduction: 25 }
16740
- }] }
17078
+ }
16741
17079
  }
16742
17080
  ]
16743
17081
  }
@@ -16773,8 +17111,10 @@ const editorialLink = defineButtonPersonality({
16773
17111
  name: "Primary",
16774
17112
  enabled: true,
16775
17113
  priority: 1,
16776
- background: { type: "transparent" },
16777
- textColorToken: "text",
17114
+ background: {
17115
+ type: "surface",
17116
+ role: "link"
17117
+ },
16778
17118
  borderRadius: "rounded-none",
16779
17119
  sizes: {
16780
17120
  sm: { padding: "0.25rem 0" },
@@ -16796,8 +17136,10 @@ const editorialLink = defineButtonPersonality({
16796
17136
  name: "Secondary",
16797
17137
  enabled: true,
16798
17138
  priority: 2,
16799
- background: { type: "transparent" },
16800
- textColorToken: "mutedText",
17139
+ background: {
17140
+ type: "surface",
17141
+ role: "link"
17142
+ },
16801
17143
  borderRadius: "rounded-none",
16802
17144
  sizes: {
16803
17145
  sm: { padding: "0.25rem 0" },
@@ -16819,8 +17161,10 @@ const editorialLink = defineButtonPersonality({
16819
17161
  name: "Outline",
16820
17162
  enabled: true,
16821
17163
  priority: 3,
16822
- background: { type: "transparent" },
16823
- textColorToken: "accent",
17164
+ background: {
17165
+ type: "surface",
17166
+ role: "link"
17167
+ },
16824
17168
  borderRadius: "rounded-none",
16825
17169
  sizes: {
16826
17170
  sm: { padding: "0.25rem 0" },
@@ -16842,19 +17186,17 @@ const editorialLink = defineButtonPersonality({
16842
17186
  name: "Ghost",
16843
17187
  enabled: true,
16844
17188
  priority: 3,
16845
- background: { type: "transparent" },
16846
- textColorToken: "mutedText",
17189
+ background: {
17190
+ type: "surface",
17191
+ role: "link"
17192
+ },
16847
17193
  borderRadius: "rounded-none",
16848
17194
  sizes: {
16849
17195
  sm: { padding: "0.25rem 0" },
16850
17196
  md: { padding: "0.5rem 0" },
16851
17197
  lg: { padding: "0.625rem 0" },
16852
17198
  xl: { padding: "0.75rem 0" }
16853
- },
16854
- effects: { hover: [{
16855
- effectId: "opacity-reduce",
16856
- options: { reduction: -20 }
16857
- }] }
17199
+ }
16858
17200
  }
16859
17201
  ]
16860
17202
  }
@@ -16916,12 +17258,8 @@ const flatInk = defineButtonPersonality({
16916
17258
  type: "solid",
16917
17259
  colorToken: "primary"
16918
17260
  },
16919
- textColorToken: "primaryForeground",
16920
17261
  borderRadius: "rounded-lg",
16921
- affordance: {
16922
- iconId: "ArrowRight",
16923
- colorToken: "accent"
16924
- },
17262
+ affordance: { iconId: "ArrowRight" },
16925
17263
  effects: { hover: [{
16926
17264
  effectId: "lift",
16927
17265
  options: { translateY: "-2px" }
@@ -16935,13 +17273,12 @@ const flatInk = defineButtonPersonality({
16935
17273
  name: "Secondary",
16936
17274
  enabled: true,
16937
17275
  priority: 2,
16938
- background: { type: "transparent" },
16939
- textColorToken: "primary",
16940
- borderRadius: "rounded-lg",
16941
- border: {
16942
- widthClass: "border",
16943
- colorToken: "border"
17276
+ background: {
17277
+ type: "surface",
17278
+ role: "link"
16944
17279
  },
17280
+ borderRadius: "rounded-lg",
17281
+ border: { widthClass: "border" },
16945
17282
  effects: { hover: [{
16946
17283
  effectId: "lift",
16947
17284
  options: { translateY: "-2px" }
@@ -16959,12 +17296,8 @@ const flatInk = defineButtonPersonality({
16959
17296
  type: "solid",
16960
17297
  colorToken: "accent"
16961
17298
  },
16962
- textColorToken: "accentForeground",
16963
17299
  borderRadius: "rounded-lg",
16964
- affordance: {
16965
- iconId: "ArrowRight",
16966
- colorToken: "accentForeground"
16967
- },
17300
+ affordance: { iconId: "ArrowRight" },
16968
17301
  effects: { hover: [{
16969
17302
  effectId: "lift",
16970
17303
  options: { translateY: "-2px" }
@@ -17027,7 +17360,6 @@ const inkStamp = defineButtonPersonality({
17027
17360
  type: "solid",
17028
17361
  colorToken: "primary"
17029
17362
  },
17030
- textColorToken: "primaryForeground",
17031
17363
  borderRadius: "rounded-md",
17032
17364
  effects: {
17033
17365
  hover: [{
@@ -17048,13 +17380,12 @@ const inkStamp = defineButtonPersonality({
17048
17380
  name: "Secondary",
17049
17381
  enabled: true,
17050
17382
  priority: 2,
17051
- background: { type: "transparent" },
17052
- textColorToken: "primary",
17053
- borderRadius: "rounded-md",
17054
- border: {
17055
- widthClass: "border-2",
17056
- colorToken: "primary"
17383
+ background: {
17384
+ type: "surface",
17385
+ role: "link"
17057
17386
  },
17387
+ borderRadius: "rounded-md",
17388
+ border: { widthClass: "border-2" },
17058
17389
  effects: { hover: [{
17059
17390
  effectId: "background-on-hover",
17060
17391
  options: { colorToken: "surface" }
@@ -17065,13 +17396,12 @@ const inkStamp = defineButtonPersonality({
17065
17396
  name: "Outline",
17066
17397
  enabled: true,
17067
17398
  priority: 3,
17068
- background: { type: "transparent" },
17069
- textColorToken: "text",
17070
- borderRadius: "rounded-md",
17071
- border: {
17072
- widthClass: "border-2",
17073
- colorToken: "border"
17399
+ background: {
17400
+ type: "surface",
17401
+ role: "link"
17074
17402
  },
17403
+ borderRadius: "rounded-md",
17404
+ border: { widthClass: "border-2" },
17075
17405
  effects: { hover: [{
17076
17406
  effectId: "background-on-hover",
17077
17407
  options: { colorToken: "surface" }
@@ -17082,8 +17412,10 @@ const inkStamp = defineButtonPersonality({
17082
17412
  name: "Ghost",
17083
17413
  enabled: true,
17084
17414
  priority: 3,
17085
- background: { type: "transparent" },
17086
- textColorToken: "accent",
17415
+ background: {
17416
+ type: "surface",
17417
+ role: "link"
17418
+ },
17087
17419
  borderRadius: "rounded-none",
17088
17420
  sizes: {
17089
17421
  sm: {
@@ -17176,7 +17508,6 @@ const joyStamp = defineButtonPersonality({
17176
17508
  type: "solid",
17177
17509
  colorToken: "primary"
17178
17510
  },
17179
- textColorToken: "primaryForeground",
17180
17511
  borderRadius: "rounded-full",
17181
17512
  shadow: {
17182
17513
  elevation: "high",
@@ -17190,13 +17521,12 @@ const joyStamp = defineButtonPersonality({
17190
17521
  name: "Secondary",
17191
17522
  enabled: true,
17192
17523
  priority: 2,
17193
- background: { type: "transparent" },
17194
- textColorToken: "text",
17195
- borderRadius: "rounded-full",
17196
- border: {
17197
- widthClass: "border-2",
17198
- colorToken: "text"
17524
+ background: {
17525
+ type: "surface",
17526
+ role: "link"
17199
17527
  },
17528
+ borderRadius: "rounded-full",
17529
+ border: { widthClass: "border-2" },
17200
17530
  shadow: {
17201
17531
  elevation: "high",
17202
17532
  softness: "hard",
@@ -17261,12 +17591,8 @@ const ornateRitual = defineButtonPersonality({
17261
17591
  type: "solid",
17262
17592
  colorToken: "primary"
17263
17593
  },
17264
- textColorToken: "primaryForeground",
17265
17594
  borderRadius: "rounded-md",
17266
- border: {
17267
- widthClass: "border",
17268
- colorToken: "primary"
17269
- },
17595
+ border: { widthClass: "border" },
17270
17596
  effects: {
17271
17597
  hover: [{
17272
17598
  effectId: "background-on-hover",
@@ -17287,12 +17613,8 @@ const ornateRitual = defineButtonPersonality({
17287
17613
  type: "solid",
17288
17614
  colorToken: "secondary"
17289
17615
  },
17290
- textColorToken: "primaryForeground",
17291
17616
  borderRadius: "rounded-md",
17292
- border: {
17293
- widthClass: "border",
17294
- colorToken: "primary"
17295
- },
17617
+ border: { widthClass: "border" },
17296
17618
  effects: { hover: [{
17297
17619
  effectId: "background-on-hover",
17298
17620
  options: { colorToken: "surface" }
@@ -17303,13 +17625,12 @@ const ornateRitual = defineButtonPersonality({
17303
17625
  name: "Outline",
17304
17626
  enabled: true,
17305
17627
  priority: 3,
17306
- background: { type: "transparent" },
17307
- textColorToken: "primary",
17308
- borderRadius: "rounded-md",
17309
- border: {
17310
- widthClass: "border",
17311
- colorToken: "primary"
17628
+ background: {
17629
+ type: "surface",
17630
+ role: "link"
17312
17631
  },
17632
+ borderRadius: "rounded-md",
17633
+ border: { widthClass: "border" },
17313
17634
  effects: { hover: [{
17314
17635
  effectId: "background-on-hover",
17315
17636
  options: { colorToken: "muted" }
@@ -17320,8 +17641,10 @@ const ornateRitual = defineButtonPersonality({
17320
17641
  name: "Ghost",
17321
17642
  enabled: true,
17322
17643
  priority: 3,
17323
- background: { type: "transparent" },
17324
- textColorToken: "accent",
17644
+ background: {
17645
+ type: "surface",
17646
+ role: "link"
17647
+ },
17325
17648
  borderRadius: "rounded-none",
17326
17649
  sizes: {
17327
17650
  sm: {
@@ -17403,14 +17726,10 @@ const pebble = defineButtonPersonality({
17403
17726
  type: "solid",
17404
17727
  colorToken: "primary"
17405
17728
  },
17406
- textColorToken: "primaryForeground",
17407
17729
  borderRadius: "rounded-full",
17408
17730
  effects: { hover: [{
17409
- effectId: "background-on-hover-alpha",
17410
- options: {
17411
- colorToken: "primary",
17412
- opacity: 88
17413
- }
17731
+ effectId: "darken-background",
17732
+ options: { amount: 100 }
17414
17733
  }] }
17415
17734
  },
17416
17735
  {
@@ -17418,13 +17737,12 @@ const pebble = defineButtonPersonality({
17418
17737
  name: "Secondary",
17419
17738
  enabled: true,
17420
17739
  priority: 2,
17421
- background: { type: "transparent" },
17422
- textColorToken: "primary",
17423
- borderRadius: "rounded-full",
17424
- border: {
17425
- widthClass: "border-2",
17426
- colorToken: "accent"
17740
+ background: {
17741
+ type: "surface",
17742
+ role: "link"
17427
17743
  },
17744
+ borderRadius: "rounded-full",
17745
+ border: { widthClass: "border-2" },
17428
17746
  effects: { hover: [{
17429
17747
  effectId: "background-on-hover",
17430
17748
  options: { colorToken: "accent" }
@@ -17435,13 +17753,12 @@ const pebble = defineButtonPersonality({
17435
17753
  name: "Outline",
17436
17754
  enabled: true,
17437
17755
  priority: 3,
17438
- background: { type: "transparent" },
17439
- textColorToken: "text",
17440
- borderRadius: "rounded-full",
17441
- border: {
17442
- widthClass: "border",
17443
- colorToken: "border"
17756
+ background: {
17757
+ type: "surface",
17758
+ role: "link"
17444
17759
  },
17760
+ borderRadius: "rounded-full",
17761
+ border: { widthClass: "border" },
17445
17762
  effects: { hover: [{
17446
17763
  effectId: "background-on-hover",
17447
17764
  options: { colorToken: "surface" }
@@ -17452,8 +17769,10 @@ const pebble = defineButtonPersonality({
17452
17769
  name: "Ghost",
17453
17770
  enabled: true,
17454
17771
  priority: 3,
17455
- background: { type: "transparent" },
17456
- textColorToken: "primary",
17772
+ background: {
17773
+ type: "surface",
17774
+ role: "link"
17775
+ },
17457
17776
  borderRadius: "rounded-full",
17458
17777
  sizes: {
17459
17778
  sm: {
@@ -17469,11 +17788,7 @@ const pebble = defineButtonPersonality({
17469
17788
  padding: "0.75rem 1.5rem",
17470
17789
  fontSize: "text-xl"
17471
17790
  }
17472
- },
17473
- effects: { hover: [{
17474
- effectId: "opacity-reduce",
17475
- options: { reduction: 30 }
17476
- }] }
17791
+ }
17477
17792
  }
17478
17793
  ]
17479
17794
  }
@@ -17535,15 +17850,11 @@ const quietArrow = defineButtonPersonality({
17535
17850
  type: "solid",
17536
17851
  colorToken: "primary"
17537
17852
  },
17538
- textColorToken: "primaryForeground",
17539
17853
  borderRadius: "rounded-xl",
17540
17854
  affordance: { iconId: "ArrowRight" },
17541
17855
  effects: { hover: [{
17542
- effectId: "background-on-hover-alpha",
17543
- options: {
17544
- colorToken: "primary",
17545
- opacity: 90
17546
- }
17856
+ effectId: "darken-background",
17857
+ options: { amount: 100 }
17547
17858
  }] }
17548
17859
  },
17549
17860
  {
@@ -17551,13 +17862,12 @@ const quietArrow = defineButtonPersonality({
17551
17862
  name: "Secondary",
17552
17863
  enabled: true,
17553
17864
  priority: 2,
17554
- background: { type: "transparent" },
17555
- textColorToken: "primary",
17556
- borderRadius: "rounded-xl",
17557
- border: {
17558
- widthClass: "border",
17559
- colorToken: "primary"
17865
+ background: {
17866
+ type: "surface",
17867
+ role: "link"
17560
17868
  },
17869
+ borderRadius: "rounded-xl",
17870
+ border: { widthClass: "border" },
17561
17871
  affordance: { iconId: "ArrowRight" },
17562
17872
  effects: { hover: [{
17563
17873
  effectId: "background-on-hover",
@@ -17569,21 +17879,22 @@ const quietArrow = defineButtonPersonality({
17569
17879
  name: "Outline",
17570
17880
  enabled: true,
17571
17881
  priority: 3,
17572
- background: { type: "transparent" },
17573
- textColorToken: "primary",
17882
+ background: {
17883
+ type: "surface",
17884
+ role: "link"
17885
+ },
17574
17886
  borderRadius: "rounded-xl",
17575
- border: {
17576
- widthClass: "border",
17577
- colorToken: "primary"
17578
- }
17887
+ border: { widthClass: "border" }
17579
17888
  },
17580
17889
  {
17581
17890
  id: "ghost",
17582
17891
  name: "Ghost",
17583
17892
  enabled: true,
17584
17893
  priority: 3,
17585
- background: { type: "transparent" },
17586
- textColorToken: "primary",
17894
+ background: {
17895
+ type: "surface",
17896
+ role: "link"
17897
+ },
17587
17898
  borderRadius: "rounded-xl"
17588
17899
  }
17589
17900
  ]
@@ -17647,7 +17958,6 @@ const showtimePill = defineButtonPersonality({
17647
17958
  type: "solid",
17648
17959
  colorToken: "primary"
17649
17960
  },
17650
- textColorToken: "background",
17651
17961
  borderRadius: "rounded-full",
17652
17962
  effects: { hover: [{ effectId: "pop" }] }
17653
17963
  },
@@ -17656,13 +17966,12 @@ const showtimePill = defineButtonPersonality({
17656
17966
  name: "Secondary",
17657
17967
  enabled: true,
17658
17968
  priority: 2,
17659
- background: { type: "transparent" },
17660
- textColorToken: "primary",
17661
- borderRadius: "rounded-full",
17662
- border: {
17663
- widthClass: "border-2",
17664
- colorToken: "primary"
17969
+ background: {
17970
+ type: "surface",
17971
+ role: "link"
17665
17972
  },
17973
+ borderRadius: "rounded-full",
17974
+ border: { widthClass: "border-2" },
17666
17975
  effects: { hover: [{ effectId: "pop" }] }
17667
17976
  },
17668
17977
  {
@@ -17674,7 +17983,6 @@ const showtimePill = defineButtonPersonality({
17674
17983
  type: "solid",
17675
17984
  colorToken: "secondary"
17676
17985
  },
17677
- textColorToken: "primary",
17678
17986
  borderRadius: "rounded-full",
17679
17987
  effects: { hover: [{ effectId: "pop" }] }
17680
17988
  },
@@ -17687,12 +17995,8 @@ const showtimePill = defineButtonPersonality({
17687
17995
  type: "solid",
17688
17996
  colorToken: "background"
17689
17997
  },
17690
- textColorToken: "primary",
17691
17998
  borderRadius: "rounded-full",
17692
- border: {
17693
- widthClass: "border-2",
17694
- colorToken: "primary"
17695
- },
17999
+ border: { widthClass: "border-2" },
17696
18000
  effects: { hover: [{
17697
18001
  effectId: "background-on-hover",
17698
18002
  options: { colorToken: "surface" }
@@ -17703,13 +18007,12 @@ const showtimePill = defineButtonPersonality({
17703
18007
  name: "Outline",
17704
18008
  enabled: true,
17705
18009
  priority: 3,
17706
- background: { type: "transparent" },
17707
- textColorToken: "primary",
17708
- borderRadius: "rounded-full",
17709
- border: {
17710
- widthClass: "border-2",
17711
- colorToken: "primary"
18010
+ background: {
18011
+ type: "surface",
18012
+ role: "link"
17712
18013
  },
18014
+ borderRadius: "rounded-full",
18015
+ border: { widthClass: "border-2" },
17713
18016
  effects: { hover: [{
17714
18017
  effectId: "background-on-hover",
17715
18018
  options: { colorToken: "background" }
@@ -17720,8 +18023,10 @@ const showtimePill = defineButtonPersonality({
17720
18023
  name: "Ghost",
17721
18024
  enabled: true,
17722
18025
  priority: 3,
17723
- background: { type: "transparent" },
17724
- textColorToken: "primary",
18026
+ background: {
18027
+ type: "surface",
18028
+ role: "link"
18029
+ },
17725
18030
  borderRadius: "rounded-full",
17726
18031
  effects: { hover: [{
17727
18032
  effectId: "background-on-hover",
@@ -17767,7 +18072,6 @@ const softPill = defineButtonPersonality({
17767
18072
  type: "solid",
17768
18073
  colorToken: "primary"
17769
18074
  },
17770
- textColorToken: "primaryForeground",
17771
18075
  borderRadius: "rounded-full",
17772
18076
  shadow: {
17773
18077
  elevation: "low",
@@ -17790,13 +18094,12 @@ const softPill = defineButtonPersonality({
17790
18094
  name: "Secondary",
17791
18095
  enabled: true,
17792
18096
  priority: 2,
17793
- background: { type: "transparent" },
17794
- textColorToken: "primary",
17795
- borderRadius: "rounded-full",
17796
- border: {
17797
- widthClass: "border-2",
17798
- colorToken: "primary"
18097
+ background: {
18098
+ type: "surface",
18099
+ role: "link"
17799
18100
  },
18101
+ borderRadius: "rounded-full",
18102
+ border: { widthClass: "border-2" },
17800
18103
  effects: { hover: [{
17801
18104
  effectId: "background-on-hover",
17802
18105
  options: { colorToken: "surface" }
@@ -17807,13 +18110,12 @@ const softPill = defineButtonPersonality({
17807
18110
  name: "Outline",
17808
18111
  enabled: true,
17809
18112
  priority: 3,
17810
- background: { type: "transparent" },
17811
- textColorToken: "text",
17812
- borderRadius: "rounded-full",
17813
- border: {
17814
- widthClass: "border",
17815
- colorToken: "border"
18113
+ background: {
18114
+ type: "surface",
18115
+ role: "link"
17816
18116
  },
18117
+ borderRadius: "rounded-full",
18118
+ border: { widthClass: "border" },
17817
18119
  effects: { hover: [{
17818
18120
  effectId: "background-on-hover",
17819
18121
  options: { colorToken: "surface" }
@@ -17824,8 +18126,10 @@ const softPill = defineButtonPersonality({
17824
18126
  name: "Ghost",
17825
18127
  enabled: true,
17826
18128
  priority: 3,
17827
- background: { type: "transparent" },
17828
- textColorToken: "text",
18129
+ background: {
18130
+ type: "surface",
18131
+ role: "link"
18132
+ },
17829
18133
  borderRadius: "rounded-full",
17830
18134
  effects: { hover: [{
17831
18135
  effectId: "background-on-hover",
@@ -17844,9 +18148,8 @@ const solidSecondary = {
17844
18148
  priority: 2,
17845
18149
  background: {
17846
18150
  type: "solid",
17847
- colorToken: "secondary"
18151
+ colorToken: unsafeAsOpaqueButtonColourToken("secondary")
17848
18152
  },
17849
- textColorToken: "onSecondary",
17850
18153
  borderRadius: "rounded-full",
17851
18154
  shadow: {
17852
18155
  elevation: "none",
@@ -18279,6 +18582,282 @@ const serifDisplayQuote = { style: "serif-display" };
18279
18582
  /** Punchy quotation mark in the secondary accent colour. Bold / accent styles. */
18280
18583
  const accentGlyphQuote = { style: "accent-glyph" };
18281
18584
  //#endregion
18585
+ //#region ../theme-core/src/gradients/types.ts
18586
+ /**
18587
+ * Gradient configuration types and schemas
18588
+ */
18589
+ /**
18590
+ * Gradient color stop
18591
+ */
18592
+ const gradientStopSchema = z$1.object({
18593
+ color: themeColorTokenSchema,
18594
+ position: z$1.number().min(0).max(100)
18595
+ });
18596
+ /**
18597
+ * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
18598
+ * so existing radial gradients are byte-identical.
18599
+ *
18600
+ * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
18601
+ * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
18602
+ */
18603
+ const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
18604
+ /**
18605
+ * Gradient configuration.
18606
+ *
18607
+ * `type` is optional and defaults to `'linear'` so gradients persisted without a
18608
+ * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
18609
+ * `'conic'` are additive kinds:
18610
+ * - linear: `angle` (degrees)
18611
+ * - radial: optional `shape` (circle/ellipse) + `position`
18612
+ * - conic: optional `angle` (from-angle, degrees) + `position`
18613
+ */
18614
+ const gradientConfigSchema = z$1.object({
18615
+ type: z$1.enum([
18616
+ "linear",
18617
+ "radial",
18618
+ "conic"
18619
+ ]).default("linear"),
18620
+ angle: z$1.number().min(0).max(360).optional(),
18621
+ shape: radialGradientShapeSchema.optional(),
18622
+ position: z$1.string().optional(),
18623
+ stops: z$1.array(gradientStopSchema).min(2)
18624
+ });
18625
+ /**
18626
+ * Curated library of gradient presets.
18627
+ * All gradients use theme color tokens, so they adapt to the site's palette.
18628
+ */
18629
+ const GRADIENT_PRESETS = [
18630
+ {
18631
+ id: "primary-secondary",
18632
+ name: "Primary to Secondary",
18633
+ description: "Blend your primary and secondary brand colors",
18634
+ type: "linear",
18635
+ angle: 135,
18636
+ stops: [{
18637
+ color: "primary-500",
18638
+ position: 0
18639
+ }, {
18640
+ color: "secondary-500",
18641
+ position: 100
18642
+ }]
18643
+ },
18644
+ {
18645
+ id: "primary-accent",
18646
+ name: "Primary to Accent",
18647
+ description: "Energetic gradient from primary to accent",
18648
+ type: "linear",
18649
+ angle: 135,
18650
+ stops: [{
18651
+ color: "primary-500",
18652
+ position: 0
18653
+ }, {
18654
+ color: "accent-500",
18655
+ position: 100
18656
+ }]
18657
+ },
18658
+ {
18659
+ id: "secondary-accent",
18660
+ name: "Secondary to Accent",
18661
+ description: "Vibrant secondary to accent blend",
18662
+ type: "linear",
18663
+ angle: 135,
18664
+ stops: [{
18665
+ color: "secondary-500",
18666
+ position: 0
18667
+ }, {
18668
+ color: "accent-500",
18669
+ position: 100
18670
+ }]
18671
+ },
18672
+ {
18673
+ id: "primary-light-dark",
18674
+ name: "Primary Light to Dark",
18675
+ description: "Subtle primary color fade",
18676
+ type: "linear",
18677
+ angle: 180,
18678
+ stops: [{
18679
+ color: "primary-500",
18680
+ position: 0
18681
+ }, {
18682
+ color: "primary-900",
18683
+ position: 100
18684
+ }]
18685
+ },
18686
+ {
18687
+ id: "primary-dark-light",
18688
+ name: "Primary Dark to Light",
18689
+ description: "Reverse primary fade",
18690
+ type: "linear",
18691
+ angle: 180,
18692
+ stops: [{
18693
+ color: "primary-900",
18694
+ position: 0
18695
+ }, {
18696
+ color: "primary-50",
18697
+ position: 100
18698
+ }]
18699
+ },
18700
+ {
18701
+ id: "secondary-light-dark",
18702
+ name: "Secondary Light to Dark",
18703
+ description: "Subtle secondary color fade",
18704
+ type: "linear",
18705
+ angle: 180,
18706
+ stops: [{
18707
+ color: "secondary-500",
18708
+ position: 0
18709
+ }, {
18710
+ color: "secondary-900",
18711
+ position: 100
18712
+ }]
18713
+ },
18714
+ {
18715
+ id: "accent-light-dark",
18716
+ name: "Accent Light to Dark",
18717
+ description: "Accent color depth fade",
18718
+ type: "linear",
18719
+ angle: 180,
18720
+ stops: [{
18721
+ color: "accent-500",
18722
+ position: 0
18723
+ }, {
18724
+ color: "accent-900",
18725
+ position: 100
18726
+ }]
18727
+ },
18728
+ {
18729
+ id: "background-surface",
18730
+ name: "Background to Surface",
18731
+ description: "Subtle depth using base colors",
18732
+ type: "linear",
18733
+ angle: 180,
18734
+ stops: [{
18735
+ color: "background",
18736
+ position: 0
18737
+ }, {
18738
+ color: "surface",
18739
+ position: 100
18740
+ }]
18741
+ },
18742
+ {
18743
+ id: "neutral-fade",
18744
+ name: "Neutral Gradient",
18745
+ description: "Elegant neutral color fade",
18746
+ type: "linear",
18747
+ angle: 180,
18748
+ stops: [{
18749
+ color: "neutral-900",
18750
+ position: 0
18751
+ }, {
18752
+ color: "neutral-50",
18753
+ position: 100
18754
+ }]
18755
+ },
18756
+ {
18757
+ id: "primary-glow",
18758
+ name: "Primary glow",
18759
+ description: "A soft top-centred glow using your primary palette",
18760
+ type: "radial",
18761
+ shape: "ellipse",
18762
+ position: "top center",
18763
+ stops: [{
18764
+ color: "primary-900",
18765
+ position: 0
18766
+ }, {
18767
+ color: "primary-950",
18768
+ position: 100
18769
+ }],
18770
+ foreground: {
18771
+ textColor: "onPrimary900",
18772
+ headingColor: "onPrimary900",
18773
+ linkColor: "onPrimary900",
18774
+ supportingColor: "onPrimary900"
18775
+ }
18776
+ },
18777
+ {
18778
+ id: "accent-glow",
18779
+ name: "Accent glow",
18780
+ description: "A soft top-centred glow using your accent palette",
18781
+ type: "radial",
18782
+ shape: "ellipse",
18783
+ position: "top center",
18784
+ stops: [{
18785
+ color: "accent-900",
18786
+ position: 0
18787
+ }, {
18788
+ color: "accent-950",
18789
+ position: 100
18790
+ }],
18791
+ foreground: {
18792
+ textColor: "onAccent900",
18793
+ headingColor: "onAccent900",
18794
+ linkColor: "onAccent900",
18795
+ supportingColor: "onAccent900"
18796
+ }
18797
+ },
18798
+ {
18799
+ id: "primary-secondary-accent",
18800
+ name: "Brand Trio",
18801
+ description: "All three brand colors in harmony",
18802
+ type: "linear",
18803
+ angle: 135,
18804
+ stops: [
18805
+ {
18806
+ color: "primary-500",
18807
+ position: 0
18808
+ },
18809
+ {
18810
+ color: "secondary-500",
18811
+ position: 50
18812
+ },
18813
+ {
18814
+ color: "accent-500",
18815
+ position: 100
18816
+ }
18817
+ ]
18818
+ }
18819
+ ].map((preset) => ({
18820
+ ...preset,
18821
+ id: preset.id,
18822
+ stops: preset.stops
18823
+ }));
18824
+ const GRADIENT_PRESET_IDS = GRADIENT_PRESETS.map((preset) => preset.id);
18825
+ const gradientPresetIdSet = new Set(GRADIENT_PRESET_IDS);
18826
+ const gradientPresetById = Object.fromEntries(GRADIENT_PRESETS.map((preset) => [preset.id, preset]));
18827
+ function parseGradientPresetId(value) {
18828
+ if (typeof value !== "string") return null;
18829
+ const normalized = value.trim();
18830
+ if (!gradientPresetIdSet.has(normalized)) return null;
18831
+ return normalized;
18832
+ }
18833
+ function getGradientPreset(id) {
18834
+ return gradientPresetById[id];
18835
+ }
18836
+ //#endregion
18837
+ //#region ../theme-core/src/gradients/utils.ts
18838
+ /**
18839
+ * Convert a GradientConfig to a CSS gradient string
18840
+ *
18841
+ * @param config - The gradient configuration using theme tokens
18842
+ * @param tokens - Map of theme color tokens to hex values
18843
+ * @returns CSS gradient string (e.g., "linear-gradient(180deg, #3b82f6 0%, #f59e0b 100%)")
18844
+ */
18845
+ function gradientToCss(config, tokens) {
18846
+ const stops = config.stops.map((stop) => {
18847
+ return `${tokens[stop.color] ?? "#000000"} ${stop.position}%`;
18848
+ }).join(", ");
18849
+ const type = config.type ?? "linear";
18850
+ if (type === "radial") {
18851
+ const position = config.position ?? "center";
18852
+ return `radial-gradient(${config.shape ?? "circle"} at ${position}, ${stops})`;
18853
+ }
18854
+ if (type === "conic") {
18855
+ const position = config.position ?? "center";
18856
+ return `conic-gradient(from ${config.angle ?? 0}deg at ${position}, ${stops})`;
18857
+ }
18858
+ return `linear-gradient(${config.angle ?? 180}deg, ${stops})`;
18859
+ }
18860
+ //#endregion
18282
18861
  //#region ../theme-core/src/site-styles/logoBranding.ts
18283
18862
  const logoTextConfidenceValues = [
18284
18863
  "low",
@@ -20600,14 +21179,12 @@ const backgroundOnHoverEffect = {
20600
21179
  ]
20601
21180
  },
20602
21181
  generateCSS: (ctx) => {
20603
- const { themeId, variantId, options, tokens } = ctx;
21182
+ const { themeId, variantId, options, tokens, buttonConfig } = ctx;
20604
21183
  const colorToken = options.colorToken || "neutral-800";
20605
- const color = tokens.getColor(colorToken);
20606
- const textColorToken = typeof options.textColorToken === "string" ? options.textColorToken : null;
20607
21184
  return `
20608
21185
  /* Add background on hover */
20609
21186
  :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
20610
- background-color: ${color};${textColorToken ? `\n color: ${tokens.getColor(textColorToken)};` : ""}
21187
+ background-color: ${tokens.getColor(colorToken)};${"textColorToken" in buttonConfig && typeof options.textColorToken === "string" ? `\n color: ${tokens.getColor(options.textColorToken)};` : ""}
20611
21188
  }
20612
21189
  `;
20613
21190
  }
@@ -20631,10 +21208,10 @@ const backgroundOnHoverAlphaEffect = {
20631
21208
  default: 50
20632
21209
  },
20633
21210
  generateCSS: (ctx) => {
20634
- const { themeId, variantId, stateGroup, options, buttonConfig } = ctx;
21211
+ const { themeId, variantId, stateGroup, options } = ctx;
20635
21212
  const rawOpacity = options.opacity;
20636
21213
  const clampedOpacity = Math.max(0, Math.min(100, typeof rawOpacity === "number" && !Number.isNaN(rawOpacity) ? rawOpacity : 50));
20637
- const cssVar = `var(--tb-${options.colorToken || buttonConfig.textColorToken || "neutral-800"})`;
21214
+ const cssVar = `var(--tb-${options.colorToken || "neutral-800"})`;
20638
21215
  return `
20639
21216
  /* Add semi-transparent background (${stateGroup}) */
20640
21217
  :where([data-theme-scope="${themeId}"]) .${variantId}${stateGroup === "base" ? "" : `:${stateGroup}`} {
@@ -20847,7 +21424,7 @@ const outerGlowEffect = {
20847
21424
  const spread = options.spread || 10;
20848
21425
  let glowColor;
20849
21426
  if (options.glowColorToken && typeof options.glowColorToken === "string") glowColor = tokens.getColor(options.glowColorToken);
20850
- else if (buttonConfig.border?.colorToken) glowColor = tokens.getColor(buttonConfig.border.colorToken);
21427
+ else if (buttonConfig.border) glowColor = "colorToken" in buttonConfig.border ? tokens.getColor(buttonConfig.border.colorToken) : "currentColor";
20851
21428
  else if (buttonConfig.background.type === "solid") glowColor = tokens.getColor(buttonConfig.background.colorToken ?? "primary-400");
20852
21429
  else glowColor = tokens.getColor("primary-400");
20853
21430
  return `
@@ -20906,7 +21483,7 @@ const retro3DShadowEffect = {
20906
21483
  /**
20907
21484
  * Map Tailwind-style gradient direction tokens to CSS gradient directions
20908
21485
  */
20909
- const GRADIENT_DIRECTION_MAP$3 = {
21486
+ const GRADIENT_DIRECTION_MAP$2 = {
20910
21487
  "to-r": "to right",
20911
21488
  "to-l": "to left",
20912
21489
  "to-t": "to top",
@@ -21836,29 +22413,6 @@ function generateSingleEffectCSS(themeId, variantId, stateGroup, effectApp, elem
21836
22413
  };
21837
22414
  return effectPreset.generateCSS(ctx);
21838
22415
  }
21839
- /**
21840
- * Get contrasting text color for a button background
21841
- *
21842
- * This function returns theme-aware text colors using the neutral scale,
21843
- * which is guaranteed to exist in every theme and harmonizes with the design.
21844
- *
21845
- * Uses neutral-50 (lightest) for dark backgrounds and neutral-950 (darkest)
21846
- * for light backgrounds. This ensures excellent contrast while respecting
21847
- * the theme's neutral palette (warm/cool tinting).
21848
- *
21849
- * For solid backgrounds with primary/secondary/neutral colors:
21850
- * - Dark backgrounds (shade >= 500): neutral-50 (very light)
21851
- * - Light backgrounds (shade < 500): neutral-950 (very dark)
21852
- */
21853
- function getContrastingTextColorToken(backgroundToken, theme) {
21854
- const isLightTheme = theme.palette.meta.mode === "light";
21855
- if (backgroundToken.startsWith("primary-") || backgroundToken.startsWith("secondary-")) return "neutral-50";
21856
- if (backgroundToken.startsWith("neutral-")) {
21857
- const match = backgroundToken.match(/neutral-(\d+)/);
21858
- if (match?.[1]) return parseInt(match[1], 10) >= 500 ? "neutral-50" : "neutral-950";
21859
- }
21860
- return isLightTheme ? "neutral-50" : "neutral-950";
21861
- }
21862
22416
  //#endregion
21863
22417
  //#region ../theme-core/src/buttons/generateDefaultButtonSystem.ts
21864
22418
  /**
@@ -21916,13 +22470,8 @@ function generateDefaultButtonSystem(theme) {
21916
22470
  priority: 1,
21917
22471
  background: {
21918
22472
  type: "solid",
21919
- colorToken: "primary"
21920
- },
21921
- textColorToken: getContrastingTextColorToken("primary", theme),
21922
- effects: { hover: [{
21923
- effectId: "opacity-reduce",
21924
- options: { reduction: 15 }
21925
- }] }
22473
+ colorToken: unsafeAsOpaqueButtonColourToken("primary")
22474
+ }
21926
22475
  },
21927
22476
  {
21928
22477
  id: "secondary",
@@ -21931,51 +22480,39 @@ function generateDefaultButtonSystem(theme) {
21931
22480
  priority: 2,
21932
22481
  background: {
21933
22482
  type: "solid",
21934
- colorToken: "neutral-700"
21935
- },
21936
- textColorToken: getContrastingTextColorToken("neutral-700", theme),
21937
- effects: { hover: [{
21938
- effectId: "opacity-reduce",
21939
- options: { reduction: 15 }
21940
- }] }
22483
+ colorToken: unsafeAsOpaqueButtonColourToken("neutral-700")
22484
+ }
21941
22485
  },
21942
22486
  {
21943
22487
  id: "outline",
21944
22488
  name: "Outline",
21945
22489
  enabled: true,
21946
22490
  priority: 3,
21947
- background: { type: "transparent" },
21948
- textColorToken: "primary",
22491
+ background: {
22492
+ type: "surface",
22493
+ role: "link"
22494
+ },
21949
22495
  shadow: {
21950
22496
  elevation: "none",
21951
22497
  softness: null,
21952
22498
  position: "bottom"
21953
22499
  },
21954
- border: {
21955
- colorToken: "primary",
21956
- widthClass: borderWidthClass
21957
- },
21958
- effects: { hover: [{
21959
- effectId: "opacity-reduce",
21960
- options: { reduction: 15 }
21961
- }] }
22500
+ border: { widthClass: borderWidthClass }
21962
22501
  },
21963
22502
  {
21964
22503
  id: "ghost",
21965
22504
  name: "Ghost",
21966
22505
  enabled: true,
21967
22506
  priority: 3,
21968
- background: { type: "transparent" },
21969
- textColorToken: "primary",
22507
+ background: {
22508
+ type: "surface",
22509
+ role: "link"
22510
+ },
21970
22511
  shadow: {
21971
22512
  elevation: "none",
21972
22513
  softness: null,
21973
22514
  position: "bottom"
21974
- },
21975
- effects: { hover: [{
21976
- effectId: "opacity-reduce",
21977
- options: { reduction: 15 }
21978
- }] }
22515
+ }
21979
22516
  }
21980
22517
  ];
21981
22518
  return {
@@ -22031,6 +22568,472 @@ function affordanceIconMaskUrl(iconId) {
22031
22568
  return `data:image/svg+xml,${`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'>${paths.map((d) => `<path d='${d}'/>`).join("")}</svg>`.replace(/"/g, "'").replace(/#/g, "%23").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ")}`;
22032
22569
  }
22033
22570
  //#endregion
22571
+ //#region ../theme-core/src/surfaces/foregroundRoles.ts
22572
+ const SURFACE_FOREGROUND_ROLES = [
22573
+ "text",
22574
+ "heading",
22575
+ "link",
22576
+ "supporting"
22577
+ ];
22578
+ //#endregion
22579
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
22580
+ const SURFACE_FOREGROUND_CSS_VARS = {
22581
+ text: "--rb-surface-text-color",
22582
+ heading: "--rb-surface-heading-color",
22583
+ link: "--rb-surface-link-color",
22584
+ supporting: "--rb-surface-supporting-color"
22585
+ };
22586
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
22587
+ const DEFAULT_ROLE_FALLBACKS = {
22588
+ text: DEFAULT_SURFACE_TEXT_COLOR,
22589
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
22590
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
22591
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
22592
+ };
22593
+ function requireSurfaceColorValue(role, value) {
22594
+ const normalized = value.trim();
22595
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
22596
+ return normalized;
22597
+ }
22598
+ function completeSurfaceForegrounds(input) {
22599
+ const text = requireSurfaceColorValue("text", input.text);
22600
+ return {
22601
+ text,
22602
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
22603
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
22604
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
22605
+ };
22606
+ }
22607
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
22608
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
22609
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
22610
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
22611
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
22612
+ }
22613
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
22614
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
22615
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
22616
+ }
22617
+ function surfaceForegroundStyle(input) {
22618
+ const foregrounds = completeSurfaceForegrounds(input);
22619
+ return {
22620
+ color: foregrounds.text,
22621
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
22622
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
22623
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
22624
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
22625
+ };
22626
+ }
22627
+ function surfaceForegroundDeclarations(input) {
22628
+ const foregrounds = completeSurfaceForegrounds(input);
22629
+ return [`color: ${foregrounds.text}`, ...SURFACE_FOREGROUND_ROLES.map((role) => `${SURFACE_FOREGROUND_CSS_VARS[role]}: ${foregrounds[role]}`)];
22630
+ }
22631
+ //#endregion
22632
+ //#region ../theme-core/src/buttons/resolveButtonColourStates.ts
22633
+ const BUTTON_VISUAL_STATES = [
22634
+ "base",
22635
+ "hover",
22636
+ "focus",
22637
+ "active",
22638
+ "disabled"
22639
+ ];
22640
+ const BUTTON_CONTRAST_POLICY = {
22641
+ textMinimumRatio: 4.5,
22642
+ nonTextMinimumRatio: 3,
22643
+ gradientSamplesPerSegment: 20,
22644
+ alphaBackgrounds: "reject-without-known-surface",
22645
+ disabled: "retain-full-colour-contract"
22646
+ };
22647
+ function compileButtonColourStates(input) {
22648
+ const baseTransition = applyColourEffects({
22649
+ variantId: input.variant.id,
22650
+ state: "base",
22651
+ treatment: input.variant.background,
22652
+ effects: input.variant.effects?.base,
22653
+ palette: input.palette
22654
+ });
22655
+ if (!baseTransition.ok) return baseTransition;
22656
+ const stateTreatments = {
22657
+ base: baseTransition.treatment,
22658
+ hover: hoverTreatment(input.variant, input.global, baseTransition.treatment),
22659
+ focus: baseTransition.treatment,
22660
+ active: baseTransition.treatment,
22661
+ disabled: baseTransition.treatment
22662
+ };
22663
+ const nonColourEffects = { ...baseTransition.nonColourEffects.length > 0 ? { base: [...baseTransition.nonColourEffects] } : {} };
22664
+ for (const state of [
22665
+ "hover",
22666
+ "focus",
22667
+ "active"
22668
+ ]) {
22669
+ const transition = applyColourEffects({
22670
+ variantId: input.variant.id,
22671
+ state,
22672
+ treatment: stateTreatments[state],
22673
+ effects: input.variant.effects?.[state],
22674
+ palette: input.palette
22675
+ });
22676
+ if (!transition.ok) return transition;
22677
+ stateTreatments[state] = transition.treatment;
22678
+ if (transition.nonColourEffects.length > 0) nonColourEffects[state] = [...transition.nonColourEffects];
22679
+ }
22680
+ const resolved = {};
22681
+ for (const state of BUTTON_VISUAL_STATES) {
22682
+ const result = resolveTreatment$1({
22683
+ variantId: input.variant.id,
22684
+ state,
22685
+ treatment: stateTreatments[state],
22686
+ palette: input.palette
22687
+ });
22688
+ if (!result.ok) return result;
22689
+ resolved[state] = result.colour;
22690
+ }
22691
+ return {
22692
+ ok: true,
22693
+ states: resolved,
22694
+ focusIndicator: resolveFocusIndicator(resolved.focus),
22695
+ nonColourEffects
22696
+ };
22697
+ }
22698
+ function formatButtonColourContractError(error) {
22699
+ switch (error.tag) {
22700
+ case "unknown_palette_token": return `Button "${error.variantId}" ${error.state} state uses unknown palette token "${error.token}".`;
22701
+ case "missing_on_colour": return `Button "${error.variantId}" ${error.state} state cannot derive ${error.foregroundToken} from ${error.backgroundToken}.`;
22702
+ case "insufficient_contrast": return `Button "${error.variantId}" ${error.state} state has ${error.ratio.toFixed(2)}:1 contrast (${error.requiredRatio}:1 required).`;
22703
+ case "unsupported_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" is not contrast-safe (${error.reason}).`;
22704
+ case "invalid_colour_effect_options": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" has an invalid ${error.option} option.`;
22705
+ case "inapplicable_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" cannot apply to a ${error.treatment} treatment.`;
22706
+ case "unknown_button_effect": return `Button "${error.variantId}" ${error.state} uses unknown effect "${error.effectId}".`;
22707
+ }
22708
+ }
22709
+ function hoverTreatment(variant, global, fallback) {
22710
+ const token = variant.hoverBackgroundToken ?? (global.hoverColor === "token" ? global.hoverColorToken : void 0);
22711
+ return token === void 0 ? fallback : {
22712
+ type: "solid",
22713
+ colorToken: token
22714
+ };
22715
+ }
22716
+ function applyColourEffects(input) {
22717
+ let treatment = input.treatment;
22718
+ const nonColourEffects = [];
22719
+ for (const effect of input.effects ?? []) {
22720
+ const policy = getButtonEffectPolicy(effect.effectId);
22721
+ if (policy === null) return unknownEffect(input, effect);
22722
+ switch (policy.classification) {
22723
+ case "non-colour":
22724
+ nonColourEffects.push(effect);
22725
+ break;
22726
+ case "unsupported-colour":
22727
+ case "unsupported-background-overlay":
22728
+ case "not-button": return unsupportedEffect(input, effect, policy.reason);
22729
+ case "colour-transition":
22730
+ if (policy.transition.kind === "adjust-shade") {
22731
+ if (treatment.type !== "solid") return inapplicableEffect(input, effect, treatment);
22732
+ const amount = readShadeAmount(effect);
22733
+ if (amount === null) return invalidEffectOption(input, effect, "amount");
22734
+ const signedAmount = policy.transition.direction === "darker" ? amount : -amount;
22735
+ treatment = {
22736
+ type: "solid",
22737
+ colorToken: adjustShade(treatment.colorToken, signedAmount, input.palette)
22738
+ };
22739
+ break;
22740
+ }
22741
+ if (policy.transition.kind === "replace-background") {
22742
+ const colorToken = readOpaqueToken(effect, "colorToken", "neutral-800");
22743
+ if (colorToken === null) return invalidEffectOption(input, effect, "colorToken");
22744
+ treatment = {
22745
+ type: "solid",
22746
+ colorToken
22747
+ };
22748
+ break;
22749
+ }
22750
+ return unknownEffect(input, effect);
22751
+ }
22752
+ }
22753
+ return {
22754
+ ok: true,
22755
+ treatment,
22756
+ nonColourEffects
22757
+ };
22758
+ }
22759
+ function resolveTreatment$1(input) {
22760
+ const treatment = input.treatment;
22761
+ switch (treatment.type) {
22762
+ case "surface": return {
22763
+ ok: true,
22764
+ colour: {
22765
+ kind: "surface",
22766
+ role: treatment.role,
22767
+ backgroundCss: "transparent",
22768
+ foregroundCss: surfaceForegroundValue(treatment.role)
22769
+ }
22770
+ };
22771
+ case "solid": return resolveSolid(input, treatment.colorToken);
22772
+ case "gradient": return resolveGradient$2(input, treatment);
22773
+ }
22774
+ }
22775
+ function resolveFocusIndicator(colour) {
22776
+ switch (colour.kind) {
22777
+ case "solid": return {
22778
+ kind: "two-tone",
22779
+ innerCss: colour.foregroundCss,
22780
+ outerCss: colour.backgroundCss,
22781
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
22782
+ };
22783
+ case "gradient": return {
22784
+ kind: "two-tone",
22785
+ innerCss: colour.foregroundCss,
22786
+ outerCss: tokenCss(colour.backgroundTokens[0]),
22787
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
22788
+ };
22789
+ case "surface": return {
22790
+ kind: "surface-role",
22791
+ colorCss: colour.foregroundCss,
22792
+ minimumSurfaceContrast: BUTTON_CONTRAST_POLICY.nonTextMinimumRatio
22793
+ };
22794
+ }
22795
+ }
22796
+ function resolveSolid(input, backgroundToken) {
22797
+ const backgroundHex = paletteHex(input, backgroundToken);
22798
+ if (!backgroundHex.ok) return backgroundHex;
22799
+ const foregroundToken = unsafeAsOpaqueButtonColourToken(onColorTokenName(backgroundToken));
22800
+ const foregroundHex = input.palette[foregroundToken];
22801
+ if (typeof foregroundHex !== "string") return {
22802
+ ok: false,
22803
+ error: {
22804
+ tag: "missing_on_colour",
22805
+ variantId: input.variantId,
22806
+ state: input.state,
22807
+ backgroundToken,
22808
+ foregroundToken
22809
+ }
22810
+ };
22811
+ const contrast = validateContrast({
22812
+ ...input,
22813
+ background: backgroundToken,
22814
+ backgroundHex: backgroundHex.hex,
22815
+ foreground: foregroundToken,
22816
+ foregroundHex
22817
+ });
22818
+ if (!contrast.ok) return contrast;
22819
+ return {
22820
+ ok: true,
22821
+ colour: {
22822
+ kind: "solid",
22823
+ backgroundToken,
22824
+ foregroundToken,
22825
+ backgroundCss: tokenCss(backgroundToken),
22826
+ foregroundCss: tokenCss(foregroundToken)
22827
+ }
22828
+ };
22829
+ }
22830
+ function resolveGradient$2(input, treatment) {
22831
+ const foregroundHex = paletteHex(input, treatment.foregroundToken);
22832
+ if (!foregroundHex.ok) return foregroundHex;
22833
+ const stopHexes = [];
22834
+ for (const token of treatment.stops) {
22835
+ const stop = paletteHex(input, token);
22836
+ if (!stop.ok) return stop;
22837
+ stopHexes.push(stop.hex);
22838
+ }
22839
+ for (let segment = 0; segment < stopHexes.length - 1; segment += 1) {
22840
+ const start = stopHexes[segment];
22841
+ const end = stopHexes[segment + 1];
22842
+ const startToken = treatment.stops[segment];
22843
+ const endToken = treatment.stops[segment + 1];
22844
+ if (start === void 0 || end === void 0 || startToken === void 0 || endToken === void 0) continue;
22845
+ for (let sample = 0; sample <= BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment; sample += 1) {
22846
+ const position = sample / BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment;
22847
+ const sampleHex = interpolateHex(start, end, position);
22848
+ const contrast = validateContrast({
22849
+ ...input,
22850
+ background: `${startToken}→${endToken}@${position.toFixed(2)}`,
22851
+ backgroundHex: sampleHex,
22852
+ foreground: treatment.foregroundToken,
22853
+ foregroundHex: foregroundHex.hex
22854
+ });
22855
+ if (!contrast.ok) return contrast;
22856
+ }
22857
+ }
22858
+ const direction = GRADIENT_DIRECTION_MAP$2[treatment.direction] ?? "to right";
22859
+ return {
22860
+ ok: true,
22861
+ colour: {
22862
+ kind: "gradient",
22863
+ backgroundTokens: treatment.stops,
22864
+ foregroundToken: treatment.foregroundToken,
22865
+ backgroundCss: `linear-gradient(${direction}, ${treatment.stops.map(tokenCss).join(", ")})`,
22866
+ foregroundCss: tokenCss(treatment.foregroundToken)
22867
+ }
22868
+ };
22869
+ }
22870
+ function paletteHex(input, token) {
22871
+ const hex = input.palette[token];
22872
+ return typeof hex === "string" ? {
22873
+ ok: true,
22874
+ hex
22875
+ } : {
22876
+ ok: false,
22877
+ error: {
22878
+ tag: "unknown_palette_token",
22879
+ variantId: input.variantId,
22880
+ state: input.state,
22881
+ token
22882
+ }
22883
+ };
22884
+ }
22885
+ function validateContrast(input) {
22886
+ const ratio = contrastRatio(input.backgroundHex, input.foregroundHex);
22887
+ return ratio >= BUTTON_CONTRAST_POLICY.textMinimumRatio ? { ok: true } : {
22888
+ ok: false,
22889
+ error: {
22890
+ tag: "insufficient_contrast",
22891
+ variantId: input.variantId,
22892
+ state: input.state,
22893
+ background: input.background,
22894
+ foreground: input.foreground,
22895
+ ratio,
22896
+ requiredRatio: BUTTON_CONTRAST_POLICY.textMinimumRatio
22897
+ }
22898
+ };
22899
+ }
22900
+ function tokenCss(token) {
22901
+ return `rgb(var(--tb-${token}))`;
22902
+ }
22903
+ function adjustShade(token, amount, palette) {
22904
+ const match = token.match(/^(.*)-(\d{2,3})$/);
22905
+ const base = match?.[1] ?? token;
22906
+ const currentShade = match?.[2] === void 0 ? 500 : Number(match[2]);
22907
+ const availableShades = [
22908
+ 50,
22909
+ 100,
22910
+ 200,
22911
+ 300,
22912
+ 400,
22913
+ 500,
22914
+ 600,
22915
+ 700,
22916
+ 800,
22917
+ 900,
22918
+ 950
22919
+ ].filter((shade) => typeof palette[`${base}-${shade}`] === "string");
22920
+ if (availableShades.length === 0) return token;
22921
+ const firstShade = availableShades[0];
22922
+ if (firstShade === void 0) return token;
22923
+ const exactIndex = availableShades.indexOf(currentShade);
22924
+ const startingIndex = exactIndex >= 0 ? exactIndex : availableShades.indexOf(availableShades.reduce((closestShade, shade) => Math.abs(shade - currentShade) < Math.abs(closestShade - currentShade) ? shade : closestShade, firstShade));
22925
+ const stepCount = Math.abs(amount) / 100;
22926
+ const direction = amount < 0 ? -1 : 1;
22927
+ const targetShade = availableShades[Math.max(0, Math.min(availableShades.length - 1, startingIndex + direction * stepCount))];
22928
+ return targetShade === void 0 ? token : unsafeAsOpaqueButtonColourToken(`${base}-${targetShade}`);
22929
+ }
22930
+ function readShadeAmount(effect) {
22931
+ const value = effect.options?.amount ?? 100;
22932
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value % 100 === 0 ? value : null;
22933
+ }
22934
+ function readOpaqueToken(effect, key, fallback) {
22935
+ return parseOpaqueButtonColourToken(effect.options?.[key] ?? fallback);
22936
+ }
22937
+ function unsupportedEffect(input, effect, reason) {
22938
+ return {
22939
+ ok: false,
22940
+ error: {
22941
+ tag: "unsupported_colour_effect",
22942
+ variantId: input.variantId,
22943
+ state: input.state,
22944
+ effectId: effect.effectId,
22945
+ reason
22946
+ }
22947
+ };
22948
+ }
22949
+ function invalidEffectOption(input, effect, option) {
22950
+ return {
22951
+ ok: false,
22952
+ error: {
22953
+ tag: "invalid_colour_effect_options",
22954
+ variantId: input.variantId,
22955
+ state: input.state,
22956
+ effectId: effect.effectId,
22957
+ option
22958
+ }
22959
+ };
22960
+ }
22961
+ function inapplicableEffect(input, effect, treatment) {
22962
+ return {
22963
+ ok: false,
22964
+ error: {
22965
+ tag: "inapplicable_colour_effect",
22966
+ variantId: input.variantId,
22967
+ state: input.state,
22968
+ effectId: effect.effectId,
22969
+ treatment: treatment.type
22970
+ }
22971
+ };
22972
+ }
22973
+ function unknownEffect(input, effect) {
22974
+ return {
22975
+ ok: false,
22976
+ error: {
22977
+ tag: "unknown_button_effect",
22978
+ variantId: input.variantId,
22979
+ state: input.state,
22980
+ effectId: effect.effectId
22981
+ }
22982
+ };
22983
+ }
22984
+ function interpolateHex(startHex, endHex, position) {
22985
+ const start = hexToRgb$1(startHex);
22986
+ const end = hexToRgb$1(endHex);
22987
+ const channel = (from, to) => Math.round(from + (to - from) * position).toString(16).padStart(2, "0");
22988
+ return `#${channel(start.r, end.r)}${channel(start.g, end.g)}${channel(start.b, end.b)}`;
22989
+ }
22990
+ //#endregion
22991
+ //#region ../theme-core/src/buttons/resolvedButtonCss.ts
22992
+ /**
22993
+ * Canonical colour declaration mapping shared by full themes and ThemeBridge.
22994
+ * Both background properties are always emitted so transitions cannot retain
22995
+ * a stale gradient or solid layer.
22996
+ */
22997
+ function resolvedButtonColourDeclarations(colour) {
22998
+ switch (colour.kind) {
22999
+ case "solid": return [
23000
+ ["background-color", colour.backgroundCss],
23001
+ ["background-image", "none"],
23002
+ ["color", colour.foregroundCss]
23003
+ ];
23004
+ case "gradient": return [
23005
+ ["background-color", "transparent"],
23006
+ ["background-image", colour.backgroundCss],
23007
+ ["color", colour.foregroundCss]
23008
+ ];
23009
+ case "surface": return [
23010
+ ["background-color", colour.backgroundCss],
23011
+ ["background-image", "none"],
23012
+ ["color", colour.foregroundCss]
23013
+ ];
23014
+ }
23015
+ }
23016
+ function resolvedButtonFocusDeclarations(indicator) {
23017
+ return indicator.kind === "two-tone" ? [
23018
+ ["outline", `2px solid ${indicator.innerCss}`],
23019
+ ["outline-offset", "2px"],
23020
+ ["box-shadow", `0 0 0 4px ${indicator.outerCss}`]
23021
+ ] : [["outline", `3px solid ${indicator.colorCss}`], ["outline-offset", "3px"]];
23022
+ }
23023
+ /**
23024
+ * Disabled buttons retain their validated colour pair and add a non-layout
23025
+ * visual affordance. `currentColor` keeps the cue inside the same contrast
23026
+ * relationship, while retaining pointer events lets the not-allowed cursor
23027
+ * remain observable.
23028
+ */
23029
+ const RESOLVED_BUTTON_DISABLED_DECLARATIONS = [
23030
+ ["cursor", "not-allowed"],
23031
+ ["transform", "none"],
23032
+ ["box-shadow", "none"],
23033
+ ["outline", "2px dashed currentColor"],
23034
+ ["outline-offset", "-4px"]
23035
+ ];
23036
+ //#endregion
22034
23037
  //#region ../theme-core/src/buttons/generateButtonCss.ts
22035
23038
  /**
22036
23039
  * Class the `button` runtime node adds when it renders its own trailing icon,
@@ -22047,7 +23050,7 @@ const LETTER_SPACING_MAP = {
22047
23050
  normal: "0",
22048
23051
  loose: "0.05em"
22049
23052
  };
22050
- function rule(selector, declarations) {
23053
+ function rule$1(selector, declarations) {
22051
23054
  return {
22052
23055
  selectors: typeof selector === "string" ? [selector] : selector,
22053
23056
  declarations
@@ -22097,6 +23100,7 @@ const STRUCTURAL_DECLARATIONS = [
22097
23100
  function generateButtonCss(options) {
22098
23101
  const { themeId, theme, buttonSystem: providedButtonSystem } = options;
22099
23102
  const tokens = new TokenResolver(theme);
23103
+ const palette = expandPalette(theme.palette);
22100
23104
  const buttonSystem = providedButtonSystem ?? generateDefaultButtonSystem(theme);
22101
23105
  const themeSizes = {
22102
23106
  ...DEFAULT_BUTTON_SIZES,
@@ -22105,34 +23109,109 @@ function generateButtonCss(options) {
22105
23109
  const enabledVariants = buttonSystem.variants.filter((v) => v.enabled);
22106
23110
  const aliasSource = resolveAliasSource(enabledVariants);
22107
23111
  const chunks = [];
22108
- for (const variant of enabledVariants) chunks.push(...variantChunks(variant, buttonSystem.global, themeSizes, themeId, tokens, theme));
23112
+ for (const variant of enabledVariants) {
23113
+ const compiled = compileRuntimeVariant(variant, buttonSystem.global, palette);
23114
+ if (compiled.fallbackError) chunks.push({ raw: `/* ${formatButtonColourContractError(compiled.fallbackError)} Using contrast-safe surface fallback. */` });
23115
+ chunks.push(...variantChunks(compiled.variant, compiled.global, themeSizes, themeId, tokens, theme, compiled.colours, compiled.focusIndicator, compiled.nonColourEffects));
23116
+ }
22109
23117
  if (aliasSource) {
22110
- const alias = {
23118
+ const compiled = compileRuntimeVariant({
22111
23119
  ...aliasSource,
22112
23120
  id: DEFAULT_VARIANT_ALIAS_ID,
22113
23121
  name: "Default"
22114
- };
22115
- chunks.push(...variantChunks(alias, buttonSystem.global, themeSizes, themeId, tokens, theme));
23122
+ }, buttonSystem.global, palette);
23123
+ if (compiled.fallbackError) chunks.push({ raw: `/* ${formatButtonColourContractError(compiled.fallbackError)} Using contrast-safe surface fallback. */` });
23124
+ chunks.push(...variantChunks(compiled.variant, compiled.global, themeSizes, themeId, tokens, theme, compiled.colours, compiled.focusIndicator, compiled.nonColourEffects));
22116
23125
  }
22117
23126
  return chunks.map(renderChunk).join("\n\n");
22118
23127
  }
23128
+ function compileRuntimeVariant(variant, global, palette) {
23129
+ const result = compileButtonColourStates({
23130
+ variant,
23131
+ global,
23132
+ palette
23133
+ });
23134
+ if (result.ok) return {
23135
+ variant,
23136
+ global,
23137
+ colours: result.states,
23138
+ focusIndicator: result.focusIndicator,
23139
+ nonColourEffects: result.nonColourEffects
23140
+ };
23141
+ const fallbackVariant = {
23142
+ ...variant,
23143
+ background: {
23144
+ type: "surface",
23145
+ role: "link"
23146
+ },
23147
+ hoverBackgroundToken: void 0,
23148
+ effects: void 0
23149
+ };
23150
+ const fallbackGlobal = {
23151
+ ...global,
23152
+ hoverColor: "none",
23153
+ hoverColorToken: void 0
23154
+ };
23155
+ const fallback = compileButtonColourStates({
23156
+ variant: fallbackVariant,
23157
+ global: fallbackGlobal,
23158
+ palette
23159
+ });
23160
+ if (!fallback.ok) {
23161
+ const colour = {
23162
+ kind: "surface",
23163
+ role: "link",
23164
+ backgroundCss: "transparent",
23165
+ foregroundCss: surfaceForegroundValue("link")
23166
+ };
23167
+ return {
23168
+ variant: fallbackVariant,
23169
+ global: fallbackGlobal,
23170
+ colours: {
23171
+ base: colour,
23172
+ hover: colour,
23173
+ focus: colour,
23174
+ active: colour,
23175
+ disabled: colour
23176
+ },
23177
+ focusIndicator: {
23178
+ kind: "surface-role",
23179
+ colorCss: colour.foregroundCss,
23180
+ minimumSurfaceContrast: BUTTON_CONTRAST_POLICY.nonTextMinimumRatio
23181
+ },
23182
+ nonColourEffects: {},
23183
+ fallbackError: result.error
23184
+ };
23185
+ }
23186
+ return {
23187
+ variant: fallbackVariant,
23188
+ global: fallbackGlobal,
23189
+ colours: fallback.states,
23190
+ focusIndicator: fallback.focusIndicator,
23191
+ nonColourEffects: fallback.nonColourEffects,
23192
+ fallbackError: result.error
23193
+ };
23194
+ }
22119
23195
  /** Pick the highest-priority variant whose role we'll alias to `.default`. */
22120
23196
  function resolveAliasSource(enabled) {
22121
23197
  if (enabled.some((v) => v.id === DEFAULT_VARIANT_ALIAS_ID)) return null;
22122
23198
  return enabled.slice().sort((a, b) => a.priority - b.priority)[0] ?? null;
22123
23199
  }
22124
- function variantChunks(variant, global, themeSizes, themeId, tokens, theme) {
23200
+ function variantChunks(variant, global, themeSizes, themeId, tokens, theme, colours, focusIndicator, nonColourEffects) {
22125
23201
  const out = [];
22126
23202
  const variantId = parseButtonVariantIdForCss(variant.id);
22127
23203
  const selector = `:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}`;
22128
- out.push(variantBaseRule(variant, selector, global, tokens, theme));
23204
+ out.push(variantBaseRule(variant, selector, global, tokens, theme, colours.base));
22129
23205
  out.push({ raw: generateInteractiveChildInheritanceCSS(selector) });
22130
- const hoverBg = variantHoverBackgroundRule(variant, variantId, global, themeId, tokens);
22131
- if (hoverBg) out.push(hoverBg);
22132
- out.push(variantFocusVisibleRule(variantId, themeId));
22133
- const effects = variantEffectsCss(variant, themeId, tokens, theme);
23206
+ for (const state of [
23207
+ "hover",
23208
+ "focus",
23209
+ "active"
23210
+ ]) if (!resolvedColoursEqual(colours.base, colours[state])) out.push(variantStateColourRule(variantId, themeId, state, colours[state]));
23211
+ const effects = variantEffectsCss(variant, nonColourEffects, themeId, tokens, theme);
22134
23212
  if (effects) out.push({ raw: effects });
22135
- const affordance = variantAffordanceRule(variant, selector, tokens);
23213
+ out.push(variantFocusVisibleRule(variantId, themeId, focusIndicator));
23214
+ const affordance = variantAffordanceRule(variant, selector);
22136
23215
  if (affordance) out.push(affordance);
22137
23216
  out.push(...variantDisabledRules(variantId, themeId));
22138
23217
  out.push(...variantSizeRules(variant, variantId, themeSizes, themeId));
@@ -22147,21 +23226,20 @@ function variantChunks(variant, global, themeSizes, themeId, tokens, theme) {
22147
23226
  * one (node overrides personality). Colour comes from the affordance token, or
22148
23227
  * `currentColor` (the button text colour) when no token is given.
22149
23228
  */
22150
- function variantAffordanceRule(variant, selector, tokens) {
23229
+ function variantAffordanceRule(variant, selector) {
22151
23230
  const affordance = variant.affordance;
22152
23231
  if (!affordance) return null;
22153
23232
  const maskUrl = affordanceIconMaskUrl(affordance.iconId);
22154
23233
  if (!maskUrl) return null;
22155
- const color = affordance.colorToken ? variant.background.type === "transparent" ? surfaceLinkColor(tokens.getColor(affordance.colorToken)) : tokens.getColor(affordance.colorToken) : "currentColor";
22156
23234
  const mask = `url("${maskUrl}") no-repeat center / contain`;
22157
- return rule(`${selector}:not(.${AFFORDANCE_SUPPRESS_CLASS})::after`, [
23235
+ return rule$1(`${selector}:not(.${AFFORDANCE_SUPPRESS_CLASS})::after`, [
22158
23236
  ["content", "\"\""],
22159
23237
  ["display", "inline-block"],
22160
23238
  ["width", "1em"],
22161
23239
  ["height", "1em"],
22162
23240
  ["margin-inline-start", "0.5em"],
22163
23241
  ["flex", "0 0 auto"],
22164
- ["background-color", color],
23242
+ ["background-color", "currentColor"],
22165
23243
  ["-webkit-mask", mask],
22166
23244
  ["mask", mask]
22167
23245
  ]);
@@ -22171,7 +23249,7 @@ function parseButtonVariantIdForCss(value) {
22171
23249
  if (!variantId) throw new Error("Button variant id must be a non-empty string");
22172
23250
  return variantId;
22173
23251
  }
22174
- function variantBaseRule(variant, selector, global, tokens, theme) {
23252
+ function variantBaseRule(variant, selector, global, tokens, theme, colour) {
22175
23253
  const decls = [];
22176
23254
  decls.push(["font-weight", String(global.fontWeight)]);
22177
23255
  decls.push(["font-family", resolveFontFamily(global)]);
@@ -22183,20 +23261,12 @@ function variantBaseRule(variant, selector, global, tokens, theme) {
22183
23261
  for (const decl of STRUCTURAL_DECLARATIONS) decls.push(decl);
22184
23262
  decls.push(["border-radius", resolveBorderRadius(variant, global)]);
22185
23263
  decls.push(["box-shadow", resolveBoxShadow(variant, global, theme, tokens)]);
22186
- if (variant.background.type === "solid") decls.push(["background-color", tokens.getColor(variant.background.colorToken)]);
22187
- else if (variant.background.type === "gradient") {
22188
- const colors = variant.background.stops.map((stop) => tokens.getColor(stop)).join(", ");
22189
- const direction = GRADIENT_DIRECTION_MAP$3[variant.background.direction] || "to right";
22190
- decls.push(["background-image", `linear-gradient(${direction}, ${colors})`]);
22191
- } else decls.push(["background-color", "transparent"]);
22192
- const foregroundColor = variant.background.type === "transparent" ? surfaceLinkColor(tokens.getColor(variant.textColorToken)) : tokens.getColor(variant.textColorToken);
22193
- decls.push(["color", foregroundColor]);
23264
+ decls.push(...resolvedButtonColourDeclarations(colour));
22194
23265
  if (variant.border) {
22195
23266
  const borderWidth = BORDER_WIDTH_MAP$4[variant.border.widthClass] || "1px";
22196
- const borderColor = variant.background.type === "transparent" ? surfaceLinkColor(tokens.getColor(variant.border.colorToken)) : tokens.getColor(variant.border.colorToken);
22197
- decls.push(["border", `${borderWidth} solid ${borderColor}`]);
23267
+ decls.push(["border", `${borderWidth} solid currentColor`]);
22198
23268
  }
22199
- return rule(selector, decls);
23269
+ return rule$1(selector, decls);
22200
23270
  }
22201
23271
  /**
22202
23272
  * Resolve a colour for a button that does not own its own background.
@@ -22205,30 +23275,22 @@ function variantBaseRule(variant, selector, global, tokens, theme) {
22205
23275
  * token fallback keeps buttons usable in contexts that do not provide that
22206
23276
  * contract (and preserves the existing theme behaviour there).
22207
23277
  */
22208
- function surfaceLinkColor(fallbackColor) {
22209
- return `var(--rb-surface-link-color, ${fallbackColor})`;
23278
+ function variantStateColourRule(variantId, themeId, state, colour) {
23279
+ return rule$1(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:${state}`, resolvedButtonColourDeclarations(colour));
22210
23280
  }
22211
- function variantHoverBackgroundRule(variant, variantId, global, themeId, tokens) {
22212
- let hoverToken = variant.hoverBackgroundToken;
22213
- if (!hoverToken && global.hoverColor === "token" && global.hoverColorToken) hoverToken = global.hoverColorToken;
22214
- if (!hoverToken) return null;
22215
- return rule(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:hover`, [["background-color", tokens.getColor(hoverToken)]]);
23281
+ function variantFocusVisibleRule(variantId, themeId, focusIndicator) {
23282
+ return rule$1(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:focus-visible`, resolvedButtonFocusDeclarations(focusIndicator));
22216
23283
  }
22217
- function variantFocusVisibleRule(variantId, themeId) {
22218
- return rule(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:focus-visible`, [["outline", "2px solid currentColor"], ["outline-offset", "3px"]]);
22219
- }
22220
- function variantEffectsCss(variant, themeId, tokens, theme) {
22221
- if (!variant.effects) return "";
23284
+ function variantEffectsCss(variant, effects, themeId, tokens, theme) {
23285
+ if (!effects) return "";
22222
23286
  const elementConfig = {
22223
- background: variant.background,
22224
- textColorToken: variant.textColorToken,
22225
- border: variant.border ? { colorToken: variant.border.colorToken } : void 0,
23287
+ background: variant.background.type === "surface" ? { type: "transparent" } : variant.background,
22226
23288
  shadow: variant.shadow
22227
23289
  };
22228
23290
  return generateEffectsCSS({
22229
23291
  themeId,
22230
23292
  variantId: variant.id,
22231
- effects: variant.effects,
23293
+ effects,
22232
23294
  elementConfig,
22233
23295
  tokens,
22234
23296
  theme
@@ -22243,18 +23305,15 @@ function variantDisabledRules(variantId, themeId) {
22243
23305
  `${base}:focus`
22244
23306
  ];
22245
23307
  const pseudoSelectors = [`${base}::before`, `${base}::after`];
22246
- return [rule(disabledSelectors, [
22247
- ["opacity", "0.5"],
22248
- ["cursor", "not-allowed"],
22249
- ["pointer-events", "none"],
22250
- ["transform", "none"],
22251
- ["box-shadow", "none"]
22252
- ]), rule(pseudoSelectors, [
23308
+ return [rule$1(disabledSelectors, [...RESOLVED_BUTTON_DISABLED_DECLARATIONS]), rule$1(pseudoSelectors, [
22253
23309
  ["opacity", "0"],
22254
23310
  ["animation", "none"],
22255
23311
  ["pointer-events", "none"]
22256
23312
  ])];
22257
23313
  }
23314
+ function resolvedColoursEqual(left, right) {
23315
+ return JSON.stringify(left) === JSON.stringify(right);
23316
+ }
22258
23317
  function variantSizeRules(variant, variantId, themeSizes, themeId) {
22259
23318
  const borderWidth = variant.border ? BORDER_WIDTH_MAP$4[variant.border.widthClass] || "1px" : "0px";
22260
23319
  const rules = [];
@@ -22264,7 +23323,7 @@ function variantSizeRules(variant, variantId, themeSizes, themeId) {
22264
23323
  const decls = [];
22265
23324
  for (const decl of paddingDeclarations(config.padding, borderWidth)) decls.push(decl);
22266
23325
  if (config.fontSize) decls.push(["font-size", FONT_SIZE_MAP[config.fontSize] || "1rem"]);
22267
- rules.push(rule(selector, decls));
23326
+ rules.push(rule$1(selector, decls));
22268
23327
  }
22269
23328
  return rules;
22270
23329
  }
@@ -22322,6 +23381,27 @@ function resolveBoxShadow(variant, global, theme, tokens) {
22322
23381
  const color = shadowConfig.colorToken ? tokens.getColor(shadowConfig.colorToken) : void 0;
22323
23382
  return generateShadowValue$1(shadowConfig.elevation, shadowConfig.softness, shadowConfig.position ?? "bottom", color);
22324
23383
  }
23384
+ buttonPersonalities.map((personality) => ({
23385
+ id: personality.id,
23386
+ name: personality.name,
23387
+ description: personality.description,
23388
+ category: presetCategory(personality),
23389
+ buttonSystem: personality.buttonSystem,
23390
+ compatibility: {
23391
+ ...personality.tags.tone ? { tones: [personality.tags.tone] } : {},
23392
+ ...personality.tags.energy ? { energies: [personality.tags.energy] } : {}
23393
+ }
23394
+ }));
23395
+ function presetCategory(personality) {
23396
+ switch (personality.tags.era) {
23397
+ case "retro": return "retro";
23398
+ case "editorial": return "minimal";
23399
+ case "organic": return "artistic";
23400
+ case "modern": return personality.tags.tone === "bold" ? "modern" : "technical";
23401
+ case "technical": return "technical";
23402
+ case void 0: return "modern";
23403
+ }
23404
+ }
22325
23405
  //#endregion
22326
23406
  //#region ../theme-core/src/cards/types.ts
22327
23407
  const cardVariantRoleValues = ["default", "alternative"];
@@ -22558,67 +23638,6 @@ function generateDefaultCardSystem(theme) {
22558
23638
  return { variants: [defaultVariant, alternativeVariant] };
22559
23639
  }
22560
23640
  //#endregion
22561
- //#region ../theme-core/src/surfaces/foregroundRoles.ts
22562
- const SURFACE_FOREGROUND_ROLES = [
22563
- "text",
22564
- "heading",
22565
- "link",
22566
- "supporting"
22567
- ];
22568
- //#endregion
22569
- //#region ../theme-core/src/surfaces/foregroundContract.ts
22570
- const SURFACE_FOREGROUND_CSS_VARS = {
22571
- text: "--rb-surface-text-color",
22572
- heading: "--rb-surface-heading-color",
22573
- link: "--rb-surface-link-color",
22574
- supporting: "--rb-surface-supporting-color"
22575
- };
22576
- const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
22577
- const DEFAULT_ROLE_FALLBACKS = {
22578
- text: DEFAULT_SURFACE_TEXT_COLOR,
22579
- heading: DEFAULT_SURFACE_TEXT_COLOR,
22580
- link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
22581
- supporting: "rgb(var(--tb-mutedText, 100 116 139))"
22582
- };
22583
- function requireSurfaceColorValue(role, value) {
22584
- const normalized = value.trim();
22585
- if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
22586
- return normalized;
22587
- }
22588
- function completeSurfaceForegrounds(input) {
22589
- const text = requireSurfaceColorValue("text", input.text);
22590
- return {
22591
- text,
22592
- heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
22593
- link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
22594
- supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
22595
- };
22596
- }
22597
- function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
22598
- const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
22599
- const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
22600
- if (role === "text") return `var(${roleVariable}, ${fallback})`;
22601
- return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
22602
- }
22603
- function surfaceForegroundValueWithOpacity(role, opacityPercent) {
22604
- if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
22605
- return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
22606
- }
22607
- function surfaceForegroundStyle(input) {
22608
- const foregrounds = completeSurfaceForegrounds(input);
22609
- return {
22610
- color: foregrounds.text,
22611
- [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
22612
- [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
22613
- [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
22614
- [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
22615
- };
22616
- }
22617
- function surfaceForegroundDeclarations(input) {
22618
- const foregrounds = completeSurfaceForegrounds(input);
22619
- return [`color: ${foregrounds.text}`, ...SURFACE_FOREGROUND_ROLES.map((role) => `${SURFACE_FOREGROUND_CSS_VARS[role]}: ${foregrounds[role]}`)];
22620
- }
22621
- //#endregion
22622
23641
  //#region ../theme-core/src/cards/generateCardCss.ts
22623
23642
  const PER_CORNER_RADIUS_MAP$1 = {
22624
23643
  square: "0",
@@ -23055,47 +24074,6 @@ const FONT_WEIGHT_MAP = {
23055
24074
  black: "800"
23056
24075
  };
23057
24076
  //#endregion
23058
- //#region ../theme-core/src/gradients/types.ts
23059
- /**
23060
- * Gradient configuration types and schemas
23061
- */
23062
- /**
23063
- * Gradient color stop
23064
- */
23065
- const gradientStopSchema = z$1.object({
23066
- color: themeColorTokenSchema,
23067
- position: z$1.number().min(0).max(100)
23068
- });
23069
- /**
23070
- * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
23071
- * so existing radial gradients are byte-identical.
23072
- *
23073
- * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
23074
- * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
23075
- */
23076
- const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
23077
- /**
23078
- * Gradient configuration.
23079
- *
23080
- * `type` is optional and defaults to `'linear'` so gradients persisted without a
23081
- * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
23082
- * `'conic'` are additive kinds:
23083
- * - linear: `angle` (degrees)
23084
- * - radial: optional `shape` (circle/ellipse) + `position`
23085
- * - conic: optional `angle` (from-angle, degrees) + `position`
23086
- */
23087
- const gradientConfigSchema = z$1.object({
23088
- type: z$1.enum([
23089
- "linear",
23090
- "radial",
23091
- "conic"
23092
- ]).default("linear"),
23093
- angle: z$1.number().min(0).max(360).optional(),
23094
- shape: radialGradientShapeSchema.optional(),
23095
- position: z$1.string().optional(),
23096
- stops: z$1.array(gradientStopSchema).min(2)
23097
- });
23098
- //#endregion
23099
24077
  //#region ../theme-core/src/interactive/baseSchema.ts
23100
24078
  /**
23101
24079
  * Shared Base Schemas for Interactive Elements
@@ -24846,6 +25824,21 @@ z$1.object({
24846
25824
  blocks: z$1.array(blockDesignPlanSchema),
24847
25825
  styleAuthorities: z$1.array(blockRuntimeStyleAuthoritySchema)
24848
25826
  }).strict();
25827
+ //#endregion
25828
+ //#region ../theme-core/src/site-styles/runtimeCompiler.ts
25829
+ function defineSectionForegroundRuntimeTokens(input) {
25830
+ return {
25831
+ textColor: requireThemeColorToken(input.textColor),
25832
+ headingColor: requireThemeColorToken(input.headingColor),
25833
+ linkColor: requireThemeColorToken(input.linkColor),
25834
+ supportingColor: requireThemeColorToken(input.supportingColor)
25835
+ };
25836
+ }
25837
+ function requireThemeColorToken(value) {
25838
+ const token = parseThemeColorToken(value);
25839
+ if (!token) throw new Error(`Invalid section foreground Theme color token: ${String(value)}`);
25840
+ return token;
25841
+ }
24849
25842
  function defineDesignRuntimeRules(rules) {
24850
25843
  return rules;
24851
25844
  }
@@ -25116,24 +26109,24 @@ const kirtanOrnateDesignRuntimeRules = defineDesignRuntimeRules({
25116
26109
  type: "color",
25117
26110
  color: "muted"
25118
26111
  },
25119
- foreground: {
26112
+ foreground: defineSectionForegroundRuntimeTokens({
25120
26113
  textColor: "text",
25121
26114
  headingColor: "primary",
25122
26115
  linkColor: "primary",
25123
26116
  supportingColor: "text"
25124
- }
26117
+ })
25125
26118
  },
25126
26119
  "brand-depth-band": {
25127
26120
  background: {
25128
26121
  type: "color",
25129
26122
  color: "primary-800"
25130
26123
  },
25131
- foreground: {
26124
+ foreground: defineSectionForegroundRuntimeTokens({
25132
26125
  textColor: "text",
25133
26126
  headingColor: "primary",
25134
26127
  linkColor: "primary",
25135
26128
  supportingColor: "text"
25136
- }
26129
+ })
25137
26130
  },
25138
26131
  "hero-image": {
25139
26132
  background: {
@@ -25141,12 +26134,32 @@ const kirtanOrnateDesignRuntimeRules = defineDesignRuntimeRules({
25141
26134
  opacity: 1,
25142
26135
  position: "center center"
25143
26136
  },
25144
- foreground: {
26137
+ foreground: defineSectionForegroundRuntimeTokens({
25145
26138
  textColor: "text",
25146
26139
  headingColor: "primary",
25147
26140
  linkColor: "primary",
25148
26141
  supportingColor: "text"
25149
- }
26142
+ })
26143
+ }
26144
+ }
26145
+ });
26146
+ const primaryGlowGradientPresetId = parseGradientPresetId("primary-glow");
26147
+ if (!primaryGlowGradientPresetId) throw new Error("Expected primary-glow in the Theme Core gradient catalog");
26148
+ const fulcrumDesignRuntimeRules = defineDesignRuntimeRules({
26149
+ ...defaultDesignRuntimeRules,
26150
+ sectionSurfaces: {
26151
+ ...defaultDesignRuntimeRules.sectionSurfaces,
26152
+ "accent-band": {
26153
+ background: {
26154
+ type: "gradient",
26155
+ gradientPresetId: primaryGlowGradientPresetId
26156
+ },
26157
+ foreground: defineSectionForegroundRuntimeTokens({
26158
+ textColor: "primaryForeground",
26159
+ headingColor: "primaryForeground",
26160
+ linkColor: "primaryForeground",
26161
+ supportingColor: "primaryForeground"
26162
+ })
25150
26163
  }
25151
26164
  }
25152
26165
  });
@@ -26534,6 +27547,8 @@ const curatedSiteStyles = [
26534
27547
  },
26535
27548
  compositionBudget: quietBudget,
26536
27549
  typography: fulcrumTypography,
27550
+ designRuntimeRules: fulcrumDesignRuntimeRules,
27551
+ recommendedAppearancePresetIds: ["cta-accent-band"],
26537
27552
  themeOverrides: {
26538
27553
  shadow: { colorToken: "primary" },
26539
27554
  accordions: {
@@ -30002,9 +31017,15 @@ function createButtonGroup(options = {}) {
30002
31017
  //#endregion
30003
31018
  //#region ../blocks/src/system/fields/ctas.ts
30004
31019
  function createCtasRepeater(options = {}) {
30005
- const { id = "ctas", label = "Calls to action", itemLabel = "CTA", minItems = 0, maxItems, button } = options;
31020
+ const { id = "ctas", label = "Calls to action", itemLabel = "CTA", minItems = 0, maxItems, button, defaultValue } = options;
31021
+ const fieldId = id;
31022
+ const buttonField = createButtonGroup({
31023
+ showGroupLabel: false,
31024
+ ...button ?? {},
31025
+ flattenInRepeater: true
31026
+ });
30006
31027
  return {
30007
- id,
31028
+ id: fieldId,
30008
31029
  type: "repeater",
30009
31030
  label,
30010
31031
  itemLabel,
@@ -30013,11 +31034,8 @@ function createCtasRepeater(options = {}) {
30013
31034
  maxItems,
30014
31035
  required: false,
30015
31036
  allowConversion: true,
30016
- schema: { fields: [createButtonGroup({
30017
- showGroupLabel: false,
30018
- ...button ?? {},
30019
- flattenInRepeater: true
30020
- })] }
31037
+ ...defaultValue !== void 0 ? { defaultValue: [...defaultValue] } : {},
31038
+ schema: { fields: [buttonField] }
30021
31039
  };
30022
31040
  }
30023
31041
  //#endregion
@@ -30067,6 +31085,7 @@ const heroCtaRowFragment = defineFragment({
30067
31085
  label: "Calls to action",
30068
31086
  itemLabel: "CTA",
30069
31087
  maxItems: 3,
31088
+ defaultValue: [],
30070
31089
  button: {
30071
31090
  variants: [
30072
31091
  {
@@ -30134,7 +31153,7 @@ const ctaCopyFragment = defineFragment({
30134
31153
  eyebrowPath: "content.eyebrow",
30135
31154
  titlePath: "content.title",
30136
31155
  className: "cta-heading"
30137
- }), richText({ className: "cta-content rb-prose rb-mx-auto rb-max-w-none" }, when("content.content"), bind("content.content"))]
31156
+ }), richText({ className: "cta-content rb-prose rb-mx-auto rb-max-w-none" }, when("content"), bind("content"))]
30138
31157
  });
30139
31158
  //#endregion
30140
31159
  //#region ../blocks/src/system/fragments/library/testimonialsHeading.ts
@@ -32351,6 +33370,7 @@ function createBlockManifest(config) {
32351
33370
  previewCustomize: config.previewCustomize,
32352
33371
  category: config.category,
32353
33372
  contentTypes: config.contentTypes ? [...config.contentTypes] : void 0,
33373
+ contentSource: config.contentSource,
32354
33374
  tags: config.tags ? [...config.tags] : [],
32355
33375
  icon: config.icon ?? "Box",
32356
33376
  layout,
@@ -32496,6 +33516,7 @@ const heroBlockDefinition = { manifest: createBlockManifest({
32496
33516
  title: "Hero",
32497
33517
  titleSource: "headline",
32498
33518
  category: "layout",
33519
+ contentSource: { kind: "inline" },
32499
33520
  includeContainerStyles: true,
32500
33521
  promotedFields: ["_sectionStyles.background.image"],
32501
33522
  fragments: [
@@ -32769,6 +33790,7 @@ const introBlockDefinition = { manifest: createBlockManifest({
32769
33790
  id: "block.intro",
32770
33791
  title: "Intro",
32771
33792
  category: "layout",
33793
+ contentSource: { kind: "inline" },
32772
33794
  includeContainerStyles: true,
32773
33795
  titleSource: "title",
32774
33796
  previewCustomize: { quickFieldPriority: [
@@ -32954,6 +33976,7 @@ const bodyTextBlockDefinition = { manifest: createBlockManifest({
32954
33976
  title: "Body Text",
32955
33977
  titleSource: "heading",
32956
33978
  category: "content",
33979
+ contentSource: { kind: "inline" },
32957
33980
  fragments: [{
32958
33981
  fragment: bodyCopyFragment,
32959
33982
  fieldPriority: 0
@@ -33010,6 +34033,10 @@ const blogPostBlockDefinition = {
33010
34033
  description: "Highlights a single blog post with title, image, and excerpt.",
33011
34034
  category: "blog",
33012
34035
  contentTypes: ["post"],
34036
+ contentSource: {
34037
+ kind: "entries",
34038
+ contentType: "post"
34039
+ },
33013
34040
  tags: [
33014
34041
  "blog",
33015
34042
  "post",
@@ -33307,6 +34334,10 @@ const blogListingManifest = createBlockManifest({
33307
34334
  ],
33308
34335
  description: "Display a collection of blog posts in grid or stack layout.",
33309
34336
  contentTypes: ["post"],
34337
+ contentSource: {
34338
+ kind: "entries",
34339
+ contentType: "post"
34340
+ },
33310
34341
  tags: [
33311
34342
  "blog",
33312
34343
  "posts",
@@ -33349,6 +34380,7 @@ const ctaFullBlockDefinition = { manifest: createBlockManifest({
33349
34380
  id: "block.cta-full",
33350
34381
  title: "Full-width CTA",
33351
34382
  category: "marketing",
34383
+ contentSource: { kind: "inline" },
33352
34384
  fragments: [{
33353
34385
  fragment: ctaCopyFragment,
33354
34386
  fieldPriority: 0
@@ -33382,6 +34414,7 @@ const singleButtonBlockDefinition = { manifest: createBlockManifest({
33382
34414
  id: "block.single-button",
33383
34415
  title: "Single Button",
33384
34416
  category: "marketing",
34417
+ contentSource: { kind: "inline" },
33385
34418
  fragments: [{
33386
34419
  fragment: singleButtonFragment,
33387
34420
  fieldPriority: 0
@@ -33664,6 +34697,7 @@ const formManifest = createBlockManifest({
33664
34697
  paletteHidden: false
33665
34698
  },
33666
34699
  category: "interactive",
34700
+ contentSource: { kind: "inline" },
33667
34701
  tags: [
33668
34702
  "form",
33669
34703
  "contact",
@@ -33726,6 +34760,7 @@ const contactNewsletterBlockDefinition = {
33726
34760
  titleSource: "contactTitle",
33727
34761
  description: "Two-column newsletter signup and contact form section.",
33728
34762
  category: "interactive",
34763
+ contentSource: { kind: "inline" },
33729
34764
  layout: contactNewsletterLayout,
33730
34765
  defaultVariant: "default",
33731
34766
  variants: { default: contactNewsletterLayout },
@@ -33929,6 +34964,7 @@ const faqBlockDefinition = { manifest: createBlockManifest({
33929
34964
  title: "FAQ",
33930
34965
  titleSource: "title",
33931
34966
  category: "content",
34967
+ contentSource: { kind: "inline" },
33932
34968
  includeContainerStyles: true,
33933
34969
  fragments: [{
33934
34970
  fragment: faqHeadingFragment,
@@ -34229,7 +35265,7 @@ const heroTitleLengthThresholds = {
34229
35265
  maxWords: 11
34230
35266
  }
34231
35267
  };
34232
- function isPlainObject$3(value) {
35268
+ function isPlainObject$4(value) {
34233
35269
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
34234
35270
  }
34235
35271
  function readString$1(obj, key) {
@@ -34238,7 +35274,7 @@ function readString$1(obj, key) {
34238
35274
  }
34239
35275
  function resolveGeneratedHeroLayoutStyles(content) {
34240
35276
  const raw = content._heroLayoutStyles;
34241
- return isPlainObject$3(raw) ? raw : null;
35277
+ return isPlainObject$4(raw) ? raw : null;
34242
35278
  }
34243
35279
  function readHeroLayoutString(content, generatedKey, fallbackKey = generatedKey) {
34244
35280
  const explicitValue = readString$1(content, fallbackKey);
@@ -34294,7 +35330,7 @@ const heroContentWrapperClassTransform = {
34294
35330
  summary: "Resolve hero content wrapper className from alignment + max width controls",
34295
35331
  schema: z$1.object({ base: z$1.string().optional() }),
34296
35332
  run: (value, options, context) => {
34297
- const content = isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {};
35333
+ const content = isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {};
34298
35334
  const alignment = resolveHeroContentAlignment(content);
34299
35335
  const maxWidth = resolveHeroContentMaxWidth(content);
34300
35336
  const typographyScale = resolveHeroTypographyScale(content);
@@ -34317,7 +35353,7 @@ const heroCtaJustifyTransform = {
34317
35353
  kind: "string",
34318
35354
  summary: "Map hero contentAlignment to CTA row justify value",
34319
35355
  run: (value, _options, context) => {
34320
- const alignment = resolveHeroContentAlignment(isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {});
35356
+ const alignment = resolveHeroContentAlignment(isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {});
34321
35357
  return alignment === "left" ? "start" : alignment === "right" ? "end" : "center";
34322
35358
  }
34323
35359
  };
@@ -34326,7 +35362,7 @@ const heroStackJustifyFromVerticalAlignmentTransform = {
34326
35362
  kind: "string",
34327
35363
  summary: "Map hero verticalAlignment to stack justify value",
34328
35364
  run: (value, _options, context) => {
34329
- const vertical = resolveHeroVerticalAlignment(isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {});
35365
+ const vertical = resolveHeroVerticalAlignment(isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {});
34330
35366
  return vertical === "top" ? "start" : vertical === "bottom" ? "end" : "center";
34331
35367
  }
34332
35368
  };
@@ -34336,7 +35372,7 @@ const heroGridClassFromVerticalAlignmentTransform = {
34336
35372
  summary: "Append align-items class for hero split layouts based on verticalAlignment",
34337
35373
  schema: z$1.object({ base: z$1.string() }),
34338
35374
  run: (value, options, context) => {
34339
- const vertical = resolveHeroVerticalAlignment(isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {});
35375
+ const vertical = resolveHeroVerticalAlignment(isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {});
34340
35376
  const itemsClass = vertical === "top" ? "rb-items-start" : vertical === "bottom" ? "rb-items-end" : "rb-items-center";
34341
35377
  return joinClasses$3([options.base, itemsClass]);
34342
35378
  }
@@ -35653,6 +36689,7 @@ const siteHeaderBlockDefinition = { manifest: createBlockManifest({
35653
36689
  id: "block.site-header",
35654
36690
  title: "Site Header",
35655
36691
  category: "layout",
36692
+ contentSource: { kind: "inline" },
35656
36693
  additionalFields: [{
35657
36694
  id: "mode",
35658
36695
  type: "select",
@@ -35857,6 +36894,7 @@ const siteFooterBlockDefinition = { manifest: createBlockManifest({
35857
36894
  id: "block.site-footer",
35858
36895
  title: "Site Footer",
35859
36896
  category: "layout",
36897
+ contentSource: { kind: "inline" },
35860
36898
  skipSectionStyles: true,
35861
36899
  layout: section$1({
35862
36900
  background: "background",
@@ -36006,6 +37044,7 @@ const testimonialsBlockDefinition = {
36006
37044
  id: "block.testimonials",
36007
37045
  title: "Testimonials",
36008
37046
  category: "marketing",
37047
+ contentSource: { kind: "inline" },
36009
37048
  includeContainerStyles: true,
36010
37049
  previewCustomize: { quickFieldPriority: [
36011
37050
  "avatarLayout",
@@ -36164,6 +37203,7 @@ const columnsBlockDefinition = { manifest: createBlockManifest({
36164
37203
  id: "block.columns",
36165
37204
  title: "Columns",
36166
37205
  category: "layout",
37206
+ contentSource: { kind: "inline" },
36167
37207
  additionalFields: [
36168
37208
  parseFieldDefinition({
36169
37209
  id: "equalHeight",
@@ -36397,6 +37437,7 @@ const collectionBlockDefinition = { manifest: createBlockManifest({
36397
37437
  id: "block.collection",
36398
37438
  title: "Collection",
36399
37439
  category: "layout",
37440
+ contentSource: { kind: "inline" },
36400
37441
  additionalFields: [
36401
37442
  parseFieldDefinition({
36402
37443
  id: "layout",
@@ -36684,6 +37725,7 @@ const featureCardsBlockDefinition = { manifest: createBlockManifest({
36684
37725
  id: "block.feature-cards",
36685
37726
  title: "Feature Cards",
36686
37727
  category: "layout",
37728
+ contentSource: { kind: "inline" },
36687
37729
  includeContainerStyles: true,
36688
37730
  titleSource: "title",
36689
37731
  additionalFields: [
@@ -37067,6 +38109,7 @@ const mediaTextBlockDefinition = { manifest: createBlockManifest({
37067
38109
  id: "block.media-text",
37068
38110
  title: "Media and Text",
37069
38111
  category: "layout",
38112
+ contentSource: { kind: "inline" },
37070
38113
  includeContainerStyles: true,
37071
38114
  titleSource: "title",
37072
38115
  previewCustomize: { quickFieldPriority: [
@@ -37467,6 +38510,7 @@ const appointmentBookingBlockDefinition = {
37467
38510
  paletteHidden: false
37468
38511
  },
37469
38512
  category: "interactive",
38513
+ contentSource: { kind: "inline" },
37470
38514
  tags: [
37471
38515
  "booking",
37472
38516
  "appointment",
@@ -37631,6 +38675,7 @@ const weeklySlotAvailabilityManifest = createBlockManifest({
37631
38675
  paletteHidden: false
37632
38676
  },
37633
38677
  category: "interactive",
38678
+ contentSource: { kind: "inline" },
37634
38679
  tags: [
37635
38680
  "booking",
37636
38681
  "appointment",
@@ -39326,6 +40371,7 @@ const eventRegistrationBlockDefinition = {
39326
40371
  titleSource: "heading",
39327
40372
  description: "Multi-step event registration form",
39328
40373
  category: "interactive",
40374
+ contentSource: { kind: "inline" },
39329
40375
  tags: [
39330
40376
  "events",
39331
40377
  "registration",
@@ -39432,15 +40478,9 @@ const eventRegistrationBlockDefinition = {
39432
40478
  defaultValue: "Complete Registration",
39433
40479
  maxLength: 40
39434
40480
  },
39435
- {
39436
- id: "buttonVariant",
39437
- type: "select",
39438
- label: "Button style",
40481
+ createButtonVariantField({
39439
40482
  description: "Choose a button style from your theme",
39440
- required: false,
39441
- multiple: false,
39442
- defaultValue: "primary",
39443
- options: [
40483
+ variants: [
39444
40484
  {
39445
40485
  value: "primary",
39446
40486
  label: "Primary"
@@ -39453,9 +40493,8 @@ const eventRegistrationBlockDefinition = {
39453
40493
  value: "outline",
39454
40494
  label: "Outline"
39455
40495
  }
39456
- ],
39457
- ui: { intent: "style" }
39458
- }
40496
+ ]
40497
+ })
39459
40498
  ],
39460
40499
  layout: [styledSection({
39461
40500
  baseClass: "",
@@ -39830,6 +40869,7 @@ const courseRegistrationBlockDefinition = {
39830
40869
  titleSource: "heading",
39831
40870
  description: "Enrollment form for multi-session courses",
39832
40871
  category: "interactive",
40872
+ contentSource: { kind: "inline" },
39833
40873
  tags: [
39834
40874
  "courses",
39835
40875
  "enrollment",
@@ -39943,15 +40983,9 @@ const courseRegistrationBlockDefinition = {
39943
40983
  defaultValue: "Sign up",
39944
40984
  maxLength: 40
39945
40985
  },
39946
- {
39947
- id: "buttonVariant",
39948
- type: "select",
39949
- label: "Button style",
40986
+ createButtonVariantField({
39950
40987
  description: "Choose a button style from your theme",
39951
- required: false,
39952
- multiple: false,
39953
- defaultValue: "primary",
39954
- options: [
40988
+ variants: [
39955
40989
  {
39956
40990
  value: "primary",
39957
40991
  label: "Primary"
@@ -39964,9 +40998,8 @@ const courseRegistrationBlockDefinition = {
39964
40998
  value: "outline",
39965
40999
  label: "Outline"
39966
41000
  }
39967
- ],
39968
- ui: { intent: "style" }
39969
- }
41001
+ ]
41002
+ })
39970
41003
  ],
39971
41004
  layout: [styledSection({
39972
41005
  baseClass: "",
@@ -40046,6 +41079,7 @@ const eventDetailsManifest = createBlockManifest({
40046
41079
  titleSource: "heading",
40047
41080
  description: "Date/time, venue, map, and other dates for an event",
40048
41081
  category: "content",
41082
+ contentSource: { kind: "entries" },
40049
41083
  tags: [
40050
41084
  "events",
40051
41085
  "venue",
@@ -40251,6 +41285,7 @@ const courseDetailsBlockDefinition = {
40251
41285
  titleSource: "heading",
40252
41286
  description: "Schedule, venue, price, and enrollment status for a course",
40253
41287
  category: "content",
41288
+ contentSource: { kind: "entries" },
40254
41289
  tags: [
40255
41290
  "courses",
40256
41291
  "schedule",
@@ -40542,6 +41577,7 @@ const eventSpotlightBlockDefinition = {
40542
41577
  paletteHidden: false
40543
41578
  },
40544
41579
  category: "content",
41580
+ contentSource: { kind: "entries" },
40545
41581
  tags: [
40546
41582
  "events",
40547
41583
  "featured",
@@ -43385,6 +44421,7 @@ const eventListingBlockDefinition = {
43385
44421
  paletteHidden: true
43386
44422
  },
43387
44423
  category: "interactive",
44424
+ contentSource: { kind: "entries" },
43388
44425
  tags: [
43389
44426
  "events",
43390
44427
  "listing",
@@ -44590,6 +45627,7 @@ const eventCalendarBlockDefinition = {
44590
45627
  paletteHidden: false
44591
45628
  },
44592
45629
  category: "interactive",
45630
+ contentSource: { kind: "entries" },
44593
45631
  tags: [
44594
45632
  "events",
44595
45633
  "calendar",
@@ -44987,6 +46025,7 @@ const recurringScheduleBlockDefinition = {
44987
46025
  titleSource: "heading",
44988
46026
  description: "A compact weekly schedule with one row per recurring series.",
44989
46027
  category: "interactive",
46028
+ contentSource: { kind: "entries" },
44990
46029
  icon: "CalendarDays",
44991
46030
  tags: [
44992
46031
  "schedule",
@@ -45617,7 +46656,10 @@ const offeringsPreviewFields = [
45617
46656
  label: "Link"
45618
46657
  }
45619
46658
  ],
45620
- ui: { intent: "display" }
46659
+ ui: {
46660
+ intent: "display",
46661
+ widget: "buttonVariant"
46662
+ }
45621
46663
  },
45622
46664
  {
45623
46665
  id: "buttonText",
@@ -45670,6 +46712,7 @@ const offeringsPreviewManifest = createBlockManifest({
45670
46712
  paletteHidden: false
45671
46713
  },
45672
46714
  category: "content",
46715
+ contentSource: { kind: "mixed" },
45673
46716
  tags: [
45674
46717
  "offerings",
45675
46718
  "events",
@@ -45838,6 +46881,7 @@ const embedManifest = createBlockManifest({
45838
46881
  id: "block.embed",
45839
46882
  title: "Embed Content",
45840
46883
  category: "content",
46884
+ contentSource: { kind: "entries" },
45841
46885
  titleSource: "heading",
45842
46886
  additionalFields: [
45843
46887
  parseFieldDefinition({
@@ -46325,6 +47369,7 @@ const imageGalleryBlockDefinition = { manifest: createBlockManifest({
46325
47369
  id: "block.image-gallery",
46326
47370
  title: "Image Gallery",
46327
47371
  category: "content",
47372
+ contentSource: { kind: "inline" },
46328
47373
  previewCustomize: { quickFieldPriority: [
46329
47374
  "aspectRatio",
46330
47375
  "slidesToShow",
@@ -46977,6 +48022,7 @@ const videoGridBlockDefinition = { manifest: createBlockManifest({
46977
48022
  id: "block.video-grid",
46978
48023
  title: "Video Grid",
46979
48024
  category: "media",
48025
+ contentSource: { kind: "inline" },
46980
48026
  skipSectionStyles: true,
46981
48027
  additionalFields: parseFieldDefinitions([
46982
48028
  {
@@ -47147,6 +48193,7 @@ const audioPlayerBlockDefinition = { manifest: createBlockManifest({
47147
48193
  title: "Audio Player",
47148
48194
  titleSource: "title",
47149
48195
  category: "media",
48196
+ contentSource: { kind: "inline" },
47150
48197
  description: "Add a branded audio player for a single track.",
47151
48198
  tags: [
47152
48199
  "audio",
@@ -47258,6 +48305,7 @@ const fileDownloadBlockDefinition = { manifest: createBlockManifest({
47258
48305
  title: "File Download",
47259
48306
  titleSource: ["title", "file.filename"],
47260
48307
  category: "media",
48308
+ contentSource: { kind: "inline" },
47261
48309
  description: "Add a simple public download link for a document, ZIP, or audio file.",
47262
48310
  tags: [
47263
48311
  "file",
@@ -48277,6 +49325,7 @@ const shopBlockDefinition = {
48277
49325
  paletteHidden: false
48278
49326
  },
48279
49327
  category: "interactive",
49328
+ contentSource: { kind: "entries" },
48280
49329
  tags: [
48281
49330
  "shop",
48282
49331
  "passes",
@@ -48476,6 +49525,7 @@ const productListBlockDefinition = {
48476
49525
  paletteHidden: false
48477
49526
  },
48478
49527
  category: "interactive",
49528
+ contentSource: { kind: "entries" },
48479
49529
  tags: [
48480
49530
  "shop",
48481
49531
  "products",
@@ -48617,6 +49667,7 @@ const productDetailBlockDefinition = {
48617
49667
  paletteHidden: false
48618
49668
  },
48619
49669
  category: "interactive",
49670
+ contentSource: { kind: "entries" },
48620
49671
  tags: [
48621
49672
  "shop",
48622
49673
  "product",
@@ -48733,6 +49784,7 @@ const cartBlockDefinition = {
48733
49784
  paletteHidden: false
48734
49785
  },
48735
49786
  category: "interactive",
49787
+ contentSource: { kind: "inline" },
48736
49788
  tags: [
48737
49789
  "shop",
48738
49790
  "cart",
@@ -48815,6 +49867,7 @@ const checkoutBlockDefinition = {
48815
49867
  paletteHidden: false
48816
49868
  },
48817
49869
  category: "interactive",
49870
+ contentSource: { kind: "inline" },
48818
49871
  tags: [
48819
49872
  "shop",
48820
49873
  "checkout",
@@ -49029,6 +50082,7 @@ const giftingBlockDefinition = {
49029
50082
  paletteHidden: false
49030
50083
  },
49031
50084
  category: "interactive",
50085
+ contentSource: { kind: "entries" },
49032
50086
  tags: [
49033
50087
  "gifting",
49034
50088
  "gift cards",
@@ -49401,6 +50455,7 @@ const newsletterSignupBlockDefinition = {
49401
50455
  titleSource: "title",
49402
50456
  description: "Newsletter subscription form with configurable field presets.",
49403
50457
  category: "interactive",
50458
+ contentSource: { kind: "inline" },
49404
50459
  fragments: [{
49405
50460
  fragment: newsletterCopyFragment,
49406
50461
  fieldPriority: 0
@@ -49582,6 +50637,7 @@ const teamMembersBlockDefinition = {
49582
50637
  id: "block.team-members",
49583
50638
  title: "Team Members",
49584
50639
  category: "content",
50640
+ contentSource: { kind: "mixed" },
49585
50641
  titleSource: "heading",
49586
50642
  additionalFields: [
49587
50643
  parseFieldDefinition({
@@ -50022,6 +51078,7 @@ const locationMapBlockDefinition = {
50022
51078
  titleSource: "heading",
50023
51079
  description: "Show a business location with an interactive map and directions link.",
50024
51080
  category: "content",
51081
+ contentSource: { kind: "inline" },
50025
51082
  tags: [
50026
51083
  "map",
50027
51084
  "location",
@@ -51028,213 +52085,6 @@ function getBlockDefinition(name) {
51028
52085
  return blockStore.get(name);
51029
52086
  }
51030
52087
  //#endregion
51031
- //#region ../theme-core/src/gradients/presets.ts
51032
- /**
51033
- * Curated library of gradient presets
51034
- * All gradients use theme color tokens, so they adapt to the site's palette
51035
- */
51036
- const GRADIENT_PRESETS = [
51037
- {
51038
- id: "primary-secondary",
51039
- name: "Primary to Secondary",
51040
- description: "Blend your primary and secondary brand colors",
51041
- type: "linear",
51042
- angle: 135,
51043
- stops: [{
51044
- color: "primary-500",
51045
- position: 0
51046
- }, {
51047
- color: "secondary-500",
51048
- position: 100
51049
- }]
51050
- },
51051
- {
51052
- id: "primary-accent",
51053
- name: "Primary to Accent",
51054
- description: "Energetic gradient from primary to accent",
51055
- type: "linear",
51056
- angle: 135,
51057
- stops: [{
51058
- color: "primary-500",
51059
- position: 0
51060
- }, {
51061
- color: "accent-500",
51062
- position: 100
51063
- }]
51064
- },
51065
- {
51066
- id: "secondary-accent",
51067
- name: "Secondary to Accent",
51068
- description: "Vibrant secondary to accent blend",
51069
- type: "linear",
51070
- angle: 135,
51071
- stops: [{
51072
- color: "secondary-500",
51073
- position: 0
51074
- }, {
51075
- color: "accent-500",
51076
- position: 100
51077
- }]
51078
- },
51079
- {
51080
- id: "primary-light-dark",
51081
- name: "Primary Light to Dark",
51082
- description: "Subtle primary color fade",
51083
- type: "linear",
51084
- angle: 180,
51085
- stops: [{
51086
- color: "primary-500",
51087
- position: 0
51088
- }, {
51089
- color: "primary-900",
51090
- position: 100
51091
- }]
51092
- },
51093
- {
51094
- id: "primary-dark-light",
51095
- name: "Primary Dark to Light",
51096
- description: "Reverse primary fade",
51097
- type: "linear",
51098
- angle: 180,
51099
- stops: [{
51100
- color: "primary-900",
51101
- position: 0
51102
- }, {
51103
- color: "primary-50",
51104
- position: 100
51105
- }]
51106
- },
51107
- {
51108
- id: "secondary-light-dark",
51109
- name: "Secondary Light to Dark",
51110
- description: "Subtle secondary color fade",
51111
- type: "linear",
51112
- angle: 180,
51113
- stops: [{
51114
- color: "secondary-500",
51115
- position: 0
51116
- }, {
51117
- color: "secondary-900",
51118
- position: 100
51119
- }]
51120
- },
51121
- {
51122
- id: "accent-light-dark",
51123
- name: "Accent Light to Dark",
51124
- description: "Accent color depth fade",
51125
- type: "linear",
51126
- angle: 180,
51127
- stops: [{
51128
- color: "accent-500",
51129
- position: 0
51130
- }, {
51131
- color: "accent-900",
51132
- position: 100
51133
- }]
51134
- },
51135
- {
51136
- id: "background-surface",
51137
- name: "Background to Surface",
51138
- description: "Subtle depth using base colors",
51139
- type: "linear",
51140
- angle: 180,
51141
- stops: [{
51142
- color: "background",
51143
- position: 0
51144
- }, {
51145
- color: "surface",
51146
- position: 100
51147
- }]
51148
- },
51149
- {
51150
- id: "neutral-fade",
51151
- name: "Neutral Gradient",
51152
- description: "Elegant neutral color fade",
51153
- type: "linear",
51154
- angle: 180,
51155
- stops: [{
51156
- color: "neutral-900",
51157
- position: 0
51158
- }, {
51159
- color: "neutral-50",
51160
- position: 100
51161
- }]
51162
- },
51163
- {
51164
- id: "primary-radial",
51165
- name: "Primary Spotlight",
51166
- description: "Radial primary gradient from center",
51167
- type: "radial",
51168
- position: "center",
51169
- stops: [{
51170
- color: "primary-500",
51171
- position: 0
51172
- }, {
51173
- color: "primary-900",
51174
- position: 100
51175
- }]
51176
- },
51177
- {
51178
- id: "accent-radial",
51179
- name: "Accent Spotlight",
51180
- description: "Radial accent gradient from center",
51181
- type: "radial",
51182
- position: "center",
51183
- stops: [{
51184
- color: "accent-500",
51185
- position: 0
51186
- }, {
51187
- color: "accent-900",
51188
- position: 100
51189
- }]
51190
- },
51191
- {
51192
- id: "primary-secondary-accent",
51193
- name: "Brand Trio",
51194
- description: "All three brand colors in harmony",
51195
- type: "linear",
51196
- angle: 135,
51197
- stops: [
51198
- {
51199
- color: "primary-500",
51200
- position: 0
51201
- },
51202
- {
51203
- color: "secondary-500",
51204
- position: 50
51205
- },
51206
- {
51207
- color: "accent-500",
51208
- position: 100
51209
- }
51210
- ]
51211
- }
51212
- ];
51213
- //#endregion
51214
- //#region ../theme-core/src/gradients/utils.ts
51215
- /**
51216
- * Convert a GradientConfig to a CSS gradient string
51217
- *
51218
- * @param config - The gradient configuration using theme tokens
51219
- * @param tokens - Map of theme color tokens to hex values
51220
- * @returns CSS gradient string (e.g., "linear-gradient(180deg, #3b82f6 0%, #f59e0b 100%)")
51221
- */
51222
- function gradientToCss(config, tokens) {
51223
- const stops = config.stops.map((stop) => {
51224
- return `${tokens[stop.color] ?? "#000000"} ${stop.position}%`;
51225
- }).join(", ");
51226
- const type = config.type ?? "linear";
51227
- if (type === "radial") {
51228
- const position = config.position ?? "center";
51229
- return `radial-gradient(${config.shape ?? "circle"} at ${position}, ${stops})`;
51230
- }
51231
- if (type === "conic") {
51232
- const position = config.position ?? "center";
51233
- return `conic-gradient(from ${config.angle ?? 0}deg at ${position}, ${stops})`;
51234
- }
51235
- return `linear-gradient(${config.angle ?? 180}deg, ${stops})`;
51236
- }
51237
- //#endregion
51238
52088
  //#region ../blocks/src/system/helpers/backgroundMedia.ts
51239
52089
  function resolveBackgroundMedia(input) {
51240
52090
  const parsedMedia = mediaSchema.safeParse(input.image);
@@ -51272,9 +52122,9 @@ function appendBackgroundMediaClasses(baseClassName, input) {
51272
52122
  ].filter(Boolean).join(" ");
51273
52123
  }
51274
52124
  function gradientPresetCss(value) {
51275
- if (!isString$1(value)) return null;
51276
- const preset = GRADIENT_PRESETS.find((candidate) => candidate.id === value.trim());
51277
- if (!preset) return null;
52125
+ const presetId = parseGradientPresetId(value);
52126
+ if (!presetId) return null;
52127
+ const preset = getGradientPreset(presetId);
51278
52128
  return gradientToCss(preset, Object.fromEntries(preset.stops.map((stop) => [stop.color, `rgb(var(--tb-${stop.color}))`])));
51279
52129
  }
51280
52130
  const backgroundTransforms = [
@@ -60366,7 +61216,7 @@ function readFromSource$1(source, segments) {
60366
61216
  /**
60367
61217
  * Type guard for plain objects.
60368
61218
  */
60369
- function isPlainObject$2(value) {
61219
+ function isPlainObject$3(value) {
60370
61220
  return typeof value === "object" && value !== null && !Array.isArray(value);
60371
61221
  }
60372
61222
  /**
@@ -60375,18 +61225,18 @@ function isPlainObject$2(value) {
60375
61225
  function parseRouteEntry(routeId, value) {
60376
61226
  if (typeof routeId !== "string" || routeId.length === 0) return null;
60377
61227
  if (typeof value === "string") return [routeId, value];
60378
- if (isPlainObject$2(value)) return [routeId, value];
61228
+ if (isPlainObject$3(value)) return [routeId, value];
60379
61229
  return null;
60380
61230
  }
60381
61231
  /**
60382
61232
  * Extracts route map from $root.routes in the viewModel.
60383
61233
  */
60384
61234
  function extractRouteMapFromViewModel(viewModel) {
60385
- if (!isPlainObject$2(viewModel)) return void 0;
61235
+ if (!isPlainObject$3(viewModel)) return void 0;
60386
61236
  const root = viewModel.$root;
60387
- if (!isPlainObject$2(root)) return void 0;
61237
+ if (!isPlainObject$3(root)) return void 0;
60388
61238
  const routes = root.routes;
60389
- if (!isPlainObject$2(routes)) return void 0;
61239
+ if (!isPlainObject$3(routes)) return void 0;
60390
61240
  const entries = Object.entries(routes).map(([routeId, value]) => parseRouteEntry(routeId, value)).filter((entry) => entry !== null);
60391
61241
  if (entries.length === 0) return void 0;
60392
61242
  return Object.fromEntries(entries);
@@ -60395,22 +61245,22 @@ function extractRouteMapFromViewModel(viewModel) {
60395
61245
  * Extracts pagesByIdentifier from $root.pagesByIdentifier in the viewModel.
60396
61246
  */
60397
61247
  function extractPagesByIdentifierFromViewModel(viewModel) {
60398
- if (!isPlainObject$2(viewModel)) return void 0;
61248
+ if (!isPlainObject$3(viewModel)) return void 0;
60399
61249
  const root = viewModel.$root;
60400
- if (!isPlainObject$2(root)) return void 0;
61250
+ if (!isPlainObject$3(root)) return void 0;
60401
61251
  const pagesByIdentifier = root.pagesByIdentifier;
60402
- if (!isPlainObject$2(pagesByIdentifier)) return void 0;
61252
+ if (!isPlainObject$3(pagesByIdentifier)) return void 0;
60403
61253
  return pagesByIdentifier;
60404
61254
  }
60405
61255
  /**
60406
61256
  * Extracts entriesByIdentifier from $root.entriesByIdentifier in the viewModel.
60407
61257
  */
60408
61258
  function extractEntriesByIdentifierFromViewModel(viewModel) {
60409
- if (!isPlainObject$2(viewModel)) return void 0;
61259
+ if (!isPlainObject$3(viewModel)) return void 0;
60410
61260
  const root = viewModel.$root;
60411
- if (!isPlainObject$2(root)) return void 0;
61261
+ if (!isPlainObject$3(root)) return void 0;
60412
61262
  const entriesByIdentifier = root.entriesByIdentifier;
60413
- if (!isPlainObject$2(entriesByIdentifier)) return void 0;
61263
+ if (!isPlainObject$3(entriesByIdentifier)) return void 0;
60414
61264
  return entriesByIdentifier;
60415
61265
  }
60416
61266
  /**
@@ -60432,7 +61282,7 @@ function applyTransformsAndFallback(value, binding, context) {
60432
61282
  const routeMap = context.routes ?? extractRouteMapFromViewModel(context.viewModel);
60433
61283
  const pagesByIdentifier = context.pagesByIdentifier ?? extractPagesByIdentifierFromViewModel(context.viewModel);
60434
61284
  const entriesByIdentifier = context.entriesByIdentifier ?? extractEntriesByIdentifierFromViewModel(context.viewModel);
60435
- const stage = (isPlainObject$2(context.viewModel.$root) ? context.viewModel.$root : null)?.previewStage === "preview" ? "preview" : "published";
61285
+ const stage = (isPlainObject$3(context.viewModel.$root) ? context.viewModel.$root : null)?.previewStage === "preview" ? "preview" : "published";
60436
61286
  const transformed = runTransforms(value, binding.transforms?.map((step) => ({
60437
61287
  id: step.id,
60438
61288
  options: step.options
@@ -60461,15 +61311,15 @@ function resolveTransformData(context, bindingPath) {
60461
61311
  const [first] = splitPath$1(bindingPath);
60462
61312
  if (typeof first === "string" && first !== "$root") {
60463
61313
  const namedScope = context.scopes.find((entry) => entry.name === first);
60464
- if (namedScope && isPlainObject$2(namedScope.value)) return namedScope.value;
61314
+ if (namedScope && isPlainObject$3(namedScope.value)) return namedScope.value;
60465
61315
  }
60466
61316
  for (let i = context.scopes.length - 1; i >= 0; i -= 1) {
60467
61317
  const scope = context.scopes[i];
60468
- if (scope && isPlainObject$2(scope.value)) return scope.value;
61318
+ if (scope && isPlainObject$3(scope.value)) return scope.value;
60469
61319
  }
60470
61320
  const content = context.viewModel.content;
60471
- if (isPlainObject$2(content)) return content;
60472
- if (isPlainObject$2(context.viewModel)) return context.viewModel;
61321
+ if (isPlainObject$3(content)) return content;
61322
+ if (isPlainObject$3(context.viewModel)) return context.viewModel;
60473
61323
  }
60474
61324
  function coerceBindingValue(value, binding) {
60475
61325
  const mode = binding.coerce;
@@ -60531,7 +61381,7 @@ function isRichTextConditionValueEmpty(value) {
60531
61381
  return richTextStructuralPresence(root).kind === "empty";
60532
61382
  }
60533
61383
  function isTipTapNode(value) {
60534
- return isPlainObject$2(value) && typeof value.type === "string";
61384
+ return isPlainObject$3(value) && typeof value.type === "string";
60535
61385
  }
60536
61386
  function richTextStructuralPresence(node) {
60537
61387
  if (typeof node.text === "string" && node.text.trim().length > 0) return {
@@ -60560,7 +61410,7 @@ function richTextSelfPresence(node) {
60560
61410
  }
60561
61411
  }
60562
61412
  function hasResolvableRichTextImageSource(node) {
60563
- const attrs = isPlainObject$2(node.attrs) ? node.attrs : {};
61413
+ const attrs = isPlainObject$3(node.attrs) ? node.attrs : {};
60564
61414
  return hasNonBlankStringAttr(attrs, "src") || hasNonBlankStringAttr(attrs, "identifier");
60565
61415
  }
60566
61416
  function hasNonBlankStringAttr(attrs, key) {
@@ -60752,7 +61602,7 @@ function getNestedBinding(bindings, path) {
60752
61602
  const segments = path.split(".");
60753
61603
  let current = bindings;
60754
61604
  for (const segment of segments) {
60755
- if (!isPlainObject$2(current)) return void 0;
61605
+ if (!isPlainObject$3(current)) return void 0;
60756
61606
  current = current[segment];
60757
61607
  }
60758
61608
  return current;
@@ -60775,7 +61625,7 @@ function resolveBindingToEditorPath(fieldName, bindings, editableRouteMetadataFi
60775
61625
  };
60776
61626
  const binding = bindings[fieldName] ?? getNestedBinding(bindings, fieldName);
60777
61627
  if (!binding) return null;
60778
- if (isPlainObject$2(binding) && "source" in binding) {
61628
+ if (isPlainObject$3(binding) && "source" in binding) {
60779
61629
  const source = binding.source;
60780
61630
  if (source === "field" && "path" in binding) {
60781
61631
  const path = binding.path;
@@ -60792,7 +61642,7 @@ function resolveBindingToEditorPath(fieldName, bindings, editableRouteMetadataFi
60792
61642
  if (!Array.isArray(options)) return null;
60793
61643
  for (const option of options) {
60794
61644
  if (typeof option === "string") return `content.${option}`;
60795
- if (isPlainObject$2(option) && "source" in option) {
61645
+ if (isPlainObject$3(option) && "source" in option) {
60796
61646
  const optSource = option.source;
60797
61647
  if (optSource === "field" && "path" in option) {
60798
61648
  const path = option.path;
@@ -60844,7 +61694,7 @@ function derivePreviewTextEditableTarget(parsedBinding, context) {
60844
61694
  };
60845
61695
  }
60846
61696
  const root = context.viewModel?.$root;
60847
- if (isPlainObject$2(root) && root.contentEntry != null && context.pathBase) return {
61697
+ if (isPlainObject$3(root) && root.contentEntry != null && context.pathBase) return {
60848
61698
  kind: "read-only",
60849
61699
  reason: "entry-template-block-field"
60850
61700
  };
@@ -60903,7 +61753,7 @@ function injectMediaSlotPath(props, node, parsedBinding, context) {
60903
61753
  if (resolveBindingToEditorPath(fieldPath, context.blockBindings, context.editableRouteMetadataFieldIds)) return props;
60904
61754
  }
60905
61755
  const root = context.viewModel?.$root;
60906
- if (isPlainObject$2(root) && root.contentEntry != null && context.pathBase) return props;
61756
+ if (isPlainObject$3(root) && root.contentEntry != null && context.pathBase) return props;
60907
61757
  return {
60908
61758
  ...props,
60909
61759
  fieldPath
@@ -61050,7 +61900,7 @@ function getNodeProps(node) {
61050
61900
  if (key.startsWith("$")) continue;
61051
61901
  legacy[key] = value;
61052
61902
  }
61053
- const props = isPlainObject$2(node.props) ? node.props : {};
61903
+ const props = isPlainObject$3(node.props) ? node.props : {};
61054
61904
  return {
61055
61905
  ...legacy,
61056
61906
  ...props
@@ -61162,7 +62012,7 @@ function generateHoverEffects(axes, _space) {
61162
62012
  //#endregion
61163
62013
  //#region ../theme-core/src/blockOverrides.ts
61164
62014
  const BLOCK_PREFIX = "block.";
61165
- function isPlainObject$1(value) {
62015
+ function isPlainObject$2(value) {
61166
62016
  return typeof value === "object" && value !== null && !Array.isArray(value);
61167
62017
  }
61168
62018
  function isUnknownArray$1(value) {
@@ -61170,7 +62020,7 @@ function isUnknownArray$1(value) {
61170
62020
  }
61171
62021
  function cloneValue(value) {
61172
62022
  if (isUnknownArray$1(value)) return value.map((item) => cloneValue(item));
61173
- if (isPlainObject$1(value)) {
62023
+ if (isPlainObject$2(value)) {
61174
62024
  const cloned = {};
61175
62025
  for (const [key, child] of Object.entries(value)) cloned[key] = cloneValue(child);
61176
62026
  return cloned;
@@ -61180,7 +62030,7 @@ function cloneValue(value) {
61180
62030
  function mergeDeep(defaultValue, overrideValue) {
61181
62031
  if (overrideValue === void 0) return cloneValue(defaultValue);
61182
62032
  if (overrideValue === null) return null;
61183
- if (isPlainObject$1(defaultValue) && isPlainObject$1(overrideValue)) {
62033
+ if (isPlainObject$2(defaultValue) && isPlainObject$2(overrideValue)) {
61184
62034
  const merged = {};
61185
62035
  const keys = /* @__PURE__ */ new Set([...Object.keys(defaultValue), ...Object.keys(overrideValue)]);
61186
62036
  for (const key of keys) merged[key] = mergeDeep(defaultValue[key], overrideValue[key]);
@@ -61237,7 +62087,7 @@ function mergeContentWithBlockStyleOverrides(content, theme, blockKind) {
61237
62087
  */
61238
62088
  function applyHeadingDecorationSignature(target, themeDecoration) {
61239
62089
  const existing = target["_sectionStyles"];
61240
- const existingObj = isPlainObject$1(existing) ? existing : void 0;
62090
+ const existingObj = isPlainObject$2(existing) ? existing : void 0;
61241
62091
  const rawOverride = existingObj?.["headingDecorationOverride"];
61242
62092
  const effective = resolveEffectiveHeadingDecoration(themeDecoration, rawOverride === "site-default" || rawOverride === "off" ? rawOverride : void 0);
61243
62093
  if (effective.kind === "none") {
@@ -64764,6 +65614,8 @@ function buildThemeCssVars(theme) {
64764
65614
  const inputRadius = mapInputRadius(inputSystem.base.corners, theme.corners);
64765
65615
  return {
64766
65616
  ...generateCssVars(theme.palette),
65617
+ ["--tb-secondaryForeground"]: "var(--tb-onSecondary)",
65618
+ ["--tb-accentForeground"]: "var(--tb-onAccent)",
64767
65619
  ["--tb-scrim"]: "0 0 0",
64768
65620
  ["--tb-shadow"]: "0 0 0",
64769
65621
  ...generateShadowCssVars(theme),
@@ -73425,6 +74277,11 @@ const containerResponsiveThemeCss = `/*
73425
74277
  flex-wrap: wrap;
73426
74278
  }
73427
74279
 
74280
+ /* Text-style CTA actions belong to the section foreground contract. */
74281
+ :where(.theme-scope) [data-block="cta-full"] .cta-row .button-link {
74282
+ color: var(--rb-surface-link-color, var(--rb-surface-text-color, rgb(var(--tb-primary))));
74283
+ }
74284
+
73428
74285
  /* =============================================================================
73429
74286
  * Block: Body Text (content block, Phase 2)
73430
74287
  *
@@ -79039,35 +79896,35 @@ z$1.object({
79039
79896
  //#endregion
79040
79897
  //#region ../blocks/src/system/runtime/bindings.ts
79041
79898
  const shouldLogWarnings = isNonProductionEnv();
79042
- function isPlainObject(value) {
79899
+ function isPlainObject$1(value) {
79043
79900
  return typeof value === "object" && value !== null && !Array.isArray(value);
79044
79901
  }
79045
79902
  function isRouteMapEntry(value) {
79046
79903
  if (typeof value === "string") return true;
79047
- return isPlainObject(value);
79904
+ return isPlainObject$1(value);
79048
79905
  }
79049
79906
  function isRouteMap(value) {
79050
- if (!isPlainObject(value)) return false;
79907
+ if (!isPlainObject$1(value)) return false;
79051
79908
  return Object.values(value).every(isRouteMapEntry);
79052
79909
  }
79053
79910
  function isPageIdentifierMap(value) {
79054
- if (!isPlainObject(value)) return false;
79911
+ if (!isPlainObject$1(value)) return false;
79055
79912
  return Object.values(value).every((v) => typeof v === "string");
79056
79913
  }
79057
79914
  function isEntryIdentifierMap(value) {
79058
- if (!isPlainObject(value)) return false;
79915
+ if (!isPlainObject$1(value)) return false;
79059
79916
  for (const v of Object.values(value)) {
79060
- if (!isPlainObject(v)) return false;
79917
+ if (!isPlainObject$1(v)) return false;
79061
79918
  if (!Object.values(v).every((inner) => typeof inner === "string")) return false;
79062
79919
  }
79063
79920
  return true;
79064
79921
  }
79065
79922
  function buildTransformContext(vm) {
79066
79923
  const root = vm.$root;
79067
- const stage = root && isPlainObject(root) && root.previewStage === "preview" ? "preview" : "published";
79068
- const routes = root && isPlainObject(root) && isRouteMap(root.routes) ? root.routes : void 0;
79069
- const pagesByIdentifier = root && isPlainObject(root) && isPageIdentifierMap(root.pagesByIdentifier) ? root.pagesByIdentifier : void 0;
79070
- const entriesByIdentifier = root && isPlainObject(root) && isEntryIdentifierMap(root.entriesByIdentifier) ? root.entriesByIdentifier : void 0;
79924
+ const stage = root && isPlainObject$1(root) && root.previewStage === "preview" ? "preview" : "published";
79925
+ const routes = root && isPlainObject$1(root) && isRouteMap(root.routes) ? root.routes : void 0;
79926
+ const pagesByIdentifier = root && isPlainObject$1(root) && isPageIdentifierMap(root.pagesByIdentifier) ? root.pagesByIdentifier : void 0;
79927
+ const entriesByIdentifier = root && isPlainObject$1(root) && isEntryIdentifierMap(root.entriesByIdentifier) ? root.entriesByIdentifier : void 0;
79071
79928
  return {
79072
79929
  theme: defaultThemeTokens,
79073
79930
  stage,
@@ -79461,8 +80318,6 @@ function parseBackgroundColorToken(value) {
79461
80318
  if (!token) return null;
79462
80319
  return token ? token : null;
79463
80320
  }
79464
- const gradientPresetIds = GRADIENT_PRESETS.map((preset) => preset.id);
79465
- new Set(gradientPresetIds);
79466
80321
  //#endregion
79467
80322
  //#region ../api/src/sdkConfig/contracts.ts
79468
80323
  /**
@@ -80149,6 +81004,7 @@ const CANONICAL_REFUND_PLANNER_ERROR_CODES = [
80149
81004
  "already_refunded",
80150
81005
  "idempotency_amount_mismatch",
80151
81006
  "idempotency_method_mismatch",
81007
+ "idempotency_receipt_mismatch",
80152
81008
  "refund_in_progress"
80153
81009
  ],
80154
81010
  "invalid_amount",
@@ -80161,7 +81017,12 @@ const CANONICAL_ADMIN_REFUND_FAILURE_CODES = [
80161
81017
  "payment_intent_not_found",
80162
81018
  "stripe_error",
80163
81019
  "stale_pending_missing_context",
80164
- "non_refundable_deposit_remaining"
81020
+ "non_refundable_deposit_remaining",
81021
+ "deposit_refund_override_required",
81022
+ "refund_revoke_requires_full_line_refund",
81023
+ "payment_receipt_not_found",
81024
+ "payment_receipt_not_refundable",
81025
+ "payment_receipt_attribution_required"
80165
81026
  ];
80166
81027
  [...CANONICAL_REFUND_PLANNER_ERROR_CODES];
80167
81028
  //#endregion
@@ -80174,7 +81035,10 @@ const STANDARD_LINE_ITEM_REFUND_INVALID_CODES = [
80174
81035
  "missing_credit_customer",
80175
81036
  "deposit_refund_override_required",
80176
81037
  "refund_revoke_requires_full_line_refund",
80177
- "unified_order_link_required"
81038
+ "unified_order_link_required",
81039
+ "payment_receipt_not_found",
81040
+ "payment_receipt_not_refundable",
81041
+ "payment_receipt_attribution_required"
80178
81042
  ];
80179
81043
  const EVENT_ATTENDEE_REFUND_INVALID_CODES = ["manual_booking_not_refundable", ...STANDARD_LINE_ITEM_REFUND_INVALID_CODES];
80180
81044
  [...CANONICAL_ADMIN_REFUND_FAILURE_CODES];
@@ -82744,6 +83608,225 @@ function isDisplayMediaKind(value) {
82744
83608
  function isFileFieldKind(value) {
82745
83609
  return FILE_FIELD_KINDS.includes(value);
82746
83610
  }
83611
+ /** Matches the closed sentinel shape `"<REQUIRED: …>"`. */
83612
+ const SENTINEL_PATTERN = /^<REQUIRED:.*>$/;
83613
+ /**
83614
+ * True when a value is an unfilled placeholder sentinel. Shared by the emitter
83615
+ * (self-check) and the validator (rejection) so recognition can't drift.
83616
+ */
83617
+ function isPlaceholderSentinel(value) {
83618
+ return typeof value === "string" && SENTINEL_PATTERN.test(value);
83619
+ }
83620
+ function effectiveReferenceKind(field) {
83621
+ const kind = field.referenceKind;
83622
+ const legacy = field.reference?.type;
83623
+ return kind ?? legacy;
83624
+ }
83625
+ function selectOptionValues$1(field) {
83626
+ const options = field.options;
83627
+ return options ? options.map((o) => o.value) : [];
83628
+ }
83629
+ /**
83630
+ * The placeholder sentinel for a leaf field. Container types (group/button/
83631
+ * repeater) do not have a leaf sentinel — they expand structurally — and calling
83632
+ * this for them returns a generic marker only for completeness.
83633
+ */
83634
+ function buildSentinel(field) {
83635
+ switch (field.type) {
83636
+ case "text":
83637
+ case "textarea": return "<REQUIRED: text>";
83638
+ case "richText": return "<REQUIRED: rich-text>";
83639
+ case "number": return "<REQUIRED: number>";
83640
+ case "date": return "<REQUIRED: date (YYYY-MM-DD)>";
83641
+ case "datetime": return "<REQUIRED: datetime (ISO 8601)>";
83642
+ case "url": return "<REQUIRED: url>";
83643
+ case "link": return "<REQUIRED: link>";
83644
+ case "media": return "<REQUIRED: media-identifier>";
83645
+ case "file": return "<REQUIRED: file-identifier>";
83646
+ case "boolean": return "<REQUIRED: boolean>";
83647
+ case "select": {
83648
+ const values = selectOptionValues$1(field);
83649
+ return values.length > 0 ? `<REQUIRED: select (${values.join("|")})>` : "<REQUIRED: select>";
83650
+ }
83651
+ case "reference": {
83652
+ const kind = effectiveReferenceKind(field);
83653
+ return kind ? `<REQUIRED: reference-identifier (${kind})>` : "<REQUIRED: reference-identifier>";
83654
+ }
83655
+ case "group": return "<REQUIRED: group>";
83656
+ case "button": return "<REQUIRED: button>";
83657
+ case "repeater": return "<REQUIRED: repeater>";
83658
+ }
83659
+ }
83660
+ //#endregion
83661
+ //#region ../content-model/src/content-types/entryValidation.ts
83662
+ function subfieldsOf$1(field) {
83663
+ const direct = field.fields;
83664
+ if (direct && direct.length > 0) return direct;
83665
+ return field.schema?.fields ?? [];
83666
+ }
83667
+ function selectOptionValues(field) {
83668
+ const options = field.options;
83669
+ return options ? options.map((o) => o.value) : [];
83670
+ }
83671
+ function isPlainObject(value) {
83672
+ return value !== null && typeof value === "object" && !Array.isArray(value);
83673
+ }
83674
+ /** Field types whose value must be a string. */
83675
+ const STRING_TYPES = /* @__PURE__ */ new Set([
83676
+ "text",
83677
+ "textarea",
83678
+ "url",
83679
+ "link",
83680
+ "media",
83681
+ "file",
83682
+ "reference",
83683
+ "date",
83684
+ "datetime",
83685
+ "select"
83686
+ ]);
83687
+ function checkLeafType(field, value, path, errors) {
83688
+ const push = (code, message) => {
83689
+ errors.push({
83690
+ path,
83691
+ fieldType: field.type,
83692
+ code,
83693
+ message
83694
+ });
83695
+ };
83696
+ if (field.type === "number") {
83697
+ if (typeof value !== "number" || Number.isNaN(value)) push("wrong_type", `"${path}" must be a number.`);
83698
+ return;
83699
+ }
83700
+ if (field.type === "boolean") {
83701
+ if (typeof value !== "boolean") push("wrong_type", `"${path}" must be a boolean.`);
83702
+ return;
83703
+ }
83704
+ if (field.type === "richText") {
83705
+ if (typeof value !== "string" && !isPlainObject(value)) push("wrong_type", `"${path}" must be rich-text (string or document object).`);
83706
+ return;
83707
+ }
83708
+ if (field.type === "select") {
83709
+ if (typeof value !== "string") {
83710
+ push("wrong_type", `"${path}" must be a string.`);
83711
+ return;
83712
+ }
83713
+ const options = selectOptionValues(field);
83714
+ if (options.length > 0 && !options.includes(value)) push("invalid_option", `"${path}" must be one of: ${options.join(", ")}.`);
83715
+ return;
83716
+ }
83717
+ if (STRING_TYPES.has(field.type)) {
83718
+ if (typeof value !== "string") push("wrong_type", `"${path}" must be a string.`);
83719
+ }
83720
+ }
83721
+ function validateField(field, value, path, errors) {
83722
+ if (value === void 0 || value === null || value === "") {
83723
+ if (field.required === true) errors.push({
83724
+ path,
83725
+ fieldType: field.type,
83726
+ code: "missing_required",
83727
+ message: `Required field "${path}" is missing.`
83728
+ });
83729
+ return;
83730
+ }
83731
+ if (isPlaceholderSentinel(value)) {
83732
+ errors.push({
83733
+ path,
83734
+ fieldType: field.type,
83735
+ code: "unfilled_placeholder",
83736
+ message: `"${path}" still contains an unfilled placeholder (${value}).`
83737
+ });
83738
+ return;
83739
+ }
83740
+ if (field.type === "group" || field.type === "button") {
83741
+ if (!isPlainObject(value)) {
83742
+ errors.push({
83743
+ path,
83744
+ fieldType: field.type,
83745
+ code: "wrong_type",
83746
+ message: `"${path}" must be an object.`
83747
+ });
83748
+ return;
83749
+ }
83750
+ validateFields(subfieldsOf$1(field), value, path, errors);
83751
+ return;
83752
+ }
83753
+ if (field.type === "repeater") {
83754
+ if (!Array.isArray(value)) {
83755
+ errors.push({
83756
+ path,
83757
+ fieldType: field.type,
83758
+ code: "wrong_type",
83759
+ message: `"${path}" must be an array.`
83760
+ });
83761
+ return;
83762
+ }
83763
+ const itemFields = subfieldsOf$1(field);
83764
+ value.forEach((item, index) => {
83765
+ const itemPath = `${path}[${index}]`;
83766
+ if (!isPlainObject(item)) {
83767
+ errors.push({
83768
+ path: itemPath,
83769
+ fieldType: "repeater",
83770
+ code: "wrong_type",
83771
+ message: `"${itemPath}" must be an object.`
83772
+ });
83773
+ return;
83774
+ }
83775
+ validateFields(itemFields, item, itemPath, errors);
83776
+ });
83777
+ return;
83778
+ }
83779
+ checkLeafType(field, value, path, errors);
83780
+ }
83781
+ function validateFields(fields, data, pathPrefix, errors) {
83782
+ for (const field of fields) {
83783
+ const path = pathPrefix ? `${pathPrefix}.${field.id}` : field.id;
83784
+ validateField(field, data[field.id], path, errors);
83785
+ }
83786
+ }
83787
+ //#endregion
83788
+ //#region ../content-model/src/content-types/scaffold.ts
83789
+ /** Nested subfields for group/repeater/button-style fields (both DSL shapes). */
83790
+ function subfieldsOf(field) {
83791
+ const direct = field.fields;
83792
+ if (direct && direct.length > 0) return direct;
83793
+ return field.schema?.fields ?? [];
83794
+ }
83795
+ const CONTAINER_TYPES = /* @__PURE__ */ new Set([
83796
+ "group",
83797
+ "button",
83798
+ "repeater"
83799
+ ]);
83800
+ /**
83801
+ * Build the placeholder value for a required field. Group/button fields recurse
83802
+ * into their required subfields; repeaters emit a single seed item so agents see
83803
+ * the item shape.
83804
+ */
83805
+ function buildFieldValue(field) {
83806
+ if (field.type === "repeater") return [buildObjectFromFields(subfieldsOf(field))];
83807
+ if (CONTAINER_TYPES.has(field.type)) {
83808
+ const subfields = subfieldsOf(field);
83809
+ if (subfields.length > 0) return buildObjectFromFields(subfields);
83810
+ if (field.type === "button") return {
83811
+ label: "<REQUIRED: text>",
83812
+ link: "<REQUIRED: link>"
83813
+ };
83814
+ return {};
83815
+ }
83816
+ return buildSentinel(field);
83817
+ }
83818
+ /**
83819
+ * Build a template object from a list of fields, stubbing required fields and
83820
+ * omitting optional ones.
83821
+ */
83822
+ function buildObjectFromFields(fields) {
83823
+ const template = {};
83824
+ for (const field of fields) {
83825
+ if (field.required !== true) continue;
83826
+ template[field.id] = buildFieldValue(field);
83827
+ }
83828
+ return template;
83829
+ }
82747
83830
  //#endregion
82748
83831
  //#region ../content-model/src/content-types/builtins.ts
82749
83832
  /**
@@ -86184,7 +87267,7 @@ var CircuitOpenError = class extends Error {
86184
87267
  * 1. This constant
86185
87268
  * 2. The "version" field in package.json
86186
87269
  */
86187
- const SDK_VERSION = "0.108.0";
87270
+ const SDK_VERSION = "0.109.0";
86188
87271
  //#endregion
86189
87272
  //#region src/next/content-load-failure.ts
86190
87273
  const SdkContentLoadError = RiverbankContentLoadError;