/* global React, Wordmark, Eyebrow, LockPill, Icon, IntentBadge, LockTile, TrustStrip, Footer, MobileStatusBar, formatPhone, submitAndGenerate */ const { useState, useEffect, useMemo, useRef } = React; // ═══════════════════════════════════════════════════════════ // PILLARS — animated three-card story // ═══════════════════════════════════════════════════════════ const Pillars = ({ accent, isMobile }) => { const [visible, setVisible] = useState(false); const [animKey, setAnimKey] = useState(0); const ref = React.useRef(null); React.useEffect(() => { const node = ref.current; if (!node) return; // Find the closest scrolling ancestor (viewport-scroll inside the device frame) let root = node.parentElement; while (root && root !== document.body) { const oy = getComputedStyle(root).overflowY; if (oy === 'auto' || oy === 'scroll') break; root = root.parentElement; } const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { setVisible(true); setAnimKey((k) => k + 1); } }); }, { root: root === document.body ? null : root, threshold: 0.15 }); io.observe(node); return () => io.disconnect(); }, []); const stagger = (i) => ({ animationDelay: `${0.12 + i * 0.18}s` }); const cls = (i) => `pillar-card${visible ? ' in' : ''}`; return (
{/* Pillar 01 — Stay anonymous */}
CONFIDENTIAL
01

Stay anonymous

No public listing. Your name, your business, your team — never disclosed without your written consent.

{/* Pillar 02 — Real market data */}
{[0.35, 0.55, 0.42, 0.78, 0.65, 0.92, 0.71, 0.58, 0.84].map((h, i) => (
))}
23 matches
5.4–6.8× EBITDA
02

Real market data

See actual buyer counts, valuation ranges, and deal terms — drawn from current mandates, not estimates.

{/* Pillar 03 — No obligation */}
{/* main horizontal path */} {/* branching dashed path going up (continue) */} {/* branching dashed path going down (walk away) */} {/* nodes */} Submit You decide Continue Walk away
03

No obligation

