@riverbankcms/sdk 0.104.0 → 0.104.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/{PreviewEditorSidebar-ywqcYjTB.mjs → PreviewEditorSidebar-CBMhU9p_.mjs} +3 -3
  2. package/dist/{PreviewEditorUI-DApUNF6g.mjs → PreviewEditorUI-C5KEw4M8.mjs} +2 -2
  3. package/dist/{SdkPreviewModeRuntime-DmHCcIMO.mjs → SdkPreviewModeRuntime-Iq12HjL7.mjs} +1147 -180
  4. package/dist/{SiteChromeCustomizeContext-BV9GU-3C.mjs → SiteChromeCustomizeContext-Cx0NMdyv.mjs} +1818 -951
  5. package/dist/_dts/ai/src/contracts/proposals.d.ts +10 -0
  6. package/dist/_dts/api/src/bookingVerticals.d.ts +64 -1
  7. package/dist/_dts/blocks/src/primitives/layout/Grid.d.ts +12 -8
  8. package/dist/_dts/blocks/src/system/blocks/events/event-calendar.d.ts +3 -0
  9. package/dist/_dts/blocks/src/system/blocks/events/shared/layout.d.ts +11 -1
  10. package/dist/_dts/blocks/src/system/blocks/testimonials.d.ts +7 -1
  11. package/dist/_dts/blocks/src/system/constants/events.d.ts +1 -1
  12. package/dist/_dts/blocks/src/system/fragments/library/testimonialsCarousel.d.ts +12 -0
  13. package/dist/_dts/blocks/src/system/node/validation-constants.d.ts +2 -2
  14. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +14 -0
  15. package/dist/_dts/blocks/src/system/runtime/nodes/buttonAffordance.d.ts +33 -0
  16. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +28 -0
  17. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +32 -0
  18. package/dist/_dts/blocks/src/system/runtime/nodes/shared/contract-schemas.d.ts +1 -0
  19. package/dist/_dts/blocks/src/system/runtime/nodes/svg.d.ts +77 -0
  20. package/dist/_dts/blocks/src/system/transforms/registry/_utils/surfaceForegroundStyles.d.ts +2 -0
  21. package/dist/_dts/blocks/src/system/transforms/registry/boxStyles.d.ts +3 -40
  22. package/dist/_dts/blocks/src/system/transforms/registry/formatting.d.ts +14 -0
  23. package/dist/_dts/blocks/src/system/transforms/registry/layout.d.ts +11 -0
  24. package/dist/_dts/core/src/branded.d.ts +5 -0
  25. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +18 -0
  26. package/dist/_dts/editor/src/index.d.ts +3 -1
  27. package/dist/_dts/editor/src/primitives/rich-text/RichTextEditorSurface.d.ts +14 -6
  28. package/dist/_dts/editor/src/primitives/rich-text/RichTextToolbar.d.ts +23 -4
  29. package/dist/_dts/editor/src/primitives/rich-text/config.d.ts +6 -0
  30. package/dist/_dts/editor/src/primitives/rich-text/extensions/RichTextLink.d.ts +1 -0
  31. package/dist/_dts/editor/src/primitives/rich-text/paste.d.ts +10 -0
  32. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +2 -2
  33. package/dist/_dts/sdk/src/version.d.ts +1 -1
  34. package/dist/_dts/theme-core/src/ThemeScope.d.ts +1 -1
  35. package/dist/_dts/theme-core/src/buttons/affordanceIcons.d.ts +29 -0
  36. package/dist/_dts/theme-core/src/buttons/generateButtonCss.d.ts +6 -0
  37. package/dist/_dts/theme-core/src/buttons/index.d.ts +1 -0
  38. package/dist/_dts/theme-core/src/buttons/personalities/flat-ink.d.ts +20 -0
  39. package/dist/_dts/theme-core/src/buttons/personalities/index.d.ts +11 -0
  40. package/dist/_dts/theme-core/src/buttons/personalities/types.d.ts +4 -0
  41. package/dist/_dts/theme-core/src/buttons/types.d.ts +34 -0
  42. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  43. package/dist/_dts/theme-core/src/gradients/types.d.ts +27 -3
  44. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +51 -15
  45. package/dist/_dts/theme-core/src/palette/curated/brand-led.d.ts +13 -0
  46. package/dist/_dts/theme-core/src/palette/curated/index.d.ts +12 -0
  47. package/dist/_dts/theme-core/src/palette/historicalThemePalette.d.ts +44 -0
  48. package/dist/_dts/theme-core/src/palette/index.d.ts +1 -0
  49. package/dist/_dts/theme-core/src/palette/legacyRuntimeProjection.d.ts +23 -6
  50. package/dist/_dts/theme-core/src/palette/legacySelection.d.ts +13 -2
  51. package/dist/_dts/theme-core/src/palette/paletteCompatibilityTransition.d.ts +0 -9
  52. package/dist/_dts/theme-core/src/palette/persistenceReadSource.d.ts +17 -2
  53. package/dist/_dts/theme-core/src/palette/themePaletteCompatibility.d.ts +3 -4
  54. package/dist/_dts/theme-core/src/runtime/themeCssVarNames.d.ts +1 -0
  55. package/dist/_dts/theme-core/src/schema.d.ts +108 -26
  56. package/dist/_dts/theme-core/src/site-styles/accessibleSurfaces.d.ts +2 -1
  57. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +50 -1
  58. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +2 -2
  59. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +4 -2
  60. package/dist/_dts/theme-core/src/site-styles/headerLooks.d.ts +56 -1
  61. package/dist/_dts/theme-core/src/site-styles/typographyPresets.d.ts +16 -1
  62. package/dist/_dts/theme-core/src/status/index.d.ts +0 -1
  63. package/dist/_dts/theme-core/src/surfaces/foregroundContract.d.ts +19 -0
  64. package/dist/_dts/theme-core/src/surfaces/foregroundRoles.d.ts +9 -0
  65. package/dist/_dts/theme-core/src/surfaces/index.d.ts +2 -0
  66. package/dist/_dts/ui/src/button.d.ts +1 -1
  67. package/dist/cli/index.mjs +1567 -470
  68. package/dist/client/{DiscountCodeDisclosure-Dit2aItR.mjs → DiscountCodeDisclosure-BXM73_E5.mjs} +1 -1
  69. package/dist/client/{EventCombined.client-BnREyCjf.mjs → EventCombined.client-DT5elP7j.mjs} +4 -4
  70. package/dist/client/{EventListing.client-0uHld51x.mjs → EventListing.client-pTYGfV9C.mjs} +2 -2
  71. package/dist/client/{LeafletStaticMap-DJcvSfmj.mjs → LeafletStaticMap-dQrhlUe3.mjs} +1 -1
  72. package/dist/client/{MagicLinkForm-EAsIktfw.mjs → MagicLinkForm-u6Dul4j5.mjs} +2 -2
  73. package/dist/client/{StepIndicator-CRR17tYI.mjs → StepIndicator-nWqineJ2.mjs} +2 -2
  74. package/dist/client/{accordion-B47HIuuH.mjs → accordion-CSJcTUyH.mjs} +1 -1
  75. package/dist/client/{bookingForm-I1E-8F9P.mjs → bookingForm-C73wOuVa.mjs} +6 -6
  76. package/dist/client/bookings.mjs +137 -1
  77. package/dist/client/{buttons-BbPn8OKA.mjs → buttons-DfYyqt8C.mjs} +138 -2
  78. package/dist/client/{carousel-OlM7SyBs.mjs → carousel-fMBb0JSc.mjs} +1 -1
  79. package/dist/client/{cart-BKpG5jz6.mjs → cart-BtaCwT3S.mjs} +3 -3
  80. package/dist/client/{checkout-Dmmr6qoU.mjs → checkout-DkTwZSnh.mjs} +3 -3
  81. package/dist/client/client.mjs +2336 -544
  82. package/dist/client/{colorStyles-DuW4Fjqd.mjs → colorStyles-Wyl6JujD.mjs} +45 -4
  83. package/dist/client/{courseRegistration-CflZiqGk.mjs → courseRegistration-DnLmapJu.mjs} +7 -7
  84. package/dist/client/{eventCalendar-BvoLWcp-.mjs → eventCalendar-BvLOc9KY.mjs} +165 -5
  85. package/dist/client/{eventCombined-DsrO8t_t.mjs → eventCombined-kShgJPQr.mjs} +2 -2
  86. package/dist/client/{eventDetails-C3hxgcJm.mjs → eventDetails-C5NTM0Vo.mjs} +3 -3
  87. package/dist/client/{eventListing-C-hb237X.mjs → eventListing-B82ZdKP6.mjs} +2 -2
  88. package/dist/client/{eventRegistration-B0gyzDGi.mjs → eventRegistration-qJVFqBWo.mjs} +7 -7
  89. package/dist/client/{form-C_RUi6oJ.mjs → form-BvAcKiuu.mjs} +2 -2
  90. package/dist/client/{headerSection-IKQiD2U0.mjs → headerSection-NvAiEHAg.mjs} +3 -3
  91. package/dist/client/hooks.mjs +1023 -337
  92. package/dist/client/{locationMap-8ZBrMzRJ.mjs → locationMap-D0j52xfI.mjs} +3 -3
  93. package/dist/client/{media-CZJKL7fi.mjs → media-BCW_g2cQ.mjs} +1 -1
  94. package/dist/client/{newsletterForm-CYk3ucwj.mjs → newsletterForm-B8_i93qH.mjs} +2 -2
  95. package/dist/client/{pricing-B5GmvfIH.mjs → pricing-DIYnyHOk.mjs} +1 -1
  96. package/dist/client/{productDetail-BjS2Dqzo.mjs → productDetail-w0eAHkuQ.mjs} +3 -3
  97. package/dist/client/{productList-BOHvC8My.mjs → productList-Vsguq5Xt.mjs} +3 -3
  98. package/dist/client/{propsDecoder-WS6UEJwZ.mjs → propsDecoder-CD2pJ1WB.mjs} +37 -2
  99. package/dist/client/rendering/client.mjs +1723 -762
  100. package/dist/client/rendering/islands.mjs +19 -19
  101. package/dist/client/rendering.mjs +2312 -520
  102. package/dist/client/{shop-CON03-wN.mjs → shop-Dgxs-Q8F.mjs} +6 -6
  103. package/dist/client/{shop-commerce-DlqrFgH8.mjs → shop-commerce-DJAhsTaT.mjs} +6 -6
  104. package/dist/client/{shop-commerce-store-l74-Kne6.mjs → shop-commerce-store-CfCXltXW.mjs} +1 -1
  105. package/dist/client/{shopShared-CQheEXSg.mjs → shopShared-CmDcPXUt.mjs} +2 -2
  106. package/dist/client/{useDiscountCodeUi-v6OluyWl.mjs → useDiscountCodeUi-CirrT2bM.mjs} +1 -1
  107. package/dist/client/{useEventFilters-BqSTcNOf.mjs → useEventFilters-CNZxlCcO.mjs} +5 -5
  108. package/dist/client/{usePolling-DMfADjIF.mjs → usePolling-Baks_iBI.mjs} +2 -2
  109. package/dist/preview-next/client/runtime.mjs +3 -3
  110. package/dist/{sdk-runtime-DWb08Xaz.mjs → sdk-runtime-BfNiFeNJ.mjs} +1 -1
  111. package/dist/{separator--6yjc914.mjs → separator-BhAR_Xmf.mjs} +1 -1
  112. package/dist/server/components.mjs +2301 -531
  113. package/dist/server/config-validation.mjs +1028 -337
  114. package/dist/server/config.mjs +1028 -337
  115. package/dist/server/data.mjs +1023 -337
  116. package/dist/server/index.mjs +1 -1
  117. package/dist/server/next.mjs +2304 -534
  118. package/dist/server/page-converter.mjs +1018 -324
  119. package/dist/server/prebuild.mjs +1 -1
  120. package/dist/server/rendering/server.mjs +2286 -521
  121. package/dist/server/rendering.mjs +2301 -531
  122. package/dist/server/routing.mjs +1100 -338
  123. package/dist/server/server.mjs +1024 -338
  124. package/dist/server/theme-bridge.mjs +1240 -77
  125. package/dist/server/theme.mjs +165 -5
  126. package/dist/styles/index.css +617 -34
  127. package/package.json +1 -1
  128. package/dist/_dts/blocks/src/system/transforms/registry/_utils/sectionForegroundStyles.d.ts +0 -2
  129. package/dist/_dts/theme-core/src/status/contextualColors.d.ts +0 -6
@@ -461,6 +461,234 @@ function featuresFromHex(hex) {
461
461
  };
462
462
  }
463
463
  //#endregion
464
+ //#region ../theme-core/src/tokens/colorTokenParts.ts
465
+ function parseOpacityPercent(source) {
466
+ const trimmed = source.trim();
467
+ if (!trimmed) return null;
468
+ if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
469
+ const value = Number(trimmed);
470
+ if (!Number.isFinite(value)) return null;
471
+ if (value < 0 || value > 100) return null;
472
+ return value;
473
+ }
474
+ function parseColorTokenParts(source) {
475
+ const trimmed = source.trim();
476
+ if (trimmed.startsWith("raw:")) return {
477
+ kind: "raw",
478
+ value: trimmed.slice(4)
479
+ };
480
+ const [token, opacityRaw, extra] = trimmed.split("/");
481
+ if (!token || extra !== void 0) return {
482
+ kind: "invalid",
483
+ token: trimmed
484
+ };
485
+ if (opacityRaw === void 0) return {
486
+ kind: "token",
487
+ token,
488
+ opacityPercent: null
489
+ };
490
+ const opacityPercent = parseOpacityPercent(opacityRaw);
491
+ if (opacityPercent === null) return {
492
+ kind: "invalid",
493
+ token: trimmed
494
+ };
495
+ return {
496
+ kind: "token",
497
+ token,
498
+ opacityPercent
499
+ };
500
+ }
501
+ //#endregion
502
+ //#region ../theme-core/src/tokens/blockColorTokens.ts
503
+ const blockColorTokenNames = [
504
+ "text",
505
+ "heading",
506
+ "mutedText",
507
+ "surface",
508
+ "background",
509
+ "foreground",
510
+ "border",
511
+ "primary",
512
+ "secondary",
513
+ "accent",
514
+ "success",
515
+ "warning",
516
+ "info",
517
+ "error",
518
+ "destructive",
519
+ "scrim",
520
+ "shadow",
521
+ "onPrimary",
522
+ "onSecondary",
523
+ "onAccent",
524
+ "onSuccess",
525
+ "onWarning",
526
+ "onInfo",
527
+ "onError",
528
+ "onDestructive",
529
+ "onSurface"
530
+ ];
531
+ const blockColorTokenNameSet = new Set(blockColorTokenNames);
532
+ function isBlockColorToken(value) {
533
+ if (typeof value !== "string") return false;
534
+ const parts = parseColorTokenParts(value);
535
+ return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
536
+ }
537
+ function parseBlockColorToken(value) {
538
+ if (!isBlockColorToken(value)) return null;
539
+ return value.trim();
540
+ }
541
+ //#endregion
542
+ //#region ../theme-core/src/tokens/themeColorTokens.ts
543
+ /**
544
+ * Known palette role token names. This is the low-level source of truth for
545
+ * palette roles used by palette variants, Theme V2 schemas, and runtime color
546
+ * reference parsing.
547
+ */
548
+ const PALETTE_TOKEN_NAMES = [
549
+ "primary",
550
+ "primaryForeground",
551
+ "secondary",
552
+ "accent",
553
+ "success",
554
+ "warning",
555
+ "destructive",
556
+ "background",
557
+ "surface",
558
+ "surfaceAlt",
559
+ "muted",
560
+ "text",
561
+ "border"
562
+ ];
563
+ const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
564
+ const themeColorBaseTokenNames = [
565
+ ...PALETTE_TOKEN_NAMES,
566
+ ...blockColorTokenNames,
567
+ "white",
568
+ "neutral",
569
+ "primaryText",
570
+ "secondaryForeground",
571
+ "accentForeground"
572
+ ];
573
+ const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
574
+ const themeColorShadeSteps = [
575
+ "50",
576
+ "100",
577
+ "200",
578
+ "300",
579
+ "400",
580
+ "500",
581
+ "600",
582
+ "700",
583
+ "800",
584
+ "900",
585
+ "950"
586
+ ];
587
+ const shadeStepSet = new Set(themeColorShadeSteps);
588
+ function isKnownBaseToken(value) {
589
+ return themeColorBaseTokenSet.has(value);
590
+ }
591
+ function isKnownShadeToken(value) {
592
+ const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
593
+ if (!match) return false;
594
+ const [, base, shade] = match;
595
+ return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
596
+ }
597
+ function isThemeColorTokenBase(value) {
598
+ return isKnownBaseToken(value) || isKnownShadeToken(value);
599
+ }
600
+ function isThemeColorToken(value) {
601
+ if (typeof value !== "string") return false;
602
+ const parts = parseColorTokenParts(value);
603
+ return parts.kind === "token" && isThemeColorTokenBase(parts.token);
604
+ }
605
+ function parseThemeColorToken(value) {
606
+ if (!isThemeColorToken(value)) return null;
607
+ return value.trim();
608
+ }
609
+ const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
610
+ //#endregion
611
+ //#region ../theme-core/src/palette/colors.ts
612
+ const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
613
+ const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
614
+ const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
615
+ const paletteModeSchema = z$1.enum(["light", "dark"]);
616
+ z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
617
+ const paletteColorsSchema = z$1.object({
618
+ primary: hexColorSchema,
619
+ primaryForeground: hexColorSchema,
620
+ secondary: hexColorSchema,
621
+ accent: hexColorSchema,
622
+ success: hexColorSchema,
623
+ warning: hexColorSchema,
624
+ destructive: hexColorSchema,
625
+ background: hexColorSchema,
626
+ surface: hexColorSchema,
627
+ surfaceAlt: hexColorSchema,
628
+ muted: hexColorSchema,
629
+ text: hexColorSchema,
630
+ border: hexColorSchema
631
+ }).strict().transform((value) => value);
632
+ function normalizePaletteHex(value) {
633
+ if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
634
+ const normalized = value.toLowerCase();
635
+ if (normalized.length === 7) return normalized;
636
+ const triplet = normalized.slice(1);
637
+ const red = triplet.charAt(0);
638
+ const green = triplet.charAt(1);
639
+ const blue = triplet.charAt(2);
640
+ return `#${red}${red}${green}${green}${blue}${blue}`;
641
+ }
642
+ function paletteColorsFromEntries(entries) {
643
+ const values = {};
644
+ for (const entry of entries) {
645
+ const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
646
+ if (!parsedRole.success) return {
647
+ ok: false,
648
+ error: {
649
+ kind: "unknown-role",
650
+ role: entry.name
651
+ }
652
+ };
653
+ const role = parsedRole.data;
654
+ if (values[role] !== void 0) return {
655
+ ok: false,
656
+ error: {
657
+ kind: "duplicate-role",
658
+ role
659
+ }
660
+ };
661
+ const hex = normalizePaletteHex(entry.hex);
662
+ if (!hex) return {
663
+ ok: false,
664
+ error: {
665
+ kind: "invalid-color",
666
+ role,
667
+ value: entry.hex
668
+ }
669
+ };
670
+ values[role] = hex;
671
+ }
672
+ const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
673
+ if (missing.length > 0) return {
674
+ ok: false,
675
+ error: {
676
+ kind: "missing-roles",
677
+ roles: missing
678
+ }
679
+ };
680
+ return {
681
+ ok: true,
682
+ colors: paletteColorsSchema.parse(values)
683
+ };
684
+ }
685
+ function requirePaletteColorsFromEntries(entries) {
686
+ const result = paletteColorsFromEntries(entries);
687
+ if (result.ok) return result.colors;
688
+ throw new Error(`Invalid complete palette: ${result.error.kind}`);
689
+ }
690
+ new Set(PALETTE_TOKEN_NAMES);
691
+ //#endregion
464
692
  //#region ../../node_modules/.pnpm/@vibrant+image@4.0.4/node_modules/@vibrant/image/dist/esm/histogram.js
