diff --git a/docs/superpowers/plans/2026-04-14-phase-1-foundation-master.md b/docs/superpowers/plans/2026-04-14-phase-1-foundation-master.md
new file mode 100644
index 00000000..1f442d30
--- /dev/null
+++ b/docs/superpowers/plans/2026-04-14-phase-1-foundation-master.md
@@ -0,0 +1,725 @@
+# Phase 1 — Foundation MASTER Plan
+
+> **This document is a plan INDEX, not an executable plan.** It lists the Phase 1 sub-plans, their dependency order, the contracts each sub-plan exports for downstream sub-plans to consume, and the shared files that cross sub-plan boundaries.
+>
+> **Do not execute this document directly.** Each sub-plan (1A through 1J) is a separate file under `docs/superpowers/plans/` with its own TDD-granular tasks. They are written on demand by re-invoking the `superpowers:writing-plans` skill with a sub-plan-specific prompt.
+
+**Goal of Phase 1:** Build the complete Modern.js + Module Federation 2.0 foundation on which all four feature migrations (Phases 2–5) will be implemented. Nothing in Phase 1 ships to production users — the output is a working dual-build artifact deployed to the `testing` environment with all observability, security, and CI pipelines live.
+
+**Phase 1 exit gate** (must pass before Phase 2 starts):
+
+- Both build targets (standalone SSR + MF 2.0 remote) produce valid artifacts in CI.
+- `mf-manifest.json` is served from the `testing` environment and consumable by a test host.
+- The smoke route (`/ru/smoke`) renders via SSR in `testing` with all observability pipelines (logger, metrics, analytics) emitting correctly, verified by inspecting the log / metrics / analytics capture endpoints.
+- All Phase 1 CI gates pass on `main`: lint, typecheck, unit (70%+ coverage on `src/features/` + `src/shared/` + `src/ui/` + `src/observability/`), bundle size, security scan, URL parity harness (infra only, no real URLs yet), SEO parity harness (infra only), VRT harness wired to Phase 0 baselines.
+- Security hardening live: CSP with per-request nonce, HTTP security headers, dependency scanning green.
+- Canary deploy pipeline functional (smoke route deployed via canary path with auto-rollback on health-check failure).
+
+**Reference spec:** `docs/superpowers/specs/2026-04-14-aeroflot-flights-react-rewrite-design.md`. Phase 1 implements sections §1–§8 of the spec (everything except the feature ports in §9.2 Phase 2+).
+
+---
+
+## Sub-plan inventory
+
+| ID | Sub-plan | Spec section | Estimated size | File |
+|---|---|---|---|---|
+| **1A** | Project skeleton + dual build targets | §1.3, §2.1, §2.2, §2.5 | Large | `docs/superpowers/plans/2026-04-14-phase-1a-skeleton.md` (TBW) |
+| **1B** | CI pipeline | §8.5 | Medium | `2026-04-14-phase-1b-ci.md` (TBW) |
+| **1C** | i18n runtime + locale port | §6.1–§6.4 | Medium | `2026-04-14-phase-1c-i18n.md` (TBW) |
+| **1D** | API client + caches + circuit breaker | §4.1, §4.2 | Medium | `2026-04-14-phase-1d-api-client.md` (TBW) |
+| **1E** | SignalR wrapper + `useLiveFlights` hook | §4.4 | Medium | `2026-04-14-phase-1e-signalr.md` (TBW) |
+| **1F** | Root layout + error routes + smoke route + SeoHead | §1.3, §3.1, §3.3, §3.6, §6.5, §6.8 | Medium | `2026-04-14-phase-1f-layout.md` (TBW) |
+| **1G** | Observability (logger, OTel, analytics) | §7.1–§7.8 | Large | `2026-04-14-phase-1g-observability.md` (TBW) |
+| **1H** | Security hardening (CSP, headers, storage) | §8.1 | Small | `2026-04-14-phase-1h-security.md` (TBW) |
+| **1I** | Deploy pipeline + health + graceful shutdown | §8.3, §8.5 | Medium | `2026-04-14-phase-1i-deploy.md` (TBW) |
+| **1J** | Parity harnesses (URL / SEO / VRT) | §3.5, §8.4 | Medium | `2026-04-14-phase-1j-parity-harness.md` (TBW) |
+
+Sizes: **Small** ≈ 5–10 tasks, **Medium** ≈ 10–20 tasks, **Large** ≈ 20–40 tasks.
+
+---
+
+## Dependency graph
+
+```
+ ┌────────────────────┐
+ │ 1A Skeleton │◄── everything depends on 1A
+ └─────┬──────────────┘
+ ┌──────────────────┬┴──────┬────────────┬────────────────┐
+ ▼ ▼ ▼ ▼ ▼
+ ┌──────────┐ ┌──────────┐ ┌──────┐ ┌──────────┐ ┌──────────────┐
+ │ 1B CI │ │ 1C i18n │ │1D API│ │1E SignalR│ │1G Observabil.│
+ └─────┬────┘ └─────┬────┘ └───┬──┘ └──────────┘ └──────┬───────┘
+ │ │ │ │
+ │ └──────┬───┘ │
+ │ ▼ │
+ │ ┌──────────────────────────────┐ │
+ │ │ 1F Root layout + routes │◄─────┘
+ │ │ (consumes 1C + 1D + 1G) │
+ │ └──────┬───────────────────────┘
+ │ ▼
+ │ ┌──────────────────────────────┐
+ │ │ 1H Security hardening │
+ │ │ (middleware into 1F) │
+ │ └──────┬───────────────────────┘
+ │ │
+ ▼ ▼
+ ┌───────────────────────────────────────┐
+ │ 1I Deploy pipeline │
+ │ (consumes 1A + 1B + 1H) │
+ └───────────────────────────────────────┘
+ │
+ ▼
+ ┌───────────────────────────────────────┐
+ │ 1J Parity harnesses │
+ │ (consumes 1A + 1B + 1F) │
+ └───────────────────────────────────────┘
+```
+
+### Execution order
+
+**Serial (1 engineer):** 1A → 1B → 1C → 1D → 1G → 1E → 1F → 1H → 1I → 1J
+Rationale: 1A unlocks everything. 1B unlocks running tests in CI. 1C and 1D unlock 1F. 1G is independent but 1F imports its hooks, so 1G comes before 1F. 1E can come after 1F because it's only consumed in Phase 2. 1H modifies files 1F creates, so it follows 1F. 1I consumes 1A+1B+1H. 1J consumes 1F.
+
+**Parallel (2+ engineers):** After 1A ships, 1B / 1C / 1D / 1E / 1G can proceed in parallel. 1F and later are sequential because of shared-file constraints.
+
+### Critical path
+
+**1A → 1C → 1F → 1H → 1I → exit gate** is the critical path with one engineer. Optimizing this path (by finishing 1A fast and prioritizing 1C + 1F + 1H as a single block) is the only way to shorten the Phase 1 calendar meaningfully.
+
+---
+
+## Contracts — what each sub-plan exports
+
+This is the section that lets sub-plans be written and reviewed independently. Every sub-plan must produce its contracts without breaking changes once another sub-plan depends on them. Contracts are enforced via TypeScript types — any change to an exported type is a cross-sub-plan review gate.
+
+### 1A — Project skeleton contracts
+
+**Exports:**
+
+- **Project layout** — the `src/` directory tree from design spec §1.3. Every other sub-plan adds files inside this tree. No sub-plan is allowed to create top-level directories outside `src/` / `tests/` / `scripts/` / `docs/` without explicit call-out.
+- **`modern.config.ts`** — the Modern.js build config with `BUILD_TARGET=standalone|remote` branching. Later sub-plans (1G, 1H, 1I) modify this file via explicit "modify" tasks — 1A owns the base structure.
+- **`tsconfig.json`** — strict mode, path aliases (`@/` → `src/`, `@phase0/` → `scripts/phase-0/`), no-unchecked-indexed-access, isolatedModules.
+- **`.eslintrc.cjs`** — including the `boundaries` plugin rules enforcing the layered dependency direction from design spec §1.2 (`features/` cannot import `routes/` or `mf/`; `ui/` cannot import `features/`; etc.).
+- **`package.json` scripts** — `dev`, `build:standalone`, `build:remote`, `build:both`, `test`, `test:coverage`, `lint`, `typecheck`.
+- **`src/env/index.ts`** — runtime env-var reader returning a typed `Env` object. Other sub-plans read env vars exclusively through this module.
+- **Empty feature and UI barrel files** — `src/features/{online-board,schedule,flights-map,popular-requests}/index.ts` and `src/ui/index.ts` exist but export nothing. Phase 2+ populates them.
+
+**TypeScript contracts:**
+
+```ts
+// src/env/index.ts
+export interface Env {
+ NODE_ENV: "development" | "testing" | "staging" | "production";
+ BUILD_TARGET: "standalone" | "remote";
+ PROD_ORIGIN: string;
+ API_BASE_URL: string;
+ SIGNALR_HUB_URL: string;
+ OTEL_EXPORTER_OTLP_ENDPOINT?: string;
+ OTEL_EXPORTER_OTLP_HEADERS?: string;
+ LOGS_ENDPOINT?: string;
+ ANALYTICS_ENABLED: { metrica: boolean; ctm: boolean; variocube: boolean; dynatrace: boolean };
+ VERSION: string; // git sha, injected at build time
+}
+export function getEnv(): Env;
+```
+
+**Exit gate for 1A:** `pnpm build:both` produces `dist/standalone/` (Node server + client bundle) and `dist/remote/` (static chunks + `mf-manifest.json`) with zero type errors and zero ESLint errors. The smoke build (no features wired yet) renders a blank `
` on both targets.
+
+---
+
+### 1B — CI pipeline contracts
+
+**Exports:**
+
+- **`.github/workflows/ci.yml`** (or equivalent for the CI provider chosen in Phase 0 assumption A3). Runs on every PR and every push to `main`: install, lint, typecheck, unit tests, build both targets, bundle-size gate, security scan.
+- **`.github/workflows/nightly.yml`** — nightly-only: contract tests, load test (stub until Phase 2), Lighthouse CI.
+- **`scripts/ci/bundle-size-gate.ts`** — reads the Rspack build stats and compares against budgets in `docs/superpowers/phase-1/bundle-budgets.json`. Fails the build if any budget is exceeded.
+- **`scripts/ci/check-coverage-delta.ts`** — reads Vitest coverage JSON and the prior-commit's coverage JSON (from the base branch), fails if coverage decreases. Uses `git show :coverage-summary.json` to read the baseline; tolerates missing baseline (first run).
+
+**TypeScript contracts:** none exported (scripts are CI-internal).
+
+**Exit gate for 1B:** A PR with a trivially-broken test fails CI; a PR with a green test passes CI in under 20 minutes. Bundle-size gate flags a fabricated regression.
+
+---
+
+### 1C — i18n runtime contracts
+
+**Exports:**
+
+- **`src/i18n/config.ts`** — factory that creates a request-scoped `i18next` instance configured with `i18next-icu`, loaded with a single locale's bundle:
+
+ ```ts
+ export function createI18nInstance(options: {
+ locale: Language;
+ initialResources?: Record>;
+ }): Promise;
+ ```
+
+- **`src/i18n/resolver.ts`** — locale resolution from URL prefix:
+
+ ```ts
+ export type Language = "ru"|"en"|"es"|"fr"|"it"|"ja"|"ko"|"zh"|"de";
+ export const LANGUAGES: readonly Language[];
+ export function isLanguage(x: string): x is Language;
+ export function resolveLocaleFromPath(pathname: string): Language | null;
+ export function stripLocaleFromPath(pathname: string): { locale: Language; rest: string } | null;
+ ```
+
+- **`src/i18n/locales/{lang}/common.json`** — 9 files, ported from `ClientApp/src/assets/i18n/*.json` using the Phase 0 translation-key inventory to drop dead keys (optional; by default, all keys port). ICU MessageFormat syntax preserved byte-for-byte.
+- **`src/i18n/serializer.ts`** — helpers to serialize the loaded locale bundle into the SSR HTML payload under `window.__I18N__` and rehydrate it on the client:
+
+ ```ts
+ export function serializeI18nForHydration(i18n: i18n): string; // emits a JSON string
+ export function hydrateI18nFromWindow(): Promise; // reads window.__I18N__
+ ```
+
+- **`src/i18n/provider.tsx`** — React Context provider + `` component + `useI18n()` accessor. **Re-exports `useTranslation` from `react-i18next`** so feature code never imports `react-i18next` directly.
+
+**TypeScript contracts** (the above — downstream sub-plans import from `@/i18n`).
+
+**Exit gate for 1C:** Vitest test renders a component with `` loaded with `ru` and asserts `t("common.someKey")` returns the Russian value. SSR + hydration roundtrip test using `renderToString` verifies no client-side re-fetch or flash.
+
+---
+
+### 1D — API client contracts
+
+**Exports:**
+
+- **`src/shared/api/client.ts`** — the `ApiClient` class:
+
+ ```ts
+ export interface ApiClientOptions {
+ baseUrl: string;
+ locale: Language;
+ traceId?: string;
+ fetchImpl?: typeof fetch; // for tests
+ defaultTimeoutMs?: number; // default 5000
+ circuitBreaker?: CircuitBreakerOptions;
+ }
+
+ export class ApiClient {
+ constructor(options: ApiClientOptions);
+ get(path: string, query?: Record): Promise;
+ post(path: string, body: unknown): Promise;
+ }
+ ```
+
+- **`src/shared/api/errors.ts`** — typed error classes:
+
+ ```ts
+ export class ApiError extends Error { constructor(message: string); }
+ export class ApiHttpError extends ApiError { status: number; body?: unknown; }
+ export class ApiTimeoutError extends ApiError { timeoutMs: number; }
+ export class ApiNetworkError extends ApiError { cause?: Error; }
+ ```
+
+- **`src/shared/api/cache.ts`** — two cache implementations:
+
+ ```ts
+ export class RequestScopedCache {
+ get(key: string): Promise | undefined;
+ set(key: string, promise: Promise): void;
+ }
+
+ export class TtlCache {
+ constructor(options: { max: number; defaultTtlMs: number });
+ get(key: string): T | undefined;
+ set(key: string, value: T, ttlMs?: number): void;
+ delete(key: string): void;
+ clear(): void;
+ size: number;
+ }
+ ```
+
+- **`src/shared/api/circuit-breaker.ts`**:
+
+ ```ts
+ export interface CircuitBreakerOptions {
+ failureThreshold?: number; // default 5
+ openDurationMs?: number; // default 30_000
+ }
+
+ export class CircuitBreaker {
+ constructor(options?: CircuitBreakerOptions);
+ exec(fn: () => Promise): Promise;
+ reset(): void;
+ state: "closed" | "open" | "half-open";
+ }
+ ```
+
+- **`src/shared/api/provider.tsx`** — React Context provider + `useApiClient()` hook. SSR-aware: on the server, the client is constructed per-request with the resolved locale; on the client, a single instance is shared across the tab.
+
+**TypeScript contracts:** the above. All ported feature code (Phase 2+) imports exclusively from `@/shared/api`.
+
+**Exit gate for 1D:** Vitest tests cover: success response deserialization; retry on 5xx; no retry on 4xx; timeout; circuit breaker open/half-open/closed state transitions; request-scoped cache dedup; TTL eviction.
+
+---
+
+### 1E — SignalR wrapper contracts
+
+**Exports:**
+
+- **`src/shared/signalr/connection.ts`** — the reference-counted connection wrapper:
+
+ ```ts
+ export interface HubOptions {
+ hubUrl: string;
+ reconnectDelaysMs?: number[]; // default [0, 2000, 10000, 30000]
+ gracePeriodMs?: number; // default 5000
+ }
+
+ export class SignalRConnection {
+ constructor(options: HubOptions);
+ subscribe(channel: string, handler: (message: unknown) => void): () => void; // returns unsubscribe
+ onStatusChange(handler: (status: ConnectionStatus) => void): () => void;
+ get status(): ConnectionStatus;
+ }
+
+ export type ConnectionStatus = "idle" | "connecting" | "live" | "reconnecting" | "offline";
+
+ export function getSharedConnection(options: HubOptions): SignalRConnection;
+ ```
+
+- **`src/shared/hooks/useLiveFlights.ts`**:
+
+ ```ts
+ export function useLiveFlights(
+ params: { date: string; departure?: string; arrival?: string },
+ initialData: T[],
+ config: { hubUrl: string; channelKey: (params: object) => string },
+ ): { data: T[]; connectionStatus: ConnectionStatus };
+ ```
+
+ SSR-safe: during SSR, returns `{ data: initialData, connectionStatus: "idle" }` without importing `@microsoft/signalr`.
+
+**TypeScript contracts:** the above. Phase 2 Online Board consumes `useLiveFlights` and the `ConnectionStatus` type.
+
+**Exit gate for 1E:**
+
+- Unit test: two rapid `useEffect` mounts (Strict Mode double-invoke simulation) result in exactly one `HubConnection.start()` call.
+- Unit test: unmount + remount within the grace period reuses the connection; unmount + remount after the grace period creates a fresh one.
+- Unit test: SSR render path does not import `@microsoft/signalr` (asserted by inspecting the SSR bundle stats for the absence of the package).
+
+---
+
+### 1F — Root layout + routes + SeoHead contracts
+
+**Exports:**
+
+- **`src/routes/layout.tsx`** — root HTML shell: ``, ``, ``, ``, root ``, root ``, root ``, root `` (from 1G).
+
+- **`src/routes/[lang]/layout.tsx`** — locale-scoped layout: validates `params.lang`, creates the request-scoped i18next instance (via 1C's `createI18nInstance`), builds the canonical URL + hreflang set, passes them into ``.
+
+- **`src/routes/error/[code]/page.tsx`** — error page rendered for `code ∈ {404, 500, 503}`. Ports the existing Angular error component layout.
+
+- **`src/routes/[lang]/smoke/page.tsx`** — smoke route that exercises every foundation subsystem: emits a log at `info`, emits a metric counter, calls `track("smoke.pageview")`, renders `{t("smoke.heading")}`, fetches a dummy API endpoint via `ApiClient.get`, renders `` with canonical + hreflang. Visible in `testing` env at `/ru/smoke` and `/en/smoke`.
+
+- **`src/ui/seo/SeoHead.tsx`** — the `` component from design spec §6.5:
+
+ ```ts
+ export interface SeoHeadProps {
+ title: string;
+ description: string;
+ canonical: string;
+ hreflang: Array<{ lang: Language | "x-default"; href: string }>;
+ og: {
+ title: string; description: string; url: string;
+ image: string; type: "website" | "article";
+ locale: string; siteName: string;
+ };
+ twitter?: {
+ card: "summary" | "summary_large_image";
+ title?: string; description?: string; image?: string;
+ };
+ jsonLd?: unknown | unknown[];
+ noindex?: boolean;
+ }
+ export function SeoHead(props: SeoHeadProps): JSX.Element;
+ ```
+
+- **`src/shared/seo/hreflang.ts`** — reusable reciprocal-hreflang builder:
+
+ ```ts
+ export function buildHreflangSet(args: {
+ canonicalOrigin: string;
+ pathWithoutLocale: string; // e.g. "/onlineboard/flight/SU100-2025-01-15"
+ }): Array<{ lang: Language | "x-default"; href: string }>;
+ ```
+
+- **`src/ui/errors/ErrorBoundary.tsx`** — React error boundary component. Logs the error (via 1G's `useLogger`), emits the `flights.react.error` metric, shows a fallback UI with a "Retry" button that resets the boundary's state.
+
+**Shared file ownership flag:** `src/routes/layout.tsx` is owned by 1F. Sub-plans 1G (analytics loader mount) and 1H (CSP nonce propagation) **modify** this file via explicit tasks in their own plans, referencing the 1F-shipped version as the base.
+
+**TypeScript contracts:** the above. Phase 2+ features import `SeoHead` + `buildHreflangSet` + `ErrorBoundary`.
+
+**Exit gate for 1F:** `/ru/smoke` and `/en/smoke` render via SSR in the `testing` env. `` contains title, description, canonical, 9 hreflang alternates + x-default, OG tags, one JSON-LD block, one `` description. Missing `lang` URLs redirect 301 to `/ru/smoke`. 404 route returns HTTP 404 with the error page body.
+
+---
+
+### 1G — Observability contracts
+
+**Exports:**
+
+- **`src/observability/logger/types.ts`** — the `Logger` interface from design spec §7.2:
+
+ ```ts
+ export type LogLevel = "debug" | "info" | "warn" | "error";
+ export type LogFields = Record;
+
+ export interface Logger {
+ debug(msg: string, fields?: LogFields): void;
+ info(msg: string, fields?: LogFields): void;
+ warn(msg: string, fields?: LogFields): void;
+ error(msg: string, fields?: LogFields & { err?: Error }): void;
+ child(context: LogFields): Logger;
+ }
+
+ export interface LogTransport {
+ write(record: LogRecord): void;
+ flush(): Promise;
+ }
+
+ export interface LogRecord {
+ ts: string; level: LogLevel; msg: string; fields: LogFields;
+ }
+ ```
+
+- **`src/observability/logger/json-lines-transport.ts`** — default `JsonLinesHttpTransport` with batching, backpressure drop, redaction, `sendBeacon` flush.
+
+- **`src/observability/logger/console-transport.ts`** — dev-mode transport that pipes records to `console[level]`.
+
+- **`src/observability/logger/root.ts`** — `createRootLogger()` factory that reads env config and picks a transport.
+
+- **`src/observability/logger/provider.tsx`** — React context + `useLogger()` hook. On the server, the hook returns the request-scoped child logger; on the client, it returns the shared root logger.
+
+- **`src/observability/metrics/otel.ts`** — OpenTelemetry setup:
+
+ ```ts
+ export function initServerOtel(env: Env): void; // called once per Node process
+ export function initBrowserOtel(env: Env): void; // called once per tab
+ export function getMeter(name: string): Meter; // thin re-export of @opentelemetry/api
+ export function getTracer(name: string): Tracer;
+ ```
+
+- **`src/observability/metrics/custom.ts`** — the minimum-set custom metrics from design spec §7.3 as exported instruments:
+
+ ```ts
+ export const flightsSsrRequestDuration: Histogram;
+ export const flightsApiRequestDuration: Histogram;
+ export const flightsApiError: Counter;
+ export const flightsSignalRConnected: UpDownCounter;
+ export const flightsSignalRMessageReceived: Counter;
+ export const flightsSignalRDisconnect: Counter;
+ export const flightsFeatureRender: Counter;
+ export const flightsReactError: Counter;
+ // web-vitals histograms created at init time, not exported statically
+ ```
+
+- **`src/observability/analytics/facade.ts`**:
+
+ ```ts
+ export interface AnalyticsProps { [k: string]: unknown; }
+
+ export interface Analytics {
+ track(event: string, props?: AnalyticsProps): void;
+ page(url: string, props?: AnalyticsProps): void;
+ }
+
+ export function createAnalytics(options: {
+ enabled: Env["ANALYTICS_ENABLED"];
+ consent: { analytics: boolean; telemetry: boolean };
+ logger: Logger;
+ }): Analytics;
+ ```
+
+- **`src/observability/analytics/adapters/{metrica,ctm,variocube,dynatrace}.ts`** — four adapters implementing:
+
+ ```ts
+ export interface AnalyticsAdapter {
+ name: string;
+ load(): Promise;
+ track(event: string, props?: AnalyticsProps): void;
+ page(url: string, props?: AnalyticsProps): void;
+ }
+ ```
+
+ Phase 1 ships these as **stubs that simulate loading and log calls instead of hitting real vendor scripts**. Real vendor scripts are wired in Phase 2A (alongside the Online Board migration) after the customer provides credentials (see Phase 0 task 14, assumption A7).
+
+- **`src/observability/analytics/loader.tsx`** — `` component that mounts in the root layout (owned by 1F, modified by 1G to add the mount). Waits for `requestIdleCallback`, then imports enabled adapters and calls `.load()`.
+
+- **`src/observability/analytics/provider.tsx`** — React context + `useAnalytics()` hook. On the server, returns a `NoopAnalytics`. On the client, returns the instance created by ``.
+
+**TypeScript contracts:** the above. `Logger`, `Analytics`, `getMeter`, `getTracer` are the four surfaces every feature imports from.
+
+**Exit gate for 1G:**
+
+- Vitest tests cover: logger batching + flush; redaction of sensitive fields; transport backpressure drops old records; dev-mode console transport; analytics facade fans out to all four stub adapters; consent=false short-circuits; adapter load failure emits `flights.analytics.load_failed` metric.
+- Integration test: smoke route (1F) emits one log, one metric, and one analytics event — all three observable in a test harness capturing the pipeline outputs.
+
+---
+
+### 1H — Security hardening contracts
+
+**Exports:**
+
+- **`src/server/middleware/csp.ts`** — Modern.js middleware that generates a per-request nonce, sets the `Content-Security-Policy` header with the nonce, and exposes the nonce to the React render tree via a request-scoped context.
+
+ ```ts
+ export function cspMiddleware(options: { reportOnly?: boolean }): ModernMiddleware;
+ export const CspNonceContext: React.Context;
+ ```
+
+- **`src/server/middleware/security-headers.ts`** — Modern.js middleware that sets HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy, Cross-Origin-Resource-Policy.
+
+- **`src/shared/storage.ts`** — the only module allowed to touch `window.localStorage` / `window.sessionStorage`. Namespaced keys + redaction on write:
+
+ ```ts
+ export const storage = {
+ get(key: string, schema: ZodSchema): T | null,
+ set(key: string, value: T, schema: ZodSchema): void,
+ delete(key: string): void,
+ clear(): void,
+ };
+ ```
+
+ A zero-dep ESLint rule in `.eslintrc.cjs` forbids `window.localStorage` references anywhere except `src/shared/storage.ts`.
+
+- **`.eslintrc.cjs` additions** — `no-eval`, `no-implied-eval`, custom rule banning raw `innerHTML` assignments in TSX (handled by the `react/no-danger` rule + a custom augmentation).
+
+**Shared file ownership flags:**
+
+- `modern.config.ts` (owned by 1A) — 1H modifies it to register the two middlewares.
+- `src/routes/layout.tsx` (owned by 1F) — 1H modifies it to propagate the CSP nonce into ``-emitted inline scripts.
+
+**TypeScript contracts:** the middleware signatures + `storage` API.
+
+**Exit gate for 1H:**
+
+- Test: an SSR render emits a CSP header containing a unique nonce per request, and every inline `