// global-home.jsx — Homepage for Teknav Global (English, LTR)
import { useState, useEffect, useRef } from 'react';
import { articles as allArticlesData, authors, categories, siteConfig } from '../global-data.js';
import { useNav, useAuth, AuthorAvatar, CategoryBadge, TypeBadge, NewsletterForm, pillBtn } from './global-ui.jsx';
// ── Hero Neural Background ──────────────────────────────────────────────────
function HeroBackground() {
const canvasRef = useRef();
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
let raf, t = 0;
const palette = [
'rgba(15,107,115,',
'rgba(196,106,77,',
'rgba(47,143,107,',
];
const nodes = Array.from({ length: 32 }, (_, i) => ({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 0.35,
vy: (Math.random() - 0.5) * 0.35,
r: Math.random() * 3 + 1.5,
color: palette[i % palette.length],
phase: Math.random() * Math.PI * 2,
}));
const resize = () => { canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; };
resize();
window.addEventListener('resize', resize);
const draw = () => {
t += 0.012;
ctx.clearRect(0, 0, canvas.width, canvas.height);
nodes.forEach(n => {
n.x += n.vx; n.y += n.vy;
if (n.x < 0 || n.x > canvas.width) n.vx *= -1;
if (n.y < 0 || n.y > canvas.height) n.vy *= -1;
nodes.forEach(m => {
const d = Math.hypot(n.x - m.x, n.y - m.y);
if (d < 130) {
ctx.beginPath();
ctx.moveTo(n.x, n.y); ctx.lineTo(m.x, m.y);
ctx.strokeStyle = `rgba(15,107,115,${0.18 * (1 - d / 130)})`;
ctx.lineWidth = 0.8; ctx.stroke();
}
});
const pulse = 0.45 + 0.18 * Math.sin(t + n.phase);
ctx.beginPath();
ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2);
ctx.fillStyle = n.color + pulse + ')';
ctx.fill();
});
raf = requestAnimationFrame(draw);
};
draw();
return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', resize); };
}, []);
return ;
}
// ── Article Card (grid) ─────────────────────────────────────────────────────
function ArticleCard({ article, delay = 0 }) {
const { navigate } = useNav();
const [vis, setVis] = useState(false);
const ref = useRef();
useEffect(() => {
const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) setVis(true); }, { threshold: 0.1 });
if (ref.current) obs.observe(ref.current);
return () => obs.disconnect();
}, []);
const author = authors.find(a => a.id === article.author);
const cat = categories.find(c => c.id === article.category);
return (
navigate('/article/' + article.slug)} style={{
background: '#fff', borderRadius: 12, border: '1px solid #E4DDD2',
padding: 24, cursor: 'pointer',
opacity: vis ? 1 : 0, transform: vis ? 'translateY(0)' : 'translateY(24px)',
transition: `opacity 0.5s ${delay}ms, transform 0.5s ${delay}ms, box-shadow 0.2s`,
boxShadow: '0 2px 12px rgba(0,0,0,0.04)',
}} onMouseEnter={e => e.currentTarget.style.boxShadow = '0 8px 32px rgba(0,0,0,0.1)'}
onMouseLeave={e => e.currentTarget.style.boxShadow = '0 2px 12px rgba(0,0,0,0.04)'}>
{article.photo ? (

{ e.target.style.display = 'none'; }} />
) : (
)}
{article.type && }
{article.title}
{(article.subtitle || article.summary || '').slice(0, 100)}...
{article.date}
{author &&
}
{article.readTime} min
);
}
// ── Featured Article ────────────────────────────────────────────────────────
function FeaturedArticle({ article }) {
const { navigate } = useNav();
const author = authors.find(a => a.id === article.author);
const cat = categories.find(c => c.id === article.category);
return (
navigate('/article/' + article.slug)} style={{
background: '#fff', borderRadius: 16, border: '1px solid #E4DDD2', padding: 0,
cursor: 'pointer', overflow: 'hidden',
boxShadow: '0 4px 24px rgba(0,0,0,0.06)',
display: 'grid', gridTemplateColumns: '1fr 1fr', minHeight: 280,
transition: 'box-shadow 0.2s',
}} className="featured-card"
onMouseEnter={e => e.currentTarget.style.boxShadow = '0 12px 48px rgba(0,0,0,0.12)'}
onMouseLeave={e => e.currentTarget.style.boxShadow = '0 4px 24px rgba(0,0,0,0.06)'}>
{article.type && }
{article.title}
{article.subtitle || article.summary}
{author &&
}
{author?.name}
{article.date} · {article.readTime} min read
{article.photo ? (

{ e.target.style.display = 'none'; }} />
) : (
)}
);
}
// ── Authors Section ─────────────────────────────────────────────────────────
function AuthorsSection() {
const { navigate } = useNav();
return (
Teknav Authors
The specialists who write Teknav's deep technology analysis
{authors.map(author => (
{ e.currentTarget.style.transform = 'translateY(-4px)'; e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.08)'; }}
onMouseLeave={e => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = 'none'; }}>
{author.name}
{author.specialty}
{author.articleCount} articles
))}
);
}
// ── Hero Section ─────────────────────────────────────────────────────────────
function HeroSection({ featured, navigate }) {
const wrapRef = useRef();
const cardRef = useRef();
useEffect(() => {
const wrap = wrapRef.current;
const card = cardRef.current;
if (!wrap || !card) return;
const onMove = e => {
const r = wrap.getBoundingClientRect();
const dx = (e.clientX - r.left) / r.width - 0.5;
const dy = (e.clientY - r.top) / r.height - 0.5;
card.style.transform = `perspective(900px) rotateX(${(-dy * 4).toFixed(2)}deg) rotateY(${(dx * 6).toFixed(2)}deg)`;
};
const onLeave = () => { card.style.transform = 'perspective(900px) rotateX(0) rotateY(0)'; };
wrap.addEventListener('mousemove', onMove);
wrap.addEventListener('mouseleave', onLeave);
return () => { wrap.removeEventListener('mousemove', onMove); wrap.removeEventListener('mouseleave', onLeave); };
}, []);
return (
Deep Technology Analysis
Understand more.
Build better.
Scientific, independent analysis of AI engineering, data infrastructure, cybersecurity, and the future of software.
{featured && (
✦
Featured
navigate('/article/' + featured.slug)} style={{ fontSize: 18, fontWeight: 700, color: '#0F2A2E', cursor: 'pointer', lineHeight: 1.65, transition: 'color 0.15s', fontFamily: 'Inter,sans-serif' }}
onMouseEnter={e => e.currentTarget.style.color = '#0F6B73'}
onMouseLeave={e => e.currentTarget.style.color = '#0F2A2E'}>
{featured.title}
{authors.find(a => a.id === featured.author)?.name}
·
{featured.type}
·
{featured.readTime} min read
)}
);
}
// ── Stats Bar ───────────────────────────────────────────────────────────────
function CountUp({ to, suffix = '', duration = 1400 }) {
const [n, setN] = useState(0);
const ref = useRef();
const started = useRef(false);
useEffect(() => {
const obs = new IntersectionObserver(([e]) => {
if (e.isIntersecting && !started.current) {
started.current = true;
const start = performance.now();
const tick = (now) => {
const p = Math.min((now - start) / duration, 1);
const eased = 1 - Math.pow(1 - p, 3);
setN(Math.round(to * eased));
if (p < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
}
}, { threshold: 0.4 });
if (ref.current) obs.observe(ref.current);
return () => obs.disconnect();
}, [to, duration]);
return {n.toLocaleString('en-US')}{suffix};
}
function StatsBar() {
const stats = [
{ label: 'Articles published', to: 75, suffix: '+', icon: '◉' },
{ label: 'Active authors', to: 2, suffix: '', icon: '✎' },
{ label: 'Monthly readers', to: 65000, suffix: '+', icon: '◐' },
{ label: 'Avg. read time', to: 10, suffix: ' min', icon: '◷' },
];
return (
{stats.map((s, i) => (
{s.icon}
{s.label}
{i < stats.length - 1 && (
)}
))}
);
}
// ── Shared section styles ───────────────────────────────────────────────────
const sectionTitle = { fontSize: 22, fontWeight: 800, color: '#263238', margin: '0 0 8px', fontFamily: 'Inter,sans-serif' };
const sectionSubtitle = { fontSize: 14, color: '#5F6B6D', margin: '0 0 32px', lineHeight: 1.7 };
// ── Article Row Compact ─────────────────────────────────────────────────────
function ArticleRowCompact({ article }) {
const { navigate } = useNav();
const cat = categories.find(c => c.id === article.category);
return (
navigate('/article/' + article.slug)} style={{
display: 'flex', gap: 20, padding: '20px 0', borderBottom: '1px solid #E4DDD2',
cursor: 'pointer', alignItems: 'flex-start', transition: 'background 0.15s',
}} onMouseEnter={e => e.currentTarget.style.background = '#F4EFE620'}
onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
{article.type && }
{article.title}
{(article.subtitle || '').slice(0, 140)}…
{authors.find(a => a.id === article.author)?.name} · {article.date} · {article.readTime} min
{article.photo && (

{ e.target.style.display = 'none'; }} />
)}
);
}
// ── Homepage ────────────────────────────────────────────────────────────────
export default function HomePage() {
const { navigate } = useNav();
const articles = allArticlesData;
const featured = articles.find(a => a.featured) || articles[0];
const latest = articles.filter(a => a.id !== featured?.id).slice(0, 6);
const popular = [...articles].sort((a, b) => (b.views || 0) - (a.views || 0)).slice(0, 4);
const aiArticles = articles.filter(a => a.category === 'ai').slice(0, 5);
const securityArticles = articles.filter(a => a.category === 'security').slice(0, 5);
return (
{featured && (
Featured
The deepest analysis this week
)}
Latest
The latest analysis from Teknav
{aiArticles.length > 0 && (
⚙
AI Engineering
Production AI systems, agents, and model infrastructure
)}
{securityArticles.length > 0 && (
⚖
Security & Legal
AI security, legal liability, and governance
{securityArticles.map(a =>
)}
)}
Most Read
Teknav's most-read articles
{popular.map((a, i) =>
)}
Topics
Explore by area of expertise
{categories.map(cat => (
navigate('/category/' + cat.id)} style={{
background: '#fff', border: `1px solid ${cat.color}30`, borderRadius: 10, padding: '20px 16px',
cursor: 'pointer', transition: 'all 0.2s', borderLeft: `3px solid ${cat.color}`,
}} onMouseEnter={e => e.currentTarget.style.background = cat.color + '08'}
onMouseLeave={e => e.currentTarget.style.background = '#fff'}>
{cat.name}
{cat.description}
{allArticlesData.filter(a => a.category === cat.id).length} articles
))}
✉
Subscribe to Teknav
Every week, Teknav's best analysis delivered straight to your inbox
);
}
export { ArticleCard, ArticleRowCompact, sectionTitle, sectionSubtitle };