ToolsWaves
Free GSAP Landing Page Template · ToolsWaves · Real Estate

Free Real Estate Hero Template with Animated Counter Loader

Free real estate landing page template from ToolsWaves featuring a counter loader climbing to $350k, four animated slides covering home loans, investment, mortgage, and premium listings, plus scattered property photos with parallax depth. Free for any commercial or personal use.

Real EstateFinanceSliderCounterParallax

About this ToolsWaves template

This is a free real estate landing page template offered through ToolsWaves. The loader features a rotating badge with a counter climbing dramatically to a six-figure amount — a visual that primes visitors for the financial dimension of the offering before any content appears. Once loaded, four slides cycle through different aspects of the real estate journey with highlighted keywords that pop into focus and scattered property photos arranged with parallax depth. The dark theme reads as premium and trustworthy.

Use this template for residential property platforms, mortgage technology brands, real estate investment firms, or any property-related landing page that needs to balance approachability with financial credibility. The template is free with no licensing restrictions — every banner on ToolsWaves is a complete HTML, CSS, and JavaScript bundle ready to copy. Replace the demo branding with your real estate firm, swap in your actual property photography, and adjust the counter target to match your typical property values.

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 Real Estate Hero Template with Animated Counter Loader</title>
  <meta name="description" content="Free GSAP real estate landing page hero from ToolsWaves" />
  <meta name="generator" content="ToolsWaves - https://toolswaves.in/landing-pages" />

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

  <!-- Twitter -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="Free Real Estate Hero Template with Animated Counter Loader" />
  <meta name="twitter:description" content="Free GSAP real estate landing page hero from ToolsWaves" />
  <meta name="twitter:image" content="https://toolswaves.in/og?title=Free%20Real%20Estate%20Hero%20Template%20with%20Animated%20Counter%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:     #0b0b0d;
    --bg-2:   #151517;
    --ink:    #ffffff;
    --muted:  rgba(255, 255, 255, 0.55);
    --line:   rgba(255, 255, 255, 0.1);
    --yellow: #fcc83d;
    --yellow-deep: #e6b01f;
    --white:  #ffffff;
}

html, body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    background: var(--bg);
    color: var(--ink);
    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(--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.06);
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 999px;
    backdrop-filter: blur(8px);
    opacity: 0.85;
    transition: opacity 0.3s, transform 0.3s;
}
.back-link:hover { opacity: 1; transform: translateY(-2px); }

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

