diff --git a/apps/website/src/app/page.tsx b/apps/website/src/app/page.tsx
index 36213c27b..4c9b7d56f 100644
--- a/apps/website/src/app/page.tsx
+++ b/apps/website/src/app/page.tsx
@@ -40,8 +40,8 @@ export default function HomePage() {
(live-stage spec §3, §8). Copy lives in STAGE_RAIL (positioning.ts). */}
- {/* The open-source beat: a slim dark strip, not a second CTA. Copy lives
- in OPEN_SOURCE_STRIP (positioning.ts). */}
+ {/* The open-source full stop: a loud dark band with one fork CTA. Copy
+ lives in OPEN_SOURCE_STRIP (positioning.ts). */}
diff --git a/apps/website/src/components/landing/OpenSourceStrip.spec.tsx b/apps/website/src/components/landing/OpenSourceStrip.spec.tsx
index 0c8857aba..8b93a5e65 100644
--- a/apps/website/src/components/landing/OpenSourceStrip.spec.tsx
+++ b/apps/website/src/components/landing/OpenSourceStrip.spec.tsx
@@ -14,13 +14,17 @@ vi.mock('../../lib/analytics/client', () => ({
beforeEach(() => trackCtaClickMock.mockClear());
describe('OpenSourceStrip', () => {
- it('renders the whole sentence as the section heading, emphasis included', () => {
- render();
+ it('makes the headline alone the section heading', () => {
+ const { container } = render();
const heading = screen.getByRole('heading', { level: 2 });
- expect(heading.textContent).toBe(
- `${OPEN_SOURCE_STRIP.lead} ${OPEN_SOURCE_STRIP.emphasis}`,
+ // Two words, not a sentence: the section's accessible name is the offer.
+ expect(heading.textContent).toBe(OPEN_SOURCE_STRIP.headline);
+ expect(heading.querySelector('em')).toBeNull();
+ // The eyebrow stays readable rather than aria-hidden, matching how
+ // SectionHeader treats its own eyebrows.
+ expect(container.querySelector('.open-source-strip-eyebrow')?.textContent).toBe(
+ OPEN_SOURCE_STRIP.eyebrow,
);
- expect(heading.querySelector('em')?.textContent).toBe(OPEN_SOURCE_STRIP.emphasis);
});
it('names the section by that heading', () => {
@@ -30,13 +34,15 @@ describe('OpenSourceStrip', () => {
expect(container.querySelector('#open-source-heading')).toBeTruthy();
});
- it('sits on the dark surface at the tight rhythm', () => {
+ it('sits on the dark surface at the FULL rhythm, not the tight one', () => {
const { container } = render();
const section = container.querySelector('[data-ui="section"]');
expect(section?.getAttribute('data-surface')).toBe('dark');
- // The strip's own padding override keys off [data-tight]; dropping the
- // prop silently restores the full 48-80px band this replaced.
- expect(section?.getAttribute('data-tight')).toBe('true');
+ // Deliberately absent. This band used to be the page's quiet beat and a
+ // guard here asserted data-tight="true" to keep it that way; the section
+ // is now a full stop, and it gets its ~461px from the standard section
+ // padding rather than from any override of its own.
+ expect(section?.getAttribute('data-tight')).toBeNull();
expect(section?.classList.contains('open-source-strip')).toBe(true);
});
@@ -56,7 +62,7 @@ describe('OpenSourceStrip', () => {
);
});
- it('is the page’s quiet beat: one action, no second CTA', () => {
+ it('offers one action and no second CTA', () => {
const { container } = render();
expect(container.querySelectorAll('a').length).toBe(1);
});
@@ -71,4 +77,15 @@ describe('OpenSourceStrip', () => {
destination_url: GITHUB_REPO_URL,
});
});
+
+ it('marks the runway decorative and puts it outside the container', () => {
+ const { container } = render();
+ const runway = container.querySelector('.open-source-strip-runway');
+ expect(runway?.getAttribute('aria-hidden')).toBe('true');
+ expect(runway?.textContent).toBe('');
+ // It spans the section, not the container, so it must not be nested in
+ // one — inside, the container's gutters would clip the marking short.
+ expect(runway?.closest('[data-ui="container"]')).toBeNull();
+ expect(runway?.parentElement?.getAttribute('data-ui')).toBe('section');
+ });
});
diff --git a/apps/website/src/components/landing/OpenSourceStrip.tsx b/apps/website/src/components/landing/OpenSourceStrip.tsx
index 085195606..9807f40aa 100644
--- a/apps/website/src/components/landing/OpenSourceStrip.tsx
+++ b/apps/website/src/components/landing/OpenSourceStrip.tsx
@@ -8,55 +8,60 @@ import { trackCtaClick } from '../../lib/analytics/client';
import { GITHUB_REPO_URL, OPEN_SOURCE_STRIP } from '../../lib/positioning';
/**
- * The homepage's open-source beat: one dark strip between the stage and the
- * teams block.
+ * The homepage's open-source beat: a dark full-stop band between the stage
+ * and the teams block.
*
- * It replaced a full dark `FinalCTA` (the "prove the Angular UI" closer). The
- * point of the section is that there is no catch and no upsell, so it is
- * deliberately the quietest band on the page: one sentence at the left edge of
- * the page container, the licence and the repo at the right, and no second
- * pitch. The four library pages still close on `FinalCTA variant="dark"` —
- * that component is untouched.
+ * It is deliberately loud. An earlier version was the quietest band on the
+ * page — the reasoning was that the offer is "no catch, no upsell," so it
+ * should not read as a second pitch. That restraint is now spent on purpose:
+ * the open-source offer is one of the strongest things the product has to
+ * say, and it is said here in two words over an aviation code.
+ *
+ * The band takes its ~461px from the standard section rhythm; there is no
+ * padding override, which is why `tight` is absent rather than false.
+ *
+ * The four library pages still close on `FinalCTA variant="dark"` — that
+ * component is untouched.
*/
export function OpenSourceStrip() {
return (
-
+ {/* Spans the section rather than the container, so the marking runs
+ edge to edge like paint on a runway. */}
+
);
}
diff --git a/apps/website/src/lib/positioning.spec.ts b/apps/website/src/lib/positioning.spec.ts
index 1b41465af..4f37dc167 100644
--- a/apps/website/src/lib/positioning.spec.ts
+++ b/apps/website/src/lib/positioning.spec.ts
@@ -157,14 +157,16 @@ describe('positioning: coding-agent prompt', () => {
});
describe('homepage restructure copy (live-stage spec §3)', () => {
- it('closes on the open-source offer: one sentence, the licence, the fork CTA', async () => {
+ it('closes on the open-source offer: an aviation eyebrow, two words, the licence and the CTA', async () => {
const { OPEN_SOURCE_STRIP } = await import('./positioning');
- expect(`${OPEN_SOURCE_STRIP.lead} ${OPEN_SOURCE_STRIP.emphasis}`).toBe(
- 'Yes, this is all free. Don’t like something? Fork us.',
- );
- // The strip is one line on a wide viewport; long copy would wrap into the
- // action group and undo the whole point of the shape.
- expect(`${OPEN_SOURCE_STRIP.lead} ${OPEN_SOURCE_STRIP.emphasis}`.length).toBeLessThanOrEqual(60);
+ expect(OPEN_SOURCE_STRIP.eyebrow).toBe('Squawk 1200');
+ expect(OPEN_SOURCE_STRIP.headline).toBe('Fork us.');
+ // A length budget, not a word count: 12 characters is about what fits
+ // on one line at 116px in Archivo Black inside the 1200px container.
+ // A wrapped headline stops reading as a full stop.
+ expect(OPEN_SOURCE_STRIP.headline.length).toBeLessThanOrEqual(12);
+ // The eyebrow sits on one line at 0.18em tracking beside nothing else.
+ expect(OPEN_SOURCE_STRIP.eyebrow.length).toBeLessThanOrEqual(14);
expect(OPEN_SOURCE_STRIP.licence).toBe('MIT');
expect(OPEN_SOURCE_STRIP.cta).toBe('Fork on GitHub');
});
diff --git a/apps/website/src/lib/positioning.ts b/apps/website/src/lib/positioning.ts
index 55faa5cba..41d787e0d 100644
--- a/apps/website/src/lib/positioning.ts
+++ b/apps/website/src/lib/positioning.ts
@@ -62,16 +62,22 @@ export const HERO_TRUST_LINE = `MIT · ${formatAngularRange(WEBSITE_SUPPORTED_AN
*/
export const GITHUB_REPO_URL = 'https://github.com/cacheplane/angular-agent-framework';
-// ── The open-source strip (the slim dark band after the stage). One sentence,
-// the licence, the repo. Bold in what it says, quiet in how it looks: the
-// point is that there is no catch, and the remedy for disagreeing with us is
-// a fork — so it must not read as another sales pitch. ──────────────────────
+// ── The open-source band (the dark full stop after the stage). An eyebrow,
+// two words, the licence and the repo. It used to be the quietest band on the
+// page; it is now loud on purpose, because the offer — no catch, and the
+// remedy for disagreeing with us is a fork — is one of the strongest things
+// the product has to say. ───────────────────────────────────────────────────
export const OPEN_SOURCE_STRIP = {
- /** The sentence, up to the part that carries the emphasis. */
- lead: 'Yes, this is all free. Don’t like something?',
- /** Rendered italic at the end of the same line. */
- emphasis: 'Fork us.',
- /** The licence tag beside the action. */
+ /**
+ * The US transponder code for VFR flight not receiving ATC services —
+ * flying with nobody controlling you, which is the offer exactly. It is
+ * texture, not information: the headline carries the whole meaning, so a
+ * reader who does not fly loses nothing.
+ */
+ eyebrow: 'Squawk 1200',
+ /** Two words at up to 116px. The band's entire argument. */
+ headline: 'Fork us.',
+ /** The bare licence, set in mono after the action. */
licence: 'MIT',
cta: 'Fork on GitHub',
} as const;
diff --git a/apps/website/src/styles/landing.css b/apps/website/src/styles/landing.css
index c9908ad78..7946e1faa 100644
--- a/apps/website/src/styles/landing.css
+++ b/apps/website/src/styles/landing.css
@@ -616,58 +616,77 @@
}
}
-/* OpenSourceStrip — components/landing/OpenSourceStrip.tsx
- *
- * A strip, not a section: the sentence sits at the left edge of the page
- * container and the action at the right, so it reads as an aside between the
- * stage and the teams block. It deliberately undercuts the tight section
- * rhythm (48–80px) — at that height the band still reads as a full closing
- * beat, which is the volume this replaced. The [data-tight] selector is two
- * attributes, so the override has to carry both to win. */
-[data-ui="section"][data-tight].open-source-strip {
- padding-top: clamp(28px, 3.2vw, 44px);
- padding-bottom: clamp(28px, 3.2vw, 44px);
-}
-.open-source-strip-inner {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 20px 32px;
- flex-wrap: wrap;
+/* The Fork us band — components/landing/OpenSourceStrip.tsx.
+ * No padding override: the band takes the standard --spacing-section-y, which
+ * is where its ~461px comes from. The dark surface already sets position:
+ * relative, but the runway depends on it, so it is set here too rather than
+ * inherited from a rule that could change for unrelated reasons. */
+.open-source-strip {
+ position: relative;
}
-.open-source-strip-line {
- /* Body-scale editorial copy, not display type: it carries an italic ,
- and Archivo Black has no italic. --font-sans (Archivo) does. */
- font-family: var(--font-sans);
- font-size: 23px;
- line-height: 1.35;
+.open-source-strip-eyebrow {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ font-weight: 700;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ /* The Eyebrow primitive pins 1.4; this rule is hand-rolled for the tighter
+ 11px/.18em the band wants, so it has to carry the line-height too rather
+ than inherit the body's. */
+ line-height: 1.4;
+ color: var(--color-signal);
+ margin: 0 0 22px;
+}
+.open-source-strip-headline {
+ /* --font-display is available here now: the italic this line used to
+ carry is gone, and Archivo Black has no italic. */
+ font-family: var(--font-display);
font-weight: 400;
- letter-spacing: -0.005em;
+ font-size: clamp(56px, 8.5vw, 116px);
+ line-height: 0.9;
+ letter-spacing: -0.04em;
color: var(--color-text-primary);
margin: 0;
}
-.open-source-strip-line em {
- font-style: italic;
-}
.open-source-strip-actions {
display: flex;
align-items: center;
- gap: 18px;
- flex: none;
+ gap: 20px;
+ margin-top: 38px;
}
.open-source-strip-licence {
- font-family: var(--font-sans);
- font-size: 12px;
- letter-spacing: 0.02em;
+ font-family: var(--font-mono);
+ font-size: 11px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
color: var(--color-text-muted);
- border: 1px solid var(--color-border);
- border-radius: var(--radius-pill, 999px);
- padding: 4px 10px;
white-space: nowrap;
}
+/* The runway centreline. A fixed 46px dash on a 92px period rather than a
+ * proportional one, so it reads as the same painted marking at every width. */
+.open-source-strip-runway {
+ /* Decorative and layered over the band's bottom padding, so it must not
+ swallow a text-selection drag — the dark surface's top seam
+ (ui.css, [data-surface="dark"]::before) sets this for the same reason. */
+ pointer-events: none;
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 30px;
+ height: 6px;
+ background: repeating-linear-gradient(
+ 90deg,
+ rgba(255, 175, 0, 0.5) 0 46px,
+ transparent 46px 92px
+ );
+}
@media (max-width: 640px) {
- .open-source-strip-line {
- font-size: 21px;
+ .open-source-strip-headline {
+ font-size: clamp(44px, 13vw, 56px);
+ }
+ .open-source-strip-actions {
+ flex-wrap: wrap;
+ gap: 14px;
}
}
diff --git a/docs/superpowers/plans/2026-09-08-fork-us-band.md b/docs/superpowers/plans/2026-09-08-fork-us-band.md
new file mode 100644
index 000000000..62e0aecfc
--- /dev/null
+++ b/docs/superpowers/plans/2026-09-08-fork-us-band.md
@@ -0,0 +1,458 @@
+# Fork Us Band 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:** Turn the homepage's quiet 204px open-source strip into a loud ~461px band: an aviation eyebrow, a two-word display headline, an amber fork button, and a runway centreline.
+
+**Architecture:** Three stacked elements inside the page container plus one full-section-width decorative rule outside it. No new component, no new file — `OpenSourceStrip` keeps its name, its id, and its place in `page.tsx`; only its markup, copy shape and styles change.
+
+**Tech Stack:** Next.js 15 App Router, React, plain CSS in `apps/website/src/styles/landing.css`, Vitest + Testing Library, Playwright.
+
+**Spec:** `docs/superpowers/specs/2026-09-08-fork-us-band-design.md`
+
+---
+
+## Background an engineer needs before starting
+
+**Run the commands CI runs**: `npx nx test website`, `npx nx lint website`, `npx nx build website` — NOT `npx vitest run --root apps/website`. The nx executor is what CI uses, and it swallows reporter output on a hard failure: when it goes red, run vitest directly alongside it to read the actual failure, then trust nx for the verdict. `nx test` does **not** type-check; `nx build` does.
+
+**Three things in the existing code will fight this change. All three are expected:**
+
+1. **A guard exists specifically to prevent it.** `OpenSourceStrip.spec.tsx` asserts `data-tight === 'true'` and comments: *"The strip's own padding override keys off [data-tight]; dropping the prop silently restores the full 48-80px band this replaced."* We are now dropping that prop on purpose. The assertion inverts and **the comment must be rewritten** — leaving it would tell the next reader the opposite of the truth.
+
+2. **The component docblock argues for quietness.** It says the section is "deliberately the quietest band on the page" because "there is no catch and no upsell." That reasoning no longer holds. Rewrite it; do not leave a docblock contradicting the component beneath it.
+
+3. **A CSS comment justifies the wrong font.** `.open-source-strip-line` says it uses `--font-sans` because the line "carries an italic ``, and Archivo Black has no italic." The `` is being removed, so that constraint disappears and the headline takes `--font-display`.
+
+**`Button`'s default variant is already `primary`.** Pass it explicitly anyway — the amber treatment is a decision the spec records, and an explicit prop keeps it visible. Verified: `[data-variant="primary"]` fills with `--color-signal` directly (`ui.css:273`), not by way of `--color-accent`, so it is amber on the dark surface with no surface-specific rule needed.
+
+**Do not touch:** the `id="open-source-heading"` (pinned by the e2e spine at `website.spec.ts:56`), the section's position in `page.tsx`, the `cta_id: 'hero_github'` analytics payload, or `FinalCTA`.
+
+---
+
+## File Structure
+
+| File | Responsibility | Change |
+| --- | --- | --- |
+| `apps/website/src/lib/positioning.ts` | The four copy strings | `OPEN_SOURCE_STRIP` reshaped |
+| `apps/website/src/lib/positioning.spec.ts` | Copy guard | Follows the reshape |
+| `apps/website/src/components/landing/OpenSourceStrip.tsx` | The band's markup | Rewritten body |
+| `apps/website/src/components/landing/OpenSourceStrip.spec.tsx` | Render guards | Heading, tight, runway |
+| `apps/website/src/styles/landing.css` | The band's styles | New rules in, old out |
+
+---
+
+## Task 1: The copy
+
+**Files:**
+- Modify: `apps/website/src/lib/positioning.ts:69-77`
+- Modify: `apps/website/src/lib/positioning.spec.ts:159-171`
+
+- [ ] **Step 1: Update the guard first**
+
+In `positioning.spec.ts`, replace the whole `describe('homepage restructure copy (live-stage spec §3)')` block with:
+
+```ts
+describe('homepage restructure copy (live-stage spec §3)', () => {
+ it('closes on the open-source offer: an aviation eyebrow, two words, the licence and the CTA', async () => {
+ const { OPEN_SOURCE_STRIP } = await import('./positioning');
+ expect(OPEN_SOURCE_STRIP.eyebrow).toBe('Squawk 1200');
+ expect(OPEN_SOURCE_STRIP.headline).toBe('Fork us.');
+ // The headline is set at up to 116px. More than two short words wraps,
+ // and a wrapped headline stops reading as a full stop.
+ expect(OPEN_SOURCE_STRIP.headline.length).toBeLessThanOrEqual(12);
+ // The eyebrow sits on one line at 0.18em tracking beside nothing else.
+ expect(OPEN_SOURCE_STRIP.eyebrow.length).toBeLessThanOrEqual(14);
+ expect(OPEN_SOURCE_STRIP.licence).toBe('MIT');
+ expect(OPEN_SOURCE_STRIP.cta).toBe('Fork on GitHub');
+ });
+});
+```
+
+- [ ] **Step 2: Run it and watch it fail**
+
+```bash
+npx nx test website
+```
+
+Expected: FAIL — `OPEN_SOURCE_STRIP.eyebrow` is `undefined`.
+
+- [ ] **Step 3: Reshape the constant**
+
+In `positioning.ts`, replace the `OPEN_SOURCE_STRIP` block with:
+
+```ts
+export const OPEN_SOURCE_STRIP = {
+ /**
+ * The US transponder code for VFR flight not receiving ATC services —
+ * flying with nobody controlling you, which is the offer exactly. It is
+ * texture, not information: the headline carries the whole meaning, so a
+ * reader who does not fly loses nothing.
+ */
+ eyebrow: 'Squawk 1200',
+ /** Two words at up to 116px. The band's entire argument. */
+ headline: 'Fork us.',
+ /** The licence tag beside the action. */
+ licence: 'MIT',
+ cta: 'Fork on GitHub',
+} as const;
+```
+
+- [ ] **Step 4: Run it**
+
+```bash
+npx nx test website
+```
+
+Expected: `positioning.spec.ts` passes. `OpenSourceStrip.spec.tsx` now FAILS — it still reads `.lead` and `.emphasis`. That is expected and Task 2 fixes it; do not patch it here.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add apps/website/src/lib/positioning.ts apps/website/src/lib/positioning.spec.ts
+git commit -m "feat(website): the open-source copy becomes an eyebrow and two words
+
+Co-Authored-By: Claude Opus 5 "
+```
+
+---
+
+## Task 2: The component
+
+**Files:**
+- Modify: `apps/website/src/components/landing/OpenSourceStrip.tsx`
+- Modify: `apps/website/src/components/landing/OpenSourceStrip.spec.tsx`
+
+- [ ] **Step 1: Update the guards**
+
+In `OpenSourceStrip.spec.tsx`:
+
+**Replace** the first test (`renders the whole sentence as the section heading, emphasis included`) with:
+
+```tsx
+ it('makes the headline alone the section heading', () => {
+ const { container } = render();
+ const heading = screen.getByRole('heading', { level: 2 });
+ // Two words, not a sentence: the section's accessible name is the offer.
+ expect(heading.textContent).toBe(OPEN_SOURCE_STRIP.headline);
+ expect(heading.querySelector('em')).toBeNull();
+ // The eyebrow stays readable rather than aria-hidden, matching how
+ // SectionHeader treats its own eyebrows.
+ expect(container.querySelector('.open-source-strip-eyebrow')?.textContent).toBe(
+ OPEN_SOURCE_STRIP.eyebrow,
+ );
+ });
+```
+
+**Replace** the third test (`sits on the dark surface at the tight rhythm`) with:
+
+```tsx
+ it('sits on the dark surface at the FULL rhythm, not the tight one', () => {
+ const { container } = render();
+ const section = container.querySelector('[data-ui="section"]');
+ expect(section?.getAttribute('data-surface')).toBe('dark');
+ // Deliberately absent. This band used to be the page's quiet beat and a
+ // guard here asserted data-tight="true" to keep it that way; the section
+ // is now a full stop, and it gets its ~461px from the standard section
+ // padding rather than from any override of its own.
+ expect(section?.getAttribute('data-tight')).toBeNull();
+ expect(section?.classList.contains('open-source-strip')).toBe(true);
+ });
+```
+
+**Rename** the fifth test from `is the page’s quiet beat: one action, no second CTA` to:
+
+```tsx
+ it('offers one action and no second CTA', () => {
+```
+
+(its body is unchanged — still `expect(container.querySelectorAll('a').length).toBe(1)`)
+
+**Add** at the end of the `describe`:
+
+```tsx
+ it('marks the runway decorative and puts it outside the container', () => {
+ const { container } = render();
+ const runway = container.querySelector('.open-source-strip-runway');
+ expect(runway?.getAttribute('aria-hidden')).toBe('true');
+ expect(runway?.textContent).toBe('');
+ // It spans the section, not the container, so it must not be nested in
+ // one — inside, the container's gutters would clip the marking short.
+ expect(runway?.closest('[data-ui="container"]')).toBeNull();
+ expect(runway?.parentElement?.getAttribute('data-ui')).toBe('section');
+ });
+```
+
+- [ ] **Step 2: Run and watch it fail**
+
+```bash
+npx nx test website
+```
+
+Expected: FAIL — the heading is still the full sentence and `.open-source-strip-runway` does not exist.
+
+- [ ] **Step 3: Rewrite the component**
+
+Replace the docblock and the `return` in `OpenSourceStrip.tsx`. **The import list does not change** — every symbol it already imports is still used.
+
+```tsx
+/**
+ * The homepage's open-source beat: a dark full-stop band between the stage
+ * and the teams block.
+ *
+ * It is deliberately loud. An earlier version was the quietest band on the
+ * page — the reasoning was that the offer is "no catch, no upsell," so it
+ * should not read as a second pitch. That restraint is now spent on purpose:
+ * the open-source offer is one of the strongest things the product has to
+ * say, and it is said here in four words and one aviation marking.
+ *
+ * The band takes its ~461px from the standard section rhythm; there is no
+ * padding override, which is why `tight` is absent rather than false.
+ *
+ * The four library pages still close on `FinalCTA variant="dark"` — that
+ * component is untouched.
+ */
+export function OpenSourceStrip() {
+ return (
+
+
+
+
+ {/* Spans the section rather than the container, so the marking runs
+ edge to edge like paint on a runway. */}
+
+
+ );
+}
+```
+
+Note the licence now sits **after** the button, where it previously came before it.
+
+- [ ] **Step 4: Run it**
+
+```bash
+npx nx test website
+```
+
+Expected: PASS.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add apps/website/src/components/landing/OpenSourceStrip.tsx apps/website/src/components/landing/OpenSourceStrip.spec.tsx
+git commit -m "feat(website): the open-source strip becomes the Fork us band
+
+Co-Authored-By: Claude Opus 5 "
+```
+
+---
+
+## Task 3: The styles
+
+**Files:**
+- Modify: `apps/website/src/styles/landing.css:625-673`
+
+- [ ] **Step 1: Replace the block**
+
+Delete these rules entirely: the `[data-ui="section"][data-tight].open-source-strip` padding override (and the comment above it), `.open-source-strip-inner`, `.open-source-strip-line`, `.open-source-strip-line em`, and the `@media (max-width: 640px)` block that only adjusts `.open-source-strip-line`.
+
+Keep `.open-source-strip-actions` and `.open-source-strip-licence` as selectors but replace their bodies. Add the rest:
+
+```css
+/* The Fork us band — components/landing/OpenSourceStrip.tsx.
+ * No padding override: the band takes the standard --spacing-section-y, which
+ * is where its ~461px comes from. The dark surface already sets position:
+ * relative, but the runway depends on it, so it is set here too rather than
+ * inherited from a rule that could change for unrelated reasons. */
+.open-source-strip {
+ position: relative;
+}
+.open-source-strip-eyebrow {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ font-weight: 700;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--color-signal);
+ margin: 0 0 22px;
+}
+.open-source-strip-headline {
+ /* --font-display is available here now: the italic this line used to
+ carry is gone, and Archivo Black has no italic. */
+ font-family: var(--font-display);
+ font-weight: 400;
+ font-size: clamp(56px, 8.5vw, 116px);
+ line-height: 0.9;
+ letter-spacing: -0.04em;
+ color: var(--color-text-primary);
+ margin: 0;
+}
+.open-source-strip-actions {
+ display: flex;
+ align-items: center;
+ gap: 20px;
+ margin-top: 38px;
+}
+.open-source-strip-licence {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--color-text-muted);
+ white-space: nowrap;
+}
+/* The runway centreline. A fixed 46px dash on a 92px period rather than a
+ * proportional one, so it reads as the same painted marking at every width. */
+.open-source-strip-runway {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 30px;
+ height: 6px;
+ background: repeating-linear-gradient(
+ 90deg,
+ rgba(255, 175, 0, 0.5) 0 46px,
+ transparent 46px 92px
+ );
+}
+@media (max-width: 640px) {
+ .open-source-strip-headline {
+ font-size: clamp(44px, 13vw, 56px);
+ }
+ .open-source-strip-actions {
+ flex-wrap: wrap;
+ gap: 14px;
+ }
+}
+```
+
+- [ ] **Step 2: Confirm nothing dangles**
+
+```bash
+grep -n "open-source-strip-inner\|open-source-strip-line" apps/website/src/styles/landing.css apps/website/src/components/landing/OpenSourceStrip.tsx || echo "clean"
+```
+
+Expected: `clean`
+
+- [ ] **Step 3: Test, lint, build**
+
+```bash
+npx nx test website && npx nx lint website && npx nx build website
+```
+
+Expected: all pass, lint 0 errors.
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add apps/website/src/styles/landing.css
+git commit -m "feat(website): display headline, amber action and runway centreline
+
+Co-Authored-By: Claude Opus 5 "
+```
+
+---
+
+## Task 4: Verification
+
+- [ ] **Step 1: The CI commands**
+
+```bash
+npx nx test website
+npx nx lint website
+npx nx build website
+```
+
+All pass; lint 0 errors (68 warnings is the current baseline).
+
+- [ ] **Step 2: The guard that must not move**
+
+```bash
+npx nx e2e website
+```
+
+Free port 3000 first if a dev server holds it. `website.spec.ts:48` asserts the spine order including `open-source-heading`. It must pass **unchanged** — if you find yourself editing the e2e spec, stop, because something drifted that was not meant to.
+
+- [ ] **Step 3: Measure it in a real browser**
+
+Start the dev server through the Browser pane (`website-dev` in `.claude/launch.json`). If a server is already running, **restart it** — a stale dev server serving an old bundle has cost this project real time before. Then at a 1440px viewport:
+
+```javascript
+const s = document.querySelector('.open-source-strip');
+const h = document.querySelector('.open-source-strip-headline');
+const r = document.querySelector('.open-source-strip-runway');
+const proofH = document.querySelector('#proof-heading');
+({ bandH: Math.round(s.getBoundingClientRect().height),
+ headlineSize: getComputedStyle(h).fontSize,
+ headlineFont: getComputedStyle(h).fontFamily,
+ headlineLines: Math.round(h.getBoundingClientRect().height / parseFloat(getComputedStyle(h).lineHeight)),
+ headlineLeft: Math.round(h.getBoundingClientRect().left),
+ proofHeadingLeft: Math.round(proofH.getBoundingClientRect().left),
+ runwaySpansSection: Math.round(r.getBoundingClientRect().width) === Math.round(s.getBoundingClientRect().width),
+ tight: s.getAttribute('data-tight') })
+```
+
+Expected: `bandH` ≈ 440–470, `headlineSize` `116px`, `headlineFont` containing `Archivo Black`, `headlineLines` **1**, `headlineLeft` **equal to** `proofHeadingLeft` (the band shares every other section's left edge), `runwaySpansSection` **true**, `tight` **null**.
+
+- [ ] **Step 4: Check the phone**
+
+Emulate 390px wide, reload, then:
+
+```javascript
+const h = document.querySelector('.open-source-strip-headline');
+({ lines: Math.round(h.getBoundingClientRect().height / parseFloat(getComputedStyle(h).lineHeight)),
+ overflow: document.documentElement.scrollWidth > document.documentElement.clientWidth })
+```
+
+Expected: `lines` **1**, `overflow` **false**.
+
+- [ ] **Step 5: Look at it**
+
+Screenshot the band and the block beneath it in one frame. Confirm by eye that the runway reads as a marking rather than as a border between the two sections, and that the amber fork button and `TeamsBlock`'s amber "Talk to an engineer" below it are both legible as separate asks. Both were flagged during design and accepted — you are confirming they look as accepted, not re-litigating them.
+
+- [ ] **Step 6: Commit any fixes**
+
+```bash
+git add -A
+git commit -m "fix(website): Fork us band verification pass
+
+Co-Authored-By: Claude Opus 5 "
+```
+
+---
+
+## Out of scope
+
+- The section does not move in `page.tsx`.
+- `FinalCTA` is untouched.
+- No repository stats. Rejected during design as more text, against the brief.
diff --git a/docs/superpowers/specs/2026-09-08-fork-us-band-design.md b/docs/superpowers/specs/2026-09-08-fork-us-band-design.md
new file mode 100644
index 000000000..1599050dd
--- /dev/null
+++ b/docs/superpowers/specs/2026-09-08-fork-us-band-design.md
@@ -0,0 +1,79 @@
+# The open-source strip becomes the "Fork us" band
+
+**Date:** 2026-09-08
+**Status:** Approved, ready for planning
+**Component:** `apps/website/src/components/landing/OpenSourceStrip.tsx`
+
+## 1. Why
+
+The homepage's open-source beat is a 204px strip carrying one 23px sentence: "Yes, this is all free. Don't like something? *Fork us.*" It was built deliberately quiet — its docblock says the point is "there is no catch and no upsell," so it avoids reading as a second pitch.
+
+That restraint is now being spent. The site has an ATC aviation identity (see `2026-09-08-preflight-checklist-design.md` and the aviation-yellow theme), and the open-source offer is one of the strongest things Threadplane has to say. It should be said loudly, in the identity's voice, in as few words as possible.
+
+**This reverses a prior intentional decision.** It is recorded here so a future reader does not "fix" it back.
+
+## 2. Shape
+
+A dark band, left-aligned to the page container, three stacked elements and one aviation device:
+
+```
+SQUAWK 1200 ← mono, amber, 11px, .18em
+
+Fork us. ← Archivo Black, clamp(56px, 8.5vw, 116px)
+
+[⚇ Fork on GitHub] MIT ← amber button, mono licence beside it
+
+- - - - - - - - - - - - - - ← runway centreline, full-bleed
+```
+
+**Copy.** `OPEN_SOURCE_STRIP` reshapes from `{lead, emphasis, licence, cta}` to:
+
+| Key | Value |
+| --- | --- |
+| `eyebrow` | `Squawk 1200` |
+| `headline` | `Fork us.` |
+| `licence` | `MIT` |
+| `cta` | `Fork on GitHub` |
+
+`lead` and `emphasis` retire, and the `` goes with them. That matters: the current CSS comment justifies `--font-sans` here on the grounds that the line "carries an italic ``, and Archivo Black has no italic." With the italic gone, the headline takes `--font-display`.
+
+**Squawk 1200** is the US transponder code for VFR flight not receiving ATC services — flying with nobody controlling you. It is the aviation term for "no permission required," which is exactly the offer. It is US-specific (Europe squawks 7000) and a non-pilot will read it as texture rather than meaning; that is accepted, because the headline carries the whole meaning on its own.
+
+**The runway centreline** is the section's one aviation device: a dashed amber rule near the band's bottom edge, spanning the **full section width, not the container's** — it is absolutely positioned against the `Section` and therefore ignores the container gutters, so the marking runs edge to edge like paint on a real runway. That means the element sits outside `` in the markup, with the `Section` as its positioning context. Pure CSS `repeating-linear-gradient` — 46px dash on a 92px period, 6px tall, `rgba(255, 175, 0, .5)` — not an SVG. The dash rhythm is fixed rather than proportional, so it reads as the same painted marking at every viewport.
+
+## 3. Metrics
+
+| | Today | New |
+| --- | --- | --- |
+| Band height (1440px) | 204px | ~461px |
+| Headline | 23px `--font-sans` | clamp(56px, 8.5vw, 116px) `--font-display` |
+| Elements | 1 sentence + pill + outlined button | eyebrow + headline + amber button + licence |
+
+The height comes free: `Section` drops `tight`, so the standard `--spacing-section-y` of `clamp(64px, 8vw, 120px)` applies. **No custom padding override** — the band uses the same rhythm as every other section, which is one less thing to maintain and the reason `tight`'s existing two-attribute override can be deleted outright.
+
+## 4. Accessibility
+
+- `
` keeps its id and becomes **"Fork us."** The section's accessible name goes from a full sentence to two words, which is clearer. The id is pinned by the e2e spine (`website.spec.ts:56`), so that test stays green untouched.
+- The eyebrow stays a readable `
` rather than `aria-hidden`, matching how `SectionHeader` treats "Climb performance". Hiding text that sighted users can read is worse than letting a screen reader say a term it cannot decode — and the headline immediately after carries the meaning regardless.
+- The runway is decorative and carries `aria-hidden="true"`.
+- Nothing animates, so there is no reduced-motion branch.
+
+## 5. Known, accepted
+
+Both were raised against rendered mockups and decided deliberately:
+
+- **Two amber CTAs sit ~200px apart.** The amber "Fork on GitHub" lands directly above `TeamsBlock`'s amber "Talk to an engineer," so the fork link draws equal visual weight to the commercial ask. The alternative — an outlined white button — was offered and declined in favour of the louder treatment.
+- **The band's lower third is empty:** 84px between the action row and the runway, then 30px below it. Pulling the runway up to the content, and trimming the band, were both offered and declined. The airiness is intentional on a full-stop band.
+
+## 6. Out of scope
+
+- The section **does not move** in `page.tsx`. It stays between `Stage` and `TeamsBlock`.
+- `FinalCTA`, which the four library pages still close on, is untouched.
+- No repository stats (stars, contributors, activity). They were considered as an "open-source proof band" direction and rejected: more text, against the brief.
+
+## 7. Verification
+
+- `npx nx test website`, `npx nx lint website`, `npx nx build website` — the CI commands.
+- `npx nx e2e website`, with the spine assertion passing **unchanged**.
+- Measured in a browser, not eyeballed: the headline sits on the container's left edge, level with every other section's left edge; the band clears ~460px at 1440px; the runway spans the full band width.
+- At 390px the headline holds one line and the action row does not overflow.