+ Signal-native chat, threads, interrupts, tool progress, and generative UI for LangGraph, AG-UI, and A2UI. MIT-licensed, self-hostable, app telemetry off by default, no React rewrite.
+
+
+
+
+
+
+ MIT
+ Angular-native Signals
+ LangGraph + AG-UI
+ A2UI-compatible
+ Self-hostable
+ App telemetry off by default
+
+
+ Not another backend agent runtime. Keep LangGraph, Genkit, Mastra, CrewAI, or your own service. Cacheplane solves the Angular UI layer.
+
+
+
+ {/* Right column — KEEP existing layered collage exactly as-is. Re-paste from
+ the prior version of this file (BrowserFrame x 2 with screenshots + code). */}
+
+ {/* ...preserve the collage block from the prior Hero.tsx... */}
+
+ );
+}
+```
+
+If the Discord URL is unknown, leave it as `https://discord.gg/cacheplane` and the operator can adjust post-merge.
+
+- [ ] **Step 3: Build to confirm**
+
+```bash
+npx nx run website:build
+```
+
+Expected: green.
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add apps/website/src/components/contact/SlaCard.tsx apps/website/src/components/contact/AltChannelRow.tsx
+git commit -m "$(cat <<'EOF'
+feat(website): SlaCard + AltChannelRow static contact-page subcomponents
+
+Renders the "replies personally from a real inbox" promise and the
+docs / GitHub issues / Discord row below the contact form, per the
+locked Direction A.v2 spec.
+
+Co-Authored-By: Claude Opus 4.7
+EOF
+)"
+```
+
+### Task 2.5: `/contact` page composition
+
+**File:** `apps/website/src/app/contact/page.tsx` (NEW)
+
+- [ ] **Step 1: Create the page**
+
+```typescript
+// SPDX-License-Identifier: MIT
+import type { Metadata } from 'next';
+import { Suspense } from 'react';
+import { tokens } from '@ngaf/design-tokens';
+import { Container } from '../../components/ui/Container';
+import { Section } from '../../components/ui/Section';
+import { Eyebrow } from '../../components/ui/Eyebrow';
+import { ContactForm } from '../../components/contact/ContactForm';
+import { GitHubStarsPill } from '../../components/contact/GitHubStarsPill';
+import { SlaCard } from '../../components/contact/SlaCard';
+import { AltChannelRow } from '../../components/contact/AltChannelRow';
+
+export const metadata: Metadata = {
+ title: 'Talk to an engineer — Cacheplane',
+ description: "Tell us what you're shipping. We'll reply within one business day — usually with code, not a calendar invite.",
+ openGraph: {
+ title: 'Talk to an engineer — Cacheplane',
+ description: "Tell us what you're shipping. We'll reply within one business day.",
+ type: 'website',
+ },
+};
+
+export default function ContactPage() {
+ return (
+
+
+
+ Contact
+
+ Talk to an engineer.
+
+
+ Tell us what you're shipping. We'll reply within one business day — usually with code, not a calendar invite.
+
+ );
+}
+```
+
+- [ ] **Step 4: Run, see pass + commit**
+
+```bash
+cd apps/website && npx vitest run src/components/pricing/CompatibilityMatrix.spec.tsx
+```
+
+Expected: 1 test passing.
+
+```bash
+git add apps/website/src/components/pricing/CompatibilityMatrix.tsx apps/website/src/components/pricing/CompatibilityMatrix.spec.tsx
+git commit -m "$(cat <<'EOF'
+feat(website): CompatibilityMatrix component
+
+Replaces the "All Angular versions" risk claim with a real
+supported/experimental/planned/unsupported matrix. Initial content
+matches the peer-deps reality: Angular 20, 21 supported; 22 planned;
+≤19 unsupported.
+
+Co-Authored-By: Claude Opus 4.7
+EOF
+)"
+```
+
+### Task 3.2: Wire `CompatibilityMatrix` into `/pricing`
+
+**File:** `apps/website/src/app/pricing/page.tsx` (MODIFIED)
+
+- [ ] **Step 1: Locate the "All Angular versions" claim**
+
+```bash
+grep -n "All Angular\|Angular versions" apps/website/src/app/pricing/page.tsx apps/website/src/components/pricing/*.tsx 2>/dev/null
+```
+
+Likely match in either `page.tsx` directly or inside `CompareTable.tsx` / `PricingGrid.tsx`. Find the exact text.
+
+- [ ] **Step 2: Replace with ``**
+
+Add the import:
+
+```typescript
+import { CompatibilityMatrix } from '../../components/pricing/CompatibilityMatrix';
+```
+
+Replace the offending row/cell/paragraph with the matrix. If the claim sits inside a feature-row of a table cell that just says "All Angular versions", swap that cell content for "See compatibility matrix below" and render the matrix as its own labeled subsection above or below the `CompareTable`.
+
+A safe insertion location: a new `` block between `` and `` titled "Compatibility":
+
+```tsx
+
+
+ Compatibility
+
+ Angular version support
+
+
+ We ship against the versions our CI tests. Other versions may work but aren't guaranteed.
+
+
+
+
+```
+
+- [ ] **Step 3: Build + commit**
+
+```bash
+npx nx run website:build
+git add apps/website/src/app/pricing/page.tsx
+git commit -m "$(cat <<'EOF'
+feat(website): wire CompatibilityMatrix into /pricing
+
+Replaces the "All Angular versions" claim with a real matrix. New
+"Compatibility" section sits between CompareTable and LeadForm.
+
+Co-Authored-By: Claude Opus 4.7
+EOF
+)"
+```
+
+---
+
+## Phase 4 — Loosen `/api/leads` + Resend template + messaging.md
+
+### Task 4.1: Loosen `/api/leads` validation + adapt Resend subject
+
+**Files:** `apps/website/src/app/api/leads/route.ts` (MODIFIED), `apps/website/emails/lead-notification.ts` (MODIFIED)
+
+- [ ] **Step 1: Update validation**
+
+In `apps/website/src/app/api/leads/route.ts`, find the validation block:
+
+```typescript
+if (!name || !email) {
+ return NextResponse.json({ error: 'name and email required' }, { status: 400 });
+}
+```
+
+Replace with:
+
+```typescript
+if (!email) {
+ return NextResponse.json({ error: 'email required' }, { status: 400 });
+}
+```
+
+- [ ] **Step 2: Update the Resend subject construction**
+
+Find the line that builds the subject:
+
+```typescript
+subject: `New lead: ${name}${company ? ` at ${company}` : ''}`,
+```
+
+Replace with:
+
+```typescript
+subject: `New lead: ${name || email}${company ? ` at ${company}` : ''}`,
+```
+
+- [ ] **Step 3: Update `emails/lead-notification.ts` to handle missing name**
+
+Read the file. If it renders `
${name}
` or similar, replace with `
${name || 'No name provided'}
` — or surface the email as the fallback identity. The shape of this template varies; pattern-match the prior behavior and make name optional throughout.
+
+- [ ] **Step 4: Verify build**
+
+```bash
+npx nx run website:build
+```
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add apps/website/src/app/api/leads/route.ts apps/website/emails/lead-notification.ts
+git commit -m "$(cat <<'EOF'
+feat(website): loosen /api/leads validation to email-only
+
+Spec 2's /contact form collects email (required) + name, company,
+message (all optional). Pricing's LeadForm continues to send name.
+Resend notification subject falls back to email when name is missing.
+The lead-notification email template no longer requires name.
+
+Spec 1E's qualified-lead gate is unchanged — still requires non-personal
+email + non-empty company. Contact-form leads without a company fire
+marketing:lead_form_success but not marketing:lead_qualified, by design.
+
+Co-Authored-By: Claude Opus 4.7
+EOF
+)"
+```
+
+### Task 4.2: Update `docs/gtm/messaging.md`
+
+**File:** `docs/gtm/messaging.md` (MODIFIED)
+
+- [ ] **Step 1: Update the Fields line in the Contact page section**
+
+Find:
+
+```
+**Fields:** email + free-text body. No stack dropdown, no company size, no "how did you hear."
+```
+
+Replace with:
+
+```
+**Fields:** email (required) + name, company, message (all optional). No stack dropdown, no company size, no "how did you hear." Optional fields feed enterprise-qualification when present.
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add docs/gtm/messaging.md
+git commit -m "$(cat <<'EOF'
+docs(gtm): update Contact page §Fields per Spec 2 implementation
+
+Direction A.v2 originally locked "email + free-text body." During Spec 2
+implementation we expanded to email (required) + name, company, message
+(all optional) so Spec 1E's captureLeadQualified gate (which requires
+company) still works for contact-form submissions.
+
+Co-Authored-By: Claude Opus 4.7
+EOF
+)"
+```
+
+---
+
+## Phase 5 — Verification
+
+### Task 5.1: Final test + build sweep (no commit)
+
+- [ ] **Step 1: Run tests**
+
+```bash
+npx nx run-many -t test -p telemetry,cockpit
+cd apps/website && npx vitest run src/components/landing src/components/contact src/components/pricing src/lib/github.spec.ts src/lib/analytics/server.spec.ts
+```
+
+Expected: green. Pre-existing e2e + open-in-cockpit failures (unrelated) may persist.
+
+- [ ] **Step 2: Run builds**
+
+```bash
+npx nx run-many -t build -p website,cockpit,telemetry
+```
+
+Expected: green.
+
+- [ ] **Step 3: Drift guard sanity (no new events; the guard should still pass)**
+
+```bash
+npx nx run posthog-tools:test
+```
+
+Expected: green. (Spec 2 doesn't introduce new event names — only `cta_id` property values, which the drift guard doesn't scan.)
+
+- [ ] **Step 4: Manual smoke**
+
+Start the website locally:
+
+```bash
+nx run website:dev
+```
+
+Visit `http://localhost:3000/`:
+- Confirm hero shows "Ship production agent UIs in Angular." H1.
+- Click `Install @ngaf/chat` — button label flips to "Copied ✓" and clipboard contains `npm install @ngaf/chat`.
+- Click `Talk to our engineers` — navigates to `/contact?source=home_hero&track=enterprise`.
+
+Visit `/contact?source=home_hero&track=enterprise`:
+- Confirm headline "Talk to an engineer." renders.
+- SLA card present.
+- Form has 4 fields (email required, others optional).
+- GitHub stars pill renders (or fallback to plain "GitHub" pill).
+- Alt-channel row visible.
+
+Visit `/pricing`:
+- Confirm `` section renders with four rows.
+
+Submit a test lead via `/contact?...` with `email=jane@acme.com, company=Acme` and verify PostHog Live Events shows `marketing:lead_form_submit`, `marketing:lead_form_success`, AND `marketing:lead_qualified` (the last one from Spec 1E's gate passing).
+
+- [ ] **Step 5: Done**
+
+If all checks pass, Spec 2 is implementation-complete. Proceed to PR.
+
+---
+
+## Self-Review
+
+**1. Spec coverage:**
+
+| Spec deliverable | Task |
+|---|---|
+| Hero rewrite with locked copy + two tracked CTAs + clipboard primary CTA | 1.2 + 1.3 |
+| `/contact` route with ContactForm (email required; name/company/message optional) | 2.5 + 2.3 |
+| GitHubStarsPill + getGitHubStars ISR + fetch-failure fallback | 2.1 + 2.2 |
+| CompatibilityMatrix on /pricing with conservative content | 3.1 + 3.2 |
+| Three full-repo sweeps: telemetry phrasing, A2UI v0.9, category sweep | 0.1, 0.2, 0.3 |
+| `CtaId` type union exported; `AnalyticsProperties.cta_id` tightened | 1.1 |
+| `/api/leads` validates email only | 4.1 |
+| Resend subject template handles missing name | 4.1 |
+| `docs/gtm/messaging.md` Contact page §Fields updated | 4.2 |
+| Hidden attribution fields populated from URL params + referrer | 2.3 (ContactForm impl) |
+| Alt-channel row (docs/GitHub issues/Discord) | 2.4 |
+| SLA card | 2.4 |
+| All affected projects' tests green | 5.1 |
+
+All deliverables covered.
+
+**2. Placeholder scan:** No "TBD", no "fill in details", no "similar to Task N" without showing code. ✓
+
+**3. Type consistency:**
+
+- `CtaId` union members `'hero_install' | 'hero_talk_to_engineers'` — defined in 1.1, used in 1.3 (`cta_id: 'hero_install'` / `'hero_talk_to_engineers'`). ✓
+- `track: 'developer' | 'enterprise'` — used in 1.3 (Hero CTAs), 2.3 (ContactForm default), 2.5 (page URL). ✓
+- `surface: 'home' | 'contact'` — `'home'` in Hero (1.3), `'contact'` in ContactForm (2.3). Both are members of the existing `AnalyticsSurface` union (`surface?: AnalyticsSurface`). ✓
+- `INSTALL_COMMAND = 'npm install @ngaf/chat'` — written in 1.3, asserted in test 1.2. ✓
+- `getGitHubStars` signature `(repo?: string) => Promise` — same across 2.1 implementation and 2.2 consumer. ✓
+- `CompatibilityMatrix` content (Angular 20, 21 / — / 22 / ≤19) consistent across 3.1 spec, 3.1 implementation, and the spec doc §3. ✓
diff --git a/docs/superpowers/specs/gtm/2026-05-16-positioning-and-risks-design.md b/docs/superpowers/specs/gtm/2026-05-16-positioning-and-risks-design.md
new file mode 100644
index 000000000..19c74102c
--- /dev/null
+++ b/docs/superpowers/specs/gtm/2026-05-16-positioning-and-risks-design.md
@@ -0,0 +1,311 @@
+---
+workstream: positioning-and-risks
+status: approved
+owner: brian
+phase: 1
+spec: docs/superpowers/specs/gtm/2026-05-16-positioning-and-risks-design.md
+plan: docs/superpowers/plans/gtm/2026-05-16-positioning-and-risks.md
+parent: docs/superpowers/specs/gtm/2026-05-13-gtm-meta-design.md
+---
+
+# Spec 2 — Positioning & Risks (Design)
+
+> Replaces the homepage hero with the locked Direction-A copy, stands up the `/contact` enterprise CTA destination, applies four risk-cleanup copy changes, and wires the two CTA tracks so the developer/enterprise funnel split becomes measurable in PostHog.
+
+## 1. Goal
+
+Deliver Phase 1's developer-clarity foundation:
+
+1. Homepage hero communicates the durable category claim ("Agent UI for Angular") in 30 seconds with a working CTA fork (developer / enterprise).
+2. `/contact` exists as the enterprise CTA destination per the locked Direction A.v2 spec.
+3. The four risk-cleanup copy changes are deployed everywhere they appear: telemetry phrasing, real Angular compatibility matrix, A2UI v0.9, "Angular Agent Framework" → "Agent UI for Angular" category sweep.
+4. Both CTA tracks emit `marketing:cta_click` with a stable `cta_id` so the developer-funnel and enterprise-funnel dashboards from Spec 1A populate.
+
+## 2. Context
+
+- Parent: `docs/superpowers/specs/gtm/2026-05-13-gtm-meta-design.md` §6 (Phase 1 critical path: 0 → 1 → 2 → 3 → 4).
+- Locked content lives in `docs/gtm/messaging.md`:
+ - **Positioning statement** (durable).
+ - **Hero copy** (H1, subhead, primary CTA, secondary CTA, proof row, subline) — locked.
+ - **Contact page (Direction A.v2)** — locked.
+ - **Risk-cleanup copy changes (Spec 2)** — 4 items, locked.
+- The current Hero in `apps/website/src/components/landing/Hero.tsx` reads "Build fullstack agentic Angular apps" — superseded by the locked H1.
+- The `/contact` route does not exist. The existing `LeadForm` lives on `/pricing` and reuses the `/api/leads` route handler.
+- Spec 1E shipped `captureLeadQualified` server-side. `/api/leads` already calls it after `captureLeadConversion`. Spec 2's contact form will POST to the same endpoint.
+- The repo's "Chat" memory note (`feedback_chat_prefix_substring_overlap.md`) warns against blind `replace_all` on overlapping substrings; "Angular Agent Framework" is a longer unique phrase, so the same risk does not apply — but a per-file review remains the right discipline.
+
+### Deviation from messaging.md (call-out)
+
+`docs/gtm/messaging.md` Contact page §"Fields" locks: *"email + free-text body. No stack dropdown, no company size, no 'how did you hear.'"* This spec **expands** the field list to **email (required) + name, company, message (all optional)** so Spec 1E's `captureLeadQualified` gate (which requires `company` to fire) still works for contact-form submissions. The form remains a single short block — no progressive disclosure, no qualification dropdowns — but the optional fields, when filled, feed enterprise qualification. The messaging.md doc gets updated in this PR to reflect the new locked field set.
+
+## 3. Scope
+
+**In:**
+
+- **Hero rewrite** (`apps/website/src/components/landing/Hero.tsx`):
+ - Eyebrow: `Agent UI for Angular · MIT` (replaces `Angular Agent Framework · MIT`).
+ - H1: `Ship production agent UIs in Angular.`
+ - Subhead: `Signal-native chat, threads, interrupts, tool progress, and generative UI for LangGraph, AG-UI, and A2UI. MIT-licensed, self-hostable, app telemetry off by default, no React rewrite.`
+ - Primary CTA: button label `Install @ngaf/chat`; click copies `npm install @ngaf/chat` to clipboard and fires `marketing:cta_click` with `cta_id: 'hero_install'`, `track: 'developer'`, `surface: 'home'`. Brief visual confirmation on copy (existing `Pill`/toast pattern if available, otherwise a label flip for ~1.5s).
+ - Secondary CTA: button label `Talk to our engineers`; routes to `/contact?source=home_hero&track=enterprise` and fires `marketing:cta_click` with `cta_id: 'hero_talk_to_engineers'`, `track: 'enterprise'`, `surface: 'home'`.
+ - Proof row: `MIT · Angular-native Signals · LangGraph + AG-UI · A2UI-compatible · Self-hostable · App telemetry off by default`.
+ - Subline under proof row: `Not another backend agent runtime. Keep LangGraph, Genkit, Mastra, CrewAI, or your own service. Cacheplane solves the Angular UI layer.`
+
+- **New `/contact` route** (`apps/website/src/app/contact/page.tsx`):
+ - Server component. Page metadata (title, OG, twitter).
+ - Headline: `Talk to an engineer.`
+ - Subhead: `Tell us what you're shipping. We'll reply within one business day — usually with code, not a calendar invite.`
+ - SLA card: `Brian or someone on the team replies personally — from a real inbox, not noreply@. We read every message.`
+ - `` (new client component): email (required) + name (optional) + company (optional) + message (optional, textarea) + hidden attribution fields populated from URL params + `document.referrer`. POSTs to `/api/leads`.
+ - `` (new server component): fetches `https://api.github.com/repos/cacheplane/angular-agent-framework` once per build / 24h ISR; renders a pill with the star count + link. Graceful fallback to a "GitHub" pill (no count) on fetch failure.
+ - Alt-channel row below form: `docs · GitHub issues · Discord` (three links).
+ - On successful submit: inline success message; no redirect.
+
+- **Loosen `/api/leads` validation** (`apps/website/src/app/api/leads/route.ts`):
+ - Require email only. `name`, `company`, `message` all optional.
+ - Existing `LeadForm` on `/pricing` continues to send `name`; new contact form sends what users fill in.
+ - Resend notification body adapts: when `name` absent, subject becomes `New lead: ` instead of `New lead: at `. Body still shows whichever fields are present.
+
+- **Four risk-cleanup copy changes (full-repo sweep):**
+ - **Telemetry phrasing.** Sweep `apps/website` + `gtm.md` + `libs/*/README.md` for "No telemetry" (and close variants) → `App telemetry off by default` with a link to `libs/telemetry/README.md`. Per-file review, no blind replace.
+ - **Compatibility matrix.** Replace the "All Angular versions" claim on `/pricing` with a new `` component:
+ - Supported: Angular 20, 21 (matches `peerDependencies: "^20.0.0 || ^21.0.0"`)
+ - Experimental: (none — empty row labeled "—")
+ - Planned: Angular 22 (next major)
+ - Unsupported: Angular ≤19
+ - **A2UI v0.9 sweep.** "A2UI v1" → "A2UI v0.9-compatible" everywhere across the full repo. Includes website copy, MDX docs, READMEs, `gtm.md`, package descriptions.
+ - **Category sweep.** "Angular Agent Framework" → "Agent UI for Angular" across the full repo. Excludes CHANGELOG.md and release-note files (historical record). Excludes class/identifier names (none exist). Per-file review.
+
+- **`marketing:cta_click` `cta_id` typing:** export a `CtaId` string union from `apps/website/src/lib/analytics/events.ts` and tighten `AnalyticsProperties.cta_id` to that union. Initial members: `'hero_install' | 'hero_talk_to_engineers'`. Future CTAs extend this list.
+
+- **`docs/gtm/messaging.md` update:** the Contact page §"Fields" line gets updated from "email + free-text body" to "email (required) + name, company, message (all optional)". The locked status remains; the field set is the only revision.
+
+**Out:**
+
+- Comparison pages (Spec 3).
+- Cockpit activation recipes (Spec 4).
+- New events. The `cta_id` property does the slicing on the existing `marketing:cta_click`.
+- Adding a hero CTA tracking insight to PostHog. The dashboards-as-code pipeline (Spec 1A) handles tile-level work; this spec only ensures the underlying event property lands cleanly.
+- Replacing or styling other landing sections (proof rows below hero, differentiator section, etc.). Codex PR #352 polished those; we keep them.
+- Repo rename or README badge updates beyond the category-sweep text replacements already enumerated.
+
+## 4. Components
+
+### 4.1 Hero (`apps/website/src/components/landing/Hero.tsx`)
+
+Replace H1, subhead, eyebrow, both buttons, proof row, and add the subline. Layout, grid, and right-column collage from PR #352 stay intact.
+
+```tsx
+'use client';
+import { useCallback, useState } from 'react';
+import { track } from '../../lib/analytics/client';
+import { analyticsEvents } from '../../lib/analytics/events';
+// ...existing imports
+
+function PrimaryInstallButton() {
+ const [copied, setCopied] = useState(false);
+ const onClick = useCallback(async () => {
+ track(analyticsEvents.marketingCtaClick, {
+ cta_id: 'hero_install',
+ track: 'developer',
+ surface: 'home',
+ });
+ try {
+ await navigator.clipboard?.writeText('npm install @ngaf/chat');
+ setCopied(true);
+ setTimeout(() => setCopied(false), 1500);
+ } catch {
+ // silent fail — focus stays on event firing
+ }
+ }, []);
+ return (
+
+ );
+}
+```
+
+Secondary CTA is a plain `