Hero: centered layout, GSAP scroll, 1.5x video, remove CTA
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
330e75a6ea
commit
a72a3c61f9
|
|
@ -14,6 +14,7 @@
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dotted-map": "^3.1.0",
|
"dotted-map": "^3.1.0",
|
||||||
"framer-motion": "^12.40.0",
|
"framer-motion": "^12.40.0",
|
||||||
|
"gsap": "^3.15.0",
|
||||||
"jalaali-js": "^1.2.8",
|
"jalaali-js": "^1.2.8",
|
||||||
"lucide-react": "^1.16.0",
|
"lucide-react": "^1.16.0",
|
||||||
"react": "^19.2.6",
|
"react": "^19.2.6",
|
||||||
|
|
@ -2201,6 +2202,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/gsap": {
|
||||||
|
"version": "3.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz",
|
||||||
|
"integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==",
|
||||||
|
"license": "Standard 'no charge' license: https://gsap.com/standard-license."
|
||||||
|
},
|
||||||
"node_modules/hermes-estree": {
|
"node_modules/hermes-estree": {
|
||||||
"version": "0.25.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dotted-map": "^3.1.0",
|
"dotted-map": "^3.1.0",
|
||||||
"framer-motion": "^12.40.0",
|
"framer-motion": "^12.40.0",
|
||||||
|
"gsap": "^3.15.0",
|
||||||
"jalaali-js": "^1.2.8",
|
"jalaali-js": "^1.2.8",
|
||||||
"lucide-react": "^1.16.0",
|
"lucide-react": "^1.16.0",
|
||||||
"react": "^19.2.6",
|
"react": "^19.2.6",
|
||||||
|
|
|
||||||
|
|
@ -1,131 +1,140 @@
|
||||||
import { useState } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import { motion } from 'framer-motion'
|
|
||||||
import { useLang } from '@/context/LangContext'
|
import { useLang } from '@/context/LangContext'
|
||||||
|
import gsap from 'gsap'
|
||||||
|
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
||||||
|
|
||||||
|
gsap.registerPlugin(ScrollTrigger)
|
||||||
|
|
||||||
const T = {
|
const T = {
|
||||||
fa: {
|
fa: {
|
||||||
h1a: 'جایی که آینده', h1b: ' به تصمیم ', h1c: 'تبدیل میشود',
|
tag: 'اندیشکده فولاد آینده',
|
||||||
sub: 'تحلیل روندها، فناوریها و سناریوهای آینده صنعت فولاد',
|
h1a: 'جایی که آینده',
|
||||||
cta1: 'گزارش ویژه ←', cta2: 'نبض صنعت',
|
h1b: 'به تصمیم تبدیل میشود',
|
||||||
video: 'Short film · Future of Steel',
|
sub: 'تحلیل روندها، فناوریها و سناریوهای آینده صنعت فولاد ایران و جهان',
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
h1a: 'Where the Future', h1b: ' Becomes ', h1c: 'Decision',
|
tag: 'Steel Policy Institute',
|
||||||
|
h1a: 'Where the Future',
|
||||||
|
h1b: 'Becomes Decision',
|
||||||
sub: 'Analyzing trends, technologies and future scenarios of the global steel industry',
|
sub: 'Analyzing trends, technologies and future scenarios of the global steel industry',
|
||||||
cta1: 'Special Report →', cta2: 'Industry Pulse',
|
|
||||||
video: 'Short film · Future of Steel',
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HeroSection() {
|
export default function HeroSection() {
|
||||||
const { lang } = useLang()
|
const { lang } = useLang()
|
||||||
const t = T[lang]
|
const t = T[lang]
|
||||||
const [repHov, setRepHov] = useState(false)
|
|
||||||
const [pulseHov, setPulseHov] = useState(false)
|
const sectionRef = useRef<HTMLElement>(null)
|
||||||
|
const videoRef = useRef<HTMLVideoElement>(null)
|
||||||
|
const overlayRef = useRef<HTMLDivElement>(null)
|
||||||
|
const textRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = textRef.current
|
||||||
|
const video = videoRef.current
|
||||||
|
const overlay = overlayRef.current
|
||||||
|
if (!el || !video || !overlay) return
|
||||||
|
|
||||||
|
video.playbackRate = 1.5
|
||||||
|
|
||||||
|
// entrance
|
||||||
|
const tl = gsap.timeline()
|
||||||
|
tl.fromTo(el, { opacity: 0, y: 40 }, { opacity: 1, y: 0, duration: 1, ease: 'power3.out' })
|
||||||
|
|
||||||
|
// scroll parallax — scrub keeps it reversible
|
||||||
|
ScrollTrigger.create({
|
||||||
|
trigger: sectionRef.current,
|
||||||
|
start: 'top top',
|
||||||
|
end: 'bottom top',
|
||||||
|
scrub: 1,
|
||||||
|
onUpdate: (self) => {
|
||||||
|
const p = self.progress
|
||||||
|
gsap.set(video, { scale: 1 + p * 0.12 })
|
||||||
|
gsap.set(overlay, { opacity: 0.15 + p * 0.55 })
|
||||||
|
gsap.set(el, { y: p * -60, opacity: 1 - p * 1.6 })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
tl.kill()
|
||||||
|
ScrollTrigger.getAll().forEach(t => t.kill())
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
|
ref={sectionRef}
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
background: 'var(--ink)',
|
background: 'var(--ink)',
|
||||||
borderBottom: '3px solid var(--red)',
|
borderBottom:'3px solid var(--red)',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
minHeight: '80vh',
|
height: '100vh',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Background video */}
|
{/* Background video */}
|
||||||
<video
|
<video
|
||||||
|
ref={videoRef}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
autoPlay
|
autoPlay muted loop playsInline
|
||||||
muted
|
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', zIndex: 0, transformOrigin: 'center center' }}
|
||||||
loop
|
src="/hero-bg.mp4#t=2"
|
||||||
playsInline
|
|
||||||
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', zIndex: 0 }}
|
|
||||||
src="/hero-bg.mp4"
|
|
||||||
/>
|
/>
|
||||||
{/* Dark overlay */}
|
{/* Dark overlay */}
|
||||||
<div aria-hidden="true" style={{ position: 'absolute', inset: 0, background: 'linear-gradient(135deg, rgba(26,23,18,0.88) 0%, rgba(26,23,18,0.72) 100%)', zIndex: 1 }} />
|
<div ref={overlayRef} aria-hidden="true" style={{ position: 'absolute', inset: 0, background: 'rgba(16,13,10,0.55)', zIndex: 1 }} />
|
||||||
{/* Grid texture */}
|
{/* Vignette */}
|
||||||
<div aria-hidden="true" style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(to right, rgba(244,239,231,0.03) 1px, transparent 1px), linear-gradient(to bottom, rgba(244,239,231,0.03) 1px, transparent 1px)', backgroundSize: '80px 80px', pointerEvents: 'none', zIndex: 2 }} />
|
<div aria-hidden="true" style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse 90% 80% at 50% 50%, transparent 40%, rgba(16,13,10,0.7) 100%)', zIndex: 2 }} />
|
||||||
{/* Red glow */}
|
|
||||||
<div aria-hidden="true" style={{ position: 'absolute', inset: 0, backgroundImage: 'radial-gradient(ellipse 80% 60% at 60% 50%, rgba(180,20,20,0.12) 0%, transparent 70%)', zIndex: 1 }} />
|
|
||||||
|
|
||||||
|
{/* Centered content */}
|
||||||
<div
|
<div
|
||||||
className="relative max-w-7xl mx-auto px-12 max-md:px-5"
|
ref={textRef}
|
||||||
style={{ paddingTop: 80, paddingBottom: 88, position: 'relative', zIndex: 3 }}
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 3,
|
||||||
|
textAlign: 'center',
|
||||||
|
direction: lang === 'fa' ? 'rtl' : 'ltr',
|
||||||
|
padding: '0 24px',
|
||||||
|
maxWidth: 760,
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{/* Overline */}
|
{/* Tag */}
|
||||||
<motion.div
|
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 10, marginBottom: 28 }}>
|
||||||
initial={{ opacity: 0, y: 8 }}
|
<div style={{ width: 28, height: 1.5, background: 'var(--red)' }} />
|
||||||
animate={{ opacity: 1, y: 0 }}
|
<span style={{ fontSize: 11, fontWeight: 700, letterSpacing: '3px', textTransform: 'uppercase', color: 'rgba(244,239,231,0.5)' }}>
|
||||||
transition={{ duration: 0.4 }}
|
{t.tag}
|
||||||
style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 32 }}
|
|
||||||
>
|
|
||||||
<div style={{ width: 40, height: 2, background: 'var(--red)' }} />
|
|
||||||
<span style={{ fontSize: 10, fontWeight: 700, letterSpacing: '3px', textTransform: 'uppercase', color: 'rgba(244,239,231,0.5)' }}>
|
|
||||||
FUTURE STEEL POLICY INSTITUTE
|
|
||||||
</span>
|
</span>
|
||||||
</motion.div>
|
<div style={{ width: 28, height: 1.5, background: 'var(--red)' }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Headline */}
|
{/* Headline */}
|
||||||
<motion.h1
|
<h1 style={{
|
||||||
initial={{ opacity: 0, y: 20 }}
|
fontSize: 'clamp(36px, 6vw, 88px)',
|
||||||
animate={{ opacity: 1, y: 0 }}
|
fontWeight: 900,
|
||||||
transition={{ duration: 0.6, delay: 0.08 }}
|
lineHeight: 1.08,
|
||||||
style={{ fontSize: 'clamp(36px, 5.5vw, 80px)', fontWeight: 900, lineHeight: 1.05, letterSpacing: '-2px', color: 'var(--paper)', marginBottom: 28, maxWidth: 820 }}
|
letterSpacing: '-2px',
|
||||||
>
|
color: 'var(--paper)',
|
||||||
|
marginBottom: 24,
|
||||||
|
}}>
|
||||||
{t.h1a}
|
{t.h1a}
|
||||||
|
<br />
|
||||||
<span style={{ color: 'var(--red)' }}>{t.h1b}</span>
|
<span style={{ color: 'var(--red)' }}>{t.h1b}</span>
|
||||||
{t.h1c}
|
</h1>
|
||||||
</motion.h1>
|
|
||||||
|
|
||||||
{/* Subtitle */}
|
{/* Subtitle */}
|
||||||
<motion.p
|
<p style={{
|
||||||
initial={{ opacity: 0 }}
|
fontSize: 'clamp(14px, 1.2vw, 18px)',
|
||||||
animate={{ opacity: 1 }}
|
lineHeight: 1.8,
|
||||||
transition={{ duration: 0.5, delay: 0.2 }}
|
color: 'rgba(244,239,231,0.55)',
|
||||||
style={{ fontSize: 'clamp(15px, 1.3vw, 19px)', lineHeight: 1.75, fontWeight: 400, color: 'rgba(244,239,231,0.6)', maxWidth: 560, marginBottom: 48, borderRight: lang === 'fa' ? '2px solid var(--red)' : 'none', borderLeft: lang === 'en' ? '2px solid var(--red)' : 'none', paddingRight: lang === 'fa' ? 18 : 0, paddingLeft: lang === 'en' ? 18 : 0 }}
|
maxWidth: 520,
|
||||||
>
|
margin: '0 auto 36px',
|
||||||
|
}}>
|
||||||
{t.sub}
|
{t.sub}
|
||||||
</motion.p>
|
</p>
|
||||||
|
|
||||||
{/* CTAs */}
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: 10 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.4, delay: 0.32 }}
|
|
||||||
style={{ display: 'flex', gap: 14, flexWrap: 'wrap' }}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
onMouseEnter={() => setRepHov(true)}
|
|
||||||
onMouseLeave={() => setRepHov(false)}
|
|
||||||
style={{ background: repHov ? 'var(--red)' : 'var(--paper)', color: repHov ? 'var(--paper)' : 'var(--ink)', border: 'none', padding: '14px 32px', fontSize: 13, fontWeight: 700, fontFamily: 'Vazir, sans-serif', cursor: 'pointer', transition: 'background 160ms, color 160ms', whiteSpace: 'nowrap', letterSpacing: '0.3px' }}
|
|
||||||
>
|
|
||||||
{t.cta1}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onMouseEnter={() => setPulseHov(true)}
|
|
||||||
onMouseLeave={() => setPulseHov(false)}
|
|
||||||
style={{ background: 'transparent', color: 'rgba(244,239,231,0.8)', border: `1px solid ${pulseHov ? 'rgba(244,239,231,0.7)' : 'rgba(244,239,231,0.3)'}`, padding: '14px 32px', fontSize: 13, fontWeight: 600, fontFamily: 'Vazir, sans-serif', cursor: 'pointer', transition: 'border-color 160ms', whiteSpace: 'nowrap' }}
|
|
||||||
>
|
|
||||||
{t.cta2}
|
|
||||||
</button>
|
|
||||||
</motion.div>
|
|
||||||
|
|
||||||
{/* Video placeholder badge */}
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
transition={{ duration: 0.4, delay: 0.5 }}
|
|
||||||
className="max-md:hidden"
|
|
||||||
style={{ position: 'absolute', bottom: 88, left: 48, display: 'flex', alignItems: 'center', gap: 10, color: 'rgba(244,239,231,0.35)', fontSize: 10, letterSpacing: '2px', textTransform: 'uppercase', direction: 'ltr' }}
|
|
||||||
>
|
|
||||||
<div style={{ width: 36, height: 36, border: '1px solid rgba(244,239,231,0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 14 }}>▶</div>
|
|
||||||
Short film · Future of Steel
|
|
||||||
</motion.div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue