PageSourceSearch

https://casa-italia.pl/reserve

html casa-italia.pl collected 2026-10-08 09:31:14 UTC 76,507 bytes, 2,314 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5	<meta charset="UTF-8" />
6	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7	<meta name="apple-itunes-app" content="app-id=6736680726, app-argument=goodresto://goodresto.app/resto/405036819902/reserve" />
8	<meta name="google-play-app" content="app-id=app.goodresto.android" />
9
10	<title>Casa Italia - Table Reservation - GoodResto</title>
11	<meta name="title" content="Casa Italia - Table Reservation - GoodResto">
12	<meta name="description" content="Reserve your table at Casa Italia. Choose a date, find available time slots, and provide additional info for the restaurant.">
13	<meta name="keywords" content="Casa Italia, table reservation, book a table, schedule, restaurant, dining, GoodResto">
14
15	<meta property="og:type" content="website">
16	<meta property="og:title" content="Casa Italia - Table Reservation - GoodResto">
17	<meta property="og:url" content="https://casa-italia.pl/reserve">
18	<meta property="og:description" content="Reserve your table at Casa Italia. Choose a date, find available time slots, and provide additional info for the restaurant.">
19	<meta property="og:image" content="https://cdn.goodresto.app/id/382549298295_md">
20	<meta property="og:site_name" content="GoodResto">
21
22	<meta name="google" content="notranslate">
23	<meta name="google" content="max-image-preview:standard">
24
25	<link rel="canonical" href="https://casa-italia.pl/reserve">
26	<link rel="icon" href="/favicon.ico">
27	<link rel="apple-touch-icon" href="/GoodRestoLogo-apple.png">
28
29	
29<script src="https://widget.goodresto.app/minialert.js"></script>
29
30	
30<script src="/tailwind-v3.4.16.js"></script>
30
31	
31<script src="https://goodresto.app/index.js"></script>
31
32	<link rel="preconnect" href="https://fonts.googleapis.com">
33	<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
34	
35	<link href="https://fonts.googleapis.com/css2?family=Courgette:wght@400;700&display=swap" rel="stylesheet">
36	
37	<link href="https://fonts.googleapis.com/css2?family=Courgette:wght@400;700&display=swap" rel="stylesheet">
38	
38<script>
39		tailwind.config = {
40			darkMode: 'class',
41			theme: {
42				extend: {},
43			}
44		}
45	</script>
45
46	<style>
47		* {
48			transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
49		}
50
51		*,
52		*:before,
53		*:after {
54			box-sizing: border-box;
55		}
56
57		[hidden] {
58			display: none !important;
59		}
60
61		
62		@media (min-width: 640px) {
63			body {
64				background-image: url('https://cdn.goodresto.app/id/382871171812_lg');
65				background-size: cover;
66				background-position: center;
67				background-attachment: fixed;
68				background-repeat: no-repeat;
69			}
70		}
71		
72
73		.gdrst-bkgd {
74			background-color: #f2d24b;
75		}
76
77		.gdrst-bkgd-button {
78			background-color: #4e8275;
79		}
80
81		.gdrst-button-text {
82			color: #fffbfb;
83		}
84
85		.gdrst-nav {
86			background-color: #f2d24b;
87		}
88
89		.gdrst-nav-alt {
90			background-color: #F4DB6F;
91		}
92
93		.gdrst-nav-fill {
94			fill: #f2d24b;
95		}
96			
97		.gdrst-nav-text {
98			color: #d43a33;
99			font-family: Courgette, serif;
100		}
101
102		.gdrst-nav-text-alt {
103			color: color-contrast(var(--base-color) vs white, black);
104		}
105
106		.gdrst-nav-text svg {
107			stroke: currentColor;
108			fill: currentColor;
109		}
110
111		.gdrst-body {
112			background-color: #f3f4f6;
113			opacity: 89%;
114		}
115
116		.gdrst-body-alt {
117			background-color: #F5F6F7;
118		}
119
120		.gdrst-body-text {
121			color: #374151;
122			font-family: Courgette, serif;
123		}
124
125		.gdrst-body-divider {
126			color: #374151;
127		}
128
129		.gdrst-social-logo svg {
130			color: #f2d24b;
131			stroke: currentColor;
132			fill: currentColor;
133		}</style>
134	<style>
135		@media (max-width: 639px) {
136			.step-indicator.is-active .step-label {
137				display: inline;
138			}
139		}
140
141		.step-dot {
142			width: 14px;
143			height: 14px;
144			border-radius: 999px;
145			background: #dc2626;
146		}
147
148		.step-line {
149			flex: 1;
150			height: 2px;
151			background: #fecaca;
152		}
153
154		.time-slot.selected {
155			@apply gdrst-nav gdrst-nav-text;
156		}
157
158		.people-option.selected {
159			@apply gdrst-nav gdrst-nav-text;
160		}
161	</style>
162</head>
163
164<body class="gdrst-bkgd overflow-x-hidden overflow-y-scroll font-sans flex flex-col min-h-screen">
165	<nav class="shadow-lg sticky top-0 z-40">
166		<div id="header-container"></div>
167	</nav>
168
169	<main class="flex-grow md:p-4">
170		<div class="gdrst-body w-full max-w-4xl rounded-lg mx-auto p-4 mt-2">
171
172			
173			<div class="flex items-center mb-2 px-1">
174				<div data-step="0" class="step-indicator flex items-center gap-2 text-sm font-medium">
175					<span class="step-icon flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 gdrst-body-text">
176						<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
177							<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
178							<circle cx="9" cy="7" r="4" />
179							<path d="M22 21v-2a4 4 0 0 0-3-3.87" />
180							<path d="M16 3.13a4 4 0 0 1 0 7.75" />
181						</svg>
182					</span>
183					<span class="hidden sm:inline step-label gdrst-body-text">Guests</span>
184				</div>
185
186				<div class="mx-3 h-px flex-1 gdrst-body-alt"></div>
187
188				<div data-step="1" class="step-indicator flex items-center gap-2 text-sm font-medium text-gray-400">
189					<span class="step-icon flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 gdrst-body-text">
190						<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
191							<rect x="3" y="4" width="18" height="18" rx="2" />
192							<line x1="16" y1="2" x2="16" y2="6" />
193							<line x1="8" y1="2" x2="8" y2="6" />
194							<line x1="3" y1="10" x2="21" y2="10" />
195							<circle cx="16" cy="16" r="3" />
196							<line x1="16" y1="14.5" x2="16" y2="16" />
197							<line x1="16" y1="16" x2="17" y2="17" />
198						</svg>
199					</span>
200					<span class="hidden sm:inline step-label gdrst-body-text">Date &amp; time</span>
201				</div>
202
203				<div class="mx-3 h-px flex-1 gdrst-body-alt"></div>
204
205				<div data-step="2" class="step-indicator flex items-center gap-2 text-sm font-medium text-gray-400">
206					<span class="step-icon flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 gdrst-body-text">
207						<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
208							<circle cx="12" cy="7" r="4" />
209							<path d="M5.5 21a6.5 6.5 0 0 1 13 0" />
210						</svg>
211					</span>
212					<span class="hidden sm:inline step-label gdrst-body-text">Contact</span>
213				</div>
214
215				<div class="mx-3 h-px flex-1 gdrst-body-alt"></div>
216
217				<div data-step="3" class="step-indicator flex items-center gap-2 text-sm font-medium text-gray-400">
218					<span class="step-icon flex h-8 w-8 items-center justify-center rounded-full border border-gray-300 gdrst-body-text">
219						<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
220							<circle cx="12" cy="12" r="10" />
221							<path d="m8 12 2.5 2.5L16 9" />
222						</svg>
223					</span>
224					<span class="hidden sm:inline step-label gdrst-body-text">Confirm</span>
225				</div>
226			</div>
227
228			<div class="gdrst-body-alt rounded-lg shadow-lg p-4">
229
230				
231
232				<div class="step" data-step="0">
233					<div class="w-full max-w-xl mx-auto">
234
235						<div id="noticeContainer" class="hidden restaurant-note bg-[#fff8e1] rounded-lg p-4 flex gap-3 items-start mb-2">
236							<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-yellow-600 flex-shrink-0" viewB
236ox="0 0 24 24" fill="currentColor">
237								<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"></path>
238							</svg>
239
240							<div class="note-content">
241								<strong class="block mb-1">Message from restaurant</strong>
242
243								<p id="noticeIndividual" class="text-md leading-relaxed whitespace-pre-line"></p>
244								<p id="noticeGroup" class="hidden text-md leading-relaxed whitespace-pre-line"></p>
245
246								<button id="noticeMore" type="button" class="hidden mx-auto mt-2 text-md font-semibold underline">Expand</button>
247							</div>
248						</div>
249
250						<h2 class="gdrst-body-text text-xl font-bold mb-2 text-center">Choose the number of guests</h2>
251
252						<div id="peopleGrid" class="grid grid-cols-3 sm:grid-cols-6 gap-3 mb-2">
253							
254							<button type="button" data-people="1" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
255								1
256							</button>
257							
258							<button type="button" data-people="2" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
259								2
260							</button>
261							
262							<button type="button" data-people="3" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
263								3
264							</button>
265							
266							<button type="button" data-people="4" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
267								4
268							</button>
269							
270							<button type="button" data-people="5" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
271								5
272							</button>
273							
274							<button type="button" data-people="6" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
275								6
276							</button>
277							
278							<button type="button" data-people="7" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
279								7
280							</button>
281							
282							<button type="button" data-people="8" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
283								8
284							</button>
285							
286							<button type="button" data-people="9" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
287								9
288							</button>
289							
290							<button type="button" data-people="10" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
291								10
292							</button>
293							
294							<button type="button" data-people="11" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
295								11
296							</button>
297							
298							<button type="button" data-people="12" class="people-option gdrst-body-text font-medium rounded-lg border px-4 py-3">
299								12
300							</button>
301							
302						</div>
303
304						
305						<div class="mb-2 text-center">
306							<input id="peopleCustom" type="number" min="16" max="100" class="hidden w-full rounded-lg border mb-3 px-3 py-2" placeholder="Number of people">
307
308							<button id="peopleMore" type="button" class="font-medium gdrst-body-text border rounded-lg p-2 hover:underline">More guests?</button>
309						</div>
310						
311
312						<div class="flex justify-end mt-2">
313							<button id="continueBtn" disabled class="btn-next gdrst-nav gdrst-nav-text px-6 py-3 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed">Continue →</button>
314						</div>
315					</div>
316				</div>
317
318				
319
320				<div class="step hidden" data-step="1">
321
322					<div class="flex justify-between items-center mb-2">
323
324						<button class="btn-back text-red-600 font-semibold">← Back</button>
325
326					</div>
327
328					<div id="date-time-picker" class="w-full max-w-xl mx-auto">
329
330						
331						<div class="flex items-center justify-between mb-4">
332							<button type="button" id="calendar-prev" class="gdrst-body-text w-10 h-10 rounded-lg border">←</button>
333
334							<h2 id="calendar-title" class="text-lg font-bold gdrst-body-text"></h2>
335
336							<button type="button" id="calendar-next" class="gdrst-body-text w-10 h-10 rounded-lg border">→</button>
337						</div>
338
339						
340						<div class="grid grid-cols-7 gap-2 mb-2 text-center">
341							<div class="text-xs font-semibold gdrst-body-text">Mon</div>
342							<div class="text-xs font-semibold gdrst-body-text">Tue</div>
343							<div class="text-xs font-semibold gdrst-body-text">Wed</div>
344							<div class="text-xs font-semibold gdrst-body-text">Thu</div>
345							<div class="text-xs font-semibold gdrst-body-text">Fri</div>
346							<div class="text-xs font-semibold gdrst-body-text">Sat</div>
347							<div class="text-xs font-semibold gdrst-body-text">Sun</div>
348						</div>
349
350						
351						<div id="calendar" class="grid grid-cols-7 grid-rows-6 gap-2 mb-2"></div>
352
353						<div id="noSlotsNoticeContainer" class="hidden bg-[#fff8e1] rounded-lg p-4 flex gap-3 items-start mb-2">
354							<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-yellow-600 flex-shrink-0" viewB
354ox="0 0 24 24" fill="currentColor">
355								<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"></path>
356							</svg>
357
358							<div>
359								<strong id="noSlotsNotice" class="block mb-1">No slots for this day, try another day.</strong>
360							</div>
361						</div>
362
363						<a id="eventCard" href="#" target="_blank" rel="noopener" class="hidden mb-3 flex items-center gap-3 rounded-xl border border-amber-200 bg-amber-50 p-4 transition hover:bg-amber-100">
364							<div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-amber-100 text-xl">✨</div>
365
366							<div class="min-w-0 flex-1">
367								<div id="eventCardTitle" class="text-sm font-semibold text-amber-900"></div>
368								<p id="eventCardDescription" class="mt-1 text-sm text-amber-800"></p>
369							</div>
370
371							<svg class="h-5 w-5 shrink-0 text-amber-700" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
372								<path d="M5 12h14M13 6l6 6-6 6"></path>
373							</svg>
374						</a>
375
376						
377
378						
379						<div id="durationContainer" class="border rounded-lg p-[10px_18px] text-base w-full relative cursor-pointer mb-2 flex justify-between items-center">
380							<div class="gdrst-body-text">Duration</div>
381
382							<div class="relative">
383								<select id="duration" class="appearance-none border rounded-lg px-[15px] py-3 text-base w-full pr-10 gdrst-body-alt gdrst-body-text">
384									<option value="2h">2 hours</option>
385								</select>
386
387								<svg class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 -960 960 960" fill="currentColor">
388									<path d="M480-360 280-560h400L480-360Z" />
389								</svg>
390							</div>
391						</div>
392
393						
394						<div id="slotsGrid" class="grid grid-cols-3 sm:grid-cols-6 gap-2 mb-2"></div>
395
396						<div id="priceSlotInfo" class="hidden flex items-start mt-2 gap-3 rounded-lg border border-green-200 bg-green-50 p-3">
397							<span class="text-green-600 text-md font-bold leading-none">$</span>
398
399							<p class="text-sm text-green-700">The dollar sign indicates that reservations for this party size at this time require a deposit or advance payment, depending on the restaurant&#39;s policy.</p>
400						</div>
401
402						<div id="autoSlotInfo" class="hidden flex items-start mt-2 gap-3 rounded-lg border border-green-200 bg-green-50 p-3">
403							<span class="mt-1 w-2 h-2 bg-green-500 rounded-full shrink-0"></span>
404
405							<p class="text-sm text-green-700">The green dot indicates an available table for the selected criteria – reservations for this time are confirmed automatically.</p>
406						</div>
407
408						<div class="flex justify-end mt-2">
409							<button id="continueBtn" disabled class="btn-next gdrst-nav gdrst-nav-text px-6 py-3 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed">Continue →</button>
410						</div>
411					</div>
412
413				</div>
414
415				
416
417				<div class="step hidden" data-step="2">
418
419					<div class="flex justify-between items-center mb-2">
420						<button class="btn-back text-red-600 font-semibold">← Back</button>
421
422						<h2 class="hidden md:block absolute left-1/2 -translate-x-1/2 text-xl font-bold gdrst-body-text">Contact details</h2>
423					</div>
424
425					<div class="w-full max-w-xl mx-auto">
426						<h2 class="block md:hidden gdrst-body-text text-xl font-bold mb-2 text-center">Contact details</h2>
427
428						<div id="main-form" class="form-section space-y-4">
429
430							<div class="form-group">
431								<input type="text" id="first-name" autocomplete="given-name" required placeholder="First name" data-i18n-hint="" class="w-full gdrst-body gdrst-body-text border rounded-lg px-3 py-3 placeholder:opacity-70">
432							</div>
433
434							<div class="form-group">
435								<input type="text" id="last-name" autocomplete="family-name" required placeholder="Last name" data-i18n-hint="" class="w-full gdrst-body gdrst-body-text border rounded-lg px-3 py-3 placeholder:opacity-70">
436							</div>
437
438							<div class="form-group">
439								<input type="email" id="email" autocomplete="email" required placeholder="Email" data-i18n-hint="email" class="w-full gdrst-body gdrst-body-text border rounded-lg px-3 py-3 placeholder:opacity-70">
440							</div>
441
442							<div class="form-group flex items-center gap-1">
443								<div id="phonePrefix" class="w-80"></div>
444								<input type="tel" id="phone" autocomplete="tel" required placeholder="Phone" data-i18n-hint="phone" class="w-full gdrst-body gdrst-body-text border rounded-lg px-3 py-3 placeholder:opacity-70">
445							</div>
446						</div>
447
448						<div class="flex justify-end mt-2">
449							<button id="continueBtn" disabled class="btn-next gdrst-nav gdrst-nav-text px-6 py-3 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed">Continue →</button>
450						</div>
451					</div>
452
453				</div>
454
455				
456
457				<div class="step hidden" data-step="3">
458					<div class="flex justify-between items-center mb-2">
459						<button class="btn-back text-red-600 font-semibold">← Back</button>
460
461						<h2 class="hidden md:block absolute left-1/2 -translate-x-1/2 text-xl font-bold gdrst-body-text">Details</h2>
462					</div>
463
464					<div class="w-full max-w-xl mx-auto">
465						<h2 class="block md:hidden gdrst-body-text text-xl font-bold mb-2 text-center">Details</h2>
466
467						
468						<div class="border rounded-lg p-[10px_18px] text-base w-full relative cursor-pointer mb-2 flex justify-between items-center">
469
470							<label for="visitKind" class="block gdrst-body-text">Type of visit</label>
471
472							<div class="relative">
473								<select id="visitKind" class="appearance-none border rounded-lg px-[15px] py-3 text-base w-full pr-10 gdrst-body-alt gdrst-body-text">
474									<option value="other">Other</option>
475									<option value="business">Business</option>
476									<option value="traveler">Traveler</option>
477									<option value="romantic">Romantic</option>
478									<option value="family">Family</option>
479									<option value="birthday">Birthday</option>
480									<option value="colleagues"></option>
481									<option value="anniversary">Anniversary</option>
482									<option value="friends">Friends</option>
483								</select>
484
485								<svg class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 -960 960 960" fill="currentColor">
486									<path d="M480-360 280-560h400L480-360Z" />
487								</svg>
488							</div>
489						</div>
490
491
492						
493						<div class="form-group mb-4">
494							<label for="specialNote" class="block gdrst-body-text mb-2">Comment (optional)</label>
495
496							<textarea id="specialNote" name="specialNote" rows="3" class="placeholder:italic border gdrst-body gdrst-body-text rounded-lg px-[15px] py-3 text-base w-full resize-none" placeholder="E.g. special seating requests, accessibility needs, or other preferences…"></textarea>
497						</div>
498
499						
500						
501						<div class="mb-4">
502
503							<label class="flex items-center gap-2 cursor-pointer">
504								<input type="checkbox" id="taxInvoice" name="tax_invoice" class="size-4">
505
506								<span class="gdrst-body-text">I want an invoice</span>
507							</label>
508
509
510							<div id="vatDetails" class="hidden mt-3 space-y-3">
511
512								<input type="text" id="taxNumber" name="vat_id" autocomplete="organization" data-i18n-hint="taxNumber" class="border placeholder:italic rounded-lg px-[15px] py-3 text-base w-full" placeholder="VAT identification number">
513
514								<input type="text" id="companyName" name="company_name" autocomplete="organization" data-i18n-hint="companyName" class="border placeholder:italic rounded-lg px-[15px] py-3 text-base w-full" placeholder="Company name">
515							</div>
516
517						</div>
518						
519
520						<div class="mb-4 border-b"></div>
521
522						
523						<div class="mt-5 space-y-4">
524
525							
526							<label class="flex items-center gap-3 cursor-pointer">
527								<input type="checkbox" id="selectAllAgreements" class="size-4 shrink-0">
528
529								<span class="gdrst-body-text font-medium">Select all agreements</span>
530							</label>
531
532							
533							<div class="flex-1">
534								<input type="checkbox" id="termsAgreement" class="mt-1 size-4 shrink-0">
535
536								<label class="gdrst-body-text" for="termsAgreement">
537									* I accept terms &amp; privacy policy
538								</label>
539
540								<details class="inline">
541									<summary class="gdrst-body-text inline cursor-pointer text-sm uppercase">
542										Expand
543									</summary>
544
545									<div class="block">
546										<p class="gdrst-body-text mt-2 ml-6 text-sm leading-relaxed">
547											<a href="https://cdn.goodresto.app/static/web-terms" target="_blank" rel="noopener noreferrer" class="terms-link">
548												terms
549											</a>
550											<span>&nbsp; &amp; &nbsp;</span>
551											<a href="https://cdn.goodresto.app/static/web-privacy" target="_blank" rel="noopener noreferrer" class="terms-link">
552												privacy policy
553											</a>
554										</p>
555									</div>
556								</details>
557							</div>
558
559							
560							<div class="flex-1">
561
562								<input type="checkbox" id="marketingRestoAgreement" class="mt-1 size-4 shrink-0">
563
564								<label for="marketingRestoAgreement" class="gdrst-body-text cursor-pointer">
565									I agree to receive marketing information from the restaurant.
566								</label>
567
568								<details class="inline">
569									<summary class="gdrst-body-text inline cursor-pointer text-sm uppercase">
570										Expand
571									</summary>
572
573									<div class="block">
574										<p class="gdrst-body-text mt-2 ml-6 text-sm leading-relaxed">
575											I consent to receiving marketing information from Restauracja Casa Italia  by electronic means (e-mail, SMS, phone). I can withdraw my consent at any time.
576										</p>
577									</div>
578								</details>
579
580							</div>
581
582
583							
584							<div class="flex-1">
585
586								<input type="checkbox" id="marketingGdrstAgreement" class="mt-1 size-4 shrink-0">
587
588								<label for="marketingGdrstAgreement" class="gdrst-body-text cursor-pointer">
589									I agree to receive marketing information from GoodResto.
590								</label>
591
592								<details class="inline">
593									<summary class="gdrst-body-text inline cursor-pointer text-sm uppercase">
594										Expand
595									</summary>
596
597									<div class="block">
598										<p class="gdrst-body-text mt-2 ml-6 text-sm leading-relaxed">
599											I consent to receiving from GoodResto sp. z o.o. marketing information including promotions, offers, and news regarding the platform and partner restaurants by electronic means (email, SMS, phone). I can withdraw my consent at any time.
600										</p>
601									</div>
602								</details>
603
604
605							</div>
606
607						</div>
608
609						
610						<div class="flex justify-center mt-6">
611							<button id="submitButton" type="submit" onclick="submitReservation()" class="gdrst-nav gdrst-nav-text reserve-button rounded-lg flex w-full h-16 items-center justify-center gap-2 sm:w-1/2">
612								<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" stroke="currentColor" height="20" width="20" viewBox="0 0 24 24" aria-hidden="true">
613									<path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M16.2,16.2L11,13V7H12.5V12.2L17,14.7L16.2,16.2Z"></path>
614								</svg>
615
616								<span class="gdrst-nav-text">Reserve</span>
617							</button>
618						</div>
619
620						<div class="text-center text-sm gdrst-body-text mt-4">Your best restaurant experience with GoodResto ❤️</div>
621					</div>
622				</div>
623
624			</div>
625		</div>
626	</main>
627
628	<footer class="gdrst-nav px-4 py-4 sm:px-6">
629		<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
630			<div class="gdrst-nav-text order-2 sm:order-1 text-center sm:text-left">
631				<a href="https://goodresto.app" class="hover:underline">© 2026 GoodResto</a>
632			</div>
633
634			<div class="flex flex-wrap justify-center sm:justify-start gap-x-4 gap-y-2 order-1 sm:order-2">
635				<a href="https://goodresto.app/privacy" class="gdrst-nav-text">Privacy</a>
636				<a href="https://goodresto.app/terms" class="gdrst-nav-text">Terms</a>
637			</div>
638
639			<div id="darkModeContainer" class="hidden order-3 flex justify-center sm:justify-end"></div>
640		</div>
641	</footer>
642
643	
643<script id="page-data" type="application/json">{"groupMax":75,"groupMin":13,"groupNotice":"","hasGroups":true,"i18nDays":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],"i18nMonths":["January","February","March","April","May","June","July","August","September","October","November","December"],"idempotencyKey":"1YofF1J1nItRctfMGQrzPrbGtRqTjFQp","individualNotice":"","maxDays":180,"peopleMax":12,"peopleMin":1}</script>
643
644	
644<script id="lang-data" type="application/json">{"cs":"Čeština","da":"Dansk","de":"Deutsch","el":"Ελληνικά","en":"English","es":"Español","fr":"Français","hr":"Hrvatski","hu":"Magyar","it":"Italiano","nl":"Nederlands","no":"Norsk","pl":"Polski","pt":"Português","ro":"Română","sk":"Slovenčina","sl":"Slovenščina","sv":"Svenska","uk":"Українська"}</script>
644
645	
645<script>
646		const PAGEDATA = JSON.parse(document.getElementById("page-data").textContent);
647		const pageLangs = JSON.parse(document.getElementById("lang-data").textContent);
648		const urlParams = new URLSearchParams(window.location.search);
649		const sessionId = 'zXyFs1b4DkVBl9De';
650		const restoId = '405036819902';
651
652		const hostname = location.hostname === "localhost"
653			? "http://localhost:8080"
654			: location.hostname === "canary-web.goodresto.app"
655				? "https://canary-web.goodresto.app"
656				: "https://goodresto.app";
657
658		let currentLang = getLang();
659
660		
661		function formatDate(date) {
662			const year = date.getFullYear();
663			const month = String(date.getMonth() + 1).padStart(2, "0");
664			const day = String(date.getDate()).padStart(2, "0");
665
666			return `${year}-${month}-${day}`;
667		}
668
669		function parseDate(value) {
670			const [year, month, day] = value.split("-").map(Number);
671
672			return new Date(year, month - 1, day);
673		}
674
675		function unixSecondsToDate(unixSeconds) {
676			return new Date(unixSeconds * 1000);
677		}
678
679		function startOfDay(date) {
680			return new Date(date.getFullYear(), date.getMonth(), date.getDate());
681		}
682
683		function addDays(date, days) {
684			const result = new Date(date);
685			result.setDate(result.getDate() + days);
686			return startOfDay(result);
687		}
688
689		function showError(err) {
690			window.MiniAlert.fire({
691				title: "Error",
692				text: err,
693
694				buttons: [
695					{
696						text: "OK"
697					}
698				]
699			});
700		}
701		
702
703		
704		let reserveData = {
705			people: 0,
706			isIndividual: true,
707			date: formatDate(startOfDay(new Date())),
708			duration: "",
709			slotTime: "",
710			guest: {},
711			signedSlots: "",
712			signedSlots2: "",
713		};
714		let dateTimePicker;
715
716		class Steps {
717			constructor() {
718				this.steps = [...document.querySelectorAll(".step")];
719				this.indicators = [...document.querySelectorAll(".step-indicator")];
720				this.current = 0;
721
722				document.querySelectorAll(".btn-next").forEach(btn => {
723					btn.addEventListener("click", () => this.next());
724				});
725
726				document.querySelectorAll(".btn-back").forEach(btn => {
727					btn.addEventListener("click", () => this.back());
728				});
729
730				this.update(true);
731			}
732
733			get activeStep() {
734				return this.steps[this.current];
735			}
736
737			setNextEnabled() {
738				this.activeStep.querySelector(".btn-next").disabled = false;
739			}
740
741			setNextDisabled() {
742				this.activeStep.querySelector(".btn-next").disabled = true;
743			}
744
745			async next() {
746				if (this.current >= this.steps.length - 1) return;
747
748				const btn = document.getElementById("continueBtn");
749				btn.disabled = true;
750
751				try {
752					this.current++;
753
754					if (this.current == 1) {
755						const res = await fetchSlots(
756							reserveData.date,
757							"_",
758							reserveData.people,
759						);
760
761						const { durDefault, durations, slots, signedSlots, signedSlots2 } = res;
762						dateTimePicker.setDurations(durations, durDefault);
763						dateTimePicker.setSlots(slots);
764						reserveData.duration = durDefault;
765						reserveData.signedSlots = signedSlots;
766						reserveData.signedSlots2 = signedSlots2;
767					}
768
769					this.update(true);
770
771					if (this.current == 2) {
772						const allSet =
773							reserveData.guest.firstName &&
774							reserveData.guest.lastName &&
775							reserveData.guest.email &&
776							reserveData.guest.phone;
777
778						if (allSet) {
779							this.setNextEnabled();
780						}
781					}
782				} finally {
783					btn.disabled = false;
784					window.scrollTo(0, 0);
785				}
786			}
787
788			back() {
789				if (this.current > 0) {
790					this.current--;
791					this.update(false);
792				}
793			}
794
795			update(isNext) {
796				this.steps.forEach((step, i) => {
797					step.classList.toggle("hidden", i !== this.current);
798				});
799
800				this.indicators.forEach((indicator, i) => {
801					const active = i === this.current;
802					const completed = i < this.current;
803
804					indicator.classList.toggle("is-active", active);
805					indicator.classList.toggle("text-gray-900", active || completed);
806					indicator.classList.toggle("text-gray-400", !active && !completed);
807
808					const icon = indicator.querySelector(".step-icon");
809
810					
811					
812					icon.classList.toggle("border", active);
813					icon.classList.toggle("border-red-700", active);
814
815					
816					icon.classList.toggle("border-gray-300", !active);
817				});
818
819				const nextBtn = this.activeStep.querySelector(".btn-next");
820				if (nextBtn) {
821					nextBtn.disabled = isNext;
822				}
823			}
824		}
825
826		const steps = new Steps();
827		
828
829		
830		if (urlParams.get("guests")) {
831			reserveData.people = parseInt(urlParams.get("guests"), 10);
832		}
833		if (urlParams.get("date")) {
834			reserveData.date = urlParams.get("date");
835		}
836		if (urlParams.get("menuKind")) {
837			reserveData.menuKind = urlParams.get("menuKind");
838		}
839
840		if (reserveData.people) {
841			steps.next();
842		}
843
844		function detectReferrerSource(urlParams) {
845			const ref = urlParams.get("ref");
846			const utmSource = urlParams.get("utm_source");
847
848			if (urlParams.get("rwg_token")) {
849				return 'rwg';
850			}
851			if (ref) {
852				return ref;
853			}
854			if (utmSource) {
855				return utmSource;
856			}
857
858			const referrer = document.referrer;
859			if (!referrer) return null;
860
861			const sources = [
862				{ domain: 'apple.com/maps', name: 'applemaps' },
863				{ domain: 'facebook.com', name: 'facebook' },
864				{ domain: 'glovoapp.com', name: 'glovo' },
865				{ domain: 'goo.gl/maps', name: 'gmaps' },
866				{ domain: 'google.com', name: 'google' },
867				{ domain: 'google.com/maps', name: 'gmaps' },
868				{ domain: 'instagram.com', name: 'instagram' },
869				{ domain: 'messenger.com', name: 'messenger' },
870				{ domain: 'opentable.com', name: 'opentable' },
871				{ domain: 't.me', name: 'telegram' },
872				{ domain: 'telegram.org', name: 'telegram' },
873				{ domain: 'thefork.com', name: 'thefork' },
874				{ domain: 'tiktok.com', name: 'tiktok' },
875				{ domain: 'tripadvisor.com', name: 'tripadvisor' },
876				{ domain: 'twitter.com', name: 'twitter' },
877				{ domain: 'ubereats.com', name: 'ubereats' },
878				{ domain: 'waze.com', name: 'waze' },
879				{ domain: 'whatsapp.com', name: 'whatsapp' },
880				{ domain: 'x.com', name: 'twitter' },
881				{ domain: 'yahoo.com', name: 'yahoo' },
882				{ domain: 'yelp.com', name: 'yelp' },
883			];
884
885			for (const { domain, name } of sources) {
886				if (referrer.includes(domain)) {
887					return name;
888				}
889			}
890			return null;
891		}
892
893		const ref = detectReferrerSource(urlParams);
894
895		const rwgStartsAt = urlParams.get("starts_at");
896		const rwgDuration = urlParams.get("duration");
897		const rwgPartySize = urlParams.get("party_size");
898
899		if (rwgStartsAt && rwgDuration && rwgPartySize) {
900			
901			reserveData.people = parseInt(rwgPartySize, 10);
902			reserveData.date = formatDate(unixSecondsToDate(rwgStartsAt));
903			reserveData.dur = parseInt(rwgDuration, 10);
904
905			steps.next();
906		}
907		
908
909		
910		let isIndividual = true;
911		const noticeContainer = document.getElementById("noticeContainer");
912		const noticeIndividual = document.getElementById("noticeIndividual");
913		const noticeGroup = document.getElementById("noticeGroup");
914		const noticeMore = document.getElementById("noticeMore");
915
916		function activeNotice() {
917			return isIndividual ? noticeIndividual : noticeGroup;
918		}
919
920		function resetNotice() {
921			for (const notice of [noticeIndividual, noticeGroup]) {
922				notice.style.maxHeight = "";
923				notice.style.overflow = "";
924			}
925
926			const notice = activeNotice();
927			const maxHeight = parseFloat(getComputedStyle(notice).lineHeight) * 5;
928			const needsMore = notice.scrollHeight > maxHeight + 1;
929
930			notice.style.maxHeight = needsMore ? `${maxHeight}px` : "";
931			notice.style.overflow = needsMore ? "hidden" : "";
932
933			noticeMore.classList.toggle("hidden", !needsMore);
934			noticeMore.classList.toggle("block", needsMore);
935		}
936
937		noticeMore.addEventListener("click", () => {
938			const notice = activeNotice();
939			notice.style.maxHeight = "";
940			notice.style.overflow = "";
941
942			noticeMore.classList.add("hidden");
943			noticeMore.classList.remove("block");
944		});
945
946		const noticeObserver = new MutationObserver(resetNotice);
947
948		for (const notice of [noticeIndividual, noticeGroup]) {
949			noticeObserver.observe(notice, {
950				attributes: true,
951				attributeFilter: ["class"],
952			});
953		}
954
955		requestAnimationFrame(resetNotice);
956
957		const peopleGrid = document.getElementById("peopleGrid");
958		const peopleMoreLess = document.getElementById("peopleMore");
959		const peopleCustom = document.getElementById("peopleCustom");
960
961		const peopleButtons = [
962			...peopleGrid.querySelectorAll(".people-option")
963		];
964
965		function peopleUnselect() {
966			peopleButtons.forEach(b => {
967				b.classList.remove("gdrst-nav", "gdrst-nav-text", "font-black");
968				b.classList.add("gdrst-body-text");
969			});
970		}
971
972		function peopleChanged(isIndividual, people) {
973			if (!isIndividual && people > PAGEDATA.groupMax) {
974				steps.setNextDisabled();
975				showError("The group is too large. This restaurant accepts reservations for up to {max} guests.".replace("{max}", PAGEDATA.groupMax));
976				return;
977			}
978
979			reserveData.isIndividual = isIndividual;
980			reserveData.people = people;
981
982			if (people) {
983				steps.setNextEnabled();
984			} else {
985				steps.setNextDisabled();
986
987				peopleUnselect();
988			}
989		}
990
991		peopleButtons.forEach(button => {
992			button.addEventListener("click", () => {
993				peopleUnselect();
994
995				button.classList.add("gdrst-nav", "gdrst-nav-text", "font-black");
996				button.classList.remove("gdrst-body-text", "font-medium");
997
998				peopleChanged(isIndividual, Number(button.dataset.people));
999			});
1000		});
1001
1002		if (peopleMoreLess) {
1003			peopleMoreLess.addEventListener("click", () => {
1004				isIndividual = !isIndividual;
1005
1006				noticeIndividual.classList.toggle("hidden");
1007				noticeGroup.classList.toggle("hidden");
1008				peopleGrid.classList.toggle("hidden");
1009				peopleCustom.classList.toggle("hidden");
1010				peopleChanged(isIndividual, null);
1011
1012				let notice;
1013				if (isIndividual) {
1014					notice = noticeIndividual.innerText;
1015					peopleMoreLess.textContent = "More guests?";
1016					peopleCustom.value = '';
1017				} else {
1018					notice = noticeGroup.innerText;
1019					peopleMoreLess.textContent = "Less guests?";
1020					peopleCustom.focus();
1021				}
1022
1023				if (!notice) {
1024					noticeContainer.classList.add("hidden");
1025				} else {
1026					noticeContainer.classList.remove("hidden");
1027				}
1028			});
1029		}
1030
1031		if (peopleCustom) {
1032			peopleCustom.addEventListener("input", () => {
1033				const value = peopleCustom.value ? Number(peopleCustom.value) : null;
1034				peopleChanged(isIndividual, value);
1035			});
1036		}
1037		
1038
1039		
1040		const autoSlotInfo = document.getElementById("autoSlotInfo");
1041		const priceSlotInfo = document.getElementById("priceSlotInfo");
1042
1043		async function fetchSlots(date, dur, people) {
1044			if (!date || !dur || !people) {
1045				return;
1046			}
1047
1048			try {
1049				const params = new URLSearchParams({ date, dur, people, });
1050
1051				const resp = await fetch(
1052					`${hostname}/api/v1/resto/405036819902/prereserve?${params}`
1053				);
1054
1055				if (!resp.ok) {
1056					throw new Error(`HTTP ${resp.status}`);
1057				}
1058
1059				return await resp.json();
1060			} catch (err) {
1061				console.error("fetchSlots:", err);
1062			}
1063		}
1064		
1065
1066		
1067		const PHONE_COUNTRIES = [
1068			{ iso: "AF", code: "+93", name: "Afghanistan", flag: "🇦🇫" },
1069			{ iso: "AL", code: "+355", name: "Albania", flag: "🇦🇱" },
1070			{ iso: "DZ", code: "+213", name: "Algeria", flag: "🇩🇿" },
1071			{ iso: "AS", code: "+1684", name: "American Samoa", flag: "🇦🇸" },
1072			{ iso: "AD", code: "+376", name: "Andorra", flag: "🇦🇩" },
1073			{ iso: "AO", code: "+244", name: "Angola", flag: "🇦🇴" },
1074			{ iso: "AI", code: "+1264", name: "Anguilla", flag: "🇦🇮" },
1075			{ iso: "AQ", code: "+672", name: "Antarctica", flag: "🇦🇶" },
1076			{ iso: "AG", code: "+1268", name: "Antigua and Barbuda", flag: "🇦🇬" },
1077			{ iso: "AR", code: "+54", name: "Argentina", flag: "🇦🇷" },
1078			{ iso: "AM", code: "+374", name: "Armenia", flag: "🇦🇲" },
1079			{ iso: "AW", code: "+297", name: "Aruba", flag: "🇦🇼" },
1080			{ iso: "AU", code: "+61", name: "Australia", flag: "🇦🇺" },
1081			{ iso: "AT", code: "+43", name: "Austria", flag: "🇦🇹" },
1082			{ iso: "AZ", code: "+994", name: "Azerbaijan", flag: "🇦🇿" },
1083			{ iso: "BS", code: "+1242", name: "Bahamas", flag: "🇧🇸" },
1084			{ iso: "BH", code: "+973", name: "Bahrain", flag: "🇧🇭" },
1085			{ iso: "BD", code: "+880", name: "Bangladesh", flag: "🇧🇩" },
1086			{ iso: "BB", code: "+1246", name: "Barbados", flag: "🇧🇧" },
1087			{ iso: "BY", code: "+375", name: "Belarus", flag: "🇧🇾" },
1088			{ iso: "BE", code: "+32", name: "Belgium", flag: "🇧🇪" },
1089			{ iso: "BZ", code: "+501", name: "Belize", flag: "🇧🇿" },
1090			{ iso: "BJ", code: "+229", name: "Benin", flag: "🇧🇯" },
1091			{ iso: "BM", code: "+1441", name: "Bermuda", flag: "🇧🇲" },
1092			{ iso: "BT", code: "+975", name: "Bhutan", flag: "🇧🇹" },
1093			{ iso: "BO", code: "+591", name: "Bolivia", flag: "🇧🇴" },
1094			{ iso: "BA", code: "+387", name: "Bosnia and Herzegovina", flag: "🇧🇦" },
1095			{ iso: "BW", code: "+267", name: "Botswana", flag: "🇧🇼" },
1096			{ iso: "BR", code: "+55", name: "Brazil", flag: "🇧🇷" },
1097			{ iso: "IO", code: "+246", name: "British Indian Ocean Territory", flag: "🇮🇴" },
1098			{ iso: "VG", code: "+1284", name: "British Virgin Islands", flag: "🇻🇬" },
1099			{ iso: "BN", code: "+673", name: "Brunei", flag: "🇧🇳" },
1100			{ iso: "BG", code: "+359", name: "Bulgaria", flag: "🇧🇬" },
1101			{ iso: "BF", code: "+226", name: "Burkina Faso", flag: "🇧🇫" },
1102			{ iso: "BI", code: "+257", name: "Burundi", flag: "🇧🇮" },
1103			{ iso: "KH", code: "+855", name: "Cambodia", flag: "🇰🇭" },
1104			{ iso: "CM", code: "+237", name: "Cameroon", flag: "🇨🇲" },
1105			{ iso: "CA", code: "+1", name: "Canada", flag: "🇨🇦" },
1106			{ iso: "CV", code: "+238", name: "Cape Verde", flag: "🇨🇻" },
1107			{ iso: "KY", code: "+1345", name: "Cayman Islands", flag: "🇰🇾" },
1108			{ iso: "CF", code: "+236", name: "Central African Republic", flag: "🇨🇫" },
1109			{ iso: "TD", code: "+235", name: "Chad", flag: "🇹🇩" },
1110			{ iso: "CL", code: "+56", name: "Chile", flag: "🇨🇱" },
1111			{ iso: "CN", code: "+86", name: "China", flag: "🇨🇳" },
1112			{ iso: "CX", code: "+61", name: "Christmas Island", flag: "🇨🇽" },
1113			{ iso: "CC", code: "+61", name: "Cocos Islands", flag: "🇨🇨" },
1114			{ iso: "CO", code: "+57", name: "Colombia", flag: "🇨🇴" },
1115			{ iso: "KM", code: "+269", name: "Comoros", flag: "🇰🇲" },
1116			{ iso: "CK", code: "+682", name: "Cook Islands", flag: "🇨🇰" },
1117			{ iso: "CR", code: "+506", name: "Costa Rica", flag: "🇨🇷" },
1118			{ iso: "HR", code: "+385", name: "Croatia", flag: "🇭🇷" },
1119			{ iso: "CU", code: "+53", name: "Cuba", flag: "🇨🇺" },
1120			{ iso: "CW", code: "+599", name: "Curacao", flag: "🇨🇼" },
1121			{ iso: "CY", code: "+357", name: "Cyprus", flag: "🇨🇾" },
1122			{ iso: "CZ", code: "+420", name: "Czech Republic", flag: "🇨🇿" },
1123			{ iso: "CD", code: "+243", name: "DR Congo", flag: "🇨🇩" },
1124			{ iso: "DK", code: "+45", name: "Denmark", flag: "🇩🇰" },
1125			{ iso: "DJ", code: "+253", name: "Djibouti", flag: "🇩🇯" },
1126			{ iso: "DM", code: "+1767", name: "Dominica", flag: "🇩🇲" },
1127			{ iso: "DO", code: "+1849", name: "Dominican Republic", flag: "🇩🇴" },
1128			{ iso: "TL", code: "+670", name: "East Timor", flag: "🇹🇱" },
1129			{ iso: "EC", code: "+593", name: "Ecuador", flag: "🇪🇨" },
1130			{ iso: "EG", code: "+20", name: "Egypt", flag: "🇪🇬" },
1131			{ iso: "SV", code: "+503", name: "El Salvador", flag: "🇸🇻" },
1132			{ iso: "GQ", code: "+240", name: "Equatorial Guinea", flag: "🇬🇶" },
1133			{ iso: "ER", code: "+291", name: "Eritrea", flag: "🇪🇷" },
1134			{ iso: "EE", code: "+372", name: "Estonia", flag: "🇪🇪" },
1135			{ iso: "SZ", code: "+268", name: "Eswatini", flag: "🇸🇿" },
1136			{ iso: "ET", code: "+251", name: "Ethiopia", flag: "🇪🇹" },
1137			{ iso: "FK", code: "+500", name: "Falkland Islands", flag: "🇫🇰" },
1138			{ iso: "FO", code: "+298", name: "Faroe Islands", flag: "🇫🇴" },
1139			{ iso: "FJ", code: "+679", name: "Fiji", flag: "🇫🇯" },
1140			{ iso: "FI", code: "+358", name: "Finland", flag: "🇫🇮" },
1141			{ iso: "FR", code: "+33", name: "France", flag: "🇫🇷" },
1142			{ iso: "GF", code: "+594", name: "French Guiana", flag: "🇬🇫" },
1143			{ iso: "PF", code: "+689", name: "French Polynesia", flag: "🇵🇫" },
1144			{ iso: "GA", code: "+241", name: "Gabon", flag: "🇬🇦" },
1145			{ iso: "GM", code: "+220", name: "Gambia", flag: "🇬🇲" },
1146			{ iso: "GE", code: "+995", name: "Georgia", flag: "🇬🇪" },
1147			{ iso: "DE", code: "+49", name: "Germany", flag: "🇩🇪" },
1148			{ iso: "GH", code: "+233", name: "Ghana", flag: "🇬🇭" },
1149			{ iso: "GI", code: "+350", name: "Gibraltar", flag: "🇬🇮" },
1150			{ iso: "GR", code: "+30", name: "Greece", flag: "🇬🇷" },
1151			{ iso: "GL", code: "+299", name: "Greenland", flag: "🇬🇱" },
1152			{ iso: "GD", code: "+1473", name: "Grenada", flag: "🇬🇩" },
1153			{ iso: "GP", code: "+590", name: "Guadeloupe", flag: "🇬🇵" },
1154			{ iso: "GU", code: "+1671", name: "Guam", flag: "🇬🇺" },
1155			{ iso: "GT", code: "+502", name: "Guatemala", flag: "🇬🇹" },
1156			{ iso: "GG", code: "+44", name: "Guernsey", flag: "🇬🇬" },
1157			{ iso: "GN", code: "+224", name: "Guinea", flag: "🇬🇳" },
1158			{ iso: "GW", code: "+245", name: "Guinea-Bissau", flag: "🇬🇼" },
1159			{ iso: "GY", code: "+592", name: "Guyana", flag: "🇬🇾" },
1160			{ iso: "HT", code: "+509", name: "Haiti", flag: "🇭🇹" },
1161			{ iso: "HN", code: "+504", name: "Honduras", flag: "🇭🇳" },
1162			{ iso: "HK", code: "+852", name: "Hong Kong", flag: "🇭🇰" },
1163			{ iso: "HU", code: "+36", name: "Hungary", flag: "🇭🇺" },
1164			{ iso: "IS", code: "+354", name: "Iceland", flag: "🇮🇸" },
1165			{ iso: "IN", code: "+91", name: "India", flag: "🇮🇳" },
1166			{ iso: "ID", code: "+62", name: "Indonesia", flag: "🇮🇩" },
1167			{ iso: "IR", code: "+98", name: "Iran", flag: "🇮🇷" },
1168			{ iso: "IQ", code: "+964", name: "Iraq", flag: "🇮🇶" },
1169			{ iso: "IE", code: "+353", name: "Ireland", flag: "🇮🇪" },
1170			{ iso: "IM", code: "+44", name: "Isle of Man", flag: "🇮🇲" },
1171			{ iso: "IL", code: "+972", name: "Israel", flag: "🇮🇱" },
1172			{ iso: "IT", code: "+39", name: "Italy", flag: "🇮🇹" },
1173			{ iso: "CI", code: "+225", name: "Ivory Coast", flag: "🇨🇮" },
1174			{ iso: "JM", code: "+1876", name: "Jamaica", flag: "🇯🇲" },
1175			{ iso: "JP", code: "+81", name: "Japan", flag: "🇯🇵" },
1176			{ iso: "JE", code: "+44", name: "Jersey", flag: "🇯🇪" },
1177			{ iso: "JO", code: "+962", name: "Jordan", flag: "🇯🇴" },
1178			{ iso: "KZ", code: "+7", name: "Kazakhstan", flag: "🇰🇿" },
1179			{ iso: "KE", code: "+254", name: "Kenya", flag: "🇰🇪" },
1180			{ iso: "KI", code: "+686", name: "Kiribati", flag: "🇰🇮" },
1181			{ iso: "XK", code: "+383", name: "Kosovo", flag: "🇽🇰" },
1182			{ iso: "KW", code: "+965", name: "Kuwait", flag: "🇰🇼" },
1183			{ iso: "KG", code: "+996", name: "Kyrgyzstan", flag: "🇰🇬" },
1184			{ iso: "LA", code: "+856", name: "Laos", flag: "🇱🇦" },
1185			{ iso: "LV", code: "+371", name: "Latvia", flag: "🇱🇻" },
1186			{ iso: "LB", code: "+961", name: "Lebanon", flag: "🇱🇧" },
1187			{ iso: "LS", code: "+266", name: "Lesotho", flag: "🇱🇸" },
1188			{ iso: "LR", code: "+231", name: "Liberia", flag: "🇱🇷" },
1189			{ iso: "LY", code: "+218", name: "Libya", flag: "🇱🇾" },
1190			{ iso: "LI", code: "+423", name: "Liechtenstein", flag: "🇱🇮" },
1191			{ iso: "LT", code: "+370", name: "Lithuania", flag: "🇱🇹" },
1192			{ iso: "LU", code: "+352", name: "Luxembourg", flag: "🇱🇺" },
1193			{ iso: "MO", code: "+853", name: "Macau", flag: "🇲🇴" },
1194			{ iso: "MG", code: "+261", name: "Madagascar", flag: "🇲🇬" },
1195			{ iso: "MW", code: "+265", name: "Malawi", flag: "🇲🇼" },
1196			{ iso: "MY", code: "+60", name: "Malaysia", flag: "🇲🇾" },
1197			{ iso: "MV", code: "+960", name: "Maldives", flag: "🇲🇻" },
1198			{ iso: "ML", code: "+223", name: "Mali", flag: "🇲🇱" },
1199			{ iso: "MT", code: "+356", name: "Malta", flag: "🇲🇹" },
1200			{ iso: "MH", code: "+692", name: "Marshall Islands", flag: "🇲🇭" },
1201			{ iso: "MQ", code: "+596", name: "Martinique", flag: "🇲🇶" },
1202			{ iso: "MR", code: "+222", name: "Mauritania", flag: "🇲🇷" },
1203			{ iso: "MU", code: "+230", name: "Mauritius", flag: "🇲🇺" },
1204			{ iso: "YT", code: "+262", name: "Mayotte", flag: "🇾🇹" },
1205			{ iso: "MX", code: "+52", name: "Mexico", flag: "🇲🇽" },
1206			{ iso: "FM", code: "+691", name: "Micronesia", flag: "🇫🇲" },
1207			{ iso: "MD", code: "+373", name: "Moldova", flag: "🇲🇩" },
1208			{ iso: "MC", code: "+377", name: "Monaco", flag: "🇲🇨" },
1209			{ iso: "MN", code: "+976", name: "Mongolia", flag: "🇲🇳" },
1210			{ iso: "ME", code: "+382", name: "Montenegro", flag: "🇲🇪" },
1211			{ iso: "MS", code: "+1664", name: "Montserrat", flag: "🇲🇸" },
1212			{ iso: "MA", code: "+212", name: "Morocco", flag: "🇲🇦" },
1213			{ iso: "MZ", code: "+258", name: "Mozambique", flag: "🇲🇿" },
1214			{ iso: "MM", code: "+95", name: "Myanmar", flag: "🇲🇲" },
1215			{ iso: "NA", code: "+264", name: "Namibia", flag: "🇳🇦" },
1216			{ iso: "NR", code: "+674", name: "Nauru", flag: "🇳🇷" },
1217			{ iso: "NP", code: "+977", name: "Nepal", flag: "🇳🇵" },
1218			{ iso: "NL", code: "+31", name: "Netherlands", flag: "🇳🇱" },
1219			{ iso: "NC", code: "+687", name: "New Caledonia", flag: "🇳🇨" },
1220			{ iso: "NZ", code: "+64", name: "New Zealand", flag: "🇳🇿" },
1221			{ iso: "NI", code: "+505", name: "Nicaragua", flag: "🇳🇮" },
1222			{ iso: "NE", code: "+227", name: "Niger", flag: "🇳🇪" },
1223			{ iso: "NG", code: "+234", name: "Nigeria", flag: "🇳🇬" },
1224			{ iso: "NU", code: "+683", name: "Niue", flag: "🇳🇺" },
1225			{ iso: "NF", code: "+672", name: "Norfolk Island", flag: "🇳🇫" },
1226			{ iso: "KP", code: "+850", name: "North Korea", flag: "🇰🇵" },
1227			{ iso: "MK", code: "+389", name: "North Macedonia", flag: "🇲🇰" },
1228			{ iso: "MP", code: "+1670", name: "Northern Mariana Islands", flag: "🇲🇵" },
1229			{ iso: "NO", code: "+47", name: "Norway", flag: "🇳🇴" },
1230			{ iso: "OM", code: "+968", name: "Oman", flag: "🇴🇲" },
1231			{ iso: "PK", code: "+92", name: "Pakistan", flag: "🇵🇰" },
1232			{ iso: "PW", code: "+680", name: "Palau", flag: "🇵🇼" },
1233			{ iso: "PS", code: "+970", name: "Palestine", flag: "🇵🇸" },
1234			{ iso: "PA", code: "+507", name: "Panama", flag: "🇵🇦" },
1235			{ iso: "PG", code: "+675", name: "Papua New Guinea", flag: "🇵🇬" },
1236			{ iso: "PY", code: "+595", name: "Paraguay", flag: "🇵🇾" },
1237			{ iso: "PE", code: "+51", name: "Peru", flag: "🇵🇪" },
1238			{ iso: "PH", code: "+63", name: "Philippines", flag: "🇵🇭" },
1239			{ iso: "PN", code: "+64", name: "Pitcairn Islands", flag: "🇵🇳" },
1240			{ iso: "PL", code: "+48", name: "Poland", flag: "🇵🇱" },
1241			{ iso: "PT", code: "+351", name: "Portugal", flag: "🇵🇹" },
1242			{ iso: "PR", code: "+1787", name: "Puerto Rico", flag: "🇵🇷" },
1243			{ iso: "QA", code: "+974", name: "Qatar", flag: "🇶🇦" },
1244			{ iso: "CG", code: "+242", name: "Republic of the Congo", flag: "🇨🇬" },
1245			{ iso: "RE", code: "+262", name: "Reunion", flag: "🇷🇪" },
1246			{ iso: "RO", code: "+40", name: "Romania", flag: "🇷🇴" },
1247			{ iso: "RU", code: "+7", name: "Russia", flag: "🇷🇺" },
1248			{ iso: "RW", code: "+250", name: "Rwanda", flag: "🇷🇼" },
1249			{ iso: "SH", code: "+290", name: "Saint Helena", flag: "🇸🇭" },
1250			{ iso: "KN", code: "+1869", name: "Saint Kitts and Nevis", flag: "🇰🇳" },
1251			{ iso: "LC", code: "+1758", name: "Saint Lucia", flag: "🇱🇨" },
1252			{ iso: "PM", code: "+508", name: "Saint Pierre and Miquelon", flag: "🇵🇲" },
1253			{ iso: "VC", code: "+1784", name: "Saint Vincent and the Grenadines", flag: "🇻🇨" },
1254			{ iso: "WS", code: "+685", name: "Samoa", flag: "🇼🇸" },
1255			{ iso: "SM", code: "+378", name: "San Marino", flag: "🇸🇲" },
1256			{ iso: "ST", code: "+239", name: "Sao Tome and Principe", flag: "🇸🇹" },
1257			{ iso: "SA", code: "+966", name: "Saudi Arabia", flag: "🇸🇦" },
1258			{ iso: "SN", code: "+221", name: "Senegal", flag: "🇸🇳" },
1259			{ iso: "RS", code: "+381", name: "Serbia", flag: "🇷🇸" },
1260			{ iso: "SC", code: "+248", name: "Seychelles", flag: "🇸🇨" },
1261			{ iso: "SL", code: "+232", name: "Sierra Leone", flag: "🇸🇱" },
1262			{ iso: "SG", code: "+65", name: "Singapore", flag: "🇸🇬" },
1263			{ iso: "SX", code: "+1721", name: "Sint Maarten", flag: "🇸🇽" },
1264			{ iso: "SK", code: "+421", name: "Slovakia", flag: "🇸🇰" },
1265			{ iso: "SI", code: "+386", name: "Slovenia", flag: "🇸🇮" },
1266			{ iso: "SB", code: "+677", name: "Solomon Islands", flag: "🇸🇧" },
1267			{ iso: "SO", code: "+252", name: "Somalia", flag: "🇸🇴" },
1268			{ iso: "ZA", code: "+27", name: "South Africa", flag: "🇿🇦" },
1269			{ iso: "KR", code: "+82", name: "South Korea", flag: "🇰🇷" },
1270			{ iso: "SS", code: "+211", name: "South Sudan", flag: "🇸🇸" },
1271			{ iso: "ES", code: "+34", name: "Spain", flag: "🇪🇸" },
1272			{ iso: "LK", code: "+94", name: "Sri Lanka", flag: "🇱🇰" },
1273			{ iso: "SD", code: "+249", name: "Sudan", flag: "🇸🇩" },
1274			{ iso: "SR", code: "+597", name: "Suriname", flag: "🇸🇷" },
1275			{ iso: "SJ", code: "+47", name: "Svalbard and Jan Mayen", flag: "🇸🇯" },
1276			{ iso: "SE", code: "+46", name: "Sweden", flag: "🇸🇪" },
1277			{ iso: "CH", code: "+41", name: "Switzerland", flag: "🇨🇭" },
1278			{ iso: "SY", code: "+963", name: "Syria", flag: "🇸🇾" },
1279			{ iso: "TW", code: "+886", name: "Taiwan", flag: "🇹🇼" },
1280			{ iso: "TJ", code: "+992", name: "Tajikistan", flag: "🇹🇯" },
1281			{ iso: "TZ", code: "+255", name: "Tanzania", flag: "🇹🇿" },
1282			{ iso: "TH", code: "+66", name: "Thailand", flag: "🇹🇭" },
1283			{ iso: "TG", code: "+228", name: "Togo", flag: "🇹🇬" },
1284			{ iso: "TK", code: "+690", name: "Tokelau", flag: "🇹🇰" },
1285			{ iso: "TO", code: "+676", name: "Tonga", flag: "🇹🇴" },
1286			{ iso: "TT", code: "+1868", name: "Trinidad and Tobago", flag: "🇹🇹" },
1287			{ iso: "TN", code: "+216", name: "Tunisia", flag: "🇹🇳" },
1288			{ iso: "TR", code: "+90", name: "Turkey", flag: "🇹🇷" },
1289			{ iso: "TM", code: "+993", name: "Turkmenistan", flag: "🇹🇲" },
1290			{ iso: "TC", code: "+1649", name: "Turks and Caicos Islands", flag: "🇹🇨" },
1291			{ iso: "TV", code: "+688", name: "Tuvalu", flag: "🇹🇻" },
1292			{ iso: "VI", code: "+1340", name: "US Virgin Islands", flag: "🇻🇮" },
1293			{ iso: "UG", code: "+256", name: "Uganda", flag: "🇺🇬" },
1294			{ iso: "UA", code: "+380", name: "Ukraine", flag: "🇺🇦" },
1295			{ iso: "AE", code: "+971", name: "United Arab Emirates", flag: "🇦🇪" },
1296			{ iso: "GB", code: "+44", name: "United Kingdom", flag: "🇬🇧" },
1297			{ iso: "US", code: "+1", name: "United States", flag: "🇺🇸" },
1298			{ iso: "UY", code: "+598", name: "Uruguay", flag: "🇺🇾" },
1299			{ iso: "UZ", code: "+998", name: "Uzbekistan", flag: "🇺🇿" },
1300			{ iso: "VU", code: "+678", name: "Vanuatu", flag: "🇻🇺" },
1301			{ iso: "VA", code: "+379", name: "Vatican City", flag: "🇻🇦" },
1302			{ iso: "VE", code: "+58", name: "Venezuela", flag: "🇻🇪" },
1303			{ iso: "VN", code: "+84", name: "Vietnam", flag: "🇻🇳" },
1304			{ iso: "WF", code: "+681", name: "Wallis and Futuna", flag: "🇼🇫" },
1305			{ iso: "EH", code: "+212", name: "Western Sahara", flag: "🇪🇭" },
1306			{ iso: "YE", code: "+967", name: "Yemen", flag: "🇾🇪" },
1307			{ iso: "ZM", code: "+260", name: "Zambia", flag: "🇿🇲" },
1308			{ iso: "ZW", code: "+263", name: "Zimbabwe", flag: "🇿🇼" },
1309		];
1310
1311		class PhonePrefixDropdown {
1312			constructor({
1313				el,
1314				countries = PHONE_COUNTRIES,
1315				defaultCode = "+48",
1316				onChange = null,
1317			} = {}) {
1318				if (!el) {
1319					throw new Error("PhonePrefixDropdown: el is required");
1320				}
1321
1322				this.el = el;
1323				this.countries = countries;
1324				this.onChange = onChange;
1325
1326
1327				const defaultCountry = countries.find(c => c.code === defaultCode) ||
1328					countries[0];
1329
1330				this.selected = defaultCountry;
1331				this.filtered = [...countries];
1332
1333				this.render();
1334				this.cacheElements();
1335				this.attachEvents();
1336				this.renderList();
1337
1338				this.selectedEl.textContent = this.format(defaultCountry);
1339			}
1340
1341			format(country) {
1342				return `${country.code} ${country.flag} ${country.name}`;
1343			}
1344
1345			render() {
1346				this.el.classList.add("relative");
1347
1348				this.el.innerHTML = `
1349	<button
1350		type="button" class="prefix-trigger gdrst-body-text rounded-lg p-[12px_18px] text-base w-full flex justify-between items-center border text-left">
1351		<span class="selected-prefix truncate"></span>
1352		<span class="text-xs opacity-60">▼</span>
1353	</button>
1354
1355	<div class="dropdown-list hidden absolute z-10 mt-1 w-full bg-white border rounded-lg shadow max-h-80 overflow-hidden flex flex-col">
1356		<div class="p-2 border-b">
1357			<input
1358				type="text"
1359				placeholder="Search..."
1360				class="search-input w-full px-3 py-2 border rounded-lg
1361							text-sm outline-none focus:ring-2 focus:ring-gray-200"
1362			/>
1363		</div>
1364
1365		<div class="list-container overflow-y-auto max-h-64"></div>
1366	</div>
1367	`;
1368			}
1369
1370			cacheElements() {
1371				this.trigger = this.el.querySelector(".prefix-trigger");
1372				this.dropdown = this.el.querySelector(".dropdown-list");
1373				this.selectedEl = this.el.querySelector(".selected-prefix");
1374				this.searchInput = this.el.querySelector(".search-input");
1375				this.listContainer = this.el.querySelector(".list-container");
1376			}
1377
1378			attachEvents() {
1379				this.trigger.addEventListener("click", () => {
1380					this.toggle();
1381				});
1382
1383				this.searchInput.addEventListener("input", () => {
1384					this.filter(this.searchInput.value);
1385				});
1386
1387				this.listContainer.addEventListener("click", e => {
1388					const item = e.target.closest("[data-iso]");
1389					if (!item) return;
1390
1391					this.select(item.dataset.iso);
1392				});
1393
1394				document.addEventListener("click", this.handleOutsideClick);
1395			}
1396
1397			handleOutsideClick = e => {
1398				if (!this.el.contains(e.target)) {
1399					this.close();
1400				}
1401			};
1402
1403			filter(query) {
1404				query = query.trim().toLowerCase();
1405
1406				this.filtered = query
1407					? this.countries.filter(country =>
1408						country.name.toLowerCase().includes(query) ||
1409						country.code.includes(query) ||
1410						country.iso.toLowerCase().includes(query)
1411					)
1412					: [...this.countries];
1413
1414				this.renderList();
1415			}
1416
1417			renderList() {
1418				this.listContainer.innerHTML = this.filtered
1419					.map(country => `
1420				<button
1421					type="button"
1422					class="block w-full px-3 py-2 text-left
1423						hover:bg-gray-100 cursor-pointer"
1424					data-iso="${country.iso}"
1425				>
1426					${this.format(country)}
1427				</button>
1428			`)
1429					.join("");
1430			}
1431
1432			select(iso) {
1433				const country = this.countries.find(c => c.iso === iso);
1434				if (!country) return;
1435
1436				this.selected = country;
1437				this.selectedEl.textContent = this.format(country);
1438
1439				this.searchInput.value = "";
1440				this.filtered = [...this.countries];
1441
1442				this.renderList();
1443				this.close();
1444
1445				this.onChange?.(country);
1446			}
1447
1448			toggle() {
1449				this.dropdown.classList.contains("hidden")
1450					? this.open()
1451					: this.close();
1452			}
1453
1454			open() {
1455				this.dropdown.classList.remove("hidden");
1456				this.searchInput.focus();
1457			}
1458
1459			close() {
1460				this.dropdown.classList.add("hidden");
1461			}
1462
1463			destroy() {
1464				document.removeEventListener("click", this.handleOutsideClick);
1465			}
1466		}
1467
1468		const phonePrefixDropdown = new PhonePrefixDropdown({
1469			el: document.querySelector("#phonePrefix"),
1470			defaultCountry: "\u002b48",
1471			onChange: country => {
1472				reserveData.guest.phoneCode = country.code;
1473			}
1474		});
1475
1476		function onGuestFieldsComplete(callback) {
1477			const firstNameField = document.getElementById("first-name");
1478			const lastNameField = document.getElementById("last-name");
1479			const emailField = document.getElementById("email");
1480			const phoneField = document.getElementById("phone");
1481
1482			const fields = [
1483				firstNameField,
1484				lastNameField,
1485				emailField,
1486				phoneField,
1487			];
1488
1489			const isValidEmail = email => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
1490
1491			const isValidPhone = phone => {
1492				const normalized = phone.replace(/[\s().-]/g, "");
1493				return /^\+?[0-9]{7,15}$/.test(normalized);
1494			};
1495
1496			const check = () => {
1497				if (steps.current !== 2) {
1498					return;
1499				}
1500
1501				const firstName = firstNameField.value.trim();
1502				const lastName = lastNameField.value.trim();
1503				const email = emailField.value.trim();
1504				const phone = phoneField.value.trim();
1505
1506				emailField.setCustomValidity(
1507					email && !isValidEmail(email) ? "Invalid email address" : ""
1508				);
1509
1510				phoneField.setCustomValidity(
1511					phone && !isValidPhone(phone) ? "Invalid phone number" : ""
1512				);
1513
1514				if (
1515					!firstName ||
1516					!lastName ||
1517					!email ||
1518					!phone ||
1519					!isValidEmail(email) ||
1520					!isValidPhone(phone)
1521				) {
1522					return false;
1523				}
1524
1525				callback(firstName, lastName, email, phone);
1526				return true;
1527			};
1528
1529			const validateFields = () => {
1530				if (steps.current === 2 && !check()) {
1531					steps.setNextDisabled();
1532				}
1533			};
1534
1535			fields.forEach(field => {
1536				["input", "change", "blur"].forEach(event => {
1537					field.addEventListener(event, validateFields);
1538				});
1539			});
1540
1541			
1542			const interval = setInterval(() => {
1543				if (steps.current !== 2) {
1544					return;
1545				}
1546				if (check()) {
1547					clearInterval(interval);
1548				} else {
1549					steps.setNextDisabled();
1550				}
1551			}, 300);
1552
1553			check();
1554		}
1555
1556		onGuestFieldsComplete((firstName, lastName, email, phone) => {
1557			reserveData.guest = {
1558				firstName,
1559				lastName,
1560				email,
1561				phone,
1562			};
1563
1564			steps.setNextEnabled();
1565		});
1566		
1567
1568		
1569		const taxInvoice = document.getElementById('taxInvoice');
1570		const vatDetails = document.getElementById('vatDetails');
1571
1572		
1573		if (taxInvoice) {
1574			taxInvoice.addEventListener('change', () => {
1575				vatDetails.classList.toggle('hidden', !taxInvoice.checked);
1576
1577				if (!taxInvoice.checked) {
1578					document.getElementById('taxNumber').value = '';
1579					document.getElementById('companyName').value = '';
1580				}
1581			});
1582		}
1583
1584		const termsAgreement = document.getElementById('termsAgreement');
1585		const selectAllAgreements = document.getElementById('selectAllAgreements');
1586
1587		const agreements = [
1588			document.getElementById('termsAgreement'),
1589			document.getElementById('marketingRestoAgreement'),
1590			document.getElementById('marketingGdrstAgreement')
1591		];
1592
1593		
1594		selectAllAgreements.addEventListener('change', () => {
1595			agreements.forEach(checkbox => {
1596				checkbox.checked = selectAllAgreements.checked;
1597			});
1598		});
1599
1600		
1601		agreements.forEach(checkbox => {
1602			checkbox.addEventListener('change', () => {
1603				selectAllAgreements.checked = agreements.every(
1604					checkbox => checkbox.checked
1605				);
1606
1607				selectAllAgreements.indeterminate =
1608					!selectAllAgreements.checked &&
1609					agreements.some(checkbox => checkbox.checked);
1610			});
1611		});
1612
1613		
1614		function getDetailsData() {
1615			const data = {
1616				visitKind: document.getElementById('visitKind').value,
1617				specialNote: document.getElementById('specialNote').value.trim(),
1618
1619				taxInvoice: {
1620					enabled: taxInvoice?.checked ?? false,
1621					vatId: document.getElementById('taxNumber')?.value?.trim() ?? '',
1622					companyName: document.getElementById('companyName')?.value?.trim() ?? '',
1623				},
1624
1625				marketing: {
1626				},
1627			};
1628
1629			return data;
1630		}
1631
1632		let isSubmitClicked = false;
1633
1634		async function submitReservation() {
1635			if (isSubmitClicked) return;
1636			isSubmitClicked = true;
1637
1638			if (!termsAgreement.checked) {
1639				isSubmitClicked = false;
1640				showError('Please accept the terms and conditions and privacy policy.');
1641				return
1642			}
1643
1644			const rawParams = Object.fromEntries(urlParams);
1645			const selectedSlot = document.querySelector('.time-slot.selected');
1646			const time = selectedSlot ? selectedSlot.firstElementChild.textContent.trim() : null;
1647			const price = selectedSlot.dataset.price;
1648			const menuKind = this.menuKind ? this.menuKind.value : '';
1649			const details = getDetailsData();
1650			const marketingResto = document.getElementById('marketingRestoAgreement').checked;
1651			const marketingGdrst = document.getElementById('marketingGdrstAgreement').checked;
1652
1653			const payload = {
1654				date: reserveData.date,
1655				time: reserveData.slotTime,
1656				dur: reserveData.duration,
1657				price: reserveData.slotPrice,
1658				people: reserveData.people,
1659				isGroup: !reserveData.isIndividual,
1660
1661				kind: details.visitKind,
1662				menuKind: menuKind,
1663				specialNote: details.specialNote,
1664
1665				wantInvoice: details.taxInvoice.enabled,
1666				taxNumber: details.taxInvoice.vatId,
1667				companyName: details.taxInvoice.companyName,
1668
1669				firstname: reserveData.guest.firstName,
1670				lastname: reserveData.guest.lastName,
1671				email: reserveData.guest.email,
1672				phone: reserveData.guest.phone,
1673				phoneCode: reserveData.guest.phoneCode,
1674
1675				marketingResto: marketingResto,
1676				marketingGdrst: marketingGdrst,
1677
1678				urlParams: rawParams,
1679				signedSlots: reserveData.signedSlots,
1680				signedSlots2: reserveData.signedSlots2,
1681
1682				idempotencyKey: PAGEDATA.idempotencyKey,
1683			};
1684
1685			try {
1686				const { result, error } = await doSubmitReservation(payload);
1687				if (error) {
1688					showError(error.message);
1689					return;
1690				}
1691
1692				if (result.redirectLink) {
1693					window.location.href = result.redirectLink;
1694					return;
1695				}
1696
1697				showSubmitSuccess(result.message);
1698			} finally {
1699				isSubmitClicked = false;
1700
1701				const gtagConv = '';
1702				if (gtagConv) {
1703					gtag('event', 'conversion', { 'send_to': gtagConv });
1704				}
1705			}
1706		}
1707
1708		function showSubmitSuccess(msg) {
1709			window.MiniAlert.fire({
1710				title: "Success",
1711				text: msg,
1712
1713				buttons: [
1714					{
1715						id: "ok",
1716						text: "OK"
1717					}
1718				],
1719
1720				onAction: async ({ id, values }) => {
1721					window.location.href = "/";
1722				}
1723			});
1724		}
1725
1726		async function doSubmitReservation(payload) {
1727			try {
1728				const resp = await fetch(`${hostname}/api/v1/resto/405036819902/reserve`, {
1729					method: 'POST',
1730					headers: {
1731						'Content-Type': 'application/json; charset=UTF-8',
1732						'Language': currentLang || 'en',
1733						'Ref': ref || 'web',
1734					},
1735					body: JSON.stringify(payload),
1736				});
1737
1738				const result = await resp.json();
1739
1740				if (!resp.ok) {
1741					return { result: null, error: result };
1742				}
1743
1744				return { result, error: null };
1745			} catch (err) {
1746				return {
1747					result: null,
1748					error: {
1749						message: err instanceof Error ? err.message : String(err),
1750					},
1751				};
1752			}
1753		}
1754
1755		
1756		class DateTimePicker {
1757			constructor(options = {}) {
1758				this.root = document.getElementById(options.id);
1759
1760				if (!this.root) {
1761					throw new Error(`DateTimePicker: #${options.id} not found`);
1762				}
1763
1764				this.maxFutureDays = options.maxFutureDays ?? 30;
1765
1766				this.selectedDate = options.selectedDate
1767					? parseDate(options.selectedDate)
1768					: startOfDay(new Date());
1769
1770				this.selectedDuration = options.selectedDuration ?? "02:00";
1771
1772				this.selectedSlot = null;
1773
1774				
1775
1776				this.events = options.events ?? {};
1777
1778				this.availableDurs = [];
1779				this.availableSlots = [];
1780
1781				this.currentMonth = new Date(
1782					this.selectedDate.getFullYear(),
1783					this.selectedDate.getMonth(),
1784					1
1785				);
1786
1787				this.onDateChange = options.onDateChange ?? (() => { });
1788				this.onDurationChange = options.onDurationChange ?? (() => { });
1789				this.onSlotChange = options.onSlotChange ?? (() => { });
1790
1791				this.calendar = this.root.querySelector("#calendar");
1792				this.calendarTitle = this.root.querySelector("#calendar-title");
1793				this.calendarPrevButton = this.root.querySelector("#calendar-prev");
1794				this.calendarNextButton = this.root.querySelector("#calendar-next");
1795
1796				this.eventCard = this.root.querySelector("#eventCard");
1797				this.eventCardTitle = this.root.querySelector("#eventCardTitle");
1798				this.eventCardDescription = this.root.querySelector("#eventCardDescription");
1799
1800				this.noSlotsNoticeContainer = this.root.querySelector("#noSlotsNoticeContainer");
1801				this.noSlotsNotice = this.root.querySelector("#noSlotsNotice");
1802
1803				this.menuKindContainer = this.root.querySelector("#menuKindContainer");
1804				this.durationContainer = this.root.querySelector("#durationContainer");
1805
1806				this.menuKind = this.root.querySelector("#menuKind");
1807				this.duration = this.root.querySelector("#duration");
1808				this.slots = this.root.querySelector("#slotsGrid");
1809
1810				this.bindEvents();
1811
1812				this.duration.value = this.selectedDuration;
1813
1814				this.render();
1815			}
1816
1817			bindEvents() {
1818				this.calendarPrevButton.addEventListener("click", () => this.prevMonth());
1819				this.calendarNextButton.addEventListener("click", () => this.nextMonth());
1820				this.duration.addEventListener("change", event => {
1821					this.selectedDuration = event.target.value;
1822					this.selectedSlot = null;
1823
1824					this.renderSlots();
1825
1826					this.onDurationChange(this.selectedDuration);
1827				});
1828			}
1829
1830			render() {
1831				this.renderCalendar();
1832				this.renderSlots();
1833			}
1834
1835			
1836
1837
1838			renderCalendar() {
1839				this.eventCard.classList.add("hidden");
1840
1841				const year = this.currentMonth.getFullYear();
1842				const month = this.currentMonth.getMonth();
1843
1844				this.calendarTitle.textContent =
1845					new Intl.DateTimeFormat(undefined, {
1846						month: "long",
1847						year: "numeric"
1848					}).format(this.currentMonth);
1849
1850				this.calendar.innerHTML = "";
1851
1852				const firstDay = new Date(year, month, 1).getDay();
1853				const mondayIndex = (firstDay + 6) % 7;
1854				const daysInMonth = new Date(year, month + 1, 0).getDate();
1855
1856				const today = startOfDay(new Date());
1857				const maxDate = addDays(today, this.maxFutureDays);
1858
1859				
1860
1861				for (let cell = 0; cell < 42; cell++) {
1862					const dayNumber = cell - mondayIndex + 1;
1863					const cellElement = document.createElement("div");
1864
1865					if (dayNumber > daysInMonth) {
1866						break;
1867					}
1868					if (dayNumber < 1) {
1869						cellElement.className = "w-full h-11";
1870						this.calendar.appendChild(cellElement);
1871						continue;
1872					}
1873
1874					const date = new Date(year, month, dayNumber);
1875					const dateString = formatDate(date);
1876					const isPast = date < today;
1877					const isTooFar = date > maxDate;
1878					const isDisabled = isPast || isTooFar;
1879					const isSelected = this.selectedDate &&
1880						formatDate(this.selectedDate) === dateString;
1881
1882					const isToday = formatDate(today) === dateString;
1883
1884					
1885
1886					const event = this.events[dateString];
1887
1888					const button = document.createElement("button");
1889					button.type = "button";
1890
1891					button.className = [
1892						"relative",
1893						"w-full",
1894						"h-11",
1895						"rounded-lg",
1896						"border",
1897						"transition",
1898						"font-medium"
1899					].join(" ");
1900
1901					
1902
1903					const day = document.createElement("span");
1904					day.textContent = dayNumber;
1905
1906					
1907
1908					day.className = "absolute inset-0 flex items-center justify-center gdrst-body-text font-medium";
1909
1910					button.appendChild(day);
1911
1912					
1913
1914					if (event) {
1915						const marker = document.createElement("span");
1916
1917						marker.className = [
1918							"absolute",
1919							"bottom-1",
1920							"left-1/2",
1921							"-translate-x-1/2",
1922							"w-1.5",
1923							"h-1.5",
1924							"rounded-full",
1925							"bg-red-500"
1926						].join(" ");
1927
1928						
1929
1930						marker.title = event.label ?? "Special event";
1931						button.appendChild(marker);
1932
1933						if (formatDate(this.selectedDate) === dateString) {
1934							this.eventCard.classList.remove("hidden");
1935							this.eventCard.href = event.link;
1936							this.eventCardTitle.textContent = event.label;
1937							this.eventCardDescription.textContent = event.descr;
1938						}
1939					}
1940
1941					
1942
1943					if (isDisabled) {
1944						button.classList.add(
1945							"text-gray-500",
1946							"border-gray-500",
1947							"cursor-not-allowed",
1948							"relative",
1949							"w-full",
1950							"h-11",
1951							"rounded-lg",
1952							"border",
1953							"border-gray-500",
1954							"font-medium",
1955							"text-gray-500",
1956							"opacity-50",
1957							"cursor-not-allowed",
1958							"transition",
1959						);
1960
1961						button.disabled = true;
1962					} else if (isSelected) {
1963						button.classList.add("gdrst-nav", "gdrst-nav-text", "font-black");
1964						button.classList.remove("gdrst-body-text", "font-medium");
1965						day.classList.add("font-black");
1966						day.classList.remove("gdrst-body-text", "font-medium");
1967
1968						
1969
1970						if (event) {
1971							const marker = button.querySelector("span:last-child");
1972							marker.classList.remove("bg-red-500");
1973							marker.classList.add("bg-white");
1974						}
1975					} else if (isToday) {
1976						button.classList.add(
1977							"text-red-600",
1978							"font-bold",
1979						);
1980					} else {
1981						button.classList.add(
1982							"border-gray-200",
1983							"hover:border-red-300"
1984						);
1985					}
1986
1987					button.addEventListener("click", () => this.selectDate(date));
1988					this.calendar.appendChild(button);
1989				}
1990
1991				this.updateMonthNavigation();
1992			}
1993
1994			selectDate(date) {
1995				steps.setNextDisabled();
1996				reserveData.slotTime = null;
1997
1998				this.selectedDate = startOfDay(date);
1999				this.selectedSlot = null;
2000
2001				this.renderCalendar();
2002				this.renderSlots();
2003
2004				this.onDateChange(formatDate(this.selectedDate));
2005			}
2006
2007			
2008
2009
2010			setEvents(events) {
2011				
2012
2013				this.events = events ?? {};
2014
2015				this.renderCalendar();
2016			}
2017
2018			setEvent(date, event = {}) {
2019				
2020
2021				this.events[date] = event;
2022
2023				this.renderCalendar();
2024			}
2025
2026			removeEvent(date) {
2027				delete this.events[date];
2028
2029				this.renderCalendar();
2030			}
2031
2032			getEvent(date) {
2033				return this.events[date] ?? null;
2034			}
2035
2036			
2037
2038
2039			prevMonth() {
2040				if (this.calendarPrevButton.disabled) {
2041					return;
2042				}
2043
2044				this.currentMonth = new Date(
2045					this.currentMonth.getFullYear(),
2046					this.currentMonth.getMonth() - 1,
2047					1
2048				);
2049
2050				this.renderCalendar();
2051			}
2052
2053			nextMonth() {
2054				if (this.calendarNextButton.disabled) {
2055					return;
2056				}
2057
2058				this.currentMonth = new Date(
2059					this.currentMonth.getFullYear(),
2060					this.currentMonth.getMonth() + 1,
2061					1
2062				);
2063
2064				this.renderCalendar();
2065			}
2066
2067			updateMonthNavigation() {
2068				const today = startOfDay(new Date());
2069				const maxDate = addDays(today, this.maxFutureDays);
2070				const monthStart = new Date(this.currentMonth.getFullYear(), this.currentMonth.getMonth(), 1);
2071				const prevMonthEnd = new Date(monthStart.getFullYear(), monthStart.getMonth(), 0);
2072
2073				this.calendarPrevButton.disabled = prevMonthEnd < today;
2074				this.calendarNextButton.disabled = monthStart > maxDate;
2075
2076				
2077				
2078			}
2079
2080			
2081
2082
2083			setDurations(durs, defaultDur = undefined) {
2084				this.availableDurs = durs ?? [];
2085
2086				this.duration.innerHTML = this.availableDurs
2087					.map(dur => `
2088            <option value="${dur.value}" ${dur.value === defaultDur ? "selected" : ""}>
2089                ${dur.text}
2090            </option>
2091        `)
2092					.join("");
2093
2094				if (defaultDur != null && this.availableDurs.some(dur => dur.value === defaultDur)) {
2095					this.duration.value = defaultDur;
2096				}
2097			}
2098
2099			setSlots(slots) {
2100				this.availableSlots = slots ?? [];
2101				this.selectedSlot = null;
2102
2103				this.renderSlots();
2104
2105				if (!slots || slots.length == 0) {
2106					this.noSlotsNoticeContainer?.classList.remove('hidden');
2107					this.menuKindContainer?.classList.add('hidden');
2108					this.durationContainer?.classList.add('hidden');
2109				} else {
2110					this.noSlotsNoticeContainer?.classList.add('hidden');
2111					this.menuKindContainer?.classList.remove('hidden');
2112					this.durationContainer?.classList.remove('hidden');
2113				}
2114			}
2115
2116			renderSlots() {
2117				this.slots.innerHTML = "";
2118
2119				for (const slot of this.availableSlots) {
2120					const element = document.createElement("div");
2121					element.dataset.time = slot.time;
2122
2123					if (slot.isAuto) {
2124						element.dataset.isAuto = "1";
2125					}
2126
2127					if (slot.price) {
2128						element.dataset.price = slot.price;
2129					}
2130
2131					element.className = [
2132						"time-slot",
2133						"flex",
2134						"items-center",
2135						"gap-1",
2136						"justify-between",
2137						"border",
2138						"border",
2139						"rounded-lg",
2140						"px-3",
2141						"py-3",
2142						"cursor-pointer",
2143						"transition",
2144						"gdrst-body-text",
2145						"font-medium",
2146					].join(" ");
2147
2148					if (this.selectedSlot?.time === slot.time) {
2149						element.classList.add(
2150							"gdrst-nav",
2151							"gdrst-nav-text",
2152							"selected",
2153							"font-black",
2154						);
2155						element.classList.remove("gdrst-body-text", "font-medium");
2156					}
2157
2158					const time = document.createElement("span");
2159					time.textContent = slot.time;
2160					element.appendChild(time);
2161
2162					const indicators = document.createElement("span");
2163					indicators.className = "flex items-center gap-1";
2164
2165					if (slot.isAuto) {
2166						const auto = document.createElement("span");
2167
2168						auto.className = [
2169							"w-2",
2170							"h-2",
2171							"bg-green-500",
2172							"rounded-full"
2173						].join(" ");
2174
2175						indicators.prepend(auto);
2176					}
2177
2178					if (slot.price) {
2179						const price = document.createElement("span");
2180
2181						price.textContent = "$";
2182
2183						price.className = [
2184							"text-green-600",
2185							"text-md",
2186							"font-bold",
2187							"leading-none"
2188						].join(" ");
2189
2190						indicators.appendChild(price);
2191					}
2192
2193					element.appendChild(indicators);
2194					element.addEventListener("click", () => this.selectSlot(slot));
2195
2196					this.slots.appendChild(element);
2197				}
2198			}
2199
2200			selectSlot(slot) {
2201				this.selectedSlot = slot;
2202
2203				this.renderSlots();
2204
2205				this.onSlotChange(slot);
2206			}
2207		}
2208
2209		dateTimePicker = new DateTimePicker({
2210			id: "date-time-picker",
2211			selectedDate: reserveData.date,
2212			months: [],
2213			weekdays: [],
2214
2215			
2216			
2217			
2218			
2219			
2220			
2221			
2222			
2223			
2224			
2225			
2226			
2227			
2228			
2229			
2230			
2231			
2232
2233			maxFutureDays: PAGEDATA.maxDays,
2234
2235			onDateChange: async date => {
2236				reserveData.date = date;
2237
2238				const { durDefault, durations, slots, signedSlots, signedSlots2 } = await fetchSlots(date, reserveData.duration, reserveData.people);
2239				dateTimePicker.setDurations(durations, durDefault);
2240				dateTimePicker.setSlots(slots);
2241				reserveData.signedSlots = signedSlots;
2242				reserveData.signedSlots2 = signedSlots2;
2243
2244				if (reserveData.date && reserveData.duration && reserveData.slotTime) {
2245					steps.setNextEnabled();
2246				}
2247			},
2248
2249			onDurationChange: duration => {
2250				reserveData.duration = duration;
2251
2252				if (reserveData.date && reserveData.duration && reserveData.slotTime) {
2253					steps.setNextEnabled();
2254				}
2255			},
2256
2257			onSlotChange: slot => {
2258				reserveData.slotTime = slot.time;
2259				reserveData.slotIsAuto = slot.isAuto;
2260				reserveData.slotPrice = slot.price;
2261
2262				if (slot.isAuto) {
2263					autoSlotInfo.classList.remove("hidden");
2264				} else {
2265					autoSlotInfo.classList.add("hidden");
2266				}
2267
2268				if (slot.price) {
2269					priceSlotInfo.classList.remove("hidden");
2270				} else {
2271					priceSlotInfo.classList.add("hidden");
2272				}
2273
2274				if (reserveData.date && reserveData.duration && reserveData.slotTime) {
2275					steps.setNextEnabled();
2276				}
2277			}
2278		});
2279
2280		let header = new NavHeader('header-container', {
2281			navName: 'Casa Italia',
2282			isMichelin: `false`,
2283			isMichelinBib: `false`,
2284			isStarWine: `false`,
2285			isGaultMillau: `false`,
2286			logoUrl: 'https:\/\/cdn.goodresto.app\/id\/382549298295_md',
2287			activeNavLink: '\/reserve',
2288
2289			navLinks: [
2290				{ label: 'Home page', href: '/', i18n: 'home' },
2291				{ label: 'Menu', href: '/menu', i18n: 'menu' },
2292				{ label: 'Reservation', href: '/reserve', i18n: 'reservation' },
2293			],
2294		});
2295
2296		const langSwitcher = new LanguageDialog('lang-btn', pageLangs, {
2297			noUpdate: true,
2298			defaultLang: getLang(),
2299			onLanguageChange: (lang) => {
2300				window.location.reload();
2301			}
2302		});
2303
2304		window.addEventListener("DOMContentLoaded", () => {
2305			new CookieConsent({
2306				gIds: "G-WZLNJ9YQSY".split(","),
2307				metaPixelIds: "1815606706548107".split(","),
2308			});
2309		});
2310	</script>
2310
2311
2312</body>
2313
2314</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.