diff --git a/src/components/ecommerce/ProductDetailCard.tsx b/src/components/ecommerce/ProductDetailCard.tsx
index 11d866b..e69de29 100644
--- a/src/components/ecommerce/ProductDetailCard.tsx
+++ b/src/components/ecommerce/ProductDetailCard.tsx
@@ -1,137 +0,0 @@
-import { useState } from "react";
-import { Star } from "lucide-react";
-import { cls } from "@/lib/utils";
-import ImageOrVideo from "@/components/ui/ImageOrVideo";
-import Button from "@/components/ui/Button";
-import Transition from "@/components/ui/Transition";
-
-type ProductVariant = {
- label: string;
- options: string[];
- selected: string;
- onChange: (value: string) => void;
-};
-
-type ProductDetailCardProps = {
- name: string;
- price: string;
- salePrice?: string;
- images: string[];
- description?: string;
- rating?: number;
- ribbon?: string;
- inventoryStatus?: "in-stock" | "out-of-stock";
- inventoryQuantity?: number;
- sku?: string;
- variants?: ProductVariant[];
- quantity?: ProductVariant;
- onAddToCart?: () => void;
- onBuyNow?: () => void;
-};
-
-const ProductDetailCard = ({ name, price, salePrice, images, description, rating = 0, ribbon, inventoryStatus, inventoryQuantity, sku, variants, quantity, onAddToCart, onBuyNow }: ProductDetailCardProps) => {
- const [selectedImage, setSelectedImage] = useState(0);
-
- return (
-
-
-
-
-
-
- {images.length > 1 && (
-
- {images.map((src, i) => (
-
- ))}
-
- )}
-
-
-
-
-
{name}
- {ribbon && {ribbon}}
-
-
-
-
- {salePrice ? (
- <>
- {price}
- {salePrice}
- >
- ) : (
- price
- )}
-
-
- {Array.from({ length: 5 }).map((_, i) => (
-
- ))}
-
-
- {(inventoryStatus || inventoryQuantity || sku) && (
-
- {inventoryStatus && (
-
- {inventoryStatus === "in-stock" ? "In Stock" : "Out of Stock"}
-
- )}
- {inventoryQuantity && (
- {inventoryQuantity} available
- )}
- {sku && SKU: {sku}}
-
- )}
- {description &&
{description}
}
- {variants && variants.length > 0 && (
-
- {variants.map((variant) => (
-
-
-
-
-
-
- ))}
-
- )}
- {quantity && (
-
-
-
-
-
-
- )}
-
-
-
-
-
-
-
- );
-};
-
-export default ProductDetailCard;
-export type { ProductVariant };
diff --git a/src/components/sections/about/AboutFeaturesSplit.tsx b/src/components/sections/about/AboutFeaturesSplit.tsx
index 3d59abb..e69de29 100644
--- a/src/components/sections/about/AboutFeaturesSplit.tsx
+++ b/src/components/sections/about/AboutFeaturesSplit.tsx
@@ -1,90 +0,0 @@
-import type { LucideIcon } from "lucide-react";
-import Button from "@/components/ui/Button";
-import TextAnimation from "@/components/ui/TextAnimation";
-import ImageOrVideo from "@/components/ui/ImageOrVideo";
-import { resolveIcon } from "@/utils/resolve-icon";
-
-type AboutFeaturesSplitProps = {
- tag: string;
- title: string;
- description: string;
- primaryButton?: { text: string; href: string };
- secondaryButton?: { text: string; href: string };
- items: { icon: string | LucideIcon; title: string; description: string }[];
-} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
-
-const AboutFeaturesSplit = ({
- tag,
- title,
- description,
- primaryButton,
- secondaryButton,
- items,
- imageSrc,
- videoSrc,
-}: AboutFeaturesSplitProps) => {
- return (
-
-
-
-
-
-
-
-
-
- {(primaryButton || secondaryButton) && (
-
- {primaryButton && }
- {secondaryButton && }
-
- )}
-
-
-
-
- {items.map((item, index) => {
- const ItemIcon = resolveIcon(item.icon);
- return (
-
-
-
-
-
-
{item.title}
-
{item.description}
-
- {index < items.length - 1 && (
-
- )}
-
- );
- })}
-
-
-
-
-
-
- );
-};
-
-export default AboutFeaturesSplit;
diff --git a/src/components/sections/blog/BlogSimpleCards.tsx b/src/components/sections/blog/BlogSimpleCards.tsx
index e128c1e..e69de29 100644
--- a/src/components/sections/blog/BlogSimpleCards.tsx
+++ b/src/components/sections/blog/BlogSimpleCards.tsx
@@ -1,159 +0,0 @@
-import { ArrowUpRight, Loader2 } from "lucide-react";
-import ScrollReveal from "@/components/ui/ScrollReveal";
-import Button from "@/components/ui/Button";
-import TextAnimation from "@/components/ui/TextAnimation";
-import GridOrCarousel from "@/components/ui/GridOrCarousel";
-import ImageOrVideo from "@/components/ui/ImageOrVideo";
-import { useButtonClick } from "@/hooks/useButtonClick";
-import useBlogPosts from "@/hooks/useBlogPosts";
-
-type BlogItem = {
- category: string;
- title: string;
- excerpt: string;
- authorName: string;
- authorImageSrc: string;
- date: string;
- imageSrc: string;
- href?: string;
- onClick?: () => void;
-};
-
-const BlogCardItem = ({ item }: { item: BlogItem }) => {
- const handleClick = useButtonClick(item.href, item.onClick);
-
- return (
-
-
-
-
-
-
-
{item.title}
-
{item.excerpt}
-
-
-
-
-
- {item.authorName}
- {item.date}
-
-
-
-
- );
-};
-
-type BlogSimpleCardsProps = {
- tag: string;
- title: string;
- description: string;
- primaryButton?: { text: string; href: string };
- secondaryButton?: { text: string; href: string };
- items?: BlogItem[];
-};
-
-const BlogSimpleCards = ({
- tag,
- title,
- description,
- primaryButton,
- secondaryButton,
- items: itemsProp,
-}: BlogSimpleCardsProps) => {
- const { posts, isLoading } = useBlogPosts();
- const isFromApi = posts.length > 0;
- const items = isFromApi
- ? posts.map((p) => ({
- category: p.category,
- title: p.title,
- excerpt: p.excerpt,
- authorName: p.authorName,
- authorImageSrc: p.authorAvatar,
- date: p.date,
- imageSrc: p.imageSrc,
- onClick: p.onBlogClick,
- }))
- : itemsProp;
-
- if (isLoading && !itemsProp) {
- return (
-
- );
- }
-
- if (!items || items.length === 0) {
- return null;
- }
-
- return (
-
-
-
-
-
-
-
-
-
- {(primaryButton || secondaryButton) && (
-
- {primaryButton && }
- {secondaryButton && }
-
- )}
-
-
-
-
- {items.map((item, index) => (
-
- ))}
-
-
-
-
- );
-};
-
-export default BlogSimpleCards;
diff --git a/src/components/sections/features/FeaturesBento.tsx b/src/components/sections/features/FeaturesBento.tsx
index 0775b16..e69de29 100644
--- a/src/components/sections/features/FeaturesBento.tsx
+++ b/src/components/sections/features/FeaturesBento.tsx
@@ -1,103 +0,0 @@
-import Button from "@/components/ui/Button";
-import TextAnimation from "@/components/ui/TextAnimation";
-import GridOrCarousel from "@/components/ui/GridOrCarousel";
-import ScrollReveal from "@/components/ui/ScrollReveal";
-import InfoCardMarquee from "@/components/ui/InfoCardMarquee";
-import TiltedStackCards from "@/components/ui/TiltedStackCards";
-import AnimatedBarChart from "@/components/ui/AnimatedBarChart";
-import OrbitingIcons from "@/components/ui/OrbitingIcons";
-import IconTextMarquee from "@/components/ui/IconTextMarquee";
-import ChatMarquee from "@/components/ui/ChatMarquee";
-import ChecklistTimeline from "@/components/ui/ChecklistTimeline";
-import MediaStack from "@/components/ui/MediaStack";
-import type { LucideIcon } from "lucide-react";
-
-type IconInput = string | LucideIcon;
-
-type FeatureCard = { title: string; description: string } & (
- | { bentoComponent: "info-card-marquee"; infoCards: { icon: IconInput; label: string; value: string }[] }
- | { bentoComponent: "tilted-stack-cards"; stackCards: [{ icon: IconInput; title: string; subtitle: string; detail: string }, { icon: IconInput; title: string; subtitle: string; detail: string }, { icon: IconInput; title: string; subtitle: string; detail: string }] }
- | { bentoComponent: "animated-bar-chart" }
- | { bentoComponent: "orbiting-icons"; centerIcon: IconInput; orbitIcons: IconInput[] }
- | { bentoComponent: "icon-text-marquee"; centerIcon: IconInput; marqueeTexts: string[] }
- | { bentoComponent: "chat-marquee"; aiIcon: IconInput; userIcon: IconInput; exchanges: { userMessage: string; aiResponse: string }[]; placeholder: string }
- | { bentoComponent: "checklist-timeline"; heading: string; subheading: string; checklistItems: [{ label: string; detail: string }, { label: string; detail: string }, { label: string; detail: string }]; completedLabel: string }
- | { bentoComponent: "media-stack"; mediaItems: [{ imageSrc?: string; videoSrc?: string }, { imageSrc?: string; videoSrc?: string }, { imageSrc?: string; videoSrc?: string }] }
-);
-
-const getBentoComponent = (feature: FeatureCard) => {
- switch (feature.bentoComponent) {
- case "info-card-marquee": return ;
- case "tilted-stack-cards": return ;
- case "animated-bar-chart": return ;
- case "orbiting-icons": return ;
- case "icon-text-marquee": return ;
- case "chat-marquee": return ;
- case "checklist-timeline": return ;
- case "media-stack": return ;
- }
-};
-
-const FeaturesBento = ({
- tag,
- title,
- description,
- primaryButton,
- secondaryButton,
- features,
-}: {
- tag: string;
- title: string;
- description: string;
- primaryButton?: { text: string; href: string };
- secondaryButton?: { text: string; href: string };
- features: FeatureCard[];
-}) => (
-
-
-
-
-
-
-
-
-
- {(primaryButton || secondaryButton) && (
-
- {primaryButton && }
- {secondaryButton && }
-
- )}
-
-
-
- {features.map((feature) => (
-
-
{getBentoComponent(feature)}
-
-
{feature.title}
-
{feature.description}
-
-
- ))}
-
-
-
-
-);
-
-export default FeaturesBento;
diff --git a/src/components/sections/features/FeaturesBentoGridCta.tsx b/src/components/sections/features/FeaturesBentoGridCta.tsx
index a7cbc38..e69de29 100644
--- a/src/components/sections/features/FeaturesBentoGridCta.tsx
+++ b/src/components/sections/features/FeaturesBentoGridCta.tsx
@@ -1,112 +0,0 @@
-import ImageOrVideo from "@/components/ui/ImageOrVideo";
-import TextAnimation from "@/components/ui/TextAnimation";
-import ScrollReveal from "@/components/ui/ScrollReveal";
-import { cls } from "@/lib/utils";
-
-type FeatureItem = {
- title: string;
- description: string;
-} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
-
-interface FeaturesBentoGridCtaProps {
- tag: string;
- title: string;
- description: string;
- features: [FeatureItem, FeatureItem, FeatureItem, FeatureItem];
- ctaButton?: {
- text: string;
- href: string;
- avatarSrc?: string;
- avatarLabel?: string;
- };
-}
-
-const FeaturesBentoGridCta = ({
- tag,
- title,
- description,
- features,
- ctaButton,
-}: FeaturesBentoGridCtaProps) => {
- const colSpans = ["md:col-span-5", "md:col-span-7", "md:col-span-7", "md:col-span-5"];
-
- return (
-
-
-
-
-
-
- {features.map((feature, index) => (
-
-
-
-
-
-
{feature.title}
-
{feature.description}
-
-
- ))}
-
-
-
-
- );
-};
-
-export default FeaturesBentoGridCta;
diff --git a/src/components/sections/footer/FooterBrandReveal.tsx b/src/components/sections/footer/FooterBrandReveal.tsx
index 6f0d18b..e69de29 100644
--- a/src/components/sections/footer/FooterBrandReveal.tsx
+++ b/src/components/sections/footer/FooterBrandReveal.tsx
@@ -1,101 +0,0 @@
-import { useRef, useEffect, useState } from "react";
-import { ChevronRight } from "lucide-react";
-import { useButtonClick } from "@/hooks/useButtonClick";
-import AutoFillText from "@/components/ui/AutoFillText";
-import { cls } from "@/lib/utils";
-
-type FooterLink = {
- label: string;
- href?: string;
- onClick?: () => void;
-};
-
-type FooterColumn = {
- items: FooterLink[];
-};
-
-const FooterLinkItem = ({ label, href, onClick }: FooterLink) => {
- const handleClick = useButtonClick(href, onClick);
-
- return (
-
-
-
-
- );
-};
-
-const FooterBrandReveal = ({
- brand,
- columns,
-}: {
- brand: string;
- columns: FooterColumn[];
-}) => {
- const footerRef = useRef(null);
- const [footerHeight, setFooterHeight] = useState(0);
-
- useEffect(() => {
- const updateHeight = () => {
- if (footerRef.current) {
- setFooterHeight(footerRef.current.offsetHeight);
- }
- };
-
- updateHeight();
-
- const resizeObserver = new ResizeObserver(updateHeight);
- if (footerRef.current) {
- resizeObserver.observe(footerRef.current);
- }
-
- return () => resizeObserver.disconnect();
- }, []);
-
- return (
-
-
-
-
-
- );
-};
-
-export default FooterBrandReveal;
diff --git a/src/components/sections/hero/HeroBillboardFloatingCards.tsx b/src/components/sections/hero/HeroBillboardFloatingCards.tsx
index a887516..e69de29 100644
--- a/src/components/sections/hero/HeroBillboardFloatingCards.tsx
+++ b/src/components/sections/hero/HeroBillboardFloatingCards.tsx
@@ -1,180 +0,0 @@
-import { useRef } from "react";
-import { useScroll, useTransform, motion } from "motion/react";
-import { Check } from "lucide-react";
-import { cls } from "@/lib/utils";
-import HeroBackgroundSlot from "@/components/ui/HeroBackgroundSlot";
-import Button from "@/components/ui/Button";
-import TextAnimation from "@/components/ui/TextAnimation";
-import AvatarGroup from "@/components/ui/AvatarGroup";
-import ImageOrVideo from "@/components/ui/ImageOrVideo";
-import ScrollReveal from "@/components/ui/ScrollReveal";
-
-type FloatingCardPosition = "top-left" | "top-right" | "middle-left" | "middle-right";
-
-type HeroBillboardFloatingCardsProps = {
- avatarsSrc: string[];
- avatarsLabel: string;
- title: string;
- description: string;
- primaryButton: { text: string; href: string };
- secondaryButton: { text: string; href: string };
- note?: string;
- floatingCardsSrc: [string, string, string, string];
- logosSrc?: string[];
-} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
-
-const POSITIONS: FloatingCardPosition[] = ["top-left", "top-right", "middle-left", "middle-right"];
-
-const FLOATING_CARD_CONFIG: Record = {
- "top-left": {
- position: "top-8 left-0",
- rotation: "-rotate-8",
- size: "size-20 xl:size-22 2xl:size-24",
- animation: { duration: 4, delay: 0, yOffset: -8, entryDelay: 0.3 },
- },
- "top-right": {
- position: "top-4 right-4",
- rotation: "rotate-10",
- size: "size-18 xl:size-20 2xl:size-22",
- animation: { duration: 5, delay: 1, yOffset: -10, entryDelay: 0.5 },
- },
- "middle-left": {
- position: "top-1/2 left-2",
- rotation: "rotate-6",
- size: "size-18 xl:size-20 2xl:size-22",
- animation: { duration: 4.5, delay: 0.5, yOffset: -9, entryDelay: 0.7 },
- },
- "middle-right": {
- position: "top-1/2 right-0",
- rotation: "-rotate-6",
- size: "size-20 xl:size-22 2xl:size-24",
- animation: { duration: 3.8, delay: 1.5, yOffset: -8, entryDelay: 0.9 },
- },
-};
-
-const HeroBillboardFloatingCards = ({
- avatarsSrc,
- avatarsLabel,
- title,
- description,
- primaryButton,
- secondaryButton,
- note,
- floatingCardsSrc,
- logosSrc,
- imageSrc,
- videoSrc,
-}: HeroBillboardFloatingCardsProps) => {
- const containerRef = useRef(null);
- const { scrollYProgress } = useScroll({ target: containerRef });
-
- const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
- const scale = useTransform(scrollYProgress, [0, 1], [1.05, 1]);
-
- return (
-
-
-
-
- {POSITIONS.map((position, index) => {
- const config = FLOATING_CARD_CONFIG[position];
- const src = floatingCardsSrc[index];
- if (!src) return null;
- return (
-
-
-
-
-
- );
- })}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {note && (
-
-
-
-
-
- {note}
-
-
- )}
-
-
-
-
-
-
-
-
-
-
-
- {logosSrc && logosSrc.length > 0 && (
-
-
- {[...logosSrc, ...logosSrc, ...logosSrc, ...logosSrc, ...logosSrc, ...logosSrc, ...logosSrc, ...logosSrc].map((logo, index) => (
-
-

-
- ))}
-
-
- )}
-
-
- );
-};
-
-export default HeroBillboardFloatingCards;
diff --git a/src/components/sections/hero/HeroSplitTestimonial.tsx b/src/components/sections/hero/HeroSplitTestimonial.tsx
index fc426e5..e69de29 100644
--- a/src/components/sections/hero/HeroSplitTestimonial.tsx
+++ b/src/components/sections/hero/HeroSplitTestimonial.tsx
@@ -1,128 +0,0 @@
-import { useEffect, useState } from "react";
-import { Star } from "lucide-react";
-import { motion, AnimatePresence } from "motion/react";
-import { cls } from "@/lib/utils";
-import Button from "@/components/ui/Button";
-import HeroBackgroundSlot from "@/components/ui/HeroBackgroundSlot";
-import TextAnimation from "@/components/ui/TextAnimation";
-import ImageOrVideo from "@/components/ui/ImageOrVideo";
-import ScrollReveal from "@/components/ui/ScrollReveal";
-
-type Testimonial = {
- name: string;
- handle: string;
- text: string;
- rating: number;
-} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
-
-type HeroSplitTestimonialProps = {
- tag: string;
- title: string;
- description: string;
- primaryButton: { text: string; href: string };
- secondaryButton: { text: string; href: string };
- testimonials: Testimonial[];
-} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
-
-const INTERVAL = 5000;
-
-const HeroSplitTestimonial = ({
- tag,
- title,
- description,
- primaryButton,
- secondaryButton,
- imageSrc,
- videoSrc,
- testimonials,
-}: HeroSplitTestimonialProps) => {
- const [currentIndex, setCurrentIndex] = useState(0);
-
- useEffect(() => {
- if (testimonials.length <= 1) return;
-
- const interval = setInterval(() => {
- setCurrentIndex((prev) => (prev + 1) % testimonials.length);
- }, INTERVAL);
- return () => clearInterval(interval);
- }, [currentIndex, testimonials.length]);
-
- const testimonial = testimonials[currentIndex];
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- {Array.from({ length: 5 }).map((_, index) => (
-
- ))}
-
-
- {testimonial.text}
-
-
-
-
- {testimonial.name}
- {testimonial.handle}
-
-
-
-
-
-
-
- );
-};
-
-export default HeroSplitTestimonial;
diff --git a/src/components/sections/hero/HeroVideoExpand.tsx b/src/components/sections/hero/HeroVideoExpand.tsx
index e0e7f08..e69de29 100644
--- a/src/components/sections/hero/HeroVideoExpand.tsx
+++ b/src/components/sections/hero/HeroVideoExpand.tsx
@@ -1,146 +0,0 @@
-import { useEffect, useRef, useState } from "react";
-import { AnimatePresence, motion, useScroll, useTransform } from "motion/react";
-import ImageOrVideo from "@/components/ui/ImageOrVideo";
-import AutoFillText from "@/components/ui/AutoFillText";
-import { useButtonClick } from "@/hooks/useButtonClick";
-
-const StaggerText = ({ text }: { text: string }) => (
-
- {[...text].map((char, index) => (
-
- {char}
-
- ))}
-
-);
-
-type HeroVideoExpandProps = {
- title: string;
- primaryButton: { text: string; href: string };
- secondaryButton: { text: string; href: string };
- onComplete?: () => void;
-} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
-
-const HeroVideoExpand = ({
- title,
- videoSrc,
- imageSrc,
- primaryButton,
- secondaryButton,
- onComplete,
-}: HeroVideoExpandProps) => {
- const [showLoader, setShowLoader] = useState(true);
- const [expanded, setExpanded] = useState(false);
- const handlePrimaryClick = useButtonClick(primaryButton.href);
- const handleSecondaryClick = useButtonClick(secondaryButton.href);
-
- const sectionRef = useRef(null);
- const { scrollYProgress } = useScroll({
- target: sectionRef,
- offset: ["start start", "end start"],
- });
- const videoY = useTransform(scrollYProgress, [0, 1], ["0px", "150px"]);
- const videoScale = useTransform(scrollYProgress, [0, 1], [1, 1.1]);
-
- useEffect(() => {
- const expandTimer = setTimeout(() => setExpanded(true), 600);
- const hideTimer = setTimeout(() => {
- setShowLoader(false);
- onComplete?.();
- }, 1500);
- return () => {
- clearTimeout(expandTimer);
- clearTimeout(hideTimer);
- };
- }, []);
-
- return (
- <>
-
- {showLoader && (
-
-
-
-
-
- )}
-
-
-
- >
- );
-};
-
-export default HeroVideoExpand;
diff --git a/src/components/sections/metrics/MetricsGradientCards.tsx b/src/components/sections/metrics/MetricsGradientCards.tsx
index ee189cd..e69de29 100644
--- a/src/components/sections/metrics/MetricsGradientCards.tsx
+++ b/src/components/sections/metrics/MetricsGradientCards.tsx
@@ -1,92 +0,0 @@
-import type { LucideIcon } from "lucide-react";
-import Button from "@/components/ui/Button";
-import TextAnimation from "@/components/ui/TextAnimation";
-import GridOrCarousel from "@/components/ui/GridOrCarousel";
-import ScrollReveal from "@/components/ui/ScrollReveal";
-import { resolveIcon } from "@/utils/resolve-icon";
-
-type Metric = {
- value: string;
- title: string;
- description: string;
- icon: string | LucideIcon;
-};
-
-const MetricsGradientCards = ({
- tag,
- title,
- description,
- primaryButton,
- secondaryButton,
- metrics,
-}: {
- tag: string;
- title: string;
- description: string;
- primaryButton?: { text: string; href: string };
- secondaryButton?: { text: string; href: string };
- metrics: Metric[];
-}) => (
-
-
-
-
-
-
-
-
-
- {(primaryButton || secondaryButton) && (
-
- {primaryButton && }
- {secondaryButton && }
-
- )}
-
-
-
-
- {metrics.map((metric) => {
- const IconComponent = resolveIcon(metric.icon);
- return (
-
-
- {metric.value}
-
-
{metric.title}
-
{metric.description}
-
-
-
-
- );
- })}
-
-
-
-
-);
-
-export default MetricsGradientCards;
diff --git a/src/components/sections/metrics/MetricsSimpleCards.tsx b/src/components/sections/metrics/MetricsSimpleCards.tsx
index e0b75bd..e69de29 100644
--- a/src/components/sections/metrics/MetricsSimpleCards.tsx
+++ b/src/components/sections/metrics/MetricsSimpleCards.tsx
@@ -1,71 +0,0 @@
-import Button from "@/components/ui/Button";
-import TextAnimation from "@/components/ui/TextAnimation";
-import GridOrCarousel from "@/components/ui/GridOrCarousel";
-import ScrollReveal from "@/components/ui/ScrollReveal";
-
-type Metric = {
- value: string;
- description: string;
-};
-
-const MetricsSimpleCards = ({
- tag,
- title,
- description,
- primaryButton,
- secondaryButton,
- metrics,
-}: {
- tag: string;
- title: string;
- description: string;
- primaryButton?: { text: string; href: string };
- secondaryButton?: { text: string; href: string };
- metrics: Metric[];
-}) => (
-
-
-
-
-
-
-
-
-
- {(primaryButton || secondaryButton) && (
-
- {primaryButton && }
- {secondaryButton && }
-
- )}
-
-
-
-
- {metrics.map((metric) => (
-
-
{metric.value}
-
{metric.description}
-
- ))}
-
-
-
-
-);
-
-export default MetricsSimpleCards;
diff --git a/src/components/sections/product/ProductMediaCards.tsx b/src/components/sections/product/ProductMediaCards.tsx
index 2131b7b..e69de29 100644
--- a/src/components/sections/product/ProductMediaCards.tsx
+++ b/src/components/sections/product/ProductMediaCards.tsx
@@ -1,119 +0,0 @@
-import { ArrowUpRight, Loader2 } from "lucide-react";
-import Button from "@/components/ui/Button";
-import TextAnimation from "@/components/ui/TextAnimation";
-import ImageOrVideo from "@/components/ui/ImageOrVideo";
-import GridOrCarousel from "@/components/ui/GridOrCarousel";
-import ScrollReveal from "@/components/ui/ScrollReveal";
-import useProducts from "@/hooks/useProducts";
-
-type ProductMediaCardsProps = {
- tag: string;
- title: string;
- description: string;
- primaryButton?: { text: string; href: string };
- secondaryButton?: { text: string; href: string };
- products?: {
- name: string;
- price: string;
- imageSrc: string;
- onClick?: () => void;
- }[];
-};
-
-const ProductMediaCards = ({
- tag,
- title,
- description,
- primaryButton,
- secondaryButton,
- products: productsProp,
-}: ProductMediaCardsProps) => {
- const { products: fetchedProducts, isLoading } = useProducts();
- const isFromApi = fetchedProducts.length > 0;
- const products = isFromApi
- ? fetchedProducts.map((p) => ({
- name: p.name,
- price: p.price,
- imageSrc: p.imageSrc,
- onClick: p.onProductClick,
- }))
- : productsProp;
-
- if (isLoading && !productsProp) {
- return (
-
- );
- }
-
- if (!products || products.length === 0) {
- return null;
- }
-
- return (
-
-
-
-
-
-
-
-
-
- {(primaryButton || secondaryButton) && (
-
- {primaryButton && }
- {secondaryButton && }
-
- )}
-
-
-
-
- {products.map((product) => (
-
- ))}
-
-
-
-
- );
-};
-
-export default ProductMediaCards;
diff --git a/src/components/sections/team/TeamGlassCards.tsx b/src/components/sections/team/TeamGlassCards.tsx
index 1245599..e69de29 100644
--- a/src/components/sections/team/TeamGlassCards.tsx
+++ b/src/components/sections/team/TeamGlassCards.tsx
@@ -1,82 +0,0 @@
-import Button from "@/components/ui/Button";
-import TextAnimation from "@/components/ui/TextAnimation";
-import ImageOrVideo from "@/components/ui/ImageOrVideo";
-import GridOrCarousel from "@/components/ui/GridOrCarousel";
-import ScrollReveal from "@/components/ui/ScrollReveal";
-
-type TeamMember = {
- name: string;
- role: string;
-} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
-
-const TeamGlassCards = ({
- tag,
- title,
- description,
- primaryButton,
- secondaryButton,
- members,
-}: {
- tag: string;
- title: string;
- description: string;
- primaryButton?: { text: string; href: string };
- secondaryButton?: { text: string; href: string };
- members: TeamMember[];
-}) => (
-
-
-
-
-
-
-
-
-
- {(primaryButton || secondaryButton) && (
-
- {primaryButton && }
- {secondaryButton && }
-
- )}
-
-
-
-
- {members.map((member) => (
-
-
-
-
-
-
- {member.name}
- {member.role}
-
-
- ))}
-
-
-
-
-);
-
-export default TeamGlassCards;
diff --git a/src/components/ui/ButtonMagnetic.tsx b/src/components/ui/ButtonMagnetic.tsx
index 9d92e43..e69de29 100644
--- a/src/components/ui/ButtonMagnetic.tsx
+++ b/src/components/ui/ButtonMagnetic.tsx
@@ -1,69 +0,0 @@
-"use client";
-
-import { useRef } from "react";
-import { motion, useMotionValue, useSpring } from "motion/react";
-import { useButtonClick } from "@/hooks/useButtonClick";
-import { cls } from "@/lib/utils";
-
-interface ButtonMagneticProps {
- text: string;
- variant?: "primary" | "secondary";
- href?: string;
- onClick?: () => void;
- animate?: boolean;
- animationDelay?: number;
- className?: string;
-}
-
-const ButtonMagnetic = ({ text, variant = "primary", href = "#", onClick, animate = true, animationDelay = 0, className = "" }: ButtonMagneticProps) => {
- const handleClick = useButtonClick(href, onClick);
- const ref = useRef(null);
-
- const x = useMotionValue(0);
- const y = useMotionValue(0);
- const springX = useSpring(x, { stiffness: 150, damping: 15 });
- const springY = useSpring(y, { stiffness: 150, damping: 15 });
-
- const handleMouseMove = (e: React.MouseEvent) => {
- if (!ref.current || window.innerWidth < 768) return;
- const rect = ref.current.getBoundingClientRect();
- const offsetX = (e.clientX - rect.left - rect.width / 2) * 0.15;
- const offsetY = (e.clientY - rect.top - rect.height / 2) * 0.15;
- x.set(offsetX);
- y.set(offsetY);
- };
-
- const handleMouseLeave = () => {
- x.set(0);
- y.set(0);
- };
-
- const button = (
-
- {text}
-
- );
-
- if (!animate) return button;
-
- return (
-
- {button}
-
- );
-};
-
-export default ButtonMagnetic;
diff --git a/src/components/ui/HorizonGlowBackground.tsx b/src/components/ui/HorizonGlowBackground.tsx
index 126b82b..e69de29 100644
--- a/src/components/ui/HorizonGlowBackground.tsx
+++ b/src/components/ui/HorizonGlowBackground.tsx
@@ -1,19 +0,0 @@
-import { cls } from "@/lib/utils";
-
-type HorizonGlowBackgroundProps = {
- position: "fixed" | "absolute";
-};
-
-const HorizonGlowBackground = ({ position }: HorizonGlowBackgroundProps) => {
- return (
-
- );
-};
-
-export default HorizonGlowBackground;
diff --git a/src/components/ui/NavbarDropdown.tsx b/src/components/ui/NavbarDropdown.tsx
index ffaf58b..e69de29 100644
--- a/src/components/ui/NavbarDropdown.tsx
+++ b/src/components/ui/NavbarDropdown.tsx
@@ -1,109 +0,0 @@
-import { useState, useEffect, useRef } from "react";
-import { motion, AnimatePresence } from "motion/react";
-import { ArrowUpRight } from "lucide-react";
-import { cls } from "@/lib/utils";
-import Button from "@/components/ui/Button";
-
-interface NavbarDropdownProps {
- logo: string;
- navItems: { name: string; href: string }[];
- ctaButton: { text: string; href: string };
-}
-
-const handleNavClick = (e: React.MouseEvent, href: string, onClose?: () => void) => {
- if (href.startsWith("#")) {
- e.preventDefault();
- const element = document.getElementById(href.slice(1));
- element?.scrollIntoView({ behavior: "smooth", block: "start" });
- }
- onClose?.();
-};
-
-const NavbarDropdown = ({ logo, navItems, ctaButton }: NavbarDropdownProps) => {
- const [menuOpen, setMenuOpen] = useState(false);
- const navRef = useRef(null);
-
- useEffect(() => {
- const handleKeyDown = (e: KeyboardEvent) => {
- if (e.key === "Escape" && menuOpen) setMenuOpen(false);
- };
- const handleClickOutside = (e: MouseEvent) => {
- if (menuOpen && navRef.current && !navRef.current.contains(e.target as Node)) {
- setMenuOpen(false);
- }
- };
- document.addEventListener("keydown", handleKeyDown);
- document.addEventListener("mousedown", handleClickOutside);
- return () => {
- document.removeEventListener("keydown", handleKeyDown);
- document.removeEventListener("mousedown", handleClickOutside);
- };
- }, [menuOpen]);
-
- return (
-
- );
-};
-
-export default NavbarDropdown;
diff --git a/src/components/ui/SectionErrorBoundary.tsx b/src/components/ui/SectionErrorBoundary.tsx
index 88d7f84..e69de29 100644
--- a/src/components/ui/SectionErrorBoundary.tsx
+++ b/src/components/ui/SectionErrorBoundary.tsx
@@ -1,90 +0,0 @@
-import { Component, type ErrorInfo, type ReactNode } from "react";
-
-/**
- * Per-section error boundary inserted around every assembled section by the
- * backend's page-assembler. Goal: a single section that throws at runtime
- * (missing required prop, broken `.map`, etc.) shows a small placeholder
- * instead of taking down the entire page with a white screen.
- *
- * Also reports the failure via the `/__webild/render-status` probe channel
- * so Bob-AI's post-commit poll picks up the section name + error message and
- * the model gets the signal to fix the right section on the next loop turn.
- *
- * The probe POST is best-effort and silent — sandbox-only (gated by
- * `window.parent !== window`), so production deploys never call it.
- */
-
-interface Props {
- /** Section slug — same value the wrapping `` uses. */
- name: string;
- children: ReactNode;
-}
-
-interface State {
- hasError: boolean;
- errorMessage?: string;
-}
-
-const RENDER_STATUS_URL = "/__webild/render-status";
-
-export default class SectionErrorBoundary extends Component
{
- state: State = { hasError: false };
-
- static getDerivedStateFromError(error: unknown): State {
- return {
- hasError: true,
- errorMessage:
- error instanceof Error ? error.message : String(error ?? "unknown"),
- };
- }
-
- componentDidCatch(error: Error, info: ErrorInfo) {
- if (typeof window === "undefined") return;
- if (window.parent === window) return;
- try {
- fetch(RENDER_STATUS_URL, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- ok: false,
- reason: "section_error_boundary",
- section: this.props.name,
- error: String(error?.message || error || "unknown"),
- stack: String(error?.stack || "").slice(0, 4000),
- componentStack: String(info?.componentStack || "").slice(0, 4000),
- t: Date.now(),
- }),
- keepalive: true,
- }).catch(() => {});
- } catch {
- /* ignore */
- }
- }
-
- render() {
- if (this.state.hasError) {
- return (
-
-
- This section failed to render.
-
-
- Section: {this.props.name}
-
- {this.state.errorMessage ? (
-
- {this.state.errorMessage}
-
- ) : null}
-
- Tell Bob exactly what's wrong (e.g. "fix the {this.props.name} section") to retry.
-
-
- );
- }
- return this.props.children;
- }
-}
diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx
index 7b6181d..8dba7c8 100644
--- a/src/pages/HomePage.tsx
+++ b/src/pages/HomePage.tsx
@@ -16,7 +16,7 @@ export default function HomePage() {