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