@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
@@ -460,6 +460,234 @@ function featuresFromHex(hex) {
460
460
  };
461
461
  }
462
462
  //#endregion
463
+ //#region ../theme-core/src/tokens/colorTokenParts.ts
464
+ function parseOpacityPercent(source) {
465
+ const trimmed = source.trim();
466
+ if (!trimmed) return null;
467
+ if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
468
+ const value = Number(trimmed);
469
+ if (!Number.isFinite(value)) return null;
470
+ if (value < 0 || value > 100) return null;
471
+ return value;
472
+ }
473
+ function parseColorTokenParts(source) {
474
+ const trimmed = source.trim();
475
+ if (trimmed.startsWith("raw:")) return {
476
+ kind: "raw",
477
+ value: trimmed.slice(4)
478
+ };
479
+ const [token, opacityRaw, extra] = trimmed.split("/");
480
+ if (!token || extra !== void 0) return {
481
+ kind: "invalid",
482
+ token: trimmed
483
+ };
484
+ if (opacityRaw === void 0) return {
485
+ kind: "token",
486
+ token,
487
+ opacityPercent: null
488
+ };
489
+ const opacityPercent = parseOpacityPercent(opacityRaw);
490
+ if (opacityPercent === null) return {
491
+ kind: "invalid",
492
+ token: trimmed
493
+ };
494
+ return {
495
+ kind: "token",
496
+ token,
497
+ opacityPercent
498
+ };
499
+ }
500
+ //#endregion
501
+ //#region ../theme-core/src/tokens/blockColorTokens.ts
502
+ const blockColorTokenNames = [
503
+ "text",
504
+ "heading",
505
+ "mutedText",
506
+ "surface",
507
+ "background",
508
+ "foreground",
509
+ "border",
510
+ "primary",
511
+ "secondary",
512
+ "accent",
513
+ "success",
514
+ "warning",
515
+ "info",
516
+ "error",
517
+ "destructive",
518
+ "scrim",
519
+ "shadow",
520
+ "onPrimary",
521
+ "onSecondary",
522
+ "onAccent",
523
+ "onSuccess",
524
+ "onWarning",
525
+ "onInfo",
526
+ "onError",
527
+ "onDestructive",
528
+ "onSurface"
529
+ ];
530
+ const blockColorTokenNameSet = new Set(blockColorTokenNames);
531
+ function isBlockColorToken(value) {
532
+ if (typeof value !== "string") return false;
533
+ const parts = parseColorTokenParts(value);
534
+ return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
535
+ }
536
+ function parseBlockColorToken(value) {
537
+ if (!isBlockColorToken(value)) return null;
538
+ return value.trim();
539
+ }
540
+ //#endregion
541
+ //#region ../theme-core/src/tokens/themeColorTokens.ts
542
+ /**
543
+ * Known palette role token names. This is the low-level source of truth for
544
+ * palette roles used by palette variants, Theme V2 schemas, and runtime color
545
+ * reference parsing.
546
+ */
547
+ const PALETTE_TOKEN_NAMES = [
548
+ "primary",
549
+ "primaryForeground",
550
+ "secondary",
551
+ "accent",
552
+ "success",
553
+ "warning",
554
+ "destructive",
555
+ "background",
556
+ "surface",
557
+ "surfaceAlt",
558
+ "muted",
559
+ "text",
560
+ "border"
561
+ ];
562
+ const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
563
+ const themeColorBaseTokenNames = [
564
+ ...PALETTE_TOKEN_NAMES,
565
+ ...blockColorTokenNames,
566
+ "white",
567
+ "neutral",
568
+ "primaryText",
569
+ "secondaryForeground",
570
+ "accentForeground"
571
+ ];
572
+ const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
573
+ const themeColorShadeSteps = [
574
+ "50",
575
+ "100",
576
+ "200",
577
+ "300",
578
+ "400",
579
+ "500",
580
+ "600",
581
+ "700",
582
+ "800",
583
+ "900",
584
+ "950"
585
+ ];
586
+ const shadeStepSet = new Set(themeColorShadeSteps);
587
+ function isKnownBaseToken(value) {
588
+ return themeColorBaseTokenSet.has(value);
589
+ }
590
+ function isKnownShadeToken(value) {
591
+ const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
592
+ if (!match) return false;
593
+ const [, base, shade] = match;
594
+ return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
595
+ }
596
+ function isThemeColorTokenBase(value) {
597
+ return isKnownBaseToken(value) || isKnownShadeToken(value);
598
+ }
599
+ function isThemeColorToken(value) {
600
+ if (typeof value !== "string") return false;
601
+ const parts = parseColorTokenParts(value);
602
+ return parts.kind === "token" && isThemeColorTokenBase(parts.token);
603
+ }
604
+ function parseThemeColorToken(value) {
605
+ if (!isThemeColorToken(value)) return null;
606
+ return value.trim();
607
+ }
608
+ const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
609
+ //#endregion
610
+ //#region ../theme-core/src/palette/colors.ts
611
+ const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
612
+ const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
613
+ const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
614
+ const paletteModeSchema = z$1.enum(["light", "dark"]);
615
+ z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
616
+ const paletteColorsSchema = z$1.object({
617
+ primary: hexColorSchema,
618
+ primaryForeground: hexColorSchema,
619
+ secondary: hexColorSchema,
620
+ accent: hexColorSchema,
621
+ success: hexColorSchema,
622
+ warning: hexColorSchema,
623
+ destructive: hexColorSchema,
624
+ background: hexColorSchema,
625
+ surface: hexColorSchema,
626
+ surfaceAlt: hexColorSchema,
627
+ muted: hexColorSchema,
628
+ text: hexColorSchema,
629
+ border: hexColorSchema
630
+ }).strict().transform((value) => value);
631
+ function normalizePaletteHex(value) {
632
+ if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
633
+ const normalized = value.toLowerCase();
634
+ if (normalized.length === 7) return normalized;
635
+ const triplet = normalized.slice(1);
636
+ const red = triplet.charAt(0);
637
+ const green = triplet.charAt(1);
638
+ const blue = triplet.charAt(2);
639
+ return `#${red}${red}${green}${green}${blue}${blue}`;
640
+ }
641
+ function paletteColorsFromEntries(entries) {
642
+ const values = {};
643
+ for (const entry of entries) {
644
+ const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
645
+ if (!parsedRole.success) return {
646
+ ok: false,
647
+ error: {
648
+ kind: "unknown-role",
649
+ role: entry.name
650
+ }
651
+ };
652
+ const role = parsedRole.data;
653
+ if (values[role] !== void 0) return {
654
+ ok: false,
655
+ error: {
656
+ kind: "duplicate-role",
657
+ role
658
+ }
659
+ };
660
+ const hex = normalizePaletteHex(entry.hex);
661
+ if (!hex) return {
662
+ ok: false,
663
+ error: {
664
+ kind: "invalid-color",
665
+ role,
666
+ value: entry.hex
667
+ }
668
+ };
669
+ values[role] = hex;
670
+ }
671
+ const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
672
+ if (missing.length > 0) return {
673
+ ok: false,
674
+ error: {
675
+ kind: "missing-roles",
676
+ roles: missing
677
+ }
678
+ };
679
+ return {
680
+ ok: true,
681
+ colors: paletteColorsSchema.parse(values)
682
+ };
683
+ }
684
+ function requirePaletteColorsFromEntries(entries) {
685
+ const result = paletteColorsFromEntries(entries);
686
+ if (result.ok) return result.colors;
687
+ throw new Error(`Invalid complete palette: ${result.error.kind}`);
688
+ }
689
+ new Set(PALETTE_TOKEN_NAMES);
690
+ //#endregion
463
691
  //#region ../../node_modules/.pnpm/@vibrant+image@4.0.4/node_modules/@vibrant/image/dist/esm/histogram.js
