@domandigital/craft 0.13.0 → 0.14.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.
package/dist/index.d.cts CHANGED
@@ -796,6 +796,13 @@ interface SnapshotSection {
796
796
  carousel?: boolean;
797
797
  /** Version 2: large standalone figures ("500+", "98%", "4.9/5") at 24px or more. */
798
798
  figures?: number;
799
+ /**
800
+ * Version 2, craft 0.13 and later: the section's visible text, one block per
801
+ * heading, paragraph, list item or quote, each at most 300 characters, at
802
+ * most 40. Navigation, buttons and footers are left out. What the
803
+ * specificity and proof tells read.
804
+ */
805
+ text?: string[];
799
806
  }
800
807
  type ImageRole = "photo" | "illustration" | "icon" | "avatar" | "logo";
801
808
  /**
@@ -907,10 +914,17 @@ interface Snapshot {
907
914
  /** Version 2: absent when the page could not be screenshotted. */
908
915
  visual?: SnapshotVisual;
909
916
  /**
910
- * Version 2, craft 0.14 and later: every image on the page, largest first, at
917
+ * Version 2, craft 0.13 and later: every image on the page, largest first, at
911
918
  * most 60. Absent in older snapshots, which the imagery tells never judge.
912
919
  */
913
920
  images?: SnapshotImage[];
921
+ /**
922
+ * Version 2, craft 0.13 and later: the text a visitor reads before
923
+ * scrolling, at most 12 blocks. Navigation, a header without the headline,
924
+ * and the words on buttons and links are left out: this is the page's
925
+ * claim, not its menu or its calls to action.
926
+ */
927
+ firstScreenText?: string[];
914
928
  /** Set when a version 1 snapshot was read and upgraded in memory. */
915
929
  migratedFrom?: 1;
916
930
  /** The design-system measures trawl has collected since v7, kept so it can switch to this. */
