feat: redesign archgw -> plano + website

This commit is contained in:
Musa 2025-11-05 16:29:09 -08:00
parent 126b029345
commit 2a50b02d03
56 changed files with 4959 additions and 264 deletions

View file

@ -0,0 +1,90 @@
import React from 'react';
interface AsciiDiagramProps {
title?: string;
content: string;
className?: string;
}
export const AsciiDiagram: React.FC<AsciiDiagramProps> = ({
title,
content,
className = ""
}) => {
return (
<div className={`max-w-4xl mx-auto mb-8 ${className}`}>
{title && (
<h2 className="text-2xl font-bold text-gray-900 dark:text-zinc-50 mb-4">
{title}
</h2>
)}
<div className="bg-gray-900 dark:bg-gray-950 rounded-lg p-6 shadow-xl overflow-x-auto">
<pre
className="relative font-mono text-xs leading-none text-white m-0 whitespace-pre"
style={{ fontFamily: 'var(--font-jetbrains-mono), monospace' }}
>
<code>{content}</code>
</pre>
</div>
</div>
);
};
// Programmatic diagram builder for non-coders
interface DiagramStep {
id: string;
label: string;
type?: 'input' | 'inner' | 'regular';
x: number;
y: number;
}
interface FlowConnection {
from: string;
to: string;
label?: string;
}
interface DiagramConfig {
title: string;
steps: DiagramStep[];
connections: FlowConnection[];
}
// Simple ASCII diagram generator
export const createDiagram = (config: DiagramConfig): string => {
// This is a simplified version - you can extend this to automatically generate
// the ASCII art from the config
// For now, return the manually created diagrams
return '';
};
// Helper to create boxes
export const createBox = (
label: string,
type: 'input' | 'inner' | 'regular' = 'regular',
width: number = 20
): string[] => {
const padding = Math.max(0, Math.floor((width - label.length) / 2));
const spaces = ' '.repeat(padding);
const remaining = width - label.length - padding;
let chars;
switch (type) {
case 'input':
chars = { tl: '╔', tr: '╗', bl: '╚', br: '╝', h: '═', v: '║' };
break;
case 'inner':
chars = { tl: '┏', tr: '┓', bl: '┗', br: '┛', h: '━', v: '┃' };
break;
case 'regular':
default:
chars = { tl: '┌', tr: '┐', bl: '└', br: '┘', h: '─', v: '│' };
}
return [
`${chars.tl}${chars.h.repeat(width)}${chars.tr}`,
`${chars.v}${spaces}${label}${' '.repeat(remaining)}${chars.v}`,
`${chars.bl}${chars.h.repeat(width)}${chars.br}`,
];
};

View file

@ -0,0 +1,32 @@
import React from 'react';
import { createFlowDiagram, FlowDiagramConfig } from '@/utils/asciiBuilder';
import { AsciiDiagram } from './AsciiDiagram';
interface DiagramBuilderProps {
config: FlowDiagramConfig;
title?: string;
}
/**
* Simple Diagram Builder Component
*
* Usage:
*
* <DiagramBuilder
* config={{
* title: "My Process",
* width: 60,
* steps: [
* { label: "Start", type: "regular" },
* { label: "Process", type: "inner" },
* { label: "End", type: "regular" }
* ]
* }}
* />
*/
export const DiagramBuilder: React.FC<DiagramBuilderProps> = ({ config, title }) => {
const asciiDiagram = createFlowDiagram(config);
return <AsciiDiagram content={asciiDiagram} title={title} />;
};

View file

