@domandigital/craft 0.18.2 → 0.19.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.
@@ -4,15 +4,15 @@ import {
4
4
  merge,
5
5
  resolvePages,
6
6
  runAudit
7
- } from "./chunk-DARKHE6A.js";
8
- import "./chunk-SL5VZJ2J.js";
7
+ } from "./chunk-J35UYAVV.js";
8
+ import "./chunk-W7PMRSDG.js";
9
9
  import "./chunk-JYNXGCYZ.js";
10
- import "./chunk-ZUYERAFM.js";
10
+ import "./chunk-NCBCZFDY.js";
11
11
  import "./chunk-OQXTQQAU.js";
12
- import "./chunk-O5B5ELJH.js";
12
+ import "./chunk-GNVZGG3E.js";
13
13
  import "./chunk-QIEDBL4J.js";
14
14
  import "./chunk-WUACK2HX.js";
15
- import "./chunk-WFB6F3BN.js";
15
+ import "./chunk-IHT6CJOM.js";
16
16
  import "./chunk-4QWEAL4G.js";
17
17
  import "./chunk-QYBAVU3P.js";
18
18
  export {
package/dist/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  run
4
- } from "./chunk-O5B5ELJH.js";
4
+ } from "./chunk-GNVZGG3E.js";
5
5
  import "./chunk-QIEDBL4J.js";
6
- import "./chunk-WFB6F3BN.js";
6
+ import "./chunk-IHT6CJOM.js";
7
7
  import "./chunk-4QWEAL4G.js";
8
8
  import "./chunk-QYBAVU3P.js";
9
9
 
