// Shared cross-page chrome + section primitives for the TensorHost // auxiliary-service pages. Reuses the marketing primitives already loaded // from site/shared.jsx (Button, Eyebrow, Section, StatusDot) and the theme // tokens defined on each host page. // Canonical page map — keep hrefs encoded so spaces survive on file:// too. const AUX_PAGES = [ { key: 'web', label: 'Web', href: 'Web%20Hosting.html' }, { key: 'storage', label: 'Storage', href: 'Cloud%20Storage.html' }, { key: 'email', label: 'Email', href: 'TensorMail.html' }, { key: 'gpu', label: 'GPU', href: 'GPU%20Rentals.html' }, ]; const HOME_HREF = 'TensorHost%20Home.html'; const CONTACT_HREF = 'TensorHost%20Home.html#contact'; const SIGNIN_HREF = 'Sign%20In.html'; const REGISTER_HREF = 'Register.html'; // Header links mirror the original home-page nav (anchors live on the home // Unified top-nav links — identical on every page. Anchors point back to the // home page; Privacy and FAQ are their own dedicated pages. const NAV_LINKS = [ { label: 'Home', href: HOME_HREF, active: 'home' }, { label: 'Privacy', href: 'Privacy.html', active: 'privacy' }, { label: 'FAQ', href: 'FAQ.html', active: 'faq' }, { label: 'Contact', href: 'Contact.html', active: 'contact' }, ]; // The four dedicated service pages, surfaced under the Services menu. const SERVICE_LINKS = [ { key: 'web', label: 'Web hosting', href: 'Web%20Hosting.html' }, { key: 'storage', label: 'Cloud storage', href: 'Cloud%20Storage.html' }, { key: 'email', label: 'Business email', href: 'TensorMail.html' }, { key: 'gpu', label: 'GPU rentals', href: 'GPU%20Rentals.html' }, ]; // ── Cross-page top navigation ────────────────────────────────────────────── function AuxNav({ theme, current }) { const [scrolled, setScrolled] = React.useState(false); const logo = theme === 'dark' ? 'site/assets/logo-mark-white.svg' : 'site/assets/logo-mark.svg'; React.useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 8); onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); return () => window.removeEventListener('scroll', onScroll); }, []); return ( ); } // ── Services dropdown ────────────────────────────────────────────────────── // "Services" header item; on hover/focus reveals the four dedicated pages. function ServicesMenu({ current }) { const [open, setOpen] = React.useState(false); const ref = React.useRef(null); const closeT = React.useRef(null); const enter = () => { clearTimeout(closeT.current); setOpen(true); }; const leave = () => { closeT.current = setTimeout(() => setOpen(false), 120); }; React.useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') setOpen(false); }; window.addEventListener('keydown', onKey); return () => { window.removeEventListener('keydown', onKey); clearTimeout(closeT.current); }; }, []); const onAux = current === 'services' || SERVICE_LINKS.some((s) => s.key === current); return (
{ e.currentTarget.style.color = 'var(--fg)'; if (!onAux && !open) e.currentTarget.style.borderBottomColor = 'var(--rule)'; }} onMouseOut={(e) => { e.currentTarget.style.color = (open || onAux) ? 'var(--fg)' : 'var(--fg-3)'; if (!onAux) e.currentTarget.style.borderBottomColor = 'transparent'; }}> Services
e.currentTarget.style.color = 'var(--fg-2)'} onMouseOut={(e) => e.currentTarget.style.color = 'var(--fg-4)'}>What we host →
{SERVICE_LINKS.map((s) => { const active = s.key === current; return ( { e.currentTarget.style.background = 'var(--bg-alt)'; e.currentTarget.style.color = 'var(--fg)'; }} onMouseOut={(e) => { e.currentTarget.style.background = active ? 'var(--bg-alt)' : 'transparent'; e.currentTarget.style.color = active ? 'var(--fg)' : 'var(--fg-2)'; }}> {active ? '●' : '○'} {s.label} ); })}
); } // ── Page hero scaffold ───────────────────────────────────────────────────── // Left: eyebrow + headline + tagline + lead + CTAs. Right: a visual slot. // Below: a 4-up stat strip. Visual is any node (the signature interactive). function PageHero({ eyebrow, title, tagline, lead, ctas, visual, stats }) { return (
{eyebrow}

{title}

{tagline && (

{tagline}

)}

{lead}

{ctas}
{visual}
{stats && (
{stats.map(([big, label, sub], i) => (
{big}
{label}
{sub}
))}
)}
); } // ── Feature grid ─────────────────────────────────────────────────────────── // features: [{ title, body, tag? }] function FeatureSection({ id = 'features', index, eyebrow, title, intro, features, cols = 3, bgAlt }) { return (
{eyebrow}

{title}

