This commit is contained in:
sasha-astiadi 2025-08-04 16:57:37 +02:00
parent 0eee1629c6
commit 045486365d
8 changed files with 277 additions and 10 deletions

View File

@ -10,10 +10,13 @@ import Benefits from '@/components/Benefits'
import Cta from '@/components/Cta' import Cta from '@/components/Cta'
import { GlobeDemo } from '@/components/GlobeDemo' import { GlobeDemo } from '@/components/GlobeDemo'
import { SpotlightPreview } from '@/components/Spotlight' import { SpotlightPreview } from '@/components/Spotlight'
import { StackSectionPreview } from '@/components/StackSection'
export default function Home() { export default function Home() {
return ( return (
<> <>
<SpotlightPreview /> <SpotlightPreview />
<StackSectionPreview />
</> </>
) )
} }

View File

@ -59,7 +59,7 @@ export function Header() {
return ( return (
<header> <header>
<nav> <nav>
<Container className="relative z-50 flex justify-between py-8"> <Container className="relative z-50 flex justify-between py-4">
<div className="relative z-10 flex items-center gap-16"> <div className="relative z-10 flex items-center gap-16">
<Link href="/" aria-label="Home"> <Link href="/" aria-label="Home">
<Logo className="h-10 w-auto" /> <Logo className="h-10 w-auto" />

View File

@ -1,10 +1,14 @@
"use client";
import React from "react"; import React from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Spotlight } from "@/components/ui/spotlight"; import { Spotlight } from "@/components/ui/spotlight";
import { Logomark } from "@/components/Logo";
import { Button } from "@/components/Button";
export function SpotlightPreview() { export function SpotlightPreview() {
return ( return (
<div className="relative flex h-[40rem] w-full overflow-hidden rounded-md bg-black/[0.96] antialiased md:items-center md:justify-center"> <div className="relative flex h-[40rem] w-full overflow-hidden rounded-md bg-transparent antialiased md:items-center md:justify-center">
<div <div
className={cn( className={cn(
"pointer-events-none absolute inset-0 [background-size:40px_40px] select-none", "pointer-events-none absolute inset-0 [background-size:40px_40px] select-none",
@ -17,15 +21,33 @@ export function SpotlightPreview() {
fill="white" fill="white"
/> />
<div className="relative z-10 mx-auto w-full max-w-7xl p-4 pt-20 md:pt-0"> <div className="relative z-10 mx-auto w-full max-w-7xl p-4 pt-20 md:pt-0">
<h1 className="bg-opacity-50 bg-gradient-to-b from-neutral-50 to-neutral-400 bg-clip-text text-center text-4xl font-bold text-transparent md:text-7xl"> <div className="flex justify-center mb-6">
Spotlight <br /> is the new trend. <div className="mb-4 relative rounded-full px-3 py-1 text-sm/6 text-gray-600 ring-1 ring-gray-900/10 hover:ring-gray-900/20">
Announcing The New TF Marketplace.{' '}
<a href="#" className="font-semibold text-white hover:text-gray-200">
<span aria-hidden="true" className="absolute inset-0" />
Read more <span aria-hidden="true">&rarr;</span>
</a>
</div>
</div>
<h1 className="bg-opacity-50 bg-gradient-to-b from-neutral-50 to-neutral-400 bg-clip-text tracking-tighter text-center text-4xl font-semibold text-transparent lg:text-6xl">
Built by Everyone <br /> for Everyone.
</h1> </h1>
<p className="mx-auto mt-4 max-w-lg text-center text-base font-normal text-neutral-300"> <p className="mx-auto mt-8 max-w-lg text-center text-base lg:text-xl font-light text-neutral-300">
Spotlight effect is a great way to draw attention to a specific part ThreeFold is a fully operational, decentralized internet infrastructure deployed locally, scalable globally, and owned and powered by the people.
of the page. Here, we are drawing the attention towards the text
section of the page. I don&apos;t know why but I&apos;m running out of
copy.
</p> </p>
<div className="mt-8 flex flex-col sm:flex-row justify-center gap-4">
<Button href="/login" variant="outline" color="gray">
Start Building
</Button>
<Button href="#" variant="outline" color="gray">
Start Hosting
</Button>
<Button href="#" variant="solid" color="white">
How it Works
</Button>
</div>
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,29 @@
"use client";
import { StackedCubes } from "@/components/ui/StackedCubes";
export function StackSectionPreview() {
return (
<section className="w-full bg-transparent px-4 py-8 sm:px-6 sm:pb-12 lg:px-8">
<div className="mx-auto max-w-7xl">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 lg:gap-16 items-center lg:items-start">
{/* Left Column - Text (1/3 width) */}
<div className="text-center lg:text-left lg:col-span-1 order-1 lg:order-1">
<h2 className="text-xl sm:text-2xl font-semibold tracking-tight leading-tight text-white lg:text-3xl">
A Decentralized Infrastructure Layer
</h2>
<p className="mt-4 sm:mt-6 text-sm font-light text-pretty text-gray-700 lg:text-base">
We have built a foundational platform that runs directly on bare metal, offering a scalable solution focused on the essential building blocks of the Internet and Cloud: compute, data, and network.
</p>
</div>
{/* Right Column - Stacked Cubes (2/3 width) */}
<div className="lg:col-span-2 flex items-center justify-center lg:justify-start order-2 lg:order-2">
<StackedCubes />
</div>
</div>
</div>
</section>
);
}

147
src/components/ui/Cube.tsx Normal file
View File

@ -0,0 +1,147 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
interface CubeProps {
title: string;
descriptionTitle: string;
description: string;
isActive: boolean;
index: number;
onHover: () => void;
onLeave: () => void;
}
const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({ index, ...props }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="507"
height="234"
fill="none"
viewBox="0 0 507 234"
{...props}
>
<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"
/>
<defs>
<linearGradient
id={`cube-gradient-${index}`}
x1="185.298"
x2="185.298"
y1="-27.5515"
y2="206.448"
gradientUnits="userSpaceOnUse"
>
<stop />
<stop offset="1" stopColor="#3F3B3E" />
</linearGradient>
</defs>
</svg>
);
export function Cube({ title, descriptionTitle, description, isActive, index, onHover, onLeave }: CubeProps) {
return (
<div className="relative flex flex-col items-center">
<motion.div
className="relative cursor-pointer"
onMouseEnter={onHover}
onMouseLeave={onLeave}
style={{
zIndex: 10 - index,
}}
animate={{
scale: isActive ? 1.05 : 1,
}}
transition={{
duration: 0.3,
ease: "easeOut",
}}
>
{/* SVG Cube */}
<CubeSvg
index={index}
className="w-48 sm:w-64 lg:w-80 h-auto drop-shadow-lg opacity-50"
style={{
filter: isActive ? 'brightness(1.2) drop-shadow(0 0 20px rgba(156, 163, 175, 0.5))' : 'brightness(0.9)',
}}
/>
{/* Title overlay */}
<div className="absolute inset-0 flex items-center justify-center">
<h3
className="text-white text-sm lg:text-base font-medium text-center px-4 drop-shadow-lg"
style={{
transform: 'rotate(0deg) skewX(0deg)',
transformOrigin: 'center'
}}
>
{title}
</h3>
</div>
{/* Description with arrow line - Desktop */}
{isActive && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
className="hidden lg:block absolute left-full top-1/2 -translate-y-1/2 z-50"
>
{/* Arrow line */}
<svg
className="absolute left-0 top-1/2 -translate-y-1/2"
width="120"
height="2"
viewBox="0 0 120 2"
fill="none"
>
<line
x1="0"
y1="1"
x2="120"
y2="1"
stroke="white"
strokeWidth="1"
opacity="0.6"
/>
</svg>
{/* Description text */}
<div className="ml-32 w-80">
<h4 className="text-white text-base font-semibold mb-2">
{descriptionTitle}
</h4>
<p className="text-white text-sm leading-relaxed font-light">
{description}
</p>
</div>
</motion.div>
)}
{/* Description for Mobile - Below cube */}
{isActive && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
transition={{ duration: 0.3 }}
className="lg:hidden absolute top-full left-1/2 -translate-x-1/2 mt-8 z-50"
>
<div className="w-64 sm:w-80 px-4">
<h4 className="text-white text-base font-semibold mb-2 text-center">
{descriptionTitle}
</h4>
<p className="text-white text-sm leading-relaxed font-light text-center">
{description}
</p>
</div>
</motion.div>
)}
</motion.div>
</div>
);
}

View File

@ -1,3 +1,5 @@
"use client";
import React from "react"; import React from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";

View File

@ -0,0 +1,61 @@
"use client";
import { useState } from "react";
import { Cube } from "@/components/ui/Cube";
const stackData = [
{
id: "network",
title: "Network",
descriptionTitle: "Secure Network",
description:
"End-to-end encrypted overlay network, always looking for the shortest possible path between participants. Logical Internet address securely linked to a private key. Unlimited scale and performance optimizations.",
position: "top",
},
{
id: "data",
title: "Data",
descriptionTitle: "Unbreakable Data",
description:
"Private, distributed, and AI-native storage with user sovereignty at its core. End-to-end encryption and redundancy, with no central control. Optimized for performance and sustainability, far surpassing traditional cloud.",
position: "middle",
},
{
id: "compute",
title: "Compute",
descriptionTitle: "Bare Metal OS",
description:
"Zero OS, an efficient and secure operating system, runs directly on the hardware enabling an autonomous cloud. Can run any Web2, Web3, or AI workload at the edge of the Internet, with more scalability and reliability.",
position: "bottom",
},
];
export function StackedCubes() {
const [active, setActive] = useState<string | null>("compute");
return (
<div className="relative w-full flex items-center justify-center lg:justify-start min-h-[600px] sm:min-h-[700px] lg:min-h-[600px]">
<div className="relative ml-0 sm:ml-4 lg:ml-8 flex flex-col items-center -space-y-4 sm:-space-y-6 lg:-space-y-8">
{stackData.map((layer, index) => (
<div
key={layer.id}
className="relative"
style={{
zIndex: 10 - index,
}}
>
<Cube
title={layer.title}
descriptionTitle={layer.descriptionTitle}
description={layer.description}
isActive={active === layer.id}
index={index}
onHover={() => setActive(layer.id)}
onLeave={() => setActive("compute")}
/>
</div>
))}
</div>
</div>
);
}

View File

@ -64,6 +64,9 @@
--container-2xl: 40rem; --container-2xl: 40rem;
/* 3D perspective utilities */
--perspective-1000: 1000px;
@keyframes fade-in { @keyframes fade-in {
from { from {
opacity: 0; opacity: 0;
@ -219,4 +222,4 @@
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
} }