@riverbankcms/sdk 0.104.1 → 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 (120) hide show
  1. package/dist/{PreviewEditorSidebar-CmaPXdKu.mjs → PreviewEditorSidebar-CBMhU9p_.mjs} +3 -3
  2. package/dist/{PreviewEditorUI-C5xRTh2b.mjs → PreviewEditorUI-C5KEw4M8.mjs} +2 -2
  3. package/dist/{SdkPreviewModeRuntime-Eks51NcN.mjs → SdkPreviewModeRuntime-Iq12HjL7.mjs} +1142 -177
  4. package/dist/{SiteChromeCustomizeContext-Bt8zb3qu.mjs → SiteChromeCustomizeContext-Cx0NMdyv.mjs} +1640 -877
  5. package/dist/_dts/ai/src/contracts/proposals.d.ts +10 -0
  6. package/dist/_dts/blocks/src/primitives/layout/Grid.d.ts +12 -8
  7. package/dist/_dts/blocks/src/system/blocks/events/event-calendar.d.ts +3 -0
  8. package/dist/_dts/blocks/src/system/blocks/events/shared/layout.d.ts +11 -1
  9. package/dist/_dts/blocks/src/system/blocks/testimonials.d.ts +7 -1
  10. package/dist/_dts/blocks/src/system/constants/events.d.ts +1 -1
  11. package/dist/_dts/blocks/src/system/fragments/library/testimonialsCarousel.d.ts +12 -0
  12. package/dist/_dts/blocks/src/system/node/validation-constants.d.ts +2 -2
  13. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +14 -0
  14. package/dist/_dts/blocks/src/system/runtime/nodes/buttonAffordance.d.ts +33 -0
  15. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +28 -0
  16. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +32 -0
  17. package/dist/_dts/blocks/src/system/runtime/nodes/shared/contract-schemas.d.ts +1 -0
  18. package/dist/_dts/blocks/src/system/runtime/nodes/svg.d.ts +77 -0
  19. package/dist/_dts/blocks/src/system/transforms/registry/_utils/surfaceForegroundStyles.d.ts +2 -0
  20. package/dist/_dts/blocks/src/system/transforms/registry/boxStyles.d.ts +3 -40
  21. package/dist/_dts/blocks/src/system/transforms/registry/formatting.d.ts +14 -0
  22. package/dist/_dts/blocks/src/system/transforms/registry/layout.d.ts +11 -0
  23. package/dist/_dts/core/src/branded.d.ts +5 -0
  24. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +18 -0
  25. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +2 -2
  26. package/dist/_dts/sdk/src/version.d.ts +1 -1
  27. package/dist/_dts/theme-core/src/ThemeScope.d.ts +1 -1
  28. package/dist/_dts/theme-core/src/buttons/affordanceIcons.d.ts +29 -0
  29. package/dist/_dts/theme-core/src/buttons/generateButtonCss.d.ts +6 -0
  30. package/dist/_dts/theme-core/src/buttons/index.d.ts +1 -0
  31. package/dist/_dts/theme-core/src/buttons/personalities/flat-ink.d.ts +20 -0
  32. package/dist/_dts/theme-core/src/buttons/personalities/index.d.ts +11 -0
  33. package/dist/_dts/theme-core/src/buttons/personalities/types.d.ts +4 -0
  34. package/dist/_dts/theme-core/src/buttons/types.d.ts +34 -0
  35. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  36. package/dist/_dts/theme-core/src/gradients/types.d.ts +27 -3
  37. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +51 -15
  38. package/dist/_dts/theme-core/src/palette/curated/brand-led.d.ts +13 -0
  39. package/dist/_dts/theme-core/src/palette/curated/index.d.ts +12 -0
  40. package/dist/_dts/theme-core/src/palette/historicalThemePalette.d.ts +44 -0
  41. package/dist/_dts/theme-core/src/palette/index.d.ts +1 -0
  42. package/dist/_dts/theme-core/src/palette/legacySelection.d.ts +13 -2
  43. package/dist/_dts/theme-core/src/palette/persistenceReadSource.d.ts +17 -2
  44. package/dist/_dts/theme-core/src/palette/themePaletteCompatibility.d.ts +3 -4
  45. package/dist/_dts/theme-core/src/runtime/themeCssVarNames.d.ts +1 -0
  46. package/dist/_dts/theme-core/src/schema.d.ts +108 -26
  47. package/dist/_dts/theme-core/src/site-styles/accessibleSurfaces.d.ts +2 -1
  48. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +50 -1
  49. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +2 -2
  50. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +4 -2
  51. package/dist/_dts/theme-core/src/site-styles/headerLooks.d.ts +56 -1
  52. package/dist/_dts/theme-core/src/site-styles/typographyPresets.d.ts +16 -1
  53. package/dist/_dts/theme-core/src/status/index.d.ts +0 -1
  54. package/dist/_dts/theme-core/src/surfaces/foregroundContract.d.ts +19 -0
  55. package/dist/_dts/theme-core/src/surfaces/foregroundRoles.d.ts +9 -0
  56. package/dist/_dts/theme-core/src/surfaces/index.d.ts +2 -0
  57. package/dist/_dts/ui/src/button.d.ts +1 -1
  58. package/dist/cli/index.mjs +1558 -468
  59. package/dist/client/{DiscountCodeDisclosure-Dit2aItR.mjs → DiscountCodeDisclosure-BXM73_E5.mjs} +1 -1
  60. package/dist/client/{EventCombined.client-BnREyCjf.mjs → EventCombined.client-DT5elP7j.mjs} +4 -4
  61. package/dist/client/{EventListing.client-0uHld51x.mjs → EventListing.client-pTYGfV9C.mjs} +2 -2
  62. package/dist/client/{LeafletStaticMap-DJcvSfmj.mjs → LeafletStaticMap-dQrhlUe3.mjs} +1 -1
  63. package/dist/client/{MagicLinkForm-EAsIktfw.mjs → MagicLinkForm-u6Dul4j5.mjs} +2 -2
  64. package/dist/client/{StepIndicator-CRR17tYI.mjs → StepIndicator-nWqineJ2.mjs} +2 -2
  65. package/dist/client/{accordion-B47HIuuH.mjs → accordion-CSJcTUyH.mjs} +1 -1
  66. package/dist/client/{bookingForm-I1E-8F9P.mjs → bookingForm-C73wOuVa.mjs} +6 -6
  67. package/dist/client/bookings.mjs +137 -1
  68. package/dist/client/{buttons-BbPn8OKA.mjs → buttons-DfYyqt8C.mjs} +138 -2
  69. package/dist/client/{carousel-OlM7SyBs.mjs → carousel-fMBb0JSc.mjs} +1 -1
  70. package/dist/client/{cart-BKpG5jz6.mjs → cart-BtaCwT3S.mjs} +3 -3
  71. package/dist/client/{checkout-Dmmr6qoU.mjs → checkout-DkTwZSnh.mjs} +3 -3
  72. package/dist/client/client.mjs +2327 -542
  73. package/dist/client/{colorStyles-DuW4Fjqd.mjs → colorStyles-Wyl6JujD.mjs} +45 -4
  74. package/dist/client/{courseRegistration-CflZiqGk.mjs → courseRegistration-DnLmapJu.mjs} +7 -7
  75. package/dist/client/{eventCalendar-BvoLWcp-.mjs → eventCalendar-BvLOc9KY.mjs} +165 -5
  76. package/dist/client/{eventCombined-DsrO8t_t.mjs → eventCombined-kShgJPQr.mjs} +2 -2
  77. package/dist/client/{eventDetails-C3hxgcJm.mjs → eventDetails-C5NTM0Vo.mjs} +3 -3
  78. package/dist/client/{eventListing-C-hb237X.mjs → eventListing-B82ZdKP6.mjs} +2 -2
  79. package/dist/client/{eventRegistration-B0gyzDGi.mjs → eventRegistration-qJVFqBWo.mjs} +7 -7
  80. package/dist/client/{form-C_RUi6oJ.mjs → form-BvAcKiuu.mjs} +2 -2
  81. package/dist/client/{headerSection-IKQiD2U0.mjs → headerSection-NvAiEHAg.mjs} +3 -3
  82. package/dist/client/hooks.mjs +1034 -356
  83. package/dist/client/{locationMap-8ZBrMzRJ.mjs → locationMap-D0j52xfI.mjs} +3 -3
  84. package/dist/client/{media-CZJKL7fi.mjs → media-BCW_g2cQ.mjs} +1 -1
  85. package/dist/client/{newsletterForm-CYk3ucwj.mjs → newsletterForm-B8_i93qH.mjs} +2 -2
  86. package/dist/client/{pricing-B5GmvfIH.mjs → pricing-DIYnyHOk.mjs} +1 -1
  87. package/dist/client/{productDetail-BjS2Dqzo.mjs → productDetail-w0eAHkuQ.mjs} +3 -3
  88. package/dist/client/{productList-BOHvC8My.mjs → productList-Vsguq5Xt.mjs} +3 -3
  89. package/dist/client/{propsDecoder-WS6UEJwZ.mjs → propsDecoder-CD2pJ1WB.mjs} +37 -2
  90. package/dist/client/rendering/client.mjs +1722 -760
  91. package/dist/client/rendering/islands.mjs +19 -19
  92. package/dist/client/rendering.mjs +2303 -518
  93. package/dist/client/{shop-CON03-wN.mjs → shop-Dgxs-Q8F.mjs} +6 -6
  94. package/dist/client/{shop-commerce-DlqrFgH8.mjs → shop-commerce-DJAhsTaT.mjs} +6 -6
  95. package/dist/client/{shop-commerce-store-l74-Kne6.mjs → shop-commerce-store-CfCXltXW.mjs} +1 -1
  96. package/dist/client/{shopShared-CQheEXSg.mjs → shopShared-CmDcPXUt.mjs} +2 -2
  97. package/dist/client/{useDiscountCodeUi-v6OluyWl.mjs → useDiscountCodeUi-CirrT2bM.mjs} +1 -1
  98. package/dist/client/{useEventFilters-BqSTcNOf.mjs → useEventFilters-CNZxlCcO.mjs} +5 -5
  99. package/dist/client/{usePolling-DMfADjIF.mjs → usePolling-Baks_iBI.mjs} +2 -2
  100. package/dist/preview-next/client/runtime.mjs +3 -3
  101. package/dist/{sdk-runtime-BUmJ0DaT.mjs → sdk-runtime-BfNiFeNJ.mjs} +1 -1
  102. package/dist/{separator-Dd-JYok7.mjs → separator-BhAR_Xmf.mjs} +1 -1
  103. package/dist/server/components.mjs +2293 -531
  104. package/dist/server/config-validation.mjs +1040 -357
  105. package/dist/server/config.mjs +1040 -357
  106. package/dist/server/data.mjs +1034 -356
  107. package/dist/server/index.mjs +1 -1
  108. package/dist/server/next.mjs +2296 -534
  109. package/dist/server/page-converter.mjs +1018 -324
  110. package/dist/server/prebuild.mjs +1 -1
  111. package/dist/server/rendering/server.mjs +2278 -521
  112. package/dist/server/rendering.mjs +2293 -531
  113. package/dist/server/routing.mjs +1092 -338
  114. package/dist/server/server.mjs +1016 -338
  115. package/dist/server/theme-bridge.mjs +1240 -77
  116. package/dist/server/theme.mjs +165 -5
  117. package/dist/styles/index.css +617 -34
  118. package/package.json +1 -1
  119. package/dist/_dts/blocks/src/system/transforms/registry/_utils/sectionForegroundStyles.d.ts +0 -2
  120. package/dist/_dts/theme-core/src/status/contextualColors.d.ts +0 -6
