Compare commits

95 Commits

Author SHA1 Message Date
76e0bdb7be style: update border colors from gray-200 to gray-300 and standardize cyan color usage 2025-10-22 15:47:52 +02:00
85c041ab49 style: update feature card styling with cyan hover effects and consistent padding 2025-10-22 15:47:41 +02:00
0231c4835c style: update stat cards with consistent hover effects and modern styling 2025-10-22 15:35:35 +02:00
4efc563aa9 feat: add Eyebrow component to section headers across landing page components 2025-10-22 15:27:47 +02:00
d70c2b6874 chore: switch from Mulish to Inter font family 2025-10-22 15:18:39 +02:00
db4c2d8ea0 Merge branch 'main' into development_new 2025-10-22 13:16:44 +02:00
28bef26fbc refactor: remove GetStarted section and consolidate with CallToAction component 2025-10-22 11:38:05 +02:00
73fe0e7c8e feat: add GetStarted section and rename deploy section to how-it-works 2025-10-22 11:34:16 +02:00
c59c09eee8 refactor: replace mailerlite popup with direct mailto link for waitlist signup 2025-10-22 11:32:04 +02:00
593201ae10 feat: add dotted glow background and enhance stack section animations 2025-10-22 11:30:26 +02:00
b46df781f8 feat: add hide-on-scroll header and redesign hero with video background 2025-10-21 16:45:54 +02:00
f44662829d feat: implement light theme version of stack section with hover animations 2025-10-20 17:08:28 +02:00
a663c32f53 feat: add decorative gradient overlays to hero section background 2025-10-20 16:56:00 +02:00
2f3dea92a2 feat: implement light theme hero section with cloud background and updated header 2025-10-20 16:20:51 +02:00
75b660d81e Merge branch 'main' into development 2025-10-20 13:56:07 +02:00
3f86c7e87f docs: add development guide and dependencies section to README 2025-10-20 13:55:50 +02:00
1c2c274848 Update README.md 2025-10-15 10:37:16 +00:00
74d5bae622 Merge branch 'main' into development 2025-10-15 12:00:50 +02:00
1577eb6c6c Update README.md 2025-10-15 09:56:06 +00:00
f4519ec8bf Update README.md 2025-10-15 09:55:00 +00:00
c09928018c docs: add deployment URLs, tech stack, and contribution guidelines to README 2025-10-15 11:50:47 +02:00
f5e5063ba1 update Join the Waitlist button 2025-10-08 12:24:30 +03:00
54333f2bd5 refactor: reposition BentoReviews section below ClickableGallery 2025-10-05 16:49:39 +02:00
e646198255 style: adjust companies section margin and remove negative top positioning 2025-10-05 05:39:07 +02:00
5f1774f03c refactor: reorder homepage sections to show network map before technologies 2025-10-01 12:25:19 +02:00
d52190268a Merge branch 'development' 2025-09-30 17:00:22 +02:00
8b2bbb2536 ok 2025-09-30 15:58:36 +02:00
6f8edae241 Merge branch 'development' 2025-09-29 19:37:09 +02:00
acbbc7445d k 2025-09-29 19:28:00 +02:00
6c3f1afecf Merge branch 'development' 2025-09-29 19:19:57 +02:00
7b5afa588e ok 2025-09-29 19:18:33 +02:00
4cc20ac13f ok 2025-09-29 19:13:25 +02:00
6116c5b87c ok 2025-09-29 18:50:08 +02:00
602b78b5bd style: improve hero section layout and text positioning for better responsiveness 2025-09-19 16:22:26 +02:00
9a4f347ee8 ok 2025-09-19 14:14:04 +02:00
017fc41d2b refactor: consolidate navigation links into reusable NavLinks component with dynamic styling 2025-09-19 14:08:46 +02:00
ab14a5a8e5 feat: add email links and update navigation structure 2025-09-19 14:01:47 +02:00
41d4c3a054 feat: adjust mobile viewport margin and update company branding in footer 2025-09-19 01:21:11 +02:00
a362985d4c style: improve text component spacing and center CTA content on mobile 2025-09-19 01:09:08 +02:00
1d66fd60a4 chore: adjust mobile carousel gap from 100 to 110 pixels 2025-09-19 01:02:53 +02:00
43d995bbc2 feat: add responsive carousel with mobile-optimized layout and controls 2025-09-19 01:01:46 +02:00
0dedde3592 style: adjust vertical padding for Companies component on mobile and desktop views 2025-09-19 00:48:17 +02:00
d5b9303d94 style: adjust spacing and padding in Steps component for better responsive layout 2025-09-19 00:46:28 +02:00
3a240177c4 feat: add responsive layout for WorldMap component on mobile devices 2025-09-19 00:43:42 +02:00
0479b7330a feat: add mobile carousel view with auto-play for BentoReviews component 2025-09-19 00:39:00 +02:00
a78bc67ed3 style: add responsive padding to BentoReviews component for mobile devices 2025-09-19 00:30:38 +02:00
a035500c34 style: adjust spacing and responsive layout for stack section and cubes 2025-09-19 00:27:35 +02:00
2b5c502724 feat: add mobile-friendly cube descriptions with click interaction 2025-09-19 00:12:16 +02:00
a462afc8b2 fix mobile 2025-09-19 00:07:14 +02:00
bf78cde2d8 feat: add row height control to BentoGrid and remove gradient blobs from StackSection 2025-09-18 20:26:37 +02:00
cde6c90033 fx 2025-09-18 20:21:48 +02:00
f5ab743987 style: update button hover states and add white variant to docs button 2025-09-18 20:11:56 +02:00
45364a7452 ok 2025-09-18 20:09:48 +02:00
b7f25d712f fix gry 2025-09-18 19:43:04 +02:00
2fdcb3697d feat: add hover opacity transitions and bottom margin to bento grid components 2025-09-18 18:17:19 +02:00
204625b9a8 add 2025-09-18 18:14:18 +02:00
02557fcb82 ok 2025-09-17 18:32:20 +02:00
9277bc7105 feat: add BentoReviews component with responsive grid layout for AI stack features 2025-09-17 17:21:30 +02:00
3f6ffbe4ea ok 2025-09-17 17:21:23 +02:00
343b7ae22e ok 2025-09-17 16:46:23 +02:00
5f6ad1c98d ok 2025-09-17 16:09:57 +02:00
9e26dce717 add 2025-09-17 15:45:33 +02:00
8f997b2f86 id 2025-09-17 15:35:06 +02:00
f712a6c894 ok 2025-09-17 15:29:08 +02:00
1708aadf1e fav 2025-09-17 15:24:42 +02:00
dadf92559c ok 2025-09-17 15:19:26 +02:00
c3481f3221 ok 2025-09-17 15:16:24 +02:00
8d83f44a07 ok 2025-09-17 15:14:51 +02:00
9faaf7dee9 ok 2025-09-17 15:07:08 +02:00
fa7b8dbbb8 ok 2025-09-17 15:05:02 +02:00
4b5c30c11e ok 2025-09-17 14:58:10 +02:00
bde7d37305 add 2025-09-17 14:55:37 +02:00
b65f2aa538 OK 2025-09-17 14:34:37 +02:00
be949d65bd ok 2025-09-17 14:29:57 +02:00
9ee0d678c9 ok 2025-09-17 14:28:40 +02:00
eb2ad2be3f ok 2025-09-17 14:21:34 +02:00
34bd6522e3 ok 2025-09-16 14:34:13 +02:00
273cb24a47 add 2025-09-16 14:24:25 +02:00
13ecc4d2ec feat: add background video and enhance hover effects in CTA and GetStarted components 2025-09-16 14:05:51 +02:00
979a6523fa OK 2025-09-16 13:54:45 +02:00
de6a83a87c dd stacks 2025-09-16 13:25:24 +02:00
4aff3a5c6b ok 2025-09-16 13:19:22 +02:00
78e1e51877 ok 2025-09-15 21:05:36 +02:00
3df200c7d2 ok 2025-09-15 20:45:18 +02:00
f4c247baa2 ok 2025-09-15 18:20:49 +02:00
cbc89b192b ok 2025-09-15 18:20:25 +02:00
c9af96e669 Merge branch 'development' 2025-09-15 18:14:48 +02:00
225c10a853 ok 2025-09-15 18:14:25 +02:00
d8da5325de ok 2025-09-15 18:01:50 +02:00
6f2f7318bc ok 2025-09-15 17:07:54 +02:00
29398d1e2e ok 2025-09-15 17:05:51 +02:00
e3ede705c7 Update next.config.js 2025-09-15 07:11:29 +00:00
Peter Nashaat
c49a51783a Lock @heroicons/react 2025-09-15 07:08:02 +00:00
4618fc3525 Merge branch 'development' 2025-09-14 17:47:13 +02:00
6e06d215fa chore: move @tailwindcss/postcss from dependencies to devDependencies 2025-09-14 17:30:56 +02:00
93 changed files with 3556 additions and 946 deletions

148
README.md
View File

@@ -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:
---
## About
This is the official website for Mycelium Cloud, built using Next.js and Tailwind CSS.
---
## Technologies
- **Framework**: [Next.js](https://nextjs.org/)
- **Language**: [TypeScript](https://www.typescriptlang.org/)
- **Styling**: [Tailwind CSS](https://tailwindcss.com/)
---
## Dependencies
- **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)
---
## 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
```
Next, run the development server:
2. **Build the Project**:
```bash
npm run dev
npm run build
```
Finally, open [http://localhost:3000](http://localhost:3000) in your browser to view the website.
3. **Start the Development Server**:
## Customizing
```bash
npm run start
```
You can start editing this template by modifying the files in the `/src` folder. The site will auto-update as you edit these files.
---
## License
## Development Guide
This site template is a commercial product and is licensed under the [Tailwind Plus license](https://tailwindcss.com/plus/license).
This project follows a modular, component-based architecture. Pages are assembled by combining reusable components into a single layout.
## Learn more
### Homepage Structure
To learn more about the technologies used in this site template, see the following resources:
The homepage (`src/app/(main)/page.tsx`) is composed of the following components:
- [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
- `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).

View File

@@ -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
View File

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

View File

@@ -1,4 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}
const nextConfig = {
output: 'export',
images: {
unoptimized: true,
},
}
module.exports = nextConfig

85
package-lock.json generated
View File

@@ -9,9 +9,10 @@
"version": "0.1.0",
"dependencies": {
"@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",
"@tailwindcss/postcss": "^4.1.7",
"@types/node": "^20.10.8",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.18",
@@ -34,6 +35,7 @@
"use-debounce": "^10.0.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.13",
"eslint": "^8.56.0",
"eslint-config-next": "^14.0.4",
"prettier": "^3.3.2",
@@ -46,6 +48,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -639,6 +642,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz",
"integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -650,6 +654,7 @@
"version": "0.44.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.44.0.tgz",
"integrity": "sha512-ZX/etZEZw8DR7zAB1eVQT40lNo0jeqpb6dCgOvctB6FIQ5PoXfMuNY8+ayQfu8tNQbAB8gQWSSJupR8NxeiZXw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -660,6 +665,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
"integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -1025,6 +1031,15 @@
"react-dom": "^18 || ^19 || ^19.0.0-rc"
}
},
"node_modules/@heroicons/react": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
"integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
"license": "MIT",
"peerDependencies": {
"react": ">= 16 || ^19.0.0-rc"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
@@ -1617,6 +1632,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
"dev": true,
"license": "ISC",
"dependencies": {
"minipass": "^7.0.4"
@@ -1939,6 +1955,7 @@
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
"integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -1951,6 +1968,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz",
"integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -2959,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",
@@ -2975,6 +3019,7 @@
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz",
"integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
@@ -2990,6 +3035,7 @@
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz",
"integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -3021,6 +3067,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3037,6 +3084,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3053,6 +3101,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3069,6 +3118,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3085,6 +3135,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3101,6 +3152,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3117,6 +3169,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3133,6 +3186,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3149,6 +3203,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3173,6 +3228,7 @@
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -3194,6 +3250,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3210,6 +3267,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3223,6 +3281,7 @@
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.13.tgz",
"integrity": "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
@@ -3263,6 +3322,7 @@
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -4982,6 +5042,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=18"
@@ -5886,6 +5947,7 @@
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
@@ -5971,6 +6033,7 @@
"version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
@@ -8402,6 +8465,7 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
"integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
"dev": true,
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
@@ -8642,6 +8706,7 @@
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
"integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
@@ -8673,6 +8738,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8693,6 +8759,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8713,6 +8780,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8733,6 +8801,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8753,6 +8822,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8773,6 +8843,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8793,6 +8864,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8813,6 +8885,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8833,6 +8906,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8853,6 +8927,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -9003,6 +9078,7 @@
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
"integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
@@ -10223,6 +10299,7 @@
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -10232,6 +10309,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
"integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
"dev": true,
"license": "MIT",
"dependencies": {
"minipass": "^7.1.2"
@@ -10257,6 +10335,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
"dev": true,
"license": "MIT",
"bin": {
"mkdirp": "dist/cjs/src/bin.js"
@@ -11002,6 +11081,7 @@
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -13410,6 +13490,7 @@
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz",
"integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -13423,6 +13504,7 @@
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
"dev": true,
"license": "ISC",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
@@ -14358,6 +14440,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=18"

View File

@@ -11,9 +11,10 @@
"browserslist": "defaults, not ie <= 11",
"dependencies": {
"@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",
"@tailwindcss/postcss": "^4.1.7",
"@types/node": "^20.10.8",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.18",
@@ -36,6 +37,7 @@
"use-debounce": "^10.0.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.13",
"eslint": "^8.56.0",
"eslint-config-next": "^14.0.4",
"prettier": "^3.3.2",

BIN
public/images/benefits.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
public/images/cloud.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

BIN
public/images/logomark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 112 KiB

BIN
public/images/mchip.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
public/images/stars.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

BIN
public/videos/agent.mp4 Normal file

Binary file not shown.

BIN
public/videos/benefits.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
public/videos/benefits.mp4 Normal file

Binary file not shown.

BIN
public/videos/cloud.mp4 Normal file

Binary file not shown.

BIN
public/videos/cloud2.mp4 Normal file

Binary file not shown.

BIN
public/videos/cta.mp4 Normal file

Binary file not shown.

Binary file not shown.

BIN
public/videos/fungistor.mp4 Normal file

Binary file not shown.

BIN
public/videos/herodb.mp4 Normal file

Binary file not shown.

BIN
public/videos/mesh.mp4 Normal file

Binary file not shown.

BIN
public/videos/sandbox.mp4 Normal file

Binary file not shown.

BIN
public/videos/universal.mp4 Normal file

Binary file not shown.

View File

@@ -1 +0,0 @@
export { Layout as default } from '@/components/Layout'

View File

@@ -3,50 +3,47 @@ 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 { CallTo } from '@/components/CallTo'
import { CallToAction } from '@/components/CallToAction'
import { ScrollDown } from '@/components/ui/ScrollDown'
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 />
<HomeHeroLight2 />
</section>
<section id="companies">
<Companies />
<section id="network">
<WorldMap />
</section>
<section id="steps">
<section id="technologies">
<StackSectionLight />
</section>
<section id="how-it-works">
<Steps />
</section>
<section id="grid-stats">
<GridStats />
<section id="llms">
<Companies />
</section>
<section id="stack-section">
<StackSectionPreview />
</section>
<section id="clickable-gallery">
<ClickableGallery />
<ClickableGalleryLight />
</section>
<section id="use-cases">
<UseCases />
<section id="bento-reviews">
<BentoReviews />
</section>
<section id="call-to-action">
<CallTo />
<section id="get-started">
<CallToAction />
</section>
<section id="faqs">
<Faqs />
</section>
<ScrollDown />
<ScrollUp />
</>
)
}

BIN
src/app/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -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,8 +26,21 @@ export default function RootLayout({
children: React.ReactNode
}) {
return (
<html lang="en" className={clsx('antialiased', mulish.variable)}>
<body>{children}</body>
<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>
)
}

View File

@@ -0,0 +1,263 @@
'use client'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import clsx from 'clsx'
import {
ArchiveBoxIcon,
CodeBracketIcon,
CpuChipIcon,
GlobeAltIcon,
MagnifyingGlassIcon,
ShareIcon,
UserGroupIcon,
CheckBadgeIcon,
} from '@heroicons/react/24/solid'
import { Container } from '@/components/Container'
import { H2, P, CT, CP } from '@/components/Texts'
import { motion, useInView } from 'framer-motion'
interface Review {
title: string
body: string
}
const reviews: Array<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.',
},
]
function getReviewIcon(title: string) {
if (title.startsWith('FungiStor')) return ArchiveBoxIcon;
if (title.startsWith('HeroDB')) return CpuChipIcon;
if (title.startsWith('MOS Sandboxes')) return CodeBracketIcon;
if (title.startsWith('Mycelium Mesh')) return ShareIcon;
if (title.startsWith('Deterministic Deployment')) return CheckBadgeIcon;
if (title.startsWith('Agent Coordination')) return UserGroupIcon;
if (title.startsWith('Universal Interface Layer')) return GlobeAltIcon;
if (title.startsWith('Semantic Index & Search')) return MagnifyingGlassIcon;
return GlobeAltIcon; // default
}
function Review({
title,
body,
className,
...props
}: Omit<React.ComponentPropsWithoutRef<'figure'>, keyof Review> & Review) {
let animationDelay = useMemo(() => {
let possibleAnimationDelays = ['0s', '0.1s', '0.2s', '0.3s', '0.4s', '0.5s']
return possibleAnimationDelays[
Math.floor(Math.random() * possibleAnimationDelays.length)
]
}, [])
return (
<figure
className={clsx(
'animate-fade-in rounded-3xl bg-gray-900/50 p-6 opacity-0 shadow-md shadow-gray-900/5',
className,
)}
style={{ animationDelay }}
{...props}
>
<blockquote className="text-white">
{React.createElement(getReviewIcon(title), { className: "h-6 w-6 text-white mb-2" })}
<CT color="light" className="mt-4 text-lg/6 font-semibold">
{title}
</CT>
<CP color="light" className="mt-3 text-sm">{body}</CP>
</blockquote>
</figure>
)
}
function splitArray<T>(array: Array<T>, numParts: number) {
let result: Array<Array<T>> = []
for (let i = 0; i < array.length; i++) {
let index = i % numParts
if (!result[index]) {
result[index] = []
}
result[index].push(array[i])
}
return result
}
function ReviewColumn({
reviews,
className,
reviewClassName,
msPerPixel = 0,
}: {
reviews: Array<Review>
className?: string
reviewClassName?: (reviewIndex: number) => string
msPerPixel?: number
}) {
let columnRef = useRef<React.ElementRef<'div'>>(null)
let [columnHeight, setColumnHeight] = useState(0)
let duration = `${columnHeight * msPerPixel}ms`
useEffect(() => {
if (!columnRef.current) {
return
}
let resizeObserver = new window.ResizeObserver(() => {
setColumnHeight(columnRef.current?.offsetHeight ?? 0)
})
resizeObserver.observe(columnRef.current)
return () => {
resizeObserver.disconnect()
}
}, [])
return (
<div
ref={columnRef}
className={clsx('animate-marquee space-y-8 py-4', className)}
style={{ '--marquee-duration': duration } as React.CSSProperties}
>
{reviews.concat(reviews).map((review, reviewIndex) => (
<Review
key={reviewIndex}
aria-hidden={reviewIndex >= reviews.length}
className={reviewClassName?.(reviewIndex % reviews.length)}
{...review}
/>
))}
</div>
)
}
function ReviewGrid() {
let containerRef = useRef<React.ElementRef<'div'>>(null)
let isInView = useInView(containerRef, { once: true, amount: 0.4 })
let columns = splitArray(reviews, 3)
let column1 = columns[0]
let column2 = columns[1]
let column3 = splitArray(columns[2], 2)
return (
<div
ref={containerRef}
className="relative -mx-4 mt-0 grid h-196 max-h-[150vh] grid-cols-1 items-start gap-8 overflow-hidden px-4 md:grid-cols-2 lg:grid-cols-3"
>
{isInView && (
<>
<ReviewColumn
reviews={[...column1, ...column3.flat(), ...column2]}
reviewClassName={(reviewIndex) =>
clsx(
reviewIndex >= column1.length + column3[0].length &&
'md:hidden',
reviewIndex >= column1.length && 'lg:hidden',
)
}
msPerPixel={10}
/>
<ReviewColumn
reviews={[...column2, ...column3[1]]}
className="hidden md:block"
reviewClassName={(reviewIndex) =>
reviewIndex >= column2.length ? 'lg:hidden' : ''
}
msPerPixel={15}
/>
<ReviewColumn
reviews={column3.flat()}
className="hidden lg:block"
msPerPixel={10}
/>
</>
)}
<div className="pointer-events-none absolute inset-x-0 top-0 h-32 bg-linear-to-b from-black" />
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-32 bg-linear-to-t from-black" />
</div>
)
}
export function UseCases() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
return (
<section
id="usecases"
ref={ref}
aria-labelledby="usecases-title"
className="bg-black py-24"
>
<Container className=''>
<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-2xl lg:max-w-5xl"
>
<H2
id="usecases-title"
color="light"
className="text-center"
>
Coming Soon: The Future of Mycelium
</H2>
<P className="mt-6 text-center" color="light">
Mycelium Cloud is evolving to bring even more powerful decentralized features, designed to enhance your experience and expand possibilities. Be the first to explore what's coming next by staying connected with our latest updates.
</P>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 1 } : { opacity: 0 }}
transition={{ duration: 1, delay: 0.2 }}
aria-hidden="true"
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
>
<div
style={{
clipPath:
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
}}
className="relative left-[calc(50%-30rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#93c5fd] to-[#9089fc] opacity-30 sm:left-[calc(50%-36rem)] sm:w-[72.1875rem]"
/>
</motion.div>
<ReviewGrid />
</Container>
</section>
)
}

View File

@@ -0,0 +1,116 @@
"use client";
import React from "react";
import Image from "next/image";
import { motion } from "framer-motion";
import { CT, CP } from '@/components/Texts';
interface CubeProps {
title: string;
descriptionTitle: string;
description: string;
isActive: boolean;
index: number;
onHover: () => void;
onLeave: () => void;
}
export function Cube({ title, descriptionTitle, description, isActive, index, onHover, onLeave }: CubeProps) {
return (
<div className="relative flex flex-col items-center">
<motion.div
className="relative cursor-pointer pointer-events-none"
onMouseEnter={onHover}
onMouseLeave={onLeave}
style={{
zIndex: 10 - index,
}}
animate={{
scale: isActive ? 1.05 : 1,
}}
transition={{
duration: 0.3,
ease: "easeOut",
}}
>
{/* Image Cube */}
<Image
src="/images/cube.png"
alt="Cube"
width={507}
height={234}
className="w-60 sm:w-80 lg:w-96 h-auto drop-shadow-lg pointer-events-auto"
style={{
filter: isActive
? 'brightness(1.1) drop-shadow(0 25px 25px rgba(144, 137, 252, 0.4))'
: 'brightness(0.9) drop-shadow(0 10px 15px rgba(144, 137, 252, 0.2))',
}}
/>
{/* Title overlay */}
<div className="absolute inset-0 flex items-center justify-center">
<CT as="h3" className="px-4 drop-shadow-lg" style={{ transform: 'rotate(0deg) skewX(0deg)', transformOrigin: 'center' }}>
{title}
</CT>
</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="currentColor"
strokeWidth="1"
opacity="0.6"
/>
</svg>
{/* Description text */}
<div className="ml-32 w-80 text-[#2F3178]">
<h4 className="text-base font-semibold mb-2">
{descriptionTitle}
</h4>
<CP color="custom">{description}</CP>
</div>
</motion.div>
)}
{/* Description for Mobile - Below cube */}
{isActive && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
transition={{ duration: 0.3 }}
className="lg:hidden absolute top-full left-1/2 -translate-x-1/2 mt-8 z-50"
>
<div className="w-64 sm:w-80 px-4 text-[#2F3178]">
<h4 className="text-base font-semibold mb-2 text-center">
{descriptionTitle}
</h4>
<CP className="text-center" color="custom">{description}</CP>
</div>
</motion.div>
)}
</motion.div>
</div>
);
}

View File

@@ -0,0 +1,117 @@
"use client";
import CountUp from "react-countup";
import React from "react";
import { Button } from "@/components/Button";
export function GridStats() {
return (
<div id="grid-stats" className="py-24 bg-transparent relative">
<div className="mx-auto max-w-2xl px-6 lg:max-w-7xl lg:px-8">
<div className="grid grid-cols-1 gap-8 lg:grid-cols-3">
{/* Column 1: Title & NODES */}
<div className="flex flex-col space-y-10">
{/* Title + Description */}
<div>
<h2 className="text-2xl font-semibold tracking-tight leading-tight text-black lg:text-4xl">
Powered by a Global Community
</h2>
<p className="mt-4 sm:mt-6 text-sm font-light text-pretty text-black lg:text-base">
ThreeFolds groundbreaking technology enables anyone individuals, organizations, and communities to deploy their own Internet infrastructure.
</p>
<Button className="mt-8" variant="outline" href="https://threefold.io/build" >Explore TFGrid </Button>
</div>
</div>
{/* Column 2: CORES (staggered) + SSD */}
<div className="flex flex-col space-y-10">
<StatCard
label="CORES"
description="A globally distributed mesh of CPU cores powering decentralized applications, AI workloads, and edge computing — without central bottlenecks."
value={<CountUp end={54_958} duration={2.5} separator="," />}
note="Total Central Processing Unit Cores available on the grid."
/>
<StatCard
label="SSD CAPACITY"
description="A distributed network of storage capacity — ready to support Web3, AI, and edge computing workloads around the world."
value={<CountUp end={7_364_506} duration={2.5} separator="," />}
unit="GB"
note="The total amount of storage (SSD, HDD, & RAM) on the grid."
/>
</div>
{/* Column 3: nodes countries */}
<div className="flex flex-col space-y-10 justify-start">
<StatCard
label="NODES"
description="A computer server 100% dedicated to the network. It is a building block of the ThreeFold Grid, providing compute, storage, and network resources."
value={<CountUp end={1778} duration={2.5} separator="," />}
note="The total number of nodes on the grid."
/>
<StatCard
label="COUNTRIES"
description="The number of countries where at least one node is connected and operational on the grid."
value={<CountUp end={51} duration={2.5} separator="," />}
note="The total number of countries with active nodes."
/>
</div>
</div>
</div>
</div>
);
}
// 🧱 Stat Card Component
function StatCard({
label,
description,
value,
unit,
note,
className = "",
}: {
label: string;
description: string;
value: React.ReactNode;
unit?: string;
note: string;
className?: string;
}) {
return (
<div
className={`relative flex flex-col overflow-hidden rounded-3xl bg-white shadow-md shadow-gray-900/5 p-8 transition-all duration-300 ease-out hover:scale-105 ${className}`}
style={{
filter: 'brightness(1)',
}}
onMouseEnter={(e) => {
e.currentTarget.style.filter = 'brightness(0.8)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.filter = 'brightness(1)';
}}
>
<h3 className="text-lg font-semibold text-gradient-neutral-vertical" style={{ textShadow: '0 0 12px rgba(255, 255, 255, 0.4), 0 0 24px rgba(255, 255, 255, 0.2)' }}>{label}</h3>
<p className="mt-2 text-sm font-light text-pretty text-black lg:text-base">
{description}
</p>
<div className="mt-8 flex items-center space-x-3">
<span className="text-gradient-neutral-vertical text-3xl"></span>
<div className="text-5xl font-semibold tracking-tight text-black tabular-nums">
{value}
{unit && (
<span className="ml-2 text-lg font-normal text-gray-800">{unit}</span>
)}
</div>
</div>
<p className="mt-2 text-sm text-gray-800 uppercase tracking-wider">
{note}
</p>
</div>
);
}

View File

@@ -0,0 +1,71 @@
'use client'
import React, { useRef } from 'react'
import { motion, useInView } from 'framer-motion'
import { H2, P, CT, CP } from '@/components/Texts'
import { TbCircleNumber1Filled, TbCircleNumber2Filled, TbCircleNumber3Filled } from 'react-icons/tb'
const features = [
{
name: 'Choose Your Intelligence',
description: 'Explore a library of leading LLMs and agentic functions. Pick the ones that fit your use case, from general assistants to specialized reasoning models.',
icon: TbCircleNumber1Filled,
},
{
name: 'Add Your Knowledge',
description:
'Connect your data or knowledge base to enable personalized, context-aware results while keeping your information private.',
icon: TbCircleNumber2Filled,
},
{
name: 'Define Your Network',
description:
'Set up and manage your nodes with ease. Scale compute and storage as you grow, while staying fully sovereign and decentralized.',
icon: TbCircleNumber3Filled,
},
]
export function Steps() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
return (
<section id="benefits" ref={ref} className="bg-white pt-0 pb-24 dark:bg-gray-900">
<div className="mx-auto max-w-7xl px-6 lg:px-0">
<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"
>
<H2 className="text-3xl font-medium tracking-tight">
Deploy Scalable LLMs and AI Agents in Seconds
</H2>
<P className="mt-6 text-lg" color="custom">
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>
<motion.ul
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"
>
{features.map((feature, index) => (
<motion.li
key={feature.name}
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-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>
<CP className="mt-2 text-sm" color="custom">{feature.description}</CP>
</motion.li>
))}
</motion.ul>
</div>
</section>
)
}

View File

@@ -0,0 +1,263 @@
'use client'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import clsx from 'clsx'
import {
ArchiveBoxIcon,
CodeBracketIcon,
CpuChipIcon,
GlobeAltIcon,
MagnifyingGlassIcon,
ShareIcon,
UserGroupIcon,
CheckBadgeIcon,
} from '@heroicons/react/24/solid'
import { Container } from '@/components/Container'
import { H2, P, CT, CP } from '@/components/Texts'
import { motion, useInView } from 'framer-motion'
interface Review {
title: string
body: string
}
const reviews: Array<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.',
},
]
function getReviewIcon(title: string) {
if (title.startsWith('FungiStor')) return ArchiveBoxIcon;
if (title.startsWith('HeroDB')) return CpuChipIcon;
if (title.startsWith('MOS Sandboxes')) return CodeBracketIcon;
if (title.startsWith('Mycelium Mesh')) return ShareIcon;
if (title.startsWith('Deterministic Deployment')) return CheckBadgeIcon;
if (title.startsWith('Agent Coordination')) return UserGroupIcon;
if (title.startsWith('Universal Interface Layer')) return GlobeAltIcon;
if (title.startsWith('Semantic Index & Search')) return MagnifyingGlassIcon;
return GlobeAltIcon; // default
}
function Review({
title,
body,
className,
...props
}: Omit<React.ComponentPropsWithoutRef<'figure'>, keyof Review> & Review) {
let animationDelay = useMemo(() => {
let possibleAnimationDelays = ['0s', '0.1s', '0.2s', '0.3s', '0.4s', '0.5s']
return possibleAnimationDelays[
Math.floor(Math.random() * possibleAnimationDelays.length)
]
}, [])
return (
<figure
className={clsx(
'animate-fade-in rounded-3xl bg-white p-6 opacity-0 shadow-md shadow-gray-900/5',
className,
)}
style={{ animationDelay }}
{...props}
>
<blockquote className="text-gray-900">
{React.createElement(getReviewIcon(title), { className: "h-6 w-6 text-[#2F3178] mb-2" })}
<CT color="primary" className="mt-4 text-lg/6 font-semibold">
{title}
</CT>
<CP color="custom" className="mt-3 text-sm">{body}</CP>
</blockquote>
</figure>
)
}
function splitArray<T>(array: Array<T>, numParts: number) {
let result: Array<Array<T>> = []
for (let i = 0; i < array.length; i++) {
let index = i % numParts
if (!result[index]) {
result[index] = []
}
result[index].push(array[i])
}
return result
}
function ReviewColumn({
reviews,
className,
reviewClassName,
msPerPixel = 0,
}: {
reviews: Array<Review>
className?: string
reviewClassName?: (reviewIndex: number) => string
msPerPixel?: number
}) {
let columnRef = useRef<React.ElementRef<'div'>>(null)
let [columnHeight, setColumnHeight] = useState(0)
let duration = `${columnHeight * msPerPixel}ms`
useEffect(() => {
if (!columnRef.current) {
return
}
let resizeObserver = new window.ResizeObserver(() => {
setColumnHeight(columnRef.current?.offsetHeight ?? 0)
})
resizeObserver.observe(columnRef.current)
return () => {
resizeObserver.disconnect()
}
}, [])
return (
<div
ref={columnRef}
className={clsx('animate-marquee space-y-8 py-4', className)}
style={{ '--marquee-duration': duration } as React.CSSProperties}
>
{reviews.concat(reviews).map((review, reviewIndex) => (
<Review
key={reviewIndex}
aria-hidden={reviewIndex >= reviews.length}
className={reviewClassName?.(reviewIndex % reviews.length)}
{...review}
/>
))}
</div>
)
}
function ReviewGrid() {
let containerRef = useRef<React.ElementRef<'div'>>(null)
let isInView = useInView(containerRef, { once: true, amount: 0.4 })
let columns = splitArray(reviews, 3)
let column1 = columns[0]
let column2 = columns[1]
let column3 = splitArray(columns[2], 2)
return (
<div
ref={containerRef}
className="relative -mx-4 mt-0 grid h-196 max-h-[150vh] grid-cols-1 items-start gap-8 overflow-hidden px-4 sm:mt-20 md:grid-cols-2 lg:grid-cols-3"
>
{isInView && (
<>
<ReviewColumn
reviews={[...column1, ...column3.flat(), ...column2]}
reviewClassName={(reviewIndex) =>
clsx(
reviewIndex >= column1.length + column3[0].length &&
'md:hidden',
reviewIndex >= column1.length && 'lg:hidden',
)
}
msPerPixel={10}
/>
<ReviewColumn
reviews={[...column2, ...column3[1]]}
className="hidden md:block"
reviewClassName={(reviewIndex) =>
reviewIndex >= column2.length ? 'lg:hidden' : ''
}
msPerPixel={15}
/>
<ReviewColumn
reviews={column3.flat()}
className="hidden lg:block"
msPerPixel={10}
/>
</>
)}
<div className="pointer-events-none absolute inset-x-0 top-0 h-32 bg-linear-to-b from-white" />
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-32 bg-linear-to-t from-white" />
</div>
)
}
export function UseCases() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
return (
<section
id="usecases"
ref={ref}
aria-labelledby="usecases-title"
className="py-12"
>
<Container className=''>
<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-2xl lg:max-w-5xl"
>
<H2
id="usecases-title"
color="primary"
className="text-center"
>
Coming Soon: The Future of Mycelium
</H2>
<P className="mt-6 text-center" color="custom">
Mycelium Cloud is evolving to bring even more powerful decentralized features, designed to enhance your experience and expand possibilities. Be the first to explore what's coming next by staying connected with our latest updates.
</P>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 1 } : { opacity: 0 }}
transition={{ duration: 1, delay: 0.2 }}
aria-hidden="true"
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
>
<div
style={{
clipPath:
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
}}
className="relative left-[calc(50%-30rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#93c5fd] to-[#9089fc] opacity-30 sm:left-[calc(50%-36rem)] sm:w-[72.1875rem]"
/>
</motion.div>
<ReviewGrid />
</Container>
</section>
)
}

View File

@@ -0,0 +1,111 @@
import { Globe } from "@/components/ui/globe"
import { motion } from "framer-motion"
import { H2, H4, CP, CT } from "@/components/Texts"
export function WorldMap() {
return (
<div className="relative h-screen max-w-full overflow-hidden -top-20">
{/* Background video */}
<video
autoPlay
loop
muted
playsInline
className="absolute inset-0 w-full h-full object-cover"
>
<source src="/videos/benefits.mp4" type="video/mp4" />
</video>
{/* Dark overlay */}
<div className="absolute inset-0 bg-black/60" />
{/* 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="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>
</div>
{/* Globe Centered */}
<div className="absolute inset-0 flex items-center justify-center">
<Globe className="top-28" />
</div>
{/* Right Side Stats Column */}
<div className="absolute right-0 top-0 bottom-0 flex flex-col justify-center gap-6 px-6 py-12 max-w-xs z-10">
<motion.div
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
className="rounded-2xl bg-white/5 backdrop-blur-md border border-white/10 p-4 shadow-md"
>
<CT color="light" className="uppercase tracking-wide">
CORES
</CT>
<H2 color="light" className="mt-2 text-3xl font-bold">
54,958
</H2>
<CP color="light" className="mt-2 text-sm">
Total Central Processing Unit Cores available on the grid.
</CP>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="rounded-2xl bg-white/5 backdrop-blur-md border border-white/10 p-4 shadow-md"
>
<CT color="light" className="uppercase tracking-wide">
NODES
</CT>
<H2 color="light" className="mt-2 text-3xl font-bold">
54,958
</H2>
<CP color="light" className="mt-2 text-sm">
Total number of nodes on the grid.
</CP>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
className="rounded-2xl bg-white/5 backdrop-blur-md border border-white/10 p-4 shadow-md"
>
<CT color="light" className="uppercase tracking-wide">
SSD CAPACITY
</CT>
<H2 color="light" className="mt-2 text-3xl font-bold">
54,958
</H2>
<CP color="light" className="mt-2 text-sm">
Total amount of storage (SSD, HDD, & RAM) on the grid.
</CP>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
className="rounded-2xl bg-white/5 backdrop-blur-md border border-white/10 p-4 shadow-md"
>
<CT color="light" className="uppercase tracking-wide">
COUNTRIES
</CT>
<H2 color="light" className="mt-2 text-3xl font-bold">
51
</H2>
<CP color="light" className="mt-2 text-sm">
Total number of countries with active nodes.
</CP>
</motion.div>
</div>
{/* Radial fade overlay */}
<div className="pointer-events-none absolute inset-0 h-full bg-[radial-gradient(circle_at_50%_200%,rgba(0,0,0,0.2),rgba(255,255,255,0))]" />
</div>
)
}

View File

@@ -0,0 +1,171 @@
"use client";
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";
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 thats 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>
<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 encryptedand verifiable from intent to execution.
</P>
</div>
</FadeIn>
</div>
{/* Desktop Grid */}
<div className="hidden lg:block">
<BentoGrid className="max-w-8xl lg:px-12 px-4 pb-12 lg:grid-cols-3">
{items.map((item, i) => (
<MotionBentoGridItem
key={i}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false, margin: '0px 0px -100px 0px' }}
transition={{ duration: 0.8, delay: 0.3 + i * 0.1 }}
className={cn(i === 3 || i === 6 ? "md:col-span-2" : "")}
rowHeight={i >= 3 ? "h-[22rem]" : ""}
title={item.title}
subtitle={item.subtitle}
description={item.description}
video={item.video}
/>
))}
</BentoGrid>
</div>
{/* Mobile Carousel */}
<div className="lg:hidden block px-4 pb-12">
<div className="relative h-[24rem] w-full overflow-hidden">
<div className="absolute inset-0" onTouchStart={handleCardTap} />
<AnimatePresence initial={false}>
<motion.div
key={activeIndex}
className="absolute h-full w-full"
initial={{ x: "100%", opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: "-100%", opacity: 0 }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
>
<MotionBentoGridItem
className="h-full"
title={items[activeIndex].title}
subtitle={items[activeIndex].subtitle}
description={items[activeIndex].description}
video={items[activeIndex].video}
/>
</motion.div>
</AnimatePresence>
<button
onClick={handlePrev}
className="absolute left-2 top-[58%] -translate-y-1/2 rounded-full bg-black/50 p-2 text-white z-10"
>
<ChevronLeft className="h-6 w-6" />
</button>
<button
onClick={handleNext}
className="absolute right-2 top-[58%] -translate-y-1/2 rounded-full bg-black/50 p-2 text-white z-10"
>
<ChevronRight className="h-6 w-6" />
</button>
</div>
</div>
</div>
);
}

View File

@@ -5,18 +5,19 @@ const baseStyles = {
solid:
'inline-flex justify-center rounded-lg py-2 px-3 text-sm font-semibold transition-colors',
outline:
'inline-flex justify-center rounded-lg border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(3)-1px)] text-sm transition-colors',
'inline-flex justify-center rounded-lg border py-[calc(theme(spacing.2)-1px)] px-[calc(theme(spacing.3)-1px)] text-sm transition-colors',
}
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-200 hover:border-gray-400 active:bg-gray-100 active:text-gray-700/80',
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 hover:text-gray-300 active:bg-gray-100 active:text-gray-800',
},
}

View File

@@ -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>

View File

@@ -0,0 +1,62 @@
'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 border-t border-gray-800"
>
<video
autoPlay
loop
muted
playsInline
className="absolute top-0 left-0 w-full h-full object-cover z-0"
>
<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-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">
<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>
<p className="mt-4 text-lg text-gray-300">
Why hand out your intelligence to centralized giants when you can
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="https://calendly.com/sachao/30min"
target="_blank"
rel="noopener noreferrer"
>
Book a Meeting
</Button>
<Button
variant="outline"
color="white"
href="mailto:info@ourworld.tf"
>
Join the Waitlist
</Button>
</div>
</div>
</FadeIn>
</Container>
</section>
)
}

View File

@@ -1,41 +1,37 @@
'use client'
import { useEffect, useMemo, useState, useRef } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useResponsiveCarousel } from '@/hooks/useResponsiveCarousel';
import Image from 'next/image'
import { motion, AnimatePresence, useInView } from 'framer-motion'
import { motion, AnimatePresence } from 'framer-motion'
import { wrap } from 'popmotion'
import { Button } from '@/components/Button';
import { H2, P, H4 } from '@/components/Texts';
import { 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,127 +49,99 @@ export function ClickableGallery() {
const prev = () => setActive((i) => wrap(0, galleryItems.length, i - 1))
return (
<div ref={ref}>
<div className="relative isolate pt-24 pb-0 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">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="custom">
The future isnt about more tools. Its about one intelligent partner that can do it all. This is your gateway to creativity, automation, and discovery.
</P>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 1 } : { opacity: 0 }}
transition={{ duration: 1, delay: 0.3 }}
aria-hidden="true"
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
>
<div
style={{
clipPath:
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
}}
className="relative left-[calc(50%-30rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#93c5fd] to-[#9089fc] opacity-30 sm:left-[calc(50%-36rem)] sm:w-[72.1875rem]"
/>
</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-background -mt-16 pt-0 pb-32"
</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 intelligenceyours.
</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)}
>
{/* Soft edge fades */}
<div className="pointer-events-none absolute inset-y-0 left-0 w-32 bg-gradient-to-r from-background to-transparent" />
<div className="pointer-events-none absolute inset-y-0 right-0 w-32 bg-gradient-to-l from-background to-transparent" />
<div className="relative w-full max-w-[1800px] h-[500px]" style={{ perspective: '1600px' }}>
<div
aria-hidden="true"
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
>
<div
style={{
clipPath:
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
}}
className="relative left-[calc(50%+4rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#93c5fd] to-[#9089fc] opacity-30 sm:left-[calc(50%+60rem)] sm:w-[72.1875rem]"
/>
</div>
<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"
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]">
{/* 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]}
@@ -181,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>
);
}

View 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 intelligenceyours.
</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>
);
}

View File

@@ -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">
<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>

View File

@@ -0,0 +1,18 @@
'use client'
import CountUp from 'react-countup'
import { SectionHeader } from './Texts'
interface CountUpNumberProps {
end: number
className?: string
color?: 'light' | 'primary' | 'secondary'
}
export function CountUpNumber({ end, className, color }: CountUpNumberProps) {
return (
<SectionHeader color={color} className={className}>
<CountUp end={end} duration={2.75} separator="," />
</SectionHeader>
)
}

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

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

View File

@@ -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 }}

View File

@@ -1,14 +1,21 @@
import { useId } from 'react'
import clsx from 'clsx'
const formClasses =
'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-hidden focus:ring-cyan-500 sm:text-sm'
const formClasses = {
light:
'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',
}
function Label({ id, children }: { id: string; children: React.ReactNode }) {
function Label({ id, children, variant = 'light' }: { id: string; children: React.ReactNode, variant?: 'light' | 'dark' }) {
return (
<label
htmlFor={id}
className="mb-2 block text-sm font-semibold text-gray-900"
className={clsx(
'mb-2 block text-sm font-semibold',
variant === 'light' ? 'text-gray-900' : 'text-white',
)}
>
{children}
</label>
@@ -19,14 +26,15 @@ export function TextField({
label,
type = 'text',
className,
variant = 'light',
...props
}: Omit<React.ComponentPropsWithoutRef<'input'>, 'id'> & { label?: string }) {
}: Omit<React.ComponentPropsWithoutRef<'input'>, 'id'> & { label?: string; variant?: 'light' | 'dark' }) {
let id = useId()
return (
<div className={className}>
{label && <Label id={id}>{label}</Label>}
<input id={id} type={type} {...props} className={formClasses} />
{label && <Label id={id} variant={variant}>{label}</Label>}
<input id={id} type={type} {...props} className={formClasses[variant]} />
</div>
)
}

View File

@@ -22,56 +22,11 @@ function QrCodeBorder(props: React.ComponentPropsWithoutRef<'svg'>) {
export function Footer() {
return (
<footer id="footer" className="border-t border-gray-200">
<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-gray-900">
<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-100 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-300 transition-colors group-hover:stroke-cyan-500" />
<Image src={qrCode} alt="" unoptimized />
</div>
<div className="ml-8 lg:w-64">
<p className="text-base font-semibold text-gray-900">
<Link href="#">
<span className="absolute inset-0 sm:rounded-2xl" />
Download the app
</Link>
</p>
<p className="mt-1 text-sm text-gray-700">
Scan the QR code to download the app from the App Store.
</p>
</div>
</div>
</div>
<div className="flex flex-col items-center border-t border-gray-200 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
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-500 md:mt-0">
&copy; Copyright ThreeFold {new Date().getFullYear()}. All rights reserved.
<div className="flex justify-center py-8">
<p className="text-sm text-gray-400">
&copy; Copyright OurWorld Holdings, {new Date().getFullYear()}. All rights reserved.
</p>
</div>
</Container>

View File

@@ -0,0 +1,75 @@
"use client";
import { SectionHeader, P, CP } from "@/components/Texts";
import { Button } from "@/components/Button";
export function GetStarted() {
const items = [
{
title: "Mycelium Docs",
description:
"Learn how Mycelium works and get a high-level understanding of its architecture, deployment, and API references.",
href: "/docs",
buttonText: "Docs",
},
{
title: "Mycelium Repository",
description:
"Explore the official code repositories, contribute, and stay up-to-date with the latest changes.",
href: "https://github.com/your-repo", // replace with actual repo
buttonText: "Explore",
},
{
title: "Mycelium Support",
description:
"Need help? Reach out to our support team or join the community to get your questions answered quickly.",
href: "/support",
buttonText: "Support",
},
];
return (
<section
className="bg-cover bg-center text-white py-32 px-6 relative"
style={{
backgroundImage: `url('/images/getstarted.webp')`,
}}
>
<div className="max-w-8xl mx-auto px-4 text-left mb-12">
<SectionHeader>Get Started</SectionHeader>
<P>Explore the documentation, code, and support channels to start building with Mycelium Cloud.</P>
</div>
<div className="grid gap-4 md:grid-cols-3 max-w-8xl mx-auto px-4">
{items.map((item, idx) => (
<div
key={idx}
className="relative flex flex-col justify-between overflow-hidden rounded-2xl border border-white/10 bg-black/40 p-6 shadow-md shadow-gray-900/5 transition-all duration-300 ease-out hover:scale-105 hover:border-white/40 hover:bg-black/60"
style={{
filter: 'brightness(1)',
}}
onMouseEnter={(e) => {
e.currentTarget.style.filter = 'brightness(0.8)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.filter = 'brightness(1)';
}}
>
{/* Title + Button Row */}
<div className="flex items-center justify-between">
<h3 className="text-xl font-semibold">{item.title}</h3>
<Button variant="outline" href={item.href} color="white">
{item.buttonText}
</Button>
</div>
{/* Divider + Description */}
<div className="mt-4 border-t border-white/10"></div>
<CP className="mt-4">{item.description}</CP>
</div>
))}
</div>
</section>
);
}

View File

@@ -2,63 +2,56 @@
import CountUp from "react-countup";
import React from "react";
import { Button } from "./Button";
import { Button } from "@/components/Button";
import { SectionHeader, P } from "@/components/Texts";
export function GridStats() {
return (
<div id="grid-stats" className="py-24 bg-transparent relative">
<div className="mx-auto max-w-2xl px-6 lg:max-w-7xl lg:px-8">
<div className="grid grid-cols-1 gap-8 lg:grid-cols-3">
{/* Column 1: Title & NODES */}
<div
id="grid-stats"
className="py-24 relative -top-20 "
style={{
backgroundImage: "url(/images/benefits.webp)",
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
<div className="max-w-2xl px-6 lg:max-w-7xl lg:px-6">
<div className="grid grid-cols-1 gap-16 lg:grid-cols-3">
{/* Column 1: Title & Description */}
<div className="flex flex-col space-y-10">
{/* Title + Description */}
<div>
<h2 className="text-2xl font-semibold tracking-tight leading-tight text-black lg:text-4xl">
Powered by a Global Community
</h2>
<p className="mt-4 sm:mt-6 text-sm font-light text-pretty text-black lg:text-base">
ThreeFolds groundbreaking technology enables anyone individuals, organizations, and communities to deploy their own Internet infrastructure.
</p>
<Button className="mt-8" variant="outline" href="https://threefold.io/build" >Explore TFGrid </Button>
<SectionHeader color="light">
Robust Infrastructure for your Intellegence Needs
</SectionHeader>
<P color="light" className="mt-6">
Mycelium's groundbreaking technology provides dedicated, performance-validated GPUs for your AI workloads.
</P>
<Button className="mt-8" variant="outline" href="https://threefold.io/build" color="white">Explore TFGrid →</Button>
</div>
</div>
</div>
{/* Column 2: CORES (staggered) + SSD */}
{/* Column 2: StatCards */}
<div className="flex flex-col space-y-10">
<StatCard
label="CORES"
description="A globally distributed mesh of CPU cores powering decentralized applications, AI workloads, and edge computing — without central bottlenecks."
value={<CountUp end={54_958} duration={2.5} separator="," />}
note="Total Central Processing Unit Cores available on the grid."
label="Dedicated Hosting"
description="Run LLMs, VLMs, and diffusion models on single-tenant GPUs with private endpoints. Bring your own weights or deploy from a curated library of open models."
/>
<StatCard
label="SSD CAPACITY"
description="A distributed network of storage capacity — ready to support Web3, AI, and edge computing workloads around the world."
value={<CountUp end={7_364_506} duration={2.5} separator="," />}
unit="GB"
note="The total amount of storage (SSD, HDD, & RAM) on the grid."
label="Data Sovereignty"
description="Mycelium nodes run on trusted infrastructure you own or choose, ensuring that no third party can access, train on, or monetize your data."
/>
</div>
{/* Column 3: nodes countries */}
<div className="flex flex-col space-y-10 justify-start">
{/* Column 3: StatCards */}
<div className="flex flex-col space-y-10 justify-start mt-20">
<StatCard
label="NODES"
description="A computer server 100% dedicated to the network. It is a building block of the ThreeFold Grid, providing compute, storage, and network resources."
value={<CountUp end={1778} duration={2.5} separator="," />}
note="The total number of nodes on the grid."
label="Seamless Scalability"
description="Myceliums decentralized infrastructure dynamically allocates compute, storage, and bandwidth across the network, so your AI workloads remain fast and resilient even under heavy demand."
/>
<StatCard
label="COUNTRIES"
description="The number of countries where at least one node is connected and operational on the grid."
value={<CountUp end={51} duration={2.5} separator="," />}
note="The total number of countries with active nodes."
label="Composable Agent Ecosystem"
description="Mix and match agents for every part of your workflow: data ingestion, cleaning, orchestration, analysis, and reporting."
/>
</div>
</div>
@@ -71,21 +64,15 @@ export function GridStats() {
function StatCard({
label,
description,
value,
unit,
note,
className = "",
className = "border border-white/10 hover:border-white/40 hover:bg-black/40",
}: {
label: string;
description: string;
value: React.ReactNode;
unit?: string;
note: string;
className?: string;
}) {
return (
<div
className={`relative flex flex-col overflow-hidden rounded-3xl bg-white shadow-md shadow-gray-900/5 p-8 transition-all duration-300 ease-out hover:scale-105 ${className}`}
className={`relative flex flex-col overflow-hidden rounded-3xl bg-gray-900/75 shadow-md shadow-gray-900/5 p-8 transition-all duration-300 ease-out hover:scale-105 ${className}`}
style={{
filter: 'brightness(1)',
}}
@@ -96,22 +83,10 @@ function StatCard({
e.currentTarget.style.filter = 'brightness(1)';
}}
>
<h3 className="text-lg font-semibold text-gradient-neutral-vertical" style={{ textShadow: '0 0 12px rgba(255, 255, 255, 0.4), 0 0 24px rgba(255, 255, 255, 0.2)' }}>{label}</h3>
<p className="mt-2 text-sm font-light text-pretty text-black lg:text-base">
<h3 className="text-lg font-semibold text-white" style={{ textShadow: '0 0 12px rgba(255, 255, 255, 0.4), 0 0 24px rgba(255, 255, 255, 0.2)' }}>{label}</h3>
<p className="mt-2 text-sm font-light text-pretty text-white lg:text-base">
{description}
</p>
<div className="mt-8 flex items-center space-x-3">
<span className="text-gradient-neutral-vertical text-3xl"></span>
<div className="text-5xl font-semibold tracking-tight text-black tabular-nums">
{value}
{unit && (
<span className="ml-2 text-lg font-normal text-gray-800">{unit}</span>
)}
</div>
</div>
<p className="mt-2 text-sm text-gray-800 uppercase tracking-wider">
{note}
</p>
</div>
);
}

View File

@@ -48,7 +48,7 @@ function MobileNavLink(
return (
<PopoverButton
as={Link}
className="block text-base/7 tracking-tight text-[#2F3178]"
className="block text-base/7 tracking-tight text-white"
{...props}
/>
)
@@ -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">
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>

View 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>
)
}

View File

@@ -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>

View File

@@ -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: '#' },
@@ -27,13 +27,14 @@ export function HomeHero() {
playsInline
className="absolute inset-0 w-full h-full object-cover"
>
<source src="/videos/mycelium.mp4" type="video/mp4" />
<source src="/videos/mycelium2.mp4" type="video/mp4" />
</video>
<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>
)
}

View 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>
)
}

