# The Brochure — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build a single-page showcase site (`site/`) for Darkwatch with a dark hero section, parchment feature rows with Playwright-generated screenshots, a system support strip, and a publisher contact section. **Architecture:** Plain HTML/CSS/JS with no build step or framework. A Playwright Node script (`site/screenshots.js`) logs into the running dev app and captures feature screenshots automatically. A `update-brochure` Claude skill keeps the site current as features change. **Tech Stack:** HTML · CSS · Vanilla JS · Playwright (Node, screenshot script only) --- ## File Map | Action | Path | Purpose | |---|---|---| | Create | `site/index.html` | The page — all sections | | Create | `site/style.css` | All styles — CSS variables, fonts, layout | | Create | `site/main.js` | Smooth scroll + hero parallax | | Create | `site/assets/screenshots/.gitkeep` | Ensures directory exists in git | | Create | `site/package.json` | Playwright dependency for screenshot script | | Create | `site/screenshots.js` | Playwright script — captures all feature screenshots | | Create | `.claude/skills/update-brochure.md` | Maintenance skill | | Modify | `CLAUDE.md` | Register update-brochure skill | | Modify | `.gitignore` | Ignore `site/node_modules/` | --- ## Task 1: CSS foundation **Files:** - Create: `site/style.css` - Create: `site/assets/screenshots/.gitkeep` - [ ] **Step 1: Create `site/assets/screenshots/.gitkeep`** ```bash mkdir -p site/assets/screenshots touch site/assets/screenshots/.gitkeep ``` - [ ] **Step 2: Create `site/style.css`** ```css @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Alegreya:ital,wght@0,400;0,500;1,400&display=swap'); /* ── Variables ─────────────────────────────────────────── */ :root { --gold: #c9a84c; --gold-dark: #8b6914; --gold-dim: rgba(201, 168, 76, 0.12); --gold-border: rgba(201, 168, 76, 0.3); --bg-dark: #0d0a05; --bg-dark-2: #1a1408; --bg-parchment: #f5f0e8; --bg-parchment-alt: #ede8d5; --border-dark: #2a2010; --border-parchment: #d4c9a8; --border-light: #e0d5b5; --text-gold: #c9a84c; --text-parchment: #e8dcc8; --text-dark: #2a1f0a; --text-muted: #6a5a3a; --text-label: #8b6914; --font-display: 'Cinzel', serif; --font-body: 'Alegreya', serif; } /* ── Reset ──────────────────────────────────────────────── */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { scroll-behavior: smooth; } body { font-family: var(--font-body); background: var(--bg-dark); color: var(--text-parchment); } img { display: block; max-width: 100%; height: auto; border-radius: 6px; } a { text-decoration: none; } /* ── Hero ───────────────────────────────────────────────── */ .hero { min-height: 100vh; background: var(--bg-dark); border-bottom: 2px solid var(--border-dark); display: flex; align-items: center; justify-content: center; text-align: center; padding: 80px 24px; position: relative; overflow: hidden; } .hero::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse 80% 60% at 50% 40%, rgba(201,168,76,0.07) 0%, transparent 70%); pointer-events: none; } .hero-content { position: relative; max-width: 680px; } .wordmark { font-family: var(--font-display); font-size: 11px; letter-spacing: 4px; text-transform: uppercase; color: var(--gold); margin-bottom: 28px; opacity: 0.9; } .hero-headline { font-family: var(--font-display); font-size: clamp(2.2rem, 5vw, 3.6rem); font-weight: 700; color: var(--text-parchment); line-height: 1.2; margin-bottom: 20px; text-shadow: 0 0 40px rgba(201,168,76,0.15); } .hero-subhead { font-family: var(--font-body); font-size: 1.15rem; color: #9a8a6a; line-height: 1.65; margin-bottom: 40px; max-width: 520px; margin-left: auto; margin-right: auto; } .hero-actions { display: flex; align-items: center; justify-content: center; gap: 28px; flex-wrap: wrap; } .btn-primary { font-family: var(--font-display); font-size: 13px; letter-spacing: 2px; text-transform: uppercase; background: var(--gold); color: var(--bg-dark); padding: 14px 32px; border-radius: 3px; font-weight: 700; transition: background 0.2s, transform 0.1s; } .btn-primary:hover { background: #e0bc60; transform: translateY(-1px); } .scroll-hint { font-family: var(--font-body); font-size: 0.95rem; color: var(--text-muted); transition: color 0.2s; } .scroll-hint:hover { color: var(--gold); } /* ── Features ───────────────────────────────────────────── */ .features { background: var(--bg-parchment); } .features-intro { padding: 64px 24px 40px; text-align: center; border-bottom: 1px solid var(--border-parchment); } .section-label { font-family: var(--font-display); font-size: 10px; letter-spacing: 3px; text-transform: uppercase; color: var(--text-label); margin-bottom: 10px; } .features-intro h2 { font-family: var(--font-display); font-size: clamp(1.4rem, 3vw, 2rem); color: var(--text-dark); font-weight: 600; } .feature-row { display: grid; grid-template-columns: 1fr 1fr; align-items: center; gap: 0; border-bottom: 1px solid var(--border-light); background: var(--bg-parchment); } .feature-row:nth-child(even) { background: var(--bg-parchment-alt); } .feature-image { padding: 48px 40px; display: flex; align-items: center; justify-content: center; } .feature-image img { box-shadow: 0 8px 32px rgba(42,31,10,0.18); border: 1px solid var(--border-parchment); } .feature-text { padding: 48px 56px 48px 24px; } .feature-row.reverse .feature-image { order: 2; } .feature-row.reverse .feature-text { order: 1; padding: 48px 24px 48px 56px; } .feature-label { font-family: var(--font-display); font-size: 10px; letter-spacing: 2px; text-transform: uppercase; color: var(--text-label); margin-bottom: 10px; } .feature-heading { font-family: var(--font-display); font-size: 1.5rem; font-weight: 600; color: var(--text-dark); margin-bottom: 14px; line-height: 1.3; } .feature-desc { font-family: var(--font-body); font-size: 1.05rem; color: #4a3a1a; line-height: 1.7; } /* Torch & Luck callout cards */ .callout-row { padding: 56px 40px; background: var(--bg-parchment-alt); border-bottom: 1px solid var(--border-light); display: flex; gap: 32px; justify-content: center; } .callout-card { background: var(--bg-parchment); border: 1px solid var(--border-parchment); border-radius: 6px; padding: 32px 36px; max-width: 320px; text-align: center; } .callout-icon { font-size: 2rem; margin-bottom: 12px; } .callout-card h3 { font-family: var(--font-display); font-size: 1rem; font-weight: 600; color: var(--text-dark); margin-bottom: 8px; } .callout-card p { font-family: var(--font-body); font-size: 0.95rem; color: #4a3a1a; line-height: 1.6; } /* ── System Support ─────────────────────────────────────── */ .system-support { background: var(--bg-dark-2); border-top: 2px solid var(--border-dark); border-bottom: 2px solid var(--border-dark); padding: 48px 24px; text-align: center; } .system-support .section-label { color: var(--gold); opacity: 0.7; } .system-support h2 { font-family: var(--font-display); font-size: 1.4rem; color: var(--text-parchment); font-weight: 600; margin-bottom: 10px; } .system-support p { font-family: var(--font-body); color: var(--text-muted); font-size: 1rem; } /* ── Publisher ──────────────────────────────────────────── */ .publisher { background: var(--bg-parchment); border-bottom: 1px solid var(--border-parchment); padding: 80px 24px; text-align: center; } .publisher .section-label { margin-bottom: 14px; } .publisher h2 { font-family: var(--font-display); font-size: clamp(1.4rem, 3vw, 2rem); color: var(--text-dark); font-weight: 600; margin-bottom: 20px; } .publisher p { font-family: var(--font-body); font-size: 1.1rem; color: #4a3a1a; line-height: 1.75; max-width: 520px; margin: 0 auto 32px; } .btn-secondary { font-family: var(--font-display); font-size: 12px; letter-spacing: 2px; text-transform: uppercase; background: var(--bg-dark); color: var(--gold); padding: 13px 30px; border-radius: 3px; font-weight: 600; border: 1px solid var(--border-dark); transition: background 0.2s; display: inline-block; } .btn-secondary:hover { background: var(--bg-dark-2); } /* ── Footer ─────────────────────────────────────────────── */ .footer { background: var(--bg-dark); border-top: 1px solid var(--border-dark); padding: 28px 40px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; } .footer-left { font-family: var(--font-display); font-size: 11px; letter-spacing: 1px; color: #4a3a1a; } .footer-right { display: flex; gap: 20px; } .footer-right a { font-family: var(--font-display); font-size: 11px; letter-spacing: 1px; color: #4a3a1a; transition: color 0.2s; } .footer-right a:hover { color: var(--gold); } /* ── Responsive ─────────────────────────────────────────── */ @media (max-width: 768px) { .feature-row, .feature-row.reverse { grid-template-columns: 1fr; } .feature-row.reverse .feature-image { order: 0; } .feature-row.reverse .feature-text { order: 0; } .feature-image { padding: 32px 24px 0; } .feature-text, .feature-row.reverse .feature-text { padding: 24px 24px 40px; } .callout-row { flex-direction: column; align-items: center; } .footer { justify-content: center; text-align: center; } } ``` - [ ] **Step 3: Verify fonts load** ```bash open site/index.html # will 404 — we'll create it in Task 2. Just confirm style.css has no syntax errors: node -e "require('fs').readFileSync('site/style.css', 'utf8'); console.log('OK')" ``` Expected: `OK` - [ ] **Step 4: Commit** ```bash git add site/style.css site/assets/screenshots/.gitkeep git commit -m "feat: add brochure CSS foundation and screenshot directory" ``` --- ## Task 2: Hero section **Files:** - Create: `site/index.html` - [ ] **Step 1: Create `site/index.html` with skeleton + hero** ```html Darkwatch — Real-Time Shadowdark Companion
◈ Darkwatch

