diff --git a/.cursor/rules/architecture.mdc b/.cursor/rules/architecture.mdc
new file mode 100644
index 00000000..036c29a8
--- /dev/null
+++ b/.cursor/rules/architecture.mdc
@@ -0,0 +1,112 @@
+---
+description: Astro architecture, component patterns, island strategy, styling approach
+globs: src/**,astro.config.mjs
+alwaysApply: false
+---
+
+# Architecture
+
+## Stack
+
+| Layer | Technology | Version | Notes |
+|-------|-----------|---------|-------|
+| Framework | Astro | 5.x | Content collections via glob loader |
+| Islands | Preact | 10.x | Via `@astrojs/preact`. Only FilterSidebar and CodeExample |
+| Shared State | Nano Stores | latest | `@nanostores/preact` for islands, vanilla subscriber for static HTML |
+| Styling | Tailwind CSS | 4.x | Via `@tailwindcss/vite` plugin (not `@astrojs/tailwind`) |
+| Fonts | Fontsource | latest | Self-hosted Fraunces, Plus Jakarta Sans, JetBrains Mono |
+| Syntax Highlighting | Shiki | built-in | Dual-theme: github-light / github-dark |
+| View Transitions | Astro `` | — | Crossfade default |
+| Testing | Vitest + Playwright | latest | Unit, integration, E2E (6-browser matrix on main) |
+
+## Content Collections
+
+Defined in `src/content.config.ts` using Astro 5 glob loader:
+
+```ts
+defineCollection({
+ loader: glob({ pattern: '**/*.md', base: './content/smells' }),
+ schema: smellSchema,
+})
+```
+
+`base` resolves relative to the project root (where `astro.config.mjs` lives), NOT relative to `src/content.config.ts`. Content files stay at `content/smells/` — no move, no symlinks.
+
+The `---` null placeholder in YAML arrays is handled via Zod `.transform(arr => arr.filter(v => v !== '---'))` in `src/schemas/smell.ts`.
+
+## Routing
+
+| Route | File | Description |
+|-------|------|-------------|
+| `/` | `src/pages/index.astro` | Catalog with hero, filter sidebar, card grid |
+| `/smells/[slug]` | `src/pages/smells/[slug].astro` | Individual smell article (56 pages) |
+| `/about` | `src/pages/about.astro` | About page |
+| `/rss.xml` | `src/pages/rss.xml.ts` | RSS feed |
+| Custom 404 | `src/pages/404.astro` | 404 with fuzzy matching and diagnoses |
+
+Uses `[slug].astro` (not `[...slug].astro`) because all 56 slugs are flat — rest params would let `/smells/foo/bar` match instead of 404ing.
+
+## Internal Links
+
+Remark plugin `src/plugins/remark-smell-links.ts` rewrites `[Name](./slug.md)` to `Name` at build time. Keeps content files portable.
+
+## Remark Plugin Pipeline
+
+Configured in `astro.config.mjs`. Ordering is load-bearing:
+
+1. `remark-smell-links` — rewrite `./slug.md` links while headings are still original
+2. `remark-overview-heading` — normalize first H2 to "Overview"
+3. `remark-strip-sections` — strip sections rendered by Astro components (Problems, Example, Refactoring, Sources)
+4. `remark-callout-sections` — wrap remaining callout sections
+
+## Islands Architecture
+
+Most pages ship **zero framework JavaScript**. Preact is used only for two interactive components:
+
+| Component | Hydration | Location |
+|-----------|-----------|----------|
+| FilterSidebar | `client:load` | Catalog — includes search input, dimension filters, active pills, mobile bottom sheet |
+| CodeExample | `client:visible` | Article pages — smelly/solution code toggle with copy button |
+
+**Everything else uses inline `
+
+
+ Redirecting to ${fullUrl}...
+
+