@@ -7534,7 +7534,7 @@ var SimpleCache = class {
7534
7534
  * 1. This constant
7535
7535
  * 2. The "version" field in package.json
7536
7536
  */
7537
- const SDK_VERSION = "0.104.1";
7537
+ const SDK_VERSION = "0.104.3";
7538
7538
  //#endregion
7539
7539
  //#region src/client/error.ts
7540
7540
  let _Symbol$for;
@@ -9944,6 +9944,234 @@ function featuresFromHex(hex) {
9944
9944
  };
9945
9945
  }
9946
9946
  //#endregion
9947
+ //#region ../theme-core/src/tokens/colorTokenParts.ts
9948
+ function parseOpacityPercent(source) {
9949
+ const trimmed = source.trim();
9950
+ if (!trimmed) return null;
9951
+ if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
9952
+ const value = Number(trimmed);
9953
+ if (!Number.isFinite(value)) return null;
9954
+ if (value < 0 || value > 100) return null;
9955
+ return value;
9956
+ }
9957
+ function parseColorTokenParts(source) {
9958
+ const trimmed = source.trim();
9959
+ if (trimmed.startsWith("raw:")) return {
9960
+ kind: "raw",
9961
+ value: trimmed.slice(4)
9962
+ };
9963
+ const [token, opacityRaw, extra] = trimmed.split("/");
9964
+ if (!token || extra !== void 0) return {
9965
+ kind: "invalid",
9966
+ token: trimmed
9967
+ };
9968
+ if (opacityRaw === void 0) return {
9969
+ kind: "token",
9970
+ token,
9971
+ opacityPercent: null
9972
+ };
9973
+ const opacityPercent = parseOpacityPercent(opacityRaw);
9974
+ if (opacityPercent === null) return {
9975
+ kind: "invalid",
9976
+ token: trimmed
9977
+ };
9978
+ return {
9979
+ kind: "token",
9980
+ token,
9981
+ opacityPercent
9982
+ };
9983
+ }
9984
+ //#endregion
9985
+ //#region ../theme-core/src/tokens/blockColorTokens.ts
9986
+ const blockColorTokenNames = [
9987
+ "text",
9988
+ "heading",
9989
+ "mutedText",
9990
+ "surface",
9991
+ "background",
9992
+ "foreground",
9993
+ "border",
9994
+ "primary",
9995
+ "secondary",
9996
+ "accent",
9997
+ "success",
9998
+ "warning",
9999
+ "info",
10000
+ "error",
10001
+ "destructive",
10002
+ "scrim",
10003
+ "shadow",
10004
+ "onPrimary",
10005
+ "onSecondary",
10006
+ "onAccent",
10007
+ "onSuccess",
10008
+ "onWarning",
10009
+ "onInfo",
10010
+ "onError",
10011
+ "onDestructive",
10012
+ "onSurface"
10013
+ ];
10014
+ const blockColorTokenNameSet = new Set(blockColorTokenNames);
10015
+ function isBlockColorToken(value) {
10016
+ if (typeof value !== "string") return false;
10017
+ const parts = parseColorTokenParts(value);
10018
+ return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
10019
+ }
10020
+ function parseBlockColorToken(value) {
10021
+ if (!isBlockColorToken(value)) return null;
10022
+ return value.trim();
10023
+ }
10024
+ //#endregion
10025
+ //#region ../theme-core/src/tokens/themeColorTokens.ts
10026
+ /**
10027
+ * Known palette role token names. This is the low-level source of truth for
10028
+ * palette roles used by palette variants, Theme V2 schemas, and runtime color
10029
+ * reference parsing.
10030
+ */
10031
+ const PALETTE_TOKEN_NAMES = [
10032
+ "primary",
10033
+ "primaryForeground",
10034
+ "secondary",
10035
+ "accent",
10036
+ "success",
10037
+ "warning",
10038
+ "destructive",
10039
+ "background",
10040
+ "surface",
10041
+ "surfaceAlt",
10042
+ "muted",
10043
+ "text",
10044
+ "border"
10045
+ ];
10046
+ const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
10047
+ const themeColorBaseTokenNames = [
10048
+ ...PALETTE_TOKEN_NAMES,
10049
+ ...blockColorTokenNames,
10050
+ "white",
10051
+ "neutral",
10052
+ "primaryText",
10053
+ "secondaryForeground",
10054
+ "accentForeground"
10055
+ ];
10056
+ const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
10057
+ const themeColorShadeSteps = [
10058
+ "50",
10059
+ "100",
10060
+ "200",
10061
+ "300",
10062
+ "400",
10063
+ "500",
10064
+ "600",
10065
+ "700",
10066
+ "800",
10067
+ "900",
10068
+ "950"
10069
+ ];
10070
+ const shadeStepSet = new Set(themeColorShadeSteps);
10071
+ function isKnownBaseToken(value) {
10072
+ return themeColorBaseTokenSet.has(value);
10073
+ }
10074
+ function isKnownShadeToken(value) {
10075
+ const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
10076
+ if (!match) return false;
10077
+ const [, base, shade] = match;
10078
+ return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
10079
+ }
10080
+ function isThemeColorTokenBase(value) {
10081
+ return isKnownBaseToken(value) || isKnownShadeToken(value);
10082
+ }
10083
+ function isThemeColorToken(value) {
10084
+ if (typeof value !== "string") return false;
10085
+ const parts = parseColorTokenParts(value);
10086
+ return parts.kind === "token" && isThemeColorTokenBase(parts.token);
10087
+ }
10088
+ function parseThemeColorToken(value) {
10089
+ if (!isThemeColorToken(value)) return null;
10090
+ return value.trim();
10091
+ }
10092
+ const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
10093
+ //#endregion
10094
+ //#region ../theme-core/src/palette/colors.ts
10095
+ const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
10096
+ const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
10097
+ const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
10098
+ const paletteModeSchema = z$1.enum(["light", "dark"]);
10099
+ z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
10100
+ const paletteColorsSchema = z$1.object({
10101
+ primary: hexColorSchema,
10102
+ primaryForeground: hexColorSchema,
10103
+ secondary: hexColorSchema,
10104
+ accent: hexColorSchema,
10105
+ success: hexColorSchema,
10106
+ warning: hexColorSchema,
10107
+ destructive: hexColorSchema,
10108
+ background: hexColorSchema,
10109
+ surface: hexColorSchema,
10110
+ surfaceAlt: hexColorSchema,
10111
+ muted: hexColorSchema,
10112
+ text: hexColorSchema,
10113
+ border: hexColorSchema
10114
+ }).strict().transform((value) => value);
10115
+ function normalizePaletteHex(value) {
10116
+ if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
10117
+ const normalized = value.toLowerCase();
10118
+ if (normalized.length === 7) return normalized;
10119
+ const triplet = normalized.slice(1);
10120
+ const red = triplet.charAt(0);
10121
+ const green = triplet.charAt(1);
10122
+ const blue = triplet.charAt(2);
10123
+ return `#${red}${red}${green}${green}${blue}${blue}`;
10124
+ }
10125
+ function paletteColorsFromEntries(entries) {
10126
+ const values = {};
10127
+ for (const entry of entries) {
10128
+ const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
10129
+ if (!parsedRole.success) return {
10130
+ ok: false,
10131
+ error: {
10132
+ kind: "unknown-role",
10133
+ role: entry.name
10134
+ }
10135
+ };
10136
+ const role = parsedRole.data;
10137
+ if (values[role] !== void 0) return {
10138
+ ok: false,
10139
+ error: {
10140
+ kind: "duplicate-role",
10141
+ role
10142
+ }
10143
+ };
10144
+ const hex = normalizePaletteHex(entry.hex);
10145
+ if (!hex) return {
10146
+ ok: false,
10147
+ error: {
10148
+ kind: "invalid-color",
10149
+ role,
10150
+ value: entry.hex
10151
+ }
10152
+ };
10153
+ values[role] = hex;
10154
+ }
10155
+ const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
10156
+ if (missing.length > 0) return {
10157
+ ok: false,
10158
+ error: {
10159
+ kind: "missing-roles",
10160
+ roles: missing
10161
+ }
10162
+ };
10163
+ return {
10164
+ ok: true,
10165
+ colors: paletteColorsSchema.parse(values)
10166
+ };
10167
+ }
10168
+ function requirePaletteColorsFromEntries(entries) {
10169
+ const result = paletteColorsFromEntries(entries);
10170
+ if (result.ok) return result.colors;
10171
+ throw new Error(`Invalid complete palette: ${result.error.kind}`);
10172
+ }
10173
+ new Set(PALETTE_TOKEN_NAMES);
10174
+ //#endregion
9947
10175
  //#region ../../node_modules/.pnpm/@vibrant+image@4.0.4/node_modules/@vibrant/image/dist/esm/histogram.js
