---
description: App Router route organization (groups, layouts, chrome composition)
globs: app/**/*.{ts,tsx}
alwaysApply: false
---

# Route organization

Top-level routes live inside **route groups** so each surface owns its own
layout and chrome. Groups are wrapping folders in `(parens)` — they organize
the file tree without affecting URLs.

## Group map

| Group | URL surface | Audience | Chrome |
|---|---|---|---|
| `app/(marketing)/` | `/`, `/learn`, `/blog`, `/templates`, future public pages | Public, indexable | `Top` (via root) + marketing `<Footer />` |
| `app/(app)/` | `/create/*`, `/login`, `/profile`, future signed-in surfaces | Authenticated product | `Top` (via root) — no footer except **`/profile`** (see `profile/layout.tsx`) |
| `app/(admin)/` | `/monitor`, future ops dashboards | Operators | `Top` (via root) — no footer |
| `app/(dev)/` | `/components-preview`, future dev previews | Local dev (NODE_ENV gated) | `Top` (via root) — no footer |
| `app/(marketing-case-study)/` | `/use-cases/[slug]/rule` | Public case-study demos | Chromeless (no global `Top`; see `navigationChromelessPath.ts`) |
| `app/api/` | API routes | n/a | n/a |

Route folders **must not** sit loose at the top level of `app/`. If a new
surface doesn't fit an existing group, add a new group rather than dropping
the folder next to `(marketing)/`.

## Layout responsibilities

- **`app/layout.tsx`** — `<html>`, `<body>`, providers (`MessagesProvider`,
  `AuthModalProvider`), fonts, and `ConditionalNavigation`. Renders
  `{children}` directly inside the flex column. **Does not** render
  `<main>` — each group layout owns that.
- **`app/(marketing)/layout.tsx`** — wraps with `<main className="flex-1">`
  and appends the public `<Footer />`.
- **`app/(app)/layout.tsx`** / **`(admin)/layout.tsx`** / **`(dev)/layout.tsx`** —
  wrap with `<main className="flex-1">`. No footer by default; **`app/(app)/profile/layout.tsx`**
  appends the marketing `<Footer />` for `/profile` only.
- **Nested layouts** (e.g. `(app)/create/layout.tsx`) compose feature-specific
  chrome inside the group's `<main>` — never render `<html>`, `<body>`,
  `<main>`, or providers.

If a route needs different chrome than its group provides, prefer adding a
**nested layout** under that route — don't introduce pathname-sniffing
client components. (`ConditionalNavigation` is the lone tolerated exception
because it carries SSR session state; do not add new pathname-conditional
chrome components.)

## Co-located component folders

Page-private server/client components that are **only** used by routes in a
given group go in `_components/` inside that group:

```
app/(marketing)/_components/MarketingRuleStackSection.tsx
```

The leading underscore makes Next.js treat the folder as **private** — it's
ignored by the router. Use this instead of letting page-only files sit next
to `page.tsx`.

Components reused across groups belong in `app/components/<category>/`
(see `component-structure.mdc`).

## Adding a new route

1. **Choose the group** by audience: marketing (public), app (signed-in),
   admin (operators), dev (local-only). When in doubt, ask whether the
   public marketing footer should appear — if yes, it's `(marketing)`.
2. Create `app/(<group>)/<route>/page.tsx`. URLs do **not** include the
   group name.
3. If the route needs its own chrome (e.g. a wizard header), add
   `app/(<group>)/<route>/layout.tsx`.
4. If the route ships private helpers, put them in
   `app/(<group>)/<route>/_components/` (or
   `app/(<group>)/_components/` for group-wide page components).

## Splitting a group

Promote a sub-cluster to its own group only when **both** are true:

- It will hold ≥2 routes that share a layout, **or** it has a clearly
  distinct audience/access model (e.g. a future `(auth)/` for
  signup/forgot/verify alongside login).
- Moving the routes pays for itself by replacing existing pathname
  conditionals or by composing real shared chrome — not just by tidying
  the folder list.

YAGNI applies: a group with one route and no shared layout is just a
folder with parens.
