feat: implement account management and subscription payment center components

This commit is contained in:
2569718930@qq.com
2026-05-24 23:09:13 +08:00
parent 79a652e8a9
commit 45e9c28437
12 changed files with 480 additions and 333 deletions
@@ -64,47 +64,47 @@ export function SubscriptionHelpClient() {
}), [isEn]); }), [isEn]);
return ( return (
<main className="min-h-screen bg-[#070d1d] px-4 py-10 text-slate-100"> <main className="min-h-screen bg-[#f4f7fb] px-4 py-10 text-slate-900">
<div className="mx-auto w-full max-w-4xl"> <div className="mx-auto w-full max-w-4xl">
<Link <Link
href="/account" href="/account"
className="mb-5 inline-flex items-center gap-2 rounded-xl border border-white/10 bg-white/5 px-3 py-2 text-sm text-slate-300 transition hover:bg-white/10" className="mb-5 inline-flex items-center gap-2 rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm font-semibold text-slate-600 shadow-sm transition hover:border-slate-300 hover:text-slate-950"
> >
<ArrowLeft size={15} /> <ArrowLeft size={15} />
{copy.back} {copy.back}
</Link> </Link>
<section className="rounded-3xl border border-blue-400/20 bg-gradient-to-b from-[#162541] to-[#0e1730] p-6 md:p-8"> <section className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm md:p-8">
<div className="mb-5 flex items-center gap-3"> <div className="mb-5 flex items-center gap-3">
<ShieldCheck className="text-cyan-300" size={22} /> <ShieldCheck className="text-blue-700" size={22} />
<h1 className="text-2xl font-bold md:text-3xl">{copy.title}</h1> <h1 className="text-2xl font-bold md:text-3xl">{copy.title}</h1>
</div> </div>
<p className="text-sm text-slate-300 md:text-base">{copy.description}</p> <p className="text-sm text-slate-600 md:text-base">{copy.description}</p>
<div className="mt-6 grid gap-3 md:grid-cols-3"> <div className="mt-6 grid gap-3 md:grid-cols-3">
<div className="rounded-2xl border border-white/10 bg-white/5 p-4"> <div className="rounded-xl border border-slate-200 bg-slate-50 p-4">
<div className="mb-2 flex items-center gap-2 text-cyan-300"> <div className="mb-2 flex items-center gap-2 text-blue-700">
<CreditCard size={16} /> <CreditCard size={16} />
<span className="text-sm font-semibold">{copy.priceLabel}</span> <span className="text-sm font-semibold">{copy.priceLabel}</span>
</div> </div>
<p className="text-xl font-bold">{copy.priceText}</p> <p className="text-xl font-bold">{copy.priceText}</p>
</div> </div>
<div className="rounded-2xl border border-white/10 bg-white/5 p-4"> <div className="rounded-xl border border-slate-200 bg-slate-50 p-4">
<div className="mb-2 flex items-center gap-2 text-emerald-300"> <div className="mb-2 flex items-center gap-2 text-emerald-700">
<Coins size={16} /> <Coins size={16} />
<span className="text-sm font-semibold">{copy.discountLabel}</span> <span className="text-sm font-semibold">{copy.discountLabel}</span>
</div> </div>
<p className="text-xl font-bold">{copy.discountText}</p> <p className="text-xl font-bold">{copy.discountText}</p>
</div> </div>
<div className="rounded-2xl border border-white/10 bg-white/5 p-4"> <div className="rounded-xl border border-slate-200 bg-slate-50 p-4">
<div className="mb-2 flex items-center gap-2 text-violet-300"> <div className="mb-2 flex items-center gap-2 text-indigo-700">
<MessageSquare size={16} /> <MessageSquare size={16} />
<span className="text-sm font-semibold">{copy.communityLabel}</span> <span className="text-sm font-semibold">{copy.communityLabel}</span>
</div> </div>
<Link <Link
href={TELEGRAM_GROUP_URL} href={TELEGRAM_GROUP_URL}
target="_blank" target="_blank"
className="inline-flex min-h-9 items-center text-sm font-semibold text-blue-300 underline decoration-blue-500/50 underline-offset-4" className="inline-flex min-h-9 items-center text-sm font-semibold text-blue-700 underline decoration-blue-500/50 underline-offset-4"
> >
{copy.communityLink} {copy.communityLink}
</Link> </Link>
@@ -112,19 +112,19 @@ export function SubscriptionHelpClient() {
</div> </div>
</section> </section>
<section className="mt-6 rounded-3xl border border-white/10 bg-[#0f162a]/80 p-6 md:p-8"> <section className="mt-6 rounded-2xl border border-slate-200 bg-white p-6 shadow-sm md:p-8">
<h2 className="mb-4 text-lg font-bold">{copy.faqTitle}</h2> <h2 className="mb-4 text-lg font-bold">{copy.faqTitle}</h2>
<div className="space-y-4"> <div className="space-y-4">
{FAQ_ITEMS.map((item) => ( {FAQ_ITEMS.map((item) => (
<article <article
key={item.q_en} key={item.q_en}
className="rounded-2xl border border-white/10 bg-white/[0.03] p-4" className="rounded-xl border border-slate-200 bg-slate-50 p-4"
> >
<h3 className="mb-2 flex items-center gap-2 text-sm font-semibold text-blue-300"> <h3 className="mb-2 flex items-center gap-2 text-sm font-semibold text-blue-700">
<CheckCircle2 size={14} /> <CheckCircle2 size={14} />
{isEn ? item.q_en : item.q_zh} {isEn ? item.q_en : item.q_zh}
</h3> </h3>
<p className="text-sm leading-6 text-slate-300"> <p className="text-sm leading-6 text-slate-600">
{isEn ? item.a_en : item.a_zh} {isEn ? item.a_en : item.a_zh}
</p> </p>
</article> </article>
+102 -104
View File
@@ -2105,27 +2105,25 @@ export function AccountCenter() {
if (loading && !refreshing) { if (loading && !refreshing) {
return ( return (
<div className="flex h-screen w-full items-center justify-center bg-[#0b0f1a]"> <div className="flex h-screen w-full items-center justify-center bg-[#f4f7fb]">
<div className="flex flex-col items-center gap-4"> <div className="flex flex-col items-center gap-4">
<Loader2 className="h-12 w-12 animate-spin text-blue-500" /> <Loader2 className="h-12 w-12 animate-spin text-blue-500" />
<p className="text-slate-400 font-medium">{copy.loadingAccount}</p> <p className="font-medium text-slate-500">{copy.loadingAccount}</p>
</div> </div>
</div> </div>
); );
} }
return ( return (
<div className="min-h-screen w-full bg-[#0b0f1a] text-slate-200 p-4 md:p-8 font-sans relative overflow-hidden flex flex-col items-center"> <div className="relative flex min-h-screen w-full flex-col items-center overflow-hidden bg-[#f4f7fb] p-4 font-sans text-slate-900 md:p-8">
{/* Aurora Shadows */} <div className="absolute inset-x-0 top-0 h-20 border-b border-slate-200 bg-white"></div>
<div className="absolute top-0 right-0 w-[600px] h-[600px] bg-blue-600/10 rounded-full blur-[140px] pointer-events-none"></div>
<div className="absolute bottom-0 left-0 w-[600px] h-[600px] bg-purple-600/10 rounded-full blur-[140px] pointer-events-none"></div>
{/* Header */} {/* Header */}
<header className="w-full max-w-6xl flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8 z-20"> <header className="w-full max-w-6xl flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8 z-20">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<Link <Link
href="/" href="/"
className="p-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-full text-slate-400 hover:text-white transition-all active:scale-90 group" className="group rounded-lg border border-slate-200 bg-white p-2 text-slate-500 shadow-sm transition-all hover:border-slate-300 hover:text-slate-950 active:scale-95"
title={copy.backHome} title={copy.backHome}
aria-label={copy.backHome} aria-label={copy.backHome}
> >
@@ -2135,7 +2133,7 @@ export function AccountCenter() {
/> />
</Link> </Link>
<div> <div>
<h1 className="text-2xl font-bold text-white flex items-center gap-2"> <h1 className="flex items-center gap-2 text-2xl font-bold text-slate-950">
{copy.accountCenter} {copy.accountCenter}
</h1> </h1>
</div> </div>
@@ -2144,7 +2142,7 @@ export function AccountCenter() {
{!showOverlay && canOpenCheckoutOverlay && ( {!showOverlay && canOpenCheckoutOverlay && (
<button <button
onClick={() => setShowOverlay(true)} onClick={() => setShowOverlay(true)}
className="flex items-center gap-2 px-4 py-2 bg-yellow-500/10 hover:bg-yellow-500/20 border border-yellow-500/30 text-yellow-500 rounded-xl text-sm transition-all animate-pulse" className="flex items-center gap-2 rounded-lg border border-amber-300 bg-amber-50 px-4 py-2 text-sm font-semibold text-amber-700 transition-all hover:bg-amber-100"
> >
<Crown size={16} />{" "} <Crown size={16} />{" "}
{showExpiringSoon || showExpiredReminder {showExpiringSoon || showExpiredReminder
@@ -2155,7 +2153,7 @@ export function AccountCenter() {
<button <button
type="button" type="button"
onClick={() => void onRefresh()} onClick={() => void onRefresh()}
className="flex items-center gap-2 px-4 py-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-sm transition-all disabled:opacity-50" className="flex items-center gap-2 rounded-lg border border-slate-200 bg-white px-4 py-2 text-sm font-semibold text-slate-700 shadow-sm transition-all hover:border-slate-300 hover:text-slate-950 disabled:opacity-50"
disabled={refreshing} disabled={refreshing}
> >
{refreshing ? ( {refreshing ? (
@@ -2168,14 +2166,14 @@ export function AccountCenter() {
{isAuthenticated ? ( {isAuthenticated ? (
<button <button
onClick={() => void onSignOut()} onClick={() => void onSignOut()}
className="flex items-center gap-2 px-4 py-2 bg-red-500/10 hover:bg-red-500/20 border border-red-500/20 text-red-400 rounded-xl text-sm transition-all" className="flex items-center gap-2 rounded-lg border border-red-200 bg-red-50 px-4 py-2 text-sm font-semibold text-red-700 transition-all hover:bg-red-100"
> >
<LogOut size={16} /> {copy.signOut} <LogOut size={16} /> {copy.signOut}
</button> </button>
) : ( ) : (
<Link <Link
href="/auth/login?next=%2Faccount" href="/auth/login?next=%2Faccount"
className="flex items-center gap-2 px-4 py-2 bg-blue-500/10 hover:bg-blue-500/20 border border-blue-500/20 text-blue-400 rounded-xl text-sm transition-all" className="flex items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-4 py-2 text-sm font-semibold text-blue-700 transition-all hover:bg-blue-100"
> >
<LogIn size={16} /> {copy.signIn} <LogIn size={16} /> {copy.signIn}
</Link> </Link>
@@ -2185,23 +2183,23 @@ export function AccountCenter() {
<main className="w-full max-w-6xl grid grid-cols-1 lg:grid-cols-12 gap-6 z-10 relative"> <main className="w-full max-w-6xl grid grid-cols-1 lg:grid-cols-12 gap-6 z-10 relative">
{(showExpiringSoon || showExpiredReminder) && ( {(showExpiringSoon || showExpiredReminder) && (
<div className="lg:col-span-12 rounded-[2rem] border border-amber-400/30 bg-amber-500/10 px-6 py-5 shadow-xl"> <div className="lg:col-span-12 rounded-2xl border border-amber-300 bg-amber-50 px-6 py-5 shadow-sm">
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between"> <div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
<div> <div>
<div className="flex items-center gap-2 text-sm font-bold text-amber-300"> <div className="flex items-center gap-2 text-sm font-bold text-amber-700">
<Crown size={16} /> <Crown size={16} />
<span>{subscriptionStatusTitle}</span> <span>{subscriptionStatusTitle}</span>
</div> </div>
<p className="mt-1 text-sm text-amber-50/90"> <p className="mt-1 text-sm text-amber-900">
{subscriptionStatusBody} {subscriptionStatusBody}
</p> </p>
{subscriptionStatusMeta ? ( {subscriptionStatusMeta ? (
<p className="mt-1 text-xs text-amber-200/80"> <p className="mt-1 text-xs text-amber-700">
{subscriptionStatusMeta} {subscriptionStatusMeta}
</p> </p>
) : null} ) : null}
{billing.canRedeem ? ( {billing.canRedeem ? (
<p className="mt-2 text-xs text-emerald-200/90"> <p className="mt-2 text-xs text-emerald-700">
{billing.pointsUsed} $ {billing.pointsUsed} $
{billing.discountAmount.toFixed(2)} {billing.discountAmount.toFixed(2)}
</p> </p>
@@ -2210,7 +2208,7 @@ export function AccountCenter() {
<button <button
type="button" type="button"
onClick={() => setShowOverlay(true)} onClick={() => setShowOverlay(true)}
className="inline-flex items-center justify-center gap-2 rounded-xl border border-amber-300/35 bg-amber-300/12 px-4 py-2 text-sm font-bold text-amber-100 transition-all hover:bg-amber-300/20" className="inline-flex items-center justify-center gap-2 rounded-lg border border-amber-300 bg-white px-4 py-2 text-sm font-bold text-amber-800 transition-all hover:bg-amber-100"
> >
<Crown size={16} /> <Crown size={16} />
{showExpiredReminder ? copy.renewNow : copy.upgradePro} {showExpiredReminder ? copy.renewNow : copy.upgradePro}
@@ -2220,39 +2218,39 @@ export function AccountCenter() {
)} )}
{/* User Card */} {/* User Card */}
<div className="lg:col-span-8 bg-white/5 backdrop-blur-xl border border-white/10 rounded-[2.5rem] p-8 shadow-2xl flex flex-col md:flex-row items-center gap-8"> <div className="flex flex-col items-center gap-8 rounded-2xl border border-slate-200 bg-white p-8 shadow-sm lg:col-span-8 md:flex-row">
<div className="relative"> <div className="relative">
<div className="w-24 h-24 rounded-3xl bg-gradient-to-tr from-blue-600 to-indigo-400 flex items-center justify-center text-3xl font-bold text-white shadow-xl shadow-blue-500/30"> <div className="flex h-24 w-24 items-center justify-center rounded-xl border border-blue-200 bg-blue-600 text-3xl font-bold text-white shadow-sm">
{initials} {initials}
</div> </div>
<div <div
className={`absolute -bottom-2 -right-2 p-1.5 rounded-xl border-4 border-[#0b0f1a] ${isSubscribed ? "bg-yellow-500 text-black" : "bg-slate-700 text-slate-400"}`} className={`absolute -bottom-2 -right-2 rounded-lg border-4 border-white p-1.5 ${isSubscribed ? "bg-amber-400 text-slate-950" : "bg-slate-100 text-slate-400"}`}
> >
<Crown size={16} fill="currentColor" /> <Crown size={16} fill="currentColor" />
</div> </div>
</div> </div>
<div className="flex-grow text-center md:text-left"> <div className="flex-grow text-center md:text-left">
<div className="flex items-center justify-center md:justify-start gap-3 mb-1"> <div className="flex items-center justify-center md:justify-start gap-3 mb-1">
<h2 className="text-3xl font-bold text-white">{displayName}</h2> <h2 className="text-3xl font-bold text-slate-950">{displayName}</h2>
<span <span
className={`px-2 py-0.5 rounded-full text-[10px] font-black uppercase tracking-tighter border ${isSubscribed ? "bg-blue-500/20 border-blue-500/40 text-blue-400" : "bg-slate-700/50 border-white/10 text-slate-500"}`} className={`rounded-full border px-2 py-0.5 text-[10px] font-black uppercase ${isSubscribed ? "border-blue-200 bg-blue-50 text-blue-700" : "border-slate-200 bg-slate-50 text-slate-500"}`}
> >
{isSubscribed {isSubscribed
? copy.proMember ? copy.proMember
: copy.freeTier} : copy.freeTier}
</span> </span>
</div> </div>
<p className="text-slate-500 font-mono text-sm mb-4"> <p className="mb-4 font-mono text-sm text-slate-500">
{email || copy.guestUser} {email || copy.guestUser}
</p> </p>
<div className="flex flex-wrap justify-center md:justify-start gap-4"> <div className="flex flex-wrap justify-center md:justify-start gap-4">
<div className="flex items-center gap-1.5 text-slate-400 text-xs"> <div className="flex items-center gap-1.5 text-xs text-slate-500">
<Hash size={14} />{" "} <Hash size={14} />{" "}
<span className="font-mono"> <span className="font-mono">
{userId ? `${userId.substring(0, 12)}...` : "--"} {userId ? `${userId.substring(0, 12)}...` : "--"}
</span> </span>
</div> </div>
<div className="flex items-center gap-1.5 text-slate-400 text-xs"> <div className="flex items-center gap-1.5 text-xs text-slate-500">
<Clock size={14} />{" "} <Clock size={14} />{" "}
<span> <span>
{copy.joinedAt}: {joinedAt} {copy.joinedAt}: {joinedAt}
@@ -2261,29 +2259,29 @@ export function AccountCenter() {
</div> </div>
</div> </div>
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<div className="px-6 py-4 bg-black/40 rounded-2xl border border-white/5 text-center min-w-[140px]"> <div className="min-w-[140px] rounded-xl border border-slate-200 bg-slate-50 px-6 py-4 text-center">
<p className="text-[10px] text-slate-500 uppercase tracking-widest mb-1"> <p className="mb-1 text-[10px] uppercase text-slate-500">
{copy.totalPoints} {copy.totalPoints}
</p> </p>
<p className="text-xl font-bold text-white flex items-center justify-center gap-2"> <p className="flex items-center justify-center gap-2 text-xl font-bold text-slate-950">
<Coins size={16} className="text-yellow-500" />{" "} <Coins size={16} className="text-yellow-500" />{" "}
{totalPoints.toLocaleString()} {totalPoints.toLocaleString()}
</p> </p>
</div> </div>
<div className="px-6 py-4 bg-emerald-500/10 rounded-2xl border border-emerald-500/20 text-center min-w-[140px]"> <div className="min-w-[140px] rounded-xl border border-emerald-200 bg-emerald-50 px-6 py-4 text-center">
<p className="text-[10px] text-emerald-300 uppercase tracking-widest mb-1 font-bold"> <p className="mb-1 text-[10px] font-bold uppercase text-emerald-700">
{copy.weeklyPoints} {copy.weeklyPoints}
</p> </p>
<p className="text-xl font-bold text-white flex items-center justify-center gap-2"> <p className="flex items-center justify-center gap-2 text-xl font-bold text-slate-950">
<TrendingUp size={16} className="text-emerald-400" />{" "} <TrendingUp size={16} className="text-emerald-400" />{" "}
{weeklyPoints.toLocaleString()} {weeklyPoints.toLocaleString()}
</p> </p>
</div> </div>
<div className="px-6 py-4 bg-blue-500/10 rounded-2xl border border-blue-500/20 text-center min-w-[140px]"> <div className="min-w-[140px] rounded-xl border border-blue-200 bg-blue-50 px-6 py-4 text-center">
<p className="text-[10px] text-blue-400 uppercase tracking-widest mb-1 font-bold"> <p className="mb-1 text-[10px] font-bold uppercase text-blue-700">
{copy.weeklyRank} {copy.weeklyRank}
</p> </p>
<p className="text-xl font-bold text-white flex items-center justify-center gap-2"> <p className="flex items-center justify-center gap-2 text-xl font-bold text-slate-950">
<Trophy size={16} className="text-amber-400" />{" "} <Trophy size={16} className="text-amber-400" />{" "}
{weeklyRank === "--" ? weeklyRank : `#${weeklyRank}`} {weeklyRank === "--" ? weeklyRank : `#${weeklyRank}`}
</p> </p>
@@ -2293,36 +2291,36 @@ export function AccountCenter() {
{/* Weekly Ranking Motivation */} {/* Weekly Ranking Motivation */}
{showSecondarySections ? ( {showSecondarySections ? (
<div className="lg:col-span-4 bg-gradient-to-br from-indigo-600/20 to-purple-600/20 border border-indigo-500/30 rounded-[2.5rem] p-6 flex flex-col justify-between shadow-xl"> <div className="flex flex-col justify-between rounded-2xl border border-slate-200 bg-white p-6 shadow-sm lg:col-span-4">
<div> <div>
<h3 className="text-lg font-bold flex items-center gap-2 text-white mb-6"> <h3 className="mb-6 flex items-center gap-2 text-lg font-bold text-slate-950">
<Sparkles size={20} className="text-yellow-400" />{" "} <Sparkles size={20} className="text-amber-500" />{" "}
{copy.weeklyRewards} {copy.weeklyRewards}
</h3> </h3>
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-center justify-between p-3 bg-white/5 rounded-xl border border-white/5"> <div className="flex items-center justify-between rounded-xl border border-slate-200 bg-slate-50 p-3">
<span className="text-sm flex items-center gap-2"> <span className="text-sm flex items-center gap-2">
<div className="w-5 h-5 bg-yellow-500 rounded text-black font-bold text-[10px] flex items-center justify-center"> <div className="w-5 h-5 bg-yellow-500 rounded text-black font-bold text-[10px] flex items-center justify-center">
1 1
</div>{" "} </div>{" "}
Top 1 Top 1
</span> </span>
<span className="text-xs font-bold text-yellow-500"> <span className="text-xs font-bold text-amber-600">
+200 & 7Pro +200 & 7Pro
</span> </span>
</div> </div>
<div className="flex items-center justify-between p-3 bg-white/5 rounded-xl border border-white/5"> <div className="flex items-center justify-between rounded-xl border border-slate-200 bg-slate-50 p-3">
<span className="text-sm flex items-center gap-2"> <span className="text-sm flex items-center gap-2">
<div className="w-5 h-5 bg-slate-300 rounded text-black font-bold text-[10px] flex items-center justify-center"> <div className="w-5 h-5 bg-slate-300 rounded text-black font-bold text-[10px] flex items-center justify-center">
2 2
</div>{" "} </div>{" "}
Top 2-3 Top 2-3
</span> </span>
<span className="text-xs font-bold text-slate-300"> <span className="text-xs font-bold text-slate-600">
+100 +100
</span> </span>
</div> </div>
<div className="flex items-center justify-between p-3 bg-white/5 rounded-xl border border-white/5"> <div className="flex items-center justify-between rounded-xl border border-slate-200 bg-slate-50 p-3">
<span className="text-sm flex items-center gap-2"> <span className="text-sm flex items-center gap-2">
<div className="w-5 h-5 bg-orange-800 rounded text-white font-bold text-[10px] flex items-center justify-center"> <div className="w-5 h-5 bg-orange-800 rounded text-white font-bold text-[10px] flex items-center justify-center">
4 4
@@ -2335,7 +2333,7 @@ export function AccountCenter() {
</div> </div>
</div> </div>
</div> </div>
<div className="mt-6 flex items-start gap-2 p-3 bg-black/20 rounded-xl"> <div className="mt-6 flex items-start gap-2 rounded-xl border border-slate-200 bg-slate-50 p-3">
<Info size={14} className="text-slate-500 mt-0.5 shrink-0" /> <Info size={14} className="text-slate-500 mt-0.5 shrink-0" />
<p className="text-[10px] text-slate-500 leading-normal italic"> <p className="text-[10px] text-slate-500 leading-normal italic">
+ +
@@ -2344,12 +2342,12 @@ export function AccountCenter() {
</div> </div>
</div> </div>
) : ( ) : (
<div className="lg:col-span-4 rounded-[2.5rem] border border-white/10 bg-white/5 p-6"> <div className="rounded-2xl border border-slate-200 bg-white p-6 lg:col-span-4">
<div className="h-6 w-40 animate-pulse rounded bg-slate-800/80" /> <div className="h-6 w-40 animate-pulse rounded bg-slate-200" />
<div className="mt-4 space-y-2"> <div className="mt-4 space-y-2">
<div className="h-12 animate-pulse rounded-xl bg-slate-800/60" /> <div className="h-12 animate-pulse rounded-xl bg-slate-100" />
<div className="h-12 animate-pulse rounded-xl bg-slate-800/60" /> <div className="h-12 animate-pulse rounded-xl bg-slate-100" />
<div className="h-12 animate-pulse rounded-xl bg-slate-800/60" /> <div className="h-12 animate-pulse rounded-xl bg-slate-100" />
</div> </div>
</div> </div>
)} )}
@@ -2359,8 +2357,8 @@ export function AccountCenter() {
<div <div
className={`grid grid-cols-1 md:grid-cols-2 gap-6 transition-all duration-700 ${canOpenCheckoutOverlay && showOverlay ? "blur-md grayscale-[0.3] opacity-30 select-none pointer-events-none" : ""}`} className={`grid grid-cols-1 md:grid-cols-2 gap-6 transition-all duration-700 ${canOpenCheckoutOverlay && showOverlay ? "blur-md grayscale-[0.3] opacity-30 select-none pointer-events-none" : ""}`}
> >
<section className="bg-white/5 border border-white/10 rounded-[2rem] p-6 space-y-3"> <section className="space-y-3 rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
<h3 className="text-sm font-bold text-blue-400 uppercase tracking-widest mb-4"> <h3 className="mb-4 text-sm font-bold uppercase text-blue-700">
{copy.membershipDetails} {copy.membershipDetails}
</h3> </h3>
<InfoRow <InfoRow
@@ -2392,8 +2390,8 @@ export function AccountCenter() {
isPrimary={isSubscribed} isPrimary={isSubscribed}
/> />
</section> </section>
<section className="bg-white/5 border border-white/10 rounded-[2rem] p-6 space-y-3"> <section className="space-y-3 rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
<h3 className="text-sm font-bold text-indigo-400 uppercase tracking-widest mb-4"> <h3 className="mb-4 text-sm font-bold uppercase text-indigo-700">
{copy.identityStatus} {copy.identityStatus}
</h3> </h3>
<InfoRow <InfoRow
@@ -2418,7 +2416,7 @@ export function AccountCenter() {
value={backend?.authenticated ? copy.passed : copy.restricted} value={backend?.authenticated ? copy.passed : copy.restricted}
/> />
{queuedExtensionSummary ? ( {queuedExtensionSummary ? (
<p className="rounded-2xl border border-cyan-400/20 bg-cyan-500/10 px-4 py-3 text-xs text-cyan-100"> <p className="rounded-xl border border-cyan-200 bg-cyan-50 px-4 py-3 text-xs text-cyan-800">
{queuedExtensionSummary} {queuedExtensionSummary}
</p> </p>
) : null} ) : null}
@@ -2464,16 +2462,16 @@ export function AccountCenter() {
{showSecondarySections ? ( {showSecondarySections ? (
<div className="lg:col-span-12 grid grid-cols-1 md:flex gap-6"> <div className="lg:col-span-12 grid grid-cols-1 md:flex gap-6">
{canAccessPaidTelegramGroup && ( {canAccessPaidTelegramGroup && (
<section className="flex-1 bg-white/5 border border-white/10 rounded-[2rem] p-8 relative overflow-hidden group"> <section className="group relative flex-1 overflow-hidden rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
<Bot <Bot
size={140} size={140}
className="absolute -right-8 -bottom-8 text-white/5 -rotate-12 group-hover:rotate-0 transition-transform duration-1000" className="absolute -right-8 -bottom-8 -rotate-12 text-slate-100 transition-transform duration-1000 group-hover:rotate-0"
/> />
<div className="relative z-10"> <div className="relative z-10">
<h3 className="text-lg font-bold mb-2 flex items-center gap-2 text-blue-400"> <h3 className="mb-2 flex items-center gap-2 text-lg font-bold text-blue-700">
<Bot size={22} /> {copy.telegramBind} <Bot size={22} /> {copy.telegramBind}
</h3> </h3>
<p className="text-slate-400 text-sm mb-6"> <p className="mb-6 text-sm text-slate-500">
{copy.telegramHint} {copy.telegramHint}
</p> </p>
@@ -2485,7 +2483,7 @@ export function AccountCenter() {
href={TELEGRAM_TOPICS_GROUP_URL} href={TELEGRAM_TOPICS_GROUP_URL}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="inline-flex min-h-9 items-center gap-1 rounded-lg border border-emerald-400/30 bg-emerald-500/10 px-3 py-2 text-xs font-semibold text-emerald-200 hover:bg-emerald-500/20" className="inline-flex min-h-9 items-center gap-1 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2 text-xs font-semibold text-emerald-700 hover:bg-emerald-100"
> >
{copy.telegramTopicsGroupLink} {copy.telegramTopicsGroupLink}
<ExternalLink size={12} /> <ExternalLink size={12} />
@@ -2496,7 +2494,7 @@ export function AccountCenter() {
href={TELEGRAM_GROUP_URL} href={TELEGRAM_GROUP_URL}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="inline-flex min-h-9 items-center gap-1 rounded-lg border border-blue-400/30 bg-blue-500/10 px-3 py-2 text-xs font-semibold text-blue-200 hover:bg-blue-500/20" className="inline-flex min-h-9 items-center gap-1 rounded-lg border border-blue-200 bg-blue-50 px-3 py-2 text-xs font-semibold text-blue-700 hover:bg-blue-100"
> >
{copy.telegramGroupLink} {copy.telegramGroupLink}
<ExternalLink size={12} /> <ExternalLink size={12} />
@@ -2504,13 +2502,13 @@ export function AccountCenter() {
) : null} ) : null}
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
<code className="flex-grow bg-black/40 border border-white/10 p-4 rounded-xl font-mono text-xs text-blue-300 overflow-hidden text-ellipsis whitespace-nowrap"> <code className="flex-grow overflow-hidden text-ellipsis whitespace-nowrap rounded-xl border border-slate-200 bg-slate-50 p-4 font-mono text-xs text-blue-700">
{bindCommand} {bindCommand}
</code> </code>
<button <button
onClick={() => void openTelegramBotBindLink()} onClick={() => void openTelegramBotBindLink()}
disabled={telegramBindOpening || !isAuthenticated} disabled={telegramBindOpening || !isAuthenticated}
className="px-4 py-3 bg-cyan-600 hover:bg-cyan-500 disabled:opacity-50 disabled:cursor-not-allowed rounded-xl transition-all shadow-lg text-white text-xs font-bold" className="rounded-xl border border-cyan-700 bg-cyan-600 px-4 py-3 text-xs font-bold text-white shadow-sm transition-all hover:bg-cyan-700 disabled:cursor-not-allowed disabled:opacity-50"
title={copy.telegramBotBindLink} title={copy.telegramBotBindLink}
aria-label={copy.telegramBotBindLink} aria-label={copy.telegramBotBindLink}
> >
@@ -2518,7 +2516,7 @@ export function AccountCenter() {
</button> </button>
<button <button
onClick={() => handleCopy(bindCommand)} onClick={() => handleCopy(bindCommand)}
className="p-4 bg-blue-600 hover:bg-blue-500 rounded-xl transition-all shadow-lg text-white" className="rounded-xl border border-blue-700 bg-blue-600 p-4 text-white shadow-sm transition-all hover:bg-blue-700"
title={copy.copyCommand} title={copy.copyCommand}
aria-label={copy.copyCommand} aria-label={copy.copyCommand}
> >
@@ -2528,7 +2526,7 @@ export function AccountCenter() {
<p className="mt-2 text-[11px] leading-5 text-slate-400"> <p className="mt-2 text-[11px] leading-5 text-slate-400">
{copy.telegramFallbackHint} {copy.telegramFallbackHint}
</p> </p>
<div className="mt-5 rounded-2xl border border-amber-400/25 bg-amber-500/8 px-4 py-3 text-xs leading-6 text-amber-100/90"> <div className="mt-5 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs leading-6 text-amber-800">
{copy.paymentManualSupport} {copy.paymentManualSupport}
</div> </div>
</div> </div>
@@ -2536,27 +2534,27 @@ export function AccountCenter() {
)} )}
{/* Payment Details / Wallet Management */} {/* Payment Details / Wallet Management */}
<section className={`bg-white/5 border border-white/10 rounded-[2rem] p-8 flex flex-col justify-between ${ <section className={`flex flex-col justify-between rounded-2xl border border-slate-200 bg-white p-8 shadow-sm ${
canAccessPaidTelegramGroup ? "w-full md:w-96" : "w-full" canAccessPaidTelegramGroup ? "w-full md:w-96" : "w-full"
}`}> }`}>
<div> <div>
<h3 className="text-blue-400 text-sm font-bold uppercase tracking-widest mb-6 flex items-center gap-2"> <h3 className="mb-6 flex items-center gap-2 text-sm font-bold uppercase text-blue-700">
<Wallet size={18} /> {copy.paymentMgmt} <Wallet size={18} /> {copy.paymentMgmt}
</h3> </h3>
{paymentError ? ( {paymentError ? (
<div className="mb-4 rounded-xl border border-red-400/40 bg-red-500/10 px-3 py-2 text-[11px] text-red-200"> <div className="mb-4 rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-[11px] text-red-700">
{paymentError} {paymentError}
</div> </div>
) : null} ) : null}
{!paymentError && paymentInfo ? ( {!paymentError && paymentInfo ? (
<div className="mb-4 rounded-xl border border-cyan-400/35 bg-cyan-500/10 px-3 py-2 text-[11px] text-cyan-200"> <div className="mb-4 rounded-xl border border-cyan-200 bg-cyan-50 px-3 py-2 text-[11px] text-cyan-800">
{paymentInfo} {paymentInfo}
{telegramBindUrl ? ( {telegramBindUrl ? (
<a <a
href={telegramBindUrl} href={telegramBindUrl}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="block mt-1 underline text-cyan-400 hover:text-cyan-300 break-all" className="mt-1 block break-all text-cyan-700 underline hover:text-cyan-900"
> >
{telegramBindUrl} {telegramBindUrl}
</a> </a>
@@ -2564,7 +2562,7 @@ export function AccountCenter() {
</div> </div>
) : null} ) : null}
{!paymentHostAllowed ? ( {!paymentHostAllowed ? (
<div className="mb-4 rounded-xl border border-amber-400/40 bg-amber-500/10 px-3 py-2 text-[11px] text-amber-200"> <div className="mb-4 rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-[11px] text-amber-800">
{copy.paymentHostBlocked.replace( {copy.paymentHostBlocked.replace(
"{host}", "{host}",
allowedPaymentHosts[0] || "polyweather-pro.vercel.app", allowedPaymentHosts[0] || "polyweather-pro.vercel.app",
@@ -2604,7 +2602,7 @@ export function AccountCenter() {
</div> </div>
{availableTokenList.length > 0 && ( {availableTokenList.length > 0 && (
<div className="mb-5"> <div className="mb-5">
<p className="text-[11px] uppercase tracking-widest text-slate-500 mb-2"> <p className="mb-2 text-[11px] uppercase text-slate-500">
{copy.paymentToken} {copy.paymentToken}
</p> </p>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">
@@ -2622,8 +2620,8 @@ export function AccountCenter() {
disabled={paymentBusy} disabled={paymentBusy}
className={`rounded-xl border px-3 py-2 text-left transition-all ${ className={`rounded-xl border px-3 py-2 text-left transition-all ${
active active
? "bg-blue-500/15 border-blue-500/40 text-white" ? "border-blue-300 bg-blue-50 text-blue-900"
: "bg-white/5 border-white/10 text-slate-400 hover:bg-white/10" : "border-slate-200 bg-white text-slate-600 hover:bg-slate-50"
}`} }`}
> >
<div className="text-xs font-bold"> <div className="text-xs font-bold">
@@ -2640,18 +2638,18 @@ export function AccountCenter() {
)} )}
{/* 支付方式选择 Tabs */} {/* 支付方式选择 Tabs */}
<div className="mt-6 border-t border-white/10 pt-6"> <div className="mt-6 border-t border-slate-200 pt-6">
<p className="text-[10px] uppercase tracking-widest text-slate-500 mb-3 font-semibold"> <p className="mb-3 text-[10px] font-semibold uppercase text-slate-500">
{copy.paymentMethodLabel} {copy.paymentMethodLabel}
</p> </p>
<div className="grid grid-cols-2 gap-2 p-1 rounded-xl bg-black/40 border border-white/5 mb-5"> <div className="mb-5 grid grid-cols-2 gap-2 rounded-xl border border-slate-200 bg-slate-100 p-1">
<button <button
type="button" type="button"
onClick={() => setPaymentMethodTab("wallet")} onClick={() => setPaymentMethodTab("wallet")}
className={`py-2 rounded-lg text-xs font-bold transition-all flex items-center justify-center gap-1.5 ${ className={`py-2 rounded-lg text-xs font-bold transition-all flex items-center justify-center gap-1.5 ${
paymentMethodTab === "wallet" paymentMethodTab === "wallet"
? "bg-blue-600/95 text-white shadow-lg" ? "bg-white text-blue-700 shadow-sm"
: "text-slate-400 hover:text-slate-200 hover:bg-white/5" : "text-slate-500 hover:bg-white/70 hover:text-slate-900"
}`} }`}
> >
<Wallet size={12} /> <Wallet size={12} />
@@ -2662,8 +2660,8 @@ export function AccountCenter() {
onClick={() => setPaymentMethodTab("manual")} onClick={() => setPaymentMethodTab("manual")}
className={`py-2 rounded-lg text-xs font-bold transition-all flex items-center justify-center gap-1.5 ${ className={`py-2 rounded-lg text-xs font-bold transition-all flex items-center justify-center gap-1.5 ${
paymentMethodTab === "manual" paymentMethodTab === "manual"
? "bg-emerald-600/95 text-white shadow-lg" ? "bg-white text-emerald-700 shadow-sm"
: "text-slate-400 hover:text-slate-200 hover:bg-white/5" : "text-slate-500 hover:bg-white/70 hover:text-slate-900"
}`} }`}
> >
<ExternalLink size={12} /> <ExternalLink size={12} />
@@ -2676,7 +2674,7 @@ export function AccountCenter() {
<p className="text-[11px] leading-relaxed text-slate-400"> <p className="text-[11px] leading-relaxed text-slate-400">
{copy.paymentWalletDesc} {copy.paymentWalletDesc}
</p> </p>
<div className="rounded-xl border border-amber-400/20 bg-amber-500/10 p-3 text-[11px] leading-relaxed text-amber-100"> <div className="rounded-xl border border-amber-200 bg-amber-50 p-3 text-[11px] leading-relaxed text-amber-800">
{copy.paymentGasWarning} {copy.paymentGasWarning}
</div> </div>
@@ -2687,8 +2685,8 @@ export function AccountCenter() {
key={w.address} key={w.address}
className={`p-3 rounded-xl border transition-all ${ className={`p-3 rounded-xl border transition-all ${
selectedWallet === w.address selectedWallet === w.address
? "bg-blue-500/10 border-blue-500/30 text-white" ? "border-blue-300 bg-blue-50 text-blue-900"
: "bg-white/5 border-white/5 text-slate-400" : "border-slate-200 bg-white text-slate-600"
}`} }`}
> >
<div className="flex items-center justify-between mb-1"> <div className="flex items-center justify-between mb-1">
@@ -2707,7 +2705,7 @@ export function AccountCenter() {
type="button" type="button"
onClick={() => void handleUnbindWallet(w.address)} onClick={() => void handleUnbindWallet(w.address)}
disabled={paymentBusy} disabled={paymentBusy}
className="inline-flex items-center gap-1 rounded-md border border-red-500/30 bg-red-500/10 px-2 py-1 text-[10px] font-semibold text-red-300 transition-all hover:bg-red-500/20 disabled:opacity-50" className="inline-flex items-center gap-1 rounded-md border border-red-200 bg-red-50 px-2 py-1 text-[10px] font-semibold text-red-700 transition-all hover:bg-red-100 disabled:opacity-50"
> >
<Minus size={12} /> <Minus size={12} />
{copy.unbind} {copy.unbind}
@@ -2717,7 +2715,7 @@ export function AccountCenter() {
))} ))}
</div> </div>
) : ( ) : (
<div className="p-4 rounded-xl border border-white/5 bg-white/5 text-center"> <div className="rounded-xl border border-slate-200 bg-slate-50 p-4 text-center">
<p className="text-xs text-slate-400 italic"> <p className="text-xs text-slate-400 italic">
{copy.noWallet} {copy.noWallet}
</p> </p>
@@ -2727,7 +2725,7 @@ export function AccountCenter() {
<div className="grid grid-cols-1 gap-2 pt-2"> <div className="grid grid-cols-1 gap-2 pt-2">
{injectedProviderOptions.length > 1 && ( {injectedProviderOptions.length > 1 && (
<label className="mb-2 block"> <label className="mb-2 block">
<span className="mb-2 block text-[11px] uppercase tracking-widest text-slate-500"> <span className="mb-2 block text-[11px] uppercase text-slate-500">
{copy.walletExtensionDetected} {copy.walletExtensionDetected}
</span> </span>
<select <select
@@ -2736,13 +2734,13 @@ export function AccountCenter() {
setSelectedInjectedProviderKey(event.target.value) setSelectedInjectedProviderKey(event.target.value)
} }
disabled={paymentBusy} disabled={paymentBusy}
className="w-full rounded-xl border border-white/10 bg-white/5 px-3 py-3 text-xs text-slate-200 outline-none transition-all hover:bg-white/10 disabled:opacity-60" className="w-full rounded-xl border border-slate-300 bg-white px-3 py-3 text-xs text-slate-900 outline-none transition-all hover:bg-slate-50 disabled:opacity-60"
> >
{injectedProviderOptions.map((option) => ( {injectedProviderOptions.map((option) => (
<option <option
key={option.key} key={option.key}
value={option.key} value={option.key}
className="bg-slate-900 text-slate-200" className="bg-white text-slate-900"
> >
{option.label} {option.label}
</option> </option>
@@ -2756,7 +2754,7 @@ export function AccountCenter() {
void connectAndBindWallet("auto"); void connectAndBindWallet("auto");
}} }}
disabled={paymentBusy || !isAuthenticated} disabled={paymentBusy || !isAuthenticated}
className="w-full py-3 border border-white/10 bg-white/5 hover:bg-white/10 rounded-xl text-xs font-bold text-slate-300 transition-all flex items-center justify-center gap-2 disabled:opacity-60" className="flex w-full items-center justify-center gap-2 rounded-xl border border-slate-300 bg-white py-3 text-xs font-bold text-slate-700 transition-all hover:bg-slate-50 disabled:opacity-60"
> >
<PlusIcon className="w-4 h-4" /> {copy.bindExt} <PlusIcon className="w-4 h-4" /> {copy.bindExt}
</button> </button>
@@ -2768,7 +2766,7 @@ export function AccountCenter() {
disabled={ disabled={
paymentBusy || !isAuthenticated || !walletConnectEnabled paymentBusy || !isAuthenticated || !walletConnectEnabled
} }
className="w-full py-3 border border-cyan-400/30 bg-cyan-500/10 hover:bg-cyan-500/20 rounded-xl text-xs font-bold text-cyan-300 transition-all flex items-center justify-center gap-2 disabled:opacity-60" className="flex w-full items-center justify-center gap-2 rounded-xl border border-cyan-200 bg-cyan-50 py-3 text-xs font-bold text-cyan-700 transition-all hover:bg-cyan-100 disabled:opacity-60"
> >
<CreditCard className="w-4 h-4" /> {copy.bindQr} <CreditCard className="w-4 h-4" /> {copy.bindQr}
</button> </button>
@@ -2788,13 +2786,13 @@ export function AccountCenter() {
{copy.paymentManualDesc} {copy.paymentManualDesc}
</p> </p>
<div className="rounded-2xl border border-emerald-400/25 bg-emerald-500/8 p-4"> <div className="rounded-2xl border border-emerald-200 bg-emerald-50 p-4">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<div> <div>
<p className="text-xs font-bold text-emerald-200"> <p className="text-xs font-bold text-emerald-800">
{copy.paymentManualTitle} {copy.paymentManualTitle}
</p> </p>
<p className="mt-1 text-[11px] leading-relaxed text-emerald-100/75"> <p className="mt-1 text-[11px] leading-relaxed text-emerald-700">
{copy.paymentManualHint} {copy.paymentManualHint}
</p> </p>
</div> </div>
@@ -2802,18 +2800,18 @@ export function AccountCenter() {
type="button" type="button"
onClick={() => void createManualPaymentIntent()} onClick={() => void createManualPaymentIntent()}
disabled={paymentBusy || !isAuthenticated} disabled={paymentBusy || !isAuthenticated}
className="w-full rounded-xl border border-emerald-400/35 bg-emerald-500/15 py-2.5 text-xs font-bold text-emerald-100 transition-all hover:bg-emerald-500/25 disabled:opacity-50" className="w-full rounded-xl border border-emerald-700 bg-emerald-600 py-2.5 text-xs font-bold text-white transition-all hover:bg-emerald-700 disabled:opacity-50"
> >
{copy.paymentManualCreate} {copy.paymentManualCreate}
</button> </button>
</div> </div>
{manualPayment ? ( {manualPayment ? (
<div className="mt-4 space-y-3 rounded-xl border border-white/10 bg-black/25 p-3"> <div className="mt-4 space-y-3 rounded-xl border border-emerald-200 bg-white p-3">
<div> <div>
<p className="text-[10px] uppercase tracking-widest text-slate-500"> <p className="text-[10px] uppercase tracking-widest text-slate-500">
{copy.paymentAmount} {copy.paymentAmount}
</p> </p>
<p className="font-mono text-sm font-bold text-white"> <p className="font-mono text-sm font-bold text-slate-950">
{manualPayment.amount_usdc}{" "} {manualPayment.amount_usdc}{" "}
{manualPayment.token_symbol || selectedTokenLabel} {manualPayment.token_symbol || selectedTokenLabel}
</p> </p>
@@ -2823,7 +2821,7 @@ export function AccountCenter() {
{copy.paymentReceiverLabel} {copy.paymentReceiverLabel}
</p> </p>
<div className="mt-1 flex gap-2"> <div className="mt-1 flex gap-2">
<code className="min-w-0 flex-1 break-all whitespace-normal rounded-lg bg-black/40 px-2 py-2 font-mono text-[11px] text-blue-200"> <code className="min-w-0 flex-1 break-all whitespace-normal rounded-lg border border-blue-200 bg-blue-50 px-2 py-2 font-mono text-[11px] text-blue-800">
{manualPayment.receiver_address} {manualPayment.receiver_address}
</code> </code>
<button <button
@@ -2831,7 +2829,7 @@ export function AccountCenter() {
onClick={() => onClick={() =>
handleCopy(manualPayment.receiver_address) handleCopy(manualPayment.receiver_address)
} }
className="rounded-lg bg-blue-600 px-2 text-xs font-bold text-white transition-colors hover:bg-blue-500" className="rounded-lg border border-blue-700 bg-blue-600 px-2 text-xs font-bold text-white transition-colors hover:bg-blue-700"
> >
{copy.paymentCopyAddress} {copy.paymentCopyAddress}
</button> </button>
@@ -2853,19 +2851,19 @@ export function AccountCenter() {
); );
}} }}
placeholder="0x..." placeholder="0x..."
className="mt-1 w-full rounded-xl border border-white/10 bg-black/30 px-3 py-2 font-mono text-xs text-slate-100 outline-none focus:border-emerald-400/50" className="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 font-mono text-xs text-slate-950 outline-none focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20"
/> />
{txValidation.loading ? ( {txValidation.loading ? (
<p className="mt-1 text-[10px] text-slate-500"> <p className="mt-1 text-[10px] text-slate-500">
... ...
</p> </p>
) : txValidation.checked && txValidation.valid ? ( ) : txValidation.checked && txValidation.valid ? (
<p className="mt-1 text-[10px] text-emerald-400"> <p className="mt-1 text-[10px] text-emerald-700">
</p> </p>
) : txValidation.checked && ) : txValidation.checked &&
txValidation.valid === false ? ( txValidation.valid === false ? (
<p className="mt-1 text-[10px] text-red-400"> <p className="mt-1 text-[10px] text-red-700">
{txValidation.reason === {txValidation.reason ===
"tx_not_mined" "tx_not_mined"
? "交易未上链,请等待" ? "交易未上链,请等待"
@@ -2890,7 +2888,7 @@ export function AccountCenter() {
paymentBusy || paymentBusy ||
(txValidation.checked && !txValidation.valid) (txValidation.checked && !txValidation.valid)
} }
className="w-full rounded-xl bg-emerald-600 px-3 py-2 text-xs font-bold text-white transition-all hover:bg-emerald-500 disabled:opacity-50" className="w-full rounded-xl border border-emerald-700 bg-emerald-600 px-3 py-2 text-xs font-bold text-white transition-all hover:bg-emerald-700 disabled:opacity-50"
> >
{copy.paymentManualSubmit} {copy.paymentManualSubmit}
</button> </button>
@@ -2905,8 +2903,8 @@ export function AccountCenter() {
</div> </div>
) : ( ) : (
<div className="lg:col-span-12 grid grid-cols-1 gap-6 md:grid-cols-3"> <div className="lg:col-span-12 grid grid-cols-1 gap-6 md:grid-cols-3">
<div className="md:col-span-2 h-48 animate-pulse rounded-[2rem] border border-white/10 bg-white/5" /> <div className="h-48 animate-pulse rounded-2xl border border-slate-200 bg-white md:col-span-2" />
<div className="h-48 animate-pulse rounded-[2rem] border border-white/10 bg-white/5" /> <div className="h-48 animate-pulse rounded-2xl border border-slate-200 bg-white" />
</div> </div>
)} )}
</main> </main>
+5 -5
View File
@@ -10,14 +10,14 @@ const AccountCenter = dynamic(
{ {
ssr: false, ssr: false,
loading: () => ( loading: () => (
<div className="min-h-screen bg-slate-950 text-slate-300"> <div className="min-h-screen bg-[#f4f7fb] text-slate-700">
<div className="mx-auto w-full max-w-6xl px-6 py-10"> <div className="mx-auto w-full max-w-6xl px-6 py-10">
<div className="h-7 w-48 animate-pulse rounded bg-slate-800/80" /> <div className="h-7 w-48 animate-pulse rounded bg-slate-200" />
<div className="mt-6 grid grid-cols-1 gap-4 md:grid-cols-3"> <div className="mt-6 grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="h-32 animate-pulse rounded-3xl bg-slate-800/70 md:col-span-2" /> <div className="h-32 animate-pulse rounded-2xl border border-slate-200 bg-white md:col-span-2" />
<div className="h-32 animate-pulse rounded-3xl bg-slate-800/70" /> <div className="h-32 animate-pulse rounded-2xl border border-slate-200 bg-white" />
</div> </div>
<div className="mt-6 h-72 animate-pulse rounded-3xl bg-slate-800/60" /> <div className="mt-6 h-72 animate-pulse rounded-2xl border border-slate-200 bg-white" />
</div> </div>
</div> </div>
), ),
@@ -13,17 +13,17 @@ export const InfoRow = ({
value, value,
isPrimary = false, isPrimary = false,
}: InfoRowProps) => ( }: InfoRowProps) => (
<div className="flex min-w-0 flex-col gap-3 p-4 bg-white/5 rounded-2xl border border-white/5 hover:bg-white/10 transition-all group sm:flex-row sm:items-center sm:justify-between"> <div className="flex min-w-0 flex-col gap-3 rounded-xl border border-slate-200 bg-white p-4 transition-all hover:border-slate-300 hover:bg-slate-50 group sm:flex-row sm:items-center sm:justify-between">
<div className="flex min-w-0 items-center gap-3"> <div className="flex min-w-0 items-center gap-3">
<div className="shrink-0 p-2 bg-slate-800 rounded-lg text-slate-400 group-hover:text-blue-400 transition-colors"> <div className="shrink-0 rounded-lg border border-slate-200 bg-slate-50 p-2 text-slate-500 transition-colors group-hover:text-blue-600">
{Icon && <Icon size={18} />} {Icon && <Icon size={18} />}
</div> </div>
<span className="min-w-0 text-slate-400 text-sm font-medium leading-5"> <span className="min-w-0 text-sm font-medium leading-5 text-slate-500">
{label} {label}
</span> </span>
</div> </div>
<span <span
className={`min-w-0 break-all text-left text-sm font-semibold font-mono sm:text-right ${isPrimary ? "text-blue-400" : "text-slate-200"}`} className={`min-w-0 break-all text-left font-mono text-sm font-semibold sm:text-right ${isPrimary ? "text-blue-700" : "text-slate-900"}`}
> >
{value} {value}
</span> </span>
+23 -24
View File
@@ -217,53 +217,52 @@ export function LoginClient({ nextPath }: LoginClientProps) {
const isLogin = mode === "login"; const isLogin = mode === "login";
return ( return (
<div className="relative flex min-h-screen w-full items-center justify-center overflow-hidden bg-[#0f172a] font-sans"> <div className="relative flex min-h-screen w-full flex-col items-center justify-center overflow-hidden bg-[#f4f7fb] px-4 py-8 font-sans text-slate-900">
<div className="absolute left-[-10%] top-[-10%] h-[40vw] w-[40vw] animate-pulse rounded-full bg-blue-600/20 blur-[120px]" /> <div className="absolute inset-x-0 top-0 h-24 border-b border-slate-200 bg-white" />
<div className="absolute bottom-[-10%] right-[-10%] h-[30vw] w-[30vw] rounded-full bg-indigo-500/20 blur-[100px]" />
<div className="relative mx-4 w-full max-w-[420px] rounded-[2rem] border border-white/10 bg-white/5 p-8 shadow-2xl backdrop-blur-xl"> <div className="relative w-full max-w-[420px] rounded-2xl border border-slate-200 bg-white p-8 shadow-[0_24px_70px_rgba(15,23,42,0.12)]">
<Link <Link
href="/" href="/"
className="group absolute left-6 top-6 rounded-full border border-white/10 bg-white/5 p-2 text-slate-400 transition-all hover:bg-white/10 hover:text-white active:scale-90" className="group absolute left-6 top-6 rounded-lg border border-slate-200 bg-slate-50 p-2 text-slate-500 transition-all hover:border-slate-300 hover:bg-white hover:text-slate-900 active:scale-95"
title={copy.backHome} title={copy.backHome}
aria-label={copy.backHome} aria-label={copy.backHome}
> >
<ChevronLeft className="h-5 w-5 transition-transform group-hover:-translate-x-0.5" /> <ChevronLeft className="h-5 w-5 transition-transform group-hover:-translate-x-0.5" />
</Link> </Link>
<div className="mb-8 flex flex-col items-center"> <div className="mb-8 flex flex-col items-center">
<div className="mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-tr from-blue-500 to-indigo-400 shadow-lg shadow-blue-500/20"> <div className="mb-4 flex h-16 w-16 items-center justify-center rounded-xl border border-blue-200 bg-blue-600 shadow-sm">
<Cloud className="h-10 w-10 text-white" /> <Cloud className="h-10 w-10 text-white" />
</div> </div>
<h1 className="text-3xl font-bold tracking-tight text-white">PolyWeather</h1> <h1 className="text-3xl font-bold tracking-tight text-slate-950">PolyWeather</h1>
<p className="mt-2 text-sm text-slate-400">{copy.subtitle}</p> <p className="mt-2 text-center text-sm text-slate-500">{copy.subtitle}</p>
</div> </div>
<button <button
type="button" type="button"
onClick={() => void onGoogleSignIn()} onClick={() => void onGoogleSignIn()}
disabled={loading} disabled={loading}
className="mb-6 flex w-full items-center justify-center rounded-xl bg-white px-4 py-3.5 font-semibold text-slate-900 shadow-lg transition-all duration-200 hover:bg-slate-100 active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-70" className="mb-6 flex w-full items-center justify-center rounded-lg border border-slate-300 bg-white px-4 py-3.5 font-semibold text-slate-900 shadow-sm transition-all duration-200 hover:border-slate-400 hover:bg-slate-50 active:scale-[0.99] disabled:cursor-not-allowed disabled:opacity-70"
> >
<Chrome className="mr-3 h-5 w-5" /> <Chrome className="mr-3 h-5 w-5" />
{copy.googleOneClick} {copy.googleOneClick}
</button> </button>
<div className="my-6 flex items-center"> <div className="my-6 flex items-center">
<div className="h-[1px] flex-grow bg-white/10" /> <div className="h-px flex-grow bg-slate-200" />
<span className="px-4 text-xs font-medium uppercase tracking-widest text-slate-500"> <span className="px-4 text-xs font-semibold uppercase text-slate-500">
{copy.orEmail} {copy.orEmail}
</span> </span>
<div className="h-[1px] flex-grow bg-white/10" /> <div className="h-px flex-grow bg-slate-200" />
</div> </div>
<div className="mb-6 flex rounded-xl bg-black/20 p-1"> <div className="mb-6 flex rounded-lg border border-slate-200 bg-slate-100 p-1">
<button <button
type="button" type="button"
onClick={() => setMode("login")} onClick={() => setMode("login")}
className={`flex-1 rounded-lg py-2 text-sm font-medium transition-all ${ className={`flex-1 rounded-lg py-2 text-sm font-medium transition-all ${
isLogin isLogin
? "bg-blue-600 text-white shadow-md" ? "bg-white text-slate-950 shadow-sm"
: "text-slate-400 hover:text-slate-200" : "text-slate-500 hover:text-slate-900"
}`} }`}
> >
{copy.login} {copy.login}
@@ -273,8 +272,8 @@ export function LoginClient({ nextPath }: LoginClientProps) {
onClick={() => setMode("signup")} onClick={() => setMode("signup")}
className={`flex-1 rounded-lg py-2 text-sm font-medium transition-all ${ className={`flex-1 rounded-lg py-2 text-sm font-medium transition-all ${
!isLogin !isLogin
? "bg-blue-600 text-white shadow-md" ? "bg-white text-slate-950 shadow-sm"
: "text-slate-400 hover:text-slate-200" : "text-slate-500 hover:text-slate-900"
}`} }`}
> >
{copy.signup} {copy.signup}
@@ -290,7 +289,7 @@ export function LoginClient({ nextPath }: LoginClientProps) {
value={email} value={email}
onChange={(event) => setEmail(event.target.value)} onChange={(event) => setEmail(event.target.value)}
placeholder="you@example.com" placeholder="you@example.com"
className="w-full rounded-xl border border-white/10 bg-white/5 py-3.5 pl-12 pr-4 text-white placeholder:text-slate-600 transition-all focus:border-blue-500/50 focus:outline-none focus:ring-2 focus:ring-blue-500/50" className="w-full rounded-lg border border-slate-300 bg-white py-3.5 pl-12 pr-4 text-slate-950 placeholder:text-slate-400 transition-all focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
/> />
</div> </div>
<div className="relative"> <div className="relative">
@@ -306,7 +305,7 @@ export function LoginClient({ nextPath }: LoginClientProps) {
? copy.passwordLoginPlaceholder ? copy.passwordLoginPlaceholder
: copy.passwordSignupPlaceholder : copy.passwordSignupPlaceholder
} }
className="w-full rounded-xl border border-white/10 bg-white/5 py-3.5 pl-12 pr-4 text-white placeholder:text-slate-600 transition-all focus:border-blue-500/50 focus:outline-none focus:ring-2 focus:ring-blue-500/50" className="w-full rounded-lg border border-slate-300 bg-white py-3.5 pl-12 pr-4 text-slate-950 placeholder:text-slate-400 transition-all focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
/> />
</div> </div>
@@ -316,7 +315,7 @@ export function LoginClient({ nextPath }: LoginClientProps) {
type="button" type="button"
onClick={() => void onResetPassword()} onClick={() => void onResetPassword()}
disabled={loading} disabled={loading}
className="text-xs text-slate-500 underline-offset-2 transition-all hover:text-blue-400 hover:underline disabled:opacity-50" className="text-xs text-slate-500 underline-offset-2 transition-all hover:text-blue-600 hover:underline disabled:opacity-50"
> >
{copy.reset} {copy.reset}
</button> </button>
@@ -326,15 +325,15 @@ export function LoginClient({ nextPath }: LoginClientProps) {
<button <button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="group mt-8 flex w-full items-center justify-center rounded-xl bg-gradient-to-r from-blue-600 to-indigo-600 py-3.5 font-bold text-white shadow-xl shadow-blue-600/20 transition-all hover:from-blue-500 hover:to-indigo-500 active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-70" className="group mt-8 flex w-full items-center justify-center rounded-lg border border-blue-700 bg-blue-600 py-3.5 font-bold text-white shadow-sm transition-all hover:bg-blue-700 active:scale-[0.99] disabled:cursor-not-allowed disabled:opacity-70"
> >
{isLogin ? copy.loginSubmit : copy.signupSubmit} {isLogin ? copy.loginSubmit : copy.signupSubmit}
<ArrowRight className="ml-2 h-5 w-5 transition-transform group-hover:translate-x-1" /> <ArrowRight className="ml-2 h-5 w-5 transition-transform group-hover:translate-x-1" />
</button> </button>
</form> </form>
{errorText ? <p className="mt-4 text-sm text-rose-300">{errorText}</p> : null} {errorText ? <p className="mt-4 rounded-lg border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-700">{errorText}</p> : null}
{infoText ? <p className="mt-4 text-sm text-emerald-300">{infoText}</p> : null} {infoText ? <p className="mt-4 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm text-emerald-700">{infoText}</p> : null}
{errorText && isLogin && errorText.includes("Invalid login") ? ( {errorText && isLogin && errorText.includes("Invalid login") ? (
<p className="mt-1 text-xs text-slate-500">{copy.loginFailedHint}</p> <p className="mt-1 text-xs text-slate-500">{copy.loginFailedHint}</p>
) : null} ) : null}
@@ -353,7 +352,7 @@ export function LoginClient({ nextPath }: LoginClientProps) {
) : null} ) : null}
</div> </div>
<div className="absolute bottom-8 flex items-center gap-4 text-sm text-slate-600"> <div className="relative mt-6 flex items-center gap-4 text-sm font-medium text-slate-500">
<span className="flex items-center"> <span className="flex items-center">
<Sun className="mr-1 h-4 w-4" /> {copy.realtime} <Sun className="mr-1 h-4 w-4" /> {copy.realtime}
</span> </span>
@@ -1062,3 +1062,33 @@
@keyframes spin { to { transform: rotate(360deg); } } @keyframes spin { to { transform: rotate(360deg); } }
/* ── Model Forecast Light Mode Overrides ── */
:global(html.light) .root :global(.model-group) {
background: #f8fafc;
border-left-color: var(--color-border-default);
}
:global(html.light) .root :global(.model-bar-track) {
background: var(--color-bg-input);
}
:global(html.light) .root :global(.model-bar-value) {
color: #ffffff;
text-shadow: 0 1px 2px rgba(15, 23, 42, 0.4);
}
:global(html.light) .root :global(.model-name strong) {
color: var(--color-text-primary);
}
:global(html.light) .root :global(.model-name span) {
color: var(--color-text-muted);
}
:global(html.light) .root :global(.model-stack-summary span) {
background: #ffffff;
border-color: var(--color-border-default);
color: var(--color-text-secondary);
}
@@ -714,6 +714,55 @@
background: var(--bg-card); background: var(--bg-card);
} }
/* ── Segmented Control: Locale Switcher in Light Mode ── */
.root :global(.scan-terminal.light .scan-locale-switch) {
border-color: #cbd5e1 !important;
background: #e2e8f0 !important;
color: #64748b !important;
}
.root :global(.scan-terminal.light .scan-locale-switch span) {
color: #64748b !important;
}
.root :global(.scan-terminal.light .scan-locale-switch span.active) {
color: #0f172a !important;
background: #ffffff !important;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08) !important;
}
/* ── Status Chips and Manual Refresh button in Light Mode ── */
.root :global(.scan-terminal.light .scan-status-chip) {
border-color: #cbd5e1 !important;
background: #ffffff !important;
color: #475569 !important;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05) !important;
}
.root :global(.scan-terminal.light .scan-status-chip.live) {
border-color: rgba(16, 185, 129, 0.22) !important;
background: rgba(16, 185, 129, 0.12) !important;
color: #065f46 !important;
}
.root :global(.scan-terminal.light .scan-status-chip.stale) {
border-color: rgba(245, 158, 11, 0.24) !important;
background: rgba(245, 158, 11, 0.12) !important;
color: #92400e !important;
}
.root :global(.scan-terminal.light button.scan-status-chip.refresh) {
border-color: #cbd5e1 !important;
background: #ffffff !important;
color: #1e293b !important;
}
.root :global(.scan-terminal.light button.scan-status-chip.refresh:hover) {
background: #f8fafc !important;
border-color: #94a3b8 !important;
}
.root :global(.scan-terminal.light .scan-mode-tab.active) { .root :global(.scan-terminal.light .scan-mode-tab.active) {
color: #1D4ED8; color: #1D4ED8;
background: #DBEAFE; background: #DBEAFE;
@@ -814,9 +863,7 @@
background: linear-gradient(180deg, rgba(224, 247, 255, 0.82), rgba(241, 248, 255, 0.92)); background: linear-gradient(180deg, rgba(224, 247, 255, 0.82), rgba(241, 248, 255, 0.92));
} }
.root :global(.scan-terminal.light .scan-locale-switch),
.root :global(.scan-terminal.light .scan-ghost-button), .root :global(.scan-terminal.light .scan-ghost-button),
.root :global(.scan-terminal.light .scan-theme-button),
.root :global(.scan-terminal.light .scan-sort-pill), .root :global(.scan-terminal.light .scan-sort-pill),
.root :global(.scan-terminal.light .scan-icon-pill), .root :global(.scan-terminal.light .scan-icon-pill),
.root :global(.scan-terminal.light .scan-account-button), .root :global(.scan-terminal.light .scan-account-button),
@@ -1221,128 +1268,128 @@
/* ── Leaflet map light overrides (needs !important to beat Leaflet inline styles) ── */ /* ── Leaflet map light overrides (needs !important to beat Leaflet inline styles) ── */
.root :global(.map), .root :global(.map),
html.light .root :global(.scan-terminal.light .scan-map-shell .map), :global(html.light) .root :global(.scan-terminal.light .scan-map-shell .map),
html.light .root :global(.scan-terminal.light .scan-map-shell .leaflet-container), :global(html.light) .root :global(.scan-terminal.light .scan-map-shell .leaflet-container),
html.light .root :global(.leaflet-container), :global(html.light) .root :global(.leaflet-container),
html.light .root :global(.leaflet-pane), :global(html.light) .root :global(.leaflet-pane),
html.light .root :global(.leaflet-map-pane), :global(html.light) .root :global(.leaflet-map-pane),
html.light .root :global(.leaflet-tile-pane) { :global(html.light) .root :global(.leaflet-tile-pane) {
background: #eef7ff !important; background: #eef7ff !important;
} }
/* ── City detail rail light overrides ── */ /* ── City detail rail light overrides ── */
html.light .root :global(.scan-city-detail-rail), :global(html.light) .root :global(.scan-city-detail-rail),
html.light .root :global(.scan-terminal.light > .detail-panel.scan-city-detail-rail) { :global(html.light) .root :global(.scan-terminal.light > .detail-panel.scan-city-detail-rail) {
background: linear-gradient(180deg, rgba(238, 247, 255, 0.98), rgba(255, 255, 255, 0.96)); background: linear-gradient(180deg, rgba(238, 247, 255, 0.98), rgba(255, 255, 255, 0.96));
border-color: rgba(37, 99, 235, 0.16); border-color: rgba(37, 99, 235, 0.16);
color: var(--text-primary); color: var(--text-primary);
} }
html.light .root :global(.scan-city-detail-rail .panel-header), :global(html.light) .root :global(.scan-city-detail-rail .panel-header),
html.light .root :global(.scan-city-detail-rail .detail-structured-section), :global(html.light) .root :global(.scan-city-detail-rail .detail-structured-section),
html.light .root :global(.scan-city-detail-rail .detail-summary-shell), :global(html.light) .root :global(.scan-city-detail-rail .detail-summary-shell),
html.light .root :global(.scan-city-detail-rail .detail-card) { :global(html.light) .root :global(.scan-city-detail-rail .detail-card) {
background: rgba(255, 255, 255, 0.74); background: rgba(255, 255, 255, 0.74);
border-color: rgba(125, 171, 214, 0.28); border-color: rgba(125, 171, 214, 0.28);
} }
html.light .root :global(.scan-city-detail-rail h2), :global(html.light) .root :global(.scan-city-detail-rail h2),
html.light .root :global(.scan-city-detail-rail h3), :global(html.light) .root :global(.scan-city-detail-rail h3),
html.light .root :global(.scan-city-detail-rail strong), :global(html.light) .root :global(.scan-city-detail-rail strong),
html.light .root :global(.scan-city-detail-rail .detail-value), :global(html.light) .root :global(.scan-city-detail-rail .detail-value),
html.light .root :global(.scan-city-detail-rail .detail-summary-temp), :global(html.light) .root :global(.scan-city-detail-rail .detail-summary-temp),
html.light .root :global(.scan-city-detail-rail .detail-section-head h3), :global(html.light) .root :global(.scan-city-detail-rail .detail-section-head h3),
html.light .root :global(.scan-city-detail-rail .panel-title-area h2) { :global(html.light) .root :global(.scan-city-detail-rail .panel-title-area h2) {
color: var(--text-primary); color: var(--text-primary);
} }
html.light .root :global(.scan-city-detail-rail p), :global(html.light) .root :global(.scan-city-detail-rail p),
html.light .root :global(.scan-city-detail-rail li), :global(html.light) .root :global(.scan-city-detail-rail li),
html.light .root :global(.scan-city-detail-rail small), :global(html.light) .root :global(.scan-city-detail-rail small),
html.light .root :global(.scan-city-detail-rail .panel-overline), :global(html.light) .root :global(.scan-city-detail-rail .panel-overline),
html.light .root :global(.scan-city-detail-rail .panel-loading-hint), :global(html.light) .root :global(.scan-city-detail-rail .panel-loading-hint),
html.light .root :global(.scan-city-detail-rail .panel-meta-chip), :global(html.light) .root :global(.scan-city-detail-rail .panel-meta-chip),
html.light .root :global(.scan-city-detail-rail .detail-section-kicker), :global(html.light) .root :global(.scan-city-detail-rail .detail-section-kicker),
html.light .root :global(.scan-city-detail-rail .detail-label), :global(html.light) .root :global(.scan-city-detail-rail .detail-label),
html.light .root :global(.scan-city-detail-rail .detail-value-muted), :global(html.light) .root :global(.scan-city-detail-rail .detail-value-muted),
html.light .root :global(.scan-city-detail-rail .detail-source-note), :global(html.light) .root :global(.scan-city-detail-rail .detail-source-note),
html.light .root :global(.scan-city-detail-rail .detail-source-kind), :global(html.light) .root :global(.scan-city-detail-rail .detail-source-kind),
html.light .root :global(.scan-city-detail-rail .detail-mini-meta), :global(html.light) .root :global(.scan-city-detail-rail .detail-mini-meta),
html.light .root :global(.scan-city-detail-rail .detail-summary-supporting), :global(html.light) .root :global(.scan-city-detail-rail .detail-summary-supporting),
html.light .root :global(.scan-city-detail-rail .scan-detail-city-sub), :global(html.light) .root :global(.scan-city-detail-rail .scan-detail-city-sub),
html.light .root :global(.scan-city-detail-rail .scan-detail-volume-caption), :global(html.light) .root :global(.scan-city-detail-rail .scan-detail-volume-caption),
html.light .root :global(.scan-city-detail-rail .scan-detail-score-label), :global(html.light) .root :global(.scan-city-detail-rail .scan-detail-score-label),
html.light .root :global(.scan-city-detail-rail .scan-detail-empty) { :global(html.light) .root :global(.scan-city-detail-rail .scan-detail-empty) {
color: var(--text-secondary); color: var(--text-secondary);
} }
html.light .root :global(.scan-city-detail-rail .panel-meta-chip), :global(html.light) .root :global(.scan-city-detail-rail .panel-meta-chip),
html.light .root :global(.scan-city-detail-rail .panel-weather-chip), :global(html.light) .root :global(.scan-city-detail-rail .panel-weather-chip),
html.light .root :global(.scan-city-detail-rail .detail-source-link), :global(html.light) .root :global(.scan-city-detail-rail .detail-source-link),
html.light .root :global(.scan-city-detail-rail .panel-action-button-secondary), :global(html.light) .root :global(.scan-city-detail-rail .panel-action-button-secondary),
html.light .root :global(.scan-city-detail-rail .panel-action-button-ghost) { :global(html.light) .root :global(.scan-city-detail-rail .panel-action-button-ghost) {
background: #eef7ff; background: #eef7ff;
border-color: rgba(37, 99, 235, 0.18); border-color: rgba(37, 99, 235, 0.18);
color: #1f3654; color: #1f3654;
} }
/* ── AI city card light overrides ── */ /* ── AI city card light overrides ── */
html.light .root :global(.scan-terminal.light .scan-ai-city-card), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-card),
html.light .root :global(.scan-terminal.light .scan-ai-city-section), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-section),
html.light .root :global(.scan-terminal.light .scan-ai-decision-band), :global(html.light) .root :global(.scan-terminal.light .scan-ai-decision-band),
html.light .root :global(.scan-terminal.light .scan-ai-market-decision), :global(html.light) .root :global(.scan-terminal.light .scan-ai-market-decision),
html.light .root :global(.scan-terminal.light .scan-ai-decision-metrics span), :global(html.light) .root :global(.scan-terminal.light .scan-ai-decision-metrics span),
html.light .root :global(.scan-terminal.light .scan-ai-city-pills span), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-pills span),
html.light .root :global(.scan-terminal.light .scan-ai-city-freshness span), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-freshness span),
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics > span), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-metrics > span),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-metrics span), :global(html.light) .root :global(.scan-terminal.light .scan-mobile-decision-metrics span),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-reason) { :global(html.light) .root :global(.scan-terminal.light .scan-mobile-decision-reason) {
background: #eef7ff; background: #eef7ff;
border-color: rgba(37, 99, 235, 0.16); border-color: rgba(37, 99, 235, 0.16);
} }
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics > span.primary) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-metrics > span.primary) {
background: #dbeafe; background: #dbeafe;
border-color: rgba(59, 130, 246, 0.34); border-color: rgba(59, 130, 246, 0.34);
} }
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics b) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-metrics b) {
color: var(--text-primary); color: var(--text-primary);
} }
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics > span.primary b) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-metrics > span.primary b) {
color: #1d4ed8; color: #1d4ed8;
} }
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics small) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-metrics small) {
color: #64748b; color: #64748b;
} }
html.light .root :global(.scan-terminal.light .scan-ai-city-card p), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-card p),
html.light .root :global(.scan-terminal.light .scan-ai-city-card li), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-card li),
html.light .root :global(.scan-terminal.light .scan-ai-city-card small), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-card small),
html.light .root :global(.scan-terminal.light .scan-ai-city-card span:not(.scan-ai-city-kicker)) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-card span:not(.scan-ai-city-kicker)) {
color: var(--text-secondary); color: var(--text-secondary);
} }
html.light .root :global(.scan-terminal.light .scan-ai-city-status-tag.green) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-status-tag.green) {
color: #047857; color: #047857;
} }
html.light .root :global(.scan-terminal.light .scan-ai-city-status-tag.blue) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-status-tag.blue) {
color: #1d4ed8; color: #1d4ed8;
} }
html.light .root :global(.scan-terminal.light .scan-ai-city-status-tag.amber) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-status-tag.amber) {
color: #b45309; color: #b45309;
} }
html.light .root :global(.scan-terminal.light .scan-ai-city-status-tag.red) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-status-tag.red) {
color: #b91c1c; color: #b91c1c;
} }
/* ── Upgrade announcement light overrides ── */ /* ── Upgrade announcement light overrides ── */
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement) { :global(html.light) .root :global(.scan-terminal.light .scan-upgrade-announcement) {
background: background:
radial-gradient(circle at top left, rgba(37, 99, 235, 0.12), transparent 36%), radial-gradient(circle at top left, rgba(37, 99, 235, 0.12), transparent 36%),
var(--bg-card); var(--bg-card);
@@ -1350,188 +1397,188 @@ html.light .root :global(.scan-terminal.light .scan-upgrade-announcement) {
box-shadow: 0 16px 36px rgba(61, 100, 145, 0.12); box-shadow: 0 16px 36px rgba(61, 100, 145, 0.12);
} }
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement-copy strong), :global(html.light) .root :global(.scan-terminal.light .scan-upgrade-announcement-copy strong),
html.light .root :global(.scan-terminal.light .scan-ai-city-mobile-priority b), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-mobile-priority b),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-metrics b), :global(html.light) .root :global(.scan-terminal.light .scan-mobile-decision-metrics b),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-reason), :global(html.light) .root :global(.scan-terminal.light .scan-mobile-decision-reason),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-head h3), :global(html.light) .root :global(.scan-terminal.light .scan-mobile-decision-head h3),
html.light .root :global(.scan-terminal.light .scan-ai-market-mobile-line b) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-market-mobile-line b) {
color: var(--text-primary); color: var(--text-primary);
} }
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement-copy p), :global(html.light) .root :global(.scan-terminal.light .scan-upgrade-announcement-copy p),
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement li), :global(html.light) .root :global(.scan-terminal.light .scan-upgrade-announcement li),
html.light .root :global(.scan-terminal.light .scan-ai-city-mobile-priority small), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-mobile-priority small),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-metrics small), :global(html.light) .root :global(.scan-terminal.light .scan-mobile-decision-metrics small),
html.light .root :global(.scan-terminal.light .scan-ai-market-mobile-line span) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-market-mobile-line span) {
color: var(--text-secondary); color: var(--text-secondary);
} }
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement-copy span) { :global(html.light) .root :global(.scan-terminal.light .scan-upgrade-announcement-copy span) {
color: #047857; color: #047857;
} }
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement li), :global(html.light) .root :global(.scan-terminal.light .scan-upgrade-announcement li),
html.light .root :global(.scan-terminal.light .scan-ai-city-mobile-priority span), :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-mobile-priority span),
html.light .root :global(.scan-terminal.light .scan-ai-decision-why), :global(html.light) .root :global(.scan-terminal.light .scan-ai-decision-why),
html.light .root :global(.scan-terminal.light .scan-ai-market-mobile-line) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-market-mobile-line) {
background: #eef7ff; background: #eef7ff;
border-color: rgba(37, 99, 235, 0.16); border-color: rgba(37, 99, 235, 0.16);
} }
html.light .root :global(.scan-terminal.light .scan-ai-decision-why) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-decision-why) {
color: #1d4ed8; color: #1d4ed8;
} }
/* ── Section titles ── */ /* ── Section titles ── */
html.light .root :global(.scan-terminal.light .scan-ai-city-section-title) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-section-title) {
color: #1d4ed8; color: #1d4ed8;
} }
html.light .root :global(.scan-terminal.light .scan-ai-decision-band span) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-decision-band span) {
color: #1d4ed8; color: #1d4ed8;
} }
html.light .root :global(.scan-terminal.light .scan-ai-decision-band p) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-decision-band p) {
color: var(--text-muted); color: var(--text-muted);
} }
html.light .root :global(.scan-terminal.light .scan-ai-decision-band strong) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-decision-band strong) {
color: var(--text-primary); color: var(--text-primary);
} }
/* ── Scrollbar light overrides ── */ /* ── Scrollbar light overrides ── */
html.light .root :global(.scan-terminal.light .scan-ai-city-body) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-city-body) {
scrollbar-color: rgba(37, 99, 235, 0.18) transparent; scrollbar-color: rgba(37, 99, 235, 0.18) transparent;
} }
html.light .root :global(.scan-ai-city-body::-webkit-scrollbar-thumb) { :global(html.light) .root :global(.scan-ai-city-body::-webkit-scrollbar-thumb) {
background: rgba(37, 99, 235, 0.22); background: rgba(37, 99, 235, 0.22);
} }
/* ── AI prediction dual-card light mode ── */ /* ── AI prediction dual-card light mode ── */
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-prediction-card) {
background: var(--bg-card); background: var(--bg-card);
border-color: var(--border-glass); border-color: var(--border-glass);
} }
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.ai) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-prediction-card.ai) {
background: linear-gradient(180deg, rgba(219, 234, 254, 0.52), var(--bg-card)); background: linear-gradient(180deg, rgba(219, 234, 254, 0.52), var(--bg-card));
border-color: rgba(14, 165, 233, 0.28); border-color: rgba(14, 165, 233, 0.28);
} }
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.deb) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-prediction-card.deb) {
background: var(--bg-card); background: var(--bg-card);
border-color: var(--border-glass); border-color: var(--border-glass);
} }
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card small) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-prediction-card small) {
color: #64748b; color: #64748b;
} }
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card strong) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-prediction-card strong) {
color: var(--text-primary); color: var(--text-primary);
} }
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.ai strong) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-prediction-card.ai strong) {
color: #0369a1; color: #0369a1;
} }
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card em) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-prediction-card em) {
color: #64748b; color: #64748b;
} }
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.ai.pending) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-prediction-card.ai.pending) {
background: rgba(219, 234, 254, 0.28); background: rgba(219, 234, 254, 0.28);
border-color: rgba(14, 165, 233, 0.16); border-color: rgba(14, 165, 233, 0.16);
} }
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.ai.pending strong) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-prediction-card.ai.pending strong) {
color: #94a3b8; color: #94a3b8;
} }
html.light .root :global(.scan-terminal.light .scan-ai-confidence) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-confidence) {
background: rgba(59, 130, 246, 0.1); background: rgba(59, 130, 246, 0.1);
color: #2563eb; color: #2563eb;
} }
html.light .root :global(.scan-terminal.light .scan-ai-confidence.high) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-confidence.high) {
background: rgba(16, 185, 129, 0.12); background: rgba(16, 185, 129, 0.12);
color: #047857; color: #047857;
} }
html.light .root :global(.scan-terminal.light .scan-ai-confidence.medium) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-confidence.medium) {
background: rgba(245, 158, 11, 0.12); background: rgba(245, 158, 11, 0.12);
color: #b45309; color: #b45309;
} }
html.light .root :global(.scan-terminal.light .scan-ai-confidence.low) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-confidence.low) {
background: rgba(148, 163, 184, 0.12); background: rgba(148, 163, 184, 0.12);
color: #64748b; color: #64748b;
} }
html.light .root :global(.scan-terminal.light .scan-ai-confidence.neutral) { :global(html.light) .root :global(.scan-terminal.light .scan-ai-confidence.neutral) {
background: rgba(148, 163, 184, 0.08); background: rgba(148, 163, 184, 0.08);
color: #64748b; color: #64748b;
} }
/* ── Empty / Error / Retry light overrides ── */ /* ── Empty / Error / Retry light overrides ── */
html.light .root :global(.scan-terminal.light .scan-empty-title), :global(html.light) .root :global(.scan-terminal.light .scan-empty-title),
html.light .root :global(.scan-terminal.light .scan-error-title) { :global(html.light) .root :global(.scan-terminal.light .scan-error-title) {
color: var(--text-primary); color: var(--text-primary);
} }
html.light .root :global(.scan-terminal.light .scan-empty-copy), :global(html.light) .root :global(.scan-terminal.light .scan-empty-copy),
html.light .root :global(.scan-terminal.light .scan-error-copy) { :global(html.light) .root :global(.scan-terminal.light .scan-error-copy) {
color: var(--text-muted); color: var(--text-muted);
} }
html.light .root :global(.scan-terminal.light .scan-empty-icon) { :global(html.light) .root :global(.scan-terminal.light .scan-empty-icon) {
background: rgba(37, 99, 235, 0.08); background: rgba(37, 99, 235, 0.08);
border-color: rgba(37, 99, 235, 0.2); border-color: rgba(37, 99, 235, 0.2);
color: #2563EB; color: #2563EB;
} }
html.light .root :global(.scan-terminal.light .scan-error-icon) { :global(html.light) .root :global(.scan-terminal.light .scan-error-icon) {
background: rgba(239, 68, 68, 0.06); background: rgba(239, 68, 68, 0.06);
border-color: rgba(239, 68, 68, 0.18); border-color: rgba(239, 68, 68, 0.18);
color: #DC2626; color: #DC2626;
} }
html.light .root :global(.scan-terminal.light .scan-retry-button) { :global(html.light) .root :global(.scan-terminal.light .scan-retry-button) {
background: rgba(37, 99, 235, 0.08); background: rgba(37, 99, 235, 0.08);
border-color: rgba(37, 99, 235, 0.24); border-color: rgba(37, 99, 235, 0.24);
color: #2563EB; color: #2563EB;
} }
html.light .root :global(.scan-terminal.light .scan-retry-button:hover) { :global(html.light) .root :global(.scan-terminal.light .scan-retry-button:hover) {
background: rgba(37, 99, 235, 0.14); background: rgba(37, 99, 235, 0.14);
} }
/* ── AMOS runway panel light overrides ── */ /* ── AMOS runway panel light overrides ── */
html.light .root :global(.scan-terminal.light .scan-amos-runway-panel) { :global(html.light) .root :global(.scan-terminal.light .scan-amos-runway-panel) {
background: rgba(37, 99, 235, 0.03); background: rgba(37, 99, 235, 0.03);
border-color: rgba(37, 99, 235, 0.12); border-color: rgba(37, 99, 235, 0.12);
} }
html.light .root :global(.scan-terminal.light .scan-amos-runway-card) { :global(html.light) .root :global(.scan-terminal.light .scan-amos-runway-card) {
background: var(--bg-card); background: var(--bg-card);
border-color: rgba(37, 99, 235, 0.08); border-color: rgba(37, 99, 235, 0.08);
} }
html.light .root :global(.scan-terminal.light .scan-amos-runway-label) { :global(html.light) .root :global(.scan-terminal.light .scan-amos-runway-label) {
color: #2563EB; color: #2563EB;
} }
html.light .root :global(.scan-terminal.light .scan-amos-runway-temp) { :global(html.light) .root :global(.scan-terminal.light .scan-amos-runway-temp) {
color: var(--text-primary); color: var(--text-primary);
} }
html.light .root :global(.scan-terminal.light .scan-amos-source-tag) { :global(html.light) .root :global(.scan-terminal.light .scan-amos-source-tag) {
background: rgba(37, 99, 235, 0.08); background: rgba(37, 99, 235, 0.08);
color: #2563EB; color: #2563EB;
} }
html.light .root :global(.scan-terminal.light .scan-amos-runway-detail) { :global(html.light) .root :global(.scan-terminal.light .scan-amos-runway-detail) {
color: #64748B; color: #64748B;
} }
@@ -1337,3 +1337,60 @@
} }
@keyframes spin { to { transform: rotate(360deg); } } @keyframes spin { to { transform: rotate(360deg); } }
/* ── AI prediction dual-card light mode ── */
:global(html.light) .root :global(.scan-ai-prediction-card) {
background: var(--color-bg-raised);
border-color: var(--color-border-default);
}
:global(html.light) .root :global(.scan-ai-prediction-card.ai) {
background: linear-gradient(180deg, rgba(37, 99, 235, 0.08), var(--color-bg-raised));
border-color: rgba(37, 99, 235, 0.22);
}
:global(html.light) .root :global(.scan-ai-prediction-card.deb) {
background: var(--color-bg-raised);
border-color: var(--color-border-default);
}
:global(html.light) .root :global(.scan-ai-prediction-card small) {
color: var(--color-text-muted);
}
:global(html.light) .root :global(.scan-ai-prediction-card strong) {
color: var(--color-text-primary);
}
:global(html.light) .root :global(.scan-ai-prediction-card.ai strong) {
color: var(--color-accent-primary);
}
:global(html.light) .root :global(.scan-ai-prediction-card em) {
color: var(--color-text-muted);
}
:global(html.light) .root :global(.scan-ai-prediction-card.ai.pending) {
background: rgba(37, 99, 235, 0.04);
border-color: rgba(37, 99, 235, 0.12);
}
:global(html.light) .root :global(.scan-ai-prediction-card.ai.pending strong) {
color: var(--color-text-disabled);
}
:global(html.light) .root :global(.scan-ai-confidence) {
background: rgba(37, 99, 235, 0.1);
color: var(--color-accent-primary);
}
:global(html.light) .root :global(.scan-ai-confidence.high) {
background: rgba(16, 185, 129, 0.12);
color: #047857;
}
:global(html.light) .root :global(.scan-ai-confidence.medium) {
background: rgba(245, 158, 11, 0.12);
color: #b45309;
}
@@ -117,6 +117,15 @@ function ScanTerminalScreen() {
} }
}, [terminalData?.generated_at]); }, [terminalData?.generated_at]);
useEffect(() => {
if (activeView === "map") {
const timer = setTimeout(() => {
window.dispatchEvent(new Event("resize"));
}, 150);
return () => clearTimeout(timer);
}
}, [activeView]);
const scanTerminalRootClassName = clsx( const scanTerminalRootClassName = clsx(
styles.root, styles.root,
scanRootClass, scanRootClass,
@@ -520,3 +520,23 @@
@keyframes spin { to { transform: rotate(360deg); } } @keyframes spin { to { transform: rotate(360deg); } }
/* ── City Jumpbar Light Mode Overrides ── */
:global(html.light) .root :global(.scan-ai-city-jumpbar) {
background: #ffffff;
border-color: #d8e0ec;
box-shadow: var(--shadow-elevation-1);
}
:global(html.light) .root :global(.scan-ai-city-jumpbar button) {
background: var(--color-bg-input);
border-color: var(--color-border-default);
color: var(--color-text-secondary);
}
:global(html.light) .root :global(.scan-ai-city-jumpbar button:hover) {
background: #e2e8f0;
border-color: var(--color-border-hover);
color: var(--color-text-primary);
}
@@ -624,10 +624,6 @@
height: 38px; height: 38px;
} }
.root :global(.scan-theme-button) {
width: 38px;
height: 38px;
}
} }
/* ── Mobile City Picker ── */ /* ── Mobile City Picker ── */
@@ -16,7 +16,7 @@
justify-content: space-between; justify-content: space-between;
gap: 12px; gap: 12px;
padding: 6px 0 18px; padding: 6px 0 18px;
border-bottom: 1px solid rgba(118, 146, 188, 0.1); border-bottom: 1px solid var(--color-border-default);
} }
.root :global(.scan-topbar-title) { .root :global(.scan-topbar-title) {
@@ -30,12 +30,12 @@
margin: 0; margin: 0;
font-size: 28px; font-size: 28px;
line-height: 1.08; line-height: 1.08;
letter-spacing: -0.04em; letter-spacing: 0;
color: #f3f8ff; color: var(--color-text-primary);
} }
.root :global(.scan-topbar-title span) { .root :global(.scan-topbar-title span) {
color: #8fa4c3; color: var(--color-text-secondary);
font-size: 14px; font-size: 14px;
line-height: 1.5; line-height: 1.5;
} }
@@ -46,17 +46,17 @@
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-end; justify-content: flex-end;
gap: 12px; gap: 12px;
color: #b4c8e2; color: var(--color-text-secondary);
font-size: 14px; font-size: 14px;
} }
.root :global(.scan-locale-switch) { .root :global(.scan-locale-switch) {
height: 36px; height: 36px;
padding: 3px; padding: 3px;
border-radius: 12px; border-radius: 10px;
border: 1px solid rgba(99, 132, 180, 0.18); border: 1px solid var(--color-border-default);
background: rgba(8, 19, 34, 0.9); background: #e2e8f0;
color: #a8bdd8; color: var(--color-text-secondary);
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 2px; gap: 2px;
@@ -69,20 +69,20 @@
min-width: 34px; min-width: 34px;
height: 28px; height: 28px;
padding: 0 8px; padding: 0 8px;
border-radius: 8px; border-radius: 7px;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.root :global(.scan-locale-switch span.active) { .root :global(.scan-locale-switch span.active) {
color: #eef7ff; color: var(--color-text-primary);
background: rgba(63, 140, 255, 0.16); background: #ffffff;
box-shadow: inset 0 0 0 1px rgba(63, 140, 255, 0.22); box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
} }
.root :global(.scan-topbar-time) { .root :global(.scan-topbar-time) {
color: #b4c8e2; color: var(--color-text-secondary);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
@@ -92,12 +92,11 @@
gap: 16px; gap: 16px;
align-items: center; align-items: center;
margin: -4px 0 6px; margin: -4px 0 6px;
border: 1px solid rgba(77, 163, 255, 0.2); border: 1px solid #bfdbfe;
border-radius: 18px; border-radius: 12px;
background: background:
radial-gradient(circle at top left, rgba(77, 163, 255, 0.18), transparent 36%), linear-gradient(90deg, rgba(239, 246, 255, 0.98), rgba(255, 255, 255, 0.98));
linear-gradient(135deg, rgba(18, 33, 58, 0.96), rgba(15, 23, 42, 0.92)); box-shadow: 0 10px 28px rgba(15, 23, 42, 0.08);
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.2);
padding: 16px 18px; padding: 16px 18px;
} }
@@ -106,7 +105,7 @@
} }
.root :global(.scan-upgrade-announcement-copy span) { .root :global(.scan-upgrade-announcement-copy span) {
color: #86efac; color: #047857;
font-size: 12px; font-size: 12px;
font-weight: 900; font-weight: 900;
letter-spacing: 0.04em; letter-spacing: 0.04em;
@@ -116,14 +115,14 @@
.root :global(.scan-upgrade-announcement-copy strong) { .root :global(.scan-upgrade-announcement-copy strong) {
display: block; display: block;
margin-top: 5px; margin-top: 5px;
color: #f3f8ff; color: var(--color-text-primary);
font-size: 18px; font-size: 18px;
line-height: 1.25; line-height: 1.25;
} }
.root :global(.scan-upgrade-announcement-copy p) { .root :global(.scan-upgrade-announcement-copy p) {
margin: 6px 0 0; margin: 6px 0 0;
color: #b4c8e2; color: var(--color-text-secondary);
font-size: 13px; font-size: 13px;
line-height: 1.55; line-height: 1.55;
} }
@@ -139,10 +138,10 @@
} }
.root :global(.scan-upgrade-announcement li) { .root :global(.scan-upgrade-announcement li) {
border: 1px solid rgba(77, 163, 255, 0.22); border: 1px solid #bfdbfe;
border-radius: 999px; border-radius: 999px;
background: rgba(77, 163, 255, 0.1); background: #eff6ff;
color: #d7e5f7; color: #1d4ed8;
font-size: 12px; font-size: 12px;
font-weight: 800; font-weight: 800;
padding: 7px 10px; padding: 7px 10px;
@@ -158,16 +157,16 @@
place-items: center; place-items: center;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
background: rgba(148, 163, 184, 0.1); background: #f1f5f9;
color: #94a3b8; color: #64748b;
font-size: 14px; font-size: 14px;
cursor: pointer; cursor: pointer;
transition: background 0.15s, color 0.15s; transition: background 0.15s, color 0.15s;
} }
.root :global(.scan-announcement-dismiss:hover) { .root :global(.scan-announcement-dismiss:hover) {
background: rgba(148, 163, 184, 0.22); background: #e2e8f0;
color: #e2e8f0; color: #0f172a;
} }
.root :global(.scan-upgrade-announcement) { .root :global(.scan-upgrade-announcement) {
@@ -176,14 +175,13 @@
.root :global(.scan-ghost-button), .root :global(.scan-ghost-button),
.root :global(.scan-cta-ghost), .root :global(.scan-cta-ghost),
.root :global(.scan-theme-button),
.root :global(.scan-detail-action-button), .root :global(.scan-detail-action-button),
.root :global(.scan-detail-icon-button), .root :global(.scan-detail-icon-button),
.root :global(.scan-view-all-button) { .root :global(.scan-view-all-button) {
border: 1px solid rgba(99, 132, 180, 0.18); border: 1px solid var(--color-border-default);
background: rgba(8, 19, 34, 0.9); background: var(--color-bg-raised);
color: #d7e5f7; color: var(--color-text-primary);
border-radius: 12px; border-radius: 10px;
} }
.root :global(.scan-ghost-button), .root :global(.scan-ghost-button),
@@ -202,13 +200,13 @@
min-height: 42px; min-height: 42px;
padding: 11px 16px; padding: 11px 16px;
border: 1px solid rgba(77, 163, 255, 0.42); border: 1px solid rgba(77, 163, 255, 0.42);
border-radius: 12px; border-radius: 10px;
background: linear-gradient(180deg, var(--color-accent-primary), #3B82F6); background: linear-gradient(180deg, var(--color-accent-primary), #3B82F6);
color: #FFFFFF; color: #FFFFFF;
font-size: 14px; font-size: 14px;
font-weight: 900; font-weight: 900;
text-decoration: none; text-decoration: none;
box-shadow: 0 10px 24px rgba(59, 130, 246, 0.22); box-shadow: 0 8px 18px rgba(37, 99, 235, 0.18);
cursor: pointer; cursor: pointer;
} }
@@ -227,18 +225,18 @@
gap: 8px; gap: 8px;
min-height: 42px; min-height: 42px;
padding: 11px 14px; padding: 11px 14px;
border: 1px solid rgba(77, 163, 255, 0.3); border: 1px solid #bfdbfe;
border-radius: 12px; border-radius: 10px;
background: linear-gradient(180deg, rgba(77, 163, 255, 0.18), rgba(59, 130, 246, 0.12)); background: #eff6ff;
color: #CFE6FF; color: #1d4ed8;
font-size: 13px; font-size: 13px;
font-weight: 900; font-weight: 900;
cursor: pointer; cursor: pointer;
} }
.root :global(.scan-ai-button:hover:not(:disabled)) { .root :global(.scan-ai-button:hover:not(:disabled)) {
border-color: rgba(111, 183, 255, 0.5); border-color: #93c5fd;
background: linear-gradient(180deg, rgba(77, 163, 255, 0.26), rgba(59, 130, 246, 0.18)); background: #dbeafe;
} }
.root :global(.scan-ai-button:disabled) { .root :global(.scan-ai-button:disabled) {
@@ -247,9 +245,9 @@
} }
.root :global(.scan-cta-ghost) { .root :global(.scan-cta-ghost) {
color: var(--color-accent-secondary); color: #047857;
border-color: rgba(23, 217, 139, 0.3); border-color: #a7f3d0;
background: rgba(23, 217, 139, 0.12); background: #ecfdf5;
} }
.root :global(.scan-account-button) { .root :global(.scan-account-button) {
@@ -258,26 +256,19 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #d7e5f7; color: var(--color-text-primary);
text-decoration: none; text-decoration: none;
border: 1px solid rgba(99, 132, 180, 0.18); border: 1px solid var(--color-border-default);
background: rgba(8, 19, 34, 0.9); background: var(--color-bg-raised);
border-radius: 12px; border-radius: 10px;
} }
.root :global(.scan-account-button:hover) { .root :global(.scan-account-button:hover) {
border-color: rgba(63, 140, 255, 0.28); border-color: #94a3b8;
background: rgba(12, 26, 44, 0.96); background: #f8fafc;
} }
.root :global(.scan-theme-button) {
width: 36px;
height: 36px;
display: inline-grid;
place-items: center;
color: #d7e5f7;
cursor: pointer;
}
.root :global(.scan-ghost-button .spin) { .root :global(.scan-ghost-button .spin) {
animation: spin 1s linear infinite; animation: spin 1s linear infinite;