# Rule: Astro Binding for every `.astro` file. ## Zero JS is the default A component ships no JavaScript unless it has a `client:*` directive. Seven of this site's ten pages ship no JS today and must continue to. - Never add `client:load` without justifying it in the PR description. - Prefer, in order: no JS → `client:visible` → `client:idle` → `client:load`. - An island is a **leaf**, not a wrapper. Hydrate the tab panel, not the page. ## Islands in this project Only these need interactivity. Anything else claiming island status is wrong: | Island | Why | Directive | | --- | --- | --- | | Guide phase/tab switchers | click-driven panel swap | `client:visible` | | Review desk catalog + file viewer | search, filter, fetch source files | `client:load` | | Vote widget | talks to `vote-service/` | `client:visible` | | Language toggle | swaps EN/PT across the page | `client:idle` | ## Structure ```astro --- // 1. imports // 2. Props interface // 3. destructure Astro.props // 4. derived values — no side effects, no fetch in components --- ``` - Typed props always: `interface Props { … }`, then `const { … } = Astro.props`. - Data loading belongs in `src/content/` collections or the page frontmatter, not inside a component. - No barrel files (`index.ts` re-export hubs). They cost tree-shaking and invite cycles. ## Content collections All copy lives in `src/content/`, typed with a Zod schema in `src/content/config.ts`. The review desk's `catalog.js` maps onto a collection almost one-to-one — do that rather than importing a 27 KB JS file. ## Styles - Component styles go in the component's `