// Seat selection screen — 3D tilted cabin map with cinematic detail
const ROW_COUNT = 28;
const SEAT_LETTERS = ["A","B","C","D","E","F"]; // 3-3 layout
const EMERGENCY_ROWS = [12, 13];
const PREMIUM_ROWS = [3, 4, 5, 6, 7];
const BLOCKED_SEATS = new Set([
"1A","1B","1C","2D","2E","2F","8C","9D","11A","14F","15B","16E","18A","19F","20C","21D","22A","23F","24B","25E","26C","27D","28A"
]);
function seatPrice(row, col) {
if (PREMIUM_ROWS.includes(row)) return 88;
if (EMERGENCY_ROWS.includes(row)) return 38;
if (col === "A" || col === "F") return 22; // window
if (col === "C" || col === "D") return 12; // aisle
return 0; // middle, free
}
function seatTier(row, col) {
if (PREMIUM_ROWS.includes(row)) return "premium";
if (EMERGENCY_ROWS.includes(row)) return "emergency";
if (col === "A" || col === "F") return "window";
if (col === "C" || col === "D") return "aisle";
return "standard";
}
function seatColor(tier, taken, sel) {
if (taken) return "rgba(11,26,43,.18)";
if (sel) return "var(--sun)";
if (tier === "premium") return "#1f3a5a";
if (tier === "emergency") return "#b54a1d";
return "white";
}
const ScreenSeats = ({ flight, query, selectedSeat, setSelectedSeat, onContinue, onBack }) => {
const [tilt, setTilt] = useState(true);
const [hovered, setHovered] = useState(null);
return (
{/* Title row */}
Step 02 — Pick your perch
Choose where you sit.
{flight.carrier.name} {flight.id} · {flight.aircraft} · {fmtDuration(flight.durationMin)} from {query.origin.city} to {query.dest.city}.
{/* Cabin + side rail */}
{/* Cabin stage */}
{/* Decorative grid */}
{/* Compass / labels */}
Cabin schematic · {flight.aircraft}
Direction of flight ↑
{/* The plane */}
{/* nose */}
{/* rows */}
{/* row letters */}
{SEAT_LETTERS.slice(0,3).map(l => {l})}
{SEAT_LETTERS.slice(3,6).map(l => {l})}
{Array.from({ length: ROW_COUNT }).map((_, idx) => {
const row = idx + 1;
const isExit = EMERGENCY_ROWS.includes(row);
return (
{row}
{SEAT_LETTERS.slice(0,3).map(c =>
)}
{isExit ? "EXIT" : ""}
{SEAT_LETTERS.slice(3,6).map(c =>
)}
{row}
{/* exit lights */}
{isExit && <>
>}
{/* premium divider */}
{row === 7 &&
↑ PREMIUM ECONOMY
}
);
})}
{/* Tail */}
{/* Floor shadow */}
{/* Right rail */}
Flight + seat
{flight.carrier.name} {flight.id} · {selectedSeat ? `Seat ${selectedSeat.row}${selectedSeat.col}` : "no seat yet"}
}
right={
Subtotal
{fmtMoney(flight.price + (selectedSeat ? seatPrice(selectedSeat.row, selectedSeat.col) : 0))}
}
secondary="Back"
onSecondary={onBack}
primary="Add passenger"
onPrimary={onContinue}
primaryDisabled={!selectedSeat}
/>
);
};
// ----- Cabin parts -----
function Fuselage({ children }) {
return (
{children}
);
}
function Nose() {
return (
);
}
function Tail() {
return (
);
}
function Seat({ row, col, sel, setSel, hovered, setHovered }) {
const id = `${row}${col}`;
const taken = BLOCKED_SEATS.has(id);
const isSel = sel && sel.row === row && sel.col === col;
const tier = seatTier(row, col);
const fill = seatColor(tier, taken, isSel);
const border = taken ? "var(--line)" : tier === "premium" ? "#1f3a5a" : tier === "emergency" ? "#b54a1d" : "var(--ink-3)";
return (
);
}
function Legend({ swatch, label, border, dashed }) {
return (
{label}
);
}
window.ScreenSeats = ScreenSeats;
window.seatPrice = seatPrice;