# [Site name] rebuild: build plan

Template based on the plan used to rebuild benschmark.eu (WordPress to Astro, Keystatic and Netlify).
Replace everything in [brackets]. Easiest way: give this file to your AI assistant at the end of your design chat
and ask it to fill it in for your site and your decisions.

Rebuild of [https://www.example.com/] (currently [WordPress + theme/page builder]) as a fast, reading-first blog.

**How to use this file.** Put it in the root of an empty Git repository together with the `design-reference/` folder. Run one phase at a time with your coding agent. Every phase ends with a gate and exactly one commit. After each gate: stop, report what was verified and how, and wait for approval before the next phase.

---

## 1. Fixed decisions

| Area | Decision |
|---|---|
| Framework | Astro, latest stable. Public pages are prerendered static HTML. |
| Content | Files in this repo (Markdoc), one folder per post with its images. |
| CMS | Keystatic. Local mode in development, GitHub mode in production, admin at `/keystatic`. |
| Hosting | Netlify, with the Astro Netlify adapter (Keystatic's admin and API routes need server code). |
| Language | [English] site. English code identifiers. |
| Design | "[Design name]", light and dark. Source of truth: `design-reference/tokens.css` and the reference pages. |
| URLs | Posts at `/blog/<slug>/`, topics at `/topics/<slug>/`. Every old URL gets a 301. |
| Topics | One topic per post: [topic A, topic B, topic C]. Free-form `tags` on top. |
| Comments | [None / provider]. |
| Search | Pagefind (static index, no server). |
| Fonts | [Font A] and [Font B], self-hosted (no requests to Google Fonts). |
| Measurement | [GA4 only, after consent / none]. |

Before installing anything, read the current official docs for Astro, Keystatic's Astro integration and the Netlify adapter. Do not rely on remembered versions or config shapes.

## 2. Open decisions (ask [your name], do not guess)

1. [Analytics measurement ID, as an environment variable, never in the repo.]
2. [Homepage portrait and credibility facts. Until supplied, keep a placeholder.]
3. [Final homepage headline.]

## 3. Rules for every phase

- Never change DNS, never touch the live WordPress site, never push to the production branch without an approved gate.
- No secrets in the repo. Use `.env` locally and Netlify environment variables in production; commit only `.env.example`.
- Never print secret values in output or logs. If one shows up anywhere, say so and recommend rotating it.
- Test content is prefixed `TEST_` in the title and `test_` in the slug (Netlify serves lowercase paths) so it can be found and removed. The launch gate fails if any test content exists.
- Verify in the real state: run the build, open the preview, check the output. A passing type check is not a verified gate.
- One branch and one pull request per phase. One commit per phase on the production branch (squash merge), message format `phase-N: <summary>`.

## 4. Design reference

`design-reference/` contains:

- `tokens.css`: all colours for both themes, fonts, sizes, radii, spacing.
- [The reference pages exported from the design chat, e.g. home, articles list and article, each in light and dark.]

Use the mock-ups for structure, order, spacing, sizes and copy. Implement with `tokens.css` plus scoped component CSS.

[Changes agreed after the design round, e.g. text column width, code size, how the contents rail behaves on small screens.]

Theme behaviour: follow `prefers-color-scheme` on first visit, switch with the header button, remember the choice in `localStorage`, and set `data-theme` on `<html>` in an inline head script so there is no flash of the wrong theme.

## 5. Content model

Post frontmatter:

| Field | Type | Notes |
|---|---|---|
| `title` | string | Required. |
| `description` | string | Required, 70 to 160 characters. Used for meta description and listings. |
| `publishedAt` | date | Required. |
| `updatedAt` | date | Optional. Shown and used for `lastmod` when present. |
| `topic` | enum | [your topics]. |
| `tags` | string[] | Optional. |
| `takeaways` | string[] | Optional. Rendered as the "Key takeaways" block above the body. |
| `faq` | `{question, answer}[]` | Optional. Rendered as the FAQ section and as FAQPage structured data. |
| `shareImage` | image | Optional. Falls back to the generated image. |
| `draft` | boolean | Drafts never build in production. |

Reading time is computed from the body, not stored.

## 6. Phases

### Phase 0: Inventory

- Ask for the WordPress export (WXR file) and the uploads folder, and place them in `migration/input/` (git-ignored).
- Build `migration/url-map.csv` with every public URL of the old site and its new target.
- Record the current PageSpeed Insights scores for the homepage and one article in `migration/baseline.md`.

**Gate:** every old URL has exactly one target, and the number of posts in the map equals the number of published posts in the export.

### Phase 1: Skeleton and design system

- Create the Astro project with the Netlify adapter, TypeScript strict.
- Add `tokens.css`, self-hosted fonts, base typography, the theme script and toggle.
- Build the shared layout: header with wordmark and navigation, footer.
- Deploy a Netlify preview from a non-production branch.

**Gate:** the preview shows an empty page with header and footer in both themes, the toggle works and persists, no request goes to a third-party host.

### Phase 2: Templates

Build with `TEST_` posts, matching the reference pages: article page, home, articles list with topic filter and search, topic page, About, Contact, Privacy, 404.

**Gate:** each template compared side by side with its reference page at 1280px and at 390px, in both themes. Keyboard navigation reaches every control. Lighthouse accessibility is 100 on the article page.

### Phase 3: CMS

- Add Keystatic with the post schema from section 5 and a `site` singleton for homepage content.
- Local mode in dev. GitHub mode in production, restricted to the owner's GitHub account.

**Gate:** the owner creates, edits and publishes a `TEST_` post from `/keystatic` on the preview without touching code, including one image, one code block and one table.

### Phase 4: Content migration

- Convert every post from the export to the content model. Images are copied locally and optimised; code blocks get a language; tables stay tables.
- Fill `description`, `topic`, `tags`, and move existing "Key takeaways" and "FAQ" sections into their fields.
- Do not rewrite the author's text. Report anything that looks broken instead of fixing it silently.

**Gate:** every migrated post checked against its original; a checklist in `migration/review.md` with one line per post (text complete, images present, code intact, links working).

### Phase 5: SEO layer

- Per page: title template `<title> | [Site name]`, meta description, canonical, Open Graph and Twitter tags, a generated share image per post.
- Structured data: BlogPosting and BreadcrumbList on articles, FAQPage where `faq` exists, Person on About.
- `sitemap.xml`, `rss.xml`, `robots.txt`.
- Redirects generated from `migration/url-map.csv` into Netlify's redirect format.

**Gate:** a script requests every old URL against the preview and gets a 301 to a page that returns 200. Zero 404s. The Rich Results test passes for one article. Lighthouse SEO is 100.

### Phase 6: Consent and measurement

- Consent banner with Google Consent Mode v2: everything denied by default, analytics storage only after opt-in, the choice can be changed from the footer.
- Analytics loads only after consent.

**Gate:** with consent refused, the network log shows no request to Google. With consent given, the page view appears in GA4 DebugView.

### Phase 7: Pre-launch check

- Remove all `TEST_` content.
- Lighthouse on mobile for home and one long article: performance 95 or higher, no layout shift.
- Check both themes, 390px and 1280px, on every template once more.
- Privacy and cookie pages match what the site actually loads.

**Gate:** all checks pass and are listed with their results in `migration/prelaunch.md`.

### Phase 8: Launch (the owner does these steps, the agent assists)

1. Merge to the production branch and confirm the production deploy.
2. Point the domain's DNS to Netlify. Keep the WordPress hosting running, unpointed, for two weeks as a fallback.
3. Submit the new sitemap in Google Search Console.
4. Re-run the redirect script against the live domain.
5. Watch Search Console coverage and performance weekly for four weeks and compare with `migration/baseline.md`.

## 7. Known URL map (to be completed in Phase 0)

| Old path | New path |
|---|---|
| `/[year]/[month]/[day]/[slug]/` | `/blog/[slug]/` |
| `/category/[name]/` | `/topics/[name]/` |
| `/feed/` | `/rss.xml` |
