first commit. all the code has been initialized by Codex

This commit is contained in:
bisco
2026-03-25 18:57:16 +01:00
parent 6c36825a93
commit 8f580745d8
5 changed files with 971 additions and 2 deletions

View File

@ -1,3 +1,54 @@
# gianca-s-event # Gianca 40 - Landing RSVP per Netlify
This is a netlify app to book the event's people. Questa cartella contiene una landing page statica pronta per Netlify.
## File inclusi
- `index.html` -> variante principale con tag bianco + icona evento
- `index-righe.html` -> variante alternativa con tag bianco + righe editoriali
- `style.css`
- `script.js`
- `assets/gianca-photo.jpg`
- `grazie/index.html` -> pagina di conferma
## Come pubblicarla su Netlify
### Metodo rapido
1. Accedi a Netlify
2. Crea un nuovo progetto con deploy manuale
3. Trascina **questa cartella** nel deploy dropzone
4. Attendi la pubblicazione
### Metodo con Git
1. Metti questi file in un repository
2. Importa il repository su Netlify
3. Nessun build command necessario
4. Publish directory: lascia la root del progetto
## Come scegliere la variante
- Se vuoi usare la versione con icona, lascia `index.html` così com'è
- Se vuoi usare la versione con righe, rinomina `index-righe.html` in `index.html` prima del deploy
oppure sostituisci il contenuto dell'`index.html` con quello dell'altra variante
## Note sul form
Il form è già impostato per Netlify Forms.
Le submission includono:
- Nome e cognome
- Presenza sì/no
- Partner sì/no
- Numero bambini
- Note
## Personalizzazioni rapide
- Titolo principale: cerca `Hey, ci sono i 40 anni del Gianca!`
- Data / luogo / orario: modifica il blocco `event-tag`
- Testi introduttivi: sezione `intro`
- Colori: variabili in cima a `style.css`
## Dominio custom
Dopo il deploy puoi collegare un dominio personalizzato dal pannello Netlify.
## Aggiornamenti v2
- Campo nome rinominato in `name` per rendere più chiara la summary delle submission in Netlify.
- Messaggio di successo sostituito con popup grafico e pulsante `OK`.

BIN
assets/gianca-photo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

185
index.html Normal file
View File

