@riverbankcms/sdk 0.149.0 → 0.150.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/EventEmbeddedCheckoutStep-D00Kc8Zj.mjs +285 -0
  2. package/dist/{PreviewEditorSidebar-DBKln68M.mjs → PreviewEditorSidebar-7ZhmmnBB.mjs} +3 -3
  3. package/dist/{PreviewEditorUI-DpRX4Mum.mjs → PreviewEditorUI-BIJucTRd.mjs} +2 -2
  4. package/dist/{SdkPreviewModeRuntime-DJPmPY-r.mjs → SdkPreviewModeRuntime-tj45gmCX.mjs} +4 -3
  5. package/dist/{SiteChromeCustomizeContext-JYA0cJ_Z.mjs → SiteChromeCustomizeContext-BFOt0ss-.mjs} +4454 -4059
  6. package/dist/_dts/ai/src/contracts/proposals.d.ts +660 -0
  7. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +510 -0
  8. package/dist/_dts/api/src/billing.d.ts +2 -2
  9. package/dist/_dts/api/src/contentRuntime.d.ts +5 -1
  10. package/dist/_dts/api/src/footerCompositionContracts.d.ts +9 -0
  11. package/dist/_dts/api/src/footerSettings.d.ts +3 -3
  12. package/dist/_dts/api/src/index.d.ts +1 -1
  13. package/dist/_dts/api/src/navigation/bookingPortalLink.d.ts +13 -0
  14. package/dist/_dts/api/src/navigation.d.ts +1 -0
  15. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +9 -173
  16. package/dist/_dts/api/src/sdkThemeWire.d.ts +12 -0
  17. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +3 -3
  18. package/dist/_dts/api/src/siteSettings.d.ts +2 -0
  19. package/dist/_dts/blocks/src/client/site-chrome.d.ts +1 -1
  20. package/dist/_dts/blocks/src/client.d.ts +2 -1
  21. package/dist/_dts/blocks/src/rendering/server.d.ts +1 -1
  22. package/dist/_dts/blocks/src/system/blocks/site-footer-runtime.d.ts +30 -3
  23. package/dist/_dts/blocks/src/system/designCapabilities.d.ts +10 -1
  24. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  25. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +38 -18
  26. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessEventRegistrationFlow.d.ts +1 -1
  27. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.server.d.ts +6 -0
  28. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventEmbeddedCheckoutStep.d.ts +13 -0
  29. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationPayment.d.ts +15 -0
  30. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationWizardActions.d.ts +0 -1
  31. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/FullyBookedStep.d.ts +6 -0
  32. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentBlockingPanel.d.ts +3 -0
  33. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentSelectionStep.d.ts +2 -0
  34. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +15 -5
  35. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +4 -2
  36. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/paymentLocation.d.ts +3 -0
  37. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/paymentResponse.d.ts +72 -0
  38. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationCompletion.d.ts +66 -5
  39. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +1 -4
  40. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationStateMachine.d.ts +13 -5
  41. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/sessionStorage.d.ts +11 -52
  42. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +19 -2
  43. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +7 -42
  44. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +7 -8
  45. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +90 -24
  46. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useGuestRegistrationRecovery.d.ts +30 -0
  47. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/usePaymentStatusRecovery.d.ts +24 -12
  48. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useRegistrationAdmission.d.ts +18 -0
  49. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/wizardSteps.d.ts +6 -1
  50. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +1 -1
  51. package/dist/_dts/blocks/src/system/runtime/shared/stripe/browserStripe.d.ts +8 -0
  52. package/dist/_dts/blocks/src/system/transforms/registry/eventCalendar.d.ts +8 -0
  53. package/dist/_dts/commerce/src/purchase/checkoutCopy.d.ts +10 -0
  54. package/dist/_dts/commerce/src/purchase/checkoutPresentation.d.ts +49 -0
  55. package/dist/_dts/commerce/src/purchase/checkoutSessionMetadata.d.ts +4 -4
  56. package/dist/_dts/commerce/src/purchase/checkoutStripeFinalizationPlanning.d.ts +5 -1
  57. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +9 -0
  58. package/dist/_dts/commerce/src/purchase/index.d.ts +2 -0
  59. package/dist/_dts/commerce/src/purchase/orderItemMetadata.d.ts +1 -0
  60. package/dist/_dts/commerce/src/purchase/stripeCheckoutReturnUrl.d.ts +9 -2
  61. package/dist/_dts/commerce/src/purchase/types.d.ts +5 -6
  62. package/dist/_dts/content-editor/src/FooterComposerOverview.d.ts +3 -2
  63. package/dist/_dts/content-editor/src/footerPreviewRuntime.d.ts +6 -1
  64. package/dist/_dts/content-editor/src/useFooterComposerState.d.ts +7 -0
  65. package/dist/_dts/core/src/index.d.ts +1 -0
  66. package/dist/_dts/core/src/patch/index.d.ts +30 -0
  67. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +30 -1
  68. package/dist/_dts/sdk/src/bookings/index.d.ts +2 -1
  69. package/dist/_dts/sdk/src/bookings/types.d.ts +1 -1
  70. package/dist/_dts/sdk/src/version.d.ts +1 -1
  71. package/dist/_dts/theme-core/src/blocks/index.d.ts +1 -0
  72. package/dist/_dts/theme-core/src/blocks/opaquePanels.d.ts +147 -0
  73. package/dist/_dts/theme-core/src/boxBackgroundSchema.d.ts +22 -0
  74. package/dist/_dts/theme-core/src/buttons/buttonSurfaceContext.d.ts +22 -0
  75. package/dist/_dts/theme-core/src/cards/roles.d.ts +12 -0
  76. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  77. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +198 -0
  78. package/dist/_dts/theme-core/src/palette/utils/index.d.ts +2 -0
  79. package/dist/_dts/theme-core/src/palette/utils/scrimContrast.d.ts +22 -0
  80. package/dist/_dts/theme-core/src/runtime/bridgeColourTokens.d.ts +10 -0
  81. package/dist/_dts/theme-core/src/schema.d.ts +330 -0
  82. package/dist/_dts/theme-core/src/site-styles/appearancePresetCatalog.d.ts +0 -51
  83. package/dist/_dts/theme-core/src/site-styles/blockAppearancePresets.d.ts +0 -51
  84. package/dist/_dts/theme-core/src/site-styles/lookResolution.d.ts +11 -0
  85. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorCapabilities.d.ts +7 -1
  86. package/dist/_dts/theme-core/src/site-styles/pageDesignEditorModel.d.ts +7 -1
  87. package/dist/_dts/theme-core/src/site-styles/previewRuntimeCapabilities.d.ts +12 -6
  88. package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +7 -4
  89. package/dist/_dts/theme-core/src/site-styles/sectionSurfaceBackgrounds.d.ts +65 -0
  90. package/dist/_dts/theme-core/src/site-styles/types.d.ts +17 -0
  91. package/dist/_dts/theme-core/src/surfaces/opaqueSurface.d.ts +43 -0
  92. package/dist/client/{BookingMailingListNote-Cr3EeTP5.mjs → BookingMailingListNote-CYHOufsI.mjs} +10 -440
  93. package/dist/client/{CheckboxChoice-B33MqoUo.mjs → CheckboxChoice-M-3HycLw.mjs} +1 -1
  94. package/dist/client/{DemoBookingPortalAccess-BFGB1Fay.mjs → DemoBookingPortalAccess-Cvr9O_ca.mjs} +2 -2
  95. package/dist/client/{EventCombined.client-DKYV0_ft.mjs → EventCombined.client-m5UZIobP.mjs} +7 -7
  96. package/dist/client/EventEmbeddedCheckoutStep-DL6zFAkA.mjs +289 -0
  97. package/dist/client/{EventListing.client-B9UIcEYc.mjs → EventListing.client-DeMfCM04.mjs} +2 -2
  98. package/dist/client/{LeafletStaticMap-CFLPkP0K.mjs → LeafletStaticMap-CvZ3gOdD.mjs} +1 -1
  99. package/dist/client/{checkoutUrls-CXhko2Rv.mjs → RichText-BtfNvQV6.mjs} +2 -29
  100. package/dist/client/{TerminalPanel-JjmgxEsR.mjs → TerminalPanel-CUCNEwlH.mjs} +2 -18
  101. package/dist/client/{ThemedButton-CKV1Jh2m.mjs → ThemedButton-C7WhXuRl.mjs} +2 -2
  102. package/dist/client/{accordion-DXWIfT3T.mjs → accordion-cQm5mjqs.mjs} +1 -1
  103. package/dist/client/{bookingForm-CjZgHNvv.mjs → bookingForm-BVPJE5RQ.mjs} +11 -10
  104. package/dist/client/bookings.mjs +2668 -1134
  105. package/dist/client/{carousel-DSLikmvZ.mjs → carousel-3vgnY7LC.mjs} +2 -2
  106. package/dist/client/{carousel.client-A2lG-9od.mjs → carousel.client-CISsdZvg.mjs} +1 -1
  107. package/dist/client/{cart-HDjWReFt.mjs → cart-BGK4oOjm.mjs} +3 -3
  108. package/dist/client/{checkout-CUvRZA3W.mjs → checkout-GsBLUNJD.mjs} +3 -3
  109. package/dist/client/checkoutUrls-ChxwaNtO.mjs +28 -0
  110. package/dist/client/client.mjs +3042 -1868
  111. package/dist/client/{colorStyles-j5U9v5JA.mjs → colorStyles-D-32xHZh.mjs} +2 -30
  112. package/dist/client/{context-DFmaZa-Q.mjs → context-D_8s12Fp.mjs} +1 -1
  113. package/dist/client/{dates-BBep1JnF.mjs → dates-DeJQbQ9T.mjs} +2 -2
  114. package/dist/client/{eventCalendar-zEobhxo4.mjs → eventCalendar-cmucD4NR.mjs} +10 -10
  115. package/dist/client/{eventCombined-Dp7bz5k-.mjs → eventCombined-ku3E9FTl.mjs} +4 -4
  116. package/dist/client/{eventDetails-FE-zqTt7.mjs → eventDetails-CyKZ61sF.mjs} +5 -5
  117. package/dist/client/{eventListing-BMt5snal.mjs → eventListing-B7yxGIhX.mjs} +4 -4
  118. package/dist/client/{eventRegistration-O6ZxHfOb.mjs → eventRegistration-BoXfSFgZ.mjs} +1403 -857
  119. package/dist/client/{form-DFa1D8vt.mjs → form-CNQOWhJo.mjs} +5 -5
  120. package/dist/client/{headerSection-CmjHLR-N.mjs → headerSection-DahaBJGQ.mjs} +3 -3
  121. package/dist/client/hooks.mjs +123 -64
  122. package/dist/client/{locationMap-BXKP_lGh.mjs → locationMap-BoDjqYlW.mjs} +4 -4
  123. package/dist/client/{media-Cl38iOMI.mjs → media-BFY0y2rb.mjs} +2 -1
  124. package/dist/client/{newsletterForm-CU-HmxGx.mjs → newsletterForm-DtzXDklw.mjs} +4 -4
  125. package/dist/client/{pricing-KEAEsibq.mjs → pricing-DXEh2Lhh.mjs} +1 -1
  126. package/dist/client/{productDetail-Ckp7MGnt.mjs → productDetail-D7cPa-00.mjs} +3 -3
  127. package/dist/client/{productList-D91V0sYa.mjs → productList-D1XMap66.mjs} +3 -3
  128. package/dist/client/{propsDecoder-DZd6RJeA.mjs → propsDecoder-DiIeBopB.mjs} +758 -245
  129. package/dist/client/rendering/client.mjs +4381 -2892
  130. package/dist/client/rendering/islands.mjs +19 -19
  131. package/dist/client/rendering.mjs +3026 -1852
  132. package/dist/client/result-DRW7JWXz.mjs +30 -0
  133. package/dist/client/{routeIslandApiConfig-E1EkhhYY.mjs → routeIslandApiConfig-wzR4lKAY.mjs} +1 -1
  134. package/dist/client/scrollToBlock-C86BYkyV.mjs +17 -0
  135. package/dist/client/{shop-4F2jLQPI.mjs → shop-CYT5P-S5.mjs} +8 -8
  136. package/dist/client/{shop-commerce-store-vicweP2K.mjs → shop-commerce-store-wKhsJQxI.mjs} +1 -1
  137. package/dist/client/{shop-commerce-BbfCJjB3.mjs → shop-commerce-ypBu_dx4.mjs} +9 -9
  138. package/dist/client/{shopShared-DlONJt0T.mjs → shopShared-DFGhYrf2.mjs} +52 -6
  139. package/dist/client/{useEventFilters-COYyL3m7.mjs → useEventFilters-blSbN5o7.mjs} +8 -8
  140. package/dist/preview-next/client/runtime.mjs +3 -3
  141. package/dist/{sdk-runtime-CwvFFjB-.mjs → sdk-runtime-2p8uQk1V.mjs} +1 -1
  142. package/dist/{separator-ByL72zUT.mjs → separator-Cvvzk0Oo.mjs} +1 -1
  143. package/dist/server/components.mjs +4066 -3765
  144. package/dist/server/config-validation.mjs +185 -126
  145. package/dist/server/config.mjs +185 -126
  146. package/dist/server/data.mjs +123 -64
  147. package/dist/server/index.mjs +1 -1
  148. package/dist/server/next.mjs +615 -314
  149. package/dist/server/page-converter.mjs +100 -56
  150. package/dist/server/prebuild.mjs +1 -1
  151. package/dist/server/rendering/server.mjs +4041 -3751
  152. package/dist/server/rendering.mjs +4069 -3768
  153. package/dist/server/routing.mjs +149 -72
  154. package/dist/server/server.mjs +124 -65
  155. package/dist/server/theme-bridge.mjs +1122 -310
  156. package/dist/server/theme.mjs +126 -116
  157. package/dist/spinner-CDo6AkDz.mjs +658 -0
  158. package/dist/styles/index.css +75 -212
  159. package/package.json +1 -1
  160. package/dist/_dts/billing/src/checkout/platform.d.ts +0 -77
  161. package/dist/_dts/billing/src/config/env.d.ts +0 -29
  162. package/dist/_dts/billing/src/stripe/client.d.ts +0 -29
  163. package/dist/_dts/blocks/src/system/runtime/hooks/usePaymentVerification.d.ts +0 -63
  164. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventCheckoutHandoffRestore.d.ts +0 -9
  165. package/dist/client/urlParams-D71VXFGx.mjs +0 -47
@@ -808,6 +808,7 @@ const buttonSystemSchema = z.strictObject({
808
808
  });
809
809
  //#endregion
