fix: rimappa tutte le foto sugli slot corretti del design figma

Le immagini erano asset reali del .fig ma piazzate in slot sbagliati.
Rimappate home/training/about dal confronto con i PDF Figma; aggiunti
i 9 ritratti team about (chiari in home, scuri in about via imageAbout),
immagini mancanti per metodo/vision/convenzioni/studios/trial e hero
contatti. Fix imgOf (match esatto) e getImage per i background dedupati.
This commit is contained in:
2026-07-02 13:56:55 +02:00
parent 0d9b645636
commit 6cc1cf5f44
48 changed files with 82 additions and 37 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

+11 -3
View File
@@ -1,8 +1,12 @@
--- ---
interface Props { title: string; eyebrow?: string } import type { ImageMetadata } from 'astro';
const { title, eyebrow } = Astro.props; import { getImage } from 'astro:assets';
interface Props { title: string; eyebrow?: string; bgImage?: ImageMetadata }
const { title, eyebrow, bgImage } = Astro.props;
const bg = bgImage ? await getImage({ src: bgImage, width: 1920 }) : null;
--- ---
<section class="phero"> <section class:list={['phero', { 'phero--photo': bg }]} style={bg ? `background-image:url(${bg.src})` : undefined}>
<div class="container" data-reveal> <div class="container" data-reveal>
{eyebrow && <p class="eyebrow">{eyebrow}</p>} {eyebrow && <p class="eyebrow">{eyebrow}</p>}
<h1>{title}</h1> <h1>{title}</h1>
@@ -12,4 +16,8 @@ const { title, eyebrow } = Astro.props;
<style> <style>
.phero { background: var(--c-bg-alt); padding-block: 70px; } .phero { background: var(--c-bg-alt); padding-block: 70px; }
.phero h1 { text-transform: lowercase; font-weight: 500; margin: 0; } .phero h1 { text-transform: lowercase; font-weight: 500; margin: 0; }
.phero--photo { position: relative; background-size: cover; background-position: center; padding-block: 110px; }
.phero--photo::before { content: ''; position: absolute; inset: 0; background: rgba(46,32,24,.6); }
.phero--photo .container { position: relative; }
.phero--photo h1 { color: #fff; }
</style> </style>
+2 -2
View File
@@ -1,6 +1,6 @@
--- ---
import { Image } from 'astro:assets'; import { Image } from 'astro:assets';
import hero1 from '../../assets/img/hero-1.jpg'; import hero1 from '../../assets/img/hero-1.png';
const slides = [ const slides = [
{ title: ['PERFORMANCE.', 'BALANCE.', 'LONGEVITY.'], image: hero1, alt: 'Atleti InsanityLab' }, { title: ['PERFORMANCE.', 'BALANCE.', 'LONGEVITY.'], image: hero1, alt: 'Atleti InsanityLab' },
]; ];
@@ -33,7 +33,7 @@ const slides = [
.hero__in { display: grid; grid-template-columns: 1fr 1.2fr; align-items: center; min-height: calc(100vh - var(--header-h)); gap: 30px; } .hero__in { display: grid; grid-template-columns: 1fr 1.2fr; align-items: center; min-height: calc(100vh - var(--header-h)); gap: 30px; }
.hero__line { display: block; } .hero__line { display: block; }
.hero__text h1 { font-weight: 500; } .hero__text h1 { font-weight: 500; }
.hero__img { object-fit: cover; height: 100%; max-height: calc(100vh - var(--header-h)); width: 100%; } .hero__img { object-fit: contain; object-position: center bottom; height: 100%; max-height: calc(100vh - var(--header-h)); width: 100%; }
.hero__arrow { position: absolute; top: 50%; transform: translateY(-50%); background: none; border: 0; font-size: 3rem; color: var(--c-heading); cursor: pointer; } .hero__arrow { position: absolute; top: 50%; transform: translateY(-50%); background: none; border: 0; font-size: 3rem; color: var(--c-heading); cursor: pointer; }
.hero__arrow--prev { left: 12px; } .hero__arrow--next { right: 12px; } .hero__arrow--prev { left: 12px; } .hero__arrow--next { right: 12px; }
@media (max-width: 991px) { .hero__in { grid-template-columns: 1fr; padding-block: 40px; min-height: 0; } } @media (max-width: 991px) { .hero__in { grid-template-columns: 1fr; padding-block: 40px; min-height: 0; } }
+6 -3
View File
@@ -1,16 +1,19 @@
--- ---
import { getImage } from 'astro:assets';
import infoPerformance from '../../assets/img/info-performance.jpg'; import infoPerformance from '../../assets/img/info-performance.jpg';
import infoBalance from '../../assets/img/info-balance.jpg'; import infoBalance from '../../assets/img/info-balance.jpg';
import infoLongevity from '../../assets/img/info-longevity.jpg'; import infoLongevity from '../../assets/img/info-longevity.jpg';
const cols = [ // getImage garantisce l'emissione dell'asset anche quando il file è byte-identico
// ad altri import (Astro dedupa e non emette l'originale usato solo come background).
const cols = await Promise.all([
{ title: 'PERFORMANCE.', image: infoPerformance }, { title: 'PERFORMANCE.', image: infoPerformance },
{ title: 'BALANCE.', image: infoBalance }, { title: 'BALANCE.', image: infoBalance },
{ title: 'LONGEVITY.', image: infoLongevity }, { title: 'LONGEVITY.', image: infoLongevity },
]; ].map(async (c) => ({ ...c, bg: (await getImage({ src: c.image, width: 1200 })).src })));
--- ---
<section class="info" aria-label="pilastri"> <section class="info" aria-label="pilastri">
{cols.map((c, i) => ( {cols.map((c, i) => (
<a class="info__col" href="/programs" style={`background-image:url(${c.image.src});--reveal-delay:${i * 0.15}s`} data-reveal="fade"> <a class="info__col" href="/programs" style={`background-image:url(${c.bg});--reveal-delay:${i * 0.15}s`} data-reveal="fade">
<span class="info__title">{c.title}</span> <span class="info__title">{c.title}</span>
</a> </a>
))} ))}
+3 -1
View File
@@ -1,5 +1,7 @@
--- ---
import quoteBg from '../../assets/img/quote-bg.jpg'; import { getImage } from 'astro:assets';
import quoteBgSrc from '../../assets/img/quote-bg.jpg';
const quoteBg = await getImage({ src: quoteBgSrc, width: 1920 });
const quote = 'PERFORMANCE È SPINGERE OLTRE I PROPRI LIMITI, BALANCE È SAPERLI ASCOLTARE, LONGEVITY È COSTRUIRE NEL TEMPO CIÒ CHE DAVVERO CONTA.'; const quote = 'PERFORMANCE È SPINGERE OLTRE I PROPRI LIMITI, BALANCE È SAPERLI ASCOLTARE, LONGEVITY È COSTRUIRE NEL TEMPO CIÒ CHE DAVVERO CONTA.';
const quoteHtml = quote.replace(/(PERFORMANCE|BALANCE|LONGEVITY)/g, '<strong>$1</strong>'); const quoteHtml = quote.replace(/(PERFORMANCE|BALANCE|LONGEVITY)/g, '<strong>$1</strong>');
--- ---
+1 -1
View File
@@ -4,7 +4,7 @@ import type { ImageMetadata } from 'astro';
import { team, homeTeam } from '../../data/team'; import { team, homeTeam } from '../../data/team';
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/team-*.{jpg,png}', { eager: true }); const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/team-*.{jpg,png}', { eager: true });
const members = homeTeam.map((short) => team.find((m) => m.shortName === short)!).filter((m) => m.image); const members = homeTeam.map((short) => team.find((m) => m.shortName === short)!).filter((m) => m.image);
const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default; const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(`${name}.`))?.[1].default;
--- ---
<section class="section"> <section class="section">
<div class="container"> <div class="container">
+10 -10
View File
@@ -1,23 +1,23 @@
export interface Member { name: string; shortName: string; role: string; bio: string; image?: string; founder: boolean } export interface Member { name: string; shortName: string; role: string; bio: string; image?: string; imageAbout?: string; founder: boolean }
export const team: Member[] = [ export const team: Member[] = [
{ name: "Donata Dal Pastro", shortName: "Donata", role: "Chinesiologa e Osteopata", founder: true, image: "team-donata", { name: "Donata Dal Pastro", shortName: "Donata", role: "Chinesiologa e Osteopata", founder: true, imageAbout: "team-donata-dark", image: "team-donata",
bio: "Donata Dal Pastro è chinesiologa e osteopata specializzata in rieducazione funzionale, riatletizzazione sportiva ed esercizio adattato. Supervisiona larea dedicata a prevenzione, recupero e benessere, con competenze specifiche anche in pediatria e gravidanza." }, bio: "Donata Dal Pastro è chinesiologa e osteopata specializzata in rieducazione funzionale, riatletizzazione sportiva ed esercizio adattato. Supervisiona larea dedicata a prevenzione, recupero e benessere, con competenze specifiche anche in pediatria e gravidanza." },
{ name: "Nicola Antonelli", shortName: "Nicola", role: "Chinesiologo e Osteopata", founder: true, image: "team-nicola", { name: "Nicola Antonelli", shortName: "Nicola", role: "Chinesiologo e Osteopata", founder: true, imageAbout: "team-nicola-dark", image: "team-nicola",
bio: "Nicola Antonelli è chinesiologo e osteopata specializzato in performance sportiva, functional training e bodybuilding. Coordina lo sviluppo tecnico dei percorsi dedicati alla performance e allallenamento avanzato." }, bio: "Nicola Antonelli è chinesiologo e osteopata specializzato in performance sportiva, functional training e bodybuilding. Coordina lo sviluppo tecnico dei percorsi dedicati alla performance e allallenamento avanzato." },
{ name: "Eva Cassetta", shortName: "Eva", role: "Business Manager Balance & Strategy", founder: true, { name: "Eva Cassetta", shortName: "Eva", role: "Business Manager Balance & Strategy", founder: true, imageAbout: "team-eva-dark",
bio: "Eva Cassetta ricopre il ruolo di Business Manager Balance & Strategy e si occupa della gestione amministrativa, economica e finanziaria del progetto, garantendone solidità, organizzazione e sostenibilità nel tempo." }, bio: "Eva Cassetta ricopre il ruolo di Business Manager Balance & Strategy e si occupa della gestione amministrativa, economica e finanziaria del progetto, garantendone solidità, organizzazione e sostenibilità nel tempo." },
{ name: "Alessandro Lazzari", shortName: "Alessandro", role: "Trainer Functional & Movement", founder: false, { name: "Alessandro Lazzari", shortName: "Alessandro", role: "Trainer Functional & Movement", founder: false, imageAbout: "team-alessandro-dark",
bio: "Trainer Functional & Movement. Laureando in Scienze Motorie, specializzato in running e atletica leggera." }, bio: "Trainer Functional & Movement. Laureando in Scienze Motorie, specializzato in running e atletica leggera." },
{ name: "Eleonora Cassani", shortName: "Eleonora", role: "Trainer Planning Manager", founder: false, image: "team-eleonora", { name: "Eleonora Cassani", shortName: "Eleonora", role: "Trainer Planning Manager", founder: false, imageAbout: "team-eleonora-dark", image: "team-eleonora",
bio: "Trainer Planning Manager. Chinesiologa specializzata in attività preventiva e adattata, con focus su allenamento in gravidanza e post-gravidanza." }, bio: "Trainer Planning Manager. Chinesiologa specializzata in attività preventiva e adattata, con focus su allenamento in gravidanza e post-gravidanza." },
{ name: "Antonello DAlessandro", shortName: "Antonello", role: "Trainer Exercise & Health", founder: false, image: "team-antonello", { name: "Antonello DAlessandro", shortName: "Antonello", role: "Trainer Exercise & Health", founder: false, imageAbout: "team-antonello-dark", image: "team-antonello",
bio: "Trainer Exercise & Health. Chinesiologo specializzato in attività preventiva e adattata, Dottore in Osteopatia." }, bio: "Trainer Exercise & Health. Chinesiologo specializzato in attività preventiva e adattata, Dottore in Osteopatia." },
{ name: "Alice Roda", shortName: "Alice", role: "Trainer Functional & Movement", founder: false, { name: "Alice Roda", shortName: "Alice", role: "Trainer Functional & Movement", founder: false, imageAbout: "team-alice-dark",
bio: "Trainer Functional & Movement. Laureanda in Scienze Motorie e Preparatore Atletico. Scienza del movimento e massima cura della persona: un approccio al functional training empatico e accogliente, dove lascolto umano guida e supporta ogni tuo progresso." }, bio: "Trainer Functional & Movement. Laureanda in Scienze Motorie e Preparatore Atletico. Scienza del movimento e massima cura della persona: un approccio al functional training empatico e accogliente, dove lascolto umano guida e supporta ogni tuo progresso." },
{ name: "Davide Bisagni", shortName: "Davide", role: "Trainer Functional & Movement", founder: false, { name: "Davide Bisagni", shortName: "Davide", role: "Trainer Functional & Movement", founder: false, imageAbout: "team-davide-dark",
bio: "Trainer Functional & Movement. Chinesiologo e allenamento funzionale al servizio del corpo: massima cura del gesto atletico e attenzione mirata al recupero per prestazioni sostenibili e durature." }, bio: "Trainer Functional & Movement. Chinesiologo e allenamento funzionale al servizio del corpo: massima cura del gesto atletico e attenzione mirata al recupero per prestazioni sostenibili e durature." },
{ name: "Giulia Barbàra", shortName: "Giulia", role: "Trainer Pilates, Yoga & Mindfulness", founder: false, { name: "Giulia Barbàra", shortName: "Giulia", role: "Trainer Pilates, Yoga & Mindfulness", founder: false, imageAbout: "team-giulia-dark",
bio: "Trainer Pilates, Yoga & Mindfulness. Laureata in Psicologia Clinica e Riabilitazione, insegnante di Pilates, Yoga e Mindfulness, con approccio orientato al benessere e allequilibrio mente-corpo." }, bio: "Trainer Pilates, Yoga & Mindfulness. Laureata in Psicologia Clinica e Riabilitazione, insegnante di Pilates, Yoga e Mindfulness, con approccio orientato al benessere e allequilibrio mente-corpo." },
]; ];
+41 -14
View File
@@ -52,9 +52,9 @@ export const prerender = true;
<h2>About</h2> <h2>About</h2>
<p>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.</p> <p>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.</p>
</div> </div>
{imgOf('about-team-group') && ( {imgOf('about-pushup') && (
<div class="about-img-row__img about-img-row__img--native"> <div class="about-img-row__img about-img-row__img--native">
<Image src={imgOf('about-team-group')!} alt="Team InsanityLab" widths={[270]} sizes="270px" /> <Image src={imgOf('about-pushup')!} alt="Allenamento InsanityLab" widths={[320, 640]} sizes="320px" />
</div> </div>
)} )}
</div> </div>
@@ -75,6 +75,11 @@ export const prerender = true;
<li>Le convenzioni vengono definite su misura insieme a InsanityLab, in base alle caratteristiche e alle necessità dell'azienda partner.</li> <li>Le convenzioni vengono definite su misura insieme a InsanityLab, in base alle caratteristiche e alle necessità dell'azienda partner.</li>
</ul> </ul>
<p class="conv__cta">Per ricevere maggiori informazioni o attivare una convenzione: {site.email} · {site.phone}</p> <p class="conv__cta">Per ricevere maggiori informazioni o attivare una convenzione: {site.email} · {site.phone}</p>
{imgOf('about-conventions') && (
<div class="round-img">
<Image src={imgOf('about-conventions')!} alt="Convenzioni aziendali" widths={[360, 720]} sizes="360px" />
</div>
)}
</div> </div>
</section> </section>
@@ -87,7 +92,7 @@ export const prerender = true;
<div class="tgrid tgrid--founders"> <div class="tgrid tgrid--founders">
{founders.map((m) => ( {founders.map((m) => (
<article class="tcard"> <article class="tcard">
{m.image && imgOf(m.image) && <Image src={imgOf(m.image)!} alt={m.name} widths={[300, 500]} sizes="(max-width:767px) 100vw, 33vw" />} {m.imageAbout && imgOf(m.imageAbout) && <Image src={imgOf(m.imageAbout)!} alt={m.name} widths={[300, 500]} sizes="(max-width:767px) 100vw, 33vw" />}
<h3>{m.shortName}</h3> <h3>{m.shortName}</h3>
<p class="tcard__role">{m.role}</p> <p class="tcard__role">{m.role}</p>
<p>{m.bio}</p> <p>{m.bio}</p>
@@ -98,7 +103,7 @@ export const prerender = true;
<div class="tgrid"> <div class="tgrid">
{staff.map((m) => ( {staff.map((m) => (
<article class="tcard"> <article class="tcard">
{m.image && imgOf(m.image) && <Image src={imgOf(m.image)!} alt={m.name} widths={[300, 500]} sizes="(max-width:767px) 100vw, 33vw" />} {m.imageAbout && imgOf(m.imageAbout) && <Image src={imgOf(m.imageAbout)!} alt={m.name} widths={[300, 500]} sizes="(max-width:767px) 100vw, 33vw" />}
<h3>{m.shortName}</h3> <h3>{m.shortName}</h3>
<p class="tcard__role">{m.role}</p> <p class="tcard__role">{m.role}</p>
<p>{m.bio}</p> <p>{m.bio}</p>
@@ -109,10 +114,21 @@ export const prerender = true;
</section> </section>
<section class="section section--alt"> <section class="section section--alt">
<div class="container section-heading"> <div class="container">
<h2>Metodo</h2> <div class="section-heading">
<p>Il team InsanityLab lavora con un approccio empatico e personalizzato, seguendo ogni persona in un percorso continuo e su misura. Unisce competenze multidisciplinari in allenamento, prevenzione, riabilitazione e performance, supportate da formazione costante. Il metodo si basa su sinergia, condivisione e qualità, per generare risultati concreti e duraturi.</p> <h2>Metodo</h2>
<p>In poche parole, il team di InsanityLab è composto da professionisti che credono profondamente in ciò che fanno: un gruppo unito da valori condivisi, formato per eccellere e guidato dalla convinzione che il movimento sia uno strumento di benessere, equilibrio e qualità della vita.</p> <p>Il team InsanityLab lavora con un approccio empatico e personalizzato, seguendo ogni persona in un percorso continuo e su misura. Unisce competenze multidisciplinari in allenamento, prevenzione, riabilitazione e performance, supportate da formazione costante. Il metodo si basa su sinergia, condivisione e qualità, per generare risultati concreti e duraturi.</p>
</div>
<div class="about-img-row">
<div class="about-img-row__text section-heading">
<p>In poche parole, il team di InsanityLab è composto da professionisti che credono profondamente in ciò che fanno: un gruppo unito da valori condivisi, formato per eccellere e guidato dalla convinzione che il movimento sia uno strumento di benessere, equilibrio e qualità della vita.</p>
</div>
{imgOf('about-team-group') && (
<div class="round-img">
<Image src={imgOf('about-team-group')!} alt="Il team InsanityLab" widths={[360, 720]} sizes="360px" />
</div>
)}
</div>
</div> </div>
</section> </section>
@@ -125,9 +141,9 @@ export const prerender = true;
<p>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.</p> <p>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.</p>
<p>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.</p> <p>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.</p>
</div> </div>
{imgOf('about-exterior') && ( {imgOf('about-studios') && (
<div class="about-img-row__img"> <div class="about-img-row__img">
<Image src={imgOf('about-exterior')!} alt="In-Sanity Studios" widths={[600, 900]} sizes="(max-width:767px) 100vw, 50vw" /> <Image src={imgOf('about-studios')!} alt="In-Sanity Studios" widths={[600, 900]} sizes="(max-width:767px) 100vw, 50vw" />
</div> </div>
)} )}
</div> </div>
@@ -135,10 +151,19 @@ export const prerender = true;
</section> </section>
<section class="section section--alt"> <section class="section section--alt">
<div class="container section-heading"> <div class="container">
<h2>Vision</h2> <div class="about-img-row">
<p>InsanityLab nasce con l'obiettivo di aiutare le persone a ritrovare energia ed equilibrio attraverso il movimento, promuovendo un approccio autentico, scientifico e umano al benessere. La sua visione va oltre l'estetica e la performance: allenarsi significa prendersi cura di sé, costruendo abitudini sane, sostenibili e orientate alla longevità, alla prevenzione e alla qualità della vita.</p> {imgOf('about-vision') && (
<p>L'obiettivo è diventare un punto di riferimento capace di lasciare un impatto reale nella comunità, trasformando il movimento in uno strumento di salute, consapevolezza e identità.</p> <div class="round-img">
<Image src={imgOf('about-vision')!} alt="La comunità InsanityLab" widths={[360, 720]} sizes="360px" />
</div>
)}
<div class="about-img-row__text section-heading">
<h2>Vision</h2>
<p>InsanityLab nasce con l'obiettivo di aiutare le persone a ritrovare energia ed equilibrio attraverso il movimento, promuovendo un approccio autentico, scientifico e umano al benessere. La sua visione va oltre l'estetica e la performance: allenarsi significa prendersi cura di sé, costruendo abitudini sane, sostenibili e orientate alla longevità, alla prevenzione e alla qualità della vita.</p>
<p>L'obiettivo è diventare un punto di riferimento capace di lasciare un impatto reale nella comunità, trasformando il movimento in uno strumento di salute, consapevolezza e identità.</p>
</div>
</div>
</div> </div>
</section> </section>
</Base> </Base>
@@ -152,6 +177,8 @@ export const prerender = true;
.about-img-row__img--native { max-width: 320px; margin: 0 auto; } .about-img-row__img--native { max-width: 320px; margin: 0 auto; }
.conv { max-width: 760px; margin: 0 auto 30px; display: grid; gap: 18px; padding-left: 20px; } .conv { max-width: 760px; margin: 0 auto 30px; display: grid; gap: 18px; padding-left: 20px; }
.conv__cta { text-align: center; font-family: var(--font-heading); letter-spacing: .06em; } .conv__cta { text-align: center; font-family: var(--font-heading); letter-spacing: .06em; }
.round-img { max-width: 380px; margin: 30px auto 0; }
.round-img img { border-radius: 50%; aspect-ratio: 1; object-fit: cover; width: 100%; }
.tgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; } .tgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; }
.tcard img { aspect-ratio: 3/4; object-fit: cover; margin-bottom: 16px; } .tcard img { aspect-ratio: 3/4; object-fit: cover; margin-bottom: 16px; }
.tcard h3 { text-transform: lowercase; letter-spacing: .2em; } .tcard h3 { text-transform: lowercase; letter-spacing: .2em; }
+2 -1
View File
@@ -3,11 +3,12 @@ import Base from '../layouts/Base.astro';
import PageHero from '../components/PageHero.astro'; import PageHero from '../components/PageHero.astro';
import ContactForm from '../components/ContactForm.astro'; import ContactForm from '../components/ContactForm.astro';
import { site } from '../data/site'; import { site } from '../data/site';
import contactHero from '../assets/img/contact-hero.jpg';
export const prerender = true; export const prerender = true;
const mapsQuery = encodeURIComponent(`${site.address}, ${site.cityLine}`); const mapsQuery = encodeURIComponent(`${site.address}, ${site.cityLine}`);
--- ---
<Base title="Contatti" description="Contatta InsanityLab: Via Leandro Alberti 76, Bologna. Compila il form o chiamaci per una consulenza iniziale."> <Base title="Contatti" description="Contatta InsanityLab: Via Leandro Alberti 76, Bologna. Compila il form o chiamaci per una consulenza iniziale.">
<PageHero title="Contattaci" /> <PageHero title="Contattaci" bgImage={contactHero} />
<section class="section"> <section class="section">
<div class="container cgrid"> <div class="container cgrid">
<div class="cmap" id="map-box"> <div class="cmap" id="map-box">
+6 -2
View File
@@ -5,6 +5,7 @@ import TrainingCards from '../components/home/TrainingCards.astro';
import Agenda from '../components/Agenda.astro'; import Agenda from '../components/Agenda.astro';
import { Image } from 'astro:assets'; import { Image } from 'astro:assets';
import { trainings } from '../data/trainings'; import { trainings } from '../data/trainings';
import trialImg from '../assets/img/trial-class.jpg';
const images = import.meta.glob<{ default: ImageMetadata }>('../assets/img/training-*.{jpg,png}', { eager: true }); const images = import.meta.glob<{ default: ImageMetadata }>('../assets/img/training-*.{jpg,png}', { eager: true });
const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1]?.default; const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1]?.default;
export const prerender = true; export const prerender = true;
@@ -39,7 +40,8 @@ export const prerender = true;
<section class="section section--alt"> <section class="section section--alt">
<div class="container trial"> <div class="container trial">
<div> <Image src={trialImg} alt="Lezione di prova InsanityLab" widths={[500, 900]} sizes="(max-width:767px) 100vw, 45vw" data-reveal="left" />
<div data-reveal="right">
<h2>Prenota la lezione di prova</h2> <h2>Prenota la lezione di prova</h2>
<p>La lezione di prova è l'occasione perfetta per conoscere il metodo, vivere l'esperienza in prima persona e capire il percorso più adatto a te. Inizia oggi il tuo percorso: prenota la tua lezione di prova.</p> <p>La lezione di prova è l'occasione perfetta per conoscere il metodo, vivere l'esperienza in prima persona e capire il percorso più adatto a te. Inizia oggi il tuo percorso: prenota la tua lezione di prova.</p>
<a class="btn btn--dark" href="/contact">Prenota</a> <a class="btn btn--dark" href="/contact">Prenota</a>
@@ -52,6 +54,8 @@ export const prerender = true;
.tblock { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center; padding-block: 40px; scroll-margin-top: calc(var(--header-h) + 20px); } .tblock { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center; padding-block: 40px; scroll-margin-top: calc(var(--header-h) + 20px); }
.tblock--reverse img { order: 2; } .tblock--reverse img { order: 2; }
.tblock h2 { letter-spacing: .1em; } .tblock h2 { letter-spacing: .1em; }
.trial { max-width: 720px; text-align: center; } .trial { display: grid; grid-template-columns: 1fr 1.2fr; gap: 60px; align-items: center; }
.trial img { width: 100%; height: auto; max-height: 460px; object-fit: cover; }
@media (max-width: 767px) { .trial { grid-template-columns: 1fr; gap: 24px; } }
@media (max-width: 767px) { .tblock { grid-template-columns: 1fr; gap: 24px; } .tblock--reverse img { order: 0; } } @media (max-width: 767px) { .tblock { grid-template-columns: 1fr; gap: 24px; } .tblock--reverse img { order: 0; } }
</style> </style>