# Context: architecture, current and target ## Current (no build step) Ten hand-written HTML pages, each linking its own CSS and one ES module: | Route | Page | Script | Stylesheets | | --- | --- | --- | --- | | `/` | `index.html` | — | `chapters.css`, `landing.css` | | `/full-guide/` | `full-guide/index.html` | `app.js` (50 KB) | `styles.css`, `responsive.css`, `audit.css` | | `/summary/` | `summary/index.html` | — | `chapters.css` | | `/models/` | `models/index.html` | — | `chapters.css` | | `/agents/` | `agents/index.html` | — | `chapters.css` | | `/skills/` | `skills/index.html` | `skills/app.js` | `skills/styles.css` | | `/rules/` | `rules/index.html` | `rules/app.js` | `rules/styles.css` | | `/skills-review/` | `skills-review/index.html` | `skills-review/app.js` | `skills-review/styles.css`, `change-lens.css` | | `/hands-on/starter/` | lab fixture | own | own | | `/hands-on/rules/` | lab fixture | own | own | Weight is concentrated: `app.js` 50 KB, `responsive.css` 30 KB, `skills-review/catalog.js` 27 KB, `skills-review/submitted-catalog.js` 18 KB. ### What each big file actually is - **`app.js`** — not really application code. It is a **bilingual content database** (`phases`, `handsOnPrompts`, `modelGuide`, `skillSources`, `skillInstallPrompts`, each keyed `{en, pt}`) plus ~12 small `render*` functions that swap `innerHTML` on tab clicks. ~50 `en:` keys. The content should become data; only the tab behaviour is interactive. - **`responsive.css`** — a 30 KB append-only layer of overrides bolted on top of `styles.css`. Expect large parts to be dead once layout moves into components. Do not port it verbatim. - **`skills-review/catalog.js`** — the real data model of the review desk: one entry per submitted skill with `id`, `author`, `title`, `status`, `focus`, `wins[]`, `improve[]`, `extras`, `improved` (full markdown). 24 entries across `catalog.js` + `submitted-catalog.js`. This is already a content collection in all but name. - **`skills-review/files.js` / `submitted-files.js`** — generated file manifests. - **`vote.js`** — the vote widget island; talks to `vote-service/`. ## Target (Astro) ``` 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 for the target - **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 today. They must still ship none. Islands are opt-in, per component, and justified. - **`hands-on/` stays vanilla.** It goes 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 `vote-service/` is a Go API on its own Kubernetes deploy cycle, reached by the review desk over `window.SKILLS_REVIEW_VOTE_API`. The refactor does not touch it. Keep the global, or replace it with a build-time `PUBLIC_VOTE_API` env var — but if you do, update `vote-service/README.md` in the same change.