It costs nothing. Walk away with your numbers. Or take the next step on your timeline, not ours.

); }; // ═══════════════════════════════════════════════════════════ // HOW IT WORKS — sequenced timeline with active-step illustrations // ═══════════════════════════════════════════════════════════ const HOW_STEPS = [ { n: '01', t: 'Answer questions', b: 'Industry, size, timing. Eight banded multiple-choice fields.', meta: '~90 seconds' }, { n: '02', t: 'See your match count', b: 'A real number drawn from active buyer mandates today.', meta: 'Live data' }, { n: '03', t: 'Review valuation range', b: 'Comparable deal multiples specific to your industry & size.', meta: 'Industry comps' }, { n: '04', t: "Decide what's next", b: 'Schedule a confidential next step — or just keep your numbers.', meta: 'No obligation' }, ]; const HowItWorks = ({ accent, isMobile }) => { const [active, setActive] = useState(0); const [autoplay, setAutoplay] = useState(false); const [hasStarted, setHasStarted] = useState(false); const ref = useRef(null); // Trigger autoplay when section enters view useEffect(() => { const node = ref.current; if (!node) return; let root = node.parentElement; while (root && root !== document.body) { const oy = getComputedStyle(root).overflowY; if (oy === 'auto' || oy === 'scroll') break; root = root.parentElement; } const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting && !hasStarted) { setHasStarted(true); setAutoplay(true); } }); }, { root: root === document.body ? null : root, threshold: 0.25 }); io.observe(node); return () => io.disconnect(); }, [hasStarted]); // Autoplay loop — advance every 2.6s useEffect(() => { if (!autoplay) return; const id = setInterval(() => { setActive((a) => (a + 1) % HOW_STEPS.length); }, 2600); return () => clearInterval(id); }, [autoplay]); const handleStepClick = (i) => { setAutoplay(false); setActive(i); }; return (
{/* Timeline rail */} {!isMobile && (
{HOW_STEPS.map((_, i) => ( ))}
)} {/* Steps grid */}
{HOW_STEPS.map((s, i) => { const isActive = i === active; const isPast = i < active; return (
handleStepClick(i)} onMouseEnter={() => !isMobile && handleStepClick(i)} >
{s.n}

{s.t}

{s.b}

{s.meta}
); })}
); }; // Per-step illustrations — re-key on 'active' to restart animations const HowIllus = ({ n, active, accent }) => { const key = active ? 'on' : 'off'; if (n === 0) { // Question fields filling in return ( {[0, 1, 2].map((i) => ( ))} ); } if (n === 1) { // Match counter ticking up return ( 2 3 MATCHES {[-32, -18, 0, 18, 32].map((dx, i) => ( ))} ); } if (n === 2) { // Valuation range bracket sliding in return ( 5.4× 6.8× EBITDA {[20, 40, 60, 80, 100, 120, 140, 160, 180].map((x, i) => ( ))} ); } if (n === 3) { // Calendar slot getting selected return ( {[0, 1, 2, 3].map((row) => ( [0, 1, 2, 3, 4].map((col) => { const idx = row * 5 + col; const isPick = row === 2 && col === 3; return ( ); }) ))} CONFIDENTIAL CALL · 30 MIN ); } return null; }; // ═══════════════════════════════════════════════════════════ // SELLER LANDING // ═══════════════════════════════════════════════════════════ const SellerLanding = ({ bp, navigate, accent, density }) => { const isMobile = bp === 'mobile'; const isTablet = bp === 'tablet'; const padX = isMobile ? 24 : isTablet ? 56 : 80; const [scrolled, setScrolled] = useState(false); const [openMenu, setOpenMenu] = useState(null); // 'insights' | 'about' | null const [drawerOpen, setDrawerOpen] = useState(false); const scrollRef = useRef(null); useEffect(() => { const el = scrollRef.current; if (!el) return; const onScroll = () => { setScrolled(el.scrollTop > 200); setOpenMenu(null); }; el.addEventListener('scroll', onScroll); return () => el.removeEventListener('scroll', onScroll); }, []); useEffect(() => { const onClick = (e) => { if (!e.target.closest('[data-nav-menu]')) setOpenMenu(null); }; document.addEventListener('click', onClick); return () => document.removeEventListener('click', onClick); }, []); return (
{/* Top nav */}
{isMobile ? (
setDrawerOpen(true)} />
) : (
How it works { e.preventDefault(); navigate('who-buys'); }} style={{ color: 'var(--ink-3)' }}>Who buys {/* Insights dropdown */}
{openMenu === 'insights' && ( setOpenMenu(null)} /> )}
{/* About dropdown */}
{openMenu === 'about' && ( setOpenMenu(null)} /> )}
{ e.preventDefault(); navigate('buyer-landing'); }} style={{ color: 'var(--ink-4)', fontSize: 12 }}>For buyers
)}
{isMobile && setDrawerOpen(false)} navigate={navigate} currentScreen="seller-landing" />} {/* Hero */}
{/* ornament rule */}
Confidential Matching · Est. 2025

See if buyers are interested in your business.

1,000+ vetted PE firms, search funds, and family offices have told us exactly what they're looking for. Answer a couple of questions and we'll show you how many match — anonymously, free, no listing.

Takes about 90 seconds.
{/* Trust strip */}
Trusted by owners across 47 states · $8.2B in active buyer demand
{/* Hero meta-row */} {!isMobile && (
Vol. I · buyside.market
)}
{/* Press strip */}
As mentioned in {isMobile ? (
{[...Array(2)].map((_, r) => ( M&A Source Axial HVAC Insider ACG ))}
) : (
M&A Source Axial HVAC Insider ACG
)}
{/* Three pillars */}
{/* How it works */}
How it works

Four steps. Roughly ninety seconds.

Designed to be the lowest-friction way to find out where you stand.
{/* Editorial quote / institutional positioning */} {!isMobile && (
Why now

The traditional path — list publicly, attract calls, lose control of the narrative — was built for a buyer's market. This is the inverse. Buyers tell us what they want. We show you whether you're a fit. You decide what happens next.

From the founding letter, 2025
)} {/* Testimonials */}
From owners who matched
{[ { q: 'I matched 11 buyers and ended up closing with one of them six months later. I never had to put a sign in the window.', who: 'HVAC owner · $4M EBITDA · Texas' }, { q: "I just wanted to know what my number was. The fact that the answer was real and specific to my industry — that's what got me to call.", who: 'MSP founder · $2.3M EBITDA · Florida' }, { q: "My CPA recommended Buyside. The most useful 90 seconds I've spent thinking about my exit in the last decade.", who: 'Distribution · $6.8M EBITDA · Georgia' }, ].map((t, i) => (

"{t.q}"

— {t.who}
))}
{/* Trust strip */}
{/* Secondary CTA */}

Find out what your business is worth to today's buyers.

