This commit is contained in:
eric
2026-03-07 09:34:01 -06:00
parent 742815f2db
commit da5f9c18a7
39 changed files with 8602 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
const links = [
{
icon: "📖",
title: "入门指南",
desc: "完整的数字游民入门教程",
label: "开始学习 ↗",
},
{
icon: "💬",
title: "Discord 社区",
desc: "与全球数字游民实时交流",
label: "加入社区 ↗",
},
{
icon: "🛒",
title: "工具集市",
desc: "发现、评测和分享远程工具",
label: "浏览工具 ↗",
},
{
icon: "📦",
title: "GitHub",
desc: "开源项目,欢迎 Star 和 PR",
label: "访问仓库 ↗",
},
{
icon: "📝",
title: "飞书知识库",
desc: "7天入门指南 · 中文图文教程",
label: "查看文档 ↗",
},
{
icon: "🗺️",
title: "目的地数据库",
desc: "全球数字游民城市信息",
label: "探索城市 ↗",
},
];
export default function Community() {
return (
<section id="community" className="py-20 sm:py-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
{/* Community cards */}
<div className="mb-20 grid gap-6 md:grid-cols-2">
<a
href="/join"
className="block rounded-2xl border border-sky-200 bg-gradient-to-br from-sky-50 to-cyan-50 p-8 transition-all hover:shadow-lg"
>
<div className="mb-2 text-sm font-semibold text-sky-600">
👥
</div>
<h3 className="mb-3 text-xl font-bold text-slate-900">
</h3>
<p className="mb-6 text-sm leading-relaxed text-slate-600">
<br />
1000+ · 线 ·
</p>
<div className="flex items-center justify-center rounded-xl bg-white/80 p-6 shadow-sm backdrop-blur-sm">
<div className="flex items-center gap-4">
<div className="flex h-14 w-14 items-center justify-center rounded-full bg-sky-100 text-3xl">
📝
</div>
<div className="text-left">
<div className="font-bold text-slate-800">
</div>
<div className="mt-1 text-xs text-slate-400">
</div>
</div>
</div>
</div>
</a>
<a
href="/course"
className="block rounded-2xl border border-amber-200 bg-gradient-to-br from-amber-50 to-orange-50 p-8 transition-all hover:shadow-lg"
>
<div className="mb-2 text-sm font-semibold text-amber-600">
🎬
</div>
<h3 className="mb-3 text-xl font-bold text-slate-900">
7×24
</h3>
<div className="mb-4 flex flex-wrap gap-3">
<span className="rounded-full bg-white/80 px-3 py-1 text-xs font-medium text-slate-600">
🔥
</span>
<span className="rounded-full bg-white/80 px-3 py-1 text-xs font-medium text-slate-600">
📹
</span>
<span className="rounded-full bg-white/80 px-3 py-1 text-xs font-medium text-slate-600">
¥199
</span>
</div>
<p className="mb-6 text-sm leading-relaxed text-slate-600">
7 ·
</p>
<div className="flex items-center justify-center rounded-xl bg-white/80 p-6 shadow-sm backdrop-blur-sm">
<div className="text-center">
<div className="text-5xl">🎓</div>
<div className="mt-3 text-sm font-medium text-slate-600">
</div>
</div>
</div>
</a>
</div>
{/* Open source contribution */}
<div className="text-center">
<div className="mb-2 text-sm font-semibold text-slate-400">
🤝
</div>
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
<span className="gradient-text"></span>
</h2>
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600">
<br />
</p>
</div>
<div className="mt-12 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{links.map((l) => (
<div
key={l.title}
className="card-hover flex items-start gap-4 rounded-xl border border-slate-100 bg-white p-5 shadow-sm"
>
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-slate-50 text-xl">
{l.icon}
</span>
<div>
<h4 className="font-bold text-slate-900">{l.title}</h4>
<p className="mt-1 text-sm text-slate-500">{l.desc}</p>
<span className="mt-2 inline-block text-sm font-medium text-sky-600">
{l.label}
</span>
</div>
</div>
))}
</div>
<div className="mt-12 text-center">
<div className="mb-4 text-lg font-bold text-slate-900">
🌟
</div>
<p className="mb-6 text-slate-600">
<br />
PR
</p>
<button className="inline-flex items-center gap-2 rounded-full bg-slate-900 px-8 py-3 font-semibold text-white transition-colors hover:bg-slate-800">
Star & Fork on GitHub
</button>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,12 @@
"use client";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
export default function DayContent({ content }: { content: string }) {
return (
<div className="prose-custom">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
</div>
);
}

