@riverbankcms/sdk 0.130.0 → 0.130.2

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 (244) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +1 -1
  3. package/dist/{PreviewEditorSidebar-DiEpbOdw.mjs → PreviewEditorSidebar-BY-1QdE2.mjs} +18 -15
  4. package/dist/{PreviewEditorUI-C6MvxnIa.mjs → PreviewEditorUI-BS34jI9_.mjs} +3 -3
  5. package/dist/{PreviewQueryProvider-C2rnoPz6.mjs → PreviewQueryProvider-BD0kOjtI.mjs} +1 -1
  6. package/dist/{SdkPreviewModeRuntime-DRXA_m5L.mjs → SdkPreviewModeRuntime-B1pWnhu-.mjs} +48 -21
  7. package/dist/{SiteChromeCustomizeContext-BTHdv5_-.mjs → SiteChromeCustomizeContext-BhUG_72i.mjs} +6180 -5076
  8. package/dist/_dts/ai/src/contracts/proposals.d.ts +309 -32
  9. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +969 -112
  10. package/dist/_dts/api/src/accountApi.d.ts +213 -0
  11. package/dist/_dts/api/src/accountEndpointDefinitions.d.ts +30 -0
  12. package/dist/_dts/api/src/accountEndpoints.d.ts +254 -0
  13. package/dist/_dts/api/src/apiCallParams.d.ts +60 -0
  14. package/dist/_dts/api/src/bookingConfig.d.ts +10 -20
  15. package/dist/_dts/api/src/bookingManagementEndpoints.d.ts +4 -5
  16. package/dist/_dts/api/src/bookingManagementEventEndpointTypes.d.ts +15 -0
  17. package/dist/_dts/api/src/bookingSettingsSections.d.ts +9 -0
  18. package/dist/_dts/api/src/bookingVerticals.d.ts +2 -2
  19. package/dist/_dts/api/src/commercePortalEndpoints.d.ts +2 -0
  20. package/dist/_dts/api/src/endpoints.d.ts +1253 -1209
  21. package/dist/_dts/api/src/event-destination-policy.d.ts +1 -1
  22. package/dist/_dts/api/src/eventManagementContracts.d.ts +83 -7
  23. package/dist/_dts/api/src/eventManagementEndpointDefinitions.d.ts +26 -0
  24. package/dist/_dts/api/src/eventSeriesPresentationPreset.d.ts +12 -0
  25. package/dist/_dts/api/src/index.d.ts +11 -3
  26. package/dist/_dts/api/src/indexCoreTypes.d.ts +1 -0
  27. package/dist/_dts/api/src/indexExtendedTypes.d.ts +1 -0
  28. package/dist/_dts/api/src/oauth.d.ts +8 -0
  29. package/dist/_dts/api/src/preview-auth.d.ts +26 -0
  30. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +5 -5
  31. package/dist/_dts/api/src/publicExperience/wholeSeriesEndpoints.d.ts +0 -1
  32. package/dist/_dts/api/src/sdk-event-upsert.d.ts +2 -0
  33. package/dist/_dts/api/src/sdkBlockUpsertContracts.d.ts +48 -0
  34. package/dist/_dts/api/src/sdkContracts.d.ts +5 -13
  35. package/dist/_dts/api/src/sdkEventCategoryContracts.d.ts +9 -0
  36. package/dist/_dts/api/src/sdkThemeWire.d.ts +40 -8
  37. package/dist/_dts/api/src/settings/permissions.d.ts +2 -2
  38. package/dist/_dts/api/src/siteApiKeyEndpointDefinitions.d.ts +66 -0
  39. package/dist/_dts/api/src/siteInfrastructure.d.ts +3 -3
  40. package/dist/_dts/api/src/siteOperations.d.ts +1 -1
  41. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +12 -4
  42. package/dist/_dts/api/src/types.d.ts +7 -59
  43. package/dist/_dts/block-form/src/client/index.d.ts +15 -23
  44. package/dist/_dts/block-form/src/index.d.ts +23 -22
  45. package/dist/_dts/blocks/src/primitives/layout/Grid.d.ts +18 -1
  46. package/dist/_dts/blocks/src/system/blocks/appointment-booking.d.ts +1 -0
  47. package/dist/_dts/blocks/src/system/blocks/events/shared/filtering.d.ts +1 -1
  48. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +61 -0
  49. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +24 -22
  50. package/dist/_dts/blocks/src/system/blocks/feature-cards.d.ts +25 -1
  51. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +7 -30
  52. package/dist/_dts/blocks/src/system/blocks/site-header.d.ts +0 -1
  53. package/dist/_dts/blocks/src/system/blocks/team-members.d.ts +4 -63
  54. package/dist/_dts/blocks/src/system/customize/backgrounds.d.ts +1 -3
  55. package/dist/_dts/blocks/src/system/fields/boxStyles.d.ts +13 -1
  56. package/dist/_dts/blocks/src/system/fields/gridLayout.d.ts +20 -0
  57. package/dist/_dts/blocks/src/system/node/fragments/classNameBinding.d.ts +6 -1
  58. package/dist/_dts/blocks/src/system/node/fragments/styledCard.d.ts +31 -0
  59. package/dist/_dts/blocks/src/system/runtime/api/endpoints.d.ts +4 -0
  60. package/dist/_dts/blocks/src/system/runtime/api/types/courses.d.ts +0 -1
  61. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +0 -1
  62. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.d.ts +4 -2
  63. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +24 -8
  64. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.server.d.ts +8 -0
  65. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.validationErrors.d.ts +17 -0
  66. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +1 -1
  67. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventScope.d.ts +1 -1
  68. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.server.d.ts +1 -1
  69. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +42 -0
  70. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +4 -1
  71. package/dist/_dts/blocks/src/system/runtime/shared/components/FeedbackMessage.d.ts +17 -1
  72. package/dist/_dts/blocks/src/system/runtime/shared/components/index.d.ts +1 -1
  73. package/dist/_dts/blocks/src/system/spacing.d.ts +1 -1
  74. package/dist/_dts/blocks/src/system/transforms/registry/_utils/boxStyleClassNames.d.ts +90 -0
  75. package/dist/_dts/blocks/src/system/transforms/registry/_utils/surfaceForegroundStyles.d.ts +4 -0
  76. package/dist/_dts/blocks/src/system/transforms/registry/boxStyles.d.ts +26 -0
  77. package/dist/_dts/blocks/src/system/transforms/registry/layout.d.ts +4 -37
  78. package/dist/_dts/content-editor/src/components/ContentFieldsForm.d.ts +2 -2
  79. package/dist/_dts/content-model/src/content-types/builtin-schemas.d.ts +6 -6
  80. package/dist/_dts/content-model/src/content-types/builtins.d.ts +6 -6
  81. package/dist/_dts/core/src/branded.d.ts +4 -0
  82. package/dist/_dts/core/src/event-page-surface-subject.d.ts +25 -4
  83. package/dist/_dts/core/src/index.d.ts +3 -0
  84. package/dist/_dts/core/src/public-event-booking-availability.d.ts +8 -0
  85. package/dist/_dts/core/src/secret.d.ts +44 -0
  86. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +341 -0
  87. package/dist/_dts/editor-blocks/src/BlockEditor.d.ts +2 -1
  88. package/dist/_dts/editor-blocks/src/BlockForm.d.ts +2 -2
  89. package/dist/_dts/editor-blocks/src/block-editor/BlockCard.d.ts +2 -2
  90. package/dist/_dts/editor-blocks/src/block-editor/BlockCardBody.d.ts +1 -1
  91. package/dist/_dts/editor-blocks/src/block-editor/BlockCardHeader.d.ts +1 -1
  92. package/dist/_dts/editor-blocks/src/block-editor/SortableBlockItem.d.ts +1 -1
  93. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockValidationManifests.d.ts +1 -1
  94. package/dist/_dts/editor-blocks/src/block-editor/preparedManifest.d.ts +1 -1
  95. package/dist/_dts/editor-blocks/src/blockPayload.d.ts +1 -1
  96. package/dist/_dts/editor-blocks/src/index.d.ts +2 -2
  97. package/dist/_dts/editor-blocks/src/media/MediaField.d.ts +2 -2
  98. package/dist/_dts/editor-blocks/src/widgets/button/ButtonField.d.ts +1 -1
  99. package/dist/_dts/editor-blocks/src/widgets/button/ButtonVariantField.d.ts +1 -1
  100. package/dist/_dts/editor-blocks/src/widgets/button/index.d.ts +1 -1
  101. package/dist/_dts/editor-blocks/src/widgets/content-type-select/ContentTypeSelectField.d.ts +1 -1
  102. package/dist/_dts/editor-blocks/src/widgets/entry-picker/EntryPickerField.d.ts +1 -1
  103. package/dist/_dts/editor-blocks/src/widgets/entry-picker/index.d.ts +1 -1
  104. package/dist/_dts/editor-blocks/src/widgets/icon-picker/IconPickerField.d.ts +1 -1
  105. package/dist/_dts/editor-blocks/src/widgets/inline-text/InlineTextField.d.ts +1 -1
  106. package/dist/_dts/editor-blocks/src/widgets/link/LinkField.d.ts +2 -2
  107. package/dist/_dts/editor-blocks/src/widgets/offerings-preview/OfferingsPreviewGroupOrderField.d.ts +1 -1
  108. package/dist/_dts/editor-blocks/src/widgets/reference/ReferenceField.d.ts +2 -2
  109. package/dist/_dts/editor-blocks/src/widgets/rich-text/RichTextField.d.ts +2 -2
  110. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/SiteOptionPickerField.d.ts +1 -1
  111. package/dist/_dts/editor-blocks/src/widgets/site-option-picker/utils.d.ts +1 -1
  112. package/dist/_dts/preview-next/src/client/blocks/useBlockCustomizeControls.d.ts +1 -1
  113. package/dist/_dts/preview-next/src/client/preview/EntryEditorPanel.d.ts +1 -1
  114. package/dist/_dts/preview-next/src/client/preview/EntrySlotBlockEditorPanel.d.ts +1 -1
  115. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +2 -0
  116. package/dist/_dts/preview-next/src/client/preview/PreviewSiteFontStorageContext.d.ts +6 -0
  117. package/dist/_dts/preview-next/src/client/preview/siteFontPreview.d.ts +5 -0
  118. package/dist/_dts/preview-next/src/client/sdk/contentStorage.d.ts +10 -0
  119. package/dist/_dts/preview-next/src/client/widgets/widgetOverrides.d.ts +1 -1
  120. package/dist/_dts/preview-next/src/index.d.ts +1 -0
  121. package/dist/_dts/preview-next/src/keepAlive.d.ts +3 -5
  122. package/dist/_dts/preview-next/src/refresh-contract.d.ts +1 -0
  123. package/dist/_dts/preview-next/src/route-contract.d.ts +7 -0
  124. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  125. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +61 -39
  126. package/dist/_dts/sdk/src/preview-next/keep-alive.d.ts +1 -1
  127. package/dist/_dts/sdk/src/rendering/components/Layout.d.ts +1 -1
  128. package/dist/_dts/sdk/src/rendering/index.d.ts +1 -1
  129. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +9 -1
  130. package/dist/_dts/sdk/src/theme-bridge/types.d.ts +4 -2
  131. package/dist/_dts/sdk/src/version.d.ts +1 -1
  132. package/dist/_dts/theme-core/src/cards/core.d.ts +54 -10
  133. package/dist/_dts/theme-core/src/cards/decorations.d.ts +33 -0
  134. package/dist/_dts/theme-core/src/cards/generateDefaultCardSystem.d.ts +7 -2
  135. package/dist/_dts/theme-core/src/cards/index.d.ts +3 -0
  136. package/dist/_dts/theme-core/src/cards/openCardVariant.d.ts +24 -0
  137. package/dist/_dts/theme-core/src/cards/roles.d.ts +38 -0
  138. package/dist/_dts/theme-core/src/cards/siteStyleDefaults.d.ts +53 -8
  139. package/dist/_dts/theme-core/src/cards/types.d.ts +489 -21
  140. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  141. package/dist/_dts/theme-core/src/header/legacyHeaderVariant.d.ts +59 -0
  142. package/dist/_dts/theme-core/src/header/paintTargets.d.ts +2 -2
  143. package/dist/_dts/theme-core/src/header/scrollTreatment.d.ts +3 -2
  144. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +12 -135
  145. package/dist/_dts/theme-core/src/palette/utils/contrast.d.ts +1 -0
  146. package/dist/_dts/theme-core/src/palette/utils/opaqueSurfaceColor.d.ts +6 -0
  147. package/dist/_dts/theme-core/src/runtime/buildThemeRuntimeFromBridge.d.ts +1 -1
  148. package/dist/_dts/theme-core/src/schema.d.ts +301 -94
  149. package/dist/_dts/theme-core/src/site-styles/appearancePresetCatalog.d.ts +84 -102
  150. package/dist/_dts/theme-core/src/site-styles/appearancePresetResolution.d.ts +12 -1
  151. package/dist/_dts/theme-core/src/site-styles/appearancePresetTypes.d.ts +9 -4
  152. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +0 -20
  153. package/dist/_dts/theme-core/src/site-styles/blockAppearancePresets.d.ts +84 -102
  154. package/dist/_dts/theme-core/src/site-styles/cardRoleVocabulary.d.ts +48 -0
  155. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +21 -385
  156. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +10 -18
  157. package/dist/_dts/theme-core/src/site-styles/headerLayoutTypes.d.ts +2 -4
  158. package/dist/_dts/theme-core/src/site-styles/headerOpeningForeground.d.ts +2 -9
  159. package/dist/_dts/theme-core/src/site-styles/headerOpeningSection.d.ts +0 -2
  160. package/dist/_dts/theme-core/src/site-styles/index.d.ts +5 -0
  161. package/dist/_dts/theme-core/src/site-styles/pageDesignAppearanceAvailability.d.ts +3 -0
  162. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorCapabilities.d.ts +22 -0
  163. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorModel.d.ts +9 -8
  164. package/dist/_dts/theme-core/src/site-styles/pageDesignIntentEquality.d.ts +3 -0
  165. package/dist/_dts/theme-core/src/site-styles/previewRuntimeCapabilities.d.ts +19 -0
  166. package/dist/_dts/theme-core/src/site-styles/runtimeBackgroundResolution.d.ts +9 -0
  167. package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +1 -62
  168. package/dist/_dts/theme-core/src/site-styles/sourceCatalogs.d.ts +13 -12
  169. package/dist/_dts/theme-core/src/site-styles/types.d.ts +50 -39
  170. package/dist/_dts/theme-core/src/siteChromePlanning.d.ts +9 -9
  171. package/dist/_dts/theme-core/src/status/generateStatusCss.d.ts +1 -0
  172. package/dist/_dts/theme-core/src/surfaces/index.d.ts +1 -0
  173. package/dist/_dts/theme-core/src/surfaces/resolveSurfaceForeground.d.ts +39 -0
  174. package/dist/_dts/ui/src/navigation-tabs.d.ts +3 -2
  175. package/dist/client/{CourseRegistrationWizard-Bvtz9G9g.mjs → CourseRegistrationWizard-DGW9F0SQ.mjs} +7 -7
  176. package/dist/client/{DiscountCodeDisclosure-Ur-Xx1U_.mjs → DiscountCodeDisclosure-BPZ-yfGI.mjs} +1 -1
  177. package/dist/client/{EventCombined.client-EhscD4U6.mjs → EventCombined.client-Cjt3kKYR.mjs} +4 -4
  178. package/dist/client/{EventListing.client-Bn26cFTY.mjs → EventListing.client-DY9dn6Fh.mjs} +2 -2
  179. package/dist/client/{FeedbackMessage-ByMrs3cr.mjs → FeedbackMessage-CtLvvS_d.mjs} +15 -3
  180. package/dist/client/{LeafletStaticMap-BUw9Ntzr.mjs → LeafletStaticMap--2c61XYn.mjs} +1 -1
  181. package/dist/client/{StepIndicator-BfbdiVQI.mjs → StepIndicator-CLjQhkDb.mjs} +3 -3
  182. package/dist/client/{accordion-CjIeof73.mjs → accordion-_boYwHHi.mjs} +1 -1
  183. package/dist/client/{bookingForm-DwEeovf_.mjs → bookingForm--AqEfyBX.mjs} +56 -39
  184. package/dist/client/bookings.mjs +393 -192
  185. package/dist/client/{carousel-BMrfgOba.mjs → carousel-CVI2dqZr.mjs} +2 -2
  186. package/dist/client/{carousel.client-CpwTUhIo.mjs → carousel.client-59G_WMr4.mjs} +1 -1
  187. package/dist/client/{cart-rXEdxTdb.mjs → cart-DR6IiPz9.mjs} +3 -3
  188. package/dist/client/{checkout-D3JlSzh3.mjs → checkout-CsrGAfQt.mjs} +3 -3
  189. package/dist/client/client.mjs +4524 -3512
  190. package/dist/client/{colorStyles-BWpj_h4C.mjs → colorStyles-CrxJrPjJ.mjs} +277 -1
  191. package/dist/client/{context-CJz1Vlsz.mjs → context-B83PiNzm.mjs} +6 -21
  192. package/dist/client/{courseRegistration-DO5XvMAA.mjs → courseRegistration-aNJVaLa3.mjs} +3 -3
  193. package/dist/client/{eventCalendar-DO6rTqCn.mjs → eventCalendar-OZ7h59VN.mjs} +6 -6
  194. package/dist/client/{eventCombined-bJ3i_ovU.mjs → eventCombined-BbJ6bFKz.mjs} +3 -3
  195. package/dist/client/{eventDetails-CCbrXE7o.mjs → eventDetails-BLyUBiCT.mjs} +3 -3
  196. package/dist/client/{eventListing-Bdlt9YTm.mjs → eventListing-DuP1yhBS.mjs} +3 -3
  197. package/dist/client/{eventRegistration-B02lt6iY.mjs → eventRegistration-BXyECPa-.mjs} +11 -12
  198. package/dist/client/{form-DrUBAAox.mjs → form-BA4t3kKU.mjs} +4 -4
  199. package/dist/client/{headerSection-MzqvHL6T.mjs → headerSection-CpNaJxhW.mjs} +2 -2
  200. package/dist/client/hooks.mjs +1967 -2059
  201. package/dist/client/{locationMap-DG8BxTBm.mjs → locationMap-3HGMCL7_.mjs} +3 -3
  202. package/dist/client/{media-CNE1Z-wb.mjs → media-BOHuueS5.mjs} +16 -4
  203. package/dist/client/{newsletterForm-BzO-Td3p.mjs → newsletterForm-C2s6Mw67.mjs} +4 -4
  204. package/dist/client/{pricing-NgK7ozkg.mjs → pricing-BiqIMgZk.mjs} +1 -1
  205. package/dist/client/{productDetail-CfQ7rhVK.mjs → productDetail-C9joWhdE.mjs} +3 -3
  206. package/dist/client/{productList-C23rOLUi.mjs → productList-CjzdU3pB.mjs} +3 -3
  207. package/dist/client/{propsDecoder-CAZdhwjs.mjs → propsDecoder-CBPGXEYX.mjs} +395 -402
  208. package/dist/client/rendering/client.mjs +2004 -2856
  209. package/dist/client/rendering/islands.mjs +20 -20
  210. package/dist/client/rendering.mjs +4605 -3496
  211. package/dist/client/{scrollToBlock-DSSqVQr1.mjs → scrollToBlock-DZ73aEX6.mjs} +1 -1
  212. package/dist/client/{shop-BMToKLK7.mjs → shop-CQRai7hj.mjs} +7 -7
  213. package/dist/client/{shop-commerce-DB4JRKmp.mjs → shop-commerce-hP9UboOx.mjs} +8 -8
  214. package/dist/client/{shop-commerce-store-BOqSIOuc.mjs → shop-commerce-store-DiD5a32e.mjs} +1 -1
  215. package/dist/client/{shopShared-B6SK9DZv.mjs → shopShared-DP8DXTkF.mjs} +5 -5
  216. package/dist/client/{useEventFilters-CULOa4nB.mjs → useEventFilters-pF_OJNQP.mjs} +137 -81
  217. package/dist/client/{usePolling-BGG_8-jl.mjs → usePolling-Cls91SD2.mjs} +2 -2
  218. package/dist/preview-next/before-render.mjs +192 -108
  219. package/dist/preview-next/client/runtime.mjs +4 -4
  220. package/dist/preview-next/keepAlive.mjs +7406 -29
  221. package/dist/preview-next/middleware.mjs +186 -107
  222. package/dist/{previewAuthStore-_GXFRg2W.mjs → previewAuthStore-CX1C_KJ4.mjs} +185 -107
  223. package/dist/{sdk-runtime-hhSOMShR.mjs → sdk-runtime-5CcPAI14.mjs} +2 -2
  224. package/dist/{separator-flRaFc14.mjs → separator-BXlYN_gr.mjs} +1 -1
  225. package/dist/server/components.mjs +6363 -4998
  226. package/dist/server/config-validation.mjs +1967 -2059
  227. package/dist/server/config.mjs +1967 -2059
  228. package/dist/server/data.mjs +1967 -2059
  229. package/dist/server/index.mjs +186 -108
  230. package/dist/server/next.mjs +6553 -5285
  231. package/dist/server/page-converter.mjs +1031 -1844
  232. package/dist/server/prebuild.mjs +1 -1
  233. package/dist/server/rendering/server.mjs +3352 -3006
  234. package/dist/server/rendering.mjs +6289 -4924
  235. package/dist/server/routing.mjs +1906 -1036
  236. package/dist/server/server.mjs +1976 -2165
  237. package/dist/server/theme-bridge.mjs +949 -670
  238. package/dist/server/theme.mjs +252 -51
  239. package/dist/styles/index.css +295 -334
  240. package/package.json +3 -2
  241. package/dist/_dts/block-form/src/client/blockDraftSaveability.d.ts +0 -1
  242. package/dist/_dts/block-form/src/widgets/index.d.ts +0 -20
  243. package/dist/_dts/blocks/src/system/runtime/api/requestBody.d.ts +0 -7
  244. package/dist/client/clsx-DgIEfj3H.mjs +0 -16
