20 Commits

Author SHA1 Message Date
Emre
45b1c43b97 improvements 2025-10-24 15:11:28 +03:00
Emre
26fbea3ec4 fixed build errors 2025-10-24 04:17:02 +03:00
Emre
3a7aa82ff7 improvements 2025-10-24 02:17:33 +03:00
644bb34419 style: reduce header padding from 8 to 4 units for more compact layout 2025-10-23 16:38:25 +02:00
9b3c620706 refactor: simplify header component and improve hero section layouts 2025-10-23 16:36:37 +02:00
987b46588b style: update header background to be more opaque with gray border 2025-10-23 16:04:04 +02:00
df88c4a14f fix: update image import path to use direct import instead of URL path 2025-10-23 16:01:43 +02:00
dfb469e7f7 Merge branch 'main' into development 2025-10-23 16:01:30 +02:00
a3cd28fc6b style: increase hover background opacity on nav links from 10% to 50% 2025-10-23 14:57:34 +02:00
758a004ce9 refactor: update homepage layout and enhance feature card styling with improved shadows 2025-10-23 14:56:45 +02:00
73832e2686 feat: enhance globe component with interactive controls and global markers 2025-10-23 14:37:13 +02:00
4cc98af570 chore: remove Next.js dependencies and update UI components with standard React 2025-10-23 14:18:16 +02:00
8f8d45c7ff refactor: reduce text flip animation duration and adjust padding 2025-10-23 04:27:55 +02:00
f2cb22ef66 feat: add animated text flip and pointer highlight components with home agent section 2025-10-23 04:24:01 +02:00
82e730346e feat: update homepage UI with new cloud section and simplified gradient backgrounds 2025-10-23 02:25:42 +02:00
c9129c5c66 feat: replace card stack with new feature grid and add product icons 2025-10-23 01:32:54 +02:00
8f1df965f2 feat: add UI components for card stack, world map and evervault card with theme support 2025-10-22 23:29:14 +02:00
af77948679 feat: implement floating navbar with aurora background and updated header styling 2025-10-22 22:25:54 +02:00
ce33e0f7c3 feat: add cloud video background and update StackSection styling to dark theme 2025-10-22 18:46:21 +02:00
1f0f18105e style: update hero section with new cloud background and button layout 2025-10-22 18:35:05 +02:00
85 changed files with 4199 additions and 4139 deletions

25
components.json Normal file
View File

@@ -0,0 +1,25 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/styles/tailwind.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {
"@aceternity": "https://ui.aceternity.com/registry/{name}.json",
"@magicui": "https://magicui.design/r/{name}.json"
}
}

View File

@@ -4,16 +4,10 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" /> <link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mycelium - Unleash the Power of Decentralized Networks</title> <title>Project Mycelium - Unleash the Power of Decentralized Networks</title>
<meta name="description" content="Discover Mycelium, an end-to-end encrypted IPv6 overlay network. The future of secure, efficient, and scalable networking." /> <meta name="description" content="Project Mycelium's technology enables anyone to deploy their own Internet infrastructure, anywhere." />
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<style>
:root {
--font-inter: 'Inter', sans-serif;
}
</style>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

4410
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,9 +10,12 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@emailjs/browser": "^4.4.1",
"@headlessui/react": "^2.2.9", "@headlessui/react": "^2.2.9",
"@heroicons/react": "^2.2.0", "@heroicons/react": "^2.2.0",
"@lobehub/icons": "^1.97.2", "@lobehub/icons": "^1.97.2",
"@react-three/drei": "^9.89.2",
"@react-three/fiber": "^8.15.12",
"@tabler/icons-react": "^3.35.0", "@tabler/icons-react": "^3.35.0",
"@tailwindcss/forms": "^0.5.10", "@tailwindcss/forms": "^0.5.10",
"@types/node": "^20.19.23", "@types/node": "^20.19.23",
@@ -22,10 +25,10 @@
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cobe": "^0.6.5", "cobe": "^0.6.5",
"dotted-map": "^2.2.3",
"framer-motion": "^10.18.0", "framer-motion": "^10.18.0",
"lucide-react": "^0.544.0", "lucide-react": "^0.544.0",
"motion": "^12.23.24", "motion": "^12.23.24",
"next": "^14.2.33",
"popmotion": "^11.0.5", "popmotion": "^11.0.5",
"react": "^18.3.1", "react": "^18.3.1",
"react-countup": "^6.5.3", "react-countup": "^6.5.3",
@@ -35,15 +38,16 @@
"react-type-animation": "^3.2.0", "react-type-animation": "^3.2.0",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.15", "tailwindcss": "^4.1.15",
"three": "^0.151.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"use-debounce": "^10.0.6" "use-debounce": "^10.0.6"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4.1.15", "@tailwindcss/postcss": "^4.1.15",
"@types/three": "^0.151.0",
"@vitejs/plugin-react": "^5.0.4", "@vitejs/plugin-react": "^5.0.4",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^8.57.1", "eslint": "^8.57.1",
"eslint-config-next": "^14.2.33",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.6.14", "prettier-plugin-tailwindcss": "^0.6.14",
"sharp": "^0.33.1", "sharp": "^0.33.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 KiB

BIN
public/images/cloud.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
public/images/cloud1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 KiB

BIN
public/images/cloudimg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1024 KiB

BIN
public/images/mchip.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/images/mchip2.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 KiB

View File

@@ -214,7 +214,7 @@
</filter> </filter>
<radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" <radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0 727 -642 0 184 1)"> gradientTransform="matrix(0 727 -642 0 184 1)">
<stop stop-color="#FAFAFA" /> <stop stop-color="#FFFFFF" />
<stop offset="1" stop-color="#E6E6E6" /> <stop offset="1" stop-color="#E6E6E6" />
</radialGradient> </radialGradient>
<radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" <radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/videos/chip_vid.mp4 Normal file

Binary file not shown.

BIN
public/videos/cloud.mp4 Normal file

Binary file not shown.

View File

@@ -4,6 +4,7 @@ import HomePage from './pages/home/HomePage'
import CloudPage from './pages/cloud/CloudPage' import CloudPage from './pages/cloud/CloudPage'
import NetworkPage from './pages/network/NetworkPage' import NetworkPage from './pages/network/NetworkPage'
import AgentsPage from './pages/agents/AgentsPage' import AgentsPage from './pages/agents/AgentsPage'
import DownloadPage from './pages/download/DownloadPage'
function App() { function App() {
return ( return (
@@ -14,6 +15,7 @@ function App() {
<Route path="cloud" element={<CloudPage />} /> <Route path="cloud" element={<CloudPage />} />
<Route path="network" element={<NetworkPage />} /> <Route path="network" element={<NetworkPage />} />
<Route path="agents" element={<AgentsPage />} /> <Route path="agents" element={<AgentsPage />} />
<Route path="download" element={<DownloadPage />} />
</Route> </Route>
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>

View File

@@ -1,19 +1,20 @@
import { useRef } from 'react' import { motion } from 'framer-motion'
import { motion, useInView } from 'framer-motion'
export function AnimatedSection({ children }: { children: React.ReactNode }) { type AnimatedSectionProps = {
const ref = useRef(null) children: React.ReactNode
const isInView = useInView(ref, { once: true, margin: '-20% 0px -20% 0px' }) id?: string
className?: string
}
export function AnimatedSection({ children, id, className }: AnimatedSectionProps) {
return ( return (
<motion.section <motion.section
ref={ref} id={id}
initial={{ opacity: 0, y: 50 }} className={className}
animate={{ initial={{ opacity: 0, y: 40 }}
opacity: isInView ? 1 : 0, whileInView={{ opacity: 1, y: 0 }}
y: isInView ? 0 : 50, viewport={{ once: true, margin: '-25% 0px -20% 0px' }}
}} transition={{ duration: 0.5, ease: 'easeOut' }}
transition={{ duration: 0.5 }}
> >
{children} {children}
</motion.section> </motion.section>

View File

@@ -3,9 +3,9 @@ import clsx from 'clsx'
const baseStyles = { const baseStyles = {
solid: solid:
'inline-flex justify-center rounded-lg py-2 px-3 text-sm font-semibold transition-colors', 'inline-flex justify-center rounded-full py-2 px-4 text-sm font-semibold transition-colors',
outline: outline:
'inline-flex justify-center rounded-lg border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(3)-1px)] text-sm transition-colors', 'inline-flex justify-center rounded-full border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(4)-1px)] text-sm transition-colors',
} }
const variantStyles = { const variantStyles = {

View File

@@ -0,0 +1,220 @@
'use client'
import { AnimatePresence, motion } from 'framer-motion'
import { useState, type ChangeEvent, type FormEvent } from 'react'
import emailjs from '@emailjs/browser'
import { CheckCircle, Send, X } from 'lucide-react'
interface ContactFormProps {
isOpen: boolean
onClose: () => void
title?: string
formType?: 'investor' | 'partner' | 'agent_waitlist'
}
const initialFormState = {
name: '',
email: '',
company: '',
message: '',
}
export default function ContactForm({
isOpen,
onClose,
title = 'Book a Meeting',
formType,
}: ContactFormProps) {
const [formData, setFormData] = useState(initialFormState)
const [isSubmitting, setIsSubmitting] = useState(false)
const [isSubmitted, setIsSubmitted] = useState(false)
const handleInputChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target
setFormData((prev) => ({
...prev,
[name]: value,
}))
}
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
setIsSubmitting(true)
try {
const templateParams = {
from_name: formData.name,
from_email: formData.email,
company: formData.company,
message: formData.message,
to_email: 'emre@incubaid.com',
form_type: formType || 'General Inquiry',
}
await emailjs.send(
'service_03d0vf8',
'template_6o6e8oe',
templateParams,
'bhkly3gzrO-SA9w7v',
)
setIsSubmitted(true)
setTimeout(() => {
setIsSubmitted(false)
setFormData(initialFormState)
onClose()
}, 3000)
} catch (error) {
console.error('Email sending failed:', error)
alert('Failed to send message. Please try again.')
} finally {
setIsSubmitting(false)
}
}
return (
<AnimatePresence>
{isOpen && (
<motion.div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.div
className="relative w-full max-w-md overflow-hidden rounded-2xl bg-card shadow-2xl"
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0 }}
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
>
<div className="flex items-center justify-between border-b border-border p-6">
<h3 className="text-xl font-bold text-foreground">{title}</h3>
<button
onClick={onClose}
className="rounded-lg p-2 transition-colors hover:bg-muted"
aria-label="Close form"
>
<X className="h-5 w-5 text-muted-foreground" />
</button>
</div>
<div className="p-6">
{isSubmitted ? (
<motion.div
className="py-8 text-center"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
<CheckCircle className="mx-auto mb-4 h-16 w-16 text-primary" />
<h4 className="mb-2 text-lg font-semibold text-foreground">Thank you!</h4>
<p className="text-muted-foreground">We&apos;ll get back to you soon.</p>
</motion.div>
) : (
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label
htmlFor="name"
className="mb-1 block text-sm font-medium text-muted-foreground"
>
Full Name *
</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleInputChange}
required
className="w-full rounded-lg border border-border bg-muted/30 px-4 py-3 text-foreground placeholder:text-muted-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary"
placeholder="Your full name"
/>
</div>
<div>
<label
htmlFor="email"
className="mb-1 block text-sm font-medium text-muted-foreground"
>
Email Address *
</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleInputChange}
required
className="w-full rounded-lg border border-border bg-muted/30 px-4 py-3 text-foreground placeholder:text-muted-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary"
placeholder="your.email@company.com"
/>
</div>
<div>
<label
htmlFor="company"
className="mb-1 block text-sm font-medium text-muted-foreground"
>
Company
</label>
<input
type="text"
id="company"
name="company"
value={formData.company}
onChange={handleInputChange}
className="w-full rounded-lg border border-border bg-muted/30 px-4 py-3 text-foreground placeholder:text-muted-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary"
placeholder="Your company name"
/>
</div>
<div>
<label
htmlFor="message"
className="mb-1 block text-sm font-medium text-muted-foreground"
>
Message
</label>
<textarea
id="message"
name="message"
value={formData.message}
onChange={handleInputChange}
rows={4}
className="w-full resize-none rounded-lg border border-border bg-muted/30 px-4 py-3 text-foreground placeholder:text-muted-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary"
placeholder={
formType === 'investor'
? 'Tell us about your investment interests and how we can collaborate.'
: formType === 'agent_waitlist'
? 'Tell us about your sovereign agent requirements.'
: 'Tell us about your project or how we can help.'
}
/>
</div>
<button
type="submit"
disabled={isSubmitting}
className="flex w-full items-center justify-center rounded-lg bg-primary px-6 py-3 font-semibold text-primary-foreground transition-opacity disabled:cursor-not-allowed disabled:opacity-50"
>
{isSubmitting ? (
<>
<div className="mr-2 h-5 w-5 animate-spin rounded-full border-2 border-primary-foreground/30 border-t-primary-foreground" />
Sending...
</>
) : (
<>
<Send className="h-5 w-5" />
<span className="ml-2">Send Message</span>
</>
)}
</button>
</form>
)}
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
)
}

View File

@@ -18,11 +18,10 @@ export function FadeIn({ children, transition, className }: FadeInProps) {
className={className} className={className}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false, margin: isMobile ? '0px 0px -50px 0px' : '0px 0px -100px 0px' }} viewport={{ once: true, amount: isMobile ? 0.2 : 0.3 }}
transition={transition || { duration: 0.5 }} transition={transition || { duration: 0.5, ease: 'easeOut' }}
> >
{children} {children}
</motion.div> </motion.div>
) )
} }

View File

@@ -35,10 +35,10 @@ export function Footer() {
</div> </div>
<div className="ml-4 lg:w-72"> <div className="ml-4 lg:w-72">
<p className="text-base font-semibold text-gray-900"> <p className="text-base font-semibold text-gray-900">
<a href="https://github.com/threefoldtech/mycelium/releases/" target="_blank" rel="noopener noreferrer"> <Link to="/download">
<span className="absolute inset-0 sm:rounded-2xl" /> <span className="absolute inset-0 sm:rounded-2xl" />
Download Mycelium Download Mycelium Connector
</a> </Link>
</p> </p>
<p className="mt-1 text-sm text-gray-700"> <p className="mt-1 text-sm text-gray-700">
Head to the GitHub to access the latest Mycelium builds for your devices. Head to the GitHub to access the latest Mycelium builds for your devices.

View File

@@ -1,23 +1,18 @@
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Container } from './Container' import { Container } from './Container'
import { Button } from './Button' import { Button } from './Button'
import pmyceliumLogo from '../images/logos/pmyceliumlogo.png'
export function Header() { export function Header() {
return ( return (
<header> <header>
<nav> <nav>
<Container className="relative z-50 flex justify-between py-8"> <Container className="relative z-50 flex justify-between py-4">
<div className="relative z-10 flex items-center gap-16"> <div className="relative z-10 flex items-center gap-16">
<Link to="/" aria-label="Home"> <Link to="/" aria-label="Home">
<img src="/src/images/logomark.svg" alt="Mycelium" className="h-10 w-auto" /> <img src={pmyceliumLogo} alt="Mycelium" className="h-8 w-auto" />
</Link> </Link>
<div className="hidden lg:flex lg:gap-10"> <div className="hidden lg:flex lg:gap-10">
<Link
to="/"
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
>
Home
</Link>
<Link <Link
to="/cloud" to="/cloud"
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors" className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
@@ -41,16 +36,16 @@ export function Header() {
<div className="flex items-center gap-6"> <div className="flex items-center gap-6">
<div className="flex items-center gap-6 max-lg:hidden"> <div className="flex items-center gap-6 max-lg:hidden">
<Button <Button
to="https://threefold.info/mycelium_network/docs/" to="https://myceliumcloud.tf"
variant="outline" variant="outline"
as="a" as="a"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
Docs Start Deployment
</Button> </Button>
<Button to="/download" variant="solid" color="cyan"> <Button to="/download" variant="solid" color="cyan">
Get Mycelium Get Mycelium Connector
</Button> </Button>
</div> </div>
</div> </div>

View File

@@ -1,12 +1,13 @@
import { Outlet } from 'react-router-dom' import { Outlet } from 'react-router-dom'
import { Header } from './Header'
import { Footer } from './Footer' import { Footer } from './Footer'
import { Header } from './Header'
export function Layout() { export function Layout() {
return ( return (
<div className="bg-gray-50 antialiased" style={{ fontFamily: 'var(--font-inter)' }}> <div className="bg-white antialiased" style={{ fontFamily: 'var(--font-inter)' }}>
<Header /> <Header />
<main> <main className="">
<Outlet /> <Outlet />
</main> </main>
<Footer /> <Footer />

View File

@@ -0,0 +1,22 @@
'use client'
import { ChevronDown } from 'lucide-react'
export function ScrollDownArrow() {
const scrollToNextSection = () => {
const nextSection = document.querySelector('#next-section') // Assuming the next section has this id
if (nextSection) {
nextSection.scrollIntoView({ behavior: 'smooth' })
}
}
return (
<button
onClick={scrollToNextSection}
className="animate-bounce text-gray-500 hover:text-gray-700"
aria-label="Scroll to next section"
>
<ChevronDown size={32} />
</button>
)
}

View File

@@ -3,6 +3,11 @@
import React from 'react' import React from 'react'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
const fontVariants = {
sans: 'font-sans',
neuton: 'font-neuton',
} as const
const colorVariants = { const colorVariants = {
primary: 'text-gray-900', primary: 'text-gray-900',
secondary: 'text-gray-600', secondary: 'text-gray-600',
@@ -15,8 +20,10 @@ const colorVariants = {
} as const } as const
type TextOwnProps = { type TextOwnProps = {
font?: keyof typeof fontVariants
color?: keyof typeof colorVariants color?: keyof typeof colorVariants
className?: string className?: string
children?: React.ReactNode
} }
// Polymorphic helpers // Polymorphic helpers
@@ -34,6 +41,7 @@ const createTextComponent = <DefaultElement extends React.ElementType>(
> >
function Text<E extends React.ElementType = DefaultElement>({ function Text<E extends React.ElementType = DefaultElement>({
font = 'sans',
as, as,
color = 'primary', color = 'primary',
className, className,
@@ -43,7 +51,12 @@ const createTextComponent = <DefaultElement extends React.ElementType>(
const Tag = (as || defaultElement) as React.ElementType const Tag = (as || defaultElement) as React.ElementType
return ( return (
<Tag <Tag
className={cn(defaultClassName, colorVariants[color], className)} className={cn(
defaultClassName,
fontVariants[font],
colorVariants[color],
className
)}
{...props} {...props}
> >
{children} {children}
@@ -87,7 +100,7 @@ export const Subtle = createTextComponent(
) )
export const H5 = createTextComponent( export const H5 = createTextComponent(
'h5', 'h5',
'text-xl lg:text-2xl font-semibold leading-snug tracking-tight' 'text-xl lg:text-2xl font-light leading-snug tracking-normal'
) )
export const Eyebrow = createTextComponent( export const Eyebrow = createTextComponent(
'h2', 'h2',

View File

@@ -0,0 +1,138 @@
import { useId } from "react";
export default function FeaturesSectionDemo() {
return (
<div className="py-20 lg:py-40">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-10 md:gap-2 max-w-7xl mx-auto">
{grid.map((feature) => (
<div
key={feature.title}
className="relative bg-gradient-to-b dark:from-neutral-900 from-neutral-100 dark:to-neutral-950 to-white p-6 rounded-3xl overflow-hidden"
>
<Grid size={20} />
<p className="text-base font-bold text-neutral-800 dark:text-white relative z-20">
{feature.title}
</p>
<p className="text-neutral-600 dark:text-neutral-400 mt-4 text-base font-normal relative z-20">
{feature.description}
</p>
</div>
))}
</div>
</div>
);
}
const grid = [
{
title: "HIPAA and SOC2 Compliant",
description:
"Our applications are HIPAA and SOC2 compliant, your data is safe with us, always.",
},
{
title: "Automated Social Media Posting",
description:
"Schedule and automate your social media posts across multiple platforms to save time and maintain a consistent online presence.",
},
{
title: "Advanced Analytics",
description:
"Gain insights into your social media performance with detailed analytics and reporting tools to measure engagement and ROI.",
},
{
title: "Content Calendar",
description:
"Plan and organize your social media content with an intuitive calendar view, ensuring you never miss a post.",
},
{
title: "Audience Targeting",
description:
"Reach the right audience with advanced targeting options, including demographics, interests, and behaviors.",
},
{
title: "Social Listening",
description:
"Monitor social media conversations and trends to stay informed about what your audience is saying and respond in real-time.",
},
{
title: "Customizable Templates",
description:
"Create stunning social media posts with our customizable templates, designed to fit your brand's unique style and voice.",
},
{
title: "Collaboration Tools",
description:
"Work seamlessly with your team using our collaboration tools, allowing you to assign tasks, share drafts, and provide feedback in real-time.",
},
];
export const Grid = ({
pattern,
size,
}: {
pattern?: number[][];
size?: number;
}) => {
const p = pattern ?? [
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
[Math.floor(Math.random() * 4) + 7, Math.floor(Math.random() * 6) + 1],
];
return (
<div className="pointer-events-none absolute left-1/2 top-0 -ml-20 -mt-2 h-full w-full [mask-image:linear-gradient(white,transparent)]">
<div className="absolute inset-0 bg-gradient-to-r [mask-image:radial-gradient(farthest-side_at_top,white,transparent)] dark:from-zinc-900/30 from-zinc-100/30 to-zinc-300/30 dark:to-zinc-900/30 opacity-100">
<GridPattern
width={size ?? 20}
height={size ?? 20}
x="-12"
y="4"
squares={p}
className="absolute inset-0 h-full w-full mix-blend-overlay dark:fill-white/10 dark:stroke-white/10 stroke-black/10 fill-black/10"
/>
</div>
</div>
);
};
export function GridPattern({ width, height, x, y, squares, ...props }: any) {
const patternId = useId();
return (
<svg aria-hidden="true" {...props}>
<defs>
<pattern
id={patternId}
width={width}
height={height}
patternUnits="userSpaceOnUse"
x={x}
y={y}
>
<path d={`M.5 ${height}V.5H${width}`} fill="none" />
</pattern>
</defs>
<rect
width="100%"
height="100%"
strokeWidth={0}
fill={`url(#${patternId})`}
/>
{squares && (
<svg x={x} y={y} className="overflow-visible">
{squares.map(([x, y]: any) => (
<rect
strokeWidth="0"
key={`${x}-${y}`}
width={width + 1}
height={height + 1}
x={x * width}
y={y * height}
/>
))}
</svg>
)}
</svg>
);
}

View File

@@ -0,0 +1,109 @@
import { cn } from "@/lib/utils";
import {
IconAdjustmentsBolt,
IconCloud,
IconCurrencyDollar,
IconEaseInOut,
IconHeart,
IconHelp,
IconRouteAltLeft,
IconTerminal2,
} from "@tabler/icons-react";
export default function FeaturesSectionDemo() {
const features = [
{
title: "Built for developers",
description:
"Built for engineers, developers, dreamers, thinkers and doers.",
icon: <IconTerminal2 />,
},
{
title: "Ease of use",
description:
"It's as easy as using an Apple, and as expensive as buying one.",
icon: <IconEaseInOut />,
},
{
title: "Pricing like no other",
description:
"Our prices are best in the market. No cap, no lock, no credit card required.",
icon: <IconCurrencyDollar />,
},
{
title: "100% Uptime guarantee",
description: "We just cannot be taken down by anyone.",
icon: <IconCloud />,
},
{
title: "Multi-tenant Architecture",
description: "You can simply share passwords instead of buying new seats",
icon: <IconRouteAltLeft />,
},
{
title: "24/7 Customer Support",
description:
"We are available a 100% of the time. Atleast our AI Agents are.",
icon: <IconHelp />,
},
{
title: "Money back guarantee",
description:
"If you donot like EveryAI, we will convince you to like us.",
icon: <IconAdjustmentsBolt />,
},
{
title: "And everything else",
description: "I just ran out of copy ideas. Accept my sincere apologies",
icon: <IconHeart />,
},
];
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 relative z-10 py-10 max-w-7xl mx-auto">
{features.map((feature, index) => (
<Feature key={feature.title} {...feature} index={index} />
))}
</div>
);
}
const Feature = ({
title,
description,
icon,
index,
}: {
title: string;
description: string;
icon: React.ReactNode;
index: number;
}) => {
return (
<div
className={cn(
"flex flex-col lg:border-r py-10 relative group/feature dark:border-neutral-800",
(index === 0 || index === 4) && "lg:border-l dark:border-neutral-800",
index < 4 && "lg:border-b dark:border-neutral-800"
)}
>
{index < 4 && (
<div className="opacity-0 group-hover/feature:opacity-100 transition duration-200 absolute inset-0 h-full w-full bg-gradient-to-t from-neutral-100 dark:from-neutral-800 to-transparent pointer-events-none" />
)}
{index >= 4 && (
<div className="opacity-0 group-hover/feature:opacity-100 transition duration-200 absolute inset-0 h-full w-full bg-gradient-to-b from-neutral-100 dark:from-neutral-800 to-transparent pointer-events-none" />
)}
<div className="mb-4 relative z-10 px-10 text-neutral-600 dark:text-neutral-400">
{icon}
</div>
<div className="text-lg font-bold mb-2 relative z-10 px-10">
<div className="absolute left-0 inset-y-0 h-6 group-hover/feature:h-8 w-1 rounded-tr-full rounded-br-full bg-neutral-300 dark:bg-neutral-700 group-hover/feature:bg-blue-500 transition-all duration-200 origin-center" />
<span className="group-hover/feature:translate-x-2 transition duration-200 inline-block text-neutral-800 dark:text-neutral-100">
{title}
</span>
</div>
<p className="text-sm text-neutral-600 dark:text-neutral-300 max-w-xs relative z-10 px-10">
{description}
</p>
</div>
);
};

View File

@@ -0,0 +1,281 @@
import React from "react";
import { cn } from "@/lib/utils";
import createGlobe from "cobe";
import { useEffect, useRef } from "react";
import { motion } from "motion/react";
import { IconBrandYoutubeFilled } from "@tabler/icons-react";
export default function FeaturesSectionDemo() {
const features = [
{
title: "Track issues effectively",
description:
"Track and manage your project issues with ease using our intuitive interface.",
skeleton: <SkeletonOne />,
className:
"col-span-1 lg:col-span-4 border-b lg:border-r dark:border-neutral-800",
},
{
title: "Capture pictures with AI",
description:
"Capture stunning photos effortlessly using our advanced AI technology.",
skeleton: <SkeletonTwo />,
className: "border-b col-span-1 lg:col-span-2 dark:border-neutral-800",
},
{
title: "Watch our AI on YouTube",
description:
"Whether its you or Tyler Durden, you can get to know about our product on YouTube",
skeleton: <SkeletonThree />,
className:
"col-span-1 lg:col-span-3 lg:border-r dark:border-neutral-800",
},
{
title: "Deploy in seconds",
description:
"With our blazing fast, state of the art, cutting edge, we are so back cloud servies (read AWS) - you can deploy your model in seconds.",
skeleton: <SkeletonFour />,
className: "col-span-1 lg:col-span-3 border-b lg:border-none",
},
];
return (
<div className="relative z-20 py-10 lg:py-40 max-w-7xl mx-auto">
<div className="px-8">
<h4 className="text-3xl lg:text-5xl lg:leading-tight max-w-5xl mx-auto text-center tracking-tight font-medium text-black dark:text-white">
Packed with thousands of features
</h4>
<p className="text-sm lg:text-base max-w-2xl my-4 mx-auto text-neutral-500 text-center font-normal dark:text-neutral-300">
From Image generation to video generation, Everything AI has APIs for
literally everything. It can even create this website copy for you.
</p>
</div>
<div className="relative ">
<div className="grid grid-cols-1 lg:grid-cols-6 mt-12 xl:border rounded-md dark:border-neutral-800">
{features.map((feature) => (
<FeatureCard key={feature.title} className={feature.className}>
<FeatureTitle>{feature.title}</FeatureTitle>
<FeatureDescription>{feature.description}</FeatureDescription>
<div className=" h-full w-full">{feature.skeleton}</div>
</FeatureCard>
))}
</div>
</div>
</div>
);
}
const FeatureCard = ({
children,
className,
}: {
children?: React.ReactNode;
className?: string;
}) => {
return (
<div className={cn(`p-4 sm:p-8 relative overflow-hidden`, className)}>
{children}
</div>
);
};
const FeatureTitle = ({ children }: { children?: React.ReactNode }) => {
return (
<p className=" max-w-5xl mx-auto text-left tracking-tight text-black dark:text-white text-xl md:text-2xl md:leading-snug">
{children}
</p>
);
};
const FeatureDescription = ({ children }: { children?: React.ReactNode }) => {
return (
<p
className={cn(
"text-sm md:text-base max-w-4xl text-left mx-auto",
"text-neutral-500 text-center font-normal dark:text-neutral-300",
"text-left max-w-sm mx-0 md:text-sm my-2"
)}
>
{children}
</p>
);
};
export const SkeletonOne = () => {
return (
<div className="relative flex py-8 px-2 gap-10 h-full">
<div className="w-full p-5 mx-auto bg-white dark:bg-neutral-900 shadow-2xl group h-full">
<div className="flex flex-1 w-full h-full flex-col space-y-2 ">
{/* TODO */}
<img
src="/linear.webp"
alt="header"
width={800}
height={800}
className="h-full w-full aspect-square object-cover object-left-top rounded-sm"
/>
</div>
</div>
<div className="absolute bottom-0 z-40 inset-x-0 h-60 bg-gradient-to-t from-white dark:from-black via-white dark:via-black to-transparent w-full pointer-events-none" />
<div className="absolute top-0 z-40 inset-x-0 h-60 bg-gradient-to-b from-white dark:from-black via-transparent to-transparent w-full pointer-events-none" />
</div>
);
};
export const SkeletonThree = () => {
return (
<a
href="https://www.youtube.com/watch?v=RPa3_AD1_Vs"
target="__blank"
className="relative flex gap-10 h-full group/image"
>
<div className="w-full mx-auto bg-transparent dark:bg-transparent group h-full">
<div className="flex flex-1 w-full h-full flex-col space-y-2 relative">
{/* TODO */}
<IconBrandYoutubeFilled className="h-20 w-20 absolute z-10 inset-0 text-red-500 m-auto " />
<img
src="https://assets.aceternity.com/fireship.jpg"
alt="header"
width={800}
height={800}
className="h-full w-full aspect-square object-cover object-center rounded-sm blur-none group-hover/image:blur-md transition-all duration-200"
/>
</div>
</div>
</a>
);
};
export const SkeletonTwo = () => {
const images = [
"https://images.unsplash.com/photo-1517322048670-4fba75cbbb62?q=80&w=3000&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1573790387438-4da905039392?q=80&w=3425&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1555400038-63f5ba517a47?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1554931670-4ebfabf6e7a9?q=80&w=3387&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1546484475-7f7bd55792da?q=80&w=2581&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
];
const imageVariants = {
whileHover: {
scale: 1.1,
rotate: 0,
zIndex: 100,
},
whileTap: {
scale: 1.1,
rotate: 0,
zIndex: 100,
},
};
return (
<div className="relative flex flex-col items-start p-8 gap-10 h-full overflow-hidden">
{/* TODO */}
<div className="flex flex-row -ml-20">
{images.map((image, idx) => (
<motion.div
variants={imageVariants}
key={"images-first" + idx}
style={{
rotate: Math.random() * 20 - 10,
}}
whileHover="whileHover"
whileTap="whileTap"
className="rounded-xl -mr-4 mt-4 p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 border border-neutral-100 shrink-0 overflow-hidden"
>
<img
src={image}
alt="bali images"
width="500"
height="500"
className="rounded-lg h-20 w-20 md:h-40 md:w-40 object-cover shrink-0"
/>
</motion.div>
))}
</div>
<div className="flex flex-row">
{images.map((image, idx) => (
<motion.div
key={"images-second" + idx}
style={{
rotate: Math.random() * 20 - 10,
}}
variants={imageVariants}
whileHover="whileHover"
whileTap="whileTap"
className="rounded-xl -mr-4 mt-4 p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 border border-neutral-100 shrink-0 overflow-hidden"
>
<img
src={image}
alt="bali images"
width="500"
height="500"
className="rounded-lg h-20 w-20 md:h-40 md:w-40 object-cover shrink-0"
/>
</motion.div>
))}
</div>
<div className="absolute left-0 z-[100] inset-y-0 w-20 bg-gradient-to-r from-white dark:from-black to-transparent h-full pointer-events-none" />
<div className="absolute right-0 z-[100] inset-y-0 w-20 bg-gradient-to-l from-white dark:from-black to-transparent h-full pointer-events-none" />
</div>
);
};
export const SkeletonFour = () => {
return (
<div className="h-60 md:h-60 flex flex-col items-center relative bg-transparent dark:bg-transparent mt-10">
<Globe className="absolute -right-10 md:-right-10 -bottom-80 md:-bottom-72" />
</div>
);
};
export const Globe = ({ className }: { className?: string }) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
let phi = 0;
if (!canvasRef.current) return;
const globe = createGlobe(canvasRef.current, {
devicePixelRatio: 2,
width: 600 * 2,
height: 600 * 2,
phi: 0,
theta: 0,
dark: 1,
diffuse: 1.2,
mapSamples: 16000,
mapBrightness: 6,
baseColor: [0.3, 0.3, 0.3],
markerColor: [0.1, 0.8, 1],
glowColor: [1, 1, 1],
markers: [
// longitude latitude
{ location: [37.7595, -122.4367], size: 0.03 },
{ location: [40.7128, -74.006], size: 0.1 },
],
onRender: (state) => {
// Called on every animation frame.
// `state` will be an empty object, return updated params.
state.phi = phi;
phi += 0.01;
},
});
return () => {
globe.destroy();
};
}, []);
return (
<canvas
ref={canvasRef}
style={{ width: 600, height: 600, maxWidth: "100%", aspectRatio: 1 }}
className={className}
/>
);
};

View File

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

View File

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

View File

@@ -1,47 +1,159 @@
import { useEffect, useRef } from 'react' "use client";
import createGlobe from 'cobe'
export function Globe({ className }: { className?: string }) { import createGlobe, { type COBEOptions } from "cobe";
const canvasRef = useRef<HTMLCanvasElement>(null) import { useMotionValue, useSpring } from "motion/react";
import { useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
const MOVEMENT_DAMPING = 1400;
const GLOBE_CONFIG: COBEOptions = {
width: 800,
height: 800,
onRender: () => {},
devicePixelRatio: 2,
phi: 0,
theta: 0.3,
dark: 0,
diffuse: 0.25, // softer shading for premium look
mapSamples: 16000,
mapBrightness: 1.1,
baseColor: [0.8, 0.8, 0.8], // sleek dark gray globe
markerColor: [0.02, 0.71, 0.83], // cyan-500
glowColor: [0.8, 0.8, 0.85], // grey
markers: [
// --- Core Global Markers ---
{ location: [14.5995, 120.9842], size: 0.03 }, // Manila
{ location: [19.076, 72.8777], size: 0.1 }, // Mumbai
{ location: [23.8103, 90.4125], size: 0.05 }, // Dhaka
{ location: [30.0444, 31.2357], size: 0.07 }, // Cairo
{ location: [39.9042, 116.4074], size: 0.08 }, // Beijing
{ location: [-23.5505, -46.6333], size: 0.1 }, // São Paulo
{ location: [19.4326, -99.1332], size: 0.1 }, // Mexico City
{ location: [40.7128, -74.006], size: 0.1 }, // New York
{ location: [34.6937, 135.5022], size: 0.05 }, // Osaka
{ location: [41.0082, 28.9784], size: 0.06 }, // Istanbul
{ location: [48.8566, 2.3522], size: 0.08 }, // Paris
{ location: [51.5072, -0.1276], size: 0.08 }, // London
{ location: [52.52, 13.405], size: 0.07 }, // Berlin
{ location: [35.6895, 139.6917], size: 0.06 }, // Tokyo
{ location: [-33.8688, 151.2093], size: 0.06 }, // Sydney
{ location: [-1.2921, 36.8219], size: 0.05 }, // Nairobi
{ location: [-34.6037, -58.3816], size: 0.07 }, // Buenos Aires
{ location: [37.7749, -122.4194], size: 0.08 }, // San Francisco
{ location: [1.3521, 103.8198], size: 0.06 }, // Singapore
{ location: [28.6139, 77.2090], size: 0.08 }, // New Delhi
{ location: [13.7563, 100.5018], size: 0.06 }, // Bangkok
{ location: [59.9343, 30.3351], size: 0.05 }, // St. Petersburg
{ location: [33.6844, 73.0479], size: 0.05 }, // Islamabad
{ location: [25.276987, 55.296249], size: 0.07 }, // Dubai
{ location: [60.1699, 24.9384], size: 0.05 }, // Helsinki
{ location: [43.6532, -79.3832], size: 0.07 }, // Toronto
{ location: [6.5244, 3.3792], size: 0.08 }, // Lagos
{ location: [50.1109, 8.6821], size: 0.06 }, // Frankfurt
// --- 12 New US + European Cities ---
{ location: [34.0522, -118.2437], size: 0.08 }, // Los Angeles
{ location: [41.8781, -87.6298], size: 0.07 }, // Chicago
{ location: [29.7604, -95.3698], size: 0.07 }, // Houston
{ location: [25.7617, -80.1918], size: 0.07 }, // Miami
{ location: [45.5017, -73.5673], size: 0.06 }, // Montreal
{ location: [47.6062, -122.3321], size: 0.06 }, // Seattle
{ location: [40.4406, -79.9959], size: 0.05 }, // Pittsburgh
{ location: [41.3851, 2.1734], size: 0.06 }, // Barcelona
{ location: [45.4642, 9.19], size: 0.06 }, // Milan
{ location: [52.3676, 4.9041], size: 0.06 }, // Amsterdam
{ location: [38.7169, -9.139], size: 0.05 }, // Lisbon
{ location: [59.3293, 18.0686], size: 0.05 }, // Stockholmx
],
};
export function Globe({
className,
config = GLOBE_CONFIG,
}: {
className?: string;
config?: COBEOptions;
}) {
let phi = 0;
let width = 0;
const canvasRef = useRef<HTMLCanvasElement>(null);
const pointerInteracting = useRef<number | null>(null);
const r = useMotionValue(0);
const rs = useSpring(r, {
mass: 1,
damping: 35, // slightly smoother motion
stiffness: 100,
});
const updatePointerInteraction = (value: number | null) => {
pointerInteracting.current = value;
if (canvasRef.current) {
canvasRef.current.style.cursor = value !== null ? "grabbing" : "grab";
}
};
const updateMovement = (clientX: number) => {
if (pointerInteracting.current !== null) {
const delta = clientX - pointerInteracting.current;
r.set(r.get() + delta / MOVEMENT_DAMPING);
}
};
useEffect(() => { useEffect(() => {
let phi = 0 const onResize = () => {
if (canvasRef.current) width = canvasRef.current.offsetWidth;
};
if (!canvasRef.current) return window.addEventListener("resize", onResize);
onResize();
const globe = createGlobe(canvasRef.current, { const globe = createGlobe(canvasRef.current!, {
devicePixelRatio: 2, ...config,
width: 600 * 2, width: width * 2,
height: 600 * 2, height: width * 2,
phi: 0,
theta: 0,
dark: 0,
diffuse: 1.2,
mapSamples: 16000,
mapBrightness: 6,
baseColor: [0.3, 0.3, 0.3],
markerColor: [0.1, 0.8, 1],
glowColor: [1, 1, 1],
markers: [
{ location: [37.7595, -122.4367], size: 0.03 },
{ location: [40.7128, -74.006], size: 0.1 },
],
onRender: (state) => { onRender: (state) => {
state.phi = phi if (!pointerInteracting.current) phi += 0.004; // slightly slower rotation for elegance
phi += 0.01 state.phi = phi + rs.get();
state.width = width * 2;
state.height = width * 2;
}, },
}) });
setTimeout(() => (canvasRef.current!.style.opacity = "1"), 0);
return () => { return () => {
globe.destroy() globe.destroy();
} window.removeEventListener("resize", onResize);
}, []) };
}, [rs, config]);
return ( return (
<canvas <div
ref={canvasRef} className={cn(
className={className} // Radial gradient background that fades to pure black at edges
style={{ width: '100%', height: '100%', maxWidth: '100%', aspectRatio: 1 }} "absolute inset-0 mx-auto aspect-[1/1] w-full max-w-[600px] rounded-full",
/> className,
) )}
>
<canvas
className={cn(
"size-full opacity-0 transition-opacity duration-500 [contain:layout_paint_size]",
)}
ref={canvasRef}
onPointerDown={(e) => {
pointerInteracting.current = e.clientX;
updatePointerInteraction(e.clientX);
}}
onPointerUp={() => updatePointerInteraction(null)}
onPointerOut={() => updatePointerInteraction(null)}
onMouseMove={(e) => updateMovement(e.clientX)}
onTouchMove={(e) =>
e.touches[0] && updateMovement(e.touches[0].clientX)
}
/>
</div>
);
} }

View File

@@ -0,0 +1,47 @@
import { useEffect, useRef } from 'react'
import createGlobe from 'cobe'
export function Globe({ className }: { className?: string }) {
const canvasRef = useRef<HTMLCanvasElement>(null)
useEffect(() => {
let phi = 0
if (!canvasRef.current) return
const globe = createGlobe(canvasRef.current, {
devicePixelRatio: 2,
width: 600 * 2,
height: 600 * 2,
phi: 0,
theta: 0,
dark: 0,
diffuse: 1.2,
mapSamples: 16000,
mapBrightness: 6,
baseColor: [0.3, 0.3, 0.3],
markerColor: [0.1, 0.8, 1],
glowColor: [1, 1, 1],
markers: [
{ location: [37.7595, -122.4367], size: 0.03 },
{ location: [40.7128, -74.006], size: 0.1 },
],
onRender: (state) => {
state.phi = phi
phi += 0.01
},
})
return () => {
globe.destroy()
}
}, [])
return (
<canvas
ref={canvasRef}
className={className}
style={{ width: '100%', height: '100%', maxWidth: '100%', aspectRatio: 1 }}
/>
)
}

View File

@@ -0,0 +1,89 @@
"use client";
import { Canvas, useFrame } from "@react-three/fiber";
import { Line, OrbitControls, useTexture } from "@react-three/drei";
import { useMemo, useRef } from "react";
type RotatableGroup = {
rotation: { y: number };
};
function Globe() {
const groupRef = useRef<RotatableGroup | null>(null);
const cloudTexture = useTexture("/images/cloud1.png");
// Rotate the globe slowly
useFrame(() => {
if (groupRef.current) {
groupRef.current.rotation.y += 0.002;
}
});
// Coordinates for markers (half-globe)
const markers = [
[0, 1, 0],
[0.7, 0.5, 0.2],
[-0.5, 0.4, 0.5],
[0.4, 0.3, -0.7],
[-0.6, -0.1, 0.3],
[0.3, -0.2, 0.8],
];
const arcPoints = useMemo(() => {
const radius = 2.5;
const verticalRadius = radius / 2;
const segments = 120;
return Array.from({ length: 8 }, () => {
const points: number[] = [];
for (let i = 0; i < segments; i++) {
const t = (i / (segments - 1)) * Math.PI;
const x = Math.cos(t) * radius;
const z = Math.sin(t) * verticalRadius;
const y = Math.sin(x / radius) * 0.5;
points.push(x, y, z);
}
return points;
});
}, []);
return (
<group ref={groupRef}>
{/* Cyan arcs */}
{arcPoints.map((points, i) => (
<Line
key={i}
points={points}
color="#00e5ff"
lineWidth={1}
transparent
opacity={0.5}
/>
))}
{/* Cloud markers */}
{markers.map(([x, y, z], i) => (
<mesh key={i} position={[x * 2.5, y * 2.5, z * 2.5]}>
<planeGeometry args={[0.3, 0.3]} />
<meshBasicMaterial
map={cloudTexture}
transparent
opacity={1}
side={2 /* DoubleSide */}
/>
</mesh>
))}
</group>
);
}
export function HalfGlobe() {
return (
<div className="w-full h-[500px] bg-white flex items-center justify-center">
<Canvas camera={{ position: [0, 1.5, 4], fov: 45 }}>
<ambientLight intensity={0.8} />
<Globe />
<OrbitControls enableZoom={false} enablePan={false} />
</Canvas>
</div>
);
}

View File

@@ -13,7 +13,7 @@ export function ScrollDown() {
return ( return (
<button <button
onClick={scrollToNext} onClick={scrollToNext}
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 text-2xl font-medium text-white lg:text-3xl animate-blink" className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 rounded-full bg-black/20 p-2 text-2xl font-medium text-white backdrop-blur-sm lg:text-3xl animate-blink"
> >
<span>scroll</span> <span>scroll</span>
<ChevronDoubleDownIcon className="h-6 w-6" /> <ChevronDoubleDownIcon className="h-6 w-6" />

View File

@@ -13,7 +13,7 @@ export function ScrollUp() {
return ( return (
<button <button
onClick={scrollToTop} onClick={scrollToTop}
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 text-2xl font-medium text-[#1c1c49] lg:text-3xl animate-blink" className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 rounded-full bg-white/20 p-2 text-2xl font-medium text-[#1c1c49] backdrop-blur-sm lg:text-3xl animate-blink"
> >
<span>top</span> <span>top</span>
<ChevronDoubleUpIcon className="h-6 w-6" /> <ChevronDoubleUpIcon className="h-6 w-6" />

View File

@@ -0,0 +1,64 @@
"use client";
import { cn } from "@/lib/utils";
import type { CSSProperties, HTMLProps, ReactNode } from "react";
interface AuroraBackgroundProps extends HTMLProps<HTMLDivElement> {
children: ReactNode;
showRadialGradient?: boolean;
}
export const AuroraBackground = ({
className,
children,
showRadialGradient = true,
...props
}: AuroraBackgroundProps) => {
return (
<main>
<div
className={cn(
"transition-bg relative flex h-[110vh] flex-col items-center justify-center bg-cover bg-center",
className
)}
style={{
backgroundImage: "url('/images/mchip.webp')",
}}
{...props}
>
<div
className="absolute inset-0 overflow-hidden"
style={
{
"--aurora":
"repeating-linear-gradient(100deg,#3b82f6_10%,#a5b4fc_15%,#93c5fd_20%,#ddd6fe_25%,#60a5fa_30%)",
"--dark-gradient":
"repeating-linear-gradient(100deg,#000_0%,#000_7%,transparent_10%,transparent_12%,#000_16%)",
"--white-gradient":
"repeating-linear-gradient(100deg,#fff_0%,#fff_7%,transparent_10%,transparent_12%,#fff_16%)",
"--blue-300": "#93c5fd",
"--blue-400": "#60a5fa",
"--blue-500": "#3b82f6",
"--indigo-300": "#a5b4fc",
"--violet-200": "#ddd6fe",
"--black": "#000",
"--white": "#fff",
"--transparent": "transparent",
} as CSSProperties
}
>
<div
// I'm sorry but this is what peak developer performance looks like // trigger warning
className={cn(
`after:animate-aurora pointer-events-none absolute -inset-[10px] [background-image:var(--white-gradient),var(--aurora)] [background-size:300%,_200%] [background-position:50%_50%,50%_50%] opacity-50 blur-[10px] invert filter will-change-transform [--aurora:repeating-linear-gradient(100deg,var(--blue-500)_10%,var(--indigo-300)_15%,var(--blue-300)_20%,var(--violet-200)_25%,var(--blue-400)_30%)] [--dark-gradient:repeating-linear-gradient(100deg,var(--black)_0%,var(--black)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--black)_16%)] [--white-gradient:repeating-linear-gradient(100deg,var(--white)_0%,var(--white)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--white)_16%)] after:absolute after:inset-0 after:[background-image:var(--white-gradient),var(--aurora)] after:[background-size:200%,_100%] after:[background-attachment:fixed] after:mix-blend-difference after:content-[""] dark:[background-image:var(--dark-gradient),var(--aurora)] dark:invert-0 after:dark:[background-image:var(--dark-gradient),var(--aurora)]`,
showRadialGradient &&
`[mask-image:radial-gradient(ellipse_at_100%_0%,black_10%,var(--transparent)_70%)]`,
)}
></div>
</div>
{children}
</div>
</main>
);
};

View File

@@ -1,6 +1,5 @@
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { CT, CP } from "@/components/Texts"; import { CT, CP } from "@/components/Texts";
import Image from 'next/image';
import React from 'react'; import React from 'react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
@@ -55,11 +54,9 @@ export const BentoGridItem = React.forwardRef<HTMLDivElement, BentoGridItemProps
className="w-full h-full object-cover opacity-90 group-hover/bento:opacity-100 transition-opacity duration-300" className="w-full h-full object-cover opacity-90 group-hover/bento:opacity-100 transition-opacity duration-300"
/> />
) : img ? ( ) : img ? (
<Image <img
src={img} src={img}
alt={title as string} alt={title as string}
width={300}
height={300}
className="w-full h-full object-cover opacity-90 group-hover/bento:opacity-100 transition-opacity duration-300" className="w-full h-full object-cover opacity-90 group-hover/bento:opacity-100 transition-opacity duration-300"
/> />
) : null} ) : null}

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import React, { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
type DottedGlowBackgroundProps = { type DottedGlowBackgroundProps = {
className?: string; className?: string;
@@ -203,12 +203,8 @@ export function DottedGlowBackground({
regenDots(); regenDots();
let last = performance.now();
const draw = (now: number) => { const draw = (now: number) => {
if (stopped) return; if (stopped) return;
const dt = (now - last) / 1000; // seconds
last = now;
const { width, height } = container.getBoundingClientRect(); const { width, height } = container.getBoundingClientRect();
ctx.clearRect(0, 0, el.width, el.height); ctx.clearRect(0, 0, el.width, el.height);

View File

@@ -0,0 +1,106 @@
"use client";
import { useMotionValue } from "motion/react";
import React, { useState, useEffect } from "react";
import { useMotionTemplate, motion } from "motion/react";
import { cn } from "@/lib/utils";
export const EvervaultCard = ({
children,
className,
}: {
children?: React.ReactNode;
className?: string;
}) => {
let mouseX = useMotionValue(0);
let mouseY = useMotionValue(0);
const [randomString, setRandomString] = useState("");
useEffect(() => {
let str = generateRandomString(1500);
setRandomString(str);
}, []);
function onMouseMove({ currentTarget, clientX, clientY }: any) {
let { left, top } = currentTarget.getBoundingClientRect();
mouseX.set(clientX - left);
mouseY.set(clientY - top);
const str = generateRandomString(1500);
setRandomString(str);
}
return (
<div
className={cn(
"p-0.5 bg-transparent aspect-square flex items-center justify-center w-full h-full relative",
className
)}
>
<div
onMouseMove={onMouseMove}
className="group/card rounded-3xl w-full relative overflow-hidden bg-transparent flex items-center justify-center h-full"
>
<CardPattern
mouseX={mouseX}
mouseY={mouseY}
randomString={randomString}
/>
<div className="relative z-10 flex items-center justify-center">
<div className="relative p-4">
{children}
</div>
</div>
</div>
</div>
);
};
export function CardPattern({ mouseX, mouseY, randomString }: any) {
let maskImage = useMotionTemplate`radial-gradient(250px at ${mouseX}px ${mouseY}px, white, transparent)`;
let style = { maskImage, WebkitMaskImage: maskImage };
return (
<div className="pointer-events-none">
<div className="absolute inset-0 rounded-2xl [mask-image:linear-gradient(white,transparent)] group-hover/card:opacity-50"></div>
<motion.div
className="absolute inset-0 rounded-2xl bg-gradient-to-r from-green-500 to-blue-700 opacity-0 group-hover/card:opacity-100 backdrop-blur-xl transition duration-500"
style={style}
/>
<motion.div
className="absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay group-hover/card:opacity-100"
style={style}
>
<p className="absolute inset-x-0 text-xs h-full break-words whitespace-pre-wrap text-white font-mono font-bold transition duration-500">
{randomString}
</p>
</motion.div>
</div>
);
}
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
export const generateRandomString = (length: number) => {
let result = "";
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
};
export const Icon = ({ className, ...rest }: any) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
className={className}
{...rest}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6v12m6-6H6" />
</svg>
);
};

View File

@@ -0,0 +1,90 @@
"use client";
import { useState } from "react";
import {
motion,
AnimatePresence,
useScroll,
useMotionValueEvent,
} from "motion/react";
import { Link } from "react-router-dom";
import { cn } from "@/lib/utils";
import { Button } from "../Button";
export const FloatingNav = ({
navItems,
className,
}: {
navItems: {
name: string;
link: string;
icon?: JSX.Element;
}[];
className?: string;
}) => {
const { scrollYProgress } = useScroll();
const [visible, setVisible] = useState(true);
useMotionValueEvent(scrollYProgress, "change", (current) => {
if (typeof current === "number") {
const previous = scrollYProgress.getPrevious();
// Check if previous is a number to avoid errors
if (typeof previous === "number" && current > previous) {
setVisible(false); // Scrolling down
} else {
setVisible(true); // Scrolling up or at the top
}
}
});
return (
<AnimatePresence mode="wait">
<motion.div
initial={{
opacity: 1,
y: -100,
}}
animate={{
y: visible ? 0 : -100,
opacity: visible ? 1 : 0,
}}
transition={{
duration: 0.2,
}}
className={cn(
"flex max-w-fit fixed top-10 inset-x-0 mx-auto border border-transparent dark:border-white/[0.2] rounded-xl dark:bg-black bg-gray-700/50 shadow-[0px_2px_3px_-1px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(25,28,33,0.02),0px_0px_0px_1px_rgba(25,28,33,0.08)] z-[5000] pr-4 pl-8 py-2 items-center justify-center space-x-12",
className
)}
>
{navItems.map((navItem, idx: number) => (
<Link
key={`link=${idx}`}
to={navItem.link}
className={cn(
"relative dark:text-neutral-50 items-center flex space-x-1 text-neutral-200 dark:hover:text-neutral-300 hover:text-cyan-500"
)}
>
<span className="block sm:hidden">{navItem.icon}</span>
<span className="hidden sm:block text-sm">{navItem.name}</span>
</Link>
))}
<div className="flex items-center gap-6">
<a
href="https://threefold.info/mycelium_network/docs/"
target="_blank"
rel="noopener noreferrer"
className={cn(
"relative dark:text-neutral-50 items-center flex space-x-1 text-neutral-200 dark:hover:text-neutral-300 hover:text-cyan-500"
)}
>
<span className="hidden sm:block text-sm">Docs</span>
</a>
<Button to="/download" variant="solid" color="cyan">
Get Mycelium Connector
</Button>
</div>
</motion.div>
</AnimatePresence>
);
};

View File

@@ -0,0 +1,158 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
import { AnimatePresence, motion } from "motion/react";
import { cn } from "@/lib/utils";
export const GlowingStarsBackgroundCard = ({
className,
children,
}: {
className?: string;
children?: React.ReactNode;
}) => {
const [mouseEnter, setMouseEnter] = useState(false);
return (
<div
onMouseEnter={() => {
setMouseEnter(true);
}}
onMouseLeave={() => {
setMouseEnter(false);
}}
className={cn(
"bg-[linear-gradient(110deg,#333_0.6%,#222)] p-4 max-w-md max-h-[20rem] h-full w-full rounded-xl border border-[#eaeaea] dark:border-neutral-600",
className
)}
>
<div className="flex justify-center items-center">
<Illustration mouseEnter={mouseEnter} />
</div>
<div className="px-2 pb-6">{children}</div>
</div>
);
};
export const GlowingStarsDescription = ({
className,
children,
}: {
className?: string;
children?: React.ReactNode;
}) => {
return (
<p className={cn("text-base text-white max-w-[16rem]", className)}>
{children}
</p>
);
};
export const GlowingStarsTitle = ({
className,
children,
}: {
className?: string;
children?: React.ReactNode;
}) => {
return (
<h2 className={cn("font-bold text-2xl text-[#eaeaea]", className)}>
{children}
</h2>
);
};
export const Illustration = ({ mouseEnter }: { mouseEnter: boolean }) => {
const stars = 108;
const columns = 18;
const [glowingStars, setGlowingStars] = useState<number[]>([]);
const highlightedStars = useRef<number[]>([]);
useEffect(() => {
const interval = setInterval(() => {
highlightedStars.current = Array.from({ length: 5 }, () =>
Math.floor(Math.random() * stars)
);
setGlowingStars([...highlightedStars.current]);
}, 3000);
return () => clearInterval(interval);
}, []);
return (
<div
className="h-48 p-1 w-full"
style={{
display: "grid",
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gap: `1px`,
}}
>
{[...Array(stars)].map((_, starIdx) => {
const isGlowing = glowingStars.includes(starIdx);
const delay = (starIdx % 10) * 0.1;
const staticDelay = starIdx * 0.01;
return (
<div
key={`matrix-col-${starIdx}}`}
className="relative flex items-center justify-center"
>
<Star
isGlowing={mouseEnter ? true : isGlowing}
delay={mouseEnter ? staticDelay : delay}
/>
{mouseEnter && <Glow delay={staticDelay} />}
<AnimatePresence mode="wait">
{isGlowing && <Glow delay={delay} />}
</AnimatePresence>
</div>
);
})}
</div>
);
};
const Star = ({ isGlowing, delay }: { isGlowing: boolean; delay: number }) => {
return (
<motion.div
key={delay}
initial={{
scale: 1,
}}
animate={{
scale: isGlowing ? [1, 1.2, 2.5, 2.2, 1.5] : 1,
background: isGlowing ? "#fff" : "#666",
}}
transition={{
duration: 2,
ease: "easeInOut",
delay: delay,
}}
className={cn("bg-[#666] h-[1px] w-[1px] rounded-full relative z-20")}
></motion.div>
);
};
const Glow = ({ delay }: { delay: number }) => {
return (
<motion.div
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
}}
transition={{
duration: 2,
ease: "easeInOut",
delay: delay,
}}
exit={{
opacity: 0,
}}
className="absolute left-1/2 -translate-x-1/2 z-10 h-[4px] w-[4px] rounded-full bg-blue-500 blur-[1px] shadow-2xl shadow-blue-400"
/>
);
};

104
src/components/ui/lamp.tsx Normal file
View File

@@ -0,0 +1,104 @@
"use client";
import React from "react";
import { motion } from "motion/react";
import { cn } from "@/lib/utils";
export default function LampDemo() {
return (
<LampContainer>
<motion.h1
initial={{ opacity: 0.5, y: 100 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{
delay: 0.3,
duration: 0.8,
ease: "easeInOut",
}}
className="mt-8 bg-gradient-to-br from-gray-300 to-gray-500 py-4 bg-clip-text text-center text-4xl font-medium tracking-tight text-transparent md:text-7xl"
>
Build lamps <br /> the right way
</motion.h1>
</LampContainer>
);
}
export const LampContainer = ({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) => {
return (
<div
className={cn(
"relative flex h-screen flex-col items-center justify-start pt-32 overflow-hidden bg-gray-950 w-full z-0",
className
)}
>
<div className="relative flex w-full flex-1 scale-y-125 items-center justify-center isolate z-0 ">
<motion.div
initial={{ opacity: 0.5, width: "15rem" }}
whileInView={{ opacity: 1, width: "30rem" }}
transition={{
delay: 0.5,
duration: 1.0,
ease: "easeInOut",
}}
style={{
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`,
}}
className="absolute inset-auto right-1/2 h-56 overflow-visible w-[30rem] bg-gradient-conic from-cyan-500 via-transparent to-transparent text-white [--conic-position:from_70deg_at_center_top]"
>
<div className="absolute w-[100%] left-0 bg-gray-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" />
<div className="absolute w-40 h-[100%] left-0 bg-gray-950 bottom-0 z-20 [mask-image:linear-gradient(to_right,white,transparent)]" />
</motion.div>
<motion.div
initial={{ opacity: 0.5, width: "15rem" }}
whileInView={{ opacity: 1, width: "30rem" }}
transition={{
delay: 0.3,
duration: 0.8,
ease: "easeInOut",
}}
style={{
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`,
}}
className="absolute inset-auto left-1/2 h-56 w-[30rem] bg-gradient-conic from-transparent via-transparent to-cyan-500 text-white [--conic-position:from_290deg_at_center_top]"
>
<div className="absolute w-40 h-[100%] right-0 bg-gray-950 bottom-0 z-20 [mask-image:linear-gradient(to_left,white,transparent)]" />
<div className="absolute w-[100%] right-0 bg-gray-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" />
</motion.div>
<div className="absolute top-1/2 h-48 w-full translate-y-12 scale-x-150 bg-gray-950 blur-2xl"></div>
<div className="absolute top-1/2 z-50 h-48 w-full bg-transparent opacity-10 backdrop-blur-md"></div>
<div className="absolute inset-auto z-50 h-36 w-[28rem] -translate-y-1/2 rounded-full bg-cyan-500 opacity-50 blur-3xl"></div>
<motion.div
initial={{ width: "8rem" }}
whileInView={{ width: "16rem" }}
transition={{
delay: 0.3,
duration: 0.8,
ease: "easeInOut",
}}
className="absolute inset-auto z-30 h-36 w-64 -translate-y-[6rem] rounded-full bg-cyan-400 blur-2xl"
></motion.div>
<motion.div
initial={{ width: "15rem" }}
whileInView={{ width: "30rem" }}
transition={{
delay: 0.3,
duration: 0.8,
ease: "easeInOut",
}}
className="absolute inset-auto z-50 h-0.5 w-[30rem] -translate-y-[7rem] bg-cyan-400 "
></motion.div>
<div className="absolute inset-auto z-40 h-44 w-full -translate-y-[12.5rem] bg-gray-950 "></div>
</div>
<div className="relative z-50 flex -translate-y-32 flex-col items-center px-5">
{children}
</div>
</div>
);
};

View File

@@ -0,0 +1,58 @@
"use client";
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { cn } from "@/lib/utils";
export const LayoutTextFlip = ({
text = "Build Amazing",
words = ["Landing Pages", "Component Blocks", "Page Sections", "3D Shaders"],
duration = 1500,
}: {
text: string;
words: string[];
duration?: number;
}) => {
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % words.length);
}, duration);
return () => clearInterval(interval);
}, []);
return (
<>
<motion.span
layoutId="subtext"
className="text-2xl font-bold tracking-tight drop-shadow-lg md:text-4xl"
>
{text}
</motion.span>
<motion.span
layout
className="relative w-fit overflow-hidden px-2 py-2 font-medium italic tracking-tight"
>
<AnimatePresence mode="popLayout">
<motion.span
key={currentIndex}
initial={{ y: -40, filter: "blur(10px)" }}
animate={{
y: 0,
filter: "blur(0px)",
}}
exit={{ y: 50, filter: "blur(10px)", opacity: 0 }}
transition={{
duration: 0.5,
}}
className={cn("inline-block whitespace-nowrap")}
>
{words[currentIndex]}
</motion.span>
</AnimatePresence>
</motion.span>
</>
);
};

View File

@@ -0,0 +1,119 @@
"use client";
import { cn } from "@/lib/utils";
import { motion } from "motion/react";
import { useRef, useEffect, useState } from "react";
export function PointerHighlight({
children,
rectangleClassName,
pointerClassName,
containerClassName,
}: {
children: React.ReactNode;
rectangleClassName?: string;
pointerClassName?: string;
containerClassName?: string;
}) {
const containerRef = useRef<HTMLDivElement>(null);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
useEffect(() => {
if (containerRef.current) {
const { width, height } = containerRef.current.getBoundingClientRect();
setDimensions({ width, height });
}
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
setDimensions({ width, height });
}
});
if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}
return () => {
if (containerRef.current) {
resizeObserver.unobserve(containerRef.current);
}
};
}, []);
return (
<div
className={cn("relative w-fit", containerClassName)}
ref={containerRef}
>
{children}
{dimensions.width > 0 && dimensions.height > 0 && (
<motion.div
className="pointer-events-none absolute inset-0 z-0"
initial={{ opacity: 0, scale: 0.95, originX: 0, originY: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, ease: "easeOut" }}
>
<motion.div
className={cn(
"absolute inset-0 border border-neutral-800 dark:border-neutral-200",
rectangleClassName,
)}
initial={{
width: 0,
height: 0,
}}
whileInView={{
width: dimensions.width,
height: dimensions.height,
}}
transition={{
duration: 1,
ease: "easeInOut",
}}
/>
<motion.div
className="pointer-events-none absolute"
initial={{ opacity: 0 }}
whileInView={{
opacity: 1,
x: dimensions.width + 4,
y: dimensions.height + 4,
}}
style={{
rotate: -90,
}}
transition={{
opacity: { duration: 0.1, ease: "easeInOut" },
duration: 1,
ease: "easeInOut",
}}
>
<Pointer
className={cn("h-5 w-5 text-blue-500", pointerClassName)}
/>
</motion.div>
</motion.div>
)}
</div>
);
}
const Pointer = ({ ...props }: React.SVGProps<SVGSVGElement>) => {
return (
<svg
stroke="currentColor"
fill="currentColor"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
viewBox="0 0 16 16"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path d="M14.082 2.182a.5.5 0 0 1 .103.557L8.528 15.467a.5.5 0 0 1-.917-.007L5.57 10.694.803 8.652a.5.5 0 0 1-.006-.916l12.728-5.657a.5.5 0 0 1 .556.103z"></path>
</svg>
);
};

View File

@@ -0,0 +1,166 @@
"use client";
import { useRef } from "react";
import { motion } from "motion/react";
import DottedMap from "dotted-map";
interface MapProps {
dots?: Array<{
start: { lat: number; lng: number; label?: string };
end: { lat: number; lng: number; label?: string };
}>;
lineColor?: string;
}
export default function WorldMap({
dots = [],
lineColor = "#06b6d4",
}: MapProps) {
const svgRef = useRef<SVGSVGElement>(null);
const map = new DottedMap({ height: 100, grid: "diagonal" });
const svgMap = map.getSVG({
radius: 0.22,
color: "#FFFFFF40", // Hardcoded for dark theme
shape: "circle",
backgroundColor: "black", // Hardcoded for dark theme
});
const projectPoint = (lat: number, lng: number) => {
const x = (lng + 180) * (800 / 360);
const y = (90 - lat) * (400 / 180);
return { x, y };
};
const createCurvedPath = (
start: { x: number; y: number },
end: { x: number; y: number }
) => {
const midX = (start.x + end.x) / 2;
const midY = Math.min(start.y, end.y) - 50;
return `M ${start.x} ${start.y} Q ${midX} ${midY} ${end.x} ${end.y}`;
};
return (
<div className="w-full aspect-[2/1] dark:bg-black bg-white rounded-lg relative font-sans">
<img
src={`data:image/svg+xml;utf8,${encodeURIComponent(svgMap)}`}
className="h-full w-full [mask-image:linear-gradient(to_bottom,transparent,white_10%,white_90%,transparent)] pointer-events-none select-none"
alt="world map"
height="495"
width="1056"
draggable={false}
/>
<svg
ref={svgRef}
viewBox="0 0 800 400"
className="w-full h-full absolute inset-0 pointer-events-none select-none"
>
{dots.map((dot, i) => {
const startPoint = projectPoint(dot.start.lat, dot.start.lng);
const endPoint = projectPoint(dot.end.lat, dot.end.lng);
return (
<g key={`path-group-${i}`}>
<motion.path
d={createCurvedPath(startPoint, endPoint)}
fill="none"
stroke="url(#path-gradient)"
strokeWidth="1"
initial={{
pathLength: 0,
}}
animate={{
pathLength: 1,
}}
transition={{
duration: 1,
delay: 0.5 * i,
ease: "easeOut",
}}
key={`start-upper-${i}`}
></motion.path>
</g>
);
})}
<defs>
<linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="white" stopOpacity="0" />
<stop offset="5%" stopColor={lineColor} stopOpacity="1" />
<stop offset="95%" stopColor={lineColor} stopOpacity="1" />
<stop offset="100%" stopColor="white" stopOpacity="0" />
</linearGradient>
</defs>
{dots.map((dot, i) => (
<g key={`points-group-${i}`}>
<g key={`start-${i}`}>
<circle
cx={projectPoint(dot.start.lat, dot.start.lng).x}
cy={projectPoint(dot.start.lat, dot.start.lng).y}
r="2"
fill={lineColor}
/>
<circle
cx={projectPoint(dot.start.lat, dot.start.lng).x}
cy={projectPoint(dot.start.lat, dot.start.lng).y}
r="2"
fill={lineColor}
opacity="0.5"
>
<animate
attributeName="r"
from="2"
to="8"
dur="1.5s"
begin="0s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
from="0.5"
to="0"
dur="1.5s"
begin="0s"
repeatCount="indefinite"
/>
</circle>
</g>
<g key={`end-${i}`}>
<circle
cx={projectPoint(dot.end.lat, dot.end.lng).x}
cy={projectPoint(dot.end.lat, dot.end.lng).y}
r="2"
fill={lineColor}
/>
<circle
cx={projectPoint(dot.end.lat, dot.end.lng).x}
cy={projectPoint(dot.end.lat, dot.end.lng).y}
r="2"
fill={lineColor}
opacity="0.5"
>
<animate
attributeName="r"
from="2"
to="8"
dur="1.5s"
begin="0s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
from="0.5"
to="0"
dur="1.5s"
begin="0s"
repeatCount="indefinite"
/>
</circle>
</g>
</g>
))}
</svg>
</div>
);
}

BIN
src/images/cloudlayer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
src/images/kubernetes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1024 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@@ -214,7 +214,7 @@
</filter> </filter>
<radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" <radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0 727 -642 0 184 1)"> gradientTransform="matrix(0 727 -642 0 184 1)">
<stop stop-color="#FAFAFA" /> <stop stop-color="#FFFFFF" />
<stop offset="1" stop-color="#E6E6E6" /> <stop offset="1" stop-color="#E6E6E6" />
</radialGradient> </radialGradient>
<radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" <radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,5 +1,8 @@
@import url('https://fonts.googleapis.com/css2?family=Neuton&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;700&display=swap');
:root { :root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; font-family: 'Mulish', system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5; line-height: 1.5;
font-weight: 400; font-weight: 400;

View File

@@ -1,5 +1,5 @@
import { type ClassValue, clsx } from 'clsx' import { clsx, type ClassValue } from "clsx"
import { twMerge } from 'tailwind-merge' import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs))

View File

@@ -0,0 +1,125 @@
'use client'
import { useId, useState } from 'react'
import { Button } from '../../components/Button'
import { Container } from '../../components/Container'
import ContactForm from '../../components/ContactForm'
function BackgroundIllustration(props: React.ComponentPropsWithoutRef<'div'>) {
const id = useId()
return (
<div {...props}>
<svg
viewBox="0 0 1026 1026"
fill="none"
aria-hidden="true"
className="absolute inset-0 h-full w-full animate-spin-slow"
>
<path
d="M1025 513c0 282.77-229.23 512-512 512S1 795.77 1 513 230.23 1 513 1s512 229.23 512 512Z"
stroke="#D4D4D4"
strokeOpacity="0.7"
/>
<path
d="M513 1025C230.23 1025 1 795.77 1 513"
stroke={`url(#${id}-gradient-1)`}
strokeLinecap="round"
/>
<defs>
<linearGradient
id={`${id}-gradient-1`}
x1="1"
y1="513"
x2="1"
y2="1025"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#06b6d4" />
<stop offset="1" stopColor="#06b6d4" stopOpacity="0" />
</linearGradient>
</defs>
</svg>
<svg
viewBox="0 0 1026 1026"
fill="none"
aria-hidden="true"
className="absolute inset-0 h-full w-full animate-spin-reverse-slower"
>
<path
d="M913 513c0 220.914-179.086 400-400 400S113 733.914 113 513s179.086-400 400-400 400 179.086 400 400Z"
stroke="#D4D4D4"
strokeOpacity="0.7"
/>
<path
d="M913 513c0 220.914-179.086 400-400 400"
stroke={`url(#${id}-gradient-2)`}
strokeLinecap="round"
/>
<defs>
<linearGradient
id={`${id}-gradient-2`}
x1="913"
y1="513"
x2="913"
y2="913"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#06b6d4" />
<stop offset="1" stopColor="#06b6d4" stopOpacity="0" />
</linearGradient>
</defs>
</svg>
</div>
)
}
export function AgentsHero() {
const [isContactOpen, setIsContactOpen] = useState(false)
return (
<>
<div className="overflow-hidden pb-24 lg:py-32 lg:pb-0">
<Container>
<div className="flex flex-col-reverse gap-y-16 lg:grid lg:grid-cols-12 lg:gap-x-8 lg:gap-y-20">
<div className="relative z-10 mx-auto max-w-2xl lg:col-span-7 lg:max-w-none lg:pt-6 xl:col-span-6">
<h1 className="text-4xl font-medium tracking-tight text-gray-900 lg:text-6xl">
Mycelium Agents
</h1>
<h2 className="mt-6 text-xl leading-normal tracking-tight text-gray-600 lg:text-2xl">
Sovereign AI agents, coming soon.
</h2>
<p className="mt-6 text-lg leading-tight text-gray-600 lg:text-xl lg:leading-normal">
Hero is the autonomous agent layer for the Mycelium platformtrusted, policy-aware AI that runs on infrastructure you control and remembers what matters.
</p>
<div className="mt-8 flex flex-wrap gap-x-6 gap-y-4">
<Button variant="solid" color="cyan" onClick={() => setIsContactOpen(true)}>
Join the Waitlist
</Button>
</div>
</div>
<div className="relative mt-0 lg:mt-10 lg:col-span-5 lg:row-span-2 xl:col-span-6">
<BackgroundIllustration className="absolute left-1/2 top-4 h-[1026px] w-[1026px] -translate-x-1/2 stroke-gray-300/70 sm:top-16 lg:-top-12 lg:ml-12" />
<div className="mx-auto h-[420px] max-w-[640px] mask-[linear-gradient(to_bottom,white_60%,transparent)] lg:absolute lg:-inset-x-10 lg:-top-24 lg:h-auto lg:pt-10 xl:-bottom-36">
<img
src="/images/agent_icon.png"
alt="Preview of Hero sovereign agent orchestration"
className="mx-auto w-full max-w-[520px]"
width={1024}
height={1024}
/>
</div>
</div>
</div>
</Container>
</div>
<ContactForm
isOpen={isContactOpen}
onClose={() => setIsContactOpen(false)}
title="Join the Waitlist"
formType="agent_waitlist"
/>
</>
)
}

View File

@@ -1,4 +1,5 @@
import { AnimatedSection } from '../../components/AnimatedSection' import { AnimatedSection } from '../../components/AnimatedSection'
import { AgentsHero } from './AgentsHero'
import { DeploySection } from './DeploySection' import { DeploySection } from './DeploySection'
import { GallerySection } from './GallerySection' import { GallerySection } from './GallerySection'
import { Companies } from './Companies' import { Companies } from './Companies'
@@ -7,6 +8,10 @@ import { BentoSection } from './BentoSection'
export default function AgentsPage() { export default function AgentsPage() {
return ( return (
<div> <div>
<AnimatedSection>
<AgentsHero />
</AnimatedSection>
<AnimatedSection> <AnimatedSection>
<DeploySection /> <DeploySection />
</AnimatedSection> </AnimatedSection>
@@ -15,7 +20,6 @@ export default function AgentsPage() {
<Companies /> <Companies />
</AnimatedSection> </AnimatedSection>
<AnimatedSection> <AnimatedSection>
<GallerySection /> <GallerySection />
</AnimatedSection> </AnimatedSection>

View File

@@ -48,16 +48,16 @@ export function BentoSection() {
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.8 }} transition={{ duration: 0.6, ease: 'easeOut' }}
className="mx-auto max-w-3xl text-center mb-16" className="mx-auto mb-16 max-w-3xl text-center"
> >
<Eyebrow color="accent">Components</Eyebrow> <Eyebrow color="accent">Components</Eyebrow>
<SectionHeader color="light"> <SectionHeader color="light">
Augmented Intelligence Fabric Intelligence Fabric
</SectionHeader> </SectionHeader>
<P className="mt-6" color="lightSecondary"> <P className="mt-6" color="lightSecondary">
A complete infrastructure for building and deploying AI agents with enterprise-grade security and performance. A complete infrastructure for building and deploying sovereign AI agents with enterprise-grade security and performance. Your data never leaves your control.
</P> </P>
</motion.div> </motion.div>
@@ -67,9 +67,9 @@ export function BentoSection() {
key={index} key={index}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true, amount: 0.2 }}
transition={{ duration: 0.5, delay: index * 0.1 }} transition={{ duration: 0.45, delay: index * 0.1, ease: 'easeOut' }}
className="rounded-2xl bg-gray-900 border border-gray-800 p-6 hover:border-cyan-500 hover:shadow-lg transition-all duration-300 hover:scale-105 overflow-hidden" className="overflow-hidden rounded-2xl border border-gray-800 bg-gray-900 p-6 transition-all duration-300 hover:scale-105 hover:border-cyan-500 hover:shadow-lg"
> >
<video <video
src={item.video} src={item.video}

View File

@@ -1,8 +1,7 @@
"use client"; "use client";
import React from "react";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { P, Eyebrow } 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";
@@ -47,8 +46,9 @@ export function Companies() {
<motion.div <motion.div
className="flex flex-col justify-center max-w-4xl items-center mb-8 mx-auto" className="flex flex-col justify-center max-w-4xl items-center mb-8 mx-auto"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }} viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.6, ease: "easeOut" }}
> >
<Eyebrow color="accent"></Eyebrow> <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">

View File

@@ -1,46 +1,63 @@
'use client' "use client";
import { useRef } from 'react' import { motion } from "framer-motion";
import { motion, useInView } from 'framer-motion' import { SectionHeader, P, Eyebrow, CT, CP } from "@/components/Texts";
import { SectionHeader, P, Eyebrow, CT, CP } from '@/components/Texts' import type { ComponentPropsWithoutRef } from "react";
import { TbCircleNumber1Filled, TbCircleNumber2Filled, TbCircleNumber3Filled } from 'react-icons/tb'
type CircleIconProps = ComponentPropsWithoutRef<"svg">;
const CircleNumber1Icon = (props: CircleIconProps) => (
<svg viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2zm.994 5.886c-.083-.777-1.008-1.16-1.617-.67l-.084.077-2 2-.083.094a1 1 0 000 1.226l.083.094.094.083a1 1 0 001.226 0l.094-.083.293-.293v5.586l.007.117a1 1 0 001.986 0l.007-.117v-8l-.006-.114z" />
</svg>
);
const CircleNumber2Icon = (props: CircleIconProps) => (
<svg viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2zm1 5h-3l-.117.007a1 1 0 000 1.986l.117.007h3v2h-2l-.15.005a2 2 0 00-1.844 1.838l-.006.157v2l.005.15a2 2 0 001.838 1.844l.157.006h3l.117-.007a1 1 0 000-1.986l-.117-.007h-3v-2h2l.15-.005a2 2 0 001.844-1.838l.006-.157v-2l-.005-.15a2 2 0 00-1.838-1.844l-.157-.006z" />
</svg>
);
const CircleNumber3Icon = (props: CircleIconProps) => (
<svg viewBox="0 0 24 24" fill="currentColor" {...props}>
<path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2zm1 5h-2l-.15.005a2 2 0 00-1.85 1.995 1 1 0 001.974.23l.02-.113.006-.117h2v2h-2l-.133.007c-1.111.12-1.154 1.73-.128 1.965l.128.021.133.007h2v2h-2l-.007-.117a1 1 0 00-1.993.117 2 2 0 001.85 1.995l.15.005h2l.15-.005a2 2 0 001.844-1.838l.006-.157v-2l-.005-.15a1.988 1.988 0 00-.17-.667l-.075-.152-.019-.032.02-.03a2.01 2.01 0 00.242-.795l.007-.174v-2l-.005-.15a2 2 0 00-1.838-1.844l-.157-.006z" />
</svg>
);
const features = [ const features = [
{ {
name: 'Choose Your Intelligence', name: 'Choose Your Intelligence',
description: 'Explore a library of leading LLMs and agentic functions. Pick the ones that fit your use case, from general assistants to specialized reasoning models.', description: 'Explore a library of leading LLMs and agentic functions. Pick the ones that fit your use case, from general assistants to specialized reasoning models.',
icon: TbCircleNumber1Filled, icon: CircleNumber1Icon,
}, },
{ {
name: 'Add Your Knowledge', name: 'Add Your Knowledge',
description: description:
'Connect your data or knowledge base to enable personalized, context-aware results while keeping your information private.', 'Connect your data or knowledge base to enable personalized, context-aware results while keeping your information private.',
icon: TbCircleNumber2Filled, icon: CircleNumber2Icon,
}, },
{ {
name: 'Define Your Network', name: 'Define Your Network',
description: description:
'Set up and manage your nodes with ease. Scale compute and storage as you grow, while staying fully sovereign and decentralized.', 'Set up and manage your nodes with ease. Scale compute and storage as you grow, while staying fully sovereign and decentralized.',
icon: TbCircleNumber3Filled, icon: CircleNumber3Icon,
}, },
] ];
export function DeploySection() { export function DeploySection() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
return ( return (
<section id="benefits" ref={ref} className="relative pt-12 lg:pt-24 pb-4 px-4 lg:px-12 text-white bg-black"> <section id="benefits" className="relative bg-black px-4 pb-4 pt-12 text-white lg:px-12 lg:pt-24">
<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 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.1 }} viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.6, ease: "easeOut", delay: 0.1 }}
className="mx-auto max-w-5xl text-center" className="mx-auto max-w-5xl text-center"
> >
<Eyebrow color="accent">Get Started</Eyebrow> <Eyebrow color="accent">Get Started</Eyebrow>
<SectionHeader className="text-3xl font-medium tracking-tight" color="light"> <SectionHeader className="text-3xl font-medium tracking-tight" color="light">
Deploy Scalable LLMs and AI Agents in Seconds Deploy Scalable LLMs and AI Agents
</SectionHeader> </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.
@@ -48,25 +65,31 @@ export function DeploySection() {
</motion.div> </motion.div>
<motion.ul <motion.ul
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={isInView ? { opacity: 1 } : { opacity: 0 }} whileInView={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.2, staggerChildren: 0.2 }} viewport={{ once: true, amount: 0.2 }}
className="mx-auto lg:mt-12 mt-8 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-8 text-base/7 sm:grid-cols-2 sm:gap-y-16 lg:mx-12 lg:max-w-7xl lg:grid-cols-3" transition={{ duration: 0.5, delay: 0.2 }}
className="mx-auto mt-8 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-8 text-base/7 sm:grid-cols-2 sm:gap-y-16 lg:mx-12 lg:mt-12 lg:max-w-7xl lg:grid-cols-3"
> >
{features.map((feature, index) => ( {features.map((feature, index) => (
<motion.li <motion.li
key={feature.name} key={feature.name}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.3 + index * 0.2 }} viewport={{ once: true, amount: 0.2 }}
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" transition={{ duration: 0.45, delay: 0.3 + index * 0.15, ease: "easeOut" }}
className="rounded-2xl border border-gray-300 bg-white/5 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 backdrop-blur-md"
> >
<feature.icon className="h-8 w-8 mb-4 text-white" /> <feature.icon className="mb-4 h-8 w-8 text-white" />
<CT as="span" className="font-semibold text-left" color="light">{feature.name}</CT> <CT as="span" className="text-left font-semibold" color="light">
<CP className="mt-2 text-sm text-left" color="light">{feature.description}</CP> {feature.name}
</CT>
<CP className="mt-2 text-left text-sm" color="light">
{feature.description}
</CP>
</motion.li> </motion.li>
))} ))}
</motion.ul> </motion.ul>
</div> </div>
</section> </section>
) );
} }

View File

@@ -48,7 +48,7 @@ export function GallerySection() {
const prev = () => setActive((i) => wrap(0, galleryItems.length, i - 1)) const prev = () => setActive((i) => wrap(0, galleryItems.length, i - 1))
return ( return (
<div className="bg-[#FAFAFA]"> <div className="bg-white">
<div className="relative isolate pt-8 pb-0 text-center w-full"> <div className="relative isolate pt-8 pb-0 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">

View File

@@ -5,29 +5,27 @@ export function CloudCTA() {
return ( return (
<section className="relative overflow-hidden py-24 lg:py-32"> <section className="relative overflow-hidden py-24 lg:py-32">
<Container className="relative"> <Container className="relative">
<div className="relative mx-auto max-w-5xl overflow-hidden rounded-3xl border border-gray-200/60 bg-white/90 px-10 py-16 text-center shadow-[0_30px_120px_-70px_rgba(6,182,212,0.65)] backdrop-blur lg:px-16 lg:py-20"> <div className="mx-auto max-w-3xl text-center">
<div className="max-w-3xl mx-auto"> <p className="text-sm font-semibold uppercase tracking-[0.3em] text-cyan-500">
<p className="text-sm font-semibold uppercase tracking-[0.3em] text-cyan-500"> Ready Today
Ready Today </p>
</p> <h2 className="mt-6 text-3xl font-semibold tracking-tight text-gray-900 lg:text-5xl">
<h2 className="mt-6 text-3xl font-semibold tracking-tight text-gray-900 lg:text-5xl"> Join thousands of developers and DevOps engineers who trust Mycelium Cloud for their production workloads.
Join thousands of developers and DevOps engineers who trust Mycelium Cloud for their production workloads. </h2>
</h2> <p className="mt-6 text-lg text-gray-600">
<p className="mt-6 text-lg text-gray-600"> Revolutionary Kubernetes automation, deterministic compute, and quantum-safe storagedelivered as a turnkey platform so your team can deploy, scale, and operate cloud-native applications with confidence.
Revolutionary Kubernetes automation, deterministic compute, and quantum-safe storagedelivered as a turnkey platform so your team can deploy, scale, and operate cloud-native applications with confidence. </p>
</p> <div className="mt-10 flex justify-center">
<div className="mt-10 flex justify-center"> <Button
<Button to="https://myceliumcloud.tf"
to="https://myceliumcloud.tf" as="a"
as="a" variant="solid"
variant="solid" color="cyan"
color="cyan" target="_blank"
target="_blank" rel="noreferrer"
rel="noreferrer" >
> Start Deployment
Start Deploying </Button>
</Button>
</div>
</div> </div>
</div> </div>
</Container> </Container>

View File

@@ -88,19 +88,10 @@ export function CloudHero() {
Mycelium Cloud blends deterministic compute with quantum-safe storage, delivering a sovereign platform built for zero-ops automation. Mycelium Cloud blends deterministic compute with quantum-safe storage, delivering a sovereign platform built for zero-ops automation.
</p> </p>
<div className="mt-8 flex flex-wrap gap-x-6 gap-y-4"> <div className="mt-8 flex flex-wrap gap-x-6 gap-y-4">
<Button to="/download" variant="solid" color="cyan"> <Button to="https://myceliumcloud.tf" variant="solid" color="cyan">
Start Deploying Start Deployment
</Button>
<Button
to="https://manual.grid.tf"
as="a"
variant="outline"
color="gray"
target="_blank"
rel="noreferrer"
>
View Documentation
</Button> </Button>
</div> </div>
</div> </div>
<div className="relative mt-0 lg:mt-10 lg:col-span-5 lg:row-span-2 xl:col-span-6"> <div className="relative mt-0 lg:mt-10 lg:col-span-5 lg:row-span-2 xl:col-span-6">

View File

@@ -48,12 +48,12 @@ export function CloudOverview() {
{focusAreas.map((item) => ( {focusAreas.map((item) => (
<div <div
key={item.title} key={item.title}
className="rounded-3xl border border-white/10 bg-white/5 p-8 text-left shadow-[0_20px_60px_-40px_rgba(6,182,212,0.6)] backdrop-blur" className="rounded-3xl p-8 text-left transition hover:-translate-y-1"
> >
<div className="text-base font-semibold text-cyan-200"> <div className="text-lg font-semibold text-white">
{item.title} {item.title}
</div> </div>
<p className="mt-4 text-sm leading-relaxed text-gray-200"> <p className="mt-4 text-sm leading-relaxed text-gray-300">
{item.description} {item.description}
</p> </p>
</div> </div>

View File

@@ -45,7 +45,7 @@ export function SecurityPillars() {
{pillars.map((pillar) => ( {pillars.map((pillar) => (
<div <div
key={pillar.title} key={pillar.title}
className="rounded-3xl border border-white/10 bg-white/5 p-8 text-left backdrop-blur-sm transition hover:-translate-y-1 hover:border-cyan-400/60" className="rounded-3xl p-8 text-left transition hover:-translate-y-1"
> >
<div className="text-lg font-semibold text-white"> <div className="text-lg font-semibold text-white">
{pillar.title} {pillar.title}

View File

@@ -0,0 +1,69 @@
'use client'
import {
BookOpenIcon,
ChatBubbleOvalLeftEllipsisIcon,
LifebuoyIcon,
UserGroupIcon,
} from '@heroicons/react/24/outline'
const features = [
{
name: 'Documentation',
description: 'Documentation for Mycelium.',
href: 'https://threefold.info/mycelium_network/docs/',
icon: BookOpenIcon,
},
{
name: 'Support',
description: 'Talk to an expert.',
href: 'https://threefoldfaq.crisp.help/en/',
icon: LifebuoyIcon,
},
{
name: 'Forum',
description: 'Forum for all your questions.',
href: 'https://forum.threefold.io/',
icon: ChatBubbleOvalLeftEllipsisIcon,
},
{
name: 'Community',
description: 'Join our Developers community on telegram.',
href: 'https://t.me/threefoldtesting',
icon: UserGroupIcon,
},
]
export function DevHub() {
return (
<div className="bg-gray-900 py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto grid max-w-2xl grid-cols-1 gap-x-8 gap-y-16 sm:gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-5">
<div className="col-span-2">
<h2 className="mb-2 text-base font-semibold leading-7 text-cyan-500">Get Started</h2>
<p className="text-3xl font-medium tracking-tight text-white lg:text-4xl">Developer Hub</p>
<p className="mt-6 text-lg text-gray-300">
Our Developer Hub is a resource center for developers looking to build on top of Mycelium.
Join our Developers community on telegram to get started.
</p>
</div>
<dl className="col-span-3 grid grid-cols-1 gap-8 sm:grid-cols-2">
{features.map((feature) => (
<a
key={feature.name}
href={feature.href}
target="_blank"
rel="noopener noreferrer"
className="block rounded-2xl border border-gray-700 bg-gray-900/40 p-6 shadow-sm transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:bg-gray-800 hover:shadow-lg hover:shadow-cyan-500/20"
>
<feature.icon aria-hidden="true" className="mb-4 h-6 w-6 flex-none text-cyan-500" />
<dt className="font-semibold text-white">{feature.name}</dt>
<dd className="mt-2 text-gray-400">{feature.description}</dd>
</a>
))}
</dl>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,104 @@
'use client'
import { motion } from 'framer-motion'
import appleIcon from '@/images/apple.svg'
import windowsIcon from '@/images/windows.svg'
import androidIcon from '@/images/android.svg'
import linuxIcon from '@/images/linux.svg'
const features = [
{
name: 'Download for iOS & MacOS',
description: 'Download Mycelium App from the Apple Store.',
href: 'https://apps.apple.com/us/app/mycelium-network/id6504277565',
icon: appleIcon,
alt: 'Apple logo',
},
{
name: 'Download for Windows',
description: 'Download the Mycelium App for Windows directly from its Github repository.',
href: 'https://github.com/threefoldtech/myceliumflut/releases',
icon: windowsIcon,
alt: 'Windows logo',
},
{
name: 'Download for Android',
description: 'Download Mycelium from the Google Play Store.',
href: 'https://play.google.com/store/apps/details?id=tech.threefold.mycelium&pli=1',
icon: androidIcon,
alt: 'Android logo',
},
{
name: 'Download for Linux',
description: 'Download the Mycelium binary for Linux directly from its Github repository.',
href: 'https://github.com/threefoldtech/mycelium/releases',
icon: linuxIcon,
alt: 'Linux logo',
},
]
export function DownloadHero() {
return (
<div className="py-16 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl lg:mx-0">
<motion.h2
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="text-5xl font-medium tracking-tight text-gray-900 lg:text-6xl"
>
Download Mycelium Network
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
className="mt-6 text-lg text-gray-600 lg:leading-8"
>
Get Mycelium Network for Android, Windows, macOS, and iOS to securely connect, store, and interact with the decentralized networkseamlessly and efficiently. Not sure how it works?{' '}
<a
href="https://threefold.info/mycelium_network/docs/"
className="font-semibold text-gray-900 underline transition-colors hover:text-cyan-500"
target="_blank"
rel="noopener noreferrer"
>
Read the manual.
</a>
</motion.p>
</div>
<div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-none">
<dl className="grid max-w-xl grid-cols-1 gap-x-8 gap-y-16 md:grid-cols-2 lg:max-w-none lg:grid-cols-4">
{features.map((feature) => (
<div
key={feature.name}
className="flex flex-col rounded-lg border border-gray-200 p-8 shadow-sm transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20"
>
<dt className="text-base font-semibold leading-7 text-gray-900">
<div className="mb-6 flex h-10 w-10 items-center justify-center">
<img src={feature.icon} alt={feature.alt} className="h-10 w-10" />
</div>
{feature.name}
</dt>
<dd className="mt-1 flex flex-auto flex-col text-base leading-7 text-gray-600">
<p className="flex-auto">{feature.description}</p>
<p className="mt-6">
<a
href={feature.href}
className="text-sm font-semibold leading-6 text-cyan-500 transition-colors hover:text-cyan-400"
target="_blank"
rel="noopener noreferrer"
>
Download Now <span aria-hidden="true"></span>
</a>
</p>
</dd>
</div>
))}
</dl>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,17 @@
import { AnimatedSection } from '@/components/AnimatedSection'
import { DownloadHero } from './DownloadHero'
import { DevHub } from './DevHub'
export default function DownloadPage() {
return (
<>
<AnimatedSection>
<DownloadHero />
</AnimatedSection>
<AnimatedSection>
<DevHub />
</AnimatedSection>
</>
)
}

View File

@@ -0,0 +1,43 @@
import { CircleBackground } from '../../components/CircleBackground'
import { Container } from '../../components/Container'
import { Button } from '../../components/Button'
export function CallToAction() {
return (
<section
id="get-started"
className="relative overflow-hidden bg-gray-900 py-32"
>
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<CircleBackground color="#06b6d4" className="animate-spin-slower" />
</div>
<Container className="relative">
<div className="mx-auto max-w-2xl text-center">
<h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-white sm:text-4xl">
Activate Your Sovereign <br />Mycelium Stack
</h2>
<p className="mt-6 text-lg text-gray-300">
Provision cloud workloads, mesh them through the encrypted Mycelium Network, and unlock AI experiences without surrendering control of your infrastructure or your data.
</p>
<div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4">
<Button to="/cloud" variant="solid" color="white">
Start Deployment
</Button>
<Button to="/download" variant="solid" color="cyan">
Get Mycelium Connector
</Button>
<Button
to="https://threefold.info/mycelium_network/docs/"
as="a"
target="_blank"
variant="outline"
color="white"
>
Read Docs
</Button>
</div>
</div>
</Container>
</section>
)
}

View File

@@ -0,0 +1,65 @@
import { H2, P } from '@/components/Texts'
import { Button } from '@/components/Button'
import { LayoutTextFlip } from '@/components/ui/layout-text-flip' // make sure this import path is correct
export function HomeAgent() {
return (
<div className="relative isolate overflow-hidden bg-white">
<div className="px-6 py-24 sm:py-32 lg:px-8">
<div className="mx-auto max-w-4xl text-center">
<H2>
Deploy your own{" "}
<span className="text-left text-black font-medium text-7xl italic bg-clip-text bg-gradient-to-r from-blue-400 via-cyan-400 to-violet-400">
<LayoutTextFlip
text=""
words={[
"GPT-5",
"Claude 3.5",
"Gemini 1.5",
"Mistral 7B",
"Llama 3.1",
"AI Agents",
]}
/>
</span>
</H2>
<P className="mx-auto mt-6 max-w-xl text-lg/8 text-pretty text-gray-600">
Mycelium delivers enterprise-grade AI experiences that run on sovereign compute and the Mycelium Network, so production agents can reason over private data, collaborate across sites, and stay fully under your governance.
</P>
<div className="mt-10 flex items-center justify-center gap-x-6">
<Button variant="solid" color="cyan" href="/signup">
Get started
</Button>
<a href="/agents" className="text-sm/6 font-semibold text-gray-900 hover:text-gray-600">
Learn more <span aria-hidden="true"></span>
</a>
</div>
</div>
</div>
<svg
viewBox="0 0 1024 1024"
aria-hidden="true"
className="absolute top-1/2 left-1/2 -z-10 size-256 -translate-x-1/2 mask-[radial-gradient(closest-side,white,transparent)]"
>
<circle
r={512}
cx={512}
cy={512}
fill="url(#8d958450-c69f-4251-94bc-4e091a323369)"
fillOpacity="0.7"
/>
<defs>
<radialGradient id="8d958450-c69f-4251-94bc-4e091a323369" cx="50%" cy="50%" r="50%">
<stop offset="0%" stopColor="#60A5FA" /> {/* blue-400 */}
<stop offset="50%" stopColor="#06B6D4" /> {/* cyan-500 */}
<stop offset="100%" stopColor="#A78BFA" /> {/* violet-400 */}
</radialGradient>
</defs>
</svg>
</div>
)
}

View File

@@ -0,0 +1,35 @@
"use client";
import { H1, H5 } from "@/components/Texts";
import { ScrollDownArrow } from '@/components/ScrollDownArrow';
export function HomeAurora() {
return (
<div
style={{
backgroundImage: "url(/images/mchip2.webp)",
backgroundSize: "cover",
backgroundPosition: "center",
}}
className="relative mx-auto flex min-h-screen flex-col items-center justify-center gap-6 px-4 pb-24 pt-24 text-gray-800 lg:pb-0"
>
<div className="text-center">
<H1>
<span className="text-bold lg:text-8xl">
Full Sovereignty for<br />Cloud, Network & AI.
</span>
</H1>
</div>
<div className="max-w-4xl text-center font-light text-gray-500">
<H5>
Build and run mission-critical workloads on distributed compute,
encrypted networking, and sovereign AI orchestration all under your
control, without centralized gatekeepers.
</H5>
</div>
<div className="pt-6">
<ScrollDownArrow />
</div>
</div>
);
}

View File

@@ -0,0 +1,279 @@
import { cn } from "@/lib/utils";
import createGlobe from "cobe";
import { useEffect, useRef, type ReactNode } from "react";
import { motion } from "motion/react";
import { IconBrandYoutubeFilled } from "@tabler/icons-react";
import { LockClosedIcon, CogIcon, BoltIcon, CurrencyDollarIcon } from '@heroicons/react/24/solid'
import { H2, P } from '@/components/Texts'
export function HomeBenefits() {
const features = [
{
title: "Sovereign",
description:
"Own your infrastructure and your data. Mycelium Cloud eliminates dependency on centralized providers, giving you full digital sovereignty.",
icon: <LockClosedIcon className="h-6 w-6 text-cyan-500" />,
},
{
title: "Autonomous",
description:
"The cloud that runs itself. From deployment to scaling, Mycelium Cloud automates everything — so your systems stay fast, resilient, and adaptive.",
icon: <CogIcon className="h-6 w-6 text-cyan-500" />,
},
{
title: "Energy Efficient",
description:
"Built on distributed nodes designed for minimal energy use, Mycelium Cloud redefines sustainability without compromising performance.",
icon: <BoltIcon className="h-6 w-6 text-cyan-500" />,
},
{
title: "Cost Efficient",
description:
"No middlemen. No inflated bills. Just pure compute power at a fraction of traditional cloud costs — optimized, transparent, and fair.",
icon: <CurrencyDollarIcon className="h-6 w-6 text-cyan-500" />,
},
];
return (
<div className="relative z-20 py-10 lg:py-40 max-w-7xl mx-auto">
<div className="px-8">
<H2 className="text-3xl lg:text-5xl lg:leading-tight max-w-5xl mx-auto text-center tracking-tight font-medium text-black dark:text-white">
Why It Changes Everything
</H2>
<P className="text-sm lg:text-base max-w-2xl my-4 mx-auto text-neutral-500 text-center font-normal dark:text-neutral-300">
Project Mycelium is a new foundation for digital independence. A self-governing, AI-powered infrastructure that gives you control, efficiency, and trust without compromise.
</P>
</div>
<div className="relative ">
<div className="mt-12 rounded-2xl border border-neutral-200/70 bg-white/40 dark:bg-black/40 dark:border-neutral-800/60 backdrop-blur">
<div className="grid grid-cols-1 md:grid-cols-2 divide-y md:divide-y-0 md:divide-x divide-neutral-200/70 dark:divide-neutral-800/60">
{features.map((feature) => (
<FeatureCard key={feature.title}>
<FeatureTitle icon={feature.icon}>{feature.title}</FeatureTitle>
<FeatureDescription>{feature.description}</FeatureDescription>
</FeatureCard>
))}
</div>
</div>
</div>
</div>
);
}
const FeatureCard = ({
children,
className,
}: {
children?: ReactNode;
className?: string;
}) => {
return (
<div className={cn(`p-6 lg:p-8 relative overflow-hidden`, className)}>
{children}
</div>
);
};
const FeatureTitle = ({ children, icon }: { children?: ReactNode; icon?: ReactNode }) => {
return (
<div className="flex items-center gap-2">
{icon}
<p className="max-w-5xl text-left tracking-tight text-black dark:text-white text-xl md:text-2xl md:leading-snug">
{children}
</p>
</div>
);
};
const FeatureDescription = ({ children }: { children?: ReactNode }) => {
return (
<p
className={cn(
"text-sm md:text-base max-w-4xl",
"text-neutral-500 font-normal dark:text-neutral-300",
"text-left max-w-sm mx-0 md:text-sm my-2"
)}
>
{children}
</p>
);
};
export const SkeletonOne = () => {
return (
<div className="relative flex py-8 px-2 gap-10 h-full">
<div className="w-full p-5 mx-auto bg-white dark:bg-neutral-900 shadow-2xl group h-full">
<div className="flex flex-1 w-full h-full flex-col space-y-2 ">
{/* TODO */}
<img
src="/linear.webp"
alt="header"
width={800}
height={800}
className="h-full w-full aspect-square object-cover object-left-top rounded-sm"
/>
</div>
</div>
<div className="absolute bottom-0 z-40 inset-x-0 h-60 bg-gradient-to-t from-white dark:from-black via-white dark:via-black to-transparent w-full pointer-events-none" />
<div className="absolute top-0 z-40 inset-x-0 h-60 bg-gradient-to-b from-white dark:from-black via-transparent to-transparent w-full pointer-events-none" />
</div>
);
};
export const SkeletonThree = () => {
return (
<a
href="https://www.youtube.com/watch?v=RPa3_AD1_Vs"
target="__blank"
className="relative flex gap-10 h-full group/image"
>
<div className="w-full mx-auto bg-transparent dark:bg-transparent group h-full">
<div className="flex flex-1 w-full h-full flex-col space-y-2 relative">
{/* TODO */}
<IconBrandYoutubeFilled className="h-20 w-20 absolute z-10 inset-0 text-red-500 m-auto " />
<img
src="https://assets.aceternity.com/fireship.jpg"
alt="header"
width={800}
height={800}
className="h-full w-full aspect-square object-cover object-center rounded-sm blur-none group-hover/image:blur-md transition-all duration-200"
/>
</div>
</div>
</a>
);
};
export const SkeletonTwo = () => {
const images = [
"https://images.unsplash.com/photo-1517322048670-4fba75cbbb62?q=80&w=3000&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1573790387438-4da905039392?q=80&w=3425&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1555400038-63f5ba517a47?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1554931670-4ebfabf6e7a9?q=80&w=3387&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://images.unsplash.com/photo-1546484475-7f7bd55792da?q=80&w=2581&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
];
const imageVariants = {
whileHover: {
scale: 1.1,
rotate: 0,
zIndex: 100,
},
whileTap: {
scale: 1.1,
rotate: 0,
zIndex: 100,
},
};
return (
<div className="relative flex flex-col items-start p-8 gap-10 h-full overflow-hidden">
{/* TODO */}
<div className="flex flex-row -ml-20">
{images.map((image, idx) => (
<motion.div
variants={imageVariants}
key={"images-first" + idx}
style={{
rotate: Math.random() * 20 - 10,
}}
whileHover="whileHover"
whileTap="whileTap"
className="rounded-xl -mr-4 mt-4 p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 border border-neutral-100 shrink-0 overflow-hidden"
>
<img
src={image}
alt="bali images"
width="500"
height="500"
className="rounded-lg h-20 w-20 md:h-40 md:w-40 object-cover shrink-0"
/>
</motion.div>
))}
</div>
<div className="flex flex-row">
{images.map((image, idx) => (
<motion.div
key={"images-second" + idx}
style={{
rotate: Math.random() * 20 - 10,
}}
variants={imageVariants}
whileHover="whileHover"
whileTap="whileTap"
className="rounded-xl -mr-4 mt-4 p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 border border-neutral-100 shrink-0 overflow-hidden"
>
<img
src={image}
alt="bali images"
width="500"
height="500"
className="rounded-lg h-20 w-20 md:h-40 md:w-40 object-cover shrink-0"
/>
</motion.div>
))}
</div>
<div className="absolute left-0 z-[100] inset-y-0 w-20 bg-gradient-to-r from-white dark:from-black to-transparent h-full pointer-events-none" />
<div className="absolute right-0 z-[100] inset-y-0 w-20 bg-gradient-to-l from-white dark:from-black to-transparent h-full pointer-events-none" />
</div>
);
};
export const SkeletonFour = () => {
return (
<div className="h-60 md:h-60 flex flex-col items-center relative bg-transparent dark:bg-transparent mt-10">
<Globe className="absolute -right-10 md:-right-10 -bottom-80 md:-bottom-72" />
</div>
);
};
export const Globe = ({ className }: { className?: string }) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
let phi = 0;
if (!canvasRef.current) return;
const globe = createGlobe(canvasRef.current, {
devicePixelRatio: 2,
width: 600 * 2,
height: 600 * 2,
phi: 0,
theta: 0,
dark: 1,
diffuse: 1.2,
mapSamples: 16000,
mapBrightness: 6,
baseColor: [0.3, 0.3, 0.3],
markerColor: [0.1, 0.8, 1],
glowColor: [1, 1, 1],
markers: [
// longitude latitude
{ location: [37.7595, -122.4367], size: 0.03 },
{ location: [40.7128, -74.006], size: 0.1 },
],
onRender: (state) => {
// Called on every animation frame.
// `state` will be an empty object, return updated params.
state.phi = phi;
phi += 0.01;
},
});
return () => {
globe.destroy();
};
}, []);
return (
<canvas
ref={canvasRef}
style={{ width: 600, height: 600, maxWidth: "100%", aspectRatio: 1 }}
className={className}
/>
);
};

View File

@@ -0,0 +1,66 @@
import { CheckCircleIcon } from '@heroicons/react/20/solid'
import { H2, P } from '@/components/Texts'
const benefits = [
'Decentralized Infrastructure',
'End-to-End Encryption',
'Sovereign Data Control',
'Scalable Kubernetes Clusters',
'Censorship-Resistant',
'Peer-to-Peer Networking',
]
export function HomeCloud() {
return (
<div className="overflow-hidden bg-white py-24">
<div className="relative isolate">
<div className="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div className="mx-auto flex max-w-2xl flex-col gap-16 bg-white px-6 py-16 shadow-lg ring-1 ring-gray-200/5 sm:rounded-3xl sm:p-8 lg:mx-0 lg:max-w-none lg:flex-row lg:items-center lg:py-20 xl:gap-x-20 xl:px-20">
<img
alt=""
src="/images/kubernetes.png"
className="h-96 w-full flex-none rounded-2xl object-cover lg:aspect-square lg:h-auto lg:max-w-sm"
/>
<div className="w-full flex-auto">
<H2 className="">
Mycelium <span className="font-medium text-7xl italic">Cloud</span>
</H2>
<P className="mt-6 text-lg/8 text-pretty text-gray-600">
A comprehensive platform for deploying and managing Kubernetes clusters on the decentralized Mycelium Grid infrastructure
</P>
<ul
role="list"
className="mt-10 grid grid-cols-1 gap-x-8 gap-y-3 text-base/7 text-gray-950 sm:grid-cols-2"
>
{benefits.map((benefit) => (
<li key={benefit} className="flex gap-x-3">
<CheckCircleIcon aria-hidden="true" className="h-7 w-5 flex-none text-cyan-500" />
{benefit}
</li>
))}
</ul>
<div className="mt-10 flex">
<a href="/cloud" className="text-sm/6 font-semibold text-cyan-600 hover:text-cyan-500">
Learn more
<span aria-hidden="true"> &rarr;</span>
</a>
</div>
</div>
</div>
</div>
<div
aria-hidden="true"
className="absolute inset-x-0 -top-16 -z-10 flex transform-gpu justify-center overflow-hidden blur-3xl"
>
<div
style={{
clipPath:
'polygon(73.6% 51.7%, 91.7% 11.8%, 100% 46.4%, 97.4% 82.2%, 92.5% 84.9%, 75.7% 64%, 55.3% 47.5%, 46.5% 49.4%, 45% 62.9%, 50.3% 87.2%, 21.3% 64.1%, 0.1% 100%, 5.4% 51.1%, 21.4% 63.9%, 58.9% 0.2%, 73.6% 51.7%)',
}}
className="aspect-1318/752 w-329.5 flex-none bg-linear-to-r from-[#9fe8fc] to-[#c6c4fa] opacity-50"
/>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,63 @@
import { InboxIcon, TrashIcon, UsersIcon } from '@heroicons/react/24/outline'
import { H2, P } from '@/components/Texts'
const features = [
{
name: 'Mycelium Network',
description:
"A global, end-to-end encrypted overlay that simply doesn't break.",
href: '/network',
icon: UsersIcon,
image: '/images/network_icon.png',
},
{
name: 'Mycelium Cloud',
description:
'An autonomous, stateless OS that enforces pre-deterministic deployments you define.',
href: '/cloud',
icon: TrashIcon,
image: '/images/cloud_icon.png',
},
{
name: 'Mycelium Agents',
description:
'Your sovereign agent with private memory and permissioned data access—always under your control.',
href: '/agents',
icon: InboxIcon,
image: '/images/agent_icon.png',
},
]
export function HomeFeatures() {
return (
<div className="">
<div className="relative bg-transparent pb-24 Pt-0 overflow-hidden">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl lg:mx-0">
<H2 className="">
The Building Blocks of <span className="font-medium text-7xl">Decentralized Future</span>
</H2>
<P className="mt-6 ">
From compute and networking to intelligent automation, these components work together to empower users, developers, and organizations to build freely, without intermediaries.
</P>
</div>
<div className="mx-auto mt-16 max-w-2xl lg:max-w-7xl">
<div className="grid grid-cols-1 gap-x-12 gap-y-12 lg:grid-cols-3">
{features.map((feature) => (
<div key={feature.name} className="relative flex flex-col p-8 rounded-3xl border border-gray-100 bg-white backdrop-blur-lg overflow-hidden shadow-lg hover:shadow-xl hover:border-cyan-500 hover:scale-105 transform transition-all duration-300">
<div className="w-30 h-30 bg-white/80 rounded-full flex items-center justify-center">
<img src={feature.image} alt="" className="w-25 h-25" />
</div>
<h3 className="mt-6 text-xl font-semibold text-black">{feature.name}</h3>
<p className="mt-4 text-base text-gray-800">{feature.description}</p>
<a href={feature.href} className="mt-6 text-base font-semibold text-black">Learn more <span aria-hidden="true"> &rarr;</span></a>
</div>
))}
</div>
</div>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,60 @@
import { GlobeAltIcon, ServerStackIcon, CpuChipIcon } from '@heroicons/react/24/solid'
import { H2, P } from '@/components/Texts'
const features = [
{
name: 'Mycelium Network',
description:
"A global, end-to-end encrypted overlay that simply doesn't break.",
href: '/network',
icon: GlobeAltIcon,
},
{
name: 'Mycelium Cloud',
description:
'An autonomous, stateless OS that enforces pre-deterministic deployments you define.',
href: '/cloud',
icon: ServerStackIcon,
},
{
name: 'Mycelium Agents',
description:
'Your sovereign agent with private memory and permissioned data access—always under your control.',
href: '/agents',
icon: CpuChipIcon,
},
]
export function HomeFeaturesDark() {
return (
<div className="">
<div className="relative bg-black py-24 overflow-hidden">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl lg:mx-0">
<H2 className="text-white">
The Building Blocks of Decentralized Future
</H2>
<P className="mt-6 text-gray-300">
From compute and networking to intelligent automation, these components work together to empower users, developers, and organizations to build freely, without intermediaries.
</P>
</div>
<div className="mx-auto mt-16 max-w-2xl lg:max-w-7xl">
<div className="grid grid-cols-1 gap-x-12 gap-y-12 lg:grid-cols-3">
{features.map((feature) => (
<div key={feature.name} className="relative flex flex-col p-8 rounded-3xl border border-gray-700 bg-gray-900/50 backdrop-blur-lg overflow-hidden shadow-2xl hover:shadow-cyan-500/40 hover:border-cyan-500 hover:scale-105 transform transition-all duration-300">
<div className="w-20 h-20 bg-gray-800/80 rounded-full flex items-center justify-center">
<feature.icon className="h-12 w-12 text-cyan-500" />
</div>
<h3 className="mt-6 text-xl font-semibold text-white">{feature.name}</h3>
<p className="mt-4 text-base text-gray-300">{feature.description}</p>
<a href={feature.href} className="mt-6 text-base font-semibold text-white">Learn more <span aria-hidden="true"> &rarr;</span></a>
</div>
))}
</div>
</div>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,123 @@
'use client'
import { Globe } from "@/components/ui/Globe"
import { motion } from "framer-motion"
import { P, CT, CP, SectionHeader, Eyebrow } from "@/components/Texts"
import { CountUpNumber } from '@/components/CountUpNumber'
export function WorldMap() {
return (
<div className="relative min-h-screen w-full overflow-hidden top-0 flex py-12 flex-col">
{/* Background video */}
<video
autoPlay
loop
muted
playsInline
className="absolute inset-0 w-full h-full object-cover"
>
<source src="/videos/benefits.mp4" type="video/mp4" />
</video>
{/* Dark overlay */}
<div className="absolute inset-0 bg-black/10" />
{/* Content */}
<div className="relative z-10 flex flex-col h-full px-8 md:px-16 py-12">
{/* Title + Subtitle */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.5, ease: "easeOut" }}
className="max-w-xl"
>
<Eyebrow color="accent">Decentralized Network</Eyebrow>
<SectionHeader color="light">Project Mycelium is Live.</SectionHeader>
<P className=" mt-4 text-base leading-relaxed" color="light">
Project Mycelium enables anyone to deploy
their own Internet infrastructure, anywhere.
</P>
</motion.div>
{/* Bottom Layout: Globe + Cards */}
<div className="mt-8 flex flex-1 flex-col lg:flex-row items-center lg:items-stretch gap-x-10">
{/* Globe Left Column */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.5, delay: 0.2, ease: "easeOut" }}
className="flex-1 flex items-center justify-center"
>
<div className="relative w-[450px] h-[450px] md:w-[600px] md:h-[600px]">
<Globe className="absolute inset-0 w-full h-full left-0 lg:-left-24" />
</div>
</motion.div>
{/* Cards Right Column */}
<div className="relative flex-1 lg:h-auto h-[700px] flex flex-col lg:block items-center gap-y-4 mt-8 lg:mt-0">
<motion.div
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.5, delay: 0.4, ease: "easeOut" }}
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"
>
<div><CT color="light" className="uppercase tracking-wide">CORES</CT></div>
<div><CountUpNumber end={54958} className="mt-2 text-3xl font-bold text-white" /></div>
<CP color="light" className="mt-2 text-sm">
Total Central Processing Unit Cores available on the grid.
</CP>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.5, delay: 0.5, ease: "easeOut" }}
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"
>
<div><CT color="light" className="uppercase tracking-wide">NODES</CT></div>
<div><CountUpNumber end={1493} className="mt-4 text-3xl font-bold text-white" /></div>
<CP color="light" className="mt-2 text-sm">
Total number of nodes on the grid.
</CP>
</motion.div>
<motion.div
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.5, delay: 0.6, ease: "easeOut" }}
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"
>
<div><CT color="light" className="uppercase tracking-wide">SSD CAPACITY</CT></div>
<div><CountUpNumber end={5388956} className="mt-2 text-3xl font-bold text-white" /></div>
<CP color="light" className="mt-2 text-sm">
Total GB amount of storage (SSD, HDD, & RAM) on the grid.
</CP>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.5, delay: 0.7, ease: "easeOut" }}
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"
>
<div><CT color="light" className="uppercase tracking-wide">COUNTRIES</CT></div>
<div><CountUpNumber end={44} className="mt-2 text-3xl font-bold text-white" /></div>
<CP color="light" className="mt-2 text-sm">
Total number of countries with active nodes.
</CP>
</motion.div>
</div>
</div>
</div>
{/* Radial fade overlay */}
<div className="pointer-events-none absolute inset-0 h-full bg-[radial-gradient(circle_at_50%_200%,rgba(0,0,0,0.2),rgba(255,255,255,0))]" />
</div>
)
}

View File

@@ -1,36 +0,0 @@
import { motion } from 'framer-motion'
import { TypeAnimation } from 'react-type-animation'
import { Container } from '../../components/Container'
export function HomeHero() {
return (
<section className="relative bg-white py-20 lg:py-32">
<Container>
<div className="mx-auto max-w-4xl text-center">
<h1 className="text-4xl lg:text-6xl font-medium tracking-tight text-gray-900">
<TypeAnimation
sequence={[
'Decentralized Autonomous Agentic Cloud.',
1000,
]}
wrapper="span"
speed={50}
repeat={0}
/>
</h1>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 1 }}
className="mt-8"
>
<p className="text-lg lg:text-xl text-gray-600">
Mycelium's advancements in Agentic infrastructure supports private, secure and autonomous Agents that connect, learn and grow with you.
</p>
</motion.div>
</div>
</Container>
</section>
)
}

View File

@@ -0,0 +1,69 @@
"use client";
import WorldMap from "@/components/ui/world-map";
import { motion } from "motion/react";
import { H2, P } from "@/components/Texts";
export function HomeMapSection() {
return (
<div className=" py-24 dark:bg-black bg-tra w-full">
<div className="max-w-7xl mx-auto text-center">
<H2 className="font-medium text-xl md:text-4xl dark:text-white text-gray-800">
Mycelium Network is{" "}
<span className="text-black text-bold italic">
{"Live.".split("").map((word, idx) => (
<motion.span
key={idx}
className="inline-block"
initial={{ x: -10, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ duration: 0.5, delay: idx * 0.04 }}
>
{word}
</motion.span>
))}
</span>
</H2>
<P className="text-sm md:text-lg text-neutral-500 max-w-2xl mx-auto py-4">
Mycelium Network's advancement technology enables anyone to deploy
their own Internet infrastructure, anywhere.
</P>
</div>
<div className="max-w-5xl mx-auto">
<WorldMap
dots={[
{
start: {
lat: 64.2008,
lng: -149.4937,
}, // Alaska (Fairbanks)
end: {
lat: 34.0522,
lng: -118.2437,
}, // Los Angeles
},
{
start: { lat: 64.2008, lng: -149.4937 }, // Alaska (Fairbanks)
end: { lat: -15.7975, lng: -47.8919 }, // Brazil (Brasília)
},
{
start: { lat: -15.7975, lng: -47.8919 }, // Brazil (Brasília)
end: { lat: 38.7223, lng: -9.1393 }, // Lisbon
},
{
start: { lat: 51.5074, lng: -0.1278 }, // London
end: { lat: 28.6139, lng: 77.209 }, // New Delhi
},
{
start: { lat: 28.6139, lng: 77.209 }, // New Delhi
end: { lat: 43.1332, lng: 131.9113 }, // Vladivostok
},
{
start: { lat: 28.6139, lng: 77.209 }, // New Delhi
end: { lat: -1.2921, lng: 36.8219 }, // Nairobi
},
]}
/>
</div>
</div>
);
}

View File

@@ -1,21 +1,38 @@
import { AnimatedSection } from '../../components/AnimatedSection' import { AnimatedSection } from '../../components/AnimatedSection'
import { HomeHero } from './HomeHero' import { HomeAurora } from './HomeAurora'
import { WorldMapSection } from './WorldMapSection' import { HomeFeaturesDark } from './HomeFeaturesDark'
import { StackSection } from './StackSection' import { StackSectionLight } from './StackSection'
import { WorldMap } from './HomeGlobe'
import { HomeBenefits } from './HomeBenefits'
import { CallToAction } from './CallToAction'
export default function HomePage() { export default function HomePage() {
return ( return (
<div> <div>
<AnimatedSection> <AnimatedSection>
<HomeHero /> <HomeAurora />
</AnimatedSection> </AnimatedSection>
<AnimatedSection> <AnimatedSection id="next-section">
<WorldMapSection /> <WorldMap />
</AnimatedSection> </AnimatedSection>
<AnimatedSection> <AnimatedSection>
<StackSection /> <StackSectionLight />
</AnimatedSection>
<AnimatedSection>
<HomeFeaturesDark />
</AnimatedSection>
<AnimatedSection>
<HomeBenefits />
</AnimatedSection>
<AnimatedSection>
<CallToAction />
</AnimatedSection> </AnimatedSection>
</div> </div>
) )

View File

@@ -1,66 +1,75 @@
import { motion } from 'framer-motion' "use client";
import { Container } from '../../components/Container'
const stackData = [ import { motion } from "framer-motion";
{ import { StackedCubesLight } from "@/components/ui/StackedCubesLight";
id: 'agent', import { P, SectionHeader, Eyebrow } from "@/components/Texts";
title: 'Agent Layer', import { FadeIn } from "@/components/ui/FadeIn";
description: import { DottedGlowBackground } from '@/components/ui/dotted-glow-background';
'Your sovereign agent with private memory and permissioned data access—always under your control. Choose from a wide library of open-source LLMs, paired with built-in semantic search and retrieval.',
},
{
id: 'network',
title: 'Network Layer',
description:
'A global, end-to-end encrypted overlay that simply doesn\'t break. Shortest-path routing moves your traffic the fastest way, every time with instant discovery.',
},
{
id: 'cloud',
title: 'Cloud Layer',
description:
'An autonomous, stateless OS that enforces pre-deterministic deployments you define. Workloads are cryptographically bound to your private key—location and access are yours.',
},
]
export function StackSection() { export function StackSectionLight() {
return ( return (
<section className="relative bg-white py-20 lg:py-32"> <section className="relative w-full overflow-hidden py-24 lg:py-40">
<Container> {/* === Background Layer === */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12 items-start"> <div className="absolute inset-0 -z-10 bg-transparent">
{/* Left Column - Text */} {/* Dotted Glow Background */}
<motion.div <DottedGlowBackground
initial={{ opacity: 0, y: 20 }} gap={15}
whileInView={{ opacity: 1, y: 0 }} radius={2}
viewport={{ once: true }} color="rgba(0,0,0,0.4)"
transition={{ duration: 0.5 }} glowColor="rgba(0,170,255,0.85)"
className="lg:col-span-1" opacity={0.2}
> />
<h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900"> {/* Faint 3D grid floor */}
The Mycelium Stack <div className="absolute inset-0 flex items-end justify-center overflow-hidden">
</h2> <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" />
<p className="mt-6 text-lg 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>
</motion.div>
{/* Right Column - Stack Cards */}
<div className="lg:col-span-2 space-y-6">
{stackData.map((layer, index) => (
<motion.div
key={layer.id}
initial={{ opacity: 0, x: 20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }}
className="rounded-2xl bg-gray-50 border border-gray-200 p-6 hover:border-cyan-500 hover:shadow-lg transition-all duration-300"
>
<h3 className="text-xl font-semibold text-gray-900">{layer.title}</h3>
<p className="mt-3 text-gray-600">{layer.description}</p>
</motion.div>
))}
</div>
</div> </div>
</Container> </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">
Project Mycelium unifies compute, storage, networking, and AI into a resilient
ecosystem that preserves data sovereignty, powers seamless collaboration,
and keeps every layer of your infrastructure secure end to end on decentralized infrastructure.
</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> </section>
) );
} }

View File

@@ -1,106 +0,0 @@
import { motion } from 'framer-motion'
import { Globe } from '../../components/ui/Globe'
import { CountUpNumber } from '../../components/CountUpNumber'
import { Container } from '../../components/Container'
export function WorldMapSection() {
return (
<section className="relative bg-gray-50 py-20 lg:py-32">
<Container>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="mb-12"
>
<h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900">
Mycelium Network is Live.
</h2>
</motion.div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
{/* Globe */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.2 }}
className="flex items-center justify-center"
>
<div className="relative w-full max-w-[500px] aspect-square">
<Globe className="w-full h-full" />
</div>
</motion.div>
{/* Stats Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.3 }}
className="rounded-2xl bg-white border border-gray-200 p-6 shadow-sm hover:shadow-md transition-shadow"
>
<p className="text-sm font-semibold uppercase tracking-wide text-cyan-500">CORES</p>
<div className="mt-2 text-3xl font-bold text-gray-900">
<CountUpNumber end={54958} />
</div>
<p className="mt-2 text-sm text-gray-600">
Total Central Processing Unit Cores available on the grid.
</p>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.4 }}
className="rounded-2xl bg-white border border-gray-200 p-6 shadow-sm hover:shadow-md transition-shadow"
>
<p className="text-sm font-semibold uppercase tracking-wide text-cyan-500">NODES</p>
<div className="mt-2 text-3xl font-bold text-gray-900">
<CountUpNumber end={1493} />
</div>
<p className="mt-2 text-sm text-gray-600">
Total number of nodes on the grid.
</p>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.5 }}
className="rounded-2xl bg-white border border-gray-200 p-6 shadow-sm hover:shadow-md transition-shadow"
>
<p className="text-sm font-semibold uppercase tracking-wide text-cyan-500">SSD CAPACITY</p>
<div className="mt-2 text-3xl font-bold text-gray-900">
<CountUpNumber end={5388956} />
</div>
<p className="mt-2 text-sm text-gray-600">
Total GB amount of storage (SSD, HDD, & RAM) on the grid.
</p>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.6 }}
className="rounded-2xl bg-white border border-gray-200 p-6 shadow-sm hover:shadow-md transition-shadow"
>
<p className="text-sm font-semibold uppercase tracking-wide text-cyan-500">COUNTRIES</p>
<div className="mt-2 text-3xl font-bold text-gray-900">
<CountUpNumber end={44} />
</div>
<p className="mt-2 text-sm text-gray-600">
Total number of countries with active nodes.
</p>
</motion.div>
</div>
</div>
</Container>
</section>
)
}

View File

@@ -21,7 +21,7 @@ export function CallToAction() {
</p> </p>
<div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4"> <div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4">
<Button to="/download" variant="solid" color="white"> <Button to="/download" variant="solid" color="white">
Get Mycelium Get Mycelium Connector
</Button> </Button>
<Button <Button
to="https://threefold.info/mycelium_network/docs/" to="https://threefold.info/mycelium_network/docs/"

View File

@@ -1,6 +1,7 @@
import { useId } from 'react' import { useId } from 'react'
import { Container } from '../../components/Container' import { Container } from '../../components/Container'
import { Button } from '../../components/Button' import { Button } from '../../components/Button'
import phoneFrame from '../../images/phoneframe.png'
function BackgroundIllustration(props: React.ComponentPropsWithoutRef<'div'>) { function BackgroundIllustration(props: React.ComponentPropsWithoutRef<'div'>) {
let id = useId() let id = useId()
@@ -73,32 +74,32 @@ function BackgroundIllustration(props: React.ComponentPropsWithoutRef<'div'>) {
export function Hero() { export function Hero() {
return ( return (
<div className="overflow-hidden lg:py-32 lg:pb-0 pb-24"> <div className="overflow-hidden pb-24 lg:py-32 lg:pb-0">
<Container> <Container>
<div className="flex flex-col-reverse gap-y-16 lg:grid lg:grid-cols-12 lg:gap-x-8 lg:gap-y-20"> <div className="flex flex-col-reverse gap-y-16 lg:grid lg:grid-cols-12 lg:gap-x-8 lg:gap-y-20">
<div className="relative z-10 mx-auto max-w-2xl lg:col-span-7 lg:max-w-none lg:pt-6 xl:col-span-6"> <div className="relative z-10 mx-auto max-w-2xl lg:col-span-7 lg:max-w-none lg:pt-6 xl:col-span-6">
<h1 className="text-4xl lg:text-6xl font-medium tracking-tight text-gray-900"> <h1 className="text-4xl font-medium tracking-tight text-gray-900 lg:text-6xl">
Mycelium Network Mycelium Network
</h1> </h1>
<h2 className="mt-6 lg:text-2xl text-xl tracking-tight leading-normal text-gray-600"> <h2 className="mt-6 text-xl leading-normal tracking-tight text-gray-600 lg:text-2xl">
Unleashing the Power of Decentralized Networks Unleashing the Power of Decentralized Networks
</h2> </h2>
<p className="mt-6 lg:text-xl text-lg text-gray-600 lg:leading-normal leading-tight"> <p className="mt-6 text-lg leading-tight text-gray-600 lg:text-xl lg:leading-normal">
Discover Mycelium, an end-to-end encrypted IPv6 overlay network. The future of secure, efficient, and scalable networking. Discover Mycelium, an end-to-end encrypted IPv6 overlay network. The future of secure, efficient, and scalable networking.
</p> </p>
<div className="mt-8 flex flex-wrap gap-x-6 gap-y-4"> <div className="mt-8 flex flex-wrap gap-x-6 gap-y-4">
<Button to="/download" variant="solid" color="cyan"> <Button to="/download" variant="solid" color="cyan">
Get Mycelium Get Mycelium Connector
</Button> </Button>
</div> </div>
</div> </div>
<div className="relative lg:mt-10 mt-0 lg:col-span-5 lg:row-span-2 xl:col-span-6"> <div className="relative mt-0 lg:mt-10 lg:col-span-5 lg:row-span-2 xl:col-span-6">
<BackgroundIllustration className="absolute top-4 left-1/2 h-[1026px] w-[1026px] -translate-x-1/2 stroke-gray-300/70 sm:top-16 lg:-top-12 lg:ml-12 ml-0" /> <BackgroundIllustration className="absolute left-1/2 top-4 h-[1026px] w-[1026px] -translate-x-1/2 stroke-gray-300/70 sm:top-16 lg:-top-12 lg:ml-12" />
<div className="mx-auto h-[448px] mask-[linear-gradient(to_bottom,white_60%,transparent)] lg:px-0 lg:absolute lg:-inset-x-10 lg:-top-24 lg:h-auto lg:pt-10 xl:-bottom-32"> <div className="mx-auto h-[460px] max-w-[520px] mask-[linear-gradient(to_bottom,white_60%,transparent)] lg:absolute lg:-inset-x-10 lg:-top-24 lg:h-auto lg:pt-10 xl:-bottom-32">
<img <img
src="/src/images/phoneframe.png" src={phoneFrame}
alt="Mycelium application demo" alt="Mycelium application demo"
className="mx-auto max-w-[366px]" className="mx-auto h-full w-auto max-w-[366px]"
width={366} width={366}
height={729} height={729}
/> />

View File

@@ -6,7 +6,6 @@ import clsx from 'clsx'
import { import {
type MotionProps, type MotionProps,
type Variant, type Variant,
type Variants,
AnimatePresence, AnimatePresence,
motion, motion,
} from 'framer-motion' } from 'framer-motion'
@@ -137,12 +136,6 @@ function DeviceTouchIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
) )
} }
const headerAnimation: Variants = {
initial: { opacity: 0, transition: { duration: 0.3 } },
animate: { opacity: 1, transition: { duration: 0.3, delay: 0.3 } },
exit: { opacity: 0, transition: { duration: 0.3 } },
}
const maxZIndex = 2147483647 const maxZIndex = 2147483647
const bodyVariantBackwards: Variant = { const bodyVariantBackwards: Variant = {

View File

@@ -7,7 +7,6 @@ type Props = {
const ACCENT = '#00b8db'; const ACCENT = '#00b8db';
const STROKE = '#111827'; const STROKE = '#111827';
const GRAY = '#9CA3AF';
const GRAY_LT = '#E5E7EB'; const GRAY_LT = '#E5E7EB';
const IconSquare = () => ( const IconSquare = () => (

View File

@@ -1,4 +1,7 @@
@import 'tailwindcss'; @import 'tailwindcss';
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@theme { @theme {
--animate-scroll: scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite; --animate-scroll: scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite;
@@ -70,7 +73,10 @@
--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-inter); --font-sans: 'Mulish', system-ui, Avenir, Helvetica, Arial, sans-serif;
--font-neuton: 'Neuton', serif;
--font-family-neuton: var(--font-neuton);
--container-2xl: 40rem; --container-2xl: 40rem;
@@ -91,10 +97,127 @@
@theme inline { @theme inline {
--animate-marquee: marquee var(--marquee-duration) linear infinite; --animate-marquee: marquee var(--marquee-duration) linear infinite;
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--color-foreground: var(--foreground);
--color-background: var(--background);
@keyframes marquee { @keyframes marquee {
100% { 100% {
transform: translateY(-50%); transform: translateY(-50%);
} }
} }
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px)
}
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
} }

View File

@@ -1,4 +1,10 @@
{ {
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"files": [], "files": [],
"references": [ "references": [
{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.app.json" },

View File

@@ -10,4 +10,22 @@ export default defineConfig({
'@': path.resolve(__dirname, './src'), '@': path.resolve(__dirname, './src'),
}, },
}, },
build: {
rollupOptions: {
output: {
manualChunks: {
react: ['react', 'react-dom', 'react-router', 'react-router-dom'],
framer: ['framer-motion'],
motion: ['motion/react'],
antd: [
'antd',
'rc-field-form',
'rc-motion',
'@ant-design/cssinjs',
'@rc-component/async-validator',
],
},
},
},
},
}) })