Copy Code<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Free Fintech SaaS Hero Template with Credit-Card Scan Loader</title>
<meta name="description" content="Free GSAP fintech landing page hero from ToolsWaves" />
<meta name="generator" content="ToolsWaves - https://toolswaves.in/landing-pages" />
<!-- Open Graph -->
<meta property="og:title" content="Free Fintech SaaS Hero Template with Credit-Card Scan Loader" />
<meta property="og:description" content="Free GSAP fintech landing page hero from ToolsWaves" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://toolswaves.in/og?title=Free%20Fintech%20SaaS%20Hero%20Template%20with%20Credit-Card%20Scan%20Loader&category=Landing%20Page&icon=%F0%9F%93%84" />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Free Fintech SaaS Hero Template with Credit-Card Scan Loader" />
<meta name="twitter:description" content="Free GSAP fintech landing page hero from ToolsWaves" />
<meta name="twitter:image" content="https://toolswaves.in/og?title=Free%20Fintech%20SaaS%20Hero%20Template%20with%20Credit-Card%20Scan%20Loader&category=Landing%20Page&icon=%F0%9F%93%84" />
<!-- Bootstrap (used by template ā replace with your own framework if you prefer) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--mint: #eaf1ed;
--mint-deep: #dde7e1;
--teal: #0f9e95;
--teal-deep: #0b817a;
--navy: #0c2530;
--navy-soft: #1a3a47;
--muted: #6b7b7a;
--line: #d4ded7;
--white: #ffffff;
--shadow-card: 0 30px 70px rgba(12, 37, 48, 0.12), 0 12px 24px rgba(12, 37, 48, 0.06);
--shadow-cc: 0 24px 50px rgba(15, 158, 149, 0.4), 0 10px 20px rgba(12, 37, 48, 0.15);
}
html, body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: var(--mint);
color: var(--navy);
overflow: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.back-link {
position: fixed;
bottom: 0.75rem;
left: 0.75rem;
z-index: 1000;
color: var(--navy);
text-decoration: none;
font-size: 0.65rem;
font-weight: 600;
letter-spacing: 0.1em;
padding: 0.45rem 0.9rem;
background: var(--white);
border-radius: 999px;
box-shadow: 0 4px 12px rgba(12, 37, 48, 0.08);
opacity: 0.85;
transition: opacity 0.3s, transform 0.3s;
}
.back-link:hover { opacity: 1; transform: translateY(-2px); }
/* =========================================================
LOADER ā credit card scan + counter
========================================================= */
.loader {
position: fixed;
inset: 0;
z-index: 999;
background: var(--mint);
display: flex;
align-items: center;
justify-content: center;
}
.loader__content {
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
}
.loader__card {
position: relative;
width: 340px;
height: 210px;
border-radius: 18px;
background: linear-gradient(135deg, var(--teal) 0%, var(--teal-deep) 100%);
padding: 1.6rem 1.5rem;
color: var(--white);
overflow: hidden;
box-shadow: var(--shadow-cc);
display: flex;
flex-direction: column;
justify-content: space-between;
}
.loader__card::before {
content: '';
position: absolute;
top: -60%;
right: -30%;
width: 200%;
height: 220%;
background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0.16) 0%, transparent 60%);
pointer-events: none;
}
.loader__card-top {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
z-index: 2;
}
.loader__card-chip {
width: 46px; height: 34px;
border-radius: 6px;
background: linear-gradient(135deg, #f3d57a, #c89a3a);
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(2, 1fr);
gap: 1px;
padding: 3px;
}
.loader__card-chip span {
background: rgba(0, 0, 0, 0.15);
border-radius: 1px;
}
.loader__card-wifi { width: 24px; height: 24px; opacity: 0.9; }
.loader__card-wifi svg { width: 100%; height: 100%; }
.loader__card-number {
display: flex;
gap: 0.9rem;
font-family: 'JetBrains Mono', monospace;
font-size: 1.15rem;
letter-spacing: 0.1em;
position: relative;
z-index: 2;
}
.loader__card-number span:last-child {
font-variant-numeric: tabular-nums;
}
.loader__card-foot {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.7rem;
letter-spacing: 0.2em;
font-weight: 600;
position: relative;
z-index: 2;
}
.loader__card-brand {
font-family: 'Inter', sans-serif;
font-style: italic;
font-weight: 800;
font-size: 1.6rem;
letter-spacing: -0.04em;
}
.loader__card-scan {
position: absolute;
top: 0;
left: -20%;
width: 40%;
height: 100%;
background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.5) 50%, transparent 100%);
transform: skewX(-15deg);
pointer-events: none;
z-index: 3;
}
.loader__card-check {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(11, 129, 122, 0.85);
color: var(--white);
opacity: 0;
backdrop-filter: blur(4px);
z-index: 4;
}
.loader__card-check svg { width: 80px; height: 80px; }
.loader__meta {
display: flex;
justify-content: space-between;
align-items: center;
width: 340px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.75rem;
color: var(--navy);
}
.loader__counter {
font-weight: 600;
font-variant-numeric: tabular-nums;
color: var(--teal-deep);
}
.loader__bar {
width: 340px;
height: 4px;
background: var(--mint-deep);
border-radius: 999px;
overflow: hidden;
}
.loader__bar-fill {
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--teal) 0%, var(--teal-deep) 100%);
border-radius: inherit;
}
/* =========================================================
FINTECH SECTION
========================================================= */
.fintech {
position: relative;
width: 100%;
height: 100vh;
min-height: 780px;
overflow: hidden;
background: var(--mint);
}
.fintech__bg {
position: absolute;
inset: 0;
background-image: repeating-linear-gradient(
-60deg,
transparent 0,
transparent 80px,
rgba(12, 37, 48, 0.025) 80px,
rgba(12, 37, 48, 0.025) 81px
);
pointer-events: none;
}
/* ---------- Top nav ---------- */
.topnav {
position: relative;
z-index: 10;
display: flex;
align-items: center;
padding: 1.5rem 3rem;
gap: 1.5rem;
}
.topnav__logo {
display: inline-flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
color: var(--navy);
}
.topnav__logo-mark { width: 28px; height: 28px; }
.topnav__logo-mark svg { width: 100%; height: 100%; display: block; }
.topnav__logo-text {
font-weight: 700;
font-size: 1.25rem;
letter-spacing: -0.01em;
}
.topnav__links {
display: flex;
gap: 2.25rem;
margin-left: 3rem;
}
.topnav__links a {
color: var(--navy);
text-decoration: none;
font-weight: 500;
font-size: 0.95rem;
position: relative;
padding-bottom: 0.2rem;
transition: color 0.3s;
}
.topnav__links a::after {
content: '';
position: absolute;
left: 0; bottom: 0;
width: 100%; height: 2px;
background: var(--teal);
transform: scaleX(0);
transform-origin: right;
transition: transform 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
.topnav__links a:hover::after { transform: scaleX(1); transform-origin: left; }
.topnav__actions {
margin-left: auto;
display: flex;
gap: 0.75rem;
}
.topnav__btn {
padding: 0.6rem 1.4rem;
border-radius: 999px;
font-weight: 600;
font-size: 0.9rem;
text-decoration: none;
transition: transform 0.25s ease, background 0.25s ease, color 0.25s ease;
}
.topnav__btn--ghost {
color: var(--navy);
border: 1px solid var(--line);
}
.topnav__btn--ghost:hover { background: var(--white); }
.topnav__btn--solid {
background: var(--teal);
color: var(--white);
box-shadow: 0 4px 14px rgba(15, 158, 149, 0.3);
}
.topnav__btn--solid:hover { background: var(--teal-deep); transform: translateY(-2px); }
/* ---------- Hero ---------- */
.hero {
position: relative;
z-index: 5;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
padding: 3rem 4rem 4rem;
align-items: center;
min-height: calc(100vh - 100px);
}
/* Left column */
.hero__left { max-width: 560px; }
.hero__title {
font-size: clamp(2.4rem, 4.4vw, 4rem);
font-weight: 700;
line-height: 1.12;
letter-spacing: -0.02em;
color: var(--navy);
margin-bottom: 1.75rem;
}
.hero__line {
display: block;
overflow: hidden;
padding-bottom: 0.05em;
}
.hero__line-inner {
display: inline-block;
transform: translateY(110%);
will-change: transform;
}
.hero__desc {
font-size: 1.05rem;
line-height: 1.55;
color: var(--muted);
margin-bottom: 2rem;
}
.hero__form {
display: flex;
align-items: center;
background: var(--white);
border-radius: 999px;
padding: 0.45rem;
max-width: 440px;
margin-bottom: 2.5rem;
box-shadow: 0 8px 24px rgba(12, 37, 48, 0.06);
}
.hero__input {
flex: 1;
border: none;
background: transparent;
padding: 0.75rem 1rem;
font-size: 0.95rem;
font-family: inherit;
color: var(--navy);
outline: none;
}
.hero__input::placeholder { color: var(--muted); }
.hero__submit {
display: inline-flex;
align-items: center;
gap: 0.4rem;
background: var(--teal);
color: var(--white);
border: none;
border-radius: 999px;
padding: 0.75rem 1.4rem;
font-size: 0.95rem;
font-weight: 600;
font-family: inherit;
cursor: pointer;
transition: background 0.25s, transform 0.25s;
}
.hero__submit svg { width: 16px; height: 16px; }
.hero__submit:hover { background: var(--teal-deep); transform: translateX(2px); }
.hero__logos {
display: flex;
gap: 2.5rem;
align-items: center;
flex-wrap: wrap;
}
.hero__logo {
font-size: 1.4rem;
font-weight: 700;
color: var(--navy);
letter-spacing: -0.02em;
opacity: 0.8;
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
.hero__logo em { color: var(--teal); font-style: normal; }
.hero__logo--2 { font-weight: 600; }
.hero__logo--3 svg { width: 18px; height: 18px; color: var(--navy); }
/* Right column ā payment card + credit card */
.hero__right {
position: relative;
min-height: 580px;
}
/* Credit card overlay */
.cc {
position: absolute;
top: -10px;
right: -40px;
width: 340px;
height: 210px;
border-radius: 18px;
background: linear-gradient(135deg, var(--teal) 0%, var(--teal-deep) 100%);
padding: 1.5rem;
color: var(--white);
box-shadow: var(--shadow-cc);
z-index: 3;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
will-change: transform;
}
.cc::before {
content: '';
position: absolute;
top: -80%; right: -40%;
width: 220%; height: 260%;
background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0.16) 0%, transparent 60%);
pointer-events: none;
}
.cc__label {
font-size: 0.75rem;
letter-spacing: 0.12em;
opacity: 0.9;
font-weight: 500;
position: relative; z-index: 2;
}
.cc__number {
display: flex;
gap: 1rem;
font-family: 'JetBrains Mono', monospace;
font-size: 1.3rem;
letter-spacing: 0.1em;
position: relative; z-index: 2;
}
.cc__wifi {
position: absolute;
right: 1.5rem;
bottom: 1.5rem;
width: 28px; height: 28px;
z-index: 2;
}
.cc__wifi svg { width: 100%; height: 100%; }
.cc__brand {
font-style: italic;
font-weight: 800;
font-size: 1.8rem;
letter-spacing: -0.04em;
position: relative; z-index: 2;
}
/* White payment card */
.pay {
position: absolute;
top: 40px;
right: 60px;
width: 380px;
background: var(--white);
border-radius: 20px;
padding: 1.75rem;
box-shadow: var(--shadow-card);
z-index: 2;
will-change: transform;
display: flex;
flex-direction: column;
gap: 1rem;
}
.pay__profile {
display: flex;
align-items: center;
gap: 0.75rem;
padding-bottom: 0.75rem;
}
.pay__avatar {
width: 44px; height: 44px;
border-radius: 12px;
overflow: hidden;
background: var(--teal);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.pay__avatar img { width: 100%; height: 100%; object-fit: cover; }
.pay__name {
font-weight: 700;
font-size: 1rem;
color: var(--navy);
}
.pay__email {
font-size: 0.8rem;
color: var(--muted);
}
.pay__invoice {
border: 1px solid var(--line);
border-radius: 14px;
padding: 1rem 1.25rem;
}
.pay__invoice-label {
font-size: 0.8rem;
color: var(--muted);
margin-bottom: 0.25rem;
}
.pay__invoice-amount {
font-size: 1.75rem;
font-weight: 700;
color: var(--navy);
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
margin-bottom: 0.25rem;
}
.pay__invoice-date {
font-size: 0.78rem;
color: var(--muted);
}
.pay__method {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.85rem 1rem;
border: 1px solid var(--line);
border-radius: 999px;
transition: border-color 0.25s ease, background 0.25s ease;
cursor: pointer;
}
.pay__method.is-active {
border-color: var(--teal);
background: rgba(15, 158, 149, 0.03);
}
.pay__method-icon { width: 22px; height: 22px; color: var(--navy); flex-shrink: 0; }
.pay__method-icon svg { width: 100%; height: 100%; }
.pay__method-label {
flex: 1;
font-size: 0.9rem;
font-weight: 500;
color: var(--navy);
}
.pay__method-radio {
width: 18px; height: 18px;
border-radius: 50%;
border: 1.5px solid var(--line);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: border-color 0.25s ease;
}
.pay__method-radio span {
width: 10px; height: 10px;
border-radius: 50%;
background: var(--teal);
transform: scale(0);
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.pay__method.is-active .pay__method-radio {
border-color: var(--teal);
}
.pay__method.is-active .pay__method-radio span {
transform: scale(1);
}
.pay__btn {
margin-top: 0.25rem;
background: var(--navy);
color: var(--white);
border: none;
border-radius: 12px;
padding: 1rem;
font-size: 1rem;
font-weight: 600;
font-family: inherit;
cursor: pointer;
position: relative;
overflow: hidden;
transition: background 0.25s, transform 0.2s;
}
.pay__btn:hover { background: var(--navy-soft); transform: translateY(-1px); }
.pay__btn::after {
content: '';
position: absolute;
top: 0; left: -100%;
width: 60%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transform: skewX(-20deg);
transition: none;
}
.pay__btn.is-shimmer::after {
animation: shimmerSweep 0.9s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes shimmerSweep {
0% { left: -100%; }
100% { left: 220%; }
}
/* ---------- Hidden initial states ---------- */
[data-nav], [data-fade], [data-logo], [data-cc], [data-pay] { opacity: 0; }
/* ---------- Responsive ---------- */
@media (max-width: 1100px) {
.topnav { padding: 1.25rem 2rem; }
.topnav__links { display: none; }
.hero { grid-template-columns: 1fr; padding: 2rem 2rem 3rem; gap: 2rem; }
.hero__right { min-height: 480px; }
.pay { right: 20px; width: 340px; }
.cc { right: 0; width: 300px; }
}
@media (max-width: 640px) {
.hero__logos { gap: 1.25rem; }
.hero__logo { font-size: 1.1rem; }
.pay { right: 50%; transform: translateX(50%); width: 90%; max-width: 360px; }
.cc { right: 50%; transform: translateX(40%); width: 280px; top: -20px; }
}
</style>
</head>
<body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Banner ā Fintech SaaS</title>
<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=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="banner-fintech-saas.css">
</head>
<body>
<!-- ========== LOADER ========== -->
<div class="loader" id="loader">
<div class="loader__content">
<div class="loader__card" id="loader-card">
<div class="loader__card-top">
<span class="loader__card-chip">
<span></span><span></span><span></span><span></span>
<span></span><span></span><span></span><span></span>
</span>
<span class="loader__card-wifi">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
<path d="M8.5 16.05a6 6 0 0 1 7 0"/>
<path d="M12 20h0"/>
</svg>
</span>
</div>
<div class="loader__card-number">
<span>ā¢ā¢ā¢ā¢</span>
<span>ā¢ā¢ā¢ā¢</span>
<span>ā¢ā¢ā¢ā¢</span>
<span id="loader-digits">0000</span>
</div>
<div class="loader__card-foot">
<span>SECURE LOAD</span>
<span class="loader__card-brand">VISA</span>
</div>
<div class="loader__card-scan" id="loader-scan"></div>
<div class="loader__card-check" id="loader-check">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
<path d="M5 12l5 5 9-11" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
<div class="loader__meta">
<span class="loader__label" id="loader-label">Securing your workspace</span>
<span class="loader__counter" id="loader-counter">$0</span>
</div>
<div class="loader__bar"><div class="loader__bar-fill" id="loader-bar"></div></div>
</div>
</div>
<section class="fintech" id="fintech">
<!-- Background diagonal stripes -->
<div class="fintech__bg"></div>
<!-- ========== TOP NAV ========== -->
<header class="topnav">
<a href="#" class="topnav__logo" data-nav>
<span class="topnav__logo-mark">
<svg viewBox="0 0 32 32" fill="none">
<path d="M4 4h16l8 8v16L20 20H4z" fill="#0f9e95"/>
<path d="M4 4h12v12L4 20z" fill="#0a2530"/>
</svg>
</span>
<span class="topnav__logo-text">Brand</span>
</a>
<nav class="topnav__links">
<a href="#" data-nav>Products</a>
<a href="#" data-nav>Customers</a>
<a href="#" data-nav>Pricing</a>
<a href="#" data-nav>Learn</a>
</nav>
<div class="topnav__actions">
<a href="#" class="topnav__btn topnav__btn--ghost" data-nav>Login</a>
<a href="#" class="topnav__btn topnav__btn--solid" data-nav>Sign Up</a>
</div>
</header>
<!-- ========== HERO CONTENT ========== -->
<div class="hero">
<!-- LEFT column -->
<div class="hero__left">
<h1 class="hero__title">
<span class="hero__line"><span class="hero__line-inner">Get paid early</span></span>
<span class="hero__line"><span class="hero__line-inner">save automatically</span></span>
<span class="hero__line"><span class="hero__line-inner">manage your flow.</span></span>
</h1>
<p class="hero__desc" data-fade>
Supports growing businesses with simple invoicing,<br>
powerful integrations, and cash-flow management tools.
</p>
<form class="hero__form" data-fade onsubmit="event.preventDefault()">
<input type="email" class="hero__input" placeholder="Your business email" aria-label="Business email">
<button type="submit" class="hero__submit">
Get Started
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M7 17L17 7M17 7H8M17 7v9" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</form>
<div class="hero__logos">
<span class="hero__logo hero__logo--1" data-logo>partner-a<em>.</em></span>
<span class="hero__logo hero__logo--2" data-logo>partnerbrand</span>
<span class="hero__logo hero__logo--3" data-logo>
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L4 9h3v9h3v-5h4v5h3V9h3z"/></svg>
partner-c
</span>
</div>
</div>
<!-- RIGHT column -->
<div class="hero__right">
<!-- Floating credit card overlay -->
<div class="cc" id="cc" data-cc>
<div class="cc__label">Credit Card</div>
<div class="cc__number">
<span>234</span>
<span>ā¢ā¢ā¢ā¢</span>
<span>ā¢ā¢ā¢ā¢</span>
</div>
<div class="cc__wifi">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
<path d="M5 12.55a11 11 0 0 1 14.08 0"/>
<path d="M8.5 16.05a6 6 0 0 1 7 0"/>
<path d="M12 20h0" stroke-linecap="round"/>
</svg>
</div>
<div class="cc__brand">VISA</div>
</div>
<!-- White payment card -->
<div class="pay" id="pay" data-pay>
<div class="pay__profile">
<div class="pay__avatar">
<img src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=96&h=96&q=80"
alt="Profile"
onerror="this.style.display='none'">
</div>
<div class="pay__info">
<div class="pay__name">Jane Doe</div>
<div class="pay__email">jane.doe@example.com</div>
</div>
</div>
<div class="pay__invoice">
<div class="pay__invoice-label">Invoice</div>
<div class="pay__invoice-amount" id="invoice-amount">$0</div>
<div class="pay__invoice-date">April 20, 2026</div>
</div>
<div class="pay__method is-active" data-method>
<span class="pay__method-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
<rect x="3" y="6" width="18" height="13" rx="2"/>
<path d="M3 10h18"/>
</svg>
</span>
<span class="pay__method-label">Credit Card</span>
<span class="pay__method-radio"><span></span></span>
</div>
<div class="pay__method" data-method>
<span class="pay__method-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
<path d="M3 10l9-6 9 6v1H3z"/>
<path d="M5 11v7M10 11v7M14 11v7M19 11v7M3 20h18"/>
</svg>
</span>
<span class="pay__method-label">Bank Account</span>
<span class="pay__method-radio"><span></span></span>
</div>
<button class="pay__btn" id="pay-btn">
<span>Pay</span>
</button>
</div>
</div>
</div>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="banner-fintech-saas.js"></script>
</body>
</html>
</body>
</html>JS banner-fintech-saas.js ā GSAP animation timeline
(() => {
const root = document.getElementById('fintech');
if (!root) return;
// -------------------------------------------------------------------
// ELEMENT REFERENCES
// -------------------------------------------------------------------
const loader = document.getElementById('loader');
const loaderCard = document.getElementById('loader-card');
const loaderScan = document.getElementById('loader-scan');
const loaderCounter = document.getElementById('loader-counter');
const loaderBar = document.getElementById('loader-bar');
const loaderDigits = document.getElementById('loader-digits');
const loaderCheck = document.getElementById('loader-check');
const loaderLabel = document.getElementById('loader-label');
const navEls = root.querySelectorAll('[data-nav]');
const lineInners = root.querySelectorAll('.hero__line-inner');
const fades = root.querySelectorAll('[data-fade]');
const logos = root.querySelectorAll('[data-logo]');
const cc = root.querySelector('[data-cc]');
const pay = root.querySelector('[data-pay]');
const invoiceAmount = document.getElementById('invoice-amount');
const payBtn = document.getElementById('pay-btn');
const methods = root.querySelectorAll('[data-method]');
const TARGET_AMOUNT = 1876580;
// -------------------------------------------------------------------
// INITIAL HIDDEN STATES
// -------------------------------------------------------------------
gsap.set(navEls, { y: -20, opacity: 0 });
gsap.set(fades, { y: 24, opacity: 0 });
gsap.set(logos, { y: 16, opacity: 0 });
gsap.set(cc, { x: 140, y: -60, rotation: 8, opacity: 0, scale: 0.85 });
gsap.set(pay, { x: 120, opacity: 0, scale: 0.92 });
// -------------------------------------------------------------------
// LOADER TIMELINE
// -------------------------------------------------------------------
const loaderTl = gsap.timeline({ onComplete: playScene });
// Card drop-in
loaderTl.from(loaderCard, {
y: -30,
opacity: 0,
duration: 0.6,
ease: 'power3.out',
}, 0);
// Scan line sweeps across the card
loaderTl.fromTo(loaderScan, {
x: 0,
}, {
x: 500,
duration: 1.3,
ease: 'power2.inOut',
repeat: 1,
}, 0.3);
// Card digit reel (simulating number scan) + currency counter + bar
const p = { digits: 0, amount: 0, progress: 0 };
loaderTl.to(p, {
digits: 9999,
duration: 1.8,
ease: 'power1.inOut',
onUpdate: () => {
// Randomise digits for scanning effect
const n = Math.floor(Math.random() * 10000);
loaderDigits.textContent = String(n).padStart(4, '0');
},
}, 0.3);
loaderTl.to(p, {
amount: TARGET_AMOUNT,
duration: 2,
ease: 'power2.out',
onUpdate: () => {
loaderCounter.textContent = '$' + Math.round(p.amount).toLocaleString('en-US');
},
}, 0.3);
loaderTl.to(loaderBar, {
width: '100%',
duration: 2,
ease: 'power2.out',
}, 0.3);
// Final digits settle on target
loaderTl.call(() => {
loaderDigits.textContent = '1234';
}, null, 2.1);
// Success state
loaderTl.to(loaderCheck, {
opacity: 1,
duration: 0.4,
ease: 'power2.out',
}, '+=0.1');
loaderTl.from(loaderCheck.querySelector('svg'), {
scale: 0,
rotation: -45,
duration: 0.6,
ease: 'back.out(2)',
}, '<');
loaderTl.call(() => {
loaderLabel.textContent = 'Workspace ready';
}, null, '<');
// Exit
loaderTl.to(loaderCard, {
y: -40,
scale: 0.94,
duration: 0.5,
ease: 'power3.in',
}, '+=0.4');
loaderTl.to(loader, {
opacity: 0,
duration: 0.6,
ease: 'power2.inOut',
}, '-=0.2');
loaderTl.set(loader, { display: 'none' });
// -------------------------------------------------------------------
// MAIN SCENE ENTRANCE
// -------------------------------------------------------------------
function playScene() {
const tl = gsap.timeline({ defaults: { ease: 'power3.out' } });
// Nav (logo + links + actions)
tl.to(navEls, {
y: 0, opacity: 1,
duration: 0.6,
stagger: 0.07,
}, 0);
// Title lines rise from below masks
tl.to(lineInners, {
y: '0%',
duration: 1,
stagger: 0.12,
ease: 'power4.out',
}, 0.2);
// Description + form
tl.to(fades, {
y: 0, opacity: 1,
duration: 0.7,
stagger: 0.15,
}, 0.7);
// Partner logos
tl.to(logos, {
y: 0, opacity: 1,
duration: 0.6,
stagger: 0.1,
}, 1);
// Payment card slides in from right first
tl.to(pay, {
x: 0,
opacity: 1,
scale: 1,
duration: 1.1,
ease: 'power4.out',
}, 0.6);
// Credit card slides in on top with a small rotation settle
tl.to(cc, {
x: 0,
y: 0,
rotation: 0,
opacity: 1,
scale: 1,
duration: 1.1,
ease: 'power4.out',
}, 0.85);
// Invoice counter
const ic = { v: 0 };
tl.to(ic, {
v: TARGET_AMOUNT,
duration: 1.6,
ease: 'power2.out',
onUpdate: () => {
invoiceAmount.textContent = '$' + Math.round(ic.v).toLocaleString('en-US');
},
}, 1.2);
// Radio indicator pulse on the active method
tl.fromTo(methods[0].querySelector('.pay__method-radio span'),
{ scale: 0 },
{ scale: 1, duration: 0.5, ease: 'back.out(2.5)' },
2.2);
// Pay button shimmer
tl.to(payBtn, {
scale: 1.02,
duration: 0.25,
yoyo: true,
repeat: 1,
ease: 'sine.inOut',
}, 2.4);
tl.call(() => parallaxReady = true, null, 2.6);
}
// Shimmer loop on the Pay button (pseudo-element animated via CSS class)
setInterval(() => {
if (!payBtn) return;
payBtn.classList.add('is-shimmer');
setTimeout(() => payBtn.classList.remove('is-shimmer'), 900);
}, 4500);
// -------------------------------------------------------------------
// METHOD RADIO SWITCH
// -------------------------------------------------------------------
methods.forEach((m) => {
m.addEventListener('click', () => {
methods.forEach(other => other.classList.remove('is-active'));
m.classList.add('is-active');
// Bounce feedback
gsap.fromTo(m,
{ scale: 0.98 },
{ scale: 1, duration: 0.4, ease: 'back.out(2)' }
);
});
});
// -------------------------------------------------------------------
// MOUSE PARALLAX on cards (3D tilt) + subtle idle float
// -------------------------------------------------------------------
const right = root.querySelector('.hero__right');
let rx = 0, ry = 0, trx = 0, try_ = 0;
let parallaxReady = false;
let idleT = 0;
right.addEventListener('mousemove', (e) => {
const r = right.getBoundingClientRect();
trx = ((e.clientX - r.left) / r.width - 0.5) * 2;
try_ = ((e.clientY - r.top) / r.height - 0.5) * 2;
});
right.addEventListener('mouseleave', () => { trx = 0; try_ = 0; });
gsap.ticker.add((time, deltaTime) => {
if (!parallaxReady) return;
rx += (trx - rx) * 0.08;
ry += (try_ - ry) * 0.08;
idleT += deltaTime * 0.001;
// Gentle breathing motion baked into the transform
const payFloat = Math.sin(idleT * 1.3) * 6;
const ccFloat = Math.sin(idleT * 1.1 + 0.8) * 9;
gsap.set(pay, {
rotationY: rx * 6,
rotationX: -ry * 4,
x: rx * 6,
y: payFloat,
transformPerspective: 1000,
transformOrigin: 'center center',
});
gsap.set(cc, {
rotationY: rx * 10,
rotationX: -ry * 6,
x: rx * 14,
y: ccFloat,
transformPerspective: 1000,
transformOrigin: 'center center',
});
});
})();