Refine homepage layout, typography, and component polish

- Header: replaced 3-bar masthead with slim dark meta strip + 68px main bar
  (brand · centered nav · search/CTA actions). ~60% shorter than before.
- Home: added numbered SectionLabel chapter markers (01–07) between
  sections with red accent bar; alternated paper/paper-2 backgrounds
  for clearer section separation.
- Typography: wrapped base h1–h4 and p element styles in @layer base so
  Tailwind utilities (text-white, etc.) can override defaults. Previously
  unlayered p { color: var(--ink-3) } was overriding all carousel white
  titles, making them appear grey-brown.
- Carousel: removed pagination arrows; strengthened title gradient,
  added top centered red category badge, text-shadow + generous padding
  so titles never crash into card edges. Modal hero received same
  treatment with overflow-safe inset positioning.
- PlansSection: rewrote with compact shadcn-pricing pattern; price
  reduced from 42px to 26px; featured plan elevated with translateY
  and shadow; star badge for "Popular" plan.
- TeamSection: redesigned member cards more compact (avatar 60→44px,
  reports count moved to top-end, line-clamp on bio).
- Removed inline padding overrides across all sections so Tailwind
  max-md:px-5 mobile responsiveness actually works.
This commit is contained in:
alireza 2026-05-25 17:17:16 +03:30
parent 2dd08650cf
commit 8c5d2a9739
20 changed files with 1406 additions and 1505 deletions

View File

@ -89,7 +89,7 @@ export function Footer() {
style={{ backgroundColor: 'var(--ink)', color: 'rgba(255,255,255,0.6)' }}
>
{/* TOP GRID */}
<div style={{ padding: '64px 48px' }}>
<div style={{ padding: '64px 0' }} className="max-w-7xl mx-auto w-full px-12 max-md:px-5">
<div
style={{
display: 'grid',
@ -167,28 +167,34 @@ export function Footer() {
{/* BOTTOM ROW */}
<div
style={{
padding: '20px 48px',
borderTop: '1px solid rgba(255,255,255,0.08)',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: 10,
color: 'rgba(255,255,255,0.25)',
flexWrap: 'wrap',
gap: 12,
}}
>
<span>© ۱۴۰۳ اندیشکده فولاد ایران تمام حقوق محفوظ است</span>
<div style={{ display: 'flex', gap: 18 }}>
{['حریم خصوصی', 'شرایط استفاده', 'درگاه پرداخت', 'نقشه سایت'].map(l => (
<a
key={l}
href="#"
style={{ color: 'rgba(255,255,255,0.25)', textDecoration: 'none' }}
>
{l}
</a>
))}
<div
style={{
padding: '20px 48px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: 10,
color: 'rgba(255,255,255,0.25)',
flexWrap: 'wrap',
gap: 12,
}}
className="max-w-7xl mx-auto w-full px-12 max-md:px-5"
>
<span>© ۱۴۰۳ اندیشکده فولاد ایران تمام حقوق محفوظ است</span>
<div style={{ display: 'flex', gap: 18 }}>
{['حریم خصوصی', 'شرایط استفاده', 'درگاه پرداخت', 'نقشه سایت'].map(l => (
<a
key={l}
href="#"
style={{ color: 'rgba(255,255,255,0.25)', textDecoration: 'none' }}
>
{l}
</a>
))}
</div>
</div>
</div>
</footer>

File diff suppressed because it is too large Load Diff

View File

@ -43,27 +43,14 @@ export function Carousel({
initialScroll?: number
}) {
const carouselRef = useRef<HTMLDivElement>(null)
const [canScrollLeft, setCanScrollLeft] = useState(false)
const [canScrollRight, setCanScrollRight] = useState(true)
const [currentIndex, setCurrentIndex] = useState(0)
const [currentIndex, setCurrentIndex] = useState(0)
useEffect(() => {
if (carouselRef.current) {
carouselRef.current.scrollLeft = initialScroll
checkScrollability()
}
}, [initialScroll])
function checkScrollability() {
if (!carouselRef.current) return
const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current
setCanScrollLeft(scrollLeft > 0)
setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 1)
}
function scrollLeft() { carouselRef.current?.scrollBy({ left: -340, behavior: 'smooth' }) }
function scrollRight() { carouselRef.current?.scrollBy({ left: 340, behavior: 'smooth' }) }
function handleCardClose(index: number) {
if (!carouselRef.current) return
const cardWidth = window.innerWidth < 768 ? 230 : 350
@ -77,7 +64,6 @@ export function Carousel({
{/* Scrollable track */}
<div
ref={carouselRef}
onScroll={checkScrollability}
className="flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-10 md:py-14 [scrollbar-width:none]"
style={{ direction: 'ltr' }}
>
@ -96,27 +82,6 @@ export function Carousel({
</div>
</div>
{/* Arrow buttons */}
<div className="flex justify-end gap-2 px-10 pb-6">
<button
onClick={scrollLeft}
disabled={!canScrollLeft}
className="h-10 w-10 rounded-full bg-[var(--paper-3)] border border-[var(--rule-thin)] flex items-center justify-center disabled:opacity-30 transition-opacity hover:bg-[var(--paper-2)]"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} className="h-5 w-5 text-[var(--ink)]">
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
</svg>
</button>
<button
onClick={scrollRight}
disabled={!canScrollRight}
className="h-10 w-10 rounded-full bg-[var(--paper-3)] border border-[var(--rule-thin)] flex items-center justify-center disabled:opacity-30 transition-opacity hover:bg-[var(--paper-2)]"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} className="h-5 w-5 text-[var(--ink)]">
<path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
</div>
</CarouselContext.Provider>
)
@ -183,19 +148,31 @@ export function Card({
</button>
{/* Hero image strip */}
<div className="relative h-52 overflow-hidden" style={{ flexShrink: 0 }}>
<div className="relative h-56 overflow-hidden" style={{ flexShrink: 0 }}>
<img
src={card.src}
alt={card.title}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent" />
{/* inset-x-0 ensures text never overflows past modal edges */}
<div className="absolute bottom-0 inset-x-0 p-6">
<p className="text-white/70 text-xs font-bold tracking-widest uppercase mb-1">
{/* Stronger gradient — fully opaque at bottom for readable text */}
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/70 via-[40%] to-transparent" />
{/* Category badge — red, top */}
<div className="absolute top-6 right-8 z-10">
<span
className="inline-block text-white px-3 py-1.5 text-[11px] font-bold tracking-[2px] whitespace-nowrap"
style={{ background: 'var(--red)' }}
>
{card.category}
</p>
<p className="text-white text-lg font-bold leading-snug line-clamp-2">
</span>
</div>
{/* Title — bottom, bright white with shadow for clarity */}
<div className="absolute bottom-0 inset-x-0 px-8 pb-6 pt-10">
<p
className="text-white text-xl font-extrabold leading-tight line-clamp-2"
style={{ textShadow: '0 2px 8px rgba(0,0,0,0.6)' }}
>
{card.title}
</p>
</div>
@ -223,15 +200,25 @@ export function Card({
whileHover={{ scale: 1.015 }}
transition={{ duration: 0.2 }}
>
{/* Bottom-to-top gradient so text is readable — tall enough to cover 2 lines */}
<div className="absolute inset-0 bg-gradient-to-t from-black/95 via-black/65 via-[35%] to-transparent z-20 pointer-events-none" />
{/* Strong bottom-to-top gradient — fully opaque at bottom so title pops */}
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/75 via-[45%] to-transparent z-20 pointer-events-none" />
{/* Text — bottom of card, small */}
<div className="relative z-30 p-5 w-full text-right overflow-hidden">
<p className="text-white/65 text-[10px] font-bold tracking-widest uppercase mb-1.5 truncate">
{/* Top centered category badge — prominent, eye-catching */}
<div className="absolute top-6 left-1/2 -translate-x-1/2 z-30">
<span
className="inline-block text-white px-4 py-1.5 text-[12px] font-bold tracking-[2px] whitespace-nowrap"
style={{ background: 'var(--red)' }}
>
{card.category}
</p>
<p className="text-white text-sm font-bold leading-snug line-clamp-2">
</span>
</div>
{/* Bottom: title — bright white, readable */}
<div className="relative z-30 px-7 pb-6 pt-10 w-full text-right overflow-hidden">
<p
className="text-white text-[16px] font-extrabold leading-snug line-clamp-3"
style={{ textShadow: '0 2px 8px rgba(0,0,0,0.6)' }}
>
{card.title}
</p>
</div>

View File

@ -85,63 +85,69 @@
--fs-sm: clamp(11px, 0.75vw, 13px);
--fs-xs: clamp(9px, 0.6vw, 11px);
/* Letter spacing */
--ls-display: -2.5px;
--ls-h1: -2px;
--ls-h2: -1px;
--ls-h3: -0.3px;
--ls-body: 0px;
--ls-label: 1.5px;
--ls-overline: 2.5px;
--ls-widest: 3px;
/* Letter spacing (Persian typography uses normal spacing to keep cursive script connected) */
--ls-display: normal;
--ls-h1: normal;
--ls-h2: normal;
--ls-h3: normal;
--ls-body: normal;
--ls-label: normal;
--ls-overline: normal;
--ls-widest: normal;
/* Line heights */
--lh-display: 1.04;
--lh-heading: 1.12;
--lh-subhead: 1.3;
/* Line heights (increased for Persian script to prevent accent collisions and overlap) */
--lh-display: 1.25;
--lh-heading: 1.3;
--lh-subhead: 1.4;
--lh-body: 1.8;
--lh-relaxed: 1.85;
--lh-label: 1.3;
--lh-label: 1.4;
}
/* ── Base element styles ──────────────────────── */
h1 {
font-size: var(--fs-h1);
font-weight: 900;
line-height: var(--lh-heading);
letter-spacing: var(--ls-h1);
color: var(--ink);
margin: 0;
}
h2 {
font-size: var(--fs-h2);
font-weight: 900;
line-height: var(--lh-heading);
letter-spacing: var(--ls-h2);
color: var(--ink);
margin: 0;
}
h3 {
font-size: var(--fs-h3);
font-weight: 700;
line-height: var(--lh-subhead);
letter-spacing: var(--ls-h3);
color: var(--ink);
margin: 0;
}
h4 {
font-size: var(--fs-h4);
font-weight: 700;
line-height: var(--lh-subhead);
color: var(--ink);
margin: 0;
}
p {
font-size: var(--fs-body);
font-weight: 400; /* was 300 — too thin on most screens */
line-height: var(--lh-body);
color: var(--ink-3);
margin: 0;
/* Base element styles
Inside @layer base so Tailwind utilities (text-white, text-red, etc.)
in the @utilities layer can override these defaults. */
@layer base {
h1 {
font-size: var(--fs-h1);
font-weight: 900;
line-height: var(--lh-heading);
letter-spacing: normal;
color: var(--ink);
margin: 0;
}
h2 {
font-size: var(--fs-h2);
font-weight: 900;
line-height: var(--lh-heading);
letter-spacing: normal;
color: var(--ink);
margin: 0;
}
h3 {
font-size: var(--fs-h3);
font-weight: 700;
line-height: var(--lh-subhead);
letter-spacing: normal;
color: var(--ink);
margin: 0;
}
h4 {
font-size: var(--fs-h4);
font-weight: 700;
line-height: var(--lh-subhead);
letter-spacing: normal;
color: var(--ink);
margin: 0;
}
p {
font-size: var(--fs-body);
font-weight: 400;
line-height: var(--lh-body);
letter-spacing: normal;
color: var(--ink-3);
margin: 0;
}
}
/*
@ -149,6 +155,12 @@ p {
*/
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* McKinsey centered container margins (physical override of standard logical properties) */
.mx-auto {
margin-left: auto !important;
margin-right: auto !important;
}
html { scroll-behavior: smooth; direction: rtl; }
body {
@ -157,6 +169,7 @@ body {
color: var(--ink);
font-size: var(--fs-body);
line-height: var(--lh-body);
letter-spacing: normal;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
}
@ -180,7 +193,7 @@ body {
.t-overline {
font-size: var(--fs-xs);
font-weight: 700;
letter-spacing: var(--ls-overline);
letter-spacing: normal;
text-transform: uppercase;
line-height: var(--lh-label);
}

View File

@ -55,14 +55,10 @@ function ReportRow({ report }: { report: (typeof reports)[0] }) {
return (
<div
className="grid grid-cols-[64px_1fr_140px_52px_80px] max-md:grid-cols-[56px_1fr_70px] archive-row items-center border-b border-[var(--rule-thin)] min-h-[40px]"
style={{
display: 'grid',
gridTemplateColumns: '64px 1fr 140px 52px 80px',
alignItems: 'center',
borderBottom: '1px solid var(--rule-thin)',
background: hovered ? 'var(--paper-2)' : 'transparent',
transition: 'background 100ms',
minHeight: 40,
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
@ -78,7 +74,6 @@ function ReportRow({ report }: { report: (typeof reports)[0] }) {
style={{
fontSize: 9,
fontWeight: 700,
letterSpacing: '0.8px',
color: typeColor,
border: `1px solid ${typeColor}`,
padding: '2px 5px',
@ -116,6 +111,7 @@ function ReportRow({ report }: { report: (typeof reports)[0] }) {
borderLeft: '1px solid var(--rule-thin)',
overflow: 'hidden',
}}
className="max-md:hidden"
>
<span
style={{
@ -139,6 +135,7 @@ function ReportRow({ report }: { report: (typeof reports)[0] }) {
borderLeft: '1px solid var(--rule-thin)',
textAlign: 'center',
}}
className="max-md:hidden"
>
<span style={{ fontSize: 11, color: 'var(--ink-5)', fontWeight: 400 }}>
{report.pages.toLocaleString('fa-IR')} ص
@ -355,29 +352,20 @@ export default function Archive() {
{/* column headers */}
<div
style={{
display: 'grid',
gridTemplateColumns: '64px 1fr 140px 52px 80px',
alignItems: 'center',
borderTop: '2px solid var(--ink)',
borderBottom: '1px solid var(--ink)',
marginTop: 8,
background: 'var(--paper-2)',
}}
className="grid grid-cols-[64px_1fr_140px_52px_80px] max-md:grid-cols-[56px_1fr_70px] archive-row items-center border-t-2 border-b border-[var(--ink)] mt-2 bg-[var(--paper-2)]"
>
{[
{ label: 'نوع', style: { padding: '6px 12px', borderLeft: '1px solid var(--rule-thin)' } },
{ label: 'عنوان گزارش', style: { padding: '6px 16px', borderLeft: '1px solid var(--rule-thin)' } },
{ label: 'نویسنده', style: { padding: '6px 12px', borderLeft: '1px solid var(--rule-thin)' } },
{ label: 'صفحه', style: { padding: '6px 10px', borderLeft: '1px solid var(--rule-thin)', textAlign: 'center' as const } },
{ label: 'نویسنده', style: { padding: '6px 12px', borderLeft: '1px solid var(--rule-thin)' }, hideMobile: true },
{ label: 'صفحه', style: { padding: '6px 10px', borderLeft: '1px solid var(--rule-thin)', textAlign: 'center' as const }, hideMobile: true },
{ label: 'قیمت', style: { padding: '6px 12px', textAlign: 'left' as const } },
].map((col) => (
<div key={col.label} style={col.style}>
<div key={col.label} style={col.style} className={col.hideMobile ? "max-md:hidden" : ""}>
<span
style={{
fontSize: 9,
fontWeight: 700,
letterSpacing: '1.2px',
color: 'var(--ink-4)',
textTransform: 'uppercase',
}}

View File

@ -1,26 +1,123 @@
import HeroSection from './sections/HeroSection'
import SnapshotBar from './sections/SnapshotBar'
import GlobalScannerSection from './sections/GlobalScannerSection'
import HeroSection from './sections/HeroSection'
import SnapshotBar from './sections/SnapshotBar'
import GlobalScannerSection from './sections/GlobalScannerSection'
import ReportsCarouselSection from './sections/ReportsCarouselSection'
import LatestNewsBentoSection from './sections/LatestNewsBentoSection'
import EventsSection from './sections/EventsSection'
import RiskSection from './sections/RiskSection'
import PlansSection from './sections/PlansSection'
import TeamSection from './sections/TeamSection'
import NewsletterSection from './sections/NewsletterSection'
import EventsSection from './sections/EventsSection'
import RiskSection from './sections/RiskSection'
import TeamSection from './sections/TeamSection'
import PlansSection from './sections/PlansSection'
import NewsletterSection from './sections/NewsletterSection'
/* Section chapter marker
A thin full-bleed dark bar that stamps the start of every
major section creates instant, unmistakable boundaries.
*/
function SectionLabel({ n, title }: { n: string; title: string }) {
return (
<div
style={{
background: 'var(--ink)',
borderTop: '1px solid rgba(255,255,255,0.08)',
position: 'relative',
}}
>
{/* Red accent bar on the right edge (RTL → visual start) */}
<div style={{
position: 'absolute',
top: 0,
bottom: 0,
right: 0,
width: 4,
background: 'var(--red)',
}} />
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 24,
}}
className="max-w-7xl mx-auto w-full px-12 py-10 max-md:px-5 max-md:py-7 max-md:gap-4"
>
<span style={{
fontSize: 36,
fontWeight: 900,
color: 'var(--red)',
letterSpacing: '-1.5px',
lineHeight: 1,
direction: 'ltr',
fontVariantNumeric: 'tabular-nums',
flexShrink: 0,
}}
className="max-md:text-[26px]"
>
{n}
</span>
<div style={{
width: 40,
height: 2,
background: 'rgba(255,255,255,0.25)',
flexShrink: 0,
}}
className="max-md:w-6"
/>
<span style={{
fontSize: 17,
fontWeight: 800,
letterSpacing: '-0.3px',
color: 'var(--paper)',
}}
className="max-md:text-[14px]"
>
{title}
</span>
</div>
</div>
)
}
/* ─── Home page ──────────────────────────────────────── */
export default function Home() {
return (
<div>
{/* ── 00 · Hero ─────────────────────────────────── */}
<HeroSection />
{/* ── Ticker ────────────────────────────────────── */}
<SnapshotBar />
{/* ── 01 · اسکنر جهانی · bg: paper ─────────────── */}
<SectionLabel n="01" title="اسکنر جهانی · رصد ۲۴ ساعته" />
<GlobalScannerSection />
{/* ── 02 · تحلیل‌ها · bg: paper-2 ──────────────── */}
<SectionLabel n="02" title="تحلیل‌های اخیر" />
<ReportsCarouselSection />
{/* ── 03 · اخبار · bg: paper ────────────────────── */}
<SectionLabel n="03" title="آخرین اخبار بازار" />
<LatestNewsBentoSection />
{/* ── 04 · رویدادها · bg: paper-2 ──────────────── */}
<SectionLabel n="04" title="رویدادهای پیش‌رو" />
<EventsSection />
{/* ── 05 · ریسک · bg: paper ─────────────────────── */}
<SectionLabel n="05" title="پایش ریسک · لحظه‌ای" />
<RiskSection />
{/* ── 06 · تیم · bg: paper-2 ────────────────────── */}
<SectionLabel n="06" title="تیم تحریریه" />
<TeamSection />
{/* ── 07 · اشتراک · bg: paper ───────────────────── */}
<SectionLabel n="07" title="اشتراک و دسترسی" />
<PlansSection />
{/* ── خبرنامه (last, no label) ───────────────────── */}
<NewsletterSection />
</div>
)

View File

@ -27,32 +27,28 @@ export default function EventsSection() {
const displayed = events.slice(0, 4)
return (
<section style={{ borderBottom: '2px solid var(--ink)' }}>
<div className="px-12 py-20 max-md:px-5 max-md:py-14">
{/* Banner */}
<div style={{ display: 'flex', alignItems: 'center', borderBottom: '1px solid var(--rule-thin)', marginBottom: 0 }}>
<div style={{ background: 'var(--ink)', color: 'var(--paper)', padding: '8px 20px', fontSize: '9px', fontWeight: 700, letterSpacing: '3px', textTransform: 'uppercase' }}>رویدادهای پیشرو</div>
<div style={{ flex: 1 }} />
<Link to="/events" style={{ fontSize: '10px', fontWeight: 700, letterSpacing: '1.5px', textTransform: 'uppercase', color: 'var(--ink-4)', textDecoration: 'none', padding: '8px 0' }}>تقویم کامل </Link>
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper-2)' }}>
<div className="max-w-7xl mx-auto px-12 py-20 max-md:px-5 max-md:py-14">
{/* Sleek top row */}
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 24 }}>
<Link to="/events" style={{ fontSize: '10px', fontWeight: 700, textTransform: 'uppercase', color: 'var(--ink-4)', textDecoration: 'none' }}>تقویم کامل </Link>
</div>
{/* 4-col grid */}
<div
style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)' }}
className="max-md:grid-cols-2 max-sm:grid-cols-1"
className="grid grid-cols-4 max-md:grid-cols-2 max-sm:grid-cols-1"
>
{displayed.map((event, idx) => (
{displayed.map((event) => (
<div
key={event.id}
style={{
padding: '32px 28px',
borderRight: idx === 0 ? 'none' : '1px solid var(--rule-thin)',
}}
className="max-md:border-b max-md:border-[var(--rule-thin)] max-md:last:border-b-0"
className="border-r border-[var(--rule-thin)] first:border-r-0 max-md:border-r-0 max-md:border-b max-md:border-[var(--rule-thin)] max-md:nth-child(even):border-r max-md:nth-child(even):border-[var(--rule-thin)] max-sm:border-r-0 max-sm:border-b max-sm:last:border-b-0 max-md:nth-child(3):border-b-0 max-md:nth-child(4):border-b-0"
>
{/* Date block */}
<div style={{ borderBottom: '2px solid var(--ink)', paddingBottom: 12, marginBottom: 16 }}>
<div style={{ fontSize: 52, fontWeight: 900, letterSpacing: '-2px', lineHeight: 1, color: 'var(--ink)' }}>
<div style={{ fontSize: 52, fontWeight: 900, lineHeight: 1, color: 'var(--ink)' }}>
{event.date.toLocaleString('fa-IR')}
</div>
<div style={{ fontSize: 12, color: 'var(--ink-4)', marginTop: 2 }}>
@ -70,7 +66,6 @@ export default function EventsSection() {
fontSize: 9,
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '1px',
padding: '3px 8px',
lineHeight: 1.4,
}}
@ -87,11 +82,11 @@ export default function EventsSection() {
{/* CTA */}
{event.registrationOpen ? (
<Link to="#" style={{ fontSize: 10, fontWeight: 700, letterSpacing: '2px', textTransform: 'uppercase', color: 'var(--red)', textDecoration: 'none', cursor: 'pointer' }}>
<Link to="#" style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', color: 'var(--red)', textDecoration: 'none', cursor: 'pointer' }}>
ثبتنام
</Link>
) : (
<span style={{ fontSize: 10, color: 'var(--ink-5)', letterSpacing: '1px', textTransform: 'uppercase', fontWeight: 700 }}>
<span style={{ fontSize: 10, color: 'var(--ink-5)', textTransform: 'uppercase', fontWeight: 700 }}>
بهزودی
</span>
)}

View File

@ -56,124 +56,100 @@ export default function GlobalScannerSection() {
overflow: 'hidden',
}}
>
{/* ── Text header ────────────────────────────────── */}
<div
style={{ textAlign: 'center' }}
className="px-12 pt-[72px] pb-12 max-md:px-5 max-md:pt-10 max-md:pb-8"
>
{/* Overline */}
<div className="max-w-7xl mx-auto w-full px-12 max-md:px-5 pb-12">
{/* ── Text header ────────────────────────────────── */}
<div
style={{
display: 'inline-flex',
alignItems: 'center',
gap: 10,
marginBottom: 20,
}}
style={{ textAlign: 'center', paddingTop: 72, paddingBottom: 48 }}
>
<div style={{ width: 20, height: 2, background: 'var(--red)' }} />
<span style={{
fontSize: 9,
fontWeight: 700,
letterSpacing: '3px',
textTransform: 'uppercase',
color: 'var(--red)',
{/* Headline */}
<h2 style={{
fontSize: 'clamp(28px, 3.5vw, 52px)',
fontWeight: 900,
lineHeight: 1.1,
letterSpacing: '-1.5px',
color: 'var(--ink)',
marginBottom: 16,
}}>
اسکنر جهانی · پوشش ۲۴ ساعته
</span>
<div style={{ width: 20, height: 2, background: 'var(--red)' }} />
تمام رویدادهای فولادی دنیا{' '}
<motion.span
style={{ color: 'var(--ink-6)', display: 'inline' }}
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.3 }}
>
را رصد میکنیم
</motion.span>
</h2>
{/* Subhead */}
<p style={{
fontSize: 15,
lineHeight: 1.85,
fontWeight: 400,
color: 'var(--ink-3)',
maxWidth: 600,
margin: '0 auto',
}}>
از بورس دالیان چین تا گمرک بصره عراق، از تصمیمات کمیته فولاد OECD
تا ظرفیتسازیهای هند هر سیگنالی که بر رقابتپذیری ایران اثر
میگذارد، پیش از بقیه به دست شما میرسد.
</p>
</div>
{/* Headline */}
<h2 style={{
fontSize: 'clamp(28px, 3.5vw, 52px)',
fontWeight: 900,
lineHeight: 1.1,
letterSpacing: '-1.5px',
color: 'var(--ink)',
marginBottom: 16,
}}>
تمام رویدادهای فولادی دنیا{' '}
<motion.span
style={{ color: 'var(--ink-6)', display: 'inline' }}
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.3 }}
>
را رصد میکنیم
</motion.span>
</h2>
{/* ── World map (full width within container) ─── */}
<div className="max-md:px-0">
<WorldMap
dots={ROUTES}
lineColor="#9b1c1c"
dotColor="#c8c2bc"
/>
</div>
{/* Subhead */}
<p style={{
fontSize: 15,
lineHeight: 1.85,
fontWeight: 400,
color: 'var(--ink-3)',
maxWidth: 600,
margin: '0 auto',
}}>
از بورس دالیان چین تا گمرک بصره عراق، از تصمیمات کمیته فولاد OECD
تا ظرفیتسازیهای هند هر سیگنالی که بر رقابتپذیری ایران اثر
میگذارد، پیش از بقیه به دست شما میرسد.
</p>
</div>
{/* ── World map (full width, no side padding) ─── */}
<div className="px-6 max-md:px-0">
<WorldMap
dots={ROUTES}
lineColor="#9b1c1c"
dotColor="#c8c2bc"
/>
</div>
{/* ── Stats strip ──────────────────────────────── */}
<div
style={{
borderTop: '1px solid var(--rule-thin)',
display: 'grid',
gridTemplateColumns: 'repeat(4, 1fr)',
}}
className="max-md:grid-cols-2"
>
{[
{ n: '۱۸۰+', label: 'بازار رصد‌شده', sub: 'در ۵ قاره' },
{ n: '۷/۲۴', label: 'پایش لحظه‌ای', sub: 'قیمت و حجم' },
{ n: '۴۰+', label: 'منبع داده', sub: 'بورس‌ها و گمرک‌ها' },
{ n: '۱۵', label: 'کشور رقیب', sub: 'زیر ذره‌بین' },
].map((s, i) => (
<motion.div
key={i}
initial={{ opacity: 0, y: 12 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.4, delay: i * 0.08 }}
style={{
padding: '28px 24px',
borderRight: i > 0 ? '1px solid var(--rule-thin)' : 'none',
textAlign: 'center',
}}
className="max-md:border-r-0 max-md:border-b max-md:border-[var(--rule-thin)]"
>
<div style={{
fontSize: 32,
fontWeight: 900,
color: 'var(--ink)',
letterSpacing: '-1px',
lineHeight: 1,
marginBottom: 4,
}}>
{s.n}
</div>
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', marginBottom: 2 }}>
{s.label}
</div>
<div style={{ fontSize: 10, color: 'var(--ink-5)' }}>
{s.sub}
</div>
</motion.div>
))}
{/* ── Stats strip ──────────────────────────────── */}
<div
style={{
borderTop: '1px solid var(--rule-thin)',
marginTop: 48,
}}
className="grid grid-cols-4 max-md:grid-cols-2 max-sm:grid-cols-1"
>
{[
{ n: '۱۸۰+', label: 'بازار رصد‌شده', sub: 'در ۵ قاره' },
{ n: '۷/۲۴', label: 'پایش لحظه‌ای', sub: 'قیمت و حجم' },
{ n: '۴۰+', label: 'منبع داده', sub: 'بورس‌ها و گمرک‌ها' },
{ n: '۱۵', label: 'کشور رقیب', sub: 'زیر ذره‌بین' },
].map((s, i) => (
<motion.div
key={i}
initial={{ opacity: 0, y: 12 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.4, delay: i * 0.08 }}
style={{
padding: '28px 24px',
textAlign: 'center',
}}
className="border-r border-[var(--rule-thin)] first:border-r-0 max-md:border-r-0 max-md:border-b max-md:border-[var(--rule-thin)] max-md:nth-child(even):border-r max-md:nth-child(even):border-[var(--rule-thin)] max-sm:border-r-0 max-sm:border-b max-sm:last:border-b-0 max-md:nth-child(3):border-b-0 max-md:nth-child(4):border-b-0"
>
<div style={{
fontSize: 32,
fontWeight: 900,
color: 'var(--ink)',
lineHeight: 1,
marginBottom: 4,
}}>
{s.n}
</div>
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', marginBottom: 2 }}>
{s.label}
</div>
<div style={{ fontSize: 10, color: 'var(--ink-5)' }}>
{s.sub}
</div>
</motion.div>
))}
</div>
</div>
</section>
)

View File

@ -82,233 +82,231 @@ export default function HeroSection() {
</div>
{/* ── Main content — anchored bottom ── */}
<div style={{
position: 'absolute',
inset: 0,
display: 'grid',
gridTemplateColumns: '1fr 240px',
alignItems: 'flex-end',
gap: 48,
}}
className="px-14 pb-[52px] max-md:grid-cols-1 max-md:px-5 max-md:pb-8"
<div
style={{
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'flex-end',
}}
>
{/* ── Left: headline + meta ── */}
<div>
{/* Overline */}
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 20 }}
>
<div style={{ width: 24, height: 2, background: 'var(--red)', flexShrink: 0 }} />
<span style={{
fontSize: 10,
fontWeight: 700,
letterSpacing: '2.5px',
textTransform: 'uppercase',
color: 'rgba(255,255,255,0.5)',
}}>
گزارش راهبردی · تحلیل سناریو
</span>
</motion.div>
{/* Headline */}
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
style={{
fontSize: 'clamp(36px, 5vw, 72px)',
fontWeight: 900,
lineHeight: 1.04,
letterSpacing: '-2.5px',
color: 'white',
marginBottom: 24,
maxWidth: 700,
}}
>
{featured.title.split(':').map((part, i) => (
<span key={i}>
{i === 0 ? part + ':' : (
<span style={{ color: 'rgba(255,255,255,0.45)' }}>{part}</span>
)}
{i === 0 && <br />}
</span>
))}
</motion.h1>
{/* Summary — one line */}
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.25 }}
style={{
fontSize: 14,
lineHeight: 1.8,
fontWeight: 400,
color: 'rgba(255,255,255,0.6)',
maxWidth: 580,
marginBottom: 32,
}}
>
مدلسازی کمی بر اساس دادههای تجارت ۱۵ کشور رقیب از بدبینانه تا خوشبینانه،
با توصیههای سیاستی مستقیم برای وزارت صمت و سازمان بنادر.
</motion.p>
{/* Divider */}
<div style={{ width: '100%', height: 1, background: 'rgba(255,255,255,0.12)', marginBottom: 24 }} />
{/* Author + Price + CTA in one bar */}
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.35 }}
style={{
display: 'flex',
alignItems: 'center',
gap: 24,
flexWrap: 'wrap',
}}
>
{/* Author */}
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<div style={{
width: 36, height: 36,
background: 'var(--red)',
color: 'white',
fontSize: 14, fontWeight: 900,
display: 'flex', alignItems: 'center', justifyContent: 'center',
flexShrink: 0,
}}>
{featured.authorInitial}
</div>
<div>
<div style={{ fontSize: 12, fontWeight: 700, color: 'white', lineHeight: 1.2 }}>
{featured.author}
</div>
<div style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)', lineHeight: 1.3 }}>
{featured.authorRole}
</div>
</div>
</div>
{/* Separator */}
<div style={{ width: 1, height: 32, background: 'rgba(255,255,255,0.15)' }} />
{/* Price */}
<div style={{ display: 'flex', alignItems: 'baseline', gap: 5 }}>
<span style={{
fontSize: 28,
fontWeight: 900,
letterSpacing: '-1px',
color: 'white',
lineHeight: 1,
}}>
{featured.price.toLocaleString('fa-IR')}
</span>
<span style={{ fontSize: 11, color: 'rgba(255,255,255,0.4)' }}>تومان</span>
</div>
{/* Buttons */}
<div style={{ display: 'flex', gap: 10 }}>
<button
onMouseEnter={() => setBuyHov(true)}
onMouseLeave={() => setBuyHov(false)}
style={{
background: buyHov ? 'var(--paper-2)' : 'var(--paper)',
color: 'var(--ink)',
border: 'none',
padding: '11px 26px',
fontSize: 12,
fontWeight: 700,
fontFamily: 'Vazir, sans-serif',
cursor: 'pointer',
letterSpacing: '0.5px',
transition: 'background 160ms',
whiteSpace: 'nowrap',
}}
>
خرید گزارش
</button>
<button
onMouseEnter={() => setFreeHov(true)}
onMouseLeave={() => setFreeHov(false)}
style={{
background: freeHov ? 'rgba(255,255,255,0.1)' : 'transparent',
color: 'white',
border: '1px solid rgba(255,255,255,0.3)',
padding: '11px 26px',
fontSize: 12,
fontWeight: 400,
fontFamily: 'Vazir, sans-serif',
cursor: 'pointer',
letterSpacing: '0.5px',
transition: 'background 160ms',
whiteSpace: 'nowrap',
}}
>
دانلود خلاصه رایگان
</button>
</div>
</motion.div>
</div>
{/* ── Right: floating stats panel ── */}
<motion.div
initial={{ opacity: 0, x: -16 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.45 }}
style={{
background: 'rgba(255,255,255,0.06)',
backdropFilter: 'blur(12px)',
WebkitBackdropFilter: 'blur(12px)',
border: '1px solid rgba(255,255,255,0.1)',
padding: '20px',
marginBottom: 2,
}}
className="max-md:hidden"
<div
className="max-w-7xl mx-auto w-full px-14 pb-[52px] grid grid-cols-1 md:grid-cols-[1fr_240px] items-end gap-12 max-md:px-5 max-md:pb-8 max-md:gap-6"
>
<div style={{
fontSize: 9,
fontWeight: 700,
letterSpacing: '2px',
textTransform: 'uppercase',
color: 'rgba(255,255,255,0.35)',
marginBottom: 16,
paddingBottom: 10,
borderBottom: '1px solid rgba(255,255,255,0.1)',
}}>
در این گزارش
</div>
{STATS.map((s, i) => (
<div
key={i}
{/* ── Left: headline + meta ── */}
<div>
{/* Overline */}
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 20 }}
>
<div style={{ width: 24, height: 2, background: 'var(--red)', flexShrink: 0 }} />
<span style={{
fontSize: 10,
fontWeight: 700,
color: 'rgba(255,255,255,0.5)',
}}>
گزارش راهبردی · تحلیل سناریو
</span>
</motion.div>
{/* Headline */}
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'baseline',
padding: '10px 0',
borderBottom: i < STATS.length - 1 ? '1px solid rgba(255,255,255,0.07)' : 'none',
fontSize: 'clamp(32px, 5vw, 64px)',
fontWeight: 900,
lineHeight: 1.2,
color: 'white',
marginBottom: 24,
maxWidth: 700,
}}
>
<span style={{ fontSize: 11, color: 'rgba(255,255,255,0.5)', fontWeight: 300 }}>
{s.label}
</span>
<span style={{
fontSize: 22,
fontWeight: 900,
letterSpacing: '-0.5px',
color: 'white',
lineHeight: 1,
}}>
{s.n}
</span>
</div>
))}
</motion.div>
{featured.title.split(':').map((part, i) => (
<span key={i}>
{i === 0 ? part + ':' : (
<span style={{ color: 'rgba(255,255,255,0.45)' }}>{part}</span>
)}
{i === 0 && <br />}
</span>
))}
</motion.h1>
{/* Summary — one line */}
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.25 }}
style={{
fontSize: 14,
lineHeight: 1.8,
fontWeight: 400,
color: 'rgba(255,255,255,0.6)',
maxWidth: 580,
marginBottom: 32,
}}
>
مدلسازی کمی بر اساس دادههای تجارت ۱۵ کشور رقیب از بدبینانه تا خوشبینانه،
با توصیههای سیاستی مستقیم برای وزارت صمت و سازمان بنادر.
</motion.p>
{/* Divider */}
<div style={{ width: '100%', height: 1, background: 'rgba(255,255,255,0.12)', marginBottom: 24 }} />
{/* Author + Price + CTA in one bar */}
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.35 }}
style={{
display: 'flex',
alignItems: 'center',
gap: 24,
flexWrap: 'wrap',
}}
>
{/* Author */}
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<div style={{
width: 36, height: 36,
background: 'var(--red)',
color: 'white',
fontSize: 14, fontWeight: 900,
display: 'flex', alignItems: 'center', justifyContent: 'center',
flexShrink: 0,
}}>
{featured.authorInitial}
</div>
<div>
<div style={{ fontSize: 12, fontWeight: 700, color: 'white', lineHeight: 1.2 }}>
{featured.author}
</div>
<div style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)', lineHeight: 1.3 }}>
{featured.authorRole}
</div>
</div>
</div>
{/* Separator */}
<div className="max-sm:hidden" style={{ width: 1, height: 32, background: 'rgba(255,255,255,0.15)' }} />
{/* Price */}
<div style={{ display: 'flex', alignItems: 'baseline', gap: 5 }}>
<span style={{
fontSize: 28,
fontWeight: 900,
color: 'white',
lineHeight: 1,
}}>
{featured.price.toLocaleString('fa-IR')}
</span>
<span style={{ fontSize: 11, color: 'rgba(255,255,255,0.4)' }}>تومان</span>
</div>
{/* Buttons */}
<div className="flex gap-2.5 max-sm:flex-col max-sm:w-full">
<button
onMouseEnter={() => setBuyHov(true)}
onMouseLeave={() => setBuyHov(false)}
style={{
background: buyHov ? 'var(--paper-2)' : 'var(--paper)',
color: 'var(--ink)',
border: 'none',
padding: '11px 26px',
fontSize: 12,
fontWeight: 700,
fontFamily: 'Vazir, sans-serif',
cursor: 'pointer',
transition: 'background 160ms',
whiteSpace: 'nowrap',
}}
className="max-sm:w-full"
>
خرید گزارش
</button>
<button
onMouseEnter={() => setFreeHov(true)}
onMouseLeave={() => setFreeHov(false)}
style={{
background: freeHov ? 'rgba(255,255,255,0.1)' : 'transparent',
color: 'white',
border: '1px solid rgba(255,255,255,0.3)',
padding: '11px 26px',
fontSize: 12,
fontWeight: 400,
fontFamily: 'Vazir, sans-serif',
cursor: 'pointer',
transition: 'background 160ms',
whiteSpace: 'nowrap',
}}
className="max-sm:w-full"
>
دانلود خلاصه رایگان
</button>
</div>
</motion.div>
</div>
{/* ── Right: floating stats panel ── */}
<motion.div
initial={{ opacity: 0, x: -16 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: 0.45 }}
style={{
background: 'rgba(255,255,255,0.06)',
backdropFilter: 'blur(12px)',
WebkitBackdropFilter: 'blur(12px)',
border: '1px solid rgba(255,255,255,0.1)',
padding: '20px',
marginBottom: 2,
}}
className="max-md:hidden"
>
<div style={{
fontSize: 9,
fontWeight: 700,
letterSpacing: '2px',
textTransform: 'uppercase',
color: 'rgba(255,255,255,0.35)',
marginBottom: 16,
paddingBottom: 10,
borderBottom: '1px solid rgba(255,255,255,0.1)',
}}>
در این گزارش
</div>
{STATS.map((s, i) => (
<div
key={i}
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'baseline',
padding: '10px 0',
borderBottom: i < STATS.length - 1 ? '1px solid rgba(255,255,255,0.07)' : 'none',
}}
>
<span style={{ fontSize: 11, color: 'rgba(255,255,255,0.5)', fontWeight: 300 }}>
{s.label}
</span>
<span style={{
fontSize: 22,
fontWeight: 900,
letterSpacing: '-0.5px',
color: 'white',
lineHeight: 1,
}}>
{s.n}
</span>
</div>
))}
</motion.div>
</div>
</div>
</section>
)

View File

@ -51,27 +51,12 @@ function Accent({ color = 'var(--red)' }: { color?: string }) {
/* ─── Section ────────────────────────────────────────────── */
export default function LatestNewsBentoSection() {
return (
<section style={{ background: 'var(--paper-2)', borderBottom: '2px solid var(--ink)' }}>
<section style={{ background: 'var(--paper)', borderBottom: '2px solid var(--ink)' }}>
{/* ── Header ── */}
<div className="px-12 pt-16 max-md:px-5 max-md:pt-10">
<div style={{
display: 'flex', alignItems: 'center', gap: 16,
marginBottom: 32, paddingBottom: 20, borderBottom: '2px solid var(--ink)',
}}>
<div style={{
background: 'var(--ink)', color: 'var(--paper)',
fontSize: 9, fontWeight: 700, letterSpacing: '3px',
textTransform: 'uppercase', padding: '6px 16px',
}}>
آخرین اخبار
</div>
<h2 style={{ fontSize: 'clamp(16px,2vw,22px)', fontWeight: 900, letterSpacing: '-0.5px' }}>
رصد روزانه بازار و صنعت
</h2>
<div style={{ flex: 1 }} />
<span style={{ fontSize: 10, fontWeight: 500, color: 'var(--ink-5)' }}>بهمن ۱۴۰۳</span>
</div>
<div className="max-w-7xl mx-auto px-12 pt-8 pb-4 max-md:px-5 max-md:pt-6 flex justify-between items-center text-xs text-[var(--ink-4)]">
<span>رصد روزانه بازار و صنعت</span>
<span>بهمن ۱۴۰۳</span>
</div>
{/* ── Bento grid ── */}
@ -80,7 +65,7 @@ export default function LatestNewsBentoSection() {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="px-12 pb-14 max-md:px-5 max-md:pb-10"
className="max-w-7xl mx-auto w-full px-12 pb-14 max-md:px-5 max-md:pb-10"
>
{/*
3-col grid on desktop, 1-col on mobile.

View File

@ -13,11 +13,11 @@ export default function NewsletterSection() {
}
return (
<section style={{ borderBottom: '3px solid var(--ink)' }}>
<section style={{ borderBottom: '3px solid var(--ink)', background: 'var(--paper-2)' }}>
{/* Outer grid — strip inline padding, use Tailwind */}
<div
style={{ display: 'grid', gridTemplateColumns: '1fr 1px 1fr', minHeight: 180 }}
className="px-12 max-md:px-5 max-md:grid-cols-1"
className="max-w-7xl mx-auto w-full px-12 max-md:px-5 max-md:grid-cols-1"
>
{/* Left: description */}
<div

View File

@ -1,28 +1,39 @@
import { useState } from 'react'
import { plans } from '@/data/plans'
/* ─── Checkmark icon ─────────────────────────────────── */
/* ─── Star icon (for "popular" badge) ──────────────── */
function Star() {
return (
<svg width="11" height="11" viewBox="0 0 16 16" fill="currentColor" style={{ flexShrink: 0 }}>
<path d="M8 1l2.09 4.23L14.78 6l-3.39 3.31.8 4.69L8 11.77 3.81 14l.8-4.69L1.22 6l4.69-.77L8 1z" />
</svg>
)
}
/* ─── Check icon ──────────────────────────────────── */
function Check({ featured }: { featured: boolean }) {
return (
<svg
width="16" height="16" viewBox="0 0 16 16" fill="none"
style={{ flexShrink: 0, marginTop: 1 }}
width="14" height="14" viewBox="0 0 16 16" fill="none"
style={{ flexShrink: 0, marginTop: 3 }}
>
<circle
cx="8" cy="8" r="7.5"
stroke={featured ? 'rgba(255,255,255,0.35)' : 'var(--rule-thin)'}
fill={featured ? 'rgba(255,255,255,0.1)' : 'transparent'}
/>
<path
d="M5 8l2 2 4-4"
stroke={featured ? '#f4efe7' : 'var(--green-ink)'}
strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"
d="M3 8.5l3 3 7-7"
stroke={featured ? 'rgba(244,239,231,0.85)' : 'var(--ink)'}
strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round"
/>
</svg>
)
}
/* ─── Individual plan card ───────────────────────────── */
/* ─── Plan tagline lookup ─────────────────────────── */
const TAGLINE: Record<string, string> = {
individual: 'برای کارشناسان و محققان',
organizational: 'برای شرکت‌ها و سازمان‌ها',
ministry: 'برای نهادهای دولتی',
}
/* ─── Individual plan card ────────────────────────── */
function PlanCard({ plan, isMiddle }: { plan: (typeof plans)[0]; isMiddle: boolean }) {
const [hovered, setHovered] = useState(false)
const featured = plan.featured
@ -30,148 +41,152 @@ function PlanCard({ plan, isMiddle }: { plan: (typeof plans)[0]; isMiddle: boole
return (
<div
style={{
background: featured ? 'var(--ink)' : 'white',
border: featured ? 'none' : '1px solid var(--rule-thin)',
borderTop: featured ? 'none' : '1px solid var(--rule-thin)',
padding: '32px 28px',
display: 'flex',
position: 'relative',
background: featured ? 'var(--ink)' : 'var(--paper)',
border: '1px solid var(--rule-thin)',
display: 'flex',
flexDirection: 'column',
position: 'relative',
boxShadow: isMiddle ? '0 8px 40px rgba(26,23,18,0.12)' : 'none',
zIndex: isMiddle ? 1 : 0,
boxShadow: isMiddle ? '0 12px 40px rgba(26,23,18,0.14)' : 'none',
zIndex: isMiddle ? 1 : 0,
transform: isMiddle ? 'translateY(-4px)' : 'none',
}}
>
{/* Badge */}
{/* "Popular" badge — top right */}
{plan.badge && (
<div style={{
position: 'absolute',
top: -12,
right: '50%',
transform: 'translateX(50%)',
background: 'var(--red)',
color: 'white',
fontSize: 9,
fontWeight: 700,
letterSpacing: '2px',
textTransform: 'uppercase',
padding: '4px 14px',
whiteSpace: 'nowrap',
position: 'absolute',
top: 12,
right: 12,
background: 'var(--red)',
color: 'white',
padding: '5px 10px',
fontSize: 10,
fontWeight: 700,
letterSpacing: '1.5px',
display: 'flex',
alignItems: 'center',
gap: 5,
zIndex: 10,
}}>
<Star />
{plan.badge}
</div>
)}
{/* Plan name */}
{/* Header */}
<div style={{
fontSize: 11,
fontWeight: 700,
letterSpacing: '1.5px',
textTransform: 'uppercase',
color: featured ? 'rgba(244,239,231,0.5)' : 'var(--ink-5)',
marginBottom: 8,
background: featured ? 'rgba(255,255,255,0.04)' : 'var(--paper-2)',
borderBottom: featured ? '1px solid rgba(255,255,255,0.08)' : '1px solid var(--rule-thin)',
padding: '20px 22px',
}}>
{plan.name}
</div>
{/* Tagline */}
<div style={{
fontSize: 13,
color: featured ? 'rgba(244,239,231,0.55)' : 'var(--ink-4)',
marginBottom: 24,
fontWeight: 300,
}}>
{plan.id === 'individual' && 'برای کارشناسان و محققان'}
{plan.id === 'organizational' && 'برای شرکت‌ها و سازمان‌ها'}
{plan.id === 'ministry' && 'برای نهادهای دولتی'}
</div>
{/* Price */}
<div style={{ marginBottom: 6 }}>
<span style={{
fontSize: 42,
fontWeight: 900,
letterSpacing: '-2px',
lineHeight: 1,
color: featured ? 'var(--paper)' : 'var(--ink)',
fontVariantNumeric: 'tabular-nums',
<div style={{
fontSize: 15,
fontWeight: 800,
color: featured ? 'var(--paper)' : 'var(--ink)',
marginBottom: 4,
}}>
{plan.price === 0 ? 'تماس' : plan.price.toLocaleString('fa-IR')}
</span>
{plan.price > 0 && (
{plan.name}
</div>
<p style={{
fontSize: 12,
color: featured ? 'rgba(244,239,231,0.55)' : 'var(--ink-4)',
marginBottom: 14,
fontWeight: 400,
lineHeight: 1.5,
}}>
{TAGLINE[plan.id]}
</p>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
<span style={{
fontSize: 13,
fontWeight: 400,
color: featured ? 'rgba(244,239,231,0.5)' : 'var(--ink-4)',
marginRight: 6,
fontSize: 26,
fontWeight: 900,
letterSpacing: '-0.8px',
lineHeight: 1,
color: featured ? 'var(--paper)' : 'var(--ink)',
fontVariantNumeric: 'tabular-nums',
}}>
تومان
{plan.price === 0 ? 'تماس' : plan.price.toLocaleString('fa-IR')}
</span>
)}
{plan.price > 0 ? (
<span style={{
fontSize: 11,
color: featured ? 'rgba(244,239,231,0.55)' : 'var(--ink-4)',
fontWeight: 500,
}}>
تومان / {plan.period}
</span>
) : (
<span style={{
fontSize: 11,
color: featured ? 'rgba(244,239,231,0.55)' : 'var(--ink-4)',
fontWeight: 500,
}}>
· {plan.period}
</span>
)}
</div>
</div>
<div style={{
fontSize: 11,
color: featured ? 'rgba(244,239,231,0.4)' : 'var(--ink-5)',
marginBottom: 28,
}}>
{plan.period}
</div>
{/* CTA button */}
<button
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
style={{
width: '100%',
padding: '12px',
border: featured
? 'none'
: hovered ? 'none' : '1px solid var(--ink)',
background: featured
? hovered ? 'var(--paper-2)' : 'var(--paper)'
: hovered ? 'var(--ink)' : 'transparent',
color: featured
? 'var(--ink)'
: hovered ? 'var(--paper)' : 'var(--ink)',
fontSize: 12,
fontWeight: 700,
fontFamily: 'Vazir, sans-serif',
cursor: 'pointer',
borderRadius: 0,
marginBottom: 28,
letterSpacing: '0.5px',
transition: 'background 180ms, color 180ms, border 180ms',
}}
>
{plan.ctaLabel}
</button>
{/* Divider */}
<div style={{
borderTop: featured ? '1px solid rgba(244,239,231,0.12)' : '1px solid var(--rule-thin)',
marginBottom: 24,
}} />
{/* Features */}
<ul style={{ listStyle: 'none', display: 'flex', flexDirection: 'column', gap: 12 }}>
<div style={{
padding: '18px 22px',
display: 'flex',
flexDirection: 'column',
gap: 10,
flex: 1,
}}>
{plan.features.map(f => (
<li key={f} style={{ display: 'flex', alignItems: 'flex-start', gap: 10, textAlign: 'right' }}>
<div key={f} style={{ display: 'flex', alignItems: 'flex-start', gap: 9 }}>
<Check featured={featured} />
<span style={{
fontSize: 13,
lineHeight: 1.5,
color: featured ? 'rgba(244,239,231,0.75)' : 'var(--ink-3)',
fontWeight: 300,
fontSize: 12,
lineHeight: 1.55,
color: featured ? 'rgba(244,239,231,0.8)' : 'var(--ink-3)',
fontWeight: 400,
}}>
{f}
</span>
</li>
</div>
))}
</ul>
</div>
{/* CTA */}
<div style={{
borderTop: featured ? '1px solid rgba(255,255,255,0.08)' : '1px solid var(--rule-thin)',
padding: '12px 22px 14px',
}}>
<button
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
style={{
width: '100%',
padding: '10px',
border: featured
? 'none'
: hovered ? 'none' : '1px solid var(--ink)',
background: featured
? hovered ? 'var(--paper-2)' : 'var(--paper)'
: hovered ? 'var(--ink)' : 'transparent',
color: featured
? 'var(--ink)'
: hovered ? 'var(--paper)' : 'var(--ink)',
fontSize: 12,
fontWeight: 700,
fontFamily: 'Vazir, sans-serif',
cursor: 'pointer',
borderRadius: 0,
letterSpacing: '0.5px',
transition: 'background 180ms, color 180ms, border 180ms',
}}
>
{plan.ctaLabel}
</button>
</div>
</div>
)
}
/* ─── Enterprise bottom card ─────────────────────────── */
/* ─── Enterprise bottom card ─────────────────────── */
const enterpriseFeatures = [
'پروژه‌های نامحدود',
'پشتیبانی تیمی اختصاصی',
@ -190,34 +205,35 @@ const enterpriseFeatures = [
function EnterpriseCard() {
const [hov, setHov] = useState(false)
return (
<div style={{
border: '1px solid var(--rule-thin)',
background: 'white',
padding: '36px 40px',
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: '40px',
alignItems: 'start',
}}
className="max-md:grid-cols-1 max-md:gap-6"
<div
style={{
border: '1px solid var(--rule-thin)',
background: 'var(--paper)',
padding: '28px 32px',
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: '32px',
alignItems: 'start',
}}
className="max-md:grid-cols-1 max-md:gap-6 max-md:p-6"
>
{/* Left: description */}
<div>
<div style={{
fontSize: 11, fontWeight: 700, letterSpacing: '2px',
textTransform: 'uppercase', color: 'var(--ink-5)', marginBottom: 10,
fontSize: 10, fontWeight: 700, letterSpacing: '1.5px',
textTransform: 'uppercase', color: 'var(--ink-5)', marginBottom: 8,
}}>
وزارتخانه / سازمان دولتی
</div>
<h3 style={{
fontSize: 22, fontWeight: 900, letterSpacing: '-0.5px',
color: 'var(--ink)', marginBottom: 12,
fontSize: 18, fontWeight: 900, letterSpacing: '-0.4px',
color: 'var(--ink)', marginBottom: 10,
}}>
راهکار سازمانی کامل
</h3>
<p style={{
fontSize: 13, lineHeight: 1.8, color: 'var(--ink-3)',
fontWeight: 300, marginBottom: 24,
fontSize: 12, lineHeight: 1.75, color: 'var(--ink-3)',
fontWeight: 400, marginBottom: 20,
}}>
به راهکارهای سفارشی، پشتیبانی اختصاصی یا قیمتگذاری حجمی نیاز دارید؟
با کارشناسان ما درباره پلن منحصربهفرد سازمانتان صحبت کنید.
@ -226,15 +242,15 @@ function EnterpriseCard() {
onMouseEnter={() => setHov(true)}
onMouseLeave={() => setHov(false)}
style={{
padding: '13px 28px',
border: '1px solid var(--ink)',
background: hov ? 'var(--ink)' : 'transparent',
color: hov ? 'var(--paper)' : 'var(--ink)',
fontSize: 12, fontWeight: 700,
fontFamily: 'Vazir, sans-serif',
cursor: 'pointer',
borderRadius: 0,
transition: 'background 180ms, color 180ms',
padding: '11px 24px',
border: '1px solid var(--ink)',
background: hov ? 'var(--ink)' : 'transparent',
color: hov ? 'var(--paper)' : 'var(--ink)',
fontSize: 12, fontWeight: 700,
fontFamily: 'Vazir, sans-serif',
cursor: 'pointer',
borderRadius: 0,
transition: 'background 180ms, color 180ms',
letterSpacing: '0.5px',
}}
>
@ -246,12 +262,12 @@ function EnterpriseCard() {
<div style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: '10px 20px',
gap: '8px 18px',
}}>
{enterpriseFeatures.map(f => (
<div key={f} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<div key={f} style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
<Check featured={false} />
<span style={{ fontSize: 12, color: 'var(--ink-3)', lineHeight: 1.4, fontWeight: 300 }}>
<span style={{ fontSize: 11, color: 'var(--ink-3)', lineHeight: 1.4, fontWeight: 400 }}>
{f}
</span>
</div>
@ -261,39 +277,37 @@ function EnterpriseCard() {
)
}
/* ─── Section ────────────────────────────────────────── */
/* ─── Section ────────────────────────────────────── */
export default function PlansSection() {
const allThree = plans
return (
<section style={{
background: 'var(--paper-2)',
background: 'var(--paper)',
borderBottom: '2px solid var(--ink)',
}}>
<div className="px-12 py-20 max-md:px-5 max-md:py-14">
<div className="max-w-6xl mx-auto px-12 py-16 max-md:px-5 max-md:py-12">
{/* Header */}
<div style={{ textAlign: 'center', marginBottom: 56 }}>
<div style={{ textAlign: 'center', marginBottom: 40 }}>
<div style={{
fontSize: 9, fontWeight: 700, letterSpacing: '3px',
fontSize: 10, fontWeight: 700, letterSpacing: '2.5px',
textTransform: 'uppercase', color: 'var(--red)',
marginBottom: 14,
marginBottom: 12,
}}>
اشتراک و دسترسی
</div>
<h2 style={{
fontSize: 'clamp(26px, 3vw, 40px)',
fontSize: 'clamp(22px, 2.4vw, 32px)',
fontWeight: 900,
letterSpacing: '-1.5px',
letterSpacing: '-1px',
color: 'var(--ink)',
marginBottom: 12,
marginBottom: 10,
}}>
پلن مناسب خود را انتخاب کنید
</h2>
<p style={{
fontSize: 14, lineHeight: 1.8,
color: 'var(--ink-4)', fontWeight: 300,
maxWidth: 480, margin: '0 auto',
fontSize: 13, lineHeight: 1.7,
color: 'var(--ink-4)', fontWeight: 400,
maxWidth: 460, margin: '0 auto',
}}>
از دسترسی پایه برای پژوهشگران تا راهکار کامل برای وزارتخانهها
هر سطحی از نیاز را پوشش میدهیم.
@ -301,16 +315,17 @@ export default function PlansSection() {
</div>
{/* 3-col plan cards */}
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: 0,
marginBottom: 32,
position: 'relative',
}}
className="max-md:grid-cols-1 max-md:gap-0"
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: 16,
marginBottom: 20,
alignItems: 'stretch',
}}
className="max-md:grid-cols-1 max-md:gap-4"
>
{allThree.map((plan, idx) => (
{plans.map((plan, idx) => (
<PlanCard
key={plan.id}
plan={plan}
@ -325,10 +340,10 @@ export default function PlansSection() {
{/* Money-back note */}
<div style={{
textAlign: 'center',
marginTop: 20,
marginTop: 18,
fontSize: 11,
color: 'var(--ink-5)',
fontWeight: 300,
fontWeight: 400,
}}>
&nbsp; ضمانت بازگشت وجه ۱۴ روزه &nbsp;·&nbsp; بدون قرارداد اجباری
</div>

View File

@ -236,33 +236,9 @@ export default function ReportsCarouselSection() {
))
return (
<section style={{ borderBottom: '2px solid var(--ink)' }}>
{/* Header */}
<div className="px-12 max-md:px-5">
<div style={{
display: 'flex', alignItems: 'center',
borderBottom: '1px solid var(--rule-thin)', paddingTop: 52,
}}>
<div style={{
background: 'var(--ink)', color: 'var(--paper)',
padding: '8px 20px', fontSize: 9, fontWeight: 700,
letterSpacing: '3px', textTransform: 'uppercase',
}}>
تحلیلهای اخیر
</div>
<div style={{ flex: 1 }} />
<Link to="/reports" style={{
fontSize: 10, fontWeight: 700, letterSpacing: '1.5px',
textTransform: 'uppercase', color: 'var(--ink-4)',
textDecoration: 'none', padding: '8px 0',
}}>
همه گزارشها
</Link>
</div>
</div>
{/* Legend */}
<div className="px-12 pt-[14px] max-md:px-5">
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper-2)', paddingBottom: 40 }}>
{/* Legend & Link Row */}
<div className="max-w-7xl mx-auto px-12 pt-10 max-md:px-5 flex items-center justify-between flex-wrap gap-4">
<div style={{ display: 'flex', gap: 16, flexWrap: 'wrap' }}>
{Object.entries(typeLabel).map(([key, label]) => (
<div key={key} style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
@ -271,9 +247,18 @@ export default function ReportsCarouselSection() {
</div>
))}
</div>
<Link to="/reports" style={{
fontSize: 10, fontWeight: 700, letterSpacing: '1.5px',
textTransform: 'uppercase', color: 'var(--ink-4)',
textDecoration: 'none',
}}>
همه گزارشها
</Link>
</div>
<Carousel items={cardElements} />
<div className="max-w-7xl mx-auto w-full px-12 max-md:px-5 mt-6">
<Carousel items={cardElements} />
</div>
</section>
)
}

View File

@ -90,51 +90,10 @@ export default function RiskSection() {
}, [])
return (
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper-2)' }}>
<div className="px-12 pt-14 max-md:px-5 max-md:pt-10">
{/* ── Header bar ── */}
<div style={{
display: 'flex',
alignItems: 'center',
borderBottom: '1px solid var(--rule-thin)',
}}>
<div style={{
background: 'var(--red)',
color: 'white',
padding: '8px 20px',
fontSize: 9,
fontWeight: 700,
letterSpacing: '3px',
textTransform: 'uppercase',
}}>
پایش ریسک · لحظهای
</div>
<div style={{
marginRight: 16,
fontSize: 10,
color: 'var(--ink-5)',
fontWeight: 600,
letterSpacing: '0.5px',
}}>
{riskItems.length} سیگنال فعال
</div>
<div style={{ flex: 1 }} />
<Link to="/risks" style={{
fontSize: 10,
fontWeight: 700,
letterSpacing: '1.5px',
textTransform: 'uppercase',
color: 'var(--ink-4)',
textDecoration: 'none',
padding: '8px 0',
}}>
همه ریسکها
</Link>
</div>
{/* ── Legend ── */}
<div style={{ display: 'flex', gap: 20, flexWrap: 'wrap', padding: '14px 0 0' }}>
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper)' }}>
{/* ── Legend, active count, and link row ── */}
<div className="max-w-7xl mx-auto px-12 pt-8 pb-2 max-md:px-5 max-md:pt-6 flex justify-between items-center flex-wrap gap-4 text-xs">
<div style={{ display: 'flex', gap: 20, flexWrap: 'wrap' }}>
{[
{ color: '#7f1d1d', label: 'بحرانی' },
{ color: '#9b1c1c', label: 'بالا' },
@ -148,6 +107,19 @@ export default function RiskSection() {
</div>
))}
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<span style={{ color: 'var(--ink-5)', fontWeight: 600 }}>{riskItems.length} سیگنال فعال</span>
<Link to="/risks" style={{
fontSize: 10,
fontWeight: 700,
letterSpacing: '1.5px',
textTransform: 'uppercase',
color: 'var(--ink-4)',
textDecoration: 'none',
}}>
همه ریسکها
</Link>
</div>
</div>
{/* ── Auto-scroll ticker ── */}

View File

@ -13,11 +13,10 @@ export default function SnapshotBar() {
>
<div
style={{
padding: '0 48px',
display: 'grid',
gridTemplateColumns: 'repeat(4, 1fr)',
}}
className="max-md:grid-cols-2 max-md:px-6"
className="max-w-7xl mx-auto w-full px-12 max-md:px-6 max-md:grid-cols-2"
>
{prices.map((price, idx) => {
const isLast = idx === prices.length - 1

View File

@ -1,48 +1,215 @@
import { useState } from 'react'
import { Link } from 'react-router-dom'
import { team } from '@/data/team'
function MemberCard({ member, idx }: { member: (typeof team)[0]; idx: number }) {
const borderRight = idx % 2 === 0 ? '1px solid var(--rule-thin)' : 'none'
const borderTop = idx >= 2 ? '1px solid var(--rule-thin)' : 'none'
function MemberCard({ member }: { member: (typeof team)[0] }) {
const [hovered, setHovered] = useState(false)
return (
<div
style={{ padding: '40px', borderRight, borderTop, cursor: 'pointer', transition: 'background 200ms' }}
onMouseEnter={e => (e.currentTarget.style.background = 'var(--paper-2)')}
onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}
className="max-md:border-r-0 max-md:border-t max-md:border-[var(--rule-thin)] max-md:first:border-t-0 max-md:p-6"
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
style={{
background: hovered ? 'var(--paper-2)' : 'var(--paper)',
display: 'flex',
flexDirection: 'column',
transition: 'background 150ms ease',
position: 'relative',
}}
className="p-6 max-md:p-5 cursor-pointer border border-[var(--rule-thin)]"
>
<div style={{ width: 56, height: 56, background: 'var(--paper-3)', border: '1px solid var(--rule-thin)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 22, fontWeight: 900, color: 'var(--ink)', marginBottom: 16 }}>
{member.initial}
{/* Top row: avatar + name/role + reports stat */}
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
{/* Avatar */}
<div
style={{
width: 44,
height: 44,
background: 'var(--ink)',
color: 'var(--paper)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 16,
fontWeight: 900,
borderRadius: '50%',
flexShrink: 0,
}}
>
{member.initial}
</div>
{/* Name + role */}
<div style={{ flex: 1, minWidth: 0 }}>
<h3
style={{
fontSize: 14,
fontWeight: 800,
color: 'var(--ink)',
lineHeight: 1.3,
marginBottom: 2,
letterSpacing: '-0.2px',
}}
>
{member.name}
</h3>
<span
style={{
fontSize: 10,
fontWeight: 700,
color: 'var(--red)',
letterSpacing: '1px',
textTransform: 'uppercase',
}}
>
{member.role}
</span>
</div>
{/* Report count — top left (RTL → visual end) */}
<div style={{ textAlign: 'left', flexShrink: 0 }}>
<div
style={{
fontSize: 18,
fontWeight: 900,
color: 'var(--ink)',
lineHeight: 1,
fontVariantNumeric: 'tabular-nums',
}}
>
{member.reportCount.toLocaleString('fa-IR')}
</div>
<div
style={{
fontSize: 9,
color: 'var(--ink-5)',
marginTop: 3,
fontWeight: 500,
}}
>
گزارش
</div>
</div>
</div>
<div style={{ fontSize: 15, fontWeight: 800, color: 'var(--ink)', letterSpacing: '-0.3px', marginBottom: 3 }}>{member.name}</div>
<div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '1.5px', textTransform: 'uppercase', color: 'var(--red)', marginBottom: 12 }}>{member.role}</div>
<p style={{ fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.75, fontWeight: 400, marginBottom: 14 }}>{member.bio}</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 5, marginBottom: 14 }}>
{member.expertise.slice(0, 4).map(tag => (
<span key={tag} style={{ fontSize: 9, padding: '2px 7px', background: 'var(--paper-3)', color: 'var(--ink-4)', fontWeight: 600, letterSpacing: '0.5px' }}>{tag}</span>
{/* Bio */}
<p
style={{
fontSize: 12,
color: 'var(--ink-3)',
lineHeight: 1.7,
fontWeight: 400,
marginTop: 14,
marginBottom: 14,
display: '-webkit-box',
WebkitLineClamp: 3,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
}}
>
{member.bio}
</p>
{/* Tags — max 3 */}
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 5, marginBottom: 12 }}>
{member.expertise.slice(0, 3).map(tag => (
<span
key={tag}
style={{
fontSize: 10,
padding: '2px 8px',
color: 'var(--ink-4)',
border: '1px solid var(--rule-thin)',
fontWeight: 500,
whiteSpace: 'nowrap',
}}
>
{tag}
</span>
))}
</div>
<div style={{ paddingTop: 12, borderTop: '1px solid var(--rule-thin)', fontSize: 11, color: 'var(--ink-5)' }}>
<strong style={{ fontSize: 20, fontWeight: 900, color: 'var(--ink)', marginLeft: 4 }}>{member.reportCount}</strong>
گزارش منتشرشده
</div>
{/* Footer: email */}
{member.email && (
<div
style={{
paddingTop: 10,
borderTop: '1px solid var(--rule-thin)',
marginTop: 'auto',
}}
>
<a
href={`mailto:${member.email}`}
style={{
fontSize: 10,
color: 'var(--ink-5)',
textDecoration: 'none',
fontWeight: 500,
direction: 'ltr',
fontFamily: 'ui-monospace, monospace',
}}
>
{member.email}
</a>
</div>
)}
</div>
)
}
export default function TeamSection() {
const displayedMembers = team.slice(0, 4)
return (
<section style={{ borderBottom: '2px solid var(--ink)' }}>
<div className="px-12 py-20 max-md:px-5 max-md:py-14">
<div style={{ display: 'flex', alignItems: 'center', borderBottom: '1px solid var(--rule-thin)', marginBottom: '40px' }}>
<div style={{ background: 'var(--ink)', color: 'var(--paper)', padding: '8px 20px', fontSize: '9px', fontWeight: 700, letterSpacing: '3px', textTransform: 'uppercase' }}>تیم تحریریه</div>
<div style={{ flex: 1 }} />
<Link to="/team" style={{ fontSize: '10px', fontWeight: 700, letterSpacing: '1.5px', textTransform: 'uppercase', color: 'var(--ink-4)', textDecoration: 'none', padding: '8px 0' }}>همه کارشناسان </Link>
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper-2)' }}>
<div className="max-w-6xl mx-auto px-12 py-16 max-md:px-5 max-md:py-12">
{/* Editorial sub-bar */}
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
borderBottom: '1px solid var(--rule-thin)',
paddingBottom: 14,
marginBottom: 24,
}}
className="flex-wrap gap-3"
>
<span
style={{
fontSize: 12,
fontWeight: 400,
color: 'var(--ink-4)',
fontStyle: 'italic',
}}
>
اعضای هیئت تحریریه و متخصصان راهبردی
</span>
<Link
to="/team"
style={{
fontSize: 11,
fontWeight: 700,
letterSpacing: '1px',
color: 'var(--red)',
textDecoration: 'none',
}}
>
همه کارشناسان
</Link>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }} className="max-md:grid-cols-1">
{team.slice(0, 4).map((member, idx) => (
<MemberCard key={member.id} member={member} idx={idx} />
{/* 2x2 grid on desktop, single column on mobile */}
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(2, 1fr)',
gap: 12,
}}
className="max-md:grid-cols-1"
>
{displayedMembers.map(member => (
<MemberCard key={member.id} member={member} />
))}
</div>
</div>

View File

@ -161,21 +161,15 @@ function ComparisonTool() {
{/* Side-by-side comparison */}
<div
style={{
display: 'grid',
gridTemplateColumns: '1fr auto 1fr',
gap: 0,
border: '1px solid var(--rule-thin)',
}}
className="grid grid-cols-[1fr_auto_1fr] max-md:grid-cols-1 border border-[var(--rule-thin)]"
>
<div style={{ padding: 32, textAlign: 'center' }}>
<div className="max-md:border-b max-md:border-[var(--rule-thin)]" style={{ padding: 32, textAlign: 'center' }}>
<p style={{ fontSize: 13, color: 'var(--ink-3)', marginBottom: 8 }}>{itemA.name}</p>
<p
style={{
fontSize: 32,
fontWeight: 900,
color: 'var(--ink)',
letterSpacing: '-1px',
lineHeight: 1,
}}
>
@ -214,13 +208,9 @@ function ComparisonTool() {
{/* VS divider */}
<div
style={{
borderRight: '1px solid var(--rule-thin)',
borderLeft: '1px solid var(--rule-thin)',
width: 60,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
className="max-md:hidden border-r border-l border-[var(--rule-thin)] flex items-center justify-center"
>
<span
style={{
@ -242,7 +232,6 @@ function ComparisonTool() {
fontSize: 32,
fontWeight: 900,
color: 'var(--ink)',
letterSpacing: '-1px',
lineHeight: 1,
}}
>
@ -526,13 +515,7 @@ export default function Market() {
subtitle="قیمت بازار داخلی — تومان به کیلوگرم / دلار به تن"
/>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(4, 1fr)',
borderTop: '1px solid var(--rule-thin)',
borderRight: '1px solid var(--rule-thin)',
}}
className="price-grid"
className="grid grid-cols-4 max-lg:grid-cols-2 max-sm:grid-cols-1 border-t border-r border-[var(--rule-thin)] price-grid"
>
{marketPrices.map((item) => (
<PriceCard key={item.id} item={item} />

View File

@ -126,95 +126,97 @@ export default function Risks() {
ماتریس ریسکهای کلیدی
</h2>
<div style={{ border: '1px solid var(--ink)', overflow: 'hidden' }}>
{/* Table Header */}
<div
style={{
display: 'grid',
gridTemplateColumns: '48px 1fr 100px 100px 120px 130px',
background: 'var(--ink)',
color: 'var(--paper)',
padding: '12px 16px',
gap: '0',
}}
>
{['#', 'عنوان ریسک', 'دسته', 'احتمال', 'شدت تأثیر', 'سطح ریسک'].map((col, i) => (
<div
key={i}
style={{
fontSize: '12px',
fontWeight: 700,
letterSpacing: '0.05em',
padding: '0 8px',
borderRight: i > 0 ? '1px solid rgba(255,255,255,0.12)' : 'none',
}}
>
{col}
</div>
))}
</div>
{/* Table Rows */}
{riskMatrix.map((risk, idx) => {
const cfg = levelConfig[risk.level as RiskLevel];
return (
<div
key={risk.id}
style={{
display: 'grid',
gridTemplateColumns: '48px 1fr 100px 100px 120px 130px',
background: idx % 2 === 0 ? 'var(--paper)' : 'var(--paper-2)',
borderTop: '1px solid var(--rule-thin)',
padding: '14px 16px',
alignItems: 'center',
}}
>
{/* # */}
<div style={{ fontSize: '13px', color: 'var(--ink-5)', fontWeight: 600, padding: '0 8px' }}>
{risk.id}
</div>
{/* Title */}
<div style={{ fontSize: '14px', fontWeight: 600, color: 'var(--ink)', padding: '0 8px', borderRight: '1px solid var(--rule-thin)' }}>
{risk.title}
</div>
{/* Category */}
<div style={{ fontSize: '12px', color: 'var(--ink-4)', padding: '0 8px', borderRight: '1px solid var(--rule-thin)' }}>
{risk.category}
</div>
{/* Probability */}
<div style={{ fontSize: '12px', color: 'var(--ink-3)', padding: '0 8px', borderRight: '1px solid var(--rule-thin)' }}>
{risk.probability}
</div>
{/* Impact */}
<div style={{ fontSize: '12px', color: 'var(--ink-3)', padding: '0 8px', borderRight: '1px solid var(--rule-thin)' }}>
{risk.impact}
</div>
{/* Level */}
<div className="overflow-x-auto border border-[var(--ink)]">
<div style={{ minWidth: 768 }}>
{/* Table Header */}
<div
style={{
display: 'grid',
gridTemplateColumns: '48px 1fr 100px 100px 120px 130px',
background: 'var(--ink)',
color: 'var(--paper)',
padding: '12px 16px',
gap: '0',
}}
>
{['#', 'عنوان ریسک', 'دسته', 'احتمال', 'شدت تأثیر', 'سطح ریسک'].map((col, i) => (
<div
key={i}
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
fontSize: '12px',
fontWeight: 700,
letterSpacing: '0.05em',
padding: '0 8px',
borderRight: '1px solid var(--rule-thin)',
borderRight: i > 0 ? '1px solid rgba(255,255,255,0.12)' : 'none',
}}
>
{col}
</div>
))}
</div>
{/* Table Rows */}
{riskMatrix.map((risk, idx) => {
const cfg = levelConfig[risk.level as RiskLevel];
return (
<div
key={risk.id}
style={{
display: 'grid',
gridTemplateColumns: '48px 1fr 100px 100px 120px 130px',
background: idx % 2 === 0 ? 'var(--paper)' : 'var(--paper-2)',
borderTop: '1px solid var(--rule-thin)',
padding: '14px 16px',
alignItems: 'center',
}}
>
{/* # */}
<div style={{ fontSize: '13px', color: 'var(--ink-5)', fontWeight: 600, padding: '0 8px' }}>
{risk.id}
</div>
{/* Title */}
<div style={{ fontSize: '14px', fontWeight: 600, color: 'var(--ink)', padding: '0 8px', borderRight: '1px solid var(--rule-thin)' }}>
{risk.title}
</div>
{/* Category */}
<div style={{ fontSize: '12px', color: 'var(--ink-4)', padding: '0 8px', borderRight: '1px solid var(--rule-thin)' }}>
{risk.category}
</div>
{/* Probability */}
<div style={{ fontSize: '12px', color: 'var(--ink-3)', padding: '0 8px', borderRight: '1px solid var(--rule-thin)' }}>
{risk.probability}
</div>
{/* Impact */}
<div style={{ fontSize: '12px', color: 'var(--ink-3)', padding: '0 8px', borderRight: '1px solid var(--rule-thin)' }}>
{risk.impact}
</div>
{/* Level */}
<div
style={{
width: '9px',
height: '9px',
borderRadius: '50%',
background: cfg.color,
flexShrink: 0,
display: 'flex',
alignItems: 'center',
gap: '8px',
padding: '0 8px',
borderRight: '1px solid var(--rule-thin)',
}}
/>
<span style={{ fontSize: '12px', fontWeight: 700, color: cfg.color }}>
{cfg.label}
</span>
>
<div
style={{
width: '9px',
height: '9px',
borderRadius: '50%',
background: cfg.color,
flexShrink: 0,
}}
/>
<span style={{ fontSize: '12px', fontWeight: 700, color: cfg.color }}>
{cfg.label}
</span>
</div>
</div>
</div>
);
})}
);
})}
</div>
</div>
</div>
@ -244,12 +246,8 @@ export default function Risks() {
<Link
key={report.id}
to={`/reports/${report.id}`}
className="grid grid-cols-[90px_1fr_120px_140px_140px] max-md:grid-cols-1 max-md:p-5 max-md:gap-3 p-4 items-center"
style={{
display: 'grid',
gridTemplateColumns: '90px 1fr 120px 140px 140px',
gap: '0',
alignItems: 'center',
padding: '16px 0',
borderBottom: '1px solid var(--rule-thin)',
background: idx % 2 === 0 ? 'var(--paper)' : 'var(--paper-2)',
textDecoration: 'none',
@ -257,7 +255,7 @@ export default function Risks() {
}}
>
{/* Type badge */}
<div style={{ padding: '0 0 0 12px' }}>
<div style={{ padding: '0 0 0 12px' }} className="max-md:p-0">
<span
style={{
fontSize: '10px',
@ -265,7 +263,6 @@ export default function Risks() {
color: 'var(--paper)',
background: '#7f1d1d',
padding: '3px 8px',
letterSpacing: '0.06em',
}}
>
ریسک
@ -277,9 +274,8 @@ export default function Risks() {
fontSize: '14px',
fontWeight: 600,
color: 'var(--ink)',
borderRight: '1px solid var(--rule-thin)',
padding: '0 16px',
}}
className="border-r border-[var(--rule-thin)] px-4 max-md:border-r-0 max-md:p-0"
>
{report.title}
</div>
@ -288,9 +284,8 @@ export default function Risks() {
style={{
fontSize: '12px',
color: 'var(--ink-5)',
borderRight: '1px solid var(--rule-thin)',
padding: '0 16px',
}}
className="border-r border-[var(--rule-thin)] px-4 max-md:border-r-0 max-md:p-0"
>
{report.publishDate}
</div>
@ -299,9 +294,8 @@ export default function Risks() {
style={{
fontSize: '12px',
color: 'var(--ink-5)',
borderRight: '1px solid var(--rule-thin)',
padding: '0 16px',
}}
className="border-r border-[var(--rule-thin)] px-4 max-md:border-r-0 max-md:p-0"
>
{report.pages} صفحه
</div>
@ -312,9 +306,8 @@ export default function Risks() {
alignItems: 'center',
justifyContent: 'flex-end',
gap: '12px',
borderRight: '1px solid var(--rule-thin)',
padding: '0 16px',
}}
className="border-r border-[var(--rule-thin)] px-4 max-md:border-r-0 max-md:p-0 max-md:justify-start"
>
<span style={{ fontSize: '13px', fontWeight: 700, color: 'var(--ink)' }}>
{formatPrice(report.price)}

View File

@ -43,7 +43,7 @@ function formatNumber(n: number): string {
return n.toLocaleString('fa-IR');
}
function PriceTicker({ item, col, row, totalCols, totalRows }: { item: MarketPrice; col: number; row: number; totalCols: number; totalRows: number }) {
function PriceTicker({ item }: { item: MarketPrice }) {
const isUp = item.trend === 'up';
const isDown = item.trend === 'down';
const trendArrow = isUp ? '▲' : isDown ? '▼' : '—';
@ -54,8 +54,6 @@ function PriceTicker({ item, col, row, totalCols, totalRows }: { item: MarketPri
<div
style={{
padding: '24px 20px',
borderLeft: col < totalCols - 1 ? '1px solid var(--rule-thin)' : 'none',
borderBottom: row < totalRows - 1 ? '1px solid var(--rule-thin)' : 'none',
background: 'var(--paper)',
}}
>
@ -136,8 +134,6 @@ function PriceTicker({ item, col, row, totalCols, totalRows }: { item: MarketPri
export default function Scanner() {
// Use first 6 prices for the grid
const gridPrices = marketPrices.slice(0, 6);
const COLS = 3;
const ROWS = Math.ceil(gridPrices.length / COLS);
// For CSS bar chart: find max price in globalComparison for proportional width
// Filter to USD-only for a fair comparison (exclude IRR)
@ -237,25 +233,17 @@ export default function Scanner() {
<div
style={{
border: '2px solid var(--ink)',
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
background: 'var(--rule-thin)',
gap: 1,
}}
className="max-md:grid-cols-1"
className="grid grid-cols-3 max-lg:grid-cols-2 max-sm:grid-cols-1"
>
{gridPrices.map((item, idx) => {
const col = idx % COLS;
const row = Math.floor(idx / COLS);
return (
<PriceTicker
key={item.id}
item={item}
col={col}
row={row}
totalCols={COLS}
totalRows={ROWS}
/>
);
})}
{gridPrices.map((item) => (
<PriceTicker
key={item.id}
item={item}
/>
))}
</div>
</div>
@ -364,8 +352,7 @@ export default function Scanner() {
</div>
<div
style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px' }}
className="max-md:grid-cols-1"
className="grid grid-cols-3 max-lg:grid-cols-2 max-sm:grid-cols-1 gap-4"
>
{riskAlerts.map((alert) => {
const level = alert.level as AlertLevel;

View File

@ -330,8 +330,7 @@ export default function Special() {
</div>
<div
className="max-md:grid-cols-1"
style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '24px' }}
className="grid grid-cols-2 max-md:grid-cols-1 gap-6"
>
{specialReports.map((report) => (
<SpecialCard key={report.id} report={report} />
@ -369,8 +368,7 @@ export default function Special() {
</div>
<div
className="max-md:grid-cols-1"
style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '20px' }}
className="grid grid-cols-3 max-lg:grid-cols-2 max-md:grid-cols-1 gap-5"
>
{otherReports.map((report) => (
<CompactCard key={report.id} report={report} />