初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,981 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<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="_base.css" />
|
||||
<link rel="stylesheet" href="_chrome.css" />
|
||||
<style>
|
||||
/* ============================================================
|
||||
* 铜镜鉴心 v2.1 · 登入灵台
|
||||
* 与 frontend/design/copper-mirror-redesign-v2.html 同源语言。
|
||||
*
|
||||
* 此页只做一件事:让用户以「照镜」的仪式感认证身份。
|
||||
* 登录卡 = 抛光铜镜(唯一的镜面),其余为镜背暗纹。
|
||||
* 星盘、铭文环、铜面噪点组成镜背的铸纹体量。
|
||||
* 扫描亮带 = 铜镜在烛光下微倾时的漫反射(唯一动态事件)。
|
||||
*
|
||||
* 亮色模式绝非「暗色反相」——而是暖纸上陈列的铜镜,
|
||||
* 保留鎏金发丝线、铜绿锈点缀、暖铜高光。
|
||||
* ============================================================ */
|
||||
|
||||
|
||||
/* ── 亮色:素绢暖纸上的铜镜 ── */
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--ink);
|
||||
color: var(--fg);
|
||||
font-family:
|
||||
"PingFang SC",
|
||||
"Microsoft YaHei",
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
system-ui,
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* 镜背基底
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
|
||||
/* 铜面噪点(feTurbulence — 金属颗粒感) */
|
||||
.grain-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
[data-theme="dark"] .grain-overlay {
|
||||
opacity: 0.035;
|
||||
mix-blend-mode: soft-light;
|
||||
}
|
||||
[data-theme="light"] .grain-overlay {
|
||||
opacity: 0.015;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
|
||||
/* 同心铭文轨 */
|
||||
.inscription-rings {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.inscription-rings svg {
|
||||
width: min(92vmin, 820px);
|
||||
height: min(92vmin, 820px);
|
||||
}
|
||||
[data-theme="dark"] .inscription-rings svg {
|
||||
opacity: 0.18;
|
||||
}
|
||||
[data-theme="light"] .inscription-rings svg {
|
||||
opacity: 0.1;
|
||||
}
|
||||
.inscription-rings .ring-ink {
|
||||
fill: none;
|
||||
stroke: var(--gilt-faint);
|
||||
stroke-width: 0.5;
|
||||
}
|
||||
.inscription-rings .ring-faint {
|
||||
fill: none;
|
||||
stroke: var(--gilt-faint);
|
||||
stroke-width: 0.3;
|
||||
stroke-dasharray: 2 6;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* 顶栏
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
|
||||
.topbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 40;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 14px clamp(20px, 4vw, 48px);
|
||||
background: color-mix(in srgb, var(--ink) 76%, transparent);
|
||||
backdrop-filter: blur(16px) saturate(140%);
|
||||
-webkit-backdrop-filter: blur(16px) saturate(140%);
|
||||
}
|
||||
.topbar .spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 6px 14px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--rule);
|
||||
background: var(--surface);
|
||||
color: var(--fg-dim);
|
||||
font-size: 11.5px;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
color 0.18s,
|
||||
border-color 0.18s,
|
||||
background 0.18s;
|
||||
font-family: inherit;
|
||||
}
|
||||
.pill:hover {
|
||||
color: var(--fg);
|
||||
border-color: var(--gilt-dim);
|
||||
background: var(--surface-hi);
|
||||
}
|
||||
.pill:focus-visible {
|
||||
outline: 2px solid var(--patina);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* 右上角星盘(活的仪器 — 140s 慢旋转)
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
|
||||
.mini-astrolabe {
|
||||
position: absolute;
|
||||
right: clamp(24px, 5vw, 72px);
|
||||
top: clamp(80px, 12vh, 120px);
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
pointer-events: none;
|
||||
}
|
||||
[data-theme="dark"] .mini-astrolabe {
|
||||
opacity: 0.25;
|
||||
}
|
||||
[data-theme="light"] .mini-astrolabe {
|
||||
opacity: 0.18;
|
||||
}
|
||||
.mini-astrolabe svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.astrolabe-rotating {
|
||||
transform-origin: center;
|
||||
animation: slow-rot 140s linear infinite;
|
||||
}
|
||||
@keyframes slow-rot {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.mini-astrolabe {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* LOGO · 铜镜徽(品牌标志,替代朱漆红点)
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
|
||||
.logo-mark {
|
||||
display: block;
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
margin: 0 auto 16px;
|
||||
}
|
||||
.logo-mark .mirror-ring {
|
||||
fill: none;
|
||||
stroke: var(--gilt);
|
||||
stroke-width: 1.4;
|
||||
opacity: 0.65;
|
||||
}
|
||||
.logo-mark .mirror-inner {
|
||||
fill: none;
|
||||
stroke: var(--patina-dim);
|
||||
stroke-width: 0.8;
|
||||
opacity: 0.45;
|
||||
}
|
||||
.logo-mark .char-ring {
|
||||
fill: none;
|
||||
stroke: var(--gilt-dim);
|
||||
stroke-width: 0.5;
|
||||
opacity: 0.35;
|
||||
}
|
||||
.logo-mark .char {
|
||||
font-family: "Songti SC", serif;
|
||||
font-size: 30px;
|
||||
fill: var(--gilt);
|
||||
font-weight: 700;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.logo-mark .dot {
|
||||
fill: var(--gilt);
|
||||
opacity: 0.35;
|
||||
}
|
||||
.logo-mark .fill-surface {
|
||||
fill: var(--patina-wash);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* 主舞台
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
|
||||
.stage {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80px clamp(20px, 4vw, 48px) 48px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* 镜面卡 · 抛光铜镜
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
|
||||
.mirror-card {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
background: var(--face-sheen), var(--surface);
|
||||
border: 1px solid var(--gilt-dim);
|
||||
border-radius: 6px;
|
||||
box-shadow:
|
||||
inset 0 1px 0 var(--face-in-top),
|
||||
inset 0 -1px 0 var(--face-in-bot),
|
||||
var(--shadow);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 扫描光泽 —— 铜镜在光源前微倾时的漫反射滑移 */
|
||||
.mirror-card::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
background-size: 200% 200%;
|
||||
background-position: 200% 200%;
|
||||
animation: mirror-sweep 9s ease-in-out infinite;
|
||||
}
|
||||
[data-theme="dark"] .mirror-card::before {
|
||||
background: linear-gradient(
|
||||
130deg,
|
||||
transparent 0%,
|
||||
transparent 28%,
|
||||
rgba(201, 162, 74, 0.025) 34%,
|
||||
rgba(255, 255, 255, 0.06) 40%,
|
||||
rgba(201, 162, 74, 0.025) 48%,
|
||||
transparent 55%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
[data-theme="light"] .mirror-card::before {
|
||||
background: linear-gradient(
|
||||
130deg,
|
||||
transparent 0%,
|
||||
transparent 24%,
|
||||
rgba(200, 170, 130, 0.06) 30%,
|
||||
rgba(255, 248, 235, 0.18) 36%,
|
||||
rgba(200, 170, 130, 0.1) 44%,
|
||||
transparent 52%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
@keyframes mirror-sweep {
|
||||
0% {
|
||||
background-position: 200% 200%;
|
||||
}
|
||||
40% {
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
60% {
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: -100% -100%;
|
||||
}
|
||||
}
|
||||
|
||||
.card-inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 44px 40px 36px;
|
||||
}
|
||||
|
||||
/* —— 品牌区 —— */
|
||||
.brand {
|
||||
text-align: center;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.brand .wordmark-lg {
|
||||
display: block;
|
||||
font-family: "Songti SC", serif;
|
||||
font-size: clamp(26px, 4vw, 30px);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.14em;
|
||||
color: var(--fg);
|
||||
margin-top: 2px;
|
||||
}
|
||||
.brand .eyebrow {
|
||||
display: block;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.26em;
|
||||
text-transform: uppercase;
|
||||
color: var(--gilt);
|
||||
font-weight: 500;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.brand .divider {
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 1px;
|
||||
background: var(--gilt-dim);
|
||||
margin: 14px auto 0;
|
||||
}
|
||||
|
||||
/* —— 表单 —— */
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
.field-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.field-group label {
|
||||
font-size: 11.5px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--fg-dim);
|
||||
}
|
||||
.field-group .input-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
}
|
||||
[data-theme="dark"] .field-group .input-wrap {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
[data-theme="light"] .field-group .input-wrap {
|
||||
background: rgba(255, 248, 235, 0.5);
|
||||
}
|
||||
|
||||
.field-group .input-wrap:focus-within {
|
||||
border-color: var(--gilt-dim);
|
||||
box-shadow:
|
||||
0 0 0 1px var(--gilt-faint),
|
||||
inset 0 0 0 1px var(--gilt-faint);
|
||||
}
|
||||
.field-group .input-wrap.error {
|
||||
border-color: var(--cinnabar-dim);
|
||||
box-shadow: 0 0 0 1px var(--cinnabar-dim);
|
||||
}
|
||||
.field-group .input-wrap.error:focus-within {
|
||||
border-color: var(--cinnabar);
|
||||
box-shadow: 0 0 0 1px
|
||||
color-mix(in srgb, var(--cinnabar) 55%, transparent);
|
||||
}
|
||||
|
||||
.field-group input {
|
||||
flex: 1;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
color: var(--fg);
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 11px 13px;
|
||||
min-width: 0;
|
||||
}
|
||||
.field-group input::placeholder {
|
||||
color: var(--fg-faint);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.field-group .input-icon {
|
||||
flex: none;
|
||||
width: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--fg-faint);
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.field-group .input-wrap:focus-within .input-icon {
|
||||
opacity: 0.85;
|
||||
color: var(--gilt);
|
||||
}
|
||||
|
||||
/* —— 错误消息 —— */
|
||||
.form-error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 9px 13px;
|
||||
border: 1px solid var(--cinnabar-dim);
|
||||
border-radius: 4px;
|
||||
font-size: 12.5px;
|
||||
color: var(--cinnabar);
|
||||
}
|
||||
[data-theme="dark"] .form-error {
|
||||
background: rgba(184, 57, 46, 0.07);
|
||||
}
|
||||
[data-theme="light"] .form-error {
|
||||
background: rgba(168, 52, 42, 0.06);
|
||||
}
|
||||
.form-error svg {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
/* —— 提交按钮 —— */
|
||||
.submit-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 12px 20px;
|
||||
margin-top: 4px;
|
||||
font-family: "Songti SC", serif;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.1em;
|
||||
color: #ece6d8;
|
||||
background: var(--cinnabar);
|
||||
border: 1px solid var(--cinnabar-dim);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.2s,
|
||||
border-color 0.2s,
|
||||
transform 0.15s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
box-shadow 0.2s;
|
||||
}
|
||||
.submit-btn:hover {
|
||||
background: color-mix(in srgb, var(--cinnabar) 88%, var(--patina));
|
||||
border-color: var(--cinnabar);
|
||||
box-shadow: 0 0 20px var(--cinnabar-dim);
|
||||
}
|
||||
.submit-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
.submit-btn:focus-visible {
|
||||
outline: 2px solid var(--patina);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.submit-btn:disabled {
|
||||
opacity: 0.55;
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.submit-btn .loading-dot {
|
||||
display: inline-block;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
animation: load-pulse 1.4s ease-in-out infinite;
|
||||
}
|
||||
@keyframes load-pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.35;
|
||||
transform: scale(0.85);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* —— 脚注 —— */
|
||||
.card-footer {
|
||||
margin-top: 32px;
|
||||
text-align: center;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--rule);
|
||||
}
|
||||
.card-footer p {
|
||||
font-size: 9.5px;
|
||||
letter-spacing: 0.22em;
|
||||
color: var(--fg-faint);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* 入场动画序列
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
|
||||
.mirror-card {
|
||||
opacity: 0;
|
||||
transform: translateY(18px);
|
||||
animation: card-enter 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
animation-delay: 0.12s;
|
||||
}
|
||||
@keyframes card-enter {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.brand > * {
|
||||
opacity: 0;
|
||||
transform: translateY(6px);
|
||||
animation: stagger-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
.brand .logo-mark {
|
||||
animation-delay: 0.22s;
|
||||
}
|
||||
.brand .wordmark-lg {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
.brand .eyebrow {
|
||||
animation-delay: 0.38s;
|
||||
}
|
||||
.brand .divider {
|
||||
animation-delay: 0.46s;
|
||||
}
|
||||
|
||||
.form > * {
|
||||
opacity: 0;
|
||||
animation: stagger-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
.form .field-group:nth-child(1) {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
.form .field-group:nth-child(2) {
|
||||
animation-delay: 0.58s;
|
||||
}
|
||||
.form .submit-btn {
|
||||
animation-delay: 0.66s;
|
||||
}
|
||||
.card-footer {
|
||||
animation-delay: 0.74s;
|
||||
opacity: 0;
|
||||
animation: stagger-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
|
||||
@keyframes stagger-in {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.mirror-card.departing {
|
||||
animation: card-depart 0.5s cubic-bezier(0.55, 0, 0.7, 0.3) forwards;
|
||||
}
|
||||
@keyframes card-depart {
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px) scale(0.97);
|
||||
filter: blur(2px);
|
||||
}
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════
|
||||
* Reduced Motion
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
.mirror-card {
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
.mirror-card::before {
|
||||
display: none !important;
|
||||
}
|
||||
.brand > *,
|
||||
.form > *,
|
||||
.card-footer {
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
.astrolabe-rotating {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
/* ═══ 统一 sidebar(6 组导航)═══ */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.siming { color: var(--cinnabar); }
|
||||
|
||||
|
||||
@media (max-width: 900px) { .sidebar-wrap { display: none; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-shell">
|
||||
<div class="sidebar-mount" data-active=""></div>
|
||||
<div class="main-area">
|
||||
<!-- 铜面噪点 -->
|
||||
<svg
|
||||
class="grain-overlay"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 200 200"
|
||||
preserveAspectRatio="none"
|
||||
>
|
||||
<filter id="bronze-grain" x="0" y="0" width="100%" height="100%">
|
||||
<feTurbulence
|
||||
type="fractalNoise"
|
||||
baseFrequency="0.75"
|
||||
numOctaves="3"
|
||||
seed="42"
|
||||
result="noise"
|
||||
/>
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.25 0"
|
||||
in="noise"
|
||||
result="colored"
|
||||
/>
|
||||
</filter>
|
||||
<rect width="100%" height="100%" filter="url(#bronze-grain)" />
|
||||
</svg>
|
||||
|
||||
<!-- 同心铭文轨 -->
|
||||
<div class="inscription-rings">
|
||||
<svg viewBox="0 0 400 400" role="img" aria-hidden="true">
|
||||
<circle cx="200" cy="200" r="195" class="ring-ink" />
|
||||
<circle cx="200" cy="200" r="185" class="ring-faint" />
|
||||
<circle cx="200" cy="200" r="170" class="ring-ink" />
|
||||
<circle cx="200" cy="200" r="155" class="ring-faint" />
|
||||
<circle cx="200" cy="200" r="135" class="ring-ink" opacity="0.3" />
|
||||
<circle cx="200" cy="200" r="110" class="ring-faint" opacity="0.2" />
|
||||
<circle cx="200" cy="200" r="80" class="ring-ink" opacity="0.15" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- 星盘装饰(140s 慢旋转) -->
|
||||
<div class="mini-astrolabe" role="img" aria-label="九枢星盘">
|
||||
<svg viewBox="0 0 100 100">
|
||||
<!-- 静止外环 -->
|
||||
<circle
|
||||
cx="50"
|
||||
cy="50"
|
||||
r="48"
|
||||
fill="none"
|
||||
stroke="var(--gilt-dim)"
|
||||
stroke-width="0.6"
|
||||
/>
|
||||
<circle
|
||||
cx="50"
|
||||
cy="50"
|
||||
r="42"
|
||||
fill="none"
|
||||
stroke="var(--gilt-faint)"
|
||||
stroke-width="0.3"
|
||||
stroke-dasharray="1.5 3"
|
||||
/>
|
||||
<!-- 旋转层 -->
|
||||
<g class="astrolabe-rotating">
|
||||
<circle
|
||||
cx="50"
|
||||
cy="50"
|
||||
r="33"
|
||||
fill="none"
|
||||
stroke="var(--patina-dim)"
|
||||
stroke-width="0.5"
|
||||
/>
|
||||
<circle
|
||||
cx="50"
|
||||
cy="50"
|
||||
r="24"
|
||||
fill="none"
|
||||
stroke="var(--gilt-faint)"
|
||||
stroke-width="0.4"
|
||||
stroke-dasharray="1 4"
|
||||
/>
|
||||
<!-- 辐线 -->
|
||||
<g stroke="var(--gilt-faint)" stroke-width="0.3" opacity="0.5">
|
||||
<line x1="50" y1="17" x2="50" y2="4" />
|
||||
<line x1="83" y1="30" x2="93" y2="22" />
|
||||
<line x1="93" y1="62" x2="97" y2="68" />
|
||||
<line x1="74" y1="81" x2="80" y2="89" />
|
||||
<line x1="32" y1="82" x2="26" y2="90" />
|
||||
<line x1="14" y1="56" x2="6" y2="60" />
|
||||
<line x1="18" y1="26" x2="10" y2="20" />
|
||||
</g>
|
||||
<!-- 节点 -->
|
||||
<g fill="var(--gilt)" opacity="0.4">
|
||||
<circle cx="50" cy="12" r="1.8" />
|
||||
<circle cx="82" cy="28" r="1.5" />
|
||||
<circle cx="88" cy="56" r="1.6" />
|
||||
<circle cx="68" cy="80" r="1.4" />
|
||||
<circle cx="34" cy="78" r="1.5" />
|
||||
<circle cx="14" cy="52" r="1.7" />
|
||||
<circle cx="22" cy="24" r="1.4" />
|
||||
</g>
|
||||
</g>
|
||||
<!-- 中心轴 -->
|
||||
<circle
|
||||
cx="50"
|
||||
cy="50"
|
||||
r="3.5"
|
||||
fill="var(--patina-bright)"
|
||||
opacity="0.5"
|
||||
/>
|
||||
<circle cx="50" cy="50" r="1.5" fill="var(--patina)" opacity="0.7" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- 顶栏 -->
|
||||
<header class="topbar">
|
||||
<span class="spacer"></span>
|
||||
<button class="pill" id="themeToggle" type="button" aria-label="切换主题">
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M8 1v2M8 13v2M2 8h2M12 8h2M3.5 3.5l1.4 1.4M11.1 11.1l1.4 1.4M3.5 12.5l1.4-1.4M11.1 4.9l1.4-1.4"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.2"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<circle
|
||||
cx="8"
|
||||
cy="8"
|
||||
r="3"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.2"
|
||||
/>
|
||||
</svg>
|
||||
<span id="themeLabel">夜铜</span>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<!-- 主舞台 -->
|
||||
<main class="stage" role="main">
|
||||
<div class="mirror-card" id="mirrorCard">
|
||||
<div class="card-inner">
|
||||
<!-- 品牌区 · 铜镜徽 + 魂镜 + 铭文 -->
|
||||
<div class="brand">
|
||||
<svg
|
||||
class="logo-mark"
|
||||
viewBox="0 0 68 68"
|
||||
role="img"
|
||||
aria-label="魂镜铜镜徽"
|
||||
>
|
||||
<circle cx="34" cy="34" r="32" class="mirror-ring" />
|
||||
<circle cx="34" cy="34" r="27" class="mirror-inner" />
|
||||
<circle cx="34" cy="34" r="22" class="char-ring" />
|
||||
<circle cx="34" cy="34" r="15" class="char-ring" />
|
||||
<circle
|
||||
cx="34"
|
||||
cy="34"
|
||||
r="8"
|
||||
fill="var(--patina-wash)"
|
||||
opacity="0.3"
|
||||
/>
|
||||
<text x="34" y="40" text-anchor="middle" class="char">魂</text>
|
||||
<circle cx="34" cy="6" r="1.5" class="dot" />
|
||||
<circle cx="34" cy="62" r="1.5" class="dot" />
|
||||
<circle cx="6" cy="34" r="1.5" class="dot" />
|
||||
<circle cx="62" cy="34" r="1.5" class="dot" />
|
||||
</svg>
|
||||
<span class="wordmark-lg">魂鏡</span>
|
||||
<span class="eyebrow">MIRROR OF THE HEART</span>
|
||||
<span class="divider" aria-hidden="true"></span>
|
||||
</div>
|
||||
|
||||
<!-- 表单 -->
|
||||
<form class="form" id="loginForm" novalidate autocomplete="off">
|
||||
<div class="field-group">
|
||||
<label for="username">用户名</label>
|
||||
<div class="input-wrap">
|
||||
<span class="input-icon mono" aria-hidden="true">◎</span>
|
||||
<input
|
||||
id="username"
|
||||
type="text"
|
||||
placeholder="铜镜所照之名"
|
||||
autocomplete="username"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="password">密令</label>
|
||||
<div class="input-wrap">
|
||||
<span class="input-icon mono" aria-hidden="true">◉</span>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="铭文锁钥"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 错误消息 -->
|
||||
<div
|
||||
id="formError"
|
||||
class="form-error"
|
||||
role="alert"
|
||||
style="display: none"
|
||||
>
|
||||
<svg
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle
|
||||
cx="8"
|
||||
cy="8"
|
||||
r="6"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.2"
|
||||
/>
|
||||
<path
|
||||
d="M8 5v3M8 10.5v.5"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.3"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
<span id="errorText"></span>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="submit-btn" id="submitBtn">
|
||||
<span id="submitText">登入</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- 脚注 -->
|
||||
<div class="card-footer">
|
||||
<p class="mono">琉璃镜 · 照见数字生命</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
/* ── 主题切换 ── */
|
||||
const root = document.documentElement;
|
||||
const label = document.getElementById("themeLabel");
|
||||
document.getElementById("themeToggle").addEventListener("click", () => {
|
||||
const n = root.getAttribute("data-theme") === "dark" ? "light" : "dark";
|
||||
root.setAttribute("data-theme", n);
|
||||
label.textContent = n === "dark" ? "夜铜" : "素绢";
|
||||
});
|
||||
|
||||
/* ── 表单交互 ── */
|
||||
const form = document.getElementById("loginForm");
|
||||
const user = document.getElementById("username");
|
||||
const pass = document.getElementById("password");
|
||||
const btn = document.getElementById("submitBtn");
|
||||
const txt = document.getElementById("submitText");
|
||||
const err = document.getElementById("formError");
|
||||
const et = document.getElementById("errorText");
|
||||
|
||||
function showError(m) {
|
||||
et.textContent = m;
|
||||
err.style.display = "flex";
|
||||
document
|
||||
.querySelectorAll(".input-wrap")
|
||||
.forEach((w) => w.classList.add("error"));
|
||||
err.style.animation = "none";
|
||||
err.offsetHeight;
|
||||
err.style.animation = "shake .35s cubic-bezier(.16,1,.3,1)";
|
||||
}
|
||||
function hideError() {
|
||||
err.style.display = "none";
|
||||
document
|
||||
.querySelectorAll(".input-wrap")
|
||||
.forEach((w) => w.classList.remove("error"));
|
||||
}
|
||||
user.addEventListener("input", hideError);
|
||||
pass.addEventListener("input", hideError);
|
||||
|
||||
const ss = document.createElement("style");
|
||||
ss.textContent = `@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-4px)}50%{transform:translateX(4px)}75%{transform:translateX(-2px)}}`;
|
||||
document.head.appendChild(ss);
|
||||
|
||||
form.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
hideError();
|
||||
if (!user.value.trim() || !pass.value) {
|
||||
showError("请填写用户名和密令。");
|
||||
return;
|
||||
}
|
||||
btn.disabled = true;
|
||||
txt.innerHTML = '登入灵台<span class="loading-dot"></span>';
|
||||
await new Promise((r) => setTimeout(r, 1500));
|
||||
const t = parseInt(sessionStorage.getItem("t") || "0");
|
||||
sessionStorage.setItem("t", String(t + 1));
|
||||
if (t === 0) {
|
||||
showError("用户名或密令错误,铜镜不识此名。");
|
||||
btn.disabled = false;
|
||||
txt.textContent = "登入";
|
||||
return;
|
||||
}
|
||||
const c = document.getElementById("mirrorCard");
|
||||
c.classList.add("departing");
|
||||
txt.textContent = "镜开灵现";
|
||||
await new Promise((r) => setTimeout(r, 600));
|
||||
txt.textContent = "登入";
|
||||
btn.disabled = false;
|
||||
c.classList.remove("departing");
|
||||
sessionStorage.removeItem("t");
|
||||
showError("演示:登录成功");
|
||||
err.style.border = "1px solid var(--patina-dim)";
|
||||
err.style.background = "var(--patina-wash)";
|
||||
err.style.color = "var(--patina)";
|
||||
const ic = err.querySelector("svg");
|
||||
if (ic) ic.style.display = "none";
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<script src="_theme.js" defer></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user