.loader__stage {
    position: relative;
    width: 220px;
    height: 220px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.loader__ring {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    will-change: transform;
}
#loader-ring-g {
    transform-origin: 110px 110px;
}
.loader__core {
    width: 80px; height: 80px;
    border-radius: 50%;
    background: var(--yellow);
    color: var(--bg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2.5rem;
    font-weight: 400;
    box-shadow: 0 0 30px rgba(252, 200, 61, 0.4),
                inset 0 2px 6px rgba(255, 255, 255, 0.3);
    will-change: transform;
    z-index: 2;
}
.loader__core span { display: block; will-change: transform; }

.loader__particles {
    position: absolute;
    inset: 0;
    pointer-events: none;
}
.loader__particles span {
    position: absolute;
    width: 4px; height: 4px;
    background: var(--yellow);
    border-radius: 50%;
    box-shadow: 0 0 8px rgba(252, 200, 61, 0.6);
    will-change: transform, opacity;
}

.loader__meta {
    display: flex;
    gap: 1.25rem;
    align-items: center;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.72rem;
    letter-spacing: 0.3em;
    color: var(--muted);
    text-transform: uppercase;
}
.loader__counter {
    color: var(--yellow);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.05em;
    min-width: 7ch;
    text-align: right;
}

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

/* Corner decorative lines */
.deco {
    position: absolute;
    z-index: 4;
    pointer-events: none;
}
.deco--tl {
    top: 10%;
    right: 42%;
    width: 1px;
    height: 60px;
    background: rgba(255, 255, 255, 0.3);
    transform: rotate(25deg);
}
.deco--tr {
    top: 35%;
    right: 8%;
    width: 1px;
    height: 80px;
    background: rgba(255, 255, 255, 0.3);
    transform: rotate(25deg);
}
.deco--bl {
    bottom: 35%;
    left: 7%;
    width: 60px;
    height: 1px;
    background: rgba(255, 255, 255, 0.3);
    transform: rotate(25deg);
}

/* ---------- Top nav ---------- */
.topnav {
    position: relative;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 2rem;
    padding: 1.5rem 3rem;
}

.topnav__logo {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    text-decoration: none;
    color: var(--ink);
    font-weight: 700;
    font-size: 1.2rem;
    letter-spacing: -0.01em;
    will-change: transform;
}
.topnav__logo-mark { width: 32px; height: 26px; }
.topnav__logo-mark svg { width: 100%; height: 100%; display: block; }

.topnav__links {
    display: flex;
    gap: 2rem;
    margin-left: 1.5rem;
}
.topnav__links a {
    position: relative;
    color: var(--ink);
    text-decoration: none;
    font-weight: 500;
    font-size: 0.95rem;
    padding: 0.3rem 0;
    will-change: transform;
}
.topnav__links a::after {
    content: '';
    position: absolute;
    left: 0; bottom: 0;
    width: 100%; height: 1.5px;
    background: var(--yellow);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.4s 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;
    align-items: center;
    gap: 1.5rem;
}
.topnav__signin {
    color: var(--ink);
    text-decoration: none;
    font-weight: 500;
    font-size: 0.95rem;
    position: relative;
    padding: 0.3rem 0;
    will-change: transform;
}
.topnav__signin::after {
    content: '';
    position: absolute;
    left: 0; bottom: 0;
    width: 100%; height: 1.5px;
    background: var(--ink);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.4s cubic-bezier(0.65, 0, 0.35, 1);
}
.topnav__signin:hover::after { transform: scaleX(1); transform-origin: left; }

.topnav__badge {
    position: relative;
    width: 54px; height: 54px;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    will-change: transform;
}
.topnav__badge-text {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    will-change: transform;
}
.topnav__badge-core {
    width: 28px; height: 28px;
    border-radius: 50%;
    background: var(--yellow);
    color: var(--bg);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.2rem;
    font-weight: 400;
    position: relative;
    z-index: 2;
}

/* ---------- Hero ---------- */
.hero {
    position: relative;
    z-index: 10;
    display: grid;
    grid-template-columns: 1.2fr 1fr;
    gap: 2rem;
    padding: 0 3rem;
    height: calc(100vh - 100px);
    align-items: flex-start;
    padding-top: 2rem;
}

.hero__left {
    position: relative;
}

.hero__title {
    font-family: 'Inter', sans-serif;
    font-weight: 700;
    font-size: clamp(2rem, 5vw, 4.5rem);
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: var(--ink);
    margin-bottom: 3.5rem;
    display: block;
}
.hero__title .hl-yellow {
    display: inline-block;
    position: relative;
    color: var(--ink);
    padding: 0 0.1em;
    will-change: transform;
}
.hero__title .hl-yellow::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    bottom: 0.05em;
    height: 0.35em;
    background: var(--yellow);
    z-index: -1;
    border-radius: 2px;
    transform-origin: left;
}
.hero__title .hl-box {
    display: inline-block;
    background: var(--white);
    color: var(--bg);
    padding: 0 0.2em 0.05em;
    margin: 0 0.05em;
    border-radius: 3px;
    will-change: transform;
}

.hero__lower {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 1.5rem;
    align-items: center;
    margin-bottom: 2rem;
}
.hero__pic {
    position: relative;
    width: 120px;
    aspect-ratio: 4 / 5;
    background: #fff;
    padding: 10px;
    border-radius: 4px;
    overflow: hidden;
    will-change: transform;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.hero__pic img {
    width: 100%; height: 100%;
    object-fit: cover;
    user-select: none;
}
.deco-line {
    position: absolute;
    top: -15px;
    left: -15px;
    width: 30px;
    height: 1px;
    background: rgba(255, 255, 255, 0.5);
    transform: rotate(45deg);
}

.hero__desc {
    font-size: 0.85rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.7);
    font-weight: 400;
}

.cta {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: #fff;
    color: var(--bg);
    text-decoration: none;
    padding: 1rem 2rem;
    border-radius: 6px;
    font-weight: 600;
    font-size: 0.95rem;
    position: relative;
    overflow: hidden;
    will-change: transform;
    transition: background 0.3s;
}
.cta svg { width: 14px; height: 14px; transition: transform 0.3s; }
.cta::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--yellow);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.5s cubic-bezier(0.65, 0, 0.35, 1);
    z-index: 0;
}
.cta span, .cta svg { position: relative; z-index: 2; }
.cta:hover::before { transform: scaleX(1); transform-origin: left; }
.cta:hover svg { transform: translateX(4px); }

/* ---------- Right scattered photos ---------- */
.hero__right {
    position: relative;
    width: 100%;
    height: 100%;
    min-height: 520px;
}
.photo {
    position: absolute;
    overflow: hidden;
    will-change: transform;
    cursor: pointer;
}
.photo img {
    width: 100%; height: 100%;
    object-fit: cover;
    user-select: none;
    transition: transform 0.6s ease;
}
.photo:hover img { transform: scale(1.08); }

.photo--gradient {
    top: 0;
    right: 42%;
    width: 180px;
    aspect-ratio: 9 / 14;
    border-radius: 4px;
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
}
.photo--gradient .photo__bg {
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, #ff6b9d 0%, #c44569 50%, #6c5ce7 100%);
    z-index: 0;
}
.photo--gradient img {
    position: relative;
    mix-blend-mode: screen;
    opacity: 0.85;
    z-index: 1;
}
.photo--gradient .photo__glare {
    position: absolute;
    top: -50%;
    left: -50%;
    width: 200%;
    height: 200%;
    background: linear-gradient(45deg, transparent 40%, rgba(255, 255, 255, 0.2) 50%, transparent 60%);
    pointer-events: none;
    z-index: 2;
}

