registri dei piani in git (AI-OS#309)
Refs gitea:Adriano/AI-OS#310 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,480 @@
|
||||
# Review package: 2fdd1b9..HEAD
|
||||
|
||||
## Commits
|
||||
45e5214 feat: homepage completa da design Figma
|
||||
|
||||
## Files changed
|
||||
src/components/Agenda.astro | 54 +++++++++++++++++++++++++++++++++
|
||||
src/components/home/FeatureBlocks.astro | 35 +++++++++++++++++++++
|
||||
src/components/home/HeroSlider.astro | 54 +++++++++++++++++++++++++++++++++
|
||||
src/components/home/InfoColumns.astro | 26 ++++++++++++++++
|
||||
src/components/home/MethodSteps.astro | 39 ++++++++++++++++++++++++
|
||||
src/components/home/PartnersStrip.astro | 18 +++++++++++
|
||||
src/components/home/ProgramsGrid.astro | 38 +++++++++++++++++++++++
|
||||
src/components/home/QuoteBanner.astro | 22 ++++++++++++++
|
||||
src/components/home/TeamSection.astro | 34 +++++++++++++++++++++
|
||||
src/components/home/TrainingCards.astro | 42 +++++++++++++++++++++++++
|
||||
src/pages/index.astro | 27 ++++++++++++++++-
|
||||
11 files changed, 388 insertions(+), 1 deletion(-)
|
||||
|
||||
## Diff
|
||||
diff --git a/src/components/Agenda.astro b/src/components/Agenda.astro
|
||||
new file mode 100644
|
||||
index 0000000..116b0c0
|
||||
--- /dev/null
|
||||
+++ b/src/components/Agenda.astro
|
||||
@@ -0,0 +1,54 @@
|
||||
+---
|
||||
+import { agenda, disciplines, days, slots } from '../data/agenda';
|
||||
+---
|
||||
+<section class="section section--dark agenda">
|
||||
+ <div class="container">
|
||||
+ <div class="section-heading"><h2>Agenda</h2></div>
|
||||
+ <div class="agenda__tabs" role="tablist">
|
||||
+ {disciplines.map((d, i) => (
|
||||
+ <button class:list={['agenda__tab', { 'is-active': i === 0 }]} data-filter={d} role="tab" aria-selected={i === 0 ? 'true' : 'false'}>{d}</button>
|
||||
+ ))}
|
||||
+ </div>
|
||||
+ <div class="agenda__grid">
|
||||
+ <div class="agenda__corner"></div>
|
||||
+ {days.map((d) => <div class="agenda__day">{d}</div>)}
|
||||
+ {slots.map((slot) => (
|
||||
+ <>
|
||||
+ <div class="agenda__slot">{slot}</div>
|
||||
+ {days.map((_, dayIdx) => {
|
||||
+ const entry = agenda.find((e) => e.day === dayIdx && e.slot === slot);
|
||||
+ return entry
|
||||
+ ? <div class="agenda__cell" data-discipline={entry.discipline}><strong>{entry.discipline}</strong><span>{entry.time}</span><em>{entry.trainer}</em></div>
|
||||
+ : <div class="agenda__cell agenda__cell--empty"></div>;
|
||||
+ })}
|
||||
+ </>
|
||||
+ ))}
|
||||
+ </div>
|
||||
+ </div>
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .agenda__tabs { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; margin-bottom: 40px; }
|
||||
+ .agenda__tab { background: transparent; color: #b9aea4; border: 0; cursor: pointer; font-family: var(--font-heading); font-size: .68rem; letter-spacing: .18em; text-transform: uppercase; padding: 10px 18px; }
|
||||
+ .agenda__tab.is-active { background: var(--c-accent); color: #fff; }
|
||||
+ .agenda__grid { display: grid; grid-template-columns: 70px repeat(7, 1fr); gap: 2px; }
|
||||
+ .agenda__day, .agenda__slot { font-family: var(--font-heading); font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; color: #b9aea4; display: flex; align-items: center; justify-content: center; padding: 10px 4px; }
|
||||
+ .agenda__cell { background: var(--c-accent); color: #fff; padding: 14px 8px; text-align: center; font-size: .72rem; display: flex; flex-direction: column; gap: 2px; }
|
||||
+ .agenda__cell strong { font-family: var(--font-heading); font-size: .7rem; letter-spacing: .1em; text-transform: uppercase; }
|
||||
+ .agenda__cell em { font-style: normal; opacity: .85; }
|
||||
+ .agenda__cell--empty { background: transparent; }
|
||||
+ .agenda__cell.is-dim { opacity: .15; }
|
||||
+ @media (max-width: 991px) { .agenda__grid { grid-template-columns: 60px repeat(7, minmax(90px, 1fr)); overflow-x: auto; } }
|
||||
+</style>
|
||||
+
|
||||
+<script>
|
||||
+ document.querySelectorAll<HTMLElement>('.agenda').forEach((root) => {
|
||||
+ const tabs = root.querySelectorAll<HTMLButtonElement>('.agenda__tab');
|
||||
+ const cells = root.querySelectorAll<HTMLElement>('.agenda__cell[data-discipline]');
|
||||
+ tabs.forEach((tab) => tab.addEventListener('click', () => {
|
||||
+ tabs.forEach((t) => { t.classList.toggle('is-active', t === tab); t.setAttribute('aria-selected', String(t === tab)); });
|
||||
+ const f = tab.dataset.filter!;
|
||||
+ cells.forEach((c) => c.classList.toggle('is-dim', f !== 'Events' && c.dataset.discipline !== f));
|
||||
+ }));
|
||||
+ });
|
||||
+</script>
|
||||
diff --git a/src/components/home/FeatureBlocks.astro b/src/components/home/FeatureBlocks.astro
|
||||
new file mode 100644
|
||||
index 0000000..914f7fd
|
||||
--- /dev/null
|
||||
+++ b/src/components/home/FeatureBlocks.astro
|
||||
@@ -0,0 +1,35 @@
|
||||
+---
|
||||
+import { Image } from 'astro:assets';
|
||||
+import featPerformance from '../../assets/img/feature-performance.jpg';
|
||||
+import featBalance from '../../assets/img/feature-balance.jpg';
|
||||
+import featLongevity from '../../assets/img/feature-longevity.jpg';
|
||||
+const blocks = [
|
||||
+ { title: 'PERFORMANCE', image: featPerformance, reverse: false,
|
||||
+ text: 'Allenamenti progettati su misura per adattarsi ai tuoi obiettivi e al tuo livello. Programmi mirati che sviluppano forza, resistenza e funzionalità in modo equilibrato. Un metodo efficace per migliorare le performance e ottenere risultati concreti e duraturi.' },
|
||||
+ { title: 'BALANCE', image: featBalance, reverse: true,
|
||||
+ text: 'L’equilibrio tra corpo e mente è la base di ogni performance duratura. Attraverso movimento, recupero e gestione dello stress, costruiamo stabilità e controllo. Un approccio integrato che sostiene benessere, energia e continuità nel tempo.' },
|
||||
+ { title: 'LONGEVITY', image: featLongevity, reverse: false,
|
||||
+ text: 'Benessere completo tra movimento, nutrizione e stile di vita. Un percorso personalizzato che unisce prevenzione, recupero ed equilibrio. Investi nella tua salute e qualità della vita nel tempo.' },
|
||||
+];
|
||||
+---
|
||||
+<section class="section">
|
||||
+ <div class="container">
|
||||
+ {blocks.map((b) => (
|
||||
+ <div class:list={['feat', { 'feat--reverse': b.reverse }]}>
|
||||
+ <Image src={b.image} alt={b.title} class="feat__img" widths={[500, 900]} sizes="(max-width: 767px) 100vw, 45vw" />
|
||||
+ <div class="feat__text">
|
||||
+ <h2>{b.title}</h2>
|
||||
+ <p>{b.text}</p>
|
||||
+ <a class="btn" href="/programs">Scopri</a>
|
||||
+ </div>
|
||||
+ </div>
|
||||
+ ))}
|
||||
+ </div>
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .feat { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center; padding-block: 40px; }
|
||||
+ .feat--reverse .feat__img { order: 2; }
|
||||
+ .feat__text h2 { text-transform: uppercase; letter-spacing: .1em; }
|
||||
+ @media (max-width: 767px) { .feat { grid-template-columns: 1fr; gap: 24px; } .feat--reverse .feat__img { order: 0; } }
|
||||
+</style>
|
||||
diff --git a/src/components/home/HeroSlider.astro b/src/components/home/HeroSlider.astro
|
||||
new file mode 100644
|
||||
index 0000000..75f3e9d
|
||||
--- /dev/null
|
||||
+++ b/src/components/home/HeroSlider.astro
|
||||
@@ -0,0 +1,54 @@
|
||||
+---
|
||||
+import { Image } from 'astro:assets';
|
||||
+import hero1 from '../../assets/img/hero-1.jpg';
|
||||
+const slides = [
|
||||
+ { title: ['PERFORMANCE.', 'BALANCE.', 'LONGEVITY.'], image: hero1, alt: 'Atleti InsanityLab' },
|
||||
+];
|
||||
+---
|
||||
+<section class="hero" aria-label="presentazione">
|
||||
+ {slides.map((s, i) => (
|
||||
+ <div class:list={['hero__slide', { 'is-active': i === 0 }]}>
|
||||
+ <div class="container hero__in">
|
||||
+ <div class="hero__text">
|
||||
+ <h1>{s.title.map((line) => <span class="hero__line">{line}</span>)}</h1>
|
||||
+ <a class="btn" href="/programs">Scopri</a>
|
||||
+ </div>
|
||||
+ <Image src={s.image} alt={s.alt} class="hero__img" widths={[600, 1000, 1400]} sizes="(max-width: 991px) 100vw, 60vw" loading="eager" />
|
||||
+ </div>
|
||||
+ </div>
|
||||
+ ))}
|
||||
+ {slides.length > 1 && (
|
||||
+ <>
|
||||
+ <button class="hero__arrow hero__arrow--prev" aria-label="precedente">‹</button>
|
||||
+ <button class="hero__arrow hero__arrow--next" aria-label="successiva">›</button>
|
||||
+ </>
|
||||
+ )}
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .hero { position: relative; background:
|
||||
+ repeating-linear-gradient(to bottom, transparent 0 10px, rgba(0,0,0,.05) 10px 11px) left top / 40% 60% no-repeat, #fff; }
|
||||
+ .hero__slide { display: none; }
|
||||
+ .hero__slide.is-active { display: block; }
|
||||
+ .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__text h1 { font-weight: 500; }
|
||||
+ .hero__img { object-fit: cover; 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--prev { left: 12px; } .hero__arrow--next { right: 12px; }
|
||||
+ @media (max-width: 991px) { .hero__in { grid-template-columns: 1fr; padding-block: 40px; min-height: 0; } }
|
||||
+</style>
|
||||
+
|
||||
+<script>
|
||||
+ const slides = document.querySelectorAll('.hero__slide');
|
||||
+ if (slides.length > 1) {
|
||||
+ let cur = 0;
|
||||
+ const show = (i: number) => {
|
||||
+ slides[cur].classList.remove('is-active');
|
||||
+ cur = (i + slides.length) % slides.length;
|
||||
+ slides[cur].classList.add('is-active');
|
||||
+ };
|
||||
+ document.querySelector('.hero__arrow--prev')?.addEventListener('click', () => show(cur - 1));
|
||||
+ document.querySelector('.hero__arrow--next')?.addEventListener('click', () => show(cur + 1));
|
||||
+ }
|
||||
+</script>
|
||||
diff --git a/src/components/home/InfoColumns.astro b/src/components/home/InfoColumns.astro
|
||||
new file mode 100644
|
||||
index 0000000..763bc2b
|
||||
--- /dev/null
|
||||
+++ b/src/components/home/InfoColumns.astro
|
||||
@@ -0,0 +1,26 @@
|
||||
+---
|
||||
+import infoPerformance from '../../assets/img/info-performance.jpg';
|
||||
+import infoBalance from '../../assets/img/info-balance.jpg';
|
||||
+import infoLongevity from '../../assets/img/info-longevity.jpg';
|
||||
+const cols = [
|
||||
+ { title: 'PERFORMANCE.', image: infoPerformance },
|
||||
+ { title: 'BALANCE.', image: infoBalance },
|
||||
+ { title: 'LONGEVITY.', image: infoLongevity },
|
||||
+];
|
||||
+---
|
||||
+<section class="info" aria-label="pilastri">
|
||||
+ {cols.map((c) => (
|
||||
+ <a class="info__col" href="/programs" style={`background-image:url(${c.image.src})`}>
|
||||
+ <span class="info__title">{c.title}</span>
|
||||
+ </a>
|
||||
+ ))}
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .info { display: grid; grid-template-columns: repeat(3, 1fr); }
|
||||
+ .info__col { position: relative; min-height: 320px; background-size: cover; background-position: center; display: flex; align-items: center; justify-content: center; text-decoration: none; }
|
||||
+ .info__col::before { content: ''; position: absolute; inset: 0; background: rgba(30,24,20,.35); transition: background .2s; }
|
||||
+ .info__col:hover::before { background: rgba(30,24,20,.15); }
|
||||
+ .info__title { position: relative; color: #fff; font-family: var(--font-heading); font-weight: 600; letter-spacing: .1em; font-size: 1.1rem; }
|
||||
+ @media (max-width: 767px) { .info { grid-template-columns: 1fr; } }
|
||||
+</style>
|
||||
diff --git a/src/components/home/MethodSteps.astro b/src/components/home/MethodSteps.astro
|
||||
new file mode 100644
|
||||
index 0000000..dfbbd1a
|
||||
--- /dev/null
|
||||
+++ b/src/components/home/MethodSteps.astro
|
||||
@@ -0,0 +1,39 @@
|
||||
+---
|
||||
+import { Image } from 'astro:assets';
|
||||
+import type { ImageMetadata } from 'astro';
|
||||
+import { methodIntro, methodExtraNote, methodSteps } from '../../data/method';
|
||||
+const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/method-*.{jpg,png}', { eager: true });
|
||||
+const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default;
|
||||
+---
|
||||
+<section class="section section--alt">
|
||||
+ <div class="container">
|
||||
+ <div class="section-heading">
|
||||
+ <h2>Il metodo<br />InsanityLab</h2>
|
||||
+ <p set:html={methodIntro.replace('Metodo Insanitylab', '<strong>Metodo Insanitylab</strong>')} />
|
||||
+ </div>
|
||||
+ <p class="method__note">{methodExtraNote}</p>
|
||||
+ <div class="method__row">
|
||||
+ {methodSteps.map((s) => (
|
||||
+ <div class:list={['method__step', { 'method__step--extra': s.extra }]}>
|
||||
+ <div class="method__circle">
|
||||
+ {imgOf(s.image) && <Image src={imgOf(s.image)!} alt="" widths={[220]} sizes="220px" />}
|
||||
+ <span>{s.n}</span>
|
||||
+ </div>
|
||||
+ <h3>{s.title}</h3>
|
||||
+ <p>{s.text}</p>
|
||||
+ </div>
|
||||
+ ))}
|
||||
+ </div>
|
||||
+ </div>
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .method__note { text-align: right; font-size: .8rem; color: var(--c-text-light); max-width: 260px; margin-left: auto; }
|
||||
+ .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 img { border-radius: 50%; width: 100%; height: 100%; object-fit: cover; }
|
||||
+ .method__circle span { position: absolute; top: -6px; left: -6px; font-family: var(--font-heading); font-size: 2.4rem; color: var(--c-heading); background: var(--c-bg-alt); padding: 0 8px; }
|
||||
+ .method__step h3 { font-size: .85rem; text-transform: uppercase; letter-spacing: .14em; }
|
||||
+ .method__step p { font-size: .85rem; color: var(--c-text-light); }
|
||||
+ @media (max-width: 767px) { .method__row { grid-template-columns: 1fr 1fr; } }
|
||||
+</style>
|
||||
diff --git a/src/components/home/PartnersStrip.astro b/src/components/home/PartnersStrip.astro
|
||||
new file mode 100644
|
||||
index 0000000..f5e8bf6
|
||||
--- /dev/null
|
||||
+++ b/src/components/home/PartnersStrip.astro
|
||||
@@ -0,0 +1,18 @@
|
||||
+---
|
||||
+import { Image } from 'astro:assets';
|
||||
+import type { ImageMetadata } from 'astro';
|
||||
+import { partners } from '../../data/partners';
|
||||
+const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/partner-*.{jpg,png}', { eager: true });
|
||||
+const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default;
|
||||
+---
|
||||
+<section class="pstrip" aria-label="partner">
|
||||
+ <div class="container pstrip__in">
|
||||
+ {partners.map((p) => imgOf(p.image) && <Image src={imgOf(p.image)!} alt={p.name} height={44} />)}
|
||||
+ </div>
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .pstrip { background: var(--c-accent); padding-block: 40px; }
|
||||
+ .pstrip__in { display: flex; align-items: center; justify-content: space-around; gap: 30px; flex-wrap: wrap; }
|
||||
+ .pstrip img { height: 44px; width: auto; opacity: .9; }
|
||||
+</style>
|
||||
diff --git a/src/components/home/ProgramsGrid.astro b/src/components/home/ProgramsGrid.astro
|
||||
new file mode 100644
|
||||
index 0000000..d4c1c98
|
||||
--- /dev/null
|
||||
+++ b/src/components/home/ProgramsGrid.astro
|
||||
@@ -0,0 +1,38 @@
|
||||
+---
|
||||
+import { programs } from '../../data/programs';
|
||||
+const icons: Record<string, string> = {
|
||||
+ 'personal-training': 'M4 12h3m10 0h3M9 8v8m6-8v8M7 10v4m10-4v4',
|
||||
+ 'performance-class': 'M13 3l-2 7h4l-6 11 2-8H7l4-10z',
|
||||
+ 'coaching': 'M12 3a4 4 0 110 8 4 4 0 010-8zm-7 18a7 7 0 0114 0',
|
||||
+ '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',
|
||||
+ 'nutrition': 'M12 3c4 0 7 3 7 8s-3 10-7 10-7-5-7-10 3-8 7-8zm0-1v4',
|
||||
+};
|
||||
+---
|
||||
+<section class="section">
|
||||
+ <div class="container">
|
||||
+ <div class="section-heading">
|
||||
+ <h2>Programmi</h2>
|
||||
+ <p>Questi sono i programmi delle classi di Insanity Lab selezionati per voi. Abbiamo scelto i migliori percorsi per offrirvi un’esperienza di allenamento completa e mirata.</p>
|
||||
+ </div>
|
||||
+ <div class="pgrid">
|
||||
+ {programs.map((p) => (
|
||||
+ <a class="pgrid__item" href={`/programs/${p.slug}`}>
|
||||
+ <svg viewBox="0 0 24 24" fill="none" stroke="var(--c-accent-dark)" stroke-width="1.4" width="52" height="52" aria-hidden="true"><path d={icons[p.slug]} /></svg>
|
||||
+ <h3>{p.title}</h3>
|
||||
+ <p>{p.subtitle}</p>
|
||||
+ </a>
|
||||
+ ))}
|
||||
+ </div>
|
||||
+ </div>
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .pgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 50px 40px; text-align: center; }
|
||||
+ .pgrid__item { text-decoration: none; }
|
||||
+ .pgrid__item svg { margin-inline: auto; }
|
||||
+ .pgrid__item h3 { text-transform: uppercase; letter-spacing: .12em; font-size: .95rem; margin: 18px 0 6px; }
|
||||
+ .pgrid__item p { font-size: .85rem; color: var(--c-text-light); margin: 0; }
|
||||
+ @media (max-width: 767px) { .pgrid { grid-template-columns: 1fr 1fr; } }
|
||||
+ @media (max-width: 480px) { .pgrid { grid-template-columns: 1fr; } }
|
||||
+</style>
|
||||
diff --git a/src/components/home/QuoteBanner.astro b/src/components/home/QuoteBanner.astro
|
||||
new file mode 100644
|
||||
index 0000000..09a6e06
|
||||
--- /dev/null
|
||||
+++ b/src/components/home/QuoteBanner.astro
|
||||
@@ -0,0 +1,22 @@
|
||||
+---
|
||||
+import quoteBg from '../../assets/img/quote-bg.jpg';
|
||||
+---
|
||||
+<section class="quote" style={`background-image:url(${quoteBg.src})`}>
|
||||
+ <div class="container quote__in">
|
||||
+ <p class="quote__mark">“</p>
|
||||
+ <blockquote>
|
||||
+ <p><strong>PERFORMANCE</strong> È SPINGERE OLTRE I PROPRI LIMITI, <strong>BALANCE</strong> È SAPERLI ASCOLTARE, <strong>LONGEVITY</strong> È COSTRUIRE NEL TEMPO CIÒ CHE DAVVERO CONTA.</p>
|
||||
+ <cite>Insanitylab</cite>
|
||||
+ </blockquote>
|
||||
+ </div>
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .quote { position: relative; background-size: cover; background-position: center; text-align: center; }
|
||||
+ .quote::before { content: ''; position: absolute; inset: 0; background: rgba(46,32,24,.82); }
|
||||
+ .quote__in { position: relative; padding-block: 110px; max-width: 820px; }
|
||||
+ .quote__mark { color: #fff; font-size: 4rem; font-family: var(--font-heading); margin: 0; line-height: 1; }
|
||||
+ .quote blockquote { margin: 0; }
|
||||
+ .quote blockquote p { color: #fff; font-family: var(--font-heading); font-size: clamp(1.1rem, 2.4vw, 1.6rem); letter-spacing: .06em; line-height: 1.6; }
|
||||
+ .quote cite { display: block; margin-top: 24px; color: var(--c-accent); font-style: normal; font-family: var(--font-heading); font-size: .75rem; letter-spacing: .3em; text-transform: uppercase; }
|
||||
+</style>
|
||||
diff --git a/src/components/home/TeamSection.astro b/src/components/home/TeamSection.astro
|
||||
new file mode 100644
|
||||
index 0000000..65cb0b7
|
||||
--- /dev/null
|
||||
+++ b/src/components/home/TeamSection.astro
|
||||
@@ -0,0 +1,34 @@
|
||||
+---
|
||||
+import { Image } from 'astro:assets';
|
||||
+import type { ImageMetadata } from 'astro';
|
||||
+import { team, homeTeam } from '../../data/team';
|
||||
+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 imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default;
|
||||
+---
|
||||
+<section class="section">
|
||||
+ <div class="container">
|
||||
+ <div class="tteam__head">
|
||||
+ <h2>Team<br />Trainer</h2>
|
||||
+ <a class="btn" href="/about">Scopri team</a>
|
||||
+ </div>
|
||||
+ <div class="tteam__grid">
|
||||
+ {members.map((m) => (
|
||||
+ <figure class="tteam__card">
|
||||
+ <Image src={imgOf(m.image!)!} alt={m.name} widths={[300, 500]} sizes="(max-width: 767px) 50vw, 25vw" />
|
||||
+ <figcaption>{m.shortName}</figcaption>
|
||||
+ </figure>
|
||||
+ ))}
|
||||
+ </div>
|
||||
+ </div>
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .tteam__head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 40px; }
|
||||
+ .tteam__head h2 { text-transform: uppercase; letter-spacing: .1em; margin: 0; }
|
||||
+ .tteam__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
|
||||
+ .tteam__card { position: relative; margin: 0; }
|
||||
+ .tteam__card img { aspect-ratio: 3/4; object-fit: cover; width: 100%; }
|
||||
+ .tteam__card figcaption { position: absolute; right: 10px; bottom: 14px; background: #fff; padding: 10px 6px; writing-mode: vertical-rl; font-family: var(--font-heading); font-size: .62rem; letter-spacing: .3em; text-transform: uppercase; color: var(--c-accent-dark); }
|
||||
+ @media (max-width: 767px) { .tteam__grid { grid-template-columns: 1fr 1fr; } }
|
||||
+</style>
|
||||
diff --git a/src/components/home/TrainingCards.astro b/src/components/home/TrainingCards.astro
|
||||
new file mode 100644
|
||||
index 0000000..93b2fbe
|
||||
--- /dev/null
|
||||
+++ b/src/components/home/TrainingCards.astro
|
||||
@@ -0,0 +1,42 @@
|
||||
+---
|
||||
+import { Image } from 'astro:assets';
|
||||
+import type { ImageMetadata } from 'astro';
|
||||
+import { trainings } from '../../data/trainings';
|
||||
+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 tones = ['taupe', 'tan', 'dark', 'charcoal'];
|
||||
+// riga 1 (training 0-1): card, foto | riga 2 (training 2-3): foto, card
|
||||
+const isSwapRow = (i: number) => Math.floor(i / 2) % 2 === 1;
|
||||
+---
|
||||
+<section class="tcards">
|
||||
+ {trainings.map((t, i) => {
|
||||
+ const img = imgOf(t.image);
|
||||
+ const card = (
|
||||
+ <div class:list={['tcards__cell', `tcards__cell--${tones[i]}`]}>
|
||||
+ <h3>{t.title}</h3>
|
||||
+ <p>{t.caption}</p>
|
||||
+ <a class="tcards__link" href={t.cta.href}>Scopri →</a>
|
||||
+ </div>
|
||||
+ );
|
||||
+ const photo = img ? <Image src={img} alt={t.title} class="tcards__img" widths={[400, 700]} sizes="(max-width: 767px) 50vw, 25vw" /> : null;
|
||||
+ return isSwapRow(i) ? <>{photo}{card}</> : <>{card}{photo}</>;
|
||||
+ })}
|
||||
+</section>
|
||||
+
|
||||
+<style>
|
||||
+ .tcards { display: grid; grid-template-columns: repeat(4, 1fr); }
|
||||
+ .tcards__cell { padding: 48px 36px; display: flex; flex-direction: column; justify-content: center; aspect-ratio: 1; }
|
||||
+ .tcards__cell--taupe { background: #6b5d5b; }
|
||||
+ .tcards__cell--dark { background: var(--c-dark); }
|
||||
+ .tcards__cell--charcoal { background: #27252a; }
|
||||
+ .tcards__cell--taupe h3, .tcards__cell--taupe p,
|
||||
+ .tcards__cell--dark h3, .tcards__cell--dark p,
|
||||
+ .tcards__cell--charcoal h3, .tcards__cell--charcoal p { color: #efe9e3; }
|
||||
+ .tcards__cell--tan { background: var(--c-accent); }
|
||||
+ .tcards__cell--tan h3, .tcards__cell--tan p { color: #fff; }
|
||||
+ .tcards__cell h3 { text-transform: uppercase; letter-spacing: .12em; }
|
||||
+ .tcards__cell p { font-size: .88rem; }
|
||||
+ .tcards__link { color: #fff; font-family: var(--font-heading); font-size: .7rem; letter-spacing: .2em; text-transform: uppercase; text-decoration: none; margin-top: 18px; }
|
||||
+ .tcards__img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 1; }
|
||||
+ @media (max-width: 767px) { .tcards { grid-template-columns: repeat(2, 1fr); } }
|
||||
+</style>
|
||||
diff --git a/src/pages/index.astro b/src/pages/index.astro
|
||||
index 86dcea2..665a928 100644
|
||||
--- a/src/pages/index.astro
|
||||
+++ b/src/pages/index.astro
|
||||
@@ -1,7 +1,32 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
+import HeroSlider from '../components/home/HeroSlider.astro';
|
||||
+import InfoColumns from '../components/home/InfoColumns.astro';
|
||||
+import FeatureBlocks from '../components/home/FeatureBlocks.astro';
|
||||
+import QuoteBanner from '../components/home/QuoteBanner.astro';
|
||||
+import TrainingCards from '../components/home/TrainingCards.astro';
|
||||
+import ProgramsGrid from '../components/home/ProgramsGrid.astro';
|
||||
+import Agenda from '../components/Agenda.astro';
|
||||
+import TeamSection from '../components/home/TeamSection.astro';
|
||||
+import MethodSteps from '../components/home/MethodSteps.astro';
|
||||
+import PartnersStrip from '../components/home/PartnersStrip.astro';
|
||||
export const prerender = true;
|
||||
---
|
||||
<Base title="Performance. Balance. Longevity.">
|
||||
- <section class="section"><div class="container"><h1>Homepage in costruzione</h1></div></section>
|
||||
+ <HeroSlider />
|
||||
+ <InfoColumns />
|
||||
+ <FeatureBlocks />
|
||||
+ <QuoteBanner />
|
||||
+ <section class="section" style="padding-bottom:0">
|
||||
+ <div class="section-heading">
|
||||
+ <h2>Training</h2>
|
||||
+ <p>Porta i tuoi allenamenti al livello successivo: trova il percorso più adatto alle tue esigenze.</p>
|
||||
+ </div>
|
||||
+ </section>
|
||||
+ <TrainingCards />
|
||||
+ <ProgramsGrid />
|
||||
+ <Agenda />
|
||||
+ <TeamSection />
|
||||
+ <MethodSteps />
|
||||
+ <PartnersStrip />
|
||||
</Base>
|
||||
Reference in New Issue
Block a user