@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
@@ -500,6 +500,234 @@ function featuresFromHex(hex) {
500
500
  };
501
501
  }
502
502
  //#endregion
503
+ //#region ../theme-core/src/tokens/colorTokenParts.ts
504
+ function parseOpacityPercent(source) {
505
+ const trimmed = source.trim();
506
+ if (!trimmed) return null;
507
+ if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
508
+ const value = Number(trimmed);
509
+ if (!Number.isFinite(value)) return null;
510
+ if (value < 0 || value > 100) return null;
511
+ return value;
512
+ }
513
+ function parseColorTokenParts(source) {
514
+ const trimmed = source.trim();
515
+ if (trimmed.startsWith("raw:")) return {
516
+ kind: "raw",
517
+ value: trimmed.slice(4)
518
+ };
519
+ const [token, opacityRaw, extra] = trimmed.split("/");
520
+ if (!token || extra !== void 0) return {
521
+ kind: "invalid",
522
+ token: trimmed
523
+ };
524
+ if (opacityRaw === void 0) return {
525
+ kind: "token",
526
+ token,
527
+ opacityPercent: null
528
+ };
529
+ const opacityPercent = parseOpacityPercent(opacityRaw);
530
+ if (opacityPercent === null) return {
531
+ kind: "invalid",
532
+ token: trimmed
533
+ };
534
+ return {
535
+ kind: "token",
536
+ token,
537
+ opacityPercent
538
+ };
539
+ }
540
+ //#endregion
541
+ //#region ../theme-core/src/tokens/blockColorTokens.ts
542
+ const blockColorTokenNames = [
543
+ "text",
544
+ "heading",
545
+ "mutedText",
546
+ "surface",
547
+ "background",
548
+ "foreground",
549
+ "border",
550
+ "primary",
551
+ "secondary",
552
+ "accent",
553
+ "success",
554
+ "warning",
555
+ "info",
556
+ "error",
557
+ "destructive",
558
+ "scrim",
559
+ "shadow",
560
+ "onPrimary",
561
+ "onSecondary",
562
+ "onAccent",
563
+ "onSuccess",
564
+ "onWarning",
565
+ "onInfo",
566
+ "onError",
567
+ "onDestructive",
568
+ "onSurface"
569
+ ];
570
+ const blockColorTokenNameSet = new Set(blockColorTokenNames);
571
+ function isBlockColorToken(value) {
572
+ if (typeof value !== "string") return false;
573
+ const parts = parseColorTokenParts(value);
574
+ return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
575
+ }
576
+ function parseBlockColorToken(value) {
577
+ if (!isBlockColorToken(value)) return null;
578
+ return value.trim();
579
+ }
580
+ //#endregion
581
+ //#region ../theme-core/src/tokens/themeColorTokens.ts
582
+ /**
583
+ * Known palette role token names. This is the low-level source of truth for
584
+ * palette roles used by palette variants, Theme V2 schemas, and runtime color
585
+ * reference parsing.
586
+ */
587
+ const PALETTE_TOKEN_NAMES = [
588
+ "primary",
589
+ "primaryForeground",
590
+ "secondary",
591
+ "accent",
592
+ "success",
593
+ "warning",
594
+ "destructive",
595
+ "background",
596
+ "surface",
597
+ "surfaceAlt",
598
+ "muted",
599
+ "text",
600
+ "border"
601
+ ];
602
+ const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
603
+ const themeColorBaseTokenNames = [
604
+ ...PALETTE_TOKEN_NAMES,
605
+ ...blockColorTokenNames,
606
+ "white",
607
+ "neutral",
608
+ "primaryText",
609
+ "secondaryForeground",
610
+ "accentForeground"
611
+ ];
612
+ const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
613
+ const themeColorShadeSteps = [
614
+ "50",
615
+ "100",
616
+ "200",
617
+ "300",
618
+ "400",
619
+ "500",
620
+ "600",
621
+ "700",
622
+ "800",
623
+ "900",
624
+ "950"
625
+ ];
626
+ const shadeStepSet = new Set(themeColorShadeSteps);
627
+ function isKnownBaseToken(value) {
628
+ return themeColorBaseTokenSet.has(value);
629
+ }
630
+ function isKnownShadeToken(value) {
631
+ const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
632
+ if (!match) return false;
633
+ const [, base, shade] = match;
634
+ return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
635
+ }
636
+ function isThemeColorTokenBase(value) {
637
+ return isKnownBaseToken(value) || isKnownShadeToken(value);
638
+ }
639
+ function isThemeColorToken(value) {
640
+ if (typeof value !== "string") return false;
641
+ const parts = parseColorTokenParts(value);
642
+ return parts.kind === "token" && isThemeColorTokenBase(parts.token);
643
+ }
644
+ function parseThemeColorToken(value) {
645
+ if (!isThemeColorToken(value)) return null;
646
+ return value.trim();
647
+ }
648
+ const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
649
+ //#endregion
650
+ //#region ../theme-core/src/palette/colors.ts
651
+ const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
652
+ const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
653
+ const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
654
+ const paletteModeSchema = z$1.enum(["light", "dark"]);
655
+ z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
656
+ const paletteColorsSchema = z$1.object({
657
+ primary: hexColorSchema,
658
+ primaryForeground: hexColorSchema,
659
+ secondary: hexColorSchema,
660
+ accent: hexColorSchema,
661
+ success: hexColorSchema,
662
+ warning: hexColorSchema,
663
+ destructive: hexColorSchema,
664
+ background: hexColorSchema,
665
+ surface: hexColorSchema,
666
+ surfaceAlt: hexColorSchema,
667
+ muted: hexColorSchema,
668
+ text: hexColorSchema,
669
+ border: hexColorSchema
670
+ }).strict().transform((value) => value);
671
+ function normalizePaletteHex(value) {
672
+ if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
673
+ const normalized = value.toLowerCase();
674
+ if (normalized.length === 7) return normalized;
675
+ const triplet = normalized.slice(1);
676
+ const red = triplet.charAt(0);
677
+ const green = triplet.charAt(1);
678
+ const blue = triplet.charAt(2);
679
+ return `#${red}${red}${green}${green}${blue}${blue}`;
680
+ }
681
+ function paletteColorsFromEntries(entries) {
682
+ const values = {};
683
+ for (const entry of entries) {
684
+ const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
685
+ if (!parsedRole.success) return {
686
+ ok: false,
687
+ error: {
688
+ kind: "unknown-role",
689
+ role: entry.name
690
+ }
691
+ };
692
+ const role = parsedRole.data;
693
+ if (values[role] !== void 0) return {
694
+ ok: false,
695
+ error: {
696
+ kind: "duplicate-role",
697
+ role
698
+ }
699
+ };
700
+ const hex = normalizePaletteHex(entry.hex);
701
+ if (!hex) return {
702
+ ok: false,
703
+ error: {
704
+ kind: "invalid-color",
705
+ role,
706
+ value: entry.hex
707
+ }
708
+ };
709
+ values[role] = hex;
710
+ }
711
+ const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
712
+ if (missing.length > 0) return {
713
+ ok: false,
714
+ error: {
715
+ kind: "missing-roles",
716
+ roles: missing
717
+ }
718
+ };
719
+ return {
720
+ ok: true,
721
+ colors: paletteColorsSchema.parse(values)
722
+ };
723
+ }
724
+ function requirePaletteColorsFromEntries(entries) {
725
+ const result = paletteColorsFromEntries(entries);
726
+ if (result.ok) return result.colors;
727
+ throw new Error(`Invalid complete palette: ${result.error.kind}`);
728
+ }
729
+ new Set(PALETTE_TOKEN_NAMES);
730
+ //#endregion
503
731
  //#region ../../node_modules/.pnpm/@vibrant+image@4.0.4/node_modules/@vibrant/image/dist/esm/histogram.js
