MatteoScript commited on
Commit
b228270
·
verified ·
1 Parent(s): d893522

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +771 -17
index.html CHANGED
@@ -1,19 +1,773 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="it">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
6
+ <meta name="theme-color" content="#0d6b7d" />
7
+ <title>Scopri il tuo Tavolo</title>
8
+ <style>
9
+ :root {
10
+ --navy: #073348;
11
+ --blue: #0a6f89;
12
+ --aqua: #9de7df;
13
+ --foam: #f3fffb;
14
+ --sand: #ffe2a3;
15
+ --gold: #f7ba56;
16
+ --coral: #ff9b86;
17
+ --white: rgba(255, 255, 255, .92);
18
+ --shadow: 0 30px 90px rgba(3, 45, 64, .34);
19
+ }
20
+
21
+ * { box-sizing: border-box; }
22
+
23
+ html,
24
+ body {
25
+ width: 100%;
26
+ min-height: 100%;
27
+ margin: 0;
28
+ font-family: ui-rounded, "SF Pro Rounded", "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
29
+ color: var(--navy);
30
+ background: #0d6b7d;
31
+ overflow-x: hidden;
32
+ }
33
+
34
+ body {
35
+ min-height: 100svh;
36
+ background:
37
+ radial-gradient(circle at 18% 10%, rgba(255,255,255,.92) 0 7%, transparent 24%),
38
+ radial-gradient(circle at 88% 13%, rgba(255,226,163,.9) 0 8%, transparent 25%),
39
+ radial-gradient(circle at 50% 96%, rgba(255,255,255,.32) 0 12%, transparent 42%),
40
+ linear-gradient(145deg, #d7fff6 0%, #8be5e2 26%, #1e98b1 61%, #064a66 100%);
41
+ }
42
+
43
+ body::before,
44
+ body::after {
45
+ content: "";
46
+ position: fixed;
47
+ left: -20vw;
48
+ right: -20vw;
49
+ bottom: -10vh;
50
+ height: 33vh;
51
+ pointer-events: none;
52
+ z-index: 0;
53
+ background:
54
+ radial-gradient(70% 90% at 50% 0%, rgba(255,255,255,.92) 0 12%, transparent 13%),
55
+ radial-gradient(70% 90% at 16% 8%, rgba(255,255,255,.70) 0 12%, transparent 13%),
56
+ radial-gradient(70% 90% at 84% 5%, rgba(255,255,255,.66) 0 12%, transparent 13%);
57
+ filter: blur(1px);
58
+ opacity: .75;
59
+ animation: wave 7s ease-in-out infinite alternate;
60
+ }
61
+
62
+ body::after {
63
+ bottom: -4vh;
64
+ height: 26vh;
65
+ opacity: .35;
66
+ animation-duration: 11s;
67
+ animation-direction: alternate-reverse;
68
+ }
69
+
70
+ @keyframes wave {
71
+ from { transform: translateX(-4vw) translateY(0) rotate(-2deg); }
72
+ to { transform: translateX(4vw) translateY(-1.5vh) rotate(1deg); }
73
+ }
74
+
75
+ .app {
76
+ position: relative;
77
+ z-index: 1;
78
+ min-height: 100svh;
79
+ display: grid;
80
+ place-items: center;
81
+ padding: max(18px, env(safe-area-inset-top)) 18px max(24px, env(safe-area-inset-bottom));
82
+ }
83
+
84
+ .card {
85
+ width: min(100%, 460px);
86
+ border-radius: 36px;
87
+ overflow: hidden;
88
+ border: 1px solid rgba(255,255,255,.56);
89
+ background: linear-gradient(180deg, rgba(255,255,255,.78), rgba(255,255,255,.42));
90
+ box-shadow: var(--shadow);
91
+ backdrop-filter: blur(26px);
92
+ -webkit-backdrop-filter: blur(26px);
93
+ animation: enter .72s cubic-bezier(.2,.9,.2,1.12) both;
94
+ }
95
+
96
+ @keyframes enter {
97
+ from { opacity: 0; transform: translateY(24px) scale(.97); }
98
+ to { opacity: 1; transform: translateY(0) scale(1); }
99
+ }
100
+
101
+ .hero {
102
+ position: relative;
103
+ padding: 32px 24px 22px;
104
+ text-align: center;
105
+ isolation: isolate;
106
+ }
107
+
108
+ .hero::before {
109
+ content: "";
110
+ position: absolute;
111
+ top: 8px;
112
+ left: 50%;
113
+ width: 184px;
114
+ height: 184px;
115
+ border-radius: 50%;
116
+ transform: translateX(-50%);
117
+ z-index: -1;
118
+ background: radial-gradient(circle, rgba(255,246,194,.96) 0 27%, rgba(255,212,122,.58) 28% 45%, transparent 46%);
119
+ animation: sunPulse 4s ease-in-out infinite alternate;
120
+ }
121
+
122
+ @keyframes sunPulse {
123
+ from { opacity: .72; transform: translateX(-50%) scale(.95); }
124
+ to { opacity: 1; transform: translateX(-50%) scale(1.06); }
125
+ }
126
+
127
+ .badge {
128
+ display: inline-flex;
129
+ align-items: center;
130
+ justify-content: center;
131
+ gap: 8px;
132
+ padding: 9px 13px;
133
+ border-radius: 999px;
134
+ color: #06425a;
135
+ background: rgba(255,255,255,.68);
136
+ box-shadow: 0 10px 24px rgba(5, 48, 68, .10);
137
+ font-size: 12px;
138
+ font-weight: 950;
139
+ letter-spacing: .12em;
140
+ text-transform: uppercase;
141
+ }
142
+
143
+ h1 {
144
+ margin: 22px auto 10px;
145
+ max-width: 8ch;
146
+ font-size: clamp(43px, 13vw, 64px);
147
+ line-height: .86;
148
+ letter-spacing: -.07em;
149
+ color: #063a52;
150
+ text-wrap: balance;
151
+ }
152
+
153
+ .subtitle {
154
+ margin: 0 auto;
155
+ max-width: 28ch;
156
+ color: rgba(7, 51, 72, .72);
157
+ font-size: 16px;
158
+ line-height: 1.42;
159
+ font-weight: 720;
160
+ }
161
+
162
+ .islands {
163
+ display: flex;
164
+ justify-content: center;
165
+ align-items: end;
166
+ gap: 7px;
167
+ height: 40px;
168
+ margin-top: 22px;
169
+ }
170
+
171
+ .island {
172
+ display: block;
173
+ border-radius: 999px 999px 22px 22px;
174
+ background: linear-gradient(180deg, #ffeeb7, #ecae47);
175
+ box-shadow: inset 0 -8px 0 rgba(129, 76, 23, .12);
176
+ animation: bob 3.2s ease-in-out infinite;
177
+ }
178
+
179
+ .island:nth-child(1) { width: 42px; height: 15px; animation-delay: -.15s; }
180
+ .island:nth-child(2) { width: 82px; height: 28px; animation-delay: -.55s; }
181
+ .island:nth-child(3) { width: 35px; height: 13px; animation-delay: -.9s; }
182
+
183
+ @keyframes bob {
184
+ 0%, 100% { transform: translateY(0); }
185
+ 50% { transform: translateY(-5px); }
186
+ }
187
+
188
+ .form {
189
+ padding: 6px 24px 28px;
190
+ }
191
+
192
+ label {
193
+ display: block;
194
+ margin: 0 0 10px;
195
+ color: rgba(7, 51, 72, .70);
196
+ font-size: 12px;
197
+ font-weight: 950;
198
+ letter-spacing: .11em;
199
+ text-transform: uppercase;
200
+ }
201
+
202
+ input {
203
+ width: 100%;
204
+ min-height: 64px;
205
+ border: 0;
206
+ outline: none;
207
+ border-radius: 24px;
208
+ padding: 0 18px;
209
+ color: var(--navy);
210
+ background: rgba(255,255,255,.91);
211
+ font-size: 18px;
212
+ font-weight: 860;
213
+ box-shadow:
214
+ inset 0 0 0 1px rgba(7, 51, 72, .09),
215
+ 0 16px 32px rgba(3, 45, 64, .14);
216
+ transition: transform .18s ease, box-shadow .18s ease, background .18s ease;
217
+ }
218
+
219
+ input::placeholder {
220
+ color: rgba(7, 51, 72, .35);
221
+ font-weight: 760;
222
+ }
223
+
224
+ input:focus {
225
+ transform: translateY(-1px);
226
+ background: #fff;
227
+ box-shadow:
228
+ inset 0 0 0 2px rgba(10,111,137,.34),
229
+ 0 18px 36px rgba(3, 45, 64, .18);
230
+ }
231
+
232
+ .discover {
233
+ position: relative;
234
+ width: 100%;
235
+ min-height: 66px;
236
+ margin-top: 14px;
237
+ border: 0;
238
+ border-radius: 24px;
239
+ overflow: hidden;
240
+ cursor: pointer;
241
+ color: #063a52;
242
+ background:
243
+ linear-gradient(115deg, rgba(255,255,255,.72), transparent 30% 68%, rgba(255,255,255,.70)),
244
+ linear-gradient(135deg, #ffe5a6, #fff0b8 45%, #ff9b86);
245
+ box-shadow: 0 20px 38px rgba(140, 82, 24, .27), inset 0 -3px 0 rgba(109, 59, 18, .14);
246
+ font-size: 18px;
247
+ font-weight: 1000;
248
+ letter-spacing: -.02em;
249
+ transition: transform .16s ease, filter .16s ease;
250
+ }
251
+
252
+ .discover::after {
253
+ content: "";
254
+ position: absolute;
255
+ top: -90%;
256
+ left: -46%;
257
+ width: 35%;
258
+ height: 280%;
259
+ background: rgba(255,255,255,.74);
260
+ transform: rotate(23deg);
261
+ animation: shine 3.15s ease-in-out infinite;
262
+ }
263
+
264
+ @keyframes shine {
265
+ 0%, 54% { left: -52%; }
266
+ 100% { left: 122%; }
267
+ }
268
+
269
+ .discover:active {
270
+ transform: translateY(2px) scale(.99);
271
+ filter: saturate(1.05);
272
+ }
273
+
274
+ .message {
275
+ min-height: 0;
276
+ margin-top: 14px;
277
+ opacity: 0;
278
+ transform: translateY(8px);
279
+ transition: opacity .24s ease, transform .24s ease;
280
+ }
281
+
282
+ .message.show {
283
+ opacity: 1;
284
+ transform: translateY(0);
285
+ }
286
+
287
+ .message-card {
288
+ border-radius: 24px;
289
+ padding: 18px 16px;
290
+ text-align: center;
291
+ color: rgba(7, 51, 72, .78);
292
+ background: rgba(255,255,255,.62);
293
+ border: 1px solid rgba(255,255,255,.6);
294
+ box-shadow: 0 14px 30px rgba(3, 45, 64, .12);
295
+ font-size: 14px;
296
+ font-weight: 800;
297
+ line-height: 1.35;
298
+ }
299
+
300
+ .reveal {
301
+ position: fixed;
302
+ inset: 0;
303
+ z-index: 20;
304
+ display: grid;
305
+ place-items: center;
306
+ padding: max(20px, env(safe-area-inset-top)) 18px max(22px, env(safe-area-inset-bottom));
307
+ color: #fff;
308
+ background:
309
+ radial-gradient(circle at 50% 21%, rgba(255,236,169,.95) 0 8%, rgba(255,206,109,.45) 9% 18%, transparent 34%),
310
+ radial-gradient(circle at 22% 78%, rgba(157,231,223,.42), transparent 31%),
311
+ radial-gradient(circle at 86% 75%, rgba(255,155,134,.36), transparent 28%),
312
+ linear-gradient(145deg, #0a87a3 0%, #075f7d 45%, #042d46 100%);
313
+ opacity: 0;
314
+ pointer-events: none;
315
+ transform: scale(1.03);
316
+ transition: opacity .42s ease, transform .42s ease;
317
+ overflow: hidden;
318
+ }
319
+
320
+ .reveal.show {
321
+ opacity: 1;
322
+ pointer-events: auto;
323
+ transform: scale(1);
324
+ }
325
+
326
+ .reveal::before,
327
+ .reveal::after {
328
+ content: "";
329
+ position: absolute;
330
+ left: -25vw;
331
+ right: -25vw;
332
+ bottom: -6vh;
333
+ height: 30vh;
334
+ background:
335
+ radial-gradient(70% 90% at 50% 0%, rgba(255,255,255,.88) 0 12%, transparent 13%),
336
+ radial-gradient(70% 90% at 15% 6%, rgba(255,255,255,.58) 0 12%, transparent 13%),
337
+ radial-gradient(70% 90% at 84% 7%, rgba(255,255,255,.52) 0 12%, transparent 13%);
338
+ opacity: .56;
339
+ animation: revealWave 6s ease-in-out infinite alternate;
340
+ }
341
+
342
+ .reveal::after {
343
+ bottom: -1vh;
344
+ opacity: .28;
345
+ animation-duration: 9s;
346
+ animation-direction: alternate-reverse;
347
+ }
348
+
349
+ @keyframes revealWave {
350
+ from { transform: translateX(-4vw) rotate(-2deg); }
351
+ to { transform: translateX(4vw) translateY(-1vh) rotate(1deg); }
352
+ }
353
+
354
+ .reveal-content {
355
+ position: relative;
356
+ z-index: 2;
357
+ width: min(100%, 560px);
358
+ min-height: min(76svh, 720px);
359
+ display: flex;
360
+ flex-direction: column;
361
+ align-items: center;
362
+ justify-content: center;
363
+ text-align: center;
364
+ border-radius: 38px;
365
+ padding: 32px 22px;
366
+ background: linear-gradient(180deg, rgba(255,255,255,.20), rgba(255,255,255,.08));
367
+ border: 1px solid rgba(255,255,255,.26);
368
+ box-shadow: 0 40px 110px rgba(0,0,0,.28);
369
+ backdrop-filter: blur(18px);
370
+ -webkit-backdrop-filter: blur(18px);
371
+ animation: revealPop .7s cubic-bezier(.2,.9,.16,1.22) both;
372
+ }
373
+
374
+ .reveal.show .reveal-content {
375
+ animation: revealPop .7s cubic-bezier(.2,.9,.16,1.22) both;
376
+ }
377
+
378
+ @keyframes revealPop {
379
+ from { opacity: 0; transform: translateY(32px) scale(.86) rotate(-1.5deg); filter: blur(8px); }
380
+ 60% { opacity: 1; transform: translateY(-4px) scale(1.03) rotate(.5deg); filter: blur(0); }
381
+ to { opacity: 1; transform: translateY(0) scale(1) rotate(0); filter: blur(0); }
382
+ }
383
+
384
+ .hello {
385
+ margin: 0 0 18px;
386
+ color: rgba(255,255,255,.86);
387
+ font-size: clamp(18px, 5vw, 24px);
388
+ font-weight: 900;
389
+ letter-spacing: .02em;
390
+ }
391
+
392
+ .small-title {
393
+ display: inline-flex;
394
+ align-items: center;
395
+ justify-content: center;
396
+ gap: 8px;
397
+ margin-bottom: 16px;
398
+ padding: 10px 14px;
399
+ border-radius: 999px;
400
+ color: #063a52;
401
+ background: rgba(255,255,255,.86);
402
+ box-shadow: 0 14px 34px rgba(0,0,0,.12);
403
+ font-size: 13px;
404
+ font-weight: 1000;
405
+ letter-spacing: .14em;
406
+ text-transform: uppercase;
407
+ }
408
+
409
+ .table-name {
410
+ margin: 0;
411
+ max-width: 9ch;
412
+ color: #fff7c8;
413
+ font-size: clamp(58px, 18vw, 118px);
414
+ line-height: .82;
415
+ letter-spacing: -.08em;
416
+ font-weight: 1000;
417
+ text-shadow:
418
+ 0 8px 0 rgba(0,0,0,.10),
419
+ 0 24px 52px rgba(0,0,0,.28);
420
+ text-wrap: balance;
421
+ }
422
+
423
+ .reveal-note {
424
+ margin: 24px auto 0;
425
+ max-width: 26ch;
426
+ color: rgba(255,255,255,.86);
427
+ font-size: 16px;
428
+ line-height: 1.35;
429
+ font-weight: 800;
430
+ }
431
+
432
+ .again {
433
+ min-height: 52px;
434
+ margin-top: 30px;
435
+ border: 0;
436
+ border-radius: 999px;
437
+ padding: 0 22px;
438
+ color: #063a52;
439
+ background: rgba(255,255,255,.92);
440
+ box-shadow: 0 18px 42px rgba(0,0,0,.18);
441
+ font-size: 15px;
442
+ font-weight: 950;
443
+ cursor: pointer;
444
+ }
445
+
446
+ .status {
447
+ position: absolute;
448
+ width: 1px;
449
+ height: 1px;
450
+ overflow: hidden;
451
+ clip: rect(0 0 0 0);
452
+ white-space: nowrap;
453
+ }
454
+
455
+ #confetti {
456
+ position: fixed;
457
+ inset: 0;
458
+ z-index: 40;
459
+ width: 100%;
460
+ height: 100%;
461
+ pointer-events: none;
462
+ }
463
+
464
+ @media (max-width: 380px) {
465
+ .app { padding-left: 12px; padding-right: 12px; }
466
+ .hero { padding: 28px 18px 20px; }
467
+ .form { padding-left: 18px; padding-right: 18px; }
468
+ input, .discover { min-height: 60px; border-radius: 22px; }
469
+ .reveal-content { border-radius: 32px; padding-left: 16px; padding-right: 16px; }
470
+ }
471
+
472
+ @media (prefers-reduced-motion: reduce) {
473
+ *, *::before, *::after {
474
+ animation-duration: .001ms !important;
475
+ animation-iteration-count: 1 !important;
476
+ transition-duration: .001ms !important;
477
+ scroll-behavior: auto !important;
478
+ }
479
+ }
480
+ </style>
481
+ </head>
482
+ <body>
483
+ <canvas id="confetti"></canvas>
484
+
485
+ <main class="app">
486
+ <section class="card" aria-label="Scopri il tavolo">
487
+ <div class="hero">
488
+ <div class="badge">💍 Tableau de Mariage</div>
489
+ <h1>Trova la tua Cala</h1>
490
+ <p class="subtitle">Inserisci nome e cognome e lasciati guidare dalle Tremiti.</p>
491
+ <div class="islands" aria-hidden="true">
492
+ <span class="island"></span>
493
+ <span class="island"></span>
494
+ <span class="island"></span>
495
+ </div>
496
+ </div>
497
+
498
+ <div class="form">
499
+ <label for="guestName">Nome e cognome</label>
500
+ <input id="guestName" autocomplete="name" inputmode="text" placeholder="Es. Gaia Pollastrini" />
501
+ <button class="discover" id="discover" type="button">Scopri il Tavolo ✨</button>
502
+ <div id="message" class="message" aria-live="polite"></div>
503
+ <div id="status" class="status" aria-live="polite"></div>
504
+ </div>
505
+ </section>
506
+ </main>
507
+
508
+ <section id="reveal" class="reveal" aria-live="assertive" aria-hidden="true">
509
+ <div class="reveal-content">
510
+ <p class="hello" id="hello"></p>
511
+ <div class="small-title">🌊 Il tuo tavolo è</div>
512
+ <h2 class="table-name" id="tableName"></h2>
513
+ <p class="reveal-note">Brinda, sorridi e goditi la serata.</p>
514
+ <button class="again" id="again" type="button">Cerca un altro nome</button>
515
+ </div>
516
+ </section>
517
+
518
+ <script>
519
+ const DATA_URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vTfZi-c3ZoLv0pHDNqK64NzBISDsNy3cF2qf-ZD02yYuURljcDVNPNXsvfWcN5oGQ/pub?output=csv";
520
+
521
+ const SAMPLE_CSV = `
522
+ Nome Cognome,Nome del tavolo
523
+ Gaia Pollastrini,Cala Matano
524
+ Luca Bianchi,Cala Tramontana
525
+ Sara Conti,Cala Spido
526
+ Marco De Santis,Cala Zio Cesare
527
+ Elena Ferri,Cala degli Inglesi
528
+ Davide Greco,Cala Tonda
529
+ Chiara Romano,Cala dei Benedettini
530
+ Alessandro Rinaldi,Cala Pietre di Fucile
531
+ Martina Esposito,Cala Sorrentino
532
+ `;
533
+
534
+ const els = {
535
+ input: document.getElementById("guestName"),
536
+ discover: document.getElementById("discover"),
537
+ message: document.getElementById("message"),
538
+ status: document.getElementById("status"),
539
+ reveal: document.getElementById("reveal"),
540
+ hello: document.getElementById("hello"),
541
+ tableName: document.getElementById("tableName"),
542
+ again: document.getElementById("again"),
543
+ canvas: document.getElementById("confetti")
544
+ };
545
+
546
+ let guests = [];
547
+
548
+ function normalizeName(value) {
549
+ return String(value || "")
550
+ .normalize("NFD")
551
+ .replace(/[\u0300-\u036f]/g, "")
552
+ .toLowerCase()
553
+ .replace(/[^\p{L}\p{N}\s'-]/gu, " ")
554
+ .replace(/\s+/g, " ")
555
+ .trim();
556
+ }
557
+
558
+ function firstName(fullName) {
559
+ return String(fullName || "").trim().split(/\s+/)[0] || "ospite";
560
+ }
561
+
562
+ function parseCSV(text) {
563
+ const rows = [];
564
+ let row = [];
565
+ let cell = "";
566
+ let quoted = false;
567
+
568
+ for (let i = 0; i < text.length; i++) {
569
+ const char = text[i];
570
+ const next = text[i + 1];
571
+
572
+ if (char === '"' && quoted && next === '"') {
573
+ cell += '"';
574
+ i++;
575
+ } else if (char === '"') {
576
+ quoted = !quoted;
577
+ } else if (char === "," && !quoted) {
578
+ row.push(cell.trim());
579
+ cell = "";
580
+ } else if ((char === "\n" || char === "\r") && !quoted) {
581
+ if (char === "\r" && next === "\n") i++;
582
+ row.push(cell.trim());
583
+ if (row.some(Boolean)) rows.push(row);
584
+ row = [];
585
+ cell = "";
586
+ } else {
587
+ cell += char;
588
+ }
589
+ }
590
+
591
+ row.push(cell.trim());
592
+ if (row.some(Boolean)) rows.push(row);
593
+ return rows;
594
+ }
595
+
596
+ function tableFromCSV(csvText) {
597
+ const rows = parseCSV(csvText);
598
+ const headerIndex = rows.findIndex(row =>
599
+ normalizeName(row[0]) === "nome cognome" &&
600
+ normalizeName(row[1]) === "nome del tavolo"
601
+ );
602
+
603
+ const start = headerIndex >= 0 ? headerIndex + 1 : 1;
604
+ return rows
605
+ .slice(start)
606
+ .map(row => ({
607
+ fullName: row[0] || "",
608
+ tableName: row[1] || "",
609
+ key: normalizeName(row[0] || "")
610
+ }))
611
+ .filter(item => item.fullName && item.tableName);
612
+ }
613
+
614
+ async function loadGuests() {
615
+ const params = new URLSearchParams(window.location.search);
616
+ const url = params.get("data") || DATA_URL;
617
+
618
+ try {
619
+ if (url && url.trim()) {
620
+ const response = await fetch(url.trim(), { cache: "no-store" });
621
+ if (!response.ok) throw new Error("Lista non disponibile");
622
+ guests = tableFromCSV(await response.text());
623
+ } else {
624
+ guests = tableFromCSV(SAMPLE_CSV);
625
+ }
626
+ els.status.textContent = "Lista invitati pronta.";
627
+ } catch (error) {
628
+ guests = tableFromCSV(SAMPLE_CSV);
629
+ els.status.textContent = "Lista invitati pronta.";
630
+ }
631
+ }
632
+
633
+ function findGuest(query) {
634
+ const key = normalizeName(query);
635
+ if (!key) return null;
636
+
637
+ const exact = guests.find(guest => guest.key === key);
638
+ if (exact) return exact;
639
+
640
+ const partials = guests.filter(guest => guest.key.includes(key) || key.includes(guest.key));
641
+ return partials.length === 1 ? partials[0] : null;
642
+ }
643
+
644
+ function showMessage(text) {
645
+ els.message.classList.remove("show");
646
+ window.setTimeout(() => {
647
+ els.message.innerHTML = `<div class="message-card">${escapeHTML(text)}</div>`;
648
+ els.message.classList.add("show");
649
+ }, 60);
650
+ }
651
+
652
+ function showReveal(guest) {
653
+ els.hello.textContent = `Ciao ${firstName(guest.fullName)} ✨`;
654
+ els.tableName.textContent = guest.tableName;
655
+ els.reveal.classList.add("show");
656
+ els.reveal.setAttribute("aria-hidden", "false");
657
+ celebrate();
658
+ }
659
+
660
+ function hideReveal() {
661
+ els.reveal.classList.remove("show");
662
+ els.reveal.setAttribute("aria-hidden", "true");
663
+ els.input.value = "";
664
+ els.message.classList.remove("show");
665
+ els.input.focus();
666
+ }
667
+
668
+ function discoverTable() {
669
+ const typed = els.input.value;
670
+ const normalized = normalizeName(typed);
671
+
672
+ if (!normalized) {
673
+ showMessage("Scrivi nome e cognome per scoprire il tuo tavolo.");
674
+ els.input.focus();
675
+ return;
676
+ }
677
+
678
+ const guest = findGuest(typed);
679
+ if (!guest) {
680
+ showMessage("Nome non trovato. Controlla di aver scritto nome e cognome corretti.");
681
+ return;
682
+ }
683
+
684
+ showReveal(guest);
685
+ }
686
+
687
+ function escapeHTML(value) {
688
+ return String(value)
689
+ .replaceAll("&", "&amp;")
690
+ .replaceAll("<", "&lt;")
691
+ .replaceAll(">", "&gt;")
692
+ .replaceAll('"', "&quot;")
693
+ .replaceAll("'", "&#039;");
694
+ }
695
+
696
+ els.discover.addEventListener("click", discoverTable);
697
+ els.again.addEventListener("click", hideReveal);
698
+ els.input.addEventListener("keydown", event => {
699
+ if (event.key === "Enter") discoverTable();
700
+ });
701
+
702
+ const ctx = els.canvas.getContext("2d");
703
+ let confetti = [];
704
+ let rafId = null;
705
+
706
+ function resizeCanvas() {
707
+ const ratio = Math.min(window.devicePixelRatio || 1, 2);
708
+ els.canvas.width = Math.floor(window.innerWidth * ratio);
709
+ els.canvas.height = Math.floor(window.innerHeight * ratio);
710
+ ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
711
+ }
712
+
713
+ function celebrate() {
714
+ resizeCanvas();
715
+ const colors = ["#fff7c8", "#ffe2a3", "#ff9b86", "#9de7df", "#ffffff"];
716
+ const centerX = window.innerWidth / 2;
717
+ const centerY = window.innerHeight * .42;
718
+
719
+ confetti = Array.from({ length: 190 }, () => {
720
+ const angle = Math.random() * Math.PI * 2;
721
+ const speed = 4 + Math.random() * 9;
722
+ return {
723
+ x: centerX,
724
+ y: centerY,
725
+ size: 5 + Math.random() * 10,
726
+ vx: Math.cos(angle) * speed,
727
+ vy: Math.sin(angle) * speed - 5,
728
+ gravity: .18 + Math.random() * .14,
729
+ rotation: Math.random() * Math.PI,
730
+ spin: (Math.random() - .5) * .34,
731
+ color: colors[Math.floor(Math.random() * colors.length)],
732
+ life: 105 + Math.random() * 55
733
+ };
734
+ });
735
+
736
+ if (rafId) cancelAnimationFrame(rafId);
737
+ animateConfetti();
738
+ }
739
+
740
+ function animateConfetti() {
741
+ ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
742
+
743
+ confetti.forEach(piece => {
744
+ piece.x += piece.vx;
745
+ piece.y += piece.vy;
746
+ piece.vy += piece.gravity;
747
+ piece.vx *= .988;
748
+ piece.rotation += piece.spin;
749
+ piece.life -= 1;
750
+
751
+ ctx.save();
752
+ ctx.translate(piece.x, piece.y);
753
+ ctx.rotate(piece.rotation);
754
+ ctx.globalAlpha = Math.max(piece.life / 120, 0);
755
+ ctx.fillStyle = piece.color;
756
+ ctx.fillRect(-piece.size / 2, -piece.size / 2, piece.size, piece.size * .62);
757
+ ctx.restore();
758
+ });
759
+
760
+ confetti = confetti.filter(piece => piece.life > 0 && piece.y < window.innerHeight + 90);
761
+
762
+ if (confetti.length) {
763
+ rafId = requestAnimationFrame(animateConfetti);
764
+ } else {
765
+ ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
766
+ }
767
+ }
768
+
769
+ window.addEventListener("resize", resizeCanvas);
770
+ loadGuests();
771
+ </script>
772
+ </body>
773
  </html>