Nike UHD Athletic Brand Landing Page
A premium dark-themed athletic brand landing page inspired by Nike — featuring animated gradient mesh backgrounds, glassmorphism product cards, CSS-only shoe illustrations, scroll-pinned innovation reveals, animated stat counters, and a phone app mockup with Nike Run Club UI and parallax tilt.
Open in Lab
MCP
gsap scrolltrigger lenis css vanilla-js canvas
Targets: JS HTML
Code
/* ═══════════════════════════════════════════════════════════════════════
Nike UHD Athletic Brand Landing Page — Styles
═══════════════════════════════════════════════════════════════════════ */
:root {
--nk-black: #000000;
--nk-dark: #111111;
--nk-surface: #1a1a1a;
--nk-border: rgba(255, 255, 255, 0.08);
--nk-volt: #cdff00;
--nk-volt-glow: rgba(205, 255, 0, 0.25);
--nk-volt-subtle: rgba(205, 255, 0, 0.08);
--nk-orange: #ff6b35;
--nk-text: #ffffff;
--nk-muted: #999999;
--nk-dim: #555555;
--glass-bg: rgba(255, 255, 255, 0.04);
--glass-border: rgba(255, 255, 255, 0.08);
--glass-hover: rgba(255, 255, 255, 0.07);
--radius: 16px;
--radius-lg: 24px;
--font: "Helvetica Neue", Helvetica, Arial, -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font);
background: var(--nk-black);
color: var(--nk-text);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
/* ── Canvas background ── */
#mesh-bg {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
}
main {
position: relative;
z-index: 1;
}
/* ── Shared ── */
section {
position: relative;
padding: 120px 1.5rem;
}
.section-container {
max-width: 1100px;
margin: 0 auto;
}
.section-header {
margin-bottom: 3.5rem;
}
.section-title {
font-size: clamp(2.5rem, 7vw, 4.5rem);
font-weight: 900;
line-height: 1.0;
letter-spacing: -0.04em;
text-transform: uppercase;
margin-bottom: 1rem;
}
.text-muted {
color: var(--nk-muted);
font-size: 1.05rem;
line-height: 1.6;
}
.text-sm {
font-size: 0.875rem;
}
.tag {
display: inline-block;
padding: 6px 16px;
background: var(--nk-volt);
color: #000;
border-radius: 100px;
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 1.25rem;
}
.reveal {
opacity: 0;
transform: translateY(30px);
}
/* ═══════════════════════════════════════════════════════════════════════
HERO
═══════════════════════════════════════════════════════════════════════ */
.hero {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 80px 1.5rem 60px;
gap: 2rem;
}
.hero-content {
position: relative;
z-index: 2;
}
.hero-swoosh {
margin-bottom: 1rem;
opacity: 0;
}
.hero-swoosh svg {
max-width: 100%;
height: auto;
}
.hero-title {
font-size: clamp(5rem, 18vw, 14rem);
font-weight: 900;
letter-spacing: -0.05em;
line-height: 0.85;
text-transform: uppercase;
background: linear-gradient(180deg, #fff 20%, var(--nk-volt) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
opacity: 0;
}
.hero-subtitle {
font-size: clamp(1rem, 2.5vw, 1.35rem);
color: var(--nk-muted);
font-weight: 400;
margin-top: 1.5rem;
letter-spacing: 0.02em;
opacity: 0;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
.hero-ctas {
display: flex;
gap: 1rem;
justify-content: center;
margin-top: 2.5rem;
opacity: 0;
}
/* ── Buttons ── */
.btn-primary {
display: inline-block;
padding: 16px 40px;
background: var(--nk-text);
color: var(--nk-black);
border-radius: 30px;
font-family: var(--font);
font-weight: 800;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 0.08em;
text-decoration: none;
transition: all 0.3s ease;
border: none;
cursor: pointer;
position: relative;
z-index: 2;
}
.btn-primary:hover {
transform: scale(1.05);
box-shadow: 0 10px 30px rgba(255, 255, 255, 0.15);
}
.btn-volt {
background: var(--nk-volt);
color: var(--nk-black);
box-shadow: 0 0 30px rgba(205, 255, 0, 0.15);
}
.btn-volt:hover {
box-shadow: 0 0 50px rgba(205, 255, 0, 0.25);
}
.btn-secondary {
display: inline-block;
padding: 16px 40px;
background: transparent;
color: var(--nk-text);
border: 2px solid rgba(255, 255, 255, 0.2);
border-radius: 30px;
font-family: var(--font);
font-weight: 800;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 0.08em;
text-decoration: none;
transition: all 0.3s ease;
cursor: pointer;
}
.btn-secondary:hover {
border-color: var(--nk-volt);
color: var(--nk-volt);
box-shadow: 0 0 20px rgba(205, 255, 0, 0.1);
}
/* ── Scroll indicator ── */
.scroll-indicator {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
color: var(--nk-dim);
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.3em;
font-weight: 700;
opacity: 0;
}
.scroll-line {
width: 1px;
height: 50px;
background: linear-gradient(to bottom, var(--nk-volt), transparent);
}
/* ═══════════════════════════════════════════════════════════════════════
PRODUCTS
═══════════════════════════════════════════════════════════════════════ */
.products-section {
padding: 100px 1.5rem 120px;
}
.products-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.25rem;
}
.product-card {
background: var(--glass-bg);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid var(--glass-border);
border-radius: var(--radius-lg);
padding: 28px 24px;
position: relative;
opacity: 0;
transform: translateY(30px);
transition: border-color 0.3s ease, background 0.3s ease, transform 0.3s ease;
}
.product-card:hover {
background: var(--glass-hover);
border-color: rgba(255, 255, 255, 0.15);
transform: translateY(-4px);
}
.product-card-featured {
border-color: rgba(205, 255, 0, 0.25);
}
.product-card-featured:hover {
border-color: rgba(205, 255, 0, 0.45);
box-shadow: 0 0 40px rgba(205, 255, 0, 0.08);
}
.shoe-visual {
width: 100%;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1.25rem;
opacity: 0.85;
}
.shoe-svg {
width: 100%;
max-width: 180px;
height: auto;
}
.product-name {
font-size: 1.15rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.02em;
margin-bottom: 0.25rem;
}
.product-category {
font-size: 0.8rem;
color: var(--nk-muted);
margin-bottom: 1.25rem;
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 500;
}
.product-meta {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.product-price {
font-size: 1.1rem;
font-weight: 800;
color: var(--nk-text);
}
.shop-tag {
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--nk-volt);
padding: 4px 12px;
border: 1px solid rgba(205, 255, 0, 0.2);
border-radius: 100px;
transition: background 0.3s ease, border-color 0.3s ease;
cursor: pointer;
}
.shop-tag:hover {
background: rgba(205, 255, 0, 0.1);
border-color: rgba(205, 255, 0, 0.4);
}
.featured-badge {
position: absolute;
top: 16px;
right: 16px;
padding: 4px 10px;
background: var(--nk-volt);
color: #000;
border-radius: 100px;
font-size: 10px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.06em;
}
/* ═══════════════════════════════════════════════════════════════════════
INNOVATION (pinned)
═══════════════════════════════════════════════════════════════════════ */
.how-track {
position: relative;
height: 300vh;
padding: 0;
}
.how-sticky {
position: sticky;
top: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8%;
gap: 4rem;
}
.how-content {
max-width: 400px;
position: relative;
flex-shrink: 0;
}
/* Step dots */
.step-dots {
display: flex;
gap: 0.5rem;
margin-bottom: 2rem;
}
.step-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.12);
transition: background 0.4s ease, transform 0.4s ease;
}
.step-dot.active {
background: var(--nk-volt);
transform: scale(1.3);
box-shadow: 0 0 10px var(--nk-volt-glow);
}
/* Step cards */
.step-card {
position: absolute;
top: 0;
left: 0;
right: 0;
opacity: 0;
transform: translateY(20px);
pointer-events: none;
transition: opacity 0.5s ease, transform 0.5s ease;
}
.step-card.active {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
position: relative;
}
.step-heading {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: 900;
letter-spacing: -0.03em;
margin-bottom: 1rem;
line-height: 1.0;
text-transform: uppercase;
}
.step-desc {
font-size: 0.95rem;
color: var(--nk-muted);
line-height: 1.7;
}
/* How visuals */
.how-visuals {
position: relative;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
min-height: 400px;
}
.how-visual {
position: absolute;
opacity: 0;
transform: translateY(20px) scale(0.95);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.how-visual.active {
opacity: 1;
transform: translateY(0) scale(1);
}
.innovation-visual {
background: var(--glass-bg);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid var(--glass-border);
border-radius: 28px;
padding: 30px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
}
.react-bubble {
animation: reactFloat 4s ease-in-out infinite alternate;
}
.react-bubble:nth-child(2n) {
animation-delay: -1s;
animation-duration: 3.5s;
}
.react-bubble:nth-child(3n) {
animation-delay: -2s;
animation-duration: 4.5s;
}
@keyframes reactFloat {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(2px, -4px);
}
}
/* ═══════════════════════════════════════════════════════════════════════
STATS
═══════════════════════════════════════════════════════════════════════ */
.stats-section {
padding: 100px 1.5rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
text-align: center;
}
.stat-item {
padding: 3rem 1.5rem;
}
.stat-number {
font-size: clamp(3rem, 8vw, 5rem);
font-weight: 900;
letter-spacing: -0.03em;
line-height: 1;
background: linear-gradient(180deg, #fff 0%, var(--nk-volt) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 0.5rem;
}
.stat-label {
font-size: 1rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 0.75rem;
}
.stat-desc {
font-size: 0.85rem;
color: var(--nk-dim);
max-width: 200px;
margin: 0 auto;
line-height: 1.5;
}
/* ═══════════════════════════════════════════════════════════════════════
ATHLETE SPOTLIGHT
═══════════════════════════════════════════════════════════════════════ */
.athlete-section {
padding: 100px 1.5rem;
}
.glass-card {
background: var(--glass-bg);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border: 1px solid var(--glass-border);
border-radius: 40px;
padding: 60px;
transition: border-color 0.4s ease;
}
.glass-card:hover {
border-color: rgba(255, 255, 255, 0.14);
}
.athlete-layout {
display: flex;
gap: 4rem;
align-items: center;
}
.athlete-figure {
flex-shrink: 0;
opacity: 0.9;
}
.athlete-figure svg {
display: block;
}
.athlete-content {
flex: 1;
}
.athlete-features {
display: flex;
flex-direction: column;
gap: 1.5rem;
margin-bottom: 2.5rem;
}
.athlete-feature {
display: flex;
align-items: flex-start;
gap: 1.25rem;
padding: 1.25rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: var(--radius);
transition: background 0.3s ease, transform 0.3s ease;
opacity: 0;
transform: translateX(-20px);
}
.athlete-feature:hover {
background: rgba(255, 255, 255, 0.06);
transform: translateX(4px);
}
.af-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: var(--nk-volt-subtle);
border-radius: 10px;
flex-shrink: 0;
}
.af-title {
font-size: 1rem;
font-weight: 700;
margin-bottom: 0.35rem;
}
.athlete-quote {
font-size: clamp(1.1rem, 2.5vw, 1.4rem);
font-style: italic;
line-height: 1.6;
color: rgba(255, 255, 255, 0.75);
border-left: 3px solid var(--nk-volt);
padding-left: 1.5rem;
margin-bottom: 1.5rem;
}
.athlete-info {
display: flex;
align-items: center;
gap: 1rem;
}
.athlete-name {
font-size: 1.15rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.athlete-sport {
font-size: 0.85rem;
color: var(--nk-volt);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
}
/* ═══════════════════════════════════════════════════════════════════════
APP SHOWCASE — Nike Run Club
═══════════════════════════════════════════════════════════════════════ */
.app-section {
padding: 120px 1.5rem;
}
.app-grid {
display: flex;
align-items: center;
gap: 5rem;
}
.app-text {
flex: 1;
}
.app-badges {
display: flex;
gap: 0.75rem;
margin-top: 2.5rem;
flex-wrap: wrap;
}
.store-badge {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 18px;
background: var(--nk-text);
color: var(--nk-black);
border-radius: 10px;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.store-badge:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(255, 255, 255, 0.15);
}
.store-text {
display: flex;
flex-direction: column;
}
.store-small {
font-size: 9px;
font-weight: 500;
opacity: 0.6;
line-height: 1;
}
.store-name {
font-size: 14px;
font-weight: 700;
line-height: 1.2;
}
/* ── Phone mockup ── */
.app-device {
position: relative;
perspective: 1000px;
}
.device-phone {
position: relative;
width: 280px;
aspect-ratio: 9 / 19;
background: var(--nk-surface);
border: 3px solid rgba(255, 255, 255, 0.1);
border-radius: 36px;
overflow: hidden;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.04), 0 30px 80px rgba(0, 0, 0, 0.6);
transform-style: preserve-3d;
}
.phone-notch {
position: absolute;
top: 8px;
left: 50%;
transform: translateX(-50%);
width: 80px;
height: 24px;
background: var(--nk-black);
border-radius: 12px;
z-index: 3;
}
.phone-screen {
width: 100%;
height: 100%;
padding: 0;
background: var(--nk-dark);
}
/* NRC UI */
.nrc-ui {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.nrc-status {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 20px 6px;
z-index: 2;
}
.nrc-time-display {
font-size: 12px;
font-weight: 600;
color: var(--nk-text);
}
.nrc-status-icons {
display: flex;
gap: 3px;
align-items: flex-end;
}
.status-bar-icon {
width: 3px;
height: 10px;
background: var(--nk-text);
border-radius: 1px;
opacity: 0.6;
}
.status-bar-icon.short {
height: 6px;
}
/* NRC Map */
.nrc-map {
flex: 1;
position: relative;
background: linear-gradient(180deg, #0a1a0a 0%, #0d1f0a 50%, #111 100%);
overflow: hidden;
min-height: 0;
}
.nrc-route-svg {
width: 100%;
height: 100%;
display: block;
}
.nrc-pulse {
animation: nrcPulse 2s ease-in-out infinite;
}
@keyframes nrcPulse {
0%,
100% {
r: 10;
opacity: 0.4;
}
50% {
r: 14;
opacity: 0.15;
}
}
/* NRC Stats */
.nrc-stats {
display: flex;
background: var(--nk-surface);
border-top: 1px solid rgba(205, 255, 0, 0.1);
padding: 14px 10px;
gap: 4px;
}
.nrc-stat {
flex: 1;
text-align: center;
display: flex;
flex-direction: column;
gap: 3px;
}
.nrc-stat-value {
font-size: 18px;
font-weight: 800;
color: var(--nk-text);
letter-spacing: -0.02em;
}
.nrc-stat-main .nrc-stat-value {
font-size: 26px;
color: var(--nk-volt);
}
.nrc-stat-label {
font-size: 8px;
color: var(--nk-muted);
text-transform: uppercase;
letter-spacing: 0.06em;
font-weight: 600;
}
/* NRC Controls */
.nrc-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
padding: 14px 16px 20px;
background: var(--nk-surface);
}
.nrc-btn-pause {
width: 56px;
height: 56px;
border-radius: 50%;
background: var(--nk-volt);
color: var(--nk-black);
border: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 0 20px rgba(205, 255, 0, 0.3);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.nrc-btn-pause:hover {
transform: scale(1.08);
box-shadow: 0 0 30px rgba(205, 255, 0, 0.45);
}
.nrc-btn-secondary {
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
color: var(--nk-muted);
border: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background 0.2s ease;
}
.nrc-btn-secondary:hover {
background: rgba(255, 255, 255, 0.14);
}
.device-glow {
position: absolute;
inset: -60px;
border-radius: 50%;
background: radial-gradient(circle, rgba(205, 255, 0, 0.1) 0%, transparent 70%);
pointer-events: none;
animation: glowPulse 6s ease-in-out infinite;
}
@keyframes glowPulse {
0%,
100% {
opacity: 0.5;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.08);
}
}
/* ═══════════════════════════════════════════════════════════════════════
CTA
═══════════════════════════════════════════════════════════════════════ */
.cta-section {
min-height: 80vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
overflow: hidden;
position: relative;
}
.cta-title {
position: relative;
z-index: 2;
}
.cta-subtitle {
position: relative;
z-index: 2;
max-width: 440px;
margin-bottom: 2.5rem;
}
.cta-rings {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.ring {
position: absolute;
border-radius: 50%;
border: 1px solid rgba(205, 255, 0, 0.06);
}
.ring-1 {
width: 300px;
height: 300px;
}
.ring-2 {
width: 500px;
height: 500px;
}
.ring-3 {
width: 700px;
height: 700px;
}
.cta-orbs {
position: absolute;
inset: 0;
pointer-events: none;
}
.cta-orb {
position: absolute;
border-radius: 50%;
filter: blur(100px);
}
.cta-orb-1 {
width: 400px;
height: 400px;
background: rgba(205, 255, 0, 0.08);
bottom: -100px;
left: -80px;
}
.cta-orb-2 {
width: 350px;
height: 350px;
background: rgba(205, 255, 0, 0.05);
top: -60px;
right: -80px;
}
/* ═══════════════════════════════════════════════════════════════════════
REDUCED MOTION
═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
.reveal {
opacity: 1;
transform: none;
}
.product-card {
opacity: 1;
transform: none;
}
.athlete-feature {
opacity: 1;
transform: none;
}
.step-card {
transition: none;
}
.how-visual {
transition: none;
}
.step-dot {
transition: none;
}
.react-bubble {
animation: none;
}
.nrc-pulse {
animation: none;
}
.device-glow {
animation: none;
opacity: 0.5;
}
.hero-title,
.hero-subtitle,
.hero-ctas,
.hero-swoosh,
.scroll-indicator {
opacity: 1;
}
}
/* ═══════════════════════════════════════════════════════════════════════
RESPONSIVE
═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 1024px) {
.products-grid {
grid-template-columns: repeat(2, 1fr);
}
.app-grid {
flex-direction: column;
text-align: center;
gap: 3rem;
}
.app-badges {
justify-content: center;
}
.athlete-layout {
flex-direction: column;
text-align: center;
gap: 2.5rem;
}
.athlete-quote {
border-left: none;
padding-left: 0;
border-top: 3px solid var(--nk-volt);
padding-top: 1.5rem;
}
.athlete-info {
justify-content: center;
}
}
@media (max-width: 768px) {
section {
padding: 80px 1.25rem;
}
.products-grid {
grid-template-columns: 1fr;
max-width: 400px;
margin: 0 auto;
}
.stats-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.stat-item {
padding: 2rem 1rem;
}
.how-sticky {
flex-direction: column;
justify-content: center;
padding: 0 1.5rem;
gap: 2rem;
}
.how-content {
max-width: 100%;
}
.how-visuals {
display: none;
}
.glass-card {
padding: 32px;
border-radius: 24px;
}
.device-phone {
width: 240px;
}
.hero-ctas {
flex-direction: column;
align-items: center;
gap: 0.75rem;
}
.athlete-feature {
flex-direction: column;
gap: 0.75rem;
}
.athlete-figure svg {
width: 100px;
height: auto;
}
}
@media (max-width: 480px) {
.hero-title {
font-size: clamp(3.5rem, 18vw, 6rem);
}
.stat-number {
font-size: 3rem;
}
.glass-card {
padding: 24px;
border-radius: 20px;
}
.device-phone {
width: 200px;
}
.section-title {
font-size: clamp(2rem, 10vw, 3rem);
}
.step-heading {
font-size: clamp(1.8rem, 8vw, 2.5rem);
}
.nrc-stat-value {
font-size: 15px;
}
.nrc-stat-main .nrc-stat-value {
font-size: 22px;
}
}/* ═══════════════════════════════════════════════════════════════════════
Nike UHD Athletic Brand Landing Page — Script
═══════════════════════════════════════════════════════════════════════ */
// ── Gradient Mesh Background (Canvas 2D) ───────────────────────────
(function () {
"use strict";
const canvas = document.getElementById("mesh-bg");
const ctx = canvas.getContext("2d");
const DPR = window.devicePixelRatio || 1;
let W, H;
function resize() {
W = window.innerWidth;
H = window.innerHeight;
canvas.width = W * DPR;
canvas.height = H * DPR;
canvas.style.width = W + "px";
canvas.style.height = H + "px";
ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
}
resize();
window.addEventListener("resize", () => {
resize();
initOrbs();
});
const ORB_CONFIGS = [
{ color: "rgba(205,255,0,0.22)", fx: 0.25, fy: 0.2, ax: 0.3, ay: 0.25, px: 0.0, py: 0.0 },
{ color: "rgba(205,255,0,0.10)", fx: 0.18, fy: 0.35, ax: 0.35, ay: 0.3, px: 1.2, py: 0.8 },
{ color: "rgba(255,107,53,0.15)", fx: 0.4, fy: 0.15, ax: 0.25, ay: 0.35, px: 2.5, py: 1.5 },
{ color: "rgba(205,255,0,0.08)", fx: 0.15, fy: 0.45, ax: 0.4, ay: 0.2, px: 0.7, py: 3.1 },
{ color: "rgba(255,107,53,0.08)", fx: 0.3, fy: 0.25, ax: 0.2, ay: 0.3, px: 3.5, py: 2.0 },
];
let orbs = [];
function initOrbs() {
orbs = ORB_CONFIGS.map((cfg, i) => ({
...cfg,
cx: W * (0.15 + (i / ORB_CONFIGS.length) * 0.7),
cy: H * (0.2 + (i % 3) * 0.3),
r: Math.min(W, H) * (0.3 + Math.random() * 0.25),
}));
}
initOrbs();
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let t = 0;
function draw() {
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, W, H);
ctx.globalCompositeOperation = "screen";
orbs.forEach((orb) => {
const speed = reducedMotion ? 0 : 1;
const x = orb.cx + Math.sin(t * orb.fx + orb.px) * orb.ax * W * speed;
const y = orb.cy + Math.cos(t * orb.fy + orb.py) * orb.ay * H * speed;
const g = ctx.createRadialGradient(x, y, 0, x, y, orb.r);
g.addColorStop(0, orb.color);
g.addColorStop(0.5, orb.color.replace(/[\d.]+\)$/, "0.06)"));
g.addColorStop(1, "transparent");
ctx.fillStyle = g;
ctx.fillRect(0, 0, W, H);
});
ctx.globalCompositeOperation = "source-over";
// Vignette
const vignette = ctx.createRadialGradient(W / 2, H / 2, H * 0.15, W / 2, H / 2, H * 0.85);
vignette.addColorStop(0, "transparent");
vignette.addColorStop(1, "rgba(0,0,0,0.6)");
ctx.fillStyle = vignette;
ctx.fillRect(0, 0, W, H);
t += 0.003;
requestAnimationFrame(draw);
}
draw();
})();
// ── Lenis Smooth Scroll ────────────────────────────────────────────
const lenis = new Lenis({
duration: 1.2,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
smoothWheel: true,
autoRaf: false,
});
gsap.registerPlugin(ScrollTrigger);
lenis.on("scroll", ScrollTrigger.update);
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
gsap.ticker.lagSmoothing(0);
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const dur = (d) => (reduced ? 0 : d);
// ═══════════════════════════════════════════════════════════════════════
// HERO ENTRANCE
// ═══════════════════════════════════════════════════════════════════════
const heroTl = gsap.timeline({ defaults: { ease: "power4.out" }, delay: 0.4 });
heroTl
.to(".hero-swoosh", {
opacity: 1,
duration: dur(1),
})
.to(
".hero-title",
{
opacity: 1,
duration: dur(2),
},
"-=0.6"
)
.to(
".hero-subtitle",
{
opacity: 1,
y: -10,
duration: dur(1.2),
},
"-=1.5"
)
.to(
".hero-ctas",
{
opacity: 1,
y: -10,
duration: dur(1),
},
"-=0.8"
)
.to(
".scroll-indicator",
{
opacity: 1,
duration: dur(0.8),
},
"-=0.4"
);
// ── Hero parallax on scroll ──
gsap.to(".hero-content", {
scale: 1.15,
opacity: 0,
filter: "blur(12px)",
scrollTrigger: {
trigger: ".hero",
start: "top top",
end: "70% top",
scrub: true,
},
});
gsap.to(".scroll-indicator", {
opacity: 0,
scrollTrigger: {
trigger: ".hero",
start: "10% top",
end: "30% top",
scrub: true,
},
});
// ═══════════════════════════════════════════════════════════════════════
// GENERIC REVEAL (elements with .reveal class)
// ═══════════════════════════════════════════════════════════════════════
gsap.utils.toArray(".reveal").forEach((el) => {
gsap.fromTo(
el,
{ opacity: 0, y: reduced ? 0 : 40 },
{
opacity: 1,
y: 0,
duration: dur(1),
ease: "power3.out",
scrollTrigger: {
trigger: el,
start: "top 85%",
toggleActions: "play none none none",
},
}
);
});
// ═══════════════════════════════════════════════════════════════════════
// PRODUCT CARDS — stagger reveal
// ═══════════════════════════════════════════════════════════════════════
const productCards = document.querySelectorAll(".product-card");
if (productCards.length) {
gsap.fromTo(
productCards,
{ opacity: 0, y: reduced ? 0 : 30, scale: reduced ? 1 : 0.96 },
{
opacity: 1,
y: 0,
scale: 1,
duration: dur(0.7),
stagger: 0.1,
ease: "power3.out",
scrollTrigger: {
trigger: ".products-grid",
start: "top 80%",
toggleActions: "play none none none",
},
}
);
}
// ═══════════════════════════════════════════════════════════════════════
// INNOVATION — pinned scroll with step swap
// ═══════════════════════════════════════════════════════════════════════
const stepCards = document.querySelectorAll(".step-card");
const howVisuals = document.querySelectorAll(".how-visual");
const stepDots = document.querySelectorAll(".step-dot");
let activeStep = 0;
function setActiveStep(index) {
if (index === activeStep) return;
stepCards.forEach((c) => c.classList.remove("active"));
stepCards[index].classList.add("active");
howVisuals.forEach((v) => v.classList.remove("active"));
howVisuals[index].classList.add("active");
stepDots.forEach((d) => d.classList.remove("active"));
stepDots[index].classList.add("active");
activeStep = index;
}
ScrollTrigger.create({
trigger: ".how-track",
start: "top top",
end: "bottom bottom",
scrub: 0,
onUpdate: (self) => {
const p = self.progress;
if (p < 0.33) setActiveStep(0);
else if (p < 0.66) setActiveStep(1);
else setActiveStep(2);
},
});
// ═══════════════════════════════════════════════════════════════════════
// STATS — count-up animation
// ═══════════════════════════════════════════════════════════════════════
document.querySelectorAll(".stat-number").forEach((el) => {
const target = parseInt(el.dataset.target, 10);
const suffix = el.dataset.suffix || "";
if (isNaN(target)) return;
el.textContent = "0" + suffix;
ScrollTrigger.create({
trigger: el,
start: "top 85%",
once: true,
onEnter: () => {
gsap.to(
{ val: 0 },
{
val: target,
duration: dur(2),
ease: "power2.out",
onUpdate: function () {
const current = Math.round(this.targets()[0].val);
el.textContent = current.toLocaleString() + suffix;
},
}
);
},
});
});
// ═══════════════════════════════════════════════════════════════════════
// ATHLETE FEATURES — stagger reveal
// ═══════════════════════════════════════════════════════════════════════
const athleteFeatures = document.querySelectorAll(".athlete-feature");
if (athleteFeatures.length) {
gsap.fromTo(
athleteFeatures,
{ opacity: 0, x: reduced ? 0 : -20 },
{
opacity: 1,
x: 0,
duration: dur(0.6),
stagger: 0.15,
ease: "power3.out",
scrollTrigger: {
trigger: ".athlete-features",
start: "top 80%",
toggleActions: "play none none none",
},
}
);
}
// ═══════════════════════════════════════════════════════════════════════
// APP PHONE — mouse tilt parallax
// ═══════════════════════════════════════════════════════════════════════
const devicePhone = document.querySelector(".app-device .device-phone");
if (devicePhone && !reduced) {
// Subtle scroll parallax
gsap.to(devicePhone, {
y: -40,
rotateX: 5,
scrollTrigger: {
trigger: ".app-section",
start: "top center",
end: "bottom center",
scrub: true,
},
});
// Mouse tilt
window.addEventListener("mousemove", (e) => {
const rect = devicePhone.getBoundingClientRect();
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
const x = (e.clientX - cx) / (rect.width / 2);
const y = (e.clientY - cy) / (rect.height / 2);
gsap.to(devicePhone, {
rotateY: x * 8,
rotateX: -y * 8,
duration: 0.8,
ease: "power2.out",
});
});
}
// ═══════════════════════════════════════════════════════════════════════
// CTA — rings + entrance
// ═══════════════════════════════════════════════════════════════════════
gsap.utils.toArray(".ring").forEach((ring, i) => {
gsap.fromTo(
ring,
{ scale: 0.5, opacity: 0 },
{
scale: 1,
opacity: 1,
duration: dur(1.5),
delay: i * 0.15,
scrollTrigger: {
trigger: ".cta-section",
start: "top 80%",
toggleActions: "play none none none",
},
}
);
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nike | Just Do It</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- Gradient Mesh Background -->
<canvas id="mesh-bg"></canvas>
<main id="main">
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- HERO -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="hero">
<div class="hero-content">
<!-- Abstract athletic swooping curve (not the Nike trademark) -->
<div class="hero-swoosh" aria-hidden="true">
<svg viewBox="0 0 320 80" fill="none" width="320" height="80">
<path d="M10 60 C60 60, 100 10, 160 10 S280 55, 310 20" stroke="var(--nk-volt)" stroke-width="2.5" fill="none" opacity="0.35" stroke-linecap="round"/>
</svg>
</div>
<h1 class="hero-title">JUST<br>DO IT.</h1>
<p class="hero-subtitle">Explore the latest Nike innovation — built for athletes who never stop.</p>
<div class="hero-ctas">
<button class="btn-primary btn-volt">Shop Now</button>
<button class="btn-secondary">Explore Collection</button>
</div>
</div>
<div class="scroll-indicator">
<span>Scroll</span>
<div class="scroll-line"></div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- PRODUCT SHOWCASE -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="products-section">
<div class="section-container">
<div class="section-header reveal">
<span class="tag">New Arrivals</span>
<h2 class="section-title">Built for speed</h2>
</div>
<div class="products-grid">
<!-- Air Max -->
<div class="product-card">
<div class="shoe-visual">
<svg viewBox="0 0 200 100" fill="none" aria-hidden="true" class="shoe-svg">
<path d="M30 70 C30 70, 25 55, 40 45 C55 35, 65 38, 80 35 C95 32, 110 28, 130 30 C150 32, 165 40, 175 50 L180 55 C180 55, 175 58, 170 60 L170 65 C170 65, 100 68, 30 70Z" stroke="var(--nk-volt)" stroke-width="1.5" fill="rgba(205,255,0,0.06)"/>
<path d="M45 50 C55 42, 70 40, 85 38" stroke="var(--nk-volt)" stroke-width="0.8" opacity="0.4"/>
<path d="M55 55 C70 48, 90 45, 110 43" stroke="var(--nk-volt)" stroke-width="0.8" opacity="0.3"/>
<!-- Sole -->
<path d="M28 70 L175 65 L178 68 C178 68, 170 75, 28 75Z" stroke="var(--nk-volt)" stroke-width="1" fill="rgba(205,255,0,0.04)"/>
<!-- Air bubble -->
<ellipse cx="60" cy="72" rx="18" ry="4" stroke="var(--nk-volt)" stroke-width="0.8" fill="rgba(205,255,0,0.08)" opacity="0.6"/>
<!-- Lace area -->
<path d="M130 32 L140 28 M135 34 L145 30 M140 36 L150 32" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.35"/>
</svg>
</div>
<h3 class="product-name">Air Max Pulse</h3>
<p class="product-category">Running</p>
<div class="product-meta">
<span class="product-price">$160</span>
<span class="shop-tag">Shop Now</span>
</div>
</div>
<!-- Pegasus -->
<div class="product-card">
<div class="shoe-visual">
<svg viewBox="0 0 200 100" fill="none" aria-hidden="true" class="shoe-svg">
<path d="M25 68 C25 68, 22 50, 38 42 C54 34, 70 36, 90 34 C110 32, 130 30, 150 35 C170 40, 178 50, 180 55 L180 60 C180 60, 170 63, 160 65 L25 68Z" stroke="var(--nk-volt)" stroke-width="1.5" fill="rgba(205,255,0,0.06)"/>
<!-- Speed lines -->
<path d="M50 48 L90 40" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.3"/>
<path d="M60 54 L100 46" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.25"/>
<path d="M70 58 L110 50" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.2"/>
<!-- Sole -->
<path d="M23 68 L163 65 L168 70 C168 70, 155 76, 23 74Z" stroke="var(--nk-volt)" stroke-width="1" fill="rgba(205,255,0,0.04)"/>
<!-- Tongue -->
<path d="M148 35 C152 28, 158 25, 162 28" stroke="var(--nk-volt)" stroke-width="0.8" opacity="0.4"/>
</svg>
</div>
<h3 class="product-name">Pegasus 41</h3>
<p class="product-category">Performance</p>
<div class="product-meta">
<span class="product-price">$140</span>
<span class="shop-tag">Shop Now</span>
</div>
</div>
<!-- Vaporfly -->
<div class="product-card product-card-featured">
<div class="shoe-visual">
<svg viewBox="0 0 200 100" fill="none" aria-hidden="true" class="shoe-svg">
<path d="M20 65 C20 65, 18 45, 35 38 C52 31, 75 33, 100 30 C125 27, 150 30, 168 42 C178 50, 182 55, 182 58 L182 62 C182 62, 165 65, 20 65Z" stroke="var(--nk-volt)" stroke-width="1.5" fill="rgba(205,255,0,0.08)"/>
<!-- Carbon plate line -->
<path d="M35 55 C60 48, 100 42, 165 50" stroke="var(--nk-volt)" stroke-width="1" opacity="0.5" stroke-dasharray="4,4"/>
<!-- ZoomX foam -->
<path d="M18 65 L170 62 L175 67 C175 67, 160 74, 18 72Z" stroke="var(--nk-volt)" stroke-width="1.2" fill="rgba(205,255,0,0.06)"/>
<!-- Ribbed upper lines -->
<path d="M50 42 L55 52 M65 39 L70 50 M80 37 L85 48 M95 35 L100 46 M110 33 L115 44 M125 32 L130 43" stroke="var(--nk-volt)" stroke-width="0.5" opacity="0.25"/>
</svg>
</div>
<h3 class="product-name">Vaporfly 3</h3>
<p class="product-category">Racing</p>
<div class="product-meta">
<span class="product-price">$260</span>
<span class="shop-tag">Shop Now</span>
</div>
<span class="featured-badge">Elite</span>
</div>
<!-- Dunk -->
<div class="product-card">
<div class="shoe-visual">
<svg viewBox="0 0 200 100" fill="none" aria-hidden="true" class="shoe-svg">
<path d="M30 68 C30 68, 28 48, 42 40 C56 32, 75 35, 95 35 C115 35, 135 38, 150 42 C165 46, 172 55, 174 60 L174 65 L30 68Z" stroke="var(--nk-volt)" stroke-width="1.5" fill="rgba(205,255,0,0.06)"/>
<!-- High-top collar -->
<path d="M150 42 C155 32, 162 26, 168 30 C172 33, 175 40, 174 48" stroke="var(--nk-volt)" stroke-width="1.2" fill="rgba(205,255,0,0.04)"/>
<!-- Panel lines -->
<path d="M60 50 C80 42, 110 40, 140 44" stroke="var(--nk-volt)" stroke-width="0.8" opacity="0.3"/>
<path d="M70 58 C90 52, 120 50, 145 52" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.2"/>
<!-- Sole -->
<path d="M28 68 L172 65 L176 70 C176 70, 162 76, 28 74Z" stroke="var(--nk-volt)" stroke-width="1" fill="rgba(205,255,0,0.04)"/>
<!-- Eyelets -->
<circle cx="152" cy="38" r="1.5" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.4"/>
<circle cx="156" cy="34" r="1.5" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.4"/>
<circle cx="160" cy="30" r="1.5" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.4"/>
</svg>
</div>
<h3 class="product-name">Dunk Low</h3>
<p class="product-category">Lifestyle</p>
<div class="product-meta">
<span class="product-price">$115</span>
<span class="shop-tag">Shop Now</span>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- INNOVATION (pinned) -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="how-track" id="innovation">
<div class="how-sticky">
<div class="how-content">
<div class="step-dots">
<span class="step-dot active" data-step="0"></span>
<span class="step-dot" data-step="1"></span>
<span class="step-dot" data-step="2"></span>
</div>
<div class="step-card active" id="step-0">
<span class="tag">Since 1987</span>
<h2 class="step-heading">Air Max</h2>
<p class="step-desc">Visible air cushioning that changed everything. Max Air units absorb impact and provide lightweight cushioning that moves with you.</p>
</div>
<div class="step-card" id="step-1">
<span class="tag">Since 2012</span>
<h2 class="step-heading">Flyknit</h2>
<p class="step-desc">Precision-engineered yarns knitted into a one-piece upper. Lightweight, form-fitting, and virtually seamless for a second-skin feel.</p>
</div>
<div class="step-card" id="step-2">
<span class="tag">Since 2017</span>
<h2 class="step-heading">React Foam</h2>
<p class="step-desc">Ultra-responsive foam that delivers an incredibly smooth ride. Lightweight, durable, and bouncy — engineered for every mile.</p>
</div>
</div>
<!-- Visual side -->
<div class="how-visuals">
<!-- Air Max Visual: air bubble representation -->
<div class="how-visual active" id="visual-0">
<div class="innovation-visual">
<div class="air-unit-visual">
<svg viewBox="0 0 260 200" fill="none" width="260" height="200" aria-hidden="true">
<!-- Shoe sole outline -->
<path d="M30 130 C30 130, 40 80, 80 60 C120 40, 180 50, 220 70 L230 90 L230 130 Z" stroke="var(--nk-volt)" stroke-width="1.5" fill="rgba(205,255,0,0.04)"/>
<!-- Air unit bubble -->
<ellipse cx="130" cy="145" rx="70" ry="20" stroke="var(--nk-volt)" stroke-width="2" fill="rgba(205,255,0,0.1)"/>
<ellipse cx="130" cy="145" rx="50" ry="14" stroke="var(--nk-volt)" stroke-width="0.8" fill="none" opacity="0.4"/>
<!-- Pressure lines -->
<path d="M80 145 L80 120" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.3" stroke-dasharray="3,3"/>
<path d="M130 145 L130 115" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.3" stroke-dasharray="3,3"/>
<path d="M180 145 L180 125" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.3" stroke-dasharray="3,3"/>
<!-- Air dots -->
<circle cx="100" cy="142" r="2" fill="var(--nk-volt)" opacity="0.4"/>
<circle cx="130" cy="140" r="2.5" fill="var(--nk-volt)" opacity="0.5"/>
<circle cx="160" cy="142" r="2" fill="var(--nk-volt)" opacity="0.4"/>
<!-- Label -->
<text x="130" y="185" text-anchor="middle" fill="var(--nk-volt)" font-size="10" font-weight="700" letter-spacing="0.15em" opacity="0.6">VISIBLE AIR</text>
</svg>
</div>
</div>
</div>
<!-- Flyknit Visual: knit pattern -->
<div class="how-visual" id="visual-1">
<div class="innovation-visual">
<div class="flyknit-visual">
<svg viewBox="0 0 260 200" fill="none" width="260" height="200" aria-hidden="true">
<!-- Knit pattern grid -->
<g opacity="0.25">
<path d="M40 40 Q70 30, 100 40 Q130 50, 160 40 Q190 30, 220 40" stroke="var(--nk-volt)" stroke-width="0.8"/>
<path d="M40 55 Q70 45, 100 55 Q130 65, 160 55 Q190 45, 220 55" stroke="var(--nk-volt)" stroke-width="0.8"/>
<path d="M40 70 Q70 60, 100 70 Q130 80, 160 70 Q190 60, 220 70" stroke="var(--nk-volt)" stroke-width="0.8"/>
<path d="M40 85 Q70 75, 100 85 Q130 95, 160 85 Q190 75, 220 85" stroke="var(--nk-volt)" stroke-width="0.8"/>
<path d="M40 100 Q70 90, 100 100 Q130 110, 160 100 Q190 90, 220 100" stroke="var(--nk-volt)" stroke-width="0.8"/>
<path d="M40 115 Q70 105, 100 115 Q130 125, 160 115 Q190 105, 220 115" stroke="var(--nk-volt)" stroke-width="0.8"/>
<path d="M40 130 Q70 120, 100 130 Q130 140, 160 130 Q190 120, 220 130" stroke="var(--nk-volt)" stroke-width="0.8"/>
</g>
<!-- Highlight thread -->
<path d="M60 60 Q100 45, 140 65 Q180 85, 210 60" stroke="var(--nk-volt)" stroke-width="2" fill="none" opacity="0.7"/>
<!-- Shoe outline over the knit -->
<path d="M50 140 C50 140, 45 90, 80 70 C115 50, 170 55, 210 80 L215 100 L215 140 Z" stroke="var(--nk-volt)" stroke-width="1.5" fill="rgba(205,255,0,0.05)"/>
<!-- Yarn dot accents -->
<circle cx="90" cy="80" r="3" fill="var(--nk-volt)" opacity="0.3"/>
<circle cx="140" cy="70" r="3" fill="var(--nk-volt)" opacity="0.4"/>
<circle cx="190" cy="80" r="3" fill="var(--nk-volt)" opacity="0.3"/>
<!-- Label -->
<text x="130" y="175" text-anchor="middle" fill="var(--nk-volt)" font-size="10" font-weight="700" letter-spacing="0.15em" opacity="0.6">FLYKNIT UPPER</text>
</svg>
</div>
</div>
</div>
<!-- React Visual: foam molecule representation -->
<div class="how-visual" id="visual-2">
<div class="innovation-visual">
<div class="react-visual">
<svg viewBox="0 0 260 200" fill="none" width="260" height="200" aria-hidden="true">
<!-- Foam molecules / bouncy circles -->
<circle cx="80" cy="80" r="25" stroke="var(--nk-volt)" stroke-width="1" fill="rgba(205,255,0,0.06)" class="react-bubble"/>
<circle cx="130" cy="60" r="30" stroke="var(--nk-volt)" stroke-width="1.2" fill="rgba(205,255,0,0.08)" class="react-bubble"/>
<circle cx="185" cy="75" r="22" stroke="var(--nk-volt)" stroke-width="1" fill="rgba(205,255,0,0.06)" class="react-bubble"/>
<circle cx="105" cy="115" r="28" stroke="var(--nk-volt)" stroke-width="1" fill="rgba(205,255,0,0.07)" class="react-bubble"/>
<circle cx="165" cy="110" r="26" stroke="var(--nk-volt)" stroke-width="1" fill="rgba(205,255,0,0.06)" class="react-bubble"/>
<circle cx="60" cy="125" r="18" stroke="var(--nk-volt)" stroke-width="0.8" fill="rgba(205,255,0,0.04)" class="react-bubble"/>
<circle cx="210" cy="105" r="16" stroke="var(--nk-volt)" stroke-width="0.8" fill="rgba(205,255,0,0.04)" class="react-bubble"/>
<!-- Bounce arrows -->
<path d="M130 140 L130 155 M125 150 L130 155 L135 150" stroke="var(--nk-volt)" stroke-width="1.2" opacity="0.5"/>
<path d="M130 40 L130 25 M125 30 L130 25 L135 30" stroke="var(--nk-volt)" stroke-width="1.2" opacity="0.5"/>
<!-- Energy wave lines -->
<path d="M40 160 Q90 145, 130 160 Q170 175, 220 160" stroke="var(--nk-volt)" stroke-width="1" opacity="0.3"/>
<path d="M50 170 Q100 158, 130 170 Q160 182, 210 170" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.2"/>
<!-- Label -->
<text x="130" y="195" text-anchor="middle" fill="var(--nk-volt)" font-size="10" font-weight="700" letter-spacing="0.15em" opacity="0.6">REACT FOAM</text>
</svg>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- STATS -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="stats-section">
<div class="section-container">
<div class="stats-grid">
<div class="stat-item reveal">
<div class="stat-number" data-target="30000" data-suffix="+">0</div>
<div class="stat-label">Athletes</div>
<p class="stat-desc">Elite and everyday athletes powered by Nike</p>
</div>
<div class="stat-item reveal">
<div class="stat-number" data-target="190" data-suffix="">0</div>
<div class="stat-label">Countries</div>
<p class="stat-desc">Global reach across every continent</p>
</div>
<div class="stat-item reveal">
<div class="stat-number" data-target="50" data-suffix="+">0</div>
<div class="stat-label">Years of Innovation</div>
<p class="stat-desc">Pushing boundaries since 1972</p>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- ATHLETE SPOTLIGHT -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="athlete-section">
<div class="section-container">
<div class="section-header reveal">
<span class="tag">Athletes</span>
<h2 class="section-title">Greatness is<br>earned</h2>
</div>
<div class="athlete-card glass-card reveal">
<div class="athlete-layout">
<div class="athlete-figure" aria-hidden="true">
<!-- CSS-only running figure silhouette -->
<svg viewBox="0 0 160 280" fill="none" width="160" height="280">
<!-- Head -->
<circle cx="85" cy="30" r="18" fill="var(--nk-volt)" opacity="0.15" stroke="var(--nk-volt)" stroke-width="1.2"/>
<!-- Neck -->
<path d="M85 48 L85 60" stroke="var(--nk-volt)" stroke-width="2.5" stroke-linecap="round"/>
<!-- Torso (leaning forward) -->
<path d="M85 60 L75 120" stroke="var(--nk-volt)" stroke-width="3" stroke-linecap="round"/>
<!-- Arms -->
<path d="M82 70 L55 50 L45 65" stroke="var(--nk-volt)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M82 75 L110 95 L120 80" stroke="var(--nk-volt)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Front leg (extended) -->
<path d="M75 120 L50 170 L30 225" stroke="var(--nk-volt)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Back leg (kicked up) -->
<path d="M75 120 L100 165 L130 150" stroke="var(--nk-volt)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Front shoe -->
<path d="M30 225 L15 228 L15 235 L35 235 L35 228" stroke="var(--nk-volt)" stroke-width="2" fill="rgba(205,255,0,0.1)" stroke-linejoin="round"/>
<!-- Back shoe -->
<path d="M130 150 L140 145 L145 150 L135 155 L130 155" stroke="var(--nk-volt)" stroke-width="2" fill="rgba(205,255,0,0.1)" stroke-linejoin="round"/>
<!-- Motion lines -->
<line x1="120" y1="40" x2="145" y2="40" stroke="var(--nk-volt)" stroke-width="0.8" opacity="0.3"/>
<line x1="125" y1="50" x2="150" y2="50" stroke="var(--nk-volt)" stroke-width="0.6" opacity="0.2"/>
<line x1="130" y1="60" x2="148" y2="60" stroke="var(--nk-volt)" stroke-width="0.5" opacity="0.15"/>
</svg>
</div>
<div class="athlete-content">
<div class="athlete-features">
<div class="athlete-feature">
<div class="af-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--nk-volt)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="24" height="24">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
</div>
<div>
<h4 class="af-title">World Record Holder</h4>
<p class="text-muted text-sm">Marathon: 2:00:35 — pushing the limits of human performance.</p>
</div>
</div>
<div class="athlete-feature">
<div class="af-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--nk-volt)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="24" height="24">
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
</svg>
</div>
<div>
<h4 class="af-title">Powered by Vaporfly</h4>
<p class="text-muted text-sm">Carbon-plated technology delivering 4% more energy return.</p>
</div>
</div>
<div class="athlete-feature">
<div class="af-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--nk-volt)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="24" height="24">
<path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"/>
</svg>
</div>
<div>
<h4 class="af-title">Heart of a Champion</h4>
<p class="text-muted text-sm">Dedication, grit, and the relentless will to win define every stride.</p>
</div>
</div>
</div>
<blockquote class="athlete-quote">
“I don't think limits exist. The more you run, the more you realize your potential is limitless.”
</blockquote>
<div class="athlete-info">
<span class="athlete-name">Eliud K.</span>
<span class="athlete-sport">Marathon Running</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- APP SHOWCASE — Nike Run Club -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="app-section">
<div class="section-container app-grid">
<div class="app-text reveal">
<span class="tag">Nike Run Club</span>
<h2 class="section-title">Track every<br>mile</h2>
<p class="text-muted">GPS tracking, audio guided runs, weekly challenges, and personalized coaching plans — all in one app built for runners.</p>
<div class="app-badges">
<div class="store-badge">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
<div class="store-text">
<span class="store-small">Download on the</span>
<span class="store-name">App Store</span>
</div>
</div>
<div class="store-badge">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 01-.61-.92V2.734a1 1 0 01.609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-3.199l2.302 2.302a1 1 0 010 1.38l-2.302 2.302L15.396 12l2.302-2.492zM5.864 3.458L16.8 9.79l-2.302 2.302L5.864 3.458z"/></svg>
<div class="store-text">
<span class="store-small">Get it on</span>
<span class="store-name">Google Play</span>
</div>
</div>
</div>
</div>
<div class="app-device reveal">
<div class="device-phone">
<div class="phone-notch"></div>
<div class="phone-screen">
<div class="nrc-ui">
<!-- Status bar -->
<div class="nrc-status">
<span class="nrc-time-display">9:41</span>
<div class="nrc-status-icons">
<div class="status-bar-icon"></div>
<div class="status-bar-icon short"></div>
<div class="status-bar-icon"></div>
</div>
</div>
<!-- Route map area -->
<div class="nrc-map">
<svg viewBox="0 0 248 180" fill="none" class="nrc-route-svg" aria-hidden="true">
<!-- Grid background -->
<g opacity="0.08">
<line x1="0" y1="30" x2="248" y2="30" stroke="white"/>
<line x1="0" y1="60" x2="248" y2="60" stroke="white"/>
<line x1="0" y1="90" x2="248" y2="90" stroke="white"/>
<line x1="0" y1="120" x2="248" y2="120" stroke="white"/>
<line x1="0" y1="150" x2="248" y2="150" stroke="white"/>
<line x1="40" y1="0" x2="40" y2="180" stroke="white"/>
<line x1="80" y1="0" x2="80" y2="180" stroke="white"/>
<line x1="120" y1="0" x2="120" y2="180" stroke="white"/>
<line x1="160" y1="0" x2="160" y2="180" stroke="white"/>
<line x1="200" y1="0" x2="200" y2="180" stroke="white"/>
</g>
<!-- Route path -->
<path d="M30 140 C50 140, 60 110, 80 100 C100 90, 110 50, 130 40 C150 30, 160 60, 180 70 C200 80, 210 50, 220 40" stroke="var(--nk-volt)" stroke-width="3" fill="none" stroke-linecap="round" class="nrc-route-path"/>
<!-- Route glow -->
<path d="M30 140 C50 140, 60 110, 80 100 C100 90, 110 50, 130 40 C150 30, 160 60, 180 70 C200 80, 210 50, 220 40" stroke="var(--nk-volt)" stroke-width="8" fill="none" stroke-linecap="round" opacity="0.15"/>
<!-- Start dot -->
<circle cx="30" cy="140" r="5" fill="var(--nk-volt)" opacity="0.8"/>
<circle cx="30" cy="140" r="8" stroke="var(--nk-volt)" stroke-width="1" fill="none" opacity="0.3"/>
<!-- Current position -->
<circle cx="220" cy="40" r="5" fill="var(--nk-volt)"/>
<circle cx="220" cy="40" r="10" stroke="var(--nk-volt)" stroke-width="1.5" fill="none" opacity="0.4" class="nrc-pulse"/>
</svg>
</div>
<!-- Run stats -->
<div class="nrc-stats">
<div class="nrc-stat">
<span class="nrc-stat-value">5:24</span>
<span class="nrc-stat-label">Pace (min/km)</span>
</div>
<div class="nrc-stat nrc-stat-main">
<span class="nrc-stat-value nrc-distance">8.42</span>
<span class="nrc-stat-label">Distance (km)</span>
</div>
<div class="nrc-stat">
<span class="nrc-stat-value">45:32</span>
<span class="nrc-stat-label">Time</span>
</div>
</div>
<!-- Controls -->
<div class="nrc-controls">
<button class="nrc-btn-secondary" aria-label="Lock">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>
</button>
<button class="nrc-btn-pause" aria-label="Pause run">
<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24"><rect x="6" y="4" width="4" height="16" rx="1"/><rect x="14" y="4" width="4" height="16" rx="1"/></svg>
</button>
<button class="nrc-btn-secondary" aria-label="Music">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>
</button>
</div>
</div>
</div>
</div>
<div class="device-glow"></div>
</div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- FINAL CTA -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="cta-section">
<div class="cta-rings" aria-hidden="true">
<div class="ring ring-1"></div>
<div class="ring ring-2"></div>
<div class="ring ring-3"></div>
</div>
<div class="cta-orbs" aria-hidden="true">
<div class="cta-orb cta-orb-1"></div>
<div class="cta-orb cta-orb-2"></div>
</div>
<h2 class="section-title cta-title reveal">Become a<br>Member</h2>
<p class="text-muted cta-subtitle reveal">Sign up for free. Get member-exclusive products, Nike by You customization, and access to the best workouts.</p>
<div class="reveal">
<button class="btn-primary btn-volt">Join Us</button>
</div>
</section>
</main>
<!-- Dependencies (same CDN pattern as lgc-80) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lenis@1.1.18/dist/lenis.min.js"></script>
<script src="./script.js"></script>
</body>
</html>Nike UHD Athletic Brand Landing Page
A premium, ultra-high-definition landing page inspired by Nike’s bold athletic design language with volt accents on deep black.
Highlights
- Animated gradient mesh — Canvas 2D background with volt/orange orbs
- Glassmorphism product cards — Frosted-glass cards with CSS-only shoe silhouettes
- Scroll-pinned Innovation section — 3-step pinned section with technology showcase
- Animated stat counters — Athletes, countries, years of innovation
- CSS-only phone mockup — Nike Run Club UI with route tracking
- Smooth scroll — Lenis-powered throughout
Sections
- Hero — Canvas gradient mesh background with volt/orange orbs, massive headline, CTA button, scroll indicator
- Product Showcase — Grid of glassmorphism cards with CSS-only shoe silhouettes, names, prices, and shop tags
- Innovation Timeline — Pinned 300vh section with 3-step technology walkthrough (Air Max, Flyknit, React) and visual representations
- Stats Counter — Animated numbers for athletes, countries, and years of innovation with scroll-triggered count-up
- Athlete Spotlight — Glass card with CSS-only athlete silhouette, quote, name, and sport
- App Showcase — CSS-only phone mockup with Nike Run Club UI, route map, pace/distance/time stats, and mouse-tilt parallax
- Final CTA — Expanding volt rings with gradient orbs and membership signup prompt
Tech Stack
- GSAP & ScrollTrigger — High-performance scroll-driven animations and pinned sections
- Lenis — Smooth, inertial scrolling
- Canvas 2D — Animated gradient mesh background with drifting volt/orange orbs
- Vanilla CSS3 — Glassmorphism, custom properties, shoe silhouettes, phone mockup
- Responsive — Breakpoints at 1024px, 768px, and 480px