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:
parent
2dd08650cf
commit
8c5d2a9739
|
|
@ -89,7 +89,7 @@ export function Footer() {
|
||||||
style={{ backgroundColor: 'var(--ink)', color: 'rgba(255,255,255,0.6)' }}
|
style={{ backgroundColor: 'var(--ink)', color: 'rgba(255,255,255,0.6)' }}
|
||||||
>
|
>
|
||||||
{/* TOP GRID */}
|
{/* 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
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
|
|
@ -167,28 +167,34 @@ export function Footer() {
|
||||||
{/* BOTTOM ROW */}
|
{/* BOTTOM ROW */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
padding: '20px 48px',
|
|
||||||
borderTop: '1px solid rgba(255,255,255,0.08)',
|
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
|
||||||
<div style={{ display: 'flex', gap: 18 }}>
|
style={{
|
||||||
{['حریم خصوصی', 'شرایط استفاده', 'درگاه پرداخت', 'نقشه سایت'].map(l => (
|
padding: '20px 48px',
|
||||||
<a
|
display: 'flex',
|
||||||
key={l}
|
justifyContent: 'space-between',
|
||||||
href="#"
|
alignItems: 'center',
|
||||||
style={{ color: 'rgba(255,255,255,0.25)', textDecoration: 'none' }}
|
fontSize: 10,
|
||||||
>
|
color: 'rgba(255,255,255,0.25)',
|
||||||
{l}
|
flexWrap: 'wrap',
|
||||||
</a>
|
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>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -43,27 +43,14 @@ export function Carousel({
|
||||||
initialScroll?: number
|
initialScroll?: number
|
||||||
}) {
|
}) {
|
||||||
const carouselRef = useRef<HTMLDivElement>(null)
|
const carouselRef = useRef<HTMLDivElement>(null)
|
||||||
const [canScrollLeft, setCanScrollLeft] = useState(false)
|
const [currentIndex, setCurrentIndex] = useState(0)
|
||||||
const [canScrollRight, setCanScrollRight] = useState(true)
|
|
||||||
const [currentIndex, setCurrentIndex] = useState(0)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (carouselRef.current) {
|
if (carouselRef.current) {
|
||||||
carouselRef.current.scrollLeft = initialScroll
|
carouselRef.current.scrollLeft = initialScroll
|
||||||
checkScrollability()
|
|
||||||
}
|
}
|
||||||
}, [initialScroll])
|
}, [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) {
|
function handleCardClose(index: number) {
|
||||||
if (!carouselRef.current) return
|
if (!carouselRef.current) return
|
||||||
const cardWidth = window.innerWidth < 768 ? 230 : 350
|
const cardWidth = window.innerWidth < 768 ? 230 : 350
|
||||||
|
|
@ -77,7 +64,6 @@ export function Carousel({
|
||||||
{/* Scrollable track */}
|
{/* Scrollable track */}
|
||||||
<div
|
<div
|
||||||
ref={carouselRef}
|
ref={carouselRef}
|
||||||
onScroll={checkScrollability}
|
|
||||||
className="flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-10 md:py-14 [scrollbar-width:none]"
|
className="flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-10 md:py-14 [scrollbar-width:none]"
|
||||||
style={{ direction: 'ltr' }}
|
style={{ direction: 'ltr' }}
|
||||||
>
|
>
|
||||||
|
|
@ -96,27 +82,6 @@ export function Carousel({
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</CarouselContext.Provider>
|
</CarouselContext.Provider>
|
||||||
)
|
)
|
||||||
|
|
@ -183,19 +148,31 @@ export function Card({
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Hero image strip */}
|
{/* Hero image strip */}
|
||||||
<div className="relative h-52 overflow-hidden" style={{ flexShrink: 0 }}>
|
<div className="relative h-56 overflow-hidden" style={{ flexShrink: 0 }}>
|
||||||
<img
|
<img
|
||||||
src={card.src}
|
src={card.src}
|
||||||
alt={card.title}
|
alt={card.title}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent" />
|
{/* Stronger gradient — fully opaque at bottom for readable text */}
|
||||||
{/* inset-x-0 ensures text never overflows past modal edges */}
|
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/70 via-[40%] to-transparent" />
|
||||||
<div className="absolute bottom-0 inset-x-0 p-6">
|
|
||||||
<p className="text-white/70 text-xs font-bold tracking-widest uppercase mb-1">
|
{/* 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}
|
{card.category}
|
||||||
</p>
|
</span>
|
||||||
<p className="text-white text-lg font-bold leading-snug line-clamp-2">
|
</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}
|
{card.title}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -223,15 +200,25 @@ export function Card({
|
||||||
whileHover={{ scale: 1.015 }}
|
whileHover={{ scale: 1.015 }}
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
{/* Bottom-to-top gradient so text is readable — tall enough to cover 2 lines */}
|
{/* Strong bottom-to-top gradient — fully opaque at bottom so title pops */}
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/95 via-black/65 via-[35%] to-transparent z-20 pointer-events-none" />
|
<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 */}
|
{/* Top centered category badge — prominent, eye-catching */}
|
||||||
<div className="relative z-30 p-5 w-full text-right overflow-hidden">
|
<div className="absolute top-6 left-1/2 -translate-x-1/2 z-30">
|
||||||
<p className="text-white/65 text-[10px] font-bold tracking-widest uppercase mb-1.5 truncate">
|
<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}
|
{card.category}
|
||||||
</p>
|
</span>
|
||||||
<p className="text-white text-sm font-bold leading-snug line-clamp-2">
|
</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}
|
{card.title}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
119
src/index.css
119
src/index.css
|
|
@ -85,63 +85,69 @@
|
||||||
--fs-sm: clamp(11px, 0.75vw, 13px);
|
--fs-sm: clamp(11px, 0.75vw, 13px);
|
||||||
--fs-xs: clamp(9px, 0.6vw, 11px);
|
--fs-xs: clamp(9px, 0.6vw, 11px);
|
||||||
|
|
||||||
/* Letter spacing */
|
/* Letter spacing (Persian typography uses normal spacing to keep cursive script connected) */
|
||||||
--ls-display: -2.5px;
|
--ls-display: normal;
|
||||||
--ls-h1: -2px;
|
--ls-h1: normal;
|
||||||
--ls-h2: -1px;
|
--ls-h2: normal;
|
||||||
--ls-h3: -0.3px;
|
--ls-h3: normal;
|
||||||
--ls-body: 0px;
|
--ls-body: normal;
|
||||||
--ls-label: 1.5px;
|
--ls-label: normal;
|
||||||
--ls-overline: 2.5px;
|
--ls-overline: normal;
|
||||||
--ls-widest: 3px;
|
--ls-widest: normal;
|
||||||
|
|
||||||
/* Line heights */
|
/* Line heights (increased for Persian script to prevent accent collisions and overlap) */
|
||||||
--lh-display: 1.04;
|
--lh-display: 1.25;
|
||||||
--lh-heading: 1.12;
|
--lh-heading: 1.3;
|
||||||
--lh-subhead: 1.3;
|
--lh-subhead: 1.4;
|
||||||
--lh-body: 1.8;
|
--lh-body: 1.8;
|
||||||
--lh-relaxed: 1.85;
|
--lh-relaxed: 1.85;
|
||||||
--lh-label: 1.3;
|
--lh-label: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Base element styles ──────────────────────── */
|
/* ── Base element styles ────────────────────────
|
||||||
h1 {
|
Inside @layer base so Tailwind utilities (text-white, text-red, etc.)
|
||||||
font-size: var(--fs-h1);
|
in the @utilities layer can override these defaults. */
|
||||||
font-weight: 900;
|
@layer base {
|
||||||
line-height: var(--lh-heading);
|
h1 {
|
||||||
letter-spacing: var(--ls-h1);
|
font-size: var(--fs-h1);
|
||||||
color: var(--ink);
|
font-weight: 900;
|
||||||
margin: 0;
|
line-height: var(--lh-heading);
|
||||||
}
|
letter-spacing: normal;
|
||||||
h2 {
|
color: var(--ink);
|
||||||
font-size: var(--fs-h2);
|
margin: 0;
|
||||||
font-weight: 900;
|
}
|
||||||
line-height: var(--lh-heading);
|
h2 {
|
||||||
letter-spacing: var(--ls-h2);
|
font-size: var(--fs-h2);
|
||||||
color: var(--ink);
|
font-weight: 900;
|
||||||
margin: 0;
|
line-height: var(--lh-heading);
|
||||||
}
|
letter-spacing: normal;
|
||||||
h3 {
|
color: var(--ink);
|
||||||
font-size: var(--fs-h3);
|
margin: 0;
|
||||||
font-weight: 700;
|
}
|
||||||
line-height: var(--lh-subhead);
|
h3 {
|
||||||
letter-spacing: var(--ls-h3);
|
font-size: var(--fs-h3);
|
||||||
color: var(--ink);
|
font-weight: 700;
|
||||||
margin: 0;
|
line-height: var(--lh-subhead);
|
||||||
}
|
letter-spacing: normal;
|
||||||
h4 {
|
color: var(--ink);
|
||||||
font-size: var(--fs-h4);
|
margin: 0;
|
||||||
font-weight: 700;
|
}
|
||||||
line-height: var(--lh-subhead);
|
h4 {
|
||||||
color: var(--ink);
|
font-size: var(--fs-h4);
|
||||||
margin: 0;
|
font-weight: 700;
|
||||||
}
|
line-height: var(--lh-subhead);
|
||||||
p {
|
letter-spacing: normal;
|
||||||
font-size: var(--fs-body);
|
color: var(--ink);
|
||||||
font-weight: 400; /* was 300 — too thin on most screens */
|
margin: 0;
|
||||||
line-height: var(--lh-body);
|
}
|
||||||
color: var(--ink-3);
|
p {
|
||||||
margin: 0;
|
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; }
|
*, *::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; }
|
html { scroll-behavior: smooth; direction: rtl; }
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|
@ -157,6 +169,7 @@ body {
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
font-size: var(--fs-body);
|
font-size: var(--fs-body);
|
||||||
line-height: var(--lh-body);
|
line-height: var(--lh-body);
|
||||||
|
letter-spacing: normal;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +193,7 @@ body {
|
||||||
.t-overline {
|
.t-overline {
|
||||||
font-size: var(--fs-xs);
|
font-size: var(--fs-xs);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: var(--ls-overline);
|
letter-spacing: normal;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
line-height: var(--lh-label);
|
line-height: var(--lh-label);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,14 +55,10 @@ function ReportRow({ report }: { report: (typeof reports)[0] }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<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={{
|
style={{
|
||||||
display: 'grid',
|
|
||||||
gridTemplateColumns: '64px 1fr 140px 52px 80px',
|
|
||||||
alignItems: 'center',
|
|
||||||
borderBottom: '1px solid var(--rule-thin)',
|
|
||||||
background: hovered ? 'var(--paper-2)' : 'transparent',
|
background: hovered ? 'var(--paper-2)' : 'transparent',
|
||||||
transition: 'background 100ms',
|
transition: 'background 100ms',
|
||||||
minHeight: 40,
|
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() => setHovered(true)}
|
onMouseEnter={() => setHovered(true)}
|
||||||
onMouseLeave={() => setHovered(false)}
|
onMouseLeave={() => setHovered(false)}
|
||||||
|
|
@ -78,7 +74,6 @@ function ReportRow({ report }: { report: (typeof reports)[0] }) {
|
||||||
style={{
|
style={{
|
||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
letterSpacing: '0.8px',
|
|
||||||
color: typeColor,
|
color: typeColor,
|
||||||
border: `1px solid ${typeColor}`,
|
border: `1px solid ${typeColor}`,
|
||||||
padding: '2px 5px',
|
padding: '2px 5px',
|
||||||
|
|
@ -116,6 +111,7 @@ function ReportRow({ report }: { report: (typeof reports)[0] }) {
|
||||||
borderLeft: '1px solid var(--rule-thin)',
|
borderLeft: '1px solid var(--rule-thin)',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
|
className="max-md:hidden"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -139,6 +135,7 @@ function ReportRow({ report }: { report: (typeof reports)[0] }) {
|
||||||
borderLeft: '1px solid var(--rule-thin)',
|
borderLeft: '1px solid var(--rule-thin)',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
}}
|
}}
|
||||||
|
className="max-md:hidden"
|
||||||
>
|
>
|
||||||
<span style={{ fontSize: 11, color: 'var(--ink-5)', fontWeight: 400 }}>
|
<span style={{ fontSize: 11, color: 'var(--ink-5)', fontWeight: 400 }}>
|
||||||
{report.pages.toLocaleString('fa-IR')} ص
|
{report.pages.toLocaleString('fa-IR')} ص
|
||||||
|
|
@ -355,29 +352,20 @@ export default function Archive() {
|
||||||
|
|
||||||
{/* column headers */}
|
{/* column headers */}
|
||||||
<div
|
<div
|
||||||
style={{
|
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)]"
|
||||||
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)',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{[
|
{[
|
||||||
{ label: 'نوع', style: { padding: '6px 12px', borderLeft: '1px solid var(--rule-thin)' } },
|
{ 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 16px', borderLeft: '1px solid var(--rule-thin)' } },
|
||||||
{ label: 'نویسنده', style: { padding: '6px 12px', borderLeft: '1px solid var(--rule-thin)' } },
|
{ 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 } },
|
{ 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 } },
|
{ label: 'قیمت', style: { padding: '6px 12px', textAlign: 'left' as const } },
|
||||||
].map((col) => (
|
].map((col) => (
|
||||||
<div key={col.label} style={col.style}>
|
<div key={col.label} style={col.style} className={col.hideMobile ? "max-md:hidden" : ""}>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
letterSpacing: '1.2px',
|
|
||||||
color: 'var(--ink-4)',
|
color: 'var(--ink-4)',
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,123 @@
|
||||||
import HeroSection from './sections/HeroSection'
|
import HeroSection from './sections/HeroSection'
|
||||||
import SnapshotBar from './sections/SnapshotBar'
|
import SnapshotBar from './sections/SnapshotBar'
|
||||||
import GlobalScannerSection from './sections/GlobalScannerSection'
|
import GlobalScannerSection from './sections/GlobalScannerSection'
|
||||||
import ReportsCarouselSection from './sections/ReportsCarouselSection'
|
import ReportsCarouselSection from './sections/ReportsCarouselSection'
|
||||||
import LatestNewsBentoSection from './sections/LatestNewsBentoSection'
|
import LatestNewsBentoSection from './sections/LatestNewsBentoSection'
|
||||||
import EventsSection from './sections/EventsSection'
|
import EventsSection from './sections/EventsSection'
|
||||||
import RiskSection from './sections/RiskSection'
|
import RiskSection from './sections/RiskSection'
|
||||||
import PlansSection from './sections/PlansSection'
|
import TeamSection from './sections/TeamSection'
|
||||||
import TeamSection from './sections/TeamSection'
|
import PlansSection from './sections/PlansSection'
|
||||||
import NewsletterSection from './sections/NewsletterSection'
|
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() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
{/* ── 00 · Hero ─────────────────────────────────── */}
|
||||||
<HeroSection />
|
<HeroSection />
|
||||||
|
|
||||||
|
{/* ── Ticker ────────────────────────────────────── */}
|
||||||
<SnapshotBar />
|
<SnapshotBar />
|
||||||
|
|
||||||
|
{/* ── 01 · اسکنر جهانی · bg: paper ─────────────── */}
|
||||||
|
<SectionLabel n="01" title="اسکنر جهانی · رصد ۲۴ ساعته" />
|
||||||
<GlobalScannerSection />
|
<GlobalScannerSection />
|
||||||
|
|
||||||
|
{/* ── 02 · تحلیلها · bg: paper-2 ──────────────── */}
|
||||||
|
<SectionLabel n="02" title="تحلیلهای اخیر" />
|
||||||
<ReportsCarouselSection />
|
<ReportsCarouselSection />
|
||||||
|
|
||||||
|
{/* ── 03 · اخبار · bg: paper ────────────────────── */}
|
||||||
|
<SectionLabel n="03" title="آخرین اخبار بازار" />
|
||||||
<LatestNewsBentoSection />
|
<LatestNewsBentoSection />
|
||||||
|
|
||||||
|
{/* ── 04 · رویدادها · bg: paper-2 ──────────────── */}
|
||||||
|
<SectionLabel n="04" title="رویدادهای پیشرو" />
|
||||||
<EventsSection />
|
<EventsSection />
|
||||||
|
|
||||||
|
{/* ── 05 · ریسک · bg: paper ─────────────────────── */}
|
||||||
|
<SectionLabel n="05" title="پایش ریسک · لحظهای" />
|
||||||
<RiskSection />
|
<RiskSection />
|
||||||
|
|
||||||
|
{/* ── 06 · تیم · bg: paper-2 ────────────────────── */}
|
||||||
|
<SectionLabel n="06" title="تیم تحریریه" />
|
||||||
<TeamSection />
|
<TeamSection />
|
||||||
|
|
||||||
|
{/* ── 07 · اشتراک · bg: paper ───────────────────── */}
|
||||||
|
<SectionLabel n="07" title="اشتراک و دسترسی" />
|
||||||
<PlansSection />
|
<PlansSection />
|
||||||
|
|
||||||
|
{/* ── خبرنامه (last, no label) ───────────────────── */}
|
||||||
<NewsletterSection />
|
<NewsletterSection />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -27,32 +27,28 @@ export default function EventsSection() {
|
||||||
const displayed = events.slice(0, 4)
|
const displayed = events.slice(0, 4)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section style={{ borderBottom: '2px solid var(--ink)' }}>
|
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper-2)' }}>
|
||||||
<div className="px-12 py-20 max-md:px-5 max-md:py-14">
|
<div className="max-w-7xl mx-auto px-12 py-20 max-md:px-5 max-md:py-14">
|
||||||
{/* Banner */}
|
{/* Sleek top row */}
|
||||||
<div style={{ display: 'flex', alignItems: 'center', borderBottom: '1px solid var(--rule-thin)', marginBottom: 0 }}>
|
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 24 }}>
|
||||||
<div style={{ background: 'var(--ink)', color: 'var(--paper)', padding: '8px 20px', fontSize: '9px', fontWeight: 700, letterSpacing: '3px', textTransform: 'uppercase' }}>رویدادهای پیشرو</div>
|
<Link to="/events" style={{ fontSize: '10px', fontWeight: 700, textTransform: 'uppercase', color: 'var(--ink-4)', textDecoration: 'none' }}>تقویم کامل ←</Link>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 4-col grid */}
|
{/* 4-col grid */}
|
||||||
<div
|
<div
|
||||||
style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)' }}
|
className="grid grid-cols-4 max-md:grid-cols-2 max-sm:grid-cols-1"
|
||||||
className="max-md:grid-cols-2 max-sm:grid-cols-1"
|
|
||||||
>
|
>
|
||||||
{displayed.map((event, idx) => (
|
{displayed.map((event) => (
|
||||||
<div
|
<div
|
||||||
key={event.id}
|
key={event.id}
|
||||||
style={{
|
style={{
|
||||||
padding: '32px 28px',
|
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 */}
|
{/* Date block */}
|
||||||
<div style={{ borderBottom: '2px solid var(--ink)', paddingBottom: 12, marginBottom: 16 }}>
|
<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')}
|
{event.date.toLocaleString('fa-IR')}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 12, color: 'var(--ink-4)', marginTop: 2 }}>
|
<div style={{ fontSize: 12, color: 'var(--ink-4)', marginTop: 2 }}>
|
||||||
|
|
@ -70,7 +66,6 @@ export default function EventsSection() {
|
||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: '1px',
|
|
||||||
padding: '3px 8px',
|
padding: '3px 8px',
|
||||||
lineHeight: 1.4,
|
lineHeight: 1.4,
|
||||||
}}
|
}}
|
||||||
|
|
@ -87,11 +82,11 @@ export default function EventsSection() {
|
||||||
|
|
||||||
{/* CTA */}
|
{/* CTA */}
|
||||||
{event.registrationOpen ? (
|
{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>
|
</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>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -56,124 +56,100 @@ export default function GlobalScannerSection() {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* ── Text header ────────────────────────────────── */}
|
<div className="max-w-7xl mx-auto w-full px-12 max-md:px-5 pb-12">
|
||||||
<div
|
{/* ── Text header ────────────────────────────────── */}
|
||||||
style={{ textAlign: 'center' }}
|
|
||||||
className="px-12 pt-[72px] pb-12 max-md:px-5 max-md:pt-10 max-md:pb-8"
|
|
||||||
>
|
|
||||||
{/* Overline */}
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{ textAlign: 'center', paddingTop: 72, paddingBottom: 48 }}
|
||||||
display: 'inline-flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 10,
|
|
||||||
marginBottom: 20,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div style={{ width: 20, height: 2, background: 'var(--red)' }} />
|
{/* Headline */}
|
||||||
<span style={{
|
<h2 style={{
|
||||||
fontSize: 9,
|
fontSize: 'clamp(28px, 3.5vw, 52px)',
|
||||||
fontWeight: 700,
|
fontWeight: 900,
|
||||||
letterSpacing: '3px',
|
lineHeight: 1.1,
|
||||||
textTransform: 'uppercase',
|
letterSpacing: '-1.5px',
|
||||||
color: 'var(--red)',
|
color: 'var(--ink)',
|
||||||
|
marginBottom: 16,
|
||||||
}}>
|
}}>
|
||||||
اسکنر جهانی · پوشش ۲۴ ساعته
|
تمام رویدادهای فولادی دنیا{' '}
|
||||||
</span>
|
<motion.span
|
||||||
<div style={{ width: 20, height: 2, background: 'var(--red)' }} />
|
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>
|
</div>
|
||||||
|
|
||||||
{/* Headline */}
|
{/* ── World map (full width within container) ─── */}
|
||||||
<h2 style={{
|
<div className="max-md:px-0">
|
||||||
fontSize: 'clamp(28px, 3.5vw, 52px)',
|
<WorldMap
|
||||||
fontWeight: 900,
|
dots={ROUTES}
|
||||||
lineHeight: 1.1,
|
lineColor="#9b1c1c"
|
||||||
letterSpacing: '-1.5px',
|
dotColor="#c8c2bc"
|
||||||
color: 'var(--ink)',
|
/>
|
||||||
marginBottom: 16,
|
</div>
|
||||||
}}>
|
|
||||||
تمام رویدادهای فولادی دنیا{' '}
|
|
||||||
<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 */}
|
{/* ── Stats strip ──────────────────────────────── */}
|
||||||
<p style={{
|
<div
|
||||||
fontSize: 15,
|
style={{
|
||||||
lineHeight: 1.85,
|
borderTop: '1px solid var(--rule-thin)',
|
||||||
fontWeight: 400,
|
marginTop: 48,
|
||||||
color: 'var(--ink-3)',
|
}}
|
||||||
maxWidth: 600,
|
className="grid grid-cols-4 max-md:grid-cols-2 max-sm:grid-cols-1"
|
||||||
margin: '0 auto',
|
>
|
||||||
}}>
|
{[
|
||||||
از بورس دالیان چین تا گمرک بصره عراق، از تصمیمات کمیته فولاد OECD
|
{ n: '۱۸۰+', label: 'بازار رصدشده', sub: 'در ۵ قاره' },
|
||||||
تا ظرفیتسازیهای هند — هر سیگنالی که بر رقابتپذیری ایران اثر
|
{ n: '۷/۲۴', label: 'پایش لحظهای', sub: 'قیمت و حجم' },
|
||||||
میگذارد، پیش از بقیه به دست شما میرسد.
|
{ n: '۴۰+', label: 'منبع داده', sub: 'بورسها و گمرکها' },
|
||||||
</p>
|
{ n: '۱۵', label: 'کشور رقیب', sub: 'زیر ذرهبین' },
|
||||||
</div>
|
].map((s, i) => (
|
||||||
|
<motion.div
|
||||||
{/* ── World map (full width, no side padding) ─── */}
|
key={i}
|
||||||
<div className="px-6 max-md:px-0">
|
initial={{ opacity: 0, y: 12 }}
|
||||||
<WorldMap
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
dots={ROUTES}
|
viewport={{ once: true }}
|
||||||
lineColor="#9b1c1c"
|
transition={{ duration: 0.4, delay: i * 0.08 }}
|
||||||
dotColor="#c8c2bc"
|
style={{
|
||||||
/>
|
padding: '28px 24px',
|
||||||
</div>
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
{/* ── Stats strip ──────────────────────────────── */}
|
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={{
|
<div style={{
|
||||||
borderTop: '1px solid var(--rule-thin)',
|
fontSize: 32,
|
||||||
display: 'grid',
|
fontWeight: 900,
|
||||||
gridTemplateColumns: 'repeat(4, 1fr)',
|
color: 'var(--ink)',
|
||||||
}}
|
lineHeight: 1,
|
||||||
className="max-md:grid-cols-2"
|
marginBottom: 4,
|
||||||
>
|
}}>
|
||||||
{[
|
{s.n}
|
||||||
{ n: '۱۸۰+', label: 'بازار رصدشده', sub: 'در ۵ قاره' },
|
</div>
|
||||||
{ n: '۷/۲۴', label: 'پایش لحظهای', sub: 'قیمت و حجم' },
|
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-2)', marginBottom: 2 }}>
|
||||||
{ n: '۴۰+', label: 'منبع داده', sub: 'بورسها و گمرکها' },
|
{s.label}
|
||||||
{ n: '۱۵', label: 'کشور رقیب', sub: 'زیر ذرهبین' },
|
</div>
|
||||||
].map((s, i) => (
|
<div style={{ fontSize: 10, color: 'var(--ink-5)' }}>
|
||||||
<motion.div
|
{s.sub}
|
||||||
key={i}
|
</div>
|
||||||
initial={{ opacity: 0, y: 12 }}
|
</motion.div>
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
))}
|
||||||
viewport={{ once: true }}
|
</div>
|
||||||
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>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -82,233 +82,231 @@ export default function HeroSection() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Main content — anchored bottom ── */}
|
{/* ── Main content — anchored bottom ── */}
|
||||||
<div style={{
|
<div
|
||||||
position: 'absolute',
|
style={{
|
||||||
inset: 0,
|
position: 'absolute',
|
||||||
display: 'grid',
|
inset: 0,
|
||||||
gridTemplateColumns: '1fr 240px',
|
display: 'flex',
|
||||||
alignItems: 'flex-end',
|
alignItems: 'flex-end',
|
||||||
gap: 48,
|
}}
|
||||||
}}
|
|
||||||
className="px-14 pb-[52px] max-md:grid-cols-1 max-md:px-5 max-md:pb-8"
|
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
{/* ── Left: headline + meta ── */}
|
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>
|
|
||||||
{/* 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 style={{
|
|
||||||
fontSize: 9,
|
{/* ── Left: headline + meta ── */}
|
||||||
fontWeight: 700,
|
<div>
|
||||||
letterSpacing: '2px',
|
{/* Overline */}
|
||||||
textTransform: 'uppercase',
|
<motion.div
|
||||||
color: 'rgba(255,255,255,0.35)',
|
initial={{ opacity: 0, y: 10 }}
|
||||||
marginBottom: 16,
|
animate={{ opacity: 1, y: 0 }}
|
||||||
paddingBottom: 10,
|
transition={{ duration: 0.5 }}
|
||||||
borderBottom: '1px solid rgba(255,255,255,0.1)',
|
style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 20 }}
|
||||||
}}>
|
>
|
||||||
در این گزارش
|
<div style={{ width: 24, height: 2, background: 'var(--red)', flexShrink: 0 }} />
|
||||||
</div>
|
<span style={{
|
||||||
{STATS.map((s, i) => (
|
fontSize: 10,
|
||||||
<div
|
fontWeight: 700,
|
||||||
key={i}
|
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={{
|
style={{
|
||||||
display: 'flex',
|
fontSize: 'clamp(32px, 5vw, 64px)',
|
||||||
justifyContent: 'space-between',
|
fontWeight: 900,
|
||||||
alignItems: 'baseline',
|
lineHeight: 1.2,
|
||||||
padding: '10px 0',
|
color: 'white',
|
||||||
borderBottom: i < STATS.length - 1 ? '1px solid rgba(255,255,255,0.07)' : 'none',
|
marginBottom: 24,
|
||||||
|
maxWidth: 700,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ fontSize: 11, color: 'rgba(255,255,255,0.5)', fontWeight: 300 }}>
|
{featured.title.split(':').map((part, i) => (
|
||||||
{s.label}
|
<span key={i}>
|
||||||
</span>
|
{i === 0 ? part + ':' : (
|
||||||
<span style={{
|
<span style={{ color: 'rgba(255,255,255,0.45)' }}>{part}</span>
|
||||||
fontSize: 22,
|
)}
|
||||||
fontWeight: 900,
|
{i === 0 && <br />}
|
||||||
letterSpacing: '-0.5px',
|
</span>
|
||||||
color: 'white',
|
))}
|
||||||
lineHeight: 1,
|
</motion.h1>
|
||||||
}}>
|
|
||||||
{s.n}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</motion.div>
|
|
||||||
|
|
||||||
|
{/* 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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -51,27 +51,12 @@ function Accent({ color = 'var(--red)' }: { color?: string }) {
|
||||||
/* ─── Section ────────────────────────────────────────────── */
|
/* ─── Section ────────────────────────────────────────────── */
|
||||||
export default function LatestNewsBentoSection() {
|
export default function LatestNewsBentoSection() {
|
||||||
return (
|
return (
|
||||||
<section style={{ background: 'var(--paper-2)', borderBottom: '2px solid var(--ink)' }}>
|
<section style={{ background: 'var(--paper)', borderBottom: '2px solid var(--ink)' }}>
|
||||||
|
|
||||||
{/* ── Header ── */}
|
{/* ── Header ── */}
|
||||||
<div className="px-12 pt-16 max-md:px-5 max-md:pt-10">
|
<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)]">
|
||||||
<div style={{
|
<span>رصد روزانه بازار و صنعت</span>
|
||||||
display: 'flex', alignItems: 'center', gap: 16,
|
<span>بهمن ۱۴۰۳</span>
|
||||||
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>
|
</div>
|
||||||
|
|
||||||
{/* ── Bento grid ── */}
|
{/* ── Bento grid ── */}
|
||||||
|
|
@ -80,7 +65,7 @@ export default function LatestNewsBentoSection() {
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
transition={{ duration: 0.5 }}
|
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.
|
3-col grid on desktop, 1-col on mobile.
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ export default function NewsletterSection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Outer grid — strip inline padding, use Tailwind */}
|
||||||
<div
|
<div
|
||||||
style={{ display: 'grid', gridTemplateColumns: '1fr 1px 1fr', minHeight: 180 }}
|
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 */}
|
{/* Left: description */}
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,39 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { plans } from '@/data/plans'
|
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 }) {
|
function Check({ featured }: { featured: boolean }) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="16" height="16" viewBox="0 0 16 16" fill="none"
|
width="14" height="14" viewBox="0 0 16 16" fill="none"
|
||||||
style={{ flexShrink: 0, marginTop: 1 }}
|
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
|
<path
|
||||||
d="M5 8l2 2 4-4"
|
d="M3 8.5l3 3 7-7"
|
||||||
stroke={featured ? '#f4efe7' : 'var(--green-ink)'}
|
stroke={featured ? 'rgba(244,239,231,0.85)' : 'var(--ink)'}
|
||||||
strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"
|
strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</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 }) {
|
function PlanCard({ plan, isMiddle }: { plan: (typeof plans)[0]; isMiddle: boolean }) {
|
||||||
const [hovered, setHovered] = useState(false)
|
const [hovered, setHovered] = useState(false)
|
||||||
const featured = plan.featured
|
const featured = plan.featured
|
||||||
|
|
@ -30,148 +41,152 @@ function PlanCard({ plan, isMiddle }: { plan: (typeof plans)[0]; isMiddle: boole
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: featured ? 'var(--ink)' : 'white',
|
position: 'relative',
|
||||||
border: featured ? 'none' : '1px solid var(--rule-thin)',
|
background: featured ? 'var(--ink)' : 'var(--paper)',
|
||||||
borderTop: featured ? 'none' : '1px solid var(--rule-thin)',
|
border: '1px solid var(--rule-thin)',
|
||||||
padding: '32px 28px',
|
display: 'flex',
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
position: 'relative',
|
boxShadow: isMiddle ? '0 12px 40px rgba(26,23,18,0.14)' : 'none',
|
||||||
boxShadow: isMiddle ? '0 8px 40px rgba(26,23,18,0.12)' : 'none',
|
zIndex: isMiddle ? 1 : 0,
|
||||||
zIndex: isMiddle ? 1 : 0,
|
transform: isMiddle ? 'translateY(-4px)' : 'none',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Badge */}
|
{/* "Popular" badge — top right */}
|
||||||
{plan.badge && (
|
{plan.badge && (
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: -12,
|
top: 12,
|
||||||
right: '50%',
|
right: 12,
|
||||||
transform: 'translateX(50%)',
|
background: 'var(--red)',
|
||||||
background: 'var(--red)',
|
color: 'white',
|
||||||
color: 'white',
|
padding: '5px 10px',
|
||||||
fontSize: 9,
|
fontSize: 10,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
letterSpacing: '2px',
|
letterSpacing: '1.5px',
|
||||||
textTransform: 'uppercase',
|
display: 'flex',
|
||||||
padding: '4px 14px',
|
alignItems: 'center',
|
||||||
whiteSpace: 'nowrap',
|
gap: 5,
|
||||||
|
zIndex: 10,
|
||||||
}}>
|
}}>
|
||||||
|
<Star />
|
||||||
{plan.badge}
|
{plan.badge}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Plan name */}
|
{/* Header */}
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 11,
|
background: featured ? 'rgba(255,255,255,0.04)' : 'var(--paper-2)',
|
||||||
fontWeight: 700,
|
borderBottom: featured ? '1px solid rgba(255,255,255,0.08)' : '1px solid var(--rule-thin)',
|
||||||
letterSpacing: '1.5px',
|
padding: '20px 22px',
|
||||||
textTransform: 'uppercase',
|
|
||||||
color: featured ? 'rgba(244,239,231,0.5)' : 'var(--ink-5)',
|
|
||||||
marginBottom: 8,
|
|
||||||
}}>
|
}}>
|
||||||
{plan.name}
|
<div style={{
|
||||||
</div>
|
fontSize: 15,
|
||||||
|
fontWeight: 800,
|
||||||
{/* Tagline */}
|
color: featured ? 'var(--paper)' : 'var(--ink)',
|
||||||
<div style={{
|
marginBottom: 4,
|
||||||
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',
|
|
||||||
}}>
|
}}>
|
||||||
{plan.price === 0 ? 'تماس' : plan.price.toLocaleString('fa-IR')}
|
{plan.name}
|
||||||
</span>
|
</div>
|
||||||
{plan.price > 0 && (
|
<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={{
|
<span style={{
|
||||||
fontSize: 13,
|
fontSize: 26,
|
||||||
fontWeight: 400,
|
fontWeight: 900,
|
||||||
color: featured ? 'rgba(244,239,231,0.5)' : 'var(--ink-4)',
|
letterSpacing: '-0.8px',
|
||||||
marginRight: 6,
|
lineHeight: 1,
|
||||||
|
color: featured ? 'var(--paper)' : 'var(--ink)',
|
||||||
|
fontVariantNumeric: 'tabular-nums',
|
||||||
}}>
|
}}>
|
||||||
تومان
|
{plan.price === 0 ? 'تماس' : plan.price.toLocaleString('fa-IR')}
|
||||||
</span>
|
</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>
|
||||||
<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 */}
|
{/* 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 => (
|
{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} />
|
<Check featured={featured} />
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: 13,
|
fontSize: 12,
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.55,
|
||||||
color: featured ? 'rgba(244,239,231,0.75)' : 'var(--ink-3)',
|
color: featured ? 'rgba(244,239,231,0.8)' : 'var(--ink-3)',
|
||||||
fontWeight: 300,
|
fontWeight: 400,
|
||||||
}}>
|
}}>
|
||||||
{f}
|
{f}
|
||||||
</span>
|
</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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Enterprise bottom card ─────────────────────────── */
|
/* ─── Enterprise bottom card ─────────────────────── */
|
||||||
const enterpriseFeatures = [
|
const enterpriseFeatures = [
|
||||||
'پروژههای نامحدود',
|
'پروژههای نامحدود',
|
||||||
'پشتیبانی تیمی اختصاصی',
|
'پشتیبانی تیمی اختصاصی',
|
||||||
|
|
@ -190,34 +205,35 @@ const enterpriseFeatures = [
|
||||||
function EnterpriseCard() {
|
function EnterpriseCard() {
|
||||||
const [hov, setHov] = useState(false)
|
const [hov, setHov] = useState(false)
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div
|
||||||
border: '1px solid var(--rule-thin)',
|
style={{
|
||||||
background: 'white',
|
border: '1px solid var(--rule-thin)',
|
||||||
padding: '36px 40px',
|
background: 'var(--paper)',
|
||||||
display: 'grid',
|
padding: '28px 32px',
|
||||||
gridTemplateColumns: '1fr 1fr',
|
display: 'grid',
|
||||||
gap: '40px',
|
gridTemplateColumns: '1fr 1fr',
|
||||||
alignItems: 'start',
|
gap: '32px',
|
||||||
}}
|
alignItems: 'start',
|
||||||
className="max-md:grid-cols-1 max-md:gap-6"
|
}}
|
||||||
|
className="max-md:grid-cols-1 max-md:gap-6 max-md:p-6"
|
||||||
>
|
>
|
||||||
{/* Left: description */}
|
{/* Left: description */}
|
||||||
<div>
|
<div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 11, fontWeight: 700, letterSpacing: '2px',
|
fontSize: 10, fontWeight: 700, letterSpacing: '1.5px',
|
||||||
textTransform: 'uppercase', color: 'var(--ink-5)', marginBottom: 10,
|
textTransform: 'uppercase', color: 'var(--ink-5)', marginBottom: 8,
|
||||||
}}>
|
}}>
|
||||||
وزارتخانه / سازمان دولتی
|
وزارتخانه / سازمان دولتی
|
||||||
</div>
|
</div>
|
||||||
<h3 style={{
|
<h3 style={{
|
||||||
fontSize: 22, fontWeight: 900, letterSpacing: '-0.5px',
|
fontSize: 18, fontWeight: 900, letterSpacing: '-0.4px',
|
||||||
color: 'var(--ink)', marginBottom: 12,
|
color: 'var(--ink)', marginBottom: 10,
|
||||||
}}>
|
}}>
|
||||||
راهکار سازمانی کامل
|
راهکار سازمانی کامل
|
||||||
</h3>
|
</h3>
|
||||||
<p style={{
|
<p style={{
|
||||||
fontSize: 13, lineHeight: 1.8, color: 'var(--ink-3)',
|
fontSize: 12, lineHeight: 1.75, color: 'var(--ink-3)',
|
||||||
fontWeight: 300, marginBottom: 24,
|
fontWeight: 400, marginBottom: 20,
|
||||||
}}>
|
}}>
|
||||||
به راهکارهای سفارشی، پشتیبانی اختصاصی یا قیمتگذاری حجمی نیاز دارید؟
|
به راهکارهای سفارشی، پشتیبانی اختصاصی یا قیمتگذاری حجمی نیاز دارید؟
|
||||||
با کارشناسان ما درباره پلن منحصربهفرد سازمانتان صحبت کنید.
|
با کارشناسان ما درباره پلن منحصربهفرد سازمانتان صحبت کنید.
|
||||||
|
|
@ -226,15 +242,15 @@ function EnterpriseCard() {
|
||||||
onMouseEnter={() => setHov(true)}
|
onMouseEnter={() => setHov(true)}
|
||||||
onMouseLeave={() => setHov(false)}
|
onMouseLeave={() => setHov(false)}
|
||||||
style={{
|
style={{
|
||||||
padding: '13px 28px',
|
padding: '11px 24px',
|
||||||
border: '1px solid var(--ink)',
|
border: '1px solid var(--ink)',
|
||||||
background: hov ? 'var(--ink)' : 'transparent',
|
background: hov ? 'var(--ink)' : 'transparent',
|
||||||
color: hov ? 'var(--paper)' : 'var(--ink)',
|
color: hov ? 'var(--paper)' : 'var(--ink)',
|
||||||
fontSize: 12, fontWeight: 700,
|
fontSize: 12, fontWeight: 700,
|
||||||
fontFamily: 'Vazir, sans-serif',
|
fontFamily: 'Vazir, sans-serif',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
transition: 'background 180ms, color 180ms',
|
transition: 'background 180ms, color 180ms',
|
||||||
letterSpacing: '0.5px',
|
letterSpacing: '0.5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -246,12 +262,12 @@ function EnterpriseCard() {
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: '1fr 1fr',
|
gridTemplateColumns: '1fr 1fr',
|
||||||
gap: '10px 20px',
|
gap: '8px 18px',
|
||||||
}}>
|
}}>
|
||||||
{enterpriseFeatures.map(f => (
|
{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} />
|
<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}
|
{f}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -261,39 +277,37 @@ function EnterpriseCard() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Section ────────────────────────────────────────── */
|
/* ─── Section ────────────────────────────────────── */
|
||||||
export default function PlansSection() {
|
export default function PlansSection() {
|
||||||
const allThree = plans
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section style={{
|
<section style={{
|
||||||
background: 'var(--paper-2)',
|
background: 'var(--paper)',
|
||||||
borderBottom: '2px solid var(--ink)',
|
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 */}
|
{/* Header */}
|
||||||
<div style={{ textAlign: 'center', marginBottom: 56 }}>
|
<div style={{ textAlign: 'center', marginBottom: 40 }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 9, fontWeight: 700, letterSpacing: '3px',
|
fontSize: 10, fontWeight: 700, letterSpacing: '2.5px',
|
||||||
textTransform: 'uppercase', color: 'var(--red)',
|
textTransform: 'uppercase', color: 'var(--red)',
|
||||||
marginBottom: 14,
|
marginBottom: 12,
|
||||||
}}>
|
}}>
|
||||||
اشتراک و دسترسی
|
اشتراک و دسترسی
|
||||||
</div>
|
</div>
|
||||||
<h2 style={{
|
<h2 style={{
|
||||||
fontSize: 'clamp(26px, 3vw, 40px)',
|
fontSize: 'clamp(22px, 2.4vw, 32px)',
|
||||||
fontWeight: 900,
|
fontWeight: 900,
|
||||||
letterSpacing: '-1.5px',
|
letterSpacing: '-1px',
|
||||||
color: 'var(--ink)',
|
color: 'var(--ink)',
|
||||||
marginBottom: 12,
|
marginBottom: 10,
|
||||||
}}>
|
}}>
|
||||||
پلن مناسب خود را انتخاب کنید
|
پلن مناسب خود را انتخاب کنید
|
||||||
</h2>
|
</h2>
|
||||||
<p style={{
|
<p style={{
|
||||||
fontSize: 14, lineHeight: 1.8,
|
fontSize: 13, lineHeight: 1.7,
|
||||||
color: 'var(--ink-4)', fontWeight: 300,
|
color: 'var(--ink-4)', fontWeight: 400,
|
||||||
maxWidth: 480, margin: '0 auto',
|
maxWidth: 460, margin: '0 auto',
|
||||||
}}>
|
}}>
|
||||||
از دسترسی پایه برای پژوهشگران تا راهکار کامل برای وزارتخانهها —
|
از دسترسی پایه برای پژوهشگران تا راهکار کامل برای وزارتخانهها —
|
||||||
هر سطحی از نیاز را پوشش میدهیم.
|
هر سطحی از نیاز را پوشش میدهیم.
|
||||||
|
|
@ -301,16 +315,17 @@ export default function PlansSection() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 3-col plan cards */}
|
{/* 3-col plan cards */}
|
||||||
<div style={{
|
<div
|
||||||
display: 'grid',
|
style={{
|
||||||
gridTemplateColumns: 'repeat(3, 1fr)',
|
display: 'grid',
|
||||||
gap: 0,
|
gridTemplateColumns: 'repeat(3, 1fr)',
|
||||||
marginBottom: 32,
|
gap: 16,
|
||||||
position: 'relative',
|
marginBottom: 20,
|
||||||
}}
|
alignItems: 'stretch',
|
||||||
className="max-md:grid-cols-1 max-md:gap-0"
|
}}
|
||||||
|
className="max-md:grid-cols-1 max-md:gap-4"
|
||||||
>
|
>
|
||||||
{allThree.map((plan, idx) => (
|
{plans.map((plan, idx) => (
|
||||||
<PlanCard
|
<PlanCard
|
||||||
key={plan.id}
|
key={plan.id}
|
||||||
plan={plan}
|
plan={plan}
|
||||||
|
|
@ -325,10 +340,10 @@ export default function PlansSection() {
|
||||||
{/* Money-back note */}
|
{/* Money-back note */}
|
||||||
<div style={{
|
<div style={{
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
marginTop: 20,
|
marginTop: 18,
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: 'var(--ink-5)',
|
color: 'var(--ink-5)',
|
||||||
fontWeight: 300,
|
fontWeight: 400,
|
||||||
}}>
|
}}>
|
||||||
✓ ضمانت بازگشت وجه ۱۴ روزه · بدون قرارداد اجباری
|
✓ ضمانت بازگشت وجه ۱۴ روزه · بدون قرارداد اجباری
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -236,33 +236,9 @@ export default function ReportsCarouselSection() {
|
||||||
))
|
))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section style={{ borderBottom: '2px solid var(--ink)' }}>
|
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper-2)', paddingBottom: 40 }}>
|
||||||
{/* Header */}
|
{/* Legend & Link Row */}
|
||||||
<div className="px-12 max-md:px-5">
|
<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', 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">
|
|
||||||
<div style={{ display: 'flex', gap: 16, flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', gap: 16, flexWrap: 'wrap' }}>
|
||||||
{Object.entries(typeLabel).map(([key, label]) => (
|
{Object.entries(typeLabel).map(([key, label]) => (
|
||||||
<div key={key} style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
|
<div key={key} style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
|
||||||
|
|
@ -271,9 +247,18 @@ export default function ReportsCarouselSection() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<Link to="/reports" style={{
|
||||||
|
fontSize: 10, fontWeight: 700, letterSpacing: '1.5px',
|
||||||
|
textTransform: 'uppercase', color: 'var(--ink-4)',
|
||||||
|
textDecoration: 'none',
|
||||||
|
}}>
|
||||||
|
همه گزارشها ←
|
||||||
|
</Link>
|
||||||
</div>
|
</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>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,51 +90,10 @@ export default function RiskSection() {
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper-2)' }}>
|
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper)' }}>
|
||||||
<div className="px-12 pt-14 max-md:px-5 max-md:pt-10">
|
{/* ── 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">
|
||||||
{/* ── Header bar ── */}
|
<div style={{ display: 'flex', gap: 20, flexWrap: 'wrap' }}>
|
||||||
<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' }}>
|
|
||||||
{[
|
{[
|
||||||
{ color: '#7f1d1d', label: 'بحرانی' },
|
{ color: '#7f1d1d', label: 'بحرانی' },
|
||||||
{ color: '#9b1c1c', label: 'بالا' },
|
{ color: '#9b1c1c', label: 'بالا' },
|
||||||
|
|
@ -148,6 +107,19 @@ export default function RiskSection() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</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>
|
</div>
|
||||||
|
|
||||||
{/* ── Auto-scroll ticker ── */}
|
{/* ── Auto-scroll ticker ── */}
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,10 @@ export default function SnapshotBar() {
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
padding: '0 48px',
|
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: 'repeat(4, 1fr)',
|
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) => {
|
{prices.map((price, idx) => {
|
||||||
const isLast = idx === prices.length - 1
|
const isLast = idx === prices.length - 1
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,215 @@
|
||||||
|
import { useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { team } from '@/data/team'
|
import { team } from '@/data/team'
|
||||||
|
|
||||||
function MemberCard({ member, idx }: { member: (typeof team)[0]; idx: number }) {
|
function MemberCard({ member }: { member: (typeof team)[0] }) {
|
||||||
const borderRight = idx % 2 === 0 ? '1px solid var(--rule-thin)' : 'none'
|
const [hovered, setHovered] = useState(false)
|
||||||
const borderTop = idx >= 2 ? '1px solid var(--rule-thin)' : 'none'
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{ padding: '40px', borderRight, borderTop, cursor: 'pointer', transition: 'background 200ms' }}
|
onMouseEnter={() => setHovered(true)}
|
||||||
onMouseEnter={e => (e.currentTarget.style.background = 'var(--paper-2)')}
|
onMouseLeave={() => setHovered(false)}
|
||||||
onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}
|
style={{
|
||||||
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"
|
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 }}>
|
{/* Top row: avatar + name/role + reports stat */}
|
||||||
{member.initial}
|
<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>
|
||||||
<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>
|
{/* Bio */}
|
||||||
<p style={{ fontSize: 13, color: 'var(--ink-3)', lineHeight: 1.75, fontWeight: 400, marginBottom: 14 }}>{member.bio}</p>
|
<p
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 5, marginBottom: 14 }}>
|
style={{
|
||||||
{member.expertise.slice(0, 4).map(tag => (
|
fontSize: 12,
|
||||||
<span key={tag} style={{ fontSize: 9, padding: '2px 7px', background: 'var(--paper-3)', color: 'var(--ink-4)', fontWeight: 600, letterSpacing: '0.5px' }}>{tag}</span>
|
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>
|
||||||
<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>
|
{/* Footer: email */}
|
||||||
گزارش منتشرشده
|
{member.email && (
|
||||||
</div>
|
<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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TeamSection() {
|
export default function TeamSection() {
|
||||||
|
const displayedMembers = team.slice(0, 4)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section style={{ borderBottom: '2px solid var(--ink)' }}>
|
<section style={{ borderBottom: '2px solid var(--ink)', background: 'var(--paper-2)' }}>
|
||||||
<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">
|
||||||
<div style={{ display: 'flex', alignItems: 'center', borderBottom: '1px solid var(--rule-thin)', marginBottom: '40px' }}>
|
{/* Editorial sub-bar */}
|
||||||
<div style={{ background: 'var(--ink)', color: 'var(--paper)', padding: '8px 20px', fontSize: '9px', fontWeight: 700, letterSpacing: '3px', textTransform: 'uppercase' }}>تیم تحریریه</div>
|
<div
|
||||||
<div style={{ flex: 1 }} />
|
style={{
|
||||||
<Link to="/team" style={{ fontSize: '10px', fontWeight: 700, letterSpacing: '1.5px', textTransform: 'uppercase', color: 'var(--ink-4)', textDecoration: 'none', padding: '8px 0' }}>همه کارشناسان ←</Link>
|
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>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }} className="max-md:grid-cols-1">
|
|
||||||
{team.slice(0, 4).map((member, idx) => (
|
{/* 2x2 grid on desktop, single column on mobile */}
|
||||||
<MemberCard key={member.id} member={member} idx={idx} />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -161,21 +161,15 @@ function ComparisonTool() {
|
||||||
|
|
||||||
{/* Side-by-side comparison */}
|
{/* Side-by-side comparison */}
|
||||||
<div
|
<div
|
||||||
style={{
|
className="grid grid-cols-[1fr_auto_1fr] max-md:grid-cols-1 border border-[var(--rule-thin)]"
|
||||||
display: 'grid',
|
|
||||||
gridTemplateColumns: '1fr auto 1fr',
|
|
||||||
gap: 0,
|
|
||||||
border: '1px solid 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: 13, color: 'var(--ink-3)', marginBottom: 8 }}>{itemA.name}</p>
|
||||||
<p
|
<p
|
||||||
style={{
|
style={{
|
||||||
fontSize: 32,
|
fontSize: 32,
|
||||||
fontWeight: 900,
|
fontWeight: 900,
|
||||||
color: 'var(--ink)',
|
color: 'var(--ink)',
|
||||||
letterSpacing: '-1px',
|
|
||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -214,13 +208,9 @@ function ComparisonTool() {
|
||||||
{/* VS divider */}
|
{/* VS divider */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
borderRight: '1px solid var(--rule-thin)',
|
|
||||||
borderLeft: '1px solid var(--rule-thin)',
|
|
||||||
width: 60,
|
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
|
<span
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -242,7 +232,6 @@ function ComparisonTool() {
|
||||||
fontSize: 32,
|
fontSize: 32,
|
||||||
fontWeight: 900,
|
fontWeight: 900,
|
||||||
color: 'var(--ink)',
|
color: 'var(--ink)',
|
||||||
letterSpacing: '-1px',
|
|
||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -526,13 +515,7 @@ export default function Market() {
|
||||||
subtitle="قیمت بازار داخلی — تومان به کیلوگرم / دلار به تن"
|
subtitle="قیمت بازار داخلی — تومان به کیلوگرم / دلار به تن"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
style={{
|
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"
|
||||||
display: 'grid',
|
|
||||||
gridTemplateColumns: 'repeat(4, 1fr)',
|
|
||||||
borderTop: '1px solid var(--rule-thin)',
|
|
||||||
borderRight: '1px solid var(--rule-thin)',
|
|
||||||
}}
|
|
||||||
className="price-grid"
|
|
||||||
>
|
>
|
||||||
{marketPrices.map((item) => (
|
{marketPrices.map((item) => (
|
||||||
<PriceCard key={item.id} item={item} />
|
<PriceCard key={item.id} item={item} />
|
||||||
|
|
|
||||||
|
|
@ -126,95 +126,97 @@ export default function Risks() {
|
||||||
ماتریس ریسکهای کلیدی
|
ماتریس ریسکهای کلیدی
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div style={{ border: '1px solid var(--ink)', overflow: 'hidden' }}>
|
<div className="overflow-x-auto border border-[var(--ink)]">
|
||||||
{/* Table Header */}
|
<div style={{ minWidth: 768 }}>
|
||||||
<div
|
{/* Table Header */}
|
||||||
style={{
|
<div
|
||||||
display: 'grid',
|
style={{
|
||||||
gridTemplateColumns: '48px 1fr 100px 100px 120px 130px',
|
display: 'grid',
|
||||||
background: 'var(--ink)',
|
gridTemplateColumns: '48px 1fr 100px 100px 120px 130px',
|
||||||
color: 'var(--paper)',
|
background: 'var(--ink)',
|
||||||
padding: '12px 16px',
|
color: 'var(--paper)',
|
||||||
gap: '0',
|
padding: '12px 16px',
|
||||||
}}
|
gap: '0',
|
||||||
>
|
}}
|
||||||
{['#', 'عنوان ریسک', 'دسته', 'احتمال', 'شدت تأثیر', 'سطح ریسک'].map((col, i) => (
|
>
|
||||||
<div
|
{['#', 'عنوان ریسک', 'دسته', 'احتمال', 'شدت تأثیر', 'سطح ریسک'].map((col, i) => (
|
||||||
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
|
<div
|
||||||
|
key={i}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
fontSize: '12px',
|
||||||
alignItems: 'center',
|
fontWeight: 700,
|
||||||
gap: '8px',
|
letterSpacing: '0.05em',
|
||||||
padding: '0 8px',
|
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
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: '9px',
|
display: 'flex',
|
||||||
height: '9px',
|
alignItems: 'center',
|
||||||
borderRadius: '50%',
|
gap: '8px',
|
||||||
background: cfg.color,
|
padding: '0 8px',
|
||||||
flexShrink: 0,
|
borderRight: '1px solid var(--rule-thin)',
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
<span style={{ fontSize: '12px', fontWeight: 700, color: cfg.color }}>
|
<div
|
||||||
{cfg.label}
|
style={{
|
||||||
</span>
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -244,12 +246,8 @@ export default function Risks() {
|
||||||
<Link
|
<Link
|
||||||
key={report.id}
|
key={report.id}
|
||||||
to={`/reports/${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={{
|
style={{
|
||||||
display: 'grid',
|
|
||||||
gridTemplateColumns: '90px 1fr 120px 140px 140px',
|
|
||||||
gap: '0',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: '16px 0',
|
|
||||||
borderBottom: '1px solid var(--rule-thin)',
|
borderBottom: '1px solid var(--rule-thin)',
|
||||||
background: idx % 2 === 0 ? 'var(--paper)' : 'var(--paper-2)',
|
background: idx % 2 === 0 ? 'var(--paper)' : 'var(--paper-2)',
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
|
|
@ -257,7 +255,7 @@ export default function Risks() {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Type badge */}
|
{/* Type badge */}
|
||||||
<div style={{ padding: '0 0 0 12px' }}>
|
<div style={{ padding: '0 0 0 12px' }} className="max-md:p-0">
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: '10px',
|
fontSize: '10px',
|
||||||
|
|
@ -265,7 +263,6 @@ export default function Risks() {
|
||||||
color: 'var(--paper)',
|
color: 'var(--paper)',
|
||||||
background: '#7f1d1d',
|
background: '#7f1d1d',
|
||||||
padding: '3px 8px',
|
padding: '3px 8px',
|
||||||
letterSpacing: '0.06em',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
ریسک
|
ریسک
|
||||||
|
|
@ -277,9 +274,8 @@ export default function Risks() {
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
color: 'var(--ink)',
|
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}
|
{report.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -288,9 +284,8 @@ export default function Risks() {
|
||||||
style={{
|
style={{
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
color: 'var(--ink-5)',
|
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}
|
{report.publishDate}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -299,9 +294,8 @@ export default function Risks() {
|
||||||
style={{
|
style={{
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
color: 'var(--ink-5)',
|
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} صفحه
|
{report.pages} صفحه
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -312,9 +306,8 @@ export default function Risks() {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
gap: '12px',
|
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)' }}>
|
<span style={{ fontSize: '13px', fontWeight: 700, color: 'var(--ink)' }}>
|
||||||
{formatPrice(report.price)}
|
{formatPrice(report.price)}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ function formatNumber(n: number): string {
|
||||||
return n.toLocaleString('fa-IR');
|
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 isUp = item.trend === 'up';
|
||||||
const isDown = item.trend === 'down';
|
const isDown = item.trend === 'down';
|
||||||
const trendArrow = isUp ? '▲' : isDown ? '▼' : '—';
|
const trendArrow = isUp ? '▲' : isDown ? '▼' : '—';
|
||||||
|
|
@ -54,8 +54,6 @@ function PriceTicker({ item, col, row, totalCols, totalRows }: { item: MarketPri
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
padding: '24px 20px',
|
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)',
|
background: 'var(--paper)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -136,8 +134,6 @@ function PriceTicker({ item, col, row, totalCols, totalRows }: { item: MarketPri
|
||||||
export default function Scanner() {
|
export default function Scanner() {
|
||||||
// Use first 6 prices for the grid
|
// Use first 6 prices for the grid
|
||||||
const gridPrices = marketPrices.slice(0, 6);
|
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
|
// For CSS bar chart: find max price in globalComparison for proportional width
|
||||||
// Filter to USD-only for a fair comparison (exclude IRR)
|
// Filter to USD-only for a fair comparison (exclude IRR)
|
||||||
|
|
@ -237,25 +233,17 @@ export default function Scanner() {
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
border: '2px solid var(--ink)',
|
border: '2px solid var(--ink)',
|
||||||
display: 'grid',
|
background: 'var(--rule-thin)',
|
||||||
gridTemplateColumns: 'repeat(3, 1fr)',
|
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) => {
|
{gridPrices.map((item) => (
|
||||||
const col = idx % COLS;
|
<PriceTicker
|
||||||
const row = Math.floor(idx / COLS);
|
key={item.id}
|
||||||
return (
|
item={item}
|
||||||
<PriceTicker
|
/>
|
||||||
key={item.id}
|
))}
|
||||||
item={item}
|
|
||||||
col={col}
|
|
||||||
row={row}
|
|
||||||
totalCols={COLS}
|
|
||||||
totalRows={ROWS}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -364,8 +352,7 @@ export default function Scanner() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px' }}
|
className="grid grid-cols-3 max-lg:grid-cols-2 max-sm:grid-cols-1 gap-4"
|
||||||
className="max-md:grid-cols-1"
|
|
||||||
>
|
>
|
||||||
{riskAlerts.map((alert) => {
|
{riskAlerts.map((alert) => {
|
||||||
const level = alert.level as AlertLevel;
|
const level = alert.level as AlertLevel;
|
||||||
|
|
|
||||||
|
|
@ -330,8 +330,7 @@ export default function Special() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="max-md:grid-cols-1"
|
className="grid grid-cols-2 max-md:grid-cols-1 gap-6"
|
||||||
style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '24px' }}
|
|
||||||
>
|
>
|
||||||
{specialReports.map((report) => (
|
{specialReports.map((report) => (
|
||||||
<SpecialCard key={report.id} report={report} />
|
<SpecialCard key={report.id} report={report} />
|
||||||
|
|
@ -369,8 +368,7 @@ export default function Special() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="max-md:grid-cols-1"
|
className="grid grid-cols-3 max-lg:grid-cols-2 max-md:grid-cols-1 gap-5"
|
||||||
style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '20px' }}
|
|
||||||
>
|
>
|
||||||
{otherReports.map((report) => (
|
{otherReports.map((report) => (
|
||||||
<CompactCard key={report.id} report={report} />
|
<CompactCard key={report.id} report={report} />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue