Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions apps/website/src/app/solutions/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { notFound } from 'next/navigation';
import { tokens } from '@cacheplane/design-tokens';
import { getSolutionBySlug, getAllSolutionSlugs } from '../../../lib/solutions-data';
import { SolutionHero } from '../../../components/landing/solutions/SolutionHero';
import { SolutionProblem } from '../../../components/landing/solutions/SolutionProblem';
import { SolutionArchitecture } from '../../../components/landing/solutions/SolutionArchitecture';
import { SolutionProofPoints } from '../../../components/landing/solutions/SolutionProofPoints';
import { SolutionFooterCTA } from '../../../components/landing/solutions/SolutionFooterCTA';
import { WhitePaperSection } from '../../../components/landing/WhitePaperSection';

interface PageProps {
params: Promise<{ slug: string }>;
}

export function generateStaticParams() {
return getAllSolutionSlugs().map(slug => ({ slug }));
}

export async function generateMetadata({ params }: PageProps) {
const { slug } = await params;
const solution = getSolutionBySlug(slug);
if (!solution) return {};
return {
title: solution.metaTitle,
description: solution.metaDescription,
};
}

export default async function SolutionPage({ params }: PageProps) {
const { slug } = await params;
const solution = getSolutionBySlug(slug);
if (!solution) notFound();

return (
<div style={{ background: tokens.gradient.bgFlow, position: 'relative', overflow: 'hidden' }}>
{/* Ambient gradient blobs */}
<div
style={{
position: 'absolute',
width: 600,
height: 600,
borderRadius: '50%',
background: `radial-gradient(circle, rgba(${solution.rgb}, 0.12) 0%, transparent 70%)`,
top: -200,
left: -150,
filter: 'blur(80px)',
pointerEvents: 'none',
}}
aria-hidden="true"
/>
<div
style={{
position: 'absolute',
width: 500,
height: 500,
borderRadius: '50%',
background: tokens.gradient.cool,
top: 800,
right: -100,
filter: 'blur(80px)',
pointerEvents: 'none',
}}
aria-hidden="true"
/>
<div
style={{
position: 'absolute',
width: 400,
height: 400,
borderRadius: '50%',
background: `radial-gradient(circle, rgba(${solution.rgb}, 0.08) 0%, transparent 70%)`,
top: 2200,
left: -100,
filter: 'blur(80px)',
pointerEvents: 'none',
}}
aria-hidden="true"
/>

<SolutionHero solution={solution} />
<SolutionProblem
color={solution.color}
painPoints={solution.painPoints}
/>
<SolutionArchitecture
color={solution.color}
intro={solution.architectureIntro}
layers={solution.architectureLayers}
/>
<SolutionProofPoints
color={solution.color}
proofPoints={solution.proofPoints}
/>
<WhitePaperSection />
<SolutionFooterCTA
color={solution.color}
headline={solution.ctaHeadline}
subtext={solution.ctaSubtext}
/>
</div>
);
}
100 changes: 100 additions & 0 deletions apps/website/src/app/solutions/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { tokens } from '@cacheplane/design-tokens';
import { SolutionsGrid } from '../../components/landing/solutions/SolutionsGrid';
import { WhitePaperSection } from '../../components/landing/WhitePaperSection';
import { PilotFooterCTA } from '../../components/landing/PilotFooterCTA';

export const metadata = {
title: 'Solutions — Angular Agent Framework',
description: 'See how Angular Agent Framework solves enterprise challenges — compliance, analytics, and customer support.',
};

export default function SolutionsIndexPage() {
return (
<div style={{ background: tokens.gradient.bgFlow, position: 'relative', overflow: 'hidden' }}>
{/* Ambient gradient blobs */}
<div
style={{
position: 'absolute',
width: 600,
height: 600,
borderRadius: '50%',
background: tokens.gradient.warm,
top: -200,
left: -150,
filter: 'blur(80px)',
pointerEvents: 'none',
}}
aria-hidden="true"
/>
<div
style={{
position: 'absolute',
width: 500,
height: 500,
borderRadius: '50%',
background: tokens.gradient.cool,
top: 800,
right: -100,
filter: 'blur(80px)',
pointerEvents: 'none',
}}
aria-hidden="true"
/>

{/* Hero */}
<section style={{ position: 'relative', overflow: 'hidden', padding: '0 2rem' }}>
<div
style={{
maxWidth: '56rem',
margin: '0 auto',
textAlign: 'center',
position: 'relative',
zIndex: 1,
}}
className="py-24 md:py-32"
>
<p
style={{
fontFamily: 'var(--font-mono, "JetBrains Mono", monospace)',
fontSize: 11,
letterSpacing: '0.08em',
color: tokens.colors.accent,
textTransform: 'uppercase',
marginBottom: '1.5rem',
}}
>
Solutions
</p>
<h1
style={{
fontFamily: 'var(--font-garamond, "EB Garamond", Georgia, serif)',
fontSize: 'clamp(36px, 5vw, 56px)',
fontWeight: 700,
lineHeight: 1.1,
color: tokens.colors.textPrimary,
marginBottom: '1.25rem',
}}
>
AI agents built for how enterprises actually work
</h1>
<p
style={{
fontFamily: 'Inter, sans-serif',
fontSize: 18,
color: tokens.colors.textSecondary,
maxWidth: '52ch',
margin: '0 auto',
lineHeight: 1.6,
}}
>
Angular Agent Framework gives your team the streaming, generative UI, and human-in-the-loop patterns that enterprise use cases demand.
</p>
</div>
</section>

<SolutionsGrid />
<WhitePaperSection />
<PilotFooterCTA />
</div>
);
}
176 changes: 176 additions & 0 deletions apps/website/src/components/landing/solutions/SolutionArchitecture.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
'use client';
import { motion } from 'framer-motion';
import Link from 'next/link';
import { tokens } from '@cacheplane/design-tokens';
import type { ArchitectureLayer } from '../../../lib/solutions-data';