Free · Confidential · No listing
{/* Sticky mobile CTA */} {isMobile && scrolled && (
)}
); }; // ═══════════════════════════════════════════════════════════ // SELLER WIZARD — 8 questions // ═══════════════════════════════════════════════════════════ const WIZARD_QUESTIONS = [ { id: 'industry', eyebrow: 'Question 01 of 08', title: 'What industry is your business in?', subtitle: 'Pick the closest match. We use NAICS to find adjacencies.', type: 'select', options: ['HVAC / Plumbing / Electrical', 'IT Services / MSP', 'Manufacturing', 'Distribution / Wholesale', 'Healthcare Services', 'Professional Services', 'Construction / Trades', 'Food & Beverage', 'Transportation / Logistics', 'Auto Services / Collision', 'Landscaping / Lawn Care', 'Waste Management / Environmental', 'Staffing / Recruiting', 'Insurance Agency', 'Home Services / Restoration', 'Veterinary / Animal Health', 'Dental / Medical Practice', 'E-Commerce / DTC', 'SaaS / Software', 'Engineering / Architecture', 'Commercial Cleaning / Janitorial', 'Fitness / Wellness', 'Other'], }, { id: 'revenue', eyebrow: 'Question 02 of 08', title: 'Annual revenue range?', subtitle: "Trailing twelve months is fine. We don't need exact figures.", type: 'select', options: ['Under $5M', '$5M – $10M', '$10M – $25M', '$25M – $50M', '$50M – $100M', 'Over $100M'], }, { id: 'ebitda', eyebrow: 'Question 03 of 08', title: 'EBITDA range?', subtitle: 'Earnings before interest, taxes, depreciation, and amortization.', type: 'select', options: ['Under $1M', '$1M – $2M', '$2M – $5M', '$5M – $10M', 'Over $10M', 'Not sure'], }, { id: 'years', eyebrow: 'Question 04 of 08', title: 'How long have you owned the business?', type: 'select', options: ['Less than 5 years', '5 – 10 years', '10 – 20 years', 'Over 20 years'], }, { id: 'state', eyebrow: 'Question 05 of 08', title: 'What state are you headquartered in?', subtitle: 'For buyers with regional focus.', type: 'state', }, { id: 'transition', eyebrow: 'Question 06 of 08', title: 'After a sale, how long would you stay involved?', subtitle: "There's no wrong answer. Buyers vary widely on this.", type: 'select', options: ['Walk away immediately', 'Stay 6 – 12 months', 'Stay 1 – 2 years', 'Stay 3+ years', 'Open to anything'], }, { id: 'intent', eyebrow: 'Question 07 of 08', title: 'Where are you in the process?', subtitle: 'Just curious is a totally valid answer.', type: 'select', options: [ { v: 'curious', label: 'Just curious about the market', sub: "You haven't decided anything yet." }, { v: 'later', label: 'Planning to sell in 1 – 2 years', sub: 'Thinking ahead, gathering information.' }, { v: 'soon', label: 'Planning to sell in 6 – 12 months', sub: 'Starting to take it seriously.' }, { v: 'actively',label: 'Actively exploring offers', sub: "You'd talk to the right buyer this week." }, ], rich: true, }, { id: 'contact', eyebrow: 'Question 08 of 08', title: 'Where should we send your results?', subtitle: 'We never sell, share, or display this information.', type: 'contact', }, ]; const STATE_LIST = ['California', 'Texas', 'Florida', 'New York', 'Illinois', '— — —', 'Alabama','Alaska','Arizona','Arkansas','Colorado','Connecticut','Delaware','Georgia','Hawaii','Idaho','Indiana','Iowa','Kansas','Kentucky','Louisiana','Maine','Maryland','Massachusetts','Michigan','Minnesota','Mississippi','Missouri','Montana','Nebraska','Nevada','New Hampshire','New Jersey','New Mexico','North Carolina','North Dakota','Ohio','Oklahoma','Oregon','Pennsylvania','Rhode Island','South Carolina','South Dakota','Tennessee','Utah','Vermont','Virginia','Washington','West Virginia','Wisconsin','Wyoming']; const SellerWizard = ({ bp, navigate, answers, setAnswers, accent, setMatchResults, submitting, setSubmitting }) => { const isMobile = bp === 'mobile'; const isTablet = bp === 'tablet'; const padX = isMobile ? 24 : isTablet ? 80 : 120; const [stepIdx, setStepIdx] = useState(0); const step = WIZARD_QUESTIONS[stepIdx]; const progress = ((stepIdx + 1) / WIZARD_QUESTIONS.length) * 100; const value = answers[step.id]; const canProceed = useMemo(() => { if (step.id === 'contact') return answers.email && /^\S+@\S+\.\S+$/.test(answers.email) && answers.phone && answers.phone.replace(/\D/g,'').length >= 10; return !!value; }, [step, value, answers]); const next = async () => { if (!canProceed || submitting) return; if (stepIdx === WIZARD_QUESTIONS.length - 1) { setSubmitting(true); try { const results = await submitAndGenerate(answers); setMatchResults(results); navigate('seller-results'); } catch (e) { console.error('Submit failed:', e); alert('Something went wrong. Please try again.'); } finally { setSubmitting(false); } } else { setStepIdx(stepIdx + 1); } }; const back = () => { if (stepIdx === 0) navigate('seller-landing'); else setStepIdx(stepIdx - 1); }; const setVal = (v) => setAnswers({ ...answers, [step.id]: v }); return (
{/* Top bar */}
Confidential
{/* Progress */}
{step.eyebrow} {Math.round(progress)}% complete
{/* Question */}

