aeorank 3.2.0 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -10
- package/dist/browser.js +90 -64
- package/dist/browser.js.map +1 -1
- package/dist/{chunk-RYV25AUV.js → chunk-DW7MPQ4X.js} +188 -30
- package/dist/chunk-DW7MPQ4X.js.map +1 -0
- package/dist/chunk-PYV5JVTC.js +179 -0
- package/dist/chunk-PYV5JVTC.js.map +1 -0
- package/dist/cli.js +83 -59
- package/dist/cli.js.map +1 -1
- package/dist/{full-site-crawler-TQ35TB2X.js → full-site-crawler-HAF2X2X3.js} +2 -2
- package/dist/{full-site-crawler-OBECS7AT.js → full-site-crawler-W3WSE6WT.js} +18 -30
- package/dist/full-site-crawler-W3WSE6WT.js.map +1 -0
- package/dist/index.cjs +277 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +90 -64
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-RYV25AUV.js.map +0 -1
- package/dist/full-site-crawler-OBECS7AT.js.map +0 -1
- /package/dist/{full-site-crawler-TQ35TB2X.js.map → full-site-crawler-HAF2X2X3.js.map} +0 -0
package/dist/browser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/parked-domain.ts","../src/duplicate-content.ts","../src/helpful-content.ts","../src/site-crawler.ts","../src/scoring.ts","../src/pillars.ts","../src/scorecard-builder.ts","../src/narrative-generator.ts","../src/multi-page-fetcher.ts","../src/page-scorer.ts","../src/page-analyzer.ts","../src/link-graph.ts","../src/fix-engine.ts","../src/headless-fetch.ts","../src/audit.ts","../src/compare.ts"],"sourcesContent":["/**\n * Parked domain detection.\n * Vendored from @aeo/queue/redirect-check (pure functions, no network).\n */\n\nexport interface ParkedDomainResult {\n isParked: boolean;\n reason?: string;\n}\n\n/** Known parking paths used by domain parking services */\nconst PARKING_PATHS = ['/lander', '/parking', '/park', '/sedoparking'];\n\n/** Known parking service domains found in page HTML (scripts, iframes, links) */\nconst PARKING_SERVICE_DOMAINS = [\n 'sedoparking.com',\n 'parkingcrew.net',\n 'bodis.com',\n 'dsparking.com',\n 'hugedomains.com',\n 'afternic.com',\n 'dan.com',\n 'undeveloped.com',\n 'domainmarket.com',\n 'sav.com',\n 'domaincontrol.com',\n 'above.com',\n 'domainlore.com',\n 'domainnamesales.com',\n 'brandbucket.com',\n 'squadhelp.com',\n 'godaddy.com/domainsearch',\n];\n\n/** Parking-specific text patterns (case-insensitive) */\nconst PARKING_TEXT_PATTERNS = [\n /\\bbuy this domain\\b/i,\n /\\bdomain is for sale\\b/i,\n /\\bthis domain may be for sale\\b/i,\n /\\bdomain for sale\\b/i,\n /\\bthis domain name is available\\b/i,\n /\\bparked by/i,\n /\\bthis page is parked/i,\n /\\bdomain has expired/i,\n /\\bthis domain has been registered/i,\n /\\bmake an offer on this domain\\b/i,\n /\\bget this domain\\b/i,\n /\\bacquire this domain\\b/i,\n];\n\nfunction detectParkingRedirect(bodySnippet: string): string | null {\n const relativeRedirect = bodySnippet.match(\n /window\\.location\\.(replace|assign|href)\\s*[=(]\\s*['\"](\\/[^'\"]*)['\"]/i,\n );\n if (!relativeRedirect) return null;\n const path = relativeRedirect[2].toLowerCase().replace(/[?#].*/, '');\n if (PARKING_PATHS.includes(path)) {\n return `js-redirect to ${relativeRedirect[2]}`;\n }\n return null;\n}\n\nfunction detectParkingService(bodySnippet: string): string | null {\n const lower = bodySnippet.toLowerCase();\n for (const service of PARKING_SERVICE_DOMAINS) {\n if (lower.includes(service)) {\n return `parking service: ${service}`;\n }\n }\n return null;\n}\n\nfunction detectParkingText(bodySnippet: string): string | null {\n for (const pattern of PARKING_TEXT_PATTERNS) {\n if (pattern.test(bodySnippet)) {\n return `parking text: ${bodySnippet.match(pattern)?.[0]}`;\n }\n }\n return null;\n}\n\n/**\n * Detect if a page is a parked/lost/for-sale domain.\n * Pure function - no network calls.\n */\nexport function detectParkedDomain(bodySnippet: string): ParkedDomainResult {\n const parkingRedirect = detectParkingRedirect(bodySnippet);\n if (parkingRedirect) return { isParked: true, reason: parkingRedirect };\n\n const parkingService = detectParkingService(bodySnippet);\n if (parkingService) return { isParked: true, reason: parkingService };\n\n const parkingText = detectParkingText(bodySnippet);\n if (parkingText) return { isParked: true, reason: parkingText };\n\n return { isParked: false };\n}\n","export interface DuplicateContentParagraph {\n text: string;\n shingles: Set<string>;\n}\n\nexport interface DuplicateContentSection {\n heading: string;\n paragraphs: DuplicateContentParagraph[];\n}\n\nconst BOILERPLATE_PATTERNS = /\\b(sign up|subscribe|get started|contact us|request a demo|free trial|book a call|schedule a|learn more|click here|follow us|share this|copyright|all rights reserved|privacy policy|terms of service)\\b/i;\n\n// Duplicate detection should focus on substantive copy, not short metadata rows.\n// 15 words is the shortest real body-paragraph fixture in the test suite, while\n// the Medicare false positives that triggered this work are 14 words or fewer.\nconst MIN_SUBSTANTIVE_WORDS = 15;\nconst MAX_METADATA_WORDS = 24;\nconst MAX_METADATA_LABEL_WORDS = 4;\n\nfunction normalizeParagraphText(htmlFragment: string): string {\n return htmlFragment\n .replace(/<[^>]*>/g, ' ')\n .replace(/&\\w+;/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .toLowerCase();\n}\n\nfunction tokenize(text: string): string[] {\n return text\n .split(/\\s+/)\n .map(word => word.replace(/^[^a-z0-9]+|[^a-z0-9]+$/gi, ''))\n .filter((word): word is string => word.length > 0);\n}\n\nfunction isBoilerplateParagraph(text: string, words: number): boolean {\n if (words < 20 && BOILERPLATE_PATTERNS.test(text)) return true;\n if (/\\b(cookie|gdpr|consent|opt.out)\\b/i.test(text) && words < 30) return true;\n return false;\n}\n\nfunction isMetadataParagraph(text: string, words: number): boolean {\n const labelMatch = text.match(/^([^:]{1,60}):\\s+/);\n if (!labelMatch) return false;\n\n const labelWords = tokenize(labelMatch[1]).length;\n return labelWords > 0 && labelWords <= MAX_METADATA_LABEL_WORDS && words <= MAX_METADATA_WORDS;\n}\n\nfunction buildShinglesFromTokens(words: string[], n: number = 4): Set<string> {\n const shingles = new Set<string>();\n for (let i = 0; i <= words.length - n; i++) {\n shingles.add(words.slice(i, i + n).join(' '));\n }\n return shingles;\n}\n\nfunction createParagraph(htmlFragment: string): DuplicateContentParagraph | null {\n const text = normalizeParagraphText(htmlFragment);\n const words = tokenize(text);\n if (words.length < MIN_SUBSTANTIVE_WORDS) return null;\n if (isBoilerplateParagraph(text, words.length)) return null;\n if (isMetadataParagraph(text, words.length)) return null;\n\n const shingles = buildShinglesFromTokens(words);\n if (shingles.size < 3) return null;\n\n return { text, shingles };\n}\n\nfunction stripNonContentHtml(html: string): string {\n return html\n .replace(/<(script|style|nav|header|footer|noscript)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi, '')\n .replace(/<aside\\b[^>]*>[\\s\\S]*?<\\/aside>/gi, '');\n}\n\nexport function extractDuplicateContentParagraphs(html: string): DuplicateContentParagraph[] {\n const cleaned = stripNonContentHtml(html);\n const matches = cleaned.match(/<p\\b[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n return matches\n .map(createParagraph)\n .filter((paragraph): paragraph is DuplicateContentParagraph => paragraph !== null);\n}\n\nexport function extractDuplicateContentSections(html: string): DuplicateContentSection[] {\n const cleaned = stripNonContentHtml(html);\n const parts = cleaned.split(/(?=<h[23]\\b[^>]*>)/i);\n const sections: DuplicateContentSection[] = [];\n\n for (const part of parts) {\n const headingMatch = part.match(/<h[23]\\b[^>]*>([\\s\\S]*?)<\\/h[23]>/i);\n const heading = headingMatch\n ? headingMatch[1].replace(/<[^>]*>/g, '').trim()\n : '(intro)';\n\n const paragraphs = (part.match(/<p\\b[^>]*>([\\s\\S]*?)<\\/p>/gi) || [])\n .map(createParagraph)\n .filter((paragraph): paragraph is DuplicateContentParagraph => paragraph !== null);\n\n if (paragraphs.length > 0) sections.push({ heading, paragraphs });\n }\n\n return sections;\n}\n\nexport function shingleJaccardSimilarity(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 && b.size === 0) return 0;\n\n let intersection = 0;\n for (const shingle of a) {\n if (b.has(shingle)) intersection++;\n }\n\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n","/**\n * Shared heuristics for helpful-content style criteria inspired by\n * Google's Who / How / Why / Experience framing.\n *\n * These are intentionally conservative:\n * - strong signals should be rewarded\n * - weak or inapplicable pages should often score neutral rather than fail\n * - no attempt is made to \"detect AI writing\"\n */\n\nfunction cap(value: number, max: number): number {\n return Math.min(max, value);\n}\n\nfunction floor(value: number, min: number): number {\n return Math.max(min, value);\n}\n\nfunction countMatches(text: string, pattern: RegExp): number {\n return text.match(pattern)?.length ?? 0;\n}\n\nfunction stripScriptsAndStyles(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ');\n}\n\nexport function getTextContent(html: string): string {\n return stripScriptsAndStyles(html)\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction getBodyHtml(html: string): string {\n const bodyMatch = html.match(/<body[^>]*>([\\s\\S]*?)<\\/body>/i);\n return bodyMatch ? bodyMatch[1] : html;\n}\n\nfunction getFirstParagraphText(html: string): string {\n const firstPara = getBodyHtml(html).match(/<p[^>]*>([\\s\\S]*?)<\\/p>/i);\n return firstPara ? firstPara[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction firstNWords(text: string, count: number): string {\n return text.split(/\\s+/).slice(0, count).join(' ');\n}\n\nfunction getHeadingTexts(html: string): string[] {\n return (html.match(/<h[1-3][^>]*>([\\s\\S]*?)<\\/h[1-3]>/gi) || [])\n .map(h => h.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim())\n .filter(Boolean);\n}\n\nfunction getH1Text(html: string): string {\n const match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n return match ? match[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTitleText(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction wordCount(text: string): number {\n return text ? text.split(/\\s+/).filter(Boolean).length : 0;\n}\n\nfunction isContentLikePage(html: string, url?: string): boolean {\n const text = getTextContent(html);\n const wc = wordCount(text);\n let signals = 0;\n\n if (url && /\\/(?:blog|article|articles|guide|guides|docs|learn|help|news|insights|resources|how-to|tutorial|case-stud|whitepaper|faq)\\b/i.test(url)) {\n signals += 2;\n }\n if (/<article[\\s>]/i.test(html)) signals += 1;\n if ((html.match(/<h[2-3][^>]*>/gi) || []).length >= 2) signals += 1;\n if (wc >= 500) signals += 1;\n if (/<time[\\s>]/i.test(html) || /datePublished|dateModified/i.test(html)) signals += 1;\n if (/written\\s+by|authored?\\s+by|reviewed\\s+by|medically\\s+reviewed/i.test(text)) signals += 1;\n\n return signals >= 2;\n}\n\nfunction expectsMethodology(html: string, url?: string): boolean {\n const text = getTextContent(html);\n const title = `${getTitleText(html)} ${getH1Text(html)}`.toLowerCase();\n const urlText = (url || '').toLowerCase();\n\n if (/(?:review|compare|comparison|vs\\.?|best|benchmark|study|analysis|survey|report|research|tested|test|methodology)/i.test(title)) {\n return true;\n }\n if (/(?:review|compare|comparison|benchmark|study|analysis|survey|research|report|best)/i.test(urlText)) {\n return true;\n }\n return /\\b(methodology|how we tested|how we reviewed|our testing|sample size|dataset|benchmark|editorial policy|review process)\\b/i.test(text);\n}\n\nfunction titleAndBodyAlign(html: string): boolean {\n const h1 = getH1Text(html);\n const title = getTitleText(html);\n const text = firstNWords(getTextContent(html), 250).toLowerCase();\n const topic = `${title} ${h1}`.toLowerCase();\n const keywords = topic\n .split(/[\\s|:()\\-/]+/)\n .filter(w => w.length >= 5 && !/^(about|guide|complete|ultimate|best|learn|understand|what|when|where|which|their|there|these|those)$/i.test(w));\n const uniqueKeywords = [...new Set(keywords)];\n if (uniqueKeywords.length === 0) return false;\n return uniqueKeywords.filter(w => text.includes(w)).length >= Math.min(2, uniqueKeywords.length);\n}\n\nconst GENERIC_OPENERS = /^(?:in today'?s|it is important to understand|in this (?:article|guide|post)|whether you are|have you ever|welcome to|if you'?re looking|in the modern|in the digital age)/i;\nconst PRACTICAL_LANGUAGE = /\\b(?:here'?s what to do|choose\\s+\\w+\\s+if|avoid\\s+\\w+\\s+when|the main risk is|the fastest option is|next step(?:s)?|best option|lowest risk|good fit if|not a good fit if|what to do next)\\b/gi;\nconst TRADEOFF_LANGUAGE = /\\b(?:however|but|trade-?off|caveat|limitation|downside|upside|risk|benefit|pros?\\b|cons?\\b|watch out|be careful|unless)\\b/gi;\nconst FLUFF_LANGUAGE = /\\b(?:comprehensive guide|ever-evolving|fast-paced world|unlock(?:ing)? the power|in conclusion|delve into|navigate the landscape|journey|seamless|robust solution)\\b/gi;\nconst EARLY_CTA_PATTERN = /\\b(?:sign up|get started|book (?:a )?demo|contact us|schedule (?:a )?call|buy now|start free|request a quote|talk to sales|subscribe)\\b/gi;\nconst FIRST_HAND_ACTIONS = /\\b(?:we|i)\\s+(?:tested|used|reviewed|implemented|measured|compared|observed|deployed|migrated|surveyed|analyzed|audited|interviewed)\\b/gi;\nconst EXPERIENCE_CONTEXT = /\\b(?:in our|during our|for our|in practice|in production|in the field|in our clinic|during implementation|over \\d+\\s+(?:days?|weeks?|months?)|across \\d+\\s+(?:accounts?|customers?|patients?|sites?|campaigns?)|with \\d+\\s+(?:participants?|users?|patients?|samples?))\\b/gi;\nconst EXPERIENCE_ARTIFACTS = /\\b(?:screenshot|photo|benchmark|timeline|before\\/after|our results|original chart|field note|walkthrough)\\b/gi;\nconst LIMITATION_LANGUAGE = /\\b(?:limitation|limitations|we found that|we learned|one caveat|did not work|failed|issue we hit|edge case|unexpectedly|drawback)\\b/gi;\nconst AUTHOR_LINK_PATTERN = /<a[^>]*href=[\"'][^\"']*\\/(?:author|authors|team|people|staff|reviewers?)\\b[^\"']*[\"'][^>]*>/i;\nconst BYLINE_PATTERN = /\\b(?:written|authored|reviewed|edited|medically reviewed)\\s+by\\b/i;\nconst AUTHOR_BIO_PATTERN = /\\b(?:about the author|author bio|reviewed by|medically reviewed by|board-certified|licensed|credentials?|specializes in|specialist in)\\b/i;\nconst METHODOLOGY_TERMS = /\\b(?:methodology|how we tested|how we reviewed|our methodology|testing process|review process|editorial policy|sample size|data collection|timeframe|criteria used|tools used|benchmark method|updated using|last reviewed|ai-assisted|ai assisted)\\b/gi;\nconst METHODOLOGY_DETAIL = /\\b(?:sample size|participants?|dataset|timeframe|criteria|weights?|tool(?:s)? used|measured over|tested over|reviewed on|last updated|last reviewed|scoring method)\\b/gi;\n\nexport function scoreHelpfulPurposeAlignment(html: string, url?: string): number {\n const text = getTextContent(html);\n if (!text) return 0;\n\n const contentLike = isContentLikePage(html, url);\n if (!contentLike && wordCount(text) < 250) return 5;\n\n let score = contentLike ? 3 : 5;\n const firstPara = getFirstParagraphText(html);\n const earlyText = firstNWords(text, 300);\n const bodyHtml = getBodyHtml(html);\n\n if (firstPara && !GENERIC_OPENERS.test(firstPara)) score += 2;\n if (countMatches(earlyText, PRACTICAL_LANGUAGE) >= 1) score += 2;\n else if (countMatches(text, PRACTICAL_LANGUAGE) >= 2) score += 1;\n\n const tradeoffCount = countMatches(text, TRADEOFF_LANGUAGE);\n if (tradeoffCount >= 2) score += 2;\n else if (tradeoffCount >= 1) score += 1;\n\n if (titleAndBodyAlign(html)) score += 1;\n if (/\\b(?:bottom line|key takeaway|here'?s the short answer|next steps?)\\b/i.test(text)) score += 1;\n\n if (firstPara && GENERIC_OPENERS.test(firstPara)) score -= 2;\n\n const earlyBodyHtml = bodyHtml.slice(0, 1800);\n const earlyCtas = countMatches(earlyBodyHtml, EARLY_CTA_PATTERN);\n if (earlyCtas >= 3) score -= 2;\n else if (earlyCtas >= 2) score -= 1;\n\n const fluffCount = countMatches(text, FLUFF_LANGUAGE);\n if (fluffCount >= 3) score -= 2;\n else if (fluffCount >= 1) score -= 1;\n\n return floor(cap(score, 10), 0);\n}\n\nexport function scoreFirstHandExperienceSignals(html: string, url?: string): number {\n const text = getTextContent(html);\n if (!text) return 0;\n\n const contentLike = isContentLikePage(html, url);\n let score = contentLike ? 2 : 5;\n\n const actionCount = countMatches(text, FIRST_HAND_ACTIONS);\n if (actionCount >= 3) score += 4;\n else if (actionCount >= 1) score += 2;\n\n const contextCount = countMatches(text, EXPERIENCE_CONTEXT);\n if (contextCount >= 2) score += 2;\n else if (contextCount >= 1) score += 1;\n\n const artifactCount = countMatches(text, EXPERIENCE_ARTIFACTS) + countMatches(html, /<figure|<figcaption/gi);\n if (artifactCount >= 3) score += 2;\n else if (artifactCount >= 1) score += 1;\n\n const limitationCount = countMatches(text, LIMITATION_LANGUAGE);\n if (limitationCount >= 2) score += 2;\n else if (limitationCount >= 1) score += 1;\n\n if (/\\b(?:manufacturer|vendor)\\s+(?:description|specification|copy)\\b/i.test(text)) score -= 1;\n\n return floor(cap(score, 10), 0);\n}\n\nexport function scoreCreatorTransparency(html: string, url?: string): number {\n const text = getTextContent(html);\n if (!text) return 0;\n\n const contentLike = isContentLikePage(html, url);\n if (!contentLike) return 5;\n\n let score = 0;\n const hasByline = BYLINE_PATTERN.test(text) || /class=[\"'][^\"']*author[^\"']*[\"']/i.test(html) || /rel=[\"']author[\"']/i.test(html);\n const hasPersonSchema = /\"@type\"\\s*:\\s*\"Person\"/i.test(html);\n\n if (hasByline) score += 3;\n if (AUTHOR_LINK_PATTERN.test(html)) score += 2;\n if (AUTHOR_BIO_PATTERN.test(text)) score += 2;\n if (/\\b(?:reviewed by|edited by|medically reviewed by)\\b/i.test(text)) score += 1;\n if (hasPersonSchema) score += 2;\n\n return floor(cap(score, 10), 0);\n}\n\nexport function scoreMethodologyTransparency(html: string, url?: string): number {\n const text = getTextContent(html);\n if (!text) return 0;\n\n const contentLike = isContentLikePage(html, url);\n const expected = expectsMethodology(html, url);\n\n let score = expected ? 2 : (contentLike ? 5 : 5);\n\n const methodologyCount = countMatches(text, METHODOLOGY_TERMS);\n if (methodologyCount >= 2) score += 3;\n else if (methodologyCount >= 1) score += 2;\n\n const detailCount = countMatches(text, METHODOLOGY_DETAIL);\n if (detailCount >= 3) score += 3;\n else if (detailCount >= 2) score += 2;\n else if (detailCount >= 1) score += 1;\n\n if (/\\b(?:tested|reviewed|analyzed)\\s+\\d+|\\bacross\\s+\\d+|\\bover\\s+\\d+\\s+(?:days?|weeks?|months?)|\\busing\\s+\\d+\\s+\\w+/i.test(text)) score += 1;\n if ((/<figure|<table/i.test(html)) && methodologyCount >= 1) score += 1;\n if (/\\b(?:ai-assisted|ai assisted|reviewed by an editor|human reviewed)\\b/i.test(text)) score += 1;\n\n return floor(cap(score, 10), 0);\n}\n","import type { AuditFinding, AuditStatus, Priority } from './types.js';\nimport { detectParkedDomain } from './parked-domain.js';\nimport {\n extractDuplicateContentParagraphs,\n extractDuplicateContentSections,\n shingleJaccardSimilarity,\n} from './duplicate-content.js';\nimport type { DuplicateContentParagraph } from './duplicate-content.js';\nimport {\n scoreHelpfulPurposeAlignment,\n scoreFirstHandExperienceSignals,\n scoreCreatorTransparency,\n scoreMethodologyTransparency,\n} from './helpful-content.js';\n\nexport interface CriterionResult {\n criterion: string;\n criterion_label: string;\n score: number;\n status: AuditStatus;\n findings: AuditFinding[];\n fix_priority: Priority;\n}\n\n// ─── Pre-fetched site data (one fetch per URL, reused across all criteria) ───\n\nexport type PageCategory = 'homepage' | 'blog' | 'about' | 'pricing' | 'services'\n | 'contact' | 'team' | 'resources' | 'docs' | 'cases' | 'faq' | 'content';\n\nexport interface FetchResult {\n text: string;\n status: number;\n finalUrl?: string;\n category?: PageCategory;\n}\n\nexport interface SiteData {\n domain: string;\n protocol: 'https' | 'http' | null;\n homepage: FetchResult | null;\n llmsTxt: FetchResult | null;\n robotsTxt: FetchResult | null;\n faqPage: FetchResult | null;\n sitemapXml: FetchResult | null;\n rssFeed: FetchResult | null;\n aiTxt: FetchResult | null;\n /** Set when homepage redirects to a different (non-brand) domain */\n redirectedTo: string | null;\n /** Set when homepage is a parked/for-sale/lost domain */\n parkedReason: string | null;\n /** Sampled blog/content pages from sitemap (up to 50) */\n blogSample?: FetchResult[];\n /** Full-crawl statistics (set when --full-crawl is used) */\n crawlStats?: {\n discovered: number;\n fetched: number;\n skipped: number;\n elapsed: number;\n };\n /** Link graph from full-site crawl */\n linkGraph?: import('./link-graph.js').LinkGraph;\n}\n\n// Raw data summary for AI narrative generation\nexport interface RawDataSummary {\n domain: string;\n protocol: 'https' | 'http' | null;\n homepage_length: number;\n homepage_text_length: number;\n has_https: boolean;\n llms_txt_status: number | null;\n llms_txt_length: number;\n robots_txt_status: number | null;\n robots_txt_snippet: string;\n robots_txt_ai_crawlers: string[];\n robots_txt_blocked_crawlers: string[];\n schema_types_found: string[];\n schema_block_count: number;\n faq_page_status: number | null;\n faq_page_length: number;\n sitemap_status: number | null;\n internal_link_count: number;\n external_link_count: number;\n question_headings_count: number;\n h1_count: number;\n has_meta_description: boolean;\n has_title: boolean;\n has_phone: boolean;\n has_address: boolean;\n has_org_schema: boolean;\n has_social_links: boolean;\n semantic_elements_found: string[];\n img_count: number;\n img_with_alt_count: number;\n has_lang_attr: boolean;\n has_aria: boolean;\n has_breadcrumbs: boolean;\n has_nav: boolean;\n has_footer: boolean;\n has_case_studies: boolean;\n has_statistics: boolean;\n has_expert_attribution: boolean;\n has_blog_section: boolean;\n // New criteria fields\n has_date_modified_schema: boolean;\n time_element_count: number;\n sitemap_url_count: number;\n has_rss_feed: boolean;\n table_count: number;\n ordered_list_count: number;\n unordered_list_count: number;\n definition_pattern_count: number;\n has_ai_txt: boolean;\n has_person_schema: boolean;\n fact_data_point_count: number;\n has_canonical: boolean;\n has_license_schema: boolean;\n sitemap_recent_lastmod_count: number;\n rendered_with_headless?: boolean;\n // Speakable schema fields\n has_speakable_schema: boolean;\n speakable_selector_count: number;\n // Blog sample fields\n blog_sample_count: number;\n blog_sample_urls: string[];\n blog_sample_schema_types: string[];\n blog_sample_question_headings: number;\n blog_sample_faq_schema_found: boolean;\n // Criteria 24-26 fields\n question_heading_answer_rate: number; // -1 = N/A, 0-100 = %\n question_heading_total: number;\n cannibalizing_pairs_count: number;\n page_titles_sampled: number;\n has_visible_date: boolean;\n has_schema_date_in_ld: boolean;\n date_modified_recency_days: number | null;\n // Full-crawl stats\n crawl_discovered: number;\n crawl_fetched: number;\n crawl_skipped: number;\n // V2 criteria fields\n citation_ready_sentences: number;\n answer_first_ratio: number; // 0-100, % of pages with answer block in first 300 words\n evidence_citations_avg: number; // avg inline citations per page\n entity_disambiguation_ratio: number; // 0-100, % of pages defining primary entity early\n extraction_friction_avg: number; // avg sentence length in words\n image_figure_ratio: number; // 0-100, % of images using figure/figcaption\n}\n\nasync function fetchText(url: string): Promise<FetchResult | null> {\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(15000),\n headers: { 'User-Agent': 'AEO-Visibility-Bot/1.0' },\n redirect: 'follow',\n });\n const text = await res.text();\n return { text: text.slice(0, 500000), status: res.status, finalUrl: res.url };\n } catch {\n return null;\n }\n}\n\n/** Extract bare domain from URL (no protocol, www, port, path) */\nfunction extractDomain(url: string): string {\n return url.replace(/^https?:\\/\\//, '').replace(/\\/.*/, '').replace(/:[0-9]+$/, '').replace(/^www\\./, '').toLowerCase();\n}\n\n/** Extract brand name from domain (everything before TLD) */\nfunction extractBrandName(domain: string): string {\n const parts = domain.split('.');\n const twoPartTlds = ['co.uk', 'com.au', 'co.jp', 'com.br', 'co.nz', 'co.in'];\n const lastTwo = parts.slice(-2).join('.');\n if (twoPartTlds.includes(lastTwo) && parts.length > 2) {\n return parts.slice(0, -2).join('.');\n }\n return parts.length > 1 ? parts.slice(0, -1).join('.') : domain;\n}\n\n/** Check if redirect target is a different brand (hijacked domain) */\nfunction detectCrossDomainRedirect(originalDomain: string, homepage: FetchResult): string | null {\n if (!homepage.finalUrl) return null;\n const finalDomain = extractDomain(homepage.finalUrl);\n const cleanOriginal = originalDomain.replace(/^www\\./, '').toLowerCase();\n if (\n finalDomain === cleanOriginal ||\n finalDomain === `www.${cleanOriginal}` ||\n extractBrandName(finalDomain) === extractBrandName(cleanOriginal)\n ) {\n return null;\n }\n return finalDomain;\n}\n\n/** Detect JS-based cross-domain redirect in HTML body */\nfunction detectJsRedirect(bodySnippet: string, originalDomain: string): string | null {\n const jsMatch = bodySnippet.match(\n /window\\.location\\.(replace|assign|href)\\s*[=(]\\s*['\"]https?:\\/\\/([^'\"]+)['\"]/i,\n );\n if (!jsMatch) return null;\n const jsDomain = extractDomain('https://' + jsMatch[2]);\n const cleanOriginal = originalDomain.replace(/^www\\./, '').toLowerCase();\n if (\n jsDomain === cleanOriginal ||\n jsDomain === `www.${cleanOriginal}` ||\n extractBrandName(jsDomain) === extractBrandName(cleanOriginal)\n ) {\n return null;\n }\n return jsDomain;\n}\n\n/** Detect HTML served for plain-text URLs (e.g. catch-all routes returning 200 for /ai.txt) */\nfunction isHtmlResponse(result: FetchResult | null): boolean {\n if (!result || result.status !== 200) return false;\n const trimmed = result.text.trimStart().slice(0, 200).toLowerCase();\n return trimmed.startsWith('<!doctype html') || trimmed.startsWith('<html') || /<head[\\s>]/i.test(trimmed);\n}\n\n/**\n * Fetches all site data in parallel with HTTPS/HTTP fallback.\n * Single entry point for all HTTP requests - no redundant fetches.\n */\nexport async function prefetchSiteData(domain: string): Promise<SiteData> {\n // Step 1: Detect protocol (HTTPS first, fallback to HTTP)\n let protocol: 'https' | 'http' | null = null;\n let homepage: FetchResult | null = null;\n\n homepage = await fetchText(`https://${domain}`);\n if (homepage && homepage.status >= 200 && homepage.status < 400) {\n protocol = 'https';\n } else {\n homepage = await fetchText(`http://${domain}`);\n if (homepage && homepage.status >= 200 && homepage.status < 400) {\n protocol = 'http';\n }\n }\n\n if (!protocol) {\n return { domain, protocol: null, homepage: null, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo: null, parkedReason: null, blogSample: [] };\n }\n\n // Check for cross-domain redirect (hijacked/expired domains)\n const httpRedirect = homepage ? detectCrossDomainRedirect(domain, homepage) : null;\n const jsRedirect = homepage ? detectJsRedirect(homepage.text.slice(0, 8192), domain) : null;\n const redirectedTo = httpRedirect || jsRedirect;\n\n if (redirectedTo) {\n return { domain, protocol, homepage, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo, parkedReason: null, blogSample: [] };\n }\n\n // Check for parked/lost/for-sale domains\n const parkedResult = homepage ? detectParkedDomain(homepage.text.slice(0, 8192)) : { isParked: false };\n if (parkedResult.isParked) {\n return { domain, protocol, homepage, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo: null, parkedReason: parkedResult.reason || 'parked', blogSample: [] };\n }\n\n const baseUrl = `${protocol}://${domain}`;\n\n // Step 2: Fetch all other resources in parallel\n const [llmsTxt, robotsTxt, faqPage, sitemapXml, aiTxt] = await Promise.all([\n fetchText(`${baseUrl}/llms.txt`),\n fetchText(`${baseUrl}/robots.txt`),\n fetchText(`${baseUrl}/faq`).then(async (result) => {\n if (result && result.status === 200) return result;\n // Fallback chain for FAQ page variants\n for (const path of ['/frequently-asked-questions', '/help', '/support', '/help-center']) {\n const fallback = await fetchText(`${baseUrl}${path}`);\n if (fallback && fallback.status === 200) return fallback;\n }\n return result;\n }),\n fetchText(`${baseUrl}/sitemap.xml`),\n fetchText(`${baseUrl}/ai.txt`),\n ]);\n\n // Step 3: Discover RSS feed URL from homepage, then fetch it\n let rssFeed: FetchResult | null = null;\n if (homepage) {\n const rssLinkMatch = homepage.text.match(/<link[^>]*type=\"application\\/(?:rss|atom)\\+xml\"[^>]*href=\"([^\"]*)\"[^>]*>/i);\n if (rssLinkMatch) {\n const rssUrl = rssLinkMatch[1].startsWith('http') ? rssLinkMatch[1] : `${baseUrl}${rssLinkMatch[1]}`;\n rssFeed = await fetchText(rssUrl);\n }\n if (!rssFeed || rssFeed.status !== 200) {\n // Fallback: try common RSS paths\n for (const path of ['/feed', '/rss.xml', '/feed.xml']) {\n rssFeed = await fetchText(`${baseUrl}${path}`);\n if (rssFeed && rssFeed.status === 200 && (rssFeed.text.includes('<rss') || rssFeed.text.includes('<feed') || rssFeed.text.includes('<channel'))) break;\n rssFeed = null;\n }\n }\n }\n\n // Step 4: If sitemapindex, fetch sub-sitemaps so downstream checks find <lastmod> and <url> entries\n if (sitemapXml && sitemapXml.status === 200 && sitemapXml.text.includes('<sitemapindex')) {\n const subUrls = extractAllSubSitemapUrls(sitemapXml.text, 5);\n if (subUrls.length > 0) {\n const subResults = await Promise.all(subUrls.map(u => fetchText(u)));\n for (const sub of subResults) {\n if (sub && sub.status === 200) {\n sitemapXml.text += '\\n' + sub.text;\n }\n }\n }\n }\n\n // Step 5: Sample blog pages from sitemap (up to 5)\n let blogSample: FetchResult[] = [];\n if (sitemapXml && sitemapXml.status === 200) {\n const sitemapForBlog = sitemapXml.text;\n\n const blogUrls = extractBlogUrlsFromSitemap(sitemapForBlog, domain, 50);\n if (blogUrls.length > 0) {\n const fetched = await Promise.all(blogUrls.map(url => fetchText(url)));\n blogSample = fetched.filter((r): r is FetchResult =>\n r !== null && r.status === 200 && r.text.length > 500\n );\n // Tag blog sample pages\n for (const page of blogSample) {\n page.category = 'blog';\n }\n }\n }\n\n // Tag homepage\n if (homepage) homepage.category = 'homepage';\n\n return { domain, protocol, homepage, llmsTxt, robotsTxt, faqPage, sitemapXml, rssFeed, aiTxt, redirectedTo: null, parkedReason: null, blogSample };\n}\n\n// ─── Blog sample helpers ─────────────────────────────────────────────────────\n\n/** Strip executable script/style tags to avoid inline JS polluting text analysis.\n * Preserves <script type=\"application/ld+json\"> (JSON-LD structured data). */\nfunction stripScripts(html: string): string {\n return html\n .replace(/<script(?![^>]*type\\s*=\\s*[\"']application\\/ld\\+json[\"'])[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '');\n}\n\n/** Concatenate homepage + blog sample HTML for combined analysis */\nfunction getCombinedHtml(data: SiteData): string {\n const parts = [stripScripts(data.homepage?.text || '')];\n if (data.blogSample) {\n for (const page of data.blogSample) {\n parts.push(stripScripts(page.text));\n }\n }\n return parts.join('\\n');\n}\n\n/** Get blog-only HTML concatenated */\nfunction getBlogHtml(data: SiteData): string {\n if (!data.blogSample || data.blogSample.length === 0) return '';\n return data.blogSample.map(p => stripScripts(p.text)).join('\\n');\n}\n\n// ─── Criterion checks (all use pre-fetched SiteData) ────────────────────────\n\n// Criterion 1: llms.txt\nfunction checkLlmsTxt(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const result = data.llmsTxt;\n\n if (!result || result.status !== 200 || isHtmlResponse(result)) {\n const statusNote = result ? (isHtmlResponse(result) ? 'HTML page served (not a valid text file)' : `HTTP ${result.status}`) : 'connection failed';\n findings.push({ severity: 'critical', detail: `No llms.txt file found at ${data.protocol}://${data.domain}/llms.txt (${statusNote})`, fix: 'Create a /llms.txt file that describes your site, services, and key pages in markdown format' });\n return { criterion: 'llms_txt', criterion_label: 'llms.txt File', score: 0, status: 'fail', findings, fix_priority: 'P0' };\n }\n\n const text = result.text;\n let score = 4; // exists\n\n if (text.length < 100) {\n findings.push({ severity: 'medium', detail: `llms.txt exists but is very short (${text.length} characters)`, fix: 'Add comprehensive description of your services, team, and key content' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: `llms.txt file found (${text.length} characters)` });\n }\n\n if (text.includes('#') || text.includes('##')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'llms.txt uses markdown headings for structure' });\n } else {\n findings.push({ severity: 'low', detail: 'llms.txt lacks markdown structure', fix: 'Add headings (# About, ## Services, etc.) for better LLM parsing' });\n }\n\n if (/https?:\\/\\//.test(text)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'llms.txt includes URLs to key pages' });\n } else {\n findings.push({ severity: 'medium', detail: 'llms.txt does not link to key pages', fix: 'Add URLs to your most important pages (services, about, FAQ)' });\n }\n\n return { criterion: 'llms_txt', criterion_label: 'llms.txt File', score: Math.min(10, score), status: score >= 7 ? 'pass' : 'partial', findings, fix_priority: score >= 7 ? 'P3' : 'P0' };\n}\n\n// Criterion 2: Schema Markup\nfunction checkSchemaMarkup(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage to check schema markup' });\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n const ldJsonMatches = html.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD structured data found on homepage', fix: 'Add Organization, LocalBusiness, or WebSite schema in a <script type=\"application/ld+json\"> tag' });\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n score += 3;\n findings.push({ severity: 'info', detail: `Found ${ldJsonMatches.length} JSON-LD block(s) on homepage` });\n\n const allSchemaText = ldJsonMatches.join(' ').toLowerCase();\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product'];\n const foundTypes: string[] = [];\n\n for (const type of schemaTypes) {\n if (allSchemaText.includes(`\"${type}\"`) || allSchemaText.includes(`\"@type\":\"${type}\"`)) {\n foundTypes.push(type);\n }\n }\n\n if (foundTypes.length > 0) {\n score += Math.min(4, foundTypes.length * 2);\n findings.push({ severity: 'info', detail: `Schema types found: ${foundTypes.join(', ')}` });\n }\n\n if (!foundTypes.includes('organization') && !foundTypes.includes('localbusiness')) {\n findings.push({ severity: 'high', detail: 'Missing Organization or LocalBusiness schema', fix: 'Add Organization schema with name, url, logo, contactPoint, and sameAs properties' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: 'Organization or LocalBusiness schema found' });\n }\n\n if (!foundTypes.includes('faqpage')) {\n findings.push({ severity: 'medium', detail: 'No FAQPage schema found', fix: 'Add FAQPage schema on pages with FAQ content' });\n } else {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQPage schema markup present' });\n }\n\n // Blog sample enhancement: additional schema types from blog posts\n if (data.blogSample && data.blogSample.length > 0) {\n const blogHtml = getBlogHtml(data);\n const blogLdJson = blogHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n if (blogLdJson.length > 0) {\n const blogSchemaText = blogLdJson.join(' ').toLowerCase();\n const blogTypes = schemaTypes.filter(t =>\n (blogSchemaText.includes(`\"${t}\"`) || blogSchemaText.includes(`\"@type\":\"${t}\"`)) && !foundTypes.includes(t)\n );\n if (blogTypes.length > 0) {\n score += Math.min(2, blogTypes.length);\n findings.push({ severity: 'info', detail: `Additional schema types found on blog pages: ${blogTypes.join(', ')}` });\n }\n // FAQPage on blog is especially valuable\n if (!foundTypes.includes('faqpage') && /faqpage/i.test(blogSchemaText)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQPage schema found on blog posts' });\n }\n }\n }\n\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 3: Q&A Content Format\nfunction checkQAFormat(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'qa_content_format', criterion_label: 'Q&A Content Format', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // Count question headings from homepage + blog sample combined\n const combinedHtml = getCombinedHtml(data);\n const hTagContent = (combinedHtml.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n const questionHeadings = hTagContent.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n\n if (questionHeadings.length >= 10) {\n score += 5;\n findings.push({ severity: 'info', detail: `Found ${questionHeadings.length} question-format headings` });\n } else if (questionHeadings.length >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `Found ${questionHeadings.length} question-format headings` });\n } else if (questionHeadings.length >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${questionHeadings.length} question-format heading(s) found`, fix: 'Structure more content as Q&A with question headings (H2/H3) followed by direct answers' });\n } else {\n findings.push({ severity: 'high', detail: 'No question-format headings found', fix: 'Add Q&A sections with headings like \"What is...?\", \"How does...?\" followed by concise answers' });\n }\n\n // Check for question heading followed by a paragraph (20-500 chars, allowing nested inline tags)\n const hasDirectAnswers = /<h[2-3][^>]*>[^<]*\\?<\\/h[2-3]>\\s*<p[^>]*>[\\s\\S]{20,500}<\\/p>/i.test(combinedHtml);\n if (hasDirectAnswers) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Content uses direct-answer format after question headings' });\n } else {\n findings.push({ severity: 'medium', detail: 'Content does not follow direct-answer format', fix: 'Start each answer paragraph with a concise 1-2 sentence definition before elaborating' });\n }\n\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n if (h1Count === 1) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Proper single H1 tag hierarchy' });\n } else if (h1Count === 0) {\n findings.push({ severity: 'high', detail: 'No H1 tag found', fix: 'Add exactly one H1 tag as the main page heading' });\n } else {\n score += 1;\n findings.push({ severity: 'medium', detail: `Multiple H1 tags found (${h1Count})`, fix: 'Use only one H1 per page; use H2/H3 for subsections' });\n }\n\n return { criterion: 'qa_content_format', criterion_label: 'Q&A Content Format', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 4: Clean HTML (includes HTTPS check - v4 requirement)\nfunction checkCleanHTML(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'clean_html', criterion_label: 'Clean, Crawlable HTML', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n const httpsAvailable = data.protocol === 'https';\n\n // HTTPS check (v4 requirement: no HTTPS = cap at 3)\n if (httpsAvailable) {\n findings.push({ severity: 'info', detail: 'Site serves over HTTPS' });\n } else {\n findings.push({ severity: 'critical', detail: 'Site does not support HTTPS', fix: 'Enable HTTPS with a valid SSL certificate. Sites without HTTPS are penalized by AI crawlers.' });\n }\n\n // Check for semantic elements\n const hasMain = /<main[\\s>]/i.test(html);\n const hasArticle = /<article[\\s>]/i.test(html);\n const hasSection = /<section[\\s>]/i.test(html);\n\n const semanticCount = [hasMain, hasArticle, hasSection].filter(Boolean).length;\n score += Math.min(3, semanticCount * 1);\n if (semanticCount >= 2) {\n findings.push({ severity: 'info', detail: `Uses semantic HTML5 elements: ${[hasMain && 'main', hasArticle && 'article', hasSection && 'section'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'medium', detail: 'Limited semantic HTML5 usage', fix: 'Wrap main content in <main>, use <article> for standalone content, <section> for grouped content' });\n }\n\n // Check H1 count\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n if (h1Count === 1) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Single H1 tag found - correct heading hierarchy' });\n } else {\n findings.push({ severity: h1Count === 0 ? 'high' : 'medium', detail: `${h1Count === 0 ? 'No' : 'Multiple'} H1 tag(s) found (${h1Count})`, fix: 'Use exactly one H1 per page' });\n }\n\n // Check for text content (not JS-only)\n const textContent = html.replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\n if (textContent.length > 500) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Page has substantial text content accessible without JavaScript' });\n } else {\n findings.push({ severity: 'high', detail: 'Very little text content visible in HTML source', fix: 'Ensure key content is server-rendered, not loaded via JavaScript only' });\n }\n\n // Check for meta tags\n const hasMetaDesc = /<meta[^>]*name=\"description\"[^>]*>/i.test(html);\n const hasTitle = /<title[^>]*>[^<]+<\\/title>/i.test(html);\n if (hasMetaDesc && hasTitle) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Page has title and meta description' });\n } else {\n findings.push({ severity: 'medium', detail: `Missing ${!hasTitle ? 'title tag' : ''}${!hasTitle && !hasMetaDesc ? ' and ' : ''}${!hasMetaDesc ? 'meta description' : ''}`, fix: 'Add <title> and <meta name=\"description\"> tags' });\n }\n\n // HTTPS cap: no HTTPS = max 3 for this criterion\n if (!httpsAvailable) {\n score = Math.min(score, 3);\n }\n\n return { criterion: 'clean_html', criterion_label: 'Clean, Crawlable HTML', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 5: Entity Consistency\nfunction checkEntityConsistency(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'entity_consistency', criterion_label: 'Entity Authority & E-E-A-T', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Check for phone numbers with context validation\n // A regex match only counts if it has supporting context (tel: link, schema, or nearby keywords)\n const hasTelLink = /href=\"tel:/i.test(html);\n const hasSchemaTelephone = /\"telephone\"/i.test(html);\n const phoneContextWords = /\\b(phone|call|tel:|contact\\s*us|fax|dial)\\b/i;\n\n const phoneRegex = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\n const phones = text.match(phoneRegex) || [];\n const contextValidatedPhones: string[] = [];\n\n if (hasTelLink || hasSchemaTelephone) {\n // Global context validates all matches\n contextValidatedPhones.push(...phones);\n } else {\n // Check each match for nearby context words (~100 chars around match)\n let match: RegExpExecArray | null;\n const phoneRegex2 = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\n while ((match = phoneRegex2.exec(text)) !== null) {\n const start = Math.max(0, match.index - 100);\n const end = Math.min(text.length, match.index + match[0].length + 100);\n const surrounding = text.slice(start, end);\n if (phoneContextWords.test(surrounding)) {\n contextValidatedPhones.push(match[0]);\n }\n }\n }\n\n const uniquePhones = [...new Set(contextValidatedPhones.map(p => p.replace(/\\D/g, '')))];\n if (uniquePhones.length === 1) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Single consistent phone number found' });\n } else if (uniquePhones.length > 1) {\n score += 1;\n findings.push({ severity: 'medium', detail: `Multiple phone numbers found (${uniquePhones.length})`, fix: 'Use one primary phone number consistently across all pages' });\n } else {\n findings.push({ severity: 'low', detail: 'No phone number found on homepage' });\n score += 1;\n }\n\n // Check for address\n const hasAddress = /\\d+\\s+\\w+\\s+(street|st|avenue|ave|road|rd|drive|dr|blvd|boulevard|lane|ln|way|court|ct)/i.test(text);\n if (hasAddress) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Physical address found on page' });\n }\n\n // Check for Organization schema\n const hasOrgSchema = /organization|localbusiness/i.test(html);\n if (hasOrgSchema) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Organization/LocalBusiness schema reinforces entity identity' });\n } else {\n findings.push({ severity: 'high', detail: 'No Organization schema to reinforce entity identity', fix: 'Add Organization JSON-LD with consistent name, address, phone, and social links' });\n }\n\n // Check for social proof\n const hasSameAs = /sameas|linkedin\\.com|facebook\\.com|twitter\\.com|x\\.com/i.test(html);\n if (hasSameAs) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Social media / sameAs references found' });\n } else {\n findings.push({ severity: 'low', detail: 'No social media links or sameAs found', fix: 'Add sameAs links in Organization schema to social profiles' });\n }\n\n return { criterion: 'entity_consistency', criterion_label: 'Entity Authority & E-E-A-T', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 6: robots.txt\nfunction checkRobotsTxt(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const result = data.robotsTxt;\n\n if (!result || result.status !== 200 || isHtmlResponse(result)) {\n findings.push({ severity: 'high', detail: 'No robots.txt file found', fix: 'Create a robots.txt that explicitly allows AI crawlers' });\n return { criterion: 'robots_txt', criterion_label: 'robots.txt for AI Crawlers', score: 2, status: 'fail', findings, fix_priority: 'P0' };\n }\n\n const text = result.text.toLowerCase();\n let score = 3; // exists\n\n const aiCrawlers = ['gptbot', 'claudebot', 'perplexitybot', 'anthropic', 'chatgpt'];\n const mentionedCrawlers = aiCrawlers.filter(c => text.includes(c));\n\n if (mentionedCrawlers.length > 0) {\n score += 3;\n findings.push({ severity: 'info', detail: `AI crawlers mentioned: ${mentionedCrawlers.join(', ')}` });\n\n const blocked = mentionedCrawlers.filter(c => {\n // Extract only this crawler's section (up to next User-agent: or EOF)\n // NOTE: do NOT use 'm' flag here - it makes $ match end-of-line, causing\n // non-greedy [\\s\\S]*? to stop at the first line instead of capturing the full section\n const sectionRegex = new RegExp(`user-agent:\\\\s*${c}[^\\\\S\\\\n]*\\\\n([\\\\s\\\\S]*?)(?=user-agent:|$)`, 'i');\n const match = sectionRegex.exec(result.text);\n if (!match) return false;\n const section = match[1];\n // If section has \"Allow: /\", the crawler is explicitly allowed (not blocked)\n if (/^allow:\\s*\\/\\s*$/im.test(section)) return false;\n // Check for \"Disallow: /\" (root-only, not /path) within this section\n return /^disallow:\\s*\\/\\s*$/im.test(section);\n });\n if (blocked.length > 0) {\n score -= 2;\n findings.push({ severity: 'critical', detail: `AI crawlers BLOCKED: ${blocked.join(', ')}`, fix: 'Change Disallow: / to Allow: / for AI crawler user-agents' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: 'AI crawlers are allowed to index the site' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No explicit AI crawler rules in robots.txt', fix: 'Add User-agent rules for GPTBot, ClaudeBot, PerplexityBot with Allow: /' });\n }\n\n if (text.includes('sitemap:')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Sitemap URL referenced in robots.txt' });\n } else {\n findings.push({ severity: 'low', detail: 'No sitemap reference in robots.txt', fix: 'Add Sitemap: https://yoursite.com/sitemap.xml to robots.txt' });\n }\n\n return { criterion: 'robots_txt', criterion_label: 'robots.txt for AI Crawlers', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P0' };\n}\n\n// Criterion 7: FAQ Section\nfunction checkFAQSection(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n const homepage = data.homepage;\n const hasHomepageFAQ = homepage && /faq|frequently\\s+asked/i.test(homepage.text);\n\n if (hasHomepageFAQ) {\n score += 2;\n findings.push({ severity: 'info', detail: 'FAQ content found on homepage' });\n } else {\n findings.push({ severity: 'low', detail: 'No FAQ content found on homepage', fix: 'Add an FAQ section to your homepage addressing common visitor questions' });\n }\n\n const faqPage = data.faqPage;\n const hasFaqPage = faqPage && faqPage.status === 200 && faqPage.text.length > 500;\n\n if (hasFaqPage) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Dedicated FAQ page exists' });\n\n if (/accordion|toggle|collaps|expand/i.test(faqPage.text)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQ uses accordion/toggle UI pattern' });\n }\n } else {\n findings.push({ severity: 'high', detail: 'No dedicated FAQ page found at /faq', fix: 'Create a comprehensive FAQ page at /faq covering common questions about your service' });\n }\n\n // Check for FAQPage schema (homepage + FAQ page + blog sample)\n const blogHtml = getBlogHtml(data);\n const allHtml = (homepage?.text || '') + (faqPage?.text || '') + blogHtml;\n if (/faqpage/i.test(allHtml) && /application\\/ld\\+json/i.test(allHtml)) {\n score += 3;\n const faqOnBlog = blogHtml && /faqpage/i.test(blogHtml) && /application\\/ld\\+json/i.test(blogHtml);\n findings.push({ severity: 'info', detail: faqOnBlog ? 'FAQPage schema markup found on blog posts' : 'FAQPage schema markup found' });\n } else {\n findings.push({ severity: 'medium', detail: 'No FAQPage schema markup', fix: 'Add FAQPage JSON-LD schema to pages with FAQ content' });\n }\n\n const questionCount = (allHtml.match(/<h[2-4][^>]*>[^<]*\\?<\\/h[2-4]>/gi) || []).length;\n if (questionCount >= 10) {\n score += 1;\n findings.push({ severity: 'info', detail: `${questionCount} question headings found across checked pages` });\n } else if (questionCount >= 5) {\n findings.push({ severity: 'low', detail: `Only ${questionCount} question headings found`, fix: 'Expand FAQ to cover at least 10-15 common questions' });\n }\n\n return { criterion: 'faq_section', criterion_label: 'Comprehensive FAQ Sections', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P1' };\n}\n\n// Criterion 8: Original Data\nfunction checkOriginalData(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'original_data', criterion_label: 'Original Data & Expert Content', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Analyze homepage + all blog sample pages for original data signals\n const allPages = [data.homepage, ...(data.blogSample || [])].filter(Boolean);\n const html = data.homepage.text;\n const allText = allPages.map(p => p!.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n const text = data.homepage.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Stats check: strong (+3) if research context nearby, weak (+1) for generic marketing stats\n const statPatterns = /\\d+%|\\d+\\s*(patients|clients|customers|cases|years|professionals|specialists|companies|users|businesses|domains|audits)/i;\n if (statPatterns.test(allText)) {\n const researchContext = /\\b(our\\s+(?:study|analysis|research|data|survey|findings|report)|we\\s+(?:surveyed|analyzed|studied|measured|tracked)|proprietary|methodology|original\\s+research)\\b/i;\n if (researchContext.test(allText)) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Proprietary statistics with research context found' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Statistics found but without research context (e.g., \"500+ clients\")', fix: 'Add context about your methodology: \"Our analysis of X found...\" or \"We surveyed Y...\"' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No proprietary data or statistics found', fix: 'Add unique statistics, case study results, or industry data that LLMs would cite as authoritative' });\n }\n\n // Case studies: with nearby metric (+3), without metric (+1)\n const caseStudyPattern = /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/i;\n if (caseStudyPattern.test(text)) {\n // Check for a numeric metric within ~200 chars of the case study mention\n const caseStudyRegex = /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/gi;\n let hasMetricNearby = false;\n let csMatch: RegExpExecArray | null;\n while ((csMatch = caseStudyRegex.exec(text)) !== null) {\n const start = Math.max(0, csMatch.index - 200);\n const end = Math.min(text.length, csMatch.index + csMatch[0].length + 200);\n const surrounding = text.slice(start, end);\n if (/\\d+%|\\$[\\d,]+|\\d+x\\b/i.test(surrounding)) {\n hasMetricNearby = true;\n break;\n }\n }\n if (hasMetricNearby) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Case studies or testimonials with specific metrics found' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Case studies or testimonials mentioned but without specific metrics', fix: 'Add measurable outcomes to case studies (e.g., \"increased traffic by 45%\")' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No case studies or testimonials found', fix: 'Add case studies with specific outcomes and metrics' });\n }\n\n // Expert attribution (+2) - check homepage + blog\n const combinedText = data.blogSample && data.blogSample.length > 0\n ? text + ' ' + data.blogSample.map(p => p.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ')\n : text;\n if (/written\\s+by|authored\\s+by|expert|specialist|board.certified|licensed/i.test(combinedText)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Expert attribution or credentials found' });\n } else {\n findings.push({ severity: 'low', detail: 'No expert attribution or credentials visible', fix: 'Add author bios with credentials to establish E-E-A-T signals' });\n }\n\n // Blog check: require actual href links to content paths, not just the word \"blog\"\n const contentLinkPattern = /href=\"[^\"]*\\/(?:blog|articles|insights|guides|resources)\\b[^\"]*\"/i;\n if (contentLinkPattern.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Links to blog/articles section found on site' });\n } else {\n findings.push({ severity: 'medium', detail: 'No links to blog or articles section found', fix: 'Create a content section with expert articles and link to it from your homepage' });\n }\n\n // Blog sample enhancement: case studies from blog posts\n if (data.blogSample && data.blogSample.length > 0 && !caseStudyPattern.test(text)) {\n const blogText = data.blogSample.map(p => p.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n if (caseStudyPattern.test(blogText)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Case studies or testimonials found on blog posts' });\n }\n }\n\n return { criterion: 'original_data', criterion_label: 'Original Data & Expert Content', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P2' };\n}\n\n// Criterion 9: Internal Linking\nfunction checkInternalLinking(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'internal_linking', criterion_label: 'Internal Linking Architecture', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n const linkMatches = html.match(/<a[^>]*href=\"([^\"]*)\"[^>]*>/gi) || [];\n const internalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('/') || href.includes(data.domain);\n });\n\n if (internalLinks.length >= 20) {\n score += 3;\n findings.push({ severity: 'info', detail: `${internalLinks.length} internal links found on homepage` });\n } else if (internalLinks.length >= 10) {\n score += 2;\n findings.push({ severity: 'low', detail: `${internalLinks.length} internal links on homepage`, fix: 'Add more internal links to key service/content pages' });\n } else {\n findings.push({ severity: 'high', detail: `Only ${internalLinks.length} internal links on homepage`, fix: 'Add prominent internal links to service pages, FAQ, blog, and about pages' });\n }\n\n if (/breadcrumb|aria-label=\"breadcrumb\"/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Breadcrumb navigation detected' });\n } else {\n findings.push({ severity: 'medium', detail: 'No breadcrumb navigation found', fix: 'Add breadcrumb navigation with BreadcrumbList schema markup' });\n }\n\n if (/<nav[\\s>]/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Semantic <nav> element used for navigation' });\n } else {\n findings.push({ severity: 'low', detail: 'No semantic <nav> element found', fix: 'Wrap navigation menus in <nav> for better AI and accessibility parsing' });\n }\n\n if (/related|see\\s+also|learn\\s+more|explore|you\\s+may\\s+also/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Related content or cross-linking sections found' });\n } else {\n findings.push({ severity: 'low', detail: 'No related content or cross-linking found', fix: 'Add \"Related Services\" or \"Learn More\" sections to build topic clusters' });\n }\n\n if (/<footer[\\s>]/i.test(html)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Footer element with likely navigation links' });\n } else {\n findings.push({ severity: 'low', detail: 'No <footer> element found', fix: 'Add a <footer> with navigation links, contact info, and site structure' });\n }\n\n return { criterion: 'internal_linking', criterion_label: 'Internal Linking Architecture', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P1' };\n}\n\n// Criterion 10: Semantic HTML\nfunction checkSemanticHTML(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'semantic_html', criterion_label: 'Semantic HTML5 & Accessibility', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for semantic element detection\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n let score = 0;\n\n const checks: [string, RegExp, string][] = [\n ['<main>', /<main[\\s>]/i, 'Wrap primary page content in <main>'],\n ['<article>', /<article[\\s>]/i, 'Use <article> for standalone content blocks'],\n ['<time>', /<time[\\s>]/i, 'Use <time> elements for dates'],\n ['<nav>', /<nav[\\s>]/i, 'Use <nav> for navigation sections'],\n ['<header>', /<header[\\s>]/i, 'Use <header> for page/section headers'],\n ['<footer>', /<footer[\\s>]/i, 'Use <footer> for page/section footers'],\n ];\n\n let found = 0;\n for (const [name, regex, fix] of checks) {\n if (regex.test(combinedHtml)) {\n found++;\n } else {\n findings.push({ severity: 'low', detail: `Missing ${name} element`, fix });\n }\n }\n score += Math.min(4, Math.floor(found * 0.7));\n if (found >= 4) findings.push({ severity: 'info', detail: `${found}/6 key semantic HTML5 elements found` });\n\n // Check img alt text\n const images = html.match(/<img[^>]*>/gi) || [];\n const imagesWithAlt = images.filter(img => /alt=\"[^\"]+\"/i.test(img));\n if (images.length > 0) {\n const ratio = imagesWithAlt.length / images.length;\n if (ratio >= 0.8) {\n score += 2;\n findings.push({ severity: 'info', detail: `${Math.round(ratio * 100)}% of images have alt text` });\n } else {\n findings.push({ severity: 'medium', detail: `Only ${Math.round(ratio * 100)}% of images have alt text`, fix: 'Add descriptive alt text to all images' });\n }\n }\n\n // Check lang attribute\n if (/lang=\"[a-z]{2}\"/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'HTML lang attribute set' });\n } else {\n findings.push({ severity: 'medium', detail: 'Missing lang attribute on <html> tag', fix: 'Add lang=\"en\" (or appropriate language) to the <html> tag' });\n }\n\n // Check for ARIA roles\n if (/role=\"|aria-/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'ARIA attributes found for accessibility' });\n } else {\n findings.push({ severity: 'low', detail: 'No ARIA roles or attributes found', fix: 'Add ARIA roles and labels to improve accessibility and semantic parsing' });\n }\n\n return { criterion: 'semantic_html', criterion_label: 'Semantic HTML5 & Accessibility', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 11: Content Freshness\nfunction checkContentFreshness(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'content_freshness', criterion_label: 'Content Freshness Signals', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // JSON-LD date signals (datePublished, dateModified)\n const hasDatePublished = /datePublished|dateCreated/i.test(html);\n const hasDateModified = /dateModified/i.test(html);\n if (hasDatePublished || hasDateModified) {\n score += 3;\n findings.push({ severity: 'info', detail: `JSON-LD date properties found: ${[hasDatePublished && 'datePublished', hasDateModified && 'dateModified'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'high', detail: 'No JSON-LD date properties (datePublished/dateModified) found', fix: 'Add datePublished and dateModified to Article or WebPage schema' });\n }\n\n // <time> elements\n const timeElements = html.match(/<time[\\s>]/gi) || [];\n if (timeElements.length >= 2) {\n score += 3;\n findings.push({ severity: 'info', detail: `${timeElements.length} <time> elements found` });\n } else if (timeElements.length === 1) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Only 1 <time> element found', fix: 'Use <time datetime=\"...\"> for all dates to help AI parsers' });\n } else {\n findings.push({ severity: 'medium', detail: 'No <time> elements found', fix: 'Wrap publication and modification dates in <time datetime=\"...\"> elements' });\n }\n\n // Article meta (article:published_time, article:modified_time)\n const hasArticleMeta = /article:published_time|article:modified_time/i.test(html);\n if (hasArticleMeta) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Open Graph article date meta tags found' });\n } else {\n findings.push({ severity: 'low', detail: 'No article:published_time or article:modified_time meta tags', fix: 'Add Open Graph article date meta tags' });\n }\n\n // Recency check - look for recent year references\n const currentYear = new Date().getFullYear();\n const hasRecentYear = html.includes(String(currentYear)) || html.includes(String(currentYear - 1));\n if (hasRecentYear) {\n score += 2;\n findings.push({ severity: 'info', detail: `References to ${currentYear} or ${currentYear - 1} found, suggesting recent content` });\n } else {\n findings.push({ severity: 'low', detail: 'No references to recent years found on homepage' });\n }\n\n return { criterion: 'content_freshness', criterion_label: 'Content Freshness Signals', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 12: Sitemap Completeness\nfunction checkSitemapCompleteness(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const sitemap = data.sitemapXml;\n\n if (!sitemap || sitemap.status !== 200) {\n findings.push({ severity: 'critical', detail: 'No sitemap.xml found', fix: 'Create a sitemap.xml with all indexable pages and submit to search engines' });\n return { criterion: 'sitemap_completeness', criterion_label: 'Sitemap Completeness', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const text = sitemap.text;\n let score = 2; // exists\n\n // Valid XML structure\n if (text.includes('<urlset') || text.includes('<sitemapindex')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Valid sitemap XML structure detected' });\n } else {\n findings.push({ severity: 'high', detail: 'sitemap.xml does not contain valid XML structure', fix: 'Ensure sitemap uses proper <urlset> or <sitemapindex> XML format' });\n }\n\n // Count URLs\n const urlCount = (text.match(/<loc>/gi) || []).length;\n if (urlCount >= 50) {\n score += 3;\n findings.push({ severity: 'info', detail: `${urlCount} URLs in sitemap` });\n } else if (urlCount >= 10) {\n score += 2;\n findings.push({ severity: 'info', detail: `${urlCount} URLs in sitemap` });\n } else if (urlCount > 0) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${urlCount} URL(s) in sitemap`, fix: 'Add all important pages to your sitemap' });\n }\n\n // Recent lastmod dates\n const lastmodMatches = text.match(/<lastmod>([^<]+)<\\/lastmod>/gi) || [];\n if (lastmodMatches.length > 0) {\n score += 2;\n findings.push({ severity: 'info', detail: `${lastmodMatches.length} URLs have lastmod dates` });\n } else {\n findings.push({ severity: 'medium', detail: 'No lastmod dates in sitemap', fix: 'Add <lastmod> dates to sitemap entries for freshness signals' });\n }\n\n // Sitemap index\n if (text.includes('<sitemapindex')) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Sitemap index found, indicating organized sitemap structure' });\n } else {\n findings.push({ severity: 'low', detail: 'No sitemap index structure', fix: 'Use a <sitemapindex> with multiple child sitemaps for larger sites to improve crawl efficiency' });\n }\n\n return { criterion: 'sitemap_completeness', criterion_label: 'Sitemap Completeness', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 13: RSS Feed\nfunction checkRssFeed(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n // Check for RSS link tag in homepage\n const hasRssLink = data.homepage && /<link[^>]*type=\"application\\/(?:rss|atom)\\+xml\"/i.test(data.homepage.text);\n if (hasRssLink) {\n score += 3;\n findings.push({ severity: 'info', detail: 'RSS/Atom feed link tag found in homepage <head>' });\n } else {\n findings.push({ severity: 'high', detail: 'No RSS/Atom feed link tag in homepage', fix: 'Add <link rel=\"alternate\" type=\"application/rss+xml\" href=\"/feed\"> to your <head>' });\n }\n\n // Check for valid feed content\n const feed = data.rssFeed;\n if (feed && feed.status === 200) {\n const feedText = feed.text;\n const isValidFeed = feedText.includes('<rss') || feedText.includes('<feed') || feedText.includes('<channel');\n if (isValidFeed) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Valid RSS/Atom feed content detected' });\n\n // Count items\n const itemCount = (feedText.match(/<item[\\s>]|<entry[\\s>]/gi) || []).length;\n if (itemCount >= 5) {\n score += 4;\n findings.push({ severity: 'info', detail: `Feed contains ${itemCount} items` });\n } else if (itemCount > 0) {\n score += 2;\n findings.push({ severity: 'low', detail: `Feed contains only ${itemCount} item(s)`, fix: 'Publish more content to populate your RSS feed with at least 5 items' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'Feed URL returned content but not valid RSS/Atom XML', fix: 'Ensure your feed outputs valid RSS 2.0 or Atom XML' });\n }\n } else if (!hasRssLink) {\n findings.push({ severity: 'medium', detail: 'No accessible RSS/Atom feed found', fix: 'Create an RSS feed to help AI engines discover and index new content automatically' });\n }\n\n return { criterion: 'rss_feed', criterion_label: 'RSS/Atom Feed', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 14: Table & List Extractability\nfunction checkTableListExtractability(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'table_list_extractability', criterion_label: 'Table & List Extractability', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML so blog tables/lists count\n const html = getCombinedHtml(data);\n let score = 0;\n\n // Tables with headers\n const tables = html.match(/<table[\\s>]/gi) || [];\n const tablesWithHeaders = (html.match(/<table[\\s\\S]*?<\\/table>/gi) || []).filter(t => /<th[\\s>]/i.test(t));\n if (tablesWithHeaders.length >= 1) {\n score += 3;\n findings.push({ severity: 'info', detail: `${tablesWithHeaders.length} table(s) with <th> headers found` });\n } else if (tables.length > 0) {\n score += 1;\n findings.push({ severity: 'medium', detail: `${tables.length} table(s) found but without <th> header cells`, fix: 'Add <th> header cells to tables for better AI extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'No HTML tables found', fix: 'Use comparison tables with <th> headers for structured data AI engines can extract' });\n }\n if (tablesWithHeaders.length >= 2) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Multiple well-structured tables present' });\n } else if (tablesWithHeaders.length === 1) {\n findings.push({ severity: 'low', detail: 'Only 1 table with headers found', fix: 'Add more comparison or data tables with <th> headers to increase extractable structured content' });\n }\n\n // Ordered lists\n const olCount = (html.match(/<ol[\\s>]/gi) || []).length;\n if (olCount >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${olCount} ordered list(s) found - good for step-by-step content` });\n } else {\n findings.push({ severity: 'low', detail: 'No ordered lists (<ol>) found', fix: 'Use <ol> for sequential content (steps, rankings, processes)' });\n }\n\n // Unordered lists\n const ulCount = (html.match(/<ul[\\s>]/gi) || []).length;\n if (ulCount >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${ulCount} unordered list(s) found` });\n } else {\n findings.push({ severity: 'low', detail: 'No unordered lists (<ul>) found', fix: 'Use <ul> for feature lists, benefits, and bullet-point content' });\n }\n\n // List items count\n const liCount = (html.match(/<li[\\s>]/gi) || []).length;\n if (liCount >= 10) {\n score += 1;\n findings.push({ severity: 'info', detail: `${liCount} list items - substantial extractable content` });\n }\n\n // Definition lists\n const dlCount = (html.match(/<dl[\\s>]/gi) || []).length;\n if (dlCount >= 1) {\n score += 1;\n findings.push({ severity: 'info', detail: `${dlCount} definition list(s) found` });\n } else {\n findings.push({ severity: 'low', detail: 'No definition lists (<dl>) found', fix: 'Use <dl>/<dt>/<dd> for term-definition pairs to improve AI extractability' });\n }\n\n // Bonus: meaningful comparison tables (3+ columns AND 3+ rows)\n for (const table of tablesWithHeaders) {\n const headerCells = (table.match(/<th[\\s>]/gi) || []).length;\n const dataCells = (table.match(/<td[\\s>]/gi) || []).length;\n if (headerCells >= 3 && dataCells >= headerCells * 2) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Meaningful comparison table found (3+ columns × 3+ rows)' });\n break;\n }\n }\n\n return { criterion: 'table_list_extractability', criterion_label: 'Table & List Extractability', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 15: Definition Patterns\nfunction checkDefinitionPatterns(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'definition_patterns', criterion_label: 'Definition Patterns', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for definition pattern detection\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const html = combinedHtml;\n let score = 0;\n\n // \"X is\" / \"X are\" / \"X refers to\" / \"defined as\" patterns\n const definitionPatterns = [\n /\\b\\w[\\w\\s]{2,30}\\bis\\s+(?:a|an|the)\\s/gi,\n /\\b\\w[\\w\\s]{2,30}\\bare\\s+(?:a|an|the)\\s/gi,\n /\\brefers?\\s+to\\b/gi,\n /\\bdefined\\s+as\\b/gi,\n /\\bknown\\s+as\\b/gi,\n /\\bmeans?\\s+that\\b/gi,\n ];\n\n let patternCount = 0;\n for (const pattern of definitionPatterns) {\n const matches = text.match(pattern) || [];\n patternCount += matches.length;\n }\n\n if (patternCount >= 3) {\n score += 5;\n findings.push({ severity: 'info', detail: `${patternCount} definition-style patterns found (e.g., \"X is a...\", \"refers to\", \"defined as\")` });\n } else if (patternCount >= 1) {\n score += 3;\n findings.push({ severity: 'low', detail: `Only ${patternCount} definition pattern(s) found`, fix: 'Start key descriptions with clear definition patterns like \"X is a...\" or \"X refers to...\"' });\n } else {\n findings.push({ severity: 'medium', detail: 'No definition patterns found', fix: 'Add clear definitions using patterns like \"[Term] is [definition]\" that AI engines can extract as snippets' });\n }\n\n // Early placement (in first 2000 chars of text content)\n const earlyText = text.slice(0, 2000);\n const earlyDefinitions = definitionPatterns.some(p => p.test(earlyText));\n // Reset lastIndex since we used global flags\n definitionPatterns.forEach(p => { p.lastIndex = 0; });\n if (earlyDefinitions) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Definition patterns appear early in page content - good for snippet extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'No definition patterns in the first 2000 characters of content', fix: 'Place key definitions early on the page where AI engines prioritize extraction' });\n }\n\n // <dfn> or <abbr> usage\n const hasDfn = /<dfn[\\s>]/i.test(html);\n const hasAbbr = /<abbr[\\s>]/i.test(html);\n if (hasDfn || hasAbbr) {\n score += 1;\n findings.push({ severity: 'info', detail: `Semantic definition elements found: ${[hasDfn && '<dfn>', hasAbbr && '<abbr>'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'low', detail: 'No <dfn> or <abbr> elements found', fix: 'Use <dfn> for term definitions and <abbr> for abbreviations to help AI parse terminology' });\n }\n\n // Glossary or definition-list patterns\n if (/<dl[\\s>]/i.test(html) || /glossary|definitions|terminology/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Glossary or definition list structure detected' });\n } else {\n findings.push({ severity: 'low', detail: 'No glossary or definition list found', fix: 'Add a glossary section using <dl>/<dt>/<dd> for key industry terms' });\n }\n\n return { criterion: 'definition_patterns', criterion_label: 'Definition Patterns', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 16: Direct Answer Density\nfunction checkDirectAnswerDensity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'direct_answer_density', criterion_label: 'Direct Answer Paragraphs', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n // Use combined HTML for Q&A pair and paragraph detection\n const html = getCombinedHtml(data);\n let score = 0;\n\n // Q&A pairs: question heading followed by paragraph\n const qaPairs = html.match(/<h[2-4][^>]*>[^<]*\\?<\\/h[2-4]>\\s*<p[^>]*>/gi) || [];\n if (qaPairs.length >= 3) {\n score += 6;\n findings.push({ severity: 'info', detail: `${qaPairs.length} question-answer pairs found (question heading + direct answer paragraph)` });\n } else if (qaPairs.length >= 1) {\n score += 3;\n findings.push({ severity: 'low', detail: `${qaPairs.length} question-answer pair(s) found`, fix: 'Add more question headings (H2/H3) immediately followed by concise answer paragraphs' });\n } else {\n findings.push({ severity: 'high', detail: 'No direct question-answer pairs found', fix: 'Structure content with question headings (e.g., \"What is X?\") immediately followed by a concise answer paragraph' });\n }\n\n // Snippet-zone paragraphs (40-150 words - ideal for AI extraction)\n const paragraphs = html.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n const snippetZoneParagraphs = paragraphs.filter(p => {\n const text = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = text.split(/\\s+/).length;\n return wordCount >= 40 && wordCount <= 150;\n });\n if (snippetZoneParagraphs.length >= 3) {\n score += 2;\n findings.push({ severity: 'info', detail: `${snippetZoneParagraphs.length} paragraphs in snippet zone (40-150 words) - ideal for AI extraction` });\n } else if (snippetZoneParagraphs.length >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${snippetZoneParagraphs.length} paragraph(s) in optimal snippet length`, fix: 'Write more paragraphs in the 40-150 word range for AI snippet extraction' });\n } else {\n findings.push({ severity: 'medium', detail: 'No paragraphs in the optimal snippet zone (40-150 words)', fix: 'Write self-contained paragraphs of 40-150 words that directly answer common questions' });\n }\n\n // Direct answer openers\n const text = data.homepage.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const directOpeners = /\\b(yes|no|the answer is|in short|simply put|to summarize)\\b/gi;\n const openerCount = (text.match(directOpeners) || []).length;\n if (openerCount >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Direct answer openers found (e.g., \"Yes,\", \"In short,\")' });\n } else {\n findings.push({ severity: 'low', detail: 'Few or no direct answer openers found', fix: 'Start answers with direct phrases like \"Yes,\", \"No,\", \"In short,\" to signal definitive answers to AI engines' });\n }\n\n return { criterion: 'direct_answer_density', criterion_label: 'Direct Answer Paragraphs', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 17: Content Licensing\nfunction checkContentLicensing(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n // ai.txt exists (ignore HTML catch-all responses)\n const aiTxt = data.aiTxt;\n if (aiTxt && aiTxt.status === 200 && aiTxt.text.length > 20 && !isHtmlResponse(aiTxt)) {\n score += 4;\n findings.push({ severity: 'info', detail: `ai.txt file found (${aiTxt.text.length} characters)` });\n } else {\n findings.push({ severity: 'high', detail: 'No ai.txt file found', fix: 'Create /ai.txt to declare your AI usage policy and content permissions for AI crawlers' });\n }\n\n const html = data.homepage?.text || '';\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n\n // Policy language on page\n const hasPolicyLanguage = /content\\s+policy|terms\\s+of\\s+use|usage\\s+rights|permission|copyright\\s+policy|licensing|creative\\s+commons/i.test(text);\n if (hasPolicyLanguage) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Content policy or licensing language found on page' });\n } else {\n findings.push({ severity: 'low', detail: 'No content policy or licensing language visible', fix: 'Add clear content usage terms or licensing information' });\n }\n\n // Schema license property\n const hasLicenseSchema = /license|copyrightHolder|copyrightYear/i.test(html) && /application\\/ld\\+json/i.test(html);\n if (hasLicenseSchema) {\n score += 2;\n findings.push({ severity: 'info', detail: 'License or copyright properties found in schema markup' });\n } else {\n findings.push({ severity: 'low', detail: 'No license or copyright properties in schema', fix: 'Add license, copyrightHolder, and copyrightYear to your schema markup' });\n }\n\n // TDM (Text and Data Mining) or Creative Commons\n const hasTdmOrCC = /tdm|text\\s+and\\s+data\\s+mining|creative\\s+commons|CC\\s+BY|creativecommons\\.org/i.test(html + (aiTxt?.text || ''));\n if (hasTdmOrCC) {\n score += 2;\n findings.push({ severity: 'info', detail: 'TDM or Creative Commons licensing references found' });\n } else {\n findings.push({ severity: 'low', detail: 'No TDM or Creative Commons licensing references found', fix: 'Add Text and Data Mining (TDM) permissions or Creative Commons licensing to signal AI-friendly content use' });\n }\n\n return { criterion: 'content_licensing', criterion_label: 'Content Licensing & AI Permissions', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 18: Author Schema Depth\nfunction checkAuthorSchemaDepth(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'author_schema_depth', criterion_label: 'Author & Expert Schema', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for author detection\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Person schema (check combined - blog posts often have author schema)\n const hasPersonSchema = /\"@type\"\\s*:\\s*\"Person\"/i.test(combinedHtml);\n if (hasPersonSchema) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Person schema found in JSON-LD' });\n } else {\n findings.push({ severity: 'medium', detail: 'No Person schema found', fix: 'Add Person schema for authors with name, jobTitle, knowsAbout, and sameAs properties' });\n }\n\n // jobTitle or knowsAbout properties\n const hasJobTitle = /jobTitle|knowsAbout|expertise|hasCredential/i.test(combinedHtml);\n if (hasJobTitle) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Author credential properties found (jobTitle/knowsAbout)' });\n } else {\n findings.push({ severity: 'low', detail: 'No jobTitle or knowsAbout in author schema', fix: 'Add jobTitle and knowsAbout to Person schema to establish expertise' });\n }\n\n // sameAs links (social profiles)\n const hasSameAs = /sameAs/i.test(combinedHtml) && hasPersonSchema;\n if (hasSameAs) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Author sameAs social profile links found' });\n } else {\n findings.push({ severity: 'low', detail: 'No sameAs links to author social profiles', fix: 'Add sameAs URLs (LinkedIn, GitHub) to Person schema to strengthen entity connections' });\n }\n\n // Visible byline\n const hasByline = /written\\s+by|authored?\\s+by|by\\s+[A-Z][a-z]+\\s+[A-Z]/i.test(text) ||\n /class=\"[^\"]*author[^\"]*\"/i.test(html) ||\n /rel=\"author\"/i.test(html);\n if (hasByline) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Visible author byline or attribution found' });\n } else {\n findings.push({ severity: 'medium', detail: 'No visible author byline found', fix: 'Add visible author names with credentials to establish E-E-A-T' });\n }\n\n // <address> element for contact\n if (/<address[\\s>]/i.test(combinedHtml)) {\n score += 1;\n findings.push({ severity: 'info', detail: '<address> element found for contact information' });\n } else {\n findings.push({ severity: 'low', detail: 'No <address> element found for contact information', fix: 'Add an <address> element with contact details to reinforce entity identity' });\n }\n\n return { criterion: 'author_schema_depth', criterion_label: 'Author & Expert Schema', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 19: Fact Density\nfunction checkFactDensity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'fact_density', criterion_label: 'Fact & Data Density', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Analyze across homepage + all blog sample pages for fact density\n const allPages = [data.homepage, ...(data.blogSample || [])].filter(Boolean);\n const allText = allPages.map(p => p!.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n const text = allText;\n const pageCount = allPages.length;\n let score = 0;\n\n // Numeric data points (percentages, money, counts)\n const dataPoints = text.match(/\\d+(?:\\.\\d+)?(?:\\s*%|\\s*\\$|\\s*USD|\\s*EUR)/g) || [];\n const countPhrases = text.match(/\\d+(?:,\\d{3})*\\+?\\s+(?:users?|clients?|customers?|companies|businesses|patients?|members?|employees?|projects?|downloads?)/gi) || [];\n const totalDataPoints = dataPoints.length + countPhrases.length;\n const avgPerPage = pageCount > 0 ? totalDataPoints / pageCount : 0;\n\n if (avgPerPage >= 4) {\n score += 5;\n findings.push({ severity: 'info', detail: `${totalDataPoints} quantitative data points found across ${pageCount} pages (avg ${avgPerPage.toFixed(1)}/page)` });\n } else if (avgPerPage >= 2) {\n score += 3;\n findings.push({ severity: 'info', detail: `${totalDataPoints} quantitative data points found across ${pageCount} pages` });\n } else if (totalDataPoints >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${totalDataPoints} quantitative data point(s) found across ${pageCount} pages`, fix: 'Add more specific numbers, percentages, and metrics to strengthen credibility' });\n } else {\n findings.push({ severity: 'high', detail: 'No quantitative data points found', fix: 'Add specific statistics (percentages, counts, comparisons) that AI engines can cite' });\n }\n\n // Year references (suggest sourced/dated claims)\n const yearRefs = text.match(/(?:19|20)\\d{2}/g) || [];\n const uniqueYears = [...new Set(yearRefs)];\n if (uniqueYears.length >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueYears.length} different year references found - suggests dated, verifiable claims` });\n } else if (uniqueYears.length === 1) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Only 1 year reference found on page', fix: 'Add more dated references and timestamps to demonstrate current, verifiable information' });\n } else {\n findings.push({ severity: 'low', detail: 'No year references found on page', fix: 'Include specific years and dates to provide verifiable, time-anchored facts' });\n }\n\n // Attribution phrases (source citations)\n const attributions = text.match(/according\\s+to|source:|study\\s+(?:by|from)|research\\s+(?:by|from|shows)|data\\s+from|report\\s+(?:by|from)|published\\s+(?:by|in)/gi) || [];\n if (attributions.length >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${attributions.length} source attribution(s) found (e.g., \"according to\", \"study by\")` });\n } else {\n findings.push({ severity: 'low', detail: 'No source attributions found', fix: 'Add citations like \"According to [source]\" or \"Research from [org] shows\" for credibility' });\n }\n\n // Units of measurement\n const units = text.match(/\\d+\\s*(?:hours?|minutes?|days?|weeks?|months?|years?|miles?|km|lbs?|kg|mg|sq\\s*ft|acres?|gallons?|liters?)/gi) || [];\n if (units.length >= 2) {\n score += 1;\n findings.push({ severity: 'info', detail: `${units.length} measurement units found (hours, miles, etc.) - adds factual precision` });\n } else {\n findings.push({ severity: 'low', detail: 'Few or no units of measurement found', fix: 'Include specific measurements (hours, miles, sq ft, etc.) to add factual precision AI engines can extract' });\n }\n\n return { criterion: 'fact_density', criterion_label: 'Fact & Data Density', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 20: Canonical URL\nfunction checkCanonicalUrl(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'canonical_url', criterion_label: 'Canonical URL Strategy', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // Canonical link present (handle either attribute order: rel before href OR href before rel)\n const canonicalMatch = html.match(/<link[^>]*rel=\"canonical\"[^>]*href=\"([^\"]*)\"[^>]*>/i)\n || html.match(/<link[^>]*href=\"([^\"]*)\"[^>]*rel=\"canonical\"[^>]*>/i);\n if (canonicalMatch) {\n score += 4;\n findings.push({ severity: 'info', detail: `Canonical URL found: ${canonicalMatch[1].slice(0, 80)}` });\n\n // Self-referencing (canonical points to the same domain)\n const canonicalUrl = canonicalMatch[1];\n if (canonicalUrl.includes(data.domain)) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Canonical URL is self-referencing (points to same domain)' });\n } else {\n findings.push({ severity: 'medium', detail: 'Canonical URL points to a different domain', fix: 'Ensure canonical URL points to the authoritative version of this page' });\n }\n\n // HTTPS canonical\n if (canonicalUrl.startsWith('https://')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Canonical URL uses HTTPS' });\n } else {\n findings.push({ severity: 'medium', detail: 'Canonical URL does not use HTTPS', fix: 'Update canonical URL to use https://' });\n }\n } else {\n findings.push({ severity: 'high', detail: 'No canonical URL tag found', fix: 'Add <link rel=\"canonical\" href=\"https://yoursite.com/page\"> to prevent duplicate content issues' });\n }\n\n // Check for duplicate canonical tags (match either attribute order)\n const allCanonicals = html.match(/<link[^>]*(?:rel=\"canonical\"|rel='canonical')[^>]*>/gi) || [];\n if (allCanonicals.length > 1) {\n score -= 1;\n findings.push({ severity: 'high', detail: `${allCanonicals.length} canonical tags found - must have exactly one`, fix: 'Remove duplicate canonical tags, keeping only one per page' });\n } else if (allCanonicals.length === 1) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Single canonical tag present (no duplicates)' });\n }\n\n return { criterion: 'canonical_url', criterion_label: 'Canonical URL Strategy', score: Math.max(0, Math.min(10, score)), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Helper: analyze sitemap lastmod dates for content velocity ──────────────\n\nexport interface SitemapDateAnalysis {\n recentCount: number;\n isUniform: boolean;\n uniformDetail?: string;\n totalWithDates: number;\n distinctRecentDays: number;\n}\n\nexport function countRecentSitemapDates(sitemapText: string): SitemapDateAnalysis {\n const lastmodMatches = sitemapText.match(/<lastmod>([^<]+)<\\/lastmod>/gi) || [];\n if (lastmodMatches.length === 0) {\n return { recentCount: 0, isUniform: false, totalWithDates: 0, distinctRecentDays: 0 };\n }\n\n const now = new Date();\n const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);\n\n // Parse all dates and group by day\n const dayCounts: Record<string, number> = {};\n let recentCount = 0;\n const recentDays = new Set<string>();\n\n for (const match of lastmodMatches) {\n const dateStr = match.replace(/<\\/?lastmod>/gi, '').trim();\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) continue;\n\n const dayKey = date.toISOString().split('T')[0];\n dayCounts[dayKey] = (dayCounts[dayKey] || 0) + 1;\n\n if (date >= ninetyDaysAgo) {\n recentCount++;\n recentDays.add(dayKey);\n }\n }\n\n // Detect uniform pattern: if most common day has >80% of all dated URLs\n const totalWithDates = Object.values(dayCounts).reduce((a, b) => a + b, 0);\n const maxDayCount = Math.max(...Object.values(dayCounts));\n const isUniform = totalWithDates >= 5 && maxDayCount / totalWithDates > 0.8;\n\n let uniformDetail: string | undefined;\n if (isUniform) {\n const topDay = Object.entries(dayCounts).find(([, count]) => count === maxDayCount)![0];\n uniformDetail = `${maxDayCount} of ${totalWithDates} URLs share lastmod date ${topDay} - likely auto-generated by build system`;\n }\n\n return {\n recentCount,\n isUniform,\n uniformDetail,\n totalWithDates,\n distinctRecentDays: recentDays.size,\n };\n}\n\n// ─── Blog URL extraction from sitemap ────────────────────────────────────────\n\nconst BLOG_PATH_PATTERNS = /\\/(?:[^/]*-?)?(?:blog|articles?|insights?|guides?|resources?|news|posts?|learn|help|how-?to|tutorials?|case-stud|whitepapers?)\\b/i;\n\nconst EXCLUDE_PATH_PATTERNS = /\\/(?:tag|category|author|page|feed|wp-content|wp-admin|wp-json|cart|checkout|login|search|api|static|assets|_next)\\b/i;\n\n/**\n * Extract blog/content URLs from a sitemap XML string.\n * Includes URLs matching common blog path patterns, plus deep paths (2+ segments).\n * Sorts by lastmod descending (newest first), returns top N.\n */\nexport function extractBlogUrlsFromSitemap(sitemapText: string, domain: string, limit: number = 50): string[] {\n const urlBlocks = sitemapText.match(/<url>([\\s\\S]*?)<\\/url>/gi) || [];\n const candidates: { url: string; lastmod: string }[] = [];\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n\n for (const block of urlBlocks) {\n const locMatch = block.match(/<loc>([^<]+)<\\/loc>/i);\n if (!locMatch) continue;\n const url = locMatch[1].trim();\n\n // Must be same domain\n try {\n const parsed = new URL(url);\n const urlDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (urlDomain !== cleanDomain) continue;\n\n // Skip root URL\n if (parsed.pathname === '/' || parsed.pathname === '') continue;\n\n const path = parsed.pathname.toLowerCase();\n\n // Exclude unwanted paths\n if (EXCLUDE_PATH_PATTERNS.test(path)) continue;\n\n // Include blog-like paths OR deep paths (2+ segments)\n const segments = path.split('/').filter(Boolean);\n const isBlogPath = BLOG_PATH_PATTERNS.test(path);\n const isDeepPath = segments.length >= 2;\n\n if (!isBlogPath && !isDeepPath) continue;\n } catch {\n continue;\n }\n\n const lastmodMatch = block.match(/<lastmod>([^<]+)<\\/lastmod>/i);\n const lastmod = lastmodMatch ? lastmodMatch[1].trim() : '';\n\n candidates.push({ url, lastmod });\n }\n\n // Sort by lastmod descending (newest first), URLs without lastmod go last\n candidates.sort((a, b) => {\n if (a.lastmod && b.lastmod) return b.lastmod.localeCompare(a.lastmod);\n if (a.lastmod) return -1;\n if (b.lastmod) return 1;\n return 0;\n });\n\n return candidates.slice(0, limit).map(c => c.url);\n}\n\n/**\n * Extract the best sub-sitemap URL from a sitemapindex.\n * WordPress often uses sitemapindex pointing to post-sitemap.xml, page-sitemap.xml, etc.\n * Returns null if not a sitemapindex.\n */\n/**\n * Extract ALL sub-sitemap URLs from a sitemapindex, up to a limit.\n * Used to fetch sub-sitemaps so downstream checks find <lastmod> and <url> entries.\n */\nexport function extractAllSubSitemapUrls(sitemapText: string, limit: number = 5): string[] {\n if (!sitemapText.includes('<sitemapindex')) return [];\n\n const sitemapLocs = sitemapText.match(/<sitemap>[\\s\\S]*?<loc>([^<]+)<\\/loc>[\\s\\S]*?<\\/sitemap>/gi) || [];\n const urls = sitemapLocs.map(block => {\n const match = block.match(/<loc>([^<]+)<\\/loc>/i);\n return match ? match[1].trim() : '';\n }).filter(Boolean);\n\n // Prefer post/blog/article sub-sitemaps first\n const preferred = urls.filter(u => /post|blog|article|page/i.test(u));\n const rest = urls.filter(u => !preferred.includes(u));\n return [...preferred, ...rest].slice(0, limit);\n}\n\nexport function extractSubSitemapUrl(sitemapText: string): string | null {\n if (!sitemapText.includes('<sitemapindex')) return null;\n\n const sitemapLocs = sitemapText.match(/<sitemap>[\\s\\S]*?<loc>([^<]+)<\\/loc>[\\s\\S]*?<\\/sitemap>/gi) || [];\n if (sitemapLocs.length === 0) return null;\n\n const urls = sitemapLocs.map(block => {\n const match = block.match(/<loc>([^<]+)<\\/loc>/i);\n return match ? match[1].trim() : '';\n }).filter(Boolean);\n\n // Prefer post/blog/article sub-sitemap\n const preferred = urls.find(u => /post|blog|article/i.test(u));\n return preferred || urls[0] || null;\n}\n\n// Criterion 21: Content Velocity\nfunction checkContentVelocity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const sitemap = data.sitemapXml;\n let score = 0;\n\n if (!sitemap || sitemap.status !== 200) {\n findings.push({ severity: 'medium', detail: 'No sitemap available to assess content velocity', fix: 'Create a sitemap.xml with lastmod dates to signal content publishing frequency' });\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n const analysis = countRecentSitemapDates(sitemap.text);\n\n if (analysis.totalWithDates === 0) {\n findings.push({ severity: 'medium', detail: 'No lastmod dates in sitemap', fix: 'Add lastmod dates to sitemap entries to signal content freshness' });\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: 2, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n score += 2;\n findings.push({ severity: 'info', detail: `${analysis.totalWithDates} pages have lastmod dates` });\n\n // Use distinct recent days when lastmod dates are uniform (build-system artifact)\n const effectiveCount = analysis.isUniform ? analysis.distinctRecentDays : analysis.recentCount;\n\n if (analysis.isUniform) {\n findings.push({ severity: 'medium', detail: analysis.uniformDetail!, fix: 'Set genuine lastmod dates per page reflecting actual content changes, not build timestamps' });\n }\n\n if (effectiveCount >= 20) {\n score += 8;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days - excellent content velocity` });\n } else if (effectiveCount >= 10) {\n score += 5;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days - good velocity` });\n } else if (effectiveCount >= 5) {\n score += 3;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days` });\n } else if (effectiveCount >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${effectiveCount} ${analysis.isUniform ? 'distinct date(s)' : 'page(s) updated'} in last 90 days`, fix: 'Publish or update content more frequently to signal active maintenance' });\n } else {\n findings.push({ severity: 'medium', detail: 'No pages updated in the last 90 days', fix: 'Update existing content and publish new pages regularly' });\n }\n\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 22: Schema Coverage (depth across types)\nfunction checkSchemaCoverage(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for schema coverage (blog posts add Article, Person, FAQPage etc.)\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n const ldJsonMatches = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD found - cannot assess schema coverage', fix: 'Add JSON-LD schema markup to improve AI engine understanding' });\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const allSchemaText = ldJsonMatches.join(' ');\n const allSchemaLower = allSchemaText.toLowerCase();\n\n // Count distinct schema properties\n const propertyMatches = allSchemaText.match(/\"[a-zA-Z@]+\"\\s*:/g) || [];\n const uniqueProperties = new Set(propertyMatches.map(p => p.replace(/[\":\\s]/g, '').toLowerCase()));\n\n if (uniqueProperties.size >= 15) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueProperties.size} unique schema properties used - rich schema depth` });\n } else if (uniqueProperties.size >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueProperties.size} unique schema properties found` });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${uniqueProperties.size} schema properties`, fix: 'Add more properties to your schema types for richer AI understanding' });\n }\n\n // Organization depth (name, url, logo, contactPoint, sameAs, address, etc.)\n const orgProps = ['name', 'url', 'logo', 'contactpoint', 'sameas', 'address', 'telephone', 'description', 'founder', 'foundingdate'];\n const orgPropsFound = orgProps.filter(p => allSchemaLower.includes(`\"${p}\"`));\n if (orgPropsFound.length >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Organization schema has ${orgPropsFound.length}/10 key properties` });\n } else if (orgPropsFound.length >= 3) {\n score += 1;\n findings.push({ severity: 'low', detail: `Organization schema has only ${orgPropsFound.length}/10 key properties`, fix: 'Add more Organization properties: logo, contactPoint, sameAs, address' });\n } else {\n findings.push({ severity: 'medium', detail: `Organization schema has only ${orgPropsFound.length} key properties`, fix: 'Add essential Organization properties: name, url, logo, contactPoint, sameAs, address, telephone' });\n }\n\n // Article schema depth\n const articleProps = ['headline', 'datepublished', 'datemodified', 'author', 'image', 'description', 'publisher'];\n const articlePropsFound = articleProps.filter(p => allSchemaLower.includes(`\"${p}\"`));\n if (articlePropsFound.length >= 4) {\n score += 2;\n findings.push({ severity: 'info', detail: `Article schema has ${articlePropsFound.length}/7 key properties` });\n } else if (articlePropsFound.length >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Article schema has only ${articlePropsFound.length}/7 key properties`, fix: 'Add headline, datePublished, dateModified, author, image, and publisher to Article schema' });\n } else {\n findings.push({ severity: 'medium', detail: 'Article schema missing or has fewer than 2 key properties', fix: 'Add Article schema with headline, datePublished, author, and publisher properties' });\n }\n\n // @id linking (connected schema graph)\n const hasIdLinking = /\"@id\"\\s*:/i.test(allSchemaText);\n if (hasIdLinking) {\n score += 2;\n findings.push({ severity: 'info', detail: '@id linking found - schema types are connected in a graph' });\n } else {\n findings.push({ severity: 'low', detail: 'No @id linking between schema types', fix: 'Use @id references to connect schema types (e.g., article.publisher -> organization)' });\n }\n\n // Number of distinct types\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product', 'person', 'event', 'offer', 'review', 'aboutpage'];\n const foundTypes = schemaTypes.filter(t => allSchemaLower.includes(`\"${t}\"`));\n if (foundTypes.length >= 3) {\n score += 2;\n findings.push({ severity: 'info', detail: `${foundTypes.length} distinct schema types used: ${foundTypes.join(', ')}` });\n } else if (foundTypes.length >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${foundTypes.length} distinct schema types used`, fix: 'Add more schema types (FAQPage, BreadcrumbList, Service) for comprehensive AI understanding' });\n } else {\n findings.push({ severity: 'medium', detail: `Only ${foundTypes.length} schema type(s) found - limited coverage`, fix: 'Add multiple schema types (Organization, WebSite, FAQPage, BreadcrumbList) for comprehensive AI understanding' });\n }\n\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 23: Speakable Schema (voice assistant readiness)\nfunction checkSpeakableSchema(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const ldJsonMatches = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD found - cannot assess speakable schema', fix: 'Add JSON-LD schema markup with SpeakableSpecification to indicate voice-readable content sections' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n const allSchemaText = ldJsonMatches.join(' ');\n const allSchemaLower = allSchemaText.toLowerCase();\n\n // Detect SpeakableSpecification type or \"speakable\" property\n const hasSpeakableType = /speakablespecification/i.test(allSchemaLower);\n const hasSpeakableProperty = /\"speakable\"\\s*:/i.test(allSchemaText);\n const hasSpeakable = hasSpeakableType || hasSpeakableProperty;\n\n if (!hasSpeakable) {\n findings.push({ severity: 'medium', detail: 'No SpeakableSpecification schema found - voice assistants cannot identify readable sections', fix: 'Add SpeakableSpecification schema with cssSelector or xpath targeting key content sections (headlines, summaries, FAQ answers)' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n // Base: speakable detected\n score += 4;\n findings.push({ severity: 'info', detail: 'SpeakableSpecification schema detected - voice assistants can identify readable content' });\n\n // Check for cssSelector or xpath targeting\n const hasCssSelector = /\"cssselector\"/i.test(allSchemaLower);\n const hasXpath = /\"xpath\"/i.test(allSchemaLower);\n if (hasCssSelector || hasXpath) {\n score += 3;\n const targetType = hasCssSelector && hasXpath ? 'cssSelector and xpath' : hasCssSelector ? 'cssSelector' : 'xpath';\n findings.push({ severity: 'info', detail: `Speakable uses ${targetType} targeting for precise content selection` });\n } else {\n findings.push({ severity: 'low', detail: 'Speakable schema lacks cssSelector or xpath targeting', fix: 'Add cssSelector (e.g., \".article-headline, .article-summary\") or xpath to precisely target speakable sections' });\n }\n\n // Check blog sample coverage\n if (data.blogSample && data.blogSample.length > 0) {\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const blogHasSpeakable = /speakablespecification/i.test(blogHtml) || /\"speakable\"\\s*:/i.test(blogHtml);\n if (blogHasSpeakable) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Speakable schema also found in blog/content pages - comprehensive voice coverage' });\n } else {\n findings.push({ severity: 'low', detail: 'Speakable schema only on homepage, not found in blog/content pages', fix: 'Add SpeakableSpecification to article pages to make blog content voice-assistant readable' });\n }\n } else {\n // No blog samples - can't check, give partial credit\n findings.push({ severity: 'info', detail: 'No blog pages sampled - blog speakable coverage not assessed' });\n }\n\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 24: Query-Answer Alignment ────────────────────────────────────\n\n/** Extract question-format headings from HTML */\nfunction extractQuestionHeadings(html: string): string[] {\n const hTags = (html.match(/<h[23][^>]*>([\\s\\S]*?)<\\/h[23]>/gi) || [])\n .map(h => h.replace(/<[^>]*>/g, '').trim());\n return hTags.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n}\n\nfunction checkQueryAnswerAlignment(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for query-answer alignment analysis' });\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const questionHeadings = extractQuestionHeadings(combinedHtml);\n\n if (questionHeadings.length === 0) {\n findings.push({ severity: 'info', detail: 'No question-format headings (H2/H3) found - scoring neutral', fix: 'Add question-based headings like \"What is...?\", \"How does...?\" to enable Q&A snippet extraction' });\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score: 5, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n // Split HTML on heading boundaries and check if answer follows each question heading\n let answered = 0;\n for (const qHeading of questionHeadings) {\n try {\n const escapedHeading = qHeading.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(escapedHeading + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>([\\\\s\\\\S]{0,1500}?)(?=<h[1-6]|$)', 'i');\n const match = pattern.exec(combinedHtml);\n if (match) {\n const afterContent = match[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n // Check if there's a meaningful answer (at least 20 chars of text content)\n if (afterContent.length >= 20) {\n answered++;\n }\n }\n } catch {\n // Skip headings that produce invalid regex (e.g. from residual script content)\n }\n }\n\n const rate = Math.round((answered / questionHeadings.length) * 100);\n let score: number;\n if (rate >= 80) {\n score = 10;\n findings.push({ severity: 'info', detail: `${answered}/${questionHeadings.length} question headings (${rate}%) followed by direct answers - excellent alignment` });\n } else if (rate >= 50) {\n score = 7;\n findings.push({ severity: 'low', detail: `${answered}/${questionHeadings.length} question headings (${rate}%) have answers`, fix: 'Add concise answer paragraphs after remaining unanswered question headings' });\n } else if (rate > 0) {\n score = 4;\n findings.push({ severity: 'medium', detail: `Only ${answered}/${questionHeadings.length} question headings (${rate}%) are followed by answers`, fix: 'Ensure each question heading is immediately followed by a direct answer paragraph' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${questionHeadings.length} question headings found but none have direct answers`, fix: 'Add answer paragraphs (2-3 sentences) immediately after each question heading' });\n }\n\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 25: Content Cannibalization ───────────────────────────────────\n\nconst STOP_WORDS = new Set([\n 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',\n 'of', 'with', 'by', 'from', 'is', 'it', 'as', 'be', 'was', 'are',\n 'this', 'that', 'your', 'our', 'we', 'you', 'how', 'what', 'why',\n]);\n\n\nfunction extractPageTitle(html: string): string {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n const title = titleMatch?.[1]?.trim() || '';\n const h1 = h1Match?.[1]?.replace(/<[^>]*>/g, '').trim() || '';\n return (title + ' ' + h1).toLowerCase().trim();\n}\n\nfunction titleToWordSet(title: string): Set<string> {\n return new Set(\n title.split(/\\s+/).filter(w => w.length > 1 && !STOP_WORDS.has(w))\n );\n}\n\nfunction jaccardSimilarity(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 && b.size === 0) return 0;\n let intersection = 0;\n for (const word of a) {\n if (b.has(word)) intersection++;\n }\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n\nfunction checkContentCannibalization(data: SiteData, topicCoherenceScore?: number): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for cannibalization analysis' });\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n // Collect pages: homepage + all blogSample\n const pages: Array<{ html: string; url: string }> = [\n { html: data.homepage.text, url: data.homepage.finalUrl || `https://${data.domain}/` },\n ];\n if (data.blogSample) {\n for (const page of data.blogSample) {\n pages.push({ html: page.text, url: page.finalUrl || '' });\n }\n }\n\n if (pages.length <= 1) {\n findings.push({ severity: 'info', detail: 'Only homepage available - cannot assess content cannibalization', fix: 'Add blog/content pages to enable cross-page topic overlap analysis' });\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score: 5, status: 'partial', findings, fix_priority: 'P3' };\n }\n\n // Extract titles and word sets\n const pageTitles = pages.map(p => ({ title: extractPageTitle(p.html), url: p.url }));\n const wordSets = pageTitles.map(p => titleToWordSet(p.title));\n\n // Find site-wide common terms (appearing in >40% of pages).\n // These are \"site theme terms\" (e.g., \"medicare\" on a Medicare site) - NOT cannibalization signals.\n // A focused site SHOULD have many pages sharing core topic terms.\n const termPageCount = new Map<string, number>();\n for (const ws of wordSets) {\n for (const w of ws) {\n termPageCount.set(w, (termPageCount.get(w) || 0) + 1);\n }\n }\n const commonTermThreshold = Math.max(3, pages.length * 0.4);\n const siteThemeTerms = new Set<string>();\n for (const [term, count] of termPageCount) {\n if (count >= commonTermThreshold) siteThemeTerms.add(term);\n }\n\n // Filter word sets: remove site theme terms to compare only distinguishing words\n const filteredSets = wordSets.map(ws => {\n const filtered = new Set<string>();\n for (const w of ws) {\n if (!siteThemeTerms.has(w)) filtered.add(w);\n }\n return filtered;\n });\n\n // Pairwise comparison on FILTERED word sets (only distinguishing terms)\n const cannibalPairs: Array<{ urlA: string; urlB: string; similarity: number }> = [];\n for (let i = 0; i < pages.length; i++) {\n for (let j = i + 1; j < pages.length; j++) {\n // Skip pairs where both have empty filtered sets (means both pages are purely about the core topic)\n if (filteredSets[i].size === 0 && filteredSets[j].size === 0) continue;\n const sim = jaccardSimilarity(filteredSets[i], filteredSets[j]);\n if (sim > 0.6) {\n cannibalPairs.push({\n urlA: pageTitles[i].url.slice(0, 60),\n urlB: pageTitles[j].url.slice(0, 60),\n similarity: Math.round(sim * 100),\n });\n }\n }\n }\n\n // Count unique URLs involved in cannibalization (not pairs)\n const cannibalUrls = new Set<string>();\n for (const pair of cannibalPairs) {\n cannibalUrls.add(pair.urlA);\n cannibalUrls.add(pair.urlB);\n }\n // Scale severity by the ratio of cannibalized pages to total pages\n const cannibalRatio = pages.length > 0 ? cannibalUrls.size / pages.length : 0;\n\n let score: number;\n if (cannibalPairs.length === 0) {\n score = 10;\n findings.push({ severity: 'info', detail: `${pages.length} pages analyzed - no content cannibalization detected` });\n } else if (cannibalRatio <= 0.05) {\n // <5% of pages overlap - negligible\n score = 9;\n findings.push({ severity: 'info', detail: `${cannibalPairs.length} pair(s) of pages with minor topic overlap (${cannibalUrls.size}/${pages.length} pages affected)` });\n } else if (cannibalRatio <= 0.10) {\n score = 7;\n findings.push({ severity: 'low', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) have overlapping topics`, fix: 'Differentiate titles and H1 headings to reduce topic overlap' });\n } else if (cannibalRatio <= 0.20) {\n score = 5;\n findings.push({ severity: 'medium', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) competing for overlapping topics`, fix: 'Consolidate overlapping pages or differentiate their titles and content focus' });\n } else if (cannibalRatio <= 0.40) {\n score = 3;\n findings.push({ severity: 'medium', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) have significant content overlap`, fix: 'Many pages compete for the same topics - consolidate or clearly differentiate them' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) competing for the same topics`, fix: 'Severe content cannibalization - consolidate overlapping pages or create clear topic differentiation' });\n }\n\n // Detail findings for up to 3 pairs\n for (const pair of cannibalPairs.slice(0, 3)) {\n findings.push({ severity: 'low', detail: `Overlap (${pair.similarity}%): ${pair.urlA} vs ${pair.urlB}` });\n }\n\n // Cross-criterion adjustment: If topic coherence is very low (<= 4), cap cannibalization score.\n // \"No overlap\" because content is scattered (humidifiers, groceries, neuropathy) is NOT\n // the same as \"no overlap\" because content is well-differentiated within a focused niche.\n if (topicCoherenceScore !== undefined && topicCoherenceScore <= 4 && score >= 8) {\n score = 6;\n findings.push({ severity: 'low', detail: 'Low topic overlap but content lacks coherent focus - not a strong signal for AI authority', fix: 'Focus content on fewer core topics to build topical authority that AI engines can identify' });\n }\n\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 26: Visible Date Signal ───────────────────────────────────────\n\nfunction checkVisibleDateSignal(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for date signal analysis' });\n return { criterion: 'visible_date_signal', criterion_label: 'Visible Date Signal', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n let score = 0;\n\n // 1. Visible <time datetime=\"...\">text</time> elements\n const timeElements = combinedHtml.match(/<time[^>]*datetime=\"[^\"]*\"[^>]*>[^<]+<\\/time>/gi) || [];\n const hasVisibleTime = timeElements.length > 0;\n if (hasVisibleTime) {\n score += 5;\n findings.push({ severity: 'info', detail: `${timeElements.length} visible <time> element(s) with datetime attribute found` });\n }\n\n // 2. datePublished/dateModified inside JSON-LD <script> blocks only\n const ldJsonBlocks = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const ldJsonText = ldJsonBlocks.join(' ');\n const hasDatePublished = /datePublished/i.test(ldJsonText);\n const hasDateModified = /dateModified/i.test(ldJsonText);\n const hasSchemaDate = hasDatePublished || hasDateModified;\n if (hasSchemaDate) {\n if (!hasVisibleTime) score += 7;\n else score += 5;\n const dateTypes = [hasDatePublished && 'datePublished', hasDateModified && 'dateModified'].filter(Boolean).join(' + ');\n findings.push({ severity: 'info', detail: `JSON-LD schema contains ${dateTypes}` });\n }\n\n // 3. article:published_time / article:modified_time meta tags\n const hasMetaPublished = /<meta[^>]*property=\"article:published_time\"[^>]*>/i.test(combinedHtml);\n const hasMetaModified = /<meta[^>]*property=\"article:modified_time\"[^>]*>/i.test(combinedHtml);\n const hasMetaDate = hasMetaPublished || hasMetaModified;\n if (hasMetaDate && !hasVisibleTime && !hasSchemaDate) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Article meta tags with date information found' });\n } else if (hasMetaDate) {\n findings.push({ severity: 'info', detail: 'Article meta date tags also present (supplementary)' });\n }\n\n // Bonus: dateModified within 6 months\n if (hasDateModified) {\n const dateModMatch = ldJsonText.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (dateModMatch) {\n const modDate = new Date(dateModMatch[1]);\n if (!isNaN(modDate.getTime())) {\n const daysDiff = Math.floor((Date.now() - modDate.getTime()) / (1000 * 60 * 60 * 24));\n if (daysDiff <= 180) {\n score += 1;\n findings.push({ severity: 'info', detail: `dateModified is recent (${daysDiff} days ago) - freshness bonus applied` });\n }\n }\n }\n }\n\n score = Math.min(10, score);\n\n if (score === 0) {\n findings.push({ severity: 'high', detail: 'No visible date signals found (no <time> elements, no JSON-LD dates, no article meta dates)', fix: 'Add <time datetime=\"...\"> elements for user-visible dates and datePublished/dateModified to JSON-LD schema' });\n }\n\n return { criterion: 'visible_date_signal', criterion_label: 'Visible Date Signal', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Raw data summary extraction (for AI narrative) ─────────────────────────\n\nexport function extractRawDataSummary(data: SiteData): RawDataSummary {\n const html = data.homepage?.text || '';\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n\n // Schema types\n const ldJsonMatches = html.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const allSchemaText = ldJsonMatches.join(' ').toLowerCase();\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product'];\n const foundSchemaTypes = schemaTypes.filter(t =>\n allSchemaText.includes(`\"${t}\"`) || allSchemaText.includes(`\"@type\":\"${t}\"`)\n );\n\n // Links\n const linkMatches = html.match(/<a[^>]*href=\"([^\"]*)\"[^>]*>/gi) || [];\n const internalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('/') || href.includes(data.domain);\n });\n const externalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('http') && !href.includes(data.domain);\n });\n\n // robots.txt AI crawlers\n const robotsText = (data.robotsTxt?.text || '').toLowerCase();\n const aiCrawlers = ['gptbot', 'claudebot', 'perplexitybot', 'anthropic', 'chatgpt'];\n const mentionedCrawlers = aiCrawlers.filter(c => robotsText.includes(c));\n const blockedCrawlers = mentionedCrawlers.filter(c => {\n const sectionRegex = new RegExp(`user-agent:\\\\s*${c}[^\\\\S\\\\n]*\\\\n([\\\\s\\\\S]*?)(?=user-agent:|$)`, 'i');\n const match = sectionRegex.exec(data.robotsTxt?.text || '');\n if (!match) return false;\n const section = match[1];\n if (/^allow:\\s*\\/\\s*$/im.test(section)) return false;\n return /^disallow:\\s*\\/\\s*$/im.test(section);\n });\n\n // Headings\n const hTagContent = (html.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n const questionHeadings = hTagContent.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n\n // Images\n const images = html.match(/<img[^>]*>/gi) || [];\n const imagesWithAlt = images.filter(img => /alt=\"[^\"]+\"/i.test(img));\n\n // Semantic elements\n const semanticChecks = ['main', 'article', 'nav', 'header', 'footer', 'section', 'time'];\n const foundElements = semanticChecks.filter(el => new RegExp(`<${el}[\\\\s>]`, 'i').test(html));\n\n return {\n domain: data.domain,\n protocol: data.protocol,\n homepage_length: html.length,\n homepage_text_length: text.trim().length,\n has_https: data.protocol === 'https',\n llms_txt_status: data.llmsTxt && !isHtmlResponse(data.llmsTxt) ? data.llmsTxt.status : null,\n llms_txt_length: data.llmsTxt?.status === 200 && !isHtmlResponse(data.llmsTxt) ? (data.llmsTxt.text.length) : 0,\n robots_txt_status: data.robotsTxt && !isHtmlResponse(data.robotsTxt) ? data.robotsTxt.status : null,\n robots_txt_snippet: (data.robotsTxt?.text || '').slice(0, 500),\n robots_txt_ai_crawlers: mentionedCrawlers,\n robots_txt_blocked_crawlers: blockedCrawlers,\n schema_types_found: foundSchemaTypes,\n schema_block_count: ldJsonMatches.length,\n faq_page_status: data.faqPage?.status ?? null,\n faq_page_length: data.faqPage?.status === 200 ? data.faqPage.text.length : 0,\n sitemap_status: data.sitemapXml?.status ?? null,\n internal_link_count: internalLinks.length,\n external_link_count: externalLinks.length,\n question_headings_count: questionHeadings.length,\n h1_count: h1Count,\n has_meta_description: /<meta[^>]*name=\"description\"[^>]*>/i.test(html),\n has_title: /<title[^>]*>[^<]+<\\/title>/i.test(html),\n has_phone: (() => {\n const phoneMatch = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/.test(text);\n if (!phoneMatch) return false;\n // Context validation: tel: link, schema telephone, or nearby keywords\n return /href=\"tel:/i.test(html) || /\"telephone\"/i.test(html) ||\n /\\b(phone|call|tel:|contact\\s*us|fax|dial)\\b/i.test(text);\n })(),\n has_address: /\\d+\\s+\\w+\\s+(street|st|avenue|ave|road|rd|drive|dr|blvd|boulevard|lane|ln|way|court|ct)/i.test(text),\n has_org_schema: /organization|localbusiness/i.test(html),\n has_social_links: /sameas|linkedin\\.com|facebook\\.com|twitter\\.com|x\\.com/i.test(html),\n semantic_elements_found: foundElements,\n img_count: images.length,\n img_with_alt_count: imagesWithAlt.length,\n has_lang_attr: /lang=\"[a-z]{2}\"/i.test(html),\n has_aria: /role=\"|aria-/i.test(html),\n has_breadcrumbs: /breadcrumb|aria-label=\"breadcrumb\"/i.test(html),\n has_nav: /<nav[\\s>]/i.test(html),\n has_footer: /<footer[\\s>]/i.test(html),\n has_case_studies: /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/i.test(text),\n has_statistics: /\\d+%|\\d+\\s*(patients|clients|customers|cases|years|professionals|specialists|companies|users|businesses|domains|audits)/i.test(text),\n has_expert_attribution: /written\\s+by|authored\\s+by|expert|specialist|board.certified|licensed/i.test(text),\n has_blog_section: /href=\"[^\"]*\\/(?:blog|articles|insights|guides|resources)\\b[^\"]*\"/i.test(html),\n // New criteria fields\n has_date_modified_schema: /dateModified/i.test(html),\n time_element_count: (html.match(/<time[\\s>]/gi) || []).length,\n sitemap_url_count: (data.sitemapXml?.text?.match(/<loc>/gi) || []).length,\n has_rss_feed: !!(data.rssFeed && data.rssFeed.status === 200 && !isHtmlResponse(data.rssFeed)),\n table_count: (html.match(/<table[\\s>]/gi) || []).length,\n ordered_list_count: (html.match(/<ol[\\s>]/gi) || []).length,\n unordered_list_count: (html.match(/<ul[\\s>]/gi) || []).length,\n definition_pattern_count: (text.match(/\\brefers?\\s+to\\b|\\bdefined\\s+as\\b|\\bknown\\s+as\\b/gi) || []).length,\n has_ai_txt: !!(data.aiTxt && data.aiTxt.status === 200 && !isHtmlResponse(data.aiTxt)),\n has_person_schema: /\"@type\"\\s*:\\s*\"Person\"/i.test(html),\n fact_data_point_count: (text.match(/\\d+(?:\\.\\d+)?(?:\\s*%|\\s*\\$|\\s*USD)/g) || []).length,\n has_canonical: /<link[^>]*rel=\"canonical\"/i.test(html),\n has_license_schema: /license|copyrightHolder/i.test(html) && /application\\/ld\\+json/i.test(html),\n sitemap_recent_lastmod_count: (() => {\n const analysis = countRecentSitemapDates(data.sitemapXml?.text || '');\n return analysis.isUniform ? analysis.distinctRecentDays : analysis.recentCount;\n })(),\n // Speakable schema fields\n has_speakable_schema: /speakablespecification/i.test(ldJsonMatches.join(' ')) || /\"speakable\"\\s*:/i.test(ldJsonMatches.join(' ')),\n speakable_selector_count: (ldJsonMatches.join(' ').match(/\"cssselector\"|\"xpath\"/gi) || []).length,\n // Blog sample fields\n blog_sample_count: data.blogSample?.length ?? 0,\n blog_sample_urls: data.blogSample?.map(p => p.finalUrl || '').filter(Boolean) ?? [],\n blog_sample_schema_types: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return [];\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const blogLd = blogHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const blogSchema = blogLd.join(' ').toLowerCase();\n const types = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product', 'person'];\n return types.filter(t => blogSchema.includes(`\"${t}\"`));\n })(),\n blog_sample_question_headings: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return 0;\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const hTags = (blogHtml.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n return hTags.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h)).length;\n })(),\n blog_sample_faq_schema_found: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return false;\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n return /faqpage/i.test(blogHtml) && /application\\/ld\\+json/i.test(blogHtml);\n })(),\n // Criteria 24-26 fields\n question_heading_answer_rate: (() => {\n const combinedHtml = getCombinedHtml(data);\n const qHeadings = extractQuestionHeadings(combinedHtml);\n if (qHeadings.length === 0) return -1;\n let answered = 0;\n for (const qh of qHeadings) {\n const escaped = qh.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pat = new RegExp(escaped + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>([\\\\s\\\\S]{0,1500}?)(?=<h[1-6]|$)', 'i');\n const m = pat.exec(combinedHtml);\n if (m && m[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim().length >= 20) answered++;\n }\n return Math.round((answered / qHeadings.length) * 100);\n })(),\n question_heading_total: extractQuestionHeadings(getCombinedHtml(data)).length,\n cannibalizing_pairs_count: (() => {\n const pages: Array<{ html: string }> = [{ html: data.homepage?.text || '' }];\n if (data.blogSample) for (const p of data.blogSample.slice(0, 5)) pages.push({ html: p.text });\n if (pages.length <= 1) return 0;\n const ws = pages.map(p => titleToWordSet(extractPageTitle(p.html)));\n let pairs = 0;\n for (let i = 0; i < ws.length; i++) {\n for (let j = i + 1; j < ws.length; j++) {\n if (jaccardSimilarity(ws[i], ws[j]) > 0.6) pairs++;\n }\n }\n return pairs;\n })(),\n page_titles_sampled: 1 + (data.blogSample?.length ?? 0),\n has_visible_date: (/<time[^>]*datetime=\"[^\"]*\"[^>]*>[^<]+<\\/time>/i).test(getCombinedHtml(data)),\n has_schema_date_in_ld: (() => {\n const ld = (getCombinedHtml(data).match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || []).join(' ');\n return /datePublished|dateModified/i.test(ld);\n })(),\n date_modified_recency_days: (() => {\n const ld = (getCombinedHtml(data).match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || []).join(' ');\n const m = ld.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (!m) return null;\n const d = new Date(m[1]);\n if (isNaN(d.getTime())) return null;\n return Math.floor((Date.now() - d.getTime()) / (1000 * 60 * 60 * 24));\n })(),\n // Full-crawl stats\n crawl_discovered: data.crawlStats?.discovered ?? 0,\n crawl_fetched: data.crawlStats?.fetched ?? 0,\n crawl_skipped: data.crawlStats?.skipped ?? 0,\n // V2 criteria fields\n citation_ready_sentences: (() => {\n const combinedText = text + ' ' + (data.blogSample?.map(p => p.text.replace(/<[^>]*>/g, ' ')).join(' ') || '');\n return (combinedText.match(/\\b\\w+\\s+(is\\s+(?:a|an)\\s|refers\\s+to|defined\\s+as)\\b/gi) || []).length;\n })(),\n answer_first_ratio: (() => {\n const pages = [html, ...(data.blogSample?.map(p => p.text) || [])];\n let answerFirst = 0;\n for (const pageHtml of pages) {\n const bodyMatch = pageHtml.match(/<body[^>]*>([\\s\\S]*)/i);\n const bodyHtml = bodyMatch ? bodyMatch[1] : pageHtml;\n const earlyParas = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 5) || [];\n for (const p of earlyParas) {\n const pText = p.replace(/<[^>]*>/g, '').trim();\n const wc = pText.split(/\\s+/).length;\n if (wc >= 40 && wc <= 80) { answerFirst++; break; }\n }\n }\n return pages.length > 0 ? Math.round((answerFirst / pages.length) * 100) : 0;\n })(),\n evidence_citations_avg: (() => {\n const allHtml = html + '\\n' + (data.blogSample?.map(p => p.text).join('\\n') || '');\n const paragraphs = allHtml.match(/<p[^>]*>[\\s\\S]*?<\\/p>/gi) || [];\n let citations = 0;\n const domainLower = data.domain.replace(/^www\\./, '').toLowerCase();\n for (const p of paragraphs) {\n const links = p.match(/<a[^>]*href=[\"'](https?:\\/\\/[^\"']+)[\"'][^>]*>/gi) || [];\n for (const link of links) {\n const href = link.match(/href=[\"'](https?:\\/\\/[^\"']+)[\"']/i);\n if (href) {\n try {\n const ld = new URL(href[1]).hostname.replace(/^www\\./, '').toLowerCase();\n if (ld !== domainLower) citations++;\n } catch { /* ignore */ }\n }\n }\n }\n const pageCount = Math.max(1, 1 + (data.blogSample?.length ?? 0));\n return Math.round((citations / pageCount) * 10) / 10;\n })(),\n entity_disambiguation_ratio: (() => {\n const pages = [html, ...(data.blogSample?.map(p => p.text) || [])];\n let defined = 0;\n for (const pageHtml of pages) {\n const h1Match = pageHtml.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (!h1Match) continue;\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3);\n const primaryNoun = h1Words.sort((a, b) => b.length - a.length)[0] || '';\n if (!primaryNoun) continue;\n const pageText = pageHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').slice(0, 500);\n if (new RegExp(`\\\\b${primaryNoun.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b[^.]*\\\\b(is|refers|defined|means)\\\\b`, 'i').test(pageText)) {\n defined++;\n }\n }\n return pages.length > 0 ? Math.round((defined / pages.length) * 100) : 0;\n })(),\n extraction_friction_avg: (() => {\n const combinedText = text + ' ' + (data.blogSample?.map(p => p.text.replace(/<[^>]*>/g, ' ')).join(' ') || '');\n const sentences = combinedText.split(/[.!?]+/).filter(s => s.trim().length > 5);\n if (sentences.length === 0) return 0;\n const totalWords = sentences.reduce((sum, s) => sum + s.trim().split(/\\s+/).length, 0);\n return Math.round((totalWords / sentences.length) * 10) / 10;\n })(),\n image_figure_ratio: (() => {\n const combinedHtml = html + '\\n' + (data.blogSample?.map(p => p.text).join('\\n') || '');\n const allImages = combinedHtml.match(/<img\\s[^>]*>/gi) || [];\n if (allImages.length === 0) return 0;\n const figureBlocks = combinedHtml.match(/<figure[\\s\\S]*?<\\/figure>/gi) || [];\n const figuresWithCaption = figureBlocks.filter(f => /<figcaption/i.test(f));\n return Math.round((figuresWithCaption.length / allImages.length) * 100);\n })(),\n };\n}\n\n// ─── Criterion 27: Topic Coherence ────────────────────────────────────────────\n\n/**\n * Extract meaningful terms from text (strip HTML, tokenize, remove stop words).\n */\nfunction extractTerms(html: string): string[] {\n const text = html\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .toLowerCase();\n return text.split(/[\\s,.!?;:()\\[\\]{}\"'\\/]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n}\n\n/**\n * Extract core topic terms from homepage.\n * Uses H1, title tag, and meta description as the strongest signals.\n */\nfunction extractCoreTopicTerms(html: string): Set<string> {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n const metaDescMatch = html.match(/<meta\\s+[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']+)[\"']/i)\n || html.match(/<meta\\s+[^>]*content=[\"']([^\"']+)[\"'][^>]*name=[\"']description[\"']/i);\n\n // Combine the most prominent text signals\n const prominentText = [\n titleMatch?.[1] || '',\n h1Match?.[1]?.replace(/<[^>]*>/g, '') || '',\n metaDescMatch?.[1] || '',\n ].join(' ');\n\n // Also extract all H2 headings from homepage for broader topic context\n const h2Matches = html.match(/<h2[^>]*>([\\s\\S]*?)<\\/h2>/gi) || [];\n const h2Text = h2Matches.map(h => h.replace(/<[^>]*>/g, '')).join(' ');\n\n const allTerms = extractTerms(prominentText + ' ' + h2Text);\n\n // Count term frequency and take top 25 most common\n const freq = new Map<string, number>();\n for (const t of allTerms) {\n freq.set(t, (freq.get(t) || 0) + 1);\n }\n\n const sorted = [...freq.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 25)\n .map(([term]) => term);\n\n return new Set(sorted);\n}\n\n/**\n * Extract title text from a page (title tag + H1 combined).\n */\nfunction getPageTopicText(html: string): string {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n return [\n titleMatch?.[1] || '',\n h1Match?.[1]?.replace(/<[^>]*>/g, '') || '',\n ].join(' ').toLowerCase().trim();\n}\n\n/**\n * Extract bigrams (2-word phrases) from text.\n */\nfunction extractBigrams(text: string): string[] {\n const words = text.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n const bigrams: string[] = [];\n for (let i = 0; i < words.length - 1; i++) {\n bigrams.push(words[i] + ' ' + words[i + 1]);\n }\n return bigrams;\n}\n\n/**\n * Topic Coherence measures two things:\n * 1. Topic Focus: What % of pages share the dominant topic bigram\n * 2. Topic Scatter: How many distinct topic clusters exist\n *\n * A site with 90% of pages about \"Medicare coverage\" = high coherence\n * A site with pages about Medicare, humidifiers, groceries, neuropathy = low coherence\n */\nfunction checkTopicCoherence(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score: 0, status: 'not_found', findings, fix_priority: 'P0' };\n }\n\n if (!data.blogSample || data.blogSample.length < 3) {\n findings.push({ severity: 'info', detail: `Only ${data.blogSample?.length || 0} blog pages found - insufficient for topic coherence analysis` });\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score: 5, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n const blogPages = data.blogSample;\n\n // ─── Extract brand/domain words to exclude from topic analysis ──────────────\n // Compound domain names like \"miramace.com\" need to be split into component words.\n // We do a two-pass approach: first collect raw frequencies, then identify brand terms.\n const domainBase = data.domain.replace(/^www\\./, '').replace(/\\.(com|org|net|io|co|ai)$/i, '').toLowerCase();\n const brandWords = new Set<string>();\n brandWords.add(domainBase);\n // Split on hyphens/underscores\n for (const part of domainBase.split(/[-_]/)) {\n if (part.length > 2) brandWords.add(part);\n }\n\n // ─── Part 1: Topic Focus via dominant terms ─────────────────────────────────\n // First pass: count ALL terms (including potential brand words)\n const rawTermFreq = new Map<string, number>();\n const pageTitleTexts: string[] = [];\n\n for (const page of blogPages) {\n const topicText = getPageTopicText(page.text);\n pageTitleTexts.push(topicText);\n const words = topicText.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n const uniqueWords = new Set(words);\n for (const w of uniqueWords) {\n rawTermFreq.set(w, (rawTermFreq.get(w) || 0) + 1);\n }\n }\n\n // Second pass: identify brand terms - words that appear in >80% of pages\n // AND are substrings of the domain name (e.g., \"mira\" is in \"miramace\")\n for (const [term, count] of rawTermFreq) {\n if (count / blogPages.length >= 0.80 && domainBase.includes(term)) {\n brandWords.add(term);\n }\n }\n\n // Third pass: rebuild frequencies without brand words\n const termFreq = new Map<string, number>();\n for (const page of blogPages) {\n const topicText = getPageTopicText(page.text);\n const words = topicText.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w) && !brandWords.has(w));\n const uniqueWords = new Set(words);\n for (const w of uniqueWords) {\n termFreq.set(w, (termFreq.get(w) || 0) + 1);\n }\n }\n\n // Find the top terms that appear across the most pages\n const sortedTerms = [...termFreq.entries()]\n .sort((a, b) => b[1] - a[1]);\n const topTerm = sortedTerms[0];\n\n // ─── Part 2: Topic Scatter via unique topic clusters ────────────────────────\n // Extract meaningful bigrams from each page title and cluster them\n const bigramFreq = new Map<string, number>();\n const pageBigrams: string[][] = [];\n\n for (const topicText of pageTitleTexts) {\n const bigrams = extractBigrams(topicText)\n .filter(bg => !bg.split(' ').some(w => brandWords.has(w)));\n pageBigrams.push(bigrams);\n const uniqueBigrams = new Set(bigrams);\n for (const bg of uniqueBigrams) {\n bigramFreq.set(bg, (bigramFreq.get(bg) || 0) + 1);\n }\n }\n\n // Find dominant bigram\n const sortedBigrams = [...bigramFreq.entries()]\n .sort((a, b) => b[1] - a[1]);\n const topBigram = sortedBigrams[0];\n\n // Count distinct \"topic clusters\" - bigrams that appear in 2+ pages but are unique subjects\n // A focused site has 1-3 topic clusters; a scattered site has 10+\n const significantBigrams = sortedBigrams.filter(([, count]) => count >= 2);\n // Group overlapping bigrams (sharing a word) into clusters\n const clusterRoots: string[] = [];\n const assigned = new Set<string>();\n for (const [bg] of significantBigrams) {\n if (assigned.has(bg)) continue;\n clusterRoots.push(bg);\n assigned.add(bg);\n const [w1, w2] = bg.split(' ');\n // Mark overlapping bigrams as same cluster\n for (const [otherBg] of significantBigrams) {\n if (assigned.has(otherBg)) continue;\n if (otherBg.includes(w1) || otherBg.includes(w2)) {\n assigned.add(otherBg);\n }\n }\n }\n const topicClusterCount = clusterRoots.length;\n\n // Count pages that contain the dominant term\n const dominantTerm = topTerm?.[0] || '';\n const dominantTermCount = topTerm?.[1] || 0;\n const focusRatio = blogPages.length > 0 ? dominantTermCount / blogPages.length : 0;\n\n // Count pages that contain the dominant bigram\n const dominantBigram = topBigram?.[0] || '';\n const dominantBigramCount = topBigram?.[1] || 0;\n const bigramFocusRatio = blogPages.length > 0 ? dominantBigramCount / blogPages.length : 0;\n\n // ─── Scoring ────────────────────────────────────────────────────────────────\n // Primary signal: what % of pages share the dominant topic term.\n // Secondary signal: how many distinct topic clusters exist.\n // A Medicare site with varied products (\"scooter\", \"wheelchair\", \"walker\") is STILL coherent\n // if 80%+ of pages share \"medicare\" as the core topic.\n\n let score = 0;\n const bestFocusRatio = Math.max(focusRatio, bigramFocusRatio);\n\n // For sites with many pages, absolute term count matters more than ratio.\n // A term appearing on 15 of 60 pages (25%) is better than 2 of 5 pages (40%).\n const dominantPageCount = Math.max(dominantTermCount, dominantBigramCount);\n const hasStrongAbsolutePresence = dominantPageCount >= 10;\n\n // Focus score (0-7 points) - scale thresholds for large sites\n if (bestFocusRatio >= 0.80) {\n score += 7;\n } else if (bestFocusRatio >= 0.60) {\n score += 6;\n } else if (bestFocusRatio >= 0.45 || (hasStrongAbsolutePresence && bestFocusRatio >= 0.30)) {\n score += 5;\n } else if (bestFocusRatio >= 0.30 || (hasStrongAbsolutePresence && bestFocusRatio >= 0.20)) {\n score += 4;\n } else if (bestFocusRatio >= 0.15) {\n score += 2;\n } else {\n score += 1;\n }\n\n // Scatter adjustment (0-3 points for few clusters, penalty for many)\n // Scale cluster thresholds by page count - more pages naturally produce more clusters\n const clusterPenaltyReduced = focusRatio >= 0.70 || hasStrongAbsolutePresence;\n const scaledLow = Math.max(3, Math.floor(blogPages.length / 10));\n const scaledMid = Math.max(6, Math.floor(blogPages.length / 5));\n const scaledHigh = Math.max(10, Math.floor(blogPages.length / 3));\n\n if (topicClusterCount <= scaledLow) {\n score += 3;\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic cluster(s) - tightly focused content` });\n } else if (topicClusterCount <= scaledMid) {\n score += clusterPenaltyReduced ? 2 : 1;\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic clusters${clusterPenaltyReduced ? ' within a focused niche' : ' - moderately focused'}` });\n } else if (topicClusterCount <= scaledHigh) {\n score += clusterPenaltyReduced ? 1 : 0;\n if (!clusterPenaltyReduced) {\n findings.push({ severity: 'low', detail: `${topicClusterCount} topic clusters - scattered content`, fix: 'Reduce the number of distinct topics. Focus blog content on 2-3 core expertise areas.' });\n } else {\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic clusters but strong core topic focus (${Math.round(focusRatio * 100)}%)` });\n }\n } else {\n score += clusterPenaltyReduced ? 0 : -2;\n if (!clusterPenaltyReduced) {\n findings.push({ severity: 'medium', detail: `${topicClusterCount} topic clusters - highly scattered content`, fix: 'Content covers too many unrelated topics. AI engines cannot identify your expertise. Focus on your core niche.' });\n } else {\n findings.push({ severity: 'low', detail: `${topicClusterCount} topic clusters despite strong core topic focus`, fix: 'Consider narrowing subtopics within your niche for even stronger AI visibility.' });\n }\n }\n\n score = Math.max(0, Math.min(10, score));\n\n // Report dominant topic\n if (dominantTerm) {\n const focusPct = Math.round(focusRatio * 100);\n findings.push({ severity: 'info', detail: `Dominant topic term: \"${dominantTerm}\" (${focusPct}% of ${blogPages.length} pages)` });\n }\n if (dominantBigram && dominantBigramCount >= 2) {\n findings.push({ severity: 'info', detail: `Dominant topic phrase: \"${dominantBigram}\" (${dominantBigramCount}/${blogPages.length} pages)` });\n }\n\n // Off-topic examples\n const offTopicExamples: string[] = [];\n for (let i = 0; i < pageTitleTexts.length && offTopicExamples.length < 3; i++) {\n if (dominantTerm && !pageTitleTexts[i].includes(dominantTerm)) {\n const title = blogPages[i].text.match(/<title[^>]*>([^<]+)<\\/title>/i)?.[1]?.trim();\n if (title && title.length > 3) offTopicExamples.push(title.slice(0, 60));\n }\n }\n if (offTopicExamples.length > 0 && score < 8) {\n findings.push({ severity: 'low', detail: `Off-topic examples: ${offTopicExamples.join('; ')}` });\n }\n\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P0' };\n}\n\n// ─── Criterion 28: Content Depth ──────────────────────────────────────────────\n\nfunction countWords(html: string): number {\n const text = html\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\nfunction countHeadings(html: string): number {\n const headings = html.match(/<h[2-6][^>]*>/gi) || [];\n return headings.length;\n}\n\nfunction checkContentDepth(data: SiteData, topicCoherenceScore?: number): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.blogSample || data.blogSample.length < 2) {\n findings.push({ severity: 'info', detail: `Only ${data.blogSample?.length || 0} blog pages found - insufficient for depth analysis` });\n return { criterion: 'content_depth', criterion_label: 'Content Depth', score: 3, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n const blogPages = data.blogSample;\n const wordCounts = blogPages.map(p => countWords(p.text));\n const headingCounts = blogPages.map(p => countHeadings(p.text));\n\n const avgWords = wordCounts.reduce((a, b) => a + b, 0) / wordCounts.length;\n const avgHeadings = headingCounts.reduce((a, b) => a + b, 0) / headingCounts.length;\n const deepPages = wordCounts.filter(w => w >= 1000).length;\n const thinPages = wordCounts.filter(w => w < 300).length;\n const deepRatio = deepPages / blogPages.length;\n const thinRatio = thinPages / blogPages.length;\n\n let score = 0;\n\n // Word count scoring (0-5 points)\n if (avgWords >= 2000) {\n score += 5;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page across ${blogPages.length} pages - excellent depth` });\n } else if (avgWords >= 1200) {\n score += 4;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page across ${blogPages.length} pages - good depth` });\n } else if (avgWords >= 800) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page - moderate depth` });\n } else if (avgWords >= 400) {\n score += 2;\n findings.push({ severity: 'low', detail: `Average ${Math.round(avgWords)} words per page - shallow content`, fix: 'Expand articles with more detail, examples, and expert analysis to build AI citation authority' });\n } else {\n score += 1;\n findings.push({ severity: 'medium', detail: `Average ${Math.round(avgWords)} words per page - very thin content`, fix: 'Content is too thin for AI engines to cite. Aim for 1000+ words per article with structured sections.' });\n }\n\n // Heading structure scoring (0-3 points)\n if (avgHeadings >= 8) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - well-structured` });\n } else if (avgHeadings >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - decent structure` });\n } else if (avgHeadings >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page`, fix: 'Add more H2/H3 headings to break content into extractable sections' });\n } else {\n findings.push({ severity: 'medium', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - minimal structure`, fix: 'Add question-format H2/H3 headings so AI engines can extract specific answers' });\n }\n\n // Deep content ratio bonus (0-2 points)\n if (deepRatio >= 0.5) {\n score += 2;\n findings.push({ severity: 'info', detail: `${deepPages}/${blogPages.length} pages (${Math.round(deepRatio * 100)}%) have 1000+ words` });\n } else if (deepRatio >= 0.25) {\n score += 1;\n findings.push({ severity: 'info', detail: `${deepPages}/${blogPages.length} pages have 1000+ words` });\n }\n\n // Thin content penalty\n if (thinRatio >= 0.5) {\n score = Math.max(0, score - 2);\n findings.push({ severity: 'medium', detail: `${thinPages}/${blogPages.length} pages (${Math.round(thinRatio * 100)}%) have under 300 words - high thin content ratio`, fix: 'Remove or expand thin pages. Thin content dilutes site quality for AI engines.' });\n } else if (thinRatio >= 0.25) {\n score = Math.max(0, score - 1);\n findings.push({ severity: 'low', detail: `${thinPages}/${blogPages.length} pages have under 300 words` });\n }\n\n // Cross-criterion: deep content on scattered topics is less valuable for AI authority.\n // A 5000-word article about humidifiers doesn't help a healthcare company's AI citations.\n let finalScore = Math.min(10, score);\n if (topicCoherenceScore !== undefined && topicCoherenceScore <= 4 && finalScore >= 8) {\n finalScore = 7;\n findings.push({ severity: 'low', detail: 'Deep content but low topic coherence - depth on scattered topics has reduced AI citation value', fix: 'Focus content depth on your core expertise area for maximum AI visibility' });\n }\n\n return { criterion: 'content_depth', criterion_label: 'Content Depth', score: finalScore, status: finalScore >= 7 ? 'pass' : finalScore >= 4 ? 'partial' : 'fail', findings, fix_priority: finalScore >= 7 ? 'P3' : 'P1' };\n}\n\ninterface HelpfulContentPageScore {\n url: string;\n score: number;\n}\n\nfunction scoreSampledPages(\n data: SiteData,\n scorer: (html: string, url?: string) => number,\n): HelpfulContentPageScore[] {\n const pages: HelpfulContentPageScore[] = [];\n\n if (data.homepage) {\n const url = data.homepage.finalUrl || (data.protocol ? `${data.protocol}://${data.domain}/` : `https://${data.domain}/`);\n pages.push({ url, score: scorer(data.homepage.text, url) });\n }\n\n if (data.blogSample) {\n for (const page of data.blogSample) {\n const url = page.finalUrl || (data.protocol ? `${data.protocol}://${data.domain}/` : `https://${data.domain}/`);\n pages.push({ url, score: scorer(page.text, url) });\n }\n }\n\n return pages;\n}\n\nfunction summarizeHelpfulScores(pageScores: HelpfulContentPageScore[]) {\n const total = pageScores.length;\n const average = total > 0 ? Math.round(pageScores.reduce((sum, p) => sum + p.score, 0) / total) : 0;\n const strong = pageScores.filter(p => p.score >= 8);\n const weak = pageScores.filter(p => p.score <= 4);\n return { total, average, strong, weak };\n}\n\n// ─── Criterion 29: Helpful Purpose Alignment ────────────────────────────────\n\nfunction checkHelpfulPurposeAlignment(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'helpful_purpose_alignment', criterion_label: 'Helpful Purpose Alignment', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const pageScores = scoreSampledPages(data, scoreHelpfulPurposeAlignment);\n const { total, average, strong, weak } = summarizeHelpfulScores(pageScores);\n\n if (average >= 8) {\n findings.push({ severity: 'info', detail: `${strong.length}/${total} pages strongly prioritize visitor task completion over filler` });\n } else if (average >= 5) {\n findings.push({ severity: 'low', detail: `${strong.length}/${total} pages clearly lead with useful guidance`, fix: 'Tighten intros, reduce generic filler, and make pages solve the promised user task faster' });\n } else {\n findings.push({ severity: 'medium', detail: `Average helpful-purpose score is ${average}/10 across ${total} sampled pages`, fix: 'Reduce search-first framing, generic intros, and CTA interruptions before the first useful answer' });\n }\n\n if (weak.length > 0) {\n findings.push({\n severity: 'low',\n detail: `${weak.length} page(s) read as weakly task-focused`,\n fix: 'Rewrite weak pages to lead with concrete answers, tradeoffs, and next steps instead of broad introductory filler',\n });\n }\n\n return { criterion: 'helpful_purpose_alignment', criterion_label: 'Helpful Purpose Alignment', score: average, status: average >= 7 ? 'pass' : average >= 4 ? 'partial' : 'fail', findings, fix_priority: average >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 30: First-Hand Experience Signals ────────────────────────────\n\nfunction checkFirstHandExperienceSignals(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'first_hand_experience_signals', criterion_label: 'First-Hand Experience Signals', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const pageScores = scoreSampledPages(data, scoreFirstHandExperienceSignals);\n const { total, average, strong, weak } = summarizeHelpfulScores(pageScores);\n\n if (average >= 8) {\n findings.push({ severity: 'info', detail: `${strong.length}/${total} pages show strong signs of direct use, testing, or observation` });\n } else if (average >= 5) {\n findings.push({ severity: 'low', detail: `Moderate experiential depth across ${total} sampled pages`, fix: 'Add real implementation details, limitations, screenshots, or direct observations where relevant' });\n } else {\n findings.push({ severity: 'medium', detail: 'Little first-hand experience is visible in sampled content', fix: 'Add evidence of real use, testing, implementation, or lived experience instead of generic summaries' });\n }\n\n if (weak.length > 0) {\n findings.push({\n severity: 'low',\n detail: `${weak.length} page(s) appear generic or second-hand`,\n fix: 'Strengthen those pages with case details, lessons learned, or original evidence from practice',\n });\n }\n\n return { criterion: 'first_hand_experience_signals', criterion_label: 'First-Hand Experience Signals', score: average, status: average >= 7 ? 'pass' : average >= 4 ? 'partial' : 'fail', findings, fix_priority: average >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 31: Creator Transparency ─────────────────────────────────────\n\nfunction checkCreatorTransparency(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'creator_transparency', criterion_label: 'Creator Transparency', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const pageScores = scoreSampledPages(data, scoreCreatorTransparency);\n const { total, average, strong, weak } = summarizeHelpfulScores(pageScores);\n\n if (average >= 8) {\n findings.push({ severity: 'info', detail: `${strong.length}/${total} sampled pages provide clear visible creator attribution` });\n } else if (average >= 5) {\n findings.push({ severity: 'low', detail: 'Visible authorship is present on some content but inconsistent', fix: 'Add bylines, author links, and reviewer details on article-like pages where readers expect them' });\n } else {\n findings.push({ severity: 'medium', detail: 'Creator visibility is weak on content-like pages', fix: 'Show clear bylines, author pages, and reviewer context rather than relying on schema alone' });\n }\n\n if (weak.length > 0) {\n findings.push({\n severity: 'low',\n detail: `${weak.length} page(s) look article-like but expose little visible author context`,\n fix: 'Add visible bylines, author bios, or reviewer attribution to those pages',\n });\n }\n\n return { criterion: 'creator_transparency', criterion_label: 'Creator Transparency', score: average, status: average >= 7 ? 'pass' : average >= 4 ? 'partial' : 'fail', findings, fix_priority: average >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 32: Methodology Transparency ─────────────────────────────────\n\nfunction checkMethodologyTransparency(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'methodology_transparency', criterion_label: 'Methodology Transparency', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const pageScores = scoreSampledPages(data, scoreMethodologyTransparency);\n const { total, average, strong, weak } = summarizeHelpfulScores(pageScores);\n\n if (average >= 8) {\n findings.push({ severity: 'info', detail: `${strong.length}/${total} pages clearly explain how content was tested, researched, reviewed, or updated` });\n } else if (average >= 5) {\n findings.push({ severity: 'low', detail: 'Some process transparency exists, but it is inconsistent', fix: 'Add \"how we tested\", methodology, review process, or update disclosures on pages where users would expect them' });\n } else {\n findings.push({ severity: 'medium', detail: 'Little content-production or review transparency is visible', fix: 'Explain how pages were researched, tested, or reviewed instead of presenting unsupported comparisons or conclusions' });\n }\n\n if (weak.length > 0) {\n findings.push({\n severity: 'low',\n detail: `${weak.length} page(s) lack visible methodology or review context`,\n fix: 'Add process detail such as sample size, criteria, tools used, review process, or update notes',\n });\n }\n\n return { criterion: 'methodology_transparency', criterion_label: 'Methodology Transparency', score: average, status: average >= 7 ? 'pass' : average >= 4 ? 'partial' : 'fail', findings, fix_priority: average >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 33: Citation-Ready Writing Quality ─────────────────────────────\n\nfunction checkCitationReadyWriting(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'citation_ready_writing', criterion_label: 'Citation-Ready Writing Quality', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const blogHtml = getBlogHtml(data);\n const blogText = blogHtml ? blogHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ') : text;\n const pageCount = Math.max(1, (data.blogSample?.length ?? 0) + 1);\n let score = 0;\n\n // Definition sentences\n const defSentences = blogText.match(/\\b\\w+\\s+(is\\s+(?:a|an)\\s|refers\\s+to|defined\\s+as)\\b/gi) || [];\n const avgDef = defSentences.length / pageCount;\n if (avgDef >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `${defSentences.length} definition sentences found (avg ${avgDef.toFixed(1)}/page)` });\n } else if (avgDef >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${defSentences.length} definition sentences found (avg ${avgDef.toFixed(1)}/page)`, fix: 'Add more self-contained definition sentences that AI can quote directly' });\n } else {\n findings.push({ severity: 'medium', detail: 'No definition sentences found', fix: 'Write clear \"X is a...\" and \"X refers to...\" sentences for key concepts' });\n }\n\n // One-claim sentences (short sentences <30 words with single clause)\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10);\n const oneClaimSentences = sentences.filter(s => {\n const words = s.trim().split(/\\s+/);\n return words.length < 30 && !/,\\s*(and|but|or|which|that|because|although|while)\\s/i.test(s);\n });\n const avgOneClaim = oneClaimSentences.length / pageCount;\n if (avgOneClaim >= 5) {\n score += 3;\n findings.push({ severity: 'info', detail: `Strong single-claim sentence density (avg ${avgOneClaim.toFixed(1)}/page)` });\n } else if (avgOneClaim >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Moderate single-claim sentence density`, fix: 'Write more concise, single-claim sentences that AI engines can quote' });\n }\n\n // Self-contained check: first sentence after question headings\n const qHeadings = extractQuestionHeadings(combinedHtml);\n if (qHeadings.length > 0) {\n let selfContained = 0;\n for (const qh of qHeadings) {\n const escaped = qh.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pat = new RegExp(escaped + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>\\\\s*<p[^>]*>([^<]{0,200})', 'i');\n const m = pat.exec(combinedHtml);\n if (m && m[1] && !/^\\s*(this|that|these|those|it)\\s/i.test(m[1].trim())) {\n selfContained++;\n }\n }\n const selfContainedRatio = selfContained / qHeadings.length;\n if (selfContainedRatio >= 0.8) {\n score += 2;\n findings.push({ severity: 'info', detail: `${Math.round(selfContainedRatio * 100)}% of answer openings are self-contained (no dangling pronouns)` });\n } else {\n findings.push({ severity: 'low', detail: `Only ${Math.round(selfContainedRatio * 100)}% of answer openings are self-contained`, fix: 'Start answer paragraphs with the entity name, not \"This\" or \"That\"' });\n }\n }\n\n // Quotation-ready lines (sentences with stat + proper noun source)\n const quotableLines = sentences.filter(s =>\n /\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(s) &&\n /\\b[A-Z][a-z]+(?:\\s+[A-Z][a-z]+)*\\b/.test(s)\n );\n const avgQuotable = quotableLines.length / pageCount;\n if (avgQuotable >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: `${quotableLines.length} quotation-ready lines with stats and sources` });\n } else if (avgQuotable >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${quotableLines.length} quotation-ready lines found`, fix: 'Combine statistics with named sources for more citable content' });\n } else {\n findings.push({ severity: 'medium', detail: 'No quotation-ready lines found', fix: 'Write sentences that combine a statistic with a named source, e.g. \"According to Harvard, 85% of...\"' });\n }\n\n return { criterion: 'citation_ready_writing', criterion_label: 'Citation-Ready Writing Quality', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 30: Answer-First Placement ──────────────────────────────────────\n\nfunction checkAnswerFirstPlacement(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'answer_first_placement', criterion_label: 'Answer-First Placement', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const pages: string[] = [stripScripts(data.homepage.text)];\n if (data.blogSample) {\n for (const p of data.blogSample) {\n pages.push(stripScripts(p.text));\n }\n }\n\n let score = 0;\n let shortAnswerCount = 0;\n let answerFirstCount = 0;\n let entityFirstCount = 0;\n\n const throatClearing = /^(In today|Welcome to|Have you ever|If you're looking|In this (article|post|guide))/i;\n\n for (const pageHtml of pages) {\n // Extract first 300 words worth of text after body\n const bodyMatch = pageHtml.match(/<body[^>]*>([\\s\\S]*)/i);\n const bodyHtml = bodyMatch ? bodyMatch[1] : pageHtml;\n const bodyText = bodyHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n const first300Words = bodyText.split(/\\s+/).slice(0, 300).join(' ');\n\n // Short answer block (40-80 words paragraph in first 300 words)\n const earlyParagraphs = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 5) || [];\n for (const p of earlyParagraphs) {\n const pText = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = pText.split(/\\s+/).length;\n if (wordCount >= 40 && wordCount <= 80 && first300Words.includes(pText.slice(0, 50))) {\n shortAnswerCount++;\n break;\n }\n }\n\n // First-paragraph answer check (vs throat-clearing)\n const firstPara = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/i);\n if (firstPara) {\n const firstParaText = firstPara[1].replace(/<[^>]*>/g, '').trim();\n if (!throatClearing.test(firstParaText)) {\n answerFirstCount++;\n }\n }\n\n // Entity in first sentence: H1 primary noun in first body sentence\n const h1Match = pageHtml.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (h1Match) {\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3 && /^[A-Z]/.test(w) || w.length > 5);\n const firstSentence = bodyText.split(/[.!?]/)[0] || '';\n if (h1Words.some(w => firstSentence.toLowerCase().includes(w.toLowerCase()))) {\n entityFirstCount++;\n }\n }\n }\n\n const totalPages = pages.length;\n const shortAnswerRatio = shortAnswerCount / totalPages;\n const answerFirstRatio = answerFirstCount / totalPages;\n const entityFirstRatio = entityFirstCount / totalPages;\n\n // Short answer block: 70%+ pages: 4 pts\n if (shortAnswerRatio >= 0.7) {\n score += 4;\n findings.push({ severity: 'info', detail: `${Math.round(shortAnswerRatio * 100)}% of pages have a short answer block (40-80 words) early` });\n } else if (shortAnswerRatio >= 0.3) {\n score += 2;\n findings.push({ severity: 'low', detail: `${Math.round(shortAnswerRatio * 100)}% of pages have an early answer block`, fix: 'Add a concise 40-80 word answer paragraph in the first 300 words of each page' });\n } else {\n findings.push({ severity: 'medium', detail: 'Few pages have an early answer block', fix: 'Place a 40-80 word summary paragraph near the top of each page for AI snippet extraction' });\n }\n\n // First-paragraph answer check: 70%+ answer-first: 3 pts\n if (answerFirstRatio >= 0.7) {\n score += 3;\n findings.push({ severity: 'info', detail: `${Math.round(answerFirstRatio * 100)}% of pages open with a direct answer (no throat-clearing)` });\n } else if (answerFirstRatio >= 0.4) {\n score += 1;\n findings.push({ severity: 'low', detail: `${Math.round(answerFirstRatio * 100)}% of pages open with a direct answer`, fix: 'Avoid openers like \"In today\\'s world...\" - start with the answer' });\n } else {\n findings.push({ severity: 'medium', detail: 'Most pages use throat-clearing openers instead of direct answers', fix: 'Replace \"In this article...\" with a direct answer to the page\\'s main question' });\n }\n\n // Entity in first sentence: 80%+ pages: 3 pts\n if (entityFirstRatio >= 0.8) {\n score += 3;\n findings.push({ severity: 'info', detail: `${Math.round(entityFirstRatio * 100)}% of pages mention the primary entity in the first sentence` });\n } else if (entityFirstRatio >= 0.5) {\n score += 1;\n findings.push({ severity: 'low', detail: `${Math.round(entityFirstRatio * 100)}% mention the primary entity in the first sentence`, fix: 'Include the H1 topic in the first body sentence for AI extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'Few pages mention the primary entity in the opening sentence', fix: 'Start content with a sentence that names the page topic explicitly' });\n }\n\n return { criterion: 'answer_first_placement', criterion_label: 'Answer-First Placement', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 31: Evidence Packaging ──────────────────────────────────────────\n\nfunction checkEvidencePackaging(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'evidence_packaging', criterion_label: 'Evidence Packaging', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const pageCount = Math.max(1, (data.blogSample?.length ?? 0) + 1);\n let score = 0;\n\n // Inline citations: <a> inside <p> linking to external domains\n const paragraphs = combinedHtml.match(/<p[^>]*>[\\s\\S]*?<\\/p>/gi) || [];\n let inlineCitations = 0;\n const domain = data.domain.replace(/^www\\./, '').toLowerCase();\n for (const p of paragraphs) {\n const links = p.match(/<a[^>]*href=[\"'](https?:\\/\\/[^\"']+)[\"'][^>]*>/gi) || [];\n for (const link of links) {\n const href = link.match(/href=[\"'](https?:\\/\\/[^\"']+)[\"']/i);\n if (href) {\n try {\n const linkDomain = new URL(href[1]).hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== domain) inlineCitations++;\n } catch { /* ignore */ }\n }\n }\n }\n const avgCitations = inlineCitations / pageCount;\n if (avgCitations >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `${inlineCitations} inline citations to external sources (avg ${avgCitations.toFixed(1)}/page)` });\n } else if (avgCitations >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${inlineCitations} inline citations found`, fix: 'Add more inline links to authoritative external sources within paragraphs' });\n } else {\n findings.push({ severity: 'medium', detail: 'No inline citations to external sources', fix: 'Link to authoritative sources within your content paragraphs to strengthen evidence' });\n }\n\n // Sources section: heading matching \"source|reference|citation|bibliography\"\n const sourcesHeading = combinedHtml.match(/<h[2-4][^>]*>[^<]*(source|reference|citation|bibliography)[^<]*<\\/h[2-4]>/gi) || [];\n const sourceSections = sourcesHeading.length;\n if (sourceSections > 0) {\n const ratio = sourceSections / pageCount;\n if (ratio >= 0.5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Sources/References section found on ${sourceSections} page(s)` });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: `Sources section found on ${sourceSections} page(s)`, fix: 'Add a Sources or References section to more content pages' });\n }\n } else {\n findings.push({ severity: 'low', detail: 'No Sources/References section found', fix: 'Add a \"Sources\" heading at the end of key articles' });\n }\n\n // Attribution phrases\n const attributionPhrases = text.match(/\\baccording\\s+to\\s+[A-Z]|\\b[A-Z][a-z]+\\s+(found|says|reports|notes|states|estimates|suggests)\\b/gi) || [];\n const avgAttribution = attributionPhrases.length / pageCount;\n if (avgAttribution >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `${attributionPhrases.length} attribution phrases found (avg ${avgAttribution.toFixed(1)}/page)` });\n } else if (avgAttribution >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${attributionPhrases.length} attribution phrases found`, fix: 'Add more \"According to [Source]...\" attribution for credibility' });\n } else {\n findings.push({ severity: 'medium', detail: 'No attribution phrases found', fix: 'Use \"According to [Name]...\" and \"[Expert] reports...\" to attribute claims' });\n }\n\n // Sourced statistics: number+unit in sentences with source name\n const sourcedStats = text.match(/\\d+(\\.\\d+)?(%|\\s*(million|billion|thousand|percent))\\b[^.]*\\b[A-Z][a-z]+\\b/gi) || [];\n const avgSourcedStats = sourcedStats.length / pageCount;\n if (avgSourcedStats >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: `${sourcedStats.length} sourced statistics found` });\n } else if (avgSourcedStats >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${sourcedStats.length} sourced statistics found`, fix: 'Pair more statistics with named sources' });\n }\n\n return { criterion: 'evidence_packaging', criterion_label: 'Evidence Packaging', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 32: Entity Disambiguation ───────────────────────────────────────\n\nfunction checkEntityDisambiguation(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'entity_disambiguation', criterion_label: 'Entity Disambiguation', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const pages: string[] = [stripScripts(data.homepage.text)];\n if (data.blogSample) {\n for (const p of data.blogSample) {\n pages.push(stripScripts(p.text));\n }\n }\n\n let score = 0;\n let definedEarlyCount = 0;\n let consistentCount = 0;\n let disambiguationCount = 0;\n\n for (const pageHtml of pages) {\n const text = pageHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const h1Match = pageHtml.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (!h1Match) continue;\n\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n // Extract primary noun from H1 (longest word or multi-word phrase)\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3);\n const primaryNoun = h1Words.sort((a, b) => b.length - a.length)[0] || '';\n if (!primaryNoun) continue;\n\n // Primary entity defined early (first 500 chars)\n const first500 = text.slice(0, 500);\n if (new RegExp(`\\\\b${primaryNoun.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b[^.]*\\\\b(is|refers|defined|means)\\\\b`, 'i').test(first500)) {\n definedEarlyCount++;\n }\n\n // Entity consistency: primary H1 noun used consistently (same term 70%+)\n const primaryNounLower = primaryNoun.toLowerCase();\n const primaryRegex = new RegExp(`\\\\b${primaryNounLower.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b`, 'gi');\n const mentions = text.match(primaryRegex) || [];\n if (mentions.length >= 3) {\n consistentCount++;\n }\n\n // Disambiguation signals\n if (/\\bunlike\\s+\\w/i.test(text) || /\\bcompared\\s+to\\s+\\w/i.test(text) || /\\bnot\\s+to\\s+be\\s+confused\\s+with\\b/i.test(text) || /\\bas\\s+opposed\\s+to\\b/i.test(text)) {\n disambiguationCount++;\n }\n }\n\n const totalPages = pages.length;\n\n // Primary entity defined early: 70%+ pages: 4 pts\n const definedRatio = definedEarlyCount / totalPages;\n if (definedRatio >= 0.7) {\n score += 4;\n findings.push({ severity: 'info', detail: `${Math.round(definedRatio * 100)}% of pages define the primary entity early` });\n } else if (definedRatio >= 0.3) {\n score += 2;\n findings.push({ severity: 'low', detail: `${Math.round(definedRatio * 100)}% of pages define the primary entity early`, fix: 'Define the main topic within the first 500 characters of each page' });\n } else {\n findings.push({ severity: 'medium', detail: 'Few pages define the primary entity early', fix: 'Start each page with a clear definition of the main topic' });\n }\n\n // Entity consistency: 70%+ pages using consistent terminology: 3 pts\n const consistentRatio = consistentCount / totalPages;\n if (consistentRatio >= 0.7) {\n score += 3;\n findings.push({ severity: 'info', detail: `${Math.round(consistentRatio * 100)}% of pages use consistent entity terminology` });\n } else if (consistentRatio >= 0.3) {\n score += 1;\n findings.push({ severity: 'low', detail: `${Math.round(consistentRatio * 100)}% of pages use consistent terminology`, fix: 'Use the same primary term consistently instead of varying synonyms' });\n } else {\n findings.push({ severity: 'low', detail: 'Low entity terminology consistency across pages', fix: 'Repeat the primary entity term consistently throughout each page' });\n }\n\n // Disambiguation signals: 30%+ pages: 3 pts\n const disambigRatio = disambiguationCount / totalPages;\n if (disambigRatio >= 0.3) {\n score += 3;\n findings.push({ severity: 'info', detail: `${disambiguationCount} page(s) use disambiguation signals (\"unlike X\", \"compared to\")` });\n } else if (disambiguationCount > 0) {\n score += 1;\n findings.push({ severity: 'low', detail: `${disambiguationCount} page(s) use disambiguation signals`, fix: 'Add \"unlike X\" or \"compared to Y\" to clarify entity boundaries for AI' });\n } else {\n findings.push({ severity: 'low', detail: 'No disambiguation signals found', fix: 'Add \"unlike X\" or \"not to be confused with Y\" to help AI engines distinguish your topics' });\n }\n\n return { criterion: 'entity_disambiguation', criterion_label: 'Entity Disambiguation', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 33: Extraction Friction Score ───────────────────────────────────\n\nfunction checkExtractionFriction(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'extraction_friction', criterion_label: 'Extraction Friction Score', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Avg sentence length in answer paragraphs\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 5);\n const sentenceLengths = sentences.map(s => s.trim().split(/\\s+/).length);\n const avgSentenceLen = sentenceLengths.length > 0 ? sentenceLengths.reduce((a, b) => a + b, 0) / sentenceLengths.length : 0;\n\n if (avgSentenceLen > 0 && avgSentenceLen < 20) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average sentence length: ${avgSentenceLen.toFixed(1)} words - excellent for AI extraction` });\n } else if (avgSentenceLen >= 20 && avgSentenceLen <= 25) {\n score += 2;\n findings.push({ severity: 'info', detail: `Average sentence length: ${avgSentenceLen.toFixed(1)} words - good` });\n } else if (avgSentenceLen > 25 && avgSentenceLen <= 35) {\n score += 1;\n findings.push({ severity: 'low', detail: `Average sentence length: ${avgSentenceLen.toFixed(1)} words - slightly long`, fix: 'Shorten sentences to under 25 words for easier AI extraction' });\n } else if (avgSentenceLen > 35) {\n findings.push({ severity: 'medium', detail: `Average sentence length: ${avgSentenceLen.toFixed(1)} words - too long for AI snippets`, fix: 'Break long sentences into shorter, single-claim statements' });\n }\n\n // Voice-friendly lead: first 2-3 sentences after H1 <75 words, active voice, no parentheticals\n const pages: string[] = [stripScripts(data.homepage.text)];\n if (data.blogSample) {\n for (const p of data.blogSample) pages.push(stripScripts(p.text));\n }\n\n let voiceFriendlyCount = 0;\n for (const pageHtml of pages) {\n const h1Match = pageHtml.match(/<h1[^>]*>[\\s\\S]*?<\\/h1>/i);\n if (!h1Match) continue;\n const afterH1 = pageHtml.slice(pageHtml.indexOf(h1Match[0]) + h1Match[0].length);\n const leadParas = afterH1.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 3) || [];\n const leadText = leadParas.map(p => p.replace(/<[^>]*>/g, '')).join(' ').trim();\n const words = leadText.split(/\\s+/).length;\n const hasParentheticals = /\\([^)]+\\)/.test(leadText);\n if (words <= 75 && words > 10 && !hasParentheticals) {\n voiceFriendlyCount++;\n }\n }\n const voiceFriendlyRatio = voiceFriendlyCount / pages.length;\n if (voiceFriendlyRatio >= 0.7) {\n score += 3;\n findings.push({ severity: 'info', detail: `${Math.round(voiceFriendlyRatio * 100)}% of pages have voice-friendly lead paragraphs` });\n } else if (voiceFriendlyRatio >= 0.3) {\n score += 1;\n findings.push({ severity: 'low', detail: `${Math.round(voiceFriendlyRatio * 100)}% have voice-friendly leads`, fix: 'Keep the first 2-3 sentences under 75 words total, using active voice with no parentheticals' });\n } else {\n findings.push({ severity: 'low', detail: 'Few pages have voice-friendly lead paragraphs', fix: 'Write concise opening paragraphs without parentheticals for voice assistant compatibility' });\n }\n\n // Jargon density: 4+ syllable words in first 500 words without inline definition\n const first500Words = text.split(/\\s+/).slice(0, 500).join(' ');\n const longWords = first500Words.match(/\\b[a-z]{10,}\\b/gi) || [];\n const avgJargon = longWords.length / Math.max(1, pages.length);\n if (avgJargon <= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Low jargon density in opening content' });\n } else if (avgJargon <= 5) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Moderate jargon in opening content', fix: 'Define technical terms inline when first used' });\n } else {\n findings.push({ severity: 'low', detail: 'High jargon density in opening content', fix: 'Replace or define complex terms to reduce extraction friction' });\n }\n\n // Hidden content penalty\n const hasHiddenContent = /display\\s*:\\s*none|visibility\\s*:\\s*hidden|\\bhidden\\b/i.test(combinedHtml) && /<(div|section|p)[^>]*(?:style=[\"'][^\"']*display\\s*:\\s*none|hidden)[^>]*>/i.test(combinedHtml);\n if (hasHiddenContent) {\n score = Math.max(0, score - 2);\n findings.push({ severity: 'medium', detail: 'Hidden content detected (display:none or hidden attribute)', fix: 'Make all content visible - hidden content is not extractable by AI engines' });\n }\n\n return { criterion: 'extraction_friction', criterion_label: 'Extraction Friction Score', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 34: Image Context for AI ────────────────────────────────────────\n\nfunction checkImageContextAI(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'image_context_ai', criterion_label: 'Image Context for AI', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n let score = 0;\n\n // Count images\n const allImages = combinedHtml.match(/<img\\s[^>]*>/gi) || [];\n if (allImages.length === 0) {\n findings.push({ severity: 'low', detail: 'No images found on pages' });\n return { criterion: 'image_context_ai', criterion_label: 'Image Context for AI', score: 5, status: 'partial', findings, fix_priority: 'P3' };\n }\n\n // <figure> + <figcaption>: 50%+ images: 4 pts\n const figureBlocks = combinedHtml.match(/<figure[\\s\\S]*?<\\/figure>/gi) || [];\n const figuresWithCaption = figureBlocks.filter(f => /<figcaption/i.test(f));\n const figureRatio = figuresWithCaption.length / allImages.length;\n if (figureRatio >= 0.5) {\n score += 4;\n findings.push({ severity: 'info', detail: `${figuresWithCaption.length}/${allImages.length} images use <figure> + <figcaption>` });\n } else if (figureRatio >= 0.25) {\n score += 2;\n findings.push({ severity: 'low', detail: `${figuresWithCaption.length}/${allImages.length} images use <figure> + <figcaption>`, fix: 'Wrap more images in <figure> with <figcaption> for AI context' });\n } else {\n findings.push({ severity: 'medium', detail: 'Few or no images use <figure> + <figcaption>', fix: 'Wrap images in <figure> elements with descriptive <figcaption> tags' });\n }\n\n // Alt text quality: alt >5 words, descriptive (not \"image\"/\"photo\")\n let goodAltCount = 0;\n for (const img of allImages) {\n const altMatch = img.match(/\\salt=[\"']([^\"']+)[\"']/i);\n if (altMatch) {\n const altText = altMatch[1].trim();\n const words = altText.split(/\\s+/).length;\n const isGeneric = /^(image|photo|picture|img|icon|logo|banner|screenshot)$/i.test(altText);\n if (words > 5 && !isGeneric) {\n goodAltCount++;\n }\n }\n }\n const altQualityRatio = goodAltCount / allImages.length;\n if (altQualityRatio >= 0.5) {\n score += 3;\n findings.push({ severity: 'info', detail: `${goodAltCount}/${allImages.length} images have descriptive alt text (>5 words)` });\n } else if (altQualityRatio >= 0.25) {\n score += 1;\n findings.push({ severity: 'low', detail: `${goodAltCount}/${allImages.length} images have quality alt text`, fix: 'Write descriptive alt text (>5 words) that explains the image content' });\n } else {\n findings.push({ severity: 'medium', detail: 'Most images lack descriptive alt text', fix: 'Add descriptive alt text (>5 words) to all images, avoiding generic terms like \"image\" or \"photo\"' });\n }\n\n // Contextual placement: images within <article>/<section>\n const contextualImages = combinedHtml.match(/<(article|section)[^>]*>[\\s\\S]*?<img[^>]*>[\\s\\S]*?<\\/\\1>/gi) || [];\n if (contextualImages.length > 0) {\n const contextRatio = Math.min(contextualImages.length, allImages.length) / allImages.length;\n if (contextRatio >= 0.5) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Images are well-placed within semantic content sections' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Some images placed within content sections', fix: 'Place images within <article> or <section> elements for better AI context' });\n }\n } else {\n findings.push({ severity: 'low', detail: 'Images not placed within semantic content sections', fix: 'Move images inside <article> or <section> elements' });\n }\n\n return { criterion: 'image_context_ai', criterion_label: 'Image Context for AI', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 39: Duplicate Content Blocks (intra-page) ─────────────────────\n\ninterface DuplicatePair { headingA: string; headingB: string; similarity: number; sample: string }\n\n/** Find duplicate paragraphs across different sections within a single page. */\nfunction findIntraPageDuplicates(html: string): DuplicatePair[] {\n const sections = extractDuplicateContentSections(html);\n if (sections.length < 2) return [];\n\n const pairs: DuplicatePair[] = [];\n for (let i = 0; i < sections.length; i++) {\n for (let j = i + 1; j < sections.length; j++) {\n let found = false;\n for (const pA of sections[i].paragraphs) {\n if (found) break;\n for (const pB of sections[j].paragraphs) {\n const sim = shingleJaccardSimilarity(pA.shingles, pB.shingles);\n if (sim > 0.4) {\n pairs.push({\n headingA: sections[i].heading,\n headingB: sections[j].heading,\n similarity: Math.round(sim * 100),\n sample: pA.text.slice(0, 80),\n });\n found = true;\n break;\n }\n }\n }\n }\n }\n return pairs;\n}\n\nfunction checkDuplicateContent(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n const pages: Array<{ html: string; url: string }> = [];\n if (data.homepage) {\n pages.push({ html: data.homepage.text, url: data.homepage.finalUrl || `https://${data.domain}/` });\n }\n if (data.blogSample) {\n for (const page of data.blogSample) {\n pages.push({ html: page.text, url: page.finalUrl || '' });\n }\n }\n\n if (pages.length === 0) {\n findings.push({ severity: 'critical', detail: 'No pages available for duplicate content analysis' });\n return { criterion: 'duplicate_content', criterion_label: 'Duplicate Content Blocks', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n // Analyze each page for intra-page duplicates\n let totalDupPages = 0;\n let totalDupPairs = 0;\n const dupDetails: Array<{ url: string; pairs: DuplicatePair[] }> = [];\n\n for (const page of pages) {\n const pairs = findIntraPageDuplicates(page.html);\n if (pairs.length > 0) {\n totalDupPages++;\n totalDupPairs += pairs.length;\n dupDetails.push({ url: page.url, pairs });\n }\n }\n\n const dupRatio = totalDupPages / pages.length;\n\n let score: number;\n if (totalDupPairs === 0) {\n score = 10;\n findings.push({ severity: 'info', detail: `${pages.length} pages analyzed - no duplicate content blocks detected` });\n } else if (dupRatio <= 0.05 && totalDupPairs <= 2) {\n score = 9;\n findings.push({ severity: 'info', detail: `${totalDupPairs} duplicate block pair(s) on ${totalDupPages} page(s) - minor` });\n } else if (dupRatio <= 0.10) {\n score = 7;\n findings.push({ severity: 'low', detail: `${totalDupPairs} duplicate block pair(s) across ${totalDupPages} page(s)`, fix: 'Rewrite duplicate sections to provide unique content in each' });\n } else if (dupRatio <= 0.20) {\n score = 5;\n findings.push({ severity: 'medium', detail: `${totalDupPages} pages (${Math.round(dupRatio * 100)}%) contain duplicate content blocks`, fix: 'Rewrite or remove repeated text blocks - LLMs may flag this as low-quality content' });\n } else if (dupRatio <= 0.40) {\n score = 3;\n findings.push({ severity: 'medium', detail: `${totalDupPages} pages (${Math.round(dupRatio * 100)}%) have significant duplicate content`, fix: 'Widespread duplicate blocks reduce content authority - rewrite each section with unique angles' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${totalDupPages} pages (${Math.round(dupRatio * 100)}%) contain duplicate content blocks`, fix: 'Severe content duplication across the site - LLMs will likely reduce citation authority' });\n }\n\n // Show up to 3 affected pages with section names and sample text\n for (const dup of dupDetails.slice(0, 3)) {\n const shortUrl = dup.url.slice(0, 60);\n for (const pair of dup.pairs.slice(0, 2)) {\n findings.push({\n severity: 'low',\n detail: `${shortUrl}: '${pair.headingA}' and '${pair.headingB}' share ${pair.similarity}% similar text (\"${pair.sample}...\")`,\n fix: `Rewrite one of these sections to eliminate duplicate content`,\n });\n }\n }\n\n return { criterion: 'duplicate_content', criterion_label: 'Duplicate Content Blocks', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 40: Cross-Page Duplicate Content ──────────────────────────────\n\nfunction checkCrossPageDuplication(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n const pages: Array<{ url: string; paragraphs: DuplicateContentParagraph[] }> = [];\n if (data.homepage) {\n pages.push({ url: data.homepage.finalUrl || `https://${data.domain}/`, paragraphs: extractDuplicateContentParagraphs(data.homepage.text) });\n }\n if (data.blogSample) {\n for (const page of data.blogSample) {\n pages.push({ url: page.finalUrl || '', paragraphs: extractDuplicateContentParagraphs(page.text) });\n }\n }\n\n if (pages.length <= 1) {\n findings.push({ severity: 'info', detail: 'Not enough pages to assess cross-page duplication' });\n return { criterion: 'cross_page_duplication', criterion_label: 'Cross-Page Duplicate Content', score: 5, status: 'partial', findings, fix_priority: 'P3' };\n }\n\n // Identify site-wide boilerplate: paragraphs appearing on >40% of pages (by shingle fingerprint)\n // These are template elements (CTAs, bios, footers) — not duplication problems\n const paragraphPageCount = new Map<string, number>();\n for (const page of pages) {\n const seen = new Set<string>();\n for (const p of page.paragraphs) {\n // Use first 5 shingles as fingerprint (fast approximation)\n const fp = [...p.shingles].slice(0, 5).join('|');\n if (!seen.has(fp)) {\n seen.add(fp);\n paragraphPageCount.set(fp, (paragraphPageCount.get(fp) || 0) + 1);\n }\n }\n }\n const boilerplateThreshold = Math.max(3, pages.length * 0.4);\n const siteBoilerprints = new Set<string>();\n for (const [fp, count] of paragraphPageCount) {\n if (count >= boilerplateThreshold) siteBoilerprints.add(fp);\n }\n\n // Compare paragraphs across pages (excluding site boilerplate)\n const crossDupPairs: Array<{ urlA: string; urlB: string; dupCount: number; sample: string }> = [];\n\n for (let i = 0; i < pages.length; i++) {\n for (let j = i + 1; j < pages.length; j++) {\n let dupCount = 0;\n let sample = '';\n for (const pA of pages[i].paragraphs) {\n const fpA = [...pA.shingles].slice(0, 5).join('|');\n if (siteBoilerprints.has(fpA)) continue;\n for (const pB of pages[j].paragraphs) {\n const sim = shingleJaccardSimilarity(pA.shingles, pB.shingles);\n if (sim > 0.4) {\n dupCount++;\n if (!sample) sample = pA.text.slice(0, 80);\n break;\n }\n }\n }\n if (dupCount >= 2) {\n crossDupPairs.push({\n urlA: pages[i].url.slice(0, 60),\n urlB: pages[j].url.slice(0, 60),\n dupCount,\n sample,\n });\n }\n }\n }\n\n // Count unique URLs involved\n const affectedUrls = new Set<string>();\n for (const pair of crossDupPairs) {\n affectedUrls.add(pair.urlA);\n affectedUrls.add(pair.urlB);\n }\n const affectedRatio = pages.length > 0 ? affectedUrls.size / pages.length : 0;\n const totalDupParagraphs = crossDupPairs.reduce((s, p) => s + p.dupCount, 0);\n\n let score: number;\n if (crossDupPairs.length === 0) {\n score = 10;\n findings.push({ severity: 'info', detail: `${pages.length} pages analyzed - no cross-page content duplication detected` });\n } else if (affectedRatio <= 0.05 && totalDupParagraphs <= 4) {\n score = 9;\n findings.push({ severity: 'info', detail: `${totalDupParagraphs} shared paragraph(s) across ${affectedUrls.size} page(s) - minor` });\n } else if (affectedRatio <= 0.10) {\n score = 7;\n findings.push({ severity: 'low', detail: `${totalDupParagraphs} shared paragraphs across ${affectedUrls.size} pages`, fix: 'Rewrite shared content so each page provides a unique perspective' });\n } else if (affectedRatio <= 0.20) {\n score = 5;\n findings.push({ severity: 'medium', detail: `${affectedUrls.size} pages (${Math.round(affectedRatio * 100)}%) share duplicate paragraphs`, fix: 'Significant cross-page duplication - AI engines may only index one version' });\n } else if (affectedRatio <= 0.40) {\n score = 3;\n findings.push({ severity: 'medium', detail: `${affectedUrls.size} pages (${Math.round(affectedRatio * 100)}%) contain shared content blocks`, fix: 'Widespread copy-paste content across pages reduces overall site authority' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${affectedUrls.size} pages (${Math.round(affectedRatio * 100)}%) share duplicate content`, fix: 'Severe cross-page duplication - AI engines will likely ignore redundant pages entirely' });\n }\n\n // Show up to 3 cross-page duplicate pairs with sample text\n for (const pair of crossDupPairs.slice(0, 3)) {\n findings.push({\n severity: 'low',\n detail: `${pair.dupCount} shared paragraph(s): ${pair.urlA} ↔ ${pair.urlB} (\"${pair.sample}...\")`,\n fix: 'Rewrite shared paragraphs so each page has unique content',\n });\n }\n\n return { criterion: 'cross_page_duplication', criterion_label: 'Cross-Page Duplicate Content', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Main audit function ────────────────────────────────────────────────────\n\n/**\n * Run all 40 criteria checks using pre-fetched site data.\n * All functions are synchronous (no HTTP calls) - data was already fetched.\n */\nexport function auditSiteFromData(data: SiteData): CriterionResult[] {\n // Compute Topic Coherence first - used to cap Content Cannibalization\n const topicCoherence = checkTopicCoherence(data);\n const cannibalization = checkContentCannibalization(data, topicCoherence.score);\n\n return [\n checkLlmsTxt(data),\n checkSchemaMarkup(data),\n checkQAFormat(data),\n checkCleanHTML(data),\n checkEntityConsistency(data),\n checkRobotsTxt(data),\n checkFAQSection(data),\n checkOriginalData(data),\n checkInternalLinking(data),\n checkSemanticHTML(data),\n checkContentFreshness(data),\n checkSitemapCompleteness(data),\n checkRssFeed(data),\n checkTableListExtractability(data),\n checkDefinitionPatterns(data),\n checkDirectAnswerDensity(data),\n checkContentLicensing(data),\n checkAuthorSchemaDepth(data),\n checkFactDensity(data),\n checkCanonicalUrl(data),\n checkContentVelocity(data),\n checkSchemaCoverage(data),\n checkSpeakableSchema(data),\n checkQueryAnswerAlignment(data),\n cannibalization,\n checkVisibleDateSignal(data),\n topicCoherence,\n checkContentDepth(data, topicCoherence.score),\n // Helpful-content criteria (#29-#32)\n checkHelpfulPurposeAlignment(data),\n checkFirstHandExperienceSignals(data),\n checkCreatorTransparency(data),\n checkMethodologyTransparency(data),\n // V2 criteria (#33-#38)\n checkCitationReadyWriting(data),\n checkAnswerFirstPlacement(data),\n checkEvidencePackaging(data),\n checkEntityDisambiguation(data),\n checkExtractionFriction(data),\n checkImageContextAI(data),\n // V3 criteria (#39-#40)\n checkDuplicateContent(data),\n checkCrossPageDuplication(data),\n ];\n}\n\n/**\n * Legacy entry point: fetches data and runs all checks.\n * Used by analyzer.ts for the /api/aeo/analyze endpoint.\n */\nexport async function auditSite(targetUrl: string): Promise<CriterionResult[]> {\n const url = new URL(targetUrl.startsWith('http') ? targetUrl : `https://${targetUrl}`);\n const domain = url.hostname.replace(/^www\\./, '');\n const data = await prefetchSiteData(domain);\n return auditSiteFromData(data);\n}\n","import type { CriterionResult } from './site-crawler.js';\n\nexport const WEIGHTS: Record<string, number> = {\n // ─── Content Substance (~55%) ─────────────────────────────────────────────\n // WHY an AI engine would cite you. These drive citation quality directly.\n topic_coherence: 0.14, // Topical authority - THE gating signal\n original_data: 0.10, // Unique value AI can't find elsewhere\n content_depth: 0.07, // Comprehensive vs thin coverage\n fact_density: 0.06, // Information density per page\n direct_answer_density: 0.05, // Direct answers to queries\n qa_content_format: 0.04, // Answer-shaped content structure\n query_answer_alignment: 0.04, // Relevance to actual AI queries\n faq_section: 0.03, // Structured Q&A pairs\n helpful_purpose_alignment: 0.03, // Visitor-helpful vs search-first framing\n first_hand_experience_signals: 0.03, // Evidence of real use or observation\n\n // ─── Content Organization (~30%) ──────────────────────────────────────────\n // HOW easily AI engines can extract and trust your content.\n entity_consistency: 0.05, // Brand authority and E-E-A-T\n internal_linking: 0.04, // Site structure and topic clusters\n content_freshness: 0.04, // Recency signals\n schema_markup: 0.03, // Structured data for discovery\n author_schema_depth: 0.03, // Expert attribution\n table_list_extractability: 0.03, // Extractable structured data\n creator_transparency: 0.02, // Visible author/reviewer clarity\n methodology_transparency: 0.02, // Process disclosure\n definition_patterns: 0.015, // Clear definitions\n visible_date_signal: 0.015, // Publication date trust\n semantic_html: 0.02, // Clean semantic structure\n clean_html: 0.02, // Parseable markup\n\n // ─── Technical Plumbing (~15%) ────────────────────────────────────────────\n // WHETHER AI crawlers can find you. Table stakes with diminishing returns.\n content_cannibalization: 0.02,\n llms_txt: 0.01,\n robots_txt: 0.01,\n content_velocity: 0.02,\n content_licensing: 0.01,\n sitemap_completeness: 0.01,\n canonical_url: 0.005,\n rss_feed: 0.00,\n schema_coverage: 0.00,\n speakable_schema: 0.00,\n\n // ─── V2 Criteria (~15%) ───────────────────────────────────────────────────\n // Citation quality, evidence packaging, and extraction friction.\n citation_ready_writing: 0.04, // Self-contained quotable sentences\n answer_first_placement: 0.03, // Answer block in first 300 words\n evidence_packaging: 0.03, // Inline citations and attribution\n entity_disambiguation: 0.02, // Clear entity boundaries\n extraction_friction: 0.02, // Sentence length, voice, jargon\n image_context_ai: 0.005, // Figure/figcaption, alt text quality\n\n // ─── V3 Criteria ────────────────────────────────────────────────────────\n duplicate_content: 0.05, // Duplicate text blocks within pages\n cross_page_duplication: 0.03, // Same paragraphs copied across pages\n};\n\nexport function calculateOverallScore(criteria: CriterionResult[]): number {\n let totalWeight = 0;\n let weightedSum = 0;\n\n for (const c of criteria) {\n const weight = WEIGHTS[c.criterion] ?? 0.05;\n weightedSum += (c.score / 10) * weight * 100;\n totalWeight += weight;\n }\n\n if (totalWeight === 0) return 0;\n\n let score = Math.round(weightedSum / totalWeight);\n\n // ─── Coherence Gate ─────────────────────────────────────────────────────\n // Topic coherence is a multiplicative signal, not just additive. A site\n // with scattered, unfocused content cannot achieve a high AEO score\n // regardless of technical perfection - AI engines won't build topical\n // trust from a grab-bag of unrelated posts.\n //\n // Thresholds map to score tiers:\n // coherence 0-1: max 35-40 (Critical gaps)\n // coherence 2-4: max 45-55 (Below average)\n // coherence 5: max 60 (Moderate ceiling)\n // coherence ≥ 6: no cap\n const coherence = criteria.find(c => c.criterion === 'topic_coherence');\n if (coherence && coherence.score < 6) {\n const cap = 35 + coherence.score * 5;\n score = Math.min(score, cap);\n }\n\n return score;\n}\n","/**\n * V2 Pillar Framework — 5-pillar scoring model.\n * Maps all 40 criteria into pillars, computes sub-scores,\n * provides client-friendly names, and calculates top-3 fixes.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface PillarScores {\n answerReadiness: number; // ~40%\n contentStructure: number; // ~25%\n trustAuthority: number; // ~15%\n technicalFoundation: number; // ~10%\n aiDiscovery: number; // ~10%\n}\n\nexport interface TopFix {\n criterion: string;\n clientName: string;\n description: string;\n impactPoints: number; // (10 - score) * weight * 100\n effort: 'Low' | 'Medium' | 'High';\n}\n\n// ─── Pillar definitions ─────────────────────────────────────────────────────\n\nexport type PillarName = 'Answer Readiness' | 'Content Structure' | 'Trust & Authority' | 'Technical Foundation' | 'AI Discovery';\n\nexport const PILLARS: Record<PillarName, string[]> = {\n 'Answer Readiness': [\n 'topic_coherence', 'original_data', 'content_depth', 'fact_density',\n 'citation_ready_writing', 'answer_first_placement', 'evidence_packaging',\n 'helpful_purpose_alignment', 'first_hand_experience_signals',\n 'duplicate_content', 'cross_page_duplication',\n ],\n 'Content Structure': [\n 'direct_answer_density', 'qa_content_format', 'query_answer_alignment',\n 'faq_section', 'table_list_extractability', 'definition_patterns', 'entity_disambiguation',\n ],\n 'Trust & Authority': [\n 'entity_consistency', 'internal_linking', 'content_freshness',\n 'author_schema_depth', 'schema_markup', 'creator_transparency',\n 'methodology_transparency',\n ],\n 'Technical Foundation': [\n 'semantic_html', 'clean_html', 'visible_date_signal',\n 'extraction_friction', 'image_context_ai', 'schema_coverage', 'speakable_schema',\n ],\n 'AI Discovery': [\n 'content_cannibalization', 'llms_txt', 'robots_txt', 'content_velocity',\n 'content_licensing', 'canonical_url', 'sitemap_completeness', 'rss_feed',\n ],\n};\n\n// ─── Client-friendly names ──────────────────────────────────────────────────\n\nexport const CLIENT_NAMES: Record<string, string> = {\n topic_coherence: 'Topical Authority',\n original_data: 'Original Research & Data',\n content_depth: 'Content Depth',\n fact_density: 'Fact & Data Density',\n citation_ready_writing: 'Citation-Ready Writing',\n answer_first_placement: 'Answer-First Placement',\n evidence_packaging: 'Evidence Packaging',\n helpful_purpose_alignment: 'Helpful Purpose Alignment',\n first_hand_experience_signals: 'First-Hand Experience Signals',\n direct_answer_density: 'Direct Answer Density',\n qa_content_format: 'Q&A Content Format',\n query_answer_alignment: 'Query-Answer Alignment',\n faq_section: 'FAQ Section',\n table_list_extractability: 'Tables & Lists',\n definition_patterns: 'Definition Patterns',\n entity_disambiguation: 'Entity Disambiguation',\n entity_consistency: 'Entity & Brand Authority',\n internal_linking: 'Internal Linking',\n content_freshness: 'Content Freshness',\n author_schema_depth: 'Author & Expert Schema',\n schema_markup: 'Schema Markup',\n creator_transparency: 'Creator Transparency',\n methodology_transparency: 'Methodology Transparency',\n semantic_html: 'Semantic HTML',\n clean_html: 'Clean HTML',\n visible_date_signal: 'Visible Date Signal',\n extraction_friction: 'Extraction Friction',\n image_context_ai: 'Image Context for AI',\n schema_coverage: 'Schema Coverage',\n speakable_schema: 'Speakable Schema',\n duplicate_content: 'Duplicate Content Blocks',\n cross_page_duplication: 'Cross-Page Duplicate Content',\n content_cannibalization: 'Content Cannibalization',\n llms_txt: 'llms.txt File',\n robots_txt: 'robots.txt for AI',\n content_velocity: 'Publishing Velocity',\n content_licensing: 'Content Licensing',\n canonical_url: 'Canonical URLs',\n sitemap_completeness: 'Sitemap Completeness',\n rss_feed: 'RSS/Atom Feed',\n};\n\n// ─── Pillar weights for sub-score computation ───────────────────────────────\n\nconst PILLAR_WEIGHTS: Record<string, number> = {\n topic_coherence: 0.14, original_data: 0.10, content_depth: 0.07,\n fact_density: 0.06, citation_ready_writing: 0.04, answer_first_placement: 0.03,\n evidence_packaging: 0.03, helpful_purpose_alignment: 0.03,\n first_hand_experience_signals: 0.03, duplicate_content: 0.05, cross_page_duplication: 0.03,\n direct_answer_density: 0.05, qa_content_format: 0.04, query_answer_alignment: 0.04,\n faq_section: 0.03, table_list_extractability: 0.03, definition_patterns: 0.015,\n entity_disambiguation: 0.02,\n entity_consistency: 0.05, internal_linking: 0.04, content_freshness: 0.04,\n author_schema_depth: 0.03, schema_markup: 0.03, creator_transparency: 0.02,\n methodology_transparency: 0.02, semantic_html: 0.02, clean_html: 0.02,\n visible_date_signal: 0.015, extraction_friction: 0.02, image_context_ai: 0.005,\n schema_coverage: 0.00, speakable_schema: 0.00,\n content_cannibalization: 0.02, llms_txt: 0.01, robots_txt: 0.01,\n content_velocity: 0.02, content_licensing: 0.01, canonical_url: 0.005,\n sitemap_completeness: 0.01, rss_feed: 0.00,\n};\n\n// ─── Effort mapping per criterion ───────────────────────────────────────────\n\nconst CRITERION_EFFORT: Record<string, TopFix['effort']> = {\n topic_coherence: 'High', original_data: 'High', content_depth: 'Medium',\n fact_density: 'Medium', citation_ready_writing: 'Medium', answer_first_placement: 'Medium',\n evidence_packaging: 'Medium', helpful_purpose_alignment: 'Medium',\n first_hand_experience_signals: 'Medium', duplicate_content: 'Medium', cross_page_duplication: 'Medium',\n direct_answer_density: 'Medium', qa_content_format: 'Medium', query_answer_alignment: 'Medium',\n faq_section: 'Medium', table_list_extractability: 'Medium', definition_patterns: 'Low',\n entity_disambiguation: 'Medium',\n entity_consistency: 'Low', internal_linking: 'Medium', content_freshness: 'Low',\n author_schema_depth: 'Low', schema_markup: 'Medium', creator_transparency: 'Low',\n methodology_transparency: 'Low',\n semantic_html: 'Low', clean_html: 'Medium', visible_date_signal: 'Low',\n extraction_friction: 'Medium', image_context_ai: 'Low', schema_coverage: 'Medium',\n speakable_schema: 'Low',\n content_cannibalization: 'Medium', llms_txt: 'Low', robots_txt: 'Low',\n content_velocity: 'High', content_licensing: 'Low', canonical_url: 'Low',\n sitemap_completeness: 'Low', rss_feed: 'Low',\n};\n\n// ─── Fix descriptions ───────────────────────────────────────────────────────\n\nconst FIX_DESCRIPTIONS: Record<string, string> = {\n topic_coherence: 'Focus blog content on 2-3 core expertise areas instead of scattered topics.',\n original_data: 'Publish original research, case studies, or proprietary data that AI engines can cite.',\n content_depth: 'Expand articles to 1000+ words with structured H2/H3 sections and expert analysis.',\n fact_density: 'Add specific numbers, percentages, and statistics throughout content.',\n citation_ready_writing: 'Write self-contained definition sentences and one-claim statements AI can quote directly.',\n answer_first_placement: 'Place a 40-80 word answer block in the first 300 words of each page.',\n evidence_packaging: 'Add inline citations, attribution phrases, and a sources section to key pages.',\n helpful_purpose_alignment: 'Lead with useful, task-solving guidance instead of search-first filler.',\n first_hand_experience_signals: 'Add concrete signs of first-hand use, testing, implementation, or observation.',\n direct_answer_density: 'Write concise 2-3 sentence answer paragraphs after each question heading.',\n qa_content_format: 'Add question-based H2/H3 headings matching common AI queries.',\n query_answer_alignment: 'Ensure every question heading is followed by a direct answer paragraph.',\n faq_section: 'Create a dedicated FAQ page with FAQPage schema markup.',\n table_list_extractability: 'Add comparison tables with headers and structured lists.',\n definition_patterns: 'Include clear \"X is defined as...\" patterns for key terms.',\n entity_disambiguation: 'Define the primary entity early and use consistent terminology.',\n entity_consistency: 'Add Organization schema with consistent name, address, and sameAs links.',\n internal_linking: 'Strengthen internal linking with descriptive anchor text between related pages.',\n content_freshness: 'Add dateModified schema and visible last-updated dates.',\n author_schema_depth: 'Add Person schema for authors with credentials and sameAs links.',\n schema_markup: 'Implement JSON-LD structured data on key pages.',\n creator_transparency: 'Show clear visible bylines, author pages, and reviewer details where readers expect them.',\n methodology_transparency: 'Explain how content was tested, researched, reviewed, or updated.',\n semantic_html: 'Use semantic HTML5 elements (main, article, nav, header, footer).',\n clean_html: 'Fix HTML structure, add meta tags, and ensure HTTPS.',\n visible_date_signal: 'Display dates using <time> elements and add datePublished to JSON-LD.',\n extraction_friction: 'Shorten sentences, use active voice, and avoid jargon in lead paragraphs.',\n image_context_ai: 'Wrap images in <figure>/<figcaption> with descriptive alt text.',\n schema_coverage: 'Extend structured data to inner pages (articles, services, products).',\n speakable_schema: 'Add SpeakableSpecification schema for voice assistant compatibility.',\n duplicate_content: 'Rewrite duplicate text blocks so each section provides unique value.',\n cross_page_duplication: 'Rewrite shared paragraphs across pages so each page has unique content.',\n content_cannibalization: 'Consolidate overlapping pages or differentiate titles and H1 headings.',\n llms_txt: 'Create a /llms.txt file describing your site for AI engines.',\n robots_txt: 'Update robots.txt to explicitly allow AI crawlers.',\n content_velocity: 'Establish a regular publishing cadence with dated sitemap entries.',\n content_licensing: 'Create /ai.txt to declare AI usage permissions.',\n canonical_url: 'Add rel=\"canonical\" tags to all pages.',\n sitemap_completeness: 'Generate a comprehensive sitemap.xml with lastmod dates.',\n rss_feed: 'Add an RSS feed linked from your homepage.',\n};\n\n// ─── Reverse lookup: criterion → pillar ─────────────────────────────────────\n\nconst CRITERION_PILLAR_MAP: Record<string, PillarName> = {};\nfor (const [pillar, criteria] of Object.entries(PILLARS)) {\n for (const c of criteria) {\n CRITERION_PILLAR_MAP[c] = pillar as PillarName;\n }\n}\n\nexport function getCriterionPillar(criterionId: string): string {\n return CRITERION_PILLAR_MAP[criterionId] || 'Unknown';\n}\n\n// ─── Pillar sub-score computation ───────────────────────────────────────────\n\nexport function computePillarScores(criteria: CriterionResult[]): PillarScores {\n const criteriaMap = new Map(criteria.map(c => [c.criterion, c]));\n\n function pillarScore(pillarCriteria: string[]): number {\n let totalWeight = 0;\n let weightedSum = 0;\n\n for (const id of pillarCriteria) {\n const c = criteriaMap.get(id);\n const weight = PILLAR_WEIGHTS[id] ?? 0.02;\n if (c) {\n weightedSum += (c.score / 10) * weight * 100;\n totalWeight += weight;\n }\n }\n\n return totalWeight === 0 ? 0 : Math.round(weightedSum / totalWeight);\n }\n\n return {\n answerReadiness: pillarScore(PILLARS['Answer Readiness']),\n contentStructure: pillarScore(PILLARS['Content Structure']),\n trustAuthority: pillarScore(PILLARS['Trust & Authority']),\n technicalFoundation: pillarScore(PILLARS['Technical Foundation']),\n aiDiscovery: pillarScore(PILLARS['AI Discovery']),\n };\n}\n\n// ─── Top fixes calculation ──────────────────────────────────────────────────\n\nexport function computeTopFixes(criteria: CriterionResult[], count = 3): TopFix[] {\n const fixes: TopFix[] = [];\n\n for (const c of criteria) {\n if (c.score >= 8) continue; // No fix needed\n const weight = PILLAR_WEIGHTS[c.criterion] ?? 0.02;\n const impactPoints = Math.round((10 - c.score) * weight * 100);\n if (impactPoints <= 0) continue;\n\n fixes.push({\n criterion: c.criterion,\n clientName: CLIENT_NAMES[c.criterion] || c.criterion_label,\n description: FIX_DESCRIPTIONS[c.criterion] || `Improve ${c.criterion_label}`,\n impactPoints,\n effort: CRITERION_EFFORT[c.criterion] || 'Medium',\n });\n }\n\n // Sort by impact descending\n fixes.sort((a, b) => b.impactPoints - a.impactPoints);\n\n return fixes.slice(0, count);\n}\n","/**\n * Shared scorecard building functions.\n * Extracted from cli/pre-crawl.ts for reuse in instant-audit and other consumers.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\nimport type { Status, FindingType, Severity, ScoreCardItem, CriterionDetail, DetailedFinding } from './types.js';\nimport type { AuditFinding } from './types.js';\nimport { getCriterionPillar, CLIENT_NAMES } from './pillars.js';\nimport { WEIGHTS } from './scoring.js';\n\n// ─── Criterion label mapping (site-crawler labels -> prompt-standard names) ──\n\nexport const CRITERION_LABELS: Record<string, string> = {\n 'llms.txt File': 'llms.txt File',\n 'Schema.org Structured Data': 'Schema.org Structured Data',\n 'Q&A Content Format': 'Q&A Content Format',\n 'Clean, Crawlable HTML': 'Clean, Crawlable HTML',\n 'Entity Authority & E-E-A-T': 'Entity Authority & NAP Consistency',\n 'robots.txt for AI Crawlers': 'robots.txt for AI Crawlers',\n 'Comprehensive FAQ Sections': 'Comprehensive FAQ Section',\n 'Original Data & Expert Content': 'Original Data & Expert Analysis',\n 'Internal Linking Architecture': 'Internal Linking Structure',\n 'Semantic HTML5 & Accessibility': 'Semantic HTML5 & Accessibility',\n 'Content Freshness Signals': 'Content Freshness Signals',\n 'Sitemap Completeness': 'Sitemap Completeness',\n 'RSS/Atom Feed': 'RSS/Atom Feed',\n 'Table & List Extractability': 'Table & List Extractability',\n 'Definition Patterns': 'Definition Patterns',\n 'Direct Answer Paragraphs': 'Direct Answer Paragraphs',\n 'Content Licensing & AI Permissions': 'Content Licensing & AI Permissions',\n 'Author & Expert Schema': 'Author & Expert Schema',\n 'Fact & Data Density': 'Fact & Data Density',\n 'Canonical URL Strategy': 'Canonical URL Strategy',\n 'Content Publishing Velocity': 'Content Publishing Velocity',\n 'Schema Coverage & Depth': 'Schema Coverage & Depth',\n 'Speakable Schema': 'Speakable Schema',\n 'Query-Answer Alignment': 'Query-Answer Alignment',\n 'Content Cannibalization': 'Content Cannibalization',\n 'Visible Date Signal': 'Visible Date Signal',\n 'Topic Coherence': 'Topic Coherence',\n 'Content Depth': 'Content Depth',\n 'Helpful Purpose Alignment': 'Helpful Purpose Alignment',\n 'First-Hand Experience Signals': 'First-Hand Experience Signals',\n 'Creator Transparency': 'Creator Transparency',\n 'Methodology Transparency': 'Methodology Transparency',\n 'Citation-Ready Writing Quality': 'Citation-Ready Writing Quality',\n 'Answer-First Placement': 'Answer-First Placement',\n 'Evidence Packaging': 'Evidence Packaging',\n 'Entity Disambiguation': 'Entity Disambiguation',\n 'Extraction Friction Score': 'Extraction Friction Score',\n 'Image Context for AI': 'Image Context for AI',\n 'Duplicate Content Blocks': 'Duplicate Content Blocks',\n 'Cross-Page Duplicate Content': 'Cross-Page Duplicate Content',\n};\n\n// ─── Score to Status mapping (matches AI prompt: 0=MISSING...8-10=STRONG) ────\n\nexport function scoreToStatus(score: number): Status {\n if (score === 0) return 'MISSING';\n if (score === 1) return 'NEARLY EMPTY';\n if (score === 2) return 'POOR';\n if (score === 3) return 'WEAK';\n if (score <= 5) return 'PARTIAL';\n if (score === 6) return 'MODERATE';\n if (score === 7) return 'GOOD';\n return 'STRONG';\n}\n\n// ─── Finding severity mapping (site-crawler -> audit format) ─────────────────\n\nexport function mapFindingSeverity(severity: AuditFinding['severity']): Severity {\n switch (severity) {\n case 'critical': return 'CRITICAL';\n case 'high': return 'MISSING';\n case 'medium': return 'ADD';\n case 'low': return 'PARTIAL';\n case 'info': return 'WORKING';\n default: return 'PARTIAL';\n }\n}\n\nexport function mapFindingType(severity: AuditFinding['severity'], hasFix: boolean): FindingType {\n if (severity === 'info') return 'Good';\n if (severity === 'critical') return 'Critical';\n if (severity === 'high') return 'Missing';\n if (hasFix) return 'Issue';\n return 'Note';\n}\n\n// ─── Convert CriterionResult to scorecard + detailedFindings format ──────────\n\nexport function buildScorecard(results: CriterionResult[]): ScoreCardItem[] {\n return results.map((r, i) => {\n const label = CRITERION_LABELS[r.criterion_label] || r.criterion_label;\n\n // Build keyFindings from the most important findings (2-3 sentences)\n const keyParts: string[] = [];\n for (const f of r.findings) {\n if (keyParts.length >= 3) break;\n keyParts.push(f.detail);\n }\n const keyFindings = keyParts.join('. ') + (keyParts.length > 0 && !keyParts[keyParts.length - 1].endsWith('.') ? '.' : '');\n\n return {\n id: i + 1,\n criterion: label,\n score: r.score,\n status: scoreToStatus(r.score),\n keyFindings,\n pillar: getCriterionPillar(r.criterion),\n clientName: CLIENT_NAMES[r.criterion] || label,\n weight: WEIGHTS[r.criterion],\n };\n });\n}\n\nexport function buildDetailedFindings(results: CriterionResult[]): CriterionDetail[] {\n return results.map((r, i) => {\n const label = CRITERION_LABELS[r.criterion_label] || r.criterion_label;\n\n const rawFindings: DetailedFinding[] = r.findings.map(f => ({\n type: mapFindingType(f.severity, !!f.fix),\n description: f.fix ? `${f.detail}. ${f.fix}` : f.detail,\n severity: mapFindingSeverity(f.severity),\n }));\n\n // Deduplicate findings by description\n const seen = new Set<string>();\n const findings: DetailedFinding[] = [];\n for (const f of rawFindings) {\n if (!seen.has(f.description)) {\n seen.add(f.description);\n findings.push(f);\n }\n }\n\n // Fallback: if a criterion somehow has fewer than 2 findings, add a single specific one\n if (findings.length < 2) {\n if (r.score >= 7) {\n findings.push({ type: 'Good', description: `${label} is well-implemented for AI engine visibility.`, severity: 'WORKING' });\n } else {\n findings.push({ type: 'Note', description: `${label} needs improvement - review specific issues above.`, severity: 'PARTIAL' });\n }\n }\n\n return {\n id: i + 1,\n name: label,\n findings,\n };\n });\n}\n","/**\n * Deterministic narrative generation from scorecard data.\n * Produces verdict, opportunities, pitchNumbers, and bottomLine\n * without any LLM calls - pure template-based generation.\n */\n\nimport type { ScoreCardItem, Deliverable, PitchMetric, ImpactLevel } from './types.js';\nimport type { CriterionResult } from './site-crawler.js';\nimport type { RawDataSummary } from './site-crawler.js';\n\n// ─── Scoring weights (mirrored from scoring.ts for impact calculation) ───────\n\nconst CRITERION_WEIGHTS: Record<string, number> = {\n // Content Substance (~55%)\n topic_coherence: 0.14,\n original_data: 0.10,\n content_depth: 0.07,\n fact_density: 0.06,\n direct_answer_density: 0.05,\n qa_content_format: 0.04,\n query_answer_alignment: 0.04,\n faq_section: 0.03,\n helpful_purpose_alignment: 0.03,\n first_hand_experience_signals: 0.03,\n // Content Organization (~30%)\n entity_consistency: 0.05,\n internal_linking: 0.04,\n content_freshness: 0.04,\n schema_markup: 0.03,\n author_schema_depth: 0.03,\n table_list_extractability: 0.03,\n creator_transparency: 0.02,\n methodology_transparency: 0.02,\n definition_patterns: 0.015,\n visible_date_signal: 0.015,\n semantic_html: 0.02,\n clean_html: 0.02,\n // Technical Plumbing (~15%)\n content_cannibalization: 0.02,\n llms_txt: 0.01,\n robots_txt: 0.01,\n content_velocity: 0.02,\n content_licensing: 0.01,\n sitemap_completeness: 0.01,\n canonical_url: 0.005,\n rss_feed: 0.00,\n schema_coverage: 0.00,\n speakable_schema: 0.00,\n // V2 Criteria (~15%)\n citation_ready_writing: 0.04,\n answer_first_placement: 0.03,\n evidence_packaging: 0.03,\n entity_disambiguation: 0.02,\n extraction_friction: 0.02,\n image_context_ai: 0.005,\n // V3 Criteria\n duplicate_content: 0.05,\n cross_page_duplication: 0.03,\n};\n\n// ─── Opportunity templates ───────────────────────────────────────────────────\n\ninterface OpportunityTemplate {\n name: string;\n effort: string;\n description: string;\n}\n\nconst OPPORTUNITY_TEMPLATES: Record<string, OpportunityTemplate> = {\n llms_txt: {\n name: 'Create llms.txt File',\n effort: 'Low',\n description: 'Add a /llms.txt file that describes your site, core services, and key pages in markdown format. This helps AI engines like ChatGPT and Claude understand your site structure and content offerings.',\n },\n schema_markup: {\n name: 'Add Schema.org Structured Data',\n effort: 'Medium',\n description: 'Implement JSON-LD structured data (Organization, Service, Product, FAQPage) on key pages. Schema markup helps AI engines extract and cite your content accurately.',\n },\n qa_content_format: {\n name: 'Restructure Content as Q&A',\n effort: 'Medium',\n description: 'Add question-based headings (H2/H3) throughout your content. Use \"What is...\", \"How does...\", \"Why should...\" patterns that match how users query AI assistants.',\n },\n clean_html: {\n name: 'Fix HTML Structure & Enable HTTPS',\n effort: 'Medium',\n description: 'Ensure clean, well-structured HTML with proper meta tags, semantic elements, and HTTPS. Clean HTML makes your content more parseable by AI crawlers.',\n },\n entity_consistency: {\n name: 'Strengthen Entity Authority (NAP)',\n effort: 'Low',\n description: 'Add Organization schema with consistent name, address, phone (NAP). Include sameAs links to social profiles and authoritative directories to strengthen entity recognition.',\n },\n robots_txt: {\n name: 'Configure robots.txt for AI Crawlers',\n effort: 'Low',\n description: 'Update robots.txt to explicitly allow AI crawlers (GPTBot, ClaudeBot, PerplexityBot). Add a Sitemap directive to help crawlers discover your content.',\n },\n faq_section: {\n name: 'Build Comprehensive FAQ Section',\n effort: 'Medium',\n description: 'Create a dedicated FAQ page with FAQPage schema markup. Cover common questions about your products, services, and industry to become a direct answer source for AI engines.',\n },\n helpful_purpose_alignment: {\n name: 'Improve Helpful Purpose Alignment',\n effort: 'Medium',\n description: 'Rewrite pages to solve the visitor task quickly and concretely. Reduce generic intros, search-first filler, and CTA interruptions before the first useful answer.',\n },\n first_hand_experience_signals: {\n name: 'Add First-Hand Experience Signals',\n effort: 'Medium',\n description: 'Show direct use, testing, implementation, or lived experience with concrete observations, examples, screenshots, and lessons learned.',\n },\n original_data: {\n name: 'Add Original Data & Case Studies',\n effort: 'High',\n description: 'Publish original research, statistics, case studies, or proprietary data that AI engines can cite. Unique data points make your content a primary source rather than a derivative one.',\n },\n internal_linking: {\n name: 'Improve Internal Linking Architecture',\n effort: 'Medium',\n description: 'Strengthen internal linking with descriptive anchor text between related pages. Add breadcrumb navigation and ensure every key page is reachable within 3 clicks from the homepage.',\n },\n semantic_html: {\n name: 'Implement Semantic HTML5',\n effort: 'Low',\n description: 'Use semantic HTML5 elements (main, article, nav, header, footer, section) to give AI parsers clear content structure. Add lang attribute and ARIA labels for accessibility.',\n },\n content_freshness: {\n name: 'Add Content Freshness Signals',\n effort: 'Low',\n description: 'Include dateModified schema, visible last-updated dates, and time elements on content pages. Fresh content signals help AI engines prioritize your pages over stale alternatives.',\n },\n sitemap_completeness: {\n name: 'Create Complete Sitemap',\n effort: 'Low',\n description: 'Generate a comprehensive sitemap.xml with lastmod dates for all important pages. A complete sitemap ensures AI crawlers can discover and prioritize your full content library.',\n },\n rss_feed: {\n name: 'Deploy RSS/Atom Feed',\n effort: 'Low',\n description: 'Add an RSS or Atom feed linked from your homepage. Feeds signal active content publishing and give AI engines a structured way to track your latest content.',\n },\n table_list_extractability: {\n name: 'Add Structured Tables & Lists',\n effort: 'Medium',\n description: 'Use HTML tables for comparison data and ordered/unordered lists for features, steps, and specifications. Structured data formats are directly extractable by AI engines for answers.',\n },\n definition_patterns: {\n name: 'Add Definition-Style Content',\n effort: 'Low',\n description: 'Include clear definition patterns (\"X refers to...\", \"X is defined as...\") for key terms and concepts. Definition-style content is highly citable by AI engines answering \"what is\" queries.',\n },\n direct_answer_density: {\n name: 'Add Direct Answer Paragraphs',\n effort: 'Medium',\n description: 'Write concise, standalone answer paragraphs (2-3 sentences) immediately after question headings. These \"snippet-ready\" paragraphs are ideal for AI engine citations.',\n },\n content_licensing: {\n name: 'Add Content Licensing & ai.txt',\n effort: 'Low',\n description: 'Create an /ai.txt file specifying AI usage permissions and add license schema to your structured data. Clear licensing signals help AI engines understand how they can use your content.',\n },\n author_schema_depth: {\n name: 'Enhance Author & Expert Schema',\n effort: 'Low',\n description: 'Add Person schema for content authors with credentials, expertise, and sameAs links. Expert attribution strengthens E-E-A-T signals that AI engines use to evaluate source credibility.',\n },\n creator_transparency: {\n name: 'Improve Creator Transparency',\n effort: 'Low',\n description: 'Add visible bylines, author pages, and reviewer/editor details so readers can clearly tell who created the content and why they are credible.',\n },\n methodology_transparency: {\n name: 'Add Methodology Transparency',\n effort: 'Low',\n description: 'Explain how pages were tested, researched, reviewed, or updated. Add methodology, criteria, sample-size, or review-process details where users expect them.',\n },\n fact_density: {\n name: 'Increase Fact & Data Density',\n effort: 'Medium',\n description: 'Add specific numbers, percentages, statistics, and data points throughout your content. Fact-dense content gives AI engines concrete data to cite rather than vague claims.',\n },\n canonical_url: {\n name: 'Fix Canonical URL Strategy',\n effort: 'Low',\n description: 'Add rel=\"canonical\" tags to all pages pointing to the preferred URL version. Canonical URLs prevent duplicate content confusion and consolidate AI engine citations to a single authoritative URL.',\n },\n content_velocity: {\n name: 'Increase Publishing Frequency',\n effort: 'High',\n description: 'Establish a regular content publishing cadence with dated entries in your sitemap. Consistent publishing signals to AI engines that your site is an active, current information source.',\n },\n schema_coverage: {\n name: 'Deepen Schema Coverage',\n effort: 'Medium',\n description: 'Extend structured data beyond the homepage to inner pages (articles, services, products). Consistent schema coverage across your site helps AI engines understand your full content depth.',\n },\n speakable_schema: {\n name: 'Add Speakable Schema',\n effort: 'Low',\n description: 'Add SpeakableSpecification schema with CSS selectors pointing to key content sections. This tells voice assistants and AI engines which parts of your page are most suitable for spoken answers.',\n },\n query_answer_alignment: {\n name: 'Improve Question-Answer Alignment',\n effort: 'Medium',\n description: 'Ensure every question-format heading (H2/H3) is followed by a direct answer paragraph. This pattern is ideal for AI engine snippet extraction.',\n },\n duplicate_content: {\n name: 'Fix Duplicate Content Blocks',\n effort: 'Medium',\n description: 'Sections within pages contain identical or near-identical text. LLMs may flag this as low-quality or thin content, reducing citation authority. Rewrite duplicate blocks with unique angles.',\n },\n cross_page_duplication: {\n name: 'Eliminate Cross-Page Duplicate Content',\n effort: 'Medium',\n description: 'The same paragraphs appear on multiple pages. AI engines may only index one version and ignore the rest. Rewrite shared content so each page offers a unique perspective.',\n },\n content_cannibalization: {\n name: 'Resolve Content Cannibalization',\n effort: 'Medium',\n description: 'Multiple pages compete for the same topic. Consolidate overlapping pages or differentiate titles and H1 headings.',\n },\n visible_date_signal: {\n name: 'Add Visible Date Signals',\n effort: 'Low',\n description: 'Display publication/modification dates visibly using <time> elements and add datePublished/dateModified to JSON-LD schema.',\n },\n topic_coherence: {\n name: 'Focus Content on Core Topics',\n effort: 'High',\n description: 'Ensure blog content consistently covers your core expertise areas rather than scattering across unrelated topics. AI engines build authority models - a site about \"Medicare coverage\" that also publishes about humidifiers and groceries dilutes its topical authority.',\n },\n content_depth: {\n name: 'Increase Content Depth',\n effort: 'Medium',\n description: 'Expand articles to 1000+ words with structured H2/H3 sections, comparison tables, and expert analysis. Thin content (under 300 words) is rarely cited by AI engines. Deep, well-structured articles demonstrate expertise.',\n },\n citation_ready_writing: {\n name: 'Improve Citation-Ready Writing',\n effort: 'Medium',\n description: 'Write self-contained definition sentences and single-claim statements that AI engines can quote directly. Avoid pronouns like \"this\" or \"that\" at the start of answer paragraphs.',\n },\n answer_first_placement: {\n name: 'Add Answer-First Placement',\n effort: 'Medium',\n description: 'Place a concise 40-80 word answer block in the first 300 words of each page. Avoid throat-clearing openers like \"In this article...\" and lead with the answer.',\n },\n evidence_packaging: {\n name: 'Package Evidence for AI',\n effort: 'Medium',\n description: 'Add inline citations to external sources, \"According to [Source]...\" attribution phrases, and a Sources section at the end of key articles.',\n },\n entity_disambiguation: {\n name: 'Add Entity Disambiguation',\n effort: 'Medium',\n description: 'Define the primary entity in the first 500 characters, use consistent terminology (same term 70%+), and add \"unlike X\" signals to help AI engines distinguish your topics.',\n },\n extraction_friction: {\n name: 'Reduce Extraction Friction',\n effort: 'Medium',\n description: 'Shorten sentences to under 25 words, use active voice in lead paragraphs, define jargon inline, and avoid hiding content behind accordions or display:none.',\n },\n image_context_ai: {\n name: 'Add Image Context for AI',\n effort: 'Low',\n description: 'Wrap images in <figure> elements with descriptive <figcaption> tags. Write alt text with 5+ words that describes the image content, not just \"image\" or \"photo\".',\n },\n};\n\n// ─── Impact calculation ──────────────────────────────────────────────────────\n\nfunction calculateImpact(score: number, weight: number, effort: string): ImpactLevel {\n const impactScore = (10 - score) * weight * 100;\n\n // Quick win override: low effort + meaningful impact\n if (effort === 'Low' && impactScore >= 3) return 'QUICK WIN';\n\n if (impactScore >= 12) return 'CRITICAL';\n if (impactScore >= 8) return 'HIGH';\n if (impactScore >= 5) return 'CORE AEO';\n if (impactScore >= 3) return 'MEDIUM';\n return 'LOW';\n}\n\n// ─── Verdict generation ──────────────────────────────────────────────────────\n\nexport function generateVerdict(\n score: number,\n scorecard: ScoreCardItem[],\n rawData: RawDataSummary,\n domain: string\n): string {\n // Score-tier opening\n let opening: string;\n if (score >= 86) {\n opening = `Excellent AEO implementation scoring ${score}/100.`;\n } else if (score >= 71) {\n opening = `Strong AEO fundamentals scoring ${score}/100 with room for optimization.`;\n } else if (score >= 56) {\n opening = `Moderate AEO readiness at ${score}/100 with significant gaps to address.`;\n } else if (score >= 41) {\n opening = `Below-average AEO readiness at ${score}/100 - multiple areas need attention.`;\n } else {\n opening = `Critical AEO gaps at ${score}/100 - ${domain} is largely invisible to AI engines.`;\n }\n\n // Top 3 strengths (score >= 8)\n const strengths = scorecard\n .filter(s => s.score >= 8)\n .sort((a, b) => b.score - a.score)\n .slice(0, 3);\n\n // Bottom 3 weaknesses (score <= 4)\n const weaknesses = scorecard\n .filter(s => s.score <= 4)\n .sort((a, b) => a.score - b.score)\n .slice(0, 3);\n\n const parts = [opening];\n\n if (strengths.length > 0) {\n const names = strengths.map(s => s.criterion);\n parts.push(`Key strengths include ${formatList(names)}.`);\n }\n\n if (weaknesses.length > 0) {\n const names = weaknesses.map(s => s.criterion);\n parts.push(`Priority gaps: ${formatList(names)}.`);\n }\n\n // Protocol note\n if (!rawData.has_https) {\n parts.push('HTTPS is not enabled, which caps several criteria scores and reduces AI crawler trust.');\n }\n\n // SPA rendering note\n if (rawData.rendered_with_headless) {\n parts.push('Note: this site uses client-side JavaScript rendering. AI crawlers see an empty page shell instead of content, which is the primary factor limiting the score.');\n }\n\n return parts.join(' ');\n}\n\n// ─── Opportunities generation ────────────────────────────────────────────────\n\nexport function generateOpportunities(\n scorecard: ScoreCardItem[],\n criterionResults: CriterionResult[]\n): Deliverable[] {\n const candidates: Array<{\n criterion: string;\n score: number;\n weight: number;\n impactScore: number;\n template: OpportunityTemplate;\n impact: ImpactLevel;\n }> = [];\n\n for (const result of criterionResults) {\n if (result.score > 7) continue; // Only generate opportunities for scores <= 7\n\n const weight = CRITERION_WEIGHTS[result.criterion] ?? 0.05;\n const template = OPPORTUNITY_TEMPLATES[result.criterion];\n if (!template) continue;\n\n const impactScore = (10 - result.score) * weight * 100;\n const impact = calculateImpact(result.score, weight, template.effort);\n\n candidates.push({\n criterion: result.criterion,\n score: result.score,\n weight,\n impactScore,\n template,\n impact,\n });\n }\n\n // Sort by impact score descending (highest impact first)\n candidates.sort((a, b) => b.impactScore - a.impactScore);\n\n // Take top 8-10\n const top = candidates.slice(0, 10);\n\n return top.map((c, i) => ({\n id: i + 1,\n name: c.template.name,\n description: c.template.description,\n effort: c.template.effort,\n impact: c.impact,\n }));\n}\n\n// ─── Pitch numbers generation ────────────────────────────────────────────────\n\nexport function generatePitchNumbers(\n score: number,\n rawData: RawDataSummary,\n scorecard: ScoreCardItem[]\n): PitchMetric[] {\n const metrics: PitchMetric[] = [];\n\n // 0. Rendering method (SPA warning - prepended so it appears first)\n if (rawData.rendered_with_headless) {\n metrics.push({\n metric: 'Rendering Method',\n value: 'Client-Side Only',\n significance: 'AI crawlers see empty HTML. All content loads via JavaScript, making this site invisible to ChatGPT, Claude, and Perplexity.',\n });\n }\n\n // 1. Overall AEO Score\n metrics.push({\n metric: 'AEO Score',\n value: `${score}/100`,\n significance: score >= 70\n ? 'Above average AI engine visibility'\n : score >= 50\n ? 'Moderate AI visibility with clear improvement paths'\n : 'Below average - significant optimization needed',\n });\n\n // 2. Schema types found\n const schemaCount = rawData.schema_types_found.length;\n metrics.push({\n metric: 'Schema Types',\n value: `${schemaCount} found`,\n significance: schemaCount >= 4\n ? 'Rich structured data helps AI engines parse content'\n : schemaCount >= 1\n ? 'Basic schema present but more types would improve AI extraction'\n : 'No structured data - AI engines cannot reliably extract content',\n });\n\n // 3. AI crawler readiness\n const aiCrawlerCount = rawData.robots_txt_ai_crawlers.length;\n const blockedCount = rawData.robots_txt_blocked_crawlers.length;\n metrics.push({\n metric: 'AI Crawler Access',\n value: blockedCount > 0\n ? `${blockedCount} blocked`\n : aiCrawlerCount > 0\n ? `${aiCrawlerCount} configured`\n : 'Not configured',\n significance: blockedCount > 0\n ? 'Active AI crawlers are blocked from accessing content'\n : aiCrawlerCount > 0\n ? 'robots.txt explicitly addresses AI crawler access'\n : 'No AI-specific crawler directives in robots.txt',\n });\n\n // 4. Content pages indexed\n const sitemapUrls = rawData.sitemap_url_count;\n metrics.push({\n metric: 'Sitemap URLs',\n value: sitemapUrls > 0 ? `${sitemapUrls} pages` : 'No sitemap',\n significance: sitemapUrls >= 50\n ? 'Comprehensive content library discoverable by AI crawlers'\n : sitemapUrls >= 10\n ? 'Moderate content footprint in sitemap'\n : sitemapUrls > 0\n ? 'Small sitemap - expanding content improves AI coverage'\n : 'No sitemap means AI crawlers must discover pages via links only',\n });\n\n // 5. Internal linking\n const linkCount = rawData.internal_link_count;\n metrics.push({\n metric: 'Internal Links',\n value: `${linkCount} links`,\n significance: linkCount >= 30\n ? 'Strong internal linking supports AI content discovery'\n : linkCount >= 10\n ? 'Moderate linking - adding more cross-references improves navigability'\n : 'Weak internal linking limits AI crawler depth',\n });\n\n // 6. Question headings\n const questionCount = rawData.question_headings_count + (rawData.blog_sample_question_headings || 0);\n if (questionCount > 0) {\n metrics.push({\n metric: 'Question Headings',\n value: `${questionCount} found`,\n significance: 'Question-based headings match how users query AI assistants',\n });\n }\n\n // 7. Criteria passing (>= 7)\n const passing = scorecard.filter(s => s.score >= 7).length;\n metrics.push({\n metric: 'Criteria Passing',\n value: `${passing}/${scorecard.length}`,\n significance: passing >= 18\n ? 'Excellent coverage across AEO dimensions'\n : passing >= 12\n ? 'Good foundation with room to improve remaining criteria'\n : `${scorecard.length - passing} criteria need attention for full AI visibility`,\n });\n\n return metrics;\n}\n\n// ─── Bottom line generation ──────────────────────────────────────────────────\n\nexport function generateBottomLine(\n score: number,\n opportunities: Deliverable[],\n scorecard: ScoreCardItem[],\n domain: string\n): string {\n const quickWins = opportunities.filter(o => o.impact === 'QUICK WIN');\n const criticalOps = opportunities.filter(o => o.impact === 'CRITICAL' || o.impact === 'HIGH');\n\n const passing = scorecard.filter(s => s.score >= 7).length;\n const total = scorecard.length;\n\n let summary: string;\n if (score >= 86) {\n summary = `${domain} demonstrates excellent AI engine optimization with ${passing}/${total} criteria at good or strong levels. Focus on maintaining content freshness and expanding structured data coverage to stay ahead.`;\n } else if (score >= 71) {\n summary = `${domain} has a solid AEO foundation with ${passing}/${total} criteria passing.`;\n if (quickWins.length > 0) {\n summary += ` ${quickWins.length} quick wins available: ${quickWins.slice(0, 3).map(q => q.name).join(', ')}.`;\n }\n if (criticalOps.length > 0) {\n summary += ` Address ${criticalOps.length} high-impact opportunities to push the score above 85.`;\n }\n } else if (score >= 56) {\n summary = `${domain} has moderate AI visibility with ${passing}/${total} criteria passing. ${opportunities.length} improvement opportunities identified.`;\n if (quickWins.length > 0) {\n summary += ` Start with quick wins: ${quickWins.slice(0, 3).map(q => q.name).join(', ')}.`;\n }\n } else if (score >= 41) {\n summary = `${domain} needs significant AEO work with only ${passing}/${total} criteria passing.`;\n if (criticalOps.length > 0) {\n summary += ` Priority: ${criticalOps.slice(0, 3).map(c => c.name).join(', ')}.`;\n }\n summary += ` Implementing the top ${Math.min(5, opportunities.length)} recommendations could improve the score by 15-25 points.`;\n } else {\n summary = `${domain} is largely invisible to AI engines with only ${passing}/${total} criteria passing. Fundamental AEO infrastructure is missing.`;\n if (opportunities.length > 0) {\n summary += ` Start with: ${opportunities.slice(0, 3).map(o => o.name).join(', ')}.`;\n }\n summary += ` A comprehensive AEO implementation could transform AI visibility from near-zero to competitive.`;\n }\n\n return summary;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction formatList(items: string[]): string {\n if (items.length === 0) return '';\n if (items.length === 1) return items[0];\n if (items.length === 2) return `${items[0]} and ${items[1]}`;\n return `${items.slice(0, -1).join(', ')}, and ${items[items.length - 1]}`;\n}\n","/**\n * Extended page discovery for instant audit.\n * Fetches additional pages beyond what prefetchSiteData provides,\n * including nav-linked pages, common paths, and content pages from sitemap.\n */\n\nimport type { FetchResult, SiteData, PageCategory } from './site-crawler.js';\n\n// ─── Fetch helper (matches site-crawler.ts fetchText) ────────────────────────\n\nasync function fetchPage(url: string, timeoutMs = 10000): Promise<FetchResult | null> {\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(timeoutMs),\n headers: { 'User-Agent': 'AEO-Visibility-Bot/1.0' },\n redirect: 'follow',\n });\n if (res.status !== 200) return null;\n const text = await res.text();\n if (text.length < 200) return null; // Skip trivially small pages\n return { text: text.slice(0, 500000), status: res.status, finalUrl: res.url };\n } catch {\n return null;\n }\n}\n\n// ─── Page variant paths ──────────────────────────────────────────────────────\n\nconst PAGE_VARIANTS: Record<string, string[]> = {\n about: ['/about', '/about-us', '/company', '/who-we-are'],\n pricing: ['/pricing', '/plans', '/packages'],\n services: ['/services', '/features', '/solutions', '/products', '/what-we-do'],\n contact: ['/contact', '/contact-us', '/get-in-touch'],\n team: ['/team', '/our-team', '/authors', '/people', '/leadership'],\n resources: ['/resources', '/resource-center', '/library'],\n docs: ['/docs', '/documentation', '/help', '/help-center', '/support'],\n cases: ['/case-studies', '/customers', '/success-stories', '/testimonials'],\n};\n\n// ─── Nav link extraction ─────────────────────────────────────────────────────\n\n/**\n * Extract internal page paths from <nav> elements in homepage HTML.\n * Returns deduplicated absolute paths (e.g. ['/about', '/pricing']).\n */\nexport function extractNavLinks(html: string, domain: string): string[] {\n // Extract links from <nav> elements\n const navBlocks = html.match(/<nav[\\s\\S]*?<\\/nav>/gi) || [];\n const navHtml = navBlocks.join('\\n');\n\n const hrefMatches = navHtml.match(/href=\"([^\"#]*)\"/gi) || [];\n const paths = new Set<string>();\n\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n\n for (const match of hrefMatches) {\n const href = match.match(/href=\"([^\"#]*)\"/i)?.[1];\n if (!href) continue;\n\n let path: string;\n if (href.startsWith('/')) {\n path = href;\n } else if (href.startsWith('http')) {\n try {\n const url = new URL(href);\n const linkDomain = url.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n path = url.pathname;\n } catch {\n continue;\n }\n } else {\n continue;\n }\n\n // Normalize: strip trailing slash, skip root and fragments\n path = path.replace(/\\/+$/, '') || '/';\n if (path === '/') continue;\n if (path.includes('#')) continue;\n\n // Skip resource/utility paths\n if (/\\.(js|css|png|jpg|svg|ico|pdf|xml|txt)$/i.test(path)) continue;\n if (/^\\/(api|wp-|static|assets|_next|auth|login|signup|cart|checkout)\\b/i.test(path)) continue;\n\n paths.add(path);\n }\n\n return Array.from(paths);\n}\n\n// ─── Full-page link extraction (fallback when no sitemap) ─────────────────\n\n/**\n * Extract ALL internal links from the full page HTML (not just <nav>).\n * Used as a fallback when no sitemap exists to build our own page list.\n * Returns deduplicated absolute paths, filtered and capped.\n */\nexport function extractAllInternalLinks(html: string, domain: string, limit = 30): string[] {\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const hrefMatches = html.match(/href=\"([^\"#]*)\"/gi) || [];\n const paths = new Set<string>();\n\n for (const match of hrefMatches) {\n const href = match.match(/href=\"([^\"#]*)\"/i)?.[1];\n if (!href) continue;\n\n let path: string;\n if (href.startsWith('/')) {\n path = href;\n } else if (href.startsWith('http')) {\n try {\n const url = new URL(href);\n const linkDomain = url.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n path = url.pathname;\n } catch {\n continue;\n }\n } else {\n continue;\n }\n\n // Normalize\n path = path.replace(/\\/+$/, '') || '/';\n if (path === '/') continue;\n if (path.includes('#') || path.includes('?')) continue;\n\n // Skip resource files and utility paths\n if (/\\.(js|css|png|jpg|jpeg|gif|svg|ico|pdf|xml|txt|zip|woff|woff2|ttf|eot|mp4|webm|mp3)$/i.test(path)) continue;\n if (/^\\/(api|wp-admin|wp-includes|wp-json|static|assets|_next|auth|login|signup|sign-up|register|cart|checkout|account|admin|cdn-cgi|feed|rss)\\b/i.test(path)) continue;\n\n // Skip anchors-only and mailto/tel\n if (path.startsWith('mailto:') || path.startsWith('tel:')) continue;\n\n paths.add(path);\n }\n\n // Return up to limit paths, prioritizing shorter paths (more likely to be main pages)\n return Array.from(paths)\n .sort((a, b) => a.split('/').length - b.split('/').length || a.localeCompare(b))\n .slice(0, limit);\n}\n\n// ─── Content page extraction from sitemap ────────────────────────────────────\n\n/**\n * Extract non-blog deep content pages from sitemap XML.\n * Targets service pages, product pages, etc. (not blog/article posts).\n */\nexport function extractContentPagesFromSitemap(\n sitemapText: string,\n domain: string,\n limit = 6\n): string[] {\n const urlBlocks = sitemapText.match(/<url>([\\s\\S]*?)<\\/url>/gi) || [];\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const candidates: string[] = [];\n\n // Paths to skip (already covered by blog sample or common pages)\n const skipPatterns = /\\/(?:blog|articles?|posts?|news|tag|category|author|feed|faq|about|pricing|contact|team|resources?|docs?|documentation|help|support|case-studies|customers|testimonials|sitemap|wp-|api|login|cart|checkout|search)\\b/i;\n\n for (const block of urlBlocks) {\n const locMatch = block.match(/<loc>([^<]+)<\\/loc>/i);\n if (!locMatch) continue;\n const url = locMatch[1].trim();\n\n try {\n const parsed = new URL(url);\n const urlDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (urlDomain !== cleanDomain) continue;\n\n if (parsed.pathname === '/' || parsed.pathname === '') continue;\n\n const path = parsed.pathname.toLowerCase();\n if (skipPatterns.test(path)) continue;\n\n // Want pages with 1-2 path segments (service pages, product pages)\n const segments = path.split('/').filter(Boolean);\n if (segments.length < 1 || segments.length > 3) continue;\n\n candidates.push(url);\n } catch {\n continue;\n }\n }\n\n // Return evenly spaced pages from the list for variety\n if (candidates.length <= limit) return candidates;\n\n const result: string[] = [];\n for (let i = 0; i < limit; i++) {\n const index = Math.round(i * (candidates.length - 1) / (limit - 1));\n result.push(candidates[index]);\n }\n return result;\n}\n\n// ─── Main multi-page fetcher ─────────────────────────────────────────────────\n\nexport interface MultiPageOptions {\n timeoutMs?: number;\n}\n\n/**\n * Fetch additional pages beyond what prefetchSiteData provides.\n * Discovers pages from nav links + common path variants + sitemap content pages.\n * All fetched pages are appended to siteData.blogSample so existing\n * getCombinedHtml() and criteria checks pick them up automatically.\n *\n * Mutates siteData in place and returns the count of new pages added.\n */\nexport async function fetchMultiPageData(\n siteData: SiteData,\n options?: MultiPageOptions\n): Promise<number> {\n if (!siteData.protocol || !siteData.homepage) return 0;\n\n const timeoutMs = options?.timeoutMs ?? 10000;\n const baseUrl = `${siteData.protocol}://${siteData.domain}`;\n const existingUrls = new Set<string>();\n\n // Track already-fetched URLs\n existingUrls.add(baseUrl + '/');\n existingUrls.add(baseUrl);\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n if (page.finalUrl) existingUrls.add(page.finalUrl);\n }\n }\n\n // Collect candidate URLs to fetch (URL -> category)\n const urlsToFetch = new Map<string, PageCategory>();\n\n // Source 1: Nav links from homepage\n const navPaths = extractNavLinks(siteData.homepage.text, siteData.domain);\n\n // Source 2: Common page variants (try nav links first, then fallback paths)\n for (const [category, variants] of Object.entries(PAGE_VARIANTS)) {\n // Check if any nav link matches this category\n const navMatch = navPaths.find(p =>\n variants.some(v => p.toLowerCase() === v || p.toLowerCase().startsWith(v + '/'))\n );\n\n if (navMatch) {\n const url = `${baseUrl}${navMatch}`;\n if (!existingUrls.has(url)) urlsToFetch.set(url, category as PageCategory);\n } else {\n // Try first variant as fallback\n const url = `${baseUrl}${variants[0]}`;\n if (!existingUrls.has(url)) urlsToFetch.set(url, category as PageCategory);\n }\n }\n\n // Source 3: Content pages from sitemap\n if (siteData.sitemapXml && siteData.sitemapXml.status === 200) {\n const contentUrls = extractContentPagesFromSitemap(\n siteData.sitemapXml.text,\n siteData.domain,\n 6\n );\n for (const url of contentUrls) {\n if (!existingUrls.has(url)) urlsToFetch.set(url, 'content');\n }\n }\n\n // Source 4: When no sitemap blog sample exists, extract ALL internal links\n // from homepage as a fallback to build our own page list\n const hasBlogSample = (siteData.blogSample?.length ?? 0) > 3;\n if (!hasBlogSample) {\n const allPaths = extractAllInternalLinks(siteData.homepage.text, siteData.domain, 30);\n for (const path of allPaths) {\n const url = `${baseUrl}${path}`;\n if (!existingUrls.has(url) && !urlsToFetch.has(url)) {\n urlsToFetch.set(url, 'content');\n }\n }\n }\n\n // Fetch all URLs in parallel\n const entries = Array.from(urlsToFetch.entries());\n if (entries.length === 0) return 0;\n\n const results = await Promise.all(entries.map(([url]) => fetchPage(url, timeoutMs)));\n\n // Append successful results to blogSample with category tags\n if (!siteData.blogSample) siteData.blogSample = [];\n\n let added = 0;\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (result && result.text.length > 500) {\n result.category = entries[i][1];\n siteData.blogSample.push(result);\n added++;\n }\n }\n\n return added;\n}\n","/**\n * Per-page AEO scoring.\n * Evaluates 25 of 40 criteria that apply at individual page level.\n * Produces a 0-75 AEO score per page (single-page cap at 75).\n */\n\nimport type { SiteData } from './site-crawler.js';\nimport {\n extractDuplicateContentSections,\n shingleJaccardSimilarity,\n} from './duplicate-content.js';\nimport {\n scoreHelpfulPurposeAlignment,\n scoreFirstHandExperienceSignals,\n scoreCreatorTransparency,\n scoreMethodologyTransparency,\n} from './helpful-content.js';\nexport {\n scoreHelpfulPurposeAlignment,\n scoreFirstHandExperienceSignals,\n scoreCreatorTransparency,\n scoreMethodologyTransparency,\n} from './helpful-content.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface PageCriterionScore {\n criterion: string;\n criterion_label: string;\n score: number;\n weight: number;\n}\n\nexport interface PageScoreResult {\n aeoScore: number;\n criterionScores: PageCriterionScore[];\n scoreCapped?: boolean;\n}\n\n// ─── Criteria weights (mirrored from scoring.ts for page-level criteria) ─\n\nconst PAGE_CRITERIA: Record<string, { weight: number; label: string }> = {\n // Content Substance\n original_data: { weight: 0.10, label: 'Original Data & Expert Content' },\n fact_density: { weight: 0.06, label: 'Fact & Data Density' },\n direct_answer_density: { weight: 0.05, label: 'Direct Answer Paragraphs' },\n qa_content_format: { weight: 0.04, label: 'Q&A Content Format' },\n query_answer_alignment: { weight: 0.04, label: 'Query-Answer Alignment' },\n faq_section: { weight: 0.03, label: 'FAQ Section Content' },\n // Content Organization\n content_freshness: { weight: 0.04, label: 'Content Freshness Signals' },\n schema_markup: { weight: 0.03, label: 'Schema.org Structured Data' },\n table_list_extractability: { weight: 0.03, label: 'Table & List Extractability' },\n definition_patterns: { weight: 0.015, label: 'Definition Patterns' },\n visible_date_signal: { weight: 0.015, label: 'Visible Date Signal' },\n semantic_html: { weight: 0.02, label: 'Semantic HTML5 & Accessibility' },\n clean_html: { weight: 0.02, label: 'Clean, Crawlable HTML' },\n // Technical Plumbing\n canonical_url: { weight: 0.005, label: 'Canonical URL Strategy' },\n // V2 Criteria\n citation_ready_writing: { weight: 0.04, label: 'Citation-Ready Writing Quality' },\n answer_first_placement: { weight: 0.03, label: 'Answer-First Placement' },\n evidence_packaging: { weight: 0.03, label: 'Evidence Packaging' },\n helpful_purpose_alignment: { weight: 0.03, label: 'Helpful Purpose Alignment' },\n first_hand_experience_signals: { weight: 0.03, label: 'First-Hand Experience Signals' },\n entity_disambiguation: { weight: 0.02, label: 'Entity Disambiguation' },\n extraction_friction: { weight: 0.02, label: 'Extraction Friction Score' },\n creator_transparency: { weight: 0.02, label: 'Creator Transparency' },\n methodology_transparency: { weight: 0.02, label: 'Methodology Transparency' },\n image_context_ai: { weight: 0.005, label: 'Image Context for AI' },\n duplicate_content: { weight: 0.05, label: 'Duplicate Content Blocks' },\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction extractJsonLdBlocks(html: string): string[] {\n const blocks: string[] = [];\n const regex = /<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(html)) !== null) {\n blocks.push(match[1]);\n }\n return blocks;\n}\n\nfunction extractTypesFromJsonLd(blocks: string[]): Set<string> {\n const types = new Set<string>();\n for (const block of blocks) {\n const typeMatches = block.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/g) || [];\n for (const m of typeMatches) {\n const t = m.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/);\n if (t) types.add(t[1]);\n }\n }\n return types;\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction extractQuestionHeadings(html: string): string[] {\n const headings = html.match(/<h[2-3][^>]*>([\\s\\S]*?)<\\/h[2-3]>/gi) || [];\n const questions: string[] = [];\n for (const h of headings) {\n const text = h.replace(/<[^>]*>/g, '').trim();\n if (/\\?$/.test(text) || /^(what|how|why|when|where|who|which|can|do|does|is|are|should|will)\\b/i.test(text)) {\n questions.push(text);\n }\n }\n return questions;\n}\n\n/** Check if a question heading is followed by an answer paragraph (≥20 chars). */\nfunction countAnsweredQuestions(html: string): { total: number; answered: number } {\n const questions = extractQuestionHeadings(html);\n if (questions.length === 0) return { total: 0, answered: 0 };\n\n let answered = 0;\n for (const q of questions) {\n // Escape the question text for regex matching\n const escaped = q.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n // Look for the heading containing this text, then a <p> after it\n const pattern = new RegExp(escaped + '[\\\\s\\\\S]*?</h[2-3]>\\\\s*<p[^>]*>([\\\\s\\\\S]*?)</p>', 'i');\n const match = html.match(pattern);\n if (match && match[1].replace(/<[^>]*>/g, '').trim().length >= 20) {\n answered++;\n }\n }\n return { total: questions.length, answered };\n}\n\nfunction cap(value: number, max: number): number {\n return Math.min(value, max);\n}\n\n// ─── 14 Scoring Functions ───────────────────────────────────────────────────\n\n/** 1. Schema.org Structured Data */\nexport function scoreSchemaMarkup(html: string): number {\n const blocks = extractJsonLdBlocks(html);\n if (blocks.length === 0) return 0;\n\n let score = 3; // has JSON-LD\n const types = extractTypesFromJsonLd(blocks);\n\n const knownTypes = ['Organization', 'LocalBusiness', 'Article', 'FAQPage', 'Product', 'WebPage',\n 'BreadcrumbList', 'HowTo', 'Person', 'WebSite', 'BlogPosting', 'Service'];\n let knownCount = 0;\n for (const t of types) {\n if (knownTypes.includes(t)) knownCount++;\n }\n score += cap(knownCount * 2, 4);\n\n if (types.has('Organization') || types.has('LocalBusiness')) score += 2;\n if (types.has('FAQPage')) score += 1;\n\n return cap(score, 10);\n}\n\n/** 2. Q&A Content Format */\nexport function scoreQAFormat(html: string): number {\n const questions = extractQuestionHeadings(html);\n let score = 0;\n\n if (questions.length >= 10) score += 5;\n else if (questions.length >= 3) score += 3;\n else if (questions.length >= 1) score += 1;\n\n // Direct-answer format: question heading followed by paragraph ≥20 chars\n const { answered } = countAnsweredQuestions(html);\n if (answered >= 1) score += 3;\n\n // Single H1\n const h1Matches = html.match(/<h1[\\s>]/gi) || [];\n if (h1Matches.length === 1) score += 2;\n\n return cap(score, 10);\n}\n\n/** 3. Clean, Crawlable HTML */\nexport function scoreCleanHtml(html: string): number {\n let score = 0;\n\n // Semantic elements\n const semantics = ['<main', '<article', '<section'];\n let semCount = 0;\n for (const tag of semantics) {\n if (html.toLowerCase().includes(tag)) semCount++;\n }\n score += cap(semCount, 3);\n\n // Single H1\n const h1Matches = html.match(/<h1[\\s>]/gi) || [];\n if (h1Matches.length === 1) score += 2;\n\n // Text content > 500 chars\n const text = getTextContent(html);\n if (text.length > 500) score += 3;\n\n // Title + meta description\n const hasTitle = /<title[^>]*>[^<]+<\\/title>/i.test(html);\n const hasDesc = /<meta\\s[^>]*name=[\"']description[\"'][^>]*content=[\"'][^\"']+[\"']/i.test(html)\n || /<meta\\s[^>]*content=[\"'][^\"']+[\"'][^>]*name=[\"']description[\"']/i.test(html);\n if (hasTitle && hasDesc) score += 2;\n\n return cap(score, 10);\n}\n\n/** 4. FAQ Section Content */\nexport function scoreFaqSection(html: string): number {\n let score = 0;\n const lowerHtml = html.toLowerCase();\n\n // FAQ/frequently-asked text\n if (/frequently\\s*asked|faq/i.test(html)) score += 2;\n\n // FAQPage in JSON-LD\n const blocks = extractJsonLdBlocks(html);\n const types = extractTypesFromJsonLd(blocks);\n if (types.has('FAQPage')) score += 3;\n\n // Question headings ≥ 10\n const questions = extractQuestionHeadings(html);\n if (questions.length >= 10) score += 1;\n\n // Accordion/toggle patterns (details/summary or common CSS classes)\n if (/<details[\\s>]/i.test(html) || /accordion|collapsible|toggle/i.test(lowerHtml)) score += 1;\n\n return cap(score, 10);\n}\n\n/** 5. Original Data & Expert Content */\nexport function scoreOriginalData(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Stats with research context\n if (/\\b(our (study|analysis|research|survey|data|findings))\\b/i.test(text)) {\n score += 3;\n } else if (/\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(text)) {\n score += 1;\n }\n\n // Case studies with metrics\n if (/\\bcase\\s+stud(y|ies)\\b/i.test(text) && /\\d+(\\.\\d+)?%|\\$[\\d,.]+/.test(text)) {\n score += 3;\n } else if (/\\bcase\\s+stud(y|ies)\\b/i.test(text)) {\n score += 1;\n }\n\n // Expert attribution\n if (/\\baccording\\s+to\\b|\\bexpert|\\b(Ph\\.?D|MD|professor|analyst|researcher)\\b/i.test(text)) {\n score += 2;\n }\n\n // Blog content (internal links to /blog)\n if (/href=[\"'][^\"']*\\/blog\\b/i.test(html)) {\n score += 2;\n }\n\n return cap(score, 10);\n}\n\n/** 6. Query-Answer Alignment */\nexport function scoreQueryAnswerAlignment(html: string): number {\n const { total, answered } = countAnsweredQuestions(html);\n\n // No question headings = neutral\n if (total === 0) return 5;\n\n const ratio = answered / total;\n if (ratio >= 0.8) return 10;\n if (ratio >= 0.5) return 7;\n if (answered > 0) return 4;\n return 0;\n}\n\n/** 7. Content Freshness Signals */\nexport function scoreContentFreshness(html: string): number {\n let score = 0;\n const blocks = extractJsonLdBlocks(html);\n\n // JSON-LD datePublished/dateModified\n const allJsonLd = blocks.join(' ');\n if (/datePublished|dateModified/i.test(allJsonLd)) score += 3;\n\n // <time> elements\n const timeElements = html.match(/<time[\\s>]/gi) || [];\n if (timeElements.length >= 2) score += 3;\n else if (timeElements.length === 1) score += 1;\n\n // Article meta tags\n if (/<meta\\s[^>]*property=[\"']article:(published_time|modified_time)[\"']/i.test(html)) score += 2;\n\n // Current or previous year reference\n const currentYear = new Date().getFullYear();\n const yearPattern = new RegExp(`\\\\b(${currentYear}|${currentYear - 1})\\\\b`);\n if (yearPattern.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 8. Table & List Extractability */\nexport function scoreTableListExtractability(html: string): number {\n let score = 0;\n\n // Tables with <th>\n const tablesWithHeaders = html.match(/<table[\\s\\S]*?<th[\\s>]/gi) || [];\n if (tablesWithHeaders.length >= 2) score += 4;\n else if (tablesWithHeaders.length === 1) score += 3;\n\n // Tables without headers\n if (tablesWithHeaders.length === 0 && /<table[\\s>]/i.test(html)) score += 1;\n\n // Ordered lists\n if (/<ol[\\s>]/i.test(html)) score += 2;\n\n // Unordered lists\n if (/<ul[\\s>]/i.test(html)) score += 2;\n\n // ≥10 list items\n const listItems = html.match(/<li[\\s>]/gi) || [];\n if (listItems.length >= 10) score += 1;\n\n // Definition lists\n if (/<dl[\\s>]/i.test(html)) score += 1;\n\n return cap(score, 10);\n}\n\n/** 9. Direct Answer Paragraphs */\nexport function scoreDirectAnswerDensity(html: string): number {\n let score = 0;\n\n // Q&A pairs (question heading + <p>)\n const { answered } = countAnsweredQuestions(html);\n if (answered >= 3) score += 6;\n else if (answered >= 1) score += 3;\n\n // Snippet-zone paragraphs (40-150 words)\n const paragraphs = html.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n let snippetCount = 0;\n for (const p of paragraphs) {\n const text = p.replace(/<[^>]*>/g, '').trim();\n const words = text.split(/\\s+/).filter(w => w.length > 0).length;\n if (words >= 40 && words <= 150) snippetCount++;\n }\n if (snippetCount >= 3) score += 2;\n else if (snippetCount >= 1) score += 1;\n\n // Direct-answer openers\n const directOpeners = getTextContent(html).match(/\\b(yes|no|in short|the answer is|simply put|in summary)\\b/gi) || [];\n if (directOpeners.length >= 2) score += 2;\n\n return cap(score, 10);\n}\n\n/** 10. Semantic HTML5 & Accessibility */\nexport function scoreSemanticHtml(html: string): number {\n let score = 0;\n const lowerHtml = html.toLowerCase();\n\n // 6 semantic elements\n const elements = ['<main', '<article', '<time', '<nav', '<header', '<footer'];\n let count = 0;\n for (const el of elements) {\n if (lowerHtml.includes(el)) count++;\n }\n score += cap(Math.floor(count * 0.7), 4);\n\n // Image alt ratio ≥ 80%\n const imgTags = html.match(/<img\\s[^>]*>/gi) || [];\n if (imgTags.length > 0) {\n let withAlt = 0;\n for (const img of imgTags) {\n if (/\\salt=[\"'][^\"']*[\"']/i.test(img)) withAlt++;\n }\n if (withAlt / imgTags.length >= 0.8) score += 2;\n }\n\n // lang attribute\n if (/<html[^>]*\\slang=[\"'][^\"']+[\"']/i.test(html)) score += 2;\n\n // ARIA attributes\n if (/\\baria-/i.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 11. Fact & Data Density */\nexport function scoreFactDensity(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Numeric data points (%, $, count phrases)\n const numericPatterns = text.match(/\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b|\\b\\d+\\s*(million|billion|thousand|users|customers|employees)\\b/gi) || [];\n if (numericPatterns.length >= 6) score += 5;\n else if (numericPatterns.length >= 3) score += 3;\n else if (numericPatterns.length >= 1) score += 1;\n\n // Year references\n const years = new Set<string>();\n const yearMatches = text.match(/\\b(19|20)\\d{2}\\b/g) || [];\n for (const y of yearMatches) years.add(y);\n if (years.size >= 2) score += 2;\n else if (years.size === 1) score += 1;\n\n // Attribution phrases\n if (/\\baccording to\\b|\\bsource:\\s|\\bcited\\b|\\breported by\\b/i.test(text)) score += 2;\n\n // Units (kg, miles, hours, etc.)\n const units = text.match(/\\b\\d+\\s*(kg|lb|miles|km|hours|minutes|days|months|years|GB|MB|TB)\\b/gi) || [];\n if (units.length >= 2) score += 1;\n\n return cap(score, 10);\n}\n\n/** 12. Definition Patterns */\nexport function scoreDefinitionPatterns(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Definition phrases\n const defPatterns = text.match(/\\b(is a|is an|refers to|defined as|means that|also known as|abbreviated as)\\b/gi) || [];\n if (defPatterns.length >= 3) score += 5;\n else if (defPatterns.length >= 1) score += 3;\n\n // Early placement (first 2000 chars)\n const early = text.slice(0, 2000);\n if (/\\b(is a|is an|refers to|defined as)\\b/i.test(early)) score += 2;\n\n // <dfn> or <abbr>\n if (/<dfn[\\s>]/i.test(html) || /<abbr[\\s>]/i.test(html)) score += 1;\n\n // <dl> or glossary\n if (/<dl[\\s>]/i.test(html) || /glossary/i.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 13. Canonical URL Strategy */\nexport function scoreCanonicalUrl(html: string, url?: string): number {\n let score = 0;\n\n // Has canonical link\n const canonicalMatch = html.match(/<link[^>]*rel=[\"']canonical[\"'][^>]*href=[\"']([^\"']+)[\"']/i)\n || html.match(/<link[^>]*href=[\"']([^\"']+)[\"'][^>]*rel=[\"']canonical[\"']/i);\n\n if (!canonicalMatch) return 0;\n score += 4;\n\n const canonicalHref = canonicalMatch[1];\n\n // Self-referencing\n if (url) {\n try {\n const canonicalUrl = new URL(canonicalHref, url);\n const pageUrl = new URL(url);\n if (canonicalUrl.pathname === pageUrl.pathname && canonicalUrl.hostname === pageUrl.hostname) {\n score += 3;\n }\n } catch {\n // Ignore URL parse errors\n }\n }\n\n // HTTPS\n if (canonicalHref.startsWith('https://')) score += 2;\n\n // Single canonical (no duplicates)\n const allCanonicals = html.match(/<link[^>]*rel=[\"']canonical[\"'][^>]*>/gi) || [];\n if (allCanonicals.length === 1) score += 1;\n\n return cap(score, 10);\n}\n\n/** 14. Visible Date Signal */\nexport function scoreVisibleDateSignal(html: string): number {\n let score = 0;\n\n // <time datetime> with visible text\n const timeWithDatetime = html.match(/<time[^>]*datetime=[\"'][^\"']+[\"'][^>]*>[^<]+<\\/time>/gi) || [];\n if (timeWithDatetime.length > 0) score += 5;\n\n // JSON-LD datePublished/dateModified\n const blocks = extractJsonLdBlocks(html);\n const allJsonLd = blocks.join(' ');\n if (/datePublished|dateModified/i.test(allJsonLd)) score += 3;\n\n // Article meta date tags\n if (/<meta\\s[^>]*property=[\"']article:(published_time|modified_time)[\"']/i.test(html)) score += 2;\n\n // dateModified within 180 days\n const modifiedMatch = allJsonLd.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (modifiedMatch) {\n try {\n const modified = new Date(modifiedMatch[1]);\n const daysDiff = (Date.now() - modified.getTime()) / (1000 * 60 * 60 * 24);\n if (daysDiff <= 180) score += 1;\n } catch {\n // Ignore date parse errors\n }\n }\n\n return cap(score, 10);\n}\n\n/** 15. Citation-Ready Writing Quality */\nexport function scoreCitationReadyWriting(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Definition sentences\n const defSentences = text.match(/\\b\\w+\\s+(is\\s+(?:a|an)\\s|refers\\s+to|defined\\s+as)\\b/gi) || [];\n if (defSentences.length >= 3) score += 3;\n else if (defSentences.length >= 1) score += 1;\n\n // One-claim sentences (<30 words, single clause)\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10);\n const oneClaimSentences = sentences.filter(s => {\n const words = s.trim().split(/\\s+/);\n return words.length < 30 && !/,\\s*(and|but|or|which|that|because|although|while)\\s/i.test(s);\n });\n if (oneClaimSentences.length >= 5) score += 3;\n else if (oneClaimSentences.length >= 2) score += 1;\n\n // Self-contained check (no dangling pronouns after question headings)\n const qHeadings = extractQuestionHeadings(html);\n if (qHeadings.length > 0) {\n let selfContained = 0;\n for (const qh of qHeadings) {\n const escaped = qh.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pat = new RegExp(escaped + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>\\\\s*<p[^>]*>([^<]{0,200})', 'i');\n const m = pat.exec(html);\n if (m && m[1] && !/^\\s*(this|that|these|those|it)\\s/i.test(m[1].trim())) {\n selfContained++;\n }\n }\n if (selfContained / qHeadings.length >= 0.8) score += 2;\n }\n\n // Quotation-ready lines\n const quotableLines = sentences.filter(s =>\n /\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(s) &&\n /\\b[A-Z][a-z]+(?:\\s+[A-Z][a-z]+)*\\b/.test(s)\n );\n if (quotableLines.length >= 2) score += 2;\n else if (quotableLines.length >= 1) score += 1;\n\n return cap(score, 10);\n}\n\n/** 16. Answer-First Placement */\nexport function scoreAnswerFirstPlacement(html: string): number {\n let score = 0;\n\n const bodyMatch = html.match(/<body[^>]*>([\\s\\S]*)/i);\n const bodyHtml = bodyMatch ? bodyMatch[1] : html;\n const bodyText = bodyHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n\n // Short answer block (40-80 words) in first 300 words\n const earlyParagraphs = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 5) || [];\n const first300Words = bodyText.split(/\\s+/).slice(0, 300).join(' ');\n for (const p of earlyParagraphs) {\n const pText = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = pText.split(/\\s+/).length;\n if (wordCount >= 40 && wordCount <= 80 && first300Words.includes(pText.slice(0, 50))) {\n score += 4;\n break;\n }\n }\n\n // First-paragraph answer check (not throat-clearing)\n const firstPara = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/i);\n if (firstPara) {\n const firstParaText = firstPara[1].replace(/<[^>]*>/g, '').trim();\n if (!/^(In today|Welcome to|Have you ever|If you're looking|In this (article|post|guide))/i.test(firstParaText)) {\n score += 3;\n }\n }\n\n // Entity in first sentence (H1 word in first body sentence)\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (h1Match) {\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3);\n const firstSentence = bodyText.split(/[.!?]/)[0] || '';\n if (h1Words.some(w => firstSentence.toLowerCase().includes(w.toLowerCase()))) {\n score += 3;\n }\n }\n\n return cap(score, 10);\n}\n\n/** 17. Evidence Packaging */\nexport function scoreEvidencePackaging(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Inline citations (<a> inside <p> linking to external domains)\n const paragraphs = html.match(/<p[^>]*>[\\s\\S]*?<\\/p>/gi) || [];\n let inlineCitations = 0;\n for (const p of paragraphs) {\n const links = p.match(/<a[^>]*href=[\"'](https?:\\/\\/[^\"']+)[\"'][^>]*>/gi) || [];\n inlineCitations += links.length;\n }\n if (inlineCitations >= 3) score += 3;\n else if (inlineCitations >= 1) score += 1;\n\n // Sources section heading\n if (/<h[2-4][^>]*>[^<]*(source|reference|citation|bibliography)[^<]*<\\/h[2-4]>/i.test(html)) {\n score += 2;\n }\n\n // Attribution phrases\n const attributionPhrases = text.match(/\\baccording\\s+to\\s+[A-Z]|\\b[A-Z][a-z]+\\s+(found|says|reports|notes|states)\\b/gi) || [];\n if (attributionPhrases.length >= 3) score += 3;\n else if (attributionPhrases.length >= 1) score += 1;\n\n // Sourced statistics\n const sourcedStats = text.match(/\\d+(\\.\\d+)?(%|\\s*(million|billion|thousand|percent))\\b[^.]*\\b[A-Z][a-z]+\\b/gi) || [];\n if (sourcedStats.length >= 2) score += 2;\n else if (sourcedStats.length >= 1) score += 1;\n\n return cap(score, 10);\n}\n\n/** 18. Entity Disambiguation */\nexport function scoreEntityDisambiguation(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (!h1Match) return 3; // neutral if no H1\n\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3);\n const primaryNoun = h1Words.sort((a, b) => b.length - a.length)[0] || '';\n if (!primaryNoun) return 3;\n\n // Primary entity defined early (first 500 chars)\n const first500 = text.slice(0, 500);\n if (new RegExp(`\\\\b${primaryNoun.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b[^.]*\\\\b(is|refers|defined|means)\\\\b`, 'i').test(first500)) {\n score += 4;\n }\n\n // Entity consistency\n const primaryRegex = new RegExp(`\\\\b${primaryNoun.toLowerCase().replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b`, 'gi');\n const mentions = text.match(primaryRegex) || [];\n if (mentions.length >= 3) score += 3;\n else if (mentions.length >= 1) score += 1;\n\n // Disambiguation signals\n if (/\\bunlike\\s+\\w/i.test(text) || /\\bcompared\\s+to\\s+\\w/i.test(text) || /\\bnot\\s+to\\s+be\\s+confused\\s+with\\b/i.test(text)) {\n score += 3;\n }\n\n return cap(score, 10);\n}\n\n/** 19. Extraction Friction Score */\nexport function scoreExtractionFriction(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Average sentence length\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 5);\n const avgLen = sentences.length > 0\n ? sentences.map(s => s.trim().split(/\\s+/).length).reduce((a, b) => a + b, 0) / sentences.length\n : 0;\n if (avgLen > 0 && avgLen < 20) score += 3;\n else if (avgLen >= 20 && avgLen <= 25) score += 2;\n else if (avgLen > 25 && avgLen <= 35) score += 1;\n\n // Voice-friendly lead (first 2-3 sentences after H1)\n const h1Match = html.match(/<h1[^>]*>[\\s\\S]*?<\\/h1>/i);\n if (h1Match) {\n const afterH1 = html.slice(html.indexOf(h1Match[0]) + h1Match[0].length);\n const leadParas = afterH1.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 3) || [];\n const leadText = leadParas.map(p => p.replace(/<[^>]*>/g, '')).join(' ').trim();\n const words = leadText.split(/\\s+/).length;\n if (words <= 75 && words > 10 && !/\\([^)]+\\)/.test(leadText)) {\n score += 3;\n }\n }\n\n // Jargon density (long words in first 500 words)\n const first500Words = text.split(/\\s+/).slice(0, 500).join(' ');\n const longWords = first500Words.match(/\\b[a-z]{10,}\\b/gi) || [];\n if (longWords.length <= 2) score += 2;\n else if (longWords.length <= 5) score += 1;\n\n // Hidden content penalty\n if (/display\\s*:\\s*none|visibility\\s*:\\s*hidden/i.test(html) && /<(div|section|p)[^>]*(?:style=[\"'][^\"']*display\\s*:\\s*none|hidden)[^>]*>/i.test(html)) {\n score = Math.max(0, score - 2);\n }\n\n return cap(score, 10);\n}\n\n/** 20. Image Context for AI */\nexport function scoreImageContextAI(html: string): number {\n let score = 0;\n\n const allImages = html.match(/<img\\s[^>]*>/gi) || [];\n if (allImages.length === 0) return 5; // neutral\n\n // <figure> + <figcaption>\n const figureBlocks = html.match(/<figure[\\s\\S]*?<\\/figure>/gi) || [];\n const figuresWithCaption = figureBlocks.filter(f => /<figcaption/i.test(f));\n const figureRatio = figuresWithCaption.length / allImages.length;\n if (figureRatio >= 0.5) score += 4;\n else if (figureRatio >= 0.25) score += 2;\n\n // Alt text quality (>5 words, not generic)\n let goodAltCount = 0;\n for (const img of allImages) {\n const altMatch = img.match(/\\salt=[\"']([^\"']+)[\"']/i);\n if (altMatch) {\n const altText = altMatch[1].trim();\n if (altText.split(/\\s+/).length > 5 && !/^(image|photo|picture|img|icon|logo|banner|screenshot)$/i.test(altText)) {\n goodAltCount++;\n }\n }\n }\n if (goodAltCount / allImages.length >= 0.5) score += 3;\n else if (goodAltCount > 0) score += 1;\n\n // Contextual placement\n const contextualImages = html.match(/<(article|section)[^>]*>[\\s\\S]*?<img[^>]*>[\\s\\S]*?<\\/\\1>/gi) || [];\n if (contextualImages.length > 0) score += 3;\n\n return cap(score, 10);\n}\n\nexport interface DuplicateDetail {\n headingA: string;\n headingB: string;\n similarity: number;\n sample: string;\n}\n\n/** 21. Duplicate Content Blocks */\nexport function scoreDuplicateContent(html: string): number {\n return scoreDuplicateContentDetailed(html).score;\n}\n\n/** Detailed duplicate detection — returns score + duplicate pairs found. */\nexport function scoreDuplicateContentDetailed(html: string): { score: number; duplicates: DuplicateDetail[] } {\n const sections = extractDuplicateContentSections(html);\n if (sections.length < 2) return { score: 10, duplicates: [] };\n\n // Total non-boilerplate paragraphs across all sections (for severity scaling)\n const totalParagraphs = sections.reduce((sum, s) => sum + s.paragraphs.length, 0);\n\n const duplicates: DuplicateDetail[] = [];\n let dupParagraphCount = 0;\n\n for (let i = 0; i < sections.length; i++) {\n for (let j = i + 1; j < sections.length; j++) {\n for (const pA of sections[i].paragraphs) {\n for (const pB of sections[j].paragraphs) {\n const sim = shingleJaccardSimilarity(pA.shingles, pB.shingles);\n if (sim > 0.4) {\n dupParagraphCount++;\n duplicates.push({\n headingA: sections[i].heading,\n headingB: sections[j].heading,\n similarity: Math.round(sim * 100),\n sample: pA.text.slice(0, 80),\n });\n break;\n }\n }\n }\n }\n }\n\n if (dupParagraphCount === 0) return { score: 10, duplicates: [] };\n\n // Scale severity by proportion of page content that's duplicated\n const dupRatio = totalParagraphs > 0 ? dupParagraphCount / totalParagraphs : 0;\n\n let score: number;\n if (dupParagraphCount === 1 && dupRatio <= 0.05) {\n score = 6;\n } else if (dupParagraphCount === 1) {\n score = 4;\n } else if (dupParagraphCount === 2) {\n score = 2;\n } else {\n // 3+ duplicate paragraphs — serious content quality issue\n score = 0;\n }\n\n return { score, duplicates };\n}\n\n// ─── Scoring function map ───────────────────────────────────────────────────\n\nconst SCORING_FUNCTIONS: Record<string, (html: string, url?: string) => number> = {\n schema_markup: scoreSchemaMarkup,\n qa_content_format: scoreQAFormat,\n clean_html: scoreCleanHtml,\n faq_section: scoreFaqSection,\n original_data: scoreOriginalData,\n query_answer_alignment: scoreQueryAnswerAlignment,\n content_freshness: scoreContentFreshness,\n table_list_extractability: scoreTableListExtractability,\n direct_answer_density: scoreDirectAnswerDensity,\n semantic_html: scoreSemanticHtml,\n fact_density: scoreFactDensity,\n definition_patterns: scoreDefinitionPatterns,\n canonical_url: scoreCanonicalUrl,\n visible_date_signal: scoreVisibleDateSignal,\n citation_ready_writing: scoreCitationReadyWriting,\n answer_first_placement: scoreAnswerFirstPlacement,\n evidence_packaging: scoreEvidencePackaging,\n helpful_purpose_alignment: scoreHelpfulPurposeAlignment,\n first_hand_experience_signals: scoreFirstHandExperienceSignals,\n entity_disambiguation: scoreEntityDisambiguation,\n extraction_friction: scoreExtractionFriction,\n creator_transparency: scoreCreatorTransparency,\n methodology_transparency: scoreMethodologyTransparency,\n image_context_ai: scoreImageContextAI,\n duplicate_content: scoreDuplicateContent,\n};\n\n// ─── Main API ───────────────────────────────────────────────────────────────\n\n/**\n * Score a single page against 25 AEO criteria.\n * Returns a 0-100 AEO score (capped at 75 for single pages) and individual criterion scores.\n */\nexport function scorePage(html: string, url?: string): PageScoreResult {\n let totalWeight = 0;\n let weightedSum = 0;\n const criterionScores: PageCriterionScore[] = [];\n\n for (const [criterion, { weight, label }] of Object.entries(PAGE_CRITERIA)) {\n const fn = SCORING_FUNCTIONS[criterion]!;\n const score = fn(html, url);\n\n criterionScores.push({ criterion, criterion_label: label, score, weight });\n weightedSum += (score / 10) * weight * 100;\n totalWeight += weight;\n }\n\n let aeoScore = totalWeight === 0 ? 0 : Math.round(weightedSum / totalWeight);\n\n // ─── Duplication Gate ──────────────────────────────────────────────────\n // Pages with significant duplicate content blocks cannot achieve high\n // scores — LLMs will flag copy-pasted sections as low-quality content\n // regardless of how well other criteria perform.\n // dup score 0: max 35 (severe — majority of content is duplicated)\n // dup score 1-2: max 45\n // dup score 3-4: max 55\n // dup score 5-6: max 65\n // dup score ≥ 7: no cap\n const dupScore = criterionScores.find(c => c.criterion === 'duplicate_content')?.score ?? 10;\n if (dupScore <= 6) {\n const dupCap = 35 + dupScore * 5;\n aeoScore = Math.min(aeoScore, dupCap);\n }\n\n const scoreCapped = aeoScore > 75;\n if (scoreCapped) aeoScore = 75;\n\n return { aeoScore, criterionScores, scoreCapped };\n}\n\n/**\n * Score all crawled pages (homepage + blogSample).\n */\nexport function scoreAllPages(siteData: SiteData): PageScoreResult[] {\n const results: PageScoreResult[] = [];\n\n if (siteData.homepage) {\n const url = siteData.protocol ? `${siteData.protocol}://${siteData.domain}` : undefined;\n results.push(scorePage(siteData.homepage.text, url));\n }\n\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n const url = page.finalUrl || undefined;\n results.push(scorePage(page.text, url));\n }\n }\n\n return results;\n}\n","/**\n * Per-page analysis for instant audit.\n * Runs 15 deterministic checks on each crawled page (no LLM).\n */\n\nimport type { PageCategory, SiteData } from './site-crawler.js';\nimport type { PageIssue, PageReview } from './types.js';\nimport { scorePage, scoreDuplicateContentDetailed } from './page-scorer.js';\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction extractTitle(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction countWords(text: string): number {\n if (!text) return 0;\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\n// ─── Individual checks ──────────────────────────────────────────────────────\n\nfunction checkMissingTitle(html: string): PageIssue | null {\n const hasTitle = /<title[^>]*>[\\s\\S]*?<\\/title>/i.test(html);\n if (!hasTitle) {\n return { check: 'missing-title', label: 'Missing <title> tag', severity: 'error' };\n }\n const title = extractTitle(html);\n if (!title) {\n return { check: 'missing-title', label: 'Empty <title> tag', severity: 'error' };\n }\n return null;\n}\n\nfunction checkMissingMetaDescription(html: string): PageIssue | null {\n const hasDesc = /<meta\\s[^>]*name=[\"']description[\"'][^>]*content=[\"'][^\"']+[\"']/i.test(html)\n || /<meta\\s[^>]*content=[\"'][^\"']+[\"'][^>]*name=[\"']description[\"']/i.test(html);\n if (!hasDesc) {\n return { check: 'missing-meta-description', label: 'Missing meta description', severity: 'error' };\n }\n return null;\n}\n\nfunction checkNoH1(html: string): PageIssue | null {\n const h1Matches = html.match(/<h1[\\s>]/gi);\n if (!h1Matches || h1Matches.length === 0) {\n return { check: 'no-h1', label: 'No <h1> tag', severity: 'error' };\n }\n return null;\n}\n\nfunction checkMultipleH1(html: string): PageIssue | null {\n const h1Matches = html.match(/<h1[\\s>]/gi);\n if (h1Matches && h1Matches.length > 1) {\n return { check: 'multiple-h1', label: `Multiple <h1> tags (${h1Matches.length})`, severity: 'error' };\n }\n return null;\n}\n\nfunction checkNoSchema(html: string): PageIssue | null {\n const hasLdJson = /<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>/i.test(html);\n if (!hasLdJson) {\n return { check: 'no-schema', label: 'No JSON-LD structured data', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkMissingCanonical(html: string): PageIssue | null {\n const hasCanonical = /<link[^>]*rel=[\"']canonical[\"'][^>]*>/i.test(html);\n if (!hasCanonical) {\n return { check: 'missing-canonical', label: 'Missing canonical link', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkMissingOgTags(html: string): PageIssue | null {\n const hasOg = /<meta\\s[^>]*property=[\"']og:/i.test(html);\n if (!hasOg) {\n return { check: 'missing-og-tags', label: 'No Open Graph tags', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkThinContent(wordCount: number): PageIssue | null {\n if (wordCount < 300) {\n return { check: 'thin-content', label: `Thin content (${wordCount} words)`, severity: 'warning' };\n }\n return null;\n}\n\nfunction checkImagesMissingAlt(html: string): PageIssue | null {\n const imgTags = html.match(/<img\\s[^>]*>/gi) || [];\n if (imgTags.length === 0) return null;\n\n let missingAlt = 0;\n for (const img of imgTags) {\n const hasAlt = /\\salt=[\"'][^\"']+[\"']/i.test(img);\n const hasEmptyAlt = /\\salt=[\"'][\"']/i.test(img); // decorative\n if (!hasAlt && !hasEmptyAlt) missingAlt++;\n }\n\n if (missingAlt > 0) {\n return {\n check: 'images-missing-alt',\n label: `${missingAlt} image${missingAlt > 1 ? 's' : ''} missing alt text`,\n severity: 'warning',\n };\n }\n return null;\n}\n\nfunction checkNoInternalLinks(html: string, url: string): PageIssue | null {\n let domain: string;\n try {\n domain = new URL(url).hostname.replace(/^www\\./, '').toLowerCase();\n } catch {\n return null;\n }\n\n const links = html.match(/<a\\s[^>]*href=[\"']([^\"'#]*?)[\"'][^>]*>/gi) || [];\n let internalCount = 0;\n\n for (const link of links) {\n const hrefMatch = link.match(/href=[\"']([^\"'#]*?)[\"']/i);\n if (!hrefMatch) continue;\n const href = hrefMatch[1];\n\n if (href.startsWith('/') && !href.startsWith('//')) {\n internalCount++;\n } else if (href.startsWith('http')) {\n try {\n const linkDomain = new URL(href).hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain === domain) internalCount++;\n } catch {\n // skip\n }\n }\n }\n\n if (internalCount === 0) {\n return { check: 'no-internal-links', label: 'No internal links found', severity: 'warning' };\n }\n return null;\n}\n\n// ─── Strength checks ────────────────────────────────────────────────────────\n\nfunction checkHasStructuredData(html: string): PageIssue | null {\n const ldBlocks = html.match(/<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n if (ldBlocks.length === 0) return null;\n\n const types = new Set<string>();\n for (const block of ldBlocks) {\n const content = block.replace(/<\\/?script[^>]*>/gi, '');\n const typeMatches = content.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/g) || [];\n for (const m of typeMatches) {\n const t = m.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/);\n if (t) types.add(t[1]);\n }\n }\n\n if (types.size > 0) {\n return {\n check: 'has-structured-data',\n label: `JSON-LD: ${Array.from(types).join(', ')}`,\n severity: 'info',\n };\n }\n return null;\n}\n\nfunction checkHasQuestionHeadings(html: string): PageIssue | null {\n const headings = html.match(/<h[2-4][^>]*>[\\s\\S]*?<\\/h[2-4]>/gi) || [];\n let questionCount = 0;\n\n for (const h of headings) {\n const text = h.replace(/<[^>]*>/g, '').trim();\n if (/\\?$/.test(text) || /^(what|how|why|when|where|who|which|can|do|does|is|are|should|will)\\b/i.test(text)) {\n questionCount++;\n }\n }\n\n if (questionCount > 0) {\n return {\n check: 'has-question-headings',\n label: `${questionCount} question-format heading${questionCount > 1 ? 's' : ''}`,\n severity: 'info',\n };\n }\n return null;\n}\n\n// ─── V2 issue checks ───────────────────────────────────────────────────────\n\nfunction checkNoAnswerBlock(html: string): PageIssue | null {\n const bodyMatch = html.match(/<body[^>]*>([\\s\\S]*)/i);\n const bodyHtml = bodyMatch ? bodyMatch[1] : html;\n const earlyParagraphs = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 5) || [];\n const bodyText = bodyHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n const first300Words = bodyText.split(/\\s+/).slice(0, 300).join(' ');\n\n for (const p of earlyParagraphs) {\n const pText = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = pText.split(/\\s+/).length;\n if (wordCount >= 40 && wordCount <= 80 && first300Words.includes(pText.slice(0, 50))) {\n return null; // Found an answer block\n }\n }\n\n return { check: 'no-answer-block', label: 'No short answer block (40-80 words) in first 300 words', severity: 'warning' };\n}\n\nfunction checkNoEvidence(html: string, url: string): PageIssue | null {\n const text = getTextContent(html);\n\n // Check for inline citations\n const paragraphs = html.match(/<p[^>]*>[\\s\\S]*?<\\/p>/gi) || [];\n let inlineCitations = 0;\n for (const p of paragraphs) {\n const links = p.match(/<a[^>]*href=[\"'](https?:\\/\\/[^\"']+)[\"'][^>]*>/gi) || [];\n inlineCitations += links.length;\n }\n\n // Check for attribution phrases\n const attributionPhrases = text.match(/\\baccording\\s+to\\s+[A-Z]|\\b[A-Z][a-z]+\\s+(found|says|reports|notes|states)\\b/gi) || [];\n\n if (inlineCitations === 0 && attributionPhrases.length === 0) {\n return { check: 'no-evidence', label: 'No inline citations or attribution phrases', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkHasCitationReadyContent(html: string): PageIssue | null {\n const text = getTextContent(html);\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10);\n\n // Check for self-contained quotable sentences with evidence\n let quotable = 0;\n for (const s of sentences) {\n const hasData = /\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(s);\n const hasSource = /\\b[A-Z][a-z]+(?:\\s+[A-Z][a-z]+)*\\b/.test(s);\n const isShort = s.trim().split(/\\s+/).length < 30;\n if (hasData && hasSource && isShort) quotable++;\n }\n\n if (quotable >= 3) {\n return { check: 'has-citation-ready-content', label: `${quotable} citation-ready sentences with data and sources`, severity: 'info' };\n }\n return null;\n}\n\nfunction checkDuplicateContentBlocks(html: string): PageIssue | null {\n const { score, duplicates } = scoreDuplicateContentDetailed(html);\n if (score <= 6 && duplicates.length > 0) {\n const first = duplicates[0];\n const label = duplicates.length === 1\n ? `Duplicate content: '${first.headingA}' and '${first.headingB}' share ${first.similarity}% similar text (\"${first.sample}...\")`\n : `${duplicates.length} duplicate blocks found (e.g. '${first.headingA}' and '${first.headingB}' — \"${first.sample}...\")`;\n return { check: 'duplicate-content', label, severity: score <= 3 ? 'error' : 'warning' };\n }\n return null;\n}\n\n// ─── Main analyzers ─────────────────────────────────────────────────────────\n\nexport function analyzePage(html: string, url: string, category: PageCategory): PageReview {\n const title = extractTitle(html);\n const textContent = getTextContent(html);\n const wordCount = countWords(textContent);\n\n const issues: PageIssue[] = [];\n const strengths: PageIssue[] = [];\n\n // Issue checks\n const issueChecks = [\n checkMissingTitle(html),\n checkMissingMetaDescription(html),\n checkNoH1(html),\n checkMultipleH1(html),\n checkNoSchema(html),\n checkMissingCanonical(html),\n checkMissingOgTags(html),\n checkThinContent(wordCount),\n checkImagesMissingAlt(html),\n checkNoInternalLinks(html, url),\n checkNoAnswerBlock(html),\n checkNoEvidence(html, url),\n checkDuplicateContentBlocks(html),\n ];\n\n for (const result of issueChecks) {\n if (result) issues.push(result);\n }\n\n // Strength checks\n const strengthChecks = [\n checkHasStructuredData(html),\n checkHasQuestionHeadings(html),\n checkHasCitationReadyContent(html),\n ];\n\n for (const result of strengthChecks) {\n if (result) strengths.push(result);\n }\n\n const { aeoScore, criterionScores } = scorePage(html, url);\n return { url, title, category, wordCount, issues, strengths, aeoScore, criterionScores };\n}\n\nexport function analyzeAllPages(siteData: SiteData): PageReview[] {\n const reviews: PageReview[] = [];\n\n // Analyze homepage\n if (siteData.homepage) {\n const url = `${siteData.protocol}://${siteData.domain}`;\n reviews.push(analyzePage(siteData.homepage.text, url, siteData.homepage.category || 'homepage'));\n }\n\n // Analyze all pages in blogSample\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n const url = page.finalUrl || 'unknown';\n reviews.push(analyzePage(page.text, url, page.category || 'content'));\n }\n }\n\n return reviews;\n}\n","/**\n * Link graph analysis for full-site AEO audits.\n * Builds an internal link graph, detects orphan pages, pillar pages,\n * hub pages, and topic clusters from crawled page data.\n */\n\nimport type { FetchResult, PageCategory } from './site-crawler.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface LinkEdge {\n source: string;\n target: string;\n anchorText: string;\n}\n\nexport interface PageNode {\n url: string;\n title: string;\n wordCount: number;\n category: PageCategory;\n inDegree: number;\n outDegree: number;\n depth: number;\n isPillar: boolean;\n isHub: boolean;\n isOrphan: boolean;\n}\n\nexport interface TopicCluster {\n pillarUrl: string;\n pillarTitle: string;\n spokes: string[];\n cohesion: number;\n}\n\nexport interface LinkGraphStats {\n totalPages: number;\n totalEdges: number;\n orphanPages: number;\n pillarPages: number;\n hubPages: number;\n avgDepth: number;\n maxDepth: number;\n clusters: number;\n}\n\nexport interface LinkGraph {\n nodes: Map<string, PageNode>;\n edges: LinkEdge[];\n stats: LinkGraphStats;\n clusters: TopicCluster[];\n}\n\n// ─── Serialization (Map -> plain object for JSON) ───────────────────────────\n\nexport interface SerializedLinkGraph {\n nodes: PageNode[];\n stats: LinkGraphStats;\n clusters: TopicCluster[];\n}\n\nexport function serializeLinkGraph(graph: LinkGraph): SerializedLinkGraph {\n return {\n nodes: Array.from(graph.nodes.values()),\n stats: graph.stats,\n clusters: graph.clusters,\n };\n}\n\n// ─── URL normalization (matches full-site-crawler.ts:421-429) ───────────────\n\nfunction normalizeUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return (parsed.origin + parsed.pathname.replace(/\\/+$/, '') + parsed.search).toLowerCase();\n } catch {\n return url.toLowerCase();\n }\n}\n\n// ─── Resource / skip-path patterns (same as full-site-crawler.ts) ───────────\n\nconst RESOURCE_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|pdf|xml|txt|woff|woff2|ttf|eot|mp4|mp3|webp|avif|zip|gz|tar|json)$/i;\n\nconst SKIP_PATH_PATTERNS = /^\\/(api|wp-admin|wp-json|static|assets|_next|auth|login|signup|cart|checkout|admin|feed|xmlrpc)\\b/i;\n\n// ─── HTML helpers (mirrors page-analyzer.ts) ────────────────────────────────\n\nfunction extractTitle(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction countWords(text: string): number {\n if (!text) return 0;\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\n// ─── Link extraction with anchor text ───────────────────────────────────────\n\n/**\n * Extract internal links from HTML with their anchor text.\n * Similar to extractInternalLinks in full-site-crawler.ts but returns LinkEdge[].\n */\nexport function extractLinksWithAnchors(\n html: string,\n sourceUrl: string,\n domain: string,\n): LinkEdge[] {\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const edges: LinkEdge[] = [];\n const seen = new Set<string>();\n\n // Match <a ...>...</a> tags to get both href and anchor text\n const anchorRegex = /<a\\s[^>]*href=\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>/gi;\n let match: RegExpExecArray | null;\n\n while ((match = anchorRegex.exec(html)) !== null) {\n const href = match[1];\n const rawAnchor = match[2];\n\n if (!href || !href.trim()) continue;\n\n let fullUrl: string;\n\n if (href.startsWith('//')) {\n fullUrl = `https:${href}`;\n } else if (href.startsWith('/')) {\n if (href.startsWith('/#')) continue;\n fullUrl = href === '/' ? `https://${domain}` : `https://${domain}${href}`;\n } else if (href.startsWith('http')) {\n fullUrl = href;\n } else if (\n href.startsWith('#') || href.startsWith('?') ||\n href.startsWith('mailto:') || href.startsWith('tel:') ||\n href.startsWith('javascript:')\n ) {\n continue;\n } else {\n fullUrl = `https://${domain}/${href}`;\n }\n\n try {\n const parsed = new URL(fullUrl);\n const linkDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n\n parsed.hash = '';\n const path = parsed.pathname;\n\n // Allow root path (homepage) - only skip empty string (shouldn't happen)\n if (path === '') continue;\n if (RESOURCE_EXTENSIONS.test(path)) continue;\n if (SKIP_PATH_PATTERNS.test(path)) continue;\n\n const normalized = normalizeUrl(fullUrl);\n const sourceNorm = normalizeUrl(sourceUrl);\n\n // Skip self-links\n if (normalized === sourceNorm) continue;\n\n // Deduplicate edges from same source to same target\n const edgeKey = `${sourceNorm}->${normalized}`;\n if (seen.has(edgeKey)) continue;\n seen.add(edgeKey);\n\n // Strip HTML tags from anchor text\n const anchorText = rawAnchor.replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\n\n edges.push({\n source: sourceNorm,\n target: normalized,\n anchorText,\n });\n } catch {\n continue;\n }\n }\n\n return edges;\n}\n\n// ─── BFS depth calculation ──────────────────────────────────────────────────\n\n/**\n * BFS from homepage to compute click depth for each node.\n * Unreachable pages get Infinity.\n */\nexport function calculateDepths(\n nodes: Map<string, PageNode>,\n adjacency: Map<string, Set<string>>,\n homepageUrl: string,\n): void {\n const homeNorm = normalizeUrl(homepageUrl);\n\n // Set all to Infinity first\n for (const node of nodes.values()) {\n node.depth = Infinity;\n }\n\n // Homepage gets depth 0\n const homeNode = nodes.get(homeNorm);\n if (homeNode) {\n homeNode.depth = 0;\n }\n\n // BFS\n const queue: string[] = [homeNorm];\n const visited = new Set<string>([homeNorm]);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n const currentNode = nodes.get(current);\n if (!currentNode) continue;\n const nextDepth = currentNode.depth + 1;\n\n const neighbors = adjacency.get(current);\n if (!neighbors) continue;\n\n for (const neighbor of neighbors) {\n if (visited.has(neighbor)) continue;\n visited.add(neighbor);\n\n const neighborNode = nodes.get(neighbor);\n if (neighborNode) {\n neighborNode.depth = nextDepth;\n queue.push(neighbor);\n }\n }\n }\n}\n\n// ─── Pillar detection ───────────────────────────────────────────────────────\n\nconst PILLAR_CATEGORIES: Set<PageCategory> = new Set(['blog', 'content', 'resources', 'docs']);\n\n/**\n * Detect pillar pages: long content with significant in/out linking.\n * wordCount >= 1500 AND inDegree >= 3 AND outDegree >= 3\n * AND category in blog/content/resources/docs AND not homepage (depth > 0).\n */\nexport function detectPillars(nodes: Map<string, PageNode>): void {\n for (const node of nodes.values()) {\n node.isPillar =\n node.wordCount >= 1500 &&\n node.inDegree >= 3 &&\n node.outDegree >= 3 &&\n PILLAR_CATEGORIES.has(node.category) &&\n node.depth > 0;\n }\n}\n\n// ─── Hub detection ──────────────────────────────────────────────────────────\n\nconst HUB_CATEGORIES: Set<PageCategory> = new Set(['homepage', 'resources', 'docs']);\n\n/**\n * Detect hub pages: high outDegree navigation/index pages.\n * outDegree >= 10 with category homepage/resources/docs, OR outDegree >= 15.\n */\nexport function detectHubs(nodes: Map<string, PageNode>): void {\n for (const node of nodes.values()) {\n node.isHub =\n (node.outDegree >= 10 && HUB_CATEGORIES.has(node.category)) ||\n node.outDegree >= 15;\n }\n}\n\n// ─── Cluster detection ──────────────────────────────────────────────────────\n\n/**\n * Detect topic clusters around pillar pages.\n * For each pillar, gather spokes (pages linked to/from pillar).\n * Cohesion = actual edges between cluster members / possible edges.\n * Minimum 2 spokes to form a cluster.\n */\nexport function detectClusters(\n nodes: Map<string, PageNode>,\n edges: LinkEdge[],\n): TopicCluster[] {\n const clusters: TopicCluster[] = [];\n\n // Build quick lookup for edges\n const edgeSet = new Set<string>();\n for (const edge of edges) {\n edgeSet.add(`${edge.source}->${edge.target}`);\n }\n\n for (const node of nodes.values()) {\n if (!node.isPillar) continue;\n\n const pillarNorm = normalizeUrl(node.url);\n const spokeSet = new Set<string>();\n\n // Find spokes: pages linked to/from this pillar\n for (const edge of edges) {\n if (edge.source === pillarNorm && nodes.has(edge.target)) {\n spokeSet.add(edge.target);\n }\n if (edge.target === pillarNorm && nodes.has(edge.source)) {\n spokeSet.add(edge.source);\n }\n }\n\n // Remove the pillar itself from spokes\n spokeSet.delete(pillarNorm);\n\n if (spokeSet.size < 2) continue;\n\n const spokes = Array.from(spokeSet);\n\n // Compute cohesion: edges between cluster members (pillar + spokes)\n const members = [pillarNorm, ...spokes];\n let actualEdges = 0;\n const possibleEdges = members.length * (members.length - 1); // directed\n\n for (const from of members) {\n for (const to of members) {\n if (from === to) continue;\n if (edgeSet.has(`${from}->${to}`)) {\n actualEdges++;\n }\n }\n }\n\n const cohesion = possibleEdges > 0 ? Math.round((actualEdges / possibleEdges) * 100) : 0;\n\n clusters.push({\n pillarUrl: node.url,\n pillarTitle: node.title,\n spokes,\n cohesion,\n });\n }\n\n return clusters;\n}\n\n// ─── Main graph builder ─────────────────────────────────────────────────────\n\n/**\n * Build a link graph from crawled pages.\n * @param pages - Array of FetchResult from full-site crawl\n * @param domain - The site domain\n * @param homepageUrl - Full URL of the homepage (e.g. https://example.com)\n */\nexport function buildLinkGraph(\n pages: FetchResult[],\n domain: string,\n homepageUrl: string,\n): LinkGraph {\n const nodes = new Map<string, PageNode>();\n const allEdges: LinkEdge[] = [];\n const adjacency = new Map<string, Set<string>>();\n const inDegreeMap = new Map<string, number>();\n\n // Phase 1: Create nodes from all pages\n for (const page of pages) {\n const url = page.finalUrl || `https://${domain}`;\n const norm = normalizeUrl(url);\n\n if (nodes.has(norm)) continue;\n\n const title = extractTitle(page.text);\n const text = getTextContent(page.text);\n const wordCount = countWords(text);\n\n nodes.set(norm, {\n url: norm,\n title,\n wordCount,\n category: page.category || 'content',\n inDegree: 0,\n outDegree: 0,\n depth: Infinity,\n isPillar: false,\n isHub: false,\n isOrphan: false,\n });\n }\n\n // Phase 2: Extract edges\n for (const page of pages) {\n const url = page.finalUrl || `https://${domain}`;\n const sourceNorm = normalizeUrl(url);\n\n const edges = extractLinksWithAnchors(page.text, url, domain);\n\n for (const edge of edges) {\n // Normalize edge source/target to match node keys\n const targetNorm = normalizeUrl(edge.target);\n\n // Only count edges to known pages\n if (!nodes.has(targetNorm)) continue;\n\n allEdges.push({\n source: sourceNorm,\n target: targetNorm,\n anchorText: edge.anchorText,\n });\n\n // Build adjacency\n if (!adjacency.has(sourceNorm)) {\n adjacency.set(sourceNorm, new Set());\n }\n adjacency.get(sourceNorm)!.add(targetNorm);\n\n // Track in-degree\n inDegreeMap.set(targetNorm, (inDegreeMap.get(targetNorm) || 0) + 1);\n }\n }\n\n // Phase 3: Set degrees\n for (const [url, node] of nodes) {\n node.inDegree = inDegreeMap.get(url) || 0;\n node.outDegree = adjacency.get(url)?.size || 0;\n }\n\n // Phase 4: BFS depths\n calculateDepths(nodes, adjacency, homepageUrl);\n\n // Phase 5: Detect pillars and hubs\n detectPillars(nodes);\n detectHubs(nodes);\n\n // Phase 6: Mark orphans (zero inbound links, not homepage)\n const homeNorm = normalizeUrl(homepageUrl);\n for (const [url, node] of nodes) {\n node.isOrphan = node.inDegree === 0 && url !== homeNorm;\n }\n\n // Phase 7: Detect clusters\n const clusters = detectClusters(nodes, allEdges);\n\n // Phase 8: Compute stats\n const depthValues = Array.from(nodes.values())\n .map(n => n.depth)\n .filter(d => d !== Infinity);\n const avgDepth = depthValues.length > 0\n ? Math.round((depthValues.reduce((s, d) => s + d, 0) / depthValues.length) * 10) / 10\n : 0;\n const maxDepth = depthValues.length > 0 ? Math.max(...depthValues) : 0;\n\n const stats: LinkGraphStats = {\n totalPages: nodes.size,\n totalEdges: allEdges.length,\n orphanPages: Array.from(nodes.values()).filter(n => n.isOrphan).length,\n pillarPages: Array.from(nodes.values()).filter(n => n.isPillar).length,\n hubPages: Array.from(nodes.values()).filter(n => n.isHub).length,\n avgDepth,\n maxDepth,\n clusters: clusters.length,\n };\n\n return { nodes, edges: allEdges, stats, clusters };\n}\n","/**\n * Fix Plan Engine - generates actionable, phased fix plans from audit scores.\n * Runs alongside the existing opportunities system (no breaking changes).\n * Uses criterion scores, optional per-page data, and optional link graph\n * to produce structured fix plans with code examples and dependency ordering.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\nimport type { PageReview, PageCriterionScore } from './types.js';\nimport type { LinkGraph } from './link-graph.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface FixAction {\n id: string;\n criterion: string;\n criterionId: string;\n title: string;\n description: string;\n impact: 'critical' | 'high' | 'medium' | 'low';\n effort: 'trivial' | 'low' | 'medium' | 'high';\n impactScore: number;\n category: 'content' | 'structure' | 'discovery' | 'trust';\n steps: string[];\n codeExample?: string;\n successCriteria: string;\n dependsOn?: string[];\n affectedPages?: string[];\n pageCount?: number;\n}\n\nexport interface FixPhase {\n phase: number;\n title: string;\n description: string;\n fixes: FixAction[];\n estimatedImpact: number;\n}\n\nexport interface FixPlanSummary {\n criticalCount: number;\n highCount: number;\n mediumCount: number;\n lowCount: number;\n quickWinCount: number;\n topOpportunity: string;\n estimatedTotalEffort: string;\n}\n\nexport interface FixPlan {\n domain: string;\n generatedAt: string;\n overallScore: number;\n projectedScore: number;\n totalFixes: number;\n phases: FixPhase[];\n quickWins: FixAction[];\n summary: FixPlanSummary;\n}\n\n// ─── Criterion weights (mirrored from scoring.ts) ────────────────────────────\n\nconst CRITERION_WEIGHTS: Record<string, number> = {\n // Content Substance (~55%)\n topic_coherence: 0.14,\n original_data: 0.10,\n content_depth: 0.07,\n fact_density: 0.06,\n direct_answer_density: 0.05,\n qa_content_format: 0.04,\n query_answer_alignment: 0.04,\n faq_section: 0.03,\n helpful_purpose_alignment: 0.03,\n first_hand_experience_signals: 0.03,\n // Content Organization (~30%)\n entity_consistency: 0.05,\n internal_linking: 0.04,\n content_freshness: 0.04,\n schema_markup: 0.03,\n author_schema_depth: 0.03,\n table_list_extractability: 0.03,\n creator_transparency: 0.02,\n methodology_transparency: 0.02,\n definition_patterns: 0.015,\n visible_date_signal: 0.015,\n semantic_html: 0.02,\n clean_html: 0.02,\n // Technical Plumbing (~15%)\n content_cannibalization: 0.02,\n duplicate_content: 0.05, cross_page_duplication: 0.03,\n llms_txt: 0.01,\n robots_txt: 0.01,\n content_velocity: 0.02,\n content_licensing: 0.01,\n sitemap_completeness: 0.01,\n canonical_url: 0.005,\n rss_feed: 0.00,\n schema_coverage: 0.00,\n speakable_schema: 0.00,\n // V2 Criteria (~15%)\n citation_ready_writing: 0.04,\n answer_first_placement: 0.03,\n evidence_packaging: 0.03,\n entity_disambiguation: 0.02,\n extraction_friction: 0.02,\n image_context_ai: 0.005,\n};\n\n// ─── Phase configuration ────────────────────────────────────────────────────\n\nconst PHASE_CONFIG: { phase: number; title: string; description: string; criteria: string[] }[] = [\n {\n phase: 1,\n title: 'Foundation',\n description: 'Discovery and structural fixes that enable AI crawlers to access and parse your content.',\n criteria: ['robots_txt', 'llms_txt', 'canonical_url', 'clean_html', 'sitemap_completeness'],\n },\n {\n phase: 2,\n title: 'Content',\n description: 'Content quality and format improvements that make your pages citable by AI engines.',\n criteria: [\n 'qa_content_format', 'faq_section', 'original_data', 'definition_patterns',\n 'direct_answer_density', 'fact_density', 'content_freshness',\n 'table_list_extractability', 'query_answer_alignment', 'visible_date_signal',\n 'topic_coherence', 'content_depth',\n 'citation_ready_writing', 'answer_first_placement', 'evidence_packaging',\n 'entity_disambiguation', 'helpful_purpose_alignment', 'first_hand_experience_signals',\n 'duplicate_content', 'cross_page_duplication',\n ],\n },\n {\n phase: 3,\n title: 'Authority',\n description: 'Trust signals, schema depth, and semantic structure that establish credibility with AI engines.',\n criteria: [\n 'schema_markup', 'schema_coverage', 'speakable_schema', 'author_schema_depth',\n 'creator_transparency', 'methodology_transparency', 'content_licensing', 'entity_consistency', 'semantic_html',\n 'image_context_ai', 'extraction_friction',\n ],\n },\n {\n phase: 4,\n title: 'Architecture',\n description: 'Site architecture, linking patterns, and publishing cadence that support long-term AI visibility.',\n criteria: ['internal_linking', 'content_velocity', 'content_cannibalization', 'rss_feed'],\n },\n];\n\n// ─── Criterion → category mapping ───────────────────────────────────────────\n\nconst CRITERION_CATEGORY: Record<string, FixAction['category']> = {\n robots_txt: 'discovery', llms_txt: 'discovery', canonical_url: 'structure',\n clean_html: 'structure', sitemap_completeness: 'discovery',\n qa_content_format: 'content', faq_section: 'content', original_data: 'content',\n definition_patterns: 'content', direct_answer_density: 'content', fact_density: 'content',\n content_freshness: 'content', table_list_extractability: 'content',\n query_answer_alignment: 'content', visible_date_signal: 'content',\n schema_markup: 'trust', schema_coverage: 'trust', speakable_schema: 'trust',\n author_schema_depth: 'trust', creator_transparency: 'trust', methodology_transparency: 'trust',\n content_licensing: 'trust', entity_consistency: 'trust',\n semantic_html: 'structure', internal_linking: 'structure', content_velocity: 'content',\n content_cannibalization: 'content', duplicate_content: 'content', cross_page_duplication: 'content', rss_feed: 'discovery',\n topic_coherence: 'content', content_depth: 'content',\n citation_ready_writing: 'content', answer_first_placement: 'content',\n evidence_packaging: 'content', helpful_purpose_alignment: 'content', first_hand_experience_signals: 'content', entity_disambiguation: 'content',\n extraction_friction: 'structure', image_context_ai: 'trust',\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction impactFromScore(score: number): FixAction['impact'] {\n if (score <= 3) return 'critical';\n if (score <= 5) return 'high';\n if (score <= 7) return 'medium';\n return 'low';\n}\n\nfunction effortForCriterion(criterion: string, score: number): FixAction['effort'] {\n // Low-effort criteria (file creation, config changes)\n const trivialCriteria = ['llms_txt', 'robots_txt', 'canonical_url', 'content_licensing', 'visible_date_signal'];\n const lowCriteria = ['rss_feed', 'sitemap_completeness', 'speakable_schema', 'author_schema_depth', 'creator_transparency', 'methodology_transparency', 'semantic_html', 'definition_patterns', 'content_freshness'];\n const highCriteria = ['original_data', 'content_velocity', 'content_cannibalization'];\n\n if (trivialCriteria.includes(criterion)) return score <= 3 ? 'low' : 'trivial';\n if (lowCriteria.includes(criterion)) return score <= 3 ? 'medium' : 'low';\n if (highCriteria.includes(criterion)) return score <= 5 ? 'high' : 'medium';\n return score <= 3 ? 'medium' : 'low';\n}\n\nfunction getAffectedPages(\n criterion: string,\n pages?: PageReview[],\n threshold = 7,\n): string[] | undefined {\n if (!pages || pages.length === 0) return undefined;\n const affected = pages.filter(p => {\n const cs = p.criterionScores?.find((c: PageCriterionScore) => c.criterion === criterion);\n return cs && cs.score < threshold;\n });\n if (affected.length === 0) return undefined;\n return affected.map(p => p.url);\n}\n\nfunction effortToHours(effort: FixAction['effort']): number {\n switch (effort) {\n case 'trivial': return 0.5;\n case 'low': return 1;\n case 'medium': return 3;\n case 'high': return 8;\n }\n}\n\n// ─── Fix generator type ─────────────────────────────────────────────────────\n\ntype FixGenerator = (\n criterion: CriterionResult,\n pages?: PageReview[],\n linkGraph?: LinkGraph,\n) => FixAction[];\n\n// ─── Fix Generators ────────────────────────────────────────────────────────\n\nconst FIX_GENERATORS: Record<string, FixGenerator> = {\n\n llms_txt: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('llms_txt', c.score);\n const fixes: FixAction[] = [];\n if (c.score <= 6) {\n fixes.push({\n id: 'fix-llms-txt-create',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create /llms.txt file',\n description: 'Add a machine-readable llms.txt file at your domain root that describes your site, services, and key pages for AI engines.',\n impact, effort,\n impactScore: 0, // calculated later\n category: 'discovery',\n steps: [\n 'Create a file named llms.txt in your site root',\n 'Add site name, description, and core URLs in markdown format',\n 'Include key service/product pages and their descriptions',\n 'Deploy and verify access at yourdomain.com/llms.txt',\n ],\n codeExample: `# Site Name\\n> One-line site description\\n\\n## Core Pages\\n- [About](/about): Company overview\\n- [Services](/services): Service offerings\\n- [Blog](/blog): Latest articles\\n\\n## Key Topics\\n- Topic 1\\n- Topic 2`,\n successCriteria: '/llms.txt returns 200 with valid markdown content',\n });\n }\n if (c.score <= 3) {\n fixes.push({\n id: 'fix-llms-txt-full',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add llms-full.txt with extended content',\n description: 'Create a comprehensive llms-full.txt with detailed page descriptions, content summaries, and topic taxonomy.',\n impact: 'medium', effort: 'low',\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create llms-full.txt alongside llms.txt',\n 'Include full page descriptions with word counts',\n 'Add topic categories and content clusters',\n 'Link from llms.txt to llms-full.txt',\n ],\n successCriteria: '/llms-full.txt returns 200 with comprehensive site map',\n });\n }\n return fixes;\n },\n\n schema_markup: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('schema_markup', c.score);\n const affected = getAffectedPages('schema_markup', pages);\n const fixes: FixAction[] = [{\n id: 'fix-schema-markup',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add JSON-LD structured data',\n description: 'Implement Organization, WebSite, and page-specific schema.org JSON-LD to help AI engines extract your content.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add Organization JSON-LD to your homepage with name, url, logo, sameAs',\n 'Add WebSite schema with SearchAction',\n 'Add page-specific schema (Article, Service, Product, FAQPage) to relevant pages',\n 'Validate with Google Rich Results Test',\n ],\n codeExample: `<script type=\"application/ld+json\">\\n{\\n \"@context\": \"https://schema.org\",\\n \"@type\": \"Organization\",\\n \"name\": \"Your Company\",\\n \"url\": \"https://example.com\",\\n \"logo\": \"https://example.com/logo.png\",\\n \"sameAs\": [\\n \"https://twitter.com/company\",\\n \"https://linkedin.com/company/company\"\\n ]\\n}\\n</script>`,\n successCriteria: 'Homepage and key pages have valid JSON-LD schema',\n dependsOn: ['fix-clean-html-structure'],\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n return fixes;\n },\n\n qa_content_format: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('qa_content_format', c.score);\n const affected = getAffectedPages('qa_content_format', pages);\n return [{\n id: 'fix-qa-format',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add question-based headings',\n description: 'Restructure content with H2/H3 question headings that match how users query AI assistants.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify top user questions for each page topic',\n 'Convert section headings to question format (What, How, Why, When)',\n 'Follow each question heading with a direct 2-3 sentence answer',\n 'Add a summary answer box at the top of long-form content',\n ],\n successCriteria: 'At least 50% of H2/H3 headings use question format',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n clean_html: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('clean_html', c.score);\n const affected = getAffectedPages('clean_html', pages);\n const fixes: FixAction[] = [{\n id: 'fix-clean-html-structure',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix HTML structure and meta tags',\n description: 'Ensure clean, well-structured HTML with proper meta tags, HTTPS, and parseable content for AI crawlers.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Enable HTTPS and redirect HTTP to HTTPS',\n 'Add proper <title>, meta description, and viewport meta tags',\n 'Fix HTML validation errors (unclosed tags, invalid nesting)',\n 'Ensure content is server-rendered (not client-side only)',\n ],\n successCriteria: 'Pages pass HTML validation with proper meta tags and HTTPS',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n return fixes;\n },\n\n entity_consistency: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('entity_consistency', c.score);\n return [{\n id: 'fix-entity-consistency',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Strengthen entity authority (NAP)',\n description: 'Add consistent name, address, phone (NAP) and sameAs links across all pages to strengthen entity recognition.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Ensure company name is consistent across all pages',\n 'Add Organization schema with full NAP details',\n 'Include sameAs links to social profiles and directories',\n 'Add logo and brand marks consistently',\n ],\n successCriteria: 'Organization schema present with consistent NAP on all pages',\n }];\n },\n\n robots_txt: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('robots_txt', c.score);\n return [{\n id: 'fix-robots-txt',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Configure robots.txt for AI crawlers',\n description: 'Update robots.txt to explicitly allow AI crawlers and include sitemap directive.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create or update robots.txt at domain root',\n 'Add User-agent rules for GPTBot, ClaudeBot, PerplexityBot',\n 'Include Sitemap directive pointing to sitemap.xml',\n 'Verify no accidental Disallow rules blocking content pages',\n ],\n codeExample: `User-agent: *\\nAllow: /\\n\\nUser-agent: GPTBot\\nAllow: /\\n\\nUser-agent: ClaudeBot\\nAllow: /\\n\\nUser-agent: PerplexityBot\\nAllow: /\\n\\nSitemap: https://example.com/sitemap.xml`,\n successCriteria: 'robots.txt returns 200 with AI crawler directives and Sitemap',\n }];\n },\n\n faq_section: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('faq_section', c.score);\n const affected = getAffectedPages('faq_section', pages);\n return [{\n id: 'fix-faq-section',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Build FAQ sections with schema',\n description: 'Create FAQ content with FAQPage schema markup on key pages to become a direct answer source for AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify 8-10 most common customer questions per service area',\n 'Create dedicated FAQ page with categorized Q&A pairs',\n 'Add inline FAQ sections to key service/product pages',\n 'Implement FAQPage JSON-LD schema on all FAQ content',\n ],\n successCriteria: 'FAQ page exists with FAQPage schema, key pages have inline FAQ sections',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n helpful_purpose_alignment: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('helpful_purpose_alignment', c.score);\n const affected = getAffectedPages('helpful_purpose_alignment', pages);\n return [{\n id: 'fix-helpful-purpose-alignment',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Make pages solve the user task faster',\n description: 'Reduce search-first filler and rewrite pages so the promised task is resolved quickly with concrete guidance, tradeoffs, and next steps.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Rewrite first paragraphs to answer the user need within the first 150-300 words',\n 'Remove generic intros like \"In this guide\" and broad filler that could fit any topic',\n 'Add concrete decision help: tradeoffs, risks, constraints, and next steps',\n 'Move aggressive CTAs below the first useful answer block',\n ],\n successCriteria: 'Pages lead with task-solving guidance instead of generic search-first framing',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n first_hand_experience_signals: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('first_hand_experience_signals', c.score);\n const affected = getAffectedPages('first_hand_experience_signals', pages);\n return [{\n id: 'fix-first-hand-experience',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add first-hand experience signals',\n description: 'Show real use, testing, implementation, or lived experience instead of relying on generic summary content.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add specific observations from real use, testing, or implementation',\n 'Document limitations, edge cases, or lessons learned in practice',\n 'Include screenshots, photos, before/after metrics, or original artifacts where relevant',\n 'Rewrite generic sections to reflect direct experience with the subject matter',\n ],\n successCriteria: 'Key pages contain credible signs of direct use or observation, not just generic advice',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n original_data: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('original_data', c.score);\n const affected = getAffectedPages('original_data', pages);\n return [{\n id: 'fix-original-data',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add original data and case studies',\n description: 'Publish proprietary data, statistics, case studies, or research that AI engines cannot find elsewhere.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify internal data assets (customer metrics, case study results, survey data)',\n 'Create data-driven content with specific numbers and percentages',\n 'Publish case studies with measurable outcomes',\n 'Add comparison tables with proprietary benchmarks',\n ],\n successCriteria: 'At least 3 pages contain original data points not found elsewhere online',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n internal_linking: (c, pages, linkGraph) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('internal_linking', c.score);\n const fixes: FixAction[] = [];\n\n // Orphan pages fix\n if (linkGraph) {\n const orphans: string[] = [];\n linkGraph.nodes.forEach((node) => {\n if (node.isOrphan) orphans.push(node.url);\n });\n if (orphans.length > 0) {\n fixes.push({\n id: 'fix-internal-linking-orphans',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Link orphan pages into site navigation',\n description: `${orphans.length} pages have no incoming internal links. These are invisible to AI crawlers that follow links.`,\n impact: orphans.length > 5 ? 'critical' : 'high',\n effort: orphans.length > 10 ? 'medium' : 'low',\n impactScore: 0,\n category: 'structure',\n steps: [\n `Identify the ${orphans.length} orphan pages with zero incoming links`,\n 'Add contextual links from related content pages',\n 'Include orphan pages in navigation menus or footer links',\n 'Add \"Related Content\" sections on relevant pages',\n ],\n successCriteria: 'All content pages have at least 1 incoming internal link',\n affectedPages: orphans.slice(0, 20),\n pageCount: orphans.length,\n });\n }\n\n // Depth fix\n if (linkGraph.stats.maxDepth > 3) {\n fixes.push({\n id: 'fix-internal-linking-depth',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Reduce page depth for deep content',\n description: `Max depth is ${linkGraph.stats.maxDepth} clicks from homepage. AI crawlers rarely follow links beyond 3 levels.`,\n impact: 'medium',\n effort: 'medium',\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Identify pages more than 3 clicks from the homepage',\n 'Add direct links from high-level pages to deep content',\n 'Consider flattening URL structure for key pages',\n 'Add hub pages that aggregate related deep content',\n ],\n successCriteria: 'All important content pages reachable within 3 clicks from homepage',\n });\n }\n\n // No clusters fix\n if (linkGraph.clusters.length === 0) {\n fixes.push({\n id: 'fix-internal-linking-clusters',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create topic clusters with pillar pages',\n description: 'No topic clusters detected. Organizing content into pillar-spoke clusters strengthens topical authority for AI engines.',\n impact: 'high',\n effort: 'high',\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Identify 3-5 core topic areas for your business',\n 'Create comprehensive pillar pages (3000+ words) for each topic',\n 'Write 5-7 supporting articles per pillar linking back to pillar',\n 'Interlink supporting articles within each cluster',\n ],\n successCriteria: 'At least 2 topic clusters with pillar page and 5+ spoke pages',\n });\n }\n } else {\n // No link graph available - generic fix\n fixes.push({\n id: 'fix-internal-linking-generic',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve internal linking architecture',\n description: 'Strengthen internal linking with descriptive anchor text between related pages.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Audit current internal link structure',\n 'Add contextual links between related content pages',\n 'Ensure every key page is reachable within 3 clicks from homepage',\n 'Use descriptive anchor text instead of \"click here\" or \"read more\"',\n ],\n successCriteria: 'Key pages have 3+ incoming internal links with descriptive anchors',\n });\n }\n return fixes;\n },\n\n semantic_html: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('semantic_html', c.score);\n const affected = getAffectedPages('semantic_html', pages);\n return [{\n id: 'fix-semantic-html',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Implement semantic HTML5 elements',\n description: 'Use semantic HTML5 elements (main, article, nav, header, footer, section) to give AI parsers clear content structure.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Wrap main content in <main> element',\n 'Use <article> for self-contained content blocks',\n 'Add <nav> for navigation and <aside> for sidebars',\n 'Add lang attribute to <html> and ARIA labels for accessibility',\n ],\n successCriteria: 'Pages use semantic HTML5 elements with lang attribute',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_freshness: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_freshness', c.score);\n const affected = getAffectedPages('content_freshness', pages);\n return [{\n id: 'fix-content-freshness',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add content freshness signals',\n description: 'Include dateModified schema, visible dates, and recent content updates to signal freshness to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add datePublished and dateModified to Article schema',\n 'Display visible \"Last updated\" dates on content pages',\n 'Update stale content with current information',\n 'Add <time> elements with datetime attributes for all dates',\n ],\n successCriteria: 'Content pages show visible dates and have dateModified in schema',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n sitemap_completeness: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('sitemap_completeness', c.score);\n return [{\n id: 'fix-sitemap',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create complete sitemap.xml',\n description: 'Generate a comprehensive sitemap with lastmod dates for all important pages.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Generate sitemap.xml listing all content pages',\n 'Include <lastmod> dates for each URL',\n 'Set <changefreq> and <priority> appropriately',\n 'Reference sitemap in robots.txt',\n ],\n successCriteria: 'sitemap.xml returns 200 with all content pages and lastmod dates',\n }];\n },\n\n rss_feed: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('rss_feed', c.score);\n return [{\n id: 'fix-rss-feed',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Deploy RSS/Atom feed',\n description: 'Add an RSS or Atom feed for your blog/news content to signal active publishing to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create RSS 2.0 or Atom feed for blog/news content',\n 'Include title, description, pubDate, and full content for each item',\n 'Add <link rel=\"alternate\" type=\"application/rss+xml\"> to page head',\n 'Auto-generate feed on each new publish',\n ],\n codeExample: `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<rss version=\"2.0\">\\n <channel>\\n <title>Your Site Blog</title>\\n <link>https://example.com/blog</link>\\n <description>Latest articles</description>\\n <item>\\n <title>Article Title</title>\\n <link>https://example.com/blog/article</link>\\n <pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate>\\n <description>Article summary</description>\\n </item>\\n </channel>\\n</rss>`,\n successCriteria: 'RSS feed returns valid XML with recent content items',\n }];\n },\n\n table_list_extractability: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('table_list_extractability', c.score);\n const affected = getAffectedPages('table_list_extractability', pages);\n return [{\n id: 'fix-tables-lists',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add structured tables and lists',\n description: 'Use HTML tables for comparison data and lists for features, steps, and specifications.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify data suitable for table format (comparisons, pricing, specs)',\n 'Convert bullet points to proper <ul>/<ol> lists',\n 'Add comparison tables with <th> headers',\n 'Ensure tables have descriptive captions',\n ],\n successCriteria: 'Key pages contain at least one HTML table or structured list',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n definition_patterns: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('definition_patterns', c.score);\n const affected = getAffectedPages('definition_patterns', pages);\n return [{\n id: 'fix-definitions',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add definition-style content',\n description: 'Include clear definition patterns for key terms and concepts that AI engines can cite for \"what is\" queries.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify key industry terms your audience searches for',\n 'Write clear definitions using \"X is...\" or \"X refers to...\" patterns',\n 'Place definitions near the top of relevant pages',\n 'Consider a glossary page for comprehensive term coverage',\n ],\n successCriteria: 'Key pages contain definition patterns for relevant terms',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n direct_answer_density: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('direct_answer_density', c.score);\n const affected = getAffectedPages('direct_answer_density', pages);\n return [{\n id: 'fix-direct-answers',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add direct answer paragraphs',\n description: 'Write concise, standalone answer paragraphs after question headings for AI engine citations.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify question-format headings on each page',\n 'Write a 2-3 sentence direct answer immediately after each heading',\n 'Ensure answers are self-contained (don\\'t require context from other sections)',\n 'Use bold for key facts within answer paragraphs',\n ],\n successCriteria: 'Question headings are followed by direct, concise answer paragraphs',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_licensing: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_licensing', c.score);\n return [{\n id: 'fix-content-licensing',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add ai.txt and content licensing',\n description: 'Create an /ai.txt file specifying AI usage permissions and add license schema to structured data.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Create ai.txt at domain root with usage permissions',\n 'Specify allowed AI uses (training, citation, summarization)',\n 'Add license information to schema markup',\n 'Consider a content licensing page linked from footer',\n ],\n codeExample: `# ai.txt - AI Usage Policy for example.com\\n\\nUser-Agent: *\\nAllow: /blog/\\nAllow: /docs/\\n\\n# Permissions\\nTraining: yes\\nCitation: yes with attribution\\nSummarization: yes`,\n successCriteria: '/ai.txt returns 200 with clear AI usage permissions',\n }];\n },\n\n author_schema_depth: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('author_schema_depth', c.score);\n return [{\n id: 'fix-author-schema',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Enhance author and expert schema',\n description: 'Add Person schema for content authors with credentials and sameAs links for E-E-A-T signals.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Create author profile pages for content creators',\n 'Add Person schema with name, jobTitle, credentials, sameAs',\n 'Link articles to author profiles via schema author property',\n 'Include author bio and expertise on article pages',\n ],\n successCriteria: 'Articles have Person schema for authors with credentials',\n }];\n },\n\n creator_transparency: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('creator_transparency', c.score);\n const affected = getAffectedPages('creator_transparency', pages);\n return [{\n id: 'fix-creator-transparency',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Make content creators clearly visible',\n description: 'Add visible bylines, author pages, and reviewer/editor attribution so readers can clearly tell who created the content.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add visible bylines to article-like pages where readers expect them',\n 'Link author names to author pages with role, expertise area, and relevant background',\n 'Add reviewer or editor attribution on sensitive or expert content',\n 'Keep visible creator identity consistent with schema markup',\n ],\n successCriteria: 'Article-like pages have clear visible bylines and linked creator context',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n methodology_transparency: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('methodology_transparency', c.score);\n const affected = getAffectedPages('methodology_transparency', pages);\n return [{\n id: 'fix-methodology-transparency',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Explain how content was tested or reviewed',\n description: 'Add methodology, criteria, testing, review, or update-process details where users would expect them.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add a \"How we tested\", \"Methodology\", or review-process section where relevant',\n 'Document criteria, tools used, sample size, timeframe, or update policy',\n 'Disclose AI assistance when a reasonable reader would expect that context',\n 'Support methodology notes with screenshots, tables, or process artifacts when possible',\n ],\n successCriteria: 'Review, comparison, and research-style pages explain how conclusions were produced',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n fact_density: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('fact_density', c.score);\n const affected = getAffectedPages('fact_density', pages);\n return [{\n id: 'fix-fact-density',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase fact and data density',\n description: 'Add specific numbers, percentages, statistics, and data points that AI engines can cite.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Review content for vague claims and replace with specific data',\n 'Add statistics, percentages, and measurable outcomes',\n 'Include source citations for data points',\n 'Add data tables or comparison charts where appropriate',\n ],\n successCriteria: 'Key pages contain at least 3 specific data points per 500 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n canonical_url: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('canonical_url', c.score);\n const affected = getAffectedPages('canonical_url', pages);\n return [{\n id: 'fix-canonical-url',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix canonical URL strategy',\n description: 'Add rel=\"canonical\" tags to all pages to prevent duplicate content confusion.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Add <link rel=\"canonical\"> to every page pointing to preferred URL',\n 'Ensure canonical URLs use consistent scheme (https) and format',\n 'Handle www vs non-www with proper redirects',\n 'Set canonical for paginated content to the main page',\n ],\n codeExample: `<link rel=\"canonical\" href=\"https://example.com/page\" />`,\n successCriteria: 'All pages have rel=\"canonical\" pointing to the correct URL',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_velocity: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_velocity', c.score);\n return [{\n id: 'fix-content-velocity',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase publishing frequency',\n description: 'Establish a regular content publishing cadence to signal active, current information to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Set a publishing schedule (weekly or bi-weekly minimum)',\n 'Create a content calendar covering key topics',\n 'Update sitemap and RSS feed with each new publish',\n 'Refresh existing evergreen content with current data',\n ],\n successCriteria: 'At least 2 new or updated content pages per month with dated entries',\n }];\n },\n\n schema_coverage: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('schema_coverage', c.score);\n return [{\n id: 'fix-schema-coverage',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Extend schema to inner pages',\n description: 'Add page-specific structured data beyond the homepage to articles, services, and product pages.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add Article schema to blog/news pages',\n 'Add Service/Product schema to service/product pages',\n 'Add BreadcrumbList schema to all inner pages',\n 'Validate each page type with Rich Results Test',\n ],\n successCriteria: 'At least 50% of content pages have page-specific schema',\n dependsOn: ['fix-schema-markup'],\n }];\n },\n\n speakable_schema: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('speakable_schema', c.score);\n return [{\n id: 'fix-speakable-schema',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add SpeakableSpecification schema',\n description: 'Add Speakable schema to tell voice assistants which content sections are best for spoken answers.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Identify key paragraphs suitable for voice readout',\n 'Add SpeakableSpecification with CSS selectors to Article schema',\n 'Point speakable selectors to headline and summary paragraphs',\n 'Test with Google structured data testing tool',\n ],\n codeExample: `\"speakable\": {\\n \"@type\": \"SpeakableSpecification\",\\n \"cssSelector\": [\\n \".article-headline\",\\n \".article-summary\"\\n ]\\n}`,\n successCriteria: 'Article pages include SpeakableSpecification in schema',\n dependsOn: ['fix-schema-markup'],\n }];\n },\n\n query_answer_alignment: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('query_answer_alignment', c.score);\n const affected = getAffectedPages('query_answer_alignment', pages);\n return [{\n id: 'fix-query-answer-alignment',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve query-answer alignment',\n description: 'Ensure question headings are followed by direct, concise answers in the first paragraph.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Audit question-format headings and their following paragraphs',\n 'Add direct answers in the first 1-2 sentences after each question heading',\n 'Remove filler text between question and answer',\n 'Ensure answers are self-contained and citable',\n ],\n successCriteria: 'Question headings have direct answer paragraphs within 50 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_cannibalization: (c, pages, linkGraph) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_cannibalization', c.score);\n const fixes: FixAction[] = [];\n\n if (linkGraph && linkGraph.clusters.length > 0) {\n // Check for low-cohesion clusters which may indicate overlapping content\n const lowCohesion = linkGraph.clusters.filter(cl => cl.cohesion < 50);\n if (lowCohesion.length > 0) {\n const affected = lowCohesion.flatMap(cl => [cl.pillarUrl, ...cl.spokes]);\n fixes.push({\n id: 'fix-content-cannibalization-overlap',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Consolidate overlapping content',\n description: `${lowCohesion.length} content clusters have low cohesion, suggesting pages compete for the same topics.`,\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify pages targeting the same keywords or topics',\n 'Merge overlapping pages into single authoritative pages',\n 'Set up 301 redirects from merged pages to consolidated page',\n 'Differentiate remaining similar pages with distinct angles',\n ],\n successCriteria: 'No two pages target the same primary keyword or topic',\n affectedPages: affected.slice(0, 20),\n pageCount: affected.length,\n });\n }\n }\n\n if (fixes.length === 0) {\n const affected = getAffectedPages('content_cannibalization', pages);\n fixes.push({\n id: 'fix-content-cannibalization',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Resolve content cannibalization',\n description: 'Multiple pages may be targeting the same topics, diluting AI engine citations.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Audit pages for overlapping topic coverage',\n 'Consolidate similar pages into comprehensive single pages',\n 'Differentiate remaining pages with distinct angles and keywords',\n 'Add canonical tags to prevent duplicate content issues',\n ],\n successCriteria: 'Each topic is covered by a single authoritative page',\n affectedPages: affected,\n pageCount: affected?.length,\n });\n }\n\n return fixes;\n },\n\n duplicate_content: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('duplicate_content', c.score);\n const affected = getAffectedPages('duplicate_content', pages);\n\n // Extract section names from findings\n const sectionPairs = c.findings\n .filter(f => f.detail.includes(\"' and '\"))\n .map(f => {\n const match = f.detail.match(/'([^']+)' and '([^']+)'/);\n return match ? { a: match[1], b: match[2] } : null;\n })\n .filter(Boolean) as Array<{ a: string; b: string }>;\n\n const steps = [\n 'Identify sections with duplicate or near-identical text',\n 'Rewrite each section to provide a unique angle on the topic',\n 'Ensure each heading section adds new information for the reader',\n ];\n if (sectionPairs.length > 0) {\n const pair = sectionPairs[0];\n steps.unshift(`Start with '${pair.a}' and '${pair.b}' which share similar text`);\n }\n\n return [{\n id: 'fix-duplicate-content',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix duplicate content blocks',\n description: 'Sections within pages contain identical or near-identical text. LLMs may flag this as low-quality content, reducing the authority of the page.',\n impact, effort,\n impactScore: 0,\n category: 'content' as const,\n steps,\n successCriteria: 'Each section within a page provides unique content',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n cross_page_duplication: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('cross_page_duplication', c.score);\n const affected = getAffectedPages('cross_page_duplication', pages);\n return [{\n id: 'fix-cross-page-duplication',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Eliminate cross-page duplicate content',\n description: 'The same paragraphs appear on multiple pages. AI engines may only index one version, wasting the others.',\n impact, effort,\n impactScore: 0,\n category: 'content' as const,\n steps: [\n 'Identify paragraphs that are copy-pasted across multiple pages',\n 'Rewrite each instance to provide a unique angle relevant to that page',\n 'Move truly shared content to a single resource page and link to it',\n 'Use canonical tags if pages must share content',\n ],\n successCriteria: 'Each page has unique body content with no copy-pasted paragraphs',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n visible_date_signal: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('visible_date_signal', c.score);\n const affected = getAffectedPages('visible_date_signal', pages);\n return [{\n id: 'fix-visible-dates',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add visible date signals',\n description: 'Add visible publication and modification dates using <time> elements for AI engine freshness assessment.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add visible \"Published\" and \"Last updated\" dates to content pages',\n 'Use <time> elements with datetime attributes',\n 'Ensure dates match dateModified in schema markup',\n 'Update dates when content is refreshed',\n ],\n codeExample: `<time datetime=\"2024-01-15\">January 15, 2024</time>`,\n successCriteria: 'Content pages show visible dates with <time> elements',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n topic_coherence: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('topic_coherence', c.score);\n return [{\n id: 'fix-topic-coherence',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Focus blog content on core expertise',\n description: 'Ensure blog content consistently covers your core topic areas. Scattered content across unrelated topics weakens AI engine authority signals.',\n impact, effort: effort === 'trivial' ? 'low' : effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify 2-3 core expertise areas your brand is known for',\n 'Audit existing blog posts and remove or consolidate off-topic content',\n 'Create a content calendar focused on core topics',\n 'Use topic clusters: pillar pages linking to supporting articles within the same niche',\n ],\n successCriteria: '80%+ of blog content covers core expertise areas with consistent topic focus',\n }];\n },\n\n content_depth: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_depth', c.score);\n const affected = getAffectedPages('content_depth', pages);\n return [{\n id: 'fix-content-depth',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase content depth and structure',\n description: 'Expand thin content with more detail, examples, and structured sections. AI engines prefer comprehensive articles with clear heading hierarchies.',\n impact, effort: effort === 'trivial' ? 'low' : effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Aim for 1000+ words per article with expert analysis and examples',\n 'Use H2/H3 subheadings every 200-300 words for clear structure',\n 'Add comparison tables, numbered steps, and data points',\n 'Remove or expand thin pages (under 300 words) that dilute site quality',\n ],\n successCriteria: 'Average article length exceeds 1000 words with 5+ subheadings per page',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n citation_ready_writing: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('citation_ready_writing', c.score);\n const affected = getAffectedPages('citation_ready_writing', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve citation-ready writing quality',\n description: 'Write self-contained definition sentences and single-claim statements that AI engines can quote directly.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Write \"X is a...\" definition sentences for key concepts',\n 'Keep sentences under 30 words with a single claim each',\n 'Start answer paragraphs with the entity name, not \"This\" or \"That\"',\n 'Pair statistics with named sources for quotable lines',\n ],\n successCriteria: 'Average 3+ definition sentences and 5+ single-claim sentences per page',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n answer_first_placement: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('answer_first_placement', c.score);\n const affected = getAffectedPages('answer_first_placement', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add answer-first content placement',\n description: 'Place a concise answer block in the first 300 words of each page.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add a 40-80 word answer paragraph within the first 300 words',\n 'Replace \"In this article...\" openers with direct answers',\n 'Include the H1 topic entity in the first body sentence',\n ],\n successCriteria: '70%+ of pages have an answer block in the first 300 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n evidence_packaging: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('evidence_packaging', c.score);\n const affected = getAffectedPages('evidence_packaging', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Package evidence for AI engines',\n description: 'Add inline citations, attribution phrases, and sources sections.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add inline links to authoritative external sources within paragraphs',\n 'Use \"According to [Source]...\" attribution phrases',\n 'Add a \"Sources\" or \"References\" heading at the end of articles',\n 'Pair statistics with named source organizations',\n ],\n successCriteria: 'Average 3+ inline citations and 3+ attribution phrases per page',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n entity_disambiguation: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('entity_disambiguation', c.score);\n const affected = getAffectedPages('entity_disambiguation', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Clarify entity boundaries',\n description: 'Define the primary entity early and use consistent terminology.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Define the primary entity within the first 500 characters',\n 'Use the same primary term consistently (avoid synonym soup)',\n 'Add \"unlike X\" or \"compared to Y\" disambiguation signals',\n ],\n successCriteria: '70%+ of pages define the primary entity early with consistent terminology',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n extraction_friction: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('extraction_friction', c.score);\n const affected = getAffectedPages('extraction_friction', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Reduce extraction friction',\n description: 'Make content easier for AI engines to extract by reducing sentence length and jargon.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Shorten sentences to under 25 words on average',\n 'Use active voice in lead paragraphs (first 2-3 sentences)',\n 'Define jargon inline when first used',\n 'Remove display:none hidden content and accordion-only content',\n ],\n successCriteria: 'Average sentence length under 25 words with no hidden content',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n image_context_ai: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('image_context_ai', c.score);\n const affected = getAffectedPages('image_context_ai', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add image context for AI engines',\n description: 'Wrap images in <figure>/<figcaption> with descriptive alt text.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Wrap images in <figure> elements with descriptive <figcaption>',\n 'Write alt text with 5+ descriptive words (avoid \"image\" or \"photo\")',\n 'Place images within <article> or <section> elements',\n ],\n codeExample: '<figure>\\n <img src=\"chart.png\" alt=\"Bar chart showing 45% reduction in fall risk for walker users\">\\n <figcaption>Walker users experienced a 45% reduction in fall risk (2025 study)</figcaption>\\n</figure>',\n successCriteria: '50%+ of images use <figure>/<figcaption> with descriptive alt text',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n};\n\n// ─── Main function ──────────────────────────────────────────────────────────\n\nexport function generateFixPlan(\n domain: string,\n overallScore: number,\n criteria: CriterionResult[],\n pagesReviewed?: PageReview[],\n linkGraph?: LinkGraph,\n): FixPlan {\n // Step 1: Generate all fixes from criterion scores\n const allFixes: FixAction[] = [];\n\n for (const criterion of criteria) {\n const generator = FIX_GENERATORS[criterion.criterion];\n if (!generator) continue;\n\n const fixes = generator(criterion, pagesReviewed, linkGraph);\n for (const fix of fixes) {\n const weight = CRITERION_WEIGHTS[criterion.criterion] ?? 0.05;\n fix.impactScore = Math.round((10 - criterion.score) * weight * 100);\n allFixes.push(fix);\n }\n }\n\n // Step 2: Group into phases\n const phases: FixPhase[] = PHASE_CONFIG.map(config => {\n const phaseFixes = allFixes\n .filter(fix => config.criteria.includes(fix.criterionId))\n .sort((a, b) => b.impactScore - a.impactScore);\n\n return {\n phase: config.phase,\n title: config.title,\n description: config.description,\n fixes: phaseFixes,\n estimatedImpact: 0, // calculated after projected score\n };\n });\n\n // Step 3: Validate dependencies (move fixes if dependency is in later phase)\n for (const phase of phases) {\n for (const fix of phase.fixes) {\n if (!fix.dependsOn) continue;\n for (const depId of fix.dependsOn) {\n // Find which phase the dependency is in\n const depPhase = phases.find(p => p.fixes.some(f => f.id === depId));\n if (depPhase && depPhase.phase > phase.phase) {\n // Dependency is in a later phase - move this fix there\n phase.fixes = phase.fixes.filter(f => f.id !== fix.id);\n depPhase.fixes.push(fix);\n depPhase.fixes.sort((a, b) => b.impactScore - a.impactScore);\n break;\n }\n }\n }\n }\n\n // Step 4: Calculate projected score\n const totalWeight = Object.values(CRITERION_WEIGHTS).reduce((s, w) => s + w, 0);\n\n // Deduplicate: only count the best fix per criterion\n const bestDeltaPerCriterion = new Map<string, number>();\n for (const fix of allFixes) {\n const criterion = criteria.find(c => c.criterion === fix.criterionId);\n if (!criterion) continue;\n const weight = CRITERION_WEIGHTS[fix.criterionId] ?? 0.05;\n let targetScore: number;\n switch (fix.effort) {\n case 'trivial': case 'low': targetScore = 8; break;\n case 'medium': targetScore = 7; break;\n case 'high': targetScore = 6; break;\n }\n const improvement = Math.max(0, targetScore - criterion.score);\n const delta = (improvement * weight / totalWeight) * 100;\n const existing = bestDeltaPerCriterion.get(fix.criterionId) ?? 0;\n if (delta > existing) bestDeltaPerCriterion.set(fix.criterionId, delta);\n }\n const scoreDelta = Array.from(bestDeltaPerCriterion.values()).reduce((s, d) => s + d, 0);\n\n const projectedScore = Math.min(100, Math.round(overallScore + scoreDelta));\n\n // Step 5: Calculate per-phase estimated impact\n for (const phase of phases) {\n let phaseImpact = 0;\n const seenCriteria = new Set<string>();\n for (const fix of phase.fixes) {\n if (seenCriteria.has(fix.criterionId)) continue;\n seenCriteria.add(fix.criterionId);\n const criterion = criteria.find(c => c.criterion === fix.criterionId);\n if (!criterion) continue;\n const weight = CRITERION_WEIGHTS[fix.criterionId] ?? 0.05;\n let targetScore: number;\n switch (fix.effort) {\n case 'trivial': case 'low': targetScore = 8; break;\n case 'medium': targetScore = 7; break;\n case 'high': targetScore = 6; break;\n }\n const improvement = Math.max(0, targetScore - criterion.score);\n phaseImpact += (improvement * weight / totalWeight) * 100;\n }\n phase.estimatedImpact = Math.round(phaseImpact);\n }\n\n // Step 6: Extract quick wins\n const quickWins = allFixes.filter(\n f => (f.effort === 'trivial' || f.effort === 'low') && (f.impact === 'critical' || f.impact === 'high'),\n );\n\n // Step 7: Build summary\n const summary: FixPlanSummary = {\n criticalCount: allFixes.filter(f => f.impact === 'critical').length,\n highCount: allFixes.filter(f => f.impact === 'high').length,\n mediumCount: allFixes.filter(f => f.impact === 'medium').length,\n lowCount: allFixes.filter(f => f.impact === 'low').length,\n quickWinCount: quickWins.length,\n topOpportunity: allFixes.length > 0\n ? allFixes.sort((a, b) => b.impactScore - a.impactScore)[0].title\n : 'None',\n estimatedTotalEffort: formatEffort(allFixes.reduce((s, f) => s + effortToHours(f.effort), 0)),\n };\n\n return {\n domain,\n generatedAt: new Date().toISOString(),\n overallScore,\n projectedScore,\n totalFixes: allFixes.length,\n phases,\n quickWins,\n summary,\n };\n}\n\nfunction formatEffort(hours: number): string {\n if (hours < 1) return '<1h';\n return `~${Math.round(hours)}h`;\n}\n","/**\r\n * SPA detection and headless Chromium rendering for pre-crawl.\r\n *\r\n * When a site returns a thin JS-only shell (e.g. React CRA, Vite SPA),\r\n * the regular fetch() gets almost no text content, causing false low scores.\r\n * This module detects those shells and re-renders them with Puppeteer.\r\n */\r\n\r\nimport type { FetchResult } from './site-crawler.js';\r\n\r\n// ─── SPA classification ─────────────────────────────────────────────────────\r\n\r\nexport type RenderingMethod = 'server' | 'client-spa';\r\n\r\ninterface RenderingClassification {\r\n method: RenderingMethod;\r\n framework: string | null;\r\n}\r\n\r\n// ─── SPA shell detection ────────────────────────────────────────────────────\r\n\r\nconst SPA_INDICATORS = [\r\n // Root mount points (empty or nearly empty, including self-closing)\r\n /<div\\s+id=[\"'](root|app|__next|__nuxt|__vue)[\"'][^>]*(?:\\/>|>\\s*<\\/div>)/i,\r\n // Framework globals\r\n /__NEXT_DATA__/,\r\n /__NUXT__/,\r\n // CRA / Vite bundle patterns\r\n /src=[\"'][^\"']*\\/static\\/js\\/main\\.[a-f0-9]+\\.js[\"']/i,\r\n /src=[\"'][^\"']*\\/assets\\/index-[a-f0-9]+\\.js[\"']/i,\r\n // React markers\r\n /data-reactroot/i,\r\n // Angular\r\n /ng-version/i,\r\n // Noscript JS warnings\r\n /<noscript>[^<]*(?:javascript|enable\\s+js|requires?\\s+javascript)[^<]*<\\/noscript>/i,\r\n];\r\n\r\n/**\r\n * Detect whether raw HTML is a thin SPA shell that needs client-side rendering.\r\n * Both conditions required:\r\n * 1. Visible text content < 500 chars (thin page)\r\n * 2. At least one SPA framework indicator present\r\n */\r\nexport function isSpaShell(html: string): boolean {\r\n // Strip all tags and collapse whitespace to get visible text\r\n const text = html\r\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\r\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\r\n .replace(/<[^>]*>/g, ' ')\r\n .replace(/\\s+/g, ' ')\r\n .trim();\r\n\r\n if (text.length >= 500) return false;\r\n\r\n return SPA_INDICATORS.some((pattern) => pattern.test(html));\r\n}\r\n\r\n/**\r\n * Classify a page's rendering method from its raw (non-headless) HTML.\r\n * Returns the method ('server' | 'client-spa') and detected framework if any.\r\n */\r\nexport function classifyRendering(html: string): RenderingClassification {\r\n if (!isSpaShell(html)) return { method: 'server', framework: null };\r\n\r\n // Detect framework from SPA indicators\r\n const frameworkPatterns: [RegExp, string][] = [\r\n [/__NEXT_DATA__/, 'next'],\r\n [/__NUXT__/, 'nuxt'],\r\n [/<div\\s+id=[\"']__vue[\"']/i, 'vue'],\r\n [/ng-version/i, 'angular'],\r\n [/data-reactroot/i, 'react'],\r\n [/<div\\s+id=[\"'](root|app)[\"'][^>]*(?:\\/>|>\\s*<\\/div>)/i, 'react'],\r\n [/src=[\"'][^\"']*\\/static\\/js\\/main\\.[a-f0-9]+\\.js[\"']/i, 'react'],\r\n [/src=[\"'][^\"']*\\/assets\\/index-[a-f0-9]+\\.js[\"']/i, 'vite'],\r\n ];\r\n\r\n for (const [pattern, framework] of frameworkPatterns) {\r\n if (pattern.test(html)) return { method: 'client-spa', framework };\r\n }\r\n\r\n return { method: 'client-spa', framework: null };\r\n}\r\n\r\n// ─── Headless Chromium rendering ────────────────────────────────────────────\r\n\r\nexport interface HeadlessOptions {\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Render a URL with headless Chromium and return the fully-rendered HTML.\r\n * Returns null if Puppeteer is not installed or any error occurs.\r\n * The caller should fall back to the raw HTML in that case.\r\n */\r\nexport async function fetchWithHeadless(\r\n url: string,\r\n options?: HeadlessOptions\r\n): Promise<FetchResult | null> {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n let puppeteer: any;\r\n try {\r\n // Dynamic import - puppeteer is an optional peer dependency\r\n const mod = 'puppeteer';\r\n puppeteer = await import(/* @vite-ignore */ mod);\r\n } catch {\r\n return null;\r\n }\r\n\r\n const timeout = options?.timeout ?? 25000;\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n let browser: any = null;\r\n\r\n try {\r\n browser = await puppeteer.launch({\r\n headless: true,\r\n args: [\r\n '--no-sandbox',\r\n '--disable-setuid-sandbox',\r\n '--disable-dev-shm-usage',\r\n '--disable-gpu',\r\n '--single-process',\r\n ],\r\n });\r\n\r\n const page = await browser.newPage();\r\n\r\n // Block heavy resources to speed up rendering\r\n await page.setRequestInterception(true);\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n page.on('request', (req: any) => {\r\n const type = req.resourceType();\r\n if (['image', 'font', 'media', 'stylesheet'].includes(type)) {\r\n req.abort();\r\n } else {\r\n req.continue();\r\n }\r\n });\r\n\r\n await page.setUserAgent('AEO-Visibility-Bot/1.0');\r\n await page.goto(url, { waitUntil: 'networkidle2', timeout });\r\n\r\n // Wait for JS to populate the body with real text\r\n try {\r\n await page.waitForFunction(\r\n 'document.body && document.body.innerText && document.body.innerText.replace(/\\\\s+/g, \" \").trim().length > 100',\r\n { timeout: 5000 }\r\n );\r\n } catch {\r\n // Body text never exceeded 100 chars - still return what we got\r\n }\r\n\r\n const html = await page.content();\r\n const finalUrl = page.url();\r\n\r\n return {\r\n text: html.slice(0, 500000),\r\n status: 200,\r\n finalUrl,\r\n };\r\n } catch {\r\n return null;\r\n } finally {\r\n if (browser) {\r\n try {\r\n await browser.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n }\r\n }\r\n}\r\n","/**\n * Programmatic audit API.\n * Runs the full 7-phase AEO audit pipeline and returns structured results.\n */\n\nimport { prefetchSiteData, auditSiteFromData, extractRawDataSummary } from './site-crawler.js';\nimport { calculateOverallScore } from './scoring.js';\nimport { isSpaShell, fetchWithHeadless, classifyRendering } from './headless-fetch.js';\nimport { buildScorecard, buildDetailedFindings } from './scorecard-builder.js';\nimport { generateVerdict, generateOpportunities, generatePitchNumbers, generateBottomLine } from './narrative-generator.js';\nimport { fetchMultiPageData } from './multi-page-fetcher.js';\nimport { analyzeAllPages } from './page-analyzer.js';\nimport { computePillarScores, computeTopFixes } from './pillars.js';\nimport type { AuditData } from './types.js';\n\nexport interface AuditOptions {\n /** Skip Puppeteer SPA rendering (default: false) */\n noHeadless?: boolean;\n /** Homepage + blog only, skip extra page discovery (default: false) */\n noMultiPage?: boolean;\n /** Fetch timeout in ms (default: 15000) */\n timeout?: number;\n /** Enable full-site BFS crawl (default: false) */\n fullCrawl?: boolean;\n /** Max pages for full crawl (default: 200) */\n maxPages?: number;\n /** Parallel fetch concurrency for full crawl (default: 5) */\n concurrency?: number;\n}\n\nexport interface AuditResult extends AuditData {\n /** True if headless browser was used for SPA rendering */\n renderedWithHeadless?: boolean;\n /** Wall-clock seconds */\n elapsed: number;\n}\n\nfunction getTextLength(html: string): number {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim().length;\n}\n\n/**\n * Run a complete AEO audit on a domain.\n *\n * @example\n * ```ts\n * import { audit } from 'aeorank';\n * const result = await audit('example.com');\n * console.log(result.overallScore); // 0-100\n * ```\n */\nexport async function audit(domain: string, options?: AuditOptions): Promise<AuditResult> {\n const startTime = Date.now();\n let renderedWithHeadless = false;\n\n // Phase 1: Fetch site data\n const siteData = await prefetchSiteData(domain);\n\n if (!siteData.protocol) {\n throw new Error(`Could not connect to ${domain} (no HTTPS or HTTP response)`);\n }\n\n if (siteData.redirectedTo) {\n throw new Error(`${domain} redirects to ${siteData.redirectedTo} (hijacked domain)`);\n }\n\n if (siteData.parkedReason) {\n throw new Error(`${domain} is a parked/lost domain (${siteData.parkedReason})`);\n }\n\n // Phase 2: SPA detection + headless rendering\n if (!options?.noHeadless && siteData.homepage && isSpaShell(siteData.homepage.text)) {\n const rawTextLen = getTextLength(siteData.homepage.text);\n const url = `${siteData.protocol}://${domain}`;\n const rendered = await fetchWithHeadless(url);\n\n if (rendered) {\n const renderedTextLen = getTextLength(rendered.text);\n if (renderedTextLen > rawTextLen) {\n siteData.homepage = rendered;\n renderedWithHeadless = true;\n }\n }\n\n if (renderedWithHeadless && siteData.faqPage && isSpaShell(siteData.faqPage.text)) {\n const faqUrl = `${siteData.protocol}://${domain}/faq`;\n const renderedFaq = await fetchWithHeadless(faqUrl);\n if (renderedFaq && getTextLength(renderedFaq.text) > getTextLength(siteData.faqPage.text)) {\n siteData.faqPage = renderedFaq;\n }\n }\n }\n\n // Phase 3: Multi-page discovery\n if (options?.fullCrawl) {\n const { crawlFullSite } = await import('./full-site-crawler.js');\n const crawlResult = await crawlFullSite(siteData, {\n maxPages: options.maxPages ?? 200,\n concurrency: options.concurrency ?? 5,\n });\n siteData.blogSample = crawlResult.pages;\n siteData.crawlStats = {\n discovered: crawlResult.discoveredUrls.length,\n fetched: crawlResult.fetchedUrls.length,\n skipped: crawlResult.skippedUrls.length,\n elapsed: crawlResult.elapsed,\n };\n } else if (!options?.noMultiPage) {\n await fetchMultiPageData(siteData);\n }\n\n // Phase 4: Score all 40 criteria\n const results = auditSiteFromData(siteData);\n const overallScore = calculateOverallScore(results);\n const rawData = extractRawDataSummary(siteData);\n if (renderedWithHeadless) rawData.rendered_with_headless = true;\n\n // Compute pillar scores and top fixes\n const pillarScores = computePillarScores(results);\n const topFixesRaw = computeTopFixes(results, 3);\n const coherenceResult = results.find(r => r.criterion === 'topic_coherence');\n const coherenceGated = !!(coherenceResult && coherenceResult.score < 6);\n\n // Phase 5: Build scorecard + detailed findings\n const scorecard = buildScorecard(results);\n const detailedFindings = buildDetailedFindings(results);\n\n // Phase 6: Generate narrative\n const verdict = generateVerdict(overallScore, scorecard, rawData, domain);\n const opportunities = generateOpportunities(scorecard, results);\n const pitchNumbers = generatePitchNumbers(overallScore, rawData, scorecard);\n const bottomLine = generateBottomLine(overallScore, opportunities, scorecard, domain);\n\n // Phase 7: Per-page analysis\n const pagesReviewed = analyzeAllPages(siteData);\n\n const elapsed = Math.round((Date.now() - startTime) / 100) / 10;\n\n return {\n site: domain,\n auditDate: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }),\n auditor: 'AEORank',\n engine: 'instant',\n overallScore,\n verdict,\n scorecard,\n detailedFindings,\n opportunities,\n pitchNumbers,\n bottomLine,\n pagesReviewed,\n pillarScores,\n coherenceGated,\n criteriaScored: results.length,\n topFixes: topFixesRaw.map(f => ({ fix: f.clientName, impact: `+${f.impactPoints} pts`, effort: f.effort })),\n elapsed,\n ...(renderedWithHeadless && { renderedWithHeadless: true }),\n };\n}\n","/**\n * Comparison mode - run two audits in parallel and compute per-criterion deltas.\n */\n\nimport { audit } from './audit.js';\nimport type { AuditOptions, AuditResult } from './audit.js';\n\nexport interface CriterionComparison {\n id: number;\n criterion: string;\n scoreA: number;\n scoreB: number;\n delta: number;\n statusA: string;\n statusB: string;\n}\n\nexport interface ComparisonResult {\n siteA: AuditResult;\n siteB: AuditResult;\n comparison: {\n scoreDelta: number;\n criteria: CriterionComparison[];\n siteAAdvantages: string[];\n siteBAdvantages: string[];\n tied: string[];\n };\n}\n\n/**\n * Audit two domains in parallel and build a per-criterion comparison.\n */\nexport async function compare(\n domainA: string,\n domainB: string,\n options?: AuditOptions,\n): Promise<ComparisonResult> {\n const [siteA, siteB] = await Promise.all([\n audit(domainA, options),\n audit(domainB, options),\n ]);\n\n const criteria: CriterionComparison[] = [];\n const siteAAdvantages: string[] = [];\n const siteBAdvantages: string[] = [];\n const tied: string[] = [];\n\n // Zip scorecards by id (both should have 26 items in the same order)\n for (let i = 0; i < siteA.scorecard.length; i++) {\n const a = siteA.scorecard[i];\n const b = siteB.scorecard[i];\n if (!a || !b) continue;\n\n const delta = a.score - b.score;\n\n criteria.push({\n id: a.id,\n criterion: a.criterion,\n scoreA: a.score,\n scoreB: b.score,\n delta,\n statusA: a.status,\n statusB: b.status,\n });\n\n if (delta > 0) siteAAdvantages.push(a.criterion);\n else if (delta < 0) siteBAdvantages.push(a.criterion);\n else tied.push(a.criterion);\n }\n\n return {\n siteA,\n siteB,\n comparison: {\n scoreDelta: siteA.overallScore - siteB.overallScore,\n criteria,\n siteAAdvantages,\n siteBAdvantages,\n tied,\n },\n };\n}\n"],"mappings":"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","names":["text","wordCount","wordCount","cap","getTextContent","extractQuestionHeadings","cap","wordCount","getTextContent","countWords","wordCount","extractTitle","getTextContent","countWords","wordCount","CRITERION_WEIGHTS","CRITERION_WEIGHTS","crawlFullSite"]}
|
|
1
|
+
{"version":3,"sources":["../src/parked-domain.ts","../src/duplicate-content.ts","../src/helpful-content.ts","../src/site-crawler.ts","../src/scoring.ts","../src/pillars.ts","../src/scorecard-builder.ts","../src/narrative-generator.ts","../src/multi-page-fetcher.ts","../src/page-scorer.ts","../src/page-analyzer.ts","../src/link-graph.ts","../src/fix-engine.ts","../src/headless-fetch.ts","../src/audit.ts","../src/compare.ts"],"sourcesContent":["/**\n * Parked domain detection.\n * Vendored from @aeo/queue/redirect-check (pure functions, no network).\n */\n\nexport interface ParkedDomainResult {\n isParked: boolean;\n reason?: string;\n}\n\n/** Known parking paths used by domain parking services */\nconst PARKING_PATHS = ['/lander', '/parking', '/park', '/sedoparking'];\n\n/** Known parking service domains found in page HTML (scripts, iframes, links) */\nconst PARKING_SERVICE_DOMAINS = [\n 'sedoparking.com',\n 'parkingcrew.net',\n 'bodis.com',\n 'dsparking.com',\n 'hugedomains.com',\n 'afternic.com',\n 'dan.com',\n 'undeveloped.com',\n 'domainmarket.com',\n 'sav.com',\n 'domaincontrol.com',\n 'above.com',\n 'domainlore.com',\n 'domainnamesales.com',\n 'brandbucket.com',\n 'squadhelp.com',\n 'godaddy.com/domainsearch',\n];\n\n/** Parking-specific text patterns (case-insensitive) */\nconst PARKING_TEXT_PATTERNS = [\n /\\bbuy this domain\\b/i,\n /\\bdomain is for sale\\b/i,\n /\\bthis domain may be for sale\\b/i,\n /\\bdomain for sale\\b/i,\n /\\bthis domain name is available\\b/i,\n /\\bparked by/i,\n /\\bthis page is parked/i,\n /\\bdomain has expired/i,\n /\\bthis domain has been registered/i,\n /\\bmake an offer on this domain\\b/i,\n /\\bget this domain\\b/i,\n /\\bacquire this domain\\b/i,\n];\n\nfunction detectParkingRedirect(bodySnippet: string): string | null {\n const relativeRedirect = bodySnippet.match(\n /window\\.location\\.(replace|assign|href)\\s*[=(]\\s*['\"](\\/[^'\"]*)['\"]/i,\n );\n if (!relativeRedirect) return null;\n const path = relativeRedirect[2].toLowerCase().replace(/[?#].*/, '');\n if (PARKING_PATHS.includes(path)) {\n return `js-redirect to ${relativeRedirect[2]}`;\n }\n return null;\n}\n\nfunction detectParkingService(bodySnippet: string): string | null {\n const lower = bodySnippet.toLowerCase();\n for (const service of PARKING_SERVICE_DOMAINS) {\n if (lower.includes(service)) {\n return `parking service: ${service}`;\n }\n }\n return null;\n}\n\nfunction detectParkingText(bodySnippet: string): string | null {\n for (const pattern of PARKING_TEXT_PATTERNS) {\n if (pattern.test(bodySnippet)) {\n return `parking text: ${bodySnippet.match(pattern)?.[0]}`;\n }\n }\n return null;\n}\n\n/**\n * Detect if a page is a parked/lost/for-sale domain.\n * Pure function - no network calls.\n */\nexport function detectParkedDomain(bodySnippet: string): ParkedDomainResult {\n const parkingRedirect = detectParkingRedirect(bodySnippet);\n if (parkingRedirect) return { isParked: true, reason: parkingRedirect };\n\n const parkingService = detectParkingService(bodySnippet);\n if (parkingService) return { isParked: true, reason: parkingService };\n\n const parkingText = detectParkingText(bodySnippet);\n if (parkingText) return { isParked: true, reason: parkingText };\n\n return { isParked: false };\n}\n","export interface DuplicateContentParagraph {\n text: string;\n shingles: Set<string>;\n}\n\nexport interface DuplicateContentSection {\n heading: string;\n paragraphs: DuplicateContentParagraph[];\n}\n\nconst BOILERPLATE_PATTERNS = /\\b(sign up|subscribe|get started|contact us|request a demo|free trial|book a call|schedule a|learn more|click here|follow us|share this|copyright|all rights reserved|privacy policy|terms of service)\\b/i;\n\n// Duplicate detection should focus on substantive copy, not short metadata rows.\n// 15 words is the shortest real body-paragraph fixture in the test suite, while\n// the Medicare false positives that triggered this work are 14 words or fewer.\nconst MIN_SUBSTANTIVE_WORDS = 15;\nconst MAX_METADATA_WORDS = 24;\nconst MAX_METADATA_LABEL_WORDS = 4;\n\nfunction normalizeParagraphText(htmlFragment: string): string {\n return htmlFragment\n .replace(/<[^>]*>/g, ' ')\n .replace(/&\\w+;/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .toLowerCase();\n}\n\nfunction tokenize(text: string): string[] {\n return text\n .split(/\\s+/)\n .map(word => word.replace(/^[^a-z0-9]+|[^a-z0-9]+$/gi, ''))\n .filter((word): word is string => word.length > 0);\n}\n\nfunction isBoilerplateParagraph(text: string, words: number): boolean {\n if (words < 20 && BOILERPLATE_PATTERNS.test(text)) return true;\n if (/\\b(cookie|gdpr|consent|opt.out)\\b/i.test(text) && words < 30) return true;\n return false;\n}\n\nfunction isMetadataParagraph(text: string, words: number): boolean {\n const labelMatch = text.match(/^([^:]{1,60}):\\s+/);\n if (!labelMatch) return false;\n\n const labelWords = tokenize(labelMatch[1]).length;\n return labelWords > 0 && labelWords <= MAX_METADATA_LABEL_WORDS && words <= MAX_METADATA_WORDS;\n}\n\nfunction buildShinglesFromTokens(words: string[], n: number = 4): Set<string> {\n const shingles = new Set<string>();\n for (let i = 0; i <= words.length - n; i++) {\n shingles.add(words.slice(i, i + n).join(' '));\n }\n return shingles;\n}\n\nfunction createParagraph(htmlFragment: string): DuplicateContentParagraph | null {\n const text = normalizeParagraphText(htmlFragment);\n const words = tokenize(text);\n if (words.length < MIN_SUBSTANTIVE_WORDS) return null;\n if (isBoilerplateParagraph(text, words.length)) return null;\n if (isMetadataParagraph(text, words.length)) return null;\n\n const shingles = buildShinglesFromTokens(words);\n if (shingles.size < 3) return null;\n\n return { text, shingles };\n}\n\nfunction stripNonContentHtml(html: string): string {\n return html\n .replace(/<(script|style|nav|header|footer|noscript)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi, '')\n .replace(/<aside\\b[^>]*>[\\s\\S]*?<\\/aside>/gi, '');\n}\n\nexport function extractDuplicateContentParagraphs(html: string): DuplicateContentParagraph[] {\n const cleaned = stripNonContentHtml(html);\n const matches = cleaned.match(/<p\\b[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n return matches\n .map(createParagraph)\n .filter((paragraph): paragraph is DuplicateContentParagraph => paragraph !== null);\n}\n\nexport function extractDuplicateContentSections(html: string): DuplicateContentSection[] {\n const cleaned = stripNonContentHtml(html);\n const parts = cleaned.split(/(?=<h[23]\\b[^>]*>)/i);\n const sections: DuplicateContentSection[] = [];\n\n for (const part of parts) {\n const headingMatch = part.match(/<h[23]\\b[^>]*>([\\s\\S]*?)<\\/h[23]>/i);\n const heading = headingMatch\n ? headingMatch[1].replace(/<[^>]*>/g, '').trim()\n : '(intro)';\n\n const paragraphs = (part.match(/<p\\b[^>]*>([\\s\\S]*?)<\\/p>/gi) || [])\n .map(createParagraph)\n .filter((paragraph): paragraph is DuplicateContentParagraph => paragraph !== null);\n\n if (paragraphs.length > 0) sections.push({ heading, paragraphs });\n }\n\n return sections;\n}\n\nexport function shingleJaccardSimilarity(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 && b.size === 0) return 0;\n\n let intersection = 0;\n for (const shingle of a) {\n if (b.has(shingle)) intersection++;\n }\n\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n","/**\n * Shared heuristics for helpful-content style criteria inspired by\n * Google's Who / How / Why / Experience framing.\n *\n * These are intentionally conservative:\n * - strong signals should be rewarded\n * - weak or inapplicable pages should often score neutral rather than fail\n * - no attempt is made to \"detect AI writing\"\n */\n\nfunction cap(value: number, max: number): number {\n return Math.min(max, value);\n}\n\nfunction floor(value: number, min: number): number {\n return Math.max(min, value);\n}\n\nfunction countMatches(text: string, pattern: RegExp): number {\n return text.match(pattern)?.length ?? 0;\n}\n\nfunction stripScriptsAndStyles(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, ' ')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, ' ');\n}\n\nexport function getTextContent(html: string): string {\n return stripScriptsAndStyles(html)\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction getBodyHtml(html: string): string {\n const bodyMatch = html.match(/<body[^>]*>([\\s\\S]*?)<\\/body>/i);\n return bodyMatch ? bodyMatch[1] : html;\n}\n\nfunction getFirstParagraphText(html: string): string {\n const firstPara = getBodyHtml(html).match(/<p[^>]*>([\\s\\S]*?)<\\/p>/i);\n return firstPara ? firstPara[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction firstNWords(text: string, count: number): string {\n return text.split(/\\s+/).slice(0, count).join(' ');\n}\n\nfunction getHeadingTexts(html: string): string[] {\n return (html.match(/<h[1-3][^>]*>([\\s\\S]*?)<\\/h[1-3]>/gi) || [])\n .map(h => h.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim())\n .filter(Boolean);\n}\n\nfunction getH1Text(html: string): string {\n const match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n return match ? match[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTitleText(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction wordCount(text: string): number {\n return text ? text.split(/\\s+/).filter(Boolean).length : 0;\n}\n\nfunction isContentLikePage(html: string, url?: string): boolean {\n const text = getTextContent(html);\n const wc = wordCount(text);\n let signals = 0;\n\n if (url && /\\/(?:blog|article|articles|guide|guides|docs|learn|help|news|insights|resources|how-to|tutorial|case-stud|whitepaper|faq)\\b/i.test(url)) {\n signals += 2;\n }\n if (/<article[\\s>]/i.test(html)) signals += 1;\n if ((html.match(/<h[2-3][^>]*>/gi) || []).length >= 2) signals += 1;\n if (wc >= 500) signals += 1;\n if (/<time[\\s>]/i.test(html) || /datePublished|dateModified/i.test(html)) signals += 1;\n if (/written\\s+by|authored?\\s+by|reviewed\\s+by|medically\\s+reviewed/i.test(text)) signals += 1;\n\n return signals >= 2;\n}\n\nfunction expectsMethodology(html: string, url?: string): boolean {\n const text = getTextContent(html);\n const title = `${getTitleText(html)} ${getH1Text(html)}`.toLowerCase();\n const urlText = (url || '').toLowerCase();\n\n if (/(?:review|compare|comparison|vs\\.?|best|benchmark|study|analysis|survey|report|research|tested|test|methodology)/i.test(title)) {\n return true;\n }\n if (/(?:review|compare|comparison|benchmark|study|analysis|survey|research|report|best)/i.test(urlText)) {\n return true;\n }\n return /\\b(methodology|how we tested|how we reviewed|our testing|sample size|dataset|benchmark|editorial policy|review process)\\b/i.test(text);\n}\n\nfunction titleAndBodyAlign(html: string): boolean {\n const h1 = getH1Text(html);\n const title = getTitleText(html);\n const text = firstNWords(getTextContent(html), 250).toLowerCase();\n const topic = `${title} ${h1}`.toLowerCase();\n const keywords = topic\n .split(/[\\s|:()\\-/]+/)\n .filter(w => w.length >= 5 && !/^(about|guide|complete|ultimate|best|learn|understand|what|when|where|which|their|there|these|those)$/i.test(w));\n const uniqueKeywords = [...new Set(keywords)];\n if (uniqueKeywords.length === 0) return false;\n return uniqueKeywords.filter(w => text.includes(w)).length >= Math.min(2, uniqueKeywords.length);\n}\n\nconst GENERIC_OPENERS = /^(?:in today'?s|it is important to understand|in this (?:article|guide|post)|whether you are|have you ever|welcome to|if you'?re looking|in the modern|in the digital age)/i;\nconst PRACTICAL_LANGUAGE = /\\b(?:here'?s what to do|choose\\s+\\w+\\s+if|avoid\\s+\\w+\\s+when|the main risk is|the fastest option is|next step(?:s)?|best option|lowest risk|good fit if|not a good fit if|what to do next)\\b/gi;\nconst TRADEOFF_LANGUAGE = /\\b(?:however|but|trade-?off|caveat|limitation|downside|upside|risk|benefit|pros?\\b|cons?\\b|watch out|be careful|unless)\\b/gi;\nconst FLUFF_LANGUAGE = /\\b(?:comprehensive guide|ever-evolving|fast-paced world|unlock(?:ing)? the power|in conclusion|delve into|navigate the landscape|journey|seamless|robust solution)\\b/gi;\nconst EARLY_CTA_PATTERN = /\\b(?:sign up|get started|book (?:a )?demo|contact us|schedule (?:a )?call|buy now|start free|request a quote|talk to sales|subscribe)\\b/gi;\nconst FIRST_HAND_ACTIONS = /\\b(?:we|i)\\s+(?:tested|used|reviewed|implemented|measured|compared|observed|deployed|migrated|surveyed|analyzed|audited|interviewed)\\b/gi;\nconst EXPERIENCE_CONTEXT = /\\b(?:in our|during our|for our|in practice|in production|in the field|in our clinic|during implementation|over \\d+\\s+(?:days?|weeks?|months?)|across \\d+\\s+(?:accounts?|customers?|patients?|sites?|campaigns?)|with \\d+\\s+(?:participants?|users?|patients?|samples?))\\b/gi;\nconst EXPERIENCE_ARTIFACTS = /\\b(?:screenshot|photo|benchmark|timeline|before\\/after|our results|original chart|field note|walkthrough)\\b/gi;\nconst LIMITATION_LANGUAGE = /\\b(?:limitation|limitations|we found that|we learned|one caveat|did not work|failed|issue we hit|edge case|unexpectedly|drawback)\\b/gi;\nconst AUTHOR_LINK_PATTERN = /<a[^>]*href=[\"'][^\"']*\\/(?:author|authors|team|people|staff|reviewers?)\\b[^\"']*[\"'][^>]*>/i;\nconst BYLINE_PATTERN = /\\b(?:written|authored|reviewed|edited|medically reviewed)\\s+by\\b/i;\nconst AUTHOR_BIO_PATTERN = /\\b(?:about the author|author bio|reviewed by|medically reviewed by|board-certified|licensed|credentials?|specializes in|specialist in)\\b/i;\nconst METHODOLOGY_TERMS = /\\b(?:methodology|how we tested|how we reviewed|our methodology|testing process|review process|editorial policy|sample size|data collection|timeframe|criteria used|tools used|benchmark method|updated using|last reviewed|ai-assisted|ai assisted)\\b/gi;\nconst METHODOLOGY_DETAIL = /\\b(?:sample size|participants?|dataset|timeframe|criteria|weights?|tool(?:s)? used|measured over|tested over|reviewed on|last updated|last reviewed|scoring method)\\b/gi;\n\nexport function scoreHelpfulPurposeAlignment(html: string, url?: string): number {\n const text = getTextContent(html);\n if (!text) return 0;\n\n const contentLike = isContentLikePage(html, url);\n if (!contentLike && wordCount(text) < 250) return 5;\n\n let score = contentLike ? 3 : 5;\n const firstPara = getFirstParagraphText(html);\n const earlyText = firstNWords(text, 300);\n const bodyHtml = getBodyHtml(html);\n\n if (firstPara && !GENERIC_OPENERS.test(firstPara)) score += 2;\n if (countMatches(earlyText, PRACTICAL_LANGUAGE) >= 1) score += 2;\n else if (countMatches(text, PRACTICAL_LANGUAGE) >= 2) score += 1;\n\n const tradeoffCount = countMatches(text, TRADEOFF_LANGUAGE);\n if (tradeoffCount >= 2) score += 2;\n else if (tradeoffCount >= 1) score += 1;\n\n if (titleAndBodyAlign(html)) score += 1;\n if (/\\b(?:bottom line|key takeaway|here'?s the short answer|next steps?)\\b/i.test(text)) score += 1;\n\n if (firstPara && GENERIC_OPENERS.test(firstPara)) score -= 2;\n\n const earlyBodyHtml = bodyHtml.slice(0, 1800);\n const earlyCtas = countMatches(earlyBodyHtml, EARLY_CTA_PATTERN);\n if (earlyCtas >= 3) score -= 2;\n else if (earlyCtas >= 2) score -= 1;\n\n const fluffCount = countMatches(text, FLUFF_LANGUAGE);\n if (fluffCount >= 3) score -= 2;\n else if (fluffCount >= 1) score -= 1;\n\n return floor(cap(score, 10), 0);\n}\n\nexport function scoreFirstHandExperienceSignals(html: string, url?: string): number {\n const text = getTextContent(html);\n if (!text) return 0;\n\n const contentLike = isContentLikePage(html, url);\n let score = contentLike ? 2 : 5;\n\n const actionCount = countMatches(text, FIRST_HAND_ACTIONS);\n if (actionCount >= 3) score += 4;\n else if (actionCount >= 1) score += 2;\n\n const contextCount = countMatches(text, EXPERIENCE_CONTEXT);\n if (contextCount >= 2) score += 2;\n else if (contextCount >= 1) score += 1;\n\n const artifactCount = countMatches(text, EXPERIENCE_ARTIFACTS) + countMatches(html, /<figure|<figcaption/gi);\n if (artifactCount >= 3) score += 2;\n else if (artifactCount >= 1) score += 1;\n\n const limitationCount = countMatches(text, LIMITATION_LANGUAGE);\n if (limitationCount >= 2) score += 2;\n else if (limitationCount >= 1) score += 1;\n\n if (/\\b(?:manufacturer|vendor)\\s+(?:description|specification|copy)\\b/i.test(text)) score -= 1;\n\n return floor(cap(score, 10), 0);\n}\n\nexport function scoreCreatorTransparency(html: string, url?: string): number {\n const text = getTextContent(html);\n if (!text) return 0;\n\n const contentLike = isContentLikePage(html, url);\n if (!contentLike) return 5;\n\n let score = 0;\n const hasByline = BYLINE_PATTERN.test(text) || /class=[\"'][^\"']*author[^\"']*[\"']/i.test(html) || /rel=[\"']author[\"']/i.test(html);\n const hasPersonSchema = /\"@type\"\\s*:\\s*\"Person\"/i.test(html);\n\n if (hasByline) score += 3;\n if (AUTHOR_LINK_PATTERN.test(html)) score += 2;\n if (AUTHOR_BIO_PATTERN.test(text)) score += 2;\n if (/\\b(?:reviewed by|edited by|medically reviewed by)\\b/i.test(text)) score += 1;\n if (hasPersonSchema) score += 2;\n\n return floor(cap(score, 10), 0);\n}\n\nexport function scoreMethodologyTransparency(html: string, url?: string): number {\n const text = getTextContent(html);\n if (!text) return 0;\n\n const contentLike = isContentLikePage(html, url);\n const expected = expectsMethodology(html, url);\n\n let score = expected ? 2 : (contentLike ? 5 : 5);\n\n const methodologyCount = countMatches(text, METHODOLOGY_TERMS);\n if (methodologyCount >= 2) score += 3;\n else if (methodologyCount >= 1) score += 2;\n\n const detailCount = countMatches(text, METHODOLOGY_DETAIL);\n if (detailCount >= 3) score += 3;\n else if (detailCount >= 2) score += 2;\n else if (detailCount >= 1) score += 1;\n\n if (/\\b(?:tested|reviewed|analyzed)\\s+\\d+|\\bacross\\s+\\d+|\\bover\\s+\\d+\\s+(?:days?|weeks?|months?)|\\busing\\s+\\d+\\s+\\w+/i.test(text)) score += 1;\n if ((/<figure|<table/i.test(html)) && methodologyCount >= 1) score += 1;\n if (/\\b(?:ai-assisted|ai assisted|reviewed by an editor|human reviewed)\\b/i.test(text)) score += 1;\n\n return floor(cap(score, 10), 0);\n}\n","import type { AuditFinding, AuditStatus, Priority } from './types.js';\nimport { detectParkedDomain } from './parked-domain.js';\nimport {\n extractDuplicateContentParagraphs,\n extractDuplicateContentSections,\n shingleJaccardSimilarity,\n} from './duplicate-content.js';\nimport type { DuplicateContentParagraph } from './duplicate-content.js';\nimport {\n scoreHelpfulPurposeAlignment,\n scoreFirstHandExperienceSignals,\n scoreCreatorTransparency,\n scoreMethodologyTransparency,\n} from './helpful-content.js';\nimport { isSafeFetchTarget, isSafePublicUrl, safeFetch } from './network-guard.js';\n\nexport interface CriterionResult {\n criterion: string;\n criterion_label: string;\n score: number;\n status: AuditStatus;\n findings: AuditFinding[];\n fix_priority: Priority;\n}\n\n// ─── Pre-fetched site data (one fetch per URL, reused across all criteria) ───\n\nexport type PageCategory = 'homepage' | 'blog' | 'about' | 'pricing' | 'services'\n | 'contact' | 'team' | 'resources' | 'docs' | 'cases' | 'faq' | 'content';\n\nexport interface FetchResult {\n text: string;\n status: number;\n finalUrl?: string;\n category?: PageCategory;\n}\n\nexport interface SiteData {\n domain: string;\n protocol: 'https' | 'http' | null;\n homepage: FetchResult | null;\n llmsTxt: FetchResult | null;\n robotsTxt: FetchResult | null;\n faqPage: FetchResult | null;\n sitemapXml: FetchResult | null;\n rssFeed: FetchResult | null;\n aiTxt: FetchResult | null;\n /** Set when homepage redirects to a different (non-brand) domain */\n redirectedTo: string | null;\n /** Set when homepage is a parked/for-sale/lost domain */\n parkedReason: string | null;\n /** Sampled blog/content pages from sitemap (up to 50) */\n blogSample?: FetchResult[];\n /** Full-crawl statistics (set when --full-crawl is used) */\n crawlStats?: {\n discovered: number;\n fetched: number;\n skipped: number;\n elapsed: number;\n };\n /** Link graph from full-site crawl */\n linkGraph?: import('./link-graph.js').LinkGraph;\n}\n\n// Raw data summary for AI narrative generation\nexport interface RawDataSummary {\n domain: string;\n protocol: 'https' | 'http' | null;\n homepage_length: number;\n homepage_text_length: number;\n has_https: boolean;\n llms_txt_status: number | null;\n llms_txt_length: number;\n robots_txt_status: number | null;\n robots_txt_snippet: string;\n robots_txt_ai_crawlers: string[];\n robots_txt_blocked_crawlers: string[];\n schema_types_found: string[];\n schema_block_count: number;\n faq_page_status: number | null;\n faq_page_length: number;\n sitemap_status: number | null;\n internal_link_count: number;\n external_link_count: number;\n question_headings_count: number;\n h1_count: number;\n has_meta_description: boolean;\n has_title: boolean;\n has_phone: boolean;\n has_address: boolean;\n has_org_schema: boolean;\n has_social_links: boolean;\n semantic_elements_found: string[];\n img_count: number;\n img_with_alt_count: number;\n has_lang_attr: boolean;\n has_aria: boolean;\n has_breadcrumbs: boolean;\n has_nav: boolean;\n has_footer: boolean;\n has_case_studies: boolean;\n has_statistics: boolean;\n has_expert_attribution: boolean;\n has_blog_section: boolean;\n // New criteria fields\n has_date_modified_schema: boolean;\n time_element_count: number;\n sitemap_url_count: number;\n has_rss_feed: boolean;\n table_count: number;\n ordered_list_count: number;\n unordered_list_count: number;\n definition_pattern_count: number;\n has_ai_txt: boolean;\n has_person_schema: boolean;\n fact_data_point_count: number;\n has_canonical: boolean;\n has_license_schema: boolean;\n sitemap_recent_lastmod_count: number;\n rendered_with_headless?: boolean;\n // Speakable schema fields\n has_speakable_schema: boolean;\n speakable_selector_count: number;\n // Blog sample fields\n blog_sample_count: number;\n blog_sample_urls: string[];\n blog_sample_schema_types: string[];\n blog_sample_question_headings: number;\n blog_sample_faq_schema_found: boolean;\n // Criteria 24-26 fields\n question_heading_answer_rate: number; // -1 = N/A, 0-100 = %\n question_heading_total: number;\n cannibalizing_pairs_count: number;\n page_titles_sampled: number;\n has_visible_date: boolean;\n has_schema_date_in_ld: boolean;\n date_modified_recency_days: number | null;\n // Full-crawl stats\n crawl_discovered: number;\n crawl_fetched: number;\n crawl_skipped: number;\n // V2 criteria fields\n citation_ready_sentences: number;\n answer_first_ratio: number; // 0-100, % of pages with answer block in first 300 words\n evidence_citations_avg: number; // avg inline citations per page\n entity_disambiguation_ratio: number; // 0-100, % of pages defining primary entity early\n extraction_friction_avg: number; // avg sentence length in words\n image_figure_ratio: number; // 0-100, % of images using figure/figcaption\n}\n\nasync function fetchText(url: string, expectedDomain?: string): Promise<FetchResult | null> {\n const res = await safeFetch(url, { timeoutMs: 15000, expectedDomain });\n if (!res) return null;\n const text = await res.text();\n return { text: text.slice(0, 500000), status: res.status, finalUrl: res.url };\n}\n\n/** Extract bare domain from URL (no protocol, www, port, path) */\nfunction extractDomain(url: string): string {\n return url.replace(/^https?:\\/\\//, '').replace(/\\/.*/, '').replace(/:[0-9]+$/, '').replace(/^www\\./, '').toLowerCase();\n}\n\n/** Extract brand name from domain (everything before TLD) */\nfunction extractBrandName(domain: string): string {\n const parts = domain.split('.');\n const twoPartTlds = ['co.uk', 'com.au', 'co.jp', 'com.br', 'co.nz', 'co.in'];\n const lastTwo = parts.slice(-2).join('.');\n if (twoPartTlds.includes(lastTwo) && parts.length > 2) {\n return parts.slice(0, -2).join('.');\n }\n return parts.length > 1 ? parts.slice(0, -1).join('.') : domain;\n}\n\n/** Check if redirect target is a different brand (hijacked domain) */\nfunction detectCrossDomainRedirect(originalDomain: string, homepage: FetchResult): string | null {\n if (!homepage.finalUrl) return null;\n const finalDomain = extractDomain(homepage.finalUrl);\n const cleanOriginal = originalDomain.replace(/^www\\./, '').toLowerCase();\n if (\n finalDomain === cleanOriginal ||\n finalDomain === `www.${cleanOriginal}` ||\n extractBrandName(finalDomain) === extractBrandName(cleanOriginal)\n ) {\n return null;\n }\n return finalDomain;\n}\n\n/** Detect JS-based cross-domain redirect in HTML body */\nfunction detectJsRedirect(bodySnippet: string, originalDomain: string): string | null {\n const jsMatch = bodySnippet.match(\n /window\\.location\\.(replace|assign|href)\\s*[=(]\\s*['\"]https?:\\/\\/([^'\"]+)['\"]/i,\n );\n if (!jsMatch) return null;\n const jsDomain = extractDomain('https://' + jsMatch[2]);\n const cleanOriginal = originalDomain.replace(/^www\\./, '').toLowerCase();\n if (\n jsDomain === cleanOriginal ||\n jsDomain === `www.${cleanOriginal}` ||\n extractBrandName(jsDomain) === extractBrandName(cleanOriginal)\n ) {\n return null;\n }\n return jsDomain;\n}\n\n/** Detect HTML served for plain-text URLs (e.g. catch-all routes returning 200 for /ai.txt) */\nfunction isHtmlResponse(result: FetchResult | null): boolean {\n if (!result || result.status !== 200) return false;\n const trimmed = result.text.trimStart().slice(0, 200).toLowerCase();\n return trimmed.startsWith('<!doctype html') || trimmed.startsWith('<html') || /<head[\\s>]/i.test(trimmed);\n}\n\n/**\n * Fetches all site data in parallel with HTTPS/HTTP fallback.\n * Single entry point for all HTTP requests - no redundant fetches.\n */\nexport async function prefetchSiteData(domain: string): Promise<SiteData> {\n if (!(await isSafeFetchTarget(`https://${domain}`))) {\n return { domain, protocol: null, homepage: null, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo: null, parkedReason: null, blogSample: [] };\n }\n\n // Step 1: Detect protocol (HTTPS first, fallback to HTTP)\n let protocol: 'https' | 'http' | null = null;\n let homepage: FetchResult | null = null;\n\n homepage = await fetchText(`https://${domain}`, domain);\n if (homepage && homepage.status >= 200 && homepage.status < 400) {\n protocol = 'https';\n } else {\n homepage = await fetchText(`http://${domain}`, domain);\n if (homepage && homepage.status >= 200 && homepage.status < 400) {\n protocol = 'http';\n }\n }\n\n if (!protocol) {\n return { domain, protocol: null, homepage: null, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo: null, parkedReason: null, blogSample: [] };\n }\n\n // Check for cross-domain redirect (hijacked/expired domains)\n const httpRedirect = homepage ? detectCrossDomainRedirect(domain, homepage) : null;\n const jsRedirect = homepage ? detectJsRedirect(homepage.text.slice(0, 8192), domain) : null;\n const redirectedTo = httpRedirect || jsRedirect;\n\n if (redirectedTo) {\n return { domain, protocol, homepage, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo, parkedReason: null, blogSample: [] };\n }\n\n // Check for parked/lost/for-sale domains\n const parkedResult = homepage ? detectParkedDomain(homepage.text.slice(0, 8192)) : { isParked: false };\n if (parkedResult.isParked) {\n return { domain, protocol, homepage, llmsTxt: null, robotsTxt: null, faqPage: null, sitemapXml: null, rssFeed: null, aiTxt: null, redirectedTo: null, parkedReason: parkedResult.reason || 'parked', blogSample: [] };\n }\n\n const baseUrl = `${protocol}://${domain}`;\n\n // Step 2: Fetch all other resources in parallel\n const [llmsTxt, robotsTxt, faqPage, sitemapXml, aiTxt] = await Promise.all([\n fetchText(`${baseUrl}/llms.txt`, domain),\n fetchText(`${baseUrl}/robots.txt`, domain),\n fetchText(`${baseUrl}/faq`, domain).then(async (result) => {\n if (result && result.status === 200) return result;\n // Fallback chain for FAQ page variants\n for (const path of ['/frequently-asked-questions', '/help', '/support', '/help-center']) {\n const fallback = await fetchText(`${baseUrl}${path}`, domain);\n if (fallback && fallback.status === 200) return fallback;\n }\n return result;\n }),\n fetchText(`${baseUrl}/sitemap.xml`, domain),\n fetchText(`${baseUrl}/ai.txt`, domain),\n ]);\n\n // Step 3: Discover RSS feed URL from homepage, then fetch it\n let rssFeed: FetchResult | null = null;\n if (homepage) {\n const rssLinkMatch = homepage.text.match(/<link[^>]*type=\"application\\/(?:rss|atom)\\+xml\"[^>]*href=\"([^\"]*)\"[^>]*>/i);\n if (rssLinkMatch) {\n const rssUrl = rssLinkMatch[1].startsWith('http') ? rssLinkMatch[1] : `${baseUrl}${rssLinkMatch[1]}`;\n rssFeed = await fetchText(rssUrl, domain);\n }\n if (!rssFeed || rssFeed.status !== 200) {\n // Fallback: try common RSS paths\n for (const path of ['/feed', '/rss.xml', '/feed.xml']) {\n rssFeed = await fetchText(`${baseUrl}${path}`, domain);\n if (rssFeed && rssFeed.status === 200 && (rssFeed.text.includes('<rss') || rssFeed.text.includes('<feed') || rssFeed.text.includes('<channel'))) break;\n rssFeed = null;\n }\n }\n }\n\n // Step 4: If sitemapindex, fetch sub-sitemaps so downstream checks find <lastmod> and <url> entries\n if (sitemapXml && sitemapXml.status === 200 && sitemapXml.text.includes('<sitemapindex')) {\n const subUrls = extractAllSubSitemapUrls(sitemapXml.text, domain, 5);\n if (subUrls.length > 0) {\n const subResults = await Promise.all(subUrls.map(u => fetchText(u, domain)));\n for (const sub of subResults) {\n if (sub && sub.status === 200) {\n sitemapXml.text += '\\n' + sub.text;\n }\n }\n }\n }\n\n // Step 5: Sample blog pages from sitemap (up to 5)\n let blogSample: FetchResult[] = [];\n if (sitemapXml && sitemapXml.status === 200) {\n const sitemapForBlog = sitemapXml.text;\n\n const blogUrls = extractBlogUrlsFromSitemap(sitemapForBlog, domain, 50);\n if (blogUrls.length > 0) {\n const fetched = await Promise.all(blogUrls.map(url => fetchText(url, domain)));\n blogSample = fetched.filter((r): r is FetchResult =>\n r !== null && r.status === 200 && r.text.length > 500\n );\n // Tag blog sample pages\n for (const page of blogSample) {\n page.category = 'blog';\n }\n }\n }\n\n // Tag homepage\n if (homepage) homepage.category = 'homepage';\n\n return { domain, protocol, homepage, llmsTxt, robotsTxt, faqPage, sitemapXml, rssFeed, aiTxt, redirectedTo: null, parkedReason: null, blogSample };\n}\n\n// ─── Blog sample helpers ─────────────────────────────────────────────────────\n\n/** Strip executable script/style tags to avoid inline JS polluting text analysis.\n * Preserves <script type=\"application/ld+json\"> (JSON-LD structured data). */\nfunction stripScripts(html: string): string {\n return html\n .replace(/<script(?![^>]*type\\s*=\\s*[\"']application\\/ld\\+json[\"'])[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '');\n}\n\n/** Concatenate homepage + blog sample HTML for combined analysis */\nfunction getCombinedHtml(data: SiteData): string {\n const parts = [stripScripts(data.homepage?.text || '')];\n if (data.blogSample) {\n for (const page of data.blogSample) {\n parts.push(stripScripts(page.text));\n }\n }\n return parts.join('\\n');\n}\n\n/** Get blog-only HTML concatenated */\nfunction getBlogHtml(data: SiteData): string {\n if (!data.blogSample || data.blogSample.length === 0) return '';\n return data.blogSample.map(p => stripScripts(p.text)).join('\\n');\n}\n\n// ─── Criterion checks (all use pre-fetched SiteData) ────────────────────────\n\n// Criterion 1: llms.txt\nfunction checkLlmsTxt(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const result = data.llmsTxt;\n\n if (!result || result.status !== 200 || isHtmlResponse(result)) {\n const statusNote = result ? (isHtmlResponse(result) ? 'HTML page served (not a valid text file)' : `HTTP ${result.status}`) : 'connection failed';\n findings.push({ severity: 'critical', detail: `No llms.txt file found at ${data.protocol}://${data.domain}/llms.txt (${statusNote})`, fix: 'Create a /llms.txt file that describes your site, services, and key pages in markdown format' });\n return { criterion: 'llms_txt', criterion_label: 'llms.txt File', score: 0, status: 'fail', findings, fix_priority: 'P0' };\n }\n\n const text = result.text;\n let score = 4; // exists\n\n if (text.length < 100) {\n findings.push({ severity: 'medium', detail: `llms.txt exists but is very short (${text.length} characters)`, fix: 'Add comprehensive description of your services, team, and key content' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: `llms.txt file found (${text.length} characters)` });\n }\n\n if (text.includes('#') || text.includes('##')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'llms.txt uses markdown headings for structure' });\n } else {\n findings.push({ severity: 'low', detail: 'llms.txt lacks markdown structure', fix: 'Add headings (# About, ## Services, etc.) for better LLM parsing' });\n }\n\n if (/https?:\\/\\//.test(text)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'llms.txt includes URLs to key pages' });\n } else {\n findings.push({ severity: 'medium', detail: 'llms.txt does not link to key pages', fix: 'Add URLs to your most important pages (services, about, FAQ)' });\n }\n\n return { criterion: 'llms_txt', criterion_label: 'llms.txt File', score: Math.min(10, score), status: score >= 7 ? 'pass' : 'partial', findings, fix_priority: score >= 7 ? 'P3' : 'P0' };\n}\n\n// Criterion 2: Schema Markup\nfunction checkSchemaMarkup(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage to check schema markup' });\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n const ldJsonMatches = html.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD structured data found on homepage', fix: 'Add Organization, LocalBusiness, or WebSite schema in a <script type=\"application/ld+json\"> tag' });\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n score += 3;\n findings.push({ severity: 'info', detail: `Found ${ldJsonMatches.length} JSON-LD block(s) on homepage` });\n\n const allSchemaText = ldJsonMatches.join(' ').toLowerCase();\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product'];\n const foundTypes: string[] = [];\n\n for (const type of schemaTypes) {\n if (allSchemaText.includes(`\"${type}\"`) || allSchemaText.includes(`\"@type\":\"${type}\"`)) {\n foundTypes.push(type);\n }\n }\n\n if (foundTypes.length > 0) {\n score += Math.min(4, foundTypes.length * 2);\n findings.push({ severity: 'info', detail: `Schema types found: ${foundTypes.join(', ')}` });\n }\n\n if (!foundTypes.includes('organization') && !foundTypes.includes('localbusiness')) {\n findings.push({ severity: 'high', detail: 'Missing Organization or LocalBusiness schema', fix: 'Add Organization schema with name, url, logo, contactPoint, and sameAs properties' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: 'Organization or LocalBusiness schema found' });\n }\n\n if (!foundTypes.includes('faqpage')) {\n findings.push({ severity: 'medium', detail: 'No FAQPage schema found', fix: 'Add FAQPage schema on pages with FAQ content' });\n } else {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQPage schema markup present' });\n }\n\n // Blog sample enhancement: additional schema types from blog posts\n if (data.blogSample && data.blogSample.length > 0) {\n const blogHtml = getBlogHtml(data);\n const blogLdJson = blogHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n if (blogLdJson.length > 0) {\n const blogSchemaText = blogLdJson.join(' ').toLowerCase();\n const blogTypes = schemaTypes.filter(t =>\n (blogSchemaText.includes(`\"${t}\"`) || blogSchemaText.includes(`\"@type\":\"${t}\"`)) && !foundTypes.includes(t)\n );\n if (blogTypes.length > 0) {\n score += Math.min(2, blogTypes.length);\n findings.push({ severity: 'info', detail: `Additional schema types found on blog pages: ${blogTypes.join(', ')}` });\n }\n // FAQPage on blog is especially valuable\n if (!foundTypes.includes('faqpage') && /faqpage/i.test(blogSchemaText)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQPage schema found on blog posts' });\n }\n }\n }\n\n return { criterion: 'schema_markup', criterion_label: 'Schema.org Structured Data', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 3: Q&A Content Format\nfunction checkQAFormat(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'qa_content_format', criterion_label: 'Q&A Content Format', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // Count question headings from homepage + blog sample combined\n const combinedHtml = getCombinedHtml(data);\n const hTagContent = (combinedHtml.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n const questionHeadings = hTagContent.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n\n if (questionHeadings.length >= 10) {\n score += 5;\n findings.push({ severity: 'info', detail: `Found ${questionHeadings.length} question-format headings` });\n } else if (questionHeadings.length >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `Found ${questionHeadings.length} question-format headings` });\n } else if (questionHeadings.length >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${questionHeadings.length} question-format heading(s) found`, fix: 'Structure more content as Q&A with question headings (H2/H3) followed by direct answers' });\n } else {\n findings.push({ severity: 'high', detail: 'No question-format headings found', fix: 'Add Q&A sections with headings like \"What is...?\", \"How does...?\" followed by concise answers' });\n }\n\n // Check for question heading followed by a paragraph (20-500 chars, allowing nested inline tags)\n const hasDirectAnswers = /<h[2-3][^>]*>[^<]*\\?<\\/h[2-3]>\\s*<p[^>]*>[\\s\\S]{20,500}<\\/p>/i.test(combinedHtml);\n if (hasDirectAnswers) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Content uses direct-answer format after question headings' });\n } else {\n findings.push({ severity: 'medium', detail: 'Content does not follow direct-answer format', fix: 'Start each answer paragraph with a concise 1-2 sentence definition before elaborating' });\n }\n\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n if (h1Count === 1) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Proper single H1 tag hierarchy' });\n } else if (h1Count === 0) {\n findings.push({ severity: 'high', detail: 'No H1 tag found', fix: 'Add exactly one H1 tag as the main page heading' });\n } else {\n score += 1;\n findings.push({ severity: 'medium', detail: `Multiple H1 tags found (${h1Count})`, fix: 'Use only one H1 per page; use H2/H3 for subsections' });\n }\n\n return { criterion: 'qa_content_format', criterion_label: 'Q&A Content Format', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 4: Clean HTML (includes HTTPS check - v4 requirement)\nfunction checkCleanHTML(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'clean_html', criterion_label: 'Clean, Crawlable HTML', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n const httpsAvailable = data.protocol === 'https';\n\n // HTTPS check (v4 requirement: no HTTPS = cap at 3)\n if (httpsAvailable) {\n findings.push({ severity: 'info', detail: 'Site serves over HTTPS' });\n } else {\n findings.push({ severity: 'critical', detail: 'Site does not support HTTPS', fix: 'Enable HTTPS with a valid SSL certificate. Sites without HTTPS are penalized by AI crawlers.' });\n }\n\n // Check for semantic elements\n const hasMain = /<main[\\s>]/i.test(html);\n const hasArticle = /<article[\\s>]/i.test(html);\n const hasSection = /<section[\\s>]/i.test(html);\n\n const semanticCount = [hasMain, hasArticle, hasSection].filter(Boolean).length;\n score += Math.min(3, semanticCount * 1);\n if (semanticCount >= 2) {\n findings.push({ severity: 'info', detail: `Uses semantic HTML5 elements: ${[hasMain && 'main', hasArticle && 'article', hasSection && 'section'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'medium', detail: 'Limited semantic HTML5 usage', fix: 'Wrap main content in <main>, use <article> for standalone content, <section> for grouped content' });\n }\n\n // Check H1 count\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n if (h1Count === 1) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Single H1 tag found - correct heading hierarchy' });\n } else {\n findings.push({ severity: h1Count === 0 ? 'high' : 'medium', detail: `${h1Count === 0 ? 'No' : 'Multiple'} H1 tag(s) found (${h1Count})`, fix: 'Use exactly one H1 per page' });\n }\n\n // Check for text content (not JS-only)\n const textContent = html.replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\n if (textContent.length > 500) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Page has substantial text content accessible without JavaScript' });\n } else {\n findings.push({ severity: 'high', detail: 'Very little text content visible in HTML source', fix: 'Ensure key content is server-rendered, not loaded via JavaScript only' });\n }\n\n // Check for meta tags\n const hasMetaDesc = /<meta[^>]*name=\"description\"[^>]*>/i.test(html);\n const hasTitle = /<title[^>]*>[^<]+<\\/title>/i.test(html);\n if (hasMetaDesc && hasTitle) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Page has title and meta description' });\n } else {\n findings.push({ severity: 'medium', detail: `Missing ${!hasTitle ? 'title tag' : ''}${!hasTitle && !hasMetaDesc ? ' and ' : ''}${!hasMetaDesc ? 'meta description' : ''}`, fix: 'Add <title> and <meta name=\"description\"> tags' });\n }\n\n // HTTPS cap: no HTTPS = max 3 for this criterion\n if (!httpsAvailable) {\n score = Math.min(score, 3);\n }\n\n return { criterion: 'clean_html', criterion_label: 'Clean, Crawlable HTML', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 5: Entity Consistency\nfunction checkEntityConsistency(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'entity_consistency', criterion_label: 'Entity Authority & E-E-A-T', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Check for phone numbers with context validation\n // A regex match only counts if it has supporting context (tel: link, schema, or nearby keywords)\n const hasTelLink = /href=\"tel:/i.test(html);\n const hasSchemaTelephone = /\"telephone\"/i.test(html);\n const phoneContextWords = /\\b(phone|call|tel:|contact\\s*us|fax|dial)\\b/i;\n\n const phoneRegex = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\n const phones = text.match(phoneRegex) || [];\n const contextValidatedPhones: string[] = [];\n\n if (hasTelLink || hasSchemaTelephone) {\n // Global context validates all matches\n contextValidatedPhones.push(...phones);\n } else {\n // Check each match for nearby context words (~100 chars around match)\n let match: RegExpExecArray | null;\n const phoneRegex2 = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g;\n while ((match = phoneRegex2.exec(text)) !== null) {\n const start = Math.max(0, match.index - 100);\n const end = Math.min(text.length, match.index + match[0].length + 100);\n const surrounding = text.slice(start, end);\n if (phoneContextWords.test(surrounding)) {\n contextValidatedPhones.push(match[0]);\n }\n }\n }\n\n const uniquePhones = [...new Set(contextValidatedPhones.map(p => p.replace(/\\D/g, '')))];\n if (uniquePhones.length === 1) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Single consistent phone number found' });\n } else if (uniquePhones.length > 1) {\n score += 1;\n findings.push({ severity: 'medium', detail: `Multiple phone numbers found (${uniquePhones.length})`, fix: 'Use one primary phone number consistently across all pages' });\n } else {\n findings.push({ severity: 'low', detail: 'No phone number found on homepage' });\n score += 1;\n }\n\n // Check for address\n const hasAddress = /\\d+\\s+\\w+\\s+(street|st|avenue|ave|road|rd|drive|dr|blvd|boulevard|lane|ln|way|court|ct)/i.test(text);\n if (hasAddress) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Physical address found on page' });\n }\n\n // Check for Organization schema\n const hasOrgSchema = /organization|localbusiness/i.test(html);\n if (hasOrgSchema) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Organization/LocalBusiness schema reinforces entity identity' });\n } else {\n findings.push({ severity: 'high', detail: 'No Organization schema to reinforce entity identity', fix: 'Add Organization JSON-LD with consistent name, address, phone, and social links' });\n }\n\n // Check for social proof\n const hasSameAs = /sameas|linkedin\\.com|facebook\\.com|twitter\\.com|x\\.com/i.test(html);\n if (hasSameAs) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Social media / sameAs references found' });\n } else {\n findings.push({ severity: 'low', detail: 'No social media links or sameAs found', fix: 'Add sameAs links in Organization schema to social profiles' });\n }\n\n return { criterion: 'entity_consistency', criterion_label: 'Entity Authority & E-E-A-T', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 6: robots.txt\nfunction checkRobotsTxt(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const result = data.robotsTxt;\n\n if (!result || result.status !== 200 || isHtmlResponse(result)) {\n findings.push({ severity: 'high', detail: 'No robots.txt file found', fix: 'Create a robots.txt that explicitly allows AI crawlers' });\n return { criterion: 'robots_txt', criterion_label: 'robots.txt for AI Crawlers', score: 2, status: 'fail', findings, fix_priority: 'P0' };\n }\n\n const text = result.text.toLowerCase();\n let score = 3; // exists\n\n const aiCrawlers = ['gptbot', 'claudebot', 'perplexitybot', 'anthropic', 'chatgpt'];\n const mentionedCrawlers = aiCrawlers.filter(c => text.includes(c));\n\n if (mentionedCrawlers.length > 0) {\n score += 3;\n findings.push({ severity: 'info', detail: `AI crawlers mentioned: ${mentionedCrawlers.join(', ')}` });\n\n const blocked = mentionedCrawlers.filter(c => {\n // Extract only this crawler's section (up to next User-agent: or EOF)\n // NOTE: do NOT use 'm' flag here - it makes $ match end-of-line, causing\n // non-greedy [\\s\\S]*? to stop at the first line instead of capturing the full section\n const sectionRegex = new RegExp(`user-agent:\\\\s*${c}[^\\\\S\\\\n]*\\\\n([\\\\s\\\\S]*?)(?=user-agent:|$)`, 'i');\n const match = sectionRegex.exec(result.text);\n if (!match) return false;\n const section = match[1];\n // If section has \"Allow: /\", the crawler is explicitly allowed (not blocked)\n if (/^allow:\\s*\\/\\s*$/im.test(section)) return false;\n // Check for \"Disallow: /\" (root-only, not /path) within this section\n return /^disallow:\\s*\\/\\s*$/im.test(section);\n });\n if (blocked.length > 0) {\n score -= 2;\n findings.push({ severity: 'critical', detail: `AI crawlers BLOCKED: ${blocked.join(', ')}`, fix: 'Change Disallow: / to Allow: / for AI crawler user-agents' });\n } else {\n score += 2;\n findings.push({ severity: 'info', detail: 'AI crawlers are allowed to index the site' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No explicit AI crawler rules in robots.txt', fix: 'Add User-agent rules for GPTBot, ClaudeBot, PerplexityBot with Allow: /' });\n }\n\n if (text.includes('sitemap:')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Sitemap URL referenced in robots.txt' });\n } else {\n findings.push({ severity: 'low', detail: 'No sitemap reference in robots.txt', fix: 'Add Sitemap: https://yoursite.com/sitemap.xml to robots.txt' });\n }\n\n return { criterion: 'robots_txt', criterion_label: 'robots.txt for AI Crawlers', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P0' };\n}\n\n// Criterion 7: FAQ Section\nfunction checkFAQSection(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n const homepage = data.homepage;\n const hasHomepageFAQ = homepage && /faq|frequently\\s+asked/i.test(homepage.text);\n\n if (hasHomepageFAQ) {\n score += 2;\n findings.push({ severity: 'info', detail: 'FAQ content found on homepage' });\n } else {\n findings.push({ severity: 'low', detail: 'No FAQ content found on homepage', fix: 'Add an FAQ section to your homepage addressing common visitor questions' });\n }\n\n const faqPage = data.faqPage;\n const hasFaqPage = faqPage && faqPage.status === 200 && faqPage.text.length > 500;\n\n if (hasFaqPage) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Dedicated FAQ page exists' });\n\n if (/accordion|toggle|collaps|expand/i.test(faqPage.text)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'FAQ uses accordion/toggle UI pattern' });\n }\n } else {\n findings.push({ severity: 'high', detail: 'No dedicated FAQ page found at /faq', fix: 'Create a comprehensive FAQ page at /faq covering common questions about your service' });\n }\n\n // Check for FAQPage schema (homepage + FAQ page + blog sample)\n const blogHtml = getBlogHtml(data);\n const allHtml = (homepage?.text || '') + (faqPage?.text || '') + blogHtml;\n if (/faqpage/i.test(allHtml) && /application\\/ld\\+json/i.test(allHtml)) {\n score += 3;\n const faqOnBlog = blogHtml && /faqpage/i.test(blogHtml) && /application\\/ld\\+json/i.test(blogHtml);\n findings.push({ severity: 'info', detail: faqOnBlog ? 'FAQPage schema markup found on blog posts' : 'FAQPage schema markup found' });\n } else {\n findings.push({ severity: 'medium', detail: 'No FAQPage schema markup', fix: 'Add FAQPage JSON-LD schema to pages with FAQ content' });\n }\n\n const questionCount = (allHtml.match(/<h[2-4][^>]*>[^<]*\\?<\\/h[2-4]>/gi) || []).length;\n if (questionCount >= 10) {\n score += 1;\n findings.push({ severity: 'info', detail: `${questionCount} question headings found across checked pages` });\n } else if (questionCount >= 5) {\n findings.push({ severity: 'low', detail: `Only ${questionCount} question headings found`, fix: 'Expand FAQ to cover at least 10-15 common questions' });\n }\n\n return { criterion: 'faq_section', criterion_label: 'Comprehensive FAQ Sections', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P1' };\n}\n\n// Criterion 8: Original Data\nfunction checkOriginalData(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'original_data', criterion_label: 'Original Data & Expert Content', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Analyze homepage + all blog sample pages for original data signals\n const allPages = [data.homepage, ...(data.blogSample || [])].filter(Boolean);\n const html = data.homepage.text;\n const allText = allPages.map(p => p!.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n const text = data.homepage.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Stats check: strong (+3) if research context nearby, weak (+1) for generic marketing stats\n const statPatterns = /\\d+%|\\d+\\s*(patients|clients|customers|cases|years|professionals|specialists|companies|users|businesses|domains|audits)/i;\n if (statPatterns.test(allText)) {\n const researchContext = /\\b(our\\s+(?:study|analysis|research|data|survey|findings|report)|we\\s+(?:surveyed|analyzed|studied|measured|tracked)|proprietary|methodology|original\\s+research)\\b/i;\n if (researchContext.test(allText)) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Proprietary statistics with research context found' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Statistics found but without research context (e.g., \"500+ clients\")', fix: 'Add context about your methodology: \"Our analysis of X found...\" or \"We surveyed Y...\"' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No proprietary data or statistics found', fix: 'Add unique statistics, case study results, or industry data that LLMs would cite as authoritative' });\n }\n\n // Case studies: with nearby metric (+3), without metric (+1)\n const caseStudyPattern = /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/i;\n if (caseStudyPattern.test(text)) {\n // Check for a numeric metric within ~200 chars of the case study mention\n const caseStudyRegex = /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/gi;\n let hasMetricNearby = false;\n let csMatch: RegExpExecArray | null;\n while ((csMatch = caseStudyRegex.exec(text)) !== null) {\n const start = Math.max(0, csMatch.index - 200);\n const end = Math.min(text.length, csMatch.index + csMatch[0].length + 200);\n const surrounding = text.slice(start, end);\n if (/\\d+%|\\$[\\d,]+|\\d+x\\b/i.test(surrounding)) {\n hasMetricNearby = true;\n break;\n }\n }\n if (hasMetricNearby) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Case studies or testimonials with specific metrics found' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Case studies or testimonials mentioned but without specific metrics', fix: 'Add measurable outcomes to case studies (e.g., \"increased traffic by 45%\")' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'No case studies or testimonials found', fix: 'Add case studies with specific outcomes and metrics' });\n }\n\n // Expert attribution (+2) - check homepage + blog\n const combinedText = data.blogSample && data.blogSample.length > 0\n ? text + ' ' + data.blogSample.map(p => p.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ')\n : text;\n if (/written\\s+by|authored\\s+by|expert|specialist|board.certified|licensed/i.test(combinedText)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Expert attribution or credentials found' });\n } else {\n findings.push({ severity: 'low', detail: 'No expert attribution or credentials visible', fix: 'Add author bios with credentials to establish E-E-A-T signals' });\n }\n\n // Blog check: require actual href links to content paths, not just the word \"blog\"\n const contentLinkPattern = /href=\"[^\"]*\\/(?:blog|articles|insights|guides|resources)\\b[^\"]*\"/i;\n if (contentLinkPattern.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Links to blog/articles section found on site' });\n } else {\n findings.push({ severity: 'medium', detail: 'No links to blog or articles section found', fix: 'Create a content section with expert articles and link to it from your homepage' });\n }\n\n // Blog sample enhancement: case studies from blog posts\n if (data.blogSample && data.blogSample.length > 0 && !caseStudyPattern.test(text)) {\n const blogText = data.blogSample.map(p => p.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n if (caseStudyPattern.test(blogText)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Case studies or testimonials found on blog posts' });\n }\n }\n\n return { criterion: 'original_data', criterion_label: 'Original Data & Expert Content', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P2' };\n}\n\n// Criterion 9: Internal Linking\nfunction checkInternalLinking(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'internal_linking', criterion_label: 'Internal Linking Architecture', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n const linkMatches = html.match(/<a[^>]*href=\"([^\"]*)\"[^>]*>/gi) || [];\n const internalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('/') || href.includes(data.domain);\n });\n\n if (internalLinks.length >= 20) {\n score += 3;\n findings.push({ severity: 'info', detail: `${internalLinks.length} internal links found on homepage` });\n } else if (internalLinks.length >= 10) {\n score += 2;\n findings.push({ severity: 'low', detail: `${internalLinks.length} internal links on homepage`, fix: 'Add more internal links to key service/content pages' });\n } else {\n findings.push({ severity: 'high', detail: `Only ${internalLinks.length} internal links on homepage`, fix: 'Add prominent internal links to service pages, FAQ, blog, and about pages' });\n }\n\n if (/breadcrumb|aria-label=\"breadcrumb\"/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Breadcrumb navigation detected' });\n } else {\n findings.push({ severity: 'medium', detail: 'No breadcrumb navigation found', fix: 'Add breadcrumb navigation with BreadcrumbList schema markup' });\n }\n\n if (/<nav[\\s>]/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Semantic <nav> element used for navigation' });\n } else {\n findings.push({ severity: 'low', detail: 'No semantic <nav> element found', fix: 'Wrap navigation menus in <nav> for better AI and accessibility parsing' });\n }\n\n if (/related|see\\s+also|learn\\s+more|explore|you\\s+may\\s+also/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Related content or cross-linking sections found' });\n } else {\n findings.push({ severity: 'low', detail: 'No related content or cross-linking found', fix: 'Add \"Related Services\" or \"Learn More\" sections to build topic clusters' });\n }\n\n if (/<footer[\\s>]/i.test(html)) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Footer element with likely navigation links' });\n } else {\n findings.push({ severity: 'low', detail: 'No <footer> element found', fix: 'Add a <footer> with navigation links, contact info, and site structure' });\n }\n\n return { criterion: 'internal_linking', criterion_label: 'Internal Linking Architecture', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P2' : 'P1' };\n}\n\n// Criterion 10: Semantic HTML\nfunction checkSemanticHTML(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'semantic_html', criterion_label: 'Semantic HTML5 & Accessibility', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for semantic element detection\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n let score = 0;\n\n const checks: [string, RegExp, string][] = [\n ['<main>', /<main[\\s>]/i, 'Wrap primary page content in <main>'],\n ['<article>', /<article[\\s>]/i, 'Use <article> for standalone content blocks'],\n ['<time>', /<time[\\s>]/i, 'Use <time> elements for dates'],\n ['<nav>', /<nav[\\s>]/i, 'Use <nav> for navigation sections'],\n ['<header>', /<header[\\s>]/i, 'Use <header> for page/section headers'],\n ['<footer>', /<footer[\\s>]/i, 'Use <footer> for page/section footers'],\n ];\n\n let found = 0;\n for (const [name, regex, fix] of checks) {\n if (regex.test(combinedHtml)) {\n found++;\n } else {\n findings.push({ severity: 'low', detail: `Missing ${name} element`, fix });\n }\n }\n score += Math.min(4, Math.floor(found * 0.7));\n if (found >= 4) findings.push({ severity: 'info', detail: `${found}/6 key semantic HTML5 elements found` });\n\n // Check img alt text\n const images = html.match(/<img[^>]*>/gi) || [];\n const imagesWithAlt = images.filter(img => /alt=\"[^\"]+\"/i.test(img));\n if (images.length > 0) {\n const ratio = imagesWithAlt.length / images.length;\n if (ratio >= 0.8) {\n score += 2;\n findings.push({ severity: 'info', detail: `${Math.round(ratio * 100)}% of images have alt text` });\n } else {\n findings.push({ severity: 'medium', detail: `Only ${Math.round(ratio * 100)}% of images have alt text`, fix: 'Add descriptive alt text to all images' });\n }\n }\n\n // Check lang attribute\n if (/lang=\"[a-z]{2}\"/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'HTML lang attribute set' });\n } else {\n findings.push({ severity: 'medium', detail: 'Missing lang attribute on <html> tag', fix: 'Add lang=\"en\" (or appropriate language) to the <html> tag' });\n }\n\n // Check for ARIA roles\n if (/role=\"|aria-/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'ARIA attributes found for accessibility' });\n } else {\n findings.push({ severity: 'low', detail: 'No ARIA roles or attributes found', fix: 'Add ARIA roles and labels to improve accessibility and semantic parsing' });\n }\n\n return { criterion: 'semantic_html', criterion_label: 'Semantic HTML5 & Accessibility', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 11: Content Freshness\nfunction checkContentFreshness(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'content_freshness', criterion_label: 'Content Freshness Signals', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // JSON-LD date signals (datePublished, dateModified)\n const hasDatePublished = /datePublished|dateCreated/i.test(html);\n const hasDateModified = /dateModified/i.test(html);\n if (hasDatePublished || hasDateModified) {\n score += 3;\n findings.push({ severity: 'info', detail: `JSON-LD date properties found: ${[hasDatePublished && 'datePublished', hasDateModified && 'dateModified'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'high', detail: 'No JSON-LD date properties (datePublished/dateModified) found', fix: 'Add datePublished and dateModified to Article or WebPage schema' });\n }\n\n // <time> elements\n const timeElements = html.match(/<time[\\s>]/gi) || [];\n if (timeElements.length >= 2) {\n score += 3;\n findings.push({ severity: 'info', detail: `${timeElements.length} <time> elements found` });\n } else if (timeElements.length === 1) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Only 1 <time> element found', fix: 'Use <time datetime=\"...\"> for all dates to help AI parsers' });\n } else {\n findings.push({ severity: 'medium', detail: 'No <time> elements found', fix: 'Wrap publication and modification dates in <time datetime=\"...\"> elements' });\n }\n\n // Article meta (article:published_time, article:modified_time)\n const hasArticleMeta = /article:published_time|article:modified_time/i.test(html);\n if (hasArticleMeta) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Open Graph article date meta tags found' });\n } else {\n findings.push({ severity: 'low', detail: 'No article:published_time or article:modified_time meta tags', fix: 'Add Open Graph article date meta tags' });\n }\n\n // Recency check - look for recent year references\n const currentYear = new Date().getFullYear();\n const hasRecentYear = html.includes(String(currentYear)) || html.includes(String(currentYear - 1));\n if (hasRecentYear) {\n score += 2;\n findings.push({ severity: 'info', detail: `References to ${currentYear} or ${currentYear - 1} found, suggesting recent content` });\n } else {\n findings.push({ severity: 'low', detail: 'No references to recent years found on homepage' });\n }\n\n return { criterion: 'content_freshness', criterion_label: 'Content Freshness Signals', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 12: Sitemap Completeness\nfunction checkSitemapCompleteness(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const sitemap = data.sitemapXml;\n\n if (!sitemap || sitemap.status !== 200) {\n findings.push({ severity: 'critical', detail: 'No sitemap.xml found', fix: 'Create a sitemap.xml with all indexable pages and submit to search engines' });\n return { criterion: 'sitemap_completeness', criterion_label: 'Sitemap Completeness', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const text = sitemap.text;\n let score = 2; // exists\n\n // Valid XML structure\n if (text.includes('<urlset') || text.includes('<sitemapindex')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Valid sitemap XML structure detected' });\n } else {\n findings.push({ severity: 'high', detail: 'sitemap.xml does not contain valid XML structure', fix: 'Ensure sitemap uses proper <urlset> or <sitemapindex> XML format' });\n }\n\n // Count URLs\n const urlCount = (text.match(/<loc>/gi) || []).length;\n if (urlCount >= 50) {\n score += 3;\n findings.push({ severity: 'info', detail: `${urlCount} URLs in sitemap` });\n } else if (urlCount >= 10) {\n score += 2;\n findings.push({ severity: 'info', detail: `${urlCount} URLs in sitemap` });\n } else if (urlCount > 0) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${urlCount} URL(s) in sitemap`, fix: 'Add all important pages to your sitemap' });\n }\n\n // Recent lastmod dates\n const lastmodMatches = text.match(/<lastmod>([^<]+)<\\/lastmod>/gi) || [];\n if (lastmodMatches.length > 0) {\n score += 2;\n findings.push({ severity: 'info', detail: `${lastmodMatches.length} URLs have lastmod dates` });\n } else {\n findings.push({ severity: 'medium', detail: 'No lastmod dates in sitemap', fix: 'Add <lastmod> dates to sitemap entries for freshness signals' });\n }\n\n // Sitemap index\n if (text.includes('<sitemapindex')) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Sitemap index found, indicating organized sitemap structure' });\n } else {\n findings.push({ severity: 'low', detail: 'No sitemap index structure', fix: 'Use a <sitemapindex> with multiple child sitemaps for larger sites to improve crawl efficiency' });\n }\n\n return { criterion: 'sitemap_completeness', criterion_label: 'Sitemap Completeness', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 13: RSS Feed\nfunction checkRssFeed(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n // Check for RSS link tag in homepage\n const hasRssLink = data.homepage && /<link[^>]*type=\"application\\/(?:rss|atom)\\+xml\"/i.test(data.homepage.text);\n if (hasRssLink) {\n score += 3;\n findings.push({ severity: 'info', detail: 'RSS/Atom feed link tag found in homepage <head>' });\n } else {\n findings.push({ severity: 'high', detail: 'No RSS/Atom feed link tag in homepage', fix: 'Add <link rel=\"alternate\" type=\"application/rss+xml\" href=\"/feed\"> to your <head>' });\n }\n\n // Check for valid feed content\n const feed = data.rssFeed;\n if (feed && feed.status === 200) {\n const feedText = feed.text;\n const isValidFeed = feedText.includes('<rss') || feedText.includes('<feed') || feedText.includes('<channel');\n if (isValidFeed) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Valid RSS/Atom feed content detected' });\n\n // Count items\n const itemCount = (feedText.match(/<item[\\s>]|<entry[\\s>]/gi) || []).length;\n if (itemCount >= 5) {\n score += 4;\n findings.push({ severity: 'info', detail: `Feed contains ${itemCount} items` });\n } else if (itemCount > 0) {\n score += 2;\n findings.push({ severity: 'low', detail: `Feed contains only ${itemCount} item(s)`, fix: 'Publish more content to populate your RSS feed with at least 5 items' });\n }\n } else {\n findings.push({ severity: 'medium', detail: 'Feed URL returned content but not valid RSS/Atom XML', fix: 'Ensure your feed outputs valid RSS 2.0 or Atom XML' });\n }\n } else if (!hasRssLink) {\n findings.push({ severity: 'medium', detail: 'No accessible RSS/Atom feed found', fix: 'Create an RSS feed to help AI engines discover and index new content automatically' });\n }\n\n return { criterion: 'rss_feed', criterion_label: 'RSS/Atom Feed', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 14: Table & List Extractability\nfunction checkTableListExtractability(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'table_list_extractability', criterion_label: 'Table & List Extractability', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML so blog tables/lists count\n const html = getCombinedHtml(data);\n let score = 0;\n\n // Tables with headers\n const tables = html.match(/<table[\\s>]/gi) || [];\n const tablesWithHeaders = (html.match(/<table[\\s\\S]*?<\\/table>/gi) || []).filter(t => /<th[\\s>]/i.test(t));\n if (tablesWithHeaders.length >= 1) {\n score += 3;\n findings.push({ severity: 'info', detail: `${tablesWithHeaders.length} table(s) with <th> headers found` });\n } else if (tables.length > 0) {\n score += 1;\n findings.push({ severity: 'medium', detail: `${tables.length} table(s) found but without <th> header cells`, fix: 'Add <th> header cells to tables for better AI extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'No HTML tables found', fix: 'Use comparison tables with <th> headers for structured data AI engines can extract' });\n }\n if (tablesWithHeaders.length >= 2) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Multiple well-structured tables present' });\n } else if (tablesWithHeaders.length === 1) {\n findings.push({ severity: 'low', detail: 'Only 1 table with headers found', fix: 'Add more comparison or data tables with <th> headers to increase extractable structured content' });\n }\n\n // Ordered lists\n const olCount = (html.match(/<ol[\\s>]/gi) || []).length;\n if (olCount >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${olCount} ordered list(s) found - good for step-by-step content` });\n } else {\n findings.push({ severity: 'low', detail: 'No ordered lists (<ol>) found', fix: 'Use <ol> for sequential content (steps, rankings, processes)' });\n }\n\n // Unordered lists\n const ulCount = (html.match(/<ul[\\s>]/gi) || []).length;\n if (ulCount >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${ulCount} unordered list(s) found` });\n } else {\n findings.push({ severity: 'low', detail: 'No unordered lists (<ul>) found', fix: 'Use <ul> for feature lists, benefits, and bullet-point content' });\n }\n\n // List items count\n const liCount = (html.match(/<li[\\s>]/gi) || []).length;\n if (liCount >= 10) {\n score += 1;\n findings.push({ severity: 'info', detail: `${liCount} list items - substantial extractable content` });\n }\n\n // Definition lists\n const dlCount = (html.match(/<dl[\\s>]/gi) || []).length;\n if (dlCount >= 1) {\n score += 1;\n findings.push({ severity: 'info', detail: `${dlCount} definition list(s) found` });\n } else {\n findings.push({ severity: 'low', detail: 'No definition lists (<dl>) found', fix: 'Use <dl>/<dt>/<dd> for term-definition pairs to improve AI extractability' });\n }\n\n // Bonus: meaningful comparison tables (3+ columns AND 3+ rows)\n for (const table of tablesWithHeaders) {\n const headerCells = (table.match(/<th[\\s>]/gi) || []).length;\n const dataCells = (table.match(/<td[\\s>]/gi) || []).length;\n if (headerCells >= 3 && dataCells >= headerCells * 2) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Meaningful comparison table found (3+ columns × 3+ rows)' });\n break;\n }\n }\n\n return { criterion: 'table_list_extractability', criterion_label: 'Table & List Extractability', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 15: Definition Patterns\nfunction checkDefinitionPatterns(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'definition_patterns', criterion_label: 'Definition Patterns', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for definition pattern detection\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const html = combinedHtml;\n let score = 0;\n\n // \"X is\" / \"X are\" / \"X refers to\" / \"defined as\" patterns\n const definitionPatterns = [\n /\\b\\w[\\w\\s]{2,30}\\bis\\s+(?:a|an|the)\\s/gi,\n /\\b\\w[\\w\\s]{2,30}\\bare\\s+(?:a|an|the)\\s/gi,\n /\\brefers?\\s+to\\b/gi,\n /\\bdefined\\s+as\\b/gi,\n /\\bknown\\s+as\\b/gi,\n /\\bmeans?\\s+that\\b/gi,\n ];\n\n let patternCount = 0;\n for (const pattern of definitionPatterns) {\n const matches = text.match(pattern) || [];\n patternCount += matches.length;\n }\n\n if (patternCount >= 3) {\n score += 5;\n findings.push({ severity: 'info', detail: `${patternCount} definition-style patterns found (e.g., \"X is a...\", \"refers to\", \"defined as\")` });\n } else if (patternCount >= 1) {\n score += 3;\n findings.push({ severity: 'low', detail: `Only ${patternCount} definition pattern(s) found`, fix: 'Start key descriptions with clear definition patterns like \"X is a...\" or \"X refers to...\"' });\n } else {\n findings.push({ severity: 'medium', detail: 'No definition patterns found', fix: 'Add clear definitions using patterns like \"[Term] is [definition]\" that AI engines can extract as snippets' });\n }\n\n // Early placement (in first 2000 chars of text content)\n const earlyText = text.slice(0, 2000);\n const earlyDefinitions = definitionPatterns.some(p => p.test(earlyText));\n // Reset lastIndex since we used global flags\n definitionPatterns.forEach(p => { p.lastIndex = 0; });\n if (earlyDefinitions) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Definition patterns appear early in page content - good for snippet extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'No definition patterns in the first 2000 characters of content', fix: 'Place key definitions early on the page where AI engines prioritize extraction' });\n }\n\n // <dfn> or <abbr> usage\n const hasDfn = /<dfn[\\s>]/i.test(html);\n const hasAbbr = /<abbr[\\s>]/i.test(html);\n if (hasDfn || hasAbbr) {\n score += 1;\n findings.push({ severity: 'info', detail: `Semantic definition elements found: ${[hasDfn && '<dfn>', hasAbbr && '<abbr>'].filter(Boolean).join(', ')}` });\n } else {\n findings.push({ severity: 'low', detail: 'No <dfn> or <abbr> elements found', fix: 'Use <dfn> for term definitions and <abbr> for abbreviations to help AI parse terminology' });\n }\n\n // Glossary or definition-list patterns\n if (/<dl[\\s>]/i.test(html) || /glossary|definitions|terminology/i.test(html)) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Glossary or definition list structure detected' });\n } else {\n findings.push({ severity: 'low', detail: 'No glossary or definition list found', fix: 'Add a glossary section using <dl>/<dt>/<dd> for key industry terms' });\n }\n\n return { criterion: 'definition_patterns', criterion_label: 'Definition Patterns', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 16: Direct Answer Density\nfunction checkDirectAnswerDensity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'direct_answer_density', criterion_label: 'Direct Answer Paragraphs', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n // Use combined HTML for Q&A pair and paragraph detection\n const html = getCombinedHtml(data);\n let score = 0;\n\n // Q&A pairs: question heading followed by paragraph\n const qaPairs = html.match(/<h[2-4][^>]*>[^<]*\\?<\\/h[2-4]>\\s*<p[^>]*>/gi) || [];\n if (qaPairs.length >= 3) {\n score += 6;\n findings.push({ severity: 'info', detail: `${qaPairs.length} question-answer pairs found (question heading + direct answer paragraph)` });\n } else if (qaPairs.length >= 1) {\n score += 3;\n findings.push({ severity: 'low', detail: `${qaPairs.length} question-answer pair(s) found`, fix: 'Add more question headings (H2/H3) immediately followed by concise answer paragraphs' });\n } else {\n findings.push({ severity: 'high', detail: 'No direct question-answer pairs found', fix: 'Structure content with question headings (e.g., \"What is X?\") immediately followed by a concise answer paragraph' });\n }\n\n // Snippet-zone paragraphs (40-150 words - ideal for AI extraction)\n const paragraphs = html.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n const snippetZoneParagraphs = paragraphs.filter(p => {\n const text = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = text.split(/\\s+/).length;\n return wordCount >= 40 && wordCount <= 150;\n });\n if (snippetZoneParagraphs.length >= 3) {\n score += 2;\n findings.push({ severity: 'info', detail: `${snippetZoneParagraphs.length} paragraphs in snippet zone (40-150 words) - ideal for AI extraction` });\n } else if (snippetZoneParagraphs.length >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${snippetZoneParagraphs.length} paragraph(s) in optimal snippet length`, fix: 'Write more paragraphs in the 40-150 word range for AI snippet extraction' });\n } else {\n findings.push({ severity: 'medium', detail: 'No paragraphs in the optimal snippet zone (40-150 words)', fix: 'Write self-contained paragraphs of 40-150 words that directly answer common questions' });\n }\n\n // Direct answer openers\n const text = data.homepage.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const directOpeners = /\\b(yes|no|the answer is|in short|simply put|to summarize)\\b/gi;\n const openerCount = (text.match(directOpeners) || []).length;\n if (openerCount >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Direct answer openers found (e.g., \"Yes,\", \"In short,\")' });\n } else {\n findings.push({ severity: 'low', detail: 'Few or no direct answer openers found', fix: 'Start answers with direct phrases like \"Yes,\", \"No,\", \"In short,\" to signal definitive answers to AI engines' });\n }\n\n return { criterion: 'direct_answer_density', criterion_label: 'Direct Answer Paragraphs', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P2' : 'P1' };\n}\n\n// Criterion 17: Content Licensing\nfunction checkContentLicensing(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n let score = 0;\n\n // ai.txt exists (ignore HTML catch-all responses)\n const aiTxt = data.aiTxt;\n if (aiTxt && aiTxt.status === 200 && aiTxt.text.length > 20 && !isHtmlResponse(aiTxt)) {\n score += 4;\n findings.push({ severity: 'info', detail: `ai.txt file found (${aiTxt.text.length} characters)` });\n } else {\n findings.push({ severity: 'high', detail: 'No ai.txt file found', fix: 'Create /ai.txt to declare your AI usage policy and content permissions for AI crawlers' });\n }\n\n const html = data.homepage?.text || '';\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n\n // Policy language on page\n const hasPolicyLanguage = /content\\s+policy|terms\\s+of\\s+use|usage\\s+rights|permission|copyright\\s+policy|licensing|creative\\s+commons/i.test(text);\n if (hasPolicyLanguage) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Content policy or licensing language found on page' });\n } else {\n findings.push({ severity: 'low', detail: 'No content policy or licensing language visible', fix: 'Add clear content usage terms or licensing information' });\n }\n\n // Schema license property\n const hasLicenseSchema = /license|copyrightHolder|copyrightYear/i.test(html) && /application\\/ld\\+json/i.test(html);\n if (hasLicenseSchema) {\n score += 2;\n findings.push({ severity: 'info', detail: 'License or copyright properties found in schema markup' });\n } else {\n findings.push({ severity: 'low', detail: 'No license or copyright properties in schema', fix: 'Add license, copyrightHolder, and copyrightYear to your schema markup' });\n }\n\n // TDM (Text and Data Mining) or Creative Commons\n const hasTdmOrCC = /tdm|text\\s+and\\s+data\\s+mining|creative\\s+commons|CC\\s+BY|creativecommons\\.org/i.test(html + (aiTxt?.text || ''));\n if (hasTdmOrCC) {\n score += 2;\n findings.push({ severity: 'info', detail: 'TDM or Creative Commons licensing references found' });\n } else {\n findings.push({ severity: 'low', detail: 'No TDM or Creative Commons licensing references found', fix: 'Add Text and Data Mining (TDM) permissions or Creative Commons licensing to signal AI-friendly content use' });\n }\n\n return { criterion: 'content_licensing', criterion_label: 'Content Licensing & AI Permissions', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 4 ? 'P3' : 'P2' };\n}\n\n// Criterion 18: Author Schema Depth\nfunction checkAuthorSchemaDepth(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'author_schema_depth', criterion_label: 'Author & Expert Schema', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for author detection\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Person schema (check combined - blog posts often have author schema)\n const hasPersonSchema = /\"@type\"\\s*:\\s*\"Person\"/i.test(combinedHtml);\n if (hasPersonSchema) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Person schema found in JSON-LD' });\n } else {\n findings.push({ severity: 'medium', detail: 'No Person schema found', fix: 'Add Person schema for authors with name, jobTitle, knowsAbout, and sameAs properties' });\n }\n\n // jobTitle or knowsAbout properties\n const hasJobTitle = /jobTitle|knowsAbout|expertise|hasCredential/i.test(combinedHtml);\n if (hasJobTitle) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Author credential properties found (jobTitle/knowsAbout)' });\n } else {\n findings.push({ severity: 'low', detail: 'No jobTitle or knowsAbout in author schema', fix: 'Add jobTitle and knowsAbout to Person schema to establish expertise' });\n }\n\n // sameAs links (social profiles)\n const hasSameAs = /sameAs/i.test(combinedHtml) && hasPersonSchema;\n if (hasSameAs) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Author sameAs social profile links found' });\n } else {\n findings.push({ severity: 'low', detail: 'No sameAs links to author social profiles', fix: 'Add sameAs URLs (LinkedIn, GitHub) to Person schema to strengthen entity connections' });\n }\n\n // Visible byline\n const hasByline = /written\\s+by|authored?\\s+by|by\\s+[A-Z][a-z]+\\s+[A-Z]/i.test(text) ||\n /class=\"[^\"]*author[^\"]*\"/i.test(html) ||\n /rel=\"author\"/i.test(html);\n if (hasByline) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Visible author byline or attribution found' });\n } else {\n findings.push({ severity: 'medium', detail: 'No visible author byline found', fix: 'Add visible author names with credentials to establish E-E-A-T' });\n }\n\n // <address> element for contact\n if (/<address[\\s>]/i.test(combinedHtml)) {\n score += 1;\n findings.push({ severity: 'info', detail: '<address> element found for contact information' });\n } else {\n findings.push({ severity: 'low', detail: 'No <address> element found for contact information', fix: 'Add an <address> element with contact details to reinforce entity identity' });\n }\n\n return { criterion: 'author_schema_depth', criterion_label: 'Author & Expert Schema', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 19: Fact Density\nfunction checkFactDensity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'fact_density', criterion_label: 'Fact & Data Density', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Analyze across homepage + all blog sample pages for fact density\n const allPages = [data.homepage, ...(data.blogSample || [])].filter(Boolean);\n const allText = allPages.map(p => p!.text.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ')).join(' ');\n const text = allText;\n const pageCount = allPages.length;\n let score = 0;\n\n // Numeric data points (percentages, money, counts)\n const dataPoints = text.match(/\\d+(?:\\.\\d+)?(?:\\s*%|\\s*\\$|\\s*USD|\\s*EUR)/g) || [];\n const countPhrases = text.match(/\\d+(?:,\\d{3})*\\+?\\s+(?:users?|clients?|customers?|companies|businesses|patients?|members?|employees?|projects?|downloads?)/gi) || [];\n const totalDataPoints = dataPoints.length + countPhrases.length;\n const avgPerPage = pageCount > 0 ? totalDataPoints / pageCount : 0;\n\n if (avgPerPage >= 4) {\n score += 5;\n findings.push({ severity: 'info', detail: `${totalDataPoints} quantitative data points found across ${pageCount} pages (avg ${avgPerPage.toFixed(1)}/page)` });\n } else if (avgPerPage >= 2) {\n score += 3;\n findings.push({ severity: 'info', detail: `${totalDataPoints} quantitative data points found across ${pageCount} pages` });\n } else if (totalDataPoints >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${totalDataPoints} quantitative data point(s) found across ${pageCount} pages`, fix: 'Add more specific numbers, percentages, and metrics to strengthen credibility' });\n } else {\n findings.push({ severity: 'high', detail: 'No quantitative data points found', fix: 'Add specific statistics (percentages, counts, comparisons) that AI engines can cite' });\n }\n\n // Year references (suggest sourced/dated claims)\n const yearRefs = text.match(/(?:19|20)\\d{2}/g) || [];\n const uniqueYears = [...new Set(yearRefs)];\n if (uniqueYears.length >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueYears.length} different year references found - suggests dated, verifiable claims` });\n } else if (uniqueYears.length === 1) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Only 1 year reference found on page', fix: 'Add more dated references and timestamps to demonstrate current, verifiable information' });\n } else {\n findings.push({ severity: 'low', detail: 'No year references found on page', fix: 'Include specific years and dates to provide verifiable, time-anchored facts' });\n }\n\n // Attribution phrases (source citations)\n const attributions = text.match(/according\\s+to|source:|study\\s+(?:by|from)|research\\s+(?:by|from|shows)|data\\s+from|report\\s+(?:by|from)|published\\s+(?:by|in)/gi) || [];\n if (attributions.length >= 1) {\n score += 2;\n findings.push({ severity: 'info', detail: `${attributions.length} source attribution(s) found (e.g., \"according to\", \"study by\")` });\n } else {\n findings.push({ severity: 'low', detail: 'No source attributions found', fix: 'Add citations like \"According to [source]\" or \"Research from [org] shows\" for credibility' });\n }\n\n // Units of measurement\n const units = text.match(/\\d+\\s*(?:hours?|minutes?|days?|weeks?|months?|years?|miles?|km|lbs?|kg|mg|sq\\s*ft|acres?|gallons?|liters?)/gi) || [];\n if (units.length >= 2) {\n score += 1;\n findings.push({ severity: 'info', detail: `${units.length} measurement units found (hours, miles, etc.) - adds factual precision` });\n } else {\n findings.push({ severity: 'low', detail: 'Few or no units of measurement found', fix: 'Include specific measurements (hours, miles, sq ft, etc.) to add factual precision AI engines can extract' });\n }\n\n return { criterion: 'fact_density', criterion_label: 'Fact & Data Density', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 20: Canonical URL\nfunction checkCanonicalUrl(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'canonical_url', criterion_label: 'Canonical URL Strategy', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const html = data.homepage.text;\n let score = 0;\n\n // Canonical link present (handle either attribute order: rel before href OR href before rel)\n const canonicalMatch = html.match(/<link[^>]*rel=\"canonical\"[^>]*href=\"([^\"]*)\"[^>]*>/i)\n || html.match(/<link[^>]*href=\"([^\"]*)\"[^>]*rel=\"canonical\"[^>]*>/i);\n if (canonicalMatch) {\n score += 4;\n findings.push({ severity: 'info', detail: `Canonical URL found: ${canonicalMatch[1].slice(0, 80)}` });\n\n // Self-referencing (canonical points to the same domain)\n const canonicalUrl = canonicalMatch[1];\n if (canonicalUrl.includes(data.domain)) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Canonical URL is self-referencing (points to same domain)' });\n } else {\n findings.push({ severity: 'medium', detail: 'Canonical URL points to a different domain', fix: 'Ensure canonical URL points to the authoritative version of this page' });\n }\n\n // HTTPS canonical\n if (canonicalUrl.startsWith('https://')) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Canonical URL uses HTTPS' });\n } else {\n findings.push({ severity: 'medium', detail: 'Canonical URL does not use HTTPS', fix: 'Update canonical URL to use https://' });\n }\n } else {\n findings.push({ severity: 'high', detail: 'No canonical URL tag found', fix: 'Add <link rel=\"canonical\" href=\"https://yoursite.com/page\"> to prevent duplicate content issues' });\n }\n\n // Check for duplicate canonical tags (match either attribute order)\n const allCanonicals = html.match(/<link[^>]*(?:rel=\"canonical\"|rel='canonical')[^>]*>/gi) || [];\n if (allCanonicals.length > 1) {\n score -= 1;\n findings.push({ severity: 'high', detail: `${allCanonicals.length} canonical tags found - must have exactly one`, fix: 'Remove duplicate canonical tags, keeping only one per page' });\n } else if (allCanonicals.length === 1) {\n score += 1;\n findings.push({ severity: 'info', detail: 'Single canonical tag present (no duplicates)' });\n }\n\n return { criterion: 'canonical_url', criterion_label: 'Canonical URL Strategy', score: Math.max(0, Math.min(10, score)), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Helper: analyze sitemap lastmod dates for content velocity ──────────────\n\nexport interface SitemapDateAnalysis {\n recentCount: number;\n isUniform: boolean;\n uniformDetail?: string;\n totalWithDates: number;\n distinctRecentDays: number;\n}\n\nexport function countRecentSitemapDates(sitemapText: string): SitemapDateAnalysis {\n const lastmodMatches = sitemapText.match(/<lastmod>([^<]+)<\\/lastmod>/gi) || [];\n if (lastmodMatches.length === 0) {\n return { recentCount: 0, isUniform: false, totalWithDates: 0, distinctRecentDays: 0 };\n }\n\n const now = new Date();\n const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);\n\n // Parse all dates and group by day\n const dayCounts: Record<string, number> = {};\n let recentCount = 0;\n const recentDays = new Set<string>();\n\n for (const match of lastmodMatches) {\n const dateStr = match.replace(/<\\/?lastmod>/gi, '').trim();\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) continue;\n\n const dayKey = date.toISOString().split('T')[0];\n dayCounts[dayKey] = (dayCounts[dayKey] || 0) + 1;\n\n if (date >= ninetyDaysAgo) {\n recentCount++;\n recentDays.add(dayKey);\n }\n }\n\n // Detect uniform pattern: if most common day has >80% of all dated URLs\n const totalWithDates = Object.values(dayCounts).reduce((a, b) => a + b, 0);\n const maxDayCount = Math.max(...Object.values(dayCounts));\n const isUniform = totalWithDates >= 5 && maxDayCount / totalWithDates > 0.8;\n\n let uniformDetail: string | undefined;\n if (isUniform) {\n const topDay = Object.entries(dayCounts).find(([, count]) => count === maxDayCount)![0];\n uniformDetail = `${maxDayCount} of ${totalWithDates} URLs share lastmod date ${topDay} - likely auto-generated by build system`;\n }\n\n return {\n recentCount,\n isUniform,\n uniformDetail,\n totalWithDates,\n distinctRecentDays: recentDays.size,\n };\n}\n\n// ─── Blog URL extraction from sitemap ────────────────────────────────────────\n\nconst BLOG_PATH_PATTERNS = /\\/(?:[^/]*-?)?(?:blog|articles?|insights?|guides?|resources?|news|posts?|learn|help|how-?to|tutorials?|case-stud|whitepapers?)\\b/i;\n\nconst EXCLUDE_PATH_PATTERNS = /\\/(?:tag|category|author|page|feed|wp-content|wp-admin|wp-json|cart|checkout|login|search|api|static|assets|_next)\\b/i;\n\n/**\n * Extract blog/content URLs from a sitemap XML string.\n * Includes URLs matching common blog path patterns, plus deep paths (2+ segments).\n * Sorts by lastmod descending (newest first), returns top N.\n */\nexport function extractBlogUrlsFromSitemap(sitemapText: string, domain: string, limit: number = 50): string[] {\n const urlBlocks = sitemapText.match(/<url>([\\s\\S]*?)<\\/url>/gi) || [];\n const candidates: { url: string; lastmod: string }[] = [];\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n\n for (const block of urlBlocks) {\n const locMatch = block.match(/<loc>([^<]+)<\\/loc>/i);\n if (!locMatch) continue;\n const url = locMatch[1].trim();\n\n // Must be same domain\n try {\n const parsed = new URL(url);\n const urlDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (urlDomain !== cleanDomain) continue;\n\n // Skip root URL\n if (parsed.pathname === '/' || parsed.pathname === '') continue;\n\n const path = parsed.pathname.toLowerCase();\n\n // Exclude unwanted paths\n if (EXCLUDE_PATH_PATTERNS.test(path)) continue;\n\n // Include blog-like paths OR deep paths (2+ segments)\n const segments = path.split('/').filter(Boolean);\n const isBlogPath = BLOG_PATH_PATTERNS.test(path);\n const isDeepPath = segments.length >= 2;\n\n if (!isBlogPath && !isDeepPath) continue;\n } catch {\n continue;\n }\n\n const lastmodMatch = block.match(/<lastmod>([^<]+)<\\/lastmod>/i);\n const lastmod = lastmodMatch ? lastmodMatch[1].trim() : '';\n\n candidates.push({ url, lastmod });\n }\n\n // Sort by lastmod descending (newest first), URLs without lastmod go last\n candidates.sort((a, b) => {\n if (a.lastmod && b.lastmod) return b.lastmod.localeCompare(a.lastmod);\n if (a.lastmod) return -1;\n if (b.lastmod) return 1;\n return 0;\n });\n\n return candidates.slice(0, limit).map(c => c.url);\n}\n\n/**\n * Extract the best sub-sitemap URL from a sitemapindex.\n * WordPress often uses sitemapindex pointing to post-sitemap.xml, page-sitemap.xml, etc.\n * Returns null if not a sitemapindex.\n */\n/**\n * Extract ALL sub-sitemap URLs from a sitemapindex, up to a limit.\n * Used to fetch sub-sitemaps so downstream checks find <lastmod> and <url> entries.\n */\nexport function extractAllSubSitemapUrls(\n sitemapText: string,\n domainOrLimit?: string | number,\n maybeLimit: number = 5,\n): string[] {\n if (!sitemapText.includes('<sitemapindex')) return [];\n\n const domain = typeof domainOrLimit === 'string' ? domainOrLimit : undefined;\n const limit = typeof domainOrLimit === 'number' ? domainOrLimit : maybeLimit;\n\n const sitemapLocs = sitemapText.match(/<sitemap>[\\s\\S]*?<loc>([^<]+)<\\/loc>[\\s\\S]*?<\\/sitemap>/gi) || [];\n const urls = sitemapLocs.map(block => {\n const match = block.match(/<loc>([^<]+)<\\/loc>/i);\n return match ? match[1].trim() : '';\n }).filter(url => !!url && isSafePublicUrl(url, domain));\n\n // Prefer post/blog/article sub-sitemaps first\n const preferred = urls.filter(u => /post|blog|article|page/i.test(u));\n const rest = urls.filter(u => !preferred.includes(u));\n return [...preferred, ...rest].slice(0, limit);\n}\n\nexport function extractSubSitemapUrl(sitemapText: string, domain?: string): string | null {\n if (!sitemapText.includes('<sitemapindex')) return null;\n\n const sitemapLocs = sitemapText.match(/<sitemap>[\\s\\S]*?<loc>([^<]+)<\\/loc>[\\s\\S]*?<\\/sitemap>/gi) || [];\n if (sitemapLocs.length === 0) return null;\n\n const urls = sitemapLocs.map(block => {\n const match = block.match(/<loc>([^<]+)<\\/loc>/i);\n return match ? match[1].trim() : '';\n }).filter(url => !!url && isSafePublicUrl(url, domain));\n\n // Prefer post/blog/article sub-sitemap\n const preferred = urls.find(u => /post|blog|article/i.test(u));\n return preferred || urls[0] || null;\n}\n\n// Criterion 21: Content Velocity\nfunction checkContentVelocity(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n const sitemap = data.sitemapXml;\n let score = 0;\n\n if (!sitemap || sitemap.status !== 200) {\n findings.push({ severity: 'medium', detail: 'No sitemap available to assess content velocity', fix: 'Create a sitemap.xml with lastmod dates to signal content publishing frequency' });\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n const analysis = countRecentSitemapDates(sitemap.text);\n\n if (analysis.totalWithDates === 0) {\n findings.push({ severity: 'medium', detail: 'No lastmod dates in sitemap', fix: 'Add lastmod dates to sitemap entries to signal content freshness' });\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: 2, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n score += 2;\n findings.push({ severity: 'info', detail: `${analysis.totalWithDates} pages have lastmod dates` });\n\n // Use distinct recent days when lastmod dates are uniform (build-system artifact)\n const effectiveCount = analysis.isUniform ? analysis.distinctRecentDays : analysis.recentCount;\n\n if (analysis.isUniform) {\n findings.push({ severity: 'medium', detail: analysis.uniformDetail!, fix: 'Set genuine lastmod dates per page reflecting actual content changes, not build timestamps' });\n }\n\n if (effectiveCount >= 20) {\n score += 8;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days - excellent content velocity` });\n } else if (effectiveCount >= 10) {\n score += 5;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days - good velocity` });\n } else if (effectiveCount >= 5) {\n score += 3;\n findings.push({ severity: 'info', detail: `${effectiveCount} ${analysis.isUniform ? 'distinct dates' : 'pages updated'} in last 90 days` });\n } else if (effectiveCount >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${effectiveCount} ${analysis.isUniform ? 'distinct date(s)' : 'page(s) updated'} in last 90 days`, fix: 'Publish or update content more frequently to signal active maintenance' });\n } else {\n findings.push({ severity: 'medium', detail: 'No pages updated in the last 90 days', fix: 'Update existing content and publish new pages regularly' });\n }\n\n return { criterion: 'content_velocity', criterion_label: 'Content Publishing Velocity', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// Criterion 22: Schema Coverage (depth across types)\nfunction checkSchemaCoverage(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n // Use combined HTML for schema coverage (blog posts add Article, Person, FAQPage etc.)\n const combinedHtml = getCombinedHtml(data);\n const html = data.homepage.text;\n const ldJsonMatches = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD found - cannot assess schema coverage', fix: 'Add JSON-LD schema markup to improve AI engine understanding' });\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const allSchemaText = ldJsonMatches.join(' ');\n const allSchemaLower = allSchemaText.toLowerCase();\n\n // Count distinct schema properties\n const propertyMatches = allSchemaText.match(/\"[a-zA-Z@]+\"\\s*:/g) || [];\n const uniqueProperties = new Set(propertyMatches.map(p => p.replace(/[\":\\s]/g, '').toLowerCase()));\n\n if (uniqueProperties.size >= 15) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueProperties.size} unique schema properties used - rich schema depth` });\n } else if (uniqueProperties.size >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `${uniqueProperties.size} unique schema properties found` });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${uniqueProperties.size} schema properties`, fix: 'Add more properties to your schema types for richer AI understanding' });\n }\n\n // Organization depth (name, url, logo, contactPoint, sameAs, address, etc.)\n const orgProps = ['name', 'url', 'logo', 'contactpoint', 'sameas', 'address', 'telephone', 'description', 'founder', 'foundingdate'];\n const orgPropsFound = orgProps.filter(p => allSchemaLower.includes(`\"${p}\"`));\n if (orgPropsFound.length >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Organization schema has ${orgPropsFound.length}/10 key properties` });\n } else if (orgPropsFound.length >= 3) {\n score += 1;\n findings.push({ severity: 'low', detail: `Organization schema has only ${orgPropsFound.length}/10 key properties`, fix: 'Add more Organization properties: logo, contactPoint, sameAs, address' });\n } else {\n findings.push({ severity: 'medium', detail: `Organization schema has only ${orgPropsFound.length} key properties`, fix: 'Add essential Organization properties: name, url, logo, contactPoint, sameAs, address, telephone' });\n }\n\n // Article schema depth\n const articleProps = ['headline', 'datepublished', 'datemodified', 'author', 'image', 'description', 'publisher'];\n const articlePropsFound = articleProps.filter(p => allSchemaLower.includes(`\"${p}\"`));\n if (articlePropsFound.length >= 4) {\n score += 2;\n findings.push({ severity: 'info', detail: `Article schema has ${articlePropsFound.length}/7 key properties` });\n } else if (articlePropsFound.length >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Article schema has only ${articlePropsFound.length}/7 key properties`, fix: 'Add headline, datePublished, dateModified, author, image, and publisher to Article schema' });\n } else {\n findings.push({ severity: 'medium', detail: 'Article schema missing or has fewer than 2 key properties', fix: 'Add Article schema with headline, datePublished, author, and publisher properties' });\n }\n\n // @id linking (connected schema graph)\n const hasIdLinking = /\"@id\"\\s*:/i.test(allSchemaText);\n if (hasIdLinking) {\n score += 2;\n findings.push({ severity: 'info', detail: '@id linking found - schema types are connected in a graph' });\n } else {\n findings.push({ severity: 'low', detail: 'No @id linking between schema types', fix: 'Use @id references to connect schema types (e.g., article.publisher -> organization)' });\n }\n\n // Number of distinct types\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product', 'person', 'event', 'offer', 'review', 'aboutpage'];\n const foundTypes = schemaTypes.filter(t => allSchemaLower.includes(`\"${t}\"`));\n if (foundTypes.length >= 3) {\n score += 2;\n findings.push({ severity: 'info', detail: `${foundTypes.length} distinct schema types used: ${foundTypes.join(', ')}` });\n } else if (foundTypes.length >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Only ${foundTypes.length} distinct schema types used`, fix: 'Add more schema types (FAQPage, BreadcrumbList, Service) for comprehensive AI understanding' });\n } else {\n findings.push({ severity: 'medium', detail: `Only ${foundTypes.length} schema type(s) found - limited coverage`, fix: 'Add multiple schema types (Organization, WebSite, FAQPage, BreadcrumbList) for comprehensive AI understanding' });\n }\n\n return { criterion: 'schema_coverage', criterion_label: 'Schema Coverage & Depth', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// Criterion 23: Speakable Schema (voice assistant readiness)\nfunction checkSpeakableSchema(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const ldJsonMatches = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n let score = 0;\n\n if (ldJsonMatches.length === 0) {\n findings.push({ severity: 'critical', detail: 'No JSON-LD found - cannot assess speakable schema', fix: 'Add JSON-LD schema markup with SpeakableSpecification to indicate voice-readable content sections' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n const allSchemaText = ldJsonMatches.join(' ');\n const allSchemaLower = allSchemaText.toLowerCase();\n\n // Detect SpeakableSpecification type or \"speakable\" property\n const hasSpeakableType = /speakablespecification/i.test(allSchemaLower);\n const hasSpeakableProperty = /\"speakable\"\\s*:/i.test(allSchemaText);\n const hasSpeakable = hasSpeakableType || hasSpeakableProperty;\n\n if (!hasSpeakable) {\n findings.push({ severity: 'medium', detail: 'No SpeakableSpecification schema found - voice assistants cannot identify readable sections', fix: 'Add SpeakableSpecification schema with cssSelector or xpath targeting key content sections (headlines, summaries, FAQ answers)' });\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: 0, status: 'fail', findings, fix_priority: 'P2' };\n }\n\n // Base: speakable detected\n score += 4;\n findings.push({ severity: 'info', detail: 'SpeakableSpecification schema detected - voice assistants can identify readable content' });\n\n // Check for cssSelector or xpath targeting\n const hasCssSelector = /\"cssselector\"/i.test(allSchemaLower);\n const hasXpath = /\"xpath\"/i.test(allSchemaLower);\n if (hasCssSelector || hasXpath) {\n score += 3;\n const targetType = hasCssSelector && hasXpath ? 'cssSelector and xpath' : hasCssSelector ? 'cssSelector' : 'xpath';\n findings.push({ severity: 'info', detail: `Speakable uses ${targetType} targeting for precise content selection` });\n } else {\n findings.push({ severity: 'low', detail: 'Speakable schema lacks cssSelector or xpath targeting', fix: 'Add cssSelector (e.g., \".article-headline, .article-summary\") or xpath to precisely target speakable sections' });\n }\n\n // Check blog sample coverage\n if (data.blogSample && data.blogSample.length > 0) {\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const blogHasSpeakable = /speakablespecification/i.test(blogHtml) || /\"speakable\"\\s*:/i.test(blogHtml);\n if (blogHasSpeakable) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Speakable schema also found in blog/content pages - comprehensive voice coverage' });\n } else {\n findings.push({ severity: 'low', detail: 'Speakable schema only on homepage, not found in blog/content pages', fix: 'Add SpeakableSpecification to article pages to make blog content voice-assistant readable' });\n }\n } else {\n // No blog samples - can't check, give partial credit\n findings.push({ severity: 'info', detail: 'No blog pages sampled - blog speakable coverage not assessed' });\n }\n\n return { criterion: 'speakable_schema', criterion_label: 'Speakable Schema', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 24: Query-Answer Alignment ────────────────────────────────────\n\n/** Extract question-format headings from HTML */\nfunction extractQuestionHeadings(html: string): string[] {\n const hTags = (html.match(/<h[23][^>]*>([\\s\\S]*?)<\\/h[23]>/gi) || [])\n .map(h => h.replace(/<[^>]*>/g, '').trim());\n return hTags.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n}\n\nfunction checkQueryAnswerAlignment(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for query-answer alignment analysis' });\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const questionHeadings = extractQuestionHeadings(combinedHtml);\n\n if (questionHeadings.length === 0) {\n findings.push({ severity: 'info', detail: 'No question-format headings (H2/H3) found - scoring neutral', fix: 'Add question-based headings like \"What is...?\", \"How does...?\" to enable Q&A snippet extraction' });\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score: 5, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n // Split HTML on heading boundaries and check if answer follows each question heading\n let answered = 0;\n for (const qHeading of questionHeadings) {\n try {\n const escapedHeading = qHeading.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(escapedHeading + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>([\\\\s\\\\S]{0,1500}?)(?=<h[1-6]|$)', 'i');\n const match = pattern.exec(combinedHtml);\n if (match) {\n const afterContent = match[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n // Check if there's a meaningful answer (at least 20 chars of text content)\n if (afterContent.length >= 20) {\n answered++;\n }\n }\n } catch {\n // Skip headings that produce invalid regex (e.g. from residual script content)\n }\n }\n\n const rate = Math.round((answered / questionHeadings.length) * 100);\n let score: number;\n if (rate >= 80) {\n score = 10;\n findings.push({ severity: 'info', detail: `${answered}/${questionHeadings.length} question headings (${rate}%) followed by direct answers - excellent alignment` });\n } else if (rate >= 50) {\n score = 7;\n findings.push({ severity: 'low', detail: `${answered}/${questionHeadings.length} question headings (${rate}%) have answers`, fix: 'Add concise answer paragraphs after remaining unanswered question headings' });\n } else if (rate > 0) {\n score = 4;\n findings.push({ severity: 'medium', detail: `Only ${answered}/${questionHeadings.length} question headings (${rate}%) are followed by answers`, fix: 'Ensure each question heading is immediately followed by a direct answer paragraph' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${questionHeadings.length} question headings found but none have direct answers`, fix: 'Add answer paragraphs (2-3 sentences) immediately after each question heading' });\n }\n\n return { criterion: 'query_answer_alignment', criterion_label: 'Query-Answer Alignment', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 25: Content Cannibalization ───────────────────────────────────\n\nconst STOP_WORDS = new Set([\n 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',\n 'of', 'with', 'by', 'from', 'is', 'it', 'as', 'be', 'was', 'are',\n 'this', 'that', 'your', 'our', 'we', 'you', 'how', 'what', 'why',\n]);\n\n\nfunction extractPageTitle(html: string): string {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n const title = titleMatch?.[1]?.trim() || '';\n const h1 = h1Match?.[1]?.replace(/<[^>]*>/g, '').trim() || '';\n return (title + ' ' + h1).toLowerCase().trim();\n}\n\nfunction titleToWordSet(title: string): Set<string> {\n return new Set(\n title.split(/\\s+/).filter(w => w.length > 1 && !STOP_WORDS.has(w))\n );\n}\n\nfunction jaccardSimilarity(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 && b.size === 0) return 0;\n let intersection = 0;\n for (const word of a) {\n if (b.has(word)) intersection++;\n }\n const union = a.size + b.size - intersection;\n return union === 0 ? 0 : intersection / union;\n}\n\nfunction checkContentCannibalization(data: SiteData, topicCoherenceScore?: number): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for cannibalization analysis' });\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n // Collect pages: homepage + all blogSample\n const pages: Array<{ html: string; url: string }> = [\n { html: data.homepage.text, url: data.homepage.finalUrl || `https://${data.domain}/` },\n ];\n if (data.blogSample) {\n for (const page of data.blogSample) {\n pages.push({ html: page.text, url: page.finalUrl || '' });\n }\n }\n\n if (pages.length <= 1) {\n findings.push({ severity: 'info', detail: 'Only homepage available - cannot assess content cannibalization', fix: 'Add blog/content pages to enable cross-page topic overlap analysis' });\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score: 5, status: 'partial', findings, fix_priority: 'P3' };\n }\n\n // Extract titles and word sets\n const pageTitles = pages.map(p => ({ title: extractPageTitle(p.html), url: p.url }));\n const wordSets = pageTitles.map(p => titleToWordSet(p.title));\n\n // Find site-wide common terms (appearing in >40% of pages).\n // These are \"site theme terms\" (e.g., \"medicare\" on a Medicare site) - NOT cannibalization signals.\n // A focused site SHOULD have many pages sharing core topic terms.\n const termPageCount = new Map<string, number>();\n for (const ws of wordSets) {\n for (const w of ws) {\n termPageCount.set(w, (termPageCount.get(w) || 0) + 1);\n }\n }\n const commonTermThreshold = Math.max(3, pages.length * 0.4);\n const siteThemeTerms = new Set<string>();\n for (const [term, count] of termPageCount) {\n if (count >= commonTermThreshold) siteThemeTerms.add(term);\n }\n\n // Filter word sets: remove site theme terms to compare only distinguishing words\n const filteredSets = wordSets.map(ws => {\n const filtered = new Set<string>();\n for (const w of ws) {\n if (!siteThemeTerms.has(w)) filtered.add(w);\n }\n return filtered;\n });\n\n // Pairwise comparison on FILTERED word sets (only distinguishing terms)\n const cannibalPairs: Array<{ urlA: string; urlB: string; similarity: number }> = [];\n for (let i = 0; i < pages.length; i++) {\n for (let j = i + 1; j < pages.length; j++) {\n // Skip pairs where both have empty filtered sets (means both pages are purely about the core topic)\n if (filteredSets[i].size === 0 && filteredSets[j].size === 0) continue;\n const sim = jaccardSimilarity(filteredSets[i], filteredSets[j]);\n if (sim > 0.6) {\n cannibalPairs.push({\n urlA: pageTitles[i].url.slice(0, 60),\n urlB: pageTitles[j].url.slice(0, 60),\n similarity: Math.round(sim * 100),\n });\n }\n }\n }\n\n // Count unique URLs involved in cannibalization (not pairs)\n const cannibalUrls = new Set<string>();\n for (const pair of cannibalPairs) {\n cannibalUrls.add(pair.urlA);\n cannibalUrls.add(pair.urlB);\n }\n // Scale severity by the ratio of cannibalized pages to total pages\n const cannibalRatio = pages.length > 0 ? cannibalUrls.size / pages.length : 0;\n\n let score: number;\n if (cannibalPairs.length === 0) {\n score = 10;\n findings.push({ severity: 'info', detail: `${pages.length} pages analyzed - no content cannibalization detected` });\n } else if (cannibalRatio <= 0.05) {\n // <5% of pages overlap - negligible\n score = 9;\n findings.push({ severity: 'info', detail: `${cannibalPairs.length} pair(s) of pages with minor topic overlap (${cannibalUrls.size}/${pages.length} pages affected)` });\n } else if (cannibalRatio <= 0.10) {\n score = 7;\n findings.push({ severity: 'low', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) have overlapping topics`, fix: 'Differentiate titles and H1 headings to reduce topic overlap' });\n } else if (cannibalRatio <= 0.20) {\n score = 5;\n findings.push({ severity: 'medium', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) competing for overlapping topics`, fix: 'Consolidate overlapping pages or differentiate their titles and content focus' });\n } else if (cannibalRatio <= 0.40) {\n score = 3;\n findings.push({ severity: 'medium', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) have significant content overlap`, fix: 'Many pages compete for the same topics - consolidate or clearly differentiate them' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${cannibalUrls.size} pages (${Math.round(cannibalRatio * 100)}%) competing for the same topics`, fix: 'Severe content cannibalization - consolidate overlapping pages or create clear topic differentiation' });\n }\n\n // Detail findings for up to 3 pairs\n for (const pair of cannibalPairs.slice(0, 3)) {\n findings.push({ severity: 'low', detail: `Overlap (${pair.similarity}%): ${pair.urlA} vs ${pair.urlB}` });\n }\n\n // Cross-criterion adjustment: If topic coherence is very low (<= 4), cap cannibalization score.\n // \"No overlap\" because content is scattered (humidifiers, groceries, neuropathy) is NOT\n // the same as \"no overlap\" because content is well-differentiated within a focused niche.\n if (topicCoherenceScore !== undefined && topicCoherenceScore <= 4 && score >= 8) {\n score = 6;\n findings.push({ severity: 'low', detail: 'Low topic overlap but content lacks coherent focus - not a strong signal for AI authority', fix: 'Focus content on fewer core topics to build topical authority that AI engines can identify' });\n }\n\n return { criterion: 'content_cannibalization', criterion_label: 'Content Cannibalization', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 26: Visible Date Signal ───────────────────────────────────────\n\nfunction checkVisibleDateSignal(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'No homepage available for date signal analysis' });\n return { criterion: 'visible_date_signal', criterion_label: 'Visible Date Signal', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n let score = 0;\n\n // 1. Visible <time datetime=\"...\">text</time> elements\n const timeElements = combinedHtml.match(/<time[^>]*datetime=\"[^\"]*\"[^>]*>[^<]+<\\/time>/gi) || [];\n const hasVisibleTime = timeElements.length > 0;\n if (hasVisibleTime) {\n score += 5;\n findings.push({ severity: 'info', detail: `${timeElements.length} visible <time> element(s) with datetime attribute found` });\n }\n\n // 2. datePublished/dateModified inside JSON-LD <script> blocks only\n const ldJsonBlocks = combinedHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const ldJsonText = ldJsonBlocks.join(' ');\n const hasDatePublished = /datePublished/i.test(ldJsonText);\n const hasDateModified = /dateModified/i.test(ldJsonText);\n const hasSchemaDate = hasDatePublished || hasDateModified;\n if (hasSchemaDate) {\n if (!hasVisibleTime) score += 7;\n else score += 5;\n const dateTypes = [hasDatePublished && 'datePublished', hasDateModified && 'dateModified'].filter(Boolean).join(' + ');\n findings.push({ severity: 'info', detail: `JSON-LD schema contains ${dateTypes}` });\n }\n\n // 3. article:published_time / article:modified_time meta tags\n const hasMetaPublished = /<meta[^>]*property=\"article:published_time\"[^>]*>/i.test(combinedHtml);\n const hasMetaModified = /<meta[^>]*property=\"article:modified_time\"[^>]*>/i.test(combinedHtml);\n const hasMetaDate = hasMetaPublished || hasMetaModified;\n if (hasMetaDate && !hasVisibleTime && !hasSchemaDate) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Article meta tags with date information found' });\n } else if (hasMetaDate) {\n findings.push({ severity: 'info', detail: 'Article meta date tags also present (supplementary)' });\n }\n\n // Bonus: dateModified within 6 months\n if (hasDateModified) {\n const dateModMatch = ldJsonText.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (dateModMatch) {\n const modDate = new Date(dateModMatch[1]);\n if (!isNaN(modDate.getTime())) {\n const daysDiff = Math.floor((Date.now() - modDate.getTime()) / (1000 * 60 * 60 * 24));\n if (daysDiff <= 180) {\n score += 1;\n findings.push({ severity: 'info', detail: `dateModified is recent (${daysDiff} days ago) - freshness bonus applied` });\n }\n }\n }\n }\n\n score = Math.min(10, score);\n\n if (score === 0) {\n findings.push({ severity: 'high', detail: 'No visible date signals found (no <time> elements, no JSON-LD dates, no article meta dates)', fix: 'Add <time datetime=\"...\"> elements for user-visible dates and datePublished/dateModified to JSON-LD schema' });\n }\n\n return { criterion: 'visible_date_signal', criterion_label: 'Visible Date Signal', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Raw data summary extraction (for AI narrative) ─────────────────────────\n\nexport function extractRawDataSummary(data: SiteData): RawDataSummary {\n const html = data.homepage?.text || '';\n const text = html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n\n // Schema types\n const ldJsonMatches = html.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const allSchemaText = ldJsonMatches.join(' ').toLowerCase();\n const schemaTypes = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product'];\n const foundSchemaTypes = schemaTypes.filter(t =>\n allSchemaText.includes(`\"${t}\"`) || allSchemaText.includes(`\"@type\":\"${t}\"`)\n );\n\n // Links\n const linkMatches = html.match(/<a[^>]*href=\"([^\"]*)\"[^>]*>/gi) || [];\n const internalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('/') || href.includes(data.domain);\n });\n const externalLinks = linkMatches.filter(l => {\n const href = l.match(/href=\"([^\"]*)\"/)?.[1] || '';\n return href.startsWith('http') && !href.includes(data.domain);\n });\n\n // robots.txt AI crawlers\n const robotsText = (data.robotsTxt?.text || '').toLowerCase();\n const aiCrawlers = ['gptbot', 'claudebot', 'perplexitybot', 'anthropic', 'chatgpt'];\n const mentionedCrawlers = aiCrawlers.filter(c => robotsText.includes(c));\n const blockedCrawlers = mentionedCrawlers.filter(c => {\n const sectionRegex = new RegExp(`user-agent:\\\\s*${c}[^\\\\S\\\\n]*\\\\n([\\\\s\\\\S]*?)(?=user-agent:|$)`, 'i');\n const match = sectionRegex.exec(data.robotsTxt?.text || '');\n if (!match) return false;\n const section = match[1];\n if (/^allow:\\s*\\/\\s*$/im.test(section)) return false;\n return /^disallow:\\s*\\/\\s*$/im.test(section);\n });\n\n // Headings\n const hTagContent = (html.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n const questionHeadings = hTagContent.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h));\n const h1Count = (html.match(/<h1[\\s>]/gi) || []).length;\n\n // Images\n const images = html.match(/<img[^>]*>/gi) || [];\n const imagesWithAlt = images.filter(img => /alt=\"[^\"]+\"/i.test(img));\n\n // Semantic elements\n const semanticChecks = ['main', 'article', 'nav', 'header', 'footer', 'section', 'time'];\n const foundElements = semanticChecks.filter(el => new RegExp(`<${el}[\\\\s>]`, 'i').test(html));\n\n return {\n domain: data.domain,\n protocol: data.protocol,\n homepage_length: html.length,\n homepage_text_length: text.trim().length,\n has_https: data.protocol === 'https',\n llms_txt_status: data.llmsTxt && !isHtmlResponse(data.llmsTxt) ? data.llmsTxt.status : null,\n llms_txt_length: data.llmsTxt?.status === 200 && !isHtmlResponse(data.llmsTxt) ? (data.llmsTxt.text.length) : 0,\n robots_txt_status: data.robotsTxt && !isHtmlResponse(data.robotsTxt) ? data.robotsTxt.status : null,\n robots_txt_snippet: (data.robotsTxt?.text || '').slice(0, 500),\n robots_txt_ai_crawlers: mentionedCrawlers,\n robots_txt_blocked_crawlers: blockedCrawlers,\n schema_types_found: foundSchemaTypes,\n schema_block_count: ldJsonMatches.length,\n faq_page_status: data.faqPage?.status ?? null,\n faq_page_length: data.faqPage?.status === 200 ? data.faqPage.text.length : 0,\n sitemap_status: data.sitemapXml?.status ?? null,\n internal_link_count: internalLinks.length,\n external_link_count: externalLinks.length,\n question_headings_count: questionHeadings.length,\n h1_count: h1Count,\n has_meta_description: /<meta[^>]*name=\"description\"[^>]*>/i.test(html),\n has_title: /<title[^>]*>[^<]+<\\/title>/i.test(html),\n has_phone: (() => {\n const phoneMatch = /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/.test(text);\n if (!phoneMatch) return false;\n // Context validation: tel: link, schema telephone, or nearby keywords\n return /href=\"tel:/i.test(html) || /\"telephone\"/i.test(html) ||\n /\\b(phone|call|tel:|contact\\s*us|fax|dial)\\b/i.test(text);\n })(),\n has_address: /\\d+\\s+\\w+\\s+(street|st|avenue|ave|road|rd|drive|dr|blvd|boulevard|lane|ln|way|court|ct)/i.test(text),\n has_org_schema: /organization|localbusiness/i.test(html),\n has_social_links: /sameas|linkedin\\.com|facebook\\.com|twitter\\.com|x\\.com/i.test(html),\n semantic_elements_found: foundElements,\n img_count: images.length,\n img_with_alt_count: imagesWithAlt.length,\n has_lang_attr: /lang=\"[a-z]{2}\"/i.test(html),\n has_aria: /role=\"|aria-/i.test(html),\n has_breadcrumbs: /breadcrumb|aria-label=\"breadcrumb\"/i.test(html),\n has_nav: /<nav[\\s>]/i.test(html),\n has_footer: /<footer[\\s>]/i.test(html),\n has_case_studies: /case\\s+stud|testimonial|success\\s+stor|client\\s+stor/i.test(text),\n has_statistics: /\\d+%|\\d+\\s*(patients|clients|customers|cases|years|professionals|specialists|companies|users|businesses|domains|audits)/i.test(text),\n has_expert_attribution: /written\\s+by|authored\\s+by|expert|specialist|board.certified|licensed/i.test(text),\n has_blog_section: /href=\"[^\"]*\\/(?:blog|articles|insights|guides|resources)\\b[^\"]*\"/i.test(html),\n // New criteria fields\n has_date_modified_schema: /dateModified/i.test(html),\n time_element_count: (html.match(/<time[\\s>]/gi) || []).length,\n sitemap_url_count: (data.sitemapXml?.text?.match(/<loc>/gi) || []).length,\n has_rss_feed: !!(data.rssFeed && data.rssFeed.status === 200 && !isHtmlResponse(data.rssFeed)),\n table_count: (html.match(/<table[\\s>]/gi) || []).length,\n ordered_list_count: (html.match(/<ol[\\s>]/gi) || []).length,\n unordered_list_count: (html.match(/<ul[\\s>]/gi) || []).length,\n definition_pattern_count: (text.match(/\\brefers?\\s+to\\b|\\bdefined\\s+as\\b|\\bknown\\s+as\\b/gi) || []).length,\n has_ai_txt: !!(data.aiTxt && data.aiTxt.status === 200 && !isHtmlResponse(data.aiTxt)),\n has_person_schema: /\"@type\"\\s*:\\s*\"Person\"/i.test(html),\n fact_data_point_count: (text.match(/\\d+(?:\\.\\d+)?(?:\\s*%|\\s*\\$|\\s*USD)/g) || []).length,\n has_canonical: /<link[^>]*rel=\"canonical\"/i.test(html),\n has_license_schema: /license|copyrightHolder/i.test(html) && /application\\/ld\\+json/i.test(html),\n sitemap_recent_lastmod_count: (() => {\n const analysis = countRecentSitemapDates(data.sitemapXml?.text || '');\n return analysis.isUniform ? analysis.distinctRecentDays : analysis.recentCount;\n })(),\n // Speakable schema fields\n has_speakable_schema: /speakablespecification/i.test(ldJsonMatches.join(' ')) || /\"speakable\"\\s*:/i.test(ldJsonMatches.join(' ')),\n speakable_selector_count: (ldJsonMatches.join(' ').match(/\"cssselector\"|\"xpath\"/gi) || []).length,\n // Blog sample fields\n blog_sample_count: data.blogSample?.length ?? 0,\n blog_sample_urls: data.blogSample?.map(p => p.finalUrl || '').filter(Boolean) ?? [],\n blog_sample_schema_types: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return [];\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const blogLd = blogHtml.match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n const blogSchema = blogLd.join(' ').toLowerCase();\n const types = ['organization', 'localbusiness', 'faqpage', 'service', 'article', 'webpage', 'website', 'breadcrumblist', 'howto', 'product', 'person'];\n return types.filter(t => blogSchema.includes(`\"${t}\"`));\n })(),\n blog_sample_question_headings: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return 0;\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n const hTags = (blogHtml.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi) || []).map(h => h.replace(/<[^>]*>/g, ''));\n return hTags.filter(h => h.includes('?') || /^(what|how|why|when|who|where|can|do|does|is|are|should)\\s/i.test(h)).length;\n })(),\n blog_sample_faq_schema_found: (() => {\n if (!data.blogSample || data.blogSample.length === 0) return false;\n const blogHtml = data.blogSample.map(p => p.text).join('\\n');\n return /faqpage/i.test(blogHtml) && /application\\/ld\\+json/i.test(blogHtml);\n })(),\n // Criteria 24-26 fields\n question_heading_answer_rate: (() => {\n const combinedHtml = getCombinedHtml(data);\n const qHeadings = extractQuestionHeadings(combinedHtml);\n if (qHeadings.length === 0) return -1;\n let answered = 0;\n for (const qh of qHeadings) {\n const escaped = qh.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pat = new RegExp(escaped + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>([\\\\s\\\\S]{0,1500}?)(?=<h[1-6]|$)', 'i');\n const m = pat.exec(combinedHtml);\n if (m && m[1].replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim().length >= 20) answered++;\n }\n return Math.round((answered / qHeadings.length) * 100);\n })(),\n question_heading_total: extractQuestionHeadings(getCombinedHtml(data)).length,\n cannibalizing_pairs_count: (() => {\n const pages: Array<{ html: string }> = [{ html: data.homepage?.text || '' }];\n if (data.blogSample) for (const p of data.blogSample.slice(0, 5)) pages.push({ html: p.text });\n if (pages.length <= 1) return 0;\n const ws = pages.map(p => titleToWordSet(extractPageTitle(p.html)));\n let pairs = 0;\n for (let i = 0; i < ws.length; i++) {\n for (let j = i + 1; j < ws.length; j++) {\n if (jaccardSimilarity(ws[i], ws[j]) > 0.6) pairs++;\n }\n }\n return pairs;\n })(),\n page_titles_sampled: 1 + (data.blogSample?.length ?? 0),\n has_visible_date: (/<time[^>]*datetime=\"[^\"]*\"[^>]*>[^<]+<\\/time>/i).test(getCombinedHtml(data)),\n has_schema_date_in_ld: (() => {\n const ld = (getCombinedHtml(data).match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || []).join(' ');\n return /datePublished|dateModified/i.test(ld);\n })(),\n date_modified_recency_days: (() => {\n const ld = (getCombinedHtml(data).match(/<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([\\s\\S]*?)<\\/script>/gi) || []).join(' ');\n const m = ld.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (!m) return null;\n const d = new Date(m[1]);\n if (isNaN(d.getTime())) return null;\n return Math.floor((Date.now() - d.getTime()) / (1000 * 60 * 60 * 24));\n })(),\n // Full-crawl stats\n crawl_discovered: data.crawlStats?.discovered ?? 0,\n crawl_fetched: data.crawlStats?.fetched ?? 0,\n crawl_skipped: data.crawlStats?.skipped ?? 0,\n // V2 criteria fields\n citation_ready_sentences: (() => {\n const combinedText = text + ' ' + (data.blogSample?.map(p => p.text.replace(/<[^>]*>/g, ' ')).join(' ') || '');\n return (combinedText.match(/\\b\\w+\\s+(is\\s+(?:a|an)\\s|refers\\s+to|defined\\s+as)\\b/gi) || []).length;\n })(),\n answer_first_ratio: (() => {\n const pages = [html, ...(data.blogSample?.map(p => p.text) || [])];\n let answerFirst = 0;\n for (const pageHtml of pages) {\n const bodyMatch = pageHtml.match(/<body[^>]*>([\\s\\S]*)/i);\n const bodyHtml = bodyMatch ? bodyMatch[1] : pageHtml;\n const earlyParas = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 5) || [];\n for (const p of earlyParas) {\n const pText = p.replace(/<[^>]*>/g, '').trim();\n const wc = pText.split(/\\s+/).length;\n if (wc >= 40 && wc <= 80) { answerFirst++; break; }\n }\n }\n return pages.length > 0 ? Math.round((answerFirst / pages.length) * 100) : 0;\n })(),\n evidence_citations_avg: (() => {\n const allHtml = html + '\\n' + (data.blogSample?.map(p => p.text).join('\\n') || '');\n const paragraphs = allHtml.match(/<p[^>]*>[\\s\\S]*?<\\/p>/gi) || [];\n let citations = 0;\n const domainLower = data.domain.replace(/^www\\./, '').toLowerCase();\n for (const p of paragraphs) {\n const links = p.match(/<a[^>]*href=[\"'](https?:\\/\\/[^\"']+)[\"'][^>]*>/gi) || [];\n for (const link of links) {\n const href = link.match(/href=[\"'](https?:\\/\\/[^\"']+)[\"']/i);\n if (href) {\n try {\n const ld = new URL(href[1]).hostname.replace(/^www\\./, '').toLowerCase();\n if (ld !== domainLower) citations++;\n } catch { /* ignore */ }\n }\n }\n }\n const pageCount = Math.max(1, 1 + (data.blogSample?.length ?? 0));\n return Math.round((citations / pageCount) * 10) / 10;\n })(),\n entity_disambiguation_ratio: (() => {\n const pages = [html, ...(data.blogSample?.map(p => p.text) || [])];\n let defined = 0;\n for (const pageHtml of pages) {\n const h1Match = pageHtml.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (!h1Match) continue;\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3);\n const primaryNoun = h1Words.sort((a, b) => b.length - a.length)[0] || '';\n if (!primaryNoun) continue;\n const pageText = pageHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').slice(0, 500);\n if (new RegExp(`\\\\b${primaryNoun.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b[^.]*\\\\b(is|refers|defined|means)\\\\b`, 'i').test(pageText)) {\n defined++;\n }\n }\n return pages.length > 0 ? Math.round((defined / pages.length) * 100) : 0;\n })(),\n extraction_friction_avg: (() => {\n const combinedText = text + ' ' + (data.blogSample?.map(p => p.text.replace(/<[^>]*>/g, ' ')).join(' ') || '');\n const sentences = combinedText.split(/[.!?]+/).filter(s => s.trim().length > 5);\n if (sentences.length === 0) return 0;\n const totalWords = sentences.reduce((sum, s) => sum + s.trim().split(/\\s+/).length, 0);\n return Math.round((totalWords / sentences.length) * 10) / 10;\n })(),\n image_figure_ratio: (() => {\n const combinedHtml = html + '\\n' + (data.blogSample?.map(p => p.text).join('\\n') || '');\n const allImages = combinedHtml.match(/<img\\s[^>]*>/gi) || [];\n if (allImages.length === 0) return 0;\n const figureBlocks = combinedHtml.match(/<figure[\\s\\S]*?<\\/figure>/gi) || [];\n const figuresWithCaption = figureBlocks.filter(f => /<figcaption/i.test(f));\n return Math.round((figuresWithCaption.length / allImages.length) * 100);\n })(),\n };\n}\n\n// ─── Criterion 27: Topic Coherence ────────────────────────────────────────────\n\n/**\n * Extract meaningful terms from text (strip HTML, tokenize, remove stop words).\n */\nfunction extractTerms(html: string): string[] {\n const text = html\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .toLowerCase();\n return text.split(/[\\s,.!?;:()\\[\\]{}\"'\\/]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n}\n\n/**\n * Extract core topic terms from homepage.\n * Uses H1, title tag, and meta description as the strongest signals.\n */\nfunction extractCoreTopicTerms(html: string): Set<string> {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n const metaDescMatch = html.match(/<meta\\s+[^>]*name=[\"']description[\"'][^>]*content=[\"']([^\"']+)[\"']/i)\n || html.match(/<meta\\s+[^>]*content=[\"']([^\"']+)[\"'][^>]*name=[\"']description[\"']/i);\n\n // Combine the most prominent text signals\n const prominentText = [\n titleMatch?.[1] || '',\n h1Match?.[1]?.replace(/<[^>]*>/g, '') || '',\n metaDescMatch?.[1] || '',\n ].join(' ');\n\n // Also extract all H2 headings from homepage for broader topic context\n const h2Matches = html.match(/<h2[^>]*>([\\s\\S]*?)<\\/h2>/gi) || [];\n const h2Text = h2Matches.map(h => h.replace(/<[^>]*>/g, '')).join(' ');\n\n const allTerms = extractTerms(prominentText + ' ' + h2Text);\n\n // Count term frequency and take top 25 most common\n const freq = new Map<string, number>();\n for (const t of allTerms) {\n freq.set(t, (freq.get(t) || 0) + 1);\n }\n\n const sorted = [...freq.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 25)\n .map(([term]) => term);\n\n return new Set(sorted);\n}\n\n/**\n * Extract title text from a page (title tag + H1 combined).\n */\nfunction getPageTopicText(html: string): string {\n const titleMatch = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n return [\n titleMatch?.[1] || '',\n h1Match?.[1]?.replace(/<[^>]*>/g, '') || '',\n ].join(' ').toLowerCase().trim();\n}\n\n/**\n * Extract bigrams (2-word phrases) from text.\n */\nfunction extractBigrams(text: string): string[] {\n const words = text.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n const bigrams: string[] = [];\n for (let i = 0; i < words.length - 1; i++) {\n bigrams.push(words[i] + ' ' + words[i + 1]);\n }\n return bigrams;\n}\n\n/**\n * Topic Coherence measures two things:\n * 1. Topic Focus: What % of pages share the dominant topic bigram\n * 2. Topic Scatter: How many distinct topic clusters exist\n *\n * A site with 90% of pages about \"Medicare coverage\" = high coherence\n * A site with pages about Medicare, humidifiers, groceries, neuropathy = low coherence\n */\nfunction checkTopicCoherence(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score: 0, status: 'not_found', findings, fix_priority: 'P0' };\n }\n\n if (!data.blogSample || data.blogSample.length < 3) {\n findings.push({ severity: 'info', detail: `Only ${data.blogSample?.length || 0} blog pages found - insufficient for topic coherence analysis` });\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score: 5, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n const blogPages = data.blogSample;\n\n // ─── Extract brand/domain words to exclude from topic analysis ──────────────\n // Compound domain names like \"miramace.com\" need to be split into component words.\n // We do a two-pass approach: first collect raw frequencies, then identify brand terms.\n const domainBase = data.domain.replace(/^www\\./, '').replace(/\\.(com|org|net|io|co|ai)$/i, '').toLowerCase();\n const brandWords = new Set<string>();\n brandWords.add(domainBase);\n // Split on hyphens/underscores\n for (const part of domainBase.split(/[-_]/)) {\n if (part.length > 2) brandWords.add(part);\n }\n\n // ─── Part 1: Topic Focus via dominant terms ─────────────────────────────────\n // First pass: count ALL terms (including potential brand words)\n const rawTermFreq = new Map<string, number>();\n const pageTitleTexts: string[] = [];\n\n for (const page of blogPages) {\n const topicText = getPageTopicText(page.text);\n pageTitleTexts.push(topicText);\n const words = topicText.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w));\n const uniqueWords = new Set(words);\n for (const w of uniqueWords) {\n rawTermFreq.set(w, (rawTermFreq.get(w) || 0) + 1);\n }\n }\n\n // Second pass: identify brand terms - words that appear in >80% of pages\n // AND are substrings of the domain name (e.g., \"mira\" is in \"miramace\")\n for (const [term, count] of rawTermFreq) {\n if (count / blogPages.length >= 0.80 && domainBase.includes(term)) {\n brandWords.add(term);\n }\n }\n\n // Third pass: rebuild frequencies without brand words\n const termFreq = new Map<string, number>();\n for (const page of blogPages) {\n const topicText = getPageTopicText(page.text);\n const words = topicText.split(/[\\s,.!?;:()\\[\\]{}\"'\\/&]+/)\n .filter(w => w.length > 2 && !STOP_WORDS.has(w) && !/^\\d+$/.test(w) && !brandWords.has(w));\n const uniqueWords = new Set(words);\n for (const w of uniqueWords) {\n termFreq.set(w, (termFreq.get(w) || 0) + 1);\n }\n }\n\n // Find the top terms that appear across the most pages\n const sortedTerms = [...termFreq.entries()]\n .sort((a, b) => b[1] - a[1]);\n const topTerm = sortedTerms[0];\n\n // ─── Part 2: Topic Scatter via unique topic clusters ────────────────────────\n // Extract meaningful bigrams from each page title and cluster them\n const bigramFreq = new Map<string, number>();\n const pageBigrams: string[][] = [];\n\n for (const topicText of pageTitleTexts) {\n const bigrams = extractBigrams(topicText)\n .filter(bg => !bg.split(' ').some(w => brandWords.has(w)));\n pageBigrams.push(bigrams);\n const uniqueBigrams = new Set(bigrams);\n for (const bg of uniqueBigrams) {\n bigramFreq.set(bg, (bigramFreq.get(bg) || 0) + 1);\n }\n }\n\n // Find dominant bigram\n const sortedBigrams = [...bigramFreq.entries()]\n .sort((a, b) => b[1] - a[1]);\n const topBigram = sortedBigrams[0];\n\n // Count distinct \"topic clusters\" - bigrams that appear in 2+ pages but are unique subjects\n // A focused site has 1-3 topic clusters; a scattered site has 10+\n const significantBigrams = sortedBigrams.filter(([, count]) => count >= 2);\n // Group overlapping bigrams (sharing a word) into clusters\n const clusterRoots: string[] = [];\n const assigned = new Set<string>();\n for (const [bg] of significantBigrams) {\n if (assigned.has(bg)) continue;\n clusterRoots.push(bg);\n assigned.add(bg);\n const [w1, w2] = bg.split(' ');\n // Mark overlapping bigrams as same cluster\n for (const [otherBg] of significantBigrams) {\n if (assigned.has(otherBg)) continue;\n if (otherBg.includes(w1) || otherBg.includes(w2)) {\n assigned.add(otherBg);\n }\n }\n }\n const topicClusterCount = clusterRoots.length;\n\n // Count pages that contain the dominant term\n const dominantTerm = topTerm?.[0] || '';\n const dominantTermCount = topTerm?.[1] || 0;\n const focusRatio = blogPages.length > 0 ? dominantTermCount / blogPages.length : 0;\n\n // Count pages that contain the dominant bigram\n const dominantBigram = topBigram?.[0] || '';\n const dominantBigramCount = topBigram?.[1] || 0;\n const bigramFocusRatio = blogPages.length > 0 ? dominantBigramCount / blogPages.length : 0;\n\n // ─── Scoring ────────────────────────────────────────────────────────────────\n // Primary signal: what % of pages share the dominant topic term.\n // Secondary signal: how many distinct topic clusters exist.\n // A Medicare site with varied products (\"scooter\", \"wheelchair\", \"walker\") is STILL coherent\n // if 80%+ of pages share \"medicare\" as the core topic.\n\n let score = 0;\n const bestFocusRatio = Math.max(focusRatio, bigramFocusRatio);\n\n // For sites with many pages, absolute term count matters more than ratio.\n // A term appearing on 15 of 60 pages (25%) is better than 2 of 5 pages (40%).\n const dominantPageCount = Math.max(dominantTermCount, dominantBigramCount);\n const hasStrongAbsolutePresence = dominantPageCount >= 10;\n\n // Focus score (0-7 points) - scale thresholds for large sites\n if (bestFocusRatio >= 0.80) {\n score += 7;\n } else if (bestFocusRatio >= 0.60) {\n score += 6;\n } else if (bestFocusRatio >= 0.45 || (hasStrongAbsolutePresence && bestFocusRatio >= 0.30)) {\n score += 5;\n } else if (bestFocusRatio >= 0.30 || (hasStrongAbsolutePresence && bestFocusRatio >= 0.20)) {\n score += 4;\n } else if (bestFocusRatio >= 0.15) {\n score += 2;\n } else {\n score += 1;\n }\n\n // Scatter adjustment (0-3 points for few clusters, penalty for many)\n // Scale cluster thresholds by page count - more pages naturally produce more clusters\n const clusterPenaltyReduced = focusRatio >= 0.70 || hasStrongAbsolutePresence;\n const scaledLow = Math.max(3, Math.floor(blogPages.length / 10));\n const scaledMid = Math.max(6, Math.floor(blogPages.length / 5));\n const scaledHigh = Math.max(10, Math.floor(blogPages.length / 3));\n\n if (topicClusterCount <= scaledLow) {\n score += 3;\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic cluster(s) - tightly focused content` });\n } else if (topicClusterCount <= scaledMid) {\n score += clusterPenaltyReduced ? 2 : 1;\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic clusters${clusterPenaltyReduced ? ' within a focused niche' : ' - moderately focused'}` });\n } else if (topicClusterCount <= scaledHigh) {\n score += clusterPenaltyReduced ? 1 : 0;\n if (!clusterPenaltyReduced) {\n findings.push({ severity: 'low', detail: `${topicClusterCount} topic clusters - scattered content`, fix: 'Reduce the number of distinct topics. Focus blog content on 2-3 core expertise areas.' });\n } else {\n findings.push({ severity: 'info', detail: `${topicClusterCount} topic clusters but strong core topic focus (${Math.round(focusRatio * 100)}%)` });\n }\n } else {\n score += clusterPenaltyReduced ? 0 : -2;\n if (!clusterPenaltyReduced) {\n findings.push({ severity: 'medium', detail: `${topicClusterCount} topic clusters - highly scattered content`, fix: 'Content covers too many unrelated topics. AI engines cannot identify your expertise. Focus on your core niche.' });\n } else {\n findings.push({ severity: 'low', detail: `${topicClusterCount} topic clusters despite strong core topic focus`, fix: 'Consider narrowing subtopics within your niche for even stronger AI visibility.' });\n }\n }\n\n score = Math.max(0, Math.min(10, score));\n\n // Report dominant topic\n if (dominantTerm) {\n const focusPct = Math.round(focusRatio * 100);\n findings.push({ severity: 'info', detail: `Dominant topic term: \"${dominantTerm}\" (${focusPct}% of ${blogPages.length} pages)` });\n }\n if (dominantBigram && dominantBigramCount >= 2) {\n findings.push({ severity: 'info', detail: `Dominant topic phrase: \"${dominantBigram}\" (${dominantBigramCount}/${blogPages.length} pages)` });\n }\n\n // Off-topic examples\n const offTopicExamples: string[] = [];\n for (let i = 0; i < pageTitleTexts.length && offTopicExamples.length < 3; i++) {\n if (dominantTerm && !pageTitleTexts[i].includes(dominantTerm)) {\n const title = blogPages[i].text.match(/<title[^>]*>([^<]+)<\\/title>/i)?.[1]?.trim();\n if (title && title.length > 3) offTopicExamples.push(title.slice(0, 60));\n }\n }\n if (offTopicExamples.length > 0 && score < 8) {\n findings.push({ severity: 'low', detail: `Off-topic examples: ${offTopicExamples.join('; ')}` });\n }\n\n return { criterion: 'topic_coherence', criterion_label: 'Topic Coherence', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P0' };\n}\n\n// ─── Criterion 28: Content Depth ──────────────────────────────────────────────\n\nfunction countWords(html: string): number {\n const text = html\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\nfunction countHeadings(html: string): number {\n const headings = html.match(/<h[2-6][^>]*>/gi) || [];\n return headings.length;\n}\n\nfunction checkContentDepth(data: SiteData, topicCoherenceScore?: number): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.blogSample || data.blogSample.length < 2) {\n findings.push({ severity: 'info', detail: `Only ${data.blogSample?.length || 0} blog pages found - insufficient for depth analysis` });\n return { criterion: 'content_depth', criterion_label: 'Content Depth', score: 3, status: 'partial', findings, fix_priority: 'P2' };\n }\n\n const blogPages = data.blogSample;\n const wordCounts = blogPages.map(p => countWords(p.text));\n const headingCounts = blogPages.map(p => countHeadings(p.text));\n\n const avgWords = wordCounts.reduce((a, b) => a + b, 0) / wordCounts.length;\n const avgHeadings = headingCounts.reduce((a, b) => a + b, 0) / headingCounts.length;\n const deepPages = wordCounts.filter(w => w >= 1000).length;\n const thinPages = wordCounts.filter(w => w < 300).length;\n const deepRatio = deepPages / blogPages.length;\n const thinRatio = thinPages / blogPages.length;\n\n let score = 0;\n\n // Word count scoring (0-5 points)\n if (avgWords >= 2000) {\n score += 5;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page across ${blogPages.length} pages - excellent depth` });\n } else if (avgWords >= 1200) {\n score += 4;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page across ${blogPages.length} pages - good depth` });\n } else if (avgWords >= 800) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average ${Math.round(avgWords)} words per page - moderate depth` });\n } else if (avgWords >= 400) {\n score += 2;\n findings.push({ severity: 'low', detail: `Average ${Math.round(avgWords)} words per page - shallow content`, fix: 'Expand articles with more detail, examples, and expert analysis to build AI citation authority' });\n } else {\n score += 1;\n findings.push({ severity: 'medium', detail: `Average ${Math.round(avgWords)} words per page - very thin content`, fix: 'Content is too thin for AI engines to cite. Aim for 1000+ words per article with structured sections.' });\n }\n\n // Heading structure scoring (0-3 points)\n if (avgHeadings >= 8) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - well-structured` });\n } else if (avgHeadings >= 5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - decent structure` });\n } else if (avgHeadings >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page`, fix: 'Add more H2/H3 headings to break content into extractable sections' });\n } else {\n findings.push({ severity: 'medium', detail: `Average ${avgHeadings.toFixed(1)} subheadings per page - minimal structure`, fix: 'Add question-format H2/H3 headings so AI engines can extract specific answers' });\n }\n\n // Deep content ratio bonus (0-2 points)\n if (deepRatio >= 0.5) {\n score += 2;\n findings.push({ severity: 'info', detail: `${deepPages}/${blogPages.length} pages (${Math.round(deepRatio * 100)}%) have 1000+ words` });\n } else if (deepRatio >= 0.25) {\n score += 1;\n findings.push({ severity: 'info', detail: `${deepPages}/${blogPages.length} pages have 1000+ words` });\n }\n\n // Thin content penalty\n if (thinRatio >= 0.5) {\n score = Math.max(0, score - 2);\n findings.push({ severity: 'medium', detail: `${thinPages}/${blogPages.length} pages (${Math.round(thinRatio * 100)}%) have under 300 words - high thin content ratio`, fix: 'Remove or expand thin pages. Thin content dilutes site quality for AI engines.' });\n } else if (thinRatio >= 0.25) {\n score = Math.max(0, score - 1);\n findings.push({ severity: 'low', detail: `${thinPages}/${blogPages.length} pages have under 300 words` });\n }\n\n // Cross-criterion: deep content on scattered topics is less valuable for AI authority.\n // A 5000-word article about humidifiers doesn't help a healthcare company's AI citations.\n let finalScore = Math.min(10, score);\n if (topicCoherenceScore !== undefined && topicCoherenceScore <= 4 && finalScore >= 8) {\n finalScore = 7;\n findings.push({ severity: 'low', detail: 'Deep content but low topic coherence - depth on scattered topics has reduced AI citation value', fix: 'Focus content depth on your core expertise area for maximum AI visibility' });\n }\n\n return { criterion: 'content_depth', criterion_label: 'Content Depth', score: finalScore, status: finalScore >= 7 ? 'pass' : finalScore >= 4 ? 'partial' : 'fail', findings, fix_priority: finalScore >= 7 ? 'P3' : 'P1' };\n}\n\ninterface HelpfulContentPageScore {\n url: string;\n score: number;\n}\n\nfunction scoreSampledPages(\n data: SiteData,\n scorer: (html: string, url?: string) => number,\n): HelpfulContentPageScore[] {\n const pages: HelpfulContentPageScore[] = [];\n\n if (data.homepage) {\n const url = data.homepage.finalUrl || (data.protocol ? `${data.protocol}://${data.domain}/` : `https://${data.domain}/`);\n pages.push({ url, score: scorer(data.homepage.text, url) });\n }\n\n if (data.blogSample) {\n for (const page of data.blogSample) {\n const url = page.finalUrl || (data.protocol ? `${data.protocol}://${data.domain}/` : `https://${data.domain}/`);\n pages.push({ url, score: scorer(page.text, url) });\n }\n }\n\n return pages;\n}\n\nfunction summarizeHelpfulScores(pageScores: HelpfulContentPageScore[]) {\n const total = pageScores.length;\n const average = total > 0 ? Math.round(pageScores.reduce((sum, p) => sum + p.score, 0) / total) : 0;\n const strong = pageScores.filter(p => p.score >= 8);\n const weak = pageScores.filter(p => p.score <= 4);\n return { total, average, strong, weak };\n}\n\n// ─── Criterion 29: Helpful Purpose Alignment ────────────────────────────────\n\nfunction checkHelpfulPurposeAlignment(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'helpful_purpose_alignment', criterion_label: 'Helpful Purpose Alignment', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const pageScores = scoreSampledPages(data, scoreHelpfulPurposeAlignment);\n const { total, average, strong, weak } = summarizeHelpfulScores(pageScores);\n\n if (average >= 8) {\n findings.push({ severity: 'info', detail: `${strong.length}/${total} pages strongly prioritize visitor task completion over filler` });\n } else if (average >= 5) {\n findings.push({ severity: 'low', detail: `${strong.length}/${total} pages clearly lead with useful guidance`, fix: 'Tighten intros, reduce generic filler, and make pages solve the promised user task faster' });\n } else {\n findings.push({ severity: 'medium', detail: `Average helpful-purpose score is ${average}/10 across ${total} sampled pages`, fix: 'Reduce search-first framing, generic intros, and CTA interruptions before the first useful answer' });\n }\n\n if (weak.length > 0) {\n findings.push({\n severity: 'low',\n detail: `${weak.length} page(s) read as weakly task-focused`,\n fix: 'Rewrite weak pages to lead with concrete answers, tradeoffs, and next steps instead of broad introductory filler',\n });\n }\n\n return { criterion: 'helpful_purpose_alignment', criterion_label: 'Helpful Purpose Alignment', score: average, status: average >= 7 ? 'pass' : average >= 4 ? 'partial' : 'fail', findings, fix_priority: average >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 30: First-Hand Experience Signals ────────────────────────────\n\nfunction checkFirstHandExperienceSignals(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'first_hand_experience_signals', criterion_label: 'First-Hand Experience Signals', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const pageScores = scoreSampledPages(data, scoreFirstHandExperienceSignals);\n const { total, average, strong, weak } = summarizeHelpfulScores(pageScores);\n\n if (average >= 8) {\n findings.push({ severity: 'info', detail: `${strong.length}/${total} pages show strong signs of direct use, testing, or observation` });\n } else if (average >= 5) {\n findings.push({ severity: 'low', detail: `Moderate experiential depth across ${total} sampled pages`, fix: 'Add real implementation details, limitations, screenshots, or direct observations where relevant' });\n } else {\n findings.push({ severity: 'medium', detail: 'Little first-hand experience is visible in sampled content', fix: 'Add evidence of real use, testing, implementation, or lived experience instead of generic summaries' });\n }\n\n if (weak.length > 0) {\n findings.push({\n severity: 'low',\n detail: `${weak.length} page(s) appear generic or second-hand`,\n fix: 'Strengthen those pages with case details, lessons learned, or original evidence from practice',\n });\n }\n\n return { criterion: 'first_hand_experience_signals', criterion_label: 'First-Hand Experience Signals', score: average, status: average >= 7 ? 'pass' : average >= 4 ? 'partial' : 'fail', findings, fix_priority: average >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 31: Creator Transparency ─────────────────────────────────────\n\nfunction checkCreatorTransparency(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'creator_transparency', criterion_label: 'Creator Transparency', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const pageScores = scoreSampledPages(data, scoreCreatorTransparency);\n const { total, average, strong, weak } = summarizeHelpfulScores(pageScores);\n\n if (average >= 8) {\n findings.push({ severity: 'info', detail: `${strong.length}/${total} sampled pages provide clear visible creator attribution` });\n } else if (average >= 5) {\n findings.push({ severity: 'low', detail: 'Visible authorship is present on some content but inconsistent', fix: 'Add bylines, author links, and reviewer details on article-like pages where readers expect them' });\n } else {\n findings.push({ severity: 'medium', detail: 'Creator visibility is weak on content-like pages', fix: 'Show clear bylines, author pages, and reviewer context rather than relying on schema alone' });\n }\n\n if (weak.length > 0) {\n findings.push({\n severity: 'low',\n detail: `${weak.length} page(s) look article-like but expose little visible author context`,\n fix: 'Add visible bylines, author bios, or reviewer attribution to those pages',\n });\n }\n\n return { criterion: 'creator_transparency', criterion_label: 'Creator Transparency', score: average, status: average >= 7 ? 'pass' : average >= 4 ? 'partial' : 'fail', findings, fix_priority: average >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 32: Methodology Transparency ─────────────────────────────────\n\nfunction checkMethodologyTransparency(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'methodology_transparency', criterion_label: 'Methodology Transparency', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const pageScores = scoreSampledPages(data, scoreMethodologyTransparency);\n const { total, average, strong, weak } = summarizeHelpfulScores(pageScores);\n\n if (average >= 8) {\n findings.push({ severity: 'info', detail: `${strong.length}/${total} pages clearly explain how content was tested, researched, reviewed, or updated` });\n } else if (average >= 5) {\n findings.push({ severity: 'low', detail: 'Some process transparency exists, but it is inconsistent', fix: 'Add \"how we tested\", methodology, review process, or update disclosures on pages where users would expect them' });\n } else {\n findings.push({ severity: 'medium', detail: 'Little content-production or review transparency is visible', fix: 'Explain how pages were researched, tested, or reviewed instead of presenting unsupported comparisons or conclusions' });\n }\n\n if (weak.length > 0) {\n findings.push({\n severity: 'low',\n detail: `${weak.length} page(s) lack visible methodology or review context`,\n fix: 'Add process detail such as sample size, criteria, tools used, review process, or update notes',\n });\n }\n\n return { criterion: 'methodology_transparency', criterion_label: 'Methodology Transparency', score: average, status: average >= 7 ? 'pass' : average >= 4 ? 'partial' : 'fail', findings, fix_priority: average >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 33: Citation-Ready Writing Quality ─────────────────────────────\n\nfunction checkCitationReadyWriting(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'citation_ready_writing', criterion_label: 'Citation-Ready Writing Quality', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const blogHtml = getBlogHtml(data);\n const blogText = blogHtml ? blogHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ') : text;\n const pageCount = Math.max(1, (data.blogSample?.length ?? 0) + 1);\n let score = 0;\n\n // Definition sentences\n const defSentences = blogText.match(/\\b\\w+\\s+(is\\s+(?:a|an)\\s|refers\\s+to|defined\\s+as)\\b/gi) || [];\n const avgDef = defSentences.length / pageCount;\n if (avgDef >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `${defSentences.length} definition sentences found (avg ${avgDef.toFixed(1)}/page)` });\n } else if (avgDef >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${defSentences.length} definition sentences found (avg ${avgDef.toFixed(1)}/page)`, fix: 'Add more self-contained definition sentences that AI can quote directly' });\n } else {\n findings.push({ severity: 'medium', detail: 'No definition sentences found', fix: 'Write clear \"X is a...\" and \"X refers to...\" sentences for key concepts' });\n }\n\n // One-claim sentences (short sentences <30 words with single clause)\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10);\n const oneClaimSentences = sentences.filter(s => {\n const words = s.trim().split(/\\s+/);\n return words.length < 30 && !/,\\s*(and|but|or|which|that|because|although|while)\\s/i.test(s);\n });\n const avgOneClaim = oneClaimSentences.length / pageCount;\n if (avgOneClaim >= 5) {\n score += 3;\n findings.push({ severity: 'info', detail: `Strong single-claim sentence density (avg ${avgOneClaim.toFixed(1)}/page)` });\n } else if (avgOneClaim >= 2) {\n score += 1;\n findings.push({ severity: 'low', detail: `Moderate single-claim sentence density`, fix: 'Write more concise, single-claim sentences that AI engines can quote' });\n }\n\n // Self-contained check: first sentence after question headings\n const qHeadings = extractQuestionHeadings(combinedHtml);\n if (qHeadings.length > 0) {\n let selfContained = 0;\n for (const qh of qHeadings) {\n const escaped = qh.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pat = new RegExp(escaped + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>\\\\s*<p[^>]*>([^<]{0,200})', 'i');\n const m = pat.exec(combinedHtml);\n if (m && m[1] && !/^\\s*(this|that|these|those|it)\\s/i.test(m[1].trim())) {\n selfContained++;\n }\n }\n const selfContainedRatio = selfContained / qHeadings.length;\n if (selfContainedRatio >= 0.8) {\n score += 2;\n findings.push({ severity: 'info', detail: `${Math.round(selfContainedRatio * 100)}% of answer openings are self-contained (no dangling pronouns)` });\n } else {\n findings.push({ severity: 'low', detail: `Only ${Math.round(selfContainedRatio * 100)}% of answer openings are self-contained`, fix: 'Start answer paragraphs with the entity name, not \"This\" or \"That\"' });\n }\n }\n\n // Quotation-ready lines (sentences with stat + proper noun source)\n const quotableLines = sentences.filter(s =>\n /\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(s) &&\n /\\b[A-Z][a-z]+(?:\\s+[A-Z][a-z]+)*\\b/.test(s)\n );\n const avgQuotable = quotableLines.length / pageCount;\n if (avgQuotable >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: `${quotableLines.length} quotation-ready lines with stats and sources` });\n } else if (avgQuotable >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${quotableLines.length} quotation-ready lines found`, fix: 'Combine statistics with named sources for more citable content' });\n } else {\n findings.push({ severity: 'medium', detail: 'No quotation-ready lines found', fix: 'Write sentences that combine a statistic with a named source, e.g. \"According to Harvard, 85% of...\"' });\n }\n\n return { criterion: 'citation_ready_writing', criterion_label: 'Citation-Ready Writing Quality', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 30: Answer-First Placement ──────────────────────────────────────\n\nfunction checkAnswerFirstPlacement(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'answer_first_placement', criterion_label: 'Answer-First Placement', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const pages: string[] = [stripScripts(data.homepage.text)];\n if (data.blogSample) {\n for (const p of data.blogSample) {\n pages.push(stripScripts(p.text));\n }\n }\n\n let score = 0;\n let shortAnswerCount = 0;\n let answerFirstCount = 0;\n let entityFirstCount = 0;\n\n const throatClearing = /^(In today|Welcome to|Have you ever|If you're looking|In this (article|post|guide))/i;\n\n for (const pageHtml of pages) {\n // Extract first 300 words worth of text after body\n const bodyMatch = pageHtml.match(/<body[^>]*>([\\s\\S]*)/i);\n const bodyHtml = bodyMatch ? bodyMatch[1] : pageHtml;\n const bodyText = bodyHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n const first300Words = bodyText.split(/\\s+/).slice(0, 300).join(' ');\n\n // Short answer block (40-80 words paragraph in first 300 words)\n const earlyParagraphs = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 5) || [];\n for (const p of earlyParagraphs) {\n const pText = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = pText.split(/\\s+/).length;\n if (wordCount >= 40 && wordCount <= 80 && first300Words.includes(pText.slice(0, 50))) {\n shortAnswerCount++;\n break;\n }\n }\n\n // First-paragraph answer check (vs throat-clearing)\n const firstPara = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/i);\n if (firstPara) {\n const firstParaText = firstPara[1].replace(/<[^>]*>/g, '').trim();\n if (!throatClearing.test(firstParaText)) {\n answerFirstCount++;\n }\n }\n\n // Entity in first sentence: H1 primary noun in first body sentence\n const h1Match = pageHtml.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (h1Match) {\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3 && /^[A-Z]/.test(w) || w.length > 5);\n const firstSentence = bodyText.split(/[.!?]/)[0] || '';\n if (h1Words.some(w => firstSentence.toLowerCase().includes(w.toLowerCase()))) {\n entityFirstCount++;\n }\n }\n }\n\n const totalPages = pages.length;\n const shortAnswerRatio = shortAnswerCount / totalPages;\n const answerFirstRatio = answerFirstCount / totalPages;\n const entityFirstRatio = entityFirstCount / totalPages;\n\n // Short answer block: 70%+ pages: 4 pts\n if (shortAnswerRatio >= 0.7) {\n score += 4;\n findings.push({ severity: 'info', detail: `${Math.round(shortAnswerRatio * 100)}% of pages have a short answer block (40-80 words) early` });\n } else if (shortAnswerRatio >= 0.3) {\n score += 2;\n findings.push({ severity: 'low', detail: `${Math.round(shortAnswerRatio * 100)}% of pages have an early answer block`, fix: 'Add a concise 40-80 word answer paragraph in the first 300 words of each page' });\n } else {\n findings.push({ severity: 'medium', detail: 'Few pages have an early answer block', fix: 'Place a 40-80 word summary paragraph near the top of each page for AI snippet extraction' });\n }\n\n // First-paragraph answer check: 70%+ answer-first: 3 pts\n if (answerFirstRatio >= 0.7) {\n score += 3;\n findings.push({ severity: 'info', detail: `${Math.round(answerFirstRatio * 100)}% of pages open with a direct answer (no throat-clearing)` });\n } else if (answerFirstRatio >= 0.4) {\n score += 1;\n findings.push({ severity: 'low', detail: `${Math.round(answerFirstRatio * 100)}% of pages open with a direct answer`, fix: 'Avoid openers like \"In today\\'s world...\" - start with the answer' });\n } else {\n findings.push({ severity: 'medium', detail: 'Most pages use throat-clearing openers instead of direct answers', fix: 'Replace \"In this article...\" with a direct answer to the page\\'s main question' });\n }\n\n // Entity in first sentence: 80%+ pages: 3 pts\n if (entityFirstRatio >= 0.8) {\n score += 3;\n findings.push({ severity: 'info', detail: `${Math.round(entityFirstRatio * 100)}% of pages mention the primary entity in the first sentence` });\n } else if (entityFirstRatio >= 0.5) {\n score += 1;\n findings.push({ severity: 'low', detail: `${Math.round(entityFirstRatio * 100)}% mention the primary entity in the first sentence`, fix: 'Include the H1 topic in the first body sentence for AI extraction' });\n } else {\n findings.push({ severity: 'low', detail: 'Few pages mention the primary entity in the opening sentence', fix: 'Start content with a sentence that names the page topic explicitly' });\n }\n\n return { criterion: 'answer_first_placement', criterion_label: 'Answer-First Placement', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 31: Evidence Packaging ──────────────────────────────────────────\n\nfunction checkEvidencePackaging(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'evidence_packaging', criterion_label: 'Evidence Packaging', score: 0, status: 'not_found', findings, fix_priority: 'P1' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const pageCount = Math.max(1, (data.blogSample?.length ?? 0) + 1);\n let score = 0;\n\n // Inline citations: <a> inside <p> linking to external domains\n const paragraphs = combinedHtml.match(/<p[^>]*>[\\s\\S]*?<\\/p>/gi) || [];\n let inlineCitations = 0;\n const domain = data.domain.replace(/^www\\./, '').toLowerCase();\n for (const p of paragraphs) {\n const links = p.match(/<a[^>]*href=[\"'](https?:\\/\\/[^\"']+)[\"'][^>]*>/gi) || [];\n for (const link of links) {\n const href = link.match(/href=[\"'](https?:\\/\\/[^\"']+)[\"']/i);\n if (href) {\n try {\n const linkDomain = new URL(href[1]).hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== domain) inlineCitations++;\n } catch { /* ignore */ }\n }\n }\n }\n const avgCitations = inlineCitations / pageCount;\n if (avgCitations >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `${inlineCitations} inline citations to external sources (avg ${avgCitations.toFixed(1)}/page)` });\n } else if (avgCitations >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${inlineCitations} inline citations found`, fix: 'Add more inline links to authoritative external sources within paragraphs' });\n } else {\n findings.push({ severity: 'medium', detail: 'No inline citations to external sources', fix: 'Link to authoritative sources within your content paragraphs to strengthen evidence' });\n }\n\n // Sources section: heading matching \"source|reference|citation|bibliography\"\n const sourcesHeading = combinedHtml.match(/<h[2-4][^>]*>[^<]*(source|reference|citation|bibliography)[^<]*<\\/h[2-4]>/gi) || [];\n const sourceSections = sourcesHeading.length;\n if (sourceSections > 0) {\n const ratio = sourceSections / pageCount;\n if (ratio >= 0.5) {\n score += 2;\n findings.push({ severity: 'info', detail: `Sources/References section found on ${sourceSections} page(s)` });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: `Sources section found on ${sourceSections} page(s)`, fix: 'Add a Sources or References section to more content pages' });\n }\n } else {\n findings.push({ severity: 'low', detail: 'No Sources/References section found', fix: 'Add a \"Sources\" heading at the end of key articles' });\n }\n\n // Attribution phrases\n const attributionPhrases = text.match(/\\baccording\\s+to\\s+[A-Z]|\\b[A-Z][a-z]+\\s+(found|says|reports|notes|states|estimates|suggests)\\b/gi) || [];\n const avgAttribution = attributionPhrases.length / pageCount;\n if (avgAttribution >= 3) {\n score += 3;\n findings.push({ severity: 'info', detail: `${attributionPhrases.length} attribution phrases found (avg ${avgAttribution.toFixed(1)}/page)` });\n } else if (avgAttribution >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${attributionPhrases.length} attribution phrases found`, fix: 'Add more \"According to [Source]...\" attribution for credibility' });\n } else {\n findings.push({ severity: 'medium', detail: 'No attribution phrases found', fix: 'Use \"According to [Name]...\" and \"[Expert] reports...\" to attribute claims' });\n }\n\n // Sourced statistics: number+unit in sentences with source name\n const sourcedStats = text.match(/\\d+(\\.\\d+)?(%|\\s*(million|billion|thousand|percent))\\b[^.]*\\b[A-Z][a-z]+\\b/gi) || [];\n const avgSourcedStats = sourcedStats.length / pageCount;\n if (avgSourcedStats >= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: `${sourcedStats.length} sourced statistics found` });\n } else if (avgSourcedStats >= 1) {\n score += 1;\n findings.push({ severity: 'low', detail: `${sourcedStats.length} sourced statistics found`, fix: 'Pair more statistics with named sources' });\n }\n\n return { criterion: 'evidence_packaging', criterion_label: 'Evidence Packaging', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 32: Entity Disambiguation ───────────────────────────────────────\n\nfunction checkEntityDisambiguation(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'entity_disambiguation', criterion_label: 'Entity Disambiguation', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const pages: string[] = [stripScripts(data.homepage.text)];\n if (data.blogSample) {\n for (const p of data.blogSample) {\n pages.push(stripScripts(p.text));\n }\n }\n\n let score = 0;\n let definedEarlyCount = 0;\n let consistentCount = 0;\n let disambiguationCount = 0;\n\n for (const pageHtml of pages) {\n const text = pageHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n const h1Match = pageHtml.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (!h1Match) continue;\n\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n // Extract primary noun from H1 (longest word or multi-word phrase)\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3);\n const primaryNoun = h1Words.sort((a, b) => b.length - a.length)[0] || '';\n if (!primaryNoun) continue;\n\n // Primary entity defined early (first 500 chars)\n const first500 = text.slice(0, 500);\n if (new RegExp(`\\\\b${primaryNoun.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b[^.]*\\\\b(is|refers|defined|means)\\\\b`, 'i').test(first500)) {\n definedEarlyCount++;\n }\n\n // Entity consistency: primary H1 noun used consistently (same term 70%+)\n const primaryNounLower = primaryNoun.toLowerCase();\n const primaryRegex = new RegExp(`\\\\b${primaryNounLower.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b`, 'gi');\n const mentions = text.match(primaryRegex) || [];\n if (mentions.length >= 3) {\n consistentCount++;\n }\n\n // Disambiguation signals\n if (/\\bunlike\\s+\\w/i.test(text) || /\\bcompared\\s+to\\s+\\w/i.test(text) || /\\bnot\\s+to\\s+be\\s+confused\\s+with\\b/i.test(text) || /\\bas\\s+opposed\\s+to\\b/i.test(text)) {\n disambiguationCount++;\n }\n }\n\n const totalPages = pages.length;\n\n // Primary entity defined early: 70%+ pages: 4 pts\n const definedRatio = definedEarlyCount / totalPages;\n if (definedRatio >= 0.7) {\n score += 4;\n findings.push({ severity: 'info', detail: `${Math.round(definedRatio * 100)}% of pages define the primary entity early` });\n } else if (definedRatio >= 0.3) {\n score += 2;\n findings.push({ severity: 'low', detail: `${Math.round(definedRatio * 100)}% of pages define the primary entity early`, fix: 'Define the main topic within the first 500 characters of each page' });\n } else {\n findings.push({ severity: 'medium', detail: 'Few pages define the primary entity early', fix: 'Start each page with a clear definition of the main topic' });\n }\n\n // Entity consistency: 70%+ pages using consistent terminology: 3 pts\n const consistentRatio = consistentCount / totalPages;\n if (consistentRatio >= 0.7) {\n score += 3;\n findings.push({ severity: 'info', detail: `${Math.round(consistentRatio * 100)}% of pages use consistent entity terminology` });\n } else if (consistentRatio >= 0.3) {\n score += 1;\n findings.push({ severity: 'low', detail: `${Math.round(consistentRatio * 100)}% of pages use consistent terminology`, fix: 'Use the same primary term consistently instead of varying synonyms' });\n } else {\n findings.push({ severity: 'low', detail: 'Low entity terminology consistency across pages', fix: 'Repeat the primary entity term consistently throughout each page' });\n }\n\n // Disambiguation signals: 30%+ pages: 3 pts\n const disambigRatio = disambiguationCount / totalPages;\n if (disambigRatio >= 0.3) {\n score += 3;\n findings.push({ severity: 'info', detail: `${disambiguationCount} page(s) use disambiguation signals (\"unlike X\", \"compared to\")` });\n } else if (disambiguationCount > 0) {\n score += 1;\n findings.push({ severity: 'low', detail: `${disambiguationCount} page(s) use disambiguation signals`, fix: 'Add \"unlike X\" or \"compared to Y\" to clarify entity boundaries for AI' });\n } else {\n findings.push({ severity: 'low', detail: 'No disambiguation signals found', fix: 'Add \"unlike X\" or \"not to be confused with Y\" to help AI engines distinguish your topics' });\n }\n\n return { criterion: 'entity_disambiguation', criterion_label: 'Entity Disambiguation', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 33: Extraction Friction Score ───────────────────────────────────\n\nfunction checkExtractionFriction(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'extraction_friction', criterion_label: 'Extraction Friction Score', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n const text = combinedHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ');\n let score = 0;\n\n // Avg sentence length in answer paragraphs\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 5);\n const sentenceLengths = sentences.map(s => s.trim().split(/\\s+/).length);\n const avgSentenceLen = sentenceLengths.length > 0 ? sentenceLengths.reduce((a, b) => a + b, 0) / sentenceLengths.length : 0;\n\n if (avgSentenceLen > 0 && avgSentenceLen < 20) {\n score += 3;\n findings.push({ severity: 'info', detail: `Average sentence length: ${avgSentenceLen.toFixed(1)} words - excellent for AI extraction` });\n } else if (avgSentenceLen >= 20 && avgSentenceLen <= 25) {\n score += 2;\n findings.push({ severity: 'info', detail: `Average sentence length: ${avgSentenceLen.toFixed(1)} words - good` });\n } else if (avgSentenceLen > 25 && avgSentenceLen <= 35) {\n score += 1;\n findings.push({ severity: 'low', detail: `Average sentence length: ${avgSentenceLen.toFixed(1)} words - slightly long`, fix: 'Shorten sentences to under 25 words for easier AI extraction' });\n } else if (avgSentenceLen > 35) {\n findings.push({ severity: 'medium', detail: `Average sentence length: ${avgSentenceLen.toFixed(1)} words - too long for AI snippets`, fix: 'Break long sentences into shorter, single-claim statements' });\n }\n\n // Voice-friendly lead: first 2-3 sentences after H1 <75 words, active voice, no parentheticals\n const pages: string[] = [stripScripts(data.homepage.text)];\n if (data.blogSample) {\n for (const p of data.blogSample) pages.push(stripScripts(p.text));\n }\n\n let voiceFriendlyCount = 0;\n for (const pageHtml of pages) {\n const h1Match = pageHtml.match(/<h1[^>]*>[\\s\\S]*?<\\/h1>/i);\n if (!h1Match) continue;\n const afterH1 = pageHtml.slice(pageHtml.indexOf(h1Match[0]) + h1Match[0].length);\n const leadParas = afterH1.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 3) || [];\n const leadText = leadParas.map(p => p.replace(/<[^>]*>/g, '')).join(' ').trim();\n const words = leadText.split(/\\s+/).length;\n const hasParentheticals = /\\([^)]+\\)/.test(leadText);\n if (words <= 75 && words > 10 && !hasParentheticals) {\n voiceFriendlyCount++;\n }\n }\n const voiceFriendlyRatio = voiceFriendlyCount / pages.length;\n if (voiceFriendlyRatio >= 0.7) {\n score += 3;\n findings.push({ severity: 'info', detail: `${Math.round(voiceFriendlyRatio * 100)}% of pages have voice-friendly lead paragraphs` });\n } else if (voiceFriendlyRatio >= 0.3) {\n score += 1;\n findings.push({ severity: 'low', detail: `${Math.round(voiceFriendlyRatio * 100)}% have voice-friendly leads`, fix: 'Keep the first 2-3 sentences under 75 words total, using active voice with no parentheticals' });\n } else {\n findings.push({ severity: 'low', detail: 'Few pages have voice-friendly lead paragraphs', fix: 'Write concise opening paragraphs without parentheticals for voice assistant compatibility' });\n }\n\n // Jargon density: 4+ syllable words in first 500 words without inline definition\n const first500Words = text.split(/\\s+/).slice(0, 500).join(' ');\n const longWords = first500Words.match(/\\b[a-z]{10,}\\b/gi) || [];\n const avgJargon = longWords.length / Math.max(1, pages.length);\n if (avgJargon <= 2) {\n score += 2;\n findings.push({ severity: 'info', detail: 'Low jargon density in opening content' });\n } else if (avgJargon <= 5) {\n score += 1;\n findings.push({ severity: 'low', detail: 'Moderate jargon in opening content', fix: 'Define technical terms inline when first used' });\n } else {\n findings.push({ severity: 'low', detail: 'High jargon density in opening content', fix: 'Replace or define complex terms to reduce extraction friction' });\n }\n\n // Hidden content penalty\n const hasHiddenContent = /display\\s*:\\s*none|visibility\\s*:\\s*hidden|\\bhidden\\b/i.test(combinedHtml) && /<(div|section|p)[^>]*(?:style=[\"'][^\"']*display\\s*:\\s*none|hidden)[^>]*>/i.test(combinedHtml);\n if (hasHiddenContent) {\n score = Math.max(0, score - 2);\n findings.push({ severity: 'medium', detail: 'Hidden content detected (display:none or hidden attribute)', fix: 'Make all content visible - hidden content is not extractable by AI engines' });\n }\n\n return { criterion: 'extraction_friction', criterion_label: 'Extraction Friction Score', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 34: Image Context for AI ────────────────────────────────────────\n\nfunction checkImageContextAI(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n if (!data.homepage) {\n findings.push({ severity: 'critical', detail: 'Could not fetch homepage' });\n return { criterion: 'image_context_ai', criterion_label: 'Image Context for AI', score: 0, status: 'not_found', findings, fix_priority: 'P2' };\n }\n\n const combinedHtml = getCombinedHtml(data);\n let score = 0;\n\n // Count images\n const allImages = combinedHtml.match(/<img\\s[^>]*>/gi) || [];\n if (allImages.length === 0) {\n findings.push({ severity: 'low', detail: 'No images found on pages' });\n return { criterion: 'image_context_ai', criterion_label: 'Image Context for AI', score: 5, status: 'partial', findings, fix_priority: 'P3' };\n }\n\n // <figure> + <figcaption>: 50%+ images: 4 pts\n const figureBlocks = combinedHtml.match(/<figure[\\s\\S]*?<\\/figure>/gi) || [];\n const figuresWithCaption = figureBlocks.filter(f => /<figcaption/i.test(f));\n const figureRatio = figuresWithCaption.length / allImages.length;\n if (figureRatio >= 0.5) {\n score += 4;\n findings.push({ severity: 'info', detail: `${figuresWithCaption.length}/${allImages.length} images use <figure> + <figcaption>` });\n } else if (figureRatio >= 0.25) {\n score += 2;\n findings.push({ severity: 'low', detail: `${figuresWithCaption.length}/${allImages.length} images use <figure> + <figcaption>`, fix: 'Wrap more images in <figure> with <figcaption> for AI context' });\n } else {\n findings.push({ severity: 'medium', detail: 'Few or no images use <figure> + <figcaption>', fix: 'Wrap images in <figure> elements with descriptive <figcaption> tags' });\n }\n\n // Alt text quality: alt >5 words, descriptive (not \"image\"/\"photo\")\n let goodAltCount = 0;\n for (const img of allImages) {\n const altMatch = img.match(/\\salt=[\"']([^\"']+)[\"']/i);\n if (altMatch) {\n const altText = altMatch[1].trim();\n const words = altText.split(/\\s+/).length;\n const isGeneric = /^(image|photo|picture|img|icon|logo|banner|screenshot)$/i.test(altText);\n if (words > 5 && !isGeneric) {\n goodAltCount++;\n }\n }\n }\n const altQualityRatio = goodAltCount / allImages.length;\n if (altQualityRatio >= 0.5) {\n score += 3;\n findings.push({ severity: 'info', detail: `${goodAltCount}/${allImages.length} images have descriptive alt text (>5 words)` });\n } else if (altQualityRatio >= 0.25) {\n score += 1;\n findings.push({ severity: 'low', detail: `${goodAltCount}/${allImages.length} images have quality alt text`, fix: 'Write descriptive alt text (>5 words) that explains the image content' });\n } else {\n findings.push({ severity: 'medium', detail: 'Most images lack descriptive alt text', fix: 'Add descriptive alt text (>5 words) to all images, avoiding generic terms like \"image\" or \"photo\"' });\n }\n\n // Contextual placement: images within <article>/<section>\n const contextualImages = combinedHtml.match(/<(article|section)[^>]*>[\\s\\S]*?<img[^>]*>[\\s\\S]*?<\\/\\1>/gi) || [];\n if (contextualImages.length > 0) {\n const contextRatio = Math.min(contextualImages.length, allImages.length) / allImages.length;\n if (contextRatio >= 0.5) {\n score += 3;\n findings.push({ severity: 'info', detail: 'Images are well-placed within semantic content sections' });\n } else {\n score += 1;\n findings.push({ severity: 'low', detail: 'Some images placed within content sections', fix: 'Place images within <article> or <section> elements for better AI context' });\n }\n } else {\n findings.push({ severity: 'low', detail: 'Images not placed within semantic content sections', fix: 'Move images inside <article> or <section> elements' });\n }\n\n return { criterion: 'image_context_ai', criterion_label: 'Image Context for AI', score: Math.min(10, score), status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P2' };\n}\n\n// ─── Criterion 39: Duplicate Content Blocks (intra-page) ─────────────────────\n\ninterface DuplicatePair { headingA: string; headingB: string; similarity: number; sample: string }\n\n/** Find duplicate paragraphs across different sections within a single page. */\nfunction findIntraPageDuplicates(html: string): DuplicatePair[] {\n const sections = extractDuplicateContentSections(html);\n if (sections.length < 2) return [];\n\n const pairs: DuplicatePair[] = [];\n for (let i = 0; i < sections.length; i++) {\n for (let j = i + 1; j < sections.length; j++) {\n let found = false;\n for (const pA of sections[i].paragraphs) {\n if (found) break;\n for (const pB of sections[j].paragraphs) {\n const sim = shingleJaccardSimilarity(pA.shingles, pB.shingles);\n if (sim > 0.4) {\n pairs.push({\n headingA: sections[i].heading,\n headingB: sections[j].heading,\n similarity: Math.round(sim * 100),\n sample: pA.text.slice(0, 80),\n });\n found = true;\n break;\n }\n }\n }\n }\n }\n return pairs;\n}\n\nfunction checkDuplicateContent(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n const pages: Array<{ html: string; url: string }> = [];\n if (data.homepage) {\n pages.push({ html: data.homepage.text, url: data.homepage.finalUrl || `https://${data.domain}/` });\n }\n if (data.blogSample) {\n for (const page of data.blogSample) {\n pages.push({ html: page.text, url: page.finalUrl || '' });\n }\n }\n\n if (pages.length === 0) {\n findings.push({ severity: 'critical', detail: 'No pages available for duplicate content analysis' });\n return { criterion: 'duplicate_content', criterion_label: 'Duplicate Content Blocks', score: 0, status: 'fail', findings, fix_priority: 'P1' };\n }\n\n // Analyze each page for intra-page duplicates\n let totalDupPages = 0;\n let totalDupPairs = 0;\n const dupDetails: Array<{ url: string; pairs: DuplicatePair[] }> = [];\n\n for (const page of pages) {\n const pairs = findIntraPageDuplicates(page.html);\n if (pairs.length > 0) {\n totalDupPages++;\n totalDupPairs += pairs.length;\n dupDetails.push({ url: page.url, pairs });\n }\n }\n\n const dupRatio = totalDupPages / pages.length;\n\n let score: number;\n if (totalDupPairs === 0) {\n score = 10;\n findings.push({ severity: 'info', detail: `${pages.length} pages analyzed - no duplicate content blocks detected` });\n } else if (dupRatio <= 0.05 && totalDupPairs <= 2) {\n score = 9;\n findings.push({ severity: 'info', detail: `${totalDupPairs} duplicate block pair(s) on ${totalDupPages} page(s) - minor` });\n } else if (dupRatio <= 0.10) {\n score = 7;\n findings.push({ severity: 'low', detail: `${totalDupPairs} duplicate block pair(s) across ${totalDupPages} page(s)`, fix: 'Rewrite duplicate sections to provide unique content in each' });\n } else if (dupRatio <= 0.20) {\n score = 5;\n findings.push({ severity: 'medium', detail: `${totalDupPages} pages (${Math.round(dupRatio * 100)}%) contain duplicate content blocks`, fix: 'Rewrite or remove repeated text blocks - LLMs may flag this as low-quality content' });\n } else if (dupRatio <= 0.40) {\n score = 3;\n findings.push({ severity: 'medium', detail: `${totalDupPages} pages (${Math.round(dupRatio * 100)}%) have significant duplicate content`, fix: 'Widespread duplicate blocks reduce content authority - rewrite each section with unique angles' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${totalDupPages} pages (${Math.round(dupRatio * 100)}%) contain duplicate content blocks`, fix: 'Severe content duplication across the site - LLMs will likely reduce citation authority' });\n }\n\n // Show up to 3 affected pages with section names and sample text\n for (const dup of dupDetails.slice(0, 3)) {\n const shortUrl = dup.url.slice(0, 60);\n for (const pair of dup.pairs.slice(0, 2)) {\n findings.push({\n severity: 'low',\n detail: `${shortUrl}: '${pair.headingA}' and '${pair.headingB}' share ${pair.similarity}% similar text (\"${pair.sample}...\")`,\n fix: `Rewrite one of these sections to eliminate duplicate content`,\n });\n }\n }\n\n return { criterion: 'duplicate_content', criterion_label: 'Duplicate Content Blocks', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Criterion 40: Cross-Page Duplicate Content ──────────────────────────────\n\nfunction checkCrossPageDuplication(data: SiteData): CriterionResult {\n const findings: AuditFinding[] = [];\n\n const pages: Array<{ url: string; paragraphs: DuplicateContentParagraph[] }> = [];\n if (data.homepage) {\n pages.push({ url: data.homepage.finalUrl || `https://${data.domain}/`, paragraphs: extractDuplicateContentParagraphs(data.homepage.text) });\n }\n if (data.blogSample) {\n for (const page of data.blogSample) {\n pages.push({ url: page.finalUrl || '', paragraphs: extractDuplicateContentParagraphs(page.text) });\n }\n }\n\n if (pages.length <= 1) {\n findings.push({ severity: 'info', detail: 'Not enough pages to assess cross-page duplication' });\n return { criterion: 'cross_page_duplication', criterion_label: 'Cross-Page Duplicate Content', score: 5, status: 'partial', findings, fix_priority: 'P3' };\n }\n\n // Identify site-wide boilerplate: paragraphs appearing on >40% of pages (by shingle fingerprint)\n // These are template elements (CTAs, bios, footers) — not duplication problems\n const paragraphPageCount = new Map<string, number>();\n for (const page of pages) {\n const seen = new Set<string>();\n for (const p of page.paragraphs) {\n // Use first 5 shingles as fingerprint (fast approximation)\n const fp = [...p.shingles].slice(0, 5).join('|');\n if (!seen.has(fp)) {\n seen.add(fp);\n paragraphPageCount.set(fp, (paragraphPageCount.get(fp) || 0) + 1);\n }\n }\n }\n const boilerplateThreshold = Math.max(3, pages.length * 0.4);\n const siteBoilerprints = new Set<string>();\n for (const [fp, count] of paragraphPageCount) {\n if (count >= boilerplateThreshold) siteBoilerprints.add(fp);\n }\n\n // Compare paragraphs across pages (excluding site boilerplate)\n const crossDupPairs: Array<{ urlA: string; urlB: string; dupCount: number; sample: string }> = [];\n\n for (let i = 0; i < pages.length; i++) {\n for (let j = i + 1; j < pages.length; j++) {\n let dupCount = 0;\n let sample = '';\n for (const pA of pages[i].paragraphs) {\n const fpA = [...pA.shingles].slice(0, 5).join('|');\n if (siteBoilerprints.has(fpA)) continue;\n for (const pB of pages[j].paragraphs) {\n const sim = shingleJaccardSimilarity(pA.shingles, pB.shingles);\n if (sim > 0.4) {\n dupCount++;\n if (!sample) sample = pA.text.slice(0, 80);\n break;\n }\n }\n }\n if (dupCount >= 2) {\n crossDupPairs.push({\n urlA: pages[i].url.slice(0, 60),\n urlB: pages[j].url.slice(0, 60),\n dupCount,\n sample,\n });\n }\n }\n }\n\n // Count unique URLs involved\n const affectedUrls = new Set<string>();\n for (const pair of crossDupPairs) {\n affectedUrls.add(pair.urlA);\n affectedUrls.add(pair.urlB);\n }\n const affectedRatio = pages.length > 0 ? affectedUrls.size / pages.length : 0;\n const totalDupParagraphs = crossDupPairs.reduce((s, p) => s + p.dupCount, 0);\n\n let score: number;\n if (crossDupPairs.length === 0) {\n score = 10;\n findings.push({ severity: 'info', detail: `${pages.length} pages analyzed - no cross-page content duplication detected` });\n } else if (affectedRatio <= 0.05 && totalDupParagraphs <= 4) {\n score = 9;\n findings.push({ severity: 'info', detail: `${totalDupParagraphs} shared paragraph(s) across ${affectedUrls.size} page(s) - minor` });\n } else if (affectedRatio <= 0.10) {\n score = 7;\n findings.push({ severity: 'low', detail: `${totalDupParagraphs} shared paragraphs across ${affectedUrls.size} pages`, fix: 'Rewrite shared content so each page provides a unique perspective' });\n } else if (affectedRatio <= 0.20) {\n score = 5;\n findings.push({ severity: 'medium', detail: `${affectedUrls.size} pages (${Math.round(affectedRatio * 100)}%) share duplicate paragraphs`, fix: 'Significant cross-page duplication - AI engines may only index one version' });\n } else if (affectedRatio <= 0.40) {\n score = 3;\n findings.push({ severity: 'medium', detail: `${affectedUrls.size} pages (${Math.round(affectedRatio * 100)}%) contain shared content blocks`, fix: 'Widespread copy-paste content across pages reduces overall site authority' });\n } else {\n score = 0;\n findings.push({ severity: 'high', detail: `${affectedUrls.size} pages (${Math.round(affectedRatio * 100)}%) share duplicate content`, fix: 'Severe cross-page duplication - AI engines will likely ignore redundant pages entirely' });\n }\n\n // Show up to 3 cross-page duplicate pairs with sample text\n for (const pair of crossDupPairs.slice(0, 3)) {\n findings.push({\n severity: 'low',\n detail: `${pair.dupCount} shared paragraph(s): ${pair.urlA} ↔ ${pair.urlB} (\"${pair.sample}...\")`,\n fix: 'Rewrite shared paragraphs so each page has unique content',\n });\n }\n\n return { criterion: 'cross_page_duplication', criterion_label: 'Cross-Page Duplicate Content', score, status: score >= 7 ? 'pass' : score >= 4 ? 'partial' : 'fail', findings, fix_priority: score >= 7 ? 'P3' : 'P1' };\n}\n\n// ─── Main audit function ────────────────────────────────────────────────────\n\n/**\n * Run all 40 criteria checks using pre-fetched site data.\n * All functions are synchronous (no HTTP calls) - data was already fetched.\n */\nexport function auditSiteFromData(data: SiteData): CriterionResult[] {\n // Compute Topic Coherence first - used to cap Content Cannibalization\n const topicCoherence = checkTopicCoherence(data);\n const cannibalization = checkContentCannibalization(data, topicCoherence.score);\n\n return [\n checkLlmsTxt(data),\n checkSchemaMarkup(data),\n checkQAFormat(data),\n checkCleanHTML(data),\n checkEntityConsistency(data),\n checkRobotsTxt(data),\n checkFAQSection(data),\n checkOriginalData(data),\n checkInternalLinking(data),\n checkSemanticHTML(data),\n checkContentFreshness(data),\n checkSitemapCompleteness(data),\n checkRssFeed(data),\n checkTableListExtractability(data),\n checkDefinitionPatterns(data),\n checkDirectAnswerDensity(data),\n checkContentLicensing(data),\n checkAuthorSchemaDepth(data),\n checkFactDensity(data),\n checkCanonicalUrl(data),\n checkContentVelocity(data),\n checkSchemaCoverage(data),\n checkSpeakableSchema(data),\n checkQueryAnswerAlignment(data),\n cannibalization,\n checkVisibleDateSignal(data),\n topicCoherence,\n checkContentDepth(data, topicCoherence.score),\n // Helpful-content criteria (#29-#32)\n checkHelpfulPurposeAlignment(data),\n checkFirstHandExperienceSignals(data),\n checkCreatorTransparency(data),\n checkMethodologyTransparency(data),\n // V2 criteria (#33-#38)\n checkCitationReadyWriting(data),\n checkAnswerFirstPlacement(data),\n checkEvidencePackaging(data),\n checkEntityDisambiguation(data),\n checkExtractionFriction(data),\n checkImageContextAI(data),\n // V3 criteria (#39-#40)\n checkDuplicateContent(data),\n checkCrossPageDuplication(data),\n ];\n}\n\n/**\n * Legacy entry point: fetches data and runs all checks.\n * Used by analyzer.ts for the /api/aeo/analyze endpoint.\n */\nexport async function auditSite(targetUrl: string): Promise<CriterionResult[]> {\n const normalizedTarget = targetUrl.startsWith('http') ? targetUrl : `https://${targetUrl}`;\n if (!(await isSafeFetchTarget(normalizedTarget))) {\n throw new Error(`Refusing to audit private or local address: ${targetUrl}`);\n }\n const url = new URL(normalizedTarget);\n const domain = url.hostname.replace(/^www\\./, '');\n const data = await prefetchSiteData(domain);\n return auditSiteFromData(data);\n}\n","import type { CriterionResult } from './site-crawler.js';\n\nexport const WEIGHTS: Record<string, number> = {\n // ─── Content Substance (~55%) ─────────────────────────────────────────────\n // WHY an AI engine would cite you. These drive citation quality directly.\n topic_coherence: 0.14, // Topical authority - THE gating signal\n original_data: 0.10, // Unique value AI can't find elsewhere\n content_depth: 0.07, // Comprehensive vs thin coverage\n fact_density: 0.06, // Information density per page\n direct_answer_density: 0.05, // Direct answers to queries\n qa_content_format: 0.04, // Answer-shaped content structure\n query_answer_alignment: 0.04, // Relevance to actual AI queries\n faq_section: 0.03, // Structured Q&A pairs\n helpful_purpose_alignment: 0.03, // Visitor-helpful vs search-first framing\n first_hand_experience_signals: 0.03, // Evidence of real use or observation\n\n // ─── Content Organization (~30%) ──────────────────────────────────────────\n // HOW easily AI engines can extract and trust your content.\n entity_consistency: 0.05, // Brand authority and E-E-A-T\n internal_linking: 0.04, // Site structure and topic clusters\n content_freshness: 0.04, // Recency signals\n schema_markup: 0.03, // Structured data for discovery\n author_schema_depth: 0.03, // Expert attribution\n table_list_extractability: 0.03, // Extractable structured data\n creator_transparency: 0.02, // Visible author/reviewer clarity\n methodology_transparency: 0.02, // Process disclosure\n definition_patterns: 0.015, // Clear definitions\n visible_date_signal: 0.015, // Publication date trust\n semantic_html: 0.02, // Clean semantic structure\n clean_html: 0.02, // Parseable markup\n\n // ─── Technical Plumbing (~15%) ────────────────────────────────────────────\n // WHETHER AI crawlers can find you. Table stakes with diminishing returns.\n content_cannibalization: 0.02,\n llms_txt: 0.01,\n robots_txt: 0.01,\n content_velocity: 0.02,\n content_licensing: 0.01,\n sitemap_completeness: 0.01,\n canonical_url: 0.005,\n rss_feed: 0.005,\n schema_coverage: 0.005,\n speakable_schema: 0.005,\n\n // ─── V2 Criteria (~15%) ───────────────────────────────────────────────────\n // Citation quality, evidence packaging, and extraction friction.\n citation_ready_writing: 0.04, // Self-contained quotable sentences\n answer_first_placement: 0.03, // Answer block in first 300 words\n evidence_packaging: 0.03, // Inline citations and attribution\n entity_disambiguation: 0.02, // Clear entity boundaries\n extraction_friction: 0.02, // Sentence length, voice, jargon\n image_context_ai: 0.005, // Figure/figcaption, alt text quality\n\n // ─── V3 Criteria ────────────────────────────────────────────────────────\n duplicate_content: 0.05, // Duplicate text blocks within pages\n cross_page_duplication: 0.03, // Same paragraphs copied across pages\n};\n\nexport function calculateOverallScore(criteria: CriterionResult[]): number {\n let totalWeight = 0;\n let weightedSum = 0;\n\n for (const c of criteria) {\n const weight = WEIGHTS[c.criterion] ?? 0.05;\n weightedSum += (c.score / 10) * weight * 100;\n totalWeight += weight;\n }\n\n if (totalWeight === 0) return 0;\n\n let score = Math.round(weightedSum / totalWeight);\n\n // ─── Coherence Gate ─────────────────────────────────────────────────────\n // Topic coherence is a multiplicative signal, not just additive. A site\n // with scattered, unfocused content cannot achieve a high AEO score\n // regardless of technical perfection - AI engines won't build topical\n // trust from a grab-bag of unrelated posts.\n //\n // Thresholds map to score tiers:\n // coherence 0-1: max 35-40 (Critical gaps)\n // coherence 2-4: max 45-55 (Below average)\n // coherence 5: max 60 (Moderate ceiling)\n // coherence ≥ 6: no cap\n const coherence = criteria.find(c => c.criterion === 'topic_coherence');\n if (coherence && coherence.score < 6) {\n const cap = 35 + coherence.score * 5;\n score = Math.min(score, cap);\n }\n\n return score;\n}\n","/**\n * V2 Pillar Framework — 5-pillar scoring model.\n * Maps all 40 criteria into pillars, computes sub-scores,\n * provides client-friendly names, and calculates top-3 fixes.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface PillarScores {\n answerReadiness: number; // ~40%\n contentStructure: number; // ~25%\n trustAuthority: number; // ~15%\n technicalFoundation: number; // ~10%\n aiDiscovery: number; // ~10%\n}\n\nexport interface TopFix {\n criterion: string;\n clientName: string;\n description: string;\n impactPoints: number; // (10 - score) * weight * 100\n effort: 'Low' | 'Medium' | 'High';\n}\n\n// ─── Pillar definitions ─────────────────────────────────────────────────────\n\nexport type PillarName = 'Answer Readiness' | 'Content Structure' | 'Trust & Authority' | 'Technical Foundation' | 'AI Discovery';\n\nexport const PILLARS: Record<PillarName, string[]> = {\n 'Answer Readiness': [\n 'topic_coherence', 'original_data', 'content_depth', 'fact_density',\n 'citation_ready_writing', 'answer_first_placement', 'evidence_packaging',\n 'helpful_purpose_alignment', 'first_hand_experience_signals',\n 'duplicate_content', 'cross_page_duplication',\n ],\n 'Content Structure': [\n 'direct_answer_density', 'qa_content_format', 'query_answer_alignment',\n 'faq_section', 'table_list_extractability', 'definition_patterns', 'entity_disambiguation',\n ],\n 'Trust & Authority': [\n 'entity_consistency', 'internal_linking', 'content_freshness',\n 'author_schema_depth', 'schema_markup', 'creator_transparency',\n 'methodology_transparency',\n ],\n 'Technical Foundation': [\n 'semantic_html', 'clean_html', 'visible_date_signal',\n 'extraction_friction', 'image_context_ai', 'schema_coverage', 'speakable_schema',\n ],\n 'AI Discovery': [\n 'content_cannibalization', 'llms_txt', 'robots_txt', 'content_velocity',\n 'content_licensing', 'canonical_url', 'sitemap_completeness', 'rss_feed',\n ],\n};\n\n// ─── Client-friendly names ──────────────────────────────────────────────────\n\nexport const CLIENT_NAMES: Record<string, string> = {\n topic_coherence: 'Topical Authority',\n original_data: 'Original Research & Data',\n content_depth: 'Content Depth',\n fact_density: 'Fact & Data Density',\n citation_ready_writing: 'Citation-Ready Writing',\n answer_first_placement: 'Answer-First Placement',\n evidence_packaging: 'Evidence Packaging',\n helpful_purpose_alignment: 'Helpful Purpose Alignment',\n first_hand_experience_signals: 'First-Hand Experience Signals',\n direct_answer_density: 'Direct Answer Density',\n qa_content_format: 'Q&A Content Format',\n query_answer_alignment: 'Query-Answer Alignment',\n faq_section: 'FAQ Section',\n table_list_extractability: 'Tables & Lists',\n definition_patterns: 'Definition Patterns',\n entity_disambiguation: 'Entity Disambiguation',\n entity_consistency: 'Entity & Brand Authority',\n internal_linking: 'Internal Linking',\n content_freshness: 'Content Freshness',\n author_schema_depth: 'Author & Expert Schema',\n schema_markup: 'Schema Markup',\n creator_transparency: 'Creator Transparency',\n methodology_transparency: 'Methodology Transparency',\n semantic_html: 'Semantic HTML',\n clean_html: 'Clean HTML',\n visible_date_signal: 'Visible Date Signal',\n extraction_friction: 'Extraction Friction',\n image_context_ai: 'Image Context for AI',\n schema_coverage: 'Schema Coverage',\n speakable_schema: 'Speakable Schema',\n duplicate_content: 'Duplicate Content Blocks',\n cross_page_duplication: 'Cross-Page Duplicate Content',\n content_cannibalization: 'Content Cannibalization',\n llms_txt: 'llms.txt File',\n robots_txt: 'robots.txt for AI',\n content_velocity: 'Publishing Velocity',\n content_licensing: 'Content Licensing',\n canonical_url: 'Canonical URLs',\n sitemap_completeness: 'Sitemap Completeness',\n rss_feed: 'RSS/Atom Feed',\n};\n\n// ─── Pillar weights for sub-score computation ───────────────────────────────\n\nconst PILLAR_WEIGHTS: Record<string, number> = {\n topic_coherence: 0.14, original_data: 0.10, content_depth: 0.07,\n fact_density: 0.06, citation_ready_writing: 0.04, answer_first_placement: 0.03,\n evidence_packaging: 0.03, helpful_purpose_alignment: 0.03,\n first_hand_experience_signals: 0.03, duplicate_content: 0.05, cross_page_duplication: 0.03,\n direct_answer_density: 0.05, qa_content_format: 0.04, query_answer_alignment: 0.04,\n faq_section: 0.03, table_list_extractability: 0.03, definition_patterns: 0.015,\n entity_disambiguation: 0.02,\n entity_consistency: 0.05, internal_linking: 0.04, content_freshness: 0.04,\n author_schema_depth: 0.03, schema_markup: 0.03, creator_transparency: 0.02,\n methodology_transparency: 0.02, semantic_html: 0.02, clean_html: 0.02,\n visible_date_signal: 0.015, extraction_friction: 0.02, image_context_ai: 0.005,\n schema_coverage: 0.005, speakable_schema: 0.005,\n content_cannibalization: 0.02, llms_txt: 0.01, robots_txt: 0.01,\n content_velocity: 0.02, content_licensing: 0.01, canonical_url: 0.005,\n sitemap_completeness: 0.01, rss_feed: 0.005,\n};\n\n// ─── Effort mapping per criterion ───────────────────────────────────────────\n\nconst CRITERION_EFFORT: Record<string, TopFix['effort']> = {\n topic_coherence: 'High', original_data: 'High', content_depth: 'Medium',\n fact_density: 'Medium', citation_ready_writing: 'Medium', answer_first_placement: 'Medium',\n evidence_packaging: 'Medium', helpful_purpose_alignment: 'Medium',\n first_hand_experience_signals: 'Medium', duplicate_content: 'Medium', cross_page_duplication: 'Medium',\n direct_answer_density: 'Medium', qa_content_format: 'Medium', query_answer_alignment: 'Medium',\n faq_section: 'Medium', table_list_extractability: 'Medium', definition_patterns: 'Low',\n entity_disambiguation: 'Medium',\n entity_consistency: 'Low', internal_linking: 'Medium', content_freshness: 'Low',\n author_schema_depth: 'Low', schema_markup: 'Medium', creator_transparency: 'Low',\n methodology_transparency: 'Low',\n semantic_html: 'Low', clean_html: 'Medium', visible_date_signal: 'Low',\n extraction_friction: 'Medium', image_context_ai: 'Low', schema_coverage: 'Medium',\n speakable_schema: 'Low',\n content_cannibalization: 'Medium', llms_txt: 'Low', robots_txt: 'Low',\n content_velocity: 'High', content_licensing: 'Low', canonical_url: 'Low',\n sitemap_completeness: 'Low', rss_feed: 'Low',\n};\n\n// ─── Fix descriptions ───────────────────────────────────────────────────────\n\nconst FIX_DESCRIPTIONS: Record<string, string> = {\n topic_coherence: 'Focus blog content on 2-3 core expertise areas instead of scattered topics.',\n original_data: 'Publish original research, case studies, or proprietary data that AI engines can cite.',\n content_depth: 'Expand articles to 1000+ words with structured H2/H3 sections and expert analysis.',\n fact_density: 'Add specific numbers, percentages, and statistics throughout content.',\n citation_ready_writing: 'Write self-contained definition sentences and one-claim statements AI can quote directly.',\n answer_first_placement: 'Place a 40-80 word answer block in the first 300 words of each page.',\n evidence_packaging: 'Add inline citations, attribution phrases, and a sources section to key pages.',\n helpful_purpose_alignment: 'Lead with useful, task-solving guidance instead of search-first filler.',\n first_hand_experience_signals: 'Add concrete signs of first-hand use, testing, implementation, or observation.',\n direct_answer_density: 'Write concise 2-3 sentence answer paragraphs after each question heading.',\n qa_content_format: 'Add question-based H2/H3 headings matching common AI queries.',\n query_answer_alignment: 'Ensure every question heading is followed by a direct answer paragraph.',\n faq_section: 'Create a dedicated FAQ page with FAQPage schema markup.',\n table_list_extractability: 'Add comparison tables with headers and structured lists.',\n definition_patterns: 'Include clear \"X is defined as...\" patterns for key terms.',\n entity_disambiguation: 'Define the primary entity early and use consistent terminology.',\n entity_consistency: 'Add Organization schema with consistent name, address, and sameAs links.',\n internal_linking: 'Strengthen internal linking with descriptive anchor text between related pages.',\n content_freshness: 'Add dateModified schema and visible last-updated dates.',\n author_schema_depth: 'Add Person schema for authors with credentials and sameAs links.',\n schema_markup: 'Implement JSON-LD structured data on key pages.',\n creator_transparency: 'Show clear visible bylines, author pages, and reviewer details where readers expect them.',\n methodology_transparency: 'Explain how content was tested, researched, reviewed, or updated.',\n semantic_html: 'Use semantic HTML5 elements (main, article, nav, header, footer).',\n clean_html: 'Fix HTML structure, add meta tags, and ensure HTTPS.',\n visible_date_signal: 'Display dates using <time> elements and add datePublished to JSON-LD.',\n extraction_friction: 'Shorten sentences, use active voice, and avoid jargon in lead paragraphs.',\n image_context_ai: 'Wrap images in <figure>/<figcaption> with descriptive alt text.',\n schema_coverage: 'Extend structured data to inner pages (articles, services, products).',\n speakable_schema: 'Add SpeakableSpecification schema for voice assistant compatibility.',\n duplicate_content: 'Rewrite duplicate text blocks so each section provides unique value.',\n cross_page_duplication: 'Rewrite shared paragraphs across pages so each page has unique content.',\n content_cannibalization: 'Consolidate overlapping pages or differentiate titles and H1 headings.',\n llms_txt: 'Create a /llms.txt file describing your site for AI engines.',\n robots_txt: 'Update robots.txt to explicitly allow AI crawlers.',\n content_velocity: 'Establish a regular publishing cadence with dated sitemap entries.',\n content_licensing: 'Create /ai.txt to declare AI usage permissions.',\n canonical_url: 'Add rel=\"canonical\" tags to all pages.',\n sitemap_completeness: 'Generate a comprehensive sitemap.xml with lastmod dates.',\n rss_feed: 'Add an RSS feed linked from your homepage.',\n};\n\n// ─── Reverse lookup: criterion → pillar ─────────────────────────────────────\n\nconst CRITERION_PILLAR_MAP: Record<string, PillarName> = {};\nfor (const [pillar, criteria] of Object.entries(PILLARS)) {\n for (const c of criteria) {\n CRITERION_PILLAR_MAP[c] = pillar as PillarName;\n }\n}\n\nexport function getCriterionPillar(criterionId: string): string {\n return CRITERION_PILLAR_MAP[criterionId] || 'Unknown';\n}\n\n// ─── Pillar sub-score computation ───────────────────────────────────────────\n\nexport function computePillarScores(criteria: CriterionResult[]): PillarScores {\n const criteriaMap = new Map(criteria.map(c => [c.criterion, c]));\n\n function pillarScore(pillarCriteria: string[]): number {\n let totalWeight = 0;\n let weightedSum = 0;\n\n for (const id of pillarCriteria) {\n const c = criteriaMap.get(id);\n const weight = PILLAR_WEIGHTS[id] ?? 0.02;\n if (c) {\n weightedSum += (c.score / 10) * weight * 100;\n totalWeight += weight;\n }\n }\n\n return totalWeight === 0 ? 0 : Math.round(weightedSum / totalWeight);\n }\n\n return {\n answerReadiness: pillarScore(PILLARS['Answer Readiness']),\n contentStructure: pillarScore(PILLARS['Content Structure']),\n trustAuthority: pillarScore(PILLARS['Trust & Authority']),\n technicalFoundation: pillarScore(PILLARS['Technical Foundation']),\n aiDiscovery: pillarScore(PILLARS['AI Discovery']),\n };\n}\n\n// ─── Top fixes calculation ──────────────────────────────────────────────────\n\nexport function computeTopFixes(criteria: CriterionResult[], count = 3): TopFix[] {\n const fixes: TopFix[] = [];\n\n for (const c of criteria) {\n if (c.score >= 8) continue; // No fix needed\n const weight = PILLAR_WEIGHTS[c.criterion] ?? 0.02;\n const impactPoints = Math.round((10 - c.score) * weight * 100);\n if (impactPoints <= 0) continue;\n\n fixes.push({\n criterion: c.criterion,\n clientName: CLIENT_NAMES[c.criterion] || c.criterion_label,\n description: FIX_DESCRIPTIONS[c.criterion] || `Improve ${c.criterion_label}`,\n impactPoints,\n effort: CRITERION_EFFORT[c.criterion] || 'Medium',\n });\n }\n\n // Sort by impact descending\n fixes.sort((a, b) => b.impactPoints - a.impactPoints);\n\n return fixes.slice(0, count);\n}\n","/**\n * Shared scorecard building functions.\n * Extracted from cli/pre-crawl.ts for reuse in instant-audit and other consumers.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\nimport type { Status, FindingType, Severity, ScoreCardItem, CriterionDetail, DetailedFinding } from './types.js';\nimport type { AuditFinding } from './types.js';\nimport { getCriterionPillar, CLIENT_NAMES } from './pillars.js';\nimport { WEIGHTS } from './scoring.js';\n\n// ─── Criterion label mapping (site-crawler labels -> prompt-standard names) ──\n\nexport const CRITERION_LABELS: Record<string, string> = {\n 'llms.txt File': 'llms.txt File',\n 'Schema.org Structured Data': 'Schema.org Structured Data',\n 'Q&A Content Format': 'Q&A Content Format',\n 'Clean, Crawlable HTML': 'Clean, Crawlable HTML',\n 'Entity Authority & E-E-A-T': 'Entity Authority & NAP Consistency',\n 'robots.txt for AI Crawlers': 'robots.txt for AI Crawlers',\n 'Comprehensive FAQ Sections': 'Comprehensive FAQ Section',\n 'Original Data & Expert Content': 'Original Data & Expert Analysis',\n 'Internal Linking Architecture': 'Internal Linking Structure',\n 'Semantic HTML5 & Accessibility': 'Semantic HTML5 & Accessibility',\n 'Content Freshness Signals': 'Content Freshness Signals',\n 'Sitemap Completeness': 'Sitemap Completeness',\n 'RSS/Atom Feed': 'RSS/Atom Feed',\n 'Table & List Extractability': 'Table & List Extractability',\n 'Definition Patterns': 'Definition Patterns',\n 'Direct Answer Paragraphs': 'Direct Answer Paragraphs',\n 'Content Licensing & AI Permissions': 'Content Licensing & AI Permissions',\n 'Author & Expert Schema': 'Author & Expert Schema',\n 'Fact & Data Density': 'Fact & Data Density',\n 'Canonical URL Strategy': 'Canonical URL Strategy',\n 'Content Publishing Velocity': 'Content Publishing Velocity',\n 'Schema Coverage & Depth': 'Schema Coverage & Depth',\n 'Speakable Schema': 'Speakable Schema',\n 'Query-Answer Alignment': 'Query-Answer Alignment',\n 'Content Cannibalization': 'Content Cannibalization',\n 'Visible Date Signal': 'Visible Date Signal',\n 'Topic Coherence': 'Topic Coherence',\n 'Content Depth': 'Content Depth',\n 'Helpful Purpose Alignment': 'Helpful Purpose Alignment',\n 'First-Hand Experience Signals': 'First-Hand Experience Signals',\n 'Creator Transparency': 'Creator Transparency',\n 'Methodology Transparency': 'Methodology Transparency',\n 'Citation-Ready Writing Quality': 'Citation-Ready Writing Quality',\n 'Answer-First Placement': 'Answer-First Placement',\n 'Evidence Packaging': 'Evidence Packaging',\n 'Entity Disambiguation': 'Entity Disambiguation',\n 'Extraction Friction Score': 'Extraction Friction Score',\n 'Image Context for AI': 'Image Context for AI',\n 'Duplicate Content Blocks': 'Duplicate Content Blocks',\n 'Cross-Page Duplicate Content': 'Cross-Page Duplicate Content',\n};\n\n// ─── Score to Status mapping (matches AI prompt: 0=MISSING...8-10=STRONG) ────\n\nexport function scoreToStatus(score: number): Status {\n if (score === 0) return 'MISSING';\n if (score === 1) return 'NEARLY EMPTY';\n if (score === 2) return 'POOR';\n if (score === 3) return 'WEAK';\n if (score <= 5) return 'PARTIAL';\n if (score === 6) return 'MODERATE';\n if (score === 7) return 'GOOD';\n return 'STRONG';\n}\n\n// ─── Finding severity mapping (site-crawler -> audit format) ─────────────────\n\nexport function mapFindingSeverity(severity: AuditFinding['severity']): Severity {\n switch (severity) {\n case 'critical': return 'CRITICAL';\n case 'high': return 'MISSING';\n case 'medium': return 'ADD';\n case 'low': return 'PARTIAL';\n case 'info': return 'WORKING';\n default: return 'PARTIAL';\n }\n}\n\nexport function mapFindingType(severity: AuditFinding['severity'], hasFix: boolean): FindingType {\n if (severity === 'info') return 'Good';\n if (severity === 'critical') return 'Critical';\n if (severity === 'high') return 'Missing';\n if (hasFix) return 'Issue';\n return 'Note';\n}\n\n// ─── Convert CriterionResult to scorecard + detailedFindings format ──────────\n\nexport function buildScorecard(results: CriterionResult[]): ScoreCardItem[] {\n return results.map((r, i) => {\n const label = CRITERION_LABELS[r.criterion_label] || r.criterion_label;\n\n // Build keyFindings from the most important findings (2-3 sentences)\n const keyParts: string[] = [];\n for (const f of r.findings) {\n if (keyParts.length >= 3) break;\n keyParts.push(f.detail);\n }\n const keyFindings = keyParts.join('. ') + (keyParts.length > 0 && !keyParts[keyParts.length - 1].endsWith('.') ? '.' : '');\n\n return {\n id: i + 1,\n criterion: label,\n score: r.score,\n status: scoreToStatus(r.score),\n keyFindings,\n pillar: getCriterionPillar(r.criterion),\n clientName: CLIENT_NAMES[r.criterion] || label,\n weight: WEIGHTS[r.criterion],\n };\n });\n}\n\nexport function buildDetailedFindings(results: CriterionResult[]): CriterionDetail[] {\n return results.map((r, i) => {\n const label = CRITERION_LABELS[r.criterion_label] || r.criterion_label;\n\n const rawFindings: DetailedFinding[] = r.findings.map(f => ({\n type: mapFindingType(f.severity, !!f.fix),\n description: f.fix ? `${f.detail}. ${f.fix}` : f.detail,\n severity: mapFindingSeverity(f.severity),\n }));\n\n // Deduplicate findings by description\n const seen = new Set<string>();\n const findings: DetailedFinding[] = [];\n for (const f of rawFindings) {\n if (!seen.has(f.description)) {\n seen.add(f.description);\n findings.push(f);\n }\n }\n\n // Fallback: if a criterion somehow has fewer than 2 findings, add a single specific one\n if (findings.length < 2) {\n if (r.score >= 7) {\n findings.push({ type: 'Good', description: `${label} is well-implemented for AI engine visibility.`, severity: 'WORKING' });\n } else {\n findings.push({ type: 'Note', description: `${label} needs improvement - review specific issues above.`, severity: 'PARTIAL' });\n }\n }\n\n return {\n id: i + 1,\n name: label,\n findings,\n };\n });\n}\n","/**\n * Deterministic narrative generation from scorecard data.\n * Produces verdict, opportunities, pitchNumbers, and bottomLine\n * without any LLM calls - pure template-based generation.\n */\n\nimport type { ScoreCardItem, Deliverable, PitchMetric, ImpactLevel } from './types.js';\nimport type { CriterionResult } from './site-crawler.js';\nimport type { RawDataSummary } from './site-crawler.js';\n\n// ─── Scoring weights (mirrored from scoring.ts for impact calculation) ───────\n\nconst CRITERION_WEIGHTS: Record<string, number> = {\n // Content Substance (~55%)\n topic_coherence: 0.14,\n original_data: 0.10,\n content_depth: 0.07,\n fact_density: 0.06,\n direct_answer_density: 0.05,\n qa_content_format: 0.04,\n query_answer_alignment: 0.04,\n faq_section: 0.03,\n helpful_purpose_alignment: 0.03,\n first_hand_experience_signals: 0.03,\n // Content Organization (~30%)\n entity_consistency: 0.05,\n internal_linking: 0.04,\n content_freshness: 0.04,\n schema_markup: 0.03,\n author_schema_depth: 0.03,\n table_list_extractability: 0.03,\n creator_transparency: 0.02,\n methodology_transparency: 0.02,\n definition_patterns: 0.015,\n visible_date_signal: 0.015,\n semantic_html: 0.02,\n clean_html: 0.02,\n // Technical Plumbing (~15%)\n content_cannibalization: 0.02,\n llms_txt: 0.01,\n robots_txt: 0.01,\n content_velocity: 0.02,\n content_licensing: 0.01,\n sitemap_completeness: 0.01,\n canonical_url: 0.005,\n rss_feed: 0.005,\n schema_coverage: 0.005,\n speakable_schema: 0.005,\n // V2 Criteria (~15%)\n citation_ready_writing: 0.04,\n answer_first_placement: 0.03,\n evidence_packaging: 0.03,\n entity_disambiguation: 0.02,\n extraction_friction: 0.02,\n image_context_ai: 0.005,\n // V3 Criteria\n duplicate_content: 0.05,\n cross_page_duplication: 0.03,\n};\n\n// ─── Opportunity templates ───────────────────────────────────────────────────\n\ninterface OpportunityTemplate {\n name: string;\n effort: string;\n description: string;\n}\n\nconst OPPORTUNITY_TEMPLATES: Record<string, OpportunityTemplate> = {\n llms_txt: {\n name: 'Create llms.txt File',\n effort: 'Low',\n description: 'Add a /llms.txt file that describes your site, core services, and key pages in markdown format. This helps AI engines like ChatGPT and Claude understand your site structure and content offerings.',\n },\n schema_markup: {\n name: 'Add Schema.org Structured Data',\n effort: 'Medium',\n description: 'Implement JSON-LD structured data (Organization, Service, Product, FAQPage) on key pages. Schema markup helps AI engines extract and cite your content accurately.',\n },\n qa_content_format: {\n name: 'Restructure Content as Q&A',\n effort: 'Medium',\n description: 'Add question-based headings (H2/H3) throughout your content. Use \"What is...\", \"How does...\", \"Why should...\" patterns that match how users query AI assistants.',\n },\n clean_html: {\n name: 'Fix HTML Structure & Enable HTTPS',\n effort: 'Medium',\n description: 'Ensure clean, well-structured HTML with proper meta tags, semantic elements, and HTTPS. Clean HTML makes your content more parseable by AI crawlers.',\n },\n entity_consistency: {\n name: 'Strengthen Entity Authority (NAP)',\n effort: 'Low',\n description: 'Add Organization schema with consistent name, address, phone (NAP). Include sameAs links to social profiles and authoritative directories to strengthen entity recognition.',\n },\n robots_txt: {\n name: 'Configure robots.txt for AI Crawlers',\n effort: 'Low',\n description: 'Update robots.txt to explicitly allow AI crawlers (GPTBot, ClaudeBot, PerplexityBot). Add a Sitemap directive to help crawlers discover your content.',\n },\n faq_section: {\n name: 'Build Comprehensive FAQ Section',\n effort: 'Medium',\n description: 'Create a dedicated FAQ page with FAQPage schema markup. Cover common questions about your products, services, and industry to become a direct answer source for AI engines.',\n },\n helpful_purpose_alignment: {\n name: 'Improve Helpful Purpose Alignment',\n effort: 'Medium',\n description: 'Rewrite pages to solve the visitor task quickly and concretely. Reduce generic intros, search-first filler, and CTA interruptions before the first useful answer.',\n },\n first_hand_experience_signals: {\n name: 'Add First-Hand Experience Signals',\n effort: 'Medium',\n description: 'Show direct use, testing, implementation, or lived experience with concrete observations, examples, screenshots, and lessons learned.',\n },\n original_data: {\n name: 'Add Original Data & Case Studies',\n effort: 'High',\n description: 'Publish original research, statistics, case studies, or proprietary data that AI engines can cite. Unique data points make your content a primary source rather than a derivative one.',\n },\n internal_linking: {\n name: 'Improve Internal Linking Architecture',\n effort: 'Medium',\n description: 'Strengthen internal linking with descriptive anchor text between related pages. Add breadcrumb navigation and ensure every key page is reachable within 3 clicks from the homepage.',\n },\n semantic_html: {\n name: 'Implement Semantic HTML5',\n effort: 'Low',\n description: 'Use semantic HTML5 elements (main, article, nav, header, footer, section) to give AI parsers clear content structure. Add lang attribute and ARIA labels for accessibility.',\n },\n content_freshness: {\n name: 'Add Content Freshness Signals',\n effort: 'Low',\n description: 'Include dateModified schema, visible last-updated dates, and time elements on content pages. Fresh content signals help AI engines prioritize your pages over stale alternatives.',\n },\n sitemap_completeness: {\n name: 'Create Complete Sitemap',\n effort: 'Low',\n description: 'Generate a comprehensive sitemap.xml with lastmod dates for all important pages. A complete sitemap ensures AI crawlers can discover and prioritize your full content library.',\n },\n rss_feed: {\n name: 'Deploy RSS/Atom Feed',\n effort: 'Low',\n description: 'Add an RSS or Atom feed linked from your homepage. Feeds signal active content publishing and give AI engines a structured way to track your latest content.',\n },\n table_list_extractability: {\n name: 'Add Structured Tables & Lists',\n effort: 'Medium',\n description: 'Use HTML tables for comparison data and ordered/unordered lists for features, steps, and specifications. Structured data formats are directly extractable by AI engines for answers.',\n },\n definition_patterns: {\n name: 'Add Definition-Style Content',\n effort: 'Low',\n description: 'Include clear definition patterns (\"X refers to...\", \"X is defined as...\") for key terms and concepts. Definition-style content is highly citable by AI engines answering \"what is\" queries.',\n },\n direct_answer_density: {\n name: 'Add Direct Answer Paragraphs',\n effort: 'Medium',\n description: 'Write concise, standalone answer paragraphs (2-3 sentences) immediately after question headings. These \"snippet-ready\" paragraphs are ideal for AI engine citations.',\n },\n content_licensing: {\n name: 'Add Content Licensing & ai.txt',\n effort: 'Low',\n description: 'Create an /ai.txt file specifying AI usage permissions and add license schema to your structured data. Clear licensing signals help AI engines understand how they can use your content.',\n },\n author_schema_depth: {\n name: 'Enhance Author & Expert Schema',\n effort: 'Low',\n description: 'Add Person schema for content authors with credentials, expertise, and sameAs links. Expert attribution strengthens E-E-A-T signals that AI engines use to evaluate source credibility.',\n },\n creator_transparency: {\n name: 'Improve Creator Transparency',\n effort: 'Low',\n description: 'Add visible bylines, author pages, and reviewer/editor details so readers can clearly tell who created the content and why they are credible.',\n },\n methodology_transparency: {\n name: 'Add Methodology Transparency',\n effort: 'Low',\n description: 'Explain how pages were tested, researched, reviewed, or updated. Add methodology, criteria, sample-size, or review-process details where users expect them.',\n },\n fact_density: {\n name: 'Increase Fact & Data Density',\n effort: 'Medium',\n description: 'Add specific numbers, percentages, statistics, and data points throughout your content. Fact-dense content gives AI engines concrete data to cite rather than vague claims.',\n },\n canonical_url: {\n name: 'Fix Canonical URL Strategy',\n effort: 'Low',\n description: 'Add rel=\"canonical\" tags to all pages pointing to the preferred URL version. Canonical URLs prevent duplicate content confusion and consolidate AI engine citations to a single authoritative URL.',\n },\n content_velocity: {\n name: 'Increase Publishing Frequency',\n effort: 'High',\n description: 'Establish a regular content publishing cadence with dated entries in your sitemap. Consistent publishing signals to AI engines that your site is an active, current information source.',\n },\n schema_coverage: {\n name: 'Deepen Schema Coverage',\n effort: 'Medium',\n description: 'Extend structured data beyond the homepage to inner pages (articles, services, products). Consistent schema coverage across your site helps AI engines understand your full content depth.',\n },\n speakable_schema: {\n name: 'Add Speakable Schema',\n effort: 'Low',\n description: 'Add SpeakableSpecification schema with CSS selectors pointing to key content sections. This tells voice assistants and AI engines which parts of your page are most suitable for spoken answers.',\n },\n query_answer_alignment: {\n name: 'Improve Question-Answer Alignment',\n effort: 'Medium',\n description: 'Ensure every question-format heading (H2/H3) is followed by a direct answer paragraph. This pattern is ideal for AI engine snippet extraction.',\n },\n duplicate_content: {\n name: 'Fix Duplicate Content Blocks',\n effort: 'Medium',\n description: 'Sections within pages contain identical or near-identical text. LLMs may flag this as low-quality or thin content, reducing citation authority. Rewrite duplicate blocks with unique angles.',\n },\n cross_page_duplication: {\n name: 'Eliminate Cross-Page Duplicate Content',\n effort: 'Medium',\n description: 'The same paragraphs appear on multiple pages. AI engines may only index one version and ignore the rest. Rewrite shared content so each page offers a unique perspective.',\n },\n content_cannibalization: {\n name: 'Resolve Content Cannibalization',\n effort: 'Medium',\n description: 'Multiple pages compete for the same topic. Consolidate overlapping pages or differentiate titles and H1 headings.',\n },\n visible_date_signal: {\n name: 'Add Visible Date Signals',\n effort: 'Low',\n description: 'Display publication/modification dates visibly using <time> elements and add datePublished/dateModified to JSON-LD schema.',\n },\n topic_coherence: {\n name: 'Focus Content on Core Topics',\n effort: 'High',\n description: 'Ensure blog content consistently covers your core expertise areas rather than scattering across unrelated topics. AI engines build authority models - a site about \"Medicare coverage\" that also publishes about humidifiers and groceries dilutes its topical authority.',\n },\n content_depth: {\n name: 'Increase Content Depth',\n effort: 'Medium',\n description: 'Expand articles to 1000+ words with structured H2/H3 sections, comparison tables, and expert analysis. Thin content (under 300 words) is rarely cited by AI engines. Deep, well-structured articles demonstrate expertise.',\n },\n citation_ready_writing: {\n name: 'Improve Citation-Ready Writing',\n effort: 'Medium',\n description: 'Write self-contained definition sentences and single-claim statements that AI engines can quote directly. Avoid pronouns like \"this\" or \"that\" at the start of answer paragraphs.',\n },\n answer_first_placement: {\n name: 'Add Answer-First Placement',\n effort: 'Medium',\n description: 'Place a concise 40-80 word answer block in the first 300 words of each page. Avoid throat-clearing openers like \"In this article...\" and lead with the answer.',\n },\n evidence_packaging: {\n name: 'Package Evidence for AI',\n effort: 'Medium',\n description: 'Add inline citations to external sources, \"According to [Source]...\" attribution phrases, and a Sources section at the end of key articles.',\n },\n entity_disambiguation: {\n name: 'Add Entity Disambiguation',\n effort: 'Medium',\n description: 'Define the primary entity in the first 500 characters, use consistent terminology (same term 70%+), and add \"unlike X\" signals to help AI engines distinguish your topics.',\n },\n extraction_friction: {\n name: 'Reduce Extraction Friction',\n effort: 'Medium',\n description: 'Shorten sentences to under 25 words, use active voice in lead paragraphs, define jargon inline, and avoid hiding content behind accordions or display:none.',\n },\n image_context_ai: {\n name: 'Add Image Context for AI',\n effort: 'Low',\n description: 'Wrap images in <figure> elements with descriptive <figcaption> tags. Write alt text with 5+ words that describes the image content, not just \"image\" or \"photo\".',\n },\n};\n\n// ─── Impact calculation ──────────────────────────────────────────────────────\n\nfunction calculateImpact(score: number, weight: number, effort: string): ImpactLevel {\n const impactScore = (10 - score) * weight * 100;\n\n // Quick win override: low effort + meaningful impact\n if (effort === 'Low' && impactScore >= 3) return 'QUICK WIN';\n\n if (impactScore >= 12) return 'CRITICAL';\n if (impactScore >= 8) return 'HIGH';\n if (impactScore >= 5) return 'CORE AEO';\n if (impactScore >= 3) return 'MEDIUM';\n return 'LOW';\n}\n\n// ─── Verdict generation ──────────────────────────────────────────────────────\n\nexport function generateVerdict(\n score: number,\n scorecard: ScoreCardItem[],\n rawData: RawDataSummary,\n domain: string\n): string {\n // Score-tier opening\n let opening: string;\n if (score >= 86) {\n opening = `Excellent AEO implementation scoring ${score}/100.`;\n } else if (score >= 71) {\n opening = `Strong AEO fundamentals scoring ${score}/100 with room for optimization.`;\n } else if (score >= 56) {\n opening = `Moderate AEO readiness at ${score}/100 with significant gaps to address.`;\n } else if (score >= 41) {\n opening = `Below-average AEO readiness at ${score}/100 - multiple areas need attention.`;\n } else {\n opening = `Critical AEO gaps at ${score}/100 - ${domain} is largely invisible to AI engines.`;\n }\n\n // Top 3 strengths (score >= 8)\n const strengths = scorecard\n .filter(s => s.score >= 8)\n .sort((a, b) => b.score - a.score)\n .slice(0, 3);\n\n // Bottom 3 weaknesses (score <= 4)\n const weaknesses = scorecard\n .filter(s => s.score <= 4)\n .sort((a, b) => a.score - b.score)\n .slice(0, 3);\n\n const parts = [opening];\n\n if (strengths.length > 0) {\n const names = strengths.map(s => s.criterion);\n parts.push(`Key strengths include ${formatList(names)}.`);\n }\n\n if (weaknesses.length > 0) {\n const names = weaknesses.map(s => s.criterion);\n parts.push(`Priority gaps: ${formatList(names)}.`);\n }\n\n // Protocol note\n if (!rawData.has_https) {\n parts.push('HTTPS is not enabled, which caps several criteria scores and reduces AI crawler trust.');\n }\n\n // SPA rendering note\n if (rawData.rendered_with_headless) {\n parts.push('Note: this site uses client-side JavaScript rendering. AI crawlers see an empty page shell instead of content, which is the primary factor limiting the score.');\n }\n\n return parts.join(' ');\n}\n\n// ─── Opportunities generation ────────────────────────────────────────────────\n\nexport function generateOpportunities(\n scorecard: ScoreCardItem[],\n criterionResults: CriterionResult[]\n): Deliverable[] {\n const candidates: Array<{\n criterion: string;\n score: number;\n weight: number;\n impactScore: number;\n template: OpportunityTemplate;\n impact: ImpactLevel;\n }> = [];\n\n for (const result of criterionResults) {\n if (result.score > 7) continue; // Only generate opportunities for scores <= 7\n\n const weight = CRITERION_WEIGHTS[result.criterion] ?? 0.05;\n const template = OPPORTUNITY_TEMPLATES[result.criterion];\n if (!template) continue;\n\n const impactScore = (10 - result.score) * weight * 100;\n const impact = calculateImpact(result.score, weight, template.effort);\n\n candidates.push({\n criterion: result.criterion,\n score: result.score,\n weight,\n impactScore,\n template,\n impact,\n });\n }\n\n // Sort by impact score descending (highest impact first)\n candidates.sort((a, b) => b.impactScore - a.impactScore);\n\n // Take top 8-10\n const top = candidates.slice(0, 10);\n\n return top.map((c, i) => ({\n id: i + 1,\n name: c.template.name,\n description: c.template.description,\n effort: c.template.effort,\n impact: c.impact,\n }));\n}\n\n// ─── Pitch numbers generation ────────────────────────────────────────────────\n\nexport function generatePitchNumbers(\n score: number,\n rawData: RawDataSummary,\n scorecard: ScoreCardItem[]\n): PitchMetric[] {\n const metrics: PitchMetric[] = [];\n\n // 0. Rendering method (SPA warning - prepended so it appears first)\n if (rawData.rendered_with_headless) {\n metrics.push({\n metric: 'Rendering Method',\n value: 'Client-Side Only',\n significance: 'AI crawlers see empty HTML. All content loads via JavaScript, making this site invisible to ChatGPT, Claude, and Perplexity.',\n });\n }\n\n // 1. Overall AEO Score\n metrics.push({\n metric: 'AEO Score',\n value: `${score}/100`,\n significance: score >= 70\n ? 'Above average AI engine visibility'\n : score >= 50\n ? 'Moderate AI visibility with clear improvement paths'\n : 'Below average - significant optimization needed',\n });\n\n // 2. Schema types found\n const schemaCount = rawData.schema_types_found.length;\n metrics.push({\n metric: 'Schema Types',\n value: `${schemaCount} found`,\n significance: schemaCount >= 4\n ? 'Rich structured data helps AI engines parse content'\n : schemaCount >= 1\n ? 'Basic schema present but more types would improve AI extraction'\n : 'No structured data - AI engines cannot reliably extract content',\n });\n\n // 3. AI crawler readiness\n const aiCrawlerCount = rawData.robots_txt_ai_crawlers.length;\n const blockedCount = rawData.robots_txt_blocked_crawlers.length;\n metrics.push({\n metric: 'AI Crawler Access',\n value: blockedCount > 0\n ? `${blockedCount} blocked`\n : aiCrawlerCount > 0\n ? `${aiCrawlerCount} configured`\n : 'Not configured',\n significance: blockedCount > 0\n ? 'Active AI crawlers are blocked from accessing content'\n : aiCrawlerCount > 0\n ? 'robots.txt explicitly addresses AI crawler access'\n : 'No AI-specific crawler directives in robots.txt',\n });\n\n // 4. Content pages indexed\n const sitemapUrls = rawData.sitemap_url_count;\n metrics.push({\n metric: 'Sitemap URLs',\n value: sitemapUrls > 0 ? `${sitemapUrls} pages` : 'No sitemap',\n significance: sitemapUrls >= 50\n ? 'Comprehensive content library discoverable by AI crawlers'\n : sitemapUrls >= 10\n ? 'Moderate content footprint in sitemap'\n : sitemapUrls > 0\n ? 'Small sitemap - expanding content improves AI coverage'\n : 'No sitemap means AI crawlers must discover pages via links only',\n });\n\n // 5. Internal linking\n const linkCount = rawData.internal_link_count;\n metrics.push({\n metric: 'Internal Links',\n value: `${linkCount} links`,\n significance: linkCount >= 30\n ? 'Strong internal linking supports AI content discovery'\n : linkCount >= 10\n ? 'Moderate linking - adding more cross-references improves navigability'\n : 'Weak internal linking limits AI crawler depth',\n });\n\n // 6. Question headings\n const questionCount = rawData.question_headings_count + (rawData.blog_sample_question_headings || 0);\n if (questionCount > 0) {\n metrics.push({\n metric: 'Question Headings',\n value: `${questionCount} found`,\n significance: 'Question-based headings match how users query AI assistants',\n });\n }\n\n // 7. Criteria passing (>= 7)\n const passing = scorecard.filter(s => s.score >= 7).length;\n metrics.push({\n metric: 'Criteria Passing',\n value: `${passing}/${scorecard.length}`,\n significance: passing >= 18\n ? 'Excellent coverage across AEO dimensions'\n : passing >= 12\n ? 'Good foundation with room to improve remaining criteria'\n : `${scorecard.length - passing} criteria need attention for full AI visibility`,\n });\n\n return metrics;\n}\n\n// ─── Bottom line generation ──────────────────────────────────────────────────\n\nexport function generateBottomLine(\n score: number,\n opportunities: Deliverable[],\n scorecard: ScoreCardItem[],\n domain: string\n): string {\n const quickWins = opportunities.filter(o => o.impact === 'QUICK WIN');\n const criticalOps = opportunities.filter(o => o.impact === 'CRITICAL' || o.impact === 'HIGH');\n\n const passing = scorecard.filter(s => s.score >= 7).length;\n const total = scorecard.length;\n\n let summary: string;\n if (score >= 86) {\n summary = `${domain} demonstrates excellent AI engine optimization with ${passing}/${total} criteria at good or strong levels. Focus on maintaining content freshness and expanding structured data coverage to stay ahead.`;\n } else if (score >= 71) {\n summary = `${domain} has a solid AEO foundation with ${passing}/${total} criteria passing.`;\n if (quickWins.length > 0) {\n summary += ` ${quickWins.length} quick wins available: ${quickWins.slice(0, 3).map(q => q.name).join(', ')}.`;\n }\n if (criticalOps.length > 0) {\n summary += ` Address ${criticalOps.length} high-impact opportunities to push the score above 85.`;\n }\n } else if (score >= 56) {\n summary = `${domain} has moderate AI visibility with ${passing}/${total} criteria passing. ${opportunities.length} improvement opportunities identified.`;\n if (quickWins.length > 0) {\n summary += ` Start with quick wins: ${quickWins.slice(0, 3).map(q => q.name).join(', ')}.`;\n }\n } else if (score >= 41) {\n summary = `${domain} needs significant AEO work with only ${passing}/${total} criteria passing.`;\n if (criticalOps.length > 0) {\n summary += ` Priority: ${criticalOps.slice(0, 3).map(c => c.name).join(', ')}.`;\n }\n summary += ` Implementing the top ${Math.min(5, opportunities.length)} recommendations could improve the score by 15-25 points.`;\n } else {\n summary = `${domain} is largely invisible to AI engines with only ${passing}/${total} criteria passing. Fundamental AEO infrastructure is missing.`;\n if (opportunities.length > 0) {\n summary += ` Start with: ${opportunities.slice(0, 3).map(o => o.name).join(', ')}.`;\n }\n summary += ` A comprehensive AEO implementation could transform AI visibility from near-zero to competitive.`;\n }\n\n return summary;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction formatList(items: string[]): string {\n if (items.length === 0) return '';\n if (items.length === 1) return items[0];\n if (items.length === 2) return `${items[0]} and ${items[1]}`;\n return `${items.slice(0, -1).join(', ')}, and ${items[items.length - 1]}`;\n}\n","/**\n * Extended page discovery for instant audit.\n * Fetches additional pages beyond what prefetchSiteData provides,\n * including nav-linked pages, common paths, and content pages from sitemap.\n */\n\nimport type { FetchResult, SiteData, PageCategory } from './site-crawler.js';\nimport { safeFetch } from './network-guard.js';\n\n// ─── Fetch helper (matches site-crawler.ts fetchText) ────────────────────────\n\nasync function fetchPage(url: string, domain: string, timeoutMs = 10000): Promise<FetchResult | null> {\n const res = await safeFetch(url, { timeoutMs, expectedDomain: domain });\n if (!res || res.status !== 200) return null;\n const text = await res.text();\n if (text.length < 200) return null; // Skip trivially small pages\n return { text: text.slice(0, 500000), status: res.status, finalUrl: res.url };\n}\n\n// ─── Page variant paths ──────────────────────────────────────────────────────\n\nconst PAGE_VARIANTS: Record<string, string[]> = {\n about: ['/about', '/about-us', '/company', '/who-we-are'],\n pricing: ['/pricing', '/plans', '/packages'],\n services: ['/services', '/features', '/solutions', '/products', '/what-we-do'],\n contact: ['/contact', '/contact-us', '/get-in-touch'],\n team: ['/team', '/our-team', '/authors', '/people', '/leadership'],\n resources: ['/resources', '/resource-center', '/library'],\n docs: ['/docs', '/documentation', '/help', '/help-center', '/support'],\n cases: ['/case-studies', '/customers', '/success-stories', '/testimonials'],\n};\n\n// ─── Nav link extraction ─────────────────────────────────────────────────────\n\n/**\n * Extract internal page paths from <nav> elements in homepage HTML.\n * Returns deduplicated absolute paths (e.g. ['/about', '/pricing']).\n */\nexport function extractNavLinks(html: string, domain: string): string[] {\n // Extract links from <nav> elements\n const navBlocks = html.match(/<nav[\\s\\S]*?<\\/nav>/gi) || [];\n const navHtml = navBlocks.join('\\n');\n\n const hrefMatches = navHtml.match(/href=\"([^\"#]*)\"/gi) || [];\n const paths = new Set<string>();\n\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n\n for (const match of hrefMatches) {\n const href = match.match(/href=\"([^\"#]*)\"/i)?.[1];\n if (!href) continue;\n\n let path: string;\n if (href.startsWith('/')) {\n path = href;\n } else if (href.startsWith('http')) {\n try {\n const url = new URL(href);\n const linkDomain = url.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n path = url.pathname;\n } catch {\n continue;\n }\n } else {\n continue;\n }\n\n // Normalize: strip trailing slash, skip root and fragments\n path = path.replace(/\\/+$/, '') || '/';\n if (path === '/') continue;\n if (path.includes('#')) continue;\n\n // Skip resource/utility paths\n if (/\\.(js|css|png|jpg|svg|ico|pdf|xml|txt)$/i.test(path)) continue;\n if (/^\\/(api|wp-|static|assets|_next|auth|login|signup|cart|checkout)\\b/i.test(path)) continue;\n\n paths.add(path);\n }\n\n return Array.from(paths);\n}\n\n// ─── Full-page link extraction (fallback when no sitemap) ─────────────────\n\n/**\n * Extract ALL internal links from the full page HTML (not just <nav>).\n * Used as a fallback when no sitemap exists to build our own page list.\n * Returns deduplicated absolute paths, filtered and capped.\n */\nexport function extractAllInternalLinks(html: string, domain: string, limit = 30): string[] {\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const hrefMatches = html.match(/href=\"([^\"#]*)\"/gi) || [];\n const paths = new Set<string>();\n\n for (const match of hrefMatches) {\n const href = match.match(/href=\"([^\"#]*)\"/i)?.[1];\n if (!href) continue;\n\n let path: string;\n if (href.startsWith('/')) {\n path = href;\n } else if (href.startsWith('http')) {\n try {\n const url = new URL(href);\n const linkDomain = url.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n path = url.pathname;\n } catch {\n continue;\n }\n } else {\n continue;\n }\n\n // Normalize\n path = path.replace(/\\/+$/, '') || '/';\n if (path === '/') continue;\n if (path.includes('#') || path.includes('?')) continue;\n\n // Skip resource files and utility paths\n if (/\\.(js|css|png|jpg|jpeg|gif|svg|ico|pdf|xml|txt|zip|woff|woff2|ttf|eot|mp4|webm|mp3)$/i.test(path)) continue;\n if (/^\\/(api|wp-admin|wp-includes|wp-json|static|assets|_next|auth|login|signup|sign-up|register|cart|checkout|account|admin|cdn-cgi|feed|rss)\\b/i.test(path)) continue;\n\n // Skip anchors-only and mailto/tel\n if (path.startsWith('mailto:') || path.startsWith('tel:')) continue;\n\n paths.add(path);\n }\n\n // Return up to limit paths, prioritizing shorter paths (more likely to be main pages)\n return Array.from(paths)\n .sort((a, b) => a.split('/').length - b.split('/').length || a.localeCompare(b))\n .slice(0, limit);\n}\n\n// ─── Content page extraction from sitemap ────────────────────────────────────\n\n/**\n * Extract non-blog deep content pages from sitemap XML.\n * Targets service pages, product pages, etc. (not blog/article posts).\n */\nexport function extractContentPagesFromSitemap(\n sitemapText: string,\n domain: string,\n limit = 6\n): string[] {\n const urlBlocks = sitemapText.match(/<url>([\\s\\S]*?)<\\/url>/gi) || [];\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const candidates: string[] = [];\n\n // Paths to skip (already covered by blog sample or common pages)\n const skipPatterns = /\\/(?:blog|articles?|posts?|news|tag|category|author|feed|faq|about|pricing|contact|team|resources?|docs?|documentation|help|support|case-studies|customers|testimonials|sitemap|wp-|api|login|cart|checkout|search)\\b/i;\n\n for (const block of urlBlocks) {\n const locMatch = block.match(/<loc>([^<]+)<\\/loc>/i);\n if (!locMatch) continue;\n const url = locMatch[1].trim();\n\n try {\n const parsed = new URL(url);\n const urlDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (urlDomain !== cleanDomain) continue;\n\n if (parsed.pathname === '/' || parsed.pathname === '') continue;\n\n const path = parsed.pathname.toLowerCase();\n if (skipPatterns.test(path)) continue;\n\n // Want pages with 1-2 path segments (service pages, product pages)\n const segments = path.split('/').filter(Boolean);\n if (segments.length < 1 || segments.length > 3) continue;\n\n candidates.push(url);\n } catch {\n continue;\n }\n }\n\n // Return evenly spaced pages from the list for variety\n if (candidates.length <= limit) return candidates;\n\n const result: string[] = [];\n for (let i = 0; i < limit; i++) {\n const index = Math.round(i * (candidates.length - 1) / (limit - 1));\n result.push(candidates[index]);\n }\n return result;\n}\n\n// ─── Main multi-page fetcher ─────────────────────────────────────────────────\n\nexport interface MultiPageOptions {\n timeoutMs?: number;\n}\n\n/**\n * Fetch additional pages beyond what prefetchSiteData provides.\n * Discovers pages from nav links + common path variants + sitemap content pages.\n * All fetched pages are appended to siteData.blogSample so existing\n * getCombinedHtml() and criteria checks pick them up automatically.\n *\n * Mutates siteData in place and returns the count of new pages added.\n */\nexport async function fetchMultiPageData(\n siteData: SiteData,\n options?: MultiPageOptions\n): Promise<number> {\n if (!siteData.protocol || !siteData.homepage) return 0;\n\n const timeoutMs = options?.timeoutMs ?? 10000;\n const baseUrl = `${siteData.protocol}://${siteData.domain}`;\n const existingUrls = new Set<string>();\n\n // Track already-fetched URLs\n existingUrls.add(baseUrl + '/');\n existingUrls.add(baseUrl);\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n if (page.finalUrl) existingUrls.add(page.finalUrl);\n }\n }\n\n // Collect candidate URLs to fetch (URL -> category)\n const urlsToFetch = new Map<string, PageCategory>();\n\n // Source 1: Nav links from homepage\n const navPaths = extractNavLinks(siteData.homepage.text, siteData.domain);\n\n // Source 2: Common page variants (try nav links first, then fallback paths)\n for (const [category, variants] of Object.entries(PAGE_VARIANTS)) {\n // Check if any nav link matches this category\n const navMatch = navPaths.find(p =>\n variants.some(v => p.toLowerCase() === v || p.toLowerCase().startsWith(v + '/'))\n );\n\n if (navMatch) {\n const url = `${baseUrl}${navMatch}`;\n if (!existingUrls.has(url)) urlsToFetch.set(url, category as PageCategory);\n } else {\n // Try first variant as fallback\n const url = `${baseUrl}${variants[0]}`;\n if (!existingUrls.has(url)) urlsToFetch.set(url, category as PageCategory);\n }\n }\n\n // Source 3: Content pages from sitemap\n if (siteData.sitemapXml && siteData.sitemapXml.status === 200) {\n const contentUrls = extractContentPagesFromSitemap(\n siteData.sitemapXml.text,\n siteData.domain,\n 6\n );\n for (const url of contentUrls) {\n if (!existingUrls.has(url)) urlsToFetch.set(url, 'content');\n }\n }\n\n // Source 4: When no sitemap blog sample exists, extract ALL internal links\n // from homepage as a fallback to build our own page list\n const hasBlogSample = (siteData.blogSample?.length ?? 0) > 3;\n if (!hasBlogSample) {\n const allPaths = extractAllInternalLinks(siteData.homepage.text, siteData.domain, 30);\n for (const path of allPaths) {\n const url = `${baseUrl}${path}`;\n if (!existingUrls.has(url) && !urlsToFetch.has(url)) {\n urlsToFetch.set(url, 'content');\n }\n }\n }\n\n // Fetch all URLs in parallel\n const entries = Array.from(urlsToFetch.entries());\n if (entries.length === 0) return 0;\n\n const results = await Promise.all(entries.map(([url]) => fetchPage(url, siteData.domain, timeoutMs)));\n\n // Append successful results to blogSample with category tags\n if (!siteData.blogSample) siteData.blogSample = [];\n\n let added = 0;\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n if (result && result.text.length > 500) {\n result.category = entries[i][1];\n siteData.blogSample.push(result);\n added++;\n }\n }\n\n return added;\n}\n","/**\n * Per-page AEO scoring.\n * Evaluates 25 of 40 criteria that apply at individual page level.\n * Produces a 0-75 AEO score per page (single-page cap at 75).\n */\n\nimport type { SiteData } from './site-crawler.js';\nimport {\n extractDuplicateContentSections,\n shingleJaccardSimilarity,\n} from './duplicate-content.js';\nimport {\n scoreHelpfulPurposeAlignment,\n scoreFirstHandExperienceSignals,\n scoreCreatorTransparency,\n scoreMethodologyTransparency,\n} from './helpful-content.js';\nexport {\n scoreHelpfulPurposeAlignment,\n scoreFirstHandExperienceSignals,\n scoreCreatorTransparency,\n scoreMethodologyTransparency,\n} from './helpful-content.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface PageCriterionScore {\n criterion: string;\n criterion_label: string;\n score: number;\n weight: number;\n}\n\nexport interface PageScoreResult {\n aeoScore: number;\n criterionScores: PageCriterionScore[];\n scoreCapped?: boolean;\n}\n\n// ─── Criteria weights (mirrored from scoring.ts for page-level criteria) ─\n\nconst PAGE_CRITERIA: Record<string, { weight: number; label: string }> = {\n // Content Substance\n original_data: { weight: 0.10, label: 'Original Data & Expert Content' },\n fact_density: { weight: 0.06, label: 'Fact & Data Density' },\n direct_answer_density: { weight: 0.05, label: 'Direct Answer Paragraphs' },\n qa_content_format: { weight: 0.04, label: 'Q&A Content Format' },\n query_answer_alignment: { weight: 0.04, label: 'Query-Answer Alignment' },\n faq_section: { weight: 0.03, label: 'FAQ Section Content' },\n // Content Organization\n content_freshness: { weight: 0.04, label: 'Content Freshness Signals' },\n schema_markup: { weight: 0.03, label: 'Schema.org Structured Data' },\n table_list_extractability: { weight: 0.03, label: 'Table & List Extractability' },\n definition_patterns: { weight: 0.015, label: 'Definition Patterns' },\n visible_date_signal: { weight: 0.015, label: 'Visible Date Signal' },\n semantic_html: { weight: 0.02, label: 'Semantic HTML5 & Accessibility' },\n clean_html: { weight: 0.02, label: 'Clean, Crawlable HTML' },\n // Technical Plumbing\n canonical_url: { weight: 0.005, label: 'Canonical URL Strategy' },\n // V2 Criteria\n citation_ready_writing: { weight: 0.04, label: 'Citation-Ready Writing Quality' },\n answer_first_placement: { weight: 0.03, label: 'Answer-First Placement' },\n evidence_packaging: { weight: 0.03, label: 'Evidence Packaging' },\n helpful_purpose_alignment: { weight: 0.03, label: 'Helpful Purpose Alignment' },\n first_hand_experience_signals: { weight: 0.03, label: 'First-Hand Experience Signals' },\n entity_disambiguation: { weight: 0.02, label: 'Entity Disambiguation' },\n extraction_friction: { weight: 0.02, label: 'Extraction Friction Score' },\n creator_transparency: { weight: 0.02, label: 'Creator Transparency' },\n methodology_transparency: { weight: 0.02, label: 'Methodology Transparency' },\n image_context_ai: { weight: 0.005, label: 'Image Context for AI' },\n duplicate_content: { weight: 0.05, label: 'Duplicate Content Blocks' },\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction extractJsonLdBlocks(html: string): string[] {\n const blocks: string[] = [];\n const regex = /<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(html)) !== null) {\n blocks.push(match[1]);\n }\n return blocks;\n}\n\nfunction extractTypesFromJsonLd(blocks: string[]): Set<string> {\n const types = new Set<string>();\n for (const block of blocks) {\n const typeMatches = block.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/g) || [];\n for (const m of typeMatches) {\n const t = m.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/);\n if (t) types.add(t[1]);\n }\n }\n return types;\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction extractQuestionHeadings(html: string): string[] {\n const headings = html.match(/<h[2-3][^>]*>([\\s\\S]*?)<\\/h[2-3]>/gi) || [];\n const questions: string[] = [];\n for (const h of headings) {\n const text = h.replace(/<[^>]*>/g, '').trim();\n if (/\\?$/.test(text) || /^(what|how|why|when|where|who|which|can|do|does|is|are|should|will)\\b/i.test(text)) {\n questions.push(text);\n }\n }\n return questions;\n}\n\n/** Check if a question heading is followed by an answer paragraph (≥20 chars). */\nfunction countAnsweredQuestions(html: string): { total: number; answered: number } {\n const questions = extractQuestionHeadings(html);\n if (questions.length === 0) return { total: 0, answered: 0 };\n\n let answered = 0;\n for (const q of questions) {\n // Escape the question text for regex matching\n const escaped = q.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n // Look for the heading containing this text, then a <p> after it\n const pattern = new RegExp(escaped + '[\\\\s\\\\S]*?</h[2-3]>\\\\s*<p[^>]*>([\\\\s\\\\S]*?)</p>', 'i');\n const match = html.match(pattern);\n if (match && match[1].replace(/<[^>]*>/g, '').trim().length >= 20) {\n answered++;\n }\n }\n return { total: questions.length, answered };\n}\n\nfunction cap(value: number, max: number): number {\n return Math.min(value, max);\n}\n\n// ─── 14 Scoring Functions ───────────────────────────────────────────────────\n\n/** 1. Schema.org Structured Data */\nexport function scoreSchemaMarkup(html: string): number {\n const blocks = extractJsonLdBlocks(html);\n if (blocks.length === 0) return 0;\n\n let score = 3; // has JSON-LD\n const types = extractTypesFromJsonLd(blocks);\n\n const knownTypes = ['Organization', 'LocalBusiness', 'Article', 'FAQPage', 'Product', 'WebPage',\n 'BreadcrumbList', 'HowTo', 'Person', 'WebSite', 'BlogPosting', 'Service'];\n let knownCount = 0;\n for (const t of types) {\n if (knownTypes.includes(t)) knownCount++;\n }\n score += cap(knownCount * 2, 4);\n\n if (types.has('Organization') || types.has('LocalBusiness')) score += 2;\n if (types.has('FAQPage')) score += 1;\n\n return cap(score, 10);\n}\n\n/** 2. Q&A Content Format */\nexport function scoreQAFormat(html: string): number {\n const questions = extractQuestionHeadings(html);\n let score = 0;\n\n if (questions.length >= 10) score += 5;\n else if (questions.length >= 3) score += 3;\n else if (questions.length >= 1) score += 1;\n\n // Direct-answer format: question heading followed by paragraph ≥20 chars\n const { answered } = countAnsweredQuestions(html);\n if (answered >= 1) score += 3;\n\n // Single H1\n const h1Matches = html.match(/<h1[\\s>]/gi) || [];\n if (h1Matches.length === 1) score += 2;\n\n return cap(score, 10);\n}\n\n/** 3. Clean, Crawlable HTML */\nexport function scoreCleanHtml(html: string): number {\n let score = 0;\n\n // Semantic elements\n const semantics = ['<main', '<article', '<section'];\n let semCount = 0;\n for (const tag of semantics) {\n if (html.toLowerCase().includes(tag)) semCount++;\n }\n score += cap(semCount, 3);\n\n // Single H1\n const h1Matches = html.match(/<h1[\\s>]/gi) || [];\n if (h1Matches.length === 1) score += 2;\n\n // Text content > 500 chars\n const text = getTextContent(html);\n if (text.length > 500) score += 3;\n\n // Title + meta description\n const hasTitle = /<title[^>]*>[^<]+<\\/title>/i.test(html);\n const hasDesc = /<meta\\s[^>]*name=[\"']description[\"'][^>]*content=[\"'][^\"']+[\"']/i.test(html)\n || /<meta\\s[^>]*content=[\"'][^\"']+[\"'][^>]*name=[\"']description[\"']/i.test(html);\n if (hasTitle && hasDesc) score += 2;\n\n return cap(score, 10);\n}\n\n/** 4. FAQ Section Content */\nexport function scoreFaqSection(html: string): number {\n let score = 0;\n const lowerHtml = html.toLowerCase();\n\n // FAQ/frequently-asked text\n if (/frequently\\s*asked|faq/i.test(html)) score += 2;\n\n // FAQPage in JSON-LD\n const blocks = extractJsonLdBlocks(html);\n const types = extractTypesFromJsonLd(blocks);\n if (types.has('FAQPage')) score += 3;\n\n // Question headings ≥ 10\n const questions = extractQuestionHeadings(html);\n if (questions.length >= 10) score += 1;\n\n // Accordion/toggle patterns (details/summary or common CSS classes)\n if (/<details[\\s>]/i.test(html) || /accordion|collapsible|toggle/i.test(lowerHtml)) score += 1;\n\n return cap(score, 10);\n}\n\n/** 5. Original Data & Expert Content */\nexport function scoreOriginalData(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Stats with research context\n if (/\\b(our (study|analysis|research|survey|data|findings))\\b/i.test(text)) {\n score += 3;\n } else if (/\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(text)) {\n score += 1;\n }\n\n // Case studies with metrics\n if (/\\bcase\\s+stud(y|ies)\\b/i.test(text) && /\\d+(\\.\\d+)?%|\\$[\\d,.]+/.test(text)) {\n score += 3;\n } else if (/\\bcase\\s+stud(y|ies)\\b/i.test(text)) {\n score += 1;\n }\n\n // Expert attribution\n if (/\\baccording\\s+to\\b|\\bexpert|\\b(Ph\\.?D|MD|professor|analyst|researcher)\\b/i.test(text)) {\n score += 2;\n }\n\n // Blog content (internal links to /blog)\n if (/href=[\"'][^\"']*\\/blog\\b/i.test(html)) {\n score += 2;\n }\n\n return cap(score, 10);\n}\n\n/** 6. Query-Answer Alignment */\nexport function scoreQueryAnswerAlignment(html: string): number {\n const { total, answered } = countAnsweredQuestions(html);\n\n // No question headings = neutral\n if (total === 0) return 5;\n\n const ratio = answered / total;\n if (ratio >= 0.8) return 10;\n if (ratio >= 0.5) return 7;\n if (answered > 0) return 4;\n return 0;\n}\n\n/** 7. Content Freshness Signals */\nexport function scoreContentFreshness(html: string): number {\n let score = 0;\n const blocks = extractJsonLdBlocks(html);\n\n // JSON-LD datePublished/dateModified\n const allJsonLd = blocks.join(' ');\n if (/datePublished|dateModified/i.test(allJsonLd)) score += 3;\n\n // <time> elements\n const timeElements = html.match(/<time[\\s>]/gi) || [];\n if (timeElements.length >= 2) score += 3;\n else if (timeElements.length === 1) score += 1;\n\n // Article meta tags\n if (/<meta\\s[^>]*property=[\"']article:(published_time|modified_time)[\"']/i.test(html)) score += 2;\n\n // Current or previous year reference\n const currentYear = new Date().getFullYear();\n const yearPattern = new RegExp(`\\\\b(${currentYear}|${currentYear - 1})\\\\b`);\n if (yearPattern.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 8. Table & List Extractability */\nexport function scoreTableListExtractability(html: string): number {\n let score = 0;\n\n // Tables with <th>\n const tablesWithHeaders = html.match(/<table[\\s\\S]*?<th[\\s>]/gi) || [];\n if (tablesWithHeaders.length >= 2) score += 4;\n else if (tablesWithHeaders.length === 1) score += 3;\n\n // Tables without headers\n if (tablesWithHeaders.length === 0 && /<table[\\s>]/i.test(html)) score += 1;\n\n // Ordered lists\n if (/<ol[\\s>]/i.test(html)) score += 2;\n\n // Unordered lists\n if (/<ul[\\s>]/i.test(html)) score += 2;\n\n // ≥10 list items\n const listItems = html.match(/<li[\\s>]/gi) || [];\n if (listItems.length >= 10) score += 1;\n\n // Definition lists\n if (/<dl[\\s>]/i.test(html)) score += 1;\n\n return cap(score, 10);\n}\n\n/** 9. Direct Answer Paragraphs */\nexport function scoreDirectAnswerDensity(html: string): number {\n let score = 0;\n\n // Q&A pairs (question heading + <p>)\n const { answered } = countAnsweredQuestions(html);\n if (answered >= 3) score += 6;\n else if (answered >= 1) score += 3;\n\n // Snippet-zone paragraphs (40-150 words)\n const paragraphs = html.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi) || [];\n let snippetCount = 0;\n for (const p of paragraphs) {\n const text = p.replace(/<[^>]*>/g, '').trim();\n const words = text.split(/\\s+/).filter(w => w.length > 0).length;\n if (words >= 40 && words <= 150) snippetCount++;\n }\n if (snippetCount >= 3) score += 2;\n else if (snippetCount >= 1) score += 1;\n\n // Direct-answer openers\n const directOpeners = getTextContent(html).match(/\\b(yes|no|in short|the answer is|simply put|in summary)\\b/gi) || [];\n if (directOpeners.length >= 2) score += 2;\n\n return cap(score, 10);\n}\n\n/** 10. Semantic HTML5 & Accessibility */\nexport function scoreSemanticHtml(html: string): number {\n let score = 0;\n const lowerHtml = html.toLowerCase();\n\n // 6 semantic elements\n const elements = ['<main', '<article', '<time', '<nav', '<header', '<footer'];\n let count = 0;\n for (const el of elements) {\n if (lowerHtml.includes(el)) count++;\n }\n score += cap(Math.floor(count * 0.7), 4);\n\n // Image alt ratio ≥ 80%\n const imgTags = html.match(/<img\\s[^>]*>/gi) || [];\n if (imgTags.length > 0) {\n let withAlt = 0;\n for (const img of imgTags) {\n if (/\\salt=[\"'][^\"']*[\"']/i.test(img)) withAlt++;\n }\n if (withAlt / imgTags.length >= 0.8) score += 2;\n }\n\n // lang attribute\n if (/<html[^>]*\\slang=[\"'][^\"']+[\"']/i.test(html)) score += 2;\n\n // ARIA attributes\n if (/\\baria-/i.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 11. Fact & Data Density */\nexport function scoreFactDensity(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Numeric data points (%, $, count phrases)\n const numericPatterns = text.match(/\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b|\\b\\d+\\s*(million|billion|thousand|users|customers|employees)\\b/gi) || [];\n if (numericPatterns.length >= 6) score += 5;\n else if (numericPatterns.length >= 3) score += 3;\n else if (numericPatterns.length >= 1) score += 1;\n\n // Year references\n const years = new Set<string>();\n const yearMatches = text.match(/\\b(19|20)\\d{2}\\b/g) || [];\n for (const y of yearMatches) years.add(y);\n if (years.size >= 2) score += 2;\n else if (years.size === 1) score += 1;\n\n // Attribution phrases\n if (/\\baccording to\\b|\\bsource:\\s|\\bcited\\b|\\breported by\\b/i.test(text)) score += 2;\n\n // Units (kg, miles, hours, etc.)\n const units = text.match(/\\b\\d+\\s*(kg|lb|miles|km|hours|minutes|days|months|years|GB|MB|TB)\\b/gi) || [];\n if (units.length >= 2) score += 1;\n\n return cap(score, 10);\n}\n\n/** 12. Definition Patterns */\nexport function scoreDefinitionPatterns(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Definition phrases\n const defPatterns = text.match(/\\b(is a|is an|refers to|defined as|means that|also known as|abbreviated as)\\b/gi) || [];\n if (defPatterns.length >= 3) score += 5;\n else if (defPatterns.length >= 1) score += 3;\n\n // Early placement (first 2000 chars)\n const early = text.slice(0, 2000);\n if (/\\b(is a|is an|refers to|defined as)\\b/i.test(early)) score += 2;\n\n // <dfn> or <abbr>\n if (/<dfn[\\s>]/i.test(html) || /<abbr[\\s>]/i.test(html)) score += 1;\n\n // <dl> or glossary\n if (/<dl[\\s>]/i.test(html) || /glossary/i.test(html)) score += 2;\n\n return cap(score, 10);\n}\n\n/** 13. Canonical URL Strategy */\nexport function scoreCanonicalUrl(html: string, url?: string): number {\n let score = 0;\n\n // Has canonical link\n const canonicalMatch = html.match(/<link[^>]*rel=[\"']canonical[\"'][^>]*href=[\"']([^\"']+)[\"']/i)\n || html.match(/<link[^>]*href=[\"']([^\"']+)[\"'][^>]*rel=[\"']canonical[\"']/i);\n\n if (!canonicalMatch) return 0;\n score += 4;\n\n const canonicalHref = canonicalMatch[1];\n\n // Self-referencing\n if (url) {\n try {\n const canonicalUrl = new URL(canonicalHref, url);\n const pageUrl = new URL(url);\n if (canonicalUrl.pathname === pageUrl.pathname && canonicalUrl.hostname === pageUrl.hostname) {\n score += 3;\n }\n } catch {\n // Ignore URL parse errors\n }\n }\n\n // HTTPS\n if (canonicalHref.startsWith('https://')) score += 2;\n\n // Single canonical (no duplicates)\n const allCanonicals = html.match(/<link[^>]*rel=[\"']canonical[\"'][^>]*>/gi) || [];\n if (allCanonicals.length === 1) score += 1;\n\n return cap(score, 10);\n}\n\n/** 14. Visible Date Signal */\nexport function scoreVisibleDateSignal(html: string): number {\n let score = 0;\n\n // <time datetime> with visible text\n const timeWithDatetime = html.match(/<time[^>]*datetime=[\"'][^\"']+[\"'][^>]*>[^<]+<\\/time>/gi) || [];\n if (timeWithDatetime.length > 0) score += 5;\n\n // JSON-LD datePublished/dateModified\n const blocks = extractJsonLdBlocks(html);\n const allJsonLd = blocks.join(' ');\n if (/datePublished|dateModified/i.test(allJsonLd)) score += 3;\n\n // Article meta date tags\n if (/<meta\\s[^>]*property=[\"']article:(published_time|modified_time)[\"']/i.test(html)) score += 2;\n\n // dateModified within 180 days\n const modifiedMatch = allJsonLd.match(/\"dateModified\"\\s*:\\s*\"([^\"]+)\"/i);\n if (modifiedMatch) {\n try {\n const modified = new Date(modifiedMatch[1]);\n const daysDiff = (Date.now() - modified.getTime()) / (1000 * 60 * 60 * 24);\n if (daysDiff <= 180) score += 1;\n } catch {\n // Ignore date parse errors\n }\n }\n\n return cap(score, 10);\n}\n\n/** 15. Citation-Ready Writing Quality */\nexport function scoreCitationReadyWriting(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Definition sentences\n const defSentences = text.match(/\\b\\w+\\s+(is\\s+(?:a|an)\\s|refers\\s+to|defined\\s+as)\\b/gi) || [];\n if (defSentences.length >= 3) score += 3;\n else if (defSentences.length >= 1) score += 1;\n\n // One-claim sentences (<30 words, single clause)\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10);\n const oneClaimSentences = sentences.filter(s => {\n const words = s.trim().split(/\\s+/);\n return words.length < 30 && !/,\\s*(and|but|or|which|that|because|although|while)\\s/i.test(s);\n });\n if (oneClaimSentences.length >= 5) score += 3;\n else if (oneClaimSentences.length >= 2) score += 1;\n\n // Self-contained check (no dangling pronouns after question headings)\n const qHeadings = extractQuestionHeadings(html);\n if (qHeadings.length > 0) {\n let selfContained = 0;\n for (const qh of qHeadings) {\n const escaped = qh.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pat = new RegExp(escaped + '[\\\\s\\\\S]{0,200}?<\\\\/h[23]>\\\\s*<p[^>]*>([^<]{0,200})', 'i');\n const m = pat.exec(html);\n if (m && m[1] && !/^\\s*(this|that|these|those|it)\\s/i.test(m[1].trim())) {\n selfContained++;\n }\n }\n if (selfContained / qHeadings.length >= 0.8) score += 2;\n }\n\n // Quotation-ready lines\n const quotableLines = sentences.filter(s =>\n /\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(s) &&\n /\\b[A-Z][a-z]+(?:\\s+[A-Z][a-z]+)*\\b/.test(s)\n );\n if (quotableLines.length >= 2) score += 2;\n else if (quotableLines.length >= 1) score += 1;\n\n return cap(score, 10);\n}\n\n/** 16. Answer-First Placement */\nexport function scoreAnswerFirstPlacement(html: string): number {\n let score = 0;\n\n const bodyMatch = html.match(/<body[^>]*>([\\s\\S]*)/i);\n const bodyHtml = bodyMatch ? bodyMatch[1] : html;\n const bodyText = bodyHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n\n // Short answer block (40-80 words) in first 300 words\n const earlyParagraphs = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 5) || [];\n const first300Words = bodyText.split(/\\s+/).slice(0, 300).join(' ');\n for (const p of earlyParagraphs) {\n const pText = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = pText.split(/\\s+/).length;\n if (wordCount >= 40 && wordCount <= 80 && first300Words.includes(pText.slice(0, 50))) {\n score += 4;\n break;\n }\n }\n\n // First-paragraph answer check (not throat-clearing)\n const firstPara = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/i);\n if (firstPara) {\n const firstParaText = firstPara[1].replace(/<[^>]*>/g, '').trim();\n if (!/^(In today|Welcome to|Have you ever|If you're looking|In this (article|post|guide))/i.test(firstParaText)) {\n score += 3;\n }\n }\n\n // Entity in first sentence (H1 word in first body sentence)\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (h1Match) {\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3);\n const firstSentence = bodyText.split(/[.!?]/)[0] || '';\n if (h1Words.some(w => firstSentence.toLowerCase().includes(w.toLowerCase()))) {\n score += 3;\n }\n }\n\n return cap(score, 10);\n}\n\n/** 17. Evidence Packaging */\nexport function scoreEvidencePackaging(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Inline citations (<a> inside <p> linking to external domains)\n const paragraphs = html.match(/<p[^>]*>[\\s\\S]*?<\\/p>/gi) || [];\n let inlineCitations = 0;\n for (const p of paragraphs) {\n const links = p.match(/<a[^>]*href=[\"'](https?:\\/\\/[^\"']+)[\"'][^>]*>/gi) || [];\n inlineCitations += links.length;\n }\n if (inlineCitations >= 3) score += 3;\n else if (inlineCitations >= 1) score += 1;\n\n // Sources section heading\n if (/<h[2-4][^>]*>[^<]*(source|reference|citation|bibliography)[^<]*<\\/h[2-4]>/i.test(html)) {\n score += 2;\n }\n\n // Attribution phrases\n const attributionPhrases = text.match(/\\baccording\\s+to\\s+[A-Z]|\\b[A-Z][a-z]+\\s+(found|says|reports|notes|states)\\b/gi) || [];\n if (attributionPhrases.length >= 3) score += 3;\n else if (attributionPhrases.length >= 1) score += 1;\n\n // Sourced statistics\n const sourcedStats = text.match(/\\d+(\\.\\d+)?(%|\\s*(million|billion|thousand|percent))\\b[^.]*\\b[A-Z][a-z]+\\b/gi) || [];\n if (sourcedStats.length >= 2) score += 2;\n else if (sourcedStats.length >= 1) score += 1;\n\n return cap(score, 10);\n}\n\n/** 18. Entity Disambiguation */\nexport function scoreEntityDisambiguation(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n const h1Match = html.match(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/i);\n if (!h1Match) return 3; // neutral if no H1\n\n const h1Text = h1Match[1].replace(/<[^>]*>/g, '').trim();\n const h1Words = h1Text.split(/\\s+/).filter(w => w.length > 3);\n const primaryNoun = h1Words.sort((a, b) => b.length - a.length)[0] || '';\n if (!primaryNoun) return 3;\n\n // Primary entity defined early (first 500 chars)\n const first500 = text.slice(0, 500);\n if (new RegExp(`\\\\b${primaryNoun.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b[^.]*\\\\b(is|refers|defined|means)\\\\b`, 'i').test(first500)) {\n score += 4;\n }\n\n // Entity consistency\n const primaryRegex = new RegExp(`\\\\b${primaryNoun.toLowerCase().replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b`, 'gi');\n const mentions = text.match(primaryRegex) || [];\n if (mentions.length >= 3) score += 3;\n else if (mentions.length >= 1) score += 1;\n\n // Disambiguation signals\n if (/\\bunlike\\s+\\w/i.test(text) || /\\bcompared\\s+to\\s+\\w/i.test(text) || /\\bnot\\s+to\\s+be\\s+confused\\s+with\\b/i.test(text)) {\n score += 3;\n }\n\n return cap(score, 10);\n}\n\n/** 19. Extraction Friction Score */\nexport function scoreExtractionFriction(html: string): number {\n let score = 0;\n const text = getTextContent(html);\n\n // Average sentence length\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 5);\n const avgLen = sentences.length > 0\n ? sentences.map(s => s.trim().split(/\\s+/).length).reduce((a, b) => a + b, 0) / sentences.length\n : 0;\n if (avgLen > 0 && avgLen < 20) score += 3;\n else if (avgLen >= 20 && avgLen <= 25) score += 2;\n else if (avgLen > 25 && avgLen <= 35) score += 1;\n\n // Voice-friendly lead (first 2-3 sentences after H1)\n const h1Match = html.match(/<h1[^>]*>[\\s\\S]*?<\\/h1>/i);\n if (h1Match) {\n const afterH1 = html.slice(html.indexOf(h1Match[0]) + h1Match[0].length);\n const leadParas = afterH1.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 3) || [];\n const leadText = leadParas.map(p => p.replace(/<[^>]*>/g, '')).join(' ').trim();\n const words = leadText.split(/\\s+/).length;\n if (words <= 75 && words > 10 && !/\\([^)]+\\)/.test(leadText)) {\n score += 3;\n }\n }\n\n // Jargon density (long words in first 500 words)\n const first500Words = text.split(/\\s+/).slice(0, 500).join(' ');\n const longWords = first500Words.match(/\\b[a-z]{10,}\\b/gi) || [];\n if (longWords.length <= 2) score += 2;\n else if (longWords.length <= 5) score += 1;\n\n // Hidden content penalty\n if (/display\\s*:\\s*none|visibility\\s*:\\s*hidden/i.test(html) && /<(div|section|p)[^>]*(?:style=[\"'][^\"']*display\\s*:\\s*none|hidden)[^>]*>/i.test(html)) {\n score = Math.max(0, score - 2);\n }\n\n return cap(score, 10);\n}\n\n/** 20. Image Context for AI */\nexport function scoreImageContextAI(html: string): number {\n let score = 0;\n\n const allImages = html.match(/<img\\s[^>]*>/gi) || [];\n if (allImages.length === 0) return 5; // neutral\n\n // <figure> + <figcaption>\n const figureBlocks = html.match(/<figure[\\s\\S]*?<\\/figure>/gi) || [];\n const figuresWithCaption = figureBlocks.filter(f => /<figcaption/i.test(f));\n const figureRatio = figuresWithCaption.length / allImages.length;\n if (figureRatio >= 0.5) score += 4;\n else if (figureRatio >= 0.25) score += 2;\n\n // Alt text quality (>5 words, not generic)\n let goodAltCount = 0;\n for (const img of allImages) {\n const altMatch = img.match(/\\salt=[\"']([^\"']+)[\"']/i);\n if (altMatch) {\n const altText = altMatch[1].trim();\n if (altText.split(/\\s+/).length > 5 && !/^(image|photo|picture|img|icon|logo|banner|screenshot)$/i.test(altText)) {\n goodAltCount++;\n }\n }\n }\n if (goodAltCount / allImages.length >= 0.5) score += 3;\n else if (goodAltCount > 0) score += 1;\n\n // Contextual placement\n const contextualImages = html.match(/<(article|section)[^>]*>[\\s\\S]*?<img[^>]*>[\\s\\S]*?<\\/\\1>/gi) || [];\n if (contextualImages.length > 0) score += 3;\n\n return cap(score, 10);\n}\n\nexport interface DuplicateDetail {\n headingA: string;\n headingB: string;\n similarity: number;\n sample: string;\n}\n\n/** 21. Duplicate Content Blocks */\nexport function scoreDuplicateContent(html: string): number {\n return scoreDuplicateContentDetailed(html).score;\n}\n\n/** Detailed duplicate detection — returns score + duplicate pairs found. */\nexport function scoreDuplicateContentDetailed(html: string): { score: number; duplicates: DuplicateDetail[] } {\n const sections = extractDuplicateContentSections(html);\n if (sections.length < 2) return { score: 10, duplicates: [] };\n\n // Total non-boilerplate paragraphs across all sections (for severity scaling)\n const totalParagraphs = sections.reduce((sum, s) => sum + s.paragraphs.length, 0);\n\n const duplicates: DuplicateDetail[] = [];\n let dupParagraphCount = 0;\n\n for (let i = 0; i < sections.length; i++) {\n for (let j = i + 1; j < sections.length; j++) {\n for (const pA of sections[i].paragraphs) {\n for (const pB of sections[j].paragraphs) {\n const sim = shingleJaccardSimilarity(pA.shingles, pB.shingles);\n if (sim > 0.4) {\n dupParagraphCount++;\n duplicates.push({\n headingA: sections[i].heading,\n headingB: sections[j].heading,\n similarity: Math.round(sim * 100),\n sample: pA.text.slice(0, 80),\n });\n break;\n }\n }\n }\n }\n }\n\n if (dupParagraphCount === 0) return { score: 10, duplicates: [] };\n\n // Scale severity by proportion of page content that's duplicated\n const dupRatio = totalParagraphs > 0 ? dupParagraphCount / totalParagraphs : 0;\n\n let score: number;\n if (dupParagraphCount === 1 && dupRatio <= 0.05) {\n score = 6;\n } else if (dupParagraphCount === 1) {\n score = 4;\n } else if (dupParagraphCount === 2) {\n score = 2;\n } else {\n // 3+ duplicate paragraphs — serious content quality issue\n score = 0;\n }\n\n return { score, duplicates };\n}\n\n// ─── Scoring function map ───────────────────────────────────────────────────\n\nconst SCORING_FUNCTIONS: Record<string, (html: string, url?: string) => number> = {\n schema_markup: scoreSchemaMarkup,\n qa_content_format: scoreQAFormat,\n clean_html: scoreCleanHtml,\n faq_section: scoreFaqSection,\n original_data: scoreOriginalData,\n query_answer_alignment: scoreQueryAnswerAlignment,\n content_freshness: scoreContentFreshness,\n table_list_extractability: scoreTableListExtractability,\n direct_answer_density: scoreDirectAnswerDensity,\n semantic_html: scoreSemanticHtml,\n fact_density: scoreFactDensity,\n definition_patterns: scoreDefinitionPatterns,\n canonical_url: scoreCanonicalUrl,\n visible_date_signal: scoreVisibleDateSignal,\n citation_ready_writing: scoreCitationReadyWriting,\n answer_first_placement: scoreAnswerFirstPlacement,\n evidence_packaging: scoreEvidencePackaging,\n helpful_purpose_alignment: scoreHelpfulPurposeAlignment,\n first_hand_experience_signals: scoreFirstHandExperienceSignals,\n entity_disambiguation: scoreEntityDisambiguation,\n extraction_friction: scoreExtractionFriction,\n creator_transparency: scoreCreatorTransparency,\n methodology_transparency: scoreMethodologyTransparency,\n image_context_ai: scoreImageContextAI,\n duplicate_content: scoreDuplicateContent,\n};\n\n// ─── Main API ───────────────────────────────────────────────────────────────\n\n/**\n * Score a single page against 25 AEO criteria.\n * Returns a 0-100 AEO score (capped at 75 for single pages) and individual criterion scores.\n */\nexport function scorePage(html: string, url?: string): PageScoreResult {\n let totalWeight = 0;\n let weightedSum = 0;\n const criterionScores: PageCriterionScore[] = [];\n\n for (const [criterion, { weight, label }] of Object.entries(PAGE_CRITERIA)) {\n const fn = SCORING_FUNCTIONS[criterion]!;\n const score = fn(html, url);\n\n criterionScores.push({ criterion, criterion_label: label, score, weight });\n weightedSum += (score / 10) * weight * 100;\n totalWeight += weight;\n }\n\n let aeoScore = totalWeight === 0 ? 0 : Math.round(weightedSum / totalWeight);\n\n // ─── Duplication Gate ──────────────────────────────────────────────────\n // Pages with significant duplicate content blocks cannot achieve high\n // scores — LLMs will flag copy-pasted sections as low-quality content\n // regardless of how well other criteria perform.\n // dup score 0: max 35 (severe — majority of content is duplicated)\n // dup score 1-2: max 45\n // dup score 3-4: max 55\n // dup score 5-6: max 65\n // dup score ≥ 7: no cap\n const dupScore = criterionScores.find(c => c.criterion === 'duplicate_content')?.score ?? 10;\n if (dupScore <= 6) {\n const dupCap = 35 + dupScore * 5;\n aeoScore = Math.min(aeoScore, dupCap);\n }\n\n const scoreCapped = aeoScore > 75;\n if (scoreCapped) aeoScore = 75;\n\n return { aeoScore, criterionScores, scoreCapped };\n}\n\n/**\n * Score all crawled pages (homepage + blogSample).\n */\nexport function scoreAllPages(siteData: SiteData): PageScoreResult[] {\n const results: PageScoreResult[] = [];\n\n if (siteData.homepage) {\n const url = siteData.protocol ? `${siteData.protocol}://${siteData.domain}` : undefined;\n results.push(scorePage(siteData.homepage.text, url));\n }\n\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n const url = page.finalUrl || undefined;\n results.push(scorePage(page.text, url));\n }\n }\n\n return results;\n}\n","/**\n * Per-page analysis for instant audit.\n * Runs 15 deterministic checks on each crawled page (no LLM).\n */\n\nimport type { PageCategory, SiteData } from './site-crawler.js';\nimport type { PageIssue, PageReview } from './types.js';\nimport { scorePage, scoreDuplicateContentDetailed } from './page-scorer.js';\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction extractTitle(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction countWords(text: string): number {\n if (!text) return 0;\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\n// ─── Individual checks ──────────────────────────────────────────────────────\n\nfunction checkMissingTitle(html: string): PageIssue | null {\n const hasTitle = /<title[^>]*>[\\s\\S]*?<\\/title>/i.test(html);\n if (!hasTitle) {\n return { check: 'missing-title', label: 'Missing <title> tag', severity: 'error' };\n }\n const title = extractTitle(html);\n if (!title) {\n return { check: 'missing-title', label: 'Empty <title> tag', severity: 'error' };\n }\n return null;\n}\n\nfunction checkMissingMetaDescription(html: string): PageIssue | null {\n const hasDesc = /<meta\\s[^>]*name=[\"']description[\"'][^>]*content=[\"'][^\"']+[\"']/i.test(html)\n || /<meta\\s[^>]*content=[\"'][^\"']+[\"'][^>]*name=[\"']description[\"']/i.test(html);\n if (!hasDesc) {\n return { check: 'missing-meta-description', label: 'Missing meta description', severity: 'error' };\n }\n return null;\n}\n\nfunction checkNoH1(html: string): PageIssue | null {\n const h1Matches = html.match(/<h1[\\s>]/gi);\n if (!h1Matches || h1Matches.length === 0) {\n return { check: 'no-h1', label: 'No <h1> tag', severity: 'error' };\n }\n return null;\n}\n\nfunction checkMultipleH1(html: string): PageIssue | null {\n const h1Matches = html.match(/<h1[\\s>]/gi);\n if (h1Matches && h1Matches.length > 1) {\n return { check: 'multiple-h1', label: `Multiple <h1> tags (${h1Matches.length})`, severity: 'error' };\n }\n return null;\n}\n\nfunction checkNoSchema(html: string): PageIssue | null {\n const hasLdJson = /<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>/i.test(html);\n if (!hasLdJson) {\n return { check: 'no-schema', label: 'No JSON-LD structured data', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkMissingCanonical(html: string): PageIssue | null {\n const hasCanonical = /<link[^>]*rel=[\"']canonical[\"'][^>]*>/i.test(html);\n if (!hasCanonical) {\n return { check: 'missing-canonical', label: 'Missing canonical link', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkMissingOgTags(html: string): PageIssue | null {\n const hasOg = /<meta\\s[^>]*property=[\"']og:/i.test(html);\n if (!hasOg) {\n return { check: 'missing-og-tags', label: 'No Open Graph tags', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkThinContent(wordCount: number): PageIssue | null {\n if (wordCount < 300) {\n return { check: 'thin-content', label: `Thin content (${wordCount} words)`, severity: 'warning' };\n }\n return null;\n}\n\nfunction checkImagesMissingAlt(html: string): PageIssue | null {\n const imgTags = html.match(/<img\\s[^>]*>/gi) || [];\n if (imgTags.length === 0) return null;\n\n let missingAlt = 0;\n for (const img of imgTags) {\n const hasAlt = /\\salt=[\"'][^\"']+[\"']/i.test(img);\n const hasEmptyAlt = /\\salt=[\"'][\"']/i.test(img); // decorative\n if (!hasAlt && !hasEmptyAlt) missingAlt++;\n }\n\n if (missingAlt > 0) {\n return {\n check: 'images-missing-alt',\n label: `${missingAlt} image${missingAlt > 1 ? 's' : ''} missing alt text`,\n severity: 'warning',\n };\n }\n return null;\n}\n\nfunction checkNoInternalLinks(html: string, url: string): PageIssue | null {\n let domain: string;\n try {\n domain = new URL(url).hostname.replace(/^www\\./, '').toLowerCase();\n } catch {\n return null;\n }\n\n const links = html.match(/<a\\s[^>]*href=[\"']([^\"'#]*?)[\"'][^>]*>/gi) || [];\n let internalCount = 0;\n\n for (const link of links) {\n const hrefMatch = link.match(/href=[\"']([^\"'#]*?)[\"']/i);\n if (!hrefMatch) continue;\n const href = hrefMatch[1];\n\n if (href.startsWith('/') && !href.startsWith('//')) {\n internalCount++;\n } else if (href.startsWith('http')) {\n try {\n const linkDomain = new URL(href).hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain === domain) internalCount++;\n } catch {\n // skip\n }\n }\n }\n\n if (internalCount === 0) {\n return { check: 'no-internal-links', label: 'No internal links found', severity: 'warning' };\n }\n return null;\n}\n\n// ─── Strength checks ────────────────────────────────────────────────────────\n\nfunction checkHasStructuredData(html: string): PageIssue | null {\n const ldBlocks = html.match(/<script[^>]*type=[\"']application\\/ld\\+json[\"'][^>]*>([\\s\\S]*?)<\\/script>/gi) || [];\n if (ldBlocks.length === 0) return null;\n\n const types = new Set<string>();\n for (const block of ldBlocks) {\n const content = block.replace(/<\\/?script[^>]*>/gi, '');\n const typeMatches = content.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/g) || [];\n for (const m of typeMatches) {\n const t = m.match(/\"@type\"\\s*:\\s*\"([^\"]+)\"/);\n if (t) types.add(t[1]);\n }\n }\n\n if (types.size > 0) {\n return {\n check: 'has-structured-data',\n label: `JSON-LD: ${Array.from(types).join(', ')}`,\n severity: 'info',\n };\n }\n return null;\n}\n\nfunction checkHasQuestionHeadings(html: string): PageIssue | null {\n const headings = html.match(/<h[2-4][^>]*>[\\s\\S]*?<\\/h[2-4]>/gi) || [];\n let questionCount = 0;\n\n for (const h of headings) {\n const text = h.replace(/<[^>]*>/g, '').trim();\n if (/\\?$/.test(text) || /^(what|how|why|when|where|who|which|can|do|does|is|are|should|will)\\b/i.test(text)) {\n questionCount++;\n }\n }\n\n if (questionCount > 0) {\n return {\n check: 'has-question-headings',\n label: `${questionCount} question-format heading${questionCount > 1 ? 's' : ''}`,\n severity: 'info',\n };\n }\n return null;\n}\n\n// ─── V2 issue checks ───────────────────────────────────────────────────────\n\nfunction checkNoAnswerBlock(html: string): PageIssue | null {\n const bodyMatch = html.match(/<body[^>]*>([\\s\\S]*)/i);\n const bodyHtml = bodyMatch ? bodyMatch[1] : html;\n const earlyParagraphs = bodyHtml.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)?.slice(0, 5) || [];\n const bodyText = bodyHtml.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n const first300Words = bodyText.split(/\\s+/).slice(0, 300).join(' ');\n\n for (const p of earlyParagraphs) {\n const pText = p.replace(/<[^>]*>/g, '').trim();\n const wordCount = pText.split(/\\s+/).length;\n if (wordCount >= 40 && wordCount <= 80 && first300Words.includes(pText.slice(0, 50))) {\n return null; // Found an answer block\n }\n }\n\n return { check: 'no-answer-block', label: 'No short answer block (40-80 words) in first 300 words', severity: 'warning' };\n}\n\nfunction checkNoEvidence(html: string, url: string): PageIssue | null {\n const text = getTextContent(html);\n\n // Check for inline citations\n const paragraphs = html.match(/<p[^>]*>[\\s\\S]*?<\\/p>/gi) || [];\n let inlineCitations = 0;\n for (const p of paragraphs) {\n const links = p.match(/<a[^>]*href=[\"'](https?:\\/\\/[^\"']+)[\"'][^>]*>/gi) || [];\n inlineCitations += links.length;\n }\n\n // Check for attribution phrases\n const attributionPhrases = text.match(/\\baccording\\s+to\\s+[A-Z]|\\b[A-Z][a-z]+\\s+(found|says|reports|notes|states)\\b/gi) || [];\n\n if (inlineCitations === 0 && attributionPhrases.length === 0) {\n return { check: 'no-evidence', label: 'No inline citations or attribution phrases', severity: 'warning' };\n }\n return null;\n}\n\nfunction checkHasCitationReadyContent(html: string): PageIssue | null {\n const text = getTextContent(html);\n const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10);\n\n // Check for self-contained quotable sentences with evidence\n let quotable = 0;\n for (const s of sentences) {\n const hasData = /\\d+(\\.\\d+)?%|\\$[\\d,.]+|\\b\\d{1,3}(,\\d{3})+\\b/.test(s);\n const hasSource = /\\b[A-Z][a-z]+(?:\\s+[A-Z][a-z]+)*\\b/.test(s);\n const isShort = s.trim().split(/\\s+/).length < 30;\n if (hasData && hasSource && isShort) quotable++;\n }\n\n if (quotable >= 3) {\n return { check: 'has-citation-ready-content', label: `${quotable} citation-ready sentences with data and sources`, severity: 'info' };\n }\n return null;\n}\n\nfunction checkDuplicateContentBlocks(html: string): PageIssue | null {\n const { score, duplicates } = scoreDuplicateContentDetailed(html);\n if (score <= 6 && duplicates.length > 0) {\n const first = duplicates[0];\n const label = duplicates.length === 1\n ? `Duplicate content: '${first.headingA}' and '${first.headingB}' share ${first.similarity}% similar text (\"${first.sample}...\")`\n : `${duplicates.length} duplicate blocks found (e.g. '${first.headingA}' and '${first.headingB}' — \"${first.sample}...\")`;\n return { check: 'duplicate-content', label, severity: score <= 3 ? 'error' : 'warning' };\n }\n return null;\n}\n\n// ─── Main analyzers ─────────────────────────────────────────────────────────\n\nexport function analyzePage(html: string, url: string, category: PageCategory): PageReview {\n const title = extractTitle(html);\n const textContent = getTextContent(html);\n const wordCount = countWords(textContent);\n\n const issues: PageIssue[] = [];\n const strengths: PageIssue[] = [];\n\n // Issue checks\n const issueChecks = [\n checkMissingTitle(html),\n checkMissingMetaDescription(html),\n checkNoH1(html),\n checkMultipleH1(html),\n checkNoSchema(html),\n checkMissingCanonical(html),\n checkMissingOgTags(html),\n checkThinContent(wordCount),\n checkImagesMissingAlt(html),\n checkNoInternalLinks(html, url),\n checkNoAnswerBlock(html),\n checkNoEvidence(html, url),\n checkDuplicateContentBlocks(html),\n ];\n\n for (const result of issueChecks) {\n if (result) issues.push(result);\n }\n\n // Strength checks\n const strengthChecks = [\n checkHasStructuredData(html),\n checkHasQuestionHeadings(html),\n checkHasCitationReadyContent(html),\n ];\n\n for (const result of strengthChecks) {\n if (result) strengths.push(result);\n }\n\n const { aeoScore, criterionScores } = scorePage(html, url);\n return { url, title, category, wordCount, issues, strengths, aeoScore, criterionScores };\n}\n\nexport function analyzeAllPages(siteData: SiteData): PageReview[] {\n const reviews: PageReview[] = [];\n\n // Analyze homepage\n if (siteData.homepage) {\n const url = `${siteData.protocol}://${siteData.domain}`;\n reviews.push(analyzePage(siteData.homepage.text, url, siteData.homepage.category || 'homepage'));\n }\n\n // Analyze all pages in blogSample\n if (siteData.blogSample) {\n for (const page of siteData.blogSample) {\n const url = page.finalUrl || 'unknown';\n reviews.push(analyzePage(page.text, url, page.category || 'content'));\n }\n }\n\n return reviews;\n}\n","/**\n * Link graph analysis for full-site AEO audits.\n * Builds an internal link graph, detects orphan pages, pillar pages,\n * hub pages, and topic clusters from crawled page data.\n */\n\nimport type { FetchResult, PageCategory } from './site-crawler.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface LinkEdge {\n source: string;\n target: string;\n anchorText: string;\n}\n\nexport interface PageNode {\n url: string;\n title: string;\n wordCount: number;\n category: PageCategory;\n inDegree: number;\n outDegree: number;\n depth: number;\n isPillar: boolean;\n isHub: boolean;\n isOrphan: boolean;\n}\n\nexport interface TopicCluster {\n pillarUrl: string;\n pillarTitle: string;\n spokes: string[];\n cohesion: number;\n}\n\nexport interface LinkGraphStats {\n totalPages: number;\n totalEdges: number;\n orphanPages: number;\n pillarPages: number;\n hubPages: number;\n avgDepth: number;\n maxDepth: number;\n clusters: number;\n}\n\nexport interface LinkGraph {\n nodes: Map<string, PageNode>;\n edges: LinkEdge[];\n stats: LinkGraphStats;\n clusters: TopicCluster[];\n}\n\n// ─── Serialization (Map -> plain object for JSON) ───────────────────────────\n\nexport interface SerializedLinkGraph {\n nodes: PageNode[];\n stats: LinkGraphStats;\n clusters: TopicCluster[];\n}\n\nexport function serializeLinkGraph(graph: LinkGraph): SerializedLinkGraph {\n return {\n nodes: Array.from(graph.nodes.values()),\n stats: graph.stats,\n clusters: graph.clusters,\n };\n}\n\n// ─── URL normalization (matches full-site-crawler.ts:421-429) ───────────────\n\nfunction normalizeUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return (parsed.origin + parsed.pathname.replace(/\\/+$/, '') + parsed.search).toLowerCase();\n } catch {\n return url.toLowerCase();\n }\n}\n\n// ─── Resource / skip-path patterns (same as full-site-crawler.ts) ───────────\n\nconst RESOURCE_EXTENSIONS = /\\.(js|css|png|jpg|jpeg|gif|svg|ico|pdf|xml|txt|woff|woff2|ttf|eot|mp4|mp3|webp|avif|zip|gz|tar|json)$/i;\n\nconst SKIP_PATH_PATTERNS = /^\\/(api|wp-admin|wp-json|static|assets|_next|auth|login|signup|cart|checkout|admin|feed|xmlrpc)\\b/i;\n\n// ─── HTML helpers (mirrors page-analyzer.ts) ────────────────────────────────\n\nfunction extractTitle(html: string): string {\n const match = html.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/i);\n return match ? match[1].replace(/\\s+/g, ' ').trim() : '';\n}\n\nfunction getTextContent(html: string): string {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction countWords(text: string): number {\n if (!text) return 0;\n return text.split(/\\s+/).filter(w => w.length > 0).length;\n}\n\n// ─── Link extraction with anchor text ───────────────────────────────────────\n\n/**\n * Extract internal links from HTML with their anchor text.\n * Similar to extractInternalLinks in full-site-crawler.ts but returns LinkEdge[].\n */\nexport function extractLinksWithAnchors(\n html: string,\n sourceUrl: string,\n domain: string,\n): LinkEdge[] {\n const cleanDomain = domain.replace(/^www\\./, '').toLowerCase();\n const edges: LinkEdge[] = [];\n const seen = new Set<string>();\n\n // Match <a ...>...</a> tags to get both href and anchor text\n const anchorRegex = /<a\\s[^>]*href=\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>/gi;\n let match: RegExpExecArray | null;\n\n while ((match = anchorRegex.exec(html)) !== null) {\n const href = match[1];\n const rawAnchor = match[2];\n\n if (!href || !href.trim()) continue;\n\n let fullUrl: string;\n\n if (href.startsWith('//')) {\n fullUrl = `https:${href}`;\n } else if (href.startsWith('/')) {\n if (href.startsWith('/#')) continue;\n fullUrl = href === '/' ? `https://${domain}` : `https://${domain}${href}`;\n } else if (href.startsWith('http')) {\n fullUrl = href;\n } else if (\n href.startsWith('#') || href.startsWith('?') ||\n href.startsWith('mailto:') || href.startsWith('tel:') ||\n href.startsWith('javascript:')\n ) {\n continue;\n } else {\n fullUrl = `https://${domain}/${href}`;\n }\n\n try {\n const parsed = new URL(fullUrl);\n const linkDomain = parsed.hostname.replace(/^www\\./, '').toLowerCase();\n if (linkDomain !== cleanDomain) continue;\n\n parsed.hash = '';\n const path = parsed.pathname;\n\n // Allow root path (homepage) - only skip empty string (shouldn't happen)\n if (path === '') continue;\n if (RESOURCE_EXTENSIONS.test(path)) continue;\n if (SKIP_PATH_PATTERNS.test(path)) continue;\n\n const normalized = normalizeUrl(fullUrl);\n const sourceNorm = normalizeUrl(sourceUrl);\n\n // Skip self-links\n if (normalized === sourceNorm) continue;\n\n // Deduplicate edges from same source to same target\n const edgeKey = `${sourceNorm}->${normalized}`;\n if (seen.has(edgeKey)) continue;\n seen.add(edgeKey);\n\n // Strip HTML tags from anchor text\n const anchorText = rawAnchor.replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\n\n edges.push({\n source: sourceNorm,\n target: normalized,\n anchorText,\n });\n } catch {\n continue;\n }\n }\n\n return edges;\n}\n\n// ─── BFS depth calculation ──────────────────────────────────────────────────\n\n/**\n * BFS from homepage to compute click depth for each node.\n * Unreachable pages get Infinity.\n */\nexport function calculateDepths(\n nodes: Map<string, PageNode>,\n adjacency: Map<string, Set<string>>,\n homepageUrl: string,\n): void {\n const homeNorm = normalizeUrl(homepageUrl);\n\n // Set all to Infinity first\n for (const node of nodes.values()) {\n node.depth = Infinity;\n }\n\n // Homepage gets depth 0\n const homeNode = nodes.get(homeNorm);\n if (homeNode) {\n homeNode.depth = 0;\n }\n\n // BFS\n const queue: string[] = [homeNorm];\n const visited = new Set<string>([homeNorm]);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n const currentNode = nodes.get(current);\n if (!currentNode) continue;\n const nextDepth = currentNode.depth + 1;\n\n const neighbors = adjacency.get(current);\n if (!neighbors) continue;\n\n for (const neighbor of neighbors) {\n if (visited.has(neighbor)) continue;\n visited.add(neighbor);\n\n const neighborNode = nodes.get(neighbor);\n if (neighborNode) {\n neighborNode.depth = nextDepth;\n queue.push(neighbor);\n }\n }\n }\n}\n\n// ─── Pillar detection ───────────────────────────────────────────────────────\n\nconst PILLAR_CATEGORIES: Set<PageCategory> = new Set(['blog', 'content', 'resources', 'docs']);\n\n/**\n * Detect pillar pages: long content with significant in/out linking.\n * wordCount >= 1500 AND inDegree >= 3 AND outDegree >= 3\n * AND category in blog/content/resources/docs AND not homepage (depth > 0).\n */\nexport function detectPillars(nodes: Map<string, PageNode>): void {\n for (const node of nodes.values()) {\n node.isPillar =\n node.wordCount >= 1500 &&\n node.inDegree >= 3 &&\n node.outDegree >= 3 &&\n PILLAR_CATEGORIES.has(node.category) &&\n node.depth > 0;\n }\n}\n\n// ─── Hub detection ──────────────────────────────────────────────────────────\n\nconst HUB_CATEGORIES: Set<PageCategory> = new Set(['homepage', 'resources', 'docs']);\n\n/**\n * Detect hub pages: high outDegree navigation/index pages.\n * outDegree >= 10 with category homepage/resources/docs, OR outDegree >= 15.\n */\nexport function detectHubs(nodes: Map<string, PageNode>): void {\n for (const node of nodes.values()) {\n node.isHub =\n (node.outDegree >= 10 && HUB_CATEGORIES.has(node.category)) ||\n node.outDegree >= 15;\n }\n}\n\n// ─── Cluster detection ──────────────────────────────────────────────────────\n\n/**\n * Detect topic clusters around pillar pages.\n * For each pillar, gather spokes (pages linked to/from pillar).\n * Cohesion = actual edges between cluster members / possible edges.\n * Minimum 2 spokes to form a cluster.\n */\nexport function detectClusters(\n nodes: Map<string, PageNode>,\n edges: LinkEdge[],\n): TopicCluster[] {\n const clusters: TopicCluster[] = [];\n\n // Build quick lookup for edges\n const edgeSet = new Set<string>();\n for (const edge of edges) {\n edgeSet.add(`${edge.source}->${edge.target}`);\n }\n\n for (const node of nodes.values()) {\n if (!node.isPillar) continue;\n\n const pillarNorm = normalizeUrl(node.url);\n const spokeSet = new Set<string>();\n\n // Find spokes: pages linked to/from this pillar\n for (const edge of edges) {\n if (edge.source === pillarNorm && nodes.has(edge.target)) {\n spokeSet.add(edge.target);\n }\n if (edge.target === pillarNorm && nodes.has(edge.source)) {\n spokeSet.add(edge.source);\n }\n }\n\n // Remove the pillar itself from spokes\n spokeSet.delete(pillarNorm);\n\n if (spokeSet.size < 2) continue;\n\n const spokes = Array.from(spokeSet);\n\n // Compute cohesion: edges between cluster members (pillar + spokes)\n const members = [pillarNorm, ...spokes];\n let actualEdges = 0;\n const possibleEdges = members.length * (members.length - 1); // directed\n\n for (const from of members) {\n for (const to of members) {\n if (from === to) continue;\n if (edgeSet.has(`${from}->${to}`)) {\n actualEdges++;\n }\n }\n }\n\n const cohesion = possibleEdges > 0 ? Math.round((actualEdges / possibleEdges) * 100) : 0;\n\n clusters.push({\n pillarUrl: node.url,\n pillarTitle: node.title,\n spokes,\n cohesion,\n });\n }\n\n return clusters;\n}\n\n// ─── Main graph builder ─────────────────────────────────────────────────────\n\n/**\n * Build a link graph from crawled pages.\n * @param pages - Array of FetchResult from full-site crawl\n * @param domain - The site domain\n * @param homepageUrl - Full URL of the homepage (e.g. https://example.com)\n */\nexport function buildLinkGraph(\n pages: FetchResult[],\n domain: string,\n homepageUrl: string,\n): LinkGraph {\n const nodes = new Map<string, PageNode>();\n const allEdges: LinkEdge[] = [];\n const adjacency = new Map<string, Set<string>>();\n const inDegreeMap = new Map<string, number>();\n\n // Phase 1: Create nodes from all pages\n for (const page of pages) {\n const url = page.finalUrl || `https://${domain}`;\n const norm = normalizeUrl(url);\n\n if (nodes.has(norm)) continue;\n\n const title = extractTitle(page.text);\n const text = getTextContent(page.text);\n const wordCount = countWords(text);\n\n nodes.set(norm, {\n url: norm,\n title,\n wordCount,\n category: page.category || 'content',\n inDegree: 0,\n outDegree: 0,\n depth: Infinity,\n isPillar: false,\n isHub: false,\n isOrphan: false,\n });\n }\n\n // Phase 2: Extract edges\n for (const page of pages) {\n const url = page.finalUrl || `https://${domain}`;\n const sourceNorm = normalizeUrl(url);\n\n const edges = extractLinksWithAnchors(page.text, url, domain);\n\n for (const edge of edges) {\n // Normalize edge source/target to match node keys\n const targetNorm = normalizeUrl(edge.target);\n\n // Only count edges to known pages\n if (!nodes.has(targetNorm)) continue;\n\n allEdges.push({\n source: sourceNorm,\n target: targetNorm,\n anchorText: edge.anchorText,\n });\n\n // Build adjacency\n if (!adjacency.has(sourceNorm)) {\n adjacency.set(sourceNorm, new Set());\n }\n adjacency.get(sourceNorm)!.add(targetNorm);\n\n // Track in-degree\n inDegreeMap.set(targetNorm, (inDegreeMap.get(targetNorm) || 0) + 1);\n }\n }\n\n // Phase 3: Set degrees\n for (const [url, node] of nodes) {\n node.inDegree = inDegreeMap.get(url) || 0;\n node.outDegree = adjacency.get(url)?.size || 0;\n }\n\n // Phase 4: BFS depths\n calculateDepths(nodes, adjacency, homepageUrl);\n\n // Phase 5: Detect pillars and hubs\n detectPillars(nodes);\n detectHubs(nodes);\n\n // Phase 6: Mark orphans (zero inbound links, not homepage)\n const homeNorm = normalizeUrl(homepageUrl);\n for (const [url, node] of nodes) {\n node.isOrphan = node.inDegree === 0 && url !== homeNorm;\n }\n\n // Phase 7: Detect clusters\n const clusters = detectClusters(nodes, allEdges);\n\n // Phase 8: Compute stats\n const depthValues = Array.from(nodes.values())\n .map(n => n.depth)\n .filter(d => d !== Infinity);\n const avgDepth = depthValues.length > 0\n ? Math.round((depthValues.reduce((s, d) => s + d, 0) / depthValues.length) * 10) / 10\n : 0;\n const maxDepth = depthValues.length > 0 ? Math.max(...depthValues) : 0;\n\n const stats: LinkGraphStats = {\n totalPages: nodes.size,\n totalEdges: allEdges.length,\n orphanPages: Array.from(nodes.values()).filter(n => n.isOrphan).length,\n pillarPages: Array.from(nodes.values()).filter(n => n.isPillar).length,\n hubPages: Array.from(nodes.values()).filter(n => n.isHub).length,\n avgDepth,\n maxDepth,\n clusters: clusters.length,\n };\n\n return { nodes, edges: allEdges, stats, clusters };\n}\n","/**\n * Fix Plan Engine - generates actionable, phased fix plans from audit scores.\n * Runs alongside the existing opportunities system (no breaking changes).\n * Uses criterion scores, optional per-page data, and optional link graph\n * to produce structured fix plans with code examples and dependency ordering.\n */\n\nimport type { CriterionResult } from './site-crawler.js';\nimport type { PageReview, PageCriterionScore } from './types.js';\nimport type { LinkGraph } from './link-graph.js';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface FixAction {\n id: string;\n criterion: string;\n criterionId: string;\n title: string;\n description: string;\n impact: 'critical' | 'high' | 'medium' | 'low';\n effort: 'trivial' | 'low' | 'medium' | 'high';\n impactScore: number;\n category: 'content' | 'structure' | 'discovery' | 'trust';\n steps: string[];\n codeExample?: string;\n successCriteria: string;\n dependsOn?: string[];\n affectedPages?: string[];\n pageCount?: number;\n}\n\nexport interface FixPhase {\n phase: number;\n title: string;\n description: string;\n fixes: FixAction[];\n estimatedImpact: number;\n}\n\nexport interface FixPlanSummary {\n criticalCount: number;\n highCount: number;\n mediumCount: number;\n lowCount: number;\n quickWinCount: number;\n topOpportunity: string;\n estimatedTotalEffort: string;\n}\n\nexport interface FixPlan {\n domain: string;\n generatedAt: string;\n overallScore: number;\n projectedScore: number;\n totalFixes: number;\n phases: FixPhase[];\n quickWins: FixAction[];\n summary: FixPlanSummary;\n}\n\n// ─── Criterion weights (mirrored from scoring.ts) ────────────────────────────\n\nconst CRITERION_WEIGHTS: Record<string, number> = {\n // Content Substance (~55%)\n topic_coherence: 0.14,\n original_data: 0.10,\n content_depth: 0.07,\n fact_density: 0.06,\n direct_answer_density: 0.05,\n qa_content_format: 0.04,\n query_answer_alignment: 0.04,\n faq_section: 0.03,\n helpful_purpose_alignment: 0.03,\n first_hand_experience_signals: 0.03,\n // Content Organization (~30%)\n entity_consistency: 0.05,\n internal_linking: 0.04,\n content_freshness: 0.04,\n schema_markup: 0.03,\n author_schema_depth: 0.03,\n table_list_extractability: 0.03,\n creator_transparency: 0.02,\n methodology_transparency: 0.02,\n definition_patterns: 0.015,\n visible_date_signal: 0.015,\n semantic_html: 0.02,\n clean_html: 0.02,\n // Technical Plumbing (~15%)\n content_cannibalization: 0.02,\n duplicate_content: 0.05, cross_page_duplication: 0.03,\n llms_txt: 0.01,\n robots_txt: 0.01,\n content_velocity: 0.02,\n content_licensing: 0.01,\n sitemap_completeness: 0.01,\n canonical_url: 0.005,\n rss_feed: 0.005,\n schema_coverage: 0.005,\n speakable_schema: 0.005,\n // V2 Criteria (~15%)\n citation_ready_writing: 0.04,\n answer_first_placement: 0.03,\n evidence_packaging: 0.03,\n entity_disambiguation: 0.02,\n extraction_friction: 0.02,\n image_context_ai: 0.005,\n};\n\n// ─── Phase configuration ────────────────────────────────────────────────────\n\nconst PHASE_CONFIG: { phase: number; title: string; description: string; criteria: string[] }[] = [\n {\n phase: 1,\n title: 'Foundation',\n description: 'Discovery and structural fixes that enable AI crawlers to access and parse your content.',\n criteria: ['robots_txt', 'llms_txt', 'canonical_url', 'clean_html', 'sitemap_completeness'],\n },\n {\n phase: 2,\n title: 'Content',\n description: 'Content quality and format improvements that make your pages citable by AI engines.',\n criteria: [\n 'qa_content_format', 'faq_section', 'original_data', 'definition_patterns',\n 'direct_answer_density', 'fact_density', 'content_freshness',\n 'table_list_extractability', 'query_answer_alignment', 'visible_date_signal',\n 'topic_coherence', 'content_depth',\n 'citation_ready_writing', 'answer_first_placement', 'evidence_packaging',\n 'entity_disambiguation', 'helpful_purpose_alignment', 'first_hand_experience_signals',\n 'duplicate_content', 'cross_page_duplication',\n ],\n },\n {\n phase: 3,\n title: 'Authority',\n description: 'Trust signals, schema depth, and semantic structure that establish credibility with AI engines.',\n criteria: [\n 'schema_markup', 'schema_coverage', 'speakable_schema', 'author_schema_depth',\n 'creator_transparency', 'methodology_transparency', 'content_licensing', 'entity_consistency', 'semantic_html',\n 'image_context_ai', 'extraction_friction',\n ],\n },\n {\n phase: 4,\n title: 'Architecture',\n description: 'Site architecture, linking patterns, and publishing cadence that support long-term AI visibility.',\n criteria: ['internal_linking', 'content_velocity', 'content_cannibalization', 'rss_feed'],\n },\n];\n\n// ─── Criterion → category mapping ───────────────────────────────────────────\n\nconst CRITERION_CATEGORY: Record<string, FixAction['category']> = {\n robots_txt: 'discovery', llms_txt: 'discovery', canonical_url: 'structure',\n clean_html: 'structure', sitemap_completeness: 'discovery',\n qa_content_format: 'content', faq_section: 'content', original_data: 'content',\n definition_patterns: 'content', direct_answer_density: 'content', fact_density: 'content',\n content_freshness: 'content', table_list_extractability: 'content',\n query_answer_alignment: 'content', visible_date_signal: 'content',\n schema_markup: 'trust', schema_coverage: 'trust', speakable_schema: 'trust',\n author_schema_depth: 'trust', creator_transparency: 'trust', methodology_transparency: 'trust',\n content_licensing: 'trust', entity_consistency: 'trust',\n semantic_html: 'structure', internal_linking: 'structure', content_velocity: 'content',\n content_cannibalization: 'content', duplicate_content: 'content', cross_page_duplication: 'content', rss_feed: 'discovery',\n topic_coherence: 'content', content_depth: 'content',\n citation_ready_writing: 'content', answer_first_placement: 'content',\n evidence_packaging: 'content', helpful_purpose_alignment: 'content', first_hand_experience_signals: 'content', entity_disambiguation: 'content',\n extraction_friction: 'structure', image_context_ai: 'trust',\n};\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction impactFromScore(score: number): FixAction['impact'] {\n if (score <= 3) return 'critical';\n if (score <= 5) return 'high';\n if (score <= 7) return 'medium';\n return 'low';\n}\n\nfunction effortForCriterion(criterion: string, score: number): FixAction['effort'] {\n // Low-effort criteria (file creation, config changes)\n const trivialCriteria = ['llms_txt', 'robots_txt', 'canonical_url', 'content_licensing', 'visible_date_signal'];\n const lowCriteria = ['rss_feed', 'sitemap_completeness', 'speakable_schema', 'author_schema_depth', 'creator_transparency', 'methodology_transparency', 'semantic_html', 'definition_patterns', 'content_freshness'];\n const highCriteria = ['original_data', 'content_velocity', 'content_cannibalization'];\n\n if (trivialCriteria.includes(criterion)) return score <= 3 ? 'low' : 'trivial';\n if (lowCriteria.includes(criterion)) return score <= 3 ? 'medium' : 'low';\n if (highCriteria.includes(criterion)) return score <= 5 ? 'high' : 'medium';\n return score <= 3 ? 'medium' : 'low';\n}\n\nfunction getAffectedPages(\n criterion: string,\n pages?: PageReview[],\n threshold = 7,\n): string[] | undefined {\n if (!pages || pages.length === 0) return undefined;\n const affected = pages.filter(p => {\n const cs = p.criterionScores?.find((c: PageCriterionScore) => c.criterion === criterion);\n return cs && cs.score < threshold;\n });\n if (affected.length === 0) return undefined;\n return affected.map(p => p.url);\n}\n\nfunction effortToHours(effort: FixAction['effort']): number {\n switch (effort) {\n case 'trivial': return 0.5;\n case 'low': return 1;\n case 'medium': return 3;\n case 'high': return 8;\n }\n}\n\n// ─── Fix generator type ─────────────────────────────────────────────────────\n\ntype FixGenerator = (\n criterion: CriterionResult,\n pages?: PageReview[],\n linkGraph?: LinkGraph,\n) => FixAction[];\n\n// ─── Fix Generators ────────────────────────────────────────────────────────\n\nconst FIX_GENERATORS: Record<string, FixGenerator> = {\n\n llms_txt: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('llms_txt', c.score);\n const fixes: FixAction[] = [];\n if (c.score <= 6) {\n fixes.push({\n id: 'fix-llms-txt-create',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create /llms.txt file',\n description: 'Add a machine-readable llms.txt file at your domain root that describes your site, services, and key pages for AI engines.',\n impact, effort,\n impactScore: 0, // calculated later\n category: 'discovery',\n steps: [\n 'Create a file named llms.txt in your site root',\n 'Add site name, description, and core URLs in markdown format',\n 'Include key service/product pages and their descriptions',\n 'Deploy and verify access at yourdomain.com/llms.txt',\n ],\n codeExample: `# Site Name\\n> One-line site description\\n\\n## Core Pages\\n- [About](/about): Company overview\\n- [Services](/services): Service offerings\\n- [Blog](/blog): Latest articles\\n\\n## Key Topics\\n- Topic 1\\n- Topic 2`,\n successCriteria: '/llms.txt returns 200 with valid markdown content',\n });\n }\n if (c.score <= 3) {\n fixes.push({\n id: 'fix-llms-txt-full',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add llms-full.txt with extended content',\n description: 'Create a comprehensive llms-full.txt with detailed page descriptions, content summaries, and topic taxonomy.',\n impact: 'medium', effort: 'low',\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create llms-full.txt alongside llms.txt',\n 'Include full page descriptions with word counts',\n 'Add topic categories and content clusters',\n 'Link from llms.txt to llms-full.txt',\n ],\n successCriteria: '/llms-full.txt returns 200 with comprehensive site map',\n });\n }\n return fixes;\n },\n\n schema_markup: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('schema_markup', c.score);\n const affected = getAffectedPages('schema_markup', pages);\n const fixes: FixAction[] = [{\n id: 'fix-schema-markup',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add JSON-LD structured data',\n description: 'Implement Organization, WebSite, and page-specific schema.org JSON-LD to help AI engines extract your content.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add Organization JSON-LD to your homepage with name, url, logo, sameAs',\n 'Add WebSite schema with SearchAction',\n 'Add page-specific schema (Article, Service, Product, FAQPage) to relevant pages',\n 'Validate with Google Rich Results Test',\n ],\n codeExample: `<script type=\"application/ld+json\">\\n{\\n \"@context\": \"https://schema.org\",\\n \"@type\": \"Organization\",\\n \"name\": \"Your Company\",\\n \"url\": \"https://example.com\",\\n \"logo\": \"https://example.com/logo.png\",\\n \"sameAs\": [\\n \"https://twitter.com/company\",\\n \"https://linkedin.com/company/company\"\\n ]\\n}\\n</script>`,\n successCriteria: 'Homepage and key pages have valid JSON-LD schema',\n dependsOn: ['fix-clean-html-structure'],\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n return fixes;\n },\n\n qa_content_format: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('qa_content_format', c.score);\n const affected = getAffectedPages('qa_content_format', pages);\n return [{\n id: 'fix-qa-format',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add question-based headings',\n description: 'Restructure content with H2/H3 question headings that match how users query AI assistants.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify top user questions for each page topic',\n 'Convert section headings to question format (What, How, Why, When)',\n 'Follow each question heading with a direct 2-3 sentence answer',\n 'Add a summary answer box at the top of long-form content',\n ],\n successCriteria: 'At least 50% of H2/H3 headings use question format',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n clean_html: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('clean_html', c.score);\n const affected = getAffectedPages('clean_html', pages);\n const fixes: FixAction[] = [{\n id: 'fix-clean-html-structure',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix HTML structure and meta tags',\n description: 'Ensure clean, well-structured HTML with proper meta tags, HTTPS, and parseable content for AI crawlers.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Enable HTTPS and redirect HTTP to HTTPS',\n 'Add proper <title>, meta description, and viewport meta tags',\n 'Fix HTML validation errors (unclosed tags, invalid nesting)',\n 'Ensure content is server-rendered (not client-side only)',\n ],\n successCriteria: 'Pages pass HTML validation with proper meta tags and HTTPS',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n return fixes;\n },\n\n entity_consistency: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('entity_consistency', c.score);\n return [{\n id: 'fix-entity-consistency',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Strengthen entity authority (NAP)',\n description: 'Add consistent name, address, phone (NAP) and sameAs links across all pages to strengthen entity recognition.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Ensure company name is consistent across all pages',\n 'Add Organization schema with full NAP details',\n 'Include sameAs links to social profiles and directories',\n 'Add logo and brand marks consistently',\n ],\n successCriteria: 'Organization schema present with consistent NAP on all pages',\n }];\n },\n\n robots_txt: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('robots_txt', c.score);\n return [{\n id: 'fix-robots-txt',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Configure robots.txt for AI crawlers',\n description: 'Update robots.txt to explicitly allow AI crawlers and include sitemap directive.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create or update robots.txt at domain root',\n 'Add User-agent rules for GPTBot, ClaudeBot, PerplexityBot',\n 'Include Sitemap directive pointing to sitemap.xml',\n 'Verify no accidental Disallow rules blocking content pages',\n ],\n codeExample: `User-agent: *\\nAllow: /\\n\\nUser-agent: GPTBot\\nAllow: /\\n\\nUser-agent: ClaudeBot\\nAllow: /\\n\\nUser-agent: PerplexityBot\\nAllow: /\\n\\nSitemap: https://example.com/sitemap.xml`,\n successCriteria: 'robots.txt returns 200 with AI crawler directives and Sitemap',\n }];\n },\n\n faq_section: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('faq_section', c.score);\n const affected = getAffectedPages('faq_section', pages);\n return [{\n id: 'fix-faq-section',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Build FAQ sections with schema',\n description: 'Create FAQ content with FAQPage schema markup on key pages to become a direct answer source for AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify 8-10 most common customer questions per service area',\n 'Create dedicated FAQ page with categorized Q&A pairs',\n 'Add inline FAQ sections to key service/product pages',\n 'Implement FAQPage JSON-LD schema on all FAQ content',\n ],\n successCriteria: 'FAQ page exists with FAQPage schema, key pages have inline FAQ sections',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n helpful_purpose_alignment: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('helpful_purpose_alignment', c.score);\n const affected = getAffectedPages('helpful_purpose_alignment', pages);\n return [{\n id: 'fix-helpful-purpose-alignment',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Make pages solve the user task faster',\n description: 'Reduce search-first filler and rewrite pages so the promised task is resolved quickly with concrete guidance, tradeoffs, and next steps.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Rewrite first paragraphs to answer the user need within the first 150-300 words',\n 'Remove generic intros like \"In this guide\" and broad filler that could fit any topic',\n 'Add concrete decision help: tradeoffs, risks, constraints, and next steps',\n 'Move aggressive CTAs below the first useful answer block',\n ],\n successCriteria: 'Pages lead with task-solving guidance instead of generic search-first framing',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n first_hand_experience_signals: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('first_hand_experience_signals', c.score);\n const affected = getAffectedPages('first_hand_experience_signals', pages);\n return [{\n id: 'fix-first-hand-experience',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add first-hand experience signals',\n description: 'Show real use, testing, implementation, or lived experience instead of relying on generic summary content.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add specific observations from real use, testing, or implementation',\n 'Document limitations, edge cases, or lessons learned in practice',\n 'Include screenshots, photos, before/after metrics, or original artifacts where relevant',\n 'Rewrite generic sections to reflect direct experience with the subject matter',\n ],\n successCriteria: 'Key pages contain credible signs of direct use or observation, not just generic advice',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n original_data: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('original_data', c.score);\n const affected = getAffectedPages('original_data', pages);\n return [{\n id: 'fix-original-data',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add original data and case studies',\n description: 'Publish proprietary data, statistics, case studies, or research that AI engines cannot find elsewhere.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify internal data assets (customer metrics, case study results, survey data)',\n 'Create data-driven content with specific numbers and percentages',\n 'Publish case studies with measurable outcomes',\n 'Add comparison tables with proprietary benchmarks',\n ],\n successCriteria: 'At least 3 pages contain original data points not found elsewhere online',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n internal_linking: (c, pages, linkGraph) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('internal_linking', c.score);\n const fixes: FixAction[] = [];\n\n // Orphan pages fix\n if (linkGraph) {\n const orphans: string[] = [];\n linkGraph.nodes.forEach((node) => {\n if (node.isOrphan) orphans.push(node.url);\n });\n if (orphans.length > 0) {\n fixes.push({\n id: 'fix-internal-linking-orphans',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Link orphan pages into site navigation',\n description: `${orphans.length} pages have no incoming internal links. These are invisible to AI crawlers that follow links.`,\n impact: orphans.length > 5 ? 'critical' : 'high',\n effort: orphans.length > 10 ? 'medium' : 'low',\n impactScore: 0,\n category: 'structure',\n steps: [\n `Identify the ${orphans.length} orphan pages with zero incoming links`,\n 'Add contextual links from related content pages',\n 'Include orphan pages in navigation menus or footer links',\n 'Add \"Related Content\" sections on relevant pages',\n ],\n successCriteria: 'All content pages have at least 1 incoming internal link',\n affectedPages: orphans.slice(0, 20),\n pageCount: orphans.length,\n });\n }\n\n // Depth fix\n if (linkGraph.stats.maxDepth > 3) {\n fixes.push({\n id: 'fix-internal-linking-depth',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Reduce page depth for deep content',\n description: `Max depth is ${linkGraph.stats.maxDepth} clicks from homepage. AI crawlers rarely follow links beyond 3 levels.`,\n impact: 'medium',\n effort: 'medium',\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Identify pages more than 3 clicks from the homepage',\n 'Add direct links from high-level pages to deep content',\n 'Consider flattening URL structure for key pages',\n 'Add hub pages that aggregate related deep content',\n ],\n successCriteria: 'All important content pages reachable within 3 clicks from homepage',\n });\n }\n\n // No clusters fix\n if (linkGraph.clusters.length === 0) {\n fixes.push({\n id: 'fix-internal-linking-clusters',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create topic clusters with pillar pages',\n description: 'No topic clusters detected. Organizing content into pillar-spoke clusters strengthens topical authority for AI engines.',\n impact: 'high',\n effort: 'high',\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Identify 3-5 core topic areas for your business',\n 'Create comprehensive pillar pages (3000+ words) for each topic',\n 'Write 5-7 supporting articles per pillar linking back to pillar',\n 'Interlink supporting articles within each cluster',\n ],\n successCriteria: 'At least 2 topic clusters with pillar page and 5+ spoke pages',\n });\n }\n } else {\n // No link graph available - generic fix\n fixes.push({\n id: 'fix-internal-linking-generic',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve internal linking architecture',\n description: 'Strengthen internal linking with descriptive anchor text between related pages.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Audit current internal link structure',\n 'Add contextual links between related content pages',\n 'Ensure every key page is reachable within 3 clicks from homepage',\n 'Use descriptive anchor text instead of \"click here\" or \"read more\"',\n ],\n successCriteria: 'Key pages have 3+ incoming internal links with descriptive anchors',\n });\n }\n return fixes;\n },\n\n semantic_html: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('semantic_html', c.score);\n const affected = getAffectedPages('semantic_html', pages);\n return [{\n id: 'fix-semantic-html',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Implement semantic HTML5 elements',\n description: 'Use semantic HTML5 elements (main, article, nav, header, footer, section) to give AI parsers clear content structure.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Wrap main content in <main> element',\n 'Use <article> for self-contained content blocks',\n 'Add <nav> for navigation and <aside> for sidebars',\n 'Add lang attribute to <html> and ARIA labels for accessibility',\n ],\n successCriteria: 'Pages use semantic HTML5 elements with lang attribute',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_freshness: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_freshness', c.score);\n const affected = getAffectedPages('content_freshness', pages);\n return [{\n id: 'fix-content-freshness',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add content freshness signals',\n description: 'Include dateModified schema, visible dates, and recent content updates to signal freshness to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add datePublished and dateModified to Article schema',\n 'Display visible \"Last updated\" dates on content pages',\n 'Update stale content with current information',\n 'Add <time> elements with datetime attributes for all dates',\n ],\n successCriteria: 'Content pages show visible dates and have dateModified in schema',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n sitemap_completeness: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('sitemap_completeness', c.score);\n return [{\n id: 'fix-sitemap',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Create complete sitemap.xml',\n description: 'Generate a comprehensive sitemap with lastmod dates for all important pages.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Generate sitemap.xml listing all content pages',\n 'Include <lastmod> dates for each URL',\n 'Set <changefreq> and <priority> appropriately',\n 'Reference sitemap in robots.txt',\n ],\n successCriteria: 'sitemap.xml returns 200 with all content pages and lastmod dates',\n }];\n },\n\n rss_feed: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('rss_feed', c.score);\n return [{\n id: 'fix-rss-feed',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Deploy RSS/Atom feed',\n description: 'Add an RSS or Atom feed for your blog/news content to signal active publishing to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'discovery',\n steps: [\n 'Create RSS 2.0 or Atom feed for blog/news content',\n 'Include title, description, pubDate, and full content for each item',\n 'Add <link rel=\"alternate\" type=\"application/rss+xml\"> to page head',\n 'Auto-generate feed on each new publish',\n ],\n codeExample: `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<rss version=\"2.0\">\\n <channel>\\n <title>Your Site Blog</title>\\n <link>https://example.com/blog</link>\\n <description>Latest articles</description>\\n <item>\\n <title>Article Title</title>\\n <link>https://example.com/blog/article</link>\\n <pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate>\\n <description>Article summary</description>\\n </item>\\n </channel>\\n</rss>`,\n successCriteria: 'RSS feed returns valid XML with recent content items',\n }];\n },\n\n table_list_extractability: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('table_list_extractability', c.score);\n const affected = getAffectedPages('table_list_extractability', pages);\n return [{\n id: 'fix-tables-lists',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add structured tables and lists',\n description: 'Use HTML tables for comparison data and lists for features, steps, and specifications.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify data suitable for table format (comparisons, pricing, specs)',\n 'Convert bullet points to proper <ul>/<ol> lists',\n 'Add comparison tables with <th> headers',\n 'Ensure tables have descriptive captions',\n ],\n successCriteria: 'Key pages contain at least one HTML table or structured list',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n definition_patterns: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('definition_patterns', c.score);\n const affected = getAffectedPages('definition_patterns', pages);\n return [{\n id: 'fix-definitions',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add definition-style content',\n description: 'Include clear definition patterns for key terms and concepts that AI engines can cite for \"what is\" queries.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify key industry terms your audience searches for',\n 'Write clear definitions using \"X is...\" or \"X refers to...\" patterns',\n 'Place definitions near the top of relevant pages',\n 'Consider a glossary page for comprehensive term coverage',\n ],\n successCriteria: 'Key pages contain definition patterns for relevant terms',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n direct_answer_density: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('direct_answer_density', c.score);\n const affected = getAffectedPages('direct_answer_density', pages);\n return [{\n id: 'fix-direct-answers',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add direct answer paragraphs',\n description: 'Write concise, standalone answer paragraphs after question headings for AI engine citations.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify question-format headings on each page',\n 'Write a 2-3 sentence direct answer immediately after each heading',\n 'Ensure answers are self-contained (don\\'t require context from other sections)',\n 'Use bold for key facts within answer paragraphs',\n ],\n successCriteria: 'Question headings are followed by direct, concise answer paragraphs',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_licensing: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_licensing', c.score);\n return [{\n id: 'fix-content-licensing',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add ai.txt and content licensing',\n description: 'Create an /ai.txt file specifying AI usage permissions and add license schema to structured data.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Create ai.txt at domain root with usage permissions',\n 'Specify allowed AI uses (training, citation, summarization)',\n 'Add license information to schema markup',\n 'Consider a content licensing page linked from footer',\n ],\n codeExample: `# ai.txt - AI Usage Policy for example.com\\n\\nUser-Agent: *\\nAllow: /blog/\\nAllow: /docs/\\n\\n# Permissions\\nTraining: yes\\nCitation: yes with attribution\\nSummarization: yes`,\n successCriteria: '/ai.txt returns 200 with clear AI usage permissions',\n }];\n },\n\n author_schema_depth: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('author_schema_depth', c.score);\n return [{\n id: 'fix-author-schema',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Enhance author and expert schema',\n description: 'Add Person schema for content authors with credentials and sameAs links for E-E-A-T signals.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Create author profile pages for content creators',\n 'Add Person schema with name, jobTitle, credentials, sameAs',\n 'Link articles to author profiles via schema author property',\n 'Include author bio and expertise on article pages',\n ],\n successCriteria: 'Articles have Person schema for authors with credentials',\n }];\n },\n\n creator_transparency: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('creator_transparency', c.score);\n const affected = getAffectedPages('creator_transparency', pages);\n return [{\n id: 'fix-creator-transparency',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Make content creators clearly visible',\n description: 'Add visible bylines, author pages, and reviewer/editor attribution so readers can clearly tell who created the content.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add visible bylines to article-like pages where readers expect them',\n 'Link author names to author pages with role, expertise area, and relevant background',\n 'Add reviewer or editor attribution on sensitive or expert content',\n 'Keep visible creator identity consistent with schema markup',\n ],\n successCriteria: 'Article-like pages have clear visible bylines and linked creator context',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n methodology_transparency: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('methodology_transparency', c.score);\n const affected = getAffectedPages('methodology_transparency', pages);\n return [{\n id: 'fix-methodology-transparency',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Explain how content was tested or reviewed',\n description: 'Add methodology, criteria, testing, review, or update-process details where users would expect them.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add a \"How we tested\", \"Methodology\", or review-process section where relevant',\n 'Document criteria, tools used, sample size, timeframe, or update policy',\n 'Disclose AI assistance when a reasonable reader would expect that context',\n 'Support methodology notes with screenshots, tables, or process artifacts when possible',\n ],\n successCriteria: 'Review, comparison, and research-style pages explain how conclusions were produced',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n fact_density: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('fact_density', c.score);\n const affected = getAffectedPages('fact_density', pages);\n return [{\n id: 'fix-fact-density',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase fact and data density',\n description: 'Add specific numbers, percentages, statistics, and data points that AI engines can cite.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Review content for vague claims and replace with specific data',\n 'Add statistics, percentages, and measurable outcomes',\n 'Include source citations for data points',\n 'Add data tables or comparison charts where appropriate',\n ],\n successCriteria: 'Key pages contain at least 3 specific data points per 500 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n canonical_url: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('canonical_url', c.score);\n const affected = getAffectedPages('canonical_url', pages);\n return [{\n id: 'fix-canonical-url',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix canonical URL strategy',\n description: 'Add rel=\"canonical\" tags to all pages to prevent duplicate content confusion.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Add <link rel=\"canonical\"> to every page pointing to preferred URL',\n 'Ensure canonical URLs use consistent scheme (https) and format',\n 'Handle www vs non-www with proper redirects',\n 'Set canonical for paginated content to the main page',\n ],\n codeExample: `<link rel=\"canonical\" href=\"https://example.com/page\" />`,\n successCriteria: 'All pages have rel=\"canonical\" pointing to the correct URL',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_velocity: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_velocity', c.score);\n return [{\n id: 'fix-content-velocity',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase publishing frequency',\n description: 'Establish a regular content publishing cadence to signal active, current information to AI engines.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Set a publishing schedule (weekly or bi-weekly minimum)',\n 'Create a content calendar covering key topics',\n 'Update sitemap and RSS feed with each new publish',\n 'Refresh existing evergreen content with current data',\n ],\n successCriteria: 'At least 2 new or updated content pages per month with dated entries',\n }];\n },\n\n schema_coverage: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('schema_coverage', c.score);\n return [{\n id: 'fix-schema-coverage',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Extend schema to inner pages',\n description: 'Add page-specific structured data beyond the homepage to articles, services, and product pages.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Add Article schema to blog/news pages',\n 'Add Service/Product schema to service/product pages',\n 'Add BreadcrumbList schema to all inner pages',\n 'Validate each page type with Rich Results Test',\n ],\n successCriteria: 'At least 50% of content pages have page-specific schema',\n dependsOn: ['fix-schema-markup'],\n }];\n },\n\n speakable_schema: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('speakable_schema', c.score);\n return [{\n id: 'fix-speakable-schema',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add SpeakableSpecification schema',\n description: 'Add Speakable schema to tell voice assistants which content sections are best for spoken answers.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Identify key paragraphs suitable for voice readout',\n 'Add SpeakableSpecification with CSS selectors to Article schema',\n 'Point speakable selectors to headline and summary paragraphs',\n 'Test with Google structured data testing tool',\n ],\n codeExample: `\"speakable\": {\\n \"@type\": \"SpeakableSpecification\",\\n \"cssSelector\": [\\n \".article-headline\",\\n \".article-summary\"\\n ]\\n}`,\n successCriteria: 'Article pages include SpeakableSpecification in schema',\n dependsOn: ['fix-schema-markup'],\n }];\n },\n\n query_answer_alignment: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('query_answer_alignment', c.score);\n const affected = getAffectedPages('query_answer_alignment', pages);\n return [{\n id: 'fix-query-answer-alignment',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve query-answer alignment',\n description: 'Ensure question headings are followed by direct, concise answers in the first paragraph.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Audit question-format headings and their following paragraphs',\n 'Add direct answers in the first 1-2 sentences after each question heading',\n 'Remove filler text between question and answer',\n 'Ensure answers are self-contained and citable',\n ],\n successCriteria: 'Question headings have direct answer paragraphs within 50 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n content_cannibalization: (c, pages, linkGraph) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_cannibalization', c.score);\n const fixes: FixAction[] = [];\n\n if (linkGraph && linkGraph.clusters.length > 0) {\n // Check for low-cohesion clusters which may indicate overlapping content\n const lowCohesion = linkGraph.clusters.filter(cl => cl.cohesion < 50);\n if (lowCohesion.length > 0) {\n const affected = lowCohesion.flatMap(cl => [cl.pillarUrl, ...cl.spokes]);\n fixes.push({\n id: 'fix-content-cannibalization-overlap',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Consolidate overlapping content',\n description: `${lowCohesion.length} content clusters have low cohesion, suggesting pages compete for the same topics.`,\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify pages targeting the same keywords or topics',\n 'Merge overlapping pages into single authoritative pages',\n 'Set up 301 redirects from merged pages to consolidated page',\n 'Differentiate remaining similar pages with distinct angles',\n ],\n successCriteria: 'No two pages target the same primary keyword or topic',\n affectedPages: affected.slice(0, 20),\n pageCount: affected.length,\n });\n }\n }\n\n if (fixes.length === 0) {\n const affected = getAffectedPages('content_cannibalization', pages);\n fixes.push({\n id: 'fix-content-cannibalization',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Resolve content cannibalization',\n description: 'Multiple pages may be targeting the same topics, diluting AI engine citations.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Audit pages for overlapping topic coverage',\n 'Consolidate similar pages into comprehensive single pages',\n 'Differentiate remaining pages with distinct angles and keywords',\n 'Add canonical tags to prevent duplicate content issues',\n ],\n successCriteria: 'Each topic is covered by a single authoritative page',\n affectedPages: affected,\n pageCount: affected?.length,\n });\n }\n\n return fixes;\n },\n\n duplicate_content: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('duplicate_content', c.score);\n const affected = getAffectedPages('duplicate_content', pages);\n\n // Extract section names from findings\n const sectionPairs = c.findings\n .filter(f => f.detail.includes(\"' and '\"))\n .map(f => {\n const match = f.detail.match(/'([^']+)' and '([^']+)'/);\n return match ? { a: match[1], b: match[2] } : null;\n })\n .filter(Boolean) as Array<{ a: string; b: string }>;\n\n const steps = [\n 'Identify sections with duplicate or near-identical text',\n 'Rewrite each section to provide a unique angle on the topic',\n 'Ensure each heading section adds new information for the reader',\n ];\n if (sectionPairs.length > 0) {\n const pair = sectionPairs[0];\n steps.unshift(`Start with '${pair.a}' and '${pair.b}' which share similar text`);\n }\n\n return [{\n id: 'fix-duplicate-content',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Fix duplicate content blocks',\n description: 'Sections within pages contain identical or near-identical text. LLMs may flag this as low-quality content, reducing the authority of the page.',\n impact, effort,\n impactScore: 0,\n category: 'content' as const,\n steps,\n successCriteria: 'Each section within a page provides unique content',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n cross_page_duplication: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('cross_page_duplication', c.score);\n const affected = getAffectedPages('cross_page_duplication', pages);\n return [{\n id: 'fix-cross-page-duplication',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Eliminate cross-page duplicate content',\n description: 'The same paragraphs appear on multiple pages. AI engines may only index one version, wasting the others.',\n impact, effort,\n impactScore: 0,\n category: 'content' as const,\n steps: [\n 'Identify paragraphs that are copy-pasted across multiple pages',\n 'Rewrite each instance to provide a unique angle relevant to that page',\n 'Move truly shared content to a single resource page and link to it',\n 'Use canonical tags if pages must share content',\n ],\n successCriteria: 'Each page has unique body content with no copy-pasted paragraphs',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n visible_date_signal: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('visible_date_signal', c.score);\n const affected = getAffectedPages('visible_date_signal', pages);\n return [{\n id: 'fix-visible-dates',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add visible date signals',\n description: 'Add visible publication and modification dates using <time> elements for AI engine freshness assessment.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add visible \"Published\" and \"Last updated\" dates to content pages',\n 'Use <time> elements with datetime attributes',\n 'Ensure dates match dateModified in schema markup',\n 'Update dates when content is refreshed',\n ],\n codeExample: `<time datetime=\"2024-01-15\">January 15, 2024</time>`,\n successCriteria: 'Content pages show visible dates with <time> elements',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n topic_coherence: (c) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('topic_coherence', c.score);\n return [{\n id: 'fix-topic-coherence',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Focus blog content on core expertise',\n description: 'Ensure blog content consistently covers your core topic areas. Scattered content across unrelated topics weakens AI engine authority signals.',\n impact, effort: effort === 'trivial' ? 'low' : effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Identify 2-3 core expertise areas your brand is known for',\n 'Audit existing blog posts and remove or consolidate off-topic content',\n 'Create a content calendar focused on core topics',\n 'Use topic clusters: pillar pages linking to supporting articles within the same niche',\n ],\n successCriteria: '80%+ of blog content covers core expertise areas with consistent topic focus',\n }];\n },\n\n content_depth: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('content_depth', c.score);\n const affected = getAffectedPages('content_depth', pages);\n return [{\n id: 'fix-content-depth',\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Increase content depth and structure',\n description: 'Expand thin content with more detail, examples, and structured sections. AI engines prefer comprehensive articles with clear heading hierarchies.',\n impact, effort: effort === 'trivial' ? 'low' : effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Aim for 1000+ words per article with expert analysis and examples',\n 'Use H2/H3 subheadings every 200-300 words for clear structure',\n 'Add comparison tables, numbered steps, and data points',\n 'Remove or expand thin pages (under 300 words) that dilute site quality',\n ],\n successCriteria: 'Average article length exceeds 1000 words with 5+ subheadings per page',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n citation_ready_writing: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('citation_ready_writing', c.score);\n const affected = getAffectedPages('citation_ready_writing', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Improve citation-ready writing quality',\n description: 'Write self-contained definition sentences and single-claim statements that AI engines can quote directly.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Write \"X is a...\" definition sentences for key concepts',\n 'Keep sentences under 30 words with a single claim each',\n 'Start answer paragraphs with the entity name, not \"This\" or \"That\"',\n 'Pair statistics with named sources for quotable lines',\n ],\n successCriteria: 'Average 3+ definition sentences and 5+ single-claim sentences per page',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n answer_first_placement: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('answer_first_placement', c.score);\n const affected = getAffectedPages('answer_first_placement', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add answer-first content placement',\n description: 'Place a concise answer block in the first 300 words of each page.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add a 40-80 word answer paragraph within the first 300 words',\n 'Replace \"In this article...\" openers with direct answers',\n 'Include the H1 topic entity in the first body sentence',\n ],\n successCriteria: '70%+ of pages have an answer block in the first 300 words',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n evidence_packaging: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('evidence_packaging', c.score);\n const affected = getAffectedPages('evidence_packaging', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Package evidence for AI engines',\n description: 'Add inline citations, attribution phrases, and sources sections.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Add inline links to authoritative external sources within paragraphs',\n 'Use \"According to [Source]...\" attribution phrases',\n 'Add a \"Sources\" or \"References\" heading at the end of articles',\n 'Pair statistics with named source organizations',\n ],\n successCriteria: 'Average 3+ inline citations and 3+ attribution phrases per page',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n entity_disambiguation: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('entity_disambiguation', c.score);\n const affected = getAffectedPages('entity_disambiguation', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Clarify entity boundaries',\n description: 'Define the primary entity early and use consistent terminology.',\n impact, effort,\n impactScore: 0,\n category: 'content',\n steps: [\n 'Define the primary entity within the first 500 characters',\n 'Use the same primary term consistently (avoid synonym soup)',\n 'Add \"unlike X\" or \"compared to Y\" disambiguation signals',\n ],\n successCriteria: '70%+ of pages define the primary entity early with consistent terminology',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n extraction_friction: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('extraction_friction', c.score);\n const affected = getAffectedPages('extraction_friction', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Reduce extraction friction',\n description: 'Make content easier for AI engines to extract by reducing sentence length and jargon.',\n impact, effort,\n impactScore: 0,\n category: 'structure',\n steps: [\n 'Shorten sentences to under 25 words on average',\n 'Use active voice in lead paragraphs (first 2-3 sentences)',\n 'Define jargon inline when first used',\n 'Remove display:none hidden content and accordion-only content',\n ],\n successCriteria: 'Average sentence length under 25 words with no hidden content',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n\n image_context_ai: (c, pages) => {\n if (c.score >= 10) return [];\n const impact = impactFromScore(c.score);\n const effort = effortForCriterion('image_context_ai', c.score);\n const affected = getAffectedPages('image_context_ai', pages);\n return [{\n id: `fix-${c.criterion}`,\n criterion: c.criterion_label,\n criterionId: c.criterion,\n title: 'Add image context for AI engines',\n description: 'Wrap images in <figure>/<figcaption> with descriptive alt text.',\n impact, effort,\n impactScore: 0,\n category: 'trust',\n steps: [\n 'Wrap images in <figure> elements with descriptive <figcaption>',\n 'Write alt text with 5+ descriptive words (avoid \"image\" or \"photo\")',\n 'Place images within <article> or <section> elements',\n ],\n codeExample: '<figure>\\n <img src=\"chart.png\" alt=\"Bar chart showing 45% reduction in fall risk for walker users\">\\n <figcaption>Walker users experienced a 45% reduction in fall risk (2025 study)</figcaption>\\n</figure>',\n successCriteria: '50%+ of images use <figure>/<figcaption> with descriptive alt text',\n affectedPages: affected,\n pageCount: affected?.length,\n }];\n },\n};\n\n// ─── Main function ──────────────────────────────────────────────────────────\n\nexport function generateFixPlan(\n domain: string,\n overallScore: number,\n criteria: CriterionResult[],\n pagesReviewed?: PageReview[],\n linkGraph?: LinkGraph,\n): FixPlan {\n // Step 1: Generate all fixes from criterion scores\n const allFixes: FixAction[] = [];\n\n for (const criterion of criteria) {\n const generator = FIX_GENERATORS[criterion.criterion];\n if (!generator) continue;\n\n const fixes = generator(criterion, pagesReviewed, linkGraph);\n for (const fix of fixes) {\n const weight = CRITERION_WEIGHTS[criterion.criterion] ?? 0.05;\n fix.impactScore = Math.round((10 - criterion.score) * weight * 100);\n allFixes.push(fix);\n }\n }\n\n // Step 2: Group into phases\n const phases: FixPhase[] = PHASE_CONFIG.map(config => {\n const phaseFixes = allFixes\n .filter(fix => config.criteria.includes(fix.criterionId))\n .sort((a, b) => b.impactScore - a.impactScore);\n\n return {\n phase: config.phase,\n title: config.title,\n description: config.description,\n fixes: phaseFixes,\n estimatedImpact: 0, // calculated after projected score\n };\n });\n\n // Step 3: Validate dependencies (move fixes if dependency is in later phase)\n for (const phase of phases) {\n for (const fix of phase.fixes) {\n if (!fix.dependsOn) continue;\n for (const depId of fix.dependsOn) {\n // Find which phase the dependency is in\n const depPhase = phases.find(p => p.fixes.some(f => f.id === depId));\n if (depPhase && depPhase.phase > phase.phase) {\n // Dependency is in a later phase - move this fix there\n phase.fixes = phase.fixes.filter(f => f.id !== fix.id);\n depPhase.fixes.push(fix);\n depPhase.fixes.sort((a, b) => b.impactScore - a.impactScore);\n break;\n }\n }\n }\n }\n\n // Step 4: Calculate projected score\n const totalWeight = Object.values(CRITERION_WEIGHTS).reduce((s, w) => s + w, 0);\n\n // Deduplicate: only count the best fix per criterion\n const bestDeltaPerCriterion = new Map<string, number>();\n for (const fix of allFixes) {\n const criterion = criteria.find(c => c.criterion === fix.criterionId);\n if (!criterion) continue;\n const weight = CRITERION_WEIGHTS[fix.criterionId] ?? 0.05;\n let targetScore: number;\n switch (fix.effort) {\n case 'trivial': case 'low': targetScore = 8; break;\n case 'medium': targetScore = 7; break;\n case 'high': targetScore = 6; break;\n }\n const improvement = Math.max(0, targetScore - criterion.score);\n const delta = (improvement * weight / totalWeight) * 100;\n const existing = bestDeltaPerCriterion.get(fix.criterionId) ?? 0;\n if (delta > existing) bestDeltaPerCriterion.set(fix.criterionId, delta);\n }\n const scoreDelta = Array.from(bestDeltaPerCriterion.values()).reduce((s, d) => s + d, 0);\n\n const projectedScore = Math.min(100, Math.round(overallScore + scoreDelta));\n\n // Step 5: Calculate per-phase estimated impact\n for (const phase of phases) {\n let phaseImpact = 0;\n const seenCriteria = new Set<string>();\n for (const fix of phase.fixes) {\n if (seenCriteria.has(fix.criterionId)) continue;\n seenCriteria.add(fix.criterionId);\n const criterion = criteria.find(c => c.criterion === fix.criterionId);\n if (!criterion) continue;\n const weight = CRITERION_WEIGHTS[fix.criterionId] ?? 0.05;\n let targetScore: number;\n switch (fix.effort) {\n case 'trivial': case 'low': targetScore = 8; break;\n case 'medium': targetScore = 7; break;\n case 'high': targetScore = 6; break;\n }\n const improvement = Math.max(0, targetScore - criterion.score);\n phaseImpact += (improvement * weight / totalWeight) * 100;\n }\n phase.estimatedImpact = Math.round(phaseImpact);\n }\n\n // Step 6: Extract quick wins\n const quickWins = allFixes.filter(\n f => (f.effort === 'trivial' || f.effort === 'low') && (f.impact === 'critical' || f.impact === 'high'),\n );\n\n // Step 7: Build summary\n const summary: FixPlanSummary = {\n criticalCount: allFixes.filter(f => f.impact === 'critical').length,\n highCount: allFixes.filter(f => f.impact === 'high').length,\n mediumCount: allFixes.filter(f => f.impact === 'medium').length,\n lowCount: allFixes.filter(f => f.impact === 'low').length,\n quickWinCount: quickWins.length,\n topOpportunity: allFixes.length > 0\n ? allFixes.sort((a, b) => b.impactScore - a.impactScore)[0].title\n : 'None',\n estimatedTotalEffort: formatEffort(allFixes.reduce((s, f) => s + effortToHours(f.effort), 0)),\n };\n\n return {\n domain,\n generatedAt: new Date().toISOString(),\n overallScore,\n projectedScore,\n totalFixes: allFixes.length,\n phases,\n quickWins,\n summary,\n };\n}\n\nfunction formatEffort(hours: number): string {\n if (hours < 1) return '<1h';\n return `~${Math.round(hours)}h`;\n}\n","/**\r\n * SPA detection and headless Chromium rendering for pre-crawl.\r\n *\r\n * When a site returns a thin JS-only shell (e.g. React CRA, Vite SPA),\r\n * the regular fetch() gets almost no text content, causing false low scores.\r\n * This module detects those shells and re-renders them with Puppeteer.\r\n */\r\n\r\nimport type { FetchResult } from './site-crawler.js';\nimport { isSafeFetchTarget, normalizeHostname } from './network-guard.js';\n\r\n// ─── SPA classification ─────────────────────────────────────────────────────\r\n\r\nexport type RenderingMethod = 'server' | 'client-spa';\r\n\r\ninterface RenderingClassification {\r\n method: RenderingMethod;\r\n framework: string | null;\r\n}\r\n\r\n// ─── SPA shell detection ────────────────────────────────────────────────────\r\n\r\nconst SPA_INDICATORS = [\r\n // Root mount points (empty or nearly empty, including self-closing)\r\n /<div\\s+id=[\"'](root|app|__next|__nuxt|__vue)[\"'][^>]*(?:\\/>|>\\s*<\\/div>)/i,\r\n // Framework globals\r\n /__NEXT_DATA__/,\r\n /__NUXT__/,\r\n // CRA / Vite bundle patterns\r\n /src=[\"'][^\"']*\\/static\\/js\\/main\\.[a-f0-9]+\\.js[\"']/i,\r\n /src=[\"'][^\"']*\\/assets\\/index-[a-f0-9]+\\.js[\"']/i,\r\n // React markers\r\n /data-reactroot/i,\r\n // Angular\r\n /ng-version/i,\r\n // Noscript JS warnings\r\n /<noscript>[^<]*(?:javascript|enable\\s+js|requires?\\s+javascript)[^<]*<\\/noscript>/i,\r\n];\r\n\r\n/**\r\n * Detect whether raw HTML is a thin SPA shell that needs client-side rendering.\r\n * Both conditions required:\r\n * 1. Visible text content < 500 chars (thin page)\r\n * 2. At least one SPA framework indicator present\r\n */\r\nexport function isSpaShell(html: string): boolean {\r\n // Strip all tags and collapse whitespace to get visible text\r\n const text = html\r\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\r\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\r\n .replace(/<[^>]*>/g, ' ')\r\n .replace(/\\s+/g, ' ')\r\n .trim();\r\n\r\n if (text.length >= 500) return false;\r\n\r\n return SPA_INDICATORS.some((pattern) => pattern.test(html));\r\n}\r\n\r\n/**\r\n * Classify a page's rendering method from its raw (non-headless) HTML.\r\n * Returns the method ('server' | 'client-spa') and detected framework if any.\r\n */\r\nexport function classifyRendering(html: string): RenderingClassification {\r\n if (!isSpaShell(html)) return { method: 'server', framework: null };\r\n\r\n // Detect framework from SPA indicators\r\n const frameworkPatterns: [RegExp, string][] = [\r\n [/__NEXT_DATA__/, 'next'],\r\n [/__NUXT__/, 'nuxt'],\r\n [/<div\\s+id=[\"']__vue[\"']/i, 'vue'],\r\n [/ng-version/i, 'angular'],\r\n [/data-reactroot/i, 'react'],\r\n [/<div\\s+id=[\"'](root|app)[\"'][^>]*(?:\\/>|>\\s*<\\/div>)/i, 'react'],\r\n [/src=[\"'][^\"']*\\/static\\/js\\/main\\.[a-f0-9]+\\.js[\"']/i, 'react'],\r\n [/src=[\"'][^\"']*\\/assets\\/index-[a-f0-9]+\\.js[\"']/i, 'vite'],\r\n ];\r\n\r\n for (const [pattern, framework] of frameworkPatterns) {\r\n if (pattern.test(html)) return { method: 'client-spa', framework };\r\n }\r\n\r\n return { method: 'client-spa', framework: null };\r\n}\r\n\r\n// ─── Headless Chromium rendering ────────────────────────────────────────────\r\n\r\nexport interface HeadlessOptions {\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Render a URL with headless Chromium and return the fully-rendered HTML.\r\n * Returns null if Puppeteer is not installed or any error occurs.\r\n * The caller should fall back to the raw HTML in that case.\r\n */\r\nexport async function fetchWithHeadless(\n url: string,\n options?: HeadlessOptions\n): Promise<FetchResult | null> {\n let expectedDomain: string;\n try {\n expectedDomain = normalizeHostname(new URL(url).hostname);\n } catch {\n return null;\n }\n\n if (!(await isSafeFetchTarget(url, expectedDomain))) return null;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let puppeteer: any;\n try {\r\n // Dynamic import - puppeteer is an optional peer dependency\r\n const mod = 'puppeteer';\r\n puppeteer = await import(/* @vite-ignore */ mod);\r\n } catch {\r\n return null;\r\n }\r\n\r\n const timeout = options?.timeout ?? 25000;\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n let browser: any = null;\r\n\r\n try {\r\n browser = await puppeteer.launch({\r\n headless: true,\r\n args: [\r\n '--no-sandbox',\r\n '--disable-setuid-sandbox',\r\n '--disable-dev-shm-usage',\r\n '--disable-gpu',\r\n '--single-process',\r\n ],\r\n });\r\n\r\n const page = await browser.newPage();\r\n\r\n // Block heavy resources to speed up rendering\r\n await page.setRequestInterception(true);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n page.on('request', (req: any) => {\n void (async () => {\n const alreadyHandled = typeof req.isInterceptResolutionHandled === 'function'\n ? req.isInterceptResolutionHandled()\n : false;\n if (alreadyHandled) return;\n\n if (!(await isSafeFetchTarget(req.url(), expectedDomain))) {\n try {\n if (!req.isInterceptResolutionHandled?.()) await req.abort();\n } catch {\n // Ignore abort errors\n }\n return;\n }\n\n const type = req.resourceType();\n try {\n if (!req.isInterceptResolutionHandled?.()) {\n if (['image', 'font', 'media', 'stylesheet'].includes(type)) {\n await req.abort();\n } else {\n await req.continue();\n }\n }\n } catch {\n // Ignore interception errors\n }\n })();\n });\n\r\n await page.setUserAgent('AEO-Visibility-Bot/1.0');\r\n await page.goto(url, { waitUntil: 'networkidle2', timeout });\r\n\r\n // Wait for JS to populate the body with real text\r\n try {\r\n await page.waitForFunction(\r\n 'document.body && document.body.innerText && document.body.innerText.replace(/\\\\s+/g, \" \").trim().length > 100',\r\n { timeout: 5000 }\r\n );\r\n } catch {\r\n // Body text never exceeded 100 chars - still return what we got\r\n }\r\n\n const html = await page.content();\n const finalUrl = page.url();\n if (!(await isSafeFetchTarget(finalUrl, expectedDomain))) return null;\n\n return {\n text: html.slice(0, 500000),\n status: 200,\r\n finalUrl,\r\n };\r\n } catch {\r\n return null;\r\n } finally {\r\n if (browser) {\r\n try {\r\n await browser.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n }\r\n }\r\n}\r\n","/**\n * Programmatic audit API.\n * Runs the full 7-phase AEO audit pipeline and returns structured results.\n */\n\nimport { prefetchSiteData, auditSiteFromData, extractRawDataSummary } from './site-crawler.js';\nimport { calculateOverallScore } from './scoring.js';\nimport { isSpaShell, fetchWithHeadless, classifyRendering } from './headless-fetch.js';\nimport { buildScorecard, buildDetailedFindings } from './scorecard-builder.js';\nimport { generateVerdict, generateOpportunities, generatePitchNumbers, generateBottomLine } from './narrative-generator.js';\nimport { fetchMultiPageData } from './multi-page-fetcher.js';\nimport { analyzeAllPages } from './page-analyzer.js';\nimport { computePillarScores, computeTopFixes } from './pillars.js';\nimport { isSafeFetchTarget } from './network-guard.js';\nimport type { AuditData } from './types.js';\n\nexport interface AuditOptions {\n /** Skip Puppeteer SPA rendering (default: false) */\n noHeadless?: boolean;\n /** Homepage + blog only, skip extra page discovery (default: false) */\n noMultiPage?: boolean;\n /** Fetch timeout in ms (default: 15000) */\n timeout?: number;\n /** Enable full-site BFS crawl (default: false) */\n fullCrawl?: boolean;\n /** Max pages for full crawl (default: 200) */\n maxPages?: number;\n /** Parallel fetch concurrency for full crawl (default: 5) */\n concurrency?: number;\n}\n\nexport interface AuditResult extends AuditData {\n /** True if headless browser was used for SPA rendering */\n renderedWithHeadless?: boolean;\n /** Wall-clock seconds */\n elapsed: number;\n}\n\nfunction getTextLength(html: string): number {\n return html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]*>/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim().length;\n}\n\n/**\n * Run a complete AEO audit on a domain.\n *\n * @example\n * ```ts\n * import { audit } from 'aeorank';\n * const result = await audit('example.com');\n * console.log(result.overallScore); // 0-100\n * ```\n */\nexport async function audit(domain: string, options?: AuditOptions): Promise<AuditResult> {\n const normalizedTarget = domain.startsWith('http') ? domain : `https://${domain}`;\n if (!(await isSafeFetchTarget(normalizedTarget))) {\n throw new Error(`Refusing to audit private or local address: ${domain}`);\n }\n\n const startTime = Date.now();\n let renderedWithHeadless = false;\n\n // Phase 1: Fetch site data\n const siteData = await prefetchSiteData(domain);\n\n if (!siteData.protocol) {\n throw new Error(`Could not connect to ${domain} (no HTTPS or HTTP response)`);\n }\n\n if (siteData.redirectedTo) {\n throw new Error(`${domain} redirects to ${siteData.redirectedTo} (hijacked domain)`);\n }\n\n if (siteData.parkedReason) {\n throw new Error(`${domain} is a parked/lost domain (${siteData.parkedReason})`);\n }\n\n // Phase 2: SPA detection + headless rendering\n if (!options?.noHeadless && siteData.homepage && isSpaShell(siteData.homepage.text)) {\n const rawTextLen = getTextLength(siteData.homepage.text);\n const url = `${siteData.protocol}://${domain}`;\n const rendered = await fetchWithHeadless(url);\n\n if (rendered) {\n const renderedTextLen = getTextLength(rendered.text);\n if (renderedTextLen > rawTextLen) {\n siteData.homepage = rendered;\n renderedWithHeadless = true;\n }\n }\n\n if (renderedWithHeadless && siteData.faqPage && isSpaShell(siteData.faqPage.text)) {\n const faqUrl = `${siteData.protocol}://${domain}/faq`;\n const renderedFaq = await fetchWithHeadless(faqUrl);\n if (renderedFaq && getTextLength(renderedFaq.text) > getTextLength(siteData.faqPage.text)) {\n siteData.faqPage = renderedFaq;\n }\n }\n }\n\n // Phase 3: Multi-page discovery\n if (options?.fullCrawl) {\n const { crawlFullSite } = await import('./full-site-crawler.js');\n const crawlResult = await crawlFullSite(siteData, {\n maxPages: options.maxPages ?? 200,\n concurrency: options.concurrency ?? 5,\n });\n siteData.blogSample = crawlResult.pages;\n siteData.crawlStats = {\n discovered: crawlResult.discoveredUrls.length,\n fetched: crawlResult.fetchedUrls.length,\n skipped: crawlResult.skippedUrls.length,\n elapsed: crawlResult.elapsed,\n };\n } else if (!options?.noMultiPage) {\n await fetchMultiPageData(siteData);\n }\n\n // Phase 4: Score all 40 criteria\n const results = auditSiteFromData(siteData);\n const overallScore = calculateOverallScore(results);\n const rawData = extractRawDataSummary(siteData);\n if (renderedWithHeadless) rawData.rendered_with_headless = true;\n\n // Compute pillar scores and top fixes\n const pillarScores = computePillarScores(results);\n const topFixesRaw = computeTopFixes(results, 3);\n const coherenceResult = results.find(r => r.criterion === 'topic_coherence');\n const coherenceGated = !!(coherenceResult && coherenceResult.score < 6);\n\n // Phase 5: Build scorecard + detailed findings\n const scorecard = buildScorecard(results);\n const detailedFindings = buildDetailedFindings(results);\n\n // Phase 6: Generate narrative\n const verdict = generateVerdict(overallScore, scorecard, rawData, domain);\n const opportunities = generateOpportunities(scorecard, results);\n const pitchNumbers = generatePitchNumbers(overallScore, rawData, scorecard);\n const bottomLine = generateBottomLine(overallScore, opportunities, scorecard, domain);\n\n // Phase 7: Per-page analysis\n const pagesReviewed = analyzeAllPages(siteData);\n\n const elapsed = Math.round((Date.now() - startTime) / 100) / 10;\n\n return {\n site: domain,\n auditDate: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }),\n auditor: 'AEORank',\n engine: 'instant',\n overallScore,\n verdict,\n scorecard,\n detailedFindings,\n opportunities,\n pitchNumbers,\n bottomLine,\n pagesReviewed,\n pillarScores,\n coherenceGated,\n criteriaScored: results.length,\n topFixes: topFixesRaw.map(f => ({ fix: f.clientName, impact: `+${f.impactPoints} pts`, effort: f.effort })),\n elapsed,\n ...(renderedWithHeadless && { renderedWithHeadless: true }),\n };\n}\n","/**\n * Comparison mode - run two audits in parallel and compute per-criterion deltas.\n */\n\nimport { audit } from './audit.js';\nimport type { AuditOptions, AuditResult } from './audit.js';\n\nexport interface CriterionComparison {\n id: number;\n criterion: string;\n scoreA: number;\n scoreB: number;\n delta: number;\n statusA: string;\n statusB: string;\n}\n\nexport interface ComparisonResult {\n siteA: AuditResult;\n siteB: AuditResult;\n comparison: {\n scoreDelta: number;\n criteria: CriterionComparison[];\n siteAAdvantages: string[];\n siteBAdvantages: string[];\n tied: string[];\n };\n}\n\n/**\n * Audit two domains in parallel and build a per-criterion comparison.\n */\nexport async function compare(\n domainA: string,\n domainB: string,\n options?: AuditOptions,\n): Promise<ComparisonResult> {\n const [siteA, siteB] = await Promise.all([\n audit(domainA, options),\n audit(domainB, options),\n ]);\n\n const criteria: CriterionComparison[] = [];\n const siteAAdvantages: string[] = [];\n const siteBAdvantages: string[] = [];\n const tied: string[] = [];\n\n // Zip scorecards by id (both should have 26 items in the same order)\n for (let i = 0; i < siteA.scorecard.length; i++) {\n const a = siteA.scorecard[i];\n const b = siteB.scorecard[i];\n if (!a || !b) continue;\n\n const delta = a.score - b.score;\n\n criteria.push({\n id: a.id,\n criterion: a.criterion,\n scoreA: a.score,\n scoreB: b.score,\n delta,\n statusA: a.status,\n statusB: b.status,\n });\n\n if (delta > 0) siteAAdvantages.push(a.criterion);\n else if (delta < 0) siteBAdvantages.push(a.criterion);\n else tied.push(a.criterion);\n }\n\n return {\n siteA,\n siteB,\n comparison: {\n scoreDelta: siteA.overallScore - siteB.overallScore,\n criteria,\n siteAAdvantages,\n siteBAdvantages,\n tied,\n },\n };\n}\n"],"mappings":"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","names":["text","wordCount","wordCount","cap","getTextContent","extractQuestionHeadings","cap","wordCount","getTextContent","countWords","wordCount","extractTitle","getTextContent","countWords","wordCount","CRITERION_WEIGHTS","CRITERION_WEIGHTS","crawlFullSite"]}
|