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:
alireza 2026-05-31 12:54:22 +03:30
parent 330e75a6ea
commit a72a3c61f9
3 changed files with 108 additions and 91 deletions

7
package-lock.json generated
View File

@ -14,6 +14,7 @@
"clsx": "^2.1.1",
"dotted-map": "^3.1.0",
"framer-motion": "^12.40.0",
"gsap": "^3.15.0",
"jalaali-js": "^1.2.8",
"lucide-react": "^1.16.0",
"react": "^19.2.6",
@ -2201,6 +2202,12 @@
"dev": true,
"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": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",

View File

@ -16,6 +16,7 @@
"clsx": "^2.1.1",
"dotted-map": "^3.1.0",
"framer-motion": "^12.40.0",
"gsap": "^3.15.0",
"jalaali-js": "^1.2.8",
"lucide-react": "^1.16.0",
"react": "^19.2.6",

View File

@ -1,131 +1,140 @@
import { useState } from 'react'
import { motion } from 'framer-motion'
import { useEffect, useRef } from 'react'
import { useLang } from '@/context/LangContext'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
const T = {
fa: {
h1a: 'جایی که آینده', h1b: ' به تصمیم ', h1c: 'تبدیل می‌شود',
sub: 'تحلیل روندها، فناوری‌ها و سناریوهای آینده صنعت فولاد',
cta1: 'گزارش ویژه ←', cta2: 'نبض صنعت',
video: 'Short film · Future of Steel',
tag: 'اندیشکده‌ فولاد آینده',
h1a: 'جایی که آینده',
h1b: 'به تصمیم تبدیل می‌شود',
sub: 'تحلیل روندها، فناوری‌ها و سناریوهای آینده صنعت فولاد ایران و جهان',
},
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',
cta1: 'Special Report →', cta2: 'Industry Pulse',
video: 'Short film · Future of Steel',
},
}
export default function HeroSection() {
const { lang } = useLang()
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 (
<section
ref={sectionRef}
style={{
position: 'relative',
background: 'var(--ink)',
borderBottom: '3px solid var(--red)',
overflow: 'hidden',
minHeight: '80vh',
display: 'flex',
flexDirection: 'column',
position: 'relative',
background: 'var(--ink)',
borderBottom:'3px solid var(--red)',
overflow: 'hidden',
height: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{/* Background video */}
<video
ref={videoRef}
aria-hidden="true"
autoPlay
muted
loop
playsInline
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', zIndex: 0 }}
src="/hero-bg.mp4"
autoPlay muted loop playsInline
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', zIndex: 0, transformOrigin: 'center center' }}
src="/hero-bg.mp4#t=2"
/>
{/* 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 }} />
{/* Grid texture */}
<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 }} />
{/* 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 }} />
<div ref={overlayRef} aria-hidden="true" style={{ position: 'absolute', inset: 0, background: 'rgba(16,13,10,0.55)', zIndex: 1 }} />
{/* Vignette */}
<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 }} />
{/* Centered content */}
<div
className="relative max-w-7xl mx-auto px-12 max-md:px-5"
style={{ paddingTop: 80, paddingBottom: 88, position: 'relative', zIndex: 3 }}
ref={textRef}
style={{
position: 'relative',
zIndex: 3,
textAlign: 'center',
direction: lang === 'fa' ? 'rtl' : 'ltr',
padding: '0 24px',
maxWidth: 760,
width: '100%',
}}
>
{/* Overline */}
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
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
{/* Tag */}
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 10, marginBottom: 28 }}>
<div style={{ width: 28, height: 1.5, background: 'var(--red)' }} />
<span style={{ fontSize: 11, fontWeight: 700, letterSpacing: '3px', textTransform: 'uppercase', color: 'rgba(244,239,231,0.5)' }}>
{t.tag}
</span>
</motion.div>
<div style={{ width: 28, height: 1.5, background: 'var(--red)' }} />
</div>
{/* Headline */}
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.08 }}
style={{ fontSize: 'clamp(36px, 5.5vw, 80px)', fontWeight: 900, lineHeight: 1.05, letterSpacing: '-2px', color: 'var(--paper)', marginBottom: 28, maxWidth: 820 }}
>
<h1 style={{
fontSize: 'clamp(36px, 6vw, 88px)',
fontWeight: 900,
lineHeight: 1.08,
letterSpacing: '-2px',
color: 'var(--paper)',
marginBottom: 24,
}}>
{t.h1a}
<br />
<span style={{ color: 'var(--red)' }}>{t.h1b}</span>
{t.h1c}
</motion.h1>
</h1>
{/* Subtitle */}
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.2 }}
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 }}
>
<p style={{
fontSize: 'clamp(14px, 1.2vw, 18px)',
lineHeight: 1.8,
color: 'rgba(244,239,231,0.55)',
maxWidth: 520,
margin: '0 auto 36px',
}}>
{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>
</section>
)