@domandigital/craft 0.18.2 → 0.19.1

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.
package/dist/index.js CHANGED
@@ -4042,8 +4042,100 @@ function markupParts(file) {
4042
4042
  function textNodeIsProse(body) {
4043
4043
  return /[a-zA-Z]{2,}/.test(body) && /\s/.test(body.trim()) || /[a-zA-Z]{4,}[.!?]/.test(body);
4044
4044
  }
4045
+ var FRONT_MATTER_PROSE = /* @__PURE__ */ new Set(["title", "description"]);
4046
+ var KEY_LINE = /^(\s*)(?:-\s+)?([A-Za-z][\w-]*)[ \t]*:[ \t]*/;
4047
+ var BLOCK_SCALAR = /^(?:[|>][+-]?\d*|[|>]\d+[+-]?)$/;
4048
+ function readFrontKey(line) {
4049
+ const m = KEY_LINE.exec(line);
4050
+ if (!m) return null;
4051
+ return { indent: m[1].length, name: m[2], valueAt: m[0].length, value: line.slice(m[0].length) };
4052
+ }
4053
+ function indentOf(line) {
4054
+ let i = 0;
4055
+ while (i < line.length && (line[i] === " " || line[i] === " ")) i += 1;
4056
+ return i;
4057
+ }
4058
+ function commentAt(value) {
4059
+ if (value.startsWith("#")) return 0;
4060
+ const found = /[ \t]+#/.exec(value);
4061
+ return found ? found.index : -1;
4062
+ }
4063
+ function keepSpan(line, from, to) {
4064
+ const start = Math.max(0, Math.min(from, line.length));
4065
+ const end = Math.max(start, Math.min(to, line.length));
4066
+ return " ".repeat(start) + line.slice(start, end) + " ".repeat(line.length - end);
4067
+ }
4068
+ function quoteEnd(line, from, quote) {
4069
+ for (let i = from; i < line.length; i += 1) {
4070
+ if (quote === '"' && line[i] === "\\") {
4071
+ i += 1;
4072
+ continue;
4073
+ }
4074
+ if (quote === "'" && line[i] === "'" && line[i + 1] === "'") {
4075
+ i += 1;
4076
+ continue;
4077
+ }
4078
+ if (line[i] === quote) return i;
4079
+ }
4080
+ return -1;
4081
+ }
4082
+ function maskFrontMatter(block) {
4083
+ const lines = block.split("\n");
4084
+ const out = lines.map((line) => blank(line));
4085
+ for (let i = 1; i < lines.length; ) {
4086
+ const line = lines[i];
4087
+ if (line === "---" || line === "...") break;
4088
+ const key = readFrontKey(line);
4089
+ if (!key || !FRONT_MATTER_PROSE.has(key.name.toLowerCase())) {
4090
+ i += 1;
4091
+ continue;
4092
+ }
4093
+ const quote = key.value.startsWith('"') ? '"' : key.value.startsWith("'") ? "'" : "";
4094
+ if (quote) {
4095
+ let cursor = key.valueAt + 1;
4096
+ while (i < lines.length && lines[i] !== "---" && lines[i] !== "...") {
4097
+ const current = lines[i];
4098
+ const end = quoteEnd(current, cursor, quote);
4099
+ out[i] = keepSpan(current, cursor, end === -1 ? current.length : end);
4100
+ i += 1;
4101
+ cursor = 0;
4102
+ if (end !== -1) break;
4103
+ }
4104
+ continue;
4105
+ }
4106
+ const hash = commentAt(key.value);
4107
+ const body = (hash === -1 ? key.value : key.value.slice(0, hash)).trim();
4108
+ if (BLOCK_SCALAR.test(body)) {
4109
+ i += 1;
4110
+ while (i < lines.length && lines[i] !== "---" && lines[i] !== "...") {
4111
+ const current = lines[i];
4112
+ if (current.trim() !== "" && indentOf(current) <= key.indent) break;
4113
+ if (current.trim()) out[i] = current;
4114
+ i += 1;
4115
+ }
4116
+ continue;
4117
+ }
4118
+ if (body === "" || body === "~" || /^null$/i.test(body) || body.startsWith("[") || body.startsWith("{")) {
4119
+ i += 1;
4120
+ continue;
4121
+ }
4122
+ out[i] = keepSpan(line, key.valueAt, hash === -1 ? line.length : key.valueAt + hash);
4123
+ i += 1;
4124
+ while (i < lines.length && lines[i] !== "---" && lines[i] !== "...") {
4125
+ const current = lines[i];
4126
+ if (current.trim() === "" || indentOf(current) <= key.indent) break;
4127
+ if (!/^\s*#/.test(current)) {
4128
+ const cut = commentAt(current);
4129
+ out[i] = cut === -1 ? current : keepSpan(current, 0, cut);
4130
+ }
4131
+ i += 1;
4132
+ }
4133
+ }
4134
+ const masked = out.join("\n");
4135
+ return masked.length === block.length ? masked : blank(block);
4136
+ }
4045
4137
  function proseBlocks(file) {
4046
- const masked = file.text.replace(/^(```|~~~)[\s\S]*?^\1/gm, blank).replace(/`[^`\n]*`/g, (m) => " ".repeat(m.length)).replace(/^---\n[\s\S]*?\n---\n/, blank).replace(/<!--[\s\S]*?-->/g, blank);
4138
+ const masked = file.text.replace(/^(```|~~~)[\s\S]*?^\1/gm, blank).replace(/`[^`\n]*`/g, (m) => " ".repeat(m.length)).replace(/^---\n[\s\S]*?\n---\n/, (block) => maskFrontMatter(block)).replace(/<!--[\s\S]*?-->/g, blank);
4047
4139
  return [{ path: file.path, text: masked, offset: 0 }];
4048
4140
  }
4049
4141
  function isData(path) {
@@ -5031,7 +5123,7 @@ var RENDERED_PATHS = {
5031
5123
  };
5032
5124
 
5033
5125
  // src/fingerprint/index.ts
5034
- var FINGERPRINT_VERSION = 2;
5126
+ var FINGERPRINT_VERSION = 3;
5035
5127
  var CHROMATIC = 0.03;
5036
5128
  var MIN_ACCENT_AREA = 2e4;
5037
5129
  var oklch = (value) => {
@@ -5067,6 +5159,24 @@ function median(values) {
5067
5159
  const mid = Math.floor(sorted.length / 2);
5068
5160
  return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
5069
5161
  }
5162
+ var FULL_WIDTH_IMAGE = 0.8;
5163
+ function heroOf(s) {
5164
+ const h1 = s.headings.find((h) => h.level === 1);
5165
+ const inside = (top, x) => top >= x.top && top < x.top + x.height;
5166
+ const index = (() => {
5167
+ const withH1 = h1 ? s.sections.findIndex((x) => x.kind === "hero" && inside(h1.top, x)) : -1;
5168
+ return withH1 >= 0 ? withH1 : s.sections.findIndex((x) => x.kind === "hero");
5169
+ })();
5170
+ if (index < 0) return void 0;
5171
+ const section = s.sections[index];
5172
+ const headline = h1 && inside(h1.top, section) ? { sizePx: h1.sizePx, weight: h1.weight, class: fontClass(normaliseFamily(h1.family)), italic: h1.italic || h1.italicPart } : null;
5173
+ if (!s.images) return { headline };
5174
+ 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];
5175
+ if (!picture2) return { headline, image: "none" };
5176
+ if (picture2.left === void 0) return { headline };
5177
+ const image = picture2.width >= FULL_WIDTH_IMAGE * s.viewport.width ? "full" : picture2.left + picture2.width / 2 < s.viewport.width / 2 ? "left" : "right";
5178
+ return { headline, image };
5179
+ }
5070
5180
  function fingerprint(s) {
5071
5181
  const effects = [];
5072
5182
  if (s.effects.glass > 0) effects.push("glass-panel");
@@ -5078,6 +5188,7 @@ function fingerprint(s) {
5078
5188
  if (s.effects.eyebrowChip) effects.push("hero-eyebrow-chip");
5079
5189
  if (s.motion.introOverlay) effects.push("intro-cinematic");
5080
5190
  const below = s.sections.filter((x) => x.top > s.viewport.height).length;
5191
+ const hero = heroOf(s);
5081
5192
  return {
5082
5193
  version: FINGERPRINT_VERSION,
5083
5194
  accent: accentOf(s),
@@ -5101,10 +5212,28 @@ function fingerprint(s) {
5101
5212
  carousel: x.carousel ?? false,
5102
5213
  figures: x.figures ?? 0
5103
5214
  }))
5104
- } : {}
5215
+ } : {},
5216
+ ...hero ? { hero } : {}
5105
5217
  };
5106
5218
  }
5107
- var DISTANCE_WEIGHTS = { accent: 0.25, type: 0.25, ground: 0.1, shape: 0.1, motion: 0.1, effects: 0.1, layout: 0.1 };
5219
+ var WARM_HUE = [
5220
+ [330, 360],
5221
+ [0, 130]
5222
+ ];
5223
+ var COOL_HUE = [[160, 310]];
5224
+ var TEMPERATURE_FULL = 6e-3;
5225
+ var TEMPERATURE_NAMED = 0.4;
5226
+ function groundTemperature(o) {
5227
+ const h = (o.h % 360 + 360) % 360;
5228
+ const within = (bands) => bands.some(([lo, hi]) => h >= lo && h < hi);
5229
+ const strength = Math.min(1, o.c / TEMPERATURE_FULL);
5230
+ return within(WARM_HUE) ? strength : within(COOL_HUE) ? -strength : 0;
5231
+ }
5232
+ function temperatureName(o) {
5233
+ const t = groundTemperature(o);
5234
+ return t >= TEMPERATURE_NAMED ? "warm" : t <= -TEMPERATURE_NAMED ? "cool" : "neutral";
5235
+ }
5236
+ 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 };
5108
5237
  function levenshtein(a, b, cost = (x, y) => x === y ? 0 : 1) {
5109
5238
  const row = Array.from({ length: b.length + 1 }, (_, i) => i);
5110
5239
  for (let i = 1; i <= a.length; i += 1) {
@@ -5131,18 +5260,62 @@ function layoutDistance(a, b) {
5131
5260
  return longest === 0 ? 0 : levenshtein(a.layout, b.layout) / longest;
5132
5261
  }
5133
5262
  var clamp = (n) => Math.max(0, Math.min(1, n));
5263
+ function bandOf(l) {
5264
+ return l < 0.4 ? "dark" : l >= 0.75 ? "light" : "mid";
5265
+ }
5266
+ function rhythmOf(fp) {
5267
+ if (!fp.sections) return null;
5268
+ const out = [];
5269
+ for (const s of fp.sections) {
5270
+ if (s.background === void 0) return null;
5271
+ const band = bandOf((s.background ?? fp.ground).l);
5272
+ if (out[out.length - 1] !== band) out.push(band);
5273
+ }
5274
+ return out;
5275
+ }
5276
+ var OPENING_BANDS = 3;
5277
+ function rhythmDistance(a, b) {
5278
+ const ra = rhythmOf(a);
5279
+ const rb = rhythmOf(b);
5280
+ if (!ra || !rb) return null;
5281
+ const over = (x, y) => {
5282
+ const longest = Math.max(x.length, y.length);
5283
+ return longest === 0 ? 0 : levenshtein(x, y) / longest;
5284
+ };
5285
+ return (over(ra.slice(0, OPENING_BANDS), rb.slice(0, OPENING_BANDS)) + over(ra, rb)) / 2;
5286
+ }
5287
+ var HEADLINE_OCTAVES = 1;
5288
+ function heroSection(fp) {
5289
+ return fp.sections?.find((s) => s.role === "hero");
5290
+ }
5291
+ function heroDistance(a, b) {
5292
+ const sa = heroSection(a);
5293
+ const sb = heroSection(b);
5294
+ if (!sa || !sb) return null;
5295
+ const parts2 = [Math.abs(sa.centred - sb.centred), clamp(Math.abs(sa.width - sb.width) / 0.5)];
5296
+ const ha = a.hero?.headline;
5297
+ const hb = b.hero?.headline;
5298
+ 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));
5299
+ else if (a.hero && b.hero && (ha || hb)) parts2.push(1);
5300
+ if (a.hero?.image && b.hero?.image) parts2.push(a.hero.image === b.hero.image ? 0 : 1);
5301
+ return parts2.reduce((s, p) => s + p, 0) / parts2.length;
5302
+ }
5134
5303
  function faceDistance(a, b) {
5135
5304
  if (a.family.toLowerCase() === b.family.toLowerCase()) return 0;
5136
5305
  return a.class === b.class ? 0.5 : 1;
5137
5306
  }
5138
5307
  var ACCENT_SCALE = 0.3;
5139
5308
  var GROUND_SCALE = 0.15;
5309
+ var TEMPERATURE_WEIGHT = 0.6;
5140
5310
  var SHARED_PART = 0.2;
5141
5311
  var ACCENT_SHARED = ACCENT_SCALE * SHARED_PART;
5142
5312
  var GROUND_SHARED = GROUND_SCALE * SHARED_PART;
5313
+ function groundDistance(a, b) {
5314
+ return clamp(deltaEOk(a, b) / GROUND_SCALE + TEMPERATURE_WEIGHT * Math.abs(groundTemperature(a) - groundTemperature(b)) / 2);
5315
+ }
5143
5316
  function fingerprintDistance(a, b) {
5144
5317
  const accent = a.accent && b.accent ? clamp(deltaEOk(a.accent, b.accent) / ACCENT_SCALE) : a.accent || b.accent ? 1 : 0;
5145
- const ground = clamp(deltaEOk(a.ground, b.ground) / GROUND_SCALE);
5318
+ const ground = groundDistance(a.ground, b.ground);
5146
5319
  const type = 0.6 * faceDistance(a.display, b.display) + 0.4 * faceDistance(a.body, b.body);
5147
5320
  const shape = a.roundness === null || b.roundness === null ? a.roundness === b.roundness ? 0 : 0.5 : clamp(Math.abs(a.roundness - b.roundness) / 0.5);
5148
5321
  const motion = clamp(Math.abs(a.motion - b.motion));
@@ -5150,9 +5323,18 @@ function fingerprintDistance(a, b) {
5150
5323
  const shared = a.effects.filter((e) => b.effects.includes(e)).length;
5151
5324
  const effects = union.size === 0 ? 0 : 1 - shared / union.size;
5152
5325
  const layout = layoutDistance(a, b);
5153
- const parts2 = { accent, ground, type, shape, motion, effects, layout };
5154
- const total = Object.keys(DISTANCE_WEIGHTS).reduce((sum, k) => sum + DISTANCE_WEIGHTS[k] * parts2[k], 0);
5155
- return { total: Math.round(total * 1e3) / 1e3, parts: parts2 };
5326
+ const rhythm = rhythmDistance(a, b);
5327
+ const hero = heroDistance(a, b);
5328
+ const parts2 = { accent, ground, type, shape, motion, effects, layout, rhythm, hero };
5329
+ let sum = 0;
5330
+ let weight = 0;
5331
+ for (const k of Object.keys(DISTANCE_WEIGHTS)) {
5332
+ const p = parts2[k];
5333
+ if (p === null) continue;
5334
+ sum += DISTANCE_WEIGHTS[k] * p;
5335
+ weight += DISTANCE_WEIGHTS[k];
5336
+ }
5337
+ return { total: Math.round(sum / weight * 1e3) / 1e3, parts: parts2 };
5156
5338
  }
5157
5339
  function nearest(target2, set, k = set.length) {
5158
5340
  return set.map((item) => ({ id: item.id, distance: fingerprintDistance(target2, item.fingerprint) })).sort((x, y) => x.distance.total - y.distance.total).slice(0, k);
@@ -5230,6 +5412,12 @@ function groundName(o) {
5230
5412
  if (o.l >= 0.97 && o.c < 0.012) return "white";
5231
5413
  return o.c < 0.02 ? "grey" : "tinted";
5232
5414
  }
5415
+ function groundLabel(o) {
5416
+ const name = groundName(o);
5417
+ if (name === "cream" || name === "tinted") return name;
5418
+ const temperature = temperatureName(o);
5419
+ return temperature === "neutral" ? name : `${temperature} ${name}`;
5420
+ }
5233
5421
  function shapeName(roundness) {
5234
5422
  if (roundness === null) return "no buttons";
5235
5423
  if (roundness >= 0.45) return "pill";
@@ -8504,6 +8692,33 @@ var LICENCES = [
8504
8692
  checked: "2026-09-24",
8505
8693
  source: "https://iconscout.com/licenses",
8506
8694
  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."
8695
+ },
8696
+ {
8697
+ name: "Saira Semi Condensed",
8698
+ subject: "face",
8699
+ licence: "OFL-1.1",
8700
+ multiClient: "yes",
8701
+ checked: "2026-10-09",
8702
+ source: "https://github.com/google/fonts/blob/main/ofl/sairasemicondensed/OFL.txt",
8703
+ 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)."
8704
+ },
8705
+ {
8706
+ name: "Public Sans",
8707
+ subject: "face",
8708
+ licence: "OFL-1.1",
8709
+ multiClient: "yes",
8710
+ checked: "2026-10-09",
8711
+ source: "https://github.com/google/fonts/blob/main/ofl/publicsans/OFL.txt",
8712
+ 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)."
8713
+ },
8714
+ {
8715
+ name: "Martian Mono",
8716
+ subject: "face",
8717
+ licence: "OFL-1.1",
8718
+ multiClient: "yes",
8719
+ checked: "2026-10-09",
8720
+ source: "https://github.com/google/fonts/blob/main/ofl/martianmono/OFL.txt",
8721
+ 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)."
8507
8722
  }
8508
8723
  ];
8509
8724
  function faceLicence(family) {
@@ -8870,7 +9085,7 @@ var PART_AREA = {
8870
9085
  effects: { area: "effects", choice: "motif" },
8871
9086
  motion: { area: "motion", choice: "signature" }
8872
9087
  };
8873
- var DEFAULTS = /^(?:ground )?(?:white|grey)(?: ground)?$|no scroll reveals|no reveals|accent (?:none|neutral)|^no buttons|shape no buttons/;
9088
+ var DEFAULTS = /^(?:ground )?(?:white|grey|light)(?: ground)?$|no scroll reveals|no reveals|accent (?:none|neutral)|^no buttons|shape no buttons|^light page throughout$/;
8874
9089
  function designFindings(findings, copyTells) {
8875
9090
  return (findings?.findings ?? []).filter((f4) => !copyTells.has(f4.tell));
8876
9091
  }
@@ -8977,7 +9192,7 @@ function areaOfShared(part) {
8977
9192
  if (/accent|ground/.test(part)) return "colour";
8978
9193
  if (/buttons/.test(part)) return "shape";
8979
9194
  if (/reveal/.test(part)) return "motion";
8980
- if (/running order/.test(part)) return "layout";
9195
+ if (/running order|hero|page throughout/.test(part)) return "layout";
8981
9196
  return "effects";
8982
9197
  }
8983
9198
  function choiceOfShared(part) {
@@ -8987,7 +9202,7 @@ function choiceOfShared(part) {
8987
9202
  if (/ground/.test(part)) return "ground";
8988
9203
  if (/buttons/.test(part)) return "shape";
8989
9204
  if (/reveal/.test(part)) return "signature";
8990
- return /running order/.test(part) ? void 0 : "motif";
9205
+ return /running order|hero|page throughout/.test(part) ? void 0 : "motif";
8991
9206
  }
8992
9207
 
8993
9208
  // src/calibrate/index.ts
@@ -9094,19 +9309,45 @@ function addSite(register, site) {
9094
9309
  const sites = [...register.sites.filter((s) => s.id !== site.id), site].sort((a, b) => a.id.localeCompare(b.id));
9095
9310
  return { version: ESTATE_VERSION, sites };
9096
9311
  }
9312
+ function sharedGround(a, b) {
9313
+ const [x, y] = [groundLabel(a.ground), groundLabel(b.ground)];
9314
+ if (x === y) return `${x} ground`;
9315
+ const temperature = temperatureName(a.ground);
9316
+ if (temperature !== "neutral" && temperature === temperatureName(b.ground)) return `${temperature} ground`;
9317
+ return `${a.ground.l < 0.3 ? "dark" : "light"} ground`;
9318
+ }
9319
+ function sharedRhythm(a, b) {
9320
+ const bands = rhythmOf(a) ?? [];
9321
+ const word = (band) => band;
9322
+ if (bands.length <= 1) return `${bands[0] ? word(bands[0]) : "light"} page throughout`;
9323
+ const second = bands[1] === "mid" ? "mid-tone band" : `${word(bands[1])} band`;
9324
+ return `${word(bands[0])} hero then ${second}`;
9325
+ }
9326
+ function sharedHero(a, b) {
9327
+ const hero = a.sections?.find((s) => s.role === "hero");
9328
+ const placement = hero && hero.centred >= 0.5 ? "centred" : "left-aligned";
9329
+ const headline = a.hero?.headline && b.hero?.headline && a.hero.headline.class === b.hero.headline.class ? `${a.hero.headline.class} ` : "";
9330
+ 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}`}` : "";
9331
+ return `${placement} ${headline}hero${image}`;
9332
+ }
9097
9333
  function sharedParts(a, b, d = fingerprintDistance(a, b)) {
9098
9334
  const out = [];
9099
- const close = (k) => d.parts[k] <= SHARED_PART;
9335
+ const close = (k) => {
9336
+ const p = d.parts[k];
9337
+ return p !== null && p <= SHARED_PART;
9338
+ };
9100
9339
  if (close("accent")) out.push(a.accent || b.accent ? `${hueName(a.accent)} accent` : "no accent");
9101
9340
  if (close("type")) {
9102
9341
  if (a.display.family === b.display.family) out.push(`${a.display.family} headline`);
9103
9342
  if (a.body.family === b.body.family) out.push(`${a.body.family} body`);
9104
9343
  }
9105
- if (close("ground")) out.push(`${groundName(a.ground)} ground`);
9344
+ if (close("ground")) out.push(sharedGround(a, b));
9106
9345
  if (close("shape")) out.push(`${shapeName(a.roundness)} buttons`);
9107
9346
  if (close("motion")) out.push(a.motion > 0 || b.motion > 0 ? "scroll reveals" : "no reveals");
9108
9347
  if (close("effects") && a.effects.length) out.push(a.effects.filter((e) => b.effects.includes(e)).join(", "));
9109
- if (close("layout")) out.push("running order");
9348
+ if (close("hero")) out.push(sharedHero(a, b));
9349
+ if (close("rhythm")) out.push(sharedRhythm(a, b));
9350
+ if (close("layout")) out.push("same running order");
9110
9351
  return out;
9111
9352
  }
9112
9353
  function compareToEstate(target2, register, options = {}) {
@@ -9245,6 +9486,7 @@ export {
9245
9486
  MOOD,
9246
9487
  NEGATIVE_REASSURANCE,
9247
9488
  NULL_VERSION,
9489
+ OPENING_BANDS,
9248
9490
  PAGE_TYPES,
9249
9491
  PILL_LIMIT,
9250
9492
  PLAINER_WORDS,
@@ -9288,6 +9530,9 @@ export {
9288
9530
  STOCK_PHRASES,
9289
9531
  STUDY_WORDS,
9290
9532
  TELL_HEAVY,
9533
+ TEMPERATURE_FULL,
9534
+ TEMPERATURE_NAMED,
9535
+ TEMPERATURE_WEIGHT,
9291
9536
  TRADE_NOUNS,
9292
9537
  TYPE_STEPS,
9293
9538
  TYPICAL_AT,
@@ -9299,6 +9544,7 @@ export {
9299
9544
  applyHouseGate,
9300
9545
  applySeverity,
9301
9546
  auditSnapshot,
9547
+ bandOf,
9302
9548
  briefTerms,
9303
9549
  calibrate,
9304
9550
  catalogueTable,
@@ -9362,10 +9608,14 @@ export {
9362
9608
  globToRegExp,
9363
9609
  gradientContrast,
9364
9610
  gradientStops,
9611
+ groundDistance,
9612
+ groundLabel,
9365
9613
  groundName,
9614
+ groundTemperature,
9366
9615
  harvest,
9367
9616
  hedgeCount,
9368
9617
  hedgedClose,
9618
+ heroDistance,
9369
9619
  hexToOklch,
9370
9620
  holdHouseBlocks,
9371
9621
  houseRule,
@@ -9413,6 +9663,8 @@ export {
9413
9663
  renderedOrder,
9414
9664
  researchPrompt,
9415
9665
  retrofitPlan,
9666
+ rhythmDistance,
9667
+ rhythmOf,
9416
9668
  runTell,
9417
9669
  scanProvenance,
9418
9670
  scanSource,
@@ -9439,6 +9691,7 @@ export {
9439
9691
  tailwindV3Preset,
9440
9692
  tailwindV4Theme,
9441
9693
  tellById,
9694
+ temperatureName,
9442
9695
  toGamut,
9443
9696
  toJson,
9444
9697
  toSarif,
package/licences.json CHANGED
@@ -99,6 +99,33 @@
99
99
  "checked": "2026-09-24",
100
100
  "source": "https://iconscout.com/licenses",
101
101
  "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."
102
+ },
103
+ {
104
+ "name": "Saira Semi Condensed",
105
+ "subject": "face",
106
+ "licence": "OFL-1.1",
107
+ "multiClient": "yes",
108
+ "checked": "2026-10-09",
109
+ "source": "https://github.com/google/fonts/blob/main/ofl/sairasemicondensed/OFL.txt",
110
+ "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)."
111
+ },
112
+ {
113
+ "name": "Public Sans",
114
+ "subject": "face",
115
+ "licence": "OFL-1.1",
116
+ "multiClient": "yes",
117
+ "checked": "2026-10-09",
118
+ "source": "https://github.com/google/fonts/blob/main/ofl/publicsans/OFL.txt",
119
+ "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)."
120
+ },
121
+ {
122
+ "name": "Martian Mono",
123
+ "subject": "face",
124
+ "licence": "OFL-1.1",
125
+ "multiClient": "yes",
126
+ "checked": "2026-10-09",
127
+ "source": "https://github.com/google/fonts/blob/main/ofl/martianmono/OFL.txt",
128
+ "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)."
102
129
  }
103
130
  ]
104
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@domandigital/craft",
3
- "version": "0.18.2",
3
+ "version": "0.19.1",
4
4
  "private": false,
5
5
  "description": "The Doman Digital house craft standard as numbers: OKLCh colour ramps that snap to existing anchors, derived semantic tokens with measured WCAG and APCA contrast, one shared motion vocabulary for CSS and JS, and a base stylesheet. Zero dependencies.",
6
6
  "license": "Apache-2.0",