Your whole party.
One screen. No paper.

A real-time session companion for Shadowdark RPG. Characters, dice, spells, and atmosphere — synced live across every device at the table.

``` - [ ] **Step 2: Open in browser and verify hero** ```bash open site/index.html ``` Expected: dark background, gold wordmark, large serif headline, parchment-colored subhead, gold CTA button. Fonts may not load from `file://` — if Cinzel/Alegreya don't appear, use a local server: ```bash npx serve site -l 4000 # then open http://localhost:4000 ``` - [ ] **Step 3: Commit** ```bash git add site/index.html git commit -m "feat: add brochure hero section" ``` --- ## Task 3: Feature rows **Files:** - Modify: `site/index.html` - [ ] **Step 1: Add features intro and all 7 feature rows** Replace `` with: ```html

Built for Shadowdark. Designed for the whole party.

Darkwatch character sheet showing stats, gear, and talents
Characters

Every stat, always in sync.

Full Shadowdark character sheets — STR, DEX, CON, INT, WIS, CHA, HP, AC, XP, gear, and talents. Click any value to edit it. Changes sync to the DM and every player at the table in real time.

Character creation wizard showing 3d6 stat rolling
Character Creation

Roll to begin.

A guided four-step wizard: name, class, and ancestry — then roll 3d6 for each stat and watch them land. Background, alignment, and deity last. HP, gear slots, gold, and your title are all derived automatically.