504
732
  var Histogram = class {
505
733
  constructor(pixels, opts) {
@@ -1511,255 +1739,28 @@ function _generateEmptySwatches(palette, _maxPopulation, opts) {
1511
1739
  }
1512
1740
  if (!palette.DarkMuted && palette.DarkVibrant) {
1513
1741
  let [h, s, l] = palette.DarkVibrant.hsl;
1514
- l = opts.targetMutesSaturation;
1515
- palette.DarkMuted = new Swatch(hslToRgb(h, s, l), 0);
1516
- }
1517
- if (!palette.LightMuted && palette.LightVibrant) {
1518
- let [h, s, l] = palette.LightVibrant.hsl;
1519
- l = opts.targetMutesSaturation;
1520
- palette.LightMuted = new Swatch(hslToRgb(h, s, l), 0);
1521
- }
1522
- }
1523
- const DefaultGenerator = ((swatches, opts) => {
1524
- opts = Object.assign({}, DefaultOpts, opts);
1525
- const maxPopulation = _findMaxPopulation(swatches);
1526
- const palette = _generateVariationColors(swatches, maxPopulation, opts);
1527
- _generateEmptySwatches(palette, maxPopulation, opts);
1528
- return palette;
1529
- });
1530
- //#endregion
1531
- //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/pipeline/index.js
1532
- 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);
1533
- //#endregion
1534
- //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/browser.js
1535
- Vibrant.use(pipeline);
1536
- //#endregion
1537
- //#region ../theme-core/src/tokens/colorTokenParts.ts
1538
- function parseOpacityPercent(source) {
1539
- const trimmed = source.trim();
1540
- if (!trimmed) return null;
1541
- if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
1542
- const value = Number(trimmed);
1543
- if (!Number.isFinite(value)) return null;
1544
- if (value < 0 || value > 100) return null;
1545
- return value;
1546
- }
1547
- function parseColorTokenParts(source) {
1548
- const trimmed = source.trim();
1549
- if (trimmed.startsWith("raw:")) return {
1550
- kind: "raw",
1551
- value: trimmed.slice(4)
1552
- };
1553
- const [token, opacityRaw, extra] = trimmed.split("/");
1554
- if (!token || extra !== void 0) return {
1555
- kind: "invalid",
1556
- token: trimmed
1557
- };
1558
- if (opacityRaw === void 0) return {
1559
- kind: "token",
1560
- token,
1561
- opacityPercent: null
1562
- };
1563
- const opacityPercent = parseOpacityPercent(opacityRaw);
1564
- if (opacityPercent === null) return {
1565
- kind: "invalid",
1566
- token: trimmed
1567
- };
1568
- return {
1569
- kind: "token",
1570
- token,
1571
- opacityPercent
1572
- };
1573
- }
1574
- //#endregion
1575
- //#region ../theme-core/src/tokens/blockColorTokens.ts
1576
- const blockColorTokenNames = [
1577
- "text",
1578
- "heading",
1579
- "mutedText",
1580
- "surface",
1581
- "background",
1582
- "foreground",
1583
- "border",
1584
- "primary",
1585
- "secondary",
1586
- "accent",
1587
- "success",
1588
- "warning",
1589
- "info",
1590
- "error",
1591
- "destructive",
1592
- "scrim",
1593
- "shadow",
1594
- "onPrimary",
1595
- "onSecondary",
1596
- "onAccent",
1597
- "onSuccess",
1598
- "onWarning",
1599
- "onInfo",
1600
- "onError",
1601
- "onDestructive",
1602
- "onSurface"
1603
- ];
1604
- const blockColorTokenNameSet = new Set(blockColorTokenNames);
1605
- function isBlockColorToken(value) {
1606
- if (typeof value !== "string") return false;
1607
- const parts = parseColorTokenParts(value);
1608
- return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
1609
- }
1610
- function parseBlockColorToken(value) {
1611
- if (!isBlockColorToken(value)) return null;
1612
- return value.trim();
1613
- }
1614
- //#endregion
1615
- //#region ../theme-core/src/tokens/themeColorTokens.ts
1616
- /**
1617
- * Known palette role token names. This is the low-level source of truth for
1618
- * palette roles used by palette variants, Theme V2 schemas, and runtime color
1619
- * reference parsing.
1620
- */
1621
- const PALETTE_TOKEN_NAMES = [
1622
- "primary",
1623
- "primaryForeground",
1624
- "secondary",
1625
- "accent",
1626
- "success",
1627
- "warning",
1628
- "destructive",
1629
- "background",
1630
- "surface",
1631
- "surfaceAlt",
1632
- "muted",
1633
- "text",
1634
- "border"
1635
- ];
1636
- const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
1637
- const themeColorBaseTokenNames = [
1638
- ...PALETTE_TOKEN_NAMES,
1639
- ...blockColorTokenNames,
1640
- "white",
1641
- "neutral",
1642
- "primaryText",
1643
- "secondaryForeground",
1644
- "accentForeground"
1645
- ];
1646
- const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
1647
- const themeColorShadeSteps = [
1648
- "50",
1649
- "100",
1650
- "200",
1651
- "300",
1652
- "400",
1653
- "500",
1654
- "600",
1655
- "700",
1656
- "800",
1657
- "900",
1658
- "950"
1659
- ];
1660
- const shadeStepSet = new Set(themeColorShadeSteps);
1661
- function isKnownBaseToken(value) {
1662
- return themeColorBaseTokenSet.has(value);
1663
- }
1664
- function isKnownShadeToken(value) {
1665
- const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
1666
- if (!match) return false;
1667
- const [, base, shade] = match;
1668
- return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
1669
- }
1670
- function isThemeColorTokenBase(value) {
1671
- return isKnownBaseToken(value) || isKnownShadeToken(value);
1672
- }
1673
- function isThemeColorToken(value) {
1674
- if (typeof value !== "string") return false;
1675
- const parts = parseColorTokenParts(value);
1676
- return parts.kind === "token" && isThemeColorTokenBase(parts.token);
1677
- }
1678
- function parseThemeColorToken(value) {
1679
- if (!isThemeColorToken(value)) return null;
1680
- return value.trim();
1681
- }
1682
- const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
1683
- //#endregion
1684
- //#region ../theme-core/src/palette/colors.ts
1685
- const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
1686
- const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
1687
- const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
1688
- const paletteModeSchema = z$1.enum(["light", "dark"]);
1689
- z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
1690
- const paletteColorsSchema = z$1.object({
1691
- primary: hexColorSchema,
1692
- primaryForeground: hexColorSchema,
1693
- secondary: hexColorSchema,
1694
- accent: hexColorSchema,
1695
- success: hexColorSchema,
1696
- warning: hexColorSchema,
1697
- destructive: hexColorSchema,
1698
- background: hexColorSchema,
1699
- surface: hexColorSchema,
1700
- surfaceAlt: hexColorSchema,
1701
- muted: hexColorSchema,
1702
- text: hexColorSchema,
1703
- border: hexColorSchema
1704
- }).strict().transform((value) => value);
1705
- function normalizePaletteHex(value) {
1706
- if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
1707
- const normalized = value.toLowerCase();
1708
- if (normalized.length === 7) return normalized;
1709
- const triplet = normalized.slice(1);
1710
- const red = triplet.charAt(0);
1711
- const green = triplet.charAt(1);
1712
- const blue = triplet.charAt(2);
1713
- return `#${red}${red}${green}${green}${blue}${blue}`;
1714
- }
1715
- function paletteColorsFromEntries(entries) {
1716
- const values = {};
1717
- for (const entry of entries) {
1718
- const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
1719
- if (!parsedRole.success) return {
1720
- ok: false,
1721
- error: {
1722
- kind: "unknown-role",
1723
- role: entry.name
1724
- }
1725
- };
1726
- const role = parsedRole.data;
1727
- if (values[role] !== void 0) return {
1728
- ok: false,
1729
- error: {
1730
- kind: "duplicate-role",
1731
- role
1732
- }
1733
- };
1734
- const hex = normalizePaletteHex(entry.hex);
1735
- if (!hex) return {
1736
- ok: false,
1737
- error: {
1738
- kind: "invalid-color",
1739
- role,
1740
- value: entry.hex
1741
- }
1742
- };
1743
- values[role] = hex;
1742
+ l = opts.targetMutesSaturation;
1743
+ palette.DarkMuted = new Swatch(hslToRgb(h, s, l), 0);
1744
+ }
1745
+ if (!palette.LightMuted && palette.LightVibrant) {
1746
+ let [h, s, l] = palette.LightVibrant.hsl;
1747
+ l = opts.targetMutesSaturation;
1748
+ palette.LightMuted = new Swatch(hslToRgb(h, s, l), 0);
1744
1749
  }
1745
- const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
1746
- if (missing.length > 0) return {
1747
- ok: false,
1748
- error: {
1749
- kind: "missing-roles",
1750
- roles: missing
1751
- }
1752
- };
1753
- return {
1754
- ok: true,
1755
- colors: paletteColorsSchema.parse(values)
1756
- };
1757
- }
1758
- function requirePaletteColorsFromEntries(entries) {
1759
- const result = paletteColorsFromEntries(entries);
1760
- if (result.ok) return result.colors;
1761
- throw new Error(`Invalid complete palette: ${result.error.kind}`);
1762
1750
  }
1751
+ const DefaultGenerator = ((swatches, opts) => {
1752
+ opts = Object.assign({}, DefaultOpts, opts);
1753
+ const maxPopulation = _findMaxPopulation(swatches);
1754
+ const palette = _generateVariationColors(swatches, maxPopulation, opts);
1755
+ _generateEmptySwatches(palette, maxPopulation, opts);
1756
+ return palette;
1757
+ });
1758
+ //#endregion
1759
+ //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/pipeline/index.js
1760
+ 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);
1761
+ //#endregion
1762
+ //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/browser.js
1763
+ Vibrant.use(pipeline);
1763
1764
  //#endregion
1764
1765
  //#region ../theme-core/src/site-styles/styleTags.ts
