diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..e2044f2
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,18 @@
+name: Docs Build
+
+on:
+ pull_request:
+ push:
+ branches: [master]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: npm
+ - run: npm ci
+ - run: npm run docs:build
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..d2b41f1
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,72 @@
+# Contributing
+
+Thanks for helping document GuildYapper. This guide covers everything you need to open a useful PR without a back-and-forth review.
+
+## Local Setup
+
+```sh
+npm install
+npm run docs:dev
+```
+
+`docs:dev` serves the site locally with hot reload so you can see your changes as you write.
+
+Before opening a PR, run:
+
+```sh
+npm run docs:build
+```
+
+**This is your self-check.** The build fails on any dead internal link, so if it succeeds, your links are correct. CI runs this same command on every PR, so catching it locally saves a review round trip.
+
+## Page Structure
+
+Every page follows the same shape. Use `docs/features/signups.md` as the canonical example. It has:
+
+- Frontmatter with a one-line `description`.
+- An H1 title.
+- A short `## Who Can Use It` near the top — who (members, officers/admins, public) can use the feature. Many pages, including `signups.md`, open with a `## What It Does` summary first; exact ordering of the early sections varies by area (`docs/features/` tends to summarize first, `docs/events/` tends to put `Who Can Use It` first), so match the neighboring pages in whichever folder you're editing rather than a fixed sequence.
+- Content sections covering how to use the feature.
+- `## Common Issues` at the end, when the page has enough known gotchas to warrant it.
+
+Not every page needs every section (a reference page like the glossary skips `Who Can Use It`), but don't invent a different shape without a reason.
+
+## Vocabulary
+
+- Say **platoon** (in-game party) on the first occurrence in a page.
+- Write command names exactly as they appear in the bot, including flags/options (for example `` `/signup nodewar` ``, not "the nodewar signup command").
+
+## Screenshots
+
+- Place images under `docs/public/screenshots//` (e.g. `signups/`, `parties/`, `scores/`) using existing folders where possible — see `docs/public/screenshots/README.md`.
+- Format: `.webp`.
+- Use the `ScreenshotFigure` component to display one:
+
+ ```md
+