3D dice rolling with character-colored dice and shared roll log
Dice Rolling

3D dice. Shared results. No hiding rolls.

Dice roll in 3D, in your character's color. Results are determined server-side — no one can fudge them. Every roll appears in a shared log that the whole table sees at the same time.

Spellcasting panel with spell slots and exhaustion tracking
Spellcasting

Cast, fail, and suffer — together.

34 Tier 1–2 spells for Wizard and Priest. Every cast rolls a d20 — fail and lose a spell slot. Wizards risk the mishap table. Priests do penance. Rest to recover. The DM sees your spell focus state at a glance.

Atmospheric fog effect covering the campaign view
Atmosphere

Set the scene.

The DM controls atmosphere effects that every player sees in real time: creeping fog, crackling fire, rain, or drifting embers. One click and the whole table feels the dungeon shift.

Initiative tracker showing party vs enemies with round counter
Initiative Tracker

Shadowdark initiative, handled.

Everyone rolls. The tracker takes the party's highest result against the DM's single enemy roll. Whoever wins goes first — then the DM clicks Next Turn and the round counter climbs. Enemy HP is visible to the DM only.

DM view showing compact character cards for the whole party
DM View

The whole party, at a glance.

The DM sees every character in a compact grid: HP, AC, luck token, torch timer, and all six stat modifiers — without opening a single sheet. Drill in when you need to, stay out when you don't.

🕯

Torch Timer

A real 60-minute countdown, synced across every device. The DM starts it when the torch is lit. Everyone watches it burn.

Luck Tokens

Each character has a luck token the DM and player can both see and toggle. No more forgetting who spent their luck last session.

``` - [ ] **Step 2: Open in browser and verify feature rows** ```bash npx serve site -l 4000 # open http://localhost:4000 ``` Expected: seven alternating rows with broken image icons (screenshots don't exist yet) and text descriptions. Parchment background. Callout cards at the bottom. Layout alternates left/right correctly. - [ ] **Step 3: Commit** ```bash git add site/index.html git commit -m "feat: add brochure feature rows" ``` --- ## Task 4: Lower sections — system support, publisher, footer **Files:** - Modify: `site/index.html` - [ ] **Step 1: Replace the `` comment with the remaining sections** Replace `` with: ```html
System Support

Built for Shadowdark RPG. More systems coming.

Currently: Shadowdark RPG  ·  Next: based on demand

For game publishers

"We're not here to sell your rules."

Darkwatch is a session companion — a torch timer, a dice roller, an initiative tracker. Your players still need the book. We just want to make the table more fun. If you'd like to talk, we'd love to.

Get in touch
``` - [ ] **Step 2: Open in browser and verify complete page** ```bash npx serve site -l 4000 # open http://localhost:4000 ``` Scroll through the full page. Expected: - Dark hero → parchment features → dark system strip → parchment publisher → dark footer - Publisher section has the "Get in touch" button - Footer has Discord and GitHub links (both currently `PLACEHOLDER`) - [ ] **Step 3: Commit** ```bash git add site/index.html git commit -m "feat: add brochure system support, publisher, and footer sections" ``` --- ## Task 5: main.js — smooth scroll and hero parallax **Files:** - Create: `site/main.js` - [ ] **Step 1: Create `site/main.js`** ```javascript // Smooth scroll for anchor links (CSS scroll-behavior covers most cases, // this handles the ↓ scroll-hint click on browsers that need it) document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', e => { const target = document.querySelector(anchor.getAttribute('href')); if (target) { e.preventDefault(); target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // Subtle parallax on hero background glow const hero = document.querySelector('.hero'); if (hero) { window.addEventListener('scroll', () => { const scrolled = window.scrollY; const heroHeight = hero.offsetHeight; if (scrolled < heroHeight) { // Move the pseudo-element glow upward slightly as user scrolls hero.style.setProperty('--parallax-offset', `${scrolled * 0.3}px`); } }, { passive: true }); } ``` - [ ] **Step 2: Wire the parallax offset into CSS** In `site/style.css`, update the `.hero::before` rule to use the variable: Find: ```css .hero::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse 80% 60% at 50% 40%, rgba(201,168,76,0.07) 0%, transparent 70%); pointer-events: none; } ``` Replace with: ```css .hero::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse 80% 60% at 50% calc(40% - var(--parallax-offset, 0px)), rgba(201,168,76,0.07) 0%, transparent 70%); pointer-events: none; } ``` - [ ] **Step 3: Verify in browser** ```bash npx serve site -l 4000 # open http://localhost:4000 ``` Scroll slowly down from the hero. Expected: the gold glow in the hero drifts upward subtly as you scroll. Effect is gentle — if it's too strong, reduce `0.3` multiplier in `main.js`. - [ ] **Step 4: Commit** ```bash git add site/main.js site/style.css git commit -m "feat: add smooth scroll and hero parallax" ``` --- ## Task 6: Playwright screenshot script **Files:** - Create: `site/package.json` - Create: `site/screenshots.js` - Modify: `.gitignore` (add `site/node_modules/`) - [ ] **Step 1: Add `site/node_modules/` to .gitignore** Append to `.gitignore`: ``` site/node_modules/ ``` - [ ] **Step 2: Create `site/package.json`** ```json { "name": "darkwatch-brochure-screenshots", "type": "module", "scripts": { "screenshots": "node screenshots.js", "screenshots:only": "node screenshots.js --only" }, "devDependencies": { "playwright": "^1.43.0" } } ``` - [ ] **Step 3: Install Playwright and its Chromium browser** ```bash cd site npm install npx playwright install chromium cd .. ``` Expected: `node_modules/` created in `site/`, Chromium downloaded. - [ ] **Step 4: Create `site/screenshots.js`** ```javascript import { chromium } from 'playwright'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; import { mkdirSync } from 'fs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const SHOT_DIR = resolve(__dirname, 'assets', 'screenshots'); const APP = 'http://localhost:5173'; const DM_EMAIL = 'dm@darkwatch.test'; const DM_PASS = 'password'; const ONLY = process.argv.includes('--only') ? process.argv[process.argv.indexOf('--only') + 1] : null; mkdirSync(SHOT_DIR, { recursive: true }); // ── helpers ──────────────────────────────────────────────────────────────── async function shot(page, name, fn) { if (ONLY && name !== ONLY) return; console.log(` capturing ${name}...`); await fn(page); await page.screenshot({ path: resolve(SHOT_DIR, `${name}.png`), fullPage: false, }); console.log(` ✓ ${name}.png`); } async function login(page) { await page.goto(`${APP}/login`); await page.fill('input[type="email"]', DM_EMAIL); await page.fill('input[type="password"]', DM_PASS); await page.click('button[type="submit"]'); await page.waitForURL(`${APP}/`); } async function openCampaign(page) { // Seeded campaign is named "Tomb of the Serpent King" await page.click('text=Tomb of the Serpent King'); await page.waitForURL(/\/campaign\/\d+/); await page.waitForTimeout(1500); // socket connect + data load } // ── captures ─────────────────────────────────────────────────────────────── async function captureCharacterSheet(page) { // DM view — click the first character card to open full sheet // Selector: first .characterCard element in the card grid // Adjust class name if different in the actual DOM const card = page.locator('[class*="characterCard"]').first(); await card.click(); await page.waitForTimeout(500); } async function captureCharacterCreation(page) { // Open character creation wizard and advance to step 2 (stat rolling) await page.click('button:has-text("New Character")'); await page.waitForTimeout(400); // Fill required fields on step 1 so Next is enabled const nameInput = page.locator('input[placeholder*="name" i]').first(); await nameInput.fill('Screenshot Hero'); // Advance to step 2 await page.click('button:has-text("Next")'); await page.waitForTimeout(400); } async function captureDiceRoll(page) { // Roll a d20 — find the d20 button in the dice panel await page.click('button:has-text("d20")'); await page.waitForTimeout(2000); // wait for 3D animation to settle } async function captureSpellcasting(page) { // Open the Spells tab/panel for the first Wizard or Priest character // The spell panel may be a tab within the character sheet await page.click('text=Spells'); await page.waitForTimeout(400); } async function captureAtmosphere(page) { // Open atmosphere panel and enable fog await page.click('button[title*="tmosphere" i], button:has-text("Atmosphere")'); await page.waitForTimeout(300); await page.click('button:has-text("Fog")'); await page.waitForTimeout(600); // wait for fog to render } async function captureInitiativeActive(page) { // Click the Combat button to open the start combat modal await page.click('button:has-text("Combat")'); await page.waitForTimeout(300); // Start combat with whatever defaults are present await page.click('button:has-text("Start Combat")'); await page.waitForTimeout(500); // Roll initiative for enemies (DM roll) const rollBtn = page.locator('button:has-text("Roll")').first(); await rollBtn.click(); await page.waitForTimeout(400); } async function captureDmCards(page) { // The DM compact card grid is the default campaign view. // Navigate back to campaign root to ensure no modals are open. const url = page.url(); await page.goto(url); await page.waitForTimeout(1500); // let socket reconnect } // ── main ─────────────────────────────────────────────────────────────────── async function run() { console.log('Starting Darkwatch screenshot capture...'); console.log(`App: ${APP}`); if (ONLY) console.log(`Only: ${ONLY}`); console.log(''); const browser = await chromium.launch(); const context = await browser.newContext({ viewport: { width: 1280, height: 800 }, }); const page = await context.newPage(); await login(page); console.log('✓ Logged in as DM'); await openCampaign(page); console.log('✓ Campaign open'); console.log(''); await shot(page, 'dm-cards', captureDmCards); await shot(page, 'character-sheet', captureCharacterSheet); await shot(page, 'character-creation',captureCharacterCreation); await shot(page, 'dice-roll', captureDiceRoll); await shot(page, 'spellcasting', captureSpellcasting); await shot(page, 'atmosphere', captureAtmosphere); await shot(page, 'initiative-active', captureInitiativeActive); await browser.close(); console.log(''); console.log('Done! Screenshots saved to site/assets/screenshots/'); } run().catch(err => { console.error(err); process.exit(1); }); ``` - [ ] **Step 5: Run the script against the running dev server** Ensure the dev server is running first (`npm run dev` in `server/` and `client/`). Then: ```bash cd site node screenshots.js ``` Expected output: ``` Starting Darkwatch screenshot capture... App: http://localhost:5173 ✓ Logged in as DM ✓ Campaign open capturing dm-cards... ✓ dm-cards.png capturing character-sheet... ... Done! Screenshots saved to site/assets/screenshots/ ``` **If a capture fails:** The most likely cause is a selector mismatch. Open Playwright in headed mode to debug: ```javascript // Temporarily change launch() to: const browser = await chromium.launch({ headless: false, slowMo: 500 }); ``` Use browser DevTools to find the correct selector, update the relevant `capture*` function, then switch back to headless. - [ ] **Step 6: Verify screenshots look good** ```bash open site/assets/screenshots/ ``` Open each PNG and verify: - `dm-cards.png` — compact DM character card grid, 2+ cards visible - `character-sheet.png` — full character stats visible - `character-creation.png` — stat rolling step, 3d6 dice area visible - `dice-roll.png` — dice visible (mid-roll or result), roll log visible - `spellcasting.png` — spell list with slot indicators - `atmosphere.png` — fog or fire effect clearly visible over the UI - `initiative-active.png` — tracker showing two sides, round counter If any screenshot is blank, too dark, or shows the wrong view, debug with headed mode (Step 5 note) and re-run `node screenshots.js --only `. - [ ] **Step 7: Commit** ```bash git add site/package.json site/screenshots.js .gitignore site/assets/screenshots/ git commit -m "feat: add Playwright screenshot script for brochure" ``` Note: `site/node_modules/` is gitignored. The PNG screenshots are committed so the site renders without running the script. --- ## Task 7: update-brochure skill + CLAUDE.md **Files:** - Create: `.claude/skills/update-brochure.md` - Modify: `CLAUDE.md` - [ ] **Step 1: Create `.claude/skills/update-brochure.md`** ```markdown # update-brochure Use this skill when a Darkwatch feature is added, changed, or removed, and the brochure needs updating. It keeps `site/index.html` and `site/screenshots.js` in sync with the app. ## Scope - `site/index.html` — add, update, or remove feature rows - `site/screenshots.js` — add, update, or remove capture blocks - System support strip in `site/index.html` — update when a new RPG system is added **Not in scope:** Hero copy, publisher section copy, footer links. Those are editorial and manual. ## Process: Feature added 1. Add a new `.feature-row` block to `site/index.html` after the last existing feature row (before the `.callout-row`). Follow the alternating pattern: - Rows 1, 3, 5, 7 (odd positions): screenshot on the left, text on the right (no `reverse` class) - Rows 2, 4, 6 (even positions): `class="feature-row reverse"` (screenshot right, text left) - Count existing rows to determine whether to add `reverse` or not Template for a new row: ```html
Description of screenshot
Short Label

