// 5-step booking overlay const { useMemo: _useMemo } = React; const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"]; const DAYS = ["Mo","Tu","We","Th","Fr","Sa","Su"]; function startOfMonth(y, m) { return new Date(y, m, 1); } function daysInMonth(y, m) { return new Date(y, m + 1, 0).getDate(); } function fmtDate(d) { if (!d) return "—"; return d.toLocaleDateString("en-GB", { weekday: "short", day: "numeric", month: "short" }); } function nightsBetween(a, b) { if (!a || !b) return 0; return Math.round((b - a) / (1000 * 60 * 60 * 24)); } function Calendar({ year, month, range, hover, onPick, onHover, onNav }) { const first = startOfMonth(year, month); // shift so Monday is first const startDay = (first.getDay() + 6) % 7; const days = daysInMonth(year, month); const cells = []; for (let i = 0; i < startDay; i++) cells.push(null); for (let d = 1; d <= days; d++) cells.push(new Date(year, month, d)); const today = new Date(); today.setHours(0,0,0,0); return (

{MONTHS[month]} {year}

{DAYS.map(d =>
{d}
)} {cells.map((d, i) => { if (!d) return
; const t = d.getTime(); const disabled = d < today; let cls = "day"; if (disabled) cls += " disabled"; if (range[0] && range[0].getTime() === t) cls += " start"; if (range[1] && range[1].getTime() === t) cls += " end"; if (range[0] && range[1] && t > range[0].getTime() && t < range[1].getTime()) cls += " in-range"; else if (range[0] && !range[1] && hover && t > range[0].getTime() && t < hover.getTime()) cls += " in-range"; return (
!disabled && onPick(d)} onMouseEnter={() => !disabled && onHover(d)} > {d.getDate()}
); })}
); } function StepIndicator({ step }) { const steps = ["Dates", "Room", "Extras", "Guest", "Confirm"]; return (
{steps.map((s, i) => (
0{i+1} {s}
{i < steps.length - 1 &&
} ))}
); } function StepDates({ state, set }) { const [calY, setCalY] = useState(state.checkIn ? state.checkIn.getFullYear() : 2026); const [calM, setCalM] = useState(state.checkIn ? state.checkIn.getMonth() : 6); const [hover, setHover] = useState(null); const pick = (d) => { if (!state.checkIn || (state.checkIn && state.checkOut)) { set({ checkIn: d, checkOut: null }); } else if (d > state.checkIn) { set({ checkOut: d }); } else { set({ checkIn: d, checkOut: null }); } }; const nextY = calM === 11 ? calY + 1 : calY; const nextM = (calM + 1) % 12; const nights = nightsBetween(state.checkIn, state.checkOut); return (
STEP 01 / 05

When would you like to come?

Pick check-in, then check-out. Minimum stay is two nights in high season.
{ const nm = calM + dir; if (nm < 0) { setCalY(calY - 1); setCalM(11); } else if (nm > 11) { setCalY(calY + 1); setCalM(0); } else setCalM(nm); }} /> { const nm = calM + dir; if (nm < 0) { setCalY(calY - 1); setCalM(11); } else if (nm > 11) { setCalY(calY + 1); setCalM(0); } else setCalM(nm); }} />

Your stay

Check in {fmtDate(state.checkIn)}
Check out {fmtDate(state.checkOut)}
Nights {nights || "—"}

Who's coming

