format(www)

This commit is contained in:
Musa 2025-12-12 11:36:54 -08:00
parent a825ce9add
commit 64d45a7a00
27 changed files with 257 additions and 314 deletions

View file

@ -1,101 +0,0 @@
# ArchGW Monorepo
This is a Turborepo monorepo containing the Next.js applications and shared packages.
## Structure
```
.
├── apps/
│ ├── www/ # Marketing website
│ └── docs/ # Documentation site
├── packages/
│ ├── ui/ # Shared UI components (Navbar, Footer, Logo, etc.)
│ ├── shared-styles/ # Shared CSS and Tailwind configuration
│ ├── tailwind-config/ # Tailwind configuration
│ └── tsconfig/ # Shared TypeScript configurations
└── turbo.json # Turborepo configuration
```
## Getting Started
### Install Dependencies
```bash
npm install
```
### Development
Run all apps in development mode:
```bash
npm run dev
```
Or run specific apps:
```bash
# Marketing website (port 3000)
cd apps/www && npm run dev
# Documentation (port 3001)
cd apps/docs && npm run dev
```
### Build
Build all apps:
```bash
npm run build
```
### Lint & Type Check
```bash
npm run lint
npm run typecheck
```
## Shared Packages
### @archgw/ui
Shared React components including:
- `Navbar` - Navigation bar component
- `Footer` - Footer component
- `Logo` - Logo component
- UI components (Button, Dialog, etc.)
### @archgw/shared-styles
Shared CSS styles including:
- Tailwind CSS configuration
- Font definitions (IBM Plex Sans, JetBrains Mono)
- CSS variables for theming
### @archgw/tailwind-config
Shared Tailwind CSS configuration.
### @archgw/tsconfig
Shared TypeScript configurations:
- `base.json` - Base TypeScript config
- `nextjs.json` - Next.js specific config
## Design System
Both apps share the same design system:
- Same fonts (IBM Plex Sans, JetBrains Mono)
- Same color palette
- Same components (Navbar, Footer)
- Same Tailwind configuration
## Notes
- Fonts are stored in each app's `public/fonts/` directory
- Both apps use the same shared components and styles
- The monorepo uses npm workspaces and Turborepo for build orchestration

View file

@ -19,7 +19,7 @@ const nextConfig: NextConfig = {
"node_modules",
"../../node_modules",
];
if (!isServer) {
config.resolve.fallback = {
...config.resolve.fallback,

View file

@ -16,4 +16,4 @@ export default defineConfig({
schema: {
types: schemaTypes,
},
});
});

View file

@ -8,7 +8,9 @@ export const runtime = "edge";
function loadFont(fileName: string, baseUrl: string) {
return fetch(new URL(`/fonts/${fileName}`, baseUrl)).then((res) => {
if (!res.ok) {
throw new Error(`Failed to fetch font ${fileName}: ${res.status} ${res.statusText}`);
throw new Error(
`Failed to fetch font ${fileName}: ${res.status} ${res.statusText}`,
);
}
return res.arrayBuffer();
});
@ -63,16 +65,20 @@ export async function GET(
// Load fonts with error handling
let fontData;
try {
const [ibmPlexSans, jetbrainsMonoRegular, jetbrainsMonoMedium, jetbrainsMonoBold] =
await Promise.all([
loadFont("IBMPlexSans-VariableFont_wdth,wght.otf", fontBaseUrl),
loadFont("JetBrainsMono-Regular.otf", fontBaseUrl),
loadFont("JetBrainsMono-Medium.otf", fontBaseUrl),
loadFont("jetbrains-mono-bold.otf", fontBaseUrl),
]).catch((error: Error) => {
console.error("Error loading fonts:", error);
throw new Error(`Failed to load fonts: ${error.message}`);
});
const [
ibmPlexSans,
jetbrainsMonoRegular,
jetbrainsMonoMedium,
jetbrainsMonoBold,
] = await Promise.all([
loadFont("IBMPlexSans-VariableFont_wdth,wght.otf", fontBaseUrl),
loadFont("JetBrainsMono-Regular.otf", fontBaseUrl),
loadFont("JetBrainsMono-Medium.otf", fontBaseUrl),
loadFont("jetbrains-mono-bold.otf", fontBaseUrl),
]).catch((error: Error) => {
console.error("Error loading fonts:", error);
throw new Error(`Failed to load fonts: ${error.message}`);
});
fontData = {
ibmPlexSans,
@ -81,7 +87,8 @@ export async function GET(
jetbrainsMonoBold,
};
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
const errorMessage =
error instanceof Error ? error.message : "Unknown error";
console.error("Font loading error:", errorMessage);
return new Response(
JSON.stringify({
@ -97,7 +104,9 @@ export async function GET(
const post = await getBlogPost(slug);
if (!post) {
return new Response(JSON.stringify({ error: "Post not found" }), { status: 404 });
return new Response(JSON.stringify({ error: "Post not found" }), {
status: 404,
});
}
// Get author image URL if available
@ -114,8 +123,7 @@ export async function GET(
: request.nextUrl.origin);
const logoUrl = `${baseUrl}/Logomark.png`;
return new ImageResponse(
(
return new ImageResponse(
<div
style={{
background: "linear-gradient(to top right, #ffffff, #dcdfff)",
@ -259,53 +267,53 @@ export async function GET(
)}
</div>
</div>
</div>
),
{
width: 1200,
height: 630,
fonts: [
{
name: "IBM Plex Sans Regular",
data: fontData.ibmPlexSans,
style: "normal",
weight: 400,
},
{
name: "IBM Plex Sans Medium",
data: fontData.ibmPlexSans,
style: "normal",
weight: 500,
},
{
name: "IBM Plex Sans Bold",
data: fontData.ibmPlexSans,
style: "normal",
weight: 700,
},
{
name: "JetBrains Mono Regular",
data: fontData.jetbrainsMonoRegular,
style: "normal",
weight: 400,
},
{
name: "JetBrains Mono Medium",
data: fontData.jetbrainsMonoMedium,
style: "normal",
weight: 500,
},
{
name: "JetBrains Mono Bold",
data: fontData.jetbrainsMonoBold,
style: "normal",
weight: 600,
},
],
},
);
</div>,
{
width: 1200,
height: 630,
fonts: [
{
name: "IBM Plex Sans Regular",
data: fontData.ibmPlexSans,
style: "normal",
weight: 400,
},
{
name: "IBM Plex Sans Medium",
data: fontData.ibmPlexSans,
style: "normal",
weight: 500,
},
{
name: "IBM Plex Sans Bold",
data: fontData.ibmPlexSans,
style: "normal",
weight: 700,
},
{
name: "JetBrains Mono Regular",
data: fontData.jetbrainsMonoRegular,
style: "normal",
weight: 400,
},
{
name: "JetBrains Mono Medium",
data: fontData.jetbrainsMonoMedium,
style: "normal",
weight: 500,
},
{
name: "JetBrains Mono Bold",
data: fontData.jetbrainsMonoBold,
style: "normal",
weight: 600,
},
],
},
);
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
const errorMessage =
error instanceof Error ? error.message : "Unknown error";
console.error("Error generating image response:", error);
return new Response(
JSON.stringify({
@ -316,4 +324,3 @@ export async function GET(
);
}
}

View file

@ -49,7 +49,7 @@ export async function generateMetadata({
// Get baseUrl - use NEXT_PUBLIC_APP_URL if set, otherwise construct from VERCEL_URL
// Restrict to allowed hosts: localhost:3000, archgw-tau.vercel.app, or plano.katanemo.com
let baseUrl = "http://localhost:3000";
if (process.env.NEXT_PUBLIC_APP_URL) {
const url = process.env.NEXT_PUBLIC_APP_URL;
if (
@ -118,4 +118,3 @@ interface LayoutProps {
export default async function Layout({ children, params }: LayoutProps) {
return <>{children}</>;
}

View file

@ -33,4 +33,3 @@ export default function NotFound() {
</div>
);
}

View file

@ -204,4 +204,3 @@ export default async function BlogPostPage({
</article>
);
}

View file

@ -117,4 +117,3 @@ export default async function BlogPage() {
</div>
);
}

View file

@ -1,7 +1,15 @@
"use client";
import React from "react";
import { ResearchHero, ResearchGrid, ResearchTimeline, ResearchCTA, ResearchCapabilities, ResearchBenchmarks, ResearchFamily } from "@/components/research";
import {
ResearchHero,
ResearchGrid,
ResearchTimeline,
ResearchCTA,
ResearchCapabilities,
ResearchBenchmarks,
ResearchFamily,
} from "@/components/research";
import { UnlockPotentialSection } from "@/components/UnlockPotentialSection";
export default function ResearchPage() {

View file

@ -9,4 +9,3 @@ export default function StudioLayout({
</div>
);
}

View file

@ -85,4 +85,3 @@ export function BlogCard({ post, index = 0 }: BlogCardProps) {
</motion.div>
);
}

View file

@ -23,4 +23,3 @@ export function BlogHeader() {
</motion.section>
);
}

View file

@ -3,10 +3,7 @@
export function BlogSectionHeader() {
return (
<h2 className="text-2xl sm:text-3xl lg:text-4xl font-normal leading-tight tracking-tighter text-black mb-12">
<span className="font-sans">
The latest and greatest from our blog.
</span>
<span className="font-sans">The latest and greatest from our blog.</span>
</h2>
);
}

View file

@ -3,11 +3,7 @@
import { usePathname } from "next/navigation";
import { Navbar, Footer } from "@katanemo/ui";
export function ConditionalLayout({
children,
}: {
children: React.ReactNode;
}) {
export function ConditionalLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const isStudio = pathname?.startsWith("/studio");
@ -23,4 +19,3 @@ export function ConditionalLayout({
</div>
);
}

View file

@ -115,4 +115,3 @@ export function FeaturedBlogCard({ post }: FeaturedBlogCardProps) {
</motion.div>
);
}

View file

@ -44,9 +44,8 @@ export function Hero() {
{/* Subheading with CTA Buttons */}
<div className="max-w-7xl relative z-10">
<p className="text-base sm:text-lg md:text-xl lg:text-[22px] font-sans font-[400] tracking-[-1.0px] sm:tracking-[-1.22px]! text-black max-w-76 sm:max-w-2xl mb-6">
Build agents faster, and deliver them reliably to production -
by offloading the critical plumbing work to Plano.
Build agents faster, and deliver them reliably to production - by
offloading the critical plumbing work to Plano.
</p>
{/* CTA Buttons */}

View file

@ -150,7 +150,9 @@ export function IdeaToAgentSection() {
</div>
<Button asChild className="mt-6 sm:mt-8 w-full sm:w-auto">
<Link href={carouselData[currentSlide].link}>Learn more</Link>
<Link href={carouselData[currentSlide].link}>
Learn more
</Link>
</Button>
</div>
</div>

View file

@ -20,15 +20,18 @@ export function IntroSection() {
{/* Body Text */}
<div className="text-white text-sm sm:text-base lg:text-lg max-w-[713px]">
<p className="mb-0">
Plano is a models-native proxy and dataplane for agents that handles
critical plumbing work in AI - agent routing and orchestration, rich agentic
traces, guardrail hooks, and smart model routing APIs for LLMs. Use any
language, AI framework, and deliver agents to productions quickly with Plano.
Plano is a models-native proxy and dataplane for agents that
handles critical plumbing work in AI - agent routing and
orchestration, rich agentic traces, guardrail hooks, and smart
model routing APIs for LLMs. Use any language, AI framework, and
deliver agents to productions quickly with Plano.
</p>
<p className="mb-0 mt-4">
Developers can focus more on core product logic of agents.
Product teams can accelerate feedback loops for reinforcement learning.
Engineering teams can standardize policies and access controls across every agent and LLM for safer, more reliable scaling.
Developers can focus more on core product logic of agents.
Product teams can accelerate feedback loops for reinforcement
learning. Engineering teams can standardize policies and access
controls across every agent and LLM for safer, more reliable
scaling.
</p>
</div>
</div>

View file

@ -34,17 +34,17 @@ export function LogoCloud() {
const isTMobile = index === 1; // T-Mobile is before HP
const isHP = index === 2; // HP is in center
const isSanDisk = index === 3; // SanDisk is after HP
// Custom spacing for logos around HP on large screens
let spacingClass = 'lg:mx-6 xl:mx-8'; // Default spacing
let spacingClass = "lg:mx-6 xl:mx-8"; // Default spacing
if (isTMobile) {
spacingClass = 'lg:mr-3 xl:mr-4 lg:ml-6 xl:ml-8'; // Smaller gap to HP
spacingClass = "lg:mr-3 xl:mr-4 lg:ml-6 xl:ml-8"; // Smaller gap to HP
} else if (isHP) {
spacingClass = 'lg:mx-3 xl:mx-4'; // Smaller gaps on both sides
spacingClass = "lg:mx-3 xl:mx-4"; // Smaller gaps on both sides
} else if (isSanDisk) {
spacingClass = 'lg:ml-3 xl:ml-4 lg:mr-6 xl:mr-8'; // Smaller gap from HP
spacingClass = "lg:ml-3 xl:ml-4 lg:mr-6 xl:mr-8"; // Smaller gap from HP
}
return (
<div
key={logo.name}

View file

@ -13,7 +13,7 @@ const squares = [
{ 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 },
@ -22,7 +22,7 @@ const squares = [
{ 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 },
@ -31,7 +31,7 @@ const squares = [
{ 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 },
@ -40,7 +40,7 @@ const squares = [
{ 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 },
@ -49,7 +49,7 @@ const squares = [
{ 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 },
@ -58,7 +58,7 @@ const squares = [
{ 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 },
@ -92,7 +92,7 @@ function getDeterministicValues(index: number) {
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),
@ -105,9 +105,9 @@ function getDeterministicValues(index: number) {
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, '-');
const gradientId = useId().replace(/:/g, "-");
const maskId = useId().replace(/:/g, "-");
return (
<div className="absolute inset-0 pointer-events-none opacity-100">
<motion.div
@ -143,55 +143,61 @@ export function NetworkAnimation({ className }: NetworkAnimationProps) {
<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
}}
/>
);
})}
{/* 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>

View file

@ -11,22 +11,22 @@ const components = {
types: {
image: ({ value }: any) => {
if (!value?.asset) return null;
const imageUrl = urlFor(value);
const asset = value.asset;
// Get natural dimensions if available from metadata
const dimensions = asset.metadata?.dimensions;
const width = dimensions?.width || 1000;
const height = dimensions?.height || 562;
const aspectRatio = dimensions ? height / width : 0.5625; // Default to 16:9 if no dimensions
return (
<div className="my-6 lg:my-8">
<div className="max-w-3xl mx-auto">
<div className="relative w-full overflow-hidden rounded-lg bg-black/5">
<div
className="relative w-full"
<div
className="relative w-full"
style={{ paddingBottom: `${aspectRatio * 100}%` }}
>
<Image
@ -39,7 +39,9 @@ const components = {
</div>
</div>
{value.alt && (
<p className="mt-2 text-sm text-black/60 text-center">{value.alt}</p>
<p className="mt-2 text-sm text-black/60 text-center">
{value.alt}
</p>
)}
</div>
</div>
@ -91,12 +93,8 @@ const components = {
),
},
listItem: {
bullet: (props: any) => (
<li className="ml-4">{props.children}</li>
),
number: (props: any) => (
<li className="ml-4">{props.children}</li>
),
bullet: (props: any) => <li className="ml-4">{props.children}</li>,
number: (props: any) => <li className="ml-4">{props.children}</li>,
},
marks: {
strong: ({ children }: { children: React.ReactNode }) => (
@ -109,7 +107,11 @@ const components = {
<a
href={props.value?.href || "#"}
target={props.value?.href?.startsWith("http") ? "_blank" : undefined}
rel={props.value?.href?.startsWith("http") ? "noopener noreferrer" : undefined}
rel={
props.value?.href?.startsWith("http")
? "noopener noreferrer"
: undefined
}
className="text-[var(--secondary)] hover:underline font-medium"
>
{props.children}
@ -121,4 +123,3 @@ const components = {
export function PortableText({ content }: PortableTextProps) {
return <SanityPortableText value={content} components={components} />;
}

View file

@ -115,7 +115,9 @@ export function UseCasesSection() {
What's possible with Plano
</h2>
<Button asChild className="hidden lg:block">
<Link href="https://docs.plano.katanemo.com/getting-started/installation">Start building</Link>
<Link href="https://docs.plano.katanemo.com/getting-started/installation">
Start building
</Link>
</Button>
</div>
</div>
@ -156,7 +158,9 @@ export function UseCasesSection() {
{/* Start building button - Mobile only, appears last */}
<div className="lg:hidden mt-8">
<Button asChild className="w-full">
<Link href="https://docs.plano.katanemo.com/getting-started/installation">Start building</Link>
<Link href="https://docs.plano.katanemo.com/getting-started/installation">
Start building
</Link>
</Button>
</div>
</div>
@ -284,7 +288,10 @@ export function UseCasesSection() {
</div>
<div className="flex flex-col sm:flex-row gap-3 w-full sm:w-auto order-1">
<Button asChild className="w-full sm:w-auto">
<Link href="https://docs.plano.katanemo.com/getting-started/installation" className="flex items-center gap-2">
<Link
href="https://docs.plano.katanemo.com/getting-started/installation"
className="flex items-center gap-2"
>
Start building
<ArrowRightIcon className="w-4 h-4" />
</Link>

View file

@ -51,7 +51,7 @@ const verticalCarouselData = [
category: "PROGRAMMABLE ARCHITECTURE",
title: "",
description: [
"As agent workloads move beyond prototypes, teams end up scattering critical logic across apps: compliance checks, context \"patches,\" provider-specific quirks, etc. That glue code gets duplicated across agents, is hard to audit, and slows iteration because every policy or workflow change requires touching application code and redeploying.",
'As agent workloads move beyond prototypes, teams end up scattering critical logic across apps: compliance checks, context "patches," provider-specific quirks, etc. That glue code gets duplicated across agents, is hard to audit, and slows iteration because every policy or workflow change requires touching application code and redeploying.',
"Plano keeps that logic in one place with a programmable Agent Filter Chain—hooks that can inspect, mutate, or terminate prompt traffic early, turning common steps (policy enforcement, jailbreak checks, context engineering, tool gating, routing hints) into reusable building blocks.",
],
diagram: "/PromptRouting.svg",
@ -180,11 +180,23 @@ export function VerticalCarouselSection() {
{/* Description */}
<div className="text-white text-sm sm:text-base lg:text-lg max-w-full lg:max-w-md -mt-0.5">
{verticalCarouselData[activeSlide].description.map((paragraph, index) => (
<p key={index} className={index < verticalCarouselData[activeSlide].description.length - 1 ? "mb-4" : "mb-0"}>
{paragraph}
</p>
))}
{verticalCarouselData[activeSlide].description.map(
(paragraph, index) => (
<p
key={index}
className={
index <
verticalCarouselData[activeSlide].description
.length -
1
? "mb-4"
: "mb-0"
}
>
{paragraph}
</p>
),
)}
</div>
</div>
</div>

View file

@ -16,7 +16,9 @@ export function ResearchBenchmarks() {
{/* 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>
<span className="font-sans">
Production excellence, outperforming frontier LLMs
</span>
</h2>
</div>

View file

@ -9,26 +9,34 @@ export function ResearchCTA() {
<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>
<span className="font-sans">
Meet Plano-Orchestrator. Our latest models.
</span>
</h1>
</div>
{/* Description with CTA Buttons */}
<div className="max-w-5xl">
<p className="leading-relaxed sm:text-lg md:text-lg lg:text-[18px] font-sans font-normal 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.
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>
<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>
<Link href="https://docs.plano.katanemo.com">
Get Started with Plano
</Link>
</Button>
</div>
</div>

View file

@ -52,14 +52,16 @@ export function ResearchCapabilities() {
{/* 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>
<span className="font-sans">
Accurately route with confidence with no compromise
</span>
</h2>
<p className="text-white/90 w-full sm:w-[75%] text-sm sm:text-base 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 className="text-white/90 w-full sm:w-[75%] text-sm sm:text-base 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>
@ -67,15 +69,18 @@ export function ResearchCapabilities() {
<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 }>> = {
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 */}
@ -84,9 +89,7 @@ export function ResearchCapabilities() {
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]" />
)}
{Icon && <Icon className="w-24 h-24 text-[#9797ea]" />}
</motion.div>
{/* Title */}
@ -107,15 +110,18 @@ export function ResearchCapabilities() {
<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 }>> = {
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}
@ -123,9 +129,7 @@ export function ResearchCapabilities() {
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]" />
)}
{Icon && <Icon className="w-24 h-24 text-[#9797ea]" />}
</motion.div>
);
})}

View file

@ -24,9 +24,7 @@ export function ResearchHero() {
</span>
<span className="text-xs sm:text-sm font-[600] tracking-[-0.6px]! text-black leading-tight">
<span className="">
Plano Orchestrator models released
</span>
<span className="">Plano Orchestrator models released</span>
</span>
</div>
</div>
@ -40,16 +38,19 @@ export function ResearchHero() {
{/* 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-[22px] font-sans font-normal tracking-[-1.0px] sm:tracking-[-1.22px]! text-black">
Our applied research focuses on how to deliver agents safely, efficiently,
and with improved real-world performance critical for any AI application,
but work that sits outside of any agent's core product logic.
Our applied research focuses on how to deliver agents safely,
efficiently, and with improved real-world performance critical for
any AI application, but work that sits outside of any agent's core
product 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>
<Button asChild className="w-full sm:w-auto">
<Link href="https://huggingface.co/katanemo">
Available on Hugging Face
</Link>
</Button>
</div>
</div>
</section>