# Examples Page Overdrive 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:** Bring `landing/examples/index.html` into the same execution-pointer visual language as the redesigned landing page so the `/` → `/examples/` transition feels like one site, not two.

**Architecture:** The examples page is statically generated by `scripts/build_examples_gallery.py`. **All redesign work edits that Python generator's f-string templates and Python constants — never the generated HTML directly.** After each edit, regenerate the HTML and visually verify in a browser. The redesign duplicates the landing page's execution-pointer atoms (`.exec-dot`, `.exec-caret`, `.exec-scan`, tokens like `--exec-color`, `--exec-pulse-dur`, `@keyframes exec-pulse / exec-blink / exec-stamp`) into the examples page's inline `<style>` block to match the landing page's "single self-contained HTML" architecture. The redesign covers six sections: nav dot, terminal-session header, category rail, search row, `ls -la` card rows, and `$ cat` card expansion.

**Tech Stack:** Python 3.9+ (the generator), vanilla HTML/CSS/JS (the output), no new dependencies, no new build steps. Reference spec: `docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md`.

---

## Pre-flight: file inventory and conventions

Before starting any task, the implementer should know these absolute facts:

**Generator source** (where ALL edits land):
- `scripts/build_examples_gallery.py` — 327 lines, single file. The docstring at the top says `> site/examples/index.html` but **the real output path is `landing/examples/index.html`**. The docstring is stale; ignore it.

**Generator regeneration command:**
```bash
python scripts/build_examples_gallery.py > landing/examples/index.html
```

Run this after every task that changes the generator. Verify the file size is ~600KB (it bundles all 88 example sources inline), and verify line count is ~180.

**Key locations inside `scripts/build_examples_gallery.py`** (current line numbers — they will shift as edits land, so re-grep if uncertain):

| What | Generator lines | Generated HTML lines |
|---|---|---|
| `:root` CSS vars | `247` (the long `:root{{...}}` line) | `13` |
| `nav` markup | `288-291` | `54-57` |
| `.ph` page header | `292` | `58` |
| `.ct` filter container | `293-297` | `59-80` |
| `.cb` chip row template | `295` (consumes `cat_btns` from `178-183`) | `61-78` |
| `.el` card list | `298-300` (consumes `cards` from `189-233`) | `81+` |
| Inline `<script>` | `301-309` | `171-180` |
| `extract_metadata()` | `65-154` | n/a |
| `build_gallery()` | `172-311` | n/a |
| `main()` | `314-322` | n/a |

**Reference spec:** `docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md`. Re-read its §1–§6 before starting the matching task. The spec is the source of truth for visuals; this plan is the source of truth for sequence and code.

**Landing page atom source** (for copy-paste reference):
- Tokens: `landing/index.html:298-311`
- `.exec-dot` / `.exec-caret` / `.exec-scan`: `landing/index.html:2553-2616`
- `@keyframes exec-pulse / exec-blink / exec-scan-sweep / exec-stamp`: `landing/index.html:2568-2624`

**Security note about existing code.** The generator's existing `toggle()` function uses a `.innerHTML` assignment to render syntax-highlighted source into a `<pre>` element. The highlighter function `hl()` escapes `&`, `<`, and `>` before any replacement. The `SRC` data source is the repository's own `examples/*.py` files read at build time — not user input. This pre-existing pattern is not modified by this plan; the only change to `toggle()` is an `aria-expanded` attribute sync (Step 6.3), which is a surgical single-line addition, not a rewrite of the rendering path.

**Test loop convention** for every task:
1. Edit the generator.
2. Regenerate: `python scripts/build_examples_gallery.py > landing/examples/index.html`
3. Open the regenerated file in a browser (locally: `open landing/examples/index.html` on macOS or use a static server).
4. Open the browser DevTools console — confirm zero errors.
5. Smoke-test the affected behavior (specified per task).
6. Commit.

**Commit message convention** (matches recent project history):
- `feat(examples): <what>` for new features
- `style(examples): <what>` for CSS-only changes
- `refactor(examples): <what>` for restructuring with no visual change

---

## Task 1: Duplicate execution-pointer atoms into the generator

**Goal:** Add the shared design tokens, `.exec-dot`, `.exec-caret`, `.exec-scan`, `.sr-only`, and the `@keyframes` from the landing page into the examples generator's inline `<style>` block. No visual change yet — this just makes the atoms available for later tasks. Verifies that adding ~80 lines of CSS to the generator does not break regeneration.

**Files:**
- Modify: `scripts/build_examples_gallery.py:247` (the long `:root{{...}}` line and the surrounding `<style>` block)

- [ ] **Step 1.1: Read the current `:root` line and the lines immediately following**

Read `scripts/build_examples_gallery.py:245-285`. This is the inline `<style>` block. The `:root` CSS variables are on a single very long line. The atoms must be added directly after the existing `:root{{...}}` line, before the `html{{...}}` rule.

- [ ] **Step 1.2: Add execution-pointer tokens to `:root`**

In `scripts/build_examples_gallery.py`, locate the long `:root{{...}}` line at line 247. It currently ends with `...opacity='0.018'/%3E%3C/svg%3E\")}}`. Add the new tokens **inside** the `:root` braces, just before the closing `}}`. Note that this is a Python f-string so single braces become `{{` and `}}`.

The new content to insert (verbatim, before the closing `}}` of `:root`):

```
--exec-color:#22d3ee;--exec-glow:rgba(34,211,238,0.55);--exec-glow-soft:rgba(34,211,238,0.18);--exec-pulse-dur:1.6s;--exec-step-dur:0.55s;--exec-ease-step:cubic-bezier(0.4,0,0.2,1);--exec-ease-soft:cubic-bezier(0.16,1,0.3,1);--exec-blink-dur:1.05s
```

After the edit, the line should still be one single line and still end with `}}`.

- [ ] **Step 1.3: Add execution-pointer atoms after the existing CSS rules**

In `scripts/build_examples_gallery.py`, locate line 284 (the `@media(max-width:640px){{...}}` rule — the last CSS rule before the closing `</style>`). Insert these new lines AFTER line 284 and BEFORE line 285 (`  </style>`).

The new lines (each one is one CSS rule on its own line — match the existing minified style):

```
.sr-only{{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}}
.exec-dot{{display:inline-block;width:8px;height:8px;border-radius:999px;background:var(--exec-color);box-shadow:0 0 0 0 var(--exec-glow);animation:exec-pulse var(--exec-pulse-dur) var(--exec-ease-soft) infinite;vertical-align:middle}}
.exec-dot--lg{{width:10px;height:10px}}
.exec-dot--sm{{width:6px;height:6px}}
@keyframes exec-pulse{{0%{{box-shadow:0 0 0 0 var(--exec-glow)}}60%{{box-shadow:0 0 0 8px rgba(34,211,238,0)}}100%{{box-shadow:0 0 0 0 rgba(34,211,238,0)}}}}
.exec-caret{{display:inline-block;width:0.55em;height:1.1em;vertical-align:text-bottom;background:var(--exec-color);box-shadow:0 0 6px var(--exec-glow);animation:exec-blink var(--exec-blink-dur) steps(2,jump-none) infinite;margin-left:2px}}
.exec-caret--thin{{width:2px;box-shadow:0 0 4px var(--exec-glow-soft)}}
@keyframes exec-blink{{0%,49%{{opacity:1}}50%,100%{{opacity:0}}}}
.exec-scan{{position:relative;overflow:hidden}}
.exec-scan.in-view::after{{content:"";position:absolute;top:0;left:-25%;width:25%;height:100%;background:linear-gradient(90deg,rgba(34,211,238,0) 0%,rgba(34,211,238,0.18) 40%,rgba(34,211,238,0.55) 50%,rgba(34,211,238,0.18) 60%,rgba(34,211,238,0) 100%);pointer-events:none;animation:exec-scan-sweep 1.4s var(--exec-ease-step) 0.2s 1 forwards}}
@keyframes exec-scan-sweep{{0%{{transform:translateX(0)}}100%{{transform:translateX(520%)}}}}
@keyframes exec-stamp{{0%{{transform:scale(0.92);box-shadow:0 0 0 0 var(--exec-glow)}}40%{{transform:scale(1.02);box-shadow:0 0 0 6px var(--exec-glow-soft)}}100%{{transform:scale(1);box-shadow:0 0 0 1px rgba(34,211,238,0.18)}}}}
@media(prefers-reduced-motion:reduce){{.exec-dot{{animation:none;box-shadow:0 0 6px var(--exec-glow)}}.exec-caret{{animation:none;opacity:1}}.exec-scan.in-view::after{{animation:none;display:none}}}}
```

These rules duplicate the landing page atoms verbatim, with single braces escaped as double braces for the Python f-string.

- [ ] **Step 1.4: Regenerate the HTML and verify it parses**

Run:
```bash
python scripts/build_examples_gallery.py > landing/examples/index.html
```

Expected: command exits with code 0, no Python tracebacks. The output file is approximately 600KB.

- [ ] **Step 1.5: Verify in browser**

Open `landing/examples/index.html` in a browser. Open DevTools console.

Expected:
- Page renders identically to before (no visual change yet — the new classes are not used by any element).
- DevTools console shows zero errors and zero warnings.
- DevTools Elements panel: searching for `--exec-color` in the inline `<style>` finds the new token.

- [ ] **Step 1.6: Commit**

```bash
git add scripts/build_examples_gallery.py landing/examples/index.html
git commit -m "$(cat <<'EOF'
refactor(examples): import execution-pointer atoms from landing

Duplicate the landing page's design tokens, .exec-dot, .exec-caret,
.exec-scan, .sr-only, @keyframes exec-pulse/exec-blink/exec-scan-sweep/
exec-stamp, and prefers-reduced-motion fallbacks into the examples page
generator's inline <style>. No visual change yet — these atoms become
the foundation for the §1–§6 redesign in subsequent commits.

Spec: docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md
EOF
)"
```

---

## Task 2: Add `.exec-dot` to the nav brand (§6)

**Goal:** Add a permanent pulsing cyan dot to the left of the `selectools` wordmark in the nav. This is the smallest visible diff and is the single most important cross-page coherence signal.

**Files:**
- Modify: `scripts/build_examples_gallery.py:289` (the `<a class="nl">` element)

- [ ] **Step 2.1: Locate the nav brand line in the generator**

Read `scripts/build_examples_gallery.py:288-291`. The relevant line is:

```python
  <a href="../" class="nl">selectools <span>examples</span></a>
```

- [ ] **Step 2.2: Insert the `.exec-dot` span before the wordmark**

Edit line 289 to:

```python
  <a href="../" class="nl"><span class="exec-dot"></span>&nbsp;selectools <span>examples</span></a>
```

The `&nbsp;` provides spacing between the dot and the wordmark. The dot will pulse continuously via the `@keyframes exec-pulse` from Task 1.

- [ ] **Step 2.3: Regenerate the HTML**

Run:
```bash
python scripts/build_examples_gallery.py > landing/examples/index.html
```

Expected: exits 0, file ~600KB.

- [ ] **Step 2.4: Verify in browser**

Open `landing/examples/index.html` in a browser at desktop width.

Expected:
- A small cyan dot appears immediately to the left of "selectools" in the top-left nav.
- The dot visibly pulses on a ~1.6s loop with a glow effect.
- The wordmark text is unchanged.
- DevTools console: zero errors.

In a second browser window or tab, open `landing/index.html` (the main landing page). Confirm the same dot appears in the same position. The two pages should now feel like the same site at first glance.

- [ ] **Step 2.5: Verify reduced-motion fallback**

In DevTools, open Rendering tab → Emulate CSS media feature `prefers-reduced-motion: reduce`. Reload.

Expected: the dot is still visible and glowing but does NOT pulse (no animation).

- [ ] **Step 2.6: Commit**

```bash
git add scripts/build_examples_gallery.py landing/examples/index.html
git commit -m "$(cat <<'EOF'
feat(examples): add pulsing exec-dot to nav brand (§6)

Adds a permanent cyan execution-pointer dot to the left of the
selectools wordmark in the examples page nav. Matches the landing
page's wordmark variant 1 — a user clicking between / and /examples/
now sees the same pulse in the same place.

Respects prefers-reduced-motion (becomes a static glow).

Spec §6: docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md
EOF
)"
```

---

## Task 3: Replace `.ph` with the terminal-session header (§1)

**Goal:** Replace the current `<h1>88 Example Scripts</h1>` block with a terminal-window panel that types `ls examples/` on page load and live-mirrors the search and category state into the prompt suffix.

**Files:**
- Modify: `scripts/build_examples_gallery.py:254-255` (the `.ph` CSS rules)
- Modify: `scripts/build_examples_gallery.py:292` (the `.ph` markup template)
- Modify: `scripts/build_examples_gallery.py:301-309` (the inline `<script>` — add `typeLine()` and `syncPrompt()`)
- Modify: `scripts/build_examples_gallery.py:304` (the `flt()` body — update counter format)

- [ ] **Step 3.1: Replace the `.ph` CSS with `.ex-term` CSS**

In `scripts/build_examples_gallery.py`, locate lines 254-255:

```
.ph{{max-width:960px;margin:0 auto;padding:48px 20px 24px}}
.ph h1{{font-size:28px;letter-spacing:-0.03em;margin-bottom:8px;font-weight:800}}.ph p{{color:var(--dm);font-size:15px;max-width:600px;line-height:1.6}}
```

Replace those two lines with:

```
.ex-term{{max-width:960px;margin:32px auto 24px;background:#0b1220;border:1px solid var(--bd);border-radius:14px;box-shadow:0 20px 60px -28px rgba(0,0,0,0.55),0 0 0 1px rgba(34,211,238,0.05);overflow:hidden}}
.ex-term__bar{{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--bd);background:rgba(15,23,42,0.7)}}
.ex-term__dot{{width:11px;height:11px;border-radius:999px}}
.ex-term__dot--r{{background:rgba(239,68,68,0.85)}}
.ex-term__dot--y{{background:rgba(250,204,21,0.85)}}
.ex-term__dot--g{{background:rgba(34,197,94,0.85)}}
.ex-term__name{{margin-left:8px;font-family:var(--mono);font-size:12px;color:var(--ft)}}
.ex-term__shell{{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--ft);letter-spacing:0.08em}}
.ex-term__body{{padding:22px 22px 24px;font-family:var(--mono)}}
.ex-prompt{{font-family:var(--mono);font-size:13px;line-height:1.75;white-space:pre;overflow-x:auto}}
.ex-prompt__user{{color:var(--cy)}}
.ex-prompt__at{{color:var(--ft)}}
.ex-prompt__host{{color:var(--cy)}}
.ex-prompt__colon{{color:var(--ft)}}
.ex-prompt__path{{color:#fbbf24}}
.ex-prompt__glyph{{color:var(--gn);margin:0 6px}}
.ex-prompt__cmd{{color:var(--tx)}}
.ex-prompt__flags{{color:#fbbf24}}
.ex-prompt__grep{{color:var(--ft)}}
.ex-subtitle{{margin-top:14px;font-family:var(--font);font-size:14px;color:var(--dm);max-width:600px;line-height:1.6}}
@media(max-width:640px){{.ex-prompt__user,.ex-prompt__at,.ex-prompt__host,.ex-prompt__colon,.ex-prompt__path{{display:none}}.ex-prompt__glyph{{margin-left:0}}}}
@media(prefers-reduced-motion:reduce){{.ex-prompt .exec-caret{{animation:none;opacity:1}}}}
```

- [ ] **Step 3.2: Replace the `.ph` markup template**

In `scripts/build_examples_gallery.py`, locate line 292:

```python
<div class="ph"><h1>{total} Example Scripts</h1><p>Runnable Python examples covering agents, RAG, multi-agent graphs, evals, streaming, guardrails, and more. {no_key} run without an API key.</p></div>
```

Replace it with this multi-line template (mind the f-string braces):

```python
<header class="ex-term">
  <div class="ex-term__bar">
    <span class="ex-term__dot ex-term__dot--r"></span>
    <span class="ex-term__dot ex-term__dot--y"></span>
    <span class="ex-term__dot ex-term__dot--g"></span>
    <span class="ex-term__name">~/selectools/examples</span>
    <span class="ex-term__shell">zsh</span>
  </div>
  <div class="ex-term__body">
    <div class="ex-prompt" aria-hidden="true"><span class="ex-prompt__user">selectools</span><span class="ex-prompt__at">@</span><span class="ex-prompt__host">examples.dev</span><span class="ex-prompt__colon">:</span><span class="ex-prompt__path">~/selectools/examples</span><span class="ex-prompt__glyph">$</span><span class="ex-prompt__cmd" id="ex-cmd"></span><span class="ex-prompt__flags" id="ex-flags"></span><span class="ex-prompt__grep" id="ex-grep"></span><span class="exec-caret"></span></div>
    <h1 class="sr-only">Selectools examples — {total} runnable Python scripts</h1>
    <p class="ex-subtitle">{total} runnable scripts covering agents, RAG, multi-agent graphs, evals, streaming, and guardrails. {no_key} run without an API key.</p>
  </div>
</header>
```

Notice: the `ex-prompt__cmd` span is now empty — `id="ex-cmd"` will be filled by the type-on routine on page load. The flags and grep spans are also empty and will be filled by `syncPrompt()`.

- [ ] **Step 3.3: Update the `flt()` body to the new counter format**

In `scripts/build_examples_gallery.py`, locate the `flt()` function inside the inline `<script>` at line 304. Its final statement currently ends with:

```
document.getElementById('rc').textContent=c+' example'+(c!==1?'s':'')
```

Replace that single statement with:

```
document.getElementById('rc').textContent='# '+c+' files match'
```

Do not modify any other part of `flt()`. Only the counter text format changes.

- [ ] **Step 3.4: Add `typeLine()`, `syncPrompt()`, and `bootPrompt()` to the inline `<script>`**

In `scripts/build_examples_gallery.py`, locate the script block around lines 301-309. After the existing `function cpSrc(...)` line (around line 308) and BEFORE the closing `</script>` tag, add this block on three new lines:

```
function syncPrompt(){{const q=document.getElementById('si').value;document.getElementById('ex-grep').textContent=q?' | grep -i '+q:'';document.getElementById('ex-flags').textContent=ac==='all'?'':' --tags '+ac}}
function typeLine(target,text,perChar,done){{let i=0;const tick=()=>{{if(i<=text.length){{target.textContent=text.slice(0,i);i++;setTimeout(tick,perChar)}}else if(done){{done()}}}};tick()}}
(function bootPrompt(){{const cmd=document.getElementById('ex-cmd');if(!cmd)return;const reduced=window.matchMedia('(prefers-reduced-motion: reduce)').matches;if(reduced){{cmd.textContent='ls examples/';syncPrompt();return}}typeLine(cmd,'ls examples/',35,syncPrompt)}})();
```

Note that `syncPrompt()` writes to `.textContent` only — it does not touch any HTML rendering path. The `typeLine()` helper also writes only to `.textContent`. Both are safe text-only DOM updates.

- [ ] **Step 3.5: Wire `syncPrompt()` to the search input's `oninput`**

In `scripts/build_examples_gallery.py`, locate line 294:

```python
  <input class="si" type="text" placeholder="Search examples\u2026" oninput="flt()" id="si" />
```

Replace `oninput="flt()"` with `oninput="flt();syncPrompt()"`. The full line becomes:

```python
  <input class="si" type="text" placeholder="Search examples\u2026" oninput="flt();syncPrompt()" id="si" />
```

- [ ] **Step 3.6: Regenerate the HTML**

Run:
```bash
python scripts/build_examples_gallery.py > landing/examples/index.html
```

Expected: exits 0, file ~600KB.

- [ ] **Step 3.7: Verify in browser — type-on, live-mirror, mobile, reduced-motion**

Open `landing/examples/index.html` in a browser at desktop width.

Expected on page load:
- A terminal panel appears below the nav. It has a colored window-control bar (red/yellow/green dots) with `~/selectools/examples` and `zsh` labels.
- Inside the terminal body, the prompt `selectools@examples.dev:~/selectools/examples $ ` appears immediately, fully styled.
- After ~10ms, the cursor types `ls examples/` character-by-character at ~35ms per char (~420ms total).
- After typing finishes, a blinking cyan caret remains at the end of the line.
- Below the prompt, a paragraph reads "88 runnable scripts covering agents, RAG, multi-agent graphs, evals, streaming, and guardrails. 34 run without an API key."

Expected on interaction:
- Type `rag` into the search input. The terminal prompt updates live to show ` | grep -i rag` appended after `ls examples/`.
- Clear the search. The grep suffix disappears (no dangling pipe).
- The counter below the rail updates to `# 12 files match` while filtered, `# 88 files match` when cleared.

Mobile fallback test:
- In DevTools, switch to a 375×812 viewport (iPhone). Reload.
- Expected: the `selectools@examples.dev:~/selectools/examples` prefix is hidden. Only `$ ls examples/` and the caret are visible. Typing in search still updates the grep suffix.

Reduced-motion test:
- DevTools → Rendering → Emulate `prefers-reduced-motion: reduce`. Reload.
- Expected: the `ls examples/` text appears fully typed instantly (no character-by-character animation). The caret is visible but does not blink.

- [ ] **Step 3.8: Commit**

```bash
git add scripts/build_examples_gallery.py landing/examples/index.html
git commit -m "$(cat <<'EOF'
feat(examples): replace page header with terminal-session panel (§1)

Replaces the bare <h1> + paragraph with a full terminal-window panel
that types out 'ls examples/' on page load and live-mirrors the search
state into the prompt suffix as ' | grep -i <query>'.

Counter format changes from 'N examples' to '# N files match' to
match the monospace comment aesthetic.

The category --tags suffix wiring lands in Task 4 once the rail exists.

Adds typeLine() and syncPrompt() helpers and a bootPrompt() IIFE that
respects prefers-reduced-motion. Mobile collapses to '$ ls examples/'.
Both helpers write only to .textContent — no HTML rendering paths.

Spec §1: docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md
EOF
)"
```

---

## Task 4: Replace the chip row with the proportional-width category rail (§2)

**Goal:** Remove the 18 pill-shaped category buttons (`.cb`) and replace them with a single horizontal bar of segments whose widths are proportional to category counts. On viewport entry, the rail "stamps" each segment left-to-right in sequence.

**Files:**
- Modify: `scripts/build_examples_gallery.py:259-261` (the `.cr` and `.cb` CSS rules)
- Modify: `scripts/build_examples_gallery.py:178-183` (the `cat_btns` Python loop that builds the chip markup)
- Modify: `scripts/build_examples_gallery.py:295` (the f-string slot that emits `cat_btns`)
- Modify: `scripts/build_examples_gallery.py:305` (the inline JS chip click handler)

- [ ] **Step 4.1: Replace `.cr` and `.cb` CSS with `.ex-rail` CSS**

In `scripts/build_examples_gallery.py`, locate lines 259-261:

```
.cr{{display:flex;flex-wrap:wrap;gap:6px}}
.cb{{font-family:var(--font);font-size:12px;font-weight:500;padding:6px 14px;border-radius:100px;border:1px solid rgba(51,65,85,0.6);background:rgba(30,41,59,0.7);color:var(--dm);cursor:pointer;transition:all .15s;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}}
.cb:hover{{background:rgba(51,65,85,0.5);border-color:var(--dm);color:var(--tx)}}.cb.on{{background:rgba(34,211,238,0.12);border-color:rgba(34,211,238,0.35);color:var(--cy);box-shadow:0 0 12px rgba(34,211,238,0.08)}}
```

