@seaverseai/auth-sdk 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,417 @@
1
+ # @seaverseai/auth-sdk
2
+
3
+ SeaVerse 认证 SDK - 提供完整的用户认证功能和精美的登录注册 UI 组件
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@seaverseai/auth-sdk.svg)](https://www.npmjs.com/package/@seaverseai/auth-sdk)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## 功能特性
9
+
10
+ - 🔐 **邮箱登录/注册** - 传统用户名密码认证
11
+ - 🌐 **OAuth 第三方登录** - 支持 Google、Discord、GitHub
12
+ - 🎨 **精美登录 UI** - 开箱即用的登录弹窗组件
13
+ - 📱 **完全响应式** - 适配移动端/平板/桌面
14
+ - 🎭 **主题支持** - 暗色/亮色主题切换
15
+ - ⚡ **TypeScript** - 完整的类型定义
16
+ - 🔒 **安全** - 内置 CSRF 保护
17
+ - 🌍 **多环境支持** - 一键切换生产/测试/开发环境
18
+
19
+ ## 安装
20
+
21
+ ```bash
22
+ npm install @seaverseai/auth-sdk
23
+ ```
24
+
25
+ ## 快速开始
26
+
27
+ ### 1. API 客户端使用
28
+
29
+ #### 方式 A:指定环境(推荐)
30
+
31
+ ```typescript
32
+ import { SeaVerseBackendAPIClient } from '@seaverseai/auth-sdk';
33
+
34
+ // 正式环境
35
+ const client = new SeaVerseBackendAPIClient({
36
+ environment: 'production', // 'production' | 'staging' | 'development' | 'local'
37
+ });
38
+
39
+ // 或者测试环境
40
+ const client = new SeaVerseBackendAPIClient({
41
+ environment: 'staging',
42
+ });
43
+ ```
44
+
45
+ #### 方式 B:自动检测环境
46
+
47
+ ```typescript
48
+ import { SeaVerseBackendAPIClient } from '@seaverseai/auth-sdk';
49
+
50
+ // SDK 会根据当前域名自动选择环境
51
+ const client = new SeaVerseBackendAPIClient();
52
+
53
+ // 注册
54
+ const registerResult = await client.register({
55
+ email: 'user@example.com',
56
+ password: 'password123',
57
+ });
58
+
59
+ // 登录
60
+ const loginResult = await client.login({
61
+ email: 'user@example.com',
62
+ password: 'password123',
63
+ });
64
+
65
+ // 保存 token
66
+ localStorage.setItem('token', loginResult.token);
67
+
68
+ // 获取当前用户
69
+ const user = await client.getCurrentUser();
70
+
71
+ // 登出
72
+ await client.logout();
73
+ ```
74
+
75
+ ### 2. 使用登录弹窗 UI
76
+
77
+ ```typescript
78
+ import { SeaVerseBackendAPIClient, createAuthModal } from '@seaverseai/auth-sdk';
79
+ import '@seaverseai/auth-sdk/src/auth-modal.css';
80
+
81
+ const client = new SeaVerseBackendAPIClient({
82
+ baseURL: 'https://account-hub.sg.seaverse.dev',
83
+ });
84
+
85
+ const authModal = createAuthModal({
86
+ client,
87
+ theme: 'dark', // 'dark' 或 'light'
88
+
89
+ onLoginSuccess: (token, user) => {
90
+ localStorage.setItem('token', token);
91
+ console.log('登录成功:', user);
92
+ },
93
+
94
+ onSignupSuccess: (token, user) => {
95
+ localStorage.setItem('token', token);
96
+ console.log('注册成功:', user);
97
+ },
98
+
99
+ onError: (error) => {
100
+ console.error('错误:', error.message);
101
+ },
102
+ });
103
+
104
+ // 显示登录界面
105
+ authModal.show('login');
106
+
107
+ // 显示注册界面
108
+ authModal.show('signup');
109
+ ```
110
+
111
+ ## OAuth 第三方登录
112
+
113
+ ### 1. 配置 OAuth
114
+
115
+ ```typescript
116
+ const authModal = createAuthModal({
117
+ client,
118
+
119
+ oauthConfig: {
120
+ google: {
121
+ clientId: 'YOUR_GOOGLE_CLIENT_ID',
122
+ redirectUri: 'https://yourdomain.com/auth/callback',
123
+ },
124
+ discord: {
125
+ clientId: 'YOUR_DISCORD_CLIENT_ID',
126
+ redirectUri: 'https://yourdomain.com/auth/callback',
127
+ },
128
+ github: {
129
+ clientId: 'YOUR_GITHUB_CLIENT_ID',
130
+ redirectUri: 'https://yourdomain.com/auth/callback',
131
+ },
132
+ },
133
+
134
+ onLoginSuccess: (token, user) => {
135
+ localStorage.setItem('token', token);
136
+ },
137
+ });
138
+
139
+ authModal.show('login');
140
+ ```
141
+
142
+ ### 2. 处理 OAuth 回调
143
+
144
+ 在你的回调页面(如 `/auth/callback`)添加:
145
+
146
+ ```typescript
147
+ import { SeaVerseBackendAPIClient, AuthModal } from '@seaverseai/auth-sdk';
148
+
149
+ const client = new SeaVerseBackendAPIClient({
150
+ baseURL: 'https://account-hub.sg.seaverse.dev',
151
+ });
152
+
153
+ // 自动处理 OAuth 回调
154
+ AuthModal.handleOAuthCallbackFromUrl(client, {
155
+ onLoginSuccess: (token, user) => {
156
+ localStorage.setItem('token', token);
157
+ window.location.href = '/dashboard';
158
+ },
159
+ onError: (error) => {
160
+ console.error('登录失败:', error);
161
+ window.location.href = '/';
162
+ },
163
+ });
164
+ ```
165
+
166
+ ### 3. 获取 OAuth Client ID
167
+
168
+ | 平台 | 配置地址 |
169
+ |------|---------|
170
+ | Google | https://console.cloud.google.com/ → 创建 OAuth 客户端 ID |
171
+ | Discord | https://discord.com/developers/applications → OAuth2 设置 |
172
+ | GitHub | https://github.com/settings/developers → OAuth Apps |
173
+
174
+ **重要**:在 OAuth 应用中配置的 Redirect URI 必须与代码中的 `redirectUri` 完全一致。
175
+
176
+ ## API 参考
177
+
178
+ ### 认证方法
179
+
180
+ ```typescript
181
+ // 注册
182
+ await client.register({ email, password, invitationCode? })
183
+
184
+ // 登录
185
+ await client.login({ email, password })
186
+
187
+ // 获取当前用户
188
+ await client.getCurrentUser()
189
+
190
+ // 登出
191
+ await client.logout()
192
+
193
+ // 忘记密码
194
+ await client.forgotPassword({ email })
195
+
196
+ // 重置密码
197
+ await client.resetPassword({ token, newPassword })
198
+ ```
199
+
200
+ ### OAuth 方法
201
+
202
+ ```typescript
203
+ // Google 登录
204
+ await client.googleCodeToToken({ code })
205
+
206
+ // Discord 登录
207
+ await client.discordCodeToToken({ code })
208
+
209
+ // GitHub 登录
210
+ await client.githubCodeToToken({ code })
211
+
212
+ // 解绑账号
213
+ await client.unlinkGoogle()
214
+ await client.unlinkDiscord()
215
+ await client.unlinkGithub()
216
+ ```
217
+
218
+ ### UI 方法
219
+
220
+ ```typescript
221
+ // 显示登录/注册
222
+ authModal.show('login') // 或 'signup'
223
+
224
+ // 隐藏弹窗
225
+ authModal.hide()
226
+
227
+ // 销毁弹窗
228
+ authModal.destroy()
229
+ ```
230
+
231
+ ## 类型定义
232
+
233
+ ```typescript
234
+ // 用户信息
235
+ interface User {
236
+ id?: string;
237
+ email?: string;
238
+ username?: string;
239
+ emailVerified?: boolean;
240
+ googleId?: string;
241
+ discordId?: string;
242
+ githubId?: string;
243
+ }
244
+
245
+ // 登录响应
246
+ interface LoginResponse {
247
+ token: string;
248
+ user: User;
249
+ }
250
+
251
+ // 注册响应
252
+ interface RegisterResponse {
253
+ success: boolean;
254
+ message: string;
255
+ userId: string;
256
+ }
257
+
258
+ // OAuth 配置
259
+ interface OAuthConfig {
260
+ google?: {
261
+ clientId: string;
262
+ redirectUri: string;
263
+ scope?: string; // 默认: 'openid email profile'
264
+ };
265
+ discord?: {
266
+ clientId: string;
267
+ redirectUri: string;
268
+ scope?: string; // 默认: 'identify email'
269
+ };
270
+ github?: {
271
+ clientId: string;
272
+ redirectUri: string;
273
+ scope?: string; // 默认: 'read:user user:email'
274
+ };
275
+ }
276
+ ```
277
+
278
+ ## 完整示例
279
+
280
+ ```html
281
+ <!DOCTYPE html>
282
+ <html>
283
+ <head>
284
+ <meta charset="UTF-8">
285
+ <title>登录示例</title>
286
+ <link rel="stylesheet" href="node_modules/@seaverseai/auth-sdk/src/auth-modal.css">
287
+ </head>
288
+ <body>
289
+ <h1>欢迎</h1>
290
+ <button id="loginBtn">登录</button>
291
+
292
+ <script type="module">
293
+ import { SeaVerseBackendAPIClient, createAuthModal } from '@seaverseai/auth-sdk';
294
+
295
+ const client = new SeaVerseBackendAPIClient({
296
+ baseURL: 'https://account-hub.sg.seaverse.dev',
297
+ });
298
+
299
+ const authModal = createAuthModal({
300
+ client,
301
+ theme: 'dark',
302
+
303
+ oauthConfig: {
304
+ google: {
305
+ clientId: 'YOUR_GOOGLE_CLIENT_ID',
306
+ redirectUri: window.location.origin + '/callback.html',
307
+ },
308
+ },
309
+
310
+ onLoginSuccess: (token, user) => {
311
+ localStorage.setItem('token', token);
312
+ alert('登录成功: ' + user.email);
313
+ },
314
+ });
315
+
316
+ document.getElementById('loginBtn').onclick = () => {
317
+ authModal.show('login');
318
+ };
319
+ </script>
320
+ </body>
321
+ </html>
322
+ ```
323
+
324
+ ## React 集成
325
+
326
+ ```tsx
327
+ import { useEffect, useState } from 'react';
328
+ import { SeaVerseBackendAPIClient, createAuthModal } from '@seaverseai/auth-sdk';
329
+ import '@seaverseai/auth-sdk/src/auth-modal.css';
330
+
331
+ function App() {
332
+ const [authModal, setAuthModal] = useState(null);
333
+
334
+ useEffect(() => {
335
+ const client = new SeaVerseBackendAPIClient({
336
+ baseURL: 'https://account-hub.sg.seaverse.dev',
337
+ });
338
+
339
+ const modal = createAuthModal({
340
+ client,
341
+ onLoginSuccess: (token) => {
342
+ localStorage.setItem('token', token);
343
+ },
344
+ });
345
+
346
+ setAuthModal(modal);
347
+ return () => modal?.destroy();
348
+ }, []);
349
+
350
+ return (
351
+ <button onClick={() => authModal?.show('login')}>
352
+ 登录
353
+ </button>
354
+ );
355
+ }
356
+ ```
357
+
358
+ ## 常见问题
359
+
360
+ ### OAuth redirect_uri_mismatch 错误?
361
+
362
+ 确保 OAuth 应用配置中的重定向 URI 与代码中完全一致(包括协议、域名、端口、路径)。
363
+
364
+ ### 如何自动携带 Token?
365
+
366
+ ```typescript
367
+ import { AuthFactory } from '@seaverseai/auth-sdk';
368
+
369
+ const client = new SeaVerseBackendAPIClient({
370
+ baseURL: 'https://account-hub.sg.seaverse.dev',
371
+ auth: AuthFactory.create({
372
+ type: 'jwt',
373
+ credentials: {
374
+ type: 'jwt',
375
+ token: localStorage.getItem('token'),
376
+ },
377
+ }),
378
+ });
379
+ ```
380
+
381
+ ### 如何只启用部分 OAuth 登录?
382
+
383
+ 只配置需要的平台即可,未配置的平台按钮点击时会提示错误:
384
+
385
+ ```typescript
386
+ oauthConfig: {
387
+ google: { /* ... */ },
388
+ // 不配置 discord 和 github
389
+ }
390
+ ```
391
+
392
+ ### 本地开发如何测试 OAuth?
393
+
394
+ 大多数 OAuth 提供商允许 `http://localhost:PORT` 作为开发环境的重定向 URI。
395
+
396
+ ## 开发
397
+
398
+ ```bash
399
+ # 安装依赖
400
+ pnpm install
401
+
402
+ # 构建
403
+ pnpm run build
404
+
405
+ # 测试
406
+ pnpm test
407
+ ```
408
+
409
+ ## License
410
+
411
+ MIT © SeaVerse Team
412
+
413
+ ## 技术支持
414
+
415
+ - 📧 Email: support@seaverse.com
416
+ - 🐛 Issues: https://github.com/seaverseai/sv-sdk/issues
417
+ - 📖 Docs: https://docs.seaverse.dev
@@ -0,0 +1 @@
1
+ :root{--font-display:-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;--font-sans:-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;--color-neon-green:#10b981;--color-text-primary:#fff;--color-text-secondary:#a1a1aa;--color-text-tertiary:#71717a;--gradient-neon:linear-gradient(135deg,#10b981,#06b6d4)}.auth-modal{align-items:center;animation:modalFadeIn .3s ease-out;display:flex;inset:0;justify-content:center;overflow:hidden;padding:20px;position:fixed;z-index:9999}.auth-modal.hidden{display:none}@keyframes modalFadeIn{0%{opacity:0}to{opacity:1}}.auth-modal-backdrop{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);background:rgba(0,0,0,.85);inset:0;position:absolute}.auth-modal-content{animation:modalSlideUp .4s cubic-bezier(.4,0,.2,1);background:#0d0d0d;border:1px solid hsla(0,0%,100%,.08);border-radius:20px;box-shadow:0 25px 80px rgba(0,0,0,.8);display:grid;grid-template-columns:1fr 1fr;max-height:90vh;max-width:900px;overflow:hidden;position:relative;transition:max-width .3s ease;width:100%;z-index:10}.auth-modal-content:has(#loginForm:not(.hidden)){grid-template-columns:1fr auto;max-width:none;width:auto}.auth-modal-content:has(#loginForm:not(.hidden)) .auth-modal-right{min-width:0;width:auto}.auth-modal-content:has(#authMessage:not(.hidden)){grid-template-columns:1fr;max-width:480px}.auth-modal-content:has(#authMessage:not(.hidden)) .auth-modal-left{display:none}@keyframes modalSlideUp{0%{opacity:0;transform:translateY(30px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.auth-modal-left{background:#000;border-right:1px solid hsla(0,0%,100%,.05);display:flex;flex-direction:column;overflow:hidden;padding:32px;position:relative}.auth-modal-left:before{animation:meshRotate 20s linear infinite;background:radial-gradient(at 0 0,rgba(99,102,241,.3) 0,transparent 50%),radial-gradient(at 50% 0,rgba(139,92,246,.3) 0,transparent 50%),radial-gradient(at 100% 0,rgba(236,72,153,.3) 0,transparent 50%),radial-gradient(at 0 50%,rgba(16,185,129,.3) 0,transparent 50%),radial-gradient(at 100% 50%,rgba(244,63,94,.3) 0,transparent 50%),radial-gradient(at 0 100%,rgba(236,72,153,.3) 0,transparent 50%),radial-gradient(at 100% 100%,rgba(139,92,246,.3) 0,transparent 50%);content:"";filter:blur(80px);height:200%;inset:-50%;opacity:.6;position:absolute;width:200%}@keyframes meshRotate{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.auth-modal-left:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence baseFrequency='.65' numOctaves='3' stitchTiles='stitch' type='fractalNoise'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)' opacity='.05'/%3E%3C/svg%3E");content:"";inset:0;mix-blend-mode:overlay;opacity:.4;pointer-events:none;position:absolute}.auth-modal-logo{align-items:center;display:flex;gap:10px;position:relative;z-index:10}.auth-modal-logo .logo-icon{filter:drop-shadow(0 0 10px rgba(16,185,129,.3));height:28px;width:28px}.auth-modal-logo .logo-text{color:#fff;font-family:var(--font-display);font-size:18px;font-weight:700;text-shadow:0 0 20px rgba(0,0,0,.5)}.auth-modal-decoration{inset:0;overflow:hidden;pointer-events:none;position:absolute}.auth-modal-decoration .glow-orb-1{animation:breathe 8s ease-in-out infinite;background:radial-gradient(circle,hsla(0,0%,100%,.05) 0,transparent 70%);border-radius:50%;filter:blur(60px);height:400px;left:50%;pointer-events:none;position:absolute;top:50%;transform:translate(-50%,-50%);width:400px}@keyframes breathe{0%,to{opacity:.5;transform:translate(-50%,-50%) scale(1)}50%{opacity:.8;transform:translate(-50%,-50%) scale(1.2)}}.auth-modal-decoration .glow-orb-2{animation:breathe 10s ease-in-out infinite;animation-delay:2s;background:radial-gradient(circle,rgba(139,92,246,.08) 0,transparent 70%);border-radius:50%;filter:blur(50px);height:300px;pointer-events:none;position:absolute;right:-10%;top:20%;width:300px}.auth-modal-branding{margin-bottom:48px;margin-top:auto;position:relative;z-index:10}.auth-modal-branding .branding-title{color:#fff;font-family:var(--font-display);font-size:36px;font-weight:700;letter-spacing:-.02em;line-height:1.15;margin-bottom:12px;text-shadow:0 2px 10px rgba(0,0,0,.3)}.auth-modal-branding .branding-subtitle{color:hsla(0,0%,100%,.8);font-size:14px;line-height:1.6;max-width:260px}.auth-modal-right{background:#121212;display:flex;flex-direction:column;max-height:90vh;overflow-y:auto;padding:56px 48px 42px;position:relative}.auth-modal-right::-webkit-scrollbar{width:6px}.auth-modal-right::-webkit-scrollbar-track{background:transparent}.auth-modal-right::-webkit-scrollbar-thumb{background:hsla(0,0%,100%,.1);border-radius:3px}.auth-modal-close{align-items:center;background:transparent;border:none;border-radius:8px;color:hsla(0,0%,100%,.5);cursor:pointer;display:flex;height:36px;justify-content:center;position:absolute;right:16px;top:16px;transition:all .2s ease;width:36px;z-index:20}.auth-modal-close:hover{background:hsla(0,0%,100%,.1);color:#fff}.auth-form-view{animation:formFadeIn .3s ease-out}#loginForm .form-input{width:360px}@keyframes formFadeIn{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.auth-form-view.hidden{display:none}.auth-form-header{margin-bottom:28px;text-align:center}.auth-form-title{color:#fff;font-family:var(--font-display);font-size:26px;font-weight:700;letter-spacing:-.02em;margin-bottom:8px}.auth-form-subtitle{color:var(--color-text-secondary);font-size:14px}.auth-form{gap:16px}.auth-form,.form-group{display:flex;flex-direction:column}.form-group{gap:6px}.form-label{font-size:12px;font-weight:500;letter-spacing:.02em;opacity:.9;text-transform:uppercase}.form-input,.form-label{color:var(--color-text-primary)}.form-input{background:rgba(30,30,30,.8);border:1px solid hsla(0,0%,100%,.1);border-radius:8px;font-family:var(--font-sans);font-size:14px;height:40px;outline:none;padding:0 14px;transition:all .2s ease;width:100%}.form-input::placeholder{color:hsla(0,0%,100%,.35)}.form-input:hover{border-color:hsla(0,0%,100%,.15)}.form-input:focus{background:rgba(30,30,30,.9);border-color:#10b981;box-shadow:0 0 0 2px rgba(16,185,129,.15)}.form-input:invalid:not(:placeholder-shown){border-color:#ff006e}.input-with-icon{align-items:center;display:flex;position:relative}.input-with-icon .form-input{padding-right:40px}.input-icon-btn{align-items:center;background:transparent;border:none;border-radius:4px;color:hsla(0,0%,100%,.4);cursor:pointer;display:flex;justify-content:center;padding:6px;position:absolute;right:8px;transition:color .2s ease}.input-icon-btn:hover{background:hsla(0,0%,100%,.05);color:hsla(0,0%,100%,.7)}.input-icon-btn .hidden{display:none}.strength-text{align-items:center;color:var(--color-text-tertiary);display:flex;font-size:12px;font-weight:500;gap:6px;margin-top:4px}.form-group-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:8px}.form-group-header .form-label{margin-bottom:0}.forgot-password-link{color:hsla(0,0%,100%,.5);font-size:13px;font-weight:500;position:relative;text-decoration:none;transition:all .25s ease}.forgot-password-link:after{background:#10b981;bottom:-2px;content:"";height:1px;left:0;position:absolute;transition:width .3s ease;width:0}.forgot-password-link:hover{color:#10b981}.forgot-password-link:hover:after{width:100%}.link-primary{color:var(--color-neon-green);font-weight:600;position:relative;text-decoration:none;text-shadow:0 0 10px rgba(0,255,136,.3);transition:all .2s ease}.link-primary:after{background:var(--color-neon-green);bottom:-2px;content:"";height:1px;left:0;position:absolute;transform:scaleX(0);transform-origin:right;transition:transform .3s ease;width:100%}.link-primary:hover{color:#0f8;text-shadow:0 0 20px rgba(0,255,136,.5)}.link-primary:hover:after{transform:scaleX(1);transform-origin:left}.btn-auth-primary{align-items:center;background:var(--gradient-neon);border:none;border-radius:10px;box-shadow:0 4px 12px rgba(16,185,129,.25);color:#000;cursor:pointer;display:flex;font-family:var(--font-sans);font-size:14px;font-weight:600;gap:8px;height:44px;justify-content:center;margin-top:8px;overflow:hidden;padding:0 24px;position:relative;transition:all .3s cubic-bezier(.4,0,.2,1);width:100%}.btn-auth-primary:before{background:linear-gradient(135deg,transparent,hsla(0,0%,100%,.2) 50%,transparent);content:"";inset:0;opacity:0;position:absolute;transition:opacity .3s ease}.btn-auth-primary:hover{box-shadow:0 8px 20px rgba(16,185,129,.35);transform:translateY(-2px)}.btn-auth-primary:hover:before{opacity:1}.btn-auth-primary:active{box-shadow:0 2px 8px rgba(16,185,129,.25);transform:translateY(0)}.btn-auth-primary:disabled{cursor:not-allowed;opacity:.6;transform:none}.btn-auth-primary .btn-text{position:relative;z-index:2}.btn-auth-primary .btn-loader{align-items:center;display:flex;justify-content:center;position:relative;z-index:2}.btn-auth-primary .btn-loader.hidden{display:none}.spinner{animation:spin .8s linear infinite;fill:none;height:20px;stroke-width:3;width:20px}@keyframes spin{to{transform:rotate(1turn)}}.spinner-track{stroke:rgba(0,0,0,.2)}.spinner-circle{animation:spinDash 1.2s ease-in-out infinite;stroke:#000;stroke-dasharray:50;stroke-dashoffset:50}@keyframes spinDash{0%{stroke-dashoffset:50}50%{stroke-dashoffset:12.5}to{stroke-dashoffset:50}}.forgot-password-icon{align-items:center;display:flex;height:100px;justify-content:center;margin:0 auto 24px;position:relative;width:100px}.forgot-password-icon .icon-glow{animation:pulseGlow 3s ease-in-out infinite;background:radial-gradient(circle,rgba(16,185,129,.25) 0,transparent 70%);border-radius:50%;filter:blur(15px);inset:-10px;position:absolute}@keyframes pulseGlow{0%,to{opacity:.5;transform:scale(.95)}50%{opacity:.8;transform:scale(1.05)}}.forgot-password-icon .icon-lock{animation:floatIcon 4s ease-in-out infinite;color:#10b981;filter:drop-shadow(0 0 10px rgba(16,185,129,.4));position:relative;z-index:2}@keyframes floatIcon{0%,to{transform:translateY(0)}50%{transform:translateY(-6px)}}.btn-forgot-password{align-items:center;display:flex;gap:8px;justify-content:center}.btn-forgot-password .btn-arrow{transition:transform .3s ease}.btn-forgot-password:hover .btn-arrow{transform:translateX(4px)}.auth-footer{margin-top:24px;text-align:center}.forgot-footer{display:flex;justify-content:center}.back-to-login{align-items:center;border-radius:6px;color:var(--color-text-secondary);display:inline-flex;font-size:14px;font-weight:500;gap:8px;padding:8px 12px;text-decoration:none;transition:all .2s ease}.back-to-login:hover{background:rgba(16,185,129,.05);color:var(--color-neon-green)}.back-to-login svg{transition:transform .3s ease}.back-to-login:hover svg{transform:translateX(-4px)}.auth-message-content{align-items:center;display:flex;flex-direction:column;padding:24px;text-align:center}.message-icon{align-items:center;background:rgba(16,185,129,.1);border-radius:50%;color:#10b981;display:flex;height:80px;justify-content:center;margin-bottom:24px;width:80px}.message-icon svg{animation:successPulse 2s ease-in-out infinite;filter:drop-shadow(0 0 10px rgba(16,185,129,.3))}@keyframes successPulse{0%,to{transform:scale(1)}50%{transform:scale(1.05)}}.message-title{color:#fff;font-family:var(--font-display);font-size:24px;font-weight:700;margin-bottom:12px}.message-text{color:var(--color-text-secondary);font-size:14px;line-height:1.6;margin-bottom:24px}.divider{align-items:center;color:var(--color-text-tertiary);display:flex;font-size:11px;font-weight:600;gap:12px;letter-spacing:.08em;margin:24px 0 20px;position:relative;text-align:center}.divider:after,.divider:before{background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.1),transparent);content:"";flex:1;height:1px}.social-buttons-grid{display:grid;gap:12px;grid-template-columns:1fr 1fr;margin-bottom:12px}.btn-social{align-items:center;background:rgba(30,30,30,.8);border:1px solid hsla(0,0%,100%,.1);border-radius:10px;color:var(--color-text-primary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:14px;font-weight:500;gap:10px;height:44px;justify-content:center;overflow:hidden;padding:0 16px;position:relative;transition:all .25s cubic-bezier(.4,0,.2,1)}.btn-social:before{background:linear-gradient(135deg,transparent,hsla(0,0%,100%,.05) 50%,transparent);content:"";inset:0;opacity:0;position:absolute;transition:opacity .3s ease}.btn-social:hover{background:rgba(40,40,40,.9);border-color:hsla(0,0%,100%,.2);box-shadow:0 4px 12px rgba(0,0,0,.3);transform:translateY(-2px)}.btn-social:hover:before{opacity:1}.btn-social:active{transform:translateY(0)}.btn-social svg{flex-shrink:0;height:20px;width:20px}.btn-social span{position:relative;white-space:nowrap;z-index:2}.btn-social-full{align-items:center;background:rgba(30,30,30,.8);border:1px solid hsla(0,0%,100%,.1);border-radius:10px;color:var(--color-text-primary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:14px;font-weight:500;gap:10px;height:44px;justify-content:center;overflow:hidden;padding:0 16px;position:relative;transition:all .25s cubic-bezier(.4,0,.2,1);width:100%}.btn-social-full:before{background:linear-gradient(135deg,transparent,hsla(0,0%,100%,.05) 50%,transparent);content:"";inset:0;opacity:0;position:absolute;transition:opacity .3s ease}.btn-social-full:hover{background:rgba(40,40,40,.9);border-color:hsla(0,0%,100%,.2);box-shadow:0 4px 12px rgba(0,0,0,.3);transform:translateY(-2px)}.btn-social-full:hover:before{opacity:1}.btn-social-full:active{transform:translateY(0)}.btn-social-full svg{flex-shrink:0;height:20px;width:20px}.btn-social-full span{position:relative;z-index:2}@media (max-width:768px){.auth-modal-content{border-radius:0;grid-template-columns:1fr;max-height:100vh;max-width:100%}.auth-modal-left{display:none}.auth-modal-right{max-height:100vh;padding:24px}.auth-form-title{font-size:22px}#loginForm .form-input{width:100%}.btn-auth-primary{font-size:15px;height:48px}.social-buttons-grid{gap:10px;grid-template-columns:1fr}.btn-social,.btn-social-full{height:48px}}@media (max-width:480px){.auth-modal{padding:0}.auth-modal-content{border-radius:0;max-height:100vh}.auth-modal-right{padding:20px 16px}.auth-form-header{margin-bottom:20px}}