1765
1766
  /**
@@ -2034,6 +2035,71 @@ const brandLedJewel = defineCuratedPalette({
2034
2035
  }
2035
2036
  ]
2036
2037
  });
2038
+ const brandLedSpruce = defineCuratedPalette({
2039
+ id: "brand-led-spruce",
2040
+ name: "Brand-led — Spruce",
2041
+ description: "Spruce-ink primary and moss secondary with an amber accent over a green-mist ground and hairline sage borders.",
2042
+ tags: {
2043
+ tone: "classic",
2044
+ energy: "calm",
2045
+ era: "editorial"
2046
+ },
2047
+ mode: "light",
2048
+ colors: [
2049
+ {
2050
+ name: "primary",
2051
+ hex: "#14312b"
2052
+ },
2053
+ {
2054
+ name: "secondary",
2055
+ hex: "#356a59"
2056
+ },
2057
+ {
2058
+ name: "accent",
2059
+ hex: "#f08a4b"
2060
+ },
2061
+ {
2062
+ name: "success",
2063
+ hex: "#356a59"
2064
+ },
2065
+ {
2066
+ name: "warning",
2067
+ hex: "#f08a4b"
2068
+ },
2069
+ {
2070
+ name: "destructive",
2071
+ hex: "#b5561f"
2072
+ },
2073
+ {
2074
+ name: "background",
2075
+ hex: "#ecefe9"
2076
+ },
2077
+ {
2078
+ name: "surface",
2079
+ hex: "#ffffff"
2080
+ },
2081
+ {
2082
+ name: "surfaceAlt",
2083
+ hex: "#f4f6f1"
2084
+ },
2085
+ {
2086
+ name: "muted",
2087
+ hex: "#5c6a64"
2088
+ },
2089
+ {
2090
+ name: "text",
2091
+ hex: "#1c2a26"
2092
+ },
2093
+ {
2094
+ name: "primaryForeground",
2095
+ hex: "#f4f7f1"
2096
+ },
2097
+ {
2098
+ name: "border",
2099
+ hex: "#d7ddd2"
2100
+ }
2101
+ ]
2102
+ });
2037
2103
  const brandLedBurgundyGold = defineCuratedPalette({
2038
2104
  id: "brand-led-burgundy-gold",
2039
2105
  name: "Brand-led — Burgundy Gold",
@@ -2573,6 +2639,7 @@ const curatedPalettes = [
2573
2639
  brandLedWarm,
2574
2640
  brandLedJewel,
2575
2641
  brandLedBurgundyGold,
2642
+ brandLedSpruce,
2576
2643
  defineCuratedPalette({
2577
2644
  id: "warm-neutral-clay",
2578
2645
  name: "Warm Neutral — Clay",
@@ -3037,6 +3104,55 @@ z$1.discriminatedUnion("kind", [
3037
3104
  })
3038
3105
  ]);
3039
3106
  //#endregion
3107
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
3108
+ const SURFACE_FOREGROUND_CSS_VARS = {
3109
+ text: "--rb-surface-text-color",
3110
+ heading: "--rb-surface-heading-color",
3111
+ link: "--rb-surface-link-color",
3112
+ supporting: "--rb-surface-supporting-color"
3113
+ };
3114
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
3115
+ const DEFAULT_ROLE_FALLBACKS = {
3116
+ text: DEFAULT_SURFACE_TEXT_COLOR,
3117
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
3118
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
3119
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
3120
+ };
3121
+ function requireSurfaceColorValue(role, value) {
3122
+ const normalized = value.trim();
3123
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
3124
+ return normalized;
3125
+ }
3126
+ function completeSurfaceForegrounds(input) {
3127
+ const text = requireSurfaceColorValue("text", input.text);
3128
+ return {
3129
+ text,
3130
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
3131
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
3132
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
3133
+ };
3134
+ }
3135
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
3136
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
3137
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
3138
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
3139
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
3140
+ }
3141
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
3142
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
3143
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
3144
+ }
3145
+ function surfaceForegroundStyle(input) {
3146
+ const foregrounds = completeSurfaceForegrounds(input);
3147
+ return {
3148
+ color: foregrounds.text,
3149
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
3150
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
3151
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
3152
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
3153
+ };
3154
+ }
3155
+ //#endregion
3040
3156
  //#region ../theme-core/src/utils/colorStyles.ts
3041
3157
  const COLOR_VAR_PREFIX = "--tb-";
3042
3158
  function parseToken(source) {
@@ -3048,9 +3164,17 @@ function parseToken(source) {
3048
3164
  if (parts.kind === "invalid") return { token: source };
3049
3165
  return {
3050
3166
  token: parts.token,
3051
- alpha: parts.opacityPercent === null ? void 0 : parts.opacityPercent / 100
3167
+ opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
3052
3168
  };
3053
3169
  }
3170
+ const SURFACE_TEXT_ROLE_BY_TOKEN = {
3171
+ text: "text",
3172
+ heading: "heading",
3173
+ mutedText: "supporting"
3174
+ };
3175
+ function surfaceRoleForTextToken(token) {
3176
+ return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
3177
+ }
3054
3178
  /**
3055
3179
  * Converts a theme color token to a CSS rgb value.
3056
3180
  * This is the primary function for applying theme colors to CSS properties.
@@ -3064,11 +3188,11 @@ function parseToken(source) {
3064
3188
  * rgbColorValue('raw:transparent') // => 'transparent'
3065
3189
  */
3066
3190
  function rgbColorValue(token) {
3067
- const { token: baseToken, alpha, raw } = parseToken(token);
3191
+ const { token: baseToken, opacityPercent, raw } = parseToken(token);
3068
3192
  if (raw) return raw;
3069
3193
  const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
3070
- if (alpha === void 0) return `rgb(var(${cssVar}))`;
3071
- return `rgb(var(${cssVar}) / ${alpha})`;
3194
+ if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
3195
+ return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
3072
3196
  }
3073
3197
  /**
3074
3198
  * Creates a backgroundColor style object from a theme token.
@@ -3086,6 +3210,9 @@ function backgroundColorStyle(token) {
3086
3210
  * @returns CSSProperties with color
3087
3211
  */
3088
3212
  function textColorStyle(token) {
3213
+ const parsed = parseToken(token);
3214
+ const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
3215
+ if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
3089
3216
  return { color: rgbColorValue(token) };
3090
3217
  }
3091
3218
  /**
@@ -3355,6 +3482,10 @@ const buttonBorderSchema = z$1.object({
3355
3482
  widthClass: z$1.string(),
3356
3483
  colorToken: themeColorTokenSchema
3357
3484
  });
3485
+ const buttonVariantAffordanceSchema = z$1.object({
3486
+ iconId: z$1.string().min(1),
3487
+ colorToken: themeColorTokenSchema.optional()
3488
+ });
3358
3489
  const variantShadowSchema = z$1.object({
3359
3490
  elevation: z$1.enum([
3360
3491
  "none",
@@ -3408,7 +3539,8 @@ const buttonVariantSchema = z$1.object({
3408
3539
  border: buttonBorderSchema.optional(),
3409
3540
  hoverBackgroundToken: z$1.string().optional(),
3410
3541
  effects: variantEffectsSchema.optional(),
3411
- sizes: variantSizeOverridesSchema.optional()
3542
+ sizes: variantSizeOverridesSchema.optional(),
3543
+ affordance: buttonVariantAffordanceSchema.optional()
3412
3544
  });
3413
3545
  const buttonSystemSchema = z$1.object({
3414
3546
  global: buttonGlobalSettingsSchema,
@@ -3953,6 +4085,136 @@ const editorialLink = defineButtonPersonality({
3953
4085
  }
3954
4086
  });
3955
4087
  //#endregion
4088
+ //#region ../theme-core/src/buttons/personalities/flat-ink.ts
4089
+ /**
4090
+ * Flat Ink — a solid ink primary, a hairline ghost secondary, and a single
4091
+ * amber accent climax, all completely flat (no drop shadow) with a modest
4092
+ * radius. Reads as grounded, editorial and clinician-precise: the Fulcrum
4093
+ * physiotherapy voice. The primary shifts to the secondary (moss) on hover and
4094
+ * every variant lifts 2px; the amber accent is meant to be used sparingly as
4095
+ * the page's one conversion climax.
4096
+ */
4097
+ const flatInk = defineButtonPersonality({
4098
+ id: "flat-ink",
4099
+ name: "Flat Ink",
4100
+ description: "Flat solid-ink primary, hairline ghost secondary, and a single amber accent climax — modest radius, no shadow.",
4101
+ tags: {
4102
+ tone: "classic",
4103
+ energy: "calm",
4104
+ motion: "subtle",
4105
+ era: "editorial"
4106
+ },
4107
+ buttonSystem: {
4108
+ global: {
4109
+ cornerStyle: "rounded",
4110
+ shadow: "low",
4111
+ textTransform: "none",
4112
+ fontWeight: 600,
4113
+ borderWidth: 1,
4114
+ typography: "body",
4115
+ letterSpacing: "tight",
4116
+ paddingPreset: "default",
4117
+ hoverTransform: "lift",
4118
+ hoverColor: "none"
4119
+ },
4120
+ sizes: {
4121
+ sm: {
4122
+ padding: "0.625rem 1.125rem",
4123
+ fontSize: "text-sm"
4124
+ },
4125
+ md: { padding: "0.8125rem 1.375rem" },
4126
+ lg: {
4127
+ padding: "0.9375rem 1.625rem",
4128
+ fontSize: "text-lg"
4129
+ },
4130
+ xl: {
4131
+ padding: "1.0625rem 2rem",
4132
+ fontSize: "text-xl"
4133
+ }
4134
+ },
4135
+ variants: [
4136
+ {
4137
+ id: "primary",
4138
+ name: "Primary",
4139
+ enabled: true,
4140
+ priority: 1,
4141
+ background: {
4142
+ type: "solid",
4143
+ colorToken: "primary"
4144
+ },
4145
+ textColorToken: "primaryForeground",
4146
+ borderRadius: "rounded-lg",
4147
+ affordance: {
4148
+ iconId: "ArrowRight",
4149
+ colorToken: "accent"
4150
+ },
4151
+ shadow: {
4152
+ elevation: "none",
4153
+ softness: null,
4154
+ position: "bottom"
4155
+ },
4156
+ effects: { hover: [{
4157
+ effectId: "lift",
4158
+ options: { translateY: "-2px" }
4159
+ }, {
4160
+ effectId: "background-on-hover",
4161
+ options: { colorToken: "secondary" }
4162
+ }] }
4163
+ },
4164
+ {
4165
+ id: "secondary",
4166
+ name: "Secondary",
4167
+ enabled: true,
4168
+ priority: 2,
4169
+ background: { type: "transparent" },
4170
+ textColorToken: "primary",
4171
+ borderRadius: "rounded-lg",
4172
+ border: {
4173
+ widthClass: "border",
4174
+ colorToken: "border"
4175
+ },
4176
+ shadow: {
4177
+ elevation: "none",
4178
+ softness: null,
4179
+ position: "bottom"
4180
+ },
4181
+ effects: { hover: [{
4182
+ effectId: "lift",
4183
+ options: { translateY: "-2px" }
4184
+ }, {
4185
+ effectId: "background-on-hover",
4186
+ options: { colorToken: "surfaceAlt" }
4187
+ }] }
4188
+ },
4189
+ {
4190
+ id: "accent",
4191
+ name: "Accent",
4192
+ enabled: true,
4193
+ priority: 3,
4194
+ background: {
4195
+ type: "solid",
4196
+ colorToken: "accent"
4197
+ },
4198
+ textColorToken: "accentForeground",
4199
+ borderRadius: "rounded-lg",
4200
+ shadow: {
4201
+ elevation: "none",
4202
+ softness: null,
4203
+ position: "bottom"
4204
+ },
4205
+ affordance: {
4206
+ iconId: "ArrowRight",
4207
+ colorToken: "accentForeground"
4208
+ },
4209
+ effects: { hover: [{
4210
+ effectId: "lift",
4211
+ options: { translateY: "-2px" }
4212
+ }] }
4213
+ }
4214
+ ]
4215
+ }
4216
+ });
4217
+ //#endregion
3956
4218
  //#region ../theme-core/src/buttons/personalities/ink-stamp.ts
