Compare commits
19 Commits
74d5bae622
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 76e0bdb7be | |||
| 85c041ab49 | |||
| 0231c4835c | |||
| 4efc563aa9 | |||
| d70c2b6874 | |||
| db4c2d8ea0 | |||
| 28bef26fbc | |||
| 73fe0e7c8e | |||
| c59c09eee8 | |||
| 593201ae10 | |||
| b46df781f8 | |||
| f44662829d | |||
| a663c32f53 | |||
| 2f3dea92a2 | |||
| 75b660d81e | |||
| 3f86c7e87f | |||
| 1c2c274848 | |||
| 1577eb6c6c | |||
| f4519ec8bf |
73
README.md
73
README.md
@@ -1,9 +1,11 @@
|
|||||||
# Mycelium Cloud Website
|
# Project Mycelium Website
|
||||||
|
|
||||||
- **Main Branch:** [https://project.mycelium.tf/](https://project.mycelium.tf/)
|
|
||||||
- **Dev Branch:** [https://www2.project.mycelium.tf/](https://www2.project.mycelium.tf/)
|
|
||||||
- **Repository:** [https://git.ourworld.tf/ourworld_web/www_project_mycelium/](https://git.ourworld.tf/ourworld_web/www_project_mycelium/)
|
- **Repository:** [https://git.ourworld.tf/ourworld_web/www_project_mycelium/](https://git.ourworld.tf/ourworld_web/www_project_mycelium/)
|
||||||
|
|
||||||
|
- **Main Branch (Production):** [https://project.mycelium.tf/](https://project.mycelium.tf/)
|
||||||
|
- **Dev Branch (Staging):** [https://www2.project.mycelium.tf/](https://www2.project.mycelium.tf/)
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -20,11 +22,19 @@ This is the official website for Mycelium Cloud, built using Next.js and Tailwin
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 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
|
## File Structure
|
||||||
|
|
||||||
- **Pages**: To edit the content of a specific page, navigate to `src/app/(main)/`.
|
- **Pages**: To edit the content of a specific page, navigate to `src/app/(main)/`.
|
||||||
- **Components**: Reusable components are located in `src/components/`.
|
- **Components**: Reusable components are located in `src/components/`.
|
||||||
- **Images and Videos**: Add or modify images and videos in the `public/` directory. Images are in `public/images/` and videos are in `public/videos/`.
|
- **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.
|
- **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.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -60,6 +70,57 @@ Follow these steps to get the project running locally:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Development Guide
|
||||||
|
|
||||||
|
This project follows a modular, component-based architecture. Pages are assembled by combining reusable components into a single layout.
|
||||||
|
|
||||||
|
### Homepage Structure
|
||||||
|
|
||||||
|
The homepage (`src/app/(main)/page.tsx`) is composed of the following components:
|
||||||
|
|
||||||
|
- `HomeHero`
|
||||||
|
- `WorldMap`
|
||||||
|
- `StackSectionPreview`
|
||||||
|
- `Steps`
|
||||||
|
- `Companies`
|
||||||
|
- `ClickableGallery`
|
||||||
|
- `BentoReviews`
|
||||||
|
- `CallToAction`
|
||||||
|
|
||||||
|
To edit a specific section of the homepage, navigate to `src/components/` and modify the corresponding component file.
|
||||||
|
|
||||||
|
### Base Layout
|
||||||
|
|
||||||
|
The main layout for the application is defined in `src/components/Layout.tsx`. This file includes the `Header` and `Footer` and wraps the primary content of each page.
|
||||||
|
|
||||||
|
### Creating a New Page
|
||||||
|
|
||||||
|
To create a new page, follow these steps:
|
||||||
|
|
||||||
|
1. **Create a Folder**: Inside the `src/app/(main)/` directory, create a new folder with the desired URL slug for your page (e.g., `new-page`).
|
||||||
|
|
||||||
|
2. **Create the Page File**: Inside the new folder, create a `page.tsx` file.
|
||||||
|
|
||||||
|
3. **Add Page Content**: Compose your page by importing and using the reusable components from `src/components/`. For example:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { HomeHero } from '@/components/HomeHero'
|
||||||
|
import { Faqs } from '@/components/Faqs'
|
||||||
|
|
||||||
|
export default function NewPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<HomeHero />
|
||||||
|
<Faqs />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The new page will be accessible at `http://localhost:3000/new-page`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
- **Never update the `main` branch directly.** All changes must be reviewed and merged by the team through a pull request.
|
- **Never update the `main` branch directly.** All changes must be reviewed and merged by the team through a pull request.
|
||||||
@@ -72,7 +133,9 @@ Follow these steps to get the project running locally:
|
|||||||
|
|
||||||
To report an issue, please use the following link and provide the requested information:
|
To report an issue, please use the following link and provide the requested information:
|
||||||
|
|
||||||
- **Issue Tracker**: [https://git.ourworld.tf/tfgrid_internal/circle_tfgrid_ops/issues](https://git.ourworld.tf/tfgrid_internal/circle_tfgrid_ops/issues)
|
- **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:
|
When reporting an issue, please include:
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"hooks": "@/hooks"
|
"hooks": "@/hooks"
|
||||||
},
|
},
|
||||||
"registries": {
|
"registries": {
|
||||||
"@magicui": "https://magicui.design/r/{name}.json"
|
"@magicui": "https://magicui.design/r/{name}.json",
|
||||||
|
"@aceternity": "https://ui.aceternity.com/registry/{name}.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/images/cloud.png
Normal file
BIN
public/images/cloud.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/images/mchip.webp
Normal file
BIN
public/images/mchip.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
public/videos/cloud.mp4
Normal file
BIN
public/videos/cloud.mp4
Normal file
Binary file not shown.
BIN
public/videos/cloud2.mp4
Normal file
BIN
public/videos/cloud2.mp4
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
export { Layout as default } from '@/components/Layout'
|
|
||||||
@@ -3,9 +3,11 @@ import { Faqs } from '@/components/Faqs'
|
|||||||
import { UseCases } from '@/components/UseCases'
|
import { UseCases } from '@/components/UseCases'
|
||||||
import { Steps } from '@/components/Steps'
|
import { Steps } from '@/components/Steps'
|
||||||
import { HomeHero } from '@/components/HomeHero'
|
import { HomeHero } from '@/components/HomeHero'
|
||||||
|
import { HomeHeroLight } from '@/components/HomeHeroLight'
|
||||||
|
import { HomeHeroLight2 } from '@/components/HomeHeroLight2'
|
||||||
import { HomeAbout } from '@/components/HomeAbout'
|
import { HomeAbout } from '@/components/HomeAbout'
|
||||||
import { ClickableGallery } from '@/components/ClickableGallery'
|
import { ClickableGalleryLight } from '@/components/ClickableGalleryLight'
|
||||||
import { StackSectionPreview } from '@/components/StackSection'
|
import { StackSectionLight } from '@/components/StackSectionLight'
|
||||||
import { Companies } from '@/components/Companies'
|
import { Companies } from '@/components/Companies'
|
||||||
import { CallToAction } from '@/components/CallToAction'
|
import { CallToAction } from '@/components/CallToAction'
|
||||||
import { ScrollDown } from '@/components/ui/ScrollDown'
|
import { ScrollDown } from '@/components/ui/ScrollDown'
|
||||||
@@ -19,27 +21,27 @@ export default function Home() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section id="home-hero">
|
<section id="home-hero">
|
||||||
<HomeHero />
|
<HomeHeroLight2 />
|
||||||
</section>
|
</section>
|
||||||
<section id="network">
|
<section id="network">
|
||||||
<WorldMap />
|
<WorldMap />
|
||||||
</section>
|
</section>
|
||||||
<section id="technologies">
|
<section id="technologies">
|
||||||
<StackSectionPreview />
|
<StackSectionLight />
|
||||||
</section>
|
</section>
|
||||||
<section id="deploy">
|
<section id="how-it-works">
|
||||||
<Steps />
|
<Steps />
|
||||||
</section>
|
</section>
|
||||||
<section id="llms">
|
<section id="llms">
|
||||||
<Companies />
|
<Companies />
|
||||||
</section>
|
</section>
|
||||||
<section id="clickable-gallery">
|
<section id="clickable-gallery">
|
||||||
<ClickableGallery />
|
<ClickableGalleryLight />
|
||||||
</section>
|
</section>
|
||||||
<section id="bento-reviews">
|
<section id="bento-reviews">
|
||||||
<BentoReviews />
|
<BentoReviews />
|
||||||
</section>
|
</section>
|
||||||
<section id="call-to-action">
|
<section id="get-started">
|
||||||
<CallToAction />
|
<CallToAction />
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { type Metadata } from 'next'
|
import { type Metadata } from 'next'
|
||||||
import Script from 'next/script'
|
import Script from 'next/script'
|
||||||
import { Mulish } from 'next/font/google'
|
import { Inter } from 'next/font/google'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|
||||||
import '@/styles/tailwind.css'
|
import '@/styles/tailwind.css'
|
||||||
|
|
||||||
const mulish = Mulish({
|
const inter = Inter({
|
||||||
subsets: ['latin'],
|
subsets: ['latin'],
|
||||||
display: 'swap',
|
display: 'swap',
|
||||||
variable: '--font-mulish',
|
variable: '--font-inter',
|
||||||
})
|
})
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
@@ -26,7 +26,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={clsx('antialiased', mulish.variable)}>
|
<html lang="en" className={clsx('antialiased', inter.variable)}>
|
||||||
<head>
|
<head>
|
||||||
{/* MailerLite Universal */}
|
{/* MailerLite Universal */}
|
||||||
<Script id="mailerlite-universal" strategy="afterInteractive">
|
<Script id="mailerlite-universal" strategy="afterInteractive">
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function Steps() {
|
|||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
||||||
transition={{ duration: 0.5, delay: 0.3 + index * 0.2 }}
|
transition={{ duration: 0.5, delay: 0.3 + index * 0.2 }}
|
||||||
className="rounded-2xl border border-gray-200 p-8 dark:border-gray-700"
|
className="rounded-2xl border border-gray-300 p-8 dark:border-gray-700"
|
||||||
>
|
>
|
||||||
<feature.icon className="h-8 w-8 mb-4 text-[#2F3178]" />
|
<feature.icon className="h-8 w-8 mb-4 text-[#2F3178]" />
|
||||||
<CT as="span" className="font-semibold">{feature.name}</CT>
|
<CT as="span" className="font-semibold">{feature.name}</CT>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { H2, P } from "@/components/Texts";
|
import { SectionHeader, P, Eyebrow } from "@/components/Texts";
|
||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import { BentoGrid, MotionBentoGridItem } from "@/components/ui/bento-grid";
|
import { BentoGrid, MotionBentoGridItem } from "@/components/ui/bento-grid";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
@@ -95,7 +95,8 @@ export function BentoReviews() {
|
|||||||
<div className="relative isolate py-24 bg-black text-center w-full lg:px-0 px-4">
|
<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 }}>
|
<FadeIn transition={{ duration: 0.8, delay: 0.1 }}>
|
||||||
<div className="mx-auto max-w-5xl ">
|
<div className="mx-auto max-w-5xl ">
|
||||||
<H2 className="text-center">Augmented Intelligence Fabric</H2>
|
<Eyebrow color="accent">Components</Eyebrow>
|
||||||
|
<SectionHeader className="text-center">Augmented Intelligence Fabric</SectionHeader>
|
||||||
</div>
|
</div>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
<FadeIn transition={{ duration: 0.8, delay: 0.2 }}>
|
<FadeIn transition={{ duration: 0.8, delay: 0.2 }}>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const baseStyles = {
|
|||||||
|
|
||||||
const variantStyles = {
|
const variantStyles = {
|
||||||
solid: {
|
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:
|
white:
|
||||||
'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70',
|
'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',
|
gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80',
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { H2, P } from '@/components/Texts'
|
import { SectionHeader, P } from '@/components/Texts'
|
||||||
|
|
||||||
export function CallTo() {
|
export function CallTo() {
|
||||||
return (
|
return (
|
||||||
<div className="relative isolate overflow-hidden max-w-5xl mx-auto py-24">
|
<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">
|
<div className="mx-auto max-w-4xl text-center">
|
||||||
<H2 color="primary">
|
<SectionHeader color="primary">
|
||||||
Are you Ready?
|
Are you Ready?
|
||||||
</H2>
|
</SectionHeader>
|
||||||
<P color="custom" className="mt-8 max-w-3xl">
|
<P className="mt-8 max-w-3xl">
|
||||||
Why hand out your intelligence to centralized giants when you can build your own?
|
Why hand out your intelligence to centralized giants when you can build your own?
|
||||||
</P>
|
</P>
|
||||||
<div className="mt-10 flex items-center justify-center gap-x-6">
|
<div className="mt-10 flex items-center justify-center gap-x-6">
|
||||||
@@ -23,12 +23,7 @@ export function CallTo() {
|
|||||||
Book a Meeting
|
Book a Meeting
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="javascript:;"
|
href="mailto:info@ourworld.tf"
|
||||||
onClick={() => {
|
|
||||||
if (typeof window !== 'undefined' && (window as any).ml_account) {
|
|
||||||
(window as any).ml_account('webforms', '6108375', 'l9m8g1', 'show')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="text-sm/6 font-semibold text-[#2F3178] hover:text-[#2F3178]/80"
|
className="text-sm/6 font-semibold text-[#2F3178] hover:text-[#2F3178]/80"
|
||||||
>
|
>
|
||||||
Join the Waitlist <span aria-hidden="true">→</span>
|
Join the Waitlist <span aria-hidden="true">→</span>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { CircleBackground } from '@/components/CircleBackground'
|
|||||||
import { Container } from '@/components/Container'
|
import { Container } from '@/components/Container'
|
||||||
import { Button } from '@/components/Button'
|
import { Button } from '@/components/Button'
|
||||||
import { FadeIn } from '@/components/FadeIn'
|
import { FadeIn } from '@/components/FadeIn'
|
||||||
|
import { Eyebrow } from '@/components/Texts'
|
||||||
|
|
||||||
export function CallToAction() {
|
export function CallToAction() {
|
||||||
return (
|
return (
|
||||||
@@ -27,6 +28,7 @@ export function CallToAction() {
|
|||||||
<Container className="relative z-20">
|
<Container className="relative z-20">
|
||||||
<FadeIn>
|
<FadeIn>
|
||||||
<div className="mx-auto max-w-md text-center">
|
<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">
|
<h2 className="text-3xl font-medium tracking-tight text-white sm:text-4xl">
|
||||||
Decentralized AI Agents that are Truly Yours
|
Decentralized AI Agents that are Truly Yours
|
||||||
</h2>
|
</h2>
|
||||||
@@ -47,11 +49,7 @@ export function CallToAction() {
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="white"
|
color="white"
|
||||||
onClick={() => {
|
href="mailto:info@ourworld.tf"
|
||||||
if (typeof window !== 'undefined' && (window as any).ml_account) {
|
|
||||||
(window as any).ml_account('webforms', '6108375', 'l9m8g1', 'show')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Join the Waitlist
|
Join the Waitlist
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Image from 'next/image'
|
|||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import { wrap } from 'popmotion'
|
import { wrap } from 'popmotion'
|
||||||
import { Button } from '@/components/Button';
|
import { Button } from '@/components/Button';
|
||||||
import { H2, P, CT } from '@/components/Texts';
|
import { SectionHeader, P, CT } from '@/components/Texts';
|
||||||
import { TypeAnimation } from 'react-type-animation'
|
import { TypeAnimation } from 'react-type-animation'
|
||||||
import { FadeIn } from './FadeIn';
|
import { FadeIn } from './FadeIn';
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ export function ClickableGallery() {
|
|||||||
<div className="relative isolate pt-8 pb-0 bg-transparent text-center w-full">
|
<div className="relative isolate pt-8 pb-0 bg-transparent text-center w-full">
|
||||||
<FadeIn transition={{ duration: 0.8, delay: 0.1 }}>
|
<FadeIn transition={{ duration: 0.8, delay: 0.1 }}>
|
||||||
<div className="mx-auto max-w-5xl lg:mt-12">
|
<div className="mx-auto max-w-5xl lg:mt-12">
|
||||||
<H2 className="text-center">Agents with Endless Possibilities.</H2>
|
<SectionHeader className="text-center">Agents with Endless Possibilities.</SectionHeader>
|
||||||
</div>
|
</div>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
<FadeIn transition={{ duration: 0.8, delay: 0.2 }}>
|
<FadeIn transition={{ duration: 0.8, delay: 0.2 }}>
|
||||||
|
|||||||
180
src/components/ClickableGalleryLight.tsx
Normal file
180
src/components/ClickableGalleryLight.tsx
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useResponsiveCarousel } from '@/hooks/useResponsiveCarousel';
|
||||||
|
import Image from 'next/image'
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
|
import { wrap } from 'popmotion'
|
||||||
|
import { Button } from '@/components/Button';
|
||||||
|
import { SectionHeader, P, CT, Eyebrow } from '@/components/Texts';
|
||||||
|
import { TypeAnimation } from 'react-type-animation'
|
||||||
|
import { FadeIn } from './FadeIn';
|
||||||
|
|
||||||
|
const galleryItems = [
|
||||||
|
{ text: 'Navigate and interact with any web interface', image: '/images/gallery/interface.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Process documents across all formats', image: '/images/gallery/docs.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Execute multi-step workflows autonomously', image: '/images/gallery/flow.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Manage calendars, emails, and tasks', image: '/images/gallery/calendar.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Perform deep semantic search across all data sources', image: '/images/gallery/data.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Identify patterns in complex datasets', image: '/images/gallery/datasets.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Provide real-time market intelligence', image: '/images/gallery/market.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Generate and debug code in multiple languages', image: '/images/gallery/code.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Create consistent branded content', image: '/images/gallery/branding.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Translate and localize materials', image: '/images/gallery/translate.jpg', width: 448, height: 277 },
|
||||||
|
{ text: 'Transform and migrate data structures', image: '/images/gallery/structure.jpg', width: 448, height: 277 },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 🔧 Carousel Config
|
||||||
|
const VISIBLE = 4;
|
||||||
|
const AUTOPLAY_MS = 3200;
|
||||||
|
|
||||||
|
export function ClickableGalleryLight() {
|
||||||
|
const [active, setActive] = useState(0);
|
||||||
|
const [hovering, setHovering] = useState(false);
|
||||||
|
const { GAP, ROT_Y, DEPTH, SCALE_DROP } = useResponsiveCarousel();
|
||||||
|
|
||||||
|
// autoplay
|
||||||
|
useEffect(() => {
|
||||||
|
if (hovering) return
|
||||||
|
const id = setInterval(() => setActive((i) => wrap(0, galleryItems.length, i + 1)), AUTOPLAY_MS)
|
||||||
|
return () => clearInterval(id)
|
||||||
|
}, [hovering])
|
||||||
|
|
||||||
|
const indices = useMemo(
|
||||||
|
() => [...Array(VISIBLE * 2 + 1)].map((_, i) => wrap(0, galleryItems.length, active + i - VISIBLE)),
|
||||||
|
[active]
|
||||||
|
)
|
||||||
|
|
||||||
|
const next = () => setActive((i) => wrap(0, galleryItems.length, i + 1))
|
||||||
|
const prev = () => setActive((i) => wrap(0, galleryItems.length, i - 1))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-[#FAFAFA]">
|
||||||
|
<div className="relative isolate pt-8 pb-0 text-center w-full">
|
||||||
|
<FadeIn transition={{ duration: 0.8, delay: 0.1 }}>
|
||||||
|
<div className="mx-auto max-w-5xl lg:mt-12">
|
||||||
|
<Eyebrow color="accent">Use Cases</Eyebrow>
|
||||||
|
<SectionHeader className="text-center" color="dark">Agents with Endless Possibilities.</SectionHeader>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn transition={{ duration: 0.8, delay: 0.2 }}>
|
||||||
|
<div className="mx-auto max-w-4xl mt-6 lg:px-0 px-4">
|
||||||
|
<P className="text-center" color="dark">
|
||||||
|
Your private agent coordinates a team of specialists that spin up on demand, collaborate across your world, and deliver end-to-end results.
|
||||||
|
Many agents, one intelligence—yours.
|
||||||
|
</P>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
</div>
|
||||||
|
<FadeIn transition={{ duration: 1, delay: 0.4 }}>
|
||||||
|
<section
|
||||||
|
className="relative w-full flex items-center justify-center overflow-hidden -mt-8 pt-0 pb-0"
|
||||||
|
onMouseEnter={() => setHovering(true)}
|
||||||
|
onMouseLeave={() => setHovering(false)}
|
||||||
|
>
|
||||||
|
<div className="relative w-full max-w-[1800px] h-[300px] md:h-[500px]" style={{ perspective: '1600px' }}>
|
||||||
|
<div className="absolute inset-0" style={{ transformStyle: 'preserve-3d' }}>
|
||||||
|
<AnimatePresence initial={false}>
|
||||||
|
{indices.map((idx, i) => {
|
||||||
|
const distance = i - VISIBLE;
|
||||||
|
const item = galleryItems[idx];
|
||||||
|
|
||||||
|
const x = distance * GAP;
|
||||||
|
const z = -Math.abs(distance) * DEPTH;
|
||||||
|
const r = distance * ROT_Y;
|
||||||
|
const s = 1 - Math.abs(distance) * SCALE_DROP;
|
||||||
|
const o = distance === 0 ? 1 : 0.80;
|
||||||
|
const zIndex = 100 - Math.abs(distance);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
key={`${idx}-${i}`}
|
||||||
|
className={`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 will-change-transform overflow-hidden ${distance === 0 ? 'rounded-xl' : ''}`}
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{
|
||||||
|
transform: `translateX(${x}px) translateZ(${z}px) rotateY(${r}deg) scale(${s})`,
|
||||||
|
zIndex,
|
||||||
|
opacity: o,
|
||||||
|
boxShadow: distance === 0 ? '0 0 20px 5px rgba(0, 0, 0, 0.1)' : 'none',
|
||||||
|
}}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
transition={{ type: 'spring', stiffness: 220, damping: 26 }}
|
||||||
|
onClick={() => setActive(idx)}
|
||||||
|
>
|
||||||
|
<div className="relative bg-gray-100 flex items-center justify-center">
|
||||||
|
<Image
|
||||||
|
src={item.image}
|
||||||
|
alt={item.text}
|
||||||
|
width={item.width}
|
||||||
|
height={item.height}
|
||||||
|
className="object-contain"
|
||||||
|
priority={i === VISIBLE}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</AnimatePresence>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Arrows */}
|
||||||
|
<div className="absolute inset-y-0 left-8 hidden md:flex items-center z-50">
|
||||||
|
<button
|
||||||
|
onClick={prev}
|
||||||
|
className="bg-white/50 rounded-full p-2 shadow-lg backdrop-blur-md text-black"
|
||||||
|
aria-label="Previous"
|
||||||
|
>
|
||||||
|
<svg className="size-8" viewBox="0 0 24 24" fill="none" dangerouslySetInnerHTML={{ __html: '<path d="M15 19L8 12l7-7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>' }} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="absolute inset-y-0 right-8 hidden md:flex items-center z-50">
|
||||||
|
<button
|
||||||
|
onClick={next}
|
||||||
|
className="bg-white/50 rounded-full p-2 shadow-lg backdrop-blur-md text-black"
|
||||||
|
aria-label="Next"
|
||||||
|
>
|
||||||
|
<svg className="size-8" viewBox="0 0 24 24" fill="none" dangerouslySetInnerHTML={{ __html: '<path d="M9 5l7 7-7 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>' }} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Foreground pill (Desktop) */}
|
||||||
|
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-[60] hidden md:block">
|
||||||
|
<div className="flex items-center justify-between w-[1040px] gap-6 rounded-2xl bg-gray-100/80 shadow-[0_8px_40px_rgba(0,0,0,0.15)] px-12 backdrop-blur">
|
||||||
|
<CT as="h4" className="max-w-[820px] h-[72px] flex items-center" color="dark">
|
||||||
|
<TypeAnimation
|
||||||
|
key={active}
|
||||||
|
sequence={[galleryItems[active].text]}
|
||||||
|
wrapper="span"
|
||||||
|
speed={50}
|
||||||
|
repeat={0}
|
||||||
|
/>
|
||||||
|
</CT>
|
||||||
|
<Button href="#" color="cyan" className="text-sm px-4 py-2 lg:text-base whitespace-nowrap">
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Text box (Mobile) */}
|
||||||
|
<div className="md:hidden w-full px-4 -mt-12 mb-16">
|
||||||
|
<div className="flex flex-row items-center justify-between w-full gap-x-4 rounded-2xl bg-gray-100/80 p-4 backdrop-blur-md">
|
||||||
|
<CT as="h4" className="w-full text-left h-[72px] leading-tight flex items-center" color="dark">
|
||||||
|
<TypeAnimation
|
||||||
|
key={active}
|
||||||
|
sequence={[galleryItems[active].text]}
|
||||||
|
wrapper="span"
|
||||||
|
speed={50}
|
||||||
|
repeat={0}
|
||||||
|
/>
|
||||||
|
</CT>
|
||||||
|
<Button href="#" color="cyan" className="text-xs px-3 py-1.5 whitespace-nowrap">
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { motion } from "framer-motion";
|
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";
|
import { InfiniteMovingCards } from "@/components/magicui/infinite-moving-cards";
|
||||||
|
|
||||||
|
|
||||||
@@ -50,6 +50,7 @@ export function Companies() {
|
|||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 1 }}
|
transition={{ duration: 1 }}
|
||||||
>
|
>
|
||||||
|
<Eyebrow color="accent"></Eyebrow>
|
||||||
<P className="hidden min-xl:text-gray-100 text-center mb-6">
|
<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.
|
Mycelium Cloud allows you to deploy and scale AI agents from top global providers on a decentralized, privacy-first infrastructure.
|
||||||
</P>
|
</P>
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import CountUp from 'react-countup'
|
import CountUp from 'react-countup'
|
||||||
import { H2 } from './Texts'
|
import { SectionHeader } from './Texts'
|
||||||
|
|
||||||
interface CountUpNumberProps {
|
interface CountUpNumberProps {
|
||||||
end: number
|
end: number
|
||||||
className?: string
|
className?: string
|
||||||
color?: 'light' | 'primary' | 'secondary' | 'custom'
|
color?: 'light' | 'primary' | 'secondary'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CountUpNumber({ end, className, color }: CountUpNumberProps) {
|
export function CountUpNumber({ end, className, color }: CountUpNumberProps) {
|
||||||
return (
|
return (
|
||||||
<H2 color={color} className={className}>
|
<SectionHeader color={color} className={className}>
|
||||||
<CountUp end={end} duration={2.75} separator="," />
|
<CountUp end={end} duration={2.75} separator="," />
|
||||||
</H2>
|
</SectionHeader>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export function Faqs() {
|
|||||||
id="faqs"
|
id="faqs"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
aria-labelledby="faqs-title"
|
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
|
<motion.div
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import clsx from 'clsx'
|
|||||||
|
|
||||||
const formClasses = {
|
const formClasses = {
|
||||||
light:
|
light:
|
||||||
'block w-full appearance-none rounded-lg border border-gray-200 bg-white py-[calc(--spacing(2)-1px)] px-[calc(--spacing(3)-1px)] text-gray-900 placeholder:text-gray-400 focus:border-cyan-500 focus:outline-none focus:ring-cyan-500 sm:text-sm',
|
'block w-full appearance-none rounded-lg border border-gray-300 bg-white py-[calc(--spacing(2)-1px)] px-[calc(--spacing(3)-1px)] text-gray-900 placeholder:text-gray-400 focus:border-cyan-500 focus:outline-none focus:ring-cyan-500 sm:text-sm',
|
||||||
dark:
|
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',
|
'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',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { H2, P, CP } from "@/components/Texts";
|
import { SectionHeader, P, CP } from "@/components/Texts";
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
|
|
||||||
export function GetStarted() {
|
export function GetStarted() {
|
||||||
@@ -36,7 +36,7 @@ export function GetStarted() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="max-w-8xl mx-auto px-4 text-left mb-12">
|
<div className="max-w-8xl mx-auto px-4 text-left mb-12">
|
||||||
<H2>Get Started</H2>
|
<SectionHeader>Get Started</SectionHeader>
|
||||||
<P>Explore the documentation, code, and support channels to start building with Mycelium Cloud.</P>
|
<P>Explore the documentation, code, and support channels to start building with Mycelium Cloud.</P>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import CountUp from "react-countup";
|
import CountUp from "react-countup";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { H2, P } from "@/components/Texts";
|
import { SectionHeader, P } from "@/components/Texts";
|
||||||
|
|
||||||
export function GridStats() {
|
export function GridStats() {
|
||||||
return (
|
return (
|
||||||
@@ -21,9 +21,9 @@ export function GridStats() {
|
|||||||
{/* Column 1: Title & Description */}
|
{/* Column 1: Title & Description */}
|
||||||
<div className="flex flex-col space-y-10">
|
<div className="flex flex-col space-y-10">
|
||||||
<div>
|
<div>
|
||||||
<H2 color="light">
|
<SectionHeader color="light">
|
||||||
Robust Infrastructure for your Intellegence Needs
|
Robust Infrastructure for your Intellegence Needs
|
||||||
</H2>
|
</SectionHeader>
|
||||||
<P color="light" className="mt-6">
|
<P color="light" className="mt-6">
|
||||||
Mycelium's groundbreaking technology provides dedicated, performance-validated GPUs for your AI workloads.
|
Mycelium's groundbreaking technology provides dedicated, performance-validated GPUs for your AI workloads.
|
||||||
</P>
|
</P>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export function Header() {
|
|||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<PopoverButton
|
<PopoverButton
|
||||||
className="relative z-10 -m-2 inline-flex items-center rounded-lg stroke-white p-2 hover:bg-gray-200/50 hover:stroke-gray-400 focus:not-data-focus:outline-hidden active:stroke-white"
|
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"
|
aria-label="Toggle site navigation"
|
||||||
>
|
>
|
||||||
{({ open }) =>
|
{({ open }) =>
|
||||||
@@ -113,11 +113,7 @@ export function Header() {
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="white"
|
color="white"
|
||||||
onClick={() => {
|
href="mailto:info@ourworld.tf"
|
||||||
if (typeof window !== 'undefined' && (window as any).ml_account) {
|
|
||||||
(window as any).ml_account('webforms', '6108375', 'l9m8g1', 'show')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Join the Waitlist
|
Join the Waitlist
|
||||||
</Button>
|
</Button>
|
||||||
@@ -134,11 +130,7 @@ export function Header() {
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="white"
|
color="white"
|
||||||
onClick={() => {
|
href="mailto:info@ourworld.tf"
|
||||||
if (typeof window !== 'undefined' && (window as any).ml_account) {
|
|
||||||
(window as any).ml_account('webforms', '6108375', 'l9m8g1', 'show')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Join the Waitlist
|
Join the Waitlist
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
98
src/components/HeaderLight.tsx
Normal file
98
src/components/HeaderLight.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
|
||||||
|
function NavLinks() {
|
||||||
|
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
|
||||||
|
let timeoutRef = useRef<number | null>(null)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-x-5">
|
||||||
|
<div className="flex items-center gap-x-5 border-l border-white/10 pl-5">
|
||||||
|
{[
|
||||||
|
['Technologies', '/#technologies'],
|
||||||
|
['Network', '/#network'],
|
||||||
|
['How it Works', '/#how-it-works'],
|
||||||
|
['Get Started', '/#get-started'],
|
||||||
|
['Contact', '/#contact'],
|
||||||
|
].map(([label, href], index) => (
|
||||||
|
<Link
|
||||||
|
key={label}
|
||||||
|
href={href}
|
||||||
|
className={clsx(
|
||||||
|
'relative rounded-lg px-3 py-2 text-sm text-black transition-colors delay-150 hover:text-gray-700 hover:delay-0',
|
||||||
|
)}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
window.clearTimeout(timeoutRef.current)
|
||||||
|
}
|
||||||
|
setHoveredIndex(index)
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
timeoutRef.current = window.setTimeout(() => {
|
||||||
|
setHoveredIndex(null)
|
||||||
|
}, 200)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AnimatePresence>
|
||||||
|
{hoveredIndex === index && (
|
||||||
|
<motion.span
|
||||||
|
className="absolute inset-0 rounded-lg bg-white/10"
|
||||||
|
layoutId="hoverBackground"
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1, transition: { duration: 0.15 } }}
|
||||||
|
exit={{
|
||||||
|
opacity: 0,
|
||||||
|
transition: { duration: 0.15 },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
<span className="relative z-10">{label}</span>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HeaderLight() {
|
||||||
|
const [isVisible, setIsVisible] = useState(true);
|
||||||
|
const [lastScrollY, setLastScrollY] = useState(0);
|
||||||
|
|
||||||
|
const controlHeader = () => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
if (window.scrollY > lastScrollY && window.scrollY > 100) { // Hides when scrolling down past 100px
|
||||||
|
setIsVisible(false);
|
||||||
|
} else { // Shows when scrolling up
|
||||||
|
setIsVisible(true);
|
||||||
|
}
|
||||||
|
setLastScrollY(window.scrollY);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('scroll', controlHeader);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('scroll', controlHeader);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [lastScrollY]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.header
|
||||||
|
className="fixed top-4 left-0 right-0 z-50 flex justify-center"
|
||||||
|
initial={{ y: 0, opacity: 1 }}
|
||||||
|
animate={{ y: isVisible ? 0 : -100, opacity: isVisible ? 1 : 0 }}
|
||||||
|
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||||
|
>
|
||||||
|
<div className="rounded-full bg-gray-50/90 px-5 py-3 shadow-lg ring-1 ring-white/10 backdrop-blur-sm">
|
||||||
|
<NavLinks />
|
||||||
|
</div>
|
||||||
|
</motion.header>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import { Bars3Icon, XMarkIcon, ChevronDoubleDownIcon } from '@heroicons/react/24
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import diamondSvg from '@/images/diamond.svg'
|
import diamondSvg from '@/images/diamond.svg'
|
||||||
import { Container } from '@/components/Container';
|
import { Container } from '@/components/Container';
|
||||||
import { H2, P, H3 } from '@/components/Texts';
|
import { SectionHeader, P, H3 } from '@/components/Texts';
|
||||||
import { Candy } from '@/components/Candy'
|
import { Candy } from '@/components/Candy'
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
@@ -65,10 +65,10 @@ export function HomeAbout() {
|
|||||||
transition={{ duration: 1, delay: 0.2 }}
|
transition={{ duration: 1, delay: 0.2 }}
|
||||||
className="absolute top-24 left-0 max-w-xl text-left"
|
className="absolute top-24 left-0 max-w-xl text-left"
|
||||||
>
|
>
|
||||||
<H2>
|
<SectionHeader>
|
||||||
Enterprise AI Agents
|
Enterprise AI Agents
|
||||||
That Never Sleep.
|
That Never Sleep.
|
||||||
</H2>
|
</SectionHeader>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
@@ -76,7 +76,7 @@ export function HomeAbout() {
|
|||||||
transition={{ duration: 1, delay: 0.4 }}
|
transition={{ duration: 1, delay: 0.4 }}
|
||||||
className="absolute top-54 left-0 max-w-xl text-left"
|
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.
|
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>
|
</P>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { motion } from 'framer-motion'
|
|||||||
import { TypeAnimation } from 'react-type-animation'
|
import { TypeAnimation } from 'react-type-animation'
|
||||||
import { Dialog, DialogPanel } from '@headlessui/react'
|
import { Dialog, DialogPanel } from '@headlessui/react'
|
||||||
import { Bars3Icon, XMarkIcon, ChevronDoubleDownIcon } from '@heroicons/react/24/outline'
|
import { Bars3Icon, XMarkIcon, ChevronDoubleDownIcon } from '@heroicons/react/24/outline'
|
||||||
import { H1, H2, PL } from '@/components/Texts'
|
import { H1, H2, P } from '@/components/Texts'
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{ name: 'Product', href: '#' },
|
{ name: 'Product', href: '#' },
|
||||||
@@ -54,9 +54,9 @@ export function HomeHero() {
|
|||||||
transition={{ duration: 1, delay: 1 }}
|
transition={{ duration: 1, delay: 1 }}
|
||||||
className="mt-12"
|
className="mt-12"
|
||||||
>
|
>
|
||||||
<PL className="mx-auto max-w-4xl text-center 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.
|
Mycelium's advancements in Agentic infrastructure supports private, secure and autonomous Agents that connect, learn and grow with you.
|
||||||
</PL>
|
</P>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
55
src/components/HomeHeroLight.tsx
Normal file
55
src/components/HomeHeroLight.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
import { TypeAnimation } from 'react-type-animation'
|
||||||
|
import { Dialog, DialogPanel } from '@headlessui/react'
|
||||||
|
import { Bars3Icon, XMarkIcon, ChevronDoubleDownIcon } from '@heroicons/react/24/outline'
|
||||||
|
import { H1, H2, PL } from '@/components/Texts'
|
||||||
|
import { ChevronRightIcon } from '@heroicons/react/20/solid'
|
||||||
|
|
||||||
|
const navigation = [
|
||||||
|
{ name: 'Product', href: '#' },
|
||||||
|
{ name: 'Features', href: '#' },
|
||||||
|
{ name: 'Marketplace', href: '#' },
|
||||||
|
{ name: 'Company', href: '#' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function HomeHeroLight() {
|
||||||
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="relative isolate overflow-hidden bg-white"
|
||||||
|
style={{
|
||||||
|
backgroundImage: 'url(/images/cloud.png)',
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundPosition: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
|
||||||
|
<div className="mx-auto max-w-7xl px-6 pt-10 pb-24 sm:pb-32 lg:flex lg:px-8 lg:py-40">
|
||||||
|
<div className="mx-auto max-w-2xl shrink-0 lg:mx-0 lg:pt-8">
|
||||||
|
|
||||||
|
<h1 className="mt-10 text-5xl font-semibold tracking-tight text-pretty text-gray-900 sm:text-7xl">
|
||||||
|
Decentralized Autonomous Agentic Cloud.
|
||||||
|
</h1>
|
||||||
|
<p className="mt-8 text-lg font-medium text-pretty text-gray-500 sm:text-xl/8">
|
||||||
|
Mycelium's advancements in Agentic infrastructure supports private, secure and autonomous Agents that connect, learn and grow with you.
|
||||||
|
</p>
|
||||||
|
<div className="mt-10 flex items-center gap-x-6">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-xs hover:bg-indigo-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||||
|
>
|
||||||
|
Get started
|
||||||
|
</a>
|
||||||
|
<a href="#" className="text-sm/6 font-semibold text-gray-900">
|
||||||
|
Learn more <span aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
60
src/components/HomeHeroLight2.tsx
Normal file
60
src/components/HomeHeroLight2.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useRef, useEffect } from 'react'
|
||||||
|
import { H1, H2, P, H5, Eyebrow } from '@/components/Texts'
|
||||||
|
|
||||||
|
export function HomeHeroLight2() {
|
||||||
|
const videoRef = useRef<HTMLVideoElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (videoRef.current) videoRef.current.playbackRate = 0.4
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="relative h-screen overflow-hidden">
|
||||||
|
{/* Background video */}
|
||||||
|
<video
|
||||||
|
ref={videoRef}
|
||||||
|
src="/videos/cloud.mp4"
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
muted
|
||||||
|
playsInline
|
||||||
|
className="absolute inset-0 h-full w-full object-cover z-[-10]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Global soft wash + blur */}
|
||||||
|
<div className="absolute inset-0 bg-white opacity-30 backdrop-blur-md z-0" />
|
||||||
|
|
||||||
|
{/* Center “halo” for text legibility */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 z-0"
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
'radial-gradient(ellipse at center, rgba(255,255,255,0.96) 0%, rgba(255,255,255,0.88) 15%, rgba(255,255,255,0.72) 35%, rgba(255,255,255,0.08) 75%)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="relative z-10 h-full flex items-center justify-center">
|
||||||
|
<div className="mx-auto max-w-4xl text-center px-6 lg:px-8">
|
||||||
|
<Eyebrow color="accent"></Eyebrow>
|
||||||
|
<H1
|
||||||
|
className="pt-6"
|
||||||
|
style={{ textShadow: '0 2px 8px rgba(0,0,0,0.08)' }}
|
||||||
|
>
|
||||||
|
Decentralized Autonomous Agentic Cloud.
|
||||||
|
</H1>
|
||||||
|
|
||||||
|
<H5
|
||||||
|
className="mt-8"
|
||||||
|
style={{ textShadow: '0 1px 4px rgba(0,0,0,0.06)' }}
|
||||||
|
>
|
||||||
|
Mycelium's advancements in Agentic infrastructure support private, secure, and
|
||||||
|
autonomous Agents that connect, learn, and grow with you.
|
||||||
|
</H5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import { Footer } from '@/components/Footer'
|
import { Footer } from '@/components/Footer'
|
||||||
import { Header } from '@/components/Header'
|
import { HeaderLight } from '@/components/HeaderLight'
|
||||||
|
|
||||||
export function Layout({ children }: { children: React.ReactNode }) {
|
export function Layout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<HeaderLight />
|
||||||
|
{children}
|
||||||
<main className="flex-auto">{children}</main>
|
<main className="flex-auto">{children}</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Link from 'next/link'
|
|||||||
import { AnimatePresence, motion } from 'framer-motion'
|
import { AnimatePresence, motion } from 'framer-motion'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|
||||||
export function NavLinks({ className }: { className?: string }) {
|
export function NavLinks({ className, theme = 'dark' }: { className?: string, theme?: 'dark' | 'light' }) {
|
||||||
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
|
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
|
||||||
let timeoutRef = useRef<number | null>(null)
|
let timeoutRef = useRef<number | null>(null)
|
||||||
|
|
||||||
@@ -33,7 +33,10 @@ export function NavLinks({ className }: { className?: string }) {
|
|||||||
key={label}
|
key={label}
|
||||||
href={href}
|
href={href}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'relative -mx-3 -my-2 rounded-lg px-3 py-2 text-sm text-white transition-colors delay-150 hover:text-gray-300 hover:delay-0',
|
'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,
|
className,
|
||||||
)}
|
)}
|
||||||
onClick={(e) => handleClick(e, href)}
|
onClick={(e) => handleClick(e, href)}
|
||||||
@@ -52,7 +55,10 @@ export function NavLinks({ className }: { className?: string }) {
|
|||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{hoveredIndex === index && (
|
{hoveredIndex === index && (
|
||||||
<motion.span
|
<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"
|
layoutId="hoverBackground"
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1, transition: { duration: 0.15 } }}
|
animate={{ opacity: 1, transition: { duration: 0.15 } }}
|
||||||
|
|||||||
75
src/components/StackSectionLight.tsx
Normal file
75
src/components/StackSectionLight.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { StackedCubesLight } from "@/components/ui/StackedCubesLight";
|
||||||
|
import { H2, P, SectionHeader, Eyebrow } from "@/components/Texts";
|
||||||
|
import { FadeIn } from "./FadeIn";
|
||||||
|
import { DottedGlowBackground } from '@/components/ui/dotted-glow-background';
|
||||||
|
|
||||||
|
export function StackSectionLight() {
|
||||||
|
return (
|
||||||
|
<section className="relative w-full overflow-hidden py-24 lg:py-40">
|
||||||
|
{/* === Background Layer === */}
|
||||||
|
<div className="absolute inset-0 -z-10 bg-[#FAFAFA]">
|
||||||
|
{/* Dotted Glow Background */}
|
||||||
|
<DottedGlowBackground
|
||||||
|
gap={15}
|
||||||
|
radius={2}
|
||||||
|
color="rgba(0,0,0,0.4)"
|
||||||
|
glowColor="rgba(0,170,255,0.85)"
|
||||||
|
opacity={0.2}
|
||||||
|
/>
|
||||||
|
{/* Faint 3D grid floor */}
|
||||||
|
<div className="absolute inset-0 flex items-end justify-center overflow-hidden">
|
||||||
|
<div className="w-[200vw] h-[200vh] bg-[linear-gradient(to_right,rgba(0,0,0,0.03)_1px,transparent_1px),linear-gradient(to_bottom,rgba(0,0,0,0.03)_1px,transparent_1px)] bg-[size:60px_60px] [transform:perspective(800px)_rotateX(70deg)] origin-bottom opacity-50" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* === Content === */}
|
||||||
|
<div className="relative mx-auto max-w-7xl px-6 lg:px-8 grid grid-cols-1 lg:grid-cols-3 gap-16 items-center">
|
||||||
|
{/* Left Column - Text */}
|
||||||
|
<div className="text-center lg:text-left">
|
||||||
|
<FadeIn>
|
||||||
|
<Eyebrow color="accent">Technology Layers</Eyebrow>
|
||||||
|
<SectionHeader color="dark" className="text-4xl sm:text-5xl font-semibold">
|
||||||
|
The Mycelium Stack
|
||||||
|
</SectionHeader>
|
||||||
|
</FadeIn>
|
||||||
|
|
||||||
|
<FadeIn>
|
||||||
|
<P color="dark" className="mt-6 text-lg leading-relaxed text-gray-600">
|
||||||
|
Built with Mycelium technology, our AI infrastructure ensures
|
||||||
|
unbreakable networks, complete data sovereignty, ultra-secure
|
||||||
|
agent-human communication, and unhackable data storage systems.
|
||||||
|
</P>
|
||||||
|
</FadeIn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right Column - Animated Stack */}
|
||||||
|
<div className="lg:col-span-2 flex items-center justify-center lg:justify-start relative">
|
||||||
|
<motion.div
|
||||||
|
initial={{ y: 30, opacity: 0 }}
|
||||||
|
whileInView={{ y: 0, opacity: 1 }}
|
||||||
|
transition={{ duration: 1.2, ease: "easeOut" }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
animate={{
|
||||||
|
y: [0, -10, 0],
|
||||||
|
rotateZ: [0, 0.5, -0.5, 0],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 6,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: "easeInOut",
|
||||||
|
}}
|
||||||
|
className="relative"
|
||||||
|
>
|
||||||
|
<StackedCubesLight />
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import React, { useRef } from 'react'
|
import React, { useRef } from 'react'
|
||||||
import { motion, useInView } from 'framer-motion'
|
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'
|
import { TbCircleNumber1Filled, TbCircleNumber2Filled, TbCircleNumber3Filled } from 'react-icons/tb'
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
@@ -30,7 +30,7 @@ export function Steps() {
|
|||||||
const isInView = useInView(ref, { once: true });
|
const isInView = useInView(ref, { once: true });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="benefits" ref={ref} className="relative pt-12 pb-4 px-4 lg:px-12 text-white">
|
<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">
|
<div className="relative px-6 lg:px-12">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
@@ -38,9 +38,10 @@ export function Steps() {
|
|||||||
transition={{ duration: 0.8, delay: 0.1 }}
|
transition={{ duration: 0.8, delay: 0.1 }}
|
||||||
className="mx-auto max-w-5xl text-center"
|
className="mx-auto max-w-5xl text-center"
|
||||||
>
|
>
|
||||||
<H2 className="text-3xl font-medium tracking-tight" color="light">
|
<Eyebrow color="accent">Get Started</Eyebrow>
|
||||||
|
<SectionHeader className="text-3xl font-medium tracking-tight" color="light">
|
||||||
Deploy Scalable LLMs and AI Agents in Seconds
|
Deploy Scalable LLMs and AI Agents in Seconds
|
||||||
</H2>
|
</SectionHeader>
|
||||||
<P className="mt-6" color="light">
|
<P className="mt-6" color="light">
|
||||||
Launch and scale intelligence on your own terms. Mycelium Cloud makes it simple to deploy models, integrate knowledge, and run everything on a network you control.
|
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>
|
</P>
|
||||||
@@ -57,7 +58,7 @@ export function Steps() {
|
|||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
||||||
transition={{ duration: 0.5, delay: 0.3 + index * 0.2 }}
|
transition={{ duration: 0.5, delay: 0.3 + index * 0.2 }}
|
||||||
className="rounded-2xl border border-white/20 bg-black/30 lg:py-8 lg:px-8 py-6 px-6 backdrop-blur-sm transition-all duration-300 ease-in-out hover:scale-105 hover:border-white/40 hover:bg-black/40"
|
className="rounded-2xl border border-gray-300 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 bg-white/5 backdrop-blur-md"
|
||||||
>
|
>
|
||||||
<feature.icon className="h-8 w-8 mb-4 text-white" />
|
<feature.icon className="h-8 w-8 mb-4 text-white" />
|
||||||
<CT as="span" className="font-semibold" color="light">{feature.name}</CT>
|
<CT as="span" className="font-semibold" color="light">{feature.name}</CT>
|
||||||
|
|||||||
@@ -4,10 +4,14 @@ import React from 'react'
|
|||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const colorVariants = {
|
const colorVariants = {
|
||||||
primary: 'text-[#fffff]',
|
primary: 'text-gray-900',
|
||||||
secondary: 'text-gray-200',
|
secondary: 'text-gray-600',
|
||||||
custom: 'text-[#015eff]',
|
light: 'text-gray-50',
|
||||||
light: '[#fcfcfc]',
|
accent: 'text-cyan-500',
|
||||||
|
white: 'text-white',
|
||||||
|
dark: 'text-gray-950',
|
||||||
|
tertiary: 'text-gray-700',
|
||||||
|
lightSecondary: 'text-gray-300',
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
type TextOwnProps = {
|
type TextOwnProps = {
|
||||||
@@ -15,17 +19,19 @@ type TextOwnProps = {
|
|||||||
className?: string
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Polymorphic helpers (no forwardRef needed)
|
// Polymorphic helpers
|
||||||
type PolymorphicProps<E extends React.ElementType, P> =
|
type PolymorphicProps<E extends React.ElementType, P> = P & {
|
||||||
P & { as?: E } &
|
as?: E
|
||||||
Omit<React.ComponentPropsWithoutRef<E>, keyof P | 'as'>
|
} & Omit<React.ComponentPropsWithoutRef<E>, keyof P | 'as'>
|
||||||
|
|
||||||
const createTextComponent = <DefaultElement extends React.ElementType>(
|
const createTextComponent = <DefaultElement extends React.ElementType>(
|
||||||
defaultElement: DefaultElement,
|
defaultElement: DefaultElement,
|
||||||
defaultClassName: string
|
defaultClassName: string
|
||||||
) => {
|
) => {
|
||||||
type Props<E extends React.ElementType = DefaultElement> =
|
type Props<E extends React.ElementType = DefaultElement> = PolymorphicProps<
|
||||||
PolymorphicProps<E, TextOwnProps>
|
E,
|
||||||
|
TextOwnProps
|
||||||
|
>
|
||||||
|
|
||||||
function Text<E extends React.ElementType = DefaultElement>({
|
function Text<E extends React.ElementType = DefaultElement>({
|
||||||
as,
|
as,
|
||||||
@@ -38,24 +44,106 @@ const createTextComponent = <DefaultElement extends React.ElementType>(
|
|||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
className={cn(defaultClassName, colorVariants[color], className)}
|
className={cn(defaultClassName, colorVariants[color], className)}
|
||||||
{...(props as object)}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Tag>
|
</Tag>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
;(Text as any).displayName = `Text(${typeof defaultElement === 'string' ? defaultElement : 'Component'})`
|
;(Text as any).displayName = `Text(${typeof defaultElement === 'string' ? defaultElement : 'Component'
|
||||||
|
})`
|
||||||
return Text
|
return Text
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exports
|
// Exports based on your tailwind.css and the example
|
||||||
export const H1 = createTextComponent('h1', 'text-5xl font-medium tracking-tight text-balance lg:text-8xl')
|
export const H1 = createTextComponent(
|
||||||
export const PL = createTextComponent('p', 'text-2xl font-medium text-pretty leading-[1.2] lg:text-3xl')
|
'h1',
|
||||||
export const H2 = createTextComponent('h2', 'text-3xl font-medium text-pretty lg:text-4xl')
|
'text-6xl lg:text-7xl font-medium leading-tight tracking-tight'
|
||||||
export const P = createTextComponent('p', 'text-lg font-normal text-pretty leading-snug lg:text-xl lg:leading-normal')
|
)
|
||||||
export const H3 = createTextComponent('h3', 'text-2xl lg:text-3xl font-medium')
|
export const H2 = createTextComponent(
|
||||||
export const H4 = createTextComponent('h4', 'text-xl lg:text-2xl font-semibold leading-[1.15]')
|
'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 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')
|
export const CP = createTextComponent('p', 'text-sm lg:text-base leading-[1.525] font-light')
|
||||||
export const NL = createTextComponent('span', 'text-lg font-semibold leading-[1.23]')
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
} from '@heroicons/react/24/solid'
|
} from '@heroicons/react/24/solid'
|
||||||
|
|
||||||
import { Container } from '@/components/Container'
|
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'
|
import { motion, useInView } from 'framer-motion'
|
||||||
|
|
||||||
interface Review {
|
interface Review {
|
||||||
@@ -147,9 +147,9 @@ export function UseCases() {
|
|||||||
transition={{ duration: 0.8, delay: 0.1 }}
|
transition={{ duration: 0.8, delay: 0.1 }}
|
||||||
className="flex flex-col items-start justify-start pt-10 lg:pr-12"
|
className="flex flex-col items-start justify-start pt-10 lg:pr-12"
|
||||||
>
|
>
|
||||||
<H2 id="usecases-title" color="light" className="text-left">
|
<SectionHeader id="usecases-title" color="light" className="text-left">
|
||||||
Augmented Intelligence Fabric
|
Augmented Intelligence Fabric
|
||||||
</H2>
|
</SectionHeader>
|
||||||
<P className="mt-4 text-left" color="light">
|
<P className="mt-4 text-left" color="light">
|
||||||
The sovereign substrate for autonomous AI.
|
The sovereign substrate for autonomous AI.
|
||||||
Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.
|
Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { Globe } from "@/components/ui/globe"
|
import { Globe } from "@/components/ui/globe"
|
||||||
import { motion } from "framer-motion"
|
import { motion } from "framer-motion"
|
||||||
import { H2, P, CT, CP } from "@/components/Texts"
|
import { H2, P, CT, CP, SectionHeader, Eyebrow } from "@/components/Texts"
|
||||||
import { CountUpNumber } from './CountUpNumber'
|
import { CountUpNumber } from './CountUpNumber'
|
||||||
|
|
||||||
export function WorldMap() {
|
export function WorldMap() {
|
||||||
@@ -31,8 +31,9 @@ export function WorldMap() {
|
|||||||
transition={{ duration: 0.5 }}
|
transition={{ duration: 0.5 }}
|
||||||
className="max-w-xl"
|
className="max-w-xl"
|
||||||
>
|
>
|
||||||
<H2 color="light">Mycelium Network is Live.</H2>
|
<Eyebrow color="accent">Network</Eyebrow>
|
||||||
<P className="hidden mt-4 text-base leading-relaxed font-light" color="light">
|
<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
|
Mycelium Cloud's advancement technology enables anyone to deploy
|
||||||
their own Internet infrastructure, anywhere.
|
their own Internet infrastructure, anywhere.
|
||||||
</P>
|
</P>
|
||||||
@@ -58,8 +59,7 @@ export function WorldMap() {
|
|||||||
initial={{ opacity: 0, x: -20 }}
|
initial={{ opacity: 0, x: -20 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ duration: 0.5, delay: 0.4 }}
|
transition={{ duration: 0.5, delay: 0.4 }}
|
||||||
whileHover={{ scale: 1.05 }}
|
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"
|
||||||
className="lg:absolute lg:top-12 lg:-left-12 rounded-xl bg-white/5 backdrop-blur-md border border-white/10 px-4 lg:py-8 py-6 shadow-md w-80"
|
|
||||||
>
|
>
|
||||||
<CT color="light" className="uppercase tracking-wide">CORES</CT>
|
<CT color="light" className="uppercase tracking-wide">CORES</CT>
|
||||||
<CountUpNumber end={54958} color="light" className="mt-2 text-3xl font-bold" />
|
<CountUpNumber end={54958} color="light" className="mt-2 text-3xl font-bold" />
|
||||||
@@ -72,8 +72,7 @@ export function WorldMap() {
|
|||||||
initial={{ opacity: 0, x: 20 }}
|
initial={{ opacity: 0, x: 20 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ duration: 0.5, delay: 0.5 }}
|
transition={{ duration: 0.5, delay: 0.5 }}
|
||||||
whileHover={{ scale: 1.05 }}
|
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"
|
||||||
className="lg:absolute lg:-top-10 lg:right-0 rounded-xl bg-white/5 backdrop-blur-md border border-white/10 px-4 lg:py-8 py-6 shadow-md w-80"
|
|
||||||
>
|
>
|
||||||
<CT color="light" className="uppercase tracking-wide">NODES</CT>
|
<CT color="light" className="uppercase tracking-wide">NODES</CT>
|
||||||
<CountUpNumber end={1493} color="light" className="mt-2 text-3xl font-bold" />
|
<CountUpNumber end={1493} color="light" className="mt-2 text-3xl font-bold" />
|
||||||
@@ -86,8 +85,7 @@ export function WorldMap() {
|
|||||||
initial={{ opacity: 0, x: -20 }}
|
initial={{ opacity: 0, x: -20 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ duration: 0.5, delay: 0.6 }}
|
transition={{ duration: 0.5, delay: 0.6 }}
|
||||||
whileHover={{ scale: 1.05 }}
|
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"
|
||||||
className="lg:absolute lg:bottom-28 lg:-left-12 rounded-xl bg-white/5 backdrop-blur-md border border-white/10 px-4 lg:py-8 py-6 shadow-md w-80"
|
|
||||||
>
|
>
|
||||||
<CT color="light" className="uppercase tracking-wide">SSD CAPACITY</CT>
|
<CT color="light" className="uppercase tracking-wide">SSD CAPACITY</CT>
|
||||||
<CountUpNumber end={5388956} color="light" className="mt-2 text-3xl font-bold" />
|
<CountUpNumber end={5388956} color="light" className="mt-2 text-3xl font-bold" />
|
||||||
@@ -100,8 +98,7 @@ export function WorldMap() {
|
|||||||
initial={{ opacity: 0, x: 20 }}
|
initial={{ opacity: 0, x: 20 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ duration: 0.5, delay: 0.7 }}
|
transition={{ duration: 0.5, delay: 0.7 }}
|
||||||
whileHover={{ scale: 1.05 }}
|
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"
|
||||||
className="lg:absolute lg:top-44 lg:right-0 rounded-xl bg-white/5 backdrop-blur-md border border-white/10 px-4 lg:py-8 py-6 shadow-md w-80"
|
|
||||||
>
|
>
|
||||||
<CT color="light" className="uppercase tracking-wide">COUNTRIES</CT>
|
<CT color="light" className="uppercase tracking-wide">COUNTRIES</CT>
|
||||||
<CountUpNumber end={44} color="light" className="mt-2 text-3xl font-bold" />
|
<CountUpNumber end={44} color="light" className="mt-2 text-3xl font-bold" />
|
||||||
|
|||||||
131
src/components/ui/CubeLight.tsx
Normal file
131
src/components/ui/CubeLight.tsx
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
interface CubeProps {
|
||||||
|
title: string;
|
||||||
|
descriptionTitle: string;
|
||||||
|
description: string;
|
||||||
|
isActive: boolean;
|
||||||
|
index: number;
|
||||||
|
onHover: () => void;
|
||||||
|
onLeave: () => void;
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({ index, ...props }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="507"
|
||||||
|
height="234"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 507 234"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill={`url(#cube-gradient-${index})`}
|
||||||
|
d="M491.651 144.747L287.198 227.339C265.219 236.22 241.783 236.22 219.802 227.339L15.3486 144.747C-5.11621 136.479 -5.11621 97.5191 15.3486 89.2539L219.802 6.65884C241.783 -2.21961 265.219 -2.21961 287.198 6.65884L491.651 89.2539C512.116 97.5191 512.116 136.479 491.651 144.747Z"
|
||||||
|
/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient
|
||||||
|
id={`cube-gradient-${index}`}
|
||||||
|
x1="185.298"
|
||||||
|
x2="185.298"
|
||||||
|
y1="-27.5515"
|
||||||
|
y2="206.448"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stopColor="#E5E7EB" />
|
||||||
|
<stop offset="1" stopColor="#9CA3AF" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export function CubeLight({ title, descriptionTitle, description, isActive, index, onHover, onLeave, onClick }: CubeProps) {
|
||||||
|
return (
|
||||||
|
<div className="relative flex flex-col items-center">
|
||||||
|
<motion.div
|
||||||
|
className="relative cursor-pointer"
|
||||||
|
onMouseEnter={onHover}
|
||||||
|
onMouseLeave={onLeave}
|
||||||
|
onClick={onClick}
|
||||||
|
style={{
|
||||||
|
zIndex: 10 - index,
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
scale: isActive ? 1.05 : 1,
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 0.3,
|
||||||
|
ease: "easeOut",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* SVG Cube */}
|
||||||
|
<CubeSvg
|
||||||
|
index={index}
|
||||||
|
className="w-48 sm:w-64 lg:w-80 h-auto drop-shadow-lg opacity-80"
|
||||||
|
style={{
|
||||||
|
filter: isActive ? 'brightness(1.1) drop-shadow(0 0 15px rgba(0, 0, 0, 0.2))' : 'brightness(1)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Title overlay */}
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<h3
|
||||||
|
className="text-black text-sm lg:text-base font-medium text-center px-4 drop-shadow-sm"
|
||||||
|
style={{
|
||||||
|
transform: 'rotate(0deg) skewX(0deg)',
|
||||||
|
transformOrigin: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Description with arrow line - Desktop */}
|
||||||
|
{isActive && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
className="hidden lg:block absolute left-full top-1/2 -translate-y-1/2 z-50"
|
||||||
|
>
|
||||||
|
{/* Arrow line */}
|
||||||
|
<svg
|
||||||
|
className="absolute left-0 top-1/2 -translate-y-1/2"
|
||||||
|
width="120"
|
||||||
|
height="2"
|
||||||
|
viewBox="0 0 120 2"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<line
|
||||||
|
x1="0"
|
||||||
|
y1="1"
|
||||||
|
x2="120"
|
||||||
|
y2="1"
|
||||||
|
stroke="black"
|
||||||
|
strokeWidth="1"
|
||||||
|
opacity="0.6"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{/* Description text */}
|
||||||
|
<div className="ml-32 w-80">
|
||||||
|
<h4 className="text-black text-base font-semibold mb-2">
|
||||||
|
{descriptionTitle}
|
||||||
|
</h4>
|
||||||
|
<p className="text-gray-800 text-sm leading-relaxed font-light">
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Description for Mobile - Below cube */}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
95
src/components/ui/StackedCubesLight.tsx
Normal file
95
src/components/ui/StackedCubesLight.tsx
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { CubeLight } from "@/components/ui/CubeLight"
|
||||||
|
|
||||||
|
const stackData = [
|
||||||
|
{
|
||||||
|
id: "agent",
|
||||||
|
title: "Agent Layer",
|
||||||
|
descriptionTitle: "Your sovereign agent with private memory and permissioned data access—always under your control.",
|
||||||
|
description:
|
||||||
|
"Choose from a wide library of open-source LLMs, paired with built-in semantic search and retrieval.\nIt coordinates across people, apps, and other agents to plan, create, and execute.\nIt operates inside a compliant legal & financial sandbox, ready for real-world transactions and operations.\nMore than just an assistant—an intelligent partner that learns and does your way.",
|
||||||
|
position: "top",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "network",
|
||||||
|
title: "Network Layer",
|
||||||
|
descriptionTitle: "A global, end-to-end encrypted overlay that simply doesn’t break.",
|
||||||
|
description:
|
||||||
|
"Shortest-path routing moves your traffic the fastest way, every time.\nInstant discovery with integrated DNS, semantic search, and indexing.\nA distributed CDN and edge delivery keep content available and tamper-resistant worldwide.\nBuilt-in tool services and secure coding sandboxes—seamless on phones, desktops, and edge.",
|
||||||
|
position: "middle",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "cloud",
|
||||||
|
title: "Cloud Layer",
|
||||||
|
descriptionTitle: "An autonomous, stateless OS that enforces pre-deterministic deployments you define.",
|
||||||
|
description:
|
||||||
|
"Workloads are cryptographically bound to your private key—location and access are yours.\nNo cloud vendor or middleman in the path: end-to-end ownership and isolation by default.\nGeo-aware placement delivers locality, compliance, and ultra-low latency where it matters.\nEncrypted, erasure-coded storage, decentralized compute and GPU on demand—including LLMs.",
|
||||||
|
position: "bottom",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function StackedCubesLight() {
|
||||||
|
const [active, setActive] = useState<string | null>("agent");
|
||||||
|
const [selectedForMobile, setSelectedForMobile] = useState<string | null>("agent");
|
||||||
|
|
||||||
|
const handleCubeClick = (id: string) => {
|
||||||
|
setSelectedForMobile(prev => (prev === id ? null : id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedMobileLayer = stackData.find(layer => layer.id === selectedForMobile);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<div
|
||||||
|
className="relative w-full flex items-center justify-center lg:justify-center min-h-[450px] lg:min-h-[400px]"
|
||||||
|
onMouseLeave={() => setActive("agent")}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
className="relative lg:pl-0 pl-6 h-[300px] lg:h-[400px] w-64 sm:w-80 lg:w-96 scale-120 lg:scale-100"
|
||||||
|
animate={{ y: ["-8px", "8px"] }}
|
||||||
|
transition={{
|
||||||
|
duration: 4,
|
||||||
|
repeat: Infinity,
|
||||||
|
repeatType: "reverse",
|
||||||
|
ease: "easeInOut",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{stackData.map((layer, index) => (
|
||||||
|
<div
|
||||||
|
key={layer.id}
|
||||||
|
className="absolute"
|
||||||
|
style={{
|
||||||
|
top: `calc(${index * 30}% - ${index * 10}px)`,
|
||||||
|
zIndex: active === layer.id ? 20 : 10 - index,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CubeLight
|
||||||
|
title={layer.title}
|
||||||
|
descriptionTitle={layer.descriptionTitle}
|
||||||
|
description={layer.description}
|
||||||
|
isActive={active === layer.id}
|
||||||
|
index={index}
|
||||||
|
onHover={() => setActive(layer.id)}
|
||||||
|
onLeave={() => {}}
|
||||||
|
onClick={() => handleCubeClick(layer.id)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
{selectedMobileLayer && (
|
||||||
|
<div className="lg:hidden w-full max-w-md p-6 -mt-8 bg-gray-200/50 rounded-lg">
|
||||||
|
<h4 className="text-black text-lg font-semibold mb-2 text-center">
|
||||||
|
{selectedMobileLayer.descriptionTitle}
|
||||||
|
</h4>
|
||||||
|
<p className="text-gray-700 text-sm leading-relaxed text-center">
|
||||||
|
{selectedMobileLayer.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
308
src/components/ui/dotted-glow-background.tsx
Normal file
308
src/components/ui/dotted-glow-background.tsx
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
type DottedGlowBackgroundProps = {
|
||||||
|
className?: string;
|
||||||
|
/** distance between dot centers in pixels */
|
||||||
|
gap?: number;
|
||||||
|
/** base radius of each dot in CSS px */
|
||||||
|
radius?: number;
|
||||||
|
/** dot color (will pulse by alpha) */
|
||||||
|
color?: string;
|
||||||
|
/** optional dot color for dark mode */
|
||||||
|
darkColor?: string;
|
||||||
|
/** shadow/glow color for bright dots */
|
||||||
|
glowColor?: string;
|
||||||
|
/** optional glow color for dark mode */
|
||||||
|
darkGlowColor?: string;
|
||||||
|
/** optional CSS variable name for light dot color (e.g. --color-zinc-900) */
|
||||||
|
colorLightVar?: string;
|
||||||
|
/** optional CSS variable name for dark dot color (e.g. --color-zinc-100) */
|
||||||
|
colorDarkVar?: string;
|
||||||
|
/** optional CSS variable name for light glow color */
|
||||||
|
glowColorLightVar?: string;
|
||||||
|
/** optional CSS variable name for dark glow color */
|
||||||
|
glowColorDarkVar?: string;
|
||||||
|
/** global opacity for the whole layer */
|
||||||
|
opacity?: number;
|
||||||
|
/** background radial fade opacity (0 = transparent background) */
|
||||||
|
backgroundOpacity?: number;
|
||||||
|
/** minimum per-dot speed in rad/s */
|
||||||
|
speedMin?: number;
|
||||||
|
/** maximum per-dot speed in rad/s */
|
||||||
|
speedMax?: number;
|
||||||
|
/** global speed multiplier for all dots */
|
||||||
|
speedScale?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canvas-based dotted background that randomly glows and dims.
|
||||||
|
* - Uses a stable grid of dots.
|
||||||
|
* - Each dot gets its own phase + speed producing organic shimmering.
|
||||||
|
* - Handles high-DPI and resizes via ResizeObserver.
|
||||||
|
*/
|
||||||
|
export function DottedGlowBackground({
|
||||||
|
className,
|
||||||
|
gap = 12,
|
||||||
|
radius = 2,
|
||||||
|
color = "rgba(0,0,0,0.7)",
|
||||||
|
darkColor,
|
||||||
|
glowColor = "rgba(0, 170, 255, 0.85)",
|
||||||
|
darkGlowColor,
|
||||||
|
colorLightVar,
|
||||||
|
colorDarkVar,
|
||||||
|
glowColorLightVar,
|
||||||
|
glowColorDarkVar,
|
||||||
|
opacity = 0.6,
|
||||||
|
backgroundOpacity = 0,
|
||||||
|
speedMin = 0.4,
|
||||||
|
speedMax = 1.3,
|
||||||
|
speedScale = 1,
|
||||||
|
}: DottedGlowBackgroundProps) {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||||
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [resolvedColor, setResolvedColor] = useState<string>(color);
|
||||||
|
const [resolvedGlowColor, setResolvedGlowColor] = useState<string>(glowColor);
|
||||||
|
|
||||||
|
// Resolve CSS variable value from the container or root
|
||||||
|
const resolveCssVariable = (
|
||||||
|
el: Element,
|
||||||
|
variableName?: string,
|
||||||
|
): string | null => {
|
||||||
|
if (!variableName) return null;
|
||||||
|
const normalized = variableName.startsWith("--")
|
||||||
|
? variableName
|
||||||
|
: `--${variableName}`;
|
||||||
|
const fromEl = getComputedStyle(el as Element)
|
||||||
|
.getPropertyValue(normalized)
|
||||||
|
.trim();
|
||||||
|
if (fromEl) return fromEl;
|
||||||
|
const root = document.documentElement;
|
||||||
|
const fromRoot = getComputedStyle(root).getPropertyValue(normalized).trim();
|
||||||
|
return fromRoot || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const detectDarkMode = (): boolean => {
|
||||||
|
const root = document.documentElement;
|
||||||
|
if (root.classList.contains("dark")) return true;
|
||||||
|
if (root.classList.contains("light")) return false;
|
||||||
|
return (
|
||||||
|
window.matchMedia &&
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep resolved colors in sync with theme changes and prop updates
|
||||||
|
useEffect(() => {
|
||||||
|
const container = containerRef.current ?? document.documentElement;
|
||||||
|
|
||||||
|
const compute = () => {
|
||||||
|
const isDark = detectDarkMode();
|
||||||
|
|
||||||
|
let nextColor: string = color;
|
||||||
|
let nextGlow: string = glowColor;
|
||||||
|
|
||||||
|
if (isDark) {
|
||||||
|
const varDot = resolveCssVariable(container, colorDarkVar);
|
||||||
|
const varGlow = resolveCssVariable(container, glowColorDarkVar);
|
||||||
|
nextColor = varDot || darkColor || nextColor;
|
||||||
|
nextGlow = varGlow || darkGlowColor || nextGlow;
|
||||||
|
} else {
|
||||||
|
const varDot = resolveCssVariable(container, colorLightVar);
|
||||||
|
const varGlow = resolveCssVariable(container, glowColorLightVar);
|
||||||
|
nextColor = varDot || nextColor;
|
||||||
|
nextGlow = varGlow || nextGlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
setResolvedColor(nextColor);
|
||||||
|
setResolvedGlowColor(nextGlow);
|
||||||
|
};
|
||||||
|
|
||||||
|
compute();
|
||||||
|
|
||||||
|
const mql = window.matchMedia
|
||||||
|
? window.matchMedia("(prefers-color-scheme: dark)")
|
||||||
|
: null;
|
||||||
|
const handleMql = () => compute();
|
||||||
|
mql?.addEventListener?.("change", handleMql);
|
||||||
|
|
||||||
|
const mo = new MutationObserver(() => compute());
|
||||||
|
mo.observe(document.documentElement, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ["class", "style"],
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mql?.removeEventListener?.("change", handleMql);
|
||||||
|
mo.disconnect();
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
color,
|
||||||
|
darkColor,
|
||||||
|
glowColor,
|
||||||
|
darkGlowColor,
|
||||||
|
colorLightVar,
|
||||||
|
colorDarkVar,
|
||||||
|
glowColorLightVar,
|
||||||
|
glowColorDarkVar,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = canvasRef.current;
|
||||||
|
const container = containerRef.current;
|
||||||
|
if (!el || !container) return;
|
||||||
|
|
||||||
|
const ctx = el.getContext("2d");
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
let raf = 0;
|
||||||
|
let stopped = false;
|
||||||
|
|
||||||
|
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
||||||
|
|
||||||
|
const resize = () => {
|
||||||
|
const { width, height } = container.getBoundingClientRect();
|
||||||
|
el.width = Math.max(1, Math.floor(width * dpr));
|
||||||
|
el.height = Math.max(1, Math.floor(height * dpr));
|
||||||
|
el.style.width = `${Math.floor(width)}px`;
|
||||||
|
el.style.height = `${Math.floor(height)}px`;
|
||||||
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ro = new ResizeObserver(resize);
|
||||||
|
ro.observe(container);
|
||||||
|
resize();
|
||||||
|
|
||||||
|
// Precompute dot metadata for a medium-sized grid and regenerate on resize
|
||||||
|
let dots: { x: number; y: number; phase: number; speed: number }[] = [];
|
||||||
|
|
||||||
|
const regenDots = () => {
|
||||||
|
dots = [];
|
||||||
|
const { width, height } = container.getBoundingClientRect();
|
||||||
|
const cols = Math.ceil(width / gap) + 2;
|
||||||
|
const rows = Math.ceil(height / gap) + 2;
|
||||||
|
const min = Math.min(speedMin, speedMax);
|
||||||
|
const max = Math.max(speedMin, speedMax);
|
||||||
|
for (let i = -1; i < cols; i++) {
|
||||||
|
for (let j = -1; j < rows; j++) {
|
||||||
|
const x = i * gap + (j % 2 === 0 ? 0 : gap * 0.5); // offset every other row
|
||||||
|
const y = j * gap;
|
||||||
|
// Randomize phase and speed slightly per dot
|
||||||
|
const phase = Math.random() * Math.PI * 2;
|
||||||
|
const span = Math.max(max - min, 0);
|
||||||
|
const speed = min + Math.random() * span; // configurable rad/s
|
||||||
|
dots.push({ x, y, phase, speed });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const regenThrottled = () => {
|
||||||
|
regenDots();
|
||||||
|
};
|
||||||
|
|
||||||
|
regenDots();
|
||||||
|
|
||||||
|
let last = performance.now();
|
||||||
|
|
||||||
|
const draw = (now: number) => {
|
||||||
|
if (stopped) return;
|
||||||
|
const dt = (now - last) / 1000; // seconds
|
||||||
|
last = now;
|
||||||
|
const { width, height } = container.getBoundingClientRect();
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, el.width, el.height);
|
||||||
|
ctx.globalAlpha = opacity;
|
||||||
|
|
||||||
|
// optional subtle background fade for depth (defaults to 0 = transparent)
|
||||||
|
if (backgroundOpacity > 0) {
|
||||||
|
const grad = ctx.createRadialGradient(
|
||||||
|
width * 0.5,
|
||||||
|
height * 0.4,
|
||||||
|
Math.min(width, height) * 0.1,
|
||||||
|
width * 0.5,
|
||||||
|
height * 0.5,
|
||||||
|
Math.max(width, height) * 0.7,
|
||||||
|
);
|
||||||
|
grad.addColorStop(0, "rgba(0,0,0,0)");
|
||||||
|
grad.addColorStop(
|
||||||
|
1,
|
||||||
|
`rgba(0,0,0,${Math.min(Math.max(backgroundOpacity, 0), 1)})`,
|
||||||
|
);
|
||||||
|
ctx.fillStyle = grad as unknown as CanvasGradient;
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// animate dots
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = resolvedColor;
|
||||||
|
|
||||||
|
const time = (now / 1000) * Math.max(speedScale, 0);
|
||||||
|
for (let i = 0; i < dots.length; i++) {
|
||||||
|
const d = dots[i];
|
||||||
|
// Linear triangle wave 0..1..0 for linear glow/dim
|
||||||
|
const mod = (time * d.speed + d.phase) % 2;
|
||||||
|
const lin = mod < 1 ? mod : 2 - mod; // 0..1..0
|
||||||
|
const a = 0.25 + 0.55 * lin; // 0.25..0.8 linearly
|
||||||
|
|
||||||
|
// draw glow when bright
|
||||||
|
if (a > 0.6) {
|
||||||
|
const glow = (a - 0.6) / 0.4; // 0..1
|
||||||
|
ctx.shadowColor = resolvedGlowColor;
|
||||||
|
ctx.shadowBlur = 6 * glow;
|
||||||
|
} else {
|
||||||
|
ctx.shadowColor = "transparent";
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.globalAlpha = a * opacity;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(d.x, d.y, radius, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
raf = requestAnimationFrame(draw);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResize = () => {
|
||||||
|
resize();
|
||||||
|
regenThrottled();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
raf = requestAnimationFrame(draw);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
stopped = true;
|
||||||
|
cancelAnimationFrame(raf);
|
||||||
|
window.removeEventListener("resize", handleResize);
|
||||||
|
ro.disconnect();
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
gap,
|
||||||
|
radius,
|
||||||
|
resolvedColor,
|
||||||
|
resolvedGlowColor,
|
||||||
|
opacity,
|
||||||
|
backgroundOpacity,
|
||||||
|
speedMin,
|
||||||
|
speedMax,
|
||||||
|
speedScale,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className={className}
|
||||||
|
style={{ position: "absolute", inset: 0 }}
|
||||||
|
>
|
||||||
|
<canvas
|
||||||
|
ref={canvasRef}
|
||||||
|
style={{ display: "block", width: "100%", height: "100%" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DottedGlowBackground;
|
||||||
@@ -20,8 +20,9 @@ const GLOBE_CONFIG: COBEOptions = {
|
|||||||
mapSamples: 16000,
|
mapSamples: 16000,
|
||||||
mapBrightness: 1.1,
|
mapBrightness: 1.1,
|
||||||
baseColor: [0.8, 0.8, 0.8], // sleek dark gray globe
|
baseColor: [0.8, 0.8, 0.8], // sleek dark gray globe
|
||||||
markerColor: [0.3, 0.6, 1], // soft, elegant blue
|
markerColor: [0.02, 0.71, 0.83], // cyan-500
|
||||||
glowColor: [0.8, 0.8, 0.85], // subtle glow
|
glowColor: [0.8, 0.8, 0.85], // grey
|
||||||
|
|
||||||
markers: [
|
markers: [
|
||||||
// --- Core Global Markers ---
|
// --- Core Global Markers ---
|
||||||
{ location: [14.5995, 120.9842], size: 0.03 }, // Manila
|
{ location: [14.5995, 120.9842], size: 0.03 }, // Manila
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
--color-gray-900: oklch(0.205 0 0);
|
--color-gray-900: oklch(0.205 0 0);
|
||||||
--color-gray-950: oklch(0.145 0 0);
|
--color-gray-950: oklch(0.145 0 0);
|
||||||
|
|
||||||
--font-sans: var(--font-mulish);
|
--font-sans: var(--font-inter);
|
||||||
|
|
||||||
--container-2xl: 40rem;
|
--container-2xl: 40rem;
|
||||||
|
|
||||||
|
|||||||
@@ -24,5 +24,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"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" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user