Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ed7169b88 |
@@ -14,8 +14,5 @@ export default defineConfig({
|
|||||||
redirects: {
|
redirects: {
|
||||||
'/programs': { status: 301, destination: '/services' },
|
'/programs': { status: 301, destination: '/services' },
|
||||||
'/programs/[slug]': { status: 301, destination: '/services/[slug]' },
|
'/programs/[slug]': { status: 301, destination: '/services/[slug]' },
|
||||||
// Modifiche 02: "Pilates Flow" → "Profilazione". "Rehab" esce dalla griglia Servizi e
|
|
||||||
// diventa un riquadro Training, ma la pagina dettaglio resta su /services/rehab.
|
|
||||||
'/services/pilates-flow': { status: 301, destination: '/services/profilazione' },
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 80 KiB |
@@ -25,15 +25,11 @@ const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div class="method__cta" data-reveal>
|
|
||||||
<a class="btn btn--dark" href="/contact"><T tag="home.method.cta" as="span" /></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.method__note { text-align: right; font-size: .8rem; color: var(--c-text-light); max-width: 260px; margin-left: auto; }
|
.method__note { text-align: right; font-size: .8rem; color: var(--c-text-light); max-width: 260px; margin-left: auto; }
|
||||||
.method__cta { text-align: center; margin-top: 48px; }
|
|
||||||
.method__row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 30px; text-align: center; position: relative; }
|
.method__row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 30px; text-align: center; position: relative; }
|
||||||
.method__circle { position: relative; width: 150px; height: 150px; margin: 0 auto 20px; border: 1px dashed var(--c-accent-dark); border-radius: 50%; padding: 8px; }
|
.method__circle { position: relative; width: 150px; height: 150px; margin: 0 auto 20px; border: 1px dashed var(--c-accent-dark); border-radius: 50%; padding: 8px; }
|
||||||
.method__circle img { border-radius: 50%; width: 100%; height: 100%; object-fit: cover; }
|
.method__circle img { border-radius: 50%; width: 100%; height: 100%; object-fit: cover; }
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const isSwapRow = (i: number) => Math.floor(i / 2) % 2 === 1;
|
|||||||
{trainings.map((tr, i) => {
|
{trainings.map((tr, i) => {
|
||||||
const img = imgOf(tr.image);
|
const img = imgOf(tr.image);
|
||||||
const card = (
|
const card = (
|
||||||
<div class:list={['tcards__cell', `tcards__cell--${tones[i % tones.length]}`]} data-reveal="fade" style={`--reveal-delay:${(i % 2) * 0.15}s`}>
|
<div class:list={['tcards__cell', `tcards__cell--${tones[i]}`]} data-reveal="fade" style={`--reveal-delay:${(i % 2) * 0.15}s`}>
|
||||||
<T tag={`training.${tr.id}.title`} as="h3" />
|
<T tag={`training.${tr.id}.title`} as="h3" />
|
||||||
<T tag={`training.${tr.id}.caption`} as="p" />
|
<T tag={`training.${tr.id}.caption`} as="p" />
|
||||||
<a class="tcards__link" href={tr.cta.href}><T tag="home.training-cards.link-label" as="span" /> →</a>
|
<a class="tcards__link" href={tr.cta.href}><T tag="home.training-cards.link-label" as="span" /> →</a>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const contentSeed: SeedEntry[] = [
|
|||||||
T('global.nav.3.label', site.navigation[2].label, []),
|
T('global.nav.3.label', site.navigation[2].label, []),
|
||||||
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, []),
|
||||||
|
|
||||||
// --- Header ---
|
// --- Header ---
|
||||||
T('header.logo.alt', 'InsanityLab', []),
|
T('header.logo.alt', 'InsanityLab', []),
|
||||||
@@ -48,8 +49,6 @@ export const contentSeed: SeedEntry[] = [
|
|||||||
T('footer.training.2.label', site.footerTraining[1].label, []),
|
T('footer.training.2.label', site.footerTraining[1].label, []),
|
||||||
T('footer.training.3.label', site.footerTraining[2].label, []),
|
T('footer.training.3.label', site.footerTraining[2].label, []),
|
||||||
T('footer.training.4.label', site.footerTraining[3].label, []),
|
T('footer.training.4.label', site.footerTraining[3].label, []),
|
||||||
T('footer.training.5.label', site.footerTraining[4].label, []),
|
|
||||||
T('footer.training.6.label', site.footerTraining[5].label, []),
|
|
||||||
T('footer.services.1.label', site.footerServices[0].label, []),
|
T('footer.services.1.label', site.footerServices[0].label, []),
|
||||||
T('footer.services.2.label', site.footerServices[1].label, []),
|
T('footer.services.2.label', site.footerServices[1].label, []),
|
||||||
T('footer.services.3.label', site.footerServices[2].label, []),
|
T('footer.services.3.label', site.footerServices[2].label, []),
|
||||||
@@ -138,7 +137,6 @@ export const contentSeed: SeedEntry[] = [
|
|||||||
|
|
||||||
// --- Home: metodo (data-driven da method.ts) ---
|
// --- Home: metodo (data-driven da method.ts) ---
|
||||||
H('home.method.title', 'Il metodo<br />InsanityLab'),
|
H('home.method.title', 'Il metodo<br />InsanityLab'),
|
||||||
T('home.method.cta', 'Contattaci', []),
|
|
||||||
H('method.intro', methodIntro.replace('Metodo Insanitylab', '<strong>Metodo Insanitylab</strong>')),
|
H('method.intro', methodIntro.replace('Metodo Insanitylab', '<strong>Metodo Insanitylab</strong>')),
|
||||||
T('method.extra-note', methodExtraNote, []),
|
T('method.extra-note', methodExtraNote, []),
|
||||||
...methodSteps.flatMap((s) => [
|
...methodSteps.flatMap((s) => [
|
||||||
@@ -179,14 +177,14 @@ export const contentSeed: SeedEntry[] = [
|
|||||||
|
|
||||||
// --- About: about ---
|
// --- About: about ---
|
||||||
T('about.about.title', 'About'),
|
T('about.about.title', 'About'),
|
||||||
T('about.about.body', "InsanityLab nasce dalla visione di creare uno spazio dove il fitness incontrasse la salute in modo reale e profondo, non superficiale. L'idea di partenza era semplice ma rivoluzionaria nel settore: il movimento non dovrebbe essere vissuto come un sacrificio estetico, ma come un atto di cura verso se stessi. Da questa convinzione è nato un laboratorio esclusivo di benessere, pensato da chi conosce il corpo in profondità — non solo come semplici trainer, ma come osteopati e chinesiologi esperti del movimento."),
|
T('about.about.body', "InsanityLab nasce con l'obiettivo di unire fitness e salute in un unico percorso dedicato al benessere globale, superando il concetto tradizionale di palestra. Un laboratorio esclusivo, guidato da osteopati e chinesiologi e professionisti specializzati, dove movimento, prevenzione e performance convivono all'interno di un approccio personalizzato e consapevole."),
|
||||||
I('about.about.image'),
|
I('about.about.image'),
|
||||||
|
|
||||||
// --- About: convenzioni ---
|
// --- About: convenzioni ---
|
||||||
T('about.convenzioni.title', 'Convenzioni'),
|
T('about.convenzioni.title', 'Convenzioni'),
|
||||||
T('about.convenzioni.body', "InsanityLab collabora con aziende, enti e organizzazioni che desiderano investire concretamente nel benessere delle proprie persone, attraverso programmi dedicati alla salute, al movimento e alla qualità della vita. Le convenzioni nascono con l'obiettivo di portare il valore del benessere anche all'interno del contesto professionale, creando percorsi che favoriscano equilibrio fisico, prevenzione e performance quotidiana."),
|
T('about.convenzioni.body', "InsanityLab collabora con aziende, enti e organizzazioni che desiderano investire concretamente nel benessere delle proprie persone, attraverso programmi dedicati alla salute, al movimento e alla qualità della vita. Le convenzioni nascono con l'obiettivo di portare il valore del benessere anche all'interno del contesto professionale, creando percorsi che favoriscano equilibrio fisico, prevenzione e performance quotidiana."),
|
||||||
T('about.convenzioni.item1', 'Le realtà convenzionate possono accedere a programmi di welfare aziendale personalizzati.'),
|
T('about.convenzioni.item1', 'Le realtà convenzionate possono accedere a programmi di welfare aziendale personalizzati, con percorsi individuali, small group training e classi di gruppo seguiti da professionisti qualificati.'),
|
||||||
T('about.convenzioni.item2', 'Ogni collaborazione parte da una consulenza preliminare per analizzare esigenze e obiettivi, così da sviluppare soluzioni efficaci e sostenibili.'),
|
T('about.convenzioni.item2', 'Ogni collaborazione parte da una consulenza gratuita preliminare per analizzare esigenze e obiettivi, così da sviluppare soluzioni efficaci e sostenibili.'),
|
||||||
T('about.convenzioni.item3', "Le convenzioni vengono definite su misura insieme a InsanityLab, in base alle caratteristiche e alle necessità dell'azienda partner."),
|
T('about.convenzioni.item3', "Le convenzioni vengono definite su misura insieme a InsanityLab, in base alle caratteristiche e alle necessità dell'azienda partner."),
|
||||||
T('about.convenzioni.cta-text', 'Per ricevere maggiori informazioni o attivare una convenzione:', []),
|
T('about.convenzioni.cta-text', 'Per ricevere maggiori informazioni o attivare una convenzione:', []),
|
||||||
I('about.convenzioni.image'),
|
I('about.convenzioni.image'),
|
||||||
@@ -204,7 +202,7 @@ export const contentSeed: SeedEntry[] = [
|
|||||||
|
|
||||||
// --- About: studios ---
|
// --- About: studios ---
|
||||||
T('about.studios.title', 'In-Sanity Studios'),
|
T('about.studios.title', 'In-Sanity Studios'),
|
||||||
T('about.studios.body1', "IN-SANITY STUDIOS è il nuovo spazio professionale nato dall'evoluzione di InsanityLab, dedicato ai professionisti della salute e del benessere."),
|
T('about.studios.body1', "IN-SANITY STUDIOS è il nuovo spazio professionale nato dall'evoluzione di InsanityLab, dedicato ai professionisti della salute, del benessere e della performance. Più che un semplice insieme di studi professionali, rappresenta un ecosistema multidisciplinare pensato per favorire collaborazione, qualità del servizio e integrazione tra competenze diverse."),
|
||||||
T('about.studios.body2', "Il progetto nasce con l'obiettivo di offrire a liberi professionisti uno spazio elegante, riservato e già attrezzato, in cui poter svolgere la propria attività all'interno di un contesto consolidato e altamente qualificato, senza dover affrontare i costi e le complessità di una struttura indipendente."),
|
T('about.studios.body2', "Il progetto nasce con l'obiettivo di offrire a liberi professionisti uno spazio elegante, riservato e già attrezzato, in cui poter svolgere la propria attività all'interno di un contesto consolidato e altamente qualificato, senza dover affrontare i costi e le complessità di una struttura indipendente."),
|
||||||
T('about.studios.body3', 'IN-SANITY STUDIOS si rivolge a figure specializzate nel settore salute e wellness, come fisioterapisti, osteopati, psicologi, nutrizionisti, massaggiatori e altri professionisti dedicati alla cura della persona. Ogni studio è progettato per garantire comfort, professionalità e continuità operativa, all\'interno di un ambiente coerente con la filosofia di InsanityLab: mettere il benessere globale della persona al centro del percorso.'),
|
T('about.studios.body3', 'IN-SANITY STUDIOS si rivolge a figure specializzate nel settore salute e wellness, come fisioterapisti, osteopati, psicologi, nutrizionisti, massaggiatori e altri professionisti dedicati alla cura della persona. Ogni studio è progettato per garantire comfort, professionalità e continuità operativa, all\'interno di un ambiente coerente con la filosofia di InsanityLab: mettere il benessere globale della persona al centro del percorso.'),
|
||||||
I('about.studios.image'),
|
I('about.studios.image'),
|
||||||
@@ -228,7 +226,7 @@ export const contentSeed: SeedEntry[] = [
|
|||||||
T('about.timeline.stage3.text', 'Contenuto in arrivo: il cliente fornirà il testo di questa tappa.'),
|
T('about.timeline.stage3.text', 'Contenuto in arrivo: il cliente fornirà il testo di questa tappa.'),
|
||||||
// --- Services: lista (/services) ---
|
// --- Services: lista (/services) ---
|
||||||
T('services.meta.title', 'Servizi', []),
|
T('services.meta.title', 'Servizi', []),
|
||||||
T('services.meta.description', 'I servizi InsanityLab: Personal Training, Performance Class, Coaching, Profilazione, Osteopatia e Nutrizione.', []),
|
T('services.meta.description', 'I servizi InsanityLab: Personal Training, Performance Class, Coaching, Pilates Flow, Rehab e Nutrizione.', []),
|
||||||
T('services.hero.title', 'Servizi', []),
|
T('services.hero.title', 'Servizi', []),
|
||||||
T('services.intro.title', 'Servizi'),
|
T('services.intro.title', 'Servizi'),
|
||||||
T('services.intro.body', "Questi sono i servizi delle classi di Insanity Lab selezionati per voi. Abbiamo scelto i migliori percorsi per offrirvi un'esperienza di allenamento completa e mirata."),
|
T('services.intro.body', "Questi sono i servizi delle classi di Insanity Lab selezionati per voi. Abbiamo scelto i migliori percorsi per offrirvi un'esperienza di allenamento completa e mirata."),
|
||||||
@@ -275,7 +273,7 @@ export const contentSeed: SeedEntry[] = [
|
|||||||
|
|
||||||
// --- Training: meta, hero, intro, trial ---
|
// --- Training: meta, hero, intro, trial ---
|
||||||
T('training.meta.title', 'Training', []),
|
T('training.meta.title', 'Training', []),
|
||||||
T('training.meta.description', 'One to One, Small Group, Rehab, Holistic Class, Reformer Class e Coaching: trova il training InsanityLab più adatto a te.', []),
|
T('training.meta.description', 'One to One, Small Groups, Performance Class e Fit Remote: trova il training InsanityLab più adatto a te.', []),
|
||||||
T('training.hero.title', 'training', []),
|
T('training.hero.title', 'training', []),
|
||||||
T('training.intro.title', 'Training'),
|
T('training.intro.title', 'Training'),
|
||||||
T('training.intro.subtitle', 'Porta i tuoi allenamenti al livello successivo: programmi completi e percorsi su misura. Trova il training più adatto alle tue esigenze.'),
|
T('training.intro.subtitle', 'Porta i tuoi allenamenti al livello successivo: programmi completi e percorsi su misura. Trova il training più adatto alle tue esigenze.'),
|
||||||
|
|||||||
@@ -6,10 +6,4 @@ export const serviceIcons: Record<string, string> = {
|
|||||||
'pilates-flow': 'M12 4a2 2 0 110 4 2 2 0 010-4zM6 20c2-6 10-6 12 0M12 8v6',
|
'pilates-flow': 'M12 4a2 2 0 110 4 2 2 0 010-4zM6 20c2-6 10-6 12 0M12 8v6',
|
||||||
'rehab': 'M12 21C7 17 3 13 3 9a5 5 0 019-3 5 5 0 019 3c0 4-4 8-9 12z',
|
'rehab': 'M12 21C7 17 3 13 3 9a5 5 0 019-3 5 5 0 019 3c0 4-4 8-9 12z',
|
||||||
'nutrition': 'M12 3c4 0 7 3 7 8s-3 10-7 10-7-5-7-10 3-8 7-8zm0-1v4',
|
'nutrition': 'M12 3c4 0 7 3 7 8s-3 10-7 10-7-5-7-10 3-8 7-8zm0-1v4',
|
||||||
// Servizi rinominati (modifiche 02): profilazione (grafico/analisi), osteopatia (mani)
|
|
||||||
'profilazione': 'M4 20V6m5 14V4m5 16v-8m5 8V9M3 20h18',
|
|
||||||
'osteopatia': 'M8 3v7a4 4 0 008 0V3M6 14c0 4 3 7 6 7s6-3 6-7',
|
|
||||||
// Riquadri Training con pagina dettaglio
|
|
||||||
'holistic-class': 'M12 3a3 3 0 110 6 3 3 0 010-6zm0 6v7m-4 3l4-3 4 3',
|
|
||||||
'reformer-class': 'M3 8h18M3 16h18M6 8v8m12-8v8M9 12h6',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ export interface Service {
|
|||||||
features: string[];
|
features: string[];
|
||||||
faq: { q: string; a: string }[];
|
faq: { q: string; a: string }[];
|
||||||
hasPricing: boolean;
|
hasPricing: boolean;
|
||||||
/** false = pagina dettaglio esiste ma la voce non compare nella griglia /services
|
|
||||||
* (usata come approfondimento dai riquadri Training: Rehab, Holistic, Reformer). */
|
|
||||||
grid?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const services: Service[] = [
|
export const services: Service[] = [
|
||||||
@@ -59,32 +56,30 @@ export const services: Service[] = [
|
|||||||
hasPricing: false,
|
hasPricing: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// NB: sostituisce "Pilates Flow" (il Pilates confluisce nella Holistic Class in Training).
|
slug: "pilates-flow",
|
||||||
// Copy BOZZA basata sul metodo ISL (profilazione fisica) — da confermare col cliente.
|
title: "Pilates Flow",
|
||||||
slug: "profilazione",
|
subtitle: "Movimento e controllo",
|
||||||
title: "Profilazione",
|
excerpt: "Il Pilates Flow migliora postura, core e consapevolezza corporea con un movimento fluido, mentre l’osteopatia supporta recupero, prevenzione e benessere globale integrandosi con l’allenamento.",
|
||||||
subtitle: "Check-up e valutazione fisica",
|
longDescription: "Il Pilates Flow unisce movimento fluido e controllo: migliora postura, core e consapevolezza corporea. Integrato con l’osteopatia, supporta recupero, prevenzione e benessere globale all’interno del percorso di allenamento.",
|
||||||
excerpt: "La profilazione fisica è il punto di partenza del metodo InsanityLab: una valutazione completa che definisce lo stato di salute iniziale e le priorità del percorso.",
|
features: ["Postura e core", "Movimento fluido e controllo", "Integrazione con osteopatia", "Benessere globale"],
|
||||||
longDescription: "Attraverso check-up, plicometria e analisi antropometriche misuriamo composizione corporea, postura e stato di forma. I dati raccolti guidano la costruzione di un programma personalizzato e permettono di monitorare i progressi nel tempo.",
|
|
||||||
features: ["Check-up fisico iniziale", "Plicometria e analisi antropometriche", "Definizione delle priorità del percorso", "Monitoraggio periodico dei progressi"],
|
|
||||||
faq: [
|
faq: [
|
||||||
{ q: "Quando si fa la profilazione?", a: "All’inizio del percorso e poi periodicamente, per misurare i progressi e aggiornare il programma." },
|
{ q: "A chi è adatto il Pilates Flow?", a: "A chiunque voglia migliorare postura, mobilità e controllo del movimento, a ogni livello." },
|
||||||
{ q: "Che cosa comprende?", a: "Check-up fisico, plicometria e analisi antropometriche; alcune misurazioni avanzate sono disponibili su richiesta." },
|
{ q: "Serve esperienza precedente?", a: "No: le progressioni sono graduali e seguite da professionisti qualificati." },
|
||||||
|
{ q: "Come si integra con l’osteopatia?", a: "Il lavoro osteopatico supporta recupero e prevenzione, in sinergia con le sessioni di movimento." },
|
||||||
],
|
],
|
||||||
hasPricing: false,
|
hasPricing: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// NB: sostituisce "Rehab" nella pagina Servizi (il Rehab si sposta in Training).
|
slug: "rehab",
|
||||||
slug: "osteopatia",
|
title: "Rehab",
|
||||||
title: "Osteopatia",
|
subtitle: "Recupero post-infortunio, patologie muscolo-scheletriche",
|
||||||
subtitle: "Approccio manuale globale non invasivo",
|
excerpt: "Il Rehab InsanityLab è un percorso supervisionato che accompagna dal recupero riabilitativo al ritorno all’attività fisica in modo sicuro, progressivo e personalizzato.",
|
||||||
excerpt: "L’osteopatia è una delle colonne portanti dell’approccio integrato di InsanityLab. Non è un servizio aggiunto: è parte della visione.",
|
longDescription: "Il Rehab accompagna dal recupero post-infortunio o da patologie muscolo-scheletriche fino al ritorno all’attività fisica. Percorso supervisionato da chinesiologi e osteopati, sicuro, progressivo e personalizzato.",
|
||||||
longDescription: "L’osteopatia è indicata sia in modo preventivo, per chi vuole mantenere il proprio equilibrio corporeo, sia in modo curativo, per chi sta affrontando un problema specifico. È particolarmente utile in sinergia con l’allenamento, perché permette di lavorare su compensi e rigidità che altrimenti limiterebbero i progressi. Nel nostro approccio rientrano anche l’osteopatia neonatale e pediatrica.",
|
features: ["Recupero post-infortunio", "Patologie muscolo-scheletriche", "Supervisione di chinesiologi e osteopati", "Progressione sicura e personalizzata"],
|
||||||
features: ["Dolori acuti e cronici (cervicale, lombare, articolazioni)", "Tensioni muscolari e fasciali", "Problemi posturali", "Cefalee", "Disturbi viscerali correlati a tensioni strutturali", "Recupero post-traumatico"],
|
|
||||||
faq: [
|
faq: [
|
||||||
{ q: "L’osteopatia è preventiva o curativa?", a: "Entrambe: è utile sia per mantenere l’equilibrio corporeo sia per affrontare un problema specifico, in sinergia con l’allenamento." },
|
{ q: "Chi segue il percorso Rehab?", a: "Professionisti specializzati in rieducazione funzionale ed esercizio adattato, in sinergia con l’area osteopatica." },
|
||||||
{ q: "Trattate anche neonati e bambini?", a: "Sì, il nostro approccio comprende anche l’osteopatia neonatale e pediatrica." },
|
{ q: "Serve una diagnosi medica?", a: "Se disponibile è utile portarla alla prima consulenza; il percorso si integra con le indicazioni dei sanitari." },
|
||||||
{ q: "Serve una prescrizione medica?", a: "Non è necessaria; se disponibile, eventuale documentazione clinica è utile alla prima valutazione." },
|
{ q: "Quanto dura il percorso?", a: "Dipende dalla condizione di partenza: la progressione viene definita e aggiornata durante il percorso." },
|
||||||
],
|
],
|
||||||
hasPricing: false,
|
hasPricing: false,
|
||||||
},
|
},
|
||||||
@@ -102,52 +97,6 @@ export const services: Service[] = [
|
|||||||
],
|
],
|
||||||
hasPricing: false,
|
hasPricing: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
// Approfondimento del riquadro Training "Rehab" (fuori dalla griglia /services).
|
|
||||||
slug: "rehab",
|
|
||||||
title: "Rehab",
|
|
||||||
subtitle: "Recupero post-infortunio, patologie muscolo-scheletriche",
|
|
||||||
excerpt: "Accompagniamo il corpo nel recupero della sua funzionalità attraverso il movimento guidato: il ponte tra la fine del percorso medico-riabilitativo e il ritorno a una vita attiva piena.",
|
|
||||||
longDescription: "Il Rehab InsanityLab significa accompagnare il corpo nel recupero della sua funzionalità attraverso il movimento guidato. Non è fisioterapia in senso clinico: è il ponte tra la fine del percorso medico-riabilitativo e il ritorno a una vita attiva piena, con la stessa cura scientifica ma in un contesto di allenamento. Il servizio lavora con un ampio spettro di condizioni e ogni situazione viene valutata singolarmente con metodologie evidence based.",
|
|
||||||
features: ["Recupero post-chirurgico e post-traumatico ortopedico", "Problemi posturali e motori", "Lombalgia e dolori cronici", "Ipertensione e patologie metaboliche", "Lipedema", "Gravidanza e periodo post-parto", "Attività motoria adattata per anziani (AFA)"],
|
|
||||||
faq: [
|
|
||||||
{ q: "Chi segue il percorso Rehab?", a: "Professionisti specializzati in rieducazione funzionale ed esercizio adattato, in sinergia con medici specialisti, fisioterapisti e osteopati." },
|
|
||||||
{ q: "Serve una diagnosi medica?", a: "Se disponibile è utile portarla alla prima consulenza; il percorso si integra con le indicazioni dei sanitari." },
|
|
||||||
{ q: "Quanto dura il percorso?", a: "Dipende dalla condizione di partenza: la progressione viene definita e aggiornata durante il percorso." },
|
|
||||||
],
|
|
||||||
hasPricing: false,
|
|
||||||
grid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Approfondimento del riquadro Training "Holistic Class" (ex Pilates Flow / Performance Class).
|
|
||||||
slug: "holistic-class",
|
|
||||||
title: "Holistic Class",
|
|
||||||
subtitle: "Classi fino a 8 persone con trainer certificato",
|
|
||||||
excerpt: "Mobilità articolare, respirazione, equilibrio. Da sola o integrata nei tuoi allenamenti, adatta a tutti i livelli. Sessioni da 40 minuti.",
|
|
||||||
longDescription: "La Holistic Class lavora su mobilità articolare, respirazione ed equilibrio. Puoi seguirla da sola o integrarla nei tuoi allenamenti; è adatta a tutti i livelli e ogni sessione dura 40 minuti. Comprende lavoro di Pilates Flow, Mobility e Yoga.",
|
|
||||||
features: ["Pilates Flow", "Mobility", "Yoga", "Sessioni da 40 minuti", "Adatta a tutti i livelli"],
|
|
||||||
faq: [
|
|
||||||
{ q: "A chi è adatta la Holistic Class?", a: "A chiunque voglia migliorare mobilità, respirazione ed equilibrio, a ogni livello di partenza." },
|
|
||||||
{ q: "Posso integrarla con altri allenamenti?", a: "Sì: funziona sia come percorso a sé sia come complemento al tuo programma di allenamento." },
|
|
||||||
],
|
|
||||||
hasPricing: false,
|
|
||||||
grid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Approfondimento del riquadro Training "Reformer Class" (nuova).
|
|
||||||
slug: "reformer-class",
|
|
||||||
title: "Reformer Class",
|
|
||||||
subtitle: "Classi fino a 5 persone con macchinari reformer",
|
|
||||||
excerpt: "Core, postura e mobilità su macchina reformer. Lavoro a basso impatto sulle articolazioni, adatto a tutti i livelli. Lezioni da 40 minuti.",
|
|
||||||
longDescription: "La Reformer Class utilizza i macchinari reformer per il pilates in classi fino a 5 persone. Il lavoro è dedicato a core, postura e mobilità, a basso impatto sulle articolazioni e adatto a tutti i livelli. Ogni lezione dura 40 minuti.",
|
|
||||||
features: ["Macchinari reformer", "Core, postura e mobilità", "Lavoro a basso impatto sulle articolazioni", "Classi fino a 5 persone", "Lezioni da 40 minuti"],
|
|
||||||
faq: [
|
|
||||||
{ q: "Serve esperienza con il reformer?", a: "No: le progressioni sono graduali e seguite da trainer certificati, adatte anche a chi inizia." },
|
|
||||||
{ q: "Quante persone per classe?", a: "Massimo 5, per garantire attenzione e correzione costante." },
|
|
||||||
],
|
|
||||||
hasPricing: false,
|
|
||||||
grid: false,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getService(slug: string): Service | undefined {
|
export function getService(slug: string): Service | undefined {
|
||||||
|
|||||||
@@ -16,21 +16,20 @@ export const site = {
|
|||||||
{ label: "Servizi", href: "/services" },
|
{ label: "Servizi", href: "/services" },
|
||||||
{ label: "Blog & Edugo", href: "/blog" },
|
{ label: "Blog & Edugo", href: "/blog" },
|
||||||
{ label: "Contatti", href: "/contact" },
|
{ label: "Contatti", href: "/contact" },
|
||||||
|
{ label: "Pacchetti", href: "/pacchetti" },
|
||||||
],
|
],
|
||||||
footerTraining: [
|
footerTraining: [
|
||||||
{ label: "One to one", href: "/training#one-to-one" },
|
{ label: "One to one", href: "/training#one-to-one" },
|
||||||
{ label: "Small group", href: "/training#small-groups" },
|
{ label: "Small groups", href: "/training#small-groups" },
|
||||||
{ label: "Rehab", href: "/training#rehab" },
|
{ label: "Performance class", href: "/training#performance-class" },
|
||||||
{ label: "Holistic class", href: "/training#holistic-class" },
|
{ label: "Fit remote", href: "/training#fit-remote" },
|
||||||
{ label: "Reformer class", href: "/training#reformer-class" },
|
|
||||||
{ label: "Coaching", href: "/training#coaching" },
|
|
||||||
],
|
],
|
||||||
footerServices: [
|
footerServices: [
|
||||||
{ label: "Personal Training", href: "/services/personal-training" },
|
{ label: "Personal Training", href: "/services/personal-training" },
|
||||||
{ label: "Performance Class", href: "/services/performance-class" },
|
{ label: "Performance Class", href: "/services/performance-class" },
|
||||||
{ label: "Coaching", href: "/services/coaching" },
|
{ label: "Coaching", href: "/services/coaching" },
|
||||||
{ label: "Profilazione", href: "/services/profilazione" },
|
{ label: "Rehab", href: "/services/rehab" },
|
||||||
{ label: "Osteopatia", href: "/services/osteopatia" },
|
{ label: "Pilates Flow", href: "/services/pilates-flow" },
|
||||||
{ label: "Nutrizione", href: "/services/nutrition" },
|
{ label: "Nutrizione", href: "/services/nutrition" },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
export const trainings = [
|
export const trainings = [
|
||||||
{ id: "one-to-one", title: "One to One", caption: "Allenamento con un trainer dedicato", image: "training-one-to-one", description: "Allenati con un trainer dedicato che costruisce un percorso su misura per i tuoi obiettivi. Ogni sessione one to one è pensata per migliorare performance, tecnica e risultati. Supporto costante, attenzione totale e allenamenti personalizzati per dare il massimo.", cta: { label: "Contattaci", href: "/contact" } },
|
{ id: "one-to-one", title: "One to One", caption: "Allenamento con un trainer dedicato", image: "training-one-to-one", description: "Allenati con un trainer dedicato che costruisce un percorso su misura per i tuoi obiettivi. Ogni sessione one to one è pensata per migliorare performance, tecnica e risultati. Supporto costante, attenzione totale e allenamenti personalizzati per dare il massimo.", cta: { label: "Contattaci", href: "/contact" } },
|
||||||
{ id: "small-groups", title: "Small Group", caption: "Mini gruppi (max 4 persone) con trainer dedicato", image: "training-small-groups", description: "Allenati in mini gruppi da massimo 4 persone con un trainer dedicato. Un’esperienza small group che unisce attenzione personalizzata, motivazione e condivisione. Programmi mirati e supporto costante per raggiungere i tuoi obiettivi insieme.", cta: { label: "Contattaci", href: "/contact" } },
|
{ id: "small-groups", title: "Small Groups", caption: "Mini gruppi (max 3 persone) con trainer dedicato", image: "training-small-groups", description: "Allenati in mini gruppi da massimo 3 persone con un trainer dedicato. Un’esperienza small group che unisce attenzione personalizzata, motivazione e condivisione. Programmi mirati e supporto costante per raggiungere i tuoi obiettivi insieme.", cta: { label: "Contattaci", href: "/contact" } },
|
||||||
{ id: "rehab", title: "Rehab", caption: "Recupero funzionale attraverso il movimento guidato", image: "training-rehab", description: "Il Rehab InsanityLab accompagna il corpo nel recupero della sua funzionalità attraverso il movimento guidato: il ponte tra la fine del percorso medico-riabilitativo e il ritorno a una vita attiva piena, con la stessa cura scientifica ma in un contesto di allenamento.", cta: { label: "Scopri", href: "/services/rehab" } },
|
{ id: "performance-class", title: "Performance Class", caption: "Classi fino a 8 persone con trainer certificato", image: "training-performance-class", description: "Allenati in classi fino a 8 persone guidate da trainer certificati. Sessioni dinamiche ad alta intensità pensate per migliorare forza, resistenza e performance. Energia, motivazione e allenamenti strutturati per dare il massimo in ogni workout.", cta: { label: "Scopri", href: "/services/performance-class" } },
|
||||||
{ id: "holistic-class", title: "Holistic Class", caption: "Classi fino a 8 persone con trainer certificato", image: "training-holistic-class", description: "Mobilità articolare, respirazione, equilibrio. Da sola o integrata nei tuoi allenamenti, adatta a tutti i livelli. Sessioni da 40 minuti che uniscono Pilates Flow, Mobility e Yoga.", cta: { label: "Scopri", href: "/services/holistic-class" } },
|
{ id: "fit-remote", title: "Fit Remote", caption: "Piano di allenamento su misura, ovunque ti alleni.", image: "training-fit-remote", description: "Con Fit Remote hai un piano di allenamento su misura, ovunque ti alleni. Programmi personalizzati creati sui tuoi obiettivi, da seguire in palestra, a casa o in viaggio. Supporto costante e allenamenti pensati per adattarsi al tuo stile di vita.", cta: { label: "Contattaci", href: "/contact" } },
|
||||||
{ id: "reformer-class", title: "Reformer Class", caption: "Classi fino a 5 persone con macchinari reformer", image: "training-reformer-class", description: "Core, postura e mobilità su macchina reformer. Lavoro a basso impatto sulle articolazioni, adatto a tutti i livelli. Lezioni da 40 minuti in classi fino a 5 persone.", cta: { label: "Scopri", href: "/services/reformer-class" } },
|
|
||||||
{ id: "coaching", title: "Coaching", caption: "Programma su misura e monitoraggio, anche online", image: "training-coaching", description: "Con il coaching hai un programma di allenamento su misura e un monitoraggio costante, ovunque ti alleni: in palestra, a casa o in viaggio. Il percorso sviluppa autonomia e continuità, adattandosi al tuo stile di vita (anche online, formula Fit Remote).", cta: { label: "Scopri", href: "/services/coaching" } },
|
|
||||||
] as const;
|
] as const;
|
||||||
|
|||||||
@@ -114,47 +114,6 @@ export function createDb(path?: string): Database.Database {
|
|||||||
const ins = db.prepare('INSERT OR IGNORE INTO content_blocks (tag, type, value, guid) VALUES (?, ?, ?, ?)');
|
const ins = db.prepare('INSERT OR IGNORE INTO content_blocks (tag, type, value, guid) VALUES (?, ?, ?, ?)');
|
||||||
const syncTx = db.transaction(() => { for (const e of contentSeed) ins.run(e.tag, e.type, e.value, randomUUID()); });
|
const syncTx = db.transaction(() => { for (const e of contentSeed) ins.run(e.tag, e.type, e.value, randomUUID()); });
|
||||||
syncTx();
|
syncTx();
|
||||||
|
|
||||||
// Modifiche 02 (pagina Servizi): "Pilates Flow" → "Profilazione" e "Rehab" → "Osteopatia".
|
|
||||||
// I nuovi servizi hanno slug nuovi (tag service.profilazione.* / service.osteopatia.*) e
|
|
||||||
// vengono inseriti dal seed qui sopra. Restano da aggiornare le righe EXISTING il cui valore
|
|
||||||
// cambia: label del footer e meta description. UPDATE guardato dal vecchio valore → idempotente
|
|
||||||
// e non sovrascrive eventuali modifiche fatte dal pannello.
|
|
||||||
const SERVIZI_02: [string, string, string][] = [
|
|
||||||
['footer.services.4.label', 'Rehab', 'Profilazione'],
|
|
||||||
['footer.services.5.label', 'Pilates Flow', 'Osteopatia'],
|
|
||||||
['services.meta.description',
|
|
||||||
'I servizi InsanityLab: Personal Training, Performance Class, Coaching, Pilates Flow, Rehab e Nutrizione.',
|
|
||||||
'I servizi InsanityLab: Personal Training, Performance Class, Coaching, Profilazione, Osteopatia e Nutrizione.'],
|
|
||||||
// Pagina Training: riquadri passati a One to One, Small Group, Rehab, Holistic Class,
|
|
||||||
// Reformer Class, Coaching. Label footer + meta aggiornate (righe 5-6 sono nuovi tag).
|
|
||||||
['footer.training.2.label', 'Small groups', 'Small group'],
|
|
||||||
['footer.training.3.label', 'Performance class', 'Rehab'],
|
|
||||||
['footer.training.4.label', 'Fit remote', 'Holistic class'],
|
|
||||||
['training.meta.description',
|
|
||||||
'One to One, Small Groups, Performance Class e Fit Remote: trova il training InsanityLab più adatto a te.',
|
|
||||||
'One to One, Small Group, Rehab, Holistic Class, Reformer Class e Coaching: trova il training InsanityLab più adatto a te.'],
|
|
||||||
];
|
|
||||||
const upd02 = db.prepare('UPDATE content_blocks SET value = ? WHERE tag = ? AND value = ?');
|
|
||||||
const upd02Tx = db.transaction(() => { for (const [tag, oldV, newV] of SERVIZI_02) upd02.run(newV, tag, oldV); });
|
|
||||||
upd02Tx();
|
|
||||||
|
|
||||||
// Modifiche 02 (pagina Insanitylab/about): testi riscritti. Guardia via frammento LIKE
|
|
||||||
// distintivo del vecchio testo → robusto agli apostrofi tipografici, idempotente e
|
|
||||||
// rispettoso delle eventuali riscritture da pannello (se il frammento non c'è più, salta).
|
|
||||||
const ABOUT_02: [string, string, string][] = [
|
|
||||||
['about.about.body', '%unire fitness e salute in un unico percorso%',
|
|
||||||
"InsanityLab nasce dalla visione di creare uno spazio dove il fitness incontrasse la salute in modo reale e profondo, non superficiale. L'idea di partenza era semplice ma rivoluzionaria nel settore: il movimento non dovrebbe essere vissuto come un sacrificio estetico, ma come un atto di cura verso se stessi. Da questa convinzione è nato un laboratorio esclusivo di benessere, pensato da chi conosce il corpo in profondità — non solo come semplici trainer, ma come osteopati e chinesiologi esperti del movimento."],
|
|
||||||
['about.convenzioni.item1', '%small group training e classi di gruppo%',
|
|
||||||
'Le realtà convenzionate possono accedere a programmi di welfare aziendale personalizzati.'],
|
|
||||||
['about.convenzioni.item2', '%consulenza gratuita preliminare%',
|
|
||||||
'Ogni collaborazione parte da una consulenza preliminare per analizzare esigenze e obiettivi, così da sviluppare soluzioni efficaci e sostenibili.'],
|
|
||||||
['about.studios.body1', '%della performance. Più che un semplice insieme%',
|
|
||||||
"IN-SANITY STUDIOS è il nuovo spazio professionale nato dall'evoluzione di InsanityLab, dedicato ai professionisti della salute e del benessere."],
|
|
||||||
];
|
|
||||||
const updAbout = db.prepare('UPDATE content_blocks SET value = ? WHERE tag = ? AND value LIKE ?');
|
|
||||||
const updAboutTx = db.transaction(() => { for (const [tag, frag, newV] of ABOUT_02) updAbout.run(newV, tag, frag); });
|
|
||||||
updAboutTx();
|
|
||||||
// Backfill: righe pre-esistenti (già presenti prima dell'introduzione del guid) restano
|
// Backfill: righe pre-esistenti (già presenti prima dell'introduzione del guid) restano
|
||||||
// ignorate dall'INSERT OR IGNORE e hanno guid nullo → assegna un UUID a ciascuna.
|
// ignorate dall'INSERT OR IGNORE e hanno guid nullo → assegna un UUID a ciascuna.
|
||||||
const missing = db.prepare(`SELECT tag FROM content_blocks WHERE guid IS NULL OR guid = ''`).all() as { tag: string }[];
|
const missing = db.prepare(`SELECT tag FROM content_blocks WHERE guid IS NULL OR guid = ''`).all() as { tag: string }[];
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import type { ImageMetadata } from 'astro';
|
import type { ImageMetadata } from 'astro';
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import PageHero from '../components/PageHero.astro';
|
import PageHero from '../components/PageHero.astro';
|
||||||
|
import Timeline from '../components/about/Timeline.astro';
|
||||||
import { team } from '../data/team';
|
import { team } from '../data/team';
|
||||||
import T from '../components/content/T.astro';
|
import T from '../components/content/T.astro';
|
||||||
import TImg from '../components/content/TImg.astro';
|
import TImg from '../components/content/TImg.astro';
|
||||||
@@ -62,6 +63,8 @@ export const prerender = false;
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<Timeline />
|
||||||
|
|
||||||
<section class="section section--dark">
|
<section class="section section--dark">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-heading">
|
<div class="section-heading">
|
||||||
@@ -118,6 +121,25 @@ export const prerender = false;
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="section section--alt">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-heading">
|
||||||
|
<T tag="about.metodo.title" as="h2" />
|
||||||
|
<T tag="about.metodo.body" as="p" />
|
||||||
|
</div>
|
||||||
|
<div class="about-img-row">
|
||||||
|
<div class="about-img-row__text section-heading">
|
||||||
|
<T tag="about.metodo.closing" as="p" />
|
||||||
|
</div>
|
||||||
|
{imgOf('about-team-group') && (
|
||||||
|
<div class="round-img">
|
||||||
|
<TImg tag="about.metodo.image" src={imgOf('about-team-group')!} alt="Il team InsanityLab" widths={[360, 720]} sizes="360px" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="about-img-row">
|
<div class="about-img-row">
|
||||||
@@ -136,6 +158,22 @@ export const prerender = false;
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="section section--alt">
|
||||||
|
<div class="container">
|
||||||
|
<div class="about-img-row">
|
||||||
|
{imgOf('about-vision') && (
|
||||||
|
<div class="round-img">
|
||||||
|
<TImg tag="about.vision.image" src={imgOf('about-vision')!} alt="La comunità InsanityLab" widths={[360, 720]} sizes="360px" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div class="about-img-row__text section-heading">
|
||||||
|
<T tag="about.vision.title" as="h2" />
|
||||||
|
<T tag="about.vision.body1" as="p" />
|
||||||
|
<T tag="about.vision.body2" as="p" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const prerender = false;
|
|||||||
const service = getService(Astro.params.slug!);
|
const service = getService(Astro.params.slug!);
|
||||||
if (!service) return new Response(null, { status: 404 });
|
if (!service) return new Response(null, { status: 404 });
|
||||||
const slug = service.slug;
|
const slug = service.slug;
|
||||||
const others = services.filter((p) => p.slug !== service.slug && p.grid !== false);
|
const others = services.filter((p) => p.slug !== service.slug);
|
||||||
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/program-*.jpg', { eager: true });
|
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/program-*.jpg', { eager: true });
|
||||||
const imgOf = (slug: string) => Object.entries(images).find(([p]) => p.includes(`program-${slug}.`))?.[1]?.default;
|
const imgOf = (slug: string) => Object.entries(images).find(([p]) => p.includes(`program-${slug}.`))?.[1]?.default;
|
||||||
const heroImg = imgOf(service.slug);
|
const heroImg = imgOf(service.slug);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import TImg from '../../components/content/TImg.astro';
|
|||||||
import { t } from '../../lib/content';
|
import { t } from '../../lib/content';
|
||||||
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/program-*.jpg', { eager: true });
|
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/program-*.jpg', { eager: true });
|
||||||
const imgOf = (slug: string) => Object.entries(images).find(([p]) => p.includes(`program-${slug}.`))?.[1]?.default;
|
const imgOf = (slug: string) => Object.entries(images).find(([p]) => p.includes(`program-${slug}.`))?.[1]?.default;
|
||||||
const gridServices = services.filter((p) => p.grid !== false);
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
---
|
---
|
||||||
<Base title={t('services.meta.title')} description={t('services.meta.description')}>
|
<Base title={t('services.meta.title')} description={t('services.meta.description')}>
|
||||||
@@ -20,7 +19,7 @@ export const prerender = false;
|
|||||||
<T tag="services.intro.body" as="p" />
|
<T tag="services.intro.body" as="p" />
|
||||||
</div>
|
</div>
|
||||||
<div class="plist">
|
<div class="plist">
|
||||||
{gridServices.map((p, i) => (
|
{services.map((p, i) => (
|
||||||
<article class="plist__card" data-reveal style={`--reveal-delay:${((i % 3) * 0.15).toFixed(2)}s`}>
|
<article class="plist__card" data-reveal style={`--reveal-delay:${((i % 3) * 0.15).toFixed(2)}s`}>
|
||||||
{imgOf(p.slug) && (
|
{imgOf(p.slug) && (
|
||||||
<a class="plist__photo" href={`/services/${p.slug}`}>
|
<a class="plist__photo" href={`/services/${p.slug}`}>
|
||||||
|
|||||||
@@ -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'];
|
const STATIC = ['/', '/about', '/contact', '/training', '/services', '/blog', '/pacchetti'];
|
||||||
|
|
||||||
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(/\/$/, '');
|
||||||
|
|||||||