const { useState, useEffect, useRef } = React; function Logo({ size = 'md' }) { // Exact lockup from vyovam.com — tall pill V mark + heavy VYOVAM wordmark, all white on navy return (
VYOVAM TECH SERVICES LLC
); } /* ------------ Theme toggle: Day / Night / Auto ------------ */ function ThemeToggle() { const [mode, setMode] = useState(() => { try { return localStorage.getItem('vyovam-theme') || 'auto'; } catch (e) { return 'auto'; } }); useEffect(() => { try { localStorage.setItem('vyovam-theme', mode); } catch (e) {} const mq = window.matchMedia('(prefers-color-scheme: dark)'); const apply = () => { const dark = mode === 'dark' || (mode === 'auto' && mq.matches); document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light'); }; apply(); mq.addEventListener('change', apply); return () => mq.removeEventListener('change', apply); }, [mode]); const next = { auto: 'light', light: 'dark', dark: 'auto' }; const labels = { auto: 'Auto', light: 'Day', dark: 'Night' }; const icons = { light: ( ), dark: ( ), auto: ( ) }; return ( ); } function Nav({ page, setPage }) { const [open, setOpen] = useState(false); const links = [ { id: 'home', label: 'Home' }, { id: 'infrastructure', label: 'Algo Trading' }, { id: 'ecommerce', label: 'E-Commerce', soon: true }, { id: 'fluentme', label: 'FluentMe' }, { id: 'contact', label: 'Contact' } ]; const go = (id) => (e) => { e.preventDefault(); setPage(id); setOpen(false); }; return ( ); } /* ------------ Live ticker bar (custom scrolling marquee) ------------ Sensex + Nifty (Algomind) scroll together with the global markets (Yahoo Finance) via one server-side proxy. Fully theme-aware — no third-party widget, so no day/night colour desync. */ function Ticker() { const [items, setItems] = useState([]); useEffect(() => { let alive = true; const load = async () => { try { const r = await fetch('ticker-data.php', { cache: 'no-store' }); if (!r.ok) return; const j = await r.json(); if (alive && j && Array.isArray(j.items) && j.items.length) setItems(j.items); } catch (e) { /* keep last good data */ } }; load(); const id = setInterval(load, 30000); return () => { alive = false; clearInterval(id); }; }, []); const fmt = (p, dp) => Number(p).toLocaleString('en-US', { minimumFractionDigits: dp, maximumFractionDigits: dp }); const renderItem = (it, key) => { const pct = Number(it.changePct) || 0; const cls = pct > 0 ? 'up' : pct < 0 ? 'down' : 'flat'; const arrow = pct > 0 ? '▲' : pct < 0 ? '▼' : '·'; return ( {it.name} {fmt(it.price, it.dp || 2)} {arrow} {Math.abs(pct).toFixed(2)}% · ); }; const loop = [...items, ...items]; const dur = Math.max(28, items.length * 4.5); // seconds — consistent scroll speed return (
Live
{items.length ? (
{loop.map((it, i) => renderItem(it, i))}
) : (
Loading live market data…
)}
); } /* ------------ Sparkline ------------ */ function Sparkline({ seed = 1, up = true }) { const points = React.useMemo(() => { const n = 30; const arr = []; let v = 50; let rng = seed; const rand = () => { rng = (rng * 9301 + 49297) % 233280; return rng / 233280; }; for (let i = 0; i < n; i++) { const drift = up ? 0.6 : -0.4; v += (rand() - 0.5) * 6 + drift; arr.push(Math.max(10, Math.min(90, v))); } return arr; }, [seed, up]); const w = 200, h = 48; const path = points.map((p, i) => { const x = (i / (points.length - 1)) * w; const y = h - (p / 100) * h; return `${i === 0 ? 'M' : 'L'} ${x.toFixed(1)} ${y.toFixed(1)}`; }).join(' '); const area = `${path} L ${w} ${h} L 0 ${h} Z`; const color = up ? '#4ade80' : '#f87171'; const gradId = `sparkG-${seed}`; return ( ); } /* ------------ Star rating ------------ */ function Stars({ count = 5 }) { return (
{Array.from({ length: count }).map((_, i) => ( ))}
); } function Footer({ setPage }) { return ( ); } function HeroChart() { // generate a synthetic upward chart with noise const points = React.useMemo(() => { const n = 80; const arr = []; let v = 30; for (let i = 0; i < n; i++) { v += Math.sin(i * 0.4) * 1.2 + i / n * 1.4 + (Math.random() - 0.45) * 2.4; arr.push(Math.max(8, Math.min(92, v))); } return arr; }, []); const w = 400,h = 240; const path = points.map((p, i) => { const x = i / (points.length - 1) * w; const y = h - p / 100 * h; return `${i === 0 ? 'M' : 'L'} ${x.toFixed(1)} ${y.toFixed(1)}`; }).join(' '); const area = `${path} L ${w} ${h} L 0 ${h} Z`; // Drawing animation const [drawn, setDrawn] = useState(false); useEffect(() => {const t = setTimeout(() => setDrawn(true), 100);return () => clearTimeout(t);}, []); return ( {/* marker dots */} {[20, 40, 60].map((i) => { const p = points[i]; const x = i / (points.length - 1) * w; const y = h - p / 100 * h; return ; })} {/* end marker */} ); } function Marquee() { const items = [ 'Equities', 'Derivatives', 'FX', 'Crypto', 'Amazon Seller Central', 'Shopify', 'Walmart Marketplace', 'eBay', 'Inventory Sync', 'Order Automation', 'Backtesting', 'Risk Management']; // duplicate so loop is seamless const list = [...items, ...items]; return (
{list.map((s, i) => {s} )}
); } function Reveal({ children, delay = 0 }) { const ref = useRef(null); const [shown, setShown] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const obs = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) { setTimeout(() => setShown(true), delay); obs.unobserve(el); } }, { threshold: 0.12 }); obs.observe(el); return () => obs.disconnect(); }, [delay]); return (
{children}
); } Object.assign(window, { Logo, Nav, Footer, HeroChart, Marquee, Reveal, Ticker, Sparkline, Stars });