ToolsWaves
Free GSAP Landing Page Template · ToolsWaves · Portfolio

Free Creative Portfolio Hero Template with Clapperboard Loader

Free creative portfolio landing page template from ToolsWaves featuring a clapperboard countdown loader (3-2-1-ACTION), a looping background video, custom cursor, magnetic nav, chip hover tilt, mute toggle, and character-proximity title effects. Free for commercial use.

VideoClapperboardCustom CursorPortfolioCinematic

About this ToolsWaves template

This free creative portfolio landing page template on ToolsWaves draws on film-industry visual language. The loader is a clapperboard counting down 3-2-1-ACTION before the page resolves — an unmistakable signal that motion is central to the work being showcased. A looping background video plays behind everything, a custom cursor follows movement, and magnetic navigation responds to proximity. Service chips tilt slightly on hover, the title characters react to cursor proximity individually, and a mute toggle lets visitors decide whether the ambient audio plays.

Use this template for video production studios, motion design portfolios, creative agencies, or anyone whose primary medium is moving image. ToolsWaves provides this template free for any commercial or personal project — copy the three template files and replace the placeholder showreel video with your own studio's footage. The custom cursor and magnetic navigation can be tuned in the JavaScript file to match the energy level of your brand, from subtle and reserved to more pronounced and theatrical.

Copy the code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Free Creative Portfolio Hero Template with Clapperboard Loader</title>
  <meta name="description" content="Free GSAP video production landing page hero from ToolsWaves" />
  <meta name="generator" content="ToolsWaves - https://toolswaves.in/landing-pages" />

  <!-- Open Graph -->
  <meta property="og:title" content="Free Creative Portfolio Hero Template with Clapperboard Loader" />
  <meta property="og:description" content="Free GSAP video production landing page hero from ToolsWaves" />
  <meta property="og:type" content="website" />
  <meta property="og:image" content="https://toolswaves.in/og?title=Free%20Creative%20Portfolio%20Hero%20Template%20with%20Clapperboard%20Loader&category=Landing%20Page&icon=%F0%9F%93%84" />

  <!-- Twitter -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="Free Creative Portfolio Hero Template with Clapperboard Loader" />
  <meta name="twitter:description" content="Free GSAP video production landing page hero from ToolsWaves" />
  <meta name="twitter:image" content="https://toolswaves.in/og?title=Free%20Creative%20Portfolio%20Hero%20Template%20with%20Clapperboard%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 {
    --bg:     #0a0a0a;
    --ink:    #ffffff;
    --muted:  rgba(255, 255, 255, 0.55);
    --line:   rgba(255, 255, 255, 0.12);
    --lime:   #c9ff2d;
    --lime-deep: #a8dd15;
}

html, body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    background: var(--bg);
    color: var(--ink);
    overflow: hidden;
    cursor: none;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

@media (hover: none), (pointer: coarse) {
    html, body { cursor: auto; }
}

.back-link {
    position: fixed;
    bottom: 4.5rem;
    left: 0.75rem;
    z-index: 1000;
    color: var(--ink);
    text-decoration: none;
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    padding: 0.45rem 0.9rem;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 999px;
    backdrop-filter: blur(8px);
    opacity: 0.85;
    cursor: none;
    transition: opacity 0.3s, transform 0.3s, background 0.3s;
}
.back-link:hover { opacity: 1; transform: translateY(-2px); background: rgba(255, 255, 255, 0.14); }

/* =========================================================
   CUSTOM CURSOR
   ========================================================= */
.cursor-dot, .cursor-ring {
    position: fixed;
    top: 0; left: 0;
    pointer-events: none;
    z-index: 10000;
    transform: translate(-50%, -50%);
    mix-blend-mode: difference;
    will-change: transform;
}
.cursor-dot {
    width: 6px; height: 6px;
    background: var(--lime);
    border-radius: 50%;
}
.cursor-ring {
    width: 36px; height: 36px;
    border: 1px solid rgba(201, 255, 45, 0.6);
    border-radius: 50%;
    transition: width 0.3s ease, height 0.3s ease, border-color 0.3s ease;
}
.cursor-ring.is-hover {
    width: 60px; height: 60px;
    border-color: var(--lime);
}
@media (hover: none), (pointer: coarse) {
    .cursor-dot, .cursor-ring { display: none; }
}

/* =========================================================
   LOADER — Clapperboard
   ========================================================= */
.loader {
    position: fixed;
    inset: 0;
    z-index: 999;
    background: var(--bg);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2rem;
}

.clapper {
    position: relative;
    width: min(420px, 78vw);
    height: min(260px, 55vw);
    will-change: transform;
}

.clapper__arm {
    position: absolute;
    top: 0; left: 0;
    width: 100%;
    height: 30%;
    background: var(--ink);
    transform-origin: 6% 100%;
    z-index: 2;
    display: flex;
    align-items: flex-end;
    padding-top: 4px;
    will-change: transform;
}
.clapper__stripes {
    display: flex;
    width: 100%;
    height: 65%;
    overflow: hidden;
}
.clapper__stripes span {
    flex: 1;
    transform: skewX(-30deg);
    transform-origin: bottom;
}
.clapper__stripes span:nth-child(odd) { background: var(--ink); }
.clapper__stripes span:nth-child(even) { background: var(--bg); }

