diff --git a/apps/website/scripts/check-style-migration.mts b/apps/website/scripts/check-style-migration.mts index 8869892ba..5802e179b 100644 --- a/apps/website/scripts/check-style-migration.mts +++ b/apps/website/scripts/check-style-migration.mts @@ -64,8 +64,12 @@ function normProp(p: string): string { return p.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase()); } function normValue(prop: string, raw: string): string { - let v = raw.trim().replace(/^['"`]|['"`]$/g, ''); - const tok = v.match(/^tokens\.([a-zA-Z.]+)$/); + let v = raw.trim(); + // Strip quotes only when one pair wraps the WHOLE value; stripping a lone + // leading quote mangled internally-quoted font stacks ('"X", monospace'). + const q = v[0]; + if ((q === '"' || q === "'" || q === '`') && v.endsWith(q)) v = v.slice(1, -1); + const tok = v.match(/^tokens\.([a-zA-Z0-9.]+)$/); if (tok) v = tokenValue(tok[1]) ?? v; v = v.replace(/var\((--[a-z0-9-]+)\)/g, (_, name) => cssVars.get(name) ?? _); if (/^-?\d+(\.\d+)?$/.test(v) && !UNITLESS.has(prop)) v = `${v}px`; diff --git a/apps/website/src/app/about/page.tsx b/apps/website/src/app/about/page.tsx index 0f63937a9..aaee94e05 100644 --- a/apps/website/src/app/about/page.tsx +++ b/apps/website/src/app/about/page.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: MIT import Link from 'next/link'; -import { tokens } from '@threadplane/design-tokens'; import { Container } from '../../components/ui/Container'; import { Section } from '../../components/ui/Section'; import { Eyebrow } from '../../components/ui/Eyebrow'; @@ -31,26 +30,6 @@ export const metadata = createPageMetadata({ type: 'website', }); -const bodyStyle = { - fontFamily: tokens.typography.bodyLg.family, - fontSize: tokens.typography.bodyLg.size, - lineHeight: tokens.typography.bodyLg.line, - color: tokens.colors.textSecondary, - margin: 0, - marginBottom: 16, - maxWidth: '60ch', -} as const; - -const headingStyle = { - fontFamily: tokens.typography.h2.family, - fontSize: tokens.typography.h2.size, - color: tokens.colors.textPrimary, - margin: 0, - marginBottom: 12, -} as const; - -const linkStyle = { color: tokens.colors.accent } as const; - export default function AboutPage() { return ( <> @@ -58,41 +37,29 @@ export default function AboutPage() {
-
- About -

+
+ About +

Who writes Threadplane

-

{ABOUT_INTRO}

+

{ABOUT_INTRO}

-

{ABOUT_HISTORY_HEADING}

+

{ABOUT_HISTORY_HEADING}

{ABOUT_HISTORY.map((paragraph) => ( -

+

{paragraph}

))} -

{ABOUT_PRINCIPLES}

-

{ABOUT_PERSONAL}

+

{ABOUT_PRINCIPLES}

+

{ABOUT_PERSONAL}

-

+

github.com/{author.github} @@ -103,12 +70,12 @@ export default function AboutPage() {

-
-

What Threadplane is

-

{LONG_SUBHEAD}

-

+

+

What Threadplane is

+

{LONG_SUBHEAD}

+

The source is public at{' '} - + github.com/cacheplane/angular-agent-framework . @@ -119,23 +86,23 @@ export default function AboutPage() {

-
-

How it is licensed

-

+

+

How it is licensed

+

@threadplane/chat is free for noncommercial use under PolyForm Noncommercial 1.0.0; commercial production use requires a Threadplane Commercial license. The other libraries are MIT. The{' '} - + licensing docs {' '} - and the pricing page have the details. + and the pricing page have the details.

-

+

Questions about a specific build go to{' '} - + contact - ; ongoing writing is on the blog. + ; ongoing writing is on the blog.

diff --git a/apps/website/src/app/ag-ui/page.tsx b/apps/website/src/app/ag-ui/page.tsx index b0b2d2aab..e1e5c10db 100644 --- a/apps/website/src/app/ag-ui/page.tsx +++ b/apps/website/src/app/ag-ui/page.tsx @@ -1,5 +1,4 @@ import Link from 'next/link'; -import { tokens } from '@threadplane/design-tokens'; import { Container } from '../../components/ui/Container'; import { Section } from '../../components/ui/Section'; import { Eyebrow } from '../../components/ui/Eyebrow'; @@ -24,61 +23,29 @@ export default async function AgUiPage() { {/* Hero */}
-
- @threadplane/ag-ui -

+
+ @threadplane/ag-ui +

One adapter. Eight backends.

-

+

Build an Angular agent UI on any AG-UI-compatible runtime — CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit, or LangGraph fronted by AG-UI. Same Agent contract and chat surface; runtime-specific history and checkpoint behavior stays with the backend.

-
+
-

- Talking to LangGraph Platform directly? See Choosing an adapter. +

+ Talking to LangGraph Platform directly? See Choosing an adapter.

-
+
MIT Angular 20+ AG-UI protocol
-

+

Already on LangGraph?{' '} - + See @threadplane/langgraph {' '} for native streaming, checkpoints, and the typed LangGraph SDK path. @@ -127,17 +94,7 @@ export default async function AgUiPage() { visualLeft visual={ -

+            
 {`import { provideAgent, injectAgent } from '@threadplane/ag-ui';
 import { ChatComponent } from '@threadplane/chat';
 
diff --git a/apps/website/src/app/blog/[slug]/page.tsx b/apps/website/src/app/blog/[slug]/page.tsx
index ce64c7712..629fa79d4 100644
--- a/apps/website/src/app/blog/[slug]/page.tsx
+++ b/apps/website/src/app/blog/[slug]/page.tsx
@@ -1,6 +1,5 @@
 import type { Metadata } from 'next';
 import { notFound } from 'next/navigation';
-import { tokens } from '@threadplane/design-tokens';
 import { MdxRenderer } from '../../../components/docs/MdxRenderer';
 import { DocsTOC } from '../../../components/docs/DocsTOC';
 import { AuthorByline } from '../../../components/blog/AuthorByline';
@@ -88,50 +87,22 @@ export default async function BlogPostPage({ params }: Params) {
   ]);
 
   return (
-    
+
{postData ? : null} -
-
-
- +
+
+
+ {primaryTag} · {formatPostDate(post.frontmatter.date)} · {minutes} min read -

+

{post.frontmatter.title}

-

+

{post.frontmatter.description}

-
+
{post.frontmatter.tags && post.frontmatter.tags.length > 0 ? ( diff --git a/apps/website/src/app/blog/page.tsx b/apps/website/src/app/blog/page.tsx index 67a2bb25d..4535a055c 100644 --- a/apps/website/src/app/blog/page.tsx +++ b/apps/website/src/app/blog/page.tsx @@ -1,5 +1,4 @@ import Link from 'next/link'; -import { tokens } from '@threadplane/design-tokens'; import { createPageMetadata } from '../../lib/site-metadata'; import { getAllPosts, getFeaturedPost, getAllTags } from '../../lib/blog'; import { FeaturedPostCard } from '../../components/blog/FeaturedPostCard'; @@ -34,35 +33,16 @@ export default async function BlogIndexPage({ searchParams }: Props) { const grid = featured ? filtered.filter((p) => p.slug !== featured.slug) : filtered; return ( -
-
-
- +
+
+
+ Blog -

+

Articles from Threadplane

-

+

Writing on agent UI for Angular — production patterns, design choices, and what we're shipping.

@@ -73,45 +53,16 @@ export default async function BlogIndexPage({ searchParams }: Props) { {featured ? : null} {grid.length === 0 ? ( -
-

+

+

No posts tagged {activeTag} yet.

- + View all posts
) : ( -
+
{grid.map((p) => ( ))} diff --git a/apps/website/src/app/chat/page.tsx b/apps/website/src/app/chat/page.tsx index edd086ea7..7f392f831 100644 --- a/apps/website/src/app/chat/page.tsx +++ b/apps/website/src/app/chat/page.tsx @@ -1,4 +1,3 @@ -import { tokens } from '@threadplane/design-tokens'; import { Container } from '../../components/ui/Container'; import { Section } from '../../components/ui/Section'; import { Eyebrow } from '../../components/ui/Eyebrow'; @@ -24,42 +23,21 @@ export default async function ChatPage() { {/* Hero */}
-
- @threadplane/chat -

+
+ @threadplane/chat +

Drop-in chat for Angular agents.

-

+

chat-timeline + chat-debug + GenUI surfaces. Production-shaped from day one, themable to your design system, or use the headless primitives if you want full control.

-
+
-
+
MIT Vercel json-render Google A2UI @@ -87,27 +65,19 @@ export default async function ChatPage() { cta={{ label: 'See @threadplane/chat docs', href: '/docs/chat/getting-started/introduction' }} visual={ -
-
+
+
streaming 3 tools 1 interrupt
-
+
tool · query_inventory · 240ms
-
+
{`{ items: 47, low_stock: 3, total_value: 12400 }`}
-
+
replay · 0:24 · paused on interrupt
diff --git a/apps/website/src/app/contact/page.tsx b/apps/website/src/app/contact/page.tsx index 81d2f6d7a..35dceb2df 100644 --- a/apps/website/src/app/contact/page.tsx +++ b/apps/website/src/app/contact/page.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: MIT import React, { Suspense } from 'react'; -import { tokens } from '@threadplane/design-tokens'; import { Container } from '../../components/ui/Container'; import { Section } from '../../components/ui/Section'; import { Eyebrow } from '../../components/ui/Eyebrow'; @@ -21,43 +20,21 @@ export default function ContactPage() { return (
-
- Contact -

+
+ 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.

-
+
-
+
diff --git a/apps/website/src/app/error.tsx b/apps/website/src/app/error.tsx index 73ca655e3..5e89581f7 100644 --- a/apps/website/src/app/error.tsx +++ b/apps/website/src/app/error.tsx @@ -1,7 +1,6 @@ 'use client'; import { useEffect } from 'react'; -import { tokens } from '@threadplane/design-tokens'; import { Container } from '../components/ui/Container'; import { Section } from '../components/ui/Section'; import { Eyebrow } from '../components/ui/Eyebrow'; @@ -28,51 +27,23 @@ export default function ErrorBoundary({ error, reset }: ErrorBoundaryProps) { return (
-
- Error -

+
+ Error +

Something went wrong.

-

+

An unexpected error stopped this page from rendering. The team has been notified. You can try again, or head back home.

{error.digest ? ( -

+

Error ID: {error.digest}

) : ( -
+
)} -
+
diff --git a/apps/website/src/app/langgraph/page.tsx b/apps/website/src/app/langgraph/page.tsx index 56c70920c..7f84472df 100644 --- a/apps/website/src/app/langgraph/page.tsx +++ b/apps/website/src/app/langgraph/page.tsx @@ -1,4 +1,3 @@ -import { tokens } from '@threadplane/design-tokens'; import { Container } from '../../components/ui/Container'; import { Section } from '../../components/ui/Section'; import { Eyebrow } from '../../components/ui/Eyebrow'; @@ -24,43 +23,22 @@ export default async function LangGraphPage() { {/* Hero */}
-
- @threadplane/langgraph -

+
+ @threadplane/langgraph +

LangGraph agent UI for Angular.

-

+

Ship LangGraph agents inside your Angular 20+ app with headless chat, durable threads, interrupts, branch/history, tool progress, and generative UI.

-
+
-

- Not sure if LangGraph is right for your backend? See Choosing an adapter. +

+ Not sure if LangGraph is right for your backend? See Choosing an adapter.

-
+
MIT Angular 20+ LangGraph + AG-UI @@ -88,17 +66,7 @@ export default async function LangGraphPage() { cta={{ label: 'API reference', href: '/docs/langgraph/api/inject-agent' }} visual={ -
+            
 {`import { provideAgent } from '@threadplane/langgraph';
 
 export const appConfig: ApplicationConfig = {
diff --git a/apps/website/src/app/not-found.tsx b/apps/website/src/app/not-found.tsx
index 1c3857d60..b11d978d1 100644
--- a/apps/website/src/app/not-found.tsx
+++ b/apps/website/src/app/not-found.tsx
@@ -1,4 +1,3 @@
-import { tokens } from '@threadplane/design-tokens';
 import { Container } from '../components/ui/Container';
 import { Section } from '../components/ui/Section';
 import { Eyebrow } from '../components/ui/Eyebrow';
@@ -13,37 +12,16 @@ export default function NotFound() {
   return (
     
-
- 404 -

+
+ 404 +

Page not found.

-

+

The page you were looking for doesn't exist. It may have moved, or the link you followed might be broken.

-
+
diff --git a/apps/website/src/app/page.tsx b/apps/website/src/app/page.tsx index 28cd0e628..883de97ef 100644 --- a/apps/website/src/app/page.tsx +++ b/apps/website/src/app/page.tsx @@ -18,7 +18,6 @@ import { FinalCTA } from '../components/landing/FinalCTA'; import { RecentArticles } from '../components/landing/RecentArticles'; import { Section } from '../components/ui/Section'; import { Container } from '../components/ui/Container'; -import { tokens } from '@threadplane/design-tokens'; import { createPageMetadata, LONG_SUBHEAD, PRIMARY_TAGLINE } from '../lib/site-metadata'; export const metadata = createPageMetadata({ @@ -57,7 +56,7 @@ async function buildPanes(media: SectionMedia, clipUrl: string): Promise 1 ? block.label : 'Code', content: ( -
+
), @@ -72,7 +71,7 @@ async function buildPanes(media: SectionMedia, clipUrl: string): Promise -
+
{/* Mounted only when its tab is selected — `MediumSwitcher` renders one pane at a time, so this iframe is never requested on page load. @@ -84,7 +83,7 @@ async function buildPanes(media: SectionMedia, clipUrl: string): Promise
@@ -122,7 +121,7 @@ export default async function HomePage() { headline="Build the Angular UI layer for production agents." body={ <> - provideAgent + injectAgent() give you headless chat, durable threads, interrupts, tool progress, and generative UI. LangGraph and AG-UI adapters share the contract, so teams can swap runtimes without rewriting the Angular surface. + provideAgent + injectAgent() give you headless chat, durable threads, interrupts, tool progress, and generative UI. LangGraph and AG-UI adapters share the contract, so teams can swap runtimes without rewriting the Angular surface. } bullets={[ @@ -195,10 +194,10 @@ export default async function HomePage() { headline="Nothing irreversible happens without a human." body={ <> - interrupt() freezes the graph + interrupt() freezes the graph mid-run and the pause lives in the checkpoint, not in component state. Your UI renders the proposal, the human answers, and{' '} - submit({'{ resume }'}) continues + submit({'{ resume }'}) continues the run — with the decision written back beside the action it gated. } diff --git a/apps/website/src/app/pilot-to-prod/page.tsx b/apps/website/src/app/pilot-to-prod/page.tsx index ebf8de61f..1c01c2dd2 100644 --- a/apps/website/src/app/pilot-to-prod/page.tsx +++ b/apps/website/src/app/pilot-to-prod/page.tsx @@ -1,4 +1,3 @@ -import { tokens } from '@threadplane/design-tokens'; import { Container } from '../../components/ui/Container'; import { Section } from '../../components/ui/Section'; import { Eyebrow } from '../../components/ui/Eyebrow'; @@ -25,40 +24,19 @@ export default function PilotToProdPage() { {/* Hero */}
-
- Pilot to production -

+
+ Pilot to production +

8 weeks. One working agent. Production-ready patterns.

-

+

Pilot-to-Prod is a concierge engagement. We ship your first Angular agent on your real data, in your real app — and your engineers own it at the end.

-
+
-
+
Fixed scope Source delivered IP yours @@ -88,9 +66,9 @@ export default function PilotToProdPage() { cta={{ label: 'See sample roadmap', href: '#whitepaper-block' }} visual={ -
- Roadmap draft -
    +
    + Roadmap draft +
      {[ ['W1', 'Stakeholder interviews + workflow audit'], ['W2', 'Agent shortlist + integration plan'], @@ -98,15 +76,9 @@ export default function PilotToProdPage() { ['W6–7', 'Harden + observability + deploy'], ['W8', 'Train your team · handoff'], ].map(([w, desc]) => ( -
    • - {w} - {desc} +
    • + {w} + {desc}
    • ))}
    @@ -139,7 +111,7 @@ export default function PilotToProdPage() { Cockpit reference app — live chat surface ready to receive a message @@ -168,9 +140,9 @@ export default function PilotToProdPage() { cta={{ label: 'Production patterns', href: '/docs/langgraph/guides/deployment' }} visual={ -
    - Production checklist -
      +
      + Production checklist +
        {[ 'Streaming latency budget defined', 'Tool-call error boundaries wired', @@ -179,13 +151,8 @@ export default function PilotToProdPage() { 'On-call runbook delivered', 'Deploy + rollback tested', ].map((item) => ( -
      • - +
      • + {item}
      • ))} @@ -198,32 +165,13 @@ export default function PilotToProdPage() { {/* Outcomes */}
        -
        - What you walk away with -

        +
        + What you walk away with +

        A working agent. A trained team. A runbook.

        -
        +
        {[ { t: 'Working demo', d: 'Live on your data, in your app — not in a sandbox.' }, { t: 'Hardened patterns', d: 'Error/fallback/observability built in from the start.' }, @@ -231,24 +179,10 @@ export default function PilotToProdPage() { { t: 'Trained team', d: 'Your engineers own the framework and the runbook.' }, ].map((o) => ( -

        +

        {o.t}

        -

        +

        {o.d}

        @@ -263,30 +197,12 @@ export default function PilotToProdPage() { {/* Contact anchor */}
        -
        - Discovery call -

        +
        + Discovery call +

        Tell us about your stack.

        -

        +

        30-minute discovery call. We'll dig into your Angular surface, your agent-eligible workflows, and whether Pilot-to-Prod is the right fit. No pitch deck.

        -
        +
        MIT Vercel json-render Google A2UI @@ -85,18 +63,8 @@ export default async function RenderPage() { cta={{ label: 'See @threadplane/render docs', href: '/docs/render/getting-started/introduction' }} visual={ -
        -
        +
        +
        {`{ "type": "card", "props": { @@ -106,19 +74,14 @@ export default async function RenderPage() { } }`}
        -
        -
        +
        +
        AI-rendered · YourCardComponent
        -
        +
        Q3 revenue: $4.2M
        -
        +18% vs Q2
        +
        +18% vs Q2
        diff --git a/apps/website/src/app/solutions/[slug]/page.tsx b/apps/website/src/app/solutions/[slug]/page.tsx index 790ad2a9d..f4bbdfa5b 100644 --- a/apps/website/src/app/solutions/[slug]/page.tsx +++ b/apps/website/src/app/solutions/[slug]/page.tsx @@ -1,6 +1,5 @@ import { notFound } from 'next/navigation'; import Link from 'next/link'; -import { tokens } from '@threadplane/design-tokens'; import { getSolutionBySlug, getAllSolutionSlugs, @@ -47,56 +46,19 @@ function PainPoints({ items, accent }: { items: SolutionPainPoint[]; accent: str return (
        -
        - The problem -

        +
        + The problem +

        Why this is hard today.

        -
        +
        {items.map((p) => ( -

        +

        {p.title}

        -

        +

        {p.description}

        @@ -119,90 +81,38 @@ function Architecture({ return (
        -
        - Architecture -

        +
        + Architecture +

        How the three libraries compose.

        -

        +

        {intro}

        -
        +
        {layers.map((l) => { const href = LIBRARY_HREF[l.library]; const cardInner = ( - -
        -

        + +
        +

        {l.library}

        {l.pkg}
        -

        +

        {l.role}

        {href ? ( - + See {l.library} docs → ) : null}
        ); return href ? ( - + {cardInner} ) : ( @@ -219,57 +129,19 @@ function Capabilities({ items, accent }: { items: ProofPoint[]; accent: string } return (
        -
        - What you ship -

        +
        + What you ship +

        Capabilities the framework delivers.

        -
        +
        {items.map((p) => ( -
        +
        {p.metric}
        -

        +

        {p.label}

        @@ -290,37 +162,15 @@ export default async function SolutionPage({ params }: PageProps) { {/* Hero — solution-tinted accent */}
        -
        - {solution.eyebrow} -

        +
        + {solution.eyebrow} +

        {solution.title}

        -

        +

        {solution.subtitle}

        -
        +
        diff --git a/apps/website/src/app/solutions/page.tsx b/apps/website/src/app/solutions/page.tsx index a57572b67..1cc5a4afa 100644 --- a/apps/website/src/app/solutions/page.tsx +++ b/apps/website/src/app/solutions/page.tsx @@ -1,5 +1,4 @@ import Link from 'next/link'; -import { tokens } from '@threadplane/design-tokens'; import { Container } from '../../components/ui/Container'; import { Section } from '../../components/ui/Section'; import { Eyebrow } from '../../components/ui/Eyebrow'; @@ -23,33 +22,12 @@ export default function SolutionsIndexPage() { {/* Hero */}
        -
        - Solutions -

        +
        + Solutions +

        AI agents for how enterprises actually work.

        -

        +

        Streaming, generative UI, and human-in-the-loop patterns that enterprise use cases demand — wired into Angular from day one.

        @@ -59,78 +37,31 @@ export default function SolutionsIndexPage() { {/* Solutions grid */}
        -
        - By use case -

        +
        + By use case +

        Where agents earn their keep.

        -
        +
        {SOLUTIONS.map((s) => ( - - - {s.eyebrow} -

        + + + {s.eyebrow} +

        {s.title.replace('\n', ' ')}

        -

        +

        {s.subtitle}

        -
        +
        {s.proofPoints.slice(0, 2).map((p) => ( {p.metric} {p.label} ))}
        - + See the solution → diff --git a/apps/website/src/app/thanks/page.tsx b/apps/website/src/app/thanks/page.tsx index fdcb58c4f..b8d76328c 100644 --- a/apps/website/src/app/thanks/page.tsx +++ b/apps/website/src/app/thanks/page.tsx @@ -1,5 +1,4 @@ // SPDX-License-Identifier: MIT -import { tokens } from '@threadplane/design-tokens'; import { Container } from '../../components/ui/Container'; import { Section } from '../../components/ui/Section'; import { Eyebrow } from '../../components/ui/Eyebrow'; @@ -26,46 +25,18 @@ export default async function ThanksPage({ searchParams }: PageProps) { return (
        -
        - Payment received -

        +
        + Payment received +

        Thanks for your purchase.

        -

        - Your @threadplane/chat license token will be emailed to the address on your receipt within a few minutes. Paste it into your app's provideChat() config to activate. +

        + Your @threadplane/chat license token will be emailed to the address on your receipt within a few minutes. Paste it into your app's provideChat() config to activate.

        -

        +

        If you don't see the email within 10 minutes, check spam or contact us.

        -
        +
        diff --git a/apps/website/src/styles/pages.css b/apps/website/src/styles/pages.css index df170a587..d988f24c7 100644 --- a/apps/website/src/styles/pages.css +++ b/apps/website/src/styles/pages.css @@ -369,3 +369,1040 @@ .docs-shell-body { background: var(--color-surface); } + +/* About page — app/about/page.tsx */ +.about-section-inner { + max-width: 720px; +} +.about-eyebrow-spaced { + margin-bottom: 16px; +} +.about-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 16px; + letter-spacing: -0.02em; +} +.about-body { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0; + margin-bottom: 16px; + max-width: 60ch; +} +.about-body-last { + margin-bottom: 0; +} +.about-h2 { + font-family: var(--font-garamond); + font-size: var(--text-h2); + color: var(--color-text-primary); + margin: 0; + margin-bottom: 12px; +} +.about-h2-spaced { + margin-top: 32px; +} +.about-link { + color: var(--color-accent); +} + +/* Solutions index page — app/solutions/page.tsx */ +.sol-index-hero-inner { + max-width: 820px; + margin: 0 auto; + text-align: center; +} +.sol-index-eyebrow-spaced { + margin-bottom: 16px; +} +.sol-index-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 24px; + letter-spacing: -0.02em; +} +.sol-index-hero-body { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto; + max-width: 640px; +} +.sol-index-grid-header { + text-align: center; + margin-bottom: 48px; +} +.sol-index-eyebrow-tight { + margin-bottom: 12px; +} +.sol-index-h2 { + font-family: var(--font-garamond); + font-size: var(--text-h2); + line-height: var(--text-h2--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + letter-spacing: -0.015em; +} +.sol-index-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 16px; + max-width: 1100px; + margin: 0 auto; +} +.sol-index-card-link { + text-decoration: none; +} +.sol-index-card { + height: 100%; + display: flex; + flex-direction: column; +} +.sol-index-card-title { + font-family: var(--font-inter); + font-size: 22px; + line-height: 1.25; + font-weight: 600; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 12px; + white-space: pre-line; +} +.sol-index-card-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body); + line-height: var(--text-body--line-height); + color: var(--color-text-secondary); + margin: 0; + margin-bottom: 16px; + flex: 1; +} +.sol-index-pills-row { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 16px; +} +.sol-index-cta { + font-family: Inter, system-ui, sans-serif; + font-size: 14px; + font-weight: 600; + color: var(--color-accent); +} + +/* Solution detail page — app/solutions/[slug]/page.tsx */ +.sol-page-section-header { + text-align: center; + max-width: 720px; + margin: 0 auto 48px; +} +.sol-page-eyebrow-tight { + margin-bottom: 12px; +} +.sol-page-h2 { + font-family: var(--font-garamond); + font-size: var(--text-h2); + line-height: var(--text-h2--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + letter-spacing: -0.015em; +} +.sol-page-h2-spaced { + margin-bottom: 16px; +} +.sol-page-section-intro { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0; +} +.sol-page-grid-260 { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 16px; + max-width: 1100px; + margin: 0 auto; +} +.sol-page-grid-280 { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 16px; + max-width: 1100px; + margin: 0 auto; +} +.sol-page-card-h3 { + font-family: var(--font-inter); + font-size: 18px; + line-height: 1.3; + font-weight: 600; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 10px; +} +.sol-page-card-body { + font-family: var(--font-inter); + font-size: var(--text-body); + line-height: var(--text-body--line-height); + color: var(--color-text-secondary); + margin: 0; +} +.sol-page-arch-card { + height: 100%; +} +.sol-page-arch-card-header { + display: flex; + align-items: baseline; + gap: 8px; + margin-bottom: 12px; +} +.sol-page-arch-title { + font-family: "EB Garamond", Georgia, serif; + font-size: 22px; + font-weight: 700; + color: var(--color-text-primary); + margin: 0; +} +.sol-page-role { + font-family: var(--font-inter); + font-size: var(--text-body); + line-height: var(--text-body--line-height); + color: var(--color-text-secondary); + margin: 0; +} +.sol-page-role-linked { + margin-bottom: 16px; +} +.sol-page-arch-cta { + font-family: Inter, system-ui, sans-serif; + font-size: 14px; + font-weight: 600; + color: var(--color-accent); +} +.sol-page-link-plain { + text-decoration: none; +} +.sol-page-metric { + font-family: "EB Garamond", Georgia, serif; + font-style: italic; + font-size: 32px; + font-weight: 700; + line-height: 1.1; + margin: 0; + margin-bottom: 12px; +} +.sol-page-hero-inner { + max-width: 820px; + margin: 0 auto; + text-align: center; +} +.sol-page-eyebrow-spaced { + margin-bottom: 16px; +} +.sol-page-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 24px; + letter-spacing: -0.02em; + white-space: pre-line; +} +.sol-page-hero-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto 32px; + max-width: 640px; +} +.sol-page-hero-buttons { + display: flex; + justify-content: center; + gap: 12px; + flex-wrap: wrap; +} + +/* Pilot-to-Prod page — app/pilot-to-prod/page.tsx */ +.pilot-hero-inner { + max-width: 880px; + margin: 0 auto; + text-align: center; +} +.pilot-eyebrow-spaced { + margin-bottom: 16px; +} +.pilot-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 24px; + letter-spacing: -0.02em; +} +.pilot-hero-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto 32px; + max-width: 640px; +} +.pilot-hero-buttons { + display: flex; + justify-content: center; + gap: 12px; + flex-wrap: wrap; + margin-bottom: 20px; +} +.pilot-hero-pills { + display: flex; + justify-content: center; + gap: 8px; + flex-wrap: wrap; +} +.pilot-visual-panel { + padding: 28px; + min-height: 320px; + background: var(--color-surface); +} +.pilot-eyebrow-tight { + margin-bottom: 12px; +} +.pilot-roadmap-list { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: 12px; +} +.pilot-roadmap-item { + display: flex; + gap: 12px; + align-items: baseline; +} +.pilot-roadmap-week { + font-family: "JetBrains Mono", monospace; + font-size: 11px; + color: var(--color-accent); + font-weight: 700; + flex: 0 0 40px; +} +.pilot-roadmap-desc { + font-size: 14px; + color: var(--color-text-secondary); +} +.pilot-screenshot { + display: block; + width: 100%; + height: auto; +} +.pilot-checklist-list { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: 8px; +} +.pilot-checklist-item { + display: flex; + gap: 10px; + align-items: center; + font-size: 14px; + color: var(--color-text-secondary); +} +.pilot-checklist-badge { + width: 18px; + height: 18px; + border-radius: var(--radius-full); + background: var(--color-accent); + color: var(--color-text-inverted); + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: 700; +} +.pilot-section-header { + max-width: 720px; + margin: 0 auto; + text-align: center; + margin-bottom: 48px; +} +.pilot-h2 { + font-family: var(--font-garamond); + font-size: var(--text-h2); + line-height: var(--text-h2--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + letter-spacing: -0.015em; +} +.pilot-h2-spaced { + margin-bottom: 16px; +} +.pilot-outcomes-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 16px; + max-width: 1000px; + margin: 0 auto; +} +.pilot-outcome-h3 { + font-family: var(--font-inter); + font-size: 17px; + line-height: 1.3; + font-weight: 600; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 8px; +} +.pilot-outcome-body { + font-family: var(--font-inter); + font-size: 14px; + line-height: var(--text-body--line-height); + color: var(--color-text-secondary); + margin: 0; +} +.pilot-contact-inner { + max-width: 720px; + margin: 0 auto; + text-align: center; +} +.pilot-contact-body { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 0 32px 0; +} + +/* Render landing page — app/render/page.tsx */ +.render-page-hero-inner { + max-width: 820px; + margin: 0 auto; + text-align: center; +} +.render-page-eyebrow-spaced { + margin-bottom: 16px; +} +.render-page-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 24px; + letter-spacing: -0.02em; +} +.render-page-hero-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto 32px; + max-width: 640px; +} +.render-page-hero-buttons { + display: flex; + justify-content: center; + gap: 12px; + flex-wrap: wrap; + margin-bottom: 20px; +} +.render-page-hero-pills { + display: flex; + justify-content: center; + gap: 8px; + flex-wrap: wrap; +} +.render-page-visual-panel { + padding: 24px; + min-height: 320px; + background: var(--color-surface); +} +.render-page-spec-block { + background: var(--color-surface-tinted); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + padding: 12px; + margin-bottom: 12px; + font-family: "JetBrains Mono", monospace; + font-size: 11px; + color: var(--color-text-secondary); + white-space: pre; +} +.render-page-rendered-card { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + padding: 16px; +} +.render-page-ai-label { + font-family: "JetBrains Mono", monospace; + font-size: 11px; + color: var(--color-accent); + margin-bottom: 8px; +} +.render-page-card-title { + font-family: "EB Garamond", Georgia, serif; + font-size: 18px; + font-weight: 700; + color: var(--color-text-primary); + margin-bottom: 4px; +} +.render-page-card-value { + font-size: 13px; + color: var(--color-text-secondary); +} + +/* Chat landing page — app/chat/page.tsx */ +.chat-page-hero-inner { + max-width: 820px; + margin: 0 auto; + text-align: center; +} +.chat-page-eyebrow-spaced { + margin-bottom: 16px; +} +.chat-page-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 24px; + letter-spacing: -0.02em; +} +.chat-page-hero-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto 32px; + max-width: 640px; +} +.chat-page-hero-buttons { + display: flex; + justify-content: center; + gap: 12px; + flex-wrap: wrap; + margin-bottom: 20px; +} +.chat-page-hero-pills { + display: flex; + justify-content: center; + gap: 8px; + flex-wrap: wrap; +} +.chat-page-visual-panel { + padding: 24px; + min-height: 320px; + background: linear-gradient(180deg, #fff, #f8fafc); +} +.chat-page-visual-pills-row { + display: flex; + gap: 8px; + margin-bottom: 16px; +} +.chat-page-tool-label { + font-family: "JetBrains Mono", monospace; + font-size: 11px; + color: var(--color-text-muted); + margin-bottom: 8px; +} +.chat-page-result-block { + background: var(--color-surface-tinted); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + padding: 12px; + font-family: "JetBrains Mono", monospace; + font-size: 12px; + color: var(--color-text-primary); +} +.chat-page-replay-footer { + margin-top: 16px; + padding-top: 12px; + border-top: 1px solid var(--color-border); + font-family: "JetBrains Mono", monospace; + font-size: 11px; + color: var(--color-text-secondary); +} + +/* AG-UI landing page — app/ag-ui/page.tsx */ +.ag-ui-page-hero-inner { + max-width: 820px; + margin: 0 auto; + text-align: center; +} +.ag-ui-page-eyebrow-spaced { + margin-bottom: 16px; +} +.ag-ui-page-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 24px; + letter-spacing: -0.02em; +} +.ag-ui-page-hero-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto 32px; + max-width: 680px; +} +.ag-ui-page-hero-buttons { + display: flex; + justify-content: center; + gap: 12px; + flex-wrap: wrap; + margin-bottom: 12px; +} +.ag-ui-page-adapter-note { + font-size: 13px; + color: var(--color-text-muted); + margin-bottom: 20px; +} +.ag-ui-page-adapter-link { + color: var(--color-accent); +} +.ag-ui-page-hero-pills { + display: flex; + justify-content: center; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 24px; +} +.ag-ui-page-langgraph-note { + font-family: Inter, system-ui, sans-serif; + font-size: 13px; + color: var(--color-text-muted); + margin: 0 auto; + max-width: 520px; +} +.ag-ui-page-langgraph-link { + color: var(--color-accent); + text-decoration: none; + font-weight: 600; +} +.ag-ui-page-code-pre { + margin: 0; + padding: 20px 22px; + background: #1a1b26; + color: #a9b1d6; + font-family: "JetBrains Mono", monospace; + font-size: 13px; + line-height: 1.6; + min-height: 320px; + overflow: auto; +} + +/* LangGraph landing page — app/langgraph/page.tsx */ +.langgraph-page-hero-inner { + max-width: 820px; + margin: 0 auto; + text-align: center; +} +.langgraph-page-eyebrow-spaced { + margin-bottom: 16px; +} +.langgraph-page-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 24px; + letter-spacing: -0.02em; +} +.langgraph-page-hero-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto 32px; + max-width: 640px; +} +.langgraph-page-hero-buttons { + display: flex; + justify-content: center; + gap: 12px; + flex-wrap: wrap; + margin-bottom: 12px; +} +.langgraph-page-adapter-note { + font-size: 13px; + color: var(--color-text-muted); + margin-bottom: 20px; +} +.langgraph-page-adapter-link { + color: var(--color-accent); +} +.langgraph-page-hero-pills { + display: flex; + justify-content: center; + gap: 8px; + flex-wrap: wrap; +} +.langgraph-page-code-pre { + margin: 0; + padding: 20px 22px; + background: #1a1b26; + color: #a9b1d6; + font-family: "JetBrains Mono", monospace; + font-size: 13px; + line-height: 1.6; + min-height: 320px; + overflow: auto; +} + +/* Blog index page — app/blog/page.tsx */ +.blog-index-page { + padding-top: 80px; + background: var(--color-canvas); + min-height: 100vh; +} +.blog-index-inner { + max-width: 960px; + margin: 0 auto; + padding: 35px 24px 64px; +} +.blog-index-header { + margin-bottom: 32px; +} +.blog-index-eyebrow-spaced { + margin-bottom: 16px; +} +.blog-index-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + letter-spacing: -0.02em; + color: var(--color-text-primary); + margin: 0 0 16px; +} +.blog-index-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0; + max-width: 60ch; +} +.blog-index-empty { + padding: 48px; + text-align: center; + background: var(--color-surface-tinted); + border: 1px solid var(--color-border); + border-radius: 12px; +} +.blog-index-empty-text { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 0 16px; +} +.blog-index-empty-link { + color: var(--color-accent); + text-decoration: underline; + font-weight: 500; +} +.blog-index-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 16px; +} + +/* Blog post page — app/blog/[slug]/page.tsx */ +.blog-post-page { + padding-top: 80px; + background: var(--color-canvas); + min-height: 100vh; +} +.blog-post-layout { + display: flex; + justify-content: center; + align-items: flex-start; + max-width: 1280px; + margin: 0 auto; + gap: 0; +} +.blog-post-article { + width: 100%; + max-width: 768px; + padding: 64px 24px; + flex-shrink: 0; +} +.blog-post-header { + margin-bottom: 48px; +} +.blog-post-eyebrow-spaced { + margin-bottom: 24px; +} +.blog-post-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + letter-spacing: -0.02em; + color: var(--color-text-primary); + margin: 0 0 24px; +} +.blog-post-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 0 32px; + max-width: 60ch; +} +.blog-post-byline-row { + display: flex; + align-items: center; + gap: 20px; + flex-wrap: wrap; + margin-bottom: 20px; +} + +/* Thanks page — app/thanks/page.tsx */ +.thanks-inner { + text-align: center; + max-width: 640px; + margin: 0 auto; +} +.thanks-eyebrow-spaced { + margin-bottom: 16px; +} +.thanks-h1 { + font-family: var(--font-garamond); + font-weight: 700; + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + color: var(--color-text-primary); + margin: 0; + margin-bottom: 16px; + letter-spacing: -0.02em; +} +.thanks-body { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto 24px; +} +.thanks-code { + font-family: "JetBrains Mono", monospace; +} +.thanks-note { + font-family: var(--font-inter); + font-size: 13px; + line-height: 1.6; + color: var(--color-text-muted); + margin: 0 auto 32px; +} +.thanks-buttons { + display: flex; + gap: 12px; + justify-content: center; + flex-wrap: wrap; +} + +/* Error boundary page — app/error.tsx */ +.error-page-inner { + max-width: 640px; + margin: 0 auto; + text-align: center; +} +.error-page-eyebrow-spaced { + margin-bottom: 16px; +} +.error-page-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 16px; + letter-spacing: -0.02em; +} +.error-page-body { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto 12px; + max-width: 480px; +} +.error-page-digest { + font-family: "JetBrains Mono", monospace; + font-size: 12px; + color: var(--color-text-muted); + margin: 0 0 28px; +} +.error-page-spacer { + height: 28px; +} +.error-page-buttons { + display: flex; + justify-content: center; + gap: 12px; + flex-wrap: wrap; +} + +/* Pricing page — app/pricing/page.tsx */ +.pricing-page-hero-inner { + text-align: center; + max-width: 760px; + margin: 0 auto; +} +.pricing-page-eyebrow-spaced { + margin-bottom: 16px; +} +.pricing-page-h1 { + font-family: var(--font-garamond); + font-weight: 700; + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + color: var(--color-text-primary); + margin: 0; + letter-spacing: -0.02em; +} +.pricing-page-eyebrow-tight { + margin-bottom: 12px; +} +.pricing-page-h2 { + font-family: var(--font-garamond); + font-size: var(--text-h2); + margin: 0; + margin-bottom: 16px; + color: var(--color-text-primary); +} +.pricing-page-compat-body { + margin: 0; + margin-bottom: 24px; + color: var(--color-text-secondary); + max-width: 60ch; +} + +/* Home page — app/page.tsx */ +.home-code-frame { + border-radius: 14px; + overflow: hidden; + border: 1px solid var(--color-border); +} +.home-live-frame { + position: relative; + width: 100%; + aspect-ratio: 16 / 10; + background: #15161f; +} +.home-live-iframe { + width: 100%; + height: 100%; + border: none; + display: block; +} +.home-code { + font-family: "JetBrains Mono", monospace; +} + +/* Contact page — app/contact/page.tsx */ +.contact-page-inner { + max-width: 720px; +} +.contact-page-eyebrow-spaced { + margin-bottom: 16px; +} +.contact-page-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 16px; + letter-spacing: -0.02em; +} +.contact-page-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0; + margin-bottom: 24px; + max-width: 60ch; +} +.contact-page-sla-wrap { + margin-bottom: 24px; +} +.contact-page-links-row { + margin-top: 32px; + display: flex; + flex-direction: column; + gap: 16px; +} + +/* Not-found page — app/not-found.tsx */ +.nf-inner { + max-width: 640px; + margin: 0 auto; + text-align: center; +} +.nf-eyebrow-spaced { + margin-bottom: 16px; +} +.nf-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 16px; + letter-spacing: -0.02em; +} +.nf-body { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0 auto 32px; + max-width: 480px; +} +.nf-buttons { + display: flex; + justify-content: center; + gap: 12px; + flex-wrap: wrap; +}