9948
10176
  var Histogram = class {
9949
10177
  constructor(pixels, opts) {
@@ -10978,233 +11206,6 @@ const pipeline = new BasicPipeline().filter.register("default", (r, g, b, a) =>
10978
11206
  //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/browser.js
10979
11207
  Vibrant.use(pipeline);
10980
11208
  //#endregion
10981
- //#region ../theme-core/src/tokens/colorTokenParts.ts
10982
- function parseOpacityPercent(source) {
10983
- const trimmed = source.trim();
10984
- if (!trimmed) return null;
10985
- if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
10986
- const value = Number(trimmed);
10987
- if (!Number.isFinite(value)) return null;
10988
- if (value < 0 || value > 100) return null;
10989
- return value;
10990
- }
10991
- function parseColorTokenParts(source) {
10992
- const trimmed = source.trim();
10993
- if (trimmed.startsWith("raw:")) return {
10994
- kind: "raw",
10995
- value: trimmed.slice(4)
10996
- };
10997
- const [token, opacityRaw, extra] = trimmed.split("/");
10998
- if (!token || extra !== void 0) return {
10999
- kind: "invalid",
11000
- token: trimmed
11001
- };
11002
- if (opacityRaw === void 0) return {
11003
- kind: "token",
11004
- token,
11005
- opacityPercent: null
11006
- };
11007
- const opacityPercent = parseOpacityPercent(opacityRaw);
11008
- if (opacityPercent === null) return {
11009
- kind: "invalid",
11010
- token: trimmed
11011
- };
11012
- return {
11013
- kind: "token",
11014
- token,
11015
- opacityPercent
11016
- };
11017
- }
11018
- //#endregion
11019
- //#region ../theme-core/src/tokens/blockColorTokens.ts
11020
- const blockColorTokenNames = [
11021
- "text",
11022
- "heading",
11023
- "mutedText",
11024
- "surface",
11025
- "background",
11026
- "foreground",
11027
- "border",
11028
- "primary",
11029
- "secondary",
11030
- "accent",
11031
- "success",
11032
- "warning",
11033
- "info",
11034
- "error",
11035
- "destructive",
11036
- "scrim",
11037
- "shadow",
11038
- "onPrimary",
11039
- "onSecondary",
11040
- "onAccent",
11041
- "onSuccess",
11042
- "onWarning",
11043
- "onInfo",
11044
- "onError",
11045
- "onDestructive",
11046
- "onSurface"
11047
- ];
11048
- const blockColorTokenNameSet = new Set(blockColorTokenNames);
11049
- function isBlockColorToken(value) {
11050
- if (typeof value !== "string") return false;
11051
- const parts = parseColorTokenParts(value);
11052
- return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
11053
- }
11054
- function parseBlockColorToken(value) {
11055
- if (!isBlockColorToken(value)) return null;
11056
- return value.trim();
11057
- }
11058
- //#endregion
11059
- //#region ../theme-core/src/tokens/themeColorTokens.ts
11060
- /**
11061
- * Known palette role token names. This is the low-level source of truth for
11062
- * palette roles used by palette variants, Theme V2 schemas, and runtime color
11063
- * reference parsing.
11064
- */
11065
- const PALETTE_TOKEN_NAMES = [
11066
- "primary",
11067
- "primaryForeground",
11068
- "secondary",
11069
- "accent",
11070
- "success",
11071
- "warning",
11072
- "destructive",
11073
- "background",
11074
- "surface",
11075
- "surfaceAlt",
11076
- "muted",
11077
- "text",
11078
- "border"
11079
- ];
11080
- const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
11081
- const themeColorBaseTokenNames = [
11082
- ...PALETTE_TOKEN_NAMES,
11083
- ...blockColorTokenNames,
11084
- "white",
11085
- "neutral",
11086
- "primaryText",
11087
- "secondaryForeground",
11088
- "accentForeground"
11089
- ];
11090
- const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
11091
- const themeColorShadeSteps = [
11092
- "50",
11093
- "100",
11094
- "200",
11095
- "300",
11096
- "400",
11097
- "500",
11098
- "600",
11099
- "700",
11100
- "800",
11101
- "900",
11102
- "950"
11103
- ];
11104
- const shadeStepSet = new Set(themeColorShadeSteps);
11105
- function isKnownBaseToken(value) {
11106
- return themeColorBaseTokenSet.has(value);
11107
- }
11108
- function isKnownShadeToken(value) {
11109
- const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
11110
- if (!match) return false;
11111
- const [, base, shade] = match;
11112
- return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
11113
- }
11114
- function isThemeColorTokenBase(value) {
11115
- return isKnownBaseToken(value) || isKnownShadeToken(value);
11116
- }
11117
- function isThemeColorToken(value) {
11118
- if (typeof value !== "string") return false;
11119
- const parts = parseColorTokenParts(value);
11120
- return parts.kind === "token" && isThemeColorTokenBase(parts.token);
11121
- }
11122
- function parseThemeColorToken(value) {
11123
- if (!isThemeColorToken(value)) return null;
11124
- return value.trim();
11125
- }
11126
- const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
11127
- //#endregion
11128
- //#region ../theme-core/src/palette/colors.ts
11129
- const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
11130
- const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
11131
- const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
11132
- const paletteModeSchema = z$1.enum(["light", "dark"]);
11133
- z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
11134
- const paletteColorsSchema = z$1.object({
11135
- primary: hexColorSchema,
11136
- primaryForeground: hexColorSchema,
11137
- secondary: hexColorSchema,
11138
- accent: hexColorSchema,
11139
- success: hexColorSchema,
11140
- warning: hexColorSchema,
11141
- destructive: hexColorSchema,
11142
- background: hexColorSchema,
11143
- surface: hexColorSchema,
11144
- surfaceAlt: hexColorSchema,
11145
- muted: hexColorSchema,
11146
- text: hexColorSchema,
11147
- border: hexColorSchema
11148
- }).strict().transform((value) => value);
11149
- function normalizePaletteHex(value) {
11150
- if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
11151
- const normalized = value.toLowerCase();
11152
- if (normalized.length === 7) return normalized;
11153
- const triplet = normalized.slice(1);
11154
- const red = triplet.charAt(0);
11155
- const green = triplet.charAt(1);
11156
- const blue = triplet.charAt(2);
11157
- return `#${red}${red}${green}${green}${blue}${blue}`;
11158
- }
11159
- function paletteColorsFromEntries(entries) {
11160
- const values = {};
11161
- for (const entry of entries) {
11162
- const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
11163
- if (!parsedRole.success) return {
11164
- ok: false,
11165
- error: {
11166
- kind: "unknown-role",
11167
- role: entry.name
11168
- }
11169
- };
11170
- const role = parsedRole.data;
11171
- if (values[role] !== void 0) return {
11172
- ok: false,
11173
- error: {
11174
- kind: "duplicate-role",
11175
- role
11176
- }
11177
- };
11178
- const hex = normalizePaletteHex(entry.hex);
11179
- if (!hex) return {
11180
- ok: false,
11181
- error: {
11182
- kind: "invalid-color",
11183
- role,
11184
- value: entry.hex
11185
- }
11186
- };
11187
- values[role] = hex;
11188
- }
11189
- const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
11190
- if (missing.length > 0) return {
11191
- ok: false,
11192
- error: {
11193
- kind: "missing-roles",
11194
- roles: missing
11195
- }
11196
- };
11197
- return {
11198
- ok: true,
11199
- colors: paletteColorsSchema.parse(values)
11200
- };
11201
- }
11202
- function requirePaletteColorsFromEntries(entries) {
11203
- const result = paletteColorsFromEntries(entries);
11204
- if (result.ok) return result.colors;
11205
- throw new Error(`Invalid complete palette: ${result.error.kind}`);
11206
- }
11207
- //#endregion
11208
11209
  //#region ../theme-core/src/site-styles/styleTags.ts
11209
11210
  /**
11210
11211
  * Descriptive style-axis metadata for button personalities and palette
@@ -11478,6 +11479,71 @@ const brandLedJewel = defineCuratedPalette({
11478
11479
  }
11479
11480
  ]
11480
11481
  });
11482
+ const brandLedSpruce = defineCuratedPalette({
11483
+ id: "brand-led-spruce",
11484
+ name: "Brand-led — Spruce",
11485
+ description: "Spruce-ink primary and moss secondary with an amber accent over a green-mist ground and hairline sage borders.",
11486
+ tags: {
11487
+ tone: "classic",
11488
+ energy: "calm",
11489
+ era: "editorial"
11490
+ },
11491
+ mode: "light",
11492
+ colors: [
11493
+ {
11494
+ name: "primary",
11495
+ hex: "#14312b"
11496
+ },
11497
+ {
11498
+ name: "secondary",
11499
+ hex: "#356a59"
11500
+ },
11501
+ {
11502
+ name: "accent",
11503
+ hex: "#f08a4b"
11504
+ },
11505
+ {
11506
+ name: "success",
11507
+ hex: "#356a59"
11508
+ },
11509
+ {
11510
+ name: "warning",
11511
+ hex: "#f08a4b"
11512
+ },
11513
+ {
11514
+ name: "destructive",
11515
+ hex: "#b5561f"
11516
+ },
11517
+ {
11518
+ name: "background",
11519
+ hex: "#ecefe9"
11520
+ },
11521
+ {
11522
+ name: "surface",
11523
+ hex: "#ffffff"
11524
+ },
11525
+ {
11526
+ name: "surfaceAlt",
11527
+ hex: "#f4f6f1"
11528
+ },
11529
+ {
11530
+ name: "muted",
11531
+ hex: "#5c6a64"
11532
+ },
11533
+ {
11534
+ name: "text",
11535
+ hex: "#1c2a26"
11536
+ },
11537
+ {
11538
+ name: "primaryForeground",
11539
+ hex: "#f4f7f1"
11540
+ },
11541
+ {
11542
+ name: "border",
11543
+ hex: "#d7ddd2"
11544
+ }
11545
+ ]
11546
+ });
11481
11547
  const brandLedBurgundyGold = defineCuratedPalette({
11482
11548
  id: "brand-led-burgundy-gold",
11483
11549
  name: "Brand-led — Burgundy Gold",
@@ -12017,6 +12083,7 @@ const curatedPalettes = [
12017
12083
  brandLedWarm,
12018
12084
  brandLedJewel,
12019
12085
  brandLedBurgundyGold,
12086
+ brandLedSpruce,
12020
12087
  defineCuratedPalette({
12021
12088
  id: "warm-neutral-clay",
12022
12089
  name: "Warm Neutral — Clay",
@@ -12481,6 +12548,55 @@ z$1.discriminatedUnion("kind", [
12481
12548
  })
12482
12549
  ]);
12483
12550
  //#endregion
12551
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
12552
+ const SURFACE_FOREGROUND_CSS_VARS = {
12553
+ text: "--rb-surface-text-color",
12554
+ heading: "--rb-surface-heading-color",
12555
+ link: "--rb-surface-link-color",
12556
+ supporting: "--rb-surface-supporting-color"
12557
+ };
12558
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
12559
+ const DEFAULT_ROLE_FALLBACKS = {
12560
+ text: DEFAULT_SURFACE_TEXT_COLOR,
12561
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
12562
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
12563
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
12564
+ };
12565
+ function requireSurfaceColorValue(role, value) {
12566
+ const normalized = value.trim();
12567
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
12568
+ return normalized;
12569
+ }
12570
+ function completeSurfaceForegrounds(input) {
12571
+ const text = requireSurfaceColorValue("text", input.text);
12572
+ return {
12573
+ text,
12574
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
12575
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
12576
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
12577
+ };
12578
+ }
12579
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
12580
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
12581
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
12582
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
12583
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
12584
+ }
12585
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
12586
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
12587
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
12588
+ }
12589
+ function surfaceForegroundStyle(input) {
12590
+ const foregrounds = completeSurfaceForegrounds(input);
12591
+ return {
12592
+ color: foregrounds.text,
12593
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
12594
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
12595
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
12596
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
12597
+ };
12598
+ }
12599
+ //#endregion
12484
12600
  //#region ../theme-core/src/utils/colorStyles.ts
12485
12601
  const COLOR_VAR_PREFIX = "--tb-";
12486
12602
  function parseToken(source) {
@@ -12492,9 +12608,17 @@ function parseToken(source) {
12492
12608
  if (parts.kind === "invalid") return { token: source };
12493
12609
  return {
12494
12610
  token: parts.token,
12495
- alpha: parts.opacityPercent === null ? void 0 : parts.opacityPercent / 100
12611
+ opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
12496
12612
  };
12497
12613
  }
12614
+ const SURFACE_TEXT_ROLE_BY_TOKEN = {
12615
+ text: "text",
12616
+ heading: "heading",
12617
+ mutedText: "supporting"
12618
+ };
12619
+ function surfaceRoleForTextToken(token) {
12620
+ return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
12621
+ }
12498
12622
  /**
12499
12623
  * Converts a theme color token to a CSS rgb value.
12500
12624
  * This is the primary function for applying theme colors to CSS properties.
@@ -12508,11 +12632,11 @@ function parseToken(source) {
12508
12632
  * rgbColorValue('raw:transparent') // => 'transparent'
12509
12633
  */
12510
12634
  function rgbColorValue(token) {
12511
- const { token: baseToken, alpha, raw } = parseToken(token);
12635
+ const { token: baseToken, opacityPercent, raw } = parseToken(token);
12512
12636
  if (raw) return raw;
12513
12637
  const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
12514
- if (alpha === void 0) return `rgb(var(${cssVar}))`;
12515
- return `rgb(var(${cssVar}) / ${alpha})`;
12638
+ if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
12639
+ return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
12516
12640
  }
12517
12641
  /**
12518
12642
  * Creates a backgroundColor style object from a theme token.
@@ -12530,6 +12654,9 @@ function backgroundColorStyle(token) {
12530
12654
  * @returns CSSProperties with color
12531
12655
  */
12532
12656
  function textColorStyle(token) {
12657
+ const parsed = parseToken(token);
12658
+ const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
12659
+ if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
12533
12660
  return { color: rgbColorValue(token) };
12534
12661
  }
12535
12662
  /**
@@ -12799,6 +12926,10 @@ const buttonBorderSchema = z$1.object({
12799
12926
  widthClass: z$1.string(),
12800
12927
  colorToken: themeColorTokenSchema
12801
12928
  });
12929
+ const buttonVariantAffordanceSchema = z$1.object({
12930
+ iconId: z$1.string().min(1),
12931
+ colorToken: themeColorTokenSchema.optional()
12932
+ });
12802
12933
  const variantShadowSchema = z$1.object({
12803
12934
  elevation: z$1.enum([
12804
12935
  "none",
@@ -12852,7 +12983,8 @@ const buttonVariantSchema = z$1.object({
12852
12983
  border: buttonBorderSchema.optional(),
12853
12984
  hoverBackgroundToken: z$1.string().optional(),
12854
12985
  effects: variantEffectsSchema.optional(),
12855
- sizes: variantSizeOverridesSchema.optional()
12986
+ sizes: variantSizeOverridesSchema.optional(),
12987
+ affordance: buttonVariantAffordanceSchema.optional()
12856
12988
  });
12857
12989
  const buttonSystemSchema = z$1.object({
12858
12990
  global: buttonGlobalSettingsSchema,
@@ -13397,6 +13529,136 @@ const editorialLink = defineButtonPersonality({
13397
13529
  }
13398
13530
  });
13399
13531
  //#endregion
13532
+ //#region ../theme-core/src/buttons/personalities/flat-ink.ts
13533
+ /**
13534
+ * Flat Ink — a solid ink primary, a hairline ghost secondary, and a single
13535
+ * amber accent climax, all completely flat (no drop shadow) with a modest
13536
+ * radius. Reads as grounded, editorial and clinician-precise: the Fulcrum
13537
+ * physiotherapy voice. The primary shifts to the secondary (moss) on hover and
13538
+ * every variant lifts 2px; the amber accent is meant to be used sparingly as
13539
+ * the page's one conversion climax.
13540
+ */
13541
+ const flatInk = defineButtonPersonality({
13542
+ id: "flat-ink",
13543
+ name: "Flat Ink",
13544
+ description: "Flat solid-ink primary, hairline ghost secondary, and a single amber accent climax — modest radius, no shadow.",
13545
+ tags: {
13546
+ tone: "classic",
13547
+ energy: "calm",
13548
+ motion: "subtle",
13549
+ era: "editorial"
13550
+ },
13551
+ buttonSystem: {
13552
+ global: {
13553
+ cornerStyle: "rounded",
13554
+ shadow: "low",
13555
+ textTransform: "none",
13556
+ fontWeight: 600,
13557
+ borderWidth: 1,
13558
+ typography: "body",
13559
+ letterSpacing: "tight",
13560
+ paddingPreset: "default",
13561
+ hoverTransform: "lift",
13562
+ hoverColor: "none"
13563
+ },
13564
+ sizes: {
13565
+ sm: {
13566
+ padding: "0.625rem 1.125rem",
13567
+ fontSize: "text-sm"
13568
+ },
13569
+ md: { padding: "0.8125rem 1.375rem" },
13570
+ lg: {
13571
+ padding: "0.9375rem 1.625rem",
13572
+ fontSize: "text-lg"
13573
+ },
13574
+ xl: {
13575
+ padding: "1.0625rem 2rem",
13576
+ fontSize: "text-xl"
13577
+ }
13578
+ },
13579
+ variants: [
13580
+ {
13581
+ id: "primary",
13582
+ name: "Primary",
13583
+ enabled: true,
13584
+ priority: 1,
13585
+ background: {
13586
+ type: "solid",
13587
+ colorToken: "primary"
13588
+ },
13589
+ textColorToken: "primaryForeground",
13590
+ borderRadius: "rounded-lg",
13591
+ affordance: {
13592
+ iconId: "ArrowRight",
13593
+ colorToken: "accent"
13594
+ },
13595
+ shadow: {
13596
+ elevation: "none",
13597
+ softness: null,
13598
+ position: "bottom"
13599
+ },
13600
+ effects: { hover: [{
13601
+ effectId: "lift",
13602
+ options: { translateY: "-2px" }
13603
+ }, {
13604
+ effectId: "background-on-hover",
13605
+ options: { colorToken: "secondary" }
13606
+ }] }
13607
+ },
13608
+ {
13609
+ id: "secondary",
13610
+ name: "Secondary",
13611
+ enabled: true,
13612
+ priority: 2,
13613
+ background: { type: "transparent" },
13614
+ textColorToken: "primary",
13615
+ borderRadius: "rounded-lg",
13616
+ border: {
13617
+ widthClass: "border",
13618
+ colorToken: "border"
13619
+ },
13620
+ shadow: {
13621
+ elevation: "none",
13622
+ softness: null,
13623
+ position: "bottom"
13624
+ },
13625
+ effects: { hover: [{
13626
+ effectId: "lift",
13627
+ options: { translateY: "-2px" }
13628
+ }, {
13629
+ effectId: "background-on-hover",
13630
+ options: { colorToken: "surfaceAlt" }
13631
+ }] }
13632
+ },
13633
+ {
13634
+ id: "accent",
13635
+ name: "Accent",
13636
+ enabled: true,
13637
+ priority: 3,
13638
+ background: {
13639
+ type: "solid",
13640
+ colorToken: "accent"
13641
+ },
13642
+ textColorToken: "accentForeground",
13643
+ borderRadius: "rounded-lg",
13644
+ shadow: {
13645
+ elevation: "none",
13646
+ softness: null,
13647
+ position: "bottom"
13648
+ },
13649
+ affordance: {
13650
+ iconId: "ArrowRight",
13651
+ colorToken: "accentForeground"
13652
+ },
13653
+ effects: { hover: [{
13654
+ effectId: "lift",
13655
+ options: { translateY: "-2px" }
13656
+ }] }
13657
+ }
13658
+ ]
13659
+ }
13660
+ });
13661
+ //#endregion
13400
13662
  //#region ../theme-core/src/buttons/personalities/ink-stamp.ts
13401
13663
  /**
13402
13664
  * Ink Stamp — solid earthy fills with a bordered, hand-stamped block-print
@@ -14167,6 +14429,7 @@ const buttonPersonalities = [
14167
14429
  editorialLink,
14168
14430
  pebble,
14169
14431
  inkStamp,
14432
+ flatInk,
14170
14433
  brushedWash,
14171
14434
  showtimePill,
14172
14435
  joyStamp,
@@ -14524,11 +14787,31 @@ const gradientStopSchema = z$1.object({
14524
14787
  position: z$1.number().min(0).max(100)
14525
14788
  });
14526
14789
  /**
14527
- * Gradient configuration
14790
+ * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
14791
+ * so existing radial gradients are byte-identical.
14792
+ *
14793
+ * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
14794
+ * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
14795
+ */
14796
+ const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
14797
+ /**
14798
+ * Gradient configuration.
14799
+ *
14800
+ * `type` is optional and defaults to `'linear'` so gradients persisted without a
14801
+ * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
14802
+ * `'conic'` are additive kinds:
14803
+ * - linear: `angle` (degrees)
14804
+ * - radial: optional `shape` (circle/ellipse) + `position`
14805
+ * - conic: optional `angle` (from-angle, degrees) + `position`
14528
14806
  */
14529
14807
  const gradientConfigSchema = z$1.object({
14530
- type: z$1.enum(["linear", "radial"]),
14808
+ type: z$1.enum([
14809
+ "linear",
14810
+ "radial",
14811
+ "conic"
14812
+ ]).default("linear"),
14531
14813
  angle: z$1.number().min(0).max(360).optional(),
14814
+ shape: radialGradientShapeSchema.optional(),
14532
14815
  position: z$1.string().optional(),
14533
14816
  stops: z$1.array(gradientStopSchema).min(2)
14534
14817
  });
@@ -15510,6 +15793,7 @@ const typographyScaleSchema = z$1.enum([
15510
15793
  "majorThird",
15511
15794
  "perfectFourth"
15512
15795
  ]);
15796
+ const eyebrowTypographyStyleSchema = z$1.object({ family: z$1.string().min(1) });
15513
15797
  const typographySchema = z$1.object({
15514
15798
  body: bodyTypographyStyleSchema,
15515
15799
  headings: z$1.object({
@@ -15522,7 +15806,8 @@ const typographySchema = z$1.object({
15522
15806
  h6: headingTypographyOverridesSchema.optional()
15523
15807
  }),
15524
15808
  scale: typographyScaleSchema,
15525
- proseLinkStyle: proseLinkStyleSchema.optional()
15809
+ proseLinkStyle: proseLinkStyleSchema.optional(),
15810
+ eyebrow: eyebrowTypographyStyleSchema.optional()
15526
15811
  });
15527
15812
  const spaceSchema = z$1.enum([
15528
15813
  "comfortable",
@@ -15547,7 +15832,8 @@ const shadow = z$1.object({
15547
15832
  "soft",
15548
15833
  "hard"
15549
15834
  ]).nullable(),
15550
- position: z$1.enum(["bottom", "bottom-right"]).default("bottom")
15835
+ position: z$1.enum(["bottom", "bottom-right"]).default("bottom"),
15836
+ colorToken: themeColorTokenSchema.optional()
15551
15837
  });
15552
15838
  const border = z$1.object({
15553
15839
  width: z$1.enum([
@@ -15792,6 +16078,7 @@ const headerSchema = z$1.object({
15792
16078
  navWeight: navFontWeight.default("medium"),
15793
16079
  navTypography: z$1.enum(["body", "heading"]).optional(),
15794
16080
  navTextSize: navLinkStyleSchema.shape.textSize,
16081
+ navPlacement: z$1.enum(["end", "center"]).optional(),
15795
16082
  container: headerContainerSchema,
15796
16083
  border: headerBorderSchema,
15797
16084
  shadow: componentShadowSchema.optional(),
@@ -17997,6 +18284,13 @@ const legacyHeaderLookAliases = defineExactCatalog()([
17997
18284
  styleId: asHeaderStyleId("minimal")
17998
18285
  }
17999
18286
  },
18287
+ {
18288
+ lookId: asHeaderLookId("classic-centered-nav"),
18289
+ selection: {
18290
+ layoutId: asHeaderLayoutId("classic"),
18291
+ styleId: asHeaderStyleId("fine-line")
18292
+ }
18293
+ },
18000
18294
  {
18001
18295
  lookId: asHeaderLookId("brand-solid"),
18002
18296
  selection: {
@@ -18067,6 +18361,50 @@ function getCompatibleHeaderStyleIds(layoutId) {
18067
18361
  return isHeaderStyleCompatibleWithLayout(getHeaderStyleOrThrow(styleId), layoutId);
18068
18362
  });
18069
18363
  }
18364
+ function applyNavTypography(style, typography) {
18365
+ switch (typography) {
18366
+ case "plain": return style;
18367
+ case "heading": return {
18368
+ ...style,
18369
+ typography: "heading",
18370
+ fontWeight: "medium",
18371
+ letterSpacing: "wide"
18372
+ };
18373
+ case "quiet-caps": return {
18374
+ ...style,
18375
+ textTransform: "uppercase",
18376
+ textSize: "sm",
18377
+ letterSpacing: "wide"
18378
+ };
18379
+ case "brand-caps": return {
18380
+ ...style,
18381
+ textTransform: "uppercase",
18382
+ textSize: "sm",
18383
+ letterSpacing: "wider"
18384
+ };
18385
+ default: return assertNever$1(typography, "Unhandled header nav typography");
18386
+ }
18387
+ }
18388
+ function underlineNavLinkStyle(colorToken, underlineColorToken, typography = "plain") {
18389
+ return applyNavTypography({
18390
+ name: "Underline grow",
18391
+ typography: "body",
18392
+ fontWeight: "medium",
18393
+ textTransform: "none",
18394
+ italic: false,
18395
+ colorToken,
18396
+ padding: "compact",
18397
+ paddingX: "compact",
18398
+ borderRadius: "none",
18399
+ effects: { hover: [{
18400
+ effectId: "nav-underline",
18401
+ options: {
18402
+ style: "grow",
18403
+ ...underlineColorToken ? { colorToken: underlineColorToken } : {}
18404
+ }
18405
+ }] }
18406
+ }, typography);
18407
+ }
18070
18408
  function themedHeaderShadow(theme) {
18071
18409
  switch (theme.shadow.elevation) {
18072
18410
  case "none": return;
@@ -18166,6 +18504,38 @@ function cleanBaseHeader(theme) {
18166
18504
  ctaGap: "default"
18167
18505
  };
18168
18506
  }
18507
+ function classicCenteredNavHeader(theme) {
18508
+ return {
18509
+ ...baseHeader(theme),
18510
+ variant: "classic",
18511
+ navPlacement: "center",
18512
+ positioning: "sticky",
18513
+ shrinkOnScroll: false,
18514
+ background: {
18515
+ type: "color",
18516
+ color: "surface"
18517
+ },
18518
+ textColor: "text",
18519
+ navColor: "text",
18520
+ navStyle: "underline-grow",
18521
+ navWeight: "medium",
18522
+ navLinkStyle: underlineNavLinkStyle("text", "accent"),
18523
+ border: {
18524
+ style: "solid",
18525
+ width: "thin",
18526
+ position: "bottom",
18527
+ colorToken: "border"
18528
+ },
18529
+ ctaTreatment: "primary",
18530
+ ctaVariant: resolveHeaderCtaVariant(theme, "primary"),
18531
+ ctaGap: "default",
18532
+ logoText: {
18533
+ mobileMaxWidth: "sm",
18534
+ mobileWrapLines: 1,
18535
+ hideOnShrink: false
18536
+ }
18537
+ };
18538
+ }
18169
18539
  function serifLineHeader(theme) {
18170
18540
  return {
18171
18541
  ...baseHeader(theme),
@@ -18330,6 +18700,21 @@ const headerLookCatalog = defineExactCatalog()([
18330
18700
  logoAccommodations: inlineClassicLogoAccommodations,
18331
18701
  compile: cleanBaseHeader
18332
18702
  },
18703
+ {
18704
+ id: asHeaderLookId("classic-centered-nav"),
18705
+ label: "Centered nav",
18706
+ visualTreatment: "solid-neutral",
18707
+ layoutBehavior: "sticky",
18708
+ configuration: {
18709
+ structure: "inline-classic",
18710
+ treatment: "solid-neutral",
18711
+ behavior: "sticky"
18712
+ },
18713
+ openingRequirement: "none",
18714
+ maxRecommendedNavItems: 6,
18715
+ logoAccommodations: inlineClassicLogoAccommodations,
18716
+ compile: classicCenteredNavHeader
18717
+ },
18333
18718
  {
18334
18719
  id: asHeaderLookId("brand-solid"),
18335
18720
  label: "Brand solid",
@@ -19496,6 +19881,74 @@ const typographyPresets = [
19496
19881
  }
19497
19882
  }
19498
19883
  }
19884
+ }),
19885
+ defineTypographyPreset({
19886
+ id: "typography-preset:fulcrum",
19887
+ name: "Fulcrum",
19888
+ description: "Newsreader serif headings over an IBM Plex Sans body, with an IBM Plex Mono eyebrow role for uppercase kickers and mono captions.",
19889
+ defaultFonts: {
19890
+ body: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
19891
+ heading: "Newsreader, Georgia, \"Times New Roman\", serif"
19892
+ },
19893
+ recommendedFonts: {
19894
+ body: [
19895
+ "IBM Plex Sans",
19896
+ "Source Sans 3",
19897
+ "Inter"
19898
+ ],
19899
+ heading: [
19900
+ "Newsreader",
19901
+ "Source Serif 4",
19902
+ "Lora"
19903
+ ]
19904
+ },
19905
+ slotPolicies: {
19906
+ body: bodyReadablePolicy,
19907
+ heading: displayHeadingPolicy
19908
+ },
19909
+ typography: {
19910
+ body: {
19911
+ family: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
19912
+ size: "lg",
19913
+ weight: "regular",
19914
+ lineHeight: "relaxed",
19915
+ letterSpacing: "normal"
19916
+ },
19917
+ headings: {
19918
+ default: {
19919
+ family: "Newsreader, Georgia, \"Times New Roman\", serif",
19920
+ weight: "medium",
19921
+ lineHeight: "normal",
19922
+ letterSpacing: "tight",
19923
+ colorToken: "primary",
19924
+ italic: null,
19925
+ case: null
19926
+ },
19927
+ h1: {
19928
+ size: "2xl",
19929
+ weight: "medium",
19930
+ lineHeight: "normal",
19931
+ letterSpacing: "tight",
19932
+ italic: null
19933
+ },
19934
+ h2: {
19935
+ size: "xl",
19936
+ weight: "medium",
19937
+ lineHeight: "normal",
19938
+ letterSpacing: "tight",
19939
+ italic: null
19940
+ },
19941
+ h3: {
19942
+ size: "md",
19943
+ weight: "semibold",
19944
+ lineHeight: "normal",
19945
+ letterSpacing: "normal",
19946
+ italic: null
19947
+ }
19948
+ },
19949
+ scale: "spacious",
19950
+ eyebrow: { family: "IBM Plex Mono, ui-monospace, \"SF Mono\", monospace" }
19951
+ }
19499
19952
  })
19500
19953
  ];
19501
19954
  new Map(typographyPresets.map((preset) => [preset.id, preset]));
@@ -19716,7 +20169,8 @@ const curatedSiteStyleIdValues = [
19716
20169
  "site-style:bold-launch",
19717
20170
  "site-style:south-west-twerk-school",
19718
20171
  "site-style:kirtan-ornate",
19719
- "site-style:original-joy"
20172
+ "site-style:original-joy",
20173
+ "site-style:fulcrum"
19720
20174
  ];
19721
20175
  const quietBudget = {
19722
20176
  accentDensity: "low",
@@ -21170,6 +21624,104 @@ const curatedSiteStyles = [
21170
21624
  buttonChoice("showtime-pill")
21171
21625
  ],
21172
21626
  paletteRecommendations: [paletteRecommendation("warm-dark-ember")]
21627
+ }),
21628
+ defineCuratedSiteStyle({
21629
+ id: "site-style:fulcrum",
21630
+ name: "Fulcrum",
21631
+ headingDecoration: {
21632
+ family: "short-center-rule",
21633
+ colorToken: "accent"
21634
+ },
21635
+ 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.",
21636
+ 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.",
21637
+ siteCreatorSelection: {
21638
+ kind: "site_creator_keyword_heuristic",
21639
+ priority: 28
21640
+ },
21641
+ selectionKeywordRules: [
21642
+ {
21643
+ kind: "substring",
21644
+ value: "physiotherap"
21645
+ },
21646
+ {
21647
+ kind: "word",
21648
+ value: "physio"
21649
+ },
21650
+ {
21651
+ kind: "phrase",
21652
+ value: "sports injury"
21653
+ },
21654
+ {
21655
+ kind: "phrase",
21656
+ value: "rehab clinic"
21657
+ },
21658
+ {
21659
+ kind: "substring",
21660
+ value: "musculoskeletal"
21661
+ },
21662
+ {
21663
+ kind: "phrase",
21664
+ value: "sports therapy"
21665
+ }
21666
+ ],
21667
+ tokenRecipes: {
21668
+ palette: "brand-led",
21669
+ contrast: "standard",
21670
+ radius: "rounded",
21671
+ shadow: "subtle",
21672
+ typography: "serif-editorial",
21673
+ spacing: "airy",
21674
+ motion: "subtle"
21675
+ },
21676
+ compositionBudget: quietBudget,
21677
+ typographyPresetChoices: [
21678
+ typographyChoice("typography-preset:fulcrum"),
21679
+ typographyChoice("typography-preset:editorial-serif"),
21680
+ typographyChoice("typography-preset:modern-sans")
21681
+ ],
21682
+ defaultTypographyPresetId: "typography-preset:fulcrum",
21683
+ themeOverrides: {
21684
+ shadow: { colorToken: "primary" },
21685
+ accordions: {
21686
+ background: null,
21687
+ spacing: "comfortable",
21688
+ corners: "square",
21689
+ border: null,
21690
+ shadow: null,
21691
+ icon: {
21692
+ style: "plus-minus",
21693
+ position: "right",
21694
+ size: "md",
21695
+ colorToken: "accent"
21696
+ },
21697
+ trigger: {
21698
+ textColorToken: "primary",
21699
+ fontWeight: "medium",
21700
+ fontFamily: "heading",
21701
+ fontScale: "md"
21702
+ },
21703
+ hover: { textColorToken: "secondary" },
21704
+ content: { textColorToken: "muted" }
21705
+ },
21706
+ testimonialQuote: serifDisplayQuote
21707
+ },
21708
+ recommendedHeaderLookIds: [
21709
+ "classic-centered-nav",
21710
+ "serif-line",
21711
+ "centered-calm",
21712
+ "clean-base"
21713
+ ],
21714
+ recommendedFooterLookIds: ["footer-editorial-columns", "footer-muted-grounding"],
21715
+ buttonPersonalityChoices: [
21716
+ buttonChoice("flat-ink"),
21717
+ buttonChoice("confident-chip"),
21718
+ buttonChoice("editorial-link")
21719
+ ],
21720
+ paletteRecommendations: [
21721
+ paletteRecommendation("brand-led-spruce"),
21722
+ paletteRecommendation("warm-neutral-forest"),
21723
+ paletteRecommendation("soft-natural-sage")
21724
+ ]
21173
21725
  })
21174
21726
  ];
21175
21727
  new Map(curatedSiteStyles.map((style) => [style.id, style]));
@@ -21611,7 +22163,15 @@ function gradientToCss(config, tokens) {
21611
22163
  const stops = config.stops.map((stop) => {
21612
22164
  return `${tokens[stop.color] ?? "#000000"} ${stop.position}%`;
21613
22165
  }).join(", ");
21614
- if (config.type === "radial") return `radial-gradient(circle at ${config.position ?? "center"}, ${stops})`;
22166
+ const type = config.type ?? "linear";
22167
+ if (type === "radial") {
22168
+ const position = config.position ?? "center";
22169
+ return `radial-gradient(${config.shape ?? "circle"} at ${position}, ${stops})`;
22170
+ }
22171
+ if (type === "conic") {
22172
+ const position = config.position ?? "center";
22173
+ return `conic-gradient(from ${config.angle ?? 0}deg at ${position}, ${stops})`;
22174
+ }
21615
22175
  return `linear-gradient(${config.angle ?? 180}deg, ${stops})`;
21616
22176
  }
21617
22177
  const BREAKPOINT_LG = 1024;
@@ -24405,7 +24965,7 @@ function resolveBackgroundColorValue(value, context) {
24405
24965
  return resolveThemeColorReferenceValue(value, { palette: context.theme?.palette });
24406
24966
  }
24407
24967
  //#endregion
24408
- //#region ../blocks/src/system/transforms/registry/_utils/sectionForegroundStyles.ts
24968
+ //#region ../blocks/src/system/transforms/registry/_utils/surfaceForegroundStyles.ts
24409
24969
  function readColorToken(input, key) {
24410
24970
  const value = input[key];
24411
24971
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
@@ -24414,18 +24974,17 @@ function resolveOptionalForegroundColor(value, context) {
24414
24974
  if (!value) return null;
24415
24975
  return resolveBackgroundColorValue(value, context);
24416
24976
  }
24417
- function resolveSectionForegroundStyles(value, context) {
24977
+ function resolveSurfaceForegroundStyles(value, context) {
24418
24978
  if (!value || typeof value !== "object") return {};
24419
24979
  const input = value;
24420
24980
  const resolvedText = resolveOptionalForegroundColor(readColorToken(input, "textColor"), context);
24421
24981
  if (!resolvedText) return {};
24422
- return {
24423
- color: resolvedText,
24424
- "--section-text-color": resolvedText,
24425
- "--section-heading-color": resolveOptionalForegroundColor(readColorToken(input, "headingColor"), context) ?? resolvedText,
24426
- "--section-link-color": resolveOptionalForegroundColor(readColorToken(input, "linkColor"), context) ?? resolvedText,
24427
- "--section-supporting-color": resolveOptionalForegroundColor(readColorToken(input, "supportingColor"), context) ?? resolvedText
24428
- };
24982
+ return { ...surfaceForegroundStyle({
24983
+ text: resolvedText,
24984
+ heading: resolveOptionalForegroundColor(readColorToken(input, "headingColor"), context) ?? void 0,
24985
+ link: resolveOptionalForegroundColor(readColorToken(input, "linkColor"), context) ?? void 0,
24986
+ supporting: resolveOptionalForegroundColor(readColorToken(input, "supportingColor"), context) ?? void 0
24987
+ }) };
24429
24988
  }
24430
24989
  //#endregion
24431
24990
  //#region ../blocks/src/system/transforms/registry/boxStyles.ts
@@ -24763,67 +25322,12 @@ const containerStylesResolveBoxClassName = {
24763
25322
  }
24764
25323
  };
24765
25324
  /**
24766
- * Resolves section text color style object.
24767
- *
24768
- * The value should be the `_sectionStyles.background.textColor` token.
24769
- * Returns an empty object when unset so the section inherits text color.
24770
- *
24771
- * Outputs both `color` and `--section-text-color` CSS variable so that
24772
- * headings (which have explicit color rules in typography CSS) can inherit
24773
- * the section's text color via the CSS variable cascade.
24774
- */
24775
- const sectionTextColorStyleTransform = {
24776
- id: "sectionStyles.resolveTextColorStyle",
24777
- kind: "formatter",
24778
- summary: "Resolve section text color token into a style object",
24779
- run: (value, options, context) => {
24780
- const fallbackToken = options?.fallbackToken;
24781
- if (typeof value === "string" && value.trim()) {
24782
- const colorValue = resolveBackgroundColorValue(value.trim(), context);
24783
- return {
24784
- color: colorValue,
24785
- "--section-text-color": colorValue
24786
- };
24787
- }
24788
- if (typeof fallbackToken === "string" && fallbackToken.trim()) {
24789
- const colorValue = resolveBackgroundColorValue(fallbackToken.trim(), context);
24790
- return {
24791
- color: colorValue,
24792
- "--section-text-color": colorValue
24793
- };
24794
- }
24795
- return {};
24796
- }
24797
- };
24798
- /**
24799
- * Resolves section heading color style object.
24800
- *
24801
- * The value should be the `_sectionStyles.background.headingColor` token.
24802
- * Returns an empty object when unset so headings inherit from textColor or default.
24803
- *
24804
- * Outputs `--section-heading-color` CSS variable which typography CSS can use
24805
- * to style headings differently from body text within a section.
24806
- */
24807
- const sectionHeadingColorStyleTransform = {
24808
- id: "sectionStyles.resolveHeadingColorStyle",
24809
- kind: "formatter",
24810
- summary: "Resolve section heading color token into a style object",
24811
- run: (value, options, context) => {
24812
- const fallbackToken = options?.fallbackToken;
24813
- if (typeof value === "string" && value.trim()) return { "--section-heading-color": resolveBackgroundColorValue(value.trim(), context) };
24814
- if (typeof fallbackToken === "string" && fallbackToken.trim()) return { "--section-heading-color": resolveBackgroundColorValue(fallbackToken.trim(), context) };
24815
- return {};
24816
- }
24817
- };
24818
- /**
24819
- * Resolves section foreground colors from the background object.
25325
+ * Resolves surface foreground colors from the background object.
24820
25326
  *
24821
25327
  * The value should be the full `_sectionStyles.background` object.
24822
25328
  * Outputs:
24823
- * - `color` and `--section-text-color` from background.textColor
24824
- * - `--section-heading-color` from background.headingColor (falls back to textColor if not set)
24825
- * - `--section-link-color` from background.linkColor (falls back to textColor if not set)
24826
- * - `--section-supporting-color` from background.supportingColor (falls back to textColor if not set)
25329
+ * - `color` and the complete `--rb-surface-*-color` contract
25330
+ * - optional roles fall back atomically to background.textColor
24827
25331
  *
24828
25332
  * This combined transform replaces separate foreground color transforms
24829
25333
  * when both need to be applied to the same style prop.
@@ -24831,8 +25335,8 @@ const sectionHeadingColorStyleTransform = {
24831
25335
  const sectionColorsStyleTransform = {
24832
25336
  id: "sectionStyles.resolveColorsStyle",
24833
25337
  kind: "formatter",
24834
- summary: "Resolve section foreground colors into a style object",
24835
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
25338
+ summary: "Resolve surface foreground colors into a style object",
25339
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
24836
25340
  };
24837
25341
  /**
24838
25342
  * Maps semantic gap values to theme-aware gap classes.
@@ -24871,8 +25375,6 @@ const boxStylesTransforms = [
24871
25375
  sectionClassNameTransform,
24872
25376
  containerStylesResolveClassName,
24873
25377
  containerStylesResolveBoxClassName,
24874
- sectionTextColorStyleTransform,
24875
- sectionHeadingColorStyleTransform,
24876
25378
  sectionColorsStyleTransform,
24877
25379
  {
24878
25380
  id: "boxStyles.semanticGap",
@@ -25776,7 +26278,29 @@ const ctaRowFragment = defineFragment({
25776
26278
  fields: [createCtasRepeater({
25777
26279
  label: "Calls to action",
25778
26280
  itemLabel: "CTA",
25779
- maxItems: 3
26281
+ maxItems: 3,
26282
+ button: { variants: [
26283
+ {
26284
+ value: "primary",
26285
+ label: "Primary"
26286
+ },
26287
+ {
26288
+ value: "secondary",
26289
+ label: "Secondary"
26290
+ },
26291
+ {
26292
+ value: "outline",
26293
+ label: "Outline"
26294
+ },
26295
+ {
26296
+ value: "link",
26297
+ label: "Link"
26298
+ },
26299
+ {
26300
+ value: "accent",
26301
+ label: "Accent"
26302
+ }
26303
+ ] }
25780
26304
  })],
25781
26305
  layout: [ctaRow({
25782
26306
  collectionPath: "content.ctas",
@@ -26057,6 +26581,55 @@ function buildGridCardsLayout(className) {
26057
26581
  return el("div", { className }, buildResponsiveGridCards());
26058
26582
  }
26059
26583
  /**
26584
+ * Build a single "featured editorial" testimonial card.
26585
+ *
26586
+ * This card type deliberately renders NO avatar/headshot: an amber quote mark,
26587
+ * the quote body, then the author name and a context line. Colour + surface
26588
+ * treatment is driven entirely by `variantClass` (see testimonials.css):
26589
+ * - `rb-testimonials-fe-card--feature` → spruce-ink card (dark primary bg)
26590
+ * - `rb-testimonials-fe-card--plain` → white surface card
26591
+ *
26592
+ * Extra modifiers (`repeat`, `when`) are forwarded to the card node so the
26593
+ * caller can drive first-vs-rest branching.
26594
+ */
26595
+ function buildFeaturedEditorialCard(variantClass, ...mods) {
26596
+ const quoteMark = text({
26597
+ as: "span",
26598
+ className: "rb-testimonials-fe-qmark",
26599
+ "aria-hidden": "true"
26600
+ }, props({ value: "“" }));
26601
+ const quote = richText({ className: "rb-testimonials-fe-quote rb-max-w-none" }, bind("testimonial.content.body"), when("testimonial.content.body"));
26602
+ const meta = el("div", { className: "rb-testimonials-fe-meta" }, [text({
26603
+ as: "strong",
26604
+ className: "rb-testimonials-fe-name"
26605
+ }, bind("testimonial.content.name", { fallback: "Anonymous" })), text({
26606
+ as: "span",
26607
+ className: "rb-testimonials-fe-context"
26608
+ }, when("testimonial.content.jobTitle"), bind("testimonial.content.jobTitle"))]);
26609
+ return el("div", { className: `rb-testimonials-fe-card ${variantClass}` }, [
26610
+ quoteMark,
26611
+ quote,
26612
+ meta
26613
+ ], ...mods);
26614
+ }
26615
+ /**
26616
+ * Build the "featured editorial" layout: an asymmetric two-column arrangement
26617
+ * where the FIRST testimonial is a large featured (spruce-ink) card and the
26618
+ * remaining testimonials stack as white cards beside it.
26619
+ *
26620
+ * First-vs-rest branching is done without an offset primitive: the featured
26621
+ * card uses `repeat(..., { limit: 1 })` (entry 0 only), and the rest column
26622
+ * repeats over every entry but skips index 0 via `when("tIndex", { equals: 0,
26623
+ * not: true })`. The `$when` guard is re-evaluated per repeated item with the
26624
+ * index scope in context, so entry 0 produces no node in the rest column.
26625
+ */
26626
+ function buildFeaturedEditorialLayout() {
26627
+ 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", {
26628
+ equals: 0,
26629
+ not: true
26630
+ }))])]);
26631
+ }
26632
+ /**
26060
26633
  * Fragment definition for testimonials carousel.
26061
26634
  * Provides field definitions and data loader configuration.
26062
26635
  *
@@ -29074,7 +29647,7 @@ const defaultLayout = styledSection({
29074
29647
  children: styledContainer({
29075
29648
  baseClassName: "rb-relative rb-isolate",
29076
29649
  children: stack({
29077
- gap: "xl",
29650
+ gap: "lg",
29078
29651
  className: "rb-w-full rb-max-w-4xl rb-mx-auto"
29079
29652
  }, [headingLayout, accordionLayout])
29080
29653
  }),
@@ -29091,7 +29664,7 @@ const questionColumnsLayout = styledSection({
29091
29664
  children: styledContainer({
29092
29665
  baseClassName: "rb-relative rb-isolate",
29093
29666
  children: stack({
29094
- gap: "xl",
29667
+ gap: "lg",
29095
29668
  className: "rb-w-full"
29096
29669
  }, [headingLayout, questionColumnsAccordionLayout])
29097
29670
  }),
@@ -29256,7 +29829,26 @@ const formattingTransforms = [
29256
29829
  arrayLengthTransform,
29257
29830
  jsonArrayTransform,
29258
29831
  csvTransform,
29259
- marqueeLoopTransform
29832
+ marqueeLoopTransform,
29833
+ {
29834
+ id: "value.mapToken",
29835
+ kind: "formatter",
29836
+ summary: "Map a state/enum value to a theme token colour (rgb(var(--tb-<role>)))",
29837
+ schema: z$1.object({
29838
+ /**
29839
+ * Maps an input state/enum value (stringified) to a theme token ROLE name
29840
+ * (e.g. `secondary`, `accent`, `muted`, `border`). The role is wrapped to
29841
+ * `rgb(var(--tb-<role>))` so the result drops straight into a `style` binding.
29842
+ */
29843
+ cases: z$1.record(z$1.string(), z$1.string()),
29844
+ /** Role used when the value matches no case. Omit to yield `undefined`. */
29845
+ fallback: z$1.string().optional()
29846
+ }),
29847
+ run: (value, options) => {
29848
+ const role = options.cases[String(value)] ?? options.fallback;
29849
+ return role ? `rgb(var(--tb-${role}))` : void 0;
29850
+ }
29851
+ }
29260
29852
  ];
29261
29853
  //#endregion
29262
29854
  //#region ../blocks/src/system/transforms/registry/ui.ts
@@ -29840,6 +30432,26 @@ const headerLogoTextVisibleTransform = {
29840
30432
  return resolveHeaderBrandDisplay(asPartialObject(value)) !== "logo-only";
29841
30433
  }
29842
30434
  };
30435
+ /**
30436
+ * Selects which classic-layout nav row should render.
30437
+ *
30438
+ * - `'inline'` → classic variant with the historical logo-left / nav-right row
30439
+ * - `'centered'`→ classic variant with a centred (flex-1) nav and a right CTA
30440
+ * - `'other'` → not a classic variant (centered/floating/transparent handle it)
30441
+ *
30442
+ * Consumed by the site-header block's `$when` gates so the two classic rows are
30443
+ * mutually exclusive without introducing a new top-level header variant.
30444
+ */
30445
+ const classicNavModeTransform = {
30446
+ id: "layout.classicNavMode",
30447
+ kind: "formatter",
30448
+ summary: "Resolve which classic-layout nav row to render",
30449
+ run: (value) => {
30450
+ const header = asPartialObject(value);
30451
+ if (header?.variant !== "classic") return "other";
30452
+ return header?.navPlacement === "center" ? "centered" : "inline";
30453
+ }
30454
+ };
29843
30455
  const maxWidthClassTransform = {
29844
30456
  id: "layout.maxWidthClass",
29845
30457
  kind: "string",
@@ -30022,8 +30634,9 @@ const footerRootStyleTransform = {
30022
30634
  styleParts.push(backgroundColorStyle(token));
30023
30635
  }
30024
30636
  }
