13 Commits

Author SHA1 Message Date
2988ce5335 chore: update favicon and remove unused favicon assets 2025-10-15 16:55:30 +02:00
4934dc7f35 feat: add favicon.ico to metadata configuration in root layout 2025-10-15 16:51:39 +02:00
acd46171c8 style: add hover scale effect and improve download buttons layout 2025-10-15 16:47:30 +02:00
1494a83812 feat: add success state and green button variant for newsletter signup form 2025-10-15 16:35:47 +02:00
ae277d33b5 feat: add newsletter subscription form with loading and error states in Footer component 2025-10-15 16:31:22 +02:00
794605117a style: update link colors and hover states for documentation and support links 2025-10-15 16:21:19 +02:00
39e19a95d0 style: replace border with outline and update animation colors to cyan 2025-10-15 16:12:24 +02:00
e598e2ffb1 feat: enhance UI with hover effects, animations and add download links 2025-10-15 16:08:31 +02:00
5d37cb4b3b fix: update logo path from logo.svg to logomark.svg in Footer component 2025-10-15 15:47:35 +02:00
607a31e96d refactor: update DevHub component styling with bordered feature cards and code bracket icons 2025-10-15 15:42:17 +02:00
50f8ae3d69 refactor: update download link to internal route and hide demo button 2025-10-15 15:37:23 +02:00
4056d31743 fix: update logo import path in Footer component to use alias 2025-10-15 15:33:25 +02:00
4b5d1c7f00 refactor: import phone frame SVG directly instead of using public path 2025-10-15 15:29:15 +02:00
23 changed files with 238 additions and 76 deletions

View File

