diff --git a/global/src/global-ui.jsx b/global/src/global-ui.jsx
new file mode 100644
index 0000000..b397e81
--- /dev/null
+++ b/global/src/global-ui.jsx
@@ -0,0 +1,498 @@
+// global-ui.jsx — Shared UI components for Teknav Global (English, LTR)
+import { useState, useEffect, useRef, useCallback, createContext, useContext } from 'react';
+import { authors, categories, siteConfig } from '../global-data.js';
+import { api } from './lib/api.js';
+
+// ── Toast System ────────────────────────────────────────────────────────────
+const ToastCtx = createContext(null);
+function ToastProvider({ children }) {
+ const [toasts, setToasts] = useState([]);
+ const add = useCallback((msg, type = 'success') => {
+ const id = Date.now();
+ setToasts(t => [...t, { id, msg, type }]);
+ setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 3200);
+ }, []);
+ return (
+
+ {children}
+
+ {toasts.map(t => (
+
{t.msg}
+ ))}
+
+
+ );
+}
+const useToast = () => useContext(ToastCtx);
+
+// ── Navigation Context ──────────────────────────────────────────────────────
+const NavCtx = createContext(null);
+function getCurrentPage() {
+ const pathname = window.location.pathname;
+ const cleanPath = pathname.length > 1 && pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
+ const path = `${cleanPath}${window.location.search}`;
+ return path === '' ? '/' : path;
+}
+function NavProvider({ children }) {
+ const [page, setPage] = useState(getCurrentPage);
+
+ const navigate = useCallback((to) => {
+ if (`${window.location.pathname}${window.location.search}` !== to) {
+ window.history.pushState(null, '', to);
+ }
+ setPage(to);
+ window.scrollTo(0, 0);
+ }, []);
+
+ useEffect(() => {
+ const handler = () => setPage(getCurrentPage());
+ window.addEventListener('popstate', handler);
+ return () => window.removeEventListener('popstate', handler);
+ }, []);
+
+ return {children};
+}
+const useNav = () => useContext(NavCtx);
+
+// ── Auth Context ────────────────────────────────────────────────────────────
+const AuthCtx = createContext(null);
+function AuthProvider({ children }) {
+ const [user, setUser] = useState(null);
+ const [bootstrapping, setBootstrapping] = useState(true);
+
+ useEffect(() => {
+ let cancelled = false;
+ api.get('/api/auth/me')
+ .then((res) => { if (!cancelled) setUser(res?.user ?? null); })
+ .catch(() => {})
+ .finally(() => { if (!cancelled) setBootstrapping(false); });
+ return () => { cancelled = true; };
+ }, []);
+
+ const logout = async () => {
+ try { await api.post('/api/auth/logout', {}); } catch (e) { console.warn('[auth/logout]', e); }
+ setUser(null);
+ };
+
+ return {children};
+}
+const useAuth = () => useContext(AuthCtx);
+
+// ── Badges ──────────────────────────────────────────────────────────────────
+const TYPE_COLORS = {
+ 'Deep Analysis': { bg: '#0F6B73', text: '#fff' },
+ 'Technical Guide': { bg: '#D49A2A', text: '#fff' },
+ 'Data Viz': { bg: '#C76D4A', text: '#fff' },
+ 'Research': { bg: '#2F8F6B', text: '#fff' },
+ 'Fact-Checked': { bg: '#263238', text: '#fff' },
+ 'Breaking': { bg: '#C94C4C', text: '#fff' },
+ 'Analysis': { bg: '#5F6B6D', text: '#fff' },
+ 'Technical Analysis': { bg: '#0F6B73', text: '#fff' },
+ 'Guide': { bg: '#D49A2A', text: '#fff' },
+ 'default': { bg: '#E4DDD2', text: '#263238' },
+};
+function CategoryBadge({ name, color, small }) {
+ return (
+ {name}
+ );
+}
+function TypeBadge({ type, small }) {
+ const c = TYPE_COLORS[type] || TYPE_COLORS.default;
+ return (
+ {type}
+ );
+}
+
+// ── Header ──────────────────────────────────────────────────────────────────
+const NAV_ITEMS = [
+ { label: 'Home', path: '/' },
+ { label: 'AI', path: '/category/ai' },
+ { label: 'Security', path: '/category/security' },
+ { label: 'Software', path: '/category/software' },
+ { label: 'Hardware', path: '/category/hardware' },
+ { label: 'Data', path: '/category/data' },
+ { label: 'Policy', path: '/category/policy' },
+ { label: 'Articles', path: '/articles' },
+];
+
+function Header() {
+ const { navigate, page } = useNav();
+ const { user, logout } = useAuth();
+ const [scrolled, setScrolled] = useState(false);
+ const [mobileOpen, setMobileOpen] = useState(false);
+ const [searchOpen, setSearchOpen] = useState(false);
+ const [searchQ, setSearchQ] = useState('');
+ const searchRef = useRef();
+
+ useEffect(() => {
+ const h = () => setScrolled(window.scrollY > 20);
+ window.addEventListener('scroll', h);
+ return () => window.removeEventListener('scroll', h);
+ }, []);
+
+ useEffect(() => {
+ if (searchOpen) setTimeout(() => searchRef.current?.focus(), 100);
+ }, [searchOpen]);
+
+ const handleSearch = (e) => {
+ e.preventDefault();
+ if (searchQ.trim()) { navigate('/articles?q=' + encodeURIComponent(searchQ)); setSearchOpen(false); setSearchQ(''); }
+ };
+
+ return (
+ <>
+
+
+ {/* Logo */}
+
+
+ {/* Nav */}
+
+
+ {/* Actions */}
+
+
+
+ فارسی
+
+ {user ? (
+
+ {user.username && (
+ @{user.username}
+ )}
+
+
+ ) : (
+
+ )}
+
+
+
+
+ {/* Search bar */}
+ {searchOpen && (
+
+
+
+ )}
+
+
+ {/* Mobile Drawer */}
+ {mobileOpen && (
+
+
setMobileOpen(false)} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.4)' }} />
+
+
+ Teknav
+
+
+ {NAV_ITEMS.map(item => (
+
+ ))}
+
فارسی (Persian)
+
+
+ )}
+
+ >
+ );
+}
+
+// ── Footer ──────────────────────────────────────────────────────────────────
+function Footer() {
+ const { navigate } = useNav();
+ return (
+
+ );
+}
+
+// ── Newsletter Form ─────────────────────────────────────────────────────────
+function NewsletterForm() {
+ const [email, setEmail] = useState('');
+ const [state, setState] = useState('idle');
+ const toast = useToast();
+
+ const submit = async (e) => {
+ e.preventDefault();
+ if (!email.includes('@') || !email.includes('.')) { toast('Please enter a valid email address', 'error'); return; }
+ try {
+ await api.post('/api/newsletter/subscribe', { email });
+ setState('done');
+ toast('You\'ve subscribed to Teknav!', 'success');
+ } catch {
+ toast('Subscription error — please try again', 'error');
+ }
+ };
+
+ if (state === 'done') return (
+
+
✓
+
You're subscribed!
+
You'll receive Teknav's latest analysis in your inbox.
+
+ );
+
+ return (
+
+ );
+}
+
+// ── Skeleton Loader ─────────────────────────────────────────────────────────
+function SkeletonLoader({ lines = 3, height = 16 }) {
+ return (
+
+ {Array.from({ length: lines }).map((_, i) => (
+
+ ))}
+
+ );
+}
+
+// ── Empty State ─────────────────────────────────────────────────────────────
+function EmptyState({ title = 'No results found', subtitle = 'Try different filters or search terms' }) {
+ return (
+
+
◎
+
{title}
+
{subtitle}
+
+ );
+}
+
+// ── Reading Progress ────────────────────────────────────────────────────────
+function ReadingProgress() {
+ const [pct, setPct] = useState(0);
+ useEffect(() => {
+ const h = () => {
+ const el = document.documentElement;
+ const scroll = el.scrollTop || document.body.scrollTop;
+ const total = el.scrollHeight - el.clientHeight;
+ setPct(total > 0 ? (scroll / total) * 100 : 0);
+ };
+ window.addEventListener('scroll', h);
+ return () => window.removeEventListener('scroll', h);
+ }, []);
+ return (
+
+ );
+}
+
+// ── Author Avatar ───────────────────────────────────────────────────────────
+function AuthorAvatar({ author, size = 40 }) {
+ const a = typeof author === 'string' ? authors.find(x => x.id === author) : author;
+ if (!a) return null;
+ return (
+
{a.initials || a.name.slice(0, 2)}
+ );
+}
+
+// ── Push Notification Prompt ────────────────────────────────────────────────
+function PushPrompt() {
+ const [visible, setVisible] = useState(false);
+ const toast = useToast();
+
+ useEffect(() => {
+ if (
+ 'PushManager' in window &&
+ 'serviceWorker' in navigator &&
+ typeof Notification !== 'undefined' &&
+ Notification.permission === 'default' &&
+ !localStorage.getItem('push_prompt_dismissed_en')
+ ) {
+ const t = setTimeout(() => setVisible(true), 8000);
+ return () => clearTimeout(t);
+ }
+ }, []);
+
+ if (!visible) return null;
+
+ const handleEnable = async () => {
+ try {
+ const perm = await Notification.requestPermission();
+ if (perm === 'granted') toast('Browser notifications enabled');
+ } catch { /* ignore */ }
+ setVisible(false);
+ };
+
+ const dismiss = () => {
+ localStorage.setItem('push_prompt_dismissed_en', '1');
+ setVisible(false);
+ };
+
+ return (
+
+
🔔
+
+
Browser notifications
+
Never miss a Teknav article
+
+
+
+
+
+
+ );
+}
+
+// ── Shared styles ───────────────────────────────────────────────────────────
+const iconBtnStyle = {
+ background: 'none', border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
+ width: 36, height: 36, borderRadius: 8, transition: 'background 0.15s',
+};
+const pillBtn = {
+ border: 'none', cursor: 'pointer', padding: '8px 18px', borderRadius: 8,
+ fontSize: 13, fontFamily: 'Inter,sans-serif', fontWeight: 600,
+ transition: 'all 0.15s', display: 'inline-flex', alignItems: 'center', gap: 6,
+};
+const inputStyle = {
+ border: '1px solid #E4DDD2', borderRadius: 8, padding: '9px 14px',
+ fontSize: 13, fontFamily: 'Inter,sans-serif', background: '#fff',
+ color: '#263238', outline: 'none', width: '100%',
+};
+
+export {
+ ToastProvider, useToast, NavProvider, useNav, AuthProvider, useAuth,
+ CategoryBadge, TypeBadge, AuthorAvatar,
+ Header, Footer, NewsletterForm, SkeletonLoader, EmptyState, ReadingProgress,
+ PushPrompt,
+ iconBtnStyle, pillBtn, inputStyle,
+};