feat: updated Research page

This commit is contained in:
Musa 2025-12-11 18:59:32 -08:00
parent 73fce0cf18
commit 573744961d
26 changed files with 1186 additions and 254 deletions

View file

@ -5,12 +5,15 @@ import { NetworkAnimation } from "./NetworkAnimation";
export function Hero() {
return (
<section className="relative pt-8 sm:pt-12 lg:pt-1 pb-6 px-4 sm: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-6 sm:mb-4">
<section className="relative pt-8 sm:pt-12 lg:pt-1 pb-6 px-4 sm:px-6 lg:px-8 overflow-hidden">
<div className="max-w-[81rem] mx-auto relative">
<div className="hidden lg:block absolute inset-0 pointer-events-none ">
<NetworkAnimation />
</div>
<div className="lg:hidden absolute inset-0 pointer-events-none">
<NetworkAnimation className="!w-[300px] !h-[300px] left-82! top-1! opacity-90! " />
</div>
<div className="max-w-3xl mb-6 sm:mb-4 relative z-10">
{/* Version Badge */}
<div className="mb-4 sm:mb-6">
<div className="inline-flex flex-wrap items-center gap-1.5 sm:gap-2 px-3 sm:px-4 py-1 rounded-full bg-[rgba(185,191,255,0.4)] border border-[var(--secondary)] shadow backdrop-blur">
@ -30,7 +33,7 @@ export function Hero() {
</div>
{/* Main Heading */}
<h1 className="text-4xl sm:text-4xl md:text-5xl lg:text-7xl font-normal leading-tight tracking-tighter text-black mb-4 sm:mb-6 flex flex-col gap-0 sm:-space-y-2 lg:-space-y-3">
<h1 className="text-4xl sm:text-4xl md:text-5xl lg:text-7xl font-normal leading-tight tracking-tighter text-black flex flex-col gap-0 sm:-space-y-2 lg:-space-y-3">
<span className="font-sans">Delivery infrastructure </span>
<span className="font-sans font-medium text-[var(--secondary)]">
for agentic apps
@ -39,13 +42,14 @@ export function Hero() {
</div>
{/* Subheading with CTA Buttons */}
<div className="max-w-7xl flex flex-col lg:flex-row lg:items-end lg:justify-between gap-6">
<p className="text-base sm:text-lg md:text-xl lg:text-2xl font-sans font-[400] tracking-[-1.2px] sm:tracking-[-1.82px]! text-black max-w-4xl">
Build agents faster, and deliver them reliably to prod by offloading plumbing work in AI
<div className="max-w-7xl relative z-10">
<p className="text-base sm:text-lg md:text-xl lg:text-2xl font-sans font-[400] tracking-[-1.2px] sm:tracking-[-1.82px]! text-black max-w-70 sm:max-w-2xl mb-6">
Build agents faster, and deliver them reliably to production -
by offloading the plumbing work in building agentic applications.
</p>
{/* CTA Buttons */}
<div className="mb-0.5 flex flex-col sm:flex-row items-stretch sm:items-center gap-3 sm:gap-4 w-full sm:w-auto lg:justify-end">
<div className="flex flex-col sm:flex-row items-stretch sm:items-start gap-3 sm:gap-4">
<Button asChild className="w-full sm:w-auto">
<Link href="/get-started">Get started</Link>
</Button>

View file

@ -3,6 +3,7 @@
import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Button } from "@katanemo/ui";
import Link from "next/link";
const carouselData = [
{
@ -12,6 +13,7 @@ const carouselData = [
description:
"Building AI agents is hard enough (iterate on prompts and evaluate LLMs, etc), the plumbing work shouldn't add to that complexity. Plano takes care of the critical plumbing work like routing and orchestration to agents that slows you down and locks you into rigid frameworks, freeing developers to innovate on what truly matters.",
image: "/LaunchFaster.svg",
link: "https://docs.plano.katanemo.com/getting-started/installation",
},
{
id: 2,
@ -20,6 +22,7 @@ const carouselData = [
description:
"Build with multiple LLMs or model versions with a single unified API. Plano centralizes access controls, offers resiliency for traffic to 100+ LLMs -- all without you having to write a single line of code.",
image: "/BuildWithChoice.svg",
link: "https://docs.plano.katanemo.com/getting-started/installation",
},
{
id: 3,
@ -28,6 +31,7 @@ const carouselData = [
description:
"Knowing when agents fail or delight users is a critical signal that feeds into a reinforcement learning and optimization cycle. Plano makes this trivial by sampling hyper-rich information traces from live production agentic interactions so that you can improve agent performance faster.",
image: "/Telemetry.svg",
link: "https://docs.plano.katanemo.com/getting-started/installation",
},
{
id: 4,
@ -36,6 +40,7 @@ const carouselData = [
description:
"Plano comes built-in with a state-of-the-art guardrail model you can use for things like jailbreak detection. But you can easily extend those capabilities via plano's agent filter chain to apply custom policy checks in a centralized way and keep users engaged on topics relevant to your requirements.",
image: "/ShipConfidently.svg",
link: "https://docs.plano.katanemo.com/getting-started/installation",
},
{
id: 5,
@ -44,6 +49,7 @@ const carouselData = [
description:
"Plano's sidecar deployment model avoids library-based abstractions - operating as a protocol-native data plane that integrates seamlessly with your existing agents via agentic APIs (like v1/responses). This decouples your core agent logic from plumbing concerns - run it alongside any framework without code changes, vendor lock-in, or performance overhead.",
image: "/Contextual.svg",
link: "https://docs.plano.katanemo.com/getting-started/installation",
},
];
@ -143,8 +149,8 @@ export function IdeaToAgentSection() {
</p>
</div>
<Button className="mt-6 sm:mt-8 w-full sm:w-auto">
Learn more
<Button asChild className="mt-6 sm:mt-8 w-full sm:w-auto">
<Link href={carouselData[currentSlide].link}>Learn more</Link>
</Button>
</div>
</div>

View file

@ -5,9 +5,9 @@ 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-center">
<div className="flex flex-col lg:flex-row gap-12">
{/* Left Content */}
<div className="flex-1">
<div className="flex-1 mt-2">
{/* Heading */}
<p className="font-mono font-bold text-primary-light text-xl tracking-[1.92px]! mb-4 leading-[1.102]">
WHY PLANO?

View file

@ -26,7 +26,7 @@ const customerLogos = [
export function LogoCloud() {
return (
<section className="relative py-6 sm:py-8 px-4 sm:px-6 lg:px-8">
<section className="relative py-6 sm:py-8 px-4 sm:px-6 lg:px-8 bg-transparent">
<div className="max-w-[81rem] mx-auto">
<div className="grid grid-cols-2 md:grid-cols-3 lg:flex lg:flex-row lg:justify-center lg:items-center gap-4 sm:gap-6 md:gap-8 lg:gap-0 place-items-center">
{customerLogos.map((logo, index) => {

View file

@ -1,203 +1,200 @@
"use client";
import React, { useEffect, useState } from "react";
import React, { useId } 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 },
// Define the grid of squares with their positions and colors
const squares = [
// Column 1 (x=3)
{ x: 3, y: 3, color: "#B0B7FF", col: 0, row: 0 },
{ x: 3, y: 6, color: "#B0B7FF", col: 0, row: 1 },
{ x: 3, y: 9, color: "#B0B7FF", col: 0, row: 2 },
{ x: 3, y: 12, color: "#ABB2FA", col: 0, row: 3 },
{ x: 3, y: 15, color: "#ABB2FA", col: 0, row: 4 },
{ x: 3, y: 18, color: "#ABB2FA", col: 0, row: 5 },
{ x: 3, y: 21, color: "#969FF4", col: 0, row: 6 },
// Column 2 (x=6)
{ x: 6, y: 3, color: "#B0B7FF", col: 1, row: 0 },
{ x: 6, y: 6, color: "#B0B7FF", col: 1, row: 1 },
{ x: 6, y: 9, color: "#ABB2FA", col: 1, row: 2 },
{ x: 6, y: 12, color: "#ABB2FA", col: 1, row: 3 },
{ x: 6, y: 15, color: "#ABB2FA", col: 1, row: 4 },
{ x: 6, y: 18, color: "#969FF4", col: 1, row: 5 },
{ x: 6, y: 21, color: "#969FF4", col: 1, row: 6 },
// Column 3 (x=9)
{ x: 9, y: 3, color: "#B0B7FF", col: 2, row: 0 },
{ x: 9, y: 6, color: "#ABB2FA", col: 2, row: 1 },
{ x: 9, y: 9, color: "#ABB2FA", col: 2, row: 2 },
{ x: 9, y: 12, color: "#ABB2FA", col: 2, row: 3 },
{ x: 9, y: 15, color: "#969FF4", col: 2, row: 4 },
{ x: 9, y: 18, color: "#969FF4", col: 2, row: 5 },
{ x: 9, y: 21, color: "#969FF4", col: 2, row: 6 },
// Column 4 (x=12)
{ x: 12, y: 3, color: "#ABB2FA", col: 3, row: 0 },
{ x: 12, y: 6, color: "#ABB2FA", col: 3, row: 1 },
{ x: 12, y: 9, color: "#ABB2FA", col: 3, row: 2 },
{ x: 12, y: 12, color: "#969FF4", col: 3, row: 3 },
{ x: 12, y: 15, color: "#969FF4", col: 3, row: 4 },
{ x: 12, y: 18, color: "#969FF4", col: 3, row: 5 },
{ x: 12, y: 21, color: "#969FF4", col: 3, row: 6 },
// Column 5 (x=15)
{ x: 15, y: 3, color: "#ABB2FA", col: 4, row: 0 },
{ x: 15, y: 6, color: "#ABB2FA", col: 4, row: 1 },
{ x: 15, y: 9, color: "#969FF4", col: 4, row: 2 },
{ x: 15, y: 12, color: "#969FF4", col: 4, row: 3 },
{ x: 15, y: 15, color: "#969FF4", col: 4, row: 4 },
{ x: 15, y: 18, color: "#969FF4", col: 4, row: 5 },
{ x: 15, y: 21, color: "#969FF4", col: 4, row: 6 },
// Column 6 (x=18)
{ x: 18, y: 3, color: "#ABB2FA", col: 5, row: 0 },
{ x: 18, y: 6, color: "#969FF4", col: 5, row: 1 },
{ x: 18, y: 9, color: "#969FF4", col: 5, row: 2 },
{ x: 18, y: 12, color: "#969FF4", col: 5, row: 3 },
{ x: 18, y: 15, color: "#969FF4", col: 5, row: 4 },
{ x: 18, y: 18, color: "#969FF4", col: 5, row: 5 },
{ x: 18, y: 21, color: "#969FF4", col: 5, row: 6 },
// Column 7 (x=21)
{ x: 21, y: 3, color: "#969FF4", col: 6, row: 0 },
{ x: 21, y: 6, color: "#969FF4", col: 6, row: 1 },
{ x: 21, y: 9, color: "#969FF4", col: 6, row: 2 },
{ x: 21, y: 12, color: "#969FF4", col: 6, row: 3 },
{ x: 21, y: 15, color: "#969FF4", col: 6, row: 4 },
{ x: 21, y: 18, color: "#969FF4", col: 6, row: 5 },
{ x: 21, y: 21, color: "#969FF4", col: 6, row: 6 },
];
const connections: Connection[] = [
// Group 1 connections
{ from: "1", to: "2" },
{ from: "2", to: "3" },
interface NetworkAnimationProps {
className?: string;
}
// // Group 2 connections
// { from: "4", to: "5" },
// { from: "5", to: "6" },
// Deterministic seeded random number generator for consistent SSR/client values
function seededRandom(seed: number): number {
const x = Math.sin(seed) * 10000;
return x - Math.floor(x);
}
// Group 3 connections
{ from: "7", to: "8" },
{ from: "8", to: "9" },
];
// Round to fixed precision to avoid floating-point precision differences
function roundToPrecision(value: number, precision: number = 10): number {
return Math.round(value * Math.pow(10, precision)) / Math.pow(10, precision);
}
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);
}, []);
// Generate deterministic random values based on index
function getDeterministicValues(index: number) {
const seed1 = index * 0.1;
const seed2 = index * 0.2;
const seed3 = index * 0.3;
const seed4 = index * 0.4;
const seed5 = index * 0.5;
const seed6 = index * 0.6;
return {
duration: roundToPrecision(3 + seededRandom(seed1) * 3, 10), // 3-6 seconds
peakOpacity: roundToPrecision(0.7 + seededRandom(seed2) * 0.3, 10),
baseOpacity: roundToPrecision(0.3 + seededRandom(seed3) * 0.2, 10),
midOpacity: roundToPrecision(0.5 + seededRandom(seed4) * 0.2, 10),
baseBrightness: roundToPrecision(0.85 + seededRandom(seed5) * 0.15, 10),
peakBrightness: roundToPrecision(1.0 + seededRandom(seed6) * 0.2, 10),
};
}
export function NetworkAnimation({ className }: NetworkAnimationProps) {
// Generate unique IDs for gradient and mask to avoid conflicts when multiple instances exist
const gradientId = useId().replace(/:/g, '-');
const maskId = useId().replace(/:/g, '-');
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" }}
<div className="absolute inset-0 pointer-events-none opacity-100">
<motion.div
className={`absolute
top-[9%] right-[-3%] w-[380px] h-[380px] ${className || ""}`}
initial={{
rotate: 9, // Start at the same rotation as animation to prevent flicker
}}
animate={{
rotate: [9, 10, 9], // Slight breathing rotation
}}
transition={{
duration: 8,
repeat: Infinity,
ease: "easeInOut",
}}
>
<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>
<svg
width="100%"
height="100%"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
{/* Gradient mask: transparent at bottom, opaque at top */}
<linearGradient id={gradientId} x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stopColor="white" stopOpacity="1" />
<stop offset="50%" stopColor="white" stopOpacity="0.5" />
<stop offset="100%" stopColor="white" stopOpacity="0" />
</linearGradient>
<mask id={maskId}>
<rect width="26" height="26" fill={`url(#${gradientId})`} />
</mask>
</defs>
<g mask={`url(#${maskId})`}>
{/* Outer border */}
<rect width="26" height="26" fill="#7780D9" />
{/* Inner background */}
<rect x="2" y="2" width="22" height="22" fill="#B9BFFF" />
{/* Animated squares with wave effect */}
{squares.map((square, index) => {
// Use deterministic values based on index for SSR/client consistency
const { duration, peakOpacity, baseOpacity, midOpacity, baseBrightness, peakBrightness } =
getDeterministicValues(index);
return (
<motion.path
key={`square-${index}`}
d={`M${square.x} ${square.y}H${square.x + 2}V${square.y + 2}H${square.x}V${square.y}Z`}
fill={square.color}
initial={{
opacity: roundToPrecision(baseOpacity, 10),
filter: `brightness(${roundToPrecision(baseBrightness, 10)})`,
}}
animate={{
opacity: [
roundToPrecision(baseOpacity, 10),
roundToPrecision(midOpacity, 10),
roundToPrecision(peakOpacity, 10),
roundToPrecision(midOpacity, 10),
roundToPrecision(baseOpacity, 10),
],
filter: [
`brightness(${roundToPrecision(baseBrightness, 10)})`,
`brightness(${roundToPrecision((baseBrightness + peakBrightness) / 2, 10)})`,
`brightness(${roundToPrecision(peakBrightness, 10)})`,
`brightness(${roundToPrecision((baseBrightness + peakBrightness) / 2, 10)})`,
`brightness(${roundToPrecision(baseBrightness, 10)})`,
],
}}
transition={{
duration: roundToPrecision(duration, 10),
delay: 0, // No delay - instant start
repeat: Infinity,
ease: "easeInOut",
repeatDelay: 0, // No pause between cycles
}}
/>
);
})}
</g>
</svg>
</motion.div>
</div>
);
}

View file

@ -1,5 +1,6 @@
import React from "react";
import { Button } from "@katanemo/ui";
import Link from "next/link";
interface UnlockPotentialSectionProps {
variant?: "transparent" | "black";
@ -31,8 +32,12 @@ export function UnlockPotentialSection({
</h2>
<div className="flex flex-col sm:flex-row gap-5">
<Button>Deploy today</Button>
<Button variant="secondaryDark">Documentation</Button>
<Button asChild>
<Link href="https://docs.plano.katanemo.com">Deploy today</Link>
</Button>
<Button variant="secondaryDark" asChild>
<Link href="https://docs.plano.katanemo.com">Documentation</Link>
</Button>
</div>
</div>
</div>

View file

@ -20,6 +20,7 @@ import {
DialogClose,
} from "@katanemo/ui";
import { motion, AnimatePresence } from "framer-motion";
import Link from "next/link";
interface UseCase {
id: number;
@ -113,7 +114,9 @@ export function UseCasesSection() {
<h2 className="font-sans font-normal text-2xl sm:text-3xl lg:text-4xl tracking-[-2px] sm:tracking-[-2.88px]! text-black leading-[1.03]">
What's possible with Plano
</h2>
<Button className="hidden lg:block">Start building</Button>
<Button asChild className="hidden lg:block">
<Link href="https://docs.plano.katanemo.com/getting-started/installation">Start building</Link>
</Button>
</div>
</div>
@ -152,7 +155,9 @@ export function UseCasesSection() {
{/* Start building button - Mobile only, appears last */}
<div className="lg:hidden mt-8">
<Button className="w-full">Start building</Button>
<Button asChild className="w-full">
<Link href="https://docs.plano.katanemo.com/getting-started/installation">Start building</Link>
</Button>
</div>
</div>
@ -278,7 +283,8 @@ export function UseCasesSection() {
<span>Ready to get started?</span>
</div>
<div className="flex flex-col sm:flex-row gap-3 w-full sm:w-auto order-1">
<Button className="w-full sm:w-auto">
<Button asChild className="w-full sm:w-auto">
<Link href="https://docs.plano.katanemo.com/getting-started/installation">Start building</Link>
Start building
<ArrowRightIcon className="w-4 h-4" />
</Button>

View file

@ -0,0 +1,65 @@
import React from "react";
import Image from "next/image";
export function ResearchBenchmarks() {
return (
<section className="relative py-12 sm:py-16 lg:py-20 px-4 sm:px-6 lg:px-[102px] bg-[#1a1a1a] border-b-2 border-white/10">
<div className="max-w-[81rem] mx-auto">
{/* Section Header */}
<div className="mb-8 sm:mb-12 lg:mb-6">
{/* BENCHMARKS Label */}
<div className="mb-4 sm:mb-2">
<div className="font-mono font-bold text-[#9797ea] text-sm sm:text-base lg:text-xl tracking-[1.44px] sm:tracking-[1.92px]! leading-[1.502]">
BENCHMARKS
</div>
</div>
{/* Title */}
<h2 className="text-4xl sm:text-4xl md:text-5xl lg:text-4xl font-medium leading-tight tracking-[-0.06em]! text-white">
<span className="font-sans">Production excellence, outperforming frontier LLMs</span>
</h2>
</div>
{/* Benchmarks Image */}
{/* Mobile: Full-width scrollable container that extends to viewport edges */}
<div
className="mt-5 lg:hidden relative left-1/2 right-1/2 -ml-[50vw] -mr-[50vw] w-screen overflow-x-auto overflow-y-visible"
style={{
scrollbarWidth: "none",
msOverflowStyle: "none",
WebkitOverflowScrolling: "touch",
}}
>
<style jsx>{`
.benchmarks-scroll-container::-webkit-scrollbar {
display: none;
}
`}</style>
<div className="benchmarks-scroll-container inline-block pl-4 sm:pl-6">
<Image
src="/Benchmarks.svg"
alt="Benchmarks"
width={1200}
height={600}
className="h-auto"
style={{ width: "1200px", maxWidth: "none", display: "block" }}
priority
/>
</div>
</div>
{/* Desktop: Normal display */}
<div className="hidden lg:block w-full">
<Image
src="/Benchmarks.svg"
alt="Benchmarks"
width={1200}
height={600}
className="w-full h-auto"
priority
/>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,39 @@
import React from "react";
import { Button } from "@katanemo/ui";
import Link from "next/link";
export function ResearchCTA() {
return (
<section className="relative pt-16 sm:pt-20 lg:pt-24 pb-12 sm:pb-16 lg:pb-20 px-4 sm:px-6 lg:px-[102px] bg-[#1a1a1a]">
<div className="max-w-[81rem] mx-auto relative z-10">
<div className="max-w-4xl">
{/* Main Heading */}
<h1 className="text-4xl sm:text-4xl md:text-5xl lg:text-5xl font-medium leading-tight tracking-[-0.06em]! text-white -ml-1 mb-3 mt-4">
<span className="font-sans">Meet Plano-Orchestrator. Our latest models.</span>
</h1>
</div>
{/* Description with CTA Buttons */}
<div className="max-w-2xl">
{/* text-base sm:text-lg md:text-xl lg:text-2xl font-sans font-[400] tracking-[-1.2px] sm:tracking-[-1.82px]! text-black max-w-70 sm:max-w-2xl mb-6 */}
<p className="leading-relaxed sm:text-lg md:text-xl lg:text-2xl font-sans font-normal tracking-[-1.0px] sm:tracking-[-1.22px]! text-white/90 mb-6">
Plano-Orchestrator is a family of state-of-the-art routing and orchestration models
that decides which agent(s) or LLM(s) should handle each request, and in what sequence.
Built for multi-agent orchestration systems, Plano-Orchestrator excels at analyzing
user intent and conversation context to make precise routing and orchestration decisions.
</p>
{/* CTA Buttons */}
<div className="flex flex-col sm:flex-row items-stretch sm:items-start gap-3 sm:gap-4">
<Button asChild className="w-full sm:w-auto">
<Link href="https://huggingface.co/katanemo">Download Plano models</Link>
</Button>
<Button variant="secondary" asChild className="w-full sm:w-auto">
<Link href="https://docs.plano.katanemo.com">Get Started with Plano</Link>
</Button>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,152 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
import { MessagesSquare, GitFork, Route, RefreshCw } from "lucide-react";
interface Capability {
id: number;
title: string;
description: string;
}
const capabilitiesData: Capability[] = [
{
id: 1,
title: "Multi-turn Understanding",
description:
"Makes routing decisions based on full conversation history, maintaining contextual awareness across extended dialogues with evolving user needs.",
},
{
id: 2,
title: "Multi-Intent Detection",
description:
"Identifies when a single user message requires multiple agents simultaneously, enabling parallel/sequential routing to fulfill complex requests",
},
{
id: 3,
title: "Content-Dependency Routing",
description:
"Correctly interprets ambiguous or referential messages by leveraging prior conversation context for accurate routing decisions.",
},
{
id: 4,
title: "Conversational-Flow Handling",
description:
"Understands diverse interaction patterns including follow-ups, clarifications, confirmations, and corrections within ongoing conversations.",
},
];
export function ResearchCapabilities() {
return (
<section className="relative py-12 sm:py-16 lg:py-20 px-4 sm:px-6 lg:px-[102px] bg-[#1a1a1a]">
<div className="max-w-[81rem] mx-auto">
{/* Section Header */}
<div className="mb-8 sm:mb-12 lg:mb-10">
{/* PLANO-4B CAPABILITIES Label */}
<div className="mb-2 sm:mb-1">
<div className="font-mono font-bold text-[#9797ea] text-sm sm:text-base lg:text-xl tracking-[1.44px] sm:tracking-[1.92px]! leading-[1.502]">
PLANO-4B CAPABILITIES
</div>
</div>
{/* Title */}
<h2 className="text-4xl sm:text-4xl md:text-5xl lg:text-4xl font-medium leading-tight tracking-[-0.06em]! text-white mb-4">
<span className="font-sans">Accurately route with confidence with no compromise</span>
</h2>
<p className="font-mono text-white/90 w-[75%] text-sm sm:text-base tracking-[-0.8px] sm:tracking-[-1.2px]! leading-relaxed">
Designed for real-world deployments, it delivers strong performance across general
conversations, coding tasks, and long-context multi-turn conversations, while remaining
efficient enough for low-latency production environments.
</p>
</div>
{/* Mobile: Icon card above title/description, stacked vertically */}
<div className="lg:hidden grid grid-cols-1 gap-8">
{capabilitiesData.map((capability) => {
// Map each capability to its icon
const iconMap: Record<number, React.ComponentType<{ className?: string }>> = {
1: MessagesSquare, // Multi-turn Understanding
2: GitFork, // Multi-Intent Detection
3: Route, // Content-Dependency Routing
4: RefreshCw, // Conversational-Flow Handling
};
const Icon = iconMap[capability.id];
return (
<div key={capability.id} className="flex flex-col">
{/* Icon Card */}
<motion.div
whileHover={{ y: -4 }}
transition={{ duration: 0.2 }}
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-6 h-40 flex items-center justify-center mb-4"
>
{Icon && (
<Icon className="w-24 h-24 text-[#9797ea]" />
)}
</motion.div>
{/* Title */}
<h3 className="font-sans font-medium text-white text-xl tracking-[-1.2px]! leading-[1.102] mb-3">
{capability.title}
</h3>
{/* Description */}
<p className="font-mono text-white/90 text-base tracking-[-0.8px]! leading-relaxed">
{capability.description}
</p>
</div>
);
})}
</div>
{/* Desktop: Icon cards separate from titles/descriptions */}
<div className="hidden lg:grid lg:grid-cols-4 gap-6 mb-6">
{capabilitiesData.map((capability) => {
// Map each capability to its icon
const iconMap: Record<number, React.ComponentType<{ className?: string }>> = {
1: MessagesSquare, // Multi-turn Understanding
2: GitFork, // Multi-Intent Detection
3: Route, // Content-Dependency Routing
4: RefreshCw, // Conversational-Flow Handling
};
const Icon = iconMap[capability.id];
return (
<motion.div
key={capability.id}
whileHover={{ y: -4 }}
transition={{ duration: 0.2 }}
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-6 h-52 flex items-center justify-center"
>
{Icon && (
<Icon className="w-24 h-24 text-[#9797ea]" />
)}
</motion.div>
);
})}
</div>
{/* Desktop: Titles and Descriptions Below Boxes */}
<div className="hidden lg:grid lg:grid-cols-4 gap-6">
{capabilitiesData.map((capability) => (
<div key={capability.id}>
{/* Title */}
<h3 className="font-sans font-medium text-white text-2xl tracking-[-1.2px]! leading-[1.102] mb-4">
{capability.title}
</h3>
{/* Description */}
<p className="font-mono text-white/90 text-base tracking-[-1.2px]! leading-relaxed">
{capability.description}
</p>
</div>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,111 @@
"use client";
import React from "react";
import Image from "next/image";
import { Check } from "lucide-react";
import { motion } from "framer-motion";
interface ModelFeature {
text: string;
}
interface Model {
id: number;
name: string;
logo: string;
features: ModelFeature[];
}
const modelsData: Model[] = [
{
id: 1,
name: "Plano-4B",
logo: "/Plano4B-Logo.svg",
features: [
{ text: "Optimized for production routing with sub-100ms latency" },
{ text: "84-87% accuracy on long-context scenarios" },
{ text: "Cost-effective model selection at scale" },
{ text: "Seamless agent orchestration capabilities" },
{ text: "Frontier-level performance at fraction of cost" },
],
},
{
id: 2,
name: "Plano-30B-A3B",
logo: "/Plano30B-Logo.svg",
features: [
{ text: "Advanced routing intelligence for complex workflows" },
{ text: "Enhanced context understanding and preservation" },
{ text: "Superior accuracy for multi-agent coordination" },
{ text: "Enterprise-grade performance and reliability" },
{ text: "Scalable architecture for high-throughput systems" },
],
},
];
export function ResearchFamily() {
return (
<section className="relative py-16 sm:py-20 lg:py-24 px-4 sm:px-6 lg:px-[102px] bg-white">
<div className="max-w-[81rem] mx-auto">
{/* Section Header */}
<div className="mb-8 sm:mb-12 lg:mb-10">
{/* PLANO FAMILY Label */}
<div className="mb-4 sm:mb-2">
<div className="font-mono font-bold text-[#2A3178] text-sm sm:text-base lg:text-xl tracking-[1.44px] sm:tracking-[1.92px]! leading-[1.502]">
PLANO FAMILY
</div>
</div>
{/* Title */}
<h2 className="text-4xl sm:text-4xl md:text-5xl lg:text-4xl font-medium leading-tight tracking-[-0.06em]! text-black -ml-1">
<span className="font-sans">Plano Models</span>
</h2>
</div>
{/* 2 Card Grid - Side by Side */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
{modelsData.map((model) => (
<motion.div
key={model.id}
whileHover={{ y: -4 }}
transition={{ duration: 0.2 }}
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-6 sm:p-6 lg:p-6 h-72"
>
{/* Empty box - content is below */}
</motion.div>
))}
</div>
{/* Titles and Descriptions Below Boxes */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{modelsData.map((model) => (
<div key={model.id}>
{/* Logo */}
<div className="mb-6">
<Image
src={model.logo}
alt={model.name}
width={200}
height={60}
className="h-12 w-auto"
/>
</div>
{/* Features List */}
<div>
{model.features.map((feature, index) => (
<div key={index} className="flex items-start gap-3 mb-4">
<Check className="w-5 h-5 text-[var(--primary)] flex-shrink-0 mt-0.5" />
<p className="font-mono text-black text-sm sm:text-base tracking-[-0.8px] sm:tracking-[-1.2px]! leading-relaxed">
{feature.text}
</p>
</div>
))}
</div>
</div>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,49 @@
import React from "react";
interface ResearchItem {
title: string;
description: string;
}
const researchItems: ResearchItem[] = [
{
title: "Model Routing",
description:
"Great agent UX starts with using the best model for a task — fast and cheap when it can be, smarter when it needs to be — and our routing research gives developers the controls to do exactly that. Our policy-based router captures your evals and preferences, while our performance-based router learns from real traffic over time, so you can evolve model choices without retraining.",
},
{
title: "Governance & Learning",
description:
"Building an agent is easy — knowing what it does in production and how to improve it is very hard. Our research focuses on making agent behavior observable and governable: studying how agents respond to real and adversarial traffic, policy changes, and turning signals into learning loops that make agents safer and more effective over time.",
},
{
title: "Better Performance",
description:
"Better system performance comes from directing traffic to the right agents for each task or workflow. We build compact orchestration models that manage traffic between agents — ensuring clean handoffs, preserved context, and reliable multi-agent collaboration across distributed systems.",
},
];
export function ResearchGrid() {
return (
<section className="relative py-12 sm:py-16 lg:py-20 px-4 sm:px-6 lg:px-[102px] bg-white">
<div className="max-w-[81rem] mx-auto">
{/* 3 Column Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8 lg:gap-12">
{researchItems.map((item, index) => (
<div key={index} className="flex flex-col">
{/* Title */}
<h3 className="font-sans font-medium text-2xl sm:text-3xl lg:text-3xl tracking-[-1.5px] sm:tracking-[-2px]! text-black leading-[1.1] mb-2 sm:mb-4">
{item.title}
</h3>
{/* Description */}
<p className="font-mono text-black text-sm sm:text-base lg:text-lg leading-relaxed tracking-[-0.8px] sm:tracking-[-1.2px]!">
{item.description}
</p>
</div>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,59 @@
import React from "react";
import { NetworkAnimation } from "../NetworkAnimation";
import { Button } from "@katanemo/ui";
import Link from "next/link";
export function ResearchHero() {
return (
<section className="relative pt-8 sm:pt-12 lg:pt-16 pb-12 sm:pb-16 lg:pb-20 px-4 sm:px-6 lg:px-[102px] overflow-hidden">
<div className="max-w-[81rem] mx-auto relative">
<div className="hidden lg:block absolute inset-0 pointer-events-none">
<NetworkAnimation className="!w-[500px] !h-[500px] xl:!w-[600px] xl:!h-[600px] 2xl:!w-[570px] 2xl:!h-[570px]" />
</div>
<div className="lg:hidden absolute inset-0 pointer-events-none">
<NetworkAnimation className="!w-[300px] !h-[300px] left-77! -top-2! opacity-90! " />
</div>
<div className="max-w-3xl relative z-10">
{/* Badge */}
<div className="mb-4 sm:mb-6">
<div className="inline-flex flex-wrap items-center gap-1.5 sm:gap-2 px-3 sm:px-4 py-1 rounded-full bg-[rgba(185,191,255,0.4)] border border-[var(--secondary)] shadow backdrop-blur">
<span className="text-xs sm:text-sm font-medium text-black/65">
New!
</span>
<span className="text-xs sm:text-sm font-medium text-black hidden sm:inline">
</span>
<span className="text-xs sm:text-sm font-[600] tracking-[-0.6px]! text-black leading-tight">
<span className="hidden sm:inline">
Plano-4B - The state-of-the-art agent routing and orchestration LLM
</span>
<span className="sm:hidden">Unified /v1/responses API</span>
</span>
</div>
</div>
{/* Main Heading */}
<h1 className="text-4xl sm:text-4xl md:text-5xl lg:text-7xl font-medium leading-tight tracking-tighter text-black -ml-1 mb-3 mt-4">
<span className="font-sans">Research</span>
</h1>
</div>
{/* Description */}
<div className="max-w-70 sm:max-w-2xl relative z-10">
<p className="text-base sm:text-lg md:text-xl lg:text-2xl font-sans font-normal tracking-[-1.0px] sm:tracking-[-1.22px]! text-black">
Our open source applied research focuses on how to deliver agents
safely, efficiently, and with predictable real-world performance
critical for any AI application, but sits outside any products core
business logic.
</p>
</div>
<div className="flex flex-col sm:flex-row items-stretch sm:items-start gap-3 sm:gap-4 mt-6 sm:mt-8 relative z-10">
<Button asChild className="w-full sm:w-auto">
<Link href="https://huggingface.co/katanemo">Available on Hugging Face</Link>
</Button>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,50 @@
import React from "react";
import Image from "next/image";
export function ResearchTimeline() {
return (
<section className="relative py-12 sm:py-16 lg:py-16 px-4 sm:px-6 lg:px-[102px] bg-white border-b border-gray-200">
<div className="max-w-[81rem] mx-auto">
{/* Timeline Image */}
{/* Mobile: Full-width scrollable container that extends to viewport edges */}
<div
className="mt-5 lg:hidden relative left-1/2 right-1/2 -ml-[50vw] -mr-[50vw] w-screen overflow-x-auto overflow-y-visible"
style={{
scrollbarWidth: "none",
msOverflowStyle: "none",
WebkitOverflowScrolling: "touch",
}}
>
<style jsx>{`
.timeline-scroll-container::-webkit-scrollbar {
display: none;
}
`}</style>
<div className="timeline-scroll-container inline-block pl-4 sm:pl-6">
<Image
src="/Timeline.svg"
alt="Research Timeline"
width={1200}
height={600}
className="h-auto"
style={{ width: "1200px", maxWidth: "none", display: "block" }}
priority
/>
</div>
</div>
{/* Desktop: Normal display */}
<div className="hidden lg:block w-full">
<Image
src="/Timeline.svg"
alt="Research Timeline"
width={1200}
height={600}
className="w-full h-auto"
priority
/>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,7 @@
export { ResearchHero } from "./ResearchHero";
export { ResearchGrid } from "./ResearchGrid";
export { ResearchTimeline } from "./ResearchTimeline";
export { ResearchCTA } from "./ResearchCTA";
export { ResearchCapabilities } from "./ResearchCapabilities";
export { ResearchBenchmarks } from "./ResearchBenchmarks";
export { ResearchFamily } from "./ResearchFamily";