// shared.jsx — Verigo Global: brand mark, icons, navigation, footer, UI primitives // All exported to window for cross-file use. /* ── PALETTE ─────────────────────────────────────────────── */ const V = { purple: '#4B4FD9', purpleLight: '#6E72E8', purpleDark: '#3438B0', purpleSubtle: '#EBEBFA', orange: '#E8622A', orangeLight: '#F08050', orangeDark: '#C44E1E', orangeSubtle: '#FDF0EA', black: '#0A0A0A', charcoal: '#1E1E2E', ink: '#111118', g900: '#111118', g800: '#222230', g700: '#3A3A4A', g600: '#5A5A6A', g500: '#7A7A8A', g400: '#9A9AAA', g300: '#C0C0CC', g200: '#DCDCE8', g100: '#EDEDF5', g50: '#F5F5F7', white: '#FFFFFF' }; const MAXW = 1280; const FONT = "'DM Sans', system-ui, sans-serif"; /* ── SUPABASE (form inquiries) ────────────────────────────── Loaded via CDN script tag (window.supabase) on every page. The anon key below is the public/browser key — it is safe to ship client-side; write access is restricted by the `inquiries` table's Row Level Security policy (insert-only, no read). ── */ const SUPABASE_URL = 'https://vgktunjxooukoinvxqcl.supabase.co'; const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZna3R1bmp4b291a29pbnZ4cWNsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODIwODg3NzYsImV4cCI6MjA5NzY2NDc3Nn0.vyUi8XCNqiGJGqp_UtawGXFEQD7V5VCBpyAeo5RO54g'; function getSupabaseClient() { if (!window.supabase || !window.supabase.createClient) return null; if (!window.__vgSupabase) { window.__vgSupabase = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY); } return window.__vgSupabase; } // Called after a successful insert. Fires the admin-notification Edge Function // directly from the browser — no Database Webhook involved, so it isn't // affected by that feature's setup on any given Supabase project. Never // throws and never awaited by the caller: a slow or failed email send must // never block or fail the form itself, since the inquiry is already saved. function notifyAdmin(row) { try { fetch(SUPABASE_URL + '/functions/v1/send-inquiry-email', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + SUPABASE_ANON_KEY }, body: JSON.stringify({ record: { ...row, created_at: new Date().toISOString() } }), }).catch((e) => console.warn('Admin email notification failed (inquiry was still saved):', e)); } catch (e) { console.warn('Admin email notification failed (inquiry was still saved):', e); } } // Insert into the `inquiries` table, then fire the admin notification email. // Never throws and never blocks the calling form's existing UX (unlock/download/ // etc still happens even if this fails or the SDK hasn't loaded yet — this is // additive, not load-bearing). async function submitInquiry(payload) { try { const client = getSupabaseClient(); if (!client) { console.warn('Supabase not available; inquiry not saved remotely.'); return { error: 'no-client' }; } const row = { form_type: payload.form_type || 'unknown', name: payload.name || null, email: payload.email || null, company: payload.company || null, phone: payload.phone || null, framework: payload.framework || null, service_interest: payload.service_interest || null, message: payload.message || null, source_page: (typeof window !== 'undefined' && window.location) ? window.location.pathname : null, metadata: payload.metadata || null, }; const { error } = await client.from('inquiries').insert([row]); if (error) { console.error('Supabase insert failed:', error.message); return { error }; } notifyAdmin(row); return { error: null }; } catch (e) { console.error('submitInquiry error:', e); return { error: e }; } } /* ── BRAND MARK (split-color V with orange accent) ───────── */ const BrandMark = ({ size = 40, light = false }) => ; /* ── ICONS — lucide-style, 24×24, 1.75 stroke ───────────── */ const I = (paths) => ({ size = 24, color = 'currentColor', sw = 1.75 } = {}) => {paths}; const ICN = { shield: I(<>), search: I(<>), clipboard: I(<>), file: I(<>), activity: I(<>), users: I(<>), globe: I(<>), target: I(<>), layers: I(<>), award: I(<>), gauge: I(<>), refresh: I(<>), lock: I(<>), network: I(<>), building: I(<>), briefcase: I(<>), check: I(<>), arrow: I(<>), mapPin: I(<>), trending: I(<>), zap: I(<>), handshake: I(<>), book: I(<>), cpu: I(<>), server: I(<>), scale: I(<>), compass: I(<>), flag: I(<>), heart: I(<>), bolt: I(<>), doc: I(<>), star: I(<>) }; const Icon = ({ name, size = 24, color = 'currentColor', sw = 1.75 }) => { const C = ICN[name] || ICN.shield; return ; }; /* ── GEO WATERMARK ───────────────────────────────────────── */ const GeoWatermark = ({ color = V.purple, style }) => ; /* ── HERO WATERMARKS — one distinct, darker variant per page ─ */ const hexPts = (cx, cy, r) => [30, 90, 150, 210, 270, 330].map((a) => { const rad = a * Math.PI / 180; return `${(cx + r * Math.cos(rad)).toFixed(1)},${(cy + r * Math.sin(rad)).toFixed(1)}`; }).join(' '); const WM_VARIANTS = { // Home — angular faceted grid + nested squares arch: (c) => <> {[0, 1, 2, 3, 4].map((i) => )} {[0, 1, 2, 3].map((i) => )} {[154, 110, 66].map((s, i) => )} , // Services — concentric rings + radiating spokes rings: (c) => <> {[130, 96, 62, 30].map((r, i) => )} {[0, 30, 60, 90, 120, 150, 180].map((a, i) => {const rad = a * Math.PI / 180;return ;})} , // Toolkits — stacked layers + dot field stack: (c) => <> {[0, 1, 2, 3].map((i) => )} {[0, 1, 2, 3, 4].map((r) => [0, 1, 2, 3, 4].map((cc) => ))} , // Standards — hexagon tessellation hex: (c) => <> {[[305, 85], [365, 185], [305, 285], [200, 125], [200, 245], [140, 185]].map(([x, y], i) => )} , // Partners — network graph nodes: (c) => { const pts = [[305, 75], [372, 155], [330, 255], [215, 95], [165, 205], [260, 180], [115, 120]]; const edges = [[5, 0], [5, 1], [5, 2], [5, 3], [5, 4], [3, 0], [6, 3], [4, 2]]; return <> {edges.map(([a, b], i) => )} {pts.map(([x, y], i) => )} {pts.map(([x, y], i) => )} ; }, // About — orbit rings + crosshair orbit: (c) => <> , // Contact — dot matrix + diagonal + square grid: (c) => <> {Array.from({ length: 6 }).map((_, r) => Array.from({ length: 8 }).map((_, cc) => ))} }; const HeroWatermark = ({ variant = 'rings', color, opacity = 0.14, style }) => { const c = color || V.purpleDark; return ( ); }; /* ── HEADER ──────────────────────────────────────────────── */ const NAV = [ { id: 'services', label: 'Services' }, { id: 'training', label: 'Training' }, { id: 'toolkits', label: 'Toolkits' }, { id: 'standards', label: 'Standards' }, { id: 'partners', label: 'Partners' }, { id: 'resources', label: 'Resources' }, { id: 'about', label: 'About Us' }]; const Header = ({ page, onNav }) => { const active = (page || '').split(':')[0]; const [open, setOpen] = React.useState(false); const go = (dest) => { setOpen(false); onNav(dest); }; return (
go('home')} role="button"> Verigo Global
{open &&
{NAV.map((l) => {e.preventDefault();go(l.id);}} style={{ ...hd.mobileLink, ...(active === l.id ? hd.mobileLinkActive : {}) }}>{l.label} )}
}
); }; const hd = { root: { position: 'sticky', top: 0, zIndex: 100, background: 'rgba(255,255,255,0.92)', backdropFilter: 'blur(12px)', borderBottom: `1px solid ${V.g200}`, height: 68 }, inner: { maxWidth: MAXW, margin: '0 auto', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 32px' }, logo: { display: 'flex', alignItems: 'center', cursor: 'pointer' }, nav: { display: 'flex', gap: 2 }, link: { fontSize: 14, fontWeight: 500, color: V.g600, padding: '8px 14px', borderRadius: 7, textDecoration: 'none', transition: 'all 150ms ease-out', fontFamily: FONT }, linkActive: { color: V.purple, fontWeight: 600, background: V.purpleSubtle }, actions: { display: 'flex', gap: 10, alignItems: 'center' }, btnGhost: { fontFamily: FONT, fontSize: 13, fontWeight: 600, padding: '9px 16px', borderRadius: 8, cursor: 'pointer', background: 'transparent', color: V.black, border: `1.5px solid ${V.g200}`, transition: 'all 150ms' }, btnPrimary: { fontFamily: FONT, fontSize: 13, fontWeight: 600, padding: '9px 18px', borderRadius: 8, cursor: 'pointer', background: V.purple, color: '#fff', border: 'none', transition: 'all 150ms' }, toggle: { display: 'none', alignItems: 'center', justifyContent: 'center', width: 40, height: 40, flexShrink: 0, background: 'transparent', color: V.black, border: `1.5px solid ${V.g200}`, borderRadius: 8, cursor: 'pointer', padding: 0 }, mobilePanel: { position: 'absolute', top: '100%', left: 0, right: 0, background: '#fff', borderBottom: `1px solid ${V.g200}`, boxShadow: '0 16px 32px rgba(0,0,0,0.10)', display: 'flex', flexDirection: 'column', padding: '8px 20px 24px', zIndex: 99, maxHeight: 'calc(100vh - 68px)', overflowY: 'auto' }, mobileLink: { display: 'block', padding: '13px 4px', fontSize: 15.5, fontWeight: 600, color: V.g700, textDecoration: 'none', borderBottom: `1px solid ${V.g100}`, fontFamily: FONT }, mobileLinkActive: { color: V.purple }, mobileActions: { display: 'flex', flexDirection: 'column', gap: 10, marginTop: 16 } }; /* ── FOOTER ──────────────────────────────────────────────── */ const Footer = ({ onNav }) => { const cols = [ ['Services', [['Readiness Assessments', 'services'], ['Implementation Toolkits', 'toolkits'], ['Pre-Audit Preparation', 'services'], ['Formal Audits', 'services'], ['CCP Training', 'training']]], ['Standards', [['ISO 27001', 'standard:iso27001'], ['SOC 2', 'standard:soc2'], ['CMMC 2.0', 'standard:cmmc'], ['NIST', 'standard:nist']]], ['Resources', [['Webinars', 'resources'], ['Articles', 'resources'], ['Free Downloads', 'resources'], ['Knowledge Base', 'resources']]], ['Company', [['About Us', 'about'], ['Partners', 'partners'], ['Compliance by Design', 'about'], ['Start a Conversation', 'contact']]]]; return (

Compliance by Design. We embed recognized security frameworks into how your organization operates — so compliance is a state, not an event.

{['United States', 'United Kingdom', 'India', 'Singapore'].map((m) => {m} )}
{cols.map(([heading, items]) =>
{heading}
{items.map(([label, dest]) => {e.preventDefault();onNav(dest);}}>{label} )}
)}
© 2026 Verigo Global. All rights reserved. Information Security Compliance Consulting.
{['CISSP', 'CISM', 'ISO 27001 LA', 'CMMC CCA'].map((c) => {c})}
); }; const ScrollTopBtn = () => { const [vis, setVis] = React.useState(false); React.useEffect(() => { const h = () => setVis(window.pageYOffset > 500); window.addEventListener('scroll', h); return () => window.removeEventListener('scroll', h); }, []); if (!vis) return null; return ( ); }; const ft = { root: { background: V.charcoal, padding: '72px 0 0' }, inner: { maxWidth: MAXW, margin: '0 auto', padding: '0 32px' }, top: { display: 'grid', gridTemplateColumns: '360px 1fr', gap: 72, paddingBottom: 48, borderBottom: '1px solid rgba(255,255,255,0.08)' }, tagline: { fontSize: 14, color: 'rgba(255,255,255,0.5)', lineHeight: 1.65, margin: '0 0 22px', maxWidth: 320 }, markets: { display: 'flex', flexWrap: 'wrap', gap: 8 }, market: { fontSize: 11, fontWeight: 600, padding: '5px 11px', borderRadius: 9999, background: 'rgba(75,79,217,0.2)', color: '#9aa0f5', letterSpacing: '0.02em' }, cols: { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 24 }, col: { display: 'flex', flexDirection: 'column', gap: 11 }, colHead: { fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(255,255,255,0.35)', marginBottom: 4 }, colLink: { fontSize: 13.5, color: 'rgba(255,255,255,0.62)', textDecoration: 'none', transition: 'color 150ms', fontFamily: FONT }, bottom: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '22px 0', gap: 24, flexWrap: 'wrap' }, copy: { fontSize: 12, color: 'rgba(255,255,255,0.3)' }, legal: { display: 'flex', gap: 8 }, cred: { fontSize: 10, fontWeight: 600, padding: '3px 8px', borderRadius: 4, background: 'rgba(255,255,255,0.06)', color: 'rgba(255,255,255,0.4)', letterSpacing: '0.04em', fontFamily: "'JetBrains Mono', monospace" } }; /* ── UI PRIMITIVES ───────────────────────────────────────── */ const Overline = ({ children, color = V.orange, style }) =>
{children}
; const SectionHeader = ({ overline, title, sub, align = 'left', maxSub = 560, dark = false }) =>
{overline && {overline}}

{title}

{sub &&

{sub}

}
; const Btn = ({ children, kind = 'primary', onClick, style }) => { const base = { fontFamily: FONT, fontSize: 15, fontWeight: 600, padding: '14px 28px', borderRadius: 9, cursor: 'pointer', border: 'none', transition: 'all 150ms ease-out', display: 'inline-flex', alignItems: 'center', gap: 8 }; const kinds = { primary: { background: V.purple, color: '#fff' }, dark: { background: V.black, color: '#fff' }, accent: { background: V.orange, color: '#fff' }, ghost: { background: 'transparent', color: V.black, border: `1.5px solid ${V.g200}`, padding: '12.5px 26px' }, ghostLight: { background: 'transparent', color: '#fff', border: '1.5px solid rgba(255,255,255,0.25)', padding: '12.5px 26px' }, ghostBrand: { background: 'transparent', color: V.purple, border: `1.5px solid ${V.purple}`, padding: '12.5px 26px' } }; return ; }; const Tag = ({ children, color = V.g500, bg = V.g50, border = V.g200 }) => {children}; const Stat = ({ value, label, sub, color = V.black }) =>
{value} {label} {sub && {sub}}
; // Full-bleed CTA band reused across pages — muted grey-purple, square corners const CTABand = ({ overline = 'Ready to begin?', title, body, primary = 'Start a Conversation', onNav, dest = 'contact' }) =>
{overline}

{title}

{body}

onNav(dest)}>{primary}
; // Page hero band (subtle grey, distinct darker watermark per page) const PageHero = ({ overline, title, sub, children, variant = 'rings', wmColor, wmOpacity = 0.14 }) =>
{overline}

{title}

{sub &&

{sub}

} {children}
; const Section = ({ bg = '#fff', pad = '80px 0', children, style }) =>
{children}
; /* ── LEAD CAPTURE MODAL — reused for gated PDFs + evaluation ─ */ const LeadCaptureModal = ({ icon = 'lock', eyebrow, heading, intro, cta = 'Continue', note, onClose, onSubmit }) => { const [f, setF] = React.useState({ name: '', email: '', company: '', phone: '' }); const input = { fontFamily: FONT, fontSize: 14, color: V.black, background: '#fff', border: `1.5px solid ${V.g200}`, borderRadius: 8, padding: '11px 14px', outline: 'none', width: '100%' }; const label = { fontSize: 12.5, fontWeight: 600, color: V.black, display: 'block', marginBottom: 7 }; const field = (key, lbl, props = {}) =>
setF((v) => ({ ...v, [key]: e.target.value }))} {...props} />
; return (
e.stopPropagation()} style={{ background: '#fff', borderRadius: 16, maxWidth: 480, width: '100%', boxShadow: '0 24px 64px rgba(0,0,0,0.3)', overflow: 'hidden', maxHeight: '92vh', overflowY: 'auto' }}>
{eyebrow &&
{eyebrow}
}
{heading}
{e.preventDefault();onSubmit(f);}} className="vg-modal-pad" style={{ padding: '26px 32px 30px', display: 'flex', flexDirection: 'column', gap: 16 }}> {intro &&

{intro}

}
{field('name', 'Full name', { required: true, placeholder: 'Jane Smith' })} {field('email', 'Work email', { required: true, type: 'email', placeholder: 'jane@company.com' })}
{field('company', 'Company', { required: true, placeholder: 'Acme Technologies' })} {field('phone', 'Phone number', { required: true, type: 'tel', placeholder: '+1 555 000 1234' })}
{cta}

{note || "We'll only use your details to share this and follow up. No spam."}

); }; Object.assign(window, { V, MAXW, FONT, BrandMark, Icon, ICN, GeoWatermark, Header, Footer, ScrollTopBtn, Overline, SectionHeader, Btn, Tag, Stat, CTABand, PageHero, Section, HeroWatermark, LeadCaptureModal, submitInquiry });