MatteoScript commited on
Commit
5686878
·
verified ·
1 Parent(s): 7eb6063

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +367 -328
index.html CHANGED
@@ -1,3 +1,4 @@
 
1
  <html lang="it">
2
  <head>
3
  <meta charset="utf-8" />
@@ -7,16 +8,19 @@
7
 
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
- <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800&family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400&family=Great+Vibes&display=swap" rel="stylesheet">
 
11
 
12
  <style>
13
  @font-face {
14
- font-family: "Amsterdam One";
 
15
  src:
16
- url("./AmsterdamOne.woff2") format("woff2"),
17
- url("./AmsterdamOne.woff") format("woff"),
18
- url("/file=AmsterdamOne.woff2") format("woff2"),
19
- url("/file=AmsterdamOne.woff") format("woff");
 
20
  font-weight: 400;
21
  font-style: normal;
22
  font-display: swap;
@@ -35,20 +39,21 @@
35
  --cream: #F3EFE9;
36
  --paper: #FFFDF8;
37
  --ink: rgba(37, 75, 107, .82);
38
- --ink-soft: rgba(37, 75, 107, .56);
39
  --ink-faint: rgba(37, 75, 107, .34);
40
- --hair: rgba(147, 150, 117, .26);
41
  --font-main: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
42
  --font-serif: "Cormorant Garamond", "Cormorant", Georgia, serif;
43
- --font-script: "Amsterdam One", "Great Vibes", "Snell Roundhand", cursive;
44
-
45
  --grain:
46
- url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.86' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.145 0 0 0 0 0.294 0 0 0 0 0.420 0 0 0 0.045 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
47
  }
48
 
49
  * { box-sizing: border-box; }
50
 