810
810
  //#region ../theme-core/src/palette/utils/colorConversion.ts
811
+ const clamp01 = (x) => Math.max(0, Math.min(1, x));
811
812
  function hexToRgb(hex) {
812
813
  const h = hex.replace(/^#/, "");
813
814
  const m = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
@@ -819,12 +820,38 @@ function hexToRgb(hex) {
819
820
  b: int & 255
820
821
  };
821
822
  }
823
+ function rgbToHex(r, g, b) {
824
+ const to = (n) => Math.round(n).toString(16).padStart(2, "0");
825
+ return `#${to(r)}${to(g)}${to(b)}`;
826
+ }
822
827
  function srgbToLinear(u) {
823
828
  const x = u / 255;
824
829
  return x <= .04045 ? x / 12.92 : Math.pow((x + .055) / 1.055, 2.4);
825
830
  }
826
- //#endregion
827
- //#region ../theme-core/src/palette/utils/contrast.ts
831
+ function linearToSrgb(x) {
832
+ return clamp01(x <= .0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - .055) * 255;
833
+ }
834
+ function oklabToSrgb(L, a, b) {
835
+ const l_ = Math.pow(L + .3963377774 * a + .2158037573 * b, 3);
836
+ const m_ = Math.pow(L - .1055613458 * a - .0638541728 * b, 3);
837
+ const s_ = Math.pow(L - .0894841775 * a - 1.291485548 * b, 3);
838
+ const R = 4.0767416621 * l_ - 3.3077115913 * m_ + .2309699292 * s_;
839
+ const G = -1.2684380046 * l_ + 2.6097574011 * m_ - .3413193965 * s_;
840
+ const B = -.0041960863 * l_ - .7034186147 * m_ + 1.707614701 * s_;
841
+ return {
842
+ r: linearToSrgb(R),
843
+ g: linearToSrgb(G),
844
+ b: linearToSrgb(B)
845
+ };
846
+ }
847
+ function oklchToOklab(L, C, h) {
848
+ const hr = h * Math.PI / 180;
849
+ return {
850
+ L,
851
+ a: C * Math.cos(hr),
852
+ b: C * Math.sin(hr)
853
+ };
854
+ }
828
855
  function relativeLuminance(r, g, b) {
829
856
  const R = srgbToLinear(r), G = srgbToLinear(g), B = srgbToLinear(b);
830
857
  return .2126 * R + .7152 * G + .0722 * B;
@@ -961,6 +988,17 @@ function surfaceForegroundStyle(input) {
961
988
  function surfaceForegroundDeclarations(input) {
962
989
  return Object.entries(surfaceForegroundStyle(input)).map(([property, value]) => `${property}: ${value};`);
963
990
  }
991
+ function surfaceForegroundInputFromResolved(foreground, color) {
992
+ const { roles } = foreground;
993
+ return {
994
+ text: color(roles.text),
995
+ heading: color(roles.heading),
996
+ link: color(roles.link),
997
+ linkHover: color(roles.linkHover),
998
+ supporting: color(roles.supporting),
999
+ hoverDecoration: foreground.hoverDecoration
1000
+ };
1001
+ }
964
1002
  function hoverDecoration(input) {
965
1003
  return input.hoverDecoration ?? "underline";
966
1004
  }
@@ -985,6 +1023,16 @@ const GRADIENT_DIRECTION_MAP$1 = {
985
1023
  "to-tr": "to top right",
986
1024
  "to-tl": "to top left"
987
1025
  };
1026
+ /**
1027
+ * Map Tailwind border-width tokens to CSS values
1028
+ */
1029
+ const BORDER_WIDTH_MAP$3 = {
1030
+ "border-0": "0px",
1031
+ "border": "1px",
1032
+ "border-2": "2px",
1033
+ "border-4": "4px",
1034
+ "border-8": "8px"
1035
+ };
988
1036
  //#endregion
989
1037
  //#region ../theme-core/src/buttons/effectClassification.ts
990
1038
  /**
@@ -1659,6 +1707,278 @@ function interpolateHex(startHex, endHex, position) {
1659
1707
  return `#${channel(start.r, end.r)}${channel(start.g, end.g)}${channel(start.b, end.b)}`;
1660
1708
  }
1661
1709
  //#endregion
1710
+ //#region ../theme-core/src/palette/utils/opaqueSurfaceColor.ts
1711
+ /** Resolve an opaque colour, compositing alpha against an explicit underlay. */
1712
+ function resolveOpaqueSurfaceHex(reference, palette, underlayReference) {
1713
+ const opaque = resolveConcreteColorHex(reference, palette);
1714
+ if (opaque) return opaque;
1715
+ if (!underlayReference) return null;
1716
+ const underlayHex = resolveConcreteColorHex(underlayReference, palette);
1717
+ if (!underlayHex) return null;
1718
+ const parts = parseColorTokenParts(reference);
1719
+ if (parts.kind === "token" && parts.opacityPercent !== null) {
1720
+ const foregroundHex = normalizePaletteHex(palette[parts.token] ?? "");
1721
+ return foregroundHex ? compositeHex(foregroundHex, underlayHex, parts.opacityPercent / 100) : null;
1722
+ }
1723
+ const rgba = parseRgba(parts.kind === "raw" ? parts.value : reference);
1724
+ return rgba ? compositeHex(rgbToHex(rgba.r, rgba.g, rgba.b), underlayHex, rgba.alpha) : null;
1725
+ }
1726
+ /** Resolve a colour reference only when it is already fully opaque. */
1727
+ function resolveConcreteColorHex(reference, palette) {
1728
+ const literalHex = normalizePaletteHex(reference);
1729
+ if (literalHex) return literalHex;
1730
+ const parts = parseColorTokenParts(reference);
1731
+ if (parts.kind === "token") {
1732
+ if (parts.opacityPercent !== null) return null;
1733
+ return normalizePaletteHex(palette[parts.token] ?? "");
1734
+ }
1735
+ const literal = parts.kind === "raw" ? parts.value : reference;
1736
+ if (literal.toLowerCase() === "black") return "#000000";
1737
+ if (literal.toLowerCase() === "white") return "#ffffff";
1738
+ return normalizePaletteHex(literal);
1739
+ }
1740
+ function compositeHex(foregroundHex, underlayHex, alpha) {
1741
+ const foreground = hexToRgb(foregroundHex);
1742
+ const underlay = hexToRgb(underlayHex);
1743
+ const channel = (foregroundValue, underlayValue) => Math.round(foregroundValue * alpha + underlayValue * (1 - alpha));
1744
+ return rgbToHex(channel(foreground.r, underlay.r), channel(foreground.g, underlay.g), channel(foreground.b, underlay.b));
1745
+ }
1746
+ function parseRgba(value) {
1747
+ const match = value.trim().match(/^rgba?\(\s*([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*,\s*|\s+)([\d.]+)(?:\s*(?:,|\/)\s*([\d.]+%?))?\s*\)$/i);
1748
+ if (!match) return null;
1749
+ const [, rSource, gSource, bSource] = match;
1750
+ if (!rSource || !gSource || !bSource) return null;
1751
+ const r = Number(rSource);
1752
+ const g = Number(gSource);
1753
+ const b = Number(bSource);
1754
+ const alpha = parseAlpha(match[4]);
1755
+ if (alpha === null || !isRgbChannel(r) || !isRgbChannel(g) || !isRgbChannel(b)) return null;
1756
+ return {
1757
+ r,
1758
+ g,
1759
+ b,
1760
+ alpha
1761
+ };
1762
+ }
1763
+ function parseAlpha(source) {
1764
+ if (!source) return 1;
1765
+ const value = source.endsWith("%") ? Number(source.slice(0, -1)) / 100 : Number(source);
1766
+ return Number.isFinite(value) && value >= 0 && value <= 1 ? value : null;
1767
+ }
1768
+ function isRgbChannel(value) {
1769
+ return Number.isFinite(value) && value >= 0 && value <= 255;
1770
+ }
1771
+ /**
1772
+ * Derive the hover foreground that belongs with a resolved resting foreground.
1773
+ *
1774
+ * A resting foreground is chosen to be legible against a known surface. A hover
1775
+ * state derived from it stays within a bounded distance of that colour, so it
1776
+ * inherits most of the resting colour's contrast margin instead of asserting
1777
+ * an unrelated brand colour that no one checked against the surface.
1778
+ *
1779
+ * That bounded-distance property is what lets this be used on a transparent
1780
+ * overlay header, where the backdrop is only partly knowable: the scrolled
1781
+ * state composites a 60% glass tint over arbitrary page content, so no
1782
+ * compile-time contrast proof is available there. Staying near a colour that
1783
+ * is already trusted in both states is a weaker claim than a contrast proof,
1784
+ * and it is one this function can actually keep.
1785
+ *
1786
+ * Returns `null` when the resting token is not a plain palette token — an
1787
+ * already-faded or raw value has no well-defined softening, and the caller
1788
+ * should leave hover alone rather than guess.
1789
+ */
1790
+ function deriveForegroundHoverToken(restingToken) {
1791
+ const parts = parseColorTokenParts(restingToken);
1792
+ if (parts.kind !== "token") return null;
1793
+ if (parts.opacityPercent !== null) return null;
1794
+ return parseThemeColorToken(`${parts.token}/85`);
1795
+ }
1796
+ /**
1797
+ * Prefer an authored hover that survives every measured backdrop, then a
1798
+ * checked softening of the resting colour. A null backdrop set represents a
1799
+ * trusted treatment: only an explicit authored hover may change the colour.
1800
+ * When neither option works, retain the resting colour and underline it.
1801
+ */
1802
+ function planForegroundHover(input) {
1803
+ const { restingToken, candidate, palette, backdrops } = input;
1804
+ if (candidate && candidate !== restingToken) {
1805
+ if (backdrops === null || survivesEveryBackdrop(candidate, palette, backdrops)) return {
1806
+ kind: "candidate",
1807
+ token: candidate,
1808
+ decoration: "initial"
1809
+ };
1810
+ }
1811
+ const softened = deriveForegroundHoverToken(restingToken);
1812
+ if (softened && backdrops && survivesEveryBackdrop(softened, palette, backdrops)) return {
1813
+ kind: "softened",
1814
+ token: softened,
1815
+ decoration: "initial"
1816
+ };
1817
+ return {
1818
+ kind: "resting",
1819
+ token: restingToken,
1820
+ decoration: "underline"
1821
+ };
1822
+ }
1823
+ function survivesEveryBackdrop(token, palette, backdrops) {
1824
+ return backdrops.length > 0 && backdrops.every((backdrop) => {
1825
+ const foreground = resolveOpaqueSurfaceHex(token, palette, backdrop);
1826
+ return foreground !== null && contrastRatio(foreground, backdrop) >= 4.5;
1827
+ });
1828
+ }
1829
+ //#endregion
1830
+ //#region ../theme-core/src/surfaces/resolveSurfaceForeground.ts
1831
+ const FALLBACK_TEXT_TOKENS = [
1832
+ "text",
1833
+ "primaryForeground",
1834
+ "secondaryForeground",
1835
+ "accentForeground",
1836
+ "neutral-950",
1837
+ "neutral-50"
1838
+ ];
1839
+ const CANONICAL_EQUIVALENT_TOKEN_ORDER = [
1840
+ "text",
1841
+ "primaryForeground",
1842
+ "secondaryForeground",
1843
+ "accentForeground",
1844
+ "onPrimary",
1845
+ "onSecondary",
1846
+ "onAccent",
1847
+ "onSurface",
1848
+ "neutral-950",
1849
+ "neutral-50"
1850
+ ];
1851
+ /**
1852
+ * Resolve the complete foreground contract for an effective page/section or
1853
+ * site-chrome background.
1854
+ *
1855
+ * Deterministic colour backgrounds are validated at ordinary-text WCAG AA.
1856
+ * Existing image/video/gradient treatments are trusted only when they already
1857
+ * carry a usable foreground token; this function deliberately does not claim
1858
+ * pixel-level contrast proof for media.
1859
+ */
1860
+ function resolveSurfaceForeground(background, palette, candidates = {}, options = {}) {
1861
+ const authored = foregroundCandidates(background, candidates);
1862
+ if (background === null) return resolveDeterministicForeground("background", {
1863
+ ...authored,
1864
+ text: authored.text ?? "text"
1865
+ }, palette);
1866
+ if (background.type === "color") {
1867
+ const backgroundReference = normalizedReference(background.color);
1868
+ if (!backgroundReference || backgroundReference === "transparent") return { kind: "unknown" };
1869
+ return resolveDeterministicForeground(backgroundReference, authored, palette, options.compositeBase);
1870
+ }
1871
+ return resolveTrustedTreatmentForeground(authored, palette);
1872
+ }
1873
+ function foregroundCandidates(background, overrides) {
1874
+ return {
1875
+ text: candidateOrBackground(overrides.text, background?.textColor),
1876
+ heading: candidateOrBackground(overrides.heading, background?.headingColor),
1877
+ link: candidateOrBackground(overrides.link, background?.linkColor),
1878
+ linkHover: candidateOrBackground(overrides.linkHover),
1879
+ supporting: candidateOrBackground(overrides.supporting, background?.supportingColor)
1880
+ };
1881
+ }
1882
+ function candidateOrBackground(candidate, backgroundValue) {
1883
+ return candidate ?? backgroundValue ?? null;
1884
+ }
1885
+ function resolveDeterministicForeground(backgroundReference, candidates, palette, compositeBase) {
1886
+ const backgroundHex = resolveOpaqueSurfaceHex(backgroundReference, palette, compositeBase);
1887
+ if (!backgroundHex) return resolveTrustedTreatmentForeground(candidates, palette);
1888
+ const backgroundToken = paletteToken(backgroundReference);
1889
+ const derivedOnToken = backgroundToken ? onColorTokenName(backgroundToken) : null;
1890
+ const text = firstSafeToken([
1891
+ candidates.text,
1892
+ derivedOnToken,
1893
+ ...FALLBACK_TEXT_TOKENS
1894
+ ], backgroundHex, palette, derivedOnToken ? [derivedOnToken] : []);
1895
+ if (!text) return { kind: "unknown" };
1896
+ return knownForeground({
1897
+ text,
1898
+ heading: safeTokenOr(candidates.heading, text, backgroundHex, palette),
1899
+ link: safeTokenOr(candidates.link, text, backgroundHex, palette),
1900
+ supporting: safeTokenOr(candidates.supporting, text, backgroundHex, palette)
1901
+ }, {
1902
+ kind: "measured",
1903
+ backdropHex: backgroundHex
1904
+ }, candidates.linkHover, palette);
1905
+ }
1906
+ function resolveTrustedTreatmentForeground(candidates, palette) {
1907
+ const text = canonicalToken(candidates.text, palette);
1908
+ if (!text) return { kind: "unknown" };
1909
+ return knownForeground({
1910
+ text,
1911
+ heading: canonicalToken(candidates.heading, palette) ?? text,
1912
+ link: canonicalToken(candidates.link, palette) ?? text,
1913
+ supporting: canonicalToken(candidates.supporting, palette) ?? text
1914
+ }, { kind: "trusted-treatment" }, candidates.linkHover, palette);
1915
+ }
1916
+ function knownForeground(roles, provenance, hoverCandidate, palette) {
1917
+ const hover = planForegroundHover({
1918
+ restingToken: roles.link,
1919
+ candidate: canonicalHoverToken(hoverCandidate, palette),
1920
+ palette,
1921
+ backdrops: provenance.kind === "measured" ? [provenance.backdropHex] : null
1922
+ });
1923
+ return {
1924
+ kind: "known",
1925
+ provenance,
1926
+ roles: {
1927
+ ...roles,
1928
+ linkHover: hover.token
1929
+ },
1930
+ hoverDecoration: hover.decoration
1931
+ };
1932
+ }
1933
+ function canonicalHoverToken(reference, palette) {
1934
+ const token = parseThemeColorToken(reference);
1935
+ if (token) {
1936
+ const parts = parseColorTokenParts(token);
1937
+ if (parts.kind === "token" && resolveConcreteColorHex(parts.token, palette)) return token;
1938
+ }
1939
+ return canonicalToken(reference, palette);
1940
+ }
1941
+ function safeTokenOr(candidate, fallback, backgroundHex, palette) {
1942
+ return firstSafeToken([candidate], backgroundHex, palette) ?? fallback;
1943
+ }
1944
+ function firstSafeToken(candidates, backgroundHex, palette, preferredEquivalentTokens = []) {
1945
+ const seen = /* @__PURE__ */ new Set();
1946
+ for (const candidate of candidates) {
1947
+ const token = canonicalToken(candidate, palette, preferredEquivalentTokens);
1948
+ if (!token || seen.has(token)) continue;
1949
+ seen.add(token);
1950
+ const foregroundHex = resolveConcreteColorHex(token, palette);
1951
+ if (foregroundHex && contrastRatio(backgroundHex, foregroundHex) >= 4.5) return token;
1952
+ }
1953
+ return null;
1954
+ }
1955
+ function canonicalToken(reference, palette, preferredEquivalentTokens = []) {
1956
+ const normalized = normalizedReference(reference);
1957
+ if (!normalized || normalized === "transparent") return null;
1958
+ const direct = parseThemeColorToken(normalized);
1959
+ if (direct && resolveConcreteColorHex(direct, palette)) return direct;
1960
+ const referenceHex = resolveConcreteColorHex(normalized, palette);
1961
+ if (!referenceHex) return null;
1962
+ const equivalent = [
1963
+ ...preferredEquivalentTokens,
1964
+ ...CANONICAL_EQUIVALENT_TOKEN_ORDER,
1965
+ ...Object.keys(palette).sort()
1966
+ ].find((token) => {
1967
+ const parsed = parseThemeColorToken(token);
1968
+ return parsed !== null && resolveConcreteColorHex(parsed, palette)?.toLowerCase() === referenceHex.toLowerCase();
1969
+ });
1970
+ return equivalent ? parseThemeColorToken(equivalent) : null;
1971
+ }
1972
+ function paletteToken(reference) {
1973
+ const parts = parseColorTokenParts(reference);
1974
+ return parts.kind === "token" && parts.opacityPercent === null ? parts.token : null;
1975
+ }
1976
+ function normalizedReference(value) {
1977
+ if (typeof value !== "string") return null;
1978
+ const normalized = value.trim();
1979
+ return normalized.length > 0 ? normalized : null;
1980
+ }
1981
+ //#endregion
1662
1982
  //#region ../theme-core/src/utils/colorStyles.ts
1663
1983
  const COLOR_VAR_PREFIX = "--tb-";
1664
1984
  function parseToken(source) {
@@ -1791,14 +2111,449 @@ function shadeBackgroundEffect(config) {
1791
2111
  * palette context and happens in `parseThemeColorReference`.
1792
2112
  */
1793
2113
  const tokenOrCssColorValueSchema = z.string();
2114
+ const CSS_COLOR_FUNCTION_PATTERN = /^(?:rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color|color-mix|light-dark)\(/i;
2115
+ const CSS_HEX_COLOR_PATTERN = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
2116
+ const CSS_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
2117
+ "transparent",
2118
+ "currentcolor",
2119
+ "inherit",
2120
+ "initial",
2121
+ "revert",
2122
+ "revert-layer",
2123
+ "unset"
2124
+ ]);
2125
+ const COMMON_CSS_NAMED_COLORS = /* @__PURE__ */ new Set([
2126
+ "black",
2127
+ "blue",
2128
+ "gray",
2129
+ "green",
2130
+ "grey",
2131
+ "orange",
2132
+ "pink",
2133
+ "purple",
2134
+ "red",
2135
+ "white",
2136
+ "yellow"
2137
+ ]);
2138
+ function isCssColorLiteral(value) {
2139
+ const trimmed = value.trim();
2140
+ if (!trimmed) return false;
2141
+ return CSS_HEX_COLOR_PATTERN.test(trimmed) || CSS_COLOR_FUNCTION_PATTERN.test(trimmed) || /^var\(/i.test(trimmed) || CSS_COLOR_KEYWORDS.has(trimmed.toLowerCase());
2142
+ }
2143
+ function parseContextThemeColorToken(value, context) {
2144
+ const parts = parseColorTokenParts(value);
2145
+ if (parts.kind !== "token") return null;
2146
+ if (!(Boolean(context?.palette?.[value.trim()]) || parts.opacityPercent !== null && Boolean(context?.palette?.[parts.token]))) return null;
2147
+ return value.trim();
2148
+ }
2149
+ function isTokenShapedTypo(value) {
2150
+ if (COMMON_CSS_NAMED_COLORS.has(value.toLowerCase())) return false;
2151
+ if (!/^[a-zA-Z][a-zA-Z0-9]*(?:-[a-zA-Z0-9]+)*(?:\/[^/]+)?$/.test(value)) return false;
2152
+ return !isCssColorLiteral(value);
2153
+ }
2154
+ function warnUnknownThemeColorReference(value) {
2155
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
2156
+ if (!isTokenShapedTypo(value)) return;
2157
+ console.warn(`[theme-core] Unknown Theme V2 color token "${value}" treated as literal CSS. Use raw:* for deliberate custom CSS values.`);
2158
+ }
2159
+ function parseThemeColorReference(input, context) {
2160
+ const trimmed = input.trim();
2161
+ if (trimmed.startsWith("raw:")) return {
2162
+ kind: "literal-css",
2163
+ value: trimmed.slice(4)
2164
+ };
2165
+ const contextToken = parseContextThemeColorToken(trimmed, context);
2166
+ if (contextToken) return {
2167
+ kind: "palette-context-token",
2168
+ token: contextToken
2169
+ };
2170
+ const knownToken = parseThemeColorToken(trimmed);
2171
+ if (knownToken) return {
2172
+ kind: "theme-token",
2173
+ token: knownToken
2174
+ };
2175
+ warnUnknownThemeColorReference(trimmed);
2176
+ return {
2177
+ kind: "literal-css",
2178
+ value: trimmed
2179
+ };
2180
+ }
2181
+ function resolveThemeColorReferenceValue(input, context) {
2182
+ const reference = parseThemeColorReference(input, context);
2183
+ return reference.kind === "literal-css" ? reference.value : rgbColorValue(reference.token);
2184
+ }
1794
2185
  //#endregion
1795
2186
  //#region ../theme-core/src/buttons/buttonSurfaceContext.ts
2187
+ /**
2188
+ * Derive only what the rendered section can honestly prove about its backing.
2189
+ * Images, gradients, alpha/composite layers and arbitrary CSS remain unknown.
2190
+ */
2191
+ function deriveButtonSurfaceContext(input, palette, fallbackToken) {
2192
+ const background = deriveKnownBackground(input, palette, fallbackToken);
2193
+ if (background.kind === "unknown") return background;
2194
+ const explicitForeground = resolveKnownColour(input.kind === "solid" || input.kind === "inherit" ? input.foreground ?? "" : "", palette);
2195
+ const foreground = explicitForeground && contrastRatio(background.colour.hex, explicitForeground.hex) >= 4.5 ? explicitForeground : safestBlackOrWhite(background.colour.hex);
2196
+ return {
2197
+ kind: "known-solid",
2198
+ source: background.source,
2199
+ surface: {
2200
+ kind: "solid",
2201
+ backgroundHex: background.colour.hex,
2202
+ backgroundCss: background.colour.css,
2203
+ foregroundHex: foreground.hex,
2204
+ foregroundCss: foreground.css
2205
+ }
2206
+ };
2207
+ }
2208
+ function deriveKnownBackground(input, palette, fallbackToken) {
2209
+ switch (input.kind) {
2210
+ case "inherit": return fallbackBackground(fallbackToken, palette);
2211
+ case "image": return {
2212
+ kind: "unknown",
2213
+ reason: "image-background"
2214
+ };
2215
+ case "gradient": return {
2216
+ kind: "unknown",
2217
+ reason: "gradient-background"
2218
+ };
2219
+ case "unknown": return {
2220
+ kind: "unknown",
2221
+ reason: "unresolvable-colour"
2222
+ };
2223
+ case "solid": if (input.composited) return {
2224
+ kind: "unknown",
2225
+ reason: "composited-background"
2226
+ };
2227
+ }
2228
+ const authored = readNonEmptyString(input.color);
2229
+ const backgroundValue = authored ?? fallbackToken;
2230
+ if (!backgroundValue) return {
2231
+ kind: "unknown",
2232
+ reason: "missing-background"
2233
+ };
2234
+ const colour = resolveKnownSurfaceColour(backgroundValue, palette, authored ? fallbackToken : void 0);
2235
+ if (!colour) return {
2236
+ kind: "unknown",
2237
+ reason: "unresolvable-colour"
2238
+ };
2239
+ return {
2240
+ kind: "known",
2241
+ colour,
2242
+ source: authored ? colour.source : "fallback-token"
2243
+ };
2244
+ }
2245
+ function resolveKnownSurfaceColour(value, palette, underlayReference) {
2246
+ const hex = resolveOpaqueSurfaceHex(value, palette, underlayReference);
2247
+ if (!hex) return resolveKnownColour(value, palette);
2248
+ const reference = parseThemeColorReference(value, { palette });
2249
+ return {
2250
+ hex,
2251
+ css: resolveThemeColorReferenceValue(value, { palette }),
2252
+ source: reference.kind === "literal-css" ? "literal-css" : "palette-token"
2253
+ };
2254
+ }
2255
+ function fallbackBackground(fallbackToken, palette) {
2256
+ if (!fallbackToken) return {
2257
+ kind: "unknown",
2258
+ reason: "missing-background"
2259
+ };
2260
+ const colour = resolveKnownColour(fallbackToken, palette);
2261
+ return colour ? {
2262
+ kind: "known",
2263
+ colour,
2264
+ source: "fallback-token"
2265
+ } : {
2266
+ kind: "unknown",
2267
+ reason: "unresolvable-colour"
2268
+ };
2269
+ }
2270
+ /**
2271
+ * The complete button context a surface publishes: `--rb-action-*` variables
2272
+ * for every role, resolved against the surface when its colour is provable,
2273
+ * and an explicit reset when it is not (image, gradient, composited), so a
2274
+ * nested surface never inherits a context resolved for an outer band.
2275
+ * A surface with no background of its own publishes nothing and inherits.
2276
+ *
2277
+ * Sections (through the blocks background transforms), card roles and opaque
2278
+ * block panels all publish through this one function.
2279
+ */
2280
+ function surfaceButtonContextStyles(input) {
2281
+ if (!input.buttonSystem) return {};
2282
+ const context = deriveButtonSurfaceContext(input.surface, input.palette, input.fallbackToken);
2283
+ if (context.kind !== "known-solid") return context.reason === "missing-background" ? {} : contextualButtonSurfaceResetStyles();
2284
+ return knownSurfaceButtonContextStyles({
2285
+ surface: context.surface,
2286
+ palette: input.palette,
2287
+ buttonSystem: input.buttonSystem
2288
+ });
2289
+ }
2290
+ /** Every role's contextual styles against one proven solid surface. */
2291
+ function knownSurfaceButtonContextStyles(input) {
2292
+ return Object.fromEntries(VARIANT_ROLES.flatMap((role) => Object.entries(contextualButtonSurfaceStyles(resolveContextualButtonRole({
2293
+ role,
2294
+ buttonSystem: input.buttonSystem,
2295
+ palette: input.palette,
2296
+ surface: input.surface
2297
+ })))));
2298
+ }
2299
+ function resolveContextualButtonRole(input) {
2300
+ const requested = findVariant(input.buttonSystem, input.role);
2301
+ if (!requested) return {
2302
+ kind: "unresolved",
2303
+ requestedRole: input.role,
2304
+ rejectedCandidates: []
2305
+ };
2306
+ const rejected = [];
2307
+ const preferred = compileCandidate(requested, input.buttonSystem, input.palette, input.surface);
2308
+ if (preferred.ok) {
2309
+ if (preferred.contextualHoverRepair) return {
2310
+ kind: "repaired",
2311
+ requestedRole: input.role,
2312
+ candidate: "contextual-hover",
2313
+ states: preferred.states,
2314
+ focusIndicator: preferred.focusIndicator,
2315
+ boundary: boundaryForVariant(requested),
2316
+ rejectedCandidates: []
2317
+ };
2318
+ return {
2319
+ kind: "preferred",
2320
+ requestedRole: input.role,
2321
+ states: preferred.states,
2322
+ focusIndicator: preferred.focusIndicator,
2323
+ boundary: boundaryForVariant(requested)
2324
+ };
2325
+ }
2326
+ rejected.push({
2327
+ candidate: "preferred",
2328
+ error: preferred.error
2329
+ });
2330
+ const policy = input.buttonSystem.surfacePolicy ?? STANDARD_BUTTON_SURFACE_POLICY;
2331
+ for (const fallback of policy[input.role]) {
2332
+ const candidate = candidateVariant(fallback, requested, input.buttonSystem);
2333
+ if (!candidate) continue;
2334
+ const compiled = compileCandidate(candidate.variant, input.buttonSystem, input.palette, input.surface);
2335
+ if (!compiled.ok) {
2336
+ rejected.push({
2337
+ candidate: candidate.name,
2338
+ error: compiled.error
2339
+ });
2340
+ continue;
2341
+ }
2342
+ return {
2343
+ kind: "repaired",
2344
+ requestedRole: input.role,
2345
+ candidate: candidate.name,
2346
+ states: candidate.name === "surface-outline" ? withSurfaceInversion(compiled.states, input.surface) : compiled.states,
2347
+ focusIndicator: compiled.focusIndicator,
2348
+ boundary: candidate.boundary,
2349
+ rejectedCandidates: rejected
2350
+ };
2351
+ }
2352
+ return {
2353
+ kind: "unresolved",
2354
+ requestedRole: input.role,
2355
+ rejectedCandidates: rejected
2356
+ };
2357
+ }
2358
+ function contextualButtonSurfaceStyles(resolution) {
2359
+ if (resolution.kind === "unresolved") return resetActionVariables(resolution.requestedRole);
2360
+ const styles = {};
2361
+ for (const state of BUTTON_VISUAL_STATES) {
2362
+ const colour = resolution.states[state];
2363
+ const prefix = actionVariablePrefix(resolution.requestedRole, state);
2364
+ styles[`${prefix}-background-color`] = colour.kind === "solid" || colour.kind === "known-solid" ? colour.backgroundCss : "transparent";
2365
+ styles[`${prefix}-background-image`] = colour.kind === "gradient" ? colour.backgroundCss : "none";
2366
+ styles[`${prefix}-color`] = colour.foregroundCss;
2367
+ }
2368
+ const focus = resolution.focusIndicator;
2369
+ styles[actionVariableName(resolution.requestedRole, "focus", "outline")] = focus.kind === "two-tone" ? `2px solid ${focus.innerCss}` : `3px solid ${focus.colorCss}`;
2370
+ styles[actionVariableName(resolution.requestedRole, "focus", "outline-offset")] = focus.kind === "two-tone" ? "2px" : "3px";
2371
+ styles[actionVariableName(resolution.requestedRole, "focus", "box-shadow")] = focus.kind === "two-tone" ? `0 0 0 4px ${focus.outerCss}` : "none";
2372
+ styles[actionVariableName(resolution.requestedRole, "base", "outline")] = resolution.boundary ? `${resolution.boundary.width} solid ${resolution.boundary.color}` : "none";
2373
+ styles[actionVariableName(resolution.requestedRole, "base", "outline-offset")] = resolution.boundary ? `-${resolution.boundary.width}` : "0px";
2374
+ return styles;
2375
+ }
2376
+ /**
2377
+ * Break an inherited surface contract when a nested element establishes a
2378
+ * backing whose colours cannot be proven (for example an image or gradient).
2379
+ */
2380
+ function contextualButtonSurfaceResetStyles() {
2381
+ return Object.fromEntries(VARIANT_ROLES.flatMap((role) => Object.entries(resetActionVariables(role))));
2382
+ }
2383
+ function resetActionVariables(role) {
2384
+ const styles = {};
2385
+ for (const state of BUTTON_VISUAL_STATES) for (const property of [
2386
+ "background-color",
2387
+ "background-image",
2388
+ "color"
2389
+ ]) styles[actionVariableName(role, state, property)] = "initial";
2390
+ for (const [state, property] of [
2391
+ ["base", "outline"],
2392
+ ["base", "outline-offset"],
2393
+ ["focus", "outline"],
2394
+ ["focus", "outline-offset"],
2395
+ ["focus", "box-shadow"]
2396
+ ]) styles[actionVariableName(role, state, property)] = "initial";
2397
+ return styles;
2398
+ }
2399
+ function withSurfaceInversion(states, surface) {
2400
+ const inverted = {
2401
+ kind: "known-solid",
2402
+ backgroundCss: surface.foregroundCss,
2403
+ foregroundCss: surface.backgroundCss
2404
+ };
2405
+ return {
2406
+ ...states,
2407
+ hover: inverted,
2408
+ active: inverted
2409
+ };
2410
+ }
1796
2411
  function actionVariableName(role, state, property) {
1797
2412
  return `--rb-action-${role}-${state}-${property}`;
1798
2413
  }
1799
2414
  function actionVariableFallback(role, state, property, fallback) {
1800
2415
  return `var(${actionVariableName(role, state, property)}, ${fallback})`;
1801
2416
  }
2417
+ function actionVariablePrefix(role, state) {
2418
+ return `--rb-action-${role}-${state}`;
2419
+ }
2420
+ function compileCandidate(variant, system, palette, surface) {
2421
+ return compileButtonColourStates({
2422
+ variant,
2423
+ global: system.global,
2424
+ palette,
2425
+ surface
2426
+ });
2427
+ }
2428
+ function candidateVariant(fallback, requested, system) {
2429
+ if (fallback.kind === "variant") {
2430
+ const variant = findVariant(system, fallback.role);
2431
+ return variant ? {
2432
+ name: fallback.role,
2433
+ variant,
2434
+ boundary: boundaryForVariant(variant)
2435
+ } : null;
2436
+ }
2437
+ const width = BORDER_WIDTH_MAP$3.border ?? "1px";
2438
+ return {
2439
+ name: "surface-outline",
2440
+ variant: {
2441
+ ...requested,
2442
+ background: {
2443
+ type: "surface",
2444
+ role: "link"
2445
+ },
2446
+ border: { widthClass: "border" },
2447
+ hoverBackgroundToken: void 0,
2448
+ contextualHover: void 0,
2449
+ effects: void 0
2450
+ },
2451
+ boundary: {
2452
+ width,
2453
+ color: "currentColor"
2454
+ }
2455
+ };
2456
+ }
2457
+ function boundaryForVariant(variant) {
2458
+ if (!variant.border) return null;
2459
+ return {
2460
+ width: BORDER_WIDTH_MAP$3[variant.border.widthClass] ?? "1px",
2461
+ color: "currentColor"
2462
+ };
2463
+ }
2464
+ function findVariant(system, role) {
2465
+ return system.variants.find((variant) => variant.enabled && variant.id === role);
2466
+ }
2467
+ function resolveKnownColour(value, palette) {
2468
+ const reference = parseThemeColorReference(value, { palette });
2469
+ if (reference.kind !== "literal-css") {
2470
+ const token = String(reference.token);
2471
+ const paletteHex = palette[token];
2472
+ const hex = paletteHex ? normalizeOpaqueHex(paletteHex) : null;
2473
+ if (hex) return {
2474
+ hex,
2475
+ css: resolveThemeColorReferenceValue(value, { palette }),
2476
+ source: "palette-token"
2477
+ };
2478
+ const namedHex = NAMED_OPAQUE_COLOURS[token.toLowerCase()];
2479
+ return namedHex ? {
2480
+ hex: namedHex,
2481
+ css: token,
2482
+ source: "literal-css"
2483
+ } : null;
2484
+ }
2485
+ const hex = normalizeOpaqueCssColour(reference.value);
2486
+ return hex ? {
2487
+ hex,
2488
+ css: reference.value,
2489
+ source: "literal-css"
2490
+ } : null;
2491
+ }
2492
+ const NAMED_OPAQUE_COLOURS = {
2493
+ black: "#000000",
2494
+ white: "#ffffff",
2495
+ red: "#ff0000",
2496
+ green: "#008000",
2497
+ blue: "#0000ff",
2498
+ yellow: "#ffff00",
2499
+ gray: "#808080",
2500
+ grey: "#808080",
2501
+ orange: "#ffa500",
2502
+ purple: "#800080",
2503
+ pink: "#ffc0cb"
2504
+ };
2505
+ function normalizeOpaqueCssColour(value) {
2506
+ const trimmed = value.trim();
2507
+ const hex = normalizeOpaqueHex(trimmed);
2508
+ if (hex) return hex;
2509
+ return NAMED_OPAQUE_COLOURS[trimmed.toLowerCase()] ?? normalizeOpaqueRgb(trimmed) ?? normalizeOpaqueOklch(trimmed);
2510
+ }
2511
+ function normalizeOpaqueRgb(value) {
2512
+ const match = value.match(/^rgb\(\s*(\d{1,3})\s*[, ]\s*(\d{1,3})\s*[, ]\s*(\d{1,3})\s*\)$/i);
2513
+ if (!match) return null;
2514
+ const channels = match.slice(1).map(Number);
2515
+ if (channels.some((channel) => channel < 0 || channel > 255)) return null;
2516
+ const [red, green, blue] = channels;
2517
+ return red === void 0 || green === void 0 || blue === void 0 ? null : rgbToHex(red, green, blue);
2518
+ }
2519
+ function normalizeOpaqueOklch(value) {
2520
+ const match = value.match(/^oklch\(\s*([\d.]+)(%)?\s+([\d.]+)\s+([\d.]+)(?:deg)?\s*\)$/i);
2521
+ if (!match?.[1] || !match[3] || !match[4]) return null;
2522
+ const lightness = Number(match[1]) / (match[2] ? 100 : 1);
2523
+ const chroma = Number(match[3]);
2524
+ const hue = Number(match[4]);
2525
+ if (![
2526
+ lightness,
2527
+ chroma,
2528
+ hue
2529
+ ].every(Number.isFinite)) return null;
2530
+ if (lightness < 0 || lightness > 1) return null;
2531
+ const lab = oklchToOklab(lightness, chroma, hue);
2532
+ const rgb = oklabToSrgb(lab.L, lab.a, lab.b);
2533
+ return rgbToHex(clampChannel(rgb.r), clampChannel(rgb.g), clampChannel(rgb.b));
2534
+ }
2535
+ function clampChannel(value) {
2536
+ return Math.max(0, Math.min(255, value));
2537
+ }
2538
+ function normalizeOpaqueHex(value) {
2539
+ const match = value.trim().match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
2540
+ if (!match?.[1]) return null;
2541
+ return `#${(match[1].length === 3 ? match[1].split("").map((digit) => `${digit}${digit}`).join("") : match[1]).toLowerCase()}`;
2542
+ }
2543
+ function safestBlackOrWhite(backgroundHex) {
2544
+ return contrastRatio(backgroundHex, "#000000") >= contrastRatio(backgroundHex, "#ffffff") ? {
2545
+ hex: "#000000",
2546
+ css: "#000000",
2547
+ source: "literal-css"
2548
+ } : {
2549
+ hex: "#ffffff",
2550
+ css: "#ffffff",
2551
+ source: "literal-css"
2552
+ };
2553
+ }
2554
+ function readNonEmptyString(value) {
2555
+ return typeof value === "string" && value.trim() ? value.trim() : null;
2556
+ }
1802
2557
  //#endregion
1803
2558
  //#region ../theme-core/src/buttons/resolvedButtonCss.ts
1804
2559
  /**
@@ -3914,6 +4669,50 @@ function cardDecorationPseudoRules(containerSelectors, decorations, resolveColor
3914
4669
  });
3915
4670
  }
3916
4671
  //#endregion
4672
+ //#region ../theme-core/src/surfaces/opaqueSurface.ts
4673
+ /**
4674
+ * The declarations that make an element own its surface: `color`, every
4675
+ * `--rb-surface-*` role and every `--rb-action-*` variable. Card roles and
4676
+ * opaque block panels both publish through this, so a nested surface can never
4677
+ * inherit a band's light text or its band-resolved buttons.
4678
+ */
4679
+ function opaqueSurfaceDeclarations(paint, context) {
4680
+ if (paint.kind === "transparent") return inheritedSurfaceDeclarations();
4681
+ const textToken = paint.foregroundToken ?? "text";
4682
+ const foreground = resolveSurfaceForeground(paint.kind === "solid" ? {
4683
+ type: "color",
4684
+ color: paint.token,
4685
+ textColor: textToken
4686
+ } : {
4687
+ type: "gradient",
4688
+ textColor: textToken
4689
+ }, context.palette, { linkHover: context.linkHoverToken });
4690
+ const buttonContext = context.buttonSystem ? surfaceButtonContextStyles({
4691
+ surface: paint.kind === "solid" ? {
4692
+ kind: "solid",
4693
+ color: paint.token,
4694
+ foreground: foreground.kind === "known" ? foreground.roles.link : void 0,
4695
+ composited: false
4696
+ } : { kind: "gradient" },
4697
+ palette: context.palette,
4698
+ buttonSystem: context.buttonSystem
4699
+ }) : contextualButtonSurfaceResetStyles();
4700
+ return [...surfaceForegroundDeclarations(foreground.kind === "known" ? surfaceForegroundInputFromResolved(foreground, rgbColorValue) : { text: colorValue(textToken) }), ...Object.entries(buttonContext).map(([property, value]) => `${property}: ${value};`)];
4701
+ }
4702
+ function colorValue(token) {
4703
+ const parsed = parseThemeColorToken(token);
4704
+ return parsed ? rgbColorValue(parsed) : token;
4705
+ }
4706
+ /** Every property a surface publishes, set to `inherit`. */
4707
+ function inheritedSurfaceDeclarations() {
4708
+ return [
4709
+ "color",
4710
+ ...Object.values(SURFACE_FOREGROUND_CSS_VARS),
4711
+ "--rb-surface-link-hover-decoration",
4712
+ ...Object.keys(contextualButtonSurfaceResetStyles())
4713
+ ].map((property) => `${property}: inherit;`);
4714
+ }
4715
+ //#endregion
3917
4716
  //#region ../theme-core/src/utils/cssDeclarations.ts
3918
4717
  /** Render CSS declarations whether callers provide terminators or fragments. */
3919
4718
  function renderCssDeclarations(declarations) {
@@ -5213,12 +6012,22 @@ const mediaPresentationSchema = commonSchema.extend({
5213
6012
  const boxBackgroundSchema = z.discriminatedUnion("type", [
5214
6013
  commonSchema.extend({
5215
6014
  type: z.literal("color"),
5216
- color: tokenOrCssColorValueSchema.nullable().optional()
6015
+ color: tokenOrCssColorValueSchema.nullable().optional(),
6016
+ /**
6017
+ * Compiler-owned. The scrim a site-style colour surface lays over media
6018
+ * that replaces it (a Look's band meeting an authored photo), sized so the
6019
+ * surface's foreground stays legible over any pixel. A colour background
6020
+ * never draws it itself; `resolveSectionBackground` moves it onto the
6021
+ * merged media background as its overlay.
6022
+ */
6023
+ mediaScrim: boxBackgroundOverlaySchema.nullable().optional()
5217
6024
  }),
5218
6025
  commonSchema.extend({
5219
6026
  type: z.literal("gradient"),
5220
6027
  gradient: z.string().nullable().optional(),
5221
- gradientPresetId: z.string().min(1).nullable().optional()
6028
+ gradientPresetId: z.string().min(1).nullable().optional(),
6029
+ /** Compiler-owned; see the colour branch. */
6030
+ mediaScrim: boxBackgroundOverlaySchema.nullable().optional()
5222
6031
  }),
5223
6032
  mediaPresentationSchema.extend({
5224
6033
  type: z.literal("image"),
@@ -6048,6 +6857,172 @@ function generateShadowCssVars(theme) {
6048
6857
  };
6049
6858
  }
6050
6859
  //#endregion
6860
+ //#region ../theme-core/src/blocks/opaquePanels.ts
6861
+ /**
6862
+ * Every element in `blocks-static/` CSS that paints its own background behind
6863
+ * text or buttons, with the palette token it paints.
6864
+ *
6865
+ * The CSS owns layout and the paint itself; this registry owns what the paint
6866
+ * implies. `generateOpaquePanelCss` publishes each panel's foreground contract
6867
+ * and button context from the theme, exactly as card roles and section
6868
+ * backgrounds do, so a white panel inside a dark band never inherits the band's
6869
+ * light text or its band-resolved buttons. `opaquePanels.test.ts` scans the
6870
+ * static CSS and fails when a rule paints a surface token without an entry
6871
+ * here, or when a registered rule declares the contract by hand.
6872
+ *
6873
+ * Selectors are written without the theme-scope prefix; the generator adds it.
6874
+ * A `transparent` entry is a treatment that turns a surface-owning class into
6875
+ * a boxless row, so it hands the surrounding surface's contract back.
6876
+ */
6877
+ const opaquePanels = [
6878
+ {
6879
+ selector: ":where([data-block=\"event-registration\"], [data-block=\"appointment-booking\"]) .rb-reg-surface",
6880
+ paint: {
6881
+ kind: "solid",
6882
+ token: "surface"
6883
+ }
6884
+ },
6885
+ {
6886
+ selector: ":where([data-block='event-registration']) .er-booking-teaser",
6887
+ paint: {
6888
+ kind: "solid",
6889
+ token: "surface"
6890
+ }
6891
+ },
6892
+ {
6893
+ selector: ":where([data-block='event-registration']) .er-booking-bar",
6894
+ paint: {
6895
+ kind: "solid",
6896
+ token: "background"
6897
+ }
6898
+ },
6899
+ {
6900
+ selector: ":where([data-block='event-registration']) .er-modal",
6901
+ paint: {
6902
+ kind: "solid",
6903
+ token: "background"
6904
+ }
6905
+ },
6906
+ {
6907
+ selector: ":where([data-block='event-registration']) .er-booking-overlay__panel",
6908
+ paint: {
6909
+ kind: "solid",
6910
+ token: "background"
6911
+ }
6912
+ },
6913
+ {
6914
+ selector: ":where([data-block='event-calendar']) .event-agenda-card",
6915
+ paint: {
6916
+ kind: "solid",
6917
+ token: "surface"
6918
+ }
6919
+ },
6920
+ {
6921
+ selector: ":where([data-block='event-calendar']) .event-timetable-event",
6922
+ paint: {
6923
+ kind: "solid",
6924
+ token: "surface"
6925
+ }
6926
+ },
6927
+ {
6928
+ selector: ":where([data-block='event-calendar']) .event-week-board",
6929
+ paint: {
6930
+ kind: "solid",
6931
+ token: "surface"
6932
+ }
6933
+ },
6934
+ {
6935
+ selector: ":where([data-block='event-calendar']) .event-week-board-top",
6936
+ paint: {
6937
+ kind: "solid",
6938
+ token: "surfaceAlt"
6939
+ }
6940
+ },
6941
+ {
6942
+ selector: ":where([data-block='event-calendar']) .event-week-board-foot",
6943
+ paint: {
6944
+ kind: "solid",
6945
+ token: "surfaceAlt"
6946
+ }
6947
+ },
6948
+ {
6949
+ selector: ":where([data-block='event-calendar']) .ec-modal",
6950
+ paint: {
6951
+ kind: "solid",
6952
+ token: "surface"
6953
+ }
6954
+ },
6955
+ {
6956
+ selector: ".event-listing-compact-container",
6957
+ paint: {
6958
+ kind: "solid",
6959
+ token: "surface"
6960
+ }
6961
+ },
6962
+ {
6963
+ selector: ".shop__card",
6964
+ paint: {
6965
+ kind: "solid",
6966
+ token: "surface"
6967
+ }
6968
+ },
6969
+ {
6970
+ selector: ".shop--register .shop__card",
6971
+ paint: { kind: "transparent" }
6972
+ },
6973
+ {
6974
+ selector: ":where([data-block=\"shop\"]) .shop__return",
6975
+ paint: {
6976
+ kind: "solid",
6977
+ token: "surface"
6978
+ }
6979
+ },
6980
+ {
6981
+ selector: "[data-block=\"feature-cards\"][data-block-composition=\"asymmetric-emphasis\"] .rb-feature-card:nth-child(even)",
6982
+ paint: {
6983
+ kind: "solid",
6984
+ token: "secondary"
6985
+ }
6986
+ },
6987
+ {
6988
+ selector: ".rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role=\"panel\"]",
6989
+ paint: {
6990
+ kind: "solid",
6991
+ token: "primary"
6992
+ }
6993
+ },
6994
+ {
6995
+ selector: ":is(.rb-event-list-treatment-ruled-register, .rb-event-list-treatment-editorial-register, .rb-event-list-treatment-dense-index) .event-card.event-card",
6996
+ paint: { kind: "transparent" }
6997
+ },
6998
+ {
6999
+ selector: ".feedback-callout",
7000
+ paint: {
7001
+ kind: "solid",
7002
+ token: "surface"
7003
+ }
7004
+ },
7005
+ {
7006
+ selector: "[data-block=\"gifting\"] .gifting-feedback",
7007
+ paint: {
7008
+ kind: "solid",
7009
+ token: "surface"
7010
+ }
7011
+ }
7012
+ ];
7013
+ /**
7014
+ * Publishes every registered panel's surface contract. Emitted after the card
7015
+ * CSS in both the theme runtime and the SDK theme bridge, so a composition
7016
+ * that recolours a card role (the feature-card diptych panel) or a treatment
7017
+ * that empties one (the event registers) wins over the role's own contract.
7018
+ */
7019
+ function generateOpaquePanelCss(input) {
7020
+ return opaquePanels.map((panel) => {
7021
+ const declarations = opaqueSurfaceDeclarations(panel.paint, input.surface);
7022
+ return `:where([data-theme-scope="${input.themeId}"]) ${panel.selector} {\n ${renderCssDeclarations(declarations)}\n}`;
7023
+ }).join("\n\n");
7024
+ }
7025
+ //#endregion
6051
7026
  //#region ../theme-core/src/site-styles/styleTags.ts
6052
7027
  /**
6053
7028
  * Descriptive style-axis metadata for button personalities and palette
@@ -9504,6 +10479,17 @@ function defineBlockAppearancePreset(preset) {
9504
10479
  * (`defineBlockAppearancePreset` defaults it to `none`) — the block's own
9505
10480
  * composition still governs how its items look. The per-Look rationale for the card-bearing ones lives
9506
10481
  * in `.project/plans/1297-card-role-contract-site-styles-own-card-surfaces/mapping-table.md`.
10482
+ *
10483
+ * A Look asking for the `alternative` or `open` role is offered only where the
10484
+ * site style defines that role (`siteStyleSupportsLookDesign`), so two Looks
10485
+ * that differ only in role are distinct wherever both are offered.
10486
+ *
10487
+ * Retiring a Look is removing its entry. A stored pin to a retired id reads as
10488
+ * an unsupported pin in the page design editor model and renders the block's
10489
+ * auto Look, the same path an unavailable Look takes. Duplicates retired in
10490
+ * the 2026-09 Look audit: `feature-cards-soft-panels`,
10491
+ * `feature-cards-photo-tiles`, `feature-cards-editorial-pathways`,
10492
+ * `testimonials-default-cards`.
9507
10493
  */
9508
10494
  const brandScrimOptionGroups = [{
9509
10495
  id: "image-surface-treatment",
@@ -9888,16 +10874,6 @@ defineBlockAppearancePreset({
9888
10874
  treatment: "framed-programme"
9889
10875
  }
9890
10876
  }
9891
- }), defineBlockAppearancePreset({
9892
- id: "feature-cards-soft-panels",
9893
- target: "feature-cards",
9894
- label: "Soft tinted panels",
9895
- blockKinds: ["block.feature-cards"],
9896
- design: {
9897
- sectionSurface: "base",
9898
- contentFrame: plainFrame,
9899
- cardRole: "alternative"
9900
- }
9901
10877
  }), defineBlockAppearancePreset({
9902
10878
  id: "feature-cards-editorial-diptych",
9903
10879
  target: "feature-cards",
@@ -9909,27 +10885,6 @@ defineBlockAppearancePreset({
9909
10885
  contentFrame: plainFrame,
9910
10886
  cardRole: "default"
9911
10887
  }
9912
- }), defineBlockAppearancePreset({
9913
- id: "feature-cards-editorial-pathways",
9914
- target: "feature-cards",
9915
- label: "Editorial pathways",
9916
- blockKinds: ["block.feature-cards"],
9917
- constraints: { layoutVariantKeys: ["editorial-pathways"] },
9918
- design: {
9919
- sectionSurface: "base",
9920
- contentFrame: plainFrame,
9921
- cardRole: "default"
9922
- }
9923
- }), defineBlockAppearancePreset({
9924
- id: "feature-cards-photo-tiles",
9925
- target: "feature-cards",
9926
- label: "Photo tiles",
9927
- blockKinds: ["block.feature-cards"],
9928
- design: {
9929
- sectionSurface: "base",
9930
- contentFrame: plainFrame,
9931
- cardRole: "default"
9932
- }
9933
10888
  }), defineBlockAppearancePreset({
9934
10889
  id: "events-lined-panel",
9935
10890
  target: "events",
@@ -10018,16 +10973,6 @@ defineBlockAppearancePreset({
10018
10973
  contentFrame: panelFrame("raised"),
10019
10974
  cardRole: "default"
10020
10975
  }
10021
- }), defineBlockAppearancePreset({
10022
- id: "testimonials-default-cards",
10023
- target: "testimonials",
10024
- label: "Default cards",
10025
- blockKinds: ["block.testimonials"],
10026
- design: {
10027
- sectionSurface: "base",
10028
- contentFrame: plainFrame,
10029
- cardRole: "default"
10030
- }
10031
10976
  }), defineBlockAppearancePreset({
10032
10977
  id: "testimonials-statement-cards",
10033
10978
  target: "testimonials",
@@ -11262,9 +12207,7 @@ const curatedSiteStyles = [
11262
12207
  recommendedAppearancePresetIds: [
11263
12208
  "hero-split-editorial",
11264
12209
  "intro-portrait-bio",
11265
- "feature-cards-soft-panels",
11266
- "feature-cards-editorial-columns",
11267
- "feature-cards-photo-tiles",
12210
+ "feature-cards-bold-colors",
11268
12211
  "testimonials-non-carded",
11269
12212
  "faq-contained-help",
11270
12213
  "contact-form-panel"
@@ -11908,8 +12851,7 @@ const curatedSiteStyles = [
11908
12851
  "intro-portrait-bio",
11909
12852
  "feature-cards-editorial-columns",
11910
12853
  "feature-cards-editorial-diptych",
11911
- "feature-cards-editorial-pathways",
11912
- "feature-cards-soft-panels",
12854
+ "feature-cards-bold-colors",
11913
12855
  "events-lined-panel",
11914
12856
  "testimonials-non-carded",
11915
12857
  "faq-base-accordion",
@@ -12563,7 +13505,7 @@ ${`:where([data-theme-scope="${themeId}"]) :where([data-block="event-registratio
12563
13505
  --er-step-inactive: rgb(var(--tb-mutedText));
12564
13506
  --er-step-completed: rgb(var(--tb-success));
12565
13507
  --er-step-text: rgb(255 255 255);
12566
- --er-step-text-active: rgb(var(--tb-primaryText, 255 255 255));
13508
+ --er-step-text-active: rgb(var(--tb-onPrimary, 255 255 255));
12567
13509
  --er-step-connector: rgba(var(--tb-border), 0.5);
12568
13510
 
12569
13511
  /* Step title */
@@ -12607,7 +13549,7 @@ ${`:where([data-theme-scope="${themeId}"]) :where([data-block="event-registratio
12607
13549
 
12608
13550
  /* Checkmark indicator */
12609
13551
  --er-check-bg: rgb(var(--tb-primary));
12610
- --er-check-text: rgb(var(--tb-primaryText, 255 255 255));
13552
+ --er-check-text: rgb(var(--tb-onPrimary, 255 255 255));
12611
13553
 
12612
13554
  /* Order summary */
12613
13555
  --er-summary-bg: rgba(var(--tb-primary), 0.1);
@@ -12819,7 +13761,7 @@ ${`:where([data-theme-scope="${themeId}"]) :where([data-block="appointment-booki
12819
13761
  --rb-reg-step-completed: rgb(var(--tb-success));
12820
13762
  --rb-reg-step-inactive: rgb(var(--tb-mutedText));
12821
13763
  --rb-reg-step-text: rgb(255 255 255);
12822
- --rb-reg-step-text-active: rgb(var(--tb-primaryText, 255 255 255));
13764
+ --rb-reg-step-text-active: rgb(var(--tb-onPrimary, 255 255 255));
12823
13765
  --rb-reg-step-connector: rgba(var(--tb-border), 0.5);
12824
13766
  --rb-reg-summary-bg: var(--ab-muted-bg);
12825
13767
  --rb-reg-summary-border: var(--ab-card-border);
@@ -16682,140 +17624,17 @@ const containerResponsiveThemeCss = `/*
16682
17624
  border: 1px solid var(--rb-reg-border, rgb(var(--tb-border, 226 232 240)));
16683
17625
  padding: clamp(1rem, 2vw, 1.5rem);
16684
17626
  background: var(--rb-reg-surface-bg, rgb(var(--tb-surface, 248 250 252)));
16685
- color: var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42))));
16686
- --rb-surface-text-color: var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42))));
16687
- --rb-surface-heading-color: var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42))));
16688
- --rb-surface-link-color: var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42))));
16689
- --rb-surface-link-hover-color: var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42))));
16690
- --rb-surface-link-hover-decoration: underline;
16691
- --rb-surface-supporting-color: var(--rb-reg-on-surface-muted, var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42)))));
17627
+ /* The panel paints its own surface, so it owns its foreground and its
17628
+ * button context. Both are published from the theme by the opaque-panel
17629
+ * registry (theme-core/src/blocks/opaquePanels.ts): an enclosing brand band
17630
+ * would otherwise hand its light text and band-resolved buttons (on Anna's
17631
+ * therapy page, a white-on-white Continue) to this light panel. */
16692
17632
  transition: border-color var(--rb-block-transition, 150ms) ease;
16693
17633
  width: 100%;
16694
17634
  max-width: 800px;
16695
17635
  margin-inline: 0;
16696
17636
  }
16697
17637
 
16698
- /* The panel paints its own opaque surface, so it also has to break the button
16699
- * colour contract it inherits. An enclosing section that sits on a brand band
16700
- * publishes \`--rb-action-*\` variables resolved against *that* band — on Anna's
16701
- * therapy page a ghost primary: transparent background, white label. Those
16702
- * variables inherit, so without this reset a Continue button inside the light
16703
- * form panel renders white-on-white and only appears on hover.
16704
- *
16705
- * Resetting to \`initial\` sends each button back to the generated fallback in
16706
- * its own rule (\`var(--rb-action-…, rgb(var(--tb-primary)))\`), which is the
16707
- * theme's standard button resolved against a normal surface — correct here,
16708
- * because this panel *is* a normal surface.
16709
- *
16710
- * Mirrors \`contextualButtonSurfaceResetStyles()\` in theme-core/buttons; the
16711
- * pairing is pinned by registration-surface-action-reset.test.ts. */
16712
- :where(.theme-scope) :where([data-block="event-registration"], [data-block="appointment-booking"]) .rb-reg-surface {
16713
- --rb-action-primary-base-background-color: initial;
16714
- --rb-action-primary-base-background-image: initial;
16715
- --rb-action-primary-base-color: initial;
16716
- --rb-action-primary-hover-background-color: initial;
16717
- --rb-action-primary-hover-background-image: initial;
16718
- --rb-action-primary-hover-color: initial;
16719
- --rb-action-primary-focus-background-color: initial;
16720
- --rb-action-primary-focus-background-image: initial;
16721
- --rb-action-primary-focus-color: initial;
16722
- --rb-action-primary-active-background-color: initial;
16723
- --rb-action-primary-active-background-image: initial;
16724
- --rb-action-primary-active-color: initial;
16725
- --rb-action-primary-disabled-background-color: initial;
16726
- --rb-action-primary-disabled-background-image: initial;
16727
- --rb-action-primary-disabled-color: initial;
16728
- --rb-action-primary-base-outline: initial;
16729
- --rb-action-primary-base-outline-offset: initial;
16730
- --rb-action-primary-focus-outline: initial;
16731
- --rb-action-primary-focus-outline-offset: initial;
16732
- --rb-action-primary-focus-box-shadow: initial;
16733
-
16734
- --rb-action-secondary-base-background-color: initial;
16735
- --rb-action-secondary-base-background-image: initial;
16736
- --rb-action-secondary-base-color: initial;
16737
- --rb-action-secondary-hover-background-color: initial;
16738
- --rb-action-secondary-hover-background-image: initial;
16739
- --rb-action-secondary-hover-color: initial;
16740
- --rb-action-secondary-focus-background-color: initial;
16741
- --rb-action-secondary-focus-background-image: initial;
16742
- --rb-action-secondary-focus-color: initial;
16743
- --rb-action-secondary-active-background-color: initial;
16744
- --rb-action-secondary-active-background-image: initial;
16745
- --rb-action-secondary-active-color: initial;
16746
- --rb-action-secondary-disabled-background-color: initial;
16747
- --rb-action-secondary-disabled-background-image: initial;
16748
- --rb-action-secondary-disabled-color: initial;
16749
- --rb-action-secondary-base-outline: initial;
16750
- --rb-action-secondary-base-outline-offset: initial;
16751
- --rb-action-secondary-focus-outline: initial;
16752
- --rb-action-secondary-focus-outline-offset: initial;
16753
- --rb-action-secondary-focus-box-shadow: initial;
16754
-
16755
- --rb-action-outline-base-background-color: initial;
16756
- --rb-action-outline-base-background-image: initial;
16757
- --rb-action-outline-base-color: initial;
16758
- --rb-action-outline-hover-background-color: initial;
16759
- --rb-action-outline-hover-background-image: initial;
16760
- --rb-action-outline-hover-color: initial;
16761
- --rb-action-outline-focus-background-color: initial;
16762
- --rb-action-outline-focus-background-image: initial;
16763
- --rb-action-outline-focus-color: initial;
16764
- --rb-action-outline-active-background-color: initial;
16765
- --rb-action-outline-active-background-image: initial;
16766
- --rb-action-outline-active-color: initial;
16767
- --rb-action-outline-disabled-background-color: initial;
16768
- --rb-action-outline-disabled-background-image: initial;
16769
- --rb-action-outline-disabled-color: initial;
16770
- --rb-action-outline-base-outline: initial;
16771
- --rb-action-outline-base-outline-offset: initial;
16772
- --rb-action-outline-focus-outline: initial;
16773
- --rb-action-outline-focus-outline-offset: initial;
16774
- --rb-action-outline-focus-box-shadow: initial;
16775
-
16776
- --rb-action-ghost-base-background-color: initial;
16777
- --rb-action-ghost-base-background-image: initial;
16778
- --rb-action-ghost-base-color: initial;
16779
- --rb-action-ghost-hover-background-color: initial;
16780
- --rb-action-ghost-hover-background-image: initial;
16781
- --rb-action-ghost-hover-color: initial;
16782
- --rb-action-ghost-focus-background-color: initial;
16783
- --rb-action-ghost-focus-background-image: initial;
16784
- --rb-action-ghost-focus-color: initial;
16785
- --rb-action-ghost-active-background-color: initial;
16786
- --rb-action-ghost-active-background-image: initial;
16787
- --rb-action-ghost-active-color: initial;
16788
- --rb-action-ghost-disabled-background-color: initial;
16789
- --rb-action-ghost-disabled-background-image: initial;
16790
- --rb-action-ghost-disabled-color: initial;
16791
- --rb-action-ghost-base-outline: initial;
16792
- --rb-action-ghost-base-outline-offset: initial;
16793
- --rb-action-ghost-focus-outline: initial;
16794
- --rb-action-ghost-focus-outline-offset: initial;
16795
- --rb-action-ghost-focus-box-shadow: initial;
16796
-
16797
- --rb-action-accent-base-background-color: initial;
16798
- --rb-action-accent-base-background-image: initial;
16799
- --rb-action-accent-base-color: initial;
16800
- --rb-action-accent-hover-background-color: initial;
16801
- --rb-action-accent-hover-background-image: initial;
16802
- --rb-action-accent-hover-color: initial;
16803
- --rb-action-accent-focus-background-color: initial;
16804
- --rb-action-accent-focus-background-image: initial;
16805
- --rb-action-accent-focus-color: initial;
16806
- --rb-action-accent-active-background-color: initial;
16807
- --rb-action-accent-active-background-image: initial;
16808
- --rb-action-accent-active-color: initial;
16809
- --rb-action-accent-disabled-background-color: initial;
16810
- --rb-action-accent-disabled-background-image: initial;
16811
- --rb-action-accent-disabled-color: initial;
16812
- --rb-action-accent-base-outline: initial;
16813
- --rb-action-accent-base-outline-offset: initial;
16814
- --rb-action-accent-focus-outline: initial;
16815
- --rb-action-accent-focus-outline-offset: initial;
16816
- --rb-action-accent-focus-box-shadow: initial;
16817
- }
16818
-
16819
17638
  /* Selectable option card */
16820
17639
  :where(.theme-scope) :where([data-block="event-registration"], [data-block="appointment-booking"]) .rb-option-card {
16821
17640
  position: relative;
@@ -16875,7 +17694,7 @@ const containerResponsiveThemeCss = `/*
16875
17694
  :where(.theme-scope) :where([data-block="event-registration"], [data-block="appointment-booking"]) .rb-option-check svg {
16876
17695
  width: 0.75rem;
16877
17696
  height: 0.75rem;
16878
- color: var(--rb-reg-check-text, rgb(var(--tb-primaryText, var(--tb-primaryForeground, 255 255 255))));
17697
+ color: var(--rb-reg-check-text, rgb(var(--tb-onPrimary, var(--tb-primaryForeground, 255 255 255))));
16879
17698
  }
16880
17699
 
16881
17700
  /* Step indicator */
@@ -16915,12 +17734,12 @@ const containerResponsiveThemeCss = `/*
16915
17734
 
16916
17735
  :where(.theme-scope) :where([data-block="event-registration"], [data-block="appointment-booking"]) .rb-step-circle--active {
16917
17736
  background: var(--rb-reg-step-active, rgb(var(--tb-primary, 17 24 39)));
16918
- color: var(--rb-reg-step-text-active, rgb(var(--tb-primaryText, var(--tb-primaryForeground, 255 255 255))));
17737
+ color: var(--rb-reg-step-text-active, rgb(var(--tb-onPrimary, var(--tb-primaryForeground, 255 255 255))));
16919
17738
  }
16920
17739
 
16921
17740
  :where(.theme-scope) :where([data-block="event-registration"], [data-block="appointment-booking"]) .rb-step-circle--completed {
16922
17741
  background: var(--rb-reg-step-completed, rgb(var(--tb-success, 22 163 74)));
16923
- color: var(--rb-reg-step-text-active, rgb(var(--tb-primaryText, var(--tb-primaryForeground, 255 255 255))));
17742
+ color: var(--rb-reg-step-text-active, rgb(var(--tb-onPrimary, var(--tb-primaryForeground, 255 255 255))));
16924
17743
  }
16925
17744
 
16926
17745
  :where(.theme-scope) :where([data-block="event-registration"], [data-block="appointment-booking"]) .rb-step-check {
@@ -17179,7 +17998,7 @@ const containerResponsiveThemeCss = `/*
17179
17998
  justify-content: center;
17180
17999
  border-radius: calc(var(--rb-block-radius) * 0.6);
17181
18000
  background: rgb(var(--tb-primary));
17182
- color: rgb(var(--tb-primaryText, var(--tb-primaryForeground, 255 255 255)));
18001
+ color: rgb(var(--tb-onPrimary, var(--tb-primaryForeground, 255 255 255)));
17183
18002
  padding: 0.625rem 1rem;
17184
18003
  font-size: 0.875rem;
17185
18004
  font-weight: 600;
@@ -19154,7 +19973,7 @@ const containerResponsiveThemeCss = `/*
19154
19973
  :where(.theme-scope) :where([data-block='event-registration']) .er-modal-btn {
19155
19974
  padding: 0.75rem 1.5rem;
19156
19975
  background: rgb(var(--tb-primary));
19157
- color: rgb(var(--tb-primary-foreground, 255 255 255));
19976
+ color: rgb(var(--tb-onPrimary, 255 255 255));
19158
19977
  border: none;
19159
19978
  border-radius: var(--rb-block-radius);
19160
19979
  font-weight: 500;
@@ -19202,7 +20021,7 @@ const containerResponsiveThemeCss = `/*
19202
20021
  border: 1px solid var(--er-border);
19203
20022
  border-radius: var(--rb-block-radius);
19204
20023
  background: var(--er-event-card-bg);
19205
- color: var(--er-on-surface);
20024
+ /* Foreground and button context: theme-core/src/blocks/opaquePanels.ts. */
19206
20025
  }
19207
20026
 
19208
20027
  :where(.theme-scope)
@@ -19353,7 +20172,7 @@ const containerResponsiveThemeCss = `/*
19353
20172
  min-height: 0;
19354
20173
  overflow: hidden;
19355
20174
  background: rgb(var(--tb-background));
19356
- color: var(--er-on-surface);
20175
+ /* Foreground and button context: theme-core/src/blocks/opaquePanels.ts. */
19357
20176
  }
19358
20177
 
19359
20178
  :where(.theme-scope)
@@ -20120,7 +20939,7 @@ const containerResponsiveThemeCss = `/*
20120
20939
  .event-category-btn--active {
20121
20940
  background-color: rgb(var(--tb-primary));
20122
20941
  border-color: rgb(var(--tb-primary));
20123
- color: var(--ec-event-solid-text);
20942
+ color: rgb(var(--tb-onPrimary));
20124
20943
  }
20125
20944
 
20126
20945
  :where(.theme-scope)
@@ -20187,7 +21006,7 @@ const containerResponsiveThemeCss = `/*
20187
21006
  opacity: 1;
20188
21007
  background-color: rgb(var(--tb-primary));
20189
21008
  border-color: rgb(var(--tb-primary));
20190
- color: rgb(var(--tb-primaryText, 255 255 255));
21009
+ color: rgb(var(--tb-onPrimary, 255 255 255));
20191
21010
  }
20192
21011
 
20193
21012
  /* -----------------------------------------------------------------------------
@@ -20245,7 +21064,7 @@ const containerResponsiveThemeCss = `/*
20245
21064
  text-transform: uppercase;
20246
21065
  letter-spacing: 0.05em;
20247
21066
  background-color: rgb(var(--tb-primary));
20248
- color: rgb(var(--tb-primaryText, 255 255 255));
21067
+ color: rgb(var(--tb-onPrimary, 255 255 255));
20249
21068
  border-radius: var(--rb-button-radius);
20250
21069
  }
20251
21070
 
@@ -20287,7 +21106,8 @@ const containerResponsiveThemeCss = `/*
20287
21106
  border: 1px solid var(--ec-border-subtle);
20288
21107
  border-radius: var(--rb-block-radius);
20289
21108
  text-decoration: none;
20290
- color: inherit;
21109
+ /* Paints its own surface: its foreground and button context come from the
21110
+ * opaque-panel registry (theme-core/src/blocks/opaquePanels.ts). */
20291
21111
  transition: all var(--rb-block-transition) ease;
20292
21112
  }
20293
21113
 
@@ -20621,7 +21441,7 @@ const containerResponsiveThemeCss = `/*
20621
21441
  .event-timetable-day-tab--active {
20622
21442
  background-color: rgb(var(--tb-primary));
20623
21443
  border-color: rgb(var(--tb-primary));
20624
- color: rgb(var(--tb-primaryText, 255 255 255));
21444
+ color: rgb(var(--tb-onPrimary, 255 255 255));
20625
21445
  }
20626
21446
 
20627
21447
  :where(.theme-scope)
@@ -20687,7 +21507,7 @@ const containerResponsiveThemeCss = `/*
20687
21507
  padding: 0.125rem 0.375rem;
20688
21508
  border-radius: var(--rb-button-radius);
20689
21509
  background-color: rgb(var(--tb-primary));
20690
- color: rgb(var(--tb-primaryText, 255 255 255));
21510
+ color: rgb(var(--tb-onPrimary, 255 255 255));
20691
21511
  font-size: 0.625rem;
20692
21512
  font-weight: 700;
20693
21513
  text-transform: uppercase;
@@ -20743,7 +21563,6 @@ const containerResponsiveThemeCss = `/*
20743
21563
  border-left-width: 4px;
20744
21564
  border-radius: calc(var(--rb-block-radius) / 1.5);
20745
21565
  background-color: var(--ec-surface);
20746
- color: var(--ec-text);
20747
21566
  box-shadow: 0 1px 2px rgba(var(--tb-text), 0.08);
20748
21567
  cursor: pointer;
20749
21568
  font: inherit;
@@ -20916,7 +21735,6 @@ const containerResponsiveThemeCss = `/*
20916
21735
  border: 1px solid var(--ec-border);
20917
21736
  border-radius: var(--rb-block-radius);
20918
21737
  background-color: var(--ec-surface);
20919
- color: var(--ec-text);
20920
21738
  overflow: hidden;
20921
21739
  }
20922
21740
 
@@ -21866,7 +22684,7 @@ const containerResponsiveThemeCss = `/*
21866
22684
  justify-content: center;
21867
22685
  border-radius: calc(var(--rb-reg-radius, var(--rb-block-radius, 1rem)) * 0.6);
21868
22686
  background: rgb(var(--tb-primary, 17 24 39));
21869
- color: rgb(var(--tb-primaryText, var(--tb-primaryForeground, 255 255 255)));
22687
+ color: rgb(var(--tb-onPrimary, var(--tb-primaryForeground, 255 255 255)));
21870
22688
  padding: 0.625rem 1rem;
21871
22689
  font-size: 0.875rem;
21872
22690
  font-weight: 600;
@@ -22304,7 +23122,7 @@ const containerResponsiveThemeCss = `/*
22304
23122
 
22305
23123
  :where(.theme-scope) .event-reset-filters-btn:hover {
22306
23124
  background-color: rgb(var(--tb-primary));
22307
- color: rgb(var(--tb-primaryText, 255 255 255));
23125
+ color: rgb(var(--tb-onPrimary, 255 255 255));
22308
23126
  }
22309
23127
 
22310
23128
  :where(.theme-scope) .event-reset-filters-btn:focus-visible {
@@ -22662,14 +23480,8 @@ const containerResponsiveThemeCss = `/*
22662
23480
  border: 0;
22663
23481
  border-bottom: 1px solid color-mix(in oklab, currentColor, transparent 78%);
22664
23482
  border-radius: 0;
23483
+ /* Boxless row: the opaque-panel registry hands the band's contract back. */
22665
23484
  background: transparent;
22666
- color: inherit;
22667
- --rb-surface-text-color: inherit;
22668
- --rb-surface-heading-color: inherit;
22669
- --rb-surface-link-color: inherit;
22670
- --rb-surface-link-hover-color: inherit;
22671
- --rb-surface-link-hover-decoration: inherit;
22672
- --rb-surface-supporting-color: inherit;
22673
23485
  }
22674
23486
 
22675
23487
  :where(.theme-scope) .shop--register .shop__card-header {
@@ -22761,14 +23573,8 @@ const containerResponsiveThemeCss = `/*
22761
23573
 
22762
23574
  /* Product card */
22763
23575
  :where(.theme-scope) .shop__card {
23576
+ /* Foreground and button context: theme-core/src/blocks/opaquePanels.ts. */
22764
23577
  background: rgb(var(--tb-surface));
22765
- color: rgb(var(--tb-text, 17 24 39));
22766
- --rb-surface-text-color: rgb(var(--tb-text, 17 24 39));
22767
- --rb-surface-heading-color: rgb(var(--tb-text, 17 24 39));
22768
- --rb-surface-link-color: rgb(var(--tb-primary, var(--tb-text, 17 24 39)));
22769
- --rb-surface-link-hover-color: var(--rb-prose-link-hover-color, rgb(var(--tb-primary, var(--tb-text, 17 24 39))));
22770
- --rb-surface-link-hover-decoration: initial;
22771
- --rb-surface-supporting-color: rgb(var(--tb-mutedText, 100 116 139));
22772
23578
  border: 1px solid rgb(var(--tb-border));
22773
23579
  border-radius: var(--shop-card-radius);
22774
23580
  padding: 1.5rem;
@@ -22929,14 +23735,8 @@ const containerResponsiveThemeCss = `/*
22929
23735
  margin-bottom: 1.25rem;
22930
23736
  border: 1px solid rgb(var(--tb-border));
22931
23737
  border-radius: var(--shop-card-radius);
23738
+ /* Foreground and button context: theme-core/src/blocks/opaquePanels.ts. */
22932
23739
  background: rgb(var(--tb-surface));
22933
- color: rgb(var(--tb-text, 17 24 39));
22934
- --rb-surface-text-color: rgb(var(--tb-text, 17 24 39));
22935
- --rb-surface-heading-color: rgb(var(--tb-text, 17 24 39));
22936
- --rb-surface-link-color: rgb(var(--tb-primary, var(--tb-text, 17 24 39)));
22937
- --rb-surface-link-hover-color: var(--rb-prose-link-hover-color, rgb(var(--tb-primary, var(--tb-text, 17 24 39))));
22938
- --rb-surface-link-hover-decoration: initial;
22939
- --rb-surface-supporting-color: rgb(var(--tb-mutedText, 100 116 139));
22940
23740
  }
22941
23741
 
22942
23742
  :where(.theme-scope) :where([data-block="shop"]) .rb-state-icon--success {
@@ -24879,8 +25679,11 @@ const containerResponsiveThemeCss = `/*
24879
25679
  border-radius: 9999px;
24880
25680
  }
24881
25681
 
25682
+ /* The callout is its own opaque surface: tones tint over the theme surface,
25683
+ * not over whatever band the callout sits on, so its foreground and button
25684
+ * context (theme-core/src/blocks/opaquePanels.ts) hold on any section. */
24882
25685
  :where(.theme-scope) .feedback-callout {
24883
- background: rgba(var(--tb-surface, 248 250 252), 0.92);
25686
+ background: rgb(var(--tb-surface, 248 250 252));
24884
25687
  border-color: rgba(var(--tb-border, 226 232 240), 0.72);
24885
25688
  --rb-feedback-callout-bg-opacity: 0.1;
24886
25689
  --rb-feedback-callout-border-opacity: 0.24;
@@ -24888,25 +25691,33 @@ const containerResponsiveThemeCss = `/*
24888
25691
 
24889
25692
  :where(.theme-scope) .feedback-callout[data-feedback-tone="info"] {
24890
25693
  --rb-feedback-callout-bg-opacity: 0.08;
24891
- background: rgba(var(--tb-primary, 37 99 235), var(--rb-feedback-callout-bg-opacity));
25694
+ background:
25695
+ linear-gradient(rgba(var(--tb-primary, 37 99 235), var(--rb-feedback-callout-bg-opacity)), rgba(var(--tb-primary, 37 99 235), var(--rb-feedback-callout-bg-opacity))),
25696
+ rgb(var(--tb-surface, 248 250 252));
24892
25697
  border-color: rgba(var(--tb-primary, 37 99 235), var(--rb-feedback-callout-border-opacity));
24893
25698
  }
24894
25699
 
24895
25700
  :where(.theme-scope) .feedback-callout[data-feedback-tone="success"] {
24896
- background: rgba(var(--tb-success, 34 197 94), var(--rb-feedback-callout-bg-opacity));
25701
+ background:
25702
+ linear-gradient(rgba(var(--tb-success, 34 197 94), var(--rb-feedback-callout-bg-opacity)), rgba(var(--tb-success, 34 197 94), var(--rb-feedback-callout-bg-opacity))),
25703
+ rgb(var(--tb-surface, 248 250 252));
24897
25704
  border-color: rgba(var(--tb-success, 34 197 94), var(--rb-feedback-callout-border-opacity));
24898
25705
  }
24899
25706
 
24900
25707
  :where(.theme-scope) .feedback-callout[data-feedback-tone="warning"] {
24901
25708
  --rb-feedback-callout-bg-opacity: 0.12;
24902
25709
  --rb-feedback-callout-border-opacity: 0.28;
24903
- background: rgba(var(--tb-warning, 245 158 11), var(--rb-feedback-callout-bg-opacity));
25710
+ background:
25711
+ linear-gradient(rgba(var(--tb-warning, 245 158 11), var(--rb-feedback-callout-bg-opacity)), rgba(var(--tb-warning, 245 158 11), var(--rb-feedback-callout-bg-opacity))),
25712
+ rgb(var(--tb-surface, 248 250 252));
24904
25713
  border-color: rgba(var(--tb-warning, 245 158 11), var(--rb-feedback-callout-border-opacity));
24905
25714
  }
24906
25715
 
24907
25716
  :where(.theme-scope) .feedback-callout[data-feedback-tone="error"] {
24908
25717
  --rb-feedback-callout-border-opacity: 0.26;
24909
- background: rgba(var(--tb-destructive, 239 68 68), var(--rb-feedback-callout-bg-opacity));
25718
+ background:
25719
+ linear-gradient(rgba(var(--tb-destructive, 239 68 68), var(--rb-feedback-callout-bg-opacity)), rgba(var(--tb-destructive, 239 68 68), var(--rb-feedback-callout-bg-opacity))),
25720
+ rgb(var(--tb-surface, 248 250 252));
24910
25721
  border-color: rgba(var(--tb-destructive, 239 68 68), var(--rb-feedback-callout-border-opacity));
24911
25722
  }
24912
25723
 
@@ -25380,26 +26191,30 @@ const containerResponsiveThemeCss = `/*
25380
26191
  border: 1px solid rgb(var(--tb-text, 17 24 39) / 0.12);
25381
26192
  border-radius: 1rem;
25382
26193
  padding: 0.875rem 1rem;
25383
- background: rgb(var(--tb-surface, 255 255 255) / 0.92);
25384
- color: rgb(var(--tb-text, 17 24 39));
26194
+ /* Opaque surface; foreground and button context come from the opaque-panel
26195
+ * registry (theme-core/src/blocks/opaquePanels.ts). Tones tint over it. */
26196
+ background: rgb(var(--tb-surface, 255 255 255));
25385
26197
  }
25386
26198
 
25387
26199
  :where(.theme-scope) [data-block="gifting"] .gifting-feedback[data-feedback-tone="success"] {
25388
26200
  border-color: rgba(var(--tb-success, 34 197 94), 0.3);
25389
- background: rgba(var(--tb-success, 34 197 94), 0.1);
25390
- color: rgb(var(--tb-success, 34 197 94));
26201
+ background:
26202
+ linear-gradient(rgba(var(--tb-success, 34 197 94), 0.1), rgba(var(--tb-success, 34 197 94), 0.1)),
26203
+ rgb(var(--tb-surface, 255 255 255));
25391
26204
  }
25392
26205
 
25393
26206
  :where(.theme-scope) [data-block="gifting"] .gifting-feedback[data-feedback-tone="warning"] {
25394
26207
  border-color: rgba(var(--tb-warning, 245 158 11), 0.35);
25395
- background: rgba(var(--tb-warning, 245 158 11), 0.12);
25396
- color: rgb(var(--tb-warning, 245 158 11));
26208
+ background:
26209
+ linear-gradient(rgba(var(--tb-warning, 245 158 11), 0.12), rgba(var(--tb-warning, 245 158 11), 0.12)),
26210
+ rgb(var(--tb-surface, 255 255 255));
25397
26211
  }
25398
26212
 
25399
26213
  :where(.theme-scope) [data-block="gifting"] .gifting-feedback[data-feedback-tone="error"] {
25400
26214
  border-color: rgba(var(--tb-destructive, 239 68 68), 0.3);
25401
- background: rgba(var(--tb-destructive, 239 68 68), 0.1);
25402
- color: rgb(var(--tb-destructive, 239 68 68));
26215
+ background:
26216
+ linear-gradient(rgba(var(--tb-destructive, 239 68 68), 0.1), rgba(var(--tb-destructive, 239 68 68), 0.1)),
26217
+ rgb(var(--tb-surface, 255 255 255));
25403
26218
  }
25404
26219
 
25405
26220
  /* =============================================================================
@@ -25779,10 +26594,14 @@ const containerResponsiveThemeCss = `/*
25779
26594
  * These treatments retain the event list's content and structural layout.
25780
26595
  * Their contrast relationships come exclusively from the active semantic
25781
26596
  * section surface and named palette tokens.
26597
+ *
26598
+ * The treatment list is \`:is()\`, not \`:where()\`, so each rule outranks the card
26599
+ * role (\`.card-role-x.card-role-x\`) the event card also carries: the rows must
26600
+ * stay boxless whichever stylesheet the page loads last.
25782
26601
  * ============================================================================= */
25783
26602
 
25784
26603
  :where(.theme-scope)
25785
- :where(
26604
+ :is(
25786
26605
  .rb-event-list-treatment-ruled-register,
25787
26606
  .rb-event-list-treatment-editorial-register,
25788
26607
  .rb-event-list-treatment-dense-index
@@ -25794,12 +26613,12 @@ const containerResponsiveThemeCss = `/*
25794
26613
  border-radius: 0;
25795
26614
  background: transparent;
25796
26615
  box-shadow: none;
25797
- color: inherit;
26616
+ /* Boxless row: the opaque-panel registry hands the band's contract back. */
25798
26617
  }
25799
26618
 
25800
26619
  /* Register treatments intentionally prioritise schedule data over imagery. */
25801
26620
  :where(.theme-scope)
25802
- :where(
26621
+ :is(
25803
26622
  .rb-event-list-treatment-ruled-register,
25804
26623
  .rb-event-list-treatment-editorial-register,
25805
26624
  .rb-event-list-treatment-dense-index
@@ -25810,7 +26629,7 @@ const containerResponsiveThemeCss = `/*
25810
26629
  }
25811
26630
 
25812
26631
  :where(.theme-scope)
25813
- :where(
26632
+ :is(
25814
26633
  .rb-event-list-treatment-ruled-register,
25815
26634
  .rb-event-list-treatment-editorial-register,
25816
26635
  .rb-event-list-treatment-dense-index
@@ -25822,7 +26641,7 @@ const containerResponsiveThemeCss = `/*
25822
26641
  }
25823
26642
 
25824
26643
  :where(.theme-scope)
25825
- :where(
26644
+ :is(
25826
26645
  .rb-event-list-treatment-ruled-register,
25827
26646
  .rb-event-list-treatment-editorial-register,
25828
26647
  .rb-event-list-treatment-dense-index
@@ -25834,7 +26653,7 @@ const containerResponsiveThemeCss = `/*
25834
26653
  }
25835
26654
 
25836
26655
  :where(.theme-scope)
25837
- :where(
26656
+ :is(
25838
26657
  .rb-event-list-treatment-ruled-register,
25839
26658
  .rb-event-list-treatment-editorial-register,
25840
26659
  .rb-event-list-treatment-dense-index
@@ -25846,7 +26665,7 @@ const containerResponsiveThemeCss = `/*
25846
26665
  }
25847
26666
 
25848
26667
  :where(.theme-scope)
25849
- :where(
26668
+ :is(
25850
26669
  .rb-event-list-treatment-ruled-register,
25851
26670
  .rb-event-list-treatment-editorial-register,
25852
26671
  .rb-event-list-treatment-dense-index
@@ -25944,7 +26763,7 @@ const containerResponsiveThemeCss = `/*
25944
26763
  @container rb-site (min-width: 768px) {
25945
26764
  @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
25946
26765
  :where(.theme-scope)
25947
- :where(
26766
+ :is(
25948
26767
  .rb-event-list-treatment-ruled-register,
25949
26768
  .rb-event-list-treatment-editorial-register,
25950
26769
  .rb-event-list-treatment-dense-index
@@ -25954,7 +26773,7 @@ const containerResponsiveThemeCss = `/*
25954
26773
  }
25955
26774
 
25956
26775
  :where(.theme-scope)
25957
- :where(
26776
+ :is(
25958
26777
  .rb-event-list-treatment-ruled-register,
25959
26778
  .rb-event-list-treatment-editorial-register,
25960
26779
  .rb-event-list-treatment-dense-index
@@ -25969,7 +26788,7 @@ const containerResponsiveThemeCss = `/*
25969
26788
  }
25970
26789
 
25971
26790
  :where(.theme-scope)
25972
- :where(
26791
+ :is(
25973
26792
  .rb-event-list-treatment-ruled-register,
25974
26793
  .rb-event-list-treatment-editorial-register,
25975
26794
  .rb-event-list-treatment-dense-index
@@ -25980,7 +26799,7 @@ const containerResponsiveThemeCss = `/*
25980
26799
  }
25981
26800
 
25982
26801
  :where(.theme-scope)
25983
- :where(
26802
+ :is(
25984
26803
  .rb-event-list-treatment-ruled-register,
25985
26804
  .rb-event-list-treatment-editorial-register,
25986
26805
  .rb-event-list-treatment-dense-index
@@ -25993,7 +26812,7 @@ const containerResponsiveThemeCss = `/*
25993
26812
  }
25994
26813
 
25995
26814
  :where(.theme-scope)
25996
- :where(
26815
+ :is(
25997
26816
  .rb-event-list-treatment-ruled-register,
25998
26817
  .rb-event-list-treatment-editorial-register,
25999
26818
  .rb-event-list-treatment-dense-index
@@ -26005,7 +26824,7 @@ const containerResponsiveThemeCss = `/*
26005
26824
  }
26006
26825
 
26007
26826
  :where(.theme-scope)
26008
- :where(
26827
+ :is(
26009
26828
  .rb-event-list-treatment-ruled-register,
26010
26829
  .rb-event-list-treatment-editorial-register,
26011
26830
  .rb-event-list-treatment-dense-index
@@ -26016,7 +26835,7 @@ const containerResponsiveThemeCss = `/*
26016
26835
  }
26017
26836
 
26018
26837
  :where(.theme-scope)
26019
- :where(
26838
+ :is(
26020
26839
  .rb-event-list-treatment-ruled-register,
26021
26840
  .rb-event-list-treatment-editorial-register,
26022
26841
  .rb-event-list-treatment-dense-index
@@ -26024,7 +26843,7 @@ const containerResponsiveThemeCss = `/*
26024
26843
  .event-card.event-card.event-card--horizontal
26025
26844
  .card-content > .card-body,
26026
26845
  :where(.theme-scope)
26027
- :where(
26846
+ :is(
26028
26847
  .rb-event-list-treatment-ruled-register,
26029
26848
  .rb-event-list-treatment-editorial-register,
26030
26849
  .rb-event-list-treatment-dense-index
@@ -26035,7 +26854,7 @@ const containerResponsiveThemeCss = `/*
26035
26854
  }
26036
26855
 
26037
26856
  :where(.theme-scope)
26038
- :where(
26857
+ :is(
26039
26858
  .rb-event-list-treatment-ruled-register,
26040
26859
  .rb-event-list-treatment-editorial-register,
26041
26860
  .rb-event-list-treatment-dense-index
@@ -26086,13 +26905,7 @@ const containerResponsiveThemeCss = `/*
26086
26905
  padding: clamp(1.125rem, 2.5vw, 1.875rem);
26087
26906
  border-radius: calc(var(--rb-block-radius, 0.75rem) * 1.5);
26088
26907
  background: rgb(var(--tb-secondary));
26089
- color: rgb(var(--tb-onSecondary));
26090
- --rb-surface-text-color: rgb(var(--tb-onSecondary));
26091
- --rb-surface-heading-color: rgb(var(--tb-onSecondary));
26092
- --rb-surface-link-color: rgb(var(--tb-onSecondary));
26093
- --rb-surface-link-hover-color: rgb(var(--tb-onSecondary));
26094
- --rb-surface-link-hover-decoration: underline;
26095
- --rb-surface-supporting-color: rgb(var(--tb-onSecondary));
26908
+ /* Foreground and button context: theme-core/src/blocks/opaquePanels.ts. */
26096
26909
  }
26097
26910
 
26098
26911
  :where(.theme-scope)
@@ -26217,16 +27030,8 @@ const containerResponsiveThemeCss = `/*
26217
27030
  }
26218
27031
 
26219
27032
  /* Diptych filled panel: a composition over the \`alternative\` role's surface.
26220
- The 18% supporting alpha is accepted CSS-side — see issue 1297 mapping-table. */
26221
- :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] {
26222
- color: rgb(var(--tb-onPrimary));
26223
- --rb-surface-text-color: rgb(var(--tb-onPrimary));
26224
- --rb-surface-heading-color: rgb(var(--tb-onPrimary));
26225
- --rb-surface-link-color: rgb(var(--tb-onPrimary));
26226
- --rb-surface-link-hover-color: rgb(var(--tb-onPrimary));
26227
- --rb-surface-link-hover-decoration: underline;
26228
- --rb-surface-supporting-color: color-mix(in oklab, rgb(var(--tb-onPrimary)), transparent 18%);
26229
- }
27033
+ It paints primary (below); its foreground and button context for that paint
27034
+ come from theme-core/src/blocks/opaquePanels.ts. */
26230
27035
 
26231
27036
  :where(.theme-scope) .rb-feature-cards-grid--editorial-diptych .rb-feature-card[data-surface-role="panel"] > .rb-bg-surface {
26232
27037
  background: rgb(var(--tb-primary));
@@ -26729,6 +27534,51 @@ function ThemeScopeCore({ cssVars, buttonCss, headerCss, footerCss, cardCss, acc
26729
27534
  });
26730
27535
  }
26731
27536
  //#endregion
27537
+ //#region ../theme-core/src/runtime/bridgeColourTokens.ts
27538
+ /**
27539
+ * The SDK theme bridge's colour tokens: RGB channel triplets as CSS vars, the
27540
+ * on-colours derived for them, and the same tokens as an opaque hex palette
27541
+ * for the contrast-checked generators (buttons, block panels).
27542
+ */
27543
+ const RGB_CHANNELS_PATTERN = /^(\d{1,3})\s+(\d{1,3})\s+(\d{1,3})(?:\s*\/\s*(0|1|0?\.\d+|\d{1,3}%))?$/;
27544
+ function hexToRgbChannels(hex) {
27545
+ try {
27546
+ const { r, g, b } = hexToRgb(hex);
27547
+ return `${r} ${g} ${b}`;
27548
+ } catch {
27549
+ throw new Error(`[ThemeBridge] Invalid token value "${hex}": expected a valid hex colour, CSS var(), or RGB channel value.`);
27550
+ }
27551
+ }
27552
+ function deriveConcreteBridgeOnTokens(tokens) {
27553
+ return Object.fromEntries(Object.entries(tokens).flatMap(([name, value]) => {
27554
+ if (!isOnColorBackgroundCandidateToken(name)) return [];
27555
+ const hex = rgbChannelsToOpaqueHex(value);
27556
+ const onToken = onColorTokenName(name);
27557
+ return hex === null || tokens[onToken] !== void 0 ? [] : [[onToken, hexToRgbChannels(getTextColorForBackground(hex))]];
27558
+ }));
27559
+ }
27560
+ function buildBridgeButtonPalette(tokens) {
27561
+ return {
27562
+ ...Object.fromEntries(Object.entries({
27563
+ ...tokens,
27564
+ ...deriveConcreteBridgeOnTokens(tokens)
27565
+ }).flatMap(([name, value]) => {
27566
+ const hex = rgbChannelsToOpaqueHex(value);
27567
+ return hex === null ? [] : [[name, hex]];
27568
+ })),
27569
+ meta: { mode: "light" }
27570
+ };
27571
+ }
27572
+ function rgbChannelsToOpaqueHex(value) {
27573
+ const match = value.match(RGB_CHANNELS_PATTERN);
27574
+ if (!match || match[4] !== void 0) return null;
27575
+ return `#${[
27576
+ Number(match[1]),
27577
+ Number(match[2]),
27578
+ Number(match[3])
27579
+ ].map((channel) => channel.toString(16).padStart(2, "0")).join("")}`;
27580
+ }
27581
+ //#endregion
26732
27582
  //#region ../theme-core/src/buttons/core.ts
26733
27583
  const CORNER_RADIUS_MAP$1 = {
26734
27584
  square: "0",
@@ -27063,7 +27913,6 @@ const BRIDGE_DEFAULT_TOKENS = {
27063
27913
  };
27064
27914
  const TOKEN_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9-]*$/;
27065
27915
  const CSS_VARIABLE_REFERENCE_PATTERN = /^var\(\s*--[a-zA-Z][a-zA-Z0-9-]*(?:\s*,\s*[^{};<>]+)?\s*\)$/;
27066
- const RGB_CHANNELS_PATTERN = /^(\d{1,3})\s+(\d{1,3})\s+(\d{1,3})(?:\s*\/\s*(0|1|0?\.\d+|\d{1,3}%))?$/;
27067
27916
  const CSS_DECLARATION_PROPERTY_PATTERN = /^(?:-[a-zA-Z][a-zA-Z0-9-]*|[a-zA-Z][a-zA-Z0-9-]*|--[a-zA-Z][a-zA-Z0-9-]*)$/;
27068
27917
  const UNSAFE_CSS_MARKUP_PATTERN = /[{}<>]|<\/|<script/i;
27069
27918
  const EXTERNAL_URL_PATTERN = /url\s*\(\s*['"]?(?:https?:|\/\/)/i;
@@ -27149,14 +27998,6 @@ const TOKEN_ALIASES = [
27149
27998
  target: "info"
27150
27999
  }
27151
28000
  ];
27152
- function hexToRgbChannels(hex) {
27153
- try {
27154
- const { r, g, b } = hexToRgb(hex);
27155
- return `${r} ${g} ${b}`;
27156
- } catch {
27157
- throw new Error(`[ThemeBridge] Invalid token value "${hex}": expected a valid hex colour, CSS var(), or RGB channel value.`);
27158
- }
27159
- }
27160
28001
  function normalizeTokenValue(value) {
27161
28002
  const trimmedValue = value.trim();
27162
28003
  if (!isSafeBridgeTokenValue(trimmedValue)) throw new Error(`[ThemeBridge] Invalid token value "${value}": expected a hex colour, CSS var(), or RGB channel value.`);
@@ -27174,7 +28015,13 @@ function buildThemeBridgeRuntime(config) {
27174
28015
  themeId,
27175
28016
  cssVars,
27176
28017
  buttonCss: shouldGenerateComponent(config.components?.buttons) ? generateButtonsCss(config, corners, config.shadows ?? "medium") : void 0,
27177
- cardCss: generateCardsCss(config, corners, config.shadows ?? "medium"),
28018
+ cardCss: `${generateCardsCss(config, corners, config.shadows ?? "medium")}\n\n${generateOpaquePanelCss({
28019
+ themeId,
28020
+ surface: {
28021
+ palette: buildBridgeButtonPalette(resolveBridgeTokens(validatedConfig.tokens)),
28022
+ buttonSystem: void 0
28023
+ }
28024
+ })}`,
27178
28025
  inputCss: shouldGenerateComponent(config.components?.inputs) ? generateInputsCss(corners, config.shadows ?? "medium") : void 0,
27179
28026
  statusCss: generateStatusCss({
27180
28027
  themeId,
@@ -27336,41 +28183,6 @@ function resolveBridgeTokens(tokens) {
27336
28183
  for (const [name, value] of Object.entries(BRIDGE_DEFAULT_TOKENS)) if (!isPresentTokenValue(resolved[name])) resolved[name] = value;
27337
28184
  return resolved;
27338
28185
  }
27339
- function deriveConcreteBridgeOnTokens(tokens) {
27340
- const derived = {};
27341
- for (const [name, value] of Object.entries(tokens)) {
27342
- if (!isOnColorBackgroundCandidateToken(name)) continue;
27343
- const hex = rgbChannelsToOpaqueHex(value);
27344
- if (hex === null) continue;
27345
- const onToken = onColorTokenName(name);
27346
- if (tokens[onToken] === void 0) derived[onToken] = hexToRgbChannels(getTextColorForBackground(hex));
27347
- }
27348
- return derived;
27349
- }
27350
- function buildBridgeButtonPalette(tokens) {
27351
- const palette = {};
27352
- for (const [name, value] of Object.entries(tokens)) {
27353
- const hex = rgbChannelsToOpaqueHex(value);
27354
- if (hex !== null) palette[name] = hex;
27355
- }
27356
- for (const [name, value] of Object.entries(deriveConcreteBridgeOnTokens(tokens))) {
27357
- const hex = rgbChannelsToOpaqueHex(value);
27358
- if (hex !== null) palette[name] = hex;
27359
- }
27360
- return {
27361
- ...palette,
27362
- meta: { mode: "light" }
27363
- };
27364
- }
27365
- function rgbChannelsToOpaqueHex(value) {
27366
- const match = value.match(RGB_CHANNELS_PATTERN);
27367
- if (!match || match[4] !== void 0) return null;
27368
- return `#${[
27369
- Number(match[1]),
27370
- Number(match[2]),
27371
- Number(match[3])
27372
- ].map((channel) => channel.toString(16).padStart(2, "0")).join("")}`;
27373
- }
27374
28186
  function isPresentTokenValue(value) {
27375
28187
  return typeof value === "string" && value.trim().length > 0;
27376
28188
  }