flowlink-auth 2.7.3 → 2.7.5

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/src/SignUp.jsx ADDED
@@ -0,0 +1,747 @@
1
+ // // src/signup.jsx
2
+ // 'use client'
3
+ // import React, { useState } from 'react'
4
+ // import { useAuth } from './provider.js'
5
+
6
+ // export default function SignUp() {
7
+ // const {
8
+ // publishableKey,
9
+ // baseUrl,
10
+ // redirect,
11
+ // redirectTo,
12
+ // user,
13
+ // loadingUser,
14
+ // fetchMe,
15
+ // setUser
16
+ // } = useAuth()
17
+
18
+ // const [name, setName] = useState('')
19
+ // const [email, setEmail] = useState('')
20
+ // const [password, setPassword] = useState('')
21
+ // const [loading, setLoading] = useState(false)
22
+ // const [error, setError] = useState(null)
23
+ // const [message, setMessage] = useState(null)
24
+
25
+ // if (loadingUser) return null
26
+
27
+ // if (user && redirect) {
28
+ // if (typeof redirectTo === 'function') redirectTo(redirect)
29
+ // else if (typeof window !== 'undefined') window.location.assign(redirect)
30
+ // return null
31
+ // }
32
+
33
+ // async function submit(e) {
34
+ // e.preventDefault()
35
+ // setError(null)
36
+ // setMessage(null)
37
+ // setLoading(true)
38
+
39
+ // const url = `${(baseUrl || '').replace(/\/+$/, '')}/api/sdk/signup`
40
+
41
+ // try {
42
+ // const res = await fetch(url, {
43
+ // method: 'POST',
44
+ // credentials: 'include',
45
+ // headers: {
46
+ // 'Content-Type': 'application/json',
47
+ // 'x-publishable-key': publishableKey || ''
48
+ // },
49
+ // body: JSON.stringify({ name, email, password })
50
+ // })
51
+
52
+ // const data = await res.json().catch(() => ({}))
53
+ // if (!res.ok) throw new Error(data.error || 'Signup failed')
54
+
55
+ // if (data.user && typeof setUser === 'function') setUser(data.user)
56
+ // if (typeof fetchMe === 'function') await fetchMe()
57
+
58
+ // setMessage('Account created. Redirecting…')
59
+
60
+ // if (redirect) {
61
+ // setTimeout(() => {
62
+ // if (typeof redirectTo === 'function') redirectTo(redirect)
63
+ // else if (typeof window !== 'undefined') window.location.assign(redirect)
64
+ // }, 300)
65
+ // }
66
+ // } catch (err) {
67
+ // setError(err?.message ?? 'Network error')
68
+ // console.error('Signup error:', err)
69
+ // } finally {
70
+ // setLoading(false)
71
+ // }
72
+ // }
73
+
74
+ // async function startOAuthFlow(provider) {
75
+ // // prevent double clicks
76
+ // setError(null)
77
+ // setLoading(true)
78
+
79
+ // try {
80
+ // const rid =
81
+ // (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')
82
+ // ? crypto.randomUUID()
83
+ // : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`
84
+
85
+ // // callback must match what your SDK start expects and what Google console allows
86
+ // const callbackUrl = encodeURIComponent(`${window.location.origin}/signup`)
87
+
88
+ // // build start URL (server returns { oauthUrl })
89
+ // const sdkBase = (typeof process !== 'undefined' && process.env && process.env.NEXT_PUBLIC_FLOWLINK_BASE_URL)
90
+ // || baseUrl || window.location.origin.replace(/\/+$/, '')
91
+ // const startUrl = `${sdkBase}/sdk/auth/start?rid=${rid}&source=${encodeURIComponent(provider)}&callbackUrl=${callbackUrl}`
92
+
93
+ // // ensure publishableKey exists
94
+ // if (!publishableKey) {
95
+ // throw new Error('Missing publishable key (client side). Set NEXT_PUBLIC_FLOWLINK_PUBLISHABLE_KEY or provide publishableKey in provider.')
96
+ // }
97
+
98
+ // const res = await fetch(startUrl, {
99
+ // method: 'GET',
100
+ // headers: {
101
+ // 'x-publishable-key': publishableKey
102
+ // }
103
+ // })
104
+
105
+ // const data = await res.json().catch(() => null)
106
+ // if (!res.ok) {
107
+ // throw new Error(data?.error || `OAuth start failed (${res.status})`)
108
+ // }
109
+ // if (!data?.oauthUrl) {
110
+ // throw new Error('SDK start did not return oauthUrl')
111
+ // }
112
+
113
+ // // navigate to provider (Google/GitHub)
114
+ // window.location.href = data.oauthUrl
115
+ // } catch (err) {
116
+ // console.error('OAuth start error:', err)
117
+ // setError(err?.message || 'OAuth start failed')
118
+ // setLoading(false)
119
+ // }
120
+ // }
121
+
122
+ // const handleGoogle = (e) => {
123
+ // if (e && typeof e.preventDefault === 'function') e.preventDefault()
124
+ // startOAuthFlow('google')
125
+ // }
126
+
127
+ // const handleGithub = (e) => {
128
+ // if (e && typeof e.preventDefault === 'function') e.preventDefault()
129
+ // startOAuthFlow('github')
130
+ // }
131
+ // return (
132
+ // <div style={overlay}>
133
+ // <div style={modal}>
134
+ // <h2 style={title}>Create account</h2>
135
+
136
+ // <form onSubmit={submit}>
137
+ // <label style={label}>Full name</label>
138
+ // <input
139
+ // name="name"
140
+ // style={input}
141
+ // value={name}
142
+ // onChange={e => setName(e.target.value)}
143
+ // autoComplete="name"
144
+ // />
145
+
146
+ // <label style={label}>Email</label>
147
+ // <input
148
+ // name="email"
149
+ // type="email"
150
+ // style={input}
151
+ // value={email}
152
+ // onChange={e => setEmail(e.target.value)}
153
+ // required
154
+ // autoComplete="email"
155
+ // />
156
+
157
+ // <label style={label}>Password</label>
158
+ // <input
159
+ // name="password"
160
+ // type="password"
161
+ // style={input}
162
+ // value={password}
163
+ // onChange={e => setPassword(e.target.value)}
164
+ // required
165
+ // autoComplete="new-password"
166
+ // />
167
+
168
+ // <div style={{ marginTop: 12 }}>
169
+ // <button style={button} type="submit" disabled={loading}>
170
+ // {loading ? 'Creating...' : 'Create account'}
171
+ // </button>
172
+ // </div>
173
+
174
+ // <div style={{ display: 'flex', gap: 8, marginTop: 16 }}>
175
+ // <button
176
+ // type="button"
177
+ // onClick={handleGoogle}
178
+ // style={oauthButtonGoogle}
179
+ // >
180
+ // Continue with Google
181
+ // </button>
182
+
183
+ // <button
184
+ // type="button"
185
+ // onClick={handleGithub}
186
+ // style={oauthButtonGithub}
187
+ // >
188
+ // Continue with GitHub
189
+ // </button>
190
+ // </div>
191
+
192
+ // {error && <div style={errorBox}>{error}</div>}
193
+ // {message && <div style={successBox}>{message}</div>}
194
+ // </form>
195
+ // </div>
196
+ // </div>
197
+ // )
198
+ // }
199
+
200
+ // const overlay = {
201
+ // position: 'fixed',
202
+ // inset: 0,
203
+ // background: 'rgba(0,0,0,0.5)',
204
+ // display: 'flex',
205
+ // justifyContent: 'center',
206
+ // alignItems: 'center',
207
+ // padding: 20,
208
+ // zIndex: 9999
209
+ // }
210
+ // const modal = {
211
+ // width: '100%',
212
+ // maxWidth: 420,
213
+ // background: '#0f1724',
214
+ // borderRadius: 12,
215
+ // padding: 24,
216
+ // color: '#fff'
217
+ // }
218
+ // const title = { margin: 0, fontSize: 20, fontWeight: 600 }
219
+ // const label = { marginTop: 10, display: 'block', fontSize: 13 }
220
+ // const input = {
221
+ // width: '100%',
222
+ // padding: '10px 12px',
223
+ // borderRadius: 8,
224
+ // background: '#0b1220',
225
+ // color: '#fff',
226
+ // border: '1px solid #1e293b',
227
+ // marginTop: 6
228
+ // }
229
+ // const button = {
230
+ // marginTop: 15,
231
+ // width: '100%',
232
+ // padding: '10px 12px',
233
+ // borderRadius: 8,
234
+ // background: '#2563eb',
235
+ // border: 'none',
236
+ // color: '#fff',
237
+ // fontWeight: 600,
238
+ // cursor: 'pointer'
239
+ // }
240
+ // const oauthButtonGoogle = {
241
+ // flex: 1,
242
+ // padding: '10px 12px',
243
+ // borderRadius: 8,
244
+ // background: '#db4437',
245
+ // color: '#fff',
246
+ // border: 'none',
247
+ // cursor: 'pointer'
248
+ // }
249
+ // const oauthButtonGithub = {
250
+ // flex: 1,
251
+ // padding: '10px 12px',
252
+ // borderRadius: 8,
253
+ // background: '#24292f',
254
+ // color: '#fff',
255
+ // border: 'none',
256
+ // cursor: 'pointer'
257
+ // }
258
+ // const errorBox = { marginTop: 10, color: '#ffb4b4', fontSize: 13 }
259
+ // const successBox = { marginTop: 10, color: '#bef264', fontSize: 13 }
260
+
261
+ 'use client'
262
+ import React, { useState, useRef, useEffect } from 'react'
263
+ import { ToastContainer, toast } from 'react-toastify'
264
+ import 'react-toastify/dist/ReactToastify.css'
265
+ import { useAuth } from './provider.js'
266
+
267
+ export default function SignUp() {
268
+ const {
269
+ publishableKey,
270
+ baseUrl,
271
+ redirect,
272
+ redirectTo,
273
+ user,
274
+ loadingUser,
275
+ fetchMe,
276
+ setUser
277
+ } = useAuth()
278
+
279
+ const [name, setName] = useState('')
280
+ const [email, setEmail] = useState('')
281
+ const [password, setPassword] = useState('')
282
+ const [loading, setLoading] = useState(false)
283
+ const [message, setMessage] = useState(null)
284
+ const redirectTimer = useRef(null)
285
+
286
+ useEffect(() => {
287
+ return () => {
288
+ if (redirectTimer.current) clearTimeout(redirectTimer.current)
289
+ }
290
+ }, [])
291
+
292
+ if (loadingUser) return null
293
+
294
+ if (user && redirect) {
295
+ if (typeof redirectTo === 'function') redirectTo(redirect)
296
+ else if (typeof window !== 'undefined') window.location.assign(redirect)
297
+ return null
298
+ }
299
+
300
+ async function submit(e) {
301
+ e.preventDefault()
302
+ if (loading) return
303
+ setMessage(null)
304
+ setLoading(true)
305
+
306
+ const url = `${(baseUrl || '').replace(/\/+$/, '')}/api/sdk/signup`
307
+
308
+ try {
309
+ const res = await fetch(url, {
310
+ method: 'POST',
311
+ credentials: 'include',
312
+ headers: {
313
+ 'Content-Type': 'application/json',
314
+ 'x-publishable-key': publishableKey || ''
315
+ },
316
+ body: JSON.stringify({ name, email, password })
317
+ })
318
+
319
+ const data = await res.json().catch(async () => {
320
+ const text = await res.text().catch(() => '')
321
+ return { _raw: text }
322
+ })
323
+ if (!res.ok) {
324
+ const errMsg = data?.error || data?._raw || `Signup failed (${res.status})`
325
+ throw new Error(errMsg)
326
+ }
327
+
328
+ if (data.user && typeof setUser === 'function') setUser(data.user)
329
+ if (typeof fetchMe === 'function') await fetchMe()
330
+
331
+ setMessage('Account created. Redirecting…')
332
+ toast.success('Account created. Redirecting…')
333
+
334
+ if (redirect) {
335
+ redirectTimer.current = setTimeout(() => {
336
+ if (typeof redirectTo === 'function') redirectTo(redirect)
337
+ else if (typeof window !== 'undefined') window.location.assign(redirect)
338
+ }, 300)
339
+ }
340
+ } catch (err) {
341
+ const text = err?.message ?? 'Network error'
342
+ toast.error(text)
343
+ console.error('Signup error:', err)
344
+ } finally {
345
+ setLoading(false)
346
+ }
347
+
348
+
349
+ }
350
+
351
+ async function startOAuthFlow(provider) {
352
+ if (loading) return
353
+ // prevent double clicks
354
+ setLoading(true)
355
+
356
+ try {
357
+ const rid =
358
+ (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')
359
+ ? crypto.randomUUID()
360
+ : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`
361
+
362
+ // callback must match what your SDK start expects and what Google console allows
363
+ const callbackUrl = encodeURIComponent(`${window.location.origin}/signup`)
364
+
365
+ // build start URL (server returns { oauthUrl })
366
+ const sdkBase = baseUrl || window.location.origin.replace(/\/+$/, '')
367
+ const startUrl = `${sdkBase}/sdk/auth/start?rid=${rid}&source=${encodeURIComponent(provider)}&callbackUrl=${callbackUrl}`
368
+
369
+ // ensure publishableKey exists
370
+ if (!publishableKey) {
371
+ throw new Error('Missing publishable key (client side). Set NEXT_PUBLIC_FLOWLINK_PUBLISHABLE_KEY or provide publishableKey in provider.')
372
+ }
373
+
374
+ const res = await fetch(startUrl, {
375
+ method: 'GET',
376
+ headers: {
377
+ 'x-publishable-key': publishableKey
378
+ }
379
+ })
380
+
381
+ const data = await res.json().catch(() => null)
382
+ if (!res.ok) {
383
+ throw new Error(data?.error || `OAuth start failed (${res.status})`)
384
+ }
385
+ if (!data?.oauthUrl) {
386
+ throw new Error('SDK start did not return oauthUrl')
387
+ }
388
+
389
+ // navigate to provider (Google/GitHub)
390
+ window.location.href = data.oauthUrl
391
+ } catch (err) {
392
+ console.error('OAuth start error:', err)
393
+ toast.error(err?.message || 'OAuth start failed')
394
+ setLoading(false)
395
+ }
396
+
397
+
398
+ }
399
+
400
+ const handleGoogle = (e) => {
401
+ if (e && typeof e.preventDefault === 'function') e.preventDefault()
402
+ startOAuthFlow('google')
403
+ }
404
+
405
+ const handleGithub = (e) => {
406
+ if (e && typeof e.preventDefault === 'function') e.preventDefault()
407
+ startOAuthFlow('github')
408
+ }
409
+
410
+ return (
411
+ <div style={page}>
412
+ <ToastContainer position="top-right" autoClose={5000} />
413
+ <div style={card}>
414
+ <div style={cardInner}>
415
+ <div style={brand}>
416
+ <div style={brandRow}>
417
+ <div style={logoPlaceholder} aria-hidden />
418
+ <h1 style={brandTitle}>Create account</h1>
419
+ </div>
420
+ <div style={brandSub}>
421
+ <div style={brandLead}>Sign up to continue</div>
422
+ <div style={brandMuted}>Welcome! Create your account.</div>
423
+ </div>
424
+ </div>
425
+
426
+ <div style={oauthRow}>
427
+ <button
428
+ onClick={handleGoogle}
429
+ type="button"
430
+ style={{ ...oauthButton, ...oauthGoogle }}
431
+ disabled={loading}
432
+ aria-disabled={loading}
433
+ >
434
+ <svg width={18} style={{ marginRight: 10 }} viewBox="-3 0 262 262" xmlns="http://www.w3.org/2000/svg" fill="#000000" aria-hidden>
435
+ <path d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622 38.755 30.023 2.685.268c24.659-22.774 38.875-56.282 38.875-96.027" fill="#4285F4"></path>
436
+ <path d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055-34.523 0-63.824-22.773-74.269-54.25l-1.531.13-40.298 31.187-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1" fill="#34A853"></path>
437
+ <path d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82 0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602l42.356-32.782" fill="#FBBC05"></path>
438
+ <path d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0 79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251" fill="#EB4335"></path>
439
+ </svg>
440
+ <span>{loading ? 'Loading...' : 'Continue with Google'}</span>
441
+ </button>
442
+
443
+ <button
444
+ onClick={handleGithub}
445
+ type="button"
446
+ style={{ ...oauthButton, ...oauthGithub }}
447
+ disabled={loading}
448
+ aria-disabled={loading}
449
+ >
450
+ <svg width={18} style={{ marginRight: 10 }} xmlns="http://www.w3.org/2000/svg" fill="white" viewBox="0 0 20 20" aria-hidden>
451
+ <path fillRule="evenodd" d="M10 .333A9.911 9.911 0 0 0 6.866 19.65c.5.092.678-.215.678-.477 0-.237-.01-1.017-.014-1.845-2.757.6-3.338-1.169-3.338-1.169a2.627 2.627 0 0 0-1.1-1.451c-.9-.615.07-.6.07-.6a2.084 2.084 0 0 1 1.518 1.021 2.11 2.11 0 0 0 2.884.823c.044-.503.268-.973.63-1.325-2.2-.25-4.516-1.1-4.516-4.9A3.832 3.832 0 0 1 4.7 7.068a3.56 3.56 0 0 1 .095-2.623s.832-.266 2.726 1.016a9.409 9.409 0 0 1 4.962 0c1.89-1.282 2.717-1.016 2.717-1.016.366.83.402 1.768.1 2.623a3.827 3.827 0 0 1 1.02 2.659c0 3.807-2.319 4.644-4.525 4.889a2.366 2.366 0 0 1 .673 1.834c0 1.326-.012 2.394-.012 2.72 0 .263.18.572.681.475A9.911 9.911 0 0 0 10 .333Z" clipRule="evenodd" />
452
+ </svg>
453
+ <span>{loading ? 'Loading...' : 'Continue with GitHub'}</span>
454
+ </button>
455
+ </div>
456
+
457
+ <div style={dividerRow}>
458
+ <div style={line} />
459
+ <div style={orText}>or</div>
460
+ <div style={line} />
461
+ </div>
462
+
463
+ <form onSubmit={submit} style={form}>
464
+ <label style={label} htmlFor="name">
465
+ <span style={labelText}>Name</span>
466
+ <input
467
+ id="name"
468
+ type="text"
469
+ value={name}
470
+ onChange={e => setName(e.target.value)}
471
+ placeholder="Your name"
472
+ style={input}
473
+ autoComplete="name"
474
+ />
475
+ </label>
476
+
477
+ <label style={label} htmlFor="email">
478
+ <span style={labelText}>Email address</span>
479
+ <input
480
+ id="email"
481
+ type="email"
482
+ value={email}
483
+ onChange={e => setEmail(e.target.value)}
484
+ required
485
+ placeholder="you@example.com"
486
+ style={input}
487
+ autoComplete="email"
488
+ />
489
+ </label>
490
+
491
+ <label style={label} htmlFor="password">
492
+ <span style={labelText}>Password</span>
493
+ <input
494
+ id="password"
495
+ type="password"
496
+ value={password}
497
+ onChange={e => setPassword(e.target.value)}
498
+ required
499
+ placeholder="••••••••"
500
+ style={input}
501
+ autoComplete="new-password"
502
+ />
503
+ </label>
504
+
505
+ <button
506
+ type="submit"
507
+ style={submitButton}
508
+ disabled={loading}
509
+ aria-disabled={loading}
510
+ >
511
+ {loading ? 'Signing up...' : 'Sign Up'}
512
+ </button>
513
+ </form>
514
+ </div>
515
+
516
+ <div style={below}>
517
+ <div style={belowRow}>
518
+ <span style={muted}>Already have an account? </span>
519
+ <a href="/sign-in" style={link}>Sign in</a>
520
+ </div>
521
+ <div style={dividerThin} />
522
+ <div style={secured}>
523
+ <div style={securedText}>Secured by auth</div>
524
+ </div>
525
+ </div>
526
+ </div>
527
+ </div>
528
+
529
+
530
+ )
531
+ }
532
+
533
+ /* styles (JS objects) */
534
+ const page = {
535
+ position: 'fixed',
536
+ inset: 0,
537
+ display: 'flex',
538
+ alignItems: 'center',
539
+ justifyContent: 'center',
540
+ padding: 24,
541
+ background: 'linear-gradient(to bottom, #0b1220 0%, #071023 40%, #02040a 100%)',
542
+ color: '#f8e9d3',
543
+ minHeight: '100vh',
544
+ zIndex: 9999
545
+ }
546
+
547
+ const card = {
548
+ width: '100%',
549
+ maxWidth: 520,
550
+ borderRadius: 18,
551
+ background: 'linear-gradient(180deg, rgba(15,19,36,0.9) 0%, rgba(6,10,18,0.95) 100%)',
552
+ border: '1px solid rgba(148,163,184,0.06)',
553
+ boxShadow: '0 10px 30px rgba(2,6,23,0.6)',
554
+ overflow: 'hidden',
555
+ display: 'flex',
556
+ flexDirection: 'column',
557
+ gap: 12
558
+ }
559
+
560
+ const cardInner = {
561
+ padding: 26,
562
+ display: 'flex',
563
+ flexDirection: 'column',
564
+ gap: 18
565
+ }
566
+
567
+ const brand = {
568
+ display: 'flex',
569
+ flexDirection: 'column',
570
+ gap: 8
571
+ }
572
+
573
+ const brandRow = {
574
+ display: 'flex',
575
+ alignItems: 'center',
576
+ gap: 12
577
+ }
578
+
579
+ const logoPlaceholder = {
580
+ width: 36,
581
+ height: 36,
582
+ borderRadius: 999,
583
+ background: 'linear-gradient(135deg,#2b313a,#0f1724)'
584
+ }
585
+
586
+ const brandTitle = {
587
+ margin: 0,
588
+ fontSize: 20,
589
+ fontWeight: 600,
590
+ color: '#fff'
591
+ }
592
+
593
+ const brandSub = {
594
+ display: 'flex',
595
+ flexDirection: 'column',
596
+ gap: 4
597
+ }
598
+
599
+ const brandLead = {
600
+ fontSize: 15,
601
+ color: 'rgba(255,255,255,0.95)'
602
+ }
603
+
604
+ const brandMuted = {
605
+ fontSize: 13,
606
+ color: 'rgba(255,255,255,0.65)',
607
+ marginTop: 2
608
+ }
609
+
610
+ const oauthRow = {
611
+ display: 'flex',
612
+ gap: 10,
613
+ marginTop: 8
614
+ }
615
+
616
+ const oauthButton = {
617
+ flex: 1,
618
+ display: 'inline-flex',
619
+ alignItems: 'center',
620
+ justifyContent: 'center',
621
+ padding: '10px 12px',
622
+ borderRadius: 10,
623
+ border: '1px solid rgba(148,163,184,0.08)',
624
+ fontSize: 14,
625
+ cursor: 'pointer',
626
+ userSelect: 'none',
627
+ gap: 8
628
+ }
629
+
630
+ const oauthGoogle = {
631
+ background: 'rgba(255,255,255,0.06)',
632
+ color: '#fff'
633
+ }
634
+
635
+ const oauthGithub = {
636
+ background: 'rgba(255,255,255,0.03)',
637
+ color: '#fff'
638
+ }
639
+
640
+ const dividerRow = {
641
+ display: 'flex',
642
+ alignItems: 'center',
643
+ gap: 12,
644
+ marginTop: 14
645
+ }
646
+
647
+ const line = {
648
+ flex: 1,
649
+ height: 1,
650
+ background: 'rgba(148,163,184,0.06)'
651
+ }
652
+
653
+ const orText = {
654
+ fontSize: 13,
655
+ color: 'rgba(255,255,255,0.6)',
656
+ padding: '0 8px'
657
+ }
658
+
659
+ const form = {
660
+ display: 'flex',
661
+ flexDirection: 'column',
662
+ gap: 12,
663
+ marginTop: 6
664
+ }
665
+
666
+ const label = {
667
+ display: 'flex',
668
+ flexDirection: 'column',
669
+ gap: 6
670
+ }
671
+
672
+ const labelText = {
673
+ fontSize: 13,
674
+ color: 'rgba(255,255,255,0.66)'
675
+ }
676
+
677
+ const input = {
678
+ width: '100%',
679
+ padding: '10px 12px',
680
+ borderRadius: 10,
681
+ background: 'rgba(11,18,32,0.5)',
682
+ color: '#f8e9d3',
683
+ border: '1px solid rgba(148,163,184,0.06)',
684
+ fontSize: 14,
685
+ outline: 'none',
686
+ boxSizing: 'border-box'
687
+ }
688
+
689
+ const submitButton = {
690
+ marginTop: 6,
691
+ width: '100%',
692
+ padding: '10px 12px',
693
+ borderRadius: 10,
694
+ background: 'linear-gradient(180deg,#2563eb,#60a5fa)',
695
+ border: 'none',
696
+ color: '#fff',
697
+ fontWeight: 600,
698
+ cursor: 'pointer',
699
+ fontSize: 15,
700
+ display: 'inline-flex',
701
+ alignItems: 'center',
702
+ justifyContent: 'center'
703
+ }
704
+
705
+ const below = {
706
+ padding: '12px 20px 20px 20px',
707
+ borderTop: '1px solid rgba(148,163,184,0.03)',
708
+ display: 'flex',
709
+ flexDirection: 'column',
710
+ gap: 10
711
+ }
712
+
713
+ const belowRow = {
714
+ textAlign: 'center',
715
+ display: 'flex',
716
+ justifyContent: 'center',
717
+ gap: 8,
718
+ alignItems: 'center'
719
+ }
720
+
721
+ const muted = {
722
+ color: 'rgba(255,255,255,0.65)',
723
+ fontSize: 13
724
+ }
725
+
726
+ const link = {
727
+ color: '#3b82f6',
728
+ textDecoration: 'none',
729
+ fontWeight: 600
730
+ }
731
+
732
+ const dividerThin = {
733
+ height: 1,
734
+ background: 'rgba(148,163,184,0.05)',
735
+ marginTop: 6,
736
+ marginBottom: 6
737
+ }
738
+
739
+ const secured = {
740
+ textAlign: 'center'
741
+ }
742
+
743
+ const securedText = {
744
+ color: 'rgba(255,255,255,0.9)',
745
+ fontSize: 13,
746
+ fontWeight: 600
747
+ }