diff --git a/docs/infra.md b/docs/infra.md
new file mode 100644
index 0000000..97be9a7
--- /dev/null
+++ b/docs/infra.md
@@ -0,0 +1,19 @@
+# Infrastructure notes (state that lives outside this repo)
+
+## Domains (Vercel dashboard → Settings → Domains)
+
+- Production domains: `hackimi.dev` (apex) and `www.hackimi.dev`.
+- **Canonical host: apex `https://hackimi.dev`** — every canonical tag, sitemap URL, robots Sitemap line, and JSON-LD url in this repo uses apex.
+- Redirect direction: `www` → 308 → apex. (Flipped 2026-08-\_\_ by Nima; before that the dashboard had apex → www, contradicting the code's canonicals.)
+- `hackimi.vercel.app` additionally 308s to apex via `next.config.js` (exact-host rule — preview URLs on \*.vercel.app are deliberately not matched).
+
+## Deployments
+
+- Production deploys are automatic but CI-gated (commit `1c92564`, 2026-06-02): GitHub Actions runs lint/types/Vitest/Playwright on every push, and a deploy job (`vercel build` + `vercel deploy --prebuilt --prod`) runs only on `main` after the tests pass.
+- `vercel.json` sets `git.deploymentEnabled.main: false` to disable Vercel's OWN git auto-deploy for `main` only — so production flows exclusively through the tested CI gate while PR/branch previews keep deploying via Vercel as normal. Merging to `main` therefore DOES deploy, via CI.
+
+## Search Console
+
+- Property: `https://hackimi.dev` (URL-prefix), meta-tag verification.
+- Token lives in `SITE_GOOGLE_SITE_VERIFICATION` (`src/lib/site.ts`); empty string disables the tag entirely.
+- After each SEO-relevant deploy: submit `sitemap.xml`, request indexing of changed URLs.
diff --git a/docs/superpowers/plans/2026-08-09-seo-name-query.md b/docs/superpowers/plans/2026-08-09-seo-name-query.md
new file mode 100644
index 0000000..6c61371
--- /dev/null
+++ b/docs/superpowers/plans/2026-08-09-seo-name-query.md
@@ -0,0 +1,1005 @@
+# SEO Name-Query Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Make hackimi.dev win the "Nima Hakimi" name query: recover the dead `/projects` ranking URL, name-led titles, entity-graph JSON-LD, favicon + branded OG card, honest sitemap, Search Console wiring.
+
+**Architecture:** All changes ride the existing metadata pipeline (`src/lib/site.ts` constants → `src/lib/metadata.ts` builders → per-page `metadata` exports). New surfaces: `next.config.js` redirects, a `JsonLd` component for structured-data blocks, a site footer, Next file-convention icons, and a committed asset-generator script.
+
+**Tech Stack:** Next.js 16 App Router (Turbopack), React 19, Vitest, Playwright, Prettier 3.8.3.
+
+**Spec:** `docs/superpowers/specs/2026-08-09-seo-name-query-design.md`
+
+## Global Constraints
+
+- Positioning wording in SEO surfaces: exactly `Developer & AI Engineer` (prose form: `developer and AI engineer`). **Visible site copy keeps "frontend"** — do NOT touch hero/about/contact/case-study visible text (`src/app/page.tsx` JSX, `about` prose, `ContactClient` blurb, `src/data/*` narratives).
+- Canonical host is apex `https://hackimi.dev` everywhere in code. Never introduce `www.`
+- TDD: every task starts with a failing test. Run tests with `npm run test` (Vitest) / `npm run test:e2e` (Playwright).
+- Playwright locally requires **unsandboxed Bash** and `ulimit -n 10240` (Chromium fails to launch sandboxed: "Permission denied (1100)"). Same for the asset-generator script (it launches Chromium).
+- Turbopack dev server's first cold run may fail 1 random e2e while compiling on demand — re-run before blaming a change; production build is stable.
+- Commits: conventional messages, no AI attribution, no session links, no Claude-Session trailer.
+- `npm run lint` must stay 0 errors / 0 warnings; `npm run check` (Prettier) must pass before each commit.
+
+---
+
+### Task 1: Permanent redirects — recover the dead /projects URL
+
+**Files:**
+
+- Modify: `next.config.js` (currently an empty `nextConfig = {}`)
+- Create: `e2e/seo.spec.ts`
+
+**Interfaces:**
+
+- Produces: `e2e/seo.spec.ts` — later tasks append describe-blocks to this file.
+
+- [ ] **Step 1: Write the failing e2e test**
+
+Create `e2e/seo.spec.ts`:
+
+```ts
+import { test, expect } from '@playwright/test';
+
+test.describe('redirects', () => {
+ test('/projects permanently redirects to /work', async ({ request }) => {
+ const res = await request.get('/projects', { maxRedirects: 0 });
+ expect(res.status()).toBe(308);
+ expect(res.headers()['location']).toBe('/work');
+ });
+
+ test('/projects/anything redirects to /work', async ({ request }) => {
+ const res = await request.get('/projects/old-page', { maxRedirects: 0 });
+ expect(res.status()).toBe(308);
+ expect(res.headers()['location']).toBe('/work');
+ });
+});
+```
+
+- [ ] **Step 2: Run to verify it fails**
+
+Run (unsandboxed, `ulimit -n 10240` first): `npx playwright test e2e/seo.spec.ts` Expected: FAIL — `/projects` returns 404, not 308.
+
+- [ ] **Step 3: Implement redirects**
+
+Replace `next.config.js` content:
+
+```js
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ async redirects() {
+ return [
+ // The pre-2026-06-02 site served /projects (Google's current #1 result
+ // for "Nima Hakimi developer"); the redesign renamed it to /work.
+ { source: '/projects', destination: '/work', permanent: true },
+ { source: '/projects/:path*', destination: '/work', permanent: true },
+ // Exact-host only: preview deployments on *.vercel.app must stay untouched.
+ {
+ source: '/:path*',
+ has: [{ type: 'host', value: 'hackimi.vercel.app' }],
+ destination: 'https://hackimi.dev/:path*',
+ permanent: true,
+ },
+ ];
+ },
+};
+
+module.exports = nextConfig;
+```
+
+- [ ] **Step 4: Run to verify it passes**
+
+Run: `npx playwright test e2e/seo.spec.ts` — Expected: PASS. If it fails unexpectedly after the config edit: Turbopack's persistent FS cache is known to go stale across `next.config.js` changes in this repo — `rm -rf .next`, restart the server, re-run BEFORE debugging the redirect rule itself. Also verify the host rule manually (dev server on :3000): `curl -s -o /dev/null -w '%{http_code} %{redirect_url}' -H 'Host: hackimi.vercel.app' http://localhost:3000/about` Expected: `308 https://hackimi.dev/about`. If the dev server doesn't honor the Host match, verify against `npm run build && npm run start` instead; if it only works in prod mode, note that in the task report — the rule itself is correct.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add next.config.js e2e/seo.spec.ts
+git commit -m "feat: permanent redirects for legacy /projects and vercel.app host"
+```
+
+---
+
+### Task 2: Positioning constants — Developer & AI Engineer
+
+**Files:**
+
+- Modify: `src/lib/site.ts`
+- Modify: `tests/unit/site.test.ts`
+
+**Interfaces:**
+
+- Produces: `SITE_ROLE = 'Developer & AI Engineer'`, `SITE_TITLE = 'Nima Hakimi — Developer & AI Engineer'` (em-dash), updated `SITE_DESCRIPTION`/`SITE_KEYWORDS`, and `SITE_SOCIAL_LINKS.orcid`. Tasks 3–7 consume these.
+
+- [ ] **Step 1: Update the pinned test to the new contract**
+
+In `tests/unit/site.test.ts` replace the SITE_TITLE test:
+
+```ts
+it('SITE_TITLE is `${SITE_AUTHOR} — ${SITE_ROLE}` (em-dash form)', () => {
+ expect(SITE_TITLE).toBe(`${SITE_AUTHOR} — ${SITE_ROLE}`);
+ expect(SITE_ROLE).toBe('Developer & AI Engineer');
+});
+```
+
+And add to the same describe:
+
+```ts
+it('social links include GitHub, LinkedIn and ORCID', () => {
+ expect(Object.keys(SITE_SOCIAL_LINKS).sort()).toEqual(['github', 'linkedin', 'orcid']);
+});
+
+it('keywords carry the new positioning, not "Frontend"', () => {
+ expect(SITE_KEYWORDS).toContain('AI Engineer');
+ expect(SITE_KEYWORDS.join(' ')).not.toMatch(/frontend/i);
+});
+```
+
+- [ ] **Step 2: Run to verify failure**
+
+Run: `npx vitest run tests/unit/site.test.ts` Expected: FAIL (pipe title, no orcid key, 'Frontend Developer' keyword).
+
+- [ ] **Step 3: Implement in `src/lib/site.ts`**
+
+```ts
+export const SITE_ROLE = 'Developer & AI Engineer';
+export const SITE_TITLE = `${SITE_AUTHOR} — ${SITE_ROLE}`;
+export const SITE_DESCRIPTION =
+ 'Portfolio of Nima Hakimi, a developer and AI engineer building accessible, high-performance web applications.';
+export const SITE_KEYWORDS = ['Nima Hakimi', 'Developer', 'AI Engineer', 'Next.js', 'React', 'Oslo'];
+```
+
+And extend the social links object:
+
+```ts
+export const SITE_SOCIAL_LINKS = {
+ github: 'https://github.com/nimkimi',
+ linkedin: 'https://linkedin.com/in/nima-hakimi-387716175',
+ orcid: 'https://orcid.org/0009-0002-6656-2498',
+};
+```
+
+Leave `SITE_OG_IMAGE` untouched in this task (Task 7 changes it).
+
+- [ ] **Step 4: Run the full unit/server suite**
+
+Run: `npm run test` Expected: PASS — `tests/unit/metadata.test.ts` compares against the imported constants, so it rides through. If `buildPersonJsonLd`'s sameAs test fails (it pins `[github, linkedin]`), do NOT fix it here — that contract changes in Task 6; temporarily it still passes because the builder maps the two keys explicitly.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/lib/site.ts tests/unit/site.test.ts
+git commit -m "feat: reposition site constants to Developer & AI Engineer"
+```
+
+---
+
+### Task 3: Name-led homepage title
+
+**Files:**
+
+- Modify: `src/lib/metadata.ts` (add `buildHomeMetadata`)
+- Modify: `src/app/page.tsx:10-14` (metadata export only — no JSX changes)
+- Modify: `tests/unit/metadata.test.ts`, `tests/integration/pages.test.tsx:58-62`
+- Modify: `e2e/seo.spec.ts` (append)
+
+**Interfaces:**
+
+- Consumes: `SITE_TITLE`, `SITE_DESCRIPTION` from Task 2.
+- Produces: `buildHomeMetadata(): Metadata` in `src/lib/metadata.ts`.
+
+Background for the implementer: Next's `title.template` applies only to **child** segments — the root `page.tsx` never receives it, so its current `title: 'Portfolio'` renders as literally `
Portfolio` in production. That is why the fix must set an absolute full-string title and why the e2e assertion (rendered layer) is the one that counts: the unit-level metadata object looked correct the whole time this bug shipped.
+
+- [ ] **Step 1: Write the failing tests**
+
+Append to `tests/unit/metadata.test.ts`:
+
+```ts
+describe('buildHomeMetadata', () => {
+ it('title is the absolute SITE_TITLE (root segment gets no template)', () => {
+ const meta = buildHomeMetadata();
+ expect(meta.title).toBe(SITE_TITLE);
+ expect(meta.openGraph?.title).toBe(SITE_TITLE);
+ expect(meta.twitter?.title).toBe(SITE_TITLE);
+ });
+
+ it('canonical and og.url are the bare SITE_URL', () => {
+ const meta = buildHomeMetadata();
+ expect(meta.alternates?.canonical).toBe(SITE_URL);
+ expect(meta.openGraph?.url).toBe(SITE_URL);
+ });
+});
+```
+
+(Import `buildHomeMetadata` in the existing import list.)
+
+In `tests/integration/pages.test.tsx`, replace the home metadata test (lines 58–62):
+
+```ts
+it('exports absolute name-led home metadata', async () => {
+ const { metadata } = await import('@/app/page');
+ const { SITE_TITLE } = await import('@/lib/site');
+ expect(metadata.title).toBe(SITE_TITLE);
+ expect(metadata.description).toContain('Nima Hakimi');
+});
+```
+
+Append to `e2e/seo.spec.ts`:
+
+```ts
+test.describe('rendered metadata', () => {
+ test('homepage title is the full name-led title', async ({ page }) => {
+ await page.goto('/');
+ await expect(page).toHaveTitle('Nima Hakimi — Developer & AI Engineer');
+ });
+
+ test('homepage canonical points at apex', async ({ page }) => {
+ await page.goto('/');
+ await expect(page.locator('link[rel="canonical"]')).toHaveAttribute('href', 'https://hackimi.dev');
+ });
+});
+```
+
+- [ ] **Step 2: Run to verify failures**
+
+Run: `npx vitest run tests/unit/metadata.test.ts tests/integration/pages.test.tsx` Expected: FAIL — `buildHomeMetadata` doesn't exist; home title is 'Portfolio'.
+
+- [ ] **Step 3: Implement**
+
+In `src/lib/metadata.ts`, after `buildPageMetadata`, add (reusing the module's existing `defaultImage`, `defaultTwitterImage`, `defaultKeywords`):
+
+```ts
+/**
+ * Home is the ONE page whose title must be absolute: Next's title.template
+ * only applies to child segments, so the root page.tsx never receives the
+ * "%s | Nima Hakimi" suffix — it must carry the full name-led string itself.
+ */
+export function buildHomeMetadata(): Metadata {
+ return {
+ title: SITE_TITLE,
+ description: SITE_DESCRIPTION,
+ keywords: defaultKeywords,
+ alternates: { canonical: SITE_URL },
+ openGraph: {
+ title: SITE_TITLE,
+ description: SITE_DESCRIPTION,
+ url: SITE_URL,
+ siteName: SITE_AUTHOR,
+ type: 'website',
+ images: [defaultImage],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: SITE_TITLE,
+ description: SITE_DESCRIPTION,
+ images: [defaultTwitterImage],
+ },
+ };
+}
+```
+
+(`SITE_TITLE` and `SITE_DESCRIPTION` are already imported at the top of the module.)
+
+In `src/app/page.tsx`, replace the metadata export (lines 10–14) with:
+
+```ts
+export const metadata = buildHomeMetadata();
+```
+
+and change the import from `buildPageMetadata` to `buildHomeMetadata`. Touch nothing else in the file.
+
+- [ ] **Step 4: Run to verify passes**
+
+Run: `npm run test` then `npx playwright test e2e/seo.spec.ts` (unsandboxed, ulimit set). Expected: all PASS.
+
+Contingency: if the e2e title assertion fails with a DOUBLED suffix (`Nima Hakimi — Developer & AI Engineer | Nima Hakimi`), the root template did apply after all — switch `buildHomeMetadata`'s title to `title: { absolute: SITE_TITLE }` and update the two object-level title assertions to match. Live-production evidence says this won't happen; the e2e exists to catch exactly this case.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/lib/metadata.ts src/app/page.tsx tests/unit/metadata.test.ts tests/integration/pages.test.tsx e2e/seo.spec.ts
+git commit -m "feat: absolute name-led homepage title via buildHomeMetadata"
+```
+
+---
+
+### Task 4: Site footer with visible name line
+
+**Files:**
+
+- Create: `src/components/layout/SiteFooter.tsx`
+- Modify: `src/app/layout.tsx:30-33` (add footer inside `SmoothScroll`, after `main`)
+- Create: `tests/components/site-footer.test.tsx`
+- Modify: `e2e/seo.spec.ts` (append)
+
+**Interfaces:**
+
+- Consumes: `SITE_AUTHOR`, `SITE_ROLE`, `SITE_LOCATION` from `src/lib/site.ts`.
+- Produces: `SiteFooter` default-export server component, rendered on every page.
+
+- [ ] **Step 0: Pre-flight — check for existing `