@@ -3283,6 +3283,26 @@ function presetCategory(recipe) {
3283
3283
  //#region ../theme-core/src/cards/types.ts
3284
3284
  const cardVariantRoleValues = ["default", "alternative"];
3285
3285
  const boxlessCardVariantValue = "none";
3286
+ const openCardRoleValue = "open";
3287
+ /**
3288
+ * The roles a site style implements with an actual card variant. `none` is
3289
+ * deliberately absent: it is a role value only, never a variant.
3290
+ */
3291
+ const surfaceCardRoleValues = [...cardVariantRoleValues, openCardRoleValue];
3292
+ /**
3293
+ * The closed card-role vocabulary Looks and blocks address cards by — this is
3294
+ * `BoxStylesCardVariant` widened with `open`, not a parallel enum.
3295
+ *
3296
+ * - `none` — no site-style card surface; the block's own composition and
3297
+ * the author's own card fields govern the item. This is *not*
3298
+ * a promise of visible boxlessness: a block whose base class
3299
+ * draws a border still draws one. It says the Look is not
3300
+ * asking the site style for a card.
3301
+ * - `open` — boxless but marked (a rule or accent edge, no enclosing box)
3302
+ * - `default` — the site's quiet everyday card
3303
+ * - `alternative` — the statement card carrying the site's personality
3304
+ */
3305
+ const cardRoleValues = [...surfaceCardRoleValues, boxlessCardVariantValue];
3286
3306
  const cardSpacingSchema = z.enum([
3287
3307
  "none",
3288
3308
  "compact",
@@ -3291,6 +3311,17 @@ const cardSpacingSchema = z.enum([
3291
3311
  "comfortable",
3292
3312
  "spacious"
3293
3313
  ]);
3314
+ /** Per-axis padding, for boxless cards whose inline padding differs from block. */
3315
+ const cardSpacingAxesSchema = z.object({
3316
+ block: cardSpacingSchema,
3317
+ inline: cardSpacingSchema
3318
+ });
3319
+ /**
3320
+ * A card's content padding: one value for both axes, or one per axis. Two
3321
+ * fields would let a variant say both and leave the reader to guess, so this is
3322
+ * a union — the legacy `spacingAxes` key is lifted into it on parse.
3323
+ */
3324
+ const cardSpacingValueSchema = z.union([cardSpacingSchema, cardSpacingAxesSchema]);
3294
3325
  const gradientDirectionSchema = z.enum([
3295
3326
  "to-r",
3296
3327
  "to-l",
@@ -3302,7 +3333,26 @@ const gradientDirectionSchema = z.enum([
3302
3333
  "to-bl"
3303
3334
  ]);
3304
3335
  z.enum(cardVariantRoleValues);
3305
- const boxStylesCardVariantSchema = z.enum([...cardVariantRoleValues, boxlessCardVariantValue]);
3336
+ const surfaceCardRoleSchema = z.enum(surfaceCardRoleValues);
3337
+ const cardRoleSchema = z.enum(cardRoleValues);
3338
+ /**
3339
+ * Historical name for the role vocabulary as it reaches box styles. Widened to
3340
+ * the full `CardRole` set — old values keep parsing, `open` now parses too.
3341
+ */
3342
+ const boxStylesCardVariantSchema = cardRoleSchema;
3343
+ const PRIORITY_ROLE_INFERENCE = {
3344
+ 1: "default",
3345
+ 2: "alternative"
3346
+ };
3347
+ /**
3348
+ * Role inference for legacy card systems authored before variants carried a
3349
+ * `role`. Priority 1 is the everyday card, priority 2 the statement card —
3350
+ * which is how variants are surfaced today. `open` is never inferred: a site
3351
+ * style has to opt into it.
3352
+ */
3353
+ function inferCardVariantRoleFromPriority(priority) {
3354
+ return PRIORITY_ROLE_INFERENCE[priority] ?? null;
3355
+ }
3306
3356
  const cardBackgroundSchema = z.discriminatedUnion("type", [
3307
3357
  z.object({
3308
3358
  type: z.literal("solid"),
@@ -3328,17 +3378,19 @@ const cardCornerValueSchema = z.enum([
3328
3378
  "pill",
3329
3379
  "expressive"
3330
3380
  ]);
3381
+ const cardEdgeSchema = z.enum([
3382
+ "left",
3383
+ "top",
3384
+ "right",
3385
+ "bottom"
3386
+ ]);
3331
3387
  /**
3332
- * A single accented card edge (e.g. a left accent bar). When set, it replaces
3333
- * the full `border` so the card reads as edge-accented rather than outlined.
3388
+ * Legacy single accented card edge (e.g. a left accent bar). Persisted and
3389
+ * curated data may still carry it; it lifts into a one-element `decorations`
3390
+ * list at the parse boundary and has no representation after that.
3334
3391
  */
3335
3392
  const cardAccentEdgeSchema = z.object({
3336
- edge: z.enum([
3337
- "left",
3338
- "top",
3339
- "right",
3340
- "bottom"
3341
- ]),
3393
+ edge: cardEdgeSchema,
3342
3394
  colorToken: themeColorTokenSchema,
3343
3395
  width: z.enum([
3344
3396
  "thin",
@@ -3347,13 +3399,43 @@ const cardAccentEdgeSchema = z.object({
3347
3399
  "heavy"
3348
3400
  ])
3349
3401
  });
3402
+ /** `hairline` (1px) is the widening the boxless `open` role needs. */
3403
+ const cardEdgeRuleWidthSchema = z.enum([
3404
+ "hairline",
3405
+ "thin",
3406
+ "medium",
3407
+ "thick",
3408
+ "heavy"
3409
+ ]);
3410
+ const cardEdgeRuleLengthSchema = z.enum(["full", "short"]);
3411
+ const cardEdgeRuleAlignSchema = z.enum([
3412
+ "start",
3413
+ "center",
3414
+ "end"
3415
+ ]);
3416
+ /**
3417
+ * Decorations are edge rules today. The alias exists so the list can grow into
3418
+ * a discriminated union without every call site changing.
3419
+ */
3420
+ const cardDecorationSchema = z.object({
3421
+ edge: cardEdgeSchema,
3422
+ colorToken: themeColorTokenSchema,
3423
+ width: cardEdgeRuleWidthSchema,
3424
+ /** Defaults to `full`. */
3425
+ length: cardEdgeRuleLengthSchema.optional(),
3426
+ /** Where a `short` rule sits along its edge. Defaults to `start`. */
3427
+ align: cardEdgeRuleAlignSchema.optional(),
3428
+ /** Opacity applied to `colorToken`, 0–1. Scoped here, not on the token. */
3429
+ alpha: z.number().min(0).max(1).optional()
3430
+ });
3431
+ const cardDecorationsSchema = z.array(cardDecorationSchema).max(2);
3350
3432
  const cardCornerRadiiSchema = z.object({
3351
3433
  topLeft: cardCornerValueSchema.optional(),
3352
3434
  topRight: cardCornerValueSchema.optional(),
3353
3435
  bottomLeft: cardCornerValueSchema.optional(),
3354
3436
  bottomRight: cardCornerValueSchema.optional()
3355
3437
  });
3356
- const cardVariantSchema = z.object({
3438
+ const cardVariantInputSchema = z.object({
3357
3439
  id: z.string(),
3358
3440
  name: z.string().min(1).max(20),
3359
3441
  enabled: z.boolean(),
@@ -3362,8 +3444,10 @@ const cardVariantSchema = z.object({
3362
3444
  z.literal(2),
3363
3445
  z.literal(3)
3364
3446
  ]),
3447
+ role: surfaceCardRoleSchema.optional(),
3365
3448
  background: cardBackgroundSchema,
3366
- spacing: cardSpacingSchema,
3449
+ spacing: cardSpacingValueSchema,
3450
+ spacingAxes: cardSpacingAxesSchema.optional(),
3367
3451
  foregroundToken: themeColorTokenSchema.optional(),
3368
3452
  shadow: z.object({
3369
3453
  elevation: z.enum([
@@ -3387,9 +3471,188 @@ const cardVariantSchema = z.object({
3387
3471
  ]).optional(),
3388
3472
  cornerRadii: cardCornerRadiiSchema.optional(),
3389
3473
  border: cardBorderSchema.optional(),
3474
+ decorations: cardDecorationsSchema.optional(),
3390
3475
  accentEdge: cardAccentEdgeSchema.optional()
3391
3476
  });
3392
- const cardSystemSchema = z.object({ variants: z.array(cardVariantSchema).min(1).max(3) });
3477
+ /**
3478
+ * Lift a legacy `accentEdge` into the decoration list. The old emitter reset
3479
+ * `border: 0` implicitly whenever an accent edge was present; conversion drops
3480
+ * the border instead, so the output is unchanged and the data says so.
3481
+ */
3482
+ function normalizeLegacyAccentEdge(variant) {
3483
+ const { accentEdge, ...rest } = normalizeLegacySpacingAxes(variant);
3484
+ if (!accentEdge) return rest;
3485
+ const { border: _droppedByAccentEdgeReset, ...withoutBorder } = rest;
3486
+ return {
3487
+ ...withoutBorder,
3488
+ decorations: [{
3489
+ ...accentEdge,
3490
+ length: "full"
3491
+ }]
3492
+ };
3493
+ }
3494
+ /**
3495
+ * Lift a legacy `spacingAxes` into `spacing`. It always won over the flat value
3496
+ * when both were set, so the lift preserves the emitted padding exactly.
3497
+ */
3498
+ function normalizeLegacySpacingAxes(variant) {
3499
+ const { spacingAxes, ...rest } = variant;
3500
+ return spacingAxes ? {
3501
+ ...rest,
3502
+ spacing: spacingAxes
3503
+ } : variant;
3504
+ }
3505
+ function validateSingleDecorationSource(variant, ctx) {
3506
+ if (!variant.accentEdge || !variant.decorations) return;
3507
+ ctx.addIssue({
3508
+ code: "custom",
3509
+ path: ["decorations"],
3510
+ message: "A card variant carries either the legacy `accentEdge` or `decorations`, never both."
3511
+ });
3512
+ }
3513
+ const cardVariantSchema = cardVariantInputSchema.superRefine(validateSingleDecorationSource).transform(normalizeLegacyAccentEdge);
3514
+ /**
3515
+ * Fill in roles for legacy systems whose variants declare none. Inference is
3516
+ * collision-tolerant on purpose: first claimant wins and a later variant that
3517
+ * would infer a taken role simply gets no role. Legacy data is not obliged to
3518
+ * be well-formed, and a card system that fails to parse takes the whole theme
3519
+ * down with it.
3520
+ */
3521
+ function withInferredRoles(variants) {
3522
+ const claimed = new Set(variants.flatMap((variant) => variant.role ? [variant.role] : []));
3523
+ return variants.map((variant) => {
3524
+ if (variant.role) return variant;
3525
+ const inferred = inferCardVariantRoleFromPriority(variant.priority);
3526
+ if (!inferred || claimed.has(inferred)) return variant;
3527
+ claimed.add(inferred);
3528
+ return {
3529
+ ...variant,
3530
+ role: inferred
3531
+ };
3532
+ });
3533
+ }
3534
+ /**
3535
+ * Only *declared* roles are validated — a site style saying "these two
3536
+ * variants are both the default card" is an authoring error worth rejecting.
3537
+ */
3538
+ function validateUniqueDeclaredCardVariantRoles(system, ctx) {
3539
+ const declared = /* @__PURE__ */ new Set();
3540
+ system.variants.forEach((variant, index) => {
3541
+ const role = variant.role;
3542
+ if (!role) return;
3543
+ if (declared.has(role)) {
3544
+ ctx.addIssue({
3545
+ code: "custom",
3546
+ path: [
3547
+ "variants",
3548
+ index,
3549
+ "role"
3550
+ ],
3551
+ message: `Card role "${role}" is already implemented by another variant.`
3552
+ });
3553
+ return;
3554
+ }
3555
+ declared.add(role);
3556
+ });
3557
+ }
3558
+ const cardSystemSchema = z.object({ variants: z.array(cardVariantSchema).min(1).max(3) }).superRefine(validateUniqueDeclaredCardVariantRoles).transform((system) => ({ variants: withInferredRoles(system.variants) }));
3559
+ //#endregion
3560
+ //#region ../theme-core/src/cards/decorations.ts
3561
+ const CARD_EDGE_RULE_WIDTH_MAP = {
3562
+ hairline: "1px",
3563
+ thin: "2px",
3564
+ medium: "3px",
3565
+ thick: "4px",
3566
+ heavy: "5px"
3567
+ };
3568
+ const CARD_EDGE_BORDER_PROPERTY_MAP = {
3569
+ left: "border-left",
3570
+ top: "border-top",
3571
+ right: "border-right",
3572
+ bottom: "border-bottom"
3573
+ };
3574
+ /**
3575
+ * The one short-rule length in the corpus (the editorial-rule accent). A fixed
3576
+ * constant keeps `length: 'short'` a semantic choice rather than a free number.
3577
+ */
3578
+ const SHORT_CARD_EDGE_RULE_LENGTH = "2.75rem";
3579
+ function isFullLength(rule) {
3580
+ return (rule.length ?? "full") === "full";
3581
+ }
3582
+ /** Apply a decoration's optional alpha to its resolved colour. */
3583
+ function cardDecorationColor(rule, resolveColor) {
3584
+ const color = resolveColor(rule.colorToken);
3585
+ if (rule.alpha === void 0 || rule.alpha >= 1) return color;
3586
+ return `color-mix(in oklab, ${color}, transparent ${Math.round((1 - rule.alpha) * 100)}%)`;
3587
+ }
3588
+ /**
3589
+ * Container border declarations for a variant's border and its full-length
3590
+ * edge rules. The two are independent: a card may be outlined *and* carry an
3591
+ * accent edge. A variant with edge rules and no border still zeroes the box so
3592
+ * only the decorated sides draw.
3593
+ */
3594
+ function cardBorderDeclarations(borderDeclaration, decorations, resolveColor) {
3595
+ const fullLengthRules = (decorations ?? []).filter(isFullLength);
3596
+ const declarations = [];
3597
+ if (borderDeclaration) declarations.push(borderDeclaration);
3598
+ else if (fullLengthRules.length > 0) declarations.push("border: 0");
3599
+ for (const rule of fullLengthRules) {
3600
+ const side = CARD_EDGE_BORDER_PROPERTY_MAP[rule.edge];
3601
+ const width = CARD_EDGE_RULE_WIDTH_MAP[rule.width];
3602
+ declarations.push(`${side}: ${width} solid ${cardDecorationColor(rule, resolveColor)}`);
3603
+ }
3604
+ return declarations;
3605
+ }
3606
+ function horizontalAlignDeclarations(align) {
3607
+ switch (align ?? "start") {
3608
+ case "start": return ["left: 0"];
3609
+ case "center": return ["left: 50%", "transform: translateX(-50%)"];
3610
+ case "end": return ["right: 0"];
3611
+ }
3612
+ }
3613
+ function verticalAlignDeclarations(align) {
3614
+ switch (align ?? "start") {
3615
+ case "start": return ["top: 0"];
3616
+ case "center": return ["top: 50%", "transform: translateY(-50%)"];
3617
+ case "end": return ["bottom: 0"];
3618
+ }
3619
+ }
3620
+ function shortEdgeRuleDeclarations(rule, resolveColor) {
3621
+ const thickness = CARD_EDGE_RULE_WIDTH_MAP[rule.width];
3622
+ const isHorizontal = rule.edge === "top" || rule.edge === "bottom";
3623
+ return [
3624
+ "content: \"\"",
3625
+ "position: absolute",
3626
+ `${rule.edge}: 0`,
3627
+ ...isHorizontal ? horizontalAlignDeclarations(rule.align) : verticalAlignDeclarations(rule.align),
3628
+ `width: ${isHorizontal ? SHORT_CARD_EDGE_RULE_LENGTH : thickness}`,
3629
+ `height: ${isHorizontal ? thickness : SHORT_CARD_EDGE_RULE_LENGTH}`,
3630
+ `background: ${cardDecorationColor(rule, resolveColor)}`
3631
+ ];
3632
+ }
3633
+ /**
3634
+ * The pseudo-elements a card container can lend to short edge rules. A short
3635
+ * rule cannot be a border, so it is drawn on `::before` / `::after` against the
3636
+ * container (which always sets `position: relative`) — and a card has exactly
3637
+ * two of those.
3638
+ */
3639
+ const CARD_DECORATION_PSEUDOS = ["::before", "::after"];
3640
+ /**
3641
+ * Complete pseudo-element rules for a variant's short edge rules.
3642
+ *
3643
+ * `cardDecorationsSchema` caps the list at two, which makes the two pseudos
3644
+ * sufficient for anything that parsed. The dependency-free core types carry a
3645
+ * plain array with no such cap, so short rules past the second are dropped
3646
+ * here rather than silently overwriting `::after`.
3647
+ */
3648
+ function cardDecorationPseudoRules(containerSelectors, decorations, resolveColor) {
3649
+ const shortRules = (decorations ?? []).filter((rule) => !isFullLength(rule));
3650
+ return CARD_DECORATION_PSEUDOS.flatMap((pseudo, index) => {
3651
+ const rule = shortRules[index];
3652
+ if (!rule) return [];
3653
+ return [`${containerSelectors.map((containerSelector) => `${containerSelector}${pseudo}`).join(",\n")} {\n ${shortEdgeRuleDeclarations(rule, resolveColor).join(";\n ")};\n}`];
3654
+ });
3655
+ }
3393
3656
  //#endregion
3394
3657
  //#region ../theme-core/src/cards/core.ts
3395
3658
  /**
@@ -3398,6 +3661,10 @@ const cardSystemSchema = z.object({ variants: z.array(cardVariantSchema).min(1).
3398
3661
  * Pure functions for generating card CSS without requiring the full Theme type.
3399
3662
  * Used by both the full CMS theme system and the SDK theme bridge.
3400
3663
  */
3664
+ /** Core colours are always emitted as raw token references. */
3665
+ function coreColor(token) {
3666
+ return `rgb(var(--tb-${token}))`;
3667
+ }
3401
3668
  const CORNER_RADIUS_MAP$2 = {
3402
3669
  square: "0",
3403
3670
  soft: "0.5rem",
@@ -3417,25 +3684,29 @@ const PER_CORNER_RADIUS_MAP = {
3417
3684
  pill: "1.5rem",
3418
3685
  expressive: "calc(var(--rb-block-radius) * 3)"
3419
3686
  };
3420
- const ACCENT_EDGE_WIDTH_MAP = {
3421
- thin: "2px",
3422
- medium: "3px",
3423
- thick: "4px",
3424
- heavy: "5px"
3425
- };
3426
- const ACCENT_EDGE_SIDE_MAP = {
3427
- left: "border-left",
3428
- top: "border-top",
3429
- right: "border-right",
3430
- bottom: "border-bottom"
3431
- };
3687
+ /**
3688
+ * The radius one corner resolves to: the variant's own override when it has
3689
+ * one, otherwise the global corners setting. Container geometry and the media
3690
+ * hook's top corners both go through here, so a variant that squares itself
3691
+ * cannot end up with rounded media.
3692
+ */
3693
+ function resolveCornerCss(value, fallback) {
3694
+ return value ? PER_CORNER_RADIUS_MAP[value] : CORNER_RADIUS_MAP$2[fallback];
3695
+ }
3696
+ /**
3697
+ * The media hook's top corners, resolved from the same geometry the container
3698
+ * used. A variant that overrides its corners (the `open` card is square)
3699
+ * therefore cannot leave its media rounded by the global setting.
3700
+ */
3701
+ function mediaCornerRules(cornerRadii, fallback) {
3702
+ return [`border-top-left-radius: ${resolveCornerCss(cornerRadii?.topLeft, fallback)}`, `border-top-right-radius: ${resolveCornerCss(cornerRadii?.topRight, fallback)}`];
3703
+ }
3432
3704
  /**
3433
3705
  * Resolve a `border-radius` shorthand from per-corner overrides, filling any
3434
3706
  * unspecified corner from the global corners setting.
3435
3707
  */
3436
3708
  function resolveCornerRadiiCss(cornerRadii, fallback) {
3437
- const fallbackValue = CORNER_RADIUS_MAP$2[fallback];
3438
- const at = (value) => value ? PER_CORNER_RADIUS_MAP[value] : fallbackValue;
3709
+ const at = (value) => resolveCornerCss(value, fallback);
3439
3710
  return `${at(cornerRadii.topLeft)} ${at(cornerRadii.topRight)} ${at(cornerRadii.bottomRight)} ${at(cornerRadii.bottomLeft)}`;
3440
3711
  }
3441
3712
  const GRADIENT_DIRECTION_MAP = {
@@ -3473,70 +3744,96 @@ const SPACING_MAP = {
3473
3744
  * @param settings - Global card settings
3474
3745
  * @returns CSS string
3475
3746
  */
3476
- function generateCardCoreCss(scopeId, variants, settings) {
3747
+ function generateCardCoreCss(scopeId, variants, settings, scope = "all") {
3477
3748
  const cssChunks = [];
3478
3749
  for (const variant of variants) {
3479
- const css = generateVariantCss$1(scopeId, variant, settings);
3480
- cssChunks.push(css);
3750
+ if (cardVariantClasses(variant, scope).length === 0) continue;
3751
+ cssChunks.push(generateVariantCss$1(scopeId, variant, settings, scope));
3481
3752
  }
3482
3753
  return cssChunks.join("\n\n");
3483
3754
  }
3484
3755
  /**
3485
- * Generate CSS for a single card variant
3486
- *
3487
- * Selectors use :where() for zero specificity, allowing site styles to override easily.
3756
+ * A variant's background and, when it owns one, the complete foreground
3757
+ * contract that goes with it. Opaque cards are surface providers; transparent
3758
+ * variants inherit the nearest outer surface atomically and declare nothing.
3488
3759
  */
3489
- function generateVariantCss$1(scopeId, variant, settings) {
3490
- const containerSelector = `:where([data-theme-scope="${scopeId}"]) .card-${variant.id}`;
3491
- const mediaSelector = `${containerSelector} .card-media`;
3492
- const contentSelector = `${containerSelector} .card-content`;
3493
- const containerRules = [];
3494
- let ownsBackground = false;
3495
- if (variant.background === "solid" && variant.backgroundToken) {
3496
- containerRules.push(`background: rgb(var(--tb-${variant.backgroundToken}))`);
3497
- ownsBackground = true;
3498
- } else if (variant.background === "gradient" && variant.gradientFromToken && variant.gradientToToken) {
3760
+ function cardSurfaceDeclarations(variant) {
3761
+ if (variant.background === "solid" && variant.backgroundToken) return [`background: rgb(var(--tb-${variant.backgroundToken}))`, ...surfaceForegroundDeclarations({ text: `rgb(var(--tb-${variant.foregroundToken ?? "text"}))` })];
3762
+ if (variant.background === "gradient" && variant.gradientFromToken && variant.gradientToToken) {
3499
3763
  const fromColor = `rgb(var(--tb-${variant.gradientFromToken}))`;
3500
3764
  const toColor = `rgb(var(--tb-${variant.gradientToToken}))`;
3501
- const direction = GRADIENT_DIRECTION_MAP[variant.gradientDirection ?? "to-r"];
3502
- containerRules.push(`background: linear-gradient(${direction}, ${fromColor}, ${toColor})`);
3503
- ownsBackground = true;
3504
- } else containerRules.push("background: transparent");
3505
- if (ownsBackground) {
3506
- const foreground = `rgb(var(--tb-${variant.foregroundToken ?? "text"}))`;
3507
- containerRules.push(...surfaceForegroundDeclarations({ text: foreground }));
3508
- }
3509
- if (variant.accentEdge) {
3510
- const side = ACCENT_EDGE_SIDE_MAP[variant.accentEdge.edge];
3511
- const width = ACCENT_EDGE_WIDTH_MAP[variant.accentEdge.width];
3512
- containerRules.push("border: 0");
3513
- containerRules.push(`${side}: ${width} solid rgb(var(--tb-${variant.accentEdge.colorToken}))`);
3514
- } else {
3515
- const borderWidth = variant.borderWidth ? BORDER_WIDTH_MAP$2[variant.borderWidth] : variant.borderToken ? BORDER_WIDTH_MAP$2[settings.borderWidth] : "0";
3516
- if (variant.borderToken && borderWidth !== "0") containerRules.push(`border: ${borderWidth} solid rgb(var(--tb-${variant.borderToken}))`);
3765
+ return [`background: linear-gradient(${GRADIENT_DIRECTION_MAP[variant.gradientDirection ?? "to-r"]}, ${fromColor}, ${toColor})`, ...surfaceForegroundDeclarations({ text: `rgb(var(--tb-${variant.foregroundToken ?? "text"}))` })];
3517
3766
  }
3767
+ return ["background: transparent"];
3768
+ }
3769
+ /**
3770
+ * Generate CSS for a single card variant
3771
+ *
3772
+ * Selectors use :where() for zero specificity, allowing site styles to override easily.
3773
+ */
3774
+ function generateVariantCss$1(scopeId, variant, settings, scope = "all") {
3775
+ const containerSelectors = cardVariantClasses(variant, scope).map((cardClass) => `:where([data-theme-scope="${scopeId}"]) .${cardClass}`);
3776
+ const containerSelector = containerSelectors.join(",\n");
3777
+ const mediaSelector = containerSelectors.map((selector) => `${selector} .card-media`).join(",\n");
3778
+ const contentSelector = containerSelectors.map((selector) => `${selector} .card-content`).join(",\n");
3779
+ const fallbackBackgroundSelector = containerSelectors.map((selector) => `${selector} > .rb-bg-surface`).join(",\n");
3780
+ const containerRules = [...cardSurfaceDeclarations(variant)];
3781
+ const decorations = variant.decorations;
3782
+ const borderWidth = variant.borderWidth ? BORDER_WIDTH_MAP$2[variant.borderWidth] : variant.borderToken ? BORDER_WIDTH_MAP$2[settings.borderWidth] : "0";
3783
+ const border = variant.borderToken && borderWidth !== "0" ? `border: ${borderWidth} solid ${coreColor(variant.borderToken)}` : null;
3784
+ containerRules.push(...cardBorderDeclarations(border, decorations, coreColor));
3518
3785
  containerRules.push(`border-radius: ${variant.cornerRadii ? resolveCornerRadiiCss(variant.cornerRadii, settings.corners) : CORNER_RADIUS_MAP$2[settings.corners]}`);
3519
- if (settings.shadow !== "none") containerRules.push(`box-shadow: ${SHADOW_VALUES$2[settings.shadow]}`);
3786
+ if (settings.shadow !== "none" && !variant.suppressShadow) containerRules.push(`box-shadow: ${SHADOW_VALUES$2[settings.shadow]}`);
3520
3787
  containerRules.push("display: flex");
3521
3788
  containerRules.push("flex-direction: column");
3522
3789
  containerRules.push("position: relative");
3523
3790
  containerRules.push("overflow: visible");
3524
- const mediaRadius = CORNER_RADIUS_MAP$2[settings.corners];
3525
- const mediaRules = [`border-top-left-radius: ${mediaRadius}`, `border-top-right-radius: ${mediaRadius}`];
3791
+ const mediaRules = mediaCornerRules(variant.cornerRadii, settings.corners);
3526
3792
  const contentRules = [`padding: ${SPACING_MAP[variant.spacing]}`];
3527
3793
  return [
3528
3794
  `${containerSelector} {\n ${containerRules.join(";\n ")};\n}`,
3795
+ ...cardDecorationPseudoRules(containerSelectors, decorations, coreColor),
3796
+ `${fallbackBackgroundSelector} {\n background: transparent;\n}`,
3529
3797
  `${mediaSelector} {\n ${mediaRules.join(";\n ")};\n}`,
3530
3798
  `${contentSelector} {\n ${contentRules.join(";\n ")};\n}`
3531
3799
  ].join("\n\n");
3532
3800
  }
3801
+ /** Every class a variant's rule is addressed by, within a scope. */
3802
+ function cardVariantClasses(variant, scope = "all") {
3803
+ const roleAliases = variant.role ? [`card-role-${variant.role}`] : [];
3804
+ if (scope === "role-aliases") return roleAliases;
3805
+ const named = [`card-${variant.id}`, ...(variant.legacyIds ?? []).map((legacyId) => `card-${legacyId}`)];
3806
+ if (scope === "named") return named;
3807
+ return [
3808
+ `card-${variant.id}`,
3809
+ ...roleAliases,
3810
+ ...named.slice(1)
3811
+ ];
3812
+ }
3533
3813
  /**
3534
- * Get default card variants for SDK usage.
3814
+ * The default card variants for SDK/bridge themes — one per surface-backed
3815
+ * role, so a bridge theme implements the same card contract a site style does
3816
+ * and compiled `_cardStyles.role` renders on it.
3817
+ *
3818
+ * Two of the pre-role trio survive as legacy ids: `.card-elevated` is the
3819
+ * `alternative` card under its old name, and both were the same surface before
3820
+ * the rename. `outlined` is different — a fully bordered box is not what the
3821
+ * `open` role means (boxless but marked), so it keeps its own variant and its
3822
+ * own declarations as a *compatibility class only*. It carries no role, which
3823
+ * means:
3824
+ *
3825
+ * - `.card-outlined` renders exactly what it always did;
3826
+ * - `role: 'open'` renders the boxless marked card on a bridge theme, the same
3827
+ * thing it renders under a site style;
3828
+ * - naming `outlined` in `components.cards.variants` enables the compatibility
3829
+ * class and nothing else — it does not publish `.card-role-open`, because
3830
+ * selecting a legacy name is not a claim on a semantic role.
3535
3831
  */
3536
3832
  function getDefaultCardVariants() {
3537
3833
  return [
3538
3834
  {
3539
3835
  id: "default",
3836
+ role: "default",
3540
3837
  background: "solid",
3541
3838
  backgroundToken: "surface",
3542
3839
  spacing: "comfortable",
@@ -3544,11 +3841,38 @@ function getDefaultCardVariants() {
3544
3841
  borderWidth: "thin"
3545
3842
  },
3546
3843
  {
3547
- id: "elevated",
3844
+ id: "alternative",
3845
+ role: "alternative",
3846
+ legacyIds: ["elevated"],
3548
3847
  background: "solid",
3549
3848
  backgroundToken: "surface",
3550
3849
  spacing: "comfortable"
3551
3850
  },
3851
+ {
3852
+ id: "open",
3853
+ role: "open",
3854
+ background: "transparent",
3855
+ spacing: "comfortable",
3856
+ suppressShadow: true,
3857
+ cornerRadii: {
3858
+ topLeft: "square",
3859
+ topRight: "square",
3860
+ bottomLeft: "square",
3861
+ bottomRight: "square"
3862
+ },
3863
+ decorations: [{
3864
+ edge: "top",
3865
+ colorToken: "border",
3866
+ width: "hairline",
3867
+ length: "full"
3868
+ }, {
3869
+ edge: "top",
3870
+ colorToken: "primary",
3871
+ width: "thick",
3872
+ length: "short",
3873
+ align: "start"
3874
+ }]
3875
+ },
3552
3876
  {
3553
3877
  id: "outlined",
3554
3878
  background: "transparent",
@@ -4424,6 +4748,58 @@ new Set(themeV2NormalEditorRawStyleFieldNames);
4424
4748
  [...themeV2RuntimeStyleFieldNames, ...themeV2BlockSpecificRawStyleFieldNames];
4425
4749
  const pageDesignEditorBackgroundOverrideOwner = "page-design-editor";
4426
4750
  const siteChromeBackgroundOverrideOwner = "site-chrome-editor";
4751
+ //#endregion
4752
+ //#region ../theme-core/src/header/legacyHeaderVariant.ts
4753
+ /**
4754
+ * The header variants the system actually renders.
4755
+ *
4756
+ * Each one has a layout row in the site-header block and a paint target in
4757
+ * `paintTargets.ts`. This is the canonical list: the block transform's variant
4758
+ * class allowlist and the AI theme generator's enum both derive from it, so
4759
+ * they cannot drift.
4760
+ */
4761
+ const headerVariantValues = [
4762
+ "classic",
4763
+ "centered",
4764
+ "floating"
4765
+ ];
4766
+ z.enum(headerVariantValues);
4767
+ /**
4768
+ * Header variant spellings that were once authorable but no longer name a
4769
+ * layout of their own, and the variant they read as now.
4770
+ *
4771
+ * `transparent` was an overlay header with no CSS of its own: everything it
4772
+ * did (transparent root background, overlay content flow) is what `floating`
4773
+ * already does, and it had zero persisted usage in production or locally when
4774
+ * it was retired. It survives here as a one-boundary alias so a theme authored
4775
+ * against the old vocabulary still parses.
4776
+ *
4777
+ * This matters because the theme schema is strict and has no per-field
4778
+ * `.catch()`: an out-of-enum variant fails `themeSchema.safeParse` for the
4779
+ * *whole* theme, which silently swaps the site's theme for
4780
+ * `modernMinimalTheme`. Aliasing here keeps that from happening.
4781
+ */
4782
+ const legacyHeaderVariants = { transparent: "floating" };
4783
+ const legacyHeaderVariantValues = Object.keys(legacyHeaderVariants);
4784
+ /**
4785
+ * The header variant boundary for themes that go through `themeSchema`:
4786
+ * canonical variants pass through, legacy spellings normalize to the variant
4787
+ * they meant, everything else fails the parse.
4788
+ *
4789
+ * Deliberately an enum plus a transform rather than a `z.preprocess`, so the
4790
+ * schema's *input* type stays an accurate union (the legacy spelling is still
4791
+ * accepted on the wire) instead of widening to `unknown`. `SdkThemeWire` in
4792
+ * `@riverbankcms/api` is `z.input<typeof themeSchema>`, so a widened input here
4793
+ * would leak straight into a public type.
4794
+ *
4795
+ * Not every read of a persisted theme goes through the schema:
4796
+ * `resolveRuntimeTheme` in
4797
+ * `apps/dashboard/src/server/services/sites/runtimeSiteResponse.ts` returns the
4798
+ * raw database JSON asserted as `Theme` with no parse, so a theme still
4799
+ * carrying a legacy spelling reaches the theme editor un-aliased. That gap
4800
+ * predates this alias and is harmless while persisted usage is zero.
4801
+ */
4802
+ const headerVariantWithLegacyAliasSchema = z.enum([...headerVariantValues, ...legacyHeaderVariantValues]).transform((value) => value in legacyHeaderVariants ? legacyHeaderVariants[value] : value);
4427
4803
  const BREAKPOINT_LG = 1024;
4428
4804
  const BREAKPOINT_XL = 1280;
4429
4805
  const BREAKPOINT_LG_DOWN = BREAKPOINT_LG - 1;
@@ -4740,14 +5116,7 @@ const cardStylesOverrideSchema = z.object({
4740
5116
  spacing: semanticSpacingSchema.nullable().optional(),
4741
5117
  raised: z.boolean().nullable().optional(),
4742
5118
  rounded: boxRoundedSchema.nullable().optional(),
4743
- treatment: z.enum([
4744
- "accent-quote",
4745
- "editorial-rule",
4746
- "soft-panel",
4747
- "editorial-serif-quote",
4748
- "editorial-open",
4749
- "color-blocked"
4750
- ]).nullable().optional(),
5119
+ role: cardRoleSchema.nullable().optional(),
4751
5120
  cardVariant: boxStylesCardVariantSchema.nullable().optional()
4752
5121
  });
4753
5122
  const blockStyleOverridesSchema = z.object({
@@ -4853,30 +5222,6 @@ const motion = z.object({
4853
5222
  ]).nullable()
4854
5223
  });
4855
5224
  z.object({ shape: corners });
4856
- z.object({
4857
- elevation: z.enum([
4858
- "none",
4859
- "low",
4860
- "medium",
4861
- "high"
4862
- ]),
4863
- border: z.enum([
4864
- "none",
4865
- "subtle",
4866
- "defined"
4867
- ]),
4868
- shape: corners,
4869
- headerStyle: z.enum([
4870
- "plain",
4871
- "accentBar",
4872
- "subtleBg"
4873
- ]).nullable(),
4874
- mediaTreatment: z.enum([
4875
- "square",
4876
- "rounded",
4877
- "bleed"
4878
- ]).nullable()
4879
- });
4880
5225
  const inputStyle = z.object({
4881
5226
  shape: corners,
4882
5227
  border: z.enum([
@@ -4891,12 +5236,6 @@ const inputStyle = z.object({
4891
5236
  ]),
4892
5237
  label: z.enum(["inside", "above"])
4893
5238
  });
4894
- const headerVariant = z.enum([
4895
- "classic",
4896
- "centered",
4897
- "transparent",
4898
- "floating"
4899
- ]);
4900
5239
  const headerPositioning = z.enum([
4901
5240
  "static",
4902
5241
  "sticky",
@@ -5040,7 +5379,7 @@ const headerBackgroundSchema = z.object({
5040
5379
  textColor: tokenOrCssColorValueSchema.nullable().optional()
5041
5380
  });
5042
5381
  const headerSchema = z.object({
5043
- variant: headerVariant,
5382
+ variant: headerVariantWithLegacyAliasSchema,
5044
5383
  positioning: headerPositioning,
5045
5384
  shrinkOnScroll: z.boolean(),
5046
5385
  maxWidth: headerMaxWidth,
@@ -6970,14 +7309,15 @@ const softEarthAccordionSystem = {
6970
7309
  /**
6971
7310
  * Reusable card systems for curated site styles.
6972
7311
  *
6973
- * generateCardCss emits a `.card-{id}` class per enabled variant. The
6974
- * intended convention (block-level role selection is wired in a follow-up) is
6975
- * a role pair:
7312
+ * generateCardCss emits a `.card-{id}` class per enabled variant, plus stable
7313
+ * `.card-role-{role}` aliases resolved through the fallback chain. Each variant
7314
+ * declares which role it implements:
6976
7315
  * - `default` — the quiet, everyday card (most blocks, most of the time)
6977
7316
  * - `alternative` — the prominent "statement" card for sections that pop
6978
- * A card-heavy site style may add a third; the schema allows up to three
6979
- * variants. The planned fallback when a block references a missing variant is
6980
- * `alternative`, then `default`.
7317
+ * - `open` — boxless but marked; falls back to the generated baseline
7318
+ * The schema allows three variants, one per role. A block asking for a role a
7319
+ * site style does not define falls back per `resolveCardRole`: `alternative` →
7320
+ * `default` → generated baseline.
6981
7321
  *
6982
7322
  * Keep `default` close to the generated baseline so shared consumers (event
6983
7323
  * cards, testimonials) stay stable; put the personality in `alternative`.
@@ -6993,6 +7333,7 @@ const southWestCardSystem = { variants: [{
6993
7333
  name: "Default",
6994
7334
  enabled: true,
6995
7335
  priority: 1,
7336
+ role: "default",
6996
7337
  background: {
6997
7338
  type: "solid",
6998
7339
  colorToken: "surface"
@@ -7010,6 +7351,7 @@ const southWestCardSystem = { variants: [{
7010
7351
  name: "Statement",
7011
7352
  enabled: true,
7012
7353
  priority: 2,
7354
+ role: "alternative",
7013
7355
  background: {
7014
7356
  type: "solid",
7015
7357
  colorToken: "primary-700"
@@ -7021,11 +7363,12 @@ const southWestCardSystem = { variants: [{
7021
7363
  softness: null,
7022
7364
  position: "bottom"
7023
7365
  },
7024
- accentEdge: {
7366
+ decorations: [{
7025
7367
  edge: "left",
7026
7368
  colorToken: "secondary",
7027
- width: "heavy"
7028
- },
7369
+ width: "heavy",
7370
+ length: "full"
7371
+ }],
7029
7372
  cornerRadii: {
7030
7373
  topLeft: "square",
7031
7374
  topRight: "expressive",
@@ -7038,6 +7381,7 @@ const clarityZenCardSystem = { variants: [{
7038
7381
  name: "Default",
7039
7382
  enabled: true,
7040
7383
  priority: 1,
7384
+ role: "default",
7041
7385
  background: {
7042
7386
  type: "solid",
7043
7387
  colorToken: "surface"
@@ -7055,6 +7399,7 @@ const clarityZenCardSystem = { variants: [{
7055
7399
  name: "Statement",
7056
7400
  enabled: true,
7057
7401
  priority: 2,
7402
+ role: "alternative",
7058
7403
  background: {
7059
7404
  type: "solid",
7060
7405
  colorToken: "surfaceAlt"
@@ -7069,6 +7414,117 @@ const clarityZenCardSystem = { variants: [{
7069
7414
  }
7070
7415
  }] };
7071
7416
  /**
7417
+ * Grounded Practice: the first site style to own the `open` role.
7418
+ *
7419
+ * `default` and `alternative` restate the generated baseline this style's token
7420
+ * recipes already produce (soft corners, flat surface card, tinted statement
7421
+ * card, hairline border) so nothing that renders today moves — declaring a card
7422
+ * system means the `.card-{id}` classes must keep existing for event and
7423
+ * offering cards.
7424
+ *
7425
+ * `open` is the change, and it is the one place this style deliberately
7426
+ * diverges from `boxlessOpenCardVariant` (the generated baseline's
7427
+ * hairline-plus-short-accent marking). Grounded Practice takes Luna's
7428
+ * therapy-page card literally
7429
+ * (`apps/dashboard/scripts/design-bakeoff/results/studies/anna-raven-yoga-20260713-115605Z/revisions/005/index.html`,
7430
+ * `.fit__card`: `padding: 1.6rem 1.45rem 1.75rem; border-top: 2px solid
7431
+ * var(--ochre); background: rgba(242,243,235,.72)`):
7432
+ *
7433
+ * - one **full-length 2px** rule in the style's ochre `accent` (`#c69a3c` —
7434
+ * the mockup's `--ochre`); `width: 'thin'` is the 2px step in
7435
+ * `CARD_EDGE_RULE_WIDTH_MAP`. No hairline underneath and no short marker:
7436
+ * the single rule *is* the marking.
7437
+ * - a light solid fill. Card backgrounds take no alpha, so the token that
7438
+ * reads like the mockup's translucent pale tint is `surfaceAlt`
7439
+ * (`#f2ecdb`), which sits one step lighter than the `muted` band the
7440
+ * Look now paints — the same lift Luna gets from `rgba(242,243,235,.72)`
7441
+ * over `--ground-2`.
7442
+ * - comfortable spacing on both axes. The baseline keeps `inline: 'none'`
7443
+ * so a boxless card stays flush with the section; a filled card needs its
7444
+ * padding back or the text touches the fill's edge.
7445
+ *
7446
+ * The baseline `boxlessOpenCardVariant` is unchanged — this divergence is
7447
+ * grounded-practice's alone. See
7448
+ * `.project/plans/1297-card-role-contract-site-styles-own-card-surfaces/decisions.md`
7449
+ * §0.7 and "Phase 2.4".
7450
+ */
7451
+ const groundedPracticeCardSystem = { variants: [
7452
+ {
7453
+ id: "default",
7454
+ name: "Default",
7455
+ enabled: true,
7456
+ priority: 1,
7457
+ role: "default",
7458
+ background: {
7459
+ type: "solid",
7460
+ colorToken: "surface"
7461
+ },
7462
+ foregroundToken: "text",
7463
+ spacing: "comfortable",
7464
+ corners: "soft",
7465
+ shadow: {
7466
+ elevation: "none",
7467
+ softness: null,
7468
+ position: "bottom"
7469
+ },
7470
+ border: {
7471
+ widthClass: "border",
7472
+ colorToken: "border"
7473
+ }
7474
+ },
7475
+ {
7476
+ id: "alternative",
7477
+ name: "Statement",
7478
+ enabled: true,
7479
+ priority: 2,
7480
+ role: "alternative",
7481
+ background: {
7482
+ type: "solid",
7483
+ colorToken: "surfaceAlt"
7484
+ },
7485
+ foregroundToken: "text",
7486
+ spacing: "comfortable",
7487
+ corners: "soft",
7488
+ shadow: {
7489
+ elevation: "low",
7490
+ softness: null,
7491
+ position: "bottom"
7492
+ },
7493
+ border: {
7494
+ widthClass: "border",
7495
+ colorToken: "border"
7496
+ }
7497
+ },
7498
+ {
7499
+ id: "open",
7500
+ name: "Open",
7501
+ enabled: true,
7502
+ priority: 3,
7503
+ role: "open",
7504
+ background: {
7505
+ type: "solid",
7506
+ colorToken: "surfaceAlt"
7507
+ },
7508
+ foregroundToken: "text",
7509
+ spacing: {
7510
+ block: "comfortable",
7511
+ inline: "comfortable"
7512
+ },
7513
+ corners: "square",
7514
+ shadow: {
7515
+ elevation: "none",
7516
+ softness: null,
7517
+ position: "bottom"
7518
+ },
7519
+ decorations: [{
7520
+ edge: "top",
7521
+ colorToken: "accent",
7522
+ width: "thin",
7523
+ length: "full"
7524
+ }]
7525
+ }
7526
+ ] };
7527
+ /**
7072
7528
  * Original Joy: warm, flat, generously rounded cards on the dark clay ground.
7073
7529
  * `default` is a quiet brown surface card with cream text; `alternative` is a
7074
7530
  * burnt-orange statement card for sections that should pop. Both flat (the
@@ -7080,6 +7536,7 @@ const originalJoyCardSystem = { variants: [{
7080
7536
  name: "Default",
7081
7537
  enabled: true,
7082
7538
  priority: 1,
7539
+ role: "default",
7083
7540
  background: {
7084
7541
  type: "solid",
7085
7542
  colorToken: "surface"
@@ -7097,6 +7554,7 @@ const originalJoyCardSystem = { variants: [{
7097
7554
  name: "Statement",
7098
7555
  enabled: true,
7099
7556
  priority: 2,
7557
+ role: "alternative",
7100
7558
  background: {
7101
7559
  type: "solid",
7102
7560
  colorToken: "primary"
@@ -7721,16 +8179,7 @@ const generatedDesignOptionCatalog = {
7721
8179
  "hero-image"
7722
8180
  ],
7723
8181
  contentFrames: generatedContentFrames,
7724
- itemSurfaces: [
7725
- "none",
7726
- "default",
7727
- "flat",
7728
- "outlined",
7729
- "raised",
7730
- "inset",
7731
- "editorial",
7732
- "color-blocked"
7733
- ],
8182
+ cardRoles: cardRoleValues,
7734
8183
  transitions: [
7735
8184
  "none",
7736
8185
  "soft-fade-short",
@@ -7747,7 +8196,7 @@ const userDesignOptionCatalog = {
7747
8196
  source: "user",
7748
8197
  sectionSurfaces: generatedDesignOptionCatalog.sectionSurfaces,
7749
8198
  contentFrames: userContentFrames,
7750
- itemSurfaces: generatedDesignOptionCatalog.itemSurfaces,
8199
+ cardRoles: generatedDesignOptionCatalog.cardRoles,
7751
8200
  transitions: [
7752
8201
  ...generatedDesignOptionCatalog.transitions,
7753
8202
  "soft-fade-expansive",
@@ -7763,7 +8212,7 @@ const userDesignOptionCatalog = {
7763
8212
  emphases: generatedDesignOptionCatalog.emphases,
7764
8213
  detachedCustom: false
7765
8214
  };
7766
- userDesignOptionCatalog.sectionSurfaces, userDesignOptionCatalog.contentFrames, userDesignOptionCatalog.itemSurfaces, userDesignOptionCatalog.transitions, userDesignOptionCatalog.emphases;
8215
+ userDesignOptionCatalog.sectionSurfaces, userDesignOptionCatalog.contentFrames, userDesignOptionCatalog.cardRoles, userDesignOptionCatalog.transitions, userDesignOptionCatalog.emphases;
7767
8216
  //#endregion
7768
8217
  //#region ../theme-core/src/site-styles/siteStyleTypography.ts
7769
8218
  const modernSansTypography = {
@@ -8218,6 +8667,8 @@ function defaultTypographyForRecipe(recipe) {
8218
8667
  case "classic-serif": return classicSerifTypography;
8219
8668
  }
8220
8669
  }
8670
+ //#endregion
8671
+ //#region ../theme-core/src/site-styles/designState.ts
8221
8672
  const designResolutionPolicySchema = z.enum([
8222
8673
  "generated",
8223
8674
  "user",
@@ -8226,108 +8677,6 @@ const designResolutionPolicySchema = z.enum([
8226
8677
  ]);
8227
8678
  const runtimeStyleFieldNameSchema = z.enum(themeV2RuntimeStyleFieldNames);
8228
8679
  const nonEmptyRuntimeStyleFieldNamesSchema = z.array(runtimeStyleFieldNameSchema).min(1).transform((fields) => [fields[0], ...fields.slice(1)]);
8229
- const heroLegibilityStrategySchema = z.enum([
8230
- "none",
8231
- "scrim-gradient",
8232
- "solid-panel"
8233
- ]);
8234
- const heroCompositionIntentSchema = z.enum(["center", "bottom-left"]);
8235
- const heroTypographyScaleIntentSchema = z.enum([
8236
- "quiet",
8237
- "editorial",
8238
- "display",
8239
- "poster"
8240
- ]);
8241
- const sectionHeightIntentSchema = z.enum([
8242
- "default",
8243
- "banner",
8244
- "hero",
8245
- "immersive"
8246
- ]);
8247
- const imageSurfaceTreatmentIntensitySchema = z.enum([
8248
- "soft",
8249
- "balanced",
8250
- "strong"
8251
- ]);
8252
- const imageSurfaceMotionSchema = z.enum(["static", "fixed-frame"]);
8253
- const imageSurfaceTreatmentSchema = z.discriminatedUnion("kind", [
8254
- z.object({ kind: z.literal("none") }).strict(),
8255
- z.object({
8256
- kind: z.literal("brand-scrim"),
8257
- token: z.enum([
8258
- "primary",
8259
- "secondary",
8260
- "accent"
8261
- ]),
8262
- intensity: imageSurfaceTreatmentIntensitySchema.optional()
8263
- }).strict(),
8264
- z.object({
8265
- kind: z.literal("brand-wash"),
8266
- token: z.enum([
8267
- "primary",
8268
- "secondary",
8269
- "accent"
8270
- ]),
8271
- intensity: imageSurfaceTreatmentIntensitySchema.optional()
8272
- }).strict(),
8273
- z.object({
8274
- kind: z.literal("neutral-scrim"),
8275
- intensity: imageSurfaceTreatmentIntensitySchema.optional()
8276
- }).strict(),
8277
- z.object({
8278
- kind: z.literal("neutral-wash"),
8279
- intensity: imageSurfaceTreatmentIntensitySchema.optional()
8280
- }).strict(),
8281
- z.object({
8282
- kind: z.literal("solid-panel"),
8283
- intensity: imageSurfaceTreatmentIntensitySchema.optional()
8284
- }).strict()
8285
- ]);
8286
- const testimonialCardTreatmentSchema = z.enum(["default", "accent-quote"]);
8287
- const tileTreatmentSchema = z.enum(tileTreatmentValues);
8288
- const eventListTreatmentSchema = z.enum(eventListTreatmentValues);
8289
- const eyebrowTreatmentSchema = z.enum(eyebrowTreatmentValues);
8290
- function uniqueNonEmpty(values) {
8291
- const uniqueValues = [...new Set(values)];
8292
- const firstValue = uniqueValues[0];
8293
- if (!firstValue) throw new Error("Expected at least one Theme V2 design state option.");
8294
- return [firstValue, ...uniqueValues.slice(1)];
8295
- }
8296
- const userPanelTreatments = uniqueNonEmpty(userDesignOptionCatalog.contentFrames.filter((frame) => frame.kind === "panel").map((frame) => frame.treatment));
8297
- const userFlushTreatments = uniqueNonEmpty(userDesignOptionCatalog.contentFrames.filter((frame) => frame.kind === "flush-panel").map((frame) => frame.treatment));
8298
- const userFlushBleeds = uniqueNonEmpty(userDesignOptionCatalog.contentFrames.filter((frame) => frame.kind === "flush-panel").map((frame) => frame.bleed));
8299
- const contentFrameIntentSchema = z.discriminatedUnion("kind", [
8300
- z.object({ kind: z.literal("plain") }).strict(),
8301
- z.object({
8302
- kind: z.literal("panel"),
8303
- treatment: z.enum(userPanelTreatments)
8304
- }).strict(),
8305
- z.object({
8306
- kind: z.literal("flush-panel"),
8307
- treatment: z.enum(userFlushTreatments),
8308
- bleed: z.enum(userFlushBleeds)
8309
- }).strict()
8310
- ]);
8311
- const blockDesignPlanSchema = z.object({
8312
- blockId: z.string().min(1).transform(asDesignBlockId),
8313
- blockKind: z.string().min(1),
8314
- purpose: z.string().min(1),
8315
- sectionSurface: z.enum(userDesignOptionCatalog.sectionSurfaces),
8316
- contentFrame: contentFrameIntentSchema,
8317
- itemSurface: z.enum(userDesignOptionCatalog.itemSurfaces),
8318
- transitionAfter: z.enum(userDesignOptionCatalog.transitions),
8319
- emphasis: z.enum(userDesignOptionCatalog.emphases),
8320
- sectionHeight: sectionHeightIntentSchema.optional(),
8321
- heroLegibility: heroLegibilityStrategySchema.optional(),
8322
- heroComposition: heroCompositionIntentSchema.optional(),
8323
- heroTypographyScale: heroTypographyScaleIntentSchema.optional(),
8324
- imageSurfaceTreatment: imageSurfaceTreatmentSchema.optional(),
8325
- imageSurfaceMotion: imageSurfaceMotionSchema.optional(),
8326
- testimonialCardTreatment: testimonialCardTreatmentSchema.optional(),
8327
- tileTreatment: tileTreatmentSchema.optional(),
8328
- eventListTreatment: eventListTreatmentSchema.optional(),
8329
- eyebrowTreatment: eyebrowTreatmentSchema.optional()
8330
- }).strict();
8331
8680
  const styleAuthoritySchema = z.discriminatedUnion("kind", [
8332
8681
  z.object({
8333
8682
  kind: z.literal("theme-v2"),
@@ -8346,22 +8695,12 @@ const styleAuthoritySchema = z.discriminatedUnion("kind", [
8346
8695
  reason: z.literal("detached-custom-runtime-fields")
8347
8696
  }).strict()
8348
8697
  ]);
8349
- const blockRuntimeStyleAuthoritySchema = z.object({
8698
+ z.object({
8350
8699
  unit: z.literal("block-runtime-fields"),
8351
8700
  blockId: z.string().min(1).transform(asDesignBlockId),
8352
8701
  fields: nonEmptyRuntimeStyleFieldNamesSchema,
8353
8702
  authority: styleAuthoritySchema
8354
8703
  }).strict();
8355
- z.object({
8356
- schemaVersion: z.literal(1),
8357
- siteStyleId: z.string().min(1).transform(asSiteStyleId),
8358
- templateId: z.string().min(1).transform(asDesignSystemTemplateId),
8359
- templateVersion: z.number().int().nonnegative(),
8360
- resolverVersion: z.number().int().nonnegative(),
8361
- rhythmPresetId: z.string().min(1),
8362
- blocks: z.array(blockDesignPlanSchema),
8363
- styleAuthorities: z.array(blockRuntimeStyleAuthoritySchema)
8364
- }).strict();
8365
8704
  //#endregion
8366
8705
  //#region ../theme-core/src/site-styles/sectionTransitions.ts
8367
8706
  function boundaryRuntimeMetrics(family, size, heightExpression) {
@@ -8488,61 +8827,7 @@ const defaultComponentRuntimeRules = defineComponentRuntimeRules({
8488
8827
  spacing: "none",
8489
8828
  rounded: "none",
8490
8829
  raised: false
8491
- } },
8492
- itemSurfaces: {
8493
- none: null,
8494
- default: {
8495
- spacing: "medium",
8496
- rounded: "lg",
8497
- raised: false
8498
- },
8499
- flat: {
8500
- spacing: "medium",
8501
- rounded: "lg",
8502
- raised: false
8503
- },
8504
- outlined: {
8505
- spacing: "medium",
8506
- rounded: "lg",
8507
- raised: false,
8508
- border: {
8509
- enabled: true,
8510
- width: "1",
8511
- colorToken: "border"
8512
- }
8513
- },
8514
- raised: {
8515
- spacing: "medium",
8516
- rounded: "xl",
8517
- raised: true
8518
- },
8519
- inset: {
8520
- treatment: "soft-panel",
8521
- spacing: "comfortable",
8522
- rounded: "xl",
8523
- raised: false,
8524
- border: { enabled: false },
8525
- background: {
8526
- type: "color",
8527
- color: "surfaceAlt",
8528
- textColor: "text"
8529
- }
8530
- },
8531
- editorial: {
8532
- treatment: "editorial-rule",
8533
- spacing: "none",
8534
- rounded: "none",
8535
- raised: false,
8536
- border: { enabled: false }
8537
- },
8538
- "color-blocked": {
8539
- treatment: "color-blocked",
8540
- spacing: "spacious",
8541
- rounded: "2xl",
8542
- raised: false,
8543
- border: { enabled: false }
8544
- }
8545
- }
8830
+ } }
8546
8831
  });
8547
8832
  function uniformSectionForeground(token) {
8548
8833
  return {
@@ -8608,36 +8893,7 @@ function componentRuntimeRulesForTokenRecipes(recipes) {
8608
8893
  rounded: recipes.radius === "square" ? "none" : cornerTreatment
8609
8894
  }
8610
8895
  },
8611
- flushFrames: defaultComponentRuntimeRules.flushFrames,
8612
- itemSurfaces: {
8613
- none: null,
8614
- default: {
8615
- ...defaultComponentRuntimeRules.itemSurfaces.default,
8616
- rounded: cornerTreatment
8617
- },
8618
- flat: {
8619
- ...defaultComponentRuntimeRules.itemSurfaces.flat,
8620
- rounded: cornerTreatment
8621
- },
8622
- outlined: {
8623
- ...defaultComponentRuntimeRules.itemSurfaces.outlined,
8624
- rounded: cornerTreatment
8625
- },
8626
- raised: {
8627
- ...defaultComponentRuntimeRules.itemSurfaces.raised,
8628
- rounded: cornerTreatment,
8629
- raised
8630
- },
8631
- inset: {
8632
- ...defaultComponentRuntimeRules.itemSurfaces.inset,
8633
- rounded: cornerTreatment
8634
- },
8635
- editorial: defaultComponentRuntimeRules.itemSurfaces.editorial,
8636
- "color-blocked": {
8637
- ...defaultComponentRuntimeRules.itemSurfaces["color-blocked"],
8638
- rounded: cornerTreatment
8639
- }
8640
- }
8896
+ flushFrames: defaultComponentRuntimeRules.flushFrames
8641
8897
  });
8642
8898
  }
8643
8899
  const kirtanOrnateDesignRuntimeRules = defineDesignRuntimeRules({
@@ -9087,7 +9343,6 @@ function defineCuratedSiteStyle(input) {
9087
9343
  compositionBudget: input.compositionBudget,
9088
9344
  supportedSectionSurfaces: generatedDesignOptionCatalog.sectionSurfaces,
9089
9345
  supportedContentFrames: input.supportedContentFrames ?? generatedContentFrameKinds,
9090
- supportedItemSurfaces: input.supportedItemSurfaces ?? generatedDesignOptionCatalog.itemSurfaces,
9091
9346
  supportedTransitions: generatedDesignOptionCatalog.transitions
9092
9347
  },
9093
9348
  headingDecoration: input.headingDecoration ?? noHeadingDecoration,
@@ -10168,6 +10423,7 @@ const curatedSiteStyles = [
10168
10423
  name: "Grounded Practice",
10169
10424
  themeOverrides: {
10170
10425
  accordions: editorialDividedAccordionSystem,
10426
+ cards: groundedPracticeCardSystem,
10171
10427
  testimonialQuote: serifDisplayQuote
10172
10428
  },
10173
10429
  description: "Quiet, grounded editorial design for practitioner-led services, recurring classes, and thoughtful one-to-one work.",
@@ -10222,7 +10478,8 @@ const curatedSiteStyles = [
10222
10478
  "feature-cards-soft-panels",
10223
10479
  "events-lined-panel",
10224
10480
  "testimonials-non-carded",
10225
- "faq-base-accordion"
10481
+ "faq-base-accordion",
10482
+ "booking-brand-band"
10226
10483
  ],
10227
10484
  recommendedFooterLookIds: ["footer-links-quiet"],
10228
10485
  buttonStyleChoices: [
@@ -10265,6 +10522,7 @@ const CORNER_MAP = {
10265
10522
  * Creates scoped CSS classes for:
10266
10523
  * - .status-error, .status-success, .status-warning, .status-info, .status-muted (text colors)
10267
10524
  * - .alert, .alert-error, .alert-success, .alert-warning, .alert-info (styled containers)
10525
+ * - .feedback-note, .feedback-note-* (the "field-note" feedback treatment)
10268
10526
  * - .required-marker (required field indicator)
10269
10527
  * - .button-link (text-styled button)
10270
10528
  *
@@ -10311,6 +10569,47 @@ ${root} .alert-info {
10311
10569
  background-color: rgba(var(--tb-info), 0.1);
10312
10570
  color: rgb(var(--tb-info));
10313
10571
  }`);
10572
+ cssBlocks.push(`/* Field-note feedback treatment */
10573
+ ${root} .feedback-note {
10574
+ --tb-feedback-note-rule: currentColor;
10575
+ display: grid;
10576
+ grid-template-columns: auto minmax(0, 1fr);
10577
+ column-gap: 0.6875rem;
10578
+ margin-top: 0.875rem;
10579
+ font-size: 0.875rem;
10580
+ /* Deliberately looser than .rb-caption's 1.25rem: validation notes wrap. */
10581
+ line-height: 1.5;
10582
+ color: ${surfaceForegroundValue("text")};
10583
+ }
10584
+
10585
+ ${root} .feedback-note::before {
10586
+ content: '';
10587
+ grid-column: 1;
10588
+ grid-row: 1;
10589
+ align-self: start;
10590
+ width: 1.375rem;
10591
+ height: 2px;
10592
+ margin-top: 0.62em;
10593
+ background-color: var(--tb-feedback-note-rule);
10594
+ }
10595
+
10596
+ ${root} .feedback-note > * {
10597
+ grid-column: 2;
10598
+ margin: 0;
10599
+ }
10600
+
10601
+ ${root} .feedback-note p {
10602
+ margin: 0;
10603
+ }
10604
+
10605
+ ${root} .feedback-note p + p {
10606
+ margin-top: 0.25rem;
10607
+ }
10608
+
10609
+ ${root} .feedback-note[data-feedback-tone="error"] { --tb-feedback-note-rule: rgb(var(--tb-accent)); }
10610
+ ${root} .feedback-note[data-feedback-tone="warning"] { --tb-feedback-note-rule: rgb(var(--tb-warning)); }
10611
+ ${root} .feedback-note[data-feedback-tone="success"] { --tb-feedback-note-rule: rgb(var(--tb-success)); }
10612
+ ${root} .feedback-note[data-feedback-tone="info"] { --tb-feedback-note-rule: rgb(var(--tb-info)); }`);
10314
10613
  cssBlocks.push(`/* Required field indicator */
10315
10614
  ${root} .required-marker {
10316
10615
  color: rgb(var(--tb-destructive));
@@ -14122,7 +14421,7 @@ const containerResponsiveThemeCss = `/*
14122
14421
  overflow-anchor: none;
14123
14422
  }
14124
14423
 
14125
- /* Base padding for horizontal variants (classic, transparent) */
14424
+ /* Base padding for horizontal variants (classic) */
14126
14425
  :where(.theme-scope) .rb-header-layout {
14127
14426
  padding-top: 1rem;
14128
14427
  padding-bottom: 1rem;
@@ -14683,6 +14982,127 @@ const containerResponsiveThemeCss = `/*
14683
14982
  margin-inline: 0;
14684
14983
  }
14685
14984
 
14985
+ /* The panel paints its own opaque surface, so it also has to break the button
14986
+ * colour contract it inherits. An enclosing section that sits on a brand band
14987
+ * publishes \`--rb-action-*\` variables resolved against *that* band — on Anna's
14988
+ * therapy page a ghost primary: transparent background, white label. Those
14989
+ * variables inherit, so without this reset a Continue button inside the light
14990
+ * form panel renders white-on-white and only appears on hover.
14991
+ *
14992
+ * Resetting to \`initial\` sends each button back to the generated fallback in
14993
+ * its own rule (\`var(--rb-action-…, rgb(var(--tb-primary)))\`), which is the
14994
+ * theme's standard button resolved against a normal surface — correct here,
14995
+ * because this panel *is* a normal surface.
14996
+ *
14997
+ * Mirrors \`contextualButtonSurfaceResetStyles()\` in theme-core/buttons; the
14998
+ * pairing is pinned by registration-surface-action-reset.test.ts. */
14999
+ :where(.theme-scope) :where([data-block="event-registration"], [data-block="course-registration"], [data-block="appointment-booking"]) .rb-reg-surface {
15000
+ --rb-action-primary-base-background-color: initial;
15001
+ --rb-action-primary-base-background-image: initial;
15002
+ --rb-action-primary-base-color: initial;
15003
+ --rb-action-primary-hover-background-color: initial;
15004
+ --rb-action-primary-hover-background-image: initial;
15005
+ --rb-action-primary-hover-color: initial;
15006
+ --rb-action-primary-focus-background-color: initial;
15007
+ --rb-action-primary-focus-background-image: initial;
15008
+ --rb-action-primary-focus-color: initial;
15009
+ --rb-action-primary-active-background-color: initial;
15010
+ --rb-action-primary-active-background-image: initial;
15011
+ --rb-action-primary-active-color: initial;
15012
+ --rb-action-primary-disabled-background-color: initial;
15013
+ --rb-action-primary-disabled-background-image: initial;
15014
+ --rb-action-primary-disabled-color: initial;
15015
+ --rb-action-primary-base-outline: initial;
15016
+ --rb-action-primary-base-outline-offset: initial;
15017
+ --rb-action-primary-focus-outline: initial;
15018
+ --rb-action-primary-focus-outline-offset: initial;
15019
+ --rb-action-primary-focus-box-shadow: initial;
15020
+
15021
+ --rb-action-secondary-base-background-color: initial;
15022
+ --rb-action-secondary-base-background-image: initial;
15023
+ --rb-action-secondary-base-color: initial;
15024
+ --rb-action-secondary-hover-background-color: initial;
15025
+ --rb-action-secondary-hover-background-image: initial;
15026
+ --rb-action-secondary-hover-color: initial;
15027
+ --rb-action-secondary-focus-background-color: initial;
15028
+ --rb-action-secondary-focus-background-image: initial;
15029
+ --rb-action-secondary-focus-color: initial;
15030
+ --rb-action-secondary-active-background-color: initial;
15031
+ --rb-action-secondary-active-background-image: initial;
15032
+ --rb-action-secondary-active-color: initial;
15033
+ --rb-action-secondary-disabled-background-color: initial;
15034
+ --rb-action-secondary-disabled-background-image: initial;
15035
+ --rb-action-secondary-disabled-color: initial;
15036
+ --rb-action-secondary-base-outline: initial;
15037
+ --rb-action-secondary-base-outline-offset: initial;
15038
+ --rb-action-secondary-focus-outline: initial;
15039
+ --rb-action-secondary-focus-outline-offset: initial;
15040
+ --rb-action-secondary-focus-box-shadow: initial;
15041
+
15042
+ --rb-action-outline-base-background-color: initial;
15043
+ --rb-action-outline-base-background-image: initial;
15044
+ --rb-action-outline-base-color: initial;
15045
+ --rb-action-outline-hover-background-color: initial;
15046
+ --rb-action-outline-hover-background-image: initial;
15047
+ --rb-action-outline-hover-color: initial;
15048
+ --rb-action-outline-focus-background-color: initial;
15049
+ --rb-action-outline-focus-background-image: initial;
15050
+ --rb-action-outline-focus-color: initial;
15051
+ --rb-action-outline-active-background-color: initial;
15052
+ --rb-action-outline-active-background-image: initial;
15053
+ --rb-action-outline-active-color: initial;
15054
+ --rb-action-outline-disabled-background-color: initial;
15055
+ --rb-action-outline-disabled-background-image: initial;
15056
+ --rb-action-outline-disabled-color: initial;
15057
+ --rb-action-outline-base-outline: initial;
15058
+ --rb-action-outline-base-outline-offset: initial;
15059
+ --rb-action-outline-focus-outline: initial;
15060
+ --rb-action-outline-focus-outline-offset: initial;
15061
+ --rb-action-outline-focus-box-shadow: initial;
15062
+
15063
+ --rb-action-ghost-base-background-color: initial;
15064
+ --rb-action-ghost-base-background-image: initial;
15065
+ --rb-action-ghost-base-color: initial;
15066
+ --rb-action-ghost-hover-background-color: initial;
15067
+ --rb-action-ghost-hover-background-image: initial;
15068
+ --rb-action-ghost-hover-color: initial;
15069
+ --rb-action-ghost-focus-background-color: initial;
15070
+ --rb-action-ghost-focus-background-image: initial;
15071
+ --rb-action-ghost-focus-color: initial;
15072
+ --rb-action-ghost-active-background-color: initial;
15073
+ --rb-action-ghost-active-background-image: initial;
15074
+ --rb-action-ghost-active-color: initial;
15075
+ --rb-action-ghost-disabled-background-color: initial;
15076
+ --rb-action-ghost-disabled-background-image: initial;
15077
+ --rb-action-ghost-disabled-color: initial;
15078
+ --rb-action-ghost-base-outline: initial;
15079
+ --rb-action-ghost-base-outline-offset: initial;
15080
+ --rb-action-ghost-focus-outline: initial;
15081
+ --rb-action-ghost-focus-outline-offset: initial;
15082
+ --rb-action-ghost-focus-box-shadow: initial;
15083
+
15084
+ --rb-action-accent-base-background-color: initial;
15085
+ --rb-action-accent-base-background-image: initial;
15086
+ --rb-action-accent-base-color: initial;
15087
+ --rb-action-accent-hover-background-color: initial;
15088
+ --rb-action-accent-hover-background-image: initial;
15089
+ --rb-action-accent-hover-color: initial;
15090
+ --rb-action-accent-focus-background-color: initial;
15091
+ --rb-action-accent-focus-background-image: initial;
15092
+ --rb-action-accent-focus-color: initial;
15093
+ --rb-action-accent-active-background-color: initial;
15094
+ --rb-action-accent-active-background-image: initial;
15095
+ --rb-action-accent-active-color: initial;
15096
+ --rb-action-accent-disabled-background-color: initial;
15097
+ --rb-action-accent-disabled-background-image: initial;
15098
+ --rb-action-accent-disabled-color: initial;
15099
+ --rb-action-accent-base-outline: initial;
15100
+ --rb-action-accent-base-outline-offset: initial;
15101
+ --rb-action-accent-focus-outline: initial;
15102
+ --rb-action-accent-focus-outline-offset: initial;
15103
+ --rb-action-accent-focus-box-shadow: initial;
15104
+ }
15105
+
14686
15106
  /* Selectable option card */
14687
15107
  :where(.theme-scope) :where([data-block="event-registration"], [data-block="course-registration"], [data-block="appointment-booking"]) .rb-option-card {
14688
15108
  position: relative;
@@ -20348,13 +20768,6 @@ const containerResponsiveThemeCss = `/*
20348
20768
  border-radius: var(--rb-block-radius);
20349
20769
  }
20350
20770
 
20351
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonial-card.rb-card-treatment-accent-quote {
20352
- border: 0;
20353
- border-left: 5px solid rgb(var(--tb-secondary));
20354
- border-radius: 0 calc(var(--rb-block-radius) * 3) 0 0;
20355
- color: rgb(var(--tb-primaryForeground));
20356
- }
20357
-
20358
20771
  :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonial-quote {
20359
20772
  display: var(--rb-testimonial-quote-display, block);
20360
20773
  font-family: var(--rb-testimonial-quote-font, inherit);
@@ -20365,54 +20778,6 @@ const containerResponsiveThemeCss = `/*
20365
20778
  color: var(--rb-testimonial-quote-color, rgb(var(--tb-primary)));
20366
20779
  }
20367
20780
 
20368
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-card-treatment-accent-quote .rb-testimonial-quote {
20369
- color: rgb(var(--tb-secondary));
20370
- font-size: calc(var(--rb-testimonial-quote-size) * 1.25);
20371
- opacity: 1;
20372
- }
20373
-
20374
- /* Open editorial testimonial card: boxless, with a hairline rule before the
20375
- * attribution, an accent ring on the avatar, and comfortable prose. The quote
20376
- * mark itself is owned by the site-style quote treatment (see the
20377
- * --rb-testimonial-quote-* vars), not by these rules.
20378
- *
20379
- * \`editorial-open\` is the current non-carded look; \`editorial-serif-quote\` is
20380
- * retained for content saved before the decomposition. */
20381
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonial-card.rb-card-treatment-editorial-open,
20382
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonial-card.rb-card-treatment-editorial-serif-quote {
20383
- background: transparent;
20384
- border: 0;
20385
- border-radius: 0;
20386
- padding: 0.25rem clamp(0.5rem, 1.5vw, 1.5rem);
20387
- }
20388
-
20389
- /* styledCard always renders an inner white rb-bg-surface fill layer; a boxless
20390
- * card must clear it so the section band shows through (otherwise: white box). */
20391
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-card-treatment-editorial-open > .rb-bg-surface,
20392
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-card-treatment-editorial-serif-quote > .rb-bg-surface {
20393
- background: transparent;
20394
- }
20395
-
20396
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-card-treatment-editorial-open .rb-prose,
20397
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-card-treatment-editorial-serif-quote .rb-prose {
20398
- font-size: 1.0625rem;
20399
- line-height: 1.7;
20400
- }
20401
-
20402
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-card-treatment-editorial-open .rb-testimonial-attribution,
20403
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-card-treatment-editorial-serif-quote .rb-testimonial-attribution {
20404
- margin-top: 1.25rem;
20405
- padding-top: 1.25rem;
20406
- border-top: 1px solid rgba(var(--tb-border), 0.75);
20407
- }
20408
-
20409
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-card-treatment-editorial-open .rb-testimonial-avatar,
20410
- :where(.theme-scope) :where([data-block="testimonials"]) .rb-card-treatment-editorial-serif-quote .rb-testimonial-avatar {
20411
- box-shadow:
20412
- 0 0 0 2px rgb(var(--tb-surface)),
20413
- 0 0 0 4px rgba(var(--tb-primary), 0.3);
20414
- }
20415
-
20416
20781
 
20417
20782
  /* Split grid: title column + cards. On desktop the title takes one quarter and
20418
20783
  * the cards fill the remaining three quarters (so title + 3 cards each read as
@@ -22871,287 +23236,6 @@ const containerResponsiveThemeCss = `/*
22871
23236
  display: none;
22872
23237
  }
22873
23238
 
22874
- /* =============================================================================
22875
- * Shared: Card treatments
22876
- *
22877
- * Visual treatments referenced from \`_cardStyles.treatment\` (see
22878
- * cardStylesOverrideSchema). Class names are emitted by the boxStyles
22879
- * transform in @riverbankcms/blocks.
22880
- *
22881
- * Also contains feature-card media/padding behaviour shared by all looks:
22882
- * - card images bleed to the card edges (flush photo tops)
22883
- * - card-level padding replaces the content stack's fallback padding so the
22884
- * two never stack up
22885
- * ============================================================================= */
22886
-
22887
- /* --- Feature card media + padding behaviour (all looks) -------------------- */
22888
-
22889
- /*
22890
- * When a look gives the card real padding (rb-container-pad-*), the image
22891
- * bleeds to the card edges and the inner content stack drops its fallback
22892
- * padding so insets don't double up.
22893
- */
22894
- :where(.theme-scope) .rb-feature-card[class*="rb-container-pad-"]:not(.rb-container-pad-none) > .rb-feature-card-media-frame {
22895
- width: calc(100% + 2 * var(--rb-card-pad, 0rem));
22896
- max-width: none;
22897
- margin: calc(-1 * var(--rb-card-pad, 0rem)) calc(-1 * var(--rb-card-pad, 0rem)) 0;
22898
- }
22899
-
22900
- :where(.theme-scope) .rb-feature-card[class*="rb-container-pad-"]:not(.rb-container-pad-none) > .rb-feature-card-media-frame + .rb-feature-card-content {
22901
- margin-top: 1.4rem;
22902
- }
22903
-
22904
- :where(.theme-scope) .rb-feature-card[class*="rb-container-pad-"]:not(.rb-container-pad-none) > .rb-feature-card-content {
22905
- padding: 0;
22906
- }
22907
-
22908
- /* --- Soft panel ------------------------------------------------------------ */
22909
-
22910
- :where(.theme-scope) .rb-card-treatment-soft-panel {
22911
- transition: transform 250ms ease, box-shadow 250ms ease;
22912
- }
22913
-
22914
- @media (prefers-reduced-motion: no-preference) {
22915
- :where(.theme-scope) .rb-card-treatment-soft-panel:hover {
22916
- transform: translateY(-3px);
22917
- box-shadow: 0 0.875rem 1.875rem rgba(var(--tb-shadow, 0 0 0), 0.1);
22918
- }
22919
- }
22920
-
22921
- :where(.theme-scope) .rb-card-treatment-soft-panel .cta-row .button-ghost {
22922
- padding-inline: 0;
22923
- }
22924
-
22925
- /* --- Editorial rule --------------------------------------------------------- */
22926
-
22927
- /*
22928
- * Boxless open column: no background, no border box, a short brand rule
22929
- * sitting on a full-width hairline at the top of the column.
22930
- */
22931
- :where(.theme-scope) .rb-card-treatment-editorial-rule {
22932
- margin-top: 1rem;
22933
- border-top: 1px solid
22934
- color-mix(in oklab, rgb(var(--tb-border, 229 231 235)), transparent 25%);
22935
- }
22936
-
22937
- :where(.theme-scope) .rb-card-treatment-editorial-rule > .rb-bg-surface {
22938
- background: transparent;
22939
- }
22940
-
22941
- :where(.theme-scope) .rb-card-treatment-editorial-rule::before {
22942
- content: "";
22943
- position: absolute;
22944
- top: 0;
22945
- left: 0;
22946
- width: 2.75rem;
22947
- height: 2px;
22948
- background: rgb(var(--tb-primary, 0 0 0));
22949
- }
22950
-
22951
- :where(.theme-scope) .rb-card-treatment-editorial-rule > .rb-feature-card-media-frame {
22952
- margin-top: 1.6rem;
22953
- border-radius: calc(var(--rb-block-radius, 0.75rem) * 0.5);
22954
- }
22955
-
22956
- :where(.theme-scope) .rb-feature-card > .rb-bg-surface,
22957
- :where(.theme-scope) .rb-feature-card > .rb-feature-card-media-frame {
22958
- border-start-start-radius: inherit;
22959
- border-start-end-radius: inherit;
22960
- }
22961
-
22962
- /* --- Editorial diptych + pathways ----------------------------------------- */
22963
-
22964
- :where(.theme-scope) .rb-feature-cards-heading--editorial {
22965
- margin-inline: 0;
22966
- text-align: left;
22967
- }
22968
-
22969
- :where(.theme-scope) .rb-feature-cards-eyebrow {
22970
- margin: 0;
22971
- color: rgb(var(--tb-accent));
22972
- font-size: 0.72rem;
22973
- font-weight: 600;
22974
- letter-spacing: 0.14em;
22975
- text-transform: uppercase;
22976
- }
22977
-
22978
- :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych {
22979
- grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
22980
- align-items: stretch;
22981
- }
22982
-
22983
- :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways {
22984
- grid-template-columns: repeat(2, minmax(0, 1fr));
22985
- align-items: start;
22986
- }
22987
-
22988
- :where(.theme-scope) .rb-feature-cards-grid:is(
22989
- .rb-feature-cards-grid--editorial-diptych,
22990
- .rb-feature-cards-grid--editorial-pathways
22991
- ) .rb-feature-card {
22992
- border: 0;
22993
- box-shadow: none;
22994
- height: auto;
22995
- min-width: 0;
22996
- }
22997
-
22998
- :where(.theme-scope) .rb-feature-cards-grid:is(
22999
- .rb-feature-cards-grid--editorial-diptych,
23000
- .rb-feature-cards-grid--editorial-pathways
23001
- ) .rb-feature-card[data-surface-role="open"] > .rb-bg-surface {
23002
- background: transparent;
23003
- }
23004
-
23005
- :where(.theme-scope) .rb-feature-cards-grid:is(
23006
- .rb-feature-cards-grid--editorial-diptych,
23007
- .rb-feature-cards-grid--editorial-pathways
23008
- ) .rb-feature-card[data-surface-role="open"] > .rb-feature-card-content {
23009
- padding-inline: 0;
23010
- }
23011
-
23012
- :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways .rb-feature-card > .rb-bg-surface {
23013
- background: transparent;
23014
- }
23015
-
23016
- :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways .rb-feature-card > .rb-feature-card-content {
23017
- padding-inline: 0;
23018
- }
23019
-
23020
- :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] {
23021
- color: rgb(var(--tb-onPrimary));
23022
- --rb-surface-text-color: rgb(var(--tb-onPrimary));
23023
- --rb-surface-heading-color: rgb(var(--tb-onPrimary));
23024
- --rb-surface-link-color: rgb(var(--tb-onPrimary));
23025
- --rb-surface-supporting-color: color-mix(in oklab, rgb(var(--tb-onPrimary)), transparent 18%);
23026
- }
23027
-
23028
- :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] > .rb-bg-surface {
23029
- background: rgb(var(--tb-primary));
23030
- border-radius: inherit;
23031
- }
23032
-
23033
- :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] > .rb-feature-card-media-frame {
23034
- width: calc(100% - 2.5rem);
23035
- margin: 1.25rem 1.25rem 0;
23036
- border-radius: calc(var(--rb-block-radius, 0.75rem) * 0.55);
23037
- }
23038
-
23039
- :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways .rb-feature-card-content {
23040
- padding-top: 1.35rem;
23041
- }
23042
-
23043
- @container rb-site (min-width: 768px) {
23044
- @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
23045
- :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] {
23046
- margin-top: clamp(2rem, 5vw, 4.5rem);
23047
- }
23048
-
23049
- :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways .rb-feature-card + .rb-feature-card {
23050
- border-left: 1px solid color-mix(in oklab, currentColor, transparent 82%);
23051
- padding-left: clamp(1.5rem, 4vw, 4rem);
23052
- }
23053
- }
23054
- }
23055
-
23056
- @container rb-site (max-width: 767px) {
23057
- @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
23058
- :where(.theme-scope) .rb-feature-cards-grid:is(
23059
- .rb-feature-cards-grid--editorial-diptych,
23060
- .rb-feature-cards-grid--editorial-pathways
23061
- ) {
23062
- grid-template-columns: minmax(0, 1fr);
23063
- }
23064
- }
23065
- }
23066
-
23067
- :where(.theme-scope) .rb-card-treatment-editorial-rule > .rb-feature-card-content {
23068
- padding: 1.6rem 0 0.25rem;
23069
- }
23070
-
23071
- :where(.theme-scope) .rb-card-treatment-editorial-rule .cta-row .button-ghost {
23072
- padding-inline: 0;
23073
- }
23074
-
23075
- /* --- Colour-blocked --------------------------------------------------------- */
23076
-
23077
- /*
23078
- * Bold, flat cards that each take a different palette colour by position,
23079
- * cycled across the grid, with a staggered vertical offset on wider screens.
23080
- * The background layer (\`.rb-bg-surface\`) is recoloured per card; the card's
23081
- * text colour is set to the matching on-colour so content stays readable.
23082
- */
23083
- :where(.theme-scope) .rb-card-treatment-color-blocked {
23084
- box-shadow: none;
23085
- }
23086
-
23087
- /* Per-card colour cycle. The \`on*\` colours are always derived by the palette
23088
- * (expandPalette emits onSurface/onSecondary/onPrimary), so no fallback. */
23089
- :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 1) > .rb-bg-surface {
23090
- background: rgb(var(--tb-surface));
23091
- }
23092
- :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 1) {
23093
- background: rgb(var(--tb-surface));
23094
- color: rgb(var(--tb-onSurface));
23095
- --rb-surface-text-color: rgb(var(--tb-onSurface));
23096
- --rb-surface-heading-color: rgb(var(--tb-onSurface));
23097
- --rb-surface-link-color: rgb(var(--tb-onSurface));
23098
- --rb-surface-supporting-color: rgb(var(--tb-onSurface));
23099
- }
23100
-
23101
- :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 2) > .rb-bg-surface {
23102
- background: rgb(var(--tb-secondary));
23103
- }
23104
- :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 2) {
23105
- background: rgb(var(--tb-secondary));
23106
- color: rgb(var(--tb-onSecondary));
23107
- --rb-surface-text-color: rgb(var(--tb-onSecondary));
23108
- --rb-surface-heading-color: rgb(var(--tb-onSecondary));
23109
- --rb-surface-link-color: rgb(var(--tb-onSecondary));
23110
- --rb-surface-supporting-color: rgb(var(--tb-onSecondary));
23111
- }
23112
-
23113
- :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 3) > .rb-bg-surface {
23114
- background: rgb(var(--tb-primary));
23115
- }
23116
- :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 3) {
23117
- background: rgb(var(--tb-primary));
23118
- color: rgb(var(--tb-onPrimary));
23119
- --rb-surface-text-color: rgb(var(--tb-onPrimary));
23120
- --rb-surface-heading-color: rgb(var(--tb-onPrimary));
23121
- --rb-surface-link-color: rgb(var(--tb-onPrimary));
23122
- --rb-surface-supporting-color: rgb(var(--tb-onPrimary));
23123
- }
23124
-
23125
- /* Keep the intended visual hierarchy; the body color itself now resolves via
23126
- * the nearest surface provider instead of requiring an inline override. */
23127
- :where(.theme-scope) .rb-card-treatment-color-blocked .rb-feature-card-body {
23128
- opacity: 0.9;
23129
- }
23130
-
23131
- /* The number marker defaults to the brand accent, which collides with the
23132
- * primary-coloured card in the cycle. On colour-blocked cards use the card's
23133
- * own on-colour (the palette guarantees it's legible on that background) and a
23134
- * faint tint of it for the badge, so the number always contrasts. */
23135
- :where(.theme-scope) .rb-card-treatment-color-blocked .rb-feature-card-number-zone {
23136
- color: inherit;
23137
- background-color: color-mix(in oklab, currentColor, transparent 88%);
23138
- }
23139
-
23140
- /* Staggered offset: drop the middle column on wider screens. Scoped to a 3-up
23141
- * grid (\`.rb-grid-cols-3\`) so 2-/4-column grids don't get a misaligned stagger;
23142
- * the colour cycle still reads as a diagonal at any column count. */
23143
- @container rb-site (min-width: 768px) {
23144
- @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
23145
- :where(.theme-scope) .rb-grid-cols-3 .rb-card-treatment-color-blocked:nth-child(3n + 2) {
23146
- margin-top: 2.75rem;
23147
- /* Override the card's \`h-full\` so the grid's stretch shrinks the card by the
23148
- * margin (a genuinely shorter card) instead of keeping full height and
23149
- * overflowing the section, which clips the rounded bottom corners. */
23150
- height: auto;
23151
- }
23152
- }
23153
- }
23154
-
23155
23239
  :where(.theme-scope) [data-block="intro"][data-block-composition="portrait-bio"][data-block-variant="media-copy"] .rb-intro-media-wrap {
23156
23240
  max-width: min(100%, 24rem);
23157
23241
  margin-inline: auto;
@@ -23661,8 +23745,9 @@ const containerResponsiveThemeCss = `/*
23661
23745
  border-radius: var(--rb-block-radius, 0.75rem);
23662
23746
  }
23663
23747
 
23664
- /* Framed programme: a close-set family of inset cards on a semantic depth
23665
- * section. The itemSurface recipe supplies each complete surface provider. */
23748
+ /* Framed programme: a close-set family of cards on a semantic depth section.
23749
+ * The Look asks for the \`alternative\` card role; the site style's card system
23750
+ * supplies the surface. */
23666
23751
  :where(.theme-scope)
23667
23752
  [data-block="feature-cards"][data-block-composition="framed-programme"]
23668
23753
  .rb-feature-cards-grid {
@@ -23695,6 +23780,135 @@ const containerResponsiveThemeCss = `/*
23695
23780
  }
23696
23781
  }
23697
23782
 
23783
+ /* --- Feature card media + padding behaviour (all looks) -------------------- */
23784
+
23785
+ :where(.theme-scope) .rb-feature-card[class*="rb-container-pad-"]:not(.rb-container-pad-none) > .rb-feature-card-media-frame {
23786
+ width: calc(100% + 2 * var(--rb-card-pad, 0rem));
23787
+ max-width: none;
23788
+ margin: calc(-1 * var(--rb-card-pad, 0rem)) calc(-1 * var(--rb-card-pad, 0rem)) 0;
23789
+ }
23790
+
23791
+ :where(.theme-scope) .rb-feature-card[class*="rb-container-pad-"]:not(.rb-container-pad-none) > .rb-feature-card-media-frame + .rb-feature-card-content {
23792
+ margin-top: 1.4rem;
23793
+ }
23794
+
23795
+ :where(.theme-scope) .rb-feature-card[class*="rb-container-pad-"]:not(.rb-container-pad-none) > .rb-feature-card-content {
23796
+ padding: 0;
23797
+ }
23798
+
23799
+ :where(.theme-scope) .rb-feature-card > .rb-bg-surface,
23800
+ :where(.theme-scope) .rb-feature-card > .rb-feature-card-media-frame {
23801
+ border-start-start-radius: inherit;
23802
+ border-start-end-radius: inherit;
23803
+ }
23804
+
23805
+ /* --- Editorial diptych + pathways ----------------------------------------- */
23806
+
23807
+ :where(.theme-scope) .rb-feature-cards-heading--editorial {
23808
+ margin-inline: 0;
23809
+ text-align: left;
23810
+ }
23811
+
23812
+ :where(.theme-scope) .rb-feature-cards-eyebrow {
23813
+ margin: 0;
23814
+ color: rgb(var(--tb-accent));
23815
+ font-size: 0.72rem;
23816
+ font-weight: 600;
23817
+ letter-spacing: 0.14em;
23818
+ text-transform: uppercase;
23819
+ }
23820
+
23821
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych {
23822
+ grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
23823
+ align-items: stretch;
23824
+ }
23825
+
23826
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways {
23827
+ grid-template-columns: repeat(2, minmax(0, 1fr));
23828
+ align-items: start;
23829
+ }
23830
+
23831
+ :where(.theme-scope) .rb-feature-cards-grid:is(
23832
+ .rb-feature-cards-grid--editorial-diptych,
23833
+ .rb-feature-cards-grid--editorial-pathways
23834
+ ) .rb-feature-card {
23835
+ border: 0;
23836
+ box-shadow: none;
23837
+ height: auto;
23838
+ min-width: 0;
23839
+ }
23840
+
23841
+ :where(.theme-scope) .rb-feature-cards-grid:is(
23842
+ .rb-feature-cards-grid--editorial-diptych,
23843
+ .rb-feature-cards-grid--editorial-pathways
23844
+ ) .rb-feature-card[data-surface-role="open"] > .rb-bg-surface {
23845
+ background: transparent;
23846
+ }
23847
+
23848
+ :where(.theme-scope) .rb-feature-cards-grid:is(
23849
+ .rb-feature-cards-grid--editorial-diptych,
23850
+ .rb-feature-cards-grid--editorial-pathways
23851
+ ) .rb-feature-card[data-surface-role="open"] > .rb-feature-card-content {
23852
+ padding-inline: 0;
23853
+ }
23854
+
23855
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways .rb-feature-card > .rb-bg-surface {
23856
+ background: transparent;
23857
+ }
23858
+
23859
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways .rb-feature-card > .rb-feature-card-content {
23860
+ padding-inline: 0;
23861
+ }
23862
+
23863
+ /* Diptych filled panel: a composition over the \`alternative\` role's surface.
23864
+ The 18% supporting alpha is accepted CSS-side — see issue 1297 mapping-table. */
23865
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] {
23866
+ color: rgb(var(--tb-onPrimary));
23867
+ --rb-surface-text-color: rgb(var(--tb-onPrimary));
23868
+ --rb-surface-heading-color: rgb(var(--tb-onPrimary));
23869
+ --rb-surface-link-color: rgb(var(--tb-onPrimary));
23870
+ --rb-surface-supporting-color: color-mix(in oklab, rgb(var(--tb-onPrimary)), transparent 18%);
23871
+ }
23872
+
23873
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] > .rb-bg-surface {
23874
+ background: rgb(var(--tb-primary));
23875
+ border-radius: inherit;
23876
+ }
23877
+
23878
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] > .rb-feature-card-media-frame {
23879
+ width: calc(100% - 2.5rem);
23880
+ margin: 1.25rem 1.25rem 0;
23881
+ border-radius: calc(var(--rb-block-radius, 0.75rem) * 0.55);
23882
+ }
23883
+
23884
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways .rb-feature-card-content {
23885
+ padding-top: 1.35rem;
23886
+ }
23887
+
23888
+ @container rb-site (min-width: 768px) {
23889
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
23890
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] {
23891
+ margin-top: clamp(2rem, 5vw, 4.5rem);
23892
+ }
23893
+
23894
+ :where(.theme-scope) .rb-feature-cards-grid--editorial-pathways .rb-feature-card + .rb-feature-card {
23895
+ border-left: 1px solid color-mix(in oklab, currentColor, transparent 82%);
23896
+ padding-left: clamp(1.5rem, 4vw, 4rem);
23897
+ }
23898
+ }
23899
+ }
23900
+
23901
+ @container rb-site (max-width: 767px) {
23902
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
23903
+ :where(.theme-scope) .rb-feature-cards-grid:is(
23904
+ .rb-feature-cards-grid--editorial-diptych,
23905
+ .rb-feature-cards-grid--editorial-pathways
23906
+ ) {
23907
+ grid-template-columns: minmax(0, 1fr);
23908
+ }
23909
+ }
23910
+ }
23911
+
23698
23912
  /* =============================================================================
23699
23913
  * Offerings Preview composition Looks promoted from the Fran André treatment
23700
23914
  * study. The outer section surface remains independently configurable. These
@@ -24086,6 +24300,51 @@ const containerResponsiveThemeCss = `/*
24086
24300
  .rb-testimonials-fe-col {
24087
24301
  grid-template-columns: repeat(2, minmax(0, 1fr));
24088
24302
  }
24303
+ }
24304
+
24305
+ /* Open-editorial testimonials composition, selected by a Look
24306
+ * (\`composition: { kind: 'testimonials', treatment: 'open-editorial' }\`).
24307
+ *
24308
+ * Two jobs. First, *boxless*: \`styledCard\` always renders an opaque
24309
+ * \`.rb-bg-surface\` fill layer, so a testimonial with no card role has to clear
24310
+ * it or it paints a white box on the section band (the same shape
24311
+ * feature-card-compositions.css uses for \`[data-surface-role="open"]\`).
24312
+ * Second, the composition proper: a hairline before the attribution, an accent
24313
+ * ring on the avatar, comfortable prose.
24314
+ *
24315
+ * What is *not* here: whether the testimonial sits in a card (the Look's
24316
+ * \`cardRole\`), how that card looks (the site style's card system), or the quote
24317
+ * mark (the site style's quote treatment, via the --rb-testimonial-quote-* vars
24318
+ * in testimonials.css).
24319
+ *
24320
+ * The block attribute is deliberately outside \`:where()\` so these rules outrank
24321
+ * the card's own utility classes, matching the Look-keyed rules above. */
24322
+ :where(.theme-scope) [data-block="testimonials"][data-block-composition="open-editorial"] .rb-testimonial-card {
24323
+ background: transparent;
24324
+ border: 0;
24325
+ border-radius: 0;
24326
+ padding: 0.25rem clamp(0.5rem, 1.5vw, 1.5rem);
24327
+ }
24328
+
24329
+ :where(.theme-scope) [data-block="testimonials"][data-block-composition="open-editorial"] .rb-testimonial-card > .rb-bg-surface {
24330
+ background: transparent;
24331
+ }
24332
+
24333
+ :where(.theme-scope) [data-block="testimonials"][data-block-composition="open-editorial"] .rb-prose {
24334
+ font-size: 1.0625rem;
24335
+ line-height: 1.7;
24336
+ }
24337
+
24338
+ :where(.theme-scope) [data-block="testimonials"][data-block-composition="open-editorial"] .rb-testimonial-attribution {
24339
+ margin-top: 1.25rem;
24340
+ padding-top: 1.25rem;
24341
+ border-top: 1px solid rgba(var(--tb-border), 0.75);
24342
+ }
24343
+
24344
+ :where(.theme-scope) [data-block="testimonials"][data-block-composition="open-editorial"] .rb-testimonial-avatar {
24345
+ box-shadow:
24346
+ 0 0 0 2px rgb(var(--tb-surface)),
24347
+ 0 0 0 4px rgba(var(--tb-primary), 0.3);
24089
24348
  }`;
24090
24349
  //#endregion
24091
24350
  //#region ../theme-core/src/ThemeScopeCore.tsx
@@ -24580,6 +24839,8 @@ const themeBridgeConfigSchema = z.object({
24580
24839
  ])).optional() })]).optional(),
24581
24840
  cards: z.union([z.boolean(), z.object({ variants: z.array(z.enum([
24582
24841
  "default",
24842
+ "alternative",
24843
+ "open",
24583
24844
  "elevated",
24584
24845
  "outlined"
24585
24846
  ])).optional() })]).optional(),
@@ -24619,6 +24880,10 @@ const TOKEN_ALIASES = [
24619
24880
  {
24620
24881
  source: "destructive",
24621
24882
  target: "error"
24883
+ },
24884
+ {
24885
+ source: "accent",
24886
+ target: "info"
24622
24887
  }
24623
24888
  ];
24624
24889
  function hexToRgbChannels(hex) {
@@ -24647,7 +24912,7 @@ function buildThemeBridgeRuntime(config) {
24647
24912
  themeId,
24648
24913
  cssVars,
24649
24914
  buttonCss: shouldGenerateComponent(config.components?.buttons) ? generateButtonsCss(config, corners, shadows) : void 0,
24650
- cardCss: shouldGenerateComponent(config.components?.cards) ? generateCardsCss(config, corners, shadows) : void 0,
24915
+ cardCss: generateCardsCss(config, corners, shadows),
24651
24916
  inputCss: shouldGenerateComponent(config.components?.inputs) ? generateInputsCss(corners, shadows) : void 0,
24652
24917
  statusCss: generateStatusCss({
24653
24918
  themeId,
@@ -24928,18 +25193,32 @@ function generateButtonsCss(config, corners, shadows) {
24928
25193
  const tokens = resolveBridgeTokens(config.tokens);
24929
25194
  return generateButtonCoreCss("sdk", variants, settings, buildBridgeButtonPalette(tokens));
24930
25195
  }
25196
+ /**
25197
+ * Bridge card CSS, in two halves with different opt-in rules.
25198
+ *
25199
+ * The `.card-role-*` aliases always emit, from the full default variant set:
25200
+ * they are the card contract compiled content renders against, so a consumer
25201
+ * who never mentions `components.cards` still gets a working card surface.
25202
+ * The named `.card-{id}` classes (and the legacy ids beside them) are a
25203
+ * catalogue an SDK site addresses by hand, so they stay opt-in and keep
25204
+ * honouring a `variants` allow-list.
25205
+ */
24931
25206
  function generateCardsCss(config, corners, shadows) {
24932
- let variants = getDefaultCardVariants();
25207
+ const allVariants = getDefaultCardVariants();
24933
25208
  const cardConfig = config.components?.cards;
24934
- if (cardConfig !== void 0 && cardConfig !== true && cardConfig !== false && cardConfig.variants) {
24935
- const requestedIds = new Set(cardConfig.variants);
24936
- variants = variants.filter((variant) => requestedIds.has(variant.id));
24937
- }
24938
- return generateCardCoreCss("sdk", variants, {
25209
+ const settings = {
24939
25210
  corners,
24940
25211
  shadow: shadows,
24941
25212
  borderWidth: "thin"
24942
- });
25213
+ };
25214
+ const roleAliasCss = generateCardCoreCss("sdk", allVariants, settings, "role-aliases");
25215
+ if (cardConfig === void 0 || cardConfig === false) return roleAliasCss;
25216
+ let namedVariants = allVariants;
25217
+ if (cardConfig !== true && cardConfig.variants) {
25218
+ const requested = new Set(cardConfig.variants);
25219
+ namedVariants = allVariants.filter((variant) => requested.has(variant.id) || variant.role !== void 0 && requested.has(variant.role) || (variant.legacyIds ?? []).some((legacyId) => requested.has(legacyId)));
25220
+ }
25221
+ return [roleAliasCss, generateCardCoreCss("sdk", namedVariants, settings, "named")].filter(Boolean).join("\n\n");
24943
25222
  }
24944
25223
  function generateInputsCss(corners, shadows) {
24945
25224
  return generateInputCoreCss("sdk", {