.clapper__body {
    position: absolute;
    top: 30%;
    left: 0;
    right: 0;
    bottom: 0;
    background: #1a1a1a;
    border: 2px solid var(--ink);
    padding: 1.25rem 1.5rem;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.85rem;
    color: var(--ink);
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    letter-spacing: 0.1em;
    gap: 0.2rem;
}
.clapper__row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
    padding-bottom: 0.35rem;
    font-size: 0.72rem;
}
.clapper__row:last-child { border-bottom: none; }
.clapper__row span { color: rgba(255, 255, 255, 0.5); }
.clapper__row b { color: var(--lime); font-weight: 600; }

.loader__countdown {
    font-family: 'Inter', sans-serif;
    font-weight: 900;
    font-size: clamp(4rem, 12vw, 9rem);
    color: var(--lime);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    text-shadow: 0 0 40px rgba(201, 255, 45, 0.3);
}

.loader__label {
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.7rem;
    letter-spacing: 0.35em;
    color: var(--muted);
}

/* =========================================================
   REEL SECTION
   ========================================================= */
.reel {
    position: relative;
    width: 100%;
    height: 100vh;
    min-height: 720px;
    overflow: hidden;
    background: var(--bg);
}

.reel__video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 1;
    filter: saturate(1.1) contrast(1.05) brightness(0.7);
}

.reel__overlay {
    position: absolute;
    inset: 0;
    z-index: 2;
    background:
        radial-gradient(ellipse at 30% 50%, rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.75) 80%),
        linear-gradient(180deg, rgba(10, 10, 10, 0.6) 0%, rgba(10, 10, 10, 0.3) 30%, rgba(10, 10, 10, 0.85) 100%);
    pointer-events: none;
}

/* ---------- Top nav ---------- */
.topnav {
    position: relative;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: 2rem;
    padding: 1rem 1.5rem;
    border-bottom: 1px solid var(--line);
    background: rgba(10, 10, 10, 0.35);
    backdrop-filter: blur(10px);
}
.topnav__logo {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--ink);
    text-decoration: none;
    font-weight: 700;
    font-size: 1.35rem;
    letter-spacing: -0.01em;
    will-change: transform;
    cursor: none;
}
.topnav__logo-mark { width: 36px; height: 36px; }
.topnav__logo-mark svg {
    width: 100%; height: 100%;
    transition: transform 0.7s cubic-bezier(0.65, 0, 0.35, 1);
}
.topnav__logo:hover .topnav__logo-mark svg { transform: rotate(360deg); }

.topnav__links {
    margin: 0 auto;
    display: flex;
    gap: 2.5rem;
}
.topnav__links a {
    position: relative;
    color: var(--ink);
    text-decoration: none;
    font-weight: 600;
    font-size: 0.85rem;
    letter-spacing: 0.1em;
    padding: 0.3rem 0;
    cursor: none;
    will-change: transform;
}
.topnav__links a.is-active { color: var(--lime); }
.topnav__links a::after {
    content: '';
    position: absolute;
    left: 0; bottom: -2px;
    width: 100%; height: 1px;
    background: var(--lime);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.4s cubic-bezier(0.65, 0, 0.35, 1);
}
.topnav__links a.is-active::after,
.topnav__links a:hover::after { transform: scaleX(1); transform-origin: left; }

.topnav__actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}
.topnav__mute {
    width: 40px; height: 40px;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: var(--ink);
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: none;
    will-change: transform;
    transition: background 0.3s ease;
}
.topnav__mute:hover { background: rgba(255, 255, 255, 0.18); }
.topnav__mute svg { width: 16px; height: 16px; }

.topnav__cta {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: var(--lime);
    color: var(--bg);
    text-decoration: none;
    padding: 0.75rem 1.2rem;
    border-radius: 999px;
    font-weight: 600;
    font-size: 0.9rem;
    cursor: none;
    will-change: transform;
    transition: background 0.3s;
}
.topnav__cta svg { width: 16px; height: 16px; transition: transform 0.3s ease; }
.topnav__cta:hover { background: var(--lime-deep); }
.topnav__cta:hover svg { transform: translateX(3px); }

/* ---------- Rails ---------- */
.rail {
    position: absolute;
    z-index: 15;
    display: flex;
    flex-direction: column;
    align-items: center;
    pointer-events: none;
}
.rail > * { pointer-events: auto; }
.rail--left {
    top: 6rem; left: 0.75rem; bottom: 5rem;
    gap: 2rem;
}
.rail--right {
    top: 6rem; right: 0.75rem; bottom: 5rem;
    gap: 1.5rem;
}

.rail__dots {
    display: grid;
    grid-template-columns: repeat(3, 6px);
    gap: 4px;
}
.rail__dots span {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--ink);
    cursor: none;
    will-change: transform;
    transition: background 0.3s ease;
}
.rail__dots span:hover { background: var(--lime); }

.rail__phone {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    color: var(--ink);
    text-decoration: none;
    font-size: 0.72rem;
    letter-spacing: 0.25em;
    font-family: 'JetBrains Mono', monospace;
    cursor: none;
    transition: color 0.3s ease;
}
.rail__phone:hover { color: var(--lime); }

