Files
Domain_web/web/home-app.jsx
Андрей Бобырев 311e501561 feat: lightweight static PCA Lab site with domain groups
Replace Next.js scanner deploy with static React CDN pages: student
VPN cabinet, regional setup, curated blocked-domain lists, and nginx
one-line install on port 80 without touching Amnezia/Docker services.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 03:34:31 +03:00

469 lines
26 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// PCA Lab — cover site: AI + cybersecurity research/education
const { useState: useH, useEffect: useEH } = React;
// Nav from nav.jsx (SiteNav)
// ───────── Hero ─────────
function Hero(){
return (
<section style={{maxWidth:1200, margin:"0 auto", padding:"72px 28px 56px"}}>
<div style={{display:"grid", gridTemplateColumns:"1.25fr 1fr", gap:48, alignItems:"center"}}>
<div>
<div style={{display:"inline-flex", alignItems:"center", gap:8,
padding:"6px 14px", borderRadius:999, background:"#fff",
border:"1px solid var(--line)", boxShadow:"0 1px 1px rgba(16,24,40,.03)",
fontSize:11, fontWeight:700, color:"var(--ink-2)", letterSpacing:".12em"}}>
<span style={{width:6, height:6, borderRadius:999, background:"var(--brand)"}}/>
ОТКРЫТЫЕ ИССЛЕДОВАНИЯ · 2026
</div>
<h1 style={{margin:"22px 0 18px", fontSize:68, fontWeight:800, lineHeight:0.98, letterSpacing:"-0.04em"}}>
На стыке&nbsp;
<span style={{fontFamily:"var(--serif)", fontWeight:400, fontStyle:"italic", color:"var(--brand)", letterSpacing:"-0.02em"}}>искусственного интеллекта</span>
&nbsp;и&nbsp;<span style={{fontFamily:"var(--serif)", fontWeight:400, fontStyle:"italic"}}>кибербезопасности</span>
</h1>
<p style={{margin:"0 0 26px", maxWidth:540, fontSize:17, lineHeight:1.55, color:"var(--ink-2)"}}>
PCA Lab независимая исследовательская группа. Курсы, лонгриды и open-source инструменты
по приватности, машинному обучению и защите сетей. Без рекламы, без слежки, без шума.
</p>
<div style={{display:"flex", gap:10, alignItems:"center", flexWrap:"wrap"}}>
<Button variant="primary" size="lg" iconRight="chevron-right" style={{background:"var(--ink)", boxShadow:"0 1px 0 rgba(255,255,255,.18) inset, 0 6px 16px rgba(0,0,0,.18)"}}>Начать с курса</Button>
<Button variant="secondary" size="lg" icon="doc">Программа</Button>
<div style={{display:"flex", alignItems:"center", gap:8, marginLeft:8, fontSize:13, color:"var(--ink-3)"}}>
<Icon name="check" size={14} color="var(--green)" stroke={2.5}/> 4 200+ слушателей
</div>
</div>
</div>
{/* Hero composition — research card stack */}
<div style={{position:"relative", height:420}}>
<Card padding={20} style={{
position:"absolute", inset:"0 0 auto 8%", width:"92%", transform:"rotate(-2deg)",
background:"linear-gradient(180deg,#fff 0%,#fafbff 100%)"
}}>
<div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-start"}}>
<Badge tone="blue" size="sm">CRYPTOGRAPHY</Badge>
<span style={{fontSize:11.5, color:"var(--ink-3)", fontFamily:"var(--mono)"}}>2026·05</span>
</div>
<h4 style={{margin:"14px 0 8px", fontSize:18, fontWeight:700, letterSpacing:"-0.01em"}}>Post-quantum handshake протоколы практический разбор</h4>
<p style={{margin:0, fontSize:13, color:"var(--ink-3)", lineHeight:1.5}}>
Сравниваем Kyber, Dilithium и SPHINCS+ в условиях ограниченного канала.
</p>
<ChartBars/>
</Card>
<Card padding={18} style={{
position:"absolute", bottom:0, left:0, width:"58%",
background:"linear-gradient(180deg,#1d1d1f 0%, #25262b 100%)", color:"#fff",
border:"1px solid rgba(255,255,255,.08)", boxShadow:"0 1px 0 rgba(255,255,255,.06) inset, 0 12px 32px rgba(0,0,0,.18)",
transform:"rotate(1.5deg)"
}}>
<div style={{display:"flex", alignItems:"center", gap:8, fontSize:11, fontWeight:700, color:"rgba(255,255,255,.7)", letterSpacing:".12em"}}>
<span style={{width:6, height:6, borderRadius:999, background:"#34c759"}}/>
LIVE COURSE
</div>
<h4 style={{margin:"10px 0 6px", fontSize:16, fontWeight:700, letterSpacing:"-0.01em"}}>Adversarial ML атаки на модели</h4>
<p style={{margin:0, fontSize:12.5, color:"rgba(255,255,255,.6)"}}>Старт 1 июня · 6 недель</p>
<div style={{marginTop:14, display:"flex", alignItems:"center", gap:-6}}>
{["#7c5cff","#ff9f0a","#34c759","#ff3b8a"].map((c,i)=>(
<div key={i} style={{
width:26, height:26, borderRadius:999, background:c, border:"2px solid #1d1d1f",
marginLeft: i===0?0:-8, fontSize:11, color:"#fff", display:"grid", placeItems:"center", fontWeight:700
}}>{["A","B","M","P"][i]}</div>
))}
<span style={{marginLeft:12, fontSize:12, color:"rgba(255,255,255,.7)"}}>+ 218 студентов</span>
</div>
</Card>
<Card padding={16} style={{
position:"absolute", top:"42%", right:"-2%", width:"52%",
transform:"rotate(3deg)"
}}>
<div style={{fontSize:11, fontWeight:700, letterSpacing:".1em", color:"var(--amber)"}}>NETWORK SECURITY</div>
<h4 style={{margin:"8px 0 8px", fontSize:15.5, fontWeight:700, letterSpacing:"-0.01em"}}>DPI-устойчивые туннели: обзор архитектур</h4>
<div style={{display:"flex", alignItems:"center", gap:10, marginTop:10}}>
<Avatar name="K" color="linear-gradient(160deg,#7ec2ff,#0a84ff)" size={28}/>
<span style={{fontSize:12, color:"var(--ink-3)"}}>К. Линеев · 12 мин</span>
</div>
</Card>
</div>
</div>
</section>
);
}
// ChartBars decoration
function ChartBars(){
const bars = [42, 68, 55, 84, 71, 90, 76, 88, 95];
return (
<div style={{marginTop:18, display:"flex", alignItems:"flex-end", gap:6, height:60}}>
{bars.map((v,i)=>(
<div key={i} style={{
flex:1, height: v + "%", borderRadius:6,
background: i===bars.length-1
? "linear-gradient(180deg,#0a84ff,#0f5dff)"
: "var(--bg-tint)",
border: i===bars.length-1 ? "0" : "1px solid var(--line)"
}}/>
))}
</div>
);
}
// ───────── Topic grid ─────────
function Topics(){
const items = [
{ tag:"AI SAFETY", title:"Безопасность языковых моделей", desc:"Prompt injection, jailbreaks, защитные слои и oversight-механизмы.",
color:"linear-gradient(160deg,#9b82ff,#6a44f5)", icon:"sparkles", articles:14},
{ tag:"NETWORK", title:"Современные сетевые протоколы", desc:"WireGuard, QUIC, HTTP/3, TLS 1.3 — как они устроены и где ломаются.",
color:"linear-gradient(160deg,#7ec2ff,#0a84ff)", icon:"globe", articles:22},
{ tag:"CRYPTOGRAPHY", title:"Прикладная криптография", desc:"От симметричных шифров до post-quantum, с практическими лабами.",
color:"linear-gradient(160deg,#1d1d1f,#3a3a3c)", icon:"lock", articles:31},
{ tag:"PRIVACY", title:"Приватность по умолчанию", desc:"Threat modeling, цифровая гигиена, OPSEC для исследователей.",
color:"linear-gradient(160deg,#34c759,#1f8a5b)", icon:"shield", articles:18},
{ tag:"MACHINE LEARNING", title:"Машинное обучение с нуля", desc:"Линейная алгебра, оптимизация и трансформеры — для практиков.",
color:"linear-gradient(160deg,#ff9f0a,#ff6b00)", icon:"brain", articles:27},
{ tag:"OFFENSIVE", title:"Red-team & пентест", desc:"Этичный взлом: methodology, инструменты, отчёты.",
color:"linear-gradient(160deg,#ff3b8a,#9b51e0)", icon:"bolt", articles:12},
];
return (
<section style={{maxWidth:1200, margin:"24px auto 0", padding:"0 28px"}}>
<SectionHeader
title="Направления"
subtitle="Шесть треков, каждый сопровождается курсами, лонгридами и открытыми инструментами"
right={<Button variant="secondary" iconRight="chevron-right">Все направления</Button>}/>
<div style={{display:"grid", gridTemplateColumns:"repeat(3,1fr)", gap:18}}>
{items.map((t,i)=>(
<Card key={i} padding={22} hover>
<div style={{display:"flex", alignItems:"flex-start", justifyContent:"space-between"}}>
<GlyphTile size={44} radius={12} gradient={t.color} icon={t.icon}/>
<span style={{
fontSize:11, fontWeight:600, color:"var(--ink-3)", fontVariantNumeric:"tabular-nums",
padding:"4px 10px", borderRadius:999, background:"var(--bg-tint)"
}}>{t.articles} материалов</span>
</div>
<div style={{marginTop:18, fontSize:10.5, fontWeight:700, color:"var(--ink-3)", letterSpacing:".12em"}}>
{t.tag}
</div>
<h3 style={{margin:"6px 0 8px", fontSize:19, fontWeight:700, letterSpacing:"-0.015em"}}>{t.title}</h3>
<p style={{margin:0, fontSize:13.5, color:"var(--ink-3)", lineHeight:1.55}}>{t.desc}</p>
<a href="#" style={{
marginTop:14, display:"inline-flex", alignItems:"center", gap:5,
fontSize:13, fontWeight:600, color:"var(--ink-2)"
}}>Изучить трек <Icon name="chevron-right" size={13} stroke={2.2}/></a>
</Card>
))}
</div>
</section>
);
}
// ───────── Featured Article + Sidebar list ─────────
function Articles(){
return (
<section style={{maxWidth:1200, margin:"72px auto 0", padding:"0 28px"}}>
<SectionHeader
title="Последние материалы"
subtitle="Лонгриды, лабораторные тетради, разборы инцидентов"
right={<Button variant="secondary" iconRight="chevron-right">Архив</Button>}/>
<div style={{display:"grid", gridTemplateColumns:"1.4fr 1fr", gap:18}}>
{/* Featured */}
<Card padding={0} hover style={{overflow:"hidden"}}>
<div style={{
height:280, position:"relative",
background:"linear-gradient(135deg,#1d1d1f 0%, #2d2d33 60%, #0f5dff 100%)",
overflow:"hidden"
}}>
<div style={{position:"absolute", inset:0,
backgroundImage:"radial-gradient(circle at 20% 30%, rgba(255,255,255,.18) 0%, transparent 50%), radial-gradient(circle at 80% 70%, rgba(15,93,255,.4) 0%, transparent 50%)",
pointerEvents:"none"}}/>
<CodeRain/>
<div style={{position:"absolute", top:20, left:20}}>
<Badge tone="blue" style={{background:"rgba(255,255,255,.92)", color:"#0a4ea3"}}>FEATURED · LONGREAD</Badge>
</div>
<div style={{position:"absolute", bottom:20, left:20, right:20, color:"#fff"}}>
<div style={{fontSize:11.5, fontWeight:600, opacity:.7, letterSpacing:".1em"}}>15 МАЯ 2026 · 22 МИН ЧТЕНИЯ</div>
<h3 style={{margin:"6px 0 0", fontSize:26, fontWeight:700, letterSpacing:"-0.02em", lineHeight:1.15}}>
Почему DPI всё чаще ошибается: разбор шести популярных эвристик
</h3>
</div>
</div>
<div style={{padding:"20px 22px"}}>
<p style={{margin:0, fontSize:14, color:"var(--ink-2)", lineHeight:1.55}}>
Современные системы глубокой инспекции пакетов проигрывают не из-за слабого железа,
а из-за переусложнённой эвристики. Разбираем причины и считаем false-positive rate на открытых датасетах.
</p>
<div style={{marginTop:14, display:"flex", alignItems:"center", gap:10}}>
<Avatar name="K" color="linear-gradient(160deg,#7ec2ff,#0a84ff)" size={32}/>
<div style={{lineHeight:1.2}}>
<div style={{fontSize:13.5, fontWeight:600}}>Константин Линеев</div>
<div style={{fontSize:12, color:"var(--ink-3)"}}>Senior researcher, Network team</div>
</div>
</div>
</div>
</Card>
{/* List of small articles */}
<div style={{display:"flex", flexDirection:"column", gap:14}}>
{[
{tag:"AI SAFETY", date:"12 мая", read:"9 мин", title:"Constitutional AI: как обучают модели по тексту правил", color:"linear-gradient(160deg,#9b82ff,#6a44f5)", author:"А. Морозова"},
{tag:"PRIVACY", date:"08 мая", read:"6 мин", title:"Threat modeling для журналистов и активистов", color:"linear-gradient(160deg,#34c759,#1f8a5b)", author:"М. Петренко"},
{tag:"CRYPTO", date:"03 мая", read:"14 мин", title:"Подписи Ed25519 — что в них особенного", color:"linear-gradient(160deg,#1d1d1f,#3a3a3c)", author:"Д. Богданов"},
{tag:"OFFENSIVE", date:"29 апр", read:"11 мин", title:"Lateral movement в облачных средах: AWS edition", color:"linear-gradient(160deg,#ff3b8a,#9b51e0)", author:"Е. Лазарев"},
].map((a,i)=>(
<Card key={i} padding={16} hover>
<div style={{display:"flex", gap:14, alignItems:"flex-start"}}>
<div style={{
width:48, height:48, borderRadius:12, flexShrink:0,
background:a.color, position:"relative", overflow:"hidden"
}}>
<div style={{position:"absolute", inset:0, opacity:.4,
backgroundImage:"radial-gradient(circle at 30% 30%, rgba(255,255,255,.4), transparent 60%)"}}/>
</div>
<div style={{flex:1, minWidth:0}}>
<div style={{display:"flex", alignItems:"center", gap:8, fontSize:10.5, color:"var(--ink-3)"}}>
<span style={{fontWeight:700, letterSpacing:".1em"}}>{a.tag}</span>
<span style={{opacity:.4}}>·</span>
<span>{a.date}</span>
<span style={{opacity:.4}}>·</span>
<span>{a.read}</span>
</div>
<h4 style={{margin:"5px 0 4px", fontSize:15, fontWeight:700, letterSpacing:"-0.01em", lineHeight:1.25}}>{a.title}</h4>
<div style={{fontSize:12, color:"var(--ink-3)"}}>{a.author}</div>
</div>
</div>
</Card>
))}
</div>
</div>
</section>
);
}
function CodeRain(){
// Static "code lines" decoration
const lines = [
"$ openssl genpkey -algorithm Ed25519",
"→ key.pem · 64 bytes",
"$ sha256sum key.pem",
"8a3f…d2c1 key.pem",
"$ ./detect --input pcap/sample.pcap",
"matched: 0 · false-positive: 12%",
];
return (
<div style={{
position:"absolute", right:18, bottom:80, fontFamily:"var(--mono)",
fontSize:11, color:"rgba(255,255,255,.42)", lineHeight:1.6,
maxWidth:"50%", textAlign:"right"
}}>
{lines.map((l,i)=><div key={i}>{l}</div>)}
</div>
);
}
// ───────── Courses CTA ─────────
function Courses(){
return (
<section style={{maxWidth:1200, margin:"72px auto 0", padding:"0 28px"}}>
<SectionHeader title="Курсы — открытый набор"
subtitle="Записываемся бесплатно, материалы остаются у вас навсегда"/>
<div style={{display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:18}}>
{[
{weeks:8, name:"Введение в нейросети", lvl:"beginner", lvlT:"green", n:1240, color:"linear-gradient(160deg,#9b82ff,#6a44f5)", icon:"brain"},
{weeks:6, name:"Cybersec для разработчиков", lvl:"intermediate", lvlT:"blue", n:864, color:"linear-gradient(160deg,#7ec2ff,#0a84ff)", icon:"shield"},
{weeks:12, name:"Криптография в продакшене", lvl:"advanced", lvlT:"purple", n:412, color:"linear-gradient(160deg,#1d1d1f,#3a3a3c)", icon:"lock"},
{weeks:4, name:"OPSEC и приватность", lvl:"beginner", lvlT:"green", n:1820, color:"linear-gradient(160deg,#34c759,#1f8a5b)", icon:"shield"},
].map((c,i)=>(
<Card key={i} padding={20} hover>
<GlyphTile size={42} radius={12} gradient={c.color} icon={c.icon}/>
<h4 style={{margin:"14px 0 6px", fontSize:16, fontWeight:700, letterSpacing:"-0.01em", minHeight:42, lineHeight:1.3}}>{c.name}</h4>
<div style={{display:"flex", alignItems:"center", gap:8, fontSize:12, color:"var(--ink-3)"}}>
<Icon name="clock" size={13}/> {c.weeks} недель
<span style={{opacity:.4}}>·</span>
<Icon name="users" size={13}/> {c.n}
</div>
<div style={{marginTop:14, paddingTop:14, borderTop:"1px solid var(--line-2)",
display:"flex", alignItems:"center", justifyContent:"space-between"}}>
<Badge tone={c.lvlT} size="sm">{c.lvl}</Badge>
<a href="#" style={{fontSize:13, fontWeight:600, color:"var(--ink-2)",
display:"inline-flex", alignItems:"center", gap:4}}>
Записаться <Icon name="chevron-right" size={13} stroke={2.2}/>
</a>
</div>
</Card>
))}
</div>
</section>
);
}
// ───────── Newsletter / CTA ─────────
function Newsletter(){
return (
<section style={{maxWidth:1200, margin:"72px auto 0", padding:"0 28px"}}>
<div style={{
position:"relative", overflow:"hidden",
borderRadius:"calc(var(--radius-card) + 6px)",
background:"linear-gradient(135deg,#0a0a0b 0%, #1d1d1f 60%, #0f5dff 200%)",
color:"#fff", padding:"48px 48px", border:"1px solid rgba(255,255,255,.06)",
boxShadow:"0 1px 0 rgba(255,255,255,.06) inset, 0 18px 50px rgba(15,93,255,.18)"
}}>
<div style={{position:"absolute", inset:0, opacity:.6,
background:"radial-gradient(500px 280px at 100% 100%, rgba(15,93,255,.4), transparent 60%), radial-gradient(400px 220px at 0% 0%, rgba(155,82,224,.18), transparent 60%)"}}/>
<div style={{position:"relative", display:"grid", gridTemplateColumns:"1.2fr 1fr", gap:36, alignItems:"center"}}>
<div>
<div style={{fontSize:11, fontWeight:700, color:"rgba(255,255,255,.6)", letterSpacing:".14em"}}>
· NEWSLETTER · ПО ЧЕТВЕРГАМ ·
</div>
<h2 style={{margin:"12px 0 10px", fontSize:34, fontWeight:700, letterSpacing:"-0.02em", lineHeight:1.1}}>
Раз в неделю лучшие материалы и одна короткая мысль
</h2>
<p style={{margin:0, fontSize:14.5, color:"rgba(255,255,255,.7)", maxWidth:480, lineHeight:1.55}}>
Без рекламы и продаж курсов. Только то, что мы сами читаем и используем в работе.
</p>
</div>
<form style={{display:"flex", gap:10}} onSubmit={(e)=>e.preventDefault()}>
<input type="email" placeholder="your@email.com" style={{
flex:1, height:48, padding:"0 18px",
border:"1px solid rgba(255,255,255,.14)", background:"rgba(255,255,255,.06)",
backdropFilter:"blur(10px)", borderRadius:14, color:"#fff",
fontSize:14.5, outline:"none"
}}/>
<button style={{
appearance:"none", border:0, height:48, padding:"0 22px",
background:"#fff", color:"#0a0a0b", fontSize:14, fontWeight:700,
borderRadius:14, cursor:"pointer"
}}>Подписаться </button>
</form>
</div>
</div>
</section>
);
}
// ───────── Footer ─────────
function Footer(){
return (
<footer style={{maxWidth:1200, margin:"72px auto 0", padding:"32px 28px 48px"}}>
<div style={{display:"grid", gridTemplateColumns:"1.5fr 1fr 1fr 1fr 1fr", gap:32, paddingBottom:32, borderBottom:"1px solid var(--line-2)"}}>
<div>
<div style={{display:"flex", alignItems:"center", gap:10}}>
<div style={{width:32, height:32, borderRadius:10, background:"linear-gradient(160deg,#1d1d1f,#2d2d33)",
display:"grid", placeItems:"center", color:"#fff"}}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="9" stroke="#fff" strokeWidth="1.6"/>
<path d="M8 7l4 10 4-10" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</div>
<div style={{fontSize:15, fontWeight:700, letterSpacing:"-0.01em"}}>PCA Lab</div>
</div>
<p style={{margin:"14px 0 14px", fontSize:13, color:"var(--ink-3)", maxWidth:280, lineHeight:1.55}}>
Независимая исследовательская группа. Курсы, открытые статьи и инструменты
по приватности и AI с 2022 года.
</p>
<div style={{display:"flex", gap:8}}>
{[
{icon:"github", l:"GitHub"},
{icon:"plane", l:"Telegram"},
{icon:"doc", l:"RSS"},
].map(s=>(
<a key={s.l} href="#" title={s.l} style={{
width:34, height:34, borderRadius:10, background:"#fff",
border:"1px solid var(--line)", display:"grid", placeItems:"center",
color:"var(--ink-2)"
}}><Icon name={s.icon} size={15}/></a>
))}
</div>
</div>
<FootCol title="Контент" links={["Главная","Курсы","Исследования","Блог","Авторы"]}/>
<FootCol title="Темы" links={["AI Safety","Network","Cryptography","Privacy","ML","Red-team"]}/>
<FootCol title="Лаборатория" links={["О нас","Резиденты","Партнёры","Контакты","Открытые позиции"]}/>
<FootCol title="Студентам" links={[
{ l:"Программа курса" },
{ l:"FAQ" },
{ l:"Помощь техподдержки" },
{ l:"Кабинет студента", href:"connect.html", highlight:true },
{ l:"Группы доменов", href:"domains.html" },
{ l:"VPN по региону", href:"connect.html?mode=region" },
]}/>
</div>
<div style={{paddingTop:24, display:"flex", justifyContent:"space-between", alignItems:"center", fontSize:12.5, color:"var(--ink-3)", flexWrap:"wrap", gap:14}}>
<div>© 20222026 PCA Lab · Все материалы под лицензией CC BY-SA 4.0</div>
<div style={{display:"flex", gap:18}}>
<a href="#" style={{color:"var(--ink-3)"}}>Конфиденциальность</a>
<a href="#" style={{color:"var(--ink-3)"}}>Условия</a>
<a href="#" style={{color:"var(--ink-3)"}}>RSS</a>
</div>
</div>
</footer>
);
}
function FootCol({ title, links }){
return (
<div>
<div style={{fontSize:11, fontWeight:700, color:"var(--ink-3)", letterSpacing:".14em", textTransform:"uppercase", marginBottom:14}}>{title}</div>
<ul style={{margin:0, padding:0, listStyle:"none", display:"flex", flexDirection:"column", gap:10}}>
{links.map((l,i)=>{
const item = typeof l === "string" ? { l } : l;
return (
<li key={i}>
<a href={item.href || "#"} style={{
fontSize:13.5, color: item.highlight ? "var(--ink)" : "var(--ink-2)",
fontWeight: item.highlight ? 600 : 500,
display:"inline-flex", alignItems:"center", gap:6,
}}>
{item.l}
{item.highlight && <Icon name="chevron-right" size={12} stroke={2.2} color="var(--ink-3)"/>}
</a>
</li>
);
})}
</ul>
</div>
);
}
// ───────── Stats strip ─────────
function StatsStrip(){
return (
<section style={{maxWidth:1200, margin:"40px auto 0", padding:"0 28px"}}>
<div style={{display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap:0,
background:"#fff", border:"1px solid var(--line)", borderRadius:18,
boxShadow:"var(--shadow-card)", overflow:"hidden"}}>
{[
{v:"124", l:"открытых статей"},
{v:"12", l:"живых курсов"},
{v:"4 200+", l:"слушателей"},
{v:"32", l:"open-source проекта"},
].map((s,i)=>(
<div key={i} style={{padding:"22px 24px", borderLeft: i===0 ? "0" : "1px solid var(--line-2)"}}>
<div style={{fontSize:30, fontWeight:700, letterSpacing:"-0.02em", fontVariantNumeric:"tabular-nums"}}>{s.v}</div>
<div style={{marginTop:2, fontSize:13, color:"var(--ink-3)"}}>{s.l}</div>
</div>
))}
</div>
</section>
);
}
// ───────── App ─────────
function HomeApp(){
return (
<div>
<SiteNav/>
<Hero/>
<StatsStrip/>
<Topics/>
<Articles/>
<Courses/>
<Newsletter/>
<Footer/>
</div>
);
}
ReactDOM.createRoot(document.getElementById("root")).render(<HomeApp/>);