@@ -3,15 +3,10 @@ import SEO from '../SEO';
33import { lazy , Suspense } from 'react' ;
44import Footer from './Footer' ;
55import AnnouncementBanner from './AnnouncementBanner' ;
6- import { useNavigate } from 'react-router-dom' ;
7- import { useState , useEffect , useCallback } from 'react' ;
8- import { Menu , X } from 'lucide-react' ;
9- import { motion , AnimatePresence } from 'framer-motion' ;
10- import LanguageSwitcher from '../Layout/LanguageSwitcher' ;
116
12- import { useLanguage } from '../../contexts/LanguageContext' ;
7+ import { useState , useCallback } from 'react' ;
8+ import SharedHeader from '../Layout/SharedHeader' ;
139
14- import { useDeviceDetection } from '../../hooks/useDeviceDetection' ;
1510
1611// Lazy-load below-the-fold sections to reduce initial JS bundle
1712const AboutSection = lazy ( ( ) => import ( './AboutSection' ) ) ;
@@ -21,40 +16,13 @@ const Conversation = lazy(() => import('./Conversation'));
2116const FAQ = lazy ( ( ) => import ( './FAQ' ) ) ;
2217
2318export default function Landing ( ) {
24- const navigate = useNavigate ( ) ;
25- const [ isMobileMenuOpen , setIsMobileMenuOpen ] = useState ( false ) ;
26- const [ isScrolled , setIsScrolled ] = useState ( false ) ;
27- const { t } = useLanguage ( ) ;
28- const { isMobile } = useDeviceDetection ( ) ;
19+
2920 const [ bannerVisible , setBannerVisible ] = useState ( true ) ;
3021
3122 const handleBannerVisibility = useCallback ( ( visible : boolean ) => {
3223 setBannerVisible ( visible ) ;
3324 } , [ ] ) ;
3425
35- // Track scroll position to change header background
36- useEffect ( ( ) => {
37- const handleScroll = ( ) => {
38- setIsScrolled ( window . scrollY > 20 ) ;
39- } ;
40- window . addEventListener ( 'scroll' , handleScroll ) ;
41- handleScroll ( ) ; // Check initial state
42- return ( ) => window . removeEventListener ( 'scroll' , handleScroll ) ;
43- } , [ ] ) ;
44-
45- const handleAboutClick = ( e : React . MouseEvent ) => {
46- e . preventDefault ( ) ;
47- e . stopPropagation ( ) ;
48- setIsMobileMenuOpen ( false ) ;
49- navigate ( '/about' ) ;
50- } ;
51-
52-
53-
54- const handleSolutionsClick = ( ) => {
55- setIsMobileMenuOpen ( false ) ;
56- navigate ( '/solutions' ) ;
57- } ;
5826
5927 return (
6028 < div className = "min-h-screen overflow-hidden relative bg-[rgb(10,10,10)]" >
@@ -66,212 +34,7 @@ export default function Landing() {
6634 { /* Announcement Banner */ }
6735 < AnnouncementBanner onVisibilityChange = { handleBannerVisibility } />
6836
69- { /* Header - Dark background fading to transparent on right for corner glow, solid when scrolled */ }
70- { /* Floating Pill Header */ }
71- < header
72- className = { `fixed z-50 left-1/2 w-[95%] max-w-[1400px] transition-all duration-300 transform -translate-x-1/2 rounded-[50px] md:rounded-[70px] px-5 md:px-10 py-3 md:py-5 shadow-2xl backdrop-blur-xl bg-[#1a1a1a]/50 hover:bg-[#1a1a1a]/70 border border-white/10 ${ isScrolled ? 'top-4 md:top-6' : 'top-4 md:top-6' } ` }
73- style = { { top : bannerVisible ? 'calc(44px + 1rem)' : '1rem' } }
74- >
75- < div className = "w-full flex items-center justify-between relative" >
76-
77- { /* Left: Logo */ }
78- < div className = "flex items-center gap-2 shrink-0" >
79- < button
80- type = "button"
81- onClick = { ( ) => navigate ( '/' ) }
82- className = "flex items-center gap-2 md:gap-3 group"
83- aria-label = "Go to home"
84- >
85- < svg viewBox = "0 0 464 468" className = "w-8 h-8 md:w-12 md:h-12" >
86- < path fill = "white" d = "M275.9 63.5c37.7 5.3 76.6 24.1 103.7 50.2 30 28.8 41.8 57.6 35.8 87.1-6.1 30.1-33.6 52.9-70.6 58.3-6 0.9-18.3 1-44.9 0.6l-36.6-0.7-0.5 17.8c-0.3 9.7-0.4 17.8-0.4 17.9 0.1 0.1 19.1 0.3 42.2 0.4 23.2 0 42.7 0.5 43.5 1 1.2 0.7 1.1 2.2-0.8 9.4-6 23-20.5 42.1-41.8 55-7.3 4.3-26.7 11.9-36 14.1-9 2-34 2-44.5 0-41.3-7.9-74.2-38-82.9-75.7-8.1-35.7 2.2-71.5 27.5-94.7 16.1-14.9 35.5-22.4 63.7-24.7l7.7-0.7v-34.1l-11.7 0.7c-22.2 1.3-37 5.3-56.4 15.2-28.7 14.6-49.7 39.3-59.9 70.2-9.6 29.3-9.3 62.6 0.8 91.4 3.3 9.2 12.2 25.6 18.3 33.8 11.3 14.9 30.6 30.8 48.7 39.9 19.9 10 49.2 15.9 73.2 14.7 26.5-1.3 52.5-9.6 74.2-23.9 26.9-17.6 47.2-47.9 53.3-79.7 1-5.2 2.3-10.1 2.8-10.8 0.8-0.9 6.9-1.2 27.1-1l26.1 0.3 0.3 3.8c1.2 14.6-10.9 52.1-23.9 74-17.8 30-43.2 54-75.9 71.5-20.9 11.2-38.3 16.5-67.2 20.7-27.6 3.9-47.9 3.1-75.8-3.1-36.9-8.3-67.8-25.6-97.1-54.6-23.6-23.2-44.8-61.9-51.7-93.8-5.1-23.7-5.5-28.1-4.9-48.8 1.7-63.2 23.4-111.8 67.7-152 28-25.4 60.4-41.3 99-48.8 18.5-3.6 46.1-4 67.9-0.9zm16.4 92.6c-6.3 2.4-12.8 8.5-15.4 14.5-2.6 6.1-2.6 18.3 0 23.9 5 11 20.2 17.7 32.3 14.1 11.9-3.4 19.8-14.3 19.8-27.1-0.1-19.9-18.2-32.5-36.7-25.4z" />
87- </ svg >
88- < span className = "text-lg md:text-[28px] font-semibold text-white/90 group-hover:text-white transition-colors leading-none tracking-tight" >
89- ClerkTree
90- </ span >
91- </ button >
92- </ div >
93-
94- { /* Center: Navigation Links - Hidden on Mobile. Centered absolutely to ensure it's precisely in the middle */ }
95- < nav className = "hidden md:flex items-center justify-center gap-12 absolute left-1/2 transform -translate-x-1/2" >
96- < button
97- type = "button"
98- onClick = { ( ) => navigate ( '/blog' ) }
99- className = "font-medium bg-transparent border-none outline-none transition-colors tracking-wide"
100- style = { {
101- fontSize : '18px' ,
102- fontFamily : 'Urbanist, sans-serif' ,
103- color : 'oklch(0.9 0 0 / 0.7)' ,
104- } }
105- onMouseEnter = { ( e ) => e . currentTarget . style . color = '#ffffff' }
106- onMouseLeave = { ( e ) => e . currentTarget . style . color = 'oklch(0.9 0 0 / 0.7)' }
107- >
108- { t ( 'nav.blog' ) }
109- </ button >
110- < button
111- type = "button"
112- onClick = { handleAboutClick }
113- className = "font-medium bg-transparent border-none outline-none transition-colors tracking-wide"
114- style = { {
115- fontSize : '18px' ,
116- fontFamily : 'Urbanist, sans-serif' ,
117- color : 'oklch(0.9 0 0 / 0.7)' ,
118- } }
119- onMouseEnter = { ( e ) => e . currentTarget . style . color = '#ffffff' }
120- onMouseLeave = { ( e ) => e . currentTarget . style . color = 'oklch(0.9 0 0 / 0.7)' }
121- >
122- { t ( 'nav.about' ) }
123- </ button >
124- < button
125- onClick = { ( ) => navigate ( '/docs' ) }
126- className = "font-medium bg-transparent border-none outline-none transition-colors tracking-wide"
127- style = { {
128- fontSize : '18px' ,
129- fontFamily : 'Urbanist, sans-serif' ,
130- color : 'oklch(0.9 0 0 / 0.7)' ,
131- } }
132- onMouseEnter = { ( e ) => e . currentTarget . style . color = '#ffffff' }
133- onMouseLeave = { ( e ) => e . currentTarget . style . color = 'oklch(0.9 0 0 / 0.7)' }
134- >
135- { t ( 'nav.docs' ) }
136- </ button >
137- </ nav >
138-
139- { /* Right: Actions */ }
140- < div className = "flex items-center justify-end gap-3 md:gap-5 shrink-0" >
141- { /* Language Switcher */ }
142- < div className = "hidden md:block w-[75px] scale-100 origin-right" >
143- < LanguageSwitcher isExpanded = { true } forceDark = { true } />
144- </ div >
145-
146- { /* CTA Book/Login Button matching the pill style */ }
147- < button
148- onClick = { ( ) => navigate ( '/login' ) }
149- className = "hidden md:flex items-center justify-center whitespace-nowrap text-[18px] font-medium transition-all h-[54px] rounded-[30px] px-10 gap-2 group"
150- style = { {
151- fontFamily : 'Urbanist, sans-serif' ,
152- background : 'rgba(255, 255, 255, 1)' ,
153- color : 'rgb(10, 10, 10)' ,
154- } }
155- >
156- { t ( 'nav.login' ) }
157- < svg width = "14" height = "14" viewBox = "0 0 12 12" fill = "none" xmlns = "http://www.w3.org/2000/svg" className = "transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-transform" >
158- < path d = "M1 11L11 1M11 1H3.5M11 1V8.5" stroke = "currentColor" strokeWidth = "1.5" strokeLinecap = "round" strokeLinejoin = "round" />
159- </ svg >
160- </ button >
161-
162- { /* Mobile Hamburger Menu Button */ }
163- < button
164- onClick = { ( ) => setIsMobileMenuOpen ( ! isMobileMenuOpen ) }
165- className = "md:hidden p-2 text-white/80 hover:text-white transition-colors ml-1"
166- aria-label = "Toggle menu"
167- >
168- { isMobileMenuOpen ? (
169- < X className = "w-6 h-6" />
170- ) : (
171- < Menu className = "w-6 h-6" />
172- ) }
173- </ button >
174- </ div >
175- </ div >
176- </ header >
177-
178- { /* Mobile Menu Overlay - offset top when banner is visible */ }
179- < AnimatePresence >
180- { isMobileMenuOpen && (
181- < >
182- { /* Backdrop */ }
183- < motion . div
184- initial = { { opacity : 0 } }
185- animate = { { opacity : 1 } }
186- exit = { { opacity : 0 } }
187- transition = { { duration : 0.2 } }
188- className = "fixed inset-0 bg-black/60 backdrop-blur-sm z-40 md:hidden"
189- onClick = { ( ) => setIsMobileMenuOpen ( false ) }
190- />
191-
192- { /* Glassy Popup Menu */ }
193- < motion . div
194- initial = { { opacity : 0 , y : - 20 , scale : 0.95 } }
195- animate = { { opacity : 1 , y : 0 , scale : 1 } }
196- exit = { { opacity : 0 , y : - 20 , scale : 0.95 } }
197- transition = { { duration : 0.2 , ease : "easeOut" } }
198- className = "fixed left-1/2 transform -translate-x-1/2 w-[95%] z-50 md:hidden"
199- style = { { top : bannerVisible ? 'calc(44px + 90px)' : '90px' } }
200- >
201- < div className = "backdrop-blur-xl bg-[#1a1a1a]/50 border border-white/10 rounded-[30px] shadow-2xl overflow-hidden p-2" >
202- < nav className = "flex flex-col p-2" >
203- < button
204- onClick = { handleAboutClick }
205- className = "flex items-center justify-between px-4 py-3.5 rounded-xl text-white/90 hover:text-white hover:bg-white/10 transition-all group"
206- >
207- < span className = "text-base font-medium font-urbanist" > { t ( 'nav.about' ) } </ span >
208- < span className = "text-white/20 group-hover:text-white/60 transition-colors" > →</ span >
209- </ button >
210-
211- < div className = "h-px bg-white/5 mx-2" />
212-
213- < button
214- onClick = { handleSolutionsClick }
215- className = "flex items-center justify-between px-4 py-3.5 rounded-xl text-white/90 hover:text-white hover:bg-white/10 transition-all group"
216- >
217- < span className = "text-base font-medium font-urbanist" > { t ( 'nav.solutions' ) } </ span >
218- < span className = "text-white/20 group-hover:text-white/60 transition-colors" > →</ span >
219- </ button >
220-
221- < div className = "h-px bg-white/5 mx-2" />
222-
223- < button
224- onClick = { ( ) => {
225- setIsMobileMenuOpen ( false ) ;
226- navigate ( '/blog' ) ;
227- } }
228- className = "flex items-center justify-between px-4 py-3.5 rounded-xl text-white/90 hover:text-white hover:bg-white/10 transition-all group"
229- >
230- < span className = "text-base font-medium font-urbanist" > { t ( 'nav.blog' ) } </ span >
231- < span className = "text-white/20 group-hover:text-white/60 transition-colors" > →</ span >
232- </ button >
233-
234- < div className = "h-px bg-white/5 mx-2" />
235-
236- < button
237- onClick = { ( ) => {
238- setIsMobileMenuOpen ( false ) ;
239- navigate ( '/docs' ) ;
240- } }
241- className = "flex items-center justify-between px-4 py-3.5 rounded-xl text-white/90 hover:text-white hover:bg-white/10 transition-all group"
242- >
243- < span className = "text-base font-medium font-urbanist" > { t ( 'nav.docs' ) } </ span >
244- < span className = "text-white/20 group-hover:text-white/60 transition-colors" > →</ span >
245- </ button >
246-
247- < div className = "h-px bg-white/5 mx-2 my-1" />
248-
249- < div className = "p-2" >
250- < LanguageSwitcher isExpanded = { true } forceDark = { true } />
251- </ div >
252-
253- < div className = "h-px bg-white/5 mx-2" />
254-
255- < button
256- onClick = { ( ) => {
257- setIsMobileMenuOpen ( false ) ;
258- navigate ( '/login' ) ;
259- } }
260- className = "flex items-center justify-center mx-2 my-2 px-4 py-3 rounded-xl text-sm font-medium transition-all"
261- style = { {
262- fontFamily : 'Urbanist, sans-serif' ,
263- background : 'rgba(255, 255, 255, 0.95)' ,
264- color : 'rgb(10, 10, 10)' ,
265- } }
266- >
267- { t ( 'nav.login' ) }
268- </ button >
269- </ nav >
270- </ div >
271- </ motion . div >
272- </ >
273- ) }
274- </ AnimatePresence >
37+ < SharedHeader bannerVisible = { bannerVisible } />
27538
27639 < div className = "relative z-10" >
27740 < Hero />
0 commit comments