Animation Explorer — scroll to explore
01

Hero Load Sequence

Full homepage hero entrance. Title slides from left, chat window from right, then subtitle + CTA stagger up. Fires once on page load via a master timeline.

AI CO-FOUNDER FOR ECOMMERCE

The CFO you never
had. Now you do.

Ask Luca analyses your store in real time — cash flow, margins, ad spend — and tells you exactly what to do next.

L
Luca
● Active now
Your CAC jumped 34% this week — Meta CPMs are up. I'd pause broad audiences and shift €4k to your top retargeting set.
Do that automatically?
Done. I'll send you a report in 24 hours.
// Hero load — master timeline, fires once
const tl = gsap.timeline({ defaults: { ease: "power3.out" } });

gsap.set(["#h-eyebrow", "#h-headline"], { x: -60, opacity: 0 });
gsap.set("#h-chat",            { x:  60, opacity: 0 });
gsap.set("#h-sub",             { y:  24, opacity: 0 });
gsap.set("#h-cta",             { y:  20, opacity: 0 });

tl.to("#h-eyebrow",  { x: 0, opacity: 1, duration: 0.7 })
  .to("#h-headline", { x: 0, opacity: 1, duration: 0.8 }, "<0.1")
  .to("#h-chat",     { x: 0, opacity: 1, duration: 0.9 }, "<0.15")
  .to("#h-sub",      { y: 0, opacity: 1, duration: 0.6 }, "-=0.4")
  .to("#h-cta",      { y: 0, opacity: 1, duration: 0.5 }, "<0.1");
02

Scroll Reveal Directions

Four direction variants used across the site. Scroll down to trigger. Direction is determined by element role and column position.

from-left
SECTION EYEBROW
Cash flow, finally clear
Used for: section eyebrows, hero titles, left-column two-col layouts.
← from-left
eyebrows · hero title · left col
from-right
HERO VISUAL
Luca spots the gap
Used for: hero visuals, right-column two-col layouts, last grid card.
→ from-right
hero visual · right col
fade-up
BODY COPY
Cut waste automatically
Used for: body copy, subtitles, middle grid cards, sequential steps.
↑ fade-up
body copy · subtitles · middle card
fade-only
STEP LIST
Connect → Analyse → Act
Used for: sequential steps, overlapping elements, decorative items.
◎ fade-only
sequential steps · decorative
// Direction map — encode at render time via data attr
const dirMap = {
  left:  { x: -50, y:   0, opacity: 0 },
  right: { x:  50, y:   0, opacity: 0 },
  up:    { x:   0, y:  40, opacity: 0 },
  fade:  { x:   0, y:   0, opacity: 0 },
};

document.querySelectorAll(".rv-card").forEach(el => {
  const dir = el.dataset.dir;
  gsap.set(el, dirMap[dir]);

  ScrollTrigger.create({
    trigger: el,
    start:   "top 82%",
    once:    true,
    onEnter: () => gsap.to(el, {
      x: 0, y: 0, opacity: 1,
      duration: 0.75,
      ease: "power3.out",
    }),
  });
});
03

Staggered Grid Entrance

Two variants: (a) all cards fade-up with stagger, (b) first/middle/last get from-left/fade-up/from-right per row. Used on the Integrations grid and Features grid.

Variant A — uniform fade-up stagger
🛍
Shopify
Revenue, orders, and product data
💳
Stripe
Payments, MRR, and churn signals
📣
Meta Ads
CPM, ROAS, and audience performance
🔍
Google Ads
Search spend and conversion tracking
📧
Klaviyo
Email revenue and list health
📊
Xero
P&L, cash flow, and tax position
Variant B — directional per column (left / up / right)
🛍
Shopify
Revenue, orders, and product data
💳
Stripe
Payments, MRR, and churn signals
📣
Meta Ads
CPM, ROAS, and audience performance
🔍
Google Ads
Search spend and conversion tracking
📧
Klaviyo
Email revenue and list health
📊
Xero
P&L, cash flow, and tax position
// Variant A — uniform stagger
gsap.set(".ga-card", { y: 40, opacity: 0 });
ScrollTrigger.create({
  trigger: "#grid-a",
  start:   "top 78%",
  once:    true,
  onEnter: () => gsap.to(".ga-card", {
    y: 0, opacity: 1, duration: 0.65,
    stagger: 0.08, ease: "power3.out",
  }),
});