.rail__scroll, .rail__follow {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    font-size: 0.65rem;
    letter-spacing: 0.35em;
    color: var(--muted);
}
.rail__follow { margin-top: 0; }
.rail__scroll-label, .rail__follow-label {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
}
.rail__scroll-line, .rail__follow-line {
    width: 1px;
    height: 70px;
    background: linear-gradient(180deg, rgba(255,255,255,0.4), transparent);
    display: block;
    transform-origin: top;
}
.rail__scroll-arrow { font-size: 0.8rem; opacity: 0.6; }

.rail__social {
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    margin-top: auto;
    align-items: center;
}
.rail__social-icon {
    width: 34px; height: 34px;
    border-radius: 50%;
    background: rgba(255,255,255,0.06);
    border: 1px solid rgba(255,255,255,0.12);
    color: var(--ink);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    font-size: 0.75rem;
    font-weight: 700;
    cursor: none;
    will-change: transform;
    transition: background 0.3s ease, color 0.3s ease;
}
.rail__social-icon svg { width: 15px; height: 15px; }
.rail__social-icon:hover { background: var(--lime); color: var(--bg); }

/* ---------- Hero content ---------- */
.hero {
    position: absolute;
    left: 5rem;
    top: 6rem;
    right: 5rem;
    bottom: 5rem;
    z-index: 10;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    gap: 1.5rem;
    pointer-events: none;
}
.hero > * { pointer-events: auto; }

.hero__eyebrow {
    position: relative;
    display: inline-flex;
    align-items: flex-start;
    gap: 0.4rem;
    color: var(--ink);
    text-decoration: none;
    font-size: 0.95rem;
    font-weight: 500;
    line-height: 1.4;
    padding-bottom: 0.4rem;
    max-width: max-content;
    cursor: none;
    will-change: transform;
}
.hero__eyebrow-text {
    border-bottom: 1px solid rgba(255, 255, 255, 0.4);
    padding-bottom: 0.2rem;
}
.hero__eyebrow-arrow {
    display: inline-block;
    font-size: 0.85em;
    transition: transform 0.4s cubic-bezier(0.65, 0, 0.35, 1);
}
.hero__eyebrow:hover .hero__eyebrow-arrow {
    transform: translate(4px, -4px) rotate(3deg);
}

.hero__title {
    font-weight: 800;
    font-size: clamp(3rem, 9vw, 8rem);
    line-height: 0.95;
    letter-spacing: -0.04em;
    color: var(--ink);
    margin: auto 0;
    max-width: 780px;
    text-shadow: 0 8px 40px rgba(0, 0, 0, 0.5);
}
.hero__line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.05em;
}
.hero__char {
    display: inline-block;
    will-change: transform, color;
    transition: color 0.3s ease;
}
.hero__char.is-space { width: 0.25em; }

.hero__play {
    display: inline-flex;
    align-items: center;
    gap: 1rem;
    text-decoration: none;
    color: var(--ink);
    max-width: max-content;
    cursor: none;
    will-change: transform;
}
.hero__play-scribble { width: 70px; height: 52px; }
.hero__play-scribble path {
    stroke-dasharray: 120;
    stroke-dashoffset: 120;
}
.hero__play-btn {
    position: relative;
    width: 62px;
    height: 62px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.hero__play-btn > svg:first-child {
    width: 100%; height: 100%;
    transition: transform 0.3s ease;
}
.hero__play-ring {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    will-change: transform;
}
.hero__play:hover .hero__play-btn > svg:first-child { transform: scale(1.08); }
.hero__play-label {
    display: flex;
    flex-direction: column;
    font-weight: 500;
    font-size: 1rem;
    line-height: 1.1;
}

/* Floating stat chips */
.chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin-top: 0.5rem;
}
.chip {
    padding: 0.45rem 0.9rem;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 999px;
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    color: var(--ink);
    backdrop-filter: blur(10px);
    cursor: none;
    will-change: transform;
    transition: background 0.3s, border-color 0.3s;
}
.chip b { color: var(--lime); margin-right: 0.3rem; font-weight: 700; }
.chip:hover { background: rgba(201, 255, 45, 0.1); border-color: rgba(201, 255, 45, 0.4); }

/* ---------- Marquee ---------- */
.marquee {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    z-index: 10;
    background: var(--lime);
    color: var(--bg);
    padding: 1.1rem 0;
    overflow: hidden;
    white-space: nowrap;
}
.marquee__track { display: flex; width: max-content; will-change: transform; }
.marquee__group { display: flex; gap: 3rem; padding: 0 1.5rem; align-items: center; }
.marquee__item {
    font-size: clamp(1rem, 1.7vw, 1.5rem);
    font-weight: 700;
    letter-spacing: -0.01em;
    cursor: none;
}

/* ---------- Initial hidden states ---------- */
[data-magnetic], [data-rail], [data-social], [data-eyebrow],
[data-title-line] .hero__char, [data-play], [data-chips] {
    opacity: 0;
}