@@ -1144,7 +1158,7 @@ interface CheckReport {
1144
1158
  * Bumped whenever an entry is added, removed or its detection changes, so a
1145
1159
  * report can say which list it was judged against.
1146
1160
  */
1147
- declare const CATALOGUE_VERSION = "2026.09.11";
1161
+ declare const CATALOGUE_VERSION = "2026.09.12";
1148
1162
  declare const CATALOGUE: readonly Tell[];
1149
1163
  declare function tellById(id: string): Tell | undefined;
1150
1164
  /**
@@ -1326,6 +1340,154 @@ declare function namedSources(sentence: string): string[];
1326
1340
  declare function findClaims(files: SourceFile[]): ClaimsReport;
1327
1341
  declare function formatClaims(report: ClaimsReport, label?: string): string;
1328
1342
 
1343
+ /**
1344
+ * Specificity: how much of a page only this business could have written.
1345
+ *
1346
+ * "Quality you can trust" fits every site in every trade. "Boilers fixed the
1347
+ * same day in Brackley, from £85" fits one. The difference is countable: names,
1348
+ * places, numbers, prices, dates, and the nouns of the trade. A page built
1349
+ * from no brief has almost none, because the model had none to give.
1350
+ *
1351
+ * Pure over text. Two things keep the count honest:
1352
+ *
1353
+ * - A Title Case or all-capitals line carries no capital-letter information,
1354
+ * so its capitals are not read as names ("Quality You Can Trust" is not a
1355
+ * proper noun). Its numbers, trade nouns and brief terms still count.
1356
+ * - The stock figures every site uses ("24/7", "100%", "5-star", "No. 1") are
1357
+ * masked before counting. They are numbers, not specifics.
1358
+ *
1359
+ * A brief (from `--brief`, `art-direction.json` or a null model) adds its own
1360
+ * terms: the business's name, its town and its trade, recognised wherever
1361
+ * they appear, even at the start of a sentence or in a shouted heading.
1362
+ */
1363
+
1364
+ type SpecificKind = FactKind | "trade" | "brief";
1365
+ interface Specific {
1366
+ kind: SpecificKind;
1367
+ /** As written. */
1368
+ value: string;
1369
+ /** Lower case, for comparing across pages. */
1370
+ key: string;
1371
+ }
1372
+ interface Specificity {
1373
+ words: number;
1374
+ /** Each distinct specific once: repeating a town ten times does not make a page ten times more particular. */
1375
+ specifics: Specific[];
1376
+ /** Specifics that are not trade nouns: a name, place, number, price, date, contact detail or brief term. */
1377
+ hard: number;
1378
+ /** Distinct trade nouns. */
1379
+ trade: number;
1380
+ /** Distinct specifics per 100 words, to one decimal place. */
1381
+ perHundred: number;
1382
+ /**
1383
+ * No hard specific and fewer than two trade nouns. One trade noun alone
1384
+ * ("quality plumbing you can trust") is what every competitor in the trade
1385
+ * also says.
1386
+ */
1387
+ generic: boolean;
1388
+ }
1389
+ /**
1390
+ * Concrete nouns of the work, across the trades and local services the estate
1391
+ * builds for. A seed list, grown from what the null models and client sites
1392
+ * show. Deliberately absent: "services", "solutions", "projects", "work",
1393
+ * "quality", which say nothing about what is done.
1394
+ */
1395
+ declare const TRADE_NOUNS: readonly string[];
1396
+ /**
1397
+ * Stock figures: numbers every site in every trade uses, masked before
1398
+ * counting so they cannot pass for a fact.
1399
+ */
1400
+ declare const STOCK_FIGURES: readonly RegExp[];
1401
+ declare const countWords: (text: string) => number;
1402
+ /**
1403
+ * The brief's own terms: its names and places (capitalised words, even at the
1404
+ * start of a sentence), its numbers, and its trade nouns.
1405
+ */
1406
+ declare function briefTerms(brief: string): string[];
1407
+ /** Every distinct specific in the text. Lines are read one at a time: pass blocks separated by newlines. */
1408
+ declare function specifics(text: string, brief?: string): Specific[];
1409
+ /** How particular a text is. Pure. */
1410
+ declare function specificity(text: string, brief?: string): Specificity;
1411
+ /** What a page says where it makes its case: the first screen, and the sections that sell the work. */
1412
+ interface PageCopy {
1413
+ url: string;
1414
+ hero: string[];
1415
+ services: string[];
1416
+ /** Every line of page text: what the other page is checked against. */
1417
+ all: string[];
1418
+ }
1419
+ /** Blocks as sentences, short fragments dropped. */
1420
+ declare function sentencesOf(blocks: string[]): string[];
1421
+ /**
1422
+ * The hero and service copy of a snapshot. Null when the snapshot has no page
1423
+ * text: one taken before craft recorded it.
1424
+ */
1425
+ declare function pageCopy(snapshot: Snapshot): PageCopy | null;
1426
+ interface CopySide {
1427
+ url: string;
1428
+ /** Hero and service sentences read. */
1429
+ sentences: number;
1430
+ /** Of those, how many hold nothing the other page does not also say. */
1431
+ interchangeable: string[];
1432
+ /** interchangeable / sentences, or null with no sentences. */
1433
+ share: number | null;
1434
+ /** Specificity of the hero and service copy together. */
1435
+ perHundred: number;
1436
+ }
1437
+ interface CompetitorComparison {
1438
+ ours: CopySide;
1439
+ competitor: CopySide;
1440
+ /** A brief's terms were recognised on both pages. */
1441
+ brief: boolean;
1442
+ }
1443
+ /**
1444
+ * Which hero and service sentences either page could swap with the other:
1445
+ * each holds no specific the other page lacks. A shared town or trade noun is
1446
+ * not unique; the business's own name, its prices and its jobs are. Pure.
1447
+ */
1448
+ declare function compareCompetitor(ours: PageCopy, competitor: PageCopy, brief?: string): CompetitorComparison;
1449
+ declare function formatCompetitor(result: CompetitorComparison): string;
1450
+
1451
+ /**
1452
+ * Specificity tells: a page that could belong to anyone.
1453
+ *
1454
+ * A model given no brief writes the claim every site makes, because it has
1455
+ * nothing particular to say: "Quality you can trust", "Your vision, our
1456
+ * expertise". And it asserts standing ("trusted", "fully qualified",
1457
+ * "award-winning") with nothing next to the assertion to back it, because it
1458
+ * has no reviews and no registration number to put there.
1459
+ *
1460
+ * Both are read from what the page says, not how it looks: `specificity.ts`
1461
+ * counts the particulars.
1462
+ */
1463
+
1464
+ /**
1465
+ * The home page's source, where the first claim is made: a root route file
1466
+ * (`app/page.tsx`, `pages/index.tsx`, `src/pages/index.astro`, SvelteKit's
1467
+ * root `+page.svelte`), a top-level `index.html`, or a hero or home component.
1468
+ * Every other page's headline is a label ("Privacy policy", "Boiler
1469
+ * repairs"), not a claim, and is not judged.
1470
+ */
1471
+ declare const HOME_FILE: RegExp;
1472
+ /** A home page URL: the site root, `index.html`, or a locale root such as `/en-gb/`. */
1473
+ declare const HOME_URL: RegExp;
1474
+ /**
1475
+ * A claim of standing: what a visitor has to take on trust unless something
1476
+ * next to it backs it. Numbers of years are left out: those are checkable
1477
+ * facts, and `craft copy claims` lists them.
1478
+ */
1479
+ declare const STANDING_CLAIM: RegExp;
1480
+ /** Accreditation bodies, trade registers and review platforms, by the names they trade under. */
1481
+ declare const PROOF_NAMES: readonly string[];
1482
+ /** Every claim of standing with no review or accreditation in its section or the one either side. */
1483
+ declare function unprovenClaims(s: Snapshot): {
1484
+ claim: string;
1485
+ sentence: string;
1486
+ section: string;
1487
+ }[];
1488
+ declare const SPECIFICITY_COPY_TELLS: CopyTell[];
1489
+ declare const SPECIFICITY_RENDERED_TELLS: RenderedTell[];
1490
+
1329
1491
  /**
1330
1492
  * Baselines: adopt craft on a repo that already has findings, and fail only
1331
1493
  * on new ones.
@@ -2390,4 +2552,4 @@ declare function characterReport(input: CharacterInputs, copyTells?: Set<string>
2390
2552
 
2391
2553
  declare function retrofitPlan(report: CharacterReport, direction?: ArtDirection | null): string;
2392
2554
 
2393
- export { AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, COMPONENT_ROLES, type CandidateKind, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type ComponentPair, type ComponentTypicality, type ContrastCheck, type ContrastReport, type CopyTell, type CraftConfig, type CraftTokensInput, type CraftTokensResult, DEFAULT_IGNORE, DENSITIES, DENSITY_NAMES, DIRECTION_VERSION, DISTANCE_WEIGHTS, DURATION_MS, DURATION_S, type DensityCssOptions, type DensityName, type DirectionChoice, type DirectionProblem, type DirectionReport, type DirectionSource, 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, type Fact, type FactComparison, type FactKind, type Finding, type Fingerprint, type FingerprintDistance, type FingerprintSection, type FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOUSE, HOUSE_BUDGET, HOUSE_LOCKED, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, IMAGERY_TELLS, type ImageProvenance, type ImageRole, JSON_SCHEMA_VERSION, LICENCES, LIGHTNESS_CURVE, type LicenceEntry, type LicenceSubject, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, type MultiClient, NEGATIVE_REASSURANCE, NULL_VERSION, type NullModel, type NullRun, type Oklch, type OklchToHexOptions, PILL_LIMIT, PLAINER_WORDS, PROMO_BADGE, PROPOSED, PROVENANCE_SCAN_BYTES, type PaletteColour, type Proposal, type ProposeInput, RAMP_STEPS, READABLE_SNAPSHOT_VERSIONS, REFLEX_FONTS_1, REFLEX_FONTS_2, RENDERED_PATHS, RENDERED_TELLS, REVEAL_LIMIT, REVIEW_PHRASES, type Ramp, type RampFromAnchorsOptions, type RampOptions, type RampStep, type RenderedTell, type RestraintBudget, type RestraintReport, type RestraintSeverity, type RestraintViolation, type Rgb, type RgbaImage, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPRING, STOCK_AVATAR_HOSTS, STOCK_PHOTO_HOSTS, STOCK_PHRASES, type SarifOptions, type SarifTell, type SectionGeometry, type SectionKind, type SectionRhythmOptions, type SectionRole, type SemanticInput, type SemanticResult, type Severity, type SeverityLevel, type SeverityOverride, 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 Surface, TELL_HEAVY, TYPE_STEPS, TYPICAL_AT, type TailwindPreset, type Tell, type TellException, type TypeStep, type Typicality, VAGUE_WORDS, type ValidateContext, type Verdict, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, auditSnapshot, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, closestComponents, compareFacts, compareToEstate, componentDistance, componentShared, componentTypicalities, componentTypicality, componentsOf, craftTokens, createBaseline, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estateComponentPairs, estatePairs, exitDuration, extractHtml, faceLicence, findClaims, findColours, findingKey, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatClaims, formatComparison, formatHex, formatReport, globToRegExp, groundName, harvest, hexToOklch, holdHouseBlocks, houseRule, hueName, hueSwatch, ignoreMatcher, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, layoutDistance, licencesJson, makeSnapshot, motionTokens, namedSources, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, paletteFromPixels, parseBaseline, parseColour, parseCraftConfig, parseHex, propose, protectedFacts, ramp, rampFromAnchors, readSnapshot, retrofitPlan, runTell, scanProvenance, scanSource, sectionRhythm, semantic, severityExceptions, shapeName, sharedParts, shouldAnimate, siblingLine, stockAvatarSource, stockSource, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, toJson, toSarif, typeFeatureTokens, typicality, validateDirection, visibleText, visualMeasures, wcagContrast, wordListsJson };
2555
+ export { AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, COMPONENT_ROLES, 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 ContrastCheck, type ContrastReport, type CopySide, type CopyTell, type CraftConfig, type CraftTokensInput, type CraftTokensResult, DEFAULT_IGNORE, DENSITIES, DENSITY_NAMES, DIRECTION_VERSION, DISTANCE_WEIGHTS, DURATION_MS, DURATION_S, type DensityCssOptions, type DensityName, type DirectionChoice, type DirectionProblem, type DirectionReport, type DirectionSource, 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, type Fact, type FactComparison, type FactKind, type Finding, type Fingerprint, type FingerprintDistance, type FingerprintSection, type FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOME_FILE, HOME_URL, HOUSE, HOUSE_BUDGET, HOUSE_LOCKED, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, IMAGERY_TELLS, type ImageProvenance, type ImageRole, JSON_SCHEMA_VERSION, LICENCES, LIGHTNESS_CURVE, type LicenceEntry, type LicenceSubject, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, type MultiClient, NEGATIVE_REASSURANCE, NULL_VERSION, type NullModel, type NullRun, type Oklch, type OklchToHexOptions, PILL_LIMIT, PLAINER_WORDS, PROMO_BADGE, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageCopy, type PaletteColour, type Proposal, type ProposeInput, RAMP_STEPS, READABLE_SNAPSHOT_VERSIONS, REFLEX_FONTS_1, REFLEX_FONTS_2, RENDERED_PATHS, RENDERED_TELLS, REVEAL_LIMIT, REVIEW_PHRASES, type Ramp, type RampFromAnchorsOptions, type RampOptions, type RampStep, type RenderedTell, type RestraintBudget, type RestraintReport, type RestraintSeverity, type RestraintViolation, type Rgb, type RgbaImage, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, 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, type SarifOptions, type SarifTell, type SectionGeometry, type SectionKind, type SectionRhythmOptions, type SectionRole, type SemanticInput, type SemanticResult, type Severity, type SeverityLevel, type SeverityOverride, 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 Surface, TELL_HEAVY, TRADE_NOUNS, TYPE_STEPS, TYPICAL_AT, type TailwindPreset, type Tell, type TellException, type TypeStep, type Typicality, VAGUE_WORDS, type ValidateContext, type Verdict, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, auditSnapshot, briefTerms, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, closestComponents, compareCompetitor, compareFacts, compareToEstate, componentDistance, componentShared, componentTypicalities, componentTypicality, componentsOf, countWords, craftTokens, createBaseline, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estateComponentPairs, estatePairs, exitDuration, extractHtml, faceLicence, findClaims, findColours, findingKey, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatClaims, formatComparison, formatCompetitor, formatHex, formatReport, globToRegExp, groundName, harvest, hexToOklch, holdHouseBlocks, houseRule, hueName, hueSwatch, ignoreMatcher, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, layoutDistance, licencesJson, makeSnapshot, motionTokens, namedSources, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, pageCopy, paletteFromPixels, parseBaseline, parseColour, parseCraftConfig, parseHex, propose, protectedFacts, ramp, rampFromAnchors, readSnapshot, retrofitPlan, runTell, scanProvenance, scanSource, sectionRhythm, semantic, sentencesOf, severityExceptions, shapeName, sharedParts, shouldAnimate, siblingLine, specificity, specifics, stockAvatarSource, stockSource, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, toJson, toSarif, typeFeatureTokens, typicality, unprovenClaims, validateDirection, visibleText, visualMeasures, wcagContrast, wordListsJson };
package/dist/index.d.ts CHANGED
@@ -796,6 +796,13 @@ interface SnapshotSection {
796
796
  carousel?: boolean;
797
797
  /** Version 2: large standalone figures ("500+", "98%", "4.9/5") at 24px or more. */
798
798
  figures?: number;
799
+ /**
800
+ * Version 2, craft 0.13 and later: the section's visible text, one block per
801
+ * heading, paragraph, list item or quote, each at most 300 characters, at
802
+ * most 40. Navigation, buttons and footers are left out. What the
803
+ * specificity and proof tells read.
804
+ */
805
+ text?: string[];
799
806
  }