.photo--phone {
    top: 6%;
    right: 5%;
    width: 210px;
    aspect-ratio: 9 / 13;
    background: #fff;
    padding: 12px;
    border-radius: 4px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.photo__bracket {
    position: absolute;
    width: 60px;
    height: 1px;
    background: rgba(255, 255, 255, 0.5);
    z-index: 3;
}
.photo__bracket--tl { top: -20px; left: -20px; transform: rotate(45deg); }
.photo__bracket--br { bottom: -20px; right: -20px; transform: rotate(45deg); }

.photo--person {
    bottom: 5%;
    right: 3%;
    width: 300px;
    aspect-ratio: 4 / 3.5;
    border-radius: 6px;
    box-shadow: 0 25px 60px rgba(0, 0, 0, 0.5);
}

/* ---------- Slider controls ---------- */
.slider-controls {
    position: absolute;
    bottom: 1.5rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 20;
    display: flex;
    align-items: center;
    gap: 1.5rem;
}
.slide-arrow {
    width: 44px; height: 44px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: var(--ink);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    will-change: transform;
    transition: background 0.3s, color 0.3s;
}
.slide-arrow svg { width: 18px; height: 18px; transition: transform 0.3s; }
.slide-arrow:hover { background: var(--yellow); color: var(--bg); border-color: var(--yellow); }

.slide-progress {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.85rem;
    color: var(--ink);
    letter-spacing: 0.1em;
    font-variant-numeric: tabular-nums;
}
.slide-progress__current { color: var(--yellow); font-weight: 700; }
.slide-progress__total { color: var(--muted); }
.slide-progress__bar {
    display: inline-block;
    position: relative;
    width: 120px;
    height: 1.5px;
    background: rgba(255, 255, 255, 0.15);
}
.slide-progress__bar span {
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 25%;
    background: var(--yellow);
    transform-origin: left;
}

/* ---------- Initial states ---------- */
[data-magnetic], [data-link], [data-photo], [data-pic], [data-controls], [data-desc] {
    opacity: 0;
}

/* ---------- Responsive ---------- */
@media (max-width: 1100px) {
    .hero { grid-template-columns: 1.3fr 1fr; padding: 0 2rem; }
    .hero__title { font-size: clamp(1.8rem, 5vw, 3rem); }
    .topnav__links { display: none; }
    .photo--gradient { width: 140px; right: 52%; }
    .photo--phone { width: 160px; }
    .photo--person { width: 240px; }
}
@media (max-width: 760px) {
    .topnav { padding: 1rem 1.5rem; gap: 1rem; }
    .hero { grid-template-columns: 1fr; padding: 0 1.5rem; height: auto; gap: 2rem; }
    .hero__title { font-size: clamp(1.5rem, 7vw, 2.5rem); margin-bottom: 1.5rem; }
    .hero__right { min-height: 280px; }
    .photo--gradient { width: 110px; top: 0; right: 60%; }
    .photo--phone { width: 120px; top: 0; }
    .photo--person { width: 180px; bottom: 0; }
    .nest { height: auto; min-height: 100vh; padding-bottom: 5rem; }
    .slider-controls { bottom: 1rem; }
    .hero__lower { grid-template-columns: 1fr; gap: 1rem; }
    .hero__pic { width: 100px; }
    .topnav__signin { font-size: 0.85rem; }
}
@media (max-width: 480px) {
    .topnav__badge { display: none; }
    .photo--gradient { display: none; }
    .deco { display: none; }
    .cta { padding: 0.75rem 1.5rem; }
}

  </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 — Nest</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@500;600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="banner-nest.css">
</head>
<body>
    

    <!-- ========== LOADER — Rotating text badge + counter ========== -->
    <div class="loader" id="loader">
        <div class="loader__stage">
            <svg class="loader__ring" viewBox="0 0 220 220" aria-hidden="true">
                <defs>
                    <path id="loaderCircle" d="M 110 110 m -90 0 a 90 90 0 1 1 180 0 a 90 90 0 1 1 -180 0"/>
                </defs>
                <g id="loader-ring-g">
                    <text font-family="Inter, sans-serif" font-weight="700" font-size="14" letter-spacing="6" fill="#fcc83d">
                        <textPath href="#loaderCircle">NEST · LOAN · HOUSE · DREAM · INVEST · NEST · LOAN · HOUSE · DREAM · INVEST · </textPath>
                    </text>
                </g>
            </svg>
            <div class="loader__core" id="loader-core">
                <span>+</span>
            </div>
            <div class="loader__particles" id="loader-particles"></div>
        </div>
        <div class="loader__meta">
            <span class="loader__label" id="loader-label">BUILDING YOUR NEST</span>
            <span class="loader__counter" id="loader-counter">$0</span>
        </div>
    </div>

    <section class="nest" id="nest">
        <!-- Decorative corner lines -->
        <div class="deco deco--tl"></div>
        <div class="deco deco--tr"></div>
        <div class="deco deco--bl"></div>

        <!-- ========== TOP NAV ========== -->
        <header class="topnav">
            <a href="#" class="topnav__logo" data-magnetic>
                <span class="topnav__logo-mark" id="logo-mark">
                    <svg viewBox="0 0 40 32" aria-hidden="true">
                        <path d="M6 20 Q 6 10 14 10 Q 16 4 22 4 Q 30 4 32 12 Q 38 12 38 20 L 38 24 L 6 24 Z" fill="#fcc83d"/>
                        <circle cx="14" cy="16" r="2" fill="#1a1a1a"/>
                        <circle cx="26" cy="14" r="1.8" fill="#1a1a1a"/>
                    </svg>
                </span>
                <span class="topnav__logo-text">Nest</span>
            </a>

            <nav class="topnav__links">
                <a href="#" data-magnetic data-link>Solution</a>
                <a href="#" data-magnetic data-link>Company</a>
                <a href="#" data-magnetic data-link>Books Of Cells</a>
            </nav>

            <div class="topnav__actions">
                <a href="#" class="topnav__signin" data-magnetic>Sign in</a>
                <a href="#" class="topnav__badge" data-magnetic id="nav-badge">
                    <svg class="topnav__badge-text" viewBox="0 0 64 64" aria-hidden="true">
                        <defs>
                            <path id="badgeCirc" d="M 32 32 m -22 0 a 22 22 0 1 1 44 0 a 22 22 0 1 1 -44 0"/>
                        </defs>
                        <text font-family="Inter, sans-serif" font-weight="700" font-size="6" letter-spacing="3.5" fill="#1a1a1a">
                            <textPath href="#badgeCirc">JOIN · NEST · NOW · </textPath>
                        </text>
                    </svg>
                    <span class="topnav__badge-core">+</span>
                </a>
            </div>
        </header>

        <!-- ========== MAIN HERO ========== -->
        <div class="hero" id="hero">

            <!-- Left content -->
            <div class="hero__left">
                <h1 class="hero__title" id="title" aria-label="Slide headline"></h1>

                <div class="hero__lower">
                    <div class="hero__pic" data-pic>
                        <img id="accent-img" src="https://images.unsplash.com/photo-1576019350023-8ea21aa65a0a?auto=format&fit=crop&w=400&q=80"
                             alt="" draggable="false" onerror="this.style.display='none'">
                        <span class="deco-line"></span>
                    </div>
                    <p class="hero__desc" id="desc" data-desc></p>
                </div>

                <a href="#" class="cta" data-magnetic data-cta>
                    <span id="cta-label">Start Owning</span>
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M5 12h14M13 6l6 6-6 6" stroke-linecap="round" stroke-linejoin="round"/>
                    </svg>
                </a>
            </div>

            <!-- Right scattered photos -->
            <div class="hero__right" id="photos">
                <div class="photo photo--gradient" data-photo>
                    <div class="photo__bg" id="pic-1-bg"></div>
                    <img id="pic-1"
                         src="https://images.unsplash.com/photo-1611162616305-c69b3fa7fbe0?auto=format&fit=crop&w=500&q=80"
                         alt="" draggable="false" onerror="this.style.display='none'">
                    <span class="photo__glare"></span>
                </div>
                <div class="photo photo--phone" data-photo>
                    <img id="pic-2"
                         src="https://images.unsplash.com/photo-1513151233558-d860c5398176?auto=format&fit=crop&w=500&q=80"
                         alt="" draggable="false" onerror="this.style.display='none'">
                    <span class="photo__bracket photo__bracket--tl"></span>
                    <span class="photo__bracket photo__bracket--br"></span>
                </div>
                <div class="photo photo--person" data-photo>
                    <img id="pic-3"
                         src="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&w=800&q=80"
                         alt="" draggable="false" onerror="this.style.display='none'">
                </div>
            </div>
        </div>

        <!-- ========== SLIDER CONTROLS ========== -->
        <div class="slider-controls" data-controls>
            <button class="slide-arrow" id="prev" data-magnetic aria-label="Previous">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M15 6l-6 6 6 6" stroke-linecap="round" stroke-linejoin="round"/></svg>
            </button>
            <div class="slide-progress">
                <span class="slide-progress__current" id="slide-current">01</span>
                <span class="slide-progress__bar"><span id="progress-fill"></span></span>
                <span class="slide-progress__total">04</span>
            </div>
            <button class="slide-arrow" id="next" data-magnetic aria-label="Next">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M9 6l6 6-6 6" stroke-linecap="round" stroke-linejoin="round"/></svg>
            </button>
        </div>
    </section>

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

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

    // -------------------------------------------------------------------
    //  SLIDE DATA — 4 unique slides
    // -------------------------------------------------------------------
    const slides = [
        {
            titleHtml: 'Get <span class="hl-yellow">$350k</span> extra<br>to get a house of<br>your <span class="hl-box">Imagination</span> now',
            desc: 'Tailored home loans and refinancing options built for first-time buyers, ambitious upgraders, and seasoned owners looking for their next address.',
            cta: 'Start Owning',
            counterTarget: 350000,
            counterPrefix: '$',
            counterSuffix: 'k',
            pic: 'https://images.unsplash.com/photo-1576019350023-8ea21aa65a0a?auto=format&fit=crop&w=400&q=80',
            pic1: 'https://images.unsplash.com/photo-1611162616305-c69b3fa7fbe0?auto=format&fit=crop&w=500&q=80',
            pic2: 'https://images.unsplash.com/photo-1513151233558-d860c5398176?auto=format&fit=crop&w=500&q=80',
            pic3: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&w=800&q=80',
        },
        {
            titleHtml: 'Earn <span class="hl-yellow">12%</span> returns<br>on every smart<br><span class="hl-box">Investment</span> you make',
            desc: 'A curated portfolio of vetted properties, fractional shares, and REITs designed to outperform inflation while you focus on the life you actually want.',
            cta: 'Start Investing',
            counterTarget: 12,
            counterPrefix: '',
            counterSuffix: '%',
            pic: 'https://images.unsplash.com/photo-1579621970588-a35d0e7ab9b6?auto=format&fit=crop&w=400&q=80',
            pic1: 'https://images.unsplash.com/photo-1590283603385-17ffb3a7f29f?auto=format&fit=crop&w=500&q=80',
            pic2: 'https://images.unsplash.com/photo-1559526324-4b87b5e36e44?auto=format&fit=crop&w=500&q=80',
            pic3: 'https://images.unsplash.com/photo-1600880292089-90a7e086ee0c?auto=format&fit=crop&w=800&q=80',
        },
        {
            titleHtml: 'Save <span class="hl-yellow">$1,200</span> monthly<br>on your long-term<br><span class="hl-box">Mortgage</span> payment',
            desc: 'Intelligent refinancing insights and negotiation support surface the best rates across 200+ lenders, cutting wasted interest and putting cash back in your pocket.',
            cta: 'Refinance Now',
            counterTarget: 1200,
            counterPrefix: '$',
            counterSuffix: '',
            pic: 'https://images.unsplash.com/photo-1563013544-824ae1b704d3?auto=format&fit=crop&w=400&q=80',
            pic1: 'https://images.unsplash.com/photo-1554224155-8d04cb21cd6c?auto=format&fit=crop&w=500&q=80',
            pic2: 'https://images.unsplash.com/photo-1579621970563-ebec7560ff3e?auto=format&fit=crop&w=500&q=80',
            pic3: 'https://images.unsplash.com/photo-1554224155-6726b3ff858f?auto=format&fit=crop&w=800&q=80',
        },
        {
            titleHtml: 'Unlock <span class="hl-yellow">Premium</span><br>access to your live<br><span class="hl-box">Portfolio</span> today',
            desc: 'Real-time dashboards, rental yield forecasts, and concierge support from senior advisors who know the local market inside and out.',
            cta: 'Go Premium',
            counterTarget: 100,
            counterPrefix: '',
            counterSuffix: '+',
            pic: 'https://images.unsplash.com/photo-1556761175-4b46a572b786?auto=format&fit=crop&w=400&q=80',
            pic1: 'https://images.unsplash.com/photo-1556761175-5973dc0f32e7?auto=format&fit=crop&w=500&q=80',
            pic2: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?auto=format&fit=crop&w=500&q=80',
            pic3: 'https://images.unsplash.com/photo-1521791136064-7986c2920216?auto=format&fit=crop&w=800&q=80',
        },
    ];
    let currentSlide = 0;

    // -------------------------------------------------------------------
    //  BUILD LOADER PARTICLES
    // -------------------------------------------------------------------
    const particlesHost = document.getElementById('loader-particles');
    const PARTICLE_COUNT = 12;
    for (let i = 0; i < PARTICLE_COUNT; i++) {
        const p = document.createElement('span');
        const angle = (i / PARTICLE_COUNT) * Math.PI * 2;
        const r = 60;
        p.style.left = (110 + Math.cos(angle) * r) + 'px';
        p.style.top  = (110 + Math.sin(angle) * r) + 'px';
        p.style.transform = 'translate(-50%, -50%)';
        particlesHost.appendChild(p);
    }
    const particles = particlesHost.querySelectorAll('span');

    // -------------------------------------------------------------------
    //  REFERENCES
    // -------------------------------------------------------------------
    const loader = document.getElementById('loader');
    const loaderCounter = document.getElementById('loader-counter');
    const loaderLabel = document.getElementById('loader-label');
    const loaderRingG = document.getElementById('loader-ring-g');
    const loaderCore = document.getElementById('loader-core');

    const magnetics = root.querySelectorAll('[data-magnetic]');
    const titleEl = document.getElementById('title');
    const descEl = document.getElementById('desc');
    const ctaLabel = document.getElementById('cta-label');
    const accentImg = document.getElementById('accent-img');
    const pic1 = document.getElementById('pic-1');
    const pic2 = document.getElementById('pic-2');
    const pic3 = document.getElementById('pic-3');
    const photoEls = root.querySelectorAll('[data-photo]');
    const heroPic = root.querySelector('[data-pic]');
    const controls = root.querySelector('[data-controls]');
    const prevBtn = document.getElementById('prev');
    const nextBtn = document.getElementById('next');
    const slideCurrent = document.getElementById('slide-current');
    const progressFill = document.getElementById('progress-fill');
    const navBadge = document.getElementById('nav-badge');
    const navBadgeText = navBadge.querySelector('.topnav__badge-text');
    const logoMark = document.getElementById('logo-mark');

    // -------------------------------------------------------------------
    //  INITIAL STATES
    // -------------------------------------------------------------------
    gsap.set(magnetics, { y: -15, opacity: 0 });
    gsap.set(photoEls, { y: 30, opacity: 0, scale: 0.9 });
    gsap.set(heroPic, { y: 20, opacity: 0 });
    gsap.set(descEl, { y: 15, opacity: 0 });
    gsap.set(controls, { y: 15, opacity: 0 });

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

    // Text ring rotates continuously
    gsap.to(loaderRingG, {
        rotation: 360,
        duration: 10,
        repeat: -1,
        ease: 'none',
        transformOrigin: '110px 110px',
    });

    // Core pulses
    gsap.to(loaderCore, {
        scale: 1.1,
        duration: 1,
        yoyo: true,
        repeat: -1,
        ease: 'sine.inOut',
    });

    // Particles orbit + twinkle
    particles.forEach((p, i) => {
        gsap.to(p, {
            scale: 1.5,
            opacity: 0.3,
            duration: 0.7,
            yoyo: true,
            repeat: -1,
            ease: 'sine.inOut',
            delay: i * 0.08,
        });
    });

    // Counter + labels
    const p = { v: 0 };
    loaderTl.to(p, {
        v: 350000,
        duration: 2.3,
        ease: 'power2.out',
        onUpdate: () => {
            const val = Math.floor(p.v);
            loaderCounter.textContent = '$' + val.toLocaleString('en-US');
            if (p.v > 100000 && loaderLabel.textContent === 'BUILDING YOUR NEST') loaderLabel.textContent = 'APPROVING BUDGET';
            if (p.v > 250000 && loaderLabel.textContent === 'APPROVING BUDGET') loaderLabel.textContent = 'ALMOST HOME';
            if (p.v > 340000 && loaderLabel.textContent === 'ALMOST HOME') loaderLabel.textContent = 'READY';
        },
    });

    // Exit
    loaderTl.to([loaderCounter, loaderLabel], {
        y: -10, opacity: 0, duration: 0.3, stagger: 0.05,
    }, '+=0.3');
    loaderTl.to('.loader__stage', {
        scale: 1.3, opacity: 0, duration: 0.7, ease: 'power3.in',
    }, '-=0.2');
    loaderTl.to(loader, {
        opacity: 0, duration: 0.4, ease: 'power2.inOut',
    }, '-=0.3');
    loaderTl.set(loader, { display: 'none' });

    // -------------------------------------------------------------------
    //  RENDER SLIDE CONTENT (with transition)
    // -------------------------------------------------------------------
    function renderSlide(idx, direction = 1) {
        const slide = slides[idx];
        const outTl = gsap.timeline();

        // Out: title slides up, desc fades, photos exit
        outTl.to(titleEl, {
            y: -20 * direction,
            opacity: 0,
            duration: 0.35,
            ease: 'power2.in',
        }, 0);
        outTl.to(descEl, {
            y: -15,
            opacity: 0,
            duration: 0.3,
            ease: 'power2.in',
        }, 0);
        outTl.to(photoEls, {
            y: 20 * direction,
            opacity: 0,
            scale: 0.92,
            duration: 0.4,
            stagger: 0.04,
            ease: 'power2.in',
        }, 0);
        outTl.to(ctaLabel, {
            y: -15, opacity: 0, duration: 0.25, ease: 'power2.in',
        }, 0);
        outTl.to(heroPic, {
            y: -15, opacity: 0, duration: 0.3, ease: 'power2.in',
        }, 0);

        outTl.call(() => {
            // Update content
            titleEl.innerHTML = slide.titleHtml;
            descEl.textContent = slide.desc;
            ctaLabel.textContent = slide.cta;
            accentImg.src = slide.pic;
            pic1.src = slide.pic1;
            pic2.src = slide.pic2;
            pic3.src = slide.pic3;

            // Update slider progress
            slideCurrent.textContent = String(idx + 1).padStart(2, '0');
            gsap.to(progressFill, {
                x: `${idx * 25}%`,
                duration: 0.5,
                ease: 'power2.out',
            });

            // Animate in
            gsap.set(titleEl, { y: 30, opacity: 0 });
            gsap.set(descEl, { y: 15, opacity: 0 });
            gsap.set(photoEls, { y: -30 * direction, opacity: 0, scale: 0.9 });
            gsap.set(ctaLabel, { y: 15, opacity: 0 });
            gsap.set(heroPic, { y: 15, opacity: 0 });

            gsap.to(titleEl, {
                y: 0, opacity: 1,
                duration: 0.7,
                ease: 'power3.out',
            });
            // Stagger the highlighted spans for pop
            const highlights = titleEl.querySelectorAll('.hl-yellow, .hl-box');
            gsap.from(highlights, {
                scale: 0.8,
                duration: 0.6,
                delay: 0.3,
                stagger: 0.1,
                ease: 'back.out(2)',
            });
            gsap.to(descEl, {
                y: 0, opacity: 1,
                duration: 0.6,
                delay: 0.25,
                ease: 'power3.out',
            });
            gsap.to(heroPic, {
                y: 0, opacity: 1,
                duration: 0.6,
                delay: 0.2,
                ease: 'power3.out',
            });
            gsap.to(ctaLabel, {
                y: 0, opacity: 1,
                duration: 0.5,
                delay: 0.4,
                ease: 'power3.out',
            });
            gsap.to(photoEls, {
                y: 0, opacity: 1, scale: 1,
                duration: 0.8,
                stagger: 0.08,
                delay: 0.2,
                ease: 'back.out(1.4)',
            });

            // Attach title hover (since innerHTML was swapped)
            attachTitleHover();
        });
    }

    function attachTitleHover() {
        const hlWords = titleEl.querySelectorAll('.hl-yellow, .hl-box');
        hlWords.forEach((w) => {
            w.addEventListener('mouseenter', () => {
                gsap.to(w, { scale: 1.08, duration: 0.3, ease: 'back.out(2)' });
            });
            w.addEventListener('mouseleave', () => {
                gsap.to(w, { scale: 1, duration: 0.4, ease: 'elastic.out(1, 0.4)' });
            });
        });
    }

    // -------------------------------------------------------------------
    //  MAIN SCENE ENTRANCE
    // -------------------------------------------------------------------
    function playScene() {
        // Initial slide content
        titleEl.innerHTML = slides[0].titleHtml;
        descEl.textContent = slides[0].desc;
        ctaLabel.textContent = slides[0].cta;

        const tl = gsap.timeline({ defaults: { ease: 'power3.out' } });

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

        // Title fade in
        gsap.set(titleEl, { y: 30, opacity: 0 });
        tl.to(titleEl, { y: 0, opacity: 1, duration: 0.9 }, 0.3);

        // Highlight pops
        const highlights = titleEl.querySelectorAll('.hl-yellow, .hl-box');
        tl.from(highlights, {
            scale: 0.8,
            duration: 0.7,
            stagger: 0.1,
            ease: 'back.out(2)',
        }, 0.6);

        // Lower content
        tl.to(heroPic, { y: 0, opacity: 1, duration: 0.7 }, 0.9);
        tl.to(descEl, { y: 0, opacity: 1, duration: 0.7 }, 1);

        // Photos scatter in
        tl.to(photoEls, {
            y: 0, opacity: 1, scale: 1,
            duration: 1,
            stagger: 0.12,
            ease: 'back.out(1.4)',
        }, 0.7);

        // Slider controls
        tl.to(controls, { y: 0, opacity: 1, duration: 0.6 }, 1.4);

        tl.call(() => {
            attachTitleHover();
            startContinuous();
            enableInteractions();
        }, null, 1.6);
    }

    // -------------------------------------------------------------------
    //  CONTINUOUS
    // -------------------------------------------------------------------
    function startContinuous() {
        // Nav badge rotates
        gsap.to(navBadgeText, {
            rotation: 360,
            duration: 12,
            repeat: -1,
            ease: 'none',
            transformOrigin: 'center center',
        });
    }

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

        // ---- Magnetic ----
        magnetics.forEach((el) => {
            const strength = el.classList.contains('cta') ? 0.3
                : el.classList.contains('slide-arrow') ? 0.4
                : el.classList.contains('topnav__badge') ? 0.4
                : el.classList.contains('topnav__logo') ? 0.2
                : 0.22;
            el.addEventListener('mousemove', (e) => {
                const r = el.getBoundingClientRect();
                const cx = r.left + r.width / 2;
                const cy = r.top + r.height / 2;
                gsap.to(el, {
                    x: (e.clientX - cx) * strength,
                    y: (e.clientY - cy) * strength,
                    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)' });
            });
        });

        // ---- Logo hover ----
        const logoEl = root.querySelector('.topnav__logo');
        logoEl.addEventListener('mouseenter', () => {
            gsap.to(logoMark.querySelector('svg'), { y: -3, rotation: -5, duration: 0.4, ease: 'back.out(2)' });
        });
        logoEl.addEventListener('mouseleave', () => {
            gsap.to(logoMark.querySelector('svg'), { y: 0, rotation: 0, duration: 0.5, ease: 'elastic.out(1, 0.4)' });
        });

        // ---- Nav badge hover: speed up + core pop ----
        navBadge.addEventListener('mouseenter', () => {
            gsap.getTweensOf(navBadgeText).forEach(t => gsap.to(t, { timeScale: 4, duration: 0.3 }));
            gsap.to(navBadge.querySelector('.topnav__badge-core'), {
                scale: 1.2, rotation: 90,
                duration: 0.3, ease: 'back.out(2)',
            });
        });
        navBadge.addEventListener('mouseleave', () => {
            gsap.getTweensOf(navBadgeText).forEach(t => gsap.to(t, { timeScale: 1, duration: 0.4 }));
            gsap.to(navBadge.querySelector('.topnav__badge-core'), {
                scale: 1, rotation: 0,
                duration: 0.5, ease: 'elastic.out(1, 0.4)',
            });
        });

        // ---- Photo hover ----
        photoEls.forEach((photo) => {
            photo.addEventListener('mouseenter', () => {
                gsap.to(photo, {
                    scale: 1.04,
                    y: -8,
                    zIndex: 10,
                    duration: 0.4,
                    ease: 'back.out(2)',
                });
            });
            photo.addEventListener('mouseleave', () => {
                gsap.to(photo, {
                    scale: 1, y: 0, zIndex: 1,
                    duration: 0.5,
                    ease: 'elastic.out(1, 0.4)',
                });
            });
        });

        // ---- Slider arrows ----
        const advance = (dir) => {
            const next = (currentSlide + dir + slides.length) % slides.length;
            currentSlide = next;
            renderSlide(next, dir);
        };
        nextBtn.addEventListener('click', () => advance(1));
        prevBtn.addEventListener('click', () => advance(-1));

        // Keyboard
        window.addEventListener('keydown', (e) => {
            if (e.key === 'ArrowLeft') advance(-1);
            if (e.key === 'ArrowRight') advance(1);
        });

        // Auto advance
        let autoInterval = setInterval(() => advance(1), 7000);
        controls.addEventListener('mouseenter', () => clearInterval(autoInterval));
        controls.addEventListener('mouseleave', () => {
            autoInterval = setInterval(() => advance(1), 7000);
        });

        // ---- CTA click particles ----
        const cta = root.querySelector('[data-cta]');
        cta.addEventListener('click', (e) => {
            e.preventDefault();
            const rect = cta.getBoundingClientRect();
            const cx = rect.left + rect.width / 2;
            const cy = rect.top + rect.height / 2;
            for (let i = 0; i < 8; i++) {
                const s = document.createElement('span');
                s.style.cssText = `position:fixed;left:${cx}px;top:${cy}px;width:6px;height:6px;background:#fcc83d;border-radius:50%;pointer-events:none;z-index:200;transform:translate(-50%,-50%);box-shadow:0 0 8px #fcc83d;`;
                document.body.appendChild(s);
                const a = (i / 8) * Math.PI * 2;
                gsap.fromTo(s,
                    { x: 0, y: 0, opacity: 1 },
                    {
                        x: Math.cos(a) * 80,
                        y: Math.sin(a) * 80,
                        opacity: 0,
                        scale: 0.3,
                        duration: 0.7,
                        ease: 'power2.out',
                        onComplete: () => s.remove(),
                    }
                );
            }
        });

        // ---- Photos parallax ----
        let mx = 0, my = 0, cmx = 0, cmy = 0;
        root.addEventListener('mousemove', (e) => {
            const r = root.getBoundingClientRect();
            mx = ((e.clientX - r.left) / r.width - 0.5) * 2;
            my = ((e.clientY - r.top) / r.height - 0.5) * 2;
        });
        root.addEventListener('mouseleave', () => { mx = 0; my = 0; });

        gsap.ticker.add(() => {
            cmx += (mx - cmx) * 0.05;
            cmy += (my - cmy) * 0.05;
            photoEls.forEach((photo, i) => {
                if (photo.matches(':hover')) return;
                const depth = 0.3 + i * 0.15;
                gsap.set(photo, {
                    x: cmx * 20 * depth,
                    y: cmy * 15 * depth,
                });
            });
        });
    }
})();

More GSAP landing pages