3 Commits

Author SHA1 Message Date
Adriano 4cee8cdd7a Revert "Rende la pagina Promo ed eventi modificabile dal pannello contenuti"
This reverts commit 1742c1d666.
2026-07-13 11:54:10 +02:00
Adriano 1742c1d666 Rende la pagina Promo ed eventi modificabile dal pannello contenuti
Tutti i testi della landing (hero, launch, card con prezzi, nota validità e FAQ)
passano ora dal sistema di content-tag `promo.*`, editabili inline e da pannello.
Il numero WhatsApp è centralizzato nel tag globale `global.whatsapp`: le risposte
FAQ usano il segnaposto {{whatsapp}}, sostituito a runtime, così si aggiorna in un
punto solo. Struttura delle 4 card e link di acquisto bsport restano nel codice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 11:51:21 +02:00
Adriano 0b938d7d09 Aggiunge integrazione Google Tag Manager attivabile via env
GTM viene caricato nel layout Base solo se la variabile GTM_ID è valorizzata.
L'ID è letto a runtime da process.env (non inlinato a build-time), così si
attiva/disattiva senza ricompilare. Con GTM_ID vuoto il sito non carica alcuno
script Google.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 10:59:34 +02:00
3 changed files with 118 additions and 195 deletions
-74
View File
@@ -24,7 +24,6 @@ export const contentSeed: SeedEntry[] = [
T('global.contact.address', site.address, []), T('global.contact.address', site.address, []),
T('global.contact.city-line', site.cityLine, []), T('global.contact.city-line', site.cityLine, []),
T('global.contact.map-query', site.mapQuery, []), T('global.contact.map-query', site.mapQuery, []),
T('global.whatsapp', '351 753 5977', []),
T('global.hours.1', site.hours[0], []), T('global.hours.1', site.hours[0], []),
T('global.hours.2', site.hours[1], []), T('global.hours.2', site.hours[1], []),
T('global.nav.1.label', site.navigation[0].label, []), T('global.nav.1.label', site.navigation[0].label, []),
@@ -300,79 +299,6 @@ export const contentSeed: SeedEntry[] = [
T('notfound.body', 'La pagina che cerchi non esiste o è stata spostata.'), T('notfound.body', 'La pagina che cerchi non esiste o è stata spostata.'),
T('notfound.cta', 'Torna alla home', []), T('notfound.cta', 'Torna alla home', []),
// --- Promo ed eventi (promo-eventi.astro) ---
// Hero
T('promo.hero.badge', 'insanity.lab · bologna savena', []),
T('promo.hero.title1', 'Allenati con metodo.'),
T('promo.hero.accent', 'Scegli come'),
T('promo.hero.title2', 'vuoi farlo.'),
H('promo.hero.desc', 'Allenati con flessibilità, senza vincoli, da subito.<br>Acquisto online in pochi secondi, pagamento sicuro via Stripe.'),
T('promo.hero.cta', 'LE NOSTRE PROPOSTE →', []),
// Launch
T('promo.launch.badge', 'solo per apertura nuova sede Settembre 2026', []),
T('promo.launch.title', 'Il nostro metodo nasce per durare.'),
T('promo.launch.accent', "Per l'apertura, un'eccezione. Solo per poco."),
T('promo.launch.desc', "In-Sanity Lab lavora per percorsi strutturati — trimestrali, semestrali — perché i risultati veri si costruiscono nel tempo. Per la nuova apertura, ci sarà un'eccezione: 5 incontri pensati come un vero mini-percorso, non semplici lezioni sciolte."),
// Perché 5
T('promo.why5.text', 'Non è una prova. È un percorso in miniatura — per capire come lavoriamo prima di impegnarti su un programma trimestrale o semestrale.'),
// Label bottone acquisto (condivisa dalle 4 card)
T('promo.buy-label', 'ACQUISTA →', []),
// Card 1 — One to One
T('promo.card.1.eyebrow', 'Personal training', []),
T('promo.card.1.title', 'One to One'),
T('promo.card.1.price', '163 €', []),
T('promo.card.1.price-old', '300 €', []),
T('promo.card.1.unit', '5 incontri', []),
T('promo.card.1.feature.1', 'Screening iniziale, definizione dei tuoi obiettivi'),
T('promo.card.1.feature.2', 'Check-up con profilazione fisica, il tuo punto di partenza'),
T('promo.card.1.feature.3', 'PT dedicato, presente a ogni sessione'),
T('promo.card.1.feature.4', 'In-sanity plan, il tuo programma personalizzato'),
// Card 2 — Small Group (in evidenza)
T('promo.card.2.badge', 'Più scelto', []),
T('promo.card.2.eyebrow', 'Personal training', []),
T('promo.card.2.title', 'Small Group'),
T('promo.card.2.price', '110 €', []),
T('promo.card.2.price-old', '220 €', []),
T('promo.card.2.unit', '5 incontri', []),
T('promo.card.2.feature.1', 'Screening iniziale, definizione dei tuoi obiettivi'),
T('promo.card.2.feature.2', 'Check-up con profilazione fisica, il tuo punto di partenza'),
T('promo.card.2.feature.3', 'PT dedicato, presente a ogni sessione'),
T('promo.card.2.feature.4', 'In-sanity plan, il tuo programma personalizzato'),
// Card 3 — Reformer
T('promo.card.3.eyebrow', 'Pilates class', []),
T('promo.card.3.title', 'Reformer'),
T('promo.card.3.price', '100 €', []),
T('promo.card.3.price-old', '140 €', []),
T('promo.card.3.unit', '5 incontri', []),
T('promo.card.3.feature.1', 'Core, postura e mobilità su macchina Reformer'),
T('promo.card.3.feature.2', 'Lavoro a basso impatto sulle articolazioni'),
T('promo.card.3.feature.3', 'Percorso breve, non lezioni isolate'),
// Card 4 — Holistic class
T('promo.card.4.eyebrow', 'Holistic class', []),
T('promo.card.4.title', 'Pilates Mat, Mobility, Yoga'),
T('promo.card.4.price', '50 €', []),
T('promo.card.4.price-old', '100 €', []),
T('promo.card.4.unit', '5 incontri', []),
T('promo.card.4.feature.1', 'Mobilità articolare, respirazione, equilibrio'),
T('promo.card.4.feature.2', 'Da solo, o integrato nei tuoi allenamenti'),
T('promo.card.4.feature.3', 'Adatto a tutti i livelli'),
// Nota validità
T('promo.validity-note', 'Disponibile solo per l\'apertura · Valido entro 2 mesi dal primo incontro e comunque non oltre il 1° dicembre 2026', []),
// FAQ — le risposte usano il segnaposto {{whatsapp}}, sostituito a runtime con global.whatsapp
T('promo.faq.title', 'Tutto quello che vuoi sapere prima di acquistare.'),
T('promo.faq.1.q', 'Come funziona il pacchetto a incontri per One to One e Small Group?'),
H('promo.faq.1.a', 'Acquisti il pacchetto online con pagamento sicuro via <span class="hl">Stripe</span>. Ricevi una conferma via email. Poi ci scrivi su <span class="hl">WhatsApp ({{whatsapp}})</span> per prenotare le singole sessioni in base agli orari disponibili. Verrai ricontattato subito tramite messaggio o chiamata! Gli incontri sono validi 2 mesi dalla data del primo incontro e comunque non oltre il 1° dicembre 2026.'),
T('promo.faq.2.q', 'Come funziona il percorso Reformer o Holistic Class?'),
H('promo.faq.2.a', 'Acquisti il pacchetto online con pagamento sicuro via <span class="hl">Stripe</span>. Ricevi una conferma via email. Poi vai nella schermata Home, clicca il servizio che hai acquistato e seleziona autonomamente la class nel giorno e nell\'orario che desideri. In caso di problemi contattaci su <span class="hl">WhatsApp ({{whatsapp}})</span>! Gli incontri sono validi 2 mesi dalla data del primo incontro e comunque non oltre il 1° dicembre 2026.'),
T('promo.faq.3.q', 'Posso usare gli incontri su servizi diversi?'),
H('promo.faq.3.a', 'Ogni pacchetto è dedicato a un singolo servizio (<span class="hl">One to One</span>, <span class="hl">Small Group</span>, <span class="hl">Reformer</span> o <span class="hl">Holistic Class</span>). Se vuoi accedere a più servizi puoi acquistare pacchetti separati per ciascuno.'),
T('promo.faq.4.q', 'Posso regalare un pacchetto a qualcun altro?'),
H('promo.faq.4.a', 'Sì. Puoi acquistare il pacchetto a tuo nome — per la fatturazione — e farlo utilizzare a un\'altra persona: perfetto se vuoi fare un regalo a un amico o un familiare. Ti basterà comunicarci chi userà gli incontri quando ci scriverai su <span class="hl">WhatsApp ({{whatsapp}})</span> per organizzare le sessioni.'),
T('promo.faq.5.q', 'Il pagamento è sicuro?'),
H('promo.faq.5.a', 'Sì. Il pagamento avviene tramite <span class="hl">Stripe</span>, una delle piattaforme di pagamento online più sicure al mondo. I tuoi dati bancari non vengono mai condivisi con insanity.lab — tutto è gestito in modo cifrato da Stripe.'),
T('promo.faq.6.q', 'Posso chiedere un rimborso?'),
H('promo.faq.6.a', 'Per richieste di rimborso o problemi con l\'acquisto scrivici su <span class="hl">WhatsApp</span> o via email. Valutiamo ogni situazione in modo diretto e umano.'),
]; ];
export const seedByTag = new Map(contentSeed.map((e) => [e.tag, e])); export const seedByTag = new Map(contentSeed.map((e) => [e.tag, e]));
+8 -67
View File
@@ -113,8 +113,6 @@ const gtmId = process.env.GTM_ID;
font: inherit; font-size: .9rem; color: #2b2320; cursor: pointer; } font: inherit; font-size: .9rem; color: #2b2320; cursor: pointer; }
.tm-field select:focus { outline: 2px solid #9c8b70; outline-offset: 1px; border-color: #9c8b70; } .tm-field select:focus { outline: 2px solid #9c8b70; outline-offset: 1px; border-color: #9c8b70; }
.tm-field select.is-set { border-color: #9c8b70; background-color: #f6f1e8; font-weight: 600; } .tm-field select.is-set { border-color: #9c8b70; background-color: #f6f1e8; font-weight: 600; }
.tm-field select.is-estimated { border-style: dashed; border-color: #c4b79f; color: #6b5b48; }
.tm-note { margin: 12px 0 0; font: 400 12px/1.5 system-ui, sans-serif; color: #8a7c68; }
.tag-modal__box input[type=file] { display: block; width: 100%; font: inherit; font-size: .9rem; padding: 10px; .tag-modal__box input[type=file] { display: block; width: 100%; font: inherit; font-size: .9rem; padding: 10px;
border: 1px dashed #cbbfad; border-radius: 10px; background: #fff; box-sizing: border-box; } border: 1px dashed #cbbfad; border-radius: 10px; background: #fff; box-sizing: border-box; }
.tm-del { margin-top: 12px; padding: 8px 14px; border: 0; border-radius: 8px; background: #b4443a; color: #fff; cursor: pointer; font: 600 .85rem system-ui, sans-serif; } .tm-del { margin-top: 12px; padding: 8px 14px; border: 0; border-radius: 8px; background: #b4443a; color: #fff; cursor: pointer; font: 600 .85rem system-ui, sans-serif; }
@@ -153,42 +151,6 @@ const gtmId = process.env.GTM_ID;
color: { accent: 'Accento', 'accent-dark': 'Accento scuro', dark: 'Scuro', heading: 'Titolo', text: 'Testo', 'text-light': 'Testo chiaro', white: 'Bianco' }, color: { accent: 'Accento', 'accent-dark': 'Accento scuro', dark: 'Scuro', heading: 'Titolo', text: 'Testo', 'text-light': 'Testo chiaro', white: 'Bianco' },
}; };
const valLabel = (g, v) => (VALUE_LABELS[g] && VALUE_LABELS[g][v]) || v; const valLabel = (g, v) => (VALUE_LABELS[g] && VALUE_LABELS[g][v]) || v;
// Stima il preset più vicino all'aspetto REALE dell'elemento (getComputedStyle).
const px = (s) => parseFloat(s) || 0;
const rgb = (s) => { const m = String(s).match(/-?\d+(\.\d+)?/g); return m ? m.slice(0, 3).map(Number) : null; };
const SIZE_SCALE = { s: 0.85, m: 1, l: 1.25, xl: 1.5, xxl: 2 };
let colorProbe = null;
function nearestSize(el) {
const cs = getComputedStyle(el);
const ref = el.parentElement ? px(getComputedStyle(el.parentElement).fontSize) : px(cs.fontSize);
const ratio = ref ? px(cs.fontSize) / ref : 1;
let best = 'm', bd = Infinity;
for (const k in SIZE_SCALE) { const d = Math.abs(SIZE_SCALE[k] - ratio); if (d < bd) { bd = d; best = k; } }
return best;
}
function nearestColor(el) {
const a = rgb(getComputedStyle(el).color); if (!a) return null;
if (!colorProbe) { colorProbe = document.createElement('span'); colorProbe.style.cssText = 'position:absolute;left:-9999px;visibility:hidden'; document.body.appendChild(colorProbe); }
let best = null, bd = Infinity;
STYLE_GROUPS.color.forEach((c) => {
colorProbe.className = 'cs-color-' + c;
const p = rgb(getComputedStyle(colorProbe).color); if (!p) return;
const d = (p[0]-a[0])**2 + (p[1]-a[1])**2 + (p[2]-a[2])**2;
if (d < bd) { bd = d; best = c; }
});
colorProbe.className = '';
return best;
}
function effective(group, el) {
const cs = getComputedStyle(el);
if (group === 'size') return nearestSize(el);
if (group === 'color') return nearestColor(el);
if (group === 'weight') return parseInt(cs.fontWeight, 10) >= 600 ? 'bold' : 'normal';
if (group === 'style') return cs.fontStyle.indexOf('italic') >= 0 ? 'italic' : 'normal';
if (group === 'align') { let x = cs.textAlign; if (x === 'start') x = 'left'; else if (x === 'end') x = 'right'; return ['left','center','right','justify'].indexOf(x) >= 0 ? x : 'left'; }
return null;
}
const modal = document.getElementById('tag-modal'); const modal = document.getElementById('tag-modal');
const tmTag = document.getElementById('tm-tag'); const tmTag = document.getElementById('tm-tag');
const tmGuid = document.getElementById('tm-guid'); const tmGuid = document.getElementById('tm-guid');
@@ -233,12 +195,7 @@ const gtmId = process.env.GTM_ID;
if (groups.length) { if (groups.length) {
const grid = document.createElement('div'); const grid = document.createElement('div');
grid.className = 'tm-styles'; grid.className = 'tm-styles';
let anyEstimate = false;
groups.forEach((group) => { groups.forEach((group) => {
const explicit = styles[group]; // valore salvato nel blocco
const est = explicit ? null : effective(group, el); // altrimenti stima dall'aspetto reale
const shown = explicit || est;
if (est) anyEstimate = true;
const field = document.createElement('label'); const field = document.createElement('label');
field.className = 'tm-field'; field.className = 'tm-field';
const cap = document.createElement('span'); const cap = document.createElement('span');
@@ -247,32 +204,22 @@ const gtmId = process.env.GTM_ID;
const sel = document.createElement('select'); const sel = document.createElement('select');
sel.dataset.group = group; sel.dataset.group = group;
sel.className = 'tm-style'; sel.className = 'tm-style';
if (est) sel.dataset.estimated = est; // marcata come stima: non fissata al salvataggio se invariata
const none = document.createElement('option'); const none = document.createElement('option');
none.value = ''; none.textContent = '— default (tema) —'; none.value = ''; none.textContent = '— default —';
sel.appendChild(none); sel.appendChild(none);
STYLE_GROUPS[group].forEach((v) => { STYLE_GROUPS[group].forEach((v) => {
const o = document.createElement('option'); const o = document.createElement('option');
o.value = v; o.textContent = valLabel(group, v) + (est === v ? ' (attuale)' : ''); o.value = v; o.textContent = valLabel(group, v);
if (shown === v) o.selected = true; if (styles[group] === v) o.selected = true;
sel.appendChild(o); sel.appendChild(o);
}); });
sel.classList.add(explicit ? 'is-set' : (est ? 'is-estimated' : '')); // Evidenzia la combo se un valore è impostato.
sel.addEventListener('change', () => { if (styles[group]) sel.classList.add('is-set');
delete sel.dataset.estimated; // scelta manuale → diventa esplicita sel.addEventListener('change', () => sel.classList.toggle('is-set', !!sel.value));
sel.classList.remove('is-estimated');
sel.classList.toggle('is-set', !!sel.value);
});
field.appendChild(cap); field.appendChild(sel); field.appendChild(cap); field.appendChild(sel);
grid.appendChild(field); grid.appendChild(field);
}); });
tmBody.appendChild(grid); tmBody.appendChild(grid);
if (anyEstimate) {
const note = document.createElement('p');
note.className = 'tm-note';
note.textContent = 'I valori "(attuale)" riflettono laspetto dal tema: cambiane uno per fissarlo sul blocco.';
tmBody.appendChild(note);
}
} }
} }
modal.hidden = false; modal.hidden = false;
@@ -290,16 +237,10 @@ const gtmId = process.env.GTM_ID;
return; return;
} }
const value = document.getElementById('tm-value').value; const value = document.getElementById('tm-value').value;
const selects = document.querySelectorAll('.tm-style');
const styles = {}; const styles = {};
selects.forEach((s) => { document.querySelectorAll('.tm-style').forEach((s) => { if (s.value) styles[s.dataset.group] = s.value; });
const v = s.value;
if (!v) return; // "default (tema)" → non impostato
if (s.dataset.estimated && v === s.dataset.estimated) return; // stima non modificata → non fissare
styles[s.dataset.group] = v;
});
const body = { value }; const body = { value };
if (selects.length) body.styles = styles; // inviato sempre (anche {}) così l'azzeramento persiste if (Object.keys(styles).length) body.styles = styles;
const r = await fetch('/api/admin/content/' + encodeURIComponent(current.tag), { const r = await fetch('/api/admin/content/' + encodeURIComponent(current.tag), {
method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),
}); });
+104 -48
View File
@@ -1,86 +1,142 @@
--- ---
import Base from '../layouts/Base.astro'; import Base from '../layouts/Base.astro';
import T from '../components/content/T.astro';
import { t, resolveContent } from '../lib/content';
// Landing promo/lancio integrata nel sito (header e footer del sito, non standalone). // Landing promo/lancio integrata nel sito (header e footer del sito, non standalone).
// I bottoni "Acquista" portano al gestionale bsport: l'utente crea l'account, // I bottoni "Acquista" portano al gestionale bsport: l'utente crea l'account,
// inserisce i dati e paga via Stripe dentro la piattaforma. Link centralizzato qui: // inserisce i dati e paga via Stripe dentro la piattaforma. Link centralizzato qui:
// quando avremo gli ID membership per singolo prodotto basterà mappare BSPORT_URL card per card. // quando avremo gli ID membership per singolo prodotto basterà mappare BSPORT_URL card per card.
const BSPORT_URL = 'https://backoffice.bsport.io/login/signup?membership=3428'; const BSPORT_URL = 'https://backoffice.bsport.io/login/signup?membership=3428';
export const prerender = false; export const prerender = false;
// I testi sono editabili dal pannello contenuti (tag `promo.*`). Qui resta solo la
// struttura: quante card, quante feature ciascuna e quale è "in evidenza".
const cards = [
{ id: 1, features: 4, featured: false },
{ id: 2, features: 4, featured: true },
{ id: 3, features: 3, featured: false },
{ id: 4, features: 3, featured: false },
];
const faqs = [1, 2, 3, 4, 5, 6];
// Numero WhatsApp centralizzato: le risposte FAQ contengono il segnaposto {{whatsapp}}
// che qui viene sostituito col valore del tag globale, così si aggiorna in un punto solo.
const wa = t('global.whatsapp');
--- ---
<Base title="Promo ed eventi — InsanityLab" description="Edizione apertura: pacchetti a 5 incontri per One to One, Small Group, Reformer e Holistic Class. Acquisto online, pagamento sicuro via Stripe."> <Base title="Promo ed eventi — InsanityLab" description="Edizione apertura: pacchetti a 5 incontri per One to One, Small Group, Reformer e Holistic Class. Acquisto online, pagamento sicuro via Stripe.">
<div class="lp"> <div class="lp">
<div class="page"> <div class="page">
<section class="hero"> <section class="hero">
<T tag="promo.hero.badge" as="div" class="badge" /> <div class="badge">insanity.lab · bologna savena</div>
<T tag="promo.hero.title1" as="h1" /> <h1>Allenati con metodo.</h1>
<T tag="promo.hero.accent" as="p" class="accent" /> <p class="accent">Scegli come</p>
<T tag="promo.hero.title2" as="h1" /> <h1>vuoi farlo.</h1>
<T tag="promo.hero.desc" as="p" class="desc" /> <p class="desc">Allenati con flessibilità, senza vincoli, da subito.<br>Acquisto online in pochi secondi, pagamento sicuro via Stripe.</p>
<a class="btn-sand" href="#proposte"><T tag="promo.hero.cta" as="span" /></a> <a class="btn-sand" href="#proposte">LE NOSTRE PROPOSTE →</a>
</section> </section>
<section class="launch"> <section class="launch">
<T tag="promo.launch.badge" as="div" class="badge" /> <div class="badge">solo per apertura nuova sede Settembre 2026</div>
<T tag="promo.launch.title" as="h1" /> <h1>Il nostro metodo nasce per durare.</h1>
<T tag="promo.launch.accent" as="p" class="accent" /> <p class="accent">Per l'apertura, un'eccezione. Solo per poco.</p>
<T tag="promo.launch.desc" as="p" class="desc" /> <p class="desc">In-Sanity Lab lavora per percorsi strutturati — trimestrali, semestrali — perché i risultati veri si costruiscono nel tempo. Per la nuova apertura, ci sarà un'eccezione: 5 incontri pensati come un vero mini-percorso, non semplici lezioni sciolte.</p>
</section> </section>
<section class="why5"> <section class="why5">
<T tag="promo.why5.text" as="p" /> <p>Non è una prova. È un percorso in miniatura — per capire come lavoriamo prima di impegnarti su un programma trimestrale o semestrale.</p>
</section> </section>
<section class="cards-wrap" id="proposte"> <section class="cards-wrap" id="proposte">
<div class="cards"> <div class="cards">
{cards.map((c) => (
<div class:list={['card', c.featured && 'featured']}> <div class="card">
{c.featured && <T tag={`promo.card.${c.id}.badge`} as="div" class="featured-badge" />} <div class="eyebrow">Personal training</div>
<T tag={`promo.card.${c.id}.eyebrow`} as="div" class="eyebrow" /> <div class="title">One to One</div>
<T tag={`promo.card.${c.id}.title`} as="div" class="title" />
<div class="price-row"> <div class="price-row">
<T tag={`promo.card.${c.id}.price`} as="div" class="price" /> <div class="price">163 €</div>
<T tag={`promo.card.${c.id}.price-old`} as="div" class="price-old" /> <div class="price-old">300 €</div>
</div> </div>
<T tag={`promo.card.${c.id}.unit`} as="div" class="unit" /> <div class="unit">5 incontri</div>
<ul> <ul>
{Array.from({ length: c.features }, (_, i) => i + 1).map((m) => ( <li>Screening iniziale, definizione dei tuoi obiettivi</li>
<T tag={`promo.card.${c.id}.feature.${m}`} as="li" /> <li>Check-up con profilazione fisica, il tuo punto di partenza</li>
))} <li>PT dedicato, presente a ogni sessione</li>
<li>In-sanity plan, il tuo programma personalizzato</li>
</ul> </ul>
<a class="buy" href={BSPORT_URL} target="_blank" rel="noopener"><T tag="promo.buy-label" as="span" /></a> <a class="buy" href={BSPORT_URL} target="_blank" rel="noopener">ACQUISTA →</a>
</div> </div>
))}
<div class="card featured">
<div class="featured-badge">Più scelto</div>
<div class="eyebrow">Personal training</div>
<div class="title">Small Group</div>
<div class="price-row">
<div class="price">110 €</div>
<div class="price-old">220 €</div>
</div> </div>
<T tag="promo.validity-note" as="p" class="validity-note" /> <div class="unit">5 incontri</div>
<ul>
<li>Screening iniziale, definizione dei tuoi obiettivi</li>
<li>Check-up con profilazione fisica, il tuo punto di partenza</li>
<li>PT dedicato, presente a ogni sessione</li>
<li>In-sanity plan, il tuo programma personalizzato</li>
</ul>
<a class="buy" href={BSPORT_URL} target="_blank" rel="noopener">ACQUISTA →</a>
</div>
<div class="card">
<div class="eyebrow">Pilates class</div>
<div class="title">Reformer</div>
<div class="price-row">
<div class="price">100 €</div>
<div class="price-old">140 €</div>
</div>
<div class="unit">5 incontri</div>
<ul>
<li>Core, postura e mobilità su macchina Reformer</li>
<li>Lavoro a basso impatto sulle articolazioni</li>
<li>Percorso breve, non lezioni isolate</li>
</ul>
<a class="buy" href={BSPORT_URL} target="_blank" rel="noopener">ACQUISTA →</a>
</div>
<div class="card">
<div class="eyebrow">Holistic class</div>
<div class="title">Pilates Mat, Mobility, Yoga</div>
<div class="price-row">
<div class="price">50 €</div>
<div class="price-old">100 €</div>
</div>
<div class="unit">5 incontri</div>
<ul>
<li>Mobilità articolare, respirazione, equilibrio</li>
<li>Da solo, o integrato nei tuoi allenamenti</li>
<li>Adatto a tutti i livelli</li>
</ul>
<a class="buy" href={BSPORT_URL} target="_blank" rel="noopener">ACQUISTA →</a>
</div>
</div>
<p class="validity-note">Disponibile solo per l'apertura · Valido entro 2 mesi dal primo incontro e comunque non oltre il 1° dicembre 2026</p>
</section> </section>
<section class="faq-wrap"> <section class="faq-wrap">
<T tag="promo.faq.title" as="h1" /> <h1>Tutto quello che vuoi sapere prima di acquistare.</h1>
{faqs.map((n) => {
const a = resolveContent(`promo.faq.${n}.a`);
return (
<div class="faq-block"> <div class="faq-block">
<div class="faq-q"><T tag={`promo.faq.${n}.q`} as="p" /><div class="faq-toggle">+</div></div> <div class="faq-q"><p>Come funziona il pacchetto a incontri per One to One e Small Group?</p><div class="faq-toggle">+</div></div>
<p class:list={['faq-a', a.classes]} data-tag={`promo.faq.${n}.a`} data-guid={a.guid || undefined} set:html={a.value.replaceAll('{{whatsapp}}', wa)} /> <p class="faq-a">Acquisti il pacchetto online con pagamento sicuro via <span class="hl">Stripe</span>. Ricevi una conferma via email. Poi ci scrivi su <span class="hl">WhatsApp (351 753 5977)</span> per prenotare le singole sessioni in base agli orari disponibili. Verrai ricontattato subito tramite messaggio o chiamata! Gli incontri sono validi 2 mesi dalla data del primo incontro e comunque non oltre il 1° dicembre 2026.</p>
</div>
<div class="faq-block">
<div class="faq-q"><p>Come funziona il percorso Reformer o Holistic Class?</p><div class="faq-toggle">+</div></div>
<p class="faq-a">Acquisti il pacchetto online con pagamento sicuro via <span class="hl">Stripe</span>. Ricevi una conferma via email. Poi vai nella schermata Home, clicca il servizio che hai acquistato e seleziona autonomamente la class nel giorno e nell'orario che desideri. In caso di problemi contattaci su <span class="hl">WhatsApp (351 753 5977)</span>! Gli incontri sono validi 2 mesi dalla data del primo incontro e comunque non oltre il 1° dicembre 2026.</p>
</div>
<div class="faq-block">
<div class="faq-q"><p>Posso usare gli incontri su servizi diversi?</p><div class="faq-toggle">+</div></div>
<p class="faq-a">Ogni pacchetto è dedicato a un singolo servizio (<span class="hl">One to One</span>, <span class="hl">Small Group</span>, <span class="hl">Reformer</span> o <span class="hl">Holistic Class</span>). Se vuoi accedere a più servizi puoi acquistare pacchetti separati per ciascuno.</p>
</div>
<div class="faq-block">
<div class="faq-q"><p>Posso regalare un pacchetto a qualcun altro?</p><div class="faq-toggle">+</div></div>
<p class="faq-a">Sì. Puoi acquistare il pacchetto a tuo nome — per la fatturazione — e farlo utilizzare a un'altra persona: perfetto se vuoi fare un regalo a un amico o un familiare. Ti basterà comunicarci chi userà gli incontri quando ci scriverai su <span class="hl">WhatsApp (351 753 5977)</span> per organizzare le sessioni.</p>
</div>
<div class="faq-block">
<div class="faq-q"><p>Il pagamento è sicuro?</p><div class="faq-toggle">+</div></div>
<p class="faq-a">Sì. Il pagamento avviene tramite <span class="hl">Stripe</span>, una delle piattaforme di pagamento online più sicure al mondo. I tuoi dati bancari non vengono mai condivisi con insanity.lab — tutto è gestito in modo cifrato da Stripe.</p>
</div>
<div class="faq-block">
<div class="faq-q"><p>Posso chiedere un rimborso?</p><div class="faq-toggle">+</div></div>
<p class="faq-a">Per richieste di rimborso o problemi con l'acquisto scrivici su <span class="hl">WhatsApp</span> o via email. Valutiamo ogni situazione in modo diretto e umano.</p>
</div> </div>
);
})}
</section> </section>
</div> </div>