// Variant B — directional per column position
const colDir = [
  { x: -50, y: 0 },   // col 0 → from-left
  { x:   0, y: 40 },  // col 1 → fade-up
  { x:  50, y: 0 },   // col 2 → from-right
];
document.querySelectorAll(".gb-card").forEach(el => {
  const d = colDir[+el.dataset.col];
  gsap.set(el, { ...d, opacity: 0 });
});
ScrollTrigger.create({
  trigger: "#grid-b",
  start:   "top 78%",
  once:    true,
  onEnter: () => {
    document.querySelectorAll(".gb-card").forEach((el, i) => {
      gsap.to(el, {
        x: 0, y: 0, opacity: 1,
        duration: 0.7, delay: i * 0.07,
        ease: "power3.out",
      });
    });
  },
});
04

Counter Animation

Stat strip with animated counters. Each counter reads data attributes for prefix, suffix, target, and decimal places. Triggers once on scroll entry.

€0M
Average annual revenue uplift
0%
Decisions made without founder input
0×
ROAS improvement in first 30 days
0s
Average time to an actionable insight
// Counter — data-attr driven, fires once
function animateCounter(el) {
  const target   = +el.dataset.target;
  const prefix   = el.dataset.prefix  || "";
  const suffix   = el.dataset.suffix  || "";
  const decimals = +el.dataset.decimals;
  const obj      = { val: 0 };

  gsap.to(obj, {
    val: target,
    duration: 1.6,
    ease: "power2.out",
    onUpdate() {
      el.textContent = prefix + obj.val.toFixed(decimals) + suffix;
    },
  });
}

ScrollTrigger.create({
  trigger: "#stat-strip",
  start:   "top 80%",
  once:    true,
  onEnter: () =>
    document.querySelectorAll(".stat-number").forEach(animateCounter),
});
05

Scrub / Scroll-Driven

(a) Progress bar across section top — scrub: 0.25. (b) Integration pipeline slides horizontally as you scroll through — scrub: 1, pin: true.

Variant A — progress bar (scrub: 0.25)
HOW IT WORKS
From connection to decision in 14 seconds
Connect your store, ad accounts, and books. Luca reads across all three simultaneously and surfaces the one action that will move the needle most — right now.
Variant B — horizontal pipeline (scrub: 1, pinned)
CONNECTED PLATFORMS
↑ scroll through this section to drive the pipeline
// (a) Progress bar — scrub: 0.25 (slightly smoothed)
gsap.set("#scrub-bar", { width: "0%" });
gsap.to("#scrub-bar", {
  width:  "100%",
  ease:   "none",
  scrollTrigger: {
    trigger: "#progress-demo",
    start:   "top top",
    end:     "bottom bottom",
    scrub:   0.25,
  },
});

// (b) Horizontal pipeline — scrub: 1, pinned
const dist = document.querySelector("#pipeline-inner").scrollWidth
           - document.querySelector("#pipeline-outer").clientWidth;

gsap.set("#pipeline-inner", { x: 0 });
gsap.to("#pipeline-inner", {
  x:    -dist,
  ease: "none",
  scrollTrigger: {
    trigger: "#pipeline-outer",
    start:   "top 70%",
    end:     "+=500",
    scrub:   1,
  },
});
06

FAQ Accordion

GSAP height animation — not CSS display toggle. Animates from 0 to measured offsetHeight. Open/close logic with close-others behaviour.

Under 3 minutes. Connect Shopify, your ad accounts, and Xero via OAuth — no API keys, no developers. Luca starts generating insights within 60 seconds of your first data sync.
Only if you choose Autopilot mode. In Co-pilot mode (the default), Luca drafts every action and waits for your one-tap approval before executing. You can switch between modes or set per-platform limits at any time.
Brands doing €1M–€100M in annual revenue. Below that, the signal-to-noise ratio is too low for Luca's models to generate reliable recommendations. Above €100M, the complexity usually warrants a full finance team — though we do work with some at that level.
Yes. All data is encrypted in transit (TLS 1.3) and at rest (AES-256). We are SOC 2 Type II compliant and GDPR-ready. Your data is never used to train any model, and you can export or delete it at any time.
// FAQ — GSAP height tween, close-others pattern
let openItem = null;

