diff --git a/apps/website/components/home-sections.tsx b/apps/website/components/home-sections.tsx
index 19f778ccb..a5c605ff0 100755
--- a/apps/website/components/home-sections.tsx
+++ b/apps/website/components/home-sections.tsx
@@ -1,17 +1,23 @@
"use client";
+import dynamic from "next/dynamic";
import { useEffect } from "react";
-import FAQ from "./faq";
-import Features from "./features";
-import Footer from "./footer";
import Hero from "./hero";
-import Pricing from "./pricing";
-import PricingModels from "./pricing-models";
-import Problem from "./problem";
-import ProductionScale from "./production-scale";
import SectionDivider from "./section-divider";
-import Solution from "./solution";
-import Testimonials from "./testimonials";
+
+// All below-fold sections are code-split into separate lazy chunks so the
+// initial JS bundle only contains the hero. Framer-motion, GSAP ScrollTrigger,
+// and lottie-web are pulled into these chunks rather than the main bundle.
+const LogoWall = dynamic(() => import("./logo-wall"));
+const Problem = dynamic(() => import("./problem"));
+const Solution = dynamic(() => import("./solution"));
+const PricingModels = dynamic(() => import("./pricing-models"));
+const Features = dynamic(() => import("./features"));
+const Testimonials = dynamic(() => import("./testimonials"));
+const ProductionScale = dynamic(() => import("./production-scale"));
+const Pricing = dynamic(() => import("./pricing"));
+const FAQ = dynamic(() => import("./faq"));
+const Footer = dynamic(() => import("./footer"));
function scrollToHash() {
const hash = window.location.hash;
@@ -36,6 +42,15 @@ export default function HomeSections() {
return (
<>
+ {/*
+
+
*/}
diff --git a/apps/website/components/logo-wall.tsx b/apps/website/components/logo-wall.tsx
new file mode 100644
index 000000000..e027e2b18
--- /dev/null
+++ b/apps/website/components/logo-wall.tsx
@@ -0,0 +1,73 @@
+"use client";
+
+import { cn } from "@/lib/utils";
+
+const LOGOS = [
+ { id: 1, name: "Mintlify", src: "/images/logos/mintlify_logo.svg.svg" },
+ { id: 2, name: "Browser Use", src: "/images/logos/Browser use.svg" },
+ { id: 3, name: "Firecrawl", src: "/images/logos/Firecrawl.svg.svg" },
+ { id: 4, name: "Mastra", src: "/images/logos/Mastra.svg.svg" },
+ { id: 5, name: "T3.chat", src: "/images/logos/T3_svg.svg" },
+ { id: 6, name: "", src: null },
+];
+
+const NUM_MOBILE_COLS = 2;
+const NUM_DESKTOP_COLS = 3;
+
+export default function LogoWall() {
+ return (
+
+
+ {/* Left heading — full width on mobile, 42% on desktop */}
+
+
+
+ Trusted by
+ AI teams
+
+
+ shipping fast
+
+
+
+
+ {/* Logo grid — 2 cols on mobile, 3 cols on desktop */}
+
+ {LOGOS.map((logo, i) => {
+ const isLastMobileCol = (i + 1) % NUM_MOBILE_COLS === 0;
+ const isLastDesktopCol = (i + 1) % NUM_DESKTOP_COLS === 0;
+ const isLastMobileRow = i >= LOGOS.length - NUM_MOBILE_COLS;
+ const isLastDesktopRow = i >= LOGOS.length - NUM_DESKTOP_COLS;
+
+ return (
+
+ {logo.src && (
+

+ )}
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/apps/website/components/navbar.tsx b/apps/website/components/navbar.tsx
index adea330c6..5d63cf272 100644
--- a/apps/website/components/navbar.tsx
+++ b/apps/website/components/navbar.tsx
@@ -289,7 +289,7 @@ export default function Navbar({
y: 0,
pointerEvents: "auto",
clipPath: "inset(0% 0 0% 0)",
- duration: 0.65,
+ duration: 0.2,
ease: "power3.inOut",
})
.to(
@@ -298,11 +298,11 @@ export default function Navbar({
opacity: 1,
filter: "blur(0px)",
scale: 1,
- duration: 0.4,
- stagger: 0.02,
+ duration: 0.15,
+ stagger: 0.01,
ease: "power2.out",
},
- "-=0.35",
+ "-=0.1",
);
},
{ scope: containerRef },
diff --git a/apps/website/components/pricing-models.tsx b/apps/website/components/pricing-models.tsx
index 354535faa..507b30ff6 100755
--- a/apps/website/components/pricing-models.tsx
+++ b/apps/website/components/pricing-models.tsx
@@ -59,14 +59,25 @@ const sidebarItems = [
] as const;
const images: Record<(typeof sidebarItems)[number]["model"], string> = {
- 0: "/images/pricing-models/Subscriptions.webp",
- 1: "/images/pricing-models/Subscriptions (1).webp",
- 2: "/images/pricing-models/Trial Configuration.webp",
- 3: "/images/pricing-models/Hybrid Plan Builder.webp",
- 4: "/images/pricing-models/Usage Metering.webp",
- 5: "/images/pricing-models/Hybrid Plan Builder (1).webp",
- 6: "/images/pricing-models/Subscriptions (2).webp",
- 7: "/images/pricing-models/Subscriptions (3).webp",
+ 0: "/images/pricing-models/Subscriptions.avif",
+ 1: "/images/pricing-models/Subscriptions (1).avif",
+ 2: "/images/pricing-models/Trial Configuration.avif",
+ 3: "/images/pricing-models/Hybrid Plan Builder.avif",
+ 4: "/images/pricing-models/Usage Metering.avif",
+ 5: "/images/pricing-models/Hybrid Plan Builder (1).avif",
+ 6: "/images/pricing-models/Subscriptions (2).avif",
+ 7: "/images/pricing-models/Subscriptions (3).avif",
+};
+
+const mobileImages: Record<(typeof sidebarItems)[number]["model"], string> = {
+ 0: "/images/pricing-models/Subscriptions-mobile.avif",
+ 1: "/images/pricing-models/Subscriptions (1)-mobile.avif",
+ 2: "/images/pricing-models/Trial Configuration-mobile.avif",
+ 3: "/images/pricing-models/Hybrid Plan Builder-mobile.avif",
+ 4: "/images/pricing-models/Usage Metering-mobile.avif",
+ 5: "/images/pricing-models/Hybrid Plan Builder (1)-mobile.avif",
+ 6: "/images/pricing-models/Subscriptions (2)-mobile.avif",
+ 7: "/images/pricing-models/Subscriptions (3)-mobile.avif",
};
export default function PricingModels() {
@@ -159,7 +170,7 @@ export default function PricingModels() {
src={src}
alt="Pricing Model"
fill
- sizes="(max-width: 768px) 100vw, 50vw"
+ sizes="(max-width: 1024px) 100vw, (max-width: 1440px) 40vw, 600px"
className="object-contain"
priority={key === "0"}
/>
@@ -194,9 +205,12 @@ export default function PricingModels() {
>
-
diff --git a/apps/website/components/pricing.tsx b/apps/website/components/pricing.tsx
index 5cc8ded07..9560e1aa0 100755
--- a/apps/website/components/pricing.tsx
+++ b/apps/website/components/pricing.tsx
@@ -52,7 +52,7 @@ export default function Pricing() {
>
{/* Desktop Background */}
{/* Mobile Background */}
+
{plan.isPro && (
)}
diff --git a/apps/website/components/problem-animation.tsx b/apps/website/components/problem-animation.tsx
index 4db5e956d..d6889c4d0 100755
--- a/apps/website/components/problem-animation.tsx
+++ b/apps/website/components/problem-animation.tsx
@@ -70,6 +70,8 @@ export default function ProblemAnimation() {
src={src}
alt=""
className="block max-h-full max-w-[90%] object-contain"
+ loading="lazy"
+ decoding="async"
draggable={false}
/>
diff --git a/apps/website/components/problem.tsx b/apps/website/components/problem.tsx
index 631aefa58..798152042 100755
--- a/apps/website/components/problem.tsx
+++ b/apps/website/components/problem.tsx
@@ -74,7 +74,7 @@ export default function Problem() {
diff --git a/apps/website/components/solution-animation.tsx b/apps/website/components/solution-animation.tsx
index 95b2cdc5f..32deb4c52 100644
--- a/apps/website/components/solution-animation.tsx
+++ b/apps/website/components/solution-animation.tsx
@@ -15,52 +15,72 @@ export default function SolutionAnimation() {
useEffect(() => {
if (!containerRef.current) return;
- let cancelled = false;
const isMobile = window.innerWidth < 768;
const url = isMobile
? "/animation/solution-mobile.json"
: "/animation/solution-desktop.json";
- fetch(url)
- .then((res) => res.json())
- .then((animationData) => {
- if (cancelled || !containerRef.current) return;
-
- const anim = lottie.loadAnimation({
- container: containerRef.current,
- renderer: "svg",
- loop: false,
- autoplay: false,
- animationData,
- });
-
- animRef.current = anim;
-
- const totalFrames = anim.totalFrames;
- const loopStart = Math.floor(totalFrames * 0.2);
-
- anim.addEventListener("complete", () => {
- anim.loop = true;
- anim.playSegments([loopStart, totalFrames], true);
- });
-
- const st = ScrollTrigger.create({
- trigger: containerRef.current,
- start: "top 75%",
- onEnter: () => anim.play(),
- });
-
- cleanupRef.current = () => {
- st.kill();
- anim.destroy();
- };
- });
-
+ let cancelled = false;
const cleanupRef = { current: () => {} };
+ const initAnimation = () => {
+ if (cancelled || !containerRef.current) return;
+
+ fetch(url)
+ .then((res) => res.json())
+ .then((animationData) => {
+ if (cancelled || !containerRef.current) return;
+
+ const anim = lottie.loadAnimation({
+ container: containerRef.current,
+ renderer: "svg",
+ loop: false,
+ autoplay: false,
+ animationData,
+ });
+
+ animRef.current = anim;
+
+ const totalFrames = anim.totalFrames;
+ const loopStart = Math.floor(totalFrames * 0.2);
+
+ anim.addEventListener("complete", () => {
+ anim.loop = true;
+ anim.playSegments([loopStart, totalFrames], true);
+ });
+
+ const st = ScrollTrigger.create({
+ trigger: containerRef.current,
+ start: "top 75%",
+ onEnter: () => anim.play(),
+ });
+
+ cleanupRef.current = () => {
+ st.kill();
+ anim.destroy();
+ };
+ });
+ };
+
+ // Defer the 1.4–2.1 MB Lottie JSON fetch until the element is close to
+ // the viewport. Without this the browser fetches it during initial load
+ // and blocks the main thread while parsing the large JSON blob.
+ const observer = new IntersectionObserver(
+ (entries) => {
+ if (entries[0].isIntersecting) {
+ observer.disconnect();
+ initAnimation();
+ }
+ },
+ { rootMargin: "200px" },
+ );
+
+ observer.observe(containerRef.current);
+
return () => {
cancelled = true;
+ observer.disconnect();
cleanupRef.current();
};
}, []);
diff --git a/apps/website/components/solution.tsx b/apps/website/components/solution.tsx
index 6bb00c58f..5ca61c596 100644
--- a/apps/website/components/solution.tsx
+++ b/apps/website/components/solution.tsx
@@ -30,7 +30,7 @@ const iconSrc = {
export default function Solution() {
return (
-
+
{
- const videoRef = useRef
(null);
const scrollRef = useRef(null);
const progressRef = useRef(null);
const [canScrollLeft, setCanScrollLeft] = useState(false);
@@ -68,12 +67,6 @@ const Testimonials = () => {
}
}, []);
- useEffect(() => {
- if (videoRef.current) {
- videoRef.current.playbackRate = 0.4;
- }
- }, []);
-
useEffect(() => {
handleScroll();
window.addEventListener("resize", handleScroll);
@@ -143,7 +136,6 @@ const Testimonials = () => {