10 HTML pages + puro.css + HANDOFF.md + 2 images (~810KB total). Reference artifacts for Stage 3 Vue 3 translation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
387 lines
19 KiB
HTML
387 lines
19 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="color-scheme" content="dark">
|
|
<title>注册 — PURO AI</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="puro.css">
|
|
<style>
|
|
body { min-height: 100vh; overflow-x: hidden; }
|
|
|
|
.split { display: grid; grid-template-columns: 1fr 1fr; min-height: 100vh; position: relative; z-index: 1; }
|
|
|
|
/* ============ LEFT (NARRATIVE + STEPS) ============ */
|
|
.narrative {
|
|
position: relative; overflow: hidden;
|
|
padding: 48px 56px;
|
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
border-right: 1px solid var(--border);
|
|
background: linear-gradient(135deg, rgba(34,211,238,0.04), transparent 60%), rgba(15, 23, 42, 0.3);
|
|
}
|
|
.narrative::before {
|
|
content: ""; position: absolute; inset: 0;
|
|
background:
|
|
radial-gradient(600px 400px at 20% 20%, rgba(34,211,238,0.08), transparent 60%),
|
|
radial-gradient(500px 400px at 90% 80%, rgba(168,85,247,0.06), transparent 60%);
|
|
pointer-events: none;
|
|
}
|
|
.narrative-inner { position: relative; display: flex; flex-direction: column; gap: 28px; z-index: 1; }
|
|
|
|
.brand-top { display: inline-flex; align-items: center; gap: 10px; font-weight: 700; font-size: 15px; letter-spacing: -0.01em; color: var(--text-0); }
|
|
.brand-top svg { width: 22px; height: 22px; color: var(--cyan); }
|
|
|
|
.n-kicker { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em; color: var(--cyan); }
|
|
.n-headline { font-size: 52px; font-weight: 800; letter-spacing: -0.03em; line-height: 1.05; display: flex; align-items: baseline; flex-wrap: wrap; gap: 14px; }
|
|
.n-headline .amber { color: var(--amber); }
|
|
.n-headline .cyan { color: var(--cyan); }
|
|
.n-headline .arrow { font-size: 38px; color: var(--text-3); font-weight: 400; }
|
|
.n-sub { color: var(--text-2); font-size: 15px; line-height: 1.75; max-width: 440px; }
|
|
.n-sub .line { display: block; }
|
|
.n-sub .puro { color: var(--text-0); font-weight: 600; }
|
|
|
|
.steps { display: flex; flex-direction: column; gap: 16px; padding: 20px 22px; background: rgba(2, 6, 23, 0.5); border: 1px solid var(--border); border-radius: var(--r-md); max-width: 440px; }
|
|
.steps-title { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); letter-spacing: 0.14em; }
|
|
.step { display: flex; align-items: flex-start; gap: 14px; font-size: 13px; }
|
|
.step-num { flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%; background: rgba(34,211,238,0.1); border: 1px solid rgba(34,211,238,0.3); color: var(--cyan); font-family: var(--font-mono); font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
|
|
.step.active .step-num { background: var(--cyan); color: #042f2e; border-color: var(--cyan); }
|
|
.step-text { color: var(--text-1); line-height: 1.5; padding-top: 2px; }
|
|
.step-text b { color: var(--text-0); font-weight: 600; }
|
|
.step-text .k { font-family: var(--font-mono); background: rgba(255,255,255,0.04); border: 1px solid var(--border-2); padding: 1px 6px; border-radius: 3px; font-size: 11.5px; color: var(--cyan); }
|
|
|
|
.n-bottom { position: relative; z-index: 1; font-family: var(--font-mono); font-size: 11px; color: var(--text-3); display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
|
|
.n-bottom .sep { color: var(--border-2); }
|
|
.n-bottom .live { color: var(--green); display: inline-flex; align-items: center; gap: 6px; }
|
|
.n-bottom .live .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--green); box-shadow: 0 0 6px var(--green); }
|
|
|
|
/* ============ RIGHT (FORM) ============ */
|
|
.form-side { display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 48px 56px; position: relative; }
|
|
.back-home { position: absolute; top: 32px; right: 32px; font-family: var(--font-mono); font-size: 12px; color: var(--text-3); display: inline-flex; align-items: center; gap: 6px; }
|
|
.back-home:hover { color: var(--text-0); }
|
|
|
|
.form-card { width: 100%; max-width: 400px; }
|
|
.form-card h1 { font-size: 32px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 6px; }
|
|
.form-card .sub { color: var(--text-2); font-size: 14px; margin-bottom: 32px; }
|
|
|
|
.field { margin-bottom: 16px; }
|
|
.field label { display: block; font-size: 12px; color: var(--text-2); font-weight: 500; margin-bottom: 8px; }
|
|
.input-wrap { position: relative; display: flex; align-items: center; background: rgba(2, 6, 23, 0.4); border: 1px solid var(--border-2); border-radius: var(--r-md); transition: all .15s; }
|
|
.input-wrap:focus-within { border-color: var(--cyan); background: rgba(34, 211, 238, 0.04); box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.1); }
|
|
.input-wrap .icon { padding: 0 12px; color: var(--text-3); flex-shrink: 0; display: flex; }
|
|
.input-wrap input { flex: 1; background: none; border: none; outline: none; font-family: inherit; color: var(--text-0); font-size: 14px; padding: 12px 14px 12px 0; }
|
|
.input-wrap input::placeholder { color: var(--text-3); }
|
|
.eye { display: flex; padding: 10px 12px; color: var(--text-3); background: none; border: none; cursor: pointer; }
|
|
.eye:hover { color: var(--text-1); }
|
|
.valid-ico { display: none; color: var(--green); padding: 0 12px; }
|
|
.input-wrap input.ok ~ .valid-ico { display: flex; }
|
|
.input-wrap input.ok { padding-right: 0; }
|
|
.input-wrap input.ok + .eye { display: none; }
|
|
|
|
/* Password strength */
|
|
.pw-strength { display: flex; gap: 4px; margin-top: 8px; margin-bottom: 6px; }
|
|
.pw-strength .bar { flex: 1; height: 3px; background: var(--border); border-radius: 2px; transition: background .2s; }
|
|
.pw-strength[data-score="1"] .bar:nth-child(1) { background: var(--red); }
|
|
.pw-strength[data-score="2"] .bar:nth-child(-n+2) { background: var(--amber); }
|
|
.pw-strength[data-score="3"] .bar:nth-child(-n+3) { background: var(--cyan); }
|
|
.pw-strength[data-score="4"] .bar { background: var(--green); }
|
|
|
|
.pw-hint { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: 10px; color: var(--text-3); letter-spacing: 0.05em; }
|
|
.pw-hint .val { color: var(--text-3); }
|
|
.pw-hint[data-score="1"] .val { color: var(--red); }
|
|
.pw-hint[data-score="2"] .val { color: var(--amber); }
|
|
.pw-hint[data-score="3"] .val { color: var(--cyan); }
|
|
.pw-hint[data-score="4"] .val { color: var(--green); }
|
|
|
|
.match-hint { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); margin-top: 6px; min-height: 14px; }
|
|
.match-hint.mismatch { color: var(--red); }
|
|
.match-hint.ok { color: var(--green); }
|
|
|
|
.check { display: inline-flex; align-items: flex-start; gap: 10px; font-size: 13px; color: var(--text-2); cursor: pointer; user-select: none; margin: 6px 0 20px; line-height: 1.5; }
|
|
.check input { position: absolute; opacity: 0; pointer-events: none; }
|
|
.check .box { width: 14px; height: 14px; border-radius: 3px; border: 1px solid var(--border-2); background: rgba(2, 6, 23, 0.4); position: relative; flex-shrink: 0; margin-top: 2px; }
|
|
.check input:checked ~ .box { background: var(--cyan); border-color: var(--cyan); }
|
|
.check input:checked ~ .box::after { content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px; border: solid #042f2e; border-width: 0 2px 2px 0; transform: rotate(45deg); }
|
|
.check a { color: var(--text-1); border-bottom: 1px dashed var(--border-2); }
|
|
.check a:hover { color: var(--cyan); border-color: var(--cyan); }
|
|
|
|
.submit-btn {
|
|
width: 100%;
|
|
padding: 14px 20px !important;
|
|
font-size: 14px !important;
|
|
margin-top: 4px;
|
|
background: var(--cyan) !important;
|
|
color: #042f2e !important;
|
|
border: 1px solid var(--cyan) !important;
|
|
font-weight: 600 !important;
|
|
z-index: 2;
|
|
}
|
|
.submit-btn:hover { background: var(--cyan-2) !important; }
|
|
.submit-btn:disabled { opacity: 0.5; cursor: not-allowed; background: var(--border-2) !important; border-color: var(--border-2) !important; color: var(--text-3) !important; }
|
|
.submit-btn .spinner { width: 14px; height: 14px; border: 2px solid rgba(0,0,0,0.2); border-top-color: #042f2e; border-radius: 50%; animation: spin .7s linear infinite; display: none; }
|
|
.submit-btn.loading .spinner { display: inline-block; }
|
|
.submit-btn.loading .label { opacity: 0.5; }
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
|
.ghost-btn { width: 100%; padding: 14px 20px !important; font-size: 14px !important; font-weight: 500 !important; }
|
|
|
|
.divider { display: flex; align-items: center; gap: 14px; margin: 24px 0; color: var(--text-3); font-size: 11px; font-family: var(--font-mono); letter-spacing: 0.15em; }
|
|
.divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
|
|
|
|
.linuxdo-ico { width: 18px; height: 18px; border-radius: 3px; background: linear-gradient(135deg, #f0a030, #f05050); display: inline-flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 800; color: white; }
|
|
|
|
.foot { margin-top: 24px; text-align: center; font-size: 13px; color: var(--text-2); }
|
|
.foot a { color: var(--cyan); font-weight: 500; }
|
|
|
|
.bonus-note { margin-top: 18px; padding: 12px 14px; background: rgba(52,211,153,0.06); border: 1px solid rgba(52,211,153,0.2); border-radius: var(--r-md); font-size: 12px; color: var(--text-1); display: flex; align-items: center; gap: 10px; }
|
|
.bonus-note .emoji { flex-shrink: 0; color: var(--green); font-weight: 700; font-family: var(--font-mono); padding: 3px 8px; background: rgba(52,211,153,0.15); border-radius: 4px; font-size: 11px; }
|
|
|
|
@media (max-width: 900px) {
|
|
.split { grid-template-columns: 1fr; }
|
|
.narrative { padding: 32px 28px; border-right: none; border-bottom: 1px solid var(--border); }
|
|
.n-headline { font-size: 36px; }
|
|
.steps, .n-bottom { display: none; }
|
|
.form-side { padding: 40px 28px; }
|
|
.back-home { top: 20px; right: 20px; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="bg-glow"></div>
|
|
<div class="grain"></div>
|
|
|
|
<div class="split">
|
|
<section class="narrative">
|
|
<div class="narrative-inner">
|
|
<a href="Landing.html" class="brand-top">
|
|
<svg viewBox="0 0 24 24" fill="none">
|
|
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
|
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
|
</svg>
|
|
PURO AI
|
|
</a>
|
|
|
|
<div>
|
|
<div class="n-kicker">// 5 分钟开始用</div>
|
|
<h1 class="n-headline" style="margin-top: 12px;">
|
|
<span class="amber">N</span> 个订阅
|
|
<span class="arrow">→</span>
|
|
<span class="cyan">1</span> 个 key
|
|
</h1>
|
|
</div>
|
|
|
|
<div class="n-sub">
|
|
<span class="line">省去切换账号的繁琐,</span>
|
|
<span class="line">省去为多个高昂订阅重复买单。</span>
|
|
<span class="line" style="margin-top: 8px;"><span class="puro">PURO</span>(纯粹)—— 让 AI 调用回归本质。</span>
|
|
</div>
|
|
|
|
<div class="steps">
|
|
<div class="steps-title">// 下一步</div>
|
|
<div class="step active">
|
|
<div class="step-num">1</div>
|
|
<div class="step-text"><b>创建账户</b> · 邮箱 + 密码,或用 LinuxDO OAuth</div>
|
|
</div>
|
|
<div class="step">
|
|
<div class="step-num">2</div>
|
|
<div class="step-text"><b>绑定订阅</b> · OAuth 接入你现有的 Claude Pro / ChatGPT Plus</div>
|
|
</div>
|
|
<div class="step">
|
|
<div class="step-num">3</div>
|
|
<div class="step-text"><b>生成 key</b> · 拿到 <span class="k">sk-puro-…</span>,换掉 SDK 的 <span class="k">base_url</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="n-bottom">
|
|
<span>Claude</span><span class="sep">·</span>
|
|
<span>ChatGPT</span><span class="sep">·</span>
|
|
<span>Codex</span><span class="sep">·</span>
|
|
<span>Gemini</span>
|
|
<span class="sep">|</span>
|
|
<span class="live"><span class="dot"></span>ai.puro.im · operational</span>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="form-side">
|
|
<a href="Landing.html" class="back-home">← 返回首页</a>
|
|
|
|
<form class="form-card" id="reg-form" autocomplete="off" novalidate>
|
|
<h1>创建账户</h1>
|
|
<p class="sub">注册即送 <b style="color:var(--cyan)">$5</b> 测试积分</p>
|
|
|
|
<div class="field">
|
|
<label for="email">邮箱</label>
|
|
<div class="input-wrap">
|
|
<span class="icon">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
<rect x="3" y="5" width="18" height="14" rx="2"/>
|
|
<path d="M3 7l9 6 9-6"/>
|
|
</svg>
|
|
</span>
|
|
<input type="email" id="email" name="email" placeholder="you@puro.im" required>
|
|
<span class="valid-ico">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M5 12l5 5L20 7"/>
|
|
</svg>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label for="password">密码</label>
|
|
<div class="input-wrap">
|
|
<span class="icon">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
|
<path d="M8 11V7a4 4 0 0 1 8 0v4"/>
|
|
</svg>
|
|
</span>
|
|
<input type="password" id="password" name="password" placeholder="至少 8 位,含字母与数字" required>
|
|
<button type="button" class="eye" id="toggle-pw" aria-label="切换显示密码">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/>
|
|
<circle cx="12" cy="12" r="3"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="pw-strength" id="pw-strength" data-score="0">
|
|
<span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span>
|
|
</div>
|
|
<div class="pw-hint" id="pw-hint" data-score="0">
|
|
<span>// strength</span>
|
|
<span class="val">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label for="password2">确认密码</label>
|
|
<div class="input-wrap">
|
|
<span class="icon">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
|
<path d="M8 11V7a4 4 0 0 1 8 0v4"/>
|
|
</svg>
|
|
</span>
|
|
<input type="password" id="password2" name="password2" placeholder="再输入一次" required>
|
|
<span class="valid-ico">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M5 12l5 5L20 7"/>
|
|
</svg>
|
|
</span>
|
|
</div>
|
|
<div class="match-hint" id="match-hint"></div>
|
|
</div>
|
|
|
|
<label class="check">
|
|
<input type="checkbox" id="terms">
|
|
<span class="box"></span>
|
|
<span>我已阅读并同意 <a href="#">服务条款</a> 与 <a href="#">隐私政策</a></span>
|
|
</label>
|
|
|
|
<button type="submit" class="btn btn-primary submit-btn" id="submit-btn" disabled>
|
|
<span class="spinner"></span>
|
|
<span class="label">创建账户 →</span>
|
|
</button>
|
|
|
|
<div class="divider">OR</div>
|
|
|
|
<button type="button" class="btn btn-ghost ghost-btn">
|
|
<span class="linuxdo-ico">L</span>
|
|
使用 LinuxDO 注册
|
|
</button>
|
|
|
|
<div class="bonus-note">
|
|
<span class="emoji">+$5</span>
|
|
<span>完成注册即送 <b style="color:var(--text-0)">$5</b> 测试积分 —— 够你跑几万次 Claude 请求。</span>
|
|
</div>
|
|
|
|
<div class="foot">
|
|
已有账户?<a href="Login.html">登录</a>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
</div>
|
|
|
|
<script>
|
|
const email = document.getElementById('email');
|
|
const pw = document.getElementById('password');
|
|
const pw2 = document.getElementById('password2');
|
|
const terms = document.getElementById('terms');
|
|
const btn = document.getElementById('submit-btn');
|
|
const strengthBars = document.getElementById('pw-strength');
|
|
const strengthHint = document.getElementById('pw-hint');
|
|
const matchHint = document.getElementById('match-hint');
|
|
const toggle = document.getElementById('toggle-pw');
|
|
|
|
toggle.addEventListener('click', () => {
|
|
const show = pw.type === 'password';
|
|
pw.type = show ? 'text' : 'password';
|
|
});
|
|
|
|
function scorePw(v) {
|
|
if (!v) return 0;
|
|
let s = 0;
|
|
if (v.length >= 8) s++;
|
|
if (/[A-Z]/.test(v) && /[a-z]/.test(v)) s++;
|
|
if (/\d/.test(v)) s++;
|
|
if (/[^A-Za-z0-9]/.test(v) || v.length >= 14) s++;
|
|
return s;
|
|
}
|
|
const labels = ['—', '弱', '中', '强', '极强'];
|
|
|
|
function updateAll() {
|
|
const s = scorePw(pw.value);
|
|
strengthBars.dataset.score = s;
|
|
strengthHint.dataset.score = s;
|
|
strengthHint.querySelector('.val').textContent = labels[s];
|
|
|
|
// email validation
|
|
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) {
|
|
email.classList.add('ok');
|
|
} else {
|
|
email.classList.remove('ok');
|
|
}
|
|
|
|
// password match
|
|
if (!pw2.value) {
|
|
matchHint.textContent = '';
|
|
matchHint.className = 'match-hint';
|
|
pw2.classList.remove('ok');
|
|
} else if (pw2.value === pw.value) {
|
|
matchHint.textContent = '// matched';
|
|
matchHint.className = 'match-hint ok';
|
|
pw2.classList.add('ok');
|
|
} else {
|
|
matchHint.textContent = '// passwords do not match';
|
|
matchHint.className = 'match-hint mismatch';
|
|
pw2.classList.remove('ok');
|
|
}
|
|
|
|
// enable submit?
|
|
const valid = email.classList.contains('ok') && s >= 2 && pw.value === pw2.value && pw2.value && terms.checked;
|
|
btn.disabled = !valid;
|
|
}
|
|
|
|
[email, pw, pw2].forEach(el => el.addEventListener('input', updateAll));
|
|
terms.addEventListener('change', updateAll);
|
|
|
|
const form = document.getElementById('reg-form');
|
|
form.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
if (btn.disabled) return;
|
|
btn.classList.add('loading');
|
|
btn.disabled = true;
|
|
setTimeout(() => {
|
|
btn.classList.remove('loading');
|
|
btn.querySelector('.label').textContent = '✓ 注册成功,正在跳转...';
|
|
btn.style.background = 'var(--green)';
|
|
setTimeout(() => { window.location.href = 'Binding.html'; }, 800);
|
|
}, 1200);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|