diff --git a/package-lock.json b/package-lock.json index 80649f3..44d3238 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "framer-motion": "^12.40.0", "gsap": "^3.15.0", "jalaali-js": "^1.2.8", + "lenis": "^1.3.23", "lucide-react": "^1.16.0", "react": "^19.2.6", "react-apexcharts": "^2.1.0", @@ -2354,6 +2355,37 @@ "json-buffer": "3.0.1" } }, + "node_modules/lenis": { + "version": "1.3.23", + "resolved": "https://registry.npmjs.org/lenis/-/lenis-1.3.23.tgz", + "integrity": "sha512-YxYq3TJqj9sJNv0V9SkyQHejt14xwyIwgDaaMK89Uf9SxQfIszu+gTQSSphh6BWlLTNVKvvXAGkg+Zf+oFIevg==", + "license": "MIT", + "workspaces": [ + "packages/*", + "playground", + "playground/*" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/darkroomengineering" + }, + "peerDependencies": { + "@nuxt/kit": ">=3.0.0", + "react": ">=17.0.0", + "vue": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "react": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", diff --git a/package.json b/package.json index 580b97d..195fad3 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "framer-motion": "^12.40.0", "gsap": "^3.15.0", "jalaali-js": "^1.2.8", + "lenis": "^1.3.23", "lucide-react": "^1.16.0", "react": "^19.2.6", "react-apexcharts": "^2.1.0", diff --git a/public/hero 1.webp b/public/hero 1.webp new file mode 100644 index 0000000..91f20a9 Binary files /dev/null and b/public/hero 1.webp differ diff --git a/public/hero 2.webp b/public/hero 2.webp new file mode 100644 index 0000000..121258a Binary files /dev/null and b/public/hero 2.webp differ diff --git a/public/hero 3.webp b/public/hero 3.webp new file mode 100644 index 0000000..37f1a57 Binary files /dev/null and b/public/hero 3.webp differ diff --git a/src/app/layout/Footer.tsx b/src/app/layout/Footer.tsx index f212cdc..44a9d08 100644 --- a/src/app/layout/Footer.tsx +++ b/src/app/layout/Footer.tsx @@ -55,7 +55,7 @@ function NewsletterBox() { letterSpacing: '2px', textTransform: 'uppercase', marginBottom: 12, }}> - خبرنامه فولاد + خبرنامه‌ فولاد آینده
(null) + + useEffect(() => { + const handler = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false) + } + document.addEventListener('mousedown', handler) + return () => document.removeEventListener('mousedown', handler) + }, []) + return ( - setHovered(true)} - onMouseLeave={() => setHovered(false)} - style={({ isActive }: { isActive: boolean }): CSSProperties => ({ - fontSize: 13, - fontWeight: 600, - color: isActive ? T.red : hovered ? T.ink : T.ink3, - textDecoration: 'none', - padding: '6px 0', - position: 'relative', - whiteSpace: 'nowrap', - transition: 'color 120ms', - borderBottom: isActive ? `2px solid ${T.red}` : '2px solid transparent', - letterSpacing: '0.1px', - })} - > - {label} - +
+ + + + {open && ( + + {SECTIONS.map((item, i) => ( + setOpen(false)} + style={({ isActive }: { isActive: boolean }): CSSProperties => ({ + display: 'flex', alignItems: 'center', justifyContent: 'space-between', + padding: '15px 20px', textDecoration: 'none', + color: isActive ? T.red : '#3d3d3d', + background: isActive ? 'rgba(155,28,28,0.04)' : 'transparent', + borderBottom: i < SECTIONS.length - 1 ? '1px solid rgba(7,29,73,0.07)' : 'none', + transition: 'background 100ms', fontSize: 14, fontWeight: 600, + })} + onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = 'rgba(7,29,73,0.03)' }} + onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = 'transparent' }} + > + {lang === 'fa' ? item.fa : item.en} + + + ))} + + )} + +
) } -function DrawerNavItem({ - to, - label, - onClose, -}: { - to: string - label: string - onClose: () => void -}) { +/* ─── plain nav item with chevron ─── */ +function PlainNavItem({ label, to }: { label: string; to: string }) { + const [hovered, setHovered] = useState(false) + const style: CSSProperties = { + display: 'flex', alignItems: 'center', gap: 4, + fontSize: 13, fontWeight: 600, + color: hovered ? T.ink : T.ink3, + textDecoration: 'none', padding: '6px 0', whiteSpace: 'nowrap', + transition: 'color 120ms', + borderBottom: '2px solid transparent', + } + const handlers = { + onMouseEnter: () => setHovered(true), + onMouseLeave: () => setHovered(false), + } + // internal app route → client-side nav; otherwise plain anchor (hash / external) + const isInternal = to.startsWith('/') + return isInternal ? ( + + {label} + + + ) : ( + + {label} + + + ) +} + +/* ─── drawer item ─── */ +function DrawerNavItem({ to, label, onClose }: { to: string; label: string; onClose: () => void }) { const [hovered, setHovered] = useState(false) return ( setHovered(true)} onMouseLeave={() => setHovered(false)} style={({ isActive }: { isActive: boolean }): CSSProperties => ({ - display: 'block', - padding: '13px 20px', - fontSize: 14, - fontWeight: 600, - color: isActive || hovered ? T.paper : T.ink, - background: isActive ? T.ink : hovered ? T.paper2 : 'transparent', - textDecoration: 'none', - borderBottom: `1px solid ${T.ruleThin}`, - transition: 'background 120ms, color 120ms', + display: 'block', padding: '13px 20px', fontSize: 14, fontWeight: 600, + color: isActive || hovered ? T.paper : T.ink, + background: isActive ? T.ink : hovered ? T.paper2 : 'transparent', + textDecoration: 'none', borderBottom: `1px solid ${T.ruleThin}`, + transition: 'background 120ms, color 120ms', })} > {label} @@ -99,41 +171,28 @@ function DrawerNavItem({ ) } -/* ───────────────────────────────────────────────────────── - MAIN COMPONENT -───────────────────────────────────────────────────────── */ +/* ─── MAIN ─── */ export default function Header() { const [mobileOpen, setMobileOpen] = useState(false) const [isMobile, setIsMobile] = useState(false) const [searchOpen, setSearchOpen] = useState(false) - const [scrolled, setScrolled] = useState(false) + const searchInputRef = useRef(null) const { lang, toggle: toggleLang } = useLang() - /* scroll listener — toggles glass + collapses meta strip */ - useEffect(() => { - const onScroll = () => setScrolled(window.scrollY > 8) - onScroll() - window.addEventListener('scroll', onScroll, { passive: true }) - return () => window.removeEventListener('scroll', onScroll) - }, []) - /* viewport listener */ useEffect(() => { const mq = window.matchMedia('(max-width: 1023px)') const handler = (e: MediaQueryListEvent | MediaQueryList) => setIsMobile(e.matches) - handler(mq) - mq.addEventListener('change', handler) + handler(mq); mq.addEventListener('change', handler) return () => mq.removeEventListener('change', handler) }, []) - /* body scroll lock */ useEffect(() => { document.body.style.overflow = mobileOpen ? 'hidden' : '' return () => { document.body.style.overflow = '' } }, [mobileOpen]) - /* focus search on open */ useEffect(() => { if (searchOpen) searchInputRef.current?.focus() }, [searchOpen]) @@ -141,127 +200,70 @@ export default function Header() { return (
- {/* ═══════════════════════════════════════════ - MAIN BAR — brand | nav | actions - ═══════════════════════════════════════════ */} -
+
- {/* ── Brand ── */} + {/* Brand */} -
- + اندیشکده فولاد آینده - + FUTURE STEEL POLICY INSTITUTE
- {/* ── Center: Nav (desktop only) ── */} + {/* Desktop Nav */} {!isMobile && ( - )} - {/* ── Left: Actions ── */} + {/* Actions */}
- {/* Search icon button */} - {/* Login + lang toggle — desktop only */} {!isMobile && ( <> @@ -273,32 +275,24 @@ export default function Header() { height: 34, padding: '0 12px', fontSize: 12, fontWeight: 700, background: 'transparent', border: `1px solid ${T.ruleThin}`, color: T.ink, cursor: 'pointer', fontFamily: 'inherit', - letterSpacing: '0.5px', transition: 'border-color 120ms, color 120ms', + letterSpacing: '0.5px', transition: 'border-color 120ms', }} - onMouseEnter={e => { e.currentTarget.style.borderColor = T.ink; e.currentTarget.style.color = T.ink }} - onMouseLeave={e => { e.currentTarget.style.borderColor = T.ruleThin; e.currentTarget.style.color = T.ink }} + onMouseEnter={e => { e.currentTarget.style.borderColor = T.ink }} + onMouseLeave={e => { e.currentTarget.style.borderColor = T.ruleThin }} > {lang === 'fa' ? 'EN' : 'FA'} )} - {/* Mobile menu */} {isMobile && (
- {/* ═══════════════════════════════════════════ - SEARCH DROPDOWN - ═══════════════════════════════════════════ */} + {/* Search */} {searchOpen && ( -
+
e.key === 'Escape' && setSearchOpen(false)} style={{ - width: '100%', - border: `1px solid ${T.ink}`, - padding: '12px 16px', - fontSize: 14, - background: T.paper, - color: T.ink, - outline: 'none', - borderRadius: 0, - fontFamily: 'inherit', - direction: 'rtl', + width: '100%', border: `1px solid ${T.ink}`, padding: '12px 16px', + fontSize: 14, background: T.paper, color: T.ink, outline: 'none', + borderRadius: 0, fontFamily: 'inherit', direction: 'rtl', }} />
@@ -352,119 +327,56 @@ export default function Header() { )} - {/* ═══════════════════════════════════════════ - MOBILE DRAWER - ═══════════════════════════════════════════ */} + {/* Mobile Drawer */} {mobileOpen && ( <> - setMobileOpen(false)} - style={{ - position: 'fixed', - inset: 0, - background: 'rgba(7,29,73,0.55)', - zIndex: 100, - }} + style={{ position: 'fixed', inset: 0, background: 'rgba(7,29,73,0.55)', zIndex: 100 }} /> - - {/* Drawer header */} -
- - اندیشکده فولاد آینده - -
- {/* Nav */} - {/* Footer */} -
+
{lang === 'fa' ? 'اشتراک سازمانی' : 'Membership'} {lang === 'fa' ? 'ورود' : 'Login'} -
- {META_LEFT.map((item) => ( - +
+ {META_LEFT.map(item => ( + {lang === 'fa' ? item.fa : item.en} ))} @@ -478,75 +390,33 @@ export default function Header() { ) } -/* ───────────────────────────────────────────────────────── - BUTTONS -───────────────────────────────────────────────────────── */ -function RedCtaButton({ - children, - style: extraStyle, -}: { - children: React.ReactNode - style?: CSSProperties -}) { +/* ─── buttons ─── */ +function RedCtaButton({ children, style: extraStyle }: { children: React.ReactNode; style?: CSSProperties }) { const [hovered, setHovered] = useState(false) return ( - + >{children} ) } -function OutlineButton({ - children, - style: extraStyle, -}: { - children: React.ReactNode - style?: CSSProperties -}) { +function OutlineButton({ children, style: extraStyle }: { children: React.ReactNode; style?: CSSProperties }) { const [hovered, setHovered] = useState(false) return ( - + >{children} ) } diff --git a/src/app/layout/RootLayout.tsx b/src/app/layout/RootLayout.tsx index 084715a..9549b43 100644 --- a/src/app/layout/RootLayout.tsx +++ b/src/app/layout/RootLayout.tsx @@ -1,11 +1,58 @@ +import { useEffect } from 'react' import { Outlet, useLocation } from 'react-router-dom' +import Lenis from 'lenis' +import { gsap } from 'gsap' +import { ScrollTrigger } from 'gsap/ScrollTrigger' import Header from './Header' import { Footer } from './Footer' -import { MarketStrip } from './MarketStrip' import { LangProvider } from '@/context/LangContext' -export default function RootLayout() { +gsap.registerPlugin(ScrollTrigger) + +/* Lenis smooth scroll synced to the GSAP ticker + ScrollTrigger (joinspread.app recipe). + One shared clock means scroll-driven animations never stutter against the smoothing. */ +function useSmoothScroll() { const { pathname } = useLocation() + + useEffect(() => { + if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return + + // Touch devices (iOS/Android) have excellent NATIVE momentum scrolling. + // Running Lenis JS smoothing on top of it = double-smoothing → heavy/laggy feel. + // So: smooth only on desktop pointers; let touch use native scroll. + const isTouch = window.matchMedia('(pointer: coarse)').matches + + const lenis = new Lenis({ + duration: 1.2, + easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), // exponential ease-out + smoothWheel: !isTouch, // desktop wheel: smooth + syncTouch: false, // touch: hands off to native momentum (no JS smoothing) + }) + + // drive ScrollTrigger from Lenis + lenis.on('scroll', ScrollTrigger.update) + + // run Lenis off GSAP's ticker (single RAF clock), lag smoothing off for tight sync + const onTick = (time: number) => lenis.raf(time * 1000) + gsap.ticker.add(onTick) + gsap.ticker.lagSmoothing(0) + + return () => { + lenis.off('scroll', ScrollTrigger.update) + gsap.ticker.remove(onTick) + lenis.destroy() + } + }, []) + + // reset scroll on route change (Lenis owns scroll position) + useEffect(() => { + window.scrollTo({ top: 0, behavior: 'auto' }) + ScrollTrigger.refresh() + }, [pathname]) +} + +export default function RootLayout() { + useSmoothScroll() return (
diff --git a/src/app/router.tsx b/src/app/router.tsx index 3a18343..68a36c1 100644 --- a/src/app/router.tsx +++ b/src/app/router.tsx @@ -17,6 +17,7 @@ import Pulse from '@/pages/Pulse/Pulse' import Membership from '@/pages/Membership/Membership' import Sustainability from '@/pages/Sustainability/Sustainability' import Geopolitics from '@/pages/Geopolitics/Geopolitics' +import AtAGlance from '@/pages/AtAGlance/AtAGlance' export const router = createBrowserRouter([ { @@ -40,6 +41,7 @@ export const router = createBrowserRouter([ { path: 'sustainability', element: }, { path: 'geopolitics', element: }, { path: 'membership', element: }, + { path: 'at-a-glance', element: }, ], }, ]) diff --git a/src/content/factoryReports.ts b/src/content/factoryReports.ts new file mode 100644 index 0000000..3ea2bfd --- /dev/null +++ b/src/content/factoryReports.ts @@ -0,0 +1,70 @@ +/* ───────────────────────────────────────────────────────────── + Factory monthly analysis reports (ماهنامه تحلیل کارخانه). + Separate from the component so it can be fed from the panel/API + later (replace FACTORY_REPORTS with a fetch to the same shape). + `dir` controls the scroll-in direction: 'left' | 'right' | 'up'. + ───────────────────────────────────────────────────────────── */ + +export type ScrollDir = 'left' | 'right' | 'up' + +export type FactoryReport = { + id: string + img: string + dir: ScrollDir + fa: { tag: string; title: string; date: string; excerpt: string } + en: { tag: string; title: string; date: string; excerpt: string } +} + +/* TEMP placeholder dataset — swap for panel/API data later. */ +export const FACTORY_REPORTS: FactoryReport[] = [ + { + id: 'fr1', img: '/news/photo-1504307651254-35680f356dfd.jpg', dir: 'left', + fa: { tag: 'تولید و صادرات', title: 'تحلیل عملکرد تولید و صادرات', date: 'بهمن ۱۴۰۳', excerpt: 'بررسی آمار تولید ماهانه، مقایسه با بودجه سالانه و روند صادرات فولاد تخت در بازارهای منطقه‌ای.' }, + en: { tag: 'Output & Exports', title: 'Production & Export Performance', date: 'Feb 2025', excerpt: 'Monthly output vs. annual budget and the flat-steel export trend across regional markets.' }, + }, + { + id: 'fr2', img: '/news/photo-1581092160607-ee22731c9b4e.jpg', dir: 'left', + fa: { tag: 'بازار و قیمت', title: 'وضعیت بازار و قیمت‌گذاری', date: 'دی ۱۴۰۳', excerpt: 'تحلیل نوسانات قیمت فولاد تخت در بازار داخلی و اثر نرخ ارز بر چشم‌انداز زمستان.' }, + en: { tag: 'Market & Price', title: 'Market & Pricing Outlook', date: 'Jan 2025', excerpt: 'Flat-steel price swings in the domestic market and the FX effect on the winter outlook.' }, + }, + { + id: 'fr3', img: '/news/photo-1518770660439-4636190af475.jpg', dir: 'left', + fa: { tag: 'فناوری', title: 'دیجیتال‌سازی خطوط تولید', date: 'آذر ۱۴۰۳', excerpt: 'گزارشی از پیاده‌سازی سامانه‌های کنترل هوشمند و اثر آن بر بهره‌وری خطوط نورد.' }, + en: { tag: 'Technology', title: 'Production Line Digitalization', date: 'Dec 2024', excerpt: 'Rolling-out smart control systems and their impact on rolling-line productivity.' }, + }, + { + id: 'fr4', img: '/news/photo-1567789884554-0b844b597180.jpg', dir: 'up', + fa: { tag: 'نیروی انسانی', title: 'تحلیل نیروی انسانی و بهره‌وری', date: 'آبان ۱۴۰۳', excerpt: 'شاخص‌های بهره‌وری نیروی کار، آموزش‌های ماهانه و آمار ایمنی شغلی.' }, + en: { tag: 'Workforce', title: 'Workforce & Productivity', date: 'Nov 2024', excerpt: 'Labor productivity indicators, monthly training and occupational-safety statistics.' }, + }, + { + id: 'fr5', img: '/news/photo-1466611653911-95081537e5b7.jpg', dir: 'up', + fa: { tag: 'پایداری', title: 'گزارش پایداری و محیط زیست', date: 'آذر ۱۴۰۳', excerpt: 'شاخص‌های زیست‌محیطی ماهانه؛ مصرف انرژی، کاهش انتشار کربن و بازچرخانی پساب صنعتی.' }, + en: { tag: 'Sustainability', title: 'Sustainability & Environment', date: 'Dec 2024', excerpt: 'Monthly environmental metrics: energy use, carbon reduction and industrial water recycling.' }, + }, + { + id: 'fr6', img: '/news/photo-1570168007204-dfb528c6958f.jpg', dir: 'up', + fa: { tag: 'زنجیره تأمین', title: 'تأمین مواد اولیه و لجستیک', date: 'مهر ۱۴۰۳', excerpt: 'وضعیت تأمین سنگ‌آهن و قراضه، موجودی انبار و تحلیل ریسک زنجیره تأمین.' }, + en: { tag: 'Supply Chain', title: 'Raw Materials & Logistics', date: 'Oct 2024', excerpt: 'Iron-ore and scrap supply, inventory levels and supply-chain risk analysis.' }, + }, + { + id: 'fr7', img: '/news/photo-1611273426858-450d8e3c9fce.jpg', dir: 'right', + fa: { tag: 'سرمایه‌گذاری', title: 'طرح‌های توسعه و سرمایه‌گذاری', date: 'شهریور ۱۴۰۳', excerpt: 'پیشرفت طرح‌های توسعه ظرفیت و تحلیل بازده سرمایه‌گذاری پروژه‌های جاری.' }, + en: { tag: 'Investment', title: 'Expansion & Investment Plans', date: 'Sep 2024', excerpt: 'Capacity-expansion progress and ROI analysis of ongoing projects.' }, + }, + { + id: 'fr8', img: '/news/photo-1485827404703-89b55fcc595e.jpg', dir: 'right', + fa: { tag: 'کیفیت', title: 'کنترل کیفیت با هوش مصنوعی', date: 'مرداد ۱۴۰۳', excerpt: 'کاربرد بینایی ماشین در تشخیص عیوب سطحی و کاهش نرخ ضایعات تولید.' }, + en: { tag: 'Quality', title: 'AI-Driven Quality Control', date: 'Aug 2024', excerpt: 'Machine vision for surface-defect detection and lower production scrap rates.' }, + }, + { + id: 'fr9', img: '/news/photo-1613665813446-82a78c468a1d.jpg', dir: 'right', + fa: { tag: 'انرژی', title: 'تحلیل مصرف انرژی و بهینه‌سازی', date: 'تیر ۱۴۰۳', excerpt: 'الگوی مصرف برق و گاز، فرصت‌های بهینه‌سازی و اثر آن بر بهای تمام‌شده.' }, + en: { tag: 'Energy', title: 'Energy Use & Optimization', date: 'Jul 2024', excerpt: 'Electricity and gas usage patterns, optimization opportunities and cost impact.' }, + }, +] + +export const FACTORY_REPORTS_META = { + fa: { overline: 'MOBARAKEH STEEL', heading: 'ماهنامه تحلیل کارخانه', sub: 'گزارش‌های تحلیلی ماهانه عملکرد، بازار، فناوری و پایداری صنعت فولاد.', cta: 'دریافت گزارش' }, + en: { overline: 'MOBARAKEH STEEL', heading: 'Factory Analysis Monthly', sub: 'Monthly analytical reports on performance, market, technology and sustainability.', cta: 'Get report' }, +} diff --git a/src/content/integrations.ts b/src/content/integrations.ts new file mode 100644 index 0000000..c3acffa --- /dev/null +++ b/src/content/integrations.ts @@ -0,0 +1,51 @@ +/* ───────────────────────────────────────────────────────────── + Integrations / data-sources showcase content. + Separate from the component so it can be fed from the panel/API + later (replace INTEGRATIONS with a fetch to the same shape). + + `logo` (a path under /public) renders a real image when present. + Otherwise the component falls back to the `icon` key (a Lucide icon). + ───────────────────────────────────────────────────────────── */ + +export type IntegrationIcon = + | 'exchange' | 'globe' | 'chart' | 'activity' | 'database' + | 'newspaper' | 'building' | 'trending' | 'coins' + +export type Integration = { + id: string + name: string // tooltip / aria label + icon: IntegrationIcon // fallback Lucide icon + logo?: string // optional real logo path under /public (e.g. '/logos/lme.svg') + accent?: string // tile accent tint + col: number // grid column (1-6) + row: number // grid row (1-5) +} + +/* Global steel data sources & exchanges, scattered across a 6×5 grid. + Empty cells stay as faint placeholders for the "constellation" look. */ +export const INTEGRATIONS: Integration[] = [ + { id: 'lme', name: 'London Metal Exchange', icon: 'exchange', accent: '#c9a227', col: 6, row: 1 }, + { id: 'shfe', name: 'Shanghai Futures Exchange', icon: 'trending', accent: '#c0392b', col: 4, row: 2 }, + { id: 'platts', name: 'S&P Global Platts', icon: 'chart', accent: '#2471a3', col: 6, row: 2 }, + { id: 'reuters', name: 'Reuters', icon: 'newspaper', accent: '#e67e22', col: 3, row: 3 }, + { id: 'fastmarkets',name: 'Fastmarkets', icon: 'activity', accent: '#1e8449', col: 5, row: 3 }, + { id: 'worldsteel', name: 'World Steel Association', icon: 'building', accent: '#7d3c98', col: 4, row: 4 }, + { id: 'mysteel', name: 'Mysteel', icon: 'database', accent: '#16a085', col: 6, row: 4 }, + { id: 'cme', name: 'CME Group', icon: 'coins', accent: '#2980b9', col: 5, row: 5 }, + { id: 'dce', name: 'Dalian Commodity Exchange', icon: 'globe', accent: '#d35400', col: 3, row: 5 }, +] + +export const INTEGRATIONS_META = { + fa: { + overline: 'DATA SOURCES', + heading: 'یکپارچگی با منابع جهانی', + sub: 'اتصال به بیش از ۱۰۰ منبع داده، بورس و پایگاه جهانی فولاد — رصد لحظه‌ای قیمت‌ها، صادرات و روندهای بازار در یک جا.', + cta: 'مشاهده منابع داده', + }, + en: { + overline: 'DATA SOURCES', + heading: 'Seamless Global Integration', + sub: 'Connected to 100+ data sources, exchanges and global steel databases — real-time prices, exports and market trends in one place.', + cta: 'View data sources', + }, +} diff --git a/src/content/radar.ts b/src/content/radar.ts new file mode 100644 index 0000000..e2dd4a2 --- /dev/null +++ b/src/content/radar.ts @@ -0,0 +1,87 @@ +/* ───────────────────────────────────────────────────────────── + Radar (رادار آینده) content layer. + Kept SEPARATE from the component so it can later be served from + the panel / API without touching UI code. When the backend is + ready, replace `RADAR_ITEMS` with a fetch to the same shape. + ───────────────────────────────────────────────────────────── */ + +export type RadarCategory = 'market' | 'tech' | 'commodity' | 'geo' | 'energy' + +export type RadarItem = { + id: string + category: RadarCategory + img: string + /** author shown on hover (replaces the category icon) */ + author: { name: string; avatar: string } + fa: { title: string; date: string; excerpt: string } + en: { title: string; date: string; excerpt: string } +} + +/* category → label (icon is chosen in the component from this key) */ +export const RADAR_CATEGORY_LABEL: Record = { + market: { fa: 'بازار', en: 'Market' }, + tech: { fa: 'فناوری', en: 'Technology' }, + commodity: { fa: 'مواد اولیه', en: 'Commodity' }, + geo: { fa: 'ژئوپلیتیک', en: 'Geopolitics' }, + energy: { fa: 'انرژی', en: 'Energy' }, +} + +/* TEMP placeholder dataset — swap for panel/API data later. */ +export const RADAR_ITEMS: RadarItem[] = [ + { + id: 'r1', category: 'commodity', + img: '/news/photo-1611273426858-450d8e3c9fce.jpg', + author: { name: 'دکتر سارا احمدی', avatar: '/news/photo-1494790108377-be9c29b29330.jpg' }, + fa: { title: 'پیش‌بینی بازار با هوش مصنوعی', date: '۱۴ اردیبهشت ۱۴۰۵', excerpt: 'مدل‌های یادگیری ماشین تقاضای لیتیوم و فلزات باتری را در افق ۲۰۳۵ بازتعریف می‌کنند.' }, + en: { title: 'AI-Powered Market Forecasting', date: 'May 4, 2026', excerpt: 'Machine-learning models are redefining lithium and battery-metal demand on the 2035 horizon.' }, + }, + { + id: 'r2', category: 'tech', + img: '/news/photo-1581092160607-ee22731c9b4e.jpg', + author: { name: 'مهندس رضا کریمی', avatar: '/news/photo-1500648767791-00dcc994a43e.jpg' }, + fa: { title: 'پیش‌بینی بازار با هوش مصنوعی', date: '۱۴ اردیبهشت ۱۴۰۵', excerpt: 'تحول دیجیتال و اتوماسیون هوشمند در حال تغییر بنیادین زنجیره ارزش فولاد هستند.' }, + en: { title: 'AI-Powered Market Forecasting', date: 'May 4, 2026', excerpt: 'Digital transformation and smart automation are fundamentally reshaping the steel value chain.' }, + }, + { + id: 'r3', category: 'market', + img: '/news/photo-1504307651254-35680f356dfd.jpg', + author: { name: 'دکتر مریم رضایی', avatar: '/news/photo-1438761681033-6461ffad8d80.jpg' }, + fa: { title: 'پیش‌بینی بازار با هوش مصنوعی', date: '۱۴ اردیبهشت ۱۴۰۵', excerpt: 'تحلیل روند قیمت طلا و فلزات گران‌بها و اثر آن بر سرمایه‌گذاری در زنجیره فولاد.' }, + en: { title: 'AI-Powered Market Forecasting', date: 'May 4, 2026', excerpt: 'Gold and precious-metal price trends and their effect on steel-chain investment.' }, + }, + { + id: 'r4', category: 'geo', + img: '/news/photo-1518770660439-4636190af475.jpg', + author: { name: 'دکتر علی موسوی', avatar: '/news/photo-1472099645785-5658abf4ff4e.jpg' }, + fa: { title: 'پیش‌بینی بازار با هوش مصنوعی', date: '۱۴ اردیبهشت ۱۴۰۵', excerpt: 'هندسه قدرت جهانی و اثر تحولات ژئوپلیتیک بر بازارهای صادراتی فولاد ایران.' }, + en: { title: 'AI-Powered Market Forecasting', date: 'May 4, 2026', excerpt: 'Global power geometry and the impact of geopolitics on Iran\'s steel export markets.' }, + }, + { + id: 'r5', category: 'energy', + img: '/news/photo-1466611653911-95081537e5b7.jpg', + author: { name: 'مهندس نازنین حسینی', avatar: '/news/photo-1544005313-94ddf0286df2.jpg' }, + fa: { title: 'گذار انرژی و فولاد سبز', date: '۱۲ اردیبهشت ۱۴۰۵', excerpt: 'هیدروژن سبز و برق تجدیدپذیر مسیر کربن‌زدایی صنعت فولاد را شتاب می‌دهند.' }, + en: { title: 'Energy Transition & Green Steel', date: 'May 2, 2026', excerpt: 'Green hydrogen and renewable power are accelerating steel decarbonization.' }, + }, + { + id: 'r6', category: 'tech', + img: '/news/photo-1567789884554-0b844b597180.jpg', + author: { name: 'دکتر سارا احمدی', avatar: '/news/photo-1494790108377-be9c29b29330.jpg' }, + fa: { title: 'اتوماسیون خطوط نورد', date: '۱۰ اردیبهشت ۱۴۰۵', excerpt: 'سیستم‌های کنترل هوشمند بهره‌وری خطوط نورد را تا ۲۲٪ افزایش داده‌اند.' }, + en: { title: 'Rolling Line Automation', date: 'Apr 30, 2026', excerpt: 'Smart control systems have boosted rolling-line productivity by up to 22%.' }, + }, + { + id: 'r7', category: 'commodity', + img: '/news/photo-1570168007204-dfb528c6958f.jpg', + author: { name: 'مهندس رضا کریمی', avatar: '/news/photo-1500648767791-00dcc994a43e.jpg' }, + fa: { title: 'زنجیره تأمین سنگ‌آهن', date: '۸ اردیبهشت ۱۴۰۵', excerpt: 'نوسانات عرضه سنگ‌آهن استرالیا و برزیل و پیامد آن برای تولیدکنندگان منطقه.' }, + en: { title: 'Iron Ore Supply Chain', date: 'Apr 28, 2026', excerpt: 'Australian and Brazilian iron-ore supply swings and their regional fallout.' }, + }, + { + id: 'r8', category: 'market', + img: '/news/photo-1540575467063-178a50c2df87.jpg', + author: { name: 'دکتر مریم رضایی', avatar: '/news/photo-1438761681033-6461ffad8d80.jpg' }, + fa: { title: 'تحلیل بازار جهانی فولاد', date: '۶ اردیبهشت ۱۴۰۵', excerpt: 'سیل صادرات فولاد چین و فشار قیمتی بر بازارهای هدف صادراتی ایران.' }, + en: { title: 'Global Steel Market Analysis', date: 'Apr 26, 2026', excerpt: 'China\'s steel export flood and price pressure on Iran\'s target markets.' }, + }, +] diff --git a/src/index.css b/src/index.css index 501930f..58cc163 100644 --- a/src/index.css +++ b/src/index.css @@ -180,7 +180,12 @@ margin-right: auto !important; } -html { scroll-behavior: smooth; direction: rtl; } +/* scroll-behavior:smooth removed — it fights Lenis JS smoothing. Lenis owns scroll now. */ +html { direction: rtl; } +html.lenis, html.lenis body { height: auto; } +.lenis.lenis-smooth { scroll-behavior: auto !important; } +.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; } +.lenis.lenis-stopped { overflow: hidden; } body { font-family: 'Vazir', 'Inter', system-ui, sans-serif; diff --git a/src/pages/AtAGlance/AtAGlance.tsx b/src/pages/AtAGlance/AtAGlance.tsx new file mode 100644 index 0000000..02cc695 --- /dev/null +++ b/src/pages/AtAGlance/AtAGlance.tsx @@ -0,0 +1,38 @@ +import { useLang } from '@/context/LangContext' +import AtAGlanceSection from '@/pages/Home/sections/AtAGlanceSection' + +export default function AtAGlance() { + const { lang } = useLang() + const isFa = lang === 'fa' + + return ( +
+ {/* page header */} +
+
+
+
+
+ AT A GLANCE +
+

+ {isFa ? 'در یک نگاه' : 'At a Glance'} +

+

+ {isFa + ? 'مهم‌ترین تحولات و رویدادهای راهبردی صنعت فولاد جهان، گزیده و تحلیل‌شده در یک نگاه.' + : 'The most important strategic developments and events in the global steel industry — curated and analyzed at a glance.'} +

+
+
+
+
+ + {/* the slider (reuses the shared section component) */} + +
+ ) +} diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index d2d0243..c206bd1 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,9 +1,13 @@ import HeroSection from './sections/HeroSection' +import SpreadFeatures from './sections/SpreadFeatures' +import IntegrationsSection from './sections/IntegrationsSection' import InternalNewsSection from './sections/InternalNewsSection' import RadarTechSection from './sections/RadarTechSection' import MapEventsSection from './sections/MapEventsSection' -import FactoryNewsletterLayout from './sections/FactoryNewsletterLayout' -import RiskSection from './sections/RiskSection' +import FactoryReportsScroll from './sections/FactoryReportsScroll' +import NewsletterMarquee from './sections/NewsletterMarquee' +import VideocastPodcastSection from './sections/VideocastPodcastSection' +import RiskSection from './sections/RiskSection' import { useLang } from '@/context/LangContext' /* ─── Home page ──────────────────────────────────────── */ @@ -12,10 +16,14 @@ export default function Home() { return (
+ - + + + +
) diff --git a/src/pages/Home/sections/AtAGlanceSection.tsx b/src/pages/Home/sections/AtAGlanceSection.tsx new file mode 100644 index 0000000..4e8c4f7 --- /dev/null +++ b/src/pages/Home/sections/AtAGlanceSection.tsx @@ -0,0 +1,192 @@ +import { useState } from 'react' +import { AnimatePresence, motion } from 'framer-motion' +import { useLang } from '@/context/LangContext' + +const NAVY = '#071d49' +const GOLD = '#c9a227' + +type Glance = { + source: string + date: string; dateEn: string + title: string; titleEn: string + desc: string; descEn: string + img: string +} + +const ITEMS: Glance[] = [ + { + source: 'Financial Times', + date: 'اردیبهشت ۱۴۰۵', dateEn: 'May 2026', + title: 'شوک هیدروژنی به بازار جهانی؛ آغاز رسمی عصر فولادسازی بدون کربن', + titleEn: 'Hydrogen Shock to Global Markets; the Carbon-Free Steel Era Begins', + desc: 'پروژه عظیم «فولاد سبز H2» در سوئد با جذب بی‌سابقه ۶.۵ میلیارد یورو سرمایه، رسماً وارد فاز اجرایی شد تا نخستین کارخانه فولادسازی در مقیاس تجاری بر پایه هیدروژن سبز (بدون نیاز به زغال‌سنگ) در جهان متولد شود. این تأسیسات با هدف کاهش ۹۵ درصدی انتشار CO₂، قواعد رقابت در زنجیره ارزش اروپا را برای همیشه بازتعریف می‌کند.', + descEn: 'Sweden\'s massive "Green Steel H2" project, having raised an unprecedented €6.5bn, has officially entered its execution phase to birth the world\'s first commercial-scale steel plant powered by green hydrogen — redefining Europe\'s value-chain competition for good.', + img: '/news/photo-1504307651254-35680f356dfd.jpg', + }, + { + source: 'Reuters', + date: 'اردیبهشت ۱۴۰۵', dateEn: 'May 2026', + title: 'مکانیسم تنظیم مرزهای کربنی اروپا وارد فاز اجرای کامل شد', + titleEn: 'EU Carbon Border Mechanism Enters Full Enforcement', + desc: 'با اجرای کامل CBAM، صادرکنندگان فولاد به اتحادیه اروپا اکنون موظف‌اند هزینه کامل کربن نهفته در محصولات خود را بپردازند. این تحول، فشار بی‌سابقه‌ای بر تولیدکنندگان مبتنی بر کوره بلند وارد کرده و مزیت رقابتی را به سمت فولاد کم‌کربن سوق می‌دهد.', + descEn: 'With CBAM now fully enforced, steel exporters to the EU must pay the full cost of embedded carbon — placing unprecedented pressure on blast-furnace producers and shifting the competitive edge toward low-carbon steel.', + img: '/news/photo-1581092160607-ee22731c9b4e.jpg', + }, + { + source: 'Bloomberg', + date: 'فروردین ۱۴۰۵', dateEn: 'Apr 2026', + title: 'هوش مصنوعی، تقاضای جهانی فولاد مراکز داده را بازتعریف می‌کند', + titleEn: 'AI Is Rewriting Global Steel Demand for Data Centers', + desc: 'موج ساخت مراکز داده هوش مصنوعی، تقاضای جدیدی برای مقاطع ساختمانی و فولاد ویژه ایجاد کرده است. تحلیلگران پیش‌بینی می‌کنند این روند تا پایان دهه، یکی از پیشران‌های اصلی رشد مصرف فولاد در اقتصادهای پیشرفته باشد.', + descEn: 'The AI data-center construction wave is creating fresh demand for structural sections and specialty steel — projected to be a leading driver of steel consumption growth in advanced economies through the decade.', + img: '/news/photo-1518770660439-4636190af475.jpg', + }, +] + +export default function AtAGlanceSection({ embedded = false }: { embedded?: boolean }) { + const { lang } = useLang() + const isFa = lang === 'fa' + const [i, setI] = useState(0) + const [dir, setDir] = useState<1 | -1>(1) + const it = ITEMS[i] + + const go = (d: 1 | -1) => { + setDir(d) + setI((prev) => (prev + d + ITEMS.length) % ITEMS.length) + } + + const T = { + heading: isFa ? 'در یک نگاه' : 'At a Glance', + } + + return ( +
+
+ + {/* heading with gold rule (hidden when embedded in a page that has its own header) */} + {!embedded && ( +
+

+ {T.heading} +

+
+
+ )} + + {/* image on the LEFT, text on the RIGHT in RTL (matches the reference). + In RTL, the first flex child lands on the right — so we use row-reverse to push image left. */} +
+ {/* ── IMAGE side (with stacked-card shadow) ── */} +
+ {/* back card (offset, behind) — offset away from the text card */} +
+ {/* front image — portrait on desktop, shorter landscape on mobile */} +
+ + + +
+
+ + {/* ── TEXT card side ── */} +
+
+ + + +

+ {isFa ? it.title : it.titleEn} +

+ + {/* date (leading side) + source (trailing side) */} +
+ + + {isFa ? it.date : it.dateEn} + + {it.source} +
+ +

+ {isFa ? it.desc : it.descEn} +

+
+
+ + {/* prev / next controls */} +
+ go(-1)} aria="prev">‹ + go(1)} aria="next">› +
+
+
+
+
+
+ ) +} + +function NavBtn({ children, onClick, aria }: { children: React.ReactNode; onClick: () => void; aria: string }) { + return ( + + ) +} + +function CalendarIcon() { + return ( + + + + + + + ) +} diff --git a/src/pages/Home/sections/FactoryNewsletterLayout.tsx b/src/pages/Home/sections/FactoryNewsletterLayout.tsx deleted file mode 100644 index 19d2f42..0000000 --- a/src/pages/Home/sections/FactoryNewsletterLayout.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import { useLang } from '@/context/LangContext' - -const REPORTS = { - fa: [ - { - title: 'تحلیل عملکرد تولید و صادرات — بهمن ۱۴۰۳', - desc: 'بررسی آمار تولید ماهانه، مقایسه با بودجه سالانه و تحلیل روند صادرات فولاد تخت در بازارهای منطقه‌ای', - date: 'بهمن ۱۴۰۳', - img: '/news/photo-1504307651254-35680f356dfd.jpg', - }, - { - title: 'وضعیت بازار و قیمت‌گذاری — دی ۱۴۰۳', - desc: 'تحلیل نوسانات قیمت فولاد تخت در بازار داخلی، بررسی اثر نرخ ارز و چشم‌انداز بازار در فصل زمستان', - date: 'دی ۱۴۰۳', - img: '/news/photo-1587293852726-70cdb56c2866.jpg', - }, - { - title: 'گزارش پایداری و محیط زیست — آذر ۱۴۰۳', - desc: 'شاخص‌های زیست‌محیطی ماهانه؛ مصرف انرژی، کاهش انتشار کربن و عملکرد سیستم بازیافت پساب صنعتی', - date: 'آذر ۱۴۰۳', - img: '/news/photo-1473341304170-971dccb5ac1e.jpg', - }, - { - title: 'تحلیل نیروی انسانی و بهره‌وری — آبان ۱۴۰۳', - desc: 'بررسی شاخص‌های بهره‌وری نیروی کار، آموزش‌های ماهانه و آمار ایمنی شغلی', - date: 'آبان ۱۴۰۳', - img: '/news/photo-1581092160607-ee22731c9b4e.jpg', - }, - ], - en: [ - { - title: 'Production & Export Performance — Feb 2025', - desc: 'Monthly production figures, annual budget comparison, and flat steel export trend analysis across regional markets.', - date: 'Feb 2025', - img: '/news/photo-1504307651254-35680f356dfd.jpg', - }, - { - title: 'Market Conditions & Pricing — Jan 2025', - desc: 'Flat steel price fluctuations in the domestic market, exchange rate impact analysis, and winter market outlook.', - date: 'Jan 2025', - img: '/news/photo-1587293852726-70cdb56c2866.jpg', - }, - { - title: 'Sustainability & Environment Report — Dec 2024', - desc: 'Monthly environmental KPIs: energy consumption, carbon emission reductions, and industrial wastewater recycling performance.', - date: 'Dec 2024', - img: '/news/photo-1473341304170-971dccb5ac1e.jpg', - }, - { - title: 'Workforce & Productivity Analysis — Nov 2024', - desc: 'Workforce productivity indicators, monthly training hours, and occupational safety statistics.', - date: 'Nov 2024', - img: '/news/photo-1581092160607-ee22731c9b4e.jpg', - }, - ], -} - -const ISSUES = { - fa: [ - { num: '۱۲', title: 'فولاد و تحریم‌های جدید: راهکارهای صادراتی ۱۴۰۴', date: 'بهمن ۱۴۰۳' }, - { num: '۱۱', title: 'قیمت‌گذاری فولاد در بازار داخلی: چالش‌ها و فرصت‌ها', date: 'دی ۱۴۰۳' }, - { num: '۱۰', title: 'آمار تولید فولاد ایران در ۹ ماهه ۱۴۰۳', date: 'آذر ۱۴۰۳' }, - { num: '۰۹', title: 'فولاد سبز؛ چشم‌انداز ایران در برابر رقبای منطقه‌ای', date: 'آبان ۱۴۰۳' }, - { num: '۰۸', title: 'تحلیل بازار عراق: فرصت‌ها و تهدیدها برای صادرکنندگان ایرانی', date: 'مهر ۱۴۰۳' }, - { num: '۰۷', title: 'هوش مصنوعی در کنترل کیفیت فولاد: کاربردها و چالش‌ها', date: 'شهریور ۱۴۰۳' }, - ], - en: [ - { num: '12', title: 'Steel & New Sanctions: Export Strategies for 1404', date: 'Feb 2025' }, - { num: '11', title: 'Steel Pricing in the Domestic Market: Challenges & Opportunities', date: 'Jan 2025' }, - { num: '10', title: 'Iran Steel Production Stats — First 9 Months of 1403', date: 'Dec 2024' }, - { num: '09', title: "Green Steel: Iran's Outlook vs Regional Competitors", date: 'Nov 2024' }, - { num: '08', title: 'Iraq Market Analysis: Opportunities & Threats for Iranian Exporters', date: 'Oct 2024' }, - { num: '07', title: 'AI in Steel Quality Control: Applications & Challenges', date: 'Sep 2024' }, - ], -} - -const T = { - fa: { - factoryOverline: 'MOBARAKEH STEEL', factoryHeading: 'ماهنامه تحلیلی کارخانه', factoryAll: 'مشاهده همه ←', - readMore: 'دریافت گزارش ←', - nlOverline: 'STEEL NEWSLETTER', nlHeading: 'خبرنامه فولاد', nlArchive: 'آرشیو کامل ←', - placeholder: 'ایمیل خود را وارد کنید...', subscribe: 'عضویت', done: '✓ ثبت شد', - nlDesc: 'هر ماه تحلیل‌های تخصصی بازار فولاد، اخبار صنعت و گزارش‌های اختصاصی را در ایمیل خود دریافت کنید.', - }, - en: { - factoryOverline: 'MOBARAKEH STEEL', factoryHeading: 'Monthly Factory Analysis', factoryAll: 'View All →', - readMore: 'Get Report →', - nlOverline: 'STEEL NEWSLETTER', nlHeading: 'Steel Newsletter', nlArchive: 'Full Archive →', - placeholder: 'Enter your email...', subscribe: 'Subscribe', done: '✓ Done', - nlDesc: 'Receive expert steel market analysis, industry news, and exclusive reports in your inbox every month.', - }, -} - -export default function FactoryNewsletterLayout() { - const { lang } = useLang() - const t = T[lang] - - return ( -
-
-
- - {/* ══ LEFT · ماهنامه ══ */} -
-
-
-
{t.factoryOverline}
-

{t.factoryHeading}

-
- {t.factoryAll} -
- -
- {REPORTS[lang].map((r, i) => ( -
{ const d = e.currentTarget as HTMLDivElement; d.style.transform = 'translateY(-3px)'; d.style.boxShadow = '0 8px 24px rgba(7,29,73,0.15)' }} - onMouseLeave={e => { const d = e.currentTarget as HTMLDivElement; d.style.transform = 'translateY(0)'; d.style.boxShadow = '0 2px 10px rgba(7,29,73,0.08)' }} - > -
- (e.currentTarget.style.transform = 'scale(1.07)')} - onMouseLeave={e => (e.currentTarget.style.transform = 'scale(1)')} - /> -
-
- {lang === 'fa' ? 'فولاد مبارکه' : 'Mobarakeh Steel'} -
-
- {r.date} -
-
-
-

{r.title}

-

{r.desc}

- {t.readMore} -
-
- ))} -
-
- - {/* ══ RIGHT · خبرنامه ══ */} -
-
-
{t.nlOverline}
-
-

{t.nlHeading}

- {t.nlArchive} -
-
- - {/* Recent issues — navy number badges */} -
- {ISSUES[lang].map((issue, i) => ( -
((e.currentTarget as HTMLDivElement).style.background = 'var(--paper-2)')} - onMouseLeave={e => ((e.currentTarget as HTMLDivElement).style.background = '#fff')} - > -
{issue.num}
-
-
{issue.title}
-
{issue.date}
-
-
-
- ))} -
-
- -
-
-
- ) -} diff --git a/src/pages/Home/sections/FactoryReportsScroll.tsx b/src/pages/Home/sections/FactoryReportsScroll.tsx new file mode 100644 index 0000000..c620e82 --- /dev/null +++ b/src/pages/Home/sections/FactoryReportsScroll.tsx @@ -0,0 +1,96 @@ +import { motion } from 'framer-motion' +import { ArrowLeft } from 'lucide-react' +import { useLang } from '@/context/LangContext' +import { FACTORY_REPORTS, FACTORY_REPORTS_META, type FactoryReport, type ScrollDir } from '@/content/factoryReports' + +const NAVY = '#071d49' +const GOLD = '#c9a227' +const EASE = [0.22, 1, 0.36, 1] as const + +/* native ScrollAnimation — slides child in from a direction when it enters view */ +function ScrollAnimation({ children, direction = 'up' }: { children: React.ReactNode; direction?: ScrollDir }) { + const offset = + direction === 'left' ? { x: -70, y: 0 } : + direction === 'right' ? { x: 70, y: 0 } : + { x: 0, y: 70 } + return ( + + {children} + + ) +} + +function ReportCard({ r, lang }: { r: FactoryReport; lang: 'fa' | 'en' }) { + const t = r[lang] + return ( +
{ const d = e.currentTarget; d.style.transform = 'translateY(-4px)'; d.style.boxShadow = '0 22px 50px -22px rgba(7,29,73,0.38)' }} + onMouseLeave={(e) => { const d = e.currentTarget; d.style.transform = 'translateY(0)'; d.style.boxShadow = '0 10px 34px -20px rgba(7,29,73,0.30)' }} + > +
+ +
+ {t.tag} +
+
+
{t.date}
+

{t.title}

+

{t.excerpt}

+ + {FACTORY_REPORTS_META[lang].cta} + + +
+
+ ) +} + +export default function FactoryReportsScroll() { + const { lang } = useLang() + const m = FACTORY_REPORTS_META[lang] + + return ( +
+
+ + {/* heading */} + +
{m.overline}
+

{m.heading}

+

{m.sub}

+
+ + {/* 3-column masonry; cards slide in from left / up / right */} +
+ {FACTORY_REPORTS.map((r) => ( + + + + ))} +
+ +
+
+ ) +} diff --git a/src/pages/Home/sections/HeroSection.tsx b/src/pages/Home/sections/HeroSection.tsx index 7bc0c4e..ae922ca 100644 --- a/src/pages/Home/sections/HeroSection.tsx +++ b/src/pages/Home/sections/HeroSection.tsx @@ -1,141 +1,207 @@ -import { useEffect, useRef } from 'react' -import { useLang } from '@/context/LangContext' -import gsap from 'gsap' +import { useRef, useState, useEffect, useLayoutEffect } from 'react' +import { gsap } from 'gsap' import { ScrollTrigger } from 'gsap/ScrollTrigger' +import { useLang } from '@/context/LangContext' gsap.registerPlugin(ScrollTrigger) -const T = { +const NAVY = '#071d49' +const PAPER = '#faf7f4' // cream background revealed under the de-zoom + +const HERO = { fa: { - tag: 'اندیشکده‌ فولاد آینده', - h1: 'جایی که آینده به تصمیم تبدیل می‌شود', - sub: 'تحلیل روندها، فناوری‌ها و سناریوهای آینده صنعت فولاد ایران و جهان', + title: 'آینده فولاد را امروز رصد کن.', + desc: 'هوشمندی که هر بازار را رصد می‌کند و راهبرد برنده را در یک نگاه به تو می‌دهد.', }, en: { - tag: 'Steel Policy Institute', - h1: 'Where the Future Becomes Decision', - sub: 'Analyzing trends, technologies and future scenarios of the global steel industry', + title: 'See the future of steel today.', + desc: 'Intelligence that scans every market and hands you the winning strategy at a glance.', }, } +const CENTER_IMG = '/hero%201.webp' + +type Sat = { img: string; x: number; y: number; w: number; depth: number; big?: boolean } +const SATELLITES: Sat[] = [ + { img: '/news/photo-1567789884554-0b844b597180.jpg', x: -38, y: -26, w: 200, depth: 1.0, big: true }, + { img: '/news/photo-1570168007204-dfb528c6958f.jpg', x: 39, y: 24, w: 200, depth: 1.0, big: true }, + { img: '/news/photo-1504307651254-35680f356dfd.jpg', x: -44, y: 20, w: 150, depth: 1.35 }, + { img: '/news/photo-1558618666-fcd25c85cd64.jpg', x: 43, y: -28, w: 140, depth: 1.4 }, + { img: '/news/photo-1518770660439-4636190af475.jpg', x: -26, y: 34, w: 120, depth: 1.7 }, + { img: '/news/photo-1611273426858-450d8e3c9fce.jpg', x: 27, y: -36, w: 120, depth: 1.7 }, + { img: '/news/photo-1540575467063-178a50c2df87.jpg', x: -30, y: -38, w: 110, depth: 1.9 }, + { img: '/news/photo-1581092160607-ee22731c9b4e.jpg', x: 31, y: 37, w: 110, depth: 1.9 }, +] + +/* ─── MAIN ─── */ export default function HeroSection() { const { lang } = useLang() - const t = T[lang] - - const sectionRef = useRef(null) - const videoRef = useRef(null) + const t = HERO[lang] + const sectionRef = useRef(null) + const pinRef = useRef(null) + const centerRef = useRef(null) const overlayRef = useRef(null) - const textRef = useRef(null) + const headlineRef = useRef(null) + const hintRef = useRef(null) + const satRefs = useRef<(HTMLDivElement | null)[]>([]) + const [isMobile, setIsMobile] = useState(false) 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()) - } + const mq = window.matchMedia('(max-width: 767px)') + const update = () => setIsMobile(mq.matches) + update() + mq.addEventListener('change', update) + return () => mq.removeEventListener('change', update) }, []) + // GSAP ScrollTrigger scrub timeline — drives the whole de-zoom + scatter off scroll progress. + // Synced to Lenis via the GSAP ticker set up in RootLayout (joinspread.app recipe). + useLayoutEffect(() => { + const section = sectionRef.current + if (!section) return + + const ctx = gsap.context(() => { + // single scrubbed timeline pinned for the section's scroll length + const tl = gsap.timeline({ + scrollTrigger: { + trigger: section, + start: 'top top', + end: 'bottom bottom', + scrub: true, + }, + }) + + // background morph navy → cream + tl.fromTo(pinRef.current, { backgroundColor: NAVY }, { backgroundColor: PAPER, ease: 'none' }, 0) + + // center image: full-bleed cover (scale 2.55) → centered rounded card + tl.fromTo(centerRef.current, + { scale: 2.55, borderRadius: 0 }, + { scale: 1, borderRadius: 18, ease: 'none' }, 0) + + // legibility overlay fades out as the image shrinks + tl.fromTo(overlayRef.current, { opacity: 0.42 }, { opacity: 0, ease: 'none', duration: 0.4 }, 0) + + // headline lifts + blurs + fades out as the de-zoom begins + tl.fromTo(headlineRef.current, + { yPercent: 0, opacity: 1, filter: 'blur(0px)' }, + { yPercent: -10, opacity: 0, filter: 'blur(8px)', ease: 'none', duration: 0.35 }, 0) + + // scroll hint fades immediately + tl.fromTo(hintRef.current, { opacity: 1 }, { opacity: 0, ease: 'none', duration: 0.08 }, 0) + + // satellites: clustered behind center → fan out to scattered positions with parallax + satRefs.current.forEach((el, i) => { + if (!el) return + const s = SATELLITES[i] + tl.fromTo(el, + { xPercent: -50, yPercent: -50, x: `${s.x * 0.12}vw`, y: `${s.y * 0.12}vh`, scale: 0.4, opacity: 0 }, + { x: `${s.x * s.depth}vw`, y: `${s.y * s.depth}vh`, scale: 1, opacity: 1, ease: 'none' }, 0) + }) + }, section) + + return () => ctx.revert() + }, [isMobile]) + return ( -
- {/* Background video */} -
+
) } diff --git a/src/pages/Home/sections/IntegrationsSection.tsx b/src/pages/Home/sections/IntegrationsSection.tsx new file mode 100644 index 0000000..8b0ec31 --- /dev/null +++ b/src/pages/Home/sections/IntegrationsSection.tsx @@ -0,0 +1,148 @@ +import { motion } from 'framer-motion' +import { + ArrowLeft, Landmark, Globe, BarChart3, Activity, Database, + Newspaper, Building2, TrendingUp, Coins, type LucideIcon, +} from 'lucide-react' +import { Link } from 'react-router-dom' +import { useLang } from '@/context/LangContext' +import { INTEGRATIONS, INTEGRATIONS_META, type IntegrationIcon } from '@/content/integrations' + +const NAVY = '#071d49' +const GOLD = '#c9a227' +const COLS = 6 +const ROWS = 5 +const EASE = [0.22, 1, 0.36, 1] as const + +const ICONS: Record = { + exchange: Landmark, globe: Globe, chart: BarChart3, activity: Activity, + database: Database, newspaper: Newspaper, building: Building2, trending: TrendingUp, coins: Coins, +} + +const CELL = new Map(INTEGRATIONS.map((it) => [`${it.col}-${it.row}`, it])) + +export default function IntegrationsSection() { + const { lang } = useLang() + const isFa = lang === 'fa' + const m = INTEGRATIONS_META[lang] + + return ( +
+ {/* drifting gold glow */} + + +
+
+ + {/* ── TEXT side ── */} +
+ +
{m.overline}
+

+ {m.heading} +

+

+ {m.sub} +

+ { (e.currentTarget as HTMLElement).style.opacity = '0.88'; (e.currentTarget as HTMLElement).style.transform = 'translateX(-3px)' }} + onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.opacity = '1'; (e.currentTarget as HTMLElement).style.transform = 'translateX(0)' }} + > + {m.cta} + + +
+
+ + {/* ── GRID side ── */} +
+
+ {Array.from({ length: COLS * ROWS }).map((_, idx) => { + const col = (idx % COLS) + 1 + const row = Math.floor(idx / COLS) + 1 + const it = CELL.get(`${col}-${row}`) + + if (!it) { + // empty placeholder cell — faint outline, soft breathing + return ( + + ) + } + + const Icon = ICONS[it.icon] + const delay = (col + row) * 0.07 + return ( + + {/* accent glow halo */} + + {/* gentle float of the icon itself */} + + {it.logo ? ( + {it.name} + ) : ( + + )} + + + ) + })} +
+
+ +
+
+
+ ) +} diff --git a/src/pages/Home/sections/NewsletterMarquee.tsx b/src/pages/Home/sections/NewsletterMarquee.tsx new file mode 100644 index 0000000..271cc8f --- /dev/null +++ b/src/pages/Home/sections/NewsletterMarquee.tsx @@ -0,0 +1,113 @@ +import { useLang } from '@/context/LangContext' + +const NAVY = '#071d49' +const GOLD = '#c9a227' + +/* خبرنامه فولاد — recent issue titles, scrolled as a continuous marquee. */ +const ISSUES = { + fa: [ + { num: '۱۲', title: 'فولاد و تحریم‌های جدید: راهکارهای صادراتی ۱۴۰۴', to: '#newsletter-12' }, + { num: '۱۱', title: 'قیمت‌گذاری فولاد در بازار داخلی: چالش‌ها و فرصت‌ها', to: '#newsletter-11' }, + { num: '۱۰', title: 'آمار تولید فولاد ایران در ۹ ماهه ۱۴۰۳', to: '#newsletter-10' }, + { num: '۰۹', title: 'فولاد سبز؛ چشم‌انداز ایران در برابر رقبای منطقه‌ای', to: '#newsletter-09' }, + { num: '۰۸', title: 'تحلیل بازار عراق: فرصت‌ها و تهدیدها برای صادرکنندگان ایرانی', to: '#newsletter-08' }, + { num: '۰۷', title: 'هوش مصنوعی در کنترل کیفیت فولاد: کاربردها و چالش‌ها', to: '#newsletter-07' }, + ], + en: [ + { num: '12', title: 'Steel & New Sanctions: Export Strategies for 1404', to: '#newsletter-12' }, + { num: '11', title: 'Steel Pricing in the Domestic Market: Challenges & Opportunities', to: '#newsletter-11' }, + { num: '10', title: 'Iran Steel Production Stats — First 9 Months of 1403', to: '#newsletter-10' }, + { num: '09', title: "Green Steel: Iran's Outlook Against Regional Rivals", to: '#newsletter-09' }, + { num: '08', title: 'Iraq Market Analysis: Opportunities & Threats for Exporters', to: '#newsletter-08' }, + { num: '07', title: 'AI in Steel Quality Control: Applications & Challenges', to: '#newsletter-07' }, + ], +} + +const T = { + fa: { overline: 'STEEL NEWSLETTER', heading: 'خبرنامه فولاد', live: 'به‌روزرسانی زنده' }, + en: { overline: 'STEEL NEWSLETTER', heading: 'Steel Newsletter', live: 'Live updates' }, +} + +function Item({ num, title, to }: { num: string; title: string; to: string }) { + return ( + { (e.currentTarget.querySelector('.nm-title') as HTMLElement).style.color = GOLD }} + onMouseLeave={(e) => { (e.currentTarget.querySelector('.nm-title') as HTMLElement).style.color = 'rgba(255,255,255,0.9)' }} + > + {num} + {title} + {/* blinking live dot on the (right) trailing side of each item */} + + + ) +} + +export default function NewsletterMarquee() { + const { lang } = useLang() + const isFa = lang === 'fa' + const t = T[lang] + const items = ISSUES[lang] + // duplicate the list so the scroll loops seamlessly + const loop = [...items, ...items] + + return ( +
+ {/* heading */} +
+
+ {t.overline} + +

{t.heading}

+ {/* live pulse */} + + + {t.live} + +
+
+ + {/* moving track with edge fades */} +
+
+
+
+ {loop.map((it, i) => )} +
+
+ + +
+ ) +} diff --git a/src/pages/Home/sections/RadarTechSection.tsx b/src/pages/Home/sections/RadarTechSection.tsx index afe856a..ce6a960 100644 --- a/src/pages/Home/sections/RadarTechSection.tsx +++ b/src/pages/Home/sections/RadarTechSection.tsx @@ -1,55 +1,7 @@ import { Link } from 'react-router-dom' import { useLang } from '@/context/LangContext' - -/* ─── Radar data ─────────────────────────────────────── */ -const SIGNALS = { - fa: [ - { - tag: 'چین', level: 'critical', levelLabel: 'بحرانی', - title: 'صادرات فولاد چین به بالاترین رکورد ۸ ساله رسید', - desc: 'در بهمن ۱۴۰۳ صادرات فولاد چین به ۱۰.۷ میلیون تن رسید — بالاترین رقم از ۲۰۱۶ — که فشار قیمتی شدیدی بر بازارهای منطقه‌ای از جمله ایران وارد کرد.', - date: 'بهمن ۱۴۰۳', img: '/news/photo-1611273426858-450d8e3c9fce.jpg', - accentColor: '#7f1d1d', levelColor: '#7f1d1d', - }, - { - tag: 'اروپا', level: 'high', levelLabel: 'بالا', - title: 'CBAM اروپا از فروردین ۱۴۰۴ اجرایی می‌شود', - desc: 'مکانیزم تعدیل مرزی کربن اتحادیه اروپا برای فولاد اجرایی شد. صادرکنندگان ایرانی باید الزامات جدید گزارش‌دهی کربن را رعایت کنند.', - date: 'دی ۱۴۰۳', img: '/news/photo-1473341304170-971dccb5ac1e.jpg', - accentColor: '#dc2626', levelColor: '#dc2626', - }, - { - tag: 'هند', level: 'medium', levelLabel: 'متوسط', - title: 'هند ظرفیت تولید فولاد خود را تا ۲۰۳۰ دو برابر می‌کند', - desc: 'با سرمایه‌گذاری ۱۴۲ میلیارد دلاری دولت هند، این کشور تا ۲۰۳۰ به ظرفیت ۳۰۰ میلیون تن می‌رسد و رقیب جدی ایران در بازارهای صادراتی می‌شود.', - date: 'آذر ۱۴۰۳', img: '/news/photo-1566873535350-6586b0b7a1f2.jpg', - accentColor: '#b45309', levelColor: '#b45309', - }, - ], - en: [ - { - tag: 'China', level: 'critical', levelLabel: 'Critical', - title: "China's Steel Exports Hit an 8-Year Record High", - desc: "In Feb 2025, China's steel exports reached 10.7 million tons — the highest since 2016 — putting severe price pressure on regional markets including Iran.", - date: 'Feb 2025', img: '/news/photo-1611273426858-450d8e3c9fce.jpg', - accentColor: '#7f1d1d', levelColor: '#7f1d1d', - }, - { - tag: 'Europe', level: 'high', levelLabel: 'High', - title: "Europe's CBAM Mechanism Takes Effect from Spring 2025", - desc: "The EU's Carbon Border Adjustment Mechanism for steel is now in force. Iranian exporters must comply with new carbon reporting requirements.", - date: 'Jan 2025', img: '/news/photo-1473341304170-971dccb5ac1e.jpg', - accentColor: '#dc2626', levelColor: '#dc2626', - }, - { - tag: 'India', level: 'medium', levelLabel: 'Medium', - title: 'India to Double Steel Production Capacity by 2030', - desc: 'With a $142 billion government investment, India targets 300 million tons capacity by 2030, becoming a serious competitor in Iran\'s export markets.', - date: 'Dec 2024', img: '/news/photo-1566873535350-6586b0b7a1f2.jpg', - accentColor: '#b45309', levelColor: '#b45309', - }, - ], -} +import { RadarCard } from '@/pages/Radar/Radar' +import { RADAR_ITEMS } from '@/content/radar' /* ─── Tech data ──────────────────────────────────────── */ const TECHS = { @@ -125,7 +77,6 @@ const T = { export default function RadarTechSection() { const { lang } = useLang() const t = T[lang] - const signals = SIGNALS[lang] const techs = TECHS[lang] return ( @@ -143,44 +94,9 @@ export default function RadarTechSection() { {t.radarAll}
-
- {signals.map((s, i) => ( -
{ (e.currentTarget as HTMLDivElement).style.transform = 'translateY(-4px)'; (e.currentTarget as HTMLDivElement).style.boxShadow = '0 10px 30px rgba(0,0,0,0.12)' }} - onMouseLeave={e => { (e.currentTarget as HTMLDivElement).style.transform = 'translateY(0)'; (e.currentTarget as HTMLDivElement).style.boxShadow = '0 2px 12px rgba(0,0,0,0.07)' }} - > - {/* Image */} -
- (e.currentTarget.style.transform = 'scale(1.06)')} - onMouseLeave={e => (e.currentTarget.style.transform = 'scale(1)')} - /> -
- {/* Level badge */} -
- {s.levelLabel} - {s.tag} -
-
- - {/* Content */} -
-

{s.title}

-

{s.desc}

-
{s.date}
-
-
+
+ {RADAR_ITEMS.slice(0, 4).map((item) => ( + ))}
diff --git a/src/pages/Home/sections/SpreadFeatures.tsx b/src/pages/Home/sections/SpreadFeatures.tsx new file mode 100644 index 0000000..e5a51b7 --- /dev/null +++ b/src/pages/Home/sections/SpreadFeatures.tsx @@ -0,0 +1,199 @@ +import { useRef, useState } from 'react' +import { motion, AnimatePresence, useScroll, useMotionValueEvent } from 'framer-motion' +import { useLang } from '@/context/LangContext' + +const NAVY = '#071d49' +const GOLD = '#c9a227' +const EASE = [0.22, 1, 0.36, 1] as const + +type Feat = { + img: string + imgSide: 'left' | 'right' + imgPos?: string + fa: { kicker: string; title: string; desc: string; stat: string; statTail: string } + en: { kicker: string; title: string; desc: string; stat: string; statTail: string } +} + +/* Your 3 hero images. As you scroll, they replace each other in place with a fade. */ +const FEATURES: Feat[] = [ + { + img: '/hero%201.webp', imgSide: 'right', imgPos: 'center 35%', + fa: { + kicker: 'اندیشکده فولاد آینده', + title: 'بینش راهبردی', + desc: 'عصرِ رقابت بر سر تناژ به پایان رسیده است. در جهان پرشتاب فردا، هژمونی صنعتی از «عمق تحلیل»، «تسلط بر شبکه‌های داده» و «جسارت در طراحی آینده» خلق می‌شود.', + stat: '۶ بازار', + statTail: 'رصد ۲۴ ساعته. هشدار در کمتر از ۱۲ ثانیه.', + }, + en: { + kicker: 'Future Steel Institute', + title: 'Strategic Vision', + desc: 'The era of tonnage competition is over. Industrial leadership is built on analytical depth, data mastery, and the courage to design the future.', + stat: '6 markets', + statTail: 'Scanned 24/7. Alerts in under 12 seconds.', + }, + }, + { + img: '/hero%202.webp', imgSide: 'left', imgPos: 'center center', + fa: { + kicker: 'فناوری صنعتی', + title: 'رادار فناوری', + desc: 'انقلاب هوش مصنوعی در حال بازنویسی DNA زنجیره ارزش است. ما سیگنال‌های ضعیف فناوری را شکار کرده و آن‌ها را به استراتژی‌های برنده برای رهبران صنعتی تبدیل می‌کنیم.', + stat: '٪۳۸', + statTail: 'میانگین حاشیه سود در هر معامله.', + }, + en: { + kicker: 'Industrial Technology', + title: 'Technology Radar', + desc: 'AI is rewriting the DNA of the value chain. We hunt weak technology signals and transform them into winning strategies for industry leaders.', + stat: '38%', + statTail: 'Average margin per deal.', + }, + }, + { + img: '/hero%203.webp', imgSide: 'right', imgPos: 'center center', + fa: { + kicker: 'ژئواکونومی', + title: 'کلان‌روندها', + desc: 'هندسه قدرت در صنعت در حال تغییر است. رمزگشایی از تقاطع پیچیده ژئواکونومی، مقررات کربنی و گذار انرژی؛ قطب‌نمای شما برای عبور ایمن از طوفان‌های آینده.', + stat: '۲۰ ساعت', + statTail: 'صرفه‌جویی هفتگی نسبت به روش دستی.', + }, + en: { + kicker: 'Geo-Economics', + title: 'Macro Trends', + desc: 'The geometry of power is shifting. Decoding the complex intersection of geo-economics, carbon regulations and energy transition — your compass for navigating the storms ahead.', + stat: '20 hrs', + statTail: 'Saved per week vs. manual.', + }, + }, +] + +/* fade-up + blur wrapper — fires fast on mount (re-keyed per active slide) */ +function FadeUp({ children, delay = 0 }: { children: React.ReactNode; delay?: number }) { + return ( + + {children} + + ) +} + +/* the text column for the active slide */ +function TextPanel({ feat, lang }: { feat: Feat; lang: 'fa' | 'en' }) { + const t = feat[lang] + return ( +
+ +
+ {t.kicker} +
+
+ + +

+ {t.title} +

+
+ +

+ {t.desc} +

+
+ +
+
+ {t.stat} + {t.statTail} +
+
+
+
+ ) +} + +/* progress rail */ +function Rail({ active, total }: { active: number; total: number }) { + return ( +
+ {Array.from({ length: total }).map((_, i) => ( +
+ ))} +
+ ) +} + +export default function SpreadFeatures() { + const { lang } = useLang() + const containerRef = useRef(null) + const [active, setActive] = useState(0) + + const { scrollYProgress } = useScroll({ target: containerRef, offset: ['start start', 'end end'] }) + useMotionValueEvent(scrollYProgress, 'change', (v) => { + const next = v < 0.34 ? 0 : v < 0.67 ? 1 : 2 + if (next !== active) setActive(next) + }) + + return ( + /* 300vh tall → inner pins for the whole scroll; each ~third = one slide. + Each full slide (image + text) is stacked and crossfaded, so the layout can + alternate per slide (img 1 right · img 2 left · img 3 right) without jumping. */ +
+
+ + {FEATURES.map((f, i) => { + const isActive = i === active + const imgRight = f.imgSide === 'right' + return ( + + {/* TEXT */} +
+ {/* only the active slide's text animates in (re-keyed so it replays each time) */} + {isActive && ( + +
+ +
+
+ )} +
+ + {/* IMAGE */} +
+ + + +
+
+ ) + })} + + +
+
+ ) +} diff --git a/src/pages/Home/sections/SteelNewsletterSection.tsx b/src/pages/Home/sections/SteelNewsletterSection.tsx deleted file mode 100644 index cb4dcec..0000000 --- a/src/pages/Home/sections/SteelNewsletterSection.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { useState } from 'react' -import { motion } from 'framer-motion' -import { useLang } from '@/context/LangContext' - -const ISSUES = { - fa: [ - { num: '۱۲', title: 'فولاد و تحریم‌های جدید: راهکارهای صادراتی ۱۴۰۴', date: 'بهمن ۱۴۰۳' }, - { num: '۱۱', title: 'قیمت‌گذاری فولاد در بازار داخلی: چالش‌ها و فرصت‌ها', date: 'دی ۱۴۰۳' }, - { num: '۱۰', title: 'آمار تولید فولاد ایران در ۹ ماهه ۱۴۰۳', date: 'آذر ۱۴۰۳' }, - ], - en: [ - { num: '12', title: 'Steel & New Sanctions: Export Strategies for 1404', date: 'Feb 2025' }, - { num: '11', title: 'Steel Pricing in the Domestic Market: Challenges & Opportunities', date: 'Jan 2025' }, - { num: '10', title: "Iran Steel Production Stats — First 9 Months of 1403", date: 'Dec 2024' }, - ], -} - - -const T = { - fa: { - overline: 'STEEL NEWSLETTER', heading: 'خبرنامه فولاد', archive: 'آرشیو کامل ←', - issueLabel: 'خبرنامه ماهانه', placeholder: 'ایمیل خود را وارد کنید...', - subscribe: 'عضویت در خبرنامه', done: '✓ ثبت شد', - overlinePlants: 'MAJOR PLANTS', headingPlants: 'کارخانه‌های بزرگ فولاد ایران', - allPlants: 'مشاهده همه ←', capacity: 'ظرفیت سالانه', - }, - en: { - overline: 'STEEL NEWSLETTER', heading: 'Steel Newsletter', archive: 'Full Archive →', - issueLabel: 'Monthly Issue', placeholder: 'Enter your email...', - subscribe: 'Subscribe', done: '✓ Subscribed', - overlinePlants: 'MAJOR PLANTS', headingPlants: "Iran's Major Steel Plants", - allPlants: 'View All →', capacity: 'Annual capacity', - }, -} - -export default function SteelNewsletterSection() { - const { lang } = useLang() - const t = T[lang] - const [email, setEmail] = useState('') - const [done, setDone] = useState(false) - - return ( -
-
- - {/* Newsletter issues */} -
-
-
-
{t.overline}
-

{t.heading}

-
- {t.archive} -
- -
- {ISSUES[lang].map((issue, i) => ( - -
- {issue.num} -
-
-
{t.issueLabel}
-

{issue.title}

-
{issue.date}
-
-
- ))} -
- - {/* Subscribe bar */} -
- setEmail(e.target.value)} - placeholder={t.placeholder} - disabled={done} - style={{ flex: 1, border: 'none', padding: '11px 14px', fontSize: 13, fontFamily: 'Vazir, sans-serif', background: 'var(--paper)', color: 'var(--ink)', outline: 'none', direction: lang === 'fa' ? 'rtl' : 'ltr' }} - /> - -
-
- -
-
- ) -} diff --git a/src/pages/Home/sections/VideocastPodcastSection.tsx b/src/pages/Home/sections/VideocastPodcastSection.tsx new file mode 100644 index 0000000..3a66ec7 --- /dev/null +++ b/src/pages/Home/sections/VideocastPodcastSection.tsx @@ -0,0 +1,229 @@ +import { useState } from 'react' +import { useLang } from '@/context/LangContext' + +const VIDEOS = [ + { + id: 1, + fa: { title: 'چشم‌انداز فولاد ایران در ۱۴۰۴', desc: 'بررسی روندهای تولید، صادرات و سیاست‌گذاری صنعت فولاد ایران در سال پیش رو' }, + en: { title: 'Iran Steel Outlook 1404', desc: 'Production trends, exports and policy analysis for Iran\'s steel industry' }, + ep: 'EP.12', duration: '۴۲ دقیقه', thumb: '/news/photo-1504307651254-35680f356dfd.jpg', + }, + { + id: 2, + fa: { title: 'تحلیل بازار جهانی فلزات پایه', desc: 'نوسانات قیمت مس، آلومینیوم و روی و تأثیر آن بر زنجیره فولاد' }, + en: { title: 'Global Base Metals Analysis', desc: 'Price volatility in copper, aluminum and zinc and its steel chain impact' }, + ep: 'EP.11', duration: '۳۸ دقیقه', thumb: '/news/photo-1558618666-fcd25c85cd64.jpg', + }, + { + id: 3, + fa: { title: 'ژئوپلیتیک و صادرات فولاد', desc: 'تأثیر تحولات منطقه‌ای بر بازارهای صادراتی فولاد ایران و فرصت‌های پیش رو' }, + en: { title: 'Geopolitics & Steel Exports', desc: 'How regional developments shape Iran\'s steel export markets' }, + ep: 'EP.10', duration: '۵۱ دقیقه', thumb: '/news/photo-1504328345606-18bbc8c9d7d1.jpg', + }, + { + id: 4, + fa: { title: 'سرمایه‌گذاری در صنعت فولاد', desc: 'فرصت‌ها و چالش‌های سرمایه‌گذاری در بخش‌های مختلف زنجیره ارزش فولاد' }, + en: { title: 'Steel Industry Investment', desc: 'Opportunities and challenges across the steel value chain' }, + ep: 'EP.09', duration: '۴۵ دقیقه', thumb: '/news/photo-1611273426858-450d8e3c9fce.jpg', + }, +] + +const PODCASTS = [ + { + id: 1, + fa: { title: 'نبض صنعت — فصل سوم', ep: 'قسمت ۱۲', desc: 'گفتگو با مدیران ارشد صنعت فولاد درباره چالش‌های تامین مالی و توسعه ظرفیت' }, + en: { title: 'Industry Pulse — Season 3', ep: 'Episode 12', desc: 'Steel executives on financing challenges and capacity expansion' }, + duration: '۵۸ دقیقه', cover: '/news/photo-1540575467063-178a50c2df87.jpg', + }, + { + id: 2, + fa: { title: 'صدای بازار', ep: 'قسمت ۸', desc: 'تحلیل هفتگی قیمت‌های شمش، میلگرد و مقاطع با کارشناسان بازار' }, + en: { title: 'Market Voice', ep: 'Episode 8', desc: 'Weekly analysis of billet, rebar and section prices with market experts' }, + duration: '۳۲ دقیقه', cover: '/news/photo-1566873535350-6586b0b7a1f2.jpg', + }, + { + id: 3, + fa: { title: 'آینده سبز فولاد', ep: 'قسمت ۵', desc: 'مسیر صنعت فولاد ایران به سمت کاهش کربن و پایداری' }, + en: { title: 'Green Steel Future', ep: 'Episode 5', desc: 'Iran\'s steel sector path toward carbon reduction and sustainability' }, + duration: '۴۴ دقیقه', cover: '/news/photo-1578662996442-48f60103fc96.jpg', + }, +] + +const T = { + fa: { + videoLabel: 'ویدیوکست', videoSub: 'آخرین گفتگوهای تصویری اندیشکده', + podLabel: 'پادکست', podSub: 'شنیدنی‌های اندیشکده فولاد آینده', + watchAll: 'همه ویدیوها ←', listenAll: 'همه قسمت‌ها ←', + }, + en: { + videoLabel: 'Videocast', videoSub: 'Latest video conversations from the Institute', + podLabel: 'Podcast', podSub: 'Audio episodes from Future Steel Institute', + watchAll: 'All videos →', listenAll: 'All episodes →', + }, +} + +export default function VideocastPodcastSection() { + const { lang } = useLang() + const t = T[lang] + const [activeVideo, setActiveVideo] = useState(0) + const [activePod, setActivePod] = useState(0) + const pod = PODCASTS[activePod] + + return ( +
+
+
+ + {/* ── VIDEOCAST 70% ── */} +
+ + {/* heading */} +
+
+
+
+ VIDEOCAST +
+

{t.videoLabel}

+

{t.videoSub}

+
+ +
+ + {/* featured */} +
+ +
+ {/* play */} +
+
+
+ {/* meta */} +
+
+ {VIDEOS[activeVideo].ep} + {VIDEOS[activeVideo].duration} +
+

{VIDEOS[activeVideo][lang].title}

+

{VIDEOS[activeVideo][lang].desc}

+
+
+ + {/* episode cards */} +
+ {VIDEOS.map((v, i) => ( + + ))} +
+
+ + {/* ── PODCAST 30% ── */} +
+ + {/* heading */} +
+
+
+ PODCAST +
+

{t.podLabel}

+

{t.podSub}

+
+ + {/* circular cover + info */} +
+
+ {/* spinning gradient ring */} +
+ {/* white gap ring */} +
+ +
+ +
+
{pod[lang].ep}
+

{pod[lang].title}

+

{pod[lang].desc}

+ {pod.duration} +
+ + {/* progress */} +
+
+
+
+
+ ۲۰:۱۲{pod.duration} +
+
+
+ + {/* episode list */} +
+ {PODCASTS.map((p, i) => ( + + ))} +
+ + +
+ +
+
+ +
+ ) +} diff --git a/src/pages/Pulse/Pulse.tsx b/src/pages/Pulse/Pulse.tsx index 9daecc6..26141d4 100644 --- a/src/pages/Pulse/Pulse.tsx +++ b/src/pages/Pulse/Pulse.tsx @@ -349,10 +349,6 @@ const PRODUCT_CATEGORIES = [ }, ] -const CONTENT_SECTIONS = [ - 'صنعت فولاد', 'بازار سرمایه', 'فلزات و کامودیتی', - 'ارزهای دیجیتال', 'بازار ارز و طلا', 'اقتصاد کلان', -] const DATA_SOURCES = ['Platts', 'Fastmarkets', 'Bainfo China', 'Dlpibica'] function CategoryColumn({ cat }: { cat: typeof PRODUCT_CATEGORIES[0] }) { diff --git a/src/pages/Radar/Radar.tsx b/src/pages/Radar/Radar.tsx index b71723c..236b8d9 100644 --- a/src/pages/Radar/Radar.tsx +++ b/src/pages/Radar/Radar.tsx @@ -1,260 +1,207 @@ -import { motion } from 'framer-motion' +import { useState } from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import { BarChart3, Cpu, Mountain, Globe, Zap, ChevronLeft, ChevronRight } from 'lucide-react' import { useLang } from '@/context/LangContext' +import { RADAR_ITEMS, type RadarItem, type RadarCategory } from '@/content/radar' -/* ─── Global signals (critical/high/medium) ─────────────── */ -const SIGNALS = { - fa: [ - { tag: 'چین', title: 'صادرات فولاد چین به ۱۱۲ میلیون تن رسید — رکورد ۸ ساله و سقوط قیمت‌های جهانی', date: 'اسفند ۱۴۰۳', level: 'critical' }, - { tag: 'اروپا', title: 'CBAM اروپا از فروردین ۱۴۰۴ اجرایی شد — مالیات کربن بر واردات فولاد', date: 'فروردین ۱۴۰۴', level: 'critical' }, - { tag: 'هند', title: 'هند ظرفیت تولید فولاد را تا ۲۰۳۰ به ۳۰۰ میلیون تن می‌رساند', date: 'بهمن ۱۴۰۳', level: 'high' }, - { tag: 'سنگ‌آهن', title: 'قیمت سنگ‌آهن دالیان ۱۸٪ جهش کرد — کاهش ذخایر استرالیا', date: 'دی ۱۴۰۳', level: 'high' }, - { tag: 'انرژی', title: 'هزینه برق صنایع فولاد ایران ۳۲٪ افزایش یافت — بحران تابستانه شبکه', date: 'آذر ۱۴۰۳', level: 'high' }, - { tag: 'فناوری', title: 'اولین کارخانه فولاد هیدروژنی اروپا در سوئد وارد مرحله تولید تجاری شد', date: 'آبان ۱۴۰۳', level: 'medium' }, - { tag: 'روسیه', title: 'تغییر مسیر صادرات فولاد روسیه به آسیا — فشار قیمتی بر بازارهای هدف ایران', date: 'مهر ۱۴۰۳', level: 'high' }, - { tag: 'AI', title: 'مایکروسافت و گوگل رویه‌های فولاد صنعتی را با مدل‌های پیش‌بینی AI بهینه کردند', date: 'شهریور ۱۴۰۳', level: 'medium' }, - { tag: 'خاورمیانه', title: 'Vision 2030 عربستان: ۱۴۰ میلیارد دلار پروژه زیرساختی تا ۲۰۳۰ — تقاضای بالقوه ۸ میلیون تن', date: 'مرداد ۱۴۰۳', level: 'medium' }, - { tag: 'نیکل', title: 'فولاد ضدزنگ: کمبود نیکل اندونزی پس از محدودیت صادرات — رشد قیمت ۲۴٪', date: 'تیر ۱۴۰۳', level: 'high' }, - { tag: 'آمریکا', title: 'IRA آمریکا: ۳ میلیارد دلار یارانه برای فولاد کم‌کربن — رقابت‌پذیری ایران زیر فشار', date: 'خرداد ۱۴۰۳', level: 'high' }, - { tag: 'ترکیه', title: 'ترکیه بزرگ‌ترین تولیدکننده فولاد اروپاست — EAF ۷۳٪ ظرفیت', date: 'اردیبهشت ۱۴۰۳', level: 'medium' }, - { tag: 'لیتیوم', title: 'تقاضای فولاد در زنجیره خودروهای برقی: ۱۸ میلیون تن اضافی تا ۲۰۳۵', date: 'فروردین ۱۴۰۳', level: 'medium' }, - { tag: 'پاکستان', title: 'بحران ارزی پاکستان — توقف LC و اختلال در ۱.۲ میلیون تن صادرات ایران', date: 'اسفند ۱۴۰۲', level: 'critical' }, - ], - en: [ - { tag: 'China', title: "China's steel exports reach 112Mt — 8-year record and global price collapse", date: 'Mar 2025', level: 'critical' }, - { tag: 'Europe', title: 'EU CBAM takes effect Spring 2025 — carbon tax on steel imports', date: 'Apr 2025', level: 'critical' }, - { tag: 'India', title: 'India to reach 300Mt steel capacity by 2030', date: 'Feb 2025', level: 'high' }, - { tag: 'Iron Ore', title: 'Dalian iron ore price surges 18% — Australian reserves declining', date: 'Jan 2025', level: 'high' }, - { tag: 'Energy', title: "Iran steel industry electricity costs up 32% — summer grid crisis", date: 'Dec 2024', level: 'high' }, - { tag: 'Technology', title: "Europe's first hydrogen steel plant in Sweden enters commercial production", date: 'Nov 2024', level: 'medium' }, - { tag: 'Russia', title: "Russia reroutes steel exports to Asia — price pressure on Iran's target markets", date: 'Oct 2024', level: 'high' }, - { tag: 'AI', title: 'Microsoft and Google optimize industrial steel processes with AI prediction models', date: 'Sep 2024', level: 'medium' }, - { tag: 'Middle East', title: 'Saudi Vision 2030: $140B in infrastructure — 8Mt potential steel demand', date: 'Aug 2024', level: 'medium' }, - { tag: 'Nickel', title: 'Stainless steel: Indonesian nickel shortage after export ban — 24% price spike', date: 'Jul 2024', level: 'high' }, - { tag: 'USA', title: 'US IRA: $3B subsidy for low-carbon steel — Iran competitiveness under pressure', date: 'Jun 2024', level: 'high' }, - { tag: 'Turkey', title: "Turkey is Europe's largest steel producer — EAF at 73% of capacity", date: 'May 2024', level: 'medium' }, - { tag: 'Lithium', title: 'Steel demand in EV supply chain: 18Mt additional by 2035', date: 'Apr 2024', level: 'medium' }, - { tag: 'Pakistan', title: "Pakistan currency crisis — LC halt disrupts 1.2Mt of Iran's exports", date: 'Mar 2024', level: 'critical' }, - ], -} +const NAVY = '#071d49' +const GOLD = '#c9a227' +const PER_PAGE = 8 -/* ─── Weak signals / emerging trends ────────────────────── */ -const WEAK_SIGNALS = { - fa: [ - { tag: 'هوش مصنوعی', title: 'LLM‌های تخصصی برای پیش‌بینی قیمت فولاد — دقت ۸۷٪ در افق ۳۰ روزه', date: 'بهمن ۱۴۰۳', level: 'medium' }, - { tag: 'بلاک‌چین', title: 'پلتفرم‌های ردیابی زنجیره تأمین فولاد با بلاک‌چین — آزمایش در ۵ شرکت بزرگ', date: 'دی ۱۴۰۳', level: 'medium' }, - { tag: 'ژئوتکنیک', title: 'کاهش ذخایر کک‌پزی با کیفیت — بحران پنهان تأمین مواد اولیه تا ۲۰۳۵', date: 'آذر ۱۴۰۳', level: 'high' }, - { tag: 'تغییر اقلیم', title: 'تندباد‌های بیشتر در خلیج فارس — خطر بندری و هزینه حمل‌ونقل بالاتر', date: 'آبان ۱۴۰۳', level: 'medium' }, - { tag: 'ساخت افزودنی', title: 'چاپ سه‌بعدی قطعات فولادی در هوافضا — جایگزینی نورد در برخی محصولات تا ۲۰۳۵', date: 'مهر ۱۴۰۳', level: 'medium' }, - { tag: 'نانوفولاد', title: 'فولاد با ساختار نانو — مقاومت ۳ برابر بیشتر با همان وزن، تهدید برای فولاد معمول', date: 'شهریور ۱۴۰۳', level: 'medium' }, - { tag: 'جمعیت', title: 'کاهش جمعیت چین — کاهش تقاضای داخلی فولاد و افزایش فشار صادراتی تا ۲۰۴۰', date: 'مرداد ۱۴۰۳', level: 'high' }, - { tag: 'فضا', title: 'صنعت فضایی تجاری: آلیاژهای فولادی خاص با تقاضای ۵۰۰ هزار تن تا ۲۰۳۵', date: 'تیر ۱۴۰۳', level: 'medium' }, - ], - en: [ - { tag: 'AI', title: 'Specialized LLMs for steel price forecasting — 87% accuracy on 30-day horizon', date: 'Feb 2025', level: 'medium' }, - { tag: 'Blockchain', title: 'Steel supply chain tracking platforms on blockchain — pilots at 5 major companies', date: 'Jan 2025', level: 'medium' }, - { tag: 'Geotechnics', title: 'Declining quality coking coal reserves — hidden raw material crisis by 2035', date: 'Dec 2024', level: 'high' }, - { tag: 'Climate', title: 'More frequent Gulf storms — port risk and higher shipping costs', date: 'Nov 2024', level: 'medium' }, - { tag: 'Additive Mfg', title: '3D printing of steel components in aerospace — replacing rolling for some products by 2035', date: 'Oct 2024', level: 'medium' }, - { tag: 'Nanosteel', title: 'Nano-structured steel — 3× strength at same weight, threatening conventional steel', date: 'Sep 2024', level: 'medium' }, - { tag: 'Demographics', title: "China's declining population — falling domestic steel demand and rising export pressure by 2040", date: 'Aug 2024', level: 'high' }, - { tag: 'Space', title: 'Commercial space industry: specialty steel alloys with 500kt demand by 2035', date: 'Jul 2024', level: 'medium' }, - ], -} - -/* ─── Foresight horizons ─────────────────────────────────── */ -const HORIZONS = { - fa: [ - { num: '۱۴۰۵', tag: 'کوتاه‌مدت', title: 'رکود صادراتی ناشی از سیل فولاد چین — فشار قیمتی ۱۵–۲۰٪', summary: 'با احتمال ۷۵٪: قیمت‌های جهانی فولاد در محدوده ۴۵۰–۵۰۰ دلار باقی می‌مانند. ایران باید بر کاهش هزینه تولید و تمایز کیفی تمرکز کند.' }, - { num: '۱۴۰۶', tag: 'میان‌مدت', title: 'اثر کامل CBAM — بازتوزیع جریان‌های تجاری فولاد جهانی', summary: 'صادرکنندگان بدون برنامه کربنی از بازار اروپا خارج می‌شوند. فرصت برای ایران: تأمین بازار جنوب جهانی که از اروپا منحرف شده‌اند.' }, - { num: '۱۴۰۷', tag: 'میان‌مدت', title: 'هند به دومین مصرف‌کننده فولاد جهان می‌رسد — فرصت بازار ۱۵ میلیون تن', summary: 'رشد زیرساختی هند تقاضای ۸۵ میلیون تن فولاد اضافی ایجاد می‌کند. ایران می‌تواند ۱۵–۲۰ میلیون تن از این بازار را جذب کند.' }, - { num: '۱۴۱۰', tag: 'بلندمدت', title: 'فولاد سبز به معیار پذیرش در بازارهای پریمیوم تبدیل می‌شود', summary: 'صادرکنندگانی که تا ۱۴۱۰ برنامه کربنی ندارند از ۶۰٪ بازارهای جهانی محروم می‌شوند. نقطه بازگشت‌ناپذیر برای ایران: ۱۴۰۷.' }, - { num: '۱۴۱۵', tag: 'بلندمدت', title: 'هیدروژن سبز ارزان‌تر از گاز طبیعی — تحول کامل DRI', summary: 'با احتمال ۵۵٪: هزینه هیدروژن سبز تا ۱۴۱۵ به زیر ۲ دلار/کیلوگرم می‌رسد. ایران با منابع خورشیدی می‌تواند مزیت رقابتی بزرگ در DRI-H₂ کسب کند.' }, - { num: '۱۴۲۰', tag: 'افق دور', title: 'ظرفیت جهانی فولاد ۲۵٪ مازاد — موج ادغام و یکپارچه‌سازی', summary: 'کارخانه‌های غیررقابتی در آسیا و خاورمیانه بسته می‌شوند. برندگان: تولیدکنندگان با هزینه پایین، برنامه سبز و دسترسی به بازارهای رو به رشد.' }, - ], - en: [ - { num: '2026', tag: 'Short-term', title: 'Export recession from Chinese steel flood — 15–20% price pressure', summary: '75% probability: global steel prices stay in $450–500 range. Iran must focus on production cost reduction and quality differentiation.' }, - { num: '2027', tag: 'Medium-term', title: 'Full CBAM effect — redistribution of global steel trade flows', summary: 'Exporters without carbon plans are pushed out of Europe. Opportunity for Iran: supply the Global South markets diverted from Europe.' }, - { num: '2028', tag: 'Medium-term', title: 'India becomes world\'s 2nd steel consumer — 15Mt market opportunity', summary: "India's infrastructure growth creates 85Mt of additional steel demand. Iran can capture 15–20Mt of this market." }, - { num: '2031', tag: 'Long-term', title: 'Green steel becomes the admission standard for premium markets', summary: 'Exporters without a carbon roadmap by 2031 are locked out of 60% of global markets. Point of no return for Iran: 2028.' }, - { num: '2036', tag: 'Long-term', title: 'Green hydrogen cheaper than natural gas — full DRI transformation', summary: '55% probability: green hydrogen cost falls below $2/kg by 2036. Iran\'s solar resources could give it a major competitive edge in DRI-H₂.' }, - { num: '2041', tag: 'Far horizon', title: '25% global steel overcapacity — wave of mergers and consolidation', summary: 'Non-competitive plants in Asia and the Middle East close. Winners: low-cost producers with green plans and access to growing markets.' }, - ], -} - -/* ─── KPIs ──────────────────────────────────────────────── */ -const KPIS = { - fa: [ - { value: '۱۱۲', unit: 'میلیون تن', label: 'صادرات فولاد چین ۱۴۰۳', delta: 'رکورد ۸ ساله' }, - { value: '۴۸۵', unit: 'دلار/تن', label: 'قیمت جهانی HRC فعلی', delta: '-۱۲٪ از اوج ۱۴۰۳' }, - { value: '۱۸۸۵', unit: 'میلیون تن', label: 'تولید جهانی فولاد ۱۴۰۲', delta: '+۱.۸٪ رشد سالانه' }, - { value: '۵۵٪', unit: '', label: 'سهم چین از تولید جهانی', delta: 'پایدار' }, - { value: '۸.۷', unit: 'میلیون تن', label: 'صادرات فولاد ایران ۱۴۰۳', delta: '+۱.۸٪ رشد' }, - ], - en: [ - { value: '112', unit: 'Mt', label: 'China steel exports 1403', delta: '8-year record' }, - { value: '$485', unit: '/t', label: 'Current global HRC price', delta: '-12% from 1403 peak' }, - { value: '1885', unit: 'Mt', label: 'Global steel production 2023', delta: '+1.8% annual growth' }, - { value: '55%', unit: '', label: "China's share of global output", delta: 'Stable' }, - { value: '8.7', unit: 'Mt', label: 'Iran steel exports 1403', delta: '+1.8% growth' }, - ], -} - -const LEVEL_COLOR: Record = { - critical: '#7f1d1d', - high: 'var(--red)', - medium: '#b45309', -} - -const LEVEL_LABEL = { - fa: { critical: 'بحرانی', high: 'بالا', medium: 'متوسط' }, - en: { critical: 'Critical', high: 'High', medium: 'Medium' }, +const CATEGORY_ICON: Record = { + market: BarChart3, + tech: Cpu, + commodity: Mountain, + geo: Globe, + energy: Zap, } const T = { - fa: { - overline: 'FORESIGHT & INTELLIGENCE', - heading: 'آینده‌پژوهی و رصد هوشمند', - sub: 'رصد سیگنال‌های قوی و ضعیف جهانی، تحلیل روندهای نوظهور و سناریوپردازی افق‌های آینده صنعت فولاد', - kpiHeading: 'شاخص‌های کلیدی بازار جهانی فولاد', - sigHeading: 'سیگنال‌های قوی — رویدادهای جاری', - weakHeading: 'سیگنال‌های ضعیف — روندهای نوظهور', - horizHeading: 'افق‌های آینده‌پژوهی — سناریوهای استراتژیک', - }, - en: { - overline: 'FORESIGHT & INTELLIGENCE', - heading: 'Foresight & Intelligence', - sub: 'Monitoring strong and weak signals, emerging trend analysis, and scenario planning for the steel industry', - kpiHeading: 'Key Global Steel Market Indicators', - sigHeading: 'Strong Signals — Current Events', - weakHeading: 'Weak Signals — Emerging Trends', - horizHeading: 'Foresight Horizons — Strategic Scenarios', - }, + fa: { overline: 'FORESIGHT & INTELLIGENCE', heading: 'رادار آینده', sub: 'رصد سیگنال‌های راهبردی و روندهای نوظهور صنعت فولاد جهان', readMore: 'ادامه مطلب' }, + en: { overline: 'FORESIGHT & INTELLIGENCE', heading: 'Future Radar', sub: 'Monitoring strategic signals and emerging trends in the global steel industry', readMore: 'Read more' }, +} + +/* ─── one card: gold bar (default) → navy author panel (hover) ─── */ +export function RadarCard({ item, lang }: { item: RadarItem; lang: 'fa' | 'en' }) { + const [hover, setHover] = useState(false) + const t = item[lang] + const Icon = CATEGORY_ICON[item.category] + + return ( + setHover(true)} + onMouseLeave={() => setHover(false)} + style={{ + position: 'relative', borderRadius: 16, overflow: 'hidden', cursor: 'pointer', + aspectRatio: '3 / 3.5', background: NAVY, + boxShadow: hover ? '0 30px 70px -30px rgba(7,29,73,0.45)' : '0 16px 40px -24px rgba(7,29,73,0.30)', + }} + animate={{ y: hover ? -4 : 0 }} + transition={{ duration: 0.35, ease: [0.22, 1, 0.36, 1] }} + > + {/* background image */} + + {/* hover dim so the navy panel reads */} + + + {/* ── DEFAULT: gold bar at the bottom ── */} + + {!hover && ( + + {/* icon on the leading (right in RTL) side of the bar */} +
+ +
+
+
+ {t.title} +
+
{t.date}
+
+
+ )} +
+ + {/* ── HOVER: navy author panel slides up ── */} + + {hover && ( + +
+ {/* author avatar replaces the category icon */} + +
+
{t.title}
+
{item.author.name} · {t.date}
+
+
+

+ {t.excerpt} +

+
+ )} +
+
+ ) +} + +/* ─── pagination ─── */ +function Pagination({ page, pages, onGo }: { page: number; pages: number; onGo: (p: number) => void }) { + if (pages <= 1) return null + // show: first, current-ish, last, with ellipsis + const nums: (number | '…')[] = [] + for (let i = 1; i <= pages; i++) { + if (i === 1 || i === pages || Math.abs(i - page) <= 1) nums.push(i) + else if (nums[nums.length - 1] !== '…') nums.push('…') + } + + const Btn = ({ children, active, onClick, disabled }: { children: React.ReactNode; active?: boolean; onClick?: () => void; disabled?: boolean }) => ( + + ) + + return ( +
+ onGo(page - 1)} disabled={page === 1}> + {nums.map((n, i) => + n === '…' + ? + : onGo(n)}>{n} + )} + onGo(page + 1)} disabled={page === pages}> +
+ ) } export default function Radar() { const { lang } = useLang() const t = T[lang] - const signals = SIGNALS[lang] - const weakSignals = WEAK_SIGNALS[lang] - const horizons = HORIZONS[lang] - const kpis = KPIS[lang] - const levelLabel = LEVEL_LABEL[lang] + const [page, setPage] = useState(1) + + const pages = Math.ceil(RADAR_ITEMS.length / PER_PAGE) + const start = (page - 1) * PER_PAGE + const visible = RADAR_ITEMS.slice(start, start + PER_PAGE) + + const go = (p: number) => { + if (p < 1 || p > pages) return + setPage(p) + window.scrollTo({ top: 0, behavior: 'smooth' }) + } return ( -
+
+
- {/* ── Hero ── */} -
-
-
-
- {t.overline} -
-

{t.heading}

-

{t.sub}

-
-
- - {/* ── KPI Strip ── */} -
-
-
{t.kpiHeading}
-
- {kpis.map((k, i) => ( - -
- {k.value}{k.unit} -
-
{k.label}
-
{k.delta}
-
- ))} + {/* heading with gold rule on the leading side */} +
+
+
{t.overline}
+

{t.heading}

+

{t.sub}

-
- {/* ── Strong Signals ── */} -
-
-
- {t.sigHeading} -
-
- {signals.map((s, i) => ( - -
-
- {s.tag} - {levelLabel[s.level as keyof typeof levelLabel]} -
-

{s.title}

-
{s.date}
- + {/* card grid */} +
+ {visible.map((item) => ( + ))}
-
- {/* ── Weak Signals ── */} -
-
-
- {t.weakHeading} -
-
- {weakSignals.map((s, i) => ( - -
-
- {s.tag} - EMERGING -
-

{s.title}

-
{s.date}
- - ))} -
+
- - {/* ── Foresight Horizons ── */} -
-
-
- {t.horizHeading} -
-
- {horizons.map((h, i) => ( - -
-
{h.num}
-
{h.tag}
-

{h.title}

-

{h.summary}

- - ))} -
-
-
) } diff --git a/vite.config.ts b/vite.config.ts index b608676..585c8d8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,6 +3,9 @@ import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' import path from 'path' +// hosts allowed through Vite's host check (for tunnels: tunnelmole, localtunnel, cloudflare) +const ALLOWED_TUNNEL_HOSTS = ['.tunnelmole.net', '.loca.lt', '.trycloudflare.com'] + export default defineConfig({ plugins: [react(), tailwindcss()], resolve: { @@ -10,4 +13,10 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, + server: { + allowedHosts: ALLOWED_TUNNEL_HOSTS, + }, + preview: { + allowedHosts: ALLOWED_TUNNEL_HOSTS, + }, })