package/dist/index.cjs CHANGED
@@ -74,6 +74,7 @@ __export(index_exports, {
74
74
  MOOD: () => MOOD,
75
75
  NEGATIVE_REASSURANCE: () => NEGATIVE_REASSURANCE,
76
76
  NULL_VERSION: () => NULL_VERSION,
77
+ OPENING_BANDS: () => OPENING_BANDS,
77
78
  PAGE_TYPES: () => PAGE_TYPES,
78
79
  PILL_LIMIT: () => PILL_LIMIT,
79
80
  PLAINER_WORDS: () => PLAINER_WORDS,
@@ -117,6 +118,9 @@ __export(index_exports, {
117
118
  STOCK_PHRASES: () => STOCK_PHRASES,
118
119
  STUDY_WORDS: () => STUDY_WORDS,
119
120
  TELL_HEAVY: () => TELL_HEAVY,
121
+ TEMPERATURE_FULL: () => TEMPERATURE_FULL,
122
+ TEMPERATURE_NAMED: () => TEMPERATURE_NAMED,
123
+ TEMPERATURE_WEIGHT: () => TEMPERATURE_WEIGHT,
120
124
  TRADE_NOUNS: () => TRADE_NOUNS,
121
125
  TYPE_STEPS: () => TYPE_STEPS,
122
126
  TYPICAL_AT: () => TYPICAL_AT,
@@ -128,6 +132,7 @@ __export(index_exports, {
128
132
  applyHouseGate: () => applyHouseGate,
129
133
  applySeverity: () => applySeverity,
130
134
  auditSnapshot: () => auditSnapshot,
135
+ bandOf: () => bandOf,
131
136
  briefTerms: () => briefTerms,
132
137
  calibrate: () => calibrate,
133
138
  catalogueTable: () => catalogueTable,
@@ -191,10 +196,14 @@ __export(index_exports, {
191
196
  globToRegExp: () => globToRegExp,
192
197
  gradientContrast: () => gradientContrast,
193
198
  gradientStops: () => gradientStops,
199
+ groundDistance: () => groundDistance,
200
+ groundLabel: () => groundLabel,
194
201
  groundName: () => groundName,
202
+ groundTemperature: () => groundTemperature,
195
203
  harvest: () => harvest,
196
204
  hedgeCount: () => hedgeCount,
197
205
  hedgedClose: () => hedgedClose,
206
+ heroDistance: () => heroDistance,
198
207
  hexToOklch: () => hexToOklch,
199
208
  holdHouseBlocks: () => holdHouseBlocks,
200
209
  houseRule: () => houseRule,
@@ -242,6 +251,8 @@ __export(index_exports, {
242
251
  renderedOrder: () => renderedOrder,
243
252
  researchPrompt: () => researchPrompt,
244
253
  retrofitPlan: () => retrofitPlan,
254
+ rhythmDistance: () => rhythmDistance,
255
+ rhythmOf: () => rhythmOf,
245
256
  runTell: () => runTell,
246
257
  scanProvenance: () => scanProvenance,
247
258
  scanSource: () => scanSource,
@@ -268,6 +279,7 @@ __export(index_exports, {
268
279
  tailwindV3Preset: () => tailwindV3Preset,
269
280
  tailwindV4Theme: () => tailwindV4Theme,
270
281
  tellById: () => tellById,
282
+ temperatureName: () => temperatureName,
271
283
  toGamut: () => toGamut,
272
284
  toJson: () => toJson,
273
285
  toSarif: () => toSarif,
@@ -5321,7 +5333,7 @@ var RENDERED_PATHS = {
5321
5333
  };
5322
5334
 
5323
5335
  // src/fingerprint/index.ts
5324
- var FINGERPRINT_VERSION = 2;
5336
+ var FINGERPRINT_VERSION = 3;
5325
5337
  var CHROMATIC = 0.03;
5326
5338
  var MIN_ACCENT_AREA = 2e4;
5327
5339
  var oklch = (value) => {
@@ -5357,6 +5369,24 @@ function median(values) {
5357
5369
  const mid = Math.floor(sorted.length / 2);
5358
5370
  return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
5359
5371
  }
5372
+ var FULL_WIDTH_IMAGE = 0.8;
5373
+ function heroOf(s) {
5374
+ const h1 = s.headings.find((h) => h.level === 1);
5375
+ const inside = (top, x) => top >= x.top && top < x.top + x.height;
5376
+ const index = (() => {
5377
+ const withH1 = h1 ? s.sections.findIndex((x) => x.kind === "hero" && inside(h1.top, x)) : -1;
5378
+ return withH1 >= 0 ? withH1 : s.sections.findIndex((x) => x.kind === "hero");
5379
+ })();
5380
+ if (index < 0) return void 0;
5381
+ const section = s.sections[index];
5382
+ const headline = h1 && inside(h1.top, section) ? { sizePx: h1.sizePx, weight: h1.weight, class: fontClass(normaliseFamily(h1.family)), italic: h1.italic || h1.italicPart } : null;
5383
+ if (!s.images) return { headline };
5384
+ const picture2 = s.images.filter((i) => (i.role === "photo" || i.role === "illustration") && (i.section === index || i.section === null && inside(i.top, section))).sort((a, b) => b.width * b.height - a.width * a.height)[0];
5385
+ if (!picture2) return { headline, image: "none" };
5386
+ if (picture2.left === void 0) return { headline };
5387
+ const image = picture2.width >= FULL_WIDTH_IMAGE * s.viewport.width ? "full" : picture2.left + picture2.width / 2 < s.viewport.width / 2 ? "left" : "right";
5388
+ return { headline, image };
5389
+ }
5360
5390
  function fingerprint(s) {
5361
5391
  const effects = [];
5362
5392
  if (s.effects.glass > 0) effects.push("glass-panel");
@@ -5368,6 +5398,7 @@ function fingerprint(s) {
5368
5398
  if (s.effects.eyebrowChip) effects.push("hero-eyebrow-chip");
5369
5399
  if (s.motion.introOverlay) effects.push("intro-cinematic");
5370
5400
  const below = s.sections.filter((x) => x.top > s.viewport.height).length;
5401
+ const hero = heroOf(s);
5371
5402
  return {
5372
5403
  version: FINGERPRINT_VERSION,
5373
5404
  accent: accentOf(s),
@@ -5391,10 +5422,28 @@ function fingerprint(s) {
5391
5422
  carousel: x.carousel ?? false,
5392
5423
  figures: x.figures ?? 0
5393
5424
  }))
5394
- } : {}
5425
+ } : {},
5426
+ ...hero ? { hero } : {}
5395
5427
  };
5396
5428
  }
5397
- var DISTANCE_WEIGHTS = { accent: 0.25, type: 0.25, ground: 0.1, shape: 0.1, motion: 0.1, effects: 0.1, layout: 0.1 };
5429
+ var WARM_HUE = [
5430
+ [330, 360],
5431
+ [0, 130]
5432
+ ];
5433
+ var COOL_HUE = [[160, 310]];
5434
+ var TEMPERATURE_FULL = 6e-3;
5435
+ var TEMPERATURE_NAMED = 0.4;
5436
+ function groundTemperature(o) {
5437
+ const h = (o.h % 360 + 360) % 360;
5438
+ const within = (bands) => bands.some(([lo, hi]) => h >= lo && h < hi);
5439
+ const strength = Math.min(1, o.c / TEMPERATURE_FULL);
5440
+ return within(WARM_HUE) ? strength : within(COOL_HUE) ? -strength : 0;
5441
+ }
5442
+ function temperatureName(o) {
5443
+ const t = groundTemperature(o);
5444
+ return t >= TEMPERATURE_NAMED ? "warm" : t <= -TEMPERATURE_NAMED ? "cool" : "neutral";
5445
+ }
5446
+ var DISTANCE_WEIGHTS = { accent: 0.2, type: 0.2, ground: 0.1, shape: 0.1, motion: 0.05, effects: 0.1, layout: 0.1, rhythm: 0.1, hero: 0.05 };
5398
5447
  function levenshtein(a, b, cost = (x, y) => x === y ? 0 : 1) {
5399
5448
  const row = Array.from({ length: b.length + 1 }, (_, i) => i);
5400
5449
  for (let i = 1; i <= a.length; i += 1) {
@@ -5421,18 +5470,62 @@ function layoutDistance(a, b) {
5421
5470
  return longest === 0 ? 0 : levenshtein(a.layout, b.layout) / longest;
5422
5471
  }
5423
5472
  var clamp = (n) => Math.max(0, Math.min(1, n));
5473
+ function bandOf(l) {
5474
+ return l < 0.4 ? "dark" : l >= 0.75 ? "light" : "mid";
5475
+ }
5476
+ function rhythmOf(fp) {
5477
+ if (!fp.sections) return null;
5478
+ const out = [];
5479
+ for (const s of fp.sections) {
5480
+ if (s.background === void 0) return null;
5481
+ const band = bandOf((s.background ?? fp.ground).l);
5482
+ if (out[out.length - 1] !== band) out.push(band);
5483
+ }
5484
+ return out;
5485
+ }
5486
+ var OPENING_BANDS = 3;
5487
+ function rhythmDistance(a, b) {
5488
+ const ra = rhythmOf(a);
5489
+ const rb = rhythmOf(b);
5490
+ if (!ra || !rb) return null;
5491
+ const over = (x, y) => {
5492
+ const longest = Math.max(x.length, y.length);
5493
+ return longest === 0 ? 0 : levenshtein(x, y) / longest;
5494
+ };
5495
+ return (over(ra.slice(0, OPENING_BANDS), rb.slice(0, OPENING_BANDS)) + over(ra, rb)) / 2;
5496
+ }
5497
+ var HEADLINE_OCTAVES = 1;
5498
+ function heroSection(fp) {
5499
+ return fp.sections?.find((s) => s.role === "hero");
5500
+ }
5501
+ function heroDistance(a, b) {
5502
+ const sa = heroSection(a);
5503
+ const sb = heroSection(b);
5504
+ if (!sa || !sb) return null;
5505
+ const parts2 = [Math.abs(sa.centred - sb.centred), clamp(Math.abs(sa.width - sb.width) / 0.5)];
5506
+ const ha = a.hero?.headline;
5507
+ const hb = b.hero?.headline;
5508
+ if (ha && hb) parts2.push(0.5 * (ha.class === hb.class ? 0 : 1) + 0.5 * clamp(Math.abs(Math.log2(ha.sizePx / hb.sizePx)) / HEADLINE_OCTAVES));
5509
+ else if (a.hero && b.hero && (ha || hb)) parts2.push(1);
5510
+ if (a.hero?.image && b.hero?.image) parts2.push(a.hero.image === b.hero.image ? 0 : 1);
5511
+ return parts2.reduce((s, p) => s + p, 0) / parts2.length;
5512
+ }
5424
5513
  function faceDistance(a, b) {
5425
5514
  if (a.family.toLowerCase() === b.family.toLowerCase()) return 0;
5426
5515
  return a.class === b.class ? 0.5 : 1;
5427
5516
  }
5428
5517
  var ACCENT_SCALE = 0.3;
5429
5518
  var GROUND_SCALE = 0.15;
5519
+ var TEMPERATURE_WEIGHT = 0.6;
5430
5520
  var SHARED_PART = 0.2;
5431
5521
  var ACCENT_SHARED = ACCENT_SCALE * SHARED_PART;
5432
5522
  var GROUND_SHARED = GROUND_SCALE * SHARED_PART;
5523
+ function groundDistance(a, b) {
5524
+ return clamp(deltaEOk(a, b) / GROUND_SCALE + TEMPERATURE_WEIGHT * Math.abs(groundTemperature(a) - groundTemperature(b)) / 2);
5525
+ }
5433
5526
  function fingerprintDistance(a, b) {
5434
5527
  const accent = a.accent && b.accent ? clamp(deltaEOk(a.accent, b.accent) / ACCENT_SCALE) : a.accent || b.accent ? 1 : 0;
5435
- const ground = clamp(deltaEOk(a.ground, b.ground) / GROUND_SCALE);
5528
+ const ground = groundDistance(a.ground, b.ground);
5436
5529
  const type = 0.6 * faceDistance(a.display, b.display) + 0.4 * faceDistance(a.body, b.body);
5437
5530
  const shape = a.roundness === null || b.roundness === null ? a.roundness === b.roundness ? 0 : 0.5 : clamp(Math.abs(a.roundness - b.roundness) / 0.5);
5438
5531
  const motion = clamp(Math.abs(a.motion - b.motion));
@@ -5440,9 +5533,18 @@ function fingerprintDistance(a, b) {
5440
5533
  const shared = a.effects.filter((e) => b.effects.includes(e)).length;
5441
5534
  const effects = union.size === 0 ? 0 : 1 - shared / union.size;
5442
5535
  const layout = layoutDistance(a, b);
5443
- const parts2 = { accent, ground, type, shape, motion, effects, layout };
5444
- const total = Object.keys(DISTANCE_WEIGHTS).reduce((sum, k) => sum + DISTANCE_WEIGHTS[k] * parts2[k], 0);
5445
- return { total: Math.round(total * 1e3) / 1e3, parts: parts2 };
5536
+ const rhythm = rhythmDistance(a, b);
5537
+ const hero = heroDistance(a, b);
5538
+ const parts2 = { accent, ground, type, shape, motion, effects, layout, rhythm, hero };
5539
+ let sum = 0;
5540
+ let weight = 0;
5541
+ for (const k of Object.keys(DISTANCE_WEIGHTS)) {
5542
+ const p = parts2[k];
5543
+ if (p === null) continue;
5544
+ sum += DISTANCE_WEIGHTS[k] * p;
5545
+ weight += DISTANCE_WEIGHTS[k];
5546
+ }
5547
+ return { total: Math.round(sum / weight * 1e3) / 1e3, parts: parts2 };
5446
5548
  }
5447
5549
  function nearest(target2, set, k = set.length) {
5448
5550
  return set.map((item) => ({ id: item.id, distance: fingerprintDistance(target2, item.fingerprint) })).sort((x, y) => x.distance.total - y.distance.total).slice(0, k);
@@ -5520,6 +5622,12 @@ function groundName(o) {
5520
5622
  if (o.l >= 0.97 && o.c < 0.012) return "white";
5521
5623
  return o.c < 0.02 ? "grey" : "tinted";
5522
5624
  }
5625
+ function groundLabel(o) {
5626
+ const name = groundName(o);
5627
+ if (name === "cream" || name === "tinted") return name;
5628
+ const temperature = temperatureName(o);
5629
+ return temperature === "neutral" ? name : `${temperature} ${name}`;
5630
+ }
5523
5631
  function shapeName(roundness) {
5524
5632
  if (roundness === null) return "no buttons";
5525
5633
  if (roundness >= 0.45) return "pill";
@@ -8794,6 +8902,33 @@ var LICENCES = [
8794
8902
  checked: "2026-09-24",
8795
8903
  source: "https://iconscout.com/licenses",
8796
8904
  note: "Each licence covers a single end product. Never in a tool, template or shared kit (create-site, craft, a house component library), even modified. Record the assets each site uses. Assets come from different contributors, so take one site's set from one contributor."
8905
+ },
8906
+ {
8907
+ name: "Saira Semi Condensed",
8908
+ subject: "face",
8909
+ licence: "OFL-1.1",
8910
+ multiClient: "yes",
8911
+ checked: "2026-10-09",
8912
+ source: "https://github.com/google/fonts/blob/main/ofl/sairasemicondensed/OFL.txt",
8913
+ note: "Copyright 2016 The Saira Project Authors (omnibus.type@gmail.com), reserved font name Saira. Keep the OFL notice with the font files; a modified build may not be called Saira. Chosen for Doman Digital's own site (DOM-397, 9 October 2026)."
8914
+ },
8915
+ {
8916
+ name: "Public Sans",
8917
+ subject: "face",
8918
+ licence: "OFL-1.1",
8919
+ multiClient: "yes",
8920
+ checked: "2026-10-09",
8921
+ source: "https://github.com/google/fonts/blob/main/ofl/publicsans/OFL.txt",
8922
+ note: "Copyright 2015 The Public Sans Project Authors (https://github.com/uswds/public-sans). Keep the OFL notice with the font files. Chosen for Doman Digital's own site (DOM-397, 9 October 2026)."
8923
+ },
8924
+ {
8925
+ name: "Martian Mono",
8926
+ subject: "face",
8927
+ licence: "OFL-1.1",
8928
+ multiClient: "yes",
8929
+ checked: "2026-10-09",
8930
+ source: "https://github.com/google/fonts/blob/main/ofl/martianmono/OFL.txt",
8931
+ note: "Copyright 2021 The Martian Mono Project Authors (https://github.com/evilmartians/mono). Keep the OFL notice with the font files. Chosen for Doman Digital's own site (DOM-397, 9 October 2026)."
8797
8932
  }
8798
8933
  ];
8799
8934
  function faceLicence(family) {
@@ -9160,7 +9295,7 @@ var PART_AREA = {
9160
9295
  effects: { area: "effects", choice: "motif" },
9161
9296
  motion: { area: "motion", choice: "signature" }
9162
9297
  };
9163
- var DEFAULTS = /^(?:ground )?(?:white|grey)(?: ground)?$|no scroll reveals|no reveals|accent (?:none|neutral)|^no buttons|shape no buttons/;
9298
+ var DEFAULTS = /^(?:ground )?(?:white|grey|light)(?: ground)?$|no scroll reveals|no reveals|accent (?:none|neutral)|^no buttons|shape no buttons|^light page throughout$/;
9164
9299
  function designFindings(findings, copyTells) {
9165
9300
  return (findings?.findings ?? []).filter((f4) => !copyTells.has(f4.tell));
9166
9301
  }
@@ -9267,7 +9402,7 @@ function areaOfShared(part) {
9267
9402
  if (/accent|ground/.test(part)) return "colour";
9268
9403
  if (/buttons/.test(part)) return "shape";
9269
9404
  if (/reveal/.test(part)) return "motion";
9270
- if (/running order/.test(part)) return "layout";
9405
+ if (/running order|hero|page throughout/.test(part)) return "layout";
9271
9406
  return "effects";
9272
9407
  }
9273
9408
  function choiceOfShared(part) {
@@ -9277,7 +9412,7 @@ function choiceOfShared(part) {
9277
9412
  if (/ground/.test(part)) return "ground";
9278
9413
  if (/buttons/.test(part)) return "shape";
9279
9414
  if (/reveal/.test(part)) return "signature";
9280
- return /running order/.test(part) ? void 0 : "motif";
9415
+ return /running order|hero|page throughout/.test(part) ? void 0 : "motif";
9281
9416
  }
9282
9417
 
9283
9418
  // src/calibrate/index.ts
@@ -9384,19 +9519,45 @@ function addSite(register, site) {
9384
9519
  const sites = [...register.sites.filter((s) => s.id !== site.id), site].sort((a, b) => a.id.localeCompare(b.id));
9385
9520
  return { version: ESTATE_VERSION, sites };
9386
9521
  }
9522
+ function sharedGround(a, b) {
9523
+ const [x, y] = [groundLabel(a.ground), groundLabel(b.ground)];
9524
+ if (x === y) return `${x} ground`;
9525
+ const temperature = temperatureName(a.ground);
9526
+ if (temperature !== "neutral" && temperature === temperatureName(b.ground)) return `${temperature} ground`;
9527
+ return `${a.ground.l < 0.3 ? "dark" : "light"} ground`;
9528
+ }
9529
+ function sharedRhythm(a, b) {
9530
+ const bands = rhythmOf(a) ?? [];
9531
+ const word = (band) => band;
9532
+ if (bands.length <= 1) return `${bands[0] ? word(bands[0]) : "light"} page throughout`;
9533
+ const second = bands[1] === "mid" ? "mid-tone band" : `${word(bands[1])} band`;
9534
+ return `${word(bands[0])} hero then ${second}`;
9535
+ }
9536
+ function sharedHero(a, b) {
9537
+ const hero = a.sections?.find((s) => s.role === "hero");
9538
+ const placement = hero && hero.centred >= 0.5 ? "centred" : "left-aligned";
9539
+ const headline = a.hero?.headline && b.hero?.headline && a.hero.headline.class === b.hero.headline.class ? `${a.hero.headline.class} ` : "";
9540
+ const image = a.hero?.image && a.hero.image === b.hero?.image && a.hero.image !== "none" ? `, ${a.hero.image === "full" ? "full-width picture" : `photo ${a.hero.image}`}` : "";
9541
+ return `${placement} ${headline}hero${image}`;
9542
+ }
9387
9543
  function sharedParts(a, b, d = fingerprintDistance(a, b)) {
9388
9544
  const out = [];
9389
- const close = (k) => d.parts[k] <= SHARED_PART;
9545
+ const close = (k) => {
9546
+ const p = d.parts[k];
9547
+ return p !== null && p <= SHARED_PART;
9548
+ };
9390
9549
  if (close("accent")) out.push(a.accent || b.accent ? `${hueName(a.accent)} accent` : "no accent");
9391
9550
  if (close("type")) {
9392
9551
  if (a.display.family === b.display.family) out.push(`${a.display.family} headline`);
9393
9552
  if (a.body.family === b.body.family) out.push(`${a.body.family} body`);
9394
9553
  }
9395
- if (close("ground")) out.push(`${groundName(a.ground)} ground`);
9554
+ if (close("ground")) out.push(sharedGround(a, b));
9396
9555
  if (close("shape")) out.push(`${shapeName(a.roundness)} buttons`);
9397
9556
  if (close("motion")) out.push(a.motion > 0 || b.motion > 0 ? "scroll reveals" : "no reveals");
9398
9557
  if (close("effects") && a.effects.length) out.push(a.effects.filter((e) => b.effects.includes(e)).join(", "));
9399
- if (close("layout")) out.push("running order");
9558
+ if (close("hero")) out.push(sharedHero(a, b));
9559
+ if (close("rhythm")) out.push(sharedRhythm(a, b));
9560
+ if (close("layout")) out.push("same running order");
9400
9561
  return out;
9401
9562
  }
9402
9563
  function compareToEstate(target2, register, options = {}) {
@@ -9536,6 +9697,7 @@ function retrofitPlan(report2, direction) {
9536
9697
  MOOD,
9537
9698
  NEGATIVE_REASSURANCE,
9538
9699
  NULL_VERSION,
9700
+ OPENING_BANDS,
9539
9701
  PAGE_TYPES,
9540
9702
  PILL_LIMIT,
9541
9703
  PLAINER_WORDS,
@@ -9579,6 +9741,9 @@ function retrofitPlan(report2, direction) {
9579
9741
  STOCK_PHRASES,
9580
9742
  STUDY_WORDS,
9581
9743
  TELL_HEAVY,
9744
+ TEMPERATURE_FULL,
9745
+ TEMPERATURE_NAMED,
9746
+ TEMPERATURE_WEIGHT,
9582
9747
  TRADE_NOUNS,
9583
9748
  TYPE_STEPS,
9584
9749
  TYPICAL_AT,
@@ -9590,6 +9755,7 @@ function retrofitPlan(report2, direction) {
9590
9755
  applyHouseGate,
9591
9756
  applySeverity,
9592
9757
  auditSnapshot,
9758
+ bandOf,
9593
9759
  briefTerms,
9594
9760
  calibrate,
9595
9761
  catalogueTable,
@@ -9653,10 +9819,14 @@ function retrofitPlan(report2, direction) {
9653
9819
  globToRegExp,
9654
9820
  gradientContrast,
9655
9821
  gradientStops,
9822
+ groundDistance,
9823
+ groundLabel,
9656
9824
  groundName,
9825
+ groundTemperature,
9657
9826
  harvest,
9658
9827
  hedgeCount,
9659
9828
  hedgedClose,
9829
+ heroDistance,
9660
9830
  hexToOklch,
9661
9831
  holdHouseBlocks,
9662
9832
  houseRule,
@@ -9704,6 +9874,8 @@ function retrofitPlan(report2, direction) {
9704
9874
  renderedOrder,
9705
9875
  researchPrompt,
9706
9876
  retrofitPlan,
9877
+ rhythmDistance,
9878
+ rhythmOf,
9707
9879
  runTell,
9708
9880
  scanProvenance,
9709
9881
  scanSource,
@@ -9730,6 +9902,7 @@ function retrofitPlan(report2, direction) {
9730
9902
  tailwindV3Preset,
9731
9903
  tailwindV4Theme,
9732
9904
  tellById,
9905
+ temperatureName,
9733
9906
  toGamut,
9734
9907
  toJson,
9735
9908
  toSarif,
package/dist/index.d.cts CHANGED
@@ -914,6 +914,8 @@ interface SnapshotImage {
914
914
  width: number;
915
915
  height: number;
916
916
  top: number;
917
+ /** Version 2, craft 0.19 and later: distance from the viewport's left edge, so a hero can say which side its picture sits. */
918
+ left?: number;
917
919
  role: ImageRole;
918
920
  alt: string | null;
919
921
  /** Empty alt, `aria-hidden` or `role="presentation"`. */
@@ -2228,8 +2230,14 @@ declare function visualMeasures(img: RgbaImage, maxSamples?: number): SnapshotVi
2228
2230
  * comparison involving it uses `layout` exactly as version 1 did, so the null
2229
2231
  * models and the estate register compare as they were calibrated until they
2230
2232
  * are re-snapshotted.
2233
+ *
2234
+ * 3 adds `hero` (the headline's size and where the hero's picture sits).
2235
+ * Rhythm, the sequence of light and dark bands, is read from the version 2
2236
+ * `sections`, so a version 2 fingerprint measures it without a fresh
2237
+ * snapshot. A part neither side carries is left out of the distance rather
2238
+ * than counted as different (see `fingerprintDistance`).
2231
2239
  */
2232
- declare const FINGERPRINT_VERSION = 2;
2240
+ declare const FINGERPRINT_VERSION = 3;
2233
2241
  /** One section as layout comparison sees it. */
2234
2242
  interface FingerprintSection {
2235
2243
  role: SectionRole;
@@ -2253,8 +2261,26 @@ interface FingerprintSection {
2253
2261
  /** Large standalone figures. */
2254
2262
  figures?: number;
2255
2263
  }
2264
+ /** Where the hero's largest picture sits, relative to the headline. */
2265
+ type HeroImage = "left" | "right" | "full" | "none";
2266
+ /** The hero's composition: the first thing two sites are compared on by eye. */
2267
+ interface FingerprintHero {
2268
+ /** The headline (the first h1 inside the hero), or null when the hero has none. */
2269
+ headline: {
2270
+ sizePx: number;
2271
+ weight: number;
2272
+ class: FontClass;
2273
+ italic: boolean;
2274
+ } | null;
2275
+ /**
2276
+ * The largest photo or illustration in the hero: beside the text (left or
2277
+ * right of the viewport's centre line), `full` when it spans the width,
2278
+ * `none` without one. Absent when the snapshot predates image positions.
2279
+ */
2280
+ image?: HeroImage;
2281
+ }
2256
2282
  interface Fingerprint {
2257
- version: 1 | 2;
2283
+ version: 1 | 2 | 3;
2258
2284
  /** The most prominent chromatic colour: button fills first, then painted area, then text. */
2259
2285
  accent: Oklch | null;
2260
2286
  ground: Oklch;
@@ -2276,10 +2302,29 @@ interface Fingerprint {
2276
2302
  layout: SectionKind[];
2277
2303
  /** Version 2: present when every section carries a role and geometry. */
2278
2304
  sections?: FingerprintSection[];
2305
+ /** Version 3: present when the page has a hero section. */
2306
+ hero?: FingerprintHero;
2279
2307
  }
2280
2308
  declare function fingerprint(s: Snapshot): Fingerprint;
2309
+ /**
2310
+ * Chroma at which a ground's tint is unmistakable. RMP's cream sits at 0.010
2311
+ * and Bellerose's stone at 0.007, both plainly warm paper; Bellerose's
2312
+ * porcelain (#F1F3F4) at 0.003 is a cool hint. A page ground is the largest
2313
+ * area on the page and the eye adapts to it, so a tint far below what reads
2314
+ * as a difference between two buttons still reads as warm or cool paper.
2315
+ */
2316
+ declare const TEMPERATURE_FULL = 0.006;
2317
+ /** A ground is named warm or cool from this temperature, about chroma 0.0024. Below it, the name stays neutral. */
2318
+ declare const TEMPERATURE_NAMED = 0.4;
2319
+ /**
2320
+ * How warm or cool a colour reads, -1 (fully cool) through 0 (neutral) to 1
2321
+ * (fully warm): the chroma over `TEMPERATURE_FULL`, signed by the hue.
2322
+ */
2323
+ declare function groundTemperature(o: Oklch): number;
2324
+ type Temperature = "warm" | "neutral" | "cool";
2325
+ declare function temperatureName(o: Oklch): Temperature;
2281
2326
  interface FingerprintDistance {
2282
- /** 0 identical, 1 nothing in common. A weighted mean of the parts. */
2327
+ /** 0 identical, 1 nothing in common. A weighted mean of the parts both sides measure. */
2283
2328
  total: number;
2284
2329
  parts: {
2285
2330
  accent: number;
@@ -2289,34 +2334,78 @@ interface FingerprintDistance {
2289
2334
  motion: number;
2290
2335
  effects: number;
2291
2336
  layout: number;
2337
+ /** Null when either side has no sections (version 1). */
2338
+ rhythm: number | null;
2339
+ /** Null when either side has no hero. */
2340
+ hero: number | null;
2292
2341
  };
2293
2342
  }
2294
2343
  /**
2295
2344
  * How much each part counts. Accent and type are where a model's defaults
2296
2345
  * show first and where a real business has something of its own, so they
2297
- * carry half the weight between them.
2346
+ * carry the most. Structure (the running order, the rhythm of light and dark
2347
+ * bands, and the hero's composition) carries a quarter between its three
2348
+ * parts: two sites that open the same way read as the same site before any
2349
+ * colour is compared. A part only one side measures is left out and the
2350
+ * rest are weighed over what remains.
2298
2351
  */
2299
2352
  declare const DISTANCE_WEIGHTS: {
2300
- readonly accent: 0.25;
2301
- readonly type: 0.25;
2353
+ readonly accent: 0.2;
2354
+ readonly type: 0.2;
2302
2355
  readonly ground: 0.1;
2303
2356
  readonly shape: 0.1;
2304
- readonly motion: 0.1;
2357
+ readonly motion: 0.05;
2305
2358
  readonly effects: 0.1;
2306
2359
  readonly layout: 0.1;
2360
+ readonly rhythm: 0.1;
2361
+ readonly hero: 0.05;
2307
2362
  };
2308
2363
  /** Layout distance, 0 to 1: by role and geometry when both have them, else by kind as in version 1. */
2309
2364
  declare function layoutDistance(a: Fingerprint, b: Fingerprint): number;
2365
+ /** A section's band by the lightness of what it is painted on. */
2366
+ type LightnessBand = "light" | "mid" | "dark";
2367
+ declare function bandOf(l: number): LightnessBand;
2368
+ /**
2369
+ * The page's rhythm: its bands of light and dark in running order, each run
2370
+ * of same-lightness sections merged into one. "Light hero, white logo strip,
2371
+ * near-black band, light features" is light, dark, light: the strip is part
2372
+ * of the light opening, which is how a visitor sees it. Null for a version 1
2373
+ * fingerprint, which has no section backgrounds.
2374
+ */
2375
+ declare function rhythmOf(fp: Fingerprint): LightnessBand[] | null;
2376
+ /** The first bands count double: the opening is what two sites are compared on before anyone scrolls. */
2377
+ declare const OPENING_BANDS = 3;
2378
+ /**
2379
+ * Rhythm distance, 0 to 1: the edit distance over the opening bands and over
2380
+ * the whole sequence, averaged. Null when either side has no sections.
2381
+ */
2382
+ declare function rhythmDistance(a: Fingerprint, b: Fingerprint): number | null;
2383
+ /**
2384
+ * Hero distance, 0 to 1, over the parts both sides measure: where the text
2385
+ * sits (centred or left), how wide it runs, the headline's class and size,
2386
+ * and which side the picture is on. Null when either side has no hero.
2387
+ */
2388
+ declare function heroDistance(a: Fingerprint, b: Fingerprint): number | null;
2310
2389
  /** ΔE in OKLab. 0.02 is barely visible; by 0.3 two accents read as different colours. */
2311
2390
  declare const ACCENT_SCALE = 0.3;
2312
2391
  /** Grounds sit near white, where a smaller step already reads as a different paper. */
2313
2392
  declare const GROUND_SCALE = 0.15;
2393
+ /**
2394
+ * How much a difference in temperature adds to the ground distance, on top
2395
+ * of ΔE. Cream against porcelain is ΔE 0.013, a tenth of `GROUND_SCALE`, and
2396
+ * nobody takes them for the same paper: the temperature carries what ΔE
2397
+ * cannot at this chroma. Warm against cool adds 0.6; warm against neutral
2398
+ * white adds 0.3; two warm papers add nothing.
2399
+ */
2400
+ declare const TEMPERATURE_WEIGHT = 0.6;
2314
2401
  /** A part at or below this distance counts as shared between two sites. */
2315
2402
  declare const SHARED_PART = 0.2;
2316
2403
  /** Two accents within this ΔE_OK read as the same colour family: about 6 on the familiar ΔE scale. */
2317
2404
  declare const ACCENT_SHARED: number;
2318
2405
  /** Two grounds within this ΔE_OK read as the same paper. */
2319
2406
  declare const GROUND_SHARED: number;
2407
+ /** Ground distance, 0 to 1: ΔE over `GROUND_SCALE`, plus the temperature gap. */
2408
+ declare function groundDistance(a: Oklch, b: Oklch): number;
2320
2409
  declare function fingerprintDistance(a: Fingerprint, b: Fingerprint): FingerprintDistance;
2321
2410
  /**
2322
2411
  * The nearest neighbours of a fingerprint in a set, closest first. Typicality
@@ -2582,8 +2671,9 @@ declare function emptyEstate(): EstateRegister;
2582
2671
  declare function addSite(register: EstateRegister, site: EstateSite): EstateRegister;
2583
2672
  /**
2584
2673
  * What two fingerprints effectively share, named the way a person would see
2585
- * it: "Fraunces headline", "no accent", "pill buttons". A part counts when its
2586
- * distance is `SHARED_PART` or less.
2674
+ * it: "Fraunces headline", "no accent", "pill buttons", "cream ground",
2675
+ * "light hero then dark band". A part counts when its distance is
2676
+ * `SHARED_PART` or less; a part neither side measures never counts.
2587
2677
  */
2588
2678
  declare function sharedParts(a: Fingerprint, b: Fingerprint, d?: FingerprintDistance): string[];
2589
2679
  interface EstateMatch {
@@ -2703,7 +2793,18 @@ interface Typicality {
2703
2793
  */
2704
2794
  declare function typicality(target: Fingerprint, runs: NullRun[], k?: number): Typicality;
2705
2795
  declare function hueName(o: Oklch | null): string;
2796
+ /**
2797
+ * The coarse name the null's choice counts use ("white", "cream"), kept
2798
+ * stable so a harvest compares with earlier runs. `groundLabel` is the one a
2799
+ * person reads.
2800
+ */
2706
2801
  declare function groundName(o: Oklch): string;
2802
+ /**
2803
+ * The ground as a person would name it, with its temperature: "cream",
2804
+ * "cool grey", "warm white", "cool dark". Every light neutral page used to be
2805
+ * "grey", which put RMP's cream and Bellerose's porcelain under one word.
2806
+ */
2807
+ declare function groundLabel(o: Oklch): string;
2707
2808
  declare function shapeName(roundness: number | null): string;
2708
2809
  /** The discrete choices a fingerprint makes, as part → value. What a person would name. */
2709
2810
  declare function choicesOf(fp: Fingerprint): Record<string, string[]>;
@@ -3345,4 +3446,4 @@ declare function characterReport(input: CharacterInputs, copyTells?: Set<string>
3345
3446
 
3346
3447
  declare function retrofitPlan(report: CharacterReport, direction?: ArtDirection | null): string;
3347
3448
 
3348
- export { ACCENT_SCALE, ACCENT_SHARED, AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type ActionPosition, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Band, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, COMPONENT_ROLES, CONSIDERATION, CONTENT_FEATURES, CONVENTION, CUSTOMER_KINDS, type Calibration, type CandidateKind, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type CompetitorComparison, type ComponentPair, type ComponentTypicality, type Consideration, type ContrastCheck, type ContrastReport, type CopySide, type CopyTell, type CraftConfig, type CraftTokensInput, type CraftTokensResult, type CustomerKind, DEFAULT_IGNORE, DENSITIES, DENSITY_NAMES, DIRECTION_VERSION, DIRECTION_VERSIONS, DISTANCE_WEIGHTS, DURATION_MS, DURATION_S, type DensityCssOptions, type DensityName, type DirectionChoice, type DirectionComparison, type DirectionMatch, type DirectionProblem, type DirectionReport, type DirectionSource, type DirectionSummary, type DurationName, EASE, EASE_TUPLE, EDGE_STEP, ESTATE_VERSION, EXIT_RATIO, type EaseName, type EmitCssOptions, type EstateMatch, type EstatePair, type EstateRegister, type EstateSite, FINGERPRINT_VERSION, FLAGGED_TARGET, FUNCTION_WORDS, type Fact, type FactComparison, type FactKind, type Finding, type Fingerprint, type FingerprintDistance, type FingerprintSection, type FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, GROUND_SCALE, GROUND_SHARED, type Gamut, type Generation, type GradientContrast, HIERARCHY_FIELDS, HOME_FILE, HOME_URL, HOUSE, HOUSE_BUDGET, HOUSE_LOCKED, HOUSE_TYPE, HUMAN_TYPICAL_TARGET, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, IMAGERY_TELLS, type ImageProvenance, type ImageRole, JSON_SCHEMA_VERSION, type JobContext, type JobMap, type JobPart, LABELS, LEXICAL_FEATURES, LICENCES, LIGHTNESS_CURVE, type Label, type LabelSummary, type LexicalFeatures, type LicenceEntry, type LicenceSubject, MIN_DECIDED, MIN_PARAGRAPHS, MIN_RUNS, MOOD, type MeasuredPage, type MotionDecision, type MotionKind, type MultiClient, NEGATIVE_REASSURANCE, NULL_VERSION, type NullModel, type NullRun, type Oklch, type OklchToHexOptions, PAGE_TYPES, PILL_LIMIT, PLAINER_WORDS, PREFERENCE, PROFILE, PROFILE_MEASURED, PROFILE_PARAGRAPHS, PROMO_BADGE, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageCopy, type PageHierarchy, type PageResult, type PageType, type PaletteColour, type PhotoReviewOptions, type PhotoReviewResult, type Proposal, type ProposeInput, RAMP_STEPS, READABLE_SNAPSHOT_VERSIONS, REFLEX_FONTS_1, REFLEX_FONTS_2, REGISTERS, REGISTER_BASELINE, REGISTER_FEATURES, REGISTER_IDS, RENDERED_PATHS, RENDERED_TELLS, REVEAL_LIMIT, REVIEW_PHRASES, type Ramp, type RampFromAnchorsOptions, type RampOptions, type RampStep, type ReasonInput, type Register, type RegisterCheck, type RegisterFeatures, type RegisterId, type RegisterProfile, type RenderedTell, type ResearchInput, type RestraintBudget, type RestraintReport, type RestraintSeverity, type RestraintViolation, type Rgb, type RgbaImage, SAMPLES_PER_SEGMENT, SCALE, SECTION_ROLES, SHADCN_LIMIT, SHADCN_PRIMITIVES, SHARED_PART, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPECIFICITY_COPY_TELLS, SPECIFICITY_RENDERED_TELLS, SPRING, STANDING_CLAIM, STOCK_AVATAR_HOSTS, STOCK_FIGURES, STOCK_PHOTO_HOSTS, STOCK_PHRASES, STUDY_WORDS, type SarifOptions, type SarifTell, type SectionGeometry, type SectionKind, type SectionRhythmOptions, type SectionRole, type SemanticInput, type SemanticResult, type Severity, type SeverityLevel, type SeverityOverride, type ShapeFeatures, type ShouldAnimateInput, type SignalResult, type Snapshot, type SnapshotColour, type SnapshotControl, type SnapshotEffects, type SnapshotFont, type SnapshotGradient, type SnapshotHeading, type SnapshotImage, type SnapshotMotion, type SnapshotPatch, type SnapshotSection, type SnapshotVisual, type SourceFile, type SourceKind, type SourceTell, type SpaceStep, type Specific, type SpecificKind, type Specificity, type StudyWord, type Surface, TELL_HEAVY, TRADE_NOUNS, TYPE_STEPS, TYPICAL_AT, type TailwindPreset, type TargetCheck, type Tell, type TellException, type TellRate, type Tricolon, type TypeStep, type Typicality, type UnmeasuredPage, VAGUE_WORDS, type ValidateContext, type Verdict, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, auditSnapshot, briefTerms, calibrate, catalogueTable, characterReport, checkCopy, checkOrder, checkPair, checkPhotoReview, checkReason, checkRegister, checkRestraint, choicesOf, clauseDepth, closestComponents, compareCompetitor, compareDirections, compareFacts, compareToDeclared, compareToEstate, componentDistance, componentShared, componentTypicalities, componentTypicality, componentsOf, contentWords, countWords, craftTokens, createBaseline, cv, deltaEOk, deltaEOkHex, densityCss, densityTokens, designTells, documentParagraphs, emitCss, emptyEstate, estateComponentPairs, estatePairs, exitDuration, extractHtml, faceLicence, featureLabel, findClaims, findColours, findingKey, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatCalibration, formatClaims, formatComparison, formatCompetitor, formatDirectionMatches, formatHex, formatReport, getRegister, globToRegExp, gradientContrast, gradientStops, groundName, harvest, hedgeCount, hedgedClose, hexToOklch, holdHouseBlocks, houseRule, hueName, hueSwatch, ignoreMatcher, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isBlurb, isCategoryLabel, isCream, isFlagged, jobWords, layoutDistance, lexicalOf, licencesJson, makeSnapshot, motionTokens, namedSources, nearest, nominalisations, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, pageCopy, paletteFromPixels, paragraphUniformity, parseBaseline, parseColour, parseCraftConfig, parseHex, participialClauses, phrasalCoordinations, propose, protectedFacts, ramp, rampFromAnchors, readSnapshot, registerBrief, registerOf, registersDoc, renderedOrder, researchPrompt, retrofitPlan, runTell, scanProvenance, scanSource, sectionRhythm, semantic, sentencesOf, severityExceptions, shapeMoves, shapeName, shapeOf, words as shapeWords, sharedParts, shouldAnimate, siblingLine, skeleton, skeletonRepeat, specificity, specifics, splitSentences, stackedConditional, stockAvatarSource, stockSource, summariseDirection, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, toJson, toSarif, tradeOf, tricolons, typeFeatureTokens, typicality, unprovenClaims, validateDirection, validateHierarchy, validateJob, visibleText, visualMeasures, vocabulary, vocabularyCounts, wcagContrast, wordListsJson };
3449
+ export { ACCENT_SCALE, ACCENT_SHARED, AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type ActionPosition, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Band, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, COMPONENT_ROLES, CONSIDERATION, CONTENT_FEATURES, CONVENTION, CUSTOMER_KINDS, type Calibration, type CandidateKind, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type CompetitorComparison, type ComponentPair, type ComponentTypicality, type Consideration, type ContrastCheck, type ContrastReport, type CopySide, type CopyTell, type CraftConfig, type CraftTokensInput, type CraftTokensResult, type CustomerKind, DEFAULT_IGNORE, DENSITIES, DENSITY_NAMES, DIRECTION_VERSION, DIRECTION_VERSIONS, DISTANCE_WEIGHTS, DURATION_MS, DURATION_S, type DensityCssOptions, type DensityName, type DirectionChoice, type DirectionComparison, type DirectionMatch, type DirectionProblem, type DirectionReport, type DirectionSource, type DirectionSummary, type DurationName, EASE, EASE_TUPLE, EDGE_STEP, ESTATE_VERSION, EXIT_RATIO, type EaseName, type EmitCssOptions, type EstateMatch, type EstatePair, type EstateRegister, type EstateSite, FINGERPRINT_VERSION, FLAGGED_TARGET, FUNCTION_WORDS, type Fact, type FactComparison, type FactKind, type Finding, type Fingerprint, type FingerprintDistance, type FingerprintHero, type FingerprintSection, type FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, GROUND_SCALE, GROUND_SHARED, type Gamut, type Generation, type GradientContrast, HIERARCHY_FIELDS, HOME_FILE, HOME_URL, HOUSE, HOUSE_BUDGET, HOUSE_LOCKED, HOUSE_TYPE, HUMAN_TYPICAL_TARGET, type HarvestCandidate, type HarvestOptions, type HeroImage, type Hit, type HouseRule, type HouseTier, IMAGERY_TELLS, type ImageProvenance, type ImageRole, JSON_SCHEMA_VERSION, type JobContext, type JobMap, type JobPart, LABELS, LEXICAL_FEATURES, LICENCES, LIGHTNESS_CURVE, type Label, type LabelSummary, type LexicalFeatures, type LicenceEntry, type LicenceSubject, type LightnessBand, MIN_DECIDED, MIN_PARAGRAPHS, MIN_RUNS, MOOD, type MeasuredPage, type MotionDecision, type MotionKind, type MultiClient, NEGATIVE_REASSURANCE, NULL_VERSION, type NullModel, type NullRun, OPENING_BANDS, type Oklch, type OklchToHexOptions, PAGE_TYPES, PILL_LIMIT, PLAINER_WORDS, PREFERENCE, PROFILE, PROFILE_MEASURED, PROFILE_PARAGRAPHS, PROMO_BADGE, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageCopy, type PageHierarchy, type PageResult, type PageType, type PaletteColour, type PhotoReviewOptions, type PhotoReviewResult, type Proposal, type ProposeInput, RAMP_STEPS, READABLE_SNAPSHOT_VERSIONS, REFLEX_FONTS_1, REFLEX_FONTS_2, REGISTERS, REGISTER_BASELINE, REGISTER_FEATURES, REGISTER_IDS, RENDERED_PATHS, RENDERED_TELLS, REVEAL_LIMIT, REVIEW_PHRASES, type Ramp, type RampFromAnchorsOptions, type RampOptions, type RampStep, type ReasonInput, type Register, type RegisterCheck, type RegisterFeatures, type RegisterId, type RegisterProfile, type RenderedTell, type ResearchInput, type RestraintBudget, type RestraintReport, type RestraintSeverity, type RestraintViolation, type Rgb, type RgbaImage, SAMPLES_PER_SEGMENT, SCALE, SECTION_ROLES, SHADCN_LIMIT, SHADCN_PRIMITIVES, SHARED_PART, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPECIFICITY_COPY_TELLS, SPECIFICITY_RENDERED_TELLS, SPRING, STANDING_CLAIM, STOCK_AVATAR_HOSTS, STOCK_FIGURES, STOCK_PHOTO_HOSTS, STOCK_PHRASES, STUDY_WORDS, type SarifOptions, type SarifTell, type SectionGeometry, type SectionKind, type SectionRhythmOptions, type SectionRole, type SemanticInput, type SemanticResult, type Severity, type SeverityLevel, type SeverityOverride, type ShapeFeatures, type ShouldAnimateInput, type SignalResult, type Snapshot, type SnapshotColour, type SnapshotControl, type SnapshotEffects, type SnapshotFont, type SnapshotGradient, type SnapshotHeading, type SnapshotImage, type SnapshotMotion, type SnapshotPatch, type SnapshotSection, type SnapshotVisual, type SourceFile, type SourceKind, type SourceTell, type SpaceStep, type Specific, type SpecificKind, type Specificity, type StudyWord, type Surface, TELL_HEAVY, TEMPERATURE_FULL, TEMPERATURE_NAMED, TEMPERATURE_WEIGHT, TRADE_NOUNS, TYPE_STEPS, TYPICAL_AT, type TailwindPreset, type TargetCheck, type Tell, type TellException, type TellRate, type Temperature, type Tricolon, type TypeStep, type Typicality, type UnmeasuredPage, VAGUE_WORDS, type ValidateContext, type Verdict, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, auditSnapshot, bandOf, briefTerms, calibrate, catalogueTable, characterReport, checkCopy, checkOrder, checkPair, checkPhotoReview, checkReason, checkRegister, checkRestraint, choicesOf, clauseDepth, closestComponents, compareCompetitor, compareDirections, compareFacts, compareToDeclared, compareToEstate, componentDistance, componentShared, componentTypicalities, componentTypicality, componentsOf, contentWords, countWords, craftTokens, createBaseline, cv, deltaEOk, deltaEOkHex, densityCss, densityTokens, designTells, documentParagraphs, emitCss, emptyEstate, estateComponentPairs, estatePairs, exitDuration, extractHtml, faceLicence, featureLabel, findClaims, findColours, findingKey, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatCalibration, formatClaims, formatComparison, formatCompetitor, formatDirectionMatches, formatHex, formatReport, getRegister, globToRegExp, gradientContrast, gradientStops, groundDistance, groundLabel, groundName, groundTemperature, harvest, hedgeCount, hedgedClose, heroDistance, hexToOklch, holdHouseBlocks, houseRule, hueName, hueSwatch, ignoreMatcher, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isBlurb, isCategoryLabel, isCream, isFlagged, jobWords, layoutDistance, lexicalOf, licencesJson, makeSnapshot, motionTokens, namedSources, nearest, nominalisations, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, pageCopy, paletteFromPixels, paragraphUniformity, parseBaseline, parseColour, parseCraftConfig, parseHex, participialClauses, phrasalCoordinations, propose, protectedFacts, ramp, rampFromAnchors, readSnapshot, registerBrief, registerOf, registersDoc, renderedOrder, researchPrompt, retrofitPlan, rhythmDistance, rhythmOf, runTell, scanProvenance, scanSource, sectionRhythm, semantic, sentencesOf, severityExceptions, shapeMoves, shapeName, shapeOf, words as shapeWords, sharedParts, shouldAnimate, siblingLine, skeleton, skeletonRepeat, specificity, specifics, splitSentences, stackedConditional, stockAvatarSource, stockSource, summariseDirection, tailwindV3Preset, tailwindV4Theme, tellById, temperatureName, toGamut, toJson, toSarif, tradeOf, tricolons, typeFeatureTokens, typicality, unprovenClaims, validateDirection, validateHierarchy, validateJob, visibleText, visualMeasures, vocabulary, vocabularyCounts, wcagContrast, wordListsJson };