e1b7fd4b0d
Replaces PassportFlightRow with the "Classic" boarding-pass design
selected from design/boarding-pass-variants.html.
Anatomy:
- BoardingPassShape: custom SwiftUI Shape — rounded rect with two
semicircular cutouts at the perforation column (top + bottom).
Hand-drawn clockwise from top-left so the path closes cleanly.
- Stub (88pt wide): orange linear-gradient background. "WN"
monospaced eyebrow at 9pt/tracking 2.2 at top, padded flight
number ("0007") at 28pt monospaced heavy in the middle,
BarcodeStripe at the bottom.
- BarcodeStripe: Canvas-drawn faux barcode — 16-element width
pattern cycles across the width, even indices fill, odd are gaps.
- Body (flex): card background. Route IATA pair at 24pt mono heavy
with an orange ▶ between, date in 10pt tracked mono uppercase,
meta row of EQP / TAIL / MI metadata with mono labels in tertiary
ink and values in primary.
- Perforation: GeometryReader-driven dashed line drawn between stub
and body, inset top/bottom to stop short of the cutouts.
Distance is recomputed inline via haversine from the AirportDatabase
since the row doesn't get the FlightHistoryStore passed in. Mile
display only — clean integer rounded value.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1146 lines
36 KiB
HTML
1146 lines
36 KiB
HTML
<!doctype html>
|
|
<html lang="en" data-theme="dark">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>Boarding Pass — Six Variations</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=Fraunces:opsz,wght@9..144,400;9..144,700;9..144,900&family=JetBrains+Mono:wght@400;500;700;800&family=Major+Mono+Display&family=Playfair+Display+SC:wght@700;900&family=Cormorant+Garamond:ital,wght@0,400;0,700;1,700&family=Allerta+Stencil&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--orange: #FF5722;
|
|
--orange-deep: #D9461A;
|
|
--orange-soft: #FF8C59;
|
|
--amber: #FFB938;
|
|
|
|
--navy: #0A1424;
|
|
--navy-mid: #142440;
|
|
--navy-deep: #050A14;
|
|
--navy-soft: #0F1E38;
|
|
|
|
--cream: #F4ECD8;
|
|
--cream-soft: #FAF5E8;
|
|
--cream-deep: #E8DDC4;
|
|
--kraft: #C9B68A;
|
|
|
|
--gold: #C8A951;
|
|
--gold-deep: #8A6F25;
|
|
--gold-light: #E7CC7F;
|
|
|
|
--postal-red: #B53026;
|
|
}
|
|
|
|
[data-theme="dark"] {
|
|
--bg: var(--navy);
|
|
--bg-2: var(--navy-deep);
|
|
--surface: var(--navy-mid);
|
|
--surface-2: var(--navy-soft);
|
|
--ink: #F4ECD8;
|
|
--ink-2: rgba(244, 236, 216, 0.65);
|
|
--ink-3: rgba(244, 236, 216, 0.4);
|
|
--hairline: rgba(255, 255, 255, 0.08);
|
|
--shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
|
|
--frame-bezel: #1a1a1a;
|
|
}
|
|
|
|
[data-theme="light"] {
|
|
--bg: var(--cream);
|
|
--bg-2: var(--cream-deep);
|
|
--surface: var(--cream-soft);
|
|
--surface-2: #FFFFFF;
|
|
--ink: #0A1424;
|
|
--ink-2: rgba(10, 20, 36, 0.65);
|
|
--ink-3: rgba(10, 20, 36, 0.45);
|
|
--hairline: rgba(0, 0, 0, 0.08);
|
|
--shadow: 0 8px 22px rgba(60, 30, 0, 0.12);
|
|
--frame-bezel: #d9d3c0;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
html, body {
|
|
margin: 0;
|
|
padding: 0;
|
|
background: var(--bg);
|
|
color: var(--ink);
|
|
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
transition: background 240ms ease;
|
|
}
|
|
|
|
.page { max-width: 1240px; margin: 0 auto; padding: 32px 24px 96px; }
|
|
|
|
.page-head {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 24px;
|
|
flex-wrap: wrap;
|
|
padding-bottom: 16px;
|
|
border-bottom: 1px solid var(--hairline);
|
|
margin-bottom: 40px;
|
|
}
|
|
|
|
.page-eyebrow {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.22em;
|
|
text-transform: uppercase;
|
|
color: var(--orange);
|
|
}
|
|
|
|
.page-title {
|
|
font-family: "Fraunces", serif;
|
|
font-variation-settings: "opsz" 144, "wght" 900;
|
|
font-size: 56px;
|
|
line-height: 1;
|
|
letter-spacing: -0.02em;
|
|
margin: 4px 0 0;
|
|
}
|
|
|
|
.page-sub {
|
|
font-size: 14px;
|
|
color: var(--ink-2);
|
|
max-width: 540px;
|
|
margin: 8px 0 0;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.theme-toggle {
|
|
margin-left: auto;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 8px 14px;
|
|
border: 1px solid var(--hairline);
|
|
border-radius: 999px;
|
|
cursor: pointer;
|
|
background: var(--surface);
|
|
color: var(--ink);
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.15em;
|
|
text-transform: uppercase;
|
|
user-select: none;
|
|
}
|
|
.theme-toggle:hover { border-color: var(--orange); color: var(--orange); }
|
|
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
|
|
gap: 48px;
|
|
}
|
|
|
|
.variant {
|
|
background: var(--bg-2);
|
|
border-radius: 28px;
|
|
padding: 28px 24px 36px;
|
|
border: 1px solid var(--hairline);
|
|
}
|
|
|
|
.variant-head {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 16px;
|
|
margin-bottom: 4px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.variant-no {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.2em;
|
|
color: var(--orange);
|
|
}
|
|
.variant-name {
|
|
font-family: "Fraunces", serif;
|
|
font-variation-settings: "opsz" 96, "wght" 800;
|
|
font-size: 26px;
|
|
line-height: 1.1;
|
|
margin: 0;
|
|
}
|
|
.variant-note {
|
|
font-size: 13px;
|
|
color: var(--ink-2);
|
|
line-height: 1.5;
|
|
margin: 8px 0 24px;
|
|
max-width: 38ch;
|
|
}
|
|
|
|
.phone {
|
|
width: 100%;
|
|
max-width: 394px;
|
|
margin: 0 auto;
|
|
background: var(--frame-bezel);
|
|
padding: 10px;
|
|
border-radius: 44px;
|
|
box-shadow: var(--shadow);
|
|
}
|
|
.phone-screen {
|
|
background: var(--bg);
|
|
border-radius: 36px;
|
|
padding: 18px 14px 22px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.island {
|
|
width: 92px; height: 28px;
|
|
background: #000;
|
|
border-radius: 16px;
|
|
margin: 0 auto 14px;
|
|
}
|
|
.rows { display: flex; flex-direction: column; gap: 12px; }
|
|
|
|
/* =========================================================
|
|
SHARED — boarding pass anatomy primitives
|
|
========================================================= */
|
|
.bp-row {
|
|
display: grid;
|
|
border-radius: 14px;
|
|
overflow: hidden;
|
|
position: relative;
|
|
min-height: 108px;
|
|
cursor: pointer;
|
|
transition: transform 180ms ease;
|
|
}
|
|
.bp-row:hover { transform: translateY(-1px); }
|
|
|
|
/* Two-circle "punch" effect on perforation column */
|
|
.punch::after {
|
|
content: "";
|
|
position: absolute;
|
|
width: 14px; height: 14px;
|
|
background: var(--bg);
|
|
border-radius: 50%;
|
|
top: -8px;
|
|
box-shadow: 0 116px 0 var(--bg);
|
|
}
|
|
|
|
/* Perforation dashed line */
|
|
.perf {
|
|
position: absolute;
|
|
top: 8px; bottom: 8px;
|
|
width: 1px;
|
|
background: repeating-linear-gradient(
|
|
to bottom,
|
|
currentColor 0 4px,
|
|
transparent 4px 8px
|
|
);
|
|
opacity: 0.32;
|
|
}
|
|
|
|
/* Barcode strip — repeating vertical lines */
|
|
.barcode {
|
|
height: 12px;
|
|
background-image: repeating-linear-gradient(
|
|
to right,
|
|
currentColor 0 1px,
|
|
transparent 1px 3px,
|
|
currentColor 3px 4px,
|
|
transparent 4px 7px,
|
|
currentColor 7px 9px,
|
|
transparent 9px 11px
|
|
);
|
|
border-radius: 1px;
|
|
opacity: 0.95;
|
|
}
|
|
|
|
/* =========================================================
|
|
V1 — THE CLASSIC (orange stub, left)
|
|
========================================================= */
|
|
.v1 { grid-template-columns: 88px 1fr; background: var(--surface); }
|
|
.v1 .perf { left: 88px; color: var(--ink); }
|
|
.v1 .punch::after { left: 82px; }
|
|
.v1 .punch:nth-child(2)::after { top: auto; bottom: -8px; box-shadow: 0 -116px 0 var(--bg); }
|
|
|
|
.v1-stub {
|
|
background: linear-gradient(135deg, var(--orange) 0%, var(--orange-deep) 100%);
|
|
padding: 14px 12px 12px;
|
|
color: white;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
}
|
|
.v1-stub-iata {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 9px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.22em;
|
|
opacity: 0.85;
|
|
}
|
|
.v1-stub-num {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-weight: 800;
|
|
font-size: 28px;
|
|
line-height: 1;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
.v1-body { padding: 14px 16px; }
|
|
.v1-route {
|
|
display: flex; align-items: baseline; gap: 8px;
|
|
margin-bottom: 4px;
|
|
}
|
|
.v1-iata {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-weight: 800;
|
|
font-size: 24px;
|
|
letter-spacing: -0.02em;
|
|
color: var(--ink);
|
|
}
|
|
.v1-arrow {
|
|
color: var(--orange);
|
|
font-weight: 900;
|
|
font-size: 13px;
|
|
}
|
|
.v1-date {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.18em;
|
|
color: var(--ink-3);
|
|
text-transform: uppercase;
|
|
margin-bottom: 8px;
|
|
}
|
|
.v1-meta {
|
|
display: flex; gap: 16px;
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 10px;
|
|
color: var(--ink-2);
|
|
}
|
|
.v1-meta dt {
|
|
display: inline; color: var(--ink-3);
|
|
margin-right: 4px; letter-spacing: 0.1em; text-transform: uppercase;
|
|
}
|
|
.v1-meta dd { display: inline; margin: 0 12px 0 0; font-weight: 600; color: var(--ink); }
|
|
|
|
/* =========================================================
|
|
V2 — REVERSE STUB (stub on right, photo-thumbnail left)
|
|
The stub is the "tear here" piece on the right.
|
|
Photo of airframe sits on the body's left edge.
|
|
========================================================= */
|
|
.v2 { grid-template-columns: 1fr 86px; background: var(--surface); }
|
|
.v2 .perf { right: 86px; color: var(--ink); }
|
|
.v2 .punch::after { right: 80px; }
|
|
.v2 .punch:nth-child(2)::after { top: auto; bottom: -8px; box-shadow: 0 -116px 0 var(--bg); }
|
|
|
|
.v2-body {
|
|
display: grid;
|
|
grid-template-columns: 70px 1fr;
|
|
gap: 12px;
|
|
padding: 12px 12px 12px 12px;
|
|
}
|
|
.v2-photo {
|
|
background-size: cover;
|
|
background-position: center;
|
|
background-color: #1c2a40;
|
|
border-radius: 8px;
|
|
}
|
|
.v2-info {
|
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
padding: 2px 0;
|
|
}
|
|
.v2-route {
|
|
display: flex; align-items: baseline; gap: 6px;
|
|
}
|
|
.v2-iata {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-weight: 800;
|
|
font-size: 22px;
|
|
letter-spacing: -0.02em;
|
|
color: var(--ink);
|
|
line-height: 1;
|
|
}
|
|
.v2-arrow {
|
|
color: var(--orange);
|
|
font-weight: 900;
|
|
font-size: 12px;
|
|
}
|
|
.v2-tag {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
color: var(--ink-2);
|
|
letter-spacing: 0.06em;
|
|
margin-top: 6px;
|
|
}
|
|
.v2-meta {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 9px;
|
|
color: var(--ink-3);
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
}
|
|
.v2-stub {
|
|
background: linear-gradient(225deg, var(--orange) 0%, var(--orange-deep) 100%);
|
|
padding: 14px 10px;
|
|
color: white;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
.v2-stub-num {
|
|
writing-mode: vertical-rl;
|
|
transform: rotate(180deg);
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-weight: 800;
|
|
font-size: 22px;
|
|
letter-spacing: 0.06em;
|
|
line-height: 1;
|
|
}
|
|
.v2-stub-iata {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 9px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.22em;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
/* =========================================================
|
|
V3 — HERITAGE NAVY (mid-century cream pass + navy stub)
|
|
1960s Pan Am / Braniff style. Serif logotype. Cream paper.
|
|
Stub is small dignified navy block.
|
|
========================================================= */
|
|
.v3 { grid-template-columns: 100px 1fr; background: var(--cream); color: var(--navy); }
|
|
.v3 .perf {
|
|
left: 100px;
|
|
color: var(--navy);
|
|
background: repeating-linear-gradient(to bottom, var(--navy) 0 2px, transparent 2px 6px);
|
|
opacity: 0.3;
|
|
}
|
|
.v3 .punch::after { left: 94px; background: var(--bg-2); }
|
|
.v3 .punch:nth-child(2)::after { top: auto; bottom: -8px; box-shadow: 0 -116px 0 var(--bg-2); }
|
|
|
|
.v3-stub {
|
|
background: var(--navy);
|
|
color: var(--cream);
|
|
padding: 12px 12px;
|
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
}
|
|
.v3-airline {
|
|
font-family: "Playfair Display SC", serif;
|
|
font-weight: 900;
|
|
font-size: 14px;
|
|
letter-spacing: 0.05em;
|
|
line-height: 1.05;
|
|
color: var(--cream);
|
|
}
|
|
.v3-stub-num {
|
|
font-family: "Cormorant Garamond", serif;
|
|
font-weight: 700;
|
|
font-size: 26px;
|
|
line-height: 1;
|
|
color: var(--gold-light);
|
|
font-style: italic;
|
|
}
|
|
.v3-stub-num small {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-style: normal;
|
|
font-size: 9px;
|
|
display: block;
|
|
letter-spacing: 0.2em;
|
|
color: var(--cream);
|
|
opacity: 0.7;
|
|
margin-bottom: 2px;
|
|
}
|
|
.v3-body {
|
|
padding: 12px 16px;
|
|
color: var(--navy);
|
|
}
|
|
.v3-route {
|
|
font-family: "Cormorant Garamond", serif;
|
|
font-weight: 700;
|
|
font-size: 22px;
|
|
line-height: 1.1;
|
|
margin-bottom: 2px;
|
|
}
|
|
.v3-route i {
|
|
font-style: italic;
|
|
color: var(--gold-deep);
|
|
font-weight: 400;
|
|
margin: 0 4px;
|
|
font-size: 17px;
|
|
}
|
|
.v3-meta {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 10px;
|
|
color: rgba(10, 20, 36, 0.6);
|
|
letter-spacing: 0.08em;
|
|
margin-bottom: 8px;
|
|
text-transform: uppercase;
|
|
}
|
|
.v3-date {
|
|
font-family: "Cormorant Garamond", serif;
|
|
font-style: italic;
|
|
font-size: 13px;
|
|
color: var(--navy);
|
|
border-top: 1px dashed rgba(10, 20, 36, 0.25);
|
|
padding-top: 6px;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
/* =========================================================
|
|
V4 — FOIL PREMIUM (cream + gold)
|
|
First-class energy. Embossed feel. Gold gradient on the
|
|
stub mimics foil block printing.
|
|
========================================================= */
|
|
.v4 { grid-template-columns: 96px 1fr; background: var(--cream-soft); color: var(--navy); }
|
|
.v4 .perf { left: 96px; color: var(--gold-deep); }
|
|
.v4 .punch::after { left: 90px; background: var(--bg-2); }
|
|
.v4 .punch:nth-child(2)::after { top: auto; bottom: -8px; box-shadow: 0 -116px 0 var(--bg-2); }
|
|
|
|
.v4-stub {
|
|
background:
|
|
linear-gradient(135deg,
|
|
var(--gold) 0%,
|
|
var(--gold-light) 40%,
|
|
var(--gold) 65%,
|
|
var(--gold-deep) 100%);
|
|
padding: 12px 12px;
|
|
color: #2A1E08;
|
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.v4-stub::after {
|
|
/* Foil shimmer streak */
|
|
content: "";
|
|
position: absolute;
|
|
top: 0; bottom: 0;
|
|
left: 30%; right: 30%;
|
|
background: linear-gradient(to right, transparent, rgba(255,255,255,0.4), transparent);
|
|
transform: skewX(-20deg);
|
|
pointer-events: none;
|
|
}
|
|
.v4-eyebrow {
|
|
font-family: "Allerta Stencil", sans-serif;
|
|
font-size: 10px;
|
|
letter-spacing: 0.2em;
|
|
text-transform: uppercase;
|
|
line-height: 1;
|
|
color: #2A1E08;
|
|
position: relative;
|
|
}
|
|
.v4-stub-num {
|
|
font-family: "Cormorant Garamond", serif;
|
|
font-weight: 700;
|
|
font-size: 30px;
|
|
font-style: italic;
|
|
line-height: 1;
|
|
position: relative;
|
|
}
|
|
.v4-stub-num small {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-weight: 700;
|
|
font-style: normal;
|
|
font-size: 9px;
|
|
letter-spacing: 0.18em;
|
|
display: block;
|
|
margin-bottom: 4px;
|
|
}
|
|
.v4-body { padding: 14px 16px; }
|
|
.v4-route {
|
|
font-family: "Cormorant Garamond", serif;
|
|
font-weight: 700;
|
|
font-size: 26px;
|
|
line-height: 1;
|
|
color: var(--navy);
|
|
margin-bottom: 4px;
|
|
}
|
|
.v4-route i { font-style: italic; color: var(--gold-deep); margin: 0 4px; font-weight: 400; }
|
|
.v4-meta {
|
|
font-family: "Allerta Stencil", sans-serif;
|
|
font-size: 10px;
|
|
letter-spacing: 0.15em;
|
|
color: rgba(10, 20, 36, 0.7);
|
|
margin-bottom: 6px;
|
|
}
|
|
.v4-foot {
|
|
display: flex; justify-content: space-between;
|
|
border-top: 0.5px solid rgba(138, 111, 37, 0.4);
|
|
padding-top: 6px;
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 10px;
|
|
color: var(--navy);
|
|
}
|
|
.v4-foot strong { color: var(--gold-deep); }
|
|
|
|
/* =========================================================
|
|
V5 — BRUTALIST MONO (black stub, terminal aesthetic)
|
|
Hi-contrast. Scanlines. Reads like a UNIX boarding pass.
|
|
========================================================= */
|
|
.v5 { grid-template-columns: 76px 1fr; background: #0c0c0c; color: var(--cream); }
|
|
.v5 .perf { left: 76px; color: var(--cream); }
|
|
.v5 .punch::after { left: 70px; }
|
|
.v5 .punch:nth-child(2)::after { top: auto; bottom: -8px; box-shadow: 0 -116px 0 var(--bg); }
|
|
.v5::before {
|
|
content: "";
|
|
position: absolute; inset: 0;
|
|
background-image: repeating-linear-gradient(
|
|
to bottom,
|
|
rgba(255,255,255,0.02) 0 1px,
|
|
transparent 1px 3px
|
|
);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.v5-stub {
|
|
background: #000;
|
|
color: var(--orange);
|
|
padding: 12px 10px;
|
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
align-items: flex-start;
|
|
border-right: 2px solid var(--orange);
|
|
}
|
|
.v5-stub-iata {
|
|
font-family: "Major Mono Display", monospace;
|
|
font-size: 11px;
|
|
letter-spacing: 0.15em;
|
|
color: var(--cream);
|
|
opacity: 0.6;
|
|
}
|
|
.v5-stub-num {
|
|
font-family: "Major Mono Display", monospace;
|
|
font-size: 22px;
|
|
line-height: 1;
|
|
color: var(--orange);
|
|
}
|
|
.v5-stub-tag {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 8px;
|
|
color: var(--orange);
|
|
letter-spacing: 0.2em;
|
|
opacity: 0.7;
|
|
}
|
|
.v5-body {
|
|
padding: 12px 14px;
|
|
color: var(--cream);
|
|
}
|
|
.v5-tag {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 9px;
|
|
color: var(--orange);
|
|
letter-spacing: 0.22em;
|
|
margin-bottom: 6px;
|
|
text-transform: uppercase;
|
|
}
|
|
.v5-route {
|
|
font-family: "Major Mono Display", monospace;
|
|
font-size: 30px;
|
|
letter-spacing: -0.02em;
|
|
line-height: 1;
|
|
color: var(--cream);
|
|
}
|
|
.v5-route .arrow {
|
|
color: var(--orange);
|
|
font-size: 16px;
|
|
margin: 0 6px;
|
|
}
|
|
.v5-meta {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 10px;
|
|
color: rgba(244, 236, 216, 0.55);
|
|
letter-spacing: 0.1em;
|
|
margin-top: 8px;
|
|
text-transform: uppercase;
|
|
display: flex; gap: 14px;
|
|
}
|
|
.v5-meta strong { color: var(--cream); font-weight: 700; }
|
|
|
|
/* =========================================================
|
|
V6 — PHOTO STUB HYBRID
|
|
The "stub" is the airframe photo itself, with text overlay.
|
|
Body holds the data. Most photogenic option.
|
|
========================================================= */
|
|
.v6 { grid-template-columns: 112px 1fr; background: var(--surface); }
|
|
.v6 .perf { left: 112px; color: var(--ink); }
|
|
.v6 .punch::after { left: 106px; }
|
|
.v6 .punch:nth-child(2)::after { top: auto; bottom: -8px; box-shadow: 0 -116px 0 var(--bg); }
|
|
|
|
.v6-photo {
|
|
position: relative;
|
|
background-size: cover;
|
|
background-position: center;
|
|
background-color: #1c2a40;
|
|
overflow: hidden;
|
|
}
|
|
.v6-photo::before {
|
|
/* darkening scrim */
|
|
content: "";
|
|
position: absolute; inset: 0;
|
|
background: linear-gradient(135deg, rgba(0,0,0,0.6), rgba(0,0,0,0.25) 60%, rgba(0,0,0,0.6));
|
|
}
|
|
.v6-overlay {
|
|
position: relative; z-index: 1;
|
|
padding: 12px 10px;
|
|
color: white;
|
|
height: 100%;
|
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
}
|
|
.v6-overlay .iata {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 9px;
|
|
letter-spacing: 0.22em;
|
|
opacity: 0.85;
|
|
}
|
|
.v6-overlay .num {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-weight: 800;
|
|
font-size: 26px;
|
|
letter-spacing: -0.02em;
|
|
line-height: 1;
|
|
text-shadow: 0 1px 8px rgba(0,0,0,0.5);
|
|
}
|
|
.v6-overlay .tail {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 9px;
|
|
letter-spacing: 0.15em;
|
|
opacity: 0.85;
|
|
}
|
|
.v6-body { padding: 14px 16px; display: flex; flex-direction: column; justify-content: space-between; }
|
|
.v6-route {
|
|
display: flex; align-items: baseline; gap: 8px;
|
|
font-family: "Fraunces", serif;
|
|
font-variation-settings: "opsz" 144, "wght" 900;
|
|
font-size: 24px;
|
|
color: var(--ink);
|
|
line-height: 1;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
.v6-route span { color: var(--orange); font-family: "JetBrains Mono", monospace; font-size: 14px; font-weight: 800; }
|
|
.v6-date {
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 10px;
|
|
letter-spacing: 0.18em;
|
|
color: var(--ink-3);
|
|
text-transform: uppercase;
|
|
margin: 4px 0 8px;
|
|
}
|
|
.v6-meta {
|
|
display: flex; gap: 6px;
|
|
font-family: "JetBrains Mono", monospace;
|
|
font-size: 10px;
|
|
}
|
|
.v6-meta span {
|
|
background: var(--bg);
|
|
padding: 3px 7px;
|
|
border-radius: 4px;
|
|
color: var(--ink-2);
|
|
letter-spacing: 0.06em;
|
|
}
|
|
|
|
/* ============== Footer note ============== */
|
|
.footer {
|
|
margin-top: 64px;
|
|
padding-top: 24px;
|
|
border-top: 1px solid var(--hairline);
|
|
font-size: 12px;
|
|
color: var(--ink-3);
|
|
line-height: 1.6;
|
|
}
|
|
.footer code {
|
|
font-family: "JetBrains Mono", monospace;
|
|
background: var(--surface);
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
color: var(--orange);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="page">
|
|
|
|
<header class="page-head">
|
|
<div>
|
|
<div class="page-eyebrow">BOARDING PASS · 6 DIRECTIONS</div>
|
|
<h1 class="page-title">Same anatomy, six personalities</h1>
|
|
<p class="page-sub">All six are boarding-pass-shaped (stub + perforation + body), but each commits to a distinct visual personality. Pick one and we port it to SwiftUI.</p>
|
|
</div>
|
|
<button class="theme-toggle" id="themeToggle" type="button">
|
|
<span id="themeLabel">DARK</span>
|
|
</button>
|
|
</header>
|
|
|
|
<div class="grid">
|
|
|
|
<!-- V1 — THE CLASSIC -->
|
|
<section class="variant">
|
|
<div class="variant-head"><span class="variant-no">01</span><h2 class="variant-name">The Classic</h2></div>
|
|
<p class="variant-note">Refined version of the original. Orange stub on the left, vertical perforation, faux barcode footer, condensed mono numerics.</p>
|
|
<div class="phone"><div class="phone-screen"><div class="island"></div><div class="rows">
|
|
<div class="bp-row v1 punch">
|
|
<div class="perf"></div>
|
|
<div class="v1-stub">
|
|
<div class="v1-stub-iata">WN</div>
|
|
<div class="v1-stub-num">0007</div>
|
|
<div class="barcode"></div>
|
|
</div>
|
|
<div class="v1-body">
|
|
<div class="v1-route">
|
|
<span class="v1-iata">DAL</span>
|
|
<span class="v1-arrow">▶</span>
|
|
<span class="v1-iata">HOU</span>
|
|
</div>
|
|
<div class="v1-date">27 MAY 26</div>
|
|
<dl class="v1-meta">
|
|
<dt>EQP</dt><dd>B737</dd>
|
|
<dt>TAIL</dt><dd>N7747C</dd>
|
|
<dt>NM</dt><dd>239</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v1 punch">
|
|
<div class="perf"></div>
|
|
<div class="v1-stub">
|
|
<div class="v1-stub-iata">WN</div>
|
|
<div class="v1-stub-num">1942</div>
|
|
<div class="barcode"></div>
|
|
</div>
|
|
<div class="v1-body">
|
|
<div class="v1-route">
|
|
<span class="v1-iata">LAS</span>
|
|
<span class="v1-arrow">▶</span>
|
|
<span class="v1-iata">DAL</span>
|
|
</div>
|
|
<div class="v1-date">27 JAN 24</div>
|
|
<dl class="v1-meta">
|
|
<dt>EQP</dt><dd>B738</dd>
|
|
<dt>TAIL</dt><dd>N281WN</dd>
|
|
<dt>NM</dt><dd>1056</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v1 punch">
|
|
<div class="perf"></div>
|
|
<div class="v1-stub">
|
|
<div class="v1-stub-iata">WN</div>
|
|
<div class="v1-stub-num">5476</div>
|
|
<div class="barcode"></div>
|
|
</div>
|
|
<div class="v1-body">
|
|
<div class="v1-route">
|
|
<span class="v1-iata">BNA</span>
|
|
<span class="v1-arrow">▶</span>
|
|
<span class="v1-iata">BOS</span>
|
|
</div>
|
|
<div class="v1-date">19 MAR 24</div>
|
|
<dl class="v1-meta">
|
|
<dt>EQP</dt><dd>B38M</dd>
|
|
<dt>TAIL</dt><dd>N8642E</dd>
|
|
<dt>NM</dt><dd>943</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div></div></div>
|
|
</section>
|
|
|
|
<!-- V2 — REVERSE STUB -->
|
|
<section class="variant">
|
|
<div class="variant-head"><span class="variant-no">02</span><h2 class="variant-name">Reverse Stub</h2></div>
|
|
<p class="variant-note">Stub flipped to the right (mirrors what you keep after the gate agent tears your pass). Photo thumb sits on the body's left. Flight number rotated vertical in the stub.</p>
|
|
<div class="phone"><div class="phone-screen"><div class="island"></div><div class="rows">
|
|
<div class="bp-row v2 punch">
|
|
<div class="perf"></div>
|
|
<div class="v2-body">
|
|
<div class="v2-photo" style="background-image:url('https://images.unsplash.com/photo-1583416750470-965b2707b355?auto=format&fit=crop&w=400&q=70')"></div>
|
|
<div class="v2-info">
|
|
<div>
|
|
<div class="v2-route"><span class="v2-iata">DAL</span><span class="v2-arrow">▶</span><span class="v2-iata">HOU</span></div>
|
|
<div class="v2-tag">B737 · N7747C</div>
|
|
</div>
|
|
<div class="v2-meta">27 May 26 · 239 nm</div>
|
|
</div>
|
|
</div>
|
|
<div class="v2-stub">
|
|
<div class="v2-stub-iata">WN</div>
|
|
<div class="v2-stub-num">0007</div>
|
|
<div class="v2-stub-iata">★</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v2 punch">
|
|
<div class="perf"></div>
|
|
<div class="v2-body">
|
|
<div class="v2-photo" style="background-image:url('https://images.unsplash.com/photo-1542296332-2e4473faf563?auto=format&fit=crop&w=400&q=70')"></div>
|
|
<div class="v2-info">
|
|
<div>
|
|
<div class="v2-route"><span class="v2-iata">LAS</span><span class="v2-arrow">▶</span><span class="v2-iata">DAL</span></div>
|
|
<div class="v2-tag">B738 · N281WN</div>
|
|
</div>
|
|
<div class="v2-meta">27 Jan 24 · 1056 nm</div>
|
|
</div>
|
|
</div>
|
|
<div class="v2-stub">
|
|
<div class="v2-stub-iata">WN</div>
|
|
<div class="v2-stub-num">1942</div>
|
|
<div class="v2-stub-iata">★</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v2 punch">
|
|
<div class="perf"></div>
|
|
<div class="v2-body">
|
|
<div class="v2-photo" style="background-image:url('https://images.unsplash.com/photo-1436491865332-7a61a109cc05?auto=format&fit=crop&w=400&q=70')"></div>
|
|
<div class="v2-info">
|
|
<div>
|
|
<div class="v2-route"><span class="v2-iata">BNA</span><span class="v2-arrow">▶</span><span class="v2-iata">BOS</span></div>
|
|
<div class="v2-tag">B38M · N8642E</div>
|
|
</div>
|
|
<div class="v2-meta">19 Mar 24 · 943 nm</div>
|
|
</div>
|
|
</div>
|
|
<div class="v2-stub">
|
|
<div class="v2-stub-iata">WN</div>
|
|
<div class="v2-stub-num">5476</div>
|
|
<div class="v2-stub-iata">★</div>
|
|
</div>
|
|
</div>
|
|
</div></div></div>
|
|
</section>
|
|
|
|
<!-- V3 — HERITAGE NAVY -->
|
|
<section class="variant">
|
|
<div class="variant-head"><span class="variant-no">03</span><h2 class="variant-name">Heritage Navy</h2></div>
|
|
<p class="variant-note">Mid-century airline aesthetic. Cream paper with a small navy stub. Cormorant italic for cities, Playfair small caps for the airline mark. Quietly dignified — feels like a 1968 TWA pass.</p>
|
|
<div class="phone"><div class="phone-screen"><div class="island"></div><div class="rows">
|
|
<div class="bp-row v3 punch">
|
|
<div class="perf"></div>
|
|
<div class="v3-stub">
|
|
<div class="v3-airline">South<br>west</div>
|
|
<div class="v3-stub-num"><small>FLT</small>0007</div>
|
|
</div>
|
|
<div class="v3-body">
|
|
<h3 class="v3-route">Dallas<i>to</i>Houston</h3>
|
|
<div class="v3-meta">DAL → HOU · B737 · N7747C</div>
|
|
<div class="v3-date">Wednesday, May 27 · 239 mi</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v3 punch">
|
|
<div class="perf"></div>
|
|
<div class="v3-stub">
|
|
<div class="v3-airline">South<br>west</div>
|
|
<div class="v3-stub-num"><small>FLT</small>1942</div>
|
|
</div>
|
|
<div class="v3-body">
|
|
<h3 class="v3-route">Las Vegas<i>to</i>Dallas</h3>
|
|
<div class="v3-meta">LAS → DAL · B738 · N281WN</div>
|
|
<div class="v3-date">Saturday, January 27 · 1,056 mi</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v3 punch">
|
|
<div class="perf"></div>
|
|
<div class="v3-stub">
|
|
<div class="v3-airline">South<br>west</div>
|
|
<div class="v3-stub-num"><small>FLT</small>5476</div>
|
|
</div>
|
|
<div class="v3-body">
|
|
<h3 class="v3-route">Nashville<i>to</i>Boston</h3>
|
|
<div class="v3-meta">BNA → BOS · B38M · N8642E</div>
|
|
<div class="v3-date">Tuesday, March 19 · 943 mi</div>
|
|
</div>
|
|
</div>
|
|
</div></div></div>
|
|
</section>
|
|
|
|
<!-- V4 — FOIL PREMIUM -->
|
|
<section class="variant">
|
|
<div class="variant-head"><span class="variant-no">04</span><h2 class="variant-name">Foil Premium</h2></div>
|
|
<p class="variant-note">Cream pass + a gold-foil stub with a shimmer streak. Allerta Stencil + Cormorant italic + JetBrains Mono. Reads like a Concorde-era first-class pass.</p>
|
|
<div class="phone"><div class="phone-screen"><div class="island"></div><div class="rows">
|
|
<div class="bp-row v4 punch">
|
|
<div class="perf"></div>
|
|
<div class="v4-stub">
|
|
<div class="v4-eyebrow">SOUTHWEST</div>
|
|
<div class="v4-stub-num"><small>WN</small>0007</div>
|
|
<div class="v4-eyebrow">★ ★ ★</div>
|
|
</div>
|
|
<div class="v4-body">
|
|
<div class="v4-route">Dallas<i>to</i>Houston</div>
|
|
<div class="v4-meta">B737 · TAIL N7747C</div>
|
|
<div class="v4-foot">
|
|
<span>27 MAY 2026</span>
|
|
<span><strong>239</strong> NM</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v4 punch">
|
|
<div class="perf"></div>
|
|
<div class="v4-stub">
|
|
<div class="v4-eyebrow">SOUTHWEST</div>
|
|
<div class="v4-stub-num"><small>WN</small>1942</div>
|
|
<div class="v4-eyebrow">★ ★ ★</div>
|
|
</div>
|
|
<div class="v4-body">
|
|
<div class="v4-route">Las Vegas<i>to</i>Dallas</div>
|
|
<div class="v4-meta">B738 · TAIL N281WN</div>
|
|
<div class="v4-foot">
|
|
<span>27 JAN 2024</span>
|
|
<span><strong>1056</strong> NM</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v4 punch">
|
|
<div class="perf"></div>
|
|
<div class="v4-stub">
|
|
<div class="v4-eyebrow">SOUTHWEST</div>
|
|
<div class="v4-stub-num"><small>WN</small>5476</div>
|
|
<div class="v4-eyebrow">★ ★ ★</div>
|
|
</div>
|
|
<div class="v4-body">
|
|
<div class="v4-route">Nashville<i>to</i>Boston</div>
|
|
<div class="v4-meta">B38M · TAIL N8642E</div>
|
|
<div class="v4-foot">
|
|
<span>19 MAR 2024</span>
|
|
<span><strong>943</strong> NM</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div></div></div>
|
|
</section>
|
|
|
|
<!-- V5 — BRUTALIST MONO -->
|
|
<section class="variant">
|
|
<div class="variant-head"><span class="variant-no">05</span><h2 class="variant-name">Brutalist Terminal</h2></div>
|
|
<p class="variant-note">Pure black canvas. Major Mono Display for the route. Faux scanlines. Orange spine on the stub. Reads like a UNIX boarding pass printed at 4am.</p>
|
|
<div class="phone"><div class="phone-screen"><div class="island"></div><div class="rows">
|
|
<div class="bp-row v5 punch">
|
|
<div class="perf"></div>
|
|
<div class="v5-stub">
|
|
<div class="v5-stub-iata">WN</div>
|
|
<div class="v5-stub-num">0007</div>
|
|
<div class="v5-stub-tag">SWA</div>
|
|
</div>
|
|
<div class="v5-body">
|
|
<div class="v5-tag">FLT WN0007 · ETD 0830 CDT</div>
|
|
<div class="v5-route">DAL<span class="arrow">▶</span>HOU</div>
|
|
<div class="v5-meta">
|
|
<span>EQP <strong>B737</strong></span>
|
|
<span>TAIL <strong>N7747C</strong></span>
|
|
<span>NM <strong>239</strong></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v5 punch">
|
|
<div class="perf"></div>
|
|
<div class="v5-stub">
|
|
<div class="v5-stub-iata">WN</div>
|
|
<div class="v5-stub-num">1942</div>
|
|
<div class="v5-stub-tag">SWA</div>
|
|
</div>
|
|
<div class="v5-body">
|
|
<div class="v5-tag">FLT WN1942 · ETD 0900 PST</div>
|
|
<div class="v5-route">LAS<span class="arrow">▶</span>DAL</div>
|
|
<div class="v5-meta">
|
|
<span>EQP <strong>B738</strong></span>
|
|
<span>TAIL <strong>N281WN</strong></span>
|
|
<span>NM <strong>1056</strong></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v5 punch">
|
|
<div class="perf"></div>
|
|
<div class="v5-stub">
|
|
<div class="v5-stub-iata">WN</div>
|
|
<div class="v5-stub-num">5476</div>
|
|
<div class="v5-stub-tag">SWA</div>
|
|
</div>
|
|
<div class="v5-body">
|
|
<div class="v5-tag">FLT WN5476 · ETD 2010 CDT</div>
|
|
<div class="v5-route">BNA<span class="arrow">▶</span>BOS</div>
|
|
<div class="v5-meta">
|
|
<span>EQP <strong>B38M</strong></span>
|
|
<span>TAIL <strong>N8642E</strong></span>
|
|
<span>NM <strong>943</strong></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div></div></div>
|
|
</section>
|
|
|
|
<!-- V6 — PHOTO STUB HYBRID -->
|
|
<section class="variant">
|
|
<div class="variant-head"><span class="variant-no">06</span><h2 class="variant-name">Photo Stub Hybrid</h2></div>
|
|
<p class="variant-note">The stub IS the airframe photo. Flight number and tail overlay on top. Body holds the route in Fraunces serif. Most photogenic of the six.</p>
|
|
<div class="phone"><div class="phone-screen"><div class="island"></div><div class="rows">
|
|
<div class="bp-row v6 punch">
|
|
<div class="perf"></div>
|
|
<div class="v6-photo" style="background-image:url('https://images.unsplash.com/photo-1583416750470-965b2707b355?auto=format&fit=crop&w=500&q=70')">
|
|
<div class="v6-overlay">
|
|
<div class="iata">WN</div>
|
|
<div class="num">0007</div>
|
|
<div class="tail">N7747C</div>
|
|
</div>
|
|
</div>
|
|
<div class="v6-body">
|
|
<div class="v6-route">DAL <span>▶</span> HOU</div>
|
|
<div class="v6-date">27 MAY 2026 · 239 NM</div>
|
|
<div class="v6-meta"><span>B737</span><span>SWA</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v6 punch">
|
|
<div class="perf"></div>
|
|
<div class="v6-photo" style="background-image:url('https://images.unsplash.com/photo-1542296332-2e4473faf563?auto=format&fit=crop&w=500&q=70')">
|
|
<div class="v6-overlay">
|
|
<div class="iata">WN</div>
|
|
<div class="num">1942</div>
|
|
<div class="tail">N281WN</div>
|
|
</div>
|
|
</div>
|
|
<div class="v6-body">
|
|
<div class="v6-route">LAS <span>▶</span> DAL</div>
|
|
<div class="v6-date">27 JAN 2024 · 1056 NM</div>
|
|
<div class="v6-meta"><span>B738</span><span>SWA</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bp-row v6 punch">
|
|
<div class="perf"></div>
|
|
<div class="v6-photo" style="background-image:url('https://images.unsplash.com/photo-1436491865332-7a61a109cc05?auto=format&fit=crop&w=500&q=70')">
|
|
<div class="v6-overlay">
|
|
<div class="iata">WN</div>
|
|
<div class="num">5476</div>
|
|
<div class="tail">N8642E</div>
|
|
</div>
|
|
</div>
|
|
<div class="v6-body">
|
|
<div class="v6-route">BNA <span>▶</span> BOS</div>
|
|
<div class="v6-date">19 MAR 2024 · 943 NM</div>
|
|
<div class="v6-meta"><span>B38M</span><span>SWA</span></div>
|
|
</div>
|
|
</div>
|
|
</div></div></div>
|
|
</section>
|
|
|
|
</div>
|
|
|
|
<footer class="footer">
|
|
All six share the same DOM anatomy (<code>.bp-row</code> + <code>.perf</code> + <code>.punch</code>) so porting back to SwiftUI is straightforward — the differences live almost entirely in the stub side and typography choices.
|
|
</footer>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
const root = document.documentElement;
|
|
const btn = document.getElementById('themeToggle');
|
|
const label = document.getElementById('themeLabel');
|
|
btn.addEventListener('click', () => {
|
|
const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
|
|
root.setAttribute('data-theme', next);
|
|
label.textContent = next.toUpperCase();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|