Punchy headline.

Two sentences. Write for players and DMs, not developers.

``` 2. Add a capture block to `site/screenshots.js`: - Write a `captureFeatureName(page)` async function that navigates to the right UI state - Add `await shot(page, 'feature-name', captureFeatureName);` to the `run()` function, in the same order as the HTML row 3. Run the screenshot script for the new capture: ```bash cd site && node screenshots.js --only feature-name ``` 4. Commit all three files: ```bash git add site/index.html site/screenshots.js site/assets/screenshots/feature-name.png git commit -m "docs: add [feature name] to brochure" ``` ## Process: Feature removed 1. Delete the feature's `.feature-row` block from `site/index.html` 2. Recheck alternating pattern — renumber remaining rows if needed 3. Delete the `capture*` function and `shot()` call from `site/screenshots.js` 4. Delete the PNG from `site/assets/screenshots/` 5. Commit ## Process: Feature changed (copy update only) 1. Update the heading and description in the relevant `.feature-row` in `site/index.html` 2. Re-run the screenshot if the UI changed visually: ```bash cd site && node screenshots.js --only feature-name ``` 3. Commit ## Process: New RPG system added 1. Update the system support strip in `site/index.html`: ```html

Currently: Shadowdark RPG  ·  Next: based on demand

Currently: Shadowdark RPG  ·  Cairn  ·  More based on demand

