PDF Report Generation System
Executive Summary
The PDF report is the client-facing deliverable — the tangible output of the entire 21-phase audit. Report quality directly impacts perceived value. A well-designed PDF report converts audit findings into client trust: 87% of clients who receive a visually professional audit report retain the agency vs. 34% who receive a plain text output (AgencyAnalytics survey, 2024). Every phase must produce its own PDF; the final master report compiles all phases into a single document.
2025–2026 reporting context:
- Include SEO Health Index = (LOCAL-IMPACT × 0.55) + (SERP-TRUST × 0.45) as the master metric
- AI Overviews impact must be called out per relevant finding (20–35% of local queries)
- All CWV thresholds use 2025 values: INP < 200ms (not FID), LCP < 2.5s, CLS < 0.1
- Score grades: A (90–100) / B (75–89) / C (60–74) / D (45–59) / F (< 45)
- Source data for reports comes from: Ahrefs (backlinks, keywords), SEMrush (traffic, gaps), Screaming Frog (technical crawl), Google Search Console (GSC) (coverage, performance), PageSpeed Insights + Lighthouse (CWV), BrightLocal (citations, GBP rank), Majestic (TF:CF), Google Business Profile (GBP), ChatGPT/Perplexity (AI visibility tests)
- GBP 2025 features to include in reports: Q&A section completeness, service menu with prices, review velocity (last 90 days), Google Reserve/Booking integration, AI Overview local pack eligibility (≥4.3 stars, 50+ reviews), GBP Posts frequency
- AI crawler access status must be reported in technical findings (14 crawlers, 3 tiers)
- llms.txt presence/absence should be flagged in AI SEO section
- Brand mention authority score (YouTube/Reddit/Wikipedia correlation with AI citations) in reputation section
- Platform-specific AI optimization status (AIO, ChatGPT, Perplexity, Gemini, Copilot) in AI SEO section
Numbered Action Steps:
- Confirm
{AUDIT_DIR}/has all required phase.mdfiles — runpython3 scripts/audit_status.py --project projects/[slug]— Effort: 1 min - Run
python3 scripts/report_compiler.py --project projects/[slug]→ generates{AUDIT_DIR}/master-report.md— Effort: 2 min - Build HTML report for each phase — use full design system (SVG gauges, color cards, tables) — Effort: 30–60 min per phase
- Save HTML to
{REPORTS_DIR}/phase-[N]-[slug].html— Effort: 1 min per phase - Run
python3 scripts/generate_pdf.py --html {REPORTS_DIR}/phase-[N]-[slug].html— generates PDF — Effort: 30 sec per phase - Confirm PDF size and page count — alert if <2 pages (likely rendering error) or >30 pages (likely runaway content) — Effort: 1 min
- Repeat steps 3–6 for all 21 phases + LOCAL-IMPACT + SERP-TRUST + master report — Effort: varies
- Deliver master-report.pdf + all phase PDFs in
{REPORTS_DIR}/folder to client — Effort: 5 min
Tools for This Phase
| Tool | Purpose | Cost |
|---|---|---|
python3 scripts/generate_pdf.py |
Chrome headless + WeasyPrint fallback PDF generation | Free (local) |
| Google Chrome headless | Primary PDF engine — highest fidelity HTML→PDF | Free |
| WeasyPrint | Fallback PDF engine — no Chrome needed | Free |
| Google Chrome browser | Preview HTML before PDF conversion | Free |
python3 scripts/report_compiler.py |
Compile all phase .md files into master-report.md | Free (local) |
Report Quality Standards (2025)
| Report Element | Minimum Standard | Excellent Standard |
|---|---|---|
| Cover page | Business name, score, date, URL | + SVG gauge chart, grade badge, key verdict |
| Dashboard page | Score cards per dimension | + Competitor comparison bars, trend arrows |
| Issue cards | Status + issue + fix | + Priority score, effort estimate, competitor context |
| Priority matrix | Impact × Feasibility table | + Visual heat map, 30/90-day roadmap |
| File size | < 5MB | < 2MB (optimized images) |
| Page count | 15–30 pages | 20–40 pages (comprehensive) |
| Completeness | All completed phases | All 21 phases + both scoring frameworks |
Required Report Sections by Report Type
| Report Type | Cover Page | Score Gauge | Issue Cards | Priority Matrix | Competitor Table | Quick Wins | Roadmap | AI Visibility Callout |
|---|---|---|---|---|---|---|---|---|
| Per-phase PDF | Yes | Yes (phase score) | Yes | Yes | Yes (phase-specific) | Yes (top 3) | No | Yes (if content/on-page/AI phase) |
| LOCAL-IMPACT PDF | Yes | Yes (composite) | No | No | Yes (benchmarks) | Yes (top 5) | No | Yes (AI readiness dimension) |
| SERP-TRUST PDF | Yes | Yes (composite) | No | No | Yes (benchmarks) | Yes (top 5) | No | Yes (T2 dimension) |
| Master Report PDF | Yes | Yes (SEO Health Index) | Yes (all phases) | Yes (master) | Yes (full competitor summary) | Yes (top 10) | Yes (30/90/180-day) | Yes (dedicated section) |
2025–2026 Report Content Requirements
| Section | What to Include | Why It Matters |
|---|---|---|
| AI Visibility Dashboard | AIO citation status, ChatGPT/Perplexity mentions, AI crawler access score | AI search drives 20–35% of local queries; clients need to see their AI presence |
| GBP Health Summary | Q&A completeness, review velocity (last 90 days), service menu, Google Reserve status, AI Overview eligibility | GBP is the #1 local ranking factor; 2025 features (Q&A, service menu) directly affect AIO inclusion |
| Brand Mention Authority | YouTube/Reddit/Wikipedia presence, brand mention score vs. backlink profile | Brand mentions correlate 3× more with AI visibility than backlinks (Ahrefs Dec 2025) |
| AI Crawler Access | Tier 1/2/3 crawler status, llms.txt presence, IndexNow status | Blocked AI crawlers = invisible to that platform regardless of content quality |
| Platform Readiness | Per-platform scores (AIO, ChatGPT, Perplexity, Gemini, Copilot) | Only 11% of domains cited by both ChatGPT and AIO — platform-specific optimization required |
| Citability Score | Answer block quality, self-containment, statistical density | AI models cite passages meeting specific structural criteria (134–167 word blocks, answer-first) |
Architecture
- Read
{AUDIT_DIR}/intake-data.mdto get PROJECT_DIR, business name, URL - Aggregate all audit findings from
{AUDIT_DIR}/*.mdfiles - Generate a complete HTML file using the design system below with real audit data
- Write the HTML file to
{REPORTS_DIR}/phase-[N]-[slug].html - Convert to PDF using the Python script (auto-detects Chrome or WeasyPrint):
Or directly via Chrome:python3 scripts/generate_pdf.py --html {REPORTS_DIR}/phase-[N]-[slug].html"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \ --headless=new --disable-gpu --no-sandbox --print-to-pdf-no-header \ --print-to-pdf="{REPORTS_DIR}/phase-[N]-[slug].pdf" \ "file:///[absolute-path-to-html]" - Report PDF path + file size to user
Step 1: Detect Chrome Path (Manual Fallback)
If not using the Python script, detect the Chrome binary:
if [ -f "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ]; then
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
elif command -v google-chrome &> /dev/null; then
CHROME="google-chrome"
elif command -v chromium-browser &> /dev/null; then
CHROME="chromium-browser"
elif command -v chromium &> /dev/null; then
CHROME="chromium"
else
echo "ERROR: Chrome not found. Install with: brew install --cask google-chrome"
exit 1
fi
echo "Chrome found at: $CHROME"
Step 2: Generate HTML Report
Write a complete HTML file using the template structure and design system below. Replace ALL placeholder values with real audit data from the conversation.
File Naming Convention
reports/{business-name-slug}-seo-audit-{YYYY-MM-DD}.html
reports/{business-name-slug}-seo-audit-{YYYY-MM-DD}.pdf
Step 3: Convert to PDF
mkdir -p reports
"$CHROME" --headless --disable-gpu --no-sandbox \
--print-to-pdf="reports/{filename}.pdf" \
"file://{absolute-path-to-html}"
HTML TEMPLATE STRUCTURE
Generate the COMPLETE HTML below, populated with real audit data.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SEO Audit Report — {BUSINESS_NAME}</title>
<style>
/* ============================================
DESIGN SYSTEM — SEO AUDIT REPORT
============================================ */
:root {
/* Brand Colors */
--primary: #1E3A5F;
--primary-light: #2563EB;
--primary-bg: #EFF6FF;
--accent: #0EA5E9;
/* Score Colors */
--score-excellent: #059669;
--score-excellent-bg: #ECFDF5;
--score-good: #10B981;
--score-good-bg: #D1FAE5;
--score-warning: #D97706;
--score-warning-bg: #FEF3C7;
--score-critical: #DC2626;
--score-critical-bg: #FEE2E2;
/* Neutrals */
--text-primary: #111827;
--text-secondary: #6B7280;
--text-muted: #9CA3AF;
--border: #E5E7EB;
--surface: #F9FAFB;
--white: #FFFFFF;
/* Typography */
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
}
/* Print Settings */
@page {
size: A4;
margin: 18mm 15mm 22mm 15mm;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-sans);
font-size: 10.5pt;
line-height: 1.6;
color: var(--text-primary);
background: var(--white);
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
/* Page Break Utilities */
.page-break { page-break-after: always; }
.no-break { page-break-inside: avoid; }
.break-before { page-break-before: always; }
/* ============================================
COVER PAGE
============================================ */
.cover {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
background: linear-gradient(135deg, var(--primary) 0%, #0F172A 100%);
color: var(--white);
padding: 60px 40px;
margin: -18mm -15mm 0 -15mm;
page-break-after: always;
}
.cover-badge {
display: inline-block;
background: rgba(255,255,255,0.15);
border: 1px solid rgba(255,255,255,0.25);
border-radius: 20px;
padding: 6px 20px;
font-size: 11pt;
letter-spacing: 1.5px;
text-transform: uppercase;
margin-bottom: 40px;
}
.cover h1 {
font-size: 32pt;
font-weight: 800;
line-height: 1.2;
margin-bottom: 16px;
}
.cover .subtitle {
font-size: 14pt;
color: rgba(255,255,255,0.8);
margin-bottom: 50px;
}
.cover-score {
margin: 30px 0;
}
.cover-meta {
margin-top: 50px;
font-size: 10pt;
color: rgba(255,255,255,0.6);
}
.cover-meta strong {
color: rgba(255,255,255,0.9);
}
.cover-meta div {
margin: 6px 0;
}
/* ============================================
TABLE OF CONTENTS
============================================ */
.toc {
padding: 40px 0;
}
.toc h2 {
font-size: 20pt;
color: var(--primary);
margin-bottom: 30px;
padding-bottom: 10px;
border-bottom: 3px solid var(--primary);
}
.toc-list {
list-style: none;
}
.toc-list li {
padding: 10px 0;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
font-size: 11pt;
}
.toc-list li .toc-num {
font-weight: 700;
color: var(--primary);
min-width: 30px;
}
.toc-list li .toc-dots {
flex: 1;
border-bottom: 1px dotted var(--text-muted);
margin: 0 10px;
height: 1px;
}
/* ============================================
SECTION HEADERS
============================================ */
.section {
padding: 10px 0 30px 0;
}
.section-header {
margin-bottom: 24px;
padding-bottom: 12px;
border-bottom: 3px solid var(--primary);
}
.section-header h2 {
font-size: 18pt;
color: var(--primary);
margin-bottom: 4px;
}
.section-header .section-sub {
font-size: 10pt;
color: var(--text-secondary);
}
h3 {
font-size: 13pt;
color: var(--primary);
margin: 20px 0 10px 0;
padding-bottom: 6px;
border-bottom: 1px solid var(--border);
}
h4 {
font-size: 11pt;
color: var(--text-primary);
margin: 14px 0 8px 0;
}
p {
margin-bottom: 10px;
}
/* ============================================
SCORE GAUGES (SVG)
============================================ */
.score-dashboard {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin: 20px 0;
}
.score-dashboard .overall {
grid-column: 1 / -1;
display: flex;
justify-content: center;
margin-bottom: 10px;
}
.gauge-card {
text-align: center;
padding: 15px;
background: var(--surface);
border-radius: 10px;
border: 1px solid var(--border);
}
.gauge-card .gauge-label {
font-size: 9pt;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 8px;
}
.gauge-overall {
text-align: center;
padding: 20px;
background: var(--primary-bg);
border-radius: 12px;
border: 2px solid var(--primary);
}
/* ============================================
ISSUE CARDS
============================================ */
.issue-card {
border-left: 4px solid;
padding: 12px 16px;
margin-bottom: 12px;
border-radius: 0 8px 8px 0;
page-break-inside: avoid;
}
.issue-card.critical {
border-color: var(--score-critical);
background: var(--score-critical-bg);
}
.issue-card.high {
border-color: var(--score-warning);
background: var(--score-warning-bg);
}
.issue-card.medium {
border-color: #FBBF24;
background: #FFFBEB;
}
.issue-card.low {
border-color: var(--score-good);
background: var(--score-good-bg);
}
.issue-card .issue-title {
font-weight: 700;
font-size: 10.5pt;
margin-bottom: 4px;
}
.issue-card .issue-desc {
font-size: 9.5pt;
color: var(--text-secondary);
margin-bottom: 6px;
}
.issue-card .issue-meta {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
/* Badges */
.badge {
display: inline-block;
padding: 2px 8px;
border-radius: 10px;
font-size: 8pt;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
}
.badge-critical { background: var(--score-critical); color: white; }
.badge-high { background: var(--score-warning); color: white; }
.badge-medium { background: #FBBF24; color: #78350F; }
.badge-low { background: var(--score-good); color: white; }
.badge-pass { background: var(--score-excellent); color: white; }
.badge-effort { background: var(--primary-light); color: white; }
.badge-info { background: #6366F1; color: white; }
/* ============================================
TABLES
============================================ */
table {
width: 100%;
border-collapse: collapse;
margin: 12px 0 20px 0;
font-size: 9.5pt;
page-break-inside: auto;
}
thead {
display: table-header-group;
}
thead th {
background: var(--primary);
color: var(--white);
padding: 10px 12px;
text-align: left;
font-weight: 600;
font-size: 9pt;
text-transform: uppercase;
letter-spacing: 0.3px;
}
tbody tr {
border-bottom: 1px solid var(--border);
}
tbody tr:nth-child(even) {
background: var(--surface);
}
tbody td {
padding: 8px 12px;
vertical-align: top;
}
/* ============================================
BAR CHARTS (CSS-only)
============================================ */
.bar-chart {
margin: 15px 0;
}
.bar-row {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.bar-label {
width: 140px;
font-size: 9pt;
font-weight: 500;
color: var(--text-primary);
flex-shrink: 0;
}
.bar-track {
flex: 1;
height: 22px;
background: var(--surface);
border-radius: 4px;
overflow: hidden;
position: relative;
}
.bar-fill {
height: 100%;
border-radius: 4px;
display: flex;
align-items: center;
padding-left: 8px;
font-size: 8pt;
font-weight: 700;
color: white;
min-width: 30px;
}
.bar-fill.client { background: var(--primary-light); }
.bar-fill.comp1 { background: #94A3B8; }
.bar-fill.comp2 { background: #CBD5E1; }
.bar-fill.comp3 { background: #E2E8F0; color: var(--text-secondary); }
/* ============================================
TIMELINE / ROADMAP
============================================ */
.timeline {
position: relative;
padding-left: 30px;
margin: 20px 0;
}
.timeline::before {
content: '';
position: absolute;
left: 8px;
top: 0;
bottom: 0;
width: 3px;
background: var(--primary);
border-radius: 2px;
}
.timeline-item {
position: relative;
margin-bottom: 20px;
page-break-inside: avoid;
}
.timeline-item::before {
content: '';
position: absolute;
left: -26px;
top: 6px;
width: 14px;
height: 14px;
background: var(--primary);
border: 3px solid var(--white);
border-radius: 50%;
box-shadow: 0 0 0 2px var(--primary);
}
.timeline-item .tl-header {
font-weight: 700;
font-size: 11pt;
color: var(--primary);
margin-bottom: 4px;
}
.timeline-item .tl-body {
font-size: 9.5pt;
color: var(--text-secondary);
background: var(--surface);
padding: 10px 14px;
border-radius: 6px;
border: 1px solid var(--border);
}
.timeline-item .tl-body ul {
margin: 6px 0 0 16px;
padding: 0;
}
.timeline-item .tl-body li {
margin-bottom: 3px;
}
/* ============================================
PRIORITY MATRIX (2x2 Grid)
============================================ */
.matrix {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
gap: 2px;
margin: 15px 0;
border: 2px solid var(--primary);
border-radius: 8px;
overflow: hidden;
}
.matrix-cell {
padding: 14px;
min-height: 120px;
}
.matrix-cell h4 {
font-size: 9pt;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
border: none;
padding: 0;
}
.matrix-cell ul {
list-style: none;
padding: 0;
}
.matrix-cell li {
font-size: 8.5pt;
padding: 3px 0;
border-bottom: 1px solid rgba(0,0,0,0.05);
}
.matrix-q1 { background: #FEE2E2; }
.matrix-q1 h4 { color: var(--score-critical); }
.matrix-q2 { background: #FEF3C7; }
.matrix-q2 h4 { color: var(--score-warning); }
.matrix-q3 { background: #DBEAFE; }
.matrix-q3 h4 { color: var(--primary-light); }
.matrix-q4 { background: #ECFDF5; }
.matrix-q4 h4 { color: var(--score-excellent); }
/* ============================================
CALLOUT BOXES
============================================ */
.callout {
padding: 14px 18px;
border-radius: 8px;
margin: 14px 0;
page-break-inside: avoid;
}
.callout-success {
background: var(--score-excellent-bg);
border: 1px solid var(--score-excellent);
}
.callout-warning {
background: var(--score-warning-bg);
border: 1px solid var(--score-warning);
}
.callout-danger {
background: var(--score-critical-bg);
border: 1px solid var(--score-critical);
}
.callout-info {
background: var(--primary-bg);
border: 1px solid var(--primary-light);
}
.callout strong {
display: block;
margin-bottom: 4px;
}
/* ============================================
KPI TABLE
============================================ */
.kpi-table td:nth-child(2),
.kpi-table td:nth-child(3) {
font-family: var(--font-mono);
font-weight: 600;
text-align: center;
}
/* ============================================
QUICK WINS
============================================ */
.quick-win {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 10px 14px;
background: var(--score-excellent-bg);
border: 1px solid var(--score-excellent);
border-radius: 8px;
margin-bottom: 10px;
page-break-inside: avoid;
}
.quick-win-num {
flex-shrink: 0;
width: 28px;
height: 28px;
background: var(--score-excellent);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 11pt;
}
.quick-win-content {
flex: 1;
}
.quick-win-content strong {
font-size: 10.5pt;
}
.quick-win-content p {
font-size: 9pt;
color: var(--text-secondary);
margin: 2px 0 0 0;
}
/* ============================================
FOOTER
============================================ */
.report-footer {
margin-top: 40px;
padding-top: 20px;
border-top: 2px solid var(--primary);
text-align: center;
font-size: 8.5pt;
color: var(--text-muted);
}
.report-footer strong {
color: var(--primary);
}
/* ============================================
STATUS INDICATORS
============================================ */
.status-pass::before { content: '✅ '; }
.status-warn::before { content: '⚠️ '; }
.status-fail::before { content: '❌ '; }
.status-info::before { content: 'ℹ️ '; }
/* ============================================
UTILITIES
============================================ */
.text-center { text-align: center; }
.text-right { text-align: right; }
.mt-20 { margin-top: 20px; }
.mb-20 { margin-bottom: 20px; }
.flex { display: flex; }
.gap-20 { gap: 20px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; }
</style>
</head>
<body>
SECTION 1: COVER PAGE
<!-- ============================================
COVER PAGE
============================================ -->
<div class="cover">
<div class="cover-badge">Local SEO Audit Report</div>
<h1>{BUSINESS_NAME}</h1>
<p class="subtitle">{WEBSITE_URL}</p>
<div class="cover-score">
<!-- OVERALL SCORE GAUGE — Large -->
<svg width="180" height="180" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="rgba(255,255,255,0.15)" stroke-width="10"/>
<circle cx="60" cy="60" r="54" fill="none"
stroke="{SCORE_COLOR}"
stroke-width="10"
stroke-dasharray="339.29"
stroke-dashoffset="{339.29 * (1 - OVERALL_SCORE/100)}"
transform="rotate(-90 60 60)"
stroke-linecap="round"/>
<text x="60" y="55" text-anchor="middle" dominant-baseline="central"
font-size="32" font-weight="800" fill="white">{OVERALL_SCORE}</text>
<text x="60" y="78" text-anchor="middle"
font-size="10" fill="rgba(255,255,255,0.7)">OVERALL SCORE</text>
</svg>
</div>
<div class="cover-meta">
<div><strong>Prepared:</strong> {DATE}</div>
<div><strong>Location:</strong> {TARGET_LOCATION}</div>
<div><strong>Auditor:</strong> Local SEO Audit System v1.0</div>
</div>
</div>
SECTION 2: TABLE OF CONTENTS
<!-- ============================================
TABLE OF CONTENTS
============================================ -->
<div class="toc page-break">
<h2>Table of Contents</h2>
<ul class="toc-list">
<li><span class="toc-num">01</span> Executive Summary <span class="toc-dots"></span></li>
<li><span class="toc-num">02</span> Score Dashboard <span class="toc-dots"></span></li>
<li><span class="toc-num">02B</span> LOCAL-IMPACT & SERP-TRUST Scores <span class="toc-dots"></span></li>
<li><span class="toc-num">03</span> Critical Issues <span class="toc-dots"></span></li>
<li><span class="toc-num">04</span> Priority Matrix <span class="toc-dots"></span></li>
<li><span class="toc-num">05</span> Quick Wins <span class="toc-dots"></span></li>
<li><span class="toc-num">06</span> Competitor Summary <span class="toc-dots"></span></li>
<li><span class="toc-num">07</span> Action Plans & Roadmap <span class="toc-dots"></span></li>
<li><span class="toc-num">08</span> KPI Tracking Framework <span class="toc-dots"></span></li>
<li><span class="toc-num">09</span> Phase-by-Phase Audit Details <span class="toc-dots"></span></li>
<li><span class="toc-num">10</span> Resource & Budget Recommendations <span class="toc-dots"></span></li>
</ul>
</div>
SECTION 3: EXECUTIVE SUMMARY
<!-- ============================================
EXECUTIVE SUMMARY
============================================ -->
<div class="section page-break">
<div class="section-header">
<h2>01 — Executive Summary</h2>
<div class="section-sub">{BUSINESS_NAME} • {WEBSITE_URL} • {DATE}</div>
</div>
<p>{EXECUTIVE_SUMMARY_PARAGRAPH_1}</p>
<p>{EXECUTIVE_SUMMARY_PARAGRAPH_2}</p>
<p>{EXECUTIVE_SUMMARY_PARAGRAPH_3}</p>
<!-- Key Findings Callouts -->
<div class="grid-2 mt-20">
<div class="callout callout-danger">
<strong>Critical Issues Found: {CRITICAL_COUNT}</strong>
{CRITICAL_SUMMARY_SENTENCE}
</div>
<div class="callout callout-warning">
<strong>High Priority Items: {HIGH_COUNT}</strong>
{HIGH_SUMMARY_SENTENCE}
</div>
<div class="callout callout-success">
<strong>Quick Wins Available: {QUICKWIN_COUNT}</strong>
{QUICKWIN_SUMMARY_SENTENCE}
</div>
<div class="callout callout-info">
<strong>Strengths Identified: {STRENGTHS_COUNT}</strong>
{STRENGTHS_SUMMARY_SENTENCE}
</div>
</div>
</div>
SECTION 4: SCORE DASHBOARD
<!-- ============================================
SCORE DASHBOARD
============================================ -->
<div class="section page-break">
<div class="section-header">
<h2>02 — Score Dashboard</h2>
<div class="section-sub">Performance across all audit dimensions</div>
</div>
<!-- Overall Score (centered, large) -->
<div class="score-dashboard">
<div class="overall">
<div class="gauge-overall">
<svg width="160" height="160" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="#E5E7EB" stroke-width="10"/>
<circle cx="60" cy="60" r="54" fill="none"
stroke="{OVERALL_SCORE_COLOR}"
stroke-width="10"
stroke-dasharray="339.29"
stroke-dashoffset="{CALC: 339.29 * (1 - OVERALL_SCORE/100)}"
transform="rotate(-90 60 60)"
stroke-linecap="round"/>
<text x="60" y="52" text-anchor="middle" dominant-baseline="central"
font-size="30" font-weight="800" fill="var(--text-primary)">{OVERALL_SCORE}</text>
<text x="60" y="75" text-anchor="middle"
font-size="9" fill="var(--text-secondary)" font-weight="600">OVERALL</text>
</svg>
<div class="gauge-label">Overall Health Score</div>
</div>
</div>
<!-- Sub-Score Gauges (8 total in 3-column grid) -->
<!-- REPEAT FOR EACH: Technical, On-Page, Content, Local SEO, Authority, AI Visibility, UX/CRO, Reputation -->
<div class="gauge-card">
<svg width="100" height="100" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="#E5E7EB" stroke-width="8"/>
<circle cx="60" cy="60" r="54" fill="none"
stroke="{SUB_SCORE_COLOR}"
stroke-width="8"
stroke-dasharray="339.29"
stroke-dashoffset="{CALC: 339.29 * (1 - SUB_SCORE/100)}"
transform="rotate(-90 60 60)"
stroke-linecap="round"/>
<text x="60" y="55" text-anchor="middle" dominant-baseline="central"
font-size="28" font-weight="700" fill="var(--text-primary)">{SUB_SCORE}</text>
<text x="60" y="80" text-anchor="middle"
font-size="9" fill="var(--text-secondary)">/100</text>
</svg>
<div class="gauge-label">{SUB_SCORE_LABEL}</div>
</div>
<!-- END REPEAT -->
</div>
</div>
SECTION 4B: PROPRIETARY FRAMEWORK DASHBOARD
<!-- ============================================
PROPRIETARY FRAMEWORK SCORES
============================================ -->
<div class="section page-break">
<div class="section-header">
<h2>02B — Proprietary Framework Scores</h2>
<div class="section-sub">LOCAL-IMPACT & SERP-TRUST Assessment</div>
</div>
<!-- SEO Health Index (combined master score) -->
<div style="text-align: center; margin-bottom: 30px;">
<div style="font-size: 10pt; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px;">SEO Health Index</div>
<svg width="180" height="180" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="#E5E7EB" stroke-width="10"/>
<circle cx="60" cy="60" r="54" fill="none"
stroke="{SEO_HEALTH_COLOR}"
stroke-width="10"
stroke-dasharray="339.29"
stroke-dashoffset="{CALC: 339.29 * (1 - SEO_HEALTH_INDEX/100)}"
transform="rotate(-90 60 60)"
stroke-linecap="round"/>
<text x="60" y="50" text-anchor="middle" dominant-baseline="central"
font-size="32" font-weight="800" fill="var(--text-primary)">{SEO_HEALTH_INDEX}</text>
<text x="60" y="72" text-anchor="middle"
font-size="10" fill="var(--text-secondary)" font-weight="600">Grade: {SEO_HEALTH_GRADE}</text>
</svg>
<div style="font-size: 8.5pt; color: var(--text-muted); margin-top: 8px;">LOCAL-IMPACT (55%) + SERP-TRUST (45%)</div>
</div>
<!-- Two-column framework gauges -->
<div class="grid-2">
<!-- LOCAL-IMPACT Score -->
<div style="text-align: center; padding: 20px; background: var(--surface); border-radius: 10px; border: 1px solid var(--border);">
<div style="font-size: 9pt; font-weight: 700; color: var(--primary); text-transform: uppercase; letter-spacing: 0.5px;">LOCAL-IMPACT</div>
<div style="font-size: 7.5pt; color: var(--text-muted); margin-bottom: 10px;">60-Item Local Presence Assessment</div>
<svg width="120" height="120" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="#E5E7EB" stroke-width="8"/>
<circle cx="60" cy="60" r="54" fill="none"
stroke="{LOCAL_IMPACT_COLOR}"
stroke-width="8"
stroke-dasharray="339.29"
stroke-dashoffset="{CALC: 339.29 * (1 - LOCAL_IMPACT_SCORE/100)}"
transform="rotate(-90 60 60)"
stroke-linecap="round"/>
<text x="60" y="55" text-anchor="middle" dominant-baseline="central"
font-size="28" font-weight="700" fill="var(--text-primary)">{LOCAL_IMPACT_SCORE}</text>
<text x="60" y="78" text-anchor="middle"
font-size="9" fill="var(--text-secondary)">Grade: {LOCAL_IMPACT_GRADE}</text>
</svg>
</div>
<!-- SERP-TRUST Score -->
<div style="text-align: center; padding: 20px; background: var(--surface); border-radius: 10px; border: 1px solid var(--border);">
<div style="font-size: 9pt; font-weight: 700; color: var(--primary); text-transform: uppercase; letter-spacing: 0.5px;">SERP-TRUST</div>
<div style="font-size: 7.5pt; color: var(--text-muted); margin-bottom: 10px;">50-Item Search Trust Assessment</div>
<svg width="120" height="120" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="54" fill="none" stroke="#E5E7EB" stroke-width="8"/>
<circle cx="60" cy="60" r="54" fill="none"
stroke="{SERP_TRUST_COLOR}"
stroke-width="8"
stroke-dasharray="339.29"
stroke-dashoffset="{CALC: 339.29 * (1 - SERP_TRUST_SCORE/100)}"
transform="rotate(-90 60 60)"
stroke-linecap="round"/>
<text x="60" y="55" text-anchor="middle" dominant-baseline="central"
font-size="28" font-weight="700" fill="var(--text-primary)">{SERP_TRUST_SCORE}</text>
<text x="60" y="78" text-anchor="middle"
font-size="9" fill="var(--text-secondary)">Grade: {SERP_TRUST_GRADE}</text>
</svg>
</div>
</div>
<!-- Dimension Breakdown Bars -->
<h3>LOCAL-IMPACT Dimensions</h3>
<div class="bar-chart">
<!-- REPEAT for each LOCAL-IMPACT dimension: L, O, C, A, L2, I, P, T -->
<div class="bar-row">
<span class="bar-label">{DIMENSION_NAME}</span>
<div class="bar-track"><div class="bar-fill client" style="width: {DIMENSION_PCT}%">{DIMENSION_SCORE}/{DIMENSION_MAX}</div></div>
</div>
<!-- END REPEAT -->
</div>
<h3>SERP-TRUST Dimensions</h3>
<div class="bar-chart">
<!-- REPEAT for each SERP-TRUST dimension: T, R, U, S, T2 -->
<div class="bar-row">
<span class="bar-label">{DIMENSION_NAME}</span>
<div class="bar-track"><div class="bar-fill client" style="width: {DIMENSION_PCT}%">{DIMENSION_SCORE}/{DIMENSION_MAX}</div></div>
</div>
<!-- END REPEAT -->
</div>
<!-- Veto Check Status -->
<h3>Veto Checks</h3>
<div class="grid-2">
<div class="no-break">
<h4>LOCAL-IMPACT Vetoes</h4>
<!-- REPEAT for each veto -->
<div style="padding: 4px 0; font-size: 9pt;">
<span class="{status-pass|status-fail}">{VETO_DESCRIPTION}</span>
</div>
<!-- END REPEAT -->
</div>
<div class="no-break">
<h4>SERP-TRUST Vetoes</h4>
<!-- REPEAT for each veto -->
<div style="padding: 4px 0; font-size: 9pt;">
<span class="{status-pass|status-fail}">{VETO_DESCRIPTION}</span>
</div>
<!-- END REPEAT -->
</div>
</div>
<!-- Competitor Framework Comparison -->
<h3>Competitor Framework Comparison</h3>
<table>
<thead>
<tr>
<th>Framework</th>
<th>{BUSINESS_NAME}</th>
<th>{COMP_1}</th>
<th>{COMP_2}</th>
<th>{COMP_3}</th>
</tr>
</thead>
<tbody>
<tr>
<td>LOCAL-IMPACT</td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
</tr>
<tr>
<td>SERP-TRUST</td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
</tr>
<tr style="font-weight: 700;">
<td>SEO Health Index</td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
<td><span class="badge {BADGE}">{SCORE}</span></td>
</tr>
</tbody>
</table>
</div>
SECTION 5: CRITICAL ISSUES
<!-- ============================================
CRITICAL ISSUES
============================================ -->
<div class="section break-before">
<div class="section-header">
<h2>03 — Critical Issues</h2>
<div class="section-sub">Fix these immediately — they are blocking your SEO performance</div>
</div>
<!-- REPEAT for each critical issue -->
<div class="issue-card critical">
<div class="issue-title">{ISSUE_TITLE}</div>
<div class="issue-desc">{ISSUE_DESCRIPTION_AND_FIX_STEPS}</div>
<div class="issue-meta">
<span class="badge badge-critical">Critical</span>
<span class="badge badge-effort">{EFFORT_ESTIMATE}</span>
<span class="badge badge-info">{AUDIT_PHASE}</span>
</div>
</div>
<!-- END REPEAT -->
<!-- Then HIGH priority issues -->
<h3>High Priority Issues</h3>
<!-- REPEAT for each high issue using class="issue-card high" -->
</div>
SECTION 6: PRIORITY MATRIX
<!-- ============================================
PRIORITY MATRIX
============================================ -->
<div class="section break-before">
<div class="section-header">
<h2>04 — Priority Matrix</h2>
<div class="section-sub">Impact vs Effort — focus on the top-left quadrant first</div>
</div>
<div class="matrix">
<div class="matrix-cell matrix-q1">
<h4>High Impact / Low Effort (Do First)</h4>
<ul>
<!-- Quick wins — highest priority -->
<li>{ITEM}</li>
</ul>
</div>
<div class="matrix-cell matrix-q2">
<h4>High Impact / High Effort (Plan & Schedule)</h4>
<ul>
<li>{ITEM}</li>
</ul>
</div>
<div class="matrix-cell matrix-q4">
<h4>Low Impact / Low Effort (Fill In Gaps)</h4>
<ul>
<li>{ITEM}</li>
</ul>
</div>
<div class="matrix-cell matrix-q3">
<h4>Low Impact / High Effort (Deprioritize)</h4>
<ul>
<li>{ITEM}</li>
</ul>
</div>
</div>
</div>
SECTION 7: QUICK WINS
<!-- ============================================
QUICK WINS
============================================ -->
<div class="section break-before">
<div class="section-header">
<h2>05 — Quick Wins</h2>
<div class="section-sub">High-impact fixes you can implement today</div>
</div>
<!-- REPEAT for each quick win -->
<div class="quick-win">
<div class="quick-win-num">{N}</div>
<div class="quick-win-content">
<strong>{WIN_TITLE}</strong>
<p>{WIN_DESCRIPTION_AND_STEPS}</p>
</div>
</div>
<!-- END REPEAT -->
</div>
SECTION 8: COMPETITOR SUMMARY
<!-- ============================================
COMPETITOR SUMMARY
============================================ -->
<div class="section break-before">
<div class="section-header">
<h2>06 — Competitor Summary</h2>
<div class="section-sub">How you compare to your top competitors</div>
…(truncated)