View File

@@ -0,0 +1,60 @@
const features = [
{
icon: "🌏",
title: "地点自由",
desc: "在世界任何角落工作——咖啡馆、海滩、共享空间,你的办公室就是整个地球。告别固定工位,拥抱无限可能。",
},
{
icon: "⚡",
title: "技能变现",
desc: "编程、设计、写作、营销、咨询……用你的专业技能换取全球收入,构建多元化的收入体系。",
},
{
icon: "🔄",
title: "工作生活平衡",
desc: "自主安排时间,工作与旅行完美融合。按照自己的节奏生活,找到真正属于你的生活方式。",
},
];
export default function Features() {
return (
<section id="features" className="py-20 sm:py-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
<span className="gradient-text"></span>
</h2>
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600">
<br className="hidden sm:block" />
</p>
</div>
<div className="mt-16 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{features.map((f) => (
<div
key={f.title}
className="card-hover rounded-2xl border border-slate-100 bg-white p-8 shadow-sm"
>
<div className="mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-sky-50 text-3xl">
{f.icon}
</div>
<h3 className="mb-3 text-xl font-bold text-slate-900">
{f.title}
</h3>
<p className="leading-relaxed text-slate-600">{f.desc}</p>
</div>
))}
</div>
<div className="mt-12 text-center">
<div className="inline-flex items-center gap-2 rounded-full border border-amber-200 bg-amber-50 px-5 py-2 text-sm font-medium text-amber-700">
<span>🔥</span>
<span className="font-bold">3500 </span>
</div>
</div>
</div>
</section>
);
}

87
app/components/Footer.tsx Normal file
View File

@@ -0,0 +1,87 @@
const footerSections = [
{
title: "指南",
links: [
{ label: "7天学习路径", href: "#roadmap" },
{ label: "全部资源", href: "#resources" },
{ label: "工具推荐", href: "#tools" },
{ label: "目的地数据库", href: "#" },
],
},
{
title: "资源",
links: [
{ label: "远程工作平台", href: "#" },
{ label: "签证政策汇总", href: "#" },
{ label: "共居空间推荐", href: "#" },
{ label: "数字游民保险", href: "#" },
],
},
{
title: "社区",
links: [
{ label: "Discord", href: "#" },
{ label: "微信群", href: "#community" },
{ label: "即刻", href: "#" },
{ label: "GitHub", href: "#" },
],
},
{
title: "关于",
links: [
{ label: "关于我们", href: "#" },
{ label: "贡献指南", href: "#" },
{ label: "隐私政策", href: "#" },
{ label: "联系我们", href: "#" },
],
},
];
export default function Footer() {
return (
<footer className="border-t border-slate-100 bg-white">
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8 lg:py-16">
<div className="grid gap-8 sm:grid-cols-2 lg:grid-cols-5">
<div className="lg:col-span-1">
<a href="#" className="flex items-center gap-2 text-lg font-bold">
<span className="text-2xl">🌍</span>
<span></span>
</a>
<p className="mt-3 text-sm leading-relaxed text-slate-500">
<br />
</p>
</div>
{footerSections.map((section) => (
<div key={section.title}>
<h4 className="mb-4 text-sm font-bold text-slate-900">
{section.title}
</h4>
<ul className="space-y-2.5">
{section.links.map((link) => (
<li key={link.label}>
<a
href={link.href}
className="text-sm text-slate-500 transition-colors hover:text-slate-700"
>
{link.label}
</a>
</li>
))}
</ul>
</div>
))}
</div>
<div className="mt-12 flex flex-col items-center justify-between gap-4 border-t border-slate-100 pt-8 sm:flex-row">
<p className="text-sm text-slate-400">
Made with 🌍 by | Digital Nomad Guide
</p>
<p className="text-sm text-slate-400"> · </p>
</div>
</div>
</footer>
);
}

102
app/components/Header.tsx Normal file
View File

