@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
@@ -2,6 +2,225 @@ import "react/jsx-runtime";
2
2
  import z, { z as z$1 } from "zod";
3
3
  import * as fs from "fs/promises";
4
4
  import * as path from "path";
5
+ //#region ../theme-core/src/tokens/colorTokenParts.ts
6
+ function parseOpacityPercent(source) {
7
+ const trimmed = source.trim();
8
+ if (!trimmed) return null;
9
+ if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
10
+ const value = Number(trimmed);
11
+ if (!Number.isFinite(value)) return null;
12
+ if (value < 0 || value > 100) return null;
13
+ return value;
14
+ }
15
+ function parseColorTokenParts(source) {
16
+ const trimmed = source.trim();
17
+ if (trimmed.startsWith("raw:")) return {
18
+ kind: "raw",
19
+ value: trimmed.slice(4)
20
+ };
21
+ const [token, opacityRaw, extra] = trimmed.split("/");
22
+ if (!token || extra !== void 0) return {
23
+ kind: "invalid",
24
+ token: trimmed
25
+ };
26
+ if (opacityRaw === void 0) return {
27
+ kind: "token",
28
+ token,
29
+ opacityPercent: null
30
+ };
31
+ const opacityPercent = parseOpacityPercent(opacityRaw);
32
+ if (opacityPercent === null) return {
33
+ kind: "invalid",
34
+ token: trimmed
35
+ };
36
+ return {
37
+ kind: "token",
38
+ token,
39
+ opacityPercent
40
+ };
41
+ }
42
+ //#endregion
43
+ //#region ../theme-core/src/tokens/blockColorTokens.ts
44
+ const blockColorTokenNames = [
45
+ "text",
46
+ "heading",
47
+ "mutedText",
48
+ "surface",
49
+ "background",
50
+ "foreground",
51
+ "border",
52
+ "primary",
53
+ "secondary",
54
+ "accent",
55
+ "success",
56
+ "warning",
57
+ "info",
58
+ "error",
59
+ "destructive",
60
+ "scrim",
61
+ "shadow",
62
+ "onPrimary",
63
+ "onSecondary",
64
+ "onAccent",
65
+ "onSuccess",
66
+ "onWarning",
67
+ "onInfo",
68
+ "onError",
69
+ "onDestructive",
70
+ "onSurface"
71
+ ];
72
+ new Set(blockColorTokenNames);
73
+ //#endregion
74
+ //#region ../theme-core/src/tokens/themeColorTokens.ts
75
+ /**
76
+ * Known palette role token names. This is the low-level source of truth for
77
+ * palette roles used by palette variants, Theme V2 schemas, and runtime color
78
+ * reference parsing.
79
+ */
80
+ const PALETTE_TOKEN_NAMES = [
81
+ "primary",
82
+ "primaryForeground",
83
+ "secondary",
84
+ "accent",
85
+ "success",
86
+ "warning",
87
+ "destructive",
88
+ "background",
89
+ "surface",
90
+ "surfaceAlt",
91
+ "muted",
92
+ "text",
93
+ "border"
94
+ ];
95
+ const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
96
+ const themeColorBaseTokenNames = [
97
+ ...PALETTE_TOKEN_NAMES,
98
+ ...blockColorTokenNames,
99
+ "white",
100
+ "neutral",
101
+ "primaryText",
102
+ "secondaryForeground",
103
+ "accentForeground"
104
+ ];
105
+ const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
106
+ const themeColorShadeSteps = [
107
+ "50",
108
+ "100",
109
+ "200",
110
+ "300",
111
+ "400",
112
+ "500",
113
+ "600",
114
+ "700",
115
+ "800",
116
+ "900",
117
+ "950"
118
+ ];
119
+ const shadeStepSet = new Set(themeColorShadeSteps);
120
+ function isKnownBaseToken(value) {
121
+ return themeColorBaseTokenSet.has(value);
122
+ }
123
+ function isKnownShadeToken(value) {
124
+ const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
125
+ if (!match) return false;
126
+ const [, base, shade] = match;
127
+ return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
128
+ }
129
+ function isThemeColorTokenBase(value) {
130
+ return isKnownBaseToken(value) || isKnownShadeToken(value);
131
+ }
132
+ function isThemeColorToken(value) {
133
+ if (typeof value !== "string") return false;
134
+ const parts = parseColorTokenParts(value);
135
+ return parts.kind === "token" && isThemeColorTokenBase(parts.token);
136
+ }
137
+ function parseThemeColorToken(value) {
138
+ if (!isThemeColorToken(value)) return null;
139
+ return value.trim();
140
+ }
141
+ const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
142
+ //#endregion
143
+ //#region ../theme-core/src/palette/colors.ts
144
+ const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
145
+ const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
146
+ const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
147
+ const paletteModeSchema = z$1.enum(["light", "dark"]);
148
+ z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
149
+ const paletteColorsSchema = z$1.object({
150
+ primary: hexColorSchema,
151
+ primaryForeground: hexColorSchema,
152
+ secondary: hexColorSchema,
153
+ accent: hexColorSchema,
154
+ success: hexColorSchema,
155
+ warning: hexColorSchema,
156
+ destructive: hexColorSchema,
157
+ background: hexColorSchema,
158
+ surface: hexColorSchema,
159
+ surfaceAlt: hexColorSchema,
160
+ muted: hexColorSchema,
161
+ text: hexColorSchema,
162
+ border: hexColorSchema
163
+ }).strict().transform((value) => value);
164
+ function normalizePaletteHex(value) {
165
+ if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
166
+ const normalized = value.toLowerCase();
167
+ if (normalized.length === 7) return normalized;
168
+ const triplet = normalized.slice(1);
169
+ const red = triplet.charAt(0);
170
+ const green = triplet.charAt(1);
171
+ const blue = triplet.charAt(2);
172
+ return `#${red}${red}${green}${green}${blue}${blue}`;
173
+ }
174
+ function paletteColorsFromEntries(entries) {
175
+ const values = {};
176
+ for (const entry of entries) {
177
+ const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
178
+ if (!parsedRole.success) return {
179
+ ok: false,
180
+ error: {
181
+ kind: "unknown-role",
182
+ role: entry.name
183
+ }
184
+ };
185
+ const role = parsedRole.data;
186
+ if (values[role] !== void 0) return {
187
+ ok: false,
188
+ error: {
189
+ kind: "duplicate-role",
190
+ role
191
+ }
192
+ };
193
+ const hex = normalizePaletteHex(entry.hex);
194
+ if (!hex) return {
195
+ ok: false,
196
+ error: {
197
+ kind: "invalid-color",
198
+ role,
199
+ value: entry.hex
200
+ }
201
+ };
202
+ values[role] = hex;
203
+ }
204
+ const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
205
+ if (missing.length > 0) return {
206
+ ok: false,
207
+ error: {
208
+ kind: "missing-roles",
209
+ roles: missing
210
+ }
211
+ };
212
+ return {
213
+ ok: true,
214
+ colors: paletteColorsSchema.parse(values)
215
+ };
216
+ }
217
+ function requirePaletteColorsFromEntries(entries) {
218
+ const result = paletteColorsFromEntries(entries);
219
+ if (result.ok) return result.colors;
220
+ throw new Error(`Invalid complete palette: ${result.error.kind}`);
221
+ }
222
+ new Set(PALETTE_TOKEN_NAMES);
223
+ //#endregion
5
224
  //#region ../../node_modules/.pnpm/@vibrant+image@4.0.4/node_modules/@vibrant/image/dist/esm/histogram.js
6
225
  var Histogram = class {
7
226
  constructor(pixels, opts) {
@@ -955,304 +1174,86 @@ function _findColorVariation(palette, swatches, maxPopulation, targetLuma, minLu
955
1174
  }
956
1175
  }
957
1176
  });