document.querySelectorAll(".faq-item").forEach(item => {
  const body  = item.querySelector(".faq-body");
  const icon  = item.querySelector(".faq-icon");
  const inner = item.querySelector(".faq-body-inner");

  gsap.set(body, { height: 0 });

  item.querySelector(".faq-trigger")
    .addEventListener("click", () => {
      const isOpen = openItem === item;

      // close current
      if (openItem) {
        const b = openItem.querySelector(".faq-body");
        const ic = openItem.querySelector(".faq-icon");
        gsap.to(b,  { height: 0, duration: 0.35, ease: "power2.inOut" });
        gsap.to(ic, { rotation: 0, duration: 0.25 });
        openItem = null;
      }

      if (!isOpen) {
        const h = inner.offsetHeight;
        gsap.to(body, { height: h, duration: 0.4, ease: "power2.inOut" });
        gsap.to(icon, { rotation: 45, duration: 0.25 });
        openItem = item;
      }
    });
});
07

CTA Section Entrance + Glow

Headline fades up on scroll entry, then the CTA button pulses with a teal box-shadow glow. The glow animates from invisible to a soft 32px spread.

GET STARTED

Your store's first fully autonomous CFO is one click away.

No setup fee. No contract. Cancel any time.

// CTA entrance + glow pulse
gsap.set(["#cta-eyebrow", "#cta-headline", "#cta-sub", "#cta-btn"],
  { y: 30, opacity: 0 });

ScrollTrigger.create({
  trigger: "#cta-stage",
  start:   "top 75%",
  once:    true,
  onEnter() {
    const tl = gsap.timeline({ defaults: { ease: "power3.out" } });

    tl.to(["#cta-eyebrow", "#cta-headline", "#cta-sub"], {
        y: 0, opacity: 1, duration: 0.7, stagger: 0.12,
      })
      .to("#cta-btn", { y: 0, opacity: 1, duration: 0.5 }, "-=0.2")
      .to("#cta-btn", {
        boxShadow: "0 0 32px 8px rgba(0,181,181,0.35)",
        duration:  0.55,
        ease:      "power2.out",
      }, "+=0.1")
      .to("#cta-btn", {
        boxShadow: "0 0 18px 4px rgba(0,181,181,0.18)",
        duration:  0.8,
        ease:      "power2.inOut",
        repeat:    -1,
        yoyo:      true,
      });
  },
});
08

Hover Micro-Interactions

Four card hover states driven by GSAP (not CSS transitions). Hover over each card to see the effect. All use mouseenter/mouseleave pairs.

(a) Border Accent Glow
Ad spend health
Hover to see the teal border glow. Used on feature cards and plan cards.
(b) Subtle Lift
Cash flow clarity
Lifts 4px and deepens shadow. Used on integration cards and blog cards.
(c) Stat Colour Shift
€2.4M
Number fills with accent on hover. Used on pain-point and outcome cards.
(d) Arrow Nudge
See the case study
Read more →
// (a) Border glow
const a = document.querySelector("#hov-a");
a.addEventListener("mouseenter", () =>
  gsap.to(a, { borderColor: "rgba(0,181,181,0.5)", duration: 0.25 }));
a.addEventListener("mouseleave", () =>
  gsap.to(a, { borderColor: "rgba(255,255,255,0.07)", duration: 0.3 }));

// (b) Lift
const b = document.querySelector("#hov-b");
b.addEventListener("mouseenter", () =>
  gsap.to(b, { y: -4, boxShadow: "0 12px 32px rgba(0,0,0,0.35)", duration: 0.25 }));
b.addEventListener("mouseleave", () =>
  gsap.to(b, { y:  0, boxShadow: "0 0 0 rgba(0,0,0,0)",       duration: 0.3 }));

