This commit is contained in:
eric
2026-03-07 12:33:14 -06:00
parent 8b034e255b
commit 9bb8bcdf0b
28 changed files with 7231 additions and 78 deletions

189
app/components/CityCard.tsx Normal file
View File

@@ -0,0 +1,189 @@
"use client";
import { useState } from "react";
import { City } from "../data/cities";
function ratingToPercent(rating: string): number {
const m: Record<string, number> = {
极好: 95,
很好: 80,
: 60,
一般: 40,
: 20,
};
return m[rating] || 50;
}
function getBarColor(p: number): string {
if (p >= 60) return "#22c55e";
if (p >= 35) return "#eab308";
return "#ef4444";
}
function ScoreBar({
icon,
label,
percent,
animate,
}: {
icon: string;
label: string;
percent: number;
animate: boolean;
}) {
return (
<div className="flex items-center gap-2 sm:gap-3">
<span className="text-white/80 text-xs sm:text-sm w-14 sm:w-16 shrink-0 flex items-center gap-1">
<span>{icon}</span>
<span className="hidden sm:inline">{label}</span>
</span>
<div className="flex-1 h-2 sm:h-2.5 bg-white/10 rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all duration-700 ease-out"
style={{
width: animate ? `${percent}%` : "0%",
backgroundColor: getBarColor(percent),
}}
/>
</div>
</div>
);
}
export default function CityCard({
city,
rank,
}: {
city: City;
rank: number;
}) {
const [hovered, setHovered] = useState(false);
const scores = [
{
icon: "⭐",
label: "综合",
percent: (city.overallScore / 5) * 100,
},
{
icon: "💵",
label: "费用",
percent: Math.max(15, 100 - city.costPerMonth / 200),
},
{
icon: "📡",
label: "网速",
percent: Math.min(95, (city.internetSpeed / 120) * 100),
},
{
icon: "👍",
label: "好评",
percent: ratingToPercent(city.liked),
},
{
icon: "👮",
label: "安全",
percent: ratingToPercent(city.safety),
},
];
return (
<div
className="city-card"
style={{
background: `linear-gradient(135deg, ${city.gradientFrom}, ${city.gradientTo})`,
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{/* Icon watermark */}
<span className="absolute bottom-8 right-2 text-[70px] sm:text-[90px] opacity-[0.12] select-none pointer-events-none leading-none">
{city.icon}
</span>
{/* ===== Default state ===== */}
<div
className={`absolute inset-0 z-10 flex flex-col justify-between p-3 sm:p-4 transition-opacity duration-300 ${hovered ? "opacity-0" : "opacity-100"}`}
>
{/* Top row */}
<div className="flex items-start justify-between">
<span className="text-white/70 text-base sm:text-xl font-bold">
{rank}
</span>
<span className="text-[10px] sm:text-xs bg-white/20 backdrop-blur-sm rounded-full px-1.5 sm:px-2 py-0.5 sm:py-1 text-white flex items-center gap-0.5">
📡 {city.internetSpeed}Mbps
</span>
</div>
{/* Center: city name + country */}
<div className="text-center -mt-2">
<h3 className="text-xl sm:text-[28px] font-bold text-white drop-shadow-md tracking-wide">
{city.name}
</h3>
<p className="text-xs sm:text-sm text-white/60 mt-0.5">
{city.countryEmoji} {city.country}
</p>
</div>
{/* Bottom: temp + cost + nomads */}
<div>
<div className="flex items-center gap-1 mb-1.5">
<span className="text-[9px] sm:text-[11px] bg-white/15 backdrop-blur-sm rounded-full px-1.5 py-0.5 text-white/80">
👥 {city.nomadsNow}
</span>
<span className="text-[9px] sm:text-[11px] bg-white/15 backdrop-blur-sm rounded-full px-1.5 py-0.5 text-white/80">
🌡 {city.temperature}°C
</span>
</div>
<div className="flex items-end justify-between text-white text-xs sm:text-sm">
<span className="text-white/80 text-[10px] sm:text-xs line-clamp-1 max-w-[60%]">
{city.description}
</span>
<span className="font-semibold shrink-0">
¥{city.costPerMonth.toLocaleString()}/
</span>
</div>
</div>
</div>
{/* ===== Hover overlay (desktop only) ===== */}
<div
className={`absolute inset-0 z-20 bg-[#2a2a2a]/95 hidden sm:flex flex-col justify-between p-4 sm:p-5 transition-opacity duration-300 ${hovered ? "opacity-100" : "opacity-0 pointer-events-none"}`}
>
{/* Top icons */}
<div className="flex items-center justify-between">
<button className="text-white/50 hover:text-red-400 transition-colors text-base">
</button>
<button
className="text-white/30 hover:text-white text-sm transition-colors"
onClick={(e) => {
e.stopPropagation();
setHovered(false);
}}
>
</button>
</div>
{/* Score bars */}
<div className="space-y-2.5 sm:space-y-3">
{scores.map((s) => (
<ScoreBar
key={s.label}
icon={s.icon}
label={s.label}
percent={s.percent}
animate={hovered}
/>
))}
</div>
{/* Bottom: city info small */}
<div className="text-center text-white/40 text-xs">
{city.name} · {city.countryEmoji} {city.country}
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,97 @@
"use client";
import { allTags } from "../data/cities";
interface FilterBarProps {
activeFilter: string;
onFilterChange: (filter: string) => void;
sortBy: string;
onSortChange: (sort: string) => void;
resultCount: number;
}
const sortOptions = [
{ value: "score", label: "综合评分" },
{ value: "cost-asc", label: "费用最低" },
{ value: "cost-desc", label: "费用最高" },
{ value: "internet", label: "网速最快" },
{ value: "safety", label: "最安全" },
{ value: "nomads", label: "游民最多" },
{ value: "temperature", label: "最温暖" },
];
const tagIcons: Record<string, string> = {
: "🌍",
: "🔥",
便: "💰",
: "📡",
: "☀️",
: "🛡️",
: "🏖️",
: "🏔️",
: "🌆",
: "🍜",
: "🎨",
线: "🏙️",
线: "⭐",
: "🏡",
: "🏛️",
: "🤝",
: "🏄",
};
export default function FilterBar({
activeFilter,
onFilterChange,
sortBy,
onSortChange,
resultCount,
}: FilterBarProps) {
return (
<div className="mb-6" id="cities">
{/* Top bar: result count + sort */}
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<h2 className="text-lg font-bold text-gray-900">
</h2>
<span className="text-sm text-gray-400">
{resultCount}
</span>
</div>
<div className="flex items-center gap-2">
<span className="text-xs text-gray-400 hidden sm:block">:</span>
<select
value={sortBy}
onChange={(e) => onSortChange(e.target.value)}
className="text-sm bg-white border border-gray-200 rounded-lg px-3 py-1.5 text-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-200 cursor-pointer"
>
{sortOptions.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
</div>
{/* Filter tags */}
<div className="flex gap-2 overflow-x-auto hide-scrollbar pb-2">
{allTags.map((tag) => (
<button
key={tag}
onClick={() => onFilterChange(tag)}
className={`filter-btn shrink-0 flex items-center gap-1 px-3 py-1.5 rounded-full text-sm font-medium border transition-all ${
activeFilter === tag
? "active border-gray-900"
: "border-gray-200 text-gray-600 hover:border-gray-300"
}`}
>
<span className="text-xs">{tagIcons[tag] || "🏷️"}</span>
<span>{tag}</span>
</button>
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,189 @@
"use client";
import { useState } from "react";
import Link from "next/link";
const avatarPhotos = [
"https://i.pravatar.cc/150?img=32",
"https://i.pravatar.cc/150?img=33",
"https://i.pravatar.cc/150?img=25",
"https://i.pravatar.cc/150?img=52",
"https://i.pravatar.cc/150?img=44",
"https://i.pravatar.cc/150?img=55",
"https://i.pravatar.cc/150?img=47",
"https://i.pravatar.cc/150?img=57",
"https://i.pravatar.cc/150?img=48",
"https://i.pravatar.cc/150?img=59",
"https://i.pravatar.cc/150?img=49",
];
const features = [
{ icon: "🍹", text: "每年在 30+ 城市参加线下聚会" },
{ icon: "❤️", text: "结识新朋友,约会交友" },
{ icon: "🧪", text: "研究目的地,找到最适合居住和工作的地方" },
{ icon: "🌎", text: "追踪你的旅行,记录你去过的地方" },
{ icon: "💬", text: "加入社群聊天,在旅途中找到你的归属" },
];
export default function HeroSection() {
const [email, setEmail] = useState("");
const handleJoin = (e: React.FormEvent) => {
e.preventDefault();
if (email.trim()) {
alert(`欢迎加入游牧中国!我们将发送确认邮件到 ${email}`);
}
};
return (
<section className="relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-orange-50/70 via-white to-[#fafafa]" />
<div
className="absolute top-0 right-0 w-[500px] h-[500px] -translate-y-1/3 translate-x-1/4 opacity-40 rounded-full"
style={{
background:
"radial-gradient(circle, rgba(249,115,22,0.25) 0%, transparent 70%)",
}}
/>
<div
className="absolute bottom-0 left-0 w-[350px] h-[350px] translate-y-1/3 -translate-x-1/4 opacity-30 rounded-full"
style={{
background:
"radial-gradient(circle, rgba(139,92,246,0.2) 0%, transparent 70%)",
}}
/>
<div className="relative max-w-[1400px] mx-auto px-5 sm:px-10 py-10 sm:py-14 md:py-20">
<div className="flex flex-col lg:flex-row lg:items-start lg:gap-10 xl:gap-16">
{/* Left Column */}
<div className="flex-1 min-w-0">
<span className="inline-flex items-center gap-1.5 text-xs font-semibold text-orange-700 bg-orange-100 px-3 py-1.5 rounded-full mb-5">
🏆 · 2024
</span>
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight leading-tight mb-5">
<span className="hero-gradient-text"></span>
<br />
<span className="text-gray-900"></span>
</h1>
<p className="text-base sm:text-lg text-gray-500 max-w-2xl mb-7 leading-relaxed">
</p>
<div className="space-y-2.5 mb-8">
{features.map((f) => (
<div key={f.text} className="flex items-start gap-2.5">
<span className="text-base sm:text-lg shrink-0 mt-0.5">
{f.icon}
</span>
<span className="text-sm sm:text-base text-gray-600 underline decoration-gray-300 underline-offset-2">
{f.text}
</span>
</div>
))}
</div>
<div className="flex items-center gap-3 mb-8">
<div className="flex -space-x-2">
{avatarPhotos.slice(0, 8).map((src, i) => (
<img
key={i}
src={src}
alt=""
className="w-8 h-8 rounded-full border-2 border-white shadow-sm object-cover"
/>
))}
</div>
<span className="text-sm text-gray-500">
<strong className="text-gray-900">5,000+</strong>
</span>
</div>
<div className="pt-7 border-t border-gray-100">
<p className="text-xs text-gray-400 uppercase tracking-wider mb-3">
</p>
<div className="flex flex-wrap items-center gap-x-8 gap-y-2">
{["36氪", "少数派", "极客公园", "虎嗅", "创业邦", "澎湃新闻"].map(
(name) => (
<span
key={name}
className="text-sm font-semibold text-gray-300 hover:text-gray-400 transition-colors cursor-default"
>
{name}
</span>
),
)}
</div>
</div>
</div>
{/* Right Column: Video + Email */}
<div className="lg:w-[360px] xl:w-[400px] shrink-0 mt-10 lg:mt-4">
<div className="bg-white rounded-2xl shadow-xl overflow-hidden border border-gray-100">
<div className="relative h-48 sm:h-56 bg-gradient-to-br from-emerald-400 via-teal-500 to-cyan-600 flex items-center justify-center cursor-pointer group overflow-hidden">
<div className="absolute inset-0 opacity-20">
<div className="absolute top-4 left-4 w-20 h-16 rounded-lg bg-white/20" />
<div className="absolute bottom-6 right-6 w-24 h-14 rounded-lg bg-white/15" />
<div className="absolute top-1/2 left-1/3 w-16 h-16 rounded-full bg-white/10" />
</div>
<div className="absolute bottom-0 left-0 right-0 h-1/3 bg-gradient-to-t from-black/20 to-transparent" />
<button className="relative w-16 h-16 rounded-full bg-white/95 flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform">
<svg
className="w-7 h-7 text-emerald-600 ml-1"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M8 5v14l11-7z" />
</svg>
</button>
</div>
<form onSubmit={handleJoin} className="p-5">
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="输入你的邮箱..."
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-[#ff4d4f]/20 focus:border-[#ff4d4f] transition-colors mb-3"
/>
<button
type="submit"
className="w-full bg-[#ff4d4f] hover:bg-[#ff3333] text-white py-3 rounded-lg text-sm font-semibold transition-colors active:scale-[0.98]"
>
</button>
<p className="text-xs text-gray-400 text-center mt-3">
</p>
</form>
</div>
<div className="mt-4 flex flex-wrap gap-2 justify-center">
<Link
href="/meetups"
className="text-xs text-gray-400 hover:text-gray-600 bg-white rounded-full px-3 py-1.5 border border-gray-100 transition-colors"
>
🍹 线
</Link>
<Link
href="/dating"
className="text-xs text-gray-400 hover:text-gray-600 bg-white rounded-full px-3 py-1.5 border border-gray-100 transition-colors"
>
</Link>
<Link
href="/join"
className="text-xs text-gray-400 hover:text-gray-600 bg-white rounded-full px-3 py-1.5 border border-gray-100 transition-colors"
>
💬
</Link>
</div>
</div>
</div>
</div>
</section>
);
}

110
app/components/Navbar.tsx Normal file
View File

@@ -0,0 +1,110 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
const navLinks = [
{ label: "城市", icon: "🏙️", href: "/" },
{ label: "旅行", icon: "✈️", href: "/meetups" },
{ label: "社区", icon: "💬", href: "/dating" },
{ label: "探索", icon: "🎒", href: "/#community" },
];
export default function Navbar() {
const [mobileOpen, setMobileOpen] = useState(false);
const pathname = usePathname();
function isActive(href: string) {
if (href.startsWith("/#")) return false;
if (href === "/") return pathname === "/";
return pathname.startsWith(href);
}
return (
<nav className="sticky top-0 z-50 bg-white/80 backdrop-blur-lg border-b border-gray-100">
<div className="max-w-[1600px] mx-auto px-4 sm:px-6 h-16 flex items-center justify-between">
{/* Logo */}
<Link href="/" className="flex items-center gap-2 shrink-0">
<span className="text-2xl">🌏</span>
<span className="text-lg font-bold text-gray-900 tracking-tight">
</span>
</Link>
{/* Center Nav */}
<div className="hidden md:flex items-center gap-1">
{navLinks.map((link) => (
<Link
key={link.label}
href={link.href}
className={`flex items-center gap-1.5 px-4 py-2 rounded-full text-sm font-medium transition-colors ${
isActive(link.href)
? "text-gray-900 bg-gray-100"
: "text-gray-500 hover:text-gray-900 hover:bg-gray-50"
}`}
>
<span>{link.icon}</span>
<span>{link.label}</span>
</Link>
))}
</div>
{/* Right Side */}
<div className="flex items-center gap-2 sm:gap-3">
<button className="hidden sm:block text-sm text-gray-600 hover:text-gray-900 px-3 py-2 rounded-full hover:bg-gray-50 transition-colors">
</button>
<Link
href="/join"
className="text-sm bg-[#ff4d4f] text-white px-4 py-2 rounded-full hover:bg-[#ff7a45] transition-colors font-medium"
>
</Link>
{/* Mobile hamburger */}
<button
className="md:hidden ml-1 p-2 rounded-lg hover:bg-gray-100"
onClick={() => setMobileOpen(!mobileOpen)}
>
{mobileOpen ? (
<svg width="20" height="20" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path d="M6 18L18 6M6 6l12 12" />
</svg>
) : (
<svg width="20" height="20" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path d="M4 6h16M4 12h16M4 18h16" />
</svg>
)}
</button>
</div>
</div>
{/* Mobile Menu */}
{mobileOpen && (
<div className="md:hidden border-t border-gray-100 bg-white px-4 py-3 space-y-1">
{navLinks.map((link) => (
<Link
key={link.label}
href={link.href}
className={`flex items-center gap-2 px-4 py-3 rounded-xl text-sm font-medium ${
isActive(link.href)
? "text-gray-900 bg-gray-50"
: "text-gray-500 hover:bg-gray-50"
}`}
onClick={() => setMobileOpen(false)}
>
<span className="text-lg">{link.icon}</span>
<span>{link.label}</span>
</Link>
))}
<div className="pt-2 border-t border-gray-100">
<button className="w-full text-left px-4 py-3 text-sm text-gray-600 hover:bg-gray-50 rounded-xl">
</button>
</div>
</div>
)}
</nav>
);
}

245
app/components/Sidebar.tsx Normal file
View File

@@ -0,0 +1,245 @@
const meetups = [
{ city: "清迈", emoji: "🇹🇭", date: "3月9日 周一", attendees: 12 },
{ city: "曼谷", emoji: "🇹🇭", date: "3月12日 周四", attendees: 8 },
{ city: "巴厘岛", emoji: "🇮🇩", date: "3月14日 周六", attendees: 15 },
{ city: "大理", emoji: "🇨🇳", date: "3月16日 周一", attendees: 6 },
{ city: "里斯本", emoji: "🇵🇹", date: "3月20日 周五", attendees: 23 },
];
const travelingNow = [
{ name: "小明", bg: "#f43f5e", destination: "清迈" },
{ name: "雨晴", bg: "#8b5cf6", destination: "巴厘岛" },
{ name: "阿杰", bg: "#3b82f6", destination: "曼谷" },
{ name: "小燕", bg: "#10b981", destination: "东京" },
{ name: "大伟", bg: "#f59e0b", destination: "里斯本" },
{ name: "佳琪", bg: "#ec4899", destination: "墨西哥城" },
{ name: "志远", bg: "#06b6d4", destination: "柏林" },
{ name: "心怡", bg: "#ef4444", destination: "首尔" },
{ name: "凯文", bg: "#84cc16", destination: "巴塞罗那" },
{ name: "子豪", bg: "#7c3aed", destination: "成都" },
{ name: "浩然", bg: "#0d9488", destination: "大理" },
{ name: "雅琪", bg: "#ea580c", destination: "台北" },
{ name: "天翔", bg: "#6366f1", destination: "布达佩斯" },
{ name: "美玲", bg: "#d946ef", destination: "梅德林" },
{ name: "俊杰", bg: "#14b8a6", destination: "普吉岛" },
{ name: "晓华", bg: "#f97316", destination: "吉隆坡" },
];
const newMembers = [
{ name: "林", bg: "#3b82f6" },
{ name: "张", bg: "#8b5cf6" },
{ name: "陈", bg: "#f43f5e" },
{ name: "周", bg: "#10b981" },
{ name: "吴", bg: "#f59e0b" },
{ name: "杨", bg: "#ec4899" },
{ name: "赵", bg: "#06b6d4" },
{ name: "黄", bg: "#ef4444" },
{ name: "孙", bg: "#84cc16" },
{ name: "马", bg: "#7c3aed" },
{ name: "朱", bg: "#0d9488" },
{ name: "胡", bg: "#ea580c" },
{ name: "郭", bg: "#6366f1" },
{ name: "何", bg: "#d946ef" },
{ name: "高", bg: "#14b8a6" },
{ name: "梁", bg: "#f97316" },
{ name: "郑", bg: "#a855f7" },
{ name: "罗", bg: "#22c55e" },
{ name: "谢", bg: "#e11d48" },
{ name: "韩", bg: "#0ea5e9" },
];
const hotTopics = [
{ title: "清迈最佳共享办公空间推荐 Top 10", views: 2340 },
{ title: "2026年数字游民签证政策汇总", views: 1890 },
{ title: "远程工作者的跨国税务规划指南", views: 1560 },
{ title: "东南亚 vs 拉美:哪个更适合游民?", views: 1230 },
{ title: "新手游民必备工具与装备清单", views: 980 },
];
export default function Sidebar() {
return (
<aside className="w-[320px] shrink-0 hidden lg:block">
<div className="sticky top-20 space-y-4 sidebar-scroll max-h-[calc(100vh-100px)] overflow-y-auto pr-1">
{/* Insurance / Visa Promo */}
<div className="rounded-2xl bg-gradient-to-br from-emerald-50 to-teal-50 border border-emerald-100 p-5">
<div className="flex items-start gap-3 mb-3">
<span className="text-2xl">🛡</span>
<div>
<h3 className="font-bold text-gray-900 text-sm"></h3>
<p className="text-xs text-gray-500 mt-0.5">
</p>
</div>
</div>
<p className="text-xs text-emerald-700 bg-emerald-100/50 rounded-lg px-3 py-2 mb-3">
195+ ¥15/
</p>
<button className="w-full text-center text-sm font-semibold text-emerald-700 hover:text-emerald-800 bg-white border border-emerald-200 rounded-full py-2 transition-colors hover:bg-emerald-50">
</button>
<p className="text-[10px] text-gray-400 text-center mt-2">广</p>
</div>
{/* Upcoming Meetups */}
<div className="rounded-2xl bg-white border border-gray-100 shadow-sm p-5">
<div className="flex items-center justify-between mb-4">
<h3 className="font-bold text-gray-900 text-sm flex items-center gap-1.5">
🍹
</h3>
<span className="text-[11px] text-gray-400">8/</span>
</div>
<div className="space-y-3">
{meetups.map((m) => (
<div
key={m.city + m.date}
className="flex items-center justify-between py-2 border-b border-gray-50 last:border-0 last:pb-0"
>
<div className="flex items-center gap-2.5">
<span className="text-lg">{m.emoji}</span>
<div>
<p className="text-sm font-medium text-gray-800">
{m.city}
</p>
<p className="text-[11px] text-gray-400">{m.date}</p>
</div>
</div>
<span className="text-[11px] text-gray-400 bg-gray-50 rounded-full px-2 py-0.5">
{m.attendees}
</span>
</div>
))}
</div>
<button className="w-full text-center text-xs font-medium text-gray-500 hover:text-gray-700 mt-3 pt-3 border-t border-gray-100 transition-colors">
</button>
</div>
{/* Traveling Now */}
<div className="rounded-2xl bg-white border border-gray-100 shadow-sm p-5">
<div className="flex items-center justify-between mb-4">
<h3 className="font-bold text-gray-900 text-sm flex items-center gap-1.5">
🛩
</h3>
<span className="text-[11px] text-gray-400">
{travelingNow.length}
</span>
</div>
<div className="flex flex-wrap gap-2">
{travelingNow.map((m) => (
<div key={m.name} className="group relative">
<div
className="w-9 h-9 rounded-full flex items-center justify-center text-xs text-white font-medium shadow-sm cursor-pointer hover:scale-110 transition-transform avatar-online"
style={{ backgroundColor: m.bg }}
title={`${m.name} · ${m.destination}`}
>
{m.name.charAt(0)}
</div>
</div>
))}
</div>
</div>
{/* New Members */}
<div className="rounded-2xl bg-white border border-gray-100 shadow-sm p-5">
<div className="flex items-center justify-between mb-4">
<h3 className="font-bold text-gray-900 text-sm flex items-center gap-1.5">
👋
</h3>
<span className="text-[11px] text-gray-400">86/</span>
</div>
<div className="flex flex-wrap gap-2">
{newMembers.map((m) => (
<div
key={m.name}
className="w-9 h-9 rounded-full flex items-center justify-center text-xs text-white font-medium shadow-sm cursor-pointer hover:scale-110 transition-transform"
style={{ backgroundColor: m.bg }}
>
{m.name}
</div>
))}
</div>
</div>
{/* Hot Topics */}
<div className="rounded-2xl bg-white border border-gray-100 shadow-sm p-5">
<h3 className="font-bold text-gray-900 text-sm flex items-center gap-1.5 mb-4">
🔥
</h3>
<div className="space-y-3">
{hotTopics.map((t, i) => (
<a
key={t.title}
href="#"
className="flex items-start gap-2.5 group"
>
<span className="text-xs font-bold text-gray-300 mt-0.5 w-4 shrink-0">
{i + 1}
</span>
<div className="flex-1 min-w-0">
<p className="text-sm text-gray-700 group-hover:text-gray-900 transition-colors line-clamp-1 leading-snug">
{t.title}
</p>
<p className="text-[11px] text-gray-400 mt-0.5">
{t.views.toLocaleString()}
</p>
</div>
</a>
))}
</div>
<button className="w-full text-center text-xs font-medium text-gray-500 hover:text-gray-700 mt-3 pt-3 border-t border-gray-100 transition-colors">
</button>
</div>
{/* Chat CTA */}
<div className="rounded-2xl bg-gradient-to-br from-violet-50 to-purple-50 border border-violet-100 p-5">
<div className="flex items-start gap-3 mb-3">
<span className="text-2xl">💬</span>
<div>
<h3 className="font-bold text-gray-900 text-sm">
</h3>
<p className="text-xs text-gray-500 mt-0.5">
3,200+
</p>
</div>
</div>
<button className="w-full text-center text-sm font-semibold text-violet-700 hover:text-violet-800 bg-white border border-violet-200 rounded-full py-2 transition-colors hover:bg-violet-50">
</button>
</div>
{/* Visa Service */}
<div className="rounded-2xl bg-gradient-to-br from-amber-50 to-orange-50 border border-amber-100 p-5">
<div className="flex items-start gap-3 mb-3">
<span className="text-2xl">🏛</span>
<div>
<h3 className="font-bold text-gray-900 text-sm"></h3>
<p className="text-xs text-gray-500 mt-0.5">
</p>
</div>
</div>
<div className="flex flex-wrap gap-1.5 mb-3">
<span className="text-[11px] bg-amber-100/60 text-amber-700 rounded-full px-2 py-0.5">
🇹🇭
</span>
<span className="text-[11px] bg-amber-100/60 text-amber-700 rounded-full px-2 py-0.5">
🇵🇹
</span>
<span className="text-[11px] bg-amber-100/60 text-amber-700 rounded-full px-2 py-0.5">
🇪🇸 西
</span>
<span className="text-[11px] bg-amber-100/60 text-amber-700 rounded-full px-2 py-0.5">
🇮🇩
</span>
</div>
<button className="w-full text-center text-sm font-semibold text-amber-700 hover:text-amber-800 bg-white border border-amber-200 rounded-full py-2 transition-colors hover:bg-amber-50">
</button>
<p className="text-[10px] text-gray-400 text-center mt-2">广</p>
</div>
</div>
</aside>
);
}