958
- return max;
959
- }
960
- function _generateVariationColors(swatches, maxPopulation, opts) {
961
- const palette = {
962
- Vibrant: null,
963
- DarkVibrant: null,
964
- LightVibrant: null,
965
- Muted: null,
966
- DarkMuted: null,
967
- LightMuted: null
968
- };
969
- palette.Vibrant = _findColorVariation(palette, swatches, maxPopulation, opts.targetNormalLuma, opts.minNormalLuma, opts.maxNormalLuma, opts.targetVibrantSaturation, opts.minVibrantSaturation, 1, opts);
970
- palette.LightVibrant = _findColorVariation(palette, swatches, maxPopulation, opts.targetLightLuma, opts.minLightLuma, 1, opts.targetVibrantSaturation, opts.minVibrantSaturation, 1, opts);
971
- palette.DarkVibrant = _findColorVariation(palette, swatches, maxPopulation, opts.targetDarkLuma, 0, opts.maxDarkLuma, opts.targetVibrantSaturation, opts.minVibrantSaturation, 1, opts);
972
- palette.Muted = _findColorVariation(palette, swatches, maxPopulation, opts.targetNormalLuma, opts.minNormalLuma, opts.maxNormalLuma, opts.targetMutesSaturation, 0, opts.maxMutesSaturation, opts);
973
- palette.LightMuted = _findColorVariation(palette, swatches, maxPopulation, opts.targetLightLuma, opts.minLightLuma, 1, opts.targetMutesSaturation, 0, opts.maxMutesSaturation, opts);
974
- palette.DarkMuted = _findColorVariation(palette, swatches, maxPopulation, opts.targetDarkLuma, 0, opts.maxDarkLuma, opts.targetMutesSaturation, 0, opts.maxMutesSaturation, opts);
975
- return palette;
976
- }
977
- function _generateEmptySwatches(palette, _maxPopulation, opts) {
978
- if (!palette.Vibrant && !palette.DarkVibrant && !palette.LightVibrant) {
979
- if (!palette.DarkVibrant && palette.DarkMuted) {
980
- let [h, s, l] = palette.DarkMuted.hsl;
981
- l = opts.targetDarkLuma;
982
- palette.DarkVibrant = new Swatch(hslToRgb(h, s, l), 0);
983
- }
984
- if (!palette.LightVibrant && palette.LightMuted) {
985
- let [h, s, l] = palette.LightMuted.hsl;
986
- l = opts.targetDarkLuma;
987
- palette.DarkVibrant = new Swatch(hslToRgb(h, s, l), 0);
988
- }
989
- }
990
- if (!palette.Vibrant && palette.DarkVibrant) {
991
- let [h, s, l] = palette.DarkVibrant.hsl;
992
- l = opts.targetNormalLuma;
993
- palette.Vibrant = new Swatch(hslToRgb(h, s, l), 0);
994
- } else if (!palette.Vibrant && palette.LightVibrant) {
995
- let [h, s, l] = palette.LightVibrant.hsl;
996
- l = opts.targetNormalLuma;
997
- palette.Vibrant = new Swatch(hslToRgb(h, s, l), 0);
998
- }
999
- if (!palette.DarkVibrant && palette.Vibrant) {
1000
- let [h, s, l] = palette.Vibrant.hsl;
1001
- l = opts.targetDarkLuma;
1002
- palette.DarkVibrant = new Swatch(hslToRgb(h, s, l), 0);
1003
- }
1004
- if (!palette.LightVibrant && palette.Vibrant) {
1005
- let [h, s, l] = palette.Vibrant.hsl;
1006
- l = opts.targetLightLuma;
1007
- palette.LightVibrant = new Swatch(hslToRgb(h, s, l), 0);
1008
- }
1009
- if (!palette.Muted && palette.Vibrant) {
1010
- let [h, s, l] = palette.Vibrant.hsl;
1011
- l = opts.targetMutesSaturation;
1012
- palette.Muted = new Swatch(hslToRgb(h, s, l), 0);
1013
- }
1014
- if (!palette.DarkMuted && palette.DarkVibrant) {
1015
- let [h, s, l] = palette.DarkVibrant.hsl;
1016
- l = opts.targetMutesSaturation;
1017
- palette.DarkMuted = new Swatch(hslToRgb(h, s, l), 0);
1018
- }
1019
- if (!palette.LightMuted && palette.LightVibrant) {
1020
- let [h, s, l] = palette.LightVibrant.hsl;
1021
- l = opts.targetMutesSaturation;
1022
- palette.LightMuted = new Swatch(hslToRgb(h, s, l), 0);
1023
- }
1024
- }
1025
- const DefaultGenerator = ((swatches, opts) => {
1026
- opts = Object.assign({}, DefaultOpts, opts);
1027
- const maxPopulation = _findMaxPopulation(swatches);
1028
- const palette = _generateVariationColors(swatches, maxPopulation, opts);
1029
- _generateEmptySwatches(palette, maxPopulation, opts);
1030
- return palette;
1031
- });
1032
- //#endregion
1033
- //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/pipeline/index.js
1034
- const pipeline = new BasicPipeline().filter.register("default", (r, g, b, a) => a >= 125 && !(r > 250 && g > 250 && b > 250)).quantizer.register("mmcq", MMCQ).generator.register("default", DefaultGenerator);
1035
- //#endregion
1036
- //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/browser.js
1037
- Vibrant.use(pipeline);
1038
- //#endregion
1039
- //#region ../theme-core/src/tokens/colorTokenParts.ts
1040
- function parseOpacityPercent(source) {
1041
- const trimmed = source.trim();
1042
- if (!trimmed) return null;
1043
- if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
1044
- const value = Number(trimmed);
1045
- if (!Number.isFinite(value)) return null;
1046
- if (value < 0 || value > 100) return null;
1047
- return value;
1048
- }
1049
- function parseColorTokenParts(source) {
1050
- const trimmed = source.trim();
1051
- if (trimmed.startsWith("raw:")) return {
1052
- kind: "raw",
1053
- value: trimmed.slice(4)
1054
- };
1055
- const [token, opacityRaw, extra] = trimmed.split("/");
1056
- if (!token || extra !== void 0) return {
1057
- kind: "invalid",
1058
- token: trimmed
1059
- };
1060
- if (opacityRaw === void 0) return {
1061
- kind: "token",
1062
- token,
1063
- opacityPercent: null
1064
- };
1065
- const opacityPercent = parseOpacityPercent(opacityRaw);
1066
- if (opacityPercent === null) return {
1067
- kind: "invalid",
1068
- token: trimmed
1069
- };
1070
- return {
1071
- kind: "token",
1072
- token,
1073
- opacityPercent
1074
- };
1075
- }
1076
- //#endregion
1077
- //#region ../theme-core/src/tokens/blockColorTokens.ts
1078
- const blockColorTokenNames = [
1079
- "text",
1080
- "heading",
1081
- "mutedText",
1082
- "surface",
1083
- "background",
1084
- "foreground",
1085
- "border",
1086
- "primary",
1087
- "secondary",
1088
- "accent",
1089
- "success",
1090
- "warning",
1091
- "info",
1092
- "error",
1093
- "destructive",
1094
- "scrim",
1095
- "shadow",
1096
- "onPrimary",
1097
- "onSecondary",
1098
- "onAccent",
1099
- "onSuccess",
1100
- "onWarning",
1101
- "onInfo",
1102
- "onError",
1103
- "onDestructive",
1104
- "onSurface"
1105
- ];
1106
- new Set(blockColorTokenNames);
1107
- //#endregion
1108
- //#region ../theme-core/src/tokens/themeColorTokens.ts
1109
- /**
1110
- * Known palette role token names. This is the low-level source of truth for
1111
- * palette roles used by palette variants, Theme V2 schemas, and runtime color
1112
- * reference parsing.
1113
- */
1114
- const PALETTE_TOKEN_NAMES = [
1115
- "primary",
1116
- "primaryForeground",
1117
- "secondary",
1118
- "accent",
1119
- "success",
1120
- "warning",
1121
- "destructive",
1122
- "background",
1123
- "surface",
1124
- "surfaceAlt",
1125
- "muted",
1126
- "text",
1127
- "border"
1128
- ];
1129
- const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
1130
- const themeColorBaseTokenNames = [
1131
- ...PALETTE_TOKEN_NAMES,
1132
- ...blockColorTokenNames,
1133
- "white",
1134
- "neutral",
1135
- "primaryText",
1136
- "secondaryForeground",
1137
- "accentForeground"
1138
- ];
1139
- const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
1140
- const themeColorShadeSteps = [
1141
- "50",
1142
- "100",
1143
- "200",
1144
- "300",
1145
- "400",
1146
- "500",
1147
- "600",
1148
- "700",
1149
- "800",
1150
- "900",
1151
- "950"
1152
- ];
1153
- const shadeStepSet = new Set(themeColorShadeSteps);
1154
- function isKnownBaseToken(value) {
1155
- return themeColorBaseTokenSet.has(value);
1156
- }
1157
- function isKnownShadeToken(value) {
1158
- const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
1159
- if (!match) return false;
1160
- const [, base, shade] = match;
1161
- return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
1162
- }
1163
- function isThemeColorTokenBase(value) {
1164
- return isKnownBaseToken(value) || isKnownShadeToken(value);
1165
- }
1166
- function isThemeColorToken(value) {
1167
- if (typeof value !== "string") return false;
1168
- const parts = parseColorTokenParts(value);
1169
- return parts.kind === "token" && isThemeColorTokenBase(parts.token);
1170
- }
1171
- function parseThemeColorToken(value) {
1172
- if (!isThemeColorToken(value)) return null;
1173
- return value.trim();
1174
- }
1175
- const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
1176
- //#endregion
1177
- //#region ../theme-core/src/palette/colors.ts
1178
- const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
1179
- const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
1180
- const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
1181
- const paletteModeSchema = z$1.enum(["light", "dark"]);
1182
- z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
1183
- const paletteColorsSchema = z$1.object({
1184
- primary: hexColorSchema,
1185
- primaryForeground: hexColorSchema,
1186
- secondary: hexColorSchema,
1187
- accent: hexColorSchema,
1188
- success: hexColorSchema,
1189
- warning: hexColorSchema,
1190
- destructive: hexColorSchema,
1191
- background: hexColorSchema,
1192
- surface: hexColorSchema,
1193
- surfaceAlt: hexColorSchema,
1194
- muted: hexColorSchema,
1195
- text: hexColorSchema,
1196
- border: hexColorSchema
1197
- }).strict().transform((value) => value);
1198
- function normalizePaletteHex(value) {
1199
- if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
1200
- const normalized = value.toLowerCase();
1201
- if (normalized.length === 7) return normalized;
1202
- const triplet = normalized.slice(1);
1203
- const red = triplet.charAt(0);
1204
- const green = triplet.charAt(1);
1205
- const blue = triplet.charAt(2);
1206
- return `#${red}${red}${green}${green}${blue}${blue}`;
1207
- }
1208
- function paletteColorsFromEntries(entries) {
1209
- const values = {};
1210
- for (const entry of entries) {
1211
- const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
1212
- if (!parsedRole.success) return {
1213
- ok: false,
1214
- error: {
1215
- kind: "unknown-role",
1216
- role: entry.name
1217
- }
1218
- };
1219
- const role = parsedRole.data;
1220
- if (values[role] !== void 0) return {
1221
- ok: false,
1222
- error: {
1223
- kind: "duplicate-role",
1224
- role
1225
- }
1226
- };
1227
- const hex = normalizePaletteHex(entry.hex);
1228
- if (!hex) return {
1229
- ok: false,
1230
- error: {
1231
- kind: "invalid-color",
1232
- role,
1233
- value: entry.hex
1234
- }
1235
- };
1236
- values[role] = hex;
1237
- }
1238
- const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
1239
- if (missing.length > 0) return {
1240
- ok: false,
1241
- error: {
1242
- kind: "missing-roles",
1243
- roles: missing
1244
- }
1245
- };
1246
- return {
1247
- ok: true,
1248
- colors: paletteColorsSchema.parse(values)
1177
+ return max;
1178
+ }
1179
+ function _generateVariationColors(swatches, maxPopulation, opts) {
1180
+ const palette = {
1181
+ Vibrant: null,
1182
+ DarkVibrant: null,
1183
+ LightVibrant: null,
1184
+ Muted: null,
1185
+ DarkMuted: null,
1186
+ LightMuted: null
1249
1187
  };
1188
+ palette.Vibrant = _findColorVariation(palette, swatches, maxPopulation, opts.targetNormalLuma, opts.minNormalLuma, opts.maxNormalLuma, opts.targetVibrantSaturation, opts.minVibrantSaturation, 1, opts);
1189
+ palette.LightVibrant = _findColorVariation(palette, swatches, maxPopulation, opts.targetLightLuma, opts.minLightLuma, 1, opts.targetVibrantSaturation, opts.minVibrantSaturation, 1, opts);
1190
+ palette.DarkVibrant = _findColorVariation(palette, swatches, maxPopulation, opts.targetDarkLuma, 0, opts.maxDarkLuma, opts.targetVibrantSaturation, opts.minVibrantSaturation, 1, opts);
1191
+ palette.Muted = _findColorVariation(palette, swatches, maxPopulation, opts.targetNormalLuma, opts.minNormalLuma, opts.maxNormalLuma, opts.targetMutesSaturation, 0, opts.maxMutesSaturation, opts);
1192
+ palette.LightMuted = _findColorVariation(palette, swatches, maxPopulation, opts.targetLightLuma, opts.minLightLuma, 1, opts.targetMutesSaturation, 0, opts.maxMutesSaturation, opts);
1193
+ palette.DarkMuted = _findColorVariation(palette, swatches, maxPopulation, opts.targetDarkLuma, 0, opts.maxDarkLuma, opts.targetMutesSaturation, 0, opts.maxMutesSaturation, opts);
1194
+ return palette;
1250
1195
  }
1251
- function requirePaletteColorsFromEntries(entries) {
1252
- const result = paletteColorsFromEntries(entries);
1253
- if (result.ok) return result.colors;
1254
- throw new Error(`Invalid complete palette: ${result.error.kind}`);
1196
+ function _generateEmptySwatches(palette, _maxPopulation, opts) {
1197
+ if (!palette.Vibrant && !palette.DarkVibrant && !palette.LightVibrant) {
1198
+ if (!palette.DarkVibrant && palette.DarkMuted) {
1199
+ let [h, s, l] = palette.DarkMuted.hsl;
1200
+ l = opts.targetDarkLuma;
1201
+ palette.DarkVibrant = new Swatch(hslToRgb(h, s, l), 0);
1202
+ }
1203
+ if (!palette.LightVibrant && palette.LightMuted) {
1204
+ let [h, s, l] = palette.LightMuted.hsl;
1205
+ l = opts.targetDarkLuma;
1206
+ palette.DarkVibrant = new Swatch(hslToRgb(h, s, l), 0);
1207
+ }
1208
+ }
1209
+ if (!palette.Vibrant && palette.DarkVibrant) {
1210
+ let [h, s, l] = palette.DarkVibrant.hsl;
1211
+ l = opts.targetNormalLuma;
1212
+ palette.Vibrant = new Swatch(hslToRgb(h, s, l), 0);
1213
+ } else if (!palette.Vibrant && palette.LightVibrant) {
1214
+ let [h, s, l] = palette.LightVibrant.hsl;
1215
+ l = opts.targetNormalLuma;
1216
+ palette.Vibrant = new Swatch(hslToRgb(h, s, l), 0);
1217
+ }
1218
+ if (!palette.DarkVibrant && palette.Vibrant) {
1219
+ let [h, s, l] = palette.Vibrant.hsl;
1220
+ l = opts.targetDarkLuma;
1221
+ palette.DarkVibrant = new Swatch(hslToRgb(h, s, l), 0);
1222
+ }
1223
+ if (!palette.LightVibrant && palette.Vibrant) {
1224
+ let [h, s, l] = palette.Vibrant.hsl;
1225
+ l = opts.targetLightLuma;
1226
+ palette.LightVibrant = new Swatch(hslToRgb(h, s, l), 0);
1227
+ }
1228
+ if (!palette.Muted && palette.Vibrant) {
1229
+ let [h, s, l] = palette.Vibrant.hsl;
1230
+ l = opts.targetMutesSaturation;
1231
+ palette.Muted = new Swatch(hslToRgb(h, s, l), 0);
1232
+ }
1233
+ if (!palette.DarkMuted && palette.DarkVibrant) {
1234
+ let [h, s, l] = palette.DarkVibrant.hsl;
1235
+ l = opts.targetMutesSaturation;
1236
+ palette.DarkMuted = new Swatch(hslToRgb(h, s, l), 0);
1237
+ }
1238
+ if (!palette.LightMuted && palette.LightVibrant) {
1239
+ let [h, s, l] = palette.LightVibrant.hsl;
1240
+ l = opts.targetMutesSaturation;
1241
+ palette.LightMuted = new Swatch(hslToRgb(h, s, l), 0);
1242
+ }
1255
1243
  }
1244
+ const DefaultGenerator = ((swatches, opts) => {
1245
+ opts = Object.assign({}, DefaultOpts, opts);
1246
+ const maxPopulation = _findMaxPopulation(swatches);
1247
+ const palette = _generateVariationColors(swatches, maxPopulation, opts);
1248
+ _generateEmptySwatches(palette, maxPopulation, opts);
1249
+ return palette;
1250
+ });
1251
+ //#endregion
1252
+ //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/pipeline/index.js
1253
+ const pipeline = new BasicPipeline().filter.register("default", (r, g, b, a) => a >= 125 && !(r > 250 && g > 250 && b > 250)).quantizer.register("mmcq", MMCQ).generator.register("default", DefaultGenerator);
1254
+ //#endregion
1255
+ //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/browser.js
1256
+ Vibrant.use(pipeline);
1256
1257
  //#endregion
1257
1258
  //#region ../theme-core/src/site-styles/styleTags.ts
1258
1259
  /**
@@ -1527,6 +1528,71 @@ const brandLedJewel = defineCuratedPalette({
1527
1528
  }
1528
1529
  ]
1529
1530
  });
1531
+ const brandLedSpruce = defineCuratedPalette({
1532
+ id: "brand-led-spruce",
1533
+ name: "Brand-led — Spruce",
1534
+ description: "Spruce-ink primary and moss secondary with an amber accent over a green-mist ground and hairline sage borders.",
1535
+ tags: {
1536
+ tone: "classic",
1537
+ energy: "calm",
1538
+ era: "editorial"
1539
+ },
1540
+ mode: "light",
1541
+ colors: [
1542
+ {
1543
+ name: "primary",
1544
+ hex: "#14312b"
1545
+ },
1546
+ {
1547
+ name: "secondary",
1548
+ hex: "#356a59"
1549
+ },
1550
+ {
1551
+ name: "accent",
1552
+ hex: "#f08a4b"
1553
+ },
1554
+ {
1555
+ name: "success",
1556
+ hex: "#356a59"
1557
+ },
1558
+ {
1559
+ name: "warning",
1560
+ hex: "#f08a4b"
1561
+ },
1562
+ {
1563
+ name: "destructive",
1564
+ hex: "#b5561f"
1565
+ },
1566
+ {
1567
+ name: "background",
1568
+ hex: "#ecefe9"
1569
+ },
1570
+ {
1571
+ name: "surface",
1572
+ hex: "#ffffff"
1573
+ },
1574
+ {
1575
+ name: "surfaceAlt",
1576
+ hex: "#f4f6f1"
1577
+ },
1578
+ {
1579
+ name: "muted",
1580
+ hex: "#5c6a64"
1581
+ },
1582
+ {
1583
+ name: "text",
1584
+ hex: "#1c2a26"
1585
+ },
1586
+ {
1587
+ name: "primaryForeground",
1588
+ hex: "#f4f7f1"
1589
+ },
1590
+ {
1591
+ name: "border",
1592
+ hex: "#d7ddd2"
1593
+ }
1594
+ ]
1595
+ });
1530
1596
  const brandLedBurgundyGold = defineCuratedPalette({
1531
1597
  id: "brand-led-burgundy-gold",
1532
1598
  name: "Brand-led — Burgundy Gold",
@@ -2066,6 +2132,7 @@ const curatedPalettes = [
2066
2132
  brandLedWarm,
2067
2133
  brandLedJewel,
2068
2134
  brandLedBurgundyGold,
2135
+ brandLedSpruce,
2069
2136
  defineCuratedPalette({
2070
2137
  id: "warm-neutral-clay",
2071
2138
  name: "Warm Neutral — Clay",
@@ -2530,6 +2597,55 @@ z$1.discriminatedUnion("kind", [
2530
2597
  })
2531
2598
  ]);
2532
2599
  //#endregion
2600
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
2601
+ const SURFACE_FOREGROUND_CSS_VARS = {
2602
+ text: "--rb-surface-text-color",
2603
+ heading: "--rb-surface-heading-color",
2604
+ link: "--rb-surface-link-color",
2605
+ supporting: "--rb-surface-supporting-color"
2606
+ };
2607
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
2608
+ const DEFAULT_ROLE_FALLBACKS = {
2609
+ text: DEFAULT_SURFACE_TEXT_COLOR,
2610
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
2611
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
2612
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
2613
+ };
2614
+ function requireSurfaceColorValue(role, value) {
2615
+ const normalized = value.trim();
2616
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
2617
+ return normalized;
2618
+ }
2619
+ function completeSurfaceForegrounds(input) {
2620
+ const text = requireSurfaceColorValue("text", input.text);
2621
+ return {
2622
+ text,
2623
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
2624
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
2625
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
2626
+ };
2627
+ }
2628
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
2629
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
2630
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
2631
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
2632
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
2633
+ }
2634
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
2635
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
2636
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
2637
+ }
2638
+ function surfaceForegroundStyle(input) {
2639
+ const foregrounds = completeSurfaceForegrounds(input);
2640
+ return {
2641
+ color: foregrounds.text,
2642
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
2643
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
2644
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
2645
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
2646
+ };
2647
+ }
2648
+ //#endregion
2533
2649
  //#region ../theme-core/src/utils/colorStyles.ts
2534
2650
  const COLOR_VAR_PREFIX = "--tb-";
2535
2651
  function parseToken(source) {
@@ -2541,9 +2657,17 @@ function parseToken(source) {
2541
2657
  if (parts.kind === "invalid") return { token: source };
2542
2658
  return {
2543
2659
  token: parts.token,
2544
- alpha: parts.opacityPercent === null ? void 0 : parts.opacityPercent / 100
2660
+ opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
2545
2661
  };
2546
2662
  }
2663
+ const SURFACE_TEXT_ROLE_BY_TOKEN = {
2664
+ text: "text",
2665
+ heading: "heading",
2666
+ mutedText: "supporting"
2667
+ };
2668
+ function surfaceRoleForTextToken(token) {
2669
+ return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
2670
+ }
2547
2671
  /**
2548
2672
  * Converts a theme color token to a CSS rgb value.
2549
2673
  * This is the primary function for applying theme colors to CSS properties.
@@ -2557,11 +2681,11 @@ function parseToken(source) {
2557
2681
  * rgbColorValue('raw:transparent') // => 'transparent'
2558
2682
  */
2559
2683
  function rgbColorValue(token) {
2560
- const { token: baseToken, alpha, raw } = parseToken(token);
2684
+ const { token: baseToken, opacityPercent, raw } = parseToken(token);
2561
2685
  if (raw) return raw;
2562
2686
  const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
2563
- if (alpha === void 0) return `rgb(var(${cssVar}))`;
2564
- return `rgb(var(${cssVar}) / ${alpha})`;
2687
+ if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
2688
+ return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
2565
2689
  }
2566
2690
  /**
2567
2691
  * Creates a backgroundColor style object from a theme token.
@@ -2579,6 +2703,9 @@ function backgroundColorStyle(token) {
2579
2703
  * @returns CSSProperties with color
2580
2704
  */
2581
2705
  function textColorStyle(token) {
2706
+ const parsed = parseToken(token);
2707
+ const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
2708
+ if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
2582
2709
  return { color: rgbColorValue(token) };
2583
2710
  }
2584
2711
  /**
@@ -2848,6 +2975,10 @@ const buttonBorderSchema = z$1.object({
2848
2975
  widthClass: z$1.string(),
2849
2976
  colorToken: themeColorTokenSchema
2850
2977
  });
2978
+ const buttonVariantAffordanceSchema = z$1.object({
2979
+ iconId: z$1.string().min(1),
2980
+ colorToken: themeColorTokenSchema.optional()
2981
+ });
2851
2982
  const variantShadowSchema = z$1.object({
2852
2983
  elevation: z$1.enum([
2853
2984
  "none",
@@ -2901,7 +3032,8 @@ const buttonVariantSchema = z$1.object({
2901
3032
  border: buttonBorderSchema.optional(),
2902
3033
  hoverBackgroundToken: z$1.string().optional(),
2903
3034
  effects: variantEffectsSchema.optional(),
2904
- sizes: variantSizeOverridesSchema.optional()
3035
+ sizes: variantSizeOverridesSchema.optional(),
3036
+ affordance: buttonVariantAffordanceSchema.optional()
2905
3037
  });
2906
3038
  const buttonSystemSchema = z$1.object({
2907
3039
  global: buttonGlobalSettingsSchema,
@@ -3446,6 +3578,136 @@ const editorialLink = defineButtonPersonality({
3446
3578
  }
3447
3579
  });
3448
3580
  //#endregion
3581
+ //#region ../theme-core/src/buttons/personalities/flat-ink.ts
3582
+ /**
3583
+ * Flat Ink — a solid ink primary, a hairline ghost secondary, and a single
3584
+ * amber accent climax, all completely flat (no drop shadow) with a modest
3585
+ * radius. Reads as grounded, editorial and clinician-precise: the Fulcrum
3586
+ * physiotherapy voice. The primary shifts to the secondary (moss) on hover and
3587
+ * every variant lifts 2px; the amber accent is meant to be used sparingly as
3588
+ * the page's one conversion climax.
3589
+ */
3590
+ const flatInk = defineButtonPersonality({
3591
+ id: "flat-ink",
3592
+ name: "Flat Ink",
3593
+ description: "Flat solid-ink primary, hairline ghost secondary, and a single amber accent climax — modest radius, no shadow.",
3594
+ tags: {
3595
+ tone: "classic",
3596
+ energy: "calm",
3597
+ motion: "subtle",
3598
+ era: "editorial"
3599
+ },
3600
+ buttonSystem: {
3601
+ global: {
3602
+ cornerStyle: "rounded",
3603
+ shadow: "low",
3604
+ textTransform: "none",
3605
+ fontWeight: 600,
3606
+ borderWidth: 1,
3607
+ typography: "body",
3608
+ letterSpacing: "tight",
3609
+ paddingPreset: "default",
3610
+ hoverTransform: "lift",
3611
+ hoverColor: "none"
3612
+ },
3613
+ sizes: {
3614
+ sm: {
3615
+ padding: "0.625rem 1.125rem",
3616
+ fontSize: "text-sm"
3617
+ },
3618
+ md: { padding: "0.8125rem 1.375rem" },
3619
+ lg: {
3620
+ padding: "0.9375rem 1.625rem",
3621
+ fontSize: "text-lg"
3622
+ },
3623
+ xl: {
3624
+ padding: "1.0625rem 2rem",
3625
+ fontSize: "text-xl"
3626
+ }
3627
+ },
3628
+ variants: [
3629
+ {
3630
+ id: "primary",
3631
+ name: "Primary",
3632
+ enabled: true,
3633
+ priority: 1,
3634
+ background: {
3635
+ type: "solid",
3636
+ colorToken: "primary"
3637
+ },
3638
+ textColorToken: "primaryForeground",
3639
+ borderRadius: "rounded-lg",
3640
+ affordance: {
3641
+ iconId: "ArrowRight",
3642
+ colorToken: "accent"
3643
+ },
3644
+ shadow: {
3645
+ elevation: "none",
3646
+ softness: null,
3647
+ position: "bottom"
3648
+ },
3649
+ effects: { hover: [{
3650
+ effectId: "lift",
3651
+ options: { translateY: "-2px" }
3652
+ }, {
3653
+ effectId: "background-on-hover",
3654
+ options: { colorToken: "secondary" }
3655
+ }] }
3656
+ },
3657
+ {
3658
+ id: "secondary",
3659
+ name: "Secondary",
3660
+ enabled: true,
3661
+ priority: 2,
3662
+ background: { type: "transparent" },
3663
+ textColorToken: "primary",
3664
+ borderRadius: "rounded-lg",
3665
+ border: {
3666
+ widthClass: "border",
3667
+ colorToken: "border"
3668
+ },
3669
+ shadow: {
3670
+ elevation: "none",
3671
+ softness: null,
3672
+ position: "bottom"
3673
+ },
3674
+ effects: { hover: [{
3675
+ effectId: "lift",
3676
+ options: { translateY: "-2px" }
3677
+ }, {
3678
+ effectId: "background-on-hover",
3679
+ options: { colorToken: "surfaceAlt" }
3680
+ }] }
3681
+ },
3682
+ {
3683
+ id: "accent",
3684
+ name: "Accent",
3685
+ enabled: true,
3686
+ priority: 3,
3687
+ background: {
3688
+ type: "solid",
3689
+ colorToken: "accent"
3690
+ },
3691
+ textColorToken: "accentForeground",
3692
+ borderRadius: "rounded-lg",
3693
+ shadow: {
3694
+ elevation: "none",
3695
+ softness: null,
3696
+ position: "bottom"
3697
+ },
3698
+ affordance: {
3699
+ iconId: "ArrowRight",
3700
+ colorToken: "accentForeground"
3701
+ },
3702
+ effects: { hover: [{
3703
+ effectId: "lift",
3704
+ options: { translateY: "-2px" }
3705
+ }] }
3706
+ }
3707
+ ]
3708
+ }
3709
+ });
3710
+ //#endregion
3449
3711
  //#region ../theme-core/src/buttons/personalities/ink-stamp.ts
3450
3712
  /**
3451
3713
  * Ink Stamp — solid earthy fills with a bordered, hand-stamped block-print
@@ -4216,6 +4478,7 @@ const buttonPersonalities = [
4216
4478
  editorialLink,
4217
4479
  pebble,
4218
4480
  inkStamp,
4481
+ flatInk,
4219
4482
  brushedWash,
4220
4483
  showtimePill,
4221
4484
  joyStamp,
@@ -4569,11 +4832,31 @@ const gradientStopSchema = z$1.object({
4569
4832
  position: z$1.number().min(0).max(100)
4570
4833
  });
4571
4834
  /**
4572
- * Gradient configuration
4835
+ * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
4836
+ * so existing radial gradients are byte-identical.
4837
+ *
4838
+ * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
4839
+ * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
4840
+ */
4841
+ const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
4842
+ /**
4843
+ * Gradient configuration.
4844
+ *
4845
+ * `type` is optional and defaults to `'linear'` so gradients persisted without a
4846
+ * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
4847
+ * `'conic'` are additive kinds:
4848
+ * - linear: `angle` (degrees)
4849
+ * - radial: optional `shape` (circle/ellipse) + `position`
4850
+ * - conic: optional `angle` (from-angle, degrees) + `position`
4573
4851
  */
4574
4852
  const gradientConfigSchema = z$1.object({
4575
- type: z$1.enum(["linear", "radial"]),
4853
+ type: z$1.enum([
4854
+ "linear",
4855
+ "radial",
4856
+ "conic"
4857
+ ]).default("linear"),
4576
4858
  angle: z$1.number().min(0).max(360).optional(),
4859
+ shape: radialGradientShapeSchema.optional(),
4577
4860
  position: z$1.string().optional(),
4578
4861
  stops: z$1.array(gradientStopSchema).min(2)
4579
4862
  });
@@ -5484,6 +5767,7 @@ const typographyScaleSchema = z$1.enum([
5484
5767
  "majorThird",
5485
5768
  "perfectFourth"
5486
5769
  ]);
5770
+ const eyebrowTypographyStyleSchema = z$1.object({ family: z$1.string().min(1) });
5487
5771
  const typographySchema = z$1.object({
5488
5772
  body: bodyTypographyStyleSchema,
5489
5773
  headings: z$1.object({
@@ -5496,7 +5780,8 @@ const typographySchema = z$1.object({
5496
5780
  h6: headingTypographyOverridesSchema.optional()
5497
5781
  }),
5498
5782
  scale: typographyScaleSchema,
5499
- proseLinkStyle: proseLinkStyleSchema.optional()
5783
+ proseLinkStyle: proseLinkStyleSchema.optional(),
5784
+ eyebrow: eyebrowTypographyStyleSchema.optional()
5500
5785
  });
5501
5786
  const spaceSchema = z$1.enum([
5502
5787
  "comfortable",
@@ -5521,7 +5806,8 @@ const shadow = z$1.object({
5521
5806
  "soft",
5522
5807
  "hard"
5523
5808
  ]).nullable(),
5524
- position: z$1.enum(["bottom", "bottom-right"]).default("bottom")
5809
+ position: z$1.enum(["bottom", "bottom-right"]).default("bottom"),
5810
+ colorToken: themeColorTokenSchema.optional()
5525
5811
  });
5526
5812
  const border = z$1.object({
5527
5813
  width: z$1.enum([
@@ -5766,6 +6052,7 @@ const headerSchema = z$1.object({
5766
6052
  navWeight: navFontWeight.default("medium"),
5767
6053
  navTypography: z$1.enum(["body", "heading"]).optional(),
5768
6054
  navTextSize: navLinkStyleSchema.shape.textSize,
6055
+ navPlacement: z$1.enum(["end", "center"]).optional(),
5769
6056
  container: headerContainerSchema,
5770
6057
  border: headerBorderSchema,
5771
6058
  shadow: componentShadowSchema.optional(),
@@ -7863,10 +8150,17 @@ headerStyleRepairChain([
7863
8150
  ]), headerStyleRepairChain(["fine-line", "minimal"]), headerStyleRepairChain(["soft-bar", "minimal"]), headerStyleRepairChain(["minimal"]);
7864
8151
  const legacyHeaderLookAliases = defineExactCatalog()([
7865
8152
  {
7866
- lookId: asHeaderLookId("clean-base"),
8153
+ lookId: asHeaderLookId("clean-base"),
8154
+ selection: {
8155
+ layoutId: asHeaderLayoutId("classic"),
8156
+ styleId: asHeaderStyleId("minimal")
8157
+ }
8158
+ },
8159
+ {
8160
+ lookId: asHeaderLookId("classic-centered-nav"),
7867
8161
  selection: {
7868
8162
  layoutId: asHeaderLayoutId("classic"),
7869
- styleId: asHeaderStyleId("minimal")
8163
+ styleId: asHeaderStyleId("fine-line")
7870
8164
  }
7871
8165
  },
7872
8166
  {
@@ -7939,6 +8233,50 @@ function getCompatibleHeaderStyleIds(layoutId) {
7939
8233
  return isHeaderStyleCompatibleWithLayout(getHeaderStyleOrThrow(styleId), layoutId);
7940
8234
  });
7941
8235
  }
8236
+ function applyNavTypography(style, typography) {
8237
+ switch (typography) {
8238
+ case "plain": return style;
8239
+ case "heading": return {
8240
+ ...style,
8241
+ typography: "heading",
8242
+ fontWeight: "medium",
8243
+ letterSpacing: "wide"
8244
+ };
8245
+ case "quiet-caps": return {
8246
+ ...style,
8247
+ textTransform: "uppercase",
8248
+ textSize: "sm",
8249
+ letterSpacing: "wide"
8250
+ };
8251
+ case "brand-caps": return {
8252
+ ...style,
8253
+ textTransform: "uppercase",
8254
+ textSize: "sm",
8255
+ letterSpacing: "wider"
8256
+ };
8257
+ default: return assertNever$1(typography, "Unhandled header nav typography");
8258
+ }
8259
+ }
8260
+ function underlineNavLinkStyle(colorToken, underlineColorToken, typography = "plain") {
8261
+ return applyNavTypography({
8262
+ name: "Underline grow",
8263
+ typography: "body",
8264
+ fontWeight: "medium",
8265
+ textTransform: "none",
8266
+ italic: false,
8267
+ colorToken,
8268
+ padding: "compact",
8269
+ paddingX: "compact",
8270
+ borderRadius: "none",
8271
+ effects: { hover: [{
8272
+ effectId: "nav-underline",
8273
+ options: {
8274
+ style: "grow",
8275
+ ...underlineColorToken ? { colorToken: underlineColorToken } : {}
8276
+ }
8277
+ }] }
8278
+ }, typography);
8279
+ }
7942
8280
  function themedHeaderShadow(theme) {
7943
8281
  switch (theme.shadow.elevation) {
7944
8282
  case "none": return;
@@ -8038,6 +8376,38 @@ function cleanBaseHeader(theme) {
8038
8376
  ctaGap: "default"
8039
8377
  };
8040
8378
  }
8379
+ function classicCenteredNavHeader(theme) {
8380
+ return {
8381
+ ...baseHeader(theme),
8382
+ variant: "classic",
8383
+ navPlacement: "center",
8384
+ positioning: "sticky",
8385
+ shrinkOnScroll: false,
8386
+ background: {
8387
+ type: "color",
8388
+ color: "surface"
8389
+ },
8390
+ textColor: "text",
8391
+ navColor: "text",
8392
+ navStyle: "underline-grow",
8393
+ navWeight: "medium",
8394
+ navLinkStyle: underlineNavLinkStyle("text", "accent"),
8395
+ border: {
8396
+ style: "solid",
8397
+ width: "thin",
8398
+ position: "bottom",
8399
+ colorToken: "border"
8400
+ },
8401
+ ctaTreatment: "primary",
8402
+ ctaVariant: resolveHeaderCtaVariant(theme, "primary"),
8403
+ ctaGap: "default",
8404
+ logoText: {
8405
+ mobileMaxWidth: "sm",
8406
+ mobileWrapLines: 1,
8407
+ hideOnShrink: false
8408
+ }
8409
+ };
8410
+ }
8041
8411
  function serifLineHeader(theme) {
8042
8412
  return {
8043
8413
  ...baseHeader(theme),
@@ -8202,6 +8572,21 @@ const headerLookCatalog = defineExactCatalog()([
8202
8572
  logoAccommodations: inlineClassicLogoAccommodations,
8203
8573
  compile: cleanBaseHeader
8204
8574
  },
8575
+ {
8576
+ id: asHeaderLookId("classic-centered-nav"),
8577
+ label: "Centered nav",
8578
+ visualTreatment: "solid-neutral",
8579
+ layoutBehavior: "sticky",
8580
+ configuration: {
8581
+ structure: "inline-classic",
8582
+ treatment: "solid-neutral",
8583
+ behavior: "sticky"
8584
+ },
8585
+ openingRequirement: "none",
8586
+ maxRecommendedNavItems: 6,
8587
+ logoAccommodations: inlineClassicLogoAccommodations,
8588
+ compile: classicCenteredNavHeader
8589
+ },
8205
8590
  {
8206
8591
  id: asHeaderLookId("brand-solid"),
8207
8592
  label: "Brand solid",
@@ -9368,6 +9753,74 @@ const typographyPresets = [
9368
9753
  }
9369
9754
  }
9370
9755
  }
9756
+ }),
9757
+ defineTypographyPreset({
9758
+ id: "typography-preset:fulcrum",
9759
+ name: "Fulcrum",
9760
+ description: "Newsreader serif headings over an IBM Plex Sans body, with an IBM Plex Mono eyebrow role for uppercase kickers and mono captions.",
9761
+ defaultFonts: {
9762
+ body: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
9763
+ heading: "Newsreader, Georgia, \"Times New Roman\", serif"
9764
+ },
9765
+ recommendedFonts: {
9766
+ body: [
9767
+ "IBM Plex Sans",
9768
+ "Source Sans 3",
9769
+ "Inter"
9770
+ ],
9771
+ heading: [
9772
+ "Newsreader",
9773
+ "Source Serif 4",
9774
+ "Lora"
9775
+ ]
9776
+ },
9777
+ slotPolicies: {
9778
+ body: bodyReadablePolicy,
9779
+ heading: displayHeadingPolicy
9780
+ },
9781
+ typography: {
9782
+ body: {
9783
+ family: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
9784
+ size: "lg",
9785
+ weight: "regular",
9786
+ lineHeight: "relaxed",
9787
+ letterSpacing: "normal"
9788
+ },
9789
+ headings: {
9790
+ default: {
9791
+ family: "Newsreader, Georgia, \"Times New Roman\", serif",
9792
+ weight: "medium",
9793
+ lineHeight: "normal",
9794
+ letterSpacing: "tight",
9795
+ colorToken: "primary",
9796
+ italic: null,
9797
+ case: null
9798
+ },
9799
+ h1: {
9800
+ size: "2xl",
9801
+ weight: "medium",
9802
+ lineHeight: "normal",
9803
+ letterSpacing: "tight",
9804
+ italic: null
9805
+ },
9806
+ h2: {
9807
+ size: "xl",
9808
+ weight: "medium",
9809
+ lineHeight: "normal",
9810
+ letterSpacing: "tight",
9811
+ italic: null
9812
+ },
9813
+ h3: {
9814
+ size: "md",
9815
+ weight: "semibold",
9816
+ lineHeight: "normal",
9817
+ letterSpacing: "normal",
9818
+ italic: null
9819
+ }
9820
+ },
9821
+ scale: "spacious",
9822
+ eyebrow: { family: "IBM Plex Mono, ui-monospace, \"SF Mono\", monospace" }
9823
+ }
9371
9824
  })
9372
9825
  ];
9373
9826
  new Map(typographyPresets.map((preset) => [preset.id, preset]));
@@ -9588,7 +10041,8 @@ const curatedSiteStyleIdValues = [
9588
10041
  "site-style:bold-launch",
9589
10042
  "site-style:south-west-twerk-school",
9590
10043
  "site-style:kirtan-ornate",
9591
- "site-style:original-joy"
10044
+ "site-style:original-joy",
10045
+ "site-style:fulcrum"
9592
10046
  ];
9593
10047
  const quietBudget = {
9594
10048
  accentDensity: "low",
@@ -11042,6 +11496,104 @@ const curatedSiteStyles = [
11042
11496
  buttonChoice("showtime-pill")
11043
11497
  ],
11044
11498
  paletteRecommendations: [paletteRecommendation("warm-dark-ember")]
11499
+ }),
11500
+ defineCuratedSiteStyle({
11501
+ id: "site-style:fulcrum",
11502
+ name: "Fulcrum",
11503
+ headingDecoration: {
11504
+ family: "short-center-rule",
11505
+ colorToken: "accent"
11506
+ },
11507
+ 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.",
11508
+ 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.",
11509
+ siteCreatorSelection: {
11510
+ kind: "site_creator_keyword_heuristic",
11511
+ priority: 28
11512
+ },
11513
+ selectionKeywordRules: [
11514
+ {
11515
+ kind: "substring",
11516
+ value: "physiotherap"
11517
+ },
11518
+ {
11519
+ kind: "word",
11520
+ value: "physio"
11521
+ },
11522
+ {
11523
+ kind: "phrase",
11524
+ value: "sports injury"
11525
+ },
11526
+ {
11527
+ kind: "phrase",
11528
+ value: "rehab clinic"
11529
+ },
11530
+ {
11531
+ kind: "substring",
11532
+ value: "musculoskeletal"
11533
+ },
11534
+ {
11535
+ kind: "phrase",
11536
+ value: "sports therapy"
11537
+ }
11538
+ ],
11539
+ tokenRecipes: {
11540
+ palette: "brand-led",
11541
+ contrast: "standard",
11542
+ radius: "rounded",
11543
+ shadow: "subtle",
11544
+ typography: "serif-editorial",
11545
+ spacing: "airy",
11546
+ motion: "subtle"
11547
+ },
11548
+ compositionBudget: quietBudget,
11549
+ typographyPresetChoices: [
11550
+ typographyChoice("typography-preset:fulcrum"),
11551
+ typographyChoice("typography-preset:editorial-serif"),
11552
+ typographyChoice("typography-preset:modern-sans")
11553
+ ],
11554
+ defaultTypographyPresetId: "typography-preset:fulcrum",
11555
+ themeOverrides: {
11556
+ shadow: { colorToken: "primary" },
11557
+ accordions: {
11558
+ background: null,
11559
+ spacing: "comfortable",
11560
+ corners: "square",
11561
+ border: null,
11562
+ shadow: null,
11563
+ icon: {
11564
+ style: "plus-minus",
11565
+ position: "right",
11566
+ size: "md",
11567
+ colorToken: "accent"
11568
+ },
11569
+ trigger: {
11570
+ textColorToken: "primary",
11571
+ fontWeight: "medium",
11572
+ fontFamily: "heading",
11573
+ fontScale: "md"
11574
+ },
11575
+ hover: { textColorToken: "secondary" },
11576
+ content: { textColorToken: "muted" }
11577
+ },
11578
+ testimonialQuote: serifDisplayQuote
11579
+ },
11580
+ recommendedHeaderLookIds: [
11581
+ "classic-centered-nav",
11582
+ "serif-line",
11583
+ "centered-calm",
11584
+ "clean-base"
11585
+ ],
11586
+ recommendedFooterLookIds: ["footer-editorial-columns", "footer-muted-grounding"],
11587
+ buttonPersonalityChoices: [
11588
+ buttonChoice("flat-ink"),
11589
+ buttonChoice("confident-chip"),
11590
+ buttonChoice("editorial-link")
11591
+ ],
11592
+ paletteRecommendations: [
11593
+ paletteRecommendation("brand-led-spruce"),
11594
+ paletteRecommendation("warm-neutral-forest"),
11595
+ paletteRecommendation("soft-natural-sage")
11596
+ ]
11045
11597
  })
11046
11598
  ];
11047
11599
  new Map(curatedSiteStyles.map((style) => [style.id, style]));
@@ -11769,6 +12321,9 @@ const ALLOWED_NODE_TYPES = [
11769
12321
  "richText",
11770
12322
  "media",
11771
12323
  "videoEmbed",
12324
+ "icon",
12325
+ "feature-card-icon",
12326
+ "svg",
11772
12327
  "button",
11773
12328
  "link",
11774
12329
  "carousel",
@@ -11805,11 +12360,13 @@ const ALLOWED_TRANSFORM_IDS = [
11805
12360
  "value.jsonArray",
11806
12361
  "value.csv",
11807
12362
  "array.marqueeLoop",
12363
+ "value.mapToken",
11808
12364
  "layout.maxWidthClass",
11809
12365
  "layout.headerRootClass",
11810
12366
  "layout.footerRootClass",
11811
12367
  "layout.headerRootStyle",
11812
12368
  "layout.headerSectionBackground",
12369
+ "layout.classicNavMode",
11813
12370
  "layout.footerRootStyle",
11814
12371
  "ui.headerNavLinkClass",
11815
12372
  "ui.headingClassFromAlignment",
@@ -15025,7 +15582,29 @@ const ctaRowFragment = defineFragment({
15025
15582
  fields: [createCtasRepeater({
15026
15583
  label: "Calls to action",
15027
15584
  itemLabel: "CTA",
15028
- maxItems: 3
15585
+ maxItems: 3,
15586
+ button: { variants: [
15587
+ {
15588
+ value: "primary",
15589
+ label: "Primary"
15590
+ },
15591
+ {
15592
+ value: "secondary",
15593
+ label: "Secondary"
15594
+ },
15595
+ {
15596
+ value: "outline",
15597
+ label: "Outline"
15598
+ },
15599
+ {
15600
+ value: "link",
15601
+ label: "Link"
15602
+ },
15603
+ {
15604
+ value: "accent",
15605
+ label: "Accent"
15606
+ }
15607
+ ] }
15029
15608
  })],
15030
15609
  layout: [ctaRow({
15031
15610
  collectionPath: "content.ctas",
@@ -15306,6 +15885,55 @@ function buildGridCardsLayout(className) {
15306
15885
  return el("div", { className }, buildResponsiveGridCards());
15307
15886
  }
15308
15887
  /**
15888
+ * Build a single "featured editorial" testimonial card.
15889
+ *
15890
+ * This card type deliberately renders NO avatar/headshot: an amber quote mark,
15891
+ * the quote body, then the author name and a context line. Colour + surface
15892
+ * treatment is driven entirely by `variantClass` (see testimonials.css):
15893
+ * - `rb-testimonials-fe-card--feature` → spruce-ink card (dark primary bg)
15894
+ * - `rb-testimonials-fe-card--plain` → white surface card
15895
+ *
15896
+ * Extra modifiers (`repeat`, `when`) are forwarded to the card node so the
15897
+ * caller can drive first-vs-rest branching.
15898
+ */
15899
+ function buildFeaturedEditorialCard(variantClass, ...mods) {
15900
+ const quoteMark = text({
15901
+ as: "span",
15902
+ className: "rb-testimonials-fe-qmark",
15903
+ "aria-hidden": "true"
15904
+ }, props({ value: "“" }));
15905
+ const quote = richText({ className: "rb-testimonials-fe-quote rb-max-w-none" }, bind("testimonial.content.body"), when("testimonial.content.body"));
15906
+ const meta = el("div", { className: "rb-testimonials-fe-meta" }, [text({
15907
+ as: "strong",
15908
+ className: "rb-testimonials-fe-name"
15909
+ }, bind("testimonial.content.name", { fallback: "Anonymous" })), text({
15910
+ as: "span",
15911
+ className: "rb-testimonials-fe-context"
15912
+ }, when("testimonial.content.jobTitle"), bind("testimonial.content.jobTitle"))]);
15913
+ return el("div", { className: `rb-testimonials-fe-card ${variantClass}` }, [
15914
+ quoteMark,
15915
+ quote,
15916
+ meta
15917
+ ], ...mods);
15918
+ }
15919
+ /**
15920
+ * Build the "featured editorial" layout: an asymmetric two-column arrangement
15921
+ * where the FIRST testimonial is a large featured (spruce-ink) card and the
15922
+ * remaining testimonials stack as white cards beside it.
15923
+ *
15924
+ * First-vs-rest branching is done without an offset primitive: the featured
15925
+ * card uses `repeat(..., { limit: 1 })` (entry 0 only), and the rest column
15926
+ * repeats over every entry but skips index 0 via `when("tIndex", { equals: 0,
15927
+ * not: true })`. The `$when` guard is re-evaluated per repeated item with the
15928
+ * index scope in context, so entry 0 produces no node in the rest column.
15929
+ */
15930
+ function buildFeaturedEditorialLayout() {
15931
+ 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", {
15932
+ equals: 0,
15933
+ not: true
15934
+ }))])]);
15935
+ }
15936
+ /**
15309
15937
  * Fragment definition for testimonials carousel.
15310
15938
  * Provides field definitions and data loader configuration.
15311
15939
  *
@@ -18323,7 +18951,7 @@ const defaultLayout = styledSection({
18323
18951
  children: styledContainer({
18324
18952
  baseClassName: "rb-relative rb-isolate",
18325
18953
  children: stack({
18326
- gap: "xl",
18954
+ gap: "lg",
18327
18955
  className: "rb-w-full rb-max-w-4xl rb-mx-auto"
18328
18956
  }, [headingLayout, accordionLayout])
18329
18957
  }),
@@ -18340,7 +18968,7 @@ const questionColumnsLayout = styledSection({
18340
18968
  children: styledContainer({
18341
18969
  baseClassName: "rb-relative rb-isolate",
18342
18970
  children: stack({
18343
- gap: "xl",
18971
+ gap: "lg",
18344
18972
  className: "rb-w-full"
18345
18973
  }, [headingLayout, questionColumnsAccordionLayout])
18346
18974
  }),
@@ -18643,7 +19271,18 @@ const classicLayout = inline({
18643
19271
  logoRow,
18644
19272
  createNavRow(`rb-ml-auto ${desktopOnlyDisplay("flex")} rb-gap-6`),
18645
19273
  headerCta
18646
- ], when("$root.theme.header.variant", { equals: "classic" }));
19274
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "inline"));
19275
+ const classicCenteredNavLayout = inline({
19276
+ className: bindProp("$root.theme.header.maxWidth", {
19277
+ transforms: pipe(tx("layout.maxWidthClass", { base: `rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6` })),
19278
+ fallback: `rb-container rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6`
19279
+ }),
19280
+ align: "center"
19281
+ }, [
19282
+ logoRow,
19283
+ createNavRow(`rb-flex-1 rb-justify-center ${desktopOnlyDisplay("flex")} rb-gap-6`, "center"),
19284
+ headerCta
19285
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "centered"));
18647
19286
  const centeredLayout = stack({
18648
19287
  gap: "md",
18649
19288
  align: "center",
@@ -18718,6 +19357,7 @@ const siteHeaderBlockDefinition = { manifest: createBlockManifest({
18718
19357
  }, [
18719
19358
  mobileHeaderBar,
18720
19359
  classicLayout,
19360
+ classicCenteredNavLayout,
18721
19361
  centeredLayout,
18722
19362
  transparentLayout,
18723
19363
  floatingLayout,
@@ -18926,7 +19566,7 @@ const footerLayout = section({
18926
19566
  style: { $bind: {
18927
19567
  from: "$root.theme.footer",
18928
19568
  transforms: [{ id: "layout.footerRootStyle" }],
18929
- fallback: mergeStyles(backgroundColorStyle("surface"), textColorStyle("text"), borderColorStyle("border"))
19569
+ fallback: mergeStyles(backgroundColorStyle("surface"), surfaceForegroundStyle({ text: "rgb(var(--tb-text))" }), borderColorStyle("border"))
18930
19570
  } }
18931
19571
  }, [
18932
19572
  simpleFooterLayout,
@@ -18980,7 +19620,7 @@ const backgroundImageClassName = "rb-absolute rb-inset-0 rb-z-neg-10 rb-h-full r
18980
19620
  */
18981
19621
  const wrapVariant$2 = (variantLayout) => styledSection({
18982
19622
  children: styledContainer({ children: stack({
18983
- gap: "xl",
19623
+ gap: "lg",
18984
19624
  className: "rb-relative"
18985
19625
  }, [...testimonialsHeadingFragment.layout, variantLayout]) }),
18986
19626
  baseClass: "",
@@ -19006,6 +19646,26 @@ const splitGridLayout = styledSection({
19006
19646
  spacing: "comfortable",
19007
19647
  imageClassName: backgroundImageClassName
19008
19648
  });
19649
+ const featuredEditorialLayout = styledSection({
19650
+ children: styledContainer({
19651
+ baseClassName: "rb-relative rb-isolate",
19652
+ children: stack({
19653
+ gap: "lg",
19654
+ className: "rb-relative"
19655
+ }, [el("div", { className: "rb-testimonials-fe-head" }, [text({
19656
+ as: "h2",
19657
+ className: "rb-text-left",
19658
+ headingDecoration: "off"
19659
+ }, when("content.heading"), bind("content.heading")), el("div", { className: "rb-testimonials-fe-leadin" }, [text({
19660
+ as: "p",
19661
+ className: "rb-prose",
19662
+ style: textColorStyle("mutedText")
19663
+ }, when("content.subheading"), bind("content.subheading"))])]), buildFeaturedEditorialLayout()])
19664
+ }),
19665
+ baseClass: "",
19666
+ spacing: "comfortable",
19667
+ imageClassName: backgroundImageClassName
19668
+ });
19009
19669
  const additionalFields = parseFieldDefinitions([
19010
19670
  ...testimonialsHeadingFragment.fields,
19011
19671
  {
@@ -19093,6 +19753,7 @@ const testimonialsBlockDefinition = {
19093
19753
  carousel: carouselLayout$1,
19094
19754
  grid: gridLayout$1,
19095
19755
  "split-grid": splitGridLayout,
19756
+ "featured-editorial": featuredEditorialLayout,
19096
19757
  highlight: highlightLayout
19097
19758
  },
19098
19759
  defaultVariant: "carousel",
@@ -19112,6 +19773,11 @@ const testimonialsBlockDefinition = {
19112
19773
  description: "Heading on the left with testimonial cards on the right",
19113
19774
  icon: "PanelLeft"
19114
19775
  },
19776
+ "featured-editorial": {
19777
+ label: "Featured editorial",
19778
+ description: "A large featured ink quote beside a column of stacked cards (no avatars)",
19779
+ icon: "Quote"
19780
+ },
19115
19781
  highlight: {
19116
19782
  label: "Highlight",
19117
19783
  description: "A single, centered testimonial with no carousel",
@@ -19584,7 +20250,7 @@ function grid$2(props, children) {
19584
20250
  const cardsLayout = styledSection({
19585
20251
  children: styledContainer({
19586
20252
  rootType: "stack",
19587
- rootProps: { gap: "xl" },
20253
+ rootProps: { gap: "lg" },
19588
20254
  children: [stack({
19589
20255
  gap: "sm",
19590
20256
  className: "rb-feature-cards-heading rb-mx-auto rb-max-w-3xl rb-text-center"
@@ -21541,8 +22207,11 @@ const recoverEmptyDescriptionWhenActive = {
21541
22207
  kind: "recover-empty-when-active",
21542
22208
  placeholder: "Add description"
21543
22209
  };
21544
- function eventSectionHeaderLayoutNodes() {
21545
- 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 }))];
22210
+ function eventSectionHeaderLayoutNodes(options = {}) {
22211
+ const headingNode = text({ as: "h2" }, bind("content.heading"), when("content.heading", { authoringPresence: recoverEmptyHeadingWhenActive }));
22212
+ const descriptionNode = richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description", { authoringPresence: recoverEmptyDescriptionWhenActive }));
22213
+ if (options.splitClassName) return [el("div", { className: options.splitClassName }, [headingNode, descriptionNode])];
22214
+ return [headingNode, descriptionNode];
21546
22215
  }
21547
22216
  //#endregion
21548
22217
  //#region ../blocks/src/system/blocks/shop.shared.ts
@@ -21609,7 +22278,8 @@ const eventCalendarDisplayModeValues = [
21609
22278
  "combined",
21610
22279
  "calendar",
21611
22280
  "list",
21612
- "timetable"
22281
+ "timetable",
22282
+ "week-board"
21613
22283
  ];
21614
22284
  const eventCalendarDefaultViewValues = ["month", "week"];
21615
22285
  const eventCalendarListLayoutValues = [
@@ -24051,7 +24721,7 @@ const eventCalendarLayout = [styledSection({
24051
24721
  children: styledContainer({
24052
24722
  rootType: "stack",
24053
24723
  rootProps: { gap: "lg" },
24054
- children: [...eventSectionHeaderLayoutNodes(), el("event-calendar", withApiBindings({
24724
+ children: [...eventSectionHeaderLayoutNodes({ splitClassName: "event-section-head event-section-head--split" }), el("event-calendar", withApiBindings({
24055
24725
  events: { $bind: { from: "data.events" } },
24056
24726
  stage: { $bind: {
24057
24727
  from: "$root.previewStage",
@@ -24162,6 +24832,10 @@ const eventCalendarAdditionalFields = [
24162
24832
  value: "timetable",
24163
24833
  label: "Timetable"
24164
24834
  },
24835
+ {
24836
+ value: "week-board",
24837
+ label: "Week board"
24838
+ },
24165
24839
  {
24166
24840
  value: "list",
24167
24841
  label: "List"
@@ -24344,7 +25018,8 @@ const eventCalendarAdditionalFields = [
24344
25018
  oneOf: [
24345
25019
  "combined",
24346
25020
  "calendar",
24347
- "timetable"
25021
+ "timetable",
25022
+ "week-board"
24348
25023
  ]
24349
25024
  }
24350
25025
  }
@@ -24447,7 +25122,8 @@ const eventCalendarAdditionalFields = [
24447
25122
  "combined",
24448
25123
  "calendar",
24449
25124
  "list",
24450
- "timetable"
25125
+ "timetable",
25126
+ "week-board"
24451
25127
  ]
24452
25128
  }
24453
25129
  }
@@ -24482,7 +25158,8 @@ const eventCalendarAdditionalFields = [
24482
25158
  "combined",
24483
25159
  "calendar",
24484
25160
  "list",
24485
- "timetable"
25161
+ "timetable",
25162
+ "week-board"
24486
25163
  ]
24487
25164
  }, {
24488
25165
  field: "showFilters",
@@ -24505,7 +25182,8 @@ const eventCalendarAdditionalFields = [
24505
25182
  "combined",
24506
25183
  "calendar",
24507
25184
  "list",
24508
- "timetable"
25185
+ "timetable",
25186
+ "week-board"
24509
25187
  ]
24510
25188
  }, {
24511
25189
  field: "showFilters",
@@ -24528,7 +25206,8 @@ const eventCalendarAdditionalFields = [
24528
25206
  "combined",
24529
25207
  "calendar",
24530
25208
  "list",
24531
- "timetable"
25209
+ "timetable",
25210
+ "week-board"
24532
25211
  ]
24533
25212
  }, {
24534
25213
  field: "showFilters",
@@ -24550,7 +25229,8 @@ const eventCalendarAdditionalFields = [
24550
25229
  oneOf: [
24551
25230
  "combined",
24552
25231
  "list",
24553
- "timetable"
25232
+ "timetable",
25233
+ "week-board"
24554
25234
  ]
24555
25235
  }, { allOf: [{
24556
25236
  field: "variant",
@@ -24726,6 +25406,7 @@ const eventCalendarVariants = {
24726
25406
  combined: eventCalendarLayout[0],
24727
25407
  calendar: eventCalendarLayout[0],
24728
25408
  timetable: eventCalendarLayout[0],
25409
+ "week-board": eventCalendarLayout[0],
24729
25410
  list: eventCalendarLayout[0]
24730
25411
  };
24731
25412
  const eventCalendarContentShape = {
@@ -24739,6 +25420,7 @@ const eventCalendarContentShape = {
24739
25420
  { value: "combined" },
24740
25421
  { value: "calendar" },
24741
25422
  { value: "timetable" },
25423
+ { value: "week-board" },
24742
25424
  { value: "list" }
24743
25425
  ]
24744
25426
  },
@@ -24805,6 +25487,7 @@ const eventCalendarBlockDefinition = {
24805
25487
  combined: { label: "Combined" },
24806
25488
  calendar: { label: "Calendar" },
24807
25489
  timetable: { label: "Timetable" },
25490
+ "week-board": { label: "Week board" },
24808
25491
  list: { label: "List" }
24809
25492
  }
24810
25493
  }, eventCalendarContentShape),
@@ -29982,6 +30665,12 @@ const systemBlockDefinitions = [
29982
30665
  family: "centered",
29983
30666
  weight: "featured",
29984
30667
  bias: "soft"
30668
+ }),
30669
+ layoutProfile({
30670
+ variantKey: "featured-editorial",
30671
+ family: "split",
30672
+ weight: "featured",
30673
+ bias: "soft"
29985
30674
  })
29986
30675
  ]
29987
30676
  })),
@@ -30114,6 +30803,11 @@ const systemBlockDefinitions = [
30114
30803
  variantKey: "timetable",
30115
30804
  family: "stack",
30116
30805
  weight: "standard"
30806
+ }),
30807
+ layoutProfile({
30808
+ variantKey: "week-board",
30809
+ family: "stack",
30810
+ weight: "standard"
30117
30811
  })
30118
30812
  ]
30119
30813
  })),