-
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 = () => {