Operating guide for AI coding agents working in this repo. Keep changes minimal, match the conventions below, and prefer reading the linked docs over inferring intent.
| `apps/civi-crm` | Public funnel intake endpoint (Next.js 16, no pages). Port `3002`. See [`docs/civi-crm/architecture.md`](docs/civi-crm/architecture.md). |
pnpm lint # eslint --max-warnings 0 across workspaces
pnpm check-types # next typegen + tsc --noEmit
pnpm generate-types # cms only — regenerates packages/types/src/payload.ts
```
`apps/web/start` is `python3 -m http.server` over the static `out/` build, not `next start`. Treat the web app as static.
## Architecture rules
1.**`apps/web` does not call the GitHub API at request time.** It reads `content/**` at build. Preview deploys build per PR; production deploys from `master`.
2.**CMS edits are PRs, not DB writes.** Payload's Postgres only stores users, sessions, drafts, and PR cache. All published content changes go through `develop` via GitHub PR (see `apps/cms/src/services/content-workflow/`).
3.**Branch model:**`develop` is the default + staging branch. `master` is production (created when production deploys spin up). Direct commits to either are blocked by branch protection. CMS-generated PRs target `develop`.
4.**Env access is typed.** Use `apps/web/lib/env.ts`; never reach into `process.env` from feature code. Add new vars to `env.ts`.
5.**The CMS refuses to boot in production-like envs without `NEXT_PUBLIC_SERVER_URL` / `NEXT_PUBLIC_WEB_URL`** — silent localhost fallback breaks CORS/cookies. Don't "fix" this by removing the assertion.
## i18n
- Active locales come from `apps/web/i18n/routing.ts` (today: `['en']`). `Language` type is pre-declared as `'en' | 'fr' | 'ko'` so adding a locale is a routing change, not a schema change.
- Page copy / structured data → `content/**/<locale>/...` JSON files.
- **All user-facing strings go through i18n keys.** Includes titles, descriptions, labels, and `createDefaultMetadata` args. Use `getTranslations` in server components, `useTranslations` in client components. Hardcoded strings will be rejected.
- A locale added to `routing.ts` makes its `<locale>.json` files required at build time — there is no silent fallback.
- After `next build`, `apps/web/scripts/strip-default-locale-prefix.sh` strips the `/en` prefix from the static export. Don't bypass it.
## Code conventions
- **Repo files are English only.** Committed code, comments, docs, copy. Chat replies can be in any language; files cannot.
- **Use British English for public paths and all user-facing copy.** This includes route segments, slugs, content JSON values, `next-intl` messages, metadata, docs that quote public copy, and CMS seed fixtures. Prefer spellings such as `decentralised`, `centralised`, `programme`, `organise`, `organisation`, `neighbourhood`, and `licence` for copy. Keep third-party API literals, official external URLs, schema.org types, package names, generated types, and internal field names unchanged when they require American spelling.
- **Icons are React SVG components** exported from `@repo/ui` (`packages/ui/src/icons`). If a designed icon is missing, stop and ask for a Figma export — don't substitute Unicode/text.
- **The λ brand mark is `<LogosMark />`** from `@repo/ui`. Size via `size`, color via parent `text-*` (the SVG uses `currentColor`). Never `<img src=".svg" />` or `<span>λ</span>`.
- **Every clickable element gets `cursor-pointer`** in its Tailwind className: buttons, `onClick` handlers, anchors, clickable cards.
- **Basecamp install CTAs keep automatic OS and architecture detection.** When publishing a new Basecamp release, update `EXTERNAL_URLS.basecampRelease` and every platform download URL together using the exact GitHub release asset names. Supported Linux and macOS clients download the matching asset directly; unknown or unsupported clients fall back to `https://github.com/logos-co/logos-basecamp/releases#release-<version>`. Content-level `external` flags must never bypass this resolver.
- **Visuals match Figma 1:1.** Pull the spec (font sizes, fills, gaps, padding) from Figma before implementing. See `docs/components.md` for canonical node IDs and `docs/web-pages.md` for per-page references.
- **Types on public APIs.** Exported functions, shared utilities, component props. Use `interface` for object shapes, `type` for unions/intersections. Avoid `any`; use `unknown` + narrowing for external input.
- **Keep reusable code out of generated or oversized files.** If a type, constant list, validator, or UI helper is used in more than one place, move it into a focused module and import it. Do not let generated files or collection configs absorb long literal unions, large option arrays, or repeated validation logic.
- **Split files by feature when they grow.** Prefer small folders with focused files (types, constants, validators, components) over large catch-all modules. Create a feature folder once a file mixes multiple responsibilities or becomes hard to scan.
- **Immutability.** Spread/copy, never mutate. `Readonly<T>` on inputs where it clarifies intent.
- **No `console.log` in committed code.**
- **Follow existing file organization.** Many small focused files over large ones. Routes live under `apps/web/app/[locale]/<route>/`; section components under `apps/web/components/sections/<section>/`.
1. Update the Zod schema in `packages/content/src/schemas/`.
2. Update / add the corresponding loader.
3. Update fixtures under `content/**`.
4. If the CMS edits this collection, update the matching Payload collection in `apps/cms/src/collections/` and the workflow service in `apps/cms/src/services/content-workflow/`.
5. Run `pnpm generate-types` (CMS) and `pnpm check-types`.
Payload collections: `Pages`, `Circles`, `Ideas`, `Rfps`, `ContentChangeRequests`, `Users`. The first four mutate via the GitHub PR workflow (see `save-as-pr.ts`, `save-idea-as-pr.ts`, `save-rfp-as-pr.ts`).
## Testing
- Vitest lives in `apps/web` (`pnpm test` from root or app). Add tests beside the code in `__tests__/` folders.
- Do not add UI implementation contract tests that assert Tailwind class strings, Figma measurements, layout spacing, motion details, hover treatment, or component source structure. UI changes are reviewed through browser verification, not brittle source-string tests.
-`docs/responsive-image-frame-fitting.md` — techniques for making images fill a card or frame perfectly at every viewport (aspect-ratio, derived heights, object-fit, sizes). Read before implementing any full-bleed or frame-fitted image section.