Compare commits
57 Commits
9277bc7105
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 76e0bdb7be | |||
| 85c041ab49 | |||
| 0231c4835c | |||
| 4efc563aa9 | |||
| d70c2b6874 | |||
| db4c2d8ea0 | |||
| 28bef26fbc | |||
| 73fe0e7c8e | |||
| c59c09eee8 | |||
| 593201ae10 | |||
| b46df781f8 | |||
| f44662829d | |||
| a663c32f53 | |||
| 2f3dea92a2 | |||
| 75b660d81e | |||
| 3f86c7e87f | |||
| 1c2c274848 | |||
| 74d5bae622 | |||
| 1577eb6c6c | |||
| f4519ec8bf | |||
| c09928018c | |||
| f5e5063ba1 | |||
| 54333f2bd5 | |||
| e646198255 | |||
| 5f1774f03c | |||
| d52190268a | |||
| 8b2bbb2536 | |||
| 6f8edae241 | |||
| acbbc7445d | |||
| 6c3f1afecf | |||
| 7b5afa588e | |||
| 4cc20ac13f | |||
| 6116c5b87c | |||
| 602b78b5bd | |||
| 9a4f347ee8 | |||
| 017fc41d2b | |||
| ab14a5a8e5 | |||
| 41d4c3a054 | |||
| a362985d4c | |||
| 1d66fd60a4 | |||
| 43d995bbc2 | |||
| 0dedde3592 | |||
| d5b9303d94 | |||
| 3a240177c4 | |||
| 0479b7330a | |||
| a78bc67ed3 | |||
| a035500c34 | |||
| 2b5c502724 | |||
| a462afc8b2 | |||
| bf78cde2d8 | |||
| cde6c90033 | |||
| f5ab743987 | |||
| 45364a7452 | |||
| b7f25d712f | |||
| 2fdcb3697d | |||
| 204625b9a8 | |||
| 02557fcb82 |
158
README.md
@@ -1,35 +1,151 @@
|
||||
# Mycelium
|
||||
# Project Mycelium 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).
|
||||
- **Repository:** [https://git.ourworld.tf/ourworld_web/www_project_mycelium/](https://git.ourworld.tf/ourworld_web/www_project_mycelium/)
|
||||
|
||||
## Getting started
|
||||
- **Main Branch (Production):** [https://project.mycelium.tf/](https://project.mycelium.tf/)
|
||||
- **Dev Branch (Staging):** [https://www2.project.mycelium.tf/](https://www2.project.mycelium.tf/)
|
||||
|
||||
To get started with this template, first install the npm dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
---
|
||||
|
||||
Next, run the development server:
|
||||
## About
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
This is the official website for Mycelium Cloud, built using Next.js and Tailwind CSS.
|
||||
|
||||
Finally, open [http://localhost:3000](http://localhost:3000) in your browser to view the website.
|
||||
---
|
||||
|
||||
## Customizing
|
||||
## Technologies
|
||||
|
||||
You can start editing this template by modifying the files in the `/src` folder. The site will auto-update as you edit these files.
|
||||
- **Framework**: [Next.js](https://nextjs.org/)
|
||||
- **Language**: [TypeScript](https://www.typescriptlang.org/)
|
||||
- **Styling**: [Tailwind CSS](https://tailwindcss.com/)
|
||||
|
||||
## License
|
||||
---
|
||||
|
||||
This site template is a commercial product and is licensed under the [Tailwind Plus license](https://tailwindcss.com/plus/license).
|
||||
## Dependencies
|
||||
|
||||
## Learn more
|
||||
- **UI**: [@headlessui/react](https://headlessui.com/)
|
||||
- **Animation**: [framer-motion](https://www.framer.com/motion/)
|
||||
- **Utilities**: [clsx](https://github.com/lukeed/clsx), [use-debounce](https://github.com/xnimorz/use-debounce)
|
||||
|
||||
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
|
||||
## File Structure
|
||||
|
||||
- **Pages**: To edit the content of a specific page, navigate to `src/app/(main)/`.
|
||||
- **Components**: Reusable components are located in `src/components/`.
|
||||
- **Images**: Add or modify images in the `public/images/` directory.
|
||||
- **CSS**: Global styles can be found in `src/styles/tailwind.css`. Most styling is done using Tailwind CSS utility classes directly in the `.tsx` files.
|
||||
|
||||
---
|
||||
|
||||
## Branding
|
||||
|
||||
- **Font**: The primary font used is [Mulish](https://fonts.google.com/specimen/Mulish).
|
||||
- **Logos**: Project logos are stored in `public/images/logos/`.
|
||||
|
||||
---
|
||||
|
||||
## Get Started
|
||||
|
||||
Follow these steps to get the project running locally:
|
||||
|
||||
1. **Install Dependencies**:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. **Build the Project**:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
3. **Start the Development Server**:
|
||||
|
||||
```bash
|
||||
npm run start
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Guide
|
||||
|
||||
This project follows a modular, component-based architecture. Pages are assembled by combining reusable components into a single layout.
|
||||
|
||||
### Homepage Structure
|
||||
|
||||
The homepage (`src/app/(main)/page.tsx`) is composed of the following components:
|
||||
|
||||
- `HomeHero`
|
||||
- `WorldMap`
|
||||
- `StackSectionPreview`
|
||||
- `Steps`
|
||||
- `Companies`
|
||||
- `ClickableGallery`
|
||||
- `BentoReviews`
|
||||
- `CallToAction`
|
||||
|
||||
To edit a specific section of the homepage, navigate to `src/components/` and modify the corresponding component file.
|
||||
|
||||
### Base Layout
|
||||
|
||||
The main layout for the application is defined in `src/components/Layout.tsx`. This file includes the `Header` and `Footer` and wraps the primary content of each page.
|
||||
|
||||
### Creating a New Page
|
||||
|
||||
To create a new page, follow these steps:
|
||||
|
||||
1. **Create a Folder**: Inside the `src/app/(main)/` directory, create a new folder with the desired URL slug for your page (e.g., `new-page`).
|
||||
|
||||
2. **Create the Page File**: Inside the new folder, create a `page.tsx` file.
|
||||
|
||||
3. **Add Page Content**: Compose your page by importing and using the reusable components from `src/components/`. For example:
|
||||
|
||||
```tsx
|
||||
import { HomeHero } from '@/components/HomeHero'
|
||||
import { Faqs } from '@/components/Faqs'
|
||||
|
||||
export default function NewPage() {
|
||||
return (
|
||||
<>
|
||||
<HomeHero />
|
||||
<Faqs />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The new page will be accessible at `http://localhost:3000/new-page`.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
- **Never update the `main` branch directly.** All changes must be reviewed and merged by the team through a pull request.
|
||||
- **Always work on the `development` branch.** Create a feature branch from `development` and submit your pull request to `development`.
|
||||
- **Request a review.** After submitting your pull request, ask the team to review and accept it into the `main` branch.
|
||||
|
||||
---
|
||||
|
||||
## Report an Error
|
||||
|
||||
To report an issue, please use the following link and provide the requested information:
|
||||
|
||||
- **Issue Tracker**: [git.ourworld.tf/ourworld_web/HOME/issues/new](https://git.ourworld.tf/ourworld_web/HOME/issues/new) and tag **OW Website & Wiki Project 2025**
|
||||
|
||||
- See the current web rpoject on [OW Website & Wiki Project 2025](https://git.ourworld.tf/ourworld_web/-/projects/153)
|
||||
|
||||
When reporting an issue, please include:
|
||||
|
||||
- **URL**: The page where the error occurred.
|
||||
- **Repo**: The repository you are working with.
|
||||
- **Branch**: The specific branch you are on.
|
||||
- **Problem**: A detailed description of the problem.
|
||||
|
||||
---
|
||||
|
||||
## Questions
|
||||
|
||||
If you have any questions, you can reach out to [sashaastiadi](https://git.ourworld.tf/sashaastiadi).
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"registries": {
|
||||
"@magicui": "https://magicui.design/r/{name}.json"
|
||||
"@magicui": "https://magicui.design/r/{name}.json",
|
||||
"@aceternity": "https://ui.aceternity.com/registry/{name}.json"
|
||||
}
|
||||
}
|
||||
|
||||
1
next-env.d.ts
vendored
@@ -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
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
BIN
public/images/cloud.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/images/gallery/branding.jpg
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
public/images/gallery/calendar.jpg
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
public/images/gallery/code.jpg
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
public/images/gallery/data.jpg
Normal file
|
After Width: | Height: | Size: 177 KiB |
BIN
public/images/gallery/datasets.jpg
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
public/images/gallery/docs.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
public/images/gallery/flow.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
public/images/gallery/interface.jpg
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
public/images/gallery/market.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
public/images/gallery/structure.jpg
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
public/images/gallery/translate.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
public/images/mchip.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/images/placeholder.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/videos/agent.mp4
Normal file
BIN
public/videos/cloud.mp4
Normal file
BIN
public/videos/cloud2.mp4
Normal file
BIN
public/videos/deterministic.mp4
Normal file
BIN
public/videos/fungistor.mp4
Normal file
BIN
public/videos/herodb.mp4
Normal file
BIN
public/videos/mesh.mp4
Normal file
BIN
public/videos/sandbox.mp4
Normal file
BIN
public/videos/universal.mp4
Normal file
@@ -1 +0,0 @@
|
||||
export { Layout as default } from '@/components/Layout'
|
||||
@@ -3,9 +3,11 @@ import { Faqs } from '@/components/Faqs'
|
||||
import { UseCases } from '@/components/UseCases'
|
||||
import { Steps } from '@/components/Steps'
|
||||
import { HomeHero } from '@/components/HomeHero'
|
||||
import { HomeHeroLight } from '@/components/HomeHeroLight'
|
||||
import { HomeHeroLight2 } from '@/components/HomeHeroLight2'
|
||||
import { HomeAbout } from '@/components/HomeAbout'
|
||||
import { ClickableGallery } from '@/components/ClickableGallery'
|
||||
import { StackSectionPreview } from '@/components/StackSection'
|
||||
import { ClickableGalleryLight } from '@/components/ClickableGalleryLight'
|
||||
import { StackSectionLight } from '@/components/StackSectionLight'
|
||||
import { Companies } from '@/components/Companies'
|
||||
import { CallToAction } from '@/components/CallToAction'
|
||||
import { ScrollDown } from '@/components/ui/ScrollDown'
|
||||
@@ -13,35 +15,33 @@ 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 (
|
||||
<>
|
||||
<section id="home-hero">
|
||||
<HomeHero />
|
||||
</section>
|
||||
<section id="about">
|
||||
<StackSectionPreview />
|
||||
<HomeHeroLight2 />
|
||||
</section>
|
||||
<section id="network">
|
||||
<WorldMap />
|
||||
</section>
|
||||
<section id="deploy">
|
||||
<section id="technologies">
|
||||
<StackSectionLight />
|
||||
</section>
|
||||
<section id="how-it-works">
|
||||
<Steps />
|
||||
</section>
|
||||
<section id="llms">
|
||||
<Companies />
|
||||
</section>
|
||||
<section id="features">
|
||||
<UseCases />
|
||||
</section>
|
||||
<section id="clickable-gallery">
|
||||
<ClickableGallery />
|
||||
<ClickableGalleryLight />
|
||||
</section>
|
||||
<section id="bento-reviews">
|
||||
<BentoReviews />
|
||||
</section>
|
||||
<section id="get-started">
|
||||
<GetStarted />
|
||||
</section>
|
||||
<section id="call-to-action">
|
||||
<CallToAction />
|
||||
</section>
|
||||
</>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { type Metadata } from 'next'
|
||||
import { Mulish } from 'next/font/google'
|
||||
import Script from 'next/script'
|
||||
import { Inter } from 'next/font/google'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import '@/styles/tailwind.css'
|
||||
|
||||
const mulish = Mulish({
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
display: 'swap',
|
||||
variable: '--font-mulish',
|
||||
variable: '--font-inter',
|
||||
})
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -25,7 +26,20 @@ export default function RootLayout({
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" className={clsx('antialiased', mulish.variable)}>
|
||||
<html lang="en" className={clsx('antialiased', inter.variable)}>
|
||||
<head>
|
||||
{/* MailerLite Universal */}
|
||||
<Script id="mailerlite-universal" strategy="afterInteractive">
|
||||
{`(function(m,a,i,l,e,r){ m['MailerLiteObject']=e;function f(){
|
||||
var c={ a:arguments,q:[]};var r=this.push(c);return "number"!=typeof r?r:f.bind(c.q);}
|
||||
f.q=f.q||[];m[e]=m[e]||f.bind(f.q);m[e].q=m[e].q||f.q;r=a.createElement(i);
|
||||
var _=a.getElementsByTagName(i)[0];r.async=1;r.src=l+'?v'+(~~(new Date().getTime()/1000000));
|
||||
_.parentNode.insertBefore(r,_);})(window, document, 'script', 'https://static.mailerlite.com/js/universal.js', 'ml');
|
||||
|
||||
var ml_account = ml('accounts', '1778010', 'x2d3d9f8n1', 'load');`}
|
||||
</Script>
|
||||
{/* End MailerLite Universal */}
|
||||
</head>
|
||||
<body className="bg-black text-white">{children}</body>
|
||||
</html>
|
||||
)
|
||||
|
||||
@@ -57,7 +57,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-gray-200 p-8 dark:border-gray-700"
|
||||
className="rounded-2xl border border-gray-300 p-8 dark:border-gray-700"
|
||||
>
|
||||
<feature.icon className="h-8 w-8 mb-4 text-[#2F3178]" />
|
||||
<CT as="span" className="font-semibold">{feature.name}</CT>
|
||||
|
||||
@@ -22,7 +22,7 @@ export function WorldMap() {
|
||||
{/* Top Left Intro Text */}
|
||||
<div className="absolute top-0 left-0 px-6 py-24 z-10 max-w-lg">
|
||||
<H4 color="light">Mycelium Network is Live.</H4>
|
||||
<CP className="mt-6 text-base leading-relaxed font-light" color="light">
|
||||
<CP className="hidden mt-6 text-base leading-relaxed font-light" color="light">
|
||||
Mycelium Cloud's advancement technology enables anyone to deploy
|
||||
their own Internet infrastructure, anywhere.
|
||||
</CP>
|
||||
|
||||
@@ -1,60 +1,171 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import React from 'react'
|
||||
import { CT, CP } from '@/components/Texts'
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SectionHeader, P, Eyebrow } 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 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.' },
|
||||
]
|
||||
const items = [
|
||||
{
|
||||
title: 'FungiStor',
|
||||
subtitle: 'Long-Term AI Memory',
|
||||
description: 'Erasure coding + compression slash storage bloat by up to 10× vs basic replication. Source-encrypted shards are geo-dispersed—lose pieces, rebuild perfectly from a quorum.',
|
||||
video: "/videos/fungistor.mp4",
|
||||
},
|
||||
{
|
||||
title: 'HeroDB',
|
||||
subtitle: 'Active AI Memory',
|
||||
description: 'Multimodal vector+keyword retrieval makes RAG feel instant across text, image, audio. Time-aware, policy-guarded context keeps results fresh while access stays governed.',
|
||||
video: "/videos/herodb.mp4",
|
||||
},
|
||||
{
|
||||
title: 'MOS Sandboxes',
|
||||
subtitle: 'Secure Agent Workspaces',
|
||||
description: 'Attested, signed workspaces spin up ≈5s worldwide—ready to execute. Hardware isolation and scoped egress: run hard, tear down clean, zero residue.',
|
||||
video: "/videos/sandbox.mp4",
|
||||
},
|
||||
{
|
||||
title: 'Mycelium Mesh',
|
||||
subtitle: 'Secure Communication Network',
|
||||
description: 'A private, public-key fabric with self-healing multi-path routing. Glides through NATs and firewalls—direct, low-latency, no middlemen.',
|
||||
video: "/videos/mesh.mp4",
|
||||
},
|
||||
{
|
||||
title: 'Deterministic Deployment',
|
||||
subtitle: 'Verifiable Code Execution',
|
||||
description: 'Declare intent, get a hash; remote attestation proves that’s what runs. Reproducible builds, signed artifacts, immutable logs—supply chain, sealed.',
|
||||
video: "/videos/deterministic.mp4",
|
||||
},
|
||||
{
|
||||
title: 'Agent Coordination',
|
||||
subtitle: 'Sovereign Workflow Management',
|
||||
description: 'Your private agent conducts swarms of specialists in parallel. Policies fan out work; human checkpoints keep you in command.',
|
||||
video: "/videos/agent.mp4",
|
||||
},
|
||||
{
|
||||
title: 'Universal Interface Layer',
|
||||
subtitle: 'AI Service Gateway',
|
||||
description: 'One gateway to open-source LLMs, tools, APIs, and data—bring your own models & keys. Semantic retrieval, function calling, and metered micropayments built in.',
|
||||
video: "/videos/universal.mp4",
|
||||
},
|
||||
];
|
||||
|
||||
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>
|
||||
<div>
|
||||
<div className="relative isolate py-24 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 ">
|
||||
<Eyebrow color="accent">Components</Eyebrow>
|
||||
<SectionHeader className="text-center">Augmented Intelligence Fabric</SectionHeader>
|
||||
</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">
|
||||
The sovereign substrate for autonomous AI. Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.
|
||||
</P>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
|
||||
{/* 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
|
||||
{/* 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}
|
||||
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>
|
||||
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>
|
||||
|
||||
{/* 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"
|
||||
{/* 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 }}
|
||||
>
|
||||
<CT className="text-white text-lg">{review.title}</CT>
|
||||
<CP className="mt-2 text-gray-400">{review.body}</CP>
|
||||
</div>
|
||||
))}
|
||||
<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>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ const baseStyles = {
|
||||
|
||||
const variantStyles = {
|
||||
solid: {
|
||||
cyan: 'relative overflow-hidden bg-[#005eff] text-white before:absolute before:inset-0 active:before:bg-transparent hover:before:bg-white/10 active:bg-[#005eff] active:text-white/80 before:transition-colors',
|
||||
cyan: 'relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-600 active:text-white/80 before:transition-colors',
|
||||
white:
|
||||
'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70',
|
||||
gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80',
|
||||
},
|
||||
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',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
import { H2, P } from '@/components/Texts'
|
||||
"use client"
|
||||
|
||||
import { SectionHeader, P } from '@/components/Texts'
|
||||
|
||||
export function CallTo() {
|
||||
return (
|
||||
<div className="relative isolate overflow-hidden max-w-5xl mx-auto py-24">
|
||||
<div className="relative isolate overflow-hidden bg-gray-50/10 px-6 py-24 text-center shadow-md shadow-gray-900/5 sm:rounded-3xl sm:px-16 border border-gray-200">
|
||||
<div className="relative isolate overflow-hidden bg-gray-50/10 px-6 py-24 text-center shadow-md shadow-gray-900/5 sm:rounded-3xl sm:px-16 border border-gray-300">
|
||||
<div className="mx-auto max-w-4xl text-center">
|
||||
<H2 color="primary">
|
||||
Say hello to Decentralized AI Agents that are Truly Yours
|
||||
</H2>
|
||||
<P color="custom" className="mt-8 max-w-3xl">
|
||||
<SectionHeader color="primary">
|
||||
Are you Ready?
|
||||
</SectionHeader>
|
||||
<P className="mt-8 max-w-3xl">
|
||||
Why hand out your intelligence to centralized giants when you can build your own?
|
||||
</P>
|
||||
<div className="mt-10 flex items-center justify-center gap-x-6">
|
||||
<a
|
||||
href="#"
|
||||
href="https://calendly.com/sachao/30min"
|
||||
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"
|
||||
className="text-sm/6 font-semibold text-[#2F3178] hover:text-[#2F3178]/80"
|
||||
>
|
||||
Join the Waitlist <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
'use client'
|
||||
|
||||
import { CircleBackground } from '@/components/CircleBackground'
|
||||
import { Container } from '@/components/Container'
|
||||
import { Button } from '@/components/Button'
|
||||
import { FadeIn } from '@/components/FadeIn'
|
||||
import { Eyebrow } from '@/components/Texts'
|
||||
|
||||
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 +22,13 @@ 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">
|
||||
<Eyebrow color="accent"></Eyebrow>
|
||||
<h2 className="text-3xl font-medium tracking-tight text-white sm:text-4xl">
|
||||
Decentralized AI Agents that are Truly Yours
|
||||
</h2>
|
||||
@@ -31,14 +37,25 @@ 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="https://calendly.com/sachao/30min"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Book a Meeting
|
||||
</Button>
|
||||
<Button href="#" variant="outline" color="white">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="white"
|
||||
href="mailto:info@ourworld.tf"
|
||||
>
|
||||
Join the Waitlist
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -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 { SectionHeader, 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,98 +49,99 @@ 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 isn’t about more tools. It’s about one intelligent partner that can do it all. This is your gateway to creativity, automation, and discovery.
|
||||
</P>
|
||||
</motion.div>
|
||||
<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 lg:mt-12">
|
||||
<SectionHeader className="text-center">Agents with Endless Possibilities.</SectionHeader>
|
||||
</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"
|
||||
</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">
|
||||
Your private agent coordinates a team of specialists that spin up on demand, collaborate across your world, and deliver end-to-end results.
|
||||
Many agents, one intelligence—yours.
|
||||
</P>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
<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-[500px]" style={{ perspective: '1600px' }}>
|
||||
<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 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)
|
||||
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"
|
||||
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)}
|
||||
>
|
||||
{/* 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 }}
|
||||
>
|
||||
<div className="relative bg-black flex items-center justify-center">
|
||||
<Image
|
||||
src={item.image}
|
||||
alt={item.text}
|
||||
fill
|
||||
className="object-contain rounded-2xl text-black"
|
||||
width={item.width}
|
||||
height={item.height}
|
||||
className="object-contain text-white"
|
||||
priority={i === VISIBLE}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Arrows */}
|
||||
<div className="absolute inset-y-0 left-8 flex items-center z-50">
|
||||
{/* Arrows */}
|
||||
<div className="absolute inset-y-0 left-8 hidden md:flex items-center z-50">
|
||||
<button
|
||||
onClick={prev}
|
||||
className="bg-white/70 hover:bg-white rounded-full p-3 shadow-lg backdrop-blur-md"
|
||||
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 flex items-center z-50">
|
||||
<div className="absolute inset-y-0 right-8 hidden md:flex items-center z-50">
|
||||
<button
|
||||
onClick={next}
|
||||
className="bg-white/70 hover:bg-white rounded-full p-3 shadow-lg backdrop-blur-md"
|
||||
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 */}
|
||||
<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">
|
||||
{/* 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]}
|
||||
@@ -152,14 +149,32 @@ export function ClickableGallery() {
|
||||
speed={50}
|
||||
repeat={0}
|
||||
/>
|
||||
</H4>
|
||||
<Button href="#" color="cyan" className="text-sm px-4 py-2 lg:text-base">
|
||||
</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>
|
||||
</motion.section>
|
||||
</div>
|
||||
)
|
||||
</FadeIn>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
180
src/components/ClickableGalleryLight.tsx
Normal file
@@ -0,0 +1,180 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useResponsiveCarousel } from '@/hooks/useResponsiveCarousel';
|
||||
import Image from 'next/image'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { wrap } from 'popmotion'
|
||||
import { Button } from '@/components/Button';
|
||||
import { SectionHeader, P, CT, Eyebrow } 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/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 AUTOPLAY_MS = 3200;
|
||||
|
||||
export function ClickableGalleryLight() {
|
||||
const [active, setActive] = useState(0);
|
||||
const [hovering, setHovering] = useState(false);
|
||||
const { GAP, ROT_Y, DEPTH, SCALE_DROP } = useResponsiveCarousel();
|
||||
|
||||
// autoplay
|
||||
useEffect(() => {
|
||||
if (hovering) return
|
||||
const id = setInterval(() => setActive((i) => wrap(0, galleryItems.length, i + 1)), AUTOPLAY_MS)
|
||||
return () => clearInterval(id)
|
||||
}, [hovering])
|
||||
|
||||
const indices = useMemo(
|
||||
() => [...Array(VISIBLE * 2 + 1)].map((_, i) => wrap(0, galleryItems.length, active + i - VISIBLE)),
|
||||
[active]
|
||||
)
|
||||
|
||||
const next = () => setActive((i) => wrap(0, galleryItems.length, i + 1))
|
||||
const prev = () => setActive((i) => wrap(0, galleryItems.length, i - 1))
|
||||
|
||||
return (
|
||||
<div className="bg-[#FAFAFA]">
|
||||
<div className="relative isolate pt-8 pb-0 text-center w-full">
|
||||
<FadeIn transition={{ duration: 0.8, delay: 0.1 }}>
|
||||
<div className="mx-auto max-w-5xl lg:mt-12">
|
||||
<Eyebrow color="accent">Use Cases</Eyebrow>
|
||||
<SectionHeader className="text-center" color="dark">Agents with Endless Possibilities.</SectionHeader>
|
||||
</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="dark">
|
||||
Your private agent coordinates a team of specialists that spin up on demand, collaborate across your world, and deliver end-to-end results.
|
||||
Many agents, one intelligence—yours.
|
||||
</P>
|
||||
</div>
|
||||
</FadeIn>
|
||||
</div>
|
||||
<FadeIn transition={{ duration: 1, delay: 0.4 }}>
|
||||
<section
|
||||
className="relative w-full flex items-center justify-center overflow-hidden -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(0, 0, 0, 0.1)' : 'none',
|
||||
}}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ type: 'spring', stiffness: 220, damping: 26 }}
|
||||
onClick={() => setActive(idx)}
|
||||
>
|
||||
<div className="relative bg-gray-100 flex items-center justify-center">
|
||||
<Image
|
||||
src={item.image}
|
||||
alt={item.text}
|
||||
width={item.width}
|
||||
height={item.height}
|
||||
className="object-contain"
|
||||
priority={i === VISIBLE}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Arrows */}
|
||||
<div className="absolute inset-y-0 left-8 hidden md:flex items-center z-50">
|
||||
<button
|
||||
onClick={prev}
|
||||
className="bg-white/50 rounded-full p-2 shadow-lg backdrop-blur-md text-black"
|
||||
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-white/50 rounded-full p-2 shadow-lg backdrop-blur-md text-black"
|
||||
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-gray-100/80 shadow-[0_8px_40px_rgba(0,0,0,0.15)] px-12 backdrop-blur">
|
||||
<CT as="h4" className="max-w-[820px] h-[72px] flex items-center" color="dark">
|
||||
<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-gray-100/80 p-4 backdrop-blur-md">
|
||||
<CT as="h4" className="w-full text-left h-[72px] leading-tight flex items-center" color="dark">
|
||||
<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>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { H2, P } from '@/components/Texts';
|
||||
import { P, Eyebrow } from '@/components/Texts';
|
||||
import { InfiniteMovingCards } from "@/components/magicui/infinite-moving-cards";
|
||||
|
||||
|
||||
@@ -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 py-4 mb-12">
|
||||
<div className="relative z-10 mx-auto w-full max-w-6xl p-4">
|
||||
|
||||
{/* Heading */}
|
||||
@@ -50,7 +50,8 @@ export function Companies() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 1 }}
|
||||
>
|
||||
<P className="text-gray-100 text-center mb-6">
|
||||
<Eyebrow color="accent"></Eyebrow>
|
||||
<P className="hidden min-xl:text-gray-100 text-center mb-6">
|
||||
Mycelium Cloud allows you to deploy and scale AI agents from top global providers on a decentralized, privacy-first infrastructure.
|
||||
</P>
|
||||
</motion.div>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import CountUp from 'react-countup'
|
||||
import { H2 } from './Texts'
|
||||
import { SectionHeader } from './Texts'
|
||||
|
||||
interface CountUpNumberProps {
|
||||
end: number
|
||||
className?: string
|
||||
color?: 'light' | 'primary' | 'secondary' | 'custom'
|
||||
color?: 'light' | 'primary' | 'secondary'
|
||||
}
|
||||
|
||||
export function CountUpNumber({ end, className, color }: CountUpNumberProps) {
|
||||
return (
|
||||
<H2 color={color} className={className}>
|
||||
<SectionHeader color={color} className={className}>
|
||||
<CountUp end={end} duration={2.75} separator="," />
|
||||
</H2>
|
||||
</SectionHeader>
|
||||
)
|
||||
}
|
||||
|
||||
28
src/components/FadeIn.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export function Faqs() {
|
||||
id="faqs"
|
||||
ref={ref}
|
||||
aria-labelledby="faqs-title"
|
||||
className="border-t border-gray-200 py-20 sm:py-32 relative overflow-hidden"
|
||||
className="border-t border-gray-300 py-20 sm:py-32 relative overflow-hidden"
|
||||
>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
|
||||
@@ -3,7 +3,7 @@ import clsx from 'clsx'
|
||||
|
||||
const formClasses = {
|
||||
light:
|
||||
'block w-full appearance-none rounded-lg border border-gray-200 bg-white py-[calc(--spacing(2)-1px)] px-[calc(--spacing(3)-1px)] text-gray-900 placeholder:text-gray-400 focus:border-cyan-500 focus:outline-none focus:ring-cyan-500 sm:text-sm',
|
||||
'block w-full appearance-none rounded-lg border border-gray-300 bg-white py-[calc(--spacing(2)-1px)] px-[calc(--spacing(3)-1px)] text-gray-900 placeholder:text-gray-400 focus:border-cyan-500 focus:outline-none focus:ring-cyan-500 sm:text-sm',
|
||||
dark:
|
||||
'block w-full appearance-none rounded-lg border border-gray-600 bg-transparent py-[calc(--spacing(2)-1px)] px-[calc(--spacing(3)-1px)] text-white placeholder:text-gray-400 focus:border-cyan-500 focus:outline-none focus:ring-cyan-500 sm:text-sm',
|
||||
}
|
||||
|
||||
@@ -24,55 +24,9 @@ export function Footer() {
|
||||
return (
|
||||
<footer id="footer" className="border-t border-gray-800">
|
||||
<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>
|
||||
<div className="flex items-center text-white">
|
||||
<Logomark className="h-10 w-10 flex-none fill-cyan-500" />
|
||||
<div className="ml-4">
|
||||
<p className="text-base font-semibold">Project Mycelium</p>
|
||||
<p className="mt-1 text-sm">Unleash the Power of Decentralized Networks</p>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="mt-11 flex gap-8">
|
||||
<NavLinks />
|
||||
</nav>
|
||||
</div>
|
||||
<div className="group relative -mx-4 flex items-center self-stretch p-4 transition-colors hover:bg-gray-800/50 sm:self-auto sm:rounded-2xl lg:mx-0 lg:self-auto lg:p-6">
|
||||
<div className="relative flex h-24 w-24 flex-none items-center justify-center">
|
||||
<QrCodeBorder className="absolute inset-0 h-full w-full stroke-gray-700 transition-colors group-hover:stroke-[#015eff]" />
|
||||
<Image src={qrCode} alt="" unoptimized />
|
||||
</div>
|
||||
<div className="ml-8 lg:w-64">
|
||||
<p className="text-base font-semibold text-white">
|
||||
<Link href="https://threefoldfaq.crisp.help/en/">
|
||||
<span className="absolute inset-0 sm:rounded-2xl" />
|
||||
Talk to a human
|
||||
</Link>
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-gray-400">
|
||||
Get in touch with us for any inquiries or questions.
|
||||
</p>
|
||||
</div>
|
||||
</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">
|
||||
<TextField
|
||||
type="email"
|
||||
aria-label="Email address"
|
||||
placeholder="Email address"
|
||||
autoComplete="email"
|
||||
required
|
||||
variant="dark"
|
||||
className="w-60 min-w-0 shrink"
|
||||
/>
|
||||
<Button type="submit" color="cyan" className="ml-4 flex-none">
|
||||
<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">
|
||||
© Copyright ThreeFold {new Date().getFullYear()}. All rights reserved.
|
||||
<div className="flex justify-center py-8">
|
||||
<p className="text-sm text-gray-400">
|
||||
© Copyright OurWorld Holdings, {new Date().getFullYear()}. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { H2, P, CP } from "@/components/Texts";
|
||||
import { SectionHeader, P, CP } from "@/components/Texts";
|
||||
import { Button } from "@/components/Button";
|
||||
|
||||
export function GetStarted() {
|
||||
@@ -36,7 +36,7 @@ export function GetStarted() {
|
||||
}}
|
||||
>
|
||||
<div className="max-w-8xl mx-auto px-4 text-left mb-12">
|
||||
<H2>Get Started</H2>
|
||||
<SectionHeader>Get Started</SectionHeader>
|
||||
<P>Explore the documentation, code, and support channels to start building with Mycelium Cloud.</P>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import CountUp from "react-countup";
|
||||
import React from "react";
|
||||
import { Button } from "@/components/Button";
|
||||
import { H2, P } from "@/components/Texts";
|
||||
import { SectionHeader, P } from "@/components/Texts";
|
||||
|
||||
export function GridStats() {
|
||||
return (
|
||||
@@ -21,9 +21,9 @@ export function GridStats() {
|
||||
{/* Column 1: Title & Description */}
|
||||
<div className="flex flex-col space-y-10">
|
||||
<div>
|
||||
<H2 color="light">
|
||||
<SectionHeader color="light">
|
||||
Robust Infrastructure for your Intellegence Needs
|
||||
</H2>
|
||||
</SectionHeader>
|
||||
<P color="light" className="mt-6">
|
||||
Mycelium's groundbreaking technology provides dedicated, performance-validated GPUs for your AI workloads.
|
||||
</P>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)
|
||||
@@ -60,7 +60,7 @@ export function Header() {
|
||||
<nav>
|
||||
<Container className="relative z-50 flex justify-between py-4">
|
||||
<div className="relative z-10 flex items-center gap-16">
|
||||
<Link href="/" aria-label="Home">
|
||||
<Link href="/" aria-label="Home" className="hidden">
|
||||
<img src="/images/logo.png" alt="Mycelium" className="h-10 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden lg:flex lg:gap-10">
|
||||
@@ -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-300/50 hover:stroke-gray-400 focus:not-data-focus:outline-hidden active:stroke-white"
|
||||
aria-label="Toggle site navigation"
|
||||
>
|
||||
{({ open }) =>
|
||||
@@ -104,28 +104,20 @@ 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">
|
||||
Docs
|
||||
<Button
|
||||
variant="outline"
|
||||
color="white"
|
||||
href="mailto:info@ourworld.tf"
|
||||
>
|
||||
Join the Waitlist
|
||||
</Button>
|
||||
<Button href="https://www.mycelium.threefold.io/download/" color="cyan">Get Started</Button>
|
||||
<Button href="https://calendly.com/sachao/30min" color="cyan">Book a Meeting</Button>
|
||||
</div>
|
||||
</PopoverPanel>
|
||||
</>
|
||||
@@ -135,10 +127,14 @@ 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">
|
||||
Docs
|
||||
<Button
|
||||
variant="outline"
|
||||
color="white"
|
||||
href="mailto:info@ourworld.tf"
|
||||
>
|
||||
Join the Waitlist
|
||||
</Button>
|
||||
<Button href="https://www.mycelium.threefold.io/download/" color="cyan">Get Started</Button>
|
||||
<Button href="https://calendly.com/sachao/30min" color="cyan">Book a Meeting</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
98
src/components/HeaderLight.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import clsx from 'clsx'
|
||||
|
||||
function NavLinks() {
|
||||
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
|
||||
let timeoutRef = useRef<number | null>(null)
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-x-5">
|
||||
<div className="flex items-center gap-x-5 border-l border-white/10 pl-5">
|
||||
{[
|
||||
['Technologies', '/#technologies'],
|
||||
['Network', '/#network'],
|
||||
['How it Works', '/#how-it-works'],
|
||||
['Get Started', '/#get-started'],
|
||||
['Contact', '/#contact'],
|
||||
].map(([label, href], index) => (
|
||||
<Link
|
||||
key={label}
|
||||
href={href}
|
||||
className={clsx(
|
||||
'relative rounded-lg px-3 py-2 text-sm text-black transition-colors delay-150 hover:text-gray-700 hover:delay-0',
|
||||
)}
|
||||
onMouseEnter={() => {
|
||||
if (timeoutRef.current) {
|
||||
window.clearTimeout(timeoutRef.current)
|
||||
}
|
||||
setHoveredIndex(index)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
timeoutRef.current = window.setTimeout(() => {
|
||||
setHoveredIndex(null)
|
||||
}, 200)
|
||||
}}
|
||||
>
|
||||
<AnimatePresence>
|
||||
{hoveredIndex === index && (
|
||||
<motion.span
|
||||
className="absolute inset-0 rounded-lg bg-white/10"
|
||||
layoutId="hoverBackground"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1, transition: { duration: 0.15 } }}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
transition: { duration: 0.15 },
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<span className="relative z-10">{label}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function HeaderLight() {
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const [lastScrollY, setLastScrollY] = useState(0);
|
||||
|
||||
const controlHeader = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (window.scrollY > lastScrollY && window.scrollY > 100) { // Hides when scrolling down past 100px
|
||||
setIsVisible(false);
|
||||
} else { // Shows when scrolling up
|
||||
setIsVisible(true);
|
||||
}
|
||||
setLastScrollY(window.scrollY);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('scroll', controlHeader);
|
||||
return () => {
|
||||
window.removeEventListener('scroll', controlHeader);
|
||||
};
|
||||
}
|
||||
}, [lastScrollY]);
|
||||
|
||||
return (
|
||||
<motion.header
|
||||
className="fixed top-4 left-0 right-0 z-50 flex justify-center"
|
||||
initial={{ y: 0, opacity: 1 }}
|
||||
animate={{ y: isVisible ? 0 : -100, opacity: isVisible ? 1 : 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
>
|
||||
<div className="rounded-full bg-gray-50/90 px-5 py-3 shadow-lg ring-1 ring-white/10 backdrop-blur-sm">
|
||||
<NavLinks />
|
||||
</div>
|
||||
</motion.header>
|
||||
)
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { Bars3Icon, XMarkIcon, ChevronDoubleDownIcon } from '@heroicons/react/24
|
||||
import Image from 'next/image'
|
||||
import diamondSvg from '@/images/diamond.svg'
|
||||
import { Container } from '@/components/Container';
|
||||
import { H2, P, H3 } from '@/components/Texts';
|
||||
import { SectionHeader, P, H3 } from '@/components/Texts';
|
||||
import { Candy } from '@/components/Candy'
|
||||
|
||||
const navigation = [
|
||||
@@ -65,10 +65,10 @@ export function HomeAbout() {
|
||||
transition={{ duration: 1, delay: 0.2 }}
|
||||
className="absolute top-24 left-0 max-w-xl text-left"
|
||||
>
|
||||
<H2>
|
||||
<SectionHeader>
|
||||
Enterprise AI Agents
|
||||
That Never Sleep.
|
||||
</H2>
|
||||
</SectionHeader>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
@@ -76,7 +76,7 @@ export function HomeAbout() {
|
||||
transition={{ duration: 1, delay: 0.4 }}
|
||||
className="absolute top-54 left-0 max-w-xl text-left"
|
||||
>
|
||||
<P color="custom">
|
||||
<P>
|
||||
With Mycelium Cloud, you can deploy purpose-built AI agents to automate any workflow. Keep complete control of your data while scaling from simple tasks to complex decision-making.
|
||||
</P>
|
||||
</motion.div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { motion } from 'framer-motion'
|
||||
import { TypeAnimation } from 'react-type-animation'
|
||||
import { Dialog, DialogPanel } from '@headlessui/react'
|
||||
import { Bars3Icon, XMarkIcon, ChevronDoubleDownIcon } from '@heroicons/react/24/outline'
|
||||
import { H1, PL } from '@/components/Texts'
|
||||
import { H1, H2, P } from '@/components/Texts'
|
||||
|
||||
const navigation = [
|
||||
{ name: 'Product', href: '#' },
|
||||
@@ -32,8 +32,9 @@ 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">
|
||||
<div className="relative mx-auto flex h-screen max-w-8xl items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="max-w-6xl">
|
||||
<H1 color="light">
|
||||
<TypeAnimation
|
||||
sequence={[
|
||||
@@ -46,17 +47,20 @@ export function HomeHero() {
|
||||
/>
|
||||
</H1>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, delay: 1 }}
|
||||
className="mt-12"
|
||||
>
|
||||
<PL className="absolute bottom-0 left-0 max-w-xl text-left text-gray-100" color="light">
|
||||
<P className="mx-auto max-w-4xl text-center " color="secondary">
|
||||
Mycelium's advancements in Agentic infrastructure supports private, secure and autonomous Agents that connect, learn and grow with you.
|
||||
</PL>
|
||||
</P>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
55
src/components/HomeHeroLight.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { TypeAnimation } from 'react-type-animation'
|
||||
import { Dialog, DialogPanel } from '@headlessui/react'
|
||||
import { Bars3Icon, XMarkIcon, ChevronDoubleDownIcon } from '@heroicons/react/24/outline'
|
||||
import { H1, H2, PL } from '@/components/Texts'
|
||||
import { ChevronRightIcon } from '@heroicons/react/20/solid'
|
||||
|
||||
const navigation = [
|
||||
{ name: 'Product', href: '#' },
|
||||
{ name: 'Features', href: '#' },
|
||||
{ name: 'Marketplace', href: '#' },
|
||||
{ name: 'Company', href: '#' },
|
||||
]
|
||||
|
||||
export function HomeHeroLight() {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative isolate overflow-hidden bg-white"
|
||||
style={{
|
||||
backgroundImage: 'url(/images/cloud.png)',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
>
|
||||
|
||||
<div className="mx-auto max-w-7xl px-6 pt-10 pb-24 sm:pb-32 lg:flex lg:px-8 lg:py-40">
|
||||
<div className="mx-auto max-w-2xl shrink-0 lg:mx-0 lg:pt-8">
|
||||
|
||||
<h1 className="mt-10 text-5xl font-semibold tracking-tight text-pretty text-gray-900 sm:text-7xl">
|
||||
Decentralized Autonomous Agentic Cloud.
|
||||
</h1>
|
||||
<p className="mt-8 text-lg font-medium text-pretty text-gray-500 sm:text-xl/8">
|
||||
Mycelium's advancements in Agentic infrastructure supports private, secure and autonomous Agents that connect, learn and grow with you.
|
||||
</p>
|
||||
<div className="mt-10 flex items-center gap-x-6">
|
||||
<a
|
||||
href="#"
|
||||
className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-xs hover:bg-indigo-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||
>
|
||||
Get started
|
||||
</a>
|
||||
<a href="#" className="text-sm/6 font-semibold text-gray-900">
|
||||
Learn more <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
60
src/components/HomeHeroLight2.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
'use client'
|
||||
|
||||
import { useRef, useEffect } from 'react'
|
||||
import { H1, H2, P, H5, Eyebrow } from '@/components/Texts'
|
||||
|
||||
export function HomeHeroLight2() {
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (videoRef.current) videoRef.current.playbackRate = 0.4
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<section className="relative h-screen overflow-hidden">
|
||||
{/* Background video */}
|
||||
<video
|
||||
ref={videoRef}
|
||||
src="/videos/cloud.mp4"
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className="absolute inset-0 h-full w-full object-cover z-[-10]"
|
||||
/>
|
||||
|
||||
{/* Global soft wash + blur */}
|
||||
<div className="absolute inset-0 bg-white opacity-30 backdrop-blur-md z-0" />
|
||||
|
||||
{/* Center “halo” for text legibility */}
|
||||
<div
|
||||
className="absolute inset-0 z-0"
|
||||
style={{
|
||||
background:
|
||||
'radial-gradient(ellipse at center, rgba(255,255,255,0.96) 0%, rgba(255,255,255,0.88) 15%, rgba(255,255,255,0.72) 35%, rgba(255,255,255,0.08) 75%)'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<div className="relative z-10 h-full flex items-center justify-center">
|
||||
<div className="mx-auto max-w-4xl text-center px-6 lg:px-8">
|
||||
<Eyebrow color="accent"></Eyebrow>
|
||||
<H1
|
||||
className="pt-6"
|
||||
style={{ textShadow: '0 2px 8px rgba(0,0,0,0.08)' }}
|
||||
>
|
||||
Decentralized Autonomous Agentic Cloud.
|
||||
</H1>
|
||||
|
||||
<H5
|
||||
className="mt-8"
|
||||
style={{ textShadow: '0 1px 4px rgba(0,0,0,0.06)' }}
|
||||
>
|
||||
Mycelium's advancements in Agentic infrastructure support private, secure, and
|
||||
autonomous Agents that connect, learn, and grow with you.
|
||||
</H5>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Footer } from '@/components/Footer'
|
||||
import { Header } from '@/components/Header'
|
||||
import { HeaderLight } from '@/components/HeaderLight'
|
||||
|
||||
export function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<HeaderLight />
|
||||
{children}
|
||||
<main className="flex-auto">{children}</main>
|
||||
<Footer />
|
||||
</>
|
||||
|
||||
@@ -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, theme = 'dark' }: { className?: string, theme?: 'dark' | 'light' }) {
|
||||
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
|
||||
let timeoutRef = useRef<number | null>(null)
|
||||
|
||||
@@ -23,17 +24,21 @@ 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 transition-colors delay-150 hover:delay-0',
|
||||
theme === 'dark'
|
||||
? 'text-white hover:text-gray-300'
|
||||
: 'text-gray-900 hover:text-gray-700',
|
||||
className,
|
||||
)}
|
||||
onClick={(e) => handleClick(e, href)}
|
||||
onMouseEnter={() => {
|
||||
if (timeoutRef.current) {
|
||||
@@ -50,7 +55,10 @@ export function NavLinks() {
|
||||
<AnimatePresence>
|
||||
{hoveredIndex === index && (
|
||||
<motion.span
|
||||
className="absolute inset-0 rounded-lg bg-white/10"
|
||||
className={clsx(
|
||||
'absolute inset-0 rounded-lg',
|
||||
theme === 'dark' ? 'bg-white/10' : 'bg-gray-900/5',
|
||||
)}
|
||||
layoutId="hoverBackground"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1, transition: { duration: 0.15 } }}
|
||||
|
||||
@@ -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 { H1, H2, P } from '@/components/Texts';
|
||||
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>
|
||||
|
||||
75
src/components/StackSectionLight.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { StackedCubesLight } from "@/components/ui/StackedCubesLight";
|
||||
import { H2, P, SectionHeader, Eyebrow } from "@/components/Texts";
|
||||
import { FadeIn } from "./FadeIn";
|
||||
import { DottedGlowBackground } from '@/components/ui/dotted-glow-background';
|
||||
|
||||
export function StackSectionLight() {
|
||||
return (
|
||||
<section className="relative w-full overflow-hidden py-24 lg:py-40">
|
||||
{/* === Background Layer === */}
|
||||
<div className="absolute inset-0 -z-10 bg-[#FAFAFA]">
|
||||
{/* Dotted Glow Background */}
|
||||
<DottedGlowBackground
|
||||
gap={15}
|
||||
radius={2}
|
||||
color="rgba(0,0,0,0.4)"
|
||||
glowColor="rgba(0,170,255,0.85)"
|
||||
opacity={0.2}
|
||||
/>
|
||||
{/* Faint 3D grid floor */}
|
||||
<div className="absolute inset-0 flex items-end justify-center overflow-hidden">
|
||||
<div className="w-[200vw] h-[200vh] bg-[linear-gradient(to_right,rgba(0,0,0,0.03)_1px,transparent_1px),linear-gradient(to_bottom,rgba(0,0,0,0.03)_1px,transparent_1px)] bg-[size:60px_60px] [transform:perspective(800px)_rotateX(70deg)] origin-bottom opacity-50" />
|
||||
</div>
|
||||
</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">
|
||||
<FadeIn>
|
||||
<Eyebrow color="accent">Technology Layers</Eyebrow>
|
||||
<SectionHeader color="dark" className="text-4xl sm:text-5xl font-semibold">
|
||||
The Mycelium Stack
|
||||
</SectionHeader>
|
||||
</FadeIn>
|
||||
|
||||
<FadeIn>
|
||||
<P color="dark" className="mt-6 text-lg leading-relaxed text-gray-600">
|
||||
Built with Mycelium technology, our AI infrastructure ensures
|
||||
unbreakable networks, complete data sovereignty, ultra-secure
|
||||
agent-human communication, and unhackable data storage systems.
|
||||
</P>
|
||||
</FadeIn>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Animated Stack */}
|
||||
<div className="lg:col-span-2 flex items-center justify-center lg:justify-start relative">
|
||||
<motion.div
|
||||
initial={{ y: 30, opacity: 0 }}
|
||||
whileInView={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 1.2, ease: "easeOut" }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<motion.div
|
||||
animate={{
|
||||
y: [0, -10, 0],
|
||||
rotateZ: [0, 0.5, -0.5, 0],
|
||||
}}
|
||||
transition={{
|
||||
duration: 6,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="relative"
|
||||
>
|
||||
<StackedCubesLight />
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import React, { useRef } from 'react'
|
||||
import { motion, useInView } from 'framer-motion'
|
||||
import { H2, P, CT, CP } from '@/components/Texts'
|
||||
import { SectionHeader, P, CT, CP, Eyebrow } from '@/components/Texts'
|
||||
import { TbCircleNumber1Filled, TbCircleNumber2Filled, TbCircleNumber3Filled } from 'react-icons/tb'
|
||||
|
||||
const features = [
|
||||
@@ -30,19 +30,19 @@ export function Steps() {
|
||||
const isInView = useInView(ref, { once: true });
|
||||
|
||||
return (
|
||||
<section id="benefits" ref={ref} className="relative bg-cover bg-center py-32 -top-20 text-white" style={{ backgroundImage: "url('/images/deployment.webp')" }}>
|
||||
<div className="absolute inset-0 bg-black/60" />
|
||||
<div className="relative px-6 lg:px-6">
|
||||
<section id="benefits" ref={ref} className="relative pt-12 lg:pt-24 pb-4 px-4 lg:px-12 text-white">
|
||||
<div className="relative px-6 lg:px-12">
|
||||
<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 lg:mx-0"
|
||||
className="mx-auto max-w-5xl text-center"
|
||||
>
|
||||
<H2 className="text-3xl font-medium tracking-tight" color="light">
|
||||
<Eyebrow color="accent">Get Started</Eyebrow>
|
||||
<SectionHeader 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">
|
||||
</SectionHeader>
|
||||
<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-12 lg:max-w-7xl 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-gray-300 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 bg-white/5 backdrop-blur-md"
|
||||
>
|
||||
<feature.icon className="h-8 w-8 mb-4 text-white" />
|
||||
<CT as="span" className="font-semibold" color="light">{feature.name}</CT>
|
||||
|
||||
@@ -4,10 +4,14 @@ import React from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const colorVariants = {
|
||||
primary: 'text-[#fffff]',
|
||||
secondary: 'text-gray-200',
|
||||
custom: 'text-[#015eff]',
|
||||
light: 'text-white',
|
||||
primary: 'text-gray-900',
|
||||
secondary: 'text-gray-600',
|
||||
light: 'text-gray-50',
|
||||
accent: 'text-cyan-500',
|
||||
white: 'text-white',
|
||||
dark: 'text-gray-950',
|
||||
tertiary: 'text-gray-700',
|
||||
lightSecondary: 'text-gray-300',
|
||||
} as const
|
||||
|
||||
type TextOwnProps = {
|
||||
@@ -15,17 +19,19 @@ type TextOwnProps = {
|
||||
className?: string
|
||||
}
|
||||
|
||||
// Polymorphic helpers (no forwardRef needed)
|
||||
type PolymorphicProps<E extends React.ElementType, P> =
|
||||
P & { as?: E } &
|
||||
Omit<React.ComponentPropsWithoutRef<E>, keyof P | 'as'>
|
||||
// Polymorphic helpers
|
||||
type PolymorphicProps<E extends React.ElementType, P> = P & {
|
||||
as?: E
|
||||
} & Omit<React.ComponentPropsWithoutRef<E>, keyof P | 'as'>
|
||||
|
||||
const createTextComponent = <DefaultElement extends React.ElementType>(
|
||||
defaultElement: DefaultElement,
|
||||
defaultClassName: string
|
||||
) => {
|
||||
type Props<E extends React.ElementType = DefaultElement> =
|
||||
PolymorphicProps<E, TextOwnProps>
|
||||
type Props<E extends React.ElementType = DefaultElement> = PolymorphicProps<
|
||||
E,
|
||||
TextOwnProps
|
||||
>
|
||||
|
||||
function Text<E extends React.ElementType = DefaultElement>({
|
||||
as,
|
||||
@@ -38,24 +44,106 @@ const createTextComponent = <DefaultElement extends React.ElementType>(
|
||||
return (
|
||||
<Tag
|
||||
className={cn(defaultClassName, colorVariants[color], className)}
|
||||
{...(props as object)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Tag>
|
||||
)
|
||||
}
|
||||
|
||||
;(Text as any).displayName = `Text(${typeof defaultElement === 'string' ? defaultElement : 'Component'})`
|
||||
;(Text as any).displayName = `Text(${typeof defaultElement === 'string' ? defaultElement : 'Component'
|
||||
})`
|
||||
return Text
|
||||
}
|
||||
|
||||
// Exports
|
||||
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 H3 = createTextComponent('h3', 'text-2xl lg:text-3xl font-medium')
|
||||
export const H4 = createTextComponent('h4', 'text-xl lg:text-2xl font-semibold leading-tight')
|
||||
// Exports based on your tailwind.css and the example
|
||||
export const H1 = createTextComponent(
|
||||
'h1',
|
||||
'text-6xl lg:text-7xl font-medium leading-tight tracking-tight'
|
||||
)
|
||||
export const H2 = createTextComponent(
|
||||
'h2',
|
||||
'text-4xl lg:text-6xl font-medium leading-tight tracking-tight'
|
||||
)
|
||||
export const H3 = createTextComponent(
|
||||
'h3',
|
||||
'text-3xl lg:text-5xl font-medium leading-tight tracking-tight'
|
||||
)
|
||||
export const H4 = createTextComponent(
|
||||
'h4',
|
||||
'text-2xl lg:text-4xl font-medium leading-snug tracking-tight'
|
||||
)
|
||||
export const P = createTextComponent(
|
||||
'p',
|
||||
'text-base lg:text-lg leading-relaxed'
|
||||
)
|
||||
export const Small = createTextComponent(
|
||||
'small',
|
||||
'text-sm font-medium leading-normal tracking-normal'
|
||||
)
|
||||
export const Subtle = createTextComponent(
|
||||
'p',
|
||||
'text-sm leading-normal tracking-normal text-gray-500'
|
||||
)
|
||||
export const H5 = createTextComponent(
|
||||
'h5',
|
||||
'text-lg lg:text-xl font-medium leading-snug tracking-tight'
|
||||
)
|
||||
export const Eyebrow = createTextComponent(
|
||||
'h2',
|
||||
'text-base/7 font-semibold tracking-wide'
|
||||
)
|
||||
export const SectionHeader = createTextComponent(
|
||||
'p',
|
||||
'text-3xl lg:text-4xl font-medium leading-tight tracking-tight'
|
||||
)
|
||||
export const CardEyebrow = createTextComponent(
|
||||
'h3',
|
||||
'text-sm/4 font-semibold tracking-wide'
|
||||
)
|
||||
export const CardTitle = createTextComponent(
|
||||
'p',
|
||||
'text-lg font-medium leading-snug tracking-tight'
|
||||
)
|
||||
export const CardDescription = createTextComponent(
|
||||
'p',
|
||||
'text-sm/6 leading-normal tracking-normal'
|
||||
)
|
||||
export const FeatureTitle = createTextComponent(
|
||||
'h3',
|
||||
'text-lg font-semibold leading-snug tracking-tight'
|
||||
)
|
||||
export const FeatureDescription = createTextComponent(
|
||||
'p',
|
||||
'text-sm leading-normal tracking-normal'
|
||||
)
|
||||
export const MobileFeatureTitle = createTextComponent(
|
||||
'h3',
|
||||
'text-sm font-semibold sm:text-lg leading-snug tracking-tight'
|
||||
)
|
||||
export const SecondaryFeatureTitle = createTextComponent(
|
||||
'h3',
|
||||
'text-base font-semibold leading-snug tracking-tight'
|
||||
)
|
||||
export const Question = createTextComponent(
|
||||
'h3',
|
||||
'text-lg/6 font-semibold tracking-tight'
|
||||
)
|
||||
export const Answer = createTextComponent(
|
||||
'p',
|
||||
'mt-4 text-sm leading-normal tracking-normal'
|
||||
)
|
||||
export const PageHeader = createTextComponent(
|
||||
'h2',
|
||||
'text-5xl lg:text-6xl font-medium leading-tight tracking-tight'
|
||||
)
|
||||
export const DownloadCardTitle = createTextComponent(
|
||||
'dt',
|
||||
'text-base/7 font-semibold tracking-wide'
|
||||
)
|
||||
export const DownloadCardDescription = createTextComponent(
|
||||
'dd',
|
||||
'text-base/7 leading-normal tracking-normal'
|
||||
)
|
||||
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')
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from '@heroicons/react/24/solid'
|
||||
|
||||
import { Container } from '@/components/Container'
|
||||
import { H2, P, CT, CP } from '@/components/Texts'
|
||||
import { SectionHeader, P, CT, CP } from '@/components/Texts'
|
||||
import { motion, useInView } from 'framer-motion'
|
||||
|
||||
interface Review {
|
||||
@@ -147,12 +147,12 @@ export function UseCases() {
|
||||
transition={{ duration: 0.8, delay: 0.1 }}
|
||||
className="flex flex-col items-start justify-start pt-10 lg:pr-12"
|
||||
>
|
||||
<H2 id="usecases-title" color="light" className="text-left">
|
||||
Mycelium Technologies
|
||||
</H2>
|
||||
<SectionHeader id="usecases-title" color="light" className="text-left">
|
||||
Augmented Intelligence Fabric
|
||||
</SectionHeader>
|
||||
<P className="mt-4 text-left" color="light">
|
||||
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.
|
||||
The sovereign substrate for autonomous AI.
|
||||
Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.
|
||||
</P>
|
||||
</motion.div>
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
import { Globe } from "@/components/ui/globe"
|
||||
import { motion } from "framer-motion"
|
||||
import { H2, P, CT, CP } from "@/components/Texts"
|
||||
import { H2, P, CT, CP, SectionHeader, Eyebrow } from "@/components/Texts"
|
||||
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
|
||||
@@ -31,8 +31,9 @@ export function WorldMap() {
|
||||
transition={{ duration: 0.5 }}
|
||||
className="max-w-xl"
|
||||
>
|
||||
<H2 color="light">Mycelium Network is Live.</H2>
|
||||
<P className="mt-4 text-base leading-relaxed font-light" color="light">
|
||||
<Eyebrow color="accent">Network</Eyebrow>
|
||||
<SectionHeader color="light">Mycelium Network is Live.</SectionHeader>
|
||||
<P className=" mt-4 text-base leading-relaxed" color="light">
|
||||
Mycelium Cloud's advancement technology enables anyone to deploy
|
||||
their own Internet infrastructure, anywhere.
|
||||
</P>
|
||||
@@ -48,18 +49,17 @@ 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 w-80 rounded-2xl border border-gray-300 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 bg-white/5 backdrop-blur-md"
|
||||
>
|
||||
<CT color="light" className="uppercase tracking-wide">CORES</CT>
|
||||
<CountUpNumber end={54958} color="light" className="mt-2 text-3xl font-bold" />
|
||||
@@ -72,8 +72,7 @@ export function WorldMap() {
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
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 w-80 rounded-2xl border border-gray-300 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 bg-white/5 backdrop-blur-md"
|
||||
>
|
||||
<CT color="light" className="uppercase tracking-wide">NODES</CT>
|
||||
<CountUpNumber end={1493} color="light" className="mt-2 text-3xl font-bold" />
|
||||
@@ -86,13 +85,12 @@ export function WorldMap() {
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
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 w-80 rounded-2xl border border-gray-300 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 bg-white/5 backdrop-blur-md"
|
||||
>
|
||||
<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>
|
||||
|
||||
@@ -100,8 +98,7 @@ export function WorldMap() {
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
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-47 lg:right-0 w-80 rounded-2xl border border-gray-300 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 bg-white/5 backdrop-blur-md"
|
||||
>
|
||||
<CT color="light" className="uppercase tracking-wide">COUNTRIES</CT>
|
||||
<CountUpNumber end={44} color="light" className="mt-2 text-3xl font-bold" />
|
||||
|
||||
@@ -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,24 +125,6 @@ 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>
|
||||
</div>
|
||||
);
|
||||
|
||||
131
src/components/ui/CubeLight.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
"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;
|
||||
onClick: () => 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 stopColor="#E5E7EB" />
|
||||
<stop offset="1" stopColor="#9CA3AF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export function CubeLight({ 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,
|
||||
}}
|
||||
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-80"
|
||||
style={{
|
||||
filter: isActive ? 'brightness(1.1) drop-shadow(0 0 15px rgba(0, 0, 0, 0.2))' : 'brightness(1)',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Title overlay */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<h3
|
||||
className="text-black text-sm lg:text-base font-medium text-center px-4 drop-shadow-sm"
|
||||
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="black"
|
||||
strokeWidth="1"
|
||||
opacity="0.6"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{/* Description text */}
|
||||
<div className="ml-32 w-80">
|
||||
<h4 className="text-black text-base font-semibold mb-2">
|
||||
{descriptionTitle}
|
||||
</h4>
|
||||
<p className="text-gray-800 text-sm leading-relaxed font-light">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Description for Mobile - Below cube */}
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,39 +8,47 @@ const stackData = [
|
||||
{
|
||||
id: "agent",
|
||||
title: "Agent Layer",
|
||||
descriptionTitle: "Personal Agents - Secure & Sovereign",
|
||||
descriptionTitle: "Your sovereign agent with private memory and permissioned data access—always under your control.",
|
||||
description:
|
||||
"Personal AI agents operate as secure digital twins, providing tailored intelligent assistance. They interact with existing chat, MCP agents, and coding tools while maintaining sovereignty and ecosystem compatibility.",
|
||||
"Choose from a wide library of open-source LLMs, paired with built-in semantic search and retrieval.\nIt coordinates across people, apps, and other agents to plan, create, and execute.\nIt operates inside a compliant legal & financial sandbox, ready for real-world transactions and operations.\nMore than just an assistant—an intelligent partner that learns and does your way.",
|
||||
position: "top",
|
||||
},
|
||||
{
|
||||
id: "ai",
|
||||
title: "AI Layer",
|
||||
descriptionTitle: "AI Agents & AI Brains + Mycelium Code & Compute Sandboxes",
|
||||
id: "network",
|
||||
title: "Network Layer",
|
||||
descriptionTitle: "A global, end-to-end encrypted overlay that simply doesn’t break.",
|
||||
description:
|
||||
"Intelligence core combining LLMs with specialized AI agents. Mycelium-powered sandboxes provide secure environments for development, testing, and compilation with active memory systems and unbreakable network architecture.",
|
||||
"Shortest-path routing moves your traffic the fastest way, every time.\nInstant discovery with integrated DNS, semantic search, and indexing.\nA distributed CDN and edge delivery keep content available and tamper-resistant worldwide.\nBuilt-in tool services and secure coding sandboxes—seamless on phones, desktops, and edge.",
|
||||
position: "middle",
|
||||
},
|
||||
{
|
||||
id: "cloud",
|
||||
title: "Cloud Layer",
|
||||
descriptionTitle: "Mycelium Compute & Storage - Decentralized Infrastructure Layer",
|
||||
descriptionTitle: "An autonomous, stateless OS that enforces pre-deterministic deployments you define.",
|
||||
description:
|
||||
"Foundation runs bare metal Zero OS enabling autonomous cloud. Decentralized infrastructure supports Web2, Web3, AI workloads with superior scalability. Built on twenty years cloud experience.",
|
||||
"Workloads are cryptographically bound to your private key—location and access are yours.\nNo cloud vendor or middleman in the path: end-to-end ownership and isolation by default.\nGeo-aware placement delivers locality, compliance, and ultra-low latency where it matters.\nEncrypted, erasure-coded storage, decentralized compute and GPU on demand—including LLMs.",
|
||||
position: "bottom",
|
||||
},
|
||||
];
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
95
src/components/ui/StackedCubesLight.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { CubeLight } from "@/components/ui/CubeLight"
|
||||
|
||||
const stackData = [
|
||||
{
|
||||
id: "agent",
|
||||
title: "Agent Layer",
|
||||
descriptionTitle: "Your sovereign agent with private memory and permissioned data access—always under your control.",
|
||||
description:
|
||||
"Choose from a wide library of open-source LLMs, paired with built-in semantic search and retrieval.\nIt coordinates across people, apps, and other agents to plan, create, and execute.\nIt operates inside a compliant legal & financial sandbox, ready for real-world transactions and operations.\nMore than just an assistant—an intelligent partner that learns and does your way.",
|
||||
position: "top",
|
||||
},
|
||||
{
|
||||
id: "network",
|
||||
title: "Network Layer",
|
||||
descriptionTitle: "A global, end-to-end encrypted overlay that simply doesn’t break.",
|
||||
description:
|
||||
"Shortest-path routing moves your traffic the fastest way, every time.\nInstant discovery with integrated DNS, semantic search, and indexing.\nA distributed CDN and edge delivery keep content available and tamper-resistant worldwide.\nBuilt-in tool services and secure coding sandboxes—seamless on phones, desktops, and edge.",
|
||||
position: "middle",
|
||||
},
|
||||
{
|
||||
id: "cloud",
|
||||
title: "Cloud Layer",
|
||||
descriptionTitle: "An autonomous, stateless OS that enforces pre-deterministic deployments you define.",
|
||||
description:
|
||||
"Workloads are cryptographically bound to your private key—location and access are yours.\nNo cloud vendor or middleman in the path: end-to-end ownership and isolation by default.\nGeo-aware placement delivers locality, compliance, and ultra-low latency where it matters.\nEncrypted, erasure-coded storage, decentralized compute and GPU on demand—including LLMs.",
|
||||
position: "bottom",
|
||||
},
|
||||
];
|
||||
|
||||
export function StackedCubesLight() {
|
||||
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-center min-h-[450px] lg:min-h-[400px]"
|
||||
onMouseLeave={() => setActive("agent")}
|
||||
>
|
||||
<motion.div
|
||||
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,
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse",
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
{stackData.map((layer, index) => (
|
||||
<div
|
||||
key={layer.id}
|
||||
className="absolute"
|
||||
style={{
|
||||
top: `calc(${index * 30}% - ${index * 10}px)`,
|
||||
zIndex: active === layer.id ? 20 : 10 - index,
|
||||
}}
|
||||
>
|
||||
<CubeLight
|
||||
title={layer.title}
|
||||
descriptionTitle={layer.descriptionTitle}
|
||||
description={layer.description}
|
||||
isActive={active === layer.id}
|
||||
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-200/50 rounded-lg">
|
||||
<h4 className="text-black text-lg font-semibold mb-2 text-center">
|
||||
{selectedMobileLayer.descriptionTitle}
|
||||
</h4>
|
||||
<p className="text-gray-700 text-sm leading-relaxed text-center">
|
||||
{selectedMobileLayer.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
79
src/components/ui/bento-grid.tsx
Normal 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);
|
||||
308
src/components/ui/dotted-glow-background.tsx
Normal file
@@ -0,0 +1,308 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
type DottedGlowBackgroundProps = {
|
||||
className?: string;
|
||||
/** distance between dot centers in pixels */
|
||||
gap?: number;
|
||||
/** base radius of each dot in CSS px */
|
||||
radius?: number;
|
||||
/** dot color (will pulse by alpha) */
|
||||
color?: string;
|
||||
/** optional dot color for dark mode */
|
||||
darkColor?: string;
|
||||
/** shadow/glow color for bright dots */
|
||||
glowColor?: string;
|
||||
/** optional glow color for dark mode */
|
||||
darkGlowColor?: string;
|
||||
/** optional CSS variable name for light dot color (e.g. --color-zinc-900) */
|
||||
colorLightVar?: string;
|
||||
/** optional CSS variable name for dark dot color (e.g. --color-zinc-100) */
|
||||
colorDarkVar?: string;
|
||||
/** optional CSS variable name for light glow color */
|
||||
glowColorLightVar?: string;
|
||||
/** optional CSS variable name for dark glow color */
|
||||
glowColorDarkVar?: string;
|
||||
/** global opacity for the whole layer */
|
||||
opacity?: number;
|
||||
/** background radial fade opacity (0 = transparent background) */
|
||||
backgroundOpacity?: number;
|
||||
/** minimum per-dot speed in rad/s */
|
||||
speedMin?: number;
|
||||
/** maximum per-dot speed in rad/s */
|
||||
speedMax?: number;
|
||||
/** global speed multiplier for all dots */
|
||||
speedScale?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Canvas-based dotted background that randomly glows and dims.
|
||||
* - Uses a stable grid of dots.
|
||||
* - Each dot gets its own phase + speed producing organic shimmering.
|
||||
* - Handles high-DPI and resizes via ResizeObserver.
|
||||
*/
|
||||
export function DottedGlowBackground({
|
||||
className,
|
||||
gap = 12,
|
||||
radius = 2,
|
||||
color = "rgba(0,0,0,0.7)",
|
||||
darkColor,
|
||||
glowColor = "rgba(0, 170, 255, 0.85)",
|
||||
darkGlowColor,
|
||||
colorLightVar,
|
||||
colorDarkVar,
|
||||
glowColorLightVar,
|
||||
glowColorDarkVar,
|
||||
opacity = 0.6,
|
||||
backgroundOpacity = 0,
|
||||
speedMin = 0.4,
|
||||
speedMax = 1.3,
|
||||
speedScale = 1,
|
||||
}: DottedGlowBackgroundProps) {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [resolvedColor, setResolvedColor] = useState<string>(color);
|
||||
const [resolvedGlowColor, setResolvedGlowColor] = useState<string>(glowColor);
|
||||
|
||||
// Resolve CSS variable value from the container or root
|
||||
const resolveCssVariable = (
|
||||
el: Element,
|
||||
variableName?: string,
|
||||
): string | null => {
|
||||
if (!variableName) return null;
|
||||
const normalized = variableName.startsWith("--")
|
||||
? variableName
|
||||
: `--${variableName}`;
|
||||
const fromEl = getComputedStyle(el as Element)
|
||||
.getPropertyValue(normalized)
|
||||
.trim();
|
||||
if (fromEl) return fromEl;
|
||||
const root = document.documentElement;
|
||||
const fromRoot = getComputedStyle(root).getPropertyValue(normalized).trim();
|
||||
return fromRoot || null;
|
||||
};
|
||||
|
||||
const detectDarkMode = (): boolean => {
|
||||
const root = document.documentElement;
|
||||
if (root.classList.contains("dark")) return true;
|
||||
if (root.classList.contains("light")) return false;
|
||||
return (
|
||||
window.matchMedia &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
);
|
||||
};
|
||||
|
||||
// Keep resolved colors in sync with theme changes and prop updates
|
||||
useEffect(() => {
|
||||
const container = containerRef.current ?? document.documentElement;
|
||||
|
||||
const compute = () => {
|
||||
const isDark = detectDarkMode();
|
||||
|
||||
let nextColor: string = color;
|
||||
let nextGlow: string = glowColor;
|
||||
|
||||
if (isDark) {
|
||||
const varDot = resolveCssVariable(container, colorDarkVar);
|
||||
const varGlow = resolveCssVariable(container, glowColorDarkVar);
|
||||
nextColor = varDot || darkColor || nextColor;
|
||||
nextGlow = varGlow || darkGlowColor || nextGlow;
|
||||
} else {
|
||||
const varDot = resolveCssVariable(container, colorLightVar);
|
||||
const varGlow = resolveCssVariable(container, glowColorLightVar);
|
||||
nextColor = varDot || nextColor;
|
||||
nextGlow = varGlow || nextGlow;
|
||||
}
|
||||
|
||||
setResolvedColor(nextColor);
|
||||
setResolvedGlowColor(nextGlow);
|
||||
};
|
||||
|
||||
compute();
|
||||
|
||||
const mql = window.matchMedia
|
||||
? window.matchMedia("(prefers-color-scheme: dark)")
|
||||
: null;
|
||||
const handleMql = () => compute();
|
||||
mql?.addEventListener?.("change", handleMql);
|
||||
|
||||
const mo = new MutationObserver(() => compute());
|
||||
mo.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["class", "style"],
|
||||
});
|
||||
|
||||
return () => {
|
||||
mql?.removeEventListener?.("change", handleMql);
|
||||
mo.disconnect();
|
||||
};
|
||||
}, [
|
||||
color,
|
||||
darkColor,
|
||||
glowColor,
|
||||
darkGlowColor,
|
||||
colorLightVar,
|
||||
colorDarkVar,
|
||||
glowColorLightVar,
|
||||
glowColorDarkVar,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const el = canvasRef.current;
|
||||
const container = containerRef.current;
|
||||
if (!el || !container) return;
|
||||
|
||||
const ctx = el.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
let raf = 0;
|
||||
let stopped = false;
|
||||
|
||||
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
||||
|
||||
const resize = () => {
|
||||
const { width, height } = container.getBoundingClientRect();
|
||||
el.width = Math.max(1, Math.floor(width * dpr));
|
||||
el.height = Math.max(1, Math.floor(height * dpr));
|
||||
el.style.width = `${Math.floor(width)}px`;
|
||||
el.style.height = `${Math.floor(height)}px`;
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
};
|
||||
|
||||
const ro = new ResizeObserver(resize);
|
||||
ro.observe(container);
|
||||
resize();
|
||||
|
||||
// Precompute dot metadata for a medium-sized grid and regenerate on resize
|
||||
let dots: { x: number; y: number; phase: number; speed: number }[] = [];
|
||||
|
||||
const regenDots = () => {
|
||||
dots = [];
|
||||
const { width, height } = container.getBoundingClientRect();
|
||||
const cols = Math.ceil(width / gap) + 2;
|
||||
const rows = Math.ceil(height / gap) + 2;
|
||||
const min = Math.min(speedMin, speedMax);
|
||||
const max = Math.max(speedMin, speedMax);
|
||||
for (let i = -1; i < cols; i++) {
|
||||
for (let j = -1; j < rows; j++) {
|
||||
const x = i * gap + (j % 2 === 0 ? 0 : gap * 0.5); // offset every other row
|
||||
const y = j * gap;
|
||||
// Randomize phase and speed slightly per dot
|
||||
const phase = Math.random() * Math.PI * 2;
|
||||
const span = Math.max(max - min, 0);
|
||||
const speed = min + Math.random() * span; // configurable rad/s
|
||||
dots.push({ x, y, phase, speed });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const regenThrottled = () => {
|
||||
regenDots();
|
||||
};
|
||||
|
||||
regenDots();
|
||||
|
||||
let last = performance.now();
|
||||
|
||||
const draw = (now: number) => {
|
||||
if (stopped) return;
|
||||
const dt = (now - last) / 1000; // seconds
|
||||
last = now;
|
||||
const { width, height } = container.getBoundingClientRect();
|
||||
|
||||
ctx.clearRect(0, 0, el.width, el.height);
|
||||
ctx.globalAlpha = opacity;
|
||||
|
||||
// optional subtle background fade for depth (defaults to 0 = transparent)
|
||||
if (backgroundOpacity > 0) {
|
||||
const grad = ctx.createRadialGradient(
|
||||
width * 0.5,
|
||||
height * 0.4,
|
||||
Math.min(width, height) * 0.1,
|
||||
width * 0.5,
|
||||
height * 0.5,
|
||||
Math.max(width, height) * 0.7,
|
||||
);
|
||||
grad.addColorStop(0, "rgba(0,0,0,0)");
|
||||
grad.addColorStop(
|
||||
1,
|
||||
`rgba(0,0,0,${Math.min(Math.max(backgroundOpacity, 0), 1)})`,
|
||||
);
|
||||
ctx.fillStyle = grad as unknown as CanvasGradient;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
// animate dots
|
||||
ctx.save();
|
||||
ctx.fillStyle = resolvedColor;
|
||||
|
||||
const time = (now / 1000) * Math.max(speedScale, 0);
|
||||
for (let i = 0; i < dots.length; i++) {
|
||||
const d = dots[i];
|
||||
// Linear triangle wave 0..1..0 for linear glow/dim
|
||||
const mod = (time * d.speed + d.phase) % 2;
|
||||
const lin = mod < 1 ? mod : 2 - mod; // 0..1..0
|
||||
const a = 0.25 + 0.55 * lin; // 0.25..0.8 linearly
|
||||
|
||||
// draw glow when bright
|
||||
if (a > 0.6) {
|
||||
const glow = (a - 0.6) / 0.4; // 0..1
|
||||
ctx.shadowColor = resolvedGlowColor;
|
||||
ctx.shadowBlur = 6 * glow;
|
||||
} else {
|
||||
ctx.shadowColor = "transparent";
|
||||
ctx.shadowBlur = 0;
|
||||
}
|
||||
|
||||
ctx.globalAlpha = a * opacity;
|
||||
ctx.beginPath();
|
||||
ctx.arc(d.x, d.y, radius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.restore();
|
||||
|
||||
raf = requestAnimationFrame(draw);
|
||||
};
|
||||
|
||||
const handleResize = () => {
|
||||
resize();
|
||||
regenThrottled();
|
||||
};
|
||||
|
||||
window.addEventListener("resize", handleResize);
|
||||
raf = requestAnimationFrame(draw);
|
||||
|
||||
return () => {
|
||||
stopped = true;
|
||||
cancelAnimationFrame(raf);
|
||||
window.removeEventListener("resize", handleResize);
|
||||
ro.disconnect();
|
||||
};
|
||||
}, [
|
||||
gap,
|
||||
radius,
|
||||
resolvedColor,
|
||||
resolvedGlowColor,
|
||||
opacity,
|
||||
backgroundOpacity,
|
||||
speedMin,
|
||||
speedMax,
|
||||
speedScale,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={className}
|
||||
style={{ position: "absolute", inset: 0 }}
|
||||
>
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
style={{ display: "block", width: "100%", height: "100%" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DottedGlowBackground;
|
||||
@@ -20,8 +20,9 @@ const GLOBE_CONFIG: COBEOptions = {
|
||||
mapSamples: 16000,
|
||||
mapBrightness: 1.1,
|
||||
baseColor: [0.8, 0.8, 0.8], // sleek dark gray globe
|
||||
markerColor: [0.3, 0.6, 1], // soft, elegant blue
|
||||
glowColor: [0.8, 0.8, 0.85], // subtle glow
|
||||
markerColor: [0.02, 0.71, 0.83], // cyan-500
|
||||
glowColor: [0.8, 0.8, 0.85], // grey
|
||||
|
||||
markers: [
|
||||
// --- Core Global Markers ---
|
||||
{ location: [14.5995, 120.9842], size: 0.03 }, // Manila
|
||||
|
||||
21
src/hooks/useMediaQuery.ts
Normal 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
|
||||
}
|
||||
39
src/hooks/useResponsiveCarousel.ts
Normal 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
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -64,7 +64,7 @@
|
||||
--color-gray-900: oklch(0.205 0 0);
|
||||
--color-gray-950: oklch(0.145 0 0);
|
||||
|
||||
--font-sans: var(--font-mulish);
|
||||
--font-sans: var(--font-inter);
|
||||
|
||||
--container-2xl: 40rem;
|
||||
|
||||
|
||||
@@ -24,5 +24,16 @@
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"exclude": ["node_modules"],
|
||||
"extend": {
|
||||
"animation": {
|
||||
"pulse-slow": "pulse 6s ease-in-out infinite"
|
||||
},
|
||||
"keyframes": {
|
||||
"pulse": {
|
||||
"0%, 100%": { "opacity": "1" },
|
||||
"50%": { "opacity": "0.6" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||