@import url(‘https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap’);
#ezh{
–ink:#14161A; –ink-soft:#54595F; –line:#DFDDD6;
–paper:#FFFFFF; –stone:#F2F1ED; –red:#C61830; –red-deep:#8E1123;
–moss:#3F5D45; –ok:#1E6B4F;
–serif:’Instrument Serif’,Georgia,serif;
–sans:’Inter’,system-ui,-apple-system,sans-serif;
–mono:’IBM Plex Mono’,ui-monospace,monospace;
all:initial; display:block;
font-family:var(–sans); color:var(–ink); font-size:16px; line-height:1.55;
-webkit-font-smoothing:antialiased; text-align:left;
}
#ezh *,#ezh *::before,#ezh *::after{box-sizing:border-box;}
#ezh p,#ezh h1,#ezh h2,#ezh h3,#ezh ul,#ezh li,#ezh figure{margin:0;padding:0;}
#ezh ul{list-style:none;}
#ezh button{font:inherit;color:inherit;background:none;border:none;cursor:pointer;}
#ezh input,#ezh select{font:inherit;color:inherit;}
#ezh :focus-visible{outline:2px solid var(–red);outline-offset:2px;}
#ezh .wrap{max-width:940px;margin:0 auto;padding:8px 16px 56px;}
/* ── masthead ─────────────────────────────────────────────── */
#ezh .top{padding:28px 0 22px;border-bottom:1px solid var(–line);}
#ezh .eyebrow{font-family:var(–mono);font-size:11px;letter-spacing:.16em;
text-transform:uppercase;color:var(–red);}
#ezh .top h2{font-family:var(–serif);font-size:clamp(30px,6vw,46px);
line-height:1.05;font-weight:400;margin-top:10px;letter-spacing:-.01em;}
#ezh .top p{color:var(–ink-soft);margin-top:10px;max-width:52ch;font-size:15px;}
/* ── the search bar ───────────────────────────────────────── */
#ezh .search{background:var(–stone);border:1px solid var(–line);
border-radius:3px;padding:18px;margin-top:24px;
display:grid;gap:14px;grid-template-columns:1fr 1fr;}
#ezh .search .full{grid-column:1/-1;}
#ezh label{display:block;font-family:var(–mono);font-size:11px;
letter-spacing:.1em;text-transform:uppercase;color:var(–ink-soft);
margin-bottom:6px;}
#ezh input[type=date],#ezh input[type=text],#ezh input[type=tel],
#ezh input[type=email],#ezh select{
width:100%;padding:11px 12px;border:1px solid var(–line);
border-radius:2px;background:var(–paper);}
#ezh .stepper{display:flex;align-items:stretch;border:1px solid var(–line);
border-radius:2px;background:var(–paper);overflow:hidden;}
#ezh .stepper button{width:44px;font-size:18px;color:var(–ink-soft);}
#ezh .stepper button:hover{background:var(–stone);color:var(–ink);}
#ezh .stepper span{flex:1;display:flex;align-items:center;justify-content:center;
font-family:var(–mono);font-size:15px;}
#ezh .nights{font-family:var(–mono);font-size:12px;color:var(–ink-soft);
grid-column:1/-1;margin-top:-4px;}
/* ── rooms ────────────────────────────────────────────────── */
#ezh .sec{margin-top:36px;}
#ezh .sec > h3{font-family:var(–serif);font-size:26px;font-weight:400;
margin-bottom:4px;}
#ezh .sec > .note{color:var(–ink-soft);font-size:14px;margin-bottom:18px;}
#ezh .rooms{display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));}
#ezh .room{border:1px solid var(–line);border-radius:3px;overflow:hidden;
background:var(–paper);display:flex;flex-direction:column;
transition:border-color .15s,box-shadow .15s;}
#ezh .room.on{border-color:var(–red);box-shadow:0 0 0 1px var(–red);}
#ezh .room.out{opacity:.5;}
/* the photo slot — a tinted panel until a real photograph exists */
#ezh .shot{aspect-ratio:4/3;background:var(–stone);position:relative;
display:flex;align-items:flex-end;padding:12px;overflow:hidden;}
#ezh .shot img{position:absolute;inset:0;width:100%;height:100%;
object-fit:cover;}
#ezh .shot .thinai{position:relative;font-family:var(–serif);font-style:italic;
font-size:19px;color:var(–moss);}
#ezh .shot.has .thinai{display:none;}
#ezh .room .body{padding:14px;display:flex;flex-direction:column;gap:8px;flex:1;}
#ezh .room h4{font-family:var(–serif);font-size:23px;font-weight:400;line-height:1;}
#ezh .room .kind{font-family:var(–mono);font-size:11px;letter-spacing:.08em;
text-transform:uppercase;color:var(–ink-soft);}
#ezh .room .blurb{font-size:14px;color:var(–ink-soft);flex:1;}
#ezh .tags{display:flex;flex-wrap:wrap;gap:5px;}
#ezh .tag{font-family:var(–mono);font-size:10.5px;letter-spacing:.05em;
border:1px solid var(–line);border-radius:2px;padding:3px 7px;color:var(–ink-soft);}
#ezh .rate{display:flex;align-items:baseline;justify-content:space-between;
border-top:1px solid var(–line);padding-top:10px;margin-top:2px;}
#ezh .rate b{font-family:var(–mono);font-weight:500;font-size:17px;}
#ezh .rate small{font-family:var(–mono);font-size:11px;color:var(–ink-soft);}
#ezh .pick{width:100%;padding:11px;border:1px solid var(–ink);border-radius:2px;
font-weight:500;font-size:14px;transition:background .15s,color .15s;}
#ezh .pick:hover{background:var(–ink);color:var(–paper);}
#ezh .room.on .pick{background:var(–red);border-color:var(–red);color:#fff;}
#ezh .room.out .pick{border-color:var(–line);color:var(–ink-soft);cursor:not-allowed;
background:none;}
/* ── extras + total ───────────────────────────────────────── */
#ezh .extras{display:grid;gap:14px;grid-template-columns:1fr 1fr;
background:var(–stone);border:1px solid var(–line);border-radius:3px;padding:18px;}
#ezh .check{display:flex;gap:10px;align-items:flex-start;grid-column:1/-1;
padding:12px;background:var(–paper);border:1px solid var(–line);border-radius:2px;}
#ezh .check input{margin-top:3px;width:17px;height:17px;accent-color:var(–red);}
#ezh .check .t{font-size:14px;}
#ezh .check .s{font-size:13px;color:var(–ink-soft);}
#ezh .bill{border:1px solid var(–ink);border-radius:3px;overflow:hidden;margin-top:18px;}
#ezh .bill .line{display:flex;justify-content:space-between;gap:16px;
padding:9px 16px;font-size:14px;}
#ezh .bill .line span:last-child{font-family:var(–mono);}
#ezh .bill .line.sub{color:var(–ink-soft);}
#ezh .bill .sum{display:flex;justify-content:space-between;align-items:baseline;
padding:13px 16px;border-top:1px solid var(–line);}
#ezh .bill .sum b{font-family:var(–mono);font-size:19px;font-weight:500;}
#ezh .bill .due{background:var(–ink);color:#fff;display:flex;
justify-content:space-between;align-items:baseline;padding:14px 16px;}
#ezh .bill .due b{font-family:var(–mono);font-size:21px;font-weight:500;}
#ezh .bill .due .s{font-size:12px;opacity:.72;display:block;margin-top:2px;}
/* ── buttons, messages ────────────────────────────────────── */
#ezh .cta{width:100%;background:var(–red);color:#fff;padding:15px;
border-radius:2px;font-weight:600;font-size:16px;margin-top:16px;
transition:background .15s;}
#ezh .cta:hover{background:var(–red-deep);}
#ezh .cta[disabled]{background:var(–line);color:var(–ink-soft);cursor:not-allowed;}
#ezh .ghost{width:100%;border:1px solid var(–line);padding:13px;border-radius:2px;
font-size:14px;margin-top:8px;}
#ezh .ghost:hover{border-color:var(–ink);}
#ezh .msg{padding:13px 15px;border-radius:2px;font-size:14px;margin-top:14px;
border-left:3px solid;}
#ezh .msg.bad{background:#FDF2F3;border-color:var(–red);}
#ezh .msg.good{background:#F0F7F3;border-color:var(–ok);}
#ezh .msg.info{background:var(–stone);border-color:var(–ink-soft);}
#ezh .done{border:1px solid var(–ok);border-radius:3px;padding:26px;
text-align:center;margin-top:20px;}
#ezh .done h3{font-family:var(–serif);font-size:29px;font-weight:400;}
#ezh .done .id{font-family:var(–mono);font-size:23px;color:var(–red);
margin:14px 0;letter-spacing:.03em;}
#ezh .done p{color:var(–ink-soft);font-size:14px;max-width:44ch;margin:0 auto;}
#ezh .upi{border:1px solid var(–line);border-radius:3px;padding:18px;margin-top:14px;}
#ezh .vpa{display:flex;gap:8px;align-items:center;margin-top:10px;}
#ezh .vpa code{flex:1;font-family:var(–mono);font-size:15px;background:var(–stone);
padding:11px 12px;border-radius:2px;overflow-x:auto;white-space:nowrap;}
#ezh .vpa button{border:1px solid var(–line);padding:11px 14px;border-radius:2px;
font-size:13px;white-space:nowrap;}
#ezh .hide{display:none !important;}
#ezh .spin{display:inline-block;width:13px;height:13px;border:2px solid rgba(255,255,255,.4);
border-top-color:#fff;border-radius:50%;animation:ezhspin .7s linear infinite;
vertical-align:-2px;margin-right:7px;}
@keyframes ezhspin{to{transform:rotate(360deg);}}
@media (prefers-reduced-motion:reduce){#ezh .spin{animation-duration:2.5s;}}
@media(max-width:600px){
#ezh .search,#ezh .extras{grid-template-columns:1fr;}
#ezh .rooms{grid-template-columns:1fr;}
}
(function(){
“use strict”;
/* ── Configuration ────────────────────────────────────────────────────────
* The one thing you may need to change here is EXEC — the Apps Script web
* app URL. Everything else, including the Razorpay key, is fetched from the
* script at run time so keys can be rotated without touching this page. */
var EXEC = “https://script.google.com/macros/s/AKfycbwUVyNn9AIKvCpF2SQRsTqyepZgqwhOIeWCJt25YT5wyC3pp0fWayuTSDTvlwmD24Ww/exec”;
var HOTEL = {
name:”Kutralam Ezhilagam”,
whatsapp:”918248320685″,
phone:”+918248320685″
};
/* Tariff. These match the sheet — change them in both places or not at all. */
var TARIFF = {
acAddOn:{ “Studio Room”:500, “Suite Room”:1000 },
extraBed:{ AC:300, “Non-AC”:200 },
maxExtraBeds:6,
advanceFraction:0.5,
lateCheckoutUplift:0.5,
checkInDefault:”12:00″, checkOutDefault:”10:00″, lateHour:”17:00″
};
/* PHOTOS — until the Room Photos screen is built into the staff app, put a
* public image URL in `photo` below and the tinted panel is replaced. Leave
* it empty and the room still shows properly, just without a picture. */
var ROOMS = [
{name:”Thillai”, type:”Studio Room”, floor:”Ground Floor”, rate:1899, ac:true, max:4,
thinai:”தில்லை”, photo:””,
blurb:”Deluxe studio opening onto the garden — the easiest room to reach, and the quietest at night.”,
tags:[“Deluxe studio”,”Garden side”,”Ground floor”]},
{name:”Aambal”, type:”Studio Room”, floor:”Ground Floor”, rate:1199, ac:false, max:2,
thinai:”ஆம்பல்”, photo:””,
blurb:”Our snug standard studio for two, kept naturally cool by the hillside air.”,
tags:[“Standard studio”,”Non-AC only”,”Best value”]},
{name:”Vaagai”, type:”Suite Room”, floor:”Ground Floor”, rate:5499, ac:true, max:15,
thinai:”வாகை”, photo:””,
blurb:”Premium family suite with four cots — built for the whole family travelling together.”,
tags:[“Family suite”,”4 cots”,”Sleeps up to 15″]},
{name:”Shenbagam”, type:”Suite Room”, floor:”First Floor”, rate:4599, ac:false, max:10,
thinai:”செண்பகம்”, photo:””,
blurb:”Premium family suite with its own kitchen — three bedrooms, three bathrooms and a balcony.”,
tags:[“3 bedrooms”,”Kitchen”,”Non-AC only”]},
{name:”Mavval”, type:”Suite Room”, floor:”First Floor”, rate:4899, ac:true, max:10,
thinai:”மாவ்வல்”, photo:””,
blurb:”Premium family suite on the first floor, with room to spread out and a view over the trees.”,
tags:[“Family suite”,”First floor”,”Sleeps up to 10″]},
{name:”Kurinji”, type:”Studio Room”, floor:”Second Floor”, rate:2899, ac:true, max:5,
thinai:”குறிஞ்சி”, photo:””,
blurb:”Deluxe studio with the mountain view — the one guests ask for by name.”,
tags:[“Mountain view”,”Deluxe studio”,”Second floor”]},
{name:”Mullai”, type:”Studio Room”, floor:”Second Floor”, rate:2199, ac:true, max:4,
thinai:”முல்லை”, photo:””,
blurb:”Deluxe studio with a balcony — morning coffee outside, with the hills in front of you.”,
tags:[“Private balcony”,”Deluxe studio”,”Second floor”]},
{name:”Marutham”, type:”Studio Room”, floor:”Second Floor”, rate:2099, ac:true, max:4,
thinai:”மருதம்”, photo:””,
blurb:”Deluxe studio with a balcony, tucked at the quiet end of the second floor.”,
tags:[“Private balcony”,”Deluxe studio”,”Quiet corner”]},
{name:”Paalai”, type:”Studio Room”, floor:”Second Floor”, rate:2699, ac:true, max:5,
thinai:”பாலை”, photo:””,
blurb:”Our largest deluxe studio — comfortable for a small family without taking a whole suite.”,
tags:[“Deluxe studio”,”Sleeps up to 5″,”Second floor”]}
];
/* ── State ────────────────────────────────────────────────────────────── */
var S = { checkIn:””, checkOut:””, adults:2, children:0, beds:0,
ac:”Non-AC”, late:false, picked:[], busy:{}, pay:null, working:false };
var $ = function(id){ return document.getElementById(id); };
var rupees = function(n){ return “\u20B9” + Math.round(n).toLocaleString(“en-IN”); };
function iso(d){
return d.getFullYear() + “-” + (“0″+(d.getMonth()+1)).slice(-2) + “-” + (“0″+d.getDate()).slice(-2);
}
function pretty(s){
if(!s) return “”;
var d = new Date(s + “T12:00:00”);
return d.toLocaleDateString(“en-IN”,{day:”numeric”,month:”short”,year:”numeric”});
}
/* Every night the stay occupies. A 5 PM departure holds the last day too —
* we must not sell a morning we cannot honour. Same rule as the sheet. */
function nights(){
var out = [];
if(!S.checkIn || !S.checkOut) return out;
var d = new Date(S.checkIn+”T12:00:00″), end = new Date(S.checkOut+”T12:00:00″), g=0;
while(d < end && g++ < 400){ out.push(iso(d)); d.setDate(d.getDate()+1); }
return out;
}
function nightCount(){ return nights().length; }
function isFree(room){
var ns = nights();
if(!ns.length) return false;
for(var i=0;i1?”s”:””),
amount:r.rate * n, sub:rupees(r.rate) + ” a night” });
});
var total = rooms;
if(S.ac === “AC”){
var acSum = 0;
S.picked.forEach(function(r){ if(r.ac) acSum += TARIFF.acAddOn[r.type] * n; });
if(acSum){ lines.push({label:”Air conditioning”, amount:acSum}); total += acSum; }
}
if(S.beds > 0){
var bed = TARIFF.extraBed[S.ac] * S.beds * n;
lines.push({ label:S.beds + ” extra bed” + (S.beds>1?”s”:””), amount:bed,
sub:rupees(TARIFF.extraBed[S.ac]) + ” each a night” });
total += bed;
}
if(S.late){
var up = 0;
S.picked.forEach(function(r){ up += r.rate * TARIFF.lateCheckoutUplift; });
lines.push({ label:”Check out at 5 PM”, amount:up, sub:”half a night” });
total += up;
}
var advance = Math.round(total * TARIFF.advanceFraction);
return { n:n, lines:lines, total:Math.round(total), advance:advance };
}
/* ── Rendering ────────────────────────────────────────────────────────── */
function drawRooms(){
var ul = $(“ezh-rooms”), n = nightCount();
ul.innerHTML = “”;
ROOMS.forEach(function(r){
var free = n ? isFree(r) : true;
var on = S.picked.indexOf(r) !== -1;
var li = document.createElement(“li”);
li.className = “room” + (on?” on”:””) + (n && !free ? ” out”:””);
var shot = r.photo
? ‘Courtallam · Tamil Nadu
Book direct at Kutralam Ezhilagam
Nine rooms, minutes from Five Falls. Booking here costs less than the travel sites — no commission sits between us.
Check in
Check out
Adults
2
Children
0
Choose your rooms
Pick your dates to see what is free.
‘ +
‘
‘;
if(!n || free){
li.querySelector(“.pick”).addEventListener(“click”, function(){
var i = S.picked.indexOf(r);
if(i === -1) S.picked.push(r); else S.picked.splice(i,1);
redraw();
});
}
ul.appendChild(li);
});
}
function drawBill(){
var q = quote(), box = $(“ezh-bill”);
if(!q.total){ box.innerHTML = “”; return; }
var html = q.lines.map(function(l){
return ‘‘+r.name+’
‘ + ‘‘+r.type+’ · ‘+r.floor+’ · up to ‘+r.max+’
‘ +
‘‘+r.blurb+’
‘ + ‘‘ + ‘‘+rupees(r.rate)+’a night’ +
(r.ac ? ” : ‘ · non-AC only’) + ‘
‘ +
‘‘ +
‘‘+l.label+
(l.sub ? ‘
‘+l.sub+’‘ : ”)+ ‘‘+rupees(l.amount)+’
‘;
}).join(“”);
html += ‘‘+l.sub+’‘ : ”)+ ‘‘+rupees(l.amount)+’
Total for the stay‘+rupees(q.total)+’
‘;
html += ‘Pay now to hold the room’ +
‘‘+rupees(q.total – q.advance)+’ at check-in‘ +
‘‘+rupees(q.advance)+’
‘;
box.innerHTML = html;
}
function redraw(){
var n = nightCount();
$(“ezh-ad-v”).textContent = S.adults;
$(“ezh-ch-v”).textContent = S.children;
$(“ezh-beds-v”).textContent = S.beds;
$(“ezh-nights”).textContent = n
? n + ” night” + (n>1?”s”:””) + ” · ” + pretty(S.checkIn) + ” to ” + pretty(S.checkOut)
: “”;
$(“ezh-avail-note”).textContent = n
? “Free on your dates. Rooms already taken are marked.”
: “Pick your dates to see what is free.”;
drawRooms();
var has = S.picked.length > 0 && n > 0;
[“ezh-extras-sec”,”ezh-bill-sec”,”ezh-you-sec”].forEach(function(id){
$(id).classList.toggle(“hide”, !has);
});
if(has) drawBill();
/* AC only offered when a selected room can actually take it */
var anyAc = S.picked.some(function(r){ return r.ac; });
$(“ezh-ac”).disabled = !anyAc;
if(!anyAc && S.ac === “AC”){ S.ac = “Non-AC”; $(“ezh-ac”).value = “Non-AC”; }
}
function say(kind, text){
$(“ezh-msg”).innerHTML = ‘‘+text+’
‘;
}
function clearSay(){ $(“ezh-msg”).innerHTML = “”; }
/* ── Talking to the sheet ─────────────────────────────────────────────── */
function get(action, extra){
var url = EXEC + “?action=” + action + (extra||””) + “&t=” + Date.now();
return fetch(url, {method:”GET”, redirect:”follow”}).then(function(r){
if(!r.ok) throw new Error(action + ” ” + r.status);
return r.json();
});
}
function post(body){
return fetch(EXEC, {
method:”POST”, redirect:”follow”,
headers:{“Content-Type”:”text/plain;charset=utf-8″},
body:JSON.stringify(body)
}).then(function(r){
if(!r.ok) throw new Error(“request ” + r.status);
return r.json();
});
}
function loadAvailability(){
return get(“publicAvail”,”&days=120″).then(function(d){
S.busy = (d && d.busy) || {};
redraw();
}).catch(function(){
say(“info”,”We could not check availability just now. You can still book — ” +
“we will confirm the room by WhatsApp.”);
});
}
function loadPayConfig(){
return get(“payConfig”).then(function(d){
S.pay = d;
if(d && d.upi && d.upi.vpa) $(“ezh-vpa”).textContent = d.upi.vpa;
}).catch(function(){ S.pay = null; });
}
/* Razorpay’s script is only fetched when it is actually needed. */
function loadRazorpay(){
return new Promise(function(res, rej){
if(window.Razorpay) return res();
var s = document.createElement(“script”);
s.src = “https://checkout.razorpay.com/v1/checkout.js”;
s.onload = res;
s.onerror = function(){ rej(new Error(“checkout script”)); };
document.head.appendChild(s);
});
}
/* ── Booking ──────────────────────────────────────────────────────────── */
function validate(){
var name = $(“ezh-name”).value.trim();
var phone = $(“ezh-phone”).value.replace(/\D/g,””);
if(name.length cap)
return “Those rooms sleep ” + cap + “. Please add another room, or reduce the guests.”;
return null;
}
function payload(extra){
var q = quote();
var base = {
action:”webBooking”,
name:$(“ezh-name”).value.trim(),
phone:”+91″ + $(“ezh-phone”).value.replace(/\D/g,””).slice(-10),
email:$(“ezh-email”).value.trim(),
room:S.picked.map(function(r){ return r.name; }).join(“,”),
checkIn:S.checkIn, checkOut:S.checkOut,
checkInTime:TARIFF.checkInDefault,
checkOutTime:S.late ? TARIFF.lateHour : TARIFF.checkOutDefault,
adults:S.adults, children:S.children,
extraBeds:S.beds, acPreference:S.ac,
total:q.total, reservationPaid:q.advance, balance:q.total – q.advance,
paymentStatus:”Advance Paid”
};
for(var k in extra) base[k] = extra[k];
return base;
}
function confirmed(res){
$(“ezh-you-sec”).classList.add(“hide”);
$(“ezh-bill-sec”).classList.add(“hide”);
$(“ezh-extras-sec”).classList.add(“hide”);
$(“ezh-done-sec”).classList.remove(“hide”);
$(“ezh-done-id”).textContent = res.bookingId || res.serial || “”;
$(“ezh-done-text”).textContent =
“We have sent the details to your WhatsApp. Keep this number for check-in — ” +
“front desk 04633 284004.”;
$(“ezh-done-sec”).scrollIntoView({behavior:”smooth”, block:”center”});
}
function busy(on, label){
S.working = on;
var b = $(“ezh-pay”);
b.disabled = on;
b.innerHTML = on ? ‘‘ + (label||”Working”) : “Pay the advance and book”;
}
$(“ezh-pay”).addEventListener(“click”, function(){
if(S.working) return;
clearSay();
var bad = validate();
if(bad) return say(“bad”, bad);
var q = quote();
if(!q.advance) return say(“bad”,”Please choose your dates and rooms first.”);
if(!S.pay || !S.pay.razorpay || !S.pay.razorpay.enabled){
say(“info”,”Card payment is not switched on yet. Please use the UPI option below — ” +
“we confirm the room as soon as the screenshot arrives.”);
$(“ezh-upi”).classList.remove(“hide”);
return;
}
busy(true,”Opening payment”);
loadRazorpay()
.then(function(){
return post({ action:”createOrder”, amount:q.advance * 100,
receipt:”EZ” + Date.now() });
})
.then(function(order){
if(!order || order.ok === false || !order.orderId)
throw new Error((order && order.message) || “Could not start the payment.”);
busy(false);
var rz = new window.Razorpay({
key: S.pay.razorpay.keyId,
order_id: order.orderId,
amount: order.amount,
currency: order.currency || “INR”,
name: HOTEL.name,
description: S.picked.map(function(r){ return r.name; }).join(“, “) +
” · ” + pretty(S.checkIn),
prefill:{ name:$(“ezh-name”).value.trim(),
contact:$(“ezh-phone”).value.replace(/\D/g,””),
email:$(“ezh-email”).value.trim() },
theme:{ color:”#C61830″ },
modal:{ ondismiss:function(){
busy(false);
say(“info”,”Payment cancelled — nothing has been charged and no room is held. ” +
“You can try again whenever you are ready.”);
}},
handler:function(r){
busy(true,”Confirming your room”);
post(payload({
paymentRef: r.razorpay_payment_id || “”,
razorpayOrderId: r.razorpay_order_id || “”,
razorpaySignature: r.razorpay_signature || “”
})).then(function(res){
busy(false);
if(!res || res.ok === false){
return say(“bad”, (res && res.message) ||
“Your payment went through but we could not hold the room. ” +
“Please send us a WhatsApp on ” + HOTEL.phone + ” — you have not been charged twice.”);
}
confirmed(res);
}).catch(function(){
busy(false);
say(“bad”,”Your payment went through but we could not reach our system. ” +
“Please send us a WhatsApp on ” + HOTEL.phone + ” with this page open — ” +
“your money is safe and we will hold the room.”);
});
}
});
rz.on(“payment.failed”, function(e){
busy(false);
var m = e && e.error && e.error.description;
say(“bad”,”The payment did not go through” + (m ? ” — ” + m : “”) +
“. No room has been held and nothing has been charged.”);
});
rz.open();
})
.catch(function(err){
busy(false);
say(“bad”, (err && err.message) ||
“We could not start the payment. Please try the UPI option below.”);
$(“ezh-upi”).classList.remove(“hide”);
});
});
/* ── UPI fallback ─────────────────────────────────────────────────────── */
$(“ezh-upi-btn”).addEventListener(“click”, function(){
$(“ezh-upi”).classList.toggle(“hide”);
var q = quote();
var msg = “Hello, I would like to book ” +
S.picked.map(function(r){ return r.name; }).join(“, “) +
” from ” + pretty(S.checkIn) + ” to ” + pretty(S.checkOut) +
“. Advance ” + rupees(q.advance) + “. Screenshot attached.”;
$(“ezh-wa”).href = “https://wa.me/” + HOTEL.whatsapp + “?text=” + encodeURIComponent(msg);
});
$(“ezh-copy”).addEventListener(“click”, function(){
var v = $(“ezh-vpa”).textContent;
var done = function(){ $(“ezh-copy”).textContent = “Copied”;
setTimeout(function(){ $(“ezh-copy”).textContent = “Copy”; }, 1800); };
if(navigator.clipboard) navigator.clipboard.writeText(v).then(done, function(){});
else done();
});
/* ── Inputs ───────────────────────────────────────────────────────────── */
document.querySelectorAll(“#ezh [data-step]”).forEach(function(b){
b.addEventListener(“click”, function(){
var p = b.getAttribute(“data-step”).split(“,”), k = p[0], d = parseInt(p[1],10);
if(k === “ad”) S.adults = Math.min(30, Math.max(1, S.adults + d));
if(k === “ch”) S.children = Math.min(20, Math.max(0, S.children + d));
if(k === “bed”) S.beds = Math.min(TARIFF.maxExtraBeds, Math.max(0, S.beds + d));
redraw();
});
});
$(“ezh-in”).addEventListener(“change”, function(){
S.checkIn = this.value;
if(S.checkOut && S.checkOut <= S.checkIn){
var d = new Date(S.checkIn+"T12:00:00"); d.setDate(d.getDate()+1);
S.checkOut = iso(d); $("ezh-out").value = S.checkOut;
}
$("ezh-out").min = S.checkIn;
S.picked = [];
redraw();
});
$("ezh-out").addEventListener("change", function(){
S.checkOut = this.value; S.picked = []; redraw();
});
$("ezh-ac").addEventListener("change", function(){ S.ac = this.value; redraw(); });
$("ezh-late").addEventListener("change", function(){ S.late = this.checked; redraw(); });
/* ── Start ────────────────────────────────────────────────────────────── */
(function start(){
var t = new Date(); t.setDate(t.getDate()+1);
var t2 = new Date(); t2.setDate(t2.getDate()+2);
S.checkIn = iso(t); S.checkOut = iso(t2);
$("ezh-in").value = S.checkIn; $("ezh-in").min = iso(new Date());
$("ezh-out").value = S.checkOut; $("ezh-out").min = S.checkIn;
redraw();
loadPayConfig();
loadAvailability();
})();
})();