-
Field report · 18 pages
+
Field report · {FIELD_REPORT.pages} pages
From Prototype to Production
Six production-readiness dimensions for Angular AI teams.
diff --git a/apps/website/src/lib/field-report.spec.ts b/apps/website/src/lib/field-report.spec.ts
new file mode 100644
index 000000000..75199f335
--- /dev/null
+++ b/apps/website/src/lib/field-report.spec.ts
@@ -0,0 +1,53 @@
+import { existsSync, readFileSync } from 'node:fs';
+import { join, resolve } from 'node:path';
+import { describe, it, expect } from 'vitest';
+import { FIELD_REPORT } from './field-report';
+
+// Anchored to the workspace root the way the other file-reading specs here
+// are, rather than to a bare relative path: both `nx test website` and
+// `vitest --root apps/website` run with the repo root as the working
+// directory, and jsdom makes `import.meta.url` an http: URL, so neither a
+// cwd-relative path nor a module-relative one resolves the file.
+const findWorkspaceRoot = (): string => {
+ let directory = process.cwd();
+ while (directory !== resolve(directory, '..')) {
+ if (existsSync(join(directory, 'nx.json'))) return directory;
+ directory = resolve(directory, '..');
+ }
+ throw new Error('workspace root (nx.json) not found');
+};
+
+const PDF = join(findWorkspaceRoot(), 'apps/website/public/whitepaper.pdf');
+
+describe('FIELD_REPORT', () => {
+ it('declares the page count the PDF actually has', () => {
+ // The homepage advertised "18 pages" for a 17-page document, in exchange
+ // for an email address. Counting the page objects in the file itself is
+ // dependency-free and means regenerating the PDF at a different length
+ // fails here instead of silently making the page lie.
+ const pdf = readFileSync(PDF, 'latin1');
+ const pages = (pdf.match(/\/Type\s*\/Page[^s]/g) || []).length;
+ expect(pages).toBeGreaterThan(0);
+ expect(FIELD_REPORT.pages).toBe(pages);
+ });
+
+ it('lists the six chapters the document actually contains', () => {
+ // Not machine-checkable: the text lives in compressed streams. Verify by
+ // hand with the command in the module's docblock when the PDF changes.
+ expect(FIELD_REPORT.chapters).toEqual([
+ 'Streaming State Management',
+ 'Thread Persistence',
+ 'Tool-Call Rendering',
+ 'Human Approval Flows',
+ 'Generative UI',
+ 'Deterministic Testing',
+ ]);
+ });
+
+ it('carries the cover strings the document prints', () => {
+ expect(FIELD_REPORT.title).toBe('From Prototype to Production');
+ expect(FIELD_REPORT.kicker).toBe('Threadplane · Open source · Angular');
+ expect(FIELD_REPORT.subtitle).toBeTruthy();
+ expect(FIELD_REPORT.year).toBe('2026');
+ });
+});
diff --git a/apps/website/src/lib/field-report.ts b/apps/website/src/lib/field-report.ts
new file mode 100644
index 000000000..de00ef6ff
--- /dev/null
+++ b/apps/website/src/lib/field-report.ts
@@ -0,0 +1,35 @@
+/**
+ * The field report's own facts, in one place.
+ *
+ * These exist as data because the site got them wrong: it advertised "18
+ * pages" for a 17-page document, and described the contents as "Error
+ * boundaries, fallbacks, observability, deploy" — three phrases that return
+ * zero matches in the file. Anything the site says about the document now
+ * reads from here, and `field-report.spec.ts` pins the page count to the PDF
+ * itself.
+ *
+ * To re-verify after regenerating the PDF:
+ *
+ * pdftotext -f 1 -l 2 apps/website/public/whitepaper.pdf - | sed -n '1,40p'
+ */
+export const FIELD_REPORT = {
+ /** Printed on the cover. */
+ title: 'From Prototype to Production',
+ /** The cover's header rule. */
+ kicker: 'Threadplane · Open source · Angular',
+ /** The cover's standfirst, verbatim. */
+ subtitle: 'Production-ready chat, threads, and generative UI for AI agents.',
+ /** The cover's date line. */
+ year: '2026',
+ /** Verified against the file, and asserted against it in the spec. */
+ pages: 17,
+ /** The table of contents, in document order. */
+ chapters: [
+ 'Streaming State Management',
+ 'Thread Persistence',
+ 'Tool-Call Rendering',
+ 'Human Approval Flows',
+ 'Generative UI',
+ 'Deterministic Testing',
+ ],
+} as const;
diff --git a/apps/website/src/styles/landing.css b/apps/website/src/styles/landing.css
index df54913be..b7c640d3d 100644
--- a/apps/website/src/styles/landing.css
+++ b/apps/website/src/styles/landing.css
@@ -355,162 +355,6 @@
}
}
-/* .pilot-* — components/landing/TeamsBlock.tsx (rules predate the rename;
- * PilotBlock.tsx was retired by the homepage restructure). */
-.pilot-block-grid {
- display: grid;
- grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
- gap: 64px;
- align-items: center;
-}
-.pilot-eyebrow {
- margin-bottom: 16px;
-}
-.pilot-rail {
- display: flex;
- align-items: baseline;
- gap: 14px;
-}
-.pilot-rail-line {
- flex: 1;
- height: 1px;
- background: var(--color-border);
-}
-.pilot-heading {
- font-family: var(--font-display);
- font-size: var(--text-h2);
- line-height: var(--text-h2--line-height);
- color: var(--color-text-primary);
- margin: 0;
- margin-bottom: 20px;
- letter-spacing: -0.015em;
-}
-.pilot-subhead {
- font-family: var(--font-sans);
- font-size: var(--text-body-lg);
- line-height: var(--text-body-lg--line-height);
- color: var(--color-text-secondary);
- margin: 0;
- margin-bottom: 24px;
-}
-.pilot-cta-row {
- display: flex;
- gap: 12px;
- flex-wrap: wrap;
-}
-.pilot-rows {
- margin: 18px 0 4px;
- border-top: 2px solid var(--color-text-primary);
- max-width: 44ch;
-}
-.pilot-row {
- display: grid;
- grid-template-columns: 1fr auto;
- gap: 16px;
- align-items: baseline;
- border-bottom: 1px solid var(--color-border);
- padding: 9px 0;
-}
-.pilot-row-claim {
- font-family: var(--font-sans);
- font-size: 15px;
- line-height: 1.45;
- color: var(--color-text-primary);
- margin: 0;
-}
-.pilot-row-tail {
- font-family: var(--font-mono);
- font-size: 11.5px;
- color: var(--color-text-muted);
- white-space: nowrap;
- margin: 0;
-}
-.pilot-steps {
- border-top: 2px solid var(--color-text-primary);
-}
-.pilot-step {
- display: grid;
- grid-template-columns: 64px 1fr;
- gap: 16px;
- align-items: start;
- padding: 14px 0;
- border-bottom: 1px solid var(--color-border);
-}
-.pilot-step:last-child {
- border-bottom: none;
-}
-.pilot-step-num {
- font-family: var(--font-display);
- font-size: 44px;
- line-height: 0.9;
- letter-spacing: -0.03em;
- color: var(--color-border);
-}
-.pilot-step-title {
- font-family: var(--font-sans);
- font-size: 15.5px;
- font-weight: 600;
- color: var(--color-text-primary);
-}
-.pilot-step-body {
- font-family: var(--font-sans);
- font-size: 14px;
- line-height: 1.5;
- color: var(--color-text-secondary);
- margin-top: 2px;
-}
-
-/* TeamsBlock: phases stacked over the field report card. */
-.teams-block-grid {
- align-items: start;
-}
-.teams-aside {
- display: flex;
- flex-direction: column;
- gap: 28px;
-}
-.teams-report {
- padding: 22px 24px;
- border: 1px solid var(--color-border);
- border-radius: 12px;
- background: var(--color-canvas);
-}
-.teams-report-badge {
- font-family: var(--font-mono);
- font-size: 11px;
- letter-spacing: 0.1em;
- text-transform: uppercase;
- color: var(--color-text-muted);
- margin: 6px 0 0;
-}
-.teams-report-title {
- font-family: var(--font-display);
- font-size: 26px;
- line-height: 1.1;
- margin: 8px 0 6px;
- color: var(--color-text-primary);
-}
-.teams-report-desc {
- font-family: var(--font-sans);
- font-size: 14px;
- line-height: 1.5;
- color: var(--color-text-secondary);
- margin: 0 0 16px;
-}
-@media (max-width: 900px) {
- .pilot-block-grid {
- /* minmax(0,…): bare 1fr keeps an auto minimum, so one wide child pushed
- * the stacked column past the viewport (findings §2). */
- grid-template-columns: minmax(0, 1fr) !important;
- gap: 40px !important;
- }
-}
-@media (max-width: 640px) {
- .pilot-row {
- grid-template-columns: 1fr;
- gap: 2px;
- }
-}
/* RecentArticles — components/landing/RecentArticles.tsx */
.recent-articles-rail {
@@ -2484,3 +2328,152 @@ a.preflight-row:hover .preflight-challenge {
gap: 24px;
}
}
+
+/* For teams — components/landing/TeamsBlock.tsx.
+ * Ask left, briefing right. The cover is a fixed 400px so the form column
+ * keeps a comfortable measure rather than stretching across the section. */
+.teams-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 400px;
+ gap: 64px;
+ /* Top, not centre. The cover is taller than the ask, so centring pushed the
+ * form down by half the difference (measured: 43px) and made the position of
+ * the section's primary action depend on how many chapters the document has. */
+ align-items: start;
+}
+.teams-eyebrow {
+ margin-bottom: 18px;
+}
+.teams-eyebrow-meta {
+ color: var(--color-text-muted);
+}
+.teams-heading {
+ font-family: var(--font-display);
+ font-weight: 400;
+ font-size: clamp(36px, 4.2vw, 54px);
+ line-height: 1.02;
+ letter-spacing: -0.03em;
+ color: var(--color-text-primary);
+ margin: 0 0 30px;
+ max-width: 16ch;
+}
+.teams-contact {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: baseline;
+ gap: 14px;
+ margin-top: 40px;
+ padding-top: 24px;
+ border-top: 1px solid var(--color-border);
+}
+.teams-contact-copy {
+ margin: 0;
+ font-size: 15px;
+ line-height: 1.6;
+ color: var(--color-text-secondary);
+}
+.teams-contact-link {
+ font-size: 15px;
+ font-weight: 600;
+ color: var(--color-accent);
+ text-decoration: none;
+ white-space: nowrap;
+}
+.teams-contact-link:hover {
+ text-decoration: underline;
+}
+
+/* The briefing preview — components/landing/FieldReportCover.tsx.
+ * .wp-paper supplies the tilt, border and shadow; these rules only style the
+ * content the library pages' version does not carry. */
+.field-report-cover {
+ display: flex;
+ justify-content: center;
+ min-width: 0;
+}
+.field-report-paper {
+ width: 100%;
+ max-width: 390px;
+}
+.field-report-kicker {
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--color-text-muted);
+ margin: 0 0 14px;
+ padding-bottom: 14px;
+ border-bottom: 1px solid var(--color-border);
+}
+.field-report-title {
+ font-family: var(--font-display);
+ font-weight: 400;
+ font-size: 25px;
+ line-height: 1.1;
+ letter-spacing: -0.02em;
+ color: var(--color-text-primary);
+ margin: 0 0 6px;
+}
+.field-report-sub {
+ font-size: 12.5px;
+ line-height: 1.5;
+ color: var(--color-text-secondary);
+ margin: 0 0 20px;
+}
+.field-report-toc-label {
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--color-text-muted);
+ margin: 0 0 10px;
+}
+.field-report-toc {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+.field-report-toc li {
+ display: flex;
+ align-items: baseline;
+ gap: 10px;
+ padding: 6px 0;
+ font-size: 12.5px;
+ color: var(--color-text-primary);
+}
+.field-report-toc li + li {
+ border-top: 1px dotted var(--color-border);
+}
+.field-report-num {
+ flex: 0 0 auto;
+ font-family: var(--font-mono);
+ font-size: 10px;
+ font-weight: 700;
+ /* NOT --color-signal. The ATC theme split the accent role precisely to keep
+ * that yellow out of text: #FFAF00 is 1.78:1 on this surface, where these
+ * markers are 10px. --color-accent is the interactive ink, at 14.86:1. */
+ color: var(--color-accent);
+}
+.field-report-foot {
+ display: flex;
+ justify-content: space-between;
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--color-text-muted);
+ border-top: 1px solid var(--color-border);
+ padding-top: 12px;
+ margin: 20px 0 0;
+}
+@media (max-width: 900px) {
+ .teams-grid {
+ /* minmax(0,…) not 1fr: a bare 1fr keeps an auto minimum, and the cover
+ would push the stacked column past the viewport. */
+ grid-template-columns: minmax(0, 1fr);
+ gap: 44px;
+ }
+ .field-report-cover {
+ justify-content: flex-start;
+ }
+}
diff --git a/docs/superpowers/plans/2026-09-08-teams-briefing.md b/docs/superpowers/plans/2026-09-08-teams-briefing.md
new file mode 100644
index 000000000..84c52e7bb
--- /dev/null
+++ b/docs/superpowers/plans/2026-09-08-teams-briefing.md
@@ -0,0 +1,819 @@
+# Teams Briefing Block 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:** Rebuild the homepage "For teams" section so the field-report form sits near the top beside a preview of the actual document, with contact demoted to a link — and correct the two false claims the section makes about that document.
+
+**Architecture:** The document's facts (page count, six chapter titles, cover strings) move into one data module read by both the eyebrow and a new `FieldReportCover` component, so the page cannot describe the PDF differently from what the PDF is. A test asserts the declared page count against the PDF bytes. `TeamsBlock` loses the duplicated pilot timeline and outcome rows and becomes a two-column ask-plus-artifact.
+
+**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-teams-briefing-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, so when it goes red run vitest directly alongside it to read the failure, then trust nx for the verdict. `nx test` does **not** type-check; `nx build` does.
+
+**Free the ports before `nx e2e`.** Two separate e2e failures on the previous change were port 4308 held by an orphaned server and the Browser pane's dev server holding `apps/website/.next/dev/lock` — neither was a test defect. Run `lsof -ti:3000,4308 | xargs kill -9` and stop any preview server first.
+
+**Facts about the PDF, already verified — do not re-derive, and do not "correct" them:**
+
+- `apps/website/public/whitepaper.pdf` is **17 pages**. Both `pdfinfo` and counting `/Type /Page` objects agree. The site currently claims 18 in three places.
+- The six chapters are **Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing**.
+- The cover reads `THREADPLANE · OPEN SOURCE · ANGULAR`, then *"Production-ready chat, threads, and generative UI for AI agents"*, then `threadplane.ai · 2026`.
+- The strings the site currently advertises — "Error boundaries, fallbacks, observability" — return **zero** matches in the document.
+
+**`.pilot-eyebrow` is shared with `/pilot-to-prod`.** Every other `.pilot-*` class belongs to `TeamsBlock` alone and can go. Deleting `.pilot-eyebrow` would break another page.
+
+**Do not touch:** the section's `id="teams"`, its `tinted` surface, its position in `page.tsx`, `WhitePaperForm` itself, or the analytics keys `home_whitepaper`, `teams-block`, `hero_talk_to_engineers`.
+
+**`WhitePaperForm` already renders its own disclosure and an "Already on the list?" direct-download link.** Do not add fine print of your own beneath the form.
+
+---
+
+## File Structure
+
+| File | Responsibility | Change |
+| --- | --- | --- |
+| `apps/website/src/lib/field-report.ts` | The document's facts, single source | **Create** |
+| `apps/website/src/lib/field-report.spec.ts` | Asserts the page count against the PDF | **Create** |
+| `apps/website/src/components/landing/FieldReportCover.tsx` | The briefing preview | **Create** |
+| `apps/website/src/components/landing/FieldReportCover.spec.tsx` | Cover render guards | **Create** |
+| `apps/website/src/components/landing/TeamsBlock.tsx` | The section | Rewritten body |
+| `apps/website/src/components/landing/TeamsBlock.spec.tsx` | Section guards | Rewritten |
+| `apps/website/src/components/landing/WhitePaperBlock.tsx` | Library-page block | Copy corrections only |
+| `apps/website/src/styles/landing.css` | Styles | New rules in, `.pilot-*`/`.teams-report-*` out |
+| `apps/website/e2e/website.spec.ts:57` | Homepage spine | One id renamed |
+
+---
+
+## Task 1: The document's facts
+
+**Files:**
+- Create: `apps/website/src/lib/field-report.ts`
+- Create: `apps/website/src/lib/field-report.spec.ts`
+
+- [ ] **Step 1: Write the failing spec**
+
+Create `apps/website/src/lib/field-report.spec.ts`:
+
+```ts
+import { readFileSync } from 'node:fs';
+import { describe, it, expect } from 'vitest';
+import { FIELD_REPORT } from './field-report';
+
+describe('FIELD_REPORT', () => {
+ it('declares the page count the PDF actually has', () => {
+ // The homepage advertised "18 pages" for a 17-page document, in exchange
+ // for an email address. Counting the page objects in the file itself is
+ // dependency-free and means regenerating the PDF at a different length
+ // fails here instead of silently making the page lie.
+ const pdf = readFileSync('public/whitepaper.pdf', 'latin1');
+ const pages = (pdf.match(/\/Type\s*\/Page[^s]/g) || []).length;
+ expect(pages).toBeGreaterThan(0);
+ expect(FIELD_REPORT.pages).toBe(pages);
+ });
+
+ it('lists the six chapters the document actually contains', () => {
+ // Not machine-checkable: the text lives in compressed streams. Verify by
+ // hand with the command in the module's docblock when the PDF changes.
+ expect(FIELD_REPORT.chapters).toEqual([
+ 'Streaming State Management',
+ 'Thread Persistence',
+ 'Tool-Call Rendering',
+ 'Human Approval Flows',
+ 'Generative UI',
+ 'Deterministic Testing',
+ ]);
+ });
+
+ it('carries the cover strings the document prints', () => {
+ expect(FIELD_REPORT.title).toBe('From Prototype to Production');
+ expect(FIELD_REPORT.kicker).toBe('Threadplane · Open source · Angular');
+ expect(FIELD_REPORT.subtitle).toBeTruthy();
+ expect(FIELD_REPORT.year).toBe('2026');
+ });
+});
+```
+
+**Correction, found during execution:** the path above does not resolve. `readFileSync` anchors to `process.cwd()`, which is the **repo root** under both `nx test` and `vitest --root apps/website`; and `import.meta.url` is no help either, because jsdom makes it an `http:` URL. Anchor to the workspace root by walking up to `nx.json`, the way `cockpit-docs-links.spec.ts` in the same directory already does. The shipped spec has the working version.
+
+- [ ] **Step 2: Run it and watch it fail**
+
+```bash
+npx nx test website
+```
+
+Expected: FAIL — `./field-report` does not exist.
+
+- [ ] **Step 3: Create the module**
+
+Create `apps/website/src/lib/field-report.ts`:
+
+```ts
+/**
+ * The field report's own facts, in one place.
+ *
+ * These exist as data because the site got them wrong: it advertised "18
+ * pages" for a 17-page document, and described the contents as "Error
+ * boundaries, fallbacks, observability, deploy" — three phrases that return
+ * zero matches in the file. Anything the site says about the document now
+ * reads from here, and `field-report.spec.ts` pins the page count to the PDF
+ * itself.
+ *
+ * To re-verify after regenerating the PDF:
+ *
+ * pdftotext -f 1 -l 2 apps/website/public/whitepaper.pdf - | sed -n '1,40p'
+ */
+export const FIELD_REPORT = {
+ /** Printed on the cover. */
+ title: 'From Prototype to Production',
+ /** The cover's header rule. */
+ kicker: 'Threadplane · Open source · Angular',
+ /** The cover's standfirst, verbatim. */
+ subtitle: 'Production-ready chat, threads, and generative UI for AI agents.',
+ /** The cover's date line. */
+ year: '2026',
+ /** Verified against the file, and asserted against it in the spec. */
+ pages: 17,
+ /** The table of contents, in document order. */
+ chapters: [
+ 'Streaming State Management',
+ 'Thread Persistence',
+ 'Tool-Call Rendering',
+ 'Human Approval Flows',
+ 'Generative UI',
+ 'Deterministic Testing',
+ ],
+} as const;
+```
+
+- [ ] **Step 4: Run it**
+
+```bash
+npx nx test website
+```
+
+Expected: PASS, 3 new tests.
+
+- [ ] **Step 5: Mutation-test the page guard**
+
+The guard is the point of this task, so prove it bites. Temporarily change `pages: 17` to `pages: 18` and run:
+
+```bash
+npx vitest run --root apps/website src/lib/field-report.spec.ts
+```
+
+Expected: FAIL with `expected 18 to be 17`. **Restore `17`** and confirm green before committing.
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add apps/website/src/lib/field-report.ts apps/website/src/lib/field-report.spec.ts
+git commit -m "feat(website): the field report's facts, pinned to the PDF
+
+Co-Authored-By: Claude Opus 5
"
+```
+
+---
+
+## Task 2: The briefing preview
+
+**Files:**
+- Create: `apps/website/src/components/landing/FieldReportCover.tsx`
+- Create: `apps/website/src/components/landing/FieldReportCover.spec.tsx`
+
+- [ ] **Step 1: Write the failing spec**
+
+Create `apps/website/src/components/landing/FieldReportCover.spec.tsx`:
+
+```tsx
+// @vitest-environment jsdom
+import { render, screen } from '@testing-library/react';
+import { describe, it, expect } from 'vitest';
+import { FieldReportCover } from './FieldReportCover';
+import { FIELD_REPORT } from '../../lib/field-report';
+
+describe('FieldReportCover', () => {
+ it('shows every chapter, in order', () => {
+ const { container } = render();
+ const items = Array.from(container.querySelectorAll('.field-report-toc li'));
+ expect(items.map((li) => li.textContent?.replace(/^\d+/, '').trim())).toEqual([
+ ...FIELD_REPORT.chapters,
+ ]);
+ });
+
+ it('is readable rather than decorative', () => {
+ // The old .wp-cover-wrap is aria-hidden because it is artwork. This one
+ // carries the table of contents, which is the reason to download — hiding
+ // it would withhold the substance from screen reader users.
+ const { container } = render();
+ expect(container.querySelector('[aria-hidden="true"].field-report-paper')).toBeNull();
+ expect(screen.getByRole('heading', { level: 3 }).textContent).toBe(FIELD_REPORT.title);
+ expect(container.querySelector('ol.field-report-toc')).toBeTruthy();
+ });
+
+ it('does not read the numbers twice', () => {
+ // The already numbers the list for assistive tech; the drawn 01/02
+ // markers are visual duplicates and must be hidden.
+ const { container } = render();
+ const nums = Array.from(container.querySelectorAll('.field-report-num'));
+ expect(nums).toHaveLength(FIELD_REPORT.chapters.length);
+ for (const n of nums) expect(n.getAttribute('aria-hidden')).toBe('true');
+ });
+
+ it('borrows the library pages’ paper styling', () => {
+ // Same object the four library pages already show, so it is not a second
+ // visual language for the same artifact.
+ const { container } = render();
+ expect(container.querySelector('.wp-paper.field-report-paper')).toBeTruthy();
+ });
+});
+```
+
+- [ ] **Step 2: Run and watch it fail**
+
+```bash
+npx nx test website
+```
+
+Expected: FAIL — `./FieldReportCover` does not exist.
+
+- [ ] **Step 3: Create the component**
+
+Create `apps/website/src/components/landing/FieldReportCover.tsx`:
+
+```tsx
+import { FIELD_REPORT } from '../../lib/field-report';
+
+/**
+ * The field report rendered as the object it is, contents included.
+ *
+ * It reuses `.wp-paper` — the tilted card the library pages already show —
+ * so the same artifact does not get two visual languages, and adds its own
+ * classes for the content the library version does not have.
+ *
+ * Deliberately NOT aria-hidden. The library page's cover is artwork and hides
+ * itself; this one prints the table of contents, which is the reason anyone
+ * gives up an email address for it.
+ */
+export function FieldReportCover() {
+ return (
+
+
+
+
{FIELD_REPORT.kicker}
+
{FIELD_REPORT.title}
+
{FIELD_REPORT.subtitle}
+
Contents
+
+ {FIELD_REPORT.chapters.map((chapter, i) => (
+ -
+
+ {String(i + 1).padStart(2, '0')}
+
+ {chapter}
+
+ ))}
+
+
+
+ threadplane.ai
+ {FIELD_REPORT.year}
+
+
+
+ );
+}
+```
+
+- [ ] **Step 4: Run it**
+
+```bash
+npx nx test website
+```
+
+Expected: PASS.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add apps/website/src/components/landing/FieldReportCover.tsx apps/website/src/components/landing/FieldReportCover.spec.tsx
+git commit -m "feat(website): the briefing preview shows the real contents
+
+Co-Authored-By: Claude Opus 5 "
+```
+
+---
+
+## Task 3: The section
+
+**Files:**
+- Modify: `apps/website/src/components/landing/TeamsBlock.tsx`
+- Modify: `apps/website/src/components/landing/TeamsBlock.spec.tsx`
+
+- [ ] **Step 1: Replace the spec**
+
+Replace the whole `describe('TeamsBlock')` body in `TeamsBlock.spec.tsx` (keep the file's existing imports, the `vi.mock`, and the `formPolicy` fixture exactly as they are, and add `FIELD_REPORT` to the imports):
+
+```tsx
+describe('TeamsBlock', () => {
+ it('leads with the ask: eyebrow, heading, then the form', () => {
+ const { container } = render();
+ const heading = screen.getByRole('heading', { level: 2 });
+ expect(heading.textContent).toBe('What breaks between a demo and production.');
+ expect(heading.id).toBe('field-report-heading');
+ expect(container.querySelectorAll('form')).toHaveLength(1);
+ expect(screen.getByLabelText('Work email')).toBeTruthy();
+ expect(screen.getByText(formPolicy.disclosures.whitepaper)).toBeTruthy();
+ });
+
+ it('derives the page count rather than typing it', () => {
+ // The section claimed "18 pages" for a 17-page document. Reading it from
+ // FIELD_REPORT means the guard against the PDF covers this string too.
+ const { container } = render();
+ const eyebrow = container.querySelector('[data-ui="eyebrow"]');
+ expect(eyebrow?.textContent).toContain(`${FIELD_REPORT.pages} pages`);
+ expect(eyebrow?.textContent).toContain('Preflight briefing');
+ });
+
+ it('shows the briefing beside the ask', () => {
+ const { container } = render();
+ expect(container.querySelector('.field-report-paper')).toBeTruthy();
+ });
+
+ it('makes contact the secondary ask, not a rival button', () => {
+ // Two same-weight buttons is what stopped the old section saying which
+ // ask mattered. The arrow is decorative so the accessible name stays clean.
+ const { container } = render();
+ const link = screen.getByRole('link', { name: 'Talk to an engineer' });
+ expect(link.getAttribute('href')).toBe('/contact?source=home_enterprise&track=enterprise');
+ expect(link.getAttribute('data-ui')).not.toBe('button');
+ // Exactly one button in the whole section, and it belongs to the form.
+ // (SubmitButton wraps Button, so the form's submit carries data-ui too.)
+ const buttons = container.querySelectorAll('[data-ui="button"]');
+ expect(buttons).toHaveLength(1);
+ expect(buttons[0].closest('form')).toBeTruthy();
+ });
+
+ it('no longer repeats the pilot programme the dedicated page owns', () => {
+ const { container } = render();
+ expect(container.querySelectorAll('.pilot-step')).toHaveLength(0);
+ expect(container.querySelectorAll('.pilot-row')).toHaveLength(0);
+ expect(screen.queryByRole('link', { name: 'See the pilot program' })).toBeNull();
+ });
+
+ it('never advertises topics the document does not contain', () => {
+ // "Error boundaries", "fallbacks" and "observability" each return zero
+ // matches in whitepaper.pdf. They were on this page for months.
+ const { container } = render();
+ const text = container.textContent ?? '';
+ for (const phrase of ['Error boundaries', 'fallbacks', 'observability', '18 pages']) {
+ expect(text).not.toContain(phrase);
+ }
+ });
+});
+```
+
+- [ ] **Step 2: Run and watch it fail**
+
+```bash
+npx nx test website
+```
+
+Expected: FAIL — the heading is still "Shipping inside a large Angular platform?" with id `pilot-heading`.
+
+- [ ] **Step 3: Rewrite the component**
+
+Replace the whole of `TeamsBlock.tsx` with:
+
+```tsx
+'use client';
+
+import type { PublicFormPolicy } from '../../lib/growth/form-policy';
+import { Container } from '../ui/Container';
+import { Section } from '../ui/Section';
+import { Eyebrow } from '../ui/Eyebrow';
+import { trackCtaClick } from '../../lib/analytics/client';
+import { WhitePaperForm } from './WhitePaperForm';
+import { FieldReportCover } from './FieldReportCover';
+import { FIELD_REPORT } from '../../lib/field-report';
+
+/**
+ * For teams: the field report is the ask, contact is the follow-up.
+ *
+ * The previous version inverted its own goals — the form sat at y=618 in a
+ * 916px section, below a four-step pilot timeline that `/pilot-to-prod`
+ * already covers in full, while a same-weight amber button sent people to
+ * contact instead. The timeline and the outcome rows are gone, the form sits
+ * beside a preview of the actual document, and contact is a link.
+ *
+ * Everything the section says about the report reads from FIELD_REPORT, which
+ * is pinned to the PDF — this block previously advertised a page count and a
+ * contents list that the file did not match.
+ */
+export function TeamsBlock({ formPolicy }: { formPolicy: PublicFormPolicy }) {
+ return (
+
+
+
+
+
+ Preflight briefing
+
+ {' '}· {FIELD_REPORT.pages} pages · free
+
+
+
+ What breaks between a demo and production.
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+The `TIMELINE` and `OUTCOMES` consts are deleted with the old body.
+
+- [ ] **Step 4: Run it**
+
+```bash
+npx nx test website
+```
+
+Expected: PASS.
+
+- [ ] **Step 5: Update the e2e spine**
+
+In `apps/website/e2e/website.spec.ts:57`, change `'pilot-heading',` to `'field-report-heading',`. **This is the only test edit this change makes** — the spine catches accidental removals, and this rename is deliberate.
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add apps/website/src/components/landing/TeamsBlock.tsx apps/website/src/components/landing/TeamsBlock.spec.tsx apps/website/e2e/website.spec.ts
+git commit -m "feat(website): For teams leads with the briefing, not the pilot timeline
+
+Co-Authored-By: Claude Opus 5 "
+```
+
+---
+
+## Task 4: The same false claims on the library pages
+
+**Files:**
+- Modify: `apps/website/src/components/landing/WhitePaperBlock.tsx:7-11,57`
+
+`WhitePaperBlock` is used by the four library pages and repeats both errors. Correcting only the homepage would knowingly leave them live.
+
+- [ ] **Step 1: Fix the rows and the badge**
+
+Add the import:
+
+```tsx
+import { FIELD_REPORT } from '../../lib/field-report';
+```
+
+Replace `ROWS` with:
+
+```tsx
+const ROWS = [
+ { claim: 'Six production-readiness dimensions', tail: `${FIELD_REPORT.pages} pages` },
+ // Was "Error boundaries, fallbacks, observability, deploy" — three phrases
+ // with zero matches in whitepaper.pdf. These are the document's actual
+ // chapters.
+ { claim: 'Streaming, persistence, tool calls, approvals, generative UI, testing', tail: 'the six chapters' },
+ { claim: 'No vendor pitch — what we learned shipping it', tail: 'free' },
+];
+```
+
+and the cover badge (line 57) with:
+
+```tsx
+ Field report · {FIELD_REPORT.pages} pages
+```
+
+Leave the third row alone. Its wording is questioned in spec §9 and is the owner's call, not a copy edit.
+
+- [ ] **Step 2: Test, lint, build**
+
+```bash
+npx nx test website && npx nx lint website && npx nx build website
+```
+
+Expected: all pass, 0 lint errors. `WhitePaperBlock.spec.tsx` does not assert any of the changed strings, so it needs no edit.
+
+- [ ] **Step 3: Confirm the claims are gone site-wide**
+
+```bash
+grep -rn "18 pages\|Error boundaries, fallbacks" apps/website/src || echo "clean"
+```
+
+Expected: `clean`
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add apps/website/src/components/landing/WhitePaperBlock.tsx
+git commit -m "fix(website): the library pages described a document we do not send
+
+Co-Authored-By: Claude Opus 5 "
+```
+
+---
+
+## Task 5: The styles
+
+**Files:**
+- Modify: `apps/website/src/styles/landing.css`
+
+- [ ] **Step 1: Add the new rules**
+
+Append to `landing.css`:
+
+```css
+/* For teams — components/landing/TeamsBlock.tsx.
+ * Ask left, briefing right. The cover is a fixed 400px so the form column
+ * keeps a comfortable measure rather than stretching across the section. */
+.teams-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 400px;
+ gap: 64px;
+ align-items: center;
+}
+.teams-eyebrow {
+ margin-bottom: 18px;
+}
+.teams-eyebrow-meta {
+ color: var(--color-text-muted);
+}
+.teams-heading {
+ font-family: var(--font-display);
+ font-weight: 400;
+ font-size: clamp(36px, 4.2vw, 54px);
+ line-height: 1.02;
+ letter-spacing: -0.03em;
+ color: var(--color-text-primary);
+ margin: 0 0 30px;
+ max-width: 16ch;
+}
+.teams-contact {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: baseline;
+ gap: 14px;
+ margin-top: 40px;
+ padding-top: 24px;
+ border-top: 1px solid var(--color-border);
+}
+.teams-contact-copy {
+ margin: 0;
+ font-size: 15px;
+ line-height: 1.6;
+ color: var(--color-text-secondary);
+}
+.teams-contact-link {
+ font-size: 15px;
+ font-weight: 600;
+ color: var(--color-accent);
+ text-decoration: none;
+ white-space: nowrap;
+}
+.teams-contact-link:hover {
+ text-decoration: underline;
+}
+
+/* The briefing preview — components/landing/FieldReportCover.tsx.
+ * .wp-paper supplies the tilt, border and shadow; these rules only style the
+ * content the library pages' version does not carry. */
+.field-report-cover {
+ display: flex;
+ justify-content: center;
+ min-width: 0;
+}
+.field-report-paper {
+ width: 100%;
+ max-width: 390px;
+}
+.field-report-kicker {
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--color-text-muted);
+ margin: 0 0 14px;
+ padding-bottom: 14px;
+ border-bottom: 1px solid var(--color-border);
+}
+.field-report-title {
+ font-family: var(--font-display);
+ font-weight: 400;
+ font-size: 25px;
+ line-height: 1.1;
+ letter-spacing: -0.02em;
+ color: var(--color-text-primary);
+ margin: 0 0 6px;
+}
+.field-report-sub {
+ font-size: 12.5px;
+ line-height: 1.5;
+ color: var(--color-text-secondary);
+ margin: 0 0 20px;
+}
+.field-report-toc-label {
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--color-text-muted);
+ margin: 0 0 10px;
+}
+.field-report-toc {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+.field-report-toc li {
+ display: flex;
+ align-items: baseline;
+ gap: 10px;
+ padding: 6px 0;
+ font-size: 12.5px;
+ color: var(--color-text-primary);
+}
+.field-report-toc li + li {
+ border-top: 1px dotted var(--color-border);
+}
+.field-report-num {
+ flex: 0 0 auto;
+ font-family: var(--font-mono);
+ font-size: 10px;
+ font-weight: 700;
+ color: var(--color-signal);
+}
+.field-report-foot {
+ display: flex;
+ justify-content: space-between;
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--color-text-muted);
+ border-top: 1px solid var(--color-border);
+ padding-top: 12px;
+ margin: 20px 0 0;
+}
+@media (max-width: 900px) {
+ .teams-grid {
+ /* minmax(0,…) not 1fr: a bare 1fr keeps an auto minimum, and the cover
+ would push the stacked column past the viewport. */
+ grid-template-columns: minmax(0, 1fr);
+ gap: 44px;
+ }
+ .field-report-cover {
+ justify-content: flex-start;
+ }
+}
+```
+
+- [ ] **Step 2: Remove what the section no longer renders**
+
+Delete these rules from `landing.css`: `.pilot-block-grid`, `.pilot-rail`, `.pilot-rail-line`, `.pilot-heading`, `.pilot-subhead`, `.pilot-cta-row`, `.pilot-rows`, `.pilot-row`, `.pilot-row-claim`, `.pilot-row-tail`, `.pilot-steps`, `.pilot-step`, `.pilot-step:last-child`, `.pilot-step-num`, `.pilot-step-title`, `.pilot-step-body`, `.teams-block-grid`, `.teams-aside`, `.teams-report`, `.teams-report-badge`, `.teams-report-title`, `.teams-report-desc`, plus any `@media` rule that only adjusts one of them.
+
+**Keep `.pilot-eyebrow`** — `/pilot-to-prod` uses it. Keep every `.wp-*` rule.
+
+Before deleting each one, confirm it is unused:
+
+```bash
+for c in pilot-block-grid pilot-rail pilot-rail-line pilot-heading pilot-subhead pilot-cta-row pilot-rows pilot-row pilot-row-claim pilot-row-tail pilot-steps pilot-step pilot-step-num pilot-step-title pilot-step-body teams-block-grid teams-aside teams-report teams-report-badge teams-report-title teams-report-desc; do
+ hits=$(grep -rl "\b$c\b" apps/website/src --include='*.tsx' | grep -v '\.spec\.' | wc -l | tr -d ' ')
+ [ "$hits" != "0" ] && echo "STILL USED: $c"
+done; echo "check complete"
+```
+
+Expected: `check complete` with no `STILL USED` lines.
+
+- [ ] **Step 3: Test, lint, build**
+
+```bash
+npx nx test website && npx nx lint website && npx nx build website
+```
+
+Expected: all pass, 0 lint errors.
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add apps/website/src/styles/landing.css
+git commit -m "feat(website): briefing block styles in, pilot timeline styles out
+
+Co-Authored-By: Claude Opus 5 "
+```
+
+---
+
+## Task 6: Verification
+
+- [ ] **Step 1: The CI commands**
+
+```bash
+npx nx test website
+npx nx lint website
+npx nx build website
+```
+
+All pass; 0 lint errors.
+
+- [ ] **Step 2: e2e**
+
+```bash
+lsof -ti:3000,4308 | xargs kill -9 2>/dev/null
+npx nx e2e website
+```
+
+Stop any Browser pane preview server first — it holds `apps/website/.next/dev/lock` and the run will fail to start a web server. The spine test must pass with `field-report-heading`; everything else must pass unchanged.
+
+- [ ] **Step 3: Measure the thing the redesign was for**
+
+Start the dev server through the Browser pane (`website-dev`), restarting it if one is already running. At a 1440px viewport:
+
+```javascript
+const s = document.querySelector('#teams');
+const top = scrollY + s.getBoundingClientRect().top;
+const rel = (el) => Math.round(scrollY + el.getBoundingClientRect().top - top);
+const form = s.querySelector('form');
+const h = document.querySelector('#field-report-heading');
+({ sectionH: Math.round(s.getBoundingClientRect().height),
+ formTop: rel(form),
+ headingLeft: Math.round(h.getBoundingClientRect().left),
+ proofHeadingLeft: Math.round(document.querySelector('#proof-heading').getBoundingClientRect().left),
+ chapters: s.querySelectorAll('.field-report-toc li').length,
+ buttons: s.querySelectorAll('[data-ui="button"]').length,
+ buttonsOutsideForm: [...s.querySelectorAll('[data-ui="button"]')].filter((b) => !b.closest('form')).length,
+ pagesClaimed: s.querySelector('[data-ui="eyebrow"]').textContent.match(/(\d+) pages/)?.[1] })
+```
+
+Expected: `formTop` **under 320** (it was 618), `chapters` **6**, `buttons` **1** and `buttonsOutsideForm` **0** — the only button is the form's own submit — `pagesClaimed` **"17"**, and `headingLeft` equal to `proofHeadingLeft`.
+
+- [ ] **Step 4: Check the phone**
+
+Emulate 390px, reload, then:
+
+```javascript
+({ overflow: document.documentElement.scrollWidth > document.documentElement.clientWidth,
+ cols: getComputedStyle(document.querySelector('.teams-grid')).gridTemplateColumns })
+```
+
+Expected: `overflow` **false**, and `cols` a single track.
+
+- [ ] **Step 5: Look at it**
+
+Screenshot the section. Confirm the cover reads as a document rather than a card, the six chapters are legible at their 12.5px, and the form is the first thing the eye lands on.
+
+- [ ] **Step 6: Commit any fixes**
+
+```bash
+git add -A
+git commit -m "fix(website): teams briefing verification pass
+
+Co-Authored-By: Claude Opus 5 "
+```
+
+---
+
+## Out of scope
+
+- **`WhitePaperBlock`'s "No vendor pitch — what we learned shipping it."** Spec §9: the document's chapters were script-generated, which makes the line hard to defend, but rewriting it or rewriting the document is the owner's call.
+- The section does not move in `page.tsx`, and `/pilot-to-prod` is untouched.
+- No change to `WhitePaperForm`, the form policy, or any analytics key.
diff --git a/docs/superpowers/specs/2026-09-08-teams-briefing-design.md b/docs/superpowers/specs/2026-09-08-teams-briefing-design.md
new file mode 100644
index 000000000..6c3e1f508
--- /dev/null
+++ b/docs/superpowers/specs/2026-09-08-teams-briefing-design.md
@@ -0,0 +1,115 @@
+# "For teams" becomes the briefing block
+
+**Date:** 2026-09-08
+**Status:** Approved, ready for planning
+**Component:** `apps/website/src/components/landing/TeamsBlock.tsx`
+**Follows:** `2026-09-08-fork-us-band-design.md` (the band directly above it)
+
+## 1. Why
+
+The section's job is the field report download, with contact as the secondary ask. Measured on the live page at 1440px, it does the opposite of that:
+
+| | |
+| --- | --- |
+| Section height | 916px |
+| The 01–04 timeline | starts at y=115 — the top of the aside |
+| Field report card | y=445 |
+| **The form** | **y=618** |
+| "Talk to an engineer" | y=667, 8,789px² |
+| "Get the field report" | 8,525px² |
+
+Three problems. The **primary conversion goal sits two-thirds down the section**. The two CTAs are within 3% of each other in visual weight, so nothing signals which ask matters. And the most valuable real estate — the top of the aside — is spent on a four-step timeline that `/pilot-to-prod` already covers in full, with week numbers.
+
+## 2. The copy was also wrong
+
+Checked against the shipped `apps/website/public/whitepaper.pdf`:
+
+| Claimed | Actual |
+| --- | --- |
+| "18 pages" (3 places) | **17 pages** — `pdfinfo` and a page-break count agree |
+| "Error boundaries, fallbacks, observability, deploy" | **`error boundar` 0 hits, `fallback` 0 hits, `observab` 0 hits** in the document |
+
+The six chapters are: **Streaming State Management · Thread Persistence · Tool-Call Rendering · Human Approval Flows · Generative UI · Deterministic Testing.**
+
+Three of the four topics named in exchange for an email address appear nowhere in what gets sent. That is the strongest argument for the design below: **the briefing preview renders the real table of contents**, so the claim and the artifact cannot drift apart again.
+
+## 3. Shape
+
+Two elements side by side, with the secondary ask beneath both:
+
+```
+PREFLIGHT BRIEFING · 17 PAGES · FREE ┌─────────────────────┐
+ │ THREADPLANE · OSS │
+What breaks between │ From Prototype │
+a demo and production. │ to Production │
+ │ CONTENTS │
+[ you@company.com ] [ Get the field report ]│ 01 Streaming State… │
+ │ 02 Thread Persist… │
+ │ …06 Deterministic… │
+────────────────────────────────────────────└─────────────────────┘
+Shipping inside a large Angular platform? … Talk to an engineer →
+```
+
+Grid is `minmax(0, 1fr) 400px` with a 64px gap, centre-aligned, collapsing to one column at 900px. The teams line spans the full width beneath, above a `1px` top border.
+
+**The heading sells; the cover identifies.** The `` is *"What breaks between a demo and production."* — the report's own title lives on the cover, so the heading earns its size instead of restating the artwork beside it.
+
+**No description line.** The eyebrow gives length and price, the heading gives the value, the cover gives the contents. A fourth line would repeat one of them.
+
+## 4. The briefing preview
+
+Reuses the existing `.wp-paper` visual language from `WhitePaperBlock` — the same `rotate(-1.2deg)`, border, radius and two-layer shadow — so it reads as the same object the library pages already show. What changes is the content: instead of a badge and one line of description, it renders the document's real front matter and table of contents.
+
+**It is not `aria-hidden`.** The existing `.wp-cover-wrap` is decorative artwork and hides itself from assistive tech; this one carries the table of contents, which is the actual reason to download. Hiding it would withhold the substance from screen reader users. The cover title is an `` and the contents are an ``, so both join the document outline.
+
+## 5. Where the facts live, and the guard that makes them un-driftable
+
+The page count and the six chapter titles are one data module — `apps/website/src/lib/field-report.ts` — read by both the eyebrow and the cover. The eyebrow's "17 pages" is **derived from it, not typed**, so the guard below covers the rendered string as well as the data.
+
+The page count is asserted against the PDF at test time, dependency-free:
+
+```ts
+const pdf = readFileSync('apps/website/public/whitepaper.pdf', 'latin1');
+const pages = (pdf.match(/\/Type\s*\/Page[^s]/g) || []).length;
+expect(FIELD_REPORT.pages).toBe(pages);
+```
+
+Verified: both this regex and `pdfinfo` return 17. Regenerating the PDF at a different length now fails the suite instead of silently making the homepage lie.
+
+**The chapter titles cannot be guarded the same way** — the text lives in compressed streams and extracting it needs a dependency CI does not have. They are verified manually; the command is recorded here so the next person does not have to rediscover it:
+
+```bash
+pdftotext -f 1 -l 2 apps/website/public/whitepaper.pdf - | sed -n '1,40p'
+```
+
+## 6. Scope beyond the section
+
+`WhitePaperBlock` — used by the four library pages — carries the **same two false claims** (`18 pages`, and `Error boundaries, fallbacks, observability, deploy`). Correcting only the homepage would knowingly leave them live elsewhere, so both strings are fixed there too. The block's layout is otherwise untouched.
+
+## 7. What gets cut
+
+- **The 01–04 timeline.** `/pilot-to-prod` covers it in full. The homepage version earned nothing and occupied the top of the aside.
+- **The four outcome rows.** They describe the pilot engagement, now carried by one sentence.
+- **"See the pilot program" button.** `/pilot-to-prod` is in the primary nav (`Nav.tsx:19`) and the footer, so it is not orphaned.
+- **The report card wrapper** (`.teams-report`, `-badge`, `-title`, `-desc`). The report is the section now; it does not need a box inside it.
+
+**Contact steps down from button to link.** Two equal-weight buttons is what stopped today's section saying which ask matters.
+
+## 8. Identifiers, tests and analytics
+
+- The `` id changes from `pilot-heading` to `field-report-heading`, and **`e2e/website.spec.ts:57` is updated in the same commit.** The spine exists to catch accidental removals; this rename is deliberate. It is the only test edit this change makes.
+- `teams-report-heading` disappears; nothing outside the component references it.
+- The section keeps `id="teams"`, its `tinted` surface and its position in `page.tsx`.
+- **Analytics keys are unchanged** so the funnel stays continuous: the form keeps `surface="home_whitepaper"` and `sourceSection="teams-block"`, and the contact link keeps `cta_id: 'hero_talk_to_engineers'` despite becoming a link.
+- The form's own disclosure (`formPolicy.disclosures.whitepaper`) and its "Already on the list?" direct-download line are rendered by `WhitePaperForm` and must not be duplicated in the section.
+
+## 9. Noted, not fixed here
+
+`WhitePaperBlock` claims *"No vendor pitch — what we learned shipping it."* Per `docs/superpowers/plans/2026-04-05-whitepaper-pipeline.md`, the document's six chapters were generated by a script calling the Anthropic API. "What we learned shipping it" is therefore hard to defend as written. It is **not** carried into the new section, and it is left alone on the library pages rather than quietly reworded, because whether to rewrite it or to rewrite the document is a call for the owner rather than a copy edit.
+
+## 10. Verification
+
+- `npx nx test website`, `npx nx lint website`, `npx nx build website`.
+- `npx nx e2e website`, with `field-report-heading` in the spine and everything else passing unchanged.
+- Measured in a browser, not eyeballed: the form sits **above y≈300** in the section (today it is at 618), the cover's contents list shows six entries, and the heading shares the container's left edge with `#proof-heading`.
+- At 390px the grid collapses to one column, the cover does not overflow, and the form's input and button stack without clipping.