/* global React, ReactDOM, Logo, Phone, AdminApprovals, AdminAudit, TweaksPanel, useTweaks, TweakSection, TweakColor, TweakRadio, injectLogoKeyframes */ const { useEffect: useEffectApp, useState: useStateApp, useRef: useRefApp, useMemo: useMemoApp } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "#F09A3A", "motion": "scroll" } /*EDITMODE-END*/; const ACCENTS = { '#F09A3A': { top: '#F8B45E', bot: '#ED8930' }, // orange '#E5428E': { top: '#F058A5', bot: '#DB347D' }, // pink '#5BAFE4': { top: '#76BFEC', bot: '#4FA4DD' }, // blue '#4FC2A0': { top: '#6BD7B0', bot: '#41B895' } // green }; function applyAccent(hex) { const stops = ACCENTS[hex] || ACCENTS['#F09A3A']; document.documentElement.style.setProperty('--accent', hex); document.documentElement.style.setProperty('--accent-top', stops.top); document.documentElement.style.setProperty('--accent-bot', stops.bot); } /* ============================================================ useScrollY, listens to window scroll ============================================================ */ function useScrollY() { const [y, setY] = useStateApp(0); useEffectApp(() => { let raf = 0; const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(() => setY(window.scrollY)); }; window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); return () => {window.removeEventListener('scroll', onScroll);cancelAnimationFrame(raf);}; }, []); return y; } /* ============================================================ FadeInWhenVisible ============================================================ */ function FadeIn({ children, delay = 0, as: As = 'div', ...props }) { const ref = useRefApp(null); const [seen, setSeen] = useStateApp(false); useEffectApp(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) {setSeen(true);io.disconnect();} }); }, { threshold: 0.12 }); io.observe(el); return () => io.disconnect(); }, []); return ( {children} ); } /* ============================================================ NAVBAR ============================================================ */ function Nav({ onTweaksToggle }) { const y = useScrollY(); return ( ); } /* ============================================================ HERO ============================================================ */ function Hero({ motion }) { const y = useScrollY(); // map first 600px of scroll to 0..1 for logo compress const scrollLink = motion === 'scroll' ? Math.min(1, Math.max(0, y / 600)) : 0; return (
An alumni network, built for institutes

A modern alumni network
your institute can actually
keep alive.

Verveli gives alumni a useful mobile space, and gives institute teams the control, verification, and visibility they need to keep it alive past the launch week.

Bring Verveli to your institute See the product
Member mobile app Institute admin console 60-second join
); } /* ============================================================ PROBLEM ============================================================ */ function Problem() { const items = [ { n: '01', t: 'Activity scattered everywhere', b: 'WhatsApp groups, LinkedIn DMs, batch spreadsheets, the occasional annual event. Nothing connects to the institute.' }, { n: '02', t: 'Institutes lose visibility', b: 'Who joined? Who responded? Which programs landed? Most coordinators are guessing from screenshots.' }, { n: '03', t: 'Alumni lose continuity', b: 'Numbers change, groups die quietly, classmates are unreachable a few years after graduation.' }, { n: '04', t: 'Coordinators burn out', b: 'One person ends up holding the whole community together by hand. It works until they move on.' }]; return (
The problem

Most alumni networks die quietly,
between WhatsApp and the next reunion.

{items.map((it, i) => {it.n}

{it.t}

{it.b}

)}
); } /* ============================================================ PRODUCT, Today / Signals / Alumni / Community tabs ============================================================ */ const PRODUCT_TABS = [ { id: 'today', label: 'Today', eyebrow: 'Today tab', title: <>A daily reason to open the app., body: 'A single feed of daily content, events, photos, news and questions from the institute. Calm, paced, and run by your team, not by chance.', features: [ { h: 'Daily questions', p: 'A small prompt every day. Alumni answer in one tap, see their batchmates.' }, { h: 'Events & RSVPs', p: 'Chapter events, alumni days, returning-to-campus visits, all in one place.' }, { h: 'Photos & news', p: 'Curated by the institute. Archive built up automatically.' }] }, { id: 'signals', label: 'Signals', eyebrow: 'Signals tab', title: <>Jobs, referrals, help requests, moving between alumni., body: 'A purpose-built feed for the practical asks that today drown in unmoderated WhatsApp groups: who’s hiring, who can refer, who works at X.', features: [ { h: 'Hiring & referrals', p: 'Posted by alumni, visible to the network, attributable.' }, { h: '“Who works at X?”', p: 'Find alumni at a target company or city, with their consent.' }, { h: 'Help requests', p: 'Asking the network for advice, intros, or a small favour, without spam.' }] }, { id: 'alumni', label: 'Alumni', eyebrow: 'Alumni tab', title: <>Profiles that actually stay current., body: 'Verified profiles, curated by your institute. Search by batch, city, company, or program. No more chasing batch reps for an updated list.', features: [ { h: 'Verified joining', p: 'Phone OTP + institute code, with optional coordinator approval.' }, { h: 'Curated profiles', p: 'Featured alumni rotate weekly. Institute keeps the editorial hand.' }, { h: 'Search that works', p: 'By batch, city, company, program, EC role, instantly.' }] }, { id: 'community', label: 'Community', eyebrow: 'Community tab', title: <>Chapters, groups, and the WhatsApp directory, in one place., body: 'Bring city chapters, batch groups, EC structure, and existing WhatsApp groups into a single, navigable community map.', features: [ { h: 'City chapters', p: 'Each with a lead, a roster, and its own calendar.' }, { h: 'Groups & EC', p: 'Topic groups, batch groups, EC roles, clear and visible.' }, { h: 'WhatsApp directory', p: 'Discover the institute’s existing WhatsApp groups without joining a hundred.' }] }]; function Product() { const [active, setActive] = useStateApp('today'); const tab = PRODUCT_TABS.find((t) => t.id === active); return (
The member app

Four tabs. Each one earns
its place on the home screen.

Verveli on mobile is deliberately small, four tabs, no inbox to drown in, no infinite scroll of strangers. Each tab does one thing for the alumnus.

{PRODUCT_TABS.map((t) => )}
{tab.eyebrow}

{tab.title}

{tab.body}

{tab.features.map((f, i) =>

{f.h}

{f.p}

)}
); } /* ============================================================ FOR INSTITUTES ============================================================ */ function ForInstitutes() { const [view, setView] = useStateApp('approvals'); return (
For institutes

A calm operator console
for alumni teams. Not a dashboard.

The Verveli admin console gives your alumni team one place to run the network. Approvals, members, programs, chapters, content, settings, and a full audit log of who did what.

Approvals

Verify joiners with email, code, or coordinator sign-off. Hold or approve in batch.

Members

Every member, searchable. Batch, city, company, program.

Programs & chapters

Preload programs, batches, and chapter structure so the network starts populated.

Daily content

Schedule the Today feed without leaving the console. Drafts and previews.

World settings

Decide what verification, privacy, and visibility look like for your institute.

Audit log

Every administrative action, time-stamped and attributable. Exportable.

{view === 'approvals' ? : }
); } /* ============================================================ TRUST ============================================================ */ function Trust() { const items = [ { h: 'Verified joining', p: '60 seconds. Institute code + phone OTP, with optional coordinator approval.', ic: }, { h: 'Approval queues', p: 'Your team holds the gate. Auto-approve is opt-in, not the default.', ic: }, { h: 'Member privacy controls', p: 'Members choose what is visible to non-batch, non-chapter, or no one.', ic: }, { h: 'Audit log', p: 'Every administrative action is logged, attributable, and exportable.', ic: }, { h: 'Institute world settings', p: 'You decide verification, privacy defaults, content moderation, retention.', ic: }, { h: 'Consent-first directory', p: 'Discovery is opt-in. Alumni can be searchable without being broadcast.', ic: }]; return (
Trust

Built with privacy and
accountability in the operating model.

{items.map((it, i) =>
{it.ic}

{it.h}

{it.p}

)}

Privacy, consent, and accountability are built into how Verveli works, guided by the principles behind India’s Digital Personal Data Protection Act.

We didn’t want a social app for our alumni.
We wanted a space we could keep alive,
with a paper trail we could trust.
Alumni Relations Lead
Tier-1 engineering institute · pilot cohort
); } /* ============================================================ OWNERSHIP, deployment flexibility / data sovereignty (moat). Benefit-level only: never describes how this is built. ============================================================ */ function Ownership() { const items = [ { h: 'Fully managed by default', p: 'Most institutes start on Verveli’s cloud and go live in days. No servers to stand up, nothing for your team to maintain.', ic: }, { h: 'Your data can stay yours', p: 'When governance demands it, alumni records can live on infrastructure your institute controls, not ours.', ic: }, { h: 'Made for sensitive institutions', p: 'Government, defence, and regulated institutes can adopt Verveli without member data leaving their environment.', ic: }, { h: 'No lock-in, ever', p: 'Change how Verveli is hosted as your needs evolve. It stays the same product, with your data intact.', ic: }]; return (
Ownership & control

Run it on our cloud, or
entirely on your servers.

The same Verveli, hosted the way your institute needs it. Start fully managed, and move closer to your own infrastructure whenever data residency or compliance asks for it.

{items.map((it, i) =>
{it.ic}

{it.h}

{it.p}

)}
); } /* ============================================================ PILOT, 4-6 week launch path ============================================================ */ function Pilot() { const steps = [ { w: 'Week 0', t: 'Kickoff', p: 'Goals, owners, and a short list of programs and chapters you want live.' }, { w: 'Week 1', t: 'World setup', p: 'Branding, verification policy, privacy defaults, admin team set up.' }, { w: 'Week 2', t: 'Preload', p: 'Programs, chapters, EC, and known alumni loaded from your existing records.' }, { w: 'Week 3', t: 'Soft launch', p: 'A seed cohort joins. Today feed starts. Daily content cadence begins.' }, { w: 'Week 4', t: 'Open up', p: 'Invites widen by batch. Coordinators approve, the network starts answering itself.' }, { w: 'Week 5', t: 'Signals on', p: 'Hiring, referrals, and help requests start flowing. Visibility for the team.' }, { w: 'Week 6', t: 'Handover', p: 'Steady-state playbook handed to your alumni office. Verveli stays close.' }]; return (
Pilot

Live in four to six weeks.
Not in six months.

A small, deliberate launch path. Each week has one job. Nothing depends on an annual event or a single coordinator pushing the rock uphill.

{steps.map((s, i) =>
{s.w}

{s.t}

{s.p}

)}
); } /* ============================================================ CLOSING CTA + FOOTER ============================================================ */ function Closing() { return (

Bring Verveli to
your institute.

A 30-minute call is the fastest way to see if Verveli fits. Write to us, we keep it small, sober, and specific.

contact@thryvx.io See the product again
); } function Footer() { return ( ); } /* ============================================================ APP ============================================================ */ function App() { injectLogoKeyframes(); const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS); // apply accent on tweak change useEffectApp(() => {applyAccent(tweaks.accent);}, [tweaks.accent]); return ( <>