{intro &&

{intro}

}
{features.map((f, i) => )}
); } function FeatureCell({ f, n, cols }) { const [hover, setHover] = React.useState(false); // Top border on every cell after the first row; left border except first col. const firstCol = (n - 1) % cols === 0; const firstRow = n <= cols; return (
setHover(true)} onMouseLeave={() => setHover(false)} style={{ padding: 30, position: 'relative', borderLeft: firstCol ? 'none' : '1px solid var(--rule-soft)', borderTop: firstRow ? 'none' : '1px solid var(--rule-soft)', background: hover ? 'var(--bg-alt)' : 'var(--surface)', transition: 'background 140ms', }}>
{String(n).padStart(2, '0')} {f.tag && {f.tag}}

{f.title}

{f.body}

); } // ── Comparison table ─────────────────────────────────────────────────────── // cols: [{ label, highlight? }] rows: [{ label, cells: [str|node] }] // The highlighted column gets the accent treatment (TensorHost). function CompareSection({ id = 'compare', index, eyebrow, title, intro, cornerLabel = '', cols, rows, note, bgAlt }) { return (
{eyebrow}

{title}

{intro &&

{intro}

}
{cols.map((c) => ( ))} {rows.map((r) => ( {r.cells.map((v, i) => { const hl = cols[i] && cols[i].highlight; return ( ); })} ))}
{cornerLabel} {c.highlight ? {c.label} : c.label}
{r.label} {v}
{note &&
{note}
}
); } // ── "What we are NOT" — dark differentiator list ──────────────────────────── // items: [{ not, body }] function NotSection({ index, title, intro, items }) { return (
What we're not

{title}

{intro &&

{intro}

}
{items.map((it) => (
×
{it.not}
{it.body}
))}
); } // ── Closing CTA band (dark) ───────────────────────────────────────────────── const CONTACT_PAGE = 'Contact.html'; // Resolve a CTA label to its destination: "Talk to us" / "Contact" → contact // page; "Email …" → mailto; everything else → the standard contact route. function ctaHref(label) { const l = (label || '').toLowerCase(); if (l.startsWith('email')) return 'mailto:info@tensorhost.com'; if (l.includes('talk') || l.includes('contact')) return CONTACT_PAGE; return CONTACT_HREF; } function CtaBand({ index, title, lead, primaryLabel = 'Get started', secondaryLabel = 'Talk to us' }) { return (
Get started

{title}

{lead}

{primaryLabel} {secondaryLabel}
Toronto · Canadian residency
); } // ── shared style atoms ─────────────────────────────────────────────────────── const auxH2 = { fontSize: 'clamp(28px,3.2vw,40px)', fontWeight: 400, letterSpacing: '-0.02em', lineHeight: 1.15, margin: '16px 0 12px', color: 'var(--fg)' }; const auxMiniTag = { fontSize: 10, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'var(--fg-4)', border: '1px solid var(--rule-soft)', borderRadius: 2, padding: '3px 8px' }; const auxThp = (first) => ({ padding: '14px 16px', textAlign: first ? 'left' : 'center', fontWeight: 400, fontSize: 11, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--fg-3)', borderBottom: '2px solid var(--fg)', whiteSpace: 'nowrap', }); const auxTdp = { padding: '15px 16px' }; const auxInvPrimary = { fontFamily: 'inherit', fontSize: 15, padding: '14px 24px', borderRadius: 2, textDecoration: 'none', background: 'var(--inv-fg)', color: 'var(--inv-bg)', border: '1px solid var(--inv-fg)', display: 'inline-flex', gap: 8, alignItems: 'center' }; const auxInvSecondary = { fontFamily: 'inherit', fontSize: 15, padding: '14px 24px', borderRadius: 2, textDecoration: 'none', background: 'transparent', color: 'var(--inv-fg)', border: '1px solid var(--inv-rule)', display: 'inline-flex', gap: 8, alignItems: 'center' }; // ── Footer (cross-page) ────────────────────────────────────────────────────── function AuxFooter() { const cols = [ ['Services', [['Web hosting', 'Web%20Hosting.html'], ['Cloud storage', 'Cloud%20Storage.html'], ['Business email', 'TensorMail.html'], ['GPU rentals', 'GPU%20Rentals.html']]], ['Company', [['Home', HOME_HREF], ['Privacy', 'Privacy.html'], ['Pricing', HOME_HREF + '#pricing'], ['FAQ', 'FAQ.html'], ['Contact', CONTACT_HREF]]], ['Legal', [['Terms', 'Terms.html'], ['Privacy policy', 'Privacy%20Policy.html'], ['Data processing (DPA)', 'Data%20Processing.html']]], ]; return ( ); } Object.assign(window, { AUX_PAGES, HOME_HREF, CONTACT_HREF, SIGNIN_HREF, REGISTER_HREF, NAV_LINKS, SERVICE_LINKS, AuxNav, SiteNav: AuxNav, ServicesMenu, PageHero, FeatureSection, CompareSection, NotSection, CtaBand, AuxFooter, });