3957
4219
  /**
3958
4220
  * Ink Stamp — solid earthy fills with a bordered, hand-stamped block-print
@@ -4723,6 +4985,7 @@ const buttonPersonalities = [
4723
4985
  editorialLink,
4724
4986
  pebble,
4725
4987
  inkStamp,
4988
+ flatInk,
4726
4989
  brushedWash,
4727
4990
  showtimePill,
4728
4991
  joyStamp,
@@ -5080,11 +5343,31 @@ const gradientStopSchema = z$1.object({
5080
5343
  position: z$1.number().min(0).max(100)
5081
5344
  });
5082
5345
  /**
5083
- * Gradient configuration
5346
+ * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
5347
+ * so existing radial gradients are byte-identical.
5348
+ *
5349
+ * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
5350
+ * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
5351
+ */
5352
+ const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
5353
+ /**
5354
+ * Gradient configuration.
5355
+ *
5356
+ * `type` is optional and defaults to `'linear'` so gradients persisted without a
5357
+ * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
5358
+ * `'conic'` are additive kinds:
5359
+ * - linear: `angle` (degrees)
5360
+ * - radial: optional `shape` (circle/ellipse) + `position`
5361
+ * - conic: optional `angle` (from-angle, degrees) + `position`
5084
5362
  */
5085
5363
  const gradientConfigSchema = z$1.object({
5086
- type: z$1.enum(["linear", "radial"]),
5364
+ type: z$1.enum([
5365
+ "linear",
5366
+ "radial",
5367
+ "conic"
5368
+ ]).default("linear"),
5087
5369
  angle: z$1.number().min(0).max(360).optional(),
5370
+ shape: radialGradientShapeSchema.optional(),
5088
5371
  position: z$1.string().optional(),
5089
5372
  stops: z$1.array(gradientStopSchema).min(2)
5090
5373
  });
@@ -6066,6 +6349,7 @@ const typographyScaleSchema = z$1.enum([
6066
6349
  "majorThird",
6067
6350
  "perfectFourth"
6068
6351
  ]);