@@ -0,0 +1,48 @@
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
const { email } = await req.json();
if (!email) {
return NextResponse.json({ error: 'Email is required' }, { status: 400 });
}
const MAILERLITE_API_KEY = process.env.MAILERLITE_API_KEY;
const MAILERLITE_GROUP_ID = process.env.MAILERLITE_GROUP_ID;
if (!MAILERLITE_API_KEY || !MAILERLITE_GROUP_ID) {
return NextResponse.json(
{ error: 'MailerLite API key or Group ID are not configured' },
{ status: 500 },
);
}
try {
const response = await fetch(
`https://api.mailerlite.com/api/v2/groups/${MAILERLITE_GROUP_ID}/subscribers`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-MailerLite-ApiKey': MAILERLITE_API_KEY,
},
body: JSON.stringify({ email }),
},
);
if (!response.ok) {
const errorData = await response.json();
return NextResponse.json(
{ error: errorData.error.message || 'Something went wrong' },
{ status: response.status },
);
}
return NextResponse.json({ success: true }, { status: 200 });
} catch (error) {
return NextResponse.json(
{ error: 'An unexpected error occurred' },
{ status: 500 },
);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -17,6 +17,9 @@ export const metadata: Metadata = {
}, },
description: description:
'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.',
icons: {
icon: '/favicon.ico',
},
} }
export default function RootLayout({ export default function RootLayout({

View File

@@ -10,7 +10,7 @@ export function About() {
className="relative overflow-hidden bg-gray-900 py-20 lg:py-32 lg:top-0 top-0" className="relative overflow-hidden bg-gray-900 py-20 lg:py-32 lg:top-0 top-0"
> >
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"> <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<CircleBackground color="#fff" className="animate-spin-slower" /> <CircleBackground color="#06b6d4" className="animate-spin-slower" />
</div> </div>
<Container className="relative"> <Container className="relative">
<div className="mx-auto max-w-3xl text-center"> <div className="mx-auto max-w-3xl text-center">

View File

@@ -11,7 +11,7 @@ export function AndroidLink({
href="#" href="#"
aria-label="Download for Android" aria-label="Download for Android"
className={clsx( className={clsx(
'flex items-center rounded-lg transition-colors px-4 py-2', 'flex items-center rounded-lg px-4 py-2 transition-all hover:scale-105',
color === 'black' color === 'black'
? 'bg-gray-800 text-white hover:bg-gray-900' ? 'bg-gray-800 text-white hover:bg-gray-900'
: 'bg-white text-gray-900 hover:bg-gray-50', : 'bg-white text-gray-900 hover:bg-gray-50',

View File

@@ -11,7 +11,7 @@ export function AppStoreLink({
href="https://apps.apple.com/us/app/mycelium-network/id6504277565" href="https://apps.apple.com/us/app/mycelium-network/id6504277565"
aria-label="Download on the App Store" aria-label="Download on the App Store"
className={clsx( className={clsx(
'rounded-lg transition-colors', 'rounded-lg transition-all hover:scale-105',
color === 'black' color === 'black'
? 'bg-gray-800 text-white hover:bg-gray-900' ? 'bg-gray-800 text-white hover:bg-gray-900'
: 'bg-white text-gray-900 hover:bg-gray-50', : 'bg-white text-gray-900 hover:bg-gray-50',

View File

@@ -14,10 +14,11 @@ const variantStyles = {
white: white:
'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70', 'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70',
gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80', gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80',
green: 'bg-green-500 text-white hover:bg-green-600',
}, },
outline: { outline: {
gray: 'border-gray-300 text-gray-700 hover:text-gray-500 hover:border-gray-400 active:bg-gray-100 active:text-gray-700/80', gray: 'border-gray-300 text-gray-700 hover:border-cyan-500 active:border-cyan-500',
white: 'border-gray-300 text-white hover:text-gray-200 hover:border-gray-400 active:bg-gray-100 active:text-gray-700/80', white: 'border-gray-300 text-white hover:border-cyan-500 active:border-cyan-500',
}, },
} }

View File

@@ -12,7 +12,7 @@ export function CallToAction() {
className="relative overflow-hidden bg-gray-900 py-20 sm:py-28" className="relative overflow-hidden bg-gray-900 py-20 sm:py-28"
> >
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"> <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<CircleBackground color="#fff" className="animate-spin-slower" /> <CircleBackground color="#06b6d4" className="animate-spin-slower" />
</div> </div>
<Container className="relative"> <Container className="relative">
<div className="mx-auto max-w-2xl sm:text-center"> <div className="mx-auto max-w-2xl sm:text-center">
@@ -22,7 +22,7 @@ export function CallToAction() {
<p className="mt-6 text-lg text-gray-300"> <p className="mt-6 text-lg text-gray-300">
Download the Mycelium app and step into the future of secure, peer-to-peer networking; fast, private, and decentralized. Download the Mycelium app and step into the future of secure, peer-to-peer networking; fast, private, and decentralized.
</p> </p>
<div className="mt-8 grid grid-cols-2 justify-items-center gap-4 sm:flex sm:justify-center"> <div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4">
<AppStoreLink color="white" /> <AppStoreLink color="white" />
<WindowsLink color="white" /> <WindowsLink color="white" />
<AndroidLink color="white" /> <AndroidLink color="white" />

View File

@@ -1,18 +1,36 @@
import { CheckIcon } from '@heroicons/react/20/solid' import {
BookOpenIcon,
LifebuoyIcon,
ChatBubbleOvalLeftEllipsisIcon,
UserGroupIcon,
} from '@heroicons/react/24/outline';
const features = [ const features = [
{ {
name: 'Documentation', name: 'Documentation',
description: 'Documentation for Mycelium.', 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: 'Support', description: 'Talk to an expert.' },
{ {
name: 'Forum', name: 'Forum',
description: 'Forum for all your questions.', description: 'Forum for all your questions.',
href: 'https://forum.threefold.io/',
icon: ChatBubbleOvalLeftEllipsisIcon,
}, },
{ name: 'Community', description: 'Join our Developers community on telegram.' }, {
name: 'Community',
] description: 'Join our Developers community on telegram.',
href: 'https://t.me/threefoldtesting',
icon: UserGroupIcon,
},
];
export function DevHub() { export function DevHub() {
return ( return (
@@ -21,26 +39,35 @@ export function DevHub() {
<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="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"> <div className="col-span-2">
<h2 className="text-base/7 font-semibold text-cyan-500 mb-2">Get Started</h2> <h2 className="text-base/7 font-semibold text-cyan-500 mb-2">Get Started</h2>
<p className="text-4xl font-semibold tracking-tight text-pretty text-white sm:text-5xl"> <p className="text-3xl lg:text-4xl font-medium tracking-tight text-white">
Developer Hub Developer Hub
</p> </p>
<p className="mt-6 text-base/7 text-gray-300"> <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. 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> </p>
</div> </div>
<dl className="col-span-3 grid grid-cols-1 gap-x-8 gap-y-10 text-base/7 text-gray-400 sm:grid-cols-2 lg:gap-y-16"> <dl className="col-span-3 grid grid-cols-1 gap-8 sm:grid-cols-2">
{features.map((feature) => ( {features.map((feature) => (
<div key={feature.name} className="relative pl-9"> <a
key={feature.name}
href={feature.href}
target="_blank"
rel="noopener noreferrer"
className="block rounded-2xl border border-gray-700 p-6 shadow-sm transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 hover:bg-gray-800"
>
<feature.icon
aria-hidden="true"
className="h-6 w-6 flex-none text-cyan-500 mb-4"
/>
<dt className="font-semibold text-white"> <dt className="font-semibold text-white">
<CheckIcon aria-hidden="true" className="absolute top-1 left-0 size-5 text-indigo-400" />
{feature.name} {feature.name}
</dt> </dt>
<dd className="mt-2">{feature.description}</dd> <dd className="mt-2 text-gray-400">{feature.description}</dd>
</div> </a>
))} ))}
</dl> </dl>
</div> </div>
</div> </div>
</div> </div>
) );
} }

View File

@@ -1,4 +1,7 @@
'use client'
import Image from 'next/image'; import Image from 'next/image';
import { motion } from 'framer-motion';
import appleIcon from '@/images/apple.svg'; import appleIcon from '@/images/apple.svg';
import windowsIcon from '@/images/windows.svg'; import windowsIcon from '@/images/windows.svg';
import androidIcon from '@/images/android.svg'; import androidIcon from '@/images/android.svg';
@@ -36,22 +39,32 @@ export default function DownloadHero() {
<div className=" py-16 sm:py-32"> <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-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl lg:mx-0"> <div className="mx-auto max-w-2xl lg:mx-0">
<h2 className="text-5xl lg:text-6xl font-medium tracking-tight text-gray-900"> <motion.h2
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="text-5xl lg:text-6xl font-medium tracking-tight text-gray-900"
>
Download Mycelium Download Mycelium
</h2> </motion.h2>
<p className="mt-6 text-lg/8 text-gray-600"> <motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
className="mt-6 text-lg/8 text-gray-600"
>
Get Mycelium for Android, Windows, macOS, and iOS to securely connect, store, and interact with the decentralized networkseamlessly and efficiently. Not sure how it works?{' '} Get Mycelium 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="text-cyan-500 hover:text-cyan-600 font-semibold underline"> <a href="https://threefold.info/mycelium_network/docs/" className="text-gray-900 hover:text-cyan-500 transition-colors font-semibold underline">
Read the manual. Read the manual.
</a> </a>
</p> </motion.p>
</div> </div>
<div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-none"> <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 lg:max-w-none md:grid-cols-2 lg:grid-cols-4"> <dl className="grid max-w-xl grid-cols-1 gap-x-8 gap-y-16 lg:max-w-none md:grid-cols-2 lg:grid-cols-4">
{features.map((feature) => ( {features.map((feature) => (
<div <div
key={feature.name} 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:bg-gray-50 hover:shadow-md hover:scale-105" 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/7 font-semibold text-gray-900"> <dt className="text-base/7 font-semibold text-gray-900">
<div className="mb-6 flex h-10 w-10 items-center justify-center"> <div className="mb-6 flex h-10 w-10 items-center justify-center">

View File

@@ -4,10 +4,8 @@ import { ArrowDownTrayIcon } from '@heroicons/react/24/solid'
export function DownloadLink() { export function DownloadLink() {
return ( return (
<Link <Link
href="https://github.com/threefoldtech/mycelium/releases" href="/download"
aria-label="Download Mycelium" aria-label="Download Mycelium"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center rounded-lg bg-cyan-500 px-4 py-2 text-sm font-semibold text-white hover:bg-cyan-600 transition-colors" className="inline-flex items-center rounded-lg bg-cyan-500 px-4 py-2 text-sm font-semibold text-white hover:bg-cyan-600 transition-colors"
> >
<ArrowDownTrayIcon className="h-5 w-5 mr-2" /> <ArrowDownTrayIcon className="h-5 w-5 mr-2" />

View File

@@ -67,8 +67,8 @@ export function Faqs() {
<p className="mt-2 text-lg text-gray-600"> <p className="mt-2 text-lg text-gray-600">
If you have anything else you want to ask,{' '} If you have anything else you want to ask,{' '}
<a <a
href="https://t.me/threefold" href="https://threefoldfaq.crisp.help/en/"
className="text-gray-900 underline" className="text-gray-900 hover:text-cyan-500 transition-colors font-semibold underline"
> >
reach out to us reach out to us
</a> </a>

View File

@@ -9,17 +9,17 @@ export function Features() {
<section id="features" className=" py-24"> <section id="features" className=" py-24">
<div className="mx-auto max-w-2xl px-6 lg:max-w-7xl lg:px-8"> <div className="mx-auto max-w-2xl px-6 lg:max-w-7xl lg:px-8">
<h2 className="text-base/7 font-semibold text-cyan-500">Core Components</h2> <h2 className="text-base/7 font-semibold text-cyan-500">Core Components</h2>
<p className="mt-2 max-w-4xl text-3xl lg:text-4xl font-medium tracking-tight text-pretty text-gray-950"> <p className="mt-2 max-w-2xl text-3xl lg:text-4xl font-medium tracking-tight text-pretty text-gray-950">
Network Capabilities Network Capabilities
</p> </p>
<p className="mt-4 max-w-xl text-lg text-gray-600"> <p className="mt-4 max-w-4xl text-lg text-gray-600">
Built for resilience and autonomy, the Mycelium Network dynamically connects nodes through intelligent routing, proxy discovery, and decentralized delivery. Built for resilience and autonomy, the Mycelium Network dynamically connects nodes through intelligent routing, proxy discovery, and decentralized delivery.
</p> </p>
<p className="mt-2 max-w-xl text-lg text-gray-600"> <p className="mt-2 max-w-4xl text-lg text-gray-600">
Each component from message passing to content distribution works in harmony to create a fully self-healing, self-optimizing data mesh. Each component from message passing to content distribution works in harmony to create a fully self-healing, self-optimizing data mesh.
</p> </p>
<div className="mt-10 grid grid-cols-1 gap-x-4 gap-y-8 sm:mt-16 lg:grid-cols-6 lg:grid-rows-2"> <div className="mt-10 grid grid-cols-1 gap-x-4 gap-y-8 sm:mt-16 lg:grid-cols-6 lg:grid-rows-2">
<div className="relative lg:col-span-3 transition-all duration-300 ease-in-out hover:scale-105"> <div className="group relative lg:col-span-3 transition-all duration-300 ease-in-out hover:scale-105">
<div className="absolute inset-0 rounded-lg bg-white max-lg:rounded-t-4xl lg:rounded-tl-4xl" /> <div className="absolute inset-0 rounded-lg bg-white max-lg:rounded-t-4xl lg:rounded-tl-4xl" />
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-t-[calc(2rem+1px)] lg:rounded-tl-[calc(2rem+1px)]"> <div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-t-[calc(2rem+1px)] lg:rounded-tl-[calc(2rem+1px)]">
<Pathfinding /> <Pathfinding />
@@ -34,9 +34,9 @@ Each component — from message passing to content distribution — works in har
</p> </p>
</div> </div>
</div> </div>
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 max-lg:rounded-t-4xl lg:rounded-tl-4xl" /> <div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 max-lg:rounded-t-4xl lg:rounded-tl-4xl group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
</div> </div>
<div className="relative lg:col-span-3 transition-all duration-300 ease-in-out hover:scale-105"> <div className="group relative lg:col-span-3 transition-all duration-300 ease-in-out hover:scale-105">
<div className="absolute inset-0 rounded-lg bg-white lg:rounded-tr-4xl" /> <div className="absolute inset-0 rounded-lg bg-white lg:rounded-tr-4xl" />
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-tr-[calc(2rem+1px)]"> <div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-tr-[calc(2rem+1px)]">
<MessageBus /> <MessageBus />
@@ -51,9 +51,9 @@ Each component — from message passing to content distribution — works in har
</p> </p>
</div> </div>
</div> </div>
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 lg:rounded-tr-4xl" /> <div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 lg:rounded-tr-4xl group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
</div> </div>
<div className="relative lg:col-span-2 transition-all duration-300 ease-in-out hover:scale-105"> <div className="group relative lg:col-span-2 transition-all duration-300 ease-in-out hover:scale-105">
<div className="absolute inset-0 rounded-lg bg-white lg:rounded-bl-4xl" /> <div className="absolute inset-0 rounded-lg bg-white lg:rounded-bl-4xl" />
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-bl-[calc(2rem+1px)]"> <div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-bl-[calc(2rem+1px)]">
<ProxyDetection className="h-80" /> <ProxyDetection className="h-80" />
@@ -68,9 +68,9 @@ Each component — from message passing to content distribution — works in har
</p> </p>
</div> </div>
</div> </div>
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 lg:rounded-bl-4xl" /> <div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 lg:rounded-bl-4xl group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
</div> </div>
<div className="relative lg:col-span-2 transition-all duration-300 ease-in-out hover:scale-105"> <div className="group relative lg:col-span-2 transition-all duration-300 ease-in-out hover:scale-105">
<div className="absolute inset-0 rounded-lg bg-white" /> <div className="absolute inset-0 rounded-lg bg-white" />
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)]"> <div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)]">
<ProxyForwarding className="h-80" /> <ProxyForwarding className="h-80" />
@@ -85,9 +85,9 @@ Each component — from message passing to content distribution — works in har
</p> </p>
</div> </div>
</div> </div>
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5" /> <div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
</div> </div>
<div className="relative lg:col-span-2 transition-all duration-300 ease-in-out hover:scale-105"> <div className="group relative lg:col-span-2 transition-all duration-300 ease-in-out hover:scale-105">
<div className="absolute inset-0 rounded-lg bg-white max-lg:rounded-b-4xl lg:rounded-br-4xl" /> <div className="absolute inset-0 rounded-lg bg-white max-lg:rounded-b-4xl lg:rounded-br-4xl" />
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-br-[calc(2rem+1px)]"> <div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-br-[calc(2rem+1px)]">
<ContentDistribution className="h-80" /> <ContentDistribution className="h-80" />
@@ -102,7 +102,7 @@ Each component — from message passing to content distribution — works in har
</p> </p>
</div> </div>
</div> </div>
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 max-lg:rounded-b-4xl lg:rounded-br-4xl" /> <div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 max-lg:rounded-b-4xl lg:rounded-br-4xl group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,20 +1,60 @@
'use client'
import Image from 'next/image' import Image from 'next/image'
import Link from 'next/link' import Link from 'next/link'
import { useState } from 'react'
import { Button } from '@/components/Button' import { Button } from '@/components/Button'
import { Container } from '@/components/Container' import { Container } from '@/components/Container'
import { TextField } from '@/components/Fields' import { TextField } from '@/components/Fields'
import { NavLinks } from '@/components/NavLinks' import { NavLinks } from '@/components/NavLinks'
import github from '@/images/github.svg' import github from '@/images/github.svg'
import logomark from '@/images/logomark.svg'
export function Footer() { export function Footer() {
const [email, setEmail] = useState('');
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [message, setMessage] = useState('');
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setLoading(true);
setSuccess(false);
setMessage('');
try {
const response = await fetch('/api/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Something went wrong');
}
setSuccess(true);
setMessage('Thanks for subscribing!');
setEmail('');
} catch (error: any) {
setMessage(error.message);
} finally {
setLoading(false);
}
};
return ( return (
<footer className="border-t border-gray-200"> <footer className="border-t border-gray-200">
<Container> <Container>
<div className="flex flex-col items-start justify-between gap-y-12 pt-16 pb-6 lg:flex-row lg:items-center lg:py-8"> <div className="flex flex-col items-start justify-between gap-y-12 pt-16 pb-6 lg:flex-row lg:items-center lg:py-8">
<div> <div>
<div className="flex items-center text-gray-900"> <div className="flex items-center text-gray-900">
<Image src="/images/logo.svg" alt="Mycelium Logomark" width={60} height={60} className="h-20 w-20 flex-none" /> <Image src={logomark} alt="Mycelium Logomark" width={60} height={60} className="h-20 w-20 flex-none" />
<div className="ml-4"> <div className="ml-4">
<p className="text-base font-semibold">Mycelium</p> <p className="text-base font-semibold">Mycelium</p>
<p className="mt-1 text-sm">Unleash the Power of Decentralized Networks</p> <p className="mt-1 text-sm">Unleash the Power of Decentralized Networks</p>
@@ -42,22 +82,35 @@ export function Footer() {
</div> </div>
</div> </div>
<div className="flex flex-col items-center border-t border-gray-200 pt-8 pb-12 md:flex-row-reverse md:justify-between md:pt-6"> <div className="flex flex-col items-center border-t border-gray-200 pt-8 pb-12 md:flex-row-reverse md:justify-between md:pt-6">
<form className="flex w-full justify-center md:w-auto"> <div>
<TextField <form className="flex w-full justify-center md:w-auto" onSubmit={handleSubmit}>
type="email" <TextField
aria-label="Email address" type="email"
placeholder="Email address" aria-label="Email address"
autoComplete="email" placeholder="Email address"
required autoComplete="email"
className="w-60 min-w-0 shrink" required
/> className="w-60 min-w-0 shrink"
<Button type="submit" color="cyan" className="ml-4 flex-none"> value={email}
<span className="hidden lg:inline">Join our newsletter</span> onChange={(e) => setEmail(e.target.value)}
<span className="lg:hidden">Join newsletter</span> />
</Button> <Button
</form> type="submit"
color={success ? 'green' : 'cyan'}
className="ml-4 flex-none"
disabled={loading || success}
>
{loading ? 'Joining...' : success ? 'Sent!' : <><span className="hidden lg:inline">Join our newsletter</span><span className="lg:hidden">Join newsletter</span></>}
</Button>
</form>
{message && <p className="mt-2 text-sm text-gray-600">{message}</p>}
</div>
<p className="mt-6 text-sm text-gray-500 md:mt-0"> <p className="mt-6 text-sm text-gray-500 md:mt-0">
&copy; Copyright ThreeFold {new Date().getFullYear()}. All rights reserved. &copy; Copyright{' '}
<a href="https://www.threefold.io" target="_blank" rel="noopener noreferrer" className="hover:text-cyan-500 transition-colors">
ThreeFold
</a>{' '}
{new Date().getFullYear()}. All rights reserved.
</p> </p>
</div> </div>
</Container> </Container>

View File

@@ -117,13 +117,13 @@ export function Hero() {
</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">
<DownloadLink /> <DownloadLink />
<Button {/* <Button
href="https://youtu.be/4oq15lxvkts?si=Heh_8DHqHaNpy3_F" href="https://youtu.be/4oq15lxvkts?si=Heh_8DHqHaNpy3_F"
variant="outline" variant="outline"
> >
<PlayIcon className="h-6 w-6 flex-none" /> <PlayIcon className="h-6 w-6 flex-none" />
<span className="ml-2.5">Watch the Demo</span> <span className="ml-2.5">Watch the Demo</span>
</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 lg:mt-10 mt-0 lg:col-span-5 lg:row-span-2 xl:col-span-6">

View File

@@ -11,7 +11,7 @@ export function LinuxLink({
href="https://github.com/threefoldtech/mycelium/releases" href="https://github.com/threefoldtech/mycelium/releases"
aria-label="Download for Linux" aria-label="Download for Linux"
className={clsx( className={clsx(
'flex items-center rounded-lg transition-colors px-4 py-2', 'flex items-center rounded-lg px-4 py-2 transition-all hover:scale-105',
color === 'black' color === 'black'
? 'bg-gray-800 text-white hover:bg-gray-900' ? 'bg-gray-800 text-white hover:bg-gray-900'
: 'bg-white text-gray-900 hover:bg-gray-50', : 'bg-white text-gray-900 hover:bg-gray-50',

View File

@@ -14,6 +14,7 @@ export function NavLinks() {
['How it Works', '/#howitworks'], ['How it Works', '/#howitworks'],
['Coming Soon', '/#comingsoon'], ['Coming Soon', '/#comingsoon'],
['FAQs', '/#faqs'], ['FAQs', '/#faqs'],
['Docs', 'https://threefold.info/mycelium_network/docs/'],
].map(([label, href], index) => ( ].map(([label, href], index) => (
<Link <Link
key={label} key={label}
@@ -31,13 +32,17 @@ export function NavLinks() {
}, 50) }, 50)
}} }}
onClick={(e) => { onClick={(e) => {
e.preventDefault() if (href.startsWith('/#')) {
const targetId = href.substring(2) e.preventDefault();
const targetElement = document.getElementById(targetId) const targetId = href.substring(2);
if (targetElement) { const targetElement = document.getElementById(targetId);
targetElement.scrollIntoView({ behavior: 'smooth' }) if (targetElement) {
targetElement.scrollIntoView({ behavior: 'smooth' });
}
} }
}} }}
target={href.startsWith('http') ? '_blank' : undefined}
rel={href.startsWith('http') ? 'noopener noreferrer' : undefined}
> >
<AnimatePresence> <AnimatePresence>
{hoveredIndex === index && ( {hoveredIndex === index && (

View File

@@ -1,6 +1,8 @@
import Image from 'next/image' import Image from 'next/image'
import clsx from 'clsx' import clsx from 'clsx'
import phoneFrame from '@/images/phone-frame.svg'
export function PhoneFrame({ export function PhoneFrame({
className, className,
children, children,
@@ -10,7 +12,7 @@ export function PhoneFrame({
return ( return (
<div className={clsx('relative aspect-[366/729]', className)} {...props}> <div className={clsx('relative aspect-[366/729]', className)} {...props}>
<Image <Image
src="/images/phone-frame.svg" src={phoneFrame}
alt="" alt=""
className="pointer-events-none absolute inset-0" className="pointer-events-none absolute inset-0"
fill fill

View File

@@ -255,7 +255,12 @@ function FeaturesDesktop() {
{features.map((feature, featureIndex) => ( {features.map((feature, featureIndex) => (
<div <div
key={feature.name} key={feature.name}
className="relative rounded-2xl transition-all duration-300 ease-in-out hover:scale-105 hover:bg-gray-800/30" className={clsx(
'relative rounded-2xl outline outline-2 transition-all duration-300 ease-in-out hover:scale-105 hover:bg-gray-800/30',
selectedIndex === featureIndex
? 'outline-cyan-500'
: 'outline-transparent hover:outline-cyan-500',
)}
> >
{featureIndex === selectedIndex && ( {featureIndex === selectedIndex && (
<motion.div <motion.div
@@ -355,7 +360,14 @@ function FeaturesMobile() {
ref={(ref) => ref && (slideRefs.current[featureIndex] = ref)} ref={(ref) => ref && (slideRefs.current[featureIndex] = ref)}
className="w-full flex-none snap-center px-4 sm:px-6 transition-all duration-300 ease-in-out hover:scale-105" className="w-full flex-none snap-center px-4 sm:px-6 transition-all duration-300 ease-in-out hover:scale-105"
> >
<div className="relative transform overflow-hidden rounded-2xl bg-gray-800 px-5 py-6"> <div
className={clsx(
'relative transform overflow-hidden rounded-2xl bg-gray-800 px-5 py-6 outline outline-2 transition-colors',
activeIndex === featureIndex
? 'outline-cyan-500'
: 'outline-transparent hover:outline-cyan-500',
)}
>
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"> <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<CircleBackground <CircleBackground
color="#13B5C8" color="#13B5C8"

View File

@@ -210,7 +210,7 @@ export function SecondaryFeatures() {
{features.map((feature) => ( {features.map((feature) => (
<li <li
key={feature.name} key={feature.name}
className="rounded-2xl border border-gray-200 p-8 transition-all duration-300 ease-in-out hover:scale-105" className="rounded-2xl border border-gray-200 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20"
> >
<feature.icon className="h-8 w-8" /> <feature.icon className="h-8 w-8" />
<h3 className="mt-6 font-semibold text-gray-900"> <h3 className="mt-6 font-semibold text-gray-900">

View File

@@ -8,10 +8,10 @@ export function WindowsLink({
}) { }) {
return ( return (
<Link <Link
href="#" href="https://github.com/threefoldtech/myceliumflut/releases"
aria-label="Download for Windows" aria-label="Download for Windows"
className={clsx( className={clsx(
'flex items-center rounded-lg transition-colors px-4 py-2', 'flex items-center rounded-lg px-4 py-2 transition-all hover:scale-105',
color === 'black' color === 'black'
? 'bg-gray-800 text-white hover:bg-gray-900' ? 'bg-gray-800 text-white hover:bg-gray-900'
: 'bg-white text-gray-900 hover:bg-gray-50', : 'bg-white text-gray-900 hover:bg-gray-50',

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 83 KiB

1
src/images/logomark.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 107 KiB