@@ -0,0 +1,102 @@
"use client";
import { useState, useEffect } from "react";
const navLinks = [
{ label: "学习路径", href: "#roadmap" },
{ label: "工具推荐", href: "#tools" },
{ label: "精选资源", href: "#resources" },
{ label: "社区", href: "#community" },
];
export default function Header() {
const [mobileOpen, setMobileOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 20);
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
return (
<header
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
scrolled
? "bg-white/80 backdrop-blur-lg shadow-sm"
: "bg-transparent"
}`}
>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex h-16 items-center justify-between">
<a href="#" className="flex items-center gap-2 text-lg font-bold">
<span className="text-2xl">🌍</span>
<span className="hidden sm:inline"></span>
<span className="sm:hidden">DN Guide</span>
</a>
<nav className="hidden items-center gap-1 md:flex">
{navLinks.map((link) => (
<a
key={link.href}
href={link.href}
className="rounded-lg px-3 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900"
>
{link.label}
</a>
))}
</nav>
<div className="flex items-center gap-3">
<a
href="#roadmap"
className="hidden rounded-full bg-sky-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-sky-600 sm:inline-flex"
>
</a>
<button
onClick={() => setMobileOpen(!mobileOpen)}
className="inline-flex items-center justify-center rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100 md:hidden"
aria-label="切换菜单"
>
{mobileOpen ? (
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
) : (
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
)}
</button>
</div>
</div>
</div>
{mobileOpen && (
<div className="border-t border-slate-100 bg-white/95 backdrop-blur-lg md:hidden">
<nav className="mx-auto max-w-7xl space-y-1 px-4 py-3">
{navLinks.map((link) => (
<a
key={link.href}
href={link.href}
onClick={() => setMobileOpen(false)}
className="block rounded-lg px-3 py-2.5 text-base font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900"
>
{link.label}
</a>
))}
<a
href="#roadmap"
onClick={() => setMobileOpen(false)}
className="mt-2 block rounded-full bg-sky-500 px-4 py-2.5 text-center text-base font-medium text-white transition-colors hover:bg-sky-600"
>
</a>
</nav>
</div>
)}
</header>
);
}

92
app/components/Hero.tsx Normal file
View File

@@ -0,0 +1,92 @@
const stats = [
{ value: "200+", label: "精选资源" },
{ value: "7 天", label: "入门路径" },
{ value: "50+", label: "远程工具" },
{ value: "30+", label: "目的地" },
{ value: "100%", label: "免费开源" },
];
export default function Hero() {
return (
<section className="relative overflow-hidden pt-24 pb-16 sm:pt-32 sm:pb-20">
<div className="pointer-events-none absolute inset-0 bg-gradient-to-b from-sky-50/80 via-white to-white" />
<div className="pointer-events-none absolute top-0 left-1/2 h-[600px] w-[800px] -translate-x-1/2 rounded-full bg-gradient-to-br from-sky-100/60 via-cyan-50/40 to-amber-50/30 blur-3xl" />
<div className="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center">
<div className="mb-6 inline-flex items-center gap-2 rounded-full border border-sky-200 bg-sky-50 px-4 py-1.5 text-sm font-medium text-sky-700">
<span>🎒</span>
<span> · 200+ </span>
</div>
<h1 className="mx-auto max-w-4xl text-4xl font-extrabold tracking-tight sm:text-5xl lg:text-7xl">
<span className="gradient-text"></span>
<br className="sm:hidden" />
</h1>
<p className="mx-auto mt-6 max-w-2xl text-lg leading-relaxed text-slate-600 sm:text-xl">
7
<br className="hidden sm:block" />
<span className="text-slate-400 sm:text-slate-600">
The open-source guide to becoming a digital nomad
</span>
</p>
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row sm:gap-4">
<a
href="#roadmap"
className="inline-flex w-full items-center justify-center gap-2 rounded-full bg-sky-500 px-8 py-3.5 text-base font-semibold text-white shadow-lg shadow-sky-500/25 transition-all hover:bg-sky-600 hover:shadow-xl hover:shadow-sky-500/30 sm:w-auto"
>
🚀
<span></span>
</a>
<a
href="#resources"
className="inline-flex w-full items-center justify-center gap-2 rounded-full border border-slate-200 bg-white px-8 py-3.5 text-base font-semibold text-slate-700 shadow-sm transition-all hover:border-slate-300 hover:bg-slate-50 sm:w-auto"
>
📚
</a>
</div>
</div>
{/* Stats marquee */}
<div className="marquee-container mt-16 overflow-hidden">
<div className="flex animate-scroll w-max gap-6">
{[...stats, ...stats].map((s, i) => (
<div
key={i}
className="flex shrink-0 items-center gap-3 rounded-2xl border border-slate-100 bg-white/80 px-6 py-4 shadow-sm backdrop-blur-sm"
>
<span className="text-2xl font-bold text-sky-600 sm:text-3xl">
{s.value}
</span>
<span className="text-sm font-medium text-slate-500">
{s.label}
</span>
</div>
))}
</div>
</div>
<div className="marquee-container mt-4 overflow-hidden">
<div className="flex animate-scroll-reverse w-max gap-6">
{[...stats, ...stats].map((s, i) => (
<div
key={i}
className="flex shrink-0 items-center gap-3 rounded-2xl border border-slate-100 bg-white/80 px-6 py-4 shadow-sm backdrop-blur-sm"
>
<span className="text-2xl font-bold text-amber-500 sm:text-3xl">
{s.value}
</span>
<span className="text-sm font-medium text-slate-500">
{s.label}
</span>
</div>
))}
</div>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,175 @@
const resources = [
{
lang: "中文",
tag: "📖 入门指南",
title: "数字游民完全入门手册",
desc: "从概念到实践,系统化的数字游民入门指南,含远程工作、签证、财务全攻略",
source: "少数派",
color: "bg-sky-50 text-sky-700 border-sky-200",
},
{
lang: "EN",
tag: "📖 经典书籍",
title: "The 4-Hour Workweek — Tim Ferriss",
desc: "数字游民圣经,重新定义工作与生活的关系,构建「新富族」生活方式",
source: "Amazon",
color: "bg-amber-50 text-amber-700 border-amber-200",
},
{
lang: "中文",
tag: "☁️ 远程求职",
title: "国内远程工作机会汇总 — 电鸭社区",
desc: "最大的中文远程工作社区,收录数千个远程岗位,涵盖开发、设计、运营等领域",
source: "电鸭社区",
color: "bg-emerald-50 text-emerald-700 border-emerald-200",
},
{
lang: "EN",
tag: "🗺️ 目的地",
title: "Nomad List — Best Cities for Digital Nomads",
desc: "全球数字游民城市数据库,含生活成本、网速、安全指数、社区活跃度等指标",
source: "Nomad List",
color: "bg-violet-50 text-violet-700 border-violet-200",
},
{
lang: "中文",
tag: "📹 视频教程",
title: "从程序员到数字游民 — 我的转型之路",
desc: "真实经历分享:如何从 996 转型为自由的数字游民,含收入、签证、生活细节",
source: "Bilibili",
color: "bg-rose-50 text-rose-700 border-rose-200",
},
{
lang: "EN",
tag: "💰 财务规划",
title: "Tax Planning for Digital Nomads — Complete Guide",
desc: "数字游民税务规划权威指南:税务居民身份、双重征税协定、合规架构搭建",
source: "SafetyWing",
color: "bg-teal-50 text-teal-700 border-teal-200",
},
{
lang: "中文",
tag: "🛠️ 工具评测",
title: "远程工作者必备工具清单 2026",
desc: "50+ 工具横评:协作、会议、设计、开发、生产力工具深度体验与推荐",
source: "即刻",
color: "bg-orange-50 text-orange-700 border-orange-200",
},
{
lang: "EN",
tag: "🔬 深度文章",
title: "State of Digital Nomads 2026 — Annual Report",
desc: "2026年度数字游民状态报告3500万全球数字游民的收入、工作、生活数据洞察",
source: "A Brother Abroad",
color: "bg-indigo-50 text-indigo-700 border-indigo-200",
},
{
lang: "中文",
tag: "📱 社区",
title: "数字游民部落 — 中文社区指南",
desc: "连接全球华人数字游民:经验分享、城市攻略、合租搭伴、线下 Meetup",
source: "微信公众号",
color: "bg-cyan-50 text-cyan-700 border-cyan-200",
},
{
lang: "EN",
tag: "📋 签证",
title: "Digital Nomad Visa Guide — 50+ Countries",
desc: "全球 50+ 国家数字游民签证政策汇总,含申请条件、费用、停留期限对比",
source: "VisaGuide.World",
color: "bg-lime-50 text-lime-700 border-lime-200",
},
{
lang: "中文",
tag: "🏠 共居空间",
title: "全球最佳 Coliving 空间推荐",
desc: "精选 30+ 数字游民共居空间:巴厘岛、清迈、里斯本、麦德林……含价格与评测",
source: "数字游民部落",
color: "bg-pink-50 text-pink-700 border-pink-200",
},
{
lang: "EN",
tag: "💼 自由职业",
title: "Freelancing 101 — From Zero to First Client",
desc: "从零开始接单指南Upwork/Fiverr/Toptal 平台对比、定价策略、客户沟通技巧",
source: "freeCodeCamp",
color: "bg-sky-50 text-sky-700 border-sky-200",
},
];
const sourceStats = [
{ value: "200+", label: "篇资源" },
{ value: "80", label: "中文资源" },
{ value: "120+", label: "英文资源" },
{ value: "12", label: "大分类" },
];
export default function Resources() {
return (
<section id="resources" className="bg-slate-50 py-20 sm:py-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center">
<div className="mb-2 text-sm font-semibold text-slate-400">
📚
</div>
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
<span className="gradient-text"></span>
</h2>
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600">
Nomad List B站 freeCodeCamp
</p>
</div>
{/* Stats */}
<div className="mt-10 flex flex-wrap items-center justify-center gap-4 text-sm">
{sourceStats.map((s) => (
<div key={s.label} className="flex items-center gap-1.5">
<span className="font-bold text-sky-600">{s.value}</span>
<span className="text-slate-500">{s.label}</span>
</div>
))}
</div>
{/* Resource grid */}
<div className="mt-12 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{resources.map((r, i) => (
<div
key={i}
className="resource-card flex flex-col rounded-xl border border-slate-100 bg-white p-5 shadow-sm"
>
<div className="mb-3 flex items-center gap-2">
<span
className={`rounded-md border px-2 py-0.5 text-xs font-semibold ${r.color}`}
>
{r.lang}
</span>
<span className="text-xs text-slate-400">{r.tag}</span>
</div>
<h3 className="mb-2 text-sm font-bold leading-snug text-slate-900">
{r.title}
</h3>
<p className="mb-4 flex-1 text-xs leading-relaxed text-slate-500">
{r.desc}
</p>
<div className="flex items-center justify-between">
<span className="text-xs font-medium text-slate-400">
{r.source}
</span>
<span className="cursor-pointer text-xs font-medium text-sky-600 transition-colors hover:text-sky-700">
访
</span>
</div>
</div>
))}
</div>
<div className="mt-10 text-center">
<button className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-6 py-3 text-sm font-semibold text-slate-700 shadow-sm transition-all hover:border-slate-300 hover:bg-slate-50">
200+
</button>
</div>
</div>
</section>
);
}

138
app/components/Roadmap.tsx Normal file
View File

@@ -0,0 +1,138 @@
const days = [
{
day: 1,
icon: "👋",
title: "认识数字游民",
desc: "了解数字游民的真正含义,评估这种生活方式是否适合你,破除常见误解。",
color: "bg-sky-500",
},
{
day: 2,
icon: "💡",
title: "发现远程技能",
desc: "掌握最受欢迎的远程工作技能,从编程、设计到内容创作,找到你的方向。",
color: "bg-cyan-500",
},
{
day: 3,
icon: "💰",
title: "构建收入体系",
desc: "自由职业、远程全职、被动收入、在线课程,构建可持续的多元收入来源。",
color: "bg-teal-500",
},
{
day: 4,
icon: "🛠️",
title: "工具武装",
desc: "生产力工具、通讯协作、项目管理、云服务,打造你的移动办公装备库。",
color: "bg-emerald-500",
},
{
day: 5,
icon: "📋",
title: "签证与法律",
desc: "数字游民签证政策、税务规划、海外保险、银行账户,搞定法律合规问题。",
color: "bg-amber-500",
},
{
day: 6,
icon: "🗺️",
title: "目的地指南",
desc: "热门数字游民城市推荐,生活成本对比、网速测评、社区活跃度一目了然。",
color: "bg-orange-500",
},
{
day: 7,
icon: "🚀",
title: "启程出发",
desc: "打包清单、过渡计划、第一站选择建议,从规划到行动的最后一步。",
color: "bg-rose-500",
},
];
export default function Roadmap() {
return (
<section id="roadmap" className="bg-slate-50 py-20 sm:py-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
7<span className="gradient-text"></span>
</h2>
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600">
</p>
</div>
<div className="relative mt-16">
{/* Desktop: vertical timeline line */}
<div className="absolute top-0 bottom-0 left-[27px] hidden w-0.5 bg-gradient-to-b from-sky-200 via-amber-200 to-rose-200 lg:left-1/2 lg:block lg:-translate-x-px" />
<div className="space-y-6 lg:space-y-8">
{days.map((d, i) => (
<div
key={d.day}
className={`relative lg:flex lg:items-center lg:gap-8 ${
i % 2 === 0 ? "lg:flex-row" : "lg:flex-row-reverse"
}`}
>
{/* Timeline dot (desktop) */}
<div className="absolute left-1/2 top-8 z-10 hidden h-4 w-4 -translate-x-1/2 rounded-full border-4 border-white shadow-sm lg:block"
style={{ background: `var(--tw-shadow-color, #0ea5e9)` }}
>
<div className={`h-full w-full rounded-full ${d.color}`} />
</div>
<div className="hidden lg:block lg:w-1/2" />
<div className="card-hover w-full rounded-2xl border border-slate-100 bg-white p-6 shadow-sm lg:w-1/2">
<div className="flex items-start gap-4">
<div
className={`flex h-12 w-12 shrink-0 items-center justify-center rounded-xl text-white text-sm font-bold ${d.color}`}
>
DAY {d.day}
</div>
<div className="min-w-0 flex-1">
<div className="mb-1 flex items-center gap-2">
<span className="text-xl">{d.icon}</span>
<h3 className="text-lg font-bold text-slate-900">
{d.title}
</h3>
</div>
<p className="text-sm leading-relaxed text-slate-600">
{d.desc}
</p>
<a
href={`/day/${d.day}`}
className="mt-3 inline-block text-sm font-medium text-sky-600 transition-colors hover:text-sky-700"
>
</a>
</div>
</div>
</div>
</div>
))}
</div>
</div>
{/* Training camp CTA */}
<div className="mt-16 overflow-hidden rounded-2xl border border-amber-200 bg-gradient-to-r from-amber-50 to-orange-50 p-8 text-center sm:p-10">
<div className="mb-2 text-sm font-semibold text-amber-600">
🎓
</div>
<h3 className="text-2xl font-bold text-slate-900">
</h3>
<p className="mx-auto mt-3 max-w-xl text-slate-600">
21线
</p>
<a
href="/course"
className="mt-6 inline-flex items-center gap-2 rounded-full bg-amber-500 px-8 py-3 font-semibold text-white transition-colors hover:bg-amber-600"
>
</a>
</div>
</div>
</section>
);
}

191
app/components/Tools.tsx Normal file
View File

@@ -0,0 +1,191 @@
const categories = [
{
icon: "💬",
title: "远程协作",
count: 12,
tools: [
{ name: "Slack", desc: "团队沟通" },
{ name: "Notion", desc: "知识管理" },
{ name: "Linear", desc: "项目追踪" },
{ name: "Figma", desc: "协同设计" },
],
color: "from-sky-500 to-cyan-500",
bg: "bg-sky-50",
text: "text-sky-700",
},
{
icon: "🎥",
title: "视频会议",
count: 8,
tools: [
{ name: "Zoom", desc: "视频会议" },
{ name: "Google Meet", desc: "在线会议" },
{ name: "Around", desc: "轻量会议" },
{ name: "Loom", desc: "异步视频" },
],
color: "from-violet-500 to-purple-500",
bg: "bg-violet-50",
text: "text-violet-700",
},
{
icon: "📋",
title: "项目管理",
count: 10,
tools: [
{ name: "Trello", desc: "看板管理" },
{ name: "Asana", desc: "任务管理" },
{ name: "Monday", desc: "工作流" },
{ name: "ClickUp", desc: "全能协作" },
],
color: "from-emerald-500 to-teal-500",
bg: "bg-emerald-50",
text: "text-emerald-700",
},
{
icon: "💻",
title: "开发工具",
count: 15,
tools: [
{ name: "VS Code", desc: "代码编辑" },
{ name: "GitHub", desc: "代码托管" },
{ name: "Vercel", desc: "部署平台" },
{ name: "Cursor", desc: "AI 编程" },
],
color: "from-blue-500 to-indigo-500",
bg: "bg-blue-50",
text: "text-blue-700",
},
{
icon: "🎨",
title: "设计工具",
count: 9,
tools: [
{ name: "Figma", desc: "UI 设计" },
{ name: "Canva", desc: "图形设计" },
{ name: "Framer", desc: "网页设计" },
{ name: "Midjourney", desc: "AI 作图" },
],
color: "from-pink-500 to-rose-500",
bg: "bg-pink-50",
text: "text-pink-700",
},
{
icon: "💳",
title: "财务工具",
count: 7,
tools: [
{ name: "Wise", desc: "跨境转账" },
{ name: "PayPal", desc: "在线支付" },
{ name: "Stripe", desc: "收款平台" },
{ name: "Deel", desc: "薪酬合规" },
],
color: "from-amber-500 to-orange-500",
bg: "bg-amber-50",
text: "text-amber-700",
},
{
icon: "🔒",
title: "安全与 VPN",
count: 6,
tools: [
{ name: "NordVPN", desc: "网络安全" },
{ name: "1Password", desc: "密码管理" },
{ name: "Authy", desc: "双因素认证" },
{ name: "ProtonMail", desc: "加密邮件" },
],
color: "from-slate-500 to-gray-600",
bg: "bg-slate-50",
text: "text-slate-700",
},
{
icon: "📝",
title: "生产力",
count: 11,
tools: [
{ name: "Obsidian", desc: "笔记系统" },
{ name: "Todoist", desc: "待办清单" },
{ name: "RescueTime", desc: "时间追踪" },
{ name: "Arc", desc: "浏览器" },
],
color: "from-teal-500 to-cyan-500",
bg: "bg-teal-50",
text: "text-teal-700",
},
];
const toolStats = [
{ value: "50+", label: "精选工具" },
{ value: "8", label: "分类" },
{ value: "15", label: "开发工具" },
{ value: "12", label: "协作工具" },
];
export default function Tools() {
return (
<section id="tools" className="py-20 sm:py-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center">
<div className="mb-4 inline-flex items-center gap-2 rounded-full border border-sky-200 bg-sky-50 px-4 py-1.5 text-sm font-medium text-sky-700">
🚀 50+
</div>
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
<span className="gradient-text"></span>
</h2>
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600">
8
</p>
</div>
<div className="mt-14 grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
{categories.map((cat) => (
<div
key={cat.title}
className="card-hover group overflow-hidden rounded-2xl border border-slate-100 bg-white shadow-sm"
>
<div className={`h-1.5 bg-gradient-to-r ${cat.color}`} />
<div className="p-5">
<div className="mb-3 flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-2xl">{cat.icon}</span>
<h3 className="font-bold text-slate-900">{cat.title}</h3>
</div>
<span className={`rounded-full px-2 py-0.5 text-xs font-semibold ${cat.bg} ${cat.text}`}>
{cat.count}
</span>
</div>
<div className="space-y-2">
{cat.tools.map((tool) => (
<div
key={tool.name}
className="flex items-center justify-between rounded-lg bg-slate-50 px-3 py-2 text-sm transition-colors group-hover:bg-slate-100/80"
>
<span className="font-medium text-slate-700">
{tool.name}
</span>
<span className="text-slate-400">{tool.desc}</span>
</div>
))}
</div>
</div>
</div>
))}
</div>
{/* Stats bar */}
<div className="mt-14 grid grid-cols-2 gap-4 sm:grid-cols-4">
{toolStats.map((s) => (
<div
key={s.label}
className="rounded-2xl border border-slate-100 bg-white p-5 text-center shadow-sm"
>
<div className="text-3xl font-extrabold text-sky-600">
{s.value}
</div>
<div className="mt-1 text-sm text-slate-500">{s.label}</div>
</div>
))}
</div>
</div>
</section>
);
}