@ -0,0 +1,185 @@
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hey, ci sono i 40 anni del Gianca!</title>
<meta name="description" content="Landing RSVP per i 40 anni del Gianca - 25 aprile 2026, Lost Paradise Beach Club, Bacoli." />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Martel+Sans:wght@400;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css" />
</head>
<body class="theme-icon">
<div class="page-shell">
<header class="hero">
<img class="hero__image" src="assets/gianca-photo.jpg" alt="Giancarlo in posa sulla spiaggia" />
<div class="hero__overlay"></div>
<div class="hero__content container">
<!-- <p class="eyebrow">Save the date</p> -->
<h1 class="hero__title">Hey, ci sono i 40 anni del Gianca!</h1>
<div class="hero__underline" aria-hidden="true"></div>
<br />
<div class="event-tag__date">Save the date</div>
<!-- <div class="event-tag event-tag--icon" aria-label="Informazioni evento">
<div class="event-tag__icon">
<span class="party-popper" aria-hidden="true"></span>
</div>
<div class="event-tag__content">
<div class="event-tag__date">25 aprile 2026</div>
<div class="event-tag__place">Lost Paradise Beach Club</div>
<div class="event-tag__divider"></div>
<div class="event-tag__meta">Bacoli • ore 12.00</div>
</div>
</div> -->
</div>
</header>
<main>
<section class="section intro">
<div class="container intro__grid">
<div class="intro__text">
<p class="section-kicker">Un pranzo vista mare</p>
<h2>Una giornata semplice, bella, piena di affetto.</h2>
<p>
Il Gianca festeggia i suoi 40 anni e ha bisogno di sapere chi ci sarà, così da organizzare al meglio tavoli, spazi e tutto il resto.
</p>
<p>
Compila il modulo qui sotto: bastano pochi secondi per confermare la presenza, indicare l'eventuale partner e il numero di bambini.
</p>
</div>
<div class="details-card">
<div class="details-card__item">
<span class="details-card__label">Quando</span>
<strong>Sabato 25 aprile 2026</strong>
</div>
<div class="details-card__item">
<span class="details-card__label">Dove</span>
<strong>Lost Paradise Beach Club</strong>
<span>Bacoli</span>
</div>
<div class="details-card__item">
<span class="details-card__label">Orario</span>
<strong>Dalle ore 12.00</strong>
</div>
</div>
</div>
</section>
<section class="section rsvp" id="rsvp">
<div class="container rsvp__grid">
<div class="rsvp__copy">
<p class="section-kicker">RSVP</p>
<h2>Conferma la tua presenza</h2>
<p>
Grazie: la tua risposta ci aiuta davvero a organizzare tutto al meglio.
</p>
<ul class="rsvp__notes">
<li>Se non puoi esserci, seleziona semplicemente “No”.</li>
<li>Se vieni, indica se sarai con partner.</li>
<li>Se ci sono bambini, scrivi quanti.</li>
</ul>
</div>
<div class="form-card">
<form
name="rsvp-gianca-40"
method="POST"
data-netlify="true"
netlify-honeypot="bot-field"
action="/grazie/"
id="rsvp-form"
novalidate
>
<input type="hidden" name="form-name" value="rsvp-gianca-40" />
<p class="hidden-field">
<label>Non compilare questo campo se sei umano: <input name="bot-field" /></label>
</p>
<div class="form-row">
<label for="fullName">Nome e cognome</label>
<input id="fullName" name="name" type="text" autocomplete="name" required />
</div>
<div class="form-row">
<span class="form-label">Confermi la tua presenza?</span>
<div class="choice-group" role="radiogroup">
<label class="choice">
<input type="radio" name="attendance" value="si" required checked />
<span>Sì, ci sarò</span>
</label>
<label class="choice">
<input type="radio" name="attendance" value="no" required />
<span>No, non riesco</span>
</label>
</div>
</div>
<div id="attendance-fields">
<div class="form-row">
<span class="form-label">Verrai con partner?</span>
<div class="choice-group">
<label class="choice">
<input type="radio" name="partner" value="si" checked />
<span></span>
</label>
<label class="choice">
<input type="radio" name="partner" value="no" />
<span>No</span>
</label>
</div>
</div>
<div class="form-row">
<label for="childrenCount">Quanti bambini verranno con te?</label>
<select id="childrenCount" name="childrenCount">
<option value="0" selected>0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4+">4 o più</option>
</select>
</div>
<div class="form-row">
<label for="notes">Note utili</label>
<textarea id="notes" name="notes" rows="4" placeholder="Allergie, esigenze particolari, eventuali dettagli utili"></textarea>
</div>
</div>
<button class="button" type="submit">Invia conferma</button>
<p class="form-status" id="form-status" aria-live="polite"></p>
</form>
</div>
</div>
</section>
</main>
<div class="success-modal" id="success-modal" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="success-title">
<div class="success-modal__backdrop" data-close-modal></div>
<div class="success-modal__dialog" role="document">
<div class="success-tag" aria-live="polite">
<!-- <div class="success-tag__line" aria-hidden="true"></div> -->
<div class="success-tag__content">
<p class="success-tag__kicker">Risposta inviata</p>
<h3 class="success-tag__title" id="success-title">Grazie per la tua risposta</h3>
<p class="success-tag__text">La conferma è stata registrata correttamente. A presto!</p>
<button class="button success-tag__button" type="button" id="success-ok">OK</button>
</div>
</div>
</div>
</div>
<footer class="footer">
<div class="container footer__inner">
<p>Ci vediamo a Bacoli.</p>
</div>
</footer>
</div>
<script src="script.js"></script>
</body>
</html>

82
script.js Normal file
View File

@ -0,0 +1,82 @@
const form = document.getElementById('rsvp-form');
const statusNode = document.getElementById('form-status');
const attendanceFields = document.getElementById('attendance-fields');
const attendanceInputs = Array.from(document.querySelectorAll('input[name="attendance"]'));
const successModal = document.getElementById('success-modal');
const successOkButton = document.getElementById('success-ok');
const closeModalNodes = Array.from(document.querySelectorAll('[data-close-modal]'));
function updateAttendanceFields() {
const selected = document.querySelector('input[name="attendance"]:checked');
const attending = selected?.value === 'si';
attendanceFields.classList.toggle('attendance-fields--hidden', !attending);
attendanceFields.querySelectorAll('input, select, textarea').forEach((field) => {
field.disabled = !attending;
if (!attending && field.tagName === 'TEXTAREA') field.value = '';
if (!attending && field.tagName === 'SELECT') field.value = '0';
});
}
function openSuccessModal() {
if (!successModal) return;
successModal.classList.add('success-modal--open');
successModal.setAttribute('aria-hidden', 'false');
document.body.classList.add('modal-open');
successOkButton?.focus();
}
function closeSuccessModal() {
if (!successModal) return;
successModal.classList.remove('success-modal--open');
successModal.setAttribute('aria-hidden', 'true');
document.body.classList.remove('modal-open');
}
attendanceInputs.forEach((input) => input.addEventListener('change', updateAttendanceFields));
closeModalNodes.forEach((node) => node.addEventListener('click', closeSuccessModal));
successOkButton?.addEventListener('click', closeSuccessModal);
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && successModal?.classList.contains('success-modal--open')) {
closeSuccessModal();
}
});
updateAttendanceFields();
function encode(data) {
return new URLSearchParams(data).toString();
}
form?.addEventListener('submit', async (event) => {
event.preventDefault();
if (!form.reportValidity()) return;
const submitButton = form.querySelector('button[type="submit"]');
const formData = new FormData(form);
submitButton.disabled = true;
statusNode.textContent = 'Invio in corso...';
try {
const response = await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: encode(formData)
});
if (!response.ok) {
throw new Error('Submission failed');
}
form.reset();
updateAttendanceFields();
statusNode.textContent = '';
openSuccessModal();
} catch (error) {
statusNode.textContent = 'Cè stato un problema nellinvio. Riprova tra poco.';
} finally {
submitButton.disabled = false;
}
});