View 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&apos;s advancements in Agentic infrastructure support private, secure, and
autonomous Agents that connect, learn, and grow with you.
</H5>
</div>
</div>
</section>
)
}

View File

@@ -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 />
</>

File diff suppressed because one or more lines are too long

View File

@@ -3,25 +3,43 @@
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)
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
if (href.startsWith('/#')) {
e.preventDefault();
const targetId = href.substring(2);
const targetElement = document.getElementById(targetId);
if (targetElement) {
targetElement.scrollIntoView({
behavior: 'smooth',
});
}
}
};
return [
['About', '/#home-about'],
['Marketplace', '/#companies'],
['Technology', '/#stack-section'],
['How it works', '/#steps'],
['Use Cases', '/#clickable-gallery'],
['Coming Soon', '/#use-cases'],
['Get Started', '/#call-to-action'],
['FAQs', '/#faqs'],
['Technologies', '/#technologies'],
['Network', '/#network'],
['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) {
window.clearTimeout(timeoutRef.current)
@@ -37,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 } }}

View File

@@ -1,63 +1,34 @@
"use client";
import { StackedCubes } from "@/components/ui/StackedCubes";
import { Button } from "@/components/Button";
import { motion, useInView } from 'framer-motion';
import { H2, P } from '@/components/Texts';
import { useRef } from "react";
import { 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 h-screen bg-transparent lg:px-0 pt-24 pb-12 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 }}
>
<P className="mx-auto mt-8 max-w-3xl" color="custom">
<FadeIn>
<P className="mx-auto mt-8 max-w-3xl" color="light">
Built with Mycelium technology, our AI infrastructure ensures unbreakable networks, complete data sovereignty, ultra-secure agent-human communication, and unhackable data storage systems.
</P>
</motion.div>
</FadeIn>
</div>
{/* Right Column - Stacked Cubes (2/3 width) */}
<div className="lg:col-span-2 flex items-center justify-center lg:justify-start order-2 lg:order-2">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}
transition={{ duration: 0.8, delay: 0.3 }}
>
<div className="lg:col-span-2 flex items-center justify-center lg:justify-start order-2 lg:order-2 mt-8 lg:mt-0">
<FadeIn>
<StackedCubes />
</motion.div>
</FadeIn>
</div>
</div>
</div>

View File

@@ -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>
);
}

View File

@@ -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,18 +30,19 @@ export function Steps() {
const isInView = useInView(ref, { once: true });
return (
<section id="benefits" ref={ref} className="bg-white pt-0 pb-24 dark:bg-gray-900">
<div className="mx-auto max-w-7xl px-6 lg:px-0">
<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">
<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="custom">
</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>
@@ -49,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
@@ -57,11 +58,11 @@ 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 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-[#2F3178]" />
<CT as="span" className="font-semibold">{feature.name}</CT>
<CP className="mt-2 text-sm" color="custom">{feature.description}</CP>
<feature.icon className="h-8 w-8 mb-4 text-white" />
<CT as="span" className="font-semibold" color="light">{feature.name}</CT>
<CP className="mt-2 text-sm" color="light">{feature.description}</CP>
</motion.li>
))}
</motion.ul>

View File

@@ -4,10 +4,14 @@ import React from 'react'
import { cn } from '@/lib/utils'
const colorVariants = {
primary: 'text-[#2F3178]',
primary: 'text-gray-900',
secondary: 'text-gray-600',
custom: 'text-[#1c1c49]',
light: 'text-white',
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-4xl font-medium text-pretty lg:text-5xl')
export const P = createTextComponent('p', 'text-xl font-normal text-pretty lg:text-2xl')
export const H3 = createTextComponent('h3', 'text-3xl lg:text-4xl font-medium')
export const H4 = createTextComponent('h4', 'text-2xl lg:text-3xl font-semibold leading-tight')
export const CT = createTextComponent('span', 'text-base 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')
// 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-[1.525] font-light')

View File

@@ -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 {
@@ -22,129 +22,75 @@ interface Review {
body: string
}
const reviews: Array<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 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.' },
]
function getReviewIcon(title: string) {
if (title.startsWith('FungiStor')) return ArchiveBoxIcon;
if (title.startsWith('HeroDB')) return CpuChipIcon;
if (title.startsWith('MOS Sandboxes')) return CodeBracketIcon;
if (title.startsWith('Mycelium Mesh')) return ShareIcon;
if (title.startsWith('Deterministic Deployment')) return CheckBadgeIcon;
if (title.startsWith('Agent Coordination')) return UserGroupIcon;
if (title.startsWith('Universal Interface Layer')) return GlobeAltIcon;
if (title.startsWith('Semantic Index & Search')) return MagnifyingGlassIcon;
return GlobeAltIcon; // default
if (title.startsWith('FungiStor')) return ArchiveBoxIcon
if (title.startsWith('HeroDB')) return CpuChipIcon
if (title.startsWith('MOS Sandboxes')) return CodeBracketIcon
if (title.startsWith('Mycelium Mesh')) return ShareIcon
if (title.startsWith('Deterministic Deployment')) return CheckBadgeIcon
if (title.startsWith('Agent Coordination')) return UserGroupIcon
if (title.startsWith('Universal Interface Layer')) return GlobeAltIcon
if (title.startsWith('Semantic Index & Search')) return MagnifyingGlassIcon
return GlobeAltIcon
}
function Review({
title,
body,
className,
...props
}: Omit<React.ComponentPropsWithoutRef<'figure'>, keyof Review> & Review) {
let animationDelay = useMemo(() => {
let possibleAnimationDelays = ['0s', '0.1s', '0.2s', '0.3s', '0.4s', '0.5s']
return possibleAnimationDelays[
Math.floor(Math.random() * possibleAnimationDelays.length)
]
function Review({ title, body, className, ...props }: Omit<React.ComponentPropsWithoutRef<'figure'>, keyof Review> & Review) {
const animationDelay = useMemo(() => {
const delays = ['0s', '0.1s', '0.2s', '0.3s', '0.4s', '0.5s']
return delays[Math.floor(Math.random() * delays.length)]
}, [])
return (
<figure
className={clsx(
'animate-fade-in rounded-3xl bg-white p-6 opacity-0 shadow-md shadow-gray-900/5',
className,
'animate-fade-in rounded-3xl bg-gray-900/50 p-6 opacity-0 shadow-md shadow-gray-900/5',
className
)}
style={{ animationDelay }}
{...props}
>
<blockquote className="text-gray-900">
{React.createElement(getReviewIcon(title), { className: "h-6 w-6 text-[#2F3178] mb-2" })}
<CT color="primary" className="mt-4 text-lg/6 font-semibold">
{title}
</CT>
<CP color="custom" className="mt-3 text-sm">{body}</CP>
<blockquote className="text-white">
{React.createElement(getReviewIcon(title), { className: 'h-6 w-6 text-white mb-2' })}
<CT color="light" className="mt-4 text-lg font-semibold">{title}</CT>
<CP color="light" className="mt-3 text-sm">{body}</CP>
</blockquote>
</figure>
)
}
function splitArray<T>(array: Array<T>, numParts: number) {
let result: Array<Array<T>> = []
function splitArray<T>(array: T[], numParts: number) {
const result: T[][] = []
for (let i = 0; i < array.length; i++) {
let index = i % numParts
if (!result[index]) {
result[index] = []
}
const index = i % numParts
if (!result[index]) result[index] = []
result[index].push(array[i])
}
return result
}
function ReviewColumn({
reviews,
className,
reviewClassName,
msPerPixel = 0,
}: {
reviews: Array<Review>
className?: string
reviewClassName?: (reviewIndex: number) => string
msPerPixel?: number
}) {
let columnRef = useRef<React.ElementRef<'div'>>(null)
let [columnHeight, setColumnHeight] = useState(0)
let duration = `${columnHeight * msPerPixel}ms`
function ReviewColumn({ reviews, className, msPerPixel = 0 }: { reviews: Review[], className?: string, msPerPixel?: number }) {
const columnRef = useRef<HTMLDivElement>(null)
const [columnHeight, setColumnHeight] = useState(0)
const duration = `${columnHeight * msPerPixel}ms`
useEffect(() => {
if (!columnRef.current) {
return
}
let resizeObserver = new window.ResizeObserver(() => {
if (!columnRef.current) return
const resizeObserver = new ResizeObserver(() =>
setColumnHeight(columnRef.current?.offsetHeight ?? 0)
})
)
resizeObserver.observe(columnRef.current)
return () => {
resizeObserver.disconnect()
}
return () => resizeObserver.disconnect()
}, [])
return (
@@ -153,110 +99,73 @@ function ReviewColumn({
className={clsx('animate-marquee space-y-8 py-4', className)}
style={{ '--marquee-duration': duration } as React.CSSProperties}
>
{reviews.concat(reviews).map((review, reviewIndex) => (
<Review
key={reviewIndex}
aria-hidden={reviewIndex >= reviews.length}
className={reviewClassName?.(reviewIndex % reviews.length)}
{...review}
/>
{reviews.concat(reviews).map((review, i) => (
<Review key={i} aria-hidden={i >= reviews.length} {...review} />
))}
</div>
)
}
function ReviewGrid() {
let containerRef = useRef<React.ElementRef<'div'>>(null)
let isInView = useInView(containerRef, { once: true, amount: 0.4 })
let columns = splitArray(reviews, 3)
let column1 = columns[0]
let column2 = columns[1]
let column3 = splitArray(columns[2], 2)
const containerRef = useRef<HTMLDivElement>(null)
const isInView = useInView(containerRef, { once: true, amount: 0.4 })
const columns = splitArray(reviews, 2)
return (
<div
ref={containerRef}
className="relative -mx-4 mt-0 grid h-196 max-h-[150vh] grid-cols-1 items-start gap-8 overflow-hidden px-4 sm:mt-20 md:grid-cols-2 lg:grid-cols-3"
>
<div ref={containerRef} className="relative grid grid-cols-1 md:grid-cols-2 gap-8 overflow-hidden h-full">
{isInView && (
<>
<ReviewColumn
reviews={[...column1, ...column3.flat(), ...column2]}
reviewClassName={(reviewIndex) =>
clsx(
reviewIndex >= column1.length + column3[0].length &&
'md:hidden',
reviewIndex >= column1.length && 'lg:hidden',
)
}
msPerPixel={10}
/>
<ReviewColumn
reviews={[...column2, ...column3[1]]}
className="hidden md:block"
reviewClassName={(reviewIndex) =>
reviewIndex >= column2.length ? 'lg:hidden' : ''
}
msPerPixel={15}
/>
<ReviewColumn
reviews={column3.flat()}
className="hidden lg:block"
msPerPixel={10}
/>
<ReviewColumn reviews={columns[0]} msPerPixel={10} />
<ReviewColumn reviews={columns[1]} msPerPixel={15} />
</>
)}
<div className="pointer-events-none absolute inset-x-0 top-0 h-32 bg-linear-to-b from-white" />
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-32 bg-linear-to-t from-white" />
{/* Top Gradient */}
<div className="pointer-events-none absolute inset-x-0 top-0 h-32 bg-gradient-to-b from-black/90 to-transparent z-10" />
{/* Bottom Gradient */}
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-32 bg-gradient-to-t from-black/90 to-transparent z-10" />
</div>
)
}
export function UseCases() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
const ref = useRef(null)
const isInView = useInView(ref, { once: true })
return (
<section
id="usecases"
ref={ref}
aria-labelledby="usecases-title"
className="py-12"
className="bg-black h-screen relative flex items-start py-12 -top-20"
>
<Container className=''>
<Container className="h-full">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-10 h-full">
{/* Left Column */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
initial={{ opacity: 0, y: 10 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 10 }}
transition={{ duration: 0.8, delay: 0.1 }}
className="mx-auto max-w-2xl lg:max-w-5xl"
className="flex flex-col items-start justify-start pt-10 lg:pr-12"
>
<H2
id="usecases-title"
color="primary"
className="text-center"
>
Coming Soon: The Future of Mycelium
</H2>
<P className="mt-6 text-center" color="custom">
Mycelium Cloud is evolving to bring even more powerful decentralized features, designed to enhance your experience and expand possibilities. Be the first to explore what's coming next by staying connected with our latest updates.
<SectionHeader id="usecases-title" color="light" className="text-left">
Augmented Intelligence Fabric
</SectionHeader>
<P className="mt-4 text-left" color="light">
The sovereign substrate for autonomous AI.
Stateless, geo-aware, end-to-end encryptedand verifiable from intent to execution.
</P>
</motion.div>
{/* Right Columns */}
<motion.div
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 1 } : { opacity: 0 }}
transition={{ duration: 1, delay: 0.2 }}
aria-hidden="true"
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
className="lg:col-span-2 h-full"
>
<div
style={{
clipPath:
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
}}
className="relative left-[calc(50%-30rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#93c5fd] to-[#9089fc] opacity-30 sm:left-[calc(50%-36rem)] sm:w-[72.1875rem]"
/>
</motion.div>
<ReviewGrid />
</motion.div>
</div>
</Container>
</section>
)

View File

@@ -1,7 +1,117 @@
'use client'
import { Globe } from "@/components/ui/globe"
import { motion } from "framer-motion"
import { H2, P, CT, CP, SectionHeader, Eyebrow } from "@/components/Texts"
import { CountUpNumber } from './CountUpNumber'
export function WorldMap() {
return (
<Globe />
<div className="relative min-h-screen w-full overflow-hidden top-0 flex py-12 flex-col">
{/* Background video */}
<video
autoPlay
loop
muted
playsInline
className="absolute inset-0 w-full h-full object-cover"
>
<source src="/videos/benefits.mp4" type="video/mp4" />
</video>
{/* Dark overlay */}
<div className="absolute inset-0 bg-black/10" />
{/* Content */}
<div className="relative z-10 flex flex-col h-full px-8 md:px-16 py-12">
{/* Title + Subtitle */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="max-w-xl"
>
<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>
</motion.div>
{/* Bottom Layout: Globe + Cards */}
<div className="mt-8 flex flex-1 flex-col lg:flex-row items-center lg:items-stretch gap-x-10">
{/* Globe Left Column */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, delay: 0.2 }}
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-0 lg:-left-24" />
</div>
</motion.div>
{/* Cards Right Column */}
<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 }}
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" />
<CP color="light" className="mt-2 text-sm">
Total Central Processing Unit Cores available on the grid.
</CP>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.5 }}
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" />
<CP color="light" className="mt-2 text-sm">
Total number of nodes on the grid.
</CP>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.6 }}
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 GB amount of storage (SSD, HDD, & RAM) on the grid.
</CP>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.7 }}
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" />
<CP color="light" className="mt-2 text-sm">
Total number of countries with active nodes.
</CP>
</motion.div>
</div>
</div>
</div>
{/* Radial fade overlay */}
<div className="pointer-events-none absolute inset-0 h-full bg-[radial-gradient(circle_at_50%_200%,rgba(0,0,0,0.2),rgba(255,255,255,0))]" />
</div>
)
}

View File

@@ -1,59 +0,0 @@
import Link from 'next/link'
import clsx from 'clsx'
const baseStyles = {
solid:
'inline-flex justify-center rounded-lg py-2 px-3 text-sm font-semibold transition-colors',
outline:
'inline-flex justify-center rounded-lg border py-[calc(theme(spacing.2)-1px)] px-[calc(theme(spacing.3)-1px)] text-sm transition-colors',
}
const variantStyles = {
solid: {
primary: 'bg-[#005eff] text-white hover:bg-[#005eff]/90 active:bg-[#005eff]/80',
white: 'bg-white text-black hover:bg-white/90 active:bg-white/90 active:text-gray-400',
gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80',
},
outline: {
primary: 'border-[#005eff] text-[#005eff] hover:border-[#005eff]/80 hover:text-[#005eff]/80 active:bg-gray-100 active:text-[#005eff]/70',
gray: 'border-gray-300 text-gray-700 hover:border-gray-400 active:bg-gray-100 active:text-gray-700/80',
},
}
type ButtonProps = (
| { variant?: 'solid'; color?: keyof typeof variantStyles.solid }
| { variant: 'outline'; color?: keyof typeof variantStyles.outline }
) & (
| Omit<React.ComponentPropsWithoutRef<typeof Link>, 'color'>
| (Omit<React.ComponentPropsWithoutRef<'button'>, 'color'> & {
href?: undefined
})
)
export function Button({ className, ...props }: ButtonProps) {
props.variant ??= 'solid'
if (props.variant === 'solid') {
props.color ??= 'primary'
} else {
props.color ??= 'gray'
}
let variantClass: string | undefined;
if (props.variant === 'outline') {
variantClass = variantStyles.outline[props.color as keyof typeof variantStyles.outline];
} else if (props.variant === 'solid') {
variantClass = variantStyles.solid[props.color as keyof typeof variantStyles.solid];
}
className = clsx(
baseStyles[props.variant],
variantClass,
className,
)
return typeof props.href === 'undefined' ? (
<button className={className} {...props} />
) : (
<Link className={className} {...props} />
)
}

View File

@@ -1,9 +1,7 @@
"use client";
import React from "react";
import Image from "next/image";
import { motion } from "framer-motion";
import { CT, CP } from '@/components/Texts';
interface CubeProps {
title: string;
@@ -13,16 +11,46 @@ interface CubeProps {
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 />
<stop offset="1" stopColor="#3F3B3E" />
</linearGradient>
</defs>
</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 pointer-events-none"
className="relative cursor-pointer"
onMouseEnter={onHover}
onMouseLeave={onLeave}
onClick={onClick}
style={{
zIndex: 10 - index,
}}
@@ -34,25 +62,26 @@ export function Cube({ title, descriptionTitle, description, isActive, index, on
ease: "easeOut",
}}
>
{/* Image Cube */}
<Image
src="/images/cube.png"
alt="Cube"
width={507}
height={234}
className="w-60 sm:w-80 lg:w-96 h-auto drop-shadow-lg pointer-events-auto"
{/* SVG Cube */}
<CubeSvg
index={index}
className="w-48 sm:w-64 lg:w-80 h-auto drop-shadow-lg opacity-50"
style={{
filter: isActive
? 'brightness(1.1) drop-shadow(0 25px 25px rgba(144, 137, 252, 0.4))'
: 'brightness(0.9) drop-shadow(0 10px 15px rgba(144, 137, 252, 0.2))',
filter: isActive ? 'brightness(1.2) drop-shadow(0 0 20px rgba(156, 163, 175, 0.5))' : 'brightness(0.9)',
}}
/>
{/* Title overlay */}
<div className="absolute inset-0 flex items-center justify-center">
<CT as="h3" className="px-4 drop-shadow-lg" style={{ transform: 'rotate(0deg) skewX(0deg)', transformOrigin: 'center' }}>
<h3
className="text-white text-sm lg:text-base font-medium text-center px-4 drop-shadow-lg"
style={{
transform: 'rotate(0deg) skewX(0deg)',
transformOrigin: 'center'
}}
>
{title}
</CT>
</h3>
</div>
{/* Description with arrow line - Desktop */}
@@ -77,39 +106,25 @@ export function Cube({ title, descriptionTitle, description, isActive, index, on
y1="1"
x2="120"
y2="1"
stroke="currentColor"
stroke="white"
strokeWidth="1"
opacity="0.6"
/>
</svg>
{/* Description text */}
<div className="ml-32 w-80 text-[#2F3178]">
<h4 className="text-base font-semibold mb-2">
<div className="ml-32 w-80">
<h4 className="text-white text-base font-semibold mb-2">
{descriptionTitle}
</h4>
<CP color="custom">{description}</CP>
<p className="text-white text-sm leading-relaxed font-light">
{description}
</p>
</div>
</motion.div>
)}
{/* Description for Mobile - Below cube */}
{isActive && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
transition={{ duration: 0.3 }}
className="lg:hidden absolute top-full left-1/2 -translate-x-1/2 mt-8 z-50"
>
<div className="w-64 sm:w-80 px-4 text-[#2F3178]">
<h4 className="text-base font-semibold mb-2 text-center">
{descriptionTitle}
</h4>
<CP className="text-center" color="custom">{description}</CP>
</div>
</motion.div>
)}
</motion.div>
</div>
);

View 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>
);
}