51
- html, body {
 
52
  width: 100%;
53
  height: 100%;
54
  margin: 0;
@@ -63,10 +68,7 @@
63
  body {
64
  min-height: 100dvh;
65
  background-color: var(--cream);
66
- background-image:
67
- radial-gradient(ellipse 70% 55% at 50% 38%, rgba(237, 211, 205, .26) 0%, rgba(237, 211, 205, 0) 60%),
68
- radial-gradient(ellipse 90% 60% at 50% 105%, rgba(143, 163, 179, .14) 0%, rgba(143, 163, 179, 0) 65%),
69
- linear-gradient(180deg, #fffdf8 0%, #f5efe7 100%);
70
  position: relative;
71
  }
72
 
@@ -76,25 +78,38 @@
76
  inset: 0;
77
  background-image: var(--grain);
78
  background-size: 220px 220px;
79
- opacity: .42;
80
  mix-blend-mode: multiply;
81
  pointer-events: none;
82
  z-index: 0;
83
  }
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  body.reveal-open { overflow: hidden; }
86
 
87
- /* ---------- decorative frame ---------- */
88
- .frame {
89
  position: fixed;
90
  inset: 18px;
91
  border: 1px solid var(--hair);
92
  border-radius: 30px;
93
  pointer-events: none;
94
- z-index: 1;
95
  }
96
 
97
- /* ---------- screen layout ---------- */
98
  .screen {
99
  position: relative;
100
  width: 100%;
@@ -110,17 +125,16 @@
110
  position: relative;
111
  width: min(100%, 500px);
112
  height: 100dvh;
113
- padding: max(24px, env(safe-area-inset-top)) 30px max(24px, env(safe-area-inset-bottom));
114
  display: grid;
115
  grid-template-rows: auto 1fr;
116
  gap: 0;
117
  overflow: hidden;
118
  }
119
 
120
- /* ---------- staggered entrance ---------- */
121
  @keyframes rise {
122
  from { opacity: 0; transform: translateY(14px); }
123
- to { opacity: 1; transform: translateY(0); }
124
  }
125
 
126
  .anim { opacity: 0; animation: rise .7s cubic-bezier(.2,.7,.2,1) forwards; }
@@ -130,23 +144,21 @@
130
  .anim-4 { animation-delay: .46s; }
131
  .anim-5 { animation-delay: .60s; }
132
 
133
- /* ---------- top mark ---------- */
134
  .top-mark {
135
  display: flex;
136
  align-items: center;
137
  justify-content: center;
138
- padding: 4px 0 0;
139
  }
140
 
141
  .logo {
142
- width: 72px;
143
- height: 72px;
144
  display: block;
145
  object-fit: contain;
146
  background: transparent;
147
  }
148
 
149
- /* ---------- hero (unified centered block: tag + title + form) ---------- */
150
  .hero {
151
  min-height: 0;
152
  display: flex;
@@ -154,59 +166,40 @@
154
  align-items: center;
155
  justify-content: center;
156
  text-align: center;
157
- gap: clamp(48px, 8dvh, 80px);
158
- padding: 0 0 clamp(8px, 2dvh, 18px);
159
  }
160
 
161
- .hero-tag {
162
- margin: 0 0 clamp(26px, 5dvh, 40px);
163
- color: var(--coral-deep);
164
- font-family: var(--font-serif);
165
- font-style: italic;
166
- font-size: 15px;
167
- font-weight: 400;
168
- letter-spacing: .04em;
169
- opacity: .88;
170
  }
171
 
172
  .script-title {
173
  margin: 0 auto;
174
  color: var(--blue);
175
  font-family: var(--font-script);
176
- font-size: clamp(60px, 16vw, 96px);
177
  font-weight: 400;
178
- line-height: .78;
179
- letter-spacing: -.025em;
180
  text-wrap: balance;
 
181
  }
182
 
183
- .script-title span { display: block; }
184
- .script-title span:first-child { transform: translateX(-.04em); }
185
- .script-title span:last-child { transform: translateX(.05em); margin-top: -.02em; }
186
-
187
- /* ---------- title with olive branches (mirrors reveal) ---------- */
188
- .title-frame {
189
- position: relative;
190
- display: inline-block;
191
- margin: 0 auto;
192
- padding: 0 clamp(34px, 9vw, 58px);
193
  }
194
 
195
- .title-branch {
196
- position: absolute;
197
- top: 50%;
198
- width: clamp(34px, 8.5vw, 50px);
199
- height: clamp(120px, 28vw, 156px);
200
- transform: translateY(-50%);
201
- color: var(--olive);
202
- opacity: .62;
203
- pointer-events: none;
204
  }
205
- .title-branch svg { width: 100%; height: 100%; display: block; overflow: visible; }
206
- .title-branch-l { left: -10px; }
207
- .title-branch-r { right: -10px; transform: translateY(-50%) scaleX(-1); }
208
 
209
- /* ---------- form block (lives inside the centered hero) ---------- */
 
 
210
  .form-block {
211
  width: 100%;
212
  max-width: 380px;
@@ -218,28 +211,27 @@
218
  justify-content: center;
219
  gap: 14px;
220
  margin: 0 0 16px;
221
- color: var(--ink-soft);
222
  font-size: 10px;
223
- font-weight: 600;
224
  letter-spacing: .34em;
225
  text-transform: uppercase;
226
  }
 
227
  .field-label::before,
228
  .field-label::after {
229
  content: "";
230
  flex: 0 0 24px;
231
  height: 1px;
232
- background: var(--hair);
233
  }
234
 
235
- .input-wrap {
236
- position: relative;
237
- }
238
 
239
  input {
240
  width: 100%;
241
  height: 56px;
242
- border: 1px solid rgba(147, 150, 117, .32);
243
  outline: 0;
244
  border-radius: 999px;
245
  padding: 0 24px;
@@ -254,7 +246,11 @@
254
  transition: border-color .22s ease, box-shadow .22s ease, background .22s ease;
255
  }
256
 
257
- input::placeholder { color: var(--ink-faint); font-weight: 400; font-style: italic; }
 
 
 
 
258
 
259
  input:focus {
260
  border-color: rgba(147, 150, 117, .55);
@@ -268,7 +264,7 @@
268
  .discover {
269
  position: relative;
270
  width: 100%;
271
- height: 56px;
272
  margin-top: 14px;
273
  border: 0;
274
  border-radius: 999px;
@@ -278,10 +274,10 @@
278
  background: var(--blue);
279
  font-family: var(--font-main);
280
  font-size: 11.5px;
281
- font-weight: 600;
282
  letter-spacing: .32em;
283
  text-transform: uppercase;
284
- box-shadow: 0 6px 18px rgba(37, 75, 107, .14);
285
  transition: transform .18s ease, box-shadow .18s ease, background .18s ease;
286
  }
287
 
@@ -290,6 +286,7 @@
290
  background: var(--blue-deep);
291
  box-shadow: 0 10px 24px rgba(37, 75, 107, .18);
292
  }
 
293
  .discover:active {
294
  transform: translateY(1px);
295
  box-shadow: 0 4px 12px rgba(37, 75, 107, .12);
@@ -302,13 +299,14 @@
302
  transform: translateY(8px);
303
  transition: opacity .26s ease, transform .26s ease;
304
  }
 
305
  .message.show { opacity: 1; transform: translateY(0); }
306
 
307
  .message-card {
308
  border-radius: 14px;
309
  padding: 12px 14px;
310
  color: var(--ink);
311
- background: rgba(255, 253, 248, .85);
312
  border: 1px solid var(--hair);
313
  text-align: center;
314
  font-size: 12.5px;
@@ -317,7 +315,6 @@
317
  letter-spacing: .005em;
318
  }
319
 
320
- /* ---------- reveal screen ---------- */
321
  .reveal {
322
  position: fixed;
323
  inset: 0;
@@ -327,10 +324,7 @@
327
  padding: max(28px, env(safe-area-inset-top)) 28px max(24px, env(safe-area-inset-bottom));
328
  color: var(--blue);
329
  background-color: var(--cream);
330
- background-image:
331
- radial-gradient(ellipse 75% 60% at 50% 38%, rgba(237, 211, 205, .32) 0%, rgba(237, 211, 205, 0) 60%),
332
- radial-gradient(ellipse 90% 55% at 50% 110%, rgba(143, 163, 179, .18) 0%, rgba(143, 163, 179, 0) 65%),
333
- linear-gradient(180deg, #fffdf8 0%, #f4ede4 100%);
334
  opacity: 0;
335
  visibility: hidden;
336
  pointer-events: none;
@@ -338,16 +332,33 @@
338
  transition: opacity .36s ease, transform .36s ease, visibility 0s linear .36s;
339
  overflow: hidden;
340
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
  .reveal::after {
342
  content: "";
343
- position: fixed;
344
  inset: 0;
345
  background-image: var(--grain);
346
  background-size: 220px 220px;
347
- opacity: .42;
348
  mix-blend-mode: multiply;
349
  pointer-events: none;
 
350
  }
 
351
  .reveal.show {
352
  opacity: 1;
353
  visibility: visible;
@@ -356,14 +367,7 @@
356
  transition: opacity .36s ease, transform .36s ease, visibility 0s;
357
  }
358
 
359
- .reveal-frame {
360
- position: fixed;
361
- inset: 18px;
362
- border: 1px solid var(--hair);
363
- border-radius: 30px;
364
- pointer-events: none;
365
- z-index: 1;
366
- }
367
 
368
  .reveal-page {
369
  position: relative;
@@ -379,114 +383,86 @@
379
 
380
  .reveal.show .reveal-step { animation: rise .8s cubic-bezier(.2,.7,.2,1) both; }
381
  .reveal.show .step-1 { animation-delay: .08s; }
382
- .reveal.show .step-2 { animation-delay: .22s; }
383
- .reveal.show .step-3 { animation-delay: .38s; }
384
- .reveal.show .step-4 { animation-delay: .56s; }
385
- .reveal.show .step-5 { animation-delay: .72s; }
386
- .reveal.show .step-6 { animation-delay: .86s; }
387
 
388
  .hello {
389
  margin: 0 0 6px;
390
  color: var(--ink-soft);
391
  font-family: var(--font-serif);
392
  font-style: italic;
393
- font-size: clamp(17px, 4.4vw, 22px);
394
  font-weight: 400;
395
  letter-spacing: .005em;
396
  }
397
 
398
  .hello-divider {
399
- width: 28px;
400
  height: 1px;
401
- margin: 0 auto clamp(18px, 3.6dvh, 28px);
402
- background: var(--hair);
403
  position: relative;
404
  }
 
405
  .hello-divider::before {
406
  content: "";
407
  position: absolute;
408
  left: 50%;
409
  top: 50%;
410
- width: 5px;
411
- height: 5px;
412
  border-radius: 999px;
413
  background: var(--coral);
414
  transform: translate(-50%, -50%);
415
  }
416
 
417
  .table-kicker {
418
- margin: 0 0 12px;
419
  color: var(--coral-deep);
420
- font-size: 11px;
421
- font-weight: 700;
422
  letter-spacing: .42em;
423
  text-transform: uppercase;
424
  }
425
- .table-kicker span {
426
- display: inline-flex;
427
- align-items: center;
428
- gap: 14px;
429
- }
430
- .table-kicker span::before,
431
- .table-kicker span::after {
432
- content: "";
433
- width: 24px;
434
- height: 1px;
435
- background: var(--coral);
436
- opacity: .55;
437
- }
438
 
439
  .table-name-wrap {
440
  position: relative;
441
  display: inline-block;
442
  margin: 0 auto;
443
- padding: 0 clamp(28px, 8vw, 56px);
444
- }
445
-
446
- .branch {
447
- position: absolute;
448
- top: 50%;
449
- width: clamp(36px, 9vw, 56px);
450
- height: clamp(72px, 18vw, 110px);
451
- transform: translateY(-50%);
452
- color: var(--olive);
453
- opacity: .68;
454
- pointer-events: none;
455
- }
456
- .branch svg { width: 100%; height: 100%; display: block; overflow: visible; }
457
- .branch-l { left: -8px; }
458
- .branch-r { right: -8px; transform: translateY(-50%) scaleX(-1); }
459
-
460
- .branch-stem {
461
- fill: none;
462
- stroke: var(--olive-deep);
463
- stroke-width: 1.4;
464
- stroke-linecap: round;
465
- vector-effect: non-scaling-stroke;
466
- }
467
- .branch-leaf {
468
- fill: var(--olive);
469
- opacity: .85;
470
- }
471
- .branch-leaf-soft {
472
- fill: var(--sage);
473
- opacity: .9;
474
  }
475
 
476
  .table-name {
477
  margin: 0 auto;
478
- max-width: 11ch;
479
  color: var(--blue);
480
  font-family: var(--font-script);
481
- font-size: clamp(76px, 23vw, 144px);
482
  font-weight: 400;
483
- line-height: .76;
484
- letter-spacing: -.035em;
485
  text-wrap: balance;
486
  }
 
487
  .table-name[data-long="true"] {
488
- max-width: 12ch;
489
- font-size: clamp(56px, 17vw, 108px);
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  }
491
 
492
  .reveal-divider {
@@ -494,54 +470,85 @@
494
  align-items: center;
495
  justify-content: center;
496
  gap: 10px;
497
- margin: clamp(20px, 4dvh, 32px) auto 0;
498
  }
 
499
  .reveal-divider::before,
500
  .reveal-divider::after {
501
  content: "";
502
- width: 38px;
503
  height: 1px;
504
  background: var(--olive);
505
  opacity: .55;
506
  }
 
507
  .reveal-divider .dot {
508
- width: 6px;
509
- height: 6px;
510
  border-radius: 999px;
511
  background: var(--coral);
512
- box-shadow: 0 0 0 4px rgba(208, 138, 122, .18);
513
  }
514
 
515
- .reveal-note {
516
  margin: 16px auto 0;
517
- max-width: 32ch;
518
  color: var(--ink-soft);
519
  font-family: var(--font-serif);
520
  font-style: italic;
521
- font-size: clamp(15px, 4vw, 18px);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  font-weight: 400;
523
- line-height: 1.45;
524
  letter-spacing: .003em;
525
  }
526
 
 
 
 
 
 
 
 
 
 
 
527
  .again {
528
  align-self: center;
529
- height: 50px;
530
- margin-top: clamp(22px, 4.4dvh, 32px);
531
- border: 1px solid rgba(37, 75, 107, .16);
532
  border-radius: 999px;
533
- padding: 0 26px;
534
  color: var(--blue);
535
  background: rgba(255, 253, 248, .92);
536
  box-shadow: 0 12px 26px rgba(37, 75, 107, .07);
537
  font-family: var(--font-main);
538
  font-size: 11px;
539
- font-weight: 700;
540
  letter-spacing: .26em;
541
  text-transform: uppercase;
542
  cursor: pointer;
543
  transition: transform .18s ease, box-shadow .18s ease, background .18s ease;
544
  }
 
545
  .again:hover {
546
  transform: translateY(-1px);
547
  background: var(--paper);
@@ -566,165 +573,135 @@
566
  pointer-events: none;
567
  }
568
 
569
- /* ---------- responsive tightening ---------- */
570
- @media (max-height: 720px) {
571
- .logo { width: 60px; height: 60px; }
572
- .script-title { font-size: clamp(54px, 14vw, 84px); }
573
- .title-branch { height: clamp(108px, 26vw, 140px); }
574
- .hero { gap: clamp(24px, 4dvh, 38px); }
 
 
 
 
 
 
 
 
575
  }
576
 
577
  @media (max-width: 380px) {
578
- .frame, .reveal-frame { inset: 12px; border-radius: 24px; }
 
579
  .page { padding-left: 22px; padding-right: 22px; }
580
- .logo { width: 60px; height: 60px; }
581
- .script-title { font-size: clamp(50px, 14vw, 76px); }
582
- .title-frame { padding: 0 clamp(28px, 7vw, 44px); }
583
- .title-branch { width: clamp(28px, 7vw, 40px); height: clamp(100px, 24vw, 124px); }
584
- input, .discover { height: 54px; }
585
- .table-name { font-size: clamp(64px, 21vw, 108px); }
586
- .table-name[data-long="true"] { font-size: clamp(48px, 16vw, 88px); }
 
 
 
587
  }
588
 
589
  @media (prefers-reduced-motion: reduce) {
590
- .anim, .reveal.show .reveal-step { animation: none; opacity: 1; transform: none; }
 
 
 
 
 
591
  }
592
  </style>
593
-
594
  </head>
 
595
  <body>
596
  <canvas id="confetti"></canvas>
597
-
598
- <!-- decorative frame -->
599
-
600
  <div class="frame" aria-hidden="true"></div>
601
 
602
  <main class="screen">
603
  <div class="page">
604
  <div class="top-mark anim anim-1">
605
- <img class="logo" src="logo.png" alt="Logo matrimonio" />
 
 
 
 
 
606
  </div>
607
- <section class="hero" aria-label="Trova la tua cala">
608
- <div>
609
- <p class="hero-tag anim anim-2">a tavola, in riva al mare</p>
610
-
611
- <div class="title-frame anim anim-3">
612
- <div class="title-branch title-branch-l" aria-hidden="true">
613
- <svg viewBox="0 0 60 156">
614
- <path class="branch-stem" d="M52 8 C 32 44, 22 92, 32 150" />
615
- <ellipse class="branch-leaf" cx="46" cy="22" rx="6" ry="2.4" transform="rotate(-25 46 22)"/>
616
- <ellipse class="branch-leaf-soft" cx="38" cy="38" rx="6.5" ry="2.6" transform="rotate(-30 38 38)"/>
617
- <ellipse class="branch-leaf" cx="33" cy="56" rx="7" ry="2.7" transform="rotate(-38 33 56)"/>
618
- <ellipse class="branch-leaf-soft" cx="29" cy="74" rx="7" ry="2.7" transform="rotate(-46 29 74)"/>
619
- <ellipse class="branch-leaf" cx="27" cy="92" rx="7" ry="2.7" transform="rotate(-54 27 92)"/>
620
- <ellipse class="branch-leaf-soft" cx="28" cy="110" rx="6.5" ry="2.6" transform="rotate(-62 28 110)"/>
621
- <ellipse class="branch-leaf" cx="30" cy="128" rx="5.6" ry="2.3" transform="rotate(-70 30 128)"/>
622
- <circle cx="42" cy="30" r="1.5" fill="var(--olive-deep)" opacity=".75"/>
623
- <circle cx="31" cy="64" r="1.5" fill="var(--olive-deep)" opacity=".75"/>
624
- <circle cx="28" cy="100" r="1.5" fill="var(--olive-deep)" opacity=".75"/>
625
- </svg>
626
- </div>
627
 
628
- <h1 class="script-title">
629
- <span>Trova la</span>
630
- <span>tua Cala</span>
631
- </h1>
632
-
633
- <div class="title-branch title-branch-r" aria-hidden="true">
634
- <svg viewBox="0 0 60 156">
635
- <path class="branch-stem" d="M52 8 C 32 44, 22 92, 32 150" />
636
- <ellipse class="branch-leaf" cx="46" cy="22" rx="6" ry="2.4" transform="rotate(-25 46 22)"/>
637
- <ellipse class="branch-leaf-soft" cx="38" cy="38" rx="6.5" ry="2.6" transform="rotate(-30 38 38)"/>
638
- <ellipse class="branch-leaf" cx="33" cy="56" rx="7" ry="2.7" transform="rotate(-38 33 56)"/>
639
- <ellipse class="branch-leaf-soft" cx="29" cy="74" rx="7" ry="2.7" transform="rotate(-46 29 74)"/>
640
- <ellipse class="branch-leaf" cx="27" cy="92" rx="7" ry="2.7" transform="rotate(-54 27 92)"/>
641
- <ellipse class="branch-leaf-soft" cx="28" cy="110" rx="6.5" ry="2.6" transform="rotate(-62 28 110)"/>
642
- <ellipse class="branch-leaf" cx="30" cy="128" rx="5.6" ry="2.3" transform="rotate(-70 30 128)"/>
643
- <circle cx="42" cy="30" r="1.5" fill="var(--olive-deep)" opacity=".75"/>
644
- <circle cx="31" cy="64" r="1.5" fill="var(--olive-deep)" opacity=".75"/>
645
- <circle cx="28" cy="100" r="1.5" fill="var(--olive-deep)" opacity=".75"/>
646
- </svg>
647
  </div>
648
- </div>
649
- </div>
650
 
651
- <div class="form-block anim anim-4">
652
- <label class="field-label" for="guestName">Nome &amp; Cognome</label>
653
- <div class="input-wrap">
654
- <input id="guestName" autocomplete="name" inputmode="text" placeholder="Es. Gaia Pollastrini" />
655
- </div>
656
- <button class="discover" id="discover" type="button">Scopri il tavolo</button>
657
- <div id="message" class="message" aria-live="polite"></div>
658
- <div id="status" class="status" aria-live="polite"></div>
 
 
659
  </div>
660
- </section>
661
- </div>
662
-
663
  </main>
664
 
665
- <!-- ===================== REVEAL ===================== -->
666
-
667
  <section id="reveal" class="reveal" aria-live="assertive" aria-hidden="true">
668
  <div class="reveal-frame" aria-hidden="true"></div>
669
 
670
- <div class="reveal-page">
671
- <p class="hello reveal-step step-1" id="hello"></p>
672
- <div class="hello-divider reveal-step step-2" aria-hidden="true"></div>
673
-
674
- <p class="table-kicker reveal-step step-3"><span>Il tuo tavolo è</span></p>
675
-
676
- <div class="table-name-wrap reveal-step step-4">
677
- <!-- left olive branch -->
678
- <div class="branch branch-l" aria-hidden="true">
679
- <svg viewBox="0 0 60 120">
680
- <path class="branch-stem" d="M50 6 C 32 36, 22 70, 30 116" />
681
- <ellipse class="branch-leaf" cx="44" cy="20" rx="6" ry="2.4" transform="rotate(-25 44 20)"/>
682
- <ellipse class="branch-leaf-soft" cx="36" cy="32" rx="6.5" ry="2.6" transform="rotate(-30 36 32)"/>
683
- <ellipse class="branch-leaf" cx="32" cy="46" rx="7" ry="2.7" transform="rotate(-38 32 46)"/>
684
- <ellipse class="branch-leaf-soft" cx="28" cy="60" rx="7" ry="2.7" transform="rotate(-46 28 60)"/>
685
- <ellipse class="branch-leaf" cx="26" cy="74" rx="7" ry="2.7" transform="rotate(-54 26 74)"/>
686
- <ellipse class="branch-leaf-soft" cx="26" cy="88" rx="6.5" ry="2.6" transform="rotate(-62 26 88)"/>
687
- <ellipse class="branch-leaf" cx="28" cy="102" rx="5.6" ry="2.3" transform="rotate(-70 28 102)"/>
688
- <!-- tiny olives -->
689
- <circle cx="40" cy="26" r="1.6" fill="var(--olive-deep)" opacity=".75"/>
690
- <circle cx="30" cy="54" r="1.6" fill="var(--olive-deep)" opacity=".75"/>
691
- <circle cx="27" cy="82" r="1.6" fill="var(--olive-deep)" opacity=".75"/>
692
- </svg>
693
- </div>
694
 
695
- <h2 class="table-name" id="tableName"></h2>
696
-
697
- <!-- right olive branch (mirrored via CSS) -->
698
- <div class="branch branch-r" aria-hidden="true">
699
- <svg viewBox="0 0 60 120">
700
- <path class="branch-stem" d="M50 6 C 32 36, 22 70, 30 116" />
701
- <ellipse class="branch-leaf" cx="44" cy="20" rx="6" ry="2.4" transform="rotate(-25 44 20)"/>
702
- <ellipse class="branch-leaf-soft" cx="36" cy="32" rx="6.5" ry="2.6" transform="rotate(-30 36 32)"/>
703
- <ellipse class="branch-leaf" cx="32" cy="46" rx="7" ry="2.7" transform="rotate(-38 32 46)"/>
704
- <ellipse class="branch-leaf-soft" cx="28" cy="60" rx="7" ry="2.7" transform="rotate(-46 28 60)"/>
705
- <ellipse class="branch-leaf" cx="26" cy="74" rx="7" ry="2.7" transform="rotate(-54 26 74)"/>
706
- <ellipse class="branch-leaf-soft" cx="26" cy="88" rx="6.5" ry="2.6" transform="rotate(-62 26 88)"/>
707
- <ellipse class="branch-leaf" cx="28" cy="102" rx="5.6" ry="2.3" transform="rotate(-70 28 102)"/>
708
- <circle cx="40" cy="26" r="1.6" fill="var(--olive-deep)" opacity=".75"/>
709
- <circle cx="30" cy="54" r="1.6" fill="var(--olive-deep)" opacity=".75"/>
710
- <circle cx="27" cy="82" r="1.6" fill="var(--olive-deep)" opacity=".75"/>
711
- </svg>
712
- </div>
713
- </div>
714
 
715
- <div class="reveal-divider reveal-step step-5" aria-hidden="true">
716
- <span class="dot"></span>
717
- </div>
 
 
 
 
 
718
 
719
- <p class="reveal-note reveal-step step-5">In che cala cenerai? <br/>L'hai appena scoperto.</p>
 
 
 
 
720
 
721
- <button class="again reveal-step step-6" id="again" type="button">Cerca un altro nome</button>
722
- </div>
723
  </section>
724
 
725
  <script>
726
  const DATA_URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSlb-0cSIFaGN_BWrn_S9tQoQsdqGH7qYRYQGC3_3wRB-rGzwZoPoxIBNr9l6NmxW9Ont82YKIdGKIR/pub?output=csv";
727
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
728
  const els = {
729
  input: document.getElementById("guestName"),
730
  discover: document.getElementById("discover"),
@@ -733,11 +710,15 @@
733
  reveal: document.getElementById("reveal"),
734
  hello: document.getElementById("hello"),
735
  tableName: document.getElementById("tableName"),
 
 
736
  again: document.getElementById("again"),
737
  canvas: document.getElementById("confetti")
738
  };
739
 
740
  let guests = [];
 
 
741
  let hideTimer = null;
742
 
743
  function normalizeName(value) {
@@ -754,6 +735,15 @@
754
  return String(fullName || "").trim().split(/\s+/)[0] || "ospite";
755
  }
756
 
 
 
 
 
 
 
 
 
 
757
  function parseCSV(text) {
758
  const rows = [];
759
  let row = [];
@@ -796,6 +786,7 @@
796
  );
797
 
798
  const start = headerIndex >= 0 ? headerIndex + 1 : 1;
 
799
  return rows
800
  .slice(start)
801
  .map(row => ({
@@ -815,6 +806,8 @@
815
  } catch (error) {
816
  guests = [];
817
  els.status.textContent = "Lista invitati non disponibile.";
 
 
818
  }
819
  }
820
 
@@ -829,6 +822,48 @@
829
  return partials.length === 1 ? partials[0] : null;
830
  }
831
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
832
  function showMessage(text) {
833
  els.message.classList.remove("show");
834
  window.setTimeout(() => {
@@ -839,13 +874,15 @@
839
 
840
  function showReveal(guest) {
841
  clearTimeout(hideTimer);
842
- const longName = guest.tableName.length > 14;
843
  els.hello.textContent = `Ciao, ${firstName(guest.fullName)}`;
844
- els.tableName.textContent = guest.tableName;
845
- els.tableName.dataset.long = longName ? "true" : "false";
 
846
  document.body.classList.add("reveal-open");
847
  els.reveal.classList.add("show");
848
  els.reveal.setAttribute("aria-hidden", "false");
 
849
  celebrate();
850
  }
851
 
@@ -858,10 +895,11 @@
858
  hideTimer = window.setTimeout(() => {
859
  els.input.value = "";
860
  els.message.classList.remove("show");
 
861
  }, 360);
862
  }
863
 
864
- function discoverTable() {
865
  const typed = els.input.value;
866
  const normalized = normalizeName(typed);
867
 
@@ -870,12 +908,19 @@
870
  return;
871
  }
872
 
 
 
 
 
 
 
873
  if (!guests.length) {
874
  showMessage("Lista invitati non ancora disponibile. Riprova tra qualche secondo.");
875
  return;
876
  }
877
 
878
  const guest = findGuest(typed);
 
879
  if (!guest) {
880
  showMessage("Nome non trovato. Controlla di aver scritto nome e cognome corretti.");
881
  return;
@@ -884,25 +929,19 @@
884
  showReveal(guest);
885
  }
886
 
887
- function escapeHTML(value) {
888
- return String(value)
889
- .replaceAll("&", "&amp;")
890
- .replaceAll("<", "&lt;")
891
- .replaceAll(">", "&gt;")
892
- .replaceAll('"', "&quot;")
893
- .replaceAll("'", "&#039;");
894
- }
895
-
896
  els.discover.addEventListener("click", discoverTable);
 
897
  els.again.addEventListener("click", hideReveal);
 
898
  els.input.addEventListener("keydown", event => {
899
  if (event.key === "Enter") discoverTable();
900
  });
 
901
  window.addEventListener("keydown", event => {
902
  if (event.key === "Escape" && els.reveal.classList.contains("show")) hideReveal();
903
  });
904
 
905
- /* ============= petali (ex-confetti) ============= */
906
  const ctx = els.canvas.getContext("2d");
907
  let petals = [];
908
  let rafId = null;
@@ -916,27 +955,26 @@
916
 
917
  function celebrate() {
918
  resizeCanvas();
 
919
  const colors = ["#EDD3CD", "#D08A7A", "#C2C5B2", "#8FA3B3", "#F3EFE9", "#EDD3CD", "#D08A7A"];
920
  const w = window.innerWidth;
921
 
922
- petals = Array.from({ length: 56 }, () => {
923
- return {
924
- x: Math.random() * w,
925
- y: -20 - Math.random() * 200,
926
- rx: 5 + Math.random() * 5,
927
- ry: 2.4 + Math.random() * 1.6,
928
- vy: 1.0 + Math.random() * 1.6,
929
- vx: (Math.random() - .5) * .6,
930
- rotation: Math.random() * Math.PI,
931
- spin: (Math.random() - .5) * .04,
932
- sway: Math.random() * Math.PI * 2,
933
- swaySpeed: .015 + Math.random() * .02,
934
- swayAmp: .4 + Math.random() * .9,
935
- color: colors[Math.floor(Math.random() * colors.length)],
936
- alpha: .75 + Math.random() * .25,
937
- life: 360 + Math.random() * 200
938
- };
939
- });
940
 
941
  if (rafId) cancelAnimationFrame(rafId);
942
  animatePetals();
@@ -951,6 +989,7 @@
951
 
952
  function animatePetals() {
953
  ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
 
954
  const h = window.innerHeight;
955
 
956
  petals.forEach(p => {
@@ -982,8 +1021,8 @@
982
  }
983
 
984
  window.addEventListener("resize", resizeCanvas);
985
- loadGuests();
986
- </script>
987
 
 
 
988
  </body>
989
  </html>
 
1
+ <!doctype html>
2
  <html lang="it">
3
  <head>
4
  <meta charset="utf-8" />
 
8
 
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link rel="preconnect" href="https://db.onlinewebfonts.com" crossorigin>
12
+ <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800&family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400;1,500&family=Great+Vibes&display=swap" rel="stylesheet">
13
 
14
  <style>
15
  @font-face {
16
+ font-family: "Amsterdam 1";
17
+ src: url("https://db.onlinewebfonts.com/t/3aab0c222119b30542df27260dad0ebd.eot");
18
  src:
19
+ url("https://db.onlinewebfonts.com/t/3aab0c222119b30542df27260dad0ebd.eot?#iefix") format("embedded-opentype"),
20
+ url("https://db.onlinewebfonts.com/t/3aab0c222119b30542df27260dad0ebd.woff2") format("woff2"),
21
+ url("https://db.onlinewebfonts.com/t/3aab0c222119b30542df27260dad0ebd.woff") format("woff"),
22
+ url("https://db.onlinewebfonts.com/t/3aab0c222119b30542df27260dad0ebd.ttf") format("truetype"),
23
+ url("https://db.onlinewebfonts.com/t/3aab0c222119b30542df27260dad0ebd.svg#Amsterdam 1") format("svg");
24
  font-weight: 400;
25
  font-style: normal;
26
  font-display: swap;
 
39
  --cream: #F3EFE9;
40
  --paper: #FFFDF8;
41
  --ink: rgba(37, 75, 107, .82);
42
+ --ink-soft: rgba(37, 75, 107, .58);
43
  --ink-faint: rgba(37, 75, 107, .34);
44
+ --hair: rgba(147, 150, 117, .24);
45
  --font-main: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
46
  --font-serif: "Cormorant Garamond", "Cormorant", Georgia, serif;
47
+ --font-script: "Amsterdam 1", "Great Vibes", "Snell Roundhand", cursive;
48
+ --pine-bg: url("./assets/pine-bg-transparent.png");
49
  --grain:
50
+ url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.86' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.145 0 0 0 0 0.294 0 0 0 0 0.420 0 0 0 0.04 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
51
  }
52
 
53
  * { box-sizing: border-box; }
54
 
55
+ html,
56
+ body {
57
  width: 100%;
58
  height: 100%;
59
  margin: 0;
 
68
  body {
69
  min-height: 100dvh;
70
  background-color: var(--cream);
71
+ background-image: none;
 
 
 
72
  position: relative;
73
  }
74
 
 
78
  inset: 0;
79
  background-image: var(--grain);
80
  background-size: 220px 220px;
81
+ opacity: .18;
82
  mix-blend-mode: multiply;
83
  pointer-events: none;
84
  z-index: 0;
85
  }
86
 
87
+ body::after {
88
+ content: "";
89
+ position: fixed;
90
+ inset: 0;
91
+ background-image: var(--pine-bg);
92
+ background-repeat: no-repeat;
93
+ background-position: center 50%;
94
+ background-size: min(112vw, 620px) auto;
95
+ opacity: .55;
96
+ mix-blend-mode: multiply;
97
+ pointer-events: none;
98
+ z-index: 1;
99
+ }
100
+
101
  body.reveal-open { overflow: hidden; }
102
 
103
+ .frame,
104
+ .reveal-frame {
105
  position: fixed;
106
  inset: 18px;
107
  border: 1px solid var(--hair);
108
  border-radius: 30px;
109
  pointer-events: none;
110
+ z-index: 2;
111
  }
112
 
 
113
  .screen {
114
  position: relative;
115
  width: 100%;
 
125
  position: relative;
126
  width: min(100%, 500px);
127
  height: 100dvh;
128
+ padding: max(22px, env(safe-area-inset-top)) 30px max(24px, env(safe-area-inset-bottom));
129
  display: grid;
130
  grid-template-rows: auto 1fr;
131
  gap: 0;
132
  overflow: hidden;
133
  }
134
 
 
135
  @keyframes rise {
136
  from { opacity: 0; transform: translateY(14px); }
137
+ to { opacity: 1; transform: translateY(0); }
138
  }
139
 
140
  .anim { opacity: 0; animation: rise .7s cubic-bezier(.2,.7,.2,1) forwards; }
 
144
  .anim-4 { animation-delay: .46s; }
145
  .anim-5 { animation-delay: .60s; }
146
 
 
147
  .top-mark {
148
  display: flex;
149
  align-items: center;
150
  justify-content: center;
151
+ padding: 0;
152
  }
153
 
154
  .logo {
155
+ width: 82px;
156
+ height: 82px;
157
  display: block;
158
  object-fit: contain;
159
  background: transparent;
160
  }
161
 
 
162
  .hero {
163
  min-height: 0;
164
  display: flex;
 
166
  align-items: center;
167
  justify-content: center;
168
  text-align: center;
169
+ gap: clamp(30px, 5.5dvh, 54px);
170
+ padding: 0 0 clamp(10px, 2dvh, 18px);
171
  }
172
 
173
+ .title-frame {
174
+ position: relative;
175
+ display: inline-block;
176
+ margin: 0 auto;
177
+ padding: 0;
 
 
 
 
178
  }
179
 
180
  .script-title {
181
  margin: 0 auto;
182
  color: var(--blue);
183
  font-family: var(--font-script);
184
+ font-size: clamp(28px, 6.9vw, 42px);
185
  font-weight: 400;
186
+ line-height: 2.45;
187
+ letter-spacing: -.012em;
188
  text-wrap: balance;
189
+ max-width: 360px;
190
  }
191
 
192
+ .script-title span {
193
+ display: block;
 
 
 
 
 
 
 
 
194
  }
195
 
196
+ .script-title span + span {
197
+ margin-top: .02em;
 
 
 
 
 
 
 
198
  }
 
 
 
199
 
200
+ .script-title span:first-child { transform: translateX(-.02em); }
201
+ .script-title span:last-child { transform: translateX(.04em); }
202
+
203
  .form-block {
204
  width: 100%;
205
  max-width: 380px;
 
211
  justify-content: center;
212
  gap: 14px;
213
  margin: 0 0 16px;
214
+ color: var(--coral);
215
  font-size: 10px;
216
+ font-weight: 700;
217
  letter-spacing: .34em;
218
  text-transform: uppercase;
219
  }
220
+
221
  .field-label::before,
222
  .field-label::after {
223
  content: "";
224
  flex: 0 0 24px;
225
  height: 1px;
226
+ background: rgba(208, 138, 122, .58);
227
  }
228
 
229
+ .input-wrap { position: relative; }
 
 
230
 
231
  input {
232
  width: 100%;
233
  height: 56px;
234
+ border: 1px solid rgba(147, 150, 117, .30);
235
  outline: 0;
236
  border-radius: 999px;
237
  padding: 0 24px;
 
246
  transition: border-color .22s ease, box-shadow .22s ease, background .22s ease;
247
  }
248
 
249
+ input::placeholder {
250
+ color: rgba(143, 163, 179, .82);
251
+ font-weight: 400;
252
+ font-style: italic;
253
+ }
254
 
255
  input:focus {
256
  border-color: rgba(147, 150, 117, .55);
 
264
  .discover {
265
  position: relative;
266
  width: 100%;
267
+ height: 58px;
268
  margin-top: 14px;
269
  border: 0;
270
  border-radius: 999px;
 
274
  background: var(--blue);
275
  font-family: var(--font-main);
276
  font-size: 11.5px;
277
+ font-weight: 700;
278
  letter-spacing: .32em;
279
  text-transform: uppercase;
280
+ box-shadow: 0 8px 20px rgba(37, 75, 107, .17);
281
  transition: transform .18s ease, box-shadow .18s ease, background .18s ease;
282
  }
283
 
 
286
  background: var(--blue-deep);
287
  box-shadow: 0 10px 24px rgba(37, 75, 107, .18);
288
  }
289
+
290
  .discover:active {
291
  transform: translateY(1px);
292
  box-shadow: 0 4px 12px rgba(37, 75, 107, .12);
 
299
  transform: translateY(8px);
300
  transition: opacity .26s ease, transform .26s ease;
301
  }
302
+
303
  .message.show { opacity: 1; transform: translateY(0); }
304
 
305
  .message-card {
306
  border-radius: 14px;
307
  padding: 12px 14px;
308
  color: var(--ink);
309
+ background: rgba(255, 253, 248, .88);
310
  border: 1px solid var(--hair);
311
  text-align: center;
312
  font-size: 12.5px;
 
315
  letter-spacing: .005em;
316
  }
317
 
 
318
  .reveal {
319
  position: fixed;
320
  inset: 0;
 
324
  padding: max(28px, env(safe-area-inset-top)) 28px max(24px, env(safe-area-inset-bottom));
325
  color: var(--blue);
326
  background-color: var(--cream);
327
+ background-image: none;
 
 
 
328
  opacity: 0;
329
  visibility: hidden;
330
  pointer-events: none;
 
332
  transition: opacity .36s ease, transform .36s ease, visibility 0s linear .36s;
333
  overflow: hidden;
334
  }
335
+
336
+ .reveal::before {
337
+ content: "";
338
+ position: absolute;
339
+ inset: 0;
340
+ background-image: var(--pine-bg);
341
+ background-repeat: no-repeat;
342
+ background-position: center 50%;
343
+ background-size: min(112vw, 620px) auto;
344
+ opacity: .55;
345
+ mix-blend-mode: multiply;
346
+ pointer-events: none;
347
+ z-index: 0;
348
+ }
349
+
350
  .reveal::after {
351
  content: "";
352
+ position: absolute;
353
  inset: 0;
354
  background-image: var(--grain);
355
  background-size: 220px 220px;
356
+ opacity: .18;
357
  mix-blend-mode: multiply;
358
  pointer-events: none;
359
+ z-index: 0;
360
  }
361
+
362
  .reveal.show {
363
  opacity: 1;
364
  visibility: visible;
 
367
  transition: opacity .36s ease, transform .36s ease, visibility 0s;
368
  }
369
 
370
+ .reveal-frame { z-index: 1; }
 
 
 
 
 
 
 
371
 
372
  .reveal-page {
373
  position: relative;
 
383
 
384
  .reveal.show .reveal-step { animation: rise .8s cubic-bezier(.2,.7,.2,1) both; }
385
  .reveal.show .step-1 { animation-delay: .08s; }
386
+ .reveal.show .step-2 { animation-delay: .20s; }
387
+ .reveal.show .step-3 { animation-delay: .32s; }
388
+ .reveal.show .step-4 { animation-delay: .46s; }
389
+ .reveal.show .step-5 { animation-delay: .60s; }
390
+ .reveal.show .step-6 { animation-delay: .74s; }
391
 
392
  .hello {
393
  margin: 0 0 6px;
394
  color: var(--ink-soft);
395
  font-family: var(--font-serif);
396
  font-style: italic;
397
+ font-size: clamp(20px, 5vw, 28px);
398
  font-weight: 400;
399
  letter-spacing: .005em;
400
  }
401
 
402
  .hello-divider {
403
+ width: 38px;
404
  height: 1px;
405
+ margin: 0 auto clamp(20px, 3.7dvh, 30px);
406
+ background: rgba(208, 138, 122, .62);
407
  position: relative;
408
  }
409
+
410
  .hello-divider::before {
411
  content: "";
412
  position: absolute;
413
  left: 50%;
414
  top: 50%;
415
+ width: 7px;
416
+ height: 7px;
417
  border-radius: 999px;
418
  background: var(--coral);
419
  transform: translate(-50%, -50%);
420
  }
421
 
422
  .table-kicker {
423
+ margin: 0 0 14px;
424
  color: var(--coral-deep);
425
+ font-size: 12px;
426
+ font-weight: 800;
427
  letter-spacing: .42em;
428
  text-transform: uppercase;
429
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
430
 
431
  .table-name-wrap {
432
  position: relative;
433
  display: inline-block;
434
  margin: 0 auto;
435
+ padding: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436
  }
437
 
438
  .table-name {
439
  margin: 0 auto;
440
+ max-width: 12ch;
441
  color: var(--blue);
442
  font-family: var(--font-script);
443
+ font-size: clamp(34px, 9.5vw, 58px);
444
  font-weight: 400;
445
+ line-height: 1.14;
446
+ letter-spacing: -.02em;
447
  text-wrap: balance;
448
  }
449
+
450
  .table-name[data-long="true"] {
451
+ max-width: 13ch;
452
+ font-size: clamp(30px, 8.2vw, 50px);
453
+ }
454
+
455
+ .table-art {
456
+ display: block;
457
+ max-width: min(86vw, 430px);
458
+ max-height: 230px;
459
+ margin: 0 auto;
460
+ object-fit: contain;
461
+ }
462
+
463
+ .table-art[hidden],
464
+ .table-name[hidden] {
465
+ display: none !important;
466
  }
467
 
468
  .reveal-divider {
 
470
  align-items: center;
471
  justify-content: center;
472
  gap: 10px;
473
+ margin: clamp(18px, 3.5dvh, 28px) auto 0;
474
  }
475
+
476
  .reveal-divider::before,
477
  .reveal-divider::after {
478
  content: "";
479
+ width: 42px;
480
  height: 1px;
481
  background: var(--olive);
482
  opacity: .55;
483
  }
484
+
485
  .reveal-divider .dot {
486
+ width: 8px;
487
+ height: 8px;
488
  border-radius: 999px;
489
  background: var(--coral);
490
+ box-shadow: 0 0 0 5px rgba(208, 138, 122, .16);
491
  }
492
 
493
+ .companions {
494
  margin: 16px auto 0;
495
+ max-width: 34ch;
496
  color: var(--ink-soft);
497
  font-family: var(--font-serif);
498
  font-style: italic;
499
+ text-align: center;
500
+ }
501
+
502
+ .companions-title {
503
+ margin: 0 0 9px;
504
+ font-size: clamp(16px, 4vw, 20px);
505
+ font-weight: 400;
506
+ line-height: 1.35;
507
+ }
508
+
509
+ .companions-list {
510
+ list-style: none;
511
+ padding: 0;
512
+ margin: 0;
513
+ }
514
+
515
+ .companions-list li {
516
+ margin: 5px 0;
517
+ font-size: clamp(16px, 4vw, 20px);
518
  font-weight: 400;
519
+ line-height: 1.38;
520
  letter-spacing: .003em;
521
  }
522
 
523
+ .cheers {
524
+ margin: 15px auto 0;
525
+ color: var(--coral);
526
+ font-family: var(--font-serif);
527
+ font-style: italic;
528
+ font-size: clamp(16px, 4vw, 20px);
529
+ font-weight: 500;
530
+ line-height: 1.35;
531
+ }
532
+
533
  .again {
534
  align-self: center;
535
+ height: 52px;
536
+ margin-top: clamp(24px, 4.6dvh, 36px);
537
+ border: 1px solid rgba(37, 75, 107, .14);
538
  border-radius: 999px;
539
+ padding: 0 28px;
540
  color: var(--blue);
541
  background: rgba(255, 253, 248, .92);
542
  box-shadow: 0 12px 26px rgba(37, 75, 107, .07);
543
  font-family: var(--font-main);
544
  font-size: 11px;
545
+ font-weight: 800;
546
  letter-spacing: .26em;
547
  text-transform: uppercase;
548
  cursor: pointer;
549
  transition: transform .18s ease, box-shadow .18s ease, background .18s ease;
550
  }
551
+
552
  .again:hover {
553
  transform: translateY(-1px);
554
  background: var(--paper);
 
573
  pointer-events: none;
574
  }
575
 
576
+ @media (max-height: 760px) {
577
+ .logo { width: 120px; height: 120px; }
578
+ .script-title { font-size: clamp(27px, 6.6vw, 40px); }
579
+ .hero { gap: clamp(28px, 5dvh, 44px); }
580
+ .hello { font-size: clamp(18px, 4.4vw, 24px); }
581
+ .table-kicker { margin-bottom: 10px; }
582
+ .table-name { font-size: clamp(32px, 8.6vw, 52px); }
583
+ .table-name[data-long="true"] { font-size: clamp(28px, 7.5vw, 46px); }
584
+ .reveal-divider { margin-top: 16px; }
585
+ .companions { margin-top: 12px; }
586
+ .companions-title { margin-bottom: 6px; }
587
+ .companions-list li { margin: 3px 0; font-size: clamp(14px, 3.6vw, 17px); line-height: 1.34; }
588
+ .cheers { margin-top: 10px; font-size: clamp(15px, 3.8vw, 18px); }
589
+ .again { height: 48px; margin-top: 20px; }
590
  }
591
 
592
  @media (max-width: 380px) {
593
+ .frame,
594
+ .reveal-frame { inset: 12px; border-radius: 24px; }
595
  .page { padding-left: 22px; padding-right: 22px; }
596
+ .logo { width: 68px; height: 68px; }
597
+ .script-title { font-size: clamp(26px, 7.8vw, 38px); }
598
+ input,
599
+ .discover { height: 54px; }
600
+ .field-label { letter-spacing: .26em; }
601
+ .field-label::before,
602
+ .field-label::after { flex-basis: 18px; }
603
+ .table-name { font-size: clamp(30px, 9vw, 50px); }
604
+ .table-name[data-long="true"] { font-size: clamp(26px, 7.8vw, 44px); }
605
+ .again { padding-inline: 22px; letter-spacing: .20em; }
606
  }
607
 
608
  @media (prefers-reduced-motion: reduce) {
609
+ .anim,
610
+ .reveal.show .reveal-step {
611
+ animation: none;
612
+ opacity: 1;
613
+ transform: none;
614
+ }
615
  }
616
  </style>
 
617
  </head>
618
+
619
  <body>
620
  <canvas id="confetti"></canvas>
 
 
 
621
  <div class="frame" aria-hidden="true"></div>
622
 
623
  <main class="screen">
624
  <div class="page">
625
  <div class="top-mark anim anim-1">
626
+ <img
627
+ class="logo"
628
+ src="./logo.png"
629
+ alt="Logo matrimonio"
630
+ onerror="this.onerror=null;this.src='./assets/logo.png';"
631
+ />
632
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
633
 
634
+ <section class="hero" aria-label="Trova la tua cala">
635
+ <div class="title-frame anim anim-3">
636
+ <h1 class="script-title" aria-label="Trova la tua Cala">
637
+ <span>Trova</span>
638
+ <span>la tua Cala</span>
639
+ </h1>
 
 
 
 
 
 
 
 
 
 
 
 
 
640
  </div>
 
 
641
 
642
+ <div class="form-block anim anim-4">
643
+ <label class="field-label" for="guestName">NOME E COGNOME</label>
644
+ <div class="input-wrap">
645
+ <input id="guestName" autocomplete="name" inputmode="text" placeholder="Es. Gaia Pollastrini" />
646
+ </div>
647
+ <button class="discover" id="discover" type="button">Scopri il tavolo</button>
648
+ <div id="message" class="message" aria-live="polite"></div>
649
+ <div id="status" class="status" aria-live="polite"></div>
650
+ </div>
651
+ </section>
652
  </div>
 
 
 
653
  </main>
654
 
 
 
655
  <section id="reveal" class="reveal" aria-live="assertive" aria-hidden="true">
656
  <div class="reveal-frame" aria-hidden="true"></div>
657
 
658
+ <div class="reveal-page">
659
+ <p class="hello reveal-step step-1" id="hello"></p>
660
+ <div class="hello-divider reveal-step step-2" aria-hidden="true"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
 
662
+ <p class="table-kicker reveal-step step-3">IL TUO TAVOLO È</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
 
664
+ <div class="table-name-wrap reveal-step step-4">
665
+ <h2 class="table-name" id="tableName"></h2>
666
+ <img class="table-art" id="tableArt" alt="Nome tavolo" hidden />
667
+ </div>
668
+
669
+ <div class="reveal-divider reveal-step step-5" aria-hidden="true">
670
+ <span class="dot"></span>
671
+ </div>
672
 
673
+ <div class="companions reveal-step step-5" id="companions">
674
+ <p class="companions-title">Sarai al tavolo con:</p>
675
+ <ul class="companions-list" id="companionsList"></ul>
676
+ <p class="cheers">Brinda, sorridi e goditi la serata!</p>
677
+ </div>
678
 
679
+ <button class="again reveal-step step-6" id="again" type="button">Cerca un altro nome</button>
680
+ </div>
681
  </section>
682
 
683
  <script>
684
  const DATA_URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vSlb-0cSIFaGN_BWrn_S9tQoQsdqGH7qYRYQGC3_3wRB-rGzwZoPoxIBNr9l6NmxW9Ont82YKIdGKIR/pub?output=csv";
685
 
686
+ /*
687
+ OPZIONALE, se poi vuoi usare immagini Canva per alcuni nomi dei tavoli:
688
+ 1. crea la cartella ./assets/tables/
689
+ 2. carica i PNG
690
+ 3. aggiungi qui una riga con chiave normalizzata.
691
+
692
+ Esempio:
693
+ const TABLE_IMAGE_BY_NAME = {
694
+ "cala degli inglesi": "./assets/tables/cala-degli-inglesi.png"
695
+ };
696
+
697
+ Se lasci l'oggetto vuoto, il nome tavolo viene scritto col font Amsterdam 1.
698
+ */
699
+ const TABLE_IMAGE_BY_NAME = {};
700
+
701
+ // false = nella lista mostra solo le altre persone del tavolo.
702
+ // true = include anche la persona cercata.
703
+ const INCLUDE_SELF_IN_TABLE_LIST = false;
704
+
705
  const els = {
706
  input: document.getElementById("guestName"),
707
  discover: document.getElementById("discover"),
 
710
  reveal: document.getElementById("reveal"),
711
  hello: document.getElementById("hello"),
712
  tableName: document.getElementById("tableName"),
713
+ tableArt: document.getElementById("tableArt"),
714
+ companionsList: document.getElementById("companionsList"),
715
  again: document.getElementById("again"),
716
  canvas: document.getElementById("confetti")
717
  };
718
 
719
  let guests = [];
720
+ let guestsLoaded = false;
721
+ let guestsLoadingPromise = null;
722
  let hideTimer = null;
723
 
724
  function normalizeName(value) {
 
735
  return String(fullName || "").trim().split(/\s+/)[0] || "ospite";
736
  }
737
 
738
+ function escapeHTML(value) {
739
+ return String(value)
740
+ .replaceAll("&", "&amp;")
741
+ .replaceAll("<", "&lt;")
742
+ .replaceAll(">", "&gt;")
743
+ .replaceAll('"', "&quot;")
744
+ .replaceAll("'", "&#039;");
745
+ }
746
+
747
  function parseCSV(text) {
748
  const rows = [];
749
  let row = [];
 
786
  );
787
 
788
  const start = headerIndex >= 0 ? headerIndex + 1 : 1;
789
+
790
  return rows
791
  .slice(start)
792
  .map(row => ({
 
806
  } catch (error) {
807
  guests = [];
808
  els.status.textContent = "Lista invitati non disponibile.";
809
+ } finally {
810
+ guestsLoaded = true;
811
  }
812
  }
813
 
 
822
  return partials.length === 1 ? partials[0] : null;
823
  }
824
 
825
+ function tableMatesFor(guest) {
826
+ const tableKey = normalizeName(guest.tableName);
827
+
828
+ return guests
829
+ .filter(person => normalizeName(person.tableName) === tableKey)
830
+ .filter(person => INCLUDE_SELF_IN_TABLE_LIST || person.key !== guest.key)
831
+ .map(person => person.fullName)
832
+ .filter(Boolean);
833
+ }
834
+
835
+ function renderCompanions(guest) {
836
+ const mates = tableMatesFor(guest);
837
+
838
+ if (!mates.length) {
839
+ els.companionsList.innerHTML = `<li>${escapeHTML("Tavolo riservato solo per te.")}</li>`;
840
+ return;
841
+ }
842
+
843
+ els.companionsList.innerHTML = mates
844
+ .map(name => `<li>${escapeHTML(name)}</li>`)
845
+ .join("");
846
+ }
847
+
848
+ function renderTableName(guest) {
849
+ const tableKey = normalizeName(guest.tableName);
850
+ const imagePath = TABLE_IMAGE_BY_NAME[tableKey];
851
+
852
+ if (imagePath) {
853
+ els.tableName.hidden = true;
854
+ els.tableArt.hidden = false;
855
+ els.tableArt.src = imagePath;
856
+ els.tableArt.alt = guest.tableName;
857
+ return;
858
+ }
859
+
860
+ els.tableArt.hidden = true;
861
+ els.tableArt.removeAttribute("src");
862
+ els.tableName.hidden = false;
863
+ els.tableName.textContent = guest.tableName;
864
+ els.tableName.dataset.long = guest.tableName.length > 14 ? "true" : "false";
865
+ }
866
+
867
  function showMessage(text) {
868
  els.message.classList.remove("show");
869
  window.setTimeout(() => {
 
874
 
875
  function showReveal(guest) {
876
  clearTimeout(hideTimer);
877
+
878
  els.hello.textContent = `Ciao, ${firstName(guest.fullName)}`;
879
+ renderTableName(guest);
880
+ renderCompanions(guest);
881
+
882
  document.body.classList.add("reveal-open");
883
  els.reveal.classList.add("show");
884
  els.reveal.setAttribute("aria-hidden", "false");
885
+
886
  celebrate();
887
  }
888
 
 
895
  hideTimer = window.setTimeout(() => {
896
  els.input.value = "";
897
  els.message.classList.remove("show");
898
+ els.input.focus({ preventScroll: true });
899
  }, 360);
900
  }
901
 
902
+ async function discoverTable() {
903
  const typed = els.input.value;
904
  const normalized = normalizeName(typed);
905
 
 
908
  return;
909
  }
910
 
911
+ if (!guestsLoaded) {
912
+ showMessage("Sto caricando la lista invitati... riprova tra un secondo.");
913
+ if (!guestsLoadingPromise) guestsLoadingPromise = loadGuests();
914
+ await guestsLoadingPromise;
915
+ }
916
+
917
  if (!guests.length) {
918
  showMessage("Lista invitati non ancora disponibile. Riprova tra qualche secondo.");
919
  return;
920
  }
921
 
922
  const guest = findGuest(typed);
923
+
924
  if (!guest) {
925
  showMessage("Nome non trovato. Controlla di aver scritto nome e cognome corretti.");
926
  return;
 
929
  showReveal(guest);
930
  }
931
 
 
 
 
 
 
 
 
 
 
932
  els.discover.addEventListener("click", discoverTable);
933
+
934
  els.again.addEventListener("click", hideReveal);
935
+
936
  els.input.addEventListener("keydown", event => {
937
  if (event.key === "Enter") discoverTable();
938
  });
939
+
940
  window.addEventListener("keydown", event => {
941
  if (event.key === "Escape" && els.reveal.classList.contains("show")) hideReveal();
942
  });
943
 
944
+ /* ============= petali ============= */
945
  const ctx = els.canvas.getContext("2d");
946
  let petals = [];
947
  let rafId = null;
 
955
 
956
  function celebrate() {
957
  resizeCanvas();
958
+
959
  const colors = ["#EDD3CD", "#D08A7A", "#C2C5B2", "#8FA3B3", "#F3EFE9", "#EDD3CD", "#D08A7A"];
960
  const w = window.innerWidth;
961
 
962
+ petals = Array.from({ length: 56 }, () => ({
963
+ x: Math.random() * w,
964
+ y: -20 - Math.random() * 200,
965
+ rx: 5 + Math.random() * 5,
966
+ ry: 2.4 + Math.random() * 1.6,
967
+ vy: 1.0 + Math.random() * 1.6,
968
+ vx: (Math.random() - .5) * .6,
969
+ rotation: Math.random() * Math.PI,
970
+ spin: (Math.random() - .5) * .04,
971
+ sway: Math.random() * Math.PI * 2,
972
+ swaySpeed: .015 + Math.random() * .02,
973
+ swayAmp: .4 + Math.random() * .9,
974
+ color: colors[Math.floor(Math.random() * colors.length)],
975
+ alpha: .75 + Math.random() * .25,
976
+ life: 360 + Math.random() * 200
977
+ }));
 
 
978
 
979
  if (rafId) cancelAnimationFrame(rafId);
980
  animatePetals();
 
989
 
990
  function animatePetals() {
991
  ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
992
+
993
  const h = window.innerHeight;
994
 
995
  petals.forEach(p => {
 
1021
  }
1022
 
1023
  window.addEventListener("resize", resizeCanvas);
 
 
1024
 
1025
+ guestsLoadingPromise = loadGuests();
1026
+ </script>
1027
  </body>
1028
  </html>