465
693
  var Histogram = class {
466
694
  constructor(pixels, opts) {
@@ -1495,233 +1723,6 @@ const pipeline = new BasicPipeline().filter.register("default", (r, g, b, a) =>
1495
1723
  //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/browser.js
1496
1724
  Vibrant.use(pipeline);
1497
1725
  //#endregion
1498
- //#region ../theme-core/src/tokens/colorTokenParts.ts
1499
- function parseOpacityPercent(source) {
1500
- const trimmed = source.trim();
1501
- if (!trimmed) return null;
1502
- if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
1503
- const value = Number(trimmed);
1504
- if (!Number.isFinite(value)) return null;
1505
- if (value < 0 || value > 100) return null;
1506
- return value;
1507
- }
1508
- function parseColorTokenParts(source) {
1509
- const trimmed = source.trim();
1510
- if (trimmed.startsWith("raw:")) return {
1511
- kind: "raw",
1512
- value: trimmed.slice(4)
1513
- };
1514
- const [token, opacityRaw, extra] = trimmed.split("/");
1515
- if (!token || extra !== void 0) return {
1516
- kind: "invalid",
1517
- token: trimmed
1518
- };
1519
- if (opacityRaw === void 0) return {
1520
- kind: "token",
1521
- token,
1522
- opacityPercent: null
1523
- };
1524
- const opacityPercent = parseOpacityPercent(opacityRaw);
1525
- if (opacityPercent === null) return {
1526
- kind: "invalid",
1527
- token: trimmed
1528
- };
1529
- return {
1530
- kind: "token",
1531
- token,
1532
- opacityPercent
1533
- };
1534
- }
1535
- //#endregion
1536
- //#region ../theme-core/src/tokens/blockColorTokens.ts
1537
- const blockColorTokenNames = [
1538
- "text",
1539
- "heading",
1540
- "mutedText",
1541
- "surface",
1542
- "background",
1543
- "foreground",
1544
- "border",
1545
- "primary",
1546
- "secondary",
1547
- "accent",
1548
- "success",
1549
- "warning",
1550
- "info",
1551
- "error",
1552
- "destructive",
1553
- "scrim",
1554
- "shadow",
1555
- "onPrimary",
1556
- "onSecondary",
1557
- "onAccent",
1558
- "onSuccess",
1559
- "onWarning",
1560
- "onInfo",
1561
- "onError",
1562
- "onDestructive",
1563
- "onSurface"
1564
- ];
1565
- const blockColorTokenNameSet = new Set(blockColorTokenNames);
1566
- function isBlockColorToken(value) {
1567
- if (typeof value !== "string") return false;
1568
- const parts = parseColorTokenParts(value);
1569
- return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
1570
- }
1571
- function parseBlockColorToken(value) {
1572
- if (!isBlockColorToken(value)) return null;
1573
- return value.trim();
1574
- }
1575
- //#endregion
1576
- //#region ../theme-core/src/tokens/themeColorTokens.ts
1577
- /**
1578
- * Known palette role token names. This is the low-level source of truth for
1579
- * palette roles used by palette variants, Theme V2 schemas, and runtime color
1580
- * reference parsing.
1581
- */
1582
- const PALETTE_TOKEN_NAMES = [
1583
- "primary",
1584
- "primaryForeground",
1585
- "secondary",
1586
- "accent",
1587
- "success",
1588
- "warning",
1589
- "destructive",
1590
- "background",
1591
- "surface",
1592
- "surfaceAlt",
1593
- "muted",
1594
- "text",
1595
- "border"
1596
- ];
1597
- const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
1598
- const themeColorBaseTokenNames = [
1599
- ...PALETTE_TOKEN_NAMES,
1600
- ...blockColorTokenNames,
1601
- "white",
1602
- "neutral",
1603
- "primaryText",
1604
- "secondaryForeground",
1605
- "accentForeground"
1606
- ];
1607
- const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
1608
- const themeColorShadeSteps = [
1609
- "50",
1610
- "100",
1611
- "200",
1612
- "300",
1613
- "400",
1614
- "500",
1615
- "600",
1616
- "700",
1617
- "800",
1618
- "900",
1619
- "950"
1620
- ];
1621
- const shadeStepSet = new Set(themeColorShadeSteps);
1622
- function isKnownBaseToken(value) {
1623
- return themeColorBaseTokenSet.has(value);
1624
- }
1625
- function isKnownShadeToken(value) {
1626
- const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
1627
- if (!match) return false;
1628
- const [, base, shade] = match;
1629
- return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
1630
- }
1631
- function isThemeColorTokenBase(value) {
1632
- return isKnownBaseToken(value) || isKnownShadeToken(value);
1633
- }
1634
- function isThemeColorToken(value) {
1635
- if (typeof value !== "string") return false;
1636
- const parts = parseColorTokenParts(value);
1637
- return parts.kind === "token" && isThemeColorTokenBase(parts.token);
1638
- }
1639
- function parseThemeColorToken(value) {
1640
- if (!isThemeColorToken(value)) return null;
1641
- return value.trim();
1642
- }
1643
- const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
1644
- //#endregion
1645
- //#region ../theme-core/src/palette/colors.ts
1646
- const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
1647
- const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
1648
- const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
1649
- const paletteModeSchema = z$1.enum(["light", "dark"]);
1650
- z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
1651
- const paletteColorsSchema = z$1.object({
1652
- primary: hexColorSchema,
1653
- primaryForeground: hexColorSchema,
1654
- secondary: hexColorSchema,
1655
- accent: hexColorSchema,
1656
- success: hexColorSchema,
1657
- warning: hexColorSchema,
1658
- destructive: hexColorSchema,
1659
- background: hexColorSchema,
1660
- surface: hexColorSchema,
1661
- surfaceAlt: hexColorSchema,
1662
- muted: hexColorSchema,
1663
- text: hexColorSchema,
1664
- border: hexColorSchema
1665
- }).strict().transform((value) => value);
1666
- function normalizePaletteHex(value) {
1667
- if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
1668
- const normalized = value.toLowerCase();
1669
- if (normalized.length === 7) return normalized;
1670
- const triplet = normalized.slice(1);
1671
- const red = triplet.charAt(0);
1672
- const green = triplet.charAt(1);
1673
- const blue = triplet.charAt(2);
1674
- return `#${red}${red}${green}${green}${blue}${blue}`;
1675
- }
1676
- function paletteColorsFromEntries(entries) {
1677
- const values = {};
1678
- for (const entry of entries) {
1679
- const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
1680
- if (!parsedRole.success) return {
1681
- ok: false,
1682
- error: {
1683
- kind: "unknown-role",
1684
- role: entry.name
1685
- }
1686
- };
1687
- const role = parsedRole.data;
1688
- if (values[role] !== void 0) return {
1689
- ok: false,
1690
- error: {
1691
- kind: "duplicate-role",
1692
- role
1693
- }
1694
- };
1695
- const hex = normalizePaletteHex(entry.hex);
1696
- if (!hex) return {
1697
- ok: false,
1698
- error: {
1699
- kind: "invalid-color",
1700
- role,
1701
- value: entry.hex
1702
- }
1703
- };
1704
- values[role] = hex;
1705
- }
1706
- const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
1707
- if (missing.length > 0) return {
1708
- ok: false,
1709
- error: {
1710
- kind: "missing-roles",
1711
- roles: missing
1712
- }
1713
- };
1714
- return {
1715
- ok: true,
1716
- colors: paletteColorsSchema.parse(values)
1717
- };
1718
- }
1719
- function requirePaletteColorsFromEntries(entries) {
1720
- const result = paletteColorsFromEntries(entries);
1721
- if (result.ok) return result.colors;
1722
- throw new Error(`Invalid complete palette: ${result.error.kind}`);
1723
- }
1724
- //#endregion
1725
1726
  //#region ../theme-core/src/site-styles/styleTags.ts
1726
1727
  /**
1727
1728
  * Descriptive style-axis metadata for button personalities and palette
@@ -1995,6 +1996,71 @@ const brandLedJewel = defineCuratedPalette({
1995
1996
  }
1996
1997
  ]
1997
1998
  });
1999
+ const brandLedSpruce = defineCuratedPalette({
2000
+ id: "brand-led-spruce",
2001
+ name: "Brand-led — Spruce",
2002
+ description: "Spruce-ink primary and moss secondary with an amber accent over a green-mist ground and hairline sage borders.",
2003
+ tags: {
2004
+ tone: "classic",
2005
+ energy: "calm",
2006
+ era: "editorial"
2007
+ },
2008
+ mode: "light",
2009
+ colors: [
2010
+ {
2011
+ name: "primary",
2012
+ hex: "#14312b"
2013
+ },
2014
+ {
2015
+ name: "secondary",
2016
+ hex: "#356a59"
2017
+ },
2018
+ {
2019
+ name: "accent",
2020
+ hex: "#f08a4b"
2021
+ },
2022
+ {
2023
+ name: "success",
2024
+ hex: "#356a59"
2025
+ },
2026
+ {
2027
+ name: "warning",
2028
+ hex: "#f08a4b"
2029
+ },
2030
+ {
2031
+ name: "destructive",
2032
+ hex: "#b5561f"
2033
+ },
2034
+ {
2035
+ name: "background",
2036
+ hex: "#ecefe9"
2037
+ },
2038
+ {
2039
+ name: "surface",
2040
+ hex: "#ffffff"
2041
+ },
2042
+ {
2043
+ name: "surfaceAlt",
2044
+ hex: "#f4f6f1"
2045
+ },
2046
+ {
2047
+ name: "muted",
2048
+ hex: "#5c6a64"
2049
+ },
2050
+ {
2051
+ name: "text",
2052
+ hex: "#1c2a26"
2053
+ },
2054
+ {
2055
+ name: "primaryForeground",
2056
+ hex: "#f4f7f1"
2057
+ },
2058
+ {
2059
+ name: "border",
2060
+ hex: "#d7ddd2"
2061
+ }
2062
+ ]
2063
+ });
1998
2064
  const brandLedBurgundyGold = defineCuratedPalette({
1999
2065
  id: "brand-led-burgundy-gold",
2000
2066
  name: "Brand-led — Burgundy Gold",
@@ -2534,6 +2600,7 @@ const curatedPalettes = [
2534
2600
  brandLedWarm,
2535
2601
  brandLedJewel,
2536
2602
  brandLedBurgundyGold,
2603
+ brandLedSpruce,
2537
2604
  defineCuratedPalette({
2538
2605
  id: "warm-neutral-clay",
2539
2606
  name: "Warm Neutral — Clay",
@@ -2998,6 +3065,55 @@ z$1.discriminatedUnion("kind", [
2998
3065
  })
2999
3066
  ]);
3000
3067
  //#endregion
3068
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
3069
+ const SURFACE_FOREGROUND_CSS_VARS = {
3070
+ text: "--rb-surface-text-color",
3071
+ heading: "--rb-surface-heading-color",
3072
+ link: "--rb-surface-link-color",
3073
+ supporting: "--rb-surface-supporting-color"
3074
+ };
3075
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
3076
+ const DEFAULT_ROLE_FALLBACKS = {
3077
+ text: DEFAULT_SURFACE_TEXT_COLOR,
3078
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
3079
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
3080
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
3081
+ };
3082
+ function requireSurfaceColorValue(role, value) {
3083
+ const normalized = value.trim();
3084
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
3085
+ return normalized;
3086
+ }
3087
+ function completeSurfaceForegrounds(input) {
3088
+ const text = requireSurfaceColorValue("text", input.text);
3089
+ return {
3090
+ text,
3091
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
3092
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
3093
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
3094
+ };
3095
+ }
3096
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
3097
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
3098
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
3099
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
3100
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
3101
+ }
3102
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
3103
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
3104
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
3105
+ }
3106
+ function surfaceForegroundStyle(input) {
3107
+ const foregrounds = completeSurfaceForegrounds(input);
3108
+ return {
3109
+ color: foregrounds.text,
3110
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
3111
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
3112
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
3113
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
3114
+ };
3115
+ }
3116
+ //#endregion
3001
3117
  //#region ../theme-core/src/utils/colorStyles.ts
3002
3118
  const COLOR_VAR_PREFIX = "--tb-";
3003
3119
  function parseToken(source) {
@@ -3009,9 +3125,17 @@ function parseToken(source) {
3009
3125
  if (parts.kind === "invalid") return { token: source };
3010
3126
  return {
3011
3127
  token: parts.token,
3012
- alpha: parts.opacityPercent === null ? void 0 : parts.opacityPercent / 100
3128
+ opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
3013
3129
  };
3014
3130
  }
3131
+ const SURFACE_TEXT_ROLE_BY_TOKEN = {
3132
+ text: "text",
3133
+ heading: "heading",
3134
+ mutedText: "supporting"
3135
+ };
3136
+ function surfaceRoleForTextToken(token) {
3137
+ return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
3138
+ }
3015
3139
  /**
3016
3140
  * Converts a theme color token to a CSS rgb value.
3017
3141
  * This is the primary function for applying theme colors to CSS properties.
@@ -3025,11 +3149,11 @@ function parseToken(source) {
3025
3149
  * rgbColorValue('raw:transparent') // => 'transparent'
3026
3150
  */
3027
3151
  function rgbColorValue(token) {
3028
- const { token: baseToken, alpha, raw } = parseToken(token);
3152
+ const { token: baseToken, opacityPercent, raw } = parseToken(token);
3029
3153
  if (raw) return raw;
3030
3154
  const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
3031
- if (alpha === void 0) return `rgb(var(${cssVar}))`;
3032
- return `rgb(var(${cssVar}) / ${alpha})`;
3155
+ if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
3156
+ return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
3033
3157
  }
3034
3158
  /**
3035
3159
  * Creates a backgroundColor style object from a theme token.
@@ -3047,6 +3171,9 @@ function backgroundColorStyle(token) {
3047
3171
  * @returns CSSProperties with color
3048
3172
  */
3049
3173
  function textColorStyle(token) {
3174
+ const parsed = parseToken(token);
3175
+ const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
3176
+ if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
3050
3177
  return { color: rgbColorValue(token) };
3051
3178
  }
3052
3179
  /**
@@ -3316,6 +3443,10 @@ const buttonBorderSchema = z$1.object({
3316
3443
  widthClass: z$1.string(),
3317
3444
  colorToken: themeColorTokenSchema
3318
3445
  });
3446
+ const buttonVariantAffordanceSchema = z$1.object({
3447
+ iconId: z$1.string().min(1),
3448
+ colorToken: themeColorTokenSchema.optional()
3449
+ });
3319
3450
  const variantShadowSchema = z$1.object({
3320
3451
  elevation: z$1.enum([
3321
3452
  "none",
@@ -3369,7 +3500,8 @@ const buttonVariantSchema = z$1.object({
3369
3500
  border: buttonBorderSchema.optional(),
3370
3501
  hoverBackgroundToken: z$1.string().optional(),
3371
3502
  effects: variantEffectsSchema.optional(),
3372
- sizes: variantSizeOverridesSchema.optional()
3503
+ sizes: variantSizeOverridesSchema.optional(),
3504
+ affordance: buttonVariantAffordanceSchema.optional()
3373
3505
  });
3374
3506
  const buttonSystemSchema = z$1.object({
3375
3507
  global: buttonGlobalSettingsSchema,
@@ -3914,6 +4046,136 @@ const editorialLink = defineButtonPersonality({
3914
4046
  }
3915
4047
  });
3916
4048
  //#endregion
4049
+ //#region ../theme-core/src/buttons/personalities/flat-ink.ts
4050
+ /**
4051
+ * Flat Ink — a solid ink primary, a hairline ghost secondary, and a single
4052
+ * amber accent climax, all completely flat (no drop shadow) with a modest
4053
+ * radius. Reads as grounded, editorial and clinician-precise: the Fulcrum
4054
+ * physiotherapy voice. The primary shifts to the secondary (moss) on hover and
4055
+ * every variant lifts 2px; the amber accent is meant to be used sparingly as
4056
+ * the page's one conversion climax.
4057
+ */
4058
+ const flatInk = defineButtonPersonality({
4059
+ id: "flat-ink",
4060
+ name: "Flat Ink",
4061
+ description: "Flat solid-ink primary, hairline ghost secondary, and a single amber accent climax — modest radius, no shadow.",
4062
+ tags: {
4063
+ tone: "classic",
4064
+ energy: "calm",
4065
+ motion: "subtle",
4066
+ era: "editorial"
4067
+ },
4068
+ buttonSystem: {
4069
+ global: {
4070
+ cornerStyle: "rounded",
4071
+ shadow: "low",
4072
+ textTransform: "none",
4073
+ fontWeight: 600,
4074
+ borderWidth: 1,
4075
+ typography: "body",
4076
+ letterSpacing: "tight",
4077
+ paddingPreset: "default",
4078
+ hoverTransform: "lift",
4079
+ hoverColor: "none"
4080
+ },
4081
+ sizes: {
4082
+ sm: {
4083
+ padding: "0.625rem 1.125rem",
4084
+ fontSize: "text-sm"
4085
+ },
4086
+ md: { padding: "0.8125rem 1.375rem" },
4087
+ lg: {
4088
+ padding: "0.9375rem 1.625rem",
4089
+ fontSize: "text-lg"
4090
+ },
4091
+ xl: {
4092
+ padding: "1.0625rem 2rem",
4093
+ fontSize: "text-xl"
4094
+ }
4095
+ },
4096
+ variants: [
4097
+ {
4098
+ id: "primary",
4099
+ name: "Primary",
4100
+ enabled: true,
4101
+ priority: 1,
4102
+ background: {
4103
+ type: "solid",
4104
+ colorToken: "primary"
4105
+ },
4106
+ textColorToken: "primaryForeground",
4107
+ borderRadius: "rounded-lg",
4108
+ affordance: {
4109
+ iconId: "ArrowRight",
4110
+ colorToken: "accent"
4111
+ },
4112
+ shadow: {
4113
+ elevation: "none",
4114
+ softness: null,
4115
+ position: "bottom"
4116
+ },
4117
+ effects: { hover: [{
4118
+ effectId: "lift",
4119
+ options: { translateY: "-2px" }
4120
+ }, {
4121
+ effectId: "background-on-hover",
4122
+ options: { colorToken: "secondary" }
4123
+ }] }
4124
+ },
4125
+ {
4126
+ id: "secondary",
4127
+ name: "Secondary",
4128
+ enabled: true,
4129
+ priority: 2,
4130
+ background: { type: "transparent" },
4131
+ textColorToken: "primary",
4132
+ borderRadius: "rounded-lg",
4133
+ border: {
4134
+ widthClass: "border",
4135
+ colorToken: "border"
4136
+ },
4137
+ shadow: {
4138
+ elevation: "none",
4139
+ softness: null,
4140
+ position: "bottom"
4141
+ },
4142
+ effects: { hover: [{
4143
+ effectId: "lift",
4144
+ options: { translateY: "-2px" }
4145
+ }, {
4146
+ effectId: "background-on-hover",
4147
+ options: { colorToken: "surfaceAlt" }
4148
+ }] }
4149
+ },
4150
+ {
4151
+ id: "accent",
4152
+ name: "Accent",
4153
+ enabled: true,
4154
+ priority: 3,
4155
+ background: {
4156
+ type: "solid",
4157
+ colorToken: "accent"
4158
+ },
4159
+ textColorToken: "accentForeground",
4160
+ borderRadius: "rounded-lg",
4161
+ shadow: {
4162
+ elevation: "none",
4163
+ softness: null,
4164
+ position: "bottom"
4165
+ },
4166
+ affordance: {
4167
+ iconId: "ArrowRight",
4168
+ colorToken: "accentForeground"
4169
+ },
4170
+ effects: { hover: [{
4171
+ effectId: "lift",
4172
+ options: { translateY: "-2px" }
4173
+ }] }
4174
+ }
4175
+ ]
4176
+ }
4177
+ });
4178
+ //#endregion
3917
4179
  //#region ../theme-core/src/buttons/personalities/ink-stamp.ts
3918
4180
  /**
3919
4181
  * Ink Stamp — solid earthy fills with a bordered, hand-stamped block-print
@@ -4684,6 +4946,7 @@ const buttonPersonalities = [
4684
4946
  editorialLink,
4685
4947
  pebble,
4686
4948
  inkStamp,
4949
+ flatInk,
4687
4950
  brushedWash,
4688
4951
  showtimePill,
4689
4952
  joyStamp,
@@ -5041,11 +5304,31 @@ const gradientStopSchema = z$1.object({
5041
5304
  position: z$1.number().min(0).max(100)
5042
5305
  });
5043
5306
  /**
5044
- * Gradient configuration
5307
+ * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
5308
+ * so existing radial gradients are byte-identical.
5309
+ *
5310
+ * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
5311
+ * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
5312
+ */
5313
+ const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
5314
+ /**
5315
+ * Gradient configuration.
5316
+ *
5317
+ * `type` is optional and defaults to `'linear'` so gradients persisted without a
5318
+ * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
5319
+ * `'conic'` are additive kinds:
5320
+ * - linear: `angle` (degrees)
5321
+ * - radial: optional `shape` (circle/ellipse) + `position`
5322
+ * - conic: optional `angle` (from-angle, degrees) + `position`
5045
5323
  */
5046
5324
  const gradientConfigSchema = z$1.object({
5047
- type: z$1.enum(["linear", "radial"]),
5325
+ type: z$1.enum([
5326
+ "linear",
5327
+ "radial",
5328
+ "conic"
5329
+ ]).default("linear"),
5048
5330
  angle: z$1.number().min(0).max(360).optional(),
5331
+ shape: radialGradientShapeSchema.optional(),
5049
5332
  position: z$1.string().optional(),
5050
5333
  stops: z$1.array(gradientStopSchema).min(2)
5051
5334
  });
@@ -6026,6 +6309,7 @@ const typographyScaleSchema = z$1.enum([
6026
6309
  "majorThird",
6027
6310
  "perfectFourth"
6028
6311
  ]);
6312
+ const eyebrowTypographyStyleSchema = z$1.object({ family: z$1.string().min(1) });
6029
6313
  const typographySchema = z$1.object({
6030
6314
  body: bodyTypographyStyleSchema,
6031
6315
  headings: z$1.object({
@@ -6038,7 +6322,8 @@ const typographySchema = z$1.object({
6038
6322
  h6: headingTypographyOverridesSchema.optional()
6039
6323
  }),
6040
6324
  scale: typographyScaleSchema,
6041
- proseLinkStyle: proseLinkStyleSchema.optional()
6325
+ proseLinkStyle: proseLinkStyleSchema.optional(),
6326
+ eyebrow: eyebrowTypographyStyleSchema.optional()
6042
6327
  });
6043
6328
  const spaceSchema = z$1.enum([
6044
6329
  "comfortable",
@@ -6063,7 +6348,8 @@ const shadow = z$1.object({
6063
6348
  "soft",
6064
6349
  "hard"
6065
6350
  ]).nullable(),
6066
- position: z$1.enum(["bottom", "bottom-right"]).default("bottom")
6351
+ position: z$1.enum(["bottom", "bottom-right"]).default("bottom"),
6352
+ colorToken: themeColorTokenSchema.optional()
6067
6353
  });
6068
6354
  const border = z$1.object({
6069
6355
  width: z$1.enum([
@@ -6308,6 +6594,7 @@ const headerSchema = z$1.object({
6308
6594
  navWeight: navFontWeight.default("medium"),
6309
6595
  navTypography: z$1.enum(["body", "heading"]).optional(),
6310
6596
  navTextSize: navLinkStyleSchema.shape.textSize,
6597
+ navPlacement: z$1.enum(["end", "center"]).optional(),
6311
6598
  container: headerContainerSchema,
6312
6599
  border: headerBorderSchema,
6313
6600
  shadow: componentShadowSchema.optional(),
@@ -8513,6 +8800,13 @@ const legacyHeaderLookAliases = defineExactCatalog()([
8513
8800
  styleId: asHeaderStyleId("minimal")
8514
8801
  }
8515
8802
  },
8803
+ {
8804
+ lookId: asHeaderLookId("classic-centered-nav"),
8805
+ selection: {
8806
+ layoutId: asHeaderLayoutId("classic"),
8807
+ styleId: asHeaderStyleId("fine-line")
8808
+ }
8809
+ },
8516
8810
  {
8517
8811
  lookId: asHeaderLookId("brand-solid"),
8518
8812
  selection: {
@@ -8583,6 +8877,50 @@ function getCompatibleHeaderStyleIds(layoutId) {
8583
8877
  return isHeaderStyleCompatibleWithLayout(getHeaderStyleOrThrow(styleId), layoutId);
8584
8878
  });
8585
8879
  }
8880
+ function applyNavTypography(style, typography) {
8881
+ switch (typography) {
8882
+ case "plain": return style;
8883
+ case "heading": return {
8884
+ ...style,
8885
+ typography: "heading",
8886
+ fontWeight: "medium",
8887
+ letterSpacing: "wide"
8888
+ };
8889
+ case "quiet-caps": return {
8890
+ ...style,
8891
+ textTransform: "uppercase",
8892
+ textSize: "sm",
8893
+ letterSpacing: "wide"
8894
+ };
8895
+ case "brand-caps": return {
8896
+ ...style,
8897
+ textTransform: "uppercase",
8898
+ textSize: "sm",
8899
+ letterSpacing: "wider"
8900
+ };
8901
+ default: return assertNever$1(typography, "Unhandled header nav typography");
8902
+ }
8903
+ }
8904
+ function underlineNavLinkStyle(colorToken, underlineColorToken, typography = "plain") {
8905
+ return applyNavTypography({
8906
+ name: "Underline grow",
8907
+ typography: "body",
8908
+ fontWeight: "medium",
8909
+ textTransform: "none",
8910
+ italic: false,
8911
+ colorToken,
8912
+ padding: "compact",
8913
+ paddingX: "compact",
8914
+ borderRadius: "none",
8915
+ effects: { hover: [{
8916
+ effectId: "nav-underline",
8917
+ options: {
8918
+ style: "grow",
8919
+ ...underlineColorToken ? { colorToken: underlineColorToken } : {}
8920
+ }
8921
+ }] }
8922
+ }, typography);
8923
+ }
8586
8924
  function themedHeaderShadow(theme) {
8587
8925
  switch (theme.shadow.elevation) {
8588
8926
  case "none": return;
@@ -8682,6 +9020,38 @@ function cleanBaseHeader(theme) {
8682
9020
  ctaGap: "default"
8683
9021
  };
8684
9022
  }
9023
+ function classicCenteredNavHeader(theme) {
9024
+ return {
9025
+ ...baseHeader(theme),
9026
+ variant: "classic",
9027
+ navPlacement: "center",
9028
+ positioning: "sticky",
9029
+ shrinkOnScroll: false,
9030
+ background: {
9031
+ type: "color",
9032
+ color: "surface"
9033
+ },
9034
+ textColor: "text",
9035
+ navColor: "text",
9036
+ navStyle: "underline-grow",
9037
+ navWeight: "medium",
9038
+ navLinkStyle: underlineNavLinkStyle("text", "accent"),
9039
+ border: {
9040
+ style: "solid",
9041
+ width: "thin",
9042
+ position: "bottom",
9043
+ colorToken: "border"
9044
+ },
9045
+ ctaTreatment: "primary",
9046
+ ctaVariant: resolveHeaderCtaVariant(theme, "primary"),
9047
+ ctaGap: "default",
9048
+ logoText: {
9049
+ mobileMaxWidth: "sm",
9050
+ mobileWrapLines: 1,
9051
+ hideOnShrink: false
9052
+ }
9053
+ };
9054
+ }
8685
9055
  function serifLineHeader(theme) {
8686
9056
  return {
8687
9057
  ...baseHeader(theme),
@@ -8846,6 +9216,21 @@ const headerLookCatalog = defineExactCatalog()([
8846
9216
  logoAccommodations: inlineClassicLogoAccommodations,
8847
9217
  compile: cleanBaseHeader
8848
9218
  },
9219
+ {
9220
+ id: asHeaderLookId("classic-centered-nav"),
9221
+ label: "Centered nav",
9222
+ visualTreatment: "solid-neutral",
9223
+ layoutBehavior: "sticky",
9224
+ configuration: {
9225
+ structure: "inline-classic",
9226
+ treatment: "solid-neutral",
9227
+ behavior: "sticky"
9228
+ },
9229
+ openingRequirement: "none",
9230
+ maxRecommendedNavItems: 6,
9231
+ logoAccommodations: inlineClassicLogoAccommodations,
9232
+ compile: classicCenteredNavHeader
9233
+ },
8849
9234
  {
8850
9235
  id: asHeaderLookId("brand-solid"),
8851
9236
  label: "Brand solid",
@@ -10012,6 +10397,74 @@ const typographyPresets = [
10012
10397
  }
10013
10398
  }
10014
10399
  }
10400
+ }),
10401
+ defineTypographyPreset({
10402
+ id: "typography-preset:fulcrum",
10403
+ name: "Fulcrum",
10404
+ description: "Newsreader serif headings over an IBM Plex Sans body, with an IBM Plex Mono eyebrow role for uppercase kickers and mono captions.",
10405
+ defaultFonts: {
10406
+ body: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
10407
+ heading: "Newsreader, Georgia, \"Times New Roman\", serif"
10408
+ },
10409
+ recommendedFonts: {
10410
+ body: [
10411
+ "IBM Plex Sans",
10412
+ "Source Sans 3",
10413
+ "Inter"
10414
+ ],
10415
+ heading: [
10416
+ "Newsreader",
10417
+ "Source Serif 4",
10418
+ "Lora"
10419
+ ]
10420
+ },
10421
+ slotPolicies: {
10422
+ body: bodyReadablePolicy,
10423
+ heading: displayHeadingPolicy
10424
+ },
10425
+ typography: {
10426
+ body: {
10427
+ family: "IBM Plex Sans, system-ui, -apple-system, sans-serif",
10428
+ size: "lg",
10429
+ weight: "regular",
10430
+ lineHeight: "relaxed",
10431
+ letterSpacing: "normal"
10432
+ },
10433
+ headings: {
10434
+ default: {
10435
+ family: "Newsreader, Georgia, \"Times New Roman\", serif",
10436
+ weight: "medium",
10437
+ lineHeight: "normal",
10438
+ letterSpacing: "tight",
10439
+ colorToken: "primary",
10440
+ italic: null,
10441
+ case: null
10442
+ },
10443
+ h1: {
10444
+ size: "2xl",
10445
+ weight: "medium",
10446
+ lineHeight: "normal",
10447
+ letterSpacing: "tight",
10448
+ italic: null
10449
+ },
10450
+ h2: {
10451
+ size: "xl",
10452
+ weight: "medium",
10453
+ lineHeight: "normal",
10454
+ letterSpacing: "tight",
10455
+ italic: null
10456
+ },
10457
+ h3: {
10458
+ size: "md",
10459
+ weight: "semibold",
10460
+ lineHeight: "normal",
10461
+ letterSpacing: "normal",
10462
+ italic: null
10463
+ }
10464
+ },
10465
+ scale: "spacious",
10466
+ eyebrow: { family: "IBM Plex Mono, ui-monospace, \"SF Mono\", monospace" }
10467
+ }
10015
10468
  })
10016
10469
  ];
10017
10470
  new Map(typographyPresets.map((preset) => [preset.id, preset]));
@@ -10232,7 +10685,8 @@ const curatedSiteStyleIdValues = [
10232
10685
  "site-style:bold-launch",
10233
10686
  "site-style:south-west-twerk-school",
10234
10687
  "site-style:kirtan-ornate",
10235
- "site-style:original-joy"
10688
+ "site-style:original-joy",
10689
+ "site-style:fulcrum"
10236
10690
  ];
10237
10691
  const quietBudget = {
10238
10692
  accentDensity: "low",
@@ -11686,6 +12140,104 @@ const curatedSiteStyles = [
11686
12140
  buttonChoice("showtime-pill")
11687
12141
  ],
11688
12142
  paletteRecommendations: [paletteRecommendation("warm-dark-ember")]
12143
+ }),
12144
+ defineCuratedSiteStyle({
12145
+ id: "site-style:fulcrum",
12146
+ name: "Fulcrum",
12147
+ headingDecoration: {
12148
+ family: "short-center-rule",
12149
+ colorToken: "accent"
12150
+ },
12151
+ 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.",
12152
+ 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.",
12153
+ siteCreatorSelection: {
12154
+ kind: "site_creator_keyword_heuristic",
12155
+ priority: 28
12156
+ },
12157
+ selectionKeywordRules: [
12158
+ {
12159
+ kind: "substring",
12160
+ value: "physiotherap"
12161
+ },
12162
+ {
12163
+ kind: "word",
12164
+ value: "physio"
12165
+ },
12166
+ {
12167
+ kind: "phrase",
12168
+ value: "sports injury"
12169
+ },
12170
+ {
12171
+ kind: "phrase",
12172
+ value: "rehab clinic"
12173
+ },
12174
+ {
12175
+ kind: "substring",
12176
+ value: "musculoskeletal"
12177
+ },
12178
+ {
12179
+ kind: "phrase",
12180
+ value: "sports therapy"
12181
+ }
12182
+ ],
12183
+ tokenRecipes: {
12184
+ palette: "brand-led",
12185
+ contrast: "standard",
12186
+ radius: "rounded",
12187
+ shadow: "subtle",
12188
+ typography: "serif-editorial",
12189
+ spacing: "airy",
12190
+ motion: "subtle"
12191
+ },
12192
+ compositionBudget: quietBudget,
12193
+ typographyPresetChoices: [
12194
+ typographyChoice("typography-preset:fulcrum"),
12195
+ typographyChoice("typography-preset:editorial-serif"),
12196
+ typographyChoice("typography-preset:modern-sans")
12197
+ ],
12198
+ defaultTypographyPresetId: "typography-preset:fulcrum",
12199
+ themeOverrides: {
12200
+ shadow: { colorToken: "primary" },
12201
+ accordions: {
12202
+ background: null,
12203
+ spacing: "comfortable",
12204
+ corners: "square",
12205
+ border: null,
12206
+ shadow: null,
12207
+ icon: {
12208
+ style: "plus-minus",
12209
+ position: "right",
12210
+ size: "md",
12211
+ colorToken: "accent"
12212
+ },
12213
+ trigger: {
12214
+ textColorToken: "primary",
12215
+ fontWeight: "medium",
12216
+ fontFamily: "heading",
12217
+ fontScale: "md"
12218
+ },
12219
+ hover: { textColorToken: "secondary" },
12220
+ content: { textColorToken: "muted" }
12221
+ },
12222
+ testimonialQuote: serifDisplayQuote
12223
+ },
12224
+ recommendedHeaderLookIds: [
12225
+ "classic-centered-nav",
12226
+ "serif-line",
12227
+ "centered-calm",
12228
+ "clean-base"
12229
+ ],
12230
+ recommendedFooterLookIds: ["footer-editorial-columns", "footer-muted-grounding"],
12231
+ buttonPersonalityChoices: [
12232
+ buttonChoice("flat-ink"),
12233
+ buttonChoice("confident-chip"),
12234
+ buttonChoice("editorial-link")
12235
+ ],
12236
+ paletteRecommendations: [
12237
+ paletteRecommendation("brand-led-spruce"),
12238
+ paletteRecommendation("warm-neutral-forest"),
12239
+ paletteRecommendation("soft-natural-sage")
12240
+ ]
11689
12241
  })
11690
12242
  ];
11691
12243
  new Map(curatedSiteStyles.map((style) => [style.id, style]));
@@ -12127,7 +12679,15 @@ function gradientToCss(config, tokens) {
12127
12679
  const stops = config.stops.map((stop) => {
12128
12680
  return `${tokens[stop.color] ?? "#000000"} ${stop.position}%`;
12129
12681
  }).join(", ");
12130
- if (config.type === "radial") return `radial-gradient(circle at ${config.position ?? "center"}, ${stops})`;
12682
+ const type = config.type ?? "linear";
12683
+ if (type === "radial") {
12684
+ const position = config.position ?? "center";
12685
+ return `radial-gradient(${config.shape ?? "circle"} at ${position}, ${stops})`;
12686
+ }
12687
+ if (type === "conic") {
12688
+ const position = config.position ?? "center";
12689
+ return `conic-gradient(from ${config.angle ?? 0}deg at ${position}, ${stops})`;
12690
+ }
12131
12691
  return `linear-gradient(${config.angle ?? 180}deg, ${stops})`;
12132
12692
  }
12133
12693
  const BREAKPOINT_LG = 1024;
@@ -14921,7 +15481,7 @@ function resolveBackgroundColorValue(value, context) {
14921
15481
  return resolveThemeColorReferenceValue(value, { palette: context.theme?.palette });
14922
15482
  }
14923
15483
  //#endregion
14924
- //#region ../blocks/src/system/transforms/registry/_utils/sectionForegroundStyles.ts
15484
+ //#region ../blocks/src/system/transforms/registry/_utils/surfaceForegroundStyles.ts
14925
15485
  function readColorToken(input, key) {
14926
15486
  const value = input[key];
14927
15487
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
@@ -14930,18 +15490,17 @@ function resolveOptionalForegroundColor(value, context) {
14930
15490
  if (!value) return null;
14931
15491
  return resolveBackgroundColorValue(value, context);
14932
15492
  }
14933
- function resolveSectionForegroundStyles(value, context) {
15493
+ function resolveSurfaceForegroundStyles(value, context) {
14934
15494
  if (!value || typeof value !== "object") return {};
14935
15495
  const input = value;
14936
15496
  const resolvedText = resolveOptionalForegroundColor(readColorToken(input, "textColor"), context);
14937
15497
  if (!resolvedText) return {};
14938
- return {
14939
- color: resolvedText,
14940
- "--section-text-color": resolvedText,
14941
- "--section-heading-color": resolveOptionalForegroundColor(readColorToken(input, "headingColor"), context) ?? resolvedText,
14942
- "--section-link-color": resolveOptionalForegroundColor(readColorToken(input, "linkColor"), context) ?? resolvedText,
14943
- "--section-supporting-color": resolveOptionalForegroundColor(readColorToken(input, "supportingColor"), context) ?? resolvedText
14944
- };
15498
+ return { ...surfaceForegroundStyle({
15499
+ text: resolvedText,
15500
+ heading: resolveOptionalForegroundColor(readColorToken(input, "headingColor"), context) ?? void 0,
15501
+ link: resolveOptionalForegroundColor(readColorToken(input, "linkColor"), context) ?? void 0,
15502
+ supporting: resolveOptionalForegroundColor(readColorToken(input, "supportingColor"), context) ?? void 0
15503
+ }) };
14945
15504
  }
14946
15505
  //#endregion
14947
15506
  //#region ../blocks/src/system/transforms/registry/boxStyles.ts
@@ -15275,67 +15834,12 @@ const containerStylesResolveBoxClassName = {
15275
15834
  }
15276
15835
  };
15277
15836
  /**
15278
- * Resolves section text color style object.
15279
- *
15280
- * The value should be the `_sectionStyles.background.textColor` token.
15281
- * Returns an empty object when unset so the section inherits text color.
15282
- *
15283
- * Outputs both `color` and `--section-text-color` CSS variable so that
15284
- * headings (which have explicit color rules in typography CSS) can inherit
15285
- * the section's text color via the CSS variable cascade.
15286
- */
15287
- const sectionTextColorStyleTransform = {
15288
- id: "sectionStyles.resolveTextColorStyle",
15289
- kind: "formatter",
15290
- summary: "Resolve section text color token into a style object",
15291
- run: (value, options, context) => {
15292
- const fallbackToken = options?.fallbackToken;
15293
- if (typeof value === "string" && value.trim()) {
15294
- const colorValue = resolveBackgroundColorValue(value.trim(), context);
15295
- return {
15296
- color: colorValue,
15297
- "--section-text-color": colorValue
15298
- };
15299
- }
15300
- if (typeof fallbackToken === "string" && fallbackToken.trim()) {
15301
- const colorValue = resolveBackgroundColorValue(fallbackToken.trim(), context);
15302
- return {
15303
- color: colorValue,
15304
- "--section-text-color": colorValue
15305
- };
15306
- }
15307
- return {};
15308
- }
15309
- };
15310
- /**
15311
- * Resolves section heading color style object.
15312
- *
15313
- * The value should be the `_sectionStyles.background.headingColor` token.
15314
- * Returns an empty object when unset so headings inherit from textColor or default.
15315
- *
15316
- * Outputs `--section-heading-color` CSS variable which typography CSS can use
15317
- * to style headings differently from body text within a section.
15318
- */
15319
- const sectionHeadingColorStyleTransform = {
15320
- id: "sectionStyles.resolveHeadingColorStyle",
15321
- kind: "formatter",
15322
- summary: "Resolve section heading color token into a style object",
15323
- run: (value, options, context) => {
15324
- const fallbackToken = options?.fallbackToken;
15325
- if (typeof value === "string" && value.trim()) return { "--section-heading-color": resolveBackgroundColorValue(value.trim(), context) };
15326
- if (typeof fallbackToken === "string" && fallbackToken.trim()) return { "--section-heading-color": resolveBackgroundColorValue(fallbackToken.trim(), context) };
15327
- return {};
15328
- }
15329
- };
15330
- /**
15331
- * Resolves section foreground colors from the background object.
15837
+ * Resolves surface foreground colors from the background object.
15332
15838
  *
15333
15839
  * The value should be the full `_sectionStyles.background` object.
15334
15840
  * Outputs:
15335
- * - `color` and `--section-text-color` from background.textColor
15336
- * - `--section-heading-color` from background.headingColor (falls back to textColor if not set)
15337
- * - `--section-link-color` from background.linkColor (falls back to textColor if not set)
15338
- * - `--section-supporting-color` from background.supportingColor (falls back to textColor if not set)
15841
+ * - `color` and the complete `--rb-surface-*-color` contract
15842
+ * - optional roles fall back atomically to background.textColor
15339
15843
  *
15340
15844
  * This combined transform replaces separate foreground color transforms
15341
15845
  * when both need to be applied to the same style prop.
@@ -15343,8 +15847,8 @@ const sectionHeadingColorStyleTransform = {
15343
15847
  const sectionColorsStyleTransform = {
15344
15848
  id: "sectionStyles.resolveColorsStyle",
15345
15849
  kind: "formatter",
15346
- summary: "Resolve section foreground colors into a style object",
15347
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
15850
+ summary: "Resolve surface foreground colors into a style object",
15851
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
15348
15852
  };
15349
15853
  /**
15350
15854
  * Maps semantic gap values to theme-aware gap classes.
@@ -15383,8 +15887,6 @@ const boxStylesTransforms = [
15383
15887
  sectionClassNameTransform,
15384
15888
  containerStylesResolveClassName,
15385
15889
  containerStylesResolveBoxClassName,
15386
- sectionTextColorStyleTransform,
15387
- sectionHeadingColorStyleTransform,
15388
15890
  sectionColorsStyleTransform,
15389
15891
  {
15390
15892
  id: "boxStyles.semanticGap",
@@ -16288,7 +16790,29 @@ const ctaRowFragment = defineFragment({
16288
16790
  fields: [createCtasRepeater({
16289
16791
  label: "Calls to action",
16290
16792
  itemLabel: "CTA",
16291
- maxItems: 3
16793
+ maxItems: 3,
16794
+ button: { variants: [
16795
+ {
16796
+ value: "primary",
16797
+ label: "Primary"
16798
+ },
16799
+ {
16800
+ value: "secondary",
16801
+ label: "Secondary"
16802
+ },
16803
+ {
16804
+ value: "outline",
16805
+ label: "Outline"
16806
+ },
16807
+ {
16808
+ value: "link",
16809
+ label: "Link"
16810
+ },
16811
+ {
16812
+ value: "accent",
16813
+ label: "Accent"
16814
+ }
16815
+ ] }
16292
16816
  })],
16293
16817
  layout: [ctaRow({
16294
16818
  collectionPath: "content.ctas",
@@ -16569,6 +17093,55 @@ function buildGridCardsLayout(className) {
16569
17093
  return el("div", { className }, buildResponsiveGridCards());
16570
17094
  }
16571
17095
  /**
17096
+ * Build a single "featured editorial" testimonial card.
17097
+ *
17098
+ * This card type deliberately renders NO avatar/headshot: an amber quote mark,
17099
+ * the quote body, then the author name and a context line. Colour + surface
17100
+ * treatment is driven entirely by `variantClass` (see testimonials.css):
17101
+ * - `rb-testimonials-fe-card--feature` → spruce-ink card (dark primary bg)
17102
+ * - `rb-testimonials-fe-card--plain` → white surface card
17103
+ *
17104
+ * Extra modifiers (`repeat`, `when`) are forwarded to the card node so the
17105
+ * caller can drive first-vs-rest branching.
17106
+ */
17107
+ function buildFeaturedEditorialCard(variantClass, ...mods) {
17108
+ const quoteMark = text({
17109
+ as: "span",
17110
+ className: "rb-testimonials-fe-qmark",
17111
+ "aria-hidden": "true"
17112
+ }, props({ value: "“" }));
17113
+ const quote = richText({ className: "rb-testimonials-fe-quote rb-max-w-none" }, bind("testimonial.content.body"), when("testimonial.content.body"));
17114
+ const meta = el("div", { className: "rb-testimonials-fe-meta" }, [text({
17115
+ as: "strong",
17116
+ className: "rb-testimonials-fe-name"
17117
+ }, bind("testimonial.content.name", { fallback: "Anonymous" })), text({
17118
+ as: "span",
17119
+ className: "rb-testimonials-fe-context"
17120
+ }, when("testimonial.content.jobTitle"), bind("testimonial.content.jobTitle"))]);
17121
+ return el("div", { className: `rb-testimonials-fe-card ${variantClass}` }, [
17122
+ quoteMark,
17123
+ quote,
17124
+ meta
17125
+ ], ...mods);
17126
+ }
17127
+ /**
17128
+ * Build the "featured editorial" layout: an asymmetric two-column arrangement
17129
+ * where the FIRST testimonial is a large featured (spruce-ink) card and the
17130
+ * remaining testimonials stack as white cards beside it.
17131
+ *
17132
+ * First-vs-rest branching is done without an offset primitive: the featured
17133
+ * card uses `repeat(..., { limit: 1 })` (entry 0 only), and the rest column
17134
+ * repeats over every entry but skips index 0 via `when("tIndex", { equals: 0,
17135
+ * not: true })`. The `$when` guard is re-evaluated per repeated item with the
17136
+ * index scope in context, so entry 0 produces no node in the rest column.
17137
+ */
17138
+ function buildFeaturedEditorialLayout() {
17139
+ 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", {
17140
+ equals: 0,
17141
+ not: true
17142
+ }))])]);
17143
+ }
17144
+ /**
16572
17145
  * Fragment definition for testimonials carousel.
16573
17146
  * Provides field definitions and data loader configuration.
16574
17147
  *
@@ -19586,7 +20159,7 @@ const defaultLayout = styledSection({
19586
20159
  children: styledContainer({
19587
20160
  baseClassName: "rb-relative rb-isolate",
19588
20161
  children: stack({
19589
- gap: "xl",
20162
+ gap: "lg",
19590
20163
  className: "rb-w-full rb-max-w-4xl rb-mx-auto"
19591
20164
  }, [headingLayout, accordionLayout])
19592
20165
  }),
@@ -19603,7 +20176,7 @@ const questionColumnsLayout = styledSection({
19603
20176
  children: styledContainer({
19604
20177
  baseClassName: "rb-relative rb-isolate",
19605
20178
  children: stack({
19606
- gap: "xl",
20179
+ gap: "lg",
19607
20180
  className: "rb-w-full"
19608
20181
  }, [headingLayout, questionColumnsAccordionLayout])
19609
20182
  }),
@@ -19768,7 +20341,26 @@ const formattingTransforms = [
19768
20341
  arrayLengthTransform,
19769
20342
  jsonArrayTransform,
19770
20343
  csvTransform,
19771
- marqueeLoopTransform
20344
+ marqueeLoopTransform,
20345
+ {
20346
+ id: "value.mapToken",
20347
+ kind: "formatter",
20348
+ summary: "Map a state/enum value to a theme token colour (rgb(var(--tb-<role>)))",
20349
+ schema: z$1.object({
20350
+ /**
20351
+ * Maps an input state/enum value (stringified) to a theme token ROLE name
20352
+ * (e.g. `secondary`, `accent`, `muted`, `border`). The role is wrapped to
20353
+ * `rgb(var(--tb-<role>))` so the result drops straight into a `style` binding.
20354
+ */
20355
+ cases: z$1.record(z$1.string(), z$1.string()),
20356
+ /** Role used when the value matches no case. Omit to yield `undefined`. */
20357
+ fallback: z$1.string().optional()
20358
+ }),
20359
+ run: (value, options) => {
20360
+ const role = options.cases[String(value)] ?? options.fallback;
20361
+ return role ? `rgb(var(--tb-${role}))` : void 0;
20362
+ }
20363
+ }
19772
20364
  ];
