@riverbankcms/sdk 0.104.0 → 0.104.3

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 (129) hide show
  1. package/dist/{PreviewEditorSidebar-ywqcYjTB.mjs → PreviewEditorSidebar-CBMhU9p_.mjs} +3 -3
  2. package/dist/{PreviewEditorUI-DApUNF6g.mjs → PreviewEditorUI-C5KEw4M8.mjs} +2 -2
  3. package/dist/{SdkPreviewModeRuntime-DmHCcIMO.mjs → SdkPreviewModeRuntime-Iq12HjL7.mjs} +1147 -180
  4. package/dist/{SiteChromeCustomizeContext-BV9GU-3C.mjs → SiteChromeCustomizeContext-Cx0NMdyv.mjs} +1818 -951
  5. package/dist/_dts/ai/src/contracts/proposals.d.ts +10 -0
  6. package/dist/_dts/api/src/bookingVerticals.d.ts +64 -1
  7. package/dist/_dts/blocks/src/primitives/layout/Grid.d.ts +12 -8
  8. package/dist/_dts/blocks/src/system/blocks/events/event-calendar.d.ts +3 -0
  9. package/dist/_dts/blocks/src/system/blocks/events/shared/layout.d.ts +11 -1
  10. package/dist/_dts/blocks/src/system/blocks/testimonials.d.ts +7 -1
  11. package/dist/_dts/blocks/src/system/constants/events.d.ts +1 -1
  12. package/dist/_dts/blocks/src/system/fragments/library/testimonialsCarousel.d.ts +12 -0
  13. package/dist/_dts/blocks/src/system/node/validation-constants.d.ts +2 -2
  14. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +14 -0
  15. package/dist/_dts/blocks/src/system/runtime/nodes/buttonAffordance.d.ts +33 -0
  16. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +28 -0
  17. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +32 -0
  18. package/dist/_dts/blocks/src/system/runtime/nodes/shared/contract-schemas.d.ts +1 -0
  19. package/dist/_dts/blocks/src/system/runtime/nodes/svg.d.ts +77 -0
  20. package/dist/_dts/blocks/src/system/transforms/registry/_utils/surfaceForegroundStyles.d.ts +2 -0
  21. package/dist/_dts/blocks/src/system/transforms/registry/boxStyles.d.ts +3 -40
  22. package/dist/_dts/blocks/src/system/transforms/registry/formatting.d.ts +14 -0
  23. package/dist/_dts/blocks/src/system/transforms/registry/layout.d.ts +11 -0
  24. package/dist/_dts/core/src/branded.d.ts +5 -0
  25. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +18 -0
  26. package/dist/_dts/editor/src/index.d.ts +3 -1
  27. package/dist/_dts/editor/src/primitives/rich-text/RichTextEditorSurface.d.ts +14 -6
  28. package/dist/_dts/editor/src/primitives/rich-text/RichTextToolbar.d.ts +23 -4
  29. package/dist/_dts/editor/src/primitives/rich-text/config.d.ts +6 -0
  30. package/dist/_dts/editor/src/primitives/rich-text/extensions/RichTextLink.d.ts +1 -0
  31. package/dist/_dts/editor/src/primitives/rich-text/paste.d.ts +10 -0
  32. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +2 -2
  33. package/dist/_dts/sdk/src/version.d.ts +1 -1
  34. package/dist/_dts/theme-core/src/ThemeScope.d.ts +1 -1
  35. package/dist/_dts/theme-core/src/buttons/affordanceIcons.d.ts +29 -0
  36. package/dist/_dts/theme-core/src/buttons/generateButtonCss.d.ts +6 -0
  37. package/dist/_dts/theme-core/src/buttons/index.d.ts +1 -0
  38. package/dist/_dts/theme-core/src/buttons/personalities/flat-ink.d.ts +20 -0
  39. package/dist/_dts/theme-core/src/buttons/personalities/index.d.ts +11 -0
  40. package/dist/_dts/theme-core/src/buttons/personalities/types.d.ts +4 -0
  41. package/dist/_dts/theme-core/src/buttons/types.d.ts +34 -0
  42. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  43. package/dist/_dts/theme-core/src/gradients/types.d.ts +27 -3
  44. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +51 -15
  45. package/dist/_dts/theme-core/src/palette/curated/brand-led.d.ts +13 -0
  46. package/dist/_dts/theme-core/src/palette/curated/index.d.ts +12 -0
  47. package/dist/_dts/theme-core/src/palette/historicalThemePalette.d.ts +44 -0
  48. package/dist/_dts/theme-core/src/palette/index.d.ts +1 -0
  49. package/dist/_dts/theme-core/src/palette/legacyRuntimeProjection.d.ts +23 -6
  50. package/dist/_dts/theme-core/src/palette/legacySelection.d.ts +13 -2
  51. package/dist/_dts/theme-core/src/palette/paletteCompatibilityTransition.d.ts +0 -9
  52. package/dist/_dts/theme-core/src/palette/persistenceReadSource.d.ts +17 -2
  53. package/dist/_dts/theme-core/src/palette/themePaletteCompatibility.d.ts +3 -4
  54. package/dist/_dts/theme-core/src/runtime/themeCssVarNames.d.ts +1 -0
  55. package/dist/_dts/theme-core/src/schema.d.ts +108 -26
  56. package/dist/_dts/theme-core/src/site-styles/accessibleSurfaces.d.ts +2 -1
  57. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +50 -1
  58. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +2 -2
  59. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +4 -2
  60. package/dist/_dts/theme-core/src/site-styles/headerLooks.d.ts +56 -1
  61. package/dist/_dts/theme-core/src/site-styles/typographyPresets.d.ts +16 -1
  62. package/dist/_dts/theme-core/src/status/index.d.ts +0 -1
  63. package/dist/_dts/theme-core/src/surfaces/foregroundContract.d.ts +19 -0
  64. package/dist/_dts/theme-core/src/surfaces/foregroundRoles.d.ts +9 -0
  65. package/dist/_dts/theme-core/src/surfaces/index.d.ts +2 -0
  66. package/dist/_dts/ui/src/button.d.ts +1 -1
  67. package/dist/cli/index.mjs +1567 -470
  68. package/dist/client/{DiscountCodeDisclosure-Dit2aItR.mjs → DiscountCodeDisclosure-BXM73_E5.mjs} +1 -1
  69. package/dist/client/{EventCombined.client-BnREyCjf.mjs → EventCombined.client-DT5elP7j.mjs} +4 -4
  70. package/dist/client/{EventListing.client-0uHld51x.mjs → EventListing.client-pTYGfV9C.mjs} +2 -2
  71. package/dist/client/{LeafletStaticMap-DJcvSfmj.mjs → LeafletStaticMap-dQrhlUe3.mjs} +1 -1
  72. package/dist/client/{MagicLinkForm-EAsIktfw.mjs → MagicLinkForm-u6Dul4j5.mjs} +2 -2
  73. package/dist/client/{StepIndicator-CRR17tYI.mjs → StepIndicator-nWqineJ2.mjs} +2 -2
  74. package/dist/client/{accordion-B47HIuuH.mjs → accordion-CSJcTUyH.mjs} +1 -1
  75. package/dist/client/{bookingForm-I1E-8F9P.mjs → bookingForm-C73wOuVa.mjs} +6 -6
  76. package/dist/client/bookings.mjs +137 -1
  77. package/dist/client/{buttons-BbPn8OKA.mjs → buttons-DfYyqt8C.mjs} +138 -2
  78. package/dist/client/{carousel-OlM7SyBs.mjs → carousel-fMBb0JSc.mjs} +1 -1
  79. package/dist/client/{cart-BKpG5jz6.mjs → cart-BtaCwT3S.mjs} +3 -3
  80. package/dist/client/{checkout-Dmmr6qoU.mjs → checkout-DkTwZSnh.mjs} +3 -3
  81. package/dist/client/client.mjs +2336 -544
  82. package/dist/client/{colorStyles-DuW4Fjqd.mjs → colorStyles-Wyl6JujD.mjs} +45 -4
  83. package/dist/client/{courseRegistration-CflZiqGk.mjs → courseRegistration-DnLmapJu.mjs} +7 -7
  84. package/dist/client/{eventCalendar-BvoLWcp-.mjs → eventCalendar-BvLOc9KY.mjs} +165 -5
  85. package/dist/client/{eventCombined-DsrO8t_t.mjs → eventCombined-kShgJPQr.mjs} +2 -2
  86. package/dist/client/{eventDetails-C3hxgcJm.mjs → eventDetails-C5NTM0Vo.mjs} +3 -3
  87. package/dist/client/{eventListing-C-hb237X.mjs → eventListing-B82ZdKP6.mjs} +2 -2
  88. package/dist/client/{eventRegistration-B0gyzDGi.mjs → eventRegistration-qJVFqBWo.mjs} +7 -7
  89. package/dist/client/{form-C_RUi6oJ.mjs → form-BvAcKiuu.mjs} +2 -2
  90. package/dist/client/{headerSection-IKQiD2U0.mjs → headerSection-NvAiEHAg.mjs} +3 -3
  91. package/dist/client/hooks.mjs +1023 -337
  92. package/dist/client/{locationMap-8ZBrMzRJ.mjs → locationMap-D0j52xfI.mjs} +3 -3
  93. package/dist/client/{media-CZJKL7fi.mjs → media-BCW_g2cQ.mjs} +1 -1
  94. package/dist/client/{newsletterForm-CYk3ucwj.mjs → newsletterForm-B8_i93qH.mjs} +2 -2
  95. package/dist/client/{pricing-B5GmvfIH.mjs → pricing-DIYnyHOk.mjs} +1 -1
  96. package/dist/client/{productDetail-BjS2Dqzo.mjs → productDetail-w0eAHkuQ.mjs} +3 -3
  97. package/dist/client/{productList-BOHvC8My.mjs → productList-Vsguq5Xt.mjs} +3 -3
  98. package/dist/client/{propsDecoder-WS6UEJwZ.mjs → propsDecoder-CD2pJ1WB.mjs} +37 -2
  99. package/dist/client/rendering/client.mjs +1723 -762
  100. package/dist/client/rendering/islands.mjs +19 -19
  101. package/dist/client/rendering.mjs +2312 -520
  102. package/dist/client/{shop-CON03-wN.mjs → shop-Dgxs-Q8F.mjs} +6 -6
  103. package/dist/client/{shop-commerce-DlqrFgH8.mjs → shop-commerce-DJAhsTaT.mjs} +6 -6
  104. package/dist/client/{shop-commerce-store-l74-Kne6.mjs → shop-commerce-store-CfCXltXW.mjs} +1 -1
  105. package/dist/client/{shopShared-CQheEXSg.mjs → shopShared-CmDcPXUt.mjs} +2 -2
  106. package/dist/client/{useDiscountCodeUi-v6OluyWl.mjs → useDiscountCodeUi-CirrT2bM.mjs} +1 -1
  107. package/dist/client/{useEventFilters-BqSTcNOf.mjs → useEventFilters-CNZxlCcO.mjs} +5 -5
  108. package/dist/client/{usePolling-DMfADjIF.mjs → usePolling-Baks_iBI.mjs} +2 -2
  109. package/dist/preview-next/client/runtime.mjs +3 -3
  110. package/dist/{sdk-runtime-DWb08Xaz.mjs → sdk-runtime-BfNiFeNJ.mjs} +1 -1
  111. package/dist/{separator--6yjc914.mjs → separator-BhAR_Xmf.mjs} +1 -1
  112. package/dist/server/components.mjs +2301 -531
  113. package/dist/server/config-validation.mjs +1028 -337
  114. package/dist/server/config.mjs +1028 -337
  115. package/dist/server/data.mjs +1023 -337
  116. package/dist/server/index.mjs +1 -1
  117. package/dist/server/next.mjs +2304 -534
  118. package/dist/server/page-converter.mjs +1018 -324
  119. package/dist/server/prebuild.mjs +1 -1
  120. package/dist/server/rendering/server.mjs +2286 -521
  121. package/dist/server/rendering.mjs +2301 -531
  122. package/dist/server/routing.mjs +1100 -338
  123. package/dist/server/server.mjs +1024 -338
  124. package/dist/server/theme-bridge.mjs +1240 -77
  125. package/dist/server/theme.mjs +165 -5
  126. package/dist/styles/index.css +617 -34
  127. package/package.json +1 -1
  128. package/dist/_dts/blocks/src/system/transforms/registry/_utils/sectionForegroundStyles.d.ts +0 -2
  129. package/dist/_dts/theme-core/src/status/contextualColors.d.ts +0 -6
@@ -5081,6 +5081,234 @@ function expandPalette(palette) {
5081
5081
  return expanded;
5082
5082
  }
5083
5083
  //#endregion
5084
+ //#region ../theme-core/src/tokens/colorTokenParts.ts
5085
+ function parseOpacityPercent(source) {
5086
+ const trimmed = source.trim();
5087
+ if (!trimmed) return null;
5088
+ if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
5089
+ const value = Number(trimmed);
5090
+ if (!Number.isFinite(value)) return null;
5091
+ if (value < 0 || value > 100) return null;
5092
+ return value;
5093
+ }
5094
+ function parseColorTokenParts(source) {
5095
+ const trimmed = source.trim();
5096
+ if (trimmed.startsWith("raw:")) return {
5097
+ kind: "raw",
5098
+ value: trimmed.slice(4)
5099
+ };
5100
+ const [token, opacityRaw, extra] = trimmed.split("/");
5101
+ if (!token || extra !== void 0) return {
5102
+ kind: "invalid",
5103
+ token: trimmed
5104
+ };
5105
+ if (opacityRaw === void 0) return {
5106
+ kind: "token",
5107
+ token,
5108
+ opacityPercent: null
5109
+ };
5110
+ const opacityPercent = parseOpacityPercent(opacityRaw);
5111
+ if (opacityPercent === null) return {
5112
+ kind: "invalid",
5113
+ token: trimmed
5114
+ };
5115
+ return {
5116
+ kind: "token",
5117
+ token,
5118
+ opacityPercent
5119
+ };
5120
+ }
5121
+ //#endregion
5122
+ //#region ../theme-core/src/tokens/blockColorTokens.ts
5123
+ const blockColorTokenNames = [
5124
+ "text",
5125
+ "heading",
5126
+ "mutedText",
5127
+ "surface",
5128
+ "background",
5129
+ "foreground",
5130
+ "border",
5131
+ "primary",
5132
+ "secondary",
5133
+ "accent",
5134
+ "success",
5135
+ "warning",
5136
+ "info",
5137
+ "error",
5138
+ "destructive",
5139
+ "scrim",
5140
+ "shadow",
5141
+ "onPrimary",
5142
+ "onSecondary",
5143
+ "onAccent",
5144
+ "onSuccess",
5145
+ "onWarning",
5146
+ "onInfo",
5147
+ "onError",
5148
+ "onDestructive",
5149
+ "onSurface"
5150
+ ];
5151
+ const blockColorTokenNameSet = new Set(blockColorTokenNames);
5152
+ function isBlockColorToken(value) {
5153
+ if (typeof value !== "string") return false;
5154
+ const parts = parseColorTokenParts(value);
5155
+ return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
5156
+ }
5157
+ function parseBlockColorToken(value) {
5158
+ if (!isBlockColorToken(value)) return null;
5159
+ return value.trim();
5160
+ }
5161
+ //#endregion
5162
+ //#region ../theme-core/src/tokens/themeColorTokens.ts
5163
+ /**
5164
+ * Known palette role token names. This is the low-level source of truth for
5165
+ * palette roles used by palette variants, Theme V2 schemas, and runtime color
5166
+ * reference parsing.
5167
+ */
5168
+ const PALETTE_TOKEN_NAMES = [
5169
+ "primary",
5170
+ "primaryForeground",
5171
+ "secondary",
5172
+ "accent",
5173
+ "success",
5174
+ "warning",
5175
+ "destructive",
5176
+ "background",
5177
+ "surface",
5178
+ "surfaceAlt",
5179
+ "muted",
5180
+ "text",
5181
+ "border"
5182
+ ];
5183
+ const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
5184
+ const themeColorBaseTokenNames = [
5185
+ ...PALETTE_TOKEN_NAMES,
5186
+ ...blockColorTokenNames,
5187
+ "white",
5188
+ "neutral",
5189
+ "primaryText",
5190
+ "secondaryForeground",
5191
+ "accentForeground"
5192
+ ];
5193
+ const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
5194
+ const themeColorShadeSteps = [
5195
+ "50",
5196
+ "100",
5197
+ "200",
5198
+ "300",
5199
+ "400",
5200
+ "500",
5201
+ "600",
5202
+ "700",
5203
+ "800",
5204
+ "900",
5205
+ "950"
5206
+ ];
5207
+ const shadeStepSet = new Set(themeColorShadeSteps);
5208
+ function isKnownBaseToken(value) {
5209
+ return themeColorBaseTokenSet.has(value);
5210
+ }
5211
+ function isKnownShadeToken(value) {
5212
+ const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
5213
+ if (!match) return false;
5214
+ const [, base, shade] = match;
5215
+ return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
5216
+ }
5217
+ function isThemeColorTokenBase(value) {
5218
+ return isKnownBaseToken(value) || isKnownShadeToken(value);
5219
+ }
5220
+ function isThemeColorToken(value) {
5221
+ if (typeof value !== "string") return false;
5222
+ const parts = parseColorTokenParts(value);
5223
+ return parts.kind === "token" && isThemeColorTokenBase(parts.token);
5224
+ }
5225
+ function parseThemeColorToken(value) {
5226
+ if (!isThemeColorToken(value)) return null;
5227
+ return value.trim();
5228
+ }
5229
+ const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
5230
+ //#endregion
5231
+ //#region ../theme-core/src/palette/colors.ts
5232
+ const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
5233
+ const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
5234
+ const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
5235
+ const paletteModeSchema = z$1.enum(["light", "dark"]);
5236
+ z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
5237
+ const paletteColorsSchema = z$1.object({
5238
+ primary: hexColorSchema,
5239
+ primaryForeground: hexColorSchema,
5240
+ secondary: hexColorSchema,
5241
+ accent: hexColorSchema,
5242
+ success: hexColorSchema,
5243
+ warning: hexColorSchema,
5244
+ destructive: hexColorSchema,
5245
+ background: hexColorSchema,
5246
+ surface: hexColorSchema,
5247
+ surfaceAlt: hexColorSchema,
5248
+ muted: hexColorSchema,
5249
+ text: hexColorSchema,
5250
+ border: hexColorSchema
5251
+ }).strict().transform((value) => value);
5252
+ function normalizePaletteHex(value) {
5253
+ if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
5254
+ const normalized = value.toLowerCase();
5255
+ if (normalized.length === 7) return normalized;
5256
+ const triplet = normalized.slice(1);
5257
+ const red = triplet.charAt(0);
5258
+ const green = triplet.charAt(1);
5259
+ const blue = triplet.charAt(2);
5260
+ return `#${red}${red}${green}${green}${blue}${blue}`;
5261
+ }
5262
+ function paletteColorsFromEntries(entries) {
5263
+ const values = {};
5264
+ for (const entry of entries) {
5265
+ const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
5266
+ if (!parsedRole.success) return {
5267
+ ok: false,
5268
+ error: {
5269
+ kind: "unknown-role",
5270
+ role: entry.name
5271
+ }
5272
+ };
5273
+ const role = parsedRole.data;
5274
+ if (values[role] !== void 0) return {
5275
+ ok: false,
5276
+ error: {
5277
+ kind: "duplicate-role",
5278
+ role
5279
+ }
5280
+ };
5281
+ const hex = normalizePaletteHex(entry.hex);
5282
+ if (!hex) return {
5283
+ ok: false,
5284
+ error: {
5285
+ kind: "invalid-color",
5286
+ role,
5287
+ value: entry.hex
5288
+ }
5289
+ };
5290
+ values[role] = hex;
5291
+ }
5292
+ const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
5293
+ if (missing.length > 0) return {
5294
+ ok: false,
5295
+ error: {
5296
+ kind: "missing-roles",
5297
+ roles: missing
5298
+ }
5299
+ };
5300
+ return {
5301
+ ok: true,
5302
+ colors: paletteColorsSchema.parse(values)
5303
+ };
5304
+ }
5305
+ function requirePaletteColorsFromEntries(entries) {
5306
+ const result = paletteColorsFromEntries(entries);
5307
+ if (result.ok) return result.colors;
5308
+ throw new Error(`Invalid complete palette: ${result.error.kind}`);
5309
+ }
5310
+ new Set(PALETTE_TOKEN_NAMES);
5311
+ //#endregion
5084
5312
  //#region ../../node_modules/.pnpm/@vibrant+image@4.0.4/node_modules/@vibrant/image/dist/esm/histogram.js