/* ---------- Responsive ---------- */
@media (max-width: 1100px) {
    .topnav__links { display: none; }
    .hero { left: 4rem; right: 3.5rem; top: 5rem; }
}
@media (max-width: 768px) {
    .rail--right .rail__follow { display: none; }
    .rail--left .rail__phone { display: none; }
    .rail__social { gap: 0.4rem; }
    .rail__social-icon { width: 28px; height: 28px; }
    .hero { left: 3rem; right: 3rem; top: 5rem; bottom: 4.5rem; }
    .hero__title { font-size: clamp(2.3rem, 11vw, 6rem); }
    .topnav { padding: 0.75rem 1rem; gap: 0.75rem; }
    .topnav__logo-text { display: none; }
    .chips { gap: 0.4rem; }
    .chip { font-size: 0.65rem; padding: 0.3rem 0.7rem; }
    .clapper__body { font-size: 0.65rem; padding: 1rem; }
    .clapper__row { font-size: 0.6rem; }
}
@media (max-width: 480px) {
    .rail { gap: 1rem; }
    .rail--left { top: 5rem; }
    .rail--right { top: 5rem; }
    .rail__scroll, .rail__follow, .rail__dots { display: none; }
    .hero { left: 1.25rem; right: 1.25rem; }
    .topnav__mute { width: 32px; height: 32px; }
    .topnav__cta { padding: 0.5rem 0.9rem; font-size: 0.8rem; }
}

  </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 — Creative Reel</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;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="banner-creative-reel.css">
</head>
<body>
    

    <!-- Custom cursor -->
    <div class="cursor-dot" id="cursor-dot"></div>
    <div class="cursor-ring" id="cursor-ring"></div>

    <!-- ========== LOADER — CLAPPERBOARD + COUNTDOWN ========== -->
    <div class="loader" id="loader">
        <div class="clapper" id="clapper">
            <div class="clapper__arm" id="clapper-arm">
                <div class="clapper__stripes">
                    <span></span><span></span><span></span><span></span>
                    <span></span><span></span><span></span><span></span>
                    <span></span><span></span><span></span><span></span>
                </div>
            </div>
            <div class="clapper__body">
                <div class="clapper__row"><span>SCENE</span><b>01</b></div>
                <div class="clapper__row"><span>TAKE</span><b>01</b></div>
                <div class="clapper__row"><span>DATE</span><b>04 · 20 · 26</b></div>
                <div class="clapper__row"><span>DIRECTOR</span><b>BRAND</b></div>
                <div class="clapper__row"><span>PROD.</span><b>CREATIVE REEL</b></div>
            </div>
        </div>
        <div class="loader__countdown" id="countdown">3</div>
        <div class="loader__label">ROLLING</div>
    </div>

    <section class="reel" id="reel">

        <!-- Background video -->
        <video class="reel__video" id="reel-video" autoplay loop muted playsinline
               poster="https://images.unsplash.com/photo-1485846234645-a62644f84728?auto=format&fit=crop&w=1600&q=75">
            <source src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4" type="video/mp4">
        </video>

        <!-- Dark overlay + vignette for readability -->
        <div class="reel__overlay"></div>

        <!-- ========== TOP NAV ========== -->
        <header class="topnav">
            <a href="#" class="topnav__logo" data-magnetic>
                <span class="topnav__logo-mark">
                    <svg viewBox="0 0 40 40">
                        <circle cx="20" cy="20" r="20" fill="#c9ff2d"/>
                        <path d="M16 12v16M12 20h16M14 14l12 12M26 14L14 26" stroke="#0a0a0a" stroke-width="1.5"/>
                    </svg>
                </span>
                <span class="topnav__logo-text">Brand</span>
            </a>
            <nav class="topnav__links">
                <a href="#" class="is-active" data-magnetic data-link>HOME</a>
                <a href="#" data-magnetic data-link>ABOUT</a>
                <a href="#" data-magnetic data-link>WORKS</a>
                <a href="#" data-magnetic data-link>SERVICES</a>
                <a href="#" data-magnetic data-link>CONTACT</a>
            </nav>
            <div class="topnav__actions">
                <button class="topnav__mute" id="mute-btn" data-magnetic aria-label="Mute">
                    <svg class="mute-on" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                        <path d="M11 5L6 9H2v6h4l5 4z"/>
                        <line x1="23" y1="9" x2="17" y2="15"/>
                        <line x1="17" y1="9" x2="23" y2="15"/>
                    </svg>
                    <svg class="mute-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" style="display:none;">
                        <path d="M11 5L6 9H2v6h4l5 4z"/>
                        <path d="M15.5 8.5a5 5 0 010 7M19 5a10 10 0 010 14"/>
                    </svg>
                </button>
                <a href="#" class="topnav__cta" data-magnetic>
                    <span>Let's Talk</span>
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                        <path d="M5 12h14M13 6l6 6-6 6" stroke-linecap="round" stroke-linejoin="round"/>
                    </svg>
                </a>
            </div>
        </header>

        <!-- ========== LEFT RAIL ========== -->
        <aside class="rail rail--left">
            <div class="rail__dots" data-rail>
                <span></span><span></span><span></span>
                <span></span><span></span><span></span>
                <span></span><span></span><span></span>
            </div>
            <a href="#" class="rail__phone" data-rail>+(02)·574·328·301</a>
            <div class="rail__scroll" data-rail>
                <span class="rail__scroll-label">SCROLL DOWN</span>
                <span class="rail__scroll-line"></span>
                <span class="rail__scroll-arrow">&#8595;</span>
            </div>
        </aside>

        <!-- ========== RIGHT RAIL ========== -->
        <aside class="rail rail--right">
            <div class="rail__follow" data-rail>
                <span class="rail__follow-label">FOLLOW ME</span>
                <span class="rail__follow-line"></span>
            </div>
            <div class="rail__social" data-rail>
                <a href="#" class="rail__social-icon" data-social aria-label="Behance"><span>Be</span></a>
                <a href="#" class="rail__social-icon" data-social aria-label="Dribbble">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                        <circle cx="12" cy="12" r="9"/>
                        <path d="M3 12c6 0 13 1 18 7M5 6c4 2 11 8 14 15M20 6c-4 4-12 6-17 4"/>
                    </svg>
                </a>
                <a href="#" class="rail__social-icon" data-social aria-label="Twitter">
                    <svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.9 7.3c0 .2 0 .4 0 .5 0 5.4-4.1 11.6-11.6 11.6-2.3 0-4.5-.7-6.3-1.9.3 0 .6.1 1 .1 1.9 0 3.7-.7 5.1-1.8-1.8 0-3.3-1.2-3.9-2.9.3 0 .5.1.8.1.4 0 .7 0 1.1-.1-1.9-.4-3.4-2.1-3.4-4.2v-.1c.6.3 1.2.5 1.9.5-1.1-.8-1.9-2.2-1.9-3.7 0-.8.2-1.5.6-2.1 2.1 2.5 5.1 4.1 8.6 4.3-.1-.3-.1-.6-.1-.9 0-2.3 1.8-4.1 4.1-4.1 1.2 0 2.3.5 3 1.3.9-.2 1.8-.5 2.6-1-.3 1-1 1.8-1.8 2.3.8-.1 1.6-.3 2.4-.6-.6.8-1.3 1.5-2.1 2.1z"/></svg>
                </a>
                <a href="#" class="rail__social-icon" data-social aria-label="LinkedIn">
                    <svg viewBox="0 0 24 24" fill="currentColor"><path d="M19.5 3h-15C3.7 3 3 3.7 3 4.5v15c0 .8.7 1.5 1.5 1.5h15c.8 0 1.5-.7 1.5-1.5v-15c0-.8-.7-1.5-1.5-1.5zM8.3 18H5.7V9.8h2.6V18zM7 8.6c-.8 0-1.5-.7-1.5-1.5S6.2 5.6 7 5.6s1.5.7 1.5 1.5S7.8 8.6 7 8.6zM18.3 18h-2.6v-4.2c0-1 0-2.3-1.4-2.3s-1.6 1.1-1.6 2.2V18H10.1V9.8h2.5v1.1h.1c.3-.7 1.2-1.4 2.5-1.4 2.7 0 3.2 1.8 3.2 4V18z"/></svg>
                </a>
            </div>
        </aside>

        <!-- ========== HERO CONTENT ========== -->
        <div class="hero" id="hero">
            <a href="#" class="hero__eyebrow" data-magnetic data-eyebrow>
                <span class="hero__eyebrow-text">Currently available for freelance<br>worldwide</span>
                <span class="hero__eyebrow-arrow">&#8599;</span>
            </a>

            <h1 class="hero__title" aria-label="Creative Visual Designer">
                <span class="hero__line" data-title-line>Creative Visual</span>
                <span class="hero__line" data-title-line>Designer</span>
            </h1>

            <a href="#" class="hero__play" data-play data-hover>
                <svg class="hero__play-scribble" viewBox="0 0 80 60" aria-hidden="true">
                    <path d="M10 30 Q 20 10, 40 20 T 70 35" stroke="#c9ff2d" stroke-width="1.8" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
                    <path d="M65 30 L 70 35 L 65 40" stroke="#c9ff2d" stroke-width="1.8" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
                <span class="hero__play-btn">
                    <svg viewBox="0 0 32 32" fill="none">
                        <circle cx="16" cy="16" r="15" stroke="#ffffff" stroke-width="1" fill="none"/>
                        <path d="M13 11l9 5-9 5z" fill="#ffffff"/>
                    </svg>
                    <svg class="hero__play-ring" viewBox="0 0 32 32" fill="none" aria-hidden="true">
                        <circle cx="16" cy="16" r="15.5" stroke="#c9ff2d" stroke-width="0.8" stroke-dasharray="4 3"/>
                    </svg>
                </span>
                <span class="hero__play-label">
                    <span>Work</span>
                    <span>Process</span>
                </span>
            </a>

            <!-- Floating stat chips -->
            <div class="chips" data-chips>
                <span class="chip"><b>12+</b> Years Experience</span>
                <span class="chip"><b>240+</b> Projects Delivered</span>
                <span class="chip"><b>18</b> Awards</span>
            </div>
        </div>

        <!-- ========== MARQUEE ========== -->
        <div class="marquee" id="marquee">
            <div class="marquee__track" id="marquee-track">
                <div class="marquee__group">
                    <span class="marquee__item">&#10036; Website Design &amp; Logo</span>
                    <span class="marquee__item">&#10036; Business Branding</span>
                    <span class="marquee__item">&#10036; Mobile Application Design</span>
                    <span class="marquee__item">&#10036; UI/UX Mobile Design</span>
                    <span class="marquee__item">&#10036; Motion &amp; Interaction</span>
                </div>
                <div class="marquee__group" aria-hidden="true">
                    <span class="marquee__item">&#10036; Website Design &amp; Logo</span>
                    <span class="marquee__item">&#10036; Business Branding</span>
                    <span class="marquee__item">&#10036; Mobile Application Design</span>
                    <span class="marquee__item">&#10036; UI/UX Mobile Design</span>
                    <span class="marquee__item">&#10036; Motion &amp; Interaction</span>
                </div>
            </div>
        </div>
    </section>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
    <script src="banner-creative-reel.js"></script>