464
692
  var Histogram = class {
465
693
  constructor(pixels, opts) {
@@ -1494,233 +1722,6 @@ const pipeline = new BasicPipeline().filter.register("default", (r, g, b, a) =>
1494
1722
  //#region ../../node_modules/.pnpm/node-vibrant@4.0.4/node_modules/node-vibrant/dist/esm/browser.js
1495
1723
  Vibrant.use(pipeline);
1496
1724
  //#endregion
1497
- //#region ../theme-core/src/tokens/colorTokenParts.ts
1498
- function parseOpacityPercent(source) {
1499
- const trimmed = source.trim();
1500
- if (!trimmed) return null;
1501
- if (!/^\d{1,3}(?:\.\d+)?$/.test(trimmed)) return null;
1502
- const value = Number(trimmed);
1503
- if (!Number.isFinite(value)) return null;
1504
- if (value < 0 || value > 100) return null;
1505
- return value;
1506
- }
1507
- function parseColorTokenParts(source) {
1508
- const trimmed = source.trim();
1509
- if (trimmed.startsWith("raw:")) return {
1510
- kind: "raw",
1511
- value: trimmed.slice(4)
1512
- };
1513
- const [token, opacityRaw, extra] = trimmed.split("/");
1514
- if (!token || extra !== void 0) return {
1515
- kind: "invalid",
1516
- token: trimmed
1517
- };
1518
- if (opacityRaw === void 0) return {
1519
- kind: "token",
1520
- token,
1521
- opacityPercent: null
1522
- };
1523
- const opacityPercent = parseOpacityPercent(opacityRaw);
1524
- if (opacityPercent === null) return {
1525
- kind: "invalid",
1526
- token: trimmed
1527
- };
1528
- return {
1529
- kind: "token",
1530
- token,
1531
- opacityPercent
1532
- };
1533
- }
1534
- //#endregion
1535
- //#region ../theme-core/src/tokens/blockColorTokens.ts
1536
- const blockColorTokenNames = [
1537
- "text",
1538
- "heading",
1539
- "mutedText",
1540
- "surface",
1541
- "background",
1542
- "foreground",
1543
- "border",
1544
- "primary",
1545
- "secondary",
1546
- "accent",
1547
- "success",
1548
- "warning",
1549
- "info",
1550
- "error",
1551
- "destructive",
1552
- "scrim",
1553
- "shadow",
1554
- "onPrimary",
1555
- "onSecondary",
1556
- "onAccent",
1557
- "onSuccess",
1558
- "onWarning",
1559
- "onInfo",
1560
- "onError",
1561
- "onDestructive",
1562
- "onSurface"
1563
- ];
1564
- const blockColorTokenNameSet = new Set(blockColorTokenNames);
1565
- function isBlockColorToken(value) {
1566
- if (typeof value !== "string") return false;
1567
- const parts = parseColorTokenParts(value);
1568
- return parts.kind === "token" && blockColorTokenNameSet.has(parts.token);
1569
- }
1570
- function parseBlockColorToken(value) {
1571
- if (!isBlockColorToken(value)) return null;
1572
- return value.trim();
1573
- }
1574
- //#endregion
1575
- //#region ../theme-core/src/tokens/themeColorTokens.ts
1576
- /**
1577
- * Known palette role token names. This is the low-level source of truth for
1578
- * palette roles used by palette variants, Theme V2 schemas, and runtime color
1579
- * reference parsing.
1580
- */
1581
- const PALETTE_TOKEN_NAMES = [
1582
- "primary",
1583
- "primaryForeground",
1584
- "secondary",
1585
- "accent",
1586
- "success",
1587
- "warning",
1588
- "destructive",
1589
- "background",
1590
- "surface",
1591
- "surfaceAlt",
1592
- "muted",
1593
- "text",
1594
- "border"
1595
- ];
1596
- const paletteTokenNameSchema = z$1.enum(PALETTE_TOKEN_NAMES);
1597
- const themeColorBaseTokenNames = [
1598
- ...PALETTE_TOKEN_NAMES,
1599
- ...blockColorTokenNames,
1600
- "white",
1601
- "neutral",
1602
- "primaryText",
1603
- "secondaryForeground",
1604
- "accentForeground"
1605
- ];
1606
- const themeColorBaseTokenSet = new Set(themeColorBaseTokenNames);
1607
- const themeColorShadeSteps = [
1608
- "50",
1609
- "100",
1610
- "200",
1611
- "300",
1612
- "400",
1613
- "500",
1614
- "600",
1615
- "700",
1616
- "800",
1617
- "900",
1618
- "950"
1619
- ];
1620
- const shadeStepSet = new Set(themeColorShadeSteps);
1621
- function isKnownBaseToken(value) {
1622
- return themeColorBaseTokenSet.has(value);
1623
- }
1624
- function isKnownShadeToken(value) {
1625
- const match = value.match(/^([a-zA-Z][a-zA-Z0-9]*)(?:-([0-9]{2,3}))$/);
1626
- if (!match) return false;
1627
- const [, base, shade] = match;
1628
- return Boolean(base && shade && isKnownBaseToken(base) && shadeStepSet.has(shade));
1629
- }
1630
- function isThemeColorTokenBase(value) {
1631
- return isKnownBaseToken(value) || isKnownShadeToken(value);
1632
- }
1633
- function isThemeColorToken(value) {
1634
- if (typeof value !== "string") return false;
1635
- const parts = parseColorTokenParts(value);
1636
- return parts.kind === "token" && isThemeColorTokenBase(parts.token);
1637
- }
1638
- function parseThemeColorToken(value) {
1639
- if (!isThemeColorToken(value)) return null;
1640
- return value.trim();
1641
- }
1642
- const themeColorTokenSchema = z$1.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
1643
- //#endregion
1644
- //#region ../theme-core/src/palette/colors.ts
1645
- const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/;
1646
- const NORMALIZABLE_HEX_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
1647
- const hexColorSchema = z$1.string().regex(HEX_COLOR_REGEX, "Hex must be #RRGGBB (lowercase)").transform((value) => value);
1648
- const paletteModeSchema = z$1.enum(["light", "dark"]);
1649
- z$1.string().regex(/^[0-9a-f]{64}$/, "Palette hash must be a lowercase SHA-256 value").transform((value) => value);
1650
- const paletteColorsSchema = z$1.object({
1651
- primary: hexColorSchema,
1652
- primaryForeground: hexColorSchema,
1653
- secondary: hexColorSchema,
1654
- accent: hexColorSchema,
1655
- success: hexColorSchema,
1656
- warning: hexColorSchema,
1657
- destructive: hexColorSchema,
1658
- background: hexColorSchema,
1659
- surface: hexColorSchema,
1660
- surfaceAlt: hexColorSchema,
1661
- muted: hexColorSchema,
1662
- text: hexColorSchema,
1663
- border: hexColorSchema
1664
- }).strict().transform((value) => value);
1665
- function normalizePaletteHex(value) {
1666
- if (!NORMALIZABLE_HEX_REGEX.test(value)) return null;
1667
- const normalized = value.toLowerCase();
1668
- if (normalized.length === 7) return normalized;
1669
- const triplet = normalized.slice(1);
1670
- const red = triplet.charAt(0);
1671
- const green = triplet.charAt(1);
1672
- const blue = triplet.charAt(2);
1673
- return `#${red}${red}${green}${green}${blue}${blue}`;
1674
- }
1675
- function paletteColorsFromEntries(entries) {
1676
- const values = {};
1677
- for (const entry of entries) {
1678
- const parsedRole = paletteTokenNameSchema.safeParse(entry.name);
1679
- if (!parsedRole.success) return {
1680
- ok: false,
1681
- error: {
1682
- kind: "unknown-role",
1683
- role: entry.name
1684
- }
1685
- };
1686
- const role = parsedRole.data;
1687
- if (values[role] !== void 0) return {
1688
- ok: false,
1689
- error: {
1690
- kind: "duplicate-role",
1691
- role
1692
- }
1693
- };
1694
- const hex = normalizePaletteHex(entry.hex);
1695
- if (!hex) return {
1696
- ok: false,
1697
- error: {
1698
- kind: "invalid-color",
1699
- role,
1700
- value: entry.hex
1701
- }
1702
- };
1703
- values[role] = hex;
1704
- }
1705
- const missing = PALETTE_TOKEN_NAMES.filter((role) => values[role] === void 0);
1706
- if (missing.length > 0) return {
1707
- ok: false,
1708
- error: {
1709
- kind: "missing-roles",
1710
- roles: missing
1711
- }
1712
- };
1713
- return {
1714
- ok: true,
1715
- colors: paletteColorsSchema.parse(values)
1716
- };
1717
- }
1718
- function requirePaletteColorsFromEntries(entries) {
1719
- const result = paletteColorsFromEntries(entries);
1720
- if (result.ok) return result.colors;
1721
- throw new Error(`Invalid complete palette: ${result.error.kind}`);
1722
- }
1723
- //#endregion
1724
1725
  //#region ../theme-core/src/site-styles/styleTags.ts
1725
1726
  /**
1726
1727
  * Descriptive style-axis metadata for button personalities and palette
@@ -1994,6 +1995,71 @@ const brandLedJewel = defineCuratedPalette({
1994
1995
  }
1995
1996
  ]
1996
1997
  });
1998
+ const brandLedSpruce = defineCuratedPalette({
1999
+ id: "brand-led-spruce",
2000
+ name: "Brand-led — Spruce",
2001
+ description: "Spruce-ink primary and moss secondary with an amber accent over a green-mist ground and hairline sage borders.",
2002
+ tags: {
2003
+ tone: "classic",
2004
+ energy: "calm",
2005
+ era: "editorial"
2006
+ },
2007
+ mode: "light",
2008
+ colors: [
2009
+ {
2010
+ name: "primary",
2011
+ hex: "#14312b"
2012
+ },
2013
+ {
2014
+ name: "secondary",
2015
+ hex: "#356a59"
2016
+ },
2017
+ {
2018
+ name: "accent",
2019
+ hex: "#f08a4b"
2020
+ },
2021
+ {
2022
+ name: "success",
2023
+ hex: "#356a59"
2024
+ },
2025
+ {
2026
+ name: "warning",
2027
+ hex: "#f08a4b"
2028
+ },
2029
+ {
2030
+ name: "destructive",
2031
+ hex: "#b5561f"
2032
+ },
2033
+ {
2034
+ name: "background",
2035
+ hex: "#ecefe9"
2036
+ },
2037
+ {
2038
+ name: "surface",
2039
+ hex: "#ffffff"
2040
+ },
2041
+ {
2042
+ name: "surfaceAlt",
2043
+ hex: "#f4f6f1"
2044
+ },
2045
+ {
2046
+ name: "muted",
2047
+ hex: "#5c6a64"
2048
+ },
2049
+ {
2050
+ name: "text",
2051
+ hex: "#1c2a26"
2052
+ },
2053
+ {
2054
+ name: "primaryForeground",
2055
+ hex: "#f4f7f1"
2056
+ },
2057
+ {
2058
+ name: "border",
2059
+ hex: "#d7ddd2"
2060
+ }
2061
+ ]
2062
+ });
1997
2063
  const brandLedBurgundyGold = defineCuratedPalette({
1998
2064
  id: "brand-led-burgundy-gold",
1999
2065
  name: "Brand-led — Burgundy Gold",
@@ -2533,6 +2599,7 @@ const curatedPalettes = [
2533
2599
  brandLedWarm,
2534
2600
  brandLedJewel,
2535
2601
  brandLedBurgundyGold,
2602
+ brandLedSpruce,
2536
2603
  defineCuratedPalette({
2537
2604
  id: "warm-neutral-clay",
2538
2605
  name: "Warm Neutral — Clay",
@@ -2997,6 +3064,55 @@ z$1.discriminatedUnion("kind", [
2997
3064
  })
2998
3065
  ]);
2999
3066
  //#endregion
3067
+ //#region ../theme-core/src/surfaces/foregroundContract.ts
3068
+ const SURFACE_FOREGROUND_CSS_VARS = {
3069
+ text: "--rb-surface-text-color",
3070
+ heading: "--rb-surface-heading-color",
3071
+ link: "--rb-surface-link-color",
3072
+ supporting: "--rb-surface-supporting-color"
3073
+ };
3074
+ const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
3075
+ const DEFAULT_ROLE_FALLBACKS = {
3076
+ text: DEFAULT_SURFACE_TEXT_COLOR,
3077
+ heading: DEFAULT_SURFACE_TEXT_COLOR,
3078
+ link: "rgb(var(--tb-primary, var(--tb-text, 17 24 39)))",
3079
+ supporting: "rgb(var(--tb-mutedText, 100 116 139))"
3080
+ };
3081
+ function requireSurfaceColorValue(role, value) {
3082
+ const normalized = value.trim();
3083
+ if (normalized.length === 0) throw new RangeError(`Surface foreground ${role} must be a non-empty CSS color.`);
3084
+ return normalized;
3085
+ }
3086
+ function completeSurfaceForegrounds(input) {
3087
+ const text = requireSurfaceColorValue("text", input.text);
3088
+ return {
3089
+ text,
3090
+ heading: input.heading === void 0 ? text : requireSurfaceColorValue("heading", input.heading),
3091
+ link: input.link === void 0 ? text : requireSurfaceColorValue("link", input.link),
3092
+ supporting: input.supporting === void 0 ? text : requireSurfaceColorValue("supporting", input.supporting)
3093
+ };
3094
+ }
3095
+ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FALLBACKS[role]) {
3096
+ const roleVariable = SURFACE_FOREGROUND_CSS_VARS[role];
3097
+ const fallback = requireSurfaceColorValue("fallback", outsideProviderFallback);
3098
+ if (role === "text") return `var(${roleVariable}, ${fallback})`;
3099
+ return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
3100
+ }
3101
+ function surfaceForegroundValueWithOpacity(role, opacityPercent) {
3102
+ if (!Number.isFinite(opacityPercent) || opacityPercent < 0 || opacityPercent > 100) throw new RangeError("Surface foreground opacity must be between 0 and 100.");
3103
+ return `color-mix(in srgb, ${surfaceForegroundValue(role)} ${opacityPercent}%, transparent)`;
3104
+ }
3105
+ function surfaceForegroundStyle(input) {
3106
+ const foregrounds = completeSurfaceForegrounds(input);
3107
+ return {
3108
+ color: foregrounds.text,
3109
+ [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
3110
+ [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
3111
+ [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
3112
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
3113
+ };
3114
+ }
3115
+ //#endregion
3000
3116
  //#region ../theme-core/src/utils/colorStyles.ts
3001
3117
  const COLOR_VAR_PREFIX = "--tb-";
3002
3118
  function parseToken(source) {
@@ -3008,9 +3124,17 @@ function parseToken(source) {
3008
3124
  if (parts.kind === "invalid") return { token: source };
3009
3125
  return {
3010
3126
  token: parts.token,
3011
- alpha: parts.opacityPercent === null ? void 0 : parts.opacityPercent / 100
3127
+ opacityPercent: parts.opacityPercent === null ? void 0 : parts.opacityPercent
3012
3128
  };
3013
3129
  }
3130
+ const SURFACE_TEXT_ROLE_BY_TOKEN = {
3131
+ text: "text",
3132
+ heading: "heading",
3133
+ mutedText: "supporting"
3134
+ };
3135
+ function surfaceRoleForTextToken(token) {
3136
+ return Object.prototype.hasOwnProperty.call(SURFACE_TEXT_ROLE_BY_TOKEN, token) ? SURFACE_TEXT_ROLE_BY_TOKEN[token] : null;
3137
+ }
3014
3138
  /**
3015
3139
  * Converts a theme color token to a CSS rgb value.
3016
3140
  * This is the primary function for applying theme colors to CSS properties.
@@ -3024,11 +3148,11 @@ function parseToken(source) {
3024
3148
  * rgbColorValue('raw:transparent') // => 'transparent'
3025
3149
  */
3026
3150
  function rgbColorValue(token) {
3027
- const { token: baseToken, alpha, raw } = parseToken(token);
3151
+ const { token: baseToken, opacityPercent, raw } = parseToken(token);
3028
3152
  if (raw) return raw;
3029
3153
  const cssVar = `${COLOR_VAR_PREFIX}${baseToken}`;
3030
- if (alpha === void 0) return `rgb(var(${cssVar}))`;
3031
- return `rgb(var(${cssVar}) / ${alpha})`;
3154
+ if (opacityPercent === void 0) return `rgb(var(${cssVar}))`;
3155
+ return `rgb(var(${cssVar}) / ${opacityPercent / 100})`;
3032
3156
  }
3033
3157
  /**
3034
3158
  * Creates a backgroundColor style object from a theme token.
@@ -3046,6 +3170,9 @@ function backgroundColorStyle(token) {
3046
3170
  * @returns CSSProperties with color
3047
3171
  */
3048
3172
  function textColorStyle(token) {
3173
+ const parsed = parseToken(token);
3174
+ const surfaceRole = parsed.raw ? null : surfaceRoleForTextToken(parsed.token);
3175
+ if (surfaceRole) return { color: parsed.opacityPercent === void 0 ? surfaceForegroundValue(surfaceRole) : surfaceForegroundValueWithOpacity(surfaceRole, parsed.opacityPercent) };
3049
3176
  return { color: rgbColorValue(token) };
3050
3177
  }
3051
3178
  /**
@@ -3315,6 +3442,10 @@ const buttonBorderSchema = z$1.object({
3315
3442
  widthClass: z$1.string(),
3316
3443
  colorToken: themeColorTokenSchema
3317
3444
  });
3445
+ const buttonVariantAffordanceSchema = z$1.object({
3446
+ iconId: z$1.string().min(1),
3447
+ colorToken: themeColorTokenSchema.optional()
3448
+ });
3318
3449
  const variantShadowSchema = z$1.object({
3319
3450
  elevation: z$1.enum([
3320
3451
  "none",
@@ -3368,7 +3499,8 @@ const buttonVariantSchema = z$1.object({
3368
3499
  border: buttonBorderSchema.optional(),
3369
3500
  hoverBackgroundToken: z$1.string().optional(),
3370
3501
  effects: variantEffectsSchema.optional(),
3371
- sizes: variantSizeOverridesSchema.optional()
3502
+ sizes: variantSizeOverridesSchema.optional(),
3503
+ affordance: buttonVariantAffordanceSchema.optional()
3372
3504
  });
3373
3505
  const buttonSystemSchema = z$1.object({
3374
3506
  global: buttonGlobalSettingsSchema,
@@ -3913,6 +4045,136 @@ const editorialLink = defineButtonPersonality({
3913
4045
  }
3914
4046
  });
3915
4047
  //#endregion
4048
+ //#region ../theme-core/src/buttons/personalities/flat-ink.ts
4049
+ /**
4050
+ * Flat Ink — a solid ink primary, a hairline ghost secondary, and a single
4051
+ * amber accent climax, all completely flat (no drop shadow) with a modest
4052
+ * radius. Reads as grounded, editorial and clinician-precise: the Fulcrum
4053
+ * physiotherapy voice. The primary shifts to the secondary (moss) on hover and
4054
+ * every variant lifts 2px; the amber accent is meant to be used sparingly as
4055
+ * the page's one conversion climax.
4056
+ */
4057
+ const flatInk = defineButtonPersonality({
4058
+ id: "flat-ink",
4059
+ name: "Flat Ink",
4060
+ description: "Flat solid-ink primary, hairline ghost secondary, and a single amber accent climax — modest radius, no shadow.",
4061
+ tags: {
4062
+ tone: "classic",
4063
+ energy: "calm",
4064
+ motion: "subtle",
4065
+ era: "editorial"
4066
+ },
4067
+ buttonSystem: {
4068
+ global: {
4069
+ cornerStyle: "rounded",
4070
+ shadow: "low",
4071
+ textTransform: "none",
4072
+ fontWeight: 600,
4073
+ borderWidth: 1,
4074
+ typography: "body",
4075
+ letterSpacing: "tight",
4076
+ paddingPreset: "default",
4077
+ hoverTransform: "lift",
4078
+ hoverColor: "none"
4079
+ },
4080
+ sizes: {
4081
+ sm: {
4082
+ padding: "0.625rem 1.125rem",
4083
+ fontSize: "text-sm"
4084
+ },
4085
+ md: { padding: "0.8125rem 1.375rem" },
4086
+ lg: {
4087
+ padding: "0.9375rem 1.625rem",
4088
+ fontSize: "text-lg"
4089
+ },
4090
+ xl: {
4091
+ padding: "1.0625rem 2rem",
4092
+ fontSize: "text-xl"
4093
+ }
4094
+ },
4095
+ variants: [
4096
+ {
4097
+ id: "primary",
4098
+ name: "Primary",
4099
+ enabled: true,
4100
+ priority: 1,
4101
+ background: {
4102
+ type: "solid",
4103
+ colorToken: "primary"
4104
+ },
4105
+ textColorToken: "primaryForeground",
4106
+ borderRadius: "rounded-lg",
4107
+ affordance: {
4108
+ iconId: "ArrowRight",
4109
+ colorToken: "accent"
4110
+ },
4111
+ shadow: {
4112
+ elevation: "none",
4113
+ softness: null,
4114
+ position: "bottom"
4115
+ },
4116
+ effects: { hover: [{
4117
+ effectId: "lift",
4118
+ options: { translateY: "-2px" }
4119
+ }, {
4120
+ effectId: "background-on-hover",
4121
+ options: { colorToken: "secondary" }
4122
+ }] }
4123
+ },
4124
+ {
4125
+ id: "secondary",
4126
+ name: "Secondary",
4127
+ enabled: true,
4128
+ priority: 2,
4129
+ background: { type: "transparent" },
4130
+ textColorToken: "primary",
4131
+ borderRadius: "rounded-lg",
4132
+ border: {
4133
+ widthClass: "border",
4134
+ colorToken: "border"
4135
+ },
4136
+ shadow: {
4137
+ elevation: "none",
4138
+ softness: null,
4139
+ position: "bottom"
4140
+ },
4141
+ effects: { hover: [{
4142
+ effectId: "lift",
4143
+ options: { translateY: "-2px" }
4144
+ }, {
4145
+ effectId: "background-on-hover",
4146
+ options: { colorToken: "surfaceAlt" }
4147
+ }] }
4148
+ },
4149
+ {
4150
+ id: "accent",
4151
+ name: "Accent",
4152
+ enabled: true,
4153
+ priority: 3,
4154
+ background: {
4155
+ type: "solid",
4156
+ colorToken: "accent"
4157
+ },
4158
+ textColorToken: "accentForeground",
4159
+ borderRadius: "rounded-lg",
4160
+ shadow: {
4161
+ elevation: "none",
4162
+ softness: null,
4163
+ position: "bottom"
4164
+ },
4165
+ affordance: {
4166
+ iconId: "ArrowRight",
4167
+ colorToken: "accentForeground"
4168
+ },
4169
+ effects: { hover: [{
4170
+ effectId: "lift",
4171
+ options: { translateY: "-2px" }
4172
+ }] }
4173
+ }
4174
+ ]
4175
+ }
4176
+ });
4177
+ //#endregion
3916
4178
  //#region ../theme-core/src/buttons/personalities/ink-stamp.ts
3917
4179
  /**
3918
4180
  * Ink Stamp — solid earthy fills with a bordered, hand-stamped block-print
@@ -4683,6 +4945,7 @@ const buttonPersonalities = [
4683
4945
  editorialLink,
4684
4946
  pebble,
4685
4947
  inkStamp,
4948
+ flatInk,
4686
4949
  brushedWash,
4687
4950
  showtimePill,
4688
4951
  joyStamp,
@@ -5040,11 +5303,31 @@ const gradientStopSchema = z$1.object({
5040
5303
  position: z$1.number().min(0).max(100)
5041
5304
  });
5042
5305
  /**
5043
- * Gradient configuration
5306
+ * Radial gradient shape. Optional; defaults to `circle` (the historical shape)
5307
+ * so existing radial gradients are byte-identical.
5308
+ *
5309
+ * Named `RadialGradientShape` to avoid clashing with `styles/gradients.ts`'s
5310
+ * `GradientShape` (which names the gradient *kind*: linear/radial/conic).
5311
+ */
5312
+ const radialGradientShapeSchema = z$1.enum(["circle", "ellipse"]);
5313
+ /**
5314
+ * Gradient configuration.
5315
+ *
5316
+ * `type` is optional and defaults to `'linear'` so gradients persisted without a
5317
+ * type (or with an explicit `'linear'`) render exactly as before. `'radial'` and
5318
+ * `'conic'` are additive kinds:
5319
+ * - linear: `angle` (degrees)
5320
+ * - radial: optional `shape` (circle/ellipse) + `position`
5321
+ * - conic: optional `angle` (from-angle, degrees) + `position`
5044
5322
  */
5045
5323
  const gradientConfigSchema = z$1.object({
5046
- type: z$1.enum(["linear", "radial"]),
5324
+ type: z$1.enum([
5325
+ "linear",
5326
+ "radial",
5327
+ "conic"
5328
+ ]).default("linear"),
5047
5329
  angle: z$1.number().min(0).max(360).optional(),
5330
+ shape: radialGradientShapeSchema.optional(),
5048
5331
  position: z$1.string().optional(),
5049
5332
  stops: z$1.array(gradientStopSchema).min(2)
5050
5333
  });
@@ -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
@@ -15279,67 +15838,12 @@ const containerStylesResolveBoxClassName = {
15279
15838
  }
15280
15839
  };
15281
15840
  /**
15282
- * Resolves section text color style object.
15283
- *
15284
- * The value should be the `_sectionStyles.background.textColor` token.
15285
- * Returns an empty object when unset so the section inherits text color.
15286
- *
15287
- * Outputs both `color` and `--section-text-color` CSS variable so that
15288
- * headings (which have explicit color rules in typography CSS) can inherit
15289
- * the section's text color via the CSS variable cascade.
15290
- */
15291
- const sectionTextColorStyleTransform = {
15292
- id: "sectionStyles.resolveTextColorStyle",
15293
- kind: "formatter",
15294
- summary: "Resolve section text color token into a style object",
15295
- run: (value, options, context) => {
15296
- const fallbackToken = options?.fallbackToken;
15297
- if (typeof value === "string" && value.trim()) {
15298
- const colorValue = resolveBackgroundColorValue(value.trim(), context);
15299
- return {
15300
- color: colorValue,
15301
- "--section-text-color": colorValue
15302
- };
15303
- }
15304
- if (typeof fallbackToken === "string" && fallbackToken.trim()) {
15305
- const colorValue = resolveBackgroundColorValue(fallbackToken.trim(), context);
15306
- return {
15307
- color: colorValue,
15308
- "--section-text-color": colorValue
15309
- };
15310
- }
15311
- return {};
15312
- }
15313
- };
15314
- /**
15315
- * Resolves section heading color style object.
15316
- *
15317
- * The value should be the `_sectionStyles.background.headingColor` token.
15318
- * Returns an empty object when unset so headings inherit from textColor or default.
15319
- *
15320
- * Outputs `--section-heading-color` CSS variable which typography CSS can use
15321
- * to style headings differently from body text within a section.
15322
- */
15323
- const sectionHeadingColorStyleTransform = {
15324
- id: "sectionStyles.resolveHeadingColorStyle",
15325
- kind: "formatter",
15326
- summary: "Resolve section heading color token into a style object",
15327
- run: (value, options, context) => {
15328
- const fallbackToken = options?.fallbackToken;
15329
- if (typeof value === "string" && value.trim()) return { "--section-heading-color": resolveBackgroundColorValue(value.trim(), context) };
15330
- if (typeof fallbackToken === "string" && fallbackToken.trim()) return { "--section-heading-color": resolveBackgroundColorValue(fallbackToken.trim(), context) };
15331
- return {};
15332
- }
15333
- };
15334
- /**
15335
- * Resolves section foreground colors from the background object.
15841
+ * Resolves surface foreground colors from the background object.
15336
15842
  *
15337
15843
  * The value should be the full `_sectionStyles.background` object.
15338
15844
  * Outputs:
15339
- * - `color` and `--section-text-color` from background.textColor
15340
- * - `--section-heading-color` from background.headingColor (falls back to textColor if not set)
15341
- * - `--section-link-color` from background.linkColor (falls back to textColor if not set)
15342
- * - `--section-supporting-color` from background.supportingColor (falls back to textColor if not set)
15845
+ * - `color` and the complete `--rb-surface-*-color` contract
15846
+ * - optional roles fall back atomically to background.textColor
15343
15847
  *
15344
15848
  * This combined transform replaces separate foreground color transforms
15345
15849
  * when both need to be applied to the same style prop.
@@ -15347,8 +15851,8 @@ const sectionHeadingColorStyleTransform = {
15347
15851
  const sectionColorsStyleTransform = {
15348
15852
  id: "sectionStyles.resolveColorsStyle",
15349
15853
  kind: "formatter",
15350
- summary: "Resolve section foreground colors into a style object",
15351
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
15854
+ summary: "Resolve surface foreground colors into a style object",
15855
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
15352
15856
  };
15353
15857
  /**
15354
15858
  * Maps semantic gap values to theme-aware gap classes.
@@ -15387,8 +15891,6 @@ const boxStylesTransforms = [
15387
15891
  sectionClassNameTransform,
15388
15892
  containerStylesResolveClassName,
15389
15893
  containerStylesResolveBoxClassName,
15390
- sectionTextColorStyleTransform,
15391
- sectionHeadingColorStyleTransform,
15392
15894
  sectionColorsStyleTransform,
15393
15895
  {
15394
15896
  id: "boxStyles.semanticGap",
@@ -16292,7 +16794,29 @@ const ctaRowFragment = defineFragment({
16292
16794
  fields: [createCtasRepeater({
16293
16795
  label: "Calls to action",
16294
16796
  itemLabel: "CTA",
16295
- maxItems: 3
16797
+ maxItems: 3,
16798
+ button: { variants: [
16799
+ {
16800
+ value: "primary",
16801
+ label: "Primary"
16802
+ },
16803
+ {
16804
+ value: "secondary",
16805
+ label: "Secondary"
16806
+ },
16807
+ {
16808
+ value: "outline",
16809
+ label: "Outline"
16810
+ },
16811
+ {
16812
+ value: "link",
16813
+ label: "Link"
16814
+ },
16815
+ {
16816
+ value: "accent",
16817
+ label: "Accent"
16818
+ }
16819
+ ] }
16296
16820
  })],
16297
16821
  layout: [ctaRow({
16298
16822
  collectionPath: "content.ctas",
@@ -16573,6 +17097,55 @@ function buildGridCardsLayout(className) {
16573
17097
  return el("div", { className }, buildResponsiveGridCards());
16574
17098
  }
16575
17099
  /**
17100
+ * Build a single "featured editorial" testimonial card.
17101
+ *
17102
+ * This card type deliberately renders NO avatar/headshot: an amber quote mark,
17103
+ * the quote body, then the author name and a context line. Colour + surface
17104
+ * treatment is driven entirely by `variantClass` (see testimonials.css):
17105
+ * - `rb-testimonials-fe-card--feature` → spruce-ink card (dark primary bg)
17106
+ * - `rb-testimonials-fe-card--plain` → white surface card
17107
+ *
17108
+ * Extra modifiers (`repeat`, `when`) are forwarded to the card node so the
17109
+ * caller can drive first-vs-rest branching.
17110
+ */
17111
+ function buildFeaturedEditorialCard(variantClass, ...mods) {
17112
+ const quoteMark = text({
17113
+ as: "span",
17114
+ className: "rb-testimonials-fe-qmark",
17115
+ "aria-hidden": "true"
17116
+ }, props({ value: "“" }));
17117
+ const quote = richText({ className: "rb-testimonials-fe-quote rb-max-w-none" }, bind("testimonial.content.body"), when("testimonial.content.body"));
17118
+ const meta = el("div", { className: "rb-testimonials-fe-meta" }, [text({
17119
+ as: "strong",
17120
+ className: "rb-testimonials-fe-name"
17121
+ }, bind("testimonial.content.name", { fallback: "Anonymous" })), text({
17122
+ as: "span",
17123
+ className: "rb-testimonials-fe-context"
17124
+ }, when("testimonial.content.jobTitle"), bind("testimonial.content.jobTitle"))]);
17125
+ return el("div", { className: `rb-testimonials-fe-card ${variantClass}` }, [
17126
+ quoteMark,
17127
+ quote,
17128
+ meta
17129
+ ], ...mods);
17130
+ }
17131
+ /**
17132
+ * Build the "featured editorial" layout: an asymmetric two-column arrangement
17133
+ * where the FIRST testimonial is a large featured (spruce-ink) card and the
17134
+ * remaining testimonials stack as white cards beside it.
17135
+ *
17136
+ * First-vs-rest branching is done without an offset primitive: the featured
17137
+ * card uses `repeat(..., { limit: 1 })` (entry 0 only), and the rest column
17138
+ * repeats over every entry but skips index 0 via `when("tIndex", { equals: 0,
17139
+ * not: true })`. The `$when` guard is re-evaluated per repeated item with the
17140
+ * index scope in context, so entry 0 produces no node in the rest column.
17141
+ */
17142
+ function buildFeaturedEditorialLayout() {
17143
+ 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", {
17144
+ equals: 0,
17145
+ not: true
17146
+ }))])]);
17147
+ }
17148
+ /**
16576
17149
  * Fragment definition for testimonials carousel.
16577
17150
  * Provides field definitions and data loader configuration.
16578
17151
  *
@@ -19590,7 +20163,7 @@ const defaultLayout = styledSection({
19590
20163
  children: styledContainer({
19591
20164
  baseClassName: "rb-relative rb-isolate",
19592
20165
  children: stack({
19593
- gap: "xl",
20166
+ gap: "lg",
19594
20167
  className: "rb-w-full rb-max-w-4xl rb-mx-auto"
19595
20168
  }, [headingLayout, accordionLayout])
19596
20169
  }),
@@ -19607,7 +20180,7 @@ const questionColumnsLayout = styledSection({
19607
20180
  children: styledContainer({
19608
20181
  baseClassName: "rb-relative rb-isolate",
19609
20182
  children: stack({
19610
- gap: "xl",
20183
+ gap: "lg",
19611
20184
  className: "rb-w-full"
19612
20185
  }, [headingLayout, questionColumnsAccordionLayout])
19613
20186
  }),
@@ -19772,7 +20345,26 @@ const formattingTransforms = [
19772
20345
  arrayLengthTransform,
19773
20346
  jsonArrayTransform,
19774
20347
  csvTransform,
19775
- marqueeLoopTransform
20348
+ marqueeLoopTransform,
20349
+ {
20350
+ id: "value.mapToken",
20351
+ kind: "formatter",
20352
+ summary: "Map a state/enum value to a theme token colour (rgb(var(--tb-<role>)))",
20353
+ schema: z$1.object({
20354
+ /**
20355
+ * Maps an input state/enum value (stringified) to a theme token ROLE name
20356
+ * (e.g. `secondary`, `accent`, `muted`, `border`). The role is wrapped to
20357
+ * `rgb(var(--tb-<role>))` so the result drops straight into a `style` binding.
20358
+ */
20359
+ cases: z$1.record(z$1.string(), z$1.string()),
20360
+ /** Role used when the value matches no case. Omit to yield `undefined`. */
20361
+ fallback: z$1.string().optional()
20362
+ }),
20363
+ run: (value, options) => {
20364
+ const role = options.cases[String(value)] ?? options.fallback;
20365
+ return role ? `rgb(var(--tb-${role}))` : void 0;
20366
+ }
20367
+ }
19776
20368
  ];
19777
20369
  //#endregion
19778
20370
  //#region ../blocks/src/system/transforms/registry/ui.ts
@@ -20356,6 +20948,26 @@ const headerLogoTextVisibleTransform = {
20356
20948
  return resolveHeaderBrandDisplay(asPartialObject(value)) !== "logo-only";
20357
20949
  }
20358
20950
  };
20951
+ /**
20952
+ * Selects which classic-layout nav row should render.
20953
+ *
20954
+ * - `'inline'` → classic variant with the historical logo-left / nav-right row
20955
+ * - `'centered'`→ classic variant with a centred (flex-1) nav and a right CTA
20956
+ * - `'other'` → not a classic variant (centered/floating/transparent handle it)
20957
+ *
20958
+ * Consumed by the site-header block's `$when` gates so the two classic rows are
20959
+ * mutually exclusive without introducing a new top-level header variant.
20960
+ */
20961
+ const classicNavModeTransform = {
20962
+ id: "layout.classicNavMode",
20963
+ kind: "formatter",
20964
+ summary: "Resolve which classic-layout nav row to render",
20965
+ run: (value) => {
20966
+ const header = asPartialObject(value);
20967
+ if (header?.variant !== "classic") return "other";
20968
+ return header?.navPlacement === "center" ? "centered" : "inline";
20969
+ }
20970
+ };
20359
20971
  const maxWidthClassTransform = {
20360
20972
  id: "layout.maxWidthClass",
20361
20973
  kind: "string",
@@ -20538,8 +21150,9 @@ const footerRootStyleTransform = {
20538
21150
  styleParts.push(backgroundColorStyle(token));
20539
21151
  }
20540
21152
  }
