diff --git a/.memory/worklog.json b/.memory/worklog.json index cd57859..4a00066 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,12 +1,5 @@ { "entries": [ - { - "files_changed": 4, - "hash": "9f706f7", - "message": "auto-save 2026-05-14 01:34 (+2, ~2)", - "ts": "2026-05-14T01:34:43+08:00", - "type": "commit" - }, { "files_changed": 6, "hash": "d04ec56", @@ -3251,6 +3244,13 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-15 16:49 (~4)", "files_changed": 1 + }, + { + "ts": "2026-05-15T16:55:21+08:00", + "type": "commit", + "message": "auto-save 2026-05-15 16:55 (~1)", + "hash": "0d57081", + "files_changed": 1 } ] } diff --git a/web/app/globals.css b/web/app/globals.css index 8772317..e324658 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -129,10 +129,7 @@ ============================================================ */ .login-page { background: - radial-gradient(circle at 15% 18%, rgba(64, 93, 230, 0.2), transparent 30%), - radial-gradient(circle at 77% 26%, rgba(245, 135, 32, 0.14), transparent 30%), - linear-gradient(115deg, #f6f7f9 0 42%, #16171c 42% 100%); - background-size: auto, auto, auto; + linear-gradient(112deg, #f7f7f4 0 50%, #111214 50% 100%); font-family: Roboto, "Geist", "Geist Fallback", sans-serif; } .login-page::before { @@ -143,8 +140,8 @@ background: linear-gradient(90deg, rgba(40, 40, 40, 0.035) 1px, transparent 1px), linear-gradient(180deg, rgba(40, 40, 40, 0.035) 1px, transparent 1px), - linear-gradient(180deg, rgba(255, 255, 255, 0.34), transparent 34%, rgba(0, 0, 0, 0.22)); - background-size: 48px 48px, 48px 48px, auto; + linear-gradient(180deg, rgba(255, 255, 255, 0.42), transparent 40%, rgba(0, 0, 0, 0.42)); + background-size: 56px 56px, 56px 56px, auto; } .login-page::after { content: ""; @@ -152,15 +149,22 @@ inset: 0; pointer-events: none; background-image: - linear-gradient(114deg, transparent 0 16%, rgba(64, 93, 230, 0.1) 16% 25%, transparent 25% 53%, rgba(245, 135, 32, 0.09) 53% 61%, transparent 61%), - linear-gradient(rgba(255, 255, 255, 0.055) 1px, transparent 1px); - background-size: 100% 6px; - opacity: 0.72; + linear-gradient(114deg, transparent 0 23%, rgba(255, 255, 255, 0.26) 23% 33%, transparent 33% 54%, rgba(214, 179, 106, 0.12) 54% 62%, transparent 62%), + linear-gradient(rgba(255, 255, 255, 0.035) 1px, transparent 1px); + background-size: auto, 100% 6px; + opacity: 0.78; mix-blend-mode: screen; } .login-hero { isolation: isolate; color: #282828; + border: 1px solid rgba(255, 255, 255, 0.8); + background: + linear-gradient(145deg, rgba(255, 255, 255, 0.96), rgba(239, 239, 235, 0.9)), + #f7f7f4; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.92), + 0 28px 70px rgba(15, 15, 15, 0.16); } .login-hero::before { content: ""; @@ -168,24 +172,23 @@ inset: 0; z-index: 0; background: - radial-gradient(circle at 75% 18%, rgba(255, 255, 255, 0.95), transparent 26%), - linear-gradient(160deg, rgba(255, 255, 255, 0.82), rgba(238, 240, 244, 0.72)), - linear-gradient(20deg, transparent 40%, rgba(40, 40, 40, 0.06) 40% 54%, transparent 54%); + linear-gradient(90deg, transparent 0 48%, rgba(255, 255, 255, 0.58) 48% 100%), + linear-gradient(18deg, transparent 0 58%, rgba(214, 179, 106, 0.1) 58% 66%, transparent 66%), + linear-gradient(180deg, rgba(255, 255, 255, 0.55), transparent 48%); } .login-hero::after { content: ""; position: absolute; - left: -18%; - top: 10%; + right: 10%; + bottom: 126px; z-index: 0; - width: 88%; - height: 68%; - border-radius: 999px; - background: linear-gradient(90deg, rgba(255, 255, 255, 0.54), rgba(240, 224, 190, 0.26), transparent); - filter: blur(18px); - transform: rotate(-11deg); + width: 46%; + height: 1px; + background: linear-gradient(90deg, transparent, rgba(40, 40, 40, 0.16), transparent); + box-shadow: 0 28px 52px rgba(40, 40, 40, 0.22); pointer-events: none; } +.login-wordmark, .login-brand-mark { display: inline-flex; align-items: baseline; @@ -193,15 +196,18 @@ color: #282828; letter-spacing: 0; } +.login-wordmark__logo, .login-brand-mark__logo { font-size: 26px; font-weight: 700; line-height: 1; } +.login-wordmark__sub, .login-brand-mark__sub { font-size: 13px; color: rgba(40, 40, 40, 0.54); } +.login-secure-pill, .login-store-pill { display: inline-flex; align-items: center; @@ -214,6 +220,142 @@ font-size: 13px; box-shadow: 0 10px 24px rgba(40, 40, 40, 0.18); } +.login-kicker { + margin: 0 0 16px; + color: rgba(40, 40, 40, 0.48); + font-size: 12px; + font-weight: 700; + letter-spacing: 0.18em; +} +.login-premium-title { + margin: 0; + max-width: 620px; + color: #0d0d0d; + font-size: clamp(44px, 5.5vw, 74px); + font-weight: 600; + line-height: 0.98; + letter-spacing: 0; +} +.login-premium-copy { + margin: 18px 0 0; + max-width: 520px; + color: rgba(40, 40, 40, 0.58); + font-size: 16px; + line-height: 1.7; +} +.login-product-stage { + position: relative; + flex: 1; + min-height: 300px; + margin-top: 26px; +} +.login-product-stage__halo { + position: absolute; + right: 7%; + bottom: 24px; + width: 52%; + height: 42px; + border-radius: 50%; + background: rgba(40, 40, 40, 0.12); + filter: blur(18px); + transform: perspective(500px) rotateX(58deg); +} +.login-product-image { + position: absolute; + right: 1%; + bottom: 0; + width: min(54%, 440px); + max-height: 360px; + object-fit: contain; + filter: + drop-shadow(0 36px 48px rgba(40, 40, 40, 0.22)) + drop-shadow(0 4px 0 rgba(255, 255, 255, 0.75)); +} +.login-product-caption { + position: absolute; + left: 0; + bottom: 50px; + width: min(310px, 46%); + border-top: 1px solid rgba(40, 40, 40, 0.16); + padding-top: 18px; + color: #151515; +} +.login-product-caption span { + display: block; + color: rgba(40, 40, 40, 0.5); + font-size: 13px; +} +.login-product-caption b { + display: block; + margin-top: 5px; + font-size: 28px; + font-weight: 600; + line-height: 1.05; +} +.login-studio-chip { + position: absolute; + display: inline-flex; + align-items: center; + gap: 8px; + min-height: 40px; + border: 1px solid rgba(255, 255, 255, 0.72); + border-radius: 999px; + background: rgba(255, 255, 255, 0.72); + padding: 0 14px; + color: rgba(40, 40, 40, 0.68); + font-size: 13px; + box-shadow: 0 18px 40px rgba(40, 40, 40, 0.1); + backdrop-filter: blur(18px); +} +.login-studio-chip--visual { + right: 3%; + top: 28px; +} +.login-studio-chip--review { + right: 35%; + bottom: 104px; +} +.login-premium-metrics { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1px; + margin-top: 24px; + overflow: hidden; + border: 1px solid rgba(40, 40, 40, 0.1); + border-radius: 8px; + background: rgba(40, 40, 40, 0.1); +} +.login-premium-metric { + min-height: 78px; + background: rgba(255, 255, 255, 0.62); + padding: 14px 16px; +} +.login-premium-metric span { + display: block; + color: rgba(40, 40, 40, 0.48); + font-size: 12px; +} +.login-premium-metric b { + display: block; + margin-top: 8px; + color: #121212; + font-size: 20px; + font-weight: 600; +} +.login-auth-panel { + border: 1px solid rgba(255, 255, 255, 0.1); + background: + linear-gradient(180deg, rgba(22, 23, 28, 0.98), rgba(10, 11, 14, 0.98)), + #101114; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.06), + 0 32px 70px rgba(0, 0, 0, 0.35); +} +.login-auth-icon { + background: linear-gradient(145deg, #2b2b2b, #111); + border: 1px solid rgba(214, 179, 106, 0.42); + box-shadow: 0 18px 42px rgba(0, 0, 0, 0.3), 0 0 0 5px rgba(214, 179, 106, 0.06); +} .login-product-ribbon { display: flex; flex-wrap: wrap; @@ -644,6 +786,46 @@ 100% { translate: 0 -8px; } } @media (max-width: 720px) { + .login-page { + background: linear-gradient(164deg, #f7f7f4 0 48%, #111214 48% 100%); + } + .login-premium-title { + font-size: 34px; + } + .login-premium-copy { + font-size: 14px; + } + .login-product-stage { + min-height: 340px; + } + .login-product-image { + right: -18%; + bottom: 28px; + width: 86%; + max-height: 280px; + } + .login-product-caption { + bottom: 34px; + width: 48%; + } + .login-product-caption b { + font-size: 22px; + } + .login-studio-chip { + font-size: 12px; + } + .login-studio-chip--visual { + right: 0; + top: 12px; + } + .login-studio-chip--review { + left: 0; + right: auto; + bottom: 118px; + } + .login-premium-metrics { + grid-template-columns: 1fr; + } .login-character-stage { min-height: 270px; } diff --git a/web/app/login/page.tsx b/web/app/login/page.tsx index 527f61c..944bafe 100644 --- a/web/app/login/page.tsx +++ b/web/app/login/page.tsx @@ -1,10 +1,11 @@ "use client" import type { FormEvent } from "react" -import { useEffect, useMemo, useState } from "react" +import { useState } from "react" import { AlertCircle, ArrowRight, + BadgeCheck, CheckCircle2, Eye, EyeOff, @@ -13,39 +14,19 @@ import { Sparkles, UserRound, } from "lucide-react" -import { AnimatedLoginCharacters, type LoginCharacterMood } from "@/components/login/animated-login-characters" type LoginStatus = "idle" | "loading" | "success" +const PRODUCT_IMAGE = + "https://www.skg.com/cdn/shop/files/17531530438558.png?v=1754462861&width=900" + export default function LoginPage() { const [username, setUsername] = useState("") const [password, setPassword] = useState("") const [remember, setRemember] = useState(true) const [showPassword, setShowPassword] = useState(false) - const [activeField, setActiveField] = useState<"username" | "password" | null>(null) const [error, setError] = useState("") const [status, setStatus] = useState("idle") - const [eyeOffset, setEyeOffset] = useState({ x: 0, y: 0 }) - - useEffect(() => { - const onPointerMove = (event: PointerEvent) => { - const centerX = window.innerWidth / 2 - const centerY = window.innerHeight / 2 - const nextX = Math.max(-1, Math.min(1, (event.clientX - centerX) / centerX)) - const nextY = Math.max(-1, Math.min(1, (event.clientY - centerY) / centerY)) - setEyeOffset({ x: nextX * 8, y: nextY * 5.5 }) - } - window.addEventListener("pointermove", onPointerMove) - return () => window.removeEventListener("pointermove", onPointerMove) - }, []) - - const mood: LoginCharacterMood = useMemo(() => { - if (status === "success") return "success" - if (error) return "error" - if (showPassword && activeField === "password") return "peek" - if (activeField || username || password) return "typing" - return "idle" - }, [activeField, error, password, showPassword, status, username]) const disabled = status === "loading" || status === "success" @@ -85,63 +66,64 @@ export default function LoginPage() { } return ( -
+
-
-
-
-
-
-
- SKG - 未来健康 -
-
-
- -
-
-

SKG Marketing Studio

-

营销内容工作台

-
-
+
+
+
+
+
+ SKG + 未来健康
-
+
- 生产入口已保护 + Secure Studio
-
-
+
-
+

登录

@@ -151,7 +133,7 @@ export default function LoginPage() {