flowlink-auth 2.7.9 → 2.8.1

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 CHANGED
@@ -258,11 +258,12 @@
258
258
  // const errorBox = { marginTop: 10, color: '#ffb4b4', fontSize: 13 }
259
259
  // const successBox = { marginTop: 10, color: '#bef264', fontSize: 13 }
260
260
 
261
+ // src/signup.jsx
261
262
  // src/signup.jsx
262
263
  'use client'
263
264
  import React, { useState, useRef, useEffect } from 'react'
264
265
  import Link from 'next/link'
265
- import { useAuth } from './provider.js' // keep if your app provides it; safe to remove if not used
266
+ import { useAuth } from './provider.js' // keep if your SDK provides this; safe to remove if not used
266
267
 
267
268
  export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
268
269
  const {
@@ -282,41 +283,49 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
282
283
  const [loading, setLoading] = useState(false)
283
284
  const [loadingOauth, setLoadingOauth] = useState({ google: false, github: false })
284
285
  const redirectTimer = useRef(null)
286
+
287
+ // Local toast system (black background)
285
288
  const toastId = useRef(0)
286
- const [toasts, setToasts] = useState([]) // local toast system to avoid external libs (black background)
289
+ const [toasts, setToasts] = useState([])
287
290
 
288
291
  useEffect(() => {
292
+ // Soft-disable pinch-zoom on mobile while this component is mounted.
293
+ // This inserts a viewport meta tag and removes it on unmount.
294
+ const meta = document.createElement('meta')
295
+ meta.name = 'viewport'
296
+ meta.content = 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no'
297
+ document.head.appendChild(meta)
298
+
289
299
  return () => {
300
+ // cleanup timer & meta tag
290
301
  if (redirectTimer.current) clearTimeout(redirectTimer.current)
291
- // clear pending toast timers
292
- toasts.forEach(t => clearTimeout(t._timer))
302
+ const existing = document.querySelector('meta[name="viewport"]')
303
+ if (existing && existing.content === meta.content) {
304
+ document.head.removeChild(existing)
305
+ }
306
+ // clear any toast timers if present (we store timers on the toast objects)
307
+ toasts.forEach(t => {
308
+ if (t._timer) clearTimeout(t._timer)
309
+ })
293
310
  }
294
311
  // eslint-disable-next-line react-hooks/exhaustive-deps
295
312
  }, [])
296
313
 
297
- // simple toast helper (auto-dismiss). type: 'success' | 'error' | 'info'
314
+ // Toast helpers
298
315
  function showToast(type, message, ms = 5000) {
299
316
  const id = ++toastId.current
300
- const created = Date.now()
301
- const t = { id, type, message, created, _timer: null }
302
- // add toast
303
- setToasts(prev => {
304
- const next = [t, ...prev].slice(0, 6)
305
- return next
306
- })
307
- // schedule removal
317
+ const t = { id, type, message, _timer: null }
318
+ setToasts(prev => [t, ...prev].slice(0, 6))
308
319
  const timer = setTimeout(() => {
309
320
  setToasts(prev => prev.filter(x => x.id !== id))
310
321
  }, ms)
311
- // store timer so cleanup can clear it
312
322
  t._timer = timer
313
323
  }
314
324
 
315
- // remove toast immediately
316
325
  function removeToast(id) {
317
326
  setToasts(prev => {
318
327
  prev.forEach(t => {
319
- if (t.id === id) clearTimeout(t._timer)
328
+ if (t.id === id && t._timer) clearTimeout(t._timer)
320
329
  })
321
330
  return prev.filter(x => x.id !== id)
322
331
  })
@@ -330,6 +339,7 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
330
339
  return null
331
340
  }
332
341
 
342
+ // Prevent double submit
333
343
  async function submit(e) {
334
344
  e.preventDefault()
335
345
  if (loading) return
@@ -366,8 +376,8 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
366
376
  })
367
377
 
368
378
  const data = await res.json().catch(async () => {
369
- const text = await res.text().catch(() => '')
370
- return { _raw: text }
379
+ const txt = await res.text().catch(() => '')
380
+ return { _raw: txt }
371
381
  })
372
382
 