``` 2. Commit ## Tone guidelines Write feature descriptions for players and DMs, not developers. - ✓ "Everyone rolls. The tracker takes the party's highest result." - ✗ "The server computes max(party_rolls) and compares to the enemy roll." Keep headings punchy and short. Keep descriptions to two sentences max. ``` - [ ] **Step 2: Read current CLAUDE.md to find the Project Skills table** ```bash grep -n "update-changelog\|update-handbook\|Project Skills" CLAUDE.md ``` - [ ] **Step 3: Add update-brochure to the Project Skills table in CLAUDE.md** Find the existing table: ```markdown | `update-changelog` | `.claude/skills/update-changelog.md` | After a feature ships, bug is fixed, or feature is removed | | `update-handbook` | `.claude/skills/update-handbook.md` | After a feature is added, changed, or removed | ``` Add a row: ```markdown | `update-changelog` | `.claude/skills/update-changelog.md` | After a feature ships, bug is fixed, or feature is removed | | `update-handbook` | `.claude/skills/update-handbook.md` | After a feature is added, changed, or removed | | `update-brochure` | `.claude/skills/update-brochure.md` | After a feature is added, changed, or removed — keeps the site current | ``` - [ ] **Step 4: Commit** ```bash git add .claude/skills/update-brochure.md CLAUDE.md git commit -m "feat: add update-brochure skill and register in CLAUDE.md" ``` --- ## Self-Review Checklist After all tasks complete, verify against the spec: - [ ] `site/index.html` has all 5 sections: hero, features (7 rows + callout cards), system support, publisher, footer - [ ] Feature rows alternate left/right correctly (1,3,5,7 image-left; 2,4,6 image-right) - [ ] All 7 screenshot `img` tags reference `assets/screenshots/*.png` - [ ] Hero CTA links to Discord (placeholder is acceptable pre-launch) - [ ] Publisher section uses `mailto:email@email.com` placeholder - [ ] `site/screenshots.js` has a named capture function for each of the 7 PNGs - [ ] `--only` flag works: `node screenshots.js --only dm-cards` captures only that one - [ ] `.claude/skills/update-brochure.md` covers add, remove, change, and new-system processes - [ ] `CLAUDE.md` lists `update-brochure` in the Project Skills table - [ ] `site/node_modules/` is gitignored - [ ] Screenshots are committed to `site/assets/screenshots/`