Travel — Backpacker / Budget Landing
A high-energy backpacker and budget-travel landing page for a fictional brand, built entirely with CSS gradients and inline SVG instead of photos. A sunset-and-denim hero pairs a layered horizon scene, a dotted route motif and a flying plane with a working trip search and count-up stats. Below it sit a scrolling deals marquee, filterable route cards with mini SVG maps, rated hostel cards, a tilted community photo wall, and a save-to-trip drawer with running totals. Sticker tape accents and playful tilt keep the mood fun and affordable.
MCP
Code
/* Driftpack — Backpacker / Budget Travel Landing
Palette: bright sunset + denim + white, chunky sans, playful tape/tilt accents */
:root {
--bg: #fffaf2;
--paper: #ffffff;
--ink: #16233f; /* deep denim ink */
--muted: #5a6b86;
--denim: #2b4d8f; /* primary denim blue */
--denim-deep: #1c2747;
--sunset: #ff7a3c; /* bright sunset orange */
--coral: #ef5a78; /* hot pink-coral */
--gold: #ffc23d; /* sticker yellow */
--teal: #15a3a3;
--sand: #f6e9d6;
--line: rgba(22, 35, 63, .12);
--line-strong: rgba(22, 35, 63, .22);
--shadow: 0 14px 32px -16px rgba(22, 35, 63, .45);
--shadow-sm: 0 6px 16px -10px rgba(22, 35, 63, .5);
--r: 16px;
--r-lg: 24px;
--maxw: 1140px;
--font-display: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
--font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
}
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: var(--font-body);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
h1, h2, h3 { font-family: var(--font-display); line-height: 1.05; margin: 0; letter-spacing: -.02em; }
a { color: inherit; }
img, svg { display: block; max-width: 100%; }
button { font-family: inherit; }
.wrap { width: min(100% - 2.5rem, var(--maxw)); margin-inline: auto; }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
position: absolute; left: 12px; top: -60px; z-index: 100;
background: var(--denim); color: #fff; padding: .6rem 1rem; border-radius: 10px;
font-weight: 700; text-decoration: none; transition: top .18s;
}
.skip-link:focus { top: 12px; }
:focus-visible {
outline: 3px solid var(--sunset);
outline-offset: 2px;
border-radius: 6px;
}
/* ---------- Buttons ---------- */
.btn {
--b: var(--denim);
display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
font-family: var(--font-display); font-weight: 800; font-size: .95rem;
border: 2px solid transparent; border-radius: 999px; cursor: pointer;
padding: .62rem 1.15rem; text-decoration: none; line-height: 1;
transition: transform .12s ease, box-shadow .15s ease, background .15s ease;
}
.btn:active { transform: translateY(1px) scale(.99); }
.btn--solid { background: var(--sunset); color: #fff; box-shadow: 0 6px 0 -1px #c9491d; }
.btn--solid:hover { background: #ff8a52; }
.btn--solid:active { box-shadow: 0 2px 0 -1px #c9491d; }
.btn--ghost { background: #fff; color: var(--ink); border-color: var(--line-strong); }
.btn--ghost:hover { border-color: var(--denim); color: var(--denim); }
.btn--lg { padding: .82rem 1.5rem; font-size: 1.02rem; }
.saved-count {
display: inline-grid; place-items: center; min-width: 1.35rem; height: 1.35rem;
padding: 0 .35rem; border-radius: 999px; background: var(--coral); color: #fff;
font-size: .78rem; font-weight: 800; line-height: 1;
}
/* ---------- Topbar ---------- */
.topbar {
position: sticky; top: 0; z-index: 50;
background: rgba(255, 250, 242, .9);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--line);
}
.topbar__inner { display: flex; align-items: center; gap: 1rem; min-height: 68px; }
.brand { display: inline-flex; align-items: center; gap: .5rem; text-decoration: none; font-family: var(--font-display); font-weight: 900; font-size: 1.3rem; }
.brand__mark { color: var(--sunset); display: inline-flex; transform: rotate(-8deg); }
.brand__word { letter-spacing: -.03em; }
.nav { display: flex; gap: 1.25rem; margin-left: 1rem; }
.nav a { text-decoration: none; font-weight: 600; color: var(--muted); position: relative; padding: .25rem 0; }
.nav a::after { content: ""; position: absolute; left: 0; bottom: -2px; width: 0; height: 3px; background: var(--sunset); border-radius: 3px; transition: width .18s; }
.nav a:hover { color: var(--ink); }
.nav a:hover::after { width: 100%; }
.topbar__actions { margin-left: auto; display: flex; align-items: center; gap: .6rem; }
.hamburger {
display: none; flex-direction: column; gap: 5px; width: 44px; height: 44px;
align-items: center; justify-content: center; background: #fff;
border: 2px solid var(--line-strong); border-radius: 12px; cursor: pointer; margin-left: auto;
}
.hamburger span:not(.sr-only) { display: block; width: 20px; height: 2.5px; background: var(--ink); border-radius: 3px; transition: transform .2s; }
.hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7.5px) rotate(45deg); }
.hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7.5px) rotate(-45deg); }
.mobile-nav { display: flex; flex-direction: column; padding: .5rem 1.25rem 1rem; gap: .15rem; border-top: 1px solid var(--line); }
.mobile-nav a { padding: .7rem .25rem; text-decoration: none; font-weight: 700; border-bottom: 1px solid var(--line); color: var(--ink); }
.mobile-nav a:last-child { border-bottom: 0; color: var(--sunset); }
/* ---------- Hero ---------- */
.hero { position: relative; overflow: hidden; }
.hero__scene { position: absolute; inset: 0; z-index: 0; }
.hero__sky { width: 100%; height: 100%; }
.hero__plane {
position: absolute; top: 14%; left: -8%; font-size: 2rem;
animation: fly 16s linear infinite;
}
@keyframes fly {
0% { left: -8%; top: 18%; transform: rotate(8deg); }
100% { left: 108%; top: 8%; transform: rotate(8deg); }
}
.hero__inner {
position: relative; z-index: 1;
padding: clamp(2.5rem, 6vw, 5rem) 0 clamp(3.5rem, 8vw, 6rem);
max-width: 720px;
}
.hero h1 {
font-size: clamp(2.4rem, 7vw, 4.6rem); font-weight: 900; color: #fff;
text-shadow: 0 4px 24px rgba(22, 35, 63, .35);
margin-top: 1rem;
}
.hero h1 .hl {
background: var(--gold); color: var(--denim-deep);
padding: 0 .15em; border-radius: 8px; box-decoration-break: clone; -webkit-box-decoration-break: clone;
transform: rotate(-1.2deg); display: inline-block;
}
.hero__lede {
color: #fff; font-size: clamp(1rem, 2.4vw, 1.22rem); max-width: 38ch;
margin: 1.1rem 0 0; font-weight: 500; text-shadow: 0 2px 12px rgba(22, 35, 63, .4);
}
.hero__lede strong { background: rgba(255,255,255,.95); color: var(--coral); padding: 0 .3em; border-radius: 6px; }
.tag {
display: inline-flex; align-items: center; gap: .4rem;
font-family: var(--font-display); font-weight: 800; font-size: .82rem;
padding: .42rem .8rem; border-radius: 8px;
}
.tag--tape {
background: var(--paper); color: var(--ink);
box-shadow: var(--shadow-sm); transform: rotate(-2deg);
position: relative;
}
.tag--tape::before {
content: ""; position: absolute; top: -9px; left: 50%; transform: translateX(-50%) rotate(3deg);
width: 46px; height: 16px; background: rgba(255, 194, 61, .7); border: 1px dashed rgba(0,0,0,.18);
border-radius: 2px;
}
.tag--tilt-r { transform: rotate(2deg); }
.searchbar {
display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1.6rem;
background: var(--paper); padding: .55rem; border-radius: 18px;
box-shadow: var(--shadow); max-width: 560px; border: 2px solid #fff;
}
.searchbar__field {
display: flex; align-items: center; gap: .5rem; flex: 1 1 180px;
background: var(--bg); border-radius: 12px; padding: .2rem .75rem; border: 1.5px solid transparent;
}
.searchbar__field:focus-within { border-color: var(--denim); }
.searchbar__field--budget { flex: 0 1 150px; }
.searchbar__icon { font-size: 1rem; }
.searchbar input, .searchbar select {
border: 0; background: transparent; font: inherit; font-weight: 600; color: var(--ink);
padding: .65rem 0; width: 100%; outline: none;
}
.searchbar select { cursor: pointer; }
.searchbar .btn { flex: 0 0 auto; }
.hero__stats { list-style: none; display: flex; flex-wrap: wrap; gap: 1.8rem; margin: 1.8rem 0 0; padding: 0; }
.hero__stats li { display: flex; flex-direction: column; }
.hero__stats strong { font-family: var(--font-display); font-size: clamp(1.5rem, 4vw, 2rem); font-weight: 900; color: #fff; text-shadow: 0 2px 10px rgba(22,35,63,.4); }
.hero__stats span { color: rgba(255,255,255,.92); font-size: .85rem; font-weight: 600; text-shadow: 0 1px 6px rgba(22,35,63,.4); }
/* ---------- Deals strip ---------- */
.deals { background: var(--denim-deep); overflow: hidden; border-block: 3px solid var(--gold); }
.deals__track {
display: flex; gap: 2.5rem; white-space: nowrap; padding: .8rem 0;
width: max-content; animation: marquee 32s linear infinite;
}
.deals:hover .deals__track, .deals__track:focus-within { animation-play-state: paused; }
.deals__item { color: #fff; font-family: var(--font-display); font-weight: 700; font-size: .95rem; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* ---------- Sections ---------- */
.section { padding: clamp(3rem, 7vw, 5.5rem) 0; }
.section--sand { background: var(--sand); }
.section__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 1rem 2rem; margin-bottom: 1.8rem; }
.kicker { font-family: var(--font-display); font-weight: 800; font-size: .82rem; letter-spacing: .04em; text-transform: uppercase; color: var(--coral); }
.section__head h2 { font-size: clamp(1.8rem, 4.5vw, 2.8rem); font-weight: 900; margin-top: .3rem; }
.section__sub { color: var(--muted); max-width: 42ch; margin: 0; font-weight: 500; }
/* chips */
.chips { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1.6rem; }
.chip {
border: 2px solid var(--line-strong); background: #fff; color: var(--ink);
padding: .5rem 1rem; border-radius: 999px; font-family: var(--font-display);
font-weight: 700; font-size: .9rem; cursor: pointer; transition: all .15s;
}
.chip:hover { border-color: var(--denim); }
.chip.is-active { background: var(--denim); color: #fff; border-color: var(--denim); }
/* ---------- Route cards ---------- */
.routes { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 1.4rem; }
.route {
background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-lg);
overflow: hidden; box-shadow: var(--shadow-sm); display: flex; flex-direction: column;
transition: transform .16s ease, box-shadow .16s ease;
}
.route:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.route__map { position: relative; aspect-ratio: 16 / 10; }
.route__map svg { width: 100%; height: 100%; }
.route__badge {
position: absolute; top: .8rem; left: .8rem; background: var(--gold); color: var(--denim-deep);
font-family: var(--font-display); font-weight: 800; font-size: .76rem; padding: .3rem .65rem;
border-radius: 8px; transform: rotate(-3deg); box-shadow: var(--shadow-sm);
}
.route__price {
position: absolute; bottom: .8rem; right: .8rem; background: #fff; color: var(--denim);
font-family: var(--font-display); font-weight: 900; font-size: 1.05rem; padding: .35rem .7rem;
border-radius: 10px; box-shadow: var(--shadow-sm);
}
.route__body { padding: 1.05rem 1.15rem 1.2rem; display: flex; flex-direction: column; gap: .55rem; flex: 1; }
.route__title { font-family: var(--font-display); font-size: 1.2rem; font-weight: 800; }
.route__meta { display: flex; flex-wrap: wrap; gap: .8rem; color: var(--muted); font-size: .85rem; font-weight: 600; }
.route__meta span { display: inline-flex; gap: .3rem; align-items: center; }
.route__stops { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .1rem; }
.route__stop { background: var(--bg); border: 1px solid var(--line); border-radius: 999px; padding: .2rem .6rem; font-size: .78rem; font-weight: 600; color: var(--ink); }
.route__foot { display: flex; align-items: center; justify-content: space-between; margin-top: auto; padding-top: .6rem; }
.route__rating { font-weight: 700; font-size: .9rem; color: var(--ink); }
.route__rating .star { color: var(--gold); }
/* heart save button */
.heart {
width: 42px; height: 42px; border-radius: 50%; border: 2px solid var(--line-strong);
background: #fff; cursor: pointer; font-size: 1.15rem; line-height: 1;
display: inline-grid; place-items: center; transition: transform .14s, border-color .15s, background .15s;
color: var(--muted);
}
.heart:hover { border-color: var(--coral); transform: scale(1.08); }
.heart[aria-pressed="true"] { background: var(--coral); border-color: var(--coral); color: #fff; }
.heart.pop { animation: pop .35s ease; }
@keyframes pop { 0%{transform:scale(1)} 40%{transform:scale(1.35)} 100%{transform:scale(1)} }
/* ---------- Hostels ---------- */
.hostels { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1.4rem; }
.hostel {
background: var(--paper); border: 1px solid var(--line); border-radius: var(--r);
overflow: hidden; box-shadow: var(--shadow-sm); display: flex; flex-direction: column;
transition: transform .16s ease, box-shadow .16s ease;
}
.hostel:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.hostel__pic { aspect-ratio: 4 / 3; position: relative; display: grid; place-items: center; font-size: 2.6rem; }
.hostel__tag {
position: absolute; top: .7rem; left: .7rem; background: rgba(255,255,255,.95);
font-family: var(--font-display); font-weight: 800; font-size: .72rem; padding: .25rem .55rem;
border-radius: 7px; color: var(--denim);
}
.hostel__heart { position: absolute; top: .6rem; right: .6rem; width: 38px; height: 38px; font-size: 1rem; }
.hostel__body { padding: .9rem 1rem 1.1rem; display: flex; flex-direction: column; gap: .45rem; flex: 1; }
.hostel__name { font-family: var(--font-display); font-weight: 800; font-size: 1.05rem; }
.hostel__loc { color: var(--muted); font-size: .82rem; font-weight: 600; display: flex; align-items: center; gap: .3rem; }
.hostel__foot { display: flex; align-items: baseline; justify-content: space-between; margin-top: auto; padding-top: .5rem; }
.hostel__price { font-family: var(--font-display); font-weight: 900; color: var(--denim); font-size: 1.15rem; }
.hostel__price span { font-size: .72rem; color: var(--muted); font-weight: 600; }
.hostel__rate { font-weight: 700; font-size: .85rem; }
.hostel__rate .star { color: var(--gold); }
/* ---------- Community wall ---------- */
.wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: .9rem; }
.tile {
position: relative; aspect-ratio: 1; border-radius: var(--r); overflow: hidden;
border: 3px solid #fff; box-shadow: var(--shadow-sm); cursor: pointer;
display: grid; place-items: center; font-size: 2.4rem;
transition: transform .16s ease;
}
.tile:nth-child(odd) { transform: rotate(-1.5deg); }
.tile:nth-child(even) { transform: rotate(1.5deg); }
.tile:hover, .tile:focus-visible { transform: rotate(0) scale(1.03); z-index: 2; }
.tile__user { position: absolute; left: .5rem; bottom: .5rem; background: rgba(22,35,63,.78); color: #fff; font-size: .7rem; font-weight: 700; padding: .15rem .45rem; border-radius: 6px; }
.tile__like {
position: absolute; right: .5rem; top: .5rem; background: rgba(255,255,255,.92);
border: 0; border-radius: 999px; padding: .2rem .5rem; font-size: .78rem; font-weight: 800;
cursor: pointer; color: var(--coral); display: inline-flex; gap: .25rem; align-items: center;
}
.tile__like[aria-pressed="true"] { background: var(--coral); color: #fff; }
/* ---------- CTA ---------- */
.cta { background: linear-gradient(135deg, var(--denim) 0%, var(--denim-deep) 60%, var(--coral) 160%); color: #fff; padding: clamp(3rem, 8vw, 5.5rem) 0; }
.cta__inner { max-width: 620px; text-align: center; margin-inline: auto; }
.cta h2 { font-size: clamp(2rem, 5.5vw, 3.2rem); font-weight: 900; margin-top: 1rem; }
.cta__inner p { color: rgba(255,255,255,.92); margin: 1rem auto 0; max-width: 46ch; }
.cta__form { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; margin: 1.6rem 0 0; }
.cta__form input {
flex: 1 1 240px; max-width: 320px; border: 0; border-radius: 999px; padding: .85rem 1.2rem;
font: inherit; font-weight: 600; color: var(--ink); outline: 3px solid transparent;
}
.cta__form input:focus-visible { outline-color: var(--gold); }
.cta__fine { margin-top: 1rem !important; font-size: .85rem; font-weight: 600; }
/* ---------- Footer ---------- */
.footer { background: var(--denim-deep); color: rgba(255,255,255,.85); padding: 2.2rem 0; }
.footer__inner { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 2rem; }
.brand--foot { color: #fff; }
.brand--foot .brand__mark { color: var(--gold); }
.footer__links { display: flex; gap: 1.2rem; flex-wrap: wrap; }
.footer__links a { color: rgba(255,255,255,.8); text-decoration: none; font-weight: 600; font-size: .9rem; }
.footer__links a:hover { color: #fff; }
.footer__note { margin: 0 0 0 auto; font-size: .82rem; opacity: .8; }
/* ---------- Drawer ---------- */
.drawer { position: fixed; inset: 0; z-index: 90; }
.drawer__scrim { position: absolute; inset: 0; background: rgba(22, 35, 63, .5); animation: fade .2s; }
.drawer__panel {
position: absolute; top: 0; right: 0; height: 100%; width: min(400px, 92vw);
background: var(--bg); box-shadow: -20px 0 50px -20px rgba(0,0,0,.5);
display: flex; flex-direction: column; animation: slide .25s ease;
}
@keyframes slide { from { transform: translateX(100%); } to { transform: translateX(0); } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
.drawer__head { display: flex; align-items: center; justify-content: space-between; padding: 1.2rem 1.3rem; border-bottom: 1px solid var(--line); }
.drawer__head h2 { font-size: 1.4rem; font-weight: 900; }
.drawer__x { width: 38px; height: 38px; border-radius: 50%; border: 2px solid var(--line-strong); background: #fff; cursor: pointer; font-size: 1rem; }
.drawer__x:hover { border-color: var(--coral); color: var(--coral); }
.drawer__list { list-style: none; margin: 0; padding: .8rem; overflow-y: auto; flex: 1; display: flex; flex-direction: column; gap: .6rem; }
.drawer__row { display: flex; align-items: center; gap: .8rem; background: #fff; border: 1px solid var(--line); border-radius: 12px; padding: .65rem .8rem; }
.drawer__emoji { font-size: 1.5rem; }
.drawer__info { flex: 1; min-width: 0; }
.drawer__info strong { display: block; font-family: var(--font-display); font-size: .95rem; }
.drawer__info span { color: var(--muted); font-size: .8rem; }
.drawer__price { font-family: var(--font-display); font-weight: 900; color: var(--denim); }
.drawer__remove { background: none; border: 0; cursor: pointer; color: var(--muted); font-size: 1.1rem; padding: .2rem; }
.drawer__remove:hover { color: var(--coral); }
.drawer__empty { padding: 2rem 1.5rem; text-align: center; color: var(--muted); font-weight: 500; }
.drawer__foot { border-top: 1px solid var(--line); padding: 1rem 1.3rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.drawer__total span { font-size: .8rem; color: var(--muted); font-weight: 600; display: block; }
.drawer__total strong { font-family: var(--font-display); font-size: 1.5rem; font-weight: 900; color: var(--ink); }
/* ---------- Toast ---------- */
.toast {
position: fixed; left: 50%; bottom: 24px; transform: translate(-50%, 24px);
background: var(--denim-deep); color: #fff; padding: .8rem 1.2rem; border-radius: 999px;
font-weight: 700; box-shadow: var(--shadow); opacity: 0; pointer-events: none;
transition: opacity .25s, transform .25s; z-index: 120; max-width: 90vw; text-align: center;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ---------- Responsive ---------- */
@media (max-width: 860px) {
.nav { display: none; }
.topbar__actions { display: none; }
.hamburger { display: flex; }
.topbar__inner { gap: .5rem; }
}
@media (max-width: 560px) {
.searchbar { padding: .5rem; }
.searchbar .btn { flex: 1 1 100%; }
.hero__stats { gap: 1.2rem; }
.section__head { flex-direction: column; align-items: flex-start; }
.footer__note { margin: .4rem 0 0; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}/* Driftpack — Backpacker / Budget Travel Landing
Vanilla JS only. Renders routes/hostels/wall, save-to-trip drawer, filters,
search, animated counters, marquee clone, join form, toast. */
(function () {
"use strict";
/* ---------- tiny helpers ---------- */
var $ = function (s, r) { return (r || document).querySelector(s); };
var $$ = function (s, r) { return Array.prototype.slice.call((r || document).querySelectorAll(s)); };
var toastEl = $("[data-toast]");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toastEl.classList.remove("show"); }, 2400);
}
function esc(s) {
return String(s).replace(/[&<>"']/g, function (c) {
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c];
});
}
/* simple inline SVG mini-map with a dotted route + pins */
function miniMap(seed, color) {
var pts = [];
var n = 4;
var rnd = seed;
function next() { rnd = (rnd * 9301 + 49297) % 233280; return rnd / 233280; }
for (var i = 0; i < n; i++) {
pts.push([20 + (next() * 0.7 + i / n * 0.25) * 280, 30 + next() * 110]);
}
var path = "M" + pts.map(function (p) { return p[0].toFixed(0) + " " + p[1].toFixed(0); }).join(" L");
var pins = pts.map(function (p, i) {
var fill = i === 0 || i === pts.length - 1 ? "#ffffff" : color;
return '<circle cx="' + p[0].toFixed(0) + '" cy="' + p[1].toFixed(0) + '" r="6" fill="' + fill + '" stroke="' + color + '" stroke-width="3"/>';
}).join("");
return '<svg viewBox="0 0 320 180" preserveAspectRatio="none" role="img" aria-label="Route map">' +
'<defs><linearGradient id="mg' + seed + '" x1="0" y1="0" x2="1" y2="1">' +
'<stop offset="0" stop-color="' + color + '" stop-opacity="0.18"/>' +
'<stop offset="1" stop-color="' + color + '" stop-opacity="0.05"/></linearGradient></defs>' +
'<rect width="320" height="180" fill="url(#mg' + seed + ')"/>' +
'<g opacity="0.18" stroke="' + color + '" stroke-width="1">' +
'<path d="M0 60 H320 M0 120 H320 M80 0 V180 M160 0 V180 M240 0 V180"/></g>' +
'<path d="' + path + '" fill="none" stroke="' + color + '" stroke-width="3.5" stroke-linecap="round" stroke-dasharray="2 12"/>' +
pins + "</svg>";
}
/* ---------- data ---------- */
var ROUTES = [
{ id: "r1", region: "asia", title: "Banana Pancake Trail", badge: "Classic", price: 28, days: 21, stops: ["Bangkok", "Chiang Mai", "Luang Prabang", "Hanoi"], rating: 4.8, color: "#15a3a3" },
{ id: "r2", region: "europe", title: "Iberian Sun Run", badge: "Cheap eats", price: 41, days: 14, stops: ["Lisbon", "Sevilla", "Granada", "Valencia"], rating: 4.7, color: "#ff7a3c" },
{ id: "r3", region: "latam", title: "Andes Gringo Loop", badge: "Epic views", price: 33, days: 18, stops: ["Cusco", "La Paz", "Uyuni", "Atacama"], rating: 4.9, color: "#ef5a78" },
{ id: "r4", region: "asia", title: "Island Hopper Lite", badge: "Beachy", price: 36, days: 12, stops: ["Bali", "Gili T", "Lombok", "Flores"], rating: 4.6, color: "#2b4d8f" },
{ id: "r5", region: "europe", title: "Balkan Bargain Belt", badge: "Underrated", price: 26, days: 16, stops: ["Split", "Mostar", "Kotor", "Tirana"], rating: 4.8, color: "#7b5cff" },
{ id: "r6", region: "latam", title: "Caribbean Coast Crawl", badge: "Warm", price: 30, days: 15, stops: ["Cartagena", "Santa Marta", "Tayrona", "Palomino"], rating: 4.5, color: "#15a3a3" }
];
var HOSTELS = [
{ id: "h1", name: "The Surf Shack", loc: "Lisbon, PT", price: 16, rating: 4.7, tag: "Free breakfast", emoji: "🏄" },
{ id: "h2", name: "Mango Tree Dorms", loc: "Chiang Mai, TH", price: 8, rating: 4.9, tag: "Rooftop bar", emoji: "🌴" },
{ id: "h3", name: "Condor Nest", loc: "Cusco, PE", price: 11, rating: 4.6, tag: "Mountain view", emoji: "🏔️" },
{ id: "h4", name: "Blue Door Hostel", loc: "Kotor, ME", price: 14, rating: 4.8, tag: "Old town", emoji: "🚪" },
{ id: "h5", name: "Gecko Garden", loc: "Gili T, ID", price: 12, rating: 4.5, tag: "Hammocks", emoji: "🦎" },
{ id: "h6", name: "Salt & Sand", loc: "Palomino, CO", price: 10, rating: 4.4, tag: "Beachfront", emoji: "🏝️" }
];
var WALL = [
{ emoji: "🏞️", user: "@maya.roams", likes: 213 },
{ emoji: "🛶", user: "@nomad_finn", likes: 98 },
{ emoji: "🌋", user: "@trekzoe", likes: 341 },
{ emoji: "🐘", user: "@goeswest", likes: 156 },
{ emoji: "🏜️", user: "@dune.diary", likes: 77 },
{ emoji: "🚲", user: "@pedalpaolo", likes: 122 },
{ emoji: "⛺", user: "@tentlife", likes: 64 },
{ emoji: "🌅", user: "@sunchaser", likes: 289 }
];
/* ---------- trip state ---------- */
var STORE_KEY = "driftpack-trip";
var trip = loadTrip();
function loadTrip() {
try { return JSON.parse(localStorage.getItem(STORE_KEY)) || {}; }
catch (e) { return {}; }
}
function saveTrip() {
try { localStorage.setItem(STORE_KEY, JSON.stringify(trip)); } catch (e) { /* private mode */ }
}
function inTrip(id) { return Object.prototype.hasOwnProperty.call(trip, id); }
function findItem(id) {
var r = ROUTES.filter(function (x) { return x.id === id; })[0];
if (r) return { id: r.id, name: r.title, sub: r.days + "-day route", price: r.price, emoji: "🧭" };
var h = HOSTELS.filter(function (x) { return x.id === id; })[0];
if (h) return { id: h.id, name: h.name, sub: h.loc, price: h.price, emoji: h.emoji };
return null;
}
function toggleSave(id) {
if (inTrip(id)) { delete trip[id]; }
else {
var item = findItem(id);
if (item) trip[id] = item;
}
saveTrip();
syncHearts();
renderDrawer();
}
function syncHearts() {
$$("[data-save]").forEach(function (btn) {
var on = inTrip(btn.getAttribute("data-save"));
btn.setAttribute("aria-pressed", on ? "true" : "false");
btn.textContent = on ? "❤" : "♡";
});
var count = Object.keys(trip).length;
$$("[data-saved-count]").forEach(function (el) { el.textContent = count; });
}
/* ---------- render routes ---------- */
var routesEl = $("[data-routes]");
function renderRoutes(filter) {
if (!routesEl) return;
var list = filter && filter !== "all" ? ROUTES.filter(function (r) { return r.region === filter; }) : ROUTES;
routesEl.innerHTML = list.map(function (r, i) {
return '' +
'<article class="route">' +
'<div class="route__map">' +
miniMap(i + 7, r.color) +
'<span class="route__badge">' + esc(r.badge) + "</span>" +
'<span class="route__price">$' + r.price + "/day</span>" +
"</div>" +
'<div class="route__body">' +
'<h3 class="route__title">' + esc(r.title) + "</h3>" +
'<div class="route__meta">' +
"<span>🗓️ " + r.days + " days</span>" +
"<span>📍 " + r.stops.length + " stops</span>" +
"</div>" +
'<div class="route__stops">' +
r.stops.map(function (s) { return '<span class="route__stop">' + esc(s) + "</span>"; }).join("") +
"</div>" +
'<div class="route__foot">' +
'<span class="route__rating"><span class="star">★</span> ' + r.rating.toFixed(1) + "</span>" +
'<button class="heart" type="button" data-save="' + r.id + '" aria-pressed="false" aria-label="Add ' + esc(r.title) + ' to trip">♡</button>' +
"</div>" +
"</div>" +
"</article>";
}).join("");
syncHearts();
}
/* ---------- render hostels ---------- */
var hostelsEl = $("[data-hostels]");
function renderHostels() {
if (!hostelsEl) return;
var grads = [
"linear-gradient(135deg,#ffd3a5,#fd9a6e)",
"linear-gradient(135deg,#a1ffce,#5ad6c0)",
"linear-gradient(135deg,#c2e0ff,#7aa6e8)",
"linear-gradient(135deg,#ffc1d8,#ef5a78)",
"linear-gradient(135deg,#e2d1ff,#9d7bff)",
"linear-gradient(135deg,#ffe8a3,#ffc23d)"
];
hostelsEl.innerHTML = HOSTELS.map(function (h, i) {
return '' +
'<article class="hostel">' +
'<div class="hostel__pic" style="background:' + grads[i % grads.length] + '">' +
"<span>" + h.emoji + "</span>" +
'<span class="hostel__tag">' + esc(h.tag) + "</span>" +
'<button class="heart hostel__heart" type="button" data-save="' + h.id + '" aria-pressed="false" aria-label="Add ' + esc(h.name) + ' to trip">♡</button>' +
"</div>" +
'<div class="hostel__body">' +
'<h3 class="hostel__name">' + esc(h.name) + "</h3>" +
'<span class="hostel__loc">📍 ' + esc(h.loc) + "</span>" +
'<div class="hostel__foot">' +
'<span class="hostel__price">$' + h.price + ' <span>/ night</span></span>' +
'<span class="hostel__rate"><span class="star">★</span> ' + h.rating.toFixed(1) + "</span>" +
"</div>" +
"</div>" +
"</article>";
}).join("");
syncHearts();
}
/* ---------- render wall ---------- */
var wallEl = $("[data-wall]");
function renderWall() {
if (!wallEl) return;
var grads = [
"linear-gradient(135deg,#2b4d8f,#15a3a3)",
"linear-gradient(135deg,#ff7a3c,#ef5a78)",
"linear-gradient(135deg,#ffc23d,#ff7a3c)",
"linear-gradient(135deg,#15a3a3,#2b4d8f)",
"linear-gradient(135deg,#ef5a78,#9d7bff)",
"linear-gradient(135deg,#7aa6e8,#2b4d8f)",
"linear-gradient(135deg,#5ad6c0,#15a3a3)",
"linear-gradient(135deg,#ffd3a5,#ef5a78)"
];
wallEl.innerHTML = WALL.map(function (w, i) {
return '' +
'<button class="tile" type="button" style="background:' + grads[i % grads.length] + '" aria-label="Photo by ' + esc(w.user) + '">' +
"<span>" + w.emoji + "</span>" +
'<span class="tile__like" data-like aria-pressed="false">❤ <b>' + w.likes + "</b></span>" +
'<span class="tile__user">' + esc(w.user) + "</span>" +
"</button>";
}).join("");
}
/* ---------- drawer ---------- */
var drawer = $("[data-drawer]");
var drawerList = $("[data-drawer-list]");
var drawerEmpty = $("[data-drawer-empty]");
var drawerTotal = $("[data-drawer-total]");
var lastFocused = null;
function renderDrawer() {
if (!drawerList) return;
var ids = Object.keys(trip);
if (!ids.length) {
drawerList.innerHTML = "";
if (drawerEmpty) drawerEmpty.style.display = "block";
} else {
if (drawerEmpty) drawerEmpty.style.display = "none";
drawerList.innerHTML = ids.map(function (id) {
var it = trip[id];
return '' +
'<li class="drawer__row">' +
'<span class="drawer__emoji" aria-hidden="true">' + (it.emoji || "📍") + "</span>" +
'<span class="drawer__info"><strong>' + esc(it.name) + "</strong><span>" + esc(it.sub || "") + "</span></span>" +
'<span class="drawer__price">$' + it.price + "</span>" +
'<button class="drawer__remove" type="button" data-remove="' + esc(id) + '" aria-label="Remove ' + esc(it.name) + '">✕</button>' +
"</li>";
}).join("");
}
var total = ids.reduce(function (sum, id) { return sum + (trip[id].price || 0); }, 0);
if (drawerTotal) drawerTotal.textContent = "$" + total;
}
function openDrawer() {
if (!drawer) return;
lastFocused = document.activeElement;
drawer.hidden = false;
document.body.style.overflow = "hidden";
var x = $(".drawer__x", drawer);
if (x) x.focus();
document.addEventListener("keydown", onDrawerKey);
}
function closeDrawer() {
if (!drawer) return;
drawer.hidden = true;
document.body.style.overflow = "";
document.removeEventListener("keydown", onDrawerKey);
if (lastFocused && lastFocused.focus) lastFocused.focus();
}
function onDrawerKey(e) {
if (e.key === "Escape") closeDrawer();
if (e.key === "Tab" && !drawer.hidden) {
var f = $$("button, a, input", drawer).filter(function (el) { return el.offsetParent !== null; });
if (!f.length) return;
var first = f[0], last = f[f.length - 1];
if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
}
}
/* ---------- counters ---------- */
function animateCounters() {
$$("[data-count]").forEach(function (el) {
var target = parseInt(el.getAttribute("data-count"), 10) || 0;
var prefix = el.getAttribute("data-prefix") || "";
var start = null, dur = 1100;
function step(ts) {
if (start === null) start = ts;
var p = Math.min((ts - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = prefix + Math.round(eased * target);
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
});
}
/* ---------- marquee: clone for seamless loop ---------- */
(function setupMarquee() {
var track = $("[data-deals-track]");
if (!track) return;
track.innerHTML += track.innerHTML; // duplicate set for -50% translate loop
})();
/* ---------- wire global events (delegation) ---------- */
document.addEventListener("click", function (e) {
var saveBtn = e.target.closest("[data-save]");
if (saveBtn) {
var id = saveBtn.getAttribute("data-save");
var wasIn = inTrip(id);
toggleSave(id);
saveBtn.classList.remove("pop");
void saveBtn.offsetWidth;
saveBtn.classList.add("pop");
var item = findItem(id);
toast(wasIn ? "Removed from your trip" : "Added " + (item ? item.name : "") + " to your trip 🎒");
return;
}
var likeBtn = e.target.closest("[data-like]");
if (likeBtn) {
e.preventDefault();
var on = likeBtn.getAttribute("aria-pressed") === "true";
var b = likeBtn.querySelector("b");
var num = parseInt(b.textContent, 10) || 0;
likeBtn.setAttribute("aria-pressed", on ? "false" : "true");
b.textContent = on ? num - 1 : num + 1;
return;
}
if (e.target.closest("[data-open-saved]")) { openDrawer(); return; }
if (e.target.closest("[data-drawer-close]")) { closeDrawer(); return; }
var rm = e.target.closest("[data-remove]");
if (rm) {
var rid = rm.getAttribute("data-remove");
delete trip[rid];
saveTrip(); syncHearts(); renderDrawer();
toast("Removed from your trip");
return;
}
if (e.target.closest("[data-clear-trip]")) {
if (!Object.keys(trip).length) { toast("Your trip is already empty"); return; }
trip = {};
saveTrip(); syncHearts(); renderDrawer();
toast("Trip cleared — fresh start ✨");
return;
}
});
/* filter chips */
$$("[data-filter]").forEach(function (chip) {
chip.addEventListener("click", function () {
$$("[data-filter]").forEach(function (c) { c.classList.remove("is-active"); c.setAttribute("aria-pressed", "false"); });
chip.classList.add("is-active");
chip.setAttribute("aria-pressed", "true");
renderRoutes(chip.getAttribute("data-filter"));
});
});
/* search */
var searchForm = $("[data-search]");
if (searchForm) {
searchForm.addEventListener("submit", function (e) {
e.preventDefault();
var where = (searchForm.where.value || "").trim();
var budget = searchForm.budget.value;
if (where) {
toast('Searching trips to "' + where + '" under $' + budget + "/day…");
var routesSection = $("#routes");
if (routesSection) routesSection.scrollIntoView({ behavior: "smooth" });
} else {
toast("Tell us where you're headed 🌍");
var input = searchForm.querySelector('input[name="where"]');
if (input) input.focus();
}
});
}
/* join form */
var joinForm = $("[data-join]");
var joinMsg = $("[data-join-msg]");
if (joinForm) {
joinForm.addEventListener("submit", function (e) {
e.preventDefault();
var email = (joinForm.email.value || "").trim();
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
toast("Pop in a valid email to get the kit");
joinForm.email.focus();
return;
}
joinForm.reset();
if (joinMsg) joinMsg.textContent = "Boom — starter kit is flying to your inbox ✈️";
toast("You're in! Check your inbox 🎒");
});
}
/* hamburger */
var hamburger = $("[data-hamburger]");
var mobileNav = $("[data-mobile-nav]");
if (hamburger && mobileNav) {
hamburger.addEventListener("click", function () {
var open = hamburger.getAttribute("aria-expanded") === "true";
hamburger.setAttribute("aria-expanded", open ? "false" : "true");
mobileNav.hidden = open;
});
mobileNav.addEventListener("click", function (e) {
if (e.target.tagName === "A") { hamburger.setAttribute("aria-expanded", "false"); mobileNav.hidden = true; }
});
}
/* ---------- boot ---------- */
renderRoutes("all");
renderHostels();
renderWall();
renderDrawer();
syncHearts();
if ("IntersectionObserver" in window) {
var hero = $(".hero__stats");
if (hero) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { animateCounters(); io.disconnect(); }
});
}, { threshold: 0.4 });
io.observe(hero);
} else { animateCounters(); }
} else {
animateCounters();
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Driftpack — Budget Adventures Around the Globe</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=Archivo:wght@500;600;700;800;900&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<header class="topbar" role="banner">
<div class="wrap topbar__inner">
<a class="brand" href="#" aria-label="Driftpack home">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 32 32" width="28" height="28" focusable="false" aria-hidden="true">
<path d="M16 3l3 6 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z" fill="currentColor"/>
</svg>
</span>
<span class="brand__word">Driftpack</span>
</a>
<nav class="nav" aria-label="Primary">
<a href="#routes">Routes</a>
<a href="#hostels">Stays</a>
<a href="#wall">Community</a>
<a href="#deals">Deals</a>
</nav>
<div class="topbar__actions">
<button class="btn btn--ghost" type="button" data-open-saved>
<span aria-hidden="true">❤</span>
<span>Trip</span>
<span class="saved-count" data-saved-count aria-live="polite">0</span>
</button>
<a class="btn btn--solid" href="#join">Join free</a>
</div>
<button class="hamburger" type="button" aria-expanded="false" aria-controls="mobile-nav" data-hamburger>
<span></span><span></span><span></span>
<span class="sr-only">Toggle menu</span>
</button>
</div>
<div class="mobile-nav" id="mobile-nav" data-mobile-nav hidden>
<a href="#routes">Routes</a>
<a href="#hostels">Stays</a>
<a href="#wall">Community</a>
<a href="#deals">Deals</a>
<a href="#join">Join free</a>
</div>
</header>
<main id="main">
<!-- HERO -->
<section class="hero" aria-labelledby="hero-title">
<div class="hero__scene" aria-hidden="true">
<svg class="hero__sky" viewBox="0 0 1200 600" preserveAspectRatio="xMidYMax slice" focusable="false">
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ffd166"/>
<stop offset="0.45" stop-color="#ff8c5a"/>
<stop offset="1" stop-color="#ef5a78"/>
</linearGradient>
<linearGradient id="hill1" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#2b3a67"/>
<stop offset="1" stop-color="#1c2747"/>
</linearGradient>
<linearGradient id="hill2" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#3a4d8f"/>
<stop offset="1" stop-color="#2b3a67"/>
</linearGradient>
</defs>
<rect width="1200" height="600" fill="url(#sky)"/>
<circle cx="880" cy="170" r="92" fill="#fff3d6" opacity="0.92"/>
<path d="M0 470 Q200 360 420 430 T840 410 T1200 450 V600 H0 Z" fill="url(#hill2)" opacity="0.85"/>
<path d="M0 520 L160 380 L300 500 L470 350 L640 510 L820 390 L1000 520 L1200 430 V600 H0 Z" fill="url(#hill1)"/>
<g stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-dasharray="2 22" fill="none" opacity="0.9">
<path d="M60 540 C 300 470, 500 560, 720 470 S 1080 430, 1160 360"/>
</g>
<circle cx="60" cy="540" r="9" fill="#fff"/>
<circle cx="1160" cy="360" r="9" fill="#fff"/>
</svg>
<span class="hero__plane" aria-hidden="true">✈️</span>
</div>
<div class="wrap hero__inner">
<span class="tag tag--tape">🎒 Real backpacker prices · no upsells</span>
<h1 id="hero-title">Wander further on a<br><span class="hl">shoestring budget.</span></h1>
<p class="hero__lede">Hand-picked hostel routes, night-bus hacks and street-food maps for travellers who count coins, not stars. Trips from <strong>$19/day</strong> — pack light, go far.</p>
<form class="searchbar" data-search aria-label="Find a route">
<label class="searchbar__field">
<span class="sr-only">Where to?</span>
<span class="searchbar__icon" aria-hidden="true">📍</span>
<input type="text" name="where" placeholder="Where to? e.g. Lisbon, Hanoi…" autocomplete="off" />
</label>
<label class="searchbar__field searchbar__field--budget">
<span class="sr-only">Daily budget</span>
<span class="searchbar__icon" aria-hidden="true">💸</span>
<select name="budget">
<option value="25">$25 / day</option>
<option value="40" selected>$40 / day</option>
<option value="60">$60 / day</option>
<option value="90">$90 / day</option>
</select>
</label>
<button class="btn btn--solid btn--lg" type="submit">Find trips</button>
</form>
<ul class="hero__stats" aria-label="Highlights">
<li><strong data-count="312">0</strong><span>routes mapped</span></li>
<li><strong data-count="48">0</strong><span>countries</span></li>
<li><strong data-count="19" data-prefix="$">0</strong><span>cheapest day</span></li>
</ul>
</div>
</section>
<!-- DEALS STRIP -->
<section class="deals" id="deals" aria-label="Flash deals">
<div class="deals__track" data-deals-track>
<span class="deals__item">🔥 Bangkok ↔ Chiang Mai night train · $11</span>
<span class="deals__item">⛺ Lisbon surf hostel · 5 nights $74</span>
<span class="deals__item">🚌 Cusco → Machu Pueblo combo · $39</span>
<span class="deals__item">🍜 Hanoi street-food crawl · $6</span>
<span class="deals__item">🎟️ Interrail 7-day flexi · $212</span>
<span class="deals__item">🏝️ Gili ferry + dorm · $28</span>
</div>
</section>
<!-- ROUTES -->
<section class="section" id="routes" aria-labelledby="routes-title">
<div class="wrap">
<div class="section__head">
<div>
<span class="kicker">Pick a line on the map</span>
<h2 id="routes-title">Legendary budget routes</h2>
</div>
<p class="section__sub">Dotted trails stitched together by travellers who did it for cheap. Filter by your vibe.</p>
</div>
<div class="chips" role="group" aria-label="Filter routes by region">
<button class="chip is-active" type="button" data-filter="all" aria-pressed="true">All</button>
<button class="chip" type="button" data-filter="asia" aria-pressed="false">SE Asia</button>
<button class="chip" type="button" data-filter="europe" aria-pressed="false">Europe</button>
<button class="chip" type="button" data-filter="latam" aria-pressed="false">Latin America</button>
</div>
<div class="routes" data-routes>
<!-- cards injected by JS -->
</div>
</div>
</section>
<!-- HOSTELS -->
<section class="section section--sand" id="hostels" aria-labelledby="hostels-title">
<div class="wrap">
<div class="section__head">
<div>
<span class="kicker">Crash for cheap</span>
<h2 id="hostels-title">Hostels travellers rate</h2>
</div>
<p class="section__sub">Dorm beds, free breakfast and rooftop hangs. Tap the heart to drop one in your trip.</p>
</div>
<div class="hostels" data-hostels>
<!-- cards injected by JS -->
</div>
</div>
</section>
<!-- COMMUNITY WALL -->
<section class="section" id="wall" aria-labelledby="wall-title">
<div class="wrap">
<div class="section__head">
<div>
<span class="kicker">#packedlight</span>
<h2 id="wall-title">The community photo wall</h2>
</div>
<p class="section__sub">Snaps from the road, posted by Driftpackers. Give one some love.</p>
</div>
<div class="wall" data-wall>
<!-- tiles injected by JS -->
</div>
</div>
</section>
<!-- CTA -->
<section class="cta" id="join" aria-labelledby="cta-title">
<div class="wrap cta__inner">
<span class="tag tag--tape tag--tilt-r">✦ Free forever · no card</span>
<h2 id="cta-title">Grab your pack. Plan the trip.</h2>
<p>Save routes, build an itinerary, and split costs with your crew. Drop your email and we'll send the budget-travel starter kit.</p>
<form class="cta__form" data-join aria-label="Join Driftpack">
<label class="sr-only" for="join-email">Email address</label>
<input id="join-email" type="email" name="email" placeholder="[email protected]" required />
<button class="btn btn--solid btn--lg" type="submit">Send my kit</button>
</form>
<p class="cta__fine" data-join-msg aria-live="polite">Used by 240k+ shoestring travellers.</p>
</div>
</section>
</main>
<footer class="footer" role="contentinfo">
<div class="wrap footer__inner">
<div class="brand brand--foot">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 32 32" width="22" height="22" aria-hidden="true"><path d="M16 3l3 6 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z" fill="currentColor"/></svg>
</span>
<span class="brand__word">Driftpack</span>
</div>
<nav class="footer__links" aria-label="Footer">
<a href="#routes">Routes</a>
<a href="#hostels">Stays</a>
<a href="#wall">Community</a>
<a href="#deals">Deals</a>
</nav>
<p class="footer__note">Fictional demo. Pack light, tip well. © 2026 Driftpack.</p>
</div>
</footer>
<!-- SAVED / TRIP DRAWER -->
<div class="drawer" data-drawer hidden>
<div class="drawer__scrim" data-drawer-close></div>
<aside class="drawer__panel" role="dialog" aria-modal="true" aria-labelledby="drawer-title">
<header class="drawer__head">
<h2 id="drawer-title">Your trip 🎒</h2>
<button class="drawer__x" type="button" data-drawer-close aria-label="Close trip">✕</button>
</header>
<ul class="drawer__list" data-drawer-list></ul>
<p class="drawer__empty" data-drawer-empty>Nothing saved yet — tap a heart to start your trip.</p>
<footer class="drawer__foot">
<div class="drawer__total"><span>Est. /day</span><strong data-drawer-total>$0</strong></div>
<button class="btn btn--solid" type="button" data-clear-trip>Clear trip</button>
</footer>
</aside>
</div>
<div class="toast" data-toast role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Backpacker / Budget Landing
Driftpack is a bright, youthful landing page for shoestring travellers. The hero leans on a sunset-to-coral CSS gradient sky, a layered SVG mountain horizon, an animated dotted route line and a looping plane — no images anywhere. A chunky headline, taped sticker badge and live count-up stats set the tone, while a working search bar lets you type a destination and pick a daily budget before scrolling you down to the routes.
The page is fully interactive. A scrolling deals strip marquee pauses on hover, region chips filter the route cards in place, and every route card renders its own seeded inline-SVG mini map with dotted trail and pins. Heart buttons on routes and hostels add items to a slide-in trip drawer that tracks an estimated per-day total and persists to localStorage, and the community photo wall lets you like tilted gradient tiles. Counters animate on scroll, the email capture validates inline, and a mobile hamburger nav, focus-visible rings, ARIA labels and a reduced-motion fallback round out the polish.
Everything is vanilla HTML, CSS and JavaScript with a single Google Fonts link — drop the three snippets together and it runs with no build step.
Illustrative travel UI only — fictional destinations, prices, and maps.