@domandigital/craft 0.21.0 → 0.22.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/CHARACTER.md +134 -3
  3. package/COPY.md +10 -3
  4. package/PRINCIPLES.md +9 -0
  5. package/README.md +20 -1
  6. package/design-review.schema.json +156 -0
  7. package/dist/{audit-LQ2TDISV.js → audit-CDMZ6JHT.js} +3 -3
  8. package/dist/audit.cjs +258 -10
  9. package/dist/audit.js +258 -10
  10. package/dist/{chunk-6DRBFDWT.js → chunk-D6TAYXOS.js} +1 -1
  11. package/dist/{chunk-G47ZCBD5.js → chunk-IAU4ZAIU.js} +2 -2
  12. package/dist/{chunk-STWH5EYU.js → chunk-ICEG4S56.js} +5 -5
  13. package/dist/{chunk-BYXDX66D.js → chunk-JT2NLOQI.js} +4 -4
  14. package/dist/{chunk-FNB3NP57.js → chunk-KH56TEFK.js} +5 -5
  15. package/dist/{chunk-DE2ZAV4G.js → chunk-MLYHSPF7.js} +166 -3
  16. package/dist/{chunk-ONA73THJ.js → chunk-NV6FKMID.js} +93 -8
  17. package/dist/{chunk-4WAIOZ4W.js → chunk-WTR77PEM.js} +14 -14
  18. package/dist/{chunk-BQ7H23QP.js → chunk-XSKLVEI6.js} +109 -2
  19. package/dist/{cli-YSGPGQ7R.js → cli-223CUNU3.js} +9 -9
  20. package/dist/{cli-XAWJWK7I.js → cli-3QV4FKM4.js} +6 -6
  21. package/dist/{cli-PBCU7Z64.js → cli-4PXDNTZX.js} +4 -4
  22. package/dist/{cli-7QL26YPR.js → cli-6SMNHOCJ.js} +1 -1
  23. package/dist/{cli-5VHX5EJF.js → cli-7PPDBN6N.js} +5 -5
  24. package/dist/{cli-5QI6NIZN.js → cli-CQUORKMC.js} +6 -6
  25. package/dist/{cli-EYHRJJAX.js → cli-FKWJF4V4.js} +8 -5
  26. package/dist/{cli-RHDNHL4U.js → cli-IQ5U76AZ.js} +5 -5
  27. package/dist/cli.js +3 -3
  28. package/dist/index.cjs +261 -10
  29. package/dist/index.d.cts +49 -3
  30. package/dist/index.d.ts +49 -3
  31. package/dist/index.js +260 -10
  32. package/dist/phone.cjs +57 -7
  33. package/dist/phone.d.cts +28 -2
  34. package/dist/phone.d.ts +28 -2
  35. package/dist/phone.js +54 -7
  36. package/dist/{viewports-cli-XYYX4SKV.js → viewports-cli-H6UIUMBC.js} +43 -82
  37. package/package.json +1 -1
  38. package/word-lists.json +11 -2
package/dist/index.d.cts CHANGED
@@ -1252,7 +1252,7 @@ interface CheckReport {
1252
1252
  * Bumped whenever an entry is added, removed or its detection changes, so a
1253
1253
  * report can say which list it was judged against.
1254
1254
  */
1255
- declare const CATALOGUE_VERSION = "2026.10.4";
1255
+ declare const CATALOGUE_VERSION = "2026.10.5";
1256
1256
  declare const CATALOGUE: readonly Tell[];
1257
1257
  declare function tellById(id: string): Tell | undefined;
1258
1258
  /**
@@ -2658,6 +2658,14 @@ type EvidenceKind = (typeof EVIDENCE_KINDS)[number];
2658
2658
  */
2659
2659
  declare const CHANGE_CLASSES: readonly ["confirmed-correction", "design-hypothesis", "optional-refinement", "no-change"];
2660
2660
  type ChangeClass = (typeof CHANGE_CLASSES)[number];
2661
+ /**
2662
+ * What was done with a note in a reviewer's own record (`reviews/`).
2663
+ * take-reworded: the diagnosis is taken and the suggested wording rewritten,
2664
+ * because a reviewer's lines are copy-checked like anyone's. partly: the aim
2665
+ * is taken by a smaller or different change. known: already tracked.
2666
+ */
2667
+ declare const REVIEW_VERDICTS: readonly ["praise", "take", "take-reworded", "partly", "decline", "founder-decision", "known"];
2668
+ type ReviewVerdict = (typeof REVIEW_VERDICTS)[number];
2661
2669
  /**
2662
2670
  * The ladder of minimum effective change, smallest first. The last two are
2663
2671
  * structural and have to say why every smaller step was not enough.
@@ -2728,6 +2736,29 @@ interface ReviewNote {
2728
2736
  placeholder?: string;
2729
2737
  }[];
2730
2738
  };
2739
+ /** Where the note was left, so it can be found again: `{ tool: "pastel", canvas, comment, url }`. */
2740
+ source?: {
2741
+ tool: string;
2742
+ canvas?: number | string;
2743
+ comment?: number | string;
2744
+ commentId?: number | string;
2745
+ url?: string;
2746
+ at?: string;
2747
+ };
2748
+ /** What was done with it, in a reviewer corpus. */
2749
+ verdict?: ReviewVerdict;
2750
+ /** The founder's call where the note needed one: the decision, stated and dated. */
2751
+ founderRuling?: {
2752
+ date: string;
2753
+ ruling: string;
2754
+ };
2755
+ /** The principle it teaches, by its id in CHARACTER.md. */
2756
+ principle?: string;
2757
+ /** Tells it informed; with text, the words that tell must fire on. */
2758
+ tells?: {
2759
+ id: string;
2760
+ text?: string;
2761
+ }[];
2731
2762
  }