6352
+ const eyebrowTypographyStyleSchema = z$1.object({ family: z$1.string().min(1) });
6069
6353
  const typographySchema = z$1.object({
6070
6354
  body: bodyTypographyStyleSchema,
6071
6355
  headings: z$1.object({
@@ -6078,7 +6362,8 @@ const typographySchema = z$1.object({
6078
6362
  h6: headingTypographyOverridesSchema.optional()
6079
6363
  }),
6080
6364
  scale: typographyScaleSchema,
6081
- proseLinkStyle: proseLinkStyleSchema.optional()
6365
+ proseLinkStyle: proseLinkStyleSchema.optional(),
6366
+ eyebrow: eyebrowTypographyStyleSchema.optional()
6082
6367
  });
6083
6368
  const spaceSchema = z$1.enum([
6084
6369
  "comfortable",
@@ -6103,7 +6388,8 @@ const shadow = z$1.object({
6103
6388
  "soft",
6104
6389
  "hard"
6105
6390
  ]).nullable(),
6106
- position: z$1.enum(["bottom", "bottom-right"]).default("bottom")
6391
+ position: z$1.enum(["bottom", "bottom-right"]).default("bottom"),
6392
+ colorToken: themeColorTokenSchema.optional()
6107
6393
  });
6108
6394
  const border = z$1.object({
6109
6395
  width: z$1.enum([
@@ -6348,6 +6634,7 @@ const headerSchema = z$1.object({
6348
6634
  navWeight: navFontWeight.default("medium"),
6349
6635
  navTypography: z$1.enum(["body", "heading"]).optional(),
6350
6636
  navTextSize: navLinkStyleSchema.shape.textSize,
6637
+ navPlacement: z$1.enum(["end", "center"]).optional(),
6351
6638
  container: headerContainerSchema,
6352
6639
  border: headerBorderSchema,
6353
6640
  shadow: componentShadowSchema.optional(),
@@ -8451,6 +8738,13 @@ const legacyHeaderLookAliases = defineExactCatalog()([
8451
8738
  styleId: asHeaderStyleId("minimal")
8452
8739
  }
8453
8740
  },
8741
+ {
8742
+ lookId: asHeaderLookId("classic-centered-nav"),
8743
+ selection: {
8744
+ layoutId: asHeaderLayoutId("classic"),
8745
+ styleId: asHeaderStyleId("fine-line")
8746
+ }
8747
+ },
8454
8748
  {
8455
8749
  lookId: asHeaderLookId("brand-solid"),
8456
8750
  selection: {
@@ -8521,6 +8815,50 @@ function getCompatibleHeaderStyleIds(layoutId) {
8521
8815
  return isHeaderStyleCompatibleWithLayout(getHeaderStyleOrThrow(styleId), layoutId);
8522
8816
  });
8523
8817
  }
8818
+ function applyNavTypography(style, typography) {
8819
+ switch (typography) {
8820
+ case "plain": return style;
8821
+ case "heading": return {
8822
+ ...style,
8823
+ typography: "heading",
8824
+ fontWeight: "medium",
8825
+ letterSpacing: "wide"
8826
+ };
8827
+ case "quiet-caps": return {
8828
+ ...style,
8829
+ textTransform: "uppercase",
8830
+ textSize: "sm",
8831
+ letterSpacing: "wide"
8832
+ };
8833
+ case "brand-caps": return {
8834
+ ...style,
8835
+ textTransform: "uppercase",
8836
+ textSize: "sm",
8837
+ letterSpacing: "wider"
8838
+ };
8839
+ default: return assertNever$1(typography, "Unhandled header nav typography");
8840
+ }
8841
+ }
8842
+ function underlineNavLinkStyle(colorToken, underlineColorToken, typography = "plain") {
8843
+ return applyNavTypography({
8844
+ name: "Underline grow",
8845
+ typography: "body",
8846
+ fontWeight: "medium",
8847
+ textTransform: "none",
8848
+ italic: false,
8849
+ colorToken,
8850
+ padding: "compact",
8851
+ paddingX: "compact",
8852
+ borderRadius: "none",
8853
+ effects: { hover: [{
8854
+ effectId: "nav-underline",
8855
+ options: {
8856
+ style: "grow",
8857
+ ...underlineColorToken ? { colorToken: underlineColorToken } : {}
8858
+ }
8859
+ }] }
8860
+ }, typography);
8861
+ }
8524
8862
  function themedHeaderShadow(theme) {
8525
8863
  switch (theme.shadow.elevation) {
8526
8864
  case "none": return;
@@ -8620,6 +8958,38 @@ function cleanBaseHeader(theme) {
8620
8958
  ctaGap: "default"
8621
8959
  };
8622
8960
  }
8961
+ function classicCenteredNavHeader(theme) {
8962
+ return {
8963
+ ...baseHeader(theme),
8964
+ variant: "classic",
8965
+ navPlacement: "center",
8966
+ positioning: "sticky",
8967
+ shrinkOnScroll: false,
8968
+ background: {
8969
+ type: "color",
8970
+ color: "surface"
8971
+ },
8972
+ textColor: "text",
8973
+ navColor: "text",
8974
+ navStyle: "underline-grow",
8975
+ navWeight: "medium",
8976
+ navLinkStyle: underlineNavLinkStyle("text", "accent"),
8977
+ border: {
8978
+ style: "solid",
8979
+ width: "thin",
8980
+ position: "bottom",
8981
+ colorToken: "border"
8982
+ },
8983
+ ctaTreatment: "primary",
8984
+ ctaVariant: resolveHeaderCtaVariant(theme, "primary"),
8985
+ ctaGap: "default",
8986
+ logoText: {
8987
+ mobileMaxWidth: "sm",
8988
+ mobileWrapLines: 1,
8989
+ hideOnShrink: false
8990
+ }
8991
+ };
8992
+ }
8623
8993
  function serifLineHeader(theme) {
8624
8994
  return {
8625
8995
  ...baseHeader(theme),
@@ -8784,6 +9154,21 @@ const headerLookCatalog = defineExactCatalog()([
8784
9154
  logoAccommodations: inlineClassicLogoAccommodations,
8785
9155
  compile: cleanBaseHeader
8786
9156
  },
9157
+ {
9158
+ id: asHeaderLookId("classic-centered-nav"),
9159
+ label: "Centered nav",
9160
+ visualTreatment: "solid-neutral",
9161
+ layoutBehavior: "sticky",
9162
+ configuration: {
9163
+ structure: "inline-classic",
9164
+ treatment: "solid-neutral",
9165
+ behavior: "sticky"
9166
+ },
9167
+ openingRequirement: "none",
9168
+ maxRecommendedNavItems: 6,
9169
+ logoAccommodations: inlineClassicLogoAccommodations,
9170
+ compile: classicCenteredNavHeader
9171
+ },
8787
9172
  {
8788
9173
  id: asHeaderLookId("brand-solid"),
8789
9174
  label: "Brand solid",
@@ -9950,6 +10335,74 @@ const typographyPresets = [
9950
10335
  }
9951
10336
  }
9952
10337
  }
10338
+ }),
10339
+ defineTypographyPreset({
10340
+ id: "typography-preset:fulcrum",
10341
+ name: "Fulcrum",
10342
+ description: "Newsreader serif headings over an IBM Plex Sans body, with an IBM Plex Mono eyebrow role for uppercase kickers and mono captions.",
10343
+ defaultFonts: {
10344
+ body: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
10345
+ heading: "Newsreader, Georgia, \"Times New Roman\", serif"
10346
+ },
10347
+ recommendedFonts: {
10348
+ body: [
10349
+ "IBM Plex Sans",
10350
+ "Source Sans 3",
10351
+ "Inter"
10352
+ ],
10353
+ heading: [
10354
+ "Newsreader",
10355
+ "Source Serif 4",
10356
+ "Lora"
10357
+ ]
10358
+ },
10359
+ slotPolicies: {
10360
+ body: bodyReadablePolicy,
10361
+ heading: displayHeadingPolicy
10362
+ },
10363
+ typography: {
10364
+ body: {
10365
+ family: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
10366
+ size: "lg",
10367
+ weight: "regular",
10368
+ lineHeight: "relaxed",
10369
+ letterSpacing: "normal"
10370
+ },
10371
+ headings: {
10372
+ default: {
10373
+ family: "Newsreader, Georgia, \"Times New Roman\", serif",
10374
+ weight: "medium",
10375
+ lineHeight: "normal",
10376
+ letterSpacing: "tight",
10377
+ colorToken: "primary",
10378
+ italic: null,
10379
+ case: null
10380
+ },
10381
+ h1: {
10382
+ size: "2xl",
10383
+ weight: "medium",
10384
+ lineHeight: "normal",
10385
+ letterSpacing: "tight",
10386
+ italic: null
10387
+ },
10388
+ h2: {
10389
+ size: "xl",
10390
+ weight: "medium",
10391
+ lineHeight: "normal",
10392
+ letterSpacing: "tight",
10393
+ italic: null
10394
+ },
10395
+ h3: {
10396
+ size: "md",
10397
+ weight: "semibold",
10398
+ lineHeight: "normal",
10399
+ letterSpacing: "normal",
10400
+ italic: null
10401
+ }
10402
+ },
10403
+ scale: "spacious",
10404
+ eyebrow: { family: "IBM Plex Mono, ui-monospace, \"SF Mono\", monospace" }
10405
+ }
9953
10406
  })
9954
10407
  ];
9955
10408
  new Map(typographyPresets.map((preset) => [preset.id, preset]));
@@ -10170,7 +10623,8 @@ const curatedSiteStyleIdValues = [
10170
10623
  "site-style:bold-launch",
10171
10624
  "site-style:south-west-twerk-school",
10172
10625
  "site-style:kirtan-ornate",
10173
- "site-style:original-joy"
10626
+ "site-style:original-joy",
10627
+ "site-style:fulcrum"
10174
10628
  ];
10175
10629
  const quietBudget = {
10176
10630
  accentDensity: "low",
@@ -11624,6 +12078,104 @@ const curatedSiteStyles = [
11624
12078
  buttonChoice("showtime-pill")
11625
12079
  ],
11626
12080
  paletteRecommendations: [paletteRecommendation("warm-dark-ember")]
12081
+ }),
12082
+ defineCuratedSiteStyle({
12083
+ id: "site-style:fulcrum",
12084
+ name: "Fulcrum",
12085
+ headingDecoration: {
12086
+ family: "short-center-rule",
12087
+ colorToken: "accent"
12088
+ },
12089
+ 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.",
12090
+ 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.",
12091
+ siteCreatorSelection: {
12092
+ kind: "site_creator_keyword_heuristic",
12093
+ priority: 28
12094
+ },
12095
+ selectionKeywordRules: [
12096
+ {
12097
+ kind: "substring",
12098
+ value: "physiotherap"
12099
+ },
12100
+ {
12101
+ kind: "word",
12102
+ value: "physio"
12103
+ },
12104
+ {
12105
+ kind: "phrase",
12106
+ value: "sports injury"
12107
+ },
12108
+ {
12109
+ kind: "phrase",
12110
+ value: "rehab clinic"
12111
+ },
12112
+ {
12113
+ kind: "substring",
12114
+ value: "musculoskeletal"
12115
+ },
12116
+ {
12117
+ kind: "phrase",
12118
+ value: "sports therapy"
12119
+ }
12120
+ ],
12121
+ tokenRecipes: {
12122
+ palette: "brand-led",
12123
+ contrast: "standard",
12124
+ radius: "rounded",
12125
+ shadow: "subtle",
12126
+ typography: "serif-editorial",
12127
+ spacing: "airy",
12128
+ motion: "subtle"
12129
+ },
12130
+ compositionBudget: quietBudget,
12131
+ typographyPresetChoices: [
12132
+ typographyChoice("typography-preset:fulcrum"),
12133
+ typographyChoice("typography-preset:editorial-serif"),
12134
+ typographyChoice("typography-preset:modern-sans")
12135
+ ],
12136
+ defaultTypographyPresetId: "typography-preset:fulcrum",
12137
+ themeOverrides: {
12138
+ shadow: { colorToken: "primary" },
12139
+ accordions: {
12140
+ background: null,
12141
+ spacing: "comfortable",
12142
+ corners: "square",
12143
+ border: null,
12144
+ shadow: null,
12145
+ icon: {
12146
+ style: "plus-minus",
12147
+ position: "right",
12148
+ size: "md",
12149
+ colorToken: "accent"
12150
+ },
12151
+ trigger: {
12152
+ textColorToken: "primary",
12153
+ fontWeight: "medium",
12154
+ fontFamily: "heading",
12155
+ fontScale: "md"
12156
+ },
12157
+ hover: { textColorToken: "secondary" },
12158
+ content: { textColorToken: "muted" }
12159
+ },
12160
+ testimonialQuote: serifDisplayQuote
12161
+ },
12162
+ recommendedHeaderLookIds: [
12163
+ "classic-centered-nav",
12164
+ "serif-line",
12165
+ "centered-calm",
12166
+ "clean-base"
12167
+ ],
12168
+ recommendedFooterLookIds: ["footer-editorial-columns", "footer-muted-grounding"],
12169
+ buttonPersonalityChoices: [
12170
+ buttonChoice("flat-ink"),
12171
+ buttonChoice("confident-chip"),
12172
+ buttonChoice("editorial-link")
12173
+ ],
12174
+ paletteRecommendations: [
12175
+ paletteRecommendation("brand-led-spruce"),
12176
+ paletteRecommendation("warm-neutral-forest"),
12177
+ paletteRecommendation("soft-natural-sage")
12178
+ ]
11627
12179
  })
11628
12180
  ];
11629
12181
  new Map(curatedSiteStyles.map((style) => [style.id, style]));
@@ -12065,7 +12617,15 @@ function gradientToCss(config, tokens) {
12065
12617
  const stops = config.stops.map((stop) => {
12066
12618
  return `${tokens[stop.color] ?? "#000000"} ${stop.position}%`;
12067
12619
  }).join(", ");
12068
- if (config.type === "radial") return `radial-gradient(circle at ${config.position ?? "center"}, ${stops})`;
12620
+ const type = config.type ?? "linear";
12621
+ if (type === "radial") {
12622
+ const position = config.position ?? "center";
12623
+ return `radial-gradient(${config.shape ?? "circle"} at ${position}, ${stops})`;
12624
+ }
12625
+ if (type === "conic") {
12626
+ const position = config.position ?? "center";
12627
+ return `conic-gradient(from ${config.angle ?? 0}deg at ${position}, ${stops})`;
12628
+ }
12069
12629
  return `linear-gradient(${config.angle ?? 180}deg, ${stops})`;
12070
12630
  }
12071
12631
  const BREAKPOINT_LG = 1024;
@@ -12423,6 +12983,9 @@ const ALLOWED_NODE_TYPES = [
12423
12983
  "richText",
12424
12984
  "media",
12425
12985
  "videoEmbed",
12986
+ "icon",
12987
+ "feature-card-icon",
12988
+ "svg",
12426
12989
  "button",
12427
12990
  "link",
12428
12991
  "carousel",
@@ -12459,11 +13022,13 @@ const ALLOWED_TRANSFORM_IDS = [
12459
13022
  "value.jsonArray",
12460
13023
  "value.csv",
12461
13024
  "array.marqueeLoop",
13025
+ "value.mapToken",
12462
13026
  "layout.maxWidthClass",
12463
13027
  "layout.headerRootClass",
12464
13028
  "layout.footerRootClass",
12465
13029
  "layout.headerRootStyle",
12466
13030
  "layout.headerSectionBackground",
13031
+ "layout.classicNavMode",
12467
13032
  "layout.footerRootStyle",
12468
13033
  "ui.headerNavLinkClass",
12469
13034
  "ui.headingClassFromAlignment",
@@ -14965,7 +15530,7 @@ function resolveBackgroundColorValue(value, context) {
14965
15530
  return resolveThemeColorReferenceValue(value, { palette: context.theme?.palette });
14966
15531
  }
14967
15532
  //#endregion
14968
- //#region ../blocks/src/system/transforms/registry/_utils/sectionForegroundStyles.ts
15533
+ //#region ../blocks/src/system/transforms/registry/_utils/surfaceForegroundStyles.ts
14969
15534
  function readColorToken(input, key) {
14970
15535
  const value = input[key];
14971
15536
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
@@ -14974,18 +15539,17 @@ function resolveOptionalForegroundColor(value, context) {
14974
15539
  if (!value) return null;
14975
15540
  return resolveBackgroundColorValue(value, context);
14976
15541
  }
14977
- function resolveSectionForegroundStyles(value, context) {
15542
+ function resolveSurfaceForegroundStyles(value, context) {
14978
15543
  if (!value || typeof value !== "object") return {};
14979
15544
  const input = value;
14980
15545
  const resolvedText = resolveOptionalForegroundColor(readColorToken(input, "textColor"), context);
14981
15546
  if (!resolvedText) return {};
14982
- return {
14983
- color: resolvedText,
14984
- "--section-text-color": resolvedText,
14985
- "--section-heading-color": resolveOptionalForegroundColor(readColorToken(input, "headingColor"), context) ?? resolvedText,
14986
- "--section-link-color": resolveOptionalForegroundColor(readColorToken(input, "linkColor"), context) ?? resolvedText,
14987
- "--section-supporting-color": resolveOptionalForegroundColor(readColorToken(input, "supportingColor"), context) ?? resolvedText
14988
- };
15547
+ return { ...surfaceForegroundStyle({
15548
+ text: resolvedText,
15549
+ heading: resolveOptionalForegroundColor(readColorToken(input, "headingColor"), context) ?? void 0,
15550
+ link: resolveOptionalForegroundColor(readColorToken(input, "linkColor"), context) ?? void 0,
15551
+ supporting: resolveOptionalForegroundColor(readColorToken(input, "supportingColor"), context) ?? void 0
15552
+ }) };
14989
15553
  }
14990
15554
  //#endregion
14991
15555
  //#region ../blocks/src/system/transforms/registry/boxStyles.ts
@@ -15323,67 +15887,12 @@ const containerStylesResolveBoxClassName = {
15323
15887
  }
15324
15888
  };
15325
15889
  /**
15326
- * Resolves section text color style object.
15327
- *
15328
- * The value should be the `_sectionStyles.background.textColor` token.
15329
- * Returns an empty object when unset so the section inherits text color.
15330
- *
15331
- * Outputs both `color` and `--section-text-color` CSS variable so that
15332
- * headings (which have explicit color rules in typography CSS) can inherit
15333
- * the section's text color via the CSS variable cascade.
15334
- */
15335
- const sectionTextColorStyleTransform = {
15336
- id: "sectionStyles.resolveTextColorStyle",
15337
- kind: "formatter",
15338
- summary: "Resolve section text color token into a style object",
15339
- run: (value, options, context) => {
15340
- const fallbackToken = options?.fallbackToken;
15341
- if (typeof value === "string" && value.trim()) {
15342
- const colorValue = resolveBackgroundColorValue(value.trim(), context);
15343
- return {
15344
- color: colorValue,
15345
- "--section-text-color": colorValue
15346
- };
15347
- }
15348
- if (typeof fallbackToken === "string" && fallbackToken.trim()) {
15349
- const colorValue = resolveBackgroundColorValue(fallbackToken.trim(), context);
15350
- return {
15351
- color: colorValue,
15352
- "--section-text-color": colorValue
15353
- };
15354
- }
15355
- return {};
15356
- }
15357
- };
15358
- /**
15359
- * Resolves section heading color style object.
15360
- *
15361
- * The value should be the `_sectionStyles.background.headingColor` token.
15362
- * Returns an empty object when unset so headings inherit from textColor or default.
15363
- *
15364
- * Outputs `--section-heading-color` CSS variable which typography CSS can use
15365
- * to style headings differently from body text within a section.
15366
- */
15367
- const sectionHeadingColorStyleTransform = {
15368
- id: "sectionStyles.resolveHeadingColorStyle",
15369
- kind: "formatter",
15370
- summary: "Resolve section heading color token into a style object",
15371
- run: (value, options, context) => {
15372
- const fallbackToken = options?.fallbackToken;
15373
- if (typeof value === "string" && value.trim()) return { "--section-heading-color": resolveBackgroundColorValue(value.trim(), context) };
15374
- if (typeof fallbackToken === "string" && fallbackToken.trim()) return { "--section-heading-color": resolveBackgroundColorValue(fallbackToken.trim(), context) };
15375
- return {};
15376
- }
15377
- };
15378
- /**
15379
- * Resolves section foreground colors from the background object.
15890
+ * Resolves surface foreground colors from the background object.
15380
15891
  *
15381
15892
  * The value should be the full `_sectionStyles.background` object.
15382
15893
  * Outputs:
15383
- * - `color` and `--section-text-color` from background.textColor
15384
- * - `--section-heading-color` from background.headingColor (falls back to textColor if not set)
15385
- * - `--section-link-color` from background.linkColor (falls back to textColor if not set)
15386
- * - `--section-supporting-color` from background.supportingColor (falls back to textColor if not set)
15894
+ * - `color` and the complete `--rb-surface-*-color` contract
15895
+ * - optional roles fall back atomically to background.textColor
15387
15896
  *
15388
15897
  * This combined transform replaces separate foreground color transforms
15389
15898
  * when both need to be applied to the same style prop.
@@ -15391,8 +15900,8 @@ const sectionHeadingColorStyleTransform = {
15391
15900
  const sectionColorsStyleTransform = {
15392
15901
  id: "sectionStyles.resolveColorsStyle",
15393
15902
  kind: "formatter",
15394
- summary: "Resolve section foreground colors into a style object",
15395
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
15903
+ summary: "Resolve surface foreground colors into a style object",
15904
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
15396
15905
  };
15397
15906
  /**
15398
15907
  * Maps semantic gap values to theme-aware gap classes.
@@ -15431,8 +15940,6 @@ const boxStylesTransforms = [
15431
15940
  sectionClassNameTransform,
15432
15941
  containerStylesResolveClassName,
15433
15942
  containerStylesResolveBoxClassName,
15434
- sectionTextColorStyleTransform,
15435
- sectionHeadingColorStyleTransform,
15436
15943
  sectionColorsStyleTransform,
15437
15944
  {
15438
15945
  id: "boxStyles.semanticGap",
@@ -16336,7 +16843,29 @@ const ctaRowFragment = defineFragment({
16336
16843
  fields: [createCtasRepeater({
16337
16844
  label: "Calls to action",
16338
16845
  itemLabel: "CTA",
16339
- maxItems: 3
16846
+ maxItems: 3,
16847
+ button: { variants: [
16848
+ {
16849
+ value: "primary",
16850
+ label: "Primary"
16851
+ },
16852
+ {
16853
+ value: "secondary",
16854
+ label: "Secondary"
16855
+ },
16856
+ {
16857
+ value: "outline",
16858
+ label: "Outline"
16859
+ },
16860
+ {
16861
+ value: "link",
16862
+ label: "Link"
16863
+ },
16864
+ {
16865
+ value: "accent",
16866
+ label: "Accent"
16867
+ }
16868
+ ] }
16340
16869
  })],
16341
16870
  layout: [ctaRow({
16342
16871
  collectionPath: "content.ctas",
@@ -16617,6 +17146,55 @@ function buildGridCardsLayout(className) {
16617
17146
  return el("div", { className }, buildResponsiveGridCards());
16618
17147
  }
16619
17148
  /**
17149
+ * Build a single "featured editorial" testimonial card.
17150
+ *
17151
+ * This card type deliberately renders NO avatar/headshot: an amber quote mark,
17152
+ * the quote body, then the author name and a context line. Colour + surface
17153
+ * treatment is driven entirely by `variantClass` (see testimonials.css):
17154
+ * - `rb-testimonials-fe-card--feature` → spruce-ink card (dark primary bg)
17155
+ * - `rb-testimonials-fe-card--plain` → white surface card
17156
+ *
17157
+ * Extra modifiers (`repeat`, `when`) are forwarded to the card node so the
17158
+ * caller can drive first-vs-rest branching.
17159
+ */
17160
+ function buildFeaturedEditorialCard(variantClass, ...mods) {
17161
+ const quoteMark = text({
17162
+ as: "span",
17163
+ className: "rb-testimonials-fe-qmark",
17164
+ "aria-hidden": "true"
17165
+ }, props({ value: "“" }));
17166
+ const quote = richText({ className: "rb-testimonials-fe-quote rb-max-w-none" }, bind("testimonial.content.body"), when("testimonial.content.body"));
17167
+ const meta = el("div", { className: "rb-testimonials-fe-meta" }, [text({
17168
+ as: "strong",
17169
+ className: "rb-testimonials-fe-name"
17170
+ }, bind("testimonial.content.name", { fallback: "Anonymous" })), text({
17171
+ as: "span",
17172
+ className: "rb-testimonials-fe-context"
17173
+ }, when("testimonial.content.jobTitle"), bind("testimonial.content.jobTitle"))]);
17174
+ return el("div", { className: `rb-testimonials-fe-card ${variantClass}` }, [
17175
+ quoteMark,
17176
+ quote,
17177
+ meta
17178
+ ], ...mods);
17179
+ }
17180
+ /**
17181
+ * Build the "featured editorial" layout: an asymmetric two-column arrangement
17182
+ * where the FIRST testimonial is a large featured (spruce-ink) card and the
17183
+ * remaining testimonials stack as white cards beside it.
17184
+ *
17185
+ * First-vs-rest branching is done without an offset primitive: the featured
17186
+ * card uses `repeat(..., { limit: 1 })` (entry 0 only), and the rest column
17187
+ * repeats over every entry but skips index 0 via `when("tIndex", { equals: 0,
17188
+ * not: true })`. The `$when` guard is re-evaluated per repeated item with the
17189
+ * index scope in context, so entry 0 produces no node in the rest column.
17190
+ */
17191
+ function buildFeaturedEditorialLayout() {
17192
+ 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", {
17193
+ equals: 0,
17194
+ not: true
17195
+ }))])]);
17196
+ }
17197
+ /**
16620
17198
  * Fragment definition for testimonials carousel.
16621
17199
  * Provides field definitions and data loader configuration.
16622
17200
  *
@@ -19634,7 +20212,7 @@ const defaultLayout = styledSection({
19634
20212
  children: styledContainer({
19635
20213
  baseClassName: "rb-relative rb-isolate",
19636
20214
  children: stack({
19637
- gap: "xl",
20215
+ gap: "lg",
19638
20216
  className: "rb-w-full rb-max-w-4xl rb-mx-auto"
19639
20217
  }, [headingLayout, accordionLayout])
19640
20218
  }),
@@ -19651,7 +20229,7 @@ const questionColumnsLayout = styledSection({
19651
20229
  children: styledContainer({
19652
20230
  baseClassName: "rb-relative rb-isolate",
19653
20231
  children: stack({
19654
- gap: "xl",
20232
+ gap: "lg",
19655
20233
  className: "rb-w-full"
19656
20234
  }, [headingLayout, questionColumnsAccordionLayout])
19657
20235
  }),
@@ -19816,7 +20394,26 @@ const formattingTransforms = [
19816
20394
  arrayLengthTransform,
19817
20395
  jsonArrayTransform,
19818
20396
  csvTransform,
19819
- marqueeLoopTransform
20397
+ marqueeLoopTransform,
20398
+ {
20399
+ id: "value.mapToken",
20400
+ kind: "formatter",
20401
+ summary: "Map a state/enum value to a theme token colour (rgb(var(--tb-<role>)))",
20402
+ schema: z$1.object({
20403
+ /**
20404
+ * Maps an input state/enum value (stringified) to a theme token ROLE name
20405
+ * (e.g. `secondary`, `accent`, `muted`, `border`). The role is wrapped to
20406
+ * `rgb(var(--tb-<role>))` so the result drops straight into a `style` binding.
20407
+ */
20408
+ cases: z$1.record(z$1.string(), z$1.string()),
20409
+ /** Role used when the value matches no case. Omit to yield `undefined`. */
20410
+ fallback: z$1.string().optional()
20411
+ }),
20412
+ run: (value, options) => {
20413
+ const role = options.cases[String(value)] ?? options.fallback;
20414
+ return role ? `rgb(var(--tb-${role}))` : void 0;
20415
+ }
20416
+ }
19820
20417
  ];
19821
20418
  //#endregion
19822
20419
  //#region ../blocks/src/system/transforms/registry/ui.ts
@@ -20400,6 +20997,26 @@ const headerLogoTextVisibleTransform = {
20400
20997
  return resolveHeaderBrandDisplay(asPartialObject(value)) !== "logo-only";
20401
20998
  }
20402
20999
  };
21000
+ /**
21001
+ * Selects which classic-layout nav row should render.
21002
+ *
21003
+ * - `'inline'` → classic variant with the historical logo-left / nav-right row
21004
+ * - `'centered'`→ classic variant with a centred (flex-1) nav and a right CTA
21005
+ * - `'other'` → not a classic variant (centered/floating/transparent handle it)
21006
+ *
21007
+ * Consumed by the site-header block's `$when` gates so the two classic rows are
21008
+ * mutually exclusive without introducing a new top-level header variant.
21009
+ */
21010
+ const classicNavModeTransform = {
21011
+ id: "layout.classicNavMode",
21012
+ kind: "formatter",
21013
+ summary: "Resolve which classic-layout nav row to render",
21014
+ run: (value) => {
21015
+ const header = asPartialObject(value);
21016
+ if (header?.variant !== "classic") return "other";
21017
+ return header?.navPlacement === "center" ? "centered" : "inline";
21018
+ }
21019
+ };
20403
21020
  const maxWidthClassTransform = {
20404
21021
  id: "layout.maxWidthClass",
20405
21022
  kind: "string",
@@ -20582,8 +21199,9 @@ const footerRootStyleTransform = {
20582
21199
  styleParts.push(backgroundColorStyle(token));
20583
21200
  }
20584
21201
  }