800
807
  type ImageRole = "photo" | "illustration" | "icon" | "avatar" | "logo";
801
808
  /**
@@ -907,10 +914,17 @@ interface Snapshot {
907
914
  /** Version 2: absent when the page could not be screenshotted. */
908
915
  visual?: SnapshotVisual;
909
916
  /**
910
- * Version 2, craft 0.14 and later: every image on the page, largest first, at
917
+ * Version 2, craft 0.13 and later: every image on the page, largest first, at
911
918
  * most 60. Absent in older snapshots, which the imagery tells never judge.
912
919
  */
913
920
  images?: SnapshotImage[];
921
+ /**
922
+ * Version 2, craft 0.13 and later: the text a visitor reads before
923
+ * scrolling, at most 12 blocks. Navigation, a header without the headline,
924
+ * and the words on buttons and links are left out: this is the page's
925
+ * claim, not its menu or its calls to action.
926
+ */
927
+ firstScreenText?: string[];
914
928
  /** Set when a version 1 snapshot was read and upgraded in memory. */
915
929
  migratedFrom?: 1;
916
930
  /** The design-system measures trawl has collected since v7, kept so it can switch to this. */
@@ -1144,7 +1158,7 @@ interface CheckReport {
1144
1158
  * Bumped whenever an entry is added, removed or its detection changes, so a
1145
1159
  * report can say which list it was judged against.
1146
1160
  */
1147
- declare const CATALOGUE_VERSION = "2026.09.11";
1161
+ declare const CATALOGUE_VERSION = "2026.09.12";
1148
1162
  declare const CATALOGUE: readonly Tell[];
1149
1163
  declare function tellById(id: string): Tell | undefined;
1150
1164
  /**
@@ -1326,6 +1340,154 @@ declare function namedSources(sentence: string): string[];
1326
1340
  declare function findClaims(files: SourceFile[]): ClaimsReport;
1327
1341
  declare function formatClaims(report: ClaimsReport, label?: string): string;
1328
1342
 
1343
+ /**
1344
+ * Specificity: how much of a page only this business could have written.
1345
+ *
1346
+ * "Quality you can trust" fits every site in every trade. "Boilers fixed the
1347
+ * same day in Brackley, from £85" fits one. The difference is countable: names,
1348
+ * places, numbers, prices, dates, and the nouns of the trade. A page built
1349
+ * from no brief has almost none, because the model had none to give.
1350
+ *
1351
+ * Pure over text. Two things keep the count honest:
1352
+ *
1353
+ * - A Title Case or all-capitals line carries no capital-letter information,
1354
+ * so its capitals are not read as names ("Quality You Can Trust" is not a
1355
+ * proper noun). Its numbers, trade nouns and brief terms still count.
1356
+ * - The stock figures every site uses ("24/7", "100%", "5-star", "No. 1") are
1357
+ * masked before counting. They are numbers, not specifics.
1358
+ *
1359
+ * A brief (from `--brief`, `art-direction.json` or a null model) adds its own
1360
+ * terms: the business's name, its town and its trade, recognised wherever
1361
+ * they appear, even at the start of a sentence or in a shouted heading.
1362
+ */
1363
+
1364
+ type SpecificKind = FactKind | "trade" | "brief";
1365
+ interface Specific {
1366
+ kind: SpecificKind;
1367
+ /** As written. */
1368
+ value: string;
1369
+ /** Lower case, for comparing across pages. */
1370
+ key: string;
1371
+ }
1372
+ interface Specificity {
1373
+ words: number;
1374
+ /** Each distinct specific once: repeating a town ten times does not make a page ten times more particular. */
1375
+ specifics: Specific[];
1376
+ /** Specifics that are not trade nouns: a name, place, number, price, date, contact detail or brief term. */
1377
+ hard: number;
1378
+ /** Distinct trade nouns. */
1379
+ trade: number;
1380
+ /** Distinct specifics per 100 words, to one decimal place. */
1381
+ perHundred: number;
1382
+ /**
1383
+ * No hard specific and fewer than two trade nouns. One trade noun alone
1384
+ * ("quality plumbing you can trust") is what every competitor in the trade
1385
+ * also says.
1386
+ */
1387
+ generic: boolean;
1388
+ }
1389
+ /**
1390
+ * Concrete nouns of the work, across the trades and local services the estate
1391
+ * builds for. A seed list, grown from what the null models and client sites
1392
+ * show. Deliberately absent: "services", "solutions", "projects", "work",
1393
+ * "quality", which say nothing about what is done.
1394
+ */
1395
+ declare const TRADE_NOUNS: readonly string[];
1396
+ /**
1397
+ * Stock figures: numbers every site in every trade uses, masked before
1398
+ * counting so they cannot pass for a fact.
1399
+ */
1400
+ declare const STOCK_FIGURES: readonly RegExp[];
1401
+ declare const countWords: (text: string) => number;
1402
+ /**
1403
+ * The brief's own terms: its names and places (capitalised words, even at the
1404
+ * start of a sentence), its numbers, and its trade nouns.
1405
+ */
1406
+ declare function briefTerms(brief: string): string[];
1407
+ /** Every distinct specific in the text. Lines are read one at a time: pass blocks separated by newlines. */
1408
+ declare function specifics(text: string, brief?: string): Specific[];
1409
+ /** How particular a text is. Pure. */
1410
+ declare function specificity(text: string, brief?: string): Specificity;
1411
+ /** What a page says where it makes its case: the first screen, and the sections that sell the work. */
1412
+ interface PageCopy {
1413
+ url: string;
1414
+ hero: string[];
1415
+ services: string[];
1416
+ /** Every line of page text: what the other page is checked against. */
1417
+ all: string[];
1418
+ }
1419
+ /** Blocks as sentences, short fragments dropped. */
1420
+ declare function sentencesOf(blocks: string[]): string[];
1421
+ /**
1422
+ * The hero and service copy of a snapshot. Null when the snapshot has no page
1423
+ * text: one taken before craft recorded it.
1424
+ */
1425
+ declare function pageCopy(snapshot: Snapshot): PageCopy | null;
1426
+ interface CopySide {
1427
+ url: string;
1428
+ /** Hero and service sentences read. */
1429
+ sentences: number;
1430
+ /** Of those, how many hold nothing the other page does not also say. */
1431
+ interchangeable: string[];
1432
+ /** interchangeable / sentences, or null with no sentences. */
1433
+ share: number | null;
1434
+ /** Specificity of the hero and service copy together. */
1435
+ perHundred: number;
1436
+ }
1437
+ interface CompetitorComparison {
1438
+ ours: CopySide;
1439
+ competitor: CopySide;
1440
+ /** A brief's terms were recognised on both pages. */
1441
+ brief: boolean;
1442
+ }
1443
+ /**
1444
+ * Which hero and service sentences either page could swap with the other:
1445
+ * each holds no specific the other page lacks. A shared town or trade noun is
1446
+ * not unique; the business's own name, its prices and its jobs are. Pure.
1447
+ */
1448
+ declare function compareCompetitor(ours: PageCopy, competitor: PageCopy, brief?: string): CompetitorComparison;
1449
+ declare function formatCompetitor(result: CompetitorComparison): string;
1450
+
1451
+ /**
1452
+ * Specificity tells: a page that could belong to anyone.
1453
+ *
1454
+ * A model given no brief writes the claim every site makes, because it has
1455
+ * nothing particular to say: "Quality you can trust", "Your vision, our
1456
+ * expertise". And it asserts standing ("trusted", "fully qualified",
1457
+ * "award-winning") with nothing next to the assertion to back it, because it
1458
+ * has no reviews and no registration number to put there.
1459
+ *
1460
+ * Both are read from what the page says, not how it looks: `specificity.ts`
1461
+ * counts the particulars.
1462
+ */
1463
+
1464
+ /**
1465
+ * The home page's source, where the first claim is made: a root route file
1466
+ * (`app/page.tsx`, `pages/index.tsx`, `src/pages/index.astro`, SvelteKit's
1467
+ * root `+page.svelte`), a top-level `index.html`, or a hero or home component.
1468
+ * Every other page's headline is a label ("Privacy policy", "Boiler
1469
+ * repairs"), not a claim, and is not judged.
1470
+ */
1471
+ declare const HOME_FILE: RegExp;
1472
+ /** A home page URL: the site root, `index.html`, or a locale root such as `/en-gb/`. */
1473
+ declare const HOME_URL: RegExp;
1474
+ /**
1475
+ * A claim of standing: what a visitor has to take on trust unless something
1476
+ * next to it backs it. Numbers of years are left out: those are checkable
1477
+ * facts, and `craft copy claims` lists them.
1478
+ */
1479
+ declare const STANDING_CLAIM: RegExp;
1480
+ /** Accreditation bodies, trade registers and review platforms, by the names they trade under. */
1481
+ declare const PROOF_NAMES: readonly string[];
1482
+ /** Every claim of standing with no review or accreditation in its section or the one either side. */
1483
+ declare function unprovenClaims(s: Snapshot): {
1484
+ claim: string;
1485
+ sentence: string;
1486
+ section: string;
1487
+ }[];
1488
+ declare const SPECIFICITY_COPY_TELLS: CopyTell[];
1489
+ declare const SPECIFICITY_RENDERED_TELLS: RenderedTell[];
1490
+
1329
1491
  /**
1330
1492
  * Baselines: adopt craft on a repo that already has findings, and fail only
1331
1493
  * on new ones.
@@ -2390,4 +2552,4 @@ declare function characterReport(input: CharacterInputs, copyTells?: Set<string>
2390
2552
 
2391
2553
  declare function retrofitPlan(report: CharacterReport, direction?: ArtDirection | null): string;
2392
2554
 
2393
- export { AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, COMPONENT_ROLES, type CandidateKind, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type ComponentPair, type ComponentTypicality, type ContrastCheck, type ContrastReport, type CopyTell, type CraftConfig, type CraftTokensInput, type CraftTokensResult, DEFAULT_IGNORE, DENSITIES, DENSITY_NAMES, DIRECTION_VERSION, DISTANCE_WEIGHTS, DURATION_MS, DURATION_S, type DensityCssOptions, type DensityName, type DirectionChoice, type DirectionProblem, type DirectionReport, type DirectionSource, 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, type Fact, type FactComparison, type FactKind, type Finding, type Fingerprint, type FingerprintDistance, type FingerprintSection, type FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOUSE, HOUSE_BUDGET, HOUSE_LOCKED, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, IMAGERY_TELLS, type ImageProvenance, type ImageRole, JSON_SCHEMA_VERSION, LICENCES, LIGHTNESS_CURVE, type LicenceEntry, type LicenceSubject, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, type MultiClient, NEGATIVE_REASSURANCE, NULL_VERSION, type NullModel, type NullRun, type Oklch, type OklchToHexOptions, PILL_LIMIT, PLAINER_WORDS, PROMO_BADGE, PROPOSED, PROVENANCE_SCAN_BYTES, type PaletteColour, type Proposal, type ProposeInput, RAMP_STEPS, READABLE_SNAPSHOT_VERSIONS, REFLEX_FONTS_1, REFLEX_FONTS_2, RENDERED_PATHS, RENDERED_TELLS, REVEAL_LIMIT, REVIEW_PHRASES, type Ramp, type RampFromAnchorsOptions, type RampOptions, type RampStep, type RenderedTell, type RestraintBudget, type RestraintReport, type RestraintSeverity, type RestraintViolation, type Rgb, type RgbaImage, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, SIBLING_AT, SNAPSHOT_VERSION, SOURCE_KINDS, SPACE_STEPS, SPRING, STOCK_AVATAR_HOSTS, STOCK_PHOTO_HOSTS, STOCK_PHRASES, type SarifOptions, type SarifTell, type SectionGeometry, type SectionKind, type SectionRhythmOptions, type SectionRole, type SemanticInput, type SemanticResult, type Severity, type SeverityLevel, type SeverityOverride, 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 Surface, TELL_HEAVY, TYPE_STEPS, TYPICAL_AT, type TailwindPreset, type Tell, type TellException, type TypeStep, type Typicality, VAGUE_WORDS, type ValidateContext, type Verdict, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, auditSnapshot, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, closestComponents, compareFacts, compareToEstate, componentDistance, componentShared, componentTypicalities, componentTypicality, componentsOf, craftTokens, createBaseline, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estateComponentPairs, estatePairs, exitDuration, extractHtml, faceLicence, findClaims, findColours, findingKey, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatClaims, formatComparison, formatHex, formatReport, globToRegExp, groundName, harvest, hexToOklch, holdHouseBlocks, houseRule, hueName, hueSwatch, ignoreMatcher, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, layoutDistance, licencesJson, makeSnapshot, motionTokens, namedSources, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, paletteFromPixels, parseBaseline, parseColour, parseCraftConfig, parseHex, propose, protectedFacts, ramp, rampFromAnchors, readSnapshot, retrofitPlan, runTell, scanProvenance, scanSource, sectionRhythm, semantic, severityExceptions, shapeName, sharedParts, shouldAnimate, siblingLine, stockAvatarSource, stockSource, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, toJson, toSarif, typeFeatureTokens, typicality, validateDirection, visibleText, visualMeasures, wcagContrast, wordListsJson };
2555
+ export { AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHOICE_KEYS, COMPONENT_ROLES, 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 ContrastCheck, type ContrastReport, type CopySide, type CopyTell, type CraftConfig, type CraftTokensInput, type CraftTokensResult, DEFAULT_IGNORE, DENSITIES, DENSITY_NAMES, DIRECTION_VERSION, DISTANCE_WEIGHTS, DURATION_MS, DURATION_S, type DensityCssOptions, type DensityName, type DirectionChoice, type DirectionProblem, type DirectionReport, type DirectionSource, 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, type Fact, type FactComparison, type FactKind, type Finding, type Fingerprint, type FingerprintDistance, type FingerprintSection, type FluidSpaceOptions, type FluidTypeOptions, type FluidTypeResult, type FontClass, type Gamut, type Generation, HOME_FILE, HOME_URL, HOUSE, HOUSE_BUDGET, HOUSE_LOCKED, HOUSE_TYPE, type HarvestCandidate, type HarvestOptions, type Hit, type HouseRule, type HouseTier, IMAGERY_TELLS, type ImageProvenance, type ImageRole, JSON_SCHEMA_VERSION, LICENCES, LIGHTNESS_CURVE, type LicenceEntry, type LicenceSubject, MIN_DECIDED, MIN_RUNS, type MotionDecision, type MotionKind, type MultiClient, NEGATIVE_REASSURANCE, NULL_VERSION, type NullModel, type NullRun, type Oklch, type OklchToHexOptions, PILL_LIMIT, PLAINER_WORDS, PROMO_BADGE, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageCopy, type PaletteColour, type Proposal, type ProposeInput, RAMP_STEPS, READABLE_SNAPSHOT_VERSIONS, REFLEX_FONTS_1, REFLEX_FONTS_2, RENDERED_PATHS, RENDERED_TELLS, REVEAL_LIMIT, REVIEW_PHRASES, type Ramp, type RampFromAnchorsOptions, type RampOptions, type RampStep, type RenderedTell, type RestraintBudget, type RestraintReport, type RestraintSeverity, type RestraintViolation, type Rgb, type RgbaImage, SCALE, SHADCN_LIMIT, SHADCN_PRIMITIVES, 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, type SarifOptions, type SarifTell, type SectionGeometry, type SectionKind, type SectionRhythmOptions, type SectionRole, type SemanticInput, type SemanticResult, type Severity, type SeverityLevel, type SeverityOverride, 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 Surface, TELL_HEAVY, TRADE_NOUNS, TYPE_STEPS, TYPICAL_AT, type TailwindPreset, type Tell, type TellException, type TypeStep, type Typicality, VAGUE_WORDS, type ValidateContext, type Verdict, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, auditSnapshot, briefTerms, catalogueTable, characterReport, checkCopy, checkPair, checkRestraint, choicesOf, closestComponents, compareCompetitor, compareFacts, compareToEstate, componentDistance, componentShared, componentTypicalities, componentTypicality, componentsOf, countWords, craftTokens, createBaseline, deltaEOk, deltaEOkHex, densityCss, densityTokens, emitCss, emptyEstate, estateComponentPairs, estatePairs, exitDuration, extractHtml, faceLicence, findClaims, findColours, findingKey, fingerprint, fingerprintDistance, fluidClamp, fluidSpace, fluidType, fontClass, formatClaims, formatComparison, formatCompetitor, formatHex, formatReport, globToRegExp, groundName, harvest, hexToOklch, holdHouseBlocks, houseRule, hueName, hueSwatch, ignoreMatcher, inP3Gamut, inSrgbGamut, initDirection, isAiViolet, isCream, layoutDistance, licencesJson, makeSnapshot, motionTokens, namedSources, nearest, normaliseFamily, nullPrompt, oklchToHex, oklchToRgb, pageCopy, paletteFromPixels, parseBaseline, parseColour, parseCraftConfig, parseHex, propose, protectedFacts, ramp, rampFromAnchors, readSnapshot, retrofitPlan, runTell, scanProvenance, scanSource, sectionRhythm, semantic, sentencesOf, severityExceptions, shapeName, sharedParts, shouldAnimate, siblingLine, specificity, specifics, stockAvatarSource, stockSource, tailwindV3Preset, tailwindV4Theme, tellById, toGamut, toJson, toSarif, typeFeatureTokens, typicality, unprovenClaims, validateDirection, visibleText, visualMeasures, wcagContrast, wordListsJson };