const LIBRARY_META: Record<string, { color: string; rgb: string; href: string }> = {
Agent: { color: tokens.colors.accent, rgb: '0,64,144', href: '/angular' },
Render: { color: tokens.colors.renderGreen, rgb: '26,122,64', href: '/render' },
Chat: { color: tokens.colors.chatPurple, rgb: '90,0,200', href: '/chat' },
};

interface SolutionArchitectureProps {
color: string;
intro: string;
layers: ArchitectureLayer[];
}

function Connector({ fromRgb, toRgb }: { fromRgb: string; toRgb: string }) {
return (
<div style={{ display: 'flex', justifyContent: 'center', padding: '2px 0' }}>
<div
style={{
width: 2,
height: 28,
background: `linear-gradient(to bottom, rgba(${fromRgb}, 0.3), rgba(${toRgb}, 0.3))`,
borderRadius: 1,
}}
/>
</div>
);
}

export function SolutionArchitecture({ color, intro, layers }: SolutionArchitectureProps) {
return (
<section className="solution-arch" style={{ padding: '80px 32px' }}>
<style>{`
@media (max-width: 767px) {
.solution-arch { padding: 60px 20px !important; }
.solution-arch-card { padding: 24px 20px 20px !important; }
}
`}</style>
<motion.div
initial={{ opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
style={{ textAlign: 'center', marginBottom: 48 }}
>
<p
style={{
fontFamily: 'var(--font-mono, "JetBrains Mono", monospace)',
fontSize: '0.7rem',
textTransform: 'uppercase',
letterSpacing: '0.12em',
fontWeight: 700,
color,
marginBottom: 14,
}}
>
The Architecture
</p>
<h2
style={{
fontFamily: 'var(--font-garamond, "EB Garamond", Georgia, serif)',
fontSize: 'clamp(26px, 3.5vw, 46px)',
fontWeight: 800,
lineHeight: 1.1,
color: tokens.colors.textPrimary,
marginBottom: 10,
}}
>
Three libraries. One solution.
</h2>
<p
style={{
fontFamily: 'var(--font-garamond, "EB Garamond", Georgia, serif)',
fontStyle: 'italic',
fontSize: '1.05rem',
color: tokens.colors.textSecondary,
maxWidth: 560,
margin: '0 auto',
}}
>
{intro}
</p>
</motion.div>

<div style={{ maxWidth: 860, margin: '0 auto', display: 'flex', flexDirection: 'column' }}>
{layers.map((layer, i) => {
const meta = LIBRARY_META[layer.library];
const prevMeta = i > 0 ? LIBRARY_META[layers[i - 1].library] : null;
return (
<div key={layer.library}>
{i > 0 && prevMeta && (
<Connector fromRgb={prevMeta.rgb} toRgb={meta.rgb} />
)}
<motion.div
className="solution-arch-card"
initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: i * 0.1 }}
style={{
background: `rgba(${meta.rgb}, 0.03)`,
border: `1px solid rgba(${meta.rgb}, 0.15)`,
borderRadius: 14,
padding: '28px 28px 24px',
display: 'flex',
flexDirection: 'column',
gap: 10,
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<span
style={{
fontFamily: 'var(--font-mono, "JetBrains Mono", monospace)',
fontSize: '0.58rem',
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.07em',
padding: '2px 9px',
borderRadius: 5,
color: '#fff',
background: meta.color,
}}
>
{layer.library}
</span>
<span
style={{
fontFamily: 'var(--font-mono, "JetBrains Mono", monospace)',
fontSize: '0.76rem',
fontWeight: 700,
color: meta.color,
}}
>
{layer.pkg}
</span>
</div>

<p
style={{
fontFamily: 'Inter, sans-serif',
fontSize: '0.88rem',
color: tokens.colors.textSecondary,
lineHeight: 1.6,
margin: 0,
}}
>
{layer.role}
</p>

<Link
href={meta.href}
style={{
fontFamily: 'var(--font-mono, "JetBrains Mono", monospace)',
fontSize: '0.72rem',
fontWeight: 700,
color: meta.color,
textDecoration: 'none',
display: 'inline-flex',
alignItems: 'center',
gap: 4,
}}
>
Explore {layer.library} →
</Link>
</motion.div>
</div>
);
})}
</div>
</section>
);
}
Loading
Loading