Add tools hub, changelog, ATM guide, and slim section nav

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
eric 2026-08-29 00:24:44 -05:00
parent 04f8c14ed5
commit 4ab2d7e412
12 changed files with 470 additions and 40 deletions

View File

@ -127,6 +127,9 @@ docker compose up -d
| 🆘 紧急联系 | 急救、医院、领事馆速查 |
| 📝 城市笔记 | 按城本地记录避坑与灵感 |
| ⏱️ 专注时钟 | 番茄钟深度工作计时 |
| 🛠️ 工具箱页 | `/tools` 分类搜索全部工具 |
| 📜 更新日志 | `/changelog` 迭代记录 |
| 🏧 取现避坑 | ATM / DCC / 换汇建议 |
| 📝 博客 | 标签筛选 + 阅读进度 + 相关推荐 + 分享 |
| ❓ FAQ | 搜索 + 展开/收起全部 |

View File

@ -0,0 +1,76 @@
import Link from "next/link";
import type { Metadata } from "next";
import SiteShell from "@/components/SiteShell";
export const metadata: Metadata = {
title: "更新日志 · nomadro",
description: "nomadro 产品更新与功能迭代记录",
};
const LOGS = [
{
date: "2026-08-29",
tag: "工具箱",
items: [
"新增 /tools 工具聚合页,按分类快速跳转",
"精简首页章节导航,核心入口更清晰",
"新增 ATM 取现避坑指南",
],
},
{
date: "2026-08-29",
tag: "实用工具",
items: [
"电源插座、紧急联系、城市笔记、专注时钟",
"税居天数、到站清单、咖啡馆推荐、新手引导",
"保险指南、支出账本、SIM/eSIM 方案",
],
},
{
date: "2026-08-28",
tag: "品牌",
items: [
"品牌统一为 nomadro",
"命运转盘、机票估算、活动日历、隐私政策",
"签证智能匹配、联合办公、行李清单",
],
},
];
export default function ChangelogPage() {
return (
<SiteShell showFooter>
<div className="legal-page">
<nav className="detail-nav">
<Link href="/">← 返回首页</Link>
</nav>
<article className="legal-content">
<span className="section-tag">📜 CHANGELOG</span>
<h1>更新日志</h1>
<p className="legal-updated">记录 nomadro 的每一次出发</p>
<div className="changelog-list">
{LOGS.map((log) => (
<section key={log.date + log.tag} className="changelog-item">
<div className="changelog-meta">
<time>{log.date}</time>
<span className="changelog-tag">{log.tag}</span>
</div>
<ul>
{log.items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</section>
))}
</div>
<div className="about-actions">
<Link href="/tools" className="btn btn-primary">打开工具箱 🛠️</Link>
<Link href="/about" className="btn btn-ghost">关于 nomadro</Link>
</div>
</article>
</div>
</SiteShell>
);
}

View File

@ -7327,3 +7327,158 @@ img { max-width: 100%; display: block; }
}
.notes-count { width: 100%; margin-top: 4px; }
}
/* ===== Tools Hub ===== */
.tools-hub-page {
padding: calc(var(--nav-height) + 40px) 0 80px;
position: relative;
z-index: 1;
}
.tools-hub-page .section-header h1 {
font-size: clamp(1.8rem, 4vw, 2.4rem);
margin: 12px 0 8px;
}
.tools-hub-toolbar {
margin: 28px auto 32px;
max-width: 900px;
}
.tools-hub-search {
width: 100%;
padding: 14px 18px;
margin-bottom: 14px;
background: var(--bg-card);
border: var(--border-glass);
border-radius: var(--radius-md);
color: var(--text-primary);
font-size: 0.95rem;
}
.tools-hub-search:focus {
outline: none;
border-color: rgba(78, 205, 196, 0.5);
}
.tools-hub-filters {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
}
.tools-hub-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
.tools-hub-card {
padding: 20px;
background: var(--bg-card);
border: var(--border-glass);
border-radius: var(--radius-lg);
backdrop-filter: blur(16px);
transition: transform 0.25s, border-color 0.25s;
display: flex;
flex-direction: column;
}
.tools-hub-card:hover {
transform: translateY(-4px);
border-color: rgba(78, 205, 196, 0.35);
}
.tools-hub-emoji { font-size: 1.8rem; margin-bottom: 8px; }
.tools-hub-card h3 { font-size: 1.05rem; margin-bottom: 6px; }
.tools-hub-card p {
font-size: 0.85rem;
color: var(--text-secondary);
line-height: 1.45;
flex: 1;
margin-bottom: 12px;
}
.tools-hub-go {
font-size: 0.85rem;
color: var(--accent-3);
font-weight: 600;
}
.section-nav-tools {
text-decoration: none;
color: inherit;
border: 1px dashed rgba(78, 205, 196, 0.35) !important;
}
/* ===== ATM Guide ===== */
.atm-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 16px;
}
.atm-card {
padding: 22px;
background: var(--bg-card);
border: var(--border-glass);
border-radius: var(--radius-lg);
backdrop-filter: blur(16px);
transition: transform 0.3s;
}
.atm-card:hover { transform: translateY(-3px); }
.atm-emoji { font-size: 1.8rem; display: block; margin-bottom: 8px; }
.atm-card h4 { font-size: 1.05rem; margin-bottom: 8px; }
.atm-card p { font-size: 0.85rem; color: var(--text-secondary); line-height: 1.5; }
/* ===== Changelog ===== */
.changelog-list { margin: 28px 0; }
.changelog-item {
padding: 20px 0;
border-bottom: 1px solid rgba(255,255,255,0.06);
}
.changelog-meta {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.changelog-meta time {
font-size: 0.85rem;
color: var(--text-secondary);
}
.changelog-tag {
font-size: 0.75rem;
padding: 3px 10px;
background: rgba(78, 205, 196, 0.12);
color: var(--accent-3);
border-radius: 12px;
font-weight: 600;
}
.changelog-item ul {
list-style: none;
padding: 0;
margin: 0;
}
.changelog-item li {
position: relative;
padding: 6px 0 6px 16px;
color: var(--text-secondary);
line-height: 1.5;
font-size: 0.95rem;
}
.changelog-item li::before {
content: "•";
position: absolute;
left: 0;
color: var(--accent-3);
}

View File

@ -12,6 +12,8 @@ export default function sitemap(): MetadataRoute.Sitemap {
{ url: `${SITE}/login`, lastModified: now, changeFrequency: "monthly", priority: 0.5 },
{ url: `${SITE}/privacy`, lastModified: now, changeFrequency: "yearly", priority: 0.3 },
{ url: `${SITE}/about`, lastModified: now, changeFrequency: "monthly", priority: 0.5 },
{ url: `${SITE}/tools`, lastModified: now, changeFrequency: "weekly", priority: 0.8 },
{ url: `${SITE}/changelog`, lastModified: now, changeFrequency: "weekly", priority: 0.4 },
{ url: `${SITE}/profile`, lastModified: now, changeFrequency: "monthly", priority: 0.4 },
...DESTINATIONS.map((slug) => ({
url: `${SITE}/destinations/${slug}`,

View File

@ -0,0 +1,71 @@
"use client";
import { useMemo, useState } from "react";
import Link from "next/link";
import { TOOL_CATEGORIES, TOOL_LINKS } from "@/lib/tools";
import SiteShell from "@/components/SiteShell";
export default function ToolsHubPage() {
const [filter, setFilter] = useState("all");
const [q, setQ] = useState("");
const filtered = useMemo(() => {
let list = filter === "all" ? TOOL_LINKS : TOOL_LINKS.filter((t) => t.category === filter);
if (q.trim()) {
const s = q.toLowerCase();
list = list.filter((t) => t.title.toLowerCase().includes(s) || t.desc.toLowerCase().includes(s));
}
return list;
}, [filter, q]);
return (
<SiteShell showFooter>
<div className="tools-hub-page">
<div className="container">
<nav className="detail-nav">
<Link href="/">← 返回首页</Link>
</nav>
<div className="section-header">
<span className="section-tag">🛠️ TOOLKIT</span>
<h1>nomadro 工具箱</h1>
<p>规划、金钱、工作、生活与安全——一站直达所有旅居工具</p>
</div>
<div className="tools-hub-toolbar">
<input
className="tools-hub-search"
placeholder="搜索工具,如「签证」「机票」「笔记」…"
value={q}
onChange={(e) => setQ(e.target.value)}
/>
<div className="tools-hub-filters">
{TOOL_CATEGORIES.map((c) => (
<button
key={c.key}
className={`filter-btn${filter === c.key ? " active" : ""}`}
onClick={() => setFilter(c.key)}
>
{c.label}
</button>
))}
</div>
</div>
<div className="tools-hub-grid">
{filtered.map((t) => (
<Link key={t.id} href={t.href} className="tools-hub-card">
<span className="tools-hub-emoji">{t.emoji}</span>
<h3>{t.title}</h3>
<p>{t.desc}</p>
<span className="tools-hub-go">打开 →</span>
</Link>
))}
</div>
{filtered.length === 0 && (
<p className="dest-empty">没有匹配的工具,试试其他关键词</p>
)}
</div>
</div>
</SiteShell>
);
}

View File

@ -0,0 +1,89 @@
"use client";
import { useMemo, useState } from "react";
import type { Destination } from "@/lib/types";
interface AtmTip {
emoji: string;
title: string;
detail: string;
}
const TIPS: Record<string, AtmTip[]> = {
bali: [
{ emoji: "🏦", title: "优先银行 ATM", detail: "BCA / Mandiri 手续费相对透明,避开独立小黄机。" },
{ emoji: "💳", title: "Wise / 实体卡", detail: "尽量用无外汇手续费卡,按需取现。" },
{ emoji: "💵", title: "别一次取太多", detail: "现金够用即可,酒店与餐厅多数可刷卡。" },
],
chiangmai: [
{ emoji: "🏧", title: "Aeon / 银行柜员机", detail: "注意屏幕提示的「动态汇率」选项,尽量选拒绝。" },
{ emoji: "💱", title: "拒绝 DCC", detail: "出现「用人民币结算?」选 No,用泰铢记账更划算。" },
{ emoji: "🧾", title: "保留小票", detail: "便于核对手续费与异常扣款。" },
],
lisbon: [
{ emoji: "🇪🇺", title: "欧元区友好", detail: "多数 ATM 支持国际卡,留意 Multibanco 标识。" },
{ emoji: "🚫", title: "拒绝动态货币转换", detail: "选以欧元扣款,避免银行额外加点。" },
{ emoji: "🪙", title: "小面额现金", detail: "市场与有轨电车偶尔需要硬币。" },
],
barcelona: [
{ emoji: "🏧", title: "银行网点 ATM", detail: "CaixaBank / Santander 更稳,避开街头独立机。" },
{ emoji: "🚫", title: "拒 DCC", detail: "选欧元结算,别让机器帮你换汇。" },
{ emoji: "👛", title: "现金备用量", detail: "堂食可刷卡,小摊与夜市准备现金。" },
],
mexico: [
{ emoji: "🏦", title: "BBVA / Santander", detail: "大行 ATM 更安全,白天取现。" },
{ emoji: "⚠️", title: "警惕侧录", detail: "检查插卡口是否松动,优先商场内机器。" },
{ emoji: "💵", title: "小费文化", detail: "预留小额比索现金给餐厅与快递。" },
],
tokyo: [
{ emoji: "🏪", title: "7-Eleven ATM", detail: "对国际卡最友好,便利店随处可取。" },
{ emoji: "💴", title: "日元现金仍重要", detail: "部分居酒屋与神社只收现金。" },
{ emoji: "💱", title: "少在机场换汇", detail: "汇率通常较差,落地后便利店 ATM 更优。" },
],
};
interface Props {
destinations: Destination[];
}
export default function AtmCashGuide({ destinations }: Props) {
const [slug, setSlug] = useState(destinations[0]?.slug || "chiangmai");
const tips = useMemo(() => TIPS[slug] || TIPS.chiangmai, [slug]);
const dest = destinations.find((d) => d.slug === slug);
return (
<section className="section atm-section" id="atm">
<div className="container">
<div className="section-header reveal">
<span className="section-tag">🏧 ATM</span>
<h2>取现与换汇避坑</h2>
<p>拒 DCC、选对机器,少交「智商税」</p>
</div>
<div className="housing-cities reveal">
{destinations.map((d) => (
<button
key={d.slug}
className={`season-city-btn${slug === d.slug ? " active" : ""}`}
onClick={() => setSlug(d.slug)}
>
{d.emoji} {d.name}
</button>
))}
</div>
<h3 className="sim-city-title reveal">{dest?.emoji} {dest?.name}</h3>
<div className="atm-grid">
{tips.map((t, i) => (
<div key={t.title} className="atm-card reveal" style={{ transitionDelay: `${i * 0.08}s` }}>
<span className="atm-emoji">{t.emoji}</span>
<h4>{t.title}</h4>
<p>{t.detail}</p>
</div>
))}
</div>
</div>
</section>
);
}

View File

@ -40,28 +40,24 @@ export default function Footer() {
<div className="footer-links">
<h4>探索</h4>
<Link href="/#destinations">目的地</Link>
<Link href="/#spin">命运转盘</Link>
<Link href="/#wifi">网速评级</Link>
<Link href="/#cafes">咖啡馆</Link>
<Link href="/#emergency">紧急联系</Link>
<Link href="/#plugs">电源插座</Link>
<Link href="/#notes">城市笔记</Link>
<Link href="/#visa">签证指南</Link>
<Link href="/#events">活动日历</Link>
<Link href="/#blog">博客</Link>
<Link href="/about">关于 nomadro</Link>
</div>
<div className="footer-links">
<h4>工具</h4>
<Link href="/tools">全部工具箱</Link>
<Link href="/#trip">行程规划</Link>
<Link href="/#focus">专注时钟</Link>
<Link href="/#expenses">支出账本</Link>
<Link href="/#tax-days">税居天数</Link>
<Link href="/#calculator">费用计算器</Link>
<Link href="/#atm">取现避坑</Link>
<Link href="/changelog">更新日志</Link>
<a href={`${SITE}/docs`} target="_blank" rel="noopener noreferrer">API 文档</a>
</div>
<div className="footer-links">
<h4>账户</h4>
<Link href="/login">登录 / 注册</Link>
<Link href="/profile">用户中心</Link>
<Link href="/about">关于 nomadro</Link>
<Link href="/privacy">隐私政策</Link>
<a href="mailto:hello@nomadro.com">hello@nomadro.com</a>
</div>

View File

@ -120,6 +120,7 @@ export default function Hero({ stats }: Props) {
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M5 12h14M12 5l7 7-7 7" /></svg>
</a>
<a href="#calculator" className="btn btn-ghost"><span>🧮 费用计算</span></a>
<a href="/tools" className="btn btn-ghost"><span>🛠️ 工具箱</span></a>
<button className="btn btn-ghost hero-matcher-btn" onClick={() => {
window.dispatchEvent(new CustomEvent("open-matcher"));
}}><span>🎯 智能匹配</span></button>

View File

@ -55,6 +55,7 @@ import PowerPlugGuide from "./PowerPlugGuide";
import EmergencyContacts from "./EmergencyContacts";
import CityNotes from "./CityNotes";
import FocusTimer from "./FocusTimer";
import AtmCashGuide from "./AtmCashGuide";
import KeyboardShortcuts from "./KeyboardShortcuts";
import BackToTop from "./BackToTop";
@ -144,6 +145,7 @@ export default function HomeClient(props: Props) {
<VisaSection visas={props.visas} />
<InsuranceGuide />
<EmergencyContacts destinations={props.destinations} />
<AtmCashGuide destinations={props.destinations} />
<CoworkingGuide />
<HousingGuide destinations={props.destinations} />
<SimGuide destinations={props.destinations} />

View File

@ -9,8 +9,8 @@ const LINKS = [
{ href: "/#map", label: "🗺️ 地图", section: "map" },
{ href: "/#destinations", label: "🌍 目的地", section: "destinations" },
{ href: "/#trip", label: "🗓️ 行程", section: "trip" },
{ href: "/#timezone", label: "🕐 时区", section: "timezone" },
{ href: "/#calculator", label: "🧮 计算器", section: "calculator" },
{ href: "/tools", label: "🛠️ 工具箱", section: "tools" },
{ href: "/#visa", label: "📋 签证", section: "visa" },
{ href: "/#blog", label: "📝 博客", section: "blog" },
];
@ -41,7 +41,8 @@ export default function Navbar() {
useEffect(() => {
if (!isHome) return;
const sections = LINKS.map((l) => document.getElementById(l.section)).filter(Boolean);
const homeLinks = LINKS.filter((l) => l.href.startsWith("/#"));
const sections = homeLinks.map((l) => document.getElementById(l.section)).filter(Boolean);
const observer = new IntersectionObserver(
(entries) => entries.forEach((e) => { if (e.isIntersecting) setActive(e.target.id); }),
{ threshold: 0.3, rootMargin: "-80px 0px -50% 0px" }
@ -84,7 +85,11 @@ export default function Navbar() {
<nav className={`nav-links${menuOpen ? " open" : ""}`}>
{LINKS.map((l) => (
<Link key={l.section} href={l.href} data-section={l.section}
className={isHome && active === l.section ? "active" : ""}
className={
l.href === "/tools"
? (pathname === "/tools" ? "active" : "")
: (isHome && active === l.section ? "active" : "")
}
onClick={() => setMenuOpen(false)}>{l.label}</Link>
))}
</nav>

View File

@ -1,36 +1,14 @@
"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
const SECTIONS = [
const PRIMARY = [
{ id: "map", emoji: "🗺️", label: "地图" },
{ id: "destinations", emoji: "🌍", label: "目的地" },
{ id: "spin", emoji: "🎲", label: "转盘" },
{ id: "timezone", emoji: "🕐", label: "时区" },
{ id: "jetlag", emoji: "😴", label: "时差" },
{ id: "trip", emoji: "🗓️", label: "行程" },
{ id: "calculator", emoji: "🧮", label: "计算器" },
{ id: "cost-compare", emoji: "💰", label: "省钱" },
{ id: "savings", emoji: "🎯", label: "存款" },
{ id: "expenses", emoji: "📒", label: "账本" },
{ id: "tax-days", emoji: "🧾", label: "税居" },
{ id: "flight", emoji: "✈️", label: "机票" },
{ id: "wifi", emoji: "📶", label: "网速" },
{ id: "focus", emoji: "⏱️", label: "专注" },
{ id: "lifestyle", emoji: "💻", label: "生活" },
{ id: "calculator", emoji: "🧮", label: "费用" },
{ id: "visa", emoji: "📋", label: "签证" },
{ id: "insurance", emoji: "🏥", label: "保险" },
{ id: "emergency", emoji: "🆘", label: "紧急" },
{ id: "coworking", emoji: "💻", label: "办公" },
{ id: "housing", emoji: "🏡", label: "住宿" },
{ id: "sim", emoji: "📱", label: "上网" },
{ id: "plugs", emoji: "🔌", label: "插座" },
{ id: "cafes", emoji: "☕", label: "咖啡" },
{ id: "season", emoji: "🌤️", label: "季节" },
{ id: "packing", emoji: "🧳", label: "行李" },
{ id: "arrival", emoji: "🛬", label: "到站" },
{ id: "phrases", emoji: "🗣️", label: "短语" },
{ id: "notes", emoji: "📝", label: "笔记" },
{ id: "events", emoji: "📅", label: "活动" },
{ id: "blog", emoji: "📝", label: "博客" },
];
@ -47,7 +25,7 @@ export default function SectionNav() {
}, []);
useEffect(() => {
const sections = SECTIONS.map((s) => document.getElementById(s.id)).filter(Boolean);
const sections = PRIMARY.map((s) => document.getElementById(s.id)).filter(Boolean);
const observer = new IntersectionObserver(
(entries) => entries.forEach((e) => { if (e.isIntersecting) setActive(e.target.id); }),
{ threshold: 0.25, rootMargin: "-80px 0px -55% 0px" }
@ -62,7 +40,7 @@ export default function SectionNav() {
return (
<nav className={`section-nav${visible ? " visible" : ""}`} aria-label="章节导航">
{SECTIONS.map((s) => (
{PRIMARY.map((s) => (
<button
key={s.id}
className={`section-nav-dot${active === s.id ? " active" : ""}`}
@ -74,6 +52,10 @@ export default function SectionNav() {
<span className="section-nav-label">{s.label}</span>
</button>
))}
<Link href="/tools" className="section-nav-dot section-nav-tools" title="全部工具" aria-label="全部工具">
<span className="section-nav-emoji">🛠️</span>
<span className="section-nav-label">工具</span>
</Link>
</nav>
);
}

48
frontend/src/lib/tools.ts Normal file
View File

@ -0,0 +1,48 @@
export interface ToolLink {
id: string;
emoji: string;
title: string;
desc: string;
href: string;
category: "plan" | "money" | "life" | "work" | "safety";
}
export const TOOL_LINKS: ToolLink[] = [
{ id: "trip", emoji: "🗓️", title: "行程规划", desc: "多城串联与预算汇总", href: "/#trip", category: "plan" },
{ id: "spin", emoji: "🎲", title: "命运转盘", desc: "随机抽下一站", href: "/#spin", category: "plan" },
{ id: "matcher", emoji: "🎯", title: "智能匹配", desc: "问卷推荐目的地", href: "/#destinations", category: "plan" },
{ id: "season", emoji: "🌤️", title: "最佳月份", desc: "避开雨季与旺季", href: "/#season", category: "plan" },
{ id: "calculator", emoji: "🧮", title: "费用计算器", desc: "按住宿档次估算", href: "/#calculator", category: "money" },
{ id: "cost-compare", emoji: "💰", title: "省钱对比", desc: "家乡 vs 旅居成本", href: "/#cost-compare", category: "money" },
{ id: "savings", emoji: "🎯", title: "启动金目标", desc: "存款进度追踪", href: "/#savings", category: "money" },
{ id: "expenses", emoji: "📒", title: "支出账本", desc: "分类记账汇总", href: "/#expenses", category: "money" },
{ id: "flight", emoji: "✈️", title: "机票估算", desc: "往返票价粗算", href: "/#flight", category: "money" },
{ id: "currency", emoji: "💱", title: "汇率换算", desc: "多币种互转", href: "/#currency", category: "money" },
{ id: "tax-days", emoji: "🧾", title: "税居天数", desc: "183 天规则追踪", href: "/#tax-days", category: "money" },
{ id: "timezone", emoji: "🕐", title: "时区看板", desc: "工作重叠分析", href: "/#timezone", category: "work" },
{ id: "jetlag", emoji: "😴", title: "时差恢复", desc: "恢复期建议", href: "/#jetlag", category: "work" },
{ id: "wifi", emoji: "📶", title: "网速评级", desc: "远程办公是否够用", href: "/#wifi", category: "work" },
{ id: "focus", emoji: "⏱️", title: "专注时钟", desc: "番茄钟深度工作", href: "/#focus", category: "work" },
{ id: "coworking", emoji: "💻", title: "联合办公", desc: "热门 Co-working", href: "/#coworking", category: "work" },
{ id: "cafes", emoji: "☕", title: "咖啡馆", desc: "办公友好咖啡店", href: "/#cafes", category: "work" },
{ id: "visa", emoji: "📋", title: "签证指南", desc: "政策与智能匹配", href: "/#visa", category: "life" },
{ id: "housing", emoji: "🏡", title: "住宿指南", desc: "Coliving / 公寓方案", href: "/#housing", category: "life" },
{ id: "sim", emoji: "📱", title: "上网方案", desc: "SIM / eSIM 推荐", href: "/#sim", category: "life" },
{ id: "plugs", emoji: "🔌", title: "电源插座", desc: "电压与插头类型", href: "/#plugs", category: "life" },
{ id: "packing", emoji: "🧳", title: "行李清单", desc: "出发前逐项勾选", href: "/#packing", category: "life" },
{ id: "arrival", emoji: "🛬", title: "到站清单", desc: "落地第一周", href: "/#arrival", category: "life" },
{ id: "phrases", emoji: "🗣️", title: "生存短语", desc: "常用语一键复制", href: "/#phrases", category: "life" },
{ id: "notes", emoji: "📝", title: "城市笔记", desc: "本地私密记录", href: "/#notes", category: "life" },
{ id: "insurance", emoji: "🏥", title: "保险指南", desc: "国际医疗保险对比", href: "/#insurance", category: "safety" },
{ id: "emergency", emoji: "🆘", title: "紧急联系", desc: "急救与领事馆", href: "/#emergency", category: "safety" },
{ id: "atm", emoji: "🏧", title: "取现避坑", desc: "ATM 与换汇建议", href: "/#atm", category: "safety" },
];
export const TOOL_CATEGORIES = [
{ key: "all", label: "🌏 全部" },
{ key: "plan", label: "🗺️ 规划" },
{ key: "money", label: "💰 金钱" },
{ key: "work", label: "💻 工作" },
{ key: "life", label: "🌴 生活" },
{ key: "safety", label: "🛡️ 安全" },
] as const;