View File

@@ -2,45 +2,53 @@
import { useState } from "react";
import { motion } from "framer-motion";
import { Cube } from "@/components/ui/Cube";
import { Cube } from "@/components/ui/Cube"
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 doesnt 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>
);
}

View 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 doesnt 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>
);
}

View File

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

View File

@@ -0,0 +1,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;

View File

@@ -16,23 +16,57 @@ const GLOBE_CONFIG: COBEOptions = {
phi: 0,
theta: 0.3,
dark: 0,
diffuse: 0.4,
diffuse: 0.25, // softer shading for premium look
mapSamples: 16000,
mapBrightness: 1.2,
baseColor: [1, 1, 1],
markerColor: [251 / 255, 100 / 255, 21 / 255],
glowColor: [1, 1, 1],
mapBrightness: 1.1,
baseColor: [0.8, 0.8, 0.8], // sleek dark gray globe
markerColor: [0.02, 0.71, 0.83], // cyan-500
glowColor: [0.8, 0.8, 0.85], // grey
markers: [
{ location: [14.5995, 120.9842], size: 0.03 },
{ location: [19.076, 72.8777], size: 0.1 },
{ location: [23.8103, 90.4125], size: 0.05 },
{ location: [30.0444, 31.2357], size: 0.07 },
{ location: [39.9042, 116.4074], size: 0.08 },
{ location: [-23.5505, -46.6333], size: 0.1 },
{ location: [19.4326, -99.1332], size: 0.1 },
{ location: [40.7128, -74.006], size: 0.1 },
{ location: [34.6937, 135.5022], size: 0.05 },
{ location: [41.0082, 28.9784], size: 0.06 },
// --- Core Global Markers ---
{ location: [14.5995, 120.9842], size: 0.03 }, // Manila
{ location: [19.076, 72.8777], size: 0.1 }, // Mumbai
{ location: [23.8103, 90.4125], size: 0.05 }, // Dhaka
{ location: [30.0444, 31.2357], size: 0.07 }, // Cairo
{ location: [39.9042, 116.4074], size: 0.08 }, // Beijing
{ location: [-23.5505, -46.6333], size: 0.1 }, // São Paulo
{ location: [19.4326, -99.1332], size: 0.1 }, // Mexico City
{ location: [40.7128, -74.006], size: 0.1 }, // New York
{ location: [34.6937, 135.5022], size: 0.05 }, // Osaka
{ location: [41.0082, 28.9784], size: 0.06 }, // Istanbul
{ location: [48.8566, 2.3522], size: 0.08 }, // Paris
{ location: [51.5072, -0.1276], size: 0.08 }, // London
{ location: [52.52, 13.405], size: 0.07 }, // Berlin
{ location: [35.6895, 139.6917], size: 0.06 }, // Tokyo
{ location: [-33.8688, 151.2093], size: 0.06 }, // Sydney
{ location: [-1.2921, 36.8219], size: 0.05 }, // Nairobi
{ location: [-34.6037, -58.3816], size: 0.07 }, // Buenos Aires
{ location: [37.7749, -122.4194], size: 0.08 }, // San Francisco
{ location: [1.3521, 103.8198], size: 0.06 }, // Singapore
{ location: [28.6139, 77.2090], size: 0.08 }, // New Delhi
{ location: [13.7563, 100.5018], size: 0.06 }, // Bangkok
{ location: [59.9343, 30.3351], size: 0.05 }, // St. Petersburg
{ location: [33.6844, 73.0479], size: 0.05 }, // Islamabad
{ location: [25.276987, 55.296249], size: 0.07 }, // Dubai
{ location: [60.1699, 24.9384], size: 0.05 }, // Helsinki
{ location: [43.6532, -79.3832], size: 0.07 }, // Toronto
{ location: [6.5244, 3.3792], size: 0.08 }, // Lagos
{ location: [50.1109, 8.6821], size: 0.06 }, // Frankfurt
// --- 12 New US + European Cities ---
{ location: [34.0522, -118.2437], size: 0.08 }, // Los Angeles
{ location: [41.8781, -87.6298], size: 0.07 }, // Chicago
{ location: [29.7604, -95.3698], size: 0.07 }, // Houston
{ location: [25.7617, -80.1918], size: 0.07 }, // Miami
{ location: [45.5017, -73.5673], size: 0.06 }, // Montreal
{ location: [47.6062, -122.3321], size: 0.06 }, // Seattle
{ location: [40.4406, -79.9959], size: 0.05 }, // Pittsburgh
{ location: [41.3851, 2.1734], size: 0.06 }, // Barcelona
{ location: [45.4642, 9.19], size: 0.06 }, // Milan
{ location: [52.3676, 4.9041], size: 0.06 }, // Amsterdam
{ location: [38.7169, -9.139], size: 0.05 }, // Lisbon
{ location: [59.3293, 18.0686], size: 0.05 }, // Stockholmx
],
};
@@ -47,12 +81,11 @@ export function Globe({
let width = 0;
const canvasRef = useRef<HTMLCanvasElement>(null);
const pointerInteracting = useRef<number | null>(null);
const pointerInteractionMovement = useRef(0);
const r = useMotionValue(0);
const rs = useSpring(r, {
mass: 1,
damping: 30,
damping: 35, // slightly smoother motion
stiffness: 100,
});
@@ -66,16 +99,13 @@ export function Globe({
const updateMovement = (clientX: number) => {
if (pointerInteracting.current !== null) {
const delta = clientX - pointerInteracting.current;
pointerInteractionMovement.current = delta;
r.set(r.get() + delta / MOVEMENT_DAMPING);
}
};
useEffect(() => {
const onResize = () => {
if (canvasRef.current) {
width = canvasRef.current.offsetWidth;
}
if (canvasRef.current) width = canvasRef.current.offsetWidth;
};
window.addEventListener("resize", onResize);
@@ -86,7 +116,7 @@ export function Globe({
width: width * 2,
height: width * 2,
onRender: (state) => {
if (!pointerInteracting.current) phi += 0.005;
if (!pointerInteracting.current) phi += 0.004; // slightly slower rotation for elegance
state.phi = phi + rs.get();
state.width = width * 2;
state.height = width * 2;
@@ -103,7 +133,8 @@ export function Globe({
return (
<div
className={cn(
"absolute inset-0 mx-auto aspect-[1/1] w-full max-w-[600px]",
// Radial gradient background that fades to pure black at edges
"absolute inset-0 mx-auto aspect-[1/1] w-full max-w-[600px] rounded-full",
className,
)}
>

View File

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

View File

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

View File

@@ -1,118 +1 @@
<svg width="80" height="80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.2 0H0v3.2h3.2V0ZM3.2 3.2H0v3.2h3.2V3.2ZM3.2 6.4H0v3.2h3.2V6.4Z" fill="#171717" />
<path
d="M3.2 9.6H0v3.2h3.2V9.6ZM3.2 12.8H0V16h3.2v-3.2ZM3.2 16H0v3.2h3.2V16ZM3.2 19.2H0v3.2h3.2v-3.2ZM3.2 25.6H0v3.2h3.2v-3.2ZM3.2 35.2H0v3.2h3.2v-3.2ZM3.2 57.6H0v3.2h3.2v-3.2Z"
fill="#171717" />
<path d="M3.2 60.8H0V64h3.2v-3.2ZM3.2 64H0v3.2h3.2V64ZM3.2 67.2H0v3.2h3.2v-3.2ZM3.2 70.4H0v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M3.2 73.6H0v3.2h3.2v-3.2ZM3.2 76.8H0V80h3.2v-3.2ZM6.4 0H3.2v3.2h3.2V0ZM6.4 19.2H3.2v3.2h3.2v-3.2ZM6.4 25.6H3.2v3.2h3.2v-3.2ZM6.4 28.8H3.2V32h3.2v-3.2ZM6.4 32H3.2v3.2h3.2V32ZM6.4 35.2H3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M6.4 38.4H3.2v3.2h3.2v-3.2ZM6.4 48H3.2v3.2h3.2V48ZM6.4 51.2H3.2v3.2h3.2v-3.2ZM6.4 57.6H3.2v3.2h3.2v-3.2ZM6.4 76.8H3.2V80h3.2v-3.2ZM9.6 0H6.4v3.2h3.2V0ZM9.6 6.4H6.4v3.2h3.2V6.4Z"
fill="#171717" />
<path
d="M9.6 9.6H6.4v3.2h3.2V9.6ZM9.6 12.8H6.4V16h3.2v-3.2ZM9.6 19.2H6.4v3.2h3.2v-3.2ZM9.6 28.8H6.4V32h3.2v-3.2ZM9.6 32H6.4v3.2h3.2V32ZM9.6 41.6H6.4v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M9.6 44.8H6.4V48h3.2v-3.2ZM9.6 57.6H6.4v3.2h3.2v-3.2ZM9.6 64H6.4v3.2h3.2V64ZM9.6 67.2H6.4v3.2h3.2v-3.2ZM9.6 70.4H6.4v3.2h3.2v-3.2ZM9.6 76.8H6.4V80h3.2v-3.2ZM12.8 0H9.6v3.2h3.2V0ZM12.8 6.4H9.6v3.2h3.2V6.4Z"
fill="#171717" />
<path
d="M12.8 9.6H9.6v3.2h3.2V9.6ZM12.8 12.8H9.6V16h3.2v-3.2ZM12.8 19.2H9.6v3.2h3.2v-3.2ZM12.8 25.6H9.6v3.2h3.2v-3.2ZM12.8 38.4H9.6v3.2h3.2v-3.2ZM12.8 48H9.6v3.2h3.2V48ZM12.8 51.2H9.6v3.2h3.2v-3.2ZM12.8 57.6H9.6v3.2h3.2v-3.2ZM12.8 64H9.6v3.2h3.2V64ZM12.8 67.2H9.6v3.2h3.2v-3.2ZM12.8 70.4H9.6v3.2h3.2v-3.2ZM12.8 76.8H9.6V80h3.2v-3.2ZM16 0h-3.2v3.2H16V0ZM16 6.4h-3.2v3.2H16V6.4Z"
fill="#171717" />
<path
d="M16 9.6h-3.2v3.2H16V9.6ZM16 12.8h-3.2V16H16v-3.2ZM16 19.2h-3.2v3.2H16v-3.2ZM16 25.6h-3.2v3.2H16v-3.2ZM16 41.6h-3.2v3.2H16v-3.2Z"
fill="#171717" />
<path
d="M16 44.8h-3.2V48H16v-3.2ZM16 57.6h-3.2v3.2H16v-3.2ZM16 64h-3.2v3.2H16V64ZM16 67.2h-3.2v3.2H16v-3.2ZM16 70.4h-3.2v3.2H16v-3.2ZM16 76.8h-3.2V80H16v-3.2ZM19.2 0H16v3.2h3.2V0ZM19.2 19.2H16v3.2h3.2v-3.2ZM19.2 28.8H16V32h3.2v-3.2ZM19.2 44.8H16V48h3.2v-3.2ZM19.2 57.6H16v3.2h3.2v-3.2ZM19.2 76.8H16V80h3.2v-3.2ZM22.4 0h-3.2v3.2h3.2V0ZM22.4 3.2h-3.2v3.2h3.2V3.2ZM22.4 6.4h-3.2v3.2h3.2V6.4Z"
fill="#171717" />
<path
d="M22.4 9.6h-3.2v3.2h3.2V9.6ZM22.4 12.8h-3.2V16h3.2v-3.2ZM22.4 16h-3.2v3.2h3.2V16ZM22.4 19.2h-3.2v3.2h3.2v-3.2ZM22.4 25.6h-3.2v3.2h3.2v-3.2ZM22.4 32h-3.2v3.2h3.2V32ZM22.4 38.4h-3.2v3.2h3.2v-3.2ZM22.4 44.8h-3.2V48h3.2v-3.2ZM22.4 51.2h-3.2v3.2h3.2v-3.2ZM22.4 57.6h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M22.4 60.8h-3.2V64h3.2v-3.2ZM22.4 64h-3.2v3.2h3.2V64ZM22.4 67.2h-3.2v3.2h3.2v-3.2ZM22.4 70.4h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M22.4 73.6h-3.2v3.2h3.2v-3.2ZM22.4 76.8h-3.2V80h3.2v-3.2ZM25.6 28.8h-3.2V32h3.2v-3.2ZM25.6 32h-3.2v3.2h3.2V32ZM25.6 35.2h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M25.6 38.4h-3.2v3.2h3.2v-3.2ZM25.6 44.8h-3.2V48h3.2v-3.2ZM25.6 48h-3.2v3.2h3.2V48ZM25.6 51.2h-3.2v3.2h3.2v-3.2ZM28.8 0h-3.2v3.2h3.2V0ZM28.8 19.2h-3.2v3.2h3.2v-3.2ZM28.8 22.4h-3.2v3.2h3.2v-3.2ZM28.8 28.8h-3.2V32h3.2v-3.2ZM28.8 32h-3.2v3.2h3.2V32ZM28.8 35.2h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path d="M28.8 38.4h-3.2v3.2h3.2v-3.2ZM28.8 44.8h-3.2V48h3.2v-3.2ZM28.8 51.2h-3.2v3.2h3.2v-3.2Z" fill="#171717" />
<path
d="M28.8 54.4h-3.2v3.2h3.2v-3.2ZM28.8 64h-3.2v3.2h3.2V64ZM28.8 67.2h-3.2v3.2h3.2v-3.2ZM28.8 73.6h-3.2v3.2h3.2v-3.2ZM28.8 76.8h-3.2V80h3.2v-3.2ZM32 0h-3.2v3.2H32V0ZM32 6.4h-3.2v3.2H32V6.4ZM32 16h-3.2v3.2H32V16ZM32 22.4h-3.2v3.2H32v-3.2ZM32 25.6h-3.2v3.2H32v-3.2ZM32 32h-3.2v3.2H32V32ZM32 38.4h-3.2v3.2H32v-3.2ZM32 54.4h-3.2v3.2H32v-3.2ZM32 57.6h-3.2v3.2H32v-3.2Z"
fill="#171717" />
<path
d="M32 60.8h-3.2V64H32v-3.2ZM32 76.8h-3.2V80H32v-3.2ZM35.2 0H32v3.2h3.2V0ZM35.2 9.6H32v3.2h3.2V9.6ZM35.2 12.8H32V16h3.2v-3.2ZM35.2 19.2H32v3.2h3.2v-3.2ZM35.2 28.8H32V32h3.2v-3.2ZM35.2 38.4H32v3.2h3.2v-3.2ZM35.2 41.6H32v3.2h3.2v-3.2ZM35.2 48H32v3.2h3.2V48ZM35.2 57.6H32v3.2h3.2v-3.2ZM35.2 64H32v3.2h3.2V64ZM35.2 76.8H32V80h3.2v-3.2Z"
fill="#171717" />
<path d="M38.4 0h-3.2v3.2h3.2V0ZM38.4 3.2h-3.2v3.2h3.2V3.2ZM38.4 6.4h-3.2v3.2h3.2V6.4Z" fill="#171717" />
<path
d="M38.4 9.6h-3.2v3.2h3.2V9.6ZM38.4 12.8h-3.2V16h3.2v-3.2ZM38.4 16h-3.2v3.2h3.2V16ZM38.4 51.2h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M38.4 54.4h-3.2v3.2h3.2v-3.2ZM38.4 60.8h-3.2V64h3.2v-3.2ZM38.4 64h-3.2v3.2h3.2V64ZM38.4 70.4h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M38.4 73.6h-3.2v3.2h3.2v-3.2ZM38.4 76.8h-3.2V80h3.2v-3.2ZM41.6 3.2h-3.2v3.2h3.2V3.2ZM41.6 12.8h-3.2V16h3.2v-3.2ZM41.6 19.2h-3.2v3.2h3.2v-3.2ZM41.6 22.4h-3.2v3.2h3.2v-3.2ZM41.6 25.6h-3.2v3.2h3.2v-3.2ZM41.6 32h-3.2v3.2h3.2V32ZM41.6 41.6h-3.2v3.2h3.2v-3.2ZM41.6 48h-3.2v3.2h3.2V48ZM41.6 51.2h-3.2v3.2h3.2v-3.2ZM41.6 57.6h-3.2v3.2h3.2v-3.2ZM41.6 67.2h-3.2v3.2h3.2v-3.2ZM41.6 73.6h-3.2v3.2h3.2v-3.2ZM41.6 76.8h-3.2V80h3.2v-3.2ZM44.8 0h-3.2v3.2h3.2V0ZM44.8 6.4h-3.2v3.2h3.2V6.4Z"
fill="#171717" />
<path d="M44.8 22.4h-3.2v3.2h3.2v-3.2ZM44.8 32h-3.2v3.2h3.2V32ZM44.8 41.6h-3.2v3.2h3.2v-3.2Z" fill="#171717" />
<path d="M44.8 44.8h-3.2V48h3.2v-3.2ZM44.8 51.2h-3.2v3.2h3.2v-3.2Z" fill="#171717" />
<path d="M44.8 54.4h-3.2v3.2h3.2v-3.2ZM44.8 57.6h-3.2v3.2h3.2v-3.2Z" fill="#171717" />
<path
d="M44.8 60.8h-3.2V64h3.2v-3.2ZM44.8 64h-3.2v3.2h3.2V64ZM44.8 67.2h-3.2v3.2h3.2v-3.2ZM44.8 70.4h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M44.8 73.6h-3.2v3.2h3.2v-3.2ZM44.8 76.8h-3.2V80h3.2v-3.2ZM48 0h-3.2v3.2H48V0ZM48 16h-3.2v3.2H48V16ZM48 19.2h-3.2v3.2H48v-3.2ZM48 25.6h-3.2v3.2H48v-3.2ZM48 44.8h-3.2V48H48v-3.2ZM48 54.4h-3.2v3.2H48v-3.2ZM48 57.6h-3.2v3.2H48v-3.2Z"
fill="#171717" />
<path
d="M48 60.8h-3.2V64H48v-3.2ZM48 67.2h-3.2v3.2H48v-3.2ZM51.2 6.4H48v3.2h3.2V6.4ZM51.2 12.8H48V16h3.2v-3.2ZM51.2 22.4H48v3.2h3.2v-3.2ZM51.2 25.6H48v3.2h3.2v-3.2ZM51.2 28.8H48V32h3.2v-3.2ZM51.2 32H48v3.2h3.2V32ZM51.2 35.2H48v3.2h3.2v-3.2ZM51.2 41.6H48v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M51.2 44.8H48V48h3.2v-3.2ZM51.2 54.4H48v3.2h3.2v-3.2ZM51.2 60.8H48V64h3.2v-3.2ZM51.2 64H48v3.2h3.2V64ZM54.4 6.4h-3.2v3.2h3.2V6.4ZM54.4 12.8h-3.2V16h3.2v-3.2ZM54.4 19.2h-3.2v3.2h3.2v-3.2ZM54.4 28.8h-3.2V32h3.2v-3.2ZM54.4 32h-3.2v3.2h3.2V32ZM54.4 38.4h-3.2v3.2h3.2v-3.2ZM54.4 48h-3.2v3.2h3.2V48Z"
fill="#171717" />
<path d="M54.4 51.2h-3.2v3.2h3.2v-3.2Z" fill="#171717" />
<path d="M54.4 54.4h-3.2v3.2h3.2v-3.2ZM54.4 57.6h-3.2v3.2h3.2v-3.2Z" fill="#171717" />
<path
d="M54.4 60.8h-3.2V64h3.2v-3.2ZM54.4 64h-3.2v3.2h3.2V64ZM54.4 67.2h-3.2v3.2h3.2v-3.2ZM54.4 70.4h-3.2v3.2h3.2v-3.2ZM54.4 76.8h-3.2V80h3.2v-3.2ZM57.6 32h-3.2v3.2h3.2V32ZM57.6 41.6h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M57.6 44.8h-3.2V48h3.2v-3.2ZM57.6 51.2h-3.2v3.2h3.2v-3.2ZM57.6 64h-3.2v3.2h3.2V64ZM57.6 70.4h-3.2v3.2h3.2v-3.2ZM57.6 76.8h-3.2V80h3.2v-3.2ZM60.8 0h-3.2v3.2h3.2V0ZM60.8 3.2h-3.2v3.2h3.2V3.2ZM60.8 6.4h-3.2v3.2h3.2V6.4Z"
fill="#171717" />
<path
d="M60.8 9.6h-3.2v3.2h3.2V9.6ZM60.8 12.8h-3.2V16h3.2v-3.2ZM60.8 16h-3.2v3.2h3.2V16ZM60.8 19.2h-3.2v3.2h3.2v-3.2ZM60.8 25.6h-3.2v3.2h3.2v-3.2ZM60.8 35.2h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path
d="M60.8 38.4h-3.2v3.2h3.2v-3.2ZM60.8 44.8h-3.2V48h3.2v-3.2ZM60.8 48h-3.2v3.2h3.2V48ZM60.8 51.2h-3.2v3.2h3.2v-3.2ZM60.8 57.6h-3.2v3.2h3.2v-3.2ZM60.8 64h-3.2v3.2h3.2V64ZM60.8 67.2h-3.2v3.2h3.2v-3.2ZM64 0h-3.2v3.2H64V0ZM64 19.2h-3.2v3.2H64v-3.2ZM64 28.8h-3.2V32H64v-3.2ZM64 38.4h-3.2v3.2H64v-3.2ZM64 48h-3.2v3.2H64V48ZM64 51.2h-3.2v3.2H64v-3.2ZM64 64h-3.2v3.2H64V64ZM64 73.6h-3.2v3.2H64v-3.2ZM67.2 0H64v3.2h3.2V0ZM67.2 6.4H64v3.2h3.2V6.4Z"
fill="#171717" />
<path
d="M67.2 9.6H64v3.2h3.2V9.6ZM67.2 12.8H64V16h3.2v-3.2ZM67.2 19.2H64v3.2h3.2v-3.2ZM67.2 28.8H64V32h3.2v-3.2ZM67.2 41.6H64v3.2h3.2v-3.2Z"
fill="#171717" />
<path d="M67.2 44.8H64V48h3.2v-3.2ZM67.2 51.2H64v3.2h3.2v-3.2Z" fill="#171717" />
<path d="M67.2 54.4H64v3.2h3.2v-3.2ZM67.2 57.6H64v3.2h3.2v-3.2Z" fill="#171717" />
<path d="M67.2 60.8H64V64h3.2v-3.2ZM67.2 64H64v3.2h3.2V64ZM67.2 70.4H64v3.2h3.2v-3.2Z" fill="#171717" />
<path d="M67.2 73.6H64v3.2h3.2v-3.2ZM70.4 0h-3.2v3.2h3.2V0ZM70.4 6.4h-3.2v3.2h3.2V6.4Z" fill="#171717" />
<path
d="M70.4 9.6h-3.2v3.2h3.2V9.6ZM70.4 12.8h-3.2V16h3.2v-3.2ZM70.4 19.2h-3.2v3.2h3.2v-3.2ZM70.4 28.8h-3.2V32h3.2v-3.2ZM70.4 32h-3.2v3.2h3.2V32ZM70.4 35.2h-3.2v3.2h3.2v-3.2ZM70.4 44.8h-3.2V48h3.2v-3.2ZM70.4 48h-3.2v3.2h3.2V48ZM70.4 70.4h-3.2v3.2h3.2v-3.2ZM70.4 76.8h-3.2V80h3.2v-3.2ZM73.6 0h-3.2v3.2h3.2V0Z"
fill="#171717" />
<path d="M73.6 6.4h-3.2v3.2h3.2V6.4Z" fill="#171717" />
<path
d="M73.6 9.6h-3.2v3.2h3.2V9.6ZM73.6 12.8h-3.2V16h3.2v-3.2ZM73.6 19.2h-3.2v3.2h3.2v-3.2ZM73.6 28.8h-3.2V32h3.2v-3.2ZM73.6 35.2h-3.2v3.2h3.2v-3.2ZM73.6 44.8h-3.2V48h3.2v-3.2ZM73.6 48h-3.2v3.2h3.2V48ZM73.6 51.2h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path d="M73.6 54.4h-3.2v3.2h3.2v-3.2ZM73.6 70.4h-3.2v3.2h3.2v-3.2Z" fill="#171717" />
<path
d="M73.6 73.6h-3.2v3.2h3.2v-3.2ZM76.8 0h-3.2v3.2h3.2V0ZM76.8 19.2h-3.2v3.2h3.2v-3.2ZM76.8 28.8h-3.2V32h3.2v-3.2ZM76.8 35.2h-3.2v3.2h3.2v-3.2ZM76.8 41.6h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path d="M76.8 44.8h-3.2V48h3.2v-3.2ZM76.8 51.2h-3.2v3.2h3.2v-3.2Z" fill="#171717" />
<path
d="M76.8 54.4h-3.2v3.2h3.2v-3.2ZM76.8 64h-3.2v3.2h3.2V64ZM76.8 67.2h-3.2v3.2h3.2v-3.2ZM76.8 70.4h-3.2v3.2h3.2v-3.2Z"
fill="#171717" />
<path d="M76.8 73.6h-3.2v3.2h3.2v-3.2ZM80 0h-3.2v3.2H80V0ZM80 3.2h-3.2v3.2H80V3.2ZM80 6.4h-3.2v3.2H80V6.4Z"
fill="#171717" />
<path
d="M80 9.6h-3.2v3.2H80V9.6ZM80 12.8h-3.2V16H80v-3.2ZM80 16h-3.2v3.2H80V16ZM80 19.2h-3.2v3.2H80v-3.2ZM80 25.6h-3.2v3.2H80v-3.2ZM80 32h-3.2v3.2H80V32ZM80 35.2h-3.2v3.2H80v-3.2Z"
fill="#171717" />
<path
d="M80 38.4h-3.2v3.2H80v-3.2ZM80 44.8h-3.2V48H80v-3.2ZM80 48h-3.2v3.2H80V48ZM80 57.6h-3.2v3.2H80v-3.2ZM80 64h-3.2v3.2H80V64ZM80 67.2h-3.2v3.2H80v-3.2ZM80 70.4h-3.2v3.2H80v-3.2Z"
fill="#171717" />
<path d="M80 73.6h-3.2v3.2H80v-3.2ZM80 76.8h-3.2V80H80v-3.2Z" fill="#171717" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="80" zoomAndPan="magnify" viewBox="0 0 60 60" height="80" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="38e6314a1e"><path d="M 6 12.121094 L 31 12.121094 L 31 34 L 6 34 Z M 6 12.121094 " clip-rule="nonzero"/></clipPath><clipPath id="e1a7034db0"><path d="M 6 12.121094 L 31 12.121094 L 31 34 L 6 34 Z M 6 12.121094 " clip-rule="nonzero"/></clipPath></defs><g clip-path="url(#38e6314a1e)"><path fill="#5999f6" d="M 29.101562 28.996094 L 18.433594 28.996094 L 10.6875 33.613281 C 10.378906 33.820312 10.019531 33.511719 10.070312 33.152344 L 11.199219 28.945312 L 7.761719 28.945312 C 6.839844 28.945312 6.019531 28.125 6.019531 27.203125 L 6.019531 13.863281 C 6.019531 12.941406 6.839844 12.121094 7.761719 12.121094 L 29.101562 12.121094 C 30.027344 12.121094 30.847656 12.941406 30.847656 13.863281 L 30.847656 27.304688 C 30.847656 28.277344 30.078125 28.996094 29.101562 28.996094 Z M 29.101562 28.996094 " fill-opacity="1" fill-rule="nonzero"/></g><path fill="#015eff" d="M 23.921875 41.617188 L 37.824219 41.617188 L 47.929688 47.617188 C 48.339844 47.875 48.800781 47.464844 48.75 47.054688 L 47.261719 41.617188 L 51.722656 41.617188 C 52.957031 41.617188 53.980469 40.539062 53.980469 39.359375 L 53.980469 21.96875 C 53.980469 20.738281 52.902344 19.710938 51.722656 19.710938 L 24.023438 19.710938 C 22.792969 19.710938 21.765625 20.789062 21.765625 21.96875 L 21.765625 39.460938 C 21.613281 40.640625 22.691406 41.617188 23.921875 41.617188 Z M 23.921875 41.617188 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 37.824219 28.945312 C 38.796875 28.945312 39.515625 29.765625 39.515625 30.636719 C 39.515625 31.511719 38.695312 32.332031 37.824219 32.332031 C 36.847656 32.332031 36.128906 31.511719 36.128906 30.636719 C 36.128906 29.765625 36.847656 28.945312 37.824219 28.945312 Z M 37.824219 28.945312 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 44.800781 28.945312 C 45.773438 28.945312 46.492188 29.765625 46.492188 30.636719 C 46.492188 31.511719 45.671875 32.332031 44.800781 32.332031 C 43.824219 32.332031 43.105469 31.511719 43.105469 30.636719 C 43.105469 29.765625 43.824219 28.945312 44.800781 28.945312 Z M 44.800781 28.945312 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 30.796875 28.945312 C 31.769531 28.945312 32.488281 29.765625 32.488281 30.636719 C 32.488281 31.511719 31.667969 32.332031 30.796875 32.332031 C 29.820312 32.332031 29.101562 31.511719 29.101562 30.636719 C 29.101562 29.765625 29.820312 28.945312 30.796875 28.945312 Z M 30.796875 28.945312 " fill-opacity="1" fill-rule="nonzero"/><g clip-path="url(#e1a7034db0)"><path fill="#5999f6" d="M 29.101562 28.996094 L 18.433594 28.996094 L 10.6875 33.613281 C 10.378906 33.820312 10.019531 33.511719 10.070312 33.152344 L 11.199219 28.945312 L 7.761719 28.945312 C 6.839844 28.945312 6.019531 28.125 6.019531 27.203125 L 6.019531 13.863281 C 6.019531 12.941406 6.839844 12.121094 7.761719 12.121094 L 29.101562 12.121094 C 30.027344 12.121094 30.847656 12.941406 30.847656 13.863281 L 30.847656 27.304688 C 30.847656 28.277344 30.078125 28.996094 29.101562 28.996094 Z M 29.101562 28.996094 " fill-opacity="1" fill-rule="nonzero"/></g><path fill="#015eff" d="M 23.921875 41.617188 L 37.824219 41.617188 L 47.929688 47.617188 C 48.339844 47.875 48.800781 47.464844 48.75 47.054688 L 47.261719 41.617188 L 51.722656 41.617188 C 52.957031 41.617188 53.980469 40.539062 53.980469 39.359375 L 53.980469 21.96875 C 53.980469 20.738281 52.902344 19.710938 51.722656 19.710938 L 24.023438 19.710938 C 22.792969 19.710938 21.765625 20.789062 21.765625 21.96875 L 21.765625 39.460938 C 21.613281 40.640625 22.691406 41.617188 23.921875 41.617188 Z M 23.921875 41.617188 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 37.824219 28.945312 C 38.796875 28.945312 39.515625 29.765625 39.515625 30.636719 C 39.515625 31.511719 38.695312 32.332031 37.824219 32.332031 C 36.847656 32.332031 36.128906 31.511719 36.128906 30.636719 C 36.128906 29.765625 36.847656 28.945312 37.824219 28.945312 Z M 37.824219 28.945312 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 44.800781 28.945312 C 45.773438 28.945312 46.492188 29.765625 46.492188 30.636719 C 46.492188 31.511719 45.671875 32.332031 44.800781 32.332031 C 43.824219 32.332031 43.105469 31.511719 43.105469 30.636719 C 43.105469 29.765625 43.824219 28.945312 44.800781 28.945312 Z M 44.800781 28.945312 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 30.796875 28.945312 C 31.769531 28.945312 32.488281 29.765625 32.488281 30.636719 C 32.488281 31.511719 31.667969 32.332031 30.796875 32.332031 C 29.820312 32.332031 29.101562 31.511719 29.101562 30.636719 C 29.101562 29.765625 29.820312 28.945312 30.796875 28.945312 Z M 30.796875 28.945312 " fill-opacity="1" fill-rule="nonzero"/></svg>

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

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

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

View File

@@ -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;

View File

@@ -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" }
}
}
}
}