// (c) Stat colour shift
const c   = document.querySelector("#hov-c");
const num = document.querySelector("#hov-c-num");
c.addEventListener("mouseenter", () =>
  gsap.to(num, { color: "#00D4C8", duration: 0.2 }));
c.addEventListener("mouseleave", () =>
  gsap.to(num, { color: "#8888A0", duration: 0.25 }));

// (d) Arrow nudge
const d     = document.querySelector("#hov-d");
const arrow = document.querySelector("#hov-arrow");
gsap.set(arrow, { display: "inline-block" });
d.addEventListener("mouseenter", () =>
  gsap.to(arrow, { x: 4, duration: 0.2, ease: "power2.out" }));
d.addEventListener("mouseleave", () =>
  gsap.to(arrow, { x: 0, duration: 0.25 }));
09

Chat Window Simulation

Messages appear sequentially on scroll entry: user message slides from right, Luca response shows typing dots then fades in from left. This is the hero visual pattern on Homepage and Agent page.

Luca — AI Co-Founder
● Live
// Chat window — sequential timeline
function buildChatTimeline() {
  const body = document.querySelector("#chat-body");
  body.innerHTML = "";

  const msgs = [
    { role: "user",  text: "Why did our ROAS drop last week?" },
    { role: "luca",  text: "Meta CPMs rose 28% — broad audiences hit hardest. Your retargeting held fine." },
    { role: "user",  text: "What should I do?" },
    { role: "luca",  text: "Pause BAU broad. Shift €3.8k to lookalike 2% and your abandoned-cart sequence." },
  ];

  const tl = gsap.timeline();

  msgs.forEach((m, i) => {
    const row    = makeRow(m);       // creates DOM node
    const bubble = row.firstChild;
    body.appendChild(row);

    const isUser = m.role === "user";
    gsap.set(row, { opacity: 0, x: isUser ? 40 : -40 });

    if (!isUser) {
      // show typing dots first
      const dots = makeTypingDots();
      body.appendChild(dots);
      tl.to(dots, { opacity: 1, duration: 0.2 }, "+=0.3")
        .to(dots, { opacity: 0, duration: 0.15, delay: 0.8 }, ">")
        .call(() => dots.remove())
        .to(row, { opacity: 1, x: 0, duration: 0.4, ease: "power2.out" });
    } else {
      tl.to(row, { opacity: 1, x: 0, duration: 0.35, ease: "power2.out" }, "+=0.5");
    }
  });

  return tl;
}
10

Pinned Scroll Section

Left column stays sticky while right-side rows reveal on scroll. This is the Autonomy Levels pattern on the Agent page. Each right row fades in as it enters the viewport.

AUTONOMY LEVELS

You decide how much Luca does.

From a second opinion to full autopilot — set the level per platform, per action type. Change it any time.

01
Insights only
Luca surfaces what's happening and why. You get a daily briefing with ranked actions, but nothing is executed without your explicit instruction. Ideal for founders who want full control with less noise.
02
Co-pilot
Luca drafts every recommended action and queues it for your one-tap approval. You review in under 30 seconds per action via Slack or the dashboard. The default for most brands.
03
Supervised autopilot
Luca executes routine actions (budget shifts within ±15%, email resends, reorder triggers) automatically but sends you a real-time log. You can undo any action within 24 hours.
04
Full autopilot
Luca operates your growth levers end-to-end. You get a weekly executive summary. Reserved for brands that have run Co-pilot for 90+ days and have validated Luca's decision quality against their own benchmarks.
// Pinned left col + staggered right-side rows
gsap.set(".pr-row", { y: 40, opacity: 0 });

document.querySelectorAll(".pr-row").forEach((row, i) => {
  ScrollTrigger.create({
    trigger:  row,
    start:    "top 80%",
    once:     true,
    onEnter: () => gsap.to(row, {
      y: 0, opacity: 1,
      duration: 0.65,
      delay:    i * 0.06,
      ease:     "power3.out",
    }),
  });
});

// Left col — sticky is handled via CSS (position: sticky)
// No GSAP pin needed; native sticky is smoother and avoids
// reflow conflicts with ScrollTrigger pin padding.