Replace those three lines with:

```
.ex-rail{{display:flex;gap:2px;height:40px;border-radius:8px;overflow:hidden;border:1px solid var(--bd);background:rgba(30,41,59,0.4)}}
.ex-rail__seg{{flex:var(--seg-weight,1) 1 0;min-width:56px;height:100%;display:flex;align-items:center;justify-content:center;gap:6px;font-family:var(--mono);font-size:12px;color:var(--dm);background:transparent;border:none;cursor:pointer;transition:background .15s,color .15s;position:relative;padding:0 8px;white-space:nowrap}}
.ex-rail__seg--all{{flex:0 0 72px}}
.ex-rail__seg:hover{{background:rgba(34,211,238,0.08);color:var(--tx)}}
.ex-rail__seg.on{{background:rgba(34,211,238,0.12);color:var(--cy);box-shadow:inset 0 -2px 0 var(--exec-color)}}
.ex-rail__name{{font-size:12px}}
.ex-rail__count{{font-size:11px;color:var(--cy);opacity:0.75}}
.ex-rail.in-view .ex-rail__seg{{animation:exec-stamp 0.6s var(--exec-ease-soft) both;animation-delay:calc(var(--seg-index,0) * 80ms)}}
@media(max-width:640px){{.ex-rail{{overflow-x:auto;-webkit-overflow-scrolling:touch;scroll-snap-type:x mandatory;height:44px}}.ex-rail__seg{{flex:0 0 auto;min-width:80px;scroll-snap-align:start}}}}
@media(prefers-reduced-motion:reduce){{.ex-rail.in-view .ex-rail__seg{{animation:none}}}}
```

- [ ] **Step 4.2: Rewrite the `cat_btns` builder loop in Python**

In `scripts/build_examples_gallery.py`, locate the existing `cat_btns` builder at lines 178-183:

```python
    cat_btns = [f'<button class="cb on" data-cat="all">All ({total})</button>']
    for c in all_cats:
        n = sum(1 for e in examples if c in e["categories"])
        icon = CAT_ICONS.get(c, "")
        label = c.replace("-", " ").title()
        cat_btns.append(f'<button class="cb" data-cat="{c}">{icon} {label} ({n})</button>')
```

Replace those six lines with:

```python
    rail_segs = [
        f'<button class="ex-rail__seg ex-rail__seg--all on" data-cat="all" '
        f'role="tab" aria-selected="true" style="--seg-index:0">'
        f'<span class="ex-rail__name">all</span>'
        f'<span class="ex-rail__count">{total}</span>'
        f'</button>'
    ]
    for idx, c in enumerate(all_cats, start=1):
        n = sum(1 for e in examples if c in e["categories"])
        rail_segs.append(
            f'<button class="ex-rail__seg" data-cat="{c}" role="tab" '
            f'aria-selected="false" style="--seg-weight:{n};--seg-index:{idx}">'
            f'<span class="ex-rail__name">{c}</span>'
            f'<span class="ex-rail__count">{n}</span>'
            f'</button>'
        )
```

Note: the variable is renamed from `cat_btns` to `rail_segs` to reflect what it now produces. The `CAT_ICONS` dictionary is no longer referenced — leave the dictionary in place (other code may still use it; do not delete it in this task).

- [ ] **Step 4.3: Update the f-string slot in the markup template**

In `scripts/build_examples_gallery.py`, locate line 295:

```python
  <div class="cr">{chr(10).join(cat_btns)}</div>
```

Replace it with:

```python
  <div class="ex-rail" id="ex-rail" role="tablist" aria-label="Filter examples by category">{chr(10).join(rail_segs)}</div>
```

- [ ] **Step 4.4: Replace the chip click handler in the inline `<script>`**

In `scripts/build_examples_gallery.py`, locate line 305:

```
document.querySelectorAll('.cb').forEach(b=>{{b.addEventListener('click',()=>{{document.querySelectorAll('.cb').forEach(x=>x.classList.remove('on'));b.classList.add('on');ac=b.dataset.cat;flt()}});}});
```

Replace it with:

```
document.querySelectorAll('.ex-rail__seg').forEach(b=>{{b.addEventListener('click',()=>{{document.querySelectorAll('.ex-rail__seg').forEach(x=>{{x.classList.remove('on');x.setAttribute('aria-selected','false')}});b.classList.add('on');b.setAttribute('aria-selected','true');ac=b.dataset.cat;b.style.animation='none';requestAnimationFrame(()=>{{b.style.animation='exec-stamp 0.6s var(--exec-ease-soft)'}});flt();syncPrompt()}});}});
(function(){{const r=document.getElementById('ex-rail');if(!r)return;const io=new IntersectionObserver((ents)=>{{ents.forEach(e=>{{if(e.isIntersecting){{r.classList.add('in-view');io.disconnect()}}}})}},{{rootMargin:'0px 0px -20% 0px'}});io.observe(r)}})();
```

The replacement does five things:
1. Selects `.ex-rail__seg` instead of `.cb`.
2. Maintains `aria-selected` correctly on click.
3. Re-runs `exec-stamp` on the clicked segment via animation reset trick (set `animation` to `none`, then on next frame set it back to `exec-stamp 0.6s var(--exec-ease-soft)`).
4. Calls `syncPrompt()` so the prompt's `--tags` flag updates.
5. Adds an `IntersectionObserver` (one-shot, disconnects after first trigger) that adds `.in-view` to the rail when it scrolls into view, triggering the staggered stamp sweep.

- [ ] **Step 4.5: Regenerate the HTML**

Run:
```bash
python scripts/build_examples_gallery.py > landing/examples/index.html
```

Expected: exits 0, file ~600KB.

- [ ] **Step 4.6: Verify in browser — sweep, click, prompt sync, mobile**

Open `landing/examples/index.html` in a browser at desktop width.

Expected on first view:
- The category rail appears below the search input as a single horizontal bar.
- The leftmost segment is a fixed-width "all 88" segment (active, cyan-tinted).
- 17 more segments to its right, sized proportionally to their category counts. `agent` (21) is the widest non-"all" segment; `audit` (1) is the narrowest (but still at least 56px wide).
- Within ~1.5s of the rail becoming visible, each segment "stamps" left-to-right with a brief scale + cyan glow (the `exec-stamp` keyframe), staggered by 80ms.

Expected on interaction:
- Click the `rag` segment. It becomes active (cyan tint + cyan inset bottom border), the previously-active segment loses its `on` state, and the segment briefly stamps again.
- The card list filters to RAG examples only.
- The terminal prompt above (from Task 3) now shows `$ ls examples/ --tags rag`.

Click `all`. The `--tags` suffix disappears from the prompt. The full list returns.

Type `rag` into the search box. The prompt shows `$ ls examples/ --tags rag | grep -i rag`. Clear the search; the grep suffix goes away but the `--tags rag` stays until you click `all`.

Mobile fallback:
- Switch to a 375×812 viewport. Reload.
- Expected: the rail is now horizontally scrollable. Each segment is at least 80px wide. Scroll-snapping engages on swipe.

Reduced-motion:
- Enable `prefers-reduced-motion`. Reload.
- Expected: the rail renders in its rest state immediately, no stamp sweep. Click handlers still work; clicked segments do NOT stamp on click.

- [ ] **Step 4.7: Commit**

```bash
git add scripts/build_examples_gallery.py landing/examples/index.html
git commit -m "$(cat <<'EOF'
feat(examples): replace chip row with proportional category rail (§2)

Removes the 18-pill .cb chip row and replaces it with a single bar
of .ex-rail__seg segments sized proportionally to each category's
count. Visually shows the shape of the catalog at a glance.

On viewport entry an IntersectionObserver triggers a left-to-right
stamp sweep (80ms stagger). Clicking a segment filters the list,
re-stamps the segment, and rewrites the terminal prompt's --tags suffix.

Mobile becomes a horizontal scroll-snap strip. Respects
prefers-reduced-motion (no sweep, no on-click stamp).

Spec §2: docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md
EOF
)"
```

---

## Task 5: Search input glyph, kbd hint, and counter format (§3)

**Goal:** Wrap the search input with a leading `⌕` glyph and a trailing `kbd` shortcut hint (`/`), wire the `/` key to focus the search, and confirm the result counter renders as `# N files match` (the `flt()` body was already updated in Task 3).

**Files:**
- Modify: `scripts/build_examples_gallery.py:256-258` (the `.ct` and `.si` CSS)
- Modify: `scripts/build_examples_gallery.py:262` (the `.rc` CSS)
- Modify: `scripts/build_examples_gallery.py:293-297` (the `.ct` markup template)
- Modify: `scripts/build_examples_gallery.py:301-309` (add `/` keydown handler to the `<script>`)

- [ ] **Step 5.1: Add `.ex-search` CSS rules**

In `scripts/build_examples_gallery.py`, locate lines 256-258:

```
.ct{{max-width:960px;margin:0 auto;padding:0 20px 16px;display:flex;flex-direction:column;gap:10px;position:sticky;top:52px;z-index:40;background:var(--bg);padding-top:10px}}
.si{{flex:1;background:var(--sf);border:1px solid var(--bd);border-radius:8px;padding:10px 14px;color:var(--tx);font-family:var(--font);font-size:14px;outline:none}}
.si:focus{{border-color:var(--cy);box-shadow:0 0 0 2px rgba(34,211,238,0.12)}}.si::placeholder{{color:var(--ft)}}
```

Add these new lines AFTER line 258 (do not delete the existing three lines — `.si` and `.ct` are still used):

```
.ex-search{{position:relative;display:flex;align-items:center}}
.ex-search__glyph{{position:absolute;left:14px;color:var(--ft);font-size:14px;pointer-events:none}}
.ex-search__kbd{{position:absolute;right:12px;font-family:var(--mono);font-size:11px;color:var(--ft);background:rgba(100,116,139,0.15);padding:2px 6px;border-radius:4px;pointer-events:none}}
.ex-search .si{{padding-left:36px;padding-right:36px}}
```

- [ ] **Step 5.2: Update the `.rc` counter style to use the mono comment color**

The existing `.rc` rule at line 262 already uses `font-family: var(--mono); font-size: 11px; color: var(--ft);` — no change needed. The counter text was already updated by the `flt()` body change in Task 3.

- [ ] **Step 5.3: Update the `.ct` markup template**

In `scripts/build_examples_gallery.py`, locate lines 293-297:

```python
<div class="ct">
  <input class="si" type="text" placeholder="Search examples\u2026" oninput="flt();syncPrompt()" id="si" />
  <div class="ex-rail" id="ex-rail" role="tablist" aria-label="Filter examples by category">{chr(10).join(rail_segs)}</div>
  <div class="rc" id="rc">{total} examples</div>
</div>
```

(Note: line 295 was already updated by Task 4 to emit the `.ex-rail`. Line 296 still emits the old text format `{total} examples`.)

Replace those five lines with:

```python
<div class="ct">
  <div class="ex-search">
    <span class="ex-search__glyph">⌕</span>
    <input class="si" type="text" placeholder="search by name or keyword\u2026" oninput="flt();syncPrompt()" id="si" autocomplete="off" />
    <kbd class="ex-search__kbd">/</kbd>
  </div>
  <div class="ex-rail" id="ex-rail" role="tablist" aria-label="Filter examples by category">{chr(10).join(rail_segs)}</div>
  <div class="rc" id="rc"># {total} files match</div>
</div>
```

Three changes: search input wrapped in `.ex-search`, placeholder text changed to lowercase `search by name or keyword…`, counter initial text format changed to `# {total} files match`.

- [ ] **Step 5.4: Add the `/` keydown handler to the inline `<script>`**

In `scripts/build_examples_gallery.py`, locate the script block. Just before the closing `</script>` tag (after the `bootPrompt` IIFE you added in Task 3), add:

```
document.addEventListener('keydown',(e)=>{{if(e.key!=='/')return;const t=e.target;if(t&&(t.tagName==='INPUT'||t.tagName==='TEXTAREA'||t.isContentEditable))return;e.preventDefault();const si=document.getElementById('si');if(si)si.focus()}});
```

The handler ignores `/` when typed inside any input/textarea/contenteditable element so it doesn't break normal typing.

- [ ] **Step 5.5: Regenerate the HTML**

Run:
```bash
python scripts/build_examples_gallery.py > landing/examples/index.html
```

Expected: exits 0, file ~600KB.

- [ ] **Step 5.6: Verify in browser — glyph, kbd, shortcut, counter format**

Open `landing/examples/index.html` in a browser at desktop width.

Expected:
- The search input now has a `⌕` glyph at the left and a small `/` keyboard hint pill at the right.
- The placeholder text is `search by name or keyword…`.
- Below the rail, the counter reads `# 88 files match`.
- Press `/` while focused outside any input. The search box gains focus immediately.
- Press `/` while typing inside the search box itself. The character `/` appears in the input (the shortcut does NOT fire).
- Type `rag`. The counter updates to `# 12 files match` (or however many RAG examples exist).
- Clear the search. Counter returns to `# 88 files match`.

Reduced-motion: no animations in this section, nothing extra to verify.

- [ ] **Step 5.7: Commit**

```bash
git add scripts/build_examples_gallery.py landing/examples/index.html
git commit -m "$(cat <<'EOF'
feat(examples): search glyph, kbd hint, and # files match counter (§3)

Wraps the search input with a leading ⌕ glyph and a trailing /
keyboard shortcut hint. Adds a global keydown listener that focuses
the search when / is pressed outside any input.

Counter format finalized as '# N files match' to match the monospace
comment aesthetic of the terminal header.

Spec §3: docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md
EOF
)"
```

---

## Task 6: Card rows as `ls -la` columns (§4)

**Goal:** Replace each card's flex-row header (`.eh`) with a 7-column CSS Grid that mimics `ls -la` output. Add a subtle 14ms-staggered enter animation for the first 30 rows. Add a mobile media query that collapses the grid to 2 visual lines.

**Files:**
- Modify: `scripts/build_examples_gallery.py:264-274` (the `.ec` and `.eh` CSS rules)
- Modify: `scripts/build_examples_gallery.py:189-233` (the Python loop that builds each card)
- Modify: the `toggle()` function inside the inline `<script>` (single-line addition for aria-expanded sync)
- Modify: the inline `<script>` (append a keydown handler for `.ex-row` elements)

- [ ] **Step 6.1: Replace `.eh` and related CSS with `.ex-row` grid CSS**

In `scripts/build_examples_gallery.py`, locate lines 264-274. Currently:

```
.ec{{border:1px solid var(--bd);border-radius:8px;overflow:hidden;background:var(--sf);background-image:var(--gr);transition:border-color .15s}}
.ec:hover{{border-color:rgba(34,211,238,0.2)}}.ec.op{{border-color:rgba(34,211,238,0.3)}}
.eh{{display:flex;align-items:center;gap:14px;padding:14px 18px;cursor:pointer;user-select:none}}
.en{{font-family:var(--mono);font-size:12px;font-weight:500;color:var(--cy);min-width:24px;flex-shrink:0}}
.ei{{flex:1;min-width:0}}.et{{font-weight:600;font-size:13px;color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}}
.ed{{font-size:12px;color:var(--dm);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}}
.em{{display:flex;gap:8px;align-items:center;flex-shrink:0}}
.ek{{font-family:var(--mono);font-size:10px;color:var(--ft);background:rgba(100,116,139,0.15);padding:2px 8px;border-radius:100px}}
.enk{{font-family:var(--mono);font-size:10px;color:var(--gn);background:rgba(34,197,94,0.1);padding:2px 8px;border-radius:100px}}
.eln{{font-family:var(--mono);font-size:10px;color:var(--ft)}}
.ev{{font-size:10px;color:var(--ft);transition:transform .2s}}.ec.op .ev{{transform:rotate(180deg)}}
```

