Streaming — Live Sports Landing
A dark, electric live-sports streaming landing for the fictional Pitchside service, built in HTML, CSS, and vanilla JS. A cinematic hero pairs a condensed billboard headline with a featured live match card whose clock ticks and score updates in real time, above an endlessly scrolling fixtures ticker of live and upcoming games. Below sit a leagues grid with live pulses, game-day feature cards, a sport-filterable today schedule with set-a-reminder buttons, a monthly to annual passes grid, a closing CTA, and a multi-column footer with scroll-reveal motion and toasts throughout.
MCP
Code
:root {
--bg: #0b0e14;
--surface: #121722;
--surface-2: #182030;
--ink: #f2f6f4;
--ink-2: #aeb8c4;
--muted: #7b8696;
--brand: #1aff6a;
--brand-ink: #04130a;
--accent: #ffffff;
--line: rgba(255, 255, 255, 0.1);
--line-2: rgba(255, 255, 255, 0.18);
--live: #ff3b5c;
--r-sm: 8px;
--r-md: 12px;
--r-lg: 18px;
--shadow: 0 24px 60px -24px rgba(0, 0, 0, 0.8);
--glow: 0 0 0 1px rgba(26, 255, 106, 0.4), 0 12px 40px -8px rgba(26, 255, 106, 0.35);
--maxw: 1180px;
--cond: "Barlow Condensed", "Inter", system-ui, sans-serif;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
overflow-x: hidden;
}
h1, h2, h3 { margin: 0; letter-spacing: -0.01em; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
.skip-link {
position: fixed; left: 12px; top: -60px; z-index: 100;
background: var(--brand); color: var(--brand-ink);
padding: 10px 14px; border-radius: var(--r-sm); font-weight: 700;
transition: top .2s;
}
.skip-link:focus { top: 12px; }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 4px; }
/* ===== Buttons ===== */
.btn {
font: inherit; font-weight: 700; cursor: pointer;
border: 1px solid transparent; border-radius: 999px;
padding: 10px 18px; color: var(--ink); background: transparent;
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
transition: transform .15s ease, background .2s, border-color .2s, box-shadow .2s;
white-space: nowrap;
}
.btn:active { transform: scale(.96); }
.btn--brand {
background: var(--brand); color: var(--brand-ink);
box-shadow: 0 8px 24px -8px rgba(26, 255, 106, 0.6);
}
.btn--brand:hover { box-shadow: var(--glow); transform: translateY(-1px); }
.btn--ghost:hover { background: var(--surface-2); }
.btn--outline { border-color: var(--line-2); }
.btn--outline:hover { border-color: var(--brand); color: var(--brand); }
.btn--lg { padding: 14px 24px; font-size: 1.02rem; }
.btn--block { width: 100%; }
/* ===== Nav ===== */
.nav {
position: sticky; top: 0; z-index: 50;
background: rgba(11, 14, 20, 0.72);
backdrop-filter: blur(14px);
border-bottom: 1px solid var(--line);
transition: box-shadow .3s, background .3s;
}
.nav.is-scrolled { box-shadow: 0 10px 30px -16px rgba(0, 0, 0, 0.9); background: rgba(11, 14, 20, 0.92); }
.nav__inner {
max-width: var(--maxw); margin: 0 auto;
display: flex; align-items: center; gap: 22px;
padding: 14px 20px;
}
.brand { display: inline-flex; align-items: center; gap: 10px; }
.brand__mark {
width: 26px; height: 26px; border-radius: 7px;
background: conic-gradient(from 220deg, var(--brand), #0fd0ff, var(--brand));
box-shadow: 0 0 16px rgba(26, 255, 106, 0.55);
position: relative;
}
.brand__mark::after {
content: ""; position: absolute; inset: 7px;
border-radius: 3px; background: var(--bg);
}
.brand__name {
font-family: var(--cond); font-weight: 800; font-size: 1.32rem;
letter-spacing: 1px;
}
.brand__name span { color: var(--brand); }
.nav__links { display: flex; gap: 22px; margin-left: 8px; flex: 1; }
.nav__links a { color: var(--ink-2); font-weight: 600; font-size: .94rem; transition: color .2s; }
.nav__links a:hover { color: var(--ink); }
.nav__actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }
.nav__burger {
display: none; flex-direction: column; gap: 5px;
background: none; border: 0; cursor: pointer; padding: 8px;
}
.nav__burger span { width: 22px; height: 2px; background: var(--ink); border-radius: 2px; transition: .25s; }
.nav__burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav__burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
/* ===== Ticker ===== */
.ticker {
display: flex; align-items: stretch;
border-top: 1px solid var(--line);
background: linear-gradient(90deg, rgba(26, 255, 106, 0.06), transparent);
overflow: hidden;
}
.ticker__tag {
display: inline-flex; align-items: center; gap: 7px;
font-family: var(--cond); font-weight: 800; letter-spacing: 1px;
font-size: .82rem; color: var(--brand);
padding: 8px 16px; white-space: nowrap; flex-shrink: 0;
border-right: 1px solid var(--line); background: var(--surface);
}
.ticker__viewport { overflow: hidden; flex: 1; display: flex; align-items: center; }
.ticker__track {
display: flex; gap: 34px; white-space: nowrap;
padding: 8px 24px; will-change: transform;
animation: marquee 38s linear infinite;
}
.ticker:hover .ticker__track { animation-play-state: paused; }
.ticker__item { display: inline-flex; align-items: center; gap: 8px; font-size: .85rem; color: var(--ink-2); }
.ticker__item b { color: var(--ink); font-weight: 600; }
.ticker__item .live { color: var(--live); font-weight: 700; display: inline-flex; align-items: center; gap: 5px; }
.ticker__item .soon { color: var(--brand); font-weight: 700; }
@keyframes marquee { to { transform: translateX(-50%); } }
.dot {
width: 8px; height: 8px; border-radius: 50%; background: currentColor;
display: inline-block; position: relative;
animation: pulse 1.6s ease-out infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 59, 92, 0.6); }
100% { box-shadow: 0 0 0 8px rgba(255, 59, 92, 0); }
}
/* ===== Mobile nav ===== */
.mnav {
display: none; flex-direction: column; gap: 4px;
padding: 14px 20px 22px; background: var(--surface);
border-bottom: 1px solid var(--line);
position: sticky; top: 0; z-index: 49;
}
.mnav a { padding: 12px 4px; font-weight: 600; color: var(--ink-2); border-bottom: 1px solid var(--line); }
.mnav .btn { margin-top: 10px; }
/* ===== Hero ===== */
.hero { position: relative; overflow: hidden; }
.hero__bg {
position: absolute; inset: 0; z-index: 0;
background:
radial-gradient(60% 80% at 18% 0%, rgba(26, 255, 106, 0.14), transparent 60%),
radial-gradient(50% 70% at 92% 30%, rgba(15, 208, 255, 0.12), transparent 60%),
linear-gradient(180deg, #0d1320, var(--bg));
}
.hero__bg::after {
content: ""; position: absolute; inset: 0;
background-image: linear-gradient(var(--line) 1px, transparent 1px),
linear-gradient(90deg, var(--line) 1px, transparent 1px);
background-size: 46px 46px;
-webkit-mask-image: radial-gradient(70% 60% at 50% 20%, #000, transparent 75%);
mask-image: radial-gradient(70% 60% at 50% 20%, #000, transparent 75%);
opacity: .5;
}
.hero__grid {
position: relative; z-index: 1;
max-width: var(--maxw); margin: 0 auto;
display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 48px; align-items: center;
padding: 70px 20px 84px;
}
.hero__copy h1 {
font-family: var(--cond); font-weight: 800;
font-size: clamp(2.6rem, 6vw, 4.4rem); line-height: 0.98;
letter-spacing: -0.5px; margin: 18px 0 18px;
text-transform: uppercase;
}
.hl {
background: linear-gradient(100deg, var(--brand), #9bffc6);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero__lede { color: var(--ink-2); font-size: 1.08rem; max-width: 44ch; }
.hero__cta { display: flex; gap: 12px; margin: 26px 0 22px; flex-wrap: wrap; }
.hero__trust { display: flex; gap: 26px; list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.hero__trust li { color: var(--muted); font-size: .9rem; }
.hero__trust strong { color: var(--brand); font-family: var(--cond); font-size: 1.3rem; margin-right: 4px; }
.badge {
display: inline-flex; align-items: center; gap: 8px;
font-size: .76rem; font-weight: 700; letter-spacing: .6px;
padding: 6px 12px; border-radius: 999px; text-transform: uppercase;
font-family: var(--cond);
}
.badge--live { background: rgba(255, 59, 92, 0.14); color: var(--live); border: 1px solid rgba(255, 59, 92, 0.3); }
/* live card */
.livecard {
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--shadow);
}
.livecard__media { position: relative; aspect-ratio: 16 / 9; overflow: hidden; }
.livecard__pitch {
position: absolute; inset: 0;
background:
repeating-linear-gradient(90deg, #0e2a18 0 36px, #0c2415 36px 72px),
radial-gradient(120% 80% at 50% 120%, rgba(26, 255, 106, 0.25), transparent 60%);
}
.livecard__pitch::before {
content: ""; position: absolute; inset: 12% 8%;
border: 2px solid rgba(255, 255, 255, 0.25); border-radius: 6px;
}
.livecard__pitch::after {
content: ""; position: absolute; left: 50%; top: 12%; bottom: 12%;
width: 2px; background: rgba(255, 255, 255, 0.25); transform: translateX(-50%);
}
.chip {
position: absolute; z-index: 2; top: 12px;
font-family: var(--cond); font-weight: 800; letter-spacing: .8px;
font-size: .72rem; padding: 5px 10px; border-radius: var(--r-sm);
display: inline-flex; align-items: center; gap: 6px;
}
.chip--live { left: 12px; background: var(--live); color: #fff; }
.chip--q { right: 12px; background: rgba(0, 0, 0, 0.55); color: var(--ink); border: 1px solid var(--line-2); backdrop-filter: blur(4px); }
.livecard__play {
position: absolute; z-index: 2; inset: 0; margin: auto;
width: 64px; height: 64px; border-radius: 50%;
background: rgba(11, 14, 20, 0.6); border: 1px solid var(--line-2);
color: var(--brand); font-size: 1.4rem; cursor: pointer;
display: grid; place-items: center; backdrop-filter: blur(6px);
transition: transform .2s, box-shadow .2s, background .2s;
}
.livecard__play:hover { transform: scale(1.08); box-shadow: var(--glow); background: rgba(26, 255, 106, 0.18); }
.livecard__body { padding: 16px 18px 18px; }
.livecard__league { font-size: .78rem; color: var(--muted); font-weight: 600; letter-spacing: .4px; text-transform: uppercase; }
.livecard__score { display: flex; align-items: center; gap: 12px; margin: 12px 0; }
.team { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }
.team--right { justify-content: flex-end; text-align: right; }
.team__crest {
width: 34px; height: 34px; border-radius: 9px; flex-shrink: 0;
display: grid; place-items: center; font-family: var(--cond);
font-weight: 800; font-size: .9rem; color: #04130a;
background: var(--brand);
}
.team__name { font-weight: 600; font-size: .92rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.score { text-align: center; }
.score__nums { font-family: var(--cond); font-weight: 800; font-size: 1.6rem; display: block; letter-spacing: 1px; }
.score__clock { font-size: .76rem; color: var(--live); font-weight: 700; }
.livecard__meta { display: flex; flex-wrap: wrap; gap: 14px; color: var(--ink-2); font-size: .82rem; border-top: 1px solid var(--line); padding-top: 12px; }
/* ===== Sections ===== */
.section { max-width: var(--maxw); margin: 0 auto; padding: 78px 20px; }
.section--alt { max-width: none; background: linear-gradient(180deg, var(--surface), var(--bg)); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.section--alt > * { max-width: var(--maxw); margin-left: auto; margin-right: auto; }
.section__head { text-align: center; max-width: 640px; margin: 0 auto 40px; }
.section__head h2 {
font-family: var(--cond); font-weight: 800; text-transform: uppercase;
font-size: clamp(1.8rem, 4vw, 2.7rem); letter-spacing: -0.3px;
}
.section__head p { color: var(--ink-2); margin-top: 10px; font-size: 1.04rem; }
/* leagues */
.leagues { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; }
.league {
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-md); padding: 18px 12px; text-align: center;
cursor: pointer; transition: transform .2s, border-color .2s, background .2s, box-shadow .2s;
position: relative;
}
.league:hover { transform: translateY(-4px); border-color: var(--brand); box-shadow: var(--glow); }
.league__logo {
width: 46px; height: 46px; margin: 0 auto 10px; border-radius: 12px;
display: grid; place-items: center; font-family: var(--cond);
font-weight: 800; font-size: 1.05rem; color: #04130a;
}
.league__name { font-weight: 600; font-size: .86rem; }
.league__count { font-size: .72rem; color: var(--muted); margin-top: 2px; }
.league__live {
position: absolute; top: 8px; right: 8px; width: 7px; height: 7px;
border-radius: 50%; background: var(--live);
box-shadow: 0 0 0 0 rgba(255, 59, 92, 0.6); animation: pulse 1.6s infinite;
}
/* features */
.features { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.feature {
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-lg); padding: 24px; transition: transform .2s, border-color .2s;
}
.feature:hover { transform: translateY(-4px); border-color: var(--line-2); }
.feature__ico {
width: 48px; height: 48px; border-radius: 13px; display: grid; place-items: center;
font-size: 1.5rem; margin-bottom: 16px;
background: rgba(26, 255, 106, 0.12); border: 1px solid rgba(26, 255, 106, 0.28);
}
.feature h3 { font-family: var(--cond); font-weight: 700; font-size: 1.3rem; text-transform: uppercase; letter-spacing: .3px; }
.feature p { color: var(--ink-2); margin-top: 8px; font-size: .95rem; }
.feature__tag { display: inline-block; margin-top: 14px; font-size: .74rem; font-weight: 700; color: var(--brand); letter-spacing: .5px; text-transform: uppercase; }
/* schedule */
.sched__filters { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-bottom: 24px; }
.filter {
font: inherit; font-weight: 600; font-size: .88rem;
padding: 8px 16px; border-radius: 999px; cursor: pointer;
background: var(--surface); color: var(--ink-2); border: 1px solid var(--line);
transition: .2s;
}
.filter:hover { color: var(--ink); border-color: var(--line-2); }
.filter.is-active { background: var(--brand); color: var(--brand-ink); border-color: var(--brand); }
.sched { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.match {
display: grid; grid-template-columns: 90px 1fr auto; gap: 16px; align-items: center;
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-md); padding: 14px 18px; transition: border-color .2s, background .2s;
}
.match:hover { border-color: var(--line-2); }
.match.is-live { border-color: rgba(255, 59, 92, 0.4); background: linear-gradient(90deg, rgba(255, 59, 92, 0.08), var(--surface)); }
.match__time { font-family: var(--cond); font-weight: 700; font-size: 1.15rem; }
.match__time .now { display: block; font-size: .68rem; color: var(--live); font-weight: 800; letter-spacing: .6px; }
.match__info { min-width: 0; }
.match__teams { font-weight: 600; }
.match__league { font-size: .78rem; color: var(--muted); margin-top: 2px; }
.match__sport {
font-size: .68rem; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
padding: 3px 8px; border-radius: 6px; background: var(--surface-2); color: var(--ink-2);
display: inline-block; margin-right: 8px;
}
.match__btn {
font: inherit; font-weight: 700; font-size: .82rem; cursor: pointer;
padding: 9px 16px; border-radius: 999px; white-space: nowrap;
background: transparent; color: var(--ink); border: 1px solid var(--line-2);
transition: .2s;
}
.match__btn:hover { border-color: var(--brand); color: var(--brand); }
.match.is-live .match__btn { background: var(--live); color: #fff; border-color: var(--live); }
.match__btn.is-set { background: var(--brand); color: var(--brand-ink); border-color: var(--brand); }
/* passes */
.billing { display: flex; align-items: center; justify-content: center; gap: 14px; margin-bottom: 32px; }
.billing__lbl { color: var(--ink-2); font-weight: 600; font-size: .95rem; transition: color .2s; }
.billing__lbl.is-on { color: var(--ink); }
.save { font-size: .7rem; color: var(--brand-ink); background: var(--brand); padding: 2px 7px; border-radius: 999px; font-weight: 800; margin-left: 4px; }
.switch {
width: 52px; height: 28px; border-radius: 999px; cursor: pointer;
background: var(--surface-2); border: 1px solid var(--line-2); position: relative; padding: 0;
transition: background .2s;
}
.switch[aria-checked="true"] { background: var(--brand); border-color: var(--brand); }
.switch__knob {
position: absolute; top: 2px; left: 2px; width: 22px; height: 22px;
border-radius: 50%; background: #fff; transition: transform .22s ease;
}
.switch[aria-checked="true"] .switch__knob { transform: translateX(24px); }
.passes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; align-items: start; }
.pass {
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-lg); padding: 26px 24px; position: relative;
transition: transform .2s, border-color .2s;
}
.pass:hover { transform: translateY(-4px); }
.pass--feat { border-color: var(--brand); box-shadow: var(--glow); }
.pass__pop {
position: absolute; top: -12px; left: 50%; transform: translateX(-50%);
background: var(--brand); color: var(--brand-ink); font-family: var(--cond);
font-weight: 800; font-size: .72rem; letter-spacing: .8px; text-transform: uppercase;
padding: 5px 12px; border-radius: 999px;
}
.pass__name { font-family: var(--cond); font-weight: 800; text-transform: uppercase; font-size: 1.4rem; }
.pass__desc { color: var(--ink-2); font-size: .9rem; margin-top: 4px; }
.pass__price { display: flex; align-items: baseline; gap: 4px; margin: 18px 0 4px; }
.pass__amt { font-family: var(--cond); font-weight: 800; font-size: 2.6rem; }
.pass__per { color: var(--muted); font-size: .9rem; }
.pass__note { font-size: .78rem; color: var(--brand); min-height: 1.1em; font-weight: 600; }
.pass ul { list-style: none; margin: 18px 0 22px; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.pass li { font-size: .9rem; color: var(--ink-2); display: flex; gap: 9px; align-items: flex-start; }
.pass li::before { content: "✓"; color: var(--brand); font-weight: 800; }
/* CTA */
.cta {
position: relative; overflow: hidden; text-align: center;
max-width: var(--maxw); margin: 70px auto; padding: 64px 24px;
background: linear-gradient(180deg, var(--surface-2), var(--surface));
border: 1px solid var(--line); border-radius: var(--r-lg);
}
.cta__glow {
position: absolute; inset: -40% 20% auto; height: 320px;
background: radial-gradient(50% 100% at 50% 0%, rgba(26, 255, 106, 0.3), transparent 70%);
filter: blur(20px);
}
.cta h2 { position: relative; font-family: var(--cond); font-weight: 800; text-transform: uppercase; font-size: clamp(1.9rem, 5vw, 3rem); }
.cta p { position: relative; color: var(--ink-2); margin: 12px 0 24px; font-size: 1.05rem; }
.cta .btn { position: relative; }
.cta__fine { position: relative; display: block; margin-top: 14px; color: var(--muted); font-size: .82rem; }
/* ===== Footer ===== */
.foot { border-top: 1px solid var(--line); background: var(--surface); }
.foot__grid {
max-width: var(--maxw); margin: 0 auto;
display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 30px;
padding: 52px 20px 30px;
}
.foot__brand p { color: var(--muted); margin-top: 12px; max-width: 30ch; font-size: .9rem; }
.foot__col h3 { font-family: var(--cond); font-weight: 700; text-transform: uppercase; font-size: 1rem; letter-spacing: .5px; margin-bottom: 12px; }
.foot__col a { display: block; color: var(--ink-2); font-size: .9rem; padding: 5px 0; transition: color .2s; }
.foot__col a:hover { color: var(--brand); }
.foot__bar {
max-width: var(--maxw); margin: 0 auto; padding: 20px;
border-top: 1px solid var(--line);
display: flex; justify-content: space-between; align-items: center; gap: 14px;
color: var(--muted); font-size: .82rem; flex-wrap: wrap;
}
.foot__social { display: flex; gap: 8px; }
.foot__social a {
width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;
background: var(--surface-2); border: 1px solid var(--line); font-weight: 700; font-size: .8rem;
transition: .2s;
}
.foot__social a:hover { border-color: var(--brand); color: var(--brand); }
/* ===== Toasts ===== */
.toasts {
position: fixed; right: 18px; bottom: 18px; z-index: 200;
display: flex; flex-direction: column; gap: 10px; align-items: flex-end;
}
.toast {
background: var(--surface-2); border: 1px solid var(--line-2);
border-left: 3px solid var(--brand);
color: var(--ink); padding: 12px 16px; border-radius: var(--r-md);
box-shadow: var(--shadow); font-size: .9rem; max-width: 320px;
animation: toastIn .3s ease; font-weight: 500;
}
.toast.out { animation: toastOut .3s ease forwards; }
@keyframes toastIn { from { opacity: 0; transform: translateY(14px); } }
@keyframes toastOut { to { opacity: 0; transform: translateY(14px); } }
/* ===== Reveal ===== */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity .6s ease, transform .6s ease; }
.reveal.in { opacity: 1; transform: none; }
/* ===== Responsive ===== */
@media (max-width: 980px) {
.nav__links { display: none; }
.nav__burger { display: flex; }
.hero__grid { grid-template-columns: 1fr; gap: 34px; }
.leagues { grid-template-columns: repeat(3, 1fr); }
.features { grid-template-columns: 1fr; }
.passes { grid-template-columns: 1fr; }
.foot__grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 520px) {
.nav__inner { padding: 12px 16px; gap: 12px; }
.nav__actions .btn--ghost { display: none; }
.section { padding: 56px 16px; }
.hero__grid { padding: 48px 16px 60px; }
.hero__trust { gap: 18px; }
.leagues { grid-template-columns: repeat(2, 1fr); }
.match { grid-template-columns: 64px 1fr; }
.match__btn { grid-column: 2; justify-self: start; }
.foot__grid { grid-template-columns: 1fr 1fr; }
.foot__bar { flex-direction: column; align-items: flex-start; }
.ticker__tag { font-size: .74rem; padding: 8px 12px; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
html { scroll-behavior: auto; }
.ticker__track { animation: none; }
.reveal { opacity: 1; transform: none; }
}(function () {
"use strict";
/* ---------- toast helper ---------- */
var toastWrap = document.getElementById("toasts");
function toast(msg) {
if (!toastWrap) return;
var el = document.createElement("div");
el.className = "toast";
el.setAttribute("role", "status");
el.textContent = msg;
toastWrap.appendChild(el);
setTimeout(function () {
el.classList.add("out");
setTimeout(function () { el.remove(); }, 320);
}, 2600);
}
document.addEventListener("click", function (e) {
var t = e.target.closest("[data-toast]");
if (t) toast(t.getAttribute("data-toast"));
});
/* ---------- data ---------- */
var leagues = [
{ abbr: "PL", name: "Premier Division", count: "3 live", color: "#1aff6a", live: true, sport: "Football" },
{ abbr: "LL", name: "Iberia Liga", count: "10 today", color: "#ff7a3c", live: false, sport: "Football" },
{ abbr: "NB", name: "Hoops League", count: "2 live", color: "#ff3b5c", live: true, sport: "Basketball" },
{ abbr: "GT", name: "Grand Tennis", count: "6 today", color: "#3aa0ff", live: false, sport: "Tennis" },
{ abbr: "F1", name: "Apex Racing", count: "Qualy live", color: "#ffd23c", live: true, sport: "Motorsport" },
{ abbr: "NH", name: "Ice Premier", count: "4 today", color: "#9b8cff", live: false, sport: "Hockey" },
{ abbr: "MB", name: "Diamond Pro", count: "8 today", color: "#33e0c3", live: false, sport: "Baseball" },
{ abbr: "RU", name: "Scrum Union", count: "1 live", color: "#ff6fae", live: true, sport: "Rugby" },
{ abbr: "GA", name: "Open Golf", count: "Round 2", color: "#7cd84a", live: false, sport: "Golf" },
{ abbr: "CL", name: "Continental Cup", count: "Tonight", color: "#5ec8ff", live: false, sport: "Football" },
{ abbr: "VB", name: "Spike Series", count: "3 today", color: "#ffa14d", live: false, sport: "Volleyball" },
{ abbr: "MX", name: "Dirt Moto GP", count: "Practice", color: "#ff5151", live: false, sport: "Motorsport" }
];
var features = [
{ ico: "📺", title: "Multi-view", desc: "Watch up to 4 matches at once on one screen, swap the main feed with a tap.", tag: "Up to 4 streams" },
{ ico: "📊", title: "Live stats overlay", desc: "Possession, xG, shot maps and player heatmaps updating in real time.", tag: "Real-time data" },
{ ico: "🔁", title: "Instant replay", desc: "Rewind any moment without missing the live action — pick your camera angle.", tag: "9 angles" },
{ ico: "🎙️", title: "Pick your commentary", desc: "Home, away, tactical or radio-style audio — switch languages on the fly.", tag: "12 languages" },
{ ico: "🔔", title: "Goal alerts", desc: "Push notifications and key-moment jumps so you never miss a goal.", tag: "Smart alerts" },
{ ico: "📱", title: "Stream anywhere", desc: "Cast to TV or keep watching on mobile in crisp 4K with zero contract.", tag: "4K · all devices" }
];
var schedule = [
{ time: "LIVE", sub: "67'", sport: "Football", teams: "North Forge vs Harbor City", league: "Premier Division", live: true },
{ time: "LIVE", sub: "Q2", sport: "Basketball", teams: "Summit Kings vs Bay Surge", league: "Hoops League", live: true },
{ time: "LIVE", sub: "Lap 8", sport: "Motorsport", teams: "Apex Racing — Coastal GP", league: "Apex Racing", live: true },
{ time: "18:30", sport: "Football", teams: "Riverside Athletic vs Crown Rovers", league: "Iberia Liga", live: false },
{ time: "19:00", sport: "Tennis", teams: "Vega vs Holloway — Semi-final", league: "Grand Tennis", live: false },
{ time: "20:15", sport: "Football", teams: "Iron Valley vs Maple United", league: "Continental Cup", live: false },
{ time: "21:00", sport: "Basketball", teams: "Delta Thunder vs Pine Pacers", league: "Hoops League", live: false },
{ time: "21:45", sport: "Hockey", teams: "Glacier Wolves vs Ember Blades", league: "Ice Premier", live: false }
];
var passes = [
{
name: "Matchday", desc: "Single sport, casual fan.", m: 9.99,
feats: ["1 sport of your choice", "Full HD streaming", "Live stats overlay", "Watch on 1 device"], feat: false
},
{
name: "All-Access", desc: "Every league, every screen.", m: 19.99,
feats: ["All 7 sports + 30 leagues", "4K Ultra HD & multi-view", "Instant replay, 9 angles", "Watch on 3 devices", "Goal alerts & reminders"], feat: true
},
{
name: "Stadium", desc: "For the whole household.", m: 29.99,
feats: ["Everything in All-Access", "6 simultaneous streams", "Pick-your-commentary audio", "Offline match downloads", "Priority support"], feat: false
}
];
/* ---------- ticker ---------- */
var tickerItems = [
{ t: "live", a: "North Forge", b: "Harbor City", x: "2-1 · 67'" },
{ t: "live", a: "Summit Kings", b: "Bay Surge", x: "58-54 · Q2" },
{ t: "soon", a: "Riverside Athletic", b: "Crown Rovers", x: "18:30" },
{ t: "live", a: "Apex Racing", b: "Coastal GP", x: "Lap 8/52" },
{ t: "soon", a: "Vega", b: "Holloway", x: "19:00 SF" },
{ t: "soon", a: "Iron Valley", b: "Maple United", x: "20:15" },
{ t: "live", a: "Scrum Union XI", b: "Cliff Raiders", x: "21-14 · 54'" },
{ t: "soon", a: "Delta Thunder", b: "Pine Pacers", x: "21:00" }
];
var tickerTrack = document.getElementById("tickerTrack");
if (tickerTrack) {
var html = "";
function row(it) {
var lead = it.t === "live"
? '<span class="live"><span class="dot"></span>LIVE</span>'
: '<span class="soon">' + it.x + '</span>';
var tail = it.t === "live" ? '<b>' + it.x + '</b>' : '';
return '<span class="ticker__item">' + lead +
'<b>' + it.a + '</b> v <b>' + it.b + '</b>' + tail + '</span>';
}
tickerItems.forEach(function (it) { html += row(it); });
// duplicate for seamless marquee loop (50% translate)
tickerTrack.innerHTML = html + html;
}
/* ---------- leagues grid ---------- */
var grid = document.getElementById("leaguesGrid");
if (grid) {
leagues.forEach(function (l) {
var card = document.createElement("button");
card.className = "league";
card.type = "button";
card.setAttribute("data-toast", "Opening " + l.name + " — " + l.sport + ".");
card.innerHTML =
(l.live ? '<span class="league__live" aria-hidden="true"></span>' : "") +
'<span class="league__logo" style="background:' + l.color + '">' + l.abbr + "</span>" +
'<span class="league__name">' + l.name + "</span>" +
'<span class="league__count">' + l.count + "</span>";
grid.appendChild(card);
});
}
/* ---------- features ---------- */
var fg = document.getElementById("featGrid");
if (fg) {
features.forEach(function (f) {
var c = document.createElement("article");
c.className = "feature";
c.innerHTML =
'<div class="feature__ico" aria-hidden="true">' + f.ico + "</div>" +
"<h3>" + f.title + "</h3><p>" + f.desc + "</p>" +
'<span class="feature__tag">' + f.tag + "</span>";
fg.appendChild(c);
});
}
/* ---------- schedule + filters ---------- */
var schedList = document.getElementById("schedList");
var schedFilters = document.getElementById("schedFilters");
var sports = ["All"].concat(
schedule.map(function (m) { return m.sport; })
.filter(function (s, i, a) { return a.indexOf(s) === i; })
);
function renderSchedule(filter) {
if (!schedList) return;
schedList.innerHTML = "";
schedule
.filter(function (m) { return filter === "All" || m.sport === filter; })
.forEach(function (m) {
var li = document.createElement("li");
li.className = "match" + (m.live ? " is-live" : "");
var timeHtml = m.live
? '<div class="match__time"><span class="now">LIVE</span>' + m.sub + "</div>"
: '<div class="match__time">' + m.time + "</div>";
li.innerHTML =
timeHtml +
'<div class="match__info">' +
'<div class="match__teams"><span class="match__sport">' + m.sport + "</span>" + m.teams + "</div>" +
'<div class="match__league">' + m.league + "</div></div>";
var btn = document.createElement("button");
btn.className = "match__btn";
btn.type = "button";
if (m.live) {
btn.textContent = "Watch live";
btn.addEventListener("click", function () { toast("Joining " + m.teams + " live…"); });
} else {
btn.textContent = "Remind me";
btn.addEventListener("click", function () {
if (btn.classList.contains("is-set")) {
btn.classList.remove("is-set");
btn.textContent = "Remind me";
toast("Reminder removed for " + m.teams + ".");
} else {
btn.classList.add("is-set");
btn.textContent = "✓ Reminder set";
toast("We'll alert you before " + m.teams + " (" + m.time + ").");
}
});
}
li.appendChild(btn);
schedList.appendChild(li);
});
}
if (schedFilters) {
sports.forEach(function (s, i) {
var b = document.createElement("button");
b.className = "filter" + (i === 0 ? " is-active" : "");
b.type = "button";
b.setAttribute("role", "tab");
b.setAttribute("aria-selected", i === 0 ? "true" : "false");
b.textContent = s;
b.addEventListener("click", function () {
schedFilters.querySelectorAll(".filter").forEach(function (x) {
x.classList.remove("is-active");
x.setAttribute("aria-selected", "false");
});
b.classList.add("is-active");
b.setAttribute("aria-selected", "true");
renderSchedule(s);
});
schedFilters.appendChild(b);
});
}
renderSchedule("All");
/* ---------- passes + billing ---------- */
var passGrid = document.getElementById("passGrid");
var billSwitch = document.getElementById("billSwitch");
var lblMonthly = document.getElementById("lblMonthly");
var lblAnnual = document.getElementById("lblAnnual");
var annual = false;
function fmt(n) { return "$" + n.toFixed(2); }
function renderPasses() {
if (!passGrid) return;
passGrid.innerHTML = "";
passes.forEach(function (p) {
var monthly = annual ? +(p.m * 0.75).toFixed(2) : p.m;
var note = annual ? "Billed " + fmt(monthly * 12) + "/yr · save 25%" : "Billed monthly";
var card = document.createElement("article");
card.className = "pass" + (p.feat ? " pass--feat" : "");
card.innerHTML =
(p.feat ? '<span class="pass__pop">Most popular</span>' : "") +
'<div class="pass__name">' + p.name + "</div>" +
'<div class="pass__desc">' + p.desc + "</div>" +
'<div class="pass__price"><span class="pass__amt">' + fmt(monthly) + '</span><span class="pass__per">/mo</span></div>' +
'<div class="pass__note">' + note + "</div>" +
"<ul>" + p.feats.map(function (f) { return "<li>" + f + "</li>"; }).join("") + "</ul>";
var btn = document.createElement("button");
btn.className = "btn " + (p.feat ? "btn--brand" : "btn--outline") + " btn--block";
btn.type = "button";
btn.textContent = "Choose " + p.name;
btn.addEventListener("click", function () {
toast(p.name + " pass selected — " + fmt(monthly) + "/mo. Free week starts now!");
});
card.appendChild(btn);
passGrid.appendChild(card);
});
}
if (billSwitch) {
billSwitch.addEventListener("click", function () {
annual = !annual;
billSwitch.setAttribute("aria-checked", String(annual));
lblMonthly.classList.toggle("is-on", !annual);
lblAnnual.classList.toggle("is-on", annual);
renderPasses();
});
}
renderPasses();
/* ---------- live score simulation ---------- */
var clockEl = document.getElementById("matchClock");
var scoreEl = document.getElementById("scoreLine");
var minute = 67, home = 2, away = 1;
var prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (clockEl && !prefersReduced) {
setInterval(function () {
minute = minute >= 90 ? 90 : minute + 1;
clockEl.textContent = minute + "'";
// occasional goal
if (Math.random() < 0.08 && minute < 90) {
if (Math.random() < 0.55) home++; else away++;
if (scoreEl) scoreEl.textContent = home + " — " + away;
toast("⚽ GOAL! " + (Math.random() < 0.5 ? "North Forge" : "Harbor City") +
" now " + home + "-" + away + ".");
}
}, 3500);
}
/* ---------- nav scroll state + mobile menu ---------- */
var nav = document.getElementById("nav");
window.addEventListener("scroll", function () {
if (nav) nav.classList.toggle("is-scrolled", window.scrollY > 8);
}, { passive: true });
var burger = document.getElementById("burger");
var mnav = document.getElementById("mnav");
if (burger && mnav) {
burger.addEventListener("click", function () {
var open = mnav.hasAttribute("hidden");
if (open) { mnav.removeAttribute("hidden"); }
else { mnav.setAttribute("hidden", ""); }
burger.setAttribute("aria-expanded", String(open));
});
mnav.querySelectorAll("a, .btn").forEach(function (a) {
a.addEventListener("click", function () {
mnav.setAttribute("hidden", "");
burger.setAttribute("aria-expanded", "false");
});
});
}
/* ---------- scroll reveal ---------- */
var reveals = document.querySelectorAll(".reveal");
if ("IntersectionObserver" in window && !prefersReduced) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { en.target.classList.add("in"); io.unobserve(en.target); }
});
}, { threshold: 0.12 });
reveals.forEach(function (r) { io.observe(r); });
} else {
reveals.forEach(function (r) { r.classList.add("in"); });
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Pitchside — Live Sports Streaming</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Barlow+Condensed:wght@600;700;800&display=swap"
rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- ===== Top nav ===== -->
<header class="nav" id="nav">
<div class="nav__inner">
<a class="brand" href="#" aria-label="Pitchside home">
<span class="brand__mark" aria-hidden="true"></span>
<span class="brand__name">PITCH<span>SIDE</span></span>
</a>
<nav class="nav__links" aria-label="Primary">
<a href="#leagues">Leagues</a>
<a href="#features">Features</a>
<a href="#schedule">Schedule</a>
<a href="#passes">Passes</a>
</nav>
<div class="nav__actions">
<button class="btn btn--ghost" data-toast="Sign in is illustrative only.">Sign in</button>
<button class="btn btn--brand" data-toast="Starting your free week…">Start free</button>
<button class="nav__burger" id="burger" aria-label="Open menu" aria-expanded="false">
<span></span><span></span><span></span>
</button>
</div>
</div>
<!-- live ticker -->
<div class="ticker" aria-label="Live and upcoming fixtures">
<span class="ticker__tag"><span class="dot"></span>LIVE & NEXT</span>
<div class="ticker__viewport">
<div class="ticker__track" id="tickerTrack"></div>
</div>
</div>
</header>
<!-- mobile nav -->
<div class="mnav" id="mnav" hidden>
<a href="#leagues">Leagues</a>
<a href="#features">Features</a>
<a href="#schedule">Schedule</a>
<a href="#passes">Passes</a>
<button class="btn btn--brand btn--block" data-toast="Starting your free week…">Start free</button>
</div>
<main id="main">
<!-- ===== Hero ===== -->
<section class="hero" aria-labelledby="heroTitle">
<div class="hero__bg" aria-hidden="true"></div>
<div class="hero__grid">
<div class="hero__copy reveal">
<span class="badge badge--live"><span class="dot"></span>LIVE NOW · 14 events</span>
<h1 id="heroTitle">Every match.<br />Every minute.<br /><span class="hl">Zero buffering.</span></h1>
<p class="hero__lede">
Stream live football, basketball, tennis and racing in 4K with multi-view, real-time
stats and instant replays. One pass, every league.
</p>
<div class="hero__cta">
<button class="btn btn--brand btn--lg" data-toast="Free week unlocked — enjoy the matches!">
Start 7-day free week
</button>
<button class="btn btn--outline btn--lg" data-toast="Loading the live multi-view…">
▶ Watch live
</button>
</div>
<ul class="hero__trust">
<li><strong>4K</strong> Ultra HD</li>
<li><strong>9</strong> camera angles</li>
<li><strong>No</strong> contract</li>
</ul>
</div>
<!-- featured live card -->
<aside class="livecard reveal" aria-label="Featured live match">
<div class="livecard__media">
<span class="chip chip--live"><span class="dot"></span>LIVE</span>
<span class="chip chip--q">4K · MULTI-VIEW</span>
<div class="livecard__pitch" aria-hidden="true"></div>
<button class="livecard__play" data-toast="Joining the live broadcast…" aria-label="Play live match">▶</button>
</div>
<div class="livecard__body">
<span class="livecard__league">Premier Division · Matchweek 31</span>
<div class="livecard__score">
<div class="team">
<span class="team__crest" data-c="#1aff6a">NF</span>
<span class="team__name">North Forge</span>
</div>
<div class="score">
<span class="score__nums" id="scoreLine">2 — 1</span>
<span class="score__clock" id="matchClock">67'</span>
</div>
<div class="team team--right">
<span class="team__name">Harbor City</span>
<span class="team__crest" data-c="#3aa0ff">HC</span>
</div>
</div>
<div class="livecard__meta">
<span>📈 Live stats</span><span>🔁 Instant replay</span><span>👥 48,210 watching</span>
</div>
</div>
</aside>
</div>
</section>
<!-- ===== Leagues grid ===== -->
<section class="section" id="leagues" aria-labelledby="leaguesTitle">
<div class="section__head reveal">
<h2 id="leaguesTitle">All your leagues, one place</h2>
<p>Switch competitions instantly — over 30 leagues across 7 sports.</p>
</div>
<div class="leagues reveal" id="leaguesGrid"></div>
</section>
<!-- ===== Features ===== -->
<section class="section section--alt" id="features" aria-labelledby="featTitle">
<div class="section__head reveal">
<h2 id="featTitle">Built for game day</h2>
<p>Pro-grade tools that put you in the director's chair.</p>
</div>
<div class="features reveal" id="featGrid"></div>
</section>
<!-- ===== Schedule ===== -->
<section class="section" id="schedule" aria-labelledby="schedTitle">
<div class="section__head reveal">
<h2 id="schedTitle">Today's schedule</h2>
<p>Filter by sport and set reminders for what's next.</p>
</div>
<div class="sched__filters reveal" id="schedFilters" role="tablist" aria-label="Filter schedule by sport"></div>
<ul class="sched reveal" id="schedList"></ul>
</section>
<!-- ===== Passes / pricing ===== -->
<section class="section section--alt" id="passes" aria-labelledby="passTitle">
<div class="section__head reveal">
<h2 id="passTitle">Pick your pass</h2>
<p>Switch billing to save with an annual pass.</p>
</div>
<div class="billing reveal">
<span id="lblMonthly" class="billing__lbl is-on">Monthly</span>
<button class="switch" id="billSwitch" role="switch" aria-checked="false" aria-label="Toggle annual billing">
<span class="switch__knob"></span>
</button>
<span id="lblAnnual" class="billing__lbl">Annual <span class="save">save 25%</span></span>
</div>
<div class="passes reveal" id="passGrid"></div>
</section>
<!-- ===== CTA ===== -->
<section class="cta reveal" aria-labelledby="ctaTitle">
<div class="cta__glow" aria-hidden="true"></div>
<h2 id="ctaTitle">Kickoff is in minutes.</h2>
<p>Join 2.4M fans streaming live sport without the cable bill.</p>
<button class="btn btn--brand btn--lg" data-toast="Welcome to Pitchside — your free week starts now!">
Start your free week
</button>
<span class="cta__fine">No card for the first 7 days · Cancel anytime</span>
</section>
</main>
<!-- ===== Footer ===== -->
<footer class="foot">
<div class="foot__grid">
<div class="foot__brand">
<span class="brand__name">PITCH<span>SIDE</span></span>
<p>Live sport, streamed in the moment. Illustrative demo service.</p>
</div>
<div class="foot__col">
<h3>Sports</h3>
<a href="#leagues">Football</a><a href="#leagues">Basketball</a>
<a href="#leagues">Tennis</a><a href="#leagues">Motorsport</a>
</div>
<div class="foot__col">
<h3>Product</h3>
<a href="#features">Multi-view</a><a href="#features">Live stats</a>
<a href="#schedule">Schedule</a><a href="#passes">Passes</a>
</div>
<div class="foot__col">
<h3>Company</h3>
<a href="#" data-toast="Illustrative link.">About</a>
<a href="#" data-toast="Illustrative link.">Careers</a>
<a href="#" data-toast="Illustrative link.">Press</a>
</div>
</div>
<div class="foot__bar">
<span>© 2026 Pitchside. Fictional demo — not a real service.</span>
<div class="foot__social">
<a href="#" data-toast="Illustrative link." aria-label="X">X</a>
<a href="#" data-toast="Illustrative link." aria-label="Instagram">IG</a>
<a href="#" data-toast="Illustrative link." aria-label="YouTube">YT</a>
</div>
</div>
</footer>
<div class="toasts" id="toasts" aria-live="polite" aria-atomic="false"></div>
<script src="script.js"></script>
</body>
</html>Live Sports Landing
A high-energy marketing landing for Pitchside, a fictional live-sports streaming service. The cinematic hero leads with a condensed Barlow uppercase billboard headline, a “live now” badge, and dual CTAs over a glowing pitch-grid backdrop. Beside it, a featured live match card renders a CSS-drawn pitch, HD/multi-view quality chips, a play button, and a scoreline whose match clock ticks minute by minute — with the occasional simulated goal firing a toast and bumping the score. An infinite marquee ticker runs live and upcoming fixtures and pauses on hover.
The page then steps through a six-tile leagues grid with pulsing live indicators, a trio-by-trio game-day feature set (multi-view, real-time stats overlay, instant replay, pick-your-commentary, goal alerts, watch anywhere), and a today’s-schedule list you can filter by sport via a tab row. Live matches show a red “LIVE” state and a “Watch live” action; upcoming matches get a toggleable “Remind me” button. A three-tier passes grid (Matchday, All-Access, Stadium) re-prices every plan through a monthly/annual role="switch" that applies a 25% annual discount and recomputes the billed-yearly note.
Everything is vanilla JS: leagues, features, schedule and passes are all data-driven and rendered at runtime, the live clock and ticker animate on timers, a shared toast() helper confirms every illustrative click, and sections reveal on scroll via IntersectionObserver. The top nav gains a shadow on scroll, the mobile menu toggles with an animated burger, focus rings stay visible, and all motion (marquee, live clock, reveals) is disabled under prefers-reduced-motion. The layout is responsive from wide desktop down to ~360px.
Illustrative UI only — fictional titles, not a real streaming service.