2732
2763
  interface DesignReview {
2733
2764
  /** Publication is the default. Protected previews may reserve explicitly marked assets. */
@@ -2736,6 +2767,21 @@ interface DesignReview {
2736
2767
  version: 1;
2737
2768
  /** Who or what was reviewed, and at which width. */
2738
2769
  subject: string;
2770
+ /** In a reviewer corpus: whose review this is, the day, where it was given and who ruled on it. */
2771
+ reviewer?: string;
2772
+ date?: string;
2773
+ source?: {
2774
+ tool: string;
2775
+ canvas?: number | string;
2776
+ link?: string;
2777
+ comments?: number;
2778
+ from?: string;
2779
+ to?: string;
2780
+ };
2781
+ rulings?: {
2782
+ by: string;
2783
+ date: string;
2784
+ };
2739
2785
  notes: ReviewNote[];
2740
2786
  }
2741
2787
  type ReviewOutcome = "change" | "hold" | "no-change" | "gather-evidence";
@@ -3225,7 +3271,7 @@ declare const NEGATIVE_REASSURANCE: readonly ["no risk", "never locked in", "not
3225
3271
  /** Banned only when vague. Reported for review, never blocking. */
3226
3272
  declare const VAGUE_WORDS: readonly ["innovative", "scalable", "end-to-end", "streamline", "solutions"];
3227
3273
  /** The review tier of copy-rules.md: normal English once, a tell when stacked. */
3228
- declare const REVIEW_PHRASES: readonly ["great question", "of course!", "absolutely!", "absolutely,", "truly", "genuinely", "deeply", "clear", "clearly", "ultimately", "one-size-fits-all", "one size fits all", "let me know if you'd", "the single most", "by far the most", "hands down the", "arguably the best", "bar none"];
3274
+ declare const REVIEW_PHRASES: readonly ["great question", "of course!", "absolutely!", "absolutely,", "truly", "genuinely", "deeply", "clear", "clearly", "ultimately", "one-size-fits-all", "one size fits all", "let me know if you'd", "the single most", "by far the most", "hands down the", "arguably the best", "bar none", "plain answer", "plain answers", "plain recommendation", "plain explanation", "plain explanations", "plain advice", "plain guidance", "plain-speaking", "plain speaking"];
3229
3275
 
3230
3276
  /**
3231
3277
  * The reason rule, one standard for every layer of an art direction.
@@ -3731,4 +3777,4 @@ declare function characterReport(input: CharacterInputs, copyTells?: Set<string>
3731
3777
 
3732
3778
  declare function retrofitPlan(report: CharacterReport, direction?: ArtDirection | null): string;
3733
3779
 
3734
- export { ACCENT_SCALE, ACCENT_SHARED, ADDITIONS, AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type ActionPosition, type Addition, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Band, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHANGE_CLASSES, CHANGE_STEPS, CHOICE_KEYS, COMPONENT_ROLES, CONFIDENCE_LENSES, CONSIDERATION, CONTENT_FEATURES, CONVENTION, CUSTOMER_KINDS, type Calibration, type CandidateKind, type ChangeClass, type ChangeStep, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type CompetitorComparison, type ComponentPair, type ComponentTypicality, type ConfidenceBasis, type ConfidenceContext, type ConfidenceFinding, type ConfidenceLens, 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 DesignReview, type DirectionChoice, type DirectionComparison, type DirectionMatch, type DirectionProblem, type DirectionReport, type DirectionSource, type DirectionSummary, type DurationName, EASE, EASE_TUPLE, EDGE_STEP, ESTATE_VERSION, EVIDENCE_KINDS, EXIT_RATIO, type EaseName, type EmitCssOptions, type EstateMatch, type EstatePair, type EstateRegister, type EstateSite, type EvidenceKind, 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, INSPECTED_KINDS, 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_DEPTHS, PROOF_KINDS, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageConfidence, type PageCopy, type PageHierarchy, type PageResult, type PageType, type PaletteColour, type PhotoReviewOptions, type PhotoReviewResult, type ProofDepth, type ProofItem, type ProofKind, 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, REPORTED_KINDS, REVEAL_LIMIT, REVIEW_BASES, 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 ReviewBasis, type ReviewEvidence, type ReviewNote, type ReviewOutcome, type ReviewProposal, type ReviewReport, type ReviewResult, 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 SuggestionStanding, 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, VIEWPORTS, type ValidateContext, type Verdict, type Viewport, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, assessConfidence, assessReview, 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, isStructural, 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, validateConfidence, validateDirection, validateHierarchy, validateJob, viewportOf, visibleText, visualMeasures, vocabulary, vocabularyCounts, wcagContrast, wordListsJson };
3780
+ export { ACCENT_SCALE, ACCENT_SHARED, ADDITIONS, AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type ActionPosition, type Addition, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Band, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHANGE_CLASSES, CHANGE_STEPS, CHOICE_KEYS, COMPONENT_ROLES, CONFIDENCE_LENSES, CONSIDERATION, CONTENT_FEATURES, CONVENTION, CUSTOMER_KINDS, type Calibration, type CandidateKind, type ChangeClass, type ChangeStep, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type CompetitorComparison, type ComponentPair, type ComponentTypicality, type ConfidenceBasis, type ConfidenceContext, type ConfidenceFinding, type ConfidenceLens, 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 DesignReview, type DirectionChoice, type DirectionComparison, type DirectionMatch, type DirectionProblem, type DirectionReport, type DirectionSource, type DirectionSummary, type DurationName, EASE, EASE_TUPLE, EDGE_STEP, ESTATE_VERSION, EVIDENCE_KINDS, EXIT_RATIO, type EaseName, type EmitCssOptions, type EstateMatch, type EstatePair, type EstateRegister, type EstateSite, type EvidenceKind, 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, INSPECTED_KINDS, 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_DEPTHS, PROOF_KINDS, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageConfidence, type PageCopy, type PageHierarchy, type PageResult, type PageType, type PaletteColour, type PhotoReviewOptions, type PhotoReviewResult, type ProofDepth, type ProofItem, type ProofKind, 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, REPORTED_KINDS, REVEAL_LIMIT, REVIEW_BASES, REVIEW_PHRASES, REVIEW_VERDICTS, 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 ReviewBasis, type ReviewEvidence, type ReviewNote, type ReviewOutcome, type ReviewProposal, type ReviewReport, type ReviewResult, type ReviewVerdict, 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 SuggestionStanding, 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, VIEWPORTS, type ValidateContext, type Verdict, type Viewport, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, assessConfidence, assessReview, 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, isStructural, 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, validateConfidence, validateDirection, validateHierarchy, validateJob, viewportOf, visibleText, visualMeasures, vocabulary, vocabularyCounts, wcagContrast, wordListsJson };
package/dist/index.d.ts CHANGED
@@ -1252,7 +1252,7 @@ interface CheckReport {
1252
1252
  * Bumped whenever an entry is added, removed or its detection changes, so a
1253
1253
  * report can say which list it was judged against.
1254
1254
  */
1255
- declare const CATALOGUE_VERSION = "2026.10.4";
1255
+ declare const CATALOGUE_VERSION = "2026.10.5";
1256
1256
  declare const CATALOGUE: readonly Tell[];
1257
1257
  declare function tellById(id: string): Tell | undefined;
1258
1258
  /**
@@ -2658,6 +2658,14 @@ type EvidenceKind = (typeof EVIDENCE_KINDS)[number];
2658
2658
  */
2659
2659
  declare const CHANGE_CLASSES: readonly ["confirmed-correction", "design-hypothesis", "optional-refinement", "no-change"];
2660
2660
  type ChangeClass = (typeof CHANGE_CLASSES)[number];
2661
+ /**
2662
+ * What was done with a note in a reviewer's own record (`reviews/`).
2663
+ * take-reworded: the diagnosis is taken and the suggested wording rewritten,
2664
+ * because a reviewer's lines are copy-checked like anyone's. partly: the aim
2665
+ * is taken by a smaller or different change. known: already tracked.
2666
+ */
2667
+ declare const REVIEW_VERDICTS: readonly ["praise", "take", "take-reworded", "partly", "decline", "founder-decision", "known"];
2668
+ type ReviewVerdict = (typeof REVIEW_VERDICTS)[number];
2661
2669
  /**
2662
2670
  * The ladder of minimum effective change, smallest first. The last two are
2663
2671
  * structural and have to say why every smaller step was not enough.
@@ -2728,6 +2736,29 @@ interface ReviewNote {
2728
2736
  placeholder?: string;
2729
2737
  }[];
2730
2738
  };
2739
+ /** Where the note was left, so it can be found again: `{ tool: "pastel", canvas, comment, url }`. */
2740
+ source?: {
2741
+ tool: string;
2742
+ canvas?: number | string;
2743
+ comment?: number | string;
2744
+ commentId?: number | string;
2745
+ url?: string;
2746
+ at?: string;
2747
+ };
2748
+ /** What was done with it, in a reviewer corpus. */
2749
+ verdict?: ReviewVerdict;
2750
+ /** The founder's call where the note needed one: the decision, stated and dated. */
2751
+ founderRuling?: {
2752
+ date: string;
2753
+ ruling: string;
2754
+ };
2755
+ /** The principle it teaches, by its id in CHARACTER.md. */
2756
+ principle?: string;
2757
+ /** Tells it informed; with text, the words that tell must fire on. */
2758
+ tells?: {
2759
+ id: string;
2760
+ text?: string;
2761
+ }[];
2731
2762
  }
2732
2763
  interface DesignReview {
2733
2764
  /** Publication is the default. Protected previews may reserve explicitly marked assets. */
@@ -2736,6 +2767,21 @@ interface DesignReview {
2736
2767
  version: 1;
2737
2768
  /** Who or what was reviewed, and at which width. */
2738
2769
  subject: string;
2770
+ /** In a reviewer corpus: whose review this is, the day, where it was given and who ruled on it. */
2771
+ reviewer?: string;
2772
+ date?: string;
2773
+ source?: {
2774
+ tool: string;
2775
+ canvas?: number | string;
2776
+ link?: string;
2777
+ comments?: number;
2778
+ from?: string;
2779
+ to?: string;
2780
+ };
2781
+ rulings?: {
2782
+ by: string;
2783
+ date: string;
2784
+ };
2739
2785
  notes: ReviewNote[];
2740
2786
  }
2741
2787
  type ReviewOutcome = "change" | "hold" | "no-change" | "gather-evidence";
@@ -3225,7 +3271,7 @@ declare const NEGATIVE_REASSURANCE: readonly ["no risk", "never locked in", "not
3225
3271
  /** Banned only when vague. Reported for review, never blocking. */
3226
3272
  declare const VAGUE_WORDS: readonly ["innovative", "scalable", "end-to-end", "streamline", "solutions"];
3227
3273
  /** The review tier of copy-rules.md: normal English once, a tell when stacked. */
3228
- declare const REVIEW_PHRASES: readonly ["great question", "of course!", "absolutely!", "absolutely,", "truly", "genuinely", "deeply", "clear", "clearly", "ultimately", "one-size-fits-all", "one size fits all", "let me know if you'd", "the single most", "by far the most", "hands down the", "arguably the best", "bar none"];
3274
+ declare const REVIEW_PHRASES: readonly ["great question", "of course!", "absolutely!", "absolutely,", "truly", "genuinely", "deeply", "clear", "clearly", "ultimately", "one-size-fits-all", "one size fits all", "let me know if you'd", "the single most", "by far the most", "hands down the", "arguably the best", "bar none", "plain answer", "plain answers", "plain recommendation", "plain explanation", "plain explanations", "plain advice", "plain guidance", "plain-speaking", "plain speaking"];
3229
3275
 
3230
3276
  /**
3231
3277
  * The reason rule, one standard for every layer of an art direction.
@@ -3731,4 +3777,4 @@ declare function characterReport(input: CharacterInputs, copyTells?: Set<string>
3731
3777
 
3732
3778
  declare function retrofitPlan(report: CharacterReport, direction?: ArtDirection | null): string;
3733
3779
 
3734
- export { ACCENT_SCALE, ACCENT_SHARED, ADDITIONS, AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type ActionPosition, type Addition, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Band, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHANGE_CLASSES, CHANGE_STEPS, CHOICE_KEYS, COMPONENT_ROLES, CONFIDENCE_LENSES, CONSIDERATION, CONTENT_FEATURES, CONVENTION, CUSTOMER_KINDS, type Calibration, type CandidateKind, type ChangeClass, type ChangeStep, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type CompetitorComparison, type ComponentPair, type ComponentTypicality, type ConfidenceBasis, type ConfidenceContext, type ConfidenceFinding, type ConfidenceLens, 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 DesignReview, type DirectionChoice, type DirectionComparison, type DirectionMatch, type DirectionProblem, type DirectionReport, type DirectionSource, type DirectionSummary, type DurationName, EASE, EASE_TUPLE, EDGE_STEP, ESTATE_VERSION, EVIDENCE_KINDS, EXIT_RATIO, type EaseName, type EmitCssOptions, type EstateMatch, type EstatePair, type EstateRegister, type EstateSite, type EvidenceKind, 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, INSPECTED_KINDS, 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_DEPTHS, PROOF_KINDS, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageConfidence, type PageCopy, type PageHierarchy, type PageResult, type PageType, type PaletteColour, type PhotoReviewOptions, type PhotoReviewResult, type ProofDepth, type ProofItem, type ProofKind, 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, REPORTED_KINDS, REVEAL_LIMIT, REVIEW_BASES, 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 ReviewBasis, type ReviewEvidence, type ReviewNote, type ReviewOutcome, type ReviewProposal, type ReviewReport, type ReviewResult, 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 SuggestionStanding, 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, VIEWPORTS, type ValidateContext, type Verdict, type Viewport, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, assessConfidence, assessReview, 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, isStructural, 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, validateConfidence, validateDirection, validateHierarchy, validateJob, viewportOf, visibleText, visualMeasures, vocabulary, vocabularyCounts, wcagContrast, wordListsJson };
3780
+ export { ACCENT_SCALE, ACCENT_SHARED, ADDITIONS, AI_PHRASES, AI_SOURCE_TYPES, AI_VIOLET, AI_WORDS, type AccentFork, type AccentForkOptions, type Action, type ActionPosition, type Addition, type Area, type ArtDirection, BASELINE_VERSION, BUZZWORDS, type Band, type Baseline, type BaselineEntry, CATALOGUE, CATALOGUE_VERSION, CHANGE_CLASSES, CHANGE_STEPS, CHOICE_KEYS, COMPONENT_ROLES, CONFIDENCE_LENSES, CONSIDERATION, CONTENT_FEATURES, CONVENTION, CUSTOMER_KINDS, type Calibration, type CandidateKind, type ChangeClass, type ChangeStep, type CharacterInputs, type CharacterReport, type CheckOptions, type CheckReport, type CheckRestraintInput, type ChoiceKey, type Claim, type ClaimsReport, type CompetitorComparison, type ComponentPair, type ComponentTypicality, type ConfidenceBasis, type ConfidenceContext, type ConfidenceFinding, type ConfidenceLens, 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 DesignReview, type DirectionChoice, type DirectionComparison, type DirectionMatch, type DirectionProblem, type DirectionReport, type DirectionSource, type DirectionSummary, type DurationName, EASE, EASE_TUPLE, EDGE_STEP, ESTATE_VERSION, EVIDENCE_KINDS, EXIT_RATIO, type EaseName, type EmitCssOptions, type EstateMatch, type EstatePair, type EstateRegister, type EstateSite, type EvidenceKind, 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, INSPECTED_KINDS, 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_DEPTHS, PROOF_KINDS, PROOF_NAMES, PROPOSED, PROVENANCE_SCAN_BYTES, type PageConfidence, type PageCopy, type PageHierarchy, type PageResult, type PageType, type PaletteColour, type PhotoReviewOptions, type PhotoReviewResult, type ProofDepth, type ProofItem, type ProofKind, 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, REPORTED_KINDS, REVEAL_LIMIT, REVIEW_BASES, REVIEW_PHRASES, REVIEW_VERDICTS, 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 ReviewBasis, type ReviewEvidence, type ReviewNote, type ReviewOutcome, type ReviewProposal, type ReviewReport, type ReviewResult, type ReviewVerdict, 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 SuggestionStanding, 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, VIEWPORTS, type ValidateContext, type Verdict, type Viewport, accentFork, addSite, apcaContrast, applyBaseline, applyHouseGate, applySeverity, assessConfidence, assessReview, 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, isStructural, 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, validateConfidence, validateDirection, validateHierarchy, validateJob, viewportOf, visibleText, visualMeasures, vocabulary, vocabularyCounts, wcagContrast, wordListsJson };
package/dist/index.js CHANGED
@@ -1711,7 +1711,17 @@ var REVIEW_PHRASES = [
1711
1711
  "by far the most",
1712
1712
  "hands down the",
1713
1713
  "arguably the best",
1714
- "bar none"
1714
+ "bar none",
1715
+ // "plain" as a self-description, the way "clear" is: say what the reader gets.
1716
+ "plain answer",
1717
+ "plain answers",
1718
+ "plain recommendation",
1719
+ "plain explanation",
1720
+ "plain explanations",
1721
+ "plain advice",
1722
+ "plain guidance",
1723
+ "plain-speaking",
1724
+ "plain speaking"
1715
1725
  ];
1716
1726
  var SALES_TAGLINES = [
1717
1727
  /\bwhere it (?:really |truly )?(?:counts|matters)(?: most)?\b/i,
@@ -1767,6 +1777,34 @@ var CLIPPED = [
1767
1777
  // "Alright Carlo, built you a proper site": the "I" left out
1768
1778
  /(?<=^|[\n>"“]|[.!?—–-]\s*|,\s+)(?:[Bb]uilt|[Mm]ade|[Pp]ut together|[Kk]nocked up)\s+(?:you|your\s+\w+|the\s+\w+)\s+an?\b/
1769
1779
  ];
1780
+ var PLAIN_FAMILY = [
1781
+ /\b(?:plain english|explained plainly|plainly explained)\b/i,
1782
+ /\bplainly\b(?!\s+marked\b)/i,
1783
+ /\bin\s+plain\s+(?:terms|language|words|speak)\b/i
1784
+ ];
1785
+ var CONFESSION = [
1786
+ // "What this is and is not:", "what we are and aren't"
1787
+ /\bwhat\s+(?:this|it|we|our\s+\w+)\s+(?:is|are)\s+and\s+(?:is\s+not|isn['’]t|are\s+not|aren['’]t)\b/i,
1788
+ // "What we are not", a label on its own; not "what we are not responsible for"
1789
+ /(?<!\band\s)\bwhat\s+we(?:\s+are|['’]re)\s+not\b(?!\s+(?:responsible|liable|able|allowed|sure)\b)/i,
1790
+ /\bwhat\s+we\s+(?:don['’]t|do\s+not)\s+do\b/i,
1791
+ // "We have not yet built and run", "we haven't yet worked with"
1792
+ /\bwe(?:\s+have|['’]ve)\s+not\s+yet\s+(?:built|run|done|worked|delivered|shipped|launched)\b/i,
1793
+ /\bwe\s+haven['’]t\s+(?:yet\s+)?(?:built|run|done|worked\s+with|delivered)\b/i,
1794
+ /\bwhat\s+we\s+have\s+and\s+(?:haven['’]t|have\s+not)\s+(?:done|built|run)\b/i,
1795
+ // "Our clinic experience so far is one practice"
1796
+ /\bour\s+(?:[\w-]+\s+){0,3}experience\s+so\s+far\s+(?:is|has\s+been|covers)\b/i,
1797
+ /\bso\s+far\s+(?:is|has\s+been)\s+(?:one|a\s+single|just\s+one)\b/i,
1798
+ /\bwhere\s+our\s+experience\s+(?:applies|ends|stops|runs\s+out)\b/i,
1799
+ /\bwhat\s+we\s+(?:would|['’]d|will|still)\s+need\s+to\s+learn\b/i,
1800
+ // "what we would and wouldn't take on", "what we won't take on"
1801
+ /\b(?:would|will)\s+and\s+(?:wouldn['’]t|would\s+not|won['’]t|will\s+not)\s+take\s+on\b/i,
1802
+ /\bwhat\s+we\s+(?:won['’]t|wouldn['’]t|would\s+not|will\s+not)\s+take\s+on\b/i,
1803
+ // "Who we don't work with", "who this is not for", "we're not for everyone"
1804
+ /\bwho\s+we\s+(?:don['’]t|do\s+not|won['’]t|will\s+not)\s+(?:work\s+with|serve|help|take\s+on)\b/i,
1805
+ /\bwho\s+(?:this|it|we)\s+(?:is|are|['’]re)\s+not\s+for\b|\bwho\s+(?:this|it)\s+isn['’]t\s+for\b/i,
1806
+ /\b(?:we|this)\s+(?:are|is|['’]re|['’]s)\s+not\s+(?:for\s+everyone|the\s+right\s+fit\s+for)\b/i
1807
+ ];
1770
1808
  var NEGATION = [
1771
1809
  // "It's not just a haircut, it's an experience."
1772
1810
  /\b(?:it['’]?s|it is|this is|this isn['’]t|we['’]re|we are|they['’]re)\s+(?:not|more than)\s+(?:just|only|merely|simply)?\s*[^.!?\n]{1,60}?[,;.—-]\s*(?:it['’]?s|it is|this is|we['’]re|we are|they['’]re)\b/i,
@@ -1896,12 +1934,25 @@ var COPY_TELLS = [
1896
1934
  generation: 2,
1897
1935
  severity: "warn",
1898
1936
  surface: "copy",
1899
- why: "Told to avoid jargon, a model announces that it is avoiding jargon. 'Explained plainly' swaps a synonym and keeps the tell.",
1900
- fix: "Use the house replacement, 'properly explained', or drop the claim and let the copy show it.",
1901
- detect: phrases([/\b(?:plain english|explained plainly|plainly explained)\b/i], quoted),
1937
+ why: "Told to avoid jargon, a model announces that it is avoiding jargon: 'in plain English', 'stated plainly', 'we'll tell you plainly', 'in plain terms'. The founder bans the whole family (20 October 2026); a reader takes it as filler.",
1938
+ fix: "Drop the adverb and say the thing: 'Our prices, stated plainly' becomes 'Our prices'. Where the claim matters, the house replacement for 'plain English' is 'properly explained'.",
1939
+ detect: phrases(PLAIN_FAMILY, quoted),
1902
1940
  fixtures: {
1903
- flag: [f("content/home.md", "Your options, in plain English.")],
1904
- pass: [f("content/home.md", "Your options, properly explained.")]
1941
+ flag: [
1942
+ f("content/home.md", "Your options, in plain English."),
1943
+ // Eunice Ordor's draft lines, Pastel 986559 #12 and #13 (her diagnosis taken, her wording not).
1944
+ f("content/clinics.md", "Tell us what's happening and we'll say plainly what we would take on."),
1945
+ f("content/pricing.md", "DD's payment terms, plainly stated."),
1946
+ f("content/clinics.md", "We explain our findings in plain terms."),
1947
+ f("content/clinics.md", "We share findings in plain language.")
1948
+ ],
1949
+ pass: [
1950
+ f("content/home.md", "Your options, properly explained."),
1951
+ f("content/pricing.md", "Our payment terms: 30% when you sign, the rest at launch."),
1952
+ // The Apache and zlib licence wording, copied into a repo as it is.
1953
+ f("LICENSE.txt", "Altered source versions must be plainly marked as such."),
1954
+ f("content/menu.md", "Plain or seeded bagels, and plain-language allergen notes.")
1955
+ ]
1905
1956
  }
1906
1957
  },
1907
1958
  {
@@ -1952,11 +2003,11 @@ var COPY_TELLS = [
1952
2003
  generation: 1,
1953
2004
  severity: "warn",
1954
2005
  surface: "copy",
1955
- why: "Truly, genuinely, clear, ultimately, 'the single most', 'bar none': normal English once, and a tell when they stack. The house rules keep them for review, never blocking.",
2006
+ why: "Truly, genuinely, clear, ultimately, 'a plain recommendation', 'the single most', 'bar none': normal English once, and a tell when they stack. The house rules keep them for review, never blocking.",
1956
2007
  fix: "Keep the one that lands hardest and cut the rest. 'What is genuinely excellent' reads better as 'what is excellent'.",
1957
2008
  detect: phrases([new RegExp(`(?:^|\\b)(?:${[...REVIEW_PHRASES].sort((a, b) => b.length - a.length).map((p) => escape(p).replace(/^(\w)/, "\\b$1").replace(/(\w)$/, "$1\\b")).join("|")})`, "i")], quoted),
1958
2009
  fixtures: {
1959
- flag: [f("content/home.md", "We are truly proud of it."), f("content/home.md", "You leave with a clear next step.")],
2010
+ flag: [f("content/home.md", "We are truly proud of it."), f("content/home.md", "You leave with a clear next step."), f("content/process.md", "You get a plain recommendation and someone who knows your systems.")],
1960
2011
  pass: [f("content/home.md", "We have fitted 400 boilers since 2009."), f("content/home.md", "Keep a safe clearance around the meter.")]
1961
2012
  }
1962
2013
  },
@@ -2037,6 +2088,40 @@ var COPY_TELLS = [
2037
2088
  ]
2038
2089
  }
2039
2090
  },
2091
+ {
2092
+ id: "confession-line",
2093
+ name: "Confession line",
2094
+ generation: 2,
2095
+ severity: "warn",
2096
+ surface: "copy",
2097
+ why: "'What this is and is not', 'we have not yet built', 'our experience so far is one practice', 'what we would and wouldn't take on': a model scopes a business by confessing its limits. It reads as small print, and it turns a buyer away before they make contact.",
2098
+ fix: "Scope by being precise about what is true, and leave the door open: 'Built and run by us for Sensphere, a practice whose clinicians work across several sites.' Keep contractual facts (eligibility, who runs what) and say them as what the buyer gets.",
2099
+ detect: phrases(CONFESSION, quoted),
2100
+ fixtures: {
2101
+ flag: [
2102
+ // Each one from the DD site, as Eunice Ordor flagged it (Pastel 986559 #8, #12, #13).
2103
+ f("content/clinics.md", "What this is and is not: a practice portal we built and run for one practice."),
2104
+ f("content/clinics.md", "We have not yet built and run the digital systems of a practice with several clinicians."),
2105
+ f("content/home.md", "Including the client portal we run for Sensphere. Our clinic experience so far is one practice."),
2106
+ f("content/clinics.md", "Tell us what's happening and we'll say what we would and wouldn't take on."),
2107
+ f("content/clinics.md", "We will say what carries over from this work and what we would need to learn first."),
2108
+ f("content/clinics.md", "We will tell you where our experience applies directly and where it is close."),
2109
+ f("content/about.md", "What we are not"),
2110
+ f("content/about.md", "Who we don't work with: anyone who needs a site this week."),
2111
+ f("content/mobile.md", "Mobile is our newest line, so we tell you what we have and haven't done.")
2112
+ ],
2113
+ pass: [
2114
+ f("content/clinics.md", "Built and run by us for Sensphere, a growing practice whose clinicians work remotely."),
2115
+ f("content/home.md", "The client portal we run for Sensphere."),
2116
+ // Contractual facts, said as they are.
2117
+ f("content/pricing.md", "Pay monthly is open to UK residents aged 18 and over, subject to status."),
2118
+ f("content/pricing.md", "Your monthly payments do not buy the site; the buy-out price does."),
2119
+ f("content/terms.md", "What we are responsible for, and what we are not responsible for, is in clause 4."),
2120
+ f("content/insurance.md", "Ask what your policy does and does not cover."),
2121
+ f("emails/deposit.md", "We have not yet received your deposit, so the date is still open.")
2122
+ ]
2123
+ }
2124
+ },
2040
2125
  {
2041
2126
  id: "hollow-imperative",
2042
2127
  name: "Hollow imperative",
@@ -3303,6 +3388,69 @@ function eyebrowChipHits(ctx) {
3303
3388
  }
3304
3389
  return hits;
3305
3390
  }
3391
+ var HERO_OPEN = /<(section|header|div|main|article|Section|Container)\b[^>]*?\b(?:class|className|id)\s*=\s*(?:"|'|\{\s*[`"']?)[^"'`>]*?(?<![a-z])hero(?![a-z])[^>]*>/gi;
3392
+ function closeOf(text, tag, from) {
3393
+ const re = new RegExp(`<(/?)${tag}\\b[^>]*?(/?)>`, "g");
3394
+ re.lastIndex = from;
3395
+ let depth = 0;
3396
+ for (let m = re.exec(text); m; m = re.exec(text)) {
3397
+ if (m[2] === "/") continue;
3398
+ depth += m[1] === "/" ? -1 : 1;
3399
+ if (depth === 0) return m.index + m[0].length;
3400
+ if (m.index - from > 8e3) break;
3401
+ }
3402
+ return Math.min(text.length, from + 4e3);
3403
+ }
3404
+ function heroSpans(path, text) {
3405
+ if (/(?:^|[\\/])[\w-]*hero[\w-]*\.(?:astro|tsx|jsx|vue|svelte)$/i.test(path)) return [[0, text.length]];
3406
+ const spans = [];
3407
+ for (const m of text.matchAll(HERO_OPEN)) {
3408
+ const at = m.index ?? 0;
3409
+ if (spans.some(([a, b]) => at >= a && at < b)) continue;
3410
+ spans.push([at, closeOf(text, m[1], at)]);
3411
+ }
3412
+ if (spans.length > 0) return spans;
3413
+ const h1 = text.search(/<h1\b/);
3414
+ if (h1 === -1) return [];
3415
+ const rest = text.slice(h1);
3416
+ const end = rest.search(/<\/(?:section|header)>|<section\b/);
3417
+ return [[h1, h1 + Math.min(end === -1 ? rest.length : end, 1500)]];
3418
+ }
3419
+ var CONTROL = /<(a|button|Button|ButtonLink|LinkButton|Link|Cta|CTA|CallToAction)\b([^>]*)>([\s\S]{0,300}?)<\/\1>/g;
3420
+ var BUTTON_COMPONENT = /^(?:Button|ButtonLink|LinkButton|Cta|CTA|CallToAction)$/;
3421
+ var BUTTON_CLASS = /(?<![a-z])(?:btn|button|cta)(?![a-z])/i;
3422
+ var TOGGLE = /\baria-(?:expanded|controls|haspopup)\b|\btype\s*=\s*["']submit["']/;
3423
+ var NOT_A_LANDING = /(?:^|[\\/])(?:error|global-error|not-found|404|500)\.\w+$|[\\/](?:admin|portal|dashboard|account|\(qa\)|\(app\)|qa)[\\/]/i;
3424
+ function buttonLabel(attrs, inner) {
3425
+ const text = inner.replace(/<[^>]*>/g, " ").replace(/\{[^}]*\}/g, " ").replace(/\s+/g, " ").trim();
3426
+ if (text && !/[{}()=]/.test(text)) return text.slice(0, 40);
3427
+ return attrs.match(/\bhref\s*=\s*["'{`$]*\{?([^"'}`\s]+)/)?.[1] ?? "a button";
3428
+ }
3429
+ function heroCtaPairHits(ctx) {
3430
+ const hits = [];
3431
+ for (const file of ctx.files) {
3432
+ if (file.kind !== "markup" || NOT_A_LANDING.test(file.path)) continue;
3433
+ const text = file.text.replace(/\\"/g, ' "');
3434
+ for (const [start, end] of heroSpans(file.path, text)) {
3435
+ const span = text.slice(start, end).replace(/<(nav|form)\b[\s\S]*?<\/\1>/gi, (s) => " ".repeat(s.length));
3436
+ const buttons = [];
3437
+ for (const m of span.matchAll(CONTROL)) {
3438
+ const [, tag, attrs, inner] = m;
3439
+ if (TOGGLE.test(attrs)) continue;
3440
+ const cls = attrs.match(/\b(?:class|className|variant)\s*=\s*(?:"([^"]*)"|'([^']*)'|\{([^}]*)\})/g)?.join(" ") ?? "";
3441
+ const tw = /\bpx-\d/.test(cls) && /\bpy-\d/.test(cls) && /\b(?:bg-|border\b|border-|rounded)/.test(cls);
3442
+ if (BUTTON_COMPONENT.test(tag) || BUTTON_CLASS.test(cls) || tw || /\brole\s*=\s*["']button["']/.test(attrs)) {
3443
+ buttons.push({ label: buttonLabel(attrs, inner), offset: start + (m.index ?? 0) });
3444
+ }
3445
+ }
3446
+ if (buttons.length >= 2) {
3447
+ const names = buttons.slice(0, 3).map((b) => `"${b.label}"`).join(", ");
3448
+ hits.push({ path: file.path, offset: buttons[1].offset, message: `${buttons.length} buttons side by side in the hero: ${names}` });
3449
+ }
3450
+ }
3451
+ }
3452
+ return hits;
3453
+ }
3306
3454
  function iconTiles(file) {
3307
3455
  return file.classUnits.filter((unit) => {
3308
3456
  const sized = has(unit, /^size-(?:8|9|10|11|12|14)$/) || (find(unit, /^h-(?:8|9|10|11|12|14)$/) ?? "x").slice(2) === (find(unit, /^w-(?:8|9|10|11|12|14)$/) ?? "y").slice(2);
@@ -3753,6 +3901,75 @@ const inter = Inter({ subsets: ["latin"] });`),
3753
3901
  <span className="inline-flex rounded-full border px-3 py-1 text-xs">Tag</span>`)]
3754
3902
  }
3755
3903
  },
3904
+ {
3905
+ id: "hero-cta-pair",
3906
+ name: "Two buttons in the hero",
3907
+ generation: 1,
3908
+ severity: "warn",
3909
+ surface: "source",
3910
+ why: "A filled button beside an outline or ghost one ('Get a free quote' / 'See our plans') is in 140 of 140 heroes Claude builds for a brief. Two equal asks make the visitor choose before they know anything, and say nobody decided what the page is for.",
3911
+ fix: "Keep one button, the ask the page exists for. Put the second route in the copy as a text link ('or see the work first'), or lower down where it answers a question.",
3912
+ detect: heroCtaPairHits,
3913
+ fixtures: {
3914
+ flag: [
3915
+ // Eunice Ordor's #5 and #36 (Pastel 986559): the DD site's own hero and process close.
3916
+ f3(
3917
+ "src/components/Hero.astro",
3918
+ `<section class="hero">
3919
+ <h1>We run the systems behind your website</h1>
3920
+ <a href="/contact" class="btn btn-primary">Get in touch</a>
3921
+ <a href="/work" class="btn btn-ghost">See our work</a>
3922
+ </section>`
3923
+ ),
3924
+ // The null build's hero, verbatim in shape (calibration/null/rmp/pages/05.html).
3925
+ f3(
3926
+ "index.html",
3927
+ `<nav><a class="btn" href="#contact">Quote</a></nav>
3928
+ <section id="hero" class="hero">
3929
+ <h1>Electricians in Uxbridge</h1>
3930
+ <a href="#contact" class="btn btn-primary">Book a Free Quote</a>
3931
+ <a href="tel:+441895555210" class="btn btn-outline">Call 01895 555 210</a>
3932
+ </section>`
3933
+ ),
3934
+ f3(
3935
+ "app/page.tsx",
3936
+ `<section className="relative py-24">
3937
+ <h1 className="text-5xl">Nails in Brackley</h1>
3938
+ <div className="flex gap-4">
3939
+ <Button asChild><a href="/book">Book now</a></Button>
3940
+ <Button variant="outline">View treatments</Button>
3941
+ </div>
3942
+ </section>`
3943
+ )
3944
+ ],
3945
+ pass: [
3946
+ // One button, and the second route as a text link in the sentence.
3947
+ f3(
3948
+ "src/components/Hero.astro",
3949
+ `<section class="hero">
3950
+ <h1>We run the systems behind your website</h1>
3951
+ <p>Tell us what's going on, or <a href="/work">see the work first</a>.</p>
3952
+ <a href="/contact" class="btn btn-primary">Get in touch</a>
3953
+ </section>`
3954
+ ),
3955
+ // A button in the nav and a search form are not the hero's ask.
3956
+ f3(
3957
+ "index.html",
3958
+ `<header class="hero">
3959
+ <nav><a class="btn" href="/book">Book</a></nav>
3960
+ <h1>Barbers in Manchester</h1>
3961
+ <a class="btn" href="/book">Book a cut</a>
3962
+ <form><button class="btn" type="submit">Search</button></form>
3963
+ </header>`
3964
+ ),
3965
+ f3("app/page.tsx", `<section className="py-24">
3966
+ <h1 className="text-5xl">Nails in Brackley</h1>
3967
+ <a className="rounded bg-ink px-5 py-3" href="/book">Book now</a>
3968
+ </section>
3969
+ <section><a className="rounded bg-ink px-5 py-3" href="/prices">Prices</a></section>`)
3970
+ ]
3971
+ }
3972
+ },
3756
3973
  {
3757
3974
  id: "icon-tile-stack",
3758
3975
  name: "Icon in a tinted tile",
@@ -4978,7 +5195,9 @@ var HOUSE = {
4978
5195
  "no-x-badge": { tier: "block", label: 'standalone "No X" badge. Reads as a slapped-on kicker, and worse when reused across pieces' },
4979
5196
  "ai-phrase": { tier: "block", label: "AI tell" },
4980
5197
  "plainer-word": { tier: "block", label: "a plainer word exists. Name the mechanism instead" },
4981
- "plain-english": { tier: "block", label: 'use "properly explained"' },
5198
+ // Widened 2026-10-20 (founder) to bare "plainly" and "in plain terms/language/words";
5199
+ // estate hits read first (DOM-1058), nearly all the stock "stated plainly".
5200
+ "plain-english": { tier: "block", label: 'the plain-English family. Drop the adverb and say the thing, or use "properly explained"' },
4982
5201
  buzzword: { tier: "block", label: "forbidden phrase" },
4983
5202
  "negative-reassurance": { tier: "block", label: "negative reassurance, say what they get" },
4984
5203
  // Evidence of a pasted chat reply, not a matter of style. Moved from review
@@ -4992,6 +5211,8 @@ var HOUSE = {
4992
5211
  "stock-email-line": { tier: "review", label: "stock email line, start with the reason for writing and end with the next step" },
4993
5212
  // New 2026-10-19 (founder): review first, like every new tell, until estate hits are read.
4994
5213
  "matey-register": { tier: "review", label: "matey register, write plain and complete British English" },
5214
+ // New 2026-10-20 (founder, from Eunice Ordor's review): review first, like every new tell.
5215
+ "confession-line": { tier: "review", label: "confession line, scope the work by saying precisely what is true" },
4995
5216
  "ai-vocabulary": { tier: "review", label: "AI vocabulary" },
4996
5217
  "stock-phrase": { tier: "review", label: "stock phrase" },
4997
5218
  "hollow-imperative": { tier: "review", label: "hollow imperative" },
@@ -5228,6 +5449,33 @@ var RENDERED_PATHS = {
5228
5449
  detect: (s) => s.effects.eyebrowChip ? [hit(s, "a small bordered pill sits above the headline")] : [],
5229
5450
  fixtures: { flag: [makeSnapshot({ effects: { eyebrowChip: true } })], pass: [makeSnapshot()] }
5230
5451
  },
5452
+ "hero-cta-pair": {
5453
+ // Version 2 geometry counts buttons and button-styled links per section. A
5454
+ // header painted inside the hero's box adds its own button, which the
5455
+ // source path can tell apart and this one cannot; a ghost button with no
5456
+ // fill or border is not counted, so this path under-reports.
5457
+ detect: (s) => {
5458
+ const first = s.sections[0];
5459
+ const controls = first?.geometry?.controls ?? 0;
5460
+ return first && (first.role ?? first.kind) === "hero" && controls >= 2 ? [hit(s, `${controls} buttons in the hero`, first.label)] : [];
5461
+ },
5462
+ fixtures: {
5463
+ flag: [
5464
+ {
5465
+ ...makeSnapshot(),
5466
+ sections: makeSnapshot().sections.map((x, i) => i === 0 ? { ...x, role: "hero", geometry: { centredShare: 0.2, mirrorSymmetry: 0.6, whitespaceRatio: 0.6, contentWidthRatio: 0.8, background: "rgb(255, 255, 255)", controls: 2 } } : x)
5467
+ }
5468
+ ],
5469
+ pass: [
5470
+ makeSnapshot(),
5471
+ // One button, with the second route as a text link: one control.
5472
+ {
5473
+ ...makeSnapshot(),
5474
+ sections: makeSnapshot().sections.map((x, i) => i === 0 ? { ...x, role: "hero", geometry: { centredShare: 0.2, mirrorSymmetry: 0.6, whitespaceRatio: 0.6, contentWidthRatio: 0.8, background: "rgb(255, 255, 255)", controls: 1 } } : x)
5475
+ }
5476
+ ]
5477
+ }
5478
+ },
5231
5479
  "icon-tile-grid": {
5232
5480
  detect: (s) => s.sections.filter((x) => x.cards >= 3 && x.iconCards >= 3).map((x) => hit(s, `${x.iconCards} icon cards in a row`, x.label)),
5233
5481
  fixtures: {
@@ -6197,7 +6445,7 @@ var IMAGERY_TELLS = [
6197
6445
  ];
6198
6446
 
6199
6447
  // src/character/check.ts
6200
- var CATALOGUE_VERSION = "2026.10.4";
6448
+ var CATALOGUE_VERSION = "2026.10.5";
6201
6449
  var CATALOGUE = [
6202
6450
  ...[...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map((t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t),
6203
6451
  ...SPECIFICITY_COPY_TELLS,
@@ -8376,6 +8624,7 @@ var INSPECTED_KINDS = ["screenshot", "rendered", "source", "measurement", "conte
8376
8624
  var REPORTED_KINDS = ["audit", "stakeholder", "customer"];
8377
8625
  var EVIDENCE_KINDS = [...INSPECTED_KINDS, ...REPORTED_KINDS];
8378
8626
  var CHANGE_CLASSES = ["confirmed-correction", "design-hypothesis", "optional-refinement", "no-change"];
8627
+ var REVIEW_VERDICTS = ["praise", "take", "take-reworded", "partly", "decline", "founder-decision", "known"];
8379
8628
  var CHANGE_STEPS = ["copy", "relocate", "group", "imagery", "trust-placement", "adjust-component", "remove-redundancy", "restructure", "new-component"];
8380
8629
  var ADDITIONS = ["carousel", "javascript", "dependency", "animation", "testimonial", "cta", "duplicate-content"];
8381
8630
 
@@ -10139,6 +10388,7 @@ export {
10139
10388
  REVEAL_LIMIT,
10140
10389
  REVIEW_BASES,
10141
10390
  REVIEW_PHRASES,
10391
+ REVIEW_VERDICTS,
10142
10392
  SAMPLES_PER_SEGMENT,
10143
10393
  SCALE,
10144
10394
  SECTION_ROLES,