feat(gdpr): conformità privacy — mappa a consenso, pagine legali, titolare

- Mappa Google su /contact ripristinata a click-to-load (consenso esplicito):
  nessun dato inviato a Google prima del click dell'utente
- Nuove pagine /privacy e /cookie-policy (informativa art. 13 GDPR e cookie),
  compilate con i dati del titolare IN-SANITY LAB S.R.L. (P.IVA 03927341200)
- Footer: ragione sociale + P.IVA e link a Privacy e Cookie Policy
- Form contatti: link all'informativa privacy sotto l'invio
- sitemap: aggiunte /privacy e /cookie-policy

Testi legali da far validare; base tecnica conforme (no analytics/tracker,
solo cookie tecnici, font self-hosted).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 16:19:57 +02:00
parent 1aa82f5332
commit 172cef236b
6 changed files with 151 additions and 5 deletions
+6
View File
@@ -20,9 +20,15 @@ const { variant = 'full' } = Astro.props;
<textarea class="field" name="message" placeholder={t('form.field.message')} required maxlength="5000"></textarea> <textarea class="field" name="message" placeholder={t('form.field.message')} required maxlength="5000"></textarea>
<input class="hp" type="text" name="website" tabindex="-1" autocomplete="off" /> <input class="hp" type="text" name="website" tabindex="-1" autocomplete="off" />
<button class="btn" type="submit"><T tag="form.submit" as="span" /></button> <button class="btn" type="submit"><T tag="form.submit" as="span" /></button>
<p class="cform__privacy">Inviando il modulo dichiari di aver letto l'<a href="/privacy">informativa sulla privacy</a>.</p>
<p class="form-msg" hidden></p> <p class="form-msg" hidden></p>
</form> </form>
<style>
.cform__privacy { font-size: .74rem; opacity: .8; margin: 8px 0 0; line-height: 1.5; }
.cform__privacy a { text-decoration: underline; }
</style>
<script> <script>
document.querySelectorAll<HTMLFormElement>('.cform').forEach((form) => { document.querySelectorAll<HTMLFormElement>('.cform').forEach((form) => {
form.addEventListener('submit', async (e) => { form.addEventListener('submit', async (e) => {
+4 -1
View File
@@ -14,6 +14,7 @@ const year = new Date().getFullYear();
<p>{t('global.contact.address')}, {t('global.contact.city-line')}<br />{t('global.contact.phone')}<br /><a href={`mailto:${t('global.contact.email')}`}>{t('global.contact.email')}</a></p> <p>{t('global.contact.address')}, {t('global.contact.city-line')}<br />{t('global.contact.phone')}<br /><a href={`mailto:${t('global.contact.email')}`}>{t('global.contact.email')}</a></p>
<p class="ftr__hours">{t('global.hours.1')}</p> <p class="ftr__hours">{t('global.hours.1')}</p>
<p class="ftr__hours">{t('global.hours.2')}</p> <p class="ftr__hours">{t('global.hours.2')}</p>
<p class="ftr__legal">IN-SANITY LAB S.R.L. — P. IVA 03927341200</p>
</div> </div>
<nav aria-label="training"> <nav aria-label="training">
<T tag="footer.col.training-title" as="h4" /> <T tag="footer.col.training-title" as="h4" />
@@ -30,7 +31,7 @@ const year = new Date().getFullYear();
</div> </div>
<div class="ftr__bar"> <div class="ftr__bar">
<div class="container ftr__bar-in"> <div class="container ftr__bar-in">
<p>© {year} <T tag="footer.copyright" as="span" /></p> <p>© {year} <T tag="footer.copyright" as="span" /> <span class="ftr__sep">·</span> <a href="/privacy">Privacy Policy</a> <span class="ftr__sep">·</span> <a href="/cookie-policy">Cookie Policy</a></p>
<p>{t('footer.social-label')} {site.socials.map((s) => <a href={s.url} rel="noopener" target="_blank">{s.label}</a>)}</p> <p>{t('footer.social-label')} {site.socials.map((s) => <a href={s.url} rel="noopener" target="_blank">{s.label}</a>)}</p>
</div> </div>
</div> </div>
@@ -44,6 +45,8 @@ const year = new Date().getFullYear();
.ftr__grid { display: grid; grid-template-columns: 1.3fr 1fr 1fr 1.3fr; gap: 40px; padding-block: 70px; } .ftr__grid { display: grid; grid-template-columns: 1.3fr 1fr 1fr 1.3fr; gap: 40px; padding-block: 70px; }
.ftr__tag { font-family: var(--font-heading); font-size: .7rem; letter-spacing: .2em; color: #fff; } .ftr__tag { font-family: var(--font-heading); font-size: .7rem; letter-spacing: .2em; color: #fff; }
.ftr__hours { margin: 0; } .ftr__hours { margin: 0; }
.ftr__legal { margin-top: 14px; font-size: .78rem; opacity: .8; }
.ftr__bar-in .ftr__sep { margin: 0 4px; opacity: .5; }
.ftr__bar { border-top: 1px solid rgba(255,255,255,.08); padding-block: 18px; } .ftr__bar { border-top: 1px solid rgba(255,255,255,.08); padding-block: 18px; }
.ftr__bar-in { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; } .ftr__bar-in { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; }
.ftr__bar-in a { display: inline; margin-left: 12px; } .ftr__bar-in a { display: inline; margin-left: 12px; }
+21 -3
View File
@@ -12,8 +12,12 @@ const mapsQuery = encodeURIComponent(t('global.contact.map-query'));
<PageHero title={t('contact.hero.title')} bgImage={getImageOverride('contact.hero.image') ?? contactHero} /> <PageHero title={t('contact.hero.title')} bgImage={getImageOverride('contact.hero.image') ?? contactHero} />
<section class="section"> <section class="section">
<div class="container cgrid"> <div class="container cgrid">
<div class="cmap"> <div class="cmap" id="map-box" data-maps-query={mapsQuery}>
<iframe src={`https://www.google.com/maps?q=${mapsQuery}&output=embed`} loading="lazy" title="Mappa InsanityLab" referrerpolicy="no-referrer-when-downgrade"></iframe> <div class="cmap__consent">
<p class="cmap__title">Mappa Google</p>
<p class="cmap__note">Caricando la mappa accetti che Google riceva i tuoi dati di connessione (incluso l'indirizzo IP) secondo la sua informativa. Vedi la nostra <a href="/cookie-policy">Cookie Policy</a>.</p>
<button class="btn btn--dark" id="map-load" type="button">Carica la mappa</button>
</div>
</div> </div>
<ContactForm variant="full" /> <ContactForm variant="full" />
</div> </div>
@@ -39,10 +43,24 @@ const mapsQuery = encodeURIComponent(t('global.contact.map-query'));
<style> <style>
.cgrid { display: grid; grid-template-columns: 1.2fr 1fr; gap: 50px; margin-bottom: 50px; } .cgrid { display: grid; grid-template-columns: 1.2fr 1fr; gap: 50px; margin-bottom: 50px; }
.cmap { background: var(--c-bg-alt); min-height: 420px; } .cmap { background: var(--c-bg-alt); min-height: 420px; display: flex; align-items: center; justify-content: center; }
.cmap iframe { display: block; width: 100%; height: 100%; min-height: 420px; border: 0; } .cmap iframe { display: block; width: 100%; height: 100%; min-height: 420px; border: 0; }
.cmap__consent { max-width: 380px; text-align: center; padding: 24px; display: flex; flex-direction: column; gap: 12px; align-items: center; }
.cmap__title { font-family: var(--font-heading); text-transform: uppercase; letter-spacing: .12em; font-size: .9rem; margin: 0; }
.cmap__note { font-size: .8rem; color: var(--c-text-light); margin: 0; line-height: 1.5; }
.cmap__note a { color: var(--c-accent-dark); }
.cinfo { display: grid; grid-template-columns: repeat(3, 1fr); gap: 30px; margin-bottom: 30px; } .cinfo { display: grid; grid-template-columns: repeat(3, 1fr); gap: 30px; margin-bottom: 30px; }
.cinfo h3 { font-size: .85rem; text-transform: uppercase; letter-spacing: .16em; } .cinfo h3 { font-size: .85rem; text-transform: uppercase; letter-spacing: .16em; }
.cinvite { max-width: 640px; } .cinvite { max-width: 640px; }
@media (max-width: 991px) { .cgrid, .cinfo { grid-template-columns: 1fr; } } @media (max-width: 991px) { .cgrid, .cinfo { grid-template-columns: 1fr; } }
</style> </style>
<script>
// GDPR: la mappa Google (terza parte) viene caricata solo dopo consenso esplicito.
document.getElementById('map-load')?.addEventListener('click', () => {
const box = document.getElementById('map-box');
if (!box) return;
const q = box.dataset.mapsQuery ?? '';
box.innerHTML = `<iframe src="https://www.google.com/maps?q=${q}&output=embed" loading="lazy" title="Mappa InsanityLab" referrerpolicy="no-referrer-when-downgrade" style="display:block;width:100%;height:100%;min-height:420px;border:0"></iframe>`;
});
</script>
+52
View File
@@ -0,0 +1,52 @@
---
import Base from '../layouts/Base.astro';
import PageHero from '../components/PageHero.astro';
export const prerender = false;
---
<Base title="Cookie Policy — InsanityLab" description="Informativa sull'uso dei cookie del sito insanitylab.it.">
<PageHero title="Cookie Policy" />
<section class="section">
<div class="container legal">
<p class="legal__updated">Ultimo aggiornamento: luglio 2026</p>
<p>La presente Cookie Policy descrive i cookie e le tecnologie analoghe utilizzati dal sito <strong>insanitylab.it</strong>, gestito da <strong>IN-SANITY LAB S.R.L.</strong> (Via Leandro Alberti 76, 40139 Bologna — P. IVA 03927341200).</p>
<h2>1. Cosa sono i cookie</h2>
<p>I cookie sono piccoli file di testo che i siti visitati inviano al dispositivo dell'utente, dove vengono memorizzati per essere ritrasmessi agli stessi siti alla visita successiva. Possono avere finalità tecniche, funzionali o di profilazione.</p>
<h2>2. Cookie utilizzati da questo sito</h2>
<p>Il sito utilizza <strong>esclusivamente cookie tecnici</strong>, necessari al funzionamento dell'area riservata. Questi cookie non richiedono il consenso dell'utente e non vengono impostati ai visitatori non autenticati.</p>
<div class="legal__table">
<table>
<thead><tr><th>Cookie</th><th>Finalità</th><th>Durata</th><th>Tipo</th></tr></thead>
<tbody>
<tr><td>session</td><td>Mantiene l'autenticazione dell'utente nell'area riservata</td><td>Sessione</td><td>Tecnico</td></tr>
<tr><td>showtags</td><td>Ricorda la preferenza di visualizzazione dei tag di modifica dei contenuti (solo utenti autorizzati)</td><td>Sessione</td><td>Tecnico / funzionale</td></tr>
</tbody>
</table>
</div>
<p>Il sito <strong>non utilizza cookie di profilazione, di marketing o di analisi statistica</strong> (nessun Google Analytics o strumento equivalente).</p>
<h2>3. Contenuti di terze parti — Google Maps</h2>
<p>La pagina Contatti può mostrare una mappa fornita da <strong>Google Maps</strong>. Per tutelare la tua privacy, la mappa <strong>non viene caricata automaticamente</strong>: compare solo dopo che hai cliccato l'apposito pulsante di consenso. Fino a quel momento nessun dato viene inviato a Google. Se scegli di caricare la mappa, Google può impostare propri cookie e trattare dati (incluso l'indirizzo IP) secondo la propria <a href="https://policies.google.com/privacy" target="_blank" rel="noopener">informativa privacy</a>.</p>
<h2>4. Gestione dei cookie dal browser</h2>
<p>L'utente può gestire o eliminare i cookie tramite le impostazioni del proprio browser. La disabilitazione dei cookie tecnici può compromettere il funzionamento dell'area riservata. Le istruzioni sono disponibili nelle guide dei principali browser (Chrome, Firefox, Safari, Edge).</p>
<h2>5. Titolare e contatti</h2>
<p>Per qualsiasi richiesta relativa ai cookie e al trattamento dei dati è possibile scrivere a <a href="mailto:info@insanitylab.it">info@insanitylab.it</a>. Per maggiori informazioni sul trattamento dei dati personali si rimanda alla <a href="/privacy">Privacy Policy</a>.</p>
</div>
</section>
</Base>
<style>
.legal { max-width: 760px; }
.legal__updated { font-size: .8rem; color: var(--c-text-light); text-transform: uppercase; letter-spacing: .1em; margin-bottom: 30px; }
.legal h2 { font-size: 1.05rem; text-transform: uppercase; letter-spacing: .08em; margin: 34px 0 12px; }
.legal p, .legal li { line-height: 1.7; }
.legal a { color: var(--c-accent-dark); }
.legal__table { overflow-x: auto; margin: 12px 0; }
.legal table { width: 100%; border-collapse: collapse; font-size: .88rem; }
.legal th, .legal td { border: 1px solid var(--c-border, #e4ded6); padding: 10px 12px; text-align: left; vertical-align: top; }
.legal th { background: var(--c-bg-alt); text-transform: uppercase; font-size: .75rem; letter-spacing: .06em; }
</style>
+67
View File
@@ -0,0 +1,67 @@
---
import Base from '../layouts/Base.astro';
import PageHero from '../components/PageHero.astro';
export const prerender = false;
---
<Base title="Privacy Policy — InsanityLab" description="Informativa sul trattamento dei dati personali ai sensi del Regolamento (UE) 2016/679 (GDPR).">
<PageHero title="Privacy Policy" />
<section class="section">
<div class="container legal">
<p class="legal__updated">Ultimo aggiornamento: luglio 2026</p>
<p>La presente informativa descrive le modalità di trattamento dei dati personali degli utenti che consultano il sito <strong>insanitylab.it</strong> e che utilizzano il modulo di contatto, ai sensi dell'articolo 13 del Regolamento (UE) 2016/679 (di seguito "GDPR").</p>
<h2>1. Titolare del trattamento</h2>
<p>
<strong>IN-SANITY LAB S.R.L.</strong><br>
Via Leandro Alberti 76, 40139 Bologna (BO)<br>
P. IVA e Codice Fiscale: 03927341200 — REA: BO-556474<br>
E-mail: <a href="mailto:info@insanitylab.it">info@insanitylab.it</a> — PEC: insanitylab@pec.it<br>
Telefono: 351 7535977
</p>
<h2>2. Tipologie di dati trattati</h2>
<p>Il sito tratta le seguenti categorie di dati:</p>
<ul>
<li><strong>Dati forniti volontariamente tramite il modulo di contatto</strong>: nome, cognome, indirizzo e-mail, numero di telefono (facoltativo) e il testo del messaggio.</li>
<li><strong>Dati di navigazione</strong>: dati tecnici trasmessi automaticamente dal browser (ad esempio indirizzo IP e log del server), trattati per il funzionamento e la sicurezza del sito.</li>
</ul>
<p>Il sito <strong>non utilizza strumenti di analisi statistica o di profilazione</strong> (es. Google Analytics) e non installa cookie di marketing.</p>
<h2>3. Finalità e base giuridica</h2>
<ul>
<li><strong>Riscontro alle richieste inviate tramite il modulo di contatto</strong> — base giuridica: esecuzione di misure precontrattuali richieste dall'interessato e legittimo interesse del Titolare a rispondere (art. 6.1 lett. b e f GDPR).</li>
<li><strong>Funzionamento e sicurezza del sito</strong> — base giuridica: legittimo interesse del Titolare (art. 6.1 lett. f GDPR).</li>
</ul>
<h2>4. Modalità e periodo di conservazione</h2>
<p>I dati sono trattati con strumenti elettronici e con misure di sicurezza adeguate. I messaggi ricevuti tramite il modulo di contatto vengono conservati per il tempo necessario a gestire la richiesta e a dare seguito agli eventuali rapporti che ne derivano, e comunque non oltre i termini previsti dalla normativa applicabile. I log tecnici del server sono conservati per il tempo strettamente necessario alle finalità di sicurezza.</p>
<h2>5. Destinatari e responsabili del trattamento</h2>
<p>I dati possono essere trattati da soggetti che forniscono servizi al Titolare in qualità di responsabili del trattamento, in particolare:</p>
<ul>
<li><strong>Aruba S.p.A.</strong> — fornitore dei servizi di posta elettronica utilizzati per l'invio e la ricezione dei messaggi del modulo di contatto.</li>
<li>Il fornitore di hosting che ospita il sito.</li>
</ul>
<p>I dati non sono diffusi né ceduti a terzi per finalità diverse da quelle indicate.</p>
<h2>6. Servizi di terze parti e trasferimenti extra UE</h2>
<p>La pagina Contatti può visualizzare una mappa fornita da <strong>Google Maps</strong> (Google Ireland Ltd. / Google LLC). La mappa viene caricata <strong>solo previo consenso esplicito</strong> dell'utente, tramite apposito pulsante: fino a quel momento nessun dato viene trasmesso a Google. In caso di caricamento, Google può trattare dati (incluso l'indirizzo IP), anche verso Paesi terzi, secondo la propria informativa privacy. Maggiori dettagli nella <a href="/cookie-policy">Cookie Policy</a>.</p>
<h2>7. Diritti dell'interessato</h2>
<p>L'interessato può in qualsiasi momento esercitare i diritti previsti dagli articoli da 15 a 22 del GDPR: accesso, rettifica, cancellazione, limitazione, portabilità, opposizione al trattamento e revoca del consenso (ove applicabile). Le richieste possono essere inviate a <a href="mailto:info@insanitylab.it">info@insanitylab.it</a>. L'interessato ha inoltre il diritto di proporre reclamo all'Autorità Garante per la protezione dei dati personali (<a href="https://www.garanteprivacy.it" target="_blank" rel="noopener">www.garanteprivacy.it</a>).</p>
<h2>8. Cookie</h2>
<p>Il sito utilizza esclusivamente cookie tecnici necessari al funzionamento dell'area riservata. Per il dettaglio si rimanda alla <a href="/cookie-policy">Cookie Policy</a>.</p>
</div>
</section>
</Base>
<style>
.legal { max-width: 760px; }
.legal__updated { font-size: .8rem; color: var(--c-text-light); text-transform: uppercase; letter-spacing: .1em; margin-bottom: 30px; }
.legal h2 { font-size: 1.05rem; text-transform: uppercase; letter-spacing: .08em; margin: 34px 0 12px; }
.legal p, .legal li { line-height: 1.7; }
.legal ul { padding-left: 20px; display: grid; gap: 8px; margin-bottom: 8px; }
.legal a { color: var(--c-accent-dark); }
</style>
+1 -1
View File
@@ -3,7 +3,7 @@ import { getDb } from '../lib/db';
import { services } from '../data/services'; import { services } from '../data/services';
export const prerender = false; export const prerender = false;
const STATIC = ['/', '/about', '/contact', '/training', '/services', '/blog', '/promo-eventi']; const STATIC = ['/', '/about', '/contact', '/training', '/services', '/blog', '/promo-eventi', '/privacy', '/cookie-policy'];
export const GET: APIRoute = ({ site }) => { export const GET: APIRoute = ({ site }) => {
const base = (site ?? new URL('https://insanitylab.tielogic.xyz')).toString().replace(/\/$/, ''); const base = (site ?? new URL('https://insanitylab.tielogic.xyz')).toString().replace(/\/$/, '');