# Context: architecture

## Current (Astro, static output)

Ten routes, one `src/pages/` entry each, built to `dist/`:

| Route                | Page                            | Islands                                         |
| -------------------- | ------------------------------- | ----------------------------------------------- |
| `/`                  | `src/pages/index.astro`         | —                                               |
| `/full-guide/`       | `src/pages/full-guide.astro`    | `GuideSelector`, `LanguageToggle`, `CopyPrompt` |
| `/summary/`          | `src/pages/summary.astro`       | —                                               |
| `/models/`           | `src/pages/models.astro`        | —                                               |
| `/agents/`           | `src/pages/agents.astro`        | —                                               |
| `/skills/`           | `src/pages/skills.astro`        | `SkillPackageExplorer`                          |
| `/rules/`            | `src/pages/rules.astro`         | `RulesInteractive`                              |
| `/skills-review/`    | `src/pages/skills-review.astro` | `legacy/skills-review/app.js`                   |
| `/hands-on/starter/` | `public/` lab fixture           | own                                             |
| `/hands-on/rules/`   | `public/` lab fixture           | own                                             |

## What is still unmigrated

`legacy/` holds the parts the migration did not componentize. They are not dead
files — the pages listed above import them, and the build fails without them.

- **`legacy/styles/guide.css`** (was `styles.css`) — the editorial visual
  system, imported by `full-guide.astro`.
- **`legacy/styles/audit.css`** (was `full-guide/audit.css`) — responsive audit
  overrides, imported by `full-guide.astro`.
- **`legacy/styles/chapters.css`** — imported by `ChapterLayout.astro`.
- **`legacy/styles/skills.css`**, **`skills-review.css`**, **`change-lens.css`**
  — imported by their respective pages.
- **`legacy/skills-review/`** — `app.js` and the module graph under it
  (`catalog.js`, `submitted-catalog.js`, `files.js`, `submitted-files.js`,
  `vote.js`). `catalog.js` + `submitted-catalog.js` are the review desk's real
  data model, 24 entries; they are a content collection in all but name.

These sit outside `src/` deliberately: `check-tokens.mjs` sweeps `src`, and
these files are full of raw hex and unnamed breakpoints. Moving one into `src/`
means migrating it to tokens in the same change, not adding an exclusion.

`responsive.css`, `landing.css`, `app.js`, `rules/app.js`, `rules/styles.css`,
and `skills/app.js` were deleted at cutover: their content lives in components.

## Layout

```
src/
  content/            catalog entries, chapter copy, EN/PT strings (typed collections)
  layouts/            BaseLayout, ChapterLayout, GuideLayout
  components/         .astro by default; islands only where marked
  styles/             tokens.css, base.css, then per-component styles
  pages/              routes mirroring today's URLs exactly
public/
  hands-on/           lab fixtures copied verbatim, never processed
```

### Non-negotiables

- **URLs do not change.** `/full-guide/`, `/skills-review/`,
  `/hands-on/starter/` and the rest must resolve exactly as they do now,
  trailing slash included. Existing links (including `docs/`, SilverBullet, and
  shared URLs with `?author=…&skill=…&view=…` query params) must keep working.
- **Zero JS by default.** Seven of the ten pages ship no JavaScript. They must
  still ship none. Islands are opt-in, per component, and justified.
- **`hands-on/` stays vanilla.** It lives in `public/` untouched. It is a lab
  fixture, not a component.
- **No external runtime requests.** `audit-ui.mjs` enforces this and it is part
  of the site's thesis. Self-host anything you add.
- **The review desk's query-param deep links keep working** — `?author=`,
  `?skill=`, `?view=`, `?file=`, `?compare=`, `?render=`. They are documented in
  the page footer and shared externally.

## Companion service

The vote API is a Go service on its own Kubernetes deploy cycle, reached by the
review desk over `window.SKILLS_REVIEW_VOTE_API`. Its source left this
repository on 2026-09-06; the deployed service is unchanged, and the review desk
still calls it. Keep the global, or replace it with a build-time
`PUBLIC_VOTE_API` env var — but if you do, update the service's own README in
the same change.

Its one-vote-per-IP assertion left `verify.mjs` with it. See
[`assertion-removals.md`](assertion-removals.md).