20541
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
20542
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
21153
+ const foreground = visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
21154
+ if (!visuals.isTransparent && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
21155
+ else if (foreground) styleParts.push({ color: foreground });
20543
21156
  styleParts.push(borderColorStyle("border"));
20544
21157
  return mergeStyles(...styleParts);
20545
21158
  }
@@ -20625,18 +21238,16 @@ const footerBottomBarStyleTransform = {
20625
21238
  const styleParts = [];
20626
21239
  const background = bottomBar?.background;
20627
21240
  if (background?.type === "color") styleParts.push(backgroundColorStyle(background.color ?? "surface"));
20628
- if (bottomBar?.textColor) styleParts.push(textColorStyle(bottomBar.textColor));
20629
21241
  const borderColorToken = bottomBar?.borderTop?.colorToken;
20630
21242
  if (borderColorToken) styleParts.push(borderColorStyle(borderColorToken));
20631
21243
  else styleParts.push(borderColorStyle("border"));
20632
21244
  const borderWidth = bottomBar?.borderTop?.width;
20633
21245
  if (borderWidth && borderWidth !== "none") styleParts.push({ borderTopWidth: borderWidth === "thin" ? "1px" : borderWidth === "medium" ? "2px" : "3px" });
20634
21246
  else if (borderWidth === "none") styleParts.push({ borderTopWidth: "0px" });
20635
- if (!bottomBar?.textColor) {
20636
- const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
20637
- if (visuals.textColorOverride) styleParts.push({ color: visuals.textColorOverride });
20638
- else if (visuals.textColorToken) styleParts.push(textColorStyle(visuals.textColorToken));
20639
- }
21247
+ const visuals = resolveBackgroundVisualStyles(extractBackgroundColorToken(background, "surface"), context.theme);
21248
+ const foreground = bottomBar?.textColor ? rgbColorValue(bottomBar.textColor) : visuals.textColorOverride ?? (visuals.textColorToken ? rgbColorValue(visuals.textColorToken) : null);
21249
+ if (background?.type === "color" && foreground) styleParts.push(surfaceForegroundStyle({ text: foreground }));
21250
+ else if (foreground && bottomBar?.textColor) styleParts.push({ color: foreground });
20640
21251
  return mergeStyles(...styleParts);
20641
21252
  }
20642
21253
  };
@@ -20771,6 +21382,7 @@ const layoutTransforms = [
20771
21382
  headerLogoClassTransform,
20772
21383
  headerLogoMediaClassTransform,
20773
21384
  headerLogoTextVisibleTransform,
21385
+ classicNavModeTransform,
20774
21386
  footerRootClassTransform,
20775
21387
  headerRootStyleTransform,
20776
21388
  headerSectionBackgroundTransform,
@@ -21177,7 +21789,18 @@ const classicLayout = inline({
21177
21789
  logoRow,
21178
21790
  createNavRow(`rb-ml-auto ${desktopOnlyDisplay("flex")} rb-gap-6`),
21179
21791
  headerCta
21180
- ], when("$root.theme.header.variant", { equals: "classic" }));
21792
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "inline"));
21793
+ const classicCenteredNavLayout = inline({
21794
+ className: bindProp("$root.theme.header.maxWidth", {
21795
+ transforms: pipe(tx("layout.maxWidthClass", { base: `rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6` })),
21796
+ fallback: `rb-container rb-header-layout ${desktopOnlyDisplay("flex")} rb-w-full rb-items-center rb-gap-6`
21797
+ }),
21798
+ align: "center"
21799
+ }, [
21800
+ logoRow,
21801
+ createNavRow(`rb-flex-1 rb-justify-center ${desktopOnlyDisplay("flex")} rb-gap-6`, "center"),
21802
+ headerCta
21803
+ ], whenTransformed$1("$root.theme.header", "layout.classicNavMode", "centered"));
21181
21804
  const centeredLayout = stack({
21182
21805
  gap: "md",
21183
21806
  align: "center",
@@ -21252,6 +21875,7 @@ const siteHeaderBlockDefinition = { manifest: createBlockManifest({
21252
21875
  }, [
21253
21876
  mobileHeaderBar,
21254
21877
  classicLayout,
21878
+ classicCenteredNavLayout,
21255
21879
  centeredLayout,
21256
21880
  transparentLayout,
21257
21881
  floatingLayout,
@@ -21460,7 +22084,7 @@ const footerLayout = section({
21460
22084
  style: { $bind: {
21461
22085
  from: "$root.theme.footer",
21462
22086
  transforms: [{ id: "layout.footerRootStyle" }],
21463
- fallback: mergeStyles(backgroundColorStyle("surface"), textColorStyle("text"), borderColorStyle("border"))
22087
+ fallback: mergeStyles(backgroundColorStyle("surface"), surfaceForegroundStyle({ text: "rgb(var(--tb-text))" }), borderColorStyle("border"))
21464
22088
  } }
21465
22089
  }, [
21466
22090
  simpleFooterLayout,
@@ -21514,7 +22138,7 @@ const backgroundImageClassName = "rb-absolute rb-inset-0 rb-z-neg-10 rb-h-full r
21514
22138
  */
21515
22139
  const wrapVariant$2 = (variantLayout) => styledSection({
21516
22140
  children: styledContainer({ children: stack({
21517
- gap: "xl",
22141
+ gap: "lg",
21518
22142
  className: "rb-relative"
21519
22143
  }, [...testimonialsHeadingFragment.layout, variantLayout]) }),
21520
22144
  baseClass: "",
@@ -21540,6 +22164,26 @@ const splitGridLayout = styledSection({
21540
22164
  spacing: "comfortable",
21541
22165
  imageClassName: backgroundImageClassName
21542
22166
  });
22167
+ const featuredEditorialLayout = styledSection({
22168
+ children: styledContainer({
22169
+ baseClassName: "rb-relative rb-isolate",
22170
+ children: stack({
22171
+ gap: "lg",
22172
+ className: "rb-relative"
22173
+ }, [el("div", { className: "rb-testimonials-fe-head" }, [text({
22174
+ as: "h2",
22175
+ className: "rb-text-left",
22176
+ headingDecoration: "off"
22177
+ }, when("content.heading"), bind("content.heading")), el("div", { className: "rb-testimonials-fe-leadin" }, [text({
22178
+ as: "p",
22179
+ className: "rb-prose",
22180
+ style: textColorStyle("mutedText")
22181
+ }, when("content.subheading"), bind("content.subheading"))])]), buildFeaturedEditorialLayout()])
22182
+ }),
22183
+ baseClass: "",
22184
+ spacing: "comfortable",
22185
+ imageClassName: backgroundImageClassName
22186
+ });
21543
22187
  const additionalFields = parseFieldDefinitions([
21544
22188
  ...testimonialsHeadingFragment.fields,
21545
22189
  {
@@ -21627,6 +22271,7 @@ const testimonialsBlockDefinition = {
21627
22271
  carousel: carouselLayout$1,
21628
22272
  grid: gridLayout$1,
21629
22273
  "split-grid": splitGridLayout,
22274
+ "featured-editorial": featuredEditorialLayout,
21630
22275
  highlight: highlightLayout
21631
22276
  },
21632
22277
  defaultVariant: "carousel",
@@ -21646,6 +22291,11 @@ const testimonialsBlockDefinition = {
21646
22291
  description: "Heading on the left with testimonial cards on the right",
21647
22292
  icon: "PanelLeft"
21648
22293
  },
22294
+ "featured-editorial": {
22295
+ label: "Featured editorial",
22296
+ description: "A large featured ink quote beside a column of stacked cards (no avatars)",
22297
+ icon: "Quote"
22298
+ },
21649
22299
  highlight: {
21650
22300
  label: "Highlight",
21651
22301
  description: "A single, centered testimonial with no carousel",
@@ -22118,7 +22768,7 @@ function grid$2(props, children) {
22118
22768
  const cardsLayout = styledSection({
22119
22769
  children: styledContainer({
22120
22770
  rootType: "stack",
22121
- rootProps: { gap: "xl" },
22771
+ rootProps: { gap: "lg" },
22122
22772
  children: [stack({
22123
22773
  gap: "sm",
22124
22774
  className: "rb-feature-cards-heading rb-mx-auto rb-max-w-3xl rb-text-center"
@@ -24005,8 +24655,11 @@ const recoverEmptyDescriptionWhenActive = {
24005
24655
  kind: "recover-empty-when-active",
24006
24656
  placeholder: "Add description"
24007
24657
  };
24008
- function eventSectionHeaderLayoutNodes() {
24009
- 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 }))];
24658
+ function eventSectionHeaderLayoutNodes(options = {}) {
24659
+ const headingNode = text({ as: "h2" }, bind("content.heading"), when("content.heading", { authoringPresence: recoverEmptyHeadingWhenActive }));
24660
+ const descriptionNode = richText({ className: "rb-prose rb-max-w-none" }, bind("content.description"), when("content.description", { authoringPresence: recoverEmptyDescriptionWhenActive }));
24661
+ if (options.splitClassName) return [el("div", { className: options.splitClassName }, [headingNode, descriptionNode])];
24662
+ return [headingNode, descriptionNode];
24010
24663
  }
24011
24664
  //#endregion
24012
24665
  //#region ../blocks/src/system/blocks/shop.shared.ts
@@ -24073,7 +24726,8 @@ const eventCalendarDisplayModeValues = [
24073
24726
  "combined",
24074
24727
  "calendar",
24075
24728
  "list",
24076
- "timetable"
24729
+ "timetable",
24730
+ "week-board"
24077
24731
  ];
24078
24732
  const eventCalendarDefaultViewValues = ["month", "week"];
24079
24733
  const eventCalendarListLayoutValues = [
@@ -26515,7 +27169,7 @@ const eventCalendarLayout = [styledSection({
26515
27169
  children: styledContainer({
26516
27170
  rootType: "stack",
26517
27171
  rootProps: { gap: "lg" },
26518
- children: [...eventSectionHeaderLayoutNodes(), el("event-calendar", withApiBindings({
27172
+ children: [...eventSectionHeaderLayoutNodes({ splitClassName: "event-section-head event-section-head--split" }), el("event-calendar", withApiBindings({
26519
27173
  events: { $bind: { from: "data.events" } },
26520
27174
  stage: { $bind: {
26521
27175
  from: "$root.previewStage",
@@ -26626,6 +27280,10 @@ const eventCalendarAdditionalFields = [
26626
27280
  value: "timetable",
26627
27281
  label: "Timetable"
26628
27282
  },
27283
+ {
27284
+ value: "week-board",
27285
+ label: "Week board"
27286
+ },
26629
27287
  {
26630
27288
  value: "list",
26631
27289
  label: "List"
@@ -26808,7 +27466,8 @@ const eventCalendarAdditionalFields = [
26808
27466
  oneOf: [
26809
27467
  "combined",
26810
27468
  "calendar",
26811
- "timetable"
27469
+ "timetable",
27470
+ "week-board"
26812
27471
  ]
26813
27472
  }
26814
27473
  }
@@ -26911,7 +27570,8 @@ const eventCalendarAdditionalFields = [
26911
27570
  "combined",
26912
27571
  "calendar",
26913
27572
  "list",
26914
- "timetable"
27573
+ "timetable",
27574
+ "week-board"
26915
27575
  ]
26916
27576
  }
26917
27577
  }
@@ -26946,7 +27606,8 @@ const eventCalendarAdditionalFields = [
26946
27606
  "combined",
26947
27607
  "calendar",
26948
27608
  "list",
26949
- "timetable"
27609
+ "timetable",
27610
+ "week-board"
26950
27611
  ]
26951
27612
  }, {
26952
27613
  field: "showFilters",
@@ -26969,7 +27630,8 @@ const eventCalendarAdditionalFields = [
26969
27630
  "combined",
26970
27631
  "calendar",
26971
27632
  "list",
26972
- "timetable"
27633
+ "timetable",
27634
+ "week-board"
26973
27635
  ]
26974
27636
  }, {
26975
27637
  field: "showFilters",
@@ -26992,7 +27654,8 @@ const eventCalendarAdditionalFields = [
26992
27654
  "combined",
26993
27655
  "calendar",
26994
27656
  "list",
26995
- "timetable"
27657
+ "timetable",
27658
+ "week-board"
26996
27659
  ]
26997
27660
  }, {
26998
27661
  field: "showFilters",
@@ -27014,7 +27677,8 @@ const eventCalendarAdditionalFields = [
27014
27677
  oneOf: [
27015
27678
  "combined",
27016
27679
  "list",
27017
- "timetable"
27680
+ "timetable",
27681
+ "week-board"
27018
27682
  ]
27019
27683
  }, { allOf: [{
27020
27684
  field: "variant",
@@ -27190,6 +27854,7 @@ const eventCalendarVariants = {
27190
27854
  combined: eventCalendarLayout[0],
27191
27855
  calendar: eventCalendarLayout[0],
27192
27856
  timetable: eventCalendarLayout[0],
27857
+ "week-board": eventCalendarLayout[0],
27193
27858
  list: eventCalendarLayout[0]
27194
27859
  };
27195
27860
  const eventCalendarContentShape = {
@@ -27203,6 +27868,7 @@ const eventCalendarContentShape = {
27203
27868
  { value: "combined" },
27204
27869
  { value: "calendar" },
27205
27870
  { value: "timetable" },
27871
+ { value: "week-board" },
27206
27872
  { value: "list" }
27207
27873
  ]
27208
27874
  },
@@ -27269,6 +27935,7 @@ const eventCalendarBlockDefinition = {
27269
27935
  combined: { label: "Combined" },
27270
27936
  calendar: { label: "Calendar" },
27271
27937
  timetable: { label: "Timetable" },
27938
+ "week-board": { label: "Week board" },
27272
27939
  list: { label: "List" }
27273
27940
  }
27274
27941
  }, eventCalendarContentShape),
@@ -32446,6 +33113,12 @@ const systemBlockDefinitions = [
32446
33113
  family: "centered",
32447
33114
  weight: "featured",
32448
33115
  bias: "soft"
33116
+ }),
33117
+ layoutProfile({
33118
+ variantKey: "featured-editorial",
33119
+ family: "split",
33120
+ weight: "featured",
33121
+ bias: "soft"
32449
33122
  })
32450
33123
  ]
32451
33124
  })),
@@ -32578,6 +33251,11 @@ const systemBlockDefinitions = [
32578
33251
  variantKey: "timetable",
32579
33252
  family: "stack",
32580
33253
  weight: "standard"
33254
+ }),
33255
+ layoutProfile({
33256
+ variantKey: "week-board",
33257
+ family: "stack",
33258
+ weight: "standard"
32581
33259
  })
32582
33260
  ]
32583
33261
  })),
@@ -32886,8 +33564,8 @@ const backgroundTransforms = [
32886
33564
  {
32887
33565
  id: "background.resolveColorsStyle",
32888
33566
  kind: "formatter",
32889
- summary: "Resolve section foreground colors from a background object into scoped CSS vars",
32890
- run: (value, _options, context) => resolveSectionForegroundStyles(value, context)
33567
+ summary: "Resolve surface foreground colors from a background object into scoped CSS vars",
33568
+ run: (value, _options, context) => resolveSurfaceForegroundStyles(value, context)
32891
33569
  },
32892
33570
  {
32893
33571
  id: "background.resolveAllowOverflow",
@@ -34008,6 +34686,8 @@ const VERTICAL_IDS = [
34008
34686
  "yoga_wellness",
34009
34687
  "fitness_pt",
34010
34688
  "dance_school",
34689
+ "physiotherapy_physical_health",
34690
+ "massage_bodywork",
34011
34691
  "therapist_practitioner",
34012
34692
  "beauty_spa",
34013
34693
  "consultant_coach",
@@ -34043,6 +34723,12 @@ const VERTICAL_BOOKING_PRESETS = [
34043
34723
  preset("dance_school.workshops", "dance_school", "workshop", "Workshop", "Workshops", "One-off workshops, masterclasses, or intensives."),
34044
34724
  preset("dance_school.private_lessons", "dance_school", "appointment", "Private lesson", "Private lessons", "One-to-one lessons or coaching sessions."),
34045
34725
  preset("dance_school.shows", "dance_school", "performance", "Show", "Shows", "Ticketed performances, showcases, and recitals."),
34726
+ preset("physiotherapy_physical_health.appointments", "physiotherapy_physical_health", "appointment", "Appointment", "Appointments", "Assessments, treatments, follow-ups, and rehabilitation sessions."),
34727
+ preset("physiotherapy_physical_health.rehab_classes", "physiotherapy_physical_health", "class", "Rehab class", "Rehab classes", "Regular rehabilitation, mobility, strength, or recovery classes."),
34728
+ preset("physiotherapy_physical_health.workshops", "physiotherapy_physical_health", "workshop", "Workshop", "Workshops", "One-off workshops, talks, or practical group sessions."),
34729
+ preset("massage_bodywork.treatments", "massage_bodywork", "appointment", "Treatment", "Treatments", "Massage treatments, bodywork sessions, and one-to-one appointments."),
34730
+ preset("massage_bodywork.group_sessions", "massage_bodywork", "class", "Group session", "Group sessions", "Regular bodywork, somatic movement, or self-massage sessions."),
34731
+ preset("massage_bodywork.workshops", "massage_bodywork", "workshop", "Workshop", "Workshops", "One-off bodywork, self-massage, or somatic practice workshops."),
34046
34732
  preset("therapist_practitioner.appointments", "therapist_practitioner", "appointment", "Appointment", "Appointments", "Sessions, consultations, treatments, or assessments."),
34047
34733
  preset("therapist_practitioner.workshops", "therapist_practitioner", "workshop", "Workshop", "Workshops", "One-off group workshops, talks, or wellbeing sessions."),
34048
34734
  preset("therapist_practitioner.retreats", "therapist_practitioner", "retreat", "Retreat", "Retreats", "Multi-day retreats or immersive group experiences."),