@tryvitris/website-harness 1.0.1 → 1.0.2

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 (70) hide show
  1. package/agent/graph.yaml +16 -8
  2. package/agent/refs/browser.md +2 -0
  3. package/agent/refs/compose.md +2 -0
  4. package/agent/refs/design-quality.md +2 -0
  5. package/agent/refs/direction.md +2 -0
  6. package/agent/refs/implement.md +2 -0
  7. package/agent/refs/template-adaptation.md +2 -0
  8. package/agent/refs/template-library.md +2 -0
  9. package/agent/refs/typography.md +15 -0
  10. package/bin/api/agent/evaluate.js +7 -6
  11. package/bin/api/agent/grade.js +7 -6
  12. package/bin/api/cli/api.js +15 -14
  13. package/bin/api/cli.js +17 -16
  14. package/bin/api/engine.js +8 -7
  15. package/bin/api/hosting.js +9 -8
  16. package/bin/api/index.js +4 -3
  17. package/bin/api/render.js +2 -1
  18. package/bin/api/theme.js +2 -1
  19. package/bin/chunks/{chunk-JBSGJR7X.js → chunk-ATPI2YRF.js} +2 -2
  20. package/bin/chunks/{chunk-KIWEFRNG.js → chunk-BMD5GVUT.js} +6 -6
  21. package/bin/chunks/{chunk-AUO5I56E.js → chunk-C3F7XQD2.js} +194 -1
  22. package/bin/chunks/{chunk-UR4NWYVW.js → chunk-CPQQQ5YB.js} +3 -3
  23. package/bin/chunks/{chunk-LKN75JGA.js → chunk-GE6UUEG2.js} +13 -8
  24. package/bin/chunks/{chunk-445FZZEC.js → chunk-GM57SNLL.js} +9 -9
  25. package/bin/chunks/{chunk-RE4GOHSU.js → chunk-GO3XEQBA.js} +3 -3
  26. package/bin/chunks/{chunk-GQPUILFR.js → chunk-JPQCUN2J.js} +1301 -1252
  27. package/bin/chunks/{chunk-37F7XIMH.js → chunk-JTXFTNEA.js} +6 -1
  28. package/bin/chunks/{chunk-L44ZTNDM.js → chunk-NMTFSWPK.js} +6 -6
  29. package/bin/chunks/{chunk-VKU5TY5C.js → chunk-OQGUEQ6F.js} +1 -1
  30. package/bin/chunks/{chunk-M72KKZIB.js → chunk-Q6FLOYHS.js} +4 -3
  31. package/bin/chunks/{chunk-H3BS2EBR.js → chunk-QRIYA3GJ.js} +3 -3
  32. package/bin/chunks/chunk-S2XVNTQP.js +90 -0
  33. package/bin/chunks/{chunk-54QPYUM2.js → chunk-XJTAR7KM.js} +1 -1
  34. package/bin/chunks/{chunk-72Y4YMTV.js → chunk-YYK6367V.js} +2 -2
  35. package/bin/chunks/{chunk-65LRZIBP.js → chunk-YYYPSFFZ.js} +2 -2
  36. package/bin/chunks/{design-context-store-ETVS4N6W.js → design-context-store-BDYBQQCO.js} +7 -6
  37. package/bin/chunks/{packets-XFXKHSEY.js → packets-RXFWDCK2.js} +7 -6
  38. package/bin/chunks/{preparation-3OR7LVPQ.js → preparation-ETEEMB5H.js} +7 -6
  39. package/bin/cloudflare/backend.js +90 -11
  40. package/bin/types/src/agent/browser-typography.d.ts +33 -0
  41. package/bin/types/src/agent/theme-suite-identity.d.ts +1 -0
  42. package/bin/types/src/agent/typography-policy.d.ts +6 -0
  43. package/bin/types/src/compile/site.d.ts +1 -1
  44. package/bin/types/src/theme.d.ts +3 -1
  45. package/bin/types/src/types.d.ts +6 -1
  46. package/bin/types/src/typography.d.ts +27 -0
  47. package/bin/vitris-art.js +7 -6
  48. package/bin/vitris-build.js +5 -4
  49. package/bin/vitris-create-site.js +9 -8
  50. package/bin/vitris-cursor-hook.js +7 -6
  51. package/bin/vitris-eval.js +9 -8
  52. package/bin/vitris-go.js +9 -8
  53. package/bin/vitris-harvest.js +8 -7
  54. package/bin/vitris-knowledge.js +8 -7
  55. package/bin/vitris-launch-preflight.js +7 -6
  56. package/bin/vitris-motion-view.js +7 -6
  57. package/bin/vitris-references.js +5 -4
  58. package/bin/vitris-refresh.js +8 -7
  59. package/bin/vitris-stock.js +1 -1
  60. package/bin/vitris-templates.js +91 -1
  61. package/bin/vitris-themes.js +7 -6
  62. package/bin/vitris-upgrade.js +8 -7
  63. package/bin/vitris-verify-browser.js +33 -8
  64. package/bin/vitris-verify-performance.js +5 -4
  65. package/bin/vitris.js +17 -16
  66. package/design-system/MASTER.md +4 -0
  67. package/docs/CLI.md +4 -0
  68. package/package.json +2 -2
  69. package/templates/site/site.json +1 -0
  70. package/templates/site/theme/defaults.json +78 -17
@@ -1,3 +1,7 @@
1
+ import {
2
+ typographyCss,
3
+ typographyEnabled
4
+ } from "./chunk-S2XVNTQP.js";
1
5
  import {
2
6
  iconCss,
3
7
  renderIcon
@@ -2573,8 +2577,9 @@ function usedComponents(pages) {
2573
2577
  }
2574
2578
  return [...names];
2575
2579
  }
2576
- function siteCss(pages, catalog) {
2580
+ function siteCss(pages, catalog, site) {
2577
2581
  const chunks = [baseCss, primitivesCss];
2582
+ if (typographyEnabled(site)) chunks.push(typographyCss());
2578
2583
  if (pages.some((page) => page.bodyHtml.includes("data-vitris-icon="))) chunks.push(iconCss);
2579
2584
  for (const name of usedComponents(pages)) {
2580
2585
  const css = catalog[name]?.css;
@@ -12,11 +12,11 @@ import {
12
12
  siteCss,
13
13
  siteIslands,
14
14
  sitemapXml
15
- } from "./chunk-37F7XIMH.js";
15
+ } from "./chunk-JTXFTNEA.js";
16
16
  import {
17
17
  mergeTheme,
18
18
  themeCss
19
- } from "./chunk-LKN75JGA.js";
19
+ } from "./chunk-GE6UUEG2.js";
20
20
  import {
21
21
  escapeHtml
22
22
  } from "./chunk-7FS67SRF.js";
@@ -256,9 +256,9 @@ async function buildSite(input) {
256
256
  };
257
257
  for (const page of compiled) {
258
258
  const js = siteIslands([page], input.catalog);
259
- pageAssets[page.path] = { css: asset(siteCss([page], input.catalog), "css"), js: js ? asset(js, "js") : null };
259
+ pageAssets[page.path] = { css: asset(siteCss([page], input.catalog, site), "css"), js: js ? asset(js, "js") : null };
260
260
  }
261
- if (backendOrigin) pageAssets[COOKIE_COMPLIANCE_PATH] = { css: asset(siteCss([], input.catalog), "css"), js: null };
261
+ if (backendOrigin) pageAssets[COOKIE_COMPLIANCE_PATH] = { css: asset(siteCss([], input.catalog, site), "css"), js: null };
262
262
  const files = compileSite({
263
263
  site,
264
264
  pages: input.pages,
@@ -283,8 +283,8 @@ async function buildSite(input) {
283
283
  await writeFile(dest, connectForms(file.html, backendOrigin, file.path, input.forms || []), "utf8");
284
284
  written.push(rel);
285
285
  }
286
- await writeFile(join(input.outDir, "site.css"), siteCss(compiled, input.catalog), "utf8");
287
- await writeFile(join(input.outDir, "theme.css"), themeCss(theme), "utf8");
286
+ await writeFile(join(input.outDir, "site.css"), siteCss(compiled, input.catalog, site), "utf8");
287
+ await writeFile(join(input.outDir, "theme.css"), themeCss(theme, site.typography), "utf8");
288
288
  if (backendOrigin) {
289
289
  await writeFile(join(input.outDir, "beacon.js"), beaconJs, "utf8");
290
290
  written.push("beacon.js");
@@ -6,7 +6,7 @@ import {
6
6
  selectStageKnowledge,
7
7
  signalsFromSite,
8
8
  siteWidgets
9
- } from "./chunk-GQPUILFR.js";
9
+ } from "./chunk-JPQCUN2J.js";
10
10
  import {
11
11
  coverageReport,
12
12
  loadKnowledgeV2
@@ -7,19 +7,19 @@ import {
7
7
  import {
8
8
  gatherBlankOnboard,
9
9
  persistOnboard
10
- } from "./chunk-54QPYUM2.js";
10
+ } from "./chunk-XJTAR7KM.js";
11
11
  import {
12
12
  persistSiteInputs,
13
13
  recordManagedKit,
14
14
  resolveSiteInputsLive
15
- } from "./chunk-GQPUILFR.js";
15
+ } from "./chunk-JPQCUN2J.js";
16
16
  import {
17
17
  initialState,
18
18
  parseGraphYaml
19
19
  } from "./chunk-BBV7PMFC.js";
20
20
  import {
21
21
  findHarnessRoot
22
- } from "./chunk-H3BS2EBR.js";
22
+ } from "./chunk-QRIYA3GJ.js";
23
23
  import {
24
24
  save
25
25
  } from "./chunk-T6PE4KN4.js";
@@ -115,6 +115,7 @@ async function createSite(opts) {
115
115
  const site = JSON.parse(readFileSync(p, "utf8"));
116
116
  site.slug = slug;
117
117
  site.name = name;
118
+ if (mode === "preserve") delete site.typography;
118
119
  site.siteId = site.siteId || randomUUID();
119
120
  site.sourceFormatVersion = 1;
120
121
  site.capabilities = backendOrigin ? ["analytics"] : [];
@@ -2,10 +2,10 @@ import {
2
2
  buildSite,
3
3
  connectForms,
4
4
  loadSiteDir
5
- } from "./chunk-L44ZTNDM.js";
5
+ } from "./chunk-NMTFSWPK.js";
6
6
  import {
7
7
  leadsJs
8
- } from "./chunk-37F7XIMH.js";
8
+ } from "./chunk-JTXFTNEA.js";
9
9
  import {
10
10
  acquirePathLock,
11
11
  files,
@@ -734,7 +734,7 @@ ${from} ${route.target} 301`;
734
734
  save(join8(stage, "package-manifest.json"), { slug: local.site.slug || "", site: local.site });
735
735
  const kernelParent = resolve3(import.meta.dirname, "..");
736
736
  const kernelSource = existsSync8(join8(kernelParent, "compile")) ? kernelParent : join8(kernelParent, "src");
737
- save(join8(stage, "build-evidence.json"), { version: 2, sourceHash: sourceHash(root), compilerHash: fingerprint(kernelSource, ["compile", "build", "runtime", "forms", "theme.ts"]) });
737
+ save(join8(stage, "build-evidence.json"), { version: 2, sourceHash: sourceHash(root), compilerHash: fingerprint(kernelSource, ["compile", "build", "runtime", "forms", "theme.ts", "typography.ts"]) });
738
738
  const previous = out + `.previous-${Date.now()}-${process.pid}`;
739
739
  if (existsSync8(out)) replacePath(out, previous);
740
740
  try {
@@ -0,0 +1,90 @@
1
+ // src/typography.ts
2
+ var TYPE_ROLES = ["display", "section", "subheading", "card-title", "body", "ui", "caption", "label"];
3
+ var MINIMUM_TYPE_PX = 12;
4
+ var DEFAULT_TYPE_ROLES = {
5
+ display: { size: "clamp(2.75rem, 1.8rem + 4vw, 5rem)", weight: 600, lineHeight: "1.08", tracking: "-0.03em", font: "display" },
6
+ section: { size: "clamp(2rem, 1.5rem + 2vw, 3.5rem)", weight: 600, lineHeight: "1.15", tracking: "-0.025em", font: "display" },
7
+ subheading: { size: "clamp(1.5rem, 1.3rem + 0.8vw, 2rem)", weight: 600, lineHeight: "1.25", tracking: "-0.015em", font: "display" },
8
+ "card-title": { size: "1.5rem", weight: 600, lineHeight: "1.3", tracking: "-0.01em", font: "display" },
9
+ body: { size: "1.125rem", weight: 400, lineHeight: "1.6", tracking: "normal", font: "body" },
10
+ ui: { size: "1rem", weight: 600, lineHeight: "1.5", tracking: "normal", font: "body" },
11
+ caption: { size: "0.875rem", weight: 400, lineHeight: "1.5", tracking: "normal", font: "body" },
12
+ label: { size: "0.875rem", weight: 600, lineHeight: "1.5", tracking: "0.02em", font: "body" }
13
+ };
14
+ var TYPE_SELECTORS = {
15
+ display: ["h1"],
16
+ section: ["h2", ".stat__value", ".stat--sm .stat__value", ".price__amount"],
17
+ subheading: ["h3", ".card__body > h3"],
18
+ "card-title": ["h4", ".card__body > h4", ".brand", ".quote", ".quote--lg", ".quote--card"],
19
+ body: ["body", ".lede", ".section__head .lede", ".lede--lg", ".list-check li", ".rating"],
20
+ ui: ["h5", "h6", "button", "input", "select", "textarea", "nav a", ".nav-links a", "summary", ".accordion summary", ".btn", ".btn-outline", ".btn-ghost", ".btn-arrow", ".skip-link", ".c-cookie-banner__title", ".c-cookie-banner__more", ".announcement a"],
21
+ caption: ["small", "figcaption", "sub", "sup", ".stat__label", ".price__period", ".byline", ".quote cite", ".quote footer", ".announcement", ".c-cookie-banner__text"],
22
+ label: ["label", ".eyebrow", ".badge", ".table th", ".chips > legend", ".chips__label", ".chips span"]
23
+ };
24
+ function typographyEnabled(site) {
25
+ if (site?.typography && site.typography.version !== 1) throw Error("Unsupported typography policy version");
26
+ return site?.typography?.version === 1;
27
+ }
28
+ function resolvedTypography(theme = {}) {
29
+ return Object.fromEntries(TYPE_ROLES.map((role) => [role, { ...DEFAULT_TYPE_ROLES[role], ...theme.typography?.roles?.[role] }]));
30
+ }
31
+ function validateTypography(value, requireRoles = false) {
32
+ const errors = [];
33
+ if (!value || typeof value !== "object" || Array.isArray(value)) return ["Typography requires a shared roles object in theme/defaults.json"];
34
+ const system = value;
35
+ if (!system.roles || typeof system.roles !== "object" || Array.isArray(system.roles)) return ["Typography roles must be an object"];
36
+ if (requireRoles) {
37
+ for (const role of TYPE_ROLES) if (!system.roles[role] || typeof system.roles[role] !== "object" || Object.keys(DEFAULT_TYPE_ROLES[role]).some((key) => !(key in system.roles[role]))) errors.push(`Typography role ${role} must declare size, weight, lineHeight, tracking and font`);
38
+ }
39
+ const check = (style, name) => {
40
+ if (!style || typeof style !== "object" || Array.isArray(style)) {
41
+ errors.push(`Invalid typography style: ${name}`);
42
+ return;
43
+ }
44
+ for (const key of ["size", "lineHeight", "tracking"]) if (style[key] !== void 0 && (typeof style[key] !== "string" || !style[key].trim() || /[;{}<>]/.test(style[key]))) errors.push(`Invalid typography ${name}.${key}`);
45
+ if (style.weight !== void 0 && (!Number.isFinite(style.weight) || style.weight < 1 || style.weight > 1e3)) errors.push(`Invalid typography ${name}.weight`);
46
+ if (style.font !== void 0 && !["body", "display"].includes(style.font)) errors.push(`Invalid typography ${name}.font`);
47
+ for (const key of Object.keys(style)) if (!["size", "weight", "lineHeight", "tracking", "font", "role", "reason"].includes(key)) errors.push(`Unknown typography property ${name}.${key}`);
48
+ };
49
+ for (const [role, style] of Object.entries(system.roles)) {
50
+ if (!TYPE_ROLES.includes(role)) errors.push(`Unknown typography role: ${role}`);
51
+ check(style, role);
52
+ }
53
+ if (system.variants !== void 0 && (!system.variants || typeof system.variants !== "object" || Array.isArray(system.variants))) errors.push("Typography variants must be an object");
54
+ else for (const [name, variant] of Object.entries(system.variants || {})) {
55
+ if (!/^[a-z][a-z0-9-]*$/.test(name) || !variant || !TYPE_ROLES.includes(variant.role) || typeof variant.reason !== "string" || !variant.reason.trim()) errors.push(`Typography variant ${name} needs a known role and reason`);
56
+ check(variant, name);
57
+ }
58
+ return errors;
59
+ }
60
+ function typographyVariables(theme) {
61
+ const errors = theme.typography ? validateTypography(theme.typography) : [];
62
+ if (errors.length) throw Error(errors.join("\n"));
63
+ const roles = resolvedTypography(theme);
64
+ const values = (prefix, style) => ` --type-${prefix}-size: ${style.size};
65
+ --type-${prefix}-weight: ${style.weight};
66
+ --type-${prefix}-leading: ${style.lineHeight};
67
+ --type-${prefix}-tracking: ${style.tracking};
68
+ --type-${prefix}-font: var(--font-${style.font});`;
69
+ return [...TYPE_ROLES.map((role) => values(role, roles[role])), ...Object.entries(theme.typography?.variants || {}).map(([name, variant]) => values(`variant-${name}`, { ...roles[variant.role], ...variant }))].join("\n");
70
+ }
71
+ function typographyCss() {
72
+ const declarations = (name) => `font-size:var(--type-${name}-size);font-weight:var(--type-${name}-weight);line-height:var(--type-${name}-leading);letter-spacing:var(--type-${name}-tracking);font-family:var(--type-${name}-font);font-synthesis:none`;
73
+ return [...TYPE_ROLES.map((role) => `${TYPE_SELECTORS[role].join(",")}{${declarations(role)}}`), ...TYPE_ROLES.map((role) => `[data-type-role="${role}"][data-type-role]{${declarations(role)}}`)].join("\n");
74
+ }
75
+ function typographyVariantCss(theme) {
76
+ return Object.keys(theme.typography?.variants || {}).map((name) => `[data-type-variant="${name}"][data-type-variant][data-type-variant]{font-size:var(--type-variant-${name}-size);font-weight:var(--type-variant-${name}-weight);line-height:var(--type-variant-${name}-leading);letter-spacing:var(--type-variant-${name}-tracking);font-family:var(--type-variant-${name}-font)}`).join("\n");
77
+ }
78
+
79
+ export {
80
+ TYPE_ROLES,
81
+ MINIMUM_TYPE_PX,
82
+ DEFAULT_TYPE_ROLES,
83
+ TYPE_SELECTORS,
84
+ typographyEnabled,
85
+ resolvedTypography,
86
+ validateTypography,
87
+ typographyVariables,
88
+ typographyCss,
89
+ typographyVariantCss
90
+ };
@@ -13,7 +13,7 @@ import {
13
13
  formatExclusion,
14
14
  isErrorPage,
15
15
  isPlaywrightLaunchFailure
16
- } from "./chunk-GQPUILFR.js";
16
+ } from "./chunk-JPQCUN2J.js";
17
17
  import {
18
18
  json,
19
19
  save
@@ -7,7 +7,7 @@ import {
7
7
  sourceRevision,
8
8
  verifyRefine,
9
9
  withMutationLock
10
- } from "./chunk-GQPUILFR.js";
10
+ } from "./chunk-JPQCUN2J.js";
11
11
  import {
12
12
  assertRequestId,
13
13
  parseChangePlan,
@@ -17,7 +17,7 @@ import {
17
17
  import {
18
18
  buildVerified,
19
19
  findHarnessRoot
20
- } from "./chunk-H3BS2EBR.js";
20
+ } from "./chunk-QRIYA3GJ.js";
21
21
  import {
22
22
  files,
23
23
  fingerprint,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-XL2DRAYI.js";
4
4
  import {
5
5
  sourceRevision
6
- } from "./chunk-GQPUILFR.js";
6
+ } from "./chunk-JPQCUN2J.js";
7
7
  import {
8
8
  previewServer
9
9
  } from "./chunk-WEFDVXFX.js";
@@ -22,7 +22,7 @@ import {
22
22
  providerConsoleWarning,
23
23
  readOnlyEmbedProvider,
24
24
  unresolvedCaptureBlockers
25
- } from "./chunk-H3BS2EBR.js";
25
+ } from "./chunk-QRIYA3GJ.js";
26
26
  import {
27
27
  save,
28
28
  within
@@ -3,7 +3,7 @@ import {
3
3
  loadPersistedDesignContextPacket,
4
4
  persistDesignContextArtifacts,
5
5
  resolveDesignContextExpansion
6
- } from "./chunk-GQPUILFR.js";
6
+ } from "./chunk-JPQCUN2J.js";
7
7
  import "./chunk-KVMZSQ3D.js";
8
8
  import "./chunk-WOORR67N.js";
9
9
  import "./chunk-PRLL7B63.js";
@@ -13,15 +13,16 @@ import "./chunk-M4LNHYYR.js";
13
13
  import "./chunk-TY33XMHW.js";
14
14
  import "./chunk-HL6JVA4A.js";
15
15
  import "./chunk-M2ETYSVJ.js";
16
- import "./chunk-AUO5I56E.js";
16
+ import "./chunk-C3F7XQD2.js";
17
17
  import "./chunk-BIEDSEL2.js";
18
18
  import "./chunk-IKNMBGGD.js";
19
19
  import "./chunk-WEFDVXFX.js";
20
- import "./chunk-H3BS2EBR.js";
21
- import "./chunk-L44ZTNDM.js";
22
- import "./chunk-37F7XIMH.js";
23
- import "./chunk-LKN75JGA.js";
20
+ import "./chunk-QRIYA3GJ.js";
21
+ import "./chunk-NMTFSWPK.js";
22
+ import "./chunk-JTXFTNEA.js";
23
+ import "./chunk-GE6UUEG2.js";
24
24
  import "./chunk-YYPYYOEV.js";
25
+ import "./chunk-S2XVNTQP.js";
25
26
  import "./chunk-GZDLXYZV.js";
26
27
  import "./chunk-G7MNMULR.js";
27
28
  import "./chunk-T6PE4KN4.js";
@@ -10,7 +10,7 @@ import {
10
10
  partitionByObligation,
11
11
  rankEvidenceRelevance,
12
12
  selectDiverseEvidence
13
- } from "./chunk-GQPUILFR.js";
13
+ } from "./chunk-JPQCUN2J.js";
14
14
  import "./chunk-KVMZSQ3D.js";
15
15
  import "./chunk-WOORR67N.js";
16
16
  import "./chunk-PRLL7B63.js";
@@ -20,15 +20,16 @@ import "./chunk-M4LNHYYR.js";
20
20
  import "./chunk-TY33XMHW.js";
21
21
  import "./chunk-HL6JVA4A.js";
22
22
  import "./chunk-M2ETYSVJ.js";
23
- import "./chunk-AUO5I56E.js";
23
+ import "./chunk-C3F7XQD2.js";
24
24
  import "./chunk-BIEDSEL2.js";
25
25
  import "./chunk-IKNMBGGD.js";
26
26
  import "./chunk-WEFDVXFX.js";
27
- import "./chunk-H3BS2EBR.js";
28
- import "./chunk-L44ZTNDM.js";
29
- import "./chunk-37F7XIMH.js";
30
- import "./chunk-LKN75JGA.js";
27
+ import "./chunk-QRIYA3GJ.js";
28
+ import "./chunk-NMTFSWPK.js";
29
+ import "./chunk-JTXFTNEA.js";
30
+ import "./chunk-GE6UUEG2.js";
31
31
  import "./chunk-YYPYYOEV.js";
32
+ import "./chunk-S2XVNTQP.js";
32
33
  import "./chunk-GZDLXYZV.js";
33
34
  import "./chunk-G7MNMULR.js";
34
35
  import "./chunk-T6PE4KN4.js";
@@ -30,7 +30,7 @@ import {
30
30
  withMutationLock,
31
31
  withMutationLockAsync,
32
32
  writeCommitJournal
33
- } from "./chunk-GQPUILFR.js";
33
+ } from "./chunk-JPQCUN2J.js";
34
34
  import "./chunk-KVMZSQ3D.js";
35
35
  import "./chunk-WOORR67N.js";
36
36
  import "./chunk-PRLL7B63.js";
@@ -40,15 +40,16 @@ import "./chunk-M4LNHYYR.js";
40
40
  import "./chunk-TY33XMHW.js";
41
41
  import "./chunk-HL6JVA4A.js";
42
42
  import "./chunk-M2ETYSVJ.js";
43
- import "./chunk-AUO5I56E.js";
43
+ import "./chunk-C3F7XQD2.js";
44
44
  import "./chunk-BIEDSEL2.js";
45
45
  import "./chunk-IKNMBGGD.js";
46
46
  import "./chunk-WEFDVXFX.js";
47
- import "./chunk-H3BS2EBR.js";
48
- import "./chunk-L44ZTNDM.js";
49
- import "./chunk-37F7XIMH.js";
50
- import "./chunk-LKN75JGA.js";
47
+ import "./chunk-QRIYA3GJ.js";
48
+ import "./chunk-NMTFSWPK.js";
49
+ import "./chunk-JTXFTNEA.js";
50
+ import "./chunk-GE6UUEG2.js";
51
51
  import "./chunk-YYPYYOEV.js";
52
+ import "./chunk-S2XVNTQP.js";
52
53
  import "./chunk-GZDLXYZV.js";
53
54
  import "./chunk-G7MNMULR.js";
54
55
  import "./chunk-T6PE4KN4.js";
@@ -14997,6 +14997,83 @@ function compileIcons(html2, route2 = "/") {
14997
14997
  return html2;
14998
14998
  }
14999
14999
 
15000
+ // src/typography.ts
15001
+ var TYPE_ROLES = ["display", "section", "subheading", "card-title", "body", "ui", "caption", "label"];
15002
+ var DEFAULT_TYPE_ROLES = {
15003
+ display: { size: "clamp(2.75rem, 1.8rem + 4vw, 5rem)", weight: 600, lineHeight: "1.08", tracking: "-0.03em", font: "display" },
15004
+ section: { size: "clamp(2rem, 1.5rem + 2vw, 3.5rem)", weight: 600, lineHeight: "1.15", tracking: "-0.025em", font: "display" },
15005
+ subheading: { size: "clamp(1.5rem, 1.3rem + 0.8vw, 2rem)", weight: 600, lineHeight: "1.25", tracking: "-0.015em", font: "display" },
15006
+ "card-title": { size: "1.5rem", weight: 600, lineHeight: "1.3", tracking: "-0.01em", font: "display" },
15007
+ body: { size: "1.125rem", weight: 400, lineHeight: "1.6", tracking: "normal", font: "body" },
15008
+ ui: { size: "1rem", weight: 600, lineHeight: "1.5", tracking: "normal", font: "body" },
15009
+ caption: { size: "0.875rem", weight: 400, lineHeight: "1.5", tracking: "normal", font: "body" },
15010
+ label: { size: "0.875rem", weight: 600, lineHeight: "1.5", tracking: "0.02em", font: "body" }
15011
+ };
15012
+ var TYPE_SELECTORS = {
15013
+ display: ["h1"],
15014
+ section: ["h2", ".stat__value", ".stat--sm .stat__value", ".price__amount"],
15015
+ subheading: ["h3", ".card__body > h3"],
15016
+ "card-title": ["h4", ".card__body > h4", ".brand", ".quote", ".quote--lg", ".quote--card"],
15017
+ body: ["body", ".lede", ".section__head .lede", ".lede--lg", ".list-check li", ".rating"],
15018
+ ui: ["h5", "h6", "button", "input", "select", "textarea", "nav a", ".nav-links a", "summary", ".accordion summary", ".btn", ".btn-outline", ".btn-ghost", ".btn-arrow", ".skip-link", ".c-cookie-banner__title", ".c-cookie-banner__more", ".announcement a"],
15019
+ caption: ["small", "figcaption", "sub", "sup", ".stat__label", ".price__period", ".byline", ".quote cite", ".quote footer", ".announcement", ".c-cookie-banner__text"],
15020
+ label: ["label", ".eyebrow", ".badge", ".table th", ".chips > legend", ".chips__label", ".chips span"]
15021
+ };
15022
+ function typographyEnabled(site) {
15023
+ if (site?.typography && site.typography.version !== 1) throw Error("Unsupported typography policy version");
15024
+ return site?.typography?.version === 1;
15025
+ }
15026
+ function resolvedTypography(theme = {}) {
15027
+ return Object.fromEntries(TYPE_ROLES.map((role) => [role, { ...DEFAULT_TYPE_ROLES[role], ...theme.typography?.roles?.[role] }]));
15028
+ }
15029
+ function validateTypography(value, requireRoles = false) {
15030
+ const errors = [];
15031
+ if (!value || typeof value !== "object" || Array.isArray(value)) return ["Typography requires a shared roles object in theme/defaults.json"];
15032
+ const system = value;
15033
+ if (!system.roles || typeof system.roles !== "object" || Array.isArray(system.roles)) return ["Typography roles must be an object"];
15034
+ if (requireRoles) {
15035
+ for (const role of TYPE_ROLES) if (!system.roles[role] || typeof system.roles[role] !== "object" || Object.keys(DEFAULT_TYPE_ROLES[role]).some((key2) => !(key2 in system.roles[role]))) errors.push(`Typography role ${role} must declare size, weight, lineHeight, tracking and font`);
15036
+ }
15037
+ const check = (style, name) => {
15038
+ if (!style || typeof style !== "object" || Array.isArray(style)) {
15039
+ errors.push(`Invalid typography style: ${name}`);
15040
+ return;
15041
+ }
15042
+ for (const key2 of ["size", "lineHeight", "tracking"]) if (style[key2] !== void 0 && (typeof style[key2] !== "string" || !style[key2].trim() || /[;{}<>]/.test(style[key2]))) errors.push(`Invalid typography ${name}.${key2}`);
15043
+ if (style.weight !== void 0 && (!Number.isFinite(style.weight) || style.weight < 1 || style.weight > 1e3)) errors.push(`Invalid typography ${name}.weight`);
15044
+ if (style.font !== void 0 && !["body", "display"].includes(style.font)) errors.push(`Invalid typography ${name}.font`);
15045
+ for (const key2 of Object.keys(style)) if (!["size", "weight", "lineHeight", "tracking", "font", "role", "reason"].includes(key2)) errors.push(`Unknown typography property ${name}.${key2}`);
15046
+ };
15047
+ for (const [role, style] of Object.entries(system.roles)) {
15048
+ if (!TYPE_ROLES.includes(role)) errors.push(`Unknown typography role: ${role}`);
15049
+ check(style, role);
15050
+ }
15051
+ if (system.variants !== void 0 && (!system.variants || typeof system.variants !== "object" || Array.isArray(system.variants))) errors.push("Typography variants must be an object");
15052
+ else for (const [name, variant] of Object.entries(system.variants || {})) {
15053
+ if (!/^[a-z][a-z0-9-]*$/.test(name) || !variant || !TYPE_ROLES.includes(variant.role) || typeof variant.reason !== "string" || !variant.reason.trim()) errors.push(`Typography variant ${name} needs a known role and reason`);
15054
+ check(variant, name);
15055
+ }
15056
+ return errors;
15057
+ }
15058
+ function typographyVariables(theme) {
15059
+ const errors = theme.typography ? validateTypography(theme.typography) : [];
15060
+ if (errors.length) throw Error(errors.join("\n"));
15061
+ const roles = resolvedTypography(theme);
15062
+ const values = (prefix, style) => ` --type-${prefix}-size: ${style.size};
15063
+ --type-${prefix}-weight: ${style.weight};
15064
+ --type-${prefix}-leading: ${style.lineHeight};
15065
+ --type-${prefix}-tracking: ${style.tracking};
15066
+ --type-${prefix}-font: var(--font-${style.font});`;
15067
+ return [...TYPE_ROLES.map((role) => values(role, roles[role])), ...Object.entries(theme.typography?.variants || {}).map(([name, variant]) => values(`variant-${name}`, { ...roles[variant.role], ...variant }))].join("\n");
15068
+ }
15069
+ function typographyCss() {
15070
+ const declarations = (name) => `font-size:var(--type-${name}-size);font-weight:var(--type-${name}-weight);line-height:var(--type-${name}-leading);letter-spacing:var(--type-${name}-tracking);font-family:var(--type-${name}-font);font-synthesis:none`;
15071
+ return [...TYPE_ROLES.map((role) => `${TYPE_SELECTORS[role].join(",")}{${declarations(role)}}`), ...TYPE_ROLES.map((role) => `[data-type-role="${role}"][data-type-role]{${declarations(role)}}`)].join("\n");
15072
+ }
15073
+ function typographyVariantCss(theme) {
15074
+ return Object.keys(theme.typography?.variants || {}).map((name) => `[data-type-variant="${name}"][data-type-variant][data-type-variant]{font-size:var(--type-variant-${name}-size);font-weight:var(--type-variant-${name}-weight);line-height:var(--type-variant-${name}-leading);letter-spacing:var(--type-variant-${name}-tracking);font-family:var(--type-variant-${name}-font)}`).join("\n");
15075
+ }
15076
+
15000
15077
  // src/compile/site.ts
15001
15078
  var baseCss = `*, *::before, *::after { box-sizing: border-box; }
15002
15079
  html {
@@ -15336,8 +15413,9 @@ function usedComponents(pages) {
15336
15413
  }
15337
15414
  return [...names];
15338
15415
  }
15339
- function siteCss(pages, catalog) {
15416
+ function siteCss(pages, catalog, site) {
15340
15417
  const chunks = [baseCss, primitivesCss];
15418
+ if (typographyEnabled(site)) chunks.push(typographyCss());
15341
15419
  if (pages.some((page2) => page2.bodyHtml.includes("data-vitris-icon="))) chunks.push(iconCss);
15342
15420
  for (const name of usedComponents(pages)) {
15343
15421
  const css = catalog[name]?.css;
@@ -15480,8 +15558,9 @@ function mergeTheme(partial) {
15480
15558
  if (!partial?.radiusLg) merged.radiusLg = partial?.radiusLg || defaultTheme.radiusLg;
15481
15559
  return merged;
15482
15560
  }
15483
- function themeCss(tokens) {
15561
+ function themeCss(tokens, typography) {
15484
15562
  const t = mergeTheme(tokens);
15563
+ const sharedType = typography?.version === 1;
15485
15564
  return `:root {
15486
15565
  --color-bg: ${t.colorBg};
15487
15566
  --color-text: ${t.colorText};
@@ -15507,12 +15586,12 @@ function themeCss(tokens) {
15507
15586
  --space-7: 3rem;
15508
15587
  --space-8: 4.5rem;
15509
15588
  --space-9: 6rem;
15510
- --step-0: ${t.typeStep0 || "clamp(0.9rem, 0.85rem + 0.2vw, 1rem)"};
15511
- --step-1: ${t.typeStep1 || "clamp(1rem, 0.95rem + 0.3vw, 1.125rem)"};
15512
- --step-2: ${t.typeStep2 || "clamp(1.2rem, 1.05rem + 0.6vw, 1.5rem)"};
15513
- --step-3: ${t.typeStep3 || "clamp(1.6rem, 1.25rem + 1.2vw, 2.25rem)"};
15589
+ --step-0: ${sharedType ? "var(--type-caption-size)" : t.typeStep0 || "clamp(0.9rem, 0.85rem + 0.2vw, 1rem)"};
15590
+ --step-1: ${sharedType ? "var(--type-body-size)" : t.typeStep1 || "clamp(1rem, 0.95rem + 0.3vw, 1.125rem)"};
15591
+ --step-2: ${sharedType ? "var(--type-subheading-size)" : t.typeStep2 || "clamp(1.2rem, 1.05rem + 0.6vw, 1.5rem)"};
15592
+ --step-3: ${sharedType ? "var(--type-section-size)" : t.typeStep3 || "clamp(1.6rem, 1.25rem + 1.2vw, 2.25rem)"};
15514
15593
  --step-4: ${t.typeStep4 || "clamp(2.1rem, 1.5rem + 2.2vw, 3.5rem)"};
15515
- --step-5: ${t.typeStep5 || "clamp(2.6rem, 1.7rem + 3.4vw, 4.75rem)"};
15594
+ --step-5: ${sharedType ? "var(--type-display-size)" : t.typeStep5 || "clamp(2.6rem, 1.7rem + 3.4vw, 4.75rem)"};
15516
15595
  --tap-min: 44px;
15517
15596
  --header-h: 4.75rem;
15518
15597
  --section-y: ${t.sectionY || "clamp(3.5rem, 3rem + 3vw, 6rem)"};
@@ -15521,8 +15600,8 @@ function themeCss(tokens) {
15521
15600
  --shadow-2: 0 8px 24px color-mix(in srgb, var(--color-text) 8%, transparent);
15522
15601
  --shadow-3: 0 20px 48px color-mix(in srgb, var(--color-text) 12%, transparent);
15523
15602
  --ring: 0 0 0 3px color-mix(in srgb, var(--color-focus) 35%, transparent);
15524
- }
15525
- `;
15603
+ ${sharedType ? typographyVariables(t) + "\n" : ""}}
15604
+ ${sharedType ? typographyVariantCss(t) + "\n" : ""}`;
15526
15605
  }
15527
15606
 
15528
15607
  // src/worker/github.ts
@@ -23629,12 +23708,12 @@ async function renderPreview(env, request) {
23629
23708
  const compiled = compileAllPages(bundle.site, bundle.pages, bundle.catalog, origin);
23630
23709
  if (rest === motionAssetHref.slice(1)) return compiled.some((page3) => page3.usesMotion) ? new Response(motionJs, { headers: { "content-type": "text/javascript; charset=utf-8", "cache-control": "public, max-age=31536000, immutable" } }) : text("Not found", 404);
23631
23710
  if (rest === "site.css") {
23632
- return new Response(siteCss(compiled, bundle.catalog), {
23711
+ return new Response(siteCss(compiled, bundle.catalog, bundle.site), {
23633
23712
  headers: { "content-type": "text/css; charset=utf-8" }
23634
23713
  });
23635
23714
  }
23636
23715
  if (rest === "theme.css") {
23637
- return new Response(themeCss(bundle.theme), {
23716
+ return new Response(themeCss(bundle.theme, bundle.site.typography), {
23638
23717
  headers: { "content-type": "text/css; charset=utf-8" }
23639
23718
  });
23640
23719
  }
@@ -0,0 +1,33 @@
1
+ import type { Page } from 'playwright';
2
+ import type { ThemeTokens } from '../types.ts';
3
+ export declare const TYPOGRAPHY_SCENARIO = "typography-v1-2026-10-28";
4
+ export type TypeObservation = {
5
+ selector: string;
6
+ text: string;
7
+ role: string;
8
+ variant?: string;
9
+ size: number;
10
+ renderedSize: number;
11
+ weight: string;
12
+ lineHeight: string;
13
+ tracking: string;
14
+ family: string;
15
+ };
16
+ export type TypographyCapture = {
17
+ path: string;
18
+ width: number;
19
+ state: string;
20
+ scenario: string;
21
+ observations: TypeObservation[];
22
+ failures: string[];
23
+ };
24
+ /** A serialized callback keeps browser DOM types out of the Worker/compiler TS program. */
25
+ export declare function captureTypography(page: Page, theme: Partial<ThemeTokens>, path: string, width: number, state: string): Promise<TypographyCapture>;
26
+ export declare function typographyConsistencyFailures(captures: TypographyCapture[]): string[];
27
+ export declare function typographyEvidenceFailures(report: {
28
+ scenario: string;
29
+ captures: TypographyCapture[];
30
+ failures: string[];
31
+ } | undefined, routes: string[]): string[];
32
+ /** Exercise ordinary local disclosure states without submitting forms or following links. */
33
+ export declare function captureTypographyStates(page: Page, theme: Partial<ThemeTokens>, path: string, width: number): Promise<TypographyCapture[]>;
@@ -22,6 +22,7 @@ export declare function suiteIdentityRoles(id: string): {
22
22
  typeStep2?: string;
23
23
  typeStep3?: string;
24
24
  typeStep4?: string;
25
+ typography?: import("../typography.ts").TypographySystem;
25
26
  sectionY?: string;
26
27
  };
27
28
  mobileDisplay: string;
@@ -0,0 +1,6 @@
1
+ import type { ThemeTokens } from '../types.ts';
2
+ export declare function typographyPolicy(root: string): {
3
+ enabled: boolean;
4
+ theme: Partial<ThemeTokens>;
5
+ };
6
+ export declare function typographyDirectionFailures(root: string): string[];
@@ -4,7 +4,7 @@ export { beaconJs };
4
4
  export declare const baseCss = "*, *::before, *::after { box-sizing: border-box; }\nhtml {\n -webkit-text-size-adjust: 100%;\n scroll-padding-top: calc(var(--header-h) + var(--space-3));\n}\nbody {\n margin: 0;\n font-family: var(--font-body);\n font-size: var(--step-1);\n background: var(--color-bg);\n color: var(--color-text);\n line-height: 1.6;\n}\nh1, h2, h3, h4 {\n font-family: var(--font-display);\n font-weight: 600;\n margin: 0;\n text-wrap: balance;\n overflow-wrap: anywhere;\n}\nh1 { font-size: var(--step-5); line-height: 1.05; letter-spacing: -0.03em; }\nh2 { font-size: var(--step-3); line-height: 1.15; letter-spacing: -0.025em; }\nh3 { font-size: var(--step-2); line-height: 1.25; letter-spacing: -0.015em; }\nh4 { font-size: var(--step-1); line-height: 1.3; letter-spacing: -0.01em; }\np { margin: 0 0 var(--space-4); text-wrap: pretty; }\np:last-child { margin-bottom: 0; }\na { color: inherit; }\na:hover { text-decoration-color: var(--color-accent); }\nimg { max-width: 100%; height: auto; display: block; }\nsvg { display: inline-block; vertical-align: middle; }\nul, ol { margin: 0 0 var(--space-4); padding-inline-start: 1.25rem; }\nul:last-child, ol:last-child { margin-bottom: 0; }\nsmall { font-size: var(--step-0); }\n:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 3px;\n}\n.skip-link {\n position: absolute;\n left: var(--space-4);\n top: var(--space-4);\n z-index: 100;\n padding: var(--space-3) var(--space-4);\n background: var(--color-surface);\n color: var(--color-text);\n border: 1px solid var(--color-border);\n border-radius: var(--radius);\n text-decoration: none;\n font-weight: 600;\n transform: translateY(-160%);\n transition: transform 160ms ease;\n}\n.skip-link:focus {\n transform: translateY(0);\n}\n.visually-hidden {\n position: absolute !important;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n.wrap {\n width: min(var(--max-width), calc(100% - 2rem));\n margin-inline: auto;\n}\n@media (min-width: 720px) {\n .wrap { width: min(var(--max-width), calc(100% - 2.5rem)); }\n}\n.btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n min-height: var(--tap-min);\n padding: 0.75rem 1.5rem;\n line-height: 1.2;\n white-space: nowrap;\n background: var(--color-accent);\n color: var(--color-accent-text);\n text-decoration: none;\n border: 1px solid transparent;\n border-radius: var(--radius-pill);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease;\n}\n.btn:hover {\n background: color-mix(in srgb, var(--color-accent) 88%, var(--color-text));\n}\n.btn-outline {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n min-height: var(--tap-min);\n padding: 0.75rem 1.5rem;\n line-height: 1.2;\n white-space: nowrap;\n background: transparent;\n color: var(--color-text);\n text-decoration: none;\n border: 1px solid var(--color-border);\n border-radius: var(--radius-pill);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease;\n}\n.btn-outline:hover {\n border-color: var(--color-text);\n background: color-mix(in srgb, var(--color-text) 6%, transparent);\n}\n.btn-arrow {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n min-height: var(--tap-min);\n white-space: nowrap;\n font-weight: 600;\n text-decoration: none;\n color: var(--color-text);\n transition: color 180ms ease;\n}\n.btn-arrow::after {\n content: \"\u2192\";\n transition: transform 180ms ease;\n}\n.btn-arrow:hover {\n color: var(--color-accent);\n}\n.btn-arrow:hover::after { transform: translateX(3px); }\n.btn-ghost {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n min-height: var(--tap-min);\n padding: 0.75rem 1.5rem;\n line-height: 1.2;\n white-space: nowrap;\n background: transparent;\n color: var(--color-text);\n text-decoration: none;\n border: 1px solid transparent;\n border-radius: var(--radius-pill);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: border-color 180ms ease, color 180ms ease, background-color 180ms ease;\n}\n.btn-ghost:hover {\n background: color-mix(in srgb, var(--color-text) 6%, transparent);\n}\n.c-cookie-banner {\n position: fixed;\n inset-inline: 0;\n bottom: 0;\n z-index: 200;\n padding: var(--space-4) 0 calc(var(--space-4) + env(safe-area-inset-bottom, 0px));\n background: var(--color-surface);\n color: var(--color-text);\n border-top: 1px solid var(--color-border);\n box-shadow: 0 -8px 24px color-mix(in srgb, var(--color-text) 8%, transparent);\n}\n.c-cookie-banner__inner {\n display: flex;\n flex-direction: column;\n gap: var(--space-4);\n}\n.c-cookie-banner__title {\n margin: 0 0 var(--space-2);\n font-family: var(--font-display);\n font-size: var(--step-1);\n font-weight: 600;\n letter-spacing: -0.02em;\n line-height: 1.25;\n}\n.c-cookie-banner__text {\n margin: 0;\n font-size: var(--step-0);\n color: var(--color-muted);\n max-width: 42rem;\n}\n.c-cookie-banner__actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-3);\n}\n.c-cookie-banner__more {\n min-height: var(--tap-min);\n display: inline-flex;\n align-items: center;\n font-weight: 600;\n font-size: var(--step-0);\n color: var(--color-text);\n text-decoration: underline;\n text-underline-offset: 0.15em;\n cursor: pointer;\n}\n.c-cookie-banner__more:hover { color: var(--color-accent); }\n@media (max-width: 719px) {\n .c-cookie-banner {\n /* A delayed first-visit beacon must not insert a banner into document flow. */\n max-height: 50dvh;\n overflow-y: auto;\n overscroll-behavior: contain;\n }\n}\n@media (min-width: 720px) {\n .c-cookie-banner__inner {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-5);\n }\n .c-cookie-banner__actions { flex-shrink: 0; }\n}\n.c-lead-ok {\n background: var(--color-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius);\n padding: var(--space-3) var(--space-4);\n margin: 0 0 var(--space-4);\n color: var(--color-text);\n}\n.c-lead-hp {\n position: absolute;\n left: -9999px;\n width: 1px;\n height: 1px;\n overflow: hidden;\n}\n.c-cookie-page {\n padding: var(--space-7) 0 var(--space-8);\n}\n.c-cookie-page h1 {\n margin-bottom: var(--space-3);\n}\n.c-cookie-page h2 {\n margin: var(--space-6) 0 var(--space-3);\n}\n.c-cookie-page .wrap { max-width: 42rem; }\n.c-cookie-page p { color: var(--color-muted); }\n.c-cookie-page__actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-3);\n margin-top: var(--space-5);\n}\n\n/* Scroll reveal \u2014 armed only after islands mark above-fold .is-in */\n[data-reveal] {\n --reveal-x: 0;\n --reveal-y: var(--reveal-dist, 16px);\n}\n[data-reveal-strength=\"whisper\"] {\n --reveal-dist: 8px;\n --reveal-dur: 400ms;\n --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);\n}\n[data-reveal-strength=\"editorial\"] {\n --reveal-dist: 16px;\n --reveal-dur: 550ms;\n --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);\n}\n[data-reveal-strength=\"noticeable\"] {\n --reveal-dist: 24px;\n --reveal-dur: 650ms;\n --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);\n}\n[data-reveal-from=\"up\"] { --reveal-x: 0; --reveal-y: var(--reveal-dist, 16px); }\n[data-reveal-from=\"down\"] { --reveal-x: 0; --reveal-y: calc(var(--reveal-dist, 16px) * -1); }\n[data-reveal-from=\"left\"] { --reveal-x: calc(var(--reveal-dist, 16px) * -1); --reveal-y: 0; }\n[data-reveal-from=\"right\"] { --reveal-x: var(--reveal-dist, 16px); --reveal-y: 0; }\n[data-reveal-child][data-reveal-from=\"up\"] {\n --reveal-child-x: 0;\n --reveal-child-y: var(--reveal-dist, 16px);\n}\n[data-reveal-child][data-reveal-from=\"down\"] {\n --reveal-child-x: 0;\n --reveal-child-y: calc(var(--reveal-dist, 16px) * -1);\n}\n[data-reveal-child][data-reveal-from=\"left\"] {\n --reveal-child-x: calc(var(--reveal-dist, 16px) * -1);\n --reveal-child-y: 0;\n}\n[data-reveal-child][data-reveal-from=\"right\"] {\n --reveal-child-x: var(--reveal-dist, 16px);\n --reveal-child-y: 0;\n}\n@media (prefers-reduced-motion: reduce) {\n *, *::before, *::after { animation: none !important; transition: none !important; }\n}\n";
5
5
  export declare function compilePage(page: PageSource, catalog: Catalog, site: SiteConfig, kernelOrigin: string): CompiledPage;
6
6
  export declare function usedComponents(pages: CompiledPage[]): string[];
7
- export declare function siteCss(pages: CompiledPage[], catalog: Catalog): string;
7
+ export declare function siteCss(pages: CompiledPage[], catalog: Catalog, site?: SiteConfig): string;
8
8
  export declare function siteIslands(pages: CompiledPage[], catalog: Catalog): string;
9
9
  export declare function renderDocument(opts: {
10
10
  site: SiteConfig;
@@ -1,3 +1,5 @@
1
1
  import type { ThemeTokens } from "./types.ts";
2
2
  export declare function mergeTheme(partial?: Partial<ThemeTokens> | null): ThemeTokens;
3
- export declare function themeCss(tokens: ThemeTokens): string;
3
+ export declare function themeCss(tokens: ThemeTokens, typography?: {
4
+ version: 1;
5
+ }): string;
@@ -26,6 +26,10 @@ export type SiteConfig = {
26
26
  version: 2;
27
27
  routes: "all-authored" | string[];
28
28
  };
29
+ /** Shared type roles and rendered acceptance; omitted on historical sites. */
30
+ typography?: {
31
+ version: 1;
32
+ };
29
33
  /** Design-run instrumentation; omitted from ordinary single-site authoring. */
30
34
  designRun?: {
31
35
  runId?: string;
@@ -61,13 +65,14 @@ export type ThemeTokens = {
61
65
  /** Pill radius for `.btn` / `.btn-outline`. Cards keep `radius`. */
62
66
  radiusPill: string;
63
67
  maxWidth: string;
64
- /** Opt-in type scale and rhythm. Omitted values retain legacy compiler defaults. */
68
+ /** Legacy scale and rhythm. Typography v1 resolves steps 0–3/5 from shared roles instead. */
65
69
  typeStep0?: string;
66
70
  typeStep1?: string;
67
71
  typeStep2?: string;
68
72
  typeStep3?: string;
69
73
  typeStep4?: string;
70
74
  typeStep5?: string;
75
+ typography?: import('./typography.ts').TypographySystem;
71
76
  sectionY?: string;
72
77
  };
73
78
  /** @deprecated Editor-era content tree — dormant patch/workflow only. */