Uber UHD Ride-Share Landing Page
A premium dark-themed ride-share landing page inspired by Uber — featuring animated gradient mesh backgrounds, glassmorphism booking cards, CSS-only vehicle illustrations, scroll-pinned feature reveals, animated stat counters, and a phone app mockup with parallax tilt.
Open in Lab
MCP
gsap scrolltrigger lenis css vanilla-js canvas
Targets: JS HTML
Code
/* ═══════════════════════════════════════════════════════════════════════
Uber UHD Ride-Share Landing Page — Styles
═══════════════════════════════════════════════════════════════════════ */
:root {
--uber-black: #000000;
--uber-dark: #0a0a0a;
--uber-surface: #141414;
--uber-border: rgba(255, 255, 255, 0.08);
--uber-green: #06c167;
--uber-green-glow: rgba(6, 193, 103, 0.25);
--uber-green-subtle: rgba(6, 193, 103, 0.08);
--uber-text: #ffffff;
--uber-muted: #999999;
--uber-dim: #666666;
--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: "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font);
background: var(--uber-black);
color: var(--uber-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: 700;
line-height: 1.05;
letter-spacing: -0.04em;
margin-bottom: 1rem;
}
.text-muted {
color: var(--uber-muted);
font-size: 1.05rem;
line-height: 1.6;
}
.text-sm {
font-size: 0.875rem;
}
.tag {
display: inline-block;
padding: 6px 16px;
background: var(--uber-green);
color: #000;
border-radius: 100px;
font-size: 11px;
font-weight: 700;
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: 2.5rem;
}
.hero-content {
position: relative;
z-index: 2;
}
.hero-title {
font-size: clamp(4rem, 14vw, 11rem);
font-weight: 800;
letter-spacing: -0.05em;
line-height: 0.9;
background: linear-gradient(180deg, #fff 30%, #666 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(--uber-muted);
font-weight: 400;
margin-top: 1.25rem;
letter-spacing: 0.01em;
opacity: 0;
}
/* ── Booking card ── */
.hero-booking {
width: 100%;
max-width: 440px;
z-index: 2;
}
.booking-card {
background: var(--glass-bg);
backdrop-filter: blur(24px) saturate(180%);
-webkit-backdrop-filter: blur(24px) saturate(180%);
border: 1px solid var(--glass-border);
border-radius: var(--radius-lg);
padding: 20px;
display: flex;
flex-direction: column;
gap: 14px;
}
.booking-input-group {
display: flex;
flex-direction: column;
gap: 0;
}
.booking-input {
display: flex;
align-items: center;
gap: 14px;
padding: 14px 16px;
background: rgba(255, 255, 255, 0.06);
border-radius: 12px;
transition: background 0.2s ease;
}
.booking-input:first-child {
border-radius: 12px 12px 0 0;
}
.booking-input:last-child {
border-radius: 0 0 12px 12px;
}
.booking-input:hover {
background: rgba(255, 255, 255, 0.09);
}
.input-dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.dot-green {
background: var(--uber-green);
box-shadow: 0 0 8px var(--uber-green-glow);
}
.dot-dark {
background: var(--uber-dim);
}
.input-placeholder {
font-size: 0.95rem;
color: var(--uber-muted);
font-weight: 400;
}
.booking-divider {
height: 1px;
background: rgba(255, 255, 255, 0.06);
margin-left: 38px;
}
.booking-btn {
width: 100%;
padding: 14px;
background: var(--uber-text);
color: var(--uber-black);
border: none;
border-radius: 12px;
font-family: var(--font);
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.booking-btn:hover {
transform: translateY(-1px);
box-shadow: 0 8px 24px rgba(255, 255, 255, 0.15);
}
/* ── 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(--uber-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(--uber-green), transparent);
}
/* ═══════════════════════════════════════════════════════════════════════
RIDE TYPES
═══════════════════════════════════════════════════════════════════════ */
.rides-section {
padding: 100px 1.5rem 120px;
}
.rides-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.25rem;
}
.ride-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;
}
.ride-card:hover {
background: var(--glass-hover);
border-color: rgba(255, 255, 255, 0.15);
transform: translateY(-4px);
}
.ride-card-featured {
border-color: rgba(6, 193, 103, 0.25);
}
.ride-card-featured:hover {
border-color: rgba(6, 193, 103, 0.45);
box-shadow: 0 0 40px rgba(6, 193, 103, 0.08);
}
.ride-icon {
width: 80px;
height: 40px;
color: var(--uber-text);
margin-bottom: 1.25rem;
opacity: 0.85;
}
.ride-name {
font-size: 1.15rem;
font-weight: 700;
margin-bottom: 0.4rem;
}
.ride-desc {
font-size: 0.8rem;
color: var(--uber-muted);
margin-bottom: 1.25rem;
line-height: 1.5;
}
.ride-meta {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.ride-time {
font-size: 0.8rem;
color: var(--uber-dim);
font-weight: 500;
}
.ride-price {
font-size: 1rem;
font-weight: 700;
color: var(--uber-text);
}
.eco-badge {
position: absolute;
top: 16px;
right: 16px;
padding: 4px 10px;
background: var(--uber-green);
color: #000;
border-radius: 100px;
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
}
/* ═══════════════════════════════════════════════════════════════════════
HOW IT WORKS (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(--uber-green);
transform: scale(1.3);
box-shadow: 0 0 10px var(--uber-green-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(1.5rem, 4vw, 2.5rem);
font-weight: 700;
letter-spacing: -0.02em;
margin-bottom: 1rem;
line-height: 1.1;
}
.step-desc {
font-size: 0.95rem;
color: var(--uber-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);
}
/* Mini phone in how-it-works */
.visual-phone-mini {
width: 220px;
aspect-ratio: 9 / 19;
background: var(--uber-surface);
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 28px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.phone-mini-screen {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.mini-map {
flex: 1;
background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%);
position: relative;
overflow: hidden;
}
.mini-map::before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
90deg,
transparent 49%,
rgba(255, 255, 255, 0.03) 49%,
rgba(255, 255, 255, 0.03) 51%,
transparent 51%
),
linear-gradient(
0deg,
transparent 49%,
rgba(255, 255, 255, 0.03) 49%,
rgba(255, 255, 255, 0.03) 51%,
transparent 51%
);
background-size: 40px 40px;
}
.mini-bottom-sheet {
background: var(--uber-surface);
padding: 16px;
display: flex;
flex-direction: column;
gap: 10px;
border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.mini-input-row {
display: flex;
align-items: center;
gap: 10px;
}
.mini-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.mini-dot.green {
background: var(--uber-green);
}
.mini-dot.dark {
background: var(--uber-dim);
}
.mini-bar {
height: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
.mini-bar.faded {
background: rgba(255, 255, 255, 0.05);
}
.w20 {
width: 20%;
}
.w30 {
width: 30%;
}
.w40 {
width: 40%;
}
.w50 {
width: 50%;
}
.w60 {
width: 60%;
}
.w70 {
width: 70%;
}
.mini-btn {
height: 32px;
background: var(--uber-text);
border-radius: 8px;
margin-top: 4px;
}
/* Visual-1: Driver matching */
.mini-car-icon {
position: absolute;
top: 35%;
left: 40%;
animation: carDrift 4s ease-in-out infinite;
}
@keyframes carDrift {
0%,
100% {
transform: translate(0, 0);
}
50% {
transform: translate(10px, -8px);
}
}
.mini-route-line {
position: absolute;
top: 30%;
left: 50%;
width: 2px;
height: 40%;
background: linear-gradient(to bottom, var(--uber-green), transparent);
opacity: 0.4;
}
.mini-driver-row {
display: flex;
align-items: center;
gap: 10px;
}
.mini-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: linear-gradient(135deg, var(--uber-green), #048a46);
flex-shrink: 0;
}
.mini-bar-stack {
display: flex;
flex-direction: column;
gap: 4px;
flex: 1;
}
.mini-eta {
font-size: 11px;
color: var(--uber-green);
font-weight: 600;
text-align: center;
padding: 6px;
background: var(--uber-green-subtle);
border-radius: 6px;
}
/* Visual-2: Riding */
.mini-map-route {
background: linear-gradient(135deg, #1a1a2e 0%, #0f3460 100%);
}
.mini-route-active {
position: absolute;
top: 20%;
left: 30%;
width: 40%;
height: 60%;
border: 2px dashed rgba(6, 193, 103, 0.3);
border-radius: 50%;
}
.mini-trip-info {
display: flex;
flex-direction: column;
gap: 4px;
}
.mini-music-row {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
background: rgba(255, 255, 255, 0.04);
border-radius: 8px;
margin-top: 4px;
}
.mini-equalizer {
display: flex;
align-items: flex-end;
gap: 2px;
height: 12px;
}
.mini-equalizer span {
width: 3px;
background: var(--uber-green);
border-radius: 1px;
animation: eqBounce 0.8s ease-in-out infinite alternate;
}
.mini-equalizer span:nth-child(1) {
height: 4px;
animation-delay: 0s;
}
.mini-equalizer span:nth-child(2) {
height: 10px;
animation-delay: 0.15s;
}
.mini-equalizer span:nth-child(3) {
height: 6px;
animation-delay: 0.3s;
}
.mini-equalizer span:nth-child(4) {
height: 8px;
animation-delay: 0.45s;
}
@keyframes eqBounce {
0% {
height: 3px;
}
100% {
height: 12px;
}
}
/* ═══════════════════════════════════════════════════════════════════════
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: 800;
letter-spacing: -0.03em;
line-height: 1;
background: linear-gradient(180deg, #fff 0%, var(--uber-green) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 0.5rem;
}
.stat-label {
font-size: 1rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 0.75rem;
}
.stat-desc {
font-size: 0.85rem;
color: var(--uber-dim);
max-width: 200px;
margin: 0 auto;
line-height: 1.5;
}
/* ═══════════════════════════════════════════════════════════════════════
SAFETY
═══════════════════════════════════════════════════════════════════════ */
.safety-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);
}
.safety-header {
text-align: center;
margin-bottom: 3.5rem;
}
.safety-icon {
width: 64px;
height: 64px;
margin: 0 auto 2rem;
color: var(--uber-green);
}
.safety-features {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.safety-feature {
display: flex;
align-items: flex-start;
gap: 1.25rem;
padding: 1.5rem;
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);
}
.safety-feature:hover {
background: rgba(255, 255, 255, 0.06);
transform: translateX(4px);
}
.sf-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: var(--uber-green-subtle);
border-radius: 10px;
color: var(--uber-green);
flex-shrink: 0;
}
.sf-title {
font-size: 1rem;
font-weight: 600;
margin-bottom: 0.35rem;
}
/* ═══════════════════════════════════════════════════════════════════════
APP SHOWCASE
═══════════════════════════════════════════════════════════════════════ */
.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(--uber-text);
color: var(--uber-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(--uber-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(--uber-black);
border-radius: 12px;
z-index: 3;
}
.phone-screen {
width: 100%;
height: 100%;
padding: 40px 16px 16px;
}
.app-ui {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 16px;
}
.app-greeting {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.02em;
padding-top: 8px;
}
.app-search-bar {
display: flex;
align-items: center;
gap: 10px;
padding: 14px 16px;
background: rgba(255, 255, 255, 0.08);
border-radius: 12px;
font-size: 14px;
color: var(--uber-muted);
}
.app-search-icon {
color: var(--uber-muted);
flex-shrink: 0;
}
.app-suggestions {
display: flex;
flex-direction: column;
gap: 2px;
}
.app-suggestion {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
border-radius: 10px;
transition: background 0.2s ease;
}
.sug-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.06);
border-radius: 8px;
color: var(--uber-muted);
flex-shrink: 0;
}
.sug-text {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.app-services {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
margin-top: auto;
padding-bottom: 8px;
}
.app-service {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--uber-muted);
font-weight: 500;
}
.service-icon-box {
width: 48px;
height: 48px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.06);
}
.ride-svc {
background: linear-gradient(135deg, rgba(6, 193, 103, 0.2), rgba(6, 193, 103, 0.05));
border: 1px solid rgba(6, 193, 103, 0.15);
}
.eats-svc {
background: linear-gradient(135deg, rgba(6, 193, 103, 0.12), rgba(255, 255, 255, 0.04));
border: 1px solid rgba(255, 255, 255, 0.06);
}
.pkg-svc {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.02));
border: 1px solid rgba(255, 255, 255, 0.06);
}
.rent-svc {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.12), rgba(59, 130, 246, 0.04));
border: 1px solid rgba(59, 130, 246, 0.1);
}
.device-glow {
position: absolute;
inset: -60px;
border-radius: 50%;
background: radial-gradient(circle, rgba(6, 193, 103, 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: 400px;
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(6, 193, 103, 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(6, 193, 103, 0.08);
bottom: -100px;
left: -80px;
}
.cta-orb-2 {
width: 350px;
height: 350px;
background: rgba(6, 193, 103, 0.05);
top: -60px;
right: -80px;
}
/* ── Buttons ── */
.btn-primary {
display: inline-block;
padding: 16px 36px;
background: var(--uber-text);
color: var(--uber-black);
border-radius: 30px;
font-family: var(--font);
font-weight: 700;
font-size: 1rem;
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-glow {
box-shadow: 0 0 40px rgba(6, 193, 103, 0.2);
}
.btn-glow:hover {
box-shadow: 0 0 60px rgba(6, 193, 103, 0.3);
}
/* ═══════════════════════════════════════════════════════════════════════
REDUCED MOTION
═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
.reveal {
opacity: 1;
transform: none;
}
.ride-card {
opacity: 1;
transform: none;
}
.safety-feature {
opacity: 1;
transform: none;
}
.step-card {
transition: none;
}
.how-visual {
transition: none;
}
.step-dot {
transition: none;
}
.mini-car-icon {
animation: none;
}
.mini-equalizer span {
animation: none;
}
.device-glow {
animation: none;
opacity: 0.5;
}
}
/* ═══════════════════════════════════════════════════════════════════════
RESPONSIVE
═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 1024px) {
.rides-grid {
grid-template-columns: repeat(2, 1fr);
}
.app-grid {
flex-direction: column;
text-align: center;
gap: 3rem;
}
.app-badges {
justify-content: center;
}
}
@media (max-width: 768px) {
section {
padding: 80px 1.25rem;
}
.rides-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-booking {
max-width: 360px;
}
}
@media (max-width: 480px) {
.hero-title {
font-size: clamp(3rem, 16vw, 5rem);
}
.booking-card {
padding: 16px;
}
.stat-number {
font-size: 3rem;
}
.glass-card {
padding: 24px;
border-radius: 20px;
}
.safety-feature {
flex-direction: column;
gap: 0.75rem;
}
.device-phone {
width: 200px;
}
}/* ═══════════════════════════════════════════════════════════════════════
Uber UHD Ride-Share 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(6,193,103,0.30)", fx: 0.25, fy: 0.2, ax: 0.3, ay: 0.25, px: 0.0, py: 0.0 },
{ color: "rgba(6,193,103,0.15)", fx: 0.18, fy: 0.35, ax: 0.35, ay: 0.3, px: 1.2, py: 0.8 },
{ color: "rgba(20,184,166,0.20)", fx: 0.4, fy: 0.15, ax: 0.25, ay: 0.35, px: 2.5, py: 1.5 },
{ color: "rgba(6,193,103,0.12)", fx: 0.15, fy: 0.45, ax: 0.4, ay: 0.2, px: 0.7, py: 3.1 },
];
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.08)"));
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-title", {
opacity: 1,
duration: dur(2),
})
.to(
".hero-subtitle",
{
opacity: 1,
y: -10,
duration: dur(1.2),
},
"-=1.5"
)
.fromTo(
".hero-booking",
{
opacity: 0,
y: reduced ? 0 : 40,
},
{
opacity: 1,
y: 0,
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(".hero-booking", {
y: -60,
opacity: 0,
scrollTrigger: {
trigger: ".hero",
start: "20% top",
end: "60% 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",
},
}
);
});
// ═══════════════════════════════════════════════════════════════════════
// RIDE CARDS — stagger reveal
// ═══════════════════════════════════════════════════════════════════════
const rideCards = document.querySelectorAll(".ride-card");
if (rideCards.length) {
gsap.fromTo(
rideCards,
{ 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: ".rides-grid",
start: "top 80%",
toggleActions: "play none none none",
},
}
);
}
// ═══════════════════════════════════════════════════════════════════════
// HOW IT WORKS — 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;
},
}
);
},
});
});
// ═══════════════════════════════════════════════════════════════════════
// SAFETY FEATURES — stagger reveal
// ═══════════════════════════════════════════════════════════════════════
const safetyFeatures = document.querySelectorAll(".safety-feature");
if (safetyFeatures.length) {
gsap.fromTo(
safetyFeatures,
{ opacity: 0, x: reduced ? 0 : -20 },
{
opacity: 1,
x: 0,
duration: dur(0.6),
stagger: 0.15,
ease: "power3.out",
scrollTrigger: {
trigger: ".safety-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>Uber | Go Anywhere</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">
<h1 class="hero-title">Go<br>anywhere</h1>
<p class="hero-subtitle">Request a ride, hop in, and go.</p>
</div>
<div class="hero-booking reveal">
<div class="booking-card">
<div class="booking-input-group">
<div class="booking-input">
<div class="input-dot dot-green"></div>
<span class="input-placeholder">Pickup location</span>
</div>
<div class="booking-divider"></div>
<div class="booking-input">
<div class="input-dot dot-dark"></div>
<span class="input-placeholder">Where to?</span>
</div>
</div>
<button class="booking-btn">Search</button>
</div>
</div>
<div class="scroll-indicator">
<span>Scroll</span>
<div class="scroll-line"></div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- RIDE TYPES -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="rides-section">
<div class="section-container">
<div class="section-header reveal">
<span class="tag">Rides</span>
<h2 class="section-title">Choose your ride</h2>
</div>
<div class="rides-grid">
<!-- UberX -->
<div class="ride-card">
<div class="ride-icon">
<svg viewBox="0 0 80 40" fill="none" aria-hidden="true">
<path d="M12 28h56M16 28c0-6 4-14 12-14h24c8 0 12 8 12 14" stroke="currentColor" stroke-width="1.5" fill="rgba(6,193,103,0.08)"/>
<circle cx="22" cy="30" r="4" stroke="currentColor" stroke-width="1.5"/>
<circle cx="58" cy="30" r="4" stroke="currentColor" stroke-width="1.5"/>
<path d="M28 14h24" stroke="currentColor" stroke-width="1" opacity="0.4"/>
<rect x="30" y="17" width="8" height="6" rx="1" stroke="currentColor" stroke-width="1" opacity="0.3"/>
<rect x="42" y="17" width="8" height="6" rx="1" stroke="currentColor" stroke-width="1" opacity="0.3"/>
</svg>
</div>
<h3 class="ride-name">UberX</h3>
<p class="ride-desc">Affordable rides, all to yourself</p>
<div class="ride-meta">
<span class="ride-time">4 min</span>
<span class="ride-price">$12.50</span>
</div>
</div>
<!-- Comfort -->
<div class="ride-card">
<div class="ride-icon">
<svg viewBox="0 0 80 40" fill="none" aria-hidden="true">
<path d="M10 28h60M14 28c0-8 6-16 14-16h24c8 0 14 8 14 16" stroke="currentColor" stroke-width="1.5" fill="rgba(6,193,103,0.08)"/>
<circle cx="22" cy="30" r="4" stroke="currentColor" stroke-width="1.5"/>
<circle cx="58" cy="30" r="4" stroke="currentColor" stroke-width="1.5"/>
<path d="M26 12h28" stroke="currentColor" stroke-width="1" opacity="0.4"/>
<rect x="28" y="15" width="10" height="7" rx="1.5" stroke="currentColor" stroke-width="1" opacity="0.3"/>
<rect x="42" y="15" width="10" height="7" rx="1.5" stroke="currentColor" stroke-width="1" opacity="0.3"/>
<path d="M38 8l2-3 2 3" stroke="currentColor" stroke-width="1" opacity="0.2"/>
</svg>
</div>
<h3 class="ride-name">Comfort</h3>
<p class="ride-desc">Newer cars with extra legroom</p>
<div class="ride-meta">
<span class="ride-time">6 min</span>
<span class="ride-price">$18.00</span>
</div>
</div>
<!-- Black -->
<div class="ride-card">
<div class="ride-icon">
<svg viewBox="0 0 80 40" fill="none" aria-hidden="true">
<path d="M8 28h64M12 28c0-10 8-18 16-18h24c8 0 16 8 16 18" stroke="currentColor" stroke-width="1.5" fill="rgba(255,255,255,0.04)"/>
<circle cx="20" cy="30" r="4.5" stroke="currentColor" stroke-width="1.5"/>
<circle cx="60" cy="30" r="4.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M24 10h32" stroke="currentColor" stroke-width="1" opacity="0.4"/>
<rect x="26" y="13" width="12" height="8" rx="2" stroke="currentColor" stroke-width="1" opacity="0.3"/>
<rect x="42" y="13" width="12" height="8" rx="2" stroke="currentColor" stroke-width="1" opacity="0.3"/>
<path d="M36 6h8" stroke="currentColor" stroke-width="1.5" opacity="0.15"/>
</svg>
</div>
<h3 class="ride-name">Black</h3>
<p class="ride-desc">Premium rides in luxury cars</p>
<div class="ride-meta">
<span class="ride-time">8 min</span>
<span class="ride-price">$35.00</span>
</div>
</div>
<!-- Green -->
<div class="ride-card ride-card-featured">
<div class="ride-icon">
<svg viewBox="0 0 80 40" fill="none" aria-hidden="true">
<path d="M12 28h56M16 28c0-6 4-14 12-14h24c8 0 12 8 12 14" stroke="currentColor" stroke-width="1.5" fill="rgba(6,193,103,0.12)"/>
<circle cx="22" cy="30" r="4" stroke="currentColor" stroke-width="1.5"/>
<circle cx="58" cy="30" r="4" stroke="currentColor" stroke-width="1.5"/>
<path d="M38 8c-2 0-4 2-4 4h8c0-2-2-4-4-4z" stroke="currentColor" stroke-width="1" opacity="0.5"/>
<line x1="38" y1="4" x2="38" y2="8" stroke="currentColor" stroke-width="1" opacity="0.4"/>
<path d="M36 5c1-1 3-1 4 0" stroke="currentColor" stroke-width="0.8" opacity="0.3"/>
</svg>
</div>
<h3 class="ride-name">Green</h3>
<p class="ride-desc">Electric and hybrid vehicles</p>
<div class="ride-meta">
<span class="ride-time">5 min</span>
<span class="ride-price">$14.00</span>
</div>
<span class="eco-badge">Eco</span>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- HOW IT WORKS (pinned) -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="how-track" id="how-it-works">
<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">Step 1</span>
<h2 class="step-heading">Request a ride</h2>
<p class="step-desc">Open the app, enter your destination, and choose your ride type. See the price upfront before you confirm.</p>
</div>
<div class="step-card" id="step-1">
<span class="tag">Step 2</span>
<h2 class="step-heading">Get matched</h2>
<p class="step-desc">A nearby driver accepts your trip in seconds. Track their arrival in real time on the map.</p>
</div>
<div class="step-card" id="step-2">
<span class="tag">Step 3</span>
<h2 class="step-heading">Enjoy the ride</h2>
<p class="step-desc">Sit back, relax, and enjoy the journey. Pay automatically — no cash, no hassle.</p>
</div>
</div>
<!-- Visual side -->
<div class="how-visuals">
<div class="how-visual active" id="visual-0">
<div class="visual-phone-mini">
<div class="phone-mini-screen">
<div class="mini-map"></div>
<div class="mini-bottom-sheet">
<div class="mini-input-row">
<div class="mini-dot green"></div>
<div class="mini-bar w70"></div>
</div>
<div class="mini-input-row">
<div class="mini-dot dark"></div>
<div class="mini-bar w50"></div>
</div>
<div class="mini-btn"></div>
</div>
</div>
</div>
</div>
<div class="how-visual" id="visual-1">
<div class="visual-phone-mini">
<div class="phone-mini-screen">
<div class="mini-map">
<div class="mini-car-icon">
<svg viewBox="0 0 24 16" fill="none" width="24" height="16">
<path d="M4 12h16M6 12c0-3 2-7 5-7h2c3 0 5 4 5 7" stroke="#06C167" stroke-width="1.2"/>
<circle cx="8" cy="13" r="2" stroke="#06C167" stroke-width="1"/>
<circle cx="16" cy="13" r="2" stroke="#06C167" stroke-width="1"/>
</svg>
</div>
<div class="mini-route-line"></div>
</div>
<div class="mini-bottom-sheet">
<div class="mini-driver-row">
<div class="mini-avatar"></div>
<div class="mini-bar-stack">
<div class="mini-bar w50"></div>
<div class="mini-bar w30 faded"></div>
</div>
</div>
<div class="mini-eta">3 min away</div>
</div>
</div>
</div>
</div>
<div class="how-visual" id="visual-2">
<div class="visual-phone-mini">
<div class="phone-mini-screen">
<div class="mini-map mini-map-route">
<div class="mini-route-active"></div>
</div>
<div class="mini-bottom-sheet">
<div class="mini-trip-info">
<div class="mini-bar w60"></div>
<div class="mini-bar w40 faded"></div>
</div>
<div class="mini-music-row">
<div class="mini-bar w20"></div>
<div class="mini-equalizer">
<span></span><span></span><span></span><span></span>
</div>
</div>
</div>
</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="10000" data-suffix="+">0</div>
<div class="stat-label">Cities</div>
<p class="stat-desc">Available in cities across the globe</p>
</div>
<div class="stat-item reveal">
<div class="stat-number" data-target="7" data-suffix="B+">0</div>
<div class="stat-label">Trips</div>
<p class="stat-desc">Completed rides worldwide</p>
</div>
<div class="stat-item reveal">
<div class="stat-number" data-target="5" data-suffix="M+">0</div>
<div class="stat-label">Drivers</div>
<p class="stat-desc">Active driver-partners earning</p>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- SAFETY -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="safety-section">
<div class="section-container">
<div class="safety-card glass-card reveal">
<div class="safety-header">
<div class="safety-icon">
<svg viewBox="0 0 48 48" fill="none" aria-hidden="true">
<path d="M24 4L6 12v12c0 11.1 7.7 21.5 18 24 10.3-2.5 18-12.9 18-24V12L24 4z" stroke="currentColor" stroke-width="1.5" fill="rgba(6,193,103,0.1)"/>
<path d="M18 24l4 4 8-8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<h2 class="section-title">Your safety<br>drives us</h2>
<p class="text-muted">Every ride comes with technology and people dedicated to keeping you safe.</p>
</div>
<div class="safety-features">
<div class="safety-feature">
<div class="sf-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="24" height="24">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
</div>
<div>
<h4 class="sf-title">Real-time trip tracking</h4>
<p class="text-muted text-sm">Share your trip status with loved ones so they can follow along.</p>
</div>
</div>
<div class="safety-feature">
<div class="sf-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="24" height="24">
<path d="M22 16.92v3a2 2 0 01-2.18 2A19.86 19.86 0 013.09 5.18 2 2 0 015.11 3h3a2 2 0 012 1.72c.13.81.36 1.6.68 2.34a2 2 0 01-.45 2.11L8.91 10.6a16 16 0 006.49 6.49l1.43-1.43a2 2 0 012.11-.45c.74.32 1.53.55 2.34.68a2 2 0 011.72 2.03z"/>
</svg>
</div>
<div>
<h4 class="sf-title">Emergency assistance</h4>
<p class="text-muted text-sm">Tap a button to connect with emergency services directly from the app.</p>
</div>
</div>
<div class="safety-feature">
<div class="sf-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" width="24" height="24">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/>
</svg>
</div>
<div>
<h4 class="sf-title">PIN verification</h4>
<p class="text-muted text-sm">Verify your driver with a unique PIN before you get in the car.</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- APP SHOWCASE -->
<!-- ═══════════════════════════════════════════════════════════ -->
<section class="app-section">
<div class="section-container app-grid">
<div class="app-text reveal">
<span class="tag">The App</span>
<h2 class="section-title">Everything<br>in one app</h2>
<p class="text-muted">Rides, food delivery, package pickup — all from a single app that millions trust every day.</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="app-ui">
<div class="app-greeting">Good evening</div>
<div class="app-search-bar">
<div class="app-search-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
</div>
<span>Where to?</span>
</div>
<div class="app-suggestions">
<div class="app-suggestion">
<div class="sug-icon">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
</div>
<div class="sug-text">
<div class="mini-bar w60"></div>
<div class="mini-bar w40 faded"></div>
</div>
</div>
<div class="app-suggestion">
<div class="sug-icon">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"/></svg>
</div>
<div class="sug-text">
<div class="mini-bar w50"></div>
<div class="mini-bar w70 faded"></div>
</div>
</div>
</div>
<div class="app-services">
<div class="app-service">
<div class="service-icon-box ride-svc"></div>
<span>Ride</span>
</div>
<div class="app-service">
<div class="service-icon-box eats-svc"></div>
<span>Eats</span>
</div>
<div class="app-service">
<div class="service-icon-box pkg-svc"></div>
<span>Package</span>
</div>
<div class="app-service">
<div class="service-icon-box rent-svc"></div>
<span>Rent</span>
</div>
</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">Start riding<br>today</h2>
<p class="text-muted cta-subtitle reveal">Download the app and get your first ride free.</p>
<div class="reveal">
<button class="btn-primary btn-glow">Get Started</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>Uber UHD Ride-Share Landing Page
A premium, ultra-high-definition landing page inspired by Uber’s dark design language. This showcase composes multiple stealthis patterns into a cohesive, production-quality experience.
Highlights
- Animated gradient mesh — Canvas 2D background with drifting green/teal orbs
- Glassmorphism booking card — Frosted-glass pickup/dropoff input mockup in the hero
- Ride type cards — Glass panels with CSS-only vehicle silhouettes and price estimates
- Scroll-pinned “How It Works” — 3-step feature section with GSAP ScrollTrigger transitions
- Animated stat counters — Scroll-triggered count-up for cities, trips, and drivers
- CSS-only phone mockup — App preview with parallax and mouse-tilt interaction
- Smooth scroll — Lenis-powered buttery scrolling throughout
Tech Stack
| Layer | Technology |
|---|---|
| Scroll | Lenis (CDN) |
| Animation | GSAP + ScrollTrigger (CDN) |
| Background | Canvas 2D gradient mesh |
| Styling | CSS custom properties, glassmorphism, backdrop-filter |
| Layout | CSS Grid + Flexbox |