## Context

Step 001 produced a working Astro project with Tailwind v4 brand tokens, Archia font-face declarations, and a smoke-test `index.astro` that manually constructs its own `<html>` document. Every subsequent page (homepage, about, AI Launchpad, contact, privacy — plus `/nl/*` variants) will need the same HTML shell with correct `<head>` meta, hreflang, OG tags, and body defaults. Without a shared layout this is copied per page, creating drift and SEO inconsistency.

Current state of note:
- `global.css` is Tailwind v4 (`@import "tailwindcss"` + `@theme`) — no `tailwind.config.mjs`
- Archia `@font-face` declarations already live in `global.css`
- `public/` has `favicon.svg` and `favicon.ico`
- No `src/layouts/`, `src/components/`, or `src/content/` directories yet

## Goals / Non-Goals

**Goals:**
- Single `BaseLayout.astro` that every page wraps its content in
- Correct SEO `<head>`: charset, viewport, favicon, font preloads, canonical, hreflang, OG, Twitter card
- Body defaults applied once (Midnight bg, Snow text, Archia font)
- JSON-LD injection mechanism (both prop and named slot)
- Stub `Nav.astro` and `Footer.astro` so BaseLayout can import them (filled in steps 003+)
- `en.json` / `nl.json` content files + `i18n.ts` loader for global meta fields
- `index.astro` updated to use `BaseLayout`

**Non-Goals:**
- Actual nav or footer UI (step 003)
- Page-specific JSON-LD schemas (each page step handles its own)
- OG image file creation (deferred — `ogImage` prop will be empty string initially)
- Any client-side JavaScript
- `@astrojs/image` or picture optimisation (images not needed in this step)

## Decisions

### 1. hreflang: explicit props, not derived

**Decision**: `BaseLayout` accepts two explicit props — `canonicalUrl: string` and `alternateUrl: string`. The component does not compute the alternate URL.

**Rationale**: EN routes are `/about`, NL routes are `/nl/about` — a simple `/nl/` prefix works for most paths, but `/contact` vs `/nl/contact` or future edge cases (slugs, catch-alls) could diverge. Explicit props make each page's intent clear and avoid regex surprises. Callers always know their own routes.

**Alternative considered**: Auto-derive by prepending/stripping `/nl/` based on `locale` prop. Rejected: fragile for edge cases, magic at a distance.

### 2. Font preloads: critical weights only

**Decision**: Preload only Archia Regular (400), SemiBold (600), and Bold (700). Thin (100) and Light (300) load on demand.

**Rationale**: Each `<link rel="preload">` is an unconditional network request. Regular is needed for all body copy; SemiBold and Bold are used for headings and CTAs — the elements visible above the fold. Thin and Light appear only in specific decorative contexts (if at all) and are not worth the preload cost.

### 3. JSON-LD: prop for common case + named slot for extras

**Decision**: BaseLayout accepts an optional `jsonLd?: Record<string, unknown>` prop. If provided, it is serialized with `JSON.stringify` and injected as `<script type="application/ld+json" set:html={...}>`. A `<slot name="jsonld" />` is also provided for pages that need additional or multiple JSON-LD blocks.

**Rationale**: Most pages will pass one structured data object (e.g., `ProfessionalService`, `WebPage`). The prop keeps call sites clean. The named slot exists as an escape hatch for pages like the homepage that may need multiple JSON-LD entries.

### 4. Nav/Footer: imported stub components

**Decision**: `BaseLayout.astro` imports `Nav.astro` and `Footer.astro` directly as Astro components. Both are created as empty stubs (render nothing). Step 003 fills in `Nav.astro`; a later step fills `Footer.astro`.

**Alternative considered**: Named slots (`<slot name="nav" />`). Rejected: would require every page to manually import and wire up Nav, which is error-prone and defeats the purpose of a shared layout.

### 5. Body defaults: Tailwind classes on `<body>`

**Decision**: Apply `class="bg-midnight text-snow font-archia"` directly on the `<body>` element in BaseLayout. No `@layer base` rule added to `global.css`.

**Rationale**: These are layout-level defaults, not global resets. Keeping them as explicit Tailwind classes on the element makes them visible and overridable per-section without specificity surprises. Consistent with how `index.astro` already applies them.

### 6. Content JSON: flat `meta` key at top level

**Decision**: Both `en.json` and `nl.json` start with a top-level `"meta"` key containing `siteName`, `defaultTitle`, `defaultDescription`, and `defaultOgImage`. Subsequent steps add page-specific keys (`"home"`, `"about"`, etc.) at the same top level.

**Rationale**: Flat top-level keys by section keep the file scannable and allow each step to add its own section without touching others. The `"meta"` key is reserved for global/shared values.

### 7. i18n helper: synchronous import, not dynamic fetch

**Decision**: `i18n.ts` exports a function `getContent(locale: string)` that does a static import of each JSON and returns the correct one. No dynamic `fetch` or `fs.readFile`.

**Rationale**: Astro builds statically — all pages are rendered at build time. Static imports are tree-shakeable and type-safe. Dynamic fetch is unnecessary complexity.

## Risks / Trade-offs

- **OG image missing** → Social card previews will have no image until a default OG image is created. Mitigation: `ogImage` prop defaults to empty string; the `<meta property="og:image">` tag is only rendered when `ogImage` is truthy.
- **Stub Nav/Footer render nothing** → The smoke-test page will show Midnight body with no navigation. This is expected and clearly temporary. Mitigation: none needed — step 003 immediately follows.
- **hreflang on smoke-test page** → `index.astro` (the brand smoke-test) will need placeholder `canonicalUrl` and `alternateUrl` props. Use `"/"` and `"/nl"` as stand-ins. This is fine since the smoke-test page will be replaced when the real homepage is built.
