feat(design): 支付渠道 switch 原型 + segswitch 原子 + 图标/语言同源闸

- .segswitch 段控原子入 atoms.css + 登记 index.html
- purchase/ui-mobile/ui-desktop 三屏:购买页支付渠道 switch(USDT→crypto 默认 / 人民币→alipay),套餐价随渠道分币种显示,删两步弹层 + nezha
- 订单列表/详情集成进移动+桌面主框架
- icons.js 58 图标 sprite 单源 + gen_flutter_icons codegen + check-l1-sync 同源闸(图标同集 + l10n 完整)
- pay-v2 开发决策日志 docs/pay-v2-dev-log.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-11 23:45:55 +08:00
parent a5adbf347b
commit f6ce5267d5
14 changed files with 5800 additions and 161 deletions
+342
View File
@@ -0,0 +1,342 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pangolin · Orders (Interactive Prototype)</title>
<link rel="stylesheet" href="../tokens.css">
<link rel="stylesheet" href="../atoms.css">
<script src="../icons.js"></script>
<style>
body{ margin:0; background:var(--bg); color:var(--fg1); font-family:var(--font-sans);
-webkit-font-smoothing:antialiased; transition:background-color var(--dur-base) var(--ease-out); }
/* 顶栏 chrome(照抄 purchase.html */
.top{ position:sticky; top:0; z-index:50; height:58px; background:var(--surface);
border-bottom:1px solid var(--border); display:flex; align-items:center; gap:var(--space-4);
padding:0 var(--space-6); box-shadow:var(--shadow-sm); }
.brand{ display:flex; align-items:center; gap:var(--space-3); font-family:var(--font-display);
font-weight:700; font-size:var(--text-body); }
.brand .mk{ width:28px; height:28px; border-radius:var(--radius-md);
background:linear-gradient(135deg,var(--clay-500),var(--clay-800));
color:#fff; display:flex; align-items:center; justify-content:center; }
.tag{ font-size:var(--text-caption); color:var(--fg3); padding-left:var(--space-3);
border-left:1px solid var(--border); font-weight:500; }
.chrome-actions{ margin-left:auto; display:inline-flex; align-items:center; gap:var(--space-3); }
.pill-btn{ display:inline-flex; align-items:center; gap:var(--space-2);
background:var(--bg-subtle); border:1px solid var(--border); color:var(--fg2);
border-radius:var(--radius-full); padding:var(--space-2) var(--space-4);
font-weight:600; font-size:var(--text-caption); cursor:pointer; }
.pill-btn svg{ width:15px; height:15px; }
.back{ font-size:var(--text-sm); color:var(--accent); text-decoration:none; font-weight:600; }
.wrap{ max-width:760px; margin:0 auto; padding:var(--space-8) var(--space-6) var(--space-16);
display:flex; flex-direction:column; align-items:center; }
.lead{ font-size:var(--text-sm); color:var(--fg2); line-height:var(--leading-relaxed);
margin:0 0 var(--space-8); text-align:center; }
.lead strong{ color:var(--fg1); }
/* 手机框 */
.phone{ width:320px; height:660px; border-radius:var(--radius-2xl); overflow:hidden;
background:var(--bg); border:1px solid var(--border); box-shadow:var(--shadow-lg);
display:flex; flex-direction:column; position:relative; }
.phone-top{ height:48px; flex:none; display:flex; align-items:center; gap:var(--space-2);
padding:0 var(--space-2); border-bottom:1px solid var(--border); background:var(--surface); z-index:5; }
.phone-top .ttl{ font-family:var(--font-sans); font-size:var(--text-h3); font-weight:700; color:var(--fg1); }
.icon-btn{ width:38px; height:38px; display:flex; align-items:center; justify-content:center;
border:none; background:transparent; color:var(--fg1); cursor:pointer; }
.icon-btn svg{ width:20px; height:20px; }
.icon-btn[hidden]{ display:none; }
.phone-body{ flex:1; position:relative; overflow:hidden; }
.screen{ position:absolute; inset:0; display:none; flex-direction:column; }
.screen.is-active{ display:flex; }
.scr{ flex:1; overflow:auto; padding:var(--space-4); }
.stack > *{ margin-bottom:var(--space-4); }
.stack > *:last-child{ margin-bottom:0; }
/* 订单列表行内胶囊压缩 */
.setting-row .st-trail .pill{ padding:3px var(--space-2); font-size:11px; }
.setting-row .st-trail > svg{ width:18px; height:18px; }
.pill-accent .dot{ background:var(--fg-on-accent); }
/* 订单详情 · 顶部概要 */
.od-summary{ display:flex; flex-direction:column; align-items:flex-start; gap:var(--space-3);
padding:var(--space-5); }
.od-plan{ font-family:var(--font-sans); font-size:var(--text-sm); font-weight:600; color:var(--fg2); }
.od-amount{ font-family:var(--font-display); font-size:var(--text-h1); font-weight:700; color:var(--fg1);
letter-spacing:var(--tracking-tight); font-feature-settings:'tnum' 1; line-height:1; }
.sec-label{ font-size:var(--text-caption); color:var(--fg3); font-weight:700;
letter-spacing:var(--tracking-caps); text-transform:uppercase;
padding:0 var(--space-2) var(--space-2); }
.kv-status{ padding-top:2px; }
.od-actions{ display:flex; flex-direction:column; gap:var(--space-3); }
/* toast(点击复制) */
.toast{ position:absolute; left:50%; bottom:var(--space-6); transform:translate(-50%, 12px);
background:var(--fg1); color:var(--bg); font-size:var(--text-caption); font-weight:600;
padding:var(--space-2) var(--space-4); border-radius:var(--radius-full); box-shadow:var(--shadow-lg);
display:flex; align-items:center; gap:var(--space-2); opacity:0; pointer-events:none;
transition:opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out); z-index:60; }
.toast svg{ width:14px; height:14px; }
.toast.show{ opacity:1; transform:translate(-50%, 0); }
</style>
</head>
<body>
<div class="top">
<div class="brand"><span class="mk"><img src="../assets/app-icon.svg" alt="Pangolin" style="width:100%;height:100%;object-fit:contain;border-radius:inherit"></span>Pangolin 穿山甲</div>
<span class="tag" data-i18n="tag">Orders · Design preview</span>
<div class="chrome-actions">
<a class="back" href="../index.html" data-i18n="back">← Registry</a>
<button class="pill-btn" id="langBtn" aria-label="language"><svg><use href="#i-globe"/></svg><span id="langLabel">中文</span></button>
<button class="pill-btn" id="themeBtn" aria-label="theme">
<svg><use href="#i-sun" id="themeIcon"/></svg><span id="themeLabel">Light</span>
</button>
</div>
</div>
<div class="wrap">
<p class="lead" data-i18n="lead">
Single phone frame walking the order flow: <strong>Orders list</strong> → tap a row → <strong>Order detail</strong> → back. Four lifecycle states (awaiting payment / paid · activating / completed / expired). Copyable order &amp; trade numbers. Toggle language (EN / 中) and theme top-right.
</p>
<div class="phone">
<div class="phone-top">
<button class="icon-btn" id="backBtn" aria-label="back" hidden><svg><use href="#i-arrow-left"/></svg></button>
<span class="ttl" id="phoneTitle" data-i18n="listTitle">Orders</span>
</div>
<div class="phone-body">
<!-- 页面 1:订单列表 -->
<section class="screen is-active" data-screen="list">
<div class="scr">
<div class="card card-flush" id="orderList"></div>
</div>
</section>
<!-- 页面 2:订单详情 -->
<section class="screen" data-screen="detail">
<div class="scr stack">
<div class="card">
<div class="od-summary">
<div class="od-plan" id="odPlan"></div>
<div class="od-amount" id="odAmount"></div>
<div id="odStatus"></div>
</div>
</div>
<div>
<div class="sec-label" data-i18n="secDetail">Details</div>
<div class="card" id="odKv"></div>
</div>
<div class="od-actions" id="odActions"></div>
</div>
</section>
<!-- toast -->
<div class="toast" id="toast"><svg><use href="#i-check"/></svg><span id="toastText"></span></div>
</div>
</div>
</div>
<script>
/* ---------- 主题 ---------- */
function applyTheme(t){
if(t==='dark'){ document.documentElement.dataset.theme='dark'; }
else { delete document.documentElement.dataset.theme; }
document.getElementById('themeIcon').setAttribute('href', t==='dark' ? '#i-moon' : '#i-sun');
document.getElementById('themeLabel').textContent = (lang==='zh') ? (t==='dark'?'深色':'浅色') : (t==='dark'?'Dark':'Light');
localStorage.setItem('pangolin-ds-theme', t);
}
document.getElementById('themeBtn').onclick = () => {
const cur = document.documentElement.dataset.theme==='dark' ? 'dark' : 'light';
applyTheme(cur==='dark' ? 'light' : 'dark');
};
/* ---------- i18n ---------- */
const I18N = {
en:{ tag:'Orders · Design preview', back:'← Registry', listTitle:'Orders', detailTitle:'Order Detail',
secDetail:'Details',
kOrderNo:'Order No.', kPlan:'Plan', kAmount:'Amount', kMethod:'Payment', kStatus:'Status',
kCreated:'Created', kValid:'Valid until', kDeadline:'Pay before', kTrade:'Trade No.',
copied:'Copied', langBtn:'中文',
actContinue:'Continue payment', actCancel:'Cancel order', actRefresh:'Refresh status',
actMembership:'View membership', actBuyAgain:'Buy again',
lead:'Single phone frame walking the order flow: <strong>Orders list</strong> → tap a row → <strong>Order detail</strong> → back. Four lifecycle states (awaiting payment / paid · activating / completed / expired). Copyable order &amp; trade numbers. Toggle language (EN / 中) and theme top-right.' },
zh:{ tag:'订单 · 设计预览', back:'← 返回登记簿', listTitle:'我的订单', detailTitle:'订单详情',
secDetail:'明细',
kOrderNo:'订单号', kPlan:'套餐', kAmount:'金额', kMethod:'支付方式', kStatus:'状态',
kCreated:'创建时间', kValid:'有效期至', kDeadline:'支付截止', kTrade:'交易流水号',
copied:'已复制', langBtn:'EN',
actContinue:'继续支付', actCancel:'取消订单', actRefresh:'刷新状态',
actMembership:'查看会员', actBuyAgain:'重新购买',
lead:'单手机框走完整订单流程:<strong>订单列表</strong> → 点某条 → <strong>订单详情</strong> → 返回。四种生命周期状态(等待付款 / 付款完成·待开通 / 交易完成 / 已过期),订单号 / 流水号可复制。右上角可切语言(EN / 中)与主题。' }
};
let lang = 'en';
/* ---------- 数据模型(pay v2 ---------- */
const PLAN = {
pro_month:{ en:'PRO Monthly', zh:'按月 PRO' },
pro_quarter:{ en:'PRO Quarterly', zh:'按季 PRO' },
pro_year:{ en:'PRO Yearly', zh:'按年 PRO' }
};
const METHOD = {
alipay:{ en:'Alipay', zh:'支付宝' },
crypto:{ en:'Crypto · USDT (TRC20)', zh:'加密货币 · USDT (TRC20)' },
nazha:{ en:'Nazhapay', zh:'哪吒支付' }
};
const STATUS = {
pending:{ pill:'is-warning', en:'Awaiting payment', zh:'等待付款' },
paid:{ pill:'pill-accent', en:'Paid · Activating', zh:'付款完成·待开通' },
settled:{ pill:'is-success', en:'Completed', zh:'交易完成' },
expired:{ pill:'is-neutral', en:'Expired', zh:'已过期' }
};
const ORDERS = [
{ id:'PGL-20260711-8F3A', plan:'pro_year', amount:'¥199.99', method:'alipay', status:'settled',
created:'2026-07-11 14:32', endKind:'valid', ended:'2027-07-11', trade:'2026071122001487652910' },
{ id:'PGL-20260710-2K9D', plan:'pro_month', amount:'¥29.99', method:'crypto', status:'paid',
created:'2026-07-10 09:18', endKind:'valid', ended:'2026-08-10', trade:'a7f3…4c1d2 (TRC20)' },
{ id:'PGL-20260709-7QX2', plan:'pro_quarter', amount:'¥68.88', method:'alipay', status:'pending',
created:'2026-07-09 21:05', endKind:'deadline', ended:'2026-07-09 21:20', trade:'—' },
{ id:'PGL-20260705-3M1P', plan:'pro_month', amount:'4.99 USDT', method:'crypto', status:'expired',
created:'2026-07-05 11:40', endKind:'deadline', ended:'2026-07-05 11:55', trade:'—' },
{ id:'PGL-20260628-9ABF', plan:'pro_year', amount:'¥199.99', method:'nazha', status:'settled',
created:'2026-06-28 16:22', endKind:'valid', ended:'2027-06-28', trade:'NZ2026062899310277' }
];
const ACTIONS = {
pending: [ {key:'actContinue', cls:'btn-primary'}, {key:'actCancel', cls:'btn-ghost'} ],
paid: [ {key:'actRefresh', cls:'btn-subtle'} ],
settled: [ {key:'actMembership', cls:'btn-primary'} ],
expired: [ {key:'actBuyAgain', cls:'btn-primary'} ]
};
/* ---------- 渲染工具 ---------- */
function t(k){ return I18N[lang][k]; }
function esc(s){ return String(s).replace(/[&<>"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c])); }
function planLabel(p){ return PLAN[p][lang]; }
function methodLabel(m){ return METHOD[m][lang]; }
function statusPill(s){
const st = STATUS[s];
return '<span class="pill ' + st.pill + '"><span class="dot"></span>' + esc(st[lang]) + '</span>';
}
/* ---------- 列表 ---------- */
function renderList(){
const html = ORDERS.map(o => {
const date = o.created.split(' ')[0];
return '<div class="setting-row is-tap" data-id="' + o.id + '">'
+ '<div class="st-body">'
+ '<div class="st-title">' + esc(planLabel(o.plan)) + '</div>'
+ '<div class="st-sub">' + esc(o.amount) + ' · ' + esc(date) + '</div>'
+ '</div>'
+ '<div class="st-trail">' + statusPill(o.status)
+ '<svg><use href="#i-chevron-right"/></svg>'
+ '</div>'
+ '</div>';
}).join('');
const list = document.getElementById('orderList');
list.innerHTML = html;
list.querySelectorAll('.setting-row').forEach(row =>
row.onclick = () => openDetail(row.dataset.id));
}
/* ---------- 详情 ---------- */
function kvRow(labelKey, value, copyable){
const copy = copyable
? '<button class="kv-copy js-copy" data-copy="' + esc(value) + '"><svg><use href="#i-copy"/></svg></button>'
: '';
return '<div class="kv-row"><span class="kv-label">' + esc(t(labelKey)) + '</span>'
+ '<span class="kv-val">' + value + '</span>' + copy + '</div>';
}
function openDetail(id){
const o = ORDERS.find(x => x.id === id);
if(!o) return;
document.getElementById('odPlan').textContent = planLabel(o.plan);
document.getElementById('odAmount').textContent = o.amount;
document.getElementById('odStatus').innerHTML = statusPill(o.status);
const endKey = o.endKind === 'valid' ? 'kValid' : 'kDeadline';
const kv = ''
+ kvRow('kOrderNo', esc(o.id), true)
+ kvRow('kPlan', esc(planLabel(o.plan)), false)
+ kvRow('kAmount', esc(o.amount), false)
+ kvRow('kMethod', esc(methodLabel(o.method)), false)
+ '<div class="kv-row"><span class="kv-label">' + esc(t('kStatus')) + '</span>'
+ '<span class="kv-val kv-status">' + statusPill(o.status) + '</span></div>'
+ kvRow('kCreated', esc(o.created), false)
+ kvRow(endKey, esc(o.ended), false)
+ kvRow('kTrade', esc(o.trade), o.trade !== '—');
document.getElementById('odKv').innerHTML = kv;
const acts = ACTIONS[o.status] || [];
document.getElementById('odActions').innerHTML = acts.map(a =>
'<button class="btn ' + a.cls + ' btn-block js-act" data-key="' + a.key + '">' + esc(t(a.key)) + '</button>'
).join('');
document.querySelectorAll('.js-act').forEach(b => b.onclick = () => {
if(b.dataset.key === 'actCancel') { show('list'); return; }
toast(t(b.dataset.key));
});
document.querySelectorAll('.js-copy').forEach(b => b.onclick = () => toast(t('copied') + '' + b.dataset.copy));
show('detail');
}
/* ---------- 屏切换 ---------- */
const screens = document.querySelectorAll('.screen');
const titleEl = document.getElementById('phoneTitle');
const backBtn = document.getElementById('backBtn');
function show(name){
screens.forEach(s => s.classList.toggle('is-active', s.dataset.screen === name));
titleEl.textContent = name === 'detail' ? t('detailTitle') : t('listTitle');
backBtn.hidden = (name === 'list');
document.querySelectorAll('.scr').forEach(el => el.scrollTop = 0);
window.__screen = name;
}
backBtn.onclick = () => show('list');
/* ---------- toast ---------- */
let toastTimer;
function toast(msg){
const el = document.getElementById('toast');
document.getElementById('toastText').textContent = msg;
el.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => el.classList.remove('show'), 1800);
}
/* ---------- 语言 ---------- */
function applyLang(l){
lang = l;
document.documentElement.lang = (l === 'zh') ? 'zh-CN' : 'en';
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.dataset.i18n;
if(k === 'lead') el.innerHTML = t('lead'); else el.textContent = t(k);
});
document.getElementById('langLabel').textContent = t('langBtn');
const curTheme = document.documentElement.dataset.theme === 'dark' ? 'dark' : 'light';
document.getElementById('themeLabel').textContent = (l==='zh') ? (curTheme==='dark'?'深色':'浅色') : (curTheme==='dark'?'Dark':'Light');
renderList();
if(window.__screen === 'detail' && window.__curId) openDetail(window.__curId);
else show(window.__screen || 'list');
localStorage.setItem('pangolin-ds-lang', l);
}
document.getElementById('langBtn').onclick = () => applyLang(lang === 'en' ? 'zh' : 'en');
/* 记住详情当前单以便切语言重渲染 */
const _openDetail = openDetail;
openDetail = function(id){ window.__curId = id; _openDetail(id); };
/* ---------- 初始化 ---------- */
applyTheme(localStorage.getItem('pangolin-ds-theme') || 'light');
applyLang(localStorage.getItem('pangolin-ds-lang') || 'en');
show('list');
</script>
</body>
</html>
+403
View File
@@ -0,0 +1,403 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>穿山甲 · 购买流程(交互原型)</title>
<link rel="stylesheet" href="../tokens.css">
<link rel="stylesheet" href="../atoms.css">
<script src="../icons.js"></script>
<style>
body{ margin:0; background:var(--bg); color:var(--fg1); font-family:var(--font-sans);
-webkit-font-smoothing:antialiased; transition:background-color var(--dur-base) var(--ease-out); }
/* 顶栏(照抄 purchase.html chrome */
.top{ position:sticky; top:0; z-index:50; height:58px; background:var(--surface);
border-bottom:1px solid var(--border); display:flex; align-items:center; gap:var(--space-4);
padding:0 var(--space-6); box-shadow:var(--shadow-sm); }
.brand{ display:flex; align-items:center; gap:var(--space-3); font-family:var(--font-display);
font-weight:700; font-size:var(--text-body); }
.brand .mk{ width:28px; height:28px; border-radius:var(--radius-md);
background:linear-gradient(135deg,var(--clay-500),var(--clay-800));
color:#fff; display:flex; align-items:center; justify-content:center; }
.brand .mk svg{ width:16px; height:16px; }
.tag{ font-size:var(--text-caption); color:var(--fg3); padding-left:var(--space-3);
border-left:1px solid var(--border); font-weight:500; }
.theme-toggle{ margin-left:auto; display:inline-flex; align-items:center; gap:var(--space-2);
background:var(--bg-subtle); border:1px solid var(--border); color:var(--fg2);
border-radius:var(--radius-full); padding:var(--space-2) var(--space-4);
font-weight:600; font-size:var(--text-caption); cursor:pointer; }
.theme-toggle svg{ width:15px; height:15px; }
.back{ font-size:var(--text-sm); color:var(--accent); text-decoration:none; font-weight:600; }
.wrap{ max-width:760px; margin:0 auto; padding:var(--space-8) var(--space-6) var(--space-16);
display:flex; flex-direction:column; align-items:center; }
.lead{ font-size:var(--text-sm); color:var(--fg2); line-height:var(--leading-relaxed);
margin:0 0 var(--space-8); text-align:center; }
.lead code{ font-family:var(--font-mono); font-size:0.85em; color:var(--accent);
background:var(--accent-subtle); padding:1px 6px; border-radius:var(--radius-sm); }
/* 手机框 */
.phone{ width:320px; height:660px; border-radius:var(--radius-2xl); overflow:hidden;
background:var(--bg); border:1px solid var(--border); box-shadow:var(--shadow-lg);
display:flex; flex-direction:column; position:relative; }
.phone-top{ height:48px; flex:none; display:flex; align-items:center; gap:var(--space-2);
padding:0 var(--space-2); border-bottom:1px solid var(--border); background:var(--surface); z-index:5; }
.phone-top .ttl{ font-family:var(--font-sans); font-size:var(--text-h3); font-weight:700; color:var(--fg1); }
.icon-btn{ width:38px; height:38px; display:flex; align-items:center; justify-content:center;
border:none; background:transparent; color:var(--fg1); cursor:pointer; }
.icon-btn svg{ width:20px; height:20px; }
.icon-btn[hidden]{ display:none; }
.phone-body{ flex:1; position:relative; overflow:hidden; }
.screen{ position:absolute; inset:0; display:none; flex-direction:column; }
.screen.is-active{ display:flex; }
.scr{ flex:1; overflow:auto; padding:var(--space-5); }
.scr.center{ display:flex; flex-direction:column; justify-content:center; }
.stack > *{ margin-bottom:var(--space-4); }
.stack > *:last-child{ margin-bottom:0; }
/* ---------- 购买页:统一特性块(只出现一次) ---------- */
.feats-note{ font-size:var(--text-caption); font-weight:700; color:var(--fg3);
letter-spacing:var(--tracking-caps); text-transform:uppercase;
margin:0 0 var(--space-3); padding-left:var(--space-1); }
.feat-block{ padding:var(--space-4) var(--space-5); }
.feat-list{ display:flex; flex-direction:column; gap:var(--space-3); }
.feat-item{ display:flex; align-items:center; gap:var(--space-3);
font-size:var(--text-sm); font-weight:500; color:var(--fg1); }
.feat-item svg{ width:16px; height:16px; flex-shrink:0; color:var(--success); }
/* ---------- 购买页:精简套餐卡(去逐卡特性,只留周期+价格+按钮) ---------- */
.plan-card{ padding:var(--space-4) var(--space-5); } /* override atoms:更矮更紧凑 */
.plan-head{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); }
.plan-meta{ display:flex; flex-direction:column; gap:var(--space-1); }
.plan-meta .plan-per{ margin:0; }
.plan-card .btn-primary{ margin-top:var(--space-4); }
/* ---------- 支付页:订单信息卡 ---------- */
.order-card{ padding:var(--space-4) var(--space-5); }
.order-hd{ font-size:var(--text-caption); font-weight:700; color:var(--fg3);
letter-spacing:var(--tracking-caps); text-transform:uppercase; margin-bottom:var(--space-3); }
.order-row{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--space-4);
padding:var(--space-2) 0; border-top:1px solid var(--border); }
.order-row:first-of-type{ border-top:none; padding-top:0; }
.order-k{ font-size:var(--text-sm); color:var(--fg3); }
.order-v{ font-family:var(--font-sans); font-size:var(--text-sm); font-weight:600; color:var(--fg1); }
.order-v.order-amt{ font-family:var(--font-mono); font-weight:700; color:var(--accent);
font-feature-settings:'tnum' 1; }
/* 按支付渠道切换 pay 屏内容(渠道即币种即方式:usdt→crypto / cny→alipay */
.screen[data-method="crypto"] .only-alipay{ display:none; }
.screen[data-method="alipay"] .only-crypto{ display:none; }
/* 轮询中 */
.polling{ display:flex; align-items:center; gap:var(--space-3); margin:var(--space-4) 0 var(--space-2);
font-size:var(--text-sm); color:var(--fg2); }
.polling .spin{ width:15px; height:15px; color:var(--accent); animation:spin 1s linear infinite; }
@keyframes spin{ to{ transform:rotate(360deg); } }
.hint-warn{ font-size:var(--text-caption); color:var(--warning); margin-top:var(--space-2); line-height:var(--leading-normal); }
.hint-mut{ font-size:var(--text-caption); color:var(--fg3); margin-top:var(--space-2); line-height:var(--leading-normal); text-align:center; }
.full{ width:100%; }
/* 评审演示区(真实由 pay 服务回调驱动,此处仅供点击走通流程) */
.demo-zone{ margin-top:var(--space-5); padding-top:var(--space-4);
border-top:1px dashed var(--border-strong); }
.demo-cap{ font-size:var(--text-caption); color:var(--fg3); text-align:center;
margin-bottom:var(--space-3); letter-spacing:var(--tracking-caps); text-transform:uppercase; font-weight:700; }
.demo-fail{ background:none; border:none; color:var(--fg3); font-size:var(--text-caption);
cursor:pointer; width:100%; padding:var(--space-2); font-weight:600; }
/* toast(点击复制 / 拉起反馈) */
.toast{ position:absolute; left:50%; bottom:var(--space-6); transform:translate(-50%, 12px);
background:var(--fg1); color:var(--bg); font-size:var(--text-caption); font-weight:600;
padding:var(--space-2) var(--space-4); border-radius:var(--radius-full); box-shadow:var(--shadow-lg);
display:flex; align-items:center; gap:var(--space-2); opacity:0; pointer-events:none;
transition:opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out); z-index:60; }
.toast svg{ width:14px; height:14px; }
.toast.show{ opacity:1; transform:translate(-50%, 0); }
</style>
</head>
<body>
<div class="top">
<div class="brand"><span class="mk"><img src="../assets/app-icon.svg" alt="穿山甲" style="width:100%;height:100%;object-fit:contain;border-radius:inherit"></span>穿山甲 Pangolin</div>
<span class="tag">购买流程 · 交互原型</span>
<a class="back" href="../index.html">← 返回登记簿</a>
<button class="theme-toggle" id="themeBtn" aria-label="切换主题">
<svg><use href="#i-sun" id="themeIcon"/></svg><span id="themeLabel">浅色</span>
</button>
</div>
<div class="wrap">
<p class="lead">
单手机框走完整购买流程:<strong>购买</strong>(特性统一置顶 + <strong>支付渠道 switch</strong> + 精简套餐卡)→ <strong>创建订单</strong>(顶部订单信息按所选渠道币种显价)→ <strong>成功 / 失败</strong>
支付渠道即币种即方式:<strong>USDT → 加密货币</strong>(默认)/ <strong>人民币 → 支付宝</strong>;套餐价随渠道切换(各币种一口价,不做汇率换算)。结算币种一经下单锁定,跨币种需重新下单(pay 侧 <code>currency_mismatch</code>)。资金外流全在 App 外,<strong>App 内无卡号录入</strong>(铁律 #10)。「▸ 模拟到账 / 失败」为评审用。切右上角看深色。
</p>
<div class="phone">
<div class="phone-top">
<button class="icon-btn" id="backBtn" aria-label="返回"><svg><use href="#i-arrow-left"/></svg></button>
<span class="ttl" id="phoneTitle">升级 PRO</span>
</div>
<div class="phone-body">
<!-- 购买页 -->
<section class="screen is-active" data-screen="purchase">
<div class="scr stack">
<!-- 统一特性块:只出现一次,所有套餐均包含 -->
<div>
<p class="feats-note">所有套餐均包含</p>
<div class="card feat-block">
<div class="feat-list">
<div class="feat-item"><svg><use href="#i-check"/></svg>不限时长 · 不限流量</div>
<div class="feat-item"><svg><use href="#i-check"/></svg>80+ 全球加速线路</div>
<div class="feat-item"><svg><use href="#i-check"/></svg>5 台设备同时在线</div>
<div class="feat-item"><svg><use href="#i-check"/></svg>优先客服</div>
</div>
</div>
</div>
<!-- 支付渠道 switch(默认 USDT·加密货币;人民币走支付宝)→ 套餐价按此币种显示 -->
<div>
<p class="feats-note">支付渠道</p>
<div class="segswitch" id="chanSwitch">
<button class="segswitch-opt is-active js-chan" data-chan="usdt"><svg><use href="#i-globe"/></svg>USDT · 加密货币</button>
<button class="segswitch-opt js-chan" data-chan="cny"><svg><use href="#i-credit-card"/></svg>人民币 · 支付宝</button>
</div>
</div>
<!-- 精简套餐卡:仅 周期 + 大价格 + 每月折算 + 立即购买;价格随渠道币种切换 -->
<div class="plan-card" data-plan="按月">
<div class="plan-head">
<div class="plan-meta">
<div class="plan-name">按月</div>
<div class="plan-per"></div>
</div>
<div class="plan-price"></div>
</div>
<button class="btn btn-primary btn-block js-buy" data-name="按月" data-until="2026-08-11">立即购买</button>
</div>
<div class="plan-card" data-plan="按季">
<div class="plan-head">
<div class="plan-meta">
<div class="plan-name">按季</div>
<div class="plan-per"></div>
</div>
<div class="plan-price"></div>
</div>
<button class="btn btn-primary btn-block js-buy" data-name="按季" data-until="2026-10-11">立即购买</button>
</div>
<div class="plan-card is-featured" data-plan="按年" style="margin-top:var(--space-5)">
<span class="plan-pop">最受欢迎</span>
<div class="plan-head">
<div class="plan-meta">
<div class="plan-name">按年</div>
<div class="plan-per"></div>
</div>
<div class="plan-price"></div>
</div>
<button class="btn btn-primary btn-block js-buy" data-name="按年" data-until="2027-07-11">立即购买</button>
</div>
</div>
</section>
<!-- 支付页(USDT / 支付宝 双态,data-method 切换) -->
<section class="screen" data-screen="pay" data-method="crypto">
<div class="scr stack">
<!-- 订单信息(两态共用,值随所选套餐+渠道带过来) -->
<div class="card order-card">
<div class="order-hd">订单信息</div>
<div class="order-row"><span class="order-k">套餐</span><span class="order-v" id="orderName">按年 · PRO</span></div>
<div class="order-row"><span class="order-k">支付渠道</span><span class="order-v" id="orderChan">加密货币 · USDT</span></div>
<div class="order-row"><span class="order-k">应付金额</span><span class="order-v order-amt" id="orderPrice">$34.99</span></div>
<div class="order-row"><span class="order-k">到期日</span><span class="order-v" id="orderUntil">2027-07-11</span></div>
</div>
<!-- USDT · crypto_address -->
<div class="card only-crypto">
<div class="kv-row"><span class="kv-label">网络</span><span class="kv-val">USDT · TRC20</span><button class="kv-copy js-copy" data-copy="USDT TRC20"><svg><use href="#i-copy"/></svg></button></div>
<div class="kv-row"><span class="kv-label">收款地址</span><span class="kv-val">TB1e7q1333iuLMLc2iQaoaqeuDF2U7cxan</span><button class="kv-copy js-copy" data-copy="TB1e7q1333iuLMLc2iQaoaqeuDF2U7cxan"><svg><use href="#i-copy"/></svg></button></div>
<div class="kv-row"><span class="kv-label">精确金额</span><span class="kv-val" id="cryptoExact">34.990001</span><button class="kv-copy js-copy" id="cryptoExactCopy" data-copy="34.990001"><svg><use href="#i-copy"/></svg></button></div>
<div class="hint-warn">请转账 <strong id="cryptoExactHint">34.990001 USDT</strong>(含小数末位),金额用于自动对账识别,多退少不到账。</div>
</div>
<!-- 支付宝 · redirect -->
<div class="card only-alipay">
<button class="btn btn-primary btn-block btn-lg js-alipay"><svg><use href="#i-external-link"/></svg>打开支付宝</button>
<div class="hint-mut">将跳转至支付宝完成付款,付款后自动返回本页并开通。</div>
</div>
<div class="polling"><svg class="spin"><use href="#i-loader-circle"/></svg><span id="pollText">等待到账确认…</span></div>
<button class="btn btn-subtle btn-block js-switch">更换支付渠道</button>
<button class="btn btn-ghost btn-block js-cancel">取消订单</button>
<div class="demo-zone">
<div class="demo-cap">评审演示</div>
<button class="btn btn-subtle btn-block js-paid">▸ 模拟到账</button>
<button class="demo-fail js-failed">▸ 模拟失败</button>
</div>
</div>
</section>
<!-- 成功态 -->
<section class="screen" data-screen="success">
<div class="scr center">
<div class="result">
<div class="result-icon"><svg><use href="#i-check-circle"/></svg></div>
<div class="result-title">支付成功</div>
<div class="result-sub" id="successSub">PRO 有效期至 2027-07-11</div>
</div>
<button class="btn btn-primary btn-block btn-lg full js-done">完成</button>
</div>
</section>
<!-- 失败态 -->
<section class="screen" data-screen="fail">
<div class="scr center">
<div class="result is-fail">
<div class="result-icon"><svg><use href="#i-alert-triangle"/></svg></div>
<div class="result-title">支付失败</div>
<div class="result-sub">订单已超时,请重试</div>
</div>
<button class="btn btn-primary btn-block btn-lg full js-retry">重新支付</button>
</div>
</section>
<!-- toast -->
<div class="toast" id="toast"><svg><use href="#i-check"/></svg><span id="toastText"></span></div>
</div>
</div>
</div>
<script>
/* ---------- 主题 ---------- */
function applyTheme(t){
if(t==='dark'){ document.documentElement.dataset.theme='dark'; }
else { delete document.documentElement.dataset.theme; }
document.getElementById('themeIcon').setAttribute('href', t==='dark' ? '#i-moon' : '#i-sun');
document.getElementById('themeLabel').textContent = t==='dark' ? '深色' : '浅色';
localStorage.setItem('pangolin-ds-theme', t);
}
document.getElementById('themeBtn').onclick = () => {
const cur = document.documentElement.dataset.theme==='dark' ? 'dark' : 'light';
applyTheme(cur==='dark' ? 'light' : 'dark');
};
applyTheme(localStorage.getItem('pangolin-ds-theme') || 'light');
/* ---------- 定价:各币种一口价(pay 侧每 SKU 按结算币种各定价,不做实时汇率换算) ---------- */
const PRICES = {
'按月': { cny:'¥29.99', cnyPer:'¥29.99 / 月', usdt:'$4.99', usdtPer:'$4.99 / 月', usdtExact:'4.990001' },
'按季': { cny:'¥68.88', cnyPer:'¥22.9 / 月', usdt:'$12.99', usdtPer:'$4.33 / 月', usdtExact:'12.990001' },
'按年': { cny:'¥199.99', cnyPer:'¥16.7 / 月', usdt:'$34.99', usdtPer:'$2.92 / 月', usdtExact:'34.990001' },
};
/* 渠道 → 币种 / 支付方式 / 订单渠道文案(USDT→crypto 默认;人民币→alipay */
const CHAN = {
usdt: { currency:'usdt', method:'crypto', orderLabel:'加密货币 · USDT' },
cny: { currency:'cny', method:'alipay', orderLabel:'支付宝 · 人民币' },
};
const order = { name:'按年', until:'2027-07-11', chan:'usdt', currency:'usdt', method:'crypto', price:'$34.99', usdtExact:'34.990001' };
const TITLES = { purchase:'升级 PRO', pay:'完成支付', success:'完成支付', fail:'完成支付' };
/* 按当前渠道币种渲染三张套餐卡的价格 */
function renderPrices(){
const cur = CHAN[order.chan].currency;
document.querySelectorAll('.plan-card').forEach(card => {
const p = PRICES[card.dataset.plan];
card.querySelector('.plan-price').textContent = cur==='usdt' ? p.usdt : p.cny;
card.querySelector('.plan-per').textContent = cur==='usdt' ? p.usdtPer : p.cnyPer;
});
}
const screens = document.querySelectorAll('.screen');
const payScreen = document.querySelector('.screen[data-screen="pay"]');
const titleEl = document.getElementById('phoneTitle');
const backBtn = document.getElementById('backBtn');
function show(name){
screens.forEach(s => s.classList.toggle('is-active', s.dataset.screen === name));
titleEl.textContent = TITLES[name] || '升级 PRO';
backBtn.hidden = (name === 'purchase');
document.querySelectorAll('.scr').forEach(el => el.scrollTop = 0);
window.__screen = name;
}
function enterPay(){
const c = CHAN[order.chan];
const p = PRICES[order.name];
order.currency = c.currency; order.method = c.method;
order.price = c.currency==='usdt' ? p.usdt : p.cny;
order.usdtExact = p.usdtExact;
payScreen.dataset.method = order.method;
/* 订单信息卡:金额按所选结算币种显价(CNY ¥ / USDT $ */
document.getElementById('orderName').textContent = order.name + ' · PRO';
document.getElementById('orderChan').textContent = c.orderLabel;
document.getElementById('orderPrice').textContent = order.price;
document.getElementById('orderUntil').textContent = order.until;
/* USDT 态:精确金额(含小数末位对账)随套餐带过来 */
if(order.method === 'crypto'){
document.getElementById('cryptoExact').textContent = p.usdtExact;
document.getElementById('cryptoExactCopy').dataset.copy = p.usdtExact;
document.getElementById('cryptoExactHint').textContent = p.usdtExact + ' USDT';
}
document.getElementById('pollText').textContent =
order.method === 'crypto' ? '等待到账确认…' : '等待付款确认…';
show('pay');
}
/* toast */
let toastTimer;
function toast(msg){
const t = document.getElementById('toast');
document.getElementById('toastText').textContent = msg;
t.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => t.classList.remove('show'), 1800);
}
/* 支付渠道 switch:切币种 → 套餐价随之刷新 */
document.querySelectorAll('.js-chan').forEach(b => b.onclick = () => {
order.chan = b.dataset.chan;
document.querySelectorAll('.js-chan').forEach(x => x.classList.toggle('is-active', x === b));
renderPrices();
});
/* 购买 → 创建订单(渠道已选)→ 支付页 */
document.querySelectorAll('.js-buy').forEach(b => b.onclick = () => {
order.name = b.dataset.name; order.until = b.dataset.until;
enterPay();
});
/* 支付页交互 */
document.querySelectorAll('.js-copy').forEach(b => b.onclick = () => toast('已复制:' + b.dataset.copy));
document.querySelector('.js-alipay').onclick = () => toast('已拉起支付宝(演示)');
document.querySelector('.js-switch').onclick = () => show('purchase'); /* 更换渠道 → 回购买页改 switch */
document.querySelector('.js-cancel').onclick = () => show('purchase');
document.querySelector('.js-paid').onclick = () => {
document.getElementById('successSub').textContent = 'PRO' + order.name + ')有效期至 ' + order.until;
show('success');
};
document.querySelector('.js-failed').onclick = () => show('fail');
/* 成功 / 失败 */
document.querySelector('.js-done').onclick = () => show('purchase');
document.querySelector('.js-retry').onclick = () => enterPay();
/* 顶栏返回:回上一态 */
backBtn.onclick = () => {
const s = window.__screen;
if(s === 'pay'){ show('purchase'); }
else if(s === 'success' || s === 'fail'){ enterPay(); }
};
renderPrices();
show('purchase');
</script>
</body>
</html>
+802
View File
@@ -0,0 +1,802 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>穿山甲 · 官网 Site 完整原型(可交互)</title>
<link rel="stylesheet" href="../tokens.css">
<link rel="stylesheet" href="../atoms.css">
<script src="../icons.js"></script>
<style>
html{ scroll-behavior:smooth; }
body{ margin:0; background:var(--bg); color:var(--fg1); font-family:var(--font-sans);
-webkit-font-smoothing:antialiased; transition:background-color var(--dur-base) var(--ease-out); }
/* ===== 登记簿 chrome(顶栏 + lead),照抄 purchase.html ===== */
.top{ position:sticky; top:0; z-index:80; height:58px; background:var(--surface);
border-bottom:1px solid var(--border); display:flex; align-items:center; gap:var(--space-4);
padding:0 var(--space-6); box-shadow:var(--shadow-sm); }
.brand{ display:flex; align-items:center; gap:var(--space-3); font-family:var(--font-display);
font-weight:700; font-size:var(--text-body); }
.brand .mk{ width:28px; height:28px; border-radius:var(--radius-md);
background:linear-gradient(135deg,var(--clay-500),var(--clay-800));
color:#fff; display:flex; align-items:center; justify-content:center; }
.brand .mk svg{ width:16px; height:16px; }
.tag{ font-size:var(--text-caption); color:var(--fg3); padding-left:var(--space-3);
border-left:1px solid var(--border); font-weight:500; }
.theme-toggle{ margin-left:auto; display:inline-flex; align-items:center; gap:var(--space-2);
background:var(--bg-subtle); border:1px solid var(--border); color:var(--fg2);
border-radius:var(--radius-full); padding:var(--space-2) var(--space-4);
font-weight:600; font-size:var(--text-caption); cursor:pointer; }
.theme-toggle svg{ width:15px; height:15px; }
.back{ font-size:var(--text-sm); color:var(--accent); text-decoration:none; font-weight:600; }
.wrap{ max-width:1160px; margin:0 auto; padding:var(--space-8) var(--space-6) var(--space-16); }
.lead{ font-size:var(--text-sm); color:var(--fg2); line-height:var(--leading-relaxed);
max-width:820px; margin:0 0 var(--space-6); }
.lead code{ font-family:var(--font-mono); font-size:0.85em; color:var(--accent);
background:var(--accent-subtle); padding:1px 6px; border-radius:var(--radius-sm); }
.vp-note{ display:inline-flex; align-items:center; gap:var(--space-2); font-size:var(--text-caption);
font-weight:600; color:var(--fg3); background:var(--bg-subtle); border:1px solid var(--border);
border-radius:var(--radius-full); padding:var(--space-2) var(--space-3); margin-bottom:var(--space-6); }
.vp-note svg{ width:14px; height:14px; }
/* ===== 浏览器外框(把整站装进一个 mock 浏览器窗口)===== */
.browser{ border:1px solid var(--border); border-radius:var(--radius-xl); overflow:hidden;
box-shadow:var(--shadow-lg); background:var(--bg); }
.bchrome{ display:flex; align-items:center; gap:var(--space-3); height:42px; flex:none;
padding:0 var(--space-4); background:var(--surface); border-bottom:1px solid var(--border); }
.bdots{ display:flex; gap:var(--space-2); }
.bdots i{ width:11px; height:11px; border-radius:var(--radius-full); background:var(--border-strong); }
.baddr{ flex:1; display:inline-flex; align-items:center; gap:var(--space-2); max-width:360px;
font-family:var(--font-mono); font-size:var(--text-caption); color:var(--fg3);
background:var(--bg-subtle); border:1px solid var(--border); border-radius:var(--radius-full);
padding:var(--space-1) var(--space-3); }
.baddr svg{ width:12px; height:12px; color:var(--success); }
/* ===== 站点容器 ===== */
.site{ background:var(--bg); }
.swrap{ max-width:1080px; margin:0 auto; padding:0 var(--space-6); }
.eyebrow{ font-family:var(--font-sans); font-size:var(--text-caption); font-weight:600;
text-transform:uppercase; letter-spacing:var(--tracking-caps); color:var(--accent); }
/* 锚点滚动补偿(登记簿 top 58 + 站点 shead 60*/
.site [id]{ scroll-margin-top:126px; }
/* 站点头 */
.shead{ position:sticky; top:58px; z-index:20; display:flex; align-items:center; gap:var(--space-5);
height:60px; padding:0 var(--space-6); background:var(--surface);
border-bottom:1px solid var(--border); }
.slogo{ display:flex; align-items:center; gap:var(--space-3); text-decoration:none; }
.slogo img{ width:34px; height:34px; display:block; }
.slogo .wm{ display:flex; flex-direction:column; line-height:1; }
.slogo .wm b{ font-family:var(--font-display); font-weight:700; font-size:var(--text-body); color:var(--fg1); }
.slogo .wm small{ font-family:var(--font-display); font-size:9px; font-weight:600;
letter-spacing:var(--tracking-caps); color:var(--accent); margin-top:2px; }
.snav{ display:flex; align-items:center; gap:var(--space-5); margin-left:var(--space-4); }
.snav a{ position:relative; font-size:var(--text-sm); font-weight:600; color:var(--fg2); text-decoration:none;
padding:var(--space-1) 0; cursor:pointer; transition:color var(--dur-fast) var(--ease-out); }
.snav a:hover{ color:var(--accent); }
.snav a.is-active{ color:var(--accent); }
.snav a.is-active::after{ content:""; position:absolute; left:0; right:0; bottom:-2px; height:2px;
border-radius:var(--radius-full); background:var(--accent); }
.shead-right{ margin-left:auto; display:flex; align-items:center; gap:var(--space-3); }
.snavtoggle{ display:none; }
/* Hero */
.hero{ padding:var(--space-16) 0 var(--space-12); }
.hero-grid{ display:grid; grid-template-columns:1.05fr 0.95fr; gap:var(--space-12); align-items:center; }
.hero h1{ margin:var(--space-3) 0 var(--space-4); white-space:pre-line; }
.hero .lede{ font-size:var(--text-body-lg); color:var(--fg2); line-height:var(--leading-relaxed);
max-width:440px; margin:0 0 var(--space-6); }
.hero-cta{ display:flex; gap:var(--space-3); flex-wrap:wrap; }
.trust{ display:flex; gap:var(--space-5); flex-wrap:wrap; margin-top:var(--space-8); }
.trust span.t{ display:inline-flex; align-items:center; gap:var(--space-2); font-size:var(--text-sm);
font-weight:600; color:var(--fg2); }
.trust span.t svg{ width:16px; height:16px; color:var(--success); }
/* Hero 右侧:连接卡 */
.orb-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-2xl);
box-shadow:var(--shadow-lg); padding:var(--space-8) var(--space-6) var(--space-6);
display:flex; flex-direction:column; align-items:center; gap:var(--space-6); }
.orb-meta{ display:flex; gap:var(--space-8); width:100%; justify-content:center; }
.orb-m{ text-align:center; }
.orb-m .k{ display:inline-flex; align-items:center; gap:var(--space-1); font-size:var(--text-caption);
font-weight:600; color:var(--fg3); }
.orb-m .k svg{ width:12px; height:12px; }
.orb-m .v{ font-family:var(--font-mono); font-size:var(--text-h3); font-weight:500; color:var(--fg1);
margin-top:var(--space-1); font-feature-settings:'tnum' 1; }
.orb-m .v small{ font-size:var(--text-caption); color:var(--fg3); }
.orb-node{ display:flex; align-items:center; gap:var(--space-3); width:100%;
background:var(--bg-subtle); border:1px solid var(--border); border-radius:var(--radius-lg);
padding:var(--space-3) var(--space-4); }
.orb-node .cc{ width:34px; height:34px; flex:none; display:flex; align-items:center; justify-content:center;
border-radius:var(--radius-md); background:var(--accent); color:var(--fg-on-accent);
font-family:var(--font-mono); font-size:var(--text-caption); font-weight:600; }
.orb-node .g{ flex:1; min-width:0; }
.orb-node .nn{ font-size:var(--text-sm); font-weight:600; color:var(--fg1); }
.orb-node .ns{ font-family:var(--font-mono); font-size:var(--text-caption); color:var(--fg3); margin-top:2px; }
.orb-node .chev{ width:18px; height:18px; color:var(--fg3); }
/* 平台条 */
.strip{ display:flex; align-items:center; justify-content:center; flex-wrap:wrap; gap:var(--space-5);
padding:var(--space-6) 0 0; border-top:1px solid var(--border); margin-top:var(--space-10); }
.strip .lbl{ font-size:var(--text-caption); font-weight:600; text-transform:uppercase;
letter-spacing:var(--tracking-caps); color:var(--fg3); }
.strip span.p{ display:inline-flex; align-items:center; gap:var(--space-2); font-size:var(--text-sm);
font-weight:600; color:var(--fg2); }
.strip span.p svg{ width:16px; height:16px; color:var(--fg3); }
/* 通用 section 头 */
.band{ padding:var(--space-16) 0; }
.band.subtle{ background:var(--bg-subtle); }
.band.tan{ background:var(--accent-subtle); }
.sec-head{ text-align:center; max-width:600px; margin:0 auto var(--space-10); }
.sec-head h2{ margin:var(--space-3) 0 var(--space-3); }
.sec-head p{ font-size:var(--text-body); color:var(--fg2); line-height:var(--leading-relaxed); margin:0; }
/* 定价「月付 / 年付」段控(chrome,仅用 token*/
.priceseg{ display:inline-flex; align-items:center; gap:var(--space-3); margin-top:var(--space-6); }
.seg{ display:inline-flex; background:var(--bg-subtle); border:1px solid var(--border);
border-radius:var(--radius-full); padding:3px; }
.seg button{ border:none; background:transparent; cursor:pointer; font-family:var(--font-sans);
font-weight:600; font-size:var(--text-sm); color:var(--fg2);
padding:var(--space-2) var(--space-5); border-radius:var(--radius-full);
transition:background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.seg button.is-active{ background:var(--surface); color:var(--accent); box-shadow:var(--shadow-sm); }
/* Features 网格 */
.feat-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--space-5); }
.feat-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl);
box-shadow:var(--shadow-sm); padding:var(--space-6); }
.feat-ic{ width:44px; height:44px; border-radius:var(--radius-lg); display:flex; align-items:center;
justify-content:center; background:var(--accent-subtle); color:var(--accent);
margin-bottom:var(--space-4); }
.feat-ic svg{ width:22px; height:22px; }
.feat-card h3{ font-family:var(--font-sans); font-size:var(--text-body-lg); font-weight:700;
color:var(--fg1); margin:0 0 var(--space-2); }
.feat-card p{ font-size:var(--text-sm); color:var(--fg2); line-height:var(--leading-normal); margin:0; }
/* Pricing */
.price-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--space-6);
align-items:start; max-width:960px; margin:0 auto; }
.plan-card .plan-desc{ font-size:var(--text-caption); color:var(--fg3); margin-top:var(--space-1); }
.plan-card.is-featured .plan-desc{ color:#fff; opacity:0.8; }
.plan-card.is-featured .plan-pop{ background:#fff; }
.plan-save{ margin-left:var(--space-2); }
.pay-note{ display:flex; gap:var(--space-4); align-items:flex-start; max-width:960px;
margin:var(--space-10) auto 0; background:var(--surface); border:1px solid var(--border);
border-radius:var(--radius-xl); box-shadow:var(--shadow-sm); padding:var(--space-6); }
.pay-note > svg{ width:26px; height:26px; flex:none; color:var(--accent); margin-top:2px; }
.pay-note b{ font-family:var(--font-sans); font-size:var(--text-body); color:var(--fg1); }
.pay-note p{ font-size:var(--text-sm); color:var(--fg2); line-height:var(--leading-relaxed);
margin:var(--space-2) 0 var(--space-4); }
.chips{ display:flex; flex-wrap:wrap; gap:var(--space-2); }
.chips .pill svg{ width:13px; height:13px; }
/* Download */
.dl-grid{ display:grid; grid-template-columns:repeat(5,1fr); gap:var(--space-4); max-width:960px; margin:0 auto; }
.dl-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl);
box-shadow:var(--shadow-sm); padding:var(--space-6) var(--space-4); text-align:center;
display:flex; flex-direction:column; align-items:center; gap:var(--space-2);
transition:transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out); }
.dl-card:hover{ transform:translateY(-3px); box-shadow:var(--shadow-lg); }
.dl-ic{ width:48px; height:48px; border-radius:var(--radius-lg); display:flex; align-items:center;
justify-content:center; background:var(--bg-subtle); color:var(--accent); margin-bottom:var(--space-1); }
.dl-ic svg{ width:24px; height:24px; }
.dl-card .pn{ font-size:var(--text-body); font-weight:700; color:var(--fg1); }
.dl-card .pv{ font-family:var(--font-mono); font-size:var(--text-caption); color:var(--fg3); margin-bottom:var(--space-3); }
/* Footer */
.foot{ background:var(--bg-subtle); border-top:1px solid var(--border); padding:var(--space-12) 0 var(--space-8); }
.foot-grid{ display:grid; grid-template-columns:1.4fr 1fr 1fr 1fr; gap:var(--space-8); }
.foot-brand .slogo{ margin-bottom:var(--space-3); }
.foot-brand p{ font-size:var(--text-sm); color:var(--fg2); line-height:var(--leading-relaxed);
max-width:280px; margin:0; }
.foot-col h4{ font-size:var(--text-caption); font-weight:700; text-transform:uppercase;
letter-spacing:var(--tracking-caps); color:var(--fg3); margin:0 0 var(--space-4); }
.foot-col a{ display:block; font-size:var(--text-sm); color:var(--fg2); text-decoration:none;
margin-bottom:var(--space-3); cursor:pointer; transition:color var(--dur-fast) var(--ease-out); }
.foot-col a:hover{ color:var(--accent); }
.foot-bar{ display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-4);
border-top:1px solid var(--border); margin-top:var(--space-10);
padding-top:var(--space-6); font-size:var(--text-caption); color:var(--fg3); }
.foot-bar .icp{ font-family:var(--font-mono); }
.foot-bar .sep{ margin-left:auto; }
/* ===== 响应式 ===== */
@media (max-width:900px){
.feat-grid{ grid-template-columns:repeat(2,1fr); }
.dl-grid{ grid-template-columns:repeat(3,1fr); }
.price-grid{ grid-template-columns:1fr; max-width:420px; }
.foot-grid{ grid-template-columns:1fr 1fr; }
}
@media (max-width:720px){
.hero-grid{ grid-template-columns:1fr; gap:var(--space-8); }
.hero{ padding:var(--space-10) 0; }
.snav{ display:none; }
.snavtoggle{ display:inline-flex; }
.feat-grid{ grid-template-columns:1fr; }
.dl-grid{ grid-template-columns:repeat(2,1fr); }
.foot-grid{ grid-template-columns:1fr; gap:var(--space-6); }
.foot-bar .sep{ margin-left:0; }
.shead-right .btn-lbl{ display:none; }
}
</style>
</head>
<body>
<!-- 登记簿 chrome -->
<div class="top">
<div class="brand"><span class="mk"><img src="../assets/app-icon.svg" alt="穿山甲" style="width:100%;height:100%;object-fit:contain;border-radius:inherit"></span>穿山甲 Pangolin</div>
<span class="tag">官网 Site · 可交互原型</span>
<a class="back" href="../index.html">← 返回登记簿</a>
<button class="theme-toggle" id="themeBtn" aria-label="切换主题">
<svg><use href="#i-sun" id="themeIcon"/></svg><span id="themeLabel">浅色</span>
</button>
</div>
<div class="wrap">
<p class="lead">
官网宣传页完整版式(顶栏 / Hero / 特性 / 定价 / 下载 / 页脚),装进 mock 浏览器窗口,<strong>已加交互</strong>
导航平滑滚动 + scrollspy 高亮、语言下拉(六语单显切换:简体中文 / EN / 日 / 韩 / 俄 / 西)、明暗主题、定价「月付 / 年付」段控切价。
仅引 <code>../tokens.css</code> / <code>../atoms.css</code> / <code>../icons.js</code>,业务色全走 <code>var(--token)</code>
套餐数字对齐 §7;文案脱敏(铁律 #13)——只讲「网络加速 / 极速畅连 / 隐私保护 / 无日志」。切右上角看深色。
</p>
<div class="vp-note"><svg><use href="#i-monitor-smartphone"/></svg>此页自适应:窗口 &lt; 720px 时导航折叠、各区块单列堆叠</div>
<div class="browser">
<div class="bchrome">
<div class="bdots"><i></i><i></i><i></i></div>
<span class="baddr"><svg><use href="#i-lock"/></svg>pangolin.example</span>
</div>
<div class="site" id="siteRoot">
<!-- ============ 站点头 ============ -->
<header class="shead">
<a class="slogo" href="#top">
<img src="../assets/logo-mark.svg" alt="穿山甲">
<span class="wm"><b data-i18n="nav.brand">穿山甲</b><small>PANGOLIN</small></span>
</a>
<nav class="snav">
<a href="#feat" data-i18n="nav.product">产品</a>
<a href="#pricing" data-i18n="nav.pricing">定价</a>
<a href="#download" data-i18n="nav.download">下载</a>
<a href="#docs" data-i18n="nav.docs">文档</a>
</nav>
<div class="shead-right">
<!-- 语言下拉(单语言单显,真能切换页面主要文案;6 语对齐线上 web/website-->
<div class="langsel" data-langsel>
<button class="langsel-trigger" aria-haspopup="true" aria-expanded="false">
<svg><use href="#i-globe"/></svg><span data-langlabel>简体中文</span><svg><use href="#i-chevron-down"/></svg>
</button>
<div class="menu" role="menu" hidden>
<button class="menu-item is-selected" role="menuitem" data-lang="zh">简体中文</button>
<button class="menu-item" role="menuitem" data-lang="en">English</button>
<button class="menu-item" role="menuitem" data-lang="ja">日本語</button>
<button class="menu-item" role="menuitem" data-lang="ko">한국어</button>
<button class="menu-item" role="menuitem" data-lang="ru">Русский</button>
<button class="menu-item" role="menuitem" data-lang="es">Español</button>
</div>
</div>
<a class="btn btn-primary" href="#download"><svg><use href="#i-download"/></svg><span class="btn-lbl" data-i18n="nav.get">立即下载</span></a>
</div>
</header>
<!-- ============ Hero ============ -->
<section class="swrap hero" id="top">
<div class="hero-grid">
<div>
<div class="eyebrow" data-i18n="hero.eyebrow">极速 · 稳定 · 省心</div>
<h1 class="t-display-xl" data-i18n="hero.h1">极速畅连,
网络如丝顺滑</h1>
<p class="lede" data-i18n="hero.lede">轻盈、亲和、即开即用的跨平台网络加速应用。一键连接,智能选线,稳定不掉线。</p>
<div class="hero-cta">
<a class="btn btn-primary btn-lg" href="#download"><svg><use href="#i-download"/></svg><span data-i18n="hero.cta1">免费下载</span></a>
<a class="btn btn-subtle btn-lg" href="#pricing" data-i18n="hero.cta2">查看定价</a>
</div>
<div class="trust">
<span class="t"><svg><use href="#i-check"/></svg><span data-i18n="hero.t1">全球加速线路</span></span>
<span class="t"><svg><use href="#i-check"/></svg><span data-i18n="hero.t2">严格无日志</span></span>
<span class="t"><svg><use href="#i-check"/></svg><span data-i18n="hero.t3">端到端加密</span></span>
</div>
</div>
<div class="orb-card">
<button class="connect-btn is-on" aria-label="已连接">
<svg class="cb-ring" viewBox="0 0 140 140"><circle class="cb-ring-full" cx="70" cy="70" r="64"/></svg>
<svg class="cb-icon"><use href="#i-shield-check"/></svg>
<span class="cb-timer">02:14:08</span>
<span class="cb-sub" data-i18n="orb.enc">已加密</span>
</button>
<div class="orb-meta">
<div class="orb-m"><div class="k"><svg><use href="#i-arrow-down"/></svg><span data-i18n="orb.down">下载</span></div><div class="v">86.4<small> Mb/s</small></div></div>
<div class="orb-m"><div class="k"><svg><use href="#i-arrow-up"/></svg><span data-i18n="orb.up">上传</span></div><div class="v">12.1<small> Mb/s</small></div></div>
</div>
<div class="orb-node">
<span class="cc">HK</span>
<div class="g"><div class="nn" data-i18n="orb.node">香港 · 流媒体</div><div class="ns" data-i18n="orb.nodesub">延迟 18ms · 已连接</div></div>
<svg class="chev"><use href="#i-chevron-right"/></svg>
</div>
</div>
</div>
<div class="strip">
<span class="lbl" data-i18n="strip.txt">已支持</span>
<span class="p"><svg><use href="#i-smartphone"/></svg>iOS</span>
<span class="p"><svg><use href="#i-smartphone"/></svg>Android</span>
<span class="p"><svg><use href="#i-laptop"/></svg>macOS</span>
<span class="p"><svg><use href="#i-monitor"/></svg>Windows</span>
<span class="p"><svg><use href="#i-terminal"/></svg>Linux</span>
</div>
</section>
<!-- ============ Features ============ -->
<section class="band subtle" id="feat">
<div class="swrap">
<div class="sec-head">
<div class="eyebrow" data-i18n="feat.eyebrow">产品</div>
<h2 class="t-h1" data-i18n="feat.h">为日常而生的连接体验</h2>
<p data-i18n="feat.sub">没有复杂配置,没有技术门槛。打开就用,把稳定与隐私留给我们。</p>
</div>
<div class="feat-grid">
<div class="feat-card">
<div class="feat-ic"><svg><use href="#i-power"/></svg></div>
<h3 data-i18n="feat.1t">一键连接</h3>
<p data-i18n="feat.1d">一颗按钮,智能挑选最快线路。无需手动配置,也能秒连。</p>
</div>
<div class="feat-card">
<div class="feat-ic"><svg><use href="#i-zap"/></svg></div>
<h3 data-i18n="feat.2t">智能分流</h3>
<p data-i18n="feat.2d">按规则智能分流,该加速的加速,本地服务直连,互不打扰。</p>
</div>
<div class="feat-card">
<div class="feat-ic"><svg><use href="#i-globe"/></svg></div>
<h3 data-i18n="feat.3t">全球加速线路</h3>
<p data-i18n="feat.3d">覆盖多地优质线路,视频与游戏优化随心选。</p>
</div>
<div class="feat-card">
<div class="feat-ic"><svg><use href="#i-eye-off"/></svg></div>
<h3 data-i18n="feat.4t">严格无日志</h3>
<p data-i18n="feat.4d">我们不记录你的浏览数据。隐私是底线,不是卖点。</p>
</div>
<div class="feat-card">
<div class="feat-ic"><svg><use href="#i-shield"/></svg></div>
<h3 data-i18n="feat.5t">Kill Switch</h3>
<p data-i18n="feat.5d">一旦连接中断,立即阻断网络,杜绝真实 IP 泄露。</p>
</div>
<div class="feat-card">
<div class="feat-ic"><svg><use href="#i-monitor-smartphone"/></svg></div>
<h3 data-i18n="feat.6t">多端同步</h3>
<p data-i18n="feat.6d">一个账户,手机、平板、电脑同时在线,最多 5 台设备。</p>
</div>
</div>
</div>
</section>
<!-- ============ Pricing ============ -->
<section class="band" id="pricing">
<div class="swrap">
<div class="sec-head">
<div class="eyebrow" data-i18n="price.eyebrow">定价</div>
<h2 class="t-h1" data-i18n="price.h">简单透明的价格</h2>
<p data-i18n="price.sub">随时升级或取消。所有套餐均含核心加密与无日志承诺。</p>
<div class="priceseg">
<div class="seg" role="tablist" aria-label="计费周期">
<button type="button" class="is-active" data-cycle="0" role="tab" aria-selected="true" data-i18n="price.monthly">按月</button>
<button type="button" data-cycle="1" role="tab" aria-selected="false" data-i18n="price.yearly">按年</button>
</div>
<span class="pill is-success" data-save hidden><span class="dot"></span><span data-i18n="price.save">省 20%</span></span>
</div>
</div>
<div class="price-grid">
<!-- 免费版 -->
<div class="plan-card">
<div class="plan-name" data-i18n="price.free">免费版</div>
<div class="plan-desc" data-i18n="price.freedesc">适合轻度使用与尝鲜</div>
<div class="plan-price-row"><span class="plan-price" data-price-free>¥0</span></div>
<div class="plan-feats">
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.free1">每日 10 分钟时长</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.free2">仅 1 个基础节点</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.free3">使用前观看广告</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.free4">核心加密 · 无日志</span></div>
</div>
<button class="btn btn-subtle btn-block" data-i18n="price.cta_free">免费下载</button>
</div>
<!-- PRO -->
<div class="plan-card is-featured">
<span class="plan-pop" data-i18n="price.popular">最受欢迎</span>
<div class="plan-name" data-i18n="price.pro">专业版</div>
<div class="plan-desc" data-i18n="price.prodesc">个人用户的最佳选择</div>
<div class="plan-price-row"><span class="plan-price" data-price-pro>¥25</span><span class="plan-per" data-i18n="price.permo">/月</span></div>
<div class="plan-feats">
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.pro1">全球加速线路</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.pro2">无限流量 · 极速</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.pro3">5 台设备同时在线</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.pro4">流媒体 & P2P 优化</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.pro5">Kill Switch · 智能分流</span></div>
</div>
<button class="btn btn-primary btn-block" data-i18n="price.cta_pro">获取激活码</button>
</div>
<!-- 团队版 -->
<div class="plan-card">
<div class="plan-name" data-i18n="price.team">团队版</div>
<div class="plan-desc" data-i18n="price.teamdesc">小团队集中管理</div>
<div class="plan-price-row"><span class="plan-price" data-price-team>¥99</span><span class="plan-per" data-i18n="team.per">/月 · 10 席位</span></div>
<div class="plan-feats">
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.team1">专业版全部功能</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.team2">10 个成员席位</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.team3">集中计费与管理</span></div>
<div class="plan-feat"><svg><use href="#i-check"/></svg><span data-i18n="pf.team4">优先客服</span></div>
</div>
<button class="btn btn-primary btn-block" data-i18n="price.cta_team">获取激活码</button>
</div>
</div>
<!-- 为什么网页上没有支付按钮 -->
<div class="pay-note">
<svg><use href="#i-shield-check"/></svg>
<div>
<b data-i18n="pay.title">为什么网页上没有支付按钮?</b>
<p data-i18n="pay.body">出于风控与隐私考虑,穿山甲不在网页或 App 内直接收款。你可以通过以下任一渠道获取激活码,在客户端内兑换即可开通。</p>
<div class="chips">
<span class="pill pill-outline"><svg><use href="#i-shopping-bag"/></svg><span data-i18n="pay.store">自助发卡商店</span></span>
<span class="pill pill-outline"><svg><use href="#i-credit-card"/></svg><span data-i18n="pay.usdt">USDT (TRC20) · 最隐私</span></span>
<span class="pill pill-outline"><svg><use href="#i-send"/></svg><span data-i18n="chip.tg">Telegram</span></span>
<span class="pill pill-outline"><svg><use href="#i-message-circle"/></svg><span data-i18n="chip.line">LINE</span></span>
<span class="pill pill-outline"><svg><use href="#i-mail"/></svg><span data-i18n="chip.mail">邮箱</span></span>
</div>
</div>
</div>
</div>
</section>
<!-- ============ Download ============ -->
<section class="band tan" id="download">
<div class="swrap">
<div class="sec-head">
<div class="eyebrow" data-i18n="dl.eyebrow">下载</div>
<h2 class="t-h1" data-i18n="dl.h">全平台,随处可用</h2>
<p data-i18n="dl.sub">一个账户,所有设备同步。下载即用,无需配置。</p>
</div>
<div class="dl-grid">
<div class="dl-card">
<div class="dl-ic"><svg><use href="#i-smartphone"/></svg></div>
<div class="pn">iOS</div><div class="pv">iOS 16+</div>
<span class="btn btn-subtle is-disabled btn-block"><svg><use href="#i-download"/></svg><span data-i18n="dl.soon">敬请期待</span></span>
</div>
<div class="dl-card">
<div class="dl-ic"><svg><use href="#i-smartphone"/></svg></div>
<div class="pn">Android</div><div class="pv">Android 9+</div>
<button class="btn btn-primary btn-block"><svg><use href="#i-download"/></svg><span data-i18n="dl.get">下载</span></button>
</div>
<div class="dl-card">
<div class="dl-ic"><svg><use href="#i-laptop"/></svg></div>
<div class="pn">macOS</div><div class="pv">macOS 12+</div>
<button class="btn btn-primary btn-block"><svg><use href="#i-download"/></svg><span data-i18n="dl.get">下载</span></button>
</div>
<div class="dl-card">
<div class="dl-ic"><svg><use href="#i-monitor"/></svg></div>
<div class="pn">Windows</div><div class="pv">Win 10/11</div>
<button class="btn btn-primary btn-block"><svg><use href="#i-download"/></svg><span data-i18n="dl.get">下载</span></button>
</div>
<div class="dl-card">
<div class="dl-ic"><svg><use href="#i-terminal"/></svg></div>
<div class="pn">Linux</div><div class="pv">deb / rpm</div>
<span class="btn btn-subtle is-disabled btn-block"><svg><use href="#i-download"/></svg><span data-i18n="dl.soon">敬请期待</span></span>
</div>
</div>
</div>
</section>
<!-- ============ Footer ============ -->
<footer class="foot" id="docs">
<div class="swrap">
<div class="foot-grid">
<div class="foot-brand">
<a class="slogo" href="#top">
<img src="../assets/logo-mark.svg" alt="穿山甲">
<span class="wm"><b data-i18n="nav.brand">穿山甲</b><small>PANGOLIN</small></span>
</a>
<p data-i18n="ft.tag">极简、轻量、亲和的跨平台网络加速应用。守护你的每一次连接。</p>
</div>
<div class="foot-col">
<h4 data-i18n="ft.product">产品</h4>
<a href="#feat" data-i18n="ft.features">功能</a>
<a href="#pricing" data-i18n="ft.pricing">定价</a>
<a href="#download" data-i18n="ft.download">下载</a>
</div>
<div class="foot-col">
<h4 data-i18n="ft.resources">资源</h4>
<a href="#docs" data-i18n="ft.docs">文档</a>
<a href="#docs" data-i18n="ft.faq">常见问题</a>
<a href="#docs" data-i18n="ft.privacy">隐私政策</a>
<a href="#docs" data-i18n="ft.status">服务状态</a>
</div>
<div class="foot-col">
<h4 data-i18n="ft.contact">联系与渠道</h4>
<a href="#" data-i18n="chip.tg">Telegram</a>
<a href="#" data-i18n="chip.line">LINE</a>
<a href="#" data-i18n="chip.mail">邮箱</a>
<span class="pill is-neutral" style="margin-top:var(--space-1)"><span class="dot"></span><span data-i18n="ft.hours">服务时间 每日 9:0024:00 (GMT+8)</span></span>
</div>
</div>
<div class="foot-bar">
<span data-i18n="ft.copy">© 2026 穿山甲 · Pangolin</span>
<span class="icp">京ICP备 0000000 号-0</span>
<span data-i18n="ft.madenote">App 内不支持直接支付 · 资金流全走外部渠道</span>
<span class="sep">
<span class="langsel" data-langsel>
<button class="langsel-trigger" aria-haspopup="true" aria-expanded="false"><svg><use href="#i-globe"/></svg><span data-langlabel>简体中文</span><svg><use href="#i-chevron-down"/></svg></button>
<div class="menu" role="menu" hidden style="right:auto; left:0; top:auto; bottom:calc(100% + var(--space-2))">
<button class="menu-item is-selected" role="menuitem" data-lang="zh">简体中文</button>
<button class="menu-item" role="menuitem" data-lang="en">English</button>
<button class="menu-item" role="menuitem" data-lang="ja">日本語</button>
<button class="menu-item" role="menuitem" data-lang="ko">한국어</button>
<button class="menu-item" role="menuitem" data-lang="ru">Русский</button>
<button class="menu-item" role="menuitem" data-lang="es">Español</button>
</div>
</span>
</span>
</div>
</div>
</footer>
</div><!-- /.site -->
</div><!-- /.browser -->
</div>
<script>
/* ============ 主题切换(明 / 暗)============ */
function applyTheme(t){
if(t==='dark'){ document.documentElement.dataset.theme='dark'; }
else { delete document.documentElement.dataset.theme; }
document.getElementById('themeIcon').setAttribute('href', t==='dark' ? '#i-moon' : '#i-sun');
document.getElementById('themeLabel').textContent = t==='dark' ? '深色' : '浅色';
localStorage.setItem('pangolin-ds-theme', t);
}
document.getElementById('themeBtn').onclick = () => {
const cur = document.documentElement.dataset.theme==='dark' ? 'dark' : 'light';
applyTheme(cur==='dark' ? 'light' : 'dark');
};
applyTheme(localStorage.getItem('pangolin-ds-theme') || 'light');
/* ============ i18n 字典(6 语单显,逐字取自 web/website/src/i18n/strings.ts============ */
const DICT = {
'nav.brand':{zh:'穿山甲',en:'Pangolin',ja:'Pangolin',ko:'Pangolin',ru:'Pangolin',es:'Pangolin'},
'nav.product':{zh:'产品',en:'Product',ja:'製品',ko:'제품',ru:'Продукт',es:'Producto'},
'nav.pricing':{zh:'定价',en:'Pricing',ja:'料金',ko:'요금제',ru:'Цены',es:'Precios'},
'nav.download':{zh:'下载',en:'Download',ja:'ダウンロード',ko:'다운로드',ru:'Скачать',es:'Descargar'},
'nav.docs':{zh:'文档',en:'Docs',ja:'ドキュメント',ko:'문서',ru:'Документация',es:'Documentación'},
'nav.get':{zh:'立即下载',en:'Get the app',ja:'アプリを入手',ko:'앱 받기',ru:'Получить приложение',es:'Obtener la app'},
'hero.eyebrow':{zh:'极速 · 稳定 · 省心',en:'Fast · Stable · Effortless',ja:'高速 · 安定 · 快適',ko:'빠름 · 안정 · 간편',ru:'Быстро · Стабильно · Без забот',es:'Rápido · Estable · Sin complicaciones'},
'hero.h1':{zh:'极速畅连,\n网络如丝顺滑',en:'Faster, smoother,\neverywhere',ja:'もっと速く、もっと滑らかに、\nどこでも',ko:'더 빠르고 더 매끄럽게,\n어디서나',ru:'Быстрее, плавнее,\nвезде',es:'Más rápido, más fluido,\nen todas partes'},
'hero.lede':{zh:'轻盈、亲和、即开即用的跨平台网络加速应用。一键连接,智能选线,稳定不掉线。',en:'A lightweight, friendly cross-platform network accelerator. One tap, smart routing, rock-solid.',ja:'軽快で使いやすい、クロスプラットフォームのネットワークアクセラレーター。ワンタップ接続、スマートルーティング、安定した通信。',ko:'가볍고 친근한 크로스플랫폼 네트워크 가속 앱. 원 탭 연결, 스마트 라우팅, 흔들림 없는 안정성.',ru:'Лёгкий и удобный кроссплатформенный ускоритель сети. Одно касание, умная маршрутизация, надёжное соединение.',es:'Un acelerador de red multiplataforma, ligero y fácil de usar. Un toque, enrutamiento inteligente, total estabilidad.'},
'hero.cta1':{zh:'免费下载',en:'Download free',ja:'無料ダウンロード',ko:'무료 다운로드',ru:'Скачать бесплатно',es:'Descargar gratis'},
'hero.cta2':{zh:'查看定价',en:'See pricing',ja:'料金を見る',ko:'요금 보기',ru:'Посмотреть цены',es:'Ver precios'},
'hero.t1':{zh:'全球加速线路',en:'Global routes',ja:'グローバル回線',ko:'글로벌 라우트',ru:'Глобальные маршруты',es:'Rutas globales'},
'hero.t2':{zh:'严格无日志',en:'Strict no-logs',ja:'厳格なノーログ',ko:'엄격한 노로그',ru:'Строго без логов',es:'Sin registros, estricto'},
'hero.t3':{zh:'端到端加密',en:'End-to-end encrypted',ja:'エンドツーエンド暗号化',ko:'엔드투엔드 암호화',ru:'Сквозное шифрование',es:'Cifrado de extremo a extremo'},
'orb.enc':{zh:'已加密',en:'Encrypted',ja:'暗号化済み',ko:'암호화됨',ru:'Зашифровано',es:'Cifrado'},
'orb.down':{zh:'下载',en:'Down',ja:'下り',ko:'다운',ru:'Приём',es:'Bajada'},
'orb.up':{zh:'上传',en:'Up',ja:'上り',ko:'업',ru:'Отдача',es:'Subida'},
'orb.node':{zh:'香港 · 流媒体',en:'Hong Kong',ja:'香港',ko:'홍콩',ru:'Гонконг',es:'Hong Kong'},
'orb.nodesub':{zh:'延迟 18ms · 已连接',en:'18ms · Connected',ja:'18ms · 接続済み',ko:'18ms · 연결됨',ru:'18 мс · Подключено',es:'18 ms · Conectado'},
'strip.txt':{zh:'已支持',en:'Available on',ja:'対応プラットフォーム',ko:'지원 플랫폼',ru:'Доступно на',es:'Disponible en'},
'feat.eyebrow':{zh:'产品',en:'Product',ja:'製品',ko:'제품',ru:'Продукт',es:'Producto'},
'feat.h':{zh:'为日常而生的连接体验',en:'Built for everyday connection',ja:'毎日の接続のために',ko:'일상을 위한 연결 경험',ru:'Создан для повседневных подключений',es:'Diseñado para la conexión de cada día'},
'feat.sub':{zh:'没有复杂配置,没有技术门槛。打开就用,把稳定与隐私留给我们。',en:'No complex setup, no jargon. Just open and go — we handle speed and privacy.',ja:'複雑な設定も専門用語も不要。開いて使うだけ。速度とプライバシーはお任せください。',ko:'복잡한 설정도, 어려운 용어도 없습니다. 열면 바로 사용, 속도와 개인정보는 저희가 책임집니다.',ru:'Никаких сложных настроек и жаргона. Просто откройте и пользуйтесь — скорость и приватность на нас.',es:'Sin configuraciones complejas ni tecnicismos. Ábrelo y listo: la velocidad y la privacidad corren por nuestra cuenta.'},
'feat.1t':{zh:'一键连接',en:'One-tap connect',ja:'ワンタップ接続',ko:'원 탭 연결',ru:'Подключение одним касанием',es:'Conexión con un toque'},
'feat.1d':{zh:'一颗按钮,智能挑选最快线路。无需手动配置,也能秒连。',en:'One button picks the fastest route. No manual setup, instant connect.',ja:'ボタン一つで最速回線を自動選択。手動設定なしで瞬時に接続。',ko:'버튼 하나로 가장 빠른 라우트를 자동 선택. 수동 설정 없이 바로 연결.',ru:'Одна кнопка выбирает самый быстрый маршрут. Без ручных настроек — мгновенное подключение.',es:'Un botón elige la ruta más rápida. Sin ajustes manuales, conexión instantánea.'},
'feat.2t':{zh:'智能分流',en:'Smart routing',ja:'スマートルーティング',ko:'스마트 라우팅',ru:'Умная маршрутизация',es:'Enrutamiento inteligente'},
'feat.2d':{zh:'按规则智能分流,该加速的加速,本地服务直连,互不打扰。',en:'Smart rule-based routing — accelerate what needs it, keep local traffic direct.',ja:'ルールベースのスマートルーティングで、必要な通信は高速化、ローカル通信は直結。',ko:'규칙 기반 스마트 라우팅으로 필요한 트래픽은 가속하고 로컬 트래픽은 직접 연결.',ru:'Умная маршрутизация по правилам: ускоряем нужное, локальный трафик идёт напрямую.',es:'Enrutamiento inteligente por reglas: acelera lo que lo necesita y deja el tráfico local directo.'},
'feat.3t':{zh:'全球加速线路',en:'Global routes',ja:'グローバル回線',ko:'글로벌 라우트',ru:'Глобальные маршруты',es:'Rutas globales'},
'feat.3d':{zh:'覆盖多地优质线路,视频与游戏优化随心选。',en:'Premium routes across regions, tuned for video & gaming.',ja:'各地の高品質回線を網羅、動画・ゲーム向けに最適化。',ko:'여러 지역의 프리미엄 라우트, 영상과 게임에 맞춰 최적화.',ru:'Премиум-маршруты по регионам, настроенные для видео и игр.',es:'Rutas premium en varias regiones, optimizadas para vídeo y juegos.'},
'feat.4t':{zh:'严格无日志',en:'Strict no-logs',ja:'厳格なノーログ',ko:'엄격한 노로그',ru:'Строго без логов',es:'Sin registros, estricto'},
'feat.4d':{zh:'我们不记录你的浏览数据。隐私是底线,不是卖点。',en:'We never log your browsing. Privacy is the baseline, not a feature.',ja:'閲覧データは一切記録しません。プライバシーは売り文句ではなく、当然の前提です。',ko:'고객의 브라우징 데이터를 절대 기록하지 않습니다. 개인정보 보호는 기본이지 특별 기능이 아닙니다.',ru:'Мы не ведём журнал вашего трафика. Приватность — это база, а не опция.',es:'Nunca registramos tu navegación. La privacidad es la base, no un extra.'},
'feat.5t':{ zh:'Kill Switch', en:'Kill Switch', ja:'キルスイッチ', ko:'킬 스위치', ru:'Kill Switch', es:'Kill Switch' },
'feat.5d':{ zh:'一旦连接中断,立即阻断网络,杜绝真实 IP 泄露。', en:'If the tunnel drops, traffic is blocked instantly — no IP leaks.', ja:'接続が切れた瞬間に通信を遮断し、実IPの漏洩を防ぎます。', ko:'터널이 끊기면 즉시 트래픽을 차단해 실제 IP 노출을 막습니다.', ru:'Если соединение обрывается, трафик мгновенно блокируется — без утечек IP.', es:'Si la conexión se cae, el tráfico se bloquea al instante:sin filtraciones de IP.' },
'feat.6t':{ zh:'多端同步', en:'Multi-device', ja:'マルチデバイス', ko:'멀티 디바이스', ru:'Много устройств', es:'Multidispositivo' },
'feat.6d':{ zh:'一个账户,手机、平板、电脑同时在线,最多 5 台设备。', en:'One account across phone, tablet and desktop — up to 5 devices.', ja:'一つのアカウントでスマホ・タブレット・PCを同時に、最大5台まで。', ko:'하나의 계정으로 휴대폰, 태블릿, PC를 동시에, 최대 5대까지.', ru:'Один аккаунт на телефоне, планшете и компьютере — до 5 устройств.', es:'Una cuenta en teléfono, tableta y ordenador:hasta 5 dispositivos.' },
'price.eyebrow':{zh:'定价',en:'Pricing',ja:'料金',ko:'요금제',ru:'Цены',es:'Precios'},
'price.h':{zh:'简单透明的价格',en:'Simple, honest pricing',ja:'シンプルで正直な料金',ko:'단순하고 정직한 가격',ru:'Простые и честные цены',es:'Precios simples y honestos'},
'price.sub':{zh:'随时升级或取消。所有套餐均含核心加密与无日志承诺。',en:'Upgrade or cancel anytime. Every plan includes core encryption and our no-logs promise.',ja:'いつでもアップグレード・解約可能。すべてのプランにコア暗号化とノーログの約束が含まれます。',ko:'언제든 업그레이드하거나 해지하세요. 모든 요금제에 핵심 암호화와 노로그 약속이 포함됩니다.',ru:'Меняйте тариф или отменяйте в любой момент. В каждый план входит базовое шифрование и обещание не вести логи.',es:'Mejora o cancela cuando quieras. Cada plan incluye cifrado esencial y nuestra promesa sin registros.'},
'price.monthly':{zh:'按月',en:'Monthly',ja:'月払い',ko:'월간',ru:'Помесячно',es:'Mensual'},
'price.yearly':{zh:'按年',en:'Yearly',ja:'年払い',ko:'연간',ru:'Ежегодно',es:'Anual'},
'price.save':{zh:'省 20%',en:'Save 20%',ja:'20%お得',ko:'20% 절약',ru:'Экономия 20%',es:'Ahorra 20%'},
'price.permo':{zh:'/月',en:'/mo',ja:'/月',ko:'/월',ru:'/мес',es:'/mes'},
'price.free':{zh:'免费版',en:'Free',ja:'無料版',ko:'무료',ru:'Бесплатный',es:'Gratis'},
'price.freedesc':{zh:'适合轻度使用与尝鲜',en:'For light, casual use',ja:'ライトなお試し利用に',ko:'가볍게 써보는 사용자에게',ru:'Для лёгкого, эпизодического использования',es:'Para un uso ligero y ocasional'},
'price.pro':{zh:'专业版',en:'Pro',ja:'プロ版',ko:'프로',ru:'Pro',es:'Pro'},
'price.prodesc':{zh:'个人用户的最佳选择',en:'Best for individuals',ja:'個人ユーザーに最適',ko:'개인 사용자에게 최적',ru:'Оптимально для одного пользователя',es:'Ideal para uso individual'},
'price.team':{zh:'团队版',en:'Team',ja:'チーム版',ko:'팀',ru:'Команда',es:'Equipo'},
'price.teamdesc':{zh:'小团队集中管理',en:'For small teams',ja:'小規模チームの一括管理に',ko:'소규모 팀 통합 관리',ru:'Для небольших команд',es:'Para equipos pequeños'},
'price.popular':{zh:'最受欢迎',en:'Most popular',ja:'一番人気',ko:'가장 인기',ru:'Самый популярный',es:'El más popular'},
'price.cta_free':{zh:'免费下载',en:'Download free',ja:'無料ダウンロード',ko:'무료 다운로드',ru:'Скачать бесплатно',es:'Descargar gratis'},
'price.cta_pro':{zh:'获取激活码',en:'Get a code',ja:'コードを入手',ko:'코드 받기',ru:'Получить код',es:'Obtener un código'},
'price.cta_team':{zh:'获取激活码',en:'Get a code',ja:'コードを入手',ko:'코드 받기',ru:'Получить код',es:'Obtener un código'},
'team.per':{zh:'/月 · 10 席位',en:'/mo · 10 seats',ja:'/月 · 10席',ko:'/월 · 10석',ru:'/мес · 10 мест',es:'/mes · 10 plazas'},
'pf.free1':{zh:'每日 10 分钟时长',en:'10 min per day',ja:'1日10分',ko:'하루 10분',ru:'10 минут в день',es:'10 min al día'},
'pf.free2':{zh:'仅 1 个基础节点',en:'1 basic node only',ja:'基本ノード1つのみ',ko:'기본 노드 1개만',ru:'Только 1 базовый узел',es:'Solo 1 nodo básico'},
'pf.free3':{zh:'使用前观看广告',en:'Watch an ad to start',ja:'利用前に広告を視聴',ko:'사용 전 광고 시청',ru:'Просмотр рекламы перед запуском',es:'Ver un anuncio para empezar'},
'pf.free4':{zh:'核心加密 · 无日志',en:'Core encryption · no-logs',ja:'コア暗号化 · ノーログ',ko:'핵심 암호화 · 노로그',ru:'Базовое шифрование · без логов',es:'Cifrado esencial · sin registros'},
'pf.pro1':{zh:'全球加速线路',en:'Global routes',ja:'グローバル回線',ko:'글로벌 라우트',ru:'Глобальные маршруты',es:'Rutas globales'},
'pf.pro2':{zh:'无限流量 · 极速',en:'Unlimited · top speed',ja:'無制限 · 最高速度',ko:'무제한 · 최고 속도',ru:'Без лимитов · максимальная скорость',es:'Ilimitado · máxima velocidad'},
'pf.pro3':{zh:'5 台设备同时在线',en:'5 devices at once',ja:'5台同時接続',ko:'동시 5대',ru:'5 устройств одновременно',es:'5 dispositivos a la vez'},
'pf.pro4':{zh:'流媒体 & P2P 优化',en:'Streaming & P2P routes',ja:'ストリーミング & P2P最適化',ko:'스트리밍 & P2P 라우트',ru:'Маршруты для стриминга и P2P',es:'Rutas para streaming y P2P'},
'pf.pro5':{zh:'Kill Switch · 智能分流',en:'Kill Switch · smart routing',ja:'キルスイッチ · スマートルーティング',ko:'킬 스위치 · 스마트 라우팅',ru:'Kill Switch · умная маршрутизация',es:'Kill Switch · enrutamiento inteligente'},
'pf.team1':{zh:'专业版全部功能',en:'Everything in Pro',ja:'プロ版の全機能',ko:'프로의 모든 기능',ru:'Всё из тарифа Pro',es:'Todo lo de Pro'},
'pf.team2':{zh:'10 个成员席位',en:'10 seats',ja:'10メンバー席',ko:'멤버 10석',ru:'10 мест для участников',es:'10 plazas'},
'pf.team3':{zh:'集中计费与管理',en:'Central billing & admin',ja:'一括請求と管理',ko:'통합 결제 및 관리',ru:'Единый биллинг и администрирование',es:'Facturación y administración centralizadas'},
'pf.team4':{zh:'优先客服',en:'Priority support',ja:'優先サポート',ko:'우선 지원',ru:'Приоритетная поддержка',es:'Soporte prioritario'},
'pay.title':{zh:'为什么网页上没有支付按钮?',en:'Why no checkout on this page?',ja:'なぜこのページに決済ボタンがないのですか?',ko:'이 페이지에 결제 버튼이 없는 이유는?',ru:'Почему на этой странице нет оплаты?',es:'¿Por qué no hay pago en esta página?'},
'pay.body':{zh:'出于风控与隐私考虑,穿山甲不在网页或 App 内直接收款。你可以通过以下任一渠道获取激活码,在客户端内兑换即可开通。',en:'For risk and privacy reasons, Pangolin never takes payment in the web or app. Get an activation code through any channel below, then redeem it in the client.',ja:'リスク管理とプライバシーの観点から、Pangolinはウェブやアプリ内で直接決済を受け付けません。以下のいずれかの窓口でアクティベーションコードを入手し、クライアント内で引き換えてください。',ko:'리스크 관리와 개인정보 보호를 위해 Pangolin은 웹이나 앱에서 직접 결제를 받지 않습니다. 아래 채널에서 활성화 코드를 받아 클라이언트에서 등록하세요.',ru:'В целях безопасности и приватности Pangolin не принимает оплату в вебе или приложении. Получите код активации по любому из каналов ниже и активируйте его в клиенте.',es:'Por motivos de seguridad y privacidad, Pangolin nunca cobra en la web ni en la app. Consigue un código de activación por cualquiera de estos canales y canjéalo en el cliente.'},
'pay.store':{zh:'自助发卡商店',en:'Self-serve store',ja:'セルフサービスストア',ko:'셀프 발급 스토어',ru:'Магазин самообслуживания',es:'Tienda de autoservicio'},
'pay.usdt':{zh:'USDT (TRC20) · 最隐私',en:'USDT (TRC20) · most private',ja:'USDT (TRC20) · 最もプライベート',ko:'USDT (TRC20) · 가장 프라이빗',ru:'USDT (TRC20) · максимум приватности',es:'USDT (TRC20) · lo más privado'},
'chip.tg':{zh:'Telegram',en:'Telegram',ja:'Telegram',ko:'Telegram',ru:'Telegram',es:'Telegram'},
'chip.line':{zh:'LINE',en:'LINE',ja:'LINE',ko:'LINE',ru:'LINE',es:'LINE'},
'chip.mail':{zh:'邮箱',en:'Email',ja:'メール',ko:'이메일',ru:'Эл. почта',es:'Correo'},
'dl.eyebrow':{zh:'下载',en:'Download',ja:'ダウンロード',ko:'다운로드',ru:'Скачать',es:'Descargar'},
'dl.h':{zh:'全平台,随处可用',en:'Every platform, everywhere',ja:'すべてのプラットフォーム、どこでも',ko:'모든 플랫폼, 어디서나',ru:'Все платформы, везде',es:'Todas las plataformas, en todas partes'},
'dl.sub':{zh:'一个账户,所有设备同步。下载即用,无需配置。',en:'One account syncs every device. Download and go.',ja:'一つのアカウントで全デバイスを同期。ダウンロードしてすぐ使えます。',ko:'하나의 계정으로 모든 기기를 동기화. 내려받아 바로 사용.',ru:'Один аккаунт синхронизирует все устройства. Скачал — и в путь.',es:'Una cuenta sincroniza todos tus dispositivos. Descarga y listo.'},
'dl.get':{zh:'下载',en:'Download',ja:'ダウンロード',ko:'다운로드',ru:'Скачать',es:'Descargar'},
'dl.soon':{zh:'敬请期待',en:'Coming soon',ja:'近日公開',ko:'출시 예정',ru:'Скоро',es:'Próximamente'},
'ft.tag':{zh:'极简、轻量、亲和的跨平台网络加速应用。守护你的每一次连接。',en:'A minimal, friendly cross-platform network accelerator. Protecting every connection.',ja:'ミニマルで軽快、使いやすいクロスプラットフォームのネットワークアクセラレーター。すべての接続を守ります。',ko:'미니멀하고 가벼우며 친근한 크로스플랫폼 네트워크 가속 앱. 모든 연결을 지킵니다.',ru:'Минималистичный, лёгкий и удобный кроссплатформенный ускоритель сети. Защищает каждое соединение.',es:'Un acelerador de red multiplataforma minimalista, ligero y amable. Protegiendo cada conexión.'},
'ft.product':{zh:'产品',en:'Product',ja:'製品',ko:'제품',ru:'Продукт',es:'Producto'},
'ft.resources':{zh:'资源',en:'Resources',ja:'リソース',ko:'리소스',ru:'Ресурсы',es:'Recursos'},
'ft.contact':{zh:'联系与渠道',en:'Contact',ja:'お問い合わせ',ko:'문의 및 채널',ru:'Контакты',es:'Contacto'},
'ft.features':{zh:'功能',en:'Features',ja:'機能',ko:'기능',ru:'Возможности',es:'Funciones'},
'ft.pricing':{zh:'定价',en:'Pricing',ja:'料金',ko:'요금제',ru:'Цены',es:'Precios'},
'ft.download':{zh:'下载',en:'Download',ja:'ダウンロード',ko:'다운로드',ru:'Скачать',es:'Descargar'},
'ft.docs':{zh:'文档',en:'Docs',ja:'ドキュメント',ko:'문서',ru:'Документация',es:'Documentación'},
'ft.faq':{zh:'常见问题',en:'FAQ',ja:'よくある質問',ko:'자주 묻는 질문',ru:'Вопросы и ответы',es:'Preguntas frecuentes'},
'ft.privacy':{zh:'隐私政策',en:'Privacy',ja:'プライバシー',ko:'개인정보',ru:'Конфиденциальность',es:'Privacidad'},
'ft.status':{zh:'服务状态',en:'Status',ja:'サービス状況',ko:'서비스 상태',ru:'Статус',es:'Estado'},
'ft.hours':{zh:'服务时间 每日 9:0024:00 (GMT+8)',en:'Hours: Daily 9:0024:00 (GMT+8)',ja:'営業時間 毎日 9:0024:00 (GMT+8)',ko:'운영 시간 매일 9:0024:00 (GMT+8)',ru:'Часы работы: ежедневно 9:0024:00 (GMT+8)',es:'Horario: todos los días 9:0024:00 (GMT+8)'},
'ft.copy':{zh:'© 2026 穿山甲 · Pangolin',en:'© 2026 Pangolin',ja:'© 2026 Pangolin',ko:'© 2026 Pangolin',ru:'© 2026 Pangolin',es:'© 2026 Pangolin'},
'ft.madenote':{zh:'App 内不支持直接支付 · 资金流全走外部渠道',en:'No in-app payment · all purchases via external channels',ja:'アプリ内決済なし · すべての購入は外部窓口で',ko:'앱 내 결제 없음 · 모든 구매는 외부 채널로',ru:'Без оплаты в приложении · все покупки через внешние каналы',es:'Sin pago en la app · todas las compras por canales externos'}
};
/* 语言原生名(下拉触发器显示)*/
const LANG_LABEL = { zh:'简体中文', en:'English', ja:'日本語', ko:'한국어', ru:'Русский', es:'Español' };
const HTML_LANG = { zh:'zh-CN', en:'en', ja:'ja', ko:'ko', ru:'ru', es:'es' };
/* 价格表:[月付, 年付·按月],对齐 §7 / strings.tszh 人民币、其余美元)*/
const PRICES = {
zh:{ free:['¥0','¥0'], pro:['¥25','¥20'], team:['¥99','¥79'] },
en:{ free:['$0','$0'], pro:['$3.99','$3.19'], team:['$13.99','$11.19'] },
ja:{ free:['$0','$0'], pro:['$3.99','$3.19'], team:['$13.99','$11.19'] },
ko:{ free:['$0','$0'], pro:['$3.99','$3.19'], team:['$13.99','$11.19'] },
ru:{ free:['$0','$0'], pro:['$3.99','$3.19'], team:['$13.99','$11.19'] },
es:{ free:['$0','$0'], pro:['$3.99','$3.19'], team:['$13.99','$11.19'] }
};
let curLang = localStorage.getItem('pangolin-site-lang') || 'zh';
if(!LANG_LABEL[curLang]) curLang = 'zh';
let curCycle = 0; // 0=月付 1=年付
function renderPrices(){
const p = PRICES[curLang] || PRICES.en;
document.querySelector('[data-price-free]').textContent = p.free[curCycle];
document.querySelector('[data-price-pro]').textContent = p.pro[curCycle];
document.querySelector('[data-price-team]').textContent = p.team[curCycle];
document.querySelectorAll('[data-save]').forEach(el => el.hidden = curCycle !== 1);
}
function setLang(lang){
if(!LANG_LABEL[lang]) lang = 'zh';
curLang = lang;
document.documentElement.lang = HTML_LANG[lang] || lang;
document.querySelectorAll('[data-i18n]').forEach(el => {
const v = DICT[el.getAttribute('data-i18n')];
if(v) el.textContent = (v[lang] != null ? v[lang] : v.en);
});
document.querySelectorAll('[data-langlabel]').forEach(el => el.textContent = LANG_LABEL[lang]);
document.querySelectorAll('[data-langsel] .menu-item').forEach(it => {
const on = it.getAttribute('data-lang') === lang;
it.classList.toggle('is-selected', on);
});
renderPrices();
localStorage.setItem('pangolin-site-lang', lang);
}
/* ============ 语言下拉:展开/收起 + 选中切换(头 + 脚共用)============ */
function initLangsels(){
const sels = [...document.querySelectorAll('[data-langsel]')];
function closeAll(){
sels.forEach(s => {
s.classList.remove('is-open');
const t = s.querySelector('.langsel-trigger');
const m = s.querySelector('.menu');
if(t) t.setAttribute('aria-expanded','false');
if(m) m.hidden = true;
});
}
sels.forEach(sel => {
const trigger = sel.querySelector('.langsel-trigger');
const menu = sel.querySelector('.menu');
trigger.addEventListener('click', e => {
e.stopPropagation();
const willOpen = menu.hidden;
closeAll();
if(willOpen){
menu.hidden = false;
trigger.setAttribute('aria-expanded','true');
sel.classList.add('is-open');
}
});
menu.querySelectorAll('.menu-item').forEach(it => {
it.addEventListener('click', () => { setLang(it.getAttribute('data-lang')); closeAll(); });
});
});
document.addEventListener('click', closeAll);
}
/* ============ 定价「月 / 年」段控 ============ */
function initPriceSeg(){
const seg = document.querySelector('.seg');
if(!seg) return;
seg.querySelectorAll('button').forEach(btn => {
btn.addEventListener('click', () => {
curCycle = Number(btn.getAttribute('data-cycle'));
seg.querySelectorAll('button').forEach(b => {
const on = b === btn;
b.classList.toggle('is-active', on);
b.setAttribute('aria-selected', String(on));
});
renderPrices();
});
});
}
/* ============ 顶栏导航:平滑滚动 + scrollspy 高亮 ============ */
function initNav(){
// 站内锚点平滑滚动(scroll-margin-top 已处理 sticky 偏移)
document.querySelectorAll('a[href^="#"]').forEach(a => {
a.addEventListener('click', e => {
const id = a.getAttribute('href');
if(id === '#') return;
const target = document.querySelector(id);
if(!target) return;
e.preventDefault();
target.scrollIntoView({ behavior:'smooth', block:'start' });
});
});
// scrollspy:观察各区块,高亮对应导航项
const map = { feat:'#feat', pricing:'#pricing', download:'#download', docs:'#docs' };
const navLinks = [...document.querySelectorAll('.snav a')];
function setActive(id){
navLinks.forEach(l => l.classList.toggle('is-active', l.getAttribute('href') === '#' + id));
}
const sections = Object.keys(map).map(id => document.getElementById(id)).filter(Boolean);
const obs = new IntersectionObserver(entries => {
// 取当前处于视口上部、可见比例最高的区块
let best = null;
entries.forEach(en => {
if(en.isIntersecting){
if(!best || en.intersectionRatio > best.intersectionRatio) best = en;
}
});
if(best) setActive(best.target.id);
}, { rootMargin:'-140px 0px -55% 0px', threshold:[0,0.1,0.25,0.5,1] });
sections.forEach(s => obs.observe(s));
}
initLangsels();
initPriceSeg();
initNav();
setLang(curLang);
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff