diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 000000000..3addf692f
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,86 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project
+
+Squonk 2 Data Manager UI — a Next.js (Pages Router) web app for the Informatics Matters Data Manager. It talks to two backend APIs: the **Data Manager (DM) API** and the **Account Server (AS) API**. UI major versions are pinned to specific API versions (see the compatibility table in `README.md`); a UI major bump corresponds to a major change in one of those APIs.
+
+## Commands
+
+Uses `pnpm` (Node >= 22, pnpm >= 10).
+
+- `pnpm dev` — start the dev server. `pnpm dev:debug` runs it under the Node inspector (attach the VSCode debugger).
+- `pnpm build` — production build. `pnpm start` — serve the production build.
+- `pnpm tsc` — one-off type check (`tsc --noEmit`). Run this to verify types; the Next build itself ignores type/eslint errors (`ignoreBuildErrors`/`ignoreDuringBuilds` in `next.config.ts`).
+- `pnpm lint` — eslint (`--max-warnings=0`). `pnpm format` — eslint `--fix`.
+- `pnpm test` — Playwright tests. `pnpm test:debug` — headed/debug mode.
+- Run a single test: `pnpm exec playwright test tests/datetime.node.ts` (or pass `-g "
"`).
+
+First-time Playwright setup: `pnpm exec playwright install-deps && pnpm exec playwright install`.
+
+`pnpm tsc` and `lint-staged` run on **pre-commit** (`.husky/pre-commit`). On `master`, the pre-commit hook also fails if `link:..` workspace links are present in `pnpm-lock.yaml`.
+
+## Architecture
+
+### API access: client + proxy
+
+The app never calls the DM/AS APIs directly from the browser. Instead:
+
+1. **Generated clients** — `@squonk/data-manager-client` and `@squonk/account-server-client` are external, orval-generated React Query clients (hooks like `useGetProjects`). Import data hooks from these packages (often subpath imports, e.g. `@squonk/data-manager-client/project`); types come from the package root.
+2. **Base URLs** are set once in `src/pages/_app.tsx` via `setDMBaseUrl`/`setASBaseUrl` to local proxy paths (`/api/dm-api`, `/api/as-api`) defined in `src/constants/proxies.ts`.
+3. **Proxy routes** — `src/pages/api/dm-api/[...dmProxy].ts` and `as-api/[...asProxy].ts` use `createProxyMiddleware` (`src/utils/api/apiProxy.ts`). The proxy reads the Auth0 access token server-side and injects `Authorization: Bearer `, then forwards to the real API (`DATA_MANAGER_API_SERVER` / `ACCOUNT_SERVER_API_SERVER`). The browser bundle therefore never sees the token. Requests work unauthenticated too — the API enforces auth.
+
+So: client hooks → local `/api/*-api` proxy → token injected → upstream API.
+
+### Auth
+
+Auth0 (`@auth0/nextjs-auth0`) fronts a Keycloak issuer. `src/pages/api/auth/[auth0].ts` provides login/logout/callback. `UserProvider` wraps the app in `_app.tsx`; hooks like `useKeycloakUser`, `useKeycloakIdToken`, `useIsAuthorized` expose user/authorization state. Project-level permissions are computed client-side in `src/hooks/projectHooks.ts` (`hasProjectRole`, `useHasProjectRole`).
+
+### State & data
+
+- **Server state**: TanStack React Query, provided in `_app.tsx`. SSR uses `HydrationBoundary` + `dehydratedState`; SSR query helpers live in `src/utils/api/ssrQueryOptions.ts` and `src/utils/next/ssr.ts`.
+- **Client state**: Jotai atoms in `src/state/*` (e.g. `eventStream.ts`, `fileSelection.ts`, `notifications.ts`). `immer`/`use-immer` are used for complex updates (`enableMapSet()` is called in `_app.tsx`).
+
+### Code organisation
+
+- `src/pages/` — Next.js Pages Router routes. `_app.tsx` composes all top-level providers (theme, Auth0, React Query, snackbar, event stream, MDX). Routes are **typed** via `nextjs-routes`; generated route types land in `types/` (configured in `next.config.ts`). Use the typed route helpers rather than raw strings.
+- `src/features/` — larger self-contained feature modules (e.g. `DatasetsTable`, `results`, `SDFViewer`, `Finance`).
+- `src/components/` — reusable UI components.
+- `src/hooks/` — shared hooks; `src/hooks/api/` wraps generated client mutations.
+- `src/utils/` — `api/` (proxy, SSR, fetch helpers), `app/` (domain helpers: paths, products, datetime, coins), `next/` (emotion cache, localStorage, orval error handling).
+- `src/layouts/` — page chrome (Header, Footer, navigation).
+- `src/constants/` — shared constants (proxy URLs, auth, fonts, datetimes).
+
+### Protobuf event stream
+
+A live WebSocket event stream (`src/components/eventStream/`, `react-use-websocket`) carries billing/charge messages encoded with Protobuf. Schemas are in `src/protobuf/proto/*.proto`; TypeScript is generated into `src/protobuf/gen/` via `buf` + `protoc-gen-es` (`buf.gen.yaml`). Regenerate with `pnpm exec buf generate` after editing `.proto` files — do not hand-edit `gen/`.
+
+### Styling & UI
+
+MUI v7 with Emotion (`jsxImportSource: "@emotion/react"` — note this affects JSX typing). Shared theme is the external `@squonk/mui-theme`. Docs pages use MDX (`@next/mdx`) under `src/pages/docs/`. Forms use `@rjsf` (JSON Schema) and `@tanstack/react-form`. Chemistry: `ketcher-*` (structure sketcher) and `@squonk/sdf-parser` / Plotly for visualisation.
+
+### Observability
+
+Sentry is wired via `sentry.*.config.ts` and `withSentryConfig` in `next.config.ts` (source maps uploaded then deleted at build). `src/instrumentation.ts` is the Next instrumentation hook.
+
+## Environment
+
+Config is entirely env-driven (`.env.*` files; Kubernetes injects `.env` at runtime). Key vars: `DATA_MANAGER_API_SERVER`, `ACCOUNT_SERVER_API_SERVER`, `KEYCLOAK_URL`, `AUTH0_SECRET`/`AUTH0_CLIENT_ID`/`AUTH0_CLIENT_SECRET`, `NEXT_PUBLIC_BASE_PATH` (the app is served under a subpath, e.g. `/data-manager-ui` — use `withBasePath` from `src/utils/app/basePath.ts` when building URLs). See `.env.local.example`. `DANGEROUS__DISABLE_SSL_CERT_CHECK_IN_API_PROXY` is test-only.
+
+## Testing (Playwright)
+
+Tests live in `tests/`, split by **project** via filename suffix (`playwright.config.ts`):
+- `*.node.ts` — Node-environment tests (no browser).
+- `*.browser.ts` — unauthenticated browser tests.
+- `*.browser-authenticated.ts` — authenticated; depend on `*.setup.ts` (login flow) which writes `storageState.json`. These retry up to 3×.
+
+Test config loads `.env.test.local` (see `.env.test.local.example`; needs `PW_USERNAME`/`PW_PASSWORD`). The web server is started by Playwright via `pnpm start` and reuses an existing server if running.
+
+## Releases
+
+`semantic-release` with conventional commits (no manual version bumps; `package.json` version is managed automatically):
+- `dev` branch → prerelease tags `X.Y.Z-dev.N` → test deployment.
+- `master` branch → stable tags `X.Y.Z` → production deployment + `:stable` Docker tag.
+
+The `/assets` folder is a git **subtree** of `InformaticsMatters/squonk-assets` — update with `git subtree pull --prefix assets main --squash`, not by editing in place.