{step.title}

{step.subtitle && (

{step.subtitle}

)}
{step.type === 'select' && !step.rich && (
{step.options.map((opt) => { const sel = value === opt; return ( ); })}
)} {step.type === 'select' && step.rich && (
{step.options.map((opt) => { const sel = value === opt.v; const isCurious = opt.v === 'curious'; return ( ); })}
)} {step.type === 'state' && (
{STATE_LIST.map((s, i) => { if (s.startsWith('—')) return
; const sel = value === s; const isTop = i < 5; return ( ); })}
)} {step.type === 'contact' && (
setAnswers({...answers, email: e.target.value})} />
setAnswers({...answers, phone: formatPhone(e.target.value)})} autoComplete="off" />
By submitting, you agree to our Privacy Policy. We do not sell or share contact information. Ever.
)}
{/* Bottom action bar */}
); }; // ═══════════════════════════════════════════════════════════ // SELLER RESULTS — cinematic reveal // ═══════════════════════════════════════════════════════════ const SAMPLE_BUYERS = [ { type: 'PE Firm', n: '#147', aum: '$1.2B AUM', focus: 'Industrials roll-ups · Sun Belt', recent: 'Closed 3 deals in last 12 mo', initials: 'PE' }, { type: 'Search Fund', n: '#89', aum: 'MBA-backed · $40M committed', focus: 'Owner-operator transition · Services', recent: 'New mandate · 2 weeks ago', initials: 'SF' }, { type: 'Family Office', n: '#34', aum: '$320M family capital', focus: 'Long hold · Recurring revenue', recent: 'Active across 4 verticals', initials: 'FO' }, ]; const SellerResults = ({ bp, navigate, answers, accent, reveal = 'cinematic', matchResults }) => { const isMobile = bp === 'mobile'; const isTablet = bp === 'tablet'; const padX = isMobile ? 24 : isTablet ? 56 : 80; // Use API results or fallback to defaults const r = matchResults || {}; const target = r.matchCount || 23; const breakdown = r.buyerBreakdown || { pe: 14, searchFunds: 6, familyOffices: 3, strategic: 0 }; const valuation = r.valuationRange || { lowMultiple: 4.8, highMultiple: 6.2, basis: 'EBITDA' }; const marketData = r.marketData || { typicalDealStructure: 'Majority buyout', avgTimeToClose: '3–5 months', earnoutPrevalence: '35% of deals include an earnout' }; const apiBuyers = r.anonymousBuyers || []; // Interleave buyer types for variety in the first 6 const allBuyers = apiBuyers.length > 0 ? (() => { const byType = {}; apiBuyers.forEach(b => { (byType[b.type] = byType[b.type] || []).push(b); }); const types = Object.keys(byType); const result = []; let idx = 0; while (result.length < apiBuyers.length) { const type = types[idx % types.length]; if (byType[type].length > 0) result.push(byType[type].shift()); idx++; if (types.every(t => byType[t].length === 0)) break; } return result; })() : SAMPLE_BUYERS; const [showAllBuyers, setShowAllBuyers] = useState(false); const visibleBuyers = showAllBuyers ? allBuyers : allBuyers.slice(0, 6); const [count, setCount] = useState(0); useEffect(() => { if (reveal === 'instant') { setCount(target); return; } const dur = reveal === 'subtle' ? 1200 : 1800; const start = performance.now(); let raf; const tick = (t) => { const p = Math.min(1, (t - start) / dur); const eased = 1 - Math.pow(1 - p, 3); setCount(Math.round(target * eased)); if (p < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [reveal]); const stagger = reveal === 'cinematic' ? 0.18 : reveal === 'subtle' ? 0.08 : 0; return (
{/* Top bar */}
Match Report · {new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
{/* Hero result */}
Your matches · Generated just now
{count}

You match {target} active buyers.

Based on your industry ({answers.industry || 'HVAC / Plumbing / Electrical'}), size ({answers.revenue || '$10M – $25M'}), and location ({answers.state || 'Texas'}).

{[ { n: breakdown.pe, label: 'PE firms' }, { n: breakdown.searchFunds, label: 'Search funds' }, { n: (breakdown.familyOffices || 0) + (breakdown.strategic || 0), label: 'Family / Strategic' }, ].map((b, i) => (
0 ? '1px solid var(--rule)' : 'none', }}>
{b.n}
{b.label}
))}
{/* Market data block */}
Market data · {answers.industry || 'HVAC / Plumbing / Electrical'} n=42 closed comps · last 18 mo
{[ { k: 'Estimated valuation', v: `${valuation.lowMultiple}× – ${valuation.highMultiple}×`, sub: `${valuation.basis} · majority transactions` }, { k: 'Typical deal structure', v: marketData.typicalDealStructure, sub: marketData.earnoutPrevalence }, { k: 'Average time to close', v: marketData.avgTimeToClose, sub: 'From mandate to wire' }, ].map((r, i) => (
{r.k}
{r.v}
{r.sub}
))}
{/* Anonymous buyer cards */}
Matched buyers