19773
20365
  //#endregion
19774
20366
  //#region ../blocks/src/system/transforms/registry/ui.ts
@@ -20352,6 +20944,26 @@ const headerLogoTextVisibleTransform = {
20352
20944
  return resolveHeaderBrandDisplay(asPartialObject(value)) !== "logo-only";
20353
20945
  }
20354
20946
  };
20947
+ /**
20948
+ * Selects which classic-layout nav row should render.
20949
+ *
20950
+ * - `'inline'` → classic variant with the historical logo-left / nav-right row
20951
+ * - `'centered'`→ classic variant with a centred (flex-1) nav and a right CTA
20952
+ * - `'other'` → not a classic variant (centered/floating/transparent handle it)
20953
+ *
20954
+ * Consumed by the site-header block's `$when` gates so the two classic rows are
20955
+ * mutually exclusive without introducing a new top-level header variant.
20956
+ */
20957
+ const classicNavModeTransform = {
20958
+ id: "layout.classicNavMode",
20959
+ kind: "formatter",
20960
+ summary: "Resolve which classic-layout nav row to render",
20961
+ run: (value) => {
20962
+ const header = asPartialObject(value);
20963
+ if (header?.variant !== "classic") return "other";
20964
+ return header?.navPlacement === "center" ? "centered" : "inline";
20965
+ }
20966
+ };
20355
20967
  const maxWidthClassTransform = {
20356
20968
  id: "layout.maxWidthClass",
20357
20969
  kind: "string",
@@ -20534,8 +21146,9 @@ const footerRootStyleTransform = {
20534
21146
  styleParts.push(backgroundColorStyle(token));
20535
21147
  }
20536
21148
  }
20537
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
20538
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
21149
+ const foreground = visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
21150
+ if (!visuals.isTransparent && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
21151
+ else if (foreground) styleParts.push({ color: foreground });
20539
21152
  styleParts.push(borderColorStyle("border"));
20540
21153
  return mergeStyles(...styleParts);
20541
21154
  }
@@ -20621,18 +21234,16 @@ const footerBottomBarStyleTransform = {
20621
21234
  const styleParts = [];
20622
21235
  const background = bottomBar?.background;
20623
21236
  if (background?.type === "color") styleParts.push(backgroundColorStyle(background.color ?? "surface"));
20624
- if (bottomBar?.textColor) styleParts.push(textColorStyle(bottomBar.textColor));
20625
21237
  const borderColorToken = bottomBar?.borderTop?.colorToken;
20626
21238
  if (borderColorToken) styleParts.push(borderColorStyle(borderColorToken));
20627
21239
  else styleParts.push(borderColorStyle("border"));
20628
21240
  const borderWidth = bottomBar?.borderTop?.width;
20629
21241
  if (borderWidth && borderWidth !== "none") styleParts.push({ borderTopWidth: borderWidth === "thin" ? "1px" : borderWidth === "medium" ? "2px" : "3px" });
20630
21242
  else if (borderWidth === "none") styleParts.push({ borderTopWidth: "0px" });
20631
- if (!bottomBar?.textColor) {
20632
- const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
20633
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
20634
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
20635
- }
21243
+ const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
21244
+ const foreground = bottomBar?.textColor ? rgbColorValue(bottomBar.textColor) : visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
21245
+ if (background?.type === "color" && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
21246
+ else if (foreground && bottomBar?.textColor) styleParts.push({ color: foreground });
20636
21247
  return mergeStyles(...styleParts);
20637
21248
  }
20638
21249
  };
