Pages Hard
Interactive Case Study
Digital agency case study page integrating parallax depth hero cards, scroll-driven SVG workflow diagram, animated result counters, and staggered testimonial reveals.
Open in Lab
MCP
gsap scrolltrigger svg flip lenis
Targets: JS HTML
Code
:root {
--bg: #070a12;
--text: #f0f4fb;
--panel: #121a2b;
--border: #263249;
--accent: #86e8ff;
--muted: #8a95a8;
--purple: #ae52ff;
--warm: #ffcc66;
--pink: #ff40d6;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
overflow-x: hidden;
}
/* โโ Hero โโ */
.hero {
position: relative;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 5rem 2rem;
overflow: hidden;
}
/* Depth cards floating in background */
.hero-bg {
position: absolute;
inset: 0;
pointer-events: none;
}
.hero-card {
position: absolute;
padding: 1rem 1.5rem;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 1.5px;
text-transform: uppercase;
color: var(--muted);
}
.hc-1 {
top: 18%;
left: 8%;
color: var(--accent);
border-color: rgba(134, 232, 255, 0.25);
}
.hc-2 {
top: 55%;
left: 5%;
color: var(--warm);
border-color: rgba(255, 204, 102, 0.25);
}
.hc-3 {
top: 25%;
right: 8%;
color: var(--purple);
border-color: rgba(174, 82, 255, 0.25);
}
.hc-4 {
top: 65%;
right: 10%;
color: var(--pink);
border-color: rgba(255, 64, 214, 0.25);
}
.hero-content {
position: relative;
z-index: 1;
max-width: 680px;
text-align: center;
}
.eyebrow {
display: inline-block;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--accent);
border: 1px solid rgba(134, 232, 255, 0.25);
border-radius: 20px;
padding: 0.3rem 1rem;
margin-bottom: 1.5rem;
}
.hero h1 {
font-size: 3.8rem;
font-weight: 900;
line-height: 1.1;
margin-bottom: 1.5rem;
letter-spacing: -2px;
}
.hero h1 em {
font-style: normal;
color: var(--accent);
}
.hero-desc {
font-size: 1.1rem;
color: var(--muted);
margin-bottom: 2rem;
max-width: 540px;
margin-left: auto;
margin-right: auto;
line-height: 1.8;
}
.hero-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
}
.hero-tags span {
font-size: 0.72rem;
font-weight: 600;
padding: 0.3rem 0.8rem;
border: 1px solid var(--border);
border-radius: 20px;
color: var(--muted);
background: rgba(18, 26, 43, 0.8);
}
/* โโ Challenge โโ */
.challenge-section {
padding: 7rem 2rem;
border-top: 1px solid var(--border);
}
.content {
max-width: 1100px;
margin: 0 auto;
}
.challenge-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
align-items: start;
}
.section-tag {
display: inline-block;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 1rem;
}
.challenge-text h2 {
font-size: 2.2rem;
font-weight: 800;
margin-bottom: 1.5rem;
letter-spacing: -0.5px;
}
.challenge-text p {
font-size: 1rem;
color: var(--muted);
margin-bottom: 1.2rem;
line-height: 1.8;
}
.quote-block {
margin-top: 2rem;
padding: 1.5rem;
border-left: 3px solid var(--accent);
background: rgba(134, 232, 255, 0.04);
border-radius: 0 8px 8px 0;
}
.quote-block blockquote {
font-size: 1rem;
color: var(--text);
font-style: italic;
margin-bottom: 0.8rem;
line-height: 1.7;
}
.quote-block cite {
font-size: 0.82rem;
color: var(--muted);
font-style: normal;
}
.challenge-stats {
display: flex;
flex-direction: column;
gap: 1rem;
}
.stat-pill {
padding: 1.5rem;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
transition: all 0.3s ease;
}
.stat-pill:hover {
border-color: var(--accent);
}
.stat-val {
display: block;
font-size: 2rem;
font-weight: 800;
color: var(--accent);
margin-bottom: 0.3rem;
font-variant-numeric: tabular-nums;
}
.stat-label {
font-size: 0.85rem;
color: var(--muted);
}
/* โโ Process / SVG โโ */
.process-section {
padding: 7rem 2rem;
border-top: 1px solid var(--border);
background: rgba(18, 26, 43, 0.5);
}
.section-header {
text-align: center;
max-width: 560px;
margin: 0 auto 4rem;
}
.section-header h2 {
font-size: 2.2rem;
font-weight: 800;
margin-bottom: 0.8rem;
letter-spacing: -0.5px;
}
.section-header p {
font-size: 1rem;
color: var(--muted);
line-height: 1.8;
}
.svg-wrapper {
max-width: 960px;
margin: 0 auto;
padding: 0 1rem;
}
#process-svg {
width: 100%;
height: auto;
overflow: visible;
}
#process-svg text {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
.proc-line {
stroke: rgba(134, 232, 255, 0.4);
}
/* โโ Results โโ */
.results-section {
padding: 7rem 2rem;
border-top: 1px solid var(--border);
}
.results-grid {
max-width: 900px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.5rem;
}
.result-card {
text-align: center;
padding: 2.5rem 1rem;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
transition: all 0.3s ease;
}
.result-card:hover {
border-color: var(--accent);
transform: translateY(-3px);
}
.result-value {
font-size: 2.8rem;
font-weight: 900;
color: var(--accent);
letter-spacing: -1px;
margin-bottom: 0.5rem;
font-variant-numeric: tabular-nums;
}
.result-label {
font-size: 0.85rem;
color: var(--text);
font-weight: 600;
margin-bottom: 0.5rem;
}
.result-delta {
font-size: 0.78rem;
color: var(--muted);
}
.trend-up {
color: #4ade80;
}
/* โโ Testimonials โโ */
.testimonials-section {
padding: 7rem 2rem;
border-top: 1px solid var(--border);
background: rgba(18, 26, 43, 0.5);
}
.testimonials-grid {
max-width: 1000px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
.tl-card {
padding: 1.8rem;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
transition: all 0.3s ease;
}
.tl-card:hover {
border-color: rgba(134, 232, 255, 0.3);
transform: translateY(-3px);
}
.tl-card blockquote {
font-size: 0.95rem;
color: var(--text);
font-style: italic;
line-height: 1.7;
margin-bottom: 1.5rem;
}
.tl-author {
display: flex;
align-items: center;
gap: 0.8rem;
}
.tl-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 800;
background: hsl(var(--av-hue), 60%, 15%);
border: 2px solid hsl(var(--av-hue), 60%, 30%);
color: hsl(var(--av-hue), 70%, 60%);
flex-shrink: 0;
}
.tl-author strong {
display: block;
font-size: 0.88rem;
color: var(--text);
}
.tl-author span {
font-size: 0.78rem;
color: var(--muted);
}
/* โโ CTA โโ */
.cta-section {
padding: 8rem 2rem;
text-align: center;
border-top: 1px solid var(--border);
background: radial-gradient(ellipse at 50% 50%, rgba(134, 232, 255, 0.06) 0%, transparent 70%);
}
.cta-section h2 {
font-size: 2.8rem;
font-weight: 900;
margin-bottom: 1rem;
letter-spacing: -1px;
}
.cta-section p {
font-size: 1.1rem;
color: var(--muted);
margin-bottom: 2rem;
}
.btn-primary {
display: inline-block;
padding: 0.9rem 2rem;
background: var(--accent);
color: var(--bg);
border-radius: 6px;
font-weight: 700;
text-decoration: none;
transition: all 0.3s ease;
}
.btn-primary:hover {
background: rgba(134, 232, 255, 0.85);
transform: translateY(-2px);
}
/* โโ Responsive โโ */
@media (max-width: 900px) {
.results-grid {
grid-template-columns: repeat(2, 1fr);
}
.testimonials-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.hero h1 {
font-size: 2.8rem;
}
.challenge-grid {
grid-template-columns: 1fr;
gap: 2rem;
}
.results-grid {
grid-template-columns: repeat(2, 1fr);
}
.hc-1,
.hc-2,
.hc-3,
.hc-4 {
display: none;
}
}
@media (max-width: 480px) {
.hero h1 {
font-size: 2.2rem;
}
.results-grid {
grid-template-columns: 1fr 1fr;
}
.result-value {
font-size: 2.2rem;
}
}
html.reduced-motion * {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}if (!window.MotionPreference) {
const __mql = window.matchMedia("(prefers-reduced-motion: reduce)");
const __listeners = new Set();
const MotionPreference = {
prefersReducedMotion() {
return __mql.matches;
},
setOverride(value) {
const reduced = Boolean(value);
document.documentElement.classList.toggle("reduced-motion", reduced);
window.dispatchEvent(new CustomEvent("motion-preference", { detail: { reduced } }));
for (const listener of __listeners) {
try {
listener({ reduced, override: reduced, systemReduced: __mql.matches });
} catch {}
}
},
onChange(listener) {
__listeners.add(listener);
try {
listener({
reduced: __mql.matches,
override: null,
systemReduced: __mql.matches,
});
} catch {}
return () => __listeners.delete(listener);
},
getState() {
return { reduced: __mql.matches, override: null, systemReduced: __mql.matches };
},
};
window.MotionPreference = MotionPreference;
}
function prefersReducedMotion() {
return window.MotionPreference.prefersReducedMotion();
}
function initDemoShell() {
// No-op shim in imported standalone snippets.
}
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { Flip } from "gsap/Flip";
import Lenis from "lenis";
gsap.registerPlugin(ScrollTrigger, Flip);
initDemoShell({
title: "Interactive Case Study",
category: "pages",
tech: ["gsap", "scrolltrigger", "svg", "flip", "lenis"],
});
const lenis = new Lenis({ lerp: 0.1, smoothWheel: true });
lenis.on("scroll", ScrollTrigger.update);
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);
const reduced = prefersReducedMotion();
if (reduced) document.documentElement.classList.add("reduced-motion");
// โโโ Hero Parallax Depth Cards (Demo 38 technique) โโโโโโโโโโโโโโโโโโโโโโโโโโ
const heroCards = document.querySelectorAll(".hero-card");
const depths = [1.0, 0.65, 0.45, 0.8];
heroCards.forEach((card, i) => {
if (!reduced) {
gsap.set(card, { opacity: 0, scale: 0.7, y: 40 });
gsap.to(card, {
opacity: 1,
scale: 1,
y: 0,
duration: 1.2,
ease: "expo.out",
delay: 0.4 + i * 0.15,
});
}
});
// Hero content entrance
if (!reduced) {
gsap.set([".hero .eyebrow", ".hero h1", ".hero-desc", ".hero-tags"], { opacity: 0, y: 30 });
gsap
.timeline({ defaults: { ease: "expo.out" } })
.to(".hero .eyebrow", { opacity: 1, y: 0, duration: 0.7, delay: 0.5 })
.to(".hero h1", { opacity: 1, y: 0, duration: 0.9 }, "-=0.4")
.to(".hero-desc", { opacity: 1, y: 0, duration: 0.7 }, "-=0.5")
.to(".hero-tags span", { opacity: 1, y: 0, duration: 0.5, stagger: 0.07 }, "-=0.4");
gsap.set(".hero-tags span", { opacity: 0, y: 15 });
}
// Hero parallax depth on scroll
if (!reduced) {
ScrollTrigger.create({
trigger: ".hero",
start: "top top",
end: "bottom top",
scrub: 1,
onUpdate: (self) => {
heroCards.forEach((card, i) => {
const d = depths[i];
gsap.set(card, { y: self.progress * d * 80, overwrite: "auto" });
});
},
});
}
// โโโ Challenge Stats (scroll counter) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
document.querySelectorAll(".stat-pill").forEach((pill, i) => {
const valEl = pill.querySelector(".stat-val");
const target = parseFloat(pill.dataset.target);
const suffix = pill.dataset.suffix || "";
if (!reduced) {
gsap.set(pill, { opacity: 0, x: -20 });
gsap.to(pill, {
opacity: 1,
x: 0,
duration: 0.7,
ease: "expo.out",
delay: i * 0.1,
scrollTrigger: {
trigger: ".challenge-stats",
start: "top 75%",
toggleActions: "play none none reverse",
},
});
ScrollTrigger.create({
trigger: pill,
start: "top 80%",
end: "top 30%",
onUpdate: (self) => {
const val = target * self.progress;
const display = target % 1 !== 0 ? val.toFixed(1) : Math.round(val);
valEl.textContent = display + suffix;
},
});
} else {
valEl.textContent = target + suffix;
}
});
// Challenge text reveal
document.querySelectorAll(".challenge-text h2, .challenge-text p, .quote-block").forEach((el) => {
if (!reduced) {
gsap.set(el, { opacity: 0, y: 25 });
gsap.to(el, {
opacity: 1,
y: 0,
duration: 0.7,
ease: "expo.out",
scrollTrigger: { trigger: el, start: "top 75%", toggleActions: "play none none reverse" },
});
}
});
// โโโ SVG Process Workflow (Demo 40 technique) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const procLines = document.querySelectorAll(".proc-line");
procLines.forEach((path) => {
const length = path.getTotalLength();
gsap.set(path, { strokeDasharray: length, strokeDashoffset: length });
if (!reduced) {
gsap.to(path, {
strokeDashoffset: 0,
ease: "none",
scrollTrigger: {
trigger: ".process-section",
start: "top 65%",
end: "bottom 70%",
scrub: 1.2,
},
});
} else {
gsap.set(path, { strokeDashoffset: 0 });
}
});
// Process nodes sequential reveal
const procNodes = ["#pn-1", "#pn-2", "#pn-3", "#pn-4", "#pn-5"];
procNodes.forEach((id, i) => {
const node = document.querySelector(id);
if (!node) return;
if (!reduced) {
gsap.to(node, {
opacity: 1,
scale: 1,
duration: 0.5,
ease: "back.out(1.7)",
scrollTrigger: {
trigger: ".process-section",
start: `top ${72 - i * 5}%`,
toggleActions: "play none none reverse",
},
});
gsap.set(node, { scale: 0.7 });
} else {
gsap.set(node, { opacity: 1 });
}
});
// โโโ Results Counters (Demo 36 technique) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
document.querySelectorAll(".result-card").forEach((card, i) => {
const valEl = card.querySelector(".result-value");
const target = parseFloat(card.dataset.target);
const suffix = card.dataset.suffix || "";
if (!reduced) {
gsap.set(card, { opacity: 0, y: 40, scale: 0.9 });
gsap.to(card, {
opacity: 1,
y: 0,
scale: 1,
duration: 0.8,
ease: "expo.out",
delay: i * 0.1,
scrollTrigger: {
trigger: ".results-grid",
start: "top 75%",
toggleActions: "play none none reverse",
},
});
ScrollTrigger.create({
trigger: card,
start: "top 80%",
end: "top 25%",
onUpdate: (self) => {
const val = target * self.progress;
const display = target % 1 !== 0 ? val.toFixed(1) : Math.round(val);
valEl.textContent = display + suffix;
},
});
} else {
valEl.textContent = target + suffix;
}
});
// โโโ Testimonial Cards โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
document.querySelectorAll(".tl-card").forEach((card, i) => {
if (!reduced) {
gsap.set(card, { opacity: 0, y: 30 });
gsap.to(card, {
opacity: 1,
y: 0,
duration: 0.8,
ease: "expo.out",
delay: i * 0.12,
scrollTrigger: {
trigger: "#tl-grid",
start: "top 75%",
toggleActions: "play none none reverse",
},
});
}
});
// โโโ Section Headers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
document.querySelectorAll(".section-header").forEach((header) => {
if (!reduced) {
gsap.set(header.children, { opacity: 0, y: 25 });
gsap.to(header.children, {
opacity: 1,
y: 0,
duration: 0.7,
stagger: 0.1,
ease: "expo.out",
scrollTrigger: { trigger: header, start: "top 75%", toggleActions: "play none none reverse" },
});
}
});
// โโโ CTA โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
if (!reduced) {
gsap.set(".cta-section h2, .cta-section p, .cta-section a", { opacity: 0, y: 25 });
gsap.to(".cta-section h2, .cta-section p, .cta-section a", {
opacity: 1,
y: 0,
duration: 0.8,
stagger: 0.12,
ease: "expo.out",
scrollTrigger: {
trigger: ".cta-section",
start: "top 75%",
toggleActions: "play none none reverse",
},
});
}
// โโโ Motion preference โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
window.addEventListener("motion-preference", (e) => {
if (e.detail.reduced) {
gsap.globalTimeline.paused(true);
} else {
gsap.globalTimeline.paused(false);
}
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Case Study โ stealthisdesign</title>
<link rel="stylesheet" href="style.css">
<script type="importmap">{"imports":{"gsap":"https://esm.sh/gsap@3.13.0","gsap/ScrollTrigger":"https://esm.sh/gsap@3.13.0/ScrollTrigger","gsap/SplitText":"https://esm.sh/gsap@3.13.0/SplitText","gsap/Flip":"https://esm.sh/gsap@3.13.0/Flip","gsap/ScrambleTextPlugin":"https://esm.sh/gsap@3.13.0/ScrambleTextPlugin","gsap/TextPlugin":"https://esm.sh/gsap@3.13.0/TextPlugin","gsap/all":"https://esm.sh/gsap@3.13.0/all","gsap/":"https://esm.sh/gsap@3.13.0/","lenis":"https://esm.sh/lenis@1.1.13/dist/lenis.mjs","three":"https://esm.sh/three@0.171.0","three/addons/":"https://esm.sh/three@0.171.0/examples/jsm/"}}</script>
<style>html.lenis,
html.lenis body {
height: auto;
}
.lenis:not(.lenis-autoToggle).lenis-stopped {
overflow: clip;
}
.lenis [data-lenis-prevent],
.lenis [data-lenis-prevent-wheel],
.lenis [data-lenis-prevent-touch] {
overscroll-behavior: contain;
}
.lenis.lenis-smooth iframe {
pointer-events: none;
}
.lenis.lenis-autoToggle {
transition-property: overflow;
transition-duration: 1ms;
transition-behavior: allow-discrete;
}</style>
</head>
<body>
<!-- Hero with parallax depth cards -->
<section class="hero">
<div class="hero-bg" aria-hidden="true">
<div class="hero-card hc-1">Strategy</div>
<div class="hero-card hc-2">Design</div>
<div class="hero-card hc-3">Engineering</div>
<div class="hero-card hc-4">Launch</div>
</div>
<div class="hero-content">
<span class="eyebrow">Case Study โ 2025</span>
<h1>Redesigning a <br><em>$40M fintech</em> product</h1>
<p class="hero-desc">How a 6-month design sprint turned a legacy banking app into a modern platform โ without losing a single user.</p>
<div class="hero-tags">
<span>UX Research</span>
<span>Product Design</span>
<span>React</span>
<span>Node.js</span>
<span>View Transitions</span>
</div>
</div>
</section>
<!-- Challenge Section -->
<section class="challenge-section">
<div class="content">
<div class="challenge-grid">
<div class="challenge-text">
<span class="section-tag">The Challenge</span>
<h2>A product stuck in 2015</h2>
<p>The client had a 40,000-user web app built in 2015 โ reliable but rigid. Users complained about confusing navigation, slow load times, and a mobile experience that barely worked. Churn was climbing.</p>
<p>Our task: redesign the entire product over 24 weeks without disrupting existing users or the underlying infrastructure.</p>
<div class="quote-block">
<blockquote>"We need this to feel like 2025, not like a spreadsheet from 2015."</blockquote>
<cite>โ Head of Product, Client Co.</cite>
</div>
</div>
<div class="challenge-stats">
<div class="stat-pill" data-target="62" data-suffix="%" data-label="Drop in task completion after login">
<span class="stat-val">0%</span>
<span class="stat-label">Task completion drop</span>
</div>
<div class="stat-pill" data-target="14" data-suffix="s" data-label="Average time to complete key action">
<span class="stat-val">0s</span>
<span class="stat-label">Avg. time-on-task</span>
</div>
<div class="stat-pill" data-target="3.1" data-suffix="โ
" data-label="App Store rating before redesign">
<span class="stat-val">0โ
</span>
<span class="stat-label">Store rating (before)</span>
</div>
</div>
</div>
</div>
</section>
<!-- SVG Workflow โ Process -->
<section class="process-section">
<div class="section-header">
<span class="section-tag">Our Process</span>
<h2>How we worked</h2>
<p>A structured 6-phase process, scroll to see each phase draw in.</p>
</div>
<div class="svg-wrapper">
<svg id="process-svg" viewBox="0 0 900 260" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
<!-- Connector lines -->
<path class="proc-line" d="M 80 130 L 210 130" fill="none" stroke="#263249" stroke-width="2"/>
<path class="proc-line" d="M 290 130 L 420 130" fill="none" stroke="#263249" stroke-width="2"/>
<path class="proc-line" d="M 500 130 L 630 130" fill="none" stroke="#263249" stroke-width="2"/>
<path class="proc-line" d="M 710 130 L 820 130" fill="none" stroke="#263249" stroke-width="2"/>
<!-- Phase nodes -->
<g class="proc-node" id="pn-1" opacity="0">
<circle cx="45" cy="130" r="32" fill="#121a2b" stroke="#86e8ff" stroke-width="2"/>
<text x="45" y="126" text-anchor="middle" fill="#86e8ff" font-size="9" font-weight="700">DISCO</text>
<text x="45" y="138" text-anchor="middle" fill="#86e8ff" font-size="9" font-weight="700">VER</text>
<text x="45" y="175" text-anchor="middle" fill="#8a95a8" font-size="10">Wk 1โ2</text>
</g>
<g class="proc-node" id="pn-2" opacity="0">
<circle cx="255" cy="130" r="32" fill="#121a2b" stroke="#ae52ff" stroke-width="2"/>
<text x="255" y="126" text-anchor="middle" fill="#ae52ff" font-size="9" font-weight="700">DEFIN</text>
<text x="255" y="138" text-anchor="middle" fill="#ae52ff" font-size="9" font-weight="700">E</text>
<text x="255" y="175" text-anchor="middle" fill="#8a95a8" font-size="10">Wk 3โ5</text>
</g>
<g class="proc-node" id="pn-3" opacity="0">
<circle cx="465" cy="130" r="32" fill="#121a2b" stroke="#ffcc66" stroke-width="2"/>
<text x="465" y="126" text-anchor="middle" fill="#ffcc66" font-size="9" font-weight="700">DESI</text>
<text x="465" y="138" text-anchor="middle" fill="#ffcc66" font-size="9" font-weight="700">GN</text>
<text x="465" y="175" text-anchor="middle" fill="#8a95a8" font-size="10">Wk 6โ12</text>
</g>
<g class="proc-node" id="pn-4" opacity="0">
<circle cx="675" cy="130" r="32" fill="#121a2b" stroke="#ff40d6" stroke-width="2"/>
<text x="675" y="126" text-anchor="middle" fill="#ff40d6" font-size="9" font-weight="700">BUILD</text>
<text x="675" y="175" text-anchor="middle" fill="#8a95a8" font-size="10">Wk 13โ22</text>
</g>
<g class="proc-node" id="pn-5" opacity="0">
<circle cx="855" cy="130" r="32" fill="#121a2b" stroke="#86e8ff" stroke-width="2"/>
<text x="855" y="126" text-anchor="middle" fill="#86e8ff" font-size="9" font-weight="700">LAUN</text>
<text x="855" y="138" text-anchor="middle" fill="#86e8ff" font-size="9" font-weight="700">CH</text>
<text x="855" y="175" text-anchor="middle" fill="#8a95a8" font-size="10">Wk 23โ24</text>
</g>
</svg>
</div>
</section>
<!-- Results / Counters Section -->
<section class="results-section">
<div class="section-header">
<span class="section-tag">The Results</span>
<h2>Numbers don't lie</h2>
</div>
<div class="results-grid">
<div class="result-card" data-target="4.7" data-suffix="โ
">
<div class="result-value">0โ
</div>
<div class="result-label">App Store Rating</div>
<div class="result-delta trend-up">โ from 3.1</div>
</div>
<div class="result-card" data-target="89" data-suffix="%">
<div class="result-value">0%</div>
<div class="result-label">Task Completion Rate</div>
<div class="result-delta trend-up">โ from 38%</div>
</div>
<div class="result-card" data-target="3.2" data-suffix="s">
<div class="result-value">0s</div>
<div class="result-label">Avg. Time-on-Task</div>
<div class="result-delta trend-up">โ from 14s</div>
</div>
<div class="result-card" data-target="22" data-suffix="%">
<div class="result-value">0%</div>
<div class="result-label">Churn Reduction</div>
<div class="result-delta trend-up">โ YoY</div>
</div>
</div>
</section>
<!-- Testimonials (FLIP grid toggle) -->
<section class="testimonials-section">
<div class="section-header">
<span class="section-tag">Client Voices</span>
<h2>What they said</h2>
</div>
<div class="testimonials-grid" id="tl-grid">
<div class="tl-card">
<blockquote>"The new app feels like a completely different product. Our team actually enjoys using it now."</blockquote>
<div class="tl-author">
<div class="tl-avatar" style="--av-hue: 195">SL</div>
<div><strong>Sarah Lin</strong><span>Head of Product</span></div>
</div>
</div>
<div class="tl-card">
<blockquote>"Response time concerns we had were handled immediately. The team is incredibly professional."</blockquote>
<div class="tl-author">
<div class="tl-avatar" style="--av-hue: 270">JM</div>
<div><strong>James Malik</strong><span>CTO</span></div>
</div>
</div>
<div class="tl-card">
<blockquote>"Finally, we can recommend the app to clients without cringing. This was career-changing work."</blockquote>
<div class="tl-author">
<div class="tl-avatar" style="--av-hue: 45">AO</div>
<div><strong>Amara Osei</strong><span>VP Customer Success</span></div>
</div>
</div>
</div>
</section>
<!-- CTA -->
<section class="cta-section">
<div class="content">
<h2>Ready to work together?</h2>
<p>We take on 2โ3 product design engagements per quarter.</p>
<a href="/" class="btn-primary">Start a Conversation</a>
</div>
</section>
<script type="module" src="script.js"></script>
</body>
</html>Interactive Case Study
Digital agency case study page integrating parallax depth hero cards, scroll-driven SVG workflow diagram, animated result counters, and staggered testimonial reveals.
Source
- Repository:
libs-genclaude - Original demo id:
42-interactive-case-study
Notes
Digital agency case study page integrating parallax depth hero cards, scroll-driven SVG workflow diagram, animated result counters, and staggered testimonial reveals.