Compare commits

..

23 Commits

Author SHA1 Message Date
9a4f347ee8 ok 2025-09-19 14:14:04 +02:00
017fc41d2b refactor: consolidate navigation links into reusable NavLinks component with dynamic styling 2025-09-19 14:08:46 +02:00
ab14a5a8e5 feat: add email links and update navigation structure 2025-09-19 14:01:47 +02:00
41d4c3a054 feat: adjust mobile viewport margin and update company branding in footer 2025-09-19 01:21:11 +02:00
a362985d4c style: improve text component spacing and center CTA content on mobile 2025-09-19 01:09:08 +02:00
1d66fd60a4 chore: adjust mobile carousel gap from 100 to 110 pixels 2025-09-19 01:02:53 +02:00
43d995bbc2 feat: add responsive carousel with mobile-optimized layout and controls 2025-09-19 01:01:46 +02:00
0dedde3592 style: adjust vertical padding for Companies component on mobile and desktop views 2025-09-19 00:48:17 +02:00
d5b9303d94 style: adjust spacing and padding in Steps component for better responsive layout 2025-09-19 00:46:28 +02:00
3a240177c4 feat: add responsive layout for WorldMap component on mobile devices 2025-09-19 00:43:42 +02:00
0479b7330a feat: add mobile carousel view with auto-play for BentoReviews component 2025-09-19 00:39:00 +02:00
a78bc67ed3 style: add responsive padding to BentoReviews component for mobile devices 2025-09-19 00:30:38 +02:00
a035500c34 style: adjust spacing and responsive layout for stack section and cubes 2025-09-19 00:27:35 +02:00
2b5c502724 feat: add mobile-friendly cube descriptions with click interaction 2025-09-19 00:12:16 +02:00
a462afc8b2 fix mobile 2025-09-19 00:07:14 +02:00
bf78cde2d8 feat: add row height control to BentoGrid and remove gradient blobs from StackSection 2025-09-18 20:26:37 +02:00
cde6c90033 fx 2025-09-18 20:21:48 +02:00
f5ab743987 style: update button hover states and add white variant to docs button 2025-09-18 20:11:56 +02:00
45364a7452 ok 2025-09-18 20:09:48 +02:00
b7f25d712f fix gry 2025-09-18 19:43:04 +02:00
2fdcb3697d feat: add hover opacity transitions and bottom margin to bento grid components 2025-09-18 18:17:19 +02:00
204625b9a8 add 2025-09-18 18:14:18 +02:00
02557fcb82 ok 2025-09-17 18:32:20 +02:00
45 changed files with 713 additions and 336 deletions

View File