5085
5313
  var Histogram = class {
5086
5314
  constructor(pixels, opts) {
@@ -6115,233 +6343,6 @@ const pipeline = new BasicPipeline().filter.register("default", (r, g, b, a) =>
6115
6343
  //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/browser.js
6116
6344
  Vibrant.use(pipeline);
6117
6345
  //#endregion
6118
- //#region ../theme-core/src/tokens/colorTokenParts.ts
6119
- function parseOpacityPercent(source) {
6120
- const trimmed = source.trim();
6121
- if (!trimmed) return null;
6122
- if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
6123
- const value = Number(trimmed);
6124
- if (!Number.isFinite(value)) return null;
6125
- if (value < 0 || value > 100) return null;
6126
- return value;
6127
- }
6128
- function parseColorTokenParts(source) {
6129
- const trimmed = source.trim();
6130
- if (trimmed.startsWith("raw:")) return {
6131
- kind: "raw",
6132
- value: trimmed.slice(4)
6133
- };
6134
- const [token, opacityRaw, extra] = trimmed.split("/");
6135
- if (!token || extra !== void 0) return {
6136
- kind: "invalid",
6137
- token: trimmed
6138
- };
6139
- if (opacityRaw === void 0) return {
6140
- kind: "token",
6141
- token,
6142
- opacityPercent: null
6143
- };
6144
- const opacityPercent = parseOpacityPercent(opacityRaw);
6145
- if (opacityPercent === null) return {
6146
- kind: "invalid",
6147
- token: trimmed
6148
- };
6149
- return {
6150
- kind: "token",
6151
- token,
6152
- opacityPercent
6153
- };
6154
- }
6155
- //#endregion
6156
- //#region ../theme-core/src/tokens/blockColorTokens.ts
6157
- const blockColorTokenNames = [
6158
- "text",
6159
- "heading",
6160
- "mutedText",
6161
- "surface",
6162
- "background",
6163
- "foreground",
6164
- "border",
6165
- "primary",
6166
- "secondary",
6167
- "accent",
6168
- "success",
6169
- "warning",
6170
- "info",
6171
- "error",
6172
- "destructive",
6173
- "scrim",
6174
- "shadow",
6175
- "onPrimary",
6176
- "onSecondary",
6177
- "onAccent",
6178
- "onSuccess",
6179
- "onWarning",
6180
- "onInfo",
6181
- "onError",
6182
- "onDestructive",
6183
- "onSurface"
6184
- ];
6185
- const blockColorTokenNameSet = new Set(blockColorTokenNames);
6186
- function isBlockColorToken(value) {
6187
- if (typeof value !== "string") return false;
6188
- const parts = parseColorTokenParts(value);
6189
- return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
6190
- }
6191
- function parseBlockColorToken(value) {
6192
- if (!isBlockColorToken(value)) return null;
6193
- return value.trim();
6194
- }
6195
- //#endregion
6196
- //#region ../theme-core/src/tokens/themeColorTokens.ts
6197
- /**
6198
- * Known palette role token names. This is the low-level source of truth for
6199
- * palette roles used by palette variants, Theme V2 schemas, and runtime color
6200
- * reference parsing.
6201
- */
6202
- const PALETTE_TOKEN_NAMES = [
6203
- "primary",
6204
- "primaryForeground",
6205
- "secondary",
6206
- "accent",
6207
- "success",
6208
- "warning",
6209
- "destructive",
6210
- "background",
6211
- "surface",
6212
- "surfaceAlt",
6213
- "muted",
6214
- "text",
6215
- "border"
6216
- ];
6217
- const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
6218
- const themeColorBaseTokenNames = [
6219
- ...PALETTE_TOKEN_NAMES,
6220
- ...blockColorTokenNames,
6221
- "white",
6222
- "neutral",
6223
- "primaryText",
6224
- "secondaryForeground",
6225
- "accentForeground"
6226
- ];
6227
- const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
6228
- const themeColorShadeSteps = [
6229
- "50",
6230
- "100",
6231
- "200",
6232
- "300",
6233
- "400",
6234
- "500",
6235
- "600",
6236
- "700",
6237
- "800",
6238
- "900",
6239
- "950"
6240
- ];
6241
- const shadeStepSet = new Set(themeColorShadeSteps);
6242
- function isKnownBaseToken(value) {
6243
- return themeColorBaseTokenSet.has(value);
6244
- }
6245
- function isKnownShadeToken(value) {
6246
- const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
6247
- if (!match) return false;
6248
- const [, base, shade] = match;
6249
- return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
6250
- }
6251
- function isThemeColorTokenBase(value) {
6252
- return isKnownBaseToken(value) || isKnownShadeToken(value);
6253
- }
6254
- function isThemeColorToken(value) {
6255
- if (typeof value !== "string") return false;
6256
- const parts = parseColorTokenParts(value);
6257
- return parts.kind === "token" && isThemeColorTokenBase(parts.token);
6258
- }
6259
- function parseThemeColorToken(value) {
6260
- if (!isThemeColorToken(value)) return null;
6261
- return value.trim();
6262
- }
6263
- const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
6264
- //#endregion
6265
- //#region ../theme-core/src/palette/colors.ts
6266
- const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
6267
- const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
6268
- const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
6269
- const paletteModeSchema = z$1.enum(["light", "dark"]);
6270
- z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
6271
- const paletteColorsSchema = z$1.object({
6272
- primary: hexColorSchema,
6273
- primaryForeground: hexColorSchema,
6274
- secondary: hexColorSchema,
6275
- accent: hexColorSchema,
6276
- success: hexColorSchema,
6277
- warning: hexColorSchema,
6278
- destructive: hexColorSchema,
6279
- background: hexColorSchema,
6280
- surface: hexColorSchema,
6281
- surfaceAlt: hexColorSchema,
6282
- muted: hexColorSchema,
6283
- text: hexColorSchema,
6284
- border: hexColorSchema
6285
- }).strict().transform((value) => value);
6286
- function normalizePaletteHex(value) {
6287
- if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
6288
- const normalized = value.toLowerCase();
6289
- if (normalized.length === 7) return normalized;
6290
- const triplet = normalized.slice(1);
6291
- const red = triplet.charAt(0);
6292
- const green = triplet.charAt(1);
6293
- const blue = triplet.charAt(2);
6294
- return `#${red}${red}${green}${green}${blue}${blue}`;
6295
- }
6296
- function paletteColorsFromEntries(entries) {
6297
- const values = {};
6298
- for (const entry of entries) {
6299
- const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
6300
- if (!parsedRole.success) return {
6301
- ok: false,
6302
- error: {
6303
- kind: "unknown-role",
6304
- role: entry.name
6305
- }
6306
- };
6307
- const role = parsedRole.data;
6308
- if (values[role] !== void 0) return {
6309
- ok: false,
6310
- error: {
6311
- kind: "duplicate-role",
6312
- role
6313
- }
6314
- };
6315
- const hex = normalizePaletteHex(entry.hex);
6316
- if (!hex) return {
6317
- ok: false,
6318
- error: {
6319
- kind: "invalid-color",
6320
- role,
6321
- value: entry.hex
6322
- }
6323
- };
6324
- values[role] = hex;
6325
- }
6326
- const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
6327
- if (missing.length > 0) return {
6328
- ok: false,
6329
- error: {
6330
- kind: "missing-roles",
6331
- roles: missing
6332
- }
6333
- };
6334
- return {
6335
- ok: true,
6336
- colors: paletteColorsSchema.parse(values)
6337
- };
6338
- }
6339
- function requirePaletteColorsFromEntries(entries) {
6340
- const result = paletteColorsFromEntries(entries);
6341
- if (result.ok) return result.colors;
6342
- throw new Error(`Invalid complete palette: ${result.error.kind}`);
6343
- }
6344
- //#endregion
6345
6346
  //#region ../theme-core/src/site-styles/styleTags.ts
6346
6347
  /**
6347
6348
  * Descriptive style-axis metadata for button personalities and palette
@@ -6615,6 +6616,71 @@ const brandLedJewel = defineCuratedPalette({
6615
6616
  }
6616
6617
  ]
6617
6618
  });
6619
+ const brandLedSpruce = defineCuratedPalette({
6620
+ id: "brand-led-spruce",
6621
+ name: "Brand-led — Spruce",
6622
+ description: "Spruce-ink primary and moss secondary with an amber accent over a green-mist ground and hairline sage borders.",
6623
+ tags: {
6624
+ tone: "classic",
6625
+ energy: "calm",
6626
+ era: "editorial"
6627
+ },
6628
+ mode: "light",
6629
+ colors: [
6630
+ {
6631
+ name: "primary",
6632
+ hex: "#14312b"
6633
+ },
6634
+ {
6635
+ name: "secondary",
6636
+ hex: "#356a59"
6637
+ },
6638
+ {
6639
+ name: "accent",
6640
+ hex: "#f08a4b"
6641
+ },
6642
+ {
6643
+ name: "success",
6644
+ hex: "#356a59"
6645
+ },
6646
+ {
6647
+ name: "warning",
6648
+ hex: "#f08a4b"
6649
+ },
6650
+ {
6651
+ name: "destructive",
6652
+ hex: "#b5561f"
6653
+ },
6654
+ {
6655
+ name: "background",
6656
+ hex: "#ecefe9"
6657
+ },
6658
+ {
6659
+ name: "surface",
6660
+ hex: "#ffffff"
6661
+ },
6662
+ {
6663
+ name: "surfaceAlt",
6664
+ hex: "#f4f6f1"
6665
+ },
6666
+ {
6667
+ name: "muted",
6668
+ hex: "#5c6a64"
6669
+ },
6670
+ {
6671
+ name: "text",
6672
+ hex: "#1c2a26"
6673
+ },
6674
+ {
6675
+ name: "primaryForeground",
6676
+ hex: "#f4f7f1"
6677
+ },
6678
+ {
6679
+ name: "border",
6680
+ hex: "#d7ddd2"
6681
+ }
6682
+ ]
6683
+ });
6618
6684
  const brandLedBurgundyGold = defineCuratedPalette({
6619
6685
  id: "brand-led-burgundy-gold",
6620
6686
  name: "Brand-led — Burgundy Gold",
@@ -7154,6 +7220,7 @@ const curatedPalettes = [
7154
7220
  brandLedWarm,
7155
7221
  brandLedJewel,
7156
7222
  brandLedBurgundyGold,
7223
+ brandLedSpruce,
7157
7224
  defineCuratedPalette({
7158
7225
  id: "warm-neutral-clay",
7159
7226
  name: "Warm Neutral — Clay",
@@ -8215,6 +8282,10 @@ const buttonBorderSchema = z$1.object({
8215
8282
  widthClass: z$1.string(),
8216
8283
  colorToken: themeColorTokenSchema
8217
8284
  });
8285
+ const buttonVariantAffordanceSchema = z$1.object({
8286
+ iconId: z$1.string().min(1),
8287
+ colorToken: themeColorTokenSchema.optional()
8288
+ });
8218
8289
  const variantShadowSchema = z$1.object({
8219
8290
  elevation: z$1.enum([
8220
8291
  "none",
@@ -8268,7 +8339,8 @@ const buttonVariantSchema = z$1.object({
8268
8339
  border: buttonBorderSchema.optional(),
8269
8340
  hoverBackgroundToken: z$1.string().optional(),
8270
8341
  effects: variantEffectsSchema.optional(),
8271
- sizes: variantSizeOverridesSchema.optional()
8342
+ sizes: variantSizeOverridesSchema.optional(),
8343
+ affordance: buttonVariantAffordanceSchema.optional()
8272
8344
  });
8273
8345
  const buttonSystemSchema = z$1.object({
8274
8346
  global: buttonGlobalSettingsSchema,
@@ -8812,6 +8884,136 @@ const editorialLink = defineButtonPersonality({
8812
8884
  }
8813
8885
  });
8814
8886
  //#endregion
8887
+ //#region ../theme-core/src/buttons/personalities/flat-ink.ts
8888
+ /**
8889
+ * Flat Ink — a solid ink primary, a hairline ghost secondary, and a single
8890
+ * amber accent climax, all completely flat (no drop shadow) with a modest
8891
+ * radius. Reads as grounded, editorial and clinician-precise: the Fulcrum
8892
+ * physiotherapy voice. The primary shifts to the secondary (moss) on hover and
8893
+ * every variant lifts 2px; the amber accent is meant to be used sparingly as
8894
+ * the page's one conversion climax.
8895
+ */
8896
+ const flatInk = defineButtonPersonality({
8897
+ id: "flat-ink",
8898
+ name: "Flat Ink",
8899
+ description: "Flat solid-ink primary, hairline ghost secondary, and a single amber accent climax — modest radius, no shadow.",
8900
+ tags: {
8901
+ tone: "classic",
8902
+ energy: "calm",
8903
+ motion: "subtle",
8904
+ era: "editorial"
8905
+ },
8906
+ buttonSystem: {
8907
+ global: {
8908
+ cornerStyle: "rounded",
8909
+ shadow: "low",
8910
+ textTransform: "none",
8911
+ fontWeight: 600,
8912
+ borderWidth: 1,
8913
+ typography: "body",
8914
+ letterSpacing: "tight",
8915
+ paddingPreset: "default",
8916
+ hoverTransform: "lift",
8917
+ hoverColor: "none"
8918
+ },
8919
+ sizes: {
8920
+ sm: {
8921
+ padding: "0.625rem 1.125rem",
8922
+ fontSize: "text-sm"
8923
+ },
8924
+ md: { padding: "0.8125rem 1.375rem" },
8925
+ lg: {
8926
+ padding: "0.9375rem 1.625rem",
8927
+ fontSize: "text-lg"
8928
+ },
8929
+ xl: {
8930
+ padding: "1.0625rem 2rem",
8931
+ fontSize: "text-xl"
8932
+ }
8933
+ },
8934
+ variants: [
8935
+ {
8936
+ id: "primary",
8937
+ name: "Primary",
8938
+ enabled: true,
8939
+ priority: 1,
8940
+ background: {
8941
+ type: "solid",
8942
+ colorToken: "primary"
8943
+ },
8944
+ textColorToken: "primaryForeground",
8945
+ borderRadius: "rounded-lg",
8946
+ affordance: {
8947
+ iconId: "ArrowRight",
8948
+ colorToken: "accent"
8949
+ },
8950
+ shadow: {
8951
+ elevation: "none",
8952
+ softness: null,
8953
+ position: "bottom"
8954
+ },
8955
+ effects: { hover: [{
8956
+ effectId: "lift",
8957
+ options: { translateY: "-2px" }
8958
+ }, {
8959
+ effectId: "background-on-hover",
8960
+ options: { colorToken: "secondary" }
8961
+ }] }
8962
+ },
8963
+ {
8964
+ id: "secondary",
8965
+ name: "Secondary",
8966
+ enabled: true,
8967
+ priority: 2,
8968
+ background: { type: "transparent" },
8969
+ textColorToken: "primary",
8970
+ borderRadius: "rounded-lg",
8971
+ border: {
8972
+ widthClass: "border",
8973
+ colorToken: "border"
8974
+ },
8975
+ shadow: {
8976
+ elevation: "none",
8977
+ softness: null,
8978
+ position: "bottom"
8979
+ },
8980
+ effects: { hover: [{
8981
+ effectId: "lift",
8982
+ options: { translateY: "-2px" }
8983
+ }, {
8984
+ effectId: "background-on-hover",
8985
+ options: { colorToken: "surfaceAlt" }
8986
+ }] }
8987
+ },
8988
+ {
8989
+ id: "accent",
8990
+ name: "Accent",
8991
+ enabled: true,
8992
+ priority: 3,
8993
+ background: {
8994
+ type: "solid",
8995
+ colorToken: "accent"
8996
+ },
8997
+ textColorToken: "accentForeground",
8998
+ borderRadius: "rounded-lg",
8999
+ shadow: {
9000
+ elevation: "none",
9001
+ softness: null,
9002
+ position: "bottom"
9003
+ },
9004
+ affordance: {
9005
+ iconId: "ArrowRight",
9006
+ colorToken: "accentForeground"
9007
+ },
9008
+ effects: { hover: [{
9009
+ effectId: "lift",
9010
+ options: { translateY: "-2px" }
9011
+ }] }
9012
+ }
9013
+ ]
9014
+ }
9015
+ });
9016
+ //#endregion
8815
9017
  //#region ../theme-core/src/buttons/personalities/ink-stamp.ts
8816
9018
  /**
8817
9019
  * Ink Stamp — solid earthy fills with a bordered, hand-stamped block-print
@@ -9582,6 +9784,7 @@ const buttonPersonalities = [
9582
9784
  editorialLink,
9583
9785
  pebble,
9584
9786
  inkStamp,
9787
+ flatInk,
9585
9788
  brushedWash,
9586
9789
  showtimePill,
9587
9790
  joyStamp,
@@ -10514,6 +10717,13 @@ const legacyHeaderLookAliases = defineExactCatalog()([
10514
10717
  styleId: asHeaderStyleId("minimal")
10515
10718
  }
10516
10719
  },
10720
+ {
10721
+ lookId: asHeaderLookId("classic-centered-nav"),
10722
+ selection: {
10723
+ layoutId: asHeaderLayoutId("classic"),
10724
+ styleId: asHeaderStyleId("fine-line")
10725
+ }
10726
+ },
10517
10727
  {
10518
10728
  lookId: asHeaderLookId("brand-solid"),
10519
10729
  selection: {
@@ -10584,6 +10794,50 @@ function getCompatibleHeaderStyleIds(layoutId) {
10584
10794
  return isHeaderStyleCompatibleWithLayout(getHeaderStyleOrThrow(styleId), layoutId);
10585
10795
  });
10586
10796
  }
10797
+ function applyNavTypography(style, typography) {
10798
+ switch (typography) {
10799
+ case "plain": return style;
10800
+ case "heading": return {
10801
+ ...style,
10802
+ typography: "heading",
10803
+ fontWeight: "medium",
10804
+ letterSpacing: "wide"
10805
+ };
10806
+ case "quiet-caps": return {
10807
+ ...style,
10808
+ textTransform: "uppercase",
10809
+ textSize: "sm",
10810
+ letterSpacing: "wide"
10811
+ };
10812
+ case "brand-caps": return {
10813
+ ...style,
10814
+ textTransform: "uppercase",
10815
+ textSize: "sm",
10816
+ letterSpacing: "wider"
10817
+ };
10818
+ default: return assertNever$2(typography, "Unhandled header nav typography");
10819
+ }
10820
+ }
10821
+ function underlineNavLinkStyle(colorToken, underlineColorToken, typography = "plain") {
10822
+ return applyNavTypography({
10823
+ name: "Underline grow",
10824
+ typography: "body",
10825
+ fontWeight: "medium",
10826
+ textTransform: "none",
10827
+ italic: false,
10828
+ colorToken,
10829
+ padding: "compact",
10830
+ paddingX: "compact",
10831
+ borderRadius: "none",
10832
+ effects: { hover: [{
10833
+ effectId: "nav-underline",
10834
+ options: {
10835
+ style: "grow",
10836
+ ...underlineColorToken ? { colorToken: underlineColorToken } : {}
10837
+ }
10838
+ }] }
10839
+ }, typography);
10840
+ }
10587
10841
  function themedHeaderShadow(theme) {
10588
10842
  switch (theme.shadow.elevation) {
10589
10843
  case "none": return;
@@ -10683,6 +10937,38 @@ function cleanBaseHeader(theme) {
10683
10937
  ctaGap: "default"
10684
10938
  };
10685
10939
  }
10940
+ function classicCenteredNavHeader(theme) {
10941
+ return {
10942
+ ...baseHeader(theme),
10943
+ variant: "classic",
10944
+ navPlacement: "center",
10945
+ positioning: "sticky",
10946
+ shrinkOnScroll: false,
10947
+ background: {
10948
+ type: "color",
10949
+ color: "surface"
10950
+ },
10951
+ textColor: "text",
10952
+ navColor: "text",
10953
+ navStyle: "underline-grow",
10954
+ navWeight: "medium",
10955
+ navLinkStyle: underlineNavLinkStyle("text", "accent"),
10956
+ border: {
10957
+ style: "solid",
10958
+ width: "thin",
10959
+ position: "bottom",
10960
+ colorToken: "border"
10961
+ },
10962
+ ctaTreatment: "primary",
10963
+ ctaVariant: resolveHeaderCtaVariant(theme, "primary"),
10964
+ ctaGap: "default",
10965
+ logoText: {
10966
+ mobileMaxWidth: "sm",
10967
+ mobileWrapLines: 1,
10968
+ hideOnShrink: false
10969
+ }
10970
+ };
10971
+ }
10686
10972
  function serifLineHeader(theme) {
10687
10973
  return {
10688
10974
  ...baseHeader(theme),
@@ -10847,6 +11133,21 @@ const headerLookCatalog = defineExactCatalog()([
10847
11133
  logoAccommodations: inlineClassicLogoAccommodations,
10848
11134
  compile: cleanBaseHeader
10849
11135
  },
11136
+ {
11137
+ id: asHeaderLookId("classic-centered-nav"),
11138
+ label: "Centered nav",
11139
+ visualTreatment: "solid-neutral",
11140
+ layoutBehavior: "sticky",
11141
+ configuration: {
11142
+ structure: "inline-classic",
11143
+ treatment: "solid-neutral",
11144
+ behavior: "sticky"
11145
+ },
11146
+ openingRequirement: "none",
11147
+ maxRecommendedNavItems: 6,
11148
+ logoAccommodations: inlineClassicLogoAccommodations,
11149
+ compile: classicCenteredNavHeader
11150
+ },
10850
11151
  {
10851
11152
  id: asHeaderLookId("brand-solid"),
10852
11153
  label: "Brand solid",
@@ -12013,6 +12314,74 @@ const typographyPresets = [
12013
12314
  }
12014
12315
  }
12015
12316
  }
12317
+ }),
12318
+ defineTypographyPreset({
12319
+ id: "typography-preset:fulcrum",
12320
+ name: "Fulcrum",
12321
+ description: "Newsreader serif headings over an IBM Plex Sans body, with an IBM Plex Mono eyebrow role for uppercase kickers and mono captions.",
12322
+ defaultFonts: {
12323
+ body: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
12324
+ heading: "Newsreader, Georgia, \"Times New Roman\", serif"
12325
+ },
12326
+ recommendedFonts: {
12327
+ body: [
12328
+ "IBM Plex Sans",
12329
+ "Source Sans 3",
12330
+ "Inter"
12331
+ ],
12332
+ heading: [
12333
+ "Newsreader",
12334
+ "Source Serif 4",
12335
+ "Lora"
12336
+ ]
12337
+ },
12338
+ slotPolicies: {
12339
+ body: bodyReadablePolicy,
12340
+ heading: displayHeadingPolicy
12341
+ },
12342
+ typography: {
12343
+ body: {
12344
+ family: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
12345
+ size: "lg",
12346
+ weight: "regular",
12347
+ lineHeight: "relaxed",
12348
+ letterSpacing: "normal"
12349
+ },
12350
+ headings: {
12351
+ default: {
12352
+ family: "Newsreader, Georgia, \"Times New Roman\", serif",
12353
+ weight: "medium",
12354
+ lineHeight: "normal",
12355
+ letterSpacing: "tight",
12356
+ colorToken: "primary",
12357
+ italic: null,
12358
+ case: null
12359
+ },
12360
+ h1: {
12361
+ size: "2xl",
12362
+ weight: "medium",
12363
+ lineHeight: "normal",
12364
+ letterSpacing: "tight",
12365
+ italic: null
12366
+ },
12367
+ h2: {
12368
+ size: "xl",
12369
+ weight: "medium",
12370
+ lineHeight: "normal",
12371
+ letterSpacing: "tight",
12372
+ italic: null
12373
+ },
12374
+ h3: {
12375
+ size: "md",
12376
+ weight: "semibold",
12377
+ lineHeight: "normal",
12378
+ letterSpacing: "normal",
12379
+ italic: null
12380
+ }
12381
+ },
12382
+ scale: "spacious",
12383
+ eyebrow: { family: "IBM Plex Mono, ui-monospace, \"SF Mono\", monospace" }
12384
+ }
12016
12385
  })
12017
12386
  ];
12018
12387
  new Map(typographyPresets.map((preset) => [preset.id, preset]));
@@ -13758,7 +14127,53 @@ function generateDefaultButtonSystem(theme) {
13758
14127
  };
13759
14128
  }
13760
14129
  //#endregion
14130
+ //#region ../theme-core/src/buttons/affordanceIcons.ts
14131
+ /**
14132
+ * Affordance icon path data for CSS-emitted button affordances.
14133
+ *
14134
+ * A button variant may declare a trailing `affordance` icon (see
14135
+ * `ButtonVariantAffordance`). The generated button CSS renders that icon as a
14136
+ * masked `::after` pseudo-element, so the personality supplies it to every
14137
+ * button of the variant without per-node authoring.
14138
+ *
14139
+ * Icons live in the `@riverbankcms/blocks` registry, but the button CSS is
14140
+ * generated here in theme-core, so this module carries a small, curated copy of
14141
+ * the *affordance-eligible* icon path data (arrows/chevrons — the action-oriented
14142
+ * subset). The `d` values below are duplicated verbatim from the matching entries
14143
+ * in `blocks/src/system/icons/svg.tsx`; ids match the registry ids exactly so a
14144
+ * node-level `trailingIcon` and a personality `affordance` share one id space.
14145
+ *
14146
+ * Keep this set intentionally tiny. It is not the full CTA icon set — only the
14147
+ * shapes that read as a trailing affordance.
14148
+ */
14149
+ /** Registry icon id → the SVG path `d` fragments composing that icon. */
14150
+ const AFFORDANCE_ICON_PATHS = {
14151
+ ArrowRight: ["M5 12h14", "m13 6 6 6-6 6"],
14152
+ ArrowLeft: ["M19 12H5", "m11 6-6 6 6 6"],
14153
+ ChevronRight: ["m9 18 6-6-6-6"],
14154
+ ChevronLeft: ["m15 18-6-6 6-6"],
14155
+ ChevronDown: ["m6 9 6 6 6-6"],
14156
+ ArrowUpRight: ["M7 17 17 7", "M7 7h10v10"]
14157
+ };
14158
+ /**
14159
+ * Build a `data:image/svg+xml,...` URL for an affordance icon, suitable for use
14160
+ * as a CSS `mask-image`. The stroke is painted solid black so the mask's alpha
14161
+ * traces the icon outline; the visible colour is supplied separately by the
14162
+ * `::after` rule's `background-color`. Returns `null` for unknown ids.
14163
+ */
14164
+ function affordanceIconMaskUrl(iconId) {
14165
+ const paths = AFFORDANCE_ICON_PATHS[iconId];
14166
+ if (!paths || paths.length === 0) return null;
14167
+ return `data:image/svg+xml,${`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'>${paths.map((d) => `<path d='${d}'/>`).join("")}</svg>`.replace(/"/g, "'").replace(/#/g, "%23").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/\s+/g, " ")}`;
14168
+ }
14169
+ //#endregion
13761
14170
  //#region ../theme-core/src/buttons/generateButtonCss.ts
14171
+ /**
14172
+ * Class the `button` runtime node adds when it renders its own trailing icon,
14173
+ * so a node-level icon suppresses the personality's CSS `::after` affordance
14174
+ * (node overrides personality). Shared so generator and renderer can't drift.
14175
+ */
14176
+ const AFFORDANCE_SUPPRESS_CLASS = "rb-affordance-off";
13762
14177
  const BUTTON_SIZE_NAMES = buttonSizeNameSchema.options;
13763
14178
  /**
13764
14179
  * Letter spacing token → CSS value.
@@ -13853,10 +14268,40 @@ function variantChunks(variant, global, themeSizes, themeId, tokens, theme) {
13853
14268
  out.push(variantFocusVisibleRule(variantId, themeId));
13854
14269
  const effects = variantEffectsCss(variant, themeId, tokens, theme);
13855
14270
  if (effects) out.push({ raw: effects });
14271
+ const affordance = variantAffordanceRule(variant, selector, tokens);
14272
+ if (affordance) out.push(affordance);
13856
14273
  out.push(...variantDisabledRules(variantId, themeId));
13857
14274
  out.push(...variantSizeRules(variant, variantId, themeSizes, themeId));
13858
14275
  return out;
13859
14276
  }
14277
+ /**
14278
+ * Emit the optional trailing affordance icon as a masked `::after`.
14279
+ *
14280
+ * Only variants that declare `affordance` with a known icon id produce a rule —
14281
+ * every other variant is byte-identical to before. The `:not(.rb-affordance-off)`
14282
+ * guard lets a `button` node that renders its own trailing icon suppress this
14283
+ * one (node overrides personality). Colour comes from the affordance token, or
14284
+ * `currentColor` (the button text colour) when no token is given.
14285
+ */
14286
+ function variantAffordanceRule(variant, selector, tokens) {
14287
+ const affordance = variant.affordance;
14288
+ if (!affordance) return null;
14289
+ const maskUrl = affordanceIconMaskUrl(affordance.iconId);
14290
+ if (!maskUrl) return null;
14291
+ const color = affordance.colorToken ? tokens.getColor(affordance.colorToken) : "currentColor";
14292
+ const mask = `url("${maskUrl}") no-repeat center / contain`;
14293
+ return rule(`${selector}:not(.${AFFORDANCE_SUPPRESS_CLASS})::after`, [
14294
+ ["content", "\"\""],
14295
+ ["display", "inline-block"],
14296
+ ["width", "1em"],
14297
+ ["height", "1em"],
14298
+ ["margin-inline-start", "0.5em"],
14299
+ ["flex", "0 0 auto"],
14300
+ ["background-color", color],
14301
+ ["-webkit-mask", mask],
14302
+ ["mask", mask]
14303
+ ]);
14304
+ }
13860
14305
  function parseButtonVariantIdForCss(value) {
13861
14306
  const variantId = parseThemeButtonVariantId(value);
13862
14307
  if (!variantId) throw new Error("Button variant id must be a non-empty string");
@@ -14238,18 +14683,65 @@ function generateDefaultCardSystem(theme) {
14238
14683
  return { variants: [defaultVariant, alternativeVariant] };
14239
14684
  }
14240
14685
  //#endregion
14241
- //#region ../theme-core/src/status/contextualColors.ts
14242
- const STATUS_MUTED_COLOR_VAR = "--rb-status-muted-color";
14243
- const DEFAULT_STATUS_MUTED_COLOR = "rgb(var(--tb-mutedText))";
14244
- const SURFACE_MUTED_STATUS_COLOR = "color-mix(in oklab, currentColor, transparent 18%)";
14245
- function statusMutedColorValue() {
14246
- return `var(${STATUS_MUTED_COLOR_VAR}, ${DEFAULT_STATUS_MUTED_COLOR})`;
14686
+ //#region ../theme-core/src/surfaces/foregroundRoles.ts
14687
+ const SURFACE_FOREGROUND_ROLES = [
14688
+ "text",
14689
+ "heading",
14690
+ "link",
14691
+ "supporting"
14692
+ ];
14693
+ //#endregion
14694
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
14695
+ const SURFACE_FOREGROUND_CSS_VARS = {
14696
+ text: "--rb-surface-text-color",
14697
+ heading: "--rb-surface-heading-color",
14698
+ link: "--rb-surface-link-color",
14699
+ supporting: "--rb-surface-supporting-color"
14700
+ };
14701
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
14702
+ const DEFAULT_ROLE_FALLBACKS = {
14703
+ text: DEFAULT_SURFACE_TEXT_COLOR,
14704
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
14705
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
14706
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
14707
+ };
14708
+ function requireSurfaceColorValue(role, value) {
14709
+ const normalized = value.trim();
14710
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
14711
+ return normalized;
14712
+ }
14713
+ function completeSurfaceForegrounds(input) {
14714
+ const text = requireSurfaceColorValue("text", input.text);
14715
+ return {
14716
+ text,
14717
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
14718
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
14719
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
14720
+ };
14247
14721
  }
14248
- function statusMutedColorDeclaration() {
14249
- return `color: ${statusMutedColorValue()};`;
14722
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
14723
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
14724
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
14725
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
14726
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
14727
+ }
14728
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
14729
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
14730
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
14731
+ }
14732
+ function surfaceForegroundStyle(input) {
14733
+ const foregrounds = completeSurfaceForegrounds(input);
14734
+ return {
14735
+ color: foregrounds.text,
14736
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
14737
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
14738
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
14739
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
14740
+ };
14250
14741
  }
14251
- function contextualMutedStatusColorDeclaration(value) {
14252
- return `${STATUS_MUTED_COLOR_VAR}: ${value}`;
14742
+ function surfaceForegroundDeclarations(input) {
14743
+ const foregrounds = completeSurfaceForegrounds(input);
14744
+ return [`color: ${foregrounds.text}`, ...SURFACE_FOREGROUND_ROLES.map((role) => `${SURFACE_FOREGROUND_CSS_VARS[role]}: ${foregrounds[role]}`)];
14253
14745
  }
14254
14746
  //#endregion
14255
14747
  //#region ../theme-core/src/cards/generateCardCss.ts
@@ -14336,8 +14828,10 @@ function generateVariantCSS(variant, themeId, tokens, theme) {
14336
14828
  const direction = GRADIENT_DIRECTION_MAP[variant.background.direction] || "to right";
14337
14829
  rules.push(`background: linear-gradient(${direction}, ${fromColor}, ${toColor})`);
14338
14830
  } else rules.push(`background: transparent`);
14339
- if (variant.foregroundToken) rules.push(`color: ${tokens.getColor(variant.foregroundToken)}`);
14340
- rules.push(contextualMutedStatusColorDeclaration(SURFACE_MUTED_STATUS_COLOR));
14831
+ if (variant.background.type !== "transparent") {
14832
+ const foreground = tokens.getColor(variant.foregroundToken ?? "text");
14833
+ rules.push(...surfaceForegroundDeclarations({ text: foreground }));
14834
+ }
14341
14835
  if (variant.accentEdge) {
14342
14836
  const side = ACCENT_EDGE_SIDE_MAP[variant.accentEdge.edge];
14343
14837
  const width = ACCENT_EDGE_WIDTH_MAP[variant.accentEdge.width];
@@ -14698,11 +15192,31 @@ const gradientStopSchema = z$1.object({
14698
15192
  position: z$1.number().min(0).max(100)
14699
15193
  });
14700
15194
  /**
14701
- * Gradient configuration
15195
+ * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
15196
+ * so existing radial gradients are byte-identical.
15197
+ *
15198
+ * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
15199
+ * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
15200
+ */
15201
+ const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
15202
+ /**
15203
+ * Gradient configuration.
15204
+ *
15205
+ * `type` is optional and defaults to `'linear'` so gradients persisted without a
15206
+ * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
15207
+ * `'conic'` are additive kinds:
15208
+ * - linear: `angle` (degrees)
15209
+ * - radial: optional `shape` (circle/ellipse) + `position`
15210
+ * - conic: optional `angle` (from-angle, degrees) + `position`
14702
15211
  */
14703
15212
  const gradientConfigSchema = z$1.object({
14704
- type: z$1.enum(["linear", "radial"]),
15213
+ type: z$1.enum([
15214
+ "linear",
15215
+ "radial",
15216
+ "conic"
15217
+ ]).default("linear"),
14705
15218
  angle: z$1.number().min(0).max(360).optional(),
15219
+ shape: radialGradientShapeSchema.optional(),
14706
15220
  position: z$1.string().optional(),
14707
15221
  stops: z$1.array(gradientStopSchema).min(2)
14708
15222
  });
@@ -15241,9 +15755,17 @@ function parseToken(source) {
15241
15755
  if (parts.kind === "invalid") return { token: source };
15242
15756
  return {
15243
15757
  token: parts.token,
15244
- alpha: parts.opacityPercent === null ? void 0 : parts.opacityPercent / 100
15758
+ opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
15245
15759
  };
15246
15760
  }
15761
+ const SURFACE_TEXT_ROLE_BY_TOKEN = {
15762
+ text: "text",
15763
+ heading: "heading",
15764
+ mutedText: "supporting"
15765
+ };
15766
+ function surfaceRoleForTextToken(token) {
15767
+ return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
15768
+ }
15247
15769
  /**
15248
15770
  * Converts a theme color token to a CSS rgb value.
15249
15771
  * This is the primary function for applying theme colors to CSS properties.
@@ -15257,11 +15779,26 @@ function parseToken(source) {
15257
15779
  * rgbColorValue('raw:transparent') // => 'transparent'
15258
15780
  */
15259
15781
  function rgbColorValue(token) {
15260
- const { token: baseToken, alpha, raw } = parseToken(token);
15782
+ const { token: baseToken, opacityPercent, raw } = parseToken(token);
15261
15783
  if (raw) return raw;
15262
15784
  const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
15263
- if (alpha === void 0) return `rgb(var(${cssVar}))`;
15264
- return `rgb(var(${cssVar}) / ${alpha})`;
15785
+ if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
15786
+ return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
15787
+ }
15788
+ /**
15789
+ * Converts a theme color token to a CSS variable reference.
15790
+ * Note: This returns the base variable without rgb() wrapper, ignoring opacity.
15791
+ *
15792
+ * @param token - Theme color token
15793
+ * @returns CSS variable reference
15794
+ *
15795
+ * @example
15796
+ * cssVarValue('primary') // => 'var(--tb-primary)'
15797
+ * cssVarValue('surface/95') // => 'var(--tb-surface)' (opacity ignored)
15798
+ */
15799
+ function cssVarValue(token) {
15800
+ const { token: baseToken } = parseToken(token);
15801
+ return `var(${COLOR_VAR_PREFIX}${baseToken})`;
15265
15802
  }
15266
15803
  /**
15267
15804
  * Creates a backgroundColor style object from a theme token.
@@ -15279,6 +15816,9 @@ function backgroundColorStyle(token) {
15279
15816
  * @returns CSSProperties with color
15280
15817
  */
15281
15818
  function textColorStyle(token) {
15819
+ const parsed = parseToken(token);
15820
+ const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
15821
+ if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
15282
15822
  return { color: rgbColorValue(token) };
15283
15823
  }
15284
15824
  /**
@@ -15685,6 +16225,7 @@ const typographyScaleSchema = z$1.enum([
15685
16225
  "majorThird",
15686
16226
  "perfectFourth"
15687
16227
  ]);
16228
+ const eyebrowTypographyStyleSchema = z$1.object({ family: z$1.string().min(1) });
15688
16229
  const typographySchema = z$1.object({
15689
16230
  body: bodyTypographyStyleSchema,
15690
16231
  headings: z$1.object({
@@ -15697,7 +16238,8 @@ const typographySchema = z$1.object({
15697
16238
  h6: headingTypographyOverridesSchema.optional()
15698
16239
  }),
15699
16240
  scale: typographyScaleSchema,
15700
- proseLinkStyle: proseLinkStyleSchema.optional()
16241
+ proseLinkStyle: proseLinkStyleSchema.optional(),
16242
+ eyebrow: eyebrowTypographyStyleSchema.optional()
15701
16243
  });
15702
16244
  const spaceSchema = z$1.enum([
15703
16245
  "comfortable",
@@ -15722,7 +16264,8 @@ const shadow = z$1.object({
15722
16264
  "soft",
15723
16265
  "hard"
15724
16266
  ]).nullable(),
15725
- position: z$1.enum(["bottom", "bottom-right"]).default("bottom")
16267
+ position: z$1.enum(["bottom", "bottom-right"]).default("bottom"),
16268
+ colorToken: themeColorTokenSchema.optional()
15726
16269
  });
15727
16270
  const border = z$1.object({
15728
16271
  width: z$1.enum([
@@ -15967,6 +16510,7 @@ const headerSchema = z$1.object({
15967
16510
  navWeight: navFontWeight.default("medium"),
15968
16511
  navTypography: z$1.enum(["body", "heading"]).optional(),
15969
16512
  navTextSize: navLinkStyleSchema.shape.textSize,
16513
+ navPlacement: z$1.enum(["end", "center"]).optional(),
15970
16514
  container: headerContainerSchema,
15971
16515
  border: headerBorderSchema,
15972
16516
  shadow: componentShadowSchema.optional(),
@@ -16460,7 +17004,8 @@ const curatedSiteStyleIdValues = [
16460
17004
  "site-style:bold-launch",
16461
17005
  "site-style:south-west-twerk-school",
16462
17006
  "site-style:kirtan-ornate",
16463
- "site-style:original-joy"
17007
+ "site-style:original-joy",
17008
+ "site-style:fulcrum"
16464
17009
  ];
16465
17010
  const quietBudget = {
16466
17011
  accentDensity: "low",
@@ -17914,6 +18459,104 @@ const curatedSiteStyles = [
17914
18459
  buttonChoice("showtime-pill")
17915
18460
  ],
17916
18461
  paletteRecommendations: [paletteRecommendation("warm-dark-ember")]
18462
+ }),
18463
+ defineCuratedSiteStyle({
18464
+ id: "site-style:fulcrum",
18465
+ name: "Fulcrum",
18466
+ headingDecoration: {
18467
+ family: "short-center-rule",
18468
+ colorToken: "accent"
18469
+ },
18470
+ description: "Spruce-ink and moss with an amber climax, Newsreader serif over IBM Plex Sans, hairline sage borders and flat buttons — a grounded, editorial physiotherapy skin.",
18471
+ generationBrief: "Use a calm, clinician-led voice: precise, reassuring and specific rather than clinical-cold. Lead with the assessment-treatment-rehab journey, mono uppercase eyebrows for practical labels (hours, registrations, appointment windows), Newsreader serif headings over IBM Plex Sans body, and a single amber call to action as the page climax. Keep proof concrete (registrations, same-week appointments, return-to-activity outcomes) and avoid spa or hard-sell wellness language or medical claims.",
18472
+ siteCreatorSelection: {
18473
+ kind: "site_creator_keyword_heuristic",
18474
+ priority: 28
18475
+ },
18476
+ selectionKeywordRules: [
18477
+ {
18478
+ kind: "substring",
18479
+ value: "physiotherap"
18480
+ },
18481
+ {
18482
+ kind: "word",
18483
+ value: "physio"
18484
+ },
18485
+ {
18486
+ kind: "phrase",
18487
+ value: "sports injury"
18488
+ },
18489
+ {
18490
+ kind: "phrase",
18491
+ value: "rehab clinic"
18492
+ },
18493
+ {
18494
+ kind: "substring",
18495
+ value: "musculoskeletal"
18496
+ },
18497
+ {
18498
+ kind: "phrase",
18499
+ value: "sports therapy"
18500
+ }
18501
+ ],
18502
+ tokenRecipes: {
18503
+ palette: "brand-led",
18504
+ contrast: "standard",
18505
+ radius: "rounded",
18506
+ shadow: "subtle",
18507
+ typography: "serif-editorial",
18508
+ spacing: "airy",
18509
+ motion: "subtle"
18510
+ },
18511
+ compositionBudget: quietBudget,
18512
+ typographyPresetChoices: [
18513
+ typographyChoice("typography-preset:fulcrum"),
18514
+ typographyChoice("typography-preset:editorial-serif"),
18515
+ typographyChoice("typography-preset:modern-sans")
18516
+ ],
18517
+ defaultTypographyPresetId: "typography-preset:fulcrum",
18518
+ themeOverrides: {
18519
+ shadow: { colorToken: "primary" },
18520
+ accordions: {
18521
+ background: null,
18522
+ spacing: "comfortable",
18523
+ corners: "square",
18524
+ border: null,
18525
+ shadow: null,
18526
+ icon: {
18527
+ style: "plus-minus",
18528
+ position: "right",
18529
+ size: "md",
18530
+ colorToken: "accent"
18531
+ },
18532
+ trigger: {
18533
+ textColorToken: "primary",
18534
+ fontWeight: "medium",
18535
+ fontFamily: "heading",
18536
+ fontScale: "md"
18537
+ },
18538
+ hover: { textColorToken: "secondary" },
18539
+ content: { textColorToken: "muted" }
18540
+ },
18541
+ testimonialQuote: serifDisplayQuote
18542
+ },
18543
+ recommendedHeaderLookIds: [
18544
+ "classic-centered-nav",
18545
+ "serif-line",
18546
+ "centered-calm",
18547
+ "clean-base"
18548
+ ],
18549
+ recommendedFooterLookIds: ["footer-editorial-columns", "footer-muted-grounding"],
18550
+ buttonPersonalityChoices: [
18551
+ buttonChoice("flat-ink"),
18552
+ buttonChoice("confident-chip"),
18553
+ buttonChoice("editorial-link")
18554
+ ],
18555
+ paletteRecommendations: [
18556
+ paletteRecommendation("brand-led-spruce"),
18557
+ paletteRecommendation("warm-neutral-forest"),
18558
+ paletteRecommendation("soft-natural-sage")
18559
+ ]
17917
18560
  })
17918
18561
  ];
17919
18562
  const curatedSiteStylesById = new Map(curatedSiteStyles.map((style) => [style.id, style]));
@@ -19966,7 +20609,7 @@ function resolveBackgroundColorValue(value, context) {
19966
20609
  return resolveThemeColorReferenceValue(value, { palette: context.theme?.palette });
19967
20610
  }
19968
20611
  //#endregion
19969
- //#region ../blocks/src/system/transforms/registry/_utils/sectionForegroundStyles.ts
20612
+ //#region ../blocks/src/system/transforms/registry/_utils/surfaceForegroundStyles.ts
19970
20613
  function readColorToken(input, key) {
19971
20614
  const value = input[key];
19972
20615
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
@@ -19975,18 +20618,17 @@ function resolveOptionalForegroundColor(value, context) {
19975
20618
  if (!value) return null;
19976
20619
  return resolveBackgroundColorValue(value, context);
19977
20620
  }
19978
- function resolveSectionForegroundStyles(value, context) {
20621
+ function resolveSurfaceForegroundStyles(value, context) {
19979
20622
  if (!value || typeof value !== "object") return {};
19980
20623
  const input = value;
19981
20624
  const resolvedText = resolveOptionalForegroundColor(readColorToken(input, "textColor"), context);
19982
20625
  if (!resolvedText) return {};
19983
- return {
19984
- color: resolvedText,
19985
- "--section-text-color": resolvedText,
19986
- "--section-heading-color": resolveOptionalForegroundColor(readColorToken(input, "headingColor"), context) ?? resolvedText,
19987
- "--section-link-color": resolveOptionalForegroundColor(readColorToken(input, "linkColor"), context) ?? resolvedText,
19988
- "--section-supporting-color": resolveOptionalForegroundColor(readColorToken(input, "supportingColor"), context) ?? resolvedText
19989
- };
20626
+ return { ...surfaceForegroundStyle({
20627
+ text: resolvedText,
20628
+ heading: resolveOptionalForegroundColor(readColorToken(input, "headingColor"), context) ?? void 0,
20629
+ link: resolveOptionalForegroundColor(readColorToken(input, "linkColor"), context) ?? void 0,
20630
+ supporting: resolveOptionalForegroundColor(readColorToken(input, "supportingColor"), context) ?? void 0
20631
+ }) };
19990
20632
  }
19991
20633
  //#endregion
19992
20634
  //#region ../blocks/src/system/transforms/registry/boxStyles.ts
@@ -20324,67 +20966,12 @@ const containerStylesResolveBoxClassName = {
20324
20966
  }
20325
20967
  };
20326
20968
  /**
20327
- * Resolves section text color style object.
20328
- *
20329
- * The value should be the `_sectionStyles.background.textColor` token.
20330
- * Returns an empty object when unset so the section inherits text color.
20331
- *
20332
- * Outputs both `color` and `--section-text-color` CSS variable so that
20333
- * headings (which have explicit color rules in typography CSS) can inherit
20334
- * the section's text color via the CSS variable cascade.
20335
- */
20336
- const sectionTextColorStyleTransform = {
20337
- id: "sectionStyles.resolveTextColorStyle",
20338
- kind: "formatter",
20339
- summary: "Resolve section text color token into a style object",
20340
- run: (value, options, context) => {
20341
- const fallbackToken = options?.fallbackToken;
20342
- if (typeof value === "string" && value.trim()) {
20343
- const colorValue = resolveBackgroundColorValue(value.trim(), context);
20344
- return {
20345
- color: colorValue,
20346
- "--section-text-color": colorValue
20347
- };
20348
- }
20349
- if (typeof fallbackToken === "string" && fallbackToken.trim()) {
20350
- const colorValue = resolveBackgroundColorValue(fallbackToken.trim(), context);
20351
- return {
20352
- color: colorValue,
20353
- "--section-text-color": colorValue
20354
- };
20355
- }
20356
- return {};
20357
- }
20358
- };
20359
- /**
20360
- * Resolves section heading color style object.
20361
- *
20362
- * The value should be the `_sectionStyles.background.headingColor` token.
20363
- * Returns an empty object when unset so headings inherit from textColor or default.
20364
- *
20365
- * Outputs `--section-heading-color` CSS variable which typography CSS can use
20366
- * to style headings differently from body text within a section.
20367
- */
20368
- const sectionHeadingColorStyleTransform = {
20369
- id: "sectionStyles.resolveHeadingColorStyle",
20370
- kind: "formatter",
20371
- summary: "Resolve section heading color token into a style object",
20372
- run: (value, options, context) => {
20373
- const fallbackToken = options?.fallbackToken;
20374
- if (typeof value === "string" && value.trim()) return { "--section-heading-color": resolveBackgroundColorValue(value.trim(), context) };
20375
- if (typeof fallbackToken === "string" && fallbackToken.trim()) return { "--section-heading-color": resolveBackgroundColorValue(fallbackToken.trim(), context) };
20376
- return {};
20377
- }
20378
- };
20379
- /**
20380
- * Resolves section foreground colors from the background object.
20969
+ * Resolves surface foreground colors from the background object.
20381
20970
  *
20382
20971
  * The value should be the full `_sectionStyles.background` object.
20383
20972
  * Outputs:
20384
- * - `color` and `--section-text-color` from background.textColor
20385
- * - `--section-heading-color` from background.headingColor (falls back to textColor if not set)
20386
- * - `--section-link-color` from background.linkColor (falls back to textColor if not set)
20387
- * - `--section-supporting-color` from background.supportingColor (falls back to textColor if not set)
20973
+ * - `color` and the complete `--rb-surface-*-color` contract
20974
+ * - optional roles fall back atomically to background.textColor
20388
20975
  *
20389
20976
  * This combined transform replaces separate foreground color transforms
20390
20977
  * when both need to be applied to the same style prop.
@@ -20392,8 +20979,8 @@ const sectionHeadingColorStyleTransform = {
20392
20979
  const sectionColorsStyleTransform = {
20393
20980
  id: "sectionStyles.resolveColorsStyle",
20394
20981
  kind: "formatter",
20395
- summary: "Resolve section foreground colors into a style object",
20396
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
20982
+ summary: "Resolve surface foreground colors into a style object",
20983
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
20397
20984
  };
20398
20985
  /**
20399
20986
  * Maps semantic gap values to theme-aware gap classes.
@@ -20432,8 +21019,6 @@ const boxStylesTransforms = [
20432
21019
  sectionClassNameTransform,
20433
21020
  containerStylesResolveClassName,
20434
21021
  containerStylesResolveBoxClassName,
20435
- sectionTextColorStyleTransform,
20436
- sectionHeadingColorStyleTransform,
20437
21022
  sectionColorsStyleTransform,
20438
21023
  {
20439
21024
  id: "boxStyles.semanticGap",
@@ -21201,7 +21786,29 @@ const ctaRowFragment = defineFragment({
21201
21786
  fields: [createCtasRepeater({
21202
21787
  label: "Calls to action",
21203
21788
  itemLabel: "CTA",
21204
- maxItems: 3
21789
+ maxItems: 3,
21790
+ button: { variants: [
21791
+ {
21792
+ value: "primary",
21793
+ label: "Primary"
21794
+ },
21795
+ {
21796
+ value: "secondary",
21797
+ label: "Secondary"
21798
+ },
21799
+ {
21800
+ value: "outline",
21801
+ label: "Outline"
21802
+ },
21803
+ {
21804
+ value: "link",
21805
+ label: "Link"
21806
+ },
21807
+ {
21808
+ value: "accent",
21809
+ label: "Accent"
21810
+ }
21811
+ ] }
21205
21812
  })],
21206
21813
  layout: [ctaRow({
21207
21814
  collectionPath: "content.ctas",
@@ -21482,6 +22089,55 @@ function buildGridCardsLayout(className) {
21482
22089
  return el("div", { className }, buildResponsiveGridCards());
21483
22090
  }
21484
22091
  /**
22092
+ * Build a single "featured editorial" testimonial card.
22093
+ *
22094
+ * This card type deliberately renders NO avatar/headshot: an amber quote mark,
22095
+ * the quote body, then the author name and a context line. Colour + surface
22096
+ * treatment is driven entirely by `variantClass` (see testimonials.css):
22097
+ * - `rb-testimonials-fe-card--feature` → spruce-ink card (dark primary bg)
22098
+ * - `rb-testimonials-fe-card--plain` → white surface card
22099
+ *
22100
+ * Extra modifiers (`repeat`, `when`) are forwarded to the card node so the
22101
+ * caller can drive first-vs-rest branching.
22102
+ */
22103
+ function buildFeaturedEditorialCard(variantClass, ...mods) {
22104
+ const quoteMark = text({
22105
+ as: "span",
22106
+ className: "rb-testimonials-fe-qmark",
22107
+ "aria-hidden": "true"
22108
+ }, props({ value: "“" }));
22109
+ const quote = richText({ className: "rb-testimonials-fe-quote rb-max-w-none" }, bind("testimonial.content.body"), when("testimonial.content.body"));
22110
+ const meta = el("div", { className: "rb-testimonials-fe-meta" }, [text({
22111
+ as: "strong",
22112
+ className: "rb-testimonials-fe-name"
22113
+ }, bind("testimonial.content.name", { fallback: "Anonymous" })), text({
22114
+ as: "span",
22115
+ className: "rb-testimonials-fe-context"
22116
+ }, when("testimonial.content.jobTitle"), bind("testimonial.content.jobTitle"))]);
22117
+ return el("div", { className: `rb-testimonials-fe-card ${variantClass}` }, [
22118
+ quoteMark,
22119
+ quote,
22120
+ meta
22121
+ ], ...mods);
22122
+ }
22123
+ /**
22124
+ * Build the "featured editorial" layout: an asymmetric two-column arrangement
22125
+ * where the FIRST testimonial is a large featured (spruce-ink) card and the
22126
+ * remaining testimonials stack as white cards beside it.
22127
+ *
22128
+ * First-vs-rest branching is done without an offset primitive: the featured
22129
+ * card uses `repeat(..., { limit: 1 })` (entry 0 only), and the rest column
22130
+ * repeats over every entry but skips index 0 via `when("tIndex", { equals: 0,
22131
+ * not: true })`. The `$when` guard is re-evaluated per repeated item with the
22132
+ * index scope in context, so entry 0 produces no node in the rest column.
22133
+ */
22134
+ function buildFeaturedEditorialLayout() {
22135
+ return el("div", { className: "rb-testimonials-fe-grid" }, [buildFeaturedEditorialCard("rb-testimonials-fe-card--feature", repeat("data.entries", "testimonial", { limit: 1 })), el("div", { className: "rb-testimonials-fe-col" }, [buildFeaturedEditorialCard("rb-testimonials-fe-card--plain", repeat("data.entries", "testimonial", { indexName: "tIndex" }), when("tIndex", {
22136
+ equals: 0,
22137
+ not: true
22138
+ }))])]);
22139
+ }
22140
+ /**
21485
22141
  * Fragment definition for testimonials carousel.
21486
22142
  * Provides field definitions and data loader configuration.
21487
22143
  *
@@ -24780,7 +25436,7 @@ const defaultLayout = styledSection({
24780
25436
  children: styledContainer({
24781
25437
  baseClassName: "rb-relative rb-isolate",
24782
25438
  children: stack({
24783
- gap: "xl",
25439
+ gap: "lg",
24784
25440
  className: "rb-w-full rb-max-w-4xl rb-mx-auto"
24785
25441
  }, [headingLayout, accordionLayout])
24786
25442
  }),
@@ -24797,7 +25453,7 @@ const questionColumnsLayout = styledSection({
24797
25453
  children: styledContainer({
24798
25454
  baseClassName: "rb-relative rb-isolate",
24799
25455
  children: stack({
24800
- gap: "xl",
25456
+ gap: "lg",
24801
25457
  className: "rb-w-full"
24802
25458
  }, [headingLayout, questionColumnsAccordionLayout])
24803
25459
  }),
@@ -24962,7 +25618,26 @@ const formattingTransforms = [
24962
25618
  arrayLengthTransform,
24963
25619
  jsonArrayTransform,
24964
25620
  csvTransform,
24965
- marqueeLoopTransform
25621
+ marqueeLoopTransform,
25622
+ {
25623
+ id: "value.mapToken",
25624
+ kind: "formatter",
25625
+ summary: "Map a state/enum value to a theme token colour (rgb(var(--tb-<role>)))",
25626
+ schema: z$1.object({
25627
+ /**
25628
+ * Maps an input state/enum value (stringified) to a theme token ROLE name
25629
+ * (e.g. `secondary`, `accent`, `muted`, `border`). The role is wrapped to
25630
+ * `rgb(var(--tb-<role>))` so the result drops straight into a `style` binding.
25631
+ */
25632
+ cases: z$1.record(z$1.string(), z$1.string()),
25633
+ /** Role used when the value matches no case. Omit to yield `undefined`. */
25634
+ fallback: z$1.string().optional()
25635
+ }),
25636
+ run: (value, options) => {
25637
+ const role = options.cases[String(value)] ?? options.fallback;
25638
+ return role ? `rgb(var(--tb-${role}))` : void 0;
25639
+ }
25640
+ }
24966
25641
  ];
24967
25642
  //#endregion
24968
25643
  //#region ../blocks/src/system/transforms/registry/ui.ts
@@ -25606,6 +26281,26 @@ const headerLogoTextVisibleTransform = {
25606
26281
  return resolveHeaderBrandDisplay(asPartialObject(value)) !== "logo-only";
25607
26282
  }
25608
26283
  };
26284
+ /**
26285
+ * Selects which classic-layout nav row should render.
26286
+ *
26287
+ * - `'inline'` → classic variant with the historical logo-left / nav-right row
26288
+ * - `'centered'`→ classic variant with a centred (flex-1) nav and a right CTA
26289
+ * - `'other'` → not a classic variant (centered/floating/transparent handle it)
26290
+ *
26291
+ * Consumed by the site-header block's `$when` gates so the two classic rows are
26292
+ * mutually exclusive without introducing a new top-level header variant.
26293
+ */
26294
+ const classicNavModeTransform = {
26295
+ id: "layout.classicNavMode",
26296
+ kind: "formatter",
26297
+ summary: "Resolve which classic-layout nav row to render",
26298
+ run: (value) => {
26299
+ const header = asPartialObject(value);
26300
+ if (header?.variant !== "classic") return "other";
26301
+ return header?.navPlacement === "center" ? "centered" : "inline";
26302
+ }
26303
+ };
25609
26304
  const maxWidthClassTransform = {
25610
26305
  id: "layout.maxWidthClass",
25611
26306
  kind: "string",
@@ -25788,8 +26483,9 @@ const footerRootStyleTransform = {
25788
26483
  styleParts.push(backgroundColorStyle(token));
25789
26484
  }
25790
26485
  }
25791
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
25792
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
26486
+ const foreground = visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
26487
+ if (!visuals.isTransparent && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
26488
+ else if (foreground) styleParts.push({ color: foreground });
25793
26489
  styleParts.push(borderColorStyle("border"));
25794
26490
  return mergeStyles(...styleParts);
25795
26491
  }
@@ -25875,18 +26571,16 @@ const footerBottomBarStyleTransform = {
25875
26571
  const styleParts = [];
25876
26572
  const background = bottomBar?.background;
25877
26573
  if (background?.type === "color") styleParts.push(backgroundColorStyle(background.color ?? "surface"));
25878
- if (bottomBar?.textColor) styleParts.push(textColorStyle(bottomBar.textColor));
25879
26574
  const borderColorToken = bottomBar?.borderTop?.colorToken;
25880
26575
  if (borderColorToken) styleParts.push(borderColorStyle(borderColorToken));
25881
26576
  else styleParts.push(borderColorStyle("border"));
25882
26577
  const borderWidth = bottomBar?.borderTop?.width;
25883
26578
  if (borderWidth && borderWidth !== "none") styleParts.push({ borderTopWidth: borderWidth === "thin" ? "1px" : borderWidth === "medium" ? "2px" : "3px" });
25884
26579
  else if (borderWidth === "none") styleParts.push({ borderTopWidth: "0px" });
25885
- if (!bottomBar?.textColor) {
25886
- const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
25887
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
25888
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
25889
- }
26580
+ const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
26581
+ const foreground = bottomBar?.textColor ? rgbColorValue(bottomBar.textColor) : visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
26582
+ if (background?.type === "color" && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
26583
+ else if (foreground && bottomBar?.textColor) styleParts.push({ color: foreground });
25890
26584
  return mergeStyles(...styleParts);
25891
26585
  }
25892
26586
  };
@@ -26021,6 +26715,7 @@ const layoutTransforms = [
26021
26715
  headerLogoClassTransform,
26022
26716
  headerLogoMediaClassTransform,
26023
26717
  headerLogoTextVisibleTransform,
26718
+ classicNavModeTransform,
26024
26719
  footerRootClassTransform,
26025
26720
  headerRootStyleTransform,
26026
26721
  headerSectionBackgroundTransform,
@@ -26460,7 +27155,18 @@ const classicLayout = inline({
26460
27155
  logoRow,
26461
27156
  createNavRow(`rb-ml-auto ${desktopOnlyDisplay("flex")} rb-gap-6`),
26462
27157
  headerCta
26463
- ], when("$root.theme.header.variant", { equals: "classic" }));
27158
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "inline"));
27159
+ const classicCenteredNavLayout = inline({
27160
+ className: bindProp("$root.theme.header.maxWidth", {
27161
+ transforms: pipe(tx("layout.maxWidthClass", { base: `rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6` })),
27162
+ fallback: `rb-container rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6`
27163
+ }),
27164
+ align: "center"
27165
+ }, [
27166
+ logoRow,
27167
+ createNavRow(`rb-flex-1 rb-justify-center ${desktopOnlyDisplay("flex")} rb-gap-6`, "center"),
27168
+ headerCta
27169
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "centered"));
26464
27170
  const centeredLayout = stack({
26465
27171
  gap: "md",
26466
27172
  align: "center",
@@ -26535,6 +27241,7 @@ const siteHeaderManifest = createBlockManifest({
26535
27241
  }, [
26536
27242
  mobileHeaderBar,
26537
27243
  classicLayout,
27244
+ classicCenteredNavLayout,
26538
27245
  centeredLayout,
26539
27246
  transparentLayout,
26540
27247
  floatingLayout,
@@ -26744,7 +27451,7 @@ const footerLayout = section({
26744
27451
  style: { $bind: {
26745
27452
  from: "$root.theme.footer",
26746
27453
  transforms: [{ id: "layout.footerRootStyle" }],
26747
- fallback: mergeStyles(backgroundColorStyle("surface"), textColorStyle("text"), borderColorStyle("border"))
27454
+ fallback: mergeStyles(backgroundColorStyle("surface"), surfaceForegroundStyle({ text: "rgb(var(--tb-text))" }), borderColorStyle("border"))
26748
27455
  } }
26749
27456
  }, [
26750
27457
  simpleFooterLayout,
@@ -26799,7 +27506,7 @@ const backgroundImageClassName = "rb-absolute rb-inset-0 rb-z-neg-10 rb-h-full r
26799
27506
  */
26800
27507
  const wrapVariant$2 = (variantLayout) => styledSection({
26801
27508
  children: styledContainer({ children: stack({
26802
- gap: "xl",
27509
+ gap: "lg",
26803
27510
  className: "rb-relative"
26804
27511
  }, [...testimonialsHeadingFragment.layout, variantLayout]) }),
26805
27512
  baseClass: "",
@@ -26825,6 +27532,26 @@ const splitGridLayout = styledSection({
26825
27532
  spacing: "comfortable",
26826
27533
  imageClassName: backgroundImageClassName
26827
27534
  });
27535
+ const featuredEditorialLayout = styledSection({
27536
+ children: styledContainer({
27537
+ baseClassName: "rb-relative rb-isolate",
27538
+ children: stack({
27539
+ gap: "lg",
27540
+ className: "rb-relative"
27541
+ }, [el("div", { className: "rb-testimonials-fe-head" }, [text({
27542
+ as: "h2",
27543
+ className: "rb-text-left",
27544
+ headingDecoration: "off"
27545
+ }, when("content.heading"), bind("content.heading")), el("div", { className: "rb-testimonials-fe-leadin" }, [text({
27546
+ as: "p",
27547
+ className: "rb-prose",
27548
+ style: textColorStyle("mutedText")
27549
+ }, when("content.subheading"), bind("content.subheading"))])]), buildFeaturedEditorialLayout()])
27550
+ }),
27551
+ baseClass: "",
27552
+ spacing: "comfortable",
27553
+ imageClassName: backgroundImageClassName
27554
+ });
26828
27555
  const additionalFields = parseFieldDefinitions([
26829
27556
  ...testimonialsHeadingFragment.fields,
26830
27557
  {
@@ -26912,6 +27639,7 @@ const testimonialsBlockDefinition = {
26912
27639
  carousel: carouselLayout$1,
26913
27640
  grid: gridLayout$1,
26914
27641
  "split-grid": splitGridLayout,
27642
+ "featured-editorial": featuredEditorialLayout,
26915
27643
  highlight: highlightLayout
26916
27644
  },
26917
27645
  defaultVariant: "carousel",
@@ -26931,6 +27659,11 @@ const testimonialsBlockDefinition = {
26931
27659
  description: "Heading on the left with testimonial cards on the right",
26932
27660
  icon: "PanelLeft"
26933
27661
  },
27662
+ "featured-editorial": {
27663
+ label: "Featured editorial",
27664
+ description: "A large featured ink quote beside a column of stacked cards (no avatars)",
27665
+ icon: "Quote"
27666
+ },
26934
27667
  highlight: {
26935
27668
  label: "Highlight",
26936
27669
  description: "A single, centered testimonial with no carousel",
@@ -27444,7 +28177,7 @@ function grid$2(props, children) {
27444
28177
  const cardsLayout = styledSection({
27445
28178
  children: styledContainer({
27446
28179
  rootType: "stack",
27447
- rootProps: { gap: "xl" },
28180
+ rootProps: { gap: "lg" },
27448
28181
  children: [stack({
27449
28182
  gap: "sm",
27450
28183
  className: "rb-feature-cards-heading rb-mx-auto rb-max-w-3xl rb-text-center"
@@ -29429,8 +30162,11 @@ const recoverEmptyDescriptionWhenActive = {
29429
30162
  kind: "recover-empty-when-active",
29430
30163
  placeholder: "Add description"
29431
30164
  };
29432
- function eventSectionHeaderLayoutNodes() {
29433
- return [text({ as: "h2" }, bind("content.heading"), when("content.heading", { authoringPresence: recoverEmptyHeadingWhenActive })), richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description", { authoringPresence: recoverEmptyDescriptionWhenActive }))];
30165
+ function eventSectionHeaderLayoutNodes(options = {}) {
30166
+ const headingNode = text({ as: "h2" }, bind("content.heading"), when("content.heading", { authoringPresence: recoverEmptyHeadingWhenActive }));
30167
+ const descriptionNode = richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description", { authoringPresence: recoverEmptyDescriptionWhenActive }));
30168
+ if (options.splitClassName) return [el("div", { className: options.splitClassName }, [headingNode, descriptionNode])];
30169
+ return [headingNode, descriptionNode];
29434
30170
  }
29435
30171
  //#endregion
29436
30172
  //#region ../blocks/src/system/blocks/shop.shared.ts
@@ -29497,7 +30233,8 @@ const eventCalendarDisplayModeValues = [
29497
30233
  "combined",
29498
30234
  "calendar",
29499
30235
  "list",
29500
- "timetable"
30236
+ "timetable",
30237
+ "week-board"
29501
30238
  ];
29502
30239
  const eventCalendarDefaultViewValues = ["month", "week"];
29503
30240
  const eventCalendarListLayoutValues = [
@@ -31917,6 +32654,40 @@ function getEventAvailability(event) {
31917
32654
  isFull: false
31918
32655
  };
31919
32656
  }
32657
+ /**
32658
+ * Derive the three-state availability status (available / filling / waitlist)
32659
+ * plus a short pill label. Builds on {@link getEventAvailability} so the
32660
+ * capacity math (availableSpots with a `capacity - registeredCount` fallback)
32661
+ * stays consistent with the binary helper used elsewhere.
32662
+ */
32663
+ function getEventAvailabilityStatus(event, options) {
32664
+ const threshold = options?.fillingThreshold ?? 2;
32665
+ const { available, isFull } = getEventAvailability(event);
32666
+ if (available == null) return {
32667
+ status: "available",
32668
+ available: null,
32669
+ isFull: false,
32670
+ label: null
32671
+ };
32672
+ if (isFull) return {
32673
+ status: "waitlist",
32674
+ available: 0,
32675
+ isFull: true,
32676
+ label: event.waitlistEnabled ? "Waitlist" : "Full"
32677
+ };
32678
+ if (available <= threshold) return {
32679
+ status: "filling",
32680
+ available,
32681
+ isFull: false,
32682
+ label: `${available} left`
32683
+ };
32684
+ return {
32685
+ status: "available",
32686
+ available,
32687
+ isFull: false,
32688
+ label: `${available} places`
32689
+ };
32690
+ }
31920
32691
  //#endregion
31921
32692
  //#region ../blocks/src/system/runtime/nodes/events/shared/utils.ts
31922
32693
  /**
@@ -34422,7 +35193,7 @@ const eventCalendarLayout = [styledSection({
34422
35193
  children: styledContainer({
34423
35194
  rootType: "stack",
34424
35195
  rootProps: { gap: "lg" },
34425
- children: [...eventSectionHeaderLayoutNodes(), el("event-calendar", withApiBindings({
35196
+ children: [...eventSectionHeaderLayoutNodes({ splitClassName: "event-section-head event-section-head--split" }), el("event-calendar", withApiBindings({
34426
35197
  events: { $bind: { from: "data.events" } },
34427
35198
  stage: { $bind: {
34428
35199
  from: "$root.previewStage",
@@ -34533,6 +35304,10 @@ const eventCalendarAdditionalFields = [
34533
35304
  value: "timetable",
34534
35305
  label: "Timetable"
34535
35306
  },
35307
+ {
35308
+ value: "week-board",
35309
+ label: "Week board"
35310
+ },
34536
35311
  {
34537
35312
  value: "list",
34538
35313
  label: "List"
@@ -34715,7 +35490,8 @@ const eventCalendarAdditionalFields = [
34715
35490
  oneOf: [
34716
35491
  "combined",
34717
35492
  "calendar",
34718
- "timetable"
35493
+ "timetable",
35494
+ "week-board"
34719
35495
  ]
34720
35496
  }
34721
35497
  }
@@ -34818,7 +35594,8 @@ const eventCalendarAdditionalFields = [
34818
35594
  "combined",
34819
35595
  "calendar",
34820
35596
  "list",
34821
- "timetable"
35597
+ "timetable",
35598
+ "week-board"
34822
35599
  ]
34823
35600
  }
34824
35601
  }
@@ -34853,7 +35630,8 @@ const eventCalendarAdditionalFields = [
34853
35630
  "combined",
34854
35631
  "calendar",
34855
35632
  "list",
34856
- "timetable"
35633
+ "timetable",
35634
+ "week-board"
34857
35635
  ]
34858
35636
  }, {
34859
35637
  field: "showFilters",
@@ -34876,7 +35654,8 @@ const eventCalendarAdditionalFields = [
34876
35654
  "combined",
34877
35655
  "calendar",
34878
35656
  "list",
34879
- "timetable"
35657
+ "timetable",
35658
+ "week-board"
34880
35659
  ]
34881
35660
  }, {
34882
35661
  field: "showFilters",
@@ -34899,7 +35678,8 @@ const eventCalendarAdditionalFields = [
34899
35678
  "combined",
34900
35679
  "calendar",
34901
35680
  "list",
34902
- "timetable"
35681
+ "timetable",
35682
+ "week-board"
34903
35683
  ]
34904
35684
  }, {
34905
35685
  field: "showFilters",
@@ -34921,7 +35701,8 @@ const eventCalendarAdditionalFields = [
34921
35701
  oneOf: [
34922
35702
  "combined",
34923
35703
  "list",
34924
- "timetable"
35704
+ "timetable",
35705
+ "week-board"
34925
35706
  ]
34926
35707
  }, { allOf: [{
34927
35708
  field: "variant",
@@ -35097,6 +35878,7 @@ const eventCalendarVariants = {
35097
35878
  combined: eventCalendarLayout[0],
35098
35879
  calendar: eventCalendarLayout[0],
35099
35880
  timetable: eventCalendarLayout[0],
35881
+ "week-board": eventCalendarLayout[0],
35100
35882
  list: eventCalendarLayout[0]
35101
35883
  };
35102
35884
  const eventCalendarContentShape = {
@@ -35110,6 +35892,7 @@ const eventCalendarContentShape = {
35110
35892
  { value: "combined" },
35111
35893
  { value: "calendar" },
35112
35894
  { value: "timetable" },
35895
+ { value: "week-board" },
35113
35896
  { value: "list" }
35114
35897
  ]
35115
35898
  },
@@ -35176,6 +35959,7 @@ const eventCalendarBlockDefinition = {
35176
35959
  combined: { label: "Combined" },
35177
35960
  calendar: { label: "Calendar" },
35178
35961
  timetable: { label: "Timetable" },
35962
+ "week-board": { label: "Week board" },
35179
35963
  list: { label: "List" }
35180
35964
  }
35181
35965
  }, eventCalendarContentShape),
@@ -40679,6 +41463,12 @@ const systemBlockDefinitions = [
40679
41463
  family: "centered",
40680
41464
  weight: "featured",
40681
41465
  bias: "soft"
41466
+ }),
41467
+ layoutProfile({
41468
+ variantKey: "featured-editorial",
41469
+ family: "split",
41470
+ weight: "featured",
41471
+ bias: "soft"
40682
41472
  })
40683
41473
  ]
40684
41474
  })),
@@ -40811,6 +41601,11 @@ const systemBlockDefinitions = [
40811
41601
  variantKey: "timetable",
40812
41602
  family: "stack",
40813
41603
  weight: "standard"
41604
+ }),
41605
+ layoutProfile({
41606
+ variantKey: "week-board",
41607
+ family: "stack",
41608
+ weight: "standard"
40814
41609
  })
40815
41610
  ]
40816
41611
  })),
@@ -41168,7 +41963,15 @@ function gradientToCss(config, tokens) {
41168
41963
  const stops = config.stops.map((stop) => {
41169
41964
  return `${tokens[stop.color] ?? "#000000"} ${stop.position}%`;
41170
41965
  }).join(", ");
41171
- if (config.type === "radial") return `radial-gradient(circle at ${config.position ?? "center"}, ${stops})`;
41966
+ const type = config.type ?? "linear";
41967
+ if (type === "radial") {
41968
+ const position = config.position ?? "center";
41969
+ return `radial-gradient(${config.shape ?? "circle"} at ${position}, ${stops})`;
41970
+ }
41971
+ if (type === "conic") {
41972
+ const position = config.position ?? "center";
41973
+ return `conic-gradient(from ${config.angle ?? 0}deg at ${position}, ${stops})`;
41974
+ }
41172
41975
  return `linear-gradient(${config.angle ?? 180}deg, ${stops})`;
41173
41976
  }
41174
41977
  //#endregion
@@ -41246,8 +42049,8 @@ const backgroundTransforms = [
41246
42049
  {
41247
42050
  id: "background.resolveColorsStyle",
41248
42051
  kind: "formatter",
41249
- summary: "Resolve section foreground colors from a background object into scoped CSS vars",
41250
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
42052
+ summary: "Resolve surface foreground colors from a background object into scoped CSS vars",
42053
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
41251
42054
  },
41252
42055
  {
41253
42056
  id: "background.resolveAllowOverflow",
@@ -41869,21 +42672,53 @@ function Inline({ as: Comp = "div", gap = "md", align = "center", justify = "sta
41869
42672
  }
41870
42673
  //#endregion
41871
42674
  //#region ../blocks/src/primitives/layout/Grid.tsx
42675
+ /** Utility class for a numeric column COUNT at a given breakpoint. */
42676
+ const COL_COUNT_CLASS = {
42677
+ base: (n) => `rb-grid-cols-${n}`,
42678
+ sm: (n) => `rb-sm-grid-cols-${n}`,
42679
+ md: (n) => `rb-md-grid-cols-${n}`,
42680
+ lg: (n) => `rb-lg-grid-cols-${n}`,
42681
+ xl: (n) => `rb-xl-grid-cols-${n}`
42682
+ };
42683
+ /** Inline custom property read by the `.rb-grid-tracks` utility per breakpoint. */
42684
+ const TRACK_VAR = {
42685
+ base: "--rb-gtc-base",
42686
+ sm: "--rb-gtc-sm",
42687
+ md: "--rb-gtc-md",
42688
+ lg: "--rb-gtc-lg",
42689
+ xl: "--rb-gtc-xl"
42690
+ };
42691
+ const GRID_BREAKPOINTS = [
42692
+ "base",
42693
+ "sm",
42694
+ "md",
42695
+ "lg",
42696
+ "xl"
42697
+ ];
41872
42698
  function Grid({ as: Comp = "div", cols = {
41873
42699
  base: 1,
41874
42700
  md: 2
41875
- }, gap = "md", className, children, "data-slot": dataSlot, ...rest }) {
41876
- const colsObj = typeof cols === "number" ? { base: cols } : cols;
41877
- const colClasses = [
41878
- colsObj.base && `rb-grid-cols-${colsObj.base}`,
41879
- colsObj.sm && `rb-sm-grid-cols-${colsObj.sm}`,
41880
- colsObj.md && `rb-md-grid-cols-${colsObj.md}`,
41881
- colsObj.lg && `rb-lg-grid-cols-${colsObj.lg}`,
41882
- colsObj.xl && `rb-xl-grid-cols-${colsObj.xl}`
41883
- ];
42701
+ }, gap = "md", className, style, children, "data-slot": dataSlot, ...rest }) {
42702
+ const colsObj = typeof cols === "number" || typeof cols === "string" ? { base: cols } : cols;
42703
+ const colClasses = [];
42704
+ const trackVars = {};
42705
+ let hasTracks = false;
42706
+ for (const bp of GRID_BREAKPOINTS) {
42707
+ const value = colsObj[bp];
42708
+ if (value == null) continue;
42709
+ if (typeof value === "string") {
42710
+ trackVars[TRACK_VAR[bp]] = value;
42711
+ hasTracks = true;
42712
+ } else colClasses.push(COL_COUNT_CLASS[bp](value));
42713
+ }
42714
+ const mergedStyle = hasTracks ? {
42715
+ ...trackVars,
42716
+ ...style
42717
+ } : style;
41884
42718
  return /* @__PURE__ */ jsx(Comp, {
41885
42719
  ...rest,
41886
- className: clsx("rb-grid", colClasses, {
42720
+ style: mergedStyle,
42721
+ className: clsx("rb-grid", hasTracks && "rb-grid-tracks", colClasses, {
41887
42722
  "rb-gap-0": gap === "none",
41888
42723
  "rb-gap-1": gap === "xs",
41889
42724
  "rb-gap-2": gap === "sm",
@@ -42120,119 +42955,6 @@ function Text({ as: Comp = "span", children, className = "", path, maxLength: _m
42120
42955
  });
42121
42956
  }
42122
42957
  //#endregion
42123
- //#region ../blocks/src/system/runtime/nodes/richText-coerce.ts
42124
- function coerceRichTextDoc(value) {
42125
- const raw = unwrapRichTextRoot(value);
42126
- if (typeof raw === "string") return createRichTextDocFromMarkdown(raw);
42127
- if (isRecord$13(raw) && typeof raw.type === "string") return raw;
42128
- return null;
42129
- }
42130
- //#endregion
42131
- //#region ../blocks/src/system/runtime/nodes/renderInlineText.tsx
42132
- function renderInlineText(value) {
42133
- return value.map((run, index) => renderRun(run, index));
42134
- }
42135
- function renderRun(run, key) {
42136
- return (run.marks ?? []).reduceRight((child, mark, index) => renderMark(mark, `${String(key)}-${index}`, child), run.text);
42137
- }
42138
- function renderMark(mark, key, child) {
42139
- switch (mark.kind) {
42140
- case "strong": return /* @__PURE__ */ jsx("strong", { children: child }, key);
42141
- case "emphasis": return /* @__PURE__ */ jsx("em", { children: child }, key);
42142
- case "accent": return /* @__PURE__ */ jsx("span", {
42143
- className: `rb-inline-text-accent rb-inline-text-accent-${mark.tone}`,
42144
- children: child
42145
- }, key);
42146
- case "highlight": return /* @__PURE__ */ jsx("span", {
42147
- className: `rb-inline-text-highlight rb-inline-text-highlight-${mark.tone}`,
42148
- children: child
42149
- }, key);
42150
- }
42151
- }
42152
- //#endregion
42153
- //#region ../blocks/src/system/runtime/nodes/basic.tsx
42154
- const TextNode = ({ value, children, blockId: _blockId, blockKind: _blockKind, blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, inlineTextFallbackPath, ...rest }) => {
42155
- const inlineText = parseInlineTextFromUnknown(value);
42156
- if (inlineText.ok && !isInlineTextEmpty(inlineText.value)) return /* @__PURE__ */ jsx(Text, {
42157
- ...rest,
42158
- children: renderInlineText(inlineText.value)
42159
- });
42160
- const fallbackValue = inlineTextFallbackPath ? readStringPath(blockContent, inlineTextFallbackPath) : null;
42161
- return /* @__PURE__ */ jsx(Text, {
42162
- ...rest,
42163
- children: fallbackValue ?? textNodeContent(value) ?? children
42164
- });
42165
- };
42166
- function textNodeContent(value) {
42167
- if (value === null || value === void 0) return null;
42168
- if (typeof value === "string" || typeof value === "number") return value;
42169
- return null;
42170
- }
42171
- function readStringPath(source, path) {
42172
- const value = getAtPath(source, path);
42173
- return typeof value === "string" ? value : null;
42174
- }
42175
- const RichTextNode = ({ value, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, ...rest }) => {
42176
- return /* @__PURE__ */ jsx(RichText, {
42177
- doc: coerceRichTextDoc(value) ?? void 0,
42178
- ...rest
42179
- });
42180
- };
42181
- const ButtonNode = ({ value, children, href, variantId = "primary", size = "md", disabled, type = "button", target, rel, linkRequired = false, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, className, ...rest }) => {
42182
- const content = value ?? children;
42183
- if (!content) {
42184
- console.warn("[ButtonNode] No content to render - value and children are both empty");
42185
- return null;
42186
- }
42187
- const resolvedHref = typeof href === "string" ? href.trim() : "";
42188
- if (linkRequired && resolvedHref.length === 0) {
42189
- if (process.env.NODE_ENV === "development") console.warn("[ButtonNode] Missing href for link-required button");
42190
- return null;
42191
- }
42192
- const resolvedVariantId = parseThemeButtonVariantId(variantId);
42193
- const combinedClassName = resolvedVariantId ? themeButtonClassName({
42194
- variant: resolvedVariantId,
42195
- size,
42196
- extraClassName: className
42197
- }) : className;
42198
- if (resolvedHref) return /* @__PURE__ */ jsx("a", {
42199
- href: resolvedHref,
42200
- target,
42201
- rel,
42202
- className: combinedClassName,
42203
- ...rest,
42204
- children: content
42205
- });
42206
- return /* @__PURE__ */ jsx("button", {
42207
- type,
42208
- disabled,
42209
- className: combinedClassName,
42210
- ...rest,
42211
- children: content
42212
- });
42213
- };
42214
- const LinkNode = ({ href, children, className, target, rel, linkRequired = false, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, ...rest }) => {
42215
- if (!children) return null;
42216
- const resolvedHref = typeof href === "string" ? href.trim() : "";
42217
- if (resolvedHref.length === 0) {
42218
- if (linkRequired && process.env.NODE_ENV === "development") console.warn("[LinkNode] Missing href for link-required link");
42219
- if (linkRequired) return null;
42220
- return /* @__PURE__ */ jsx("span", {
42221
- className,
42222
- ...rest,
42223
- children
42224
- });
42225
- }
42226
- return /* @__PURE__ */ jsx("a", {
42227
- href: resolvedHref,
42228
- className,
42229
- target,
42230
- rel,
42231
- ...rest,
42232
- children
42233
- });
42234
- };
42235
- //#endregion
42236
42958
  //#region ../blocks/src/system/icons/cta.ts
42237
42959
  /**
42238
42960
  * Curated icon set for CTA/button icons.
@@ -42331,6 +43053,171 @@ function getIconComponent(name) {
42331
43053
  return getCtaIconComponent(name) ?? getSemanticIconComponent(name);
42332
43054
  }
42333
43055
  //#endregion
43056
+ //#region ../blocks/src/system/runtime/nodes/buttonAffordance.ts
43057
+ function normalize(value) {
43058
+ const trimmed = typeof value === "string" ? value.trim() : "";
43059
+ return trimmed.length > 0 ? trimmed : null;
43060
+ }
43061
+ /**
43062
+ * Resolve the trailing affordance a button should render. Node overrides
43063
+ * personality; returns `null` when nothing is configured.
43064
+ */
43065
+ function resolveButtonAffordance(input) {
43066
+ const nodeIcon = normalize(input.trailingIcon);
43067
+ if (nodeIcon) {
43068
+ const colorToken = normalize(input.trailingIconColorToken);
43069
+ return {
43070
+ iconId: nodeIcon,
43071
+ source: "node",
43072
+ ...colorToken ? { colorToken } : {}
43073
+ };
43074
+ }
43075
+ const personality = input.personalityAffordance;
43076
+ const personalityIcon = normalize(personality?.iconId);
43077
+ if (personality && personalityIcon) {
43078
+ const colorToken = normalize(personality.colorToken);
43079
+ return {
43080
+ iconId: personalityIcon,
43081
+ source: "personality",
43082
+ ...colorToken ? { colorToken } : {}
43083
+ };
43084
+ }
43085
+ return null;
43086
+ }
43087
+ //#endregion
43088
+ //#region ../blocks/src/system/runtime/nodes/richText-coerce.ts
43089
+ function coerceRichTextDoc(value) {
43090
+ const raw = unwrapRichTextRoot(value);
43091
+ if (typeof raw === "string") return createRichTextDocFromMarkdown(raw);
43092
+ if (isRecord$13(raw) && typeof raw.type === "string") return raw;
43093
+ return null;
43094
+ }
43095
+ //#endregion
43096
+ //#region ../blocks/src/system/runtime/nodes/renderInlineText.tsx
43097
+ function renderInlineText(value) {
43098
+ return value.map((run, index) => renderRun(run, index));
43099
+ }
43100
+ function renderRun(run, key) {
43101
+ return (run.marks ?? []).reduceRight((child, mark, index) => renderMark(mark, `${String(key)}-${index}`, child), run.text);
43102
+ }
43103
+ function renderMark(mark, key, child) {
43104
+ switch (mark.kind) {
43105
+ case "strong": return /* @__PURE__ */ jsx("strong", { children: child }, key);
43106
+ case "emphasis": return /* @__PURE__ */ jsx("em", { children: child }, key);
43107
+ case "accent": return /* @__PURE__ */ jsx("span", {
43108
+ className: `rb-inline-text-accent rb-inline-text-accent-${mark.tone}`,
43109
+ children: child
43110
+ }, key);
43111
+ case "highlight": return /* @__PURE__ */ jsx("span", {
43112
+ className: `rb-inline-text-highlight rb-inline-text-highlight-${mark.tone}`,
43113
+ children: child
43114
+ }, key);
43115
+ }
43116
+ }
43117
+ //#endregion
43118
+ //#region ../blocks/src/system/runtime/nodes/basic.tsx
43119
+ const TextNode = ({ value, children, blockId: _blockId, blockKind: _blockKind, blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, inlineTextFallbackPath, ...rest }) => {
43120
+ const inlineText = parseInlineTextFromUnknown(value);
43121
+ if (inlineText.ok && !isInlineTextEmpty(inlineText.value)) return /* @__PURE__ */ jsx(Text, {
43122
+ ...rest,
43123
+ children: renderInlineText(inlineText.value)
43124
+ });
43125
+ const fallbackValue = inlineTextFallbackPath ? readStringPath(blockContent, inlineTextFallbackPath) : null;
43126
+ return /* @__PURE__ */ jsx(Text, {
43127
+ ...rest,
43128
+ children: fallbackValue ?? textNodeContent(value) ?? children
43129
+ });
43130
+ };
43131
+ function textNodeContent(value) {
43132
+ if (value === null || value === void 0) return null;
43133
+ if (typeof value === "string" || typeof value === "number") return value;
43134
+ return null;
43135
+ }
43136
+ function readStringPath(source, path) {
43137
+ const value = getAtPath(source, path);
43138
+ return typeof value === "string" ? value : null;
43139
+ }
43140
+ const RichTextNode = ({ value, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, ...rest }) => {
43141
+ return /* @__PURE__ */ jsx(RichText, {
43142
+ doc: coerceRichTextDoc(value) ?? void 0,
43143
+ ...rest
43144
+ });
43145
+ };
43146
+ /**
43147
+ * Render the resolved trailing affordance icon. Colour is applied via a theme
43148
+ * CSS var so it recolours with the palette; the icon stroke uses `currentColor`.
43149
+ */
43150
+ function renderTrailingAffordanceIcon(affordance) {
43151
+ if (!affordance) return null;
43152
+ const Icon = getIconComponent(affordance.iconId);
43153
+ if (!Icon) return null;
43154
+ return /* @__PURE__ */ jsx(Icon, {
43155
+ "aria-hidden": true,
43156
+ className: "rb-ml-2 rb-h-4 rb-w-4 rb-inline-block",
43157
+ style: affordance.colorToken ? { color: `rgb(var(--tb-${affordance.colorToken}))` } : void 0
43158
+ });
43159
+ }
43160
+ const ButtonNode = ({ value, children, href, variantId = "primary", size = "md", disabled, type = "button", target, rel, linkRequired = false, trailingIcon, trailingIconColorToken, personalityAffordance, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, className, ...rest }) => {
43161
+ const content = value ?? children;
43162
+ if (!content) {
43163
+ console.warn("[ButtonNode] No content to render - value and children are both empty");
43164
+ return null;
43165
+ }
43166
+ const affordanceIcon = renderTrailingAffordanceIcon(resolveButtonAffordance({
43167
+ trailingIcon,
43168
+ trailingIconColorToken,
43169
+ personalityAffordance
43170
+ }));
43171
+ const resolvedHref = typeof href === "string" ? href.trim() : "";
43172
+ if (linkRequired && resolvedHref.length === 0) {
43173
+ if (process.env.NODE_ENV === "development") console.warn("[ButtonNode] Missing href for link-required button");
43174
+ return null;
43175
+ }
43176
+ const extraClassName = affordanceIcon ? [className, AFFORDANCE_SUPPRESS_CLASS].filter(Boolean).join(" ") : className;
43177
+ const resolvedVariantId = parseThemeButtonVariantId(variantId);
43178
+ const combinedClassName = resolvedVariantId ? themeButtonClassName({
43179
+ variant: resolvedVariantId,
43180
+ size,
43181
+ extraClassName
43182
+ }) : extraClassName;
43183
+ if (resolvedHref) return /* @__PURE__ */ jsxs("a", {
43184
+ href: resolvedHref,
43185
+ target,
43186
+ rel,
43187
+ className: combinedClassName,
43188
+ ...rest,
43189
+ children: [content, affordanceIcon]
43190
+ });
43191
+ return /* @__PURE__ */ jsxs("button", {
43192
+ type,
43193
+ disabled,
43194
+ className: combinedClassName,
43195
+ ...rest,
43196
+ children: [content, affordanceIcon]
43197
+ });
43198
+ };
43199
+ const LinkNode = ({ href, children, className, target, rel, linkRequired = false, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, ...rest }) => {
43200
+ if (!children) return null;
43201
+ const resolvedHref = typeof href === "string" ? href.trim() : "";
43202
+ if (resolvedHref.length === 0) {
43203
+ if (linkRequired && process.env.NODE_ENV === "development") console.warn("[LinkNode] Missing href for link-required link");
43204
+ if (linkRequired) return null;
43205
+ return /* @__PURE__ */ jsx("span", {
43206
+ className,
43207
+ ...rest,
43208
+ children
43209
+ });
43210
+ }
43211
+ return /* @__PURE__ */ jsx("a", {
43212
+ href: resolvedHref,
43213
+ className,
43214
+ target,
43215
+ rel,
43216
+ ...rest,
43217
+ children
43218
+ });
43219
+ };
43220
+ //#endregion
42334
43221
  //#region ../blocks/src/system/runtime/nodes/icon.tsx
42335
43222
  const IconNode = ({ value, title, className, blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, ...rest }) => {
42336
43223
  const parsedValue = parseBlockIconValue(value);
@@ -43029,6 +43916,77 @@ function SpinnerNode({ className, size = "md", color, strokeWidth = 3, srLabel,
43029
43916
  });
43030
43917
  }
43031
43918
  //#endregion
43919
+ //#region ../blocks/src/system/runtime/nodes/svg.tsx
43920
+ /**
43921
+ * Turn paint props into a CSS style object. Colour values pass through as-is so
43922
+ * they may reference theme tokens via `var()`, which only resolves in a CSS
43923
+ * property context (not in a bare presentation attribute).
43924
+ */
43925
+ function paintStyle(shape) {
43926
+ const style = {};
43927
+ if (shape.fill !== void 0) style.fill = shape.fill;
43928
+ if (shape.stroke !== void 0) style.stroke = shape.stroke;
43929
+ if (shape.strokeWidth !== void 0) style.strokeWidth = shape.strokeWidth;
43930
+ if (shape.fillOpacity !== void 0) style.fillOpacity = shape.fillOpacity;
43931
+ if (shape.strokeOpacity !== void 0) style.strokeOpacity = shape.strokeOpacity;
43932
+ if (shape.strokeLinecap !== void 0) style.strokeLinecap = shape.strokeLinecap;
43933
+ if (shape.strokeLinejoin !== void 0) style.strokeLinejoin = shape.strokeLinejoin;
43934
+ if (shape.strokeDasharray !== void 0) style.strokeDasharray = shape.strokeDasharray;
43935
+ return style;
43936
+ }
43937
+ function renderShape(shape, key) {
43938
+ const style = paintStyle(shape);
43939
+ switch (shape.kind) {
43940
+ case "path": return /* @__PURE__ */ jsx("path", {
43941
+ d: shape.d,
43942
+ style
43943
+ }, key);
43944
+ case "ellipse": return /* @__PURE__ */ jsx("ellipse", {
43945
+ cx: shape.cx,
43946
+ cy: shape.cy,
43947
+ rx: shape.rx,
43948
+ ry: shape.ry,
43949
+ style
43950
+ }, key);
43951
+ case "circle": return /* @__PURE__ */ jsx("circle", {
43952
+ cx: shape.cx,
43953
+ cy: shape.cy,
43954
+ r: shape.r,
43955
+ style
43956
+ }, key);
43957
+ case "rect": return /* @__PURE__ */ jsx("rect", {
43958
+ x: shape.x,
43959
+ y: shape.y,
43960
+ width: shape.width,
43961
+ height: shape.height,
43962
+ rx: shape.rx,
43963
+ ry: shape.ry,
43964
+ style
43965
+ }, key);
43966
+ case "line": return /* @__PURE__ */ jsx("line", {
43967
+ x1: shape.x1,
43968
+ y1: shape.y1,
43969
+ x2: shape.x2,
43970
+ y2: shape.y2,
43971
+ style
43972
+ }, key);
43973
+ default: return null;
43974
+ }
43975
+ }
43976
+ const SvgNode = ({ blockId: _blockId, blockKind: _blockKind, blockContent: _blockContent, fragmentRegistry: _fragmentRegistry, pathBase: _pathBase, viewBox, width, height, className, style, ariaHidden = true, shapes }) => {
43977
+ return /* @__PURE__ */ jsx("svg", {
43978
+ viewBox,
43979
+ width,
43980
+ height,
43981
+ className,
43982
+ style,
43983
+ "aria-hidden": ariaHidden,
43984
+ xmlns: "http://www.w3.org/2000/svg",
43985
+ fill: "none",
43986
+ children: (Array.isArray(shapes) ? shapes : []).map((shape, index) => renderShape(shape, index))
43987
+ });
43988
+ };
43989
+ //#endregion
43032
43990
  //#region ../blocks/src/system/runtime/nodes/booking-form.state.ts
43033
43991
  function toTrimmedString(value) {
43034
43992
  const trimmed = value?.trim();
@@ -45789,6 +46747,154 @@ const WeekAgendaView = ({ days, events, showCapacity, emptyMessage = "No events
45789
46747
  });
45790
46748
  };
45791
46749
  //#endregion
46750
+ //#region ../blocks/src/system/runtime/nodes/events/shared/WeekBoardView.tsx
46751
+ const SHORT_DAY_NAMES = [
46752
+ "Sun",
46753
+ "Mon",
46754
+ "Tue",
46755
+ "Wed",
46756
+ "Thu",
46757
+ "Fri",
46758
+ "Sat"
46759
+ ];
46760
+ const LEGEND_ITEMS = [
46761
+ {
46762
+ status: "available",
46763
+ label: "Places available"
46764
+ },
46765
+ {
46766
+ status: "filling",
46767
+ label: "Filling up"
46768
+ },
46769
+ {
46770
+ status: "waitlist",
46771
+ label: "Waitlist"
46772
+ }
46773
+ ];
46774
+ /**
46775
+ * Format an event's start–end as a compact range, merging the shared meridiem.
46776
+ * "6:00 PM" + "7:00 PM" -> "6:00 – 7:00 PM"; "9:30 AM" + "10:15 AM" -> "9:30 – 10:15 AM".
46777
+ */
46778
+ function formatCompactTimeRange(event) {
46779
+ const timeZone = event.timeZone ?? null;
46780
+ const start = formatTime(event.startsAt, { timeZone });
46781
+ const end = formatTime(event.endsAt, { timeZone });
46782
+ const startMeridiem = start.slice(-2);
46783
+ const endMeridiem = end.slice(-2);
46784
+ if ((startMeridiem === "AM" || startMeridiem === "PM") && startMeridiem === endMeridiem) return `${start.slice(0, -3).trim()} – ${end}`;
46785
+ return `${start} – ${end}`;
46786
+ }
46787
+ const WeekBoardCard = ({ event, showCapacity }) => {
46788
+ const linkProps = resolveEventLinkProps(event);
46789
+ const { status, label } = getEventAvailabilityStatus(event);
46790
+ const pill = showCapacity ? label : null;
46791
+ return /* @__PURE__ */ jsxs("a", {
46792
+ ...linkProps,
46793
+ className: "event-week-board-card",
46794
+ "data-status": status,
46795
+ children: [
46796
+ /* @__PURE__ */ jsx("span", {
46797
+ className: "event-week-board-card-title",
46798
+ children: event.title
46799
+ }),
46800
+ /* @__PURE__ */ jsx("span", {
46801
+ className: "event-week-board-card-time",
46802
+ children: formatCompactTimeRange(event)
46803
+ }),
46804
+ pill ? /* @__PURE__ */ jsx("span", {
46805
+ className: "event-week-board-card-pill",
46806
+ "data-status": status,
46807
+ children: pill
46808
+ }) : null
46809
+ ]
46810
+ });
46811
+ };
46812
+ /**
46813
+ * WeekBoardView
46814
+ *
46815
+ * Compact "class day-board": day columns side by side (one card per class per
46816
+ * day), a legend of the three availability states, and a footer CTA. Collapses
46817
+ * to a stacked list on mobile. Repurposes the week-grouped agenda data as a
46818
+ * board rather than an hour-positioned timetable grid.
46819
+ */
46820
+ const WeekBoardView = ({ days, events, showCapacity, emptyMessage = "No classes scheduled this week", title = "The week ahead", footerNote = "New to a class? We'll check it's right for you first.", ctaLabel = "Reserve a place", ctaHref = null, buttonVariant = "outline" }) => {
46821
+ const eventsByDate = groupEventsByDate(events);
46822
+ const hasAnyEvents = days.some((date) => (eventsByDate.get(formatDateKey(date)) ?? []).length > 0);
46823
+ const ctaClassName = themeButtonClassName({
46824
+ variant: buttonVariant,
46825
+ size: "sm"
46826
+ });
46827
+ return /* @__PURE__ */ jsx("div", {
46828
+ className: "event-calendar event-calendar--week-board",
46829
+ "data-block": "event-calendar",
46830
+ children: /* @__PURE__ */ jsxs("div", {
46831
+ className: "event-week-board",
46832
+ style: { "--event-week-board-cols": String(days.length) },
46833
+ children: [
46834
+ /* @__PURE__ */ jsxs("div", {
46835
+ className: "event-week-board-top",
46836
+ children: [/* @__PURE__ */ jsx("span", {
46837
+ className: "event-week-board-title",
46838
+ children: title
46839
+ }), /* @__PURE__ */ jsx("div", {
46840
+ className: "event-week-board-legend",
46841
+ "aria-hidden": "true",
46842
+ children: LEGEND_ITEMS.map((item) => /* @__PURE__ */ jsxs("span", {
46843
+ className: "event-week-board-legend-item",
46844
+ children: [/* @__PURE__ */ jsx("i", {
46845
+ className: "event-week-board-legend-dot",
46846
+ "data-status": item.status
46847
+ }), item.label]
46848
+ }, item.status))
46849
+ })]
46850
+ }),
46851
+ /* @__PURE__ */ jsx("div", {
46852
+ className: "event-week-board-grid",
46853
+ children: days.map((date) => {
46854
+ const dateKey = formatDateKey(date);
46855
+ const dayEvents = eventsByDate.get(dateKey) ?? [];
46856
+ const today = isToday(date);
46857
+ return /* @__PURE__ */ jsxs("div", {
46858
+ className: clsx("event-week-board-day", dayEvents.length === 0 && "event-week-board-day--empty", today && "event-week-board-day--today"),
46859
+ children: [/* @__PURE__ */ jsxs("div", {
46860
+ className: "event-week-board-day-head",
46861
+ children: [/* @__PURE__ */ jsx("span", {
46862
+ className: "event-week-board-day-abbr",
46863
+ children: SHORT_DAY_NAMES[date.getDay()]
46864
+ }), /* @__PURE__ */ jsx("span", {
46865
+ className: "event-week-board-day-num",
46866
+ children: date.getDate()
46867
+ })]
46868
+ }), /* @__PURE__ */ jsx("div", {
46869
+ className: "event-week-board-day-body",
46870
+ children: dayEvents.map((event) => /* @__PURE__ */ jsx(WeekBoardCard, {
46871
+ event,
46872
+ showCapacity
46873
+ }, event.occurrenceId || event.id))
46874
+ })]
46875
+ }, dateKey);
46876
+ })
46877
+ }),
46878
+ /* @__PURE__ */ jsxs("div", {
46879
+ className: "event-week-board-foot",
46880
+ children: [/* @__PURE__ */ jsx("p", {
46881
+ className: "event-week-board-foot-note",
46882
+ children: hasAnyEvents ? footerNote : emptyMessage
46883
+ }), ctaHref ? /* @__PURE__ */ jsx("a", {
46884
+ className: ctaClassName,
46885
+ href: ctaHref,
46886
+ children: ctaLabel
46887
+ }) : /* @__PURE__ */ jsx("button", {
46888
+ type: "button",
46889
+ className: ctaClassName,
46890
+ children: ctaLabel
46891
+ })]
46892
+ })
46893
+ ]
46894
+ })
46895
+ });
46896
+ };
46897
+ //#endregion
45792
46898
  //#region ../blocks/src/system/runtime/nodes/events/shared/timetableModel.ts
45793
46899
  /**
45794
46900
  * Event-domain adapter over @riverbankcms/calendar.
@@ -46223,6 +47329,16 @@ function renderTimetableSsr(display) {
46223
47329
  })]
46224
47330
  });
46225
47331
  }
47332
+ function renderWeekBoardSsr(display) {
47333
+ return /* @__PURE__ */ jsx(WeekBoardView, {
47334
+ days: getWeekCalendarDayTuple(/* @__PURE__ */ new Date(), display.startOfWeek),
47335
+ events: [...display.events],
47336
+ showCapacity: display.showCapacity,
47337
+ emptyMessage: display.emptyMessage,
47338
+ ctaLabel: display.buttonText,
47339
+ buttonVariant: display.buttonVariant
47340
+ });
47341
+ }
46226
47342
  function formatHourLabel(hour) {
46227
47343
  if (hour === 0) return "12 AM";
46228
47344
  if (hour === 12) return "12 PM";
@@ -46249,6 +47365,9 @@ const EventCalendarSSR = (props) => {
46249
47365
  case "timetable":
46250
47366
  content = renderTimetableSsr(display);
46251
47367
  break;
47368
+ case "week-board":
47369
+ content = renderWeekBoardSsr(display);
47370
+ break;
46252
47371
  case "calendar":
46253
47372
  content = renderCalendarSsr(display);
46254
47373
  break;
@@ -46820,6 +47939,7 @@ typedObjectKeys(runtimeBackedBlockServerEntries);
46820
47939
  const serverComponentRegistry = {
46821
47940
  div: DivNode,
46822
47941
  figure: FigureNode,
47942
+ svg: SvgNode,
46823
47943
  section: SectionNode,
46824
47944
  headerSection: header_section_server_default,
46825
47945
  stack: StackNode,
@@ -48235,6 +49355,8 @@ function formatFontFamily(family, fallback = "sans-serif") {
48235
49355
  function generateShadowCssVars(theme) {
48236
49356
  const level = theme.shadow.elevation;
48237
49357
  const soft = theme.shadow.softness !== "crisp";
49358
+ const tintVar = theme.shadow.colorToken ? cssVarValue(theme.shadow.colorToken) : null;
49359
+ const shadowColor = (a) => tintVar ? `rgb(${tintVar} / ${a})` : `rgba(0, 0, 0, ${a})`;
48238
49360
  const base = {
48239
49361
  sm: [{
48240
49362
  y: 1,
@@ -48294,7 +49416,7 @@ function generateShadowCssVars(theme) {
48294
49416
  const blurMul = soft ? 1.2 : 1;
48295
49417
  const alphaMul = soft ? .9 : 1.05;
48296
49418
  const fmt = (layers) => layers.map((l) => {
48297
- return `0 ${Math.round(l.y * scale)}px ${Math.round(l.blur * scale * blurMul)}px ${Math.round(l.spread * scale)}px rgba(0, 0, 0, ${Math.max(0, Math.min(1, +(l.a * alphaMul).toFixed(3)))})`;
49419
+ return `0 ${Math.round(l.y * scale)}px ${Math.round(l.blur * scale * blurMul)}px ${Math.round(l.spread * scale)}px ${shadowColor(Math.max(0, Math.min(1, +(l.a * alphaMul).toFixed(3))))}`;
48298
49420
  }).join(", ");
48299
49421
  return {
48300
49422
  "--shadow-sm": fmt(base.sm),
@@ -48360,7 +49482,13 @@ function generateAccordionCss({ themeId, theme, accordionSystem }) {
48360
49482
  const questionColumnsLastItemSelector = `${questionColumnsItemSelector}:last-child`;
48361
49483
  const questionColumnsTriggerSelector = `${questionColumnsItemSelector} .accordion-trigger`;
48362
49484
  const rootRules = [];
48363
- if (accordionSystem.background) rootRules.push(`background-color: ${rgbColorValue(accordionSystem.background.colorToken)}`);
49485
+ if (accordionSystem.background) {
49486
+ rootRules.push(`background-color: ${rgbColorValue(accordionSystem.background.colorToken)}`);
49487
+ rootRules.push(...surfaceForegroundDeclarations({
49488
+ text: rgbColorValue(accordionSystem.content.textColorToken),
49489
+ heading: rgbColorValue(accordionSystem.trigger.textColorToken)
49490
+ }));
49491
+ }
48364
49492
  const borderRadius = CORNER_MAP$2[accordionSystem.corners ?? theme.corners];
48365
49493
  rootRules.push(`border-radius: ${borderRadius}`);
48366
49494
  if (accordionSystem.border) {
@@ -49944,7 +51072,7 @@ ${root} .status-error { color: rgb(var(--tb-destructive)); }
49944
51072
  ${root} .status-success { color: rgb(var(--tb-success)); }
49945
51073
  ${root} .status-warning { color: rgb(var(--tb-warning)); }
49946
51074
  ${root} .status-info { color: rgb(var(--tb-info)); }
49947
- ${root} .status-muted { ${statusMutedColorDeclaration()} }`);
51075
+ ${root} .status-muted { color: ${surfaceForegroundValue("supporting")}; }`);
49948
51076
  cssBlocks.push(`/* Alert containers */
49949
51077
  ${root} .alert {
49950
51078
  padding: 1rem;
@@ -50082,6 +51210,9 @@ ${root} .step-connector {
50082
51210
  }
50083
51211
  //#endregion
50084
51212
  //#region ../theme-core/src/typography/generateTypographyCss.ts
51213
+ function globalHeadingColorFallback(level) {
51214
+ return `var(--hc-h${level}, var(--hc-heading, ${DEFAULT_SURFACE_TEXT_COLOR}))`;
51215
+ }
50085
51216
  /**
50086
51217
  * Generate typography CSS for heading elements.
50087
51218
  *
@@ -50110,7 +51241,7 @@ ${root} :where(h1) {
50110
51241
  font-style: var(--fi-h1, var(--fi-heading, normal));
50111
51242
  letter-spacing: var(--ls-h1, var(--ls-heading));
50112
51243
  line-height: var(--lh-h1, var(--lh-heading));
50113
- color: var(--section-heading-color, var(--section-text-color, var(--hc-h1, var(--hc-heading, inherit))));
51244
+ color: ${surfaceForegroundValue("heading", globalHeadingColorFallback(1))};
50114
51245
  }
50115
51246
 
50116
51247
  ${root} :where(h2) {
@@ -50119,7 +51250,7 @@ ${root} :where(h2) {
50119
51250
  font-style: var(--fi-h2, var(--fi-heading, normal));
50120
51251
  letter-spacing: var(--ls-h2, var(--ls-heading));
50121
51252
  line-height: var(--lh-h2, var(--lh-heading));
50122
- color: var(--section-heading-color, var(--section-text-color, var(--hc-h2, var(--hc-heading, inherit))));
51253
+ color: ${surfaceForegroundValue("heading", globalHeadingColorFallback(2))};
50123
51254
  }
50124
51255
 
50125
51256
  ${root} :where(h3) {
@@ -50128,7 +51259,7 @@ ${root} :where(h3) {
50128
51259
  font-style: var(--fi-h3, var(--fi-heading, normal));
50129
51260
  letter-spacing: var(--ls-h3, var(--ls-heading));
50130
51261
  line-height: var(--lh-h3, var(--lh-heading));
50131
- color: var(--section-heading-color, var(--section-text-color, var(--hc-h3, var(--hc-heading, inherit))));
51262
+ color: ${surfaceForegroundValue("heading", globalHeadingColorFallback(3))};
50132
51263
  }
50133
51264
 
50134
51265
  ${root} :where(h4) {
@@ -50137,7 +51268,7 @@ ${root} :where(h4) {
50137
51268
  font-style: var(--fi-h4, var(--fi-heading, normal));
50138
51269
  letter-spacing: var(--ls-h4, var(--ls-heading));
50139
51270
  line-height: var(--lh-h4, var(--lh-heading));
50140
- color: var(--section-heading-color, var(--section-text-color, var(--hc-h4, var(--hc-heading, inherit))));
51271
+ color: ${surfaceForegroundValue("heading", globalHeadingColorFallback(4))};
50141
51272
  }
50142
51273
 
50143
51274
  ${root} :where(h5) {
@@ -50146,7 +51277,7 @@ ${root} :where(h5) {
50146
51277
  font-style: var(--fi-h5, var(--fi-heading, normal));
50147
51278
  letter-spacing: var(--ls-h5, var(--ls-heading));
50148
51279
  line-height: var(--lh-h5, var(--lh-heading));
50149
- color: var(--section-heading-color, var(--section-text-color, var(--hc-h5, var(--hc-heading, inherit))));
51280
+ color: ${surfaceForegroundValue("heading", globalHeadingColorFallback(5))};
50150
51281
  }
50151
51282
 
50152
51283
  ${root} :where(h6) {
@@ -50155,7 +51286,7 @@ ${root} :where(h6) {
50155
51286
  font-style: var(--fi-h6, var(--fi-heading, normal));
50156
51287
  letter-spacing: var(--ls-h6, var(--ls-heading));
50157
51288
  line-height: var(--lh-h6, var(--lh-heading));
50158
- color: var(--section-heading-color, var(--section-text-color, var(--hc-h6, var(--hc-heading, inherit))));
51289
+ color: ${surfaceForegroundValue("heading", globalHeadingColorFallback(6))};
50159
51290
  }`);
50160
51291
  cssBlocks.push(`/* Display variant for hero/splash headings */
50161
51292
  ${root} :where(.heading-display) {
@@ -50554,6 +51685,7 @@ const THEME_RADIUS_CSS_VARS = {
50554
51685
  block: "--rb-block-radius",
50555
51686
  button: "--rb-button-radius",
50556
51687
  input: "--rb-input-radius",
51688
+ panel: "--rb-panel-radius",
50557
51689
  legacyCard: "--radius-card",
50558
51690
  legacyControl: "--radius-control"
50559
51691
  };
@@ -50635,6 +51767,10 @@ ${`:where([data-theme-scope="${themeId}"]) :where([data-block="customer-portal"]
50635
51767
  /* Modal (shared rb-modal classes) */
50636
51768
  --rb-modal-overlay-bg: rgb(var(--tb-text, 0 0 0) / 0.35);
50637
51769
  --rb-modal-bg: var(--cp-card-bg);
51770
+ --rb-modal-text: var(--cp-text);
51771
+ --rb-modal-heading: var(--cp-text);
51772
+ --rb-modal-link: var(--cp-primary);
51773
+ --rb-modal-supporting: var(--cp-muted);
50638
51774
  --rb-modal-border: var(--cp-card-border);
50639
51775
  --rb-modal-shadow: var(--shadow-lg, var(--shadow-elev));
50640
51776
  --rb-modal-radius: var(--cp-card-radius);
@@ -51349,6 +52485,7 @@ function buildThemeCssVars(theme) {
51349
52485
  const inputSystem = resolveThemeInputSystem(theme);
51350
52486
  const blockRadius = mapBlockRadius(theme.corners);
51351
52487
  const buttonRadius = mapButtonRadius(theme.corners);
52488
+ const panelRadius = mapPanelRadius(theme.corners);
51352
52489
  const inputRadius = mapInputRadius(inputSystem.base.corners, theme.corners);
51353
52490
  return {
51354
52491
  ...generateCssVars(theme.palette),
@@ -51357,6 +52494,7 @@ function buildThemeCssVars(theme) {
51357
52494
  ...generateShadowCssVars(theme),
51358
52495
  ["--font-heading"]: formatFontFamily(typography.headings.default.family),
51359
52496
  ["--font-body"]: formatFontFamily(typography.body.family),
52497
+ ["--font-eyebrow"]: typography.eyebrow?.family ? formatFontFamily(typography.eyebrow.family) : "var(--font-body)",
51360
52498
  ["--font-weight-heading"]: mapWeight(typography.headings.default.weight),
51361
52499
  ["--font-weight-body"]: mapWeight(typography.body.weight),
51362
52500
  ...typography.headings.h1.weight ? { ["--fw-h1"]: mapWeight(typography.headings.h1.weight) } : {},
@@ -51407,12 +52545,13 @@ function buildThemeCssVars(theme) {
51407
52545
  ["--fs-h3-fluid"]: sized.h3Fluid,
51408
52546
  ["--fs-h1-display"]: sized.h1Display,
51409
52547
  ["--fs-h1-display-fluid"]: sized.h1DisplayFluid,
51410
- ["--shadow-elev"]: mapShadow(shadow.elevation, shadow.softness, shadow.position || "bottom"),
52548
+ ["--shadow-elev"]: mapShadow(shadow.elevation, shadow.softness, shadow.position || "bottom", shadow.colorToken),
51411
52549
  ["--space-mult"]: mapSpaceMult(space),
51412
52550
  ["--motion-duration"]: mapMotionDuration(motion.level),
51413
52551
  ["--motion-ease"]: mapMotionEasing(motion.easing ?? "standard"),
51414
52552
  ["--rt-space-y"]: mapRhythm(space),
51415
52553
  [THEME_RADIUS_CSS_VARS.block]: blockRadius,
52554
+ [THEME_RADIUS_CSS_VARS.panel]: panelRadius,
51416
52555
  ["--rb-block-transition"]: mapBlockTransition(motion.level),
51417
52556
  [THEME_RADIUS_CSS_VARS.button]: buttonRadius,
51418
52557
  ["--rb-border-width"]: mapBorderWidth(border.width),
@@ -51466,20 +52605,22 @@ function mapWeight(w) {
51466
52605
  bold: 700
51467
52606
  }[w];
51468
52607
  }
51469
- function mapShadow(level, softness, position = "bottom") {
52608
+ function mapShadow(level, softness, position = "bottom", colorToken) {
51470
52609
  if (level === "none") return "none";
52610
+ const tintVar = colorToken ? cssVarValue(colorToken) : null;
52611
+ const shadowColor = (a) => tintVar ? `rgb(${tintVar} / ${a})` : `rgba(0,0,0,${a})`;
51471
52612
  const offset = {
51472
52613
  low: 2,
51473
52614
  medium: 4,
51474
52615
  high: 6
51475
52616
  }[level];
51476
52617
  const xOffset = position === "bottom-right" ? offset : 0;
51477
- if (softness === "hard") return `${xOffset}px ${offset}px 0 0 rgba(0,0,0,1)`;
52618
+ if (softness === "hard") return `${xOffset}px ${offset}px 0 0 ${shadowColor("1")}`;
51478
52619
  const soft = softness !== "crisp";
51479
52620
  switch (level) {
51480
- case "low": return soft ? `${xOffset}px 1px 2px rgba(0,0,0,.06)` : `${xOffset}px 1px 1px rgba(0,0,0,.12)`;
51481
- case "medium": return soft ? `${xOffset}px 10px 20px rgba(0,0,0,.08)` : `${xOffset}px 6px 12px rgba(0,0,0,.16)`;
51482
- case "high": return soft ? `${xOffset}px 25px 50px rgba(0,0,0,.18)` : `${xOffset}px 20px 30px rgba(0,0,0,.28)`;
52621
+ case "low": return soft ? `${xOffset}px 1px 2px ${shadowColor(".06")}` : `${xOffset}px 1px 1px ${shadowColor(".12")}`;
52622
+ case "medium": return soft ? `${xOffset}px 10px 20px ${shadowColor(".08")}` : `${xOffset}px 6px 12px ${shadowColor(".16")}`;
52623
+ case "high": return soft ? `${xOffset}px 25px 50px ${shadowColor(".18")}` : `${xOffset}px 20px 30px ${shadowColor(".28")}`;
51483
52624
  }
51484
52625
  }
51485
52626
  function mapSpaceMult(space) {
@@ -51526,6 +52667,21 @@ function mapBlockRadius(corners) {
51526
52667
  }[corners];
51527
52668
  }
51528
52669
  /**
52670
+ * Map corner style to large-panel radius CSS value (Tier 2 `--rb-panel-radius`).
52671
+ * Derived from the same `corners` enum, one step more generous than the
52672
+ * block/card radius so large surfaces can carry a softer corner without a new
52673
+ * schema field. `rounded` lands on 20px (1.25rem), matching the physio mockup's
52674
+ * large-panel radius.
52675
+ */
52676
+ function mapPanelRadius(corners) {
52677
+ return {
52678
+ square: "0",
52679
+ soft: "0.75rem",
52680
+ rounded: "1.25rem",
52681
+ pill: "2rem"
52682
+ }[corners];
52683
+ }
52684
+ /**
51529
52685
  * Map motion level to block transition duration.
51530
52686
  * These are Tier 2 vars used consistently across blocks.
51531
52687
  */
@@ -52546,8 +53702,12 @@ const containerResponsiveThemeCss = `/*
52546
53702
  text-transform: uppercase;
52547
53703
  }
52548
53704
 
52549
- /* Eyebrow text - small uppercase labels for section introductions */
53705
+ /* Eyebrow text - small uppercase labels for section introductions.
53706
+ font-family resolves to the optional eyebrow/mono font role, falling back
53707
+ to the body font when the theme sets none (so existing themes are
53708
+ unchanged). */
52550
53709
  .rb-eyebrow {
53710
+ font-family: var(--font-eyebrow, var(--font-body));
52551
53711
  font-size: 0.875rem;
52552
53712
  font-weight: 600;
52553
53713
  letter-spacing: 0.025em;
@@ -52562,11 +53722,11 @@ const containerResponsiveThemeCss = `/*
52562
53722
  width: fit-content;
52563
53723
  align-items: center;
52564
53724
  justify-content: center;
52565
- border: 1px solid color-mix(in srgb, var(--section-text-color, rgb(var(--tb-text))) 46%, transparent);
53725
+ border: 1px solid color-mix(in srgb, var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39))) 46%, transparent);
52566
53726
  border-radius: 9999px;
52567
- background: color-mix(in srgb, var(--section-text-color, rgb(var(--tb-text))) 14%, transparent);
53727
+ background: color-mix(in srgb, var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39))) 14%, transparent);
52568
53728
  padding: 0.5rem 1rem;
52569
- color: var(--section-text-color, rgb(var(--tb-text)));
53729
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
52570
53730
  line-height: 1;
52571
53731
  }
52572
53732
  .rb-eyebrow-treatment-pill.rb-eyebrow-treatment-accent {
@@ -53068,6 +54228,58 @@ const containerResponsiveThemeCss = `/*
53068
54228
  }
53069
54229
  }
53070
54230
 
54231
+ }
54232
+
54233
+ /*
54234
+ * Responsive fractional grid tracks (rb-grid-tracks).
54235
+ *
54236
+ * Asymmetric AND responsive grid-template-columns, driven by inline custom
54237
+ * properties the Grid primitive sets per breakpoint from string track
54238
+ * templates:
54239
+ * --rb-gtc-base / --rb-gtc-sm / --rb-gtc-md / --rb-gtc-lg / --rb-gtc-xl
54240
+ * Each breakpoint falls back to the next smaller one, so only the breakpoints
54241
+ * an author actually sets take effect (e.g. base '1fr' + lg '1fr 0.9fr'
54242
+ * collapses to a single column below lg). Authored here (not per node) so the
54243
+ * @media -> @container conversion (container responsive mode) applies too.
54244
+ */
54245
+ .rb-grid-tracks {
54246
+ grid-template-columns: var(--rb-gtc-base, 1fr);
54247
+ }
54248
+ @container rb-site (min-width: 640px) {
54249
+
54250
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
54251
+ .rb-grid-tracks {
54252
+ grid-template-columns: var(--rb-gtc-sm, var(--rb-gtc-base, 1fr));
54253
+ }
54254
+ }
54255
+
54256
+ }
54257
+ @container rb-site (min-width: 768px) {
54258
+
54259
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
54260
+ .rb-grid-tracks {
54261
+ grid-template-columns: var(--rb-gtc-md, var(--rb-gtc-sm, var(--rb-gtc-base, 1fr)));
54262
+ }
54263
+ }
54264
+
54265
+ }
54266
+ @container rb-site (min-width: 1024px) {
54267
+
54268
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
54269
+ .rb-grid-tracks {
54270
+ grid-template-columns: var(--rb-gtc-lg, var(--rb-gtc-md, var(--rb-gtc-sm, var(--rb-gtc-base, 1fr))));
54271
+ }
54272
+ }
54273
+
54274
+ }
54275
+ @container rb-site (min-width: 1280px) {
54276
+
54277
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
54278
+ .rb-grid-tracks {
54279
+ grid-template-columns: var(--rb-gtc-xl, var(--rb-gtc-lg, var(--rb-gtc-md, var(--rb-gtc-sm, var(--rb-gtc-base, 1fr)))));
54280
+ }
54281
+ }
54282
+
53071
54283
  }
53072
54284
 
53073
54285
  /* Alignment */
@@ -54272,6 +55484,11 @@ const containerResponsiveThemeCss = `/*
54272
55484
  :where(.theme-scope) .rb-modal {
54273
55485
  width: min(var(--rb-modal-max-width, 560px), 100%);
54274
55486
  background: var(--rb-modal-bg, rgb(var(--tb-surface)));
55487
+ color: var(--rb-modal-text, rgb(var(--tb-text, 17 24 39)));
55488
+ --rb-surface-text-color: var(--rb-modal-text, rgb(var(--tb-text, 17 24 39)));
55489
+ --rb-surface-heading-color: var(--rb-modal-heading, var(--rb-modal-text, rgb(var(--tb-text, 17 24 39))));
55490
+ --rb-surface-link-color: var(--rb-modal-link, var(--rb-modal-text, rgb(var(--tb-text, 17 24 39))));
55491
+ --rb-surface-supporting-color: var(--rb-modal-supporting, var(--rb-modal-text, rgb(var(--tb-text, 17 24 39))));
54275
55492
  border: 1px solid var(--rb-modal-border, rgb(var(--tb-border)));
54276
55493
  border-radius: var(--rb-modal-radius, var(--radius-card));
54277
55494
  box-shadow: var(--rb-modal-shadow, var(--shadow-lg, var(--shadow-elev)));
@@ -54291,7 +55508,7 @@ const containerResponsiveThemeCss = `/*
54291
55508
  font-family: var(--font-heading);
54292
55509
  font-size: 1.0625rem;
54293
55510
  font-weight: 600;
54294
- color: rgb(var(--tb-text));
55511
+ color: var(--rb-surface-heading-color, var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39))));
54295
55512
  }
54296
55513
 
54297
55514
  :where(.theme-scope) .rb-modal__close {
@@ -54301,7 +55518,7 @@ const containerResponsiveThemeCss = `/*
54301
55518
  height: 2.25rem;
54302
55519
  padding: 0;
54303
55520
  line-height: 1;
54304
- color: rgb(var(--tb-mutedText));
55521
+ color: var(--rb-surface-supporting-color, var(--rb-surface-text-color, rgb(var(--tb-mutedText, 100 116 139))));
54305
55522
  cursor: pointer;
54306
55523
  display: grid;
54307
55524
  place-items: center;
@@ -54309,7 +55526,7 @@ const containerResponsiveThemeCss = `/*
54309
55526
  }
54310
55527
 
54311
55528
  :where(.theme-scope) .rb-modal__close:hover {
54312
- color: rgb(var(--tb-text));
55529
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
54313
55530
  background: rgba(var(--tb-border), 0.2);
54314
55531
  }
54315
55532
 
@@ -54320,7 +55537,7 @@ const containerResponsiveThemeCss = `/*
54320
55537
 
54321
55538
  :where(.theme-scope) .rb-modal__body {
54322
55539
  padding: 0.875rem 1rem 1rem;
54323
- color: rgb(var(--tb-text));
55540
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
54324
55541
  font-size: 0.9375rem;
54325
55542
  line-height: 1.45;
54326
55543
  }
@@ -54480,7 +55697,7 @@ const containerResponsiveThemeCss = `/*
54480
55697
  & h6 {
54481
55698
  font-family: var(--font-heading);
54482
55699
  /* Color is set by generateTypographyCss.ts with full cascade:
54483
- --section-heading-color -> --section-text-color -> --hc-hN -> --hc-heading -> inherit */
55700
+ --rb-surface-heading-color -> --rb-surface-text-color -> --hc-hN -> --hc-heading -> global text */
54484
55701
  font-weight: var(--font-weight-heading);
54485
55702
  text-transform: var(--tt-heading);
54486
55703
  font-variant-caps: var(--fv-heading);
@@ -54628,7 +55845,7 @@ const containerResponsiveThemeCss = `/*
54628
55845
 
54629
55846
  /* Neutral prose uses theme text color by default */
54630
55847
  :where(.theme-scope) .rb-prose-neutral {
54631
- color: var(--section-text-color, rgb(var(--tb-text)));
55848
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
54632
55849
  }
54633
55850
 
54634
55851
  :where(.theme-scope) .rb-prose p,
@@ -54662,8 +55879,14 @@ const containerResponsiveThemeCss = `/*
54662
55879
 
54663
55880
  :where(.theme-scope) .rb-prose a {
54664
55881
  --rb-prose-effective-link-color: var(
54665
- --section-link-color,
54666
- var(--rb-prose-link-color, rgb(var(--tb-primary)))
55882
+ --rb-surface-link-color,
55883
+ var(
55884
+ --rb-surface-text-color,
55885
+ var(
55886
+ --rb-prose-link-color,
55887
+ rgb(var(--tb-primary, var(--tb-text, 17 24 39)))
55888
+ )
55889
+ )
54667
55890
  );
54668
55891
  color: var(--rb-prose-effective-link-color);
54669
55892
  text-decoration-line: var(--rb-prose-link-decoration-line, underline);
@@ -54674,14 +55897,11 @@ const containerResponsiveThemeCss = `/*
54674
55897
  );
54675
55898
  text-underline-offset: var(--rb-prose-link-underline-offset, 0.14em);
54676
55899
  text-decoration-color: var(
54677
- --section-link-decoration-color,
54678
- var(
54679
- --rb-prose-link-decoration-color,
54680
- color-mix(
54681
- in srgb,
54682
- var(--rb-prose-effective-link-color) 35%,
54683
- transparent
54684
- )
55900
+ --rb-prose-link-decoration-color,
55901
+ color-mix(
55902
+ in srgb,
55903
+ var(--rb-prose-effective-link-color) 35%,
55904
+ transparent
54685
55905
  )
54686
55906
  );
54687
55907
  transition:
@@ -54705,7 +55925,7 @@ const containerResponsiveThemeCss = `/*
54705
55925
 
54706
55926
  :where(.theme-scope) .rb-prose blockquote {
54707
55927
  /* Blockquotes use muted text color */
54708
- color: var(--section-supporting-color, rgb(var(--tb-mutedText)));
55928
+ color: var(--rb-surface-supporting-color, var(--rb-surface-text-color, rgb(var(--tb-mutedText, 100 116 139))));
54709
55929
  border-left-color: rgb(var(--tb-border));
54710
55930
  }
54711
55931
 
@@ -55739,6 +56959,10 @@ const containerResponsiveThemeCss = `/*
55739
56959
  :where(.theme-scope) .card-surface {
55740
56960
  background: rgb(var(--tb-surface));
55741
56961
  color: rgb(var(--tb-onSurface, var(--tb-text)));
56962
+ --rb-surface-text-color: rgb(var(--tb-onSurface, var(--tb-text)));
56963
+ --rb-surface-heading-color: rgb(var(--tb-onSurface, var(--tb-text)));
56964
+ --rb-surface-link-color: rgb(var(--tb-onSurface, var(--tb-text)));
56965
+ --rb-surface-supporting-color: rgb(var(--tb-onSurface, var(--tb-text)));
55742
56966
  border: 1px solid rgb(var(--tb-border));
55743
56967
  border-radius: var(--radius-card);
55744
56968
  box-shadow: var(--shadow-elev);
@@ -55882,6 +57106,10 @@ const containerResponsiveThemeCss = `/*
55882
57106
  padding: clamp(1rem, 2vw, 1.5rem);
55883
57107
  background: var(--rb-reg-surface-bg, rgb(var(--tb-surface, 248 250 252)));
55884
57108
  color: var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42))));
57109
+ --rb-surface-text-color: var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42))));
57110
+ --rb-surface-heading-color: var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42))));
57111
+ --rb-surface-link-color: var(--rb-reg-on-surface, rgb(var(--tb-onSurface, var(--tb-text, 15 23 42))));
57112
+ --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)))));
55885
57113
  transition: border-color var(--rb-block-transition, 150ms) ease;
55886
57114
  width: 100%;
55887
57115
  max-width: 800px;
@@ -58969,6 +60197,350 @@ const containerResponsiveThemeCss = `/*
58969
60197
  }
58970
60198
  }
58971
60199
 
60200
+ /* -----------------------------------------------------------------------------
60201
+ * Week Board View
60202
+ * Compact "class day-board": day columns side by side, one card per class,
60203
+ * three-state availability (available / filling / waitlist), and a footer CTA.
60204
+ * ----------------------------------------------------------------------------- */
60205
+
60206
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board {
60207
+ /* Status accent tokens — map the three availability states onto theme colours.
60208
+ * available -> secondary, filling -> accent, waitlist -> muted text. */
60209
+ --ec-status-available: rgb(var(--tb-secondary));
60210
+ --ec-status-filling: rgb(var(--tb-accent));
60211
+ --ec-status-waitlist: rgba(var(--tb-text), 0.4);
60212
+ --event-week-board-cols: 7;
60213
+
60214
+ border: 1px solid var(--ec-border);
60215
+ border-radius: var(--rb-block-radius);
60216
+ background-color: var(--ec-surface);
60217
+ color: var(--ec-text);
60218
+ overflow: hidden;
60219
+ box-shadow: 0 14px 34px -24px rgba(var(--tb-text), 0.35);
60220
+ }
60221
+
60222
+ /* Top bar: title + legend */
60223
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-top {
60224
+ display: flex;
60225
+ align-items: center;
60226
+ justify-content: space-between;
60227
+ gap: 1rem;
60228
+ flex-wrap: wrap;
60229
+ padding: 1.1rem 1.5rem;
60230
+ background-color: var(--ec-header-bg);
60231
+ border-bottom: 1px solid var(--ec-border-subtle);
60232
+ }
60233
+
60234
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-title {
60235
+ font-size: 0.8125rem;
60236
+ font-weight: 600;
60237
+ letter-spacing: 0.12em;
60238
+ text-transform: uppercase;
60239
+ color: rgb(var(--tb-primary));
60240
+ }
60241
+
60242
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend {
60243
+ display: flex;
60244
+ align-items: center;
60245
+ gap: 1.1rem;
60246
+ flex-wrap: wrap;
60247
+ font-size: 0.6875rem;
60248
+ letter-spacing: 0.03em;
60249
+ opacity: 0.75;
60250
+ }
60251
+
60252
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-item {
60253
+ display: inline-flex;
60254
+ align-items: center;
60255
+ gap: 0.4rem;
60256
+ }
60257
+
60258
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot {
60259
+ width: 0.55rem;
60260
+ height: 0.55rem;
60261
+ border-radius: 50%;
60262
+ display: inline-block;
60263
+ }
60264
+
60265
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot[data-status="available"] {
60266
+ background-color: var(--ec-status-available);
60267
+ }
60268
+
60269
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot[data-status="filling"] {
60270
+ background-color: var(--ec-status-filling);
60271
+ }
60272
+
60273
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend-dot[data-status="waitlist"] {
60274
+ background-color: var(--ec-status-waitlist);
60275
+ }
60276
+
60277
+ /* Day columns grid */
60278
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-grid {
60279
+ display: grid;
60280
+ grid-template-columns: repeat(var(--event-week-board-cols), minmax(0, 1fr));
60281
+ }
60282
+
60283
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day {
60284
+ display: flex;
60285
+ flex-direction: column;
60286
+ min-height: 15rem;
60287
+ /* Tabular column rule. Driven by the theme border tokens so borderless site
60288
+ * styles (border.width: 'none' -> --rb-border-width: 0) render no grid at all,
60289
+ * while hairline/thin/thick themes get a crisp full-strength divider. */
60290
+ border-right: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
60291
+ }
60292
+
60293
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day:last-child {
60294
+ border-right: none;
60295
+ }
60296
+
60297
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-head {
60298
+ display: flex;
60299
+ align-items: baseline;
60300
+ gap: 0.4rem;
60301
+ padding: 0.9rem 0.9rem 0.65rem;
60302
+ /* Header-row underline — same border-token gating as the column rules. */
60303
+ border-bottom: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
60304
+ }
60305
+
60306
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day--today .event-week-board-day-head {
60307
+ background-color: var(--ec-today-bg);
60308
+ }
60309
+
60310
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-abbr {
60311
+ font-size: 0.6875rem;
60312
+ font-weight: 600;
60313
+ letter-spacing: 0.1em;
60314
+ text-transform: uppercase;
60315
+ opacity: 0.65;
60316
+ }
60317
+
60318
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day--today .event-week-board-day-abbr {
60319
+ opacity: 1;
60320
+ color: var(--ec-today-text);
60321
+ }
60322
+
60323
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-num {
60324
+ font-size: 0.8125rem;
60325
+ font-weight: 600;
60326
+ opacity: 0.5;
60327
+ }
60328
+
60329
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-body {
60330
+ display: flex;
60331
+ flex-direction: column;
60332
+ gap: 0.65rem;
60333
+ padding: 0.9rem;
60334
+ flex: 1;
60335
+ }
60336
+
60337
+ /* Class card */
60338
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card {
60339
+ display: flex;
60340
+ flex-direction: column;
60341
+ gap: 0.45rem;
60342
+ padding: 0.8rem 0.85rem;
60343
+ border: 1px solid var(--ec-border-subtle);
60344
+ border-left: 3px solid var(--ec-status-available);
60345
+ border-radius: calc(var(--rb-block-radius) / 1.5);
60346
+ background-color: var(--ec-surface);
60347
+ color: inherit;
60348
+ text-decoration: none;
60349
+ transition:
60350
+ transform var(--rb-block-transition) ease,
60351
+ box-shadow var(--rb-block-transition) ease,
60352
+ border-color var(--rb-block-transition) ease;
60353
+ }
60354
+
60355
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card[data-status="filling"] {
60356
+ border-left-color: var(--ec-status-filling);
60357
+ }
60358
+
60359
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card[data-status="waitlist"] {
60360
+ border-left-color: var(--ec-status-waitlist);
60361
+ }
60362
+
60363
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card:hover {
60364
+ transform: translateY(-2px);
60365
+ box-shadow: 0 6px 16px -8px rgba(var(--tb-text), 0.28);
60366
+ border-color: rgba(var(--tb-primary), 0.4);
60367
+ }
60368
+
60369
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card:focus-visible {
60370
+ outline: 2px solid rgb(var(--tb-primary));
60371
+ outline-offset: 2px;
60372
+ }
60373
+
60374
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-title {
60375
+ font-family: var(--font-heading, inherit);
60376
+ font-size: 0.9375rem;
60377
+ font-weight: 600;
60378
+ line-height: 1.2;
60379
+ }
60380
+
60381
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-time {
60382
+ font-size: 0.75rem;
60383
+ letter-spacing: 0.01em;
60384
+ opacity: 0.7;
60385
+ }
60386
+
60387
+ /* Capacity pill */
60388
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-pill {
60389
+ align-self: flex-start;
60390
+ display: inline-flex;
60391
+ align-items: center;
60392
+ padding: 0.15rem 0.6rem;
60393
+ border-radius: 999px;
60394
+ font-size: 0.6875rem;
60395
+ font-weight: 600;
60396
+ letter-spacing: 0.02em;
60397
+ /* Available (default): a calm neutral/muted pill — not a coloured outline.
60398
+ * Filling -> amber and waitlist -> muted outline are handled by the
60399
+ * data-status overrides below. */
60400
+ border: 1px solid var(--ec-border);
60401
+ background-color: rgba(var(--tb-text), 0.06);
60402
+ color: rgba(var(--tb-text), 0.62);
60403
+ border-color: var(--ec-border);
60404
+ }
60405
+
60406
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-pill[data-status="filling"] {
60407
+ background-color: rgba(var(--tb-accent), 0.14);
60408
+ color: var(--ec-status-filling);
60409
+ border-color: rgba(var(--tb-accent), 0.32);
60410
+ }
60411
+
60412
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card-pill[data-status="waitlist"] {
60413
+ background-color: transparent;
60414
+ color: rgba(var(--tb-text), 0.6);
60415
+ border-color: var(--ec-border);
60416
+ }
60417
+
60418
+ /* Footer */
60419
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-foot {
60420
+ display: flex;
60421
+ align-items: center;
60422
+ justify-content: space-between;
60423
+ gap: 1rem;
60424
+ flex-wrap: wrap;
60425
+ padding: 1.1rem 1.5rem;
60426
+ background-color: var(--ec-header-bg);
60427
+ border-top: 1px solid var(--ec-border-subtle);
60428
+ }
60429
+
60430
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-foot-note {
60431
+ margin: 0;
60432
+ font-size: 0.875rem;
60433
+ opacity: 0.75;
60434
+ }
60435
+
60436
+ /* Responsive: collapse day columns to a stacked list on narrow viewports */
60437
+ @container rb-site (max-width: 1023px) {
60438
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
60439
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-grid {
60440
+ grid-template-columns: 1fr;
60441
+ }
60442
+
60443
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day {
60444
+ min-height: 0;
60445
+ border-right: none;
60446
+ border-bottom: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
60447
+ }
60448
+
60449
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day:last-child {
60450
+ border-bottom: none;
60451
+ }
60452
+
60453
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day--empty {
60454
+ display: none;
60455
+ }
60456
+
60457
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-head {
60458
+ border-bottom: none;
60459
+ padding-bottom: 0.25rem;
60460
+ }
60461
+
60462
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-day-body {
60463
+ flex-direction: row;
60464
+ flex-wrap: wrap;
60465
+ padding-top: 0.35rem;
60466
+ }
60467
+
60468
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card {
60469
+ flex: 1 1 14rem;
60470
+ }
60471
+ }
60472
+ }
60473
+
60474
+ @container rb-site (max-width: 639px) {
60475
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
60476
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-legend {
60477
+ display: none;
60478
+ }
60479
+
60480
+ :where(.theme-scope) :where([data-block="event-calendar"]) .event-week-board-card {
60481
+ flex: 1 1 100%;
60482
+ }
60483
+ }
60484
+ }
60485
+
60486
+ /* -----------------------------------------------------------------------------
60487
+ * Week-board section band
60488
+ * Give the whole week-board section (head + panel) a subtle surface band so it
60489
+ * reads as a distinct zone from the page — the "testimonials band" pattern.
60490
+ * Painted as a pseudo-element above the section's fallback background layer
60491
+ * (z-index -10) but behind the content, so no block-content wiring is needed.
60492
+ * Surface + hairline rules are token-referenced: on a borderless site style
60493
+ * (--rb-border-width: 0) the band keeps its surface tint but drops the rules.
60494
+ * ----------------------------------------------------------------------------- */
60495
+ :where(.theme-scope) [data-slot="section"]:has(.event-calendar--week-board)::before {
60496
+ content: "";
60497
+ position: absolute;
60498
+ inset: 0;
60499
+ z-index: -9;
60500
+ background-color: rgb(var(--tb-surfaceAlt));
60501
+ border-block: var(--rb-border-width, 1px) solid rgb(var(--tb-border));
60502
+ pointer-events: none;
60503
+ }
60504
+
60505
+ /* -----------------------------------------------------------------------------
60506
+ * Event section head — split (heading left, intro right)
60507
+ * Rendered by the block frame (outside [data-block="event-calendar"]) via
60508
+ * eventSectionHeaderLayoutNodes({ splitClassName }). Lays the heading and its
60509
+ * supporting intro out as a two-column head on wide viewports, stacking on
60510
+ * narrow ones. The section band above provides the distinct background.
60511
+ * ----------------------------------------------------------------------------- */
60512
+ :where(.theme-scope) .event-section-head--split {
60513
+ display: flex;
60514
+ align-items: flex-end;
60515
+ justify-content: space-between;
60516
+ gap: clamp(1.5rem, 4vw, 3.5rem);
60517
+ flex-wrap: wrap;
60518
+ }
60519
+
60520
+ :where(.theme-scope) .event-section-head--split > :where(h1, h2, h3) {
60521
+ margin: 0;
60522
+ }
60523
+
60524
+ :where(.theme-scope) .event-section-head--split > .rb-prose {
60525
+ flex: 1 1 22rem;
60526
+ max-width: 34rem;
60527
+ margin: 0;
60528
+ }
60529
+
60530
+ @container rb-site (max-width: 767px) {
60531
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
60532
+ :where(.theme-scope) .event-section-head--split {
60533
+ flex-direction: column;
60534
+ align-items: flex-start;
60535
+ gap: 0.75rem;
60536
+ }
60537
+
60538
+ :where(.theme-scope) .event-section-head--split > .rb-prose {
60539
+ flex-basis: auto;
60540
+ }
60541
+ }
60542
+ }
60543
+
58972
60544
  /* -----------------------------------------------------------------------------
58973
60545
  * Event Calendar Modals
58974
60546
  * Modal overlays for event details and day events list
@@ -59396,7 +60968,7 @@ const containerResponsiveThemeCss = `/*
59396
60968
  }
59397
60969
 
59398
60970
  :where(.theme-scope) .rb-inline-text-accent-contrast {
59399
- color: var(--section-text-color, rgb(var(--tb-text)));
60971
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
59400
60972
  }
59401
60973
 
59402
60974
  :where(.theme-scope) .rb-inline-text-highlight {
@@ -59407,7 +60979,7 @@ const containerResponsiveThemeCss = `/*
59407
60979
  }
59408
60980
 
59409
60981
  :where(.theme-scope) .rb-inline-text-highlight-soft {
59410
- background: color-mix(in oklab, var(--section-text-color, rgb(var(--tb-text))) 14%, transparent);
60982
+ background: color-mix(in oklab, var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39))) 14%, transparent);
59411
60983
  }
59412
60984
 
59413
60985
  :where(.theme-scope) .rb-inline-text-highlight-accent {
@@ -59426,7 +60998,7 @@ const containerResponsiveThemeCss = `/*
59426
60998
 
59427
60999
  /* Eyebrow - color only (typography via .rb-eyebrow) */
59428
61000
  :where(.theme-scope) :where([data-block="hero"]) .hero-eyebrow:not(.rb-eyebrow-treatment-accent) {
59429
- color: var(--section-text-color, rgb(var(--tb-text)));
61001
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
59430
61002
  }
59431
61003
 
59432
61004
  /* Subheadline - supporting text below headline */
@@ -59436,7 +61008,7 @@ const containerResponsiveThemeCss = `/*
59436
61008
 
59437
61009
  :where(.theme-scope) :where([data-block="hero"]) .hero-subheadline {
59438
61010
  font-size: 1.125rem;
59439
- color: var(--section-supporting-color, var(--section-text-color, rgb(var(--tb-text))));
61011
+ color: var(--rb-surface-supporting-color, var(--rb-surface-text-color, rgb(var(--tb-mutedText, 100 116 139))));
59440
61012
  }
59441
61013
 
59442
61014
  @container rb-site (min-width: 640px) {
@@ -59527,7 +61099,7 @@ const containerResponsiveThemeCss = `/*
59527
61099
 
59528
61100
  /* CTA content/body text */
59529
61101
  :where(.theme-scope) :where([data-block="cta-full"]) .cta-content {
59530
- color: var(--section-supporting-color, rgb(var(--tb-mutedText)));
61102
+ color: var(--rb-surface-supporting-color, var(--rb-surface-text-color, rgb(var(--tb-mutedText, 100 116 139))));
59531
61103
  }
59532
61104
 
59533
61105
  /* CTA button row container */
@@ -59627,7 +61199,7 @@ const containerResponsiveThemeCss = `/*
59627
61199
  /* Form intro text */
59628
61200
  :where(.theme-scope) :where([data-block="form"]) .form-copy-intro {
59629
61201
  font-size: 1rem;
59630
- color: var(--section-text-color, rgb(var(--tb-text)));
61202
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
59631
61203
  }
59632
61204
 
59633
61205
  /* =============================================================================
@@ -59986,6 +61558,10 @@ const containerResponsiveThemeCss = `/*
59986
61558
  --rb-modal-overlay-bg: rgb(var(--tb-text, 0 0 0) / 0.6);
59987
61559
  --rb-modal-shadow: var(--shadow-lg, var(--shadow-elev));
59988
61560
  --rb-modal-z-index: 20000;
61561
+ --rb-modal-text: rgb(var(--tb-text, 17 24 39));
61562
+ --rb-modal-heading: rgb(var(--tb-text, 17 24 39));
61563
+ --rb-modal-link: rgb(var(--tb-primary, var(--tb-text, 17 24 39)));
61564
+ --rb-modal-supporting: rgb(var(--tb-mutedText, 100 116 139));
59989
61565
  --shop-card-radius: var(--rb-block-radius, var(--radius-card, 0.75rem));
59990
61566
  --shop-control-radius: var(--rb-input-radius, var(--radius-control, 0.375rem));
59991
61567
  }
@@ -60043,6 +61619,11 @@ const containerResponsiveThemeCss = `/*
60043
61619
  /* Product card */
60044
61620
  :where(.theme-scope) .shop__card {
60045
61621
  background: rgb(var(--tb-surface));
61622
+ color: rgb(var(--tb-text, 17 24 39));
61623
+ --rb-surface-text-color: rgb(var(--tb-text, 17 24 39));
61624
+ --rb-surface-heading-color: rgb(var(--tb-text, 17 24 39));
61625
+ --rb-surface-link-color: rgb(var(--tb-primary, var(--tb-text, 17 24 39)));
61626
+ --rb-surface-supporting-color: rgb(var(--tb-mutedText, 100 116 139));
60046
61627
  border: 1px solid rgb(var(--tb-border));
60047
61628
  border-radius: var(--shop-card-radius);
60048
61629
  padding: 1.5rem;
@@ -60197,6 +61778,11 @@ const containerResponsiveThemeCss = `/*
60197
61778
  border: 1px solid rgb(var(--tb-border));
60198
61779
  border-radius: var(--shop-card-radius);
60199
61780
  background: rgb(var(--tb-surface));
61781
+ color: rgb(var(--tb-text, 17 24 39));
61782
+ --rb-surface-text-color: rgb(var(--tb-text, 17 24 39));
61783
+ --rb-surface-heading-color: rgb(var(--tb-text, 17 24 39));
61784
+ --rb-surface-link-color: rgb(var(--tb-primary, var(--tb-text, 17 24 39)));
61785
+ --rb-surface-supporting-color: rgb(var(--tb-mutedText, 100 116 139));
60200
61786
  }
60201
61787
 
60202
61788
  :where(.theme-scope) :where([data-block="shop"]) .rb-state-icon--success {
@@ -60269,6 +61855,11 @@ const containerResponsiveThemeCss = `/*
60269
61855
  margin: 0 auto;
60270
61856
  padding: 2rem;
60271
61857
  background: var(--cp-card-bg);
61858
+ color: var(--cp-text);
61859
+ --rb-surface-text-color: var(--cp-text);
61860
+ --rb-surface-heading-color: var(--cp-text);
61861
+ --rb-surface-link-color: var(--cp-primary);
61862
+ --rb-surface-supporting-color: var(--cp-muted);
60272
61863
  border: 1px solid var(--cp-card-border);
60273
61864
  border-radius: var(--cp-card-radius);
60274
61865
  }
@@ -60397,6 +61988,11 @@ const containerResponsiveThemeCss = `/*
60397
61988
  padding: 3rem 2rem;
60398
61989
  text-align: center;
60399
61990
  background: var(--cp-card-bg);
61991
+ color: var(--cp-text);
61992
+ --rb-surface-text-color: var(--cp-text);
61993
+ --rb-surface-heading-color: var(--cp-text);
61994
+ --rb-surface-link-color: var(--cp-primary);
61995
+ --rb-surface-supporting-color: var(--cp-muted);
60400
61996
  border: 1px dashed var(--cp-card-border);
60401
61997
  border-radius: var(--cp-card-radius);
60402
61998
  }
@@ -60409,6 +62005,11 @@ const containerResponsiveThemeCss = `/*
60409
62005
  /* Card */
60410
62006
  :where(.theme-scope) :where([data-block="customer-portal"]) .cp-card {
60411
62007
  background: var(--cp-card-bg);
62008
+ color: var(--cp-text);
62009
+ --rb-surface-text-color: var(--cp-text);
62010
+ --rb-surface-heading-color: var(--cp-text);
62011
+ --rb-surface-link-color: var(--cp-primary);
62012
+ --rb-surface-supporting-color: var(--cp-muted);
60412
62013
  border: 1px solid var(--cp-card-border);
60413
62014
  border-radius: var(--cp-card-radius);
60414
62015
  padding: 1.25rem;
@@ -60573,6 +62174,11 @@ const containerResponsiveThemeCss = `/*
60573
62174
  /* Table */
60574
62175
  :where(.theme-scope) :where([data-block="customer-portal"]) .cp-table-wrap {
60575
62176
  background: var(--cp-card-bg);
62177
+ color: var(--cp-text);
62178
+ --rb-surface-text-color: var(--cp-text);
62179
+ --rb-surface-heading-color: var(--cp-text);
62180
+ --rb-surface-link-color: var(--cp-primary);
62181
+ --rb-surface-supporting-color: var(--cp-muted);
60576
62182
  border: 1px solid var(--cp-card-border);
60577
62183
  border-radius: var(--cp-card-radius);
60578
62184
  }
@@ -61134,6 +62740,143 @@ const containerResponsiveThemeCss = `/*
61134
62740
  }
61135
62741
 
61136
62742
 
62743
+ /* =============================================================================
62744
+ * Variant: featured-editorial
62745
+ *
62746
+ * An asymmetric layout: a large featured (spruce-ink) quote card beside a
62747
+ * column of stacked white cards. Renders NO avatars. Colours are palette
62748
+ * tokens only. Node structure is built in testimonialsCarousel.ts.
62749
+ * ============================================================================= */
62750
+
62751
+ /* Section head: heading left, lead-in line to the right; stacks on mobile. */
62752
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-head {
62753
+ display: flex;
62754
+ flex-direction: column;
62755
+ align-items: flex-start;
62756
+ gap: 0.875rem;
62757
+ }
62758
+
62759
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-leadin {
62760
+ max-width: 30rem;
62761
+ }
62762
+
62763
+ @container rb-site (min-width: 768px) {
62764
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
62765
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-head {
62766
+ flex-direction: row;
62767
+ align-items: flex-end;
62768
+ justify-content: space-between;
62769
+ gap: 1.5rem;
62770
+ }
62771
+ }
62772
+ }
62773
+
62774
+ /* Card grid: featured (wider) beside the stacked column. Single column on
62775
+ * small screens. */
62776
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-grid {
62777
+ display: grid;
62778
+ grid-template-columns: minmax(0, 1fr);
62779
+ gap: 1.25rem;
62780
+ align-items: stretch;
62781
+ }
62782
+
62783
+ @container rb-site (min-width: 1024px) {
62784
+ @scope ([data-rb-responsive-mode="container"]) to (:scope [data-rb-responsive-mode]) {
62785
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-grid {
62786
+ grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
62787
+ }
62788
+ }
62789
+ }
62790
+
62791
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-col {
62792
+ display: grid;
62793
+ gap: 1.25rem;
62794
+ align-content: start;
62795
+ }
62796
+
62797
+ /* Base card (white surface) */
62798
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-card {
62799
+ display: flex;
62800
+ flex-direction: column;
62801
+ height: 100%;
62802
+ border-radius: var(--rb-block-radius);
62803
+ padding: 1.875rem 1.875rem 1.625rem;
62804
+ background: rgb(var(--tb-surface));
62805
+ border: 1px solid rgba(var(--tb-border), 0.9);
62806
+ box-shadow: 0 1px 2px rgba(var(--tb-shadow, 0 0 0), 0.05);
62807
+ }
62808
+
62809
+ /* Featured (spruce-ink) card */
62810
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-card--feature {
62811
+ padding: 2.5rem;
62812
+ background: rgb(var(--tb-primary));
62813
+ border-color: transparent;
62814
+ color: rgb(var(--tb-onPrimary));
62815
+ }
62816
+
62817
+ /* Amber quote mark, oversized serif */
62818
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-qmark {
62819
+ display: block;
62820
+ height: 1.6rem;
62821
+ font-family: var(--font-heading, Georgia, serif);
62822
+ font-style: italic;
62823
+ font-size: 3.375rem;
62824
+ line-height: 0.6;
62825
+ color: rgb(var(--tb-secondary));
62826
+ }
62827
+
62828
+ /* Quote body. The inner <p> is targeted directly (not via :where) so the
62829
+ * serif display font and sizing win over the theme's base paragraph rule. */
62830
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-quote {
62831
+ flex: 1 1 auto;
62832
+ margin-top: 1.25rem;
62833
+ }
62834
+
62835
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-quote p {
62836
+ margin: 0;
62837
+ font-family: var(--font-heading, Georgia, serif);
62838
+ font-size: 1.15rem;
62839
+ line-height: 1.5;
62840
+ color: rgb(var(--tb-text));
62841
+ }
62842
+
62843
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-card--feature .rb-testimonials-fe-quote p {
62844
+ color: rgb(var(--tb-onPrimary));
62845
+ font-size: clamp(1.375rem, 2.4vw, 1.6875rem);
62846
+ line-height: 1.42;
62847
+ letter-spacing: -0.01em;
62848
+ }
62849
+
62850
+ /* Attribution: name (bold) + context line, no avatar */
62851
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-meta {
62852
+ display: flex;
62853
+ flex-direction: column;
62854
+ gap: 0.25rem;
62855
+ margin-top: 1.625rem;
62856
+ }
62857
+
62858
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-name {
62859
+ font-weight: 600;
62860
+ font-size: 0.97rem;
62861
+ color: rgb(var(--tb-text));
62862
+ }
62863
+
62864
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-context {
62865
+ font-size: 0.78rem;
62866
+ letter-spacing: 0.02em;
62867
+ color: rgb(var(--tb-mutedText));
62868
+ }
62869
+
62870
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-card--feature .rb-testimonials-fe-name {
62871
+ color: rgb(var(--tb-onPrimary));
62872
+ }
62873
+
62874
+ :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-fe-card--feature .rb-testimonials-fe-context {
62875
+ color: rgb(var(--tb-onPrimary));
62876
+ opacity: 0.7;
62877
+ }
62878
+
62879
+
61137
62880
  /* Testimonials carousel tweaks: arrow styling + gutters + equal-height cards */
61138
62881
  :where(.theme-scope) :where([data-block="testimonials"]) .rb-testimonials-carousel.carousel-root .carousel-nav-btn {
61139
62882
  height: var(--rb-testimonial-carousel-arrow-size);
@@ -62280,7 +64023,7 @@ const containerResponsiveThemeCss = `/*
62280
64023
 
62281
64024
  :where(.theme-scope) :where([data-block="newsletter-signup"]) .newsletter-copy-subtitle {
62282
64025
  font-size: 1rem;
62283
- color: var(--section-text-color, rgb(var(--tb-text)));
64026
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
62284
64027
  opacity: 0.85;
62285
64028
  }
62286
64029
 
@@ -62329,7 +64072,7 @@ const containerResponsiveThemeCss = `/*
62329
64072
  margin-top: 0.75rem;
62330
64073
  font-size: 1rem;
62331
64074
  line-height: 1.6;
62332
- color: var(--section-text-color, rgb(var(--tb-text)));
64075
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
62333
64076
  text-align: center;
62334
64077
  }
62335
64078
 
@@ -62411,7 +64154,7 @@ const containerResponsiveThemeCss = `/*
62411
64154
 
62412
64155
  :where(.theme-scope) :where([data-block="contact-newsletter"]) .form-copy-intro,
62413
64156
  :where(.theme-scope) :where([data-block="contact-newsletter"]) .newsletter-copy-subtitle {
62414
- color: var(--section-text-color, rgb(var(--tb-text)));
64157
+ color: var(--rb-surface-text-color, rgb(var(--tb-text, 17 24 39)));
62415
64158
  }
62416
64159
 
62417
64160
  :where(.theme-scope) :where([data-block="contact-newsletter"]) .contact-newsletter-info {
@@ -62809,7 +64552,7 @@ const containerResponsiveThemeCss = `/*
62809
64552
 
62810
64553
  :where(.theme-scope) .event-compact-row-title {
62811
64554
  margin: 0;
62812
- color: var(--section-heading-color, rgb(var(--tb-primary, 0 0 0)));
64555
+ color: var(--rb-surface-heading-color, var(--rb-surface-text-color, rgb(var(--tb-primary, var(--tb-text, 17 24 39)))));
62813
64556
  font-family: var(--font-heading, var(--font-body, inherit));
62814
64557
  font-size: clamp(1.05rem, 1rem + 0.35vw, 1.3rem);
62815
64558
  font-weight: 400;
@@ -63471,27 +65214,41 @@ const containerResponsiveThemeCss = `/*
63471
65214
  background: rgb(var(--tb-surface));
63472
65215
  }
63473
65216
  :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 1) {
65217
+ background: rgb(var(--tb-surface));
63474
65218
  color: rgb(var(--tb-onSurface));
65219
+ --rb-surface-text-color: rgb(var(--tb-onSurface));
65220
+ --rb-surface-heading-color: rgb(var(--tb-onSurface));
65221
+ --rb-surface-link-color: rgb(var(--tb-onSurface));
65222
+ --rb-surface-supporting-color: rgb(var(--tb-onSurface));
63475
65223
  }
63476
65224
 
63477
65225
  :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 2) > .rb-bg-surface {
63478
65226
  background: rgb(var(--tb-secondary));
63479
65227
  }
63480
65228
  :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 2) {
65229
+ background: rgb(var(--tb-secondary));
63481
65230
  color: rgb(var(--tb-onSecondary));
65231
+ --rb-surface-text-color: rgb(var(--tb-onSecondary));
65232
+ --rb-surface-heading-color: rgb(var(--tb-onSecondary));
65233
+ --rb-surface-link-color: rgb(var(--tb-onSecondary));
65234
+ --rb-surface-supporting-color: rgb(var(--tb-onSecondary));
63482
65235
  }
63483
65236
 
63484
65237
  :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 3) > .rb-bg-surface {
63485
65238
  background: rgb(var(--tb-primary));
63486
65239
  }
63487
65240
  :where(.theme-scope) .rb-card-treatment-color-blocked:nth-child(3n + 3) {
65241
+ background: rgb(var(--tb-primary));
63488
65242
  color: rgb(var(--tb-onPrimary));
65243
+ --rb-surface-text-color: rgb(var(--tb-onPrimary));
65244
+ --rb-surface-heading-color: rgb(var(--tb-onPrimary));
65245
+ --rb-surface-link-color: rgb(var(--tb-onPrimary));
65246
+ --rb-surface-supporting-color: rgb(var(--tb-onPrimary));
63489
65247
  }
63490
65248
 
63491
- /* Body copy carries an inline muted colour from the block; force it to the
63492
- * card's on-colour so it reads on every block (inline style needs !important). */
65249
+ /* Keep the intended visual hierarchy; the body color itself now resolves via
65250
+ * the nearest surface provider instead of requiring an inline override. */
63493
65251
  :where(.theme-scope) .rb-card-treatment-color-blocked .rb-feature-card-body {
63494
- color: inherit !important;
63495
65252
  opacity: 0.9;
63496
65253
  }
63497
65254
 
@@ -65454,6 +67211,8 @@ const VERTICAL_IDS = [
65454
67211
  "yoga_wellness",
65455
67212
  "fitness_pt",
65456
67213
  "dance_school",
67214
+ "physiotherapy_physical_health",
67215
+ "massage_bodywork",
65457
67216
  "therapist_practitioner",
65458
67217
  "beauty_spa",
65459
67218
  "consultant_coach",
@@ -65489,6 +67248,12 @@ const VERTICAL_BOOKING_PRESETS = [
65489
67248
  preset("dance_school.workshops", "dance_school", "workshop", "Workshop", "Workshops", "One-off workshops, masterclasses, or intensives."),
65490
67249
  preset("dance_school.private_lessons", "dance_school", "appointment", "Private lesson", "Private lessons", "One-to-one lessons or coaching sessions."),
65491
67250
  preset("dance_school.shows", "dance_school", "performance", "Show", "Shows", "Ticketed performances, showcases, and recitals."),
67251
+ preset("physiotherapy_physical_health.appointments", "physiotherapy_physical_health", "appointment", "Appointment", "Appointments", "Assessments, treatments, follow-ups, and rehabilitation sessions."),
67252
+ preset("physiotherapy_physical_health.rehab_classes", "physiotherapy_physical_health", "class", "Rehab class", "Rehab classes", "Regular rehabilitation, mobility, strength, or recovery classes."),
67253
+ preset("physiotherapy_physical_health.workshops", "physiotherapy_physical_health", "workshop", "Workshop", "Workshops", "One-off workshops, talks, or practical group sessions."),
67254
+ preset("massage_bodywork.treatments", "massage_bodywork", "appointment", "Treatment", "Treatments", "Massage treatments, bodywork sessions, and one-to-one appointments."),
67255
+ preset("massage_bodywork.group_sessions", "massage_bodywork", "class", "Group session", "Group sessions", "Regular bodywork, somatic movement, or self-massage sessions."),
67256
+ preset("massage_bodywork.workshops", "massage_bodywork", "workshop", "Workshop", "Workshops", "One-off bodywork, self-massage, or somatic practice workshops."),
65492
67257
  preset("therapist_practitioner.appointments", "therapist_practitioner", "appointment", "Appointment", "Appointments", "Sessions, consultations, treatments, or assessments."),
65493
67258
  preset("therapist_practitioner.workshops", "therapist_practitioner", "workshop", "Workshop", "Workshops", "One-off group workshops, talks, or wellbeing sessions."),
65494
67259
  preset("therapist_practitioner.retreats", "therapist_practitioner", "retreat", "Retreat", "Retreats", "Multi-day retreats or immersive group experiences."),