373
383
  if (!res.ok) {
@@ -379,7 +389,7 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
379
389
  if (typeof fetchMe === 'function') await fetchMe()
380
390
 
381
391
  showToast('success', 'Account created. Redirecting…')
382
- // redirect after tiny delay so user sees toast
392
+
383
393
  if (redirect) {
384
394
  redirectTimer.current = setTimeout(() => {
385
395
  if (typeof redirectTo === 'function') redirectTo(redirect)
@@ -387,9 +397,9 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
387
397
  }, 450)
388
398
  }
389
399
  } catch (err) {
390
- // present server errors as toast (black background)
391
400
  const text = err?.message ?? 'Network error'
392
401
  showToast('error', text)
402
+ // keep console error for integrators
393
403
  console.error('Signup error:', err)
394
404
  } finally {
395
405
  setLoading(false)
@@ -410,9 +420,7 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
410
420
  const sdkBase = baseUrl || (typeof window !== 'undefined' ? window.location.origin.replace(/\/+$/, '') : '')
411
421
  const startUrl = `${sdkBase}/sdk/auth/start?rid=${rid}&source=${encodeURIComponent(provider)}&callbackUrl=${callbackUrl}`
412
422
 
413
- if (!publishableKey) {
414
- throw new Error('Missing publishable key (client side).')
415
- }
423
+ if (!publishableKey) throw new Error('Missing publishable key (client side).')
416
424
 
417
425
  const res = await fetch(startUrl, {
418
426
  method: 'GET',
@@ -423,7 +431,7 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
423
431
  if (!res.ok) throw new Error(data?.error || `OAuth start failed (${res.status})`)
424
432
  if (!data?.oauthUrl) throw new Error('SDK start did not return oauthUrl')
425
433
 
426
- // navigate away to provider
434
+ // Navigate to provider (this will unload the page)
427
435
  if (typeof window !== 'undefined') window.location.href = data.oauthUrl
428
436
  } catch (err) {
429
437
  showToast('error', err?.message || 'OAuth start failed')
@@ -443,54 +451,51 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
443
451
  }
444
452
 
445
453
  return (
446
- <div style={overlay}>
447
- {/* toast container (custom) */}
448
- <div style={toastContainer}>
454
+ <div style={styles.overlay}>
455
+ {/* custom toast container (black background) */}
456
+ <div style={styles.toastContainer} aria-live="polite" aria-atomic="true">
449
457
  {toasts.map(t => (
450
458
  <div
451
459
  key={t.id}
452
460
  role="status"
453
- aria-live="polite"
454
461
  style={{
455
- ...toastBase,
456
- ...(t.type === 'error' ? toastError : t.type === 'success' ? toastSuccess : toastInfo)
462
+ ...styles.toastBase,
463
+ ...(t.type === 'error' ? styles.toastError : t.type === 'success' ? styles.toastSuccess : styles.toastInfo)
457
464
  }}
458
465
  onMouseEnter={() => {
459
- // pause dismiss
460
- clearTimeout(t._timer)
466
+ if (t._timer) clearTimeout(t._timer)
461
467
  }}
462
468
  onMouseLeave={() => {
463
- // restart dismiss for this toast
469
+ // restart auto-dismiss shorter when mouse leaves
464
470
  const timer = setTimeout(() => removeToast(t.id), 3000)
465
- // patch timer into state (imperative but fine for local toast)
466
471
  setToasts(prev => prev.map(x => x.id === t.id ? { ...x, _timer: timer } : x))
467
472
  }}
468
473
  >
469
474
  <div style={{ flex: 1 }}>{t.message}</div>
470
- <button aria-label="Dismiss" onClick={() => removeToast(t.id)} style={toastCloseBtn}>✕</button>
475
+ <button aria-label="Dismiss" onClick={() => removeToast(t.id)} style={styles.toastCloseBtn}>✕</button>
471
476
  </div>
472
477
  ))}
473
478
  </div>
474
479
 
475
- <div style={modal}>
476
- <div style={modalInner}>
477
- <header style={header}>
478
- <div style={brandRow}>
479
- <div style={logo}>
480
- <div style={logoCircle} aria-hidden />
480
+ <div style={styles.modal} role="dialog" aria-modal="true" aria-labelledby="signup-title">
481
+ <div style={styles.modalInner}>
482
+ <header style={styles.header}>
483
+ <div style={styles.brandRow}>
484
+ <div style={styles.logo} aria-hidden>
485
+ <div style={styles.logoCircle} />
481
486
  </div>
482
487
  <div>
483
- <h1 style={title}>Sign up to {agency?.name || 'App'}</h1>
484
- <div style={subtitle}>Welcome! Create your account.</div>
488
+ <h1 id="signup-title" style={styles.title}>Sign up to {agency?.name || 'App'}</h1>
489
+ <div style={styles.subtitle}>Welcome create your account.</div>
485
490
  </div>
486
491
  </div>
487
492
  </header>
488
493
 
489
- <section style={oauthSection}>
494
+ <section style={styles.oauthSection}>
490
495
  <button
491
496
  onClick={handleGoogle}
492
497
  type="button"
493
- style={{ ...oauthButton, ...oauthGoogle }}
498
+ style={{ ...styles.oauthButton, ...styles.oauthGoogle }}
494
499
  disabled={loading || loadingOauth.google}
495
500
  aria-disabled={loading || loadingOauth.google}
496
501
  >
@@ -506,7 +511,7 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
506
511
  <button
507
512
  onClick={handleGithub}
508
513
  type="button"
509
- style={{ ...oauthButton, ...oauthGithub }}
514
+ style={{ ...styles.oauthButton, ...styles.oauthGithub }}
510
515
  disabled={loading || loadingOauth.github}
511
516
  aria-disabled={loading || loadingOauth.github}
512
517
  >
@@ -517,28 +522,28 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
517
522
  </button>
518
523
  </section>
519
524
 
520
- <div style={dividerRow}>
521
- <div style={line} />
522
- <div style={orText}>or</div>
523
- <div style={line} />
525
+ <div style={styles.dividerRow}>
526
+ <div style={styles.line} />
527
+ <div style={styles.orText}>or</div>
528
+ <div style={styles.line} />
524
529
  </div>
525
530
 
526
- <form onSubmit={submit} style={form}>
527
- <label style={label} htmlFor="name">
528
- <span style={labelText}>Name</span>
531
+ <form onSubmit={submit} style={styles.form}>
532
+ <label style={styles.label} htmlFor="name">
533
+ <span style={styles.labelText}>Name</span>
529
534
  <input
530
535
  id="name"
531
536
  type="text"
532
537
  value={name}
533
538
  onChange={e => setName(e.target.value)}
534
539
  placeholder="Your name"
535
- style={input}
540
+ style={styles.input}
536
541
  autoComplete="name"
537
542
  />
538
543
  </label>
539
544
 
540
- <label style={label} htmlFor="email">
541
- <span style={labelText}>Email address</span>
545
+ <label style={styles.label} htmlFor="email">
546
+ <span style={styles.labelText}>Email address</span>
542
547
  <input
543
548
  id="email"
544
549
  type="email"
@@ -546,13 +551,13 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
546
551
  onChange={e => setEmail(e.target.value)}
547
552
  required
548
553
  placeholder="you@example.com"
549
- style={input}
554
+ style={styles.input}
550
555
  autoComplete="email"
551
556
  />
552
557
  </label>
553
558
 
554
- <label style={label} htmlFor="password">
555
- <span style={labelText}>Password</span>
559
+ <label style={styles.label} htmlFor="password">
560
+ <span style={styles.labelText}>Password</span>
556
561
  <input
557
562
  id="password"
558
563
  type="password"
@@ -560,14 +565,14 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
560
565
  onChange={e => setPassword(e.target.value)}
561
566
  required
562
567
  placeholder="••••••••"
563
- style={input}
568
+ style={styles.input}
564
569
  autoComplete="new-password"
565
570
  />
566
571
  </label>
567
572
 
568
573
  <button
569
574
  type="submit"
570
- style={submitButton}
575
+ style={styles.submitButton}
571
576
  disabled={loading}
572
577
  aria-disabled={loading}
573
578
  >
@@ -576,14 +581,14 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
576
581
  </form>
577
582
  </div>
578
583
 
579
- <div style={modalFooter}>
580
- <div style={belowRow}>
581
- <span style={muted}>Already have an account? </span>
582
- <Link href="/sign-in" style={link}>Sign in</Link>
584
+ <div style={styles.modalFooter}>
585
+ <div style={styles.belowRow}>
586
+ <span style={styles.muted}>Already have an account? </span>
587
+ <Link href="/sign-in" style={styles.link}>Sign in</Link>
583
588
  </div>
584
- <div style={dividerThin} />
585
- <div style={secured}>
586
- <div style={securedText}>Secured by auth</div>
589
+ <div style={styles.dividerThin} />
590
+ <div style={styles.secured}>
591
+ <div style={styles.securedText}>Secured by auth</div>
587
592
  </div>
588
593
  </div>
589
594
  </div>
@@ -591,271 +596,273 @@ export default function SignUp({ agency = { name: 'MyApp', logo: '' } }) {
591
596
  )
592
597
  }
593
598
 
594
- /* styles (JS objects) - neutral palette, translucent overlay so underlying content shows */
595
- const overlay = {
596
- position: 'fixed',
597
- inset: 0,
598
- display: 'flex',
599
- alignItems: 'center',
600
- justifyContent: 'center',
601
- padding: 20,
602
- background: 'linear-gradient(180deg, rgba(2,6,23,0.22), rgba(2,6,23,0.32))',
603
- backdropFilter: 'blur(6px)',
604
- minHeight: '100vh',
605
- zIndex: 9999
606
- }
607
-
608
- const modal = {
609
- width: '100%',
610
- maxWidth: 560,
611
- borderRadius: 14,
612
- background: 'linear-gradient(180deg, rgba(15,19,24,0.6), rgba(10,12,16,0.6))', // translucent neutral so background peeks through
613
- border: '1px solid rgba(99,102,106,0.12)', // slightly stronger neutral border
614
- boxShadow: '0 20px 50px rgba(2,6,23,0.55), inset 0 1px 0 rgba(255,255,255,0.02)',
615
- overflow: 'hidden',
616
- display: 'flex',
617
- flexDirection: 'column',
618
- transform: 'translateY(-6px)'
619
- }
620
-
621
- const modalInner = {
622
- padding: 18,
623
- display: 'flex',
624
- flexDirection: 'column',
625
- gap: 12
626
- }
627
-
628
- /* header / brand */
629
- const header = {
630
- paddingBottom: 6,
631
- borderBottom: '1px solid rgba(148,163,184,0.04)'
632
- }
633
-
634
- const brandRow = {
635
- display: 'flex',
636
- alignItems: 'center',
637
- gap: 12
638
- }
639
-
640
- const logo = {
641
- width: 44,
642
- height: 44,
643
- display: 'flex',
644
- alignItems: 'center',
645
- justifyContent: 'center'
646
- }
647
-
648
- const logoCircle = {
649
- width: 36,
650
- height: 36,
651
- borderRadius: 999,
652
- background: 'linear-gradient(135deg,#2f3438,#11151a)',
653
- boxShadow: '0 4px 12px rgba(2,6,23,0.6)'
654
- }
655
-
656
- const title = {
657
- margin: 0,
658
- fontSize: 18,
659
- fontWeight: 600,
660
- color: '#e6e6e6'
661
- }
662
-
663
- const subtitle = {
664
- fontSize: 13,
665
- color: 'rgba(230,230,230,0.72)',
666
- marginTop: 4
667
- }
668
-
669
- /* oauth row */
670
- const oauthSection = {
671
- marginTop: 8,
672
- display: 'flex',
673
- gap: 8
674
- }
675
-
676
- const oauthButton = {
677
- flex: 1,
678
- display: 'inline-flex',
679
- alignItems: 'center',
680
- justifyContent: 'center',
681
- padding: '10px 12px',
682
- borderRadius: 10,
683
- border: '1px solid rgba(148,163,184,0.08)',
684
- fontSize: 14,
685
- cursor: 'pointer',
686
- userSelect: 'none',
687
- gap: 8,
688
- minHeight: 40
689
- }
690
-
691
- const oauthGoogle = {
692
- background: 'linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0.01))',
693
- color: '#fff'
694
- }
695
-
696
- const oauthGithub = {
697
- background: 'linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.00))',
698
- color: '#fff'
699
- }
700
-
701
- /* divider row */
702
- const dividerRow = {
703
- display: 'flex',
704
- alignItems: 'center',
705
- gap: 10,
706
- marginTop: 12
707
- }
708
-
709
- const line = {
710
- flex: 1,
711
- height: 1,
712
- background: 'rgba(148,163,184,0.06)'
713
- }
714
-
715
- const orText = {
716
- fontSize: 13,
717
- color: 'rgba(230,230,230,0.65)',
718
- padding: '0 8px'
719
- }
720
-
721
- /* form */
722
- const form = {
723
- display: 'flex',
724
- flexDirection: 'column',
725
- gap: 10,
726
- marginTop: 6
727
- }
728
-
729
- const label = {
730
- display: 'flex',
731
- flexDirection: 'column',
732
- gap: 6
733
- }
734
-
735
- const labelText = {
736
- fontSize: 13,
737
- color: 'rgba(230,230,230,0.68)'
738
- }
739
-
740
- const input = {
741
- width: '100%',
742
- padding: '10px 12px',
743
- borderRadius: 10,
744
- background: 'rgba(255,255,255,0.02)',
745
- color: '#e6e6e6',
746
- border: '1px solid rgba(148,163,184,0.10)',
747
- fontSize: 14,
748
- outline: 'none',
749
- boxSizing: 'border-box',
750
- transition: 'box-shadow 120ms, border-color 120ms'
751
- }
752
-
753
- const submitButton = {
754
- marginTop: 6,
755
- width: '100%',
756
- padding: '10px 12px',
757
- borderRadius: 10,
758
- background: 'linear-gradient(180deg,#475569,#94a3b8)',
759
- border: 'none',
760
- color: '#fff',
761
- fontWeight: 600,
762
- cursor: 'pointer',
763
- fontSize: 15,
764
- display: 'inline-flex',
765
- alignItems: 'center',
766
- justifyContent: 'center',
767
- minHeight: 44
768
- }
769
-
770
- /* modal footer */
771
- const modalFooter = {
772
- padding: '12px 18px 18px 18px',
773
- borderTop: '1px solid rgba(148,163,184,0.03)',
774
- display: 'flex',
775
- flexDirection: 'column',
776
- gap: 8
777
- }
778
-
779
- const belowRow = {
780
- textAlign: 'center',
781
- display: 'flex',
782
- justifyContent: 'center',
783
- gap: 8,
784
- alignItems: 'center'
785
- }
786
-
787
- const muted = {
788
- color: 'rgba(230,230,230,0.66)',
789
- fontSize: 13
790
- }
791
-
792
- const link = {
793
- color: '#9fb0d9',
794
- textDecoration: 'none',
795
- fontWeight: 600
796
- }
797
-
798
- const dividerThin = {
799
- height: 1,
800
- background: 'rgba(148,163,184,0.04)',
801
- marginTop: 6,
802
- marginBottom: 6
803
- }
804
-
805
- const secured = {
806
- textAlign: 'center'
807
- }
808
-
809
- const securedText = {
810
- color: 'rgba(230,230,230,0.9)',
811
- fontSize: 13,
812
- fontWeight: 600
813
- }
814
-
815
- /* toasts - black background as requested */
816
- const toastContainer = {
817
- position: 'fixed',
818
- top: 18,
819
- right: 18,
820
- width: 360,
821
- maxWidth: 'calc(100% - 36px)',
822
- display: 'flex',
823
- flexDirection: 'column',
824
- gap: 10,
825
- zIndex: 60000
826
- }
827
-
828
- const toastBase = {
829
- display: 'flex',
830
- gap: 10,
831
- alignItems: 'center',
832
- padding: '10px 12px',
833
- borderRadius: 10,
834
- boxShadow: '0 8px 20px rgba(2,6,23,0.6)',
835
- color: '#fff',
836
- fontSize: 13,
837
- minWidth: 120
838
- }
839
-
840
- const toastError = {
841
- background: '#000000',
842
- border: '1px solid rgba(255,255,255,0.06)'
843
- }
844
- const toastSuccess = {
845
- background: '#000000',
846
- border: '1px solid rgba(255,255,255,0.06)'
847
- }
848
- const toastInfo = {
849
- background: '#000000',
850
- border: '1px solid rgba(255,255,255,0.06)'
851
- }
852
-
853
- const toastCloseBtn = {
854
- marginLeft: 8,
855
- background: 'transparent',
856
- border: 'none',
857
- color: 'rgba(255,255,255,0.7)',
858
- cursor: 'pointer',
859
- fontSize: 14,
860
- lineHeight: 1
599
+ /* Styles (JS objects) - reduced width and zoom-resilient layout; neutral palette */
600
+ const styles = {
601
+ overlay: {
602
+ position: 'fixed',
603
+ inset: 0,
604
+ display: 'block', // use block + scrolling container for zoom resilience
605
+ padding: 20,
606
+ background: 'linear-gradient(180deg, rgba(2,6,23,0.22), rgba(2,6,23,0.32))',
607
+ backdropFilter: 'blur(6px)',
608
+ overflowY: 'auto', // allow page scroll when zoomed
609
+ WebkitOverflowScrolling: 'touch',
610
+ minHeight: '100vh',
611
+ zIndex: 9999
612
+ },
613
+
614
+ // modal: reduced width (not too wide), centered with margin auto, allows internal scroll
615
+ modal: {
616
+ width: '100%',
617
+ maxWidth: 460, // reduced width per request
618
+ margin: '40px auto', // vertical margin so modal isn't glued to top when zoomed
619
+ borderRadius: 14,
620
+ background: 'linear-gradient(180deg, rgba(15,19,24,0.85), rgba(10,12,16,0.85))', // translucent neutral
621
+ border: '1px solid rgba(99,102,106,0.12)', // visible neutral border
622
+ boxShadow: '0 18px 48px rgba(2,6,23,0.55), inset 0 1px 0 rgba(255,255,255,0.02)',
623
+ overflow: 'hidden',
624
+ display: 'flex',
625
+ flexDirection: 'column',
626
+ maxHeight: 'calc(100vh - 80px)', // keep inside viewport
627
+ },
628
+
629
+ modalInner: {
630
+ padding: 18,
631
+ display: 'flex',
632
+ flexDirection: 'column',
633
+ gap: 12,
634
+ overflowY: 'auto'
635
+ },
636
+
637
+ header: {
638
+ paddingBottom: 6,
639
+ borderBottom: '1px solid rgba(148,163,184,0.04)'
640
+ },
641
+
642
+ brandRow: {
643
+ display: 'flex',
644
+ alignItems: 'center',
645
+ gap: 12
646
+ },
647
+
648
+ logo: {
649
+ width: 44,
650
+ height: 44,
651
+ display: 'flex',
652
+ alignItems: 'center',
653
+ justifyContent: 'center'
654
+ },
655
+
656
+ logoCircle: {
657
+ width: 36,
658
+ height: 36,
659
+ borderRadius: 999,
660
+ background: 'linear-gradient(135deg,#2f3438,#11151a)',
661
+ boxShadow: '0 4px 12px rgba(2,6,23,0.6)'
662
+ },
663
+
664
+ title: {
665
+ margin: 0,
666
+ fontSize: 18,
667
+ fontWeight: 600,
668
+ color: '#e6e6e6'
669
+ },
670
+
671
+ subtitle: {
672
+ fontSize: 13,
673
+ color: 'rgba(230,230,230,0.72)',
674
+ marginTop: 4
675
+ },
676
+
677
+ oauthSection: {
678
+ marginTop: 8,
679
+ display: 'flex',
680
+ gap: 8
681
+ },
682
+
683
+ oauthButton: {
684
+ flex: 1,
685
+ display: 'inline-flex',
686
+ alignItems: 'center',
687
+ justifyContent: 'center',
688
+ padding: '10px 12px',
689
+ borderRadius: 10,
690
+ border: '1px solid rgba(148,163,184,0.08)',
691
+ fontSize: 14,
692
+ cursor: 'pointer',
693
+ userSelect: 'none',
694
+ gap: 8,
695
+ minHeight: 40,
696
+ background: 'transparent',
697
+ color: '#fff'
698
+ },
699
+
700
+ oauthGoogle: {
701
+ background: 'linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0.01))'
702
+ },
703
+
704
+ oauthGithub: {
705
+ background: 'linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.00))'
706
+ },
707
+
708
+ dividerRow: {
709
+ display: 'flex',
710
+ alignItems: 'center',
711
+ gap: 10,
712
+ marginTop: 12
713
+ },
714
+
715
+ line: {
716
+ flex: 1,
717
+ height: 1,
718
+ background: 'rgba(148,163,184,0.06)'
719
+ },
720
+
721
+ orText: {
722
+ fontSize: 13,
723
+ color: 'rgba(230,230,230,0.65)',
724
+ padding: '0 8px'
725
+ },
726
+
727
+ form: {
728
+ display: 'flex',
729
+ flexDirection: 'column',
730
+ gap: 10,
731
+ marginTop: 6
732
+ },
733
+
734
+ label: {
735
+ display: 'flex',
736
+ flexDirection: 'column',
737
+ gap: 6
738
+ },
739
+
740
+ labelText: {
741
+ fontSize: 13,
742
+ color: 'rgba(230,230,230,0.68)'
743
+ },
744
+
745
+ input: {
746
+ width: '100%',
747
+ padding: '10px 12px',
748
+ borderRadius: 10,
749
+ background: 'rgba(255,255,255,0.02)',
750
+ color: '#e6e6e6',
751
+ border: '1px solid rgba(148,163,184,0.10)',
752
+ fontSize: 14,
753
+ outline: 'none',
754
+ boxSizing: 'border-box',
755
+ transition: 'box-shadow 120ms, border-color 120ms'
756
+ },
757
+
758
+ submitButton: {
759
+ marginTop: 6,
760
+ width: '100%',
761
+ padding: '10px 12px',
762
+ borderRadius: 10,
763
+ background: 'linear-gradient(180deg,#475569,#94a3b8)',
764
+ border: 'none',
765
+ color: '#fff',
766
+ fontWeight: 600,
767
+ cursor: 'pointer',
768
+ fontSize: 15,
769
+ display: 'inline-flex',
770
+ alignItems: 'center',
771
+ justifyContent: 'center',
772
+ minHeight: 44
773
+ },
774
+
775
+ modalFooter: {
776
+ padding: '12px 18px 18px 18px',
777
+ borderTop: '1px solid rgba(148,163,184,0.03)',
778
+ display: 'flex',
779
+ flexDirection: 'column',
780
+ gap: 8
781
+ },
782
+
783
+ belowRow: {
784
+ textAlign: 'center',
785
+ display: 'flex',
786
+ justifyContent: 'center',
787
+ gap: 8,
788
+ alignItems: 'center'
789
+ },
790
+
791
+ muted: {
792
+ color: 'rgba(230,230,230,0.66)',
793
+ fontSize: 13
794
+ },
795
+
796
+ link: {
797
+ color: '#9fb0d9',
798
+ textDecoration: 'none',
799
+ fontWeight: 600
800
+ },
801
+
802
+ dividerThin: {
803
+ height: 1,
804
+ background: 'rgba(148,163,184,0.04)',
805
+ marginTop: 6,
806
+ marginBottom: 6
807
+ },
808
+
809
+ secured: {
810
+ textAlign: 'center'
811
+ },
812
+
813
+ securedText: {
814
+ color: 'rgba(230,230,230,0.9)',
815
+ fontSize: 13,
816
+ fontWeight: 600
817
+ },
818
+
819
+ /* Toasts: black background */
820
+ toastContainer: {
821
+ position: 'fixed',
822
+ top: 18,
823
+ right: 18,
824
+ width: 360,
825
+ maxWidth: 'calc(100% - 36px)',
826
+ display: 'flex',
827
+ flexDirection: 'column',
828
+ gap: 10,
829
+ zIndex: 60000
830
+ },
831
+
832
+ toastBase: {
833
+ display: 'flex',
834
+ gap: 10,
835
+ alignItems: 'center',
836
+ padding: '10px 12px',
837
+ borderRadius: 10,
838
+ boxShadow: '0 8px 20px rgba(2,6,23,0.6)',
839
+ color: '#fff',
840
+ fontSize: 13,
841
+ minWidth: 120
842
+ },
843
+
844
+ toastError: {
845
+ background: '#000000',
846
+ border: '1px solid rgba(255,255,255,0.06)'
847
+ },
848
+
849
+ toastSuccess: {
850
+ background: '#000000',
851
+ border: '1px solid rgba(255,255,255,0.06)'
852
+ },
853
+
854
+ toastInfo: {
855
+ background: '#000000',
856
+ border: '1px solid rgba(255,255,255,0.06)'
857
+ },
858
+
859
+ toastCloseBtn: {
860
+ marginLeft: 8,
861
+ background: 'transparent',
862
+ border: 'none',
863
+ color: 'rgba(255,255,255,0.7)',
864
+ cursor: 'pointer',
865
+ fontSize: 14,
866
+ lineHeight: 1
867
+ }
861
868
  }