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>
352 lines
15 KiB
HTML
352 lines
15 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) ============ */
|
|
.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; }
|
|
|
|
.route-demo {
|
|
margin-top: 10px;
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
background: rgba(2, 6, 23, 0.6);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--r-md);
|
|
padding: 18px 22px;
|
|
max-width: 440px;
|
|
}
|
|
.route-demo .row { display: flex; gap: 20px; padding: 4px 0; align-items: center; }
|
|
.route-demo .k { color: var(--text-3); min-width: 70px; }
|
|
.route-demo .v { color: var(--text-0); }
|
|
.route-demo .pill-inline { padding: 2px 8px; border-radius: 4px; background: rgba(34,211,238,0.08); border: 1px solid rgba(34,211,238,0.25); color: var(--cyan); }
|
|
.route-demo .pill-inline.amber { background: rgba(251,191,36,0.08); border-color: rgba(251,191,36,0.25); color: var(--amber); }
|
|
.route-demo .pill-inline.green { background: rgba(52,211,153,0.08); border-color: rgba(52,211,153,0.25); color: var(--green); }
|
|
.route-demo .dot-g { width: 6px; height: 6px; border-radius: 50%; background: var(--green); display: inline-block; margin-right: 6px; box-shadow: 0 0 0 3px rgba(52,211,153,0.15); }
|
|
|
|
.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; letter-spacing: 0.01em; }
|
|
.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); }
|
|
|
|
.field-meta { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; }
|
|
.check { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-2); cursor: pointer; user-select: none; }
|
|
.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; }
|
|
.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);
|
|
}
|
|
.forgot { font-size: 13px; color: var(--text-3); }
|
|
.forgot:hover { color: var(--cyan); }
|
|
|
|
/* Submit button — explicit styles to ensure rendering */
|
|
.submit-btn {
|
|
width: 100%;
|
|
padding: 14px 20px !important;
|
|
font-size: 14px !important;
|
|
margin-top: 8px;
|
|
position: relative;
|
|
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; }
|
|
.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; }
|
|
.foot a:hover { color: var(--cyan-2); }
|
|
|
|
.legal { margin-top: 20px; text-align: center; font-size: 11px; color: var(--text-3); font-family: var(--font-mono); }
|
|
.legal a { color: var(--text-2); border-bottom: 1px dashed var(--border-2); }
|
|
.legal a:hover { color: var(--cyan); border-color: var(--cyan); }
|
|
|
|
@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; }
|
|
.route-demo, .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">
|
|
<!-- LEFT: NARRATIVE -->
|
|
<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">// 你的订阅,已经付过钱了</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="route-demo">
|
|
<div class="row"><span class="k">POST</span><span class="v">/v1/chat/completions</span></div>
|
|
<div class="row"><span class="k">model</span><span class="pill-inline">claude-sonnet-4-5</span></div>
|
|
<div class="row"><span class="k">route →</span><span class="pill-inline amber">claude-pool-03</span></div>
|
|
<div class="row"><span class="k">status</span><span><span class="dot-g"></span><span style="color:var(--green)">200</span><span style="color:var(--text-3); margin:0 6px;">·</span>213ms<span style="color:var(--text-3); margin:0 6px;">·</span>42 tok</span></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>
|
|
|
|
<!-- RIGHT: FORM -->
|
|
<section class="form-side">
|
|
<a href="Landing.html" class="back-home">← 返回首页</a>
|
|
|
|
<form class="form-card" id="login-form" autocomplete="off" novalidate>
|
|
<h1>登录</h1>
|
|
<p class="sub">用你的 PURO AI 账户继续</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>
|
|
</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="••••••••" required>
|
|
<button type="button" class="eye" id="toggle-pw" aria-label="切换显示密码">
|
|
<svg id="eye-open" 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>
|
|
<svg id="eye-closed" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="display:none;">
|
|
<path d="M9.88 9.88A3 3 0 0 0 14.12 14.12M10.73 5.08A10.94 10.94 0 0 1 12 5c6.5 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68M6.61 6.61A13.53 13.53 0 0 0 2 12s3.5 7 10 7a9.77 9.77 0 0 0 5.39-1.61M2 2l20 20"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="field-meta">
|
|
<label class="check">
|
|
<input type="checkbox" checked>
|
|
<span class="box"></span>
|
|
记住我
|
|
</label>
|
|
<a href="#" class="forgot">忘记密码?</a>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary submit-btn" id="submit-btn">
|
|
<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="foot">
|
|
没有账户?<a href="Register.html">注册</a>
|
|
</div>
|
|
|
|
<div class="legal">
|
|
登录即表示你同意 <a href="#">服务条款</a> 与 <a href="#">隐私政策</a>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
</div>
|
|
|
|
<script>
|
|
const pw = document.getElementById('password');
|
|
const toggle = document.getElementById('toggle-pw');
|
|
const eyeOpen = document.getElementById('eye-open');
|
|
const eyeClosed = document.getElementById('eye-closed');
|
|
toggle.addEventListener('click', () => {
|
|
const show = pw.type === 'password';
|
|
pw.type = show ? 'text' : 'password';
|
|
eyeOpen.style.display = show ? 'none' : 'block';
|
|
eyeClosed.style.display = show ? 'block' : 'none';
|
|
});
|
|
|
|
const form = document.getElementById('login-form');
|
|
const btn = document.getElementById('submit-btn');
|
|
form.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
if (!form.email.value || !form.password.value) return;
|
|
btn.classList.add('loading');
|
|
btn.disabled = true;
|
|
setTimeout(() => {
|
|
btn.classList.remove('loading');
|
|
btn.disabled = false;
|
|
btn.querySelector('.label').textContent = '✓ 登录成功';
|
|
btn.style.background = 'var(--green)';
|
|
setTimeout(() => { window.location.href = 'Dashboard.html'; }, 800);
|
|
}, 1000);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|