riverbankcms 0.11.0 → 0.13.0

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { A as footerNavigationLayouts, F as isPresent, J as parseUtcIsoDateTimeString, N as isCanonicalRichTextDoc, Q as parseBookingTimeZone, Ut as err, Wt as ok, b as stableJsonStringify, dt as assertNever, j as parseFooterCompositionV1, l as EVENT_PRESET_VALUES, n as NAVIGATION_LOCATIONS, s as resolveSiteVerticalBookingConfig, u as ORDINARY_EVENT_PRESET_VALUES } from "./src-CiZrHLCj.mjs";
2
+ import { A as footerNavigationLayouts, F as isPresent, J as parseUtcIsoDateTimeString, N as isCanonicalRichTextDoc, Q as parseBookingTimeZone, Ut as err, Wt as ok, b as stableJsonStringify, dt as assertNever, j as parseFooterCompositionV1, l as EVENT_PRESET_VALUES, n as NAVIGATION_LOCATIONS, s as resolveSiteVerticalBookingConfig, u as ORDINARY_EVENT_PRESET_VALUES } from "./src-CxEAuQpf.mjs";
3
3
  import "node:module";
4
4
  import { z } from "zod";
5
5
 
@@ -2096,21 +2096,13 @@ function parseThemeColorToken(value) {
2096
2096
  }
2097
2097
  const themeColorTokenSchema = z.string().min(1).refine((value) => Boolean(isThemeColorToken(value)), "Expected a known Theme V2 color token");
2098
2098
 
2099
- //#endregion
2100
- //#region ../theme-core/src/surfaces/foregroundRoles.ts
2101
- const SURFACE_FOREGROUND_ROLES = [
2102
- "text",
2103
- "heading",
2104
- "link",
2105
- "supporting"
2106
- ];
2107
-
2108
2099
  //#endregion
2109
2100
  //#region ../theme-core/src/surfaces/foregroundContract.ts
2110
2101
  const SURFACE_FOREGROUND_CSS_VARS = {
2111
2102
  text: "--rb-surface-text-color",
2112
2103
  heading: "--rb-surface-heading-color",
2113
2104
  link: "--rb-surface-link-color",
2105
+ linkHover: "--rb-surface-link-hover-color",
2114
2106
  supporting: "--rb-surface-supporting-color"
2115
2107
  };
2116
2108
  const DEFAULT_SURFACE_TEXT_COLOR = "rgb(var(--tb-text, 17 24 39))";
@@ -2120,6 +2112,7 @@ const DEFAULT_ROLE_FALLBACKS = {
2120
2112
  text: DEFAULT_SURFACE_TEXT_COLOR,
2121
2113
  heading: DEFAULT_SURFACE_TEXT_COLOR,
2122
2114
  link: DEFAULT_SURFACE_LINK_COLOR,
2115
+ linkHover: DEFAULT_SURFACE_LINK_COLOR,
2123
2116
  supporting: DEFAULT_SURFACE_SUPPORTING_COLOR
2124
2117
  };
2125
2118
  function requireSurfaceColorValue(role, value) {
@@ -2131,10 +2124,12 @@ function requireSurfaceColorValue(role, value) {
2131
2124
  }
2132
2125
  function completeSurfaceForegrounds(input) {
2133
2126
  const text = requireSurfaceColorValue("text", input.text);
2127
+ const link = input.link === undefined ? text : requireSurfaceColorValue("link", input.link);
2134
2128
  return {
2135
2129
  text,
2136
2130
  heading: input.heading === undefined ? text : requireSurfaceColorValue("heading", input.heading),
2137
- link: input.link === undefined ? text : requireSurfaceColorValue("link", input.link),
2131
+ link,
2132
+ linkHover: input.linkHover === undefined ? link : requireSurfaceColorValue("linkHover", input.linkHover),
2138
2133
  supporting: input.supporting === undefined ? text : requireSurfaceColorValue("supporting", input.supporting)
2139
2134
  };
2140
2135
  }
@@ -2144,6 +2139,9 @@ function surfaceForegroundValue(role, outsideProviderFallback = DEFAULT_ROLE_FAL
2144
2139
  if (role === "text") {
2145
2140
  return `var(${roleVariable}, ${fallback})`;
2146
2141
  }
2142
+ if (role === "linkHover") {
2143
+ return `var(${roleVariable}, ${surfaceForegroundValue("link", fallback)})`;
2144
+ }
2147
2145
  return `var(${roleVariable}, var(${SURFACE_FOREGROUND_CSS_VARS.text}, ${fallback}))`;
2148
2146
  }
2149
2147
  function surfaceForegroundValueWithOpacity(role, opacityPercent) {
@@ -2159,116 +2157,27 @@ function surfaceForegroundStyle(input) {
2159
2157
  [SURFACE_FOREGROUND_CSS_VARS.text]: foregrounds.text,
2160
2158
  [SURFACE_FOREGROUND_CSS_VARS.heading]: foregrounds.heading,
2161
2159
  [SURFACE_FOREGROUND_CSS_VARS.link]: foregrounds.link,
2162
- [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting
2160
+ [SURFACE_FOREGROUND_CSS_VARS.linkHover]: foregrounds.linkHover,
2161
+ [SURFACE_FOREGROUND_CSS_VARS.supporting]: foregrounds.supporting,
2162
+ "--rb-surface-link-hover-decoration": hoverDecoration(input)
2163
2163
  };
2164
2164
  }
2165
2165
  function surfaceForegroundDeclarations(input) {
2166
- const foregrounds = completeSurfaceForegrounds(input);
2167
- return [`color: ${foregrounds.text}`, ...SURFACE_FOREGROUND_ROLES.map((role) => `${SURFACE_FOREGROUND_CSS_VARS[role]}: ${foregrounds[role]}`)];
2168
- }
2169
-
2170
- //#endregion
2171
- //#region ../theme-core/src/surfaces/foregroundInteraction.ts
2172
- /**
2173
- * How far a hover state softens away from its resting foreground, as a
2174
- * percentage of full opacity.
2175
- *
2176
- * Softening — rather than brightening — is forced by the token vocabulary
2177
- * rather than chosen for taste. A resolved foreground is frequently an
2178
- * `on*` token, which `expandPalette` emits as pure `#FFFFFF` or `#000000`
2179
- * with no shade ramp: over a primary-painted section the resolved text role
2180
- * is `onPrimary`, and white has no headroom left to move further from its
2181
- * background. Moving *toward* the mid-range always has headroom, whatever
2182
- * the resting colour, and it keeps the hue exactly.
2183
- *
2184
- * 85% is the measured floor, not a round number. Softening costs contrast, and
2185
- * the binding case is a scrolled overlay header: `contrast-glass` paints 60%
2186
- * black or white, which composites the backdrop into a known band whatever the
2187
- * page content underneath happens to be. The lightest backdrop a dark glass can
2188
- * present is 60% black over white, `#666666`; a white foreground softened to
2189
- * 85% reads 4.69:1 there, against 4.07:1 at 75%, which fails. Nav links are
2190
- * 18px regular (`textSize: 'lg'`), so the bar is WCAG AA's 4.5:1 rather than
2191
- * the large-text 3:1.
2192
- *
2193
- * The mirror case — a light glass over dark content, `#999999` — cannot be met
2194
- * by any softening, because the *resting* colour is already only about 4.6:1
2195
- * there. That is a property of the glass rather than of this derivation, and
2196
- * it wants fixing where the glass tone is chosen.
2197
- */
2198
- const FOREGROUND_HOVER_OPACITY_PERCENT = 85;
2199
- /**
2200
- * Derive the hover foreground that belongs with a resolved resting foreground.
2201
- *
2202
- * A resting foreground is chosen to be legible against a known surface. A hover
2203
- * state derived from it stays within a bounded distance of that colour, so it
2204
- * inherits most of the resting colour's contrast margin instead of asserting
2205
- * an unrelated brand colour that no one checked against the surface.
2206
- *
2207
- * That bounded-distance property is what lets this be used on a transparent
2208
- * overlay header, where the backdrop is only partly knowable: the scrolled
2209
- * state composites a 60% glass tint over arbitrary page content, so no
2210
- * compile-time contrast proof is available there. Staying near a colour that
2211
- * is already trusted in both states is a weaker claim than a contrast proof,
2212
- * and it is one this function can actually keep.
2213
- *
2214
- * Returns `null` when the resting token is not a plain palette token — an
2215
- * already-faded or raw value has no well-defined softening, and the caller
2216
- * should leave hover alone rather than guess.
2217
- */
2218
- function deriveForegroundHoverToken(restingToken) {
2219
- const parts = parseColorTokenParts(restingToken);
2220
- if (parts.kind !== "token") return null;
2221
- if (parts.opacityPercent !== null) return null;
2222
- return parseThemeColorToken(`${parts.token}/${FOREGROUND_HOVER_OPACITY_PERCENT}`);
2223
- }
2224
- /**
2225
- * The opacity `contrast-glass` paints at. Mirrors the value in
2226
- * `generateHeaderScrollTreatmentStyles`; the two must agree, because the whole
2227
- * point of the bound below is that it describes what that treatment can produce.
2228
- */
2229
- const CONTRAST_GLASS_ALPHA = .6;
2230
- /**
2231
- * The extreme backdrop a glass tone can present, whatever page content happens
2232
- * to be underneath it.
2233
- *
2234
- * A 60% tint clamps the composite into a known band, which is what makes a
2235
- * conservative check possible at compile time even though the content passing
2236
- * under a sticky header is not knowable. For a dark glass the composite is
2237
- * lightest over white; for a light glass it is darkest over black. Those are
2238
- * the two ends that a foreground has to survive.
2239
- */
2240
- function worstCaseGlassBackdrop(tone) {
2241
- const [over, tint] = tone === "dark" ? ["#ffffff", "#000000"] : ["#000000", "#ffffff"];
2242
- return compositeHex$1(tint, over, CONTRAST_GLASS_ALPHA);
2166
+ return Object.entries(surfaceForegroundStyle(input)).map(([property, value]) => `${property}: ${value};`);
2243
2167
  }
2244
- /** Flatten `foreground` at `alpha` over `background` into an opaque hex. */
2245
- function compositeHex$1(foreground, background, alpha) {
2246
- const f = hexToRgb(foreground);
2247
- const b = hexToRgb(background);
2248
- return rgbToHex(Math.round(mix$2(b.r, f.r, alpha)), Math.round(mix$2(b.g, f.g, alpha)), Math.round(mix$2(b.b, f.b, alpha)));
2168
+ function surfaceForegroundInputFromResolved(foreground, color) {
2169
+ const { roles } = foreground;
2170
+ return {
2171
+ text: color(roles.text),
2172
+ heading: color(roles.heading),
2173
+ link: color(roles.link),
2174
+ linkHover: color(roles.linkHover),
2175
+ supporting: color(roles.supporting),
2176
+ hoverDecoration: foreground.hoverDecoration
2177
+ };
2249
2178
  }
2250
- /**
2251
- * Decide whether the resting foreground can safely be softened for hover.
2252
- *
2253
- * `glassTone` is the scroll treatment's compiled tone, or `null` when the style
2254
- * has no glass. Without one there is no bound on what the header can end up
2255
- * over, so this declines to soften rather than guess.
2256
- *
2257
- * The light-glass end cannot be satisfied by any softening: against `#999999`
2258
- * a dark resting foreground is already close to the 4.5:1 line, so every step
2259
- * away from it fails. That is why this returns a plan rather than a colour —
2260
- * the caller has to have a non-colour affordance to fall back on.
2261
- */
2262
- function planForegroundHover(restingToken, restingHex, glassTone) {
2263
- const softened = deriveForegroundHoverToken(restingToken);
2264
- if (!softened) return { kind: "resting" };
2265
- if (!restingHex || !glassTone) return { kind: "resting" };
2266
- const backdrop = worstCaseGlassBackdrop(glassTone);
2267
- const softenedHex = compositeHex$1(restingHex, backdrop, FOREGROUND_HOVER_OPACITY_PERCENT / 100);
2268
- return contrastRatio(softenedHex, backdrop) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO ? {
2269
- kind: "softened",
2270
- token: softened
2271
- } : { kind: "resting" };
2179
+ function hoverDecoration(input) {
2180
+ return input.hoverDecoration ?? "underline";
2272
2181
  }
2273
2182
 
2274
2183
  //#endregion
@@ -2446,11 +2355,11 @@ function resolveOpaqueSurfaceHex(reference, palette, underlayReference) {
2446
2355
  const parts = parseColorTokenParts(reference);
2447
2356
  if (parts.kind === "token" && parts.opacityPercent !== null) {
2448
2357
  const foregroundHex = normalizePaletteHex(palette[parts.token] ?? "");
2449
- return foregroundHex ? compositeHex(foregroundHex, underlayHex, parts.opacityPercent / 100) : null;
2358
+ return foregroundHex ? compositeHex$1(foregroundHex, underlayHex, parts.opacityPercent / 100) : null;
2450
2359
  }
2451
2360
  const raw = parts.kind === "raw" ? parts.value : reference;
2452
2361
  const rgba = parseRgba(raw);
2453
- return rgba ? compositeHex(rgbToHex(rgba.r, rgba.g, rgba.b), underlayHex, rgba.alpha) : null;
2362
+ return rgba ? compositeHex$1(rgbToHex(rgba.r, rgba.g, rgba.b), underlayHex, rgba.alpha) : null;
2454
2363
  }
2455
2364
  /** Resolve a colour reference only when it is already fully opaque. */
2456
2365
  function resolveConcreteColorHex(reference, palette) {
@@ -2466,7 +2375,7 @@ function resolveConcreteColorHex(reference, palette) {
2466
2375
  if (literal.toLowerCase() === "white") return "#ffffff";
2467
2376
  return normalizePaletteHex(literal);
2468
2377
  }
2469
- function compositeHex(foregroundHex, underlayHex, alpha) {
2378
+ function compositeHex$1(foregroundHex, underlayHex, alpha) {
2470
2379
  const foreground = hexToRgb(foregroundHex);
2471
2380
  const underlay = hexToRgb(underlayHex);
2472
2381
  const channel = (foregroundValue, underlayValue) => Math.round(foregroundValue * alpha + underlayValue * (1 - alpha));
@@ -2500,6 +2409,134 @@ function isRgbChannel(value) {
2500
2409
  return Number.isFinite(value) && value >= 0 && value <= 255;
2501
2410
  }
2502
2411
 
2412
+ //#endregion
2413
+ //#region ../theme-core/src/surfaces/foregroundInteraction.ts
2414
+ /**
2415
+ * How far a hover state softens away from its resting foreground, as a
2416
+ * percentage of full opacity.
2417
+ *
2418
+ * Softening — rather than brightening — is forced by the token vocabulary
2419
+ * rather than chosen for taste. A resolved foreground is frequently an
2420
+ * `on*` token, which `expandPalette` emits as pure `#FFFFFF` or `#000000`
2421
+ * with no shade ramp: over a primary-painted section the resolved text role
2422
+ * is `onPrimary`, and white has no headroom left to move further from its
2423
+ * background. Moving *toward* the mid-range always has headroom, whatever
2424
+ * the resting colour, and it keeps the hue exactly.
2425
+ *
2426
+ * 85% is the measured floor, not a round number. Softening costs contrast, and
2427
+ * the binding case is a scrolled overlay header: `contrast-glass` paints 60%
2428
+ * black or white, which composites the backdrop into a known band whatever the
2429
+ * page content underneath happens to be. The lightest backdrop a dark glass can
2430
+ * present is 60% black over white, `#666666`; a white foreground softened to
2431
+ * 85% reads 4.69:1 there, against 4.07:1 at 75%, which fails. Nav links are
2432
+ * 18px regular (`textSize: 'lg'`), so the bar is WCAG AA's 4.5:1 rather than
2433
+ * the large-text 3:1.
2434
+ *
2435
+ * The mirror case — a light glass over dark content, `#999999` — cannot be met
2436
+ * by any softening, because the *resting* colour is already only about 4.6:1
2437
+ * there. That is a property of the glass rather than of this derivation, and
2438
+ * it wants fixing where the glass tone is chosen.
2439
+ */
2440
+ const FOREGROUND_HOVER_OPACITY_PERCENT = 85;
2441
+ /**
2442
+ * Derive the hover foreground that belongs with a resolved resting foreground.
2443
+ *
2444
+ * A resting foreground is chosen to be legible against a known surface. A hover
2445
+ * state derived from it stays within a bounded distance of that colour, so it
2446
+ * inherits most of the resting colour's contrast margin instead of asserting
2447
+ * an unrelated brand colour that no one checked against the surface.
2448
+ *
2449
+ * That bounded-distance property is what lets this be used on a transparent
2450
+ * overlay header, where the backdrop is only partly knowable: the scrolled
2451
+ * state composites a 60% glass tint over arbitrary page content, so no
2452
+ * compile-time contrast proof is available there. Staying near a colour that
2453
+ * is already trusted in both states is a weaker claim than a contrast proof,
2454
+ * and it is one this function can actually keep.
2455
+ *
2456
+ * Returns `null` when the resting token is not a plain palette token — an
2457
+ * already-faded or raw value has no well-defined softening, and the caller
2458
+ * should leave hover alone rather than guess.
2459
+ */
2460
+ function deriveForegroundHoverToken(restingToken) {
2461
+ const parts = parseColorTokenParts(restingToken);
2462
+ if (parts.kind !== "token") return null;
2463
+ if (parts.opacityPercent !== null) return null;
2464
+ return parseThemeColorToken(`${parts.token}/${FOREGROUND_HOVER_OPACITY_PERCENT}`);
2465
+ }
2466
+ /**
2467
+ * The opacity `contrast-glass` paints at. Mirrors the value in
2468
+ * `generateHeaderScrollTreatmentStyles`; the two must agree, because the whole
2469
+ * point of the bound below is that it describes what that treatment can produce.
2470
+ */
2471
+ const CONTRAST_GLASS_ALPHA = .6;
2472
+ /**
2473
+ * The extreme backdrop a glass tone can present, whatever page content happens
2474
+ * to be underneath it.
2475
+ *
2476
+ * A 60% tint clamps the composite into a known band, which is what makes a
2477
+ * conservative check possible at compile time even though the content passing
2478
+ * under a sticky header is not knowable. For a dark glass the composite is
2479
+ * lightest over white; for a light glass it is darkest over black. Those are
2480
+ * the two ends that a foreground has to survive.
2481
+ */
2482
+ function worstCaseGlassBackdrop(tone) {
2483
+ const [over, tint] = tone === "dark" ? ["#ffffff", "#000000"] : ["#000000", "#ffffff"];
2484
+ return compositeHex(tint, over, CONTRAST_GLASS_ALPHA);
2485
+ }
2486
+ /** Flatten `foreground` at `alpha` over `background` into an opaque hex. */
2487
+ function compositeHex(foreground, background, alpha) {
2488
+ const f = hexToRgb(foreground);
2489
+ const b = hexToRgb(background);
2490
+ return rgbToHex(Math.round(mix$2(b.r, f.r, alpha)), Math.round(mix$2(b.g, f.g, alpha)), Math.round(mix$2(b.b, f.b, alpha)));
2491
+ }
2492
+ /**
2493
+ * Prefer an authored hover that survives every measured backdrop, then a
2494
+ * checked softening of the resting colour. A null backdrop set represents a
2495
+ * trusted treatment: only an explicit authored hover may change the colour.
2496
+ * When neither option works, retain the resting colour and underline it.
2497
+ */
2498
+ function planForegroundHover(input) {
2499
+ const { restingToken, candidate: candidate$1, palette, backdrops } = input;
2500
+ if (candidate$1 && candidate$1 !== restingToken) {
2501
+ if (backdrops === null || survivesEveryBackdrop(candidate$1, palette, backdrops)) {
2502
+ return {
2503
+ kind: "candidate",
2504
+ token: candidate$1,
2505
+ decoration: "initial"
2506
+ };
2507
+ }
2508
+ }
2509
+ const softened = deriveForegroundHoverToken(restingToken);
2510
+ if (softened && backdrops && survivesEveryBackdrop(softened, palette, backdrops)) {
2511
+ return {
2512
+ kind: "softened",
2513
+ token: softened,
2514
+ decoration: "initial"
2515
+ };
2516
+ }
2517
+ return {
2518
+ kind: "resting",
2519
+ token: restingToken,
2520
+ decoration: "underline"
2521
+ };
2522
+ }
2523
+ function survivesEveryBackdrop(token, palette, backdrops) {
2524
+ return backdrops.length > 0 && backdrops.every((backdrop) => {
2525
+ const foreground = resolveOpaqueSurfaceHex(token, palette, backdrop);
2526
+ return foreground !== null && contrastRatio(foreground, backdrop) >= ORDINARY_TEXT_MIN_CONTRAST_RATIO;
2527
+ });
2528
+ }
2529
+
2530
+ //#endregion
2531
+ //#region ../theme-core/src/surfaces/foregroundRoles.ts
2532
+ const SURFACE_FOREGROUND_ROLES = [
2533
+ "text",
2534
+ "heading",
2535
+ "link",
2536
+ "linkHover",
2537
+ "supporting"
2538
+ ];
2539
+
2503
2540
  //#endregion
2504
2541
  //#region ../theme-core/src/surfaces/resolveSurfaceForeground.ts
2505
2542
  const FALLBACK_TEXT_TOKENS = [
@@ -2550,12 +2587,16 @@ function resolveSurfaceForeground(background, palette, candidates = {}, options
2550
2587
  }
2551
2588
  function foregroundCandidates(background, overrides) {
2552
2589
  return {
2553
- text: overrides.text ?? background?.textColor ?? null,
2554
- heading: overrides.heading ?? background?.headingColor ?? null,
2555
- link: overrides.link ?? background?.linkColor ?? null,
2556
- supporting: overrides.supporting ?? background?.supportingColor ?? null
2590
+ text: candidateOrBackground(overrides.text, background?.textColor),
2591
+ heading: candidateOrBackground(overrides.heading, background?.headingColor),
2592
+ link: candidateOrBackground(overrides.link, background?.linkColor),
2593
+ linkHover: candidateOrBackground(overrides.linkHover),
2594
+ supporting: candidateOrBackground(overrides.supporting, background?.supportingColor)
2557
2595
  };
2558
2596
  }
2597
+ function candidateOrBackground(candidate$1, backgroundValue) {
2598
+ return candidate$1 ?? backgroundValue ?? null;
2599
+ }
2559
2600
  function resolveDeterministicForeground(backgroundReference, candidates, palette, compositeBase) {
2560
2601
  const backgroundHex = resolveOpaqueSurfaceHex(backgroundReference, palette, compositeBase);
2561
2602
  if (!backgroundHex) {
@@ -2569,29 +2610,53 @@ function resolveDeterministicForeground(backgroundReference, candidates, palette
2569
2610
  ...FALLBACK_TEXT_TOKENS
2570
2611
  ], backgroundHex, palette, derivedOnToken ? [derivedOnToken] : []);
2571
2612
  if (!text) return { kind: "unknown" };
2572
- return {
2573
- kind: "known",
2574
- roles: {
2575
- text,
2576
- heading: safeTokenOr(candidates.heading, text, backgroundHex, palette),
2577
- link: safeTokenOr(candidates.link, text, backgroundHex, palette),
2578
- supporting: safeTokenOr(candidates.supporting, text, backgroundHex, palette)
2579
- }
2580
- };
2613
+ return knownForeground({
2614
+ text,
2615
+ heading: safeTokenOr(candidates.heading, text, backgroundHex, palette),
2616
+ link: safeTokenOr(candidates.link, text, backgroundHex, palette),
2617
+ supporting: safeTokenOr(candidates.supporting, text, backgroundHex, palette)
2618
+ }, {
2619
+ kind: "measured",
2620
+ backdropHex: backgroundHex
2621
+ }, candidates.linkHover, palette);
2581
2622
  }
2582
2623
  function resolveTrustedTreatmentForeground(candidates, palette) {
2583
2624
  const text = canonicalToken(candidates.text, palette);
2584
2625
  if (!text) return { kind: "unknown" };
2626
+ return knownForeground({
2627
+ text,
2628
+ heading: canonicalToken(candidates.heading, palette) ?? text,
2629
+ link: canonicalToken(candidates.link, palette) ?? text,
2630
+ supporting: canonicalToken(candidates.supporting, palette) ?? text
2631
+ }, { kind: "trusted-treatment" }, candidates.linkHover, palette);
2632
+ }
2633
+ function knownForeground(roles, provenance, hoverCandidate, palette) {
2634
+ const hover = planForegroundHover({
2635
+ restingToken: roles.link,
2636
+ candidate: canonicalHoverToken(hoverCandidate, palette),
2637
+ palette,
2638
+ backdrops: provenance.kind === "measured" ? [provenance.backdropHex] : null
2639
+ });
2585
2640
  return {
2586
2641
  kind: "known",
2642
+ provenance,
2587
2643
  roles: {
2588
- text,
2589
- heading: canonicalToken(candidates.heading, palette) ?? text,
2590
- link: canonicalToken(candidates.link, palette) ?? text,
2591
- supporting: canonicalToken(candidates.supporting, palette) ?? text
2592
- }
2644
+ ...roles,
2645
+ linkHover: hover.token
2646
+ },
2647
+ hoverDecoration: hover.decoration
2593
2648
  };
2594
2649
  }
2650
+ function canonicalHoverToken(reference, palette) {
2651
+ const token = parseThemeColorToken(reference);
2652
+ if (token) {
2653
+ const parts = parseColorTokenParts(token);
2654
+ if (parts.kind === "token" && resolveConcreteColorHex(parts.token, palette)) {
2655
+ return token;
2656
+ }
2657
+ }
2658
+ return canonicalToken(reference, palette);
2659
+ }
2595
2660
  function safeTokenOr(candidate$1, fallback, backgroundHex, palette) {
2596
2661
  return firstSafeToken([candidate$1], backgroundHex, palette) ?? fallback;
2597
2662
  }
@@ -3591,6 +3656,226 @@ function resolveCardRole(theme, cardSystem, role) {
3591
3656
  }
3592
3657
  }
3593
3658
 
3659
+ //#endregion
3660
+ //#region ../theme-core/src/palette/generateShades.ts
3661
+ function generateShades(hexColor, name) {
3662
+ const shadeSteps = [
3663
+ 50,
3664
+ 100,
3665
+ 200,
3666
+ 300,
3667
+ 400,
3668
+ 500,
3669
+ 600,
3670
+ 700,
3671
+ 800,
3672
+ 900,
3673
+ 950
3674
+ ];
3675
+ const base = featuresFromHex(hexColor);
3676
+ const hue = base.okH;
3677
+ const baseChroma = base.okC;
3678
+ const toneMap = {
3679
+ 50: .975,
3680
+ 100: .945,
3681
+ 200: .89,
3682
+ 300: .82,
3683
+ 400: .74,
3684
+ 500: .66,
3685
+ 600: .58,
3686
+ 700: .49,
3687
+ 800: .4,
3688
+ 900: .28,
3689
+ 950: .16
3690
+ };
3691
+ const shadeDefs = shadeSteps.map((step, idx) => {
3692
+ const t = idx / (shadeSteps.length - 1);
3693
+ const L = toneMap[step];
3694
+ const chromaFactor = .06 + .94 * Math.sin(Math.PI * t);
3695
+ const C = baseChroma * chromaFactor;
3696
+ return {
3697
+ name: `${name}-${step}`,
3698
+ hex: oklchToHexGamut(L, C, hue)
3699
+ };
3700
+ });
3701
+ shadeDefs.push({
3702
+ name,
3703
+ hex: hexColor
3704
+ });
3705
+ return shadeDefs;
3706
+ }
3707
+
3708
+ //#endregion
3709
+ //#region ../theme-core/src/palette/neutralScale.ts
3710
+ function generateNeutralScale({ background, steps = 11, warm = false }) {
3711
+ const isDark = background === "#000000" || background.startsWith("#0") || background.startsWith("#1");
3712
+ const shadeNames = [
3713
+ 50,
3714
+ 100,
3715
+ 200,
3716
+ 300,
3717
+ 400,
3718
+ 500,
3719
+ 600,
3720
+ 700,
3721
+ 800,
3722
+ 900,
3723
+ 950
3724
+ ];
3725
+ const count = Math.min(steps, shadeNames.length);
3726
+ const scale$1 = [];
3727
+ for (let i = 0; i < count; i++) {
3728
+ const shadeName = shadeNames[i] ?? shadeNames[shadeNames.length - 1] ?? 950;
3729
+ const t = i / (count - 1);
3730
+ const tone = isDark ? mix$1(.06, .92, t) : mix$1(.98, .1, t);
3731
+ const hex = oklchToHexGamut(tone, .02, warm ? 85 : 250);
3732
+ const name = `neutral-${shadeName}`;
3733
+ scale$1.push({
3734
+ name,
3735
+ hex
3736
+ });
3737
+ }
3738
+ return scale$1;
3739
+ }
3740
+ function mix$1(a, b, t) {
3741
+ return a + (b - a) * Math.max(0, Math.min(1, t));
3742
+ }
3743
+
3744
+ //#endregion
3745
+ //#region ../theme-core/src/palette/utils/textColor.ts
3746
+ /**
3747
+ * Pick the better of black/white text for a given background using actual
3748
+ * contrast ratios, not a simple luminance cutoff.
3749
+ *
3750
+ * @param background - Background color in hex or rgb/rgba format (e.g., '#FFFFFF' or 'rgb(255, 255, 255)')
3751
+ * @param minContrast - Preferred minimum contrast ratio for body-sized text
3752
+ * @returns '#000000' for light backgrounds, '#FFFFFF' for dark backgrounds
3753
+ */
3754
+ function pickBlackOrWhiteTextColor(background, minContrast = 4.5) {
3755
+ const { r, g, b } = parseColorToRgb(background);
3756
+ const luminance = relativeLuminance(r, g, b);
3757
+ const blackContrast = (luminance + .05) / .05;
3758
+ const whiteContrast = 1.05 / (luminance + .05);
3759
+ if (blackContrast >= minContrast && whiteContrast < minContrast) {
3760
+ return "#000000";
3761
+ }
3762
+ if (whiteContrast >= minContrast && blackContrast < minContrast) {
3763
+ return "#FFFFFF";
3764
+ }
3765
+ return blackContrast >= whiteContrast ? "#000000" : "#FFFFFF";
3766
+ }
3767
+ /**
3768
+ * Backwards-compatible alias for callers that want the best black/white text
3769
+ * color for a background.
3770
+ */
3771
+ function getTextColorForBackground(background) {
3772
+ return pickBlackOrWhiteTextColor(background);
3773
+ }
3774
+ function parseColorToRgb(input) {
3775
+ const trimmed = input.trim();
3776
+ if (trimmed.startsWith("#")) {
3777
+ return hexToRgb(trimmed);
3778
+ }
3779
+ const rgbMatch = trimmed.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i);
3780
+ if (rgbMatch?.[1] && rgbMatch[2] && rgbMatch[3]) {
3781
+ return {
3782
+ r: clamp255(Number(rgbMatch[1])),
3783
+ g: clamp255(Number(rgbMatch[2])),
3784
+ b: clamp255(Number(rgbMatch[3]))
3785
+ };
3786
+ }
3787
+ throw new Error("Unsupported color format");
3788
+ }
3789
+ function clamp255(value) {
3790
+ if (Number.isNaN(value)) return 0;
3791
+ return Math.max(0, Math.min(255, value));
3792
+ }
3793
+
3794
+ //#endregion
3795
+ //#region ../theme-core/src/palette/expandPalette.ts
3796
+ function colorsToMap$1(palette) {
3797
+ const map = {};
3798
+ for (const c of palette.colors) {
3799
+ map[c.name] = c.hex;
3800
+ }
3801
+ return map;
3802
+ }
3803
+ function isBackgroundDark(hex) {
3804
+ try {
3805
+ return featuresFromHex(hex).Y < .5;
3806
+ } catch {
3807
+ return false;
3808
+ }
3809
+ }
3810
+ const nonShadeablePaletteRoleNames = new Set([
3811
+ "success",
3812
+ "warning",
3813
+ "destructive"
3814
+ ]);
3815
+ /**
3816
+ * Status role aliases the persisted palette does not carry itself.
3817
+ *
3818
+ * Generators reference `--tb-error` (the site-style-owned input error border)
3819
+ * and `--tb-info` (`.alert-info`, `.status-info`, the field-note rule), but the
3820
+ * palette schema only defines `destructive` and has no `info` role at all.
3821
+ * Without these aliases those declarations name variables that are never
3822
+ * defined and browsers drop them. The SDK bridge resolves the same pair via
3823
+ * TOKEN_ALIASES, so both runtimes agree on what a status token means.
3824
+ *
3825
+ * Runs after the danger → destructive promotion above, so `destructive` is
3826
+ * already the single source of truth for the error colour.
3827
+ */
3828
+ function statusAliasTokens(expanded) {
3829
+ const aliases = {};
3830
+ if (expanded.destructive) aliases.error = expanded.destructive;
3831
+ if (expanded.accent) aliases.info = expanded.accent;
3832
+ return aliases;
3833
+ }
3834
+ function expandPalette(palette) {
3835
+ const base = colorsToMap$1(palette);
3836
+ const expanded = { ...base };
3837
+ const background = base.background || base.surface || "#ffffff";
3838
+ const neutral$1 = generateNeutralScale({
3839
+ background,
3840
+ steps: 11,
3841
+ warm: false
3842
+ });
3843
+ neutral$1.forEach((n) => {
3844
+ expanded[n.name] = n.hex;
3845
+ });
3846
+ for (const [name, hex] of Object.entries(base)) {
3847
+ if (nonShadeablePaletteRoleNames.has(name)) continue;
3848
+ if (hex) {
3849
+ const shades = generateShades(hex, name);
3850
+ shades.forEach((shade) => {
3851
+ expanded[shade.name] = shade.hex;
3852
+ });
3853
+ }
3854
+ }
3855
+ if (base.text) expanded.body = base.text;
3856
+ if (base.danger && !base.destructive) {
3857
+ expanded.destructive = base.danger;
3858
+ }
3859
+ Object.assign(expanded, statusAliasTokens(expanded));
3860
+ const isDark = base.background ? isBackgroundDark(base.background) : false;
3861
+ const isDarkMode = palette.meta?.mode === "dark" || isDark;
3862
+ expanded.mutedText = isDarkMode ? expanded["neutral-600"] ?? "#666" : expanded["neutral-500"] ?? "#888";
3863
+ for (const [token, hex] of Object.entries({ ...expanded })) {
3864
+ if (!hex || !isOnColorBackgroundCandidateToken(token)) continue;
3865
+ const onToken = onColorTokenName(token);
3866
+ if (expanded[onToken]) continue;
3867
+ expanded[onToken] = getTextColorForBackground(hex);
3868
+ }
3869
+ return expanded;
3870
+ }
3871
+
3872
+ //#endregion
3873
+ //#region ../theme-core/src/utils/cssDeclarations.ts
3874
+ /** Render CSS declarations whether callers provide terminators or fragments. */
3875
+ function renderCssDeclarations(declarations) {
3876
+ return declarations.map((declaration) => declaration.endsWith(";") ? declaration : `${declaration};`).join("\n ");
3877
+ }
3878
+
3594
3879
  //#endregion
3595
3880
  //#region ../theme-core/src/cards/generateCardCss.ts
3596
3881
  const PER_CORNER_RADIUS_MAP$1 = {
@@ -3608,6 +3893,7 @@ function resolveCornerRadiiCss$1(cornerRadii, fallback) {
3608
3893
  function generateCardCss(options) {
3609
3894
  const { themeId, theme, cardSystem: providedCardSystem } = options;
3610
3895
  const tokens = new TokenResolver(theme);
3896
+ const palette = expandPalette(theme.palette);
3611
3897
  const cardSystem = providedCardSystem ?? generateDefaultCardSystem(theme);
3612
3898
  const source = providedCardSystem ? "site-style" : "generated-baseline";
3613
3899
  const aliasGroups = resolveRoleAliasGroups(theme, providedCardSystem ?? null);
@@ -3618,11 +3904,11 @@ function generateCardCss(options) {
3618
3904
  const groupKey = roleAliasGroupKey(source, variant.id);
3619
3905
  emittedGroupKeys.add(groupKey);
3620
3906
  const aliasClasses = aliasGroups.get(groupKey)?.classes ?? [];
3621
- cssChunks.push(generateVariantCSS(variant, themeId, [`card-${variant.id}`, ...aliasClasses], tokens, theme));
3907
+ cssChunks.push(generateVariantCSS(variant, themeId, [`card-${variant.id}`, ...aliasClasses], tokens, theme, palette));
3622
3908
  }
3623
3909
  for (const [groupKey, group] of aliasGroups) {
3624
3910
  if (emittedGroupKeys.has(groupKey)) continue;
3625
- cssChunks.push(generateVariantCSS(group.variant, themeId, group.classes, tokens, theme));
3911
+ cssChunks.push(generateVariantCSS(group.variant, themeId, group.classes, tokens, theme, palette));
3626
3912
  }
3627
3913
  return cssChunks.filter(Boolean).join("\n\n");
3628
3914
  }
@@ -3685,7 +3971,7 @@ const MEDIA_CORNER_MAP = {
3685
3971
  * This CSS-first approach eliminates runtime transforms for styling,
3686
3972
  * keeping transforms only for data normalization and dynamic layout.
3687
3973
  */
3688
- function generateVariantCSS(variant, themeId, cardClasses, tokens, theme) {
3974
+ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme, palette) {
3689
3975
  const selectorsFor = (suffix) => cardClasses.map((cardClass) => `:where([data-theme-scope="${themeId}"]) .${cardClass}.${cardClass}${suffix}`).join(",\n");
3690
3976
  const containerSelectors = cardClasses.map((cardClass) => `:where([data-theme-scope="${themeId}"]) .${cardClass}.${cardClass}`);
3691
3977
  const containerSelector = containerSelectors.join(",\n");
@@ -3693,21 +3979,7 @@ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme) {
3693
3979
  const contentSelector = selectorsFor(" .card-content");
3694
3980
  const fallbackBackgroundSelector = selectorsFor(" > .rb-bg-surface");
3695
3981
  const rules = [];
3696
- if (variant.background.type === "solid") {
3697
- const bgColor = tokens.getColor(variant.background.colorToken);
3698
- rules.push(`background: ${bgColor}`);
3699
- } else if (variant.background.type === "gradient") {
3700
- const fromColor = tokens.getColor(variant.background.fromToken);
3701
- const toColor = tokens.getColor(variant.background.toToken);
3702
- const direction = GRADIENT_DIRECTION_MAP$2[variant.background.direction] || "to right";
3703
- rules.push(`background: linear-gradient(${direction}, ${fromColor}, ${toColor})`);
3704
- } else {
3705
- rules.push(`background: transparent`);
3706
- }
3707
- if (variant.background.type !== "transparent") {
3708
- const foreground = tokens.getColor(variant.foregroundToken ?? "text");
3709
- rules.push(...surfaceForegroundDeclarations({ text: foreground }));
3710
- }
3982
+ rules.push(...cardSurfaceRules(variant, tokens, theme, palette));
3711
3983
  const resolveColor = (token) => tokens.getColor(token);
3712
3984
  rules.push(...cardBorderDeclarations(variant.border ? borderDeclaration(variant.border, tokens) : null, variant.decorations, resolveColor));
3713
3985
  const corners$1 = variant.corners ?? theme.corners;
@@ -3724,13 +3996,35 @@ function generateVariantCSS(variant, themeId, cardClasses, tokens, theme) {
3724
3996
  const contentRules = [`padding: ${resolveContentPadding(variant)}`];
3725
3997
  return [
3726
3998
  `${structureSelector} {\n display: flex;\n flex-direction: column;\n position: relative;\n overflow: visible;\n}`,
3727
- `${containerSelector} {\n ${rules.join(";\n ")};\n}`,
3999
+ `${containerSelector} {\n ${renderCssDeclarations(rules)}\n}`,
3728
4000
  ...cardDecorationPseudoRules(containerSelectors, variant.decorations, resolveColor),
3729
4001
  `${fallbackBackgroundSelector} {\n background: transparent;\n}`,
3730
4002
  `${mediaSelector} {\n ${mediaRules.join(";\n ")};\n}`,
3731
4003
  `${contentSelector} {\n ${contentRules.join(";\n ")};\n}`
3732
4004
  ].join("\n\n");
3733
4005
  }
4006
+ function cardSurfaceRules(variant, tokens, theme, palette) {
4007
+ if (variant.background.type === "transparent") return ["background: transparent"];
4008
+ const background = variant.background.type === "solid" ? {
4009
+ type: "color",
4010
+ color: variant.background.colorToken,
4011
+ textColor: variant.foregroundToken ?? "text"
4012
+ } : {
4013
+ type: "gradient",
4014
+ textColor: variant.foregroundToken ?? "text"
4015
+ };
4016
+ const foreground = resolveSurfaceForeground(background, palette, { linkHover: theme.typography.proseLinkStyle?.hoverColorToken });
4017
+ return [cardBackgroundRule(variant.background, tokens), ...surfaceForegroundDeclarations(foreground.kind === "known" ? surfaceForegroundInputFromResolved(foreground, (token) => tokens.getColor(token)) : { text: tokens.getColor(variant.foregroundToken ?? "text") })];
4018
+ }
4019
+ function cardBackgroundRule(background, tokens) {
4020
+ if (background.type === "solid") {
4021
+ return `background: ${tokens.getColor(background.colorToken)}`;
4022
+ }
4023
+ const from = tokens.getColor(background.fromToken);
4024
+ const to = tokens.getColor(background.toToken);
4025
+ const direction = GRADIENT_DIRECTION_MAP$2[background.direction] || "to right";
4026
+ return `background: linear-gradient(${direction}, ${from}, ${to})`;
4027
+ }
3734
4028
  function borderDeclaration(border$1, tokens) {
3735
4029
  const borderColor = tokens.getColor(border$1.colorToken);
3736
4030
  const borderWidth = border$1.widthClass === "border-4" ? "4px" : border$1.widthClass === "border-2" ? "2px" : border$1.widthClass === "border" ? "1px" : "1px";
@@ -3848,6 +4142,9 @@ function generateCardCoreCss(scopeId, variants, settings, scope = "all") {
3848
4142
  * A variant's background and, when it owns one, the complete foreground
3849
4143
  * contract that goes with it. Opaque cards are surface providers; transparent
3850
4144
  * variants inherit the nearest outer surface atomically and declare nothing.
4145
+ * The SDK bridge has token references but no resolved palette or global prose
4146
+ * preference. It therefore publishes the conservative resting hover plus
4147
+ * underline through the shared contract instead of claiming measured contrast.
3851
4148
  */
3852
4149
  function cardSurfaceDeclarations(variant) {
3853
4150
  if (variant.background === "solid" && variant.backgroundToken) {
@@ -3888,7 +4185,7 @@ function generateVariantCss(scopeId, variant, settings, scope = "all") {
3888
4185
  const mediaRules = mediaCornerRules(variant.cornerRadii, settings.corners);
3889
4186
  const contentRules = [`padding: ${SPACING_MAP[variant.spacing]}`];
3890
4187
  return [
3891
- `${containerSelector} {\n ${containerRules.join(";\n ")};\n}`,
4188
+ `${containerSelector} {\n ${renderCssDeclarations(containerRules)}\n}`,
3892
4189
  ...cardDecorationPseudoRules(containerSelectors, decorations, coreColor),
3893
4190
  `${fallbackBackgroundSelector} {\n background: transparent;\n}`,
3894
4191
  `${mediaSelector} {\n ${mediaRules.join(";\n ")};\n}`,
@@ -12789,219 +13086,6 @@ const paletteSchema = z.object({
12789
13086
  };
12790
13087
  });
12791
13088
 
12792
- //#endregion
12793
- //#region ../theme-core/src/palette/generateShades.ts
12794
- function generateShades(hexColor, name) {
12795
- const shadeSteps = [
12796
- 50,
12797
- 100,
12798
- 200,
12799
- 300,
12800
- 400,
12801
- 500,
12802
- 600,
12803
- 700,
12804
- 800,
12805
- 900,
12806
- 950
12807
- ];
12808
- const base = featuresFromHex(hexColor);
12809
- const hue = base.okH;
12810
- const baseChroma = base.okC;
12811
- const toneMap = {
12812
- 50: .975,
12813
- 100: .945,
12814
- 200: .89,
12815
- 300: .82,
12816
- 400: .74,
12817
- 500: .66,
12818
- 600: .58,
12819
- 700: .49,
12820
- 800: .4,
12821
- 900: .28,
12822
- 950: .16
12823
- };
12824
- const shadeDefs = shadeSteps.map((step, idx) => {
12825
- const t = idx / (shadeSteps.length - 1);
12826
- const L = toneMap[step];
12827
- const chromaFactor = .06 + .94 * Math.sin(Math.PI * t);
12828
- const C = baseChroma * chromaFactor;
12829
- return {
12830
- name: `${name}-${step}`,
12831
- hex: oklchToHexGamut(L, C, hue)
12832
- };
12833
- });
12834
- shadeDefs.push({
12835
- name,
12836
- hex: hexColor
12837
- });
12838
- return shadeDefs;
12839
- }
12840
-
12841
- //#endregion
12842
- //#region ../theme-core/src/palette/neutralScale.ts
12843
- function generateNeutralScale({ background, steps = 11, warm = false }) {
12844
- const isDark = background === "#000000" || background.startsWith("#0") || background.startsWith("#1");
12845
- const shadeNames = [
12846
- 50,
12847
- 100,
12848
- 200,
12849
- 300,
12850
- 400,
12851
- 500,
12852
- 600,
12853
- 700,
12854
- 800,
12855
- 900,
12856
- 950
12857
- ];
12858
- const count = Math.min(steps, shadeNames.length);
12859
- const scale$1 = [];
12860
- for (let i = 0; i < count; i++) {
12861
- const shadeName = shadeNames[i] ?? shadeNames[shadeNames.length - 1] ?? 950;
12862
- const t = i / (count - 1);
12863
- const tone = isDark ? mix$1(.06, .92, t) : mix$1(.98, .1, t);
12864
- const hex = oklchToHexGamut(tone, .02, warm ? 85 : 250);
12865
- const name = `neutral-${shadeName}`;
12866
- scale$1.push({
12867
- name,
12868
- hex
12869
- });
12870
- }
12871
- return scale$1;
12872
- }
12873
- function mix$1(a, b, t) {
12874
- return a + (b - a) * Math.max(0, Math.min(1, t));
12875
- }
12876
-
12877
- //#endregion
12878
- //#region ../theme-core/src/palette/utils/textColor.ts
12879
- /**
12880
- * Pick the better of black/white text for a given background using actual
12881
- * contrast ratios, not a simple luminance cutoff.
12882
- *
12883
- * @param background - Background color in hex or rgb/rgba format (e.g., '#FFFFFF' or 'rgb(255, 255, 255)')
12884
- * @param minContrast - Preferred minimum contrast ratio for body-sized text
12885
- * @returns '#000000' for light backgrounds, '#FFFFFF' for dark backgrounds
12886
- */
12887
- function pickBlackOrWhiteTextColor(background, minContrast = 4.5) {
12888
- const { r, g, b } = parseColorToRgb(background);
12889
- const luminance = relativeLuminance(r, g, b);
12890
- const blackContrast = (luminance + .05) / .05;
12891
- const whiteContrast = 1.05 / (luminance + .05);
12892
- if (blackContrast >= minContrast && whiteContrast < minContrast) {
12893
- return "#000000";
12894
- }
12895
- if (whiteContrast >= minContrast && blackContrast < minContrast) {
12896
- return "#FFFFFF";
12897
- }
12898
- return blackContrast >= whiteContrast ? "#000000" : "#FFFFFF";
12899
- }
12900
- /**
12901
- * Backwards-compatible alias for callers that want the best black/white text
12902
- * color for a background.
12903
- */
12904
- function getTextColorForBackground(background) {
12905
- return pickBlackOrWhiteTextColor(background);
12906
- }
12907
- function parseColorToRgb(input) {
12908
- const trimmed = input.trim();
12909
- if (trimmed.startsWith("#")) {
12910
- return hexToRgb(trimmed);
12911
- }
12912
- const rgbMatch = trimmed.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i);
12913
- if (rgbMatch?.[1] && rgbMatch[2] && rgbMatch[3]) {
12914
- return {
12915
- r: clamp255(Number(rgbMatch[1])),
12916
- g: clamp255(Number(rgbMatch[2])),
12917
- b: clamp255(Number(rgbMatch[3]))
12918
- };
12919
- }
12920
- throw new Error("Unsupported color format");
12921
- }
12922
- function clamp255(value) {
12923
- if (Number.isNaN(value)) return 0;
12924
- return Math.max(0, Math.min(255, value));
12925
- }
12926
-
12927
- //#endregion
12928
- //#region ../theme-core/src/palette/expandPalette.ts
12929
- function colorsToMap$1(palette) {
12930
- const map = {};
12931
- for (const c of palette.colors) {
12932
- map[c.name] = c.hex;
12933
- }
12934
- return map;
12935
- }
12936
- function isBackgroundDark(hex) {
12937
- try {
12938
- return featuresFromHex(hex).Y < .5;
12939
- } catch {
12940
- return false;
12941
- }
12942
- }
12943
- const nonShadeablePaletteRoleNames = new Set([
12944
- "success",
12945
- "warning",
12946
- "destructive"
12947
- ]);
12948
- /**
12949
- * Status role aliases the persisted palette does not carry itself.
12950
- *
12951
- * Generators reference `--tb-error` (the site-style-owned input error border)
12952
- * and `--tb-info` (`.alert-info`, `.status-info`, the field-note rule), but the
12953
- * palette schema only defines `destructive` and has no `info` role at all.
12954
- * Without these aliases those declarations name variables that are never
12955
- * defined and browsers drop them. The SDK bridge resolves the same pair via
12956
- * TOKEN_ALIASES, so both runtimes agree on what a status token means.
12957
- *
12958
- * Runs after the danger → destructive promotion above, so `destructive` is
12959
- * already the single source of truth for the error colour.
12960
- */
12961
- function statusAliasTokens(expanded) {
12962
- const aliases = {};
12963
- if (expanded.destructive) aliases.error = expanded.destructive;
12964
- if (expanded.accent) aliases.info = expanded.accent;
12965
- return aliases;
12966
- }
12967
- function expandPalette(palette) {
12968
- const base = colorsToMap$1(palette);
12969
- const expanded = { ...base };
12970
- const background = base.background || base.surface || "#ffffff";
12971
- const neutral$1 = generateNeutralScale({
12972
- background,
12973
- steps: 11,
12974
- warm: false
12975
- });
12976
- neutral$1.forEach((n) => {
12977
- expanded[n.name] = n.hex;
12978
- });
12979
- for (const [name, hex] of Object.entries(base)) {
12980
- if (nonShadeablePaletteRoleNames.has(name)) continue;
12981
- if (hex) {
12982
- const shades = generateShades(hex, name);
12983
- shades.forEach((shade) => {
12984
- expanded[shade.name] = shade.hex;
12985
- });
12986
- }
12987
- }
12988
- if (base.text) expanded.body = base.text;
12989
- if (base.danger && !base.destructive) {
12990
- expanded.destructive = base.danger;
12991
- }
12992
- Object.assign(expanded, statusAliasTokens(expanded));
12993
- const isDark = base.background ? isBackgroundDark(base.background) : false;
12994
- const isDarkMode = palette.meta?.mode === "dark" || isDark;
12995
- expanded.mutedText = isDarkMode ? expanded["neutral-600"] ?? "#666" : expanded["neutral-500"] ?? "#888";
12996
- for (const [token, hex] of Object.entries({ ...expanded })) {
12997
- if (!hex || !isOnColorBackgroundCandidateToken(token)) continue;
12998
- const onToken = onColorTokenName(token);
12999
- if (expanded[onToken]) continue;
13000
- expanded[onToken] = getTextColorForBackground(hex);
13001
- }
13002
- return expanded;
13003
- }
13004
-
13005
13089
  //#endregion
13006
13090
  //#region ../theme-core/src/buttons/constants.ts
13007
13091
  /**
@@ -17025,6 +17109,48 @@ const effectCompositionSchema = z.object({
17025
17109
  focus: z.array(effectApplicationSchema).optional()
17026
17110
  });
17027
17111
 
17112
+ //#endregion
17113
+ //#region ../theme-core/src/navigation/effectPolicy.ts
17114
+ /** Every registered effect has one meaning for surface-owned nav hover. */
17115
+ const NAV_EFFECT_POLICIES = {
17116
+ "darken-background": "other",
17117
+ "lighten-background": "other",
17118
+ "fade-background": "other",
17119
+ "opacity-reduce": "other",
17120
+ "animated-gradient": "other",
17121
+ "background-on-hover": "paired-hover",
17122
+ "background-on-hover-alpha": "paired-hover",
17123
+ "border-glow": "other",
17124
+ "drop-shadow": "other",
17125
+ "inner-shadow": "other",
17126
+ "neumorphic-shadow": "other",
17127
+ "outer-glow": "other",
17128
+ "retro-3d-shadow": "other",
17129
+ "scale-up": "other",
17130
+ "scale-down": "other",
17131
+ lift: "other",
17132
+ "press-down": "other",
17133
+ pop: "other",
17134
+ "aurora-glow": "other",
17135
+ "cosmic-stardust": "other",
17136
+ "gradient-border-fill": "other",
17137
+ "animated-gradient-border-fill": "other",
17138
+ "border-bottom-grow": "other",
17139
+ "gradient-text": "other",
17140
+ "text-color-change": "text-colour",
17141
+ "border-color-change": "other",
17142
+ "sketchy-border": "other",
17143
+ "pulse-animation": "other",
17144
+ "nav-underline": "underline",
17145
+ "nav-frosted-base": "other",
17146
+ "nav-frosted-hover": "other",
17147
+ "nav-frosted-active": "other"
17148
+ };
17149
+ const policiesById = NAV_EFFECT_POLICIES;
17150
+ function navEffectPolicy(effectId) {
17151
+ return policiesById[effectId] ?? null;
17152
+ }
17153
+
17028
17154
  //#endregion
17029
17155
  //#region ../theme-core/src/navigation/types.ts
17030
17156
  /**
@@ -17102,6 +17228,16 @@ const navLinkStyleSchema = z.object({
17102
17228
  borderRadius: navLinkBorderRadiusSchema.default("none"),
17103
17229
  effects: effectCompositionSchema.optional()
17104
17230
  });
17231
+ function navLinkHoverColorCandidate(style) {
17232
+ const effect = style?.effects?.hover?.find((item) => navEffectPolicy(item.effectId) === "text-colour");
17233
+ return parseThemeColorToken(effect?.options?.hoverColorToken ?? style?.hoverColorToken);
17234
+ }
17235
+ function navLinkOwnsHoverForeground(style) {
17236
+ return style?.effects?.hover?.some((effect) => navEffectPolicy(effect.effectId) === "paired-hover") ?? false;
17237
+ }
17238
+ function navLinkOwnsUnderline(style) {
17239
+ return style?.effects?.hover?.some((effect) => navEffectPolicy(effect.effectId) === "underline") ?? false;
17240
+ }
17105
17241
  /**
17106
17242
  * Padding values for nav link padding presets
17107
17243
  */
@@ -22908,6 +23044,17 @@ function reinterpretForSiteStyle(input) {
22908
23044
  };
22909
23045
  }
22910
23046
 
23047
+ //#endregion
23048
+ //#region ../theme-core/src/header/rootPaint.ts
23049
+ /** The nav container takes precedence when a floating header also has one. */
23050
+ function headerRootPaint(header) {
23051
+ const navContainerType = header?.navContainer?.type ?? "none";
23052
+ if (navContainerType === "glass" || navContainerType === "pill") {
23053
+ return "contained-nav";
23054
+ }
23055
+ return header?.variant === "floating" ? "overlay" : "own-surface";
23056
+ }
23057
+
22911
23058
  //#endregion
22912
23059
  //#region ../theme-core/src/site-styles/siteChromeBackgrounds.ts
22913
23060
  const siteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceColorTokenNames);
@@ -22942,8 +23089,7 @@ function canCustomizeSiteChromeBackground(theme, surface$1) {
22942
23089
  if (surface$1 === "footer") return true;
22943
23090
  const header = theme?.header;
22944
23091
  if (!header) return false;
22945
- const navContainerType = header.navContainer?.type ?? "none";
22946
- return header.variant !== "floating" && navContainerType !== "glass" && navContainerType !== "pill";
23092
+ return headerRootPaint(header) === "own-surface";
22947
23093
  }
22948
23094
  function siteChromeBackgroundOverrideLabel(override) {
22949
23095
  switch (override.kind) {
@@ -23009,19 +23155,10 @@ function siteChromeBackgroundOverrideFromBackground(background) {
23009
23155
  function applyHeaderBackgroundOverride(theme, header, override) {
23010
23156
  if (override.kind === "default") return header;
23011
23157
  const background = siteChromeColorBackground(override.token);
23012
- const foreground = resolveChromeForegroundToken(theme, background);
23158
+ assertChromeBackgroundHasForeground(theme, background);
23013
23159
  return {
23014
23160
  ...header,
23015
- background: {
23016
- ...background,
23017
- textColor: foreground
23018
- },
23019
- textColor: foreground,
23020
- navColor: foreground,
23021
- ...header.navLinkStyle ? { navLinkStyle: {
23022
- ...header.navLinkStyle,
23023
- colorToken: foreground
23024
- } } : {}
23161
+ background
23025
23162
  };
23026
23163
  }
23027
23164
  /**
@@ -23033,34 +23170,17 @@ function applyHeaderBackgroundOverride(theme, header, override) {
23033
23170
  function applyFooterBackgroundOverride(theme, footer, override) {
23034
23171
  if (override.kind === "default") return footer;
23035
23172
  const background = siteChromeColorBackground(override.token);
23036
- const foreground = resolveChromeForegroundToken(theme, background);
23173
+ assertChromeBackgroundHasForeground(theme, background);
23037
23174
  return {
23038
23175
  ...footer,
23039
- background: {
23040
- ...background,
23041
- textColor: foreground
23042
- },
23043
- navColor: foreground,
23044
- ...footer.navLinkStyle ? { navLinkStyle: {
23045
- ...footer.navLinkStyle,
23046
- colorToken: foreground
23047
- } } : {},
23048
- ...footer.bottomTextLinkStyle ? { bottomTextLinkStyle: {
23049
- ...footer.bottomTextLinkStyle,
23050
- colorToken: foreground
23051
- } } : {},
23052
- ...footer.bottomBar ? { bottomBar: {
23053
- ...footer.bottomBar,
23054
- textColor: foreground
23055
- } } : {}
23176
+ background
23056
23177
  };
23057
23178
  }
23058
- function resolveChromeForegroundToken(theme, background) {
23179
+ function assertChromeBackgroundHasForeground(theme, background) {
23059
23180
  const resolved = resolveSurfaceForeground(background, expandPalette(theme.palette));
23060
23181
  if (resolved.kind === "unknown") {
23061
23182
  throw new Error(`Site chrome background "${background?.type === "color" ? background.color ?? "unknown" : "unknown"}" has no resolvable AA foreground; the theme palette is incomplete`);
23062
23183
  }
23063
- return resolved.roles.text;
23064
23184
  }
23065
23185
  function siteChromeColorBackground(color) {
23066
23186
  return {
@@ -25209,12 +25329,261 @@ function compareCompiledHeaders(left, right) {
25209
25329
  };
25210
25330
  }
25211
25331
 
25332
+ //#endregion
25333
+ //#region ../theme-core/src/navigation/presets.ts
25334
+ /**
25335
+ * Built-in nav link style presets
25336
+ *
25337
+ * Each preset uses composable effects to achieve its look.
25338
+ */
25339
+ const NAV_LINK_PRESETS = {
25340
+ minimal: {
25341
+ name: "Minimal",
25342
+ typography: "body",
25343
+ fontWeight: "medium",
25344
+ textTransform: "none",
25345
+ italic: false,
25346
+ colorToken: "text",
25347
+ hoverColorToken: "primary",
25348
+ padding: "none",
25349
+ borderRadius: "none",
25350
+ effects: { hover: [{
25351
+ effectId: "text-color-change",
25352
+ options: { hoverColorToken: "primary" }
25353
+ }] }
25354
+ },
25355
+ underline: {
25356
+ name: "Underline",
25357
+ typography: "body",
25358
+ fontWeight: "medium",
25359
+ textTransform: "none",
25360
+ italic: false,
25361
+ colorToken: "text",
25362
+ padding: "none",
25363
+ borderRadius: "none",
25364
+ effects: { hover: [{
25365
+ effectId: "nav-underline",
25366
+ options: { style: "static" }
25367
+ }] }
25368
+ },
25369
+ "underline-grow": {
25370
+ name: "Underline Grow",
25371
+ typography: "body",
25372
+ fontWeight: "medium",
25373
+ textTransform: "none",
25374
+ italic: false,
25375
+ colorToken: "text",
25376
+ padding: "none",
25377
+ borderRadius: "none",
25378
+ effects: { hover: [{
25379
+ effectId: "nav-underline",
25380
+ options: { style: "grow" }
25381
+ }] }
25382
+ },
25383
+ capsule: {
25384
+ name: "Capsule",
25385
+ typography: "body",
25386
+ fontWeight: "medium",
25387
+ textTransform: "none",
25388
+ italic: false,
25389
+ colorToken: "text",
25390
+ padding: "compact",
25391
+ borderRadius: "full",
25392
+ effects: {
25393
+ hover: [{
25394
+ effectId: "background-on-hover-alpha",
25395
+ options: {
25396
+ colorToken: "primary",
25397
+ opacity: 10
25398
+ }
25399
+ }],
25400
+ active: [{
25401
+ effectId: "background-on-hover-alpha",
25402
+ options: {
25403
+ colorToken: "primary",
25404
+ opacity: 15
25405
+ }
25406
+ }]
25407
+ }
25408
+ },
25409
+ solid: {
25410
+ name: "Solid",
25411
+ typography: "body",
25412
+ fontWeight: "medium",
25413
+ textTransform: "none",
25414
+ italic: false,
25415
+ colorToken: "text",
25416
+ padding: "compact",
25417
+ borderRadius: "none",
25418
+ effects: {
25419
+ hover: [{
25420
+ effectId: "background-on-hover",
25421
+ options: {
25422
+ colorToken: "primary",
25423
+ textColorToken: "primaryForeground"
25424
+ }
25425
+ }],
25426
+ active: [{
25427
+ effectId: "background-on-hover",
25428
+ options: {
25429
+ colorToken: "primary",
25430
+ textColorToken: "primaryForeground"
25431
+ }
25432
+ }]
25433
+ }
25434
+ },
25435
+ scale: {
25436
+ name: "Scale",
25437
+ typography: "body",
25438
+ fontWeight: "medium",
25439
+ textTransform: "none",
25440
+ italic: false,
25441
+ colorToken: "text",
25442
+ padding: "none",
25443
+ borderRadius: "none",
25444
+ effects: { hover: [{
25445
+ effectId: "scale-up",
25446
+ options: { scale: 1.05 }
25447
+ }] }
25448
+ },
25449
+ frosted: {
25450
+ name: "Frosted",
25451
+ typography: "body",
25452
+ fontWeight: "medium",
25453
+ textTransform: "none",
25454
+ italic: false,
25455
+ colorToken: "text",
25456
+ padding: "compact",
25457
+ borderRadius: "md",
25458
+ effects: {
25459
+ base: [{ effectId: "nav-frosted-base" }],
25460
+ hover: [{ effectId: "nav-frosted-hover" }],
25461
+ active: [{ effectId: "nav-frosted-active" }]
25462
+ }
25463
+ }
25464
+ };
25465
+ /**
25466
+ * Get a nav link style preset by ID
25467
+ */
25468
+ function getNavLinkPreset(presetId) {
25469
+ return NAV_LINK_PRESETS[presetId];
25470
+ }
25471
+ /**
25472
+ * List of available preset IDs
25473
+ */
25474
+ const NAV_LINK_PRESET_IDS = Object.keys(NAV_LINK_PRESETS);
25475
+
25476
+ //#endregion
25477
+ //#region ../theme-core/src/header/legacyNavLinkStyle.ts
25478
+ function buildLegacyHeaderNavLinkStyle(header, contrastTreatment, navTextSize) {
25479
+ const navStyle = header.navStyle ?? "minimal";
25480
+ const basePreset = getNavLinkPreset(navStyle);
25481
+ if (!basePreset) return null;
25482
+ const style = {
25483
+ ...basePreset,
25484
+ fontWeight: header.navWeight ?? basePreset.fontWeight,
25485
+ colorToken: (header.navColor ?? header.textColor)?.trim() || basePreset.colorToken,
25486
+ ...contrastTreatment ? { contrastTreatment } : {},
25487
+ ...navTextSize ? { textSize: navTextSize } : {}
25488
+ };
25489
+ if (header.navWeight === "bold" && navStyle === "solid") {
25490
+ style.textTransform = "uppercase";
25491
+ }
25492
+ applyLegacyGlassTreatment(style, header, navStyle);
25493
+ return style;
25494
+ }
25495
+ function applyLegacyGlassTreatment(style, header, navStyle) {
25496
+ if (header.navContainer?.type !== "glass" || navStyle !== "minimal") return;
25497
+ style.borderRadius = "full";
25498
+ style.effects = {
25499
+ ...style.effects,
25500
+ hover: glassStateEffects(10),
25501
+ active: glassStateEffects(18)
25502
+ };
25503
+ }
25504
+ function glassStateEffects(opacity) {
25505
+ return [{
25506
+ effectId: "background-on-hover-alpha",
25507
+ options: {
25508
+ colorToken: "neutral-50",
25509
+ opacity
25510
+ }
25511
+ }, {
25512
+ effectId: "text-color-change",
25513
+ options: { hoverColorToken: "neutral-50" }
25514
+ }];
25515
+ }
25516
+
25517
+ //#endregion
25518
+ //#region ../theme-core/src/header/openingForegroundProjection.ts
25519
+ /** Page-scoped render data. It is deliberately absent from the persisted theme schema. */
25520
+ function withOpeningForegroundProjection(header, foreground) {
25521
+ const projected = {
25522
+ ...header,
25523
+ __runtimeOpeningForeground: foreground
25524
+ };
25525
+ return projected;
25526
+ }
25527
+ function readOpeningForegroundProjection(header) {
25528
+ return header?.__runtimeOpeningForeground ?? null;
25529
+ }
25530
+
25531
+ //#endregion
25532
+ //#region ../theme-core/src/header/effectiveNavLinkStyle.ts
25533
+ function effectiveHeaderNavLinkStyle(header, contrastTreatment = null, navTextSize = null) {
25534
+ if (!header) return null;
25535
+ if (!header.navLinkStyle) {
25536
+ return buildLegacyHeaderNavLinkStyle(header, contrastTreatment, navTextSize);
25537
+ }
25538
+ return {
25539
+ ...header.navLinkStyle,
25540
+ ...contrastTreatment ? { contrastTreatment } : {},
25541
+ ...navTextSize ? { textSize: navTextSize } : {}
25542
+ };
25543
+ }
25544
+ function headerForegroundCandidates(header) {
25545
+ if (!header) return {};
25546
+ const style = effectiveHeaderNavLinkStyle(header);
25547
+ return {
25548
+ text: header.textColor ?? header.background?.textColor,
25549
+ link: style?.colorToken ?? header.navColor ?? header.textColor,
25550
+ linkHover: navLinkHoverColorCandidate(style)
25551
+ };
25552
+ }
25553
+ function headerLeafTextColor(header) {
25554
+ if (!header) return "text";
25555
+ return header.textColor ?? header.navColor ?? header.background?.textColor ?? header.navLinkStyle?.colorToken ?? "text";
25556
+ }
25557
+ /** Transparent chrome must reset the whole contract on its own root. */
25558
+ function transparentHeaderForegroundInput(header, palette) {
25559
+ const projection = readOpeningForegroundProjection(header);
25560
+ if (projection?.kind === "known") {
25561
+ return surfaceForegroundInputFromResolved(projection, rgbColorValue);
25562
+ }
25563
+ return authoredTransparentHeaderForegroundInput(header, palette);
25564
+ }
25565
+ function authoredTransparentHeaderForegroundInput(header, palette) {
25566
+ const text = headerLeafTextColor(header);
25567
+ const style = effectiveHeaderNavLinkStyle(header);
25568
+ const link = style?.colorToken ?? text;
25569
+ const restingToken = parseThemeColorToken(link) ?? parseThemeColorToken("text");
25570
+ if (!restingToken) throw new Error("Built-in text colour token is invalid");
25571
+ const hover = planForegroundHover({
25572
+ restingToken,
25573
+ candidate: navLinkOwnsHoverForeground(style) ? null : navLinkHoverColorCandidate(style),
25574
+ palette,
25575
+ backdrops: null
25576
+ });
25577
+ return {
25578
+ text: rgbColorValue(text),
25579
+ link: rgbColorValue(link),
25580
+ linkHover: rgbColorValue(hover.token),
25581
+ hoverDecoration: navLinkOwnsUnderline(style) ? "initial" : hover.decoration
25582
+ };
25583
+ }
25584
+
25212
25585
  //#endregion
25213
25586
  //#region ../theme-core/src/site-styles/headerOpeningForeground.ts
25214
- /** The nav-link hover effect that is a plain colour change. */
25215
- const TEXT_COLOR_CHANGE_EFFECT_ID = "text-color-change";
25216
- /** The nav-link hover affordance that does not depend on a colour delta. */
25217
- const NAV_UNDERLINE_EFFECT_ID = "nav-underline";
25218
25587
  function headerStyleAvailability(recipe, context) {
25219
25588
  if (!recipe.openingForeground) return { kind: "available" };
25220
25589
  return context?.openingForeground?.kind === "known" ? { kind: "available" } : {
@@ -25231,17 +25600,33 @@ function headerStyleAvailabilityReasonLabel(reason) {
25231
25600
  function applyOpeningForeground(theme, header, recipe, context) {
25232
25601
  if (!recipe.openingForeground) return header;
25233
25602
  const foreground = context?.openingForeground ?? { kind: "unknown" };
25234
- if (foreground.kind === "unknown") return applyUnknownOpeningForegroundFallback(header);
25603
+ if (foreground.kind === "unknown") return withOpeningForegroundProjection(applyUnknownOpeningForegroundFallback(theme, header), foreground);
25604
+ return projectKnownOpeningForeground(theme, header, recipe, foreground);
25605
+ }
25606
+ function projectKnownOpeningForeground(theme, header, recipe, foreground) {
25235
25607
  const withBrandForeground = {
25236
25608
  ...header,
25237
25609
  textColor: foreground.roles.text
25238
25610
  };
25239
25611
  if (recipe.openingForeground === "brand-only") return withBrandForeground;
25240
25612
  const scrollTreatment = compileHeaderScrollTreatment(theme, recipe, foreground.roles.text);
25241
- return {
25613
+ const style = effectiveHeaderNavLinkStyle(header);
25614
+ const hoverPlan = style && !navLinkOwnsHoverForeground(style) ? planOpeningNavHover(theme, foreground, style, scrollTreatment) : null;
25615
+ const projectedForeground = hoverPlan ? {
25616
+ ...foreground,
25617
+ roles: {
25618
+ ...foreground.roles,
25619
+ linkHover: hoverPlan.token
25620
+ },
25621
+ hoverDecoration: hoverPlan.decoration
25622
+ } : foreground;
25623
+ return withOpeningForegroundProjection({
25242
25624
  ...withBrandForeground,
25243
25625
  navColor: foreground.roles.text,
25244
- navLinkStyle: header.navLinkStyle ? adaptNavLinkStyleToForeground(header.navLinkStyle, foreground.roles.link, planForegroundHover(foreground.roles.link, paletteHexForToken(theme, foreground.roles.link), scrollTreatment?.kind === "contrast-glass" ? scrollTreatment.backgroundTone : null)) : header.navLinkStyle,
25626
+ navLinkStyle: style ? navLinkOwnsHoverForeground(style) ? {
25627
+ ...style,
25628
+ colorToken: foreground.roles.link
25629
+ } : projectOpeningTextNavStyle(style, foreground.roles.link) : header.navLinkStyle,
25245
25630
  border: {
25246
25631
  style: "solid",
25247
25632
  width: "thin",
@@ -25249,79 +25634,52 @@ function applyOpeningForeground(theme, header, recipe, context) {
25249
25634
  colorToken: parseThemeColorToken(`${foreground.roles.text}/25`) ?? foreground.roles.text
25250
25635
  },
25251
25636
  ...scrollTreatment ? { scrollTreatment } : {}
25252
- };
25253
- }
25254
- /**
25255
- * Give the resolved foreground both the resting and the hover colour.
25256
- *
25257
- * Adapting the resting colour alone left hover as whatever the recipe wrote —
25258
- * for `minimal-surface` a bare `primary`, which is a brand colour no one had
25259
- * checked against the section the header floats over. On a primary-painted
25260
- * opening section that resolves to the section's own colour, and the link
25261
- * disappears on hover.
25262
- *
25263
- * The two colours are a pair: the scroll treatment picks its glass tone from
25264
- * the resting foreground precisely so one colour reads both against the opening
25265
- * section and against the glass. A hover colour outside that pairing is not
25266
- * covered by either half of it.
25267
- *
25268
- * A pill palette is left alone. It owns hover through `background-on-hover`,
25269
- * which carries its own paired background and foreground tokens, so hover
25270
- * legibility there does not depend on the surface underneath at all.
25271
- */
25272
- function adaptNavLinkStyleToForeground(navLinkStyle, linkToken, hoverPlan) {
25273
- const adapted = {
25274
- ...navLinkStyle,
25275
- colorToken: linkToken
25276
- };
25277
- const hoverEffects = navLinkStyle.effects?.hover ?? [];
25278
- const hasColorChange = hoverEffects.some(isTextColorChangeEffect);
25279
- if (!hasColorChange && hoverEffects.length > 0) return adapted;
25280
- if (!hasColorChange && !navLinkStyle.hoverColorToken) return adapted;
25281
- const underline = {
25282
- effectId: NAV_UNDERLINE_EFFECT_ID,
25637
+ }, projectedForeground);
25638
+ }
25639
+ function planOpeningNavHover(theme, foreground, style, scrollTreatment) {
25640
+ const glassBackdrop = scrollTreatment?.kind === "contrast-glass" ? worstCaseGlassBackdrop(scrollTreatment.backgroundTone) : null;
25641
+ const measured = foreground.provenance.kind === "measured";
25642
+ const candidate$1 = measured ? navLinkHoverColorCandidate(style) : foreground.hoverDecoration === "initial" ? foreground.roles.linkHover : null;
25643
+ const backdrops = measured ? [foreground.provenance.backdropHex, ...glassBackdrop ? [glassBackdrop] : []] : glassBackdrop ? [glassBackdrop] : null;
25644
+ const plan = planForegroundHover({
25645
+ restingToken: foreground.roles.link,
25646
+ candidate: candidate$1,
25647
+ palette: expandPalette(theme.palette),
25648
+ backdrops
25649
+ });
25650
+ return !measured && plan.kind === "softened" ? {
25651
+ kind: "resting",
25652
+ token: foreground.roles.link,
25653
+ decoration: "underline"
25654
+ } : plan;
25655
+ }
25656
+ /** The surface contract owns colour; the nav effect keeps its grow animation. */
25657
+ function projectOpeningTextNavStyle(style, linkToken, requireUnderline = true) {
25658
+ const hover = (style.effects?.hover ?? []).filter((effect) => navEffectPolicy(effect.effectId) !== "text-colour").map((effect) => navEffectPolicy(effect.effectId) === "underline" ? {
25659
+ ...effect,
25283
25660
  options: {
25661
+ ...effect.options,
25284
25662
  style: "grow",
25285
25663
  colorToken: linkToken
25286
25664
  }
25287
- };
25288
- const withoutColorChange = hoverEffects.filter((effect) => !isTextColorChangeEffect(effect) && !isNavUnderlineEffect(effect));
25289
- if (hoverPlan.kind === "resting") {
25290
- return {
25291
- ...adapted,
25292
- hoverColorToken: null,
25293
- effects: {
25294
- ...adapted.effects,
25295
- hover: [...withoutColorChange, underline]
25296
- }
25297
- };
25298
- }
25299
- const hoverColorToken = hoverPlan.token;
25665
+ } : effect);
25666
+ const needsUnderline = requireUnderline && !navLinkOwnsUnderline({ effects: { hover } });
25300
25667
  return {
25301
- ...adapted,
25302
- hoverColorToken,
25668
+ ...style,
25669
+ colorToken: linkToken,
25670
+ hoverColorToken: null,
25303
25671
  effects: {
25304
- ...adapted.effects,
25305
- hover: [...hoverEffects.map((effect) => isTextColorChangeEffect(effect) ? {
25306
- ...effect,
25672
+ ...style.effects,
25673
+ hover: needsUnderline ? [...hover, {
25674
+ effectId: "nav-underline",
25307
25675
  options: {
25308
- ...effect.options,
25309
- hoverColorToken
25676
+ style: "grow",
25677
+ colorToken: linkToken
25310
25678
  }
25311
- } : effect).filter((effect) => !isNavUnderlineEffect(effect)), underline]
25679
+ }] : hover
25312
25680
  }
25313
25681
  };
25314
25682
  }
25315
- function isTextColorChangeEffect(effect) {
25316
- return effect.effectId === TEXT_COLOR_CHANGE_EFFECT_ID;
25317
- }
25318
- function isNavUnderlineEffect(effect) {
25319
- return effect.effectId === NAV_UNDERLINE_EFFECT_ID;
25320
- }
25321
- /** Concrete hex behind a plain palette token, or null when it is not one. */
25322
- function paletteHexForToken(theme, token) {
25323
- return opaquePaletteHexForToken(expandPalette(theme.palette), token);
25324
- }
25325
25683
  function compileHeaderScrollTreatment(theme, recipe, foregroundToken) {
25326
25684
  if (recipe.scrollTreatment !== "contrast-glass") return undefined;
25327
25685
  const tokenParts = parseColorTokenParts(foregroundToken);
@@ -25347,19 +25705,37 @@ const UNKNOWN_OPENING_FOREGROUND_CONTAINER = {
25347
25705
  * keeps already-persisted selections readable without creating a second
25348
25706
  * availability policy.
25349
25707
  */
25350
- function applyUnknownOpeningForegroundFallback(header) {
25708
+ function applyUnknownOpeningForegroundFallback(theme, header) {
25709
+ const textToken = parseThemeColorToken("text");
25710
+ if (!textToken) throw new Error("Built-in text colour token is invalid");
25711
+ const style = effectiveHeaderNavLinkStyle(header);
25712
+ const plannedHover = style && !navLinkOwnsHoverForeground(style) ? planUnknownOpeningNavHover(theme, style, textToken) : null;
25351
25713
  return {
25352
25714
  ...header,
25353
25715
  scrollTreatment: undefined,
25354
25716
  textColor: "text",
25355
25717
  navColor: "text",
25356
- navLinkStyle: header.navLinkStyle ? {
25357
- ...header.navLinkStyle,
25358
- colorToken: "text"
25718
+ navLinkStyle: style ? navLinkOwnsHoverForeground(style) ? {
25719
+ ...style,
25720
+ colorToken: textToken
25721
+ } : {
25722
+ ...projectOpeningTextNavStyle(style, textToken, plannedHover?.kind === "resting"),
25723
+ hoverColorToken: plannedHover?.kind === "resting" ? null : plannedHover?.token
25359
25724
  } : header.navLinkStyle,
25360
25725
  container: UNKNOWN_OPENING_FOREGROUND_CONTAINER
25361
25726
  };
25362
25727
  }
25728
+ function planUnknownOpeningNavHover(theme, style, restingToken) {
25729
+ const palette = expandPalette(theme.palette);
25730
+ const surface$1 = palette.surface;
25731
+ const backdrops = surface$1 ? [compositeHex(surface$1, "#000000", UNKNOWN_OPENING_FOREGROUND_CONTAINER.opacity), compositeHex(surface$1, "#ffffff", UNKNOWN_OPENING_FOREGROUND_CONTAINER.opacity)] : null;
25732
+ return planForegroundHover({
25733
+ restingToken,
25734
+ candidate: surface$1 ? navLinkHoverColorCandidate(style) : null,
25735
+ palette,
25736
+ backdrops
25737
+ });
25738
+ }
25363
25739
  function applyPageOpeningForegroundToTheme(theme, recipe, context) {
25364
25740
  if (!recipe?.openingForeground) return theme;
25365
25741
  return {
@@ -29737,9 +30113,11 @@ const coursePolicyOverrideSchema = z.object({
29737
30113
  cutoffHours: z.number().int().nonnegative().optional()
29738
30114
  }).strict();
29739
30115
  /**
29740
- * The four booking settings a course owns for every one of its runs: where it
29741
- * is listed, whether passes and memberships may be used, and the refunds and
29742
- * transfers policy.
30116
+ * The booking settings a course owns for every one of its runs: where it is
30117
+ * listed, whether passes and memberships may be used, QR tickets
30118
+ * (`ticketingMode`, where `inherit` follows the course's event type), guest
30119
+ * details (`collectAllAttendeeDetails`, where `null` follows the event type)
30120
+ * and the refunds and transfers policy.
29743
30121
  *
29744
30122
  * Listing is spelled the way the management API spells it ('public' or
29745
30123
  * 'unlisted') rather than in the command language, which folds "not live yet"
@@ -29750,6 +30128,12 @@ const courseBookingSettingsSchema = z.object({
29750
30128
  visibility: z.enum(["public", "unlisted"]),
29751
30129
  acceptsPasses: z.boolean(),
29752
30130
  acceptsMemberships: z.boolean(),
30131
+ ticketingMode: z.enum([
30132
+ "inherit",
30133
+ "enabled",
30134
+ "disabled"
30135
+ ]).optional(),
30136
+ collectAllAttendeeDetails: z.boolean().nullable().optional(),
29753
30137
  cancellationPolicy: z.object({
29754
30138
  refunds: coursePolicyOverrideSchema,
29755
30139
  transfers: coursePolicyOverrideSchema
@@ -30753,7 +31137,6 @@ const upsertEventSeriesCommandSchema = z.object({
30753
31137
  occurrences: z.array(eventOccurrenceSchema).min(1).readonly(),
30754
31138
  content: contentSummarySchema.optional(),
30755
31139
  settings: unknownRecordSchema.optional(),
30756
- presentation: unknownRecordSchema.nullable().optional(),
30757
31140
  tags: z.array(z.string()).readonly().optional(),
30758
31141
  blocks: entrySlotBlocksSchema.optional(),
30759
31142
  staffRefs: z.array(staffRefSchema).readonly().optional()
@@ -30983,7 +31366,6 @@ const allowedRuntimes = [
30983
31366
  const intendedExposures = [
30984
31367
  "recipe",
30985
31368
  "sdk_cli",
30986
- "onboarding_planner",
30987
31369
  "ai_proposal",
30988
31370
  "internal_apply"
30989
31371
  ];
@@ -31025,21 +31407,15 @@ const allRuntimes = [
31025
31407
  const standardExposure = [
31026
31408
  "recipe",
31027
31409
  "sdk_cli",
31028
- "onboarding_planner",
31029
31410
  "internal_apply"
31030
31411
  ];
31031
31412
  const proposalExposure = [
31032
31413
  "recipe",
31033
31414
  "sdk_cli",
31034
- "onboarding_planner",
31035
31415
  "ai_proposal",
31036
31416
  "internal_apply"
31037
31417
  ];
31038
- const recipeExposure = [
31039
- "recipe",
31040
- "onboarding_planner",
31041
- "internal_apply"
31042
- ];
31418
+ const recipeExposure = ["recipe", "internal_apply"];
31043
31419
  const sdkExposure = ["sdk_cli", "internal_apply"];
31044
31420
 
31045
31421
  //#endregion
@@ -31732,6 +32108,18 @@ function isIdentifiedImage(image) {
31732
32108
  return typeof image === "object" && image !== null && typeof image.identifier === "string" && image.identifier.trim().length > 0;
31733
32109
  }
31734
32110
 
32111
+ //#endregion
32112
+ //#region ../site-commands/src/planCommandOrderingDependencies.ts
32113
+ /** A complete reorder observes the membership after all selected page mutations. */
32114
+ function planCommandOrderingDependencies(command, commands, providerByRef, requiresByCommand) {
32115
+ const referenced = (requiresByCommand.get(command.ref) ?? []).flatMap(({ ref }) => {
32116
+ const provider = providerByRef.get(ref);
32117
+ return provider === undefined ? [] : [provider];
32118
+ });
32119
+ const membership = command.type === "reorderPageBlocks" ? commands.filter((candidate$1) => (candidate$1.type === "upsertPageBlock" || candidate$1.type === "deletePageBlock") && candidate$1.input.pageIdentifier === command.input.pageIdentifier).map((candidate$1) => candidate$1.ref) : [];
32120
+ return [...new Set([...referenced, ...membership])];
32121
+ }
32122
+
31735
32123
  //#endregion
31736
32124
  //#region ../site-commands/src/guards.ts
31737
32125
  function isRecord(value) {
@@ -32161,13 +32549,12 @@ const unsupportedWholeSeriesPageFieldChecks = [
32161
32549
  (input) => input.categoryRef !== undefined,
32162
32550
  (input) => input.categoryIdentifier !== undefined,
32163
32551
  (input) => input.tags !== undefined,
32164
- (input) => input.presentation !== undefined,
32165
32552
  (input) => input.content?.imageRef !== undefined,
32166
32553
  (input) => input.content?.mediaRefs !== undefined
32167
32554
  ];
32168
32555
  function wholeSeriesPageFieldsGap(input) {
32169
32556
  if (!unsupportedWholeSeriesPageFieldChecks.some((hasField) => hasField(input))) return null;
32170
- return staticGap("event_whole_series_page_fields_unsupported", "Whole-series page, reference, tag, and presentation fields need native page-authority mapping before mutation.");
32557
+ return staticGap("event_whole_series_page_fields_unsupported", "Whole-series page, reference and tag fields need native page-authority mapping before mutation.");
32171
32558
  }
32172
32559
  function passesGap(input) {
32173
32560
  return input.bookingOffer.pricing.acceptsPasses === true ? staticGap("event_whole_series_passes_unsupported", "Whole-series commands do not support pass redemption.") : null;
@@ -32581,10 +32968,9 @@ function orderCommands(commands, providerByRef, requiresByCommand) {
32581
32968
  const outgoing = new Map();
32582
32969
  const indegree = new Map(commands.map((command) => [command.ref, 0]));
32583
32970
  for (const command of commands) {
32584
- const requires = requiresByCommand.get(command.ref) ?? [];
32585
- for (const { ref } of requires) {
32586
- const provider = providerByRef.get(ref);
32587
- if (provider === undefined || provider === command.ref) continue;
32971
+ const providers = planCommandOrderingDependencies(command, commands, providerByRef, requiresByCommand);
32972
+ for (const provider of providers) {
32973
+ if (provider === command.ref) continue;
32588
32974
  if (!outgoing.has(provider)) outgoing.set(provider, new Set());
32589
32975
  const dependents = outgoing.get(provider);
32590
32976
  if (dependents !== undefined && !dependents.has(command.ref)) {
@@ -33325,4 +33711,4 @@ function parseSdkEventRunUpsertInput(input, runIndex, options) {
33325
33711
  }
33326
33712
 
33327
33713
  //#endregion
33328
- export { BREAKPOINT_MD as $, defaultThemeTokens as $n, parseGradientPresetId as $t, createBlockRef as A, cardVariantRoleValues as An, resolvedButtonFocusDeclarations as At, createSiteCommandSiteRef as B, rgbColorValue as Bn, themeButtonExtraClassesWithTrailingIcon as Bt, pagePathForCommand as C, mediaTypeDiscriminants as Cn, featuresFromHex as Cr, ACCORDION_TRIGGER_FONT_SCALE as Ct, require_react as D, generateCardCss as Dn, generateButtonCss as Dt, require_jsx_runtime as E, getDefaultCardVariants as En, __toESM as Er, ICON_SIZE_PX as Et, createEventSeriesRef as F, TokenResolver as Fn, generateDefaultButtonSystem as Ft, readHeaderNavFontSizePreference as G, resolveSurfaceForeground as Gn, formatButtonColourContractError as Gt, refSlug as H, textColorValue as Hn, themeButtonVariantClassNames as Ht, createFooterRef as I, backgroundColorStyle as In, generateEffectsCSS as It, generateHeaderScrollTreatmentStyles as J, surfaceForegroundStyle as Jn, getTextColorForBackground as Jt, getCuratedSiteStyle as K, DEFAULT_SURFACE_TEXT_COLOR as Kn, generateShadowValue as Kt, createFormRef as L, borderColorStyle as Ln, generateBaseInteractiveCSS as Lt, createCommandRef as M, backgroundAppearanceColorTokenNames as Mn, contextualButtonSurfaceStyles as Mt, createContentEntryRef as N, backgroundAppearanceQuickColorTokenNames as Nn, deriveButtonSurfaceContext as Nt, pickShadeTowardBackground as O, generateShadowValue$1 as On, RESOLVED_BUTTON_DISABLED_DECLARATIONS as Ot, createEventCategoryRef as P, parseBackgroundAppearanceColorToken as Pn, resolveContextualButtonRole as Pt, BREAKPOINT_LG as Q, buildThemeTokens as Qn, getGradientPreset as Qt, createNavigationRef as R, cssVarValue as Rn, generateInteractiveChildInheritanceCSS as Rt, pageIdentifierForCommand as S, mediaSchema as Sn, contrastRatio as Sr, ACCORDION_SPACING_MAP as St, eventBookingOfferIntentSchema as T, generateCardCoreCss as Tn, oklchToHexGamut as Tr, ICON_CONFIG as Tt, slugPart as U, selectLogoVariant as Un, parseThemeForPersistence as Ut, createVenueRef as V, textColorStyle as Vn, themeButtonSizeClassName as Vt, mergeHeaderNavTypographyPreference as W, surfaceToneFromForegroundToken as Wn, compileButtonColourStates as Wt, getHeaderPaintTargets as X, parseBlockColorToken as Xn, gradientToCss as Xt, generateMobileOverlayBackdropFilterFix as Y, surfaceForegroundValue as Yn, footerModeSchema as Yt, renderHeaderPaintTargetRules as Z, parseColorTokenParts as Zn, GRADIENT_PRESET_IDS as Zt, getSiteCommandMetadata as _, resolveThemeColorReferenceValue as _n, tileTreatmentValues as _r, FONT_WEIGHT_MAP as _t, classifySdkEventSchedule as a, pageDesignBoundaryTreatmentSchema as an, assertNever$1 as ar, MQ_SM_UP as at, entryIdentifierForCommand as b, stripStaleTransitionStyleGroup as bn, onColorTokenName as br, LABEL_FONT_SIZE_MAP as bt, isEventVisibility as c, composeBoundary as cn, defineNoBlockDesignCapability as cr, getComponentBorderWidthPx as ct, stableStringify as d, anglePolygonCss as dn, featureCardCompositionTreatmentValues as dr, parseCustomCssRules as dt, buttonStyleSelectionSchema as en, defineBlockDesignMetadata as er, BREAKPOINT_SM as et, createDryRunReport as f, transitionCatalogueEntries as fn, headingDecorationColorTokenValues as fr, NAV_LINK_BORDER_RADIUS_MAP as ft, evaluateSiteCommandExposure as g, resolveOnColorForThemeToken as gn, testimonialsCompositionTreatmentValues as gr, NAV_LINK_TEXT_SIZE_MAP as gt, evaluateSiteCommandBatchExposure as h, isCssColorLiteral as hn, offeringsPreviewCompositionTreatmentValues as hr, NAV_LINK_PADDING_X_MAP as ht, sdkEventUpsertOptionalFieldSchemas as i, unsafeAsOpaqueButtonColourToken as in, asSiteStyleId as ir, MQ_SM_DOWN as it, createBlocksRef as j, normalizeCardRole as jn, contextualButtonSurfaceResetStyles as jt, generateCssVars as k, cardRoleSchema as kn, resolvedButtonColourDeclarations as kt, venueManagementFields as l, sectionFactsForContent as ln, eventListTreatmentValues as lr, getComponentShadowVar as lt, mapEventCommandContent as m, boxBackgroundSchema as mn, makeBoundaryId as mr, NAV_LINK_PADDING_MAP as mt, sdkCourseBookingSettingsSchema as n, VARIANT_ROLES as nn, asDesignBlockId as nr, MQ_LG_UP as nt, classifySdkEventBookingOffer as o, pageDesignIntentSchemaVersion as on, defineBlockDesignCapability as or, MQ_TABLET_ONLY as ot, parseAndPlanCommandBatchForTypes as p, boxBackgroundOverlaySchema as pn, introCompositionTreatmentValues as pr, NAV_LINK_LETTER_SPACING_MAP as pt, themeSchema as q, surfaceForegroundDeclarations as qn, expandPalette as qt, sdkEventOccurrenceInputSchema as r, buttonVariantIdSchema as rn, asPageFlowPresetId as rr, MQ_MD_DOWN as rt, eventVisibilitySchema as s, boundaryLayout as sn, defineFrameCapability as sr, headerVariantValues as st, classifySdkEventTiming as t, PUBLIC_BUTTON_ROLES as tn, asAppearancePresetId as tr, BREAKPOINT_XL as tt, sortRecord as u, boundaryRuntimeMetricsForDecorativeTransition as un, eyebrowTreatmentValues as ur, parseBlockCustomCssRules as ut, blockIdentifierForCommand as v, resolveTokenOrCssColorValue as vn, visibleHeadingDecorationFamilyValues as vr, INPUT_FONT_SIZE_MAP as vt, hasConfiguredBookingVertical as w, persistableMediaSchema as wn, hexToRgb as wr, ACCORDION_TRIGGER_LETTER_SPACING as wt, pageIdentifierForBlockCommand as x, mediaIdentifierSchema as xn, ORDINARY_TEXT_MIN_CONTRAST_RATIO as xr, ACCORDION_OPEN_ACCENT_THICKNESS as xt, blockOrderIdentifiersForCommand as y, pageDesignEditorBackgroundOverrideOwner as yn, isOnColorBackgroundCandidateToken as yr, INPUT_SPACING_MAP as yt, createPageRef as z, mergeStyles as zn, parseThemeButtonVariantId as zt };
33714
+ export { getCuratedSiteStyle as $, rgbColorValue as $n, themeButtonVariantClassNames as $t, createBlockRef as A, stripStaleTransitionStyleGroup as An, offeringsPreviewCompositionTreatmentValues as Ar, INPUT_SPACING_MAP as At, createSiteCommandSiteRef as B, getTextColorForBackground as Bn, oklchToHexGamut as Br, resolvedButtonColourDeclarations as Bt, pagePathForCommand as C, boxBackgroundSchema as Cn, defineNoBlockDesignCapability as Cr, NAV_LINK_TEXT_SIZE_MAP as Ct, require_react as D, resolveTokenOrCssColorValue as Dn, headingDecorationColorTokenValues as Dr, navEffectPolicy as Dt, require_jsx_runtime as E, resolveThemeColorReferenceValue as En, featureCardCompositionTreatmentValues as Er, navLinkOwnsUnderline as Et, createEventSeriesRef as F, generateCardCoreCss as Fn, onColorTokenName as Fr, ACCORDION_TRIGGER_LETTER_SPACING as Ft, headerForegroundCandidates as G, backgroundAppearanceColorTokenNames as Gn, resolveContextualButtonRole as Gt, refSlug as H, cardRoleSchema as Hn, contextualButtonSurfaceResetStyles as Ht, createFooterRef as I, getDefaultCardVariants as In, ORDINARY_TEXT_MIN_CONTRAST_RATIO as Ir, ICON_CONFIG as It, readOpeningForegroundProjection as J, TokenResolver as Jn, generateBaseInteractiveCSS as Jt, headerLeafTextColor as K, backgroundAppearanceQuickColorTokenNames as Kn, generateDefaultButtonSystem as Kt, createFormRef as L, generateCardCss as Ln, contrastRatio as Lr, ICON_SIZE_PX as Lt, createCommandRef as M, mediaSchema as Mn, tileTreatmentValues as Mr, ACCORDION_OPEN_ACCENT_THICKNESS as Mt, createContentEntryRef as N, mediaTypeDiscriminants as Nn, visibleHeadingDecorationFamilyValues as Nr, ACCORDION_SPACING_MAP as Nt, pickShadeTowardBackground as O, pageDesignEditorBackgroundOverrideOwner as On, introCompositionTreatmentValues as Or, FONT_WEIGHT_MAP as Ot, createEventCategoryRef as P, persistableMediaSchema as Pn, isOnColorBackgroundCandidateToken as Pr, ACCORDION_TRIGGER_FONT_SCALE as Pt, headerRootPaint as Q, mergeStyles as Qn, themeButtonSizeClassName as Qt, createNavigationRef as R, renderCssDeclarations as Rn, featuresFromHex as Rr, generateButtonCss as Rt, pageIdentifierForCommand as S, boxBackgroundOverlaySchema as Sn, defineFrameCapability as Sr, NAV_LINK_PADDING_X_MAP as St, eventBookingOfferIntentSchema as T, resolveOnColorForThemeToken as Tn, eyebrowTreatmentValues as Tr, navLinkOwnsHoverForeground as Tt, slugPart as U, cardVariantRoleValues as Un, contextualButtonSurfaceStyles as Ut, createVenueRef as V, generateShadowValue$1 as Vn, __toESM as Vr, resolvedButtonFocusDeclarations as Vt, effectiveHeaderNavLinkStyle as W, normalizeCardRole as Wn, deriveButtonSurfaceContext as Wt, mergeHeaderNavTypographyPreference as X, borderColorStyle as Xn, parseThemeButtonVariantId as Xt, getNavLinkPreset as Y, backgroundColorStyle as Yn, generateInteractiveChildInheritanceCSS as Yt, readHeaderNavFontSizePreference as Z, cssVarValue as Zn, themeButtonExtraClassesWithTrailingIcon as Zt, getSiteCommandMetadata as _, composeBoundary as _n, asDesignBlockId as _r, parseBlockCustomCssRules as _t, classifySdkEventSchedule as a, gradientToCss as an, DEFAULT_SURFACE_TEXT_COLOR as ar, BREAKPOINT_LG as at, entryIdentifierForCommand as b, anglePolygonCss as bn, assertNever$1 as br, NAV_LINK_LETTER_SPACING_MAP as bt, isEventVisibility as c, parseGradientPresetId as cn, surfaceForegroundStyle as cr, BREAKPOINT_XL as ct, stableStringify as d, VARIANT_ROLES as dn, parseBlockColorToken as dr, MQ_SM_DOWN as dt, parseThemeForPersistence as en, textColorStyle as er, themeSchema as et, createDryRunReport as f, buttonVariantIdSchema as fn, parseColorTokenParts as fr, MQ_SM_UP as ft, evaluateSiteCommandExposure as g, boundaryLayout as gn, asAppearancePresetId as gr, getComponentShadowVar as gt, evaluateSiteCommandBatchExposure as h, pageDesignIntentSchemaVersion as hn, defineBlockDesignMetadata as hr, getComponentBorderWidthPx as ht, sdkEventUpsertOptionalFieldSchemas as i, footerModeSchema as in, resolveSurfaceForeground as ir, renderHeaderPaintTargetRules as it, createBlocksRef as j, mediaIdentifierSchema as jn, testimonialsCompositionTreatmentValues as jr, LABEL_FONT_SIZE_MAP as jt, generateCssVars as k, siteChromeBackgroundOverrideOwner as kn, makeBoundaryId as kr, INPUT_FONT_SIZE_MAP as kt, venueManagementFields as l, buttonStyleSelectionSchema as ln, surfaceForegroundValue as lr, MQ_LG_UP as lt, mapEventCommandContent as m, pageDesignBoundaryTreatmentSchema as mn, defaultThemeTokens as mr, headerVariantValues as mt, sdkCourseBookingSettingsSchema as n, formatButtonColourContractError as nn, selectLogoVariant as nr, generateMobileOverlayBackdropFilterFix as nt, classifySdkEventBookingOffer as o, GRADIENT_PRESET_IDS as on, surfaceForegroundDeclarations as or, BREAKPOINT_MD as ot, parseAndPlanCommandBatchForTypes as p, unsafeAsOpaqueButtonColourToken as pn, buildThemeTokens as pr, MQ_TABLET_ONLY as pt, transparentHeaderForegroundInput as q, parseBackgroundAppearanceColorToken as qn, generateEffectsCSS as qt, sdkEventOccurrenceInputSchema as r, generateShadowValue as rn, surfaceToneFromForegroundToken as rr, getHeaderPaintTargets as rt, eventVisibilitySchema as s, getGradientPreset as sn, surfaceForegroundInputFromResolved as sr, BREAKPOINT_SM as st, classifySdkEventTiming as t, compileButtonColourStates as tn, textColorValue as tr, generateHeaderScrollTreatmentStyles as tt, sortRecord as u, PUBLIC_BUTTON_ROLES as un, parseThemeColorToken as ur, MQ_MD_DOWN as ut, blockIdentifierForCommand as v, sectionFactsForContent as vn, asPageFlowPresetId as vr, parseCustomCssRules as vt, hasConfiguredBookingVertical as w, isCssColorLiteral as wn, eventListTreatmentValues as wr, navLinkHoverColorCandidate as wt, pageIdentifierForBlockCommand as x, transitionCatalogueEntries as xn, defineBlockDesignCapability as xr, NAV_LINK_PADDING_MAP as xt, blockOrderIdentifiersForCommand as y, boundaryRuntimeMetricsForDecorativeTransition as yn, asSiteStyleId as yr, NAV_LINK_BORDER_RADIUS_MAP as yt, createPageRef as z, expandPalette as zn, hexToRgb as zr, RESOLVED_BUTTON_DISABLED_DECLARATIONS as zt };