旅行预订
旅行预订页面,包含目的地搜索、标签筛选、价格展示和收藏功能。
完整模板源代码
查看 1588 行代码
"use client";
export const dynamic = "force-static";
import { useState } from "react";
import {
ArrowRight,
ArrowUpRight,
BadgeCheck,
Calendar,
Check,
ChevronRight,
Clock,
Compass,
Heart,
Mail,
MapPin,
Menu,
Plane,
Quote,
Route,
Search,
Send,
Star,
Users,
X,
} from "@/components/IconShim";
import {
Fraunces,
Space_Mono,
Work_Sans,
} from "@/components/templates/FontShim";
import { TemplateBackLink } from "@/components/templates/TemplateBackLink";
const display = Fraunces({ subsets: ["latin"], style: ["normal", "italic"] });
const sans = Work_Sans({ subsets: ["latin"] });
const mono = Space_Mono({ weight: ["400", "700"], subsets: ["latin"] });
/* ------------------------------------------------------------------ */
/* Content */
/* ------------------------------------------------------------------ */
const NAV_LINKS = ["Destinations", "Journeys", "The Journal", "About"];
const HERO_BODY =
"Wayfare is a booking house run by travel editors. We sleep in the rooms, walk the routes, and hand you the trip already solved -- flights, stays, and the small print folded into one honest price.";
const HERO_TRUST = ["No booking fees", "Free cancellation", "24/7 human desk"];
const TICKER = [
"Kyoto",
"Merzouga",
"Reykjavik",
"Amalfi Coast",
"Jaipur",
"Banff",
"Lisbon",
"Hakone",
];
type SceneKind =
"kyoto" | "sahara" | "reykjavik" | "amalfi" | "jaipur" | "banff";
interface Destination {
scene: SceneKind;
place: string;
country: string;
stay: string;
coords: string;
price: number;
rating: number;
reviews: number;
tags: string[];
blurb: string;
}
const DESTINATIONS: Destination[] = [
{
scene: "kyoto",
place: "Kyoto",
country: "Japan",
stay: "Tawaraya Ryokan",
coords: "35.01 N / 135.77 E",
price: 312,
rating: 4.9,
reviews: 218,
tags: ["Ryokan", "Onsen", "Garden"],
blurb: "Tatami mornings and a private garden the colour of moss and rain.",
},
{
scene: "sahara",
place: "Merzouga",
country: "Morocco",
stay: "Erg Chebbi Camp",
coords: "31.10 N / 4.01 W",
price: 189,
rating: 4.8,
reviews: 164,
tags: ["Desert camp", "Stargazing", "Full board"],
blurb: "A tented suite at the foot of the dunes, silent but for the wind.",
},
{
scene: "reykjavik",
place: "Reykjavik",
country: "Iceland",
stay: "Hotel Borealis",
coords: "64.15 N / 21.94 W",
price: 268,
rating: 4.7,
reviews: 302,
tags: ["Aurora", "Geothermal", "Design"],
blurb: "Glass angled straight at the aurora, a hot spring waiting below.",
},
{
scene: "amalfi",
place: "Amalfi",
country: "Italy",
stay: "Villa Lucia",
coords: "40.63 N / 14.60 E",
price: 395,
rating: 4.9,
reviews: 276,
tags: ["Sea view", "Terrace", "Breakfast"],
blurb: "Lemon terraces stacked above a sea that refuses to be one blue.",
},
{
scene: "jaipur",
place: "Jaipur",
country: "India",
stay: "Rambagh Haveli",
coords: "26.91 N / 75.79 E",
price: 224,
rating: 4.8,
reviews: 189,
tags: ["Palace", "Courtyard", "Pool"],
blurb: "A restored haveli of pink sandstone, courtyards cool as dusk.",
},
{
scene: "banff",
place: "Banff",
country: "Canada",
stay: "Moraine Lodge",
coords: "51.18 N / 115.57 W",
price: 342,
rating: 4.7,
reviews: 241,
tags: ["Alpine", "Lake", "Fireplace"],
blurb: "Timber and glass on a glacier lake, pines right to the waterline.",
},
];
interface RouteCity {
name: string;
x: number;
y: number;
arc: string;
}
const HUB = { name: "London", x: 360, y: 150 };
const ROUTE_CITIES: RouteCity[] = [
{ name: "Reykjavik", x: 330, y: 92, arc: "M360,150 Q322,74 330,92" },
{ name: "Banff", x: 150, y: 132, arc: "M360,150 Q240,86 150,132" },
{ name: "Marrakech", x: 348, y: 252, arc: "M360,150 Q340,208 348,252" },
{ name: "Amalfi", x: 432, y: 182, arc: "M360,150 Q404,148 432,182" },
{ name: "Jaipur", x: 598, y: 216, arc: "M360,150 Q492,118 598,216" },
{ name: "Kyoto", x: 690, y: 178, arc: "M360,150 Q540,92 690,178" },
];
const WHY = [
{
icon: Compass,
label: "We go first",
body: "Every stay is booked, slept in, and written up by our own editors before it ever reaches your screen. No stock photos, no surprises at check-in.",
},
{
icon: BadgeCheck,
label: "One honest price",
body: "The nightly rate is the rate. Taxes, the cleaning charge, the resort fee that is not really a fee -- all folded in and shown before you commit a cent.",
},
{
icon: Route,
label: "The whole thread",
body: "Rooms, trains, the guide who knows the back roads. We book the journey end to end so the day you land is a day you enjoy, not a day you spend fixing.",
},
];
const SPOTLIGHT = {
kicker: "Guided journey",
title: "The Nakasendo, on foot",
lede: "Six days along the old post road through the Kiso Valley -- cedar forest, wooden inns, and the slower Japan the bullet trains forgot.",
price: 2480,
duration: "6 days",
group: "Max 8 walkers",
season: "May to November",
days: [
{
day: "Day 01",
title: "Arrive Magome",
body: "Meet at dusk in the stone-paved hill town. A kaiseki dinner at the inn and an early night before the trail begins.",
},
{
day: "Day 02",
title: "Magome to Tsumago",
body: "The classic pass through cedar and waterfalls -- eight kilometres on the old cobbled road, ringing the bear bells as you climb.",
},
{
day: "Day 03",
title: "Tsumago to Nojiri",
body: "A quieter trail between tea houses and rice terraces, ending at a family-run minshuku with a deep cypress bath.",
},
{
day: "Day 04",
title: "Kiso-Fukushima",
body: "The old barrier town and its onsen. An afternoon free to wander the sake cellars and the steep riverside lanes.",
},
{
day: "Day 05",
title: "Narai-juku",
body: "The longest of the post towns, a full kilometre of dark timber. Lacquerware workshops and lantern-lit evenings.",
},
{
day: "Day 06",
title: "On to Matsumoto",
body: "A local line down to the black crow castle and a farewell lunch of cold soba before the group parts ways.",
},
],
};
const TESTIMONIALS = [
{
quote:
"I have booked a lot of trips. This is the first time the itinerary read like it was written by a friend who actually lived there -- and every single reservation simply worked.",
name: "Marisol Vantieghem",
trip: "Amalfi Coast, in May",
},
{
quote:
"They put us in inns I could never have found, with hosts who waited up past midnight. The walking notes were precise down to the turn at the broken torii.",
name: "Theo Okonkwo",
trip: "Kiso Valley, last autumn",
},
{
quote:
"An aurora forecast by text at eleven at night, a car already warm outside the door. Wayfare thinks about the parts of a trip you forget until they go wrong.",
name: "Greta Lindholm",
trip: "Reykjavik, in winter",
},
];
const NEWSLETTER_DONE =
"You are on the list. The first dispatch lands in your inbox within the week.";
const MONTHS = [
"Any month",
"March",
"April",
"May",
"June",
"September",
"October",
];
const FOOTER_COLS = [
{
title: "Destinations",
links: ["Japan", "Italy", "Iceland", "Morocco", "India", "Canada"],
},
{
title: "Journeys",
links: ["Walking", "By rail", "Culinary", "Off-season", "Family"],
},
{
title: "Company",
links: ["Our editors", "How we book", "Careers", "Press"],
},
{
title: "Support",
links: ["Help desk", "Manage booking", "Cancellation", "Contact"],
},
];
const TAB_CTA: Record<
"hotels" | "flights" | "journeys",
{ label: string; icon: typeof Search }
> = {
hotels: { label: "Search stays", icon: Search },
flights: { label: "Find flights", icon: Plane },
journeys: { label: "Browse journeys", icon: Compass },
};
/* ------------------------------------------------------------------ */
/* Destination scenes — pure CSS + SVG flat illustration */
/* ------------------------------------------------------------------ */
function DestinationScene({ kind }: { kind: SceneKind }) {
switch (kind) {
case "kyoto":
return (
<>
<div
className="absolute inset-0"
style={{
background:
"linear-gradient(180deg,#f7cabf 0%,#ef9fa8 32%,#b56176 60%,#6e3b52 100%)",
}}
/>
<div
className="wf-float absolute left-[62%] top-[24%] h-16 w-16 rounded-full"
style={{
background:
"radial-gradient(circle at 40% 40%,#ffeede,#ffd6bf 70%)",
boxShadow: "0 0 44px 8px rgba(255,214,191,0.55)",
}}
/>
<svg
viewBox="0 0 400 260"
preserveAspectRatio="none"
className="absolute inset-0 h-full w-full"
>
<path
d="M0,150 C60,132 130,142 190,128 C258,113 332,132 400,120 L400,260 L0,260 Z"
fill="#8a5266"
/>
<path
d="M0,182 C80,162 152,180 232,166 C300,154 362,174 400,164 L400,260 L0,260 Z"
fill="#5e3450"
/>
<g fill="#33203a">
<path d="M128,198 q72,-16 144,0 l0,10 q-72,-14 -144,0 Z" />
<path d="M150,206 h12 v54 h-12 Z" />
<path d="M238,206 h12 v54 h-12 Z" />
<path d="M144,222 h112 v7 h-112 Z" />
</g>
<path d="M0,246 L400,246 L400,260 L0,260 Z" fill="#2a1a30" />
</svg>
</>
);
case "sahara":
return (
<>
<div
className="absolute inset-0"
style={{
background:
"linear-gradient(180deg,#fdedc6 0%,#f6bd72 34%,#e08a3e 62%,#a9531f 100%)",
}}
/>
<div
className="wf-float absolute left-1/2 top-[38%] h-20 w-20 -translate-x-1/2 rounded-full"
style={{
background:
"radial-gradient(circle at 50% 45%,#fff7e2,#ffe4a8 72%)",
boxShadow: "0 0 60px 12px rgba(255,220,150,0.5)",
}}
/>
<svg
viewBox="0 0 400 260"
preserveAspectRatio="none"
className="absolute inset-0 h-full w-full"
>
<path
d="M0,168 C120,138 202,176 300,150 C350,137 380,150 400,146 L400,260 L0,260 Z"
fill="#cb7a31"
/>
<path
d="M0,200 C90,174 162,206 252,186 C322,170 372,192 400,184 L400,260 L0,260 Z"
fill="#b06026"
/>
<path
d="M0,238 C110,214 192,240 292,222 C342,213 382,228 400,224 L400,260 L0,260 Z"
fill="#8a451b"
/>
</svg>
</>
);
case "reykjavik":
return (
<>
<div
className="absolute inset-0"
style={{
background:
"linear-gradient(180deg,#0c2536 0%,#123a4d 55%,#1c556a 100%)",
}}
/>
<div
className="wf-drift absolute -left-6 top-[18%] h-24 w-[70%] rotate-[-8deg] rounded-full opacity-70 blur-2xl mix-blend-screen"
style={{
background:
"linear-gradient(90deg,transparent,#79e6c2,transparent)",
}}
/>
<div
className="wf-drift-slow absolute left-[20%] top-[30%] h-20 w-[70%] rotate-[6deg] rounded-full opacity-60 blur-2xl mix-blend-screen"
style={{
background:
"linear-gradient(90deg,transparent,#63c7e6,transparent)",
}}
/>
<div
className="absolute right-[20%] top-[16%] h-7 w-7 rounded-full"
style={{
background:
"radial-gradient(circle at 45% 40%,#f4fbff,#cfe6ef 75%)",
boxShadow: "0 0 24px 4px rgba(210,235,245,0.45)",
}}
/>
<svg
viewBox="0 0 400 260"
preserveAspectRatio="none"
className="absolute inset-0 h-full w-full"
>
<g className="wf-twinkle" fill="#eaf6fb">
<circle cx="60" cy="52" r="1.4" />
<circle cx="140" cy="34" r="1.1" />
<circle cx="250" cy="60" r="1.3" />
<circle cx="330" cy="44" r="1" />
<circle cx="200" cy="30" r="1.2" />
</g>
<path
d="M0,192 L60,150 L112,186 L172,140 L232,182 L302,150 L360,186 L400,168 L400,260 L0,260 Z"
fill="#6f97a3"
/>
<path
d="M0,216 L70,182 L122,212 L192,174 L252,208 L322,180 L400,212 L400,260 L0,260 Z"
fill="#e9f2f4"
/>
</svg>
</>
);
case "amalfi":
return (
<>
<div
className="absolute inset-0"
style={{
background:
"linear-gradient(180deg,#c6e9f4 0%,#83c6e2 46%,#4595bb 74%)",
}}
/>
<div
className="wf-float absolute right-[26%] top-[20%] h-14 w-14 rounded-full"
style={{
background:
"radial-gradient(circle at 45% 42%,#fff4dc,#ffe6b8 72%)",
boxShadow: "0 0 34px 6px rgba(255,235,190,0.55)",
}}
/>
<div
className="absolute inset-x-0 bottom-0 h-[40%]"
style={{
background: "linear-gradient(180deg,#2f8ab4 0%,#12547a 100%)",
}}
/>
<div
className="absolute bottom-0 right-[26%] h-[40%] w-10 -translate-x-1/2 opacity-60 blur-[2px]"
style={{
background: "linear-gradient(180deg,#ffeecb,transparent)",
}}
/>
<svg
viewBox="0 0 400 260"
preserveAspectRatio="none"
className="absolute inset-0 h-full w-full"
>
<path d="M0,150 L34,150 L34,260 L0,260 Z" fill="none" />
<g>
<path
d="M0,116 L58,108 L96,150 L128,214 L0,238 Z"
fill="#b3541f"
/>
<rect x="14" y="150" width="18" height="16" fill="#f0e2c8" />
<rect x="40" y="140" width="16" height="15" fill="#efd8b6" />
<rect x="20" y="176" width="16" height="15" fill="#e9d2ac" />
<rect x="19" y="153" width="4" height="5" fill="#7c3d17" />
<rect x="45" y="143" width="4" height="5" fill="#7c3d17" />
</g>
<g stroke="#7fc0dc" strokeWidth="2" opacity="0.55">
<line x1="120" y1="196" x2="200" y2="196" />
<line x1="240" y1="210" x2="330" y2="210" />
<line x1="170" y1="224" x2="280" y2="224" />
</g>
</svg>
</>
);
case "jaipur":
return (
<>
<div
className="absolute inset-0"
style={{
background:
"linear-gradient(180deg,#fcdcac 0%,#f0a279 38%,#cf6470 66%,#8f3a5a 100%)",
}}
/>
<div
className="wf-float absolute left-[30%] top-[26%] h-16 w-16 rounded-full"
style={{
background:
"radial-gradient(circle at 45% 42%,#fff0d6,#ffd7ac 74%)",
boxShadow: "0 0 40px 8px rgba(255,210,170,0.5)",
}}
/>
<svg
viewBox="0 0 400 260"
preserveAspectRatio="none"
className="absolute inset-0 h-full w-full"
>
<g fill="#6e2c46">
<path d="M0,206 L400,206 L400,260 L0,260 Z" />
<path d="M172,206 C172,172 184,156 200,146 C216,156 228,172 228,206 Z" />
<rect x="198" y="132" width="4" height="16" />
<path d="M92,206 C92,186 100,175 110,169 C120,175 128,186 128,206 Z" />
<rect x="108" y="160" width="3" height="10" />
<path d="M272,206 C272,186 280,175 290,169 C300,175 308,186 308,206 Z" />
<rect x="288" y="160" width="3" height="10" />
<path d="M28,206 L28,190 L40,190 L40,206 Z M360,206 L360,190 L372,190 L372,206 Z" />
</g>
<g fill="#8f3a5a">
<path d="M150,206 q10,-22 20,0 Z" />
<path d="M230,206 q10,-22 20,0 Z" />
</g>
</svg>
</>
);
case "banff":
return (
<>
<div
className="absolute inset-0"
style={{
background:
"linear-gradient(180deg,#dcefe6 0%,#a3d0bd 42%,#4f9d86 84%)",
}}
/>
<div
className="absolute left-[22%] top-[16%] h-12 w-12 rounded-full opacity-90"
style={{
background:
"radial-gradient(circle at 45% 42%,#fbfdee,#e7f2d8 76%)",
boxShadow: "0 0 30px 6px rgba(240,248,220,0.5)",
}}
/>
<svg
viewBox="0 0 400 260"
preserveAspectRatio="none"
className="absolute inset-0 h-full w-full"
>
<path
d="M0,152 L84,96 L152,152 L224,90 L302,152 L400,110 L400,210 L0,210 Z"
fill="#3f6f63"
/>
<g fill="#eef6f2">
<path d="M224,90 L206,110 L242,110 Z" />
<path d="M84,96 L70,114 L100,114 Z" />
<path d="M400,110 L384,126 L400,126 Z" />
</g>
<g fill="#234b3b">
<path d="M40,196 L52,168 L64,196 Z" />
<path d="M78,198 L92,164 L106,198 Z" />
<path d="M120,196 L132,170 L144,196 Z" />
<path d="M262,198 L276,166 L290,198 Z" />
<path d="M306,196 L318,170 L330,196 Z" />
<path d="M348,198 L362,164 L376,198 Z" />
</g>
<path d="M0,196 L400,196 L400,260 L0,260 Z" fill="#2f7d69" />
<g stroke="#5aa992" strokeWidth="2" opacity="0.5">
<line x1="30" y1="222" x2="150" y2="222" />
<line x1="220" y1="234" x2="360" y2="234" />
</g>
</svg>
</>
);
}
}
/* ------------------------------------------------------------------ */
/* Page */
/* ------------------------------------------------------------------ */
export default function TravelBookingTemplate() {
const [menuOpen, setMenuOpen] = useState(false);
const [activeTab, setActiveTab] = useState<"hotels" | "flights" | "journeys">(
"hotels",
);
const [where, setWhere] = useState("");
const [fromCity, setFromCity] = useState("");
const [toCity, setToCity] = useState("");
const [checkIn, setCheckIn] = useState("2026-05-14");
const [checkOut, setCheckOut] = useState("2026-05-21");
const [guests, setGuests] = useState(2);
const [month, setMonth] = useState("May");
const [saved, setSaved] = useState<number[]>([]);
const [activeDay, setActiveDay] = useState(0);
const [email, setEmail] = useState("");
const [subscribed, setSubscribed] = useState(false);
const cta = TAB_CTA[activeTab];
const CtaIcon = cta.icon;
const toggleSave = (i: number) =>
setSaved((prev) =>
prev.includes(i) ? prev.filter((x) => x !== i) : [...prev, i],
);
const fieldBox =
"flex items-center gap-2.5 rounded-none border border-[#2A2018]/15 bg-[#FBF8F1] px-3.5 py-3 transition-colors focus-within:border-[#C2571B]";
const fieldInput =
"w-full bg-transparent text-sm text-[#2A2018] outline-none placeholder:text-[#A89A85]";
return (
<div
className={`${sans.className} min-h-screen bg-[#F6F1E7] text-[#2A2018] antialiased selection:bg-[#C2571B] selection:text-[#F6F1E7]`}
>
<TemplateBackLink />
<style>{`
@keyframes wf-rise { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: none; } }
@keyframes wf-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes wf-float-x { 0%, 100% { transform: translate(-50%, 0); } 50% { transform: translate(-50%, -6px); } }
@keyframes wf-drift { 0%, 100% { transform: translateX(0) rotate(-8deg); } 50% { transform: translateX(24px) rotate(-8deg); } }
@keyframes wf-drift-slow { 0%, 100% { transform: translateX(0) rotate(6deg); } 50% { transform: translateX(-20px) rotate(6deg); } }
@keyframes wf-twinkle { 0%, 100% { opacity: 0.9; } 50% { opacity: 0.2; } }
@keyframes wf-flow { to { stroke-dashoffset: -240; } }
@keyframes wf-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.wf-rise { opacity: 0; animation: wf-rise 0.8s cubic-bezier(0.22,1,0.36,1) forwards; }
.wf-float { animation: wf-float 7s ease-in-out infinite; }
.wf-drift { animation: wf-drift 12s ease-in-out infinite; }
.wf-drift-slow { animation: wf-drift-slow 16s ease-in-out infinite; }
.wf-twinkle circle { animation: wf-twinkle 4s ease-in-out infinite; }
.wf-twinkle circle:nth-child(2) { animation-delay: 0.8s; }
.wf-twinkle circle:nth-child(3) { animation-delay: 1.6s; }
.wf-twinkle circle:nth-child(4) { animation-delay: 2.4s; }
.wf-twinkle circle:nth-child(5) { animation-delay: 3.2s; }
.wf-flow { stroke-dasharray: 4 8; animation: wf-flow 6s linear infinite; }
.wf-marquee { animation: wf-marquee 32s linear infinite; }
@media (prefers-reduced-motion: reduce) {
.wf-rise, .wf-float, .wf-drift, .wf-drift-slow, .wf-marquee { animation: none; opacity: 1; transform: none; }
.wf-twinkle circle, .wf-flow { animation: none; }
}
`}</style>
{/* Paper atmosphere */}
<div
aria-hidden
className="pointer-events-none fixed inset-0 z-0"
style={{
backgroundImage:
"radial-gradient(circle, rgba(42,32,24,0.05) 1px, transparent 1px), radial-gradient(ellipse 80% 50% at 50% 0%, rgba(194,87,27,0.06), transparent 70%)",
backgroundSize: "26px 26px, 100% 100%",
}}
/>
{/* ── Nav ─────────────────────────────────────────────────── */}
<header className="sticky top-0 z-40 border border-[#2A2018]/10 bg-[#F6F1E7]/85 backdrop-blur-md">
<div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-5 md:px-8">
<a href="#" className="flex items-center gap-2.5">
<span className="grid h-8 w-8 place-items-center rounded-full border border-[#C2571B] text-[#C2571B]">
<Compass className="h-4 w-4" strokeWidth={1.8} />
</span>
<span className={`${display.className} text-xl tracking-tight`}>
Wayfare
</span>
</a>
<nav className="hidden items-center gap-9 md:flex">
{NAV_LINKS.map((link) => (
<a
key={link}
href="#"
className="text-sm text-[#6F6151] transition-colors hover:text-[#2A2018]"
>
{link}
</a>
))}
</nav>
<div className="hidden items-center gap-4 md:flex">
<a
href="#"
className="text-sm text-[#6F6151] transition-colors hover:text-[#2A2018]"
>
Sign in
</a>
<a
href="#stays"
className="group inline-flex items-center gap-1.5 bg-[#C2571B] px-4 py-2 text-sm font-medium text-[#F6F1E7] transition-colors hover:bg-[#9E4413]"
>
Plan a trip
<ArrowRight className="h-3.5 w-3.5 transition-transform group-hover:translate-x-0.5" />
</a>
</div>
<button
type="button"
onClick={() => setMenuOpen(!menuOpen)}
aria-label="Toggle menu"
className="md:hidden"
>
{menuOpen ? (
<X className="h-5 w-5" />
) : (
<Menu className="h-5 w-5" />
)}
</button>
</div>
{menuOpen && (
<nav className="border border-[#2A2018]/10 px-5 py-4 md:hidden">
{NAV_LINKS.map((link) => (
<a
key={link}
href="#"
className="block py-2.5 text-sm text-[#6F6151]"
>
{link}
</a>
))}
<a
href="#stays"
className="mt-3 block bg-[#C2571B] px-4 py-2.5 text-center text-sm font-medium text-[#F6F1E7]"
>
Plan a trip
</a>
</nav>
)}
</header>
{/* ── Hero ────────────────────────────────────────────────── */}
<section className="relative z-10">
<div className="mx-auto grid max-w-6xl items-center gap-14 px-5 pb-16 pt-14 md:grid-cols-[1.05fr_0.95fr] md:px-8 md:pb-24 md:pt-20">
<div>
<p
className={`${mono.className} wf-rise mb-6 flex items-center gap-2.5 text-[11px] tracking-[0.2em] text-[#C2571B]`}
>
<span className="inline-block h-px w-8 bg-[#C2571B]" />
THE JOURNAL OF ELSEWHERE · ISSUE 14
</p>
<h1
className={`${display.className} wf-rise text-[3rem] leading-[0.98] md:text-[4.6rem]`}
style={{ animationDelay: "80ms" }}
>
Go far. Stay a while.
<br />
<em className="text-[#C2571B]">Come back changed.</em>
</h1>
<p
className="wf-rise mt-7 max-w-md text-base leading-relaxed text-[#6F6151] md:text-lg"
style={{ animationDelay: "160ms" }}
>
{HERO_BODY}
</p>
<div
className="wf-rise mt-8 flex flex-wrap items-center gap-x-6 gap-y-2"
style={{ animationDelay: "240ms" }}
>
{HERO_TRUST.map((item) => (
<span
key={item}
className="inline-flex items-center gap-1.5 text-sm text-[#6F6151]"
>
<Check className="h-4 w-4 text-[#C2571B]" strokeWidth={2.5} />
{item}
</span>
))}
</div>
</div>
{/* Hero postcard — CSS scenery */}
<div className="wf-rise relative" style={{ animationDelay: "200ms" }}>
<div
aria-hidden
className="absolute -inset-4 bg-[#C2571B]/10 blur-3xl"
/>
<figure className="relative border border-[#2A2018]/15 bg-[#FBF8F1] p-3 shadow-[0_36px_70px_-40px_rgba(42,32,24,0.5)]">
<div className="relative h-64 overflow-hidden md:h-80">
<DestinationScene kind="amalfi" />
<span
className={`${mono.className} absolute left-3 top-3 bg-[#2A2018]/70 px-2 py-1 text-[10px] tracking-[0.14em] text-[#F6F1E7]`}
>
40.63 N / 14.60 E
</span>
</div>
<figcaption className="flex items-end justify-between px-1 pb-1 pt-3">
<div>
<p className={`${display.className} text-xl`}>Amalfi Coast</p>
<p className="text-xs text-[#8A7A64]">Villa Lucia · Italy</p>
</div>
<div className="text-right">
<p className={`${mono.className} text-lg text-[#C2571B]`}>
$395
</p>
<p className="text-[10px] uppercase tracking-wide text-[#A89A85]">
per night
</p>
</div>
</figcaption>
</figure>
</div>
</div>
{/* Search panel */}
<div className="relative z-10 mx-auto max-w-6xl px-5 md:px-8">
<div
id="stays"
className="wf-rise border border-[#2A2018]/15 bg-[#F1E8D6] p-4 shadow-[0_24px_48px_-32px_rgba(42,32,24,0.45)] md:p-5"
style={{ animationDelay: "300ms" }}
>
<div className="mb-4 flex flex-wrap gap-1">
{(["hotels", "flights", "journeys"] as const).map((tab) => (
<button
key={tab}
type="button"
onClick={() => setActiveTab(tab)}
className={`${mono.className} px-4 py-2 text-[11px] uppercase tracking-[0.14em] transition-colors ${
activeTab === tab
? "bg-[#2A2018] text-[#F6F1E7]"
: "text-[#6F6151] hover:text-[#2A2018]"
}`}
>
{tab}
</button>
))}
</div>
<div className="flex flex-col gap-3 lg:flex-row lg:items-stretch">
{activeTab === "hotels" && (
<>
<label
className={`${fieldBox} flex-[1.4]`}
aria-label="Destination"
>
<MapPin className="h-4 w-4 shrink-0 text-[#C2571B]" />
<input
value={where}
onChange={(e) => setWhere(e.target.value)}
placeholder="Where are you going?"
className={fieldInput}
/>
</label>
<label className={`${fieldBox} flex-1`} aria-label="Check in">
<Calendar className="h-4 w-4 shrink-0 text-[#C2571B]" />
<input
type="date"
value={checkIn}
onChange={(e) => setCheckIn(e.target.value)}
className={fieldInput}
/>
</label>
<label
className={`${fieldBox} flex-1`}
aria-label="Check out"
>
<Calendar className="h-4 w-4 shrink-0 text-[#C2571B]" />
<input
type="date"
value={checkOut}
onChange={(e) => setCheckOut(e.target.value)}
className={fieldInput}
/>
</label>
<label
className={`${fieldBox} w-full lg:w-40`}
aria-label="Guests"
>
<Users className="h-4 w-4 shrink-0 text-[#C2571B]" />
<select
value={guests}
onChange={(e) => setGuests(Number(e.target.value))}
className={fieldInput}
>
{[1, 2, 3, 4, 5, 6].map((n) => (
<option key={n} value={n}>
{n} guest{n > 1 ? "s" : ""}
</option>
))}
</select>
</label>
</>
)}
{activeTab === "flights" && (
<>
<label className={`${fieldBox} flex-1`} aria-label="From">
<Plane className="h-4 w-4 shrink-0 -rotate-45 text-[#C2571B]" />
<input
value={fromCity}
onChange={(e) => setFromCity(e.target.value)}
placeholder="From"
className={fieldInput}
/>
</label>
<label className={`${fieldBox} flex-1`} aria-label="To">
<MapPin className="h-4 w-4 shrink-0 text-[#C2571B]" />
<input
value={toCity}
onChange={(e) => setToCity(e.target.value)}
placeholder="To"
className={fieldInput}
/>
</label>
<label className={`${fieldBox} flex-1`} aria-label="Depart">
<Calendar className="h-4 w-4 shrink-0 text-[#C2571B]" />
<input
type="date"
value={checkIn}
onChange={(e) => setCheckIn(e.target.value)}
className={fieldInput}
/>
</label>
<label className={`${fieldBox} flex-1`} aria-label="Return">
<Calendar className="h-4 w-4 shrink-0 text-[#C2571B]" />
<input
type="date"
value={checkOut}
onChange={(e) => setCheckOut(e.target.value)}
className={fieldInput}
/>
</label>
<label
className={`${fieldBox} w-full lg:w-44`}
aria-label="Passengers"
>
<Users className="h-4 w-4 shrink-0 text-[#C2571B]" />
<select
value={guests}
onChange={(e) => setGuests(Number(e.target.value))}
className={fieldInput}
>
{[1, 2, 3, 4, 5, 6].map((n) => (
<option key={n} value={n}>
{n} passenger{n > 1 ? "s" : ""}
</option>
))}
</select>
</label>
</>
)}
{activeTab === "journeys" && (
<>
<label
className={`${fieldBox} flex-[1.6]`}
aria-label="Journey theme"
>
<Compass className="h-4 w-4 shrink-0 text-[#C2571B]" />
<input
value={where}
onChange={(e) => setWhere(e.target.value)}
placeholder="A place or an idea -- walking, wine, the north"
className={fieldInput}
/>
</label>
<label
className={`${fieldBox} w-full lg:w-48`}
aria-label="Month"
>
<Calendar className="h-4 w-4 shrink-0 text-[#C2571B]" />
<select
value={month}
onChange={(e) => setMonth(e.target.value)}
className={fieldInput}
>
{MONTHS.map((m) => (
<option key={m} value={m}>
{m}
</option>
))}
</select>
</label>
<label
className={`${fieldBox} w-full lg:w-44`}
aria-label="Travellers"
>
<Users className="h-4 w-4 shrink-0 text-[#C2571B]" />
<select
value={guests}
onChange={(e) => setGuests(Number(e.target.value))}
className={fieldInput}
>
{[1, 2, 3, 4, 5, 6].map((n) => (
<option key={n} value={n}>
{n} traveller{n > 1 ? "s" : ""}
</option>
))}
</select>
</label>
</>
)}
<button
type="button"
className="group inline-flex items-center justify-center gap-2 bg-[#C2571B] px-6 py-3 text-sm font-semibold text-[#F6F1E7] transition-colors hover:bg-[#9E4413]"
>
<CtaIcon className="h-4 w-4" />
{cta.label}
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5" />
</button>
</div>
</div>
</div>
</section>
{/* ── Ticker ──────────────────────────────────────────────── */}
<section className="relative z-10 mt-16 overflow-hidden border-y border-[#2A2018]/10 py-4">
<div className="wf-marquee flex w-max items-center gap-10 pr-10">
{[...TICKER, ...TICKER].map((place, i) => (
<span
key={i}
className={`${display.className} flex items-center gap-10 whitespace-nowrap text-lg italic text-[#2A2018]/35`}
>
{place}
<span className="text-[#C2571B]/50">/</span>
</span>
))}
</div>
</section>
{/* ── Featured stays ──────────────────────────────────────── */}
<section className="relative z-10">
<div className="mx-auto max-w-6xl px-5 py-20 md:px-8 md:py-28">
<div className="mb-12 flex flex-wrap items-end justify-between gap-6">
<div>
<p
className={`${mono.className} mb-4 text-[11px] tracking-[0.2em] text-[#C2571B]`}
>
01 / WHERE WE ARE SENDING PEOPLE
</p>
<h2
className={`${display.className} max-w-xl text-4xl leading-[1.02] md:text-5xl`}
>
Six places worth the{" "}
<em className="text-[#C2571B]">airfare.</em>
</h2>
</div>
<a
href="#"
className={`${mono.className} inline-flex items-center gap-1.5 border border-[#2A2018]/30 pb-1 text-xs uppercase tracking-[0.14em] text-[#6F6151] transition-colors hover:border-[#C2571B] hover:text-[#2A2018]`}
>
All destinations
<ArrowUpRight className="h-3.5 w-3.5" />
</a>
</div>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{DESTINATIONS.map((dest, i) => {
const isSaved = saved.includes(i);
return (
<article
key={dest.place}
className="wf-rise group flex flex-col border border-[#2A2018]/12 bg-[#FBF8F1] transition-all hover:-translate-y-1 hover:shadow-[0_28px_50px_-30px_rgba(42,32,24,0.45)]"
style={{ animationDelay: `${i * 80}ms` }}
>
<div className="relative h-52 overflow-hidden">
<DestinationScene kind={dest.scene} />
<button
type="button"
onClick={() => toggleSave(i)}
aria-label={
isSaved
? `Remove ${dest.place} from saved`
: `Save ${dest.place}`
}
aria-pressed={isSaved}
className="absolute right-3 top-3 grid h-9 w-9 place-items-center rounded-full bg-[#F6F1E7]/85 text-[#2A2018] backdrop-blur-sm transition-colors hover:bg-[#F6F1E7]"
>
<Heart
className={`h-4 w-4 ${isSaved ? "fill-[#C2571B] text-[#C2571B]" : ""}`}
/>
</button>
<span
className={`${mono.className} absolute bottom-3 left-3 bg-[#2A2018]/65 px-2 py-1 text-[10px] tracking-[0.12em] text-[#F6F1E7]`}
>
{dest.coords}
</span>
</div>
<div className="flex flex-1 flex-col p-5">
<div className="mb-1 flex items-start justify-between gap-3">
<div>
<h3
className={`${display.className} text-2xl leading-none`}
>
{dest.place}
</h3>
<p className="mt-1 text-xs uppercase tracking-wide text-[#A89A85]">
{dest.country}
</p>
</div>
<span className="inline-flex items-center gap-1 text-sm font-semibold text-[#2A2018]">
<Star className="h-3.5 w-3.5 fill-[#C2571B] text-[#C2571B]" />
{dest.rating}
</span>
</div>
<p className="mb-4 mt-2 text-sm leading-relaxed text-[#6F6151]">
{dest.blurb}
</p>
<div className="mb-5 flex flex-wrap gap-1.5">
{dest.tags.map((tag) => (
<span
key={tag}
className="border border-[#2A2018]/12 px-2 py-0.5 text-[11px] text-[#6F6151]"
>
{tag}
</span>
))}
</div>
<div className="mt-auto flex items-end justify-between border border-[#2A2018]/10 pt-4">
<div>
<span
className={`${mono.className} text-xl text-[#2A2018]`}
>
${dest.price}
</span>
<span className="text-sm text-[#8A7A64]"> / night</span>
<p className="text-[11px] text-[#A89A85]">
{dest.stay} · {dest.reviews} field notes
</p>
</div>
<a
href="#"
className="inline-flex items-center gap-1 text-sm font-medium text-[#C2571B] transition-colors hover:text-[#9E4413]"
>
View
<ArrowUpRight className="h-3.5 w-3.5" />
</a>
</div>
</div>
</article>
);
})}
</div>
</div>
</section>
{/* ── Routes panel ────────────────────────────────────────── */}
<section className="relative z-10 border-y border-[#2A2018]/10 bg-[#EFE7D6]">
<div className="mx-auto grid max-w-6xl gap-12 px-5 py-20 md:grid-cols-[0.8fr_1.2fr] md:items-center md:px-8 md:py-28">
<div>
<p
className={`${mono.className} mb-4 text-[11px] tracking-[0.2em] text-[#C2571B]`}
>
02 / THE NETWORK
</p>
<h2
className={`${display.className} text-4xl leading-[1.02] md:text-5xl`}
>
Every thread runs <em className="text-[#C2571B]">through us.</em>
</h2>
<p className="mt-5 max-w-sm text-base leading-relaxed text-[#6F6151]">
We hold rates with hundreds of small hotels and every major
carrier, then stitch the connections into a single booking. One
reference number, from your front door to the far one.
</p>
<div className="mt-8 flex gap-8">
<div>
<p className={`${mono.className} text-3xl text-[#2A2018]`}>
72
</p>
<p className="text-xs text-[#8A7A64]">countries mapped</p>
</div>
<div>
<p className={`${mono.className} text-3xl text-[#2A2018]`}>
1,900+
</p>
<p className="text-xs text-[#8A7A64]">stays slept in</p>
</div>
<div>
<p className={`${mono.className} text-3xl text-[#2A2018]`}>1</p>
<p className="text-xs text-[#8A7A64]">reference number</p>
</div>
</div>
</div>
<div className="relative overflow-hidden border border-[#2A2018]/15 bg-[#2A2018] p-4">
<div
aria-hidden
className="absolute inset-0 opacity-[0.5]"
style={{
backgroundImage:
"radial-gradient(circle, rgba(246,241,231,0.14) 1px, transparent 1px)",
backgroundSize: "22px 22px",
}}
/>
<div className="relative">
<div className="mb-3 flex items-center justify-between">
<span
className={`${mono.className} text-[10px] tracking-[0.18em] text-[#F6F1E7]/50`}
>
LIVE ROUTE MAP
</span>
<span
className={`${mono.className} flex items-center gap-1.5 text-[10px] text-[#E08B4C]`}
>
<span className="inline-block h-1.5 w-1.5 rounded-full bg-[#E08B4C]" />
6 ROUTES OPEN
</span>
</div>
<svg viewBox="0 0 800 340" className="h-full w-full">
<circle
cx={HUB.x}
cy={HUB.y}
r="70"
fill="none"
stroke="#F6F1E7"
strokeOpacity="0.1"
/>
<circle
cx={HUB.x}
cy={HUB.y}
r="120"
fill="none"
stroke="#F6F1E7"
strokeOpacity="0.06"
/>
<line
x1="40"
y1="150"
x2="760"
y2="150"
stroke="#F6F1E7"
strokeOpacity="0.08"
strokeDasharray="2 8"
/>
{ROUTE_CITIES.map((c) => (
<path
key={c.name}
d={c.arc}
fill="none"
stroke="#E08B4C"
strokeWidth="1.5"
className="wf-flow"
strokeOpacity="0.85"
/>
))}
{ROUTE_CITIES.map((c) => (
<g key={`node-${c.name}`}>
<circle
cx={c.x}
cy={c.y}
r="8"
fill="#C2571B"
fillOpacity="0.2"
/>
<circle cx={c.x} cy={c.y} r="3.5" fill="#E08B4C" />
<text
x={c.x + 10}
y={c.y + 4}
className={mono.className}
fill="#F6F1E7"
fillOpacity="0.7"
fontSize="12"
>
{c.name}
</text>
</g>
))}
<circle cx={HUB.x} cy={HUB.y} r="5" fill="#F6F1E7" />
<circle
cx={HUB.x}
cy={HUB.y}
r="10"
fill="none"
stroke="#F6F1E7"
strokeOpacity="0.4"
/>
<text
x={HUB.x + 12}
y={HUB.y + 4}
className={mono.className}
fill="#F6F1E7"
fontSize="13"
>
{HUB.name}
</text>
</svg>
<div className="pointer-events-none absolute left-[64%] top-[26%] text-[#F6F1E7]">
<Plane className="h-5 w-5 rotate-[28deg]" strokeWidth={1.6} />
</div>
</div>
</div>
</div>
</section>
{/* ── Why Wayfare ─────────────────────────────────────────── */}
<section className="relative z-10">
<div className="mx-auto max-w-6xl px-5 py-20 md:px-8 md:py-28">
<p
className={`${mono.className} mb-4 text-[11px] tracking-[0.2em] text-[#C2571B]`}
>
03 / WHY WAYFARE
</p>
<h2
className={`${display.className} mb-14 max-w-2xl text-4xl leading-[1.05] md:text-5xl`}
>
A booking house that behaves like a{" "}
<em className="text-[#C2571B]">friend with a passport.</em>
</h2>
<div className="grid gap-px bg-[#2A2018]/10 md:grid-cols-3">
{WHY.map((item) => (
<article key={item.label} className="bg-[#F6F1E7] p-8">
<span className="mb-6 grid h-11 w-11 place-items-center rounded-full border border-[#C2571B]/40 text-[#C2571B]">
<item.icon className="h-5 w-5" strokeWidth={1.6} />
</span>
<h3 className={`${display.className} mb-3 text-2xl`}>
{item.label}
</h3>
<p className="text-sm leading-relaxed text-[#6F6151]">
{item.body}
</p>
</article>
))}
</div>
</div>
</section>
{/* ── Journey spotlight ───────────────────────────────────── */}
<section className="relative z-10 border-y border-[#2A2018]/10 bg-[#EFE7D6]">
<div className="mx-auto grid max-w-6xl gap-10 px-5 py-20 md:grid-cols-2 md:px-8 md:py-28">
<div className="relative">
<div className="relative h-72 overflow-hidden border border-[#2A2018]/15 md:h-full md:min-h-[26rem]">
<DestinationScene kind="kyoto" />
<div className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-[#2A2018]/80 to-transparent p-6">
<p
className={`${mono.className} mb-2 text-[10px] tracking-[0.18em] text-[#F6F1E7]/70`}
>
{SPOTLIGHT.kicker.toUpperCase()}
</p>
<div className="flex flex-wrap items-center gap-x-5 gap-y-1 text-[#F6F1E7]">
<span className="inline-flex items-center gap-1.5 text-sm">
<Clock className="h-4 w-4 text-[#E08B4C]" />
{SPOTLIGHT.duration}
</span>
<span className="inline-flex items-center gap-1.5 text-sm">
<Users className="h-4 w-4 text-[#E08B4C]" />
{SPOTLIGHT.group}
</span>
<span className="inline-flex items-center gap-1.5 text-sm">
<Calendar className="h-4 w-4 text-[#E08B4C]" />
{SPOTLIGHT.season}
</span>
</div>
</div>
</div>
</div>
<div>
<h2
className={`${display.className} text-4xl leading-[1.03] md:text-5xl`}
>
{SPOTLIGHT.title}
</h2>
<p className="mt-4 max-w-md text-base leading-relaxed text-[#6F6151]">
{SPOTLIGHT.lede}
</p>
<div className="mt-8 border border-[#2A2018]/12">
{SPOTLIGHT.days.map((d, i) => {
const open = activeDay === i;
return (
<div key={d.day} className="border border-[#2A2018]/12">
<button
type="button"
onClick={() => setActiveDay(i)}
aria-expanded={open}
className="flex w-full items-center gap-4 py-4 text-left"
>
<span
className={`${mono.className} w-14 shrink-0 text-xs ${open ? "text-[#C2571B]" : "text-[#A89A85]"}`}
>
{d.day}
</span>
<span
className={`flex-1 text-base ${open ? "font-semibold text-[#2A2018]" : "text-[#6F6151]"}`}
>
{d.title}
</span>
<ChevronRight
className={`h-4 w-4 shrink-0 transition-transform ${open ? "rotate-90 text-[#C2571B]" : "text-[#A89A85]"}`}
/>
</button>
{open && (
<p className="pb-5 pl-[4.5rem] pr-4 text-sm leading-relaxed text-[#6F6151]">
{d.body}
</p>
)}
</div>
);
})}
</div>
<div className="mt-8 flex flex-wrap items-center gap-5">
<a
href="#"
className="group inline-flex items-center gap-2 bg-[#C2571B] px-6 py-3.5 text-sm font-semibold text-[#F6F1E7] transition-colors hover:bg-[#9E4413]"
>
Reserve this journey
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5" />
</a>
<p className="text-sm text-[#6F6151]">
from{" "}
<span className={`${mono.className} text-lg text-[#2A2018]`}>
${SPOTLIGHT.price.toLocaleString()}
</span>{" "}
per person
</p>
</div>
</div>
</div>
</section>
{/* ── Testimonials ────────────────────────────────────────── */}
<section className="relative z-10">
<div className="mx-auto max-w-6xl px-5 py-20 md:px-8 md:py-28">
<p
className={`${mono.className} mb-12 text-[11px] tracking-[0.2em] text-[#C2571B]`}
>
04 / FIELD NOTES
</p>
<div className="grid gap-10 md:grid-cols-3 md:gap-8">
{TESTIMONIALS.map((t) => (
<figure
key={t.name}
className="flex flex-col justify-between border-2 border-[#C2571B] pt-6"
>
<div>
<Quote className="h-6 w-6 text-[#C2571B]/40" />
<blockquote
className={`${display.className} mt-4 text-xl leading-[1.4] text-[#2A2018]`}
>
{t.quote}
</blockquote>
</div>
<figcaption className="mt-7">
<p className="text-sm font-semibold text-[#2A2018]">
{t.name}
</p>
<p
className={`${mono.className} mt-1 text-[11px] tracking-wide text-[#8A7A64]`}
>
{t.trip}
</p>
</figcaption>
</figure>
))}
</div>
</div>
</section>
{/* ── Newsletter ──────────────────────────────────────────── */}
<section className="relative z-10 overflow-hidden border border-[#2A2018]/10 bg-[#C2571B] text-[#F6F1E7]">
<div
aria-hidden
className="pointer-events-none absolute inset-0 opacity-20"
style={{
backgroundImage:
"radial-gradient(circle, rgba(246,241,231,0.4) 1px, transparent 1px)",
backgroundSize: "24px 24px",
}}
/>
<div className="relative mx-auto grid max-w-6xl gap-10 px-5 py-20 md:grid-cols-[1fr_0.9fr] md:items-center md:px-8 md:py-24">
<div>
<p
className={`${mono.className} mb-4 flex items-center gap-2 text-[11px] tracking-[0.2em] text-[#F6F1E7]/80`}
>
<Mail className="h-3.5 w-3.5" />
THE DISPATCH
</p>
<h2
className={`${display.className} text-4xl leading-[1.05] md:text-5xl`}
>
One letter a month. One place <em>worth the airfare.</em>
</h2>
<p className="mt-4 max-w-md text-[#F6F1E7]/80">
No listicles, no affiliate noise. A single destination our editors
have just returned from, and exactly how to do it well.
</p>
</div>
<div>
{subscribed ? (
<div className="flex items-center gap-3 border border-[#F6F1E7]/40 bg-[#F6F1E7]/10 px-5 py-6">
<BadgeCheck className="h-6 w-6 shrink-0" />
<p className="text-sm leading-relaxed">{NEWSLETTER_DONE}</p>
</div>
) : (
<form
onSubmit={(e) => {
e.preventDefault();
if (email.trim()) setSubscribed(true);
}}
className="flex flex-col gap-3 sm:flex-row"
>
<label
className="flex flex-1 items-center gap-2.5 border border-[#F6F1E7]/40 bg-[#F6F1E7]/10 px-4 py-3.5"
aria-label="Email address"
>
<Mail className="h-4 w-4 shrink-0 text-[#F6F1E7]/70" />
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com"
className="w-full bg-transparent text-sm text-[#F6F1E7] outline-none placeholder:text-[#F6F1E7]/50"
/>
</label>
<button
type="submit"
className="group inline-flex items-center justify-center gap-2 bg-[#2A2018] px-6 py-3.5 text-sm font-semibold text-[#F6F1E7] transition-colors hover:bg-[#1c150e]"
>
Subscribe
<Send className="h-4 w-4 transition-transform group-hover:translate-x-0.5" />
</button>
</form>
)}
<p className="mt-3 text-[11px] text-[#F6F1E7]/60">
Around 40,000 travellers read it. Unsubscribe in one click.
</p>
</div>
</div>
</section>
{/* ── Footer ──────────────────────────────────────────────── */}
<footer className="relative z-10 bg-[#2A2018] text-[#F6F1E7]">
<div className="mx-auto max-w-6xl px-5 py-16 md:px-8">
<div className="grid gap-10 md:grid-cols-[1.3fr_repeat(4,0.7fr)]">
<div>
<a href="#" className="flex items-center gap-2.5">
<span className="grid h-8 w-8 place-items-center rounded-full border border-[#E08B4C] text-[#E08B4C]">
<Compass className="h-4 w-4" strokeWidth={1.8} />
</span>
<span className={`${display.className} text-xl`}>Wayfare</span>
</a>
<p className="mt-5 max-w-[16rem] text-sm leading-relaxed text-[#F6F1E7]/50">
The journal of elsewhere. A booking house run by travel editors,
printed on the web since 2019.
</p>
<p
className={`${mono.className} mt-5 text-[11px] tracking-wide text-[#F6F1E7]/35`}
>
51.51 N / 0.13 W · LONDON
</p>
</div>
{FOOTER_COLS.map((col) => (
<nav key={col.title}>
<p
className={`${mono.className} mb-4 text-[11px] tracking-[0.18em] text-[#F6F1E7]/40`}
>
{col.title.toUpperCase()}
</p>
{col.links.map((link) => (
<a
key={link}
href="#"
className="block py-1.5 text-sm text-[#F6F1E7]/60 transition-colors hover:text-[#F6F1E7]"
>
{link}
</a>
))}
</nav>
))}
</div>
<div className="mt-14 flex flex-wrap items-center justify-between gap-4 border border-[#F6F1E7]/10 pt-6">
<p className={`${mono.className} text-[11px] text-[#F6F1E7]/40`}>
© 2026 WAYFARE TRAVEL CO. · ISSUE 14
</p>
<p className="text-[11px] text-[#F6F1E7]/40">
Part of{" "}
<a
href="/ui/templates/"
className="text-[#E08B4C] underline-offset-4 transition-colors hover:text-[#F6F1E7] hover:underline"
>
UI Library Templates
</a>
</p>
</div>
</div>
</footer>
</div>
);
}