diff --git a/apps/website/src/app/pricing/page.spec.tsx b/apps/website/src/app/pricing/page.spec.tsx index 321d7e651..fc3df48fc 100644 --- a/apps/website/src/app/pricing/page.spec.tsx +++ b/apps/website/src/app/pricing/page.spec.tsx @@ -16,6 +16,7 @@ describe('PricingPage', () => { expect(screen.getByText(/Every package is MIT/i)).toBeTruthy(); expect(screen.getByText(/commercial products, internal tools, and client work/i)).toBeTruthy(); expect(screen.getByText(/No Threadplane cloud/i)).toBeTruthy(); + expect(screen.getByText(/Angular 20, 21, 22, CI-tested/i)).toBeTruthy(); expect(screen.getByRole('heading', { level: 2, name: /Open software/i })).toBeTruthy(); expect(screen.getByText(/does not host your agents or conversations/i)).toBeTruthy(); expect(screen.getByText(/durable persistence.*connected backend/i)).toBeTruthy(); @@ -27,7 +28,9 @@ describe('PricingPage', () => { expect(screen.getByRole('table', { name: /Full plan comparison/i })).toBeTruthy(); expect(screen.getByRole('columnheader', { name: 'Community' })).toBeTruthy(); expect(screen.getByRole('columnheader', { name: 'Production Assurance' })).toBeTruthy(); - expect(screen.getByRole('rowheader', { name: 'MIT-licensed software' })).toBeTruthy(); + expect(screen.queryByRole('rowheader', { name: 'MIT-licensed software' })).toBeNull(); + expect(screen.getByRole('rowheader', { name: 'Private support channel' })).toBeTruthy(); + expect(screen.getByText(/identical on every path/i)).toBeTruthy(); expect(container.querySelectorAll('table[aria-label="Full plan comparison"]')).toHaveLength(1); }); diff --git a/apps/website/src/app/pricing/page.tsx b/apps/website/src/app/pricing/page.tsx index 7058bf94b..e1fa3131d 100644 --- a/apps/website/src/app/pricing/page.tsx +++ b/apps/website/src/app/pricing/page.tsx @@ -3,11 +3,11 @@ import { Section } from '../../components/ui/Section'; import { Eyebrow } from '../../components/ui/Eyebrow'; import { CompareTable } from '../../components/pricing/CompareTable'; import { ArchitectureBoundary, PricingComparison } from '../../components/pricing/PricingDetails'; -import { CompatibilityMatrix } from '../../components/pricing/CompatibilityMatrix'; import { PricingFAQ } from '../../components/pricing/PricingFAQ'; import { LeadForm } from '../../components/pricing/LeadForm'; import { FinalCTA } from '../../components/landing/FinalCTA'; import { createPageMetadata } from '../../lib/site-metadata'; +import { WEBSITE_SUPPORTED_ANGULAR_VERSIONS } from '../../components/pricing/angular-support.mjs'; export const metadata = createPageMetadata({ title: 'Pricing — Threadplane', @@ -37,6 +37,8 @@ export default function PricingPage() { Commercial use without registration or runtime checks + {WEBSITE_SUPPORTED_ANGULAR_VERSIONS}, CI-tested + No Threadplane cloud
@@ -57,19 +59,6 @@ export default function PricingPage() { -- We ship against the versions our CI tests. Other versions may work but aren't guaranteed. -
-| - Status - | -- Angular versions - | -
|---|---|
| - {row.label} - | -{row.versions} | -
Production Assurance and Pilot-to-Prod are separate choices. Request ongoing support or ask us to scope a hands-on delivery engagement.
+ + See how Pilot-to-Prod works → +
diff --git a/apps/website/src/components/pricing/PricingDetails.tsx b/apps/website/src/components/pricing/PricingDetails.tsx
index 325726e24..ff9d3f6c0 100644
--- a/apps/website/src/components/pricing/PricingDetails.tsx
+++ b/apps/website/src/components/pricing/PricingDetails.tsx
@@ -1,5 +1,4 @@
import { TIERS, type TierSlug } from '../../../../../pricing/tiers.config';
-import { WEBSITE_PRICING_SUPPORT_SUMMARY } from './angular-support.mjs';
type ComparisonCells = Record What you are buying What you are buying Full comparison Full comparison
- The software stays open. The level of support and delivery changes.
+ The software is identical on every path — MIT, all of it. What
+ changes is support.
+ Every package is MIT
+
+ Commercial use without registration or runtime checks
+
+ {WEBSITE_SUPPORTED_ANGULAR_VERSIONS}, CI-tested
+
+ No Threadplane cloud
+ ` + h2 +
+body. Wrap each kicker in the rail:
+
+```tsx
+ …
Open software. Commercial support when you want it.
@@ -137,12 +120,16 @@ export function PricingComparison() {
return (
What changes between paths.
-
+ {COMPARISON_ROWS.map((row) => (
+
- {group.title}
+
+
- {group.rows.map((row) => (
-
+ {row.label}
+ {TIERS.map((tier) => (
+
+ {row.cells[tier.slug]}
+
+ ))}
-
- ))}
-
- ))}
+ ))}
+
- {row.label}
-
- {TIERS.map((tier) => (
-
- {row.cells[tier.slug]}
-
- ))}
- Pricing FAQ.
` block (the four
+ checkmark cards) entirely — but FIRST find the "See how Pilot-to-Prod
+ works →" link inside it and MOVE that link to directly under the
+ `lead-form-subhead` paragraph, preserving its href and any onClick
+ analytics, with `className="lead-form-p2p-link"`.
+- The `lead-form-grid` two-column wrapper collapses: the form becomes the
+ single child; rename/keep the wrapper as needed so the form gets
+ `max-width: 640px` (left-aligned under the header, not centered).
+- The form element and EVERYTHING inside it: byte-untouched.
+
+- [ ] **Step 3: CSS (marketing.css)**
+
+Delete the `lead-form-value-list` / value-item rules (grep `lead-form-value`).
+Delete or neutralize `lead-form-grid` two-column rules (grep `lead-form-grid`)
+— replace with `max-width: 640px;` on the form container. Remove centering
+from `lead-form-header` (`text-align: center`, auto margins). Add:
+
+```css
+.lead-form-rail {
+ display: flex;
+ align-items: baseline;
+ gap: 14px;
+}
+.lead-form-rail-line {
+ flex: 1;
+ height: 1px;
+ background: var(--color-border);
+}
+.lead-form-p2p-link {
+ display: inline-block;
+ margin-top: 10px;
+ font-weight: 600;
+ color: var(--color-accent);
+}
+```
+
+- [ ] **Step 4: Re-run LeadForm.spec (MUST still pass) + full suite + commit**
+
+```bash
+git add apps/website/src
+git commit -m "feat(website): lead form takes center stage — tier restatement cards deleted"
+```
+
+---
+
+### Task 2: Comparison table — delete the SOFTWARE block
+
+**Files:**
+- Modify: `apps/website/src/components/pricing/PricingDetails.tsx`
+- Modify: `apps/website/src/app/pricing/page.spec.tsx`
+
+- [ ] **Step 1: Update page.spec FIRST (failing)**
+
+`page.spec.tsx:30` asserts `getByRole('rowheader', { name: 'MIT-licensed software' })`. Replace with:
+
+```tsx
+ expect(screen.queryByRole('rowheader', { name: 'MIT-licensed software' })).toBeNull();
+ expect(screen.getByRole('rowheader', { name: 'Private support channel' })).toBeTruthy();
+ expect(screen.getByText(/identical on every path/i)).toBeTruthy();
+```
+
+Run the pricing page spec — expect FAIL (the null assertion fails against the current page). Paste it.
+
+- [ ] **Step 2: PricingDetails.tsx**
+
+- Delete the entire `Software` group from `COMPARISON_GROUPS` (six rows) and
+ the now-unused `ALL_INCLUDED` rows within it. `Public documentation and
+ examples` (in Support) still uses `ALL_INCLUDED` — keep the constant.
+- Remove the now-unused `WEBSITE_PRICING_SUPPORT_SUMMARY` import (it only fed
+ the deleted Angular row) — verify with grep before removing.
+- With one group left, drop the group-header row: in the render, replace the
+ `COMPARISON_GROUPS.map` + group-row `
` with a single ` ` over the
+ remaining rows (keep the `COMPARISON_GROUPS` shape with one group if that
+ is smaller — implementer's choice — but the rendered `pricing-comparison-group-row`
+ header must NOT render when only one group exists; simplest is to flatten
+ to a `COMPARISON_ROWS` array and delete the group machinery).
+- The lede changes: `"The software stays open. The level of support and
+ delivery changes."` → `"The software is identical on every path — MIT, all
+ of it. What changes is support."`
+
+- [ ] **Step 3: Run pricing page spec (PASS) + full suite + commit**
+
+```bash
+git add apps/website/src
+git commit -m "feat(website): comparison table keeps only rows that differ"
+```
+
+---
+
+### Task 3: Compatibility section → one hero clause
+
+**Files:**
+- Modify: `apps/website/src/app/pricing/page.tsx`
+- Delete: `apps/website/src/components/pricing/CompatibilityMatrix.tsx`
+- Delete: `apps/website/src/components/pricing/CompatibilityMatrix.spec.tsx`
+- Modify: `apps/website/src/components/pricing/angular-support.mjs`
+- Modify: `apps/website/src/app/pricing/page.spec.tsx`
+- Modify: `apps/website/src/styles/marketing.css`
+
+- [ ] **Step 1: Hero license strip gains the compat clause**
+
+In `page.tsx`, the license line becomes (importing from angular-support.mjs):
+
+```tsx
+import { WEBSITE_SUPPORTED_ANGULAR_VERSIONS } from '../../components/pricing/angular-support.mjs';
+```
+```tsx
+