diff --git a/.claude/skills/seo-audit/SKILL.md b/.claude/skills/seo-audit/SKILL.md
new file mode 100644
index 0000000..493fd14
--- /dev/null
+++ b/.claude/skills/seo-audit/SKILL.md
@@ -0,0 +1,119 @@
+---
+name: seo-audit
+description: Audit and improve on-page SEO for web pages, docs sites, and marketing pages. Use whenever the user wants an SEO review or audit, wants to check or fix title tags / meta descriptions / Open Graph / Twitter cards / canonical URLs / structured data (JSON-LD) / robots / sitemap / heading structure / image alt text, asks "why isn't this page ranking / showing a preview card", wants to optimize a page for search or social sharing, or mentions SEO, meta tags, OG tags, rich results, or search snippets — even if they don't say the word "audit". Works on local source files (HTML, JSX/TSX, Markdown, Astro/Next/Vite) or a live URL. Not for off-page SEO (backlinks, ad campaigns) or keyword-volume research.
+---
+
+# On-page SEO audit
+
+Review a page (or a set of pages) against on-page SEO best practices, report what's
+wrong in priority order, and — when the user wants — fix it in the source files.
+
+The point is not to produce a checklist for its own sake. Search engines and social
+platforms read a page's `
` and semantic HTML to decide how to index it and how to
+render its preview card. When those signals are missing or wrong, the page still "works"
+for humans but is invisible or ugly to crawlers. Your job is to find that gap and close it.
+
+## Decide the target first
+
+Two modes — pick based on what the user gives you:
+
+- **Source mode (default when working in a repo):** audit the files that produce the page.
+ This is what the user usually wants when the pages live in this repo, because the fix
+ lands in code they can commit. Find where `` / metadata is set — this varies by
+ stack, so look before assuming:
+ - Static HTML → the `` in the `.html` file.
+ - Next.js → `metadata` exports / `` / `generateMetadata` in `app/` or `pages/`.
+ - Astro → frontmatter + `` in layout `.astro` files.
+ - Vite/plain SPA → `index.html` plus any runtime `document.title` / meta injection.
+ - Markdown docs (MkDocs, Docusaurus, VitePress, Jekyll) → frontmatter + the theme's
+ head template. Per-page `title`/`description` usually live in frontmatter.
+ Grep for `og:`, `twitter:`, `canonical`, `application/ld+json`, ``. Note in the report that you audited rendered output, not source.
+
+If the user hasn't said which, infer from context (a repo path → source; a URL → live) and
+state which mode you chose in one line. Don't stall on the choice.
+
+## What to check
+
+Go through these. For each, report the current value (or "missing"), whether it's OK, and
+what to change. Read `references/checklist.md` for the full detail on any item — thresholds,
+common mistakes, and exactly what good looks like. The high-signal ones, roughly in order
+of how often they matter:
+
+1. **``** — present, unique per page, ~50–60 chars, front-loads the primary term.
+2. **``** — present, ~120–160 chars, compelling, not duplicated
+ across pages. Missing/duplicate descriptions are the single most common real problem.
+3. **Open Graph** — `og:title`, `og:description`, `og:image` (absolute URL, ~1200×630),
+ `og:url`, `og:type`. This is what LinkedIn/Slack/Facebook show. A missing `og:image`
+ is why a shared link looks blank.
+4. **Twitter/X card** — `twitter:card` (usually `summary_large_image`), plus title/desc/image.
+5. **Canonical** — `` with an absolute URL, to avoid duplicate-content
+ splitting between www/non-www, trailing-slash, and query-param variants.
+6. **Headings** — exactly one `