@@ -20767,6 +21378,7 @@ const layoutTransforms = [
20767
21378
  headerLogoClassTransform,
20768
21379
  headerLogoMediaClassTransform,
20769
21380
  headerLogoTextVisibleTransform,
21381
+ classicNavModeTransform,
20770
21382
  footerRootClassTransform,
20771
21383
  headerRootStyleTransform,
20772
21384
  headerSectionBackgroundTransform,
@@ -21173,7 +21785,18 @@ const classicLayout = inline({
21173
21785
  logoRow,
21174
21786
  createNavRow(`rb-ml-auto ${desktopOnlyDisplay("flex")} rb-gap-6`),
21175
21787
  headerCta
21176
- ], when("$root.theme.header.variant", { equals: "classic" }));
21788
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "inline"));
21789
+ const classicCenteredNavLayout = inline({
21790
+ className: bindProp("$root.theme.header.maxWidth", {
21791
+ transforms: pipe(tx("layout.maxWidthClass", { base: `rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6` })),
21792
+ fallback: `rb-container rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6`
21793
+ }),
21794
+ align: "center"
21795
+ }, [
21796
+ logoRow,
21797
+ createNavRow(`rb-flex-1 rb-justify-center ${desktopOnlyDisplay("flex")} rb-gap-6`, "center"),
21798
+ headerCta
21799
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "centered"));
21177
21800
  const centeredLayout = stack({
21178
21801
  gap: "md",
21179
21802
  align: "center",
@@ -21248,6 +21871,7 @@ const siteHeaderBlockDefinition = { manifest: createBlockManifest({
21248
21871
  }, [
21249
21872
  mobileHeaderBar,
21250
21873
  classicLayout,
21874
+ classicCenteredNavLayout,
21251
21875
  centeredLayout,
21252
21876
  transparentLayout,
21253
21877
  floatingLayout,
@@ -21456,7 +22080,7 @@ const footerLayout = section({
21456
22080
  style: { $bind: {
21457
22081
  from: "$root.theme.footer",
21458
22082
  transforms: [{ id: "layout.footerRootStyle" }],
21459
- fallback: mergeStyles(backgroundColorStyle("surface"), textColorStyle("text"), borderColorStyle("border"))
22083
+ fallback: mergeStyles(backgroundColorStyle("surface"), surfaceForegroundStyle({ text: "rgb(var(--tb-text))" }), borderColorStyle("border"))
21460
22084
  } }
21461
22085
  }, [
21462
22086
  simpleFooterLayout,
@@ -21510,7 +22134,7 @@ const backgroundImageClassName = "rb-absolute rb-inset-0 rb-z-neg-10 rb-h-full r
21510
22134
  */
21511
22135
  const wrapVariant$2 = (variantLayout) => styledSection({
21512
22136
  children: styledContainer({ children: stack({
21513
- gap: "xl",
22137
+ gap: "lg",
21514
22138
  className: "rb-relative"
21515
22139
  }, [...testimonialsHeadingFragment.layout, variantLayout]) }),
21516
22140
  baseClass: "",
@@ -21536,6 +22160,26 @@ const splitGridLayout = styledSection({
21536
22160
  spacing: "comfortable",
21537
22161
  imageClassName: backgroundImageClassName
21538
22162
  });
22163
+ const featuredEditorialLayout = styledSection({
22164
+ children: styledContainer({
22165
+ baseClassName: "rb-relative rb-isolate",
22166
+ children: stack({
22167
+ gap: "lg",
22168
+ className: "rb-relative"
22169
+ }, [el("div", { className: "rb-testimonials-fe-head" }, [text({
22170
+ as: "h2",
22171
+ className: "rb-text-left",
22172
+ headingDecoration: "off"
22173
+ }, when("content.heading"), bind("content.heading")), el("div", { className: "rb-testimonials-fe-leadin" }, [text({
22174
+ as: "p",
22175
+ className: "rb-prose",
22176
+ style: textColorStyle("mutedText")
22177
+ }, when("content.subheading"), bind("content.subheading"))])]), buildFeaturedEditorialLayout()])
22178
+ }),
22179
+ baseClass: "",
22180
+ spacing: "comfortable",
22181
+ imageClassName: backgroundImageClassName
22182
+ });
21539
22183
  const additionalFields = parseFieldDefinitions([
21540
22184
  ...testimonialsHeadingFragment.fields,
21541
22185
  {
@@ -21623,6 +22267,7 @@ const testimonialsBlockDefinition = {
21623
22267
  carousel: carouselLayout$1,
21624
22268
  grid: gridLayout$1,
21625
22269
  "split-grid": splitGridLayout,
22270
+ "featured-editorial": featuredEditorialLayout,
21626
22271
  highlight: highlightLayout
21627
22272
  },
21628
22273
  defaultVariant: "carousel",
@@ -21642,6 +22287,11 @@ const testimonialsBlockDefinition = {
21642
22287
  description: "Heading on the left with testimonial cards on the right",
21643
22288
  icon: "PanelLeft"
21644
22289
  },
22290
+ "featured-editorial": {
22291
+ label: "Featured editorial",
22292
+ description: "A large featured ink quote beside a column of stacked cards (no avatars)",
22293
+ icon: "Quote"
22294
+ },
21645
22295
  highlight: {
21646
22296
  label: "Highlight",
21647
22297
  description: "A single, centered testimonial with no carousel",
@@ -22114,7 +22764,7 @@ function grid$2(props, children) {
22114
22764
  const cardsLayout = styledSection({
22115
22765
  children: styledContainer({
22116
22766
  rootType: "stack",
22117
- rootProps: { gap: "xl" },
22767
+ rootProps: { gap: "lg" },
22118
22768
  children: [stack({
22119
22769
  gap: "sm",
22120
22770
  className: "rb-feature-cards-heading rb-mx-auto rb-max-w-3xl rb-text-center"
@@ -24001,8 +24651,11 @@ const recoverEmptyDescriptionWhenActive = {
24001
24651
  kind: "recover-empty-when-active",
24002
24652
  placeholder: "Add description"
24003
24653
  };
24004
- function eventSectionHeaderLayoutNodes() {
24005
- 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 }))];
24654
+ function eventSectionHeaderLayoutNodes(options = {}) {
24655
+ const headingNode = text({ as: "h2" }, bind("content.heading"), when("content.heading", { authoringPresence: recoverEmptyHeadingWhenActive }));
24656
+ const descriptionNode = richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description", { authoringPresence: recoverEmptyDescriptionWhenActive }));
24657
+ if (options.splitClassName) return [el("div", { className: options.splitClassName }, [headingNode, descriptionNode])];
24658
+ return [headingNode, descriptionNode];
24006
24659
  }
24007
24660
  //#endregion
24008
24661
  //#region ../blocks/src/system/blocks/shop.shared.ts
@@ -24069,7 +24722,8 @@ const eventCalendarDisplayModeValues = [
24069
24722
  "combined",
24070
24723
  "calendar",
24071
24724
  "list",
24072
- "timetable"
24725
+ "timetable",
24726
+ "week-board"
24073
24727
  ];
24074
24728
  const eventCalendarDefaultViewValues = ["month", "week"];
24075
24729
  const eventCalendarListLayoutValues = [
@@ -26511,7 +27165,7 @@ const eventCalendarLayout = [styledSection({
26511
27165
  children: styledContainer({
26512
27166
  rootType: "stack",
26513
27167
  rootProps: { gap: "lg" },
26514
- children: [...eventSectionHeaderLayoutNodes(), el("event-calendar", withApiBindings({
27168
+ children: [...eventSectionHeaderLayoutNodes({ splitClassName: "event-section-head event-section-head--split" }), el("event-calendar", withApiBindings({
26515
27169
  events: { $bind: { from: "data.events" } },
26516
27170
  stage: { $bind: {
26517
27171
  from: "$root.previewStage",
@@ -26622,6 +27276,10 @@ const eventCalendarAdditionalFields = [
26622
27276
  value: "timetable",
26623
27277
  label: "Timetable"
26624
27278
  },
27279
+ {
27280
+ value: "week-board",
27281
+ label: "Week board"
27282
+ },
26625
27283
  {
26626
27284
  value: "list",
26627
27285
  label: "List"
@@ -26804,7 +27462,8 @@ const eventCalendarAdditionalFields = [
26804
27462
  oneOf: [
26805
27463
  "combined",
26806
27464
  "calendar",
26807
- "timetable"
27465
+ "timetable",
27466
+ "week-board"
26808
27467
  ]
26809
27468
  }
26810
27469
  }
@@ -26907,7 +27566,8 @@ const eventCalendarAdditionalFields = [
26907
27566
  "combined",
26908
27567
  "calendar",
26909
27568
  "list",
26910
- "timetable"
27569
+ "timetable",
27570
+ "week-board"
26911
27571
  ]
26912
27572
  }
26913
27573
  }
@@ -26942,7 +27602,8 @@ const eventCalendarAdditionalFields = [
26942
27602
  "combined",
26943
27603
  "calendar",
26944
27604
  "list",
26945
- "timetable"
27605
+ "timetable",
27606
+ "week-board"
26946
27607
  ]
26947
27608
  }, {
26948
27609
  field: "showFilters",
@@ -26965,7 +27626,8 @@ const eventCalendarAdditionalFields = [
26965
27626
  "combined",
26966
27627
  "calendar",
26967
27628
  "list",
26968
- "timetable"
27629
+ "timetable",
27630
+ "week-board"
26969
27631
  ]
26970
27632
  }, {
26971
27633
  field: "showFilters",
@@ -26988,7 +27650,8 @@ const eventCalendarAdditionalFields = [
26988
27650
  "combined",
26989
27651
  "calendar",
26990
27652
  "list",
26991
- "timetable"
27653
+ "timetable",
27654
+ "week-board"
26992
27655
  ]
26993
27656
  }, {
26994
27657
  field: "showFilters",
@@ -27010,7 +27673,8 @@ const eventCalendarAdditionalFields = [
27010
27673
  oneOf: [
27011
27674
  "combined",
27012
27675
  "list",
27013
- "timetable"
27676
+ "timetable",
27677
+ "week-board"
27014
27678
  ]
27015
27679
  }, { allOf: [{
27016
27680
  field: "variant",
@@ -27186,6 +27850,7 @@ const eventCalendarVariants = {
27186
27850
  combined: eventCalendarLayout[0],
27187
27851
  calendar: eventCalendarLayout[0],
27188
27852
  timetable: eventCalendarLayout[0],
27853
+ "week-board": eventCalendarLayout[0],
27189
27854
  list: eventCalendarLayout[0]
27190
27855
  };
27191
27856
  const eventCalendarContentShape = {
@@ -27199,6 +27864,7 @@ const eventCalendarContentShape = {
27199
27864
  { value: "combined" },
27200
27865
  { value: "calendar" },
27201
27866
  { value: "timetable" },
27867
+ { value: "week-board" },
27202
27868
  { value: "list" }
27203
27869
  ]
27204
27870
  },
@@ -27265,6 +27931,7 @@ const eventCalendarBlockDefinition = {
27265
27931
  combined: { label: "Combined" },
27266
27932
  calendar: { label: "Calendar" },
27267
27933
  timetable: { label: "Timetable" },
27934
+ "week-board": { label: "Week board" },
27268
27935
  list: { label: "List" }
27269
27936
  }
27270
27937
  }, eventCalendarContentShape),
@@ -32442,6 +33109,12 @@ const systemBlockDefinitions = [
32442
33109
  family: "centered",
32443
33110
  weight: "featured",
32444
33111
  bias: "soft"
33112
+ }),
33113
+ layoutProfile({
33114
+ variantKey: "featured-editorial",
33115
+ family: "split",
33116
+ weight: "featured",
33117
+ bias: "soft"
32445
33118
  })
32446
33119
  ]
32447
33120
  })),
@@ -32574,6 +33247,11 @@ const systemBlockDefinitions = [
32574
33247
  variantKey: "timetable",
32575
33248
  family: "stack",
32576
33249
  weight: "standard"
33250
+ }),
33251
+ layoutProfile({
33252
+ variantKey: "week-board",
33253
+ family: "stack",
33254
+ weight: "standard"
32577
33255
  })
32578
33256
  ]
32579
33257
  })),
@@ -32882,8 +33560,8 @@ const backgroundTransforms = [
32882
33560
  {
32883
33561
  id: "background.resolveColorsStyle",
32884
33562
  kind: "formatter",
32885
- summary: "Resolve section foreground colors from a background object into scoped CSS vars",
32886
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
33563
+ summary: "Resolve surface foreground colors from a background object into scoped CSS vars",
33564
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
32887
33565
  },
32888
33566
  {
32889
33567
  id: "background.resolveAllowOverflow",
@@ -34004,6 +34682,8 @@ const VERTICAL_IDS = [
34004
34682
  "yoga_wellness",
34005
34683
  "fitness_pt",
34006
34684
  "dance_school",
34685
+ "physiotherapy_physical_health",
34686
+ "massage_bodywork",
34007
34687
  "therapist_practitioner",
34008
34688
  "beauty_spa",
34009
34689
  "consultant_coach",
@@ -34039,6 +34719,12 @@ const VERTICAL_BOOKING_PRESETS = [
34039
34719
  preset("dance_school.workshops", "dance_school", "workshop", "Workshop", "Workshops", "One-off workshops, masterclasses, or intensives."),
34040
34720
  preset("dance_school.private_lessons", "dance_school", "appointment", "Private lesson", "Private lessons", "One-to-one lessons or coaching sessions."),
34041
34721
  preset("dance_school.shows", "dance_school", "performance", "Show", "Shows", "Ticketed performances, showcases, and recitals."),
34722
+ preset("physiotherapy_physical_health.appointments", "physiotherapy_physical_health", "appointment", "Appointment", "Appointments", "Assessments, treatments, follow-ups, and rehabilitation sessions."),
34723
+ preset("physiotherapy_physical_health.rehab_classes", "physiotherapy_physical_health", "class", "Rehab class", "Rehab classes", "Regular rehabilitation, mobility, strength, or recovery classes."),
34724
+ preset("physiotherapy_physical_health.workshops", "physiotherapy_physical_health", "workshop", "Workshop", "Workshops", "One-off workshops, talks, or practical group sessions."),
34725
+ preset("massage_bodywork.treatments", "massage_bodywork", "appointment", "Treatment", "Treatments", "Massage treatments, bodywork sessions, and one-to-one appointments."),
34726
+ preset("massage_bodywork.group_sessions", "massage_bodywork", "class", "Group session", "Group sessions", "Regular bodywork, somatic movement, or self-massage sessions."),
34727
+ preset("massage_bodywork.workshops", "massage_bodywork", "workshop", "Workshop", "Workshops", "One-off bodywork, self-massage, or somatic practice workshops."),
34042
34728
  preset("therapist_practitioner.appointments", "therapist_practitioner", "appointment", "Appointment", "Appointments", "Sessions, consultations, treatments, or assessments."),
34043
34729
  preset("therapist_practitioner.workshops", "therapist_practitioner", "workshop", "Workshop", "Workshops", "One-off group workshops, talks, or wellbeing sessions."),
34044
34730
  preset("therapist_practitioner.retreats", "therapist_practitioner", "retreat", "Retreat", "Retreats", "Multi-day retreats or immersive group experiences."),