@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
@@ -275,19 +275,29 @@ function isRecord$33(value) {
275
275
  //#region ../core/src/booking-date-time.ts
276
276
  const DEFAULT_LOCALE = "en-US";
277
277
  const FALLBACK_TIMEZONE = "UTC";
278
+ const DATE_TIME_FORMATTER_CACHE_LIMIT = 100;
279
+ const dateTimeFormatterCache = /* @__PURE__ */ new Map();
280
+ const timeZoneValidityCache = /* @__PURE__ */ new Map();
278
281
  function isValidTimeZone$1(timeZone) {
282
+ const cached = timeZoneValidityCache.get(timeZone);
283
+ if (cached !== void 0) return cached;
284
+ let isValid;
279
285
  try {
280
286
  new Intl.DateTimeFormat(DEFAULT_LOCALE, { timeZone }).format(/* @__PURE__ */ new Date());
281
- return true;
287
+ isValid = true;
282
288
  } catch {
283
- return false;
289
+ isValid = false;
284
290
  }
291
+ if (timeZoneValidityCache.size >= DATE_TIME_FORMATTER_CACHE_LIMIT) timeZoneValidityCache.clear();
292
+ timeZoneValidityCache.set(timeZone, isValid);
293
+ return isValid;
285
294
  }
286
295
  function getSafeTimeZone(timeZone) {
287
296
  const normalized = typeof timeZone === "string" ? timeZone.trim() : "";
288
297
  return normalized.length > 0 && isValidTimeZone$1(normalized) ? normalized : FALLBACK_TIMEZONE;
289
298
  }
290
299
  function parseStrictBookingTimeZone(timeZone) {
300
+ if (typeof timeZone !== "string") return null;
291
301
  const normalized = timeZone.trim();
292
302
  if (normalized.length === 0 || !isValidTimeZone$1(normalized)) return null;
293
303
  return normalized;
@@ -300,10 +310,19 @@ function formatParseError(error) {
300
310
  }
301
311
  }
302
312
  function formatToPartsLookup(date, locale, timeZone, format) {
303
- return new Map(new Intl.DateTimeFormat(locale, {
313
+ return new Map(getCachedDateTimeFormatter(locale, timeZone, format).formatToParts(date).map((part) => [part.type, part.value]));
314
+ }
315
+ function getCachedDateTimeFormatter(locale, timeZone, format) {
316
+ const key = `${locale}\u0000${timeZone}\u0000${JSON.stringify(Object.entries(format).sort(([left], [right]) => left.localeCompare(right)))}`;
317
+ const cached = dateTimeFormatterCache.get(key);
318
+ if (cached !== void 0) return cached;
319
+ const formatter = new Intl.DateTimeFormat(locale, {
304
320
  ...format,
305
321
  timeZone
306
- }).formatToParts(date).map((part) => [part.type, part.value]));
322
+ });
323
+ if (dateTimeFormatterCache.size >= DATE_TIME_FORMATTER_CACHE_LIMIT) dateTimeFormatterCache.clear();
324
+ dateTimeFormatterCache.set(key, formatter);
325
+ return formatter;
307
326
  }
308
327
  function getZonedDateTimeParts(date, timeZone) {
309
328
  const parts = formatToPartsLookup(date, "en-GB", timeZone, {
@@ -349,23 +368,9 @@ function toUtcIsoDateTimeString(value) {
349
368
  return parsed.value;
350
369
  }
351
370
  function parseScheduledBookingDateTime(input) {
371
+ let startsAt;
352
372
  try {
353
- const startsAt = toUtcIsoDateTimeString(input.startsAt);
354
- const timeZone = parseStrictBookingTimeZone(input.timeZone);
355
- if (timeZone === null) return {
356
- ok: false,
357
- field: "timeZone",
358
- reason: "invalid_time_zone",
359
- value: input.timeZone
360
- };
361
- return {
362
- ok: true,
363
- value: {
364
- startsAt,
365
- timeZone,
366
- locale: input.locale
367
- }
368
- };
373
+ startsAt = toUtcIsoDateTimeString(input.startsAt);
369
374
  } catch {
370
375
  return {
371
376
  ok: false,
@@ -374,6 +379,21 @@ function parseScheduledBookingDateTime(input) {
374
379
  value: input.startsAt
375
380
  };
376
381
  }
382
+ const timeZone = parseStrictBookingTimeZone(input.timeZone);
383
+ if (timeZone === null) return {
384
+ ok: false,
385
+ field: "timeZone",
386
+ reason: "invalid_time_zone",
387
+ value: typeof input.timeZone === "string" ? input.timeZone : ""
388
+ };
389
+ return {
390
+ ok: true,
391
+ value: {
392
+ startsAt,
393
+ timeZone,
394
+ locale: input.locale
395
+ }
396
+ };
377
397
  }
378
398
  function parseScheduledBookingDateRange(input) {
379
399
  const start = parseScheduledBookingDateTime(input);
@@ -412,10 +432,7 @@ function toScheduledBookingDateRange(input) {
412
432
  function formatBookingInstant(isoString, options) {
413
433
  const date = new Date(isoString);
414
434
  if (Number.isNaN(date.getTime())) return isoString;
415
- return new Intl.DateTimeFormat(options.locale ?? DEFAULT_LOCALE, {
416
- ...options.format,
417
- timeZone: getSafeTimeZone(options.timeZone)
418
- }).format(date);
435
+ return getCachedDateTimeFormatter(options.locale ?? DEFAULT_LOCALE, getSafeTimeZone(options.timeZone), options.format).format(date);
419
436
  }
420
437
  function formatBookingDate(input, format = {
421
438
  weekday: "long",
@@ -578,7 +595,8 @@ function validateRelativeStoragePath(value) {
578
595
  if (value.startsWith("sites/")) return fail({ tag: "SITE_SCOPED_PATH" });
579
596
  if (value.startsWith("/")) return fail({ tag: "LEADING_SLASH" });
580
597
  if (value.includes("//")) return fail({ tag: "DOUBLE_SLASH" });
581
- if (value.split("/").some((segment) => segment === "..")) return fail({ tag: "TRAVERSAL" });
598
+ if (value.includes("%")) return fail({ tag: "PERCENT_ENCODING" });
599
+ if (value.split("/").some((segment) => segment === "." || segment === "..")) return fail({ tag: "TRAVERSAL" });
582
600
  return ok$2(value);
583
601
  }
584
602
  function brandRelativeStoragePath(value) {
@@ -1895,6 +1913,20 @@ const blockCategoryEnum = z$1.enum([
1895
1913
  "layout",
1896
1914
  "interactive"
1897
1915
  ]);
1916
+ const blockContentSourceSchema = z$1.object({
1917
+ kind: z$1.enum([
1918
+ "inline",
1919
+ "entries",
1920
+ "mixed"
1921
+ ]),
1922
+ contentType: z$1.string().min(1).optional()
1923
+ }).superRefine((value, ctx) => {
1924
+ if (value.kind === "inline" && value.contentType) ctx.addIssue({
1925
+ code: z$1.ZodIssueCode.custom,
1926
+ path: ["contentType"],
1927
+ message: "contentType is not allowed when contentSource.kind is \"inline\""
1928
+ });
1929
+ });
1898
1930
  z$1.object({
1899
1931
  name: z$1.string().min(1),
1900
1932
  version: z$1.string().min(1),
@@ -1925,6 +1957,12 @@ z$1.object({
1925
1957
  }).optional()).optional(),
1926
1958
  category: blockCategoryEnum.optional(),
1927
1959
  contentTypes: z$1.array(z$1.string()).optional(),
1960
+ /**
1961
+ * Explicit content-source classification (inline / entries / mixed). Optional at the
1962
+ * schema level for backward compatibility with SDK-defined custom blocks, but every
1963
+ * registered SYSTEM block must declare it (enforced by a registry-wide test).
1964
+ */
1965
+ contentSource: blockContentSourceSchema.optional(),
1928
1966
  tags: z$1.array(z$1.string()).optional(),
1929
1967
  icon: z$1.string().optional()
1930
1968
  });
@@ -3468,7 +3506,7 @@ function normalizeStoredObjectChildren(children, value, ctx) {
3468
3506
  return normalized;
3469
3507
  }
3470
3508
  function normalizeStoredRepeaterItems(plan, value, ctx) {
3471
- if (!isUnknownArray$2(value)) return value;
3509
+ if (!isUnknownArray$3(value)) return value;
3472
3510
  return value.map((item) => {
3473
3511
  if (!item || typeof item !== "object" || Array.isArray(item)) return item;
3474
3512
  const itemRecord = item;
@@ -3527,7 +3565,7 @@ function getAtFieldPath(source, path) {
3527
3565
  }, source);
3528
3566
  }
3529
3567
  function normalizeRepeaterItems(plan, value, ctx) {
3530
- if (!isUnknownArray$2(value)) return value;
3568
+ if (!isUnknownArray$3(value)) return value;
3531
3569
  return value.map((item) => {
3532
3570
  if (!item || typeof item !== "object" || Array.isArray(item)) return item;
3533
3571
  const itemRecord = item;
@@ -3555,7 +3593,7 @@ function validateNormalizedFieldValue(plan, value, ctx) {
3555
3593
  case "select": return validateSelectPlan(plan, value);
3556
3594
  case "richText": return isValidRichTextDoc(value) ? [] : [issue(plan, "invalidRichText")];
3557
3595
  case "inlineText": return validateInlineTextPlan(plan, value);
3558
- case "media": return isPlainObject$4(value) ? [] : [issue(plan, "invalidMedia")];
3596
+ case "media": return isPlainObject$5(value) ? [] : [issue(plan, "invalidMedia")];
3559
3597
  case "link": return linkSchema.safeParse(value).success ? [] : [issue(plan, "invalidLink")];
3560
3598
  case "repeater": return validateRepeaterPlan(plan, value, ctx);
3561
3599
  case "group": return validateGroupPlan(plan, value, ctx);
@@ -3648,7 +3686,7 @@ function validateRepeaterPlan(plan, value, ctx) {
3648
3686
  }), ...value.flatMap((item, index) => validateRepeaterItem(plan, item, index, ctx))];
3649
3687
  }
3650
3688
  function validateRepeaterItem(plan, item, index, ctx) {
3651
- if (!isPlainObject$4(item)) return [];
3689
+ if (!isPlainObject$5(item)) return [];
3652
3690
  const itemType = item._type;
3653
3691
  const fields = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemType)?.fields : plan.repeatedItemPlan;
3654
3692
  if (!fields) return [];
@@ -3657,7 +3695,7 @@ function validateRepeaterItem(plan, item, index, ctx) {
3657
3695
  });
3658
3696
  }
3659
3697
  function validateGroupPlan(plan, value, ctx) {
3660
- if (!isPlainObject$4(value)) return [issue(plan, "wrongType", { expected: "an object" })];
3698
+ if (!isPlainObject$5(value)) return [issue(plan, "wrongType", { expected: "an object" })];
3661
3699
  const childCtx = childValidationContext(plan, ctx);
3662
3700
  return plan.children.flatMap((childPlan) => validateNormalizedFieldValue(childPlan, value[childPlan.fieldId], childCtx));
3663
3701
  }
@@ -3854,7 +3892,7 @@ function sameFieldPath(left, right) {
3854
3892
  return left.length === right.length && left.every((segment, index) => segment === right[index]);
3855
3893
  }
3856
3894
  function validateExplicitNullsForPush(plans, content, ctx) {
3857
- if (!isPlainObject$4(content)) return [];
3895
+ if (!isPlainObject$5(content)) return [];
3858
3896
  return plans.flatMap((plan) => validateExplicitNullForPushPlan(plan, content[plan.fieldId], ctx));
3859
3897
  }
3860
3898
  function validateExplicitNullForPushPlan(plan, value, ctx) {
@@ -3875,9 +3913,9 @@ function validateExplicitNullForPushPlan(plan, value, ctx) {
3875
3913
  }
3876
3914
  }
3877
3915
  function validateExplicitNullsForPushRepeater(plan, value, ctx) {
3878
- if (!isUnknownArray$2(value)) return [];
3916
+ if (!isUnknownArray$3(value)) return [];
3879
3917
  return value.flatMap((item, index) => {
3880
- if (!isPlainObject$4(item)) return [];
3918
+ if (!isPlainObject$5(item)) return [];
3881
3919
  const childPlans = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === item._type)?.fields : plan.repeatedItemPlan;
3882
3920
  if (!childPlans) return [];
3883
3921
  return childPlans.flatMap((childPlan) => validateExplicitNullForPushPlan(materializeRepeaterItemPlan(childPlan, plan.path, index), item[childPlan.fieldId], ctx));
@@ -3960,19 +3998,19 @@ function isEmptyValue$1(value) {
3960
3998
  if (isEmptyRichTextDoc(value)) return true;
3961
3999
  return false;
3962
4000
  }
3963
- function isPlainObject$4(value) {
4001
+ function isPlainObject$5(value) {
3964
4002
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
3965
4003
  }
3966
- function isUnknownArray$2(value) {
4004
+ function isUnknownArray$3(value) {
3967
4005
  return Array.isArray(value);
3968
4006
  }
3969
4007
  function isValidRichTextDoc(value) {
3970
4008
  const doc = richTextDoc(value);
3971
- return Boolean(isPlainObject$4(doc) && doc.type === "doc");
4009
+ return Boolean(isPlainObject$5(doc) && doc.type === "doc");
3972
4010
  }
3973
4011
  function isEmptyRichTextDoc(value) {
3974
4012
  const doc = richTextDoc(value);
3975
- if (!isPlainObject$4(doc)) return false;
4013
+ if (!isPlainObject$5(doc)) return false;
3976
4014
  if (doc.type !== "doc") return false;
3977
4015
  const content = doc.content;
3978
4016
  if (!Array.isArray(content) || content.length === 0) return true;
@@ -3981,8 +4019,8 @@ function isEmptyRichTextDoc(value) {
3981
4019
  return first?.type === "paragraph" && (!Array.isArray(first.content) || first.content.length === 0);
3982
4020
  }
3983
4021
  function richTextDoc(value) {
3984
- if (!isPlainObject$4(value)) return value;
3985
- return isPlainObject$4(value.doc) ? value.doc : value;
4022
+ if (!isPlainObject$5(value)) return value;
4023
+ return isPlainObject$5(value.doc) ? value.doc : value;
3986
4024
  }
3987
4025
  //#endregion
3988
4026
  //#region ../blocks/src/system/manifest/registry.ts
@@ -4050,7 +4088,7 @@ function resolveVariantKey(manifest, content) {
4050
4088
  const clamp01$3 = (x) => Math.max(0, Math.min(1, x));
4051
4089
  const mod = (a, n) => (a % n + n) % n;
4052
4090
  const deg = (x) => mod(x, 360);
4053
- function hexToRgb$2(hex) {
4091
+ function hexToRgb$1(hex) {
4054
4092
  const h = hex.replace(/^#/, "");
4055
4093
  const m = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
4056
4094
  if (m.length !== 6) throw new Error("Bad hex");
@@ -4159,7 +4197,7 @@ function oklchToHexGamut(L, C, h) {
4159
4197
  return rgbToHex$1(r, g, bb);
4160
4198
  }
4161
4199
  function featuresFromHex(hex) {
4162
- const { r, g, b } = hexToRgb$2(hex);
4200
+ const { r, g, b } = hexToRgb$1(hex);
4163
4201
  const { h, s, l } = rgbToHsl$1(r, g, b);
4164
4202
  const { L, a, b: b2 } = srgbToOklab(r, g, b);
4165
4203
  const { L: okL, C: okC, h: okH } = oklabToOklch(L, a, b2);
@@ -4186,8 +4224,8 @@ function relativeLuminance(r, g, b) {
4186
4224
  return .2126 * R + .7152 * G + .0722 * B;
4187
4225
  }
4188
4226
  function contrastRatio(hex1, hex2) {
4189
- const { r: r1, g: g1, b: b1 } = hexToRgb$2(hex1);
4190
- const { r: r2, g: g2, b: b2 } = hexToRgb$2(hex2);
4227
+ const { r: r1, g: g1, b: b1 } = hexToRgb$1(hex1);
4228
+ const { r: r2, g: g2, b: b2 } = hexToRgb$1(hex2);
4191
4229
  const L1 = relativeLuminance(r1, g1, b1);
4192
4230
  const L2 = relativeLuminance(r2, g2, b2);
4193
4231
  const [hi, lo] = L1 >= L2 ? [L1, L2] : [L2, L1];
@@ -5209,6 +5247,10 @@ const modernMinimalTheme = {
5209
5247
  name: "primary",
5210
5248
  hex: "#1f2937"
5211
5249
  },
5250
+ {
5251
+ name: "primaryForeground",
5252
+ hex: "#ffffff"
5253
+ },
5212
5254
  {
5213
5255
  name: "secondary",
5214
5256
  hex: "#6b7280"
@@ -5237,6 +5279,14 @@ const modernMinimalTheme = {
5237
5279
  name: "surface",
5238
5280
  hex: "#ffffff"
5239
5281
  },
5282
+ {
5283
+ name: "surfaceAlt",
5284
+ hex: "#f9fafb"
5285
+ },
5286
+ {
5287
+ name: "muted",
5288
+ hex: "#f3f4f6"
5289
+ },
5240
5290
  {
5241
5291
  name: "text",
5242
5292
  hex: "#1f2937"
@@ -5345,7 +5395,7 @@ function generateCssVars(palette) {
5345
5395
  const cssVars = {};
5346
5396
  Object.entries(palette).forEach(([key, value]) => {
5347
5397
  if (key === "meta" || value === null || value === void 0 || typeof value !== "string") return;
5348
- const { r, g, b } = hexToRgb$2(value);
5398
+ const { r, g, b } = hexToRgb$1(value);
5349
5399
  cssVars[`--tb-${key}`] = `${r} ${g} ${b}`;
5350
5400
  });
5351
5401
  return cssVars;
@@ -5412,7 +5462,7 @@ function getTextColorForBackground(background) {
5412
5462
  }
5413
5463
  function parseColorToRgb(input) {
5414
5464
  const trimmed = input.trim();
5415
- if (trimmed.startsWith("#")) return hexToRgb$2(trimmed);
5465
+ if (trimmed.startsWith("#")) return hexToRgb$1(trimmed);
5416
5466
  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);
5417
5467
  if (rgbMatch?.[1] && rgbMatch[2] && rgbMatch[3]) return {
5418
5468
  r: clamp255(Number(rgbMatch[1])),
@@ -8636,6 +8686,111 @@ function boundaryKey$1(previousBlockId, nextBlockId) {
8636
8686
  return makeBoundaryId(previousBlockId, nextBlockId);
8637
8687
  }
8638
8688
  //#endregion
8689
+ //#region ../theme-core/src/buttons/effectClassification.ts
8690
+ /**
8691
+ * Exhaustive live policy for every registered effect id.
8692
+ *
8693
+ * The resolver consumes the transition payload directly, so adding an effect
8694
+ * cannot update classification without also defining its runtime behavior.
8695
+ */
8696
+ const BUTTON_EFFECT_POLICIES = {
8697
+ "darken-background": {
8698
+ classification: "colour-transition",
8699
+ transition: {
8700
+ kind: "adjust-shade",
8701
+ direction: "darker"
8702
+ }
8703
+ },
8704
+ "lighten-background": {
8705
+ classification: "colour-transition",
8706
+ transition: {
8707
+ kind: "adjust-shade",
8708
+ direction: "lighter"
8709
+ }
8710
+ },
8711
+ "background-on-hover": {
8712
+ classification: "colour-transition",
8713
+ transition: { kind: "replace-background" }
8714
+ },
8715
+ "background-on-hover-alpha": {
8716
+ classification: "unsupported-colour",
8717
+ reason: "alpha_requires_surface"
8718
+ },
8719
+ "fade-background": {
8720
+ classification: "unsupported-colour",
8721
+ reason: "changes_element_opacity"
8722
+ },
8723
+ "opacity-reduce": {
8724
+ classification: "unsupported-colour",
8725
+ reason: "changes_element_opacity"
8726
+ },
8727
+ "gradient-text": {
8728
+ classification: "unsupported-colour",
8729
+ reason: "changes_foreground"
8730
+ },
8731
+ "text-color-change": {
8732
+ classification: "unsupported-colour",
8733
+ reason: "changes_foreground"
8734
+ },
8735
+ "border-color-change": {
8736
+ classification: "unsupported-colour",
8737
+ reason: "changes_border"
8738
+ },
8739
+ "aurora-glow": {
8740
+ classification: "unsupported-background-overlay",
8741
+ reason: "unresolved_background_overlay"
8742
+ },
8743
+ "cosmic-stardust": {
8744
+ classification: "unsupported-background-overlay",
8745
+ reason: "unresolved_background_overlay"
8746
+ },
8747
+ "gradient-border-fill": {
8748
+ classification: "unsupported-background-overlay",
8749
+ reason: "unresolved_background_overlay"
8750
+ },
8751
+ "animated-gradient-border-fill": {
8752
+ classification: "unsupported-background-overlay",
8753
+ reason: "unresolved_background_overlay"
8754
+ },
8755
+ "nav-underline": {
8756
+ classification: "not-button",
8757
+ reason: "not_button_effect"
8758
+ },
8759
+ "nav-frosted-base": {
8760
+ classification: "not-button",
8761
+ reason: "not_button_effect"
8762
+ },
8763
+ "nav-frosted-hover": {
8764
+ classification: "not-button",
8765
+ reason: "not_button_effect"
8766
+ },
8767
+ "nav-frosted-active": {
8768
+ classification: "not-button",
8769
+ reason: "not_button_effect"
8770
+ },
8771
+ "animated-gradient": { classification: "non-colour" },
8772
+ "border-glow": { classification: "non-colour" },
8773
+ "drop-shadow": { classification: "non-colour" },
8774
+ "inner-shadow": { classification: "non-colour" },
8775
+ "neumorphic-shadow": { classification: "non-colour" },
8776
+ "outer-glow": { classification: "non-colour" },
8777
+ "retro-3d-shadow": { classification: "non-colour" },
8778
+ "scale-up": { classification: "non-colour" },
8779
+ "scale-down": { classification: "non-colour" },
8780
+ lift: { classification: "non-colour" },
8781
+ "press-down": { classification: "non-colour" },
8782
+ pop: { classification: "non-colour" },
8783
+ "border-bottom-grow": { classification: "non-colour" },
8784
+ "sketchy-border": { classification: "non-colour" },
8785
+ "pulse-animation": { classification: "non-colour" }
8786
+ };
8787
+ function getButtonEffectPolicy(effectId) {
8788
+ return Object.prototype.hasOwnProperty.call(BUTTON_EFFECT_POLICIES, effectId) ? BUTTON_EFFECT_POLICIES[effectId] : null;
8789
+ }
8790
+ function classifyButtonEffectId(effectId) {
8791
+ return getButtonEffectPolicy(effectId)?.classification ?? "unknown";
8792
+ }
8793
+ //#endregion
8639
8794
  //#region ../theme-core/src/buttons/types.ts
8640
8795
  /**
8641
8796
  * Standard semantic variant roles a theme exposes.
@@ -8665,6 +8820,20 @@ function asSpecialVariantId(value) {
8665
8820
  * Shared between generator and renderer so neither side hard-codes the string.
8666
8821
  */
8667
8822
  const DEFAULT_VARIANT_ALIAS_ID = "default";
8823
+ function parseOpaqueButtonColourToken(value) {
8824
+ const token = parseThemeColorToken(value);
8825
+ if (token === null) return null;
8826
+ const parts = parseColorTokenParts(token);
8827
+ return parts.kind === "token" && parts.opacityPercent === null ? token : null;
8828
+ }
8829
+ function unsafeAsOpaqueButtonColourToken(value) {
8830
+ return value;
8831
+ }
8832
+ const opaqueButtonColourTokenSchema = themeColorTokenSchema.refine((value) => parseOpaqueButtonColourToken(value) !== null, "Button colour tokens must be opaque").transform((value) => {
8833
+ const token = parseOpaqueButtonColourToken(value);
8834
+ if (token === null) throw new Error("Invariant: opaque button token passed its schema");
8835
+ return token;
8836
+ });
8668
8837
  const cornerStyleSchema = z$1.enum([
8669
8838
  "square",
8670
8839
  "rounded",
@@ -8766,7 +8935,7 @@ const buttonSizesSchema = z$1.object({
8766
8935
  lg,
8767
8936
  xl: xl ?? lg
8768
8937
  }));
8769
- const buttonGlobalSettingsSchema = z$1.object({
8938
+ const canonicalButtonGlobalSettingsSchema = z$1.object({
8770
8939
  cornerStyle: cornerStyleSchema,
8771
8940
  cornerRadius: z$1.number().min(0).max(24).optional(),
8772
8941
  /**
@@ -8797,11 +8966,12 @@ const buttonGlobalSettingsSchema = z$1.object({
8797
8966
  paddingPreset: buttonPaddingPresetSchema.optional(),
8798
8967
  hoverTransform: hoverTransformSchema,
8799
8968
  hoverColor: hoverColorSchema,
8800
- hoverColorToken: themeColorTokenSchema.optional(),
8969
+ hoverColorToken: opaqueButtonColourTokenSchema.optional(),
8801
8970
  gradientStyle: gradientStyleSchema.optional(),
8802
8971
  gradientAngle: z$1.number().min(0).max(360).optional(),
8803
8972
  gradientSharpness: gradientSharpnessSchema.optional()
8804
8973
  });
8974
+ const buttonGlobalSettingsSchema = z$1.preprocess(normalizeLegacyButtonGlobalInput, canonicalButtonGlobalSettingsSchema);
8805
8975
  const gradientDirectionSchema$1 = z$1.enum([
8806
8976
  "to-r",
8807
8977
  "to-l",
@@ -8815,27 +8985,33 @@ const gradientDirectionSchema$1 = z$1.enum([
8815
8985
  const buttonBackgroundSchema = z$1.discriminatedUnion("type", [
8816
8986
  z$1.object({
8817
8987
  type: z$1.literal("solid"),
8818
- colorToken: themeColorTokenSchema
8988
+ colorToken: opaqueButtonColourTokenSchema
8819
8989
  }),
8820
8990
  z$1.object({
8821
8991
  type: z$1.literal("gradient"),
8822
- stops: z$1.array(themeColorTokenSchema).min(2),
8823
- direction: gradientDirectionSchema$1
8992
+ stops: z$1.array(opaqueButtonColourTokenSchema).min(2).transform((stops) => {
8993
+ const [first, second, ...rest] = stops;
8994
+ if (first === void 0 || second === void 0) throw new Error("Invariant: gradient stops passed the minimum length schema");
8995
+ return [
8996
+ first,
8997
+ second,
8998
+ ...rest
8999
+ ];
9000
+ }),
9001
+ direction: gradientDirectionSchema$1,
9002
+ foregroundToken: opaqueButtonColourTokenSchema
8824
9003
  }),
8825
- z$1.object({ type: z$1.literal("transparent") })
9004
+ z$1.object({
9005
+ type: z$1.literal("surface"),
9006
+ role: z$1.literal("link")
9007
+ })
8826
9008
  ]);
8827
9009
  const effectApplicationSchema$1 = z$1.object({
8828
9010
  effectId: z$1.string(),
8829
- options: z$1.record(z$1.string(), z$1.any()).optional()
8830
- });
8831
- const buttonBorderSchema = z$1.object({
8832
- widthClass: z$1.string(),
8833
- colorToken: themeColorTokenSchema
8834
- });
8835
- const buttonVariantAffordanceSchema = z$1.object({
8836
- iconId: z$1.string().min(1),
8837
- colorToken: themeColorTokenSchema.optional()
9011
+ options: z$1.record(z$1.string(), z$1.json()).optional()
8838
9012
  });
9013
+ const buttonBorderSchema = z$1.object({ widthClass: z$1.string() });
9014
+ const buttonVariantAffordanceSchema = z$1.object({ iconId: z$1.string().min(1) });
8839
9015
  const variantShadowSchema = z$1.object({
8840
9016
  elevation: z$1.enum([
8841
9017
  "none",
@@ -8876,27 +9052,170 @@ const variantSizeOverridesSchema = z$1.object({
8876
9052
  * default (e.g. link-style variants declaring zero horizontal padding at
8877
9053
  * every size).
8878
9054
  */
8879
- const buttonVariantSchema = z$1.object({
9055
+ const canonicalButtonVariantSchema = z$1.object({
8880
9056
  id: z$1.string(),
8881
9057
  name: z$1.string().min(1).max(20),
8882
9058
  enabled: z$1.boolean(),
8883
9059
  priority: prioritySchema,
8884
9060
  background: buttonBackgroundSchema,
8885
- textColorToken: themeColorTokenSchema,
8886
9061
  /** Tailwind radius token (e.g. `'rounded-md'`). Omit to inherit from `global.cornerStyle`. */
8887
9062
  borderRadius: z$1.string().optional(),
8888
9063
  shadow: variantShadowSchema.optional(),
8889
9064
  border: buttonBorderSchema.optional(),
8890
- hoverBackgroundToken: z$1.string().optional(),
9065
+ hoverBackgroundToken: opaqueButtonColourTokenSchema.optional(),
8891
9066
  effects: variantEffectsSchema.optional(),
8892
9067
  sizes: variantSizeOverridesSchema.optional(),
8893
9068
  affordance: buttonVariantAffordanceSchema.optional()
8894
9069
  });
9070
+ /**
9071
+ * Temporary persisted-theme admission boundary for issue #1206.
9072
+ *
9073
+ * Production inventory on 2026-07-22 found 22 rows using the old independent
9074
+ * `textColorToken`/border colour shape. New source and all downstream code use
9075
+ * only the canonical relationship. Delete this preprocessor after the reviewed
9076
+ * production rewrite reports zero legacy rows.
9077
+ */
9078
+ const buttonVariantSchema = z$1.preprocess(normalizeLegacyButtonVariantInput, canonicalButtonVariantSchema);
8895
9079
  const buttonSystemSchema = z$1.object({
8896
9080
  global: buttonGlobalSettingsSchema,
8897
9081
  variants: z$1.array(buttonVariantSchema).min(1).max(6),
8898
9082
  sizes: buttonSizesSchema.optional()
8899
9083
  });
9084
+ function normalizeLegacyButtonVariantInput(input) {
9085
+ if (!isUnknownRecord(input)) return input;
9086
+ const background = input.background;
9087
+ if (!isUnknownRecord(background)) return input;
9088
+ 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;
9089
+ const { textColorToken, ...withoutLegacyText } = input;
9090
+ const normalizedBorder = isUnknownRecord(input.border) && typeof input.border.widthClass === "string" ? { widthClass: input.border.widthClass } : input.border;
9091
+ const normalizedAffordance = isUnknownRecord(input.affordance) && typeof input.affordance.iconId === "string" ? { iconId: input.affordance.iconId } : input.affordance;
9092
+ const normalizedEffects = normalizeLegacyButtonEffects(input.effects, background.type);
9093
+ const hoverBackgroundToken = stripLegacyTokenAlpha(input.hoverBackgroundToken);
9094
+ if (background.type === "transparent") return {
9095
+ ...withoutLegacyText,
9096
+ background: {
9097
+ type: "surface",
9098
+ role: "link"
9099
+ },
9100
+ border: normalizedBorder,
9101
+ affordance: normalizedAffordance,
9102
+ effects: normalizedEffects,
9103
+ hoverBackgroundToken
9104
+ };
9105
+ if (background.type === "gradient") {
9106
+ const stops = Array.isArray(background.stops) ? background.stops.map(stripLegacyTokenAlpha) : background.stops;
9107
+ return {
9108
+ ...withoutLegacyText,
9109
+ background: {
9110
+ ...background,
9111
+ stops,
9112
+ foregroundToken: stripLegacyTokenAlpha(background.foregroundToken ?? textColorToken)
9113
+ },
9114
+ border: normalizedBorder,
9115
+ affordance: normalizedAffordance,
9116
+ effects: normalizedEffects,
9117
+ hoverBackgroundToken
9118
+ };
9119
+ }
9120
+ return {
9121
+ ...withoutLegacyText,
9122
+ background: background.type === "solid" ? {
9123
+ ...background,
9124
+ colorToken: stripLegacyTokenAlpha(background.colorToken)
9125
+ } : background,
9126
+ border: normalizedBorder,
9127
+ affordance: normalizedAffordance,
9128
+ effects: normalizedEffects,
9129
+ hoverBackgroundToken
9130
+ };
9131
+ }
9132
+ function normalizeLegacyButtonGlobalInput(input) {
9133
+ if (!isUnknownRecord(input)) return input;
9134
+ const hoverColorToken = stripLegacyTokenAlpha(input.hoverColorToken);
9135
+ return hoverColorToken === input.hoverColorToken ? input : {
9136
+ ...input,
9137
+ hoverColorToken
9138
+ };
9139
+ }
9140
+ function hasLegacyAlphaToken(input, background) {
9141
+ const stops = isUnknownArray$2(background.stops) ? background.stops : [];
9142
+ return [
9143
+ input.hoverBackgroundToken,
9144
+ background.colorToken,
9145
+ background.foregroundToken,
9146
+ ...stops
9147
+ ].some((token) => stripLegacyTokenAlpha(token) !== token);
9148
+ }
9149
+ function stripLegacyTokenAlpha(value) {
9150
+ if (typeof value !== "string") return value;
9151
+ const slashIndex = value.indexOf("/");
9152
+ return slashIndex < 0 ? value : value.slice(0, slashIndex);
9153
+ }
9154
+ function hasLegacyButtonEffects(input) {
9155
+ if (!isUnknownRecord(input)) return false;
9156
+ return [
9157
+ "base",
9158
+ "hover",
9159
+ "active",
9160
+ "focus"
9161
+ ].some((state) => {
9162
+ const effects = input[state];
9163
+ return Array.isArray(effects) && effects.some((effect) => {
9164
+ if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return true;
9165
+ const classification = classifyButtonEffectId(effect.effectId);
9166
+ if (classification !== "colour-transition" && classification !== "non-colour") return true;
9167
+ return effect.effectId === "background-on-hover" && isUnknownRecord(effect.options) && stripLegacyTokenAlpha(effect.options.colorToken) !== effect.options.colorToken;
9168
+ });
9169
+ });
9170
+ }
9171
+ function normalizeLegacyButtonEffects(input, backgroundType) {
9172
+ if (!isUnknownRecord(input)) return input;
9173
+ const normalized = {};
9174
+ for (const state of [
9175
+ "base",
9176
+ "hover",
9177
+ "active",
9178
+ "focus"
9179
+ ]) {
9180
+ const effects = input[state];
9181
+ if (!Array.isArray(effects)) continue;
9182
+ const safeEffects = effects.flatMap((effect) => {
9183
+ if (!isUnknownRecord(effect) || typeof effect.effectId !== "string") return [];
9184
+ const classification = classifyButtonEffectId(effect.effectId);
9185
+ if (classification === "non-colour") return [effect];
9186
+ if (classification !== "colour-transition") return [];
9187
+ if (effect.effectId === "background-on-hover") {
9188
+ const options = isUnknownRecord(effect.options) ? effect.options : null;
9189
+ const colorToken = options === null ? null : parseOpaqueButtonColourToken(stripLegacyTokenAlpha(options.colorToken));
9190
+ return colorToken === null ? [] : [{
9191
+ ...effect,
9192
+ options: {
9193
+ ...options,
9194
+ colorToken
9195
+ }
9196
+ }];
9197
+ }
9198
+ if (backgroundType !== "solid") return [];
9199
+ const rawAmount = isUnknownRecord(effect.options) ? effect.options.amount : void 0;
9200
+ const amount = typeof rawAmount === "number" && rawAmount > 0 ? Math.max(100, Math.round(rawAmount / 100) * 100) : 100;
9201
+ return [{
9202
+ ...effect,
9203
+ options: {
9204
+ ...isUnknownRecord(effect.options) ? effect.options : {},
9205
+ amount
9206
+ }
9207
+ }];
9208
+ });
9209
+ if (safeEffects.length > 0) normalized[state] = safeEffects;
9210
+ }
9211
+ return Object.keys(normalized).length === 0 ? void 0 : normalized;
9212
+ }
9213
+ function isUnknownRecord(value) {
9214
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9215
+ }
9216
+ function isUnknownArray$2(value) {
9217
+ return Array.isArray(value);
9218
+ }
8900
9219
  //#endregion
8901
9220
  //#region ../theme-core/src/buttons/personalities/types.ts
8902
9221
  function asButtonPersonalityId(value) {
@@ -8922,7 +9241,7 @@ function defineButtonPersonality(input) {
8922
9241
  name: input.name,
8923
9242
  description: input.description,
8924
9243
  tags: input.tags,
8925
- buttonSystem: input.buttonSystem
9244
+ buttonSystem: buttonSystemSchema.parse(input.buttonSystem)
8926
9245
  };
8927
9246
  }
8928
9247
  //#endregion
@@ -8975,19 +9294,15 @@ const brushedWash = defineButtonPersonality({
8975
9294
  background: {
8976
9295
  type: "gradient",
8977
9296
  stops: ["primary", "accent"],
8978
- direction: "to-r"
9297
+ direction: "to-r",
9298
+ foregroundToken: "primaryForeground"
8979
9299
  },
8980
- textColorToken: "primaryForeground",
8981
9300
  borderRadius: "rounded-lg",
8982
9301
  shadow: {
8983
9302
  elevation: "low",
8984
9303
  softness: "soft",
8985
9304
  position: "bottom"
8986
- },
8987
- effects: { hover: [{
8988
- effectId: "opacity-reduce",
8989
- options: { reduction: -15 }
8990
- }] }
9305
+ }
8991
9306
  },
8992
9307
  {
8993
9308
  id: "secondary",
@@ -8997,32 +9312,27 @@ const brushedWash = defineButtonPersonality({
8997
9312
  background: {
8998
9313
  type: "gradient",
8999
9314
  stops: ["secondary", "accent"],
9000
- direction: "to-br"
9315
+ direction: "to-br",
9316
+ foregroundToken: "primaryForeground"
9001
9317
  },
9002
- textColorToken: "primaryForeground",
9003
9318
  borderRadius: "rounded-lg",
9004
9319
  shadow: {
9005
9320
  elevation: "low",
9006
9321
  softness: "soft",
9007
9322
  position: "bottom"
9008
- },
9009
- effects: { hover: [{
9010
- effectId: "opacity-reduce",
9011
- options: { reduction: -15 }
9012
- }] }
9323
+ }
9013
9324
  },
9014
9325
  {
9015
9326
  id: "outline",
9016
9327
  name: "Outline",
9017
9328
  enabled: true,
9018
9329
  priority: 3,
9019
- background: { type: "transparent" },
9020
- textColorToken: "primary",
9021
- borderRadius: "rounded-lg",
9022
- border: {
9023
- widthClass: "border",
9024
- colorToken: "primary"
9330
+ background: {
9331
+ type: "surface",
9332
+ role: "link"
9025
9333
  },
9334
+ borderRadius: "rounded-lg",
9335
+ border: { widthClass: "border" },
9026
9336
  effects: { hover: [{
9027
9337
  effectId: "background-on-hover",
9028
9338
  options: { colorToken: "surface" }
@@ -9033,8 +9343,10 @@ const brushedWash = defineButtonPersonality({
9033
9343
  name: "Ghost",
9034
9344
  enabled: true,
9035
9345
  priority: 3,
9036
- background: { type: "transparent" },
9037
- textColorToken: "text",
9346
+ background: {
9347
+ type: "surface",
9348
+ role: "link"
9349
+ },
9038
9350
  borderRadius: "rounded-lg",
9039
9351
  effects: { hover: [{
9040
9352
  effectId: "background-on-hover",
@@ -9080,7 +9392,6 @@ const confidentChip = defineButtonPersonality({
9080
9392
  type: "solid",
9081
9393
  colorToken: "primary"
9082
9394
  },
9083
- textColorToken: "primaryForeground",
9084
9395
  borderRadius: "rounded-md",
9085
9396
  shadow: {
9086
9397
  elevation: "medium",
@@ -9090,7 +9401,7 @@ const confidentChip = defineButtonPersonality({
9090
9401
  effects: {
9091
9402
  hover: [{
9092
9403
  effectId: "darken-background",
9093
- options: { amount: 60 }
9404
+ options: { amount: 100 }
9094
9405
  }],
9095
9406
  active: [{
9096
9407
  effectId: "scale-down",
@@ -9107,12 +9418,8 @@ const confidentChip = defineButtonPersonality({
9107
9418
  type: "solid",
9108
9419
  colorToken: "surface"
9109
9420
  },
9110
- textColorToken: "text",
9111
9421
  borderRadius: "rounded-md",
9112
- border: {
9113
- widthClass: "border",
9114
- colorToken: "border"
9115
- },
9422
+ border: { widthClass: "border" },
9116
9423
  effects: { hover: [{
9117
9424
  effectId: "background-on-hover",
9118
9425
  options: { colorToken: "background" }
@@ -9123,13 +9430,12 @@ const confidentChip = defineButtonPersonality({
9123
9430
  name: "Outline",
9124
9431
  enabled: true,
9125
9432
  priority: 3,
9126
- background: { type: "transparent" },
9127
- textColorToken: "primary",
9128
- borderRadius: "rounded-md",
9129
- border: {
9130
- widthClass: "border",
9131
- colorToken: "primary"
9433
+ background: {
9434
+ type: "surface",
9435
+ role: "link"
9132
9436
  },
9437
+ borderRadius: "rounded-md",
9438
+ border: { widthClass: "border" },
9133
9439
  effects: { hover: [{
9134
9440
  effectId: "background-on-hover",
9135
9441
  options: { colorToken: "surface" }
@@ -9140,8 +9446,10 @@ const confidentChip = defineButtonPersonality({
9140
9446
  name: "Ghost",
9141
9447
  enabled: true,
9142
9448
  priority: 3,
9143
- background: { type: "transparent" },
9144
- textColorToken: "primary",
9449
+ background: {
9450
+ type: "surface",
9451
+ role: "link"
9452
+ },
9145
9453
  borderRadius: "rounded-md",
9146
9454
  effects: { hover: [{
9147
9455
  effectId: "background-on-hover",
@@ -9204,7 +9512,6 @@ const earthPill = defineButtonPersonality({
9204
9512
  type: "solid",
9205
9513
  colorToken: "primary"
9206
9514
  },
9207
- textColorToken: "primaryForeground",
9208
9515
  borderRadius: "rounded-full",
9209
9516
  shadow: {
9210
9517
  elevation: "low",
@@ -9230,13 +9537,12 @@ const earthPill = defineButtonPersonality({
9230
9537
  name: "Secondary",
9231
9538
  enabled: true,
9232
9539
  priority: 2,
9233
- background: { type: "transparent" },
9234
- textColorToken: "primary",
9235
- borderRadius: "rounded-full",
9236
- border: {
9237
- widthClass: "border",
9238
- colorToken: "primary"
9540
+ background: {
9541
+ type: "surface",
9542
+ role: "link"
9239
9543
  },
9544
+ borderRadius: "rounded-full",
9545
+ border: { widthClass: "border" },
9240
9546
  effects: { hover: [{
9241
9547
  effectId: "background-on-hover",
9242
9548
  options: { colorToken: "muted" }
@@ -9247,13 +9553,12 @@ const earthPill = defineButtonPersonality({
9247
9553
  name: "Outline",
9248
9554
  enabled: true,
9249
9555
  priority: 3,
9250
- background: { type: "transparent" },
9251
- textColorToken: "text",
9252
- borderRadius: "rounded-full",
9253
- border: {
9254
- widthClass: "border",
9255
- colorToken: "border"
9556
+ background: {
9557
+ type: "surface",
9558
+ role: "link"
9256
9559
  },
9560
+ borderRadius: "rounded-full",
9561
+ border: { widthClass: "border" },
9257
9562
  effects: { hover: [{
9258
9563
  effectId: "background-on-hover",
9259
9564
  options: { colorToken: "surfaceAlt" }
@@ -9264,8 +9569,10 @@ const earthPill = defineButtonPersonality({
9264
9569
  name: "Ghost",
9265
9570
  enabled: true,
9266
9571
  priority: 3,
9267
- background: { type: "transparent" },
9268
- textColorToken: "primary",
9572
+ background: {
9573
+ type: "surface",
9574
+ role: "link"
9575
+ },
9269
9576
  borderRadius: "rounded-full",
9270
9577
  shadow: {
9271
9578
  elevation: "none",
@@ -9286,11 +9593,7 @@ const earthPill = defineButtonPersonality({
9286
9593
  padding: "0.625rem 0",
9287
9594
  fontSize: "text-xl"
9288
9595
  }
9289
- },
9290
- effects: { hover: [{
9291
- effectId: "opacity-reduce",
9292
- options: { reduction: 25 }
9293
- }] }
9596
+ }
9294
9597
  }
9295
9598
  ]
9296
9599
  }
@@ -9326,8 +9629,10 @@ const editorialLink = defineButtonPersonality({
9326
9629
  name: "Primary",
9327
9630
  enabled: true,
9328
9631
  priority: 1,
9329
- background: { type: "transparent" },
9330
- textColorToken: "text",
9632
+ background: {
9633
+ type: "surface",
9634
+ role: "link"
9635
+ },
9331
9636
  borderRadius: "rounded-none",
9332
9637
  sizes: {
9333
9638
  sm: { padding: "0.25rem 0" },
@@ -9349,8 +9654,10 @@ const editorialLink = defineButtonPersonality({
9349
9654
  name: "Secondary",
9350
9655
  enabled: true,
9351
9656
  priority: 2,
9352
- background: { type: "transparent" },
9353
- textColorToken: "mutedText",
9657
+ background: {
9658
+ type: "surface",
9659
+ role: "link"
9660
+ },
9354
9661
  borderRadius: "rounded-none",
9355
9662
  sizes: {
9356
9663
  sm: { padding: "0.25rem 0" },
@@ -9372,8 +9679,10 @@ const editorialLink = defineButtonPersonality({
9372
9679
  name: "Outline",
9373
9680
  enabled: true,
9374
9681
  priority: 3,
9375
- background: { type: "transparent" },
9376
- textColorToken: "accent",
9682
+ background: {
9683
+ type: "surface",
9684
+ role: "link"
9685
+ },
9377
9686
  borderRadius: "rounded-none",
9378
9687
  sizes: {
9379
9688
  sm: { padding: "0.25rem 0" },
@@ -9395,19 +9704,17 @@ const editorialLink = defineButtonPersonality({
9395
9704
  name: "Ghost",
9396
9705
  enabled: true,
9397
9706
  priority: 3,
9398
- background: { type: "transparent" },
9399
- textColorToken: "mutedText",
9707
+ background: {
9708
+ type: "surface",
9709
+ role: "link"
9710
+ },
9400
9711
  borderRadius: "rounded-none",
9401
9712
  sizes: {
9402
9713
  sm: { padding: "0.25rem 0" },
9403
9714
  md: { padding: "0.5rem 0" },
9404
9715
  lg: { padding: "0.625rem 0" },
9405
9716
  xl: { padding: "0.75rem 0" }
9406
- },
9407
- effects: { hover: [{
9408
- effectId: "opacity-reduce",
9409
- options: { reduction: -20 }
9410
- }] }
9717
+ }
9411
9718
  }
9412
9719
  ]
9413
9720
  }
@@ -9469,12 +9776,8 @@ const flatInk = defineButtonPersonality({
9469
9776
  type: "solid",
9470
9777
  colorToken: "primary"
9471
9778
  },
9472
- textColorToken: "primaryForeground",
9473
9779
  borderRadius: "rounded-lg",
9474
- affordance: {
9475
- iconId: "ArrowRight",
9476
- colorToken: "accent"
9477
- },
9780
+ affordance: { iconId: "ArrowRight" },
9478
9781
  effects: { hover: [{
9479
9782
  effectId: "lift",
9480
9783
  options: { translateY: "-2px" }
@@ -9488,13 +9791,12 @@ const flatInk = defineButtonPersonality({
9488
9791
  name: "Secondary",
9489
9792
  enabled: true,
9490
9793
  priority: 2,
9491
- background: { type: "transparent" },
9492
- textColorToken: "primary",
9493
- borderRadius: "rounded-lg",
9494
- border: {
9495
- widthClass: "border",
9496
- colorToken: "border"
9794
+ background: {
9795
+ type: "surface",
9796
+ role: "link"
9497
9797
  },
9798
+ borderRadius: "rounded-lg",
9799
+ border: { widthClass: "border" },
9498
9800
  effects: { hover: [{
9499
9801
  effectId: "lift",
9500
9802
  options: { translateY: "-2px" }
@@ -9512,12 +9814,8 @@ const flatInk = defineButtonPersonality({
9512
9814
  type: "solid",
9513
9815
  colorToken: "accent"
9514
9816
  },
9515
- textColorToken: "accentForeground",
9516
9817
  borderRadius: "rounded-lg",
9517
- affordance: {
9518
- iconId: "ArrowRight",
9519
- colorToken: "accentForeground"
9520
- },
9818
+ affordance: { iconId: "ArrowRight" },
9521
9819
  effects: { hover: [{
9522
9820
  effectId: "lift",
9523
9821
  options: { translateY: "-2px" }
@@ -9580,7 +9878,6 @@ const inkStamp = defineButtonPersonality({
9580
9878
  type: "solid",
9581
9879
  colorToken: "primary"
9582
9880
  },
9583
- textColorToken: "primaryForeground",
9584
9881
  borderRadius: "rounded-md",
9585
9882
  effects: {
9586
9883
  hover: [{
@@ -9601,13 +9898,12 @@ const inkStamp = defineButtonPersonality({
9601
9898
  name: "Secondary",
9602
9899
  enabled: true,
9603
9900
  priority: 2,
9604
- background: { type: "transparent" },
9605
- textColorToken: "primary",
9606
- borderRadius: "rounded-md",
9607
- border: {
9608
- widthClass: "border-2",
9609
- colorToken: "primary"
9901
+ background: {
9902
+ type: "surface",
9903
+ role: "link"
9610
9904
  },
9905
+ borderRadius: "rounded-md",
9906
+ border: { widthClass: "border-2" },
9611
9907
  effects: { hover: [{
9612
9908
  effectId: "background-on-hover",
9613
9909
  options: { colorToken: "surface" }
@@ -9618,13 +9914,12 @@ const inkStamp = defineButtonPersonality({
9618
9914
  name: "Outline",
9619
9915
  enabled: true,
9620
9916
  priority: 3,
9621
- background: { type: "transparent" },
9622
- textColorToken: "text",
9623
- borderRadius: "rounded-md",
9624
- border: {
9625
- widthClass: "border-2",
9626
- colorToken: "border"
9917
+ background: {
9918
+ type: "surface",
9919
+ role: "link"
9627
9920
  },
9921
+ borderRadius: "rounded-md",
9922
+ border: { widthClass: "border-2" },
9628
9923
  effects: { hover: [{
9629
9924
  effectId: "background-on-hover",
9630
9925
  options: { colorToken: "surface" }
@@ -9635,8 +9930,10 @@ const inkStamp = defineButtonPersonality({
9635
9930
  name: "Ghost",
9636
9931
  enabled: true,
9637
9932
  priority: 3,
9638
- background: { type: "transparent" },
9639
- textColorToken: "accent",
9933
+ background: {
9934
+ type: "surface",
9935
+ role: "link"
9936
+ },
9640
9937
  borderRadius: "rounded-none",
9641
9938
  sizes: {
9642
9939
  sm: {
@@ -9729,7 +10026,6 @@ const joyStamp = defineButtonPersonality({
9729
10026
  type: "solid",
9730
10027
  colorToken: "primary"
9731
10028
  },
9732
- textColorToken: "primaryForeground",
9733
10029
  borderRadius: "rounded-full",
9734
10030
  shadow: {
9735
10031
  elevation: "high",
@@ -9743,13 +10039,12 @@ const joyStamp = defineButtonPersonality({
9743
10039
  name: "Secondary",
9744
10040
  enabled: true,
9745
10041
  priority: 2,
9746
- background: { type: "transparent" },
9747
- textColorToken: "text",
9748
- borderRadius: "rounded-full",
9749
- border: {
9750
- widthClass: "border-2",
9751
- colorToken: "text"
10042
+ background: {
10043
+ type: "surface",
10044
+ role: "link"
9752
10045
  },
10046
+ borderRadius: "rounded-full",
10047
+ border: { widthClass: "border-2" },
9753
10048
  shadow: {
9754
10049
  elevation: "high",
9755
10050
  softness: "hard",
@@ -9814,12 +10109,8 @@ const ornateRitual = defineButtonPersonality({
9814
10109
  type: "solid",
9815
10110
  colorToken: "primary"
9816
10111
  },
9817
- textColorToken: "primaryForeground",
9818
10112
  borderRadius: "rounded-md",
9819
- border: {
9820
- widthClass: "border",
9821
- colorToken: "primary"
9822
- },
10113
+ border: { widthClass: "border" },
9823
10114
  effects: {
9824
10115
  hover: [{
9825
10116
  effectId: "background-on-hover",
@@ -9840,12 +10131,8 @@ const ornateRitual = defineButtonPersonality({
9840
10131
  type: "solid",
9841
10132
  colorToken: "secondary"
9842
10133
  },
9843
- textColorToken: "primaryForeground",
9844
10134
  borderRadius: "rounded-md",
9845
- border: {
9846
- widthClass: "border",
9847
- colorToken: "primary"
9848
- },
10135
+ border: { widthClass: "border" },
9849
10136
  effects: { hover: [{
9850
10137
  effectId: "background-on-hover",
9851
10138
  options: { colorToken: "surface" }
@@ -9856,13 +10143,12 @@ const ornateRitual = defineButtonPersonality({
9856
10143
  name: "Outline",
9857
10144
  enabled: true,
9858
10145
  priority: 3,
9859
- background: { type: "transparent" },
9860
- textColorToken: "primary",
9861
- borderRadius: "rounded-md",
9862
- border: {
9863
- widthClass: "border",
9864
- colorToken: "primary"
10146
+ background: {
10147
+ type: "surface",
10148
+ role: "link"
9865
10149
  },
10150
+ borderRadius: "rounded-md",
10151
+ border: { widthClass: "border" },
9866
10152
  effects: { hover: [{
9867
10153
  effectId: "background-on-hover",
9868
10154
  options: { colorToken: "muted" }
@@ -9873,8 +10159,10 @@ const ornateRitual = defineButtonPersonality({
9873
10159
  name: "Ghost",
9874
10160
  enabled: true,
9875
10161
  priority: 3,
9876
- background: { type: "transparent" },
9877
- textColorToken: "accent",
10162
+ background: {
10163
+ type: "surface",
10164
+ role: "link"
10165
+ },
9878
10166
  borderRadius: "rounded-none",
9879
10167
  sizes: {
9880
10168
  sm: {
@@ -9956,14 +10244,10 @@ const pebble = defineButtonPersonality({
9956
10244
  type: "solid",
9957
10245
  colorToken: "primary"
9958
10246
  },
9959
- textColorToken: "primaryForeground",
9960
10247
  borderRadius: "rounded-full",
9961
10248
  effects: { hover: [{
9962
- effectId: "background-on-hover-alpha",
9963
- options: {
9964
- colorToken: "primary",
9965
- opacity: 88
9966
- }
10249
+ effectId: "darken-background",
10250
+ options: { amount: 100 }
9967
10251
  }] }
9968
10252
  },
9969
10253
  {
@@ -9971,13 +10255,12 @@ const pebble = defineButtonPersonality({
9971
10255
  name: "Secondary",
9972
10256
  enabled: true,
9973
10257
  priority: 2,
9974
- background: { type: "transparent" },
9975
- textColorToken: "primary",
9976
- borderRadius: "rounded-full",
9977
- border: {
9978
- widthClass: "border-2",
9979
- colorToken: "accent"
10258
+ background: {
10259
+ type: "surface",
10260
+ role: "link"
9980
10261
  },
10262
+ borderRadius: "rounded-full",
10263
+ border: { widthClass: "border-2" },
9981
10264
  effects: { hover: [{
9982
10265
  effectId: "background-on-hover",
9983
10266
  options: { colorToken: "accent" }
@@ -9988,13 +10271,12 @@ const pebble = defineButtonPersonality({
9988
10271
  name: "Outline",
9989
10272
  enabled: true,
9990
10273
  priority: 3,
9991
- background: { type: "transparent" },
9992
- textColorToken: "text",
9993
- borderRadius: "rounded-full",
9994
- border: {
9995
- widthClass: "border",
9996
- colorToken: "border"
10274
+ background: {
10275
+ type: "surface",
10276
+ role: "link"
9997
10277
  },
10278
+ borderRadius: "rounded-full",
10279
+ border: { widthClass: "border" },
9998
10280
  effects: { hover: [{
9999
10281
  effectId: "background-on-hover",
10000
10282
  options: { colorToken: "surface" }
@@ -10005,8 +10287,10 @@ const pebble = defineButtonPersonality({
10005
10287
  name: "Ghost",
10006
10288
  enabled: true,
10007
10289
  priority: 3,
10008
- background: { type: "transparent" },
10009
- textColorToken: "primary",
10290
+ background: {
10291
+ type: "surface",
10292
+ role: "link"
10293
+ },
10010
10294
  borderRadius: "rounded-full",
10011
10295
  sizes: {
10012
10296
  sm: {
@@ -10022,11 +10306,7 @@ const pebble = defineButtonPersonality({
10022
10306
  padding: "0.75rem 1.5rem",
10023
10307
  fontSize: "text-xl"
10024
10308
  }
10025
- },
10026
- effects: { hover: [{
10027
- effectId: "opacity-reduce",
10028
- options: { reduction: 30 }
10029
- }] }
10309
+ }
10030
10310
  }
10031
10311
  ]
10032
10312
  }
@@ -10088,15 +10368,11 @@ const quietArrow = defineButtonPersonality({
10088
10368
  type: "solid",
10089
10369
  colorToken: "primary"
10090
10370
  },
10091
- textColorToken: "primaryForeground",
10092
10371
  borderRadius: "rounded-xl",
10093
10372
  affordance: { iconId: "ArrowRight" },
10094
10373
  effects: { hover: [{
10095
- effectId: "background-on-hover-alpha",
10096
- options: {
10097
- colorToken: "primary",
10098
- opacity: 90
10099
- }
10374
+ effectId: "darken-background",
10375
+ options: { amount: 100 }
10100
10376
  }] }
10101
10377
  },
10102
10378
  {
@@ -10104,13 +10380,12 @@ const quietArrow = defineButtonPersonality({
10104
10380
  name: "Secondary",
10105
10381
  enabled: true,
10106
10382
  priority: 2,
10107
- background: { type: "transparent" },
10108
- textColorToken: "primary",
10109
- borderRadius: "rounded-xl",
10110
- border: {
10111
- widthClass: "border",
10112
- colorToken: "primary"
10383
+ background: {
10384
+ type: "surface",
10385
+ role: "link"
10113
10386
  },
10387
+ borderRadius: "rounded-xl",
10388
+ border: { widthClass: "border" },
10114
10389
  affordance: { iconId: "ArrowRight" },
10115
10390
  effects: { hover: [{
10116
10391
  effectId: "background-on-hover",
@@ -10122,21 +10397,22 @@ const quietArrow = defineButtonPersonality({
10122
10397
  name: "Outline",
10123
10398
  enabled: true,
10124
10399
  priority: 3,
10125
- background: { type: "transparent" },
10126
- textColorToken: "primary",
10400
+ background: {
10401
+ type: "surface",
10402
+ role: "link"
10403
+ },
10127
10404
  borderRadius: "rounded-xl",
10128
- border: {
10129
- widthClass: "border",
10130
- colorToken: "primary"
10131
- }
10405
+ border: { widthClass: "border" }
10132
10406
  },
10133
10407
  {
10134
10408
  id: "ghost",
10135
10409
  name: "Ghost",
10136
10410
  enabled: true,
10137
10411
  priority: 3,
10138
- background: { type: "transparent" },
10139
- textColorToken: "primary",
10412
+ background: {
10413
+ type: "surface",
10414
+ role: "link"
10415
+ },
10140
10416
  borderRadius: "rounded-xl"
10141
10417
  }
10142
10418
  ]
@@ -10200,7 +10476,6 @@ const showtimePill = defineButtonPersonality({
10200
10476
  type: "solid",
10201
10477
  colorToken: "primary"
10202
10478
  },
10203
- textColorToken: "background",
10204
10479
  borderRadius: "rounded-full",
10205
10480
  effects: { hover: [{ effectId: "pop" }] }
10206
10481
  },
@@ -10209,13 +10484,12 @@ const showtimePill = defineButtonPersonality({
10209
10484
  name: "Secondary",
10210
10485
  enabled: true,
10211
10486
  priority: 2,
10212
- background: { type: "transparent" },
10213
- textColorToken: "primary",
10214
- borderRadius: "rounded-full",
10215
- border: {
10216
- widthClass: "border-2",
10217
- colorToken: "primary"
10487
+ background: {
10488
+ type: "surface",
10489
+ role: "link"
10218
10490
  },
10491
+ borderRadius: "rounded-full",
10492
+ border: { widthClass: "border-2" },
10219
10493
  effects: { hover: [{ effectId: "pop" }] }
10220
10494
  },
10221
10495
  {
@@ -10227,7 +10501,6 @@ const showtimePill = defineButtonPersonality({
10227
10501
  type: "solid",
10228
10502
  colorToken: "secondary"
10229
10503
  },
10230
- textColorToken: "primary",
10231
10504
  borderRadius: "rounded-full",
10232
10505
  effects: { hover: [{ effectId: "pop" }] }
10233
10506
  },
@@ -10240,12 +10513,8 @@ const showtimePill = defineButtonPersonality({
10240
10513
  type: "solid",
10241
10514
  colorToken: "background"
10242
10515
  },
10243
- textColorToken: "primary",
10244
10516
  borderRadius: "rounded-full",
10245
- border: {
10246
- widthClass: "border-2",
10247
- colorToken: "primary"
10248
- },
10517
+ border: { widthClass: "border-2" },
10249
10518
  effects: { hover: [{
10250
10519
  effectId: "background-on-hover",
10251
10520
  options: { colorToken: "surface" }
@@ -10256,13 +10525,12 @@ const showtimePill = defineButtonPersonality({
10256
10525
  name: "Outline",
10257
10526
  enabled: true,
10258
10527
  priority: 3,
10259
- background: { type: "transparent" },
10260
- textColorToken: "primary",
10261
- borderRadius: "rounded-full",
10262
- border: {
10263
- widthClass: "border-2",
10264
- colorToken: "primary"
10528
+ background: {
10529
+ type: "surface",
10530
+ role: "link"
10265
10531
  },
10532
+ borderRadius: "rounded-full",
10533
+ border: { widthClass: "border-2" },
10266
10534
  effects: { hover: [{
10267
10535
  effectId: "background-on-hover",
10268
10536
  options: { colorToken: "background" }
@@ -10273,8 +10541,10 @@ const showtimePill = defineButtonPersonality({
10273
10541
  name: "Ghost",
10274
10542
  enabled: true,
10275
10543
  priority: 3,
10276
- background: { type: "transparent" },
10277
- textColorToken: "primary",
10544
+ background: {
10545
+ type: "surface",
10546
+ role: "link"
10547
+ },
10278
10548
  borderRadius: "rounded-full",
10279
10549
  effects: { hover: [{
10280
10550
  effectId: "background-on-hover",
@@ -10320,7 +10590,6 @@ const softPill = defineButtonPersonality({
10320
10590
  type: "solid",
10321
10591
  colorToken: "primary"
10322
10592
  },
10323
- textColorToken: "primaryForeground",
10324
10593
  borderRadius: "rounded-full",
10325
10594
  shadow: {
10326
10595
  elevation: "low",
@@ -10343,13 +10612,12 @@ const softPill = defineButtonPersonality({
10343
10612
  name: "Secondary",
10344
10613
  enabled: true,
10345
10614
  priority: 2,
10346
- background: { type: "transparent" },
10347
- textColorToken: "primary",
10348
- borderRadius: "rounded-full",
10349
- border: {
10350
- widthClass: "border-2",
10351
- colorToken: "primary"
10615
+ background: {
10616
+ type: "surface",
10617
+ role: "link"
10352
10618
  },
10619
+ borderRadius: "rounded-full",
10620
+ border: { widthClass: "border-2" },
10353
10621
  effects: { hover: [{
10354
10622
  effectId: "background-on-hover",
10355
10623
  options: { colorToken: "surface" }
@@ -10360,13 +10628,12 @@ const softPill = defineButtonPersonality({
10360
10628
  name: "Outline",
10361
10629
  enabled: true,
10362
10630
  priority: 3,
10363
- background: { type: "transparent" },
10364
- textColorToken: "text",
10365
- borderRadius: "rounded-full",
10366
- border: {
10367
- widthClass: "border",
10368
- colorToken: "border"
10631
+ background: {
10632
+ type: "surface",
10633
+ role: "link"
10369
10634
  },
10635
+ borderRadius: "rounded-full",
10636
+ border: { widthClass: "border" },
10370
10637
  effects: { hover: [{
10371
10638
  effectId: "background-on-hover",
10372
10639
  options: { colorToken: "surface" }
@@ -10377,8 +10644,10 @@ const softPill = defineButtonPersonality({
10377
10644
  name: "Ghost",
10378
10645
  enabled: true,
10379
10646
  priority: 3,
10380
- background: { type: "transparent" },
10381
- textColorToken: "text",
10647
+ background: {
10648
+ type: "surface",
10649
+ role: "link"
10650
+ },
10382
10651
  borderRadius: "rounded-full",
10383
10652
  effects: { hover: [{
10384
10653
  effectId: "background-on-hover",
@@ -10397,9 +10666,8 @@ const solidSecondary = {
10397
10666
  priority: 2,
10398
10667
  background: {
10399
10668
  type: "solid",
10400
- colorToken: "secondary"
10669
+ colorToken: unsafeAsOpaqueButtonColourToken("secondary")
10401
10670
  },
10402
- textColorToken: "onSecondary",
10403
10671
  borderRadius: "rounded-full",
10404
10672
  shadow: {
10405
10673
  elevation: "none",
@@ -10832,6 +11100,282 @@ const serifDisplayQuote = { style: "serif-display" };
10832
11100
  /** Punchy quotation mark in the secondary accent colour. Bold / accent styles. */
10833
11101
  const accentGlyphQuote = { style: "accent-glyph" };
10834
11102
  //#endregion
11103
+ //#region ../theme-core/src/gradients/types.ts
11104
+ /**
11105
+ * Gradient configuration types and schemas
11106
+ */
11107
+ /**
11108
+ * Gradient color stop
11109
+ */
11110
+ const gradientStopSchema = z$1.object({
11111
+ color: themeColorTokenSchema,
11112
+ position: z$1.number().min(0).max(100)
11113
+ });
11114
+ /**
11115
+ * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
11116
+ * so existing radial gradients are byte-identical.
11117
+ *
11118
+ * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
11119
+ * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
11120
+ */
11121
+ const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
11122
+ /**
11123
+ * Gradient configuration.
11124
+ *
11125
+ * `type` is optional and defaults to `'linear'` so gradients persisted without a
11126
+ * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
11127
+ * `'conic'` are additive kinds:
11128
+ * - linear: `angle` (degrees)
11129
+ * - radial: optional `shape` (circle/ellipse) + `position`
11130
+ * - conic: optional `angle` (from-angle, degrees) + `position`
11131
+ */
11132
+ const gradientConfigSchema = z$1.object({
11133
+ type: z$1.enum([
11134
+ "linear",
11135
+ "radial",
11136
+ "conic"
11137
+ ]).default("linear"),
11138
+ angle: z$1.number().min(0).max(360).optional(),
11139
+ shape: radialGradientShapeSchema.optional(),
11140
+ position: z$1.string().optional(),
11141
+ stops: z$1.array(gradientStopSchema).min(2)
11142
+ });
11143
+ /**
11144
+ * Curated library of gradient presets.
11145
+ * All gradients use theme color tokens, so they adapt to the site's palette.
11146
+ */
11147
+ const GRADIENT_PRESETS = [
11148
+ {
11149
+ id: "primary-secondary",
11150
+ name: "Primary to Secondary",
11151
+ description: "Blend your primary and secondary brand colors",
11152
+ type: "linear",
11153
+ angle: 135,
11154
+ stops: [{
11155
+ color: "primary-500",
11156
+ position: 0
11157
+ }, {
11158
+ color: "secondary-500",
11159
+ position: 100
11160
+ }]
11161
+ },
11162
+ {
11163
+ id: "primary-accent",
11164
+ name: "Primary to Accent",
11165
+ description: "Energetic gradient from primary to accent",
11166
+ type: "linear",
11167
+ angle: 135,
11168
+ stops: [{
11169
+ color: "primary-500",
11170
+ position: 0
11171
+ }, {
11172
+ color: "accent-500",
11173
+ position: 100
11174
+ }]
11175
+ },
11176
+ {
11177
+ id: "secondary-accent",
11178
+ name: "Secondary to Accent",
11179
+ description: "Vibrant secondary to accent blend",
11180
+ type: "linear",
11181
+ angle: 135,
11182
+ stops: [{
11183
+ color: "secondary-500",
11184
+ position: 0
11185
+ }, {
11186
+ color: "accent-500",
11187
+ position: 100
11188
+ }]
11189
+ },
11190
+ {
11191
+ id: "primary-light-dark",
11192
+ name: "Primary Light to Dark",
11193
+ description: "Subtle primary color fade",
11194
+ type: "linear",
11195
+ angle: 180,
11196
+ stops: [{
11197
+ color: "primary-500",
11198
+ position: 0
11199
+ }, {
11200
+ color: "primary-900",
11201
+ position: 100
11202
+ }]
11203
+ },
11204
+ {
11205
+ id: "primary-dark-light",
11206
+ name: "Primary Dark to Light",
11207
+ description: "Reverse primary fade",
11208
+ type: "linear",
11209
+ angle: 180,
11210
+ stops: [{
11211
+ color: "primary-900",
11212
+ position: 0
11213
+ }, {
11214
+ color: "primary-50",
11215
+ position: 100
11216
+ }]
11217
+ },
11218
+ {
11219
+ id: "secondary-light-dark",
11220
+ name: "Secondary Light to Dark",
11221
+ description: "Subtle secondary color fade",
11222
+ type: "linear",
11223
+ angle: 180,
11224
+ stops: [{
11225
+ color: "secondary-500",
11226
+ position: 0
11227
+ }, {
11228
+ color: "secondary-900",
11229
+ position: 100
11230
+ }]
11231
+ },
11232
+ {
11233
+ id: "accent-light-dark",
11234
+ name: "Accent Light to Dark",
11235
+ description: "Accent color depth fade",
11236
+ type: "linear",
11237
+ angle: 180,
11238
+ stops: [{
11239
+ color: "accent-500",
11240
+ position: 0
11241
+ }, {
11242
+ color: "accent-900",
11243
+ position: 100
11244
+ }]
11245
+ },
11246
+ {
11247
+ id: "background-surface",
11248
+ name: "Background to Surface",
11249
+ description: "Subtle depth using base colors",
11250
+ type: "linear",
11251
+ angle: 180,
11252
+ stops: [{
11253
+ color: "background",
11254
+ position: 0
11255
+ }, {
11256
+ color: "surface",
11257
+ position: 100
11258
+ }]
11259
+ },
11260
+ {
11261
+ id: "neutral-fade",
11262
+ name: "Neutral Gradient",
11263
+ description: "Elegant neutral color fade",
11264
+ type: "linear",
11265
+ angle: 180,
11266
+ stops: [{
11267
+ color: "neutral-900",
11268
+ position: 0
11269
+ }, {
11270
+ color: "neutral-50",
11271
+ position: 100
11272
+ }]
11273
+ },
11274
+ {
11275
+ id: "primary-glow",
11276
+ name: "Primary glow",
11277
+ description: "A soft top-centred glow using your primary palette",
11278
+ type: "radial",
11279
+ shape: "ellipse",
11280
+ position: "top center",
11281
+ stops: [{
11282
+ color: "primary-900",
11283
+ position: 0
11284
+ }, {
11285
+ color: "primary-950",
11286
+ position: 100
11287
+ }],
11288
+ foreground: {
11289
+ textColor: "onPrimary900",
11290
+ headingColor: "onPrimary900",
11291
+ linkColor: "onPrimary900",
11292
+ supportingColor: "onPrimary900"
11293
+ }
11294
+ },
11295
+ {
11296
+ id: "accent-glow",
11297
+ name: "Accent glow",
11298
+ description: "A soft top-centred glow using your accent palette",
11299
+ type: "radial",
11300
+ shape: "ellipse",
11301
+ position: "top center",
11302
+ stops: [{
11303
+ color: "accent-900",
11304
+ position: 0
11305
+ }, {
11306
+ color: "accent-950",
11307
+ position: 100
11308
+ }],
11309
+ foreground: {
11310
+ textColor: "onAccent900",
11311
+ headingColor: "onAccent900",
11312
+ linkColor: "onAccent900",
11313
+ supportingColor: "onAccent900"
11314
+ }
11315
+ },
11316
+ {
11317
+ id: "primary-secondary-accent",
11318
+ name: "Brand Trio",
11319
+ description: "All three brand colors in harmony",
11320
+ type: "linear",
11321
+ angle: 135,
11322
+ stops: [
11323
+ {
11324
+ color: "primary-500",
11325
+ position: 0
11326
+ },
11327
+ {
11328
+ color: "secondary-500",
11329
+ position: 50
11330
+ },
11331
+ {
11332
+ color: "accent-500",
11333
+ position: 100
11334
+ }
11335
+ ]
11336
+ }
11337
+ ].map((preset) => ({
11338
+ ...preset,
11339
+ id: preset.id,
11340
+ stops: preset.stops
11341
+ }));
11342
+ const GRADIENT_PRESET_IDS = GRADIENT_PRESETS.map((preset) => preset.id);
11343
+ const gradientPresetIdSet = new Set(GRADIENT_PRESET_IDS);
11344
+ const gradientPresetById = Object.fromEntries(GRADIENT_PRESETS.map((preset) => [preset.id, preset]));
11345
+ function parseGradientPresetId(value) {
11346
+ if (typeof value !== "string") return null;
11347
+ const normalized = value.trim();
11348
+ if (!gradientPresetIdSet.has(normalized)) return null;
11349
+ return normalized;
11350
+ }
11351
+ function getGradientPreset(id) {
11352
+ return gradientPresetById[id];
11353
+ }
11354
+ //#endregion
11355
+ //#region ../theme-core/src/gradients/utils.ts
11356
+ /**
11357
+ * Convert a GradientConfig to a CSS gradient string
11358
+ *
11359
+ * @param config - The gradient configuration using theme tokens
11360
+ * @param tokens - Map of theme color tokens to hex values
11361
+ * @returns CSS gradient string (e.g., "linear-gradient(180deg, #3b82f6 0%, #f59e0b 100%)")
11362
+ */
11363
+ function gradientToCss(config, tokens) {
11364
+ const stops = config.stops.map((stop) => {
11365
+ return `${tokens[stop.color] ?? "#000000"} ${stop.position}%`;
11366
+ }).join(", ");
11367
+ const type = config.type ?? "linear";
11368
+ if (type === "radial") {
11369
+ const position = config.position ?? "center";
11370
+ return `radial-gradient(${config.shape ?? "circle"} at ${position}, ${stops})`;
11371
+ }
11372
+ if (type === "conic") {
11373
+ const position = config.position ?? "center";
11374
+ return `conic-gradient(from ${config.angle ?? 0}deg at ${position}, ${stops})`;
11375
+ }
11376
+ return `linear-gradient(${config.angle ?? 180}deg, ${stops})`;
11377
+ }
11378
+ //#endregion
10835
11379
  //#region ../theme-core/src/site-styles/logoBranding.ts
10836
11380
  const logoTextConfidenceValues = [
10837
11381
  "low",
@@ -13153,14 +13697,12 @@ const backgroundOnHoverEffect = {
13153
13697
  ]
13154
13698
  },
13155
13699
  generateCSS: (ctx) => {
13156
- const { themeId, variantId, options, tokens } = ctx;
13700
+ const { themeId, variantId, options, tokens, buttonConfig } = ctx;
13157
13701
  const colorToken = options.colorToken || "neutral-800";
13158
- const color = tokens.getColor(colorToken);
13159
- const textColorToken = typeof options.textColorToken === "string" ? options.textColorToken : null;
13160
13702
  return `
13161
13703
  /* Add background on hover */
13162
13704
  :where([data-theme-scope="${themeId}"]) .${variantId}:hover {
13163
- background-color: ${color};${textColorToken ? `\n color: ${tokens.getColor(textColorToken)};` : ""}
13705
+ background-color: ${tokens.getColor(colorToken)};${"textColorToken" in buttonConfig && typeof options.textColorToken === "string" ? `\n color: ${tokens.getColor(options.textColorToken)};` : ""}
13164
13706
  }
13165
13707
  `;
13166
13708
  }
@@ -13184,10 +13726,10 @@ const backgroundOnHoverAlphaEffect = {
13184
13726
  default: 50
13185
13727
  },
13186
13728
  generateCSS: (ctx) => {
13187
- const { themeId, variantId, stateGroup, options, buttonConfig } = ctx;
13729
+ const { themeId, variantId, stateGroup, options } = ctx;
13188
13730
  const rawOpacity = options.opacity;
13189
13731
  const clampedOpacity = Math.max(0, Math.min(100, typeof rawOpacity === "number" && !Number.isNaN(rawOpacity) ? rawOpacity : 50));
13190
- const cssVar = `var(--tb-${options.colorToken || buttonConfig.textColorToken || "neutral-800"})`;
13732
+ const cssVar = `var(--tb-${options.colorToken || "neutral-800"})`;
13191
13733
  return `
13192
13734
  /* Add semi-transparent background (${stateGroup}) */
13193
13735
  :where([data-theme-scope="${themeId}"]) .${variantId}${stateGroup === "base" ? "" : `:${stateGroup}`} {
@@ -13400,7 +13942,7 @@ const outerGlowEffect = {
13400
13942
  const spread = options.spread || 10;
13401
13943
  let glowColor;
13402
13944
  if (options.glowColorToken && typeof options.glowColorToken === "string") glowColor = tokens.getColor(options.glowColorToken);
13403
- else if (buttonConfig.border?.colorToken) glowColor = tokens.getColor(buttonConfig.border.colorToken);
13945
+ else if (buttonConfig.border) glowColor = "colorToken" in buttonConfig.border ? tokens.getColor(buttonConfig.border.colorToken) : "currentColor";
13404
13946
  else if (buttonConfig.background.type === "solid") glowColor = tokens.getColor(buttonConfig.background.colorToken ?? "primary-400");
13405
13947
  else glowColor = tokens.getColor("primary-400");
13406
13948
  return `
@@ -13459,7 +14001,7 @@ const retro3DShadowEffect = {
13459
14001
  /**
13460
14002
  * Map Tailwind-style gradient direction tokens to CSS gradient directions
13461
14003
  */
13462
- const GRADIENT_DIRECTION_MAP$3 = {
14004
+ const GRADIENT_DIRECTION_MAP$2 = {
13463
14005
  "to-r": "to right",
13464
14006
  "to-l": "to left",
13465
14007
  "to-t": "to top",
@@ -14389,29 +14931,6 @@ function generateSingleEffectCSS(themeId, variantId, stateGroup, effectApp, elem
14389
14931
  };
14390
14932
  return effectPreset.generateCSS(ctx);
14391
14933
  }
14392
- /**
14393
- * Get contrasting text color for a button background
14394
- *
14395
- * This function returns theme-aware text colors using the neutral scale,
14396
- * which is guaranteed to exist in every theme and harmonizes with the design.
14397
- *
14398
- * Uses neutral-50 (lightest) for dark backgrounds and neutral-950 (darkest)
14399
- * for light backgrounds. This ensures excellent contrast while respecting
14400
- * the theme's neutral palette (warm/cool tinting).
14401
- *
14402
- * For solid backgrounds with primary/secondary/neutral colors:
14403
- * - Dark backgrounds (shade >= 500): neutral-50 (very light)
14404
- * - Light backgrounds (shade < 500): neutral-950 (very dark)
14405
- */
14406
- function getContrastingTextColorToken(backgroundToken, theme) {
14407
- const isLightTheme = theme.palette.meta.mode === "light";
14408
- if (backgroundToken.startsWith("primary-") || backgroundToken.startsWith("secondary-")) return "neutral-50";
14409
- if (backgroundToken.startsWith("neutral-")) {
14410
- const match = backgroundToken.match(/neutral-(\d+)/);
14411
- if (match?.[1]) return parseInt(match[1], 10) >= 500 ? "neutral-50" : "neutral-950";
14412
- }
14413
- return isLightTheme ? "neutral-50" : "neutral-950";
14414
- }
14415
14934
  //#endregion
14416
14935
  //#region ../theme-core/src/buttons/generateDefaultButtonSystem.ts
14417
14936
  /**
@@ -14469,13 +14988,8 @@ function generateDefaultButtonSystem(theme) {
14469
14988
  priority: 1,
14470
14989
  background: {
14471
14990
  type: "solid",
14472
- colorToken: "primary"
14473
- },
14474
- textColorToken: getContrastingTextColorToken("primary", theme),
14475
- effects: { hover: [{
14476
- effectId: "opacity-reduce",
14477
- options: { reduction: 15 }
14478
- }] }
14991
+ colorToken: unsafeAsOpaqueButtonColourToken("primary")
14992
+ }
14479
14993
  },
14480
14994
  {
14481
14995
  id: "secondary",
@@ -14484,51 +14998,39 @@ function generateDefaultButtonSystem(theme) {
14484
14998
  priority: 2,
14485
14999
  background: {
14486
15000
  type: "solid",
14487
- colorToken: "neutral-700"
14488
- },
14489
- textColorToken: getContrastingTextColorToken("neutral-700", theme),
14490
- effects: { hover: [{
14491
- effectId: "opacity-reduce",
14492
- options: { reduction: 15 }
14493
- }] }
15001
+ colorToken: unsafeAsOpaqueButtonColourToken("neutral-700")
15002
+ }
14494
15003
  },
14495
15004
  {
14496
15005
  id: "outline",
14497
15006
  name: "Outline",
14498
15007
  enabled: true,
14499
15008
  priority: 3,
14500
- background: { type: "transparent" },
14501
- textColorToken: "primary",
15009
+ background: {
15010
+ type: "surface",
15011
+ role: "link"
15012
+ },
14502
15013
  shadow: {
14503
15014
  elevation: "none",
14504
15015
  softness: null,
14505
15016
  position: "bottom"
14506
15017
  },
14507
- border: {
14508
- colorToken: "primary",
14509
- widthClass: borderWidthClass
14510
- },
14511
- effects: { hover: [{
14512
- effectId: "opacity-reduce",
14513
- options: { reduction: 15 }
14514
- }] }
15018
+ border: { widthClass: borderWidthClass }
14515
15019
  },
14516
15020
  {
14517
15021
  id: "ghost",
14518
15022
  name: "Ghost",
14519
15023
  enabled: true,
14520
15024
  priority: 3,
14521
- background: { type: "transparent" },
14522
- textColorToken: "primary",
15025
+ background: {
15026
+ type: "surface",
15027
+ role: "link"
15028
+ },
14523
15029
  shadow: {
14524
15030
  elevation: "none",
14525
15031
  softness: null,
14526
15032
  position: "bottom"
14527
- },
14528
- effects: { hover: [{
14529
- effectId: "opacity-reduce",
14530
- options: { reduction: 15 }
14531
- }] }
15033
+ }
14532
15034
  }
14533
15035
  ];
14534
15036
  return {
@@ -14584,6 +15086,472 @@ function affordanceIconMaskUrl(iconId) {
14584
15086
  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, " ")}`;
14585
15087
  }
14586
15088
  //#endregion
15089
+ //#region ../theme-core/src/surfaces/foregroundRoles.ts
15090
+ const SURFACE_FOREGROUND_ROLES = [
15091
+ "text",
15092
+ "heading",
15093
+ "link",
15094
+ "supporting"
15095
+ ];
15096
+ //#endregion
15097
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
15098
+ const SURFACE_FOREGROUND_CSS_VARS = {
15099
+ text: "--rb-surface-text-color",
15100
+ heading: "--rb-surface-heading-color",
15101
+ link: "--rb-surface-link-color",
15102
+ supporting: "--rb-surface-supporting-color"
15103
+ };
15104
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
15105
+ const DEFAULT_ROLE_FALLBACKS = {
15106
+ text: DEFAULT_SURFACE_TEXT_COLOR,
15107
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
15108
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
15109
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
15110
+ };
15111
+ function requireSurfaceColorValue(role, value) {
15112
+ const normalized = value.trim();
15113
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
15114
+ return normalized;
15115
+ }
15116
+ function completeSurfaceForegrounds(input) {
15117
+ const text = requireSurfaceColorValue("text", input.text);
15118
+ return {
15119
+ text,
15120
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
15121
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
15122
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
15123
+ };
15124
+ }
15125
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
15126
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
15127
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
15128
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
15129
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
15130
+ }
15131
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
15132
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
15133
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
15134
+ }
15135
+ function surfaceForegroundStyle(input) {
15136
+ const foregrounds = completeSurfaceForegrounds(input);
15137
+ return {
15138
+ color: foregrounds.text,
15139
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
15140
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
15141
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
15142
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
15143
+ };
15144
+ }
15145
+ function surfaceForegroundDeclarations(input) {
15146
+ const foregrounds = completeSurfaceForegrounds(input);
15147
+ return [`color: ${foregrounds.text}`, ...SURFACE_FOREGROUND_ROLES.map((role) => `${SURFACE_FOREGROUND_CSS_VARS[role]}: ${foregrounds[role]}`)];
15148
+ }
15149
+ //#endregion
15150
+ //#region ../theme-core/src/buttons/resolveButtonColourStates.ts
15151
+ const BUTTON_VISUAL_STATES = [
15152
+ "base",
15153
+ "hover",
15154
+ "focus",
15155
+ "active",
15156
+ "disabled"
15157
+ ];
15158
+ const BUTTON_CONTRAST_POLICY = {
15159
+ textMinimumRatio: 4.5,
15160
+ nonTextMinimumRatio: 3,
15161
+ gradientSamplesPerSegment: 20,
15162
+ alphaBackgrounds: "reject-without-known-surface",
15163
+ disabled: "retain-full-colour-contract"
15164
+ };
15165
+ function compileButtonColourStates(input) {
15166
+ const baseTransition = applyColourEffects({
15167
+ variantId: input.variant.id,
15168
+ state: "base",
15169
+ treatment: input.variant.background,
15170
+ effects: input.variant.effects?.base,
15171
+ palette: input.palette
15172
+ });
15173
+ if (!baseTransition.ok) return baseTransition;
15174
+ const stateTreatments = {
15175
+ base: baseTransition.treatment,
15176
+ hover: hoverTreatment(input.variant, input.global, baseTransition.treatment),
15177
+ focus: baseTransition.treatment,
15178
+ active: baseTransition.treatment,
15179
+ disabled: baseTransition.treatment
15180
+ };
15181
+ const nonColourEffects = { ...baseTransition.nonColourEffects.length > 0 ? { base: [...baseTransition.nonColourEffects] } : {} };
15182
+ for (const state of [
15183
+ "hover",
15184
+ "focus",
15185
+ "active"
15186
+ ]) {
15187
+ const transition = applyColourEffects({
15188
+ variantId: input.variant.id,
15189
+ state,
15190
+ treatment: stateTreatments[state],
15191
+ effects: input.variant.effects?.[state],
15192
+ palette: input.palette
15193
+ });
15194
+ if (!transition.ok) return transition;
15195
+ stateTreatments[state] = transition.treatment;
15196
+ if (transition.nonColourEffects.length > 0) nonColourEffects[state] = [...transition.nonColourEffects];
15197
+ }
15198
+ const resolved = {};
15199
+ for (const state of BUTTON_VISUAL_STATES) {
15200
+ const result = resolveTreatment$1({
15201
+ variantId: input.variant.id,
15202
+ state,
15203
+ treatment: stateTreatments[state],
15204
+ palette: input.palette
15205
+ });
15206
+ if (!result.ok) return result;
15207
+ resolved[state] = result.colour;
15208
+ }
15209
+ return {
15210
+ ok: true,
15211
+ states: resolved,
15212
+ focusIndicator: resolveFocusIndicator(resolved.focus),
15213
+ nonColourEffects
15214
+ };
15215
+ }
15216
+ function formatButtonColourContractError(error) {
15217
+ switch (error.tag) {
15218
+ case "unknown_palette_token": return `Button "${error.variantId}" ${error.state} state uses unknown palette token "${error.token}".`;
15219
+ case "missing_on_colour": return `Button "${error.variantId}" ${error.state} state cannot derive ${error.foregroundToken} from ${error.backgroundToken}.`;
15220
+ case "insufficient_contrast": return `Button "${error.variantId}" ${error.state} state has ${error.ratio.toFixed(2)}:1 contrast (${error.requiredRatio}:1 required).`;
15221
+ case "unsupported_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" is not contrast-safe (${error.reason}).`;
15222
+ case "invalid_colour_effect_options": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" has an invalid ${error.option} option.`;
15223
+ case "inapplicable_colour_effect": return `Button "${error.variantId}" ${error.state} effect "${error.effectId}" cannot apply to a ${error.treatment} treatment.`;
15224
+ case "unknown_button_effect": return `Button "${error.variantId}" ${error.state} uses unknown effect "${error.effectId}".`;
15225
+ }
15226
+ }
15227
+ function hoverTreatment(variant, global, fallback) {
15228
+ const token = variant.hoverBackgroundToken ?? (global.hoverColor === "token" ? global.hoverColorToken : void 0);
15229
+ return token === void 0 ? fallback : {
15230
+ type: "solid",
15231
+ colorToken: token
15232
+ };
15233
+ }
15234
+ function applyColourEffects(input) {
15235
+ let treatment = input.treatment;
15236
+ const nonColourEffects = [];
15237
+ for (const effect of input.effects ?? []) {
15238
+ const policy = getButtonEffectPolicy(effect.effectId);
15239
+ if (policy === null) return unknownEffect(input, effect);
15240
+ switch (policy.classification) {
15241
+ case "non-colour":
15242
+ nonColourEffects.push(effect);
15243
+ break;
15244
+ case "unsupported-colour":
15245
+ case "unsupported-background-overlay":
15246
+ case "not-button": return unsupportedEffect(input, effect, policy.reason);
15247
+ case "colour-transition":
15248
+ if (policy.transition.kind === "adjust-shade") {
15249
+ if (treatment.type !== "solid") return inapplicableEffect(input, effect, treatment);
15250
+ const amount = readShadeAmount(effect);
15251
+ if (amount === null) return invalidEffectOption(input, effect, "amount");
15252
+ const signedAmount = policy.transition.direction === "darker" ? amount : -amount;
15253
+ treatment = {
15254
+ type: "solid",
15255
+ colorToken: adjustShade(treatment.colorToken, signedAmount, input.palette)
15256
+ };
15257
+ break;
15258
+ }
15259
+ if (policy.transition.kind === "replace-background") {
15260
+ const colorToken = readOpaqueToken(effect, "colorToken", "neutral-800");
15261
+ if (colorToken === null) return invalidEffectOption(input, effect, "colorToken");
15262
+ treatment = {
15263
+ type: "solid",
15264
+ colorToken
15265
+ };
15266
+ break;
15267
+ }
15268
+ return unknownEffect(input, effect);
15269
+ }
15270
+ }
15271
+ return {
15272
+ ok: true,
15273
+ treatment,
15274
+ nonColourEffects
15275
+ };
15276
+ }
15277
+ function resolveTreatment$1(input) {
15278
+ const treatment = input.treatment;
15279
+ switch (treatment.type) {
15280
+ case "surface": return {
15281
+ ok: true,
15282
+ colour: {
15283
+ kind: "surface",
15284
+ role: treatment.role,
15285
+ backgroundCss: "transparent",
15286
+ foregroundCss: surfaceForegroundValue(treatment.role)
15287
+ }
15288
+ };
15289
+ case "solid": return resolveSolid(input, treatment.colorToken);
15290
+ case "gradient": return resolveGradient$2(input, treatment);
15291
+ }
15292
+ }
15293
+ function resolveFocusIndicator(colour) {
15294
+ switch (colour.kind) {
15295
+ case "solid": return {
15296
+ kind: "two-tone",
15297
+ innerCss: colour.foregroundCss,
15298
+ outerCss: colour.backgroundCss,
15299
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
15300
+ };
15301
+ case "gradient": return {
15302
+ kind: "two-tone",
15303
+ innerCss: colour.foregroundCss,
15304
+ outerCss: tokenCss(colour.backgroundTokens[0]),
15305
+ minimumPairContrast: BUTTON_CONTRAST_POLICY.textMinimumRatio
15306
+ };
15307
+ case "surface": return {
15308
+ kind: "surface-role",
15309
+ colorCss: colour.foregroundCss,
15310
+ minimumSurfaceContrast: BUTTON_CONTRAST_POLICY.nonTextMinimumRatio
15311
+ };
15312
+ }
15313
+ }
15314
+ function resolveSolid(input, backgroundToken) {
15315
+ const backgroundHex = paletteHex(input, backgroundToken);
15316
+ if (!backgroundHex.ok) return backgroundHex;
15317
+ const foregroundToken = unsafeAsOpaqueButtonColourToken(onColorTokenName(backgroundToken));
15318
+ const foregroundHex = input.palette[foregroundToken];
15319
+ if (typeof foregroundHex !== "string") return {
15320
+ ok: false,
15321
+ error: {
15322
+ tag: "missing_on_colour",
15323
+ variantId: input.variantId,
15324
+ state: input.state,
15325
+ backgroundToken,
15326
+ foregroundToken
15327
+ }
15328
+ };
15329
+ const contrast = validateContrast({
15330
+ ...input,
15331
+ background: backgroundToken,
15332
+ backgroundHex: backgroundHex.hex,
15333
+ foreground: foregroundToken,
15334
+ foregroundHex
15335
+ });
15336
+ if (!contrast.ok) return contrast;
15337
+ return {
15338
+ ok: true,
15339
+ colour: {
15340
+ kind: "solid",
15341
+ backgroundToken,
15342
+ foregroundToken,
15343
+ backgroundCss: tokenCss(backgroundToken),
15344
+ foregroundCss: tokenCss(foregroundToken)
15345
+ }
15346
+ };
15347
+ }
15348
+ function resolveGradient$2(input, treatment) {
15349
+ const foregroundHex = paletteHex(input, treatment.foregroundToken);
15350
+ if (!foregroundHex.ok) return foregroundHex;
15351
+ const stopHexes = [];
15352
+ for (const token of treatment.stops) {
15353
+ const stop = paletteHex(input, token);
15354
+ if (!stop.ok) return stop;
15355
+ stopHexes.push(stop.hex);
15356
+ }
15357
+ for (let segment = 0; segment < stopHexes.length - 1; segment += 1) {
15358
+ const start = stopHexes[segment];
15359
+ const end = stopHexes[segment + 1];
15360
+ const startToken = treatment.stops[segment];
15361
+ const endToken = treatment.stops[segment + 1];
15362
+ if (start === void 0 || end === void 0 || startToken === void 0 || endToken === void 0) continue;
15363
+ for (let sample = 0; sample <= BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment; sample += 1) {
15364
+ const position = sample / BUTTON_CONTRAST_POLICY.gradientSamplesPerSegment;
15365
+ const sampleHex = interpolateHex(start, end, position);
15366
+ const contrast = validateContrast({
15367
+ ...input,
15368
+ background: `${startToken}→${endToken}@${position.toFixed(2)}`,
15369
+ backgroundHex: sampleHex,
15370
+ foreground: treatment.foregroundToken,
15371
+ foregroundHex: foregroundHex.hex
15372
+ });
15373
+ if (!contrast.ok) return contrast;
15374
+ }
15375
+ }
15376
+ const direction = GRADIENT_DIRECTION_MAP$2[treatment.direction] ?? "to right";
15377
+ return {
15378
+ ok: true,
15379
+ colour: {
15380
+ kind: "gradient",
15381
+ backgroundTokens: treatment.stops,
15382
+ foregroundToken: treatment.foregroundToken,
15383
+ backgroundCss: `linear-gradient(${direction}, ${treatment.stops.map(tokenCss).join(", ")})`,
15384
+ foregroundCss: tokenCss(treatment.foregroundToken)
15385
+ }
15386
+ };
15387
+ }
15388
+ function paletteHex(input, token) {
15389
+ const hex = input.palette[token];
15390
+ return typeof hex === "string" ? {
15391
+ ok: true,
15392
+ hex
15393
+ } : {
15394
+ ok: false,
15395
+ error: {
15396
+ tag: "unknown_palette_token",
15397
+ variantId: input.variantId,
15398
+ state: input.state,
15399
+ token
15400
+ }
15401
+ };
15402
+ }
15403
+ function validateContrast(input) {
15404
+ const ratio = contrastRatio(input.backgroundHex, input.foregroundHex);
15405
+ return ratio >= BUTTON_CONTRAST_POLICY.textMinimumRatio ? { ok: true } : {
15406
+ ok: false,
15407
+ error: {
15408
+ tag: "insufficient_contrast",
15409
+ variantId: input.variantId,
15410
+ state: input.state,
15411
+ background: input.background,
15412
+ foreground: input.foreground,
15413
+ ratio,
15414
+ requiredRatio: BUTTON_CONTRAST_POLICY.textMinimumRatio
15415
+ }
15416
+ };
15417
+ }
15418
+ function tokenCss(token) {
15419
+ return `rgb(var(--tb-${token}))`;
15420
+ }
15421
+ function adjustShade(token, amount, palette) {
15422
+ const match = token.match(/^(.*)-(\d{2,3})$/);
15423
+ const base = match?.[1] ?? token;
15424
+ const currentShade = match?.[2] === void 0 ? 500 : Number(match[2]);
15425
+ const availableShades = [
15426
+ 50,
15427
+ 100,
15428
+ 200,
15429
+ 300,
15430
+ 400,
15431
+ 500,
15432
+ 600,
15433
+ 700,
15434
+ 800,
15435
+ 900,
15436
+ 950
15437
+ ].filter((shade) => typeof palette[`${base}-${shade}`] === "string");
15438
+ if (availableShades.length === 0) return token;
15439
+ const firstShade = availableShades[0];
15440
+ if (firstShade === void 0) return token;
15441
+ const exactIndex = availableShades.indexOf(currentShade);
15442
+ const startingIndex = exactIndex >= 0 ? exactIndex : availableShades.indexOf(availableShades.reduce((closestShade, shade) => Math.abs(shade - currentShade) < Math.abs(closestShade - currentShade) ? shade : closestShade, firstShade));
15443
+ const stepCount = Math.abs(amount) / 100;
15444
+ const direction = amount < 0 ? -1 : 1;
15445
+ const targetShade = availableShades[Math.max(0, Math.min(availableShades.length - 1, startingIndex + direction * stepCount))];
15446
+ return targetShade === void 0 ? token : unsafeAsOpaqueButtonColourToken(`${base}-${targetShade}`);
15447
+ }
15448
+ function readShadeAmount(effect) {
15449
+ const value = effect.options?.amount ?? 100;
15450
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value % 100 === 0 ? value : null;
15451
+ }
15452
+ function readOpaqueToken(effect, key, fallback) {
15453
+ return parseOpaqueButtonColourToken(effect.options?.[key] ?? fallback);
15454
+ }
15455
+ function unsupportedEffect(input, effect, reason) {
15456
+ return {
15457
+ ok: false,
15458
+ error: {
15459
+ tag: "unsupported_colour_effect",
15460
+ variantId: input.variantId,
15461
+ state: input.state,
15462
+ effectId: effect.effectId,
15463
+ reason
15464
+ }
15465
+ };
15466
+ }
15467
+ function invalidEffectOption(input, effect, option) {
15468
+ return {
15469
+ ok: false,
15470
+ error: {
15471
+ tag: "invalid_colour_effect_options",
15472
+ variantId: input.variantId,
15473
+ state: input.state,
15474
+ effectId: effect.effectId,
15475
+ option
15476
+ }
15477
+ };
15478
+ }
15479
+ function inapplicableEffect(input, effect, treatment) {
15480
+ return {
15481
+ ok: false,
15482
+ error: {
15483
+ tag: "inapplicable_colour_effect",
15484
+ variantId: input.variantId,
15485
+ state: input.state,
15486
+ effectId: effect.effectId,
15487
+ treatment: treatment.type
15488
+ }
15489
+ };
15490
+ }
15491
+ function unknownEffect(input, effect) {
15492
+ return {
15493
+ ok: false,
15494
+ error: {
15495
+ tag: "unknown_button_effect",
15496
+ variantId: input.variantId,
15497
+ state: input.state,
15498
+ effectId: effect.effectId
15499
+ }
15500
+ };
15501
+ }
15502
+ function interpolateHex(startHex, endHex, position) {
15503
+ const start = hexToRgb$1(startHex);
15504
+ const end = hexToRgb$1(endHex);
15505
+ const channel = (from, to) => Math.round(from + (to - from) * position).toString(16).padStart(2, "0");
15506
+ return `#${channel(start.r, end.r)}${channel(start.g, end.g)}${channel(start.b, end.b)}`;
15507
+ }
15508
+ //#endregion
15509
+ //#region ../theme-core/src/buttons/resolvedButtonCss.ts
15510
+ /**
15511
+ * Canonical colour declaration mapping shared by full themes and ThemeBridge.
15512
+ * Both background properties are always emitted so transitions cannot retain
15513
+ * a stale gradient or solid layer.
15514
+ */
15515
+ function resolvedButtonColourDeclarations(colour) {
15516
+ switch (colour.kind) {
15517
+ case "solid": return [
15518
+ ["background-color", colour.backgroundCss],
15519
+ ["background-image", "none"],
15520
+ ["color", colour.foregroundCss]
15521
+ ];
15522
+ case "gradient": return [
15523
+ ["background-color", "transparent"],
15524
+ ["background-image", colour.backgroundCss],
15525
+ ["color", colour.foregroundCss]
15526
+ ];
15527
+ case "surface": return [
15528
+ ["background-color", colour.backgroundCss],
15529
+ ["background-image", "none"],
15530
+ ["color", colour.foregroundCss]
15531
+ ];
15532
+ }
15533
+ }
15534
+ function resolvedButtonFocusDeclarations(indicator) {
15535
+ return indicator.kind === "two-tone" ? [
15536
+ ["outline", `2px solid ${indicator.innerCss}`],
15537
+ ["outline-offset", "2px"],
15538
+ ["box-shadow", `0 0 0 4px ${indicator.outerCss}`]
15539
+ ] : [["outline", `3px solid ${indicator.colorCss}`], ["outline-offset", "3px"]];
15540
+ }
15541
+ /**
15542
+ * Disabled buttons retain their validated colour pair and add a non-layout
15543
+ * visual affordance. `currentColor` keeps the cue inside the same contrast
15544
+ * relationship, while retaining pointer events lets the not-allowed cursor
15545
+ * remain observable.
15546
+ */
15547
+ const RESOLVED_BUTTON_DISABLED_DECLARATIONS = [
15548
+ ["cursor", "not-allowed"],
15549
+ ["transform", "none"],
15550
+ ["box-shadow", "none"],
15551
+ ["outline", "2px dashed currentColor"],
15552
+ ["outline-offset", "-4px"]
15553
+ ];
15554
+ //#endregion
14587
15555
  //#region ../theme-core/src/buttons/generateButtonCss.ts
14588
15556
  /**
14589
15557
  * Class the `button` runtime node adds when it renders its own trailing icon,
@@ -14600,7 +15568,7 @@ const LETTER_SPACING_MAP = {
14600
15568
  normal: "0",
14601
15569
  loose: "0.05em"
14602
15570
  };
14603
- function rule(selector, declarations) {
15571
+ function rule$1(selector, declarations) {
14604
15572
  return {
14605
15573
  selectors: typeof selector === "string" ? [selector] : selector,
14606
15574
  declarations
@@ -14650,6 +15618,7 @@ const STRUCTURAL_DECLARATIONS = [
14650
15618
  function generateButtonCss(options) {
14651
15619
  const { themeId, theme, buttonSystem: providedButtonSystem } = options;
14652
15620
  const tokens = new TokenResolver(theme);
15621
+ const palette = expandPalette(theme.palette);
14653
15622
  const buttonSystem = providedButtonSystem ?? generateDefaultButtonSystem(theme);
14654
15623
  const themeSizes = {
14655
15624
  ...DEFAULT_BUTTON_SIZES,
@@ -14658,34 +15627,109 @@ function generateButtonCss(options) {
14658
15627
  const enabledVariants = buttonSystem.variants.filter((v) => v.enabled);
14659
15628
  const aliasSource = resolveAliasSource(enabledVariants);
14660
15629
  const chunks = [];
14661
- for (const variant of enabledVariants) chunks.push(...variantChunks(variant, buttonSystem.global, themeSizes, themeId, tokens, theme));
15630
+ for (const variant of enabledVariants) {
15631
+ const compiled = compileRuntimeVariant(variant, buttonSystem.global, palette);
15632
+ if (compiled.fallbackError) chunks.push({ raw: `/* ${formatButtonColourContractError(compiled.fallbackError)} Using contrast-safe surface fallback. */` });
15633
+ chunks.push(...variantChunks(compiled.variant, compiled.global, themeSizes, themeId, tokens, theme, compiled.colours, compiled.focusIndicator, compiled.nonColourEffects));
15634
+ }
14662
15635
  if (aliasSource) {
14663
- const alias = {
15636
+ const compiled = compileRuntimeVariant({
14664
15637
  ...aliasSource,
14665
15638
  id: DEFAULT_VARIANT_ALIAS_ID,
14666
15639
  name: "Default"
14667
- };
14668
- chunks.push(...variantChunks(alias, buttonSystem.global, themeSizes, themeId, tokens, theme));
15640
+ }, buttonSystem.global, palette);
15641
+ if (compiled.fallbackError) chunks.push({ raw: `/* ${formatButtonColourContractError(compiled.fallbackError)} Using contrast-safe surface fallback. */` });
15642
+ chunks.push(...variantChunks(compiled.variant, compiled.global, themeSizes, themeId, tokens, theme, compiled.colours, compiled.focusIndicator, compiled.nonColourEffects));
14669
15643
  }
14670
15644
  return chunks.map(renderChunk).join("\n\n");
14671
15645
  }
15646
+ function compileRuntimeVariant(variant, global, palette) {
15647
+ const result = compileButtonColourStates({
15648
+ variant,
15649
+ global,
15650
+ palette
15651
+ });
15652
+ if (result.ok) return {
15653
+ variant,
15654
+ global,
15655
+ colours: result.states,
15656
+ focusIndicator: result.focusIndicator,
15657
+ nonColourEffects: result.nonColourEffects
15658
+ };
15659
+ const fallbackVariant = {
15660
+ ...variant,
15661
+ background: {
15662
+ type: "surface",
15663
+ role: "link"
15664
+ },
15665
+ hoverBackgroundToken: void 0,
15666
+ effects: void 0
15667
+ };
15668
+ const fallbackGlobal = {
15669
+ ...global,
15670
+ hoverColor: "none",
15671
+ hoverColorToken: void 0
15672
+ };
15673
+ const fallback = compileButtonColourStates({
15674
+ variant: fallbackVariant,
15675
+ global: fallbackGlobal,
15676
+ palette
15677
+ });
15678
+ if (!fallback.ok) {
15679
+ const colour = {
15680
+ kind: "surface",
15681
+ role: "link",
15682
+ backgroundCss: "transparent",
15683
+ foregroundCss: surfaceForegroundValue("link")
15684
+ };
15685
+ return {
15686
+ variant: fallbackVariant,
15687
+ global: fallbackGlobal,
15688
+ colours: {
15689
+ base: colour,
15690
+ hover: colour,
15691
+ focus: colour,
15692
+ active: colour,
15693
+ disabled: colour
15694
+ },
15695
+ focusIndicator: {
15696
+ kind: "surface-role",
15697
+ colorCss: colour.foregroundCss,
15698
+ minimumSurfaceContrast: BUTTON_CONTRAST_POLICY.nonTextMinimumRatio
15699
+ },
15700
+ nonColourEffects: {},
15701
+ fallbackError: result.error
15702
+ };
15703
+ }
15704
+ return {
15705
+ variant: fallbackVariant,
15706
+ global: fallbackGlobal,
15707
+ colours: fallback.states,
15708
+ focusIndicator: fallback.focusIndicator,
15709
+ nonColourEffects: fallback.nonColourEffects,
15710
+ fallbackError: result.error
15711
+ };
15712
+ }
14672
15713
  /** Pick the highest-priority variant whose role we'll alias to `.default`. */
14673
15714
  function resolveAliasSource(enabled) {
14674
15715
  if (enabled.some((v) => v.id === DEFAULT_VARIANT_ALIAS_ID)) return null;
14675
15716
  return enabled.slice().sort((a, b) => a.priority - b.priority)[0] ?? null;
14676
15717
  }
14677
- function variantChunks(variant, global, themeSizes, themeId, tokens, theme) {
15718
+ function variantChunks(variant, global, themeSizes, themeId, tokens, theme, colours, focusIndicator, nonColourEffects) {
14678
15719
  const out = [];
14679
15720
  const variantId = parseButtonVariantIdForCss(variant.id);
14680
15721
  const selector = `:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}`;
14681
- out.push(variantBaseRule(variant, selector, global, tokens, theme));
15722
+ out.push(variantBaseRule(variant, selector, global, tokens, theme, colours.base));
14682
15723
  out.push({ raw: generateInteractiveChildInheritanceCSS(selector) });
14683
- const hoverBg = variantHoverBackgroundRule(variant, variantId, global, themeId, tokens);
14684
- if (hoverBg) out.push(hoverBg);
14685
- out.push(variantFocusVisibleRule(variantId, themeId));
14686
- const effects = variantEffectsCss(variant, themeId, tokens, theme);
15724
+ for (const state of [
15725
+ "hover",
15726
+ "focus",
15727
+ "active"
15728
+ ]) if (!resolvedColoursEqual(colours.base, colours[state])) out.push(variantStateColourRule(variantId, themeId, state, colours[state]));
15729
+ const effects = variantEffectsCss(variant, nonColourEffects, themeId, tokens, theme);
14687
15730
  if (effects) out.push({ raw: effects });
14688
- const affordance = variantAffordanceRule(variant, selector, tokens);
15731
+ out.push(variantFocusVisibleRule(variantId, themeId, focusIndicator));
15732
+ const affordance = variantAffordanceRule(variant, selector);
14689
15733
  if (affordance) out.push(affordance);
14690
15734
  out.push(...variantDisabledRules(variantId, themeId));
14691
15735
  out.push(...variantSizeRules(variant, variantId, themeSizes, themeId));
@@ -14700,21 +15744,20 @@ function variantChunks(variant, global, themeSizes, themeId, tokens, theme) {
14700
15744
  * one (node overrides personality). Colour comes from the affordance token, or
14701
15745
  * `currentColor` (the button text colour) when no token is given.
14702
15746
  */
14703
- function variantAffordanceRule(variant, selector, tokens) {
15747
+ function variantAffordanceRule(variant, selector) {
14704
15748
  const affordance = variant.affordance;
14705
15749
  if (!affordance) return null;
14706
15750
  const maskUrl = affordanceIconMaskUrl(affordance.iconId);
14707
15751
  if (!maskUrl) return null;
14708
- const color = affordance.colorToken ? variant.background.type === "transparent" ? surfaceLinkColor(tokens.getColor(affordance.colorToken)) : tokens.getColor(affordance.colorToken) : "currentColor";
14709
15752
  const mask = `url("${maskUrl}") no-repeat center / contain`;
14710
- return rule(`${selector}:not(.${AFFORDANCE_SUPPRESS_CLASS})::after`, [
15753
+ return rule$1(`${selector}:not(.${AFFORDANCE_SUPPRESS_CLASS})::after`, [
14711
15754
  ["content", "\"\""],
14712
15755
  ["display", "inline-block"],
14713
15756
  ["width", "1em"],
14714
15757
  ["height", "1em"],
14715
15758
  ["margin-inline-start", "0.5em"],
14716
15759
  ["flex", "0 0 auto"],
14717
- ["background-color", color],
15760
+ ["background-color", "currentColor"],
14718
15761
  ["-webkit-mask", mask],
14719
15762
  ["mask", mask]
14720
15763
  ]);
@@ -14724,7 +15767,7 @@ function parseButtonVariantIdForCss(value) {
14724
15767
  if (!variantId) throw new Error("Button variant id must be a non-empty string");
14725
15768
  return variantId;
14726
15769
  }
14727
- function variantBaseRule(variant, selector, global, tokens, theme) {
15770
+ function variantBaseRule(variant, selector, global, tokens, theme, colour) {
14728
15771
  const decls = [];
14729
15772
  decls.push(["font-weight", String(global.fontWeight)]);
14730
15773
  decls.push(["font-family", resolveFontFamily(global)]);
@@ -14736,20 +15779,12 @@ function variantBaseRule(variant, selector, global, tokens, theme) {
14736
15779
  for (const decl of STRUCTURAL_DECLARATIONS) decls.push(decl);
14737
15780
  decls.push(["border-radius", resolveBorderRadius(variant, global)]);
14738
15781
  decls.push(["box-shadow", resolveBoxShadow(variant, global, theme, tokens)]);
14739
- if (variant.background.type === "solid") decls.push(["background-color", tokens.getColor(variant.background.colorToken)]);
14740
- else if (variant.background.type === "gradient") {
14741
- const colors = variant.background.stops.map((stop) => tokens.getColor(stop)).join(", ");
14742
- const direction = GRADIENT_DIRECTION_MAP$3[variant.background.direction] || "to right";
14743
- decls.push(["background-image", `linear-gradient(${direction}, ${colors})`]);
14744
- } else decls.push(["background-color", "transparent"]);
14745
- const foregroundColor = variant.background.type === "transparent" ? surfaceLinkColor(tokens.getColor(variant.textColorToken)) : tokens.getColor(variant.textColorToken);
14746
- decls.push(["color", foregroundColor]);
15782
+ decls.push(...resolvedButtonColourDeclarations(colour));
14747
15783
  if (variant.border) {
14748
15784
  const borderWidth = BORDER_WIDTH_MAP$4[variant.border.widthClass] || "1px";
14749
- const borderColor = variant.background.type === "transparent" ? surfaceLinkColor(tokens.getColor(variant.border.colorToken)) : tokens.getColor(variant.border.colorToken);
14750
- decls.push(["border", `${borderWidth} solid ${borderColor}`]);
15785
+ decls.push(["border", `${borderWidth} solid currentColor`]);
14751
15786
  }
14752
- return rule(selector, decls);
15787
+ return rule$1(selector, decls);
14753
15788
  }
14754
15789
  /**
14755
15790
  * Resolve a colour for a button that does not own its own background.
@@ -14758,30 +15793,22 @@ function variantBaseRule(variant, selector, global, tokens, theme) {
14758
15793
  * token fallback keeps buttons usable in contexts that do not provide that
14759
15794
  * contract (and preserves the existing theme behaviour there).
14760
15795
  */
14761
- function surfaceLinkColor(fallbackColor) {
14762
- return `var(--rb-surface-link-color, ${fallbackColor})`;
15796
+ function variantStateColourRule(variantId, themeId, state, colour) {
15797
+ return rule$1(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:${state}`, resolvedButtonColourDeclarations(colour));
14763
15798
  }
14764
- function variantHoverBackgroundRule(variant, variantId, global, themeId, tokens) {
14765
- let hoverToken = variant.hoverBackgroundToken;
14766
- if (!hoverToken && global.hoverColor === "token" && global.hoverColorToken) hoverToken = global.hoverColorToken;
14767
- if (!hoverToken) return null;
14768
- return rule(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:hover`, [["background-color", tokens.getColor(hoverToken)]]);
15799
+ function variantFocusVisibleRule(variantId, themeId, focusIndicator) {
15800
+ return rule$1(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:focus-visible`, resolvedButtonFocusDeclarations(focusIndicator));
14769
15801
  }
14770
- function variantFocusVisibleRule(variantId, themeId) {
14771
- return rule(`:where([data-theme-scope="${themeId}"]) ${themeButtonSelector(variantId)}:focus-visible`, [["outline", "2px solid currentColor"], ["outline-offset", "3px"]]);
14772
- }
14773
- function variantEffectsCss(variant, themeId, tokens, theme) {
14774
- if (!variant.effects) return "";
15802
+ function variantEffectsCss(variant, effects, themeId, tokens, theme) {
15803
+ if (!effects) return "";
14775
15804
  const elementConfig = {
14776
- background: variant.background,
14777
- textColorToken: variant.textColorToken,
14778
- border: variant.border ? { colorToken: variant.border.colorToken } : void 0,
15805
+ background: variant.background.type === "surface" ? { type: "transparent" } : variant.background,
14779
15806
  shadow: variant.shadow
14780
15807
  };
14781
15808
  return generateEffectsCSS({
14782
15809
  themeId,
14783
15810
  variantId: variant.id,
14784
- effects: variant.effects,
15811
+ effects,
14785
15812
  elementConfig,
14786
15813
  tokens,
14787
15814
  theme
@@ -14796,18 +15823,15 @@ function variantDisabledRules(variantId, themeId) {
14796
15823
  `${base}:focus`
14797
15824
  ];
14798
15825
  const pseudoSelectors = [`${base}::before`, `${base}::after`];
14799
- return [rule(disabledSelectors, [
14800
- ["opacity", "0.5"],
14801
- ["cursor", "not-allowed"],
14802
- ["pointer-events", "none"],
14803
- ["transform", "none"],
14804
- ["box-shadow", "none"]
14805
- ]), rule(pseudoSelectors, [
15826
+ return [rule$1(disabledSelectors, [...RESOLVED_BUTTON_DISABLED_DECLARATIONS]), rule$1(pseudoSelectors, [
14806
15827
  ["opacity", "0"],
14807
15828
  ["animation", "none"],
14808
15829
  ["pointer-events", "none"]
14809
15830
  ])];
14810
15831
  }
15832
+ function resolvedColoursEqual(left, right) {
15833
+ return JSON.stringify(left) === JSON.stringify(right);
15834
+ }
14811
15835
  function variantSizeRules(variant, variantId, themeSizes, themeId) {
14812
15836
  const borderWidth = variant.border ? BORDER_WIDTH_MAP$4[variant.border.widthClass] || "1px" : "0px";
14813
15837
  const rules = [];
@@ -14817,7 +15841,7 @@ function variantSizeRules(variant, variantId, themeSizes, themeId) {
14817
15841
  const decls = [];
14818
15842
  for (const decl of paddingDeclarations(config.padding, borderWidth)) decls.push(decl);
14819
15843
  if (config.fontSize) decls.push(["font-size", FONT_SIZE_MAP[config.fontSize] || "1rem"]);
14820
- rules.push(rule(selector, decls));
15844
+ rules.push(rule$1(selector, decls));
14821
15845
  }
14822
15846
  return rules;
14823
15847
  }
@@ -14875,6 +15899,27 @@ function resolveBoxShadow(variant, global, theme, tokens) {
14875
15899
  const color = shadowConfig.colorToken ? tokens.getColor(shadowConfig.colorToken) : void 0;
14876
15900
  return generateShadowValue$1(shadowConfig.elevation, shadowConfig.softness, shadowConfig.position ?? "bottom", color);
14877
15901
  }
15902
+ buttonPersonalities.map((personality) => ({
15903
+ id: personality.id,
15904
+ name: personality.name,
15905
+ description: personality.description,
15906
+ category: presetCategory(personality),
15907
+ buttonSystem: personality.buttonSystem,
15908
+ compatibility: {
15909
+ ...personality.tags.tone ? { tones: [personality.tags.tone] } : {},
15910
+ ...personality.tags.energy ? { energies: [personality.tags.energy] } : {}
15911
+ }
15912
+ }));
15913
+ function presetCategory(personality) {
15914
+ switch (personality.tags.era) {
15915
+ case "retro": return "retro";
15916
+ case "editorial": return "minimal";
15917
+ case "organic": return "artistic";
15918
+ case "modern": return personality.tags.tone === "bold" ? "modern" : "technical";
15919
+ case "technical": return "technical";
15920
+ case void 0: return "modern";
15921
+ }
15922
+ }
14878
15923
  //#endregion
14879
15924
  //#region ../theme-core/src/cards/types.ts
14880
15925
  const cardVariantRoleValues = ["default", "alternative"];
@@ -15111,67 +16156,6 @@ function generateDefaultCardSystem(theme) {
15111
16156
  return { variants: [defaultVariant, alternativeVariant] };
15112
16157
  }
15113
16158
  //#endregion
15114
- //#region ../theme-core/src/surfaces/foregroundRoles.ts
15115
- const SURFACE_FOREGROUND_ROLES = [
15116
- "text",
15117
- "heading",
15118
- "link",
15119
- "supporting"
15120
- ];
15121
- //#endregion
15122
- //#region ../theme-core/src/surfaces/foregroundContract.ts
15123
- const SURFACE_FOREGROUND_CSS_VARS = {
15124
- text: "--rb-surface-text-color",
15125
- heading: "--rb-surface-heading-color",
15126
- link: "--rb-surface-link-color",
15127
- supporting: "--rb-surface-supporting-color"
15128
- };
15129
- const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
15130
- const DEFAULT_ROLE_FALLBACKS = {
15131
- text: DEFAULT_SURFACE_TEXT_COLOR,
15132
- heading: DEFAULT_SURFACE_TEXT_COLOR,
15133
- link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
15134
- supporting: "rgb(var(--tb-mutedText, 100 116 139))"
15135
- };
15136
- function requireSurfaceColorValue(role, value) {
15137
- const normalized = value.trim();
15138
- if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
15139
- return normalized;
15140
- }
15141
- function completeSurfaceForegrounds(input) {
15142
- const text = requireSurfaceColorValue("text", input.text);
15143
- return {
15144
- text,
15145
- heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
15146
- link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
15147
- supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
15148
- };
15149
- }
15150
- function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
15151
- const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
15152
- const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
15153
- if (role === "text") return `var(${roleVariable}, ${fallback})`;
15154
- return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
15155
- }
15156
- function surfaceForegroundValueWithOpacity(role, opacityPercent) {
15157
- if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
15158
- return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
15159
- }
15160
- function surfaceForegroundStyle(input) {
15161
- const foregrounds = completeSurfaceForegrounds(input);
15162
- return {
15163
- color: foregrounds.text,
15164
- [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
15165
- [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
15166
- [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
15167
- [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
15168
- };
15169
- }
15170
- function surfaceForegroundDeclarations(input) {
15171
- const foregrounds = completeSurfaceForegrounds(input);
15172
- return [`color: ${foregrounds.text}`, ...SURFACE_FOREGROUND_ROLES.map((role) => `${SURFACE_FOREGROUND_CSS_VARS[role]}: ${foregrounds[role]}`)];
15173
- }
15174
- //#endregion
15175
16159
  //#region ../theme-core/src/cards/generateCardCss.ts
15176
16160
  const PER_CORNER_RADIUS_MAP$1 = {
15177
16161
  square: "0",
@@ -15608,47 +16592,6 @@ const FONT_WEIGHT_MAP = {
15608
16592
  black: "800"
15609
16593
  };
15610
16594
  //#endregion
15611
- //#region ../theme-core/src/gradients/types.ts
15612
- /**
15613
- * Gradient configuration types and schemas
15614
- */
15615
- /**
15616
- * Gradient color stop
15617
- */
15618
- const gradientStopSchema = z$1.object({
15619
- color: themeColorTokenSchema,
15620
- position: z$1.number().min(0).max(100)
15621
- });
15622
- /**
15623
- * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
15624
- * so existing radial gradients are byte-identical.
15625
- *
15626
- * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
15627
- * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
15628
- */
15629
- const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
15630
- /**
15631
- * Gradient configuration.
15632
- *
15633
- * `type` is optional and defaults to `'linear'` so gradients persisted without a
15634
- * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
15635
- * `'conic'` are additive kinds:
15636
- * - linear: `angle` (degrees)
15637
- * - radial: optional `shape` (circle/ellipse) + `position`
15638
- * - conic: optional `angle` (from-angle, degrees) + `position`
15639
- */
15640
- const gradientConfigSchema = z$1.object({
15641
- type: z$1.enum([
15642
- "linear",
15643
- "radial",
15644
- "conic"
15645
- ]).default("linear"),
15646
- angle: z$1.number().min(0).max(360).optional(),
15647
- shape: radialGradientShapeSchema.optional(),
15648
- position: z$1.string().optional(),
15649
- stops: z$1.array(gradientStopSchema).min(2)
15650
- });
15651
- //#endregion
15652
16595
  //#region ../theme-core/src/interactive/baseSchema.ts
15653
16596
  /**
15654
16597
  * Shared Base Schemas for Interactive Elements
@@ -17399,6 +18342,21 @@ z$1.object({
17399
18342
  blocks: z$1.array(blockDesignPlanSchema),
17400
18343
  styleAuthorities: z$1.array(blockRuntimeStyleAuthoritySchema)
17401
18344
  }).strict();
18345
+ //#endregion
18346
+ //#region ../theme-core/src/site-styles/runtimeCompiler.ts
18347
+ function defineSectionForegroundRuntimeTokens(input) {
18348
+ return {
18349
+ textColor: requireThemeColorToken(input.textColor),
18350
+ headingColor: requireThemeColorToken(input.headingColor),
18351
+ linkColor: requireThemeColorToken(input.linkColor),
18352
+ supportingColor: requireThemeColorToken(input.supportingColor)
18353
+ };
18354
+ }
18355
+ function requireThemeColorToken(value) {
18356
+ const token = parseThemeColorToken(value);
18357
+ if (!token) throw new Error(`Invalid section foreground Theme color token: ${String(value)}`);
18358
+ return token;
18359
+ }
17402
18360
  function defineDesignRuntimeRules(rules) {
17403
18361
  return rules;
17404
18362
  }
@@ -17669,24 +18627,24 @@ const kirtanOrnateDesignRuntimeRules = defineDesignRuntimeRules({
17669
18627
  type: "color",
17670
18628
  color: "muted"
17671
18629
  },
17672
- foreground: {
18630
+ foreground: defineSectionForegroundRuntimeTokens({
17673
18631
  textColor: "text",
17674
18632
  headingColor: "primary",
17675
18633
  linkColor: "primary",
17676
18634
  supportingColor: "text"
17677
- }
18635
+ })
17678
18636
  },
17679
18637
  "brand-depth-band": {
17680
18638
  background: {
17681
18639
  type: "color",
17682
18640
  color: "primary-800"
17683
18641
  },
17684
- foreground: {
18642
+ foreground: defineSectionForegroundRuntimeTokens({
17685
18643
  textColor: "text",
17686
18644
  headingColor: "primary",
17687
18645
  linkColor: "primary",
17688
18646
  supportingColor: "text"
17689
- }
18647
+ })
17690
18648
  },
17691
18649
  "hero-image": {
17692
18650
  background: {
@@ -17694,12 +18652,32 @@ const kirtanOrnateDesignRuntimeRules = defineDesignRuntimeRules({
17694
18652
  opacity: 1,
17695
18653
  position: "center center"
17696
18654
  },
17697
- foreground: {
18655
+ foreground: defineSectionForegroundRuntimeTokens({
17698
18656
  textColor: "text",
17699
18657
  headingColor: "primary",
17700
18658
  linkColor: "primary",
17701
18659
  supportingColor: "text"
17702
- }
18660
+ })
18661
+ }
18662
+ }
18663
+ });
18664
+ const primaryGlowGradientPresetId = parseGradientPresetId("primary-glow");
18665
+ if (!primaryGlowGradientPresetId) throw new Error("Expected primary-glow in the Theme Core gradient catalog");
18666
+ const fulcrumDesignRuntimeRules = defineDesignRuntimeRules({
18667
+ ...defaultDesignRuntimeRules,
18668
+ sectionSurfaces: {
18669
+ ...defaultDesignRuntimeRules.sectionSurfaces,
18670
+ "accent-band": {
18671
+ background: {
18672
+ type: "gradient",
18673
+ gradientPresetId: primaryGlowGradientPresetId
18674
+ },
18675
+ foreground: defineSectionForegroundRuntimeTokens({
18676
+ textColor: "primaryForeground",
18677
+ headingColor: "primaryForeground",
18678
+ linkColor: "primaryForeground",
18679
+ supportingColor: "primaryForeground"
18680
+ })
17703
18681
  }
17704
18682
  }
17705
18683
  });
@@ -19087,6 +20065,8 @@ const curatedSiteStyles = [
19087
20065
  },
19088
20066
  compositionBudget: quietBudget,
19089
20067
  typography: fulcrumTypography,
20068
+ designRuntimeRules: fulcrumDesignRuntimeRules,
20069
+ recommendedAppearancePresetIds: ["cta-accent-band"],
19090
20070
  themeOverrides: {
19091
20071
  shadow: { colorToken: "primary" },
19092
20072
  accordions: {
@@ -22555,9 +23535,15 @@ function createButtonGroup(options = {}) {
22555
23535
  //#endregion
22556
23536
  //#region ../blocks/src/system/fields/ctas.ts
22557
23537
  function createCtasRepeater(options = {}) {
22558
- const { id = "ctas", label = "Calls to action", itemLabel = "CTA", minItems = 0, maxItems, button } = options;
23538
+ const { id = "ctas", label = "Calls to action", itemLabel = "CTA", minItems = 0, maxItems, button, defaultValue } = options;
23539
+ const fieldId = id;
23540
+ const buttonField = createButtonGroup({
23541
+ showGroupLabel: false,
23542
+ ...button ?? {},
23543
+ flattenInRepeater: true
23544
+ });
22559
23545
  return {
22560
- id,
23546
+ id: fieldId,
22561
23547
  type: "repeater",
22562
23548
  label,
22563
23549
  itemLabel,
@@ -22566,11 +23552,8 @@ function createCtasRepeater(options = {}) {
22566
23552
  maxItems,
22567
23553
  required: false,
22568
23554
  allowConversion: true,
22569
- schema: { fields: [createButtonGroup({
22570
- showGroupLabel: false,
22571
- ...button ?? {},
22572
- flattenInRepeater: true
22573
- })] }
23555
+ ...defaultValue !== void 0 ? { defaultValue: [...defaultValue] } : {},
23556
+ schema: { fields: [buttonField] }
22574
23557
  };
22575
23558
  }
22576
23559
  //#endregion
@@ -22620,6 +23603,7 @@ const heroCtaRowFragment = defineFragment({
22620
23603
  label: "Calls to action",
22621
23604
  itemLabel: "CTA",
22622
23605
  maxItems: 3,
23606
+ defaultValue: [],
22623
23607
  button: {
22624
23608
  variants: [
22625
23609
  {
@@ -22687,7 +23671,7 @@ const ctaCopyFragment = defineFragment({
22687
23671
  eyebrowPath: "content.eyebrow",
22688
23672
  titlePath: "content.title",
22689
23673
  className: "cta-heading"
22690
- }), richText({ className: "cta-content rb-prose rb-mx-auto rb-max-w-none" }, when("content.content"), bind("content.content"))]
23674
+ }), richText({ className: "cta-content rb-prose rb-mx-auto rb-max-w-none" }, when("content"), bind("content"))]
22691
23675
  });
22692
23676
  //#endregion
22693
23677
  //#region ../blocks/src/system/fragments/library/testimonialsHeading.ts
@@ -24904,6 +25888,7 @@ function createBlockManifest(config) {
24904
25888
  previewCustomize: config.previewCustomize,
24905
25889
  category: config.category,
24906
25890
  contentTypes: config.contentTypes ? [...config.contentTypes] : void 0,
25891
+ contentSource: config.contentSource,
24907
25892
  tags: config.tags ? [...config.tags] : [],
24908
25893
  icon: config.icon ?? "Box",
24909
25894
  layout,
@@ -25049,6 +26034,7 @@ const heroBlockDefinition = { manifest: createBlockManifest({
25049
26034
  title: "Hero",
25050
26035
  titleSource: "headline",
25051
26036
  category: "layout",
26037
+ contentSource: { kind: "inline" },
25052
26038
  includeContainerStyles: true,
25053
26039
  promotedFields: ["_sectionStyles.background.image"],
25054
26040
  fragments: [
@@ -25322,6 +26308,7 @@ const introBlockDefinition = { manifest: createBlockManifest({
25322
26308
  id: "block.intro",
25323
26309
  title: "Intro",
25324
26310
  category: "layout",
26311
+ contentSource: { kind: "inline" },
25325
26312
  includeContainerStyles: true,
25326
26313
  titleSource: "title",
25327
26314
  previewCustomize: { quickFieldPriority: [
@@ -25507,6 +26494,7 @@ const bodyTextBlockDefinition = { manifest: createBlockManifest({
25507
26494
  title: "Body Text",
25508
26495
  titleSource: "heading",
25509
26496
  category: "content",
26497
+ contentSource: { kind: "inline" },
25510
26498
  fragments: [{
25511
26499
  fragment: bodyCopyFragment,
25512
26500
  fieldPriority: 0
@@ -25563,6 +26551,10 @@ const blogPostBlockDefinition = {
25563
26551
  description: "Highlights a single blog post with title, image, and excerpt.",
25564
26552
  category: "blog",
25565
26553
  contentTypes: ["post"],
26554
+ contentSource: {
26555
+ kind: "entries",
26556
+ contentType: "post"
26557
+ },
25566
26558
  tags: [
25567
26559
  "blog",
25568
26560
  "post",
@@ -25860,6 +26852,10 @@ const blogListingManifest = createBlockManifest({
25860
26852
  ],
25861
26853
  description: "Display a collection of blog posts in grid or stack layout.",
25862
26854
  contentTypes: ["post"],
26855
+ contentSource: {
26856
+ kind: "entries",
26857
+ contentType: "post"
26858
+ },
25863
26859
  tags: [
25864
26860
  "blog",
25865
26861
  "posts",
@@ -25902,6 +26898,7 @@ const ctaFullBlockDefinition = { manifest: createBlockManifest({
25902
26898
  id: "block.cta-full",
25903
26899
  title: "Full-width CTA",
25904
26900
  category: "marketing",
26901
+ contentSource: { kind: "inline" },
25905
26902
  fragments: [{
25906
26903
  fragment: ctaCopyFragment,
25907
26904
  fieldPriority: 0
@@ -25935,6 +26932,7 @@ const singleButtonBlockDefinition = { manifest: createBlockManifest({
25935
26932
  id: "block.single-button",
25936
26933
  title: "Single Button",
25937
26934
  category: "marketing",
26935
+ contentSource: { kind: "inline" },
25938
26936
  fragments: [{
25939
26937
  fragment: singleButtonFragment,
25940
26938
  fieldPriority: 0
@@ -26217,6 +27215,7 @@ const formManifest = createBlockManifest({
26217
27215
  paletteHidden: false
26218
27216
  },
26219
27217
  category: "interactive",
27218
+ contentSource: { kind: "inline" },
26220
27219
  tags: [
26221
27220
  "form",
26222
27221
  "contact",
@@ -26279,6 +27278,7 @@ const contactNewsletterBlockDefinition = {
26279
27278
  titleSource: "contactTitle",
26280
27279
  description: "Two-column newsletter signup and contact form section.",
26281
27280
  category: "interactive",
27281
+ contentSource: { kind: "inline" },
26282
27282
  layout: contactNewsletterLayout,
26283
27283
  defaultVariant: "default",
26284
27284
  variants: { default: contactNewsletterLayout },
@@ -26482,6 +27482,7 @@ const faqBlockDefinition = { manifest: createBlockManifest({
26482
27482
  title: "FAQ",
26483
27483
  titleSource: "title",
26484
27484
  category: "content",
27485
+ contentSource: { kind: "inline" },
26485
27486
  includeContainerStyles: true,
26486
27487
  fragments: [{
26487
27488
  fragment: faqHeadingFragment,
@@ -26782,7 +27783,7 @@ const heroTitleLengthThresholds = {
26782
27783
  maxWords: 11
26783
27784
  }
26784
27785
  };
26785
- function isPlainObject$3(value) {
27786
+ function isPlainObject$4(value) {
26786
27787
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
26787
27788
  }
26788
27789
  function readString$1(obj, key) {
@@ -26791,7 +27792,7 @@ function readString$1(obj, key) {
26791
27792
  }
26792
27793
  function resolveGeneratedHeroLayoutStyles(content) {
26793
27794
  const raw = content._heroLayoutStyles;
26794
- return isPlainObject$3(raw) ? raw : null;
27795
+ return isPlainObject$4(raw) ? raw : null;
26795
27796
  }
26796
27797
  function readHeroLayoutString(content, generatedKey, fallbackKey = generatedKey) {
26797
27798
  const explicitValue = readString$1(content, fallbackKey);
@@ -26847,7 +27848,7 @@ const heroContentWrapperClassTransform = {
26847
27848
  summary: "Resolve hero content wrapper className from alignment + max width controls",
26848
27849
  schema: z$1.object({ base: z$1.string().optional() }),
26849
27850
  run: (value, options, context) => {
26850
- const content = isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {};
27851
+ const content = isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {};
26851
27852
  const alignment = resolveHeroContentAlignment(content);
26852
27853
  const maxWidth = resolveHeroContentMaxWidth(content);
26853
27854
  const typographyScale = resolveHeroTypographyScale(content);
@@ -26870,7 +27871,7 @@ const heroCtaJustifyTransform = {
26870
27871
  kind: "string",
26871
27872
  summary: "Map hero contentAlignment to CTA row justify value",
26872
27873
  run: (value, _options, context) => {
26873
- const alignment = resolveHeroContentAlignment(isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {});
27874
+ const alignment = resolveHeroContentAlignment(isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {});
26874
27875
  return alignment === "left" ? "start" : alignment === "right" ? "end" : "center";
26875
27876
  }
26876
27877
  };
@@ -26879,7 +27880,7 @@ const heroStackJustifyFromVerticalAlignmentTransform = {
26879
27880
  kind: "string",
26880
27881
  summary: "Map hero verticalAlignment to stack justify value",
26881
27882
  run: (value, _options, context) => {
26882
- const vertical = resolveHeroVerticalAlignment(isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {});
27883
+ const vertical = resolveHeroVerticalAlignment(isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {});
26883
27884
  return vertical === "top" ? "start" : vertical === "bottom" ? "end" : "center";
26884
27885
  }
26885
27886
  };
@@ -26889,7 +27890,7 @@ const heroGridClassFromVerticalAlignmentTransform = {
26889
27890
  summary: "Append align-items class for hero split layouts based on verticalAlignment",
26890
27891
  schema: z$1.object({ base: z$1.string() }),
26891
27892
  run: (value, options, context) => {
26892
- const vertical = resolveHeroVerticalAlignment(isPlainObject$3(value) ? value : isPlainObject$3(context.data) ? context.data : {});
27893
+ const vertical = resolveHeroVerticalAlignment(isPlainObject$4(value) ? value : isPlainObject$4(context.data) ? context.data : {});
26893
27894
  const itemsClass = vertical === "top" ? "rb-items-start" : vertical === "bottom" ? "rb-items-end" : "rb-items-center";
26894
27895
  return joinClasses$3([options.base, itemsClass]);
26895
27896
  }
@@ -28206,6 +29207,7 @@ const siteHeaderManifest = createBlockManifest({
28206
29207
  id: "block.site-header",
28207
29208
  title: "Site Header",
28208
29209
  category: "layout",
29210
+ contentSource: { kind: "inline" },
28209
29211
  additionalFields: [{
28210
29212
  id: "mode",
28211
29213
  type: "select",
@@ -28411,6 +29413,7 @@ const siteFooterManifest = createBlockManifest({
28411
29413
  id: "block.site-footer",
28412
29414
  title: "Site Footer",
28413
29415
  category: "layout",
29416
+ contentSource: { kind: "inline" },
28414
29417
  skipSectionStyles: true,
28415
29418
  layout: section$1({
28416
29419
  background: "background",
@@ -28561,6 +29564,7 @@ const testimonialsBlockDefinition = {
28561
29564
  id: "block.testimonials",
28562
29565
  title: "Testimonials",
28563
29566
  category: "marketing",
29567
+ contentSource: { kind: "inline" },
28564
29568
  includeContainerStyles: true,
28565
29569
  previewCustomize: { quickFieldPriority: [
28566
29570
  "avatarLayout",
@@ -28719,6 +29723,7 @@ const columnsBlockDefinition = { manifest: createBlockManifest({
28719
29723
  id: "block.columns",
28720
29724
  title: "Columns",
28721
29725
  category: "layout",
29726
+ contentSource: { kind: "inline" },
28722
29727
  additionalFields: [
28723
29728
  parseFieldDefinition({
28724
29729
  id: "equalHeight",
@@ -28952,6 +29957,7 @@ const collectionBlockDefinition = { manifest: createBlockManifest({
28952
29957
  id: "block.collection",
28953
29958
  title: "Collection",
28954
29959
  category: "layout",
29960
+ contentSource: { kind: "inline" },
28955
29961
  additionalFields: [
28956
29962
  parseFieldDefinition({
28957
29963
  id: "layout",
@@ -29239,6 +30245,7 @@ const featureCardsBlockDefinition = { manifest: createBlockManifest({
29239
30245
  id: "block.feature-cards",
29240
30246
  title: "Feature Cards",
29241
30247
  category: "layout",
30248
+ contentSource: { kind: "inline" },
29242
30249
  includeContainerStyles: true,
29243
30250
  titleSource: "title",
29244
30251
  additionalFields: [
@@ -29622,6 +30629,7 @@ const mediaTextBlockDefinition = { manifest: createBlockManifest({
29622
30629
  id: "block.media-text",
29623
30630
  title: "Media and Text",
29624
30631
  category: "layout",
30632
+ contentSource: { kind: "inline" },
29625
30633
  includeContainerStyles: true,
29626
30634
  titleSource: "title",
29627
30635
  previewCustomize: { quickFieldPriority: [
@@ -30022,6 +31030,7 @@ const appointmentBookingBlockDefinition = {
30022
31030
  paletteHidden: false
30023
31031
  },
30024
31032
  category: "interactive",
31033
+ contentSource: { kind: "inline" },
30025
31034
  tags: [
30026
31035
  "booking",
30027
31036
  "appointment",
@@ -30186,6 +31195,7 @@ const weeklySlotAvailabilityManifest = createBlockManifest({
30186
31195
  paletteHidden: false
30187
31196
  },
30188
31197
  category: "interactive",
31198
+ contentSource: { kind: "inline" },
30189
31199
  tags: [
30190
31200
  "booking",
30191
31201
  "appointment",
@@ -31881,6 +32891,7 @@ const eventRegistrationBlockDefinition = {
31881
32891
  titleSource: "heading",
31882
32892
  description: "Multi-step event registration form",
31883
32893
  category: "interactive",
32894
+ contentSource: { kind: "inline" },
31884
32895
  tags: [
31885
32896
  "events",
31886
32897
  "registration",
@@ -31987,15 +32998,9 @@ const eventRegistrationBlockDefinition = {
31987
32998
  defaultValue: "Complete Registration",
31988
32999
  maxLength: 40
31989
33000
  },
31990
- {
31991
- id: "buttonVariant",
31992
- type: "select",
31993
- label: "Button style",
33001
+ createButtonVariantField({
31994
33002
  description: "Choose a button style from your theme",
31995
- required: false,
31996
- multiple: false,
31997
- defaultValue: "primary",
31998
- options: [
33003
+ variants: [
31999
33004
  {
32000
33005
  value: "primary",
32001
33006
  label: "Primary"
@@ -32008,9 +33013,8 @@ const eventRegistrationBlockDefinition = {
32008
33013
  value: "outline",
32009
33014
  label: "Outline"
32010
33015
  }
32011
- ],
32012
- ui: { intent: "style" }
32013
- }
33016
+ ]
33017
+ })
32014
33018
  ],
32015
33019
  layout: [styledSection({
32016
33020
  baseClass: "",
@@ -32385,6 +33389,7 @@ const courseRegistrationBlockDefinition = {
32385
33389
  titleSource: "heading",
32386
33390
  description: "Enrollment form for multi-session courses",
32387
33391
  category: "interactive",
33392
+ contentSource: { kind: "inline" },
32388
33393
  tags: [
32389
33394
  "courses",
32390
33395
  "enrollment",
@@ -32498,15 +33503,9 @@ const courseRegistrationBlockDefinition = {
32498
33503
  defaultValue: "Sign up",
32499
33504
  maxLength: 40
32500
33505
  },
32501
- {
32502
- id: "buttonVariant",
32503
- type: "select",
32504
- label: "Button style",
33506
+ createButtonVariantField({
32505
33507
  description: "Choose a button style from your theme",
32506
- required: false,
32507
- multiple: false,
32508
- defaultValue: "primary",
32509
- options: [
33508
+ variants: [
32510
33509
  {
32511
33510
  value: "primary",
32512
33511
  label: "Primary"
@@ -32519,9 +33518,8 @@ const courseRegistrationBlockDefinition = {
32519
33518
  value: "outline",
32520
33519
  label: "Outline"
32521
33520
  }
32522
- ],
32523
- ui: { intent: "style" }
32524
- }
33521
+ ]
33522
+ })
32525
33523
  ],
32526
33524
  layout: [styledSection({
32527
33525
  baseClass: "",
@@ -32601,6 +33599,7 @@ const eventDetailsManifest = createBlockManifest({
32601
33599
  titleSource: "heading",
32602
33600
  description: "Date/time, venue, map, and other dates for an event",
32603
33601
  category: "content",
33602
+ contentSource: { kind: "entries" },
32604
33603
  tags: [
32605
33604
  "events",
32606
33605
  "venue",
@@ -32806,6 +33805,7 @@ const courseDetailsBlockDefinition = {
32806
33805
  titleSource: "heading",
32807
33806
  description: "Schedule, venue, price, and enrollment status for a course",
32808
33807
  category: "content",
33808
+ contentSource: { kind: "entries" },
32809
33809
  tags: [
32810
33810
  "courses",
32811
33811
  "schedule",
@@ -33097,6 +34097,7 @@ const eventSpotlightBlockDefinition = {
33097
34097
  paletteHidden: false
33098
34098
  },
33099
34099
  category: "content",
34100
+ contentSource: { kind: "entries" },
33100
34101
  tags: [
33101
34102
  "events",
33102
34103
  "featured",
@@ -35965,6 +36966,7 @@ const eventListingBlockDefinition = {
35965
36966
  paletteHidden: true
35966
36967
  },
35967
36968
  category: "interactive",
36969
+ contentSource: { kind: "entries" },
35968
36970
  tags: [
35969
36971
  "events",
35970
36972
  "listing",
@@ -37170,6 +38172,7 @@ const eventCalendarBlockDefinition = {
37170
38172
  paletteHidden: false
37171
38173
  },
37172
38174
  category: "interactive",
38175
+ contentSource: { kind: "entries" },
37173
38176
  tags: [
37174
38177
  "events",
37175
38178
  "calendar",
@@ -37344,6 +38347,7 @@ const recurringScheduleBlockDefinition = {
37344
38347
  titleSource: "heading",
37345
38348
  description: "A compact weekly schedule with one row per recurring series.",
37346
38349
  category: "interactive",
38350
+ contentSource: { kind: "entries" },
37347
38351
  icon: "CalendarDays",
37348
38352
  tags: [
37349
38353
  "schedule",
@@ -37974,7 +38978,10 @@ const offeringsPreviewFields = [
37974
38978
  label: "Link"
37975
38979
  }
37976
38980
  ],
37977
- ui: { intent: "display" }
38981
+ ui: {
38982
+ intent: "display",
38983
+ widget: "buttonVariant"
38984
+ }
37978
38985
  },
37979
38986
  {
37980
38987
  id: "buttonText",
@@ -38027,6 +39034,7 @@ const offeringsPreviewManifest = createBlockManifest({
38027
39034
  paletteHidden: false
38028
39035
  },
38029
39036
  category: "content",
39037
+ contentSource: { kind: "mixed" },
38030
39038
  tags: [
38031
39039
  "offerings",
38032
39040
  "events",
@@ -38195,6 +39203,7 @@ const embedManifest = createBlockManifest({
38195
39203
  id: "block.embed",
38196
39204
  title: "Embed Content",
38197
39205
  category: "content",
39206
+ contentSource: { kind: "entries" },
38198
39207
  titleSource: "heading",
38199
39208
  additionalFields: [
38200
39209
  parseFieldDefinition({
@@ -38682,6 +39691,7 @@ const imageGalleryBlockDefinition = { manifest: createBlockManifest({
38682
39691
  id: "block.image-gallery",
38683
39692
  title: "Image Gallery",
38684
39693
  category: "content",
39694
+ contentSource: { kind: "inline" },
38685
39695
  previewCustomize: { quickFieldPriority: [
38686
39696
  "aspectRatio",
38687
39697
  "slidesToShow",
@@ -39334,6 +40344,7 @@ const videoGridBlockDefinition = { manifest: createBlockManifest({
39334
40344
  id: "block.video-grid",
39335
40345
  title: "Video Grid",
39336
40346
  category: "media",
40347
+ contentSource: { kind: "inline" },
39337
40348
  skipSectionStyles: true,
39338
40349
  additionalFields: parseFieldDefinitions([
39339
40350
  {
@@ -39504,6 +40515,7 @@ const audioPlayerBlockDefinition = { manifest: createBlockManifest({
39504
40515
  title: "Audio Player",
39505
40516
  titleSource: "title",
39506
40517
  category: "media",
40518
+ contentSource: { kind: "inline" },
39507
40519
  description: "Add a branded audio player for a single track.",
39508
40520
  tags: [
39509
40521
  "audio",
@@ -39615,6 +40627,7 @@ const fileDownloadBlockDefinition = { manifest: createBlockManifest({
39615
40627
  title: "File Download",
39616
40628
  titleSource: ["title", "file.filename"],
39617
40629
  category: "media",
40630
+ contentSource: { kind: "inline" },
39618
40631
  description: "Add a simple public download link for a document, ZIP, or audio file.",
39619
40632
  tags: [
39620
40633
  "file",
@@ -40634,6 +41647,7 @@ const shopBlockDefinition = {
40634
41647
  paletteHidden: false
40635
41648
  },
40636
41649
  category: "interactive",
41650
+ contentSource: { kind: "entries" },
40637
41651
  tags: [
40638
41652
  "shop",
40639
41653
  "passes",
@@ -40833,6 +41847,7 @@ const productListBlockDefinition = {
40833
41847
  paletteHidden: false
40834
41848
  },
40835
41849
  category: "interactive",
41850
+ contentSource: { kind: "entries" },
40836
41851
  tags: [
40837
41852
  "shop",
40838
41853
  "products",
@@ -40974,6 +41989,7 @@ const productDetailBlockDefinition = {
40974
41989
  paletteHidden: false
40975
41990
  },
40976
41991
  category: "interactive",
41992
+ contentSource: { kind: "entries" },
40977
41993
  tags: [
40978
41994
  "shop",
40979
41995
  "product",
@@ -41090,6 +42106,7 @@ const cartBlockDefinition = {
41090
42106
  paletteHidden: false
41091
42107
  },
41092
42108
  category: "interactive",
42109
+ contentSource: { kind: "inline" },
41093
42110
  tags: [
41094
42111
  "shop",
41095
42112
  "cart",
@@ -41172,6 +42189,7 @@ const checkoutBlockDefinition = {
41172
42189
  paletteHidden: false
41173
42190
  },
41174
42191
  category: "interactive",
42192
+ contentSource: { kind: "inline" },
41175
42193
  tags: [
41176
42194
  "shop",
41177
42195
  "checkout",
@@ -41386,6 +42404,7 @@ const giftingBlockDefinition = {
41386
42404
  paletteHidden: false
41387
42405
  },
41388
42406
  category: "interactive",
42407
+ contentSource: { kind: "entries" },
41389
42408
  tags: [
41390
42409
  "gifting",
41391
42410
  "gift cards",
@@ -41738,6 +42757,7 @@ const newsletterSignupBlockDefinition = {
41738
42757
  titleSource: "title",
41739
42758
  description: "Newsletter subscription form with configurable field presets.",
41740
42759
  category: "interactive",
42760
+ contentSource: { kind: "inline" },
41741
42761
  fragments: [{
41742
42762
  fragment: newsletterCopyFragment,
41743
42763
  fieldPriority: 0
@@ -41919,6 +42939,7 @@ const teamMembersBlockDefinition = {
41919
42939
  id: "block.team-members",
41920
42940
  title: "Team Members",
41921
42941
  category: "content",
42942
+ contentSource: { kind: "mixed" },
41922
42943
  titleSource: "heading",
41923
42944
  additionalFields: [
41924
42945
  parseFieldDefinition({
@@ -42359,6 +43380,7 @@ const locationMapBlockDefinition = {
42359
43380
  titleSource: "heading",
42360
43381
  description: "Show a business location with an interactive map and directions link.",
42361
43382
  category: "content",
43383
+ contentSource: { kind: "inline" },
42362
43384
  tags: [
42363
43385
  "map",
42364
43386
  "location",
@@ -43365,213 +44387,6 @@ function getBlockDefinition(name) {
43365
44387
  return blockStore.get(name);
43366
44388
  }
43367
44389
  //#endregion
43368
- //#region ../theme-core/src/gradients/presets.ts
43369
- /**
43370
- * Curated library of gradient presets
43371
- * All gradients use theme color tokens, so they adapt to the site's palette
43372
- */
43373
- const GRADIENT_PRESETS = [
43374
- {
43375
- id: "primary-secondary",
43376
- name: "Primary to Secondary",
43377
- description: "Blend your primary and secondary brand colors",
43378
- type: "linear",
43379
- angle: 135,
43380
- stops: [{
43381
- color: "primary-500",
43382
- position: 0
43383
- }, {
43384
- color: "secondary-500",
43385
- position: 100
43386
- }]
43387
- },
43388
- {
43389
- id: "primary-accent",
43390
- name: "Primary to Accent",
43391
- description: "Energetic gradient from primary to accent",
43392
- type: "linear",
43393
- angle: 135,
43394
- stops: [{
43395
- color: "primary-500",
43396
- position: 0
43397
- }, {
43398
- color: "accent-500",
43399
- position: 100
43400
- }]
43401
- },
43402
- {
43403
- id: "secondary-accent",
43404
- name: "Secondary to Accent",
43405
- description: "Vibrant secondary to accent blend",
43406
- type: "linear",
43407
- angle: 135,
43408
- stops: [{
43409
- color: "secondary-500",
43410
- position: 0
43411
- }, {
43412
- color: "accent-500",
43413
- position: 100
43414
- }]
43415
- },
43416
- {
43417
- id: "primary-light-dark",
43418
- name: "Primary Light to Dark",
43419
- description: "Subtle primary color fade",
43420
- type: "linear",
43421
- angle: 180,
43422
- stops: [{
43423
- color: "primary-500",
43424
- position: 0
43425
- }, {
43426
- color: "primary-900",
43427
- position: 100
43428
- }]
43429
- },
43430
- {
43431
- id: "primary-dark-light",
43432
- name: "Primary Dark to Light",
43433
- description: "Reverse primary fade",
43434
- type: "linear",
43435
- angle: 180,
43436
- stops: [{
43437
- color: "primary-900",
43438
- position: 0
43439
- }, {
43440
- color: "primary-50",
43441
- position: 100
43442
- }]
43443
- },
43444
- {
43445
- id: "secondary-light-dark",
43446
- name: "Secondary Light to Dark",
43447
- description: "Subtle secondary color fade",
43448
- type: "linear",
43449
- angle: 180,
43450
- stops: [{
43451
- color: "secondary-500",
43452
- position: 0
43453
- }, {
43454
- color: "secondary-900",
43455
- position: 100
43456
- }]
43457
- },
43458
- {
43459
- id: "accent-light-dark",
43460
- name: "Accent Light to Dark",
43461
- description: "Accent color depth fade",
43462
- type: "linear",
43463
- angle: 180,
43464
- stops: [{
43465
- color: "accent-500",
43466
- position: 0
43467
- }, {
43468
- color: "accent-900",
43469
- position: 100
43470
- }]
43471
- },
43472
- {
43473
- id: "background-surface",
43474
- name: "Background to Surface",
43475
- description: "Subtle depth using base colors",
43476
- type: "linear",
43477
- angle: 180,
43478
- stops: [{
43479
- color: "background",
43480
- position: 0
43481
- }, {
43482
- color: "surface",
43483
- position: 100
43484
- }]
43485
- },
43486
- {
43487
- id: "neutral-fade",
43488
- name: "Neutral Gradient",
43489
- description: "Elegant neutral color fade",
43490
- type: "linear",
43491
- angle: 180,
43492
- stops: [{
43493
- color: "neutral-900",
43494
- position: 0
43495
- }, {
43496
- color: "neutral-50",
43497
- position: 100
43498
- }]
43499
- },
43500
- {
43501
- id: "primary-radial",
43502
- name: "Primary Spotlight",
43503
- description: "Radial primary gradient from center",
43504
- type: "radial",
43505
- position: "center",
43506
- stops: [{
43507
- color: "primary-500",
43508
- position: 0
43509
- }, {
43510
- color: "primary-900",
43511
- position: 100
43512
- }]
43513
- },
43514
- {
43515
- id: "accent-radial",
43516
- name: "Accent Spotlight",
43517
- description: "Radial accent gradient from center",
43518
- type: "radial",
43519
- position: "center",
43520
- stops: [{
43521
- color: "accent-500",
43522
- position: 0
43523
- }, {
43524
- color: "accent-900",
43525
- position: 100
43526
- }]
43527
- },
43528
- {
43529
- id: "primary-secondary-accent",
43530
- name: "Brand Trio",
43531
- description: "All three brand colors in harmony",
43532
- type: "linear",
43533
- angle: 135,
43534
- stops: [
43535
- {
43536
- color: "primary-500",
43537
- position: 0
43538
- },
43539
- {
43540
- color: "secondary-500",
43541
- position: 50
43542
- },
43543
- {
43544
- color: "accent-500",
43545
- position: 100
43546
- }
43547
- ]
43548
- }
43549
- ];
43550
- //#endregion
43551
- //#region ../theme-core/src/gradients/utils.ts
43552
- /**
43553
- * Convert a GradientConfig to a CSS gradient string
43554
- *
43555
- * @param config - The gradient configuration using theme tokens
43556
- * @param tokens - Map of theme color tokens to hex values
43557
- * @returns CSS gradient string (e.g., "linear-gradient(180deg, #3b82f6 0%, #f59e0b 100%)")
43558
- */
43559
- function gradientToCss(config, tokens) {
43560
- const stops = config.stops.map((stop) => {
43561
- return `${tokens[stop.color] ?? "#000000"} ${stop.position}%`;
43562
- }).join(", ");
43563
- const type = config.type ?? "linear";
43564
- if (type === "radial") {
43565
- const position = config.position ?? "center";
43566
- return `radial-gradient(${config.shape ?? "circle"} at ${position}, ${stops})`;
43567
- }
43568
- if (type === "conic") {
43569
- const position = config.position ?? "center";
43570
- return `conic-gradient(from ${config.angle ?? 0}deg at ${position}, ${stops})`;
43571
- }
43572
- return `linear-gradient(${config.angle ?? 180}deg, ${stops})`;
43573
- }
43574
- //#endregion
43575
44390
  //#region ../blocks/src/system/helpers/backgroundMedia.ts
43576
44391
  function resolveBackgroundMedia(input) {
43577
44392
  const parsedMedia = mediaSchema.safeParse(input.image);
@@ -43609,9 +44424,9 @@ function appendBackgroundMediaClasses(baseClassName, input) {
43609
44424
  ].filter(Boolean).join(" ");
43610
44425
  }
43611
44426
  function gradientPresetCss(value) {
43612
- if (!isString$1(value)) return null;
43613
- const preset = GRADIENT_PRESETS.find((candidate) => candidate.id === value.trim());
43614
- if (!preset) return null;
44427
+ const presetId = parseGradientPresetId(value);
44428
+ if (!presetId) return null;
44429
+ const preset = getGradientPreset(presetId);
43615
44430
  return gradientToCss(preset, Object.fromEntries(preset.stops.map((stop) => [stop.color, `rgb(var(--tb-${stop.color}))`])));
43616
44431
  }
43617
44432
  const backgroundTransforms = [
@@ -52569,7 +53384,7 @@ function readFromSource$1(source, segments) {
52569
53384
  /**
52570
53385
  * Type guard for plain objects.
52571
53386
  */
52572
- function isPlainObject$2(value) {
53387
+ function isPlainObject$3(value) {
52573
53388
  return typeof value === "object" && value !== null && !Array.isArray(value);
52574
53389
  }
52575
53390
  /**
@@ -52578,18 +53393,18 @@ function isPlainObject$2(value) {
52578
53393
  function parseRouteEntry(routeId, value) {
52579
53394
  if (typeof routeId !== "string" || routeId.length === 0) return null;
52580
53395
  if (typeof value === "string") return [routeId, value];
52581
- if (isPlainObject$2(value)) return [routeId, value];
53396
+ if (isPlainObject$3(value)) return [routeId, value];
52582
53397
  return null;
52583
53398
  }
52584
53399
  /**
52585
53400
  * Extracts route map from $root.routes in the viewModel.
52586
53401
  */
52587
53402
  function extractRouteMapFromViewModel(viewModel) {
52588
- if (!isPlainObject$2(viewModel)) return void 0;
53403
+ if (!isPlainObject$3(viewModel)) return void 0;
52589
53404
  const root = viewModel.$root;
52590
- if (!isPlainObject$2(root)) return void 0;
53405
+ if (!isPlainObject$3(root)) return void 0;
52591
53406
  const routes = root.routes;
52592
- if (!isPlainObject$2(routes)) return void 0;
53407
+ if (!isPlainObject$3(routes)) return void 0;
52593
53408
  const entries = Object.entries(routes).map(([routeId, value]) => parseRouteEntry(routeId, value)).filter((entry) => entry !== null);
52594
53409
  if (entries.length === 0) return void 0;
52595
53410
  return Object.fromEntries(entries);
@@ -52598,22 +53413,22 @@ function extractRouteMapFromViewModel(viewModel) {
52598
53413
  * Extracts pagesByIdentifier from $root.pagesByIdentifier in the viewModel.
52599
53414
  */
52600
53415
  function extractPagesByIdentifierFromViewModel(viewModel) {
52601
- if (!isPlainObject$2(viewModel)) return void 0;
53416
+ if (!isPlainObject$3(viewModel)) return void 0;
52602
53417
  const root = viewModel.$root;
52603
- if (!isPlainObject$2(root)) return void 0;
53418
+ if (!isPlainObject$3(root)) return void 0;
52604
53419
  const pagesByIdentifier = root.pagesByIdentifier;
52605
- if (!isPlainObject$2(pagesByIdentifier)) return void 0;
53420
+ if (!isPlainObject$3(pagesByIdentifier)) return void 0;
52606
53421
  return pagesByIdentifier;
52607
53422
  }
52608
53423
  /**
52609
53424
  * Extracts entriesByIdentifier from $root.entriesByIdentifier in the viewModel.
52610
53425
  */
52611
53426
  function extractEntriesByIdentifierFromViewModel(viewModel) {
52612
- if (!isPlainObject$2(viewModel)) return void 0;
53427
+ if (!isPlainObject$3(viewModel)) return void 0;
52613
53428
  const root = viewModel.$root;
52614
- if (!isPlainObject$2(root)) return void 0;
53429
+ if (!isPlainObject$3(root)) return void 0;
52615
53430
  const entriesByIdentifier = root.entriesByIdentifier;
52616
- if (!isPlainObject$2(entriesByIdentifier)) return void 0;
53431
+ if (!isPlainObject$3(entriesByIdentifier)) return void 0;
52617
53432
  return entriesByIdentifier;
52618
53433
  }
52619
53434
  /**
@@ -52635,7 +53450,7 @@ function applyTransformsAndFallback(value, binding, context) {
52635
53450
  const routeMap = context.routes ?? extractRouteMapFromViewModel(context.viewModel);
52636
53451
  const pagesByIdentifier = context.pagesByIdentifier ?? extractPagesByIdentifierFromViewModel(context.viewModel);
52637
53452
  const entriesByIdentifier = context.entriesByIdentifier ?? extractEntriesByIdentifierFromViewModel(context.viewModel);
52638
- const stage = (isPlainObject$2(context.viewModel.$root) ? context.viewModel.$root : null)?.previewStage === "preview" ? "preview" : "published";
53453
+ const stage = (isPlainObject$3(context.viewModel.$root) ? context.viewModel.$root : null)?.previewStage === "preview" ? "preview" : "published";
52639
53454
  const transformed = runTransforms(value, binding.transforms?.map((step) => ({
52640
53455
  id: step.id,
52641
53456
  options: step.options
@@ -52664,15 +53479,15 @@ function resolveTransformData(context, bindingPath) {
52664
53479
  const [first] = splitPath$1(bindingPath);
52665
53480
  if (typeof first === "string" && first !== "$root") {
52666
53481
  const namedScope = context.scopes.find((entry) => entry.name === first);
52667
- if (namedScope && isPlainObject$2(namedScope.value)) return namedScope.value;
53482
+ if (namedScope && isPlainObject$3(namedScope.value)) return namedScope.value;
52668
53483
  }
52669
53484
  for (let i = context.scopes.length - 1; i >= 0; i -= 1) {
52670
53485
  const scope = context.scopes[i];
52671
- if (scope && isPlainObject$2(scope.value)) return scope.value;
53486
+ if (scope && isPlainObject$3(scope.value)) return scope.value;
52672
53487
  }
52673
53488
  const content = context.viewModel.content;
52674
- if (isPlainObject$2(content)) return content;
52675
- if (isPlainObject$2(context.viewModel)) return context.viewModel;
53489
+ if (isPlainObject$3(content)) return content;
53490
+ if (isPlainObject$3(context.viewModel)) return context.viewModel;
52676
53491
  }
52677
53492
  function coerceBindingValue(value, binding) {
52678
53493
  const mode = binding.coerce;
@@ -52734,7 +53549,7 @@ function isRichTextConditionValueEmpty(value) {
52734
53549
  return richTextStructuralPresence(root).kind === "empty";
52735
53550
  }
52736
53551
  function isTipTapNode(value) {
52737
- return isPlainObject$2(value) && typeof value.type === "string";
53552
+ return isPlainObject$3(value) && typeof value.type === "string";
52738
53553
  }
52739
53554
  function richTextStructuralPresence(node) {
52740
53555
  if (typeof node.text === "string" && node.text.trim().length > 0) return {
@@ -52763,7 +53578,7 @@ function richTextSelfPresence(node) {
52763
53578
  }
52764
53579
  }
52765
53580
  function hasResolvableRichTextImageSource(node) {
52766
- const attrs = isPlainObject$2(node.attrs) ? node.attrs : {};
53581
+ const attrs = isPlainObject$3(node.attrs) ? node.attrs : {};
52767
53582
  return hasNonBlankStringAttr(attrs, "src") || hasNonBlankStringAttr(attrs, "identifier");
52768
53583
  }
52769
53584
  function hasNonBlankStringAttr(attrs, key) {
@@ -52955,7 +53770,7 @@ function getNestedBinding(bindings, path) {
52955
53770
  const segments = path.split(".");
52956
53771
  let current = bindings;
52957
53772
  for (const segment of segments) {
52958
- if (!isPlainObject$2(current)) return void 0;
53773
+ if (!isPlainObject$3(current)) return void 0;
52959
53774
  current = current[segment];
52960
53775
  }
52961
53776
  return current;
@@ -52978,7 +53793,7 @@ function resolveBindingToEditorPath(fieldName, bindings, editableRouteMetadataFi
52978
53793
  };
52979
53794
  const binding = bindings[fieldName] ?? getNestedBinding(bindings, fieldName);
52980
53795
  if (!binding) return null;
52981
- if (isPlainObject$2(binding) && "source" in binding) {
53796
+ if (isPlainObject$3(binding) && "source" in binding) {
52982
53797
  const source = binding.source;
52983
53798
  if (source === "field" && "path" in binding) {
52984
53799
  const path = binding.path;
@@ -52995,7 +53810,7 @@ function resolveBindingToEditorPath(fieldName, bindings, editableRouteMetadataFi
52995
53810
  if (!Array.isArray(options)) return null;
52996
53811
  for (const option of options) {
52997
53812
  if (typeof option === "string") return `content.${option}`;
52998
- if (isPlainObject$2(option) && "source" in option) {
53813
+ if (isPlainObject$3(option) && "source" in option) {
52999
53814
  const optSource = option.source;
53000
53815
  if (optSource === "field" && "path" in option) {
53001
53816
  const path = option.path;
@@ -53047,7 +53862,7 @@ function derivePreviewTextEditableTarget(parsedBinding, context) {
53047
53862
  };
53048
53863
  }
53049
53864
  const root = context.viewModel?.$root;
53050
- if (isPlainObject$2(root) && root.contentEntry != null && context.pathBase) return {
53865
+ if (isPlainObject$3(root) && root.contentEntry != null && context.pathBase) return {
53051
53866
  kind: "read-only",
53052
53867
  reason: "entry-template-block-field"
53053
53868
  };
@@ -53106,7 +53921,7 @@ function injectMediaSlotPath(props, node, parsedBinding, context) {
53106
53921
  if (resolveBindingToEditorPath(fieldPath, context.blockBindings, context.editableRouteMetadataFieldIds)) return props;
53107
53922
  }
53108
53923
  const root = context.viewModel?.$root;
53109
- if (isPlainObject$2(root) && root.contentEntry != null && context.pathBase) return props;
53924
+ if (isPlainObject$3(root) && root.contentEntry != null && context.pathBase) return props;
53110
53925
  return {
53111
53926
  ...props,
53112
53927
  fieldPath
@@ -53253,7 +54068,7 @@ function getNodeProps(node) {
53253
54068
  if (key.startsWith("$")) continue;
53254
54069
  legacy[key] = value;
53255
54070
  }
53256
- const props = isPlainObject$2(node.props) ? node.props : {};
54071
+ const props = isPlainObject$3(node.props) ? node.props : {};
53257
54072
  return {
53258
54073
  ...legacy,
53259
54074
  ...props
@@ -53365,7 +54180,7 @@ function generateHoverEffects(axes, _space) {
53365
54180
  //#endregion
53366
54181
  //#region ../theme-core/src/blockOverrides.ts
53367
54182
  const BLOCK_PREFIX = "block.";
53368
- function isPlainObject$1(value) {
54183
+ function isPlainObject$2(value) {
53369
54184
  return typeof value === "object" && value !== null && !Array.isArray(value);
53370
54185
  }
53371
54186
  function isUnknownArray$1(value) {
@@ -53373,7 +54188,7 @@ function isUnknownArray$1(value) {
53373
54188
  }
53374
54189
  function cloneValue(value) {
53375
54190
  if (isUnknownArray$1(value)) return value.map((item) => cloneValue(item));
53376
- if (isPlainObject$1(value)) {
54191
+ if (isPlainObject$2(value)) {
53377
54192
  const cloned = {};
53378
54193
  for (const [key, child] of Object.entries(value)) cloned[key] = cloneValue(child);
53379
54194
  return cloned;
@@ -53383,7 +54198,7 @@ function cloneValue(value) {
53383
54198
  function mergeDeep(defaultValue, overrideValue) {
53384
54199
  if (overrideValue === void 0) return cloneValue(defaultValue);
53385
54200
  if (overrideValue === null) return null;
53386
- if (isPlainObject$1(defaultValue) && isPlainObject$1(overrideValue)) {
54201
+ if (isPlainObject$2(defaultValue) && isPlainObject$2(overrideValue)) {
53387
54202
  const merged = {};
53388
54203
  const keys = /* @__PURE__ */ new Set([...Object.keys(defaultValue), ...Object.keys(overrideValue)]);
53389
54204
  for (const key of keys) merged[key] = mergeDeep(defaultValue[key], overrideValue[key]);
@@ -53440,7 +54255,7 @@ function mergeContentWithBlockStyleOverrides(content, theme, blockKind) {
53440
54255
  */
53441
54256
  function applyHeadingDecorationSignature(target, themeDecoration) {
53442
54257
  const existing = target["_sectionStyles"];
53443
- const existingObj = isPlainObject$1(existing) ? existing : void 0;
54258
+ const existingObj = isPlainObject$2(existing) ? existing : void 0;
53444
54259
  const rawOverride = existingObj?.["headingDecorationOverride"];
53445
54260
  const effective = resolveEffectiveHeadingDecoration(themeDecoration, rawOverride === "site-default" || rawOverride === "off" ? rawOverride : void 0);
53446
54261
  if (effective.kind === "none") {
@@ -57006,6 +57821,8 @@ function buildThemeCssVars(theme) {
57006
57821
  const inputRadius = mapInputRadius(inputSystem.base.corners, theme.corners);
57007
57822
  return {
57008
57823
  ...generateCssVars(theme.palette),
57824
+ ["--tb-secondaryForeground"]: "var(--tb-onSecondary)",
57825
+ ["--tb-accentForeground"]: "var(--tb-onAccent)",
57009
57826
  ["--tb-scrim"]: "0 0 0",
57010
57827
  ["--tb-shadow"]: "0 0 0",
57011
57828
  ...generateShadowCssVars(theme),
@@ -65649,6 +66466,11 @@ const containerResponsiveThemeCss = `/*
65649
66466
  flex-wrap: wrap;
65650
66467
  }
65651
66468
 
66469
+ /* Text-style CTA actions belong to the section foreground contract. */
66470
+ :where(.theme-scope) [data-block="cta-full"] .cta-row .button-link {
66471
+ color: var(--rb-surface-link-color, var(--rb-surface-text-color, rgb(var(--tb-primary))));
66472
+ }
66473
+
65652
66474
  /* =============================================================================
65653
66475
  * Block: Body Text (content block, Phase 2)
65654
66476
  *
@@ -72207,35 +73029,35 @@ z.object({
72207
73029
  //#endregion
72208
73030
  //#region ../blocks/src/system/runtime/bindings.ts
72209
73031
  const shouldLogWarnings = isNonProductionEnv();
72210
- function isPlainObject(value) {
73032
+ function isPlainObject$1(value) {
72211
73033
  return typeof value === "object" && value !== null && !Array.isArray(value);
72212
73034
  }
72213
73035
  function isRouteMapEntry(value) {
72214
73036
  if (typeof value === "string") return true;
72215
- return isPlainObject(value);
73037
+ return isPlainObject$1(value);
72216
73038
  }
72217
73039
  function isRouteMap(value) {
72218
- if (!isPlainObject(value)) return false;
73040
+ if (!isPlainObject$1(value)) return false;
72219
73041
  return Object.values(value).every(isRouteMapEntry);
72220
73042
  }
72221
73043
  function isPageIdentifierMap(value) {
72222
- if (!isPlainObject(value)) return false;
73044
+ if (!isPlainObject$1(value)) return false;
72223
73045
  return Object.values(value).every((v) => typeof v === "string");
72224
73046
  }
72225
73047
  function isEntryIdentifierMap(value) {
72226
- if (!isPlainObject(value)) return false;
73048
+ if (!isPlainObject$1(value)) return false;
72227
73049
  for (const v of Object.values(value)) {
72228
- if (!isPlainObject(v)) return false;
73050
+ if (!isPlainObject$1(v)) return false;
72229
73051
  if (!Object.values(v).every((inner) => typeof inner === "string")) return false;
72230
73052
  }
72231
73053
  return true;
72232
73054
  }
72233
73055
  function buildTransformContext(vm) {
72234
73056
  const root = vm.$root;
72235
- const stage = root && isPlainObject(root) && root.previewStage === "preview" ? "preview" : "published";
72236
- const routes = root && isPlainObject(root) && isRouteMap(root.routes) ? root.routes : void 0;
72237
- const pagesByIdentifier = root && isPlainObject(root) && isPageIdentifierMap(root.pagesByIdentifier) ? root.pagesByIdentifier : void 0;
72238
- const entriesByIdentifier = root && isPlainObject(root) && isEntryIdentifierMap(root.entriesByIdentifier) ? root.entriesByIdentifier : void 0;
73057
+ const stage = root && isPlainObject$1(root) && root.previewStage === "preview" ? "preview" : "published";
73058
+ const routes = root && isPlainObject$1(root) && isRouteMap(root.routes) ? root.routes : void 0;
73059
+ const pagesByIdentifier = root && isPlainObject$1(root) && isPageIdentifierMap(root.pagesByIdentifier) ? root.pagesByIdentifier : void 0;
73060
+ const entriesByIdentifier = root && isPlainObject$1(root) && isEntryIdentifierMap(root.entriesByIdentifier) ? root.entriesByIdentifier : void 0;
72239
73061
  return {
72240
73062
  theme: defaultThemeTokens,
72241
73063
  stage,
@@ -72420,8 +73242,6 @@ function parseBackgroundColorToken(value) {
72420
73242
  if (!token) return null;
72421
73243
  return token ? token : null;
72422
73244
  }
72423
- const gradientPresetIds = GRADIENT_PRESETS.map((preset) => preset.id);
72424
- new Set(gradientPresetIds);
72425
73245
  function blockedDemoMutationPolicy(classification, publicMessage) {
72426
73246
  if (publicMessage.trim() === "") throw new Error(`Demo mutation policy ${classification} requires publicMessage.`);
72427
73247
  return {
@@ -72970,6 +73790,7 @@ const CANONICAL_REFUND_PLANNER_ERROR_CODES = [
72970
73790
  "already_refunded",
72971
73791
  "idempotency_amount_mismatch",
72972
73792
  "idempotency_method_mismatch",
73793
+ "idempotency_receipt_mismatch",
72973
73794
  "refund_in_progress"
72974
73795
  ],
72975
73796
  "invalid_amount",
@@ -72982,7 +73803,12 @@ const CANONICAL_ADMIN_REFUND_FAILURE_CODES = [
72982
73803
  "payment_intent_not_found",
72983
73804
  "stripe_error",
72984
73805
  "stale_pending_missing_context",
72985
- "non_refundable_deposit_remaining"
73806
+ "non_refundable_deposit_remaining",
73807
+ "deposit_refund_override_required",
73808
+ "refund_revoke_requires_full_line_refund",
73809
+ "payment_receipt_not_found",
73810
+ "payment_receipt_not_refundable",
73811
+ "payment_receipt_attribution_required"
72986
73812
  ];
72987
73813
  [...CANONICAL_REFUND_PLANNER_ERROR_CODES];
72988
73814
  //#endregion
@@ -72995,7 +73821,10 @@ const STANDARD_LINE_ITEM_REFUND_INVALID_CODES = [
72995
73821
  "missing_credit_customer",
72996
73822
  "deposit_refund_override_required",
72997
73823
  "refund_revoke_requires_full_line_refund",
72998
- "unified_order_link_required"
73824
+ "unified_order_link_required",
73825
+ "payment_receipt_not_found",
73826
+ "payment_receipt_not_refundable",
73827
+ "payment_receipt_attribution_required"
72999
73828
  ];
73000
73829
  const EVENT_ATTENDEE_REFUND_INVALID_CODES = ["manual_booking_not_refundable", ...STANDARD_LINE_ITEM_REFUND_INVALID_CODES];
73001
73830
  [...CANONICAL_ADMIN_REFUND_FAILURE_CODES];
@@ -74560,6 +75389,225 @@ function isDisplayMediaKind(value) {
74560
75389
  function isFileFieldKind(value) {
74561
75390
  return FILE_FIELD_KINDS.includes(value);
74562
75391
  }
75392
+ /** Matches the closed sentinel shape `"<REQUIRED: …>"`. */
75393
+ const SENTINEL_PATTERN = /^<REQUIRED:.*>$/;
75394
+ /**
75395
+ * True when a value is an unfilled placeholder sentinel. Shared by the emitter
75396
+ * (self-check) and the validator (rejection) so recognition can't drift.
75397
+ */
75398
+ function isPlaceholderSentinel(value) {
75399
+ return typeof value === "string" && SENTINEL_PATTERN.test(value);
75400
+ }
75401
+ function effectiveReferenceKind(field) {
75402
+ const kind = field.referenceKind;
75403
+ const legacy = field.reference?.type;
75404
+ return kind ?? legacy;
75405
+ }
75406
+ function selectOptionValues$1(field) {
75407
+ const options = field.options;
75408
+ return options ? options.map((o) => o.value) : [];
75409
+ }
75410
+ /**
75411
+ * The placeholder sentinel for a leaf field. Container types (group/button/
75412
+ * repeater) do not have a leaf sentinel — they expand structurally — and calling
75413
+ * this for them returns a generic marker only for completeness.
75414
+ */
75415
+ function buildSentinel(field) {
75416
+ switch (field.type) {
75417
+ case "text":
75418
+ case "textarea": return "<REQUIRED: text>";
75419
+ case "richText": return "<REQUIRED: rich-text>";
75420
+ case "number": return "<REQUIRED: number>";
75421
+ case "date": return "<REQUIRED: date (YYYY-MM-DD)>";
75422
+ case "datetime": return "<REQUIRED: datetime (ISO 8601)>";
75423
+ case "url": return "<REQUIRED: url>";
75424
+ case "link": return "<REQUIRED: link>";
75425
+ case "media": return "<REQUIRED: media-identifier>";
75426
+ case "file": return "<REQUIRED: file-identifier>";
75427
+ case "boolean": return "<REQUIRED: boolean>";
75428
+ case "select": {
75429
+ const values = selectOptionValues$1(field);
75430
+ return values.length > 0 ? `<REQUIRED: select (${values.join("|")})>` : "<REQUIRED: select>";
75431
+ }
75432
+ case "reference": {
75433
+ const kind = effectiveReferenceKind(field);
75434
+ return kind ? `<REQUIRED: reference-identifier (${kind})>` : "<REQUIRED: reference-identifier>";
75435
+ }
75436
+ case "group": return "<REQUIRED: group>";
75437
+ case "button": return "<REQUIRED: button>";
75438
+ case "repeater": return "<REQUIRED: repeater>";
75439
+ }
75440
+ }
75441
+ //#endregion
75442
+ //#region ../content-model/src/content-types/entryValidation.ts
75443
+ function subfieldsOf$1(field) {
75444
+ const direct = field.fields;
75445
+ if (direct && direct.length > 0) return direct;
75446
+ return field.schema?.fields ?? [];
75447
+ }
75448
+ function selectOptionValues(field) {
75449
+ const options = field.options;
75450
+ return options ? options.map((o) => o.value) : [];
75451
+ }
75452
+ function isPlainObject(value) {
75453
+ return value !== null && typeof value === "object" && !Array.isArray(value);
75454
+ }
75455
+ /** Field types whose value must be a string. */
75456
+ const STRING_TYPES = /* @__PURE__ */ new Set([
75457
+ "text",
75458
+ "textarea",
75459
+ "url",
75460
+ "link",
75461
+ "media",
75462
+ "file",
75463
+ "reference",
75464
+ "date",
75465
+ "datetime",
75466
+ "select"
75467
+ ]);
75468
+ function checkLeafType(field, value, path, errors) {
75469
+ const push = (code, message) => {
75470
+ errors.push({
75471
+ path,
75472
+ fieldType: field.type,
75473
+ code,
75474
+ message
75475
+ });
75476
+ };
75477
+ if (field.type === "number") {
75478
+ if (typeof value !== "number" || Number.isNaN(value)) push("wrong_type", `"${path}" must be a number.`);
75479
+ return;
75480
+ }
75481
+ if (field.type === "boolean") {
75482
+ if (typeof value !== "boolean") push("wrong_type", `"${path}" must be a boolean.`);
75483
+ return;
75484
+ }
75485
+ if (field.type === "richText") {
75486
+ if (typeof value !== "string" && !isPlainObject(value)) push("wrong_type", `"${path}" must be rich-text (string or document object).`);
75487
+ return;
75488
+ }
75489
+ if (field.type === "select") {
75490
+ if (typeof value !== "string") {
75491
+ push("wrong_type", `"${path}" must be a string.`);
75492
+ return;
75493
+ }
75494
+ const options = selectOptionValues(field);
75495
+ if (options.length > 0 && !options.includes(value)) push("invalid_option", `"${path}" must be one of: ${options.join(", ")}.`);
75496
+ return;
75497
+ }
75498
+ if (STRING_TYPES.has(field.type)) {
75499
+ if (typeof value !== "string") push("wrong_type", `"${path}" must be a string.`);
75500
+ }
75501
+ }
75502
+ function validateField(field, value, path, errors) {
75503
+ if (value === void 0 || value === null || value === "") {
75504
+ if (field.required === true) errors.push({
75505
+ path,
75506
+ fieldType: field.type,
75507
+ code: "missing_required",
75508
+ message: `Required field "${path}" is missing.`
75509
+ });
75510
+ return;
75511
+ }
75512
+ if (isPlaceholderSentinel(value)) {
75513
+ errors.push({
75514
+ path,
75515
+ fieldType: field.type,
75516
+ code: "unfilled_placeholder",
75517
+ message: `"${path}" still contains an unfilled placeholder (${value}).`
75518
+ });
75519
+ return;
75520
+ }
75521
+ if (field.type === "group" || field.type === "button") {
75522
+ if (!isPlainObject(value)) {
75523
+ errors.push({
75524
+ path,
75525
+ fieldType: field.type,
75526
+ code: "wrong_type",
75527
+ message: `"${path}" must be an object.`
75528
+ });
75529
+ return;
75530
+ }
75531
+ validateFields(subfieldsOf$1(field), value, path, errors);
75532
+ return;
75533
+ }
75534
+ if (field.type === "repeater") {
75535
+ if (!Array.isArray(value)) {
75536
+ errors.push({
75537
+ path,
75538
+ fieldType: field.type,
75539
+ code: "wrong_type",
75540
+ message: `"${path}" must be an array.`
75541
+ });
75542
+ return;
75543
+ }
75544
+ const itemFields = subfieldsOf$1(field);
75545
+ value.forEach((item, index) => {
75546
+ const itemPath = `${path}[${index}]`;
75547
+ if (!isPlainObject(item)) {
75548
+ errors.push({
75549
+ path: itemPath,
75550
+ fieldType: "repeater",
75551
+ code: "wrong_type",
75552
+ message: `"${itemPath}" must be an object.`
75553
+ });
75554
+ return;
75555
+ }
75556
+ validateFields(itemFields, item, itemPath, errors);
75557
+ });
75558
+ return;
75559
+ }
75560
+ checkLeafType(field, value, path, errors);
75561
+ }
75562
+ function validateFields(fields, data, pathPrefix, errors) {
75563
+ for (const field of fields) {
75564
+ const path = pathPrefix ? `${pathPrefix}.${field.id}` : field.id;
75565
+ validateField(field, data[field.id], path, errors);
75566
+ }
75567
+ }
75568
+ //#endregion
75569
+ //#region ../content-model/src/content-types/scaffold.ts
75570
+ /** Nested subfields for group/repeater/button-style fields (both DSL shapes). */
75571
+ function subfieldsOf(field) {
75572
+ const direct = field.fields;
75573
+ if (direct && direct.length > 0) return direct;
75574
+ return field.schema?.fields ?? [];
75575
+ }
75576
+ const CONTAINER_TYPES = /* @__PURE__ */ new Set([
75577
+ "group",
75578
+ "button",
75579
+ "repeater"
75580
+ ]);
75581
+ /**
75582
+ * Build the placeholder value for a required field. Group/button fields recurse
75583
+ * into their required subfields; repeaters emit a single seed item so agents see
75584
+ * the item shape.
75585
+ */
75586
+ function buildFieldValue(field) {
75587
+ if (field.type === "repeater") return [buildObjectFromFields(subfieldsOf(field))];
75588
+ if (CONTAINER_TYPES.has(field.type)) {
75589
+ const subfields = subfieldsOf(field);
75590
+ if (subfields.length > 0) return buildObjectFromFields(subfields);
75591
+ if (field.type === "button") return {
75592
+ label: "<REQUIRED: text>",
75593
+ link: "<REQUIRED: link>"
75594
+ };
75595
+ return {};
75596
+ }
75597
+ return buildSentinel(field);
75598
+ }
75599
+ /**
75600
+ * Build a template object from a list of fields, stubbing required fields and
75601
+ * omitting optional ones.
75602
+ */
75603
+ function buildObjectFromFields(fields) {
75604
+ const template = {};
75605
+ for (const field of fields) {
75606
+ if (field.required !== true) continue;
75607
+ template[field.id] = buildFieldValue(field);
75608
+ }
75609
+ return template;
75610
+ }
74563
75611
  //#endregion
74564
75612
  //#region ../content-model/src/content-types/builtins.ts
74565
75613
  /**