Replace lines 266-274 (everything from `.eh{{...}}` through `.ev{{...}}.ec.op .ev{{...}}`) with:

```
.ex-row{{display:grid;grid-template-columns:32px 112px 54px 72px minmax(180px,1.5fr) minmax(0,3fr) 20px;align-items:center;gap:16px;padding:12px 18px;font-family:var(--mono);font-size:12px;cursor:pointer;user-select:none;transition:background-color .15s,border-left-color .15s;border-left:2px solid transparent}}
.ex-row:hover{{background:rgba(34,211,238,0.04);border-left-color:var(--cy)}}
.ec.op .ex-row{{background:rgba(34,211,238,0.06)}}
.ex-row__num{{color:var(--cy);font-weight:500}}
.ex-row__perm{{color:var(--ft)}}
.ex-row__size{{color:var(--ft);text-align:right}}
.ex-row__key{{font-size:10px;padding:2px 8px;border-radius:100px;text-align:center}}
.ex-row__key--free{{color:var(--gn);background:rgba(34,197,94,0.1)}}
.ex-row__key--paid{{color:var(--ft);background:rgba(100,116,139,0.15)}}
.ex-row__file{{color:var(--cy);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}
.ex-row__desc{{color:var(--tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}
.ex-row__chev{{font-size:10px;color:var(--ft);transition:transform 0.22s var(--exec-ease-soft);text-align:center}}
.ec.op .ex-row__chev{{transform:rotate(180deg)}}
.ex-row--enter{{animation:ex-row-in 0.35s var(--exec-ease-soft) both;animation-delay:calc(var(--row-index,0) * 14ms)}}
@keyframes ex-row-in{{from{{opacity:0;transform:translateY(4px)}}to{{opacity:1;transform:none}}}}
@media(max-width:640px){{.ex-row{{grid-template-columns:32px 1fr 20px;gap:8px 12px}}.ex-row__num{{grid-column:1;grid-row:1 / 3;align-self:start}}.ex-row__perm{{display:none}}.ex-row__file{{grid-column:2;grid-row:1}}.ex-row__chev{{grid-column:3;grid-row:1 / 3;align-self:start}}.ex-row__size{{grid-column:2;grid-row:2;display:inline;margin-right:8px;color:var(--ft)}}.ex-row__key{{grid-column:2;grid-row:2;display:inline;margin-right:8px}}.ex-row__desc{{grid-column:2;grid-row:2;display:inline;color:var(--dm)}}}}
@media(prefers-reduced-motion:reduce){{.ex-row--enter{{animation:none}}.ex-row__chev{{transition-duration:0.01s}}}}
```

The legacy `.eh`, `.en`, `.ei`, `.et`, `.ed`, `.em`, `.ek`, `.enk`, `.eln`, `.ev` rules can be deleted since their elements are no longer rendered (the new card markup uses `.ex-row__*` exclusively). However, the old `.ec` and `.ec:hover`/`.ec.op` rules at line 264-265 must be kept — they style the card container.

- [ ] **Step 6.2: Rewrite the per-card markup builder**

In `scripts/build_examples_gallery.py`, locate the `cards` builder loop at lines 189-233. The current loop produces a `<div class="ec">` with `<div class="eh">` containing the old flex-row header. Replace lines 189-233 (the entire loop) with this new version:

```python
    cards = []
    for ex in examples:
        cats_str = " ".join(ex["categories"])
        cat_parts = []
        for c in ex["categories"]:
            label = c.replace("-", " ").title()
            if c in CAT_DOCS:
                cat_parts.append(f'<a href="../{CAT_DOCS[c]}" class="ec1">{label}</a>')
            else:
                cat_parts.append(f'<span class="ec1">{label}</span>')
        cats_html = "".join(cat_parts)
        key_class = "ex-row__key--paid" if ex["needs_key"] else "ex-row__key--free"
        key_label = "api-key" if ex["needs_key"] else "no-key"
        graph_btn = ""
        if ex["has_graph"]:
            graph_btn = f'<a href="../{BUILDER_URL}" class="eab ebu">Open in Builder</a>'
        doc_btn = ""
        for c in ex["categories"]:
            if c in CAT_DOCS:
                doc_btn = f'<a href="../{CAT_DOCS[c]}" class="eab">Docs</a>'
                break

        # Strip the leading "NN_" from the displayed filename (column 1 already shows the number)
        display_file = re.sub(r"^\d+_", "", ex["file"])

        # Per-row enter animation: only the first 30 rows get the .ex-row--enter class
        row_index = ex["num"] - 1
        enter_class = " ex-row--enter" if row_index < 30 else ""
        enter_style = f' style="--row-index:{row_index}"' if row_index < 30 else ""

        cards.append(
            f'<div class="ec" data-cats="{cats_str}" '
            f'data-title="{html.escape(ex["title"].lower())}" data-file="{ex["file"]}">'
            f'<div class="ex-row eh{enter_class}" onclick="toggle(this)" '
            f'role="button" tabindex="0" aria-expanded="false"{enter_style}>'
            f'<span class="ex-row__num">{ex["num"]:02d}</span>'
            f'<span class="ex-row__perm">-rw-r--r--</span>'
            f'<span class="ex-row__size">{ex["lines"]}L</span>'
            f'<span class="ex-row__key {key_class}">{key_label}</span>'
            f'<span class="ex-row__file">{html.escape(display_file)}</span>'
            f'<span class="ex-row__desc">{html.escape(ex["desc"] or ex["title"])}</span>'
            f'<span class="ex-row__chev ev">▾</span>'
            f'</div>'
            f'<div class="eb" style="display:none">'
            f'<div class="eg">{cats_html}</div>'
            f'<div class="ea">'
            f'<button class="eab" onclick="cpSrc(this)">Copy</button>'
            f'<a href="{REPO_URL}/blob/main/examples/{ex["file"]}" class="eab" '
            f'target="_blank">GitHub</a>{doc_btn}{graph_btn}</div>'
            f'<pre class="ep"></pre>'
            f"</div></div>"
        )
```

Notes on the changes:
- The outer `.ec` div is unchanged.
- The inner header div now has `class="ex-row eh ex-row--enter"` (or just `.ex-row eh` for rows past index 29). The `eh` class is preserved so the existing `toggle()` handler still works via `closest('.ec')`. Rows past index 29 omit the `--enter` class entirely.
- Each row is `role="button" tabindex="0" aria-expanded="false"` for keyboard accessibility (Step 6.3 wires the keydown handler).
- Filename column strips the `NN_` prefix via `re.sub(r"^\d+_", "", ex["file"])`. The full filename is still used in the GitHub link inside `.eb`.
- The chevron is `▾` (down-pointing small triangle) and gets the existing `.ev` class so the existing `.ec.op .ev` rotation rule still applies — but the new `.ex-row__chev` rule overrides the rotation with `var(--exec-ease-soft)` easing.
- Description fallback: `ex["desc"] or ex["title"]` ensures empty descriptions don't render an empty cell.

- [ ] **Step 6.3: Surgical single-line addition to `toggle()` for aria-expanded sync**

In `scripts/build_examples_gallery.py`, locate the `toggle()` function inside the inline `<script>` around line 307. Its current body includes these statements (in order):

1. `const c=h.closest('.ec'),b=c.querySelector('.eb'),p=c.querySelector('.ep');`
2. `c.classList.toggle('op');`
3. `const open=c.classList.contains('op');`
4. `b.style.display=open?'':'none';`
5. ...source loading logic which this plan does not touch...