Your {target} matches.

Identities are never revealed without your written consent.
{visibleBuyers.map((b, i) => (
Verified
{b.type}
{b.label || (b.type + ' ' + (b.n || ''))}
Capital
{b.aum}
Focus
{b.focus}
Recent activity
{b.recentActivity || b.recent}
))}
{!showAllBuyers && allBuyers.length > 6 && (
)}
{/* Capex CTA */}
Capex Advisory · A confidential next step

Want to learn more confidentially?

Capex Advisory is the sell-side advisory firm that operates Buyside's matching service. A 30-minute confidential call walks you through your specific matches, valuation methodology, and what a real process would look like — at your pace, on your terms.

No obligation. Your information is not shared with buyers without your written consent.
); }; // ═══════════════════════════════════════════════════════════ // CONFIDENTIAL CALL BOOKING // ═══════════════════════════════════════════════════════════ const BookingFlow = ({ bp, navigate, answers, accent }) => { const isMobile = bp === 'mobile'; const isTablet = bp === 'tablet'; const padX = isMobile ? 24 : isTablet ? 48 : 80; const [step, setStep] = useState('schedule'); // schedule | confirm | done const [date, setDate] = useState(null); const [time, setTime] = useState(null); const [note, setNote] = useState(''); const [booking, setBooking] = useState(false); const confirmBooking = async () => { setBooking(true); try { const dateStr = date.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }); await fetch('/api/book', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ date: dateStr, time, note, email: answers.email || '', phone: answers.phone || '', industry: answers.industry || '', revenue: answers.revenue || '', ebitda: answers.ebitda || '', }), }); } catch (e) { console.error('Booking email failed:', e); } setBooking(false); setStep('confirm'); }; const today = new Date(); const days = Array.from({length: isMobile ? 7 : 14}, (_, i) => { const d = new Date(today); d.setDate(today.getDate() + i + 1); return d; }); const times = ['9:00 AM', '10:00 AM', '11:30 AM', '1:00 PM', '2:30 PM', '4:00 PM']; return (
{/* Top bar */}
{step === 'schedule' && (
Confidential Call

Schedule a 30-minute call with your advisor.

A partner from Capex Advisory — our sell-side partner firm — will walk you through your matches, valuation, and process at your pace.

Select a date
{days.map((d, i) => { const sel = date && d.toDateString() === date.toDateString(); const isWeekend = d.getDay() === 0 || d.getDay() === 6; return ( ); })}
Select a time {date && · {date.toLocaleDateString('en-US',{weekday:'long', month:'long', day:'numeric'})}}
{times.map((t) => ( ))}
All times shown in your local timezone (CT)
Anything you want your advisor to know? (optional)