/* Axon.ps website kit — shared primitives. Exported to window for cross-file use. */ const AXON_PRODUCTS = [ { id: 'plexus', name: 'Plexus', tagline: 'AI-powered scientific research platform', line: 'A workflow platform for researchers — AI-assisted writing, reference verification, and citation accuracy.', features: ['AI-assisted paper writing', 'Reference verification', 'Citation accuracy checking'], status: 'live' }, { id: 'nowah', name: 'Nowah', tagline: 'Smart platform for municipalities & local councils', line: 'AI-powered updates on projects, grants and initiatives across Palestinian municipalities, with a full institution directory.', features: ['Municipality project tracking', 'Grant notifications', 'Institution database'], status: 'live' }, { id: 'synap', name: 'Synap', tagline: 'Local AI infrastructure & workflow solutions', line: 'Run AI models on your own hardware. We supply specialized machines and build custom, sovereign workflows.', features: ['Local model deployment', 'Specialized hardware', 'Custom automation'], status: 'live' }, { id: 'madar', name: 'Madar', tagline: 'Privacy-first VPN for mobile & desktop', line: 'A next-generation VPN built with privacy and performance at its core. Madar means "orbit".', features: ['Mobile & desktop apps', 'No-logs policy', 'High-speed servers'], status: 'soon' }, ]; function Eyebrow({ children }) { return {children}; } function StatusChip({ status }) { return status === 'live' ? Live : Coming soon; } function Button({ variant = 'signal', children, href = '#', onClick, type }) { const cls = 'btn btn-' + variant; if (type === 'submit' || onClick) return ; return {children}; } /* Axon mark — recreation of components/AxonLogo.tsx, re-pigmented for the manuscript palette. Signal waveform replays on hover + on an idle cycle. */ function AxonMark({ size = 40, wordmark = false, theme = 'paper', tone }) { const ref = React.useRef(null); const onPaper = theme === 'paper'; const ink = tone || (onPaper ? 'var(--ink-0)' : 'var(--night-text)'); const play = () => { const el = ref.current; if (!el) return; el.style.transition = 'none'; el.style.strokeDashoffset = '180'; void el.getBoundingClientRect(); el.style.transition = 'stroke-dashoffset 0.9s var(--ease)'; el.style.strokeDashoffset = '0'; }; React.useEffect(() => { const t = setTimeout(play, 300); const iv = setInterval(play, 9000); return () => { clearTimeout(t); clearInterval(iv); }; }, []); return ( {wordmark && ( axon .ps )} ); } /* Open a product detail sheet from anywhere — decoupled event so hero, orbit and ecosystem (separate script files) all drive the same overlay. */ function openProduct(id, originEl) { window.dispatchEvent(new CustomEvent('axon:product', { detail: { id, origin: originEl || null } })); } /* Product lens — the real product logo unified inside one Axon frame. The img is zoomed slightly to crop each logo's mismatched native ring, then a single consistent ink hairline (+ faint gold inner) is drawn on top. This is how four different marks read as one coherent product system. Interactive by default: click / Enter / Space opens the product sheet. */ function ProductLens({ id, size = 64, live = true, dim = false, interactive = true, name }) { const Tag = interactive ? 'button' : 'span'; const handlers = interactive ? { onClick: (e) => openProduct(id, e.currentTarget), 'aria-haspopup': 'dialog', 'aria-label': 'View ' + (name || id) + ' details', type: 'button', } : { 'aria-label': id + ' logo' }; return ( {live && } ); } /* legacy letterform sigil — kept for the brand specimen card */ function Sigil({ id, letter, size = 56, live = true }) { return ( {letter} ); } /* Signal divider — a hairline with a traveling crimson pulse */ function SignalDivider() { return (
); } /* reveal-on-scroll hook — content is ALWAYS visible by default; JS only adds a subtle entrance. Bulletproof: if IO never fires (e.g. inside a scroll container), a failsafe timer reveals everything so nothing is ever stuck. */ function useReveal() { const ref = React.useRef(null); React.useEffect(() => { const el = ref.current; if (!el) return; const nodes = Array.prototype.slice.call(el.querySelectorAll('.fade-up')); if (!nodes.length) return; // opt into the hidden pre-state only now that JS is running nodes.forEach(n => n.classList.add('pre')); const reveal = (n) => { n.classList.remove('pre'); n.classList.add('in'); }; let io = null; if ('IntersectionObserver' in window) { io = new IntersectionObserver((es) => es.forEach(e => { if (e.isIntersecting) { reveal(e.target); io.unobserve(e.target); } }), { threshold: 0.08, rootMargin: '0px 0px -5% 0px' }); nodes.forEach(n => io.observe(n)); } else { nodes.forEach(reveal); } // failsafe — never leave content invisible const t = setTimeout(() => nodes.forEach(reveal), 1600); return () => { if (io) io.disconnect(); clearTimeout(t); }; }, []); return ref; } /* canonical product detail copy (factual, no invented claims) */ const PRODUCT_DETAIL = { plexus: { name: 'Plexus', category: 'Scientific research platform', desc: 'AI-powered scientific research platform for researchers — writing support, reference verification, and citation accuracy.', soon: false, href: '/products/plexus' }, nowah: { name: 'Nowah', category: 'Municipalities & local councils', desc: 'A platform for municipalities and local councils — project updates, grant tracking, and institutional information.', soon: false, reveal: true, href: '/products/nowah' }, synap: { name: 'Synap', category: 'Local AI infrastructure', desc: 'Local AI infrastructure and workflow solutions — private model deployment, specialized hardware, and custom automation.', soon: false, href: '/products/synap' }, madar: { name: 'Madar', category: 'Privacy-first VPN', desc: 'Privacy-first VPN for mobile and desktop — built around private, reliable access and performance.', soon: true, reveal: true, href: '/products/madar' }, }; Object.assign(window, { AXON_PRODUCTS, PRODUCT_DETAIL, Eyebrow, StatusChip, Button, AxonMark, Sigil, ProductLens, openProduct, SignalDivider, useReveal });