Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1522d97f54 | |||
| cc21cb7627 | |||
| 27fdf1fc30 | |||
| ae5eef037b | |||
| 931c1e43ad | |||
| a7b05d703a | |||
| 4cee8cdd7a | |||
| 1742c1d666 | |||
| 0b938d7d09 |
@@ -11,3 +11,7 @@ CONTACT_FROM=postmaster@insanitylab.it
|
|||||||
CONTACT_TO=info@insanitylab.it
|
CONTACT_TO=info@insanitylab.it
|
||||||
DB_PATH=data/insanitylab.db
|
DB_PATH=data/insanitylab.db
|
||||||
UPLOADS_DIR=uploads
|
UPLOADS_DIR=uploads
|
||||||
|
|
||||||
|
# Google Tag Manager: ID container preso da tagmanager.google.com (accanto al nome container).
|
||||||
|
# Formato GTM-XXXXXXX. Lascia vuoto per non caricare GTM (es. in locale/staging).
|
||||||
|
GTM_ID=GTM-XXXXXXX
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ 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, []),
|
||||||
@@ -32,6 +33,7 @@ export const contentSeed: SeedEntry[] = [
|
|||||||
T('global.nav.4.label', site.navigation[3].label, []),
|
T('global.nav.4.label', site.navigation[3].label, []),
|
||||||
T('global.nav.5.label', site.navigation[4].label, []),
|
T('global.nav.5.label', site.navigation[4].label, []),
|
||||||
T('global.nav.6.label', site.navigation[5].label, []),
|
T('global.nav.6.label', site.navigation[5].label, []),
|
||||||
|
T('global.nav.7.label', site.navigation[6].label, []),
|
||||||
|
|
||||||
// --- Header ---
|
// --- Header ---
|
||||||
T('header.logo.alt', 'InsanityLab', []),
|
T('header.logo.alt', 'InsanityLab', []),
|
||||||
@@ -299,6 +301,81 @@ 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) ---
|
||||||
|
// Titolo del banner PageHero (usato dalla variante promo-eventi2)
|
||||||
|
T('promo.pagehero.title', 'Promo ed eventi'),
|
||||||
|
// 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]));
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export const site = {
|
|||||||
{ label: "Blog & Edugo", href: "/blog" },
|
{ label: "Blog & Edugo", href: "/blog" },
|
||||||
{ label: "Promo ed eventi", href: "/promo-eventi" },
|
{ label: "Promo ed eventi", href: "/promo-eventi" },
|
||||||
{ label: "Contatti", href: "/contact" },
|
{ label: "Contatti", href: "/contact" },
|
||||||
|
{ label: "Promo 2 (test)", href: "/promo-eventi2" },
|
||||||
],
|
],
|
||||||
footerTraining: [
|
footerTraining: [
|
||||||
{ label: "One to one", href: "/training#one-to-one" },
|
{ label: "One to one", href: "/training#one-to-one" },
|
||||||
|
|||||||
@@ -19,10 +19,17 @@ const user = tok ? getSessionUser(getDb(), tok) : null;
|
|||||||
const canTags = !!user && (user.role === 'superuser' || user.role === 'admin');
|
const canTags = !!user && (user.role === 'superuser' || user.role === 'admin');
|
||||||
const wantTags = Astro.cookies.get('showtags')?.value === '1' || Astro.url.searchParams.get('annotate') === '1';
|
const wantTags = Astro.cookies.get('showtags')?.value === '1' || Astro.url.searchParams.get('annotate') === '1';
|
||||||
const showTags = canTags && wantTags;
|
const showTags = canTags && wantTags;
|
||||||
|
|
||||||
|
// Google Tag Manager: l'ID container arriva da GTM_ID (.env), letto a runtime via process.env
|
||||||
|
// perché in SSR le PUBLIC_* di Vite verrebbero inlinate a build-time. Se vuoto GTM non carica.
|
||||||
|
const gtmId = process.env.GTM_ID;
|
||||||
---
|
---
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="it">
|
<html lang="it">
|
||||||
<head>
|
<head>
|
||||||
|
{gtmId && (
|
||||||
|
<script is:inline set:html={`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','${gtmId}');`} />
|
||||||
|
)}
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>{title} — InsanityLab</title>
|
<title>{title} — InsanityLab</title>
|
||||||
@@ -36,6 +43,9 @@ const showTags = canTags && wantTags;
|
|||||||
<script is:inline>document.documentElement.classList.add('js');</script>
|
<script is:inline>document.documentElement.classList.add('js');</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
{gtmId && (
|
||||||
|
<noscript set:html={`<iframe src="https://www.googletagmanager.com/ns.html?id=${gtmId}" height="0" width="0" style="display:none;visibility:hidden"></iframe>`} />
|
||||||
|
)}
|
||||||
<Header user={user} showTags={showTags} canTags={canTags} />
|
<Header user={user} showTags={showTags} canTags={canTags} />
|
||||||
<main><slot /></main>
|
<main><slot /></main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
+55
-105
@@ -1,142 +1,86 @@
|
|||||||
---
|
---
|
||||||
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">
|
||||||
<div class="badge">insanity.lab · bologna savena</div>
|
<T tag="promo.hero.badge" as="div" class="badge" />
|
||||||
<h1>Allenati con metodo.</h1>
|
<T tag="promo.hero.title1" as="h1" />
|
||||||
<p class="accent">Scegli come</p>
|
<T tag="promo.hero.accent" as="p" class="accent" />
|
||||||
<h1>vuoi farlo.</h1>
|
<T tag="promo.hero.title2" as="h1" />
|
||||||
<p class="desc">Allenati con flessibilità, senza vincoli, da subito.<br>Acquisto online in pochi secondi, pagamento sicuro via Stripe.</p>
|
<T tag="promo.hero.desc" as="p" class="desc" />
|
||||||
<a class="btn-sand" href="#proposte">LE NOSTRE PROPOSTE →</a>
|
<a class="btn-sand" href="#proposte"><T tag="promo.hero.cta" as="span" /></a>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="launch">
|
<section class="launch">
|
||||||
<div class="badge">solo per apertura nuova sede Settembre 2026</div>
|
<T tag="promo.launch.badge" as="div" class="badge" />
|
||||||
<h1>Il nostro metodo nasce per durare.</h1>
|
<T tag="promo.launch.title" as="h1" />
|
||||||
<p class="accent">Per l'apertura, un'eccezione. Solo per poco.</p>
|
<T tag="promo.launch.accent" as="p" class="accent" />
|
||||||
<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>
|
<T tag="promo.launch.desc" as="p" class="desc" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="why5">
|
<section class="why5">
|
||||||
<p>Non è una prova. È un percorso in miniatura — per capire come lavoriamo prima di impegnarti su un programma trimestrale o semestrale.</p>
|
<T tag="promo.why5.text" as="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="card">
|
<div class:list={['card', c.featured && 'featured']}>
|
||||||
<div class="eyebrow">Personal training</div>
|
{c.featured && <T tag={`promo.card.${c.id}.badge`} as="div" class="featured-badge" />}
|
||||||
<div class="title">One to One</div>
|
<T tag={`promo.card.${c.id}.eyebrow`} as="div" class="eyebrow" />
|
||||||
|
<T tag={`promo.card.${c.id}.title`} as="div" class="title" />
|
||||||
<div class="price-row">
|
<div class="price-row">
|
||||||
<div class="price">163 €</div>
|
<T tag={`promo.card.${c.id}.price`} as="div" class="price" />
|
||||||
<div class="price-old">300 €</div>
|
<T tag={`promo.card.${c.id}.price-old`} as="div" class="price-old" />
|
||||||
</div>
|
</div>
|
||||||
<div class="unit">5 incontri</div>
|
<T tag={`promo.card.${c.id}.unit`} as="div" class="unit" />
|
||||||
<ul>
|
<ul>
|
||||||
<li>Screening iniziale, definizione dei tuoi obiettivi</li>
|
{Array.from({ length: c.features }, (_, i) => i + 1).map((m) => (
|
||||||
<li>Check-up con profilazione fisica, il tuo punto di partenza</li>
|
<T tag={`promo.card.${c.id}.feature.${m}`} as="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">ACQUISTA →</a>
|
<a class="buy" href={BSPORT_URL} target="_blank" rel="noopener"><T tag="promo.buy-label" as="span" /></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>
|
||||||
<div class="unit">5 incontri</div>
|
<T tag="promo.validity-note" as="p" class="validity-note" />
|
||||||
<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">
|
||||||
<h1>Tutto quello che vuoi sapere prima di acquistare.</h1>
|
<T tag="promo.faq.title" as="h1" />
|
||||||
|
{faqs.map((n) => {
|
||||||
|
const a = resolveContent(`promo.faq.${n}.a`);
|
||||||
|
return (
|
||||||
<div class="faq-block">
|
<div class="faq-block">
|
||||||
<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>
|
<div class="faq-q"><T tag={`promo.faq.${n}.q`} as="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 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>
|
<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)} />
|
||||||
</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>
|
||||||
@@ -163,7 +107,10 @@ export const prerender = false;
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<!-- is:global perché i testi editabili sono renderizzati dal componente <T> (fuori dallo
|
||||||
|
scope della pagina): con lo scope Astro il CSS non li raggiungerebbe. Ogni regola è
|
||||||
|
prefissata `.lp`, quindi resta confinata a questa landing senza toccare il resto del sito. -->
|
||||||
|
<style is:global>
|
||||||
.lp { background: #ECE7DC; }
|
.lp { background: #ECE7DC; }
|
||||||
.lp .page { max-width: 1000px; margin: 0 auto; background: #FFFFFF; font-family: 'Inter', system-ui, Arial, sans-serif; }
|
.lp .page { max-width: 1000px; margin: 0 auto; background: #FFFFFF; font-family: 'Inter', system-ui, Arial, sans-serif; }
|
||||||
|
|
||||||
@@ -182,6 +129,9 @@ export const prerender = false;
|
|||||||
border-radius: 999px; font-size: 13px; font-weight: 700; letter-spacing: 0.04em; cursor: pointer;
|
border-radius: 999px; font-size: 13px; font-weight: 700; letter-spacing: 0.04em; cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
/* La freccia è decorazione (il testo del tag non la include più) */
|
||||||
|
.lp .btn-sand::after { content: ' →'; }
|
||||||
|
.lp .card .buy::after { content: ' →'; }
|
||||||
|
|
||||||
/* LAUNCH */
|
/* LAUNCH */
|
||||||
.lp .launch { background: #F2ECE1; padding: 72px 64px; }
|
.lp .launch { background: #F2ECE1; padding: 72px 64px; }
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
---
|
||||||
|
import Base from '../layouts/Base.astro';
|
||||||
|
import PageHero from '../components/PageHero.astro';
|
||||||
|
import T from '../components/content/T.astro';
|
||||||
|
import { t, resolveContent } from '../lib/content';
|
||||||
|
// Variante di test di /promo-eventi ristrutturata sui pattern del sito: banner PageHero
|
||||||
|
// a tutta larghezza, sezioni .section/.container su sfondo bianco, bottoni .btn globali,
|
||||||
|
// font e palette dal design system. Contenuti identici (stessi tag `promo.*`). Gli stili
|
||||||
|
// custom (griglia card, accordion FAQ) sono namespaced sotto `.promo2`.
|
||||||
|
const BSPORT_URL = 'https://backoffice.bsport.io/login/signup?membership=3428';
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
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];
|
||||||
|
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.">
|
||||||
|
<PageHero title={t('promo.pagehero.title')} eyebrow={t('promo.hero.badge')} />
|
||||||
|
|
||||||
|
<div class="promo2">
|
||||||
|
|
||||||
|
<!-- Claim principale -->
|
||||||
|
<section class="section claim-section">
|
||||||
|
<div class="claim-wrap">
|
||||||
|
<h2 class="claim">
|
||||||
|
<span class="l1"><T tag="promo.hero.title1" as="span" /></span>
|
||||||
|
<span class="l2"><em><T tag="promo.hero.accent" as="span" /></em> <T tag="promo.hero.title2" as="span" /></span>
|
||||||
|
</h2>
|
||||||
|
<T tag="promo.hero.desc" as="p" class="claim-desc" />
|
||||||
|
<a class="btn" href="#proposte"><T tag="promo.hero.cta" as="span" /></a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Lancio + perché 5 -->
|
||||||
|
<section class="section section--alt">
|
||||||
|
<div class="container launch">
|
||||||
|
<T tag="promo.launch.badge" as="p" class="eyebrow" />
|
||||||
|
<h2><T tag="promo.launch.title" as="span" /></h2>
|
||||||
|
<p class="launch-accent"><T tag="promo.launch.accent" as="span" /></p>
|
||||||
|
<T tag="promo.launch.desc" as="p" />
|
||||||
|
<T tag="promo.why5.text" as="p" class="why5" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Pacchetti -->
|
||||||
|
<section class="section" id="proposte">
|
||||||
|
<div class="container">
|
||||||
|
<div class="cards">
|
||||||
|
{cards.map((c) => (
|
||||||
|
<div class:list={['card', c.featured && 'featured']}>
|
||||||
|
{c.featured && <T tag={`promo.card.${c.id}.badge`} as="div" class="featured-badge" />}
|
||||||
|
<T tag={`promo.card.${c.id}.eyebrow`} as="div" class="eyebrow" />
|
||||||
|
<T tag={`promo.card.${c.id}.title`} as="div" class="ptitle" />
|
||||||
|
<div class="price-row">
|
||||||
|
<T tag={`promo.card.${c.id}.price`} as="div" class="price" />
|
||||||
|
<T tag={`promo.card.${c.id}.price-old`} as="div" class="price-old" />
|
||||||
|
</div>
|
||||||
|
<T tag={`promo.card.${c.id}.unit`} as="div" class="unit" />
|
||||||
|
<ul>
|
||||||
|
{Array.from({ length: c.features }, (_, i) => i + 1).map((m) => (
|
||||||
|
<T tag={`promo.card.${c.id}.feature.${m}`} as="li" />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<a class:list={['btn', c.featured ? 'btn--dark' : 'btn--light', 'buy']} href={BSPORT_URL} target="_blank" rel="noopener"><T tag="promo.buy-label" as="span" /></a>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<T tag="promo.validity-note" as="p" class="validity-note" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- FAQ -->
|
||||||
|
<section class="section section--alt">
|
||||||
|
<div class="container faq-wrap">
|
||||||
|
<T tag="promo.faq.title" as="h2" class="faq-title" />
|
||||||
|
{faqs.map((n) => {
|
||||||
|
const a = resolveContent(`promo.faq.${n}.a`);
|
||||||
|
return (
|
||||||
|
<div class="faq-block">
|
||||||
|
<div class="faq-q"><T tag={`promo.faq.${n}.q`} as="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)} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</Base>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Accordion FAQ: click (o Invio/Spazio) sul quesito apre/chiude la risposta.
|
||||||
|
document.querySelectorAll('.promo2 .faq-block .faq-q').forEach((q) => {
|
||||||
|
const block = q.closest('.faq-block');
|
||||||
|
const toggle = q.querySelector('.faq-toggle');
|
||||||
|
q.setAttribute('role', 'button');
|
||||||
|
q.setAttribute('tabindex', '0');
|
||||||
|
q.setAttribute('aria-expanded', 'false');
|
||||||
|
const set = (open: boolean) => {
|
||||||
|
block?.classList.toggle('is-open', open);
|
||||||
|
q.setAttribute('aria-expanded', String(open));
|
||||||
|
if (toggle) toggle.textContent = open ? '−' : '+';
|
||||||
|
};
|
||||||
|
q.addEventListener('click', () => set(!block?.classList.contains('is-open')));
|
||||||
|
q.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); set(!block?.classList.contains('is-open')); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- is:global perché i testi editabili sono renderizzati dal componente <T> (fuori dallo
|
||||||
|
scope della pagina). Tutto è prefissato `.promo2`; la struttura usa le classi globali
|
||||||
|
del sito (.section, .container, .btn, .eyebrow, .section-heading). -->
|
||||||
|
<style is:global>
|
||||||
|
/* Claim principale */
|
||||||
|
.promo2 .claim-section { padding-block: 72px; text-align: center; }
|
||||||
|
.promo2 .claim-wrap { max-width: 820px; margin: 0 auto; padding-inline: 20px; }
|
||||||
|
.promo2 .claim { font-family: var(--font-heading); font-weight: 500; letter-spacing: .02em;
|
||||||
|
color: var(--c-heading); line-height: 1.18; font-size: clamp(2.1rem, 4.5vw, 3.1rem); margin: 0 0 22px; }
|
||||||
|
.promo2 .claim .l1, .promo2 .claim .l2 { display: block; }
|
||||||
|
.promo2 .claim em { font-style: italic; color: var(--c-accent-dark); }
|
||||||
|
.promo2 .claim-desc { max-width: 500px; margin: 0 auto 30px; font-size: 1.05rem; color: var(--c-text); }
|
||||||
|
|
||||||
|
/* Lancio — banda evidenziata */
|
||||||
|
.promo2 .launch { max-width: 760px; text-align: center; }
|
||||||
|
.promo2 .launch .eyebrow { display: block; margin: 0 0 14px; }
|
||||||
|
.promo2 .launch h2 { margin: 0 0 6px; }
|
||||||
|
.promo2 .launch-accent { font-family: var(--font-heading); font-style: italic; font-size: 1.2rem; color: var(--c-accent-dark); margin: 0 0 22px; }
|
||||||
|
.promo2 .launch > p { color: var(--c-text); margin-inline: auto; max-width: 620px; }
|
||||||
|
.promo2 .why5 { font-family: var(--font-heading); font-style: italic; font-size: 1.25rem; line-height: 1.65;
|
||||||
|
color: var(--c-heading); margin: 32px auto 0; max-width: 600px; padding-top: 28px; border-top: 2px solid var(--c-accent); }
|
||||||
|
|
||||||
|
/* Pacchetti */
|
||||||
|
.promo2 .cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; align-items: stretch; }
|
||||||
|
.promo2 .card { background: var(--c-bg); border: 1px solid #e7e0d6; border-radius: 6px; padding: 34px 30px; position: relative; display: flex; flex-direction: column; transition: box-shadow .25s, transform .25s; }
|
||||||
|
.promo2 .card:hover { box-shadow: 0 14px 40px rgba(58,41,41,.10); transform: translateY(-3px); }
|
||||||
|
.promo2 .card.featured { border: 1px solid var(--c-accent); box-shadow: 0 10px 34px rgba(58,41,41,.08); }
|
||||||
|
.promo2 .featured-badge {
|
||||||
|
position: absolute; top: -13px; left: 50%; transform: translateX(-50%); background: var(--c-accent);
|
||||||
|
color: #fff; font-family: var(--font-heading); font-size: 10.5px; font-weight: 600; letter-spacing: .12em;
|
||||||
|
text-transform: uppercase; padding: 6px 16px; border-radius: 999px; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.promo2 .card .eyebrow { margin: 0 0 8px; font-size: .68rem; }
|
||||||
|
.promo2 .card .ptitle { font-family: var(--font-heading); font-size: 1.4rem; font-weight: 500; color: var(--c-heading); letter-spacing: .03em; margin: 0 0 18px; }
|
||||||
|
.promo2 .card .price-row { display: flex; align-items: baseline; gap: 10px; margin: 0 0 3px; }
|
||||||
|
.promo2 .card .price { font-family: var(--font-heading); font-size: 2.4rem; font-weight: 600; color: var(--c-heading); line-height: 1; }
|
||||||
|
.promo2 .card .price-old { font-size: 1.05rem; color: var(--c-text-light); text-decoration: line-through; }
|
||||||
|
.promo2 .card .unit { font-size: .78rem; text-transform: uppercase; letter-spacing: .08em; color: var(--c-text-light); margin: 0 0 22px; }
|
||||||
|
.promo2 .card ul { list-style: none; padding: 22px 0 0; margin: 0 0 26px; border-top: 1px solid #f0eae1; display: flex; flex-direction: column; gap: 11px; }
|
||||||
|
.promo2 .card li { font-size: .9rem; line-height: 1.5; color: var(--c-text); display: flex; gap: 10px; }
|
||||||
|
.promo2 .card li::before { content: "—"; color: var(--c-accent); font-weight: 700; flex-shrink: 0; }
|
||||||
|
.promo2 .card .buy { margin-top: auto; justify-content: center; }
|
||||||
|
.promo2 .validity-note { text-align: center; font-size: .82rem; color: var(--c-text-light); margin: 34px auto 0; max-width: 560px; }
|
||||||
|
|
||||||
|
/* FAQ */
|
||||||
|
.promo2 .faq-wrap { max-width: 760px; }
|
||||||
|
.promo2 .faq-title { text-align: center; text-transform: uppercase; letter-spacing: .1em; font-size: clamp(1.5rem, 3vw, 2rem); margin: 0 0 44px; }
|
||||||
|
.promo2 .faq-block { border-top: 1px solid #e4ded6; padding: 20px 4px; transition: padding .2s; }
|
||||||
|
.promo2 .faq-block:last-child { border-bottom: 1px solid #e4ded6; }
|
||||||
|
.promo2 .faq-q { display: flex; justify-content: space-between; align-items: center; gap: 24px; cursor: pointer; }
|
||||||
|
.promo2 .faq-q p { margin: 0; font-family: var(--font-heading); font-size: 1rem; font-weight: 500; color: var(--c-heading); letter-spacing: .02em; }
|
||||||
|
.promo2 .faq-q:focus-visible { outline: 2px solid var(--c-accent-dark); outline-offset: 4px; }
|
||||||
|
.promo2 .faq-toggle {
|
||||||
|
width: 32px; height: 32px; border-radius: 50%; border: 1px solid #d8cfc2; display: flex;
|
||||||
|
align-items: center; justify-content: center; flex-shrink: 0; font-size: 17px; color: var(--c-heading);
|
||||||
|
transition: background .2s ease, color .2s ease, border-color .2s ease;
|
||||||
|
}
|
||||||
|
.promo2 .faq-block.is-open .faq-toggle { background: var(--c-accent); color: #fff; border-color: var(--c-accent); }
|
||||||
|
.promo2 .faq-a { display: none; margin: 14px 0 0; font-size: .94rem; line-height: 1.75; color: var(--c-text); max-width: 640px; }
|
||||||
|
.promo2 .faq-block.is-open .faq-a { display: block; }
|
||||||
|
.promo2 .faq-a .hl { color: var(--c-accent-dark); font-weight: 600; }
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.promo2 .cards { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user