</body>
</html>

</body>
</html>
JSbanner-creative-reel.js— GSAP animation timeline
(() => {
    const root = document.getElementById('reel');
    if (!root) return;

    // -------------------------------------------------------------------
    //  SPLIT TITLE INTO CHARS
    // -------------------------------------------------------------------
    const titleLines = root.querySelectorAll('[data-title-line]');
    titleLines.forEach((line) => {
        const text = line.textContent;
        line.textContent = '';
        [...text].forEach((ch) => {
            const span = document.createElement('span');
            span.className = 'hero__char';
            if (ch === ' ') {
                span.classList.add('is-space');
                span.innerHTML = '&nbsp;';
            } else {
                span.textContent = ch;
            }
            line.appendChild(span);
        });
    });

    // -------------------------------------------------------------------
    //  REFERENCES
    // -------------------------------------------------------------------
    const loader = document.getElementById('loader');
    const clapper = document.getElementById('clapper');
    const clapperArm = document.getElementById('clapper-arm');
    const countdown = document.getElementById('countdown');
    const loaderLabel = loader.querySelector('.loader__label');

    const video = document.getElementById('reel-video');
    const muteBtn = document.getElementById('mute-btn');
    const muteOn = muteBtn.querySelector('.mute-on');
    const muteOff = muteBtn.querySelector('.mute-off');

    const cursorDot = document.getElementById('cursor-dot');
    const cursorRing = document.getElementById('cursor-ring');

    const magnetics = root.querySelectorAll('[data-magnetic]');
    const rails = root.querySelectorAll('[data-rail]');
    const socials = root.querySelectorAll('[data-social]');
    const eyebrow = root.querySelector('[data-eyebrow]');
    const titleChars = root.querySelectorAll('.hero__char');
    const playBtn = root.querySelector('[data-play]');
    const playRing = root.querySelector('.hero__play-ring');
    const playScribble = root.querySelector('.hero__play-scribble path');
    const chips = root.querySelector('[data-chips]');
    const chipEls = root.querySelectorAll('.chip');
    const marqueeTrack = document.getElementById('marquee-track');

    // -------------------------------------------------------------------
    //  INITIAL STATES
    // -------------------------------------------------------------------
    gsap.set(magnetics, { y: -20, opacity: 0 });
    gsap.set(rails, { x: (i, el) => el.closest('.rail--left') ? -30 : 30, opacity: 0 });
    gsap.set(socials, { x: 30, opacity: 0 });
    gsap.set(eyebrow, { y: 20, opacity: 0 });
    gsap.set(titleChars, { yPercent: 110, opacity: 0 });
    gsap.set(playBtn, { y: 30, opacity: 0 });
    gsap.set(chips, { y: 30, opacity: 0 });
    gsap.set(chipEls, { scale: 0.8, opacity: 0 });
    gsap.set(video, { scale: 1.15, opacity: 0 });

    // -------------------------------------------------------------------
    //  LOADER TIMELINE — Clapperboard countdown
    // -------------------------------------------------------------------
    const loaderTl = gsap.timeline({ onComplete: playScene });

    // Clapper entrance
    loaderTl.from(clapper, {
        y: -30,
        opacity: 0,
        scale: 0.92,
        duration: 0.6,
        ease: 'power3.out',
    }, 0);

    // Countdown: 3 → 2 → 1 → ACTION
    const counts = ['3', '2', '1', 'ACTION'];
    counts.forEach((val, i) => {
        loaderTl.call(() => {
            countdown.textContent = val;
            gsap.fromTo(countdown,
                { scale: 1.4, opacity: 0 },
                { scale: 1, opacity: 1, duration: 0.35, ease: 'back.out(2)' }
            );
        }, null, 0.7 + i * 0.75);
    });

    // Update label when ACTION
    loaderTl.call(() => {
        loaderLabel.textContent = 'TAKE 01';
    }, null, 0.7 + 3 * 0.75);

    // Clap arm snaps shut with a bounce
    loaderTl.to(clapperArm, {
        rotation: -28,
        duration: 0.5,
        ease: 'power2.in',
    }, '+=0.25');
    loaderTl.to(clapperArm, {
        rotation: 0,
        duration: 0.25,
        ease: 'power4.in',
        onComplete: () => {
            // Sound wave pulse effect
            gsap.fromTo(clapper,
                { scale: 1 },
                { scale: 1.04, duration: 0.1, yoyo: true, repeat: 1, ease: 'sine.inOut' }
            );
        },
    });

    // Exit: clapper rotates + shoots up, countdown shrinks, loader fades
    loaderTl.to(countdown, {
        scale: 3,
        opacity: 0,
        duration: 0.5,
        ease: 'power3.in',
    }, '+=0.15');
    loaderTl.to(clapper, {
        y: -80,
        rotation: -8,
        scale: 0.85,
        opacity: 0,
        duration: 0.7,
        ease: 'power3.in',
    }, '<');
    loaderTl.to(loaderLabel, {
        opacity: 0,
        y: -10,
        duration: 0.3,
    }, '<');
    loaderTl.to(loader, {
        yPercent: -100,
        duration: 0.8,
        ease: 'power4.inOut',
    }, '-=0.2');
    loaderTl.set(loader, { display: 'none' });

    // -------------------------------------------------------------------
    //  MAIN SCENE ENTRANCE
    // -------------------------------------------------------------------
    function playScene() {
        const tl = gsap.timeline({ defaults: { ease: 'power3.out' } });

        // Video fades in + scale settle
        tl.to(video, {
            scale: 1,
            opacity: 1,
            duration: 1.6,
            ease: 'power3.out',
        }, 0);

        // Nav
        tl.to(magnetics, {
            y: 0, opacity: 1,
            duration: 0.6,
            stagger: 0.05,
        }, 0.2);

        // Rails
        tl.to(rails, {
            x: 0, opacity: 1,
            duration: 0.7,
            stagger: 0.08,
        }, 0.4);

        tl.to(socials, {
            x: 0, opacity: 1,
            duration: 0.5,
            stagger: 0.06,
        }, 0.55);

        // Eyebrow
        tl.to(eyebrow, {
            y: 0, opacity: 1,
            duration: 0.7,
        }, 0.8);

        // Title chars
        tl.to(titleChars, {
            yPercent: 0, opacity: 1,
            duration: 1.1,
            stagger: 0.02,
            ease: 'expo.out',
        }, 0.95);

        // Play button
        tl.to(playBtn, {
            y: 0, opacity: 1,
            duration: 0.8,
            ease: 'back.out(1.6)',
        }, 1.5);

        // Scribble draw
        tl.to(playScribble, {
            strokeDashoffset: 0,
            duration: 0.9,
            ease: 'power2.out',
        }, 1.7);

        // Chips container + stagger
        tl.to(chips, { y: 0, opacity: 1, duration: 0.5 }, 1.7);
        tl.to(chipEls, {
            scale: 1, opacity: 1,
            duration: 0.5,
            stagger: 0.1,
            ease: 'back.out(1.7)',
        }, 1.8);

        tl.call(startContinuous, null, 2.1);
        tl.call(enableInteractions, null, 2.1);
    }

    // -------------------------------------------------------------------
    //  CONTINUOUS ANIMATIONS
    // -------------------------------------------------------------------
    let playRingRot = null;
    let marqueeAnim = null;
    let scrollPulse = null;

    function startContinuous() {
        playRingRot = gsap.to(playRing, {
            rotation: 360,
            duration: 8,
            repeat: -1,
            ease: 'none',
            transformOrigin: 'center center',
        });

        const group = marqueeTrack.querySelector('.marquee__group');
        const groupWidth = group.offsetWidth;
        marqueeAnim = gsap.to(marqueeTrack, {
            x: -groupWidth,
            duration: 28,
            ease: 'none',
            repeat: -1,
            modifiers: { x: gsap.utils.unitize(x => parseFloat(x) % groupWidth) },
        });

        const scrollLine = root.querySelector('.rail__scroll-line');
        if (scrollLine) {
            scrollPulse = gsap.fromTo(scrollLine, { scaleY: 0.3 }, {
                scaleY: 1,
                duration: 1.6,
                yoyo: true,
                repeat: -1,
                ease: 'sine.inOut',
                transformOrigin: 'top',
            });
        }
    }

    // -------------------------------------------------------------------
    //  INTERACTIONS
    // -------------------------------------------------------------------
    function enableInteractions() {

        // ---- Custom cursor ----
        let mx = window.innerWidth / 2, my = window.innerHeight / 2;
        let rx = mx, ry = my;

        window.addEventListener('mousemove', (e) => { mx = e.clientX; my = e.clientY; });

        gsap.ticker.add(() => {
            rx += (mx - rx) * 0.18;
            ry += (my - ry) * 0.18;
            gsap.set(cursorDot, { x: mx, y: my });
            gsap.set(cursorRing, { x: rx, y: ry });
        });

        // Expand ring on interactive elements
        const hovers = root.querySelectorAll('a, button, [data-magnetic], [data-hover], .chip, .rail__social-icon, .rail__dots span');
        hovers.forEach((el) => {
            el.addEventListener('mouseenter', () => cursorRing.classList.add('is-hover'));
            el.addEventListener('mouseleave', () => cursorRing.classList.remove('is-hover'));
        });

        // ---- Magnetic ----
        magnetics.forEach((el) => {
            const strength = el.classList.contains('topnav__cta') ? 0.35
                : el.classList.contains('topnav__mute') ? 0.45
                : el.classList.contains('topnav__logo') ? 0.2
                : el.classList.contains('hero__eyebrow') ? 0.18
                : 0.25;
            el.addEventListener('mousemove', (e) => {
                const r = el.getBoundingClientRect();
                const cx = r.left + r.width / 2;
                const cy = r.top + r.height / 2;
                const dx = (e.clientX - cx) * strength;
                const dy = (e.clientY - cy) * strength;
                gsap.to(el, { x: dx, y: dy, duration: 0.4, ease: 'power3.out' });
            });
            el.addEventListener('mouseleave', () => {
                gsap.to(el, { x: 0, y: 0, duration: 0.6, ease: 'elastic.out(1, 0.4)' });
            });
        });

        // ---- Title char proximity + hover ----
        const hero = document.getElementById('hero');
        let tmx = -9999, tmy = -9999;
        hero.addEventListener('mousemove', (e) => {
            tmx = e.clientX; tmy = e.clientY;
        });
        hero.addEventListener('mouseleave', () => {
            tmx = -9999; tmy = -9999;
            titleChars.forEach((c) => gsap.to(c, { y: 0, duration: 0.5, ease: 'power3.out' }));
        });
        gsap.ticker.add(() => {
            if (tmx < 0) return;
            titleChars.forEach((c) => {
                const r = c.getBoundingClientRect();
                if (r.width === 0) return;
                const cx = r.left + r.width / 2;
                const cy = r.top + r.height / 2;
                const dx = tmx - cx, dy = tmy - cy;
                const dist = Math.sqrt(dx * dx + dy * dy);
                if (dist < 160) gsap.set(c, { y: -(1 - dist / 160) * 22 });
                else gsap.set(c, { y: 0 });
            });
        });
        titleChars.forEach((c) => {
            c.addEventListener('mouseenter', () => gsap.to(c, { color: '#c9ff2d', duration: 0.2 }));
            c.addEventListener('mouseleave', () => gsap.to(c, { color: '#ffffff', duration: 0.4 }));
        });

        // ---- Socials spring ----
        socials.forEach((s) => {
            s.addEventListener('mouseenter', () => {
                gsap.to(s, { scale: 1.18, rotation: -8, duration: 0.4, ease: 'back.out(2)' });
            });
            s.addEventListener('mouseleave', () => {
                gsap.to(s, { scale: 1, rotation: 0, duration: 0.5, ease: 'elastic.out(1, 0.4)' });
            });
        });

        // ---- Dots ripple ----
        const dots = root.querySelectorAll('.rail__dots span');
        dots.forEach((dot, i) => {
            dot.addEventListener('mouseenter', () => {
                dots.forEach((d, j) => {
                    gsap.to(d, {
                        scale: 1.8,
                        delay: Math.abs(j - i) * 0.04,
                        duration: 0.25,
                        yoyo: true,
                        repeat: 1,
                        ease: 'power2.inOut',
                    });
                });
            });
        });

        // ---- Play button ----
        playBtn.addEventListener('mouseenter', () => {
            if (playRingRot) playRingRot.timeScale(4);
            gsap.fromTo(playScribble,
                { strokeDashoffset: 120 },
                { strokeDashoffset: 0, duration: 0.6, ease: 'power2.out' }
            );
        });
        playBtn.addEventListener('mouseleave', () => {
            if (playRingRot) playRingRot.timeScale(1);
        });

        // ---- Chips hover tilt ----
        chipEls.forEach((chip) => {
            chip.addEventListener('mouseenter', () => {
                gsap.to(chip, { y: -4, rotation: gsap.utils.random(-3, 3), duration: 0.35, ease: 'back.out(2)' });
            });
            chip.addEventListener('mouseleave', () => {
                gsap.to(chip, { y: 0, rotation: 0, duration: 0.5, ease: 'elastic.out(1, 0.4)' });
            });
        });

        // ---- Marquee pause on hover ----
        const marqueeEl = document.getElementById('marquee');
        marqueeEl.addEventListener('mouseenter', () => {
            if (marqueeAnim) gsap.to(marqueeAnim, { timeScale: 0.2, duration: 0.4 });
        });
        marqueeEl.addEventListener('mouseleave', () => {
            if (marqueeAnim) gsap.to(marqueeAnim, { timeScale: 1, duration: 0.4 });
        });

        // ---- Mute toggle ----
        muteBtn.addEventListener('click', (e) => {
            e.preventDefault();
            video.muted = !video.muted;
            muteOn.style.display  = video.muted ? ''    : 'none';
            muteOff.style.display = video.muted ? 'none' : '';
            if (!video.muted) video.play().catch(() => {});
        });

        // Ensure video plays (some browsers need user-interaction)
        video.play().catch(() => {
            // Fallback: show poster; attempt play on any click
            const tryPlay = () => {
                video.play().catch(() => {});
                window.removeEventListener('click', tryPlay);
            };
            window.addEventListener('click', tryPlay);
        });

        // ---- Video subtle parallax ----
        let vX = 0, vY = 0, vcX = 0, vcY = 0;
        root.addEventListener('mousemove', (e) => {
            const r = root.getBoundingClientRect();
            vX = ((e.clientX - r.left) / r.width - 0.5) * 2;
            vY = ((e.clientY - r.top) / r.height - 0.5) * 2;
        });
        root.addEventListener('mouseleave', () => { vX = 0; vY = 0; });
        gsap.ticker.add(() => {
            vcX += (vX - vcX) * 0.04;
            vcY += (vY - vcY) * 0.04;
            gsap.set(video, { x: -vcX * 15, y: -vcY * 10, scale: 1.05 });
        });
    }
})();

More GSAP landing pages