{[ { k: "adults", t: "Adults", s: "13+", min: 1 }, { k: "children", t: "Children", s: "3–12", min: 0 }, { k: "infants", t: "Infants", s: "0–2", min: 0 } ].map(g => (
{g.t}{g.s}
{state[g.k]}
))}
); } function StepRoom({ state, set }) { const allTypes = []; window.SK_DATA.buildings.forEach(b => { b.types.forEach(t => allTypes.push({ ...t, building: b.name, buildingId: b.id, hero: b.hero })); }); const totalGuests = state.adults + state.children; const cats = ["All", ...new Set(allTypes.map(t => t.cat))]; const [catFilter, setCatFilter] = useState("All"); let listed = allTypes; if (catFilter !== "All") listed = listed.filter(t => t.cat === catFilter); // Move non-fitting to the end listed = [ ...listed.filter(t => t.sleeps >= totalGuests && state.adults <= (t.maxAdults || t.sleeps)), ...listed.filter(t => !(t.sleeps >= totalGuests && state.adults <= (t.maxAdults || t.sleeps))) ]; const fittingCount = allTypes.filter(t => t.sleeps >= totalGuests && state.adults <= (t.maxAdults || t.sleeps)).length; return (
STEP 02 / 05

Choose your apartment.

{fittingCount} of {allTypes.length} apartment types fit {state.adults} adult{state.adults!==1 && "s"}{state.children?` + ${state.children} child${state.children!==1?"ren":""}`:""}.
{cats.map(c => ( ))}
{listed.map((t, i) => { const fits = t.sleeps >= totalGuests && state.adults <= (t.maxAdults || t.sleeps); const sel = state.roomId === `${t.buildingId}-${t.code}`; return (
fits && set({ roomId: `${t.buildingId}-${t.code}`, roomName: t.name, roomBuilding: t.building, roomPrice: t.from })} style={{ opacity: fits ? 1 : 0.45, cursor: fits ? "pointer" : "not-allowed" }} >
{t.name}
{t.name} {t.cat} · {t.building} · {t.units}
{t.size} m² · Sleeps {t.sleeps} · Max {t.maxAdults || t.sleeps} adults · {t.code === "MZN" ? "3 levels" : t.code === "DUP" || t.code === "JRD" ? "Duplex" : "One level"}
{t.code === "MZN" && "The biggest of the apartments — three levels of stone and timber, with a private roof terrace looking down the bay."} {t.code === "DUP" && "Two-storey apartment with a bedroom upstairs and the main living space opening to the courtyard."} {t.code === "1BR" && t.buildingId === "king" && "A compact one-bedroom with a small kitchen, terrace and the original stone walls."} {t.code === "1BR" && t.buildingId === "prince" && "A compact one-bedroom on the Privé side — quietest part of the resort, kitchenette, private balcony."} {t.code === "1BF" && "Same plan as the 1BR with an extra sofa-bed in the living room — for one child or a teenager."} {t.code === "FAM" && "Family Studio with kitchen — open-plan, sofa bed, sleeps four. Soft-pastel walls, generous bathroom, garden view."} {t.code === "JR1" && "Brand-new junior suite with kitchenette (not for cooking), marble bathroom and full-width balcony over the pool."} {t.code === "JRD" && "The duplex junior suite — two floors, ideal for big families. Best view of the bay."}
{["Private balcony","A/C","Free Wi-Fi","Soundproof","Safe"].map(c => {c})}
From
€{t.from}
/ night incl. tax
{sel ? "Selected ✓" : fits ? "Select" : "Too small"}
); })}
); } const EXTRAS = [ { id: "breakfast", t: "Breakfast buffet", d: "Daily buffet in the new restaurant. Local bread, eggs to order, fruit from the garden, Greek yoghurt.", cap: "Most popular", price: 14, per: "person / day" }, { id: "transfer", t: "Airport transfer", d: "Private car from Thessaloniki airport. Driver waits with a small sign. One car, up to three guests.", cap: "Logistics", price: 140, per: "one way" }, { id: "boat", t: "Diaporos boat tour", d: "Three hours around the nine islets with Captain Stelios. Stops at two hidden coves. Snorkels provided.", cap: "Half-day", price: 65, per: "person" }, { id: "late", t: "Late check-out", d: "Keep the room until 6pm on departure day. Subject to availability — we'll confirm 24h before.", cap: "Comfort", price: 35, per: "one-time" }, { id: "yoga", t: "Yoga package", d: "Seven sessions across your stay with The Harpy. Mat, towel and water included; outdoor deck.", cap: "Wellness", price: 90, per: "person / week" }, { id: "wine", t: "Welcome wine basket", d: "A bottle of Naoussa Xinomavro, local cheeses, olives and bread waiting in the room on arrival.", cap: "Arrival", price: 45, per: "one-time" } ]; function StepExtras({ state, set }) { const toggle = (id) => { const e = { ...state.extras }; if (e[id]) delete e[id]; else e[id] = true; set({ extras: e }); }; return (
STEP 03 / 05

Anything to add?

Optional. You can also arrange any of these once you arrive.
{EXTRAS.map(e => (
toggle(e.id)}>
{e.cap}
{e.t}
{e.d}
€{e.price} {e.per}
))}
); } function StepGuest({ state, set }) { const update = (k) => (e) => set({ guest: { ...state.guest, [k]: e.target.value } }); return (
STEP 04 / 05

Just a few details.

We use these only for the booking. No newsletter unless you ask.