# 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).