> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wrixton.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# War.re Monorepo: Multi-Domain App Layout and Structure

> Understand how the War.re monorepo organises two independent Next.js apps — one for war.re and one for ryan.war.re — with separate builds and deploys.

War.re is organised as a monorepo containing two fully independent Next.js applications. Each app lives in its own directory, carries its own `package.json` and `yarn.lock`, and builds and deploys separately — there is no shared runtime, no shared node\_modules, and no cross-app imports. You work inside whichever app directory you need and treat the other as a sibling project.

## The Two Apps

| Directory          | Domain                             | Purpose           |
| ------------------ | ---------------------------------- | ----------------- |
| `main/`            | [war.re](https://war.re)           | Personal homepage |
| `subdomains/ryan/` | [ryan.war.re](https://ryan.war.re) | Online resume     |

Both apps use the **Pages Router** (`pages/`), not the App Router. Each has its own `_app.tsx` and `_document.tsx` for custom wrappers, its own `styles/` directory for CSS modules, and its own `public/` directory for static assets such as favicons and OG images.

## Directory Tree

The repository root holds the two app directories alongside a small `scripts/` folder and repo-level config files. Here is a condensed view of the key paths:

```text theme={null}
war.re/                          ← repository root
├── CLAUDE.md                    ← project-wide conventions for AI tools
├── README.md
├── scripts/                     ← shared utility scripts
│
├── main/                        ← Next.js app for war.re
│   ├── package.json
│   ├── yarn.lock
│   ├── next.config.js           ← output: 'export', reactStrictMode: true
│   ├── tsconfig.json
│   ├── pages/
│   │   ├── _app.tsx
│   │   ├── _document.tsx
│   │   ├── index.tsx            ← meta-refresh redirect → /n
│   │   ├── n/
│   │   │   └── index.tsx        ← homepage content (war.re/n)
│   │   └── 404.tsx
│   ├── styles/
│   ├── public/
│   └── e2e/                     ← Playwright smoke tests
│
└── subdomains/
    └── ryan/                    ← Next.js app for ryan.war.re
        ├── package.json
        ├── yarn.lock
        ├── next.config.js       ← output: 'export', reactStrictMode: true
        ├── tsconfig.json
        ├── pages/
        │   ├── _app.tsx
        │   ├── _document.tsx
        │   ├── index.tsx        ← meta-refresh redirect → /n
        │   ├── n/
        │   │   └── index.tsx    ← resume content (ryan.war.re/n)
        │   └── 404.tsx
        ├── components/          ← resume-specific React components
        │   ├── Activity.tsx
        │   ├── ActivityList.tsx
        │   ├── Experience.tsx
        │   └── ExperienceItem.tsx
        ├── styles/
        ├── public/
        ├── __tests__/           ← Jest + React Testing Library
        └── e2e/                 ← Playwright smoke tests
```

## URL Routing and the `/n` Redirect

Neither app uses server-side rewrites. Both are configured with `output: 'export'` in `next.config.js`, which generates a fully static site that Vercel serves directly from the filesystem.

The root path (`/`) on each domain uses an HTML `<meta http-equiv="refresh">` tag to immediately redirect the browser to `/n`, which is where the actual content lives. This approach works without any server logic:

* Visiting `war.re` → browser reads the meta-refresh → navigates to `war.re/n`
* Visiting `ryan.war.re` → browser reads the meta-refresh → navigates to `ryan.war.re/n`

A `<noscript>` fallback link is included for browsers with JavaScript disabled so they still get a clickable link to `/n`.

## Pages Directory Structure

Each app mirrors the same `pages/` layout:

| File                  | Route  | Role                                  |
| --------------------- | ------ | ------------------------------------- |
| `pages/index.tsx`     | `/`    | Meta-refresh shell; redirects to `/n` |
| `pages/n/index.tsx`   | `/n`   | Main content page                     |
| `pages/_app.tsx`      | —      | Custom App wrapper                    |
| `pages/_document.tsx` | —      | Custom Document wrapper               |
| `pages/404.tsx`       | `/404` | Custom not-found page                 |

The `subdomains/ryan/` app also has a `components/` directory at the app root, holding the resume-specific React components (`Activity`, `ActivityList`, `Experience`, `ExperienceItem`) that the `/n` page composes together.

<Tip>
  Both apps configure a TypeScript path alias `@/*` that maps to the app's own project root. Use `@/components/Foo` instead of relative paths like `../../components/Foo` to keep imports clean and refactor-safe as the directory tree grows.
</Tip>

## Independent Builds

Run all commands from inside the relevant app directory — **not** from the repository root. Each app is self-contained:

```bash theme={null}
# Work on the main site
cd main/
yarn dev          # dev server at localhost:3000
yarn build        # static export → out/
yarn lint         # ESLint
yarn typecheck    # tsc --noEmit
yarn format       # Prettier (format:check to verify without writing)
yarn e2e          # Playwright smoke tests (run yarn build first)

# Work on the resume
cd subdomains/ryan/
yarn dev          # dev server at localhost:3000
yarn build        # static export → out/
yarn test         # Jest + React Testing Library
yarn lint         # ESLint
yarn typecheck    # tsc --noEmit
yarn format       # Prettier (format:check to verify without writing)
yarn e2e          # Playwright smoke tests (run yarn build first)
```

The `main/` app currently has no Jest unit tests (`--passWithNoTests` is set so the test script exits cleanly). The `subdomains/ryan/` app has full Jest coverage for its components.