20585
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
20586
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
21202
+ const foreground = visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
21203
+ if (!visuals.isTransparent && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
21204
+ else if (foreground) styleParts.push({ color: foreground });
20587
21205
  styleParts.push(borderColorStyle("border"));
20588
21206
  return mergeStyles(...styleParts);
20589
21207
  }
@@ -20669,18 +21287,16 @@ const footerBottomBarStyleTransform = {
20669
21287
  const styleParts = [];
20670
21288
  const background = bottomBar?.background;
20671
21289
  if (background?.type === "color") styleParts.push(backgroundColorStyle(background.color ?? "surface"));
20672
- if (bottomBar?.textColor) styleParts.push(textColorStyle(bottomBar.textColor));
20673
21290
  const borderColorToken = bottomBar?.borderTop?.colorToken;
20674
21291
  if (borderColorToken) styleParts.push(borderColorStyle(borderColorToken));
20675
21292
  else styleParts.push(borderColorStyle("border"));
20676
21293
  const borderWidth = bottomBar?.borderTop?.width;
20677
21294
  if (borderWidth && borderWidth !== "none") styleParts.push({ borderTopWidth: borderWidth === "thin" ? "1px" : borderWidth === "medium" ? "2px" : "3px" });
20678
21295
  else if (borderWidth === "none") styleParts.push({ borderTopWidth: "0px" });
20679
- if (!bottomBar?.textColor) {
20680
- const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
20681
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
20682
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
20683
- }
21296
+ const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
21297
+ const foreground = bottomBar?.textColor ? rgbColorValue(bottomBar.textColor) : visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
21298
+ if (background?.type === "color" && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
21299
+ else if (foreground && bottomBar?.textColor) styleParts.push({ color: foreground });
20684
21300
  return mergeStyles(...styleParts);
20685
21301
  }
20686
21302
  };