@@ -1,35 +1,83 @@
# Mycelium
# Mycelium Cloud Website
Mycelium is a [Tailwind Plus](https://tailwindcss.com/plus) site template built using [Tailwind CSS](https://tailwindcss.com) and [Next.js](https://nextjs.org).
This is the official website for Mycelium Cloud, built using Next.js and Tailwind CSS.
## Getting started
## Getting Started
To get started with this template, first install the npm dependencies:
Follow these instructions to get a local copy up and running for development and testing purposes.
```bash
npm install
```
### Prerequisites
Next, run the development server:
Make sure you have Node.js and npm installed on your machine. You can download them from [nodejs.org](https://nodejs.org/).
### Installation
1. Clone the repository to your local machine.
2. Install the NPM packages:
```bash
npm install
```
### Running the Application
To run the development server:
```bash
npm run dev
```
Finally, open [http://localhost:3000](http://localhost:3000) in your browser to view the website.
Open [http://localhost:3000](http://localhost:3000) in your browser to see the result.
## Customizing
## Git Workflow
You can start editing this template by modifying the files in the `/src` folder. The site will auto-update as you edit these files.
We follow a branching model to ensure code quality and a stable production environment. All new work should be done on a feature branch.
1. **Switch to the `development` branch** and make sure it's up to date:
```bash
git checkout development
git pull origin development
```
2. **Create a new feature branch** for your changes:
```bash
git checkout -b your-feature-name
```
3. **Make your changes and commit them**.
4. **Push your feature branch** to the remote repository:
```bash
git push origin your-feature-name
```
5. **Create a Pull Request** on GitHub from your feature branch to the `development` branch.
6. After the pull request is reviewed and merged, the changes will be on the `development` branch. To deploy to production, the `development` branch will be merged into `main`.
## Project Structure
Here is an overview of the key directories in the project:
- `src/app/(main)/page.tsx`
This is the main entry point for the homepage.
- `src/components/`
This directory contains all the reusable React components used throughout the site. The main components rendered on the homepage (`src/app/(main)/page.tsx`) are:
- `HomeHero.tsx`
- `StackSection.tsx` (as `StackSectionPreview`)
- `BentoReviews.tsx`
- `WorldMap.tsx`
- `Steps.tsx`
- `Companies.tsx`
- `ClickableGallery.tsx`
- `CallToAction.tsx`
- `public/images/`
All static images are stored here. You can find logos, gallery images, and other visual assets in this folder.
- `public/videos/`
This folder contains video assets used on the site.
## License
This site template is a commercial product and is licensed under the [Tailwind Plus license](https://tailwindcss.com/plus/license).
## Learn more
To learn more about the technologies used in this site template, see the following resources:
- [Tailwind CSS](https://tailwindcss.com/docs) - the official Tailwind CSS documentation
- [Next.js](https://nextjs.org/docs) - the official Next.js documentation
- [Headless UI](https://headlessui.dev) - the official Headless UI documentation

1
next-env.d.ts vendored
View File

@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

27
package-lock.json generated
View File

@@ -11,6 +11,7 @@
"@headlessui/react": "^2.1.0",
"@heroicons/react": "^2.2.0",
"@lobehub/icons": "^1.97.2",
"@tabler/icons-react": "^3.35.0",
"@tailwindcss/forms": "^0.5.3",
"@types/node": "^20.10.8",
"@types/react": "^18.2.55",
@@ -2976,6 +2977,32 @@
"tslib": "^2.8.0"
}
},
"node_modules/@tabler/icons": {
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.35.0.tgz",
"integrity": "sha512-yYXe+gJ56xlZFiXwV9zVoe3FWCGuZ/D7/G4ZIlDtGxSx5CGQK110wrnT29gUj52kEZoxqF7oURTk97GQxELOFQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/codecalm"
}
},
"node_modules/@tabler/icons-react": {
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.35.0.tgz",
"integrity": "sha512-XG7t2DYf3DyHT5jxFNp5xyLVbL4hMJYJhiSdHADzAjLRYfL7AnjlRfiHDHeXxkb2N103rEIvTsBRazxXtAUz2g==",
"license": "MIT",
"dependencies": {
"@tabler/icons": "3.35.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/codecalm"
},
"peerDependencies": {
"react": ">= 16"
}
},
"node_modules/@tailwindcss/forms": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",

View File

@@ -13,6 +13,7 @@
"@headlessui/react": "^2.1.0",
"@heroicons/react": "^2.2.0",
"@lobehub/icons": "^1.97.2",
"@tabler/icons-react": "^3.35.0",
"@tailwindcss/forms": "^0.5.3",
"@types/node": "^20.10.8",
"@types/react": "^18.2.55",

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
public/videos/agent.mp4 Normal file

Binary file not shown.

Binary file not shown.

BIN
public/videos/fungistor.mp4 Normal file

Binary file not shown.

BIN
public/videos/herodb.mp4 Normal file

Binary file not shown.

BIN
public/videos/mesh.mp4 Normal file

Binary file not shown.

BIN
public/videos/sandbox.mp4 Normal file

Binary file not shown.

BIN
public/videos/universal.mp4 Normal file

Binary file not shown.

View File

@@ -13,6 +13,7 @@ import { ScrollUp } from '@/components/ui/ScrollUp'
import { GridStats } from '@/components/GridStats'
import { WorldMap } from '@/components/WorldMap'
import { GetStarted } from '@/components/GetStarted'
import { BentoReviews } from '@/components/BentoReviews'
export default function Home() {
return (
@@ -20,9 +21,12 @@ export default function Home() {
<section id="home-hero">
<HomeHero />
</section>
<section id="about">
<section id="technologies">
<StackSectionPreview />
</section>
<section id="bento-reviews">
<BentoReviews />
</section>
<section id="network">
<WorldMap />
</section>
@@ -32,15 +36,9 @@ export default function Home() {
<section id="llms">
<Companies />
</section>
<section id="features">
<UseCases />
</section>
<section id="clickable-gallery">
<ClickableGallery />
</section>
<section id="get-started">
<GetStarted />
</section>
<section id="call-to-action">
<CallToAction />
</section>

View File

@@ -1,60 +1,172 @@
'use client'
"use client";
import React from 'react'
import { CT, CP } from '@/components/Texts'
import { cn } from "@/lib/utils";
import { H2, P } from "@/components/Texts";
import React, { useState, useEffect, useRef } from "react";
import { BentoGrid, MotionBentoGridItem } from "@/components/ui/bento-grid";
import { AnimatePresence, motion } from "framer-motion";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { FadeIn } from "./FadeIn";
interface Review {
title: string
body: string
}
const items = [
{
title: 'FungiStor',
subtitle: 'Long-Term AI Memory',
description: 'Quantum-safe permanent storage preserving AI knowledge forever. Zero-knowledge architecture with mathematical dispersal ensures immortality.',
video: "/videos/fungistor.mp4",
},
{
title: 'HeroDB',
subtitle: 'Active AI Memory',
description: 'High-performance datastore for AI working memory. Multi-modal indexing enables vector search with global accessibility.',
video: "/videos/herodb.mp4",
},
{
title: 'MOS Sandboxes',
subtitle: 'Secure Agent Workspaces',
description: 'Lightweight isolated environments deploying globally in five seconds. Hardware-level isolation ensures maximum security for agents.',
video: "/videos/sandbox.mp4",
},
{
title: 'Mycelium Mesh',
subtitle: 'Secure Communication Network',
description: 'Peer-to-peer overlay network with end-to-end encryption. Self-healing shortest-path routing creates resilient agentic communication.',
video: "/videos/mesh.mp4",
},
{
title: 'Deterministic Deployment',
subtitle: 'Verifiable Code Execution',
description: 'Cryptographic guarantee system ensuring deployed code matches specifications. Prevents supply-chain attacks with immutable trails.',
video: "/videos/deterministic.mp4",
},
{
title: 'Agent Coordination',
subtitle: 'Sovereign Workflow Management',
description: 'User-centric orchestration where HERO agents coordinate worker fleets. Planetary-scale coordination with instant spawning.',
video: "/videos/agent.mp4",
},
{
title: 'Universal Interface Layer',
subtitle: 'AI Service Gateway',
description: 'Unified broker connecting agents to LLMs, APIs, and services. Integrated micropayments simplify development.',
video: "/videos/universal.mp4",
},
const reviews: Review[] = [
{ title: 'FungiStor: Long-Term AI Memory', body: 'Quantum-safe permanent storage preserving AI knowledge forever. Zero-knowledge architecture with mathematical dispersal ensures immortality.' },
{ title: 'HeroDB: Active AI Memory', body: 'High-performance datastore for AI working memory. Multi-modal indexing enables vector search with global accessibility.' },
{ title: 'MOS Sandboxes: Secure Agent Workspaces', body: 'Lightweight isolated environments deploying globally in five seconds. Hardware-level isolation ensures maximum security for agents.' },
{ title: 'Mycelium Mesh: Secure Communication Network', body: 'Peer-to-peer overlay network with end-to-end encryption. Self-healing shortest-path routing creates resilient agentic communication.' },
{ title: 'Deterministic Deployment: Verifiable Code Execution', body: 'Cryptographic guarantee system ensuring deployed code matches specifications. Prevents supply-chain attacks with immutable trails.' },
{ title: 'Agent Coordination: Sovereign Workflow Management', body: 'User-centric orchestration where HERO agents coordinate worker fleets. Planetary-scale coordination with instant spawning.' },
{ title: 'Universal Interface Layer: AI Service Gateway', body: 'Unified broker connecting agents to LLMs, APIs, and services. Integrated micropayments simplify development.' },
{ title: 'Semantic Index & Search: Navigable Knowledge Fabric', body: 'Transforms data chaos into unified knowledge graphs. Goes beyond keywords to understand meaning and context.' },
]
];
export function BentoReviews() {
const [activeIndex, setActiveIndex] = useState(0);
const [isPaused, setIsPaused] = useState(false);
const intervalRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
if (!isPaused) {
intervalRef.current = setInterval(() => {
setActiveIndex((prevIndex) => (prevIndex + 1) % items.length);
}, 3000);
} else {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
}
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, [isPaused]);
const handleCardTap = () => {
setIsPaused(true);
};
const handlePrev = () => {
setActiveIndex((prevIndex) => (prevIndex - 1 + items.length) % items.length);
setIsPaused(true);
};
const handleNext = () => {
setActiveIndex((prevIndex) => (prevIndex + 1) % items.length);
setIsPaused(true);
};
return (
<div className="bg-black py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<CT className="text-indigo-400">AI Memory & Coordination</CT>
<p className="mt-2 max-w-2xl text-4xl font-semibold tracking-tight text-white sm:text-5xl">
The Mycelium Agentic Stack
</p>
{/* Bento Grid */}
<div className="mt-12 grid grid-cols-1 gap-4 sm:mt-16 lg:grid-cols-7 lg:grid-rows-2">
{/* Left + middle cards */}
{reviews.slice(0, 6).map((review, i) => (
<div
key={i}
className="relative lg:col-span-2 rounded-2xl bg-gray-900 p-8 shadow-sm border border-white/10 flex flex-col"
>
<CT className="text-white text-lg">{review.title}</CT>
<CP className="mt-2 text-gray-400">{review.body}</CP>
</div>
))}
{/* Right column (two stacked cards) */}
<div className="flex flex-col gap-4 lg:col-span-1">
{reviews.slice(6).map((review, i) => (
<div
key={i}
className="flex flex-col justify-between rounded-2xl bg-gray-900 p-6 border border-white/10 h-full"
>
<CT className="text-white text-lg">{review.title}</CT>
<CP className="mt-2 text-gray-400">{review.body}</CP>
</div>
))}
<div>
<div className="relative isolate pt-24 pb-12 bg-black text-center w-full lg:px-0 px-4">
<FadeIn transition={{ duration: 0.8, delay: 0.1 }}>
<div className="mx-auto max-w-5xl ">
<H2 className="text-center">Mycelium Technologies</H2>
</div>
</FadeIn>
<FadeIn transition={{ duration: 0.8, delay: 0.2 }}>
<div className="mx-auto max-w-4xl mt-6 mb-8">
<P className="text-center" color="primary">
A robust infrastructure layer for autonomous AI agents, our technology stack
delivers a secure, efficient, and intuitive platform for deploying and managing AI agents at scale.
</P>
</div>
</FadeIn>
</div>
{/* Desktop Grid */}
<div className="hidden lg:block">
<BentoGrid className="max-w-8xl lg:px-12 px-4 pb-12 lg:grid-cols-3">
{items.map((item, i) => (
<MotionBentoGridItem
key={i}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false, margin: '0px 0px -100px 0px' }}
transition={{ duration: 0.8, delay: 0.3 + i * 0.1 }}
className={cn(i === 3 || i === 6 ? "md:col-span-2" : "")}
rowHeight={i >= 3 ? "h-[22rem]" : ""}
title={item.title}
subtitle={item.subtitle}
description={item.description}
video={item.video}
/>
))}
</BentoGrid>
</div>
{/* Mobile Carousel */}
<div className="lg:hidden block px-4 pb-12">
<div className="relative h-[24rem] w-full overflow-hidden">
<div className="absolute inset-0" onTouchStart={handleCardTap} />
<AnimatePresence initial={false}>
<motion.div
key={activeIndex}
className="absolute h-full w-full"
initial={{ x: "100%", opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: "-100%", opacity: 0 }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
>
<MotionBentoGridItem
className="h-full"
title={items[activeIndex].title}
subtitle={items[activeIndex].subtitle}
description={items[activeIndex].description}
video={items[activeIndex].video}
/>
</motion.div>
</AnimatePresence>
<button
onClick={handlePrev}
className="absolute left-2 top-[58%] -translate-y-1/2 rounded-full bg-black/50 p-2 text-white z-10"
>
<ChevronLeft className="h-6 w-6" />
</button>
<button
onClick={handleNext}
className="absolute right-2 top-[58%] -translate-y-1/2 rounded-full bg-black/50 p-2 text-white z-10"
>
<ChevronRight className="h-6 w-6" />
</button>
</div>
</div>
</div>
)
);
}

View File

@@ -17,7 +17,7 @@ const variantStyles = {
},
outline: {
gray: 'border-gray-300 text-gray-700 hover:border-gray-400 active:bg-gray-100 active:text-gray-700/80',
white: 'border-gray-300 text-white hover:border-gray-400 active:bg-gray-100 active:text-white',
white: 'border-gray-300 text-white hover:border-gray-400 hover:text-gray-300 active:bg-gray-100 active:text-gray-800',
},
}

View File

@@ -13,12 +13,14 @@ export function CallTo() {
</P>
<div className="mt-10 flex items-center justify-center gap-x-6">
<a
href="#"
href="mailto:info@ourworld.tf"
target="_blank"
rel="noopener noreferrer"
className="rounded-md bg-[#2F3178] px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-[#2F3178]/80 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#2F3178]"
>
Book a Meeting
</a>
<a href="#" className="text-sm/6 font-semibold text-[#2F3178] hover:text-[#2F3178]/80">
<a href="mailto:info@ourworld.tf" target="_blank" rel="noopener noreferrer" className="text-sm/6 font-semibold text-[#2F3178] hover:text-[#2F3178]/80">
Join the Waitlist <span aria-hidden="true"></span>
</a>
</div>

View File

@@ -1,12 +1,13 @@
import { CircleBackground } from '@/components/CircleBackground'
import { Container } from '@/components/Container'
import { Button } from '@/components/Button'
import { FadeIn } from '@/components/FadeIn'
export function CallToAction() {
return (
<section
id="get-free-shares-today"
className="relative overflow-hidden py-20 sm:py-28"
className="relative overflow-hidden py-20 sm:py-28 border-t border-gray-800"
>
<video
autoPlay
@@ -18,11 +19,12 @@ export function CallToAction() {
<source src="/videos/cta.mp4" type="video/mp4" />
</video>
<div className="absolute top-0 left-0 w-full h-full bg-black opacity-40 z-10"></div>
<div className="absolute left-20 top-1/2 -translate-y-1/2 sm:left-1/2 sm:-translate-x-1/2 z-20">
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 sm:left-1/2 sm:-translate-x-1/2 z-20">
<CircleBackground color="#fff" className="animate-spin-slower" />
</div>
<Container className="relative z-20">
<div className="mx-auto max-w-md sm:text-center">
<FadeIn>
<div className="mx-auto max-w-md text-center">
<h2 className="text-3xl font-medium tracking-tight text-white sm:text-4xl">
Decentralized AI Agents that are Truly Yours
</h2>
@@ -31,14 +33,21 @@ export function CallToAction() {
build your own. Ready to own your intelligence?
</p>
<div className="mt-8 flex justify-center gap-x-6">
<Button variant="solid" color="cyan" href="#">
<Button
variant="solid"
color="cyan"
href="mailto:info@ourworld.tf"
target="_blank"
rel="noopener noreferrer"
>
Book a Meeting
</Button>
<Button href="#" variant="outline" color="white">
<Button href="mailto:info@ourworld.tf" target="_blank" rel="noopener noreferrer" variant="outline" color="white">
Join the Waitlist
</Button>
</div>
</div>
</FadeIn>
</Container>
</section>
)

View File

@@ -1,41 +1,37 @@
'use client'
import { useEffect, useMemo, useState, useRef } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useResponsiveCarousel } from '@/hooks/useResponsiveCarousel';
import Image from 'next/image'
import { motion, AnimatePresence, useInView } from 'framer-motion'
import { motion, AnimatePresence } from 'framer-motion'
import { wrap } from 'popmotion'
import { Button } from '@/components/Button';
import { H2, P, H4 } from '@/components/Texts';
import { H2, P, CT } from '@/components/Texts';
import { TypeAnimation } from 'react-type-animation'
import { FadeIn } from './FadeIn';
const galleryItems = [
{ text: 'Navigate and interact with any web interface', image: '/images/interface.png' },
{ text: 'Process documents across all formats', image: '/images/docs.png' },
{ text: 'Execute multi-step workflows autonomously', image: '/images/flow.png' },
{ text: 'Manage calendars, emails, and tasks', image: '/images/calendar.png' },
{ text: 'Perform deep semantic search across all data sources', image: '/images/data.png' },
{ text: 'Identify patterns in complex datasets', image: '/images/datasets.png' },
{ text: 'Provide real-time market intelligence', image: '/images/market.png' },
{ text: 'Generate and debug code in multiple languages', image: '/images/code.png' },
{ text: 'Create consistent branded content', image: '/images/branding.png' },
{ text: 'Translate and localize materials', image: '/images/translate.png' },
{ text: 'Transform and migrate data structures', image: '/images/structure.png' },
{ text: 'Navigate and interact with any web interface', image: '/images/gallery/interface.jpg', width: 448, height: 277 },
{ text: 'Process documents across all formats', image: '/images/gallery/docs.jpg', width: 448, height: 277 },
{ text: 'Execute multi-step workflows autonomously', image: '/images/gallery/flow.jpg', width: 448, height: 277 },
{ text: 'Manage calendars, emails, and tasks', image: '/images/gallery/calendar.jpg', width: 448, height: 277 },
{ text: 'Perform deep semantic search across all data sources', image: '/images/gallery/data.jpg', width: 448, height: 277 },
{ text: 'Identify patterns in complex datasets', image: '/images/gallery/datasets.jpg', width: 448, height: 277 },
{ text: 'Provide real-time market intelligence', image: '/images/gallery/market.jpg', width: 448, height: 277 },
{ text: 'Generate and debug code in multiple languages', image: '/images/gallery/code.jpg', width: 448, height: 277 },
{ text: 'Create consistent branded content', image: '/images/gallery/branding.jpg', width: 448, height: 277 },
{ text: 'Translate and localize materials', image: '/images/gallery/translate.jpg', width: 448, height: 277 },
{ text: 'Transform and migrate data structures', image: '/images/gallery/structure.jpg', width: 448, height: 277 },
]
// 🔧 Carousel Config
const VISIBLE = 4
const CARD_SIZE = 360 // square size on desktop
const GAP = 300 // spacing for larger cards
const ROT_Y = 18
const DEPTH = 210
const SCALE_DROP = 0.12
const AUTOPLAY_MS = 3200
const VISIBLE = 4;
const AUTOPLAY_MS = 3200;
export function ClickableGallery() {
const [active, setActive] = useState(0)
const [hovering, setHovering] = useState(false)
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
const [active, setActive] = useState(0);
const [hovering, setHovering] = useState(false);
const { GAP, ROT_Y, DEPTH, SCALE_DROP } = useResponsiveCarousel();
// autoplay
useEffect(() => {
@@ -53,113 +49,131 @@ export function ClickableGallery() {
const prev = () => setActive((i) => wrap(0, galleryItems.length, i - 1))
return (
<div ref={ref}>
<div className="relative isolate pt-0 pb-0 bg-black text-center w-full">
<motion.div initial={{ opacity: 0, y: 20 }} animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }} transition={{ duration: 0.8, delay: 0.1 }} className="mx-auto max-w-5xl">
<H2 className="text-center mt-12">One Agent, Endless Possibilities.</H2>
</motion.div>
<motion.div initial={{ opacity: 0, y: 20 }} animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }} transition={{ duration: 0.8, delay: 0.2 }} className="mx-auto max-w-4xl mt-6">
<P className="text-center" color="primary">
The future isnt about more tools. Its about one intelligent partner that can do it all. This is your gateway to creativity, automation, and discovery.
</P>
</motion.div>
</div>
<motion.section
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 1 } : { opacity: 0 }}
transition={{ duration: 1, delay: 0.4 }}
className="relative w-full flex items-center justify-center overflow-hidden bg-black pt-0 pb-24"
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
>
<div className="relative w-full max-w-[1800px] h-[500px]" style={{ perspective: '1600px' }}>
<div className="absolute inset-0" style={{ transformStyle: 'preserve-3d' }}>
<AnimatePresence initial={false}>
{indices.map((idx, i) => {
const distance = i - VISIBLE
const item = galleryItems[idx]
const x = distance * GAP
const z = -Math.abs(distance) * DEPTH
const r = distance * ROT_Y
const s = 1 - Math.abs(distance) * SCALE_DROP
const o = distance === 0 ? 1 : 0.90
const zIndex = 100 - Math.abs(distance)
return (
<motion.div
key={`${idx}-${i}`}
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 will-change-transform"
initial={{ opacity: 0 }}
animate={{
transform: `translateX(${x}px) translateZ(${z}px) rotateY(${r}deg) scale(${s})`,
zIndex,
opacity: o,
}}
exit={{ opacity: 0 }}
transition={{ type: 'spring', stiffness: 220, damping: 26 }}
onClick={() => setActive(idx)}
>
{/* Square container, keeps image ratio inside */}
<div
className="relative rounded-2xl overflow-hidden bg-white flex items-center justify-center"
style={{ width: CARD_SIZE, height: CARD_SIZE }}
>
<Image
src={item.image}
alt={item.text}
fill
className="object-contain rounded-2xl text-black"
priority={i === VISIBLE}
/>
</div>
</motion.div>
)
})}
</AnimatePresence>
</div>
{/* Arrows */}
<div className="absolute inset-y-0 left-8 flex items-center z-50">
<button
onClick={prev}
className="bg-white/70 hover:bg-white rounded-full p-3 shadow-lg backdrop-blur-md"
aria-label="Previous"
>
<svg className="size-8" viewBox="0 0 24 24" fill="none" dangerouslySetInnerHTML={{ __html: '<path d="M15 19L8 12l7-7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>' }} />
</button>
</div>
<div className="absolute inset-y-0 right-8 flex items-center z-50">
<button
onClick={next}
className="bg-white/70 hover:bg-white rounded-full p-3 shadow-lg backdrop-blur-md"
aria-label="Next"
>
<svg className="size-8" viewBox="0 0 24 24" fill="none" dangerouslySetInnerHTML={{ __html: '<path d="M9 5l7 7-7 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>' }} />
</button>
</div>
{/* Foreground pill */}
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-[60]">
<div className="flex items-center justify-between w-[1040px] gap-6 rounded-3xl bg-white/95 shadow-[0_8px_40px_rgba(0,0,0,0.15)] px-12 py-6 backdrop-blur">
<H4 as="h4" className="max-w-[820px] h-[72px] text-black">
<TypeAnimation
key={active}
sequence={[galleryItems[active].text]}
wrapper="span"
speed={50}
repeat={0}
/>
</H4>
<Button href="#" color="cyan" className="text-sm px-4 py-2 lg:text-base">
Start
</Button>
<div>
<div className="relative isolate pt-8 pb-0 bg-transparent text-center w-full">
<FadeIn transition={{ duration: 0.8, delay: 0.1 }}>
<div className="mx-auto max-w-5xl">
<H2 className="text-center">One Agent, Endless Possibilities.</H2>
</div>
</div>
</FadeIn>
<FadeIn transition={{ duration: 0.8, delay: 0.2 }}>
<div className="mx-auto max-w-4xl mt-6 lg:px-0 px-4">
<P className="text-center" color="primary">
The future isnt about more tools. Its about one intelligent partner that can do it all. This is your gateway to creativity, automation, and discovery.
</P>
</div>
</FadeIn>
</div>
</motion.section>
<FadeIn transition={{ duration: 1, delay: 0.4 }}>
<section
className="relative w-full flex items-center justify-center overflow-hidden bg-transparent -mt-8 pt-0 pb-0"
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
>
<div className="relative w-full max-w-[1800px] h-[300px] md:h-[500px]" style={{ perspective: '1600px' }}>
<div className="absolute inset-0" style={{ transformStyle: 'preserve-3d' }}>
<AnimatePresence initial={false}>
{indices.map((idx, i) => {
const distance = i - VISIBLE;
const item = galleryItems[idx];
const x = distance * GAP;
const z = -Math.abs(distance) * DEPTH;
const r = distance * ROT_Y;
const s = 1 - Math.abs(distance) * SCALE_DROP;
const o = distance === 0 ? 1 : 0.80;
const zIndex = 100 - Math.abs(distance);
return (
<motion.div
key={`${idx}-${i}`}
className={`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 will-change-transform overflow-hidden ${distance === 0 ? 'rounded-xl' : ''}`}
initial={{ opacity: 0 }}
animate={{
transform: `translateX(${x}px) translateZ(${z}px) rotateY(${r}deg) scale(${s})`,
zIndex,
opacity: o,
boxShadow: distance === 0 ? '0 0 20px 5px rgba(255, 255, 255, 0.2)' : 'none',
}}
exit={{ opacity: 0 }}
transition={{ type: 'spring', stiffness: 220, damping: 26 }}
onClick={() => setActive(idx)}
>
<div className="relative bg-black flex items-center justify-center">
<Image
src={item.image}
alt={item.text}
width={item.width}
height={item.height}
className="object-contain text-white"
priority={i === VISIBLE}
/>
</div>
</motion.div>
);
})}
</AnimatePresence>
</div>
</div>
{/* Arrows */}
{/* Arrows */}
<div className="absolute inset-y-0 left-8 hidden md:flex items-center z-50">
<button
onClick={prev}
className="bg-transparent rounded-full p-2 shadow-lg backdrop-blur-md"
aria-label="Previous"
>
<svg className="size-8" viewBox="0 0 24 24" fill="none" dangerouslySetInnerHTML={{ __html: '<path d="M15 19L8 12l7-7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>' }} />
</button>
</div>
<div className="absolute inset-y-0 right-8 hidden md:flex items-center z-50">
<button
onClick={next}
className="bg-transparent rounded-full p-2 shadow-lg backdrop-blur-md"
aria-label="Next"
>
<svg className="size-8" viewBox="0 0 24 24" fill="none" dangerouslySetInnerHTML={{ __html: '<path d="M9 5l7 7-7 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>' }} />
</button>
</div>
{/* Foreground pill (Desktop) */}
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-[60] hidden md:block">
<div className="flex items-center justify-between w-[1040px] gap-6 rounded-2xl bg-black/30 shadow-[0_8px_40px_rgba(0,0,0,0.15)] px-12 backdrop-blur">
<CT as="h4" className="max-w-[820px] h-[72px] text-white flex items-center">
<TypeAnimation
key={active}
sequence={[galleryItems[active].text]}
wrapper="span"
speed={50}
repeat={0}
/>
</CT>
<Button href="#" color="cyan" className="text-sm px-4 py-2 lg:text-base whitespace-nowrap">
Start
</Button>
</div>
</div>
</section>
{/* Text box (Mobile) */}
<div className="md:hidden w-full px-4 -mt-12 mb-16">
<div className="flex flex-row items-center justify-between w-full gap-x-4 rounded-2xl bg-white/10 bg-opacity-80 p-4 backdrop-blur-md">
<CT as="h4" className="w-full text-left h-[72px] text-white leading-tight flex items-center">
<TypeAnimation
key={active}
sequence={[galleryItems[active].text]}
wrapper="span"
speed={50}
repeat={0}
/>
</CT>
<Button href="#" color="cyan" className="text-xs px-3 py-1.5 whitespace-nowrap">
Start
</Button>
</div>
</div>
</FadeIn>
</div>
)
);
}

View File

@@ -40,7 +40,7 @@ const row2 = logos.slice(6);
export function Companies() {
return (
<div id="companies" className="relative bg-black flex flex-col items-center justify-center w-full overflow-hidden antialiased py-12 -top-20">
<div id="companies" className="relative bg-black flex flex-col items-center justify-center w-full overflow-hidden antialiased lg:py-12 py-4 -top-20">
<div className="relative z-10 mx-auto w-full max-w-6xl p-4">
{/* Heading */}

28
src/components/FadeIn.tsx Normal file
View File

@@ -0,0 +1,28 @@
'use client'
import { motion, Transition } from 'framer-motion'
import React from 'react'
import { useMediaQuery } from '@/hooks/useMediaQuery'
type FadeInProps = {
children: React.ReactNode
transition?: Transition
className?: string
}
export function FadeIn({ children, transition, className }: FadeInProps) {
const isMobile = useMediaQuery('(max-width: 768px)')
return (
<motion.div
className={className}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false, margin: isMobile ? '0px 0px -50px 0px' : '0px 0px -100px 0px' }}
transition={transition || { duration: 0.5 }}
>
{children}
</motion.div>
)
}

View File

@@ -6,6 +6,7 @@ import { Container } from '@/components/Container'
import { TextField } from '@/components/Fields'
import { Logomark } from '@/components/Logo'
import { NavLinks } from '@/components/NavLinks'
import { FadeIn } from '@/components/FadeIn'
import qrCode from '@/images/qr-code.svg'
function QrCodeBorder(props: React.ComponentPropsWithoutRef<'svg'>) {
@@ -23,8 +24,9 @@ function QrCodeBorder(props: React.ComponentPropsWithoutRef<'svg'>) {
export function Footer() {
return (
<footer id="footer" className="border-t border-gray-800">
<FadeIn>
<Container>
<div className="flex flex-col items-start justify-between gap-y-12 pt-16 pb-6 lg:flex-row lg:items-center lg:py-16">
<div className="flex flex-col items-start justify-between gap-y-12 pt-12 pb-6 lg:flex-row lg:items-center lg:py-12">
<div>
<div className="flex items-center text-white">
<Logomark className="h-10 w-10 flex-none fill-cyan-500" />
@@ -56,7 +58,7 @@ export function Footer() {
</div>
</div>
<div className="flex flex-col items-center border-t border-gray-800 pt-8 pb-12 md:flex-row-reverse md:justify-between md:pt-6">
<form className="flex w-full justify-center md:w-auto">
{/* <form className="flex w-full justify-center md:w-auto">
<TextField
type="email"
aria-label="Email address"
@@ -70,12 +72,13 @@ export function Footer() {
<span className="hidden lg:inline">Join our newsletter</span>
<span className="lg:hidden">Join newsletter</span>
</Button>
</form>
<p className="mt-6 text-sm text-gray-400 md:mt-0">
&copy; Copyright ThreeFold {new Date().getFullYear()}. All rights reserved.
</form> */}
<p className="mt-6 text-sm text-gray-400 md:mt-0 ">
&copy; Copyright OurWorld Holdings, {new Date().getFullYear()}. All rights reserved.
</p>
</div>
</Container>
</FadeIn>
</footer>
)
}

View File

@@ -48,7 +48,7 @@ function MobileNavLink(
return (
<PopoverButton
as={Link}
className="block text-base/7 tracking-tight text-[#2F3178]"
className="block text-base/7 tracking-tight text-white"
{...props}
/>
)
@@ -72,7 +72,7 @@ export function Header() {
{({ open }) => (
<>
<PopoverButton
className="relative z-10 -m-2 inline-flex items-center rounded-lg stroke-gray-900 p-2 hover:bg-gray-200/50 hover:stroke-gray-600 focus:not-data-focus:outline-hidden active:stroke-gray-900"
className="relative z-10 -m-2 inline-flex items-center rounded-lg stroke-white p-2 hover:bg-gray-200/50 hover:stroke-gray-400 focus:not-data-focus:outline-hidden active:stroke-white"
aria-label="Toggle site navigation"
>
{({ open }) =>
@@ -104,25 +104,13 @@ export function Header() {
y: -32,
transition: { duration: 0.2 },
}}
className="absolute inset-x-0 top-0 z-0 origin-top rounded-b-2xl bg-white px-6 pt-32 pb-6 shadow-2xl shadow-gray-900/20"
className="absolute inset-x-0 top-0 z-0 origin-top rounded-b-2xl bg-gray-900 px-6 pt-32 pb-6 shadow-2xl shadow-gray-900/20"
>
<div className="space-y-4">
<MobileNavLink href="/#about">
About
</MobileNavLink>
<MobileNavLink href="/#benefits">
Benefits
</MobileNavLink>
<MobileNavLink href="/#features">
Features
</MobileNavLink>
<MobileNavLink href="/#usecases">
Use Cases
</MobileNavLink>
<MobileNavLink href="/#faqs">FAQs</MobileNavLink>
<NavLinks className="block text-base/7 tracking-tight" />
</div>
<div className="mt-8 flex flex-col gap-4">
<Button href="https://docs.ourworld.tf/mycelium_cloud/docs/" variant="outline" color="white">
<Button href="https://docs.ourworld.tf/mycelium_cloud/docs/" variant="outline" color="white" target="_blank" rel="noopener noreferrer">
Docs
</Button>
<Button href="https://www.mycelium.threefold.io/download/" color="cyan">Get Started</Button>
@@ -135,7 +123,7 @@ export function Header() {
)}
</Popover>
<div className="flex items-center gap-6 max-lg:hidden">
<Button href="https://docs.ourworld.tf/mycelium_cloud/docs/" variant="outline">
<Button href="https://docs.ourworld.tf/mycelium_cloud/docs/" variant="outline" color="white" target="_blank" rel="noopener noreferrer">
Docs
</Button>
<Button href="https://www.mycelium.threefold.io/download/" color="cyan">Get Started</Button>

View File

@@ -32,26 +32,43 @@ export function HomeHero() {
<div className="absolute inset-0 bg-black/60" />
</div>
<div className="relative px-6 lg:px-8">
<div className="relative -top-15 mx-auto max-w-8xl h-screen flex items-center justify-center">
<div className="text-center max-w-5xl">
<H1 color="light">
<TypeAnimation
sequence={[
'Decentralized Autonomous Agentic Cloud.',
1000,
]}
wrapper="span"
speed={50}
repeat={0}
/>
</H1>
<div className="relative -top-15 mx-auto flex h-screen max-w-8xl items-center justify-center">
<div className="text-center">
<div className="max-w-5xl">
<H1 color="light">
<TypeAnimation
sequence={[
'Decentralized Autonomous Agentic Cloud.',
1000,
]}
wrapper="span"
speed={50}
repeat={0}
/>
</H1>
</div>
{/* Mobile-only PL */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 1 }}
className="lg:hidden"
>
<PL className="mt-8 max-w-xl text-center text-gray-100" color="light">
Mycelium's advancements in Agentic infrastructure supports private, secure and autonomous Agents that connect, learn and grow with you.
</PL>
</motion.div>
</div>
{/* Desktop-only PL */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 1 }}
className="hidden lg:block"
>
<PL className="absolute bottom-0 left-0 max-w-xl text-left text-gray-100" color="light">
<PL className="absolute -bottom-8 left-0 max-w-xl text-left text-gray-100" color="light">
Mycelium's advancements in Agentic infrastructure supports private, secure and autonomous Agents that connect, learn and grow with you.
</PL>
</motion.div>

View File

@@ -3,8 +3,9 @@
import { useRef, useState } from 'react'
import Link from 'next/link'
import { AnimatePresence, motion } from 'framer-motion'
import clsx from 'clsx'
export function NavLinks() {
export function NavLinks({ className }: { className?: string }) {
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
let timeoutRef = useRef<number | null>(null)
@@ -23,17 +24,18 @@ export function NavLinks() {
};
return [
['About', '/#about'],
['Technologies', '/#technologies'],
['Network', '/#network'],
['Deploy', '/#deploy'],
['LLMs', '/#llms'],
['Features', '/#features'],
['How it Works', '/#how-it-works'],
['Get Started', '/#get-started'],
].map(([label, href], index) => (
<Link
key={label}
href={href}
className="relative -mx-3 -my-2 rounded-lg px-3 py-2 text-sm text-white transition-colors delay-150 hover:text-gray-300 hover:delay-0"
className={clsx(
'relative -mx-3 -my-2 rounded-lg px-3 py-2 text-sm text-white transition-colors delay-150 hover:text-gray-300 hover:delay-0',
className,
)}
onClick={(e) => handleClick(e, href)}
onMouseEnter={() => {
if (timeoutRef.current) {

View File

@@ -1,63 +1,34 @@
"use client";
import { StackedCubes } from "@/components/ui/StackedCubes";
import { Button } from "@/components/Button";
import { motion, useInView } from 'framer-motion';
import { H2, P } from '@/components/Texts';
import { useRef } from "react";
import { FadeIn } from "./FadeIn";
export function StackSectionPreview() {
const ref = useRef(null);
const isInView = useInView(ref);
return (
<section ref={ref} className="w-full bg-transparent lg:px-0 py-24 px-6 relative">
{/* Gradient Blob Component */}
<motion.div
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 0.4 } : { opacity: 0 }}
transition={{ duration: 1, delay: 0.1 }}
className="absolute w-[400px] h-[200px] bg-gradient-to-br from-[#505050] to-[#7e7e7e] rounded-full blur-[150px] bottom-[200px] left-[-150px] z-0"
/>
<motion.div
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 0.5 } : { opacity: 0 }}
transition={{ duration: 1, delay: 0.15 }}
className="absolute w-[200px] h-[100px] bg-gradient-to-br from-[#505050] to-[#7e7e7e] rounded-full blur-[150px] top-[200px] right-[-150px] z-0"
/>
<section className="w-full bg-transparent lg:px-0 py-12 lg:py-24 px-6 relative">
<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-left lg:text-left lg:col-span-1 order-1 lg:order-1">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}
transition={{ duration: 0.8, delay: 0.2 }}
>
<div className="text-center lg:text-left lg:col-span-1 order-1 lg:order-1">
<FadeIn>
<H2 className="">
The Mycelium Stack
</H2>
</motion.div>
</FadeIn>
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}
transition={{ duration: 0.8, delay: 0.6 }}
>
<FadeIn>
<P className="mx-auto mt-8 max-w-3xl" color="light">
Built with Mycelium technology, our AI infrastructure ensures unbreakable networks, complete data sovereignty, ultra-secure agent-human communication, and unhackable data storage systems.
</P>
</motion.div>
</FadeIn>
</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">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}
transition={{ duration: 0.8, delay: 0.3 }}
>
<div className="lg:col-span-2 flex items-center justify-center lg:justify-start order-2 lg:order-2 mt-8 lg:mt-0">
<FadeIn>
<StackedCubes />
</motion.div>
</FadeIn>
</div>
</div>
</div>

View File

@@ -42,7 +42,7 @@ export function Steps() {
<H2 className="text-3xl font-medium tracking-tight" color="light">
Deploy Scalable LLMs and AI Agents in Seconds
</H2>
<P className="mt-6 text-lg" color="light">
<P className="mt-6" color="light">
Launch and scale intelligence on your own terms. Mycelium Cloud makes it simple to deploy models, integrate knowledge, and run everything on a network you control.
</P>
</motion.div>
@@ -50,7 +50,7 @@ export function Steps() {
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 1 } : { opacity: 0 }}
transition={{ duration: 0.5, delay: 0.2, staggerChildren: 0.2 }}
className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-16 text-base/7 sm:grid-cols-2 lg:mx-0 lg:max-w-none lg:grid-cols-3"
className="mx-auto lg:mt-12 mt-8 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-8 text-base/7 sm:grid-cols-2 sm:gap-y-16 lg:mx-0 lg:max-w-none lg:grid-cols-3"
>
{features.map((feature, index) => (
<motion.li
@@ -58,7 +58,7 @@ export function Steps() {
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
transition={{ duration: 0.5, delay: 0.3 + index * 0.2 }}
className="rounded-2xl border border-white/20 bg-black/20 p-8 backdrop-blur-sm transition-all duration-300 ease-in-out hover:scale-105 hover:border-white/40 hover:bg-black/40"
className="rounded-2xl border border-white/20 bg-black/20 lg:py-8 lg:px-8 py-6 px-6 backdrop-blur-sm transition-all duration-300 ease-in-out hover:scale-105 hover:border-white/40 hover:bg-black/40"
>
<feature.icon className="h-8 w-8 mb-4 text-white" />
<CT as="span" className="font-semibold" color="light">{feature.name}</CT>

View File

@@ -7,7 +7,7 @@ const colorVariants = {
primary: 'text-[#fffff]',
secondary: 'text-gray-200',
custom: 'text-[#015eff]',
light: 'text-white',
light: '[#fcfcfc]',
} as const
type TextOwnProps = {
@@ -53,9 +53,9 @@ const createTextComponent = <DefaultElement extends React.ElementType>(
export const H1 = createTextComponent('h1', 'text-5xl font-medium tracking-tight text-balance lg:text-8xl')
export const PL = createTextComponent('p', 'text-2xl font-medium text-pretty leading-[1.2] lg:text-3xl')
export const H2 = createTextComponent('h2', 'text-3xl font-medium text-pretty lg:text-4xl')
export const P = createTextComponent('p', 'text-lg font-normal text-pretty lg:text-xl')
export const P = createTextComponent('p', 'text-lg font-normal text-pretty leading-snug lg:text-xl lg:leading-normal')
export const H3 = createTextComponent('h3', 'text-2xl lg:text-3xl font-medium')
export const H4 = createTextComponent('h4', 'text-xl lg:text-2xl font-semibold leading-tight')
export const H4 = createTextComponent('h4', 'text-xl lg:text-2xl font-semibold leading-[1.15]')
export const CT = createTextComponent('span', 'text-lg lg:text-xl font-semibold text-center')
export const CP = createTextComponent('p', 'text-sm lg:text-base leading-relaxed font-light')
export const NL = createTextComponent('span', 'text-lg font-semibold leading-6')
export const CP = createTextComponent('p', 'text-sm lg:text-base leading-[1.525] font-light')
export const NL = createTextComponent('span', 'text-lg font-semibold leading-[1.23]')

View File

@@ -7,7 +7,7 @@ import { CountUpNumber } from './CountUpNumber'
export function WorldMap() {
return (
<div className="relative min-h-screen w-full overflow-hidden -top-20 flex flex-col">
<div className="relative min-h-screen w-full overflow-hidden top-0 flex py-12 flex-col">
{/* Background video */}
<video
autoPlay
@@ -48,18 +48,18 @@ export function WorldMap() {
className="flex-1 flex items-center justify-center"
>
<div className="relative w-[450px] h-[450px] md:w-[600px] md:h-[600px]">
<Globe className="absolute inset-0 w-full h-full -left-24" />
<Globe className="absolute inset-0 w-full h-full left-0 lg:-left-24" />
</div>
</motion.div>
{/* Cards Right Column */}
<div className="relative flex-1">
<div className="relative flex-1 lg:h-auto h-[700px] flex flex-col lg:block items-center gap-y-4 mt-8 lg:mt-0">
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.4 }}
whileHover={{ scale: 1.05 }}
className="absolute top-12 -left-12 rounded-2xl bg-white/5 backdrop-blur-md border border-white/10 px-4 py-8 shadow-md w-80"
className="lg:absolute lg:top-12 lg:-left-12 rounded-xl bg-white/5 backdrop-blur-md border border-white/10 px-4 lg:py-8 py-6 shadow-md w-80"
>
<CT color="light" className="uppercase tracking-wide">CORES</CT>
<CountUpNumber end={54958} color="light" className="mt-2 text-3xl font-bold" />
@@ -73,7 +73,7 @@ export function WorldMap() {
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.5 }}
whileHover={{ scale: 1.05 }}
className="absolute -top-10 right-0 rounded-2xl bg-white/5 backdrop-blur-md border border-white/10 px-4 py-8 shadow-md w-80"
className="lg:absolute lg:-top-10 lg:right-0 rounded-xl bg-white/5 backdrop-blur-md border border-white/10 px-4 lg:py-8 py-6 shadow-md w-80"
>
<CT color="light" className="uppercase tracking-wide">NODES</CT>
<CountUpNumber end={1493} color="light" className="mt-2 text-3xl font-bold" />
@@ -87,12 +87,12 @@ export function WorldMap() {
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.6 }}
whileHover={{ scale: 1.05 }}
className="absolute bottom-28 -left-12 rounded-2xl bg-white/5 backdrop-blur-md border border-white/10 px-4 py-8 shadow-md w-80"
className="lg:absolute lg:bottom-28 lg:-left-12 rounded-xl bg-white/5 backdrop-blur-md border border-white/10 px-4 lg:py-8 py-6 shadow-md w-80"
>
<CT color="light" className="uppercase tracking-wide">SSD CAPACITY</CT>
<CountUpNumber end={5388956} color="light" className="mt-2 text-3xl font-bold" />
<CP color="light" className="mt-2 text-sm">
Total amount of storage (SSD, HDD, & RAM) on the grid.
Total GB amount of storage (SSD, HDD, & RAM) on the grid.
</CP>
</motion.div>
@@ -101,7 +101,7 @@ export function WorldMap() {
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.7 }}
whileHover={{ scale: 1.05 }}
className="absolute top-44 right-0 rounded-2xl bg-white/5 backdrop-blur-md border border-white/10 px-4 py-8 shadow-md w-80"
className="lg:absolute lg:top-44 lg:right-0 rounded-xl bg-white/5 backdrop-blur-md border border-white/10 px-4 lg:py-8 py-6 shadow-md w-80"
>
<CT color="light" className="uppercase tracking-wide">COUNTRIES</CT>
<CountUpNumber end={44} color="light" className="mt-2 text-3xl font-bold" />

View File

@@ -11,6 +11,7 @@ interface CubeProps {
index: number;
onHover: () => void;
onLeave: () => void;
onClick: () => void;
}
const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({ index, ...props }) => (
@@ -42,13 +43,14 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({
</svg>
);
export function Cube({ title, descriptionTitle, description, isActive, index, onHover, onLeave }: CubeProps) {
export function Cube({ title, descriptionTitle, description, isActive, index, onHover, onLeave, onClick }: CubeProps) {
return (
<div className="relative flex flex-col items-center">
<motion.div
className="relative cursor-pointer"
onMouseEnter={onHover}
onMouseLeave={onLeave}
onClick={onClick}
style={{
zIndex: 10 - index,
}}
@@ -123,25 +125,7 @@ export function Cube({ title, descriptionTitle, description, isActive, index, on
)}
{/* 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>
</motion.div>
</div>
);
}

View File

@@ -33,14 +33,22 @@ const stackData = [
export function StackedCubes() {
const [active, setActive] = useState<string | null>("agent");
const [selectedForMobile, setSelectedForMobile] = useState<string | null>("agent");
const handleCubeClick = (id: string) => {
setSelectedForMobile(prev => (prev === id ? null : id));
};
const selectedMobileLayer = stackData.find(layer => layer.id === selectedForMobile);
return (
<div className="flex flex-col items-center">
<div
className="relative w-full flex items-center justify-center lg:justify-start min-h-[600px] sm:min-h-[700px] lg:min-h-[600px]"
className="relative w-full flex items-center justify-center lg:justify-center min-h-[450px] lg:min-h-[400px]"
onMouseLeave={() => setActive("agent")}
>
<motion.div
className="relative ml-0 sm:ml-4 lg:ml-8 h-[600px] w-96"
className="relative lg:pl-0 pl-6 h-[300px] lg:h-[400px] w-64 sm:w-80 lg:w-96 scale-120 lg:scale-100"
animate={{ y: ["-8px", "8px"] }}
transition={{
duration: 4,
@@ -54,7 +62,7 @@ export function StackedCubes() {
key={layer.id}
className="absolute"
style={{
top: `${index * 140}px`,
top: `calc(${index * 30}% - ${index * 10}px)`,
zIndex: active === layer.id ? 20 : 10 - index,
}}
>
@@ -66,10 +74,22 @@ export function StackedCubes() {
index={index}
onHover={() => setActive(layer.id)}
onLeave={() => {}}
onClick={() => handleCubeClick(layer.id)}
/>
</div>
))}
</motion.div>
</div>
{selectedMobileLayer && (
<div className="lg:hidden w-full max-w-md p-6 -mt-8 bg-gray-800/50 rounded-lg">
<h4 className="text-white text-lg font-semibold mb-2 text-center">
{selectedMobileLayer.descriptionTitle}
</h4>
<p className="text-gray-300 text-sm leading-relaxed text-center">
{selectedMobileLayer.description}
</p>
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,79 @@
import { cn } from "@/lib/utils";
import { CT, CP } from "@/components/Texts";
import Image from 'next/image';
import React from 'react';
import { motion } from 'framer-motion';
export const BentoGrid = ({
className,
children,
}: {
className?: string;
children?: React.ReactNode;
}) => {
return (
<div
className={cn(
"mx-4 grid max-w-6xl grid-cols-1 gap-4 lg:grid-cols-3",
className,
)}
>
{children}
</div>
);
};
interface BentoGridItemProps {
className?: string;
title?: string | React.ReactNode;
subtitle?: string | React.ReactNode;
description?: string | React.ReactNode;
img?: string;
video?: string;
rowHeight?: string;
}
export const BentoGridItem = React.forwardRef<HTMLDivElement, BentoGridItemProps>(
({ className, title, subtitle, description, img, video, rowHeight }, ref) => {
return (
<div
ref={ref}
className={cn(
"group/bento shadow-input row-span-1 flex flex-col justify-between rounded-xl border border-black bg-black/10 backdrop-blur-md transition-all duration-300 ease-in-out hover:scale-105 hover:border-black hover:bg-black/40",
rowHeight ? rowHeight : "h-full",
className
)}
>
<div className="relative w-full h-[65%] min-h-[6rem] bg-transparent overflow-hidden">
{video ? (
<video
src={video}
autoPlay
loop
muted
playsInline
className="w-full h-full object-cover opacity-90 group-hover/bento:opacity-100 transition-opacity duration-300"
/>
) : img ? (
<Image
src={img}
alt={title as string}
width={300}
height={300}
className="w-full h-full object-cover opacity-90 group-hover/bento:opacity-100 transition-opacity duration-300"
/>
) : null}
</div>
<div className="p-4 transition bg-white/5 hover:bg-white/7 backdrop-blur-md duration-200 group-hover/bento:translate-x-2 ">
<CT>{title}</CT>
<CP className="font-medium">{subtitle}</CP>
<CP className="mt-2">{description}</CP>
</div>
</div>
);
}
);
BentoGridItem.displayName = "BentoGridItem";
export const MotionBentoGridItem = motion(BentoGridItem);

View File

@@ -0,0 +1,21 @@
'use client'
import { useState, useEffect } from 'react'
export function useMediaQuery(query: string) {
const [matches, setMatches] = useState(false)
useEffect(() => {
const media = window.matchMedia(query)
if (media.matches !== matches) {
setMatches(media.matches)
}
const listener = () => {
setMatches(media.matches)
}
media.addEventListener('change', listener)
return () => media.removeEventListener('change', listener)
}, [matches, query])
return matches
}

View File

@@ -0,0 +1,39 @@
'use client'
import { useState, useEffect } from 'react';
// 🔧 Carousel Config
const desktopConfig = {
GAP: 300,
ROT_Y: 18,
DEPTH: 210,
SCALE_DROP: 0.12,
};
const mobileConfig = {
GAP: 110, // Smaller gap for mobile
ROT_Y: 0, // Flatter view on mobile
DEPTH: 150, // Less depth
SCALE_DROP: 0.1, // Less aggressive scaling
};
export const useResponsiveCarousel = () => {
const [config, setConfig] = useState(desktopConfig);
useEffect(() => {
const checkScreenSize = () => {
if (window.innerWidth < 768) {
setConfig(mobileConfig);
} else {
setConfig(desktopConfig);
}
};
checkScreenSize();
window.addEventListener('resize', checkScreenSize);
return () => window.removeEventListener('resize', checkScreenSize);
}, []);
return config;
};

13
src/pages/_document.tsx Normal file
View File

@@ -0,0 +1,13 @@
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}