Immediately after statement 4 (`b.style.display=open?'':'none';`) and before the source loading statement, insert this single new statement:

```
h.setAttribute('aria-expanded',open?'true':'false');
```

Do not modify any other statement inside `toggle()`. The source loading path stays untouched.

- [ ] **Step 6.4: Add a keydown handler for `.ex-row` elements**

In `scripts/build_examples_gallery.py`, locate the script block. Just before the closing `</script>` tag (after the `/` keydown handler from Task 5), add this keydown listener:

```
document.querySelectorAll('.ex-row').forEach(r=>{{r.addEventListener('keydown',(e)=>{{if(e.key==='Enter'||e.key===' '){{e.preventDefault();toggle(r)}}}})}});
```

This makes every row keyboard-activatable. The existing `toggle()` function (now updated in Step 6.3 to sync `aria-expanded`) handles the rest.

- [ ] **Step 6.5: Regenerate the HTML**

Run:
```bash
python scripts/build_examples_gallery.py > landing/examples/index.html
```

Expected: exits 0, file ~600KB.

- [ ] **Step 6.6: Verify in browser — desktop columns, mobile collapse, enter animation, keyboard**

Open `landing/examples/index.html` in a browser at desktop width (1440×900).

Expected first paint:
- Cards render as monospace rows in `ls -la` style. Each row reads (left to right): two-digit number (cyan), `-rw-r--r--` (dim), line count `46L` (dim, right-aligned), key badge `no-key` (green) or `api-key` (dim), filename without the `NN_` prefix (cyan), description (bright), chevron `▾` (dim).
- The first ~30 rows visibly fade-up in a 14ms-staggered cascade on first paint. The full cascade completes in about 450ms.
- Rows past index 29 render in their final state immediately (no animation).
- Hovering a row tints its background and grows a 2px cyan border on the left.

Click row 01:
- The row expands. The chevron rotates 180° smoothly (with the new ease curve).
- The `.eb` body becomes visible with the existing tag links + Copy/GitHub/Docs buttons + source `<pre>`.
- The row's `aria-expanded` attribute becomes `"true"` (inspect in DevTools).

Tab into the first row, then press Enter:
- The row expands. Press Enter again to collapse.
- `aria-expanded` toggles between `"true"` and `"false"` on each activation.

Mobile fallback:
- Switch to a 375×812 viewport. Reload.
- Expected: each card collapses to 2 visual lines. Line 1 has the number on the left, the filename, and the chevron on the right. Line 2 has the line count, key badge, and description (truncated). The `-rw-r--r--` permissions column is hidden on mobile.

Reduced-motion:
- Enable `prefers-reduced-motion`. Reload.
- Expected: rows appear immediately without the cascade animation. Chevron rotation is instant (0.01s). Card expansion still works.

Smoke test all 88 cards filter correctly:
- Type `rag` in search. Only RAG cards remain visible. Counter says `# 12 files match`.
- Click the `agent` segment in the rail. Only Agent cards visible.
- Click `all`. All 88 visible. Clear search.

- [ ] **Step 6.7: Commit**

```bash
git add scripts/build_examples_gallery.py landing/examples/index.html
git commit -m "$(cat <<'EOF'
feat(examples): card rows as ls -la grid columns (§4)

Replaces the flex-row .eh card header with a 7-column CSS Grid that
mimics 'ls -la' output: number, permissions, size, key badge,
filename (with NN_ prefix stripped), description, chevron.

The first 30 rows get a 14ms-staggered fade-up enter animation via
the .ex-row--enter class. Rows past index 29 render immediately to
avoid a cascade-of-88 effect.

Mobile collapses to 2-line layout with permissions column hidden.
Rows are keyboard-accessible (Enter/Space) with aria-expanded synced
via a surgical single-line addition to the existing toggle() function.

Spec §4: docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md
EOF
)"
```

---

## Task 7: `$ cat` prefix on card expansion (§5)

**Goal:** Add a one-line `$ cat examples/NN_name.py` terminal prefix above each expanded card's body content. The prefix uses the same monospace comment styling as the rest of the page.

**Files:**
- Modify: `scripts/build_examples_gallery.py:275` (the `.eb` CSS — add `.ex-cat-prefix`)
- Modify: `scripts/build_examples_gallery.py:189-233` (the per-card builder — insert prefix into `.eb`)

- [ ] **Step 7.1: Add `.ex-cat-prefix` CSS**

In `scripts/build_examples_gallery.py`, locate line 275:

```
.eb{{padding:0 18px 18px}}.eg{{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px}}
```

Add these new lines AFTER line 275 (do not delete the existing line):

```
.ex-cat-prefix{{font-family:var(--mono);font-size:11px;color:var(--ft);padding:0 0 10px;user-select:text}}
.ex-cat-prefix__glyph{{color:var(--gn);margin-right:6px}}
```

- [ ] **Step 7.2: Insert the prefix into the `.eb` body**

In `scripts/build_examples_gallery.py`, locate the per-card builder loop. Find this part of the f-string in the `cards.append(...)` call:

```python
            f'<div class="eb" style="display:none">'
            f'<div class="eg">{cats_html}</div>'
```

Replace those two lines with:

```python
            f'<div class="eb" style="display:none">'
            f'<div class="ex-cat-prefix"><span class="ex-cat-prefix__glyph">$</span>cat examples/{ex["file"]}</div>'
            f'<div class="eg">{cats_html}</div>'
```

The prefix uses the FULL filename `ex["file"]` (with `NN_` prefix), not the stripped display name. This is intentional — the path inside the `$ cat` command should be the real filename you'd type.

- [ ] **Step 7.3: Regenerate the HTML**

Run:
```bash
python scripts/build_examples_gallery.py > landing/examples/index.html
```

Expected: exits 0, file ~600KB.

- [ ] **Step 7.4: Verify in browser**

Open `landing/examples/index.html` in a browser. Click any card to expand it.

Expected:
- Above the existing tag links / Copy / GitHub / Docs buttons, a small monospace line reads: `$ cat examples/01_hello_world.py` (with the `$` in green and the rest in the dim faint color).
- The line is selectable (you can highlight + copy the path text).
- Collapsing the card hides the prefix along with the rest of the body.
- Expanding any other card shows its corresponding `$ cat examples/NN_name.py` prefix.

- [ ] **Step 7.5: Commit**

```bash
git add scripts/build_examples_gallery.py landing/examples/index.html
git commit -m "$(cat <<'EOF'
feat(examples): add \$ cat prefix to expanded card bodies (§5)

When a card is expanded, a one-line monospace '\$ cat examples/NN_name.py'
prefix renders above the existing action buttons and source pane.
Visually frames the source as the output of a real shell command.

The prefix uses the full filename (with NN_ prefix) since that is the
real path you would type in a shell.

Spec §5: docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md
EOF
)"
```

---

## Task 8: Final cleanup, audits, and polish

**Goal:** Run the reduced-motion audit, mobile audit, focus-ring audit, and remove any dead CSS/JS left over from the redesign.

**Files:**
- Modify: `scripts/build_examples_gallery.py` — only if dead code is found

- [ ] **Step 8.1: Reduced-motion audit**

Open `landing/examples/index.html` in a browser. DevTools → Rendering → Emulate `prefers-reduced-motion: reduce`. Reload.

Walk through every animation in the spec:
- §6 nav `.exec-dot`: should be a static glow, no pulse. ✓ verify.
- §1 terminal type-on: command should appear fully typed instantly. Caret should be static (visible but not blinking). ✓ verify.
- §2 rail stamp sweep: should NOT play on viewport entry. Rail in rest state immediately. ✓ verify.
- §2 rail click stamp: clicking a segment should NOT trigger a stamp. Selection still works. ✓ verify.
- §4 card row enter: rows 0–29 should appear immediately without cascade. ✓ verify.
- §4 chevron rotation: should be instant (0.01s) when opening/closing. ✓ verify.

If any animation still plays under reduced-motion, find the missing `@media (prefers-reduced-motion: reduce)` rule and add it. Common miss: a new `@keyframes` was added without a corresponding fallback.

- [ ] **Step 8.2: Mobile audit at 360px**

Switch DevTools to a 360×640 viewport (smallest realistic mobile). Reload.

Walk through:
- Nav: no horizontal overflow. The `selectools examples` brand fits.
- Terminal header: only `$ ls examples/...` is visible. No wrapping in the prompt line.
- Search input: `⌕` glyph and `/` kbd hint both visible.
- Category rail: horizontally scrollable. Touch-swipe scrolls smoothly.
- Card rows: collapse to 2-line layout. Number on left of line 1, filename + chevron on line 1, size + key + desc on line 2. No horizontal overflow on any row.
- Card expansion: source `<pre>` scrolls horizontally inside the card. Buttons wrap if needed.

Fix any horizontal overflow you find. Common cause: a fixed-width grid column that doesn't have a mobile fallback.

- [ ] **Step 8.3: Focus ring audit**

Tab through the page from the top:
- Nav links should each show a visible focus ring.
- Search input shows its existing focus ring.
- Category rail segments: each tabbable, each shows a visible focus ring.
- Card rows: each tabbable, each shows a visible focus ring.
- Inside an open card: Copy / GitHub / Docs buttons each show a visible focus ring.

If any focusable element has no visible ring, add `:focus-visible { outline: 2px solid var(--cy); outline-offset: 2px; }` for that element type.

- [ ] **Step 8.4: Dead-code grep**

In `scripts/build_examples_gallery.py`, search for any classes/identifiers that the redesign removed but left in the CSS:
- `.cb` — removed in Task 4 (was the chip class). Verify it is not in the CSS string anymore. If it is, delete those rules.
- `.cr` — removed in Task 4 (was the chip row container). Same check.
- `.eh` (as a flex layout) — the class is still applied to `.ex-row` elements for `closest()` compatibility, BUT the old `.eh{{display:flex;...}}` rule may have been deleted in Task 6. Confirm the flex rule is gone and that the surviving `.eh` references in JS still work (they use `closest('.ec')`).
- `.en`, `.ei`, `.et`, `.ed`, `.em`, `.ek`, `.enk`, `.eln` — removed in Task 6 (old card header subclasses). Verify their CSS rules are deleted.
- `CAT_ICONS` (Python dict at line 44) — no longer referenced after Task 4. Decide whether to delete the dict or keep it for potential future use. If keeping, leave it. If deleting, remove lines 44-62.

The decision criterion: delete only what is provably unused. If in doubt, leave it.

- [ ] **Step 8.5: Final regeneration and full smoke test**

Run:
```bash
python scripts/build_examples_gallery.py > landing/examples/index.html
```

Open the regenerated file. Run through the full UX:
1. Page loads. Nav dot pulses. Terminal header types `ls examples/`. ✓
2. Category rail sweeps in on first view. ✓
3. Click `rag` in the rail. List filters to 12 cards. Prompt updates to `--tags rag`. Counter says `# 12 files match`. ✓
4. Type `embed` in search. List filters to embedding-related RAG examples. Prompt updates to `--tags rag | grep -i embed`. ✓
5. Clear search. Click `all`. Full list returns. ✓
6. Click row 01 (`hello_world.py`). Card expands. `$ cat examples/01_hello_world.py` prefix shown. Source code rendered with syntax highlighting. ✓
7. Click Copy. Source is copied to clipboard. Button text changes to "Copied!" briefly. ✓
8. Click GitHub link. Opens the file on GitHub in a new tab. ✓
9. Press `/` somewhere outside an input. Search box gains focus. ✓
10. Tab through the page. Every interactive element has a focus ring. ✓
11. Switch to mobile viewport (360px). Repeat steps 1-7. All work. ✓
12. Enable reduced-motion. Reload. No animations play; all functionality intact. ✓

If any step fails, fix it now. Do not commit broken state.

- [ ] **Step 8.6: Commit cleanup**

If Step 8.4 removed any dead code, or if any audit step required a fix, commit the cleanup:

```bash
git add scripts/build_examples_gallery.py landing/examples/index.html
git commit -m "$(cat <<'EOF'
chore(examples): remove dead CSS/JS from §1-§5 redesign cleanup

Final cleanup commit for the examples page overdrive redesign:
- Reduced-motion audit pass
- Mobile audit at 360px
- Focus ring audit
- Dead-code removal for classes superseded by .ex-* equivalents

Spec: docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md
EOF
)"
```

If no cleanup was needed, skip this commit and proceed to Step 8.7.

- [ ] **Step 8.7: Open the PR**

```bash
gh pr create --title "feat(examples): overdrive redesign for /examples/" --body "$(cat <<'EOF'
## Summary
- Brings `landing/examples/index.html` into the same execution-pointer visual language as the redesigned landing page (`2026-04-07-landing-overdrive-fused-design.md`)
- Six sections rebuilt: nav dot, terminal-session header, proportional category rail, search row, ls -la card rows, $ cat card expansion
- Single file edited: `scripts/build_examples_gallery.py` (the generator). HTML is regenerated.

## Spec
`docs/superpowers/specs/2026-04-08-examples-page-overdrive-design.md`

## Test plan
- [ ] Open `/examples/` at 1440x900 — terminal header types `ls examples/`, category rail sweeps in, card rows render as ls -la columns
- [ ] Click a category segment — list filters, prompt rewrites with `--tags`, segment re-stamps
- [ ] Type in search — prompt rewrites with ` | grep -i`, counter updates to `# N files match`
- [ ] Press `/` — search input focused
- [ ] Tab through the page — every interactive element has a visible focus ring
- [ ] Expand a card — `$ cat examples/NN_name.py` prefix shown above source
- [ ] Mobile viewport (375px and 360px) — header collapses, rail scrolls, rows collapse to 2 lines
- [ ] `prefers-reduced-motion: reduce` — no animations play, all functionality intact
- [ ] Click between `/` and `/examples/` — same dot pulses in same place in nav
EOF
)"
```

---

## Self-review

After writing the plan, I checked it against the spec and against the writing-plans skill rules. Findings:

**Spec coverage** (every spec section has a task):
- §1 Terminal-session header → Task 3 ✓
- §2 Category rail → Task 4 ✓
- §3 Filter row and search input → Task 5 ✓
- §4 Card list as `ls -la` output → Task 6 ✓
- §5 Card expansion as `$ cat` output → Task 7 ✓
- §6 Nav bar → Task 2 ✓
- Setup (atom duplication) → Task 1 ✓
- Final audits → Task 8 ✓

**Placeholder scan**: no `TBD` / `TODO` / "implement appropriately" / "add error handling". Every code block contains the actual code. Every test step has the exact expected behavior to verify in browser.

**Type / name consistency**:
- `.ex-row` class is used consistently across Tasks 6, 7, and 8.
- `.ex-row--enter` modifier class introduced in Task 6 is referenced consistently in Task 8's reduced-motion audit.
- `syncPrompt()` is introduced in Task 3 and called from Task 4's rail click handler — both call sites pass no arguments, both expect the function in the global scope.
- `--seg-weight` and `--seg-index` are introduced in Task 4 and the corresponding CSS that consumes them is also in Task 4 — single-task introduction, no cross-task type drift.
- `--row-index` is similarly self-contained in Task 6.

**Bite-sized granularity**: every step is one action (read a line, edit a line, run a command, verify in browser, commit). Each task takes ~20-30 minutes including verification.

**TDD note**: this is a static-site generator producing HTML, not unit-testable business logic. The test step in each task is a browser smoke test: load the regenerated page, exercise the new behavior, verify visually. This is the right test loop for HTML/CSS work — adding pytest tests for an HTML generator that already produces a valid file would be over-engineering.
