11'use client'
22
3- import { useEffect , useState } from 'react'
3+ import { useState } from 'react'
4+ import { useRef } from 'react'
45import Link from 'next/link'
6+ import { useGSAP } from '@gsap/react'
7+ import { gsap } from 'gsap'
58import { Button } from '@/ui/button'
69import { Input } from '@/ui/input'
10+ import { ensureGsapRegistered } from '@/app/components/gsap/registry'
11+ import { AnimatedOrbitalLogo } from '@/app/components/gsap/animated-orbital-logo'
712import { ArrowRightIcon , CheckIcon , LoaderIcon } from 'lucide-react'
813
914const FOOTER_LINKS = {
@@ -64,6 +69,7 @@ const SOCIAL_LINKS = [
6469]
6570
6671export function Footer ( ) {
72+ const footerRef = useRef < HTMLElement > ( null )
6773 const [ email , setEmail ] = useState ( '' )
6874 const [ status , setStatus ] = useState <
6975 'idle' | 'loading' | 'success' | 'error'
@@ -83,16 +89,52 @@ export function Footer() {
8389 setTimeout ( ( ) => setStatus ( 'idle' ) , 3000 )
8490 }
8591
86- const [ year , setYear ] = useState < number | null > ( null )
92+ const year = new Date ( ) . getFullYear ( )
8793
88- useEffect ( ( ) => {
89- setYear ( new Date ( ) . getFullYear ( ) )
90- } , [ ] )
94+ useGSAP (
95+ ( ) => {
96+ ensureGsapRegistered ( )
97+
98+ const targets = gsap . utils . toArray < HTMLElement > (
99+ '[data-footer-reveal]'
100+ )
101+
102+ if ( targets . length === 0 ) {
103+ return
104+ }
105+
106+ const prefersReducedMotion = window . matchMedia (
107+ '(prefers-reduced-motion: reduce)'
108+ ) . matches
109+
110+ if ( prefersReducedMotion ) {
111+ gsap . set ( targets , { autoAlpha : 1 , y : 0 } )
112+ return
113+ }
114+
115+ gsap . set ( targets , { autoAlpha : 0 , y : 18 } )
116+ targets . forEach ( ( target , index ) => {
117+ gsap . to ( target , {
118+ autoAlpha : 1 ,
119+ y : 0 ,
120+ duration : 0.45 ,
121+ ease : 'power2.out' ,
122+ delay : index * 0.04 ,
123+ scrollTrigger : {
124+ trigger : target ,
125+ start : 'top 92%' ,
126+ once : true ,
127+ } ,
128+ } )
129+ } )
130+ } ,
131+ { scope : footerRef }
132+ )
91133
92134 return (
93- < footer className = "border-t border-border bg-background" >
135+ < footer ref = { footerRef } className = "border-t border-border bg-background" >
94136 { /* Newsletter section */ }
95- < div className = "border-b border-border" >
137+ < div className = "border-b border-border" data-footer-reveal >
96138 < div className = "container mx-auto px-4 py-12" >
97139 < div className = "mx-auto max-w-2xl text-center" >
98140 < h3 className = "mb-2 text-2xl font-bold text-foreground" >
@@ -103,7 +145,9 @@ export function Footer() {
103145 practices, and AI agent development tips.
104146 </ p >
105147 < form
106- onSubmit = { handleSubscribe }
148+ onSubmit = { ( event ) => {
149+ void handleSubscribe ( event )
150+ } }
107151 className = "flex flex-col gap-3 sm:flex-row sm:gap-2"
108152 >
109153 < div className = "relative flex-1" >
@@ -122,7 +166,7 @@ export function Footer() {
122166 < Button
123167 type = "submit"
124168 size = "lg"
125- className = "h-12 min-w-[140px] transition-all duration-200 ease-spring hover:-translate-y-px"
169+ className = "h-12 min-w-35 transition-all duration-200 ease-spring hover:-translate-y-px"
126170 disabled = {
127171 status === 'loading' || status === 'success'
128172 }
@@ -154,18 +198,19 @@ export function Footer() {
154198 </ div >
155199
156200 { /* Main footer content */ }
157- < div className = "container mx-auto px-4 py-12 lg:py-16" >
201+ < div className = "container mx-auto px-4 py-12 lg:py-16" data-footer-reveal >
158202 < div className = "grid grid-cols-2 gap-8 md:grid-cols-3 lg:grid-cols-6" >
159203 { /* Brand column */ }
160204 < div className = "col-span-2 md:col-span-3 lg:col-span-2" >
161205 < Link
162206 href = "/"
163207 className = "group flex items-center gap-2 transition-opacity duration-200 hover:opacity-80"
164208 >
165- < div className = "flex size-10 items-center justify-center rounded-lg bg-linear-to-br from-primary to-primary/80 shadow-lg transition-all duration-200 ease-spring group-hover:scale-105 group-hover:shadow-xl" >
166- < span className = "font-bold text-primary-foreground text-lg" >
167- A
168- </ span >
209+ < div className = "flex size-10 items-center justify-center rounded-lg bg-linear-to-br from-primary/15 to-primary/5 shadow-lg transition-all duration-200 ease-spring group-hover:scale-105 group-hover:shadow-xl" >
210+ < AnimatedOrbitalLogo
211+ size = { 26 }
212+ className = "text-primary"
213+ />
169214 </ div >
170215 < span className = "text-xl font-bold text-foreground" >
171216 AgentStack
@@ -271,10 +316,10 @@ export function Footer() {
271316 </ div >
272317
273318 { /* Bottom bar */ }
274- < div className = "border-t border-border" >
319+ < div className = "border-t border-border" data-footer-reveal >
275320 < div className = "container mx-auto flex flex-col items-center justify-between gap-4 px-4 py-6 md:flex-row" >
276321 < div className = "flex flex-col items-center gap-2 text-sm text-muted-foreground md:flex-row md:gap-4" >
277- < p > © { year ?? '' } AgentStack. All rights reserved.</ p >
322+ < p > © { year } AgentStack. All rights reserved.</ p >
278323 < span className = "hidden md:inline" > •</ span >
279324 < p > Built with ❤️ for developers</ p >
280325 </ div >
0 commit comments