Airline — Loyalty Program
A polished marketing page for SkyMiles, the loyalty program of fictional carrier Skyward Air. It pairs a status-forward hero with an interactive four-tier benefits table you highlight by hovering or tapping a column, clear earn-and-redeem loops, a partner grid, and a live miles calculator that estimates a year of flying and the gap to your next tier. A validated join form reveals an animated digital membership card. Built with vanilla HTML, CSS and JavaScript, responsive to small handsets.
MCP
Code
:root {
--sky: #0a66c2;
--sky-d: #084e95;
--sky-50: #e9f2fb;
--cloud: #f5f8fc;
--sunrise: #ff7a33;
--sunrise-50: #fff0e7;
--ink: #13233b;
--ink-2: #3a4d68;
--muted: #6b7c93;
--bg: #f5f8fc;
--surface: #ffffff;
--line: rgba(19, 35, 59, 0.1);
--line-2: rgba(19, 35, 59, 0.18);
--ok: #1f9d62;
--warn: #e0962a;
--danger: #d4493e;
--boarding: #1f9d62;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--shadow-sm: 0 1px 2px rgba(19, 35, 59, 0.06), 0 1px 3px rgba(19, 35, 59, 0.08);
--shadow-md: 0 8px 24px rgba(19, 35, 59, 0.1);
--shadow-lg: 0 18px 48px rgba(19, 35, 59, 0.16);
--gold: #c9982a;
--silver: #9aa7b5;
--plat: #4a5b72;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
font-size: 16px;
line-height: 1.5;
color: var(--ink);
background: var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.num { font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
h1, h2, h3 { margin: 0; line-height: 1.15; letter-spacing: -0.02em; }
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
a { color: var(--sky); text-decoration: none; }
.page { overflow-x: clip; }
/* ---------- buttons ---------- */
.btn {
--bg-btn: var(--surface);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font: inherit;
font-weight: 600;
padding: 12px 20px;
border-radius: 999px;
border: 1px solid var(--line-2);
background: var(--bg-btn);
color: var(--ink);
cursor: pointer;
transition: transform 0.12s ease, box-shadow 0.18s ease, background 0.18s ease, border-color 0.18s ease;
}
.btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.btn:active { transform: translateY(0); }
.btn:focus-visible { outline: 3px solid var(--sky-50); outline-offset: 2px; }
.btn--accent {
background: var(--sunrise);
border-color: var(--sunrise);
color: #fff;
box-shadow: 0 6px 18px rgba(255, 122, 51, 0.32);
}
.btn--accent:hover { background: #f56a1f; }
.btn--ghost { background: rgba(255, 255, 255, 0.14); border-color: rgba(255, 255, 255, 0.4); color: #fff; }
.btn--block { width: 100%; }
.btn--sm { padding: 9px 16px; font-size: 0.9rem; }
/* ---------- hero ---------- */
.hero {
position: relative;
color: #fff;
background:
radial-gradient(120% 120% at 85% -10%, rgba(255, 122, 51, 0.45), transparent 55%),
linear-gradient(160deg, var(--sky) 0%, var(--sky-d) 70%, #06366a 100%);
overflow: hidden;
}
.hero::after {
content: "";
position: absolute;
inset: 0;
background-image:
radial-gradient(circle at 18% 30%, rgba(255, 255, 255, 0.5) 0 1.5px, transparent 2px),
radial-gradient(circle at 72% 18%, rgba(255, 255, 255, 0.35) 0 1.2px, transparent 2px),
radial-gradient(circle at 50% 70%, rgba(255, 255, 255, 0.3) 0 1px, transparent 2px);
background-size: 220px 220px, 300px 300px, 180px 180px;
opacity: 0.6;
pointer-events: none;
}
.hero__inner {
position: relative;
max-width: 1080px;
margin: 0 auto;
padding: 32px 24px 56px;
}
.brand { display: flex; align-items: center; gap: 10px; margin-bottom: 40px; }
.brand__mark {
display: inline-grid;
place-items: center;
width: 34px;
height: 34px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.16);
color: #fff;
}
.brand__name { font-weight: 800; font-size: 1.05rem; letter-spacing: -0.01em; }
.brand__sub {
font-weight: 600;
font-size: 0.7rem;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 3px 8px;
border-radius: 999px;
background: var(--sunrise);
color: #fff;
}
.hero__title { font-size: clamp(2rem, 5.5vw, 3.4rem); font-weight: 800; }
.hero__lede { max-width: 46ch; margin-top: 16px; color: rgba(255, 255, 255, 0.86); font-size: 1.05rem; }
.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }
.hero__stats {
display: flex;
flex-wrap: wrap;
gap: 32px;
margin-top: 44px;
padding-top: 24px;
border-top: 1px solid rgba(255, 255, 255, 0.18);
}
.hero__stats strong { display: block; font-size: 1.7rem; font-weight: 800; }
.hero__stats span { font-size: 0.85rem; color: rgba(255, 255, 255, 0.78); }
/* ---------- sections ---------- */
.section { max-width: 1080px; margin: 0 auto; padding: 64px 24px; }
.section--alt { max-width: none; background: var(--surface); border-block: 1px solid var(--line); }
.section--alt > * { max-width: 1080px; margin-inline: auto; }
.section__head { margin-bottom: 32px; }
.section__head h2 { font-size: clamp(1.5rem, 3vw, 2rem); font-weight: 800; }
.section__head p { color: var(--muted); margin-top: 8px; max-width: 56ch; }
/* ---------- tier table ---------- */
.tier-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.tier-tab {
display: inline-flex;
align-items: center;
gap: 8px;
font: inherit;
font-weight: 600;
padding: 9px 16px;
border-radius: 999px;
border: 1px solid var(--line-2);
background: var(--surface);
color: var(--ink-2);
cursor: pointer;
transition: all 0.16s ease;
}
.tier-tab:hover { border-color: var(--sky); color: var(--ink); }
.tier-tab.is-active { background: var(--sky); border-color: var(--sky); color: #fff; box-shadow: var(--shadow-sm); }
.tier-tab:focus-visible { outline: 3px solid var(--sky-50); outline-offset: 2px; }
.dot { width: 10px; height: 10px; border-radius: 50%; box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.6) inset; }
.dot--blue { background: var(--sky); }
.dot--silver { background: var(--silver); }
.dot--gold { background: var(--gold); }
.dot--plat { background: var(--plat); }
.table-wrap {
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
background: var(--surface);
box-shadow: var(--shadow-sm);
}
.tiers-table { width: 100%; border-collapse: collapse; min-width: 640px; }
.tiers-table th, .tiers-table td { padding: 14px 16px; text-align: center; }
.tiers-table thead th {
background: var(--cloud);
border-bottom: 1px solid var(--line);
vertical-align: top;
}
.tiers-table .th-feature { text-align: left; }
.tiers-table thead .tier-name { display: block; font-weight: 800; }
.tiers-table thead .tier-req { display: block; font-size: 0.75rem; color: var(--muted); font-weight: 500; }
.tiers-table tbody th {
text-align: left;
font-weight: 600;
color: var(--ink-2);
white-space: nowrap;
}
.tiers-table tbody tr + tr th, .tiers-table tbody tr + tr td { border-top: 1px solid var(--line); }
.tiers-table td { font-weight: 600; font-variant-numeric: tabular-nums; }
.chk { color: var(--ok); font-weight: 800; }
.x { color: var(--line-2); }
.tiers-table [data-col].is-hi { background: var(--sunrise-50); }
.tiers-table thead [data-col].is-hi { background: #ffe3d2; box-shadow: inset 0 -3px 0 var(--sunrise); }
.tiers-table td.is-hi { color: var(--ink); }
/* ---------- how grid ---------- */
.how-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.how-card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 26px;
box-shadow: var(--shadow-sm);
}
.how-card__pill { margin-bottom: 14px; }
.how-card h3 { font-size: 1.25rem; font-weight: 800; margin-bottom: 18px; }
.steps { display: grid; gap: 16px; }
.steps li { display: flex; gap: 14px; }
.steps__n {
flex: 0 0 auto;
display: grid;
place-items: center;
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--sky-50);
color: var(--sky-d);
font-weight: 800;
font-size: 0.85rem;
}
.steps strong { display: block; }
.steps p { color: var(--muted); font-size: 0.92rem; margin-top: 2px; }
.pill {
display: inline-flex;
align-items: center;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 5px 11px;
border-radius: 999px;
}
.pill--ok { background: rgba(31, 157, 98, 0.14); color: var(--ok); }
.pill--accent { background: var(--sunrise-50); color: #c9501a; }
/* ---------- calculator ---------- */
.calc {
display: grid;
grid-template-columns: 1.4fr 1fr;
gap: 20px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 28px;
box-shadow: var(--shadow-sm);
}
.calc__form { display: grid; gap: 24px; }
.field { display: grid; gap: 8px; }
.field label { font-weight: 600; font-size: 0.92rem; }
.field__out { font-weight: 700; color: var(--sky-d); font-variant-numeric: tabular-nums; }
.calc__form .field { position: relative; }
.calc__form .field__out { position: absolute; right: 0; top: 0; }
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 6px;
border-radius: 999px;
background: linear-gradient(90deg, var(--sky) var(--pct, 30%), var(--sky-50) var(--pct, 30%));
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px; height: 20px;
border-radius: 50%;
background: var(--surface);
border: 4px solid var(--sky);
box-shadow: var(--shadow-sm);
cursor: grab;
}
input[type="range"]::-moz-range-thumb {
width: 20px; height: 20px;
border-radius: 50%;
background: var(--surface);
border: 4px solid var(--sky);
box-shadow: var(--shadow-sm);
cursor: grab;
}
input[type="range"]:focus-visible { outline: 3px solid var(--sky-50); outline-offset: 4px; }
select, input[type="text"], input[type="email"] {
font: inherit;
padding: 11px 13px;
border-radius: var(--r-sm);
border: 1px solid var(--line-2);
background: var(--surface);
color: var(--ink);
}
select:focus, input[type="text"]:focus, input[type="email"]:focus {
outline: none;
border-color: var(--sky);
box-shadow: 0 0 0 3px var(--sky-50);
}
.calc__result {
background: linear-gradient(160deg, var(--sky) 0%, var(--sky-d) 100%);
color: #fff;
border-radius: var(--r-md);
padding: 24px;
display: grid;
align-content: start;
gap: 6px;
}
.calc__label { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.08em; color: rgba(255, 255, 255, 0.8); }
.calc__total { font-size: 2.6rem; font-weight: 800; line-height: 1; }
.calc__note { color: rgba(255, 255, 255, 0.86); font-size: 0.95rem; }
.calc__bar { height: 8px; border-radius: 999px; background: rgba(255, 255, 255, 0.22); margin-top: 14px; overflow: hidden; }
.calc__bar span { display: block; height: 100%; background: var(--sunrise); border-radius: 999px; transition: width 0.4s ease; }
.calc__next { font-size: 0.85rem; color: rgba(255, 255, 255, 0.82); margin-top: 4px; }
/* ---------- partners ---------- */
.partners { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.partner {
display: flex;
align-items: center;
gap: 14px;
padding: 16px 18px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-md);
transition: transform 0.14s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.partner:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--sky-50); }
.partner__ico {
flex: 0 0 auto;
display: grid; place-items: center;
width: 44px; height: 44px;
border-radius: 12px;
background: var(--cloud);
font-size: 1.3rem;
}
.partner strong { display: block; }
.partner span { font-size: 0.84rem; color: var(--muted); }
/* ---------- join ---------- */
.section--join {
max-width: none;
background:
radial-gradient(120% 120% at 0% 0%, rgba(10, 102, 194, 0.08), transparent 50%),
var(--cloud);
border-top: 1px solid var(--line);
}
.join {
max-width: 1080px;
margin-inline: auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
align-items: start;
}
.join__copy h2 { font-size: clamp(1.6rem, 3.4vw, 2.2rem); font-weight: 800; margin: 14px 0 10px; }
.join__copy p { color: var(--ink-2); max-width: 44ch; }
.join__perks { margin-top: 20px; display: grid; gap: 10px; }
.join__perks li { position: relative; padding-left: 28px; color: var(--ink-2); }
.join__perks li::before {
content: "✓";
position: absolute; left: 0; top: 0;
width: 20px; height: 20px;
display: grid; place-items: center;
border-radius: 50%;
background: rgba(31, 157, 98, 0.16);
color: var(--ok);
font-weight: 800; font-size: 0.75rem;
}
.join__form, .member-card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 26px;
box-shadow: var(--shadow-md);
}
.join__form h3 { font-size: 1.25rem; font-weight: 800; margin-bottom: 18px; }
.join__form .row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.join__form .field { margin-bottom: 16px; }
.field--check { display: flex; flex-direction: row; align-items: center; gap: 10px; }
.field--check input { width: 18px; height: 18px; accent-color: var(--sky); }
.field--check label { font-weight: 500; font-size: 0.92rem; }
.field__err { color: var(--danger); font-size: 0.82rem; min-height: 1em; }
.field input.is-invalid { border-color: var(--danger); box-shadow: 0 0 0 3px rgba(212, 73, 62, 0.14); }
/* member card reveal */
.member-card { grid-column: 2; animation: pop 0.4s ease; }
.member-card[hidden] { display: none; }
@keyframes pop {
from { opacity: 0; transform: translateY(12px) scale(0.98); }
to { opacity: 1; transform: none; }
}
.member-card__top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px; }
.member-card__brand { font-weight: 700; font-size: 0.82rem; letter-spacing: 0.04em; color: var(--sky-d); }
.member-card__name { font-size: 1.5rem; font-weight: 800; margin-bottom: 18px; }
.member-card__row { display: flex; justify-content: space-between; gap: 16px; padding: 12px 0; border-top: 1px dashed var(--line-2); }
.member-card__k { display: block; font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.member-card__v { display: block; font-weight: 700; margin-top: 2px; }
.member-card #cardReset { margin-top: 18px; border-color: var(--line-2); background: var(--surface); color: var(--ink); }
/* ---------- footer ---------- */
.foot { padding: 32px 24px; text-align: center; color: var(--muted); font-size: 0.85rem; }
/* ---------- toast ---------- */
.toast-host {
position: fixed;
left: 50%;
bottom: 24px;
transform: translateX(-50%);
display: grid;
gap: 10px;
z-index: 50;
width: max-content;
max-width: calc(100vw - 32px);
}
.toast {
background: var(--ink);
color: #fff;
padding: 12px 18px;
border-radius: 999px;
font-weight: 600;
font-size: 0.92rem;
box-shadow: var(--shadow-lg);
display: flex;
align-items: center;
gap: 10px;
animation: toast-in 0.3s ease;
}
.toast::before { content: "✈"; color: var(--sunrise); }
.toast.is-out { animation: toast-out 0.3s ease forwards; }
@keyframes toast-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateY(12px); } }
/* ---------- responsive ---------- */
@media (max-width: 860px) {
.how-grid, .calc, .join { grid-template-columns: 1fr; }
.partners { grid-template-columns: repeat(2, 1fr); }
.member-card { grid-column: 1; }
}
@media (max-width: 520px) {
.section { padding: 48px 18px; }
.hero__inner { padding: 24px 18px 44px; }
.hero__stats { gap: 22px; }
.partners { grid-template-columns: 1fr; }
.join__form .row { grid-template-columns: 1fr; }
.calc__total { font-size: 2.2rem; }
.btn { padding: 11px 18px; }
}
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}(function () {
"use strict";
/* ---------- toast helper ---------- */
var host = document.getElementById("toastHost");
function toast(msg) {
if (!host) return;
var el = document.createElement("div");
el.className = "toast";
el.textContent = msg;
host.appendChild(el);
setTimeout(function () {
el.classList.add("is-out");
setTimeout(function () { el.remove(); }, 320);
}, 2600);
}
var fmt = function (n) { return Math.round(n).toLocaleString("en-US"); };
/* ---------- tier compare highlight ---------- */
var table = document.getElementById("tierTable");
var tabs = Array.prototype.slice.call(document.querySelectorAll(".tier-tab"));
function highlight(tier) {
if (!table) return;
table.querySelectorAll("[data-col]").forEach(function (cell) {
cell.classList.toggle("is-hi", cell.getAttribute("data-col") === tier);
});
tabs.forEach(function (t) {
var on = t.getAttribute("data-tier") === tier;
t.classList.toggle("is-active", on);
t.setAttribute("aria-selected", on ? "true" : "false");
});
}
tabs.forEach(function (tab) {
tab.addEventListener("click", function () {
highlight(tab.getAttribute("data-tier"));
});
tab.addEventListener("mouseenter", function () {
highlight(tab.getAttribute("data-tier"));
});
});
highlight("blue");
/* ---------- miles calculator ---------- */
var flights = document.getElementById("flights");
var distance = document.getElementById("distance");
var tierSel = document.getElementById("tierSel");
var flightsOut = document.getElementById("flightsOut");
var distanceOut = document.getElementById("distanceOut");
var calcTotal = document.getElementById("calcTotal");
var calcNote = document.getElementById("calcNote");
var calcBar = document.getElementById("calcBar");
var calcNext = document.getElementById("calcNext");
var TIERS = [
{ name: "Silver", req: 25000 },
{ name: "Gold", req: 60000 },
{ name: "Platinum", req: 120000 }
];
function setRangeFill(input) {
var min = +input.min, max = +input.max, val = +input.value;
var pct = ((val - min) / (max - min)) * 100;
input.style.setProperty("--pct", pct + "%");
}
function recalc() {
if (!flights) return;
setRangeFill(flights);
setRangeFill(distance);
var trips = +flights.value;
var dist = +distance.value;
var rate = parseFloat(tierSel.value);
var total = trips * dist * rate;
var awardSeats = Math.floor(total / 7500);
flightsOut.textContent = trips + (trips === 1 ? " trip" : " trips");
distanceOut.textContent = fmt(dist) + " mi";
calcTotal.textContent = fmt(total);
calcNote.innerHTML = "≈ <span class=\"num\">" + awardSeats + "</span> short-haul award seat" + (awardSeats === 1 ? "" : "s");
var next = TIERS.find(function (t) { return total < t.req; });
if (next) {
var gap = next.req - total;
calcNext.innerHTML = fmt(gap) + " miles to <strong>" + next.name + "</strong>";
calcBar.style.width = Math.max(4, Math.min(100, (total / next.req) * 100)) + "%";
} else {
calcNext.innerHTML = "Platinum status earned — top tier reached.";
calcBar.style.width = "100%";
}
}
[flights, distance, tierSel].forEach(function (el) {
if (el) el.addEventListener("input", recalc);
});
recalc();
/* ---------- join form + reveal ---------- */
var form = document.getElementById("joinForm");
var card = document.getElementById("memberCard");
var emailInput = document.getElementById("email");
var emailErr = document.getElementById("emailErr");
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function genMemberNo() {
var n = Math.floor(100000 + Math.random() * 899999);
return "SM-" + n;
}
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var first = document.getElementById("first").value.trim();
var last = document.getElementById("last").value.trim();
var email = emailInput.value.trim();
var terms = document.getElementById("terms").checked;
var ok = true;
if (!first || !last) {
toast("Please enter your full name.");
ok = false;
}
if (!EMAIL_RE.test(email)) {
emailInput.classList.add("is-invalid");
emailErr.textContent = "Enter a valid email address.";
ok = false;
} else {
emailInput.classList.remove("is-invalid");
emailErr.textContent = "";
}
if (!terms) {
toast("Please accept the program terms.");
ok = false;
}
if (!ok) return;
document.getElementById("cardName").textContent = first + " " + last;
document.getElementById("cardNo").textContent = genMemberNo();
document.getElementById("cardDate").textContent = new Date().toLocaleDateString("en-GB", {
day: "2-digit", month: "short", year: "numeric"
});
form.hidden = true;
card.hidden = false;
card.scrollIntoView({ behavior: "smooth", block: "nearest" });
toast("Welcome aboard — 5,000 miles credited!");
});
emailInput.addEventListener("input", function () {
if (emailInput.classList.contains("is-invalid") && EMAIL_RE.test(emailInput.value.trim())) {
emailInput.classList.remove("is-invalid");
emailErr.textContent = "";
}
});
}
var reset = document.getElementById("cardReset");
if (reset) {
reset.addEventListener("click", function () {
card.hidden = true;
form.hidden = false;
form.scrollIntoView({ behavior: "smooth", block: "nearest" });
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Skyward Air — SkyMiles Loyalty Program</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&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="page">
<!-- Hero -->
<header class="hero" role="banner">
<div class="hero__inner">
<div class="brand">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none"><path d="M21 16v-2l-8-5V3.5a1.5 1.5 0 0 0-3 0V9l-8 5v2l8-2.5V18l-2 1.5V21l3.5-1 3.5 1v-1.5L13 18v-4.5L21 16Z" fill="currentColor"/></svg>
</span>
<span class="brand__name">Skyward Air</span>
<span class="brand__sub">SkyMiles</span>
</div>
<h1 class="hero__title">Fly more. Earn more.<br/>Travel like it's yours.</h1>
<p class="hero__lede">Join SkyMiles and turn every journey into rewards — earn miles on flights, partners and everyday spend, then redeem them for seats, upgrades and lounge access across 140+ destinations.</p>
<div class="hero__cta">
<a href="#join" class="btn btn--accent">Join free</a>
<a href="#tiers" class="btn btn--ghost">Compare tiers</a>
</div>
<ul class="hero__stats" aria-label="Program highlights">
<li><strong class="num">140+</strong><span>Destinations</span></li>
<li><strong class="num">28</strong><span>Partner brands</span></li>
<li><strong class="num">2.4M</strong><span>Members</span></li>
</ul>
</div>
</header>
<!-- Tier comparison -->
<section class="section" id="tiers" aria-labelledby="tiers-h">
<div class="section__head">
<h2 id="tiers-h">Tier benefits</h2>
<p>Status unlocks as you fly. Hover or tap a tier to highlight what changes.</p>
</div>
<div class="tiers" role="group" aria-label="Membership tiers">
<div class="tier-tabs" role="tablist" aria-label="Highlight a tier column">
<button class="tier-tab is-active" role="tab" aria-selected="true" data-tier="blue">
<span class="dot dot--blue"></span>Blue
</button>
<button class="tier-tab" role="tab" aria-selected="false" data-tier="silver">
<span class="dot dot--silver"></span>Silver
</button>
<button class="tier-tab" role="tab" aria-selected="false" data-tier="gold">
<span class="dot dot--gold"></span>Gold
</button>
<button class="tier-tab" role="tab" aria-selected="false" data-tier="plat">
<span class="dot dot--plat"></span>Platinum
</button>
</div>
<div class="table-wrap">
<table class="tiers-table" id="tierTable">
<thead>
<tr>
<th scope="col" class="th-feature">Benefit</th>
<th scope="col" data-col="blue"><span class="tier-name">Blue</span><span class="tier-req">0 mi</span></th>
<th scope="col" data-col="silver"><span class="tier-name">Silver</span><span class="tier-req">25,000 mi</span></th>
<th scope="col" data-col="gold"><span class="tier-name">Gold</span><span class="tier-req">60,000 mi</span></th>
<th scope="col" data-col="plat"><span class="tier-name">Platinum</span><span class="tier-req">120,000 mi</span></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Miles earning rate</th>
<td data-col="blue">1×</td><td data-col="silver">1.25×</td><td data-col="gold">1.5×</td><td data-col="plat">2×</td>
</tr>
<tr>
<th scope="row">Free checked bags</th>
<td data-col="blue">0</td><td data-col="silver">1</td><td data-col="gold">2</td><td data-col="plat">3</td>
</tr>
<tr>
<th scope="row">Priority boarding</th>
<td data-col="blue"><span class="x">—</span></td><td data-col="silver"><span class="chk">✓</span></td><td data-col="gold"><span class="chk">✓</span></td><td data-col="plat"><span class="chk">✓</span></td>
</tr>
<tr>
<th scope="row">Complimentary upgrades</th>
<td data-col="blue"><span class="x">—</span></td><td data-col="silver"><span class="x">—</span></td><td data-col="gold"><span class="chk">✓</span></td><td data-col="plat"><span class="chk">✓</span></td>
</tr>
<tr>
<th scope="row">Lounge access</th>
<td data-col="blue"><span class="x">—</span></td><td data-col="silver"><span class="x">—</span></td><td data-col="gold">2 / yr</td><td data-col="plat">Unlimited</td>
</tr>
<tr>
<th scope="row">Dedicated support line</th>
<td data-col="blue"><span class="x">—</span></td><td data-col="silver"><span class="x">—</span></td><td data-col="gold"><span class="chk">✓</span></td><td data-col="plat"><span class="chk">✓</span></td>
</tr>
<tr>
<th scope="row">Award change fees</th>
<td data-col="blue">Standard</td><td data-col="silver">Reduced</td><td data-col="gold">Waived</td><td data-col="plat">Waived</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- How to earn / redeem -->
<section class="section section--alt" aria-labelledby="how-h">
<div class="section__head">
<h2 id="how-h">Earn & redeem</h2>
<p>Two simple loops keep your balance moving.</p>
</div>
<div class="how-grid">
<article class="how-card">
<span class="how-card__pill pill pill--ok">Earn</span>
<h3>Stack miles everywhere</h3>
<ul class="steps">
<li><span class="steps__n">1</span><div><strong>Fly</strong><p>Earn base miles on every paid ticket, multiplied by your tier.</p></div></li>
<li><span class="steps__n">2</span><div><strong>Shop partners</strong><p>Hotels, car hire and the SkyMiles card add miles automatically.</p></div></li>
<li><span class="steps__n">3</span><div><strong>Refer friends</strong><p>Bank 2,500 bonus miles each time a referral takes their first flight.</p></div></li>
</ul>
</article>
<article class="how-card">
<span class="how-card__pill pill pill--accent">Redeem</span>
<h3>Spend them your way</h3>
<ul class="steps">
<li><span class="steps__n">1</span><div><strong>Award seats</strong><p>From 7,500 miles one-way on short-haul economy.</p></div></li>
<li><span class="steps__n">2</span><div><strong>Cabin upgrades</strong><p>Lift any paid fare to the next cabin at the gate or online.</p></div></li>
<li><span class="steps__n">3</span><div><strong>Lounge & extras</strong><p>Day passes, extra bags and seat selection — all on miles.</p></div></li>
</ul>
</article>
</div>
</section>
<!-- Miles calculator teaser -->
<section class="section" aria-labelledby="calc-h">
<div class="section__head">
<h2 id="calc-h">Miles calculator</h2>
<p>Estimate what a typical year of flying earns you.</p>
</div>
<div class="calc">
<form class="calc__form" id="calcForm" novalidate>
<div class="field">
<label for="flights">Return flights per year</label>
<input type="range" id="flights" min="1" max="40" value="8" step="1" />
<output id="flightsOut" class="field__out">8 trips</output>
</div>
<div class="field">
<label for="distance">Avg miles per return trip</label>
<input type="range" id="distance" min="500" max="12000" value="3200" step="100" />
<output id="distanceOut" class="field__out">3,200 mi</output>
</div>
<div class="field">
<label for="tierSel">Your tier</label>
<select id="tierSel">
<option value="1">Blue · 1×</option>
<option value="1.25">Silver · 1.25×</option>
<option value="1.5" selected>Gold · 1.5×</option>
<option value="2">Platinum · 2×</option>
</select>
</div>
</form>
<aside class="calc__result" aria-live="polite">
<span class="calc__label">Estimated yearly miles</span>
<strong class="calc__total num" id="calcTotal">38,400</strong>
<p class="calc__note" id="calcNote">≈ <span class="num">5</span> short-haul award seats</p>
<div class="calc__bar"><span id="calcBar" style="width:32%"></span></div>
<p class="calc__next" id="calcNext">17,600 miles to <strong>Platinum</strong></p>
</aside>
</div>
</section>
<!-- Partners -->
<section class="section section--alt" aria-labelledby="partners-h">
<div class="section__head">
<h2 id="partners-h">Earning partners</h2>
<p>Beyond the runway — miles add up on the brands you already use.</p>
</div>
<ul class="partners" aria-label="Earning partners">
<li class="partner"><span class="partner__ico">🏨</span><div><strong>Meridian Hotels</strong><span>Up to 3× per stay</span></div></li>
<li class="partner"><span class="partner__ico">🚗</span><div><strong>DriveGo Rental</strong><span>500 mi per rental</span></div></li>
<li class="partner"><span class="partner__ico">💳</span><div><strong>SkyMiles Card</strong><span>2× on all spend</span></div></li>
<li class="partner"><span class="partner__ico">☕</span><div><strong>Brew & Co</strong><span>1× per purchase</span></div></li>
<li class="partner"><span class="partner__ico">🛍️</span><div><strong>Aralis Retail</strong><span>2× online</span></div></li>
<li class="partner"><span class="partner__ico">⛽</span><div><strong>Velo Fuel</strong><span>1× per litre</span></div></li>
</ul>
</section>
<!-- Join CTA / form -->
<section class="section section--join" id="join" aria-labelledby="join-h">
<div class="join">
<div class="join__copy">
<span class="pill pill--accent">Free forever</span>
<h2 id="join-h">Start earning today</h2>
<p>No fees, no minimums. Create your SkyMiles account and your number is ready before your next boarding pass.</p>
<ul class="join__perks">
<li>5,000 welcome miles on your first flight</li>
<li>Instant digital membership card</li>
<li>Status match from any airline program</li>
</ul>
</div>
<form class="join__form" id="joinForm" novalidate>
<h3>Join SkyMiles</h3>
<div class="row">
<div class="field">
<label for="first">First name</label>
<input type="text" id="first" name="first" autocomplete="given-name" required placeholder="Mara" />
</div>
<div class="field">
<label for="last">Last name</label>
<input type="text" id="last" name="last" autocomplete="family-name" required placeholder="Okonkwo" />
</div>
</div>
<div class="field">
<label for="email">Email</label>
<input type="email" id="email" name="email" autocomplete="email" required placeholder="[email protected]" />
<span class="field__err" id="emailErr" role="alert"></span>
</div>
<div class="field field--check">
<input type="checkbox" id="terms" />
<label for="terms">I agree to the SkyMiles program terms.</label>
</div>
<button type="submit" class="btn btn--accent btn--block">Create my account</button>
</form>
<!-- Reveal: digital card -->
<div class="member-card" id="memberCard" hidden aria-live="polite">
<div class="member-card__top">
<span class="member-card__brand">Skyward Air · SkyMiles</span>
<span class="pill pill--ok">Active</span>
</div>
<div class="member-card__name" id="cardName">Mara Okonkwo</div>
<div class="member-card__row">
<div><span class="member-card__k">Member no.</span><span class="member-card__v num" id="cardNo">SM-000000</span></div>
<div><span class="member-card__k">Tier</span><span class="member-card__v">Blue</span></div>
</div>
<div class="member-card__row">
<div><span class="member-card__k">Welcome miles</span><span class="member-card__v num">5,000</span></div>
<div><span class="member-card__k">Joined</span><span class="member-card__v num" id="cardDate">—</span></div>
</div>
<button type="button" class="btn btn--ghost btn--sm" id="cardReset">Edit details</button>
</div>
</div>
</section>
<footer class="foot">
<p>SkyMiles is an illustrative loyalty concept for Skyward Air — a fictional carrier.</p>
</footer>
</main>
<div class="toast-host" id="toastHost" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Loyalty Program
A complete loyalty landing page for SkyMiles, the rewards program of the fictional Skyward Air. The aviation-blue hero opens with the carrier mark, a sunrise-orange join button and a strip of program stats, then the page steps through everything a prospective member needs: tier benefits, how miles flow in and out, earning partners and a frictionless sign-up.
The centrepiece is a four-column benefits table spanning Blue, Silver, Gold and Platinum. A row of pill tabs lets you spotlight any tier — hovering or tapping a column washes it in sunrise tint and underlines its header so the differences read at a glance, with check marks, dashes and tabular figures keeping the comparison precise. Below it, the miles calculator turns two sliders and a tier selector into a live yearly-earnings estimate, the equivalent number of award seats, and how many miles remain to the next status level, all animated on a progress bar.
Joining is just as interactive: the form validates name, email and terms inline, then swaps itself for an animated digital membership card carrying a generated member number and join date, with an edit action to step back. A small toast helper confirms each action, and the whole layout reflows cleanly down to roughly 360px while honouring reduced-motion preferences.
Illustrative UI only — fictional airline, not a real booking or flight system.