पेज Medium
Hotel PMS — Check-in Flow
Three-step check-in wizard: lookup the reservation, verify guest identity and assign room/keys, settle deposit and print the folio.
लैब में खोलें
MCP
html css vanilla-js
Targets: JS HTML
Code
:root {
--navy: #1a2b4a;
--navy-d: #0f1d36;
--navy-2: #2d4570;
--cream: #f7f3eb;
--cream-2: #ece5d4;
--bone: #fbf8f2;
--gold: #c9a649;
--gold-light: #e0c378;
--gold-d: #a88a2e;
--ink: #161e2c;
--ink-2: #2e3a52;
--warm-gray: #6c7280;
--line: rgba(22, 30, 44, 0.1);
--line-strong: rgba(22, 30, 44, 0.2);
--success: #4a7752;
--danger: #b34232;
--warning: #d99020;
--info: #4a6da0;
--font-display: "Cormorant Garamond", Georgia, serif;
--font-body: "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
--r-sm: 6px;
--r-md: 10px;
--r-lg: 16px;
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
body {
font-family: var(--font-body);
background: var(--cream);
color: var(--ink);
-webkit-font-smoothing: antialiased;
min-height: 100vh;
}
.ci {
min-height: 100vh;
display: grid;
grid-template-rows: auto auto 1fr auto;
}
/* ── Topbar ── */
.topbar {
background: var(--bone);
border-bottom: 1px solid var(--line);
padding: 22px 32px;
display: flex;
justify-content: space-between;
align-items: center;
}
.kicker {
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.18em;
color: var(--gold-d);
font-weight: 600;
}
.topbar h1 {
font-family: var(--font-display);
font-weight: 700;
font-size: 2rem;
letter-spacing: -0.005em;
margin-top: 2px;
}
.cancel {
background: transparent;
border: 1px solid var(--line-strong);
font-family: inherit;
font-size: 0.82rem;
font-weight: 600;
color: var(--ink-2);
padding: 9px 16px;
border-radius: 999px;
cursor: pointer;
}
.cancel:hover {
border-color: var(--danger);
color: var(--danger);
}
/* ── Stepper ── */
.stepper {
display: flex;
align-items: center;
justify-content: center;
padding: 16px 32px;
background: var(--bone);
border-bottom: 1px solid var(--line);
gap: 4px;
}
.step {
background: transparent;
border: none;
font-family: inherit;
display: inline-flex;
align-items: center;
gap: 10px;
padding: 8px 16px;
border-radius: 999px;
cursor: pointer;
color: var(--warm-gray);
font-weight: 600;
}
.step .num {
width: 28px;
height: 28px;
border-radius: 999px;
background: var(--cream-2);
display: grid;
place-items: center;
font-family: var(--font-display);
font-weight: 700;
color: var(--warm-gray);
}
.step .lbl {
font-size: 0.86rem;
letter-spacing: 0.02em;
}
.step.is-active {
color: var(--navy-d);
}
.step.is-active .num {
background: var(--navy);
color: var(--bone);
}
.step.is-done .num {
background: var(--success);
color: var(--bone);
}
.step.is-done {
color: var(--success);
}
.step-line {
width: 60px;
height: 1px;
background: var(--line-strong);
}
/* ── Layout ── */
.layout {
display: grid;
grid-template-columns: 1fr 320px;
gap: 24px;
padding: 24px 32px;
align-items: start;
}
.panel {
background: var(--bone);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 28px 30px;
min-height: 480px;
}
.step-panel {
display: none;
}
.step-panel.is-active {
display: block;
}
.panel-h {
font-family: var(--font-display);
font-weight: 700;
font-size: 1.55rem;
letter-spacing: -0.005em;
color: var(--navy-d);
}
.panel-sub {
font-size: 0.92rem;
color: var(--warm-gray);
margin: 4px 0 22px;
}
/* ── Step 1 ── */
.search {
display: flex;
align-items: center;
gap: 10px;
background: var(--cream);
border: 1px solid var(--line-strong);
padding: 14px 20px;
border-radius: var(--r-md);
color: var(--warm-gray);
margin-bottom: 24px;
}
.search input {
background: transparent;
border: none;
outline: none;
font-family: inherit;
font-size: 1rem;
color: var(--ink);
width: 100%;
}
.results-h {
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.14em;
color: var(--gold-d);
font-weight: 700;
margin-bottom: 10px;
}
.results {
display: grid;
gap: 8px;
}
.res {
display: grid;
grid-template-columns: auto 1fr auto auto;
align-items: center;
gap: 14px;
padding: 14px 16px;
background: var(--cream);
border: 1px solid var(--line);
border-radius: var(--r-md);
cursor: pointer;
transition: border-color 0.12s, background 0.12s;
}
.res:hover {
border-color: var(--gold);
background: var(--bone);
}
.res.is-selected {
border-color: var(--gold);
background: var(--bone);
outline: 1px solid var(--gold);
}
.res-code {
font-family: var(--font-mono);
font-size: 0.74rem;
font-weight: 700;
background: rgba(22, 30, 44, 0.06);
color: var(--navy-d);
padding: 4px 10px;
border-radius: var(--r-sm);
}
.res-name {
font-weight: 600;
font-size: 0.96rem;
}
.res-meta {
font-size: 0.76rem;
color: var(--warm-gray);
margin-top: 2px;
}
.res-dates {
font-family: var(--font-mono);
font-size: 0.8rem;
color: var(--ink-2);
text-align: right;
}
.res-status {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 5px 12px;
border-radius: 999px;
background: rgba(74, 109, 160, 0.16);
color: var(--info);
}
/* ── Step 2 ── */
.form-block {
border-top: 1px solid var(--line);
padding-top: 18px;
margin-top: 18px;
}
.form-block:first-of-type {
border-top: none;
padding-top: 0;
margin-top: 0;
}
.form-block h3 {
font-size: 0.82rem;
text-transform: uppercase;
letter-spacing: 0.14em;
color: var(--gold-d);
font-weight: 700;
margin-bottom: 12px;
}
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px 16px;
}
.field {
display: flex;
flex-direction: column;
gap: 4px;
}
.field span {
font-size: 0.74rem;
color: var(--warm-gray);
font-weight: 600;
letter-spacing: 0.02em;
}
.field input,
.field select {
font-family: inherit;
font-size: 0.92rem;
background: var(--cream);
border: 1px solid var(--line-strong);
border-radius: var(--r-sm);
padding: 9px 12px;
color: var(--ink);
outline: none;
}
.field input:focus,
.field select:focus {
border-color: var(--gold);
}
.check {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 0.86rem;
color: var(--ink-2);
margin-top: 12px;
cursor: pointer;
}
.check input {
accent-color: var(--navy);
width: 16px;
height: 16px;
}
.sig {
margin-top: 16px;
display: grid;
gap: 6px;
}
.sig span {
font-size: 0.74rem;
color: var(--warm-gray);
font-weight: 600;
}
.sig-pad {
height: 80px;
background: repeating-linear-gradient(
0deg,
var(--cream) 0,
var(--cream) 18px,
var(--cream-2) 19px
);
border: 1px dashed var(--line-strong);
border-radius: var(--r-sm);
display: grid;
place-items: center;
font-family: var(--font-display);
font-style: italic;
color: var(--warm-gray);
cursor: pointer;
}
.rooms {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 8px;
}
.room-pick {
background: var(--cream);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 12px 14px;
cursor: pointer;
text-align: left;
font-family: inherit;
color: inherit;
display: flex;
flex-direction: column;
gap: 2px;
}
.room-pick:hover {
border-color: var(--gold);
}
.room-pick.is-selected {
background: var(--bone);
border-color: var(--gold);
outline: 1px solid var(--gold);
}
.room-pick strong {
font-family: var(--font-mono);
font-size: 1rem;
font-weight: 700;
color: var(--navy-d);
}
.room-pick small {
font-size: 0.76rem;
color: var(--warm-gray);
}
.room-pick em {
font-size: 0.7rem;
color: var(--success);
font-style: normal;
font-weight: 600;
margin-top: 4px;
}
.keys {
display: grid;
gap: 8px;
}
.key {
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
gap: 12px;
padding: 10px 14px;
background: var(--cream);
border: 1px solid var(--line);
border-radius: var(--r-md);
cursor: pointer;
}
.key input {
accent-color: var(--navy);
width: 16px;
height: 16px;
}
.key strong {
font-size: 0.9rem;
}
.key small {
display: block;
font-size: 0.74rem;
color: var(--warm-gray);
}
/* ── Step 3 ── */
.folio {
margin-bottom: 18px;
}
.folio h3 {
font-size: 0.82rem;
text-transform: uppercase;
letter-spacing: 0.14em;
color: var(--gold-d);
font-weight: 700;
margin-bottom: 10px;
}
.folio-list {
list-style: none;
background: var(--cream);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 8px 18px;
}
.folio-list li {
display: flex;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px dashed var(--line);
font-size: 0.9rem;
color: var(--ink-2);
}
.folio-list li:last-child {
border-bottom: none;
}
.folio-list li span:last-child {
font-family: var(--font-mono);
font-weight: 600;
color: var(--ink);
}
.folio-list li.total {
font-weight: 700;
color: var(--navy-d);
font-size: 1rem;
}
.folio-list li.total span:last-child {
font-size: 1.15rem;
color: var(--navy-d);
}
.folio-list .incl {
color: var(--success);
font-style: italic;
font-weight: 500;
}
.seg {
display: inline-flex;
background: var(--cream-2);
padding: 4px;
border-radius: 999px;
gap: 4px;
margin-bottom: 14px;
}
.seg-btn {
background: transparent;
border: none;
font-family: inherit;
font-weight: 600;
font-size: 0.82rem;
padding: 8px 16px;
border-radius: 999px;
cursor: pointer;
color: var(--ink-2);
}
.seg-btn.is-active {
background: var(--bone);
color: var(--navy-d);
box-shadow: 0 1px 2px rgba(22, 30, 44, 0.08);
}
.pay-body {
background: var(--cream);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px 18px;
font-size: 0.9rem;
color: var(--ink-2);
line-height: 1.55;
}
.pay-body strong {
color: var(--navy-d);
}
.welcome {
display: grid;
gap: 6px;
}
/* ── Summary rail ── */
.summary {
background: var(--bone);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 22px;
position: sticky;
top: 24px;
}
.summary h2 {
font-family: var(--font-display);
font-weight: 700;
font-size: 1.2rem;
letter-spacing: -0.005em;
color: var(--navy-d);
margin-bottom: 16px;
}
.summary-empty {
font-size: 0.86rem;
color: var(--warm-gray);
font-style: italic;
}
.sum-row {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px dashed var(--line);
font-size: 0.82rem;
}
.sum-row:last-of-type {
border-bottom: none;
}
.sum-row span:first-child {
color: var(--warm-gray);
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.7rem;
font-weight: 700;
}
.sum-row span:last-child {
font-weight: 600;
color: var(--ink);
text-align: right;
}
.sum-block {
margin-top: 14px;
padding-top: 12px;
border-top: 1px solid var(--line);
}
.sum-block strong {
font-family: var(--font-display);
font-size: 1.5rem;
color: var(--gold-d);
font-weight: 700;
display: block;
}
.sum-block small {
font-size: 0.74rem;
color: var(--warm-gray);
margin-top: 2px;
display: block;
}
/* ── Footer ── */
.footer {
background: var(--bone);
border-top: 1px solid var(--line);
padding: 14px 32px;
display: flex;
justify-content: space-between;
align-items: center;
}
.back,
.next {
font-family: inherit;
font-weight: 600;
font-size: 0.92rem;
padding: 11px 22px;
border-radius: 999px;
cursor: pointer;
border: 1px solid var(--line-strong);
background: var(--cream);
color: var(--ink-2);
}
.back:hover:not(:disabled) {
background: var(--cream-2);
}
.back:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.next {
background: var(--navy);
color: var(--bone);
border-color: var(--navy);
}
.next:hover:not(:disabled) {
background: var(--navy-d);
}
.next:disabled {
background: var(--warm-gray);
border-color: var(--warm-gray);
cursor: not-allowed;
opacity: 0.7;
}
.next.is-final {
background: var(--gold);
color: var(--navy-d);
border-color: var(--gold);
}
.next.is-final:hover:not(:disabled) {
background: var(--gold-light);
}
.footer-meta {
font-size: 0.78rem;
color: var(--warm-gray);
font-weight: 600;
letter-spacing: 0.04em;
}
/* ── Toast ── */
.toast {
position: fixed;
bottom: 90px;
left: 50%;
transform: translateX(-50%);
background: var(--navy-d);
color: var(--bone);
padding: 11px 22px;
border-radius: 999px;
font-size: 0.86rem;
font-weight: 600;
box-shadow: 0 10px 30px rgba(22, 30, 44, 0.18);
}
@media (max-width: 1024px) {
.layout {
grid-template-columns: 1fr;
}
.summary {
position: static;
}
.step-line {
width: 24px;
}
.step .lbl {
display: none;
}
}// ── Mock data ──────────────────────────────────────────────────────────────
const ARRIVALS = [
{
code: "AUR-21847",
name: "Aiko Tanaka",
meta: "2 adults · Junior Suite · 5 nights",
dates: "24 May → 29 May",
status: "Expected",
},
{
code: "AUR-21848",
name: "Mariana Sosa",
meta: "2 adults · 1 child · Deluxe Suite",
dates: "24 May → 27 May",
status: "Expected",
},
{
code: "AUR-21849",
name: "Thomas Reuter",
meta: "1 adult · Classic Double",
dates: "24 May → 25 May",
status: "Expected",
},
{
code: "AUR-21850",
name: "Olivier Banks",
meta: "2 adults · Twin Standard · late ETA",
dates: "24 May → 26 May",
status: "Late",
},
];
const ROOMS = [
{ no: "302", label: "Junior Suite · King", floor: 3, status: "Ready" },
{ no: "303", label: "Junior Suite · King", floor: 3, status: "Ready" },
{ no: "304", label: "Junior Suite · Twin", floor: 3, status: "In service" },
];
// ── State ──────────────────────────────────────────────────────────────────
let step = 1;
let selectedRes = null;
let selectedRoom = null;
let payMethod = "card";
const panels = document.querySelectorAll(".step-panel");
const steps = document.querySelectorAll(".step");
const lines = document.querySelectorAll(".step-line");
const next = document.getElementById("next");
const back = document.getElementById("back");
const footerMeta = document.getElementById("footerMeta");
const summary = document.getElementById("summary");
const toast = document.getElementById("toast");
function showToast(msg) {
toast.textContent = msg;
toast.hidden = false;
clearTimeout(showToast._t);
showToast._t = setTimeout(() => (toast.hidden = true), 1800);
}
// ── Step 1: Lookup ─────────────────────────────────────────────────────────
const resultsEl = document.getElementById("results");
const lookupInput = document.getElementById("lookup");
function renderResults() {
const q = (lookupInput.value || "").toLowerCase().trim();
const list = ARRIVALS.filter(
(r) =>
!q ||
r.name.toLowerCase().includes(q) ||
r.code.toLowerCase().includes(q) ||
r.meta.toLowerCase().includes(q)
);
resultsEl.innerHTML = list
.map(
(r) => `
<button class="res ${selectedRes?.code === r.code ? "is-selected" : ""}" data-code="${r.code}">
<span class="res-code">${r.code}</span>
<span class="res-body">
<span class="res-name">${r.name}</span>
<span class="res-meta">${r.meta}</span>
</span>
<span class="res-dates">${r.dates}</span>
<span class="res-status">${r.status}</span>
</button>`
)
.join("");
}
renderResults();
lookupInput.addEventListener("input", renderResults);
resultsEl.addEventListener("click", (e) => {
const btn = e.target.closest(".res");
if (!btn) return;
selectedRes = ARRIVALS.find((r) => r.code === btn.dataset.code);
renderResults();
renderSummary();
updateNav();
});
// ── Step 2: Rooms ──────────────────────────────────────────────────────────
const roomsEl = document.getElementById("rooms");
roomsEl.innerHTML = ROOMS.map(
(r) => `
<button class="room-pick" data-no="${r.no}">
<strong>${r.no}</strong>
<small>${r.label}</small>
<em>${r.status}</em>
</button>`
).join("");
roomsEl.addEventListener("click", (e) => {
const btn = e.target.closest(".room-pick");
if (!btn) return;
roomsEl.querySelectorAll(".room-pick").forEach((x) => x.classList.remove("is-selected"));
btn.classList.add("is-selected");
selectedRoom = ROOMS.find((r) => r.no === btn.dataset.no);
renderSummary();
});
// ── Step 3: Payment ────────────────────────────────────────────────────────
const payBody = document.getElementById("payBody");
const PAY_TEXT = {
card: `Charge <strong>€1,221.50</strong> to card on file <strong>Visa •••• 4421</strong>. Authorisation will appear on the guest folio within 10 seconds.`,
auth: `Pre-authorise <strong>€1,500.00</strong> on card on file as incidental hold. Released on check-out if no additional charges.`,
cash: `Collect <strong>€1,221.50</strong> in cash. Drawer will open after confirming. A printed receipt is issued automatically.`,
};
function renderPay() {
payBody.innerHTML = PAY_TEXT[payMethod];
}
renderPay();
document.getElementById("paySeg").addEventListener("click", (e) => {
const b = e.target.closest(".seg-btn");
if (!b) return;
document.querySelectorAll("#paySeg .seg-btn").forEach((x) => x.classList.remove("is-active"));
b.classList.add("is-active");
payMethod = b.dataset.pay;
renderPay();
});
// ── Summary ────────────────────────────────────────────────────────────────
function renderSummary() {
if (!selectedRes) {
summary.innerHTML = `<h2>Reservation</h2><p class="summary-empty">Select a reservation to populate this panel.</p>`;
return;
}
summary.innerHTML = `
<h2>Reservation</h2>
<div class="sum-row"><span>Guest</span><span>${selectedRes.name}</span></div>
<div class="sum-row"><span>Confirmation</span><span>${selectedRes.code}</span></div>
<div class="sum-row"><span>Dates</span><span>${selectedRes.dates}</span></div>
<div class="sum-row"><span>Stay</span><span>${selectedRes.meta}</span></div>
<div class="sum-row"><span>Room</span><span>${selectedRoom ? selectedRoom.no + " · " + selectedRoom.label : "—"}</span></div>
<div class="sum-block">
<strong>€1,221.50</strong>
<small>folio balance (incl. tax)</small>
</div>
`;
}
renderSummary();
// ── Stepper logic ──────────────────────────────────────────────────────────
function goto(n) {
step = n;
panels.forEach((p) => p.classList.toggle("is-active", parseInt(p.dataset.panel, 10) === n));
steps.forEach((s) => {
const sn = parseInt(s.dataset.step, 10);
s.classList.toggle("is-active", sn === n);
s.classList.toggle("is-done", sn < n);
});
updateNav();
}
function updateNav() {
back.disabled = step === 1;
footerMeta.textContent = `Step ${step} of 3`;
if (step === 3) {
next.textContent = "Complete check-in";
next.classList.add("is-final");
next.disabled = false;
} else {
next.textContent = "Continue →";
next.classList.remove("is-final");
if (step === 1) next.disabled = !selectedRes;
if (step === 2) next.disabled = false;
}
}
next.addEventListener("click", () => {
if (step === 1 && !selectedRes) return;
if (step < 3) {
goto(step + 1);
} else {
showToast(`Check-in complete · welcome to ${selectedRoom?.no || "—"}`);
next.disabled = true;
}
});
back.addEventListener("click", () => step > 1 && goto(step - 1));
steps.forEach((s) =>
s.addEventListener("click", () => {
const target = parseInt(s.dataset.step, 10);
if (target === 1 || (target === 2 && selectedRes) || (target === 3 && selectedRes))
goto(target);
})
);
updateNav();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&display=swap"
/>
<link rel="stylesheet" href="style.css" />
<title>Check-in · Aurelia Hotels</title>
</head>
<body>
<main class="ci">
<header class="topbar">
<div>
<p class="kicker">Front desk</p>
<h1>Check-in</h1>
</div>
<button class="cancel">Cancel</button>
</header>
<nav class="stepper" id="stepper">
<button class="step is-active" data-step="1">
<span class="num">1</span><span class="lbl">Lookup</span>
</button>
<span class="step-line"></span>
<button class="step" data-step="2">
<span class="num">2</span><span class="lbl">Verify & Assign</span>
</button>
<span class="step-line"></span>
<button class="step" data-step="3">
<span class="num">3</span><span class="lbl">Folio & Welcome</span>
</button>
</nav>
<section class="layout">
<section class="panel" id="panel">
<!-- step 1 -->
<article class="step-panel is-active" data-panel="1">
<h2 class="panel-h">Find the reservation</h2>
<p class="panel-sub">Search by guest name, confirmation code or last 4 digits of the card on file.</p>
<div class="search">
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><circle cx="11" cy="11" r="7" fill="none" stroke="currentColor" stroke-width="2"/><path d="m20 20-3.5-3.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
<input id="lookup" type="search" placeholder="e.g. Tanaka · AUR-21847 · 4421" />
</div>
<p class="results-h">Today's expected arrivals</p>
<div class="results" id="results"></div>
</article>
<!-- step 2 -->
<article class="step-panel" data-panel="2">
<h2 class="panel-h">Verify guest & assign room</h2>
<p class="panel-sub">Confirm identification documents and pick a compatible room.</p>
<section class="form-block">
<h3>Identity</h3>
<div class="grid-2">
<label class="field"><span>Document type</span>
<select><option>Passport</option><option>National ID</option><option>Driver's License</option></select>
</label>
<label class="field"><span>Document number</span>
<input type="text" value="P-29384721" />
</label>
<label class="field"><span>Nationality</span>
<input type="text" value="Japan" />
</label>
<label class="field"><span>Date of birth</span>
<input type="text" value="1984-11-22" />
</label>
</div>
<label class="check"><input type="checkbox" checked /> ID document scanned & verified</label>
<div class="sig">
<span>Signature</span>
<div class="sig-pad">Tap to sign</div>
</div>
</section>
<section class="form-block">
<h3>Room assignment</h3>
<div class="rooms" id="rooms"></div>
</section>
<section class="form-block">
<h3>Keys</h3>
<div class="keys">
<label class="key"><input type="checkbox" checked /> <strong>Mobile key</strong><small>Sent to guest app</small></label>
<label class="key"><input type="checkbox" /> <strong>Physical card ×2</strong><small>Encoder bay 1</small></label>
<label class="key"><input type="checkbox" /> <strong>Spa & gym access</strong><small>Included with suite</small></label>
</div>
</section>
</article>
<!-- step 3 -->
<article class="step-panel" data-panel="3">
<h2 class="panel-h">Folio & welcome</h2>
<p class="panel-sub">Review charges and capture the deposit before printing the welcome packet.</p>
<section class="folio">
<h3>Folio preview</h3>
<ul class="folio-list">
<li><span>Junior Suite · 5 nights</span><span>€1,205.00</span></li>
<li><span>City tax · 5 nights</span><span>€16.50</span></li>
<li><span>Welcome amenity</span><span class="incl">included</span></li>
<li class="total"><span>Total folio balance</span><span>€1,221.50</span></li>
</ul>
</section>
<section class="form-block">
<h3>Deposit / payment</h3>
<div class="seg" id="paySeg">
<button class="seg-btn is-active" data-pay="card">Card on file</button>
<button class="seg-btn" data-pay="auth">Pre-authorise</button>
<button class="seg-btn" data-pay="cash">Cash</button>
</div>
<div class="pay-body" id="payBody"></div>
</section>
<section class="form-block">
<h3>Welcome packet</h3>
<div class="welcome">
<label class="check"><input type="checkbox" checked /> Print folio + key card sleeves</label>
<label class="check"><input type="checkbox" checked /> Email digital receipt</label>
<label class="check"><input type="checkbox" /> SMS Wi-Fi password</label>
<label class="check"><input type="checkbox" /> Schedule turn-down service for 21:00</label>
</div>
</section>
</article>
</section>
<aside class="summary" id="summary"></aside>
</section>
<footer class="footer">
<button class="back" id="back" disabled>← Back</button>
<div class="footer-meta" id="footerMeta">Step 1 of 3</div>
<button class="next" id="next">Continue →</button>
</footer>
</main>
<div class="toast" id="toast" hidden role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Check-in Flow
The wizard a receptionist runs every time a guest walks up. Step 1 — Lookup: search by name, confirmation code or last 4 of card; results render as cards with status. Step 2 — Verify & Assign: confirm guest details (ID, signature pad placeholder), pick the room from compatible options, issue digital + physical keys. Step 3 — Folio & Welcome: review charges, capture deposit (card / cash / authorize), print welcome packet.
A persistent progress strip up top tracks the step; the right rail summarises the reservation and updates as info is captured. Vanilla JS — no framework.