Risk slider, footer fix, global padding, mobile improvements
|
|
@ -0,0 +1,81 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"ECC_DISABLED_HOOKS": "pre:edit-write:gateguard-fact-force"
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(docker --version)",
|
||||||
|
"Bash(docker buildx *)",
|
||||||
|
"Bash(npm -v)",
|
||||||
|
"Bash(npm ci *)",
|
||||||
|
"Bash(git commit -m 'Add Docker deployment for Hugging Face Spaces *)",
|
||||||
|
"Bash(npm start *)",
|
||||||
|
"Bash(git reset *)",
|
||||||
|
"Bash(git commit -m 'Add admin panel + wire main site to fetch articles from panel API *)",
|
||||||
|
"WebFetch(domain:skillooodex-steel.hf.space)",
|
||||||
|
"Bash(git fetch *)",
|
||||||
|
"Bash(git merge *)",
|
||||||
|
"Bash(powershell -NoProfile -Command 'Get-NetTCPConnection -LocalPort 3001 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }; Write-Host done')",
|
||||||
|
"Bash(curl -s http://localhost:3001/api/prices)",
|
||||||
|
"Bash(powershell -NoProfile -Command 'Get-NetTCPConnection -LocalPort 3001 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }; Start-Sleep -Milliseconds 800; Write-Host done')",
|
||||||
|
"Bash(node -e ' *)",
|
||||||
|
"Bash(powershell -NoProfile -Command 'Get-NetTCPConnection -LocalPort 3001 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }; Write-Host stopped')",
|
||||||
|
"Bash(curl -sI -H \"User-Agent: Mozilla/5.0\" https://www.tgju.org/gold-global)",
|
||||||
|
"Bash(curl -sI https://www.google.com)",
|
||||||
|
"Bash(node -e \"require\\('@tabler/icons-react'\\)\")",
|
||||||
|
"Bash(git commit -m ' *)",
|
||||||
|
"Bash(ffmpeg -version)",
|
||||||
|
"PowerShell(Get-Command ffmpeg -ErrorAction SilentlyContinue)",
|
||||||
|
"PowerShell(ffmpeg -version 2>&1)",
|
||||||
|
"PowerShell(winget install --id Gyan.FFmpeg -e --accept-source-agreements --accept-package-agreements 2>&1)",
|
||||||
|
"PowerShell($env:Path = [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"Machine\"\\) + \";\" + [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"User\"\\); ffmpeg -i \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\ezgif-1650150a406efd73.mp4\" -vf \"scale=1280:-2\" -c:v libx264 -crf 28 -preset slow -an -movflags +faststart \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg.mp4\" -y 2>&1 | Select-Object -Last 5)",
|
||||||
|
"PowerShell(\\(Get-Item \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg.mp4\"\\).Length / 1MB)",
|
||||||
|
"Bash(git lfs *)",
|
||||||
|
"PowerShell(pip install \"huggingface_hub[hf_xet]\" -q 2>&1)",
|
||||||
|
"Bash(git rm *)",
|
||||||
|
"Bash(echo \"public/hero-bg.mp4\")",
|
||||||
|
"Bash(git filter-branch *)",
|
||||||
|
"Bash(npm list *)",
|
||||||
|
"Bash(git pull *)",
|
||||||
|
"Bash(git rebase *)",
|
||||||
|
"Bash(grep -Hm1 ^description: 'c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad/.claude/skills/*/SKILL.md')",
|
||||||
|
"Bash(echo \"done\")",
|
||||||
|
"PowerShell($env:Path = [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"Machine\"\\) + \";\" + [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"User\"\\); ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg.mp4\")",
|
||||||
|
"PowerShell($env:Path = [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"Machine\"\\) + \";\" + [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"User\"\\); ffmpeg -i \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg.mp4\" -t 74.56 -c copy \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg-trimmed.mp4\" -y 2>&1 | Select-Object -Last 3)",
|
||||||
|
"PowerShell(Rename-Item \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg-trimmed.mp4\" \"hero-bg.mp4\")",
|
||||||
|
"PowerShell(python -c \"from huggingface_hub import HfApi; print\\('ok'\\)\" 2>&1)",
|
||||||
|
"PowerShell($env:Path = [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"Machine\"\\) + \";\" + [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"User\"\\); ffmpeg -i \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg.mp4\" -vf \"boxblur=4:1\" -c:a copy \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg-blur.mp4\" -y 2>&1 | Select-Object -Last 3)",
|
||||||
|
"PowerShell(Rename-Item \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg-blur.mp4\" \"hero-bg.mp4\")",
|
||||||
|
"PowerShell($env:Path = [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"Machine\"\\) + \";\" + [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"User\"\\); ffmpeg -i \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\ezgif-1650150a406efd73.mp4\" -t 74.56 -vf \"scale=1280:-2\" -c:v libx264 -crf 28 -preset slow -an -movflags +faststart \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg.mp4\" -y 2>&1 | Select-Object -Last 3)",
|
||||||
|
"Bash(python -c \"import pdfplumber; pdf = pdfplumber.open\\(r'c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\گزارش کامل برند بوک اندیشکده \\(1\\).pdf'\\); print\\(f'Pages: {len\\(pdf.pages\\)}'\\); [print\\(f'--- Page {i+1} ---\\\\n{p.extract_text\\(\\)}'\\) for i, p in enumerate\\(pdf.pages[:5]\\)]\")",
|
||||||
|
"PowerShell(pip install pdfplumber -q 2>&1)",
|
||||||
|
"PowerShell(pip install pymupdf -q 2>&1)",
|
||||||
|
"PowerShell(1..13 | ForEach-Object { Copy-Item \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\$_.jpg\" \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\$_.jpg\" }; Write-Host \"Done\")",
|
||||||
|
"Bash(git stash *)",
|
||||||
|
"Bash(git checkout *)",
|
||||||
|
"Bash(git commit *)",
|
||||||
|
"Bash(GIT_LFS_SKIP_SMUDGE=1 git fetch space)",
|
||||||
|
"Bash(sed -i 's/const FACTORIES = {/const _FACTORIES = {/' src/pages/Home/sections/SteelNewsletterSection.tsx)",
|
||||||
|
"Bash(sed -i 's/function ImageSlider\\({ lang }/function ImageSlider\\({ lang: _lang }/' src/pages/Home/sections/../../../pages/Home/sections/../../..\\\\/pages\\\\/Pulse\\\\/Pulse.tsx)",
|
||||||
|
"Bash(python3 -)",
|
||||||
|
"Bash(GIT_LFS_SKIP_SMUDGE=1 git rebase space/main)",
|
||||||
|
"Bash(git config *)",
|
||||||
|
"Bash(pip show *)",
|
||||||
|
"Bash(python -c ' *)",
|
||||||
|
"PowerShell($env:Path = [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"Machine\"\\) + \";\" + [System.Environment]::GetEnvironmentVariable\\(\"Path\",\"User\"\\); $dur = & ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg.mp4\" 2>&1; $newDur = [math]::Round\\([double]$dur - 2, 2\\); Write-Host \"Current: $dur New: $newDur\"; ffmpeg -i \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg.mp4\" -t $newDur -c copy \"c:\\\\Users\\\\DATIS STAR\\\\andishkade-foolad\\\\public\\\\hero-bg-new.mp4\" -y 2>&1 | Select-Object -Last 2)",
|
||||||
|
"Skill(update-config)",
|
||||||
|
"Skill(update-config:*)",
|
||||||
|
"Bash(npx prisma *)",
|
||||||
|
"PowerShell(Get-NetTCPConnection -LocalPort 3001 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }; Write-Host \"stopped\")",
|
||||||
|
"Bash(python3 \"C:\\\\Users\\\\DATIS STAR\\\\.claude\\\\plugins\\\\cache\\\\ui-ux-pro-max-skill\\\\ui-ux-pro-max\\\\2.5.0\\\\.claude\\\\skills\\\\ui-ux-pro-max\\\\scripts\\\\search.py\" \"steel industry admin dashboard CMS analytics RTL Persian\" --design-system -p \"Foolad Panel\")",
|
||||||
|
"Bash(python \"C:\\\\Users\\\\DATIS STAR\\\\.claude\\\\plugins\\\\cache\\\\ui-ux-pro-max-skill\\\\ui-ux-pro-max\\\\2.5.0\\\\.claude\\\\skills\\\\ui-ux-pro-max\\\\scripts\\\\search.py\" \"steel industry admin dashboard CMS analytics RTL Persian\" --design-system -p \"Foolad Panel\")",
|
||||||
|
"Read(//c/Users/DATIS STAR/.claude/plugins/cache/ui-ux-pro-max-skill/**)",
|
||||||
|
"Bash(python assets/scripts/search.py \"steel industry admin dashboard CMS analytics RTL\" --design-system -p \"Foolad Panel\")",
|
||||||
|
"Bash(Get-ChildItem -Path \"c:\\\\Users\\\\DATIS STAR\\\\پنل\\\\src\\\\app\\\\api\\\\\" -Recurse -File)",
|
||||||
|
"Read(//c/Users/DATIS STAR/**)"
|
||||||
|
],
|
||||||
|
"additionalDirectories": [
|
||||||
|
"c:\\Users\\DATIS STAR\\پنل"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1018 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 950 KiB |
|
After Width: | Height: | Size: 883 KiB |
|
After Width: | Height: | Size: 948 KiB |
|
After Width: | Height: | Size: 954 KiB |
|
After Width: | Height: | Size: 936 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 968 KiB |
|
After Width: | Height: | Size: 972 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 984 KiB |
|
After Width: | Height: | Size: 881 KiB |
|
After Width: | Height: | Size: 990 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 968 KiB |
|
After Width: | Height: | Size: 971 KiB |
|
After Width: | Height: | Size: 945 KiB |
|
After Width: | Height: | Size: 925 KiB |
|
After Width: | Height: | Size: 960 KiB |
|
After Width: | Height: | Size: 977 KiB |
|
After Width: | Height: | Size: 948 KiB |
|
After Width: | Height: | Size: 935 KiB |
|
After Width: | Height: | Size: 909 KiB |
|
After Width: | Height: | Size: 977 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1004 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:400daccc315388c24c972cb62de1902d922bcabb8062c353283a0323f468209d
|
||||||
|
size 37935185
|
||||||
|
|
@ -80,18 +80,18 @@ function NewsletterBox() {
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer dir="rtl" style={{ backgroundColor: 'var(--ink)', color: 'rgba(255,255,255,0.6)' }}>
|
<footer dir="rtl" style={{ backgroundColor: 'var(--ink)', color: 'rgba(255,255,255,0.6)' }}>
|
||||||
<div style={{ padding: '56px 0 48px' }} className="max-w-7xl mx-auto w-full px-12 max-md:px-5">
|
<div style={{ padding: '56px 0 48px' }} className="max-w-7xl mx-auto w-full px-6 max-md:px-5">
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', flexWrap: 'wrap', gap: 40 }}>
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 40, alignItems: 'flex-start' }} className="max-md:grid-cols-1 max-md:gap-10">
|
||||||
|
|
||||||
{/* Brand — no description paragraph */}
|
{/* Brand */}
|
||||||
<div style={{ maxWidth: 300 }}>
|
<div style={{ minWidth: 0 }}>
|
||||||
<div style={{ fontSize: 22, fontWeight: 900, color: '#fff', lineHeight: 1.2, marginBottom: 4 }}>
|
<div style={{ fontSize: 22, fontWeight: 900, color: '#fff', lineHeight: 1.2, marginBottom: 4 }}>
|
||||||
اندیشکده فولاد آینده
|
اندیشکده فولاد آینده
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 10, letterSpacing: 3, color: 'rgba(255,255,255,0.3)', textTransform: 'uppercase', marginBottom: 20, direction: 'ltr', textAlign: 'right' }}>
|
<div style={{ fontSize: 10, letterSpacing: 3, color: 'rgba(255,255,255,0.3)', textTransform: 'uppercase', marginBottom: 20, direction: 'ltr', textAlign: 'right', wordBreak: 'break-all' }}>
|
||||||
FUTURE STEEL POLICY INSTITUTE
|
FUTURE STEEL POLICY INSTITUTE
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', gap: 6 }}>
|
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
||||||
<SocialIcon label="in" />
|
<SocialIcon label="in" />
|
||||||
<SocialIcon label="tg" />
|
<SocialIcon label="tg" />
|
||||||
<SocialIcon label="tw" />
|
<SocialIcon label="tw" />
|
||||||
|
|
@ -99,8 +99,8 @@ export function Footer() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Links + newsletter under تماس با ما */}
|
{/* Links + newsletter */}
|
||||||
<nav aria-label="پیوندهای پایین" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
<nav aria-label="پیوندهای پایین" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
{FOOTER_LINKS.map(link => (
|
{FOOTER_LINKS.map(link => (
|
||||||
<FooterLink key={link.label} href={link.href} label={link.label} />
|
<FooterLink key={link.label} href={link.href} label={link.label} />
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -260,9 +260,31 @@ body {
|
||||||
.animate-scroll-risk {
|
.animate-scroll-risk {
|
||||||
animation: scroll 72s forwards linear infinite;
|
animation: scroll 72s forwards linear infinite;
|
||||||
}
|
}
|
||||||
|
/* ── Global breathing room ───────────────────────────── */
|
||||||
|
/* Cards, list items, table cells — give them air */
|
||||||
|
.card, [class*="card"] { padding: 1.5rem !important; }
|
||||||
|
|
||||||
|
/* Mobile: section inner containers get more horizontal space */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
section > div,
|
||||||
|
section > .max-w-7xl {
|
||||||
|
padding-left: 1.25rem !important;
|
||||||
|
padding-right: 1.25rem !important;
|
||||||
|
}
|
||||||
|
/* Extra vertical gap between stacked items */
|
||||||
|
[class*="grid-cols-1"] > * + * {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes riskCardIn {
|
||||||
|
from { opacity: 0; transform: translateY(16px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.animate-scroll { animation: none; }
|
.animate-scroll { animation: none; }
|
||||||
.animate-scroll-risk { animation: none; }
|
.animate-scroll-risk { animation: none; }
|
||||||
|
[style*="riskCardIn"] { animation: none !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ export default function FactoryNewsletterLayout() {
|
||||||
return (
|
return (
|
||||||
<section dir={lang === 'fa' ? 'rtl' : 'ltr'} style={{ background: 'var(--paper-2)' }}>
|
<section dir={lang === 'fa' ? 'rtl' : 'ltr'} style={{ background: 'var(--paper-2)' }}>
|
||||||
<div className="max-w-7xl mx-auto px-12 max-md:px-5" style={{ paddingTop: 56, paddingBottom: 64 }}>
|
<div className="max-w-7xl mx-auto px-12 max-md:px-5" style={{ paddingTop: 56, paddingBottom: 64 }}>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '60% 1fr', gap: 40 }} className="max-md:grid-cols-1">
|
<div className="grid grid-cols-[60%_1fr] max-md:grid-cols-1 gap-10">
|
||||||
|
|
||||||
{/* ══ LEFT · ماهنامه ══ */}
|
{/* ══ LEFT · ماهنامه ══ */}
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -110,7 +110,7 @@ export default function FactoryNewsletterLayout() {
|
||||||
<a href="#" style={{ fontSize: 12, fontWeight: 700, color: 'var(--red)', textDecoration: 'none', whiteSpace: 'nowrap' }}>{t.factoryAll}</a>
|
<a href="#" style={{ fontSize: 12, fontWeight: 700, color: 'var(--red)', textDecoration: 'none', whiteSpace: 'nowrap' }}>{t.factoryAll}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }} className="max-sm:grid-cols-1">
|
<div className="grid grid-cols-2 max-md:grid-cols-1 gap-3">
|
||||||
{REPORTS[lang].map((r, i) => (
|
{REPORTS[lang].map((r, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState, useRef, useCallback } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { useLang } from '@/context/LangContext'
|
import { useLang } from '@/context/LangContext'
|
||||||
|
|
||||||
|
|
@ -180,25 +180,46 @@ export default function RiskSection() {
|
||||||
return () => { cancelled = true }
|
return () => { cancelled = true }
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/* Duplicate items for seamless infinite loop (handled in JSX, not effect, to
|
const VISIBLE = 3 // cards visible on desktop
|
||||||
stay immune to StrictMode double-invocation that would otherwise clone twice). */
|
const INTERVAL = 4000
|
||||||
const loopItems = [...riskItems, ...riskItems, ...riskItems]
|
const total = riskItems.length
|
||||||
|
|
||||||
|
const [current, setCurrent] = useState(0)
|
||||||
|
const [paused, setPaused] = useState(false)
|
||||||
|
const [animDir, setAnimDir] = useState<'next' | 'prev'>('next')
|
||||||
|
const [isAnim, setIsAnim] = useState(false)
|
||||||
|
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||||
|
|
||||||
|
// touch support
|
||||||
|
const touchStartX = useRef(0)
|
||||||
|
|
||||||
|
const go = useCallback((dir: 'next' | 'prev') => {
|
||||||
|
if (isAnim) return
|
||||||
|
setAnimDir(dir)
|
||||||
|
setIsAnim(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
setCurrent(c => dir === 'next' ? (c + 1) % total : (c - 1 + total) % total)
|
||||||
|
setIsAnim(false)
|
||||||
|
}, 320)
|
||||||
|
}, [isAnim, total])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (paused) return
|
||||||
|
timerRef.current = setInterval(() => go('next'), INTERVAL)
|
||||||
|
return () => { if (timerRef.current) clearInterval(timerRef.current) }
|
||||||
|
}, [paused, go])
|
||||||
|
|
||||||
|
const visibleItems = Array.from({ length: VISIBLE }, (_, i) =>
|
||||||
|
riskItems[(current + i) % total]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section style={{ background: 'var(--paper)' }}>
|
<section style={{ background: 'var(--paper)' }}>
|
||||||
{/* ── Editorial header ── */}
|
{/* ── Header ── */}
|
||||||
<div className="max-w-7xl mx-auto px-12 pt-24 max-md:px-5 max-md:pt-16">
|
<div className="max-w-7xl mx-auto px-12 pt-24 max-md:px-5 max-md:pt-16">
|
||||||
<div
|
<div
|
||||||
className="mq-stack max-md:grid-cols-1 max-md:gap-4"
|
className="mq-stack max-md:grid-cols-1 max-md:gap-4"
|
||||||
style={{
|
style={{ display: 'grid', gridTemplateColumns: '1fr auto', alignItems: 'end', gap: 32, paddingBottom: 28, borderBottom: '1px solid var(--rule-thin)', marginBottom: 28 }}
|
||||||
display: 'grid',
|
|
||||||
gridTemplateColumns: '1fr auto',
|
|
||||||
alignItems: 'end',
|
|
||||||
gap: 32,
|
|
||||||
paddingBottom: 28,
|
|
||||||
borderBottom: '1px solid var(--rule-thin)',
|
|
||||||
marginBottom: 28,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 18 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 18 }}>
|
||||||
|
|
@ -210,134 +231,127 @@ export default function RiskSection() {
|
||||||
<h2 style={{ fontSize: 'clamp(22px, 2.5vw, 32px)', fontWeight: 900, letterSpacing: '-0.5px', color: 'var(--ink)', lineHeight: 1.2, marginBottom: 12 }}>
|
<h2 style={{ fontSize: 'clamp(22px, 2.5vw, 32px)', fontWeight: 900, letterSpacing: '-0.5px', color: 'var(--ink)', lineHeight: 1.2, marginBottom: 12 }}>
|
||||||
{t.heading}
|
{t.heading}
|
||||||
</h2>
|
</h2>
|
||||||
<p style={{ fontSize: 14, lineHeight: 1.7, color: 'var(--ink-4)', maxWidth: 580 }}>
|
<p style={{ fontSize: 14, lineHeight: 1.7, color: 'var(--ink-4)', maxWidth: 580 }}>{t.sub}</p>
|
||||||
{t.sub}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<Link to="/risks" style={{ fontSize: 11, fontWeight: 700, letterSpacing: '1.5px', textTransform: 'uppercase', color: 'var(--red)', textDecoration: 'none', whiteSpace: 'nowrap', paddingBottom: 4 }}>
|
<Link to="/risks" style={{ fontSize: 11, fontWeight: 700, letterSpacing: '1.5px', textTransform: 'uppercase', color: 'var(--red)', textDecoration: 'none', whiteSpace: 'nowrap', paddingBottom: 4 }}>
|
||||||
{t.all}
|
{t.all}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Legend row */}
|
{/* Legend */}
|
||||||
<div style={{ display: 'flex', gap: 22, flexWrap: 'wrap', marginBottom: 8 }}>
|
<div style={{ display: 'flex', gap: 22, flexWrap: 'wrap', marginBottom: 8 }}>
|
||||||
{(['critical','high','medium','low','opportunity'] as const).map((key, i) => ({
|
{(['critical','high','medium','low','opportunity'] as const).map((key, i) => (
|
||||||
color: levelCfg[key].dot, label: t.legend[i],
|
<div key={key} style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
|
||||||
})).map(l => (
|
<div style={{ width: 8, height: 8, borderRadius: '50%', background: levelCfg[key].dot }} />
|
||||||
<div key={l.label} style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
|
<span style={{ fontSize: 12, color: 'var(--ink-4)', fontWeight: 600 }}>{t.legend[i]}</span>
|
||||||
<div style={{ width: 8, height: 8, borderRadius: '50%', background: l.color }} />
|
|
||||||
<span style={{ fontSize: 12, color: 'var(--ink-4)', fontWeight: 600 }}>{l.label}</span>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Auto-scroll ticker ── */}
|
{/* ── Slider ── */}
|
||||||
<div
|
<div
|
||||||
style={{
|
className="max-w-7xl mx-auto px-12 max-md:px-5"
|
||||||
position: 'relative',
|
style={{ padding: '28px 48px 48px', position: 'relative' }}
|
||||||
overflow: 'hidden',
|
onMouseEnter={() => setPaused(true)}
|
||||||
padding: '28px 0 40px',
|
onMouseLeave={() => setPaused(false)}
|
||||||
/* Fade masks on both edges */
|
onTouchStart={e => { touchStartX.current = e.touches[0].clientX }}
|
||||||
WebkitMaskImage:
|
onTouchEnd={e => {
|
||||||
'linear-gradient(to right, transparent 0%, black 7%, black 93%, transparent 100%)',
|
const dx = touchStartX.current - e.changedTouches[0].clientX
|
||||||
maskImage:
|
if (Math.abs(dx) > 50) go(dx > 0 ? 'next' : 'prev')
|
||||||
'linear-gradient(to right, transparent 0%, black 7%, black 93%, transparent 100%)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ul
|
{/* Cards row */}
|
||||||
className="animate-scroll-risk"
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'grid',
|
||||||
gap: 20,
|
gridTemplateColumns: 'repeat(3, 1fr)',
|
||||||
width: 'max-content',
|
gap: 16,
|
||||||
flexWrap: 'nowrap',
|
overflow: 'hidden',
|
||||||
listStyle: 'none',
|
|
||||||
direction: 'ltr',
|
|
||||||
/* pause on hover */
|
|
||||||
animationPlayState: 'running',
|
|
||||||
}}
|
}}
|
||||||
onMouseEnter={e => (e.currentTarget.style.animationPlayState = 'paused')}
|
className="max-md:grid-cols-1"
|
||||||
onMouseLeave={e => (e.currentTarget.style.animationPlayState = 'running')}
|
|
||||||
>
|
>
|
||||||
{loopItems.map((item, idx) => {
|
{visibleItems.map((item, idx) => {
|
||||||
const cfg = levelCfg[item.level]
|
const cfg = levelCfg[item.level]
|
||||||
return (
|
return (
|
||||||
<li
|
<div
|
||||||
key={idx}
|
key={`${current}-${idx}`}
|
||||||
|
className={idx > 0 ? 'max-md:hidden' : ''}
|
||||||
style={{
|
style={{
|
||||||
width: 340,
|
background: 'var(--paper)',
|
||||||
flexShrink: 0,
|
border: '1px solid var(--rule-thin)',
|
||||||
background: 'var(--paper)',
|
borderTop: `3px solid ${cfg.dot}`,
|
||||||
border: '1px solid var(--rule-thin)',
|
padding: '24px 24px 20px',
|
||||||
borderTop: `3px solid ${cfg.dot}`,
|
direction: 'rtl',
|
||||||
padding: '24px 24px 20px',
|
display: 'flex',
|
||||||
direction: 'rtl',
|
|
||||||
fontFamily: 'Vazir, sans-serif',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
animation: `riskCardIn 320ms ease both`,
|
||||||
|
animationDelay: `${idx * 60}ms`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Level badge */}
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 12 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 12 }}>
|
||||||
<div style={{
|
<div style={{ width: 7, height: 7, borderRadius: '50%', background: cfg.dot, flexShrink: 0 }} />
|
||||||
width: 7,
|
<span style={{ fontSize: 9, fontWeight: 700, letterSpacing: '2px', textTransform: 'uppercase', color: cfg.dot }}>
|
||||||
height: 7,
|
|
||||||
borderRadius: '50%',
|
|
||||||
background: cfg.dot,
|
|
||||||
flexShrink: 0,
|
|
||||||
}} />
|
|
||||||
<span style={{
|
|
||||||
fontSize: 9,
|
|
||||||
fontWeight: 700,
|
|
||||||
letterSpacing: '2px',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
color: cfg.dot,
|
|
||||||
}}>
|
|
||||||
{cfg.label}
|
{cfg.label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quote */}
|
<p style={{ fontSize: 13, lineHeight: 1.85, color: 'var(--ink-2)', fontWeight: 400, marginBottom: 16, flex: 1 }}>
|
||||||
<p style={{
|
|
||||||
fontSize: 13,
|
|
||||||
lineHeight: 1.85,
|
|
||||||
color: 'var(--ink-2)',
|
|
||||||
fontWeight: 400,
|
|
||||||
marginBottom: 16,
|
|
||||||
flex: 1,
|
|
||||||
}}>
|
|
||||||
{lang === 'en' && item.quoteEn ? item.quoteEn : item.quote}
|
{lang === 'en' && item.quoteEn ? item.quoteEn : item.quote}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Footer */}
|
<div style={{ paddingTop: 12, borderTop: '1px solid var(--rule-thin)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
<div style={{
|
|
||||||
paddingTop: 12,
|
|
||||||
borderTop: '1px solid var(--rule-thin)',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}>
|
|
||||||
<div>
|
<div>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink)' }}>{lang === 'en' && item.nameEn ? item.nameEn : item.name}</div>
|
<div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink)' }}>{lang === 'en' && item.nameEn ? item.nameEn : item.name}</div>
|
||||||
<div style={{ fontSize: 10, color: 'var(--ink-5)' }}>{lang === 'en' && item.dateEn ? item.dateEn : item.date}</div>
|
<div style={{ fontSize: 10, color: 'var(--ink-5)' }}>{lang === 'en' && item.dateEn ? item.dateEn : item.date}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{ fontSize: 9, fontWeight: 700, letterSpacing: '1px', textTransform: 'uppercase', color: cfg.dot, background: cfg.bg, padding: '4px 10px' }}>
|
||||||
fontSize: 9,
|
|
||||||
fontWeight: 700,
|
|
||||||
letterSpacing: '1px',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
color: cfg.dot,
|
|
||||||
background: cfg.bg,
|
|
||||||
padding: '4px 10px',
|
|
||||||
}}>
|
|
||||||
{cfg.label}
|
{cfg.label}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</ul>
|
</div>
|
||||||
|
|
||||||
|
{/* Controls */}
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 24 }}>
|
||||||
|
{/* Dots */}
|
||||||
|
<div style={{ display: 'flex', gap: 6 }}>
|
||||||
|
{riskItems.map((_, i) => (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
onClick={() => { setAnimDir('next'); setCurrent(i) }}
|
||||||
|
style={{
|
||||||
|
width: i === current ? 24 : 8, height: 8,
|
||||||
|
borderRadius: 4, border: 'none', cursor: 'pointer',
|
||||||
|
background: i === current ? 'var(--ink)' : 'var(--rule-thin)',
|
||||||
|
transition: 'all 300ms ease', padding: 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Arrows */}
|
||||||
|
<div style={{ display: 'flex', gap: 8 }}>
|
||||||
|
<button
|
||||||
|
onClick={() => go('prev')}
|
||||||
|
style={{ width: 36, height: 36, border: '1px solid var(--rule-thin)', background: 'transparent', cursor: 'pointer', fontSize: 16, display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'background 150ms' }}
|
||||||
|
onMouseEnter={e => (e.currentTarget.style.background = 'var(--paper-2)')}
|
||||||
|
onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}
|
||||||
|
>
|
||||||
|
{lang === 'fa' ? '→' : '←'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => go('next')}
|
||||||
|
style={{ width: 36, height: 36, border: '1px solid var(--rule-thin)', background: 'transparent', cursor: 'pointer', fontSize: 16, display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'background 150ms' }}
|
||||||
|
onMouseEnter={e => (e.currentTarget.style.background = 'var(--paper-2)')}
|
||||||
|
onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}
|
||||||
|
>
|
||||||
|
{lang === 'fa' ? '←' : '→'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 1.5 MiB |