30025
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
30026
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
30637
+ const foreground = visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
30638
+ if (!visuals.isTransparent && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
30639
+ else if (foreground) styleParts.push({ color: foreground });
30027
30640
  styleParts.push(borderColorStyle("border"));
30028
30641
  return mergeStyles(...styleParts);
30029
30642
  }
@@ -30109,18 +30722,16 @@ const footerBottomBarStyleTransform = {
30109
30722
  const styleParts = [];
30110
30723
  const background = bottomBar?.background;
30111
30724
  if (background?.type === "color") styleParts.push(backgroundColorStyle(background.color ?? "surface"));
30112
- if (bottomBar?.textColor) styleParts.push(textColorStyle(bottomBar.textColor));
30113
30725
  const borderColorToken = bottomBar?.borderTop?.colorToken;
30114
30726
  if (borderColorToken) styleParts.push(borderColorStyle(borderColorToken));
30115
30727
  else styleParts.push(borderColorStyle("border"));
30116
30728
  const borderWidth = bottomBar?.borderTop?.width;
30117
30729
  if (borderWidth && borderWidth !== "none") styleParts.push({ borderTopWidth: borderWidth === "thin" ? "1px" : borderWidth === "medium" ? "2px" : "3px" });
30118
30730
  else if (borderWidth === "none") styleParts.push({ borderTopWidth: "0px" });
30119
- if (!bottomBar?.textColor) {
30120
- const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
30121
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
30122
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
30123
- }
30731
+ const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
30732
+ const foreground = bottomBar?.textColor ? rgbColorValue(bottomBar.textColor) : visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
30733
+ if (background?.type === "color" && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
30734
+ else if (foreground && bottomBar?.textColor) styleParts.push({ color: foreground });
30124
30735
  return mergeStyles(...styleParts);
30125
30736
  }
30126
30737
  };
@@ -30255,6 +30866,7 @@ const layoutTransforms = [
30255
30866
  headerLogoClassTransform,
30256
30867
  headerLogoMediaClassTransform,
30257
30868
  headerLogoTextVisibleTransform,
30869
+ classicNavModeTransform,
30258
30870
  footerRootClassTransform,
30259
30871
  headerRootStyleTransform,
30260
30872
  headerSectionBackgroundTransform,
@@ -30661,7 +31273,18 @@ const classicLayout = inline({
30661
31273
  logoRow,
30662
31274
  createNavRow(`rb-ml-auto ${desktopOnlyDisplay("flex")} rb-gap-6`),
30663
31275
  headerCta
30664
- ], when("$root.theme.header.variant", { equals: "classic" }));
31276
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "inline"));
31277
+ const classicCenteredNavLayout = inline({
31278
+ className: bindProp("$root.theme.header.maxWidth", {
31279
+ transforms: pipe(tx("layout.maxWidthClass", { base: `rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6` })),
31280
+ fallback: `rb-container rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6`
31281
+ }),
31282
+ align: "center"
31283
+ }, [
31284
+ logoRow,
31285
+ createNavRow(`rb-flex-1 rb-justify-center ${desktopOnlyDisplay("flex")} rb-gap-6`, "center"),
31286
+ headerCta
31287
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "centered"));
30665
31288
  const centeredLayout = stack({
30666
31289
  gap: "md",
30667
31290
  align: "center",
@@ -30736,6 +31359,7 @@ const siteHeaderBlockDefinition = { manifest: createBlockManifest({
30736
31359
  }, [
30737
31360
  mobileHeaderBar,
30738
31361
  classicLayout,
31362
+ classicCenteredNavLayout,
30739
31363
  centeredLayout,
30740
31364
  transparentLayout,
30741
31365
  floatingLayout,
@@ -30944,7 +31568,7 @@ const footerLayout = section({
30944
31568
  style: { $bind: {
30945
31569
  from: "$root.theme.footer",
30946
31570
  transforms: [{ id: "layout.footerRootStyle" }],
30947
- fallback: mergeStyles(backgroundColorStyle("surface"), textColorStyle("text"), borderColorStyle("border"))
31571
+ fallback: mergeStyles(backgroundColorStyle("surface"), surfaceForegroundStyle({ text: "rgb(var(--tb-text))" }), borderColorStyle("border"))
30948
31572
  } }
30949
31573
  }, [
30950
31574
  simpleFooterLayout,
@@ -30998,7 +31622,7 @@ const backgroundImageClassName = "rb-absolute rb-inset-0 rb-z-neg-10 rb-h-full r
30998
31622
  */
30999
31623
  const wrapVariant$2 = (variantLayout) => styledSection({
31000
31624
  children: styledContainer({ children: stack({
31001
- gap: "xl",
31625
+ gap: "lg",
31002
31626
  className: "rb-relative"
31003
31627
  }, [...testimonialsHeadingFragment.layout, variantLayout]) }),
31004
31628
  baseClass: "",
@@ -31024,6 +31648,26 @@ const splitGridLayout = styledSection({
31024
31648
  spacing: "comfortable",
31025
31649
  imageClassName: backgroundImageClassName
31026
31650
  });
31651
+ const featuredEditorialLayout = styledSection({
31652
+ children: styledContainer({
31653
+ baseClassName: "rb-relative rb-isolate",
31654
+ children: stack({
31655
+ gap: "lg",
31656
+ className: "rb-relative"
31657
+ }, [el("div", { className: "rb-testimonials-fe-head" }, [text({
31658
+ as: "h2",
31659
+ className: "rb-text-left",
31660
+ headingDecoration: "off"
31661
+ }, when("content.heading"), bind("content.heading")), el("div", { className: "rb-testimonials-fe-leadin" }, [text({
31662
+ as: "p",
31663
+ className: "rb-prose",
31664
+ style: textColorStyle("mutedText")
31665
+ }, when("content.subheading"), bind("content.subheading"))])]), buildFeaturedEditorialLayout()])
31666
+ }),
31667
+ baseClass: "",
31668
+ spacing: "comfortable",
31669
+ imageClassName: backgroundImageClassName
31670
+ });
31027
31671
  const additionalFields = parseFieldDefinitions([
31028
31672
  ...testimonialsHeadingFragment.fields,
31029
31673
  {
@@ -31111,6 +31755,7 @@ const testimonialsBlockDefinition = {
31111
31755
  carousel: carouselLayout$1,
31112
31756
  grid: gridLayout$1,
31113
31757
  "split-grid": splitGridLayout,
31758
+ "featured-editorial": featuredEditorialLayout,
31114
31759
  highlight: highlightLayout
31115
31760
  },
31116
31761
  defaultVariant: "carousel",
@@ -31130,6 +31775,11 @@ const testimonialsBlockDefinition = {
31130
31775
  description: "Heading on the left with testimonial cards on the right",
31131
31776
  icon: "PanelLeft"
31132
31777
  },
31778
+ "featured-editorial": {
31779
+ label: "Featured editorial",
31780
+ description: "A large featured ink quote beside a column of stacked cards (no avatars)",
31781
+ icon: "Quote"
31782
+ },
31133
31783
  highlight: {
31134
31784
  label: "Highlight",
31135
31785
  description: "A single, centered testimonial with no carousel",
@@ -31602,7 +32252,7 @@ function grid$2(props, children) {
31602
32252
  const cardsLayout = styledSection({
31603
32253
  children: styledContainer({
31604
32254
  rootType: "stack",
31605
- rootProps: { gap: "xl" },
32255
+ rootProps: { gap: "lg" },
31606
32256
  children: [stack({
31607
32257
  gap: "sm",
31608
32258
  className: "rb-feature-cards-heading rb-mx-auto rb-max-w-3xl rb-text-center"
@@ -33517,8 +34167,11 @@ const recoverEmptyDescriptionWhenActive = {
33517
34167
  kind: "recover-empty-when-active",
33518
34168
  placeholder: "Add description"
33519
34169
  };
33520
- function eventSectionHeaderLayoutNodes() {
33521
- 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 }))];
34170
+ function eventSectionHeaderLayoutNodes(options = {}) {
34171
+ const headingNode = text({ as: "h2" }, bind("content.heading"), when("content.heading", { authoringPresence: recoverEmptyHeadingWhenActive }));
34172
+ const descriptionNode = richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description", { authoringPresence: recoverEmptyDescriptionWhenActive }));
34173
+ if (options.splitClassName) return [el("div", { className: options.splitClassName }, [headingNode, descriptionNode])];
34174
+ return [headingNode, descriptionNode];
33522
34175
  }
33523
34176
  //#endregion
33524
34177
  //#region ../blocks/src/system/blocks/shop.shared.ts
@@ -33585,7 +34238,8 @@ const eventCalendarDisplayModeValues = [
33585
34238
  "combined",
33586
34239
  "calendar",
33587
34240
  "list",
33588
- "timetable"
34241
+ "timetable",
34242
+ "week-board"
33589
34243
  ];
33590
34244
  const eventCalendarDefaultViewValues = ["month", "week"];
33591
34245
  const eventCalendarListLayoutValues = [
@@ -36027,7 +36681,7 @@ const eventCalendarLayout = [styledSection({
36027
36681
  children: styledContainer({
36028
36682
  rootType: "stack",
36029
36683
  rootProps: { gap: "lg" },
36030
- children: [...eventSectionHeaderLayoutNodes(), el("event-calendar", withApiBindings({
36684
+ children: [...eventSectionHeaderLayoutNodes({ splitClassName: "event-section-head event-section-head--split" }), el("event-calendar", withApiBindings({
36031
36685
  events: { $bind: { from: "data.events" } },
36032
36686
  stage: { $bind: {
36033
36687
  from: "$root.previewStage",
@@ -36138,6 +36792,10 @@ const eventCalendarAdditionalFields = [
36138
36792
  value: "timetable",
36139
36793
  label: "Timetable"
36140
36794
  },
36795
+ {
36796
+ value: "week-board",
36797
+ label: "Week board"
36798
+ },
36141
36799
  {
36142
36800
  value: "list",
36143
36801
  label: "List"
@@ -36320,7 +36978,8 @@ const eventCalendarAdditionalFields = [
36320
36978
  oneOf: [
36321
36979
  "combined",
36322
36980
  "calendar",
36323
- "timetable"
36981
+ "timetable",
36982
+ "week-board"
36324
36983
  ]
36325
36984
  }
36326
36985
  }
@@ -36423,7 +37082,8 @@ const eventCalendarAdditionalFields = [
36423
37082
  "combined",
36424
37083
  "calendar",
36425
37084
  "list",
36426
- "timetable"
37085
+ "timetable",
37086
+ "week-board"
36427
37087
  ]
36428
37088
  }
36429
37089
  }
@@ -36458,7 +37118,8 @@ const eventCalendarAdditionalFields = [
36458
37118
  "combined",
36459
37119
  "calendar",
36460
37120
  "list",
36461
- "timetable"
37121
+ "timetable",
37122
+ "week-board"
36462
37123
  ]
36463
37124
  }, {
36464
37125
  field: "showFilters",
@@ -36481,7 +37142,8 @@ const eventCalendarAdditionalFields = [
36481
37142
  "combined",
36482
37143
  "calendar",
36483
37144
  "list",
36484
- "timetable"
37145
+ "timetable",
37146
+ "week-board"
36485
37147
  ]
36486
37148
  }, {
36487
37149
  field: "showFilters",
@@ -36504,7 +37166,8 @@ const eventCalendarAdditionalFields = [
36504
37166
  "combined",
36505
37167
  "calendar",
36506
37168
  "list",
36507
- "timetable"
37169
+ "timetable",
37170
+ "week-board"
36508
37171
  ]
36509
37172
  }, {
36510
37173
  field: "showFilters",
@@ -36526,7 +37189,8 @@ const eventCalendarAdditionalFields = [
36526
37189
  oneOf: [
36527
37190
  "combined",
36528
37191
  "list",
36529
- "timetable"
37192
+ "timetable",
37193
+ "week-board"
36530
37194
  ]
36531
37195
  }, { allOf: [{
36532
37196
  field: "variant",
@@ -36702,6 +37366,7 @@ const eventCalendarVariants = {
36702
37366
  combined: eventCalendarLayout[0],
36703
37367
  calendar: eventCalendarLayout[0],
36704
37368
  timetable: eventCalendarLayout[0],
37369
+ "week-board": eventCalendarLayout[0],
36705
37370
  list: eventCalendarLayout[0]
36706
37371
  };
36707
37372
  const eventCalendarContentShape = {
@@ -36715,6 +37380,7 @@ const eventCalendarContentShape = {
36715
37380
  { value: "combined" },
36716
37381
  { value: "calendar" },
36717
37382
  { value: "timetable" },
37383
+ { value: "week-board" },
36718
37384
  { value: "list" }
36719
37385
  ]
36720
37386
  },
@@ -36781,6 +37447,7 @@ const eventCalendarBlockDefinition = {
36781
37447
  combined: { label: "Combined" },
36782
37448
  calendar: { label: "Calendar" },
36783
37449
  timetable: { label: "Timetable" },
37450
+ "week-board": { label: "Week board" },
36784
37451
  list: { label: "List" }
36785
37452
  }
36786
37453
  }, eventCalendarContentShape),
@@ -41978,6 +42645,12 @@ const systemBlockDefinitions = [
41978
42645
  family: "centered",
41979
42646
  weight: "featured",
41980
42647
  bias: "soft"
42648
+ }),
42649
+ layoutProfile({
42650
+ variantKey: "featured-editorial",
42651
+ family: "split",
42652
+ weight: "featured",
42653
+ bias: "soft"
41981
42654
  })
41982
42655
  ]
41983
42656
  })),
@@ -42110,6 +42783,11 @@ const systemBlockDefinitions = [
42110
42783
  variantKey: "timetable",
42111
42784
  family: "stack",
42112
42785
  weight: "standard"
42786
+ }),
42787
+ layoutProfile({
42788
+ variantKey: "week-board",
42789
+ family: "stack",
42790
+ weight: "standard"
42113
42791
  })
42114
42792
  ]
42115
42793
  })),
@@ -42418,8 +43096,8 @@ const backgroundTransforms = [
42418
43096
  {
42419
43097
  id: "background.resolveColorsStyle",
42420
43098
  kind: "formatter",
42421
- summary: "Resolve section foreground colors from a background object into scoped CSS vars",
42422
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
43099
+ summary: "Resolve surface foreground colors from a background object into scoped CSS vars",
43100
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
42423
43101
  },
42424
43102
  {
42425
43103
  id: "background.resolveAllowOverflow",