651
style.css Normal file
View File

@ -0,0 +1,651 @@
:root {
--bg: #f5f7fb;
--paper: rgba(255, 255, 255, 0.9);
--paper-strong: rgba(255, 255, 255, 0.95);
--ink: #1e2530;
--ink-soft: #5c6573;
--sea: #1e578a;
--sea-dark: #153c60;
--line: rgba(44, 94, 145, 0.22);
--shadow: 0 24px 70px rgba(18, 26, 38, 0.18);
--radius-xl: 30px;
--radius-lg: 24px;
--container: 1180px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background:
radial-gradient(circle at top, rgba(63, 126, 180, 0.12), transparent 35%),
linear-gradient(180deg, #f4f7fa 0%, #eef2f7 100%);
color: var(--ink);
font-family: "Martel Sans", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
img {
max-width: 100%;
display: block;
}
.container {
width: min(calc(100% - 2rem), var(--container));
margin-inline: auto;
}
.section {
padding: 5.5rem 0;
}
.section-kicker,
.eyebrow {
margin: 0 0 0.85rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: rgba(255,255,255,0.82);
font-size: 0.84rem;
font-weight: 700;
}
.section-kicker {
color: var(--sea);
}
.hero {
position: relative;
min-height: 100svh;
overflow: clip;
display: grid;
align-items: center;
isolation: isolate;
}
.hero__image,
.hero__overlay {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.hero__image {
object-fit: cover;
}
.hero__overlay {
background:
linear-gradient(180deg, rgba(4, 10, 18, 0.52) 0%, rgba(4, 10, 18, 0.15) 30%, rgba(4, 10, 18, 0.05) 55%, rgba(4, 10, 18, 0.38) 100%),
linear-gradient(180deg, rgba(18, 49, 78, 0.24) 0%, rgba(18, 49, 78, 0.08) 100%);
}
.hero__content {
position: relative;
z-index: 2;
display: grid;
justify-items: center;
gap: 1rem;
padding: 3rem 0 6rem;
}
.hero__title {
margin: 0;
max-width: 14ch;
text-align: center;
color: #fff;
font-size: clamp(2.8rem, 6vw, 6.6rem);
line-height: 0.96;
font-weight: 800;
letter-spacing: -0.03em;
text-wrap: balance;
-webkit-text-stroke: 10px var(--sea-dark);
paint-order: stroke fill;
text-shadow: 0 12px 30px rgba(5, 11, 18, 0.18);
}
.hero__underline {
width: min(78ch, 92%);
max-width: 960px;
height: 14px;
border-radius: 999px;
background: var(--sea-dark);
position: relative;
margin-top: 0.8rem;
}
.hero__underline::after {
content: "";
position: absolute;
inset: 2px;
border-radius: inherit;
background: #fff;
}
.event-tag {
width: min(100%, 520px);
margin-top: 1.75rem;
border-radius: var(--radius-xl);
background: rgba(255, 255, 255, 0.84);
border: 1px solid rgba(255, 255, 255, 0.72);
box-shadow: var(--shadow);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
color: var(--ink);
overflow: hidden;
}
.event-tag--icon {
display: grid;
grid-template-columns: auto 1fr;
align-items: stretch;
}
.event-tag--lines {
display: grid;
grid-template-columns: 16px 1fr;
align-items: stretch;
}
.event-tag__line--vertical {
width: 3px;
margin: 1.75rem 0 1.75rem 1.6rem;
border-radius: 999px;
background: linear-gradient(180deg, rgba(30,87,138,0.86), rgba(30,87,138,0.36));
}
.event-tag__icon {
width: 108px;
display: grid;
place-items: center;
}
.party-popper {
position: relative;
display: inline-block;
width: 62px;
height: 62px;
border-radius: 50%;
background: linear-gradient(180deg, var(--sea) 0%, var(--sea-dark) 100%);
box-shadow: 0 12px 24px rgba(21, 60, 96, 0.22);
}
.party-popper::before {
content: "";
position: absolute;
left: 22px;
top: 28px;
width: 22px;
height: 18px;
background: #fff;
clip-path: polygon(0 58%, 100% 0, 68% 100%);
border-radius: 2px;
}
.party-popper::after {
content: "";
position: absolute;
left: 34px;
top: 13px;
width: 20px;
height: 20px;
border-top: 3px solid #fff;
border-right: 3px solid #fff;
transform: rotate(28deg);
border-radius: 50%;
}
.event-tag__content {
padding: 1.6rem 1.75rem 1.5rem 0.4rem;
}
.theme-lines .event-tag__content {
padding-left: 1.1rem;
}
.event-tag__date {
font-size: clamp(1.55rem, 3vw, 2.25rem);
font-weight: 800;
letter-spacing: -0.02em;
}
.event-tag__place,
.event-tag__meta {
font-size: clamp(1rem, 2vw, 1.22rem);
font-weight: 600;
}
.event-tag__place {
margin-top: 0.45rem;
}
.event-tag__divider {
width: 100%;
height: 2px;
border-radius: 999px;
background: var(--line);
margin: 0.95rem 0 0.8rem;
}
.intro__grid,
.rsvp__grid {
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: 2rem;
align-items: start;
}
.intro__text h2,
.rsvp__copy h2 {
margin: 0 0 1rem;
font-size: clamp(2rem, 4vw, 3.3rem);
line-height: 1;
letter-spacing: -0.03em;
}
.intro__text p,
.rsvp__copy p,
.rsvp__notes li {
color: var(--ink-soft);
font-size: 1.02rem;
line-height: 1.75;
}
.rsvp__notes {
margin: 1rem 0 0;
padding-left: 1.2rem;
}
.details-card,
.form-card {
background: var(--paper-strong);
border: 1px solid rgba(255,255,255,0.88);
border-radius: var(--radius-xl);
box-shadow: var(--shadow);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.details-card {
padding: 1.65rem;
display: grid;
gap: 1rem;
}
.details-card__item {
padding: 1rem 1.1rem;
border-radius: 22px;
background: rgba(244, 248, 252, 0.88);
border: 1px solid rgba(40, 89, 138, 0.08);
}
.details-card__item strong,
.details-card__item span {
display: block;
}
.details-card__label {
margin-bottom: 0.35rem;
color: var(--sea);
font-size: 0.78rem;
font-weight: 800;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.form-card {
padding: 1.8rem;
}
form {
display: grid;
gap: 1.15rem;
}
.hidden-field {
display: none !important;
}
.form-row {
display: grid;
gap: 0.55rem;
}
label,
.form-label {
font-size: 0.95rem;
font-weight: 700;
color: var(--ink);
}
input[type="text"],
select,
textarea {
width: 100%;
border: 1px solid rgba(30, 87, 138, 0.16);
border-radius: 18px;
padding: 0.95rem 1rem;
font: inherit;
color: var(--ink);
background: #fff;
transition: border-color .18s ease, box-shadow .18s ease;
}
input[type="text"]:focus,
select:focus,
textarea:focus {
outline: none;
border-color: rgba(30, 87, 138, 0.58);
box-shadow: 0 0 0 4px rgba(30, 87, 138, 0.12);
}
.choice-group {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.choice {
position: relative;
display: inline-flex;
align-items: center;
}
.choice input {
position: absolute;
inset: 0;
opacity: 0;
}
.choice span {
display: inline-flex;
align-items: center;
min-height: 48px;
padding: 0.75rem 1.05rem;
border-radius: 999px;
border: 1px solid rgba(30, 87, 138, 0.16);
background: #fff;
color: var(--ink);
font-weight: 600;
cursor: pointer;
transition: all .18s ease;
}
.choice input:checked + span {
background: rgba(30, 87, 138, 0.1);
border-color: rgba(30, 87, 138, 0.58);
color: var(--sea-dark);
}
.attendance-fields--hidden {
display: none !important;
}
.button {
appearance: none;
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 54px;
width: 100%;
border-radius: 999px;
padding: 0.9rem 1.3rem;
font: inherit;
font-weight: 800;
letter-spacing: 0.01em;
color: #fff;
text-decoration: none;
cursor: pointer;
background: linear-gradient(180deg, var(--sea) 0%, var(--sea-dark) 100%);
box-shadow: 0 16px 30px rgba(21, 60, 96, 0.18);
transition: transform .18s ease, box-shadow .18s ease, opacity .18s ease;
}
.button:hover {
transform: translateY(-1px);
box-shadow: 0 20px 34px rgba(21, 60, 96, 0.22);
}
.button:disabled {
opacity: 0.72;
cursor: wait;
}
.form-status {
min-height: 1.4rem;
margin: 0;
font-size: 0.95rem;
color: var(--sea-dark);
}
.footer {
padding: 0 0 2rem;
}
.footer__inner {
text-align: center;
color: var(--ink-soft);
font-size: 0.94rem;
}
.thankyou-page {
min-height: 100svh;
display: grid;
place-items: center;
padding: 1.25rem;
}
.thankyou__card {
width: min(100%, 620px);
padding: 2rem;
border-radius: var(--radius-xl);
background: var(--paper-strong);
border: 1px solid rgba(255,255,255,0.88);
box-shadow: var(--shadow);
text-align: center;
}
.thankyou__card h1 {
margin: 0 0 0.8rem;
font-size: clamp(2rem, 5vw, 3.2rem);
letter-spacing: -0.03em;
}
@media (max-width: 920px) {
.hero {
min-height: auto;
}
.hero__content {
padding: 4rem 0 4rem;
}
.hero__title {
max-width: 12ch;
-webkit-text-stroke: 7px var(--sea-dark);
}
.event-tag {
width: 100%;
}
.intro__grid,
.rsvp__grid {
grid-template-columns: 1fr;
}
.section {
padding: 4rem 0;
}
}
@media (max-width: 640px) {
.hero__content {
justify-items: stretch;
}
.eyebrow {
text-align: center;
}
.hero__title {
text-align: center;
font-size: clamp(2.45rem, 12vw, 4.2rem);
-webkit-text-stroke: 5px var(--sea-dark);
}
.hero__underline {
height: 11px;
width: 100%;
max-width: none;
}
.event-tag {
border-radius: 24px;
}
.event-tag--icon {
grid-template-columns: 1fr;
}
.event-tag__icon {
width: auto;
padding-top: 1.2rem;
}
.event-tag__content {
padding: 0.5rem 1.2rem 1.25rem;
}
.theme-lines .event-tag--lines {
grid-template-columns: 1fr;
}
.theme-lines .event-tag__line--vertical {
display: none;
}
.theme-lines .event-tag__content {
padding-left: 1.2rem;
}
.form-card,
.details-card {
padding: 1.2rem;
}
.choice-group {
flex-direction: column;
}
.choice span {
width: 100%;
justify-content: center;
}
}
body.modal-open {
overflow: hidden;
}
.success-modal {
position: fixed;
inset: 0;
z-index: 50;
display: grid;
place-items: center;
padding: 1.2rem;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity .2s ease, visibility .2s ease;
}
.success-modal--open {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.success-modal__backdrop {
position: absolute;
inset: 0;
background: rgba(7, 14, 24, 0.52);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
.success-modal__dialog {
position: relative;
z-index: 1;
width: min(100%, 620px);
}
.success-tag {
display: grid;
grid-template-columns: 14px 1fr;
background: rgba(255, 255, 255, 0.95);
border: 1px solid rgba(255, 255, 255, 0.9);
border-radius: 30px;
box-shadow: 0 24px 70px rgba(18, 26, 38, 0.24);
overflow: hidden;
}
.success-tag__line {
background: linear-gradient(180deg, rgba(30,87,138,0.92), rgba(30,87,138,0.36));
}
.success-tag__content {
padding: 1.7rem 1.5rem 1.5rem 1.35rem;
}
.success-tag__kicker {
margin: 0 0 0.45rem;
color: var(--sea);
font-size: 0.82rem;
font-weight: 800;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.success-tag__title {
margin: 0;
font-size: clamp(1.7rem, 4vw, 2.4rem);
line-height: 1;
letter-spacing: -0.03em;
}
.success-tag__text {
margin: 0.8rem 0 1.25rem;
color: var(--ink-soft);
line-height: 1.7;
}
.success-tag__button {
width: auto;
min-width: 132px;
}
@media (max-width: 640px) {
.success-tag {
grid-template-columns: 10px 1fr;
border-radius: 24px;
}
.success-tag__content {
padding: 1.3rem 1.1rem 1.15rem 1rem;
}
.success-tag__button {
width: 100%;
}
}