feat: update cube component color scheme from cyan to blue
- Changed cube gradient colors from cyan to blue for better visual consistency - Updated glow effects and shadows to use blue (rgba(59, 130, 246)) instead of cyan - Modified background aura gradients in StackSection for enhanced depth perception - Replaced HomeFeaturesDark component with new HomeSlider in HomePage layout - Added isolate property to StackSection to prevent gradient bleeding - Enhanced background layer in StackSection with additional
This commit is contained in:
		@@ -26,11 +26,11 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({
 | 
			
		||||
    <path
 | 
			
		||||
      fill={`url(#cube-gradient-${index})`}
 | 
			
		||||
      d="M491.651 144.747L287.198 227.339C265.219 236.22 241.783 236.22 219.802 227.339L15.3486 144.747C-5.11621 136.479 -5.11621 97.5191 15.3486 89.2539L219.802 6.65884C241.783 -2.21961 265.219 -2.21961 287.198 6.65884L491.651 89.2539C512.116 97.5191 512.116 136.479 491.651 144.747Z"
 | 
			
		||||
      stroke="rgba(0,255,255,0.25)"
 | 
			
		||||
      stroke="rgba(59, 130, 246, 0.25)"
 | 
			
		||||
      strokeWidth="0.5"
 | 
			
		||||
    />
 | 
			
		||||
    <defs>
 | 
			
		||||
      {/* Cyan-white soft gradient */}
 | 
			
		||||
      {/* Blue-white soft gradient */}
 | 
			
		||||
      <linearGradient
 | 
			
		||||
        id={`cube-gradient-${index}`}
 | 
			
		||||
        x1="185.298"
 | 
			
		||||
@@ -39,8 +39,8 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({
 | 
			
		||||
        y2="206.448"
 | 
			
		||||
        gradientUnits="userSpaceOnUse"
 | 
			
		||||
      >
 | 
			
		||||
        <stop offset="0%" stopColor="#DFFFFF" stopOpacity="0.75" />
 | 
			
		||||
        <stop offset="40%" stopColor="#A5F4FF" stopOpacity="0.8" />
 | 
			
		||||
        <stop offset="0%" stopColor="#EBF8FF" stopOpacity="0.75" />
 | 
			
		||||
        <stop offset="40%" stopColor="#BEE3F8" stopOpacity="0.8" />
 | 
			
		||||
        <stop offset="100%" stopColor="#FFFFFF" stopOpacity="0.9" />
 | 
			
		||||
      </linearGradient>
 | 
			
		||||
    </defs>
 | 
			
		||||
@@ -79,8 +79,8 @@ export function CubeLight({
 | 
			
		||||
        <div
 | 
			
		||||
          className={`absolute inset-0 blur-3xl rounded-2xl transition-all duration-500 ${
 | 
			
		||||
            isActive
 | 
			
		||||
              ? "bg-cyan-400/40 opacity-70"
 | 
			
		||||
              : "bg-cyan-200/20 opacity-40"
 | 
			
		||||
              ? "bg-blue-400/40 opacity-70"
 | 
			
		||||
              : "bg-blue-200/20 opacity-40"
 | 
			
		||||
          }`}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
@@ -90,8 +90,8 @@ export function CubeLight({
 | 
			
		||||
          className="w-48 sm:w-64 lg:w-80 h-auto relative"
 | 
			
		||||
          style={{
 | 
			
		||||
            filter: isActive
 | 
			
		||||
              ? "drop-shadow(0 0 25px rgba(0,255,255,0.4)) brightness(1.1)"
 | 
			
		||||
              : "drop-shadow(0 0 10px rgba(0,255,255,0.15)) brightness(1)",
 | 
			
		||||
              ? "drop-shadow(0 0 25px rgba(59, 130, 246, 0.4)) brightness(1.1)"
 | 
			
		||||
              : "drop-shadow(0 0 10px rgba(59, 130, 246, 0.15)) brightness(1)",
 | 
			
		||||
            transition: "all 0.4s ease",
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
@@ -99,10 +99,10 @@ export function CubeLight({
 | 
			
		||||
        {/* Title overlay */}
 | 
			
		||||
        <div className="absolute inset-0 flex items-center justify-center">
 | 
			
		||||
          <h3
 | 
			
		||||
            className="text-cyan-900 text-sm lg:text-base font-medium text-center px-4"
 | 
			
		||||
            className="text-blue-900 text-sm lg:text-base font-medium text-center px-4"
 | 
			
		||||
            style={{
 | 
			
		||||
              textShadow:
 | 
			
		||||
                "0 0 15px rgba(255,255,255,0.8), 0 0 25px rgba(0,255,255,0.5)",
 | 
			
		||||
                "0 0 15px rgba(255,255,255,0.8), 0 0 25px rgba(59, 130, 246, 0.5)",
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            {title}
 | 
			
		||||
@@ -131,7 +131,7 @@ export function CubeLight({
 | 
			
		||||
                y1="1"
 | 
			
		||||
                x2="120"
 | 
			
		||||
                y2="1"
 | 
			
		||||
                stroke="rgba(0,255,255,0.6)"
 | 
			
		||||
                stroke="rgba(59, 130, 246, 0.6)"
 | 
			
		||||
                strokeWidth="1"
 | 
			
		||||
                opacity="0.8"
 | 
			
		||||
              />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										293
									
								
								src/components/ui/apple-cards-carousel.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								src/components/ui/apple-cards-carousel.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,293 @@
 | 
			
		||||
"use client";
 | 
			
		||||
import React, {
 | 
			
		||||
  useEffect,
 | 
			
		||||
  useRef,
 | 
			
		||||
  useState,
 | 
			
		||||
  createContext,
 | 
			
		||||
  useContext,
 | 
			
		||||
} from "react";
 | 
			
		||||
import {
 | 
			
		||||
  IconArrowNarrowLeft,
 | 
			
		||||
  IconArrowNarrowRight,
 | 
			
		||||
  IconX,
 | 
			
		||||
} from "@tabler/icons-react";
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
import { AnimatePresence, motion } from "motion/react";
 | 
			
		||||
import { useOutsideClick } from "@/hooks/use-outside-click";
 | 
			
		||||
 | 
			
		||||
interface CarouselProps {
 | 
			
		||||
  items: JSX.Element[];
 | 
			
		||||
  initialScroll?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Card = {
 | 
			
		||||
  src: string;
 | 
			
		||||
  title: string;
 | 
			
		||||
  category: string;
 | 
			
		||||
  content: React.ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const CarouselContext = createContext<{
 | 
			
		||||
  onCardClose: (index: number) => void;
 | 
			
		||||
  currentIndex: number;
 | 
			
		||||
}>({
 | 
			
		||||
  onCardClose: () => {},
 | 
			
		||||
  currentIndex: 0,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const Carousel = ({ items, initialScroll = 0 }: CarouselProps) => {
 | 
			
		||||
  const carouselRef = React.useRef<HTMLDivElement>(null);
 | 
			
		||||
  const [canScrollLeft, setCanScrollLeft] = React.useState(false);
 | 
			
		||||
  const [canScrollRight, setCanScrollRight] = React.useState(true);
 | 
			
		||||
  const [currentIndex, setCurrentIndex] = useState(0);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (carouselRef.current) {
 | 
			
		||||
      carouselRef.current.scrollLeft = initialScroll;
 | 
			
		||||
      checkScrollability();
 | 
			
		||||
    }
 | 
			
		||||
  }, [initialScroll]);
 | 
			
		||||
 | 
			
		||||
  const checkScrollability = () => {
 | 
			
		||||
    if (carouselRef.current) {
 | 
			
		||||
      const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current;
 | 
			
		||||
      setCanScrollLeft(scrollLeft > 0);
 | 
			
		||||
      setCanScrollRight(scrollLeft < scrollWidth - clientWidth);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const scrollLeft = () => {
 | 
			
		||||
    if (carouselRef.current) {
 | 
			
		||||
      carouselRef.current.scrollBy({ left: -300, behavior: "smooth" });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const scrollRight = () => {
 | 
			
		||||
    if (carouselRef.current) {
 | 
			
		||||
      carouselRef.current.scrollBy({ left: 300, behavior: "smooth" });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleCardClose = (index: number) => {
 | 
			
		||||
    if (carouselRef.current) {
 | 
			
		||||
      const cardWidth = isMobile() ? 230 : 384; // (md:w-96)
 | 
			
		||||
      const gap = isMobile() ? 4 : 8;
 | 
			
		||||
      const scrollPosition = (cardWidth + gap) * (index + 1);
 | 
			
		||||
      carouselRef.current.scrollTo({
 | 
			
		||||
        left: scrollPosition,
 | 
			
		||||
        behavior: "smooth",
 | 
			
		||||
      });
 | 
			
		||||
      setCurrentIndex(index);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const isMobile = () => {
 | 
			
		||||
    return window && window.innerWidth < 768;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <CarouselContext.Provider
 | 
			
		||||
      value={{ onCardClose: handleCardClose, currentIndex }}
 | 
			
		||||
    >
 | 
			
		||||
      <div className="relative w-full">
 | 
			
		||||
        <div
 | 
			
		||||
          className="flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-10 [scrollbar-width:none] md:py-20"
 | 
			
		||||
          ref={carouselRef}
 | 
			
		||||
          onScroll={checkScrollability}
 | 
			
		||||
        >
 | 
			
		||||
          <div
 | 
			
		||||
            className={cn(
 | 
			
		||||
              "absolute right-0 z-[1000] h-auto w-[5%] overflow-hidden bg-gradient-to-l",
 | 
			
		||||
            )}
 | 
			
		||||
          ></div>
 | 
			
		||||
 | 
			
		||||
          <div
 | 
			
		||||
            className={cn(
 | 
			
		||||
              "flex flex-row justify-start gap-4 pl-4",
 | 
			
		||||
              "mx-auto max-w-7xl", // remove max-w-4xl if you want the carousel to span the full width of its container
 | 
			
		||||
            )}
 | 
			
		||||
          >
 | 
			
		||||
            {items.map((item, index) => (
 | 
			
		||||
              <motion.div
 | 
			
		||||
                initial={{
 | 
			
		||||
                  opacity: 0,
 | 
			
		||||
                  y: 20,
 | 
			
		||||
                }}
 | 
			
		||||
                animate={{
 | 
			
		||||
                  opacity: 1,
 | 
			
		||||
                  y: 0,
 | 
			
		||||
                  transition: {
 | 
			
		||||
                    duration: 0.5,
 | 
			
		||||
                    delay: 0.2 * index,
 | 
			
		||||
                    ease: "easeOut",
 | 
			
		||||
                  },
 | 
			
		||||
                }}
 | 
			
		||||
                key={"card" + index}
 | 
			
		||||
                className="rounded-3xl last:pr-[5%] md:last:pr-[33%]"
 | 
			
		||||
              >
 | 
			
		||||
                {item}
 | 
			
		||||
              </motion.div>
 | 
			
		||||
            ))}
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="mr-10 flex justify-end gap-2">
 | 
			
		||||
          <button
 | 
			
		||||
            className="relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-neutral-800 disabled:opacity-50"
 | 
			
		||||
            onClick={scrollLeft}
 | 
			
		||||
            disabled={!canScrollLeft}
 | 
			
		||||
          >
 | 
			
		||||
            <IconArrowNarrowLeft className="h-6 w-6 text-white" />
 | 
			
		||||
          </button>
 | 
			
		||||
          <button
 | 
			
		||||
            className="relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-neutral-800 disabled:opacity-50"
 | 
			
		||||
            onClick={scrollRight}
 | 
			
		||||
            disabled={!canScrollRight}
 | 
			
		||||
          >
 | 
			
		||||
            <IconArrowNarrowRight className="h-6 w-6 text-white" />
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </CarouselContext.Provider>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const Card = ({
 | 
			
		||||
  card,
 | 
			
		||||
  index,
 | 
			
		||||
  layout = false,
 | 
			
		||||
}: {
 | 
			
		||||
  card: Card;
 | 
			
		||||
  index: number;
 | 
			
		||||
  layout?: boolean;
 | 
			
		||||
}) => {
 | 
			
		||||
  const [open, setOpen] = useState(false);
 | 
			
		||||
  const containerRef = useRef<HTMLDivElement>(null);
 | 
			
		||||
  const { onCardClose } = useContext(CarouselContext);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    function onKeyDown(event: KeyboardEvent) {
 | 
			
		||||
      if (event.key === "Escape") {
 | 
			
		||||
        handleClose();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (open) {
 | 
			
		||||
      document.body.style.overflow = "hidden";
 | 
			
		||||
    } else {
 | 
			
		||||
      document.body.style.overflow = "auto";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    window.addEventListener("keydown", onKeyDown);
 | 
			
		||||
    return () => window.removeEventListener("keydown", onKeyDown);
 | 
			
		||||
  }, [open]);
 | 
			
		||||
 | 
			
		||||
  useOutsideClick(containerRef, () => handleClose());
 | 
			
		||||
 | 
			
		||||
  const handleOpen = () => {
 | 
			
		||||
    setOpen(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleClose = () => {
 | 
			
		||||
    setOpen(false);
 | 
			
		||||
    onCardClose(index);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <AnimatePresence>
 | 
			
		||||
        {open && (
 | 
			
		||||
          <div className="fixed inset-0 z-50 h-screen overflow-auto">
 | 
			
		||||
            <motion.div
 | 
			
		||||
              initial={{ opacity: 0 }}
 | 
			
		||||
              animate={{ opacity: 1 }}
 | 
			
		||||
              exit={{ opacity: 0 }}
 | 
			
		||||
              className="fixed inset-0 h-full w-full bg-black/80 backdrop-blur-lg"
 | 
			
		||||
            />
 | 
			
		||||
            <motion.div
 | 
			
		||||
              initial={{ opacity: 0 }}
 | 
			
		||||
              animate={{ opacity: 1 }}
 | 
			
		||||
              exit={{ opacity: 0 }}
 | 
			
		||||
              ref={containerRef}
 | 
			
		||||
              layoutId={layout ? `card-${card.title}` : undefined}
 | 
			
		||||
              className="relative z-[60] mx-auto my-10 h-fit max-w-5xl rounded-3xl bg-neutral-900 p-4 font-sans md:p-10"
 | 
			
		||||
            >
 | 
			
		||||
              <button
 | 
			
		||||
                className="sticky top-4 right-0 ml-auto flex h-8 w-8 items-center justify-center rounded-full bg-black dark:bg-white"
 | 
			
		||||
                onClick={handleClose}
 | 
			
		||||
              >
 | 
			
		||||
                <IconX className="h-6 w-6 text-neutral-100 dark:text-neutral-900" />
 | 
			
		||||
              </button>
 | 
			
		||||
              <motion.p
 | 
			
		||||
                layoutId={layout ? `category-${card.title}` : undefined}
 | 
			
		||||
                className="text-base font-medium text-black dark:text-white"
 | 
			
		||||
              >
 | 
			
		||||
                {card.category}
 | 
			
		||||
              </motion.p>
 | 
			
		||||
              <motion.p
 | 
			
		||||
                layoutId={layout ? `title-${card.title}` : undefined}
 | 
			
		||||
                className="mt-4 text-2xl font-semibold text-neutral-700 md:text-5xl dark:text-white"
 | 
			
		||||
              >
 | 
			
		||||
                {card.title}
 | 
			
		||||
              </motion.p>
 | 
			
		||||
              <div className="py-10">{card.content}</div>
 | 
			
		||||
            </motion.div>
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
      </AnimatePresence>
 | 
			
		||||
      <motion.button
 | 
			
		||||
        layoutId={layout ? `card-${card.title}` : undefined}
 | 
			
		||||
        onClick={handleOpen}
 | 
			
		||||
        className="relative z-10 flex h-60 w-56 flex-col items-start justify-start overflow-hidden rounded-3xl bg-neutral-900 md:h-120 md:w-96"
 | 
			
		||||
      >
 | 
			
		||||
        <div className="pointer-events-none absolute inset-x-0 top-0 z-30 h-full bg-gradient-to-b from-black/50 via-transparent to-transparent" />
 | 
			
		||||
        <div className="relative z-40 p-8">
 | 
			
		||||
          <motion.p
 | 
			
		||||
            layoutId={layout ? `category-${card.category}` : undefined}
 | 
			
		||||
            className="text-left font-sans text-sm font-medium text-white md:text-base"
 | 
			
		||||
          >
 | 
			
		||||
            {card.category}
 | 
			
		||||
          </motion.p>
 | 
			
		||||
          <motion.p
 | 
			
		||||
            layoutId={layout ? `title-${card.title}` : undefined}
 | 
			
		||||
            className="mt-2 max-w-xs text-left font-sans text-xl font-semibold [text-wrap:balance] text-white md:text-3xl"
 | 
			
		||||
          >
 | 
			
		||||
            {card.title}
 | 
			
		||||
          </motion.p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <BlurImage
 | 
			
		||||
          src={card.src}
 | 
			
		||||
          alt={card.title}
 | 
			
		||||
          className="absolute inset-0 z-10 h-full w-full object-cover"
 | 
			
		||||
        />
 | 
			
		||||
      </motion.button>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const BlurImage = ({
 | 
			
		||||
  src,
 | 
			
		||||
  className,
 | 
			
		||||
  width,
 | 
			
		||||
  height,
 | 
			
		||||
  alt,
 | 
			
		||||
  ...rest
 | 
			
		||||
}: React.ImgHTMLAttributes<HTMLImageElement>) => {
 | 
			
		||||
  const [isLoading, setLoading] = useState(true);
 | 
			
		||||
  return (
 | 
			
		||||
    <img
 | 
			
		||||
      className={cn(
 | 
			
		||||
        "h-full w-full transition duration-300",
 | 
			
		||||
        isLoading ? "blur-sm" : "blur-0",
 | 
			
		||||
        className,
 | 
			
		||||
      )}
 | 
			
		||||
      onLoad={() => setLoading(false)}
 | 
			
		||||
      src={src as string}
 | 
			
		||||
      width={width}
 | 
			
		||||
      height={height}
 | 
			
		||||
      loading="lazy"
 | 
			
		||||
      decoding="async"
 | 
			
		||||
      alt={alt ? alt : "Background of a beautiful view"}
 | 
			
		||||
      {...rest}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										42
									
								
								src/components/ui/son-of-a-glitch-text.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/components/ui/son-of-a-glitch-text.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
"use client";
 | 
			
		||||
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
import { type HTMLAttributes, useEffect, useState } from "react";
 | 
			
		||||
 | 
			
		||||
interface SonOfAGlitchProps extends HTMLAttributes<HTMLHeadingElement> {
 | 
			
		||||
  text: string;
 | 
			
		||||
  textClassName?: string;
 | 
			
		||||
  glitchClassName?: string;
 | 
			
		||||
  showGlitch?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const SonOfAGlitch = ({
 | 
			
		||||
  text,
 | 
			
		||||
  className,
 | 
			
		||||
  textClassName,
 | 
			
		||||
  glitchClassName,
 | 
			
		||||
  showGlitch = true,
 | 
			
		||||
}: SonOfAGlitchProps) => {
 | 
			
		||||
  const [isGlitching, setIsGlitching] = useState(showGlitch);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setIsGlitching(showGlitch);
 | 
			
		||||
  }, [showGlitch]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <h1 className={cn("relative font-mono", className)}>
 | 
			
		||||
      <span
 | 
			
		||||
        className={cn(
 | 
			
		||||
          "absolute top-0 left-0 w-full h-full bg-transparent",
 | 
			
		||||
          isGlitching &&
 | 
			
		||||
            "before:content-[attr(data-text)] before:absolute before:top-0 before:w-full before:h-full before:bg-transparent before:left-[-2px] before:text-red-500 before:overflow-hidden before:animate-glitch-1",
 | 
			
		||||
          isGlitching &&
 | 
			
		||||
            "after:content-[attr(data-text)] after:absolute after:top-0 after:w-full after:h-full after:bg-transparent after:left-[2px] after:text-blue-500 after:overflow-hidden after:animate-glitch-2",
 | 
			
		||||
          glitchClassName,
 | 
			
		||||
        )}
 | 
			
		||||
        data-text={text}
 | 
			
		||||
      ></span>
 | 
			
		||||
      <span className={cn(textClassName)}>{text}</span>
 | 
			
		||||
    </h1>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										23
									
								
								src/hooks/use-outside-click.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/hooks/use-outside-click.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
import React, { useEffect } from "react";
 | 
			
		||||
 | 
			
		||||
export const useOutsideClick = (
 | 
			
		||||
  ref: React.RefObject<HTMLDivElement>,
 | 
			
		||||
  callback: Function
 | 
			
		||||
) => {
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const listener = (event: any) => {
 | 
			
		||||
      if (!ref.current || ref.current.contains(event.target)) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      callback(event);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    document.addEventListener("mousedown", listener);
 | 
			
		||||
    document.addEventListener("touchstart", listener);
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      document.removeEventListener("mousedown", listener);
 | 
			
		||||
      document.removeEventListener("touchstart", listener);
 | 
			
		||||
    };
 | 
			
		||||
  }, [ref, callback]);
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										24
									
								
								src/hooks/use-outside-click.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/hooks/use-outside-click.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
import React, { useEffect } from "react";
 | 
			
		||||
 | 
			
		||||
export const useOutsideClick = (
 | 
			
		||||
  ref: React.RefObject<HTMLDivElement>,
 | 
			
		||||
  callback: Function
 | 
			
		||||
) => {
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const listener = (event: any) => {
 | 
			
		||||
      // DO NOTHING if the element being clicked is the target element or their children
 | 
			
		||||
      if (!ref.current || ref.current.contains(event.target)) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      callback(event);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    document.addEventListener("mousedown", listener);
 | 
			
		||||
    document.addEventListener("touchstart", listener);
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      document.removeEventListener("mousedown", listener);
 | 
			
		||||
      document.removeEventListener("touchstart", listener);
 | 
			
		||||
    };
 | 
			
		||||
  }, [ref, callback]);
 | 
			
		||||
};
 | 
			
		||||
@@ -5,6 +5,7 @@ import { StackSectionLight } from './StackSection'
 | 
			
		||||
import { WorldMap } from './HomeGlobe'
 | 
			
		||||
import { HomeBenefits } from './HomeBenefits'
 | 
			
		||||
import { CallToAction } from './CallToAction'
 | 
			
		||||
import { HomeSlider } from './HomeSlider'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default function HomePage() {
 | 
			
		||||
@@ -24,7 +25,7 @@ export default function HomePage() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      <AnimatedSection>
 | 
			
		||||
        <HomeFeaturesDark />
 | 
			
		||||
        <HomeSlider />
 | 
			
		||||
      </AnimatedSection>
 | 
			
		||||
 | 
			
		||||
      <AnimatedSection>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										84
									
								
								src/pages/home/HomeSlider.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/pages/home/HomeSlider.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
"use client";
 | 
			
		||||
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { Carousel, Card } from "@/components/ui/apple-cards-carousel";
 | 
			
		||||
import { H2, H3, P } from "@/components/Texts";
 | 
			
		||||
 | 
			
		||||
export function HomeSlider() {
 | 
			
		||||
  const cards = data.map((card, index) => (
 | 
			
		||||
    <Card key={card.src} card={card} index={index} />
 | 
			
		||||
  ));
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="w-full h-full py-20 bg-black">
 | 
			
		||||
      <div className="max-w-7xl mx-auto pl-4">
 | 
			
		||||
        <H3 className="text-left text-white">
 | 
			
		||||
          Discover the Mycelium Ecosystem
 | 
			
		||||
        </H3>
 | 
			
		||||
        <div className="mt-4 max-w-3xl">
 | 
			
		||||
          <P className="text-left text-neutral-400">
 | 
			
		||||
            From compute and networking to intelligent automation, these components work together to empower users, developers, and organizations to build freely, without intermediaries.
 | 
			
		||||
          </P>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <Carousel items={cards} />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DummyContent = () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="bg-neutral-800 p-8 md:p-14 rounded-3xl mb-4 max-w-3xl">
 | 
			
		||||
      <P className="text-neutral-400 text-base md:text-2xl font-sans  mx-auto">
 | 
			
		||||
        Mycelium is a decentralized compute network that allows you to run AI models and other software on a global network of devices. Mycelium is built on top of the Mycelium Network, a decentralized network of devices that are connected to each other and to the internet.
 | 
			
		||||
      </P>
 | 
			
		||||
      <img
 | 
			
		||||
        src="/images/gallery/1.webp"
 | 
			
		||||
        alt="Macbook mockup from Aceternity UI"
 | 
			
		||||
        height="500"
 | 
			
		||||
        width="500"
 | 
			
		||||
        className="md:w-1/2 md:h-1/2 h-full w-full mx-auto object-cover"
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const data = [
 | 
			
		||||
  {
 | 
			
		||||
    category: "DePIN",
 | 
			
		||||
    title: "Mycelium Network",
 | 
			
		||||
    src: "/images/gallery/9.webp",
 | 
			
		||||
    content: <DummyContent />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    category: "AI Agent",
 | 
			
		||||
    title: "Mycelium Agent",
 | 
			
		||||
    src: "/images/gallery/2.webp",
 | 
			
		||||
    content: <DummyContent />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    category: "Cloud",
 | 
			
		||||
    title: "Mycelium Cloud",
 | 
			
		||||
    src: "/images/gallery/3.webp",
 | 
			
		||||
    content: <DummyContent />,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    category: "GPU",
 | 
			
		||||
    title: "Mycelium GPU",
 | 
			
		||||
    src: "/images/gallery/4.webp",
 | 
			
		||||
    content: <DummyContent />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    category: "Compute",
 | 
			
		||||
    title: "Mycelium Compute",
 | 
			
		||||
    src: "/images/gallery/5.webp",
 | 
			
		||||
    content: <DummyContent />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    category: "Storage",
 | 
			
		||||
    title: "Mycelium Storage",
 | 
			
		||||
    src: "/images/gallery/6.webp",
 | 
			
		||||
    content: <DummyContent />,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
@@ -7,35 +7,53 @@ import { FadeIn } from "@/components/ui/FadeIn";
 | 
			
		||||
 | 
			
		||||
export function StackSectionLight() {
 | 
			
		||||
  return (
 | 
			
		||||
    <section className="relative w-full overflow-hidden py-24 lg:py-40">
 | 
			
		||||
    <section className="relative w-full overflow-hidden py-24 lg:py-40 isolate">
 | 
			
		||||
      {/* === Background Layer === */}
 | 
			
		||||
      <div className="absolute inset-0 -z-10 bg-transparent">
 | 
			
		||||
       
 | 
			
		||||
 | 
			
		||||
        {/* === Center Radial Glow Aura === */}
 | 
			
		||||
      <div className="absolute inset-0 z-0 bg-transparent">
 | 
			
		||||
        {/* Central main aura */}
 | 
			
		||||
        <motion.div
 | 
			
		||||
          className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[1200px] h-[1200px] rounded-full pointer-events-none"
 | 
			
		||||
          style={{
 | 
			
		||||
            background:
 | 
			
		||||
              "radial-gradient(circle, rgba(173,255,255,0.5) 0%, rgba(0,220,255,0.3) 30%, rgba(255,255,255,0) 70%)",
 | 
			
		||||
            filter: "blur(120px)",
 | 
			
		||||
              "radial-gradient(circle, rgba(180,255,255,0.55) 0%, rgba(0,210,255,0.35) 35%, rgba(255,255,255,0) 55%)",
 | 
			
		||||
            filter: "blur(140px)",
 | 
			
		||||
          }}
 | 
			
		||||
          animate={{
 | 
			
		||||
            opacity: [0.6, 0.8, 0.6],
 | 
			
		||||
            opacity: [0.5, 0.8, 0.5],
 | 
			
		||||
            scale: [1, 1.05, 1],
 | 
			
		||||
          }}
 | 
			
		||||
          transition={{
 | 
			
		||||
            duration: 8,
 | 
			
		||||
            duration: 9,
 | 
			
		||||
            repeat: Infinity,
 | 
			
		||||
            ease: "easeInOut",
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        {/* Faint cyan mist in the back for depth */}
 | 
			
		||||
        <motion.div
 | 
			
		||||
          className="absolute left-[70%] top-[30%] -translate-x-1/2 -translate-y-1/2 w-[1600px] h-[1600px] rounded-full pointer-events-none"
 | 
			
		||||
          style={{
 | 
			
		||||
            background:
 | 
			
		||||
              "radial-gradient(circle, rgba(100,220,255,0.25) 0%, rgba(200,255,255,0.15) 50%, rgba(255,255,255,0) 90%)",
 | 
			
		||||
            filter: "blur(200px)",
 | 
			
		||||
          }}
 | 
			
		||||
          animate={{
 | 
			
		||||
            opacity: [0.2, 0.35, 0.2],
 | 
			
		||||
            scale: [1, 1.1, 1],
 | 
			
		||||
          }}
 | 
			
		||||
          transition={{
 | 
			
		||||
            duration: 12,
 | 
			
		||||
            repeat: Infinity,
 | 
			
		||||
            ease: "easeInOut",
 | 
			
		||||
            delay: 3,
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      {/* === Content === */}
 | 
			
		||||
      <div className="relative mx-auto max-w-7xl px-6 lg:px-8 grid grid-cols-1 lg:grid-cols-3 gap-16 items-center">
 | 
			
		||||
        {/* Left Column - Text */}
 | 
			
		||||
        <div className="text-center lg:text-left">
 | 
			
		||||
        <div className="text-center lg:text-left z-10">
 | 
			
		||||
          <FadeIn>
 | 
			
		||||
            <Eyebrow color="accent">Technology Layers</Eyebrow>
 | 
			
		||||
            <SectionHeader color="dark" className="text-4xl sm:text-5xl font-semibold">
 | 
			
		||||
@@ -53,7 +71,7 @@ export function StackSectionLight() {
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        {/* Right Column - Animated Stack */}
 | 
			
		||||
        <div className="lg:col-span-2 flex items-center justify-center lg:justify-start relative">
 | 
			
		||||
        <div className="lg:col-span-2 flex items-center justify-center lg:justify-start relative z-10">
 | 
			
		||||
          <motion.div
 | 
			
		||||
            initial={{ y: 30, opacity: 0 }}
 | 
			
		||||
            whileInView={{ y: 0, opacity: 1 }}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user