// ════════════════════════════════════════════════════════════════════
// NexFlow · Sections · marquee · market signal · services · HIW ·
// terminal · case studies
// ════════════════════════════════════════════════════════════════════
// Fixed by Opus 4.7 — 2026-05-29 — P1-1: NF_Services now reads location.hash
// so the Nav dropdown, footer links, and services.html diagram nodes
// (/services.html#n8n …#report) resolve to the right service tab.
// ════════════════════════════════════════════════════════════════════
const { useState: useStateS, useEffect: useEffectS, useRef: useRefS } = React;
// Localise our own tagged list prices ("from A$2,400", "$50 USD") in copy
// to the visitor's currency. Falls back to the original string if
// engine/currency.js is absent. Bare example figures are never touched.
const nfLoc = (s) => (typeof window !== 'undefined' && window.nfLocalizePrice) ? window.nfLocalizePrice(s) : s;
// ── Marquee ────────────────────────────────────────────────────────
function Marquee() {
const phrases = [
'Stop doing it manually',
'Audit-grade by default',
'You own the code',
'40+ hours saved per client / week',
'2-week ship cycles',
'No lock-in. Ever.',
'Built on n8n · powered by AI',
];
const items = [...phrases, ...phrases];
return (
{items.map((p, i) => (
{p}
))}
);
}
// ── What we actually do · plain-language explainer (restored v7) ───
// Sits immediately after the hero so a first-time visitor gets a clear,
// jargon-free statement of the offer before anything else. Solid paper,
// high contrast, scannable — no animation behind the text.
function WhatWeDo() {
const jobs = [
{
k: 'Answer every call & message',
d: 'An AI receptionist picks up 24/7, books the job straight into your calendar, and texts the customer back — before they ring the next tradie on the list.',
href: '/services/ai-receptionist/',
},
{
k: 'Chase every lead & quote',
d: 'Every enquiry gets followed up, scored, and logged in your CRM automatically — so nothing slips through the cracks on a flat-out week.',
href: '/services/crm-automation/',
},
{
k: 'Do the paperwork',
d: 'Invoices, receipts and data entry get read and filed straight into Xero or MYOB. No more sitting down to admin at 10pm.',
href: '/services/invoice-processing/',
},
];
return (
WHAT WE ACTUALLY DO
In plain English.
NexFlow isn’t another app you have to learn. We set up AI that quietly handles the
repetitive office jobs eating your day — then we hand it over and you own it.
Built for you in about two weeks, on tools you keep. No lock-in, no jargon, no monthly
software you’ll never use.
);
}
// ── Market signal (PwC · McKinsey · Capgemini · Gartner) ───────────
function TrustStats() {
return (
MARKET SIGNAL · 2025–2026
The AI automation gap is widening. The cost of waiting is now measurable.
PwC, Capgemini, and McKinsey have stopped framing AI automation as an experiment. They now
publish hard numbers on the value being captured — and the value being left on the table by
businesses still hand-keying data.
{/* Bold any embedded numeric / metric token by splitting on the
match and wrapping it in a . The previous version
used dangerouslySetInnerHTML — safe today (label is from
the committed catalogue) but a stored-XSS sink if anything
ever lets users edit the source. React-element form is
XSS-safe by construction. */}
{
(function () {
var label = s.label || '';
// Split with a capturing group so the matches appear in the
// resulting array (at odd indices). Using a non-global regex
// because String.split's behaviour with /g flag is the same
// for this use case, and stateless regex is easier to reason
// about than re-using a /g instance whose lastIndex advances.
var parts = label.split(/(\d+%|top \d+%|\d+ slots|\d+\s?h)/);
var match = /^(\d+%|top \d+%|\d+ slots|\d+\s?h)$/;
return parts.map(function (p, i) {
return match.test(p) ? React.createElement('strong', { key: i }, p) : p;
});
})()
}
{s.sourceShort || s.source}
))}
PWC — THE LEADERS' EDGE
Companies with the best AI-driven financial outcomes are 2.8× more likely to have
increased decisions made without human intervention, and twice as likely to redesign
workflows around AI rather than bolt it on.
Only 2% of organisations have deployed AI agents at scale; another 12% at partial
scale. The blocker is rarely the model — it is governance, integration, and audit-grade ops.
Companies can boost productivity by 20–25% by automating routine tasks. The
business case is no longer about whether — it is about which workflows go first, and how to build
them so they survive audit.
SOURCE: McKinsey Global Institute · workflow automation research
What this means for an SMB. The companies pulling ahead are
not bigger or better funded — they are simply operationalising AI on real workflows. NexFlow is the
implementation layer between research-grade thinking and your inbox at 6pm.
);
}
// ── Services tab panel ────────────────────────────────────────────
// Each service maps to a 3D scene mode so the background animation
// matches the service the user is browsing. Tab clicks set the mode
// directly; auto-cycling honour the same map.
const SERVICE_SCENE_MODE = {
n8n: 'workflow',
agents: 'agents',
crm: 'workflow',
chat: 'chatbot',
doc: 'document',
report: 'dashboard',
};
function Services() {
const data = window.NF_DATA;
const [pick, setPick] = useStateS(data.SERVICES[0].id);
const [manual, setManual] = useStateS(false);
const [inView, setInView] = useStateS(false);
const cur = data.SERVICES.find(s => s.id === pick) || data.SERVICES[0];
const sectionRef = useRefS(null);
// Drive the 3D background to match the active service
useEffectS(() => {
const mode = SERVICE_SCENE_MODE[pick] || 'workflow';
if (window.NexFlowScene && window.NexFlowScene.setMode) {
window.NexFlowScene.setMode(mode);
}
}, [pick]);
// Track in-view so we only cycle when the user is here
useEffectS(() => {
if (!sectionRef.current) return;
const io = new IntersectionObserver((entries) => {
entries.forEach(e => setInView(e.intersectionRatio > 0.25));
}, { threshold: [0, 0.25, 0.5, 0.75] });
io.observe(sectionRef.current);
return () => io.disconnect();
}, []);
// Auto-cycle through service tabs whenever the section is in view and
// the user hasn't manually chosen — so the background animations
// rotate "without selecting" the way the user wants.
useEffectS(() => {
if (manual || !inView) return;
const ids = data.SERVICES.map(s => s.id);
const t = setInterval(() => {
setPick(p => ids[(ids.indexOf(p) + 1) % ids.length]);
}, 5500);
return () => clearInterval(t);
}, [manual, inView]);
// Deep-link support (P1-1). The Nav dropdown, the footer service list,
// and the services.html diagram nodes all link to /services.html#
// (n8n · agents · crm · chat · doc · report). Those tabs are React state,
// not DOM anchors, so the browser cannot scroll to them natively. Read
// the hash on mount and on hashchange: if it names a real service, select
// that tab, stop the auto-cycle, and bring the panel into view.
useEffectS(() => {
const ids = data.SERVICES.map(s => s.id);
function applyHash() {
const id = (window.location.hash || '').replace(/^#/, '');
if (!id || ids.indexOf(id) === -1) return;
setManual(true);
setPick(id);
// Defer the scroll so the tab content has reconciled and layout has
// settled before we measure the section's position.
requestAnimationFrame(() => {
if (sectionRef.current) sectionRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
}
applyHash();
window.addEventListener('hashchange', applyHash);
return () => window.removeEventListener('hashchange', applyHash);
}, []);
const handlePick = (id) => { setManual(true); setPick(id); };
const openDemo = () => {
if (window.NF_ServiceDemos && window.NF_ServiceDemos.openDemo) {
window.NF_ServiceDemos.openDemo(pick);
}
};
return (
SEE IT WORKING
Want to see it actually work? Here's a live demo of every build.
{/* Removed the visible "KEYWORDS:" strip — visible keyword lists read
as keyword-stuffing to Google and looked out of place to users.
Machine-readable keyword signals already live in the services.html
and the per-service Service schema. */}
);
}
// ── How it works · scroll-driven sticky story ─────────────────────
function HowItWorks() {
const data = window.NF_DATA;
const [active, setActive] = useStateS(0);
const rootRef = useRefS(null);
useEffectS(() => {
if (!rootRef.current) return;
const stepEls = rootRef.current.querySelectorAll('[data-hiw-step]');
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
const idx = parseInt(e.target.getAttribute('data-hiw-step'));
setActive(idx);
}
});
}, { rootMargin: '-40% 0px -40% 0px', threshold: 0 });
stepEls.forEach(el => io.observe(el));
return () => io.disconnect();
}, []);
const cur = data.HIW_STEPS[active];
return (
HOW IT WORKS
From "we should automate this" to running in production in about two weeks.
Every workflow we ship logs every step it takes — timestamped, replayable, exportable. Auditors can
follow the trail. So can you, your CFO, and your insurer. The stream on the right is a live,
anonymised sample of automations running for NexFlow clients right now.
);
}
// ── Case studies ──────────────────────────────────────────────────
function CaseStudies() {
const data = window.NF_DATA;
return (
CASE STUDIES
Three workflows we have shipped multiple times.
{data.CASES.map((c, i) => (
{c.industry}
{c.metric}
{c.metricLbl.toUpperCase()}
{c.title}
{c.body}
RECEIPTS
{c.receipt.map(r => {r})}
))}
);
}
// ── Problems we solve + plain-English "what we do" grid (v9) ───────
// Customer-language entry point: lead with the owner's pain in their
// own words, then point at the service that fixes it. Six cards below
// link to the individual service pages. Mirrors the static snapshot in
// index.html so crawlers and no-JS visitors see the same copy.
function SvcIcon({ name }) {
const p = { width: 26, height: 26, viewBox: '0 0 24 24', fill: 'none',
stroke: 'currentColor', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true };
switch (name) {
case 'phone': return ();
case 'crm': return ();
case 'invoice': return ();
case 'chat': return ();
case 'doc': return ();
case 'flow': return ();
default: return ();
}
}
function Problems() {
const data = window.NF_DATA;
const problems = data?.PROBLEMS || [];
const pages = data?.SERVICE_PAGES || [];
return (
SOUND FAMILIAR?
You started a business to do the work you love. Not the busywork.
You're great at the job — you're just flat out. Here are the things quietly costing you
time and money every week, and what we put in place so they stop.