@ -0,0 +1,98 @@
import React from "react";
import Link from "next/link";
import Image from "next/image";
const footerLinks = {
company: [
{ label: "Product", href: "/product" },
{ label: "Use Cases", href: "/use-cases" },
{ label: "Blog", href: "/blog" },
{ label: "Plano LLMs", href: "/llms" }
],
developerResources: [
{ label: "Documentation", href: "/docs" }
]
};
export function Footer() {
return (
<footer className="relative overflow-hidden py-24 px-6 lg:px-[102px] pb-48" style={{ background: 'linear-gradient(to top right, #ffffff, #dcdfff)' }}>
<div className="max-w-[81rem] mx-auto relative z-10">
{/* Main Grid Layout */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-20">
{/* Left Column - Tagline and Copyright */}
<div className="flex flex-col">
<p className="font-sans text-2xl text-black tracking-[-1.7px]! leading-8 mb-8">
Plano is the powerful, intelligent platform that empowers teams to seamlessly build, automate, and scale agentic systems with ease.
</p>
{/* Copyright */}
<div className="mt-auto">
<p className="font-sans text-base text-black/63 tracking-[-0.8px]!">
© Katanemo Labs, Inc. 2025 / Plano by Katanemo Labs, Inc.
</p>
</div>
</div>
{/* Right Column - Navigation Links */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-8">
{/* Company Links */}
<div>
<h3 className="font-sans font-medium text-3xl text-black tracking-[-1.6px]! mb-6">
Company
</h3>
<nav className="space-y-4">
{footerLinks.company.map((link) => (
<Link
key={link.href}
href={link.href}
className="block font-sans text-xl text-black tracking-[-1px]! hover:text-[var(--primary)] transition-colors"
>
{link.label}
</Link>
))}
</nav>
</div>
{/* Developer Resources */}
<div>
<h3 className="font-sans font-medium text-3xl text-black tracking-[-1.6px]! mb-6">
Developer Resources
</h3>
<nav className="space-y-4">
{footerLinks.developerResources.map((link) => (
<Link
key={link.href}
href={link.href}
className="block font-sans text-xl text-black tracking-[-1px]! hover:text-[var(--primary)] transition-colors"
>
{link.label}
</Link>
))}
</nav>
</div>
</div>
</div>
</div>
{/* Half-Cut Plano Logo Background */}
<div className="absolute bottom-0 left-0 right-0 overflow-hidden pointer-events-none">
<div className="max-w-[81rem] mx-auto px-6 lg:px-[1px]">
<div className="relative w-full flex justify-start">
<Image
src="/LogoOutline.svg"
alt="Plano Logo"
width={1800}
height={200}
className="w-150 h-auto opacity-30 select-none"
style={{
transform: 'translateY(0%)', // Push logo down more while showing top part
transformOrigin: 'center bottom'
}}
/>
</div>
</div>
</div>
</footer>
);
}

View file

@ -0,0 +1,50 @@
import React from "react";
import { Button } from "./ui/button";
import Link from "next/link";
import { NetworkAnimation } from "./NetworkAnimation";
export function Hero() {
return (
<section className="relative pt-15 pb-6 px-6 lg:px-8">
<div className="hidden lg:block">
<NetworkAnimation />
</div>
<div className="max-w-[81rem] mx-auto relative z-10">
<div className="max-w-3xl mb-4">
{/* Version Badge */}
<div className="mb-6">
<div className="inline-flex items-center gap-2 px-4 py-1 rounded-full bg-[rgba(185,191,255,0.4)] border border-[var(--secondary)] shadow backdrop-blur">
<span className="text-sm font-medium text-black/65">v0.3.12</span>
<span className="text-sm font-medium text-black"></span>
<span className="text-sm font-[600] tracking-[-0.6px]! text-black">RAG Agent Launch!</span>
</div>
</div>
{/* Main Heading */}
<h1 className="text-5xl lg:text-7xl font-normal leading-tight tracking-tight text-black mb-4 flex flex-col -space-y-3">
<span className="font-sans">The AI-native </span>
<span className="font-sans font-medium text-[var(--secondary)]">network for agents</span>
</h1>
</div>
{/* Subheading with CTA Buttons on the right */}
<div className="max-w-7xl flex flex-col lg:flex-row lg:items-center lg:justify-between gap-6">
<p className="text-xl lg:text-2xl font-sans font-[500] tracking-[-1.92px]! text-black max-w-2xl">
Build and scale AI agents without handling the low-level plumbing.
</p>
{/* CTA Buttons */}
<div className="justify-self-end flex justify-end items-center gap-4">
<Button asChild>
<Link href="/get-started">Get started</Link>
</Button>
<Button variant="secondary" asChild>
<Link href="/docs">Documentation</Link>
</Button>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,161 @@
"use client";
import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Button } from "./ui/button";
const carouselData = [
{
id: 1,
category: "LAUNCH FASTER",
title: "Focus on core objectives",
description: "Plano handles the heavy lifting for agentic apps with purpose-built LLMs that automate request clarification, query routing, and data extraction. Ship faster without juggling prompt engineering or infrastructure details.",
image: "/LaunchFaster.svg"
},
{
id: 2,
category: "SCALE EFFICIENTLY",
title: "Build without limits",
description: "From prototype to production, Plano scales with your needs. Handle thousands of concurrent requests while maintaining consistent performance and reliability across your agent network.",
image: "/ShipConfidently.svg"
},
{
id: 3,
category: "DEPLOY CONFIDENTLY",
title: "Production-ready infrastructure",
description: "Enterprise-grade security, monitoring, and governance built-in. Deploy your agents with confidence knowing they're protected by battle-tested infrastructure and compliance frameworks.",
image: "/BuildWithChoice.svg"
},
{
id: 4,
category: "INTEGRATE SEAMLESSLY",
title: "Connect everything",
description: "Unified API access across all major LLM providers. Switch between models, combine capabilities, and route requests intelligently without vendor lock-in or complex integrations.",
image: "/Telemetry.svg"
},
{
id: 5,
category: "OPTIMIZE CONTINUOUSLY",
title: "Learn and improve",
description: "Built-in analytics and feedback loops help your agents get smarter over time. Track performance, identify bottlenecks, and optimize workflows with real-time insights and automated improvements.",
image: "/Contextual.svg"
}
];
export function IdeaToAgentSection() {
const [currentSlide, setCurrentSlide] = useState(0);
const [isAutoPlaying, setIsAutoPlaying] = useState(true);
// Auto-advance slides
useEffect(() => {
if (!isAutoPlaying) return;
const interval = setInterval(() => {
setCurrentSlide((prev) => (prev + 1) % carouselData.length);
}, 10000); // 10 seconds per slide
return () => clearInterval(interval);
}, [isAutoPlaying]);
const handleSlideClick = (index: number) => {
setCurrentSlide(index);
setIsAutoPlaying(false);
// Resume auto-play after 10 seconds
setTimeout(() => setIsAutoPlaying(true), 10000);
};
return (
<section className="relative py-24 px-6 lg:px-[102px]">
<div className="max-w-[81rem] mx-auto">
{/* Main Heading */}
<h2 className="font-sans font-normal text-4xl tracking-[-2.96px]! text-black mb-10">
Idea to agent without overhead
</h2>
{/* Progress Indicators */}
<div className="flex gap-2 mb-12">
{carouselData.map((_, index) => (
<button
key={index}
onClick={() => handleSlideClick(index)}
className="relative h-2 rounded-full overflow-hidden transition-all duration-300 hover:opacity-80"
style={{ width: index === currentSlide ? '292px' : '293px' }}
>
{/* Background */}
<div className="absolute inset-0 bg-black/6 rounded-full" />
{/* Active Progress */}
{index === currentSlide && (
<motion.div
className="absolute inset-0 bg-[#7780d9] rounded-full"
initial={{ width: 0 }}
animate={{ width: "100%" }}
transition={{ duration: 10, ease: "linear" }}
key={currentSlide}
/>
)}
{/* Completed State */}
{index < currentSlide && (
<div className="absolute inset-0 bg-purple-200/90 rounded-full" />
)}
</button>
))}
</div>
{/* Carousel Content */}
<div className="relative min-h-[300px] lg:min-h-[400px]">
<AnimatePresence mode="wait">
<motion.div
key={currentSlide}
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -50 }}
transition={{ duration: 0.5, ease: "easeInOut" }}
className="absolute inset-0"
>
<div className="flex flex-col lg:flex-row lg:justify-between lg:items-center lg:gap-12">
{/* Left Content */}
<div className="flex-1">
<div className="max-w-[692px] mt-6">
{/* Category */}
<p className="font-mono font-bold text-[#2a3178] text-xl tracking-[1.92px]! mb-4 leading-[1.102]">
{carouselData[currentSlide].category}
</p>
{/* Title */}
<h3 className="font-sans font-medium text-[#9797ea] text-5xl tracking-[-2.96px]! mb-7">
{carouselData[currentSlide].title}
</h3>
{/* Description */}
<div className="font-mono text-black text-lg leading-8 max-w-140">
<p className="mb-0">
{carouselData[currentSlide].description}
</p>
</div>
<Button className="mt-8">
Learn more
</Button>
</div>
</div>
{/* Right Image - Only show if current slide has an image */}
{carouselData[currentSlide].image && (
<div className="hidden lg:flex shrink-0 w-[400px] xl:w-[500px] justify-end items-center ">
<img
src={carouselData[currentSlide].image}
alt={carouselData[currentSlide].category}
className="w-full h-auto max-h-[450px] object-contain"
/>
</div>
)}
</div>
</motion.div>
</AnimatePresence>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,38 @@
import React from "react";
import { AsciiDiagram } from "@/components/AsciiDiagram";
import { diagrams } from "@/data/diagrams";
export function IntroSection() {
return (
<section className="relative bg-[#1a1a1a] text-white py-20 px-6 lg:px-[102px]">
<div className="max-w-324 mx-auto">
<div className="flex flex-col lg:flex-row gap-12 items-start">
{/* Left Content */}
<div className="flex-1">
{/* Heading */}
<h2 className="font-sans font-medium tracking-[-1.92px]! text-[#9797ea] text-4xl leading-[1.102] mb-6 max-w-[633px]">
Go beyond AI nascent demos
</h2>
{/* Body Text */}
<div className="font-mono tracking-[-0.96px]! text-white text-lg max-w-[713px]">
<p className="mb-0 leading-8">
Plano is the infrastructure layer for building, scaling, and routing AI agents. It sits between your app and your models, enforcing safety guardrails, orchestrating multi-agent workflows, and unifying access across large language models.
</p>
<br />
<p className="mb-0 leading-8">
Developers use Plano to build faster, platform teams use it to unify governance, and AI leads use it to run continuously learning systems that stay aligned, safe, and performant. From SaaS backends to distributed ecosystems, Plano turns raw agent logic into something you can deploy, monitor, and evolve in production.
</p>
</div>
</div>
{/* Right Diagram */}
<div className="lg:w-[660px] relative flex-shrink-0">
<AsciiDiagram content={diagrams.infrastructureLayer} className="max-w-none mx-0" />
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,18 @@
import React from "react";
import Image from "next/image";
export function Logo() {
return (
<div className="flex items-center">
{/* LogoMarkSquare SVG */}
<Image
src="/Logomark.svg"
alt="Plano Logo"
width={90}
height={20}
className="flex-shrink-0"
/>
</div>
);
}

View file

@ -0,0 +1,50 @@
import React from "react";
import Image from "next/image";
const customerLogos = [
{
name: "HuggingFace",
src: "/logos/huggingface.svg"
},
{
name: "T-Mobile",
src: "/logos/tmobile.svg"
},
{
name: "Chase",
src: "/logos/chase.svg"
},
{
name: "SanDisk",
src: "/logos/sandisk.svg"
},
{
name: "Oracle",
src: "/logos/oracle.svg"
}
];
export function LogoCloud() {
return (
<section className="relative py-8 px-6 lg:px-8">
<div className="max-w-[81rem] mx-auto">
<div className="flex items-center justify-center gap-8 lg:gap-12 flex-wrap lg:flex-nowrap">
{customerLogos.map((logo) => (
<div
key={logo.name}
className="flex items-center justify-center mx-auto opacity-60 hover:opacity-80 transition-opacity duration-300 w-48 h-12"
>
<Image
src={logo.src}
alt={`${logo.name} logo`}
width={128}
height={40}
className="w-full h-full object-contain filter grayscale hover:grayscale-0 transition-all duration-300"
/>
</div>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,68 @@
"use client";
import React from "react";
import Link from "next/link";
import { Logo } from "./Logo";
import { Button } from "./ui/button";
import { cn } from "@/lib/utils";
const navItems = [
{ href: "/docs", label: "docs" },
{ href: "/pricing", label: "pricing" },
{ href: "/blog", label: "blog" },
];
export function Navbar() {
return (
<nav className="fixed top-0 left-0 right-0 z-50 bg-gradient-to-b from-transparent to-white/5 backdrop-blur border-b border-neutral-200/5">
<div className="max-w-[85rem] mx-auto px-6 lg:px-8">
<div className="flex items-center justify-between h-20">
{/* Logo */}
<Link href="/" className="flex items-center">
<Logo />
</Link>
{/* Navigation Links and CTA - Far Right */}
<div className="hidden md:flex items-center justify-end gap-8">
{navItems.map((item) => (
<Link
key={item.href}
href={item.href}
className={cn(
"text-lg font-medium text-[var(--muted)]",
"hover:text-[var(--primary)] transition-colors",
"font-mono tracking-tighter"
)}
>
{item.label}
</Link>
))}
</div>
{/* Mobile Menu Button */}
<div className="md:hidden">
<button
className="p-2 rounded-md text-[var(--muted)] hover:text-[var(--primary)]"
aria-label="Toggle menu"
>
<svg
className="h-6 w-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
</button>
</div>
</div>
</div>
</nav>
);
}

View file

@ -0,0 +1,199 @@
"use client";
import React, { useEffect, useState } from "react";
import { motion } from "framer-motion";
interface Node {
id: string;
x: number;
y: number;
size: number;
delay: number;
}
interface Connection {
from: string;
to: string;
}
interface Particle {
id: string;
connectionIndex: number;
progress: number;
}
// 4 separate groups positioned to the right of the heading text, aligned with the text vertically
const nodes: Node[] = [
// Group 1
{ id: "1", x: 20, y: 35, size: 18, delay: 0 },
{ id: "2", x: 70, y: 42, size: 12, delay: 0.8 },
{ id: "3", x: 76, y: 38, size: 16, delay: 1.6 },
// // Group 2
// { id: "4", x: 80, y: 30, size: 18, delay: 0.4 },
// { id: "5", x: 87, y: 36, size: 12, delay: 1.2 },
// { id: "6", x: 92, y: 33, size: 16, delay: 2.0 },
// Group 3
{ id: "7", x: 62, y: 48, size: 10, delay: 0.6 },
{ id: "8", x: 65, y: 52, size: 18, delay: 1.4 },
{ id: "9", x: 75, y: 48, size: 14, delay: 0.6 },
];
const connections: Connection[] = [
// Group 1 connections
{ from: "1", to: "2" },
{ from: "2", to: "3" },
// // Group 2 connections
// { from: "4", to: "5" },
// { from: "5", to: "6" },
// Group 3 connections
{ from: "7", to: "8" },
{ from: "8", to: "9" },
];
export function NetworkAnimation() {
const [particles, setParticles] = useState<Particle[]>([]);
// Create and animate particles along connections - much slower
useEffect(() => {
const createParticle = () => {
const connectionIndex = Math.floor(Math.random() * connections.length);
const particle: Particle = {
id: `particle-${Date.now()}-${Math.random()}`,
connectionIndex,
progress: 0,
};
setParticles((prev) => [...prev, particle]);
// Remove particle after animation completes
setTimeout(() => {
setParticles((prev) => prev.filter((p) => p.id !== particle.id));
}, 5000); // Slower duration
};
// Create particles at slower intervals
const interval = setInterval(createParticle, 2500);
return () => clearInterval(interval);
}, []);
// Animate particles - slower movement
useEffect(() => {
const interval = setInterval(() => {
setParticles((prev) =>
prev
.map((p) => ({ ...p, progress: p.progress + 0.008 })) // Much slower
.filter((p) => p.progress <= 1)
);
}, 50);
return () => clearInterval(interval);
}, []);
return (
<div className="absolute inset-0 pointer-events-none z-0 opacity-8 rotate-20 translate-x-40 -translate-y-40 scale-75 lg:scale-100 xl:scale-125 2xl:scale-150 [@media(min-width:1800px)]:scale-[1.60]">
<svg
className="absolute inset-0 w-full h-full rotate-10"
preserveAspectRatio="xMidYMid slice"
viewBox="790 -90 1020 840"
style={{ overflow: "visible" }}
>
<defs>
{/* Simple glow filter */}
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="coloredBlur" />
<feMerge>
<feMergeNode in="coloredBlur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
{/* Render connections - simple lines with lower opacity */}
{connections.map((conn, index) => {
const fromNode = nodes.find((n) => n.id === conn.from);
const toNode = nodes.find((n) => n.id === conn.to);
if (!fromNode || !toNode) return null;
const x1 = (fromNode.x / 100) * 1920;
const y1 = (fromNode.y / 100) * 800;
const x2 = (toNode.x / 100) * 1920;
const y2 = (toNode.y / 100) * 800;
return (
<line
key={`line-${conn.from}-${conn.to}`}
x1={x1}
y1={y1}
x2={x2}
y2={y2}
stroke="#8b91e8"
strokeWidth="1.5"
opacity={1}
/>
);
})}
{/* Render data particles with lower opacity */}
{particles.map((particle) => {
const conn = connections[particle.connectionIndex];
const fromNode = nodes.find((n) => n.id === conn.from);
const toNode = nodes.find((n) => n.id === conn.to);
if (!fromNode || !toNode) return null;
const x = ((fromNode.x + (toNode.x - fromNode.x) * particle.progress) / 100) * 1920;
const y = ((fromNode.y + (toNode.y - fromNode.y) * particle.progress) / 100) * 800;
return (
<motion.circle
key={particle.id}
cx={x}
cy={y}
r={4}
fill="#b9bfff"
initial={{ opacity: 0 }}
animate={{
opacity: [0, 0.4, 0.4, 0]
}}
transition={{
duration: 5,
times: [0, 0.2, 0.8, 1],
ease: "linear"
}}
/>
);
})}
{/* Render nodes with subtle floating animation */}
{nodes.map((node) => {
const size = node.size;
const baseX = (node.x / 100) * 1920;
const baseY = (node.y / 100) * 800;
return (
<motion.circle
key={node.id}
cx={baseX}
cy={baseY}
r={size}
fill="#8b91e8"
animate={{
cx: [baseX - 2, baseX + 2, baseX - 2],
cy: [baseY - 2, baseY + 2, baseY - 2],
}}
transition={{
duration: 10 + node.delay,
delay: node.delay,
repeat: Infinity,
ease: "easeInOut",
}}
/>
);
})}
</svg>
</div>
);
}

View file

@ -0,0 +1,36 @@
import React from "react";
import { Button } from "./ui/button";
interface UnlockPotentialSectionProps {
variant?: "transparent" | "black";
className?: string;
}
export function UnlockPotentialSection({
variant = "transparent",
className = ""
}: UnlockPotentialSectionProps) {
const backgroundClass = variant === "black" ? "bg-[#1a1a1a]" : "";
const textColor = variant === "black" ? "text-white" : "text-black";
return (
<section className={`relative py-24 px-6 lg:px-[102px] ${backgroundClass} ${className}`}>
<div className="max-w-[81rem] mx-auto">
<div className="max-w-4xl">
<h2 className={`font-sans font-normal text-3xl lg:text-4xl tracking-[-2.88px]! ${textColor} leading-[1.03] mb-8`}>
Unlock the full potential of your applications with Plano.
</h2>
<div className="flex flex-col sm:flex-row gap-5">
<Button>
Deploy today
</Button>
<Button variant="secondaryDark">
Documentation
</Button>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,89 @@
import React from "react";
import { ArrowRightIcon } from "lucide-react";
import { Button } from "./ui/button";
const useCasesData = [
{
id: 1,
category: "FROM SAAS TO AGENTS",
title: "Transform software into active agents",
link: "Learn more"
},
{
id: 2,
category: "AGENTIC TASKS",
title: "Train models through live feedback",
link: "Learn more"
},
{
id: 3,
category: "AGENTIC ROUTING",
title: "Route logic across smart agents",
link: "Learn more"
},
{
id: 4,
category: "SAAS INTEGRATIONS",
title: "Design networks of intelligent agents",
link: "Learn more"
}
];
export function UseCasesSection() {
return (
<section className="relative py-24 px-6 lg:px-[102px]">
<div className="max-w-[81rem] mx-auto">
{/* Section Header */}
<div className="mb-14">
{/* USE CASES Badge */}
<div className="mb-6">
<div className="inline-flex items-center gap-2 px-4 py-1 rounded-full bg-[rgba(185,191,255,0.4)] border border-[var(--secondary)] shadow backdrop-blur">
<span className="font-mono font-bold text-[#2a3178] text-sm tracking-[1.62px]!">USE CASES</span>
</div>
</div>
{/* Main Heading and CTA Button */}
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-6">
<h2 className="font-sans font-normal text-3xl lg:text-4xl tracking-[-2.88px]! text-black leading-[1.03]">
What's possible with Plano
</h2>
<Button>
Start building
</Button>
</div>
</div>
{/* 4 Box Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{useCasesData.map((useCase) => (
<div
key={useCase.id}
className="bg-gradient-to-b from-[rgba(177,184,255,0.16)] to-[rgba(17,28,132,0.035)] border-2 border-[rgba(171,178,250,0.27)] rounded-md p-8 h-87 flex flex-col justify-between"
>
{/* Category */}
<div className="mb-6">
<p className="font-mono font-bold text-[#2a3178] text-lg tracking-[1.92px]! w-38 mb-6">
{useCase.category}
</p>
{/* Title */}
<h3 className="font-sans font-medium text-black text-2xl lg:text-3xl tracking-[-1.2px]! leading-[1.102]">
{useCase.title}
</h3>
</div>
{/* Learn More Link */}
<div className="mt-auto">
<button className="group flex items-center gap-2 font-mono font-bold text-[var(--primary)] text-lg tracking-[1.92px]! leading-[1.45] hover:text-[var(--primary-dark)] transition-colors">
LEARN MORE
<ArrowRightIcon className="w-4 h-4" />
</button>
</div>
</div>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,125 @@
"use client";
import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Button } from "./ui/button";
const verticalCarouselData = [
{
id: 1,
category: "INTRODUCTION",
title: "Simple to revolutionary",
description: "By handling the critical yet complex tasks of prompt processing, Plano enables you to focus on what truly matters — achieving your business goals. With built-in capabilities like intelligent task routing, jailbreaking prevention, and centralized observability, Plano ensures your system runs smoothly and securely from the start, while continuously scaling with your needs."
},
{
id: 2,
category: "OPEN SOURCE",
title: "Built on proven foundations",
description: "Plano is built on open-source technologies and proven infrastructure patterns. This ensures transparency, community-driven development, and the ability to customize and extend the platform to meet your specific needs while maintaining enterprise-grade reliability."
},
{
id: 3,
category: "BUILT ON ENVOY",
title: "Enterprise-grade infrastructure",
description: "Leveraging the battle-tested Envoy Proxy, Plano inherits years of production-hardened networking capabilities. This foundation provides unmatched performance, reliability, and scalability for your AI agent infrastructure."
},
{
id: 4,
category: "PURPOSE-BUILT",
title: "Designed for AI agents",
description: "Unlike generic API gateways, Plano is purpose-built for AI agent workloads. Every feature is designed with prompt processing, model routing, and agent orchestration in mind, providing optimal performance for your AI applications."
},
{
id: 5,
category: "PROMPT ROUTING",
title: "Intelligent request handling",
description: "Advanced prompt routing capabilities ensure that each request is directed to the most appropriate model or agent. This intelligent routing optimizes for cost, performance, and accuracy based on your specific requirements and preferences."
}
];
export function VerticalCarouselSection() {
const [activeSlide, setActiveSlide] = useState(0);
const handleSlideClick = (index: number) => {
setActiveSlide(index);
};
return (
<section className="relative bg-[#1a1a1a] text-white py-24 px-6 lg:px-[102px]">
<div className="max-w-[81rem] mx-auto">
{/* Main Heading */}
<h2 className="font-sans font-normal text-3xl lg:text-4xl tracking-[-2.88px]! text-white leading-[1.03] mb-16 max-w-4xl">
Basic scenarios to powerful agentic apps in minutes
</h2>
{/* Vertical Carousel Layout */}
<div className="flex flex-col lg:flex-row">
{/* Left Sidebar Navigation */}
<div className="lg:w-72 flex-shrink-0">
<div className="relative space-y-6">
{/* Sliding Rectangle Indicator */}
<motion.div
className="absolute left-0 top-0 w-2 h-4 bg-[#6363d2] z-10 rounded-xs"
animate={{
y: activeSlide * 52 + 6 // Each item is ~28px text + 24px gap = 52px, +10px to center smaller rectangle
}}
transition={{
type: "spring",
stiffness: 300,
damping: 30,
duration: 0.6
}}
/>
{verticalCarouselData.map((item, index) => (
<div
key={item.id}
onClick={() => handleSlideClick(index)}
className="cursor-pointer relative pl-6 transition-all duration-300"
>
{/* Category Text */}
<span className={`font-mono font-bold text-lg tracking-[1.69px]! transition-colors duration-300 ${
index === activeSlide
? 'text-[#acb3fe]'
: 'text-[rgba(172,179,254,0.71)]'
}`}>
{item.category}
</span>
</div>
))}
</div>
</div>
{/* Right Content Area */}
<div className="flex-1 min-h-[400px] relative lg:-ml-8">
<AnimatePresence mode="wait">
<motion.div
key={activeSlide}
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -50 }}
transition={{ duration: 0.5, ease: "easeInOut" }}
className="absolute inset-0"
>
<div className="max-w-2xl">
{/* Title */}
<h3 className="font-sans font-medium text-[var(--primary)] text-2xl lg:text-[34px] tracking-[-2.4px]! leading-[1.03] mb-4">
{verticalCarouselData[activeSlide].title}
</h3>
{/* Description */}
<div className="font-mono text-white text-xl lg:text-lg leading-10 tracking-[-1.2px]! max-w-md">
<p className="mb-0">
{verticalCarouselData[activeSlide].description}
</p>
</div>
</div>
</motion.div>
</AnimatePresence>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,65 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[7px] font-mono font-medium tracking-[-0.989px] transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-[#7780d9] border-[#4141b2] border-[1.562px] border-solid text-white hover:bg-[#7780d9]/90 text-base leading-[1.102]",
primary:
"bg-[#7780d9] border-[#4141b2] border-[1.562px] border-solid text-white hover:bg-[#7780d9]/90 text-base leading-[1.102]",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-[#edefff] border-[#d1d1d1] border-[1.562px] border-solid text-[#494949] hover:bg-[#edefff]/90 text-base leading-[1.102]",
secondaryDark:
"bg-neutral-600 border-[#d1d1d1]/20 border-[1.562px] border-solid text-white hover:bg-[#1a1a1a]/90 text-base leading-[1.102]",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-5 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-[7px] px-5 has-[>svg]:px-4",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot : "button"
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}
export { Button, buttonVariants }