Files

868 lines
33 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>QuikCue — Custom AI Operators for Ambitious Businesses</title>
<meta name="description" content="We build custom AI operators for businesses. Your brand gets its own AI that knows your data, understands your strategy, and executes — in conversation. From £8,000/month.">
<meta property="og:title" content="QuikCue — Custom AI Operators">
<meta property="og:description" content="Not a chatbot. Not a dashboard. An AI operator built specifically for your business — wired into your systems, executing your strategy.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://quikcue.com">
<meta name="twitter:card" content="summary_large_image">
<link rel="canonical" href="https://quikcue.com">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700;800&family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
<style>
/* ── New category badge on hero ── */
.hero-category-badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: rgba(0,255,159,0.06);
border: 1px solid rgba(0,255,159,0.18);
border-radius: 99px;
padding: 6px 14px;
font-family: var(--mono);
font-size: 0.72rem;
font-weight: 600;
color: var(--accent);
letter-spacing: 0.08em;
margin-bottom: 2rem;
opacity: 0;
animation: fadeUp 0.8s 0.1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.hero-category-badge::before {
content: '';
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--accent);
animation: pulse 2s ease-out infinite;
}
/* ── Demo terminal ── */
.section-demo { padding: 0 0 8rem; }
.agent-terminal {
background: #060606;
border: 1px solid #1c1c1c;
border-radius: 16px;
overflow: hidden;
max-width: 780px;
margin: 3rem auto 0;
box-shadow: 0 60px 120px rgba(0,0,0,0.6), 0 0 0 1px rgba(0,255,159,0.03), inset 0 1px 0 rgba(255,255,255,0.02);
}
.agent-terminal-bar {
display: flex;
align-items: center;
gap: 7px;
padding: 13px 18px;
border-bottom: 1px solid #141414;
background: #040404;
}
.tdot { width: 11px; height: 11px; border-radius: 50%; flex-shrink: 0; }
.tdot-r { background: #ff5f57; }
.tdot-a { background: #febc2e; }
.tdot-g { background: #28c840; }
.tbar-title {
font-family: var(--mono);
font-size: 0.72rem;
color: #3a3a3a;
margin-left: 10px;
flex: 1;
}
.tbar-live {
font-family: var(--mono);
font-size: 0.65rem;
color: var(--accent);
display: flex;
align-items: center;
gap: 5px;
}
.tbar-live-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--accent);
animation: pulse 2s ease-out infinite;
}
.agent-terminal-body {
padding: 1.75rem 1.5rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.chat-user {
align-self: flex-end;
max-width: 70%;
background: rgba(255,255,255,0.035);
border: 1px solid rgba(255,255,255,0.07);
border-radius: 14px 14px 4px 14px;
padding: 0.75rem 1.1rem;
font-size: 0.875rem;
color: #c8c8d0;
line-height: 1.55;
}
.chat-agent {
align-self: flex-start;
max-width: 92%;
display: flex;
flex-direction: column;
gap: 5px;
}
.chat-agent-who {
font-family: var(--mono);
font-size: 0.62rem;
font-weight: 700;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.12em;
padding-left: 3px;
}
.chat-bubble {
background: rgba(0,255,159,0.035);
border: 1px solid rgba(0,255,159,0.1);
border-radius: 4px 14px 14px 14px;
padding: 0.85rem 1.1rem;
font-size: 0.875rem;
color: var(--text-secondary);
line-height: 1.65;
}
.chat-card {
background: rgba(0,10,6,0.8);
border: 1px solid rgba(0,255,159,0.1);
border-radius: 10px;
padding: 1rem 1.1rem;
margin-top: 0.65rem;
}
.chat-card-head {
font-family: var(--mono);
font-size: 0.67rem;
font-weight: 700;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 0.8rem;
}
.chat-card-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.8rem;
padding: 0.35rem 0;
border-bottom: 1px solid rgba(255,255,255,0.04);
color: var(--text-secondary);
gap: 1rem;
}
.chat-card-row:last-child { border-bottom: none; }
.chat-card-row b { color: var(--text); font-weight: 500; }
.chat-card-row em { font-style: normal; font-size: 0.72rem; color: var(--text-muted); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-card-amt { font-family: var(--mono); font-weight: 700; color: var(--accent); white-space: nowrap; font-size: 0.82rem; }
.chat-card-amt.up { color: #4ade80; }
.chat-card-more { font-size: 0.72rem; color: var(--text-muted); font-style: italic; padding-top: 0.4rem; }
.chat-card-total {
font-family: var(--mono);
font-size: 0.8rem;
font-weight: 700;
color: var(--accent);
text-align: right;
margin-top: 0.65rem;
padding-top: 0.6rem;
border-top: 1px solid rgba(0,255,159,0.12);
}
.chat-tags {
display: flex;
flex-wrap: wrap;
gap: 7px;
margin-top: 0.75rem;
}
.chat-tag {
font-family: var(--mono);
font-size: 0.62rem;
font-weight: 600;
padding: 4px 11px;
border-radius: 99px;
background: rgba(0,255,159,0.07);
color: var(--accent);
border: 1px solid rgba(0,255,159,0.18);
}
.demo-caption {
text-align: center;
margin-top: 1.25rem;
font-family: var(--mono);
font-size: 0.72rem;
color: var(--text-muted);
}
.demo-strip {
display: grid;
grid-template-columns: repeat(4, 1fr);
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
max-width: 620px;
margin: 2.5rem auto 0;
}
.demo-strip-item {
text-align: center;
padding: 1.5rem 0.5rem;
border-right: 1px solid var(--border);
}
.demo-strip-item:last-child { border-right: none; }
.demo-strip-num {
font-family: var(--mono);
font-size: 1.6rem;
font-weight: 800;
color: var(--accent);
line-height: 1;
}
.demo-strip-label {
font-family: var(--mono);
font-size: 0.6rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.1em;
margin-top: 6px;
}
/* ── Client slot card ── */
.client-card-slot {
border-style: dashed;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.slot-open-label {
font-family: var(--mono);
font-size: 0.62rem;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.15em;
margin-bottom: 0.5rem;
}
.slot-cta-btn {
display: block;
margin-top: 1.5rem;
font-family: var(--mono);
font-size: 0.8rem;
font-weight: 700;
color: var(--accent);
border: 1px solid var(--border-accent);
padding: 0.65rem 1.25rem;
border-radius: 8px;
transition: all 0.2s;
text-align: center;
}
.slot-cta-btn:hover {
background: var(--accent-glow);
border-color: var(--accent-dim);
}
@media (max-width: 640px) {
.demo-strip { grid-template-columns: repeat(2, 1fr); }
.demo-strip-item { border-bottom: 1px solid var(--border); }
.demo-strip-item:nth-child(even) { border-right: none; }
.demo-strip-item:nth-last-child(-n+2) { border-bottom: none; }
}
</style>
</head>
<body>
<!-- ═══════════════════════ NAV ═══════════════════════ -->
<nav>
<div class="nav-inner">
<a href="/" class="logo">QuikCue</a>
<div class="nav-links">
<a href="#what-we-build">What we build</a>
<a href="/pricing">Pricing</a>
<a href="/live">Live Feed</a>
<a href="#contact" class="nav-cta">Get your operator →</a>
</div>
</div>
</nav>
<!-- ═══════════════════════ HERO ═══════════════════════ -->
<header class="hero">
<div class="hero-inner">
<div class="hero-category-badge">New category of business software</div>
<h1 class="hero-title">
We build AI that<br>
<span class="accent">runs your business.</span>
</h1>
<p class="hero-sub">
Your brand gets its own AI operator — custom-built, wired into your data and
systems, deployed in 30 days. It doesn't just answer questions.
It executes strategy, on demand, in real time.
</p>
<div class="hero-ctas">
<a href="#demo" class="btn btn-primary">See it in action →</a>
<a href="#contact" class="btn btn-ghost">Talk to us</a>
</div>
</div>
<div class="hero-glow"></div>
</header>
<!-- ═══════════════════════ PROBLEM ═══════════════════════ -->
<section class="section section-problem" id="problem">
<div class="container">
<div class="section-label">The problem with AI today</div>
<h2 class="section-title">Everyone has AI. Nobody has an operator.</h2>
<div class="problem-grid">
<div class="problem-text">
<p>
ChatGPT, Copilot, Gemini — you've tried them. They're impressive in a
vacuum. They're also completely disconnected from your business.
They don't know your customers, your campaigns, your numbers, or your brand voice.
</p>
<p>
So you copy-paste data in, get a generic answer out, and execute it yourself.
The AI didn't do the work. You did. You became a human API between
your data and a language model.
</p>
<p>
And agencies? They pitch strategy. You execute it. Alone. With your tools.
On your calendar.
</p>
<p class="problem-punchline">
Neither is an operator.<br>
We build operators.
</p>
</div>
<div class="problem-stats">
<div class="problem-stat">
<div class="problem-stat-icon">🧠</div>
<div class="problem-stat-text">
<strong>Not a generic chatbot.</strong>
Your operator is built on your specific data — customers, campaigns, revenue, history.
It gives real answers from real context, not generic ones from a public model.
</div>
</div>
<div class="problem-stat">
<div class="problem-stat-icon"></div>
<div class="problem-stat-text">
<strong>Not a dashboard you stare at.</strong>
Dashboards show you what happened. Your operator acts on what's happening
right now — and asks what you want to do about it.
</div>
</div>
<div class="problem-stat">
<div class="problem-stat-icon">🎯</div>
<div class="problem-stat-text">
<strong>Not an agency retainer.</strong>
No account managers. No monthly decks. No handoffs.
An AI that executes your strategy 24/7, on demand, the moment you ask.
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ WHAT WE BUILD ═══════════════════════ -->
<section class="section section-category" id="what-we-build">
<div class="container">
<div class="section-label">What we build</div>
<h2 class="section-title">A new category of business software.</h2>
<p class="section-sub">
We call them AI operators. An AI operator isn't software you use —
it's intelligence that works inside your business. Built custom for your brand,
wired into your live systems, and capable of executing across your entire operation
through a single conversation.
</p>
<div class="pillars">
<div class="pillar">
<div class="pillar-number">01</div>
<h3 class="pillar-title">Understand</h3>
<p class="pillar-sub">Your entire business, instantly queryable.</p>
<ul class="pillar-list">
<li>Natural language queries on your live data</li>
<li>Customers, campaigns, revenue — surfaced instantly</li>
<li>No SQL. No BI tools. No exports. Just ask.</li>
<li>Context-aware — it knows your history and your patterns</li>
<li>Always connected to your actual systems in real time</li>
</ul>
<div class="pillar-proof">
<span class="pillar-proof-label">In production:</span>
31 pledges across 7 organisations, £200K+ managed. Full data query in under 2 seconds.
</div>
</div>
<div class="pillar">
<div class="pillar-number">02</div>
<h3 class="pillar-title">Execute</h3>
<p class="pillar-sub">It doesn't just report. It acts.</p>
<ul class="pillar-list">
<li>WhatsApp &amp; email campaigns sent on command</li>
<li>Personalised outreach drafted and deployed at scale</li>
<li>CRM updates and data writes — no copy-paste</li>
<li>Workflow triggers across your entire stack</li>
<li>Stripe, Salesforce, HubSpot, and any API you run on</li>
</ul>
<div class="pillar-proof">
<span class="pillar-proof-label">In production:</span>
WhatsApp nudges drafted per donor, pledge follow-ups automated, campaign reports generated on demand.
</div>
</div>
<div class="pillar">
<div class="pillar-number">03</div>
<h3 class="pillar-title">Evolve</h3>
<p class="pillar-sub">Every session, your operator gets sharper.</p>
<ul class="pillar-list">
<li>Learns your brand voice, tone, and strategic preferences</li>
<li>Recalls past decisions, rationale, and outcomes</li>
<li>Adapts to how you think and how you operate</li>
<li>Monthly enhancement sprints — new tools, new integrations</li>
<li>Becomes more valuable the longer you use it</li>
</ul>
<div class="pillar-proof">
<span class="pillar-proof-label">In production:</span>
Full conversation context, per-pledge memory, campaign history — baked in permanently.
</div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ DEMO ═══════════════════════ -->
<section class="section section-demo" id="demo">
<div class="container">
<div class="section-label">Proof of concept — live in production</div>
<h2 class="section-title">Meet the Steward.</h2>
<p class="section-sub">
Built for PledgeNow, the Steward is a live AI operator managing pledge operations
in production today. 13 custom tools. Wired into live data. This is what a
conversation with your operator looks like.
</p>
<div class="agent-terminal">
<div class="agent-terminal-bar">
<div class="tdot tdot-r"></div>
<div class="tdot tdot-a"></div>
<div class="tdot tdot-g"></div>
<span class="tbar-title">Steward — PledgeNow AI Operator · Production</span>
<span class="tbar-live">
<span class="tbar-live-dot"></span>
Live
</span>
</div>
<div class="agent-terminal-body">
<!-- Exchange 1: Understand -->
<div class="chat-user">
Who pledged in February but hasn't paid yet?
</div>
<div class="chat-agent">
<div class="chat-agent-who">Steward</div>
<div class="chat-bubble">
Found 12 outstanding pledges from February. Total exposure: £4,250.
<div class="chat-card">
<div class="chat-card-head">📋 February — Unpaid Pledges (12)</div>
<div class="chat-card-row">
<b>Ahmed Al-Rashid</b>
<em>Ramadan 2026</em>
<span class="chat-card-amt">£500</span>
</div>
<div class="chat-card-row">
<b>Fatima Malik</b>
<em>Education Fund</em>
<span class="chat-card-amt">£250</span>
</div>
<div class="chat-card-row">
<b>Omar Suleiman</b>
<em>General Sadaqah</em>
<span class="chat-card-amt">£1,000</span>
</div>
<div class="chat-card-more">+ 9 more pledges</div>
<div class="chat-card-total">Total outstanding: £4,250</div>
</div>
</div>
</div>
<!-- Exchange 2: Execute -->
<div class="chat-user">
Draft personalised WhatsApp nudges for all of them.
</div>
<div class="chat-agent">
<div class="chat-agent-who">Steward</div>
<div class="chat-bubble">
Done. 12 personalised messages drafted — each references the donor's name,
exact pledge amount, and campaign. Tone is warm, personal, non-pressuring.
<div class="chat-tags">
<span class="chat-tag">✓ 12 messages drafted</span>
<span class="chat-tag">→ Personalised per donor</span>
<span class="chat-tag">📱 WhatsApp-ready</span>
</div>
</div>
</div>
<!-- Exchange 3: Analyse -->
<div class="chat-user">
How does Ramadan 2026 compare to last year?
</div>
<div class="chat-agent">
<div class="chat-agent-who">Steward</div>
<div class="chat-bubble">
Ramadan is performing significantly stronger. Average pledge is up 60%.
<div class="chat-card">
<div class="chat-card-head">📊 Ramadan Campaign — YoY</div>
<div class="chat-card-row">
<b>Avg pledge 2026</b>
<em></em>
<span class="chat-card-amt">£387</span>
</div>
<div class="chat-card-row">
<b>Avg pledge 2025</b>
<em></em>
<span class="chat-card-amt">£241</span>
</div>
<div class="chat-card-row">
<b>Growth</b>
<em></em>
<span class="chat-card-amt up">+60.6% ↑</span>
</div>
<div class="chat-card-row">
<b>Total raised 2026</b>
<em></em>
<span class="chat-card-amt">£31,200</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="demo-caption">
Every interaction uses real tools — live data, real execution, production system.
No mocks. No demos. This is what runs today.
</div>
<div class="demo-strip">
<div class="demo-strip-item">
<div class="demo-strip-num">13</div>
<div class="demo-strip-label">Live tools</div>
</div>
<div class="demo-strip-item">
<div class="demo-strip-num">31</div>
<div class="demo-strip-label">Pledges managed</div>
</div>
<div class="demo-strip-item">
<div class="demo-strip-num">7</div>
<div class="demo-strip-label">Organisations</div>
</div>
<div class="demo-strip-item">
<div class="demo-strip-num">0</div>
<div class="demo-strip-label">Dashboards needed</div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ LIVE FEED ═══════════════════════ -->
<section class="section section-live" id="live">
<div class="container">
<div class="live-header">
<div>
<div class="section-label">Proof, not promises</div>
<h2 class="section-title">While you're reading this, we're shipping.</h2>
<p class="section-sub">
Our engineering feed is public, real-time, and unfakeable. Every deployment,
every fix, every system — logged and live. This isn't a portfolio from last year.
This is what happened <em>today</em>.
</p>
</div>
</div>
<div class="live-stats">
<div class="live-stat">
<div class="live-stat-num" id="stat-shipped"></div>
<div class="live-stat-label">Systems shipped</div>
</div>
<div class="live-stat">
<div class="live-stat-num live-stat-green" id="stat-rate"></div>
<div class="live-stat-label">Ship rate</div>
</div>
<div class="live-stat">
<div class="live-stat-num live-stat-amber" id="stat-oops"></div>
<div class="live-stat-label">Oops (transparent)</div>
</div>
<div class="live-stat">
<div class="live-stat-num live-stat-red">0</div>
<div class="live-stat-label">Downtime hours</div>
</div>
</div>
<div class="live-feed">
<div class="live-feed-header">
<div class="live-pulse"></div>
<span>Live Engineering Feed</span>
<a href="/live" class="live-feed-link">View full log →</a>
</div>
<div class="live-feed-entries" id="live-entries">
<div class="live-entry">
<span class="live-entry-title" style="opacity:0.3">Loading live feed…</span>
</div>
</div>
<div class="live-feed-fade"></div>
</div>
<div class="live-fomo">
<p>
Every entry above is a real deployment to a real production system.
No staging theater. No demo environments. Production infrastructure,
shipping continuously.
</p>
<p class="live-fomo-kicker">
If your technology partner can't show you this — ask yourself why.
</p>
</div>
</div>
</section>
<!-- ═══════════════════════ CLIENTS ═══════════════════════ -->
<section class="section section-clients">
<div class="container">
<div class="section-label">Deployed in production</div>
<h2 class="section-title">Real operators. Real businesses.</h2>
<div class="clients-grid">
<div class="client-card">
<div class="client-logo">PledgeNow — The Steward</div>
<p class="client-desc">
The world's first pledge AI operator. The Steward manages every aspect of
PledgeNow's operations through conversation — querying live data, drafting
personalised WhatsApp nudges, analysing campaign performance, and surfacing
actionable insights. No dashboards. No exports. Just a conversation.
</p>
<div class="client-stats">
<span>13 live execution tools</span>
<span>31 pledges, 7 organisations managed</span>
<span>WhatsApp-native campaign execution</span>
<span>Full analytics in natural language</span>
</div>
</div>
<div class="client-card">
<div class="client-logo">CharityRight</div>
<p class="client-desc">
End-to-end digital infrastructure for one of the UK's fastest-growing charities.
Donation processing, checkout systems, analytics dashboards, CRM integrations,
and a full automated comms stack — all built, deployed, and maintained by QuikCue.
</p>
<div class="client-stats">
<span>£4M+ donations processed</span>
<span>5 payment gateways integrated</span>
<span>24/7 automated donor comms</span>
</div>
<a href="/work/charityright" style="display:inline-block;margin-top:16px;color:var(--accent);font-family:var(--mono);font-size:0.82rem;">Read case study →</a>
</div>
<div class="client-card client-card-slot">
<div>
<div class="slot-open-label">Open slot — 2026 Q2</div>
<div class="client-logo" style="color:var(--text-muted);">Your brand.</div>
<p class="client-desc">
We build 23 new operators per quarter. Each one is custom — your data,
your systems, your strategy. Built, deployed, and live in 30 days.
If you're reading this, there's still a slot open.
</p>
<div class="client-stats">
<span>30-day build + deploy</span>
<span>Custom tools for your exact stack</span>
<span>Direct with the engineer who builds it</span>
</div>
</div>
<a href="mailto:omair@quikcue.com?subject=I%20want%20a%20custom%20AI%20operator" class="slot-cta-btn">
Claim your slot →
</a>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ CTA ═══════════════════════ -->
<section class="section section-cta" id="contact">
<div class="container">
<div class="cta-block">
<h2 class="cta-title">
Your brand deserves an AI<br>
that works as hard as <span class="accent">you do.</span>
</h2>
<p class="cta-sub">
Every day without an operator is a day your team is doing what AI should be doing —
strategy, analysis, execution, outreach. On demand. In conversation. Without handoffs.
We're ready to build yours.
</p>
<div class="cta-actions">
<a href="mailto:omair@quikcue.com?subject=I%20want%20a%20custom%20AI%20operator" class="btn btn-primary btn-large">
omair@quikcue.com →
</a>
<a href="https://wa.me/601162222512" class="btn btn-ghost btn-large">
WhatsApp us
</a>
</div>
<p class="cta-note">
No forms. No sales funnel. Just a conversation with the engineer who builds it.
</p>
</div>
</div>
</section>
<!-- ═══════════════════════ FOOTER ═══════════════════════ -->
<footer class="site-footer">
<div class="container">
<div class="footer-inner">
<div class="footer-brand">
<span class="footer-logo">QuikCue</span>
<span class="footer-tagline">We build AI operators. For businesses that refuse to be slow.</span>
</div>
<div class="footer-links">
<a href="/pricing">Pricing</a>
<a href="/live">Live Feed</a>
<a href="https://git.quikcue.com" target="_blank">Git</a>
<a href="mailto:omair@quikcue.com">Contact</a>
</div>
</div>
<div class="footer-bottom">
<span>© 2026 QuikCue Ltd. London, UK.</span>
<span class="footer-built">Custom AI operators. Shipped, not pitched.</span>
</div>
</div>
</footer>
<!-- ═══════════════════════ SCRIPTS ═══════════════════════ -->
<script>
// ── Smooth scroll ──
document.querySelectorAll('a[href^="#"]').forEach(a => {
a.addEventListener('click', e => {
e.preventDefault();
const target = document.querySelector(a.getAttribute('href'));
if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
});
// ── Intersection Observer for fade-in ──
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1, rootMargin: '0px 0px -60px 0px' });
document.querySelectorAll('.pillar, .client-card, .problem-stat, .live-stat').forEach(el => {
el.classList.add('fade-in');
observer.observe(el);
});
// ── Animate numbers ──
const animateValue = (el, start, end, duration, suffix) => {
const range = end - start;
const startTime = performance.now();
const step = (now) => {
const progress = Math.min((now - startTime) / duration, 1);
const eased = 1 - Math.pow(1 - progress, 3);
el.textContent = Math.floor(start + range * eased) + (suffix || '');
if (progress < 1) requestAnimationFrame(step);
};
requestAnimationFrame(step);
};
// ── Live Feed ──
const SHIPS_API = '/api';
async function loadLiveFeed() {
try {
const [shipsRes, statsRes] = await Promise.all([
fetch(`${SHIPS_API}/ships/showcase`),
fetch(`${SHIPS_API}/stats`)
]);
if (shipsRes.ok) renderShips(await shipsRes.json());
if (statsRes.ok) renderStats(await statsRes.json());
} catch (e) {
renderShips(FALLBACK_SHIPS);
renderStats(FALLBACK_STATS);
}
}
const FALLBACK_STATS = { total: 194, shipped: 191, oops: 38 };
const FALLBACK_SHIPS = [
{ id: 194, title: "PledgeNow Steward — AI pledge operator, 13 tools, live production", status: "shipped" },
{ id: 191, title: "Steward stress test — 31/31 scenarios pass, full tool coverage", status: "shipped" },
{ id: 184, title: "WhatsApp nudge generation — personalised per donor, batch execution", status: "shipped" },
{ id: 170, title: "Pledge analytics — campaign YoY comparison, real-time query", status: "shipped" },
{ id: 148, title: "Milestone Recognition: n8n → Mailchimp via donation_warehouse", status: "shipped" },
{ id: 120, title: "CharityRight Admin v4 — Command center redesign", status: "shipped" },
{ id: 84, title: "Supporter Care: Full refund/cancel/edit across Donations", status: "shipped" },
{ id: 22, title: "JustVitamin dynamic data + API-driven charts", status: "shipped" },
{ id: 13, title: "Database-backed shipping log with live API", status: "shipped" },
{ id: 7, title: "QuikCue comms stack — Chatwoot + n8n + WAHA", status: "shipped" },
{ id: 4, title: "CharityRight checkout v2", status: "shipped" },
{ id: 1, title: "Full server migration — 14 databases, zero downtime", status: "shipped" },
];
function renderStats(stats) {
const shipped = stats.shipped || 0;
const total = stats.total || 0;
const oops = stats.oops || 0;
const rate = total > 0 ? Math.round((shipped / total) * 100) : 0;
const elShipped = document.getElementById('stat-shipped');
const elRate = document.getElementById('stat-rate');
const elOops = document.getElementById('stat-oops');
const statsObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateValue(elShipped, 0, shipped, 1200, '');
animateValue(elRate, 0, rate, 1200, '%');
animateValue(elOops, 0, oops, 1200, '');
statsObserver.disconnect();
}
});
}, { threshold: 0.5 });
statsObserver.observe(elShipped.parentElement);
}
function renderShips(ships) {
const container = document.getElementById('live-entries');
container.innerHTML = ships.map(s => {
const badgeClass = s.status === 'shipped' ? 'badge-shipped' :
s.status === 'shipping' ? 'badge-shipping' : 'badge-planned';
const label = s.status.charAt(0).toUpperCase() + s.status.slice(1);
return `<div class="live-entry fade-in visible">
<span class="live-entry-id">#${s.id}</span>
<span class="live-entry-badge ${badgeClass}">${label}</span>
<span class="live-entry-title">${escapeHtml(s.title)}</span>
</div>`;
}).join('');
}
function escapeHtml(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
loadLiveFeed();
const pulse = document.querySelector('.live-pulse');
if (pulse) {
setInterval(() => {
pulse.classList.remove('ping');
void pulse.offsetWidth;
pulse.classList.add('ping');
}, 3000);
}
</script>
</body>
</html>