diff --git a/packages/codegraph/data/skill/seo-audit/SKILL.md b/packages/codegraph/data/skill/seo-audit/SKILL.md
index 5123a4f..37b1d76 100644
--- a/packages/codegraph/data/skill/seo-audit/SKILL.md
+++ b/packages/codegraph/data/skill/seo-audit/SKILL.md
@@ -1,108 +1,208 @@
---
name: seo-audit
description: >
- Full website SEO audit with parallel subagent delegation. Crawls up to 500
- pages, detects business type, delegates to 6 specialists, generates health
- score. Use when user says "audit", "full SEO check", "analyze my site",
- or "website health check".
-version: 1.0.0
+ Full-site SEO audit orchestrator. Crawls the site, detects business type,
+ fans out to the seo-* specialist skills, then aggregates an evidence-backed
+ 0-100 health score and a prioritized action plan (Critical → Low). Use when
+ user says "audit", "full SEO check", "analyze my site", "website health
+ check", or "SEO report". Triggers on: seo audit, full seo check, site health
+ score, action plan, crawl my site, technical+content+schema audit.
+version: 1.1.0
---
-
-# Full Website SEO Audit
-
-## Process
-
-1. **Fetch homepage** — use `scripts/fetch_page.py` to retrieve HTML
-2. **Detect business type** — analyze homepage signals per seo orchestrator
-3. **Crawl site** — follow internal links up to 500 pages, respect robots.txt
-4. **Delegate to subagents** (if available, otherwise run inline sequentially):
- - `seo-technical` — robots.txt, sitemaps, canonicals, Core Web Vitals, security headers
- - `seo-content` — E-E-A-T, readability, thin content, AI citation readiness
- - `seo-schema` — detection, validation, generation recommendations
- - `seo-sitemap` — structure analysis, quality gates, missing pages
- - `seo-performance` — LCP, INP, CLS measurements
- - `seo-visual` — screenshots, mobile testing, above-fold analysis
-5. **Score** — aggregate into SEO Health Score (0-100)
-6. **Report** — generate prioritized action plan
-
-## Crawl Configuration
-
-```
-Max pages: 500
-Respect robots.txt: Yes
-Follow redirects: Yes (max 3 hops)
-Timeout per page: 30 seconds
-Concurrent requests: 5
-Delay between requests: 1 second
-```
-
-## Output Files
-
-- `FULL-AUDIT-REPORT.md` — Comprehensive findings
-- `ACTION-PLAN.md` — Prioritized recommendations (Critical → High → Medium → Low)
-- `screenshots/` — Desktop + mobile captures (if Playwright available)
-
-## Scoring Weights
-
-| Category | Weight |
-|----------|--------|
-| Technical SEO | 25% |
-| Content Quality | 25% |
-| On-Page SEO | 20% |
-| Schema / Structured Data | 10% |
-| Performance (CWV) | 10% |
-| Images | 5% |
-| AI Search Readiness | 5% |
-
-## Report Structure
-
-### Executive Summary
-- Overall SEO Health Score (0-100)
-- Business type detected
-- Top 5 critical issues
-- Top 5 quick wins
-
-### Technical SEO
-- Crawlability issues
-- Indexability problems
-- Security concerns
-- Core Web Vitals status
-
-### Content Quality
-- E-E-A-T assessment
-- Thin content pages
-- Duplicate content issues
-- Readability scores
-
-### On-Page SEO
-- Title tag issues
-- Meta description problems
-- Heading structure
-- Internal linking gaps
-
-### Schema & Structured Data
-- Current implementation
-- Validation errors
-- Missing opportunities
-
-### Performance
-- LCP, INP, CLS scores
-- Resource optimization needs
-- Third-party script impact
-
-### Images
-- Missing alt text
-- Oversized images
-- Format recommendations
-
-### AI Search Readiness
-- Citability score
-- Structural improvements
-- Authority signals
-
-## Priority Definitions
-
-- **Critical**: Blocks indexing or causes penalties (fix immediately)
-- **High**: Significantly impacts rankings (fix within 1 week)
-- **Medium**: Optimization opportunity (fix within 1 month)
-- **Low**: Nice to have (backlog)
+
+# Full-Site SEO Audit (Orchestrator)
+
+This is the **umbrella** skill. It does not do the deep work itself — it crawls,
+classifies the site, **delegates** to the specialist `seo-*` skills, then merges
+their findings into one scored report. Every finding it emits carries **evidence**,
+a **falsifiability check**, and a **leading indicator** so the report is verifiable
+rather than vibes.
+
+## When to use
+
+- Someone asks for a whole-site audit / health check / SEO report.
+- You need a single prioritized backlog across technical, content, schema, perf, etc.
+- Before a redesign or migration (capture a baseline), or after launch (verify).
+
+For a single concern, skip this and go straight to the specialist (e.g. just schema →
+`seo-schema`). Use this when the ask is "how healthy is the whole site".
+
+## Process
+
+1. **Render the homepage.** Fetch raw + rendered HTML and extracted text (use your
+ own crawler/headless tooling). Note SPA vs SSR — for our stack this is Next.js 15
+ App Router, so most routes are RSC/SSR; flag any client-only routes that ship
+ empty initial HTML.
+2. **Detect business type / vertical.** SaaS, local service, e-commerce, publisher,
+ marketing site. This decides which conditional specialists fan out and how weights
+ shift (see Scoring).
+3. **Crawl** internal links (config below), respect `robots.txt`, dedupe by canonical.
+4. **Fan out to specialists** (run as parallel subagents if available, else inline
+ in priority order). Each returns per-finding evidence + a category sub-score.
+5. **Aggregate** into the 0-100 SEO Health Score using the weighted rubric.
+6. **Prioritize** every finding into Critical → High → Medium → Low.
+7. **Report**: executive summary, per-category sections, action plan grouped by phase.
+
+## Specialist delegation map
+
+Always run the core set. Add conditional specialists based on detected vertical /
+available data. Names below are the actual sibling skills in this catalog.
+
+| Specialist skill | Owns | Run when |
+|-------------------------|---------------------------------------------------------------------|---------------------|
+| `seo-technical` | robots.txt, sitemaps, canonicals, indexability, redirects, headers, CWV reality | always |
+| `seo-content` | E-E-A-T, search intent, thin/duplicate content, readability | always |
+| `seo-page` | On-page: title, meta description, headings, internal links per page | always |
+| `seo-schema` | JSON-LD detection, validation, missing-type opportunities | always |
+| `seo-images` | alt text, dimensions/CLS, format (AVIF/WebP), `next/image` usage | always |
+| `seo-geo` / `ai-seo` | AI-crawler access, llms.txt, citability, answer-engine readiness | always |
+| `seo-sitemap-advanced` | sitemap structure, quality gates, index/sub-sitemaps, missing pages | sites > ~50 pages |
+| `seo-hreflang` | hreflang correctness, x-default, return tags (next-intl locales) | multi-locale sites |
+| `programmatic-seo` / `seo-programmatic` | template/page-pattern quality, dup risk at scale | templated/large catalogs |
+| `seo-competitor-pages` | gap vs competitor pages for target queries | strategy/ranking goals |
+| `seo-for-devs` | implementation fixes mapped to Next.js/Payload code | when handing fixes to devs |
+
+Performance (LCP/INP/CLS) is part of `seo-technical`'s CWV reality check; capture
+field data if a CrUX/RUM source exists, otherwise label numbers as lab estimates.
+
+## Crawl configuration
+
+```
+Max pages: 500
+Respect robots.txt: yes
+Follow redirects: yes (max 3 hops)
+Timeout per page: 30s
+Concurrent requests: 5
+Delay between requests: 1s
+Canonical dedupe: yes (collapse param/duplicate URLs)
+```
+
+On large sites, cap at the page limit, report what was crawled, and estimate total
+scope rather than silently truncating.
+
+## Scoring (0-100, weighted)
+
+Each specialist returns a category sub-score (0-100). Combine with these weights.
+Shift weights by vertical (e.g. e-commerce → raise Schema; publisher → raise Content;
+local → raise the geo/local signal). State the weights you used in the report.
+
+| Category | Weight | Default rubric anchor |
+|--------------------------|:------:|-------------------------------------------------------|
+| Technical SEO | 22% | crawlable, indexable, no canonical/redirect chaos |
+| Content Quality | 23% | intent match, E-E-A-T signals, no thin/dup pages |
+| On-Page SEO | 20% | unique titles/meta, clean heading tree, internal links |
+| Schema / Structured Data | 10% | valid JSON-LD for the page types that warrant it |
+| Performance (CWV) | 10% | LCP < 2.5s, INP < 200ms, CLS < 0.1 (p75 field if avail) |
+| AI Search Readiness | 10% | AI crawlers allowed, citable structure, llms.txt |
+| Images | 5% | alt text present, sized to avoid CLS, modern formats |
+
+**Sub-score banding:** 90-100 excellent · 70-89 good · 50-69 needs work ·
+30-49 poor · 0-29 critical. The overall score is the weighted mean, rounded.
+
+## Evidence + falsifiability per finding (claude-seo's core value)
+
+Do not emit a finding without all four fields. This is what makes the audit honest
+and re-checkable instead of generic advice.
+
+- **Claim** — the specific problem, with the exact URL(s)/selector/value.
+- **Evidence** — what was observed (the rendered title, the HTTP status, the LCP
+ element, the missing JSON-LD field). Quote the real value, not a paraphrase.
+- **Falsifiability** — "How would we know this finding is wrong?" State the concrete
+ observation that would overturn it (e.g. "wrong if GSC shows these URLs indexed
+ within 14 days despite the noindex we flagged").
+- **Leading indicator** — the metric that should move first if the fix works, and
+ roughly when (e.g. "impressions in GSC for the target query cluster within 2-4
+ weeks", "INP p75 in CrUX next 28-day window", "valid items in Rich Results Test").
+
+Per-finding record shape:
+
+```json
+{
+ "title": "Homepage
duplicated across 38 routes",
+ "category": "On-Page SEO",
+ "severity": "High",
+ "evidence": "38 crawled URLs return identical 'Acme — Home'; e.g. /pricing, /about, /blog/x",
+ "recommendation": "Set route-level generateMetadata() titles; use a title.template in the root layout",
+ "falsifiability": "Wrong if these routes already have unique titles in rendered HTML (we only saw the streamed shell)",
+ "leading_indicator": "Unique-title coverage in next crawl → CTR lift in GSC for those URLs within 3-4 weeks"
+}
+```
+
+## Priority definitions
+
+- **Critical** — blocks indexing or triggers penalties (noindex on money pages,
+ blocked in robots.txt, broken canonical loops, manual action). Fix immediately.
+- **High** — materially suppresses rankings/CTR (duplicate titles at scale, missing
+ H1s on key pages, INP failing on primary templates). Fix within ~1 week.
+- **Medium** — real optimization upside (thin sections, schema gaps, image weight).
+ Fix within ~1 month.
+- **Low** — polish / backlog (minor alt-text gaps, nice-to-have schema).
+
+Tie-break by reach (how many pages) × impact (how close to conversion/indexation) ÷
+effort. Put the top quick wins (high impact, low effort) up front in the summary.
+
+## Report structure
+
+### Executive summary
+- Overall SEO Health Score (0-100) + per-category sub-scores and the weights used.
+- Detected business type / vertical and which specialists were run.
+- Top 5 critical issues and top 5 quick wins, each with its leading indicator.
+
+### Per-category sections
+For each category: sub-score, **what works**, then findings (each with the four
+evidence fields above), then the category's recommended fixes.
+
+### Action plan (phased)
+- **Phase 1 — Critical (Week 1):** indexability/crawl blockers.
+- **Phase 2 — High impact (Weeks 2-3):** on-page + CWV on primary templates.
+- **Phase 3 — Content & authority (Month 2):** depth, E-E-A-T, schema, internal links.
+- **Phase 4 — Monitoring (ongoing):** track the leading indicators; re-audit cadence.
+
+## Next.js 15 stack notes for the auditor
+
+These are the recurring, stack-specific causes behind common findings. Use them so
+recommendations map to real code rather than generic advice.
+
+- **Metadata** lives in `generateMetadata()` / the static `metadata` export per route
+ segment. Duplicate or missing titles/descriptions are almost always a missing
+ route-level export, not a CMS issue. A `title.template` in the root layout fixes
+ "brand suffix everywhere" cheaply.
+- **Canonicals & alternates** belong in `metadata.alternates` (`canonical` plus
+ `languages` for next-intl locales). Missing hreflang on a multi-locale site is a
+ `seo-hreflang` finding but the fix is here.
+- **`robots` / indexability** — a stray `metadata.robots = { index: false }` (often
+ left from staging) on a production route is a Critical finding. Also check
+ `app/robots.ts` and any middleware that sets `X-Robots-Tag`.
+- **Sitemaps** — `app/sitemap.ts` (and `generateSitemaps` for large/templated sets).
+ A static `public/sitemap.xml` that drifts from real routes is a common defect.
+- **Empty initial HTML** — `'use client'` at a route boundary or data fetched only in
+ `useEffect` means crawlers see a shell. Move data fetching to the server component;
+ this is both an indexation and an LCP problem.
+- **CWV** — LCP is usually an unoptimized hero image (use `next/image` with `priority`
+ and correct `sizes`); CLS is unsized media or late-injected banners; INP is heavy
+ client hydration. Map each to the owning template.
+- **Images** — `next/image` enforces dimensions (kills CLS) and serves AVIF/WebP;
+ flag raw ` ` on key pages.
+- **Payload CMS** — when titles/descriptions come from CMS fields, a finding may be
+ "editors left the field blank" rather than a code bug; recommend required SEO
+ fields + sensible fallbacks in `generateMetadata()`.
+
+## Output artifacts
+
+- `FULL-AUDIT-REPORT.md` — comprehensive findings (the structure above).
+- `ACTION-PLAN.md` — prioritized backlog (Critical → High → Medium → Low / phased).
+- `audit-data.json` — structured envelope (summary + categories[] with the
+ four-field findings + phased action_plan) so a report can be regenerated.
+- `screenshots/` — desktop + mobile captures, if a headless browser is available.
+
+## Error handling
+
+| Scenario | Action |
+|----------|--------|
+| URL unreachable (DNS / connection refused) | Report clearly. Do not guess content. Ask the user to verify the URL. |
+| robots.txt blocks crawling | Report which paths are blocked; audit only accessible pages and note the limitation. |
+| Rate limiting (429) | Back off, reduce concurrency, report partial results with what is missing. |
+| Large site (> 500 pages) | Cap at the limit, report crawled pages, estimate total scope. |
+| Client-only route (empty shell) | Note rendered HTML was empty; mark related findings with that caveat in falsifiability. |
+
+> Parts adapted from [claude-seo](https://github.com/AgriciDaniel/claude-seo) (MIT, © 2026 agricidaniel).
diff --git a/packages/codegraph/data/skill/seo-backlinks/SKILL.md b/packages/codegraph/data/skill/seo-backlinks/SKILL.md
new file mode 100644
index 0000000..83ca56d
--- /dev/null
+++ b/packages/codegraph/data/skill/seo-backlinks/SKILL.md
@@ -0,0 +1,245 @@
+---
+name: seo-backlinks
+description: "Off-page/backlink audit: link-profile analysis, referring-domain quality, anchor-text distribution, toxic-link detection, disavow guidance, expired-domain heritage risk, parasite-SEO exposure, and digital-PR link earning. Use when auditing or building backlinks, assessing a link profile, judging link toxicity, or planning link acquisition. Triggers on: backlinks, link profile, referring domains, anchor text, toxic links, disavow, link gap, link building, digital PR, expired domain, parasite SEO, off-page SEO."
+version: 1.0.0
+---
+
+# Backlink / Off-Page SEO Audit
+
+Off-page SEO is the part of ranking you do not fully control: who links to a site,
+with what anchor text, and how trustworthy those sources are. This skill is an
+**audit + acquisition methodology**, not a vendor wrapper. Backlink discovery needs a
+crawl of the web's link graph, which no public crawler gives in full — so the metrics
+here depend on a **backlink data source** (a backlink API, search-console link export,
+or an open web-graph dataset). See the `seo-rank-data` skill for sourcing that data and
+its confidence weighting. Everything below is the analysis you run *on top of* that data.
+
+## When to use
+
+- Auditing why a site under- or over-performs vs. its link profile.
+- Vetting a domain before purchase (expired/aged domain heritage risk).
+- Detecting toxic links and deciding whether to disavow.
+- Finding link-building opportunities (competitor gap, digital PR).
+- Checking exposure to parasite-SEO / negative-SEO patterns.
+
+## Data sources and confidence
+
+You will almost never have a complete link graph. Label every metric with its source
+and a confidence weight, and never present an inferred number as a measured fact.
+
+| Source | What it gives | Confidence | Freshness |
+|--------|---------------|------------|-----------|
+| Commercial backlink API (e.g. a SaaS index) | Backlinks, referring domains, authority, anchors, follow ratio, velocity | 1.0 | days–weeks |
+| Search Console link export (own site only) | Top linking sites + anchors (sampled, no authority) | 0.85 | ~days |
+| Open web-graph dataset (e.g. Common Crawl) | Domain-level in-degree, harmonic centrality, top referrers (no authority scores) | 0.5 | quarterly |
+| Live verification crawl (your own crawler) | Whether a *known* backlink still exists, follow/nofollow, anchor | 0.95 | real-time |
+
+**Data sufficiency gate (falsifiability):** of the 7 scored factors below, count how many
+have at least one source. If fewer than 4 are covered, do **not** emit a numeric 0–100
+score — emit `INSUFFICIENT DATA (X/7 factors)` plus the factor scores you do have. A
+confident number built on one weak source reads as "poor health" when the truth is
+"we lack data". When only an open web-graph dataset is available, cap the score at 70/100.
+
+## Audit framework (produce all 7 sections)
+
+### 1. Profile overview
+
+Pull totals: backlinks, referring domains, follow ratio, authority/rank, trend.
+
+| Metric | Good | Warning | Critical |
+|--------|------|---------|----------|
+| Referring domains | >100 | 20–100 | <20 |
+| Follow ratio | >60% | 40–60% | <40% |
+| Single-domain concentration | no domain >5% of links | one domain >10% | one domain >25% |
+| Trend | growing or stable | slow decline | rapid decline (>20%/quarter) |
+
+### 2. Anchor-text distribution
+
+Over-optimized anchors are the classic algorithmic-penalty (Penguin-lineage) signal.
+
+| Anchor type | Healthy range | Over-optimization flag |
+|-------------|---------------|------------------------|
+| Branded (company/domain) | 30–50% | <15% (looks built, not earned) |
+| Naked URL | 15–25% | — |
+| Generic ("click here", "read more") | 10–20% | — |
+| Exact-match keyword | 3–10% | **>15%** |
+| Partial-match keyword | 5–15% | >25% |
+| Long-tail / natural sentence | 5–15% | — |
+
+Flag exact-match anchors over 15%, especially when concentrated from few domains —
+that is a manipulation fingerprint, not natural editorial linking.
+
+### 3. Referring-domain quality
+
+- **TLD mix:** .edu/.gov/.org skew authoritative; a flood of cheap .xyz/.top/.info skews spam.
+- **Geo mix:** should roughly match the target market; 80%+ from irrelevant countries is a PBN tell.
+- **Authority spread:** healthy profiles have links across all authority tiers, not only the top.
+- **Per-domain follow/nofollow:** domains that *only* nofollow pass branding but little ranking value.
+
+### 4. Toxic-link detection
+
+High-risk patterns (flag immediately, candidates for disavow):
+- Private Blog Network (PBN) footprints: shared hosting/IP, identical themes, thin reciprocal money sites.
+- 100% exact-match anchors from a single domain.
+- Links from penalized or deindexed domains.
+- Mass directory dumps (50+ low-quality directory links).
+- Link farms / pages with 10k+ outbound links.
+- Sitewide footer/sidebar links across an entire domain (paid-link footprint).
+
+Medium-risk (review manually before acting):
+- Links from unrelated niches; reciprocal A↔B patterns; links from thin pages (<100 words);
+ >50 backlinks all from one domain.
+
+### 5. Expired / aged-domain heritage risk
+
+Before buying or migrating onto an aged domain, audit its *past life* — Google carries
+history forward, and a domain can arrive pre-penalized.
+- Pull historical snapshots (web archive) for prior content: was it a real site, or a spam/PBN/adult/gambling flip?
+- Check the existing backlink profile against sections 2–4 *before* relaunch.
+- Confirm it was not previously deindexed; verify a clean reindex after relaunch.
+- A domain with strong raw metrics but a toxic anchor profile or off-niche history is a liability, not a shortcut.
+
+### 6. Parasite-SEO / negative-SEO exposure
+
+- **Parasite SEO (inbound risk):** spam pages hosted on a high-authority host (UGC subdomains,
+ open profile/forum pages, abandoned subfolders) can borrow your authority. Audit subdomains
+ and user-generated areas for content you did not publish.
+- **Negative SEO:** a sudden spike of toxic links pointed at you. The defense is the *velocity*
+ baseline in section 7 — without it you cannot tell an attack from organic growth.
+
+### 7. Link velocity & gap
+
+- **New/lost links over 30/60/90 days** (needs a time-series source). Watch for:
+ unexplained spikes (possible negative-SEO injection), mass losses (penalty or content removal),
+ and steadily declining velocity (content no longer earning links).
+- **Competitor gap:** domains linking to a competitor but *not* to the target = the prioritized
+ acquisition list; domains linking to both = relationships to deepen.
+
+## Backlink health score (0–100)
+
+| Factor | Weight | Preferred source order |
+|--------|--------|------------------------|
+| Referring-domain count | 20% | API > web-graph in-degree |
+| Domain-quality distribution | 20% | API authority > web-graph centrality |
+| Anchor-text naturalness | 15% | API > Search Console anchors |
+| Toxic-link ratio | 20% | API spam signals > verification crawl |
+| Link-velocity trend | 10% | API time-series only |
+| Follow/nofollow ratio | 5% | API > verification crawl |
+| Geographic relevance | 10% | API country data |
+
+Score each factor 0–100, weight, sum. Redistribute the weight of any unsourced factor
+proportionally across the rest. Always print which factors were scored vs. skipped, with
+their source and confidence — apply the sufficiency gate from above before showing a number.
+
+## Falsifiability check (run before delivering)
+
+State, for every material claim, **how you would know it was wrong** and the **leading
+indicator** to watch.
+
+| Claim | How would we know it's wrong? | Leading indicator |
+|-------|-------------------------------|-------------------|
+| "This link is toxic / disavow it" | Disavowing a borderline link drops a ranking it was actually helping | Track rankings 4–8 weeks post-disavow; disavow conservatively, in batches |
+| "We have a toxic-anchor problem" | The over-optimized anchors come from scraper copies of one real editorial link, not built links | Group anchors by *root domain*, not by raw count |
+| "Link removed" | The page is JS-rendered and the `` exists post-hydration | Mark `unverifiable_js` instead of `removed` when the source is an SPA shell |
+| "Negative-SEO attack" | The spike is a legitimate viral/PR event | Compare against the velocity baseline + check anchor/topic relevance of new links |
+| "This aged domain is safe to buy" | Post-relaunch it fails to index or rankings never materialize | Verify reindex + run a fresh profile audit 30 days after relaunch |
+| "Referring-domain count = N" | N counts links, or counts duplicate subdomains as distinct | Deduplicate to registrable domains; reconcile summary vs. the verified link list |
+
+If any check fails, fix the finding before reporting. Distinguish "not crawled" vs.
+"below threshold" vs. "error" — never collapse them into "not found".
+
+## Disavow guidance
+
+Disavow is a last resort, scoped to **manipulative links you cannot get removed**.
+1. Attempt manual removal first (outreach to the linking site).
+2. Disavow at **domain** level (`domain:example.com`) for whole-domain spam; per-URL only for surgical cases.
+3. Keep it conservative — disavowing healthy links removes ranking signal. Re-measure after 4–8 weeks.
+4. Google's own guidance: most sites never need a disavow file; the algorithm ignores most spam automatically.
+
+## Link earning (digital PR — the durable strategy)
+
+The only links that survive algorithm updates are *editorially earned*. Prioritize creating
+link-worthy assets over chasing volume:
+- **Original data / research:** surveys, benchmarks, "state of X" reports — journalists cite primary data.
+- **Free tools & calculators:** a small Next.js utility page earns links passively for years.
+- **Expert commentary / reactive PR:** respond to journalist requests in your niche.
+- **Genuinely useful long-form** that becomes the canonical reference for a query.
+
+For our stack, ship link-bait assets as fast, indexable RSC routes:
+
+```tsx
+// app/[locale]/tools/roi-calculator/page.tsx — a linkable free-tool page (RSC + client island)
+import type { Metadata } from "next";
+import { getTranslations } from "next-intl/server";
+import { Calculator } from "./calculator"; // "use client" island for interactivity
+
+export async function generateMetadata(
+ { params }: { params: Promise<{ locale: string }> },
+): Promise {
+ const { locale } = await params;
+ const t = await getTranslations({ locale, namespace: "roiTool" });
+ const url = `${process.env.NEXT_PUBLIC_SITE_URL}/${locale}/tools/roi-calculator`;
+ return {
+ title: t("title"),
+ description: t("description"),
+ alternates: { canonical: url }, // stable canonical = links consolidate to one URL
+ openGraph: { url, title: t("title"), description: t("description") },
+ };
+}
+
+export default function Page() {
+ // Server-render the explanatory copy (crawlable, link-worthy);
+ // hydrate only the interactive widget. Keeps the asset fast + indexable.
+ return (
+
+ ROI Calculator
+
+
+ );
+}
+```
+
+JSON-LD that makes original research citable (helps journalists and AI assistants attribute you):
+
+```tsx
+// Inline in the asset page's RSC body
+const jsonLd = {
+ "@context": "https://schema.org",
+ "@type": "Dataset",
+ name: "2026 SaaS Onboarding Benchmark",
+ description: "Survey of 1,200 B2B SaaS onboarding flows.",
+ creator: { "@type": "Organization", name: "Your Org" },
+ license: "https://creativecommons.org/licenses/by/4.0/",
+ url: `${process.env.NEXT_PUBLIC_SITE_URL}/research/onboarding-2026`,
+};
+//
+```
+
+**Protect earned equity on our stack:**
+- When a linked URL changes, add a **301 redirect** in `next.config` (or middleware) so link equity transfers — never 404 a page that has backlinks.
+- Reclaim links pointing at 404s: export referring URLs to dead pages, then 301 them to the nearest live equivalent.
+- Keep canonicals stable; locale variants (`next-intl`) should canonical to the right localized URL so links to a campaign consolidate instead of splitting.
+
+## Output format
+
+```
+Backlink Health Score: XX/100 (or INSUFFICIENT DATA — Y/7 factors scored)
+
+| Section | Status | Score | Source (confidence) |
+|---------------------------|---------------|--------|---------------------|
+| Profile overview | pass/warn/fail| XX/100 | API (1.0) |
+| Anchor distribution | pass/warn/fail| XX/100 | SC (0.85) |
+| Referring-domain quality | pass/warn/fail| XX/100 | web-graph (0.5) |
+| Toxic links | pass/warn/fail| XX/100 | API (1.0) |
+| Expired/heritage risk | info | N/A | archive + API |
+| Parasite/negative exposure| pass/warn/fail| XX/100 | crawl (0.95) |
+| Link velocity & gap | pass/warn/fail| XX/100 | API time-series |
+
+Critical (fix now) · High (≤1 month) · Medium (ongoing) · Link-building opportunities (top 10)
+```
+
+Do not duplicate other skills: send crawlability/redirect-mechanics deep-dives to
+`seo-technical`, E-E-A-T/content authority to `seo-content`, and data-source setup to
+`seo-rank-data`.
+
+> Parts adapted from [claude-seo](https://github.com/AgriciDaniel/claude-seo) (MIT, © 2026 agricidaniel).
diff --git a/packages/codegraph/data/skill/seo-competitor-pages/SKILL.md b/packages/codegraph/data/skill/seo-competitor-pages/SKILL.md
index 2d11b29..c4710b6 100644
--- a/packages/codegraph/data/skill/seo-competitor-pages/SKILL.md
+++ b/packages/codegraph/data/skill/seo-competitor-pages/SKILL.md
@@ -1,205 +1,310 @@
---
name: seo-competitor-pages
description: >
- Generate SEO-optimized competitor comparison and alternatives pages. Covers
- "X vs Y" layouts, "alternatives to X" pages, feature matrices, schema markup,
- and conversion optimization. Use when user says "comparison page", "vs page",
- "alternatives page", "competitor comparison", or "X vs Y".
-version: 1.0.0
+ Build and audit competitor comparison, "alternatives to X", and category roundup
+ pages — competitive content gap analysis, SERP-feature targeting, page-level
+ teardown, feature matrices, JSON-LD, and a 0-100 prioritization rubric. Use when
+ user says "comparison page", "vs page", "alternatives page", "competitor comparison",
+ "content gap", "SERP analysis", "X vs Y". Triggers on: versus, compare competitors,
+ alternative to, comparison table, gap analysis, SERP features, competitive teardown.
+version: 1.1.0
---
-
-# Competitor Comparison & Alternatives Pages
-
-Create high-converting comparison and alternatives pages that target
-competitive intent keywords with accurate, structured content.
-
-## Page Types
-
-### 1. "X vs Y" Comparison Pages
-- Direct head-to-head comparison between two products/services
-- Balanced feature-by-feature analysis
-- Clear verdict or recommendation with justification
-- Target keyword: `[Product A] vs [Product B]`
-
-### 2. "Alternatives to X" Pages
-- List of alternatives to a specific product/service
-- Each alternative with brief summary, pros/cons, best-for use case
-- Target keyword: `[Product] alternatives`, `best alternatives to [Product]`
-
-### 3. "Best [Category] Tools" Roundup Pages
-- Curated list of top tools/services in a category
-- Ranking criteria clearly stated
-- Target keyword: `best [category] tools [year]`, `top [category] software`
-
-### 4. Comparison Table Pages
-- Feature matrix with multiple products in columns
-- Sortable/filterable if interactive
-- Target keyword: `[category] comparison`, `[category] comparison chart`
-
-## Comparison Table Generation
-
-### Feature Matrix Layout
-```
-| Feature | Your Product | Competitor A | Competitor B |
-|------------------|:------------:|:------------:|:------------:|
-| Feature 1 | ✅ | ✅ | ❌ |
-| Feature 2 | ✅ | ⚠️ Partial | ✅ |
-| Feature 3 | ✅ | ❌ | ❌ |
-| Pricing (from) | $X/mo | $Y/mo | $Z/mo |
-| Free Tier | ✅ | ❌ | ✅ |
-```
-
-### Data Accuracy Requirements
-- All feature claims must be verifiable from public sources
-- Pricing must be current (include "as of [date]" note)
-- Update frequency: review quarterly or when competitors ship major changes
-- Link to source for each competitor data point where possible
-
-## Schema Markup Recommendations
-
-### Product Schema with AggregateRating
-```json
-{
- "@context": "https://schema.org",
- "@type": "Product",
- "name": "[Product Name]",
- "description": "[Product Description]",
- "brand": {
- "@type": "Brand",
- "name": "[Brand Name]"
- },
- "aggregateRating": {
- "@type": "AggregateRating",
- "ratingValue": "[Rating]",
- "reviewCount": "[Count]",
- "bestRating": "5",
- "worstRating": "1"
- }
-}
-```
-
-### SoftwareApplication (for software comparisons)
-```json
-{
- "@context": "https://schema.org",
- "@type": "SoftwareApplication",
- "name": "[Software Name]",
- "applicationCategory": "[Category]",
- "operatingSystem": "[OS]",
- "offers": {
- "@type": "Offer",
- "price": "[Price]",
- "priceCurrency": "USD"
- }
-}
-```
-
-### ItemList (for roundup pages)
-```json
-{
- "@context": "https://schema.org",
- "@type": "ItemList",
- "name": "Best [Category] Tools [Year]",
- "itemListOrder": "https://schema.org/ItemListOrderDescending",
- "numberOfItems": "[Count]",
- "itemListElement": [
- {
- "@type": "ListItem",
- "position": 1,
- "name": "[Product Name]",
- "url": "[Product URL]"
- }
- ]
-}
-```
-
-## Keyword Targeting
-
-### Comparison Intent Patterns
-| Pattern | Example | Search Volume Signal |
-|---------|---------|---------------------|
-| `[A] vs [B]` | "Slack vs Teams" | High |
-| `[A] alternative` | "Figma alternatives" | High |
-| `[A] alternatives [year]` | "Notion alternatives 2026" | High |
-| `best [category] tools` | "best project management tools" | High |
-| `[A] vs [B] for [use case]` | "AWS vs Azure for startups" | Medium |
-| `[A] review [year]` | "Monday.com review 2026" | Medium |
-| `[A] vs [B] pricing` | "HubSpot vs Salesforce pricing" | Medium |
-| `is [A] better than [B]` | "is Notion better than Confluence" | Medium |
-
-### Title Tag Formulas
-- X vs Y: `[A] vs [B]: [Key Differentiator] ([Year])`
-- Alternatives: `[N] Best [A] Alternatives in [Year] (Free & Paid)`
-- Roundup: `[N] Best [Category] Tools in [Year] — Compared & Ranked`
-
-### H1 Patterns
-- Match title tag intent
-- Include primary keyword naturally
-- Keep under 70 characters
-
-## Conversion-Optimized Layouts
-
-### CTA Placement
-- **Above fold**: Brief comparison summary with primary CTA
-- **After comparison table**: "Try [Your Product] free" CTA
-- **Bottom of page**: Final recommendation with CTA
-- Avoid aggressive CTAs in competitor description sections (reduces trust)
-
-### Social Proof Sections
-- Customer testimonials relevant to comparison criteria
-- G2/Capterra/TrustPilot ratings (with source links)
-- Case studies showing migration from competitor
-- "Switched from [Competitor]" stories
-
-### Pricing Highlights
-- Clear pricing comparison table
-- Highlight value advantages (not just lowest price)
-- Include hidden costs (setup fees, per-user pricing, overage charges)
-- Link to full pricing page
-
-### Trust Signals
-- "Last updated [date]" timestamp
-- Author with relevant expertise
-- Methodology disclosure (how comparisons were conducted)
-- Disclosure of own product affiliation
-
-## Fairness Guidelines
-
-- **Accuracy**: All competitor information must be verifiable from public sources
-- **No defamation**: Never make false or misleading claims about competitors
-- **Cite sources**: Link to competitor websites, review sites, or documentation
-- **Timely updates**: Review and update when competitors release major changes
-- **Disclose affiliation**: Clearly state which product is yours
-- **Balanced presentation**: Acknowledge competitor strengths honestly
-- **Pricing accuracy**: Include "as of [date]" disclaimers on all pricing data
-- **Feature verification**: Test competitor features where possible, cite documentation otherwise
-
-## Internal Linking
-
-- Link to your own product/service pages from comparison sections
-- Cross-link between related comparison pages (e.g., "A vs B" links to "A vs C")
-- Link to feature-specific pages when discussing individual features
-- Breadcrumb: Home > Comparisons > [This Page]
-- Related comparisons section at bottom of page
-- Link to case studies and testimonials mentioned in the comparison
-
-## Output
-
-### Comparison Page Template
-- `COMPARISON-PAGE.md` — Ready-to-implement page structure with sections
-- Feature matrix table
-- Content outline with word count targets (minimum 1,500 words)
-
-### Schema Markup
-- `comparison-schema.json` — Product/SoftwareApplication/ItemList JSON-LD
-
-### Keyword Strategy
-- Primary and secondary keywords
-- Related long-tail opportunities
-- Content gaps vs existing competitor pages
-
-### Recommendations
-- Content improvements for existing comparison pages
-- New comparison page opportunities
-- Schema markup additions
-- Conversion optimization suggestions
+
+# Competitor Comparison & Alternatives Pages
+
+Two jobs in one skill:
+1. **Analyze** — find the competitive content gap, decide what to build, and prioritize it.
+2. **Build** — ship high-converting comparison/alternatives pages on our stack (Next.js 15 App Router + RSC, TypeScript, Tailwind, Payload CMS, next-intl) that win competitive-intent queries with accurate, structured content.
+
+Start with analysis. Building a page nobody can rank for or that nobody clicks is wasted effort.
+
+---
+
+## Part 1 — Competitive Content Gap Analysis
+
+### Method (per target query)
+1. **Pull the live SERP** for the target query (use your own crawler/SERP tooling). Record the top 10 organic results and every SERP feature present (see SERP table below).
+2. **Classify dominant intent** — is the SERP serving comparison pages, listicles, vendor pages, or forum/UGC? If the SERP is all vendor homepages, a comparison page may be the wrong format.
+3. **Teardown the top 3 pages** (see Part 2). Capture word count, sub-topics covered, entities mentioned, table presence, schema present, freshness signal.
+4. **Build the gap matrix** — sub-topics × competitors. Cells = covered / partial / missing. Your opportunity is the union of (high-value sub-topics) ∩ (thin or missing across the SERP).
+5. **Score and prioritize** each opportunity (rubric below) before committing build effort.
+
+### Gap matrix (the core artifact)
+```
+| Sub-topic / question | Comp A | Comp B | Comp C | Demand | Our angle |
+|-----------------------------|:------:|:------:|:------:|:------:|----------------|
+| Pricing breakdown w/ totals | part | full | miss | high | total-cost calc|
+| Migration steps from X | miss | miss | miss | med | step-by-step |
+| API / integrations depth | full | part | full | low | skip / brief |
+| Real limits & gotchas | miss | part | miss | high | honest section |
+```
+Rule of thumb: prioritize cells that are **high demand + missing/partial across all competitors**. Those are rankable wedges. A sub-topic everyone covers fully ("table stakes") must be present but is not a differentiator.
+
+### Entity & topic coverage check
+- Extract the entities (features, integrations, use cases, named competitors) that top pages co-mention. Missing entities = thin coverage Google can detect.
+- Don't keyword-stuff. Cover the entity *because the topic genuinely needs it*, then move on.
+
+---
+
+## Part 2 — Page-Level Competitive Teardown
+
+For each top-3 result, capture a structured snapshot (use your own crawler/tooling; do not assume any specific script exists):
+
+| Dimension | What to record | Why it matters |
+|-----------|----------------|----------------|
+| Format | vs / alternatives / roundup / vendor | Tells you the SERP-expected format |
+| Word count & depth | total words, # of H2s | Calibrate your minimum, don't pad |
+| Table presence | feature matrix? sortable? | Tables win featured snippets for comparison intent |
+| Schema | Product, SoftwareApplication, ItemList, FAQPage | Eligibility for rich results |
+| Freshness | "updated" date, year in title | Comparison intent is recency-sensitive |
+| Bias signal | balanced vs. hit-piece | Over-biased pages lose trust + links |
+| Internal links | hub/cluster wiring | Reveals their topical authority play |
+| CTA pattern | placement, aggressiveness | Conversion benchmark |
+
+Output: a one-row-per-competitor teardown table feeding the gap matrix. The goal is "be the most complete, most honest, most current page for this query" — derived from evidence, not vibes.
+
+---
+
+## Part 3 — SERP Feature Targeting
+
+Decide which SERP feature you are trying to capture; it changes how you structure the page.
+
+| SERP feature | How to target | Structural requirement |
+|--------------|---------------|------------------------|
+| Featured snippet (table) | Lead with a clean comparison table near the top | Real ``, ≤ ~8 rows, first column = labels |
+| Featured snippet (paragraph) | Answer "is A better than B?" in 40-55 words right after H1 | Direct, self-contained sentence |
+| Featured snippet (list) | "Best X alternatives" as an ordered/unordered list | ``/`` with concise item leads |
+| People Also Ask | Add an FAQ section answering the literal PAA questions | `FAQPage` JSON-LD + visible Q/A |
+| Product/Review rich result | Mark up products with ratings you actually have | `Product` + `AggregateRating` (real data only) |
+| AI Overview / generative | Clear entity definitions, comparison facts, cited claims | Scannable facts, source links |
+
+Never fabricate ratings, review counts, or prices to win a rich result — it's a manual-action and trust risk. Mark up only data you genuinely have.
+
+---
+
+## Part 4 — Prioritization Rubric (0-100)
+
+Score each candidate page before building. Sum the weighted dimensions:
+
+| Dimension | Weight | 0 | 50 | 100 |
+|-----------|:------:|---|----|-----|
+| Demand (search volume / trend) | 25 | negligible | steady mid | high & growing |
+| Win probability (gap size vs. our authority) | 25 | SERP fully saturated | beatable on depth | clear wedge, weak incumbents |
+| Commercial intent (proximity to revenue) | 25 | informational only | mid-funnel | high buyer intent (vs/pricing) |
+| Effort inverse (lower effort = higher score) | 15 | needs deep original research | moderate | data we already have |
+| Strategic fit (cluster/hub support) | 10 | orphan | adjacent | reinforces a money cluster |
+
+**Decision bands:** ≥ 70 build now · 50-69 backlog with a date · < 50 skip or revisit when authority grows.
+
+### Falsifiability — how would we know this failed?
+Define the failure condition *before* publishing, so the page is accountable:
+- **Hypothesis:** "This vs page will rank top-5 for `[A vs B]` and convert at ≥ X%."
+- **Leading indicator (2-4 weeks):** impressions in Search Console for the target query rising; average position breaking into top 20. If impressions are flat after 4 weeks of being indexed, the page is mis-targeted (wrong intent/format) — diagnose before adding more pages like it.
+- **Lagging indicator (8-12 weeks):** top-5 position + assisted conversions. No movement here despite healthy leading indicators ⇒ on-page relevance/depth gap or CTR problem (fix title/snippet), not a "wait longer" problem.
+- **Kill criterion:** no top-20 position and < N impressions after 12 weeks ⇒ consolidate into a stronger page or unpublish; don't let thin comparison pages dilute the cluster.
+
+---
+
+## Page Types
+
+### 1. "X vs Y" Comparison
+Direct head-to-head, balanced feature-by-feature analysis, clear justified verdict. Target: `[A] vs [B]`.
+
+### 2. "Alternatives to X"
+List of alternatives, each with summary, pros/cons, best-for use case. Target: `[Product] alternatives`, `best alternatives to [Product]`.
+
+### 3. "Best [Category] Tools" Roundup
+Curated ranking with stated criteria. Target: `best [category] tools [year]`, `top [category] software`.
+
+### 4. Comparison Table / Matrix
+Multiple products in columns, sortable/filterable if interactive. Target: `[category] comparison`, `[category] comparison chart`.
+
+---
+
+## Keyword Targeting
+
+| Pattern | Example | Intent strength |
+|---------|---------|-----------------|
+| `[A] vs [B]` | "Slack vs Teams" | high, mid-funnel |
+| `[A] alternatives` | "Figma alternatives" | high, commercial |
+| `[A] alternatives [year]` | "Notion alternatives 2026" | high, recency |
+| `best [category] tools` | "best project management tools" | high, top-funnel |
+| `[A] vs [B] for [use case]` | "AWS vs Azure for startups" | medium, qualified |
+| `[A] vs [B] pricing` | "HubSpot vs Salesforce pricing" | medium, high buyer intent |
+| `is [A] better than [B]` | "is Notion better than Confluence" | medium, snippet-prone |
+
+### Title / H1 formulas
+- vs: `[A] vs [B]: [Key Differentiator] ([Year])`
+- alternatives: `[N] Best [A] Alternatives in [Year] (Free & Paid)`
+- roundup: `[N] Best [Category] Tools in [Year], Compared & Ranked`
+- H1: match title intent, primary keyword natural, < 70 chars.
+
+---
+
+## Build — Next.js 15 (App Router + RSC)
+
+### Comparison page route + metadata
+```tsx
+// app/[locale]/compare/[slug]/page.tsx
+import type { Metadata } from "next";
+import { getTranslations } from "next-intl/server";
+import { getComparison } from "@/lib/payload/comparisons"; // your Payload fetch
+import { ComparisonTable } from "@/components/comparison-table";
+import { JsonLd } from "@/components/json-ld";
+
+export async function generateMetadata(
+ { params }: { params: Promise<{ locale: string; slug: string }> },
+): Promise {
+ const { slug, locale } = await params;
+ const c = await getComparison(slug, locale);
+ if (!c) return {};
+ const title = `${c.productA} vs ${c.productB}: ${c.differentiator} (${new Date().getFullYear()})`;
+ return {
+ title,
+ description: c.metaDescription,
+ alternates: { canonical: `/compare/${slug}` },
+ openGraph: { title, description: c.metaDescription, type: "article" },
+ };
+}
+
+export default async function ComparePage(
+ { params }: { params: Promise<{ locale: string; slug: string }> },
+) {
+ const { slug, locale } = await params;
+ const c = await getComparison(slug, locale); // RSC: fetched on the server
+ if (!c) return null;
+ const t = await getTranslations("compare");
+
+ return (
+
+
+ {c.productA} vs {c.productB}
+
+ {/* 40-55 word self-contained verdict → targets paragraph featured snippet */}
+ {c.verdict}
+
+
+
+
+
+ {t("updated", { date: new Date(c.updatedAt).toLocaleDateString(locale) })}
+
+
+ );
+}
+```
+
+### Accessible, snippet-friendly table component
+```tsx
+// components/comparison-table.tsx
+type Cell = "yes" | "no" | "partial" | string;
+export function ComparisonTable(
+ { rows, columns }: { rows: { feature: string; values: Cell[] }[]; columns: string[] },
+) {
+ const mark = (v: Cell) =>
+ v === "yes" ? ✓
+ : v === "no" ? —
+ : v === "partial" ? ◐
+ : v;
+ return (
+
+ Feature comparison
+
+
+ Feature
+ {columns.map((c) => (
+ {c}
+ ))}
+
+
+
+ {rows.map((r) => (
+
+ {r.feature}
+ {r.values.map((v, i) => {mark(v)} )}
+
+ ))}
+
+
+ );
+}
+```
+
+### Data accuracy (enforce in content, not just markup)
+- Every feature/pricing claim verifiable from a public source; store the source URL alongside the value in Payload.
+- Pricing carries an "as of [date]" note; review quarterly or when a competitor ships a major change.
+- Prefer Payload-managed comparison data over hardcoded JSX so non-devs can keep it current and `revalidateTag` refreshes the page.
+
+---
+
+## Schema Markup (JSON-LD)
+
+Render via a small `` server component (`` inside any string field (e.g.
+a description) from breaking out of the tag — the one real XSS/parsing footgun
+with inline JSON-LD.
+
+### Reusable `` component
+
+```tsx
+// components/seo/json-ld.tsx — Server Component (no "use client")
+import type { Thing, WithContext } from "schema-dts";
+
+export function JsonLd({ data }: { data: WithContext }) {
+ return (
+
+ );
+}
+```
+
+```tsx
+import { JsonLd } from "@/components/seo/json-ld";
+// ...
+
+```
+
+### Multiple types — prefer one `@graph`
+
+Don't scatter many `