@@ -20815,6 +21431,7 @@ const layoutTransforms = [
20815
21431
  headerLogoClassTransform,
20816
21432
  headerLogoMediaClassTransform,
20817
21433
  headerLogoTextVisibleTransform,
21434
+ classicNavModeTransform,
20818
21435
  footerRootClassTransform,
20819
21436
  headerRootStyleTransform,
20820
21437
  headerSectionBackgroundTransform,
@@ -21211,7 +21828,18 @@ const classicLayout = inline({
21211
21828
  logoRow,
21212
21829
  createNavRow(`rb-ml-auto ${desktopOnlyDisplay("flex")} rb-gap-6`),
21213
21830
  headerCta
21214
- ], when("$root.theme.header.variant", { equals: "classic" }));
21831
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "inline"));
21832
+ const classicCenteredNavLayout = inline({
21833
+ className: bindProp("$root.theme.header.maxWidth", {
21834
+ transforms: pipe(tx("layout.maxWidthClass", { base: `rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6` })),
21835
+ fallback: `rb-container rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6`
21836
+ }),
21837
+ align: "center"
21838
+ }, [
21839
+ logoRow,
21840
+ createNavRow(`rb-flex-1 rb-justify-center ${desktopOnlyDisplay("flex")} rb-gap-6`, "center"),
21841
+ headerCta
21842
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "centered"));
21215
21843
  const centeredLayout = stack({
21216
21844
  gap: "md",
21217
21845
  align: "center",
@@ -21286,6 +21914,7 @@ const siteHeaderBlockDefinition = { manifest: createBlockManifest({
21286
21914
  }, [
21287
21915
  mobileHeaderBar,
21288
21916
  classicLayout,
21917
+ classicCenteredNavLayout,
21289
21918
  centeredLayout,
21290
21919
  transparentLayout,
21291
21920
  floatingLayout,
@@ -21494,7 +22123,7 @@ const footerLayout = section({
21494
22123
  style: { $bind: {
21495
22124
  from: "$root.theme.footer",
21496
22125
  transforms: [{ id: "layout.footerRootStyle" }],
21497
- fallback: mergeStyles(backgroundColorStyle("surface"), textColorStyle("text"), borderColorStyle("border"))
22126
+ fallback: mergeStyles(backgroundColorStyle("surface"), surfaceForegroundStyle({ text: "rgb(var(--tb-text))" }), borderColorStyle("border"))
21498
22127
  } }
21499
22128
  }, [
21500
22129
  simpleFooterLayout,
@@ -21548,7 +22177,7 @@ const backgroundImageClassName = "rb-absolute rb-inset-0 rb-z-neg-10 rb-h-full r
21548
22177
  */
21549
22178
  const wrapVariant$2 = (variantLayout) => styledSection({
21550
22179
  children: styledContainer({ children: stack({
21551
- gap: "xl",
22180
+ gap: "lg",
21552
22181
  className: "rb-relative"
21553
22182
  }, [...testimonialsHeadingFragment.layout, variantLayout]) }),
21554
22183
  baseClass: "",
@@ -21574,6 +22203,26 @@ const splitGridLayout = styledSection({
21574
22203
  spacing: "comfortable",
21575
22204
  imageClassName: backgroundImageClassName
21576
22205
  });
22206
+ const featuredEditorialLayout = styledSection({
22207
+ children: styledContainer({
22208
+ baseClassName: "rb-relative rb-isolate",
22209
+ children: stack({
22210
+ gap: "lg",
22211
+ className: "rb-relative"
22212
+ }, [el("div", { className: "rb-testimonials-fe-head" }, [text({
22213
+ as: "h2",
22214
+ className: "rb-text-left",
22215
+ headingDecoration: "off"
22216
+ }, when("content.heading"), bind("content.heading")), el("div", { className: "rb-testimonials-fe-leadin" }, [text({
22217
+ as: "p",
22218
+ className: "rb-prose",
22219
+ style: textColorStyle("mutedText")
22220
+ }, when("content.subheading"), bind("content.subheading"))])]), buildFeaturedEditorialLayout()])
22221
+ }),
22222
+ baseClass: "",
22223
+ spacing: "comfortable",
22224
+ imageClassName: backgroundImageClassName
22225
+ });
21577
22226
  const additionalFields = parseFieldDefinitions([
21578
22227
  ...testimonialsHeadingFragment.fields,
21579
22228
  {
@@ -21661,6 +22310,7 @@ const testimonialsBlockDefinition = {
21661
22310
  carousel: carouselLayout$1,
21662
22311
  grid: gridLayout$1,
21663
22312
  "split-grid": splitGridLayout,
22313
+ "featured-editorial": featuredEditorialLayout,
21664
22314
  highlight: highlightLayout
21665
22315
  },
21666
22316
  defaultVariant: "carousel",
@@ -21680,6 +22330,11 @@ const testimonialsBlockDefinition = {
21680
22330
  description: "Heading on the left with testimonial cards on the right",
21681
22331
  icon: "PanelLeft"
21682
22332
  },
22333
+ "featured-editorial": {
22334
+ label: "Featured editorial",
22335
+ description: "A large featured ink quote beside a column of stacked cards (no avatars)",
22336
+ icon: "Quote"
22337
+ },
21683
22338
  highlight: {
21684
22339
  label: "Highlight",
21685
22340
  description: "A single, centered testimonial with no carousel",
@@ -22152,7 +22807,7 @@ function grid$2(props, children) {
22152
22807
  const cardsLayout = styledSection({
22153
22808
  children: styledContainer({
22154
22809
  rootType: "stack",
22155
- rootProps: { gap: "xl" },
22810
+ rootProps: { gap: "lg" },
22156
22811
  children: [stack({
22157
22812
  gap: "sm",
22158
22813
  className: "rb-feature-cards-heading rb-mx-auto rb-max-w-3xl rb-text-center"
@@ -24039,8 +24694,11 @@ const recoverEmptyDescriptionWhenActive = {
24039
24694
  kind: "recover-empty-when-active",
24040
24695
  placeholder: "Add description"
24041
24696
  };
24042
- function eventSectionHeaderLayoutNodes() {
24043
- 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 }))];
24697
+ function eventSectionHeaderLayoutNodes(options = {}) {
24698
+ const headingNode = text({ as: "h2" }, bind("content.heading"), when("content.heading", { authoringPresence: recoverEmptyHeadingWhenActive }));
24699
+ const descriptionNode = richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description", { authoringPresence: recoverEmptyDescriptionWhenActive }));
24700
+ if (options.splitClassName) return [el("div", { className: options.splitClassName }, [headingNode, descriptionNode])];
24701
+ return [headingNode, descriptionNode];
24044
24702
  }
24045
24703
  //#endregion
24046
24704
  //#region ../blocks/src/system/blocks/shop.shared.ts
@@ -24107,7 +24765,8 @@ const eventCalendarDisplayModeValues = [
24107
24765
  "combined",
24108
24766
  "calendar",
24109
24767
  "list",
24110
- "timetable"
24768
+ "timetable",
24769
+ "week-board"
24111
24770
  ];
24112
24771
  const eventCalendarDefaultViewValues = ["month", "week"];
24113
24772
  const eventCalendarListLayoutValues = [
@@ -26549,7 +27208,7 @@ const eventCalendarLayout = [styledSection({
26549
27208
  children: styledContainer({
26550
27209
  rootType: "stack",
26551
27210
  rootProps: { gap: "lg" },
26552
- children: [...eventSectionHeaderLayoutNodes(), el("event-calendar", withApiBindings({
27211
+ children: [...eventSectionHeaderLayoutNodes({ splitClassName: "event-section-head event-section-head--split" }), el("event-calendar", withApiBindings({
26553
27212
  events: { $bind: { from: "data.events" } },
26554
27213
  stage: { $bind: {
26555
27214
  from: "$root.previewStage",
@@ -26660,6 +27319,10 @@ const eventCalendarAdditionalFields = [
26660
27319
  value: "timetable",
26661
27320
  label: "Timetable"
26662
27321
  },
27322
+ {
27323
+ value: "week-board",
27324
+ label: "Week board"
27325
+ },
26663
27326
  {
26664
27327
  value: "list",
26665
27328
  label: "List"
@@ -26842,7 +27505,8 @@ const eventCalendarAdditionalFields = [
26842
27505
  oneOf: [
26843
27506
  "combined",
26844
27507
  "calendar",
26845
- "timetable"
27508
+ "timetable",
27509
+ "week-board"
26846
27510
  ]
26847
27511
  }
26848
27512
  }
@@ -26945,7 +27609,8 @@ const eventCalendarAdditionalFields = [
26945
27609
  "combined",
26946
27610
  "calendar",
26947
27611
  "list",
26948
- "timetable"
27612
+ "timetable",
27613
+ "week-board"
26949
27614
  ]
26950
27615
  }
26951
27616
  }
@@ -26980,7 +27645,8 @@ const eventCalendarAdditionalFields = [
26980
27645
  "combined",
26981
27646
  "calendar",
26982
27647
  "list",
26983
- "timetable"
27648
+ "timetable",
27649
+ "week-board"
26984
27650
  ]
26985
27651
  }, {
26986
27652
  field: "showFilters",
@@ -27003,7 +27669,8 @@ const eventCalendarAdditionalFields = [
27003
27669
  "combined",
27004
27670
  "calendar",
27005
27671
  "list",
27006
- "timetable"
27672
+ "timetable",
27673
+ "week-board"
27007
27674
  ]
27008
27675
  }, {
27009
27676
  field: "showFilters",
@@ -27026,7 +27693,8 @@ const eventCalendarAdditionalFields = [
27026
27693
  "combined",
27027
27694
  "calendar",
27028
27695
  "list",
27029
- "timetable"
27696
+ "timetable",
27697
+ "week-board"
27030
27698
  ]
27031
27699
  }, {
27032
27700
  field: "showFilters",
@@ -27048,7 +27716,8 @@ const eventCalendarAdditionalFields = [
27048
27716
  oneOf: [
27049
27717
  "combined",
27050
27718
  "list",
27051
- "timetable"
27719
+ "timetable",
27720
+ "week-board"
27052
27721
  ]
27053
27722
  }, { allOf: [{
27054
27723
  field: "variant",
@@ -27224,6 +27893,7 @@ const eventCalendarVariants = {
27224
27893
  combined: eventCalendarLayout[0],
27225
27894
  calendar: eventCalendarLayout[0],
27226
27895
  timetable: eventCalendarLayout[0],
27896
+ "week-board": eventCalendarLayout[0],
27227
27897
  list: eventCalendarLayout[0]
27228
27898
  };
27229
27899
  const eventCalendarContentShape = {
@@ -27237,6 +27907,7 @@ const eventCalendarContentShape = {
27237
27907
  { value: "combined" },
27238
27908
  { value: "calendar" },
27239
27909
  { value: "timetable" },
27910
+ { value: "week-board" },
27240
27911
  { value: "list" }
27241
27912
  ]
27242
27913
  },
@@ -27303,6 +27974,7 @@ const eventCalendarBlockDefinition = {
27303
27974
  combined: { label: "Combined" },
27304
27975
  calendar: { label: "Calendar" },
27305
27976
  timetable: { label: "Timetable" },
27977
+ "week-board": { label: "Week board" },
27306
27978
  list: { label: "List" }
27307
27979
  }
27308
27980
  }, eventCalendarContentShape),
@@ -32480,6 +33152,12 @@ const systemBlockDefinitions = [
32480
33152
  family: "centered",
32481
33153
  weight: "featured",
32482
33154
  bias: "soft"
33155
+ }),
33156
+ layoutProfile({
33157
+ variantKey: "featured-editorial",
33158
+ family: "split",
33159
+ weight: "featured",
33160
+ bias: "soft"
32483
33161
  })
32484
33162
  ]
32485
33163
  })),
@@ -32612,6 +33290,11 @@ const systemBlockDefinitions = [
32612
33290
  variantKey: "timetable",
32613
33291
  family: "stack",
32614
33292
  weight: "standard"
33293
+ }),
33294
+ layoutProfile({
33295
+ variantKey: "week-board",
33296
+ family: "stack",
33297
+ weight: "standard"
32615
33298
  })
32616
33299
  ]
32617
33300
  })),
@@ -32920,8 +33603,8 @@ const backgroundTransforms = [
32920
33603
  {
32921
33604
  id: "background.resolveColorsStyle",
32922
33605
  kind: "formatter",
32923
- summary: "Resolve section foreground colors from a background object into scoped CSS vars",
32924
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
33606
+ summary: "Resolve surface foreground colors from a background object into scoped CSS vars",
33607
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
32925
33608
  },
32926
33609
  {
32927
33610
  id: "background.resolveAllowOverflow",