ready to add animations

This commit is contained in:
Emre
2025-10-11 02:55:49 +03:00
parent 60fa49c0ef
commit 16c1a09bc4
9 changed files with 109 additions and 118 deletions

View File

@@ -35,7 +35,7 @@ export const Header = () => {
isScrolled ? 'bg-mist/95 shadow-lg backdrop-blur-sm' : 'bg-transparent',
)}
>
<div className="mx-auto flex max-w-6xl items-center justify-between py-4">
<div className="flex w-full items-center justify-between px-6 py-4 sm:px-10 lg:px-16">
<div className="flex items-center gap-8">
<Link to="/" className="flex items-center">
<img
@@ -51,8 +51,8 @@ export const Header = () => {
to={to}
className={({ isActive }) =>
cn(
'text-sm font-medium uppercase tracking-wide text-slate-500 transition-colors duration-300',
isActive && 'text-brand-600',
'text-sm font-medium uppercase tracking-wide text-white transition-colors duration-300 hover:text-white/80',
isActive && 'text-white',
)
}
>
@@ -104,15 +104,15 @@ export const Header = () => {
transition={{ duration: 0.25, ease: 'easeOut' }}
className="border-t border-slate-100 bg-mist/95 px-6 py-4 shadow-lg md:hidden"
>
<div className="mx-auto flex max-w-6xl flex-col gap-4">
<div className="flex flex-col gap-4">
{navItems.map(({ label, to }) => (
<NavLink
key={to}
to={to}
className={({ isActive }) =>
cn(
'text-base font-medium uppercase tracking-wide text-slate-600',
isActive && 'text-brand-700',
'text-base font-medium uppercase tracking-wide text-white transition-colors duration-300',
isActive && 'text-white',
)
}
>

View File

@@ -16,7 +16,7 @@ export const AboutHero = () => {
transition={{ duration: 0.6, ease: 'easeOut' }}
className="relative z-10 px-6 py-20 sm:px-10 lg:px-16 lg:py-24"
>
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-white/70">
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-white">
About GeoMind
</p>
<h1 className="mt-6 text-3xl font-semibold leading-tight sm:text-4xl lg:text-5xl">

View File

@@ -3,54 +3,92 @@ import { ScrollLockedSection } from './components/ScrollLockedSection';
import { CtaSection } from './components/CtaSection';
import { FooterSection } from './components/FooterSection';
const highlightTextClass = 'text-brand-500';
const sections = [
{
id: 'deploy',
eyebrow: 'Deploy',
title: 'Launch seamlessly across your environments.',
id: 'datacenter-economy',
eyebrow: 'Datacenter Economy',
title: (
<>
The Datacenter Economy,{' '}
<span className={highlightTextClass}>Decentralized</span>
</>
),
description:
'Use this space to outline the deployment story. Highlight speed, reliability, or any differentiators that matter for your product rollout.',
'Physical infrastructure meets profitable capacity. Deploy hardware, join the grid, earn from day one.',
},
{
id: 'plug_play',
eyebrow: 'Plug & Play',
title: 'Connect data sources without orchestration headaches.',
id: 'deploy-anywhere',
eyebrow: 'Deployment Tiers',
title: (
<>
<span className={highlightTextClass}>Deploy</span> Anywhere, Any Scale
</>
),
description:
'Describe how integrations work and communicate the ease of getting started. Keep the copy short and scannable.',
'Tier-H for homes and communities. Tier-S for enterprises and unused commercial space. Same economics, different scale.',
},
{
id: 'slice',
eyebrow: 'Slice',
title: 'Break complex geospatial datasets into digestible layers.',
id: 'autonomous-stack',
eyebrow: 'Autonomous Stack',
title: (
<>
Five Primitives, <span className={highlightTextClass}>Zero Dependencies</span>
</>
),
description:
'Talk through the slicing concept in a sentence or two. This is placeholder text while the final story is drafted.',
'Hardware ships with OS, Compute, Storage, Network, and GPU built-in. Fully autonomous operation eliminates vendor lock-in completely.',
},
{
id: 'global',
eyebrow: 'Global',
title: 'Stay synchronized across every region you operate in.',
id: 'capacity-marketplace',
eyebrow: 'Marketplace',
title: (
<>
Local Hardware, <span className={highlightTextClass}>Global</span> Liquidity
</>
),
description:
'Explain the global coverage or synchronization narrative you want to share. Replace this block with final messaging later.',
'The OS divides capacity into tradeable slices. Use what you need. Sell idle capacity automatically. Access scales instantly.',
},
{
id: 'profit',
eyebrow: 'Profit',
title: 'Make the commercial case with defensible metrics.',
id: 'dual-revenue',
eyebrow: 'Revenue Streams',
title: (
<>
<span className={highlightTextClass}>Earn</span> While You Operate
</>
),
description:
'Use these lines to tease the ROI conversation. Mention efficiency, cost savings, or other proof points once they are ready.',
'Run your workloads during business hours. Rent excess capacity 24/7. Infrastructure becomes an income-generating asset, not a cost center.',
},
{
id: 'usecases',
eyebrow: 'Use Cases',
title: 'Show how teams activate the platform day-to-day.',
id: 'economic-advantage',
eyebrow: 'Economics',
title: (
<>
<span className={highlightTextClass}>Lower Costs</span>, Higher Margins
</>
),
description:
'Imagine this section as a carousel or story. For now, it is a placeholder where future customer narratives will land.',
'Minimal datacenter build costs. Energy-efficient autonomous operation. No enterprise maintenance overhead. Math works from day one.',
},
{
id: 'instant-demand',
eyebrow: 'Demand',
title: (
<>
<span className={highlightTextClass}>Built-In</span> Customer Base
</>
),
description:
'Applications already consume capacity from the grid. Deploy hardware, connect to marketplace, start earning. No customer acquisition needed.',
},
];
export const HomePage = () => {
return (
<div className="flex min-h-screen flex-col bg-mist text-ink">
<div className="flex min-h-screen flex-col bg-black text-white">
<div className="flex flex-col">
<HeroSection />
{sections.map((section) => (

View File

@@ -2,14 +2,14 @@ import { motion } from 'framer-motion';
export const CtaSection = () => {
return (
<section className="snap-start bg-[rgb(172,173,175)]">
<section className="snap-start bg-black">
<div className="mx-auto flex w-full max-w-5xl flex-col items-center gap-10 px-6 py-40 text-center sm:px-10">
<motion.span
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-20%' }}
transition={{ duration: 0.6, ease: 'easeOut' }}
className="text-xs font-semibold uppercase tracking-[0.4em] text-ink/60"
className="text-xs font-semibold uppercase tracking-[0.4em] text-white"
>
Ready when you are
</motion.span>
@@ -18,7 +18,7 @@ export const CtaSection = () => {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-20%' }}
transition={{ duration: 0.7, ease: 'easeOut', delay: 0.15 }}
className="text-4xl font-medium leading-tight text-ink sm:text-5xl"
className="text-4xl font-medium leading-tight text-white sm:text-5xl"
>
Drop in your call to action headline and invite people forward.
</motion.h3>
@@ -27,7 +27,7 @@ export const CtaSection = () => {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-20%' }}
transition={{ duration: 0.7, ease: 'easeOut', delay: 0.25 }}
className="max-w-2xl text-lg text-ink/70"
className="max-w-2xl text-lg text-white/70"
>
Use this block to reinforce the value proposition and give your visitor a clear next step.
Consider pairing it with a lead capture form or direct contact pathway later.
@@ -38,7 +38,7 @@ export const CtaSection = () => {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-20%' }}
transition={{ duration: 0.6, ease: 'easeOut', delay: 0.35 }}
className="rounded-full border border-ink/10 bg-ink px-10 py-4 text-sm font-semibold uppercase tracking-[0.35em] text-mist transition-transform duration-300 hover:-translate-y-0.5 hover:shadow-[0_16px_36px_-24px_rgba(0,0,0,0.65)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ink/40 focus-visible:ring-offset-2 focus-visible:ring-offset-mist"
className="rounded-full border border-white/20 bg-white px-10 py-4 text-sm font-semibold uppercase tracking-[0.35em] text-black transition-transform duration-300 hover:-translate-y-0.5 hover:bg-white/90 hover:shadow-[0_16px_36px_-24px_rgba(0,0,0,0.65)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40 focus-visible:ring-offset-2 focus-visible:ring-offset-black"
>
Placeholder CTA
</motion.button>

View File

@@ -10,37 +10,37 @@ export const FooterSection = () => {
const year = new Date().getFullYear();
return (
<footer className="bg-[rgb(172,173,175)] px-6 pb-16 pt-12 sm:px-10 lg:px-20">
<div className="mx-auto flex w-full max-w-7xl flex-col gap-12 border-t border-ink/10 pt-12 md:flex-row md:items-center md:justify-between">
<footer className="bg-black px-6 pb-16 pt-12 sm:px-10 lg:px-20">
<div className="mx-auto flex w-full max-w-7xl flex-col gap-12 border-t border-white/10 pt-12 md:flex-row md:items-center md:justify-between">
<div className="space-y-3">
<span className="text-xs font-semibold uppercase tracking-[0.35em] text-ink/60">
<span className="text-xs font-semibold uppercase tracking-[0.35em] text-white">
Geomind
</span>
<p className="max-w-sm text-sm text-ink/60">
<p className="max-w-sm text-sm text-white">
Anchor your footer copy here. Add a short positioning line or compliance text once ready.
</p>
</div>
<nav className="flex flex-col items-start gap-4 text-sm font-medium uppercase tracking-[0.3em] text-ink/50 md:flex-row md:items-center md:gap-8">
<nav className="flex flex-col items-start gap-4 text-sm font-medium uppercase tracking-[0.3em] text-white md:flex-row md:items-center md:gap-8">
{footerLinks.map(({ label, to }) => (
<Link key={to} to={to} className="transition-colors duration-300 hover:text-ink/80">
<Link key={to} to={to} className="transition-colors duration-300 hover:text-white/80">
{label}
</Link>
))}
<a
href="mailto:support@threefold.tech"
className="rounded-full border border-ink/10 px-5 py-2 text-xs font-semibold tracking-[0.3em] text-ink transition-colors duration-300 hover:border-ink/40"
className="rounded-full border border-white/20 px-5 py-2 text-xs font-semibold tracking-[0.3em] text-white transition-colors duration-300 hover:border-white/50"
>
Contact
</a>
</nav>
</div>
<div className="mx-auto mt-12 flex w-full max-w-7xl items-center justify-between text-xs uppercase tracking-[0.3em] text-ink/40">
<div className="mx-auto mt-12 flex w-full max-w-7xl items-center justify-between text-xs uppercase tracking-[0.3em] text-white">
<span>© {year} Geomind</span>
<div className="flex gap-4">
<a href="#" className="transition-colors duration-300 hover:text-ink/70">
<a href="#" className="transition-colors duration-300 hover:text-white/80">
Privacy
</a>
<a href="#" className="transition-colors duration-300 hover:text-ink/70">
<a href="#" className="transition-colors duration-300 hover:text-white/80">
Terms
</a>
</div>

View File

@@ -3,14 +3,12 @@ import { motion } from 'framer-motion';
export const HeroSection = () => {
return (
<section className="relative snap-start">
<div className="relative flex h-screen w-full flex-col overflow-hidden bg-mist">
<div className="relative flex h-screen w-full flex-col overflow-hidden bg-black">
<video className="absolute inset-0 h-full w-full object-cover" autoPlay muted loop playsInline>
<source src="/videos/hero.mp4" type="video/mp4" />
</video>
<div className="absolute inset-0 bg-mist/70 backdrop-blur-[2px]" />
<div className="relative z-10 mx-auto flex h-full w-full max-w-7xl flex-col justify-between px-6 pb-16 pt-32 sm:px-10 lg:px-20">
<div className="relative z-10 flex h-full w-full flex-col justify-between px-6 pb-16 pt-32 sm:px-10 lg:px-16">
<motion.div
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
@@ -18,10 +16,10 @@ export const HeroSection = () => {
className="max-w-3xl space-y-6"
>
<h1 className="text-4xl font-medium leading-tight text-ink sm:text-5xl md:text-6xl">
<h1 className="text-4xl font-medium leading-tight text-white sm:text-5xl md:text-6xl">
The Planet's Sovereign Agentic Cloud
</h1>
<p className="max-w-xl text-lg text-ink/80">
<p className="max-w-xl text-lg text-white">
A new generation of decentralized cloud and AI infrastructure.
<br />
Secure, scalable, efficient, and sovereign by design. Deploy your own datacenter, scale globally, and turn infrastructure into profit.
@@ -32,11 +30,11 @@ export const HeroSection = () => {
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: 'easeOut', delay: 0.4 }}
className="flex items-center justify-between text-xs uppercase tracking-[0.3em] text-ink/60"
className="flex items-center justify-between text-xs uppercase tracking-[0.3em] text-white"
>
<span>Scroll to explore</span>
<div className="flex items-center gap-3">
<span className="h-px w-16 bg-ink/40" />
<span className="h-px w-16 bg-white/60" />
<span>Landing overview</span>
</div>
</motion.div>

View File

@@ -1,15 +1,12 @@
import { motion, useScroll, useSpring, useMotionValueEvent } from 'framer-motion';
import { useRef, useState } from 'react';
import { ReactNode, useRef } from 'react';
type ScrollLockedSectionProps = {
id: string;
eyebrow: string;
title: string;
title: ReactNode;
description: string;
};
const TAB_ITEMS = ['Inbox 24', 'Meetings 8', 'Subscriptions 2', 'Newsletters 6', 'Shopping 8'];
export const ScrollLockedSection = ({
id,
eyebrow,
@@ -17,76 +14,34 @@ export const ScrollLockedSection = ({
description,
}: ScrollLockedSectionProps) => {
const sectionRef = useRef<HTMLElement | null>(null);
const { scrollYProgress } = useScroll({
target: sectionRef,
offset: ['start start', 'end end'],
});
const smoothProgress = useSpring(scrollYProgress, { stiffness: 120, damping: 30, mass: 0.4 });
const [percent, setPercent] = useState(0);
const [activeIndex, setActiveIndex] = useState(0);
useMotionValueEvent(smoothProgress, 'change', (value) => {
const percentage = Math.round(value * 100);
setPercent(percentage);
const nextIndex = Math.min(
TAB_ITEMS.length - 1,
Math.round(value * (TAB_ITEMS.length - 1)),
);
setActiveIndex(nextIndex);
});
return (
<section id={id} ref={sectionRef} className="relative h-[200vh] snap-start bg-[rgb(172,173,175)]">
<section id={id} ref={sectionRef} className="relative h-[200vh] snap-start bg-black">
<div className="sticky top-0 flex h-screen flex-col">
<div className="mx-auto flex h-full w-full max-w-7xl flex-col justify-between px-6 py-24 sm:px-10 lg:px-20">
<div className="space-y-6">
<span className="text-[11px] font-semibold uppercase tracking-[0.45em] text-ink/45">
<div className="flex h-full w-full flex-col px-6 sm:px-10 lg:px-16">
{/* Text Section - Takes ~20% of vertical space */}
<div className="flex-shrink-0 space-y-4 pt-16 sm:pt-20 lg:pt-24">
<span className="text-[11px] font-semibold uppercase tracking-[0.45em] text-white">
{eyebrow}
</span>
<div className="max-w-3xl space-y-3">
<p className="text-3xl font-semibold leading-tight text-ink sm:text-[2.25rem] sm:leading-[1.2]">
<p className="text-2xl font-semibold leading-tight text-white sm:text-3xl lg:text-[2.25rem] lg:leading-[1.2]">
{title}
</p>
<p className="text-lg leading-relaxed text-ink/70">{description}</p>
<p className="text-base leading-relaxed text-white/70 sm:text-lg">{description}</p>
</div>
</div>
<div className="mt-20 flex flex-1 flex-col justify-start">
<div className="flex w-full flex-1 flex-col overflow-hidden rounded-[36px] border border-ink/10 bg-mist/70 shadow-[0_26px_64px_-48px_rgba(0,0,0,0.6)] backdrop-blur">
<div className="flex items-center justify-between px-10 pt-10 text-[11px] font-semibold uppercase tracking-[0.35em] text-ink/45">
<span>Animation Placeholder</span>
<motion.span initial={false} animate={{ opacity: percent > 0 ? 1 : 0.3 }}>
{percent}%
</motion.span>
</div>
<div className="flex-1 px-10 pb-12">
<div className="flex h-full items-center justify-center rounded-[28px] border border-dashed border-ink/15 bg-white/50 text-xs font-semibold uppercase tracking-[0.4em] text-ink/25">
Content Canvas
</div>
</div>
<div className="border-t border-ink/10 bg-mist/80 px-8 py-6">
<div className="flex items-center justify-between text-[11px] font-medium uppercase tracking-[0.3em] text-ink/35">
{TAB_ITEMS.map((tab, index) => (
<div key={tab} className="relative flex flex-1 justify-center">
<span className={index === activeIndex ? 'text-ink' : 'text-ink/40'}>{tab}</span>
<motion.span
initial={false}
animate={{ opacity: index === activeIndex ? 1 : 0 }}
transition={{ duration: 0.3, ease: 'easeOut' }}
className="absolute -bottom-3 left-1/2 h-1 w-16 -translate-x-1/2 rounded-full bg-ink"
/>
</div>
))}
</div>
</div>
</div>
<div className="mt-10 flex justify-center">
{/* Animation Section - Takes ~80% of vertical space */}
<div className="flex flex-col items-center gap-6 py-10 sm:gap-8 sm:py-12 lg:gap-10">
<div className="mx-auto w-full border border-white/10 bg-black shadow-[0_26px_64px_-48px_rgba(0,0,0,0.6)] aspect-[28/8]" />
{/* Button below animation card */}
<div className="flex justify-center">
<button
type="button"
className="flex items-center gap-2 rounded-full border border-ink/15 bg-white/70 px-6 py-2 text-[11px] font-semibold uppercase tracking-[0.35em] text-ink/50 shadow-[0_16px_32px_-28px_rgba(0,0,0,0.65)] transition-colors duration-300 hover:border-ink/30 hover:text-ink/70"
className="rounded-full border border-brand-200 bg-white px-5 py-2 text-sm font-semibold text-brand-700 shadow-subtle transition-all duration-300 hover:-translate-y-0.5 hover:border-brand-700 hover:bg-brand-700 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-300 focus-visible:ring-offset-2 focus-visible:ring-offset-black"
>
Directory
<span className="text-ink/30">Info</span>
Directory Info
</button>
</div>
</div>

View File

@@ -17,7 +17,7 @@ export const TechnologyHero = () => {
transition={{ duration: 0.6, ease: 'easeOut' }}
className="relative z-10 px-6 py-20 sm:px-10 lg:px-16 lg:py-24"
>
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-white/70">
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-white">
Technology
</p>
<h1 className="mt-6 text-3xl font-semibold leading-tight sm:text-4xl lg:text-5xl">

View File

@@ -16,7 +16,7 @@ export const UseCasesHero = () => {
transition={{ duration: 0.6, ease: 'easeOut' }}
className="relative z-10 px-6 py-20 sm:px-10 lg:px-16 lg:py-24"
>
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-white/70">
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-white">
Real World Applications
</p>
<h1 className="mt-6 text-3xl font-semibold leading-tight sm:text-4xl lg:text-5xl">