33091be802
# Conflicts: # apps/sito/compose.yaml
383 lines
21 KiB
Plaintext
383 lines
21 KiB
Plaintext
---
|
||
import Base from '../../layouts/Base.astro';
|
||
import T from '../../components/content/T.astro';
|
||
import { t, resolveContent } from '../../lib/content';
|
||
import { edizioneAttiva, tagEdizione } from '../../data/promo-edizioni';
|
||
import { getSessionUser, SESSION_COOKIE } from '../../lib/auth';
|
||
import { getDb } from '../../lib/db';
|
||
// Landing promo/lancio integrata nel sito (header e footer del sito, non standalone).
|
||
// I bottoni "Acquista" portano al gestionale bsport: l'utente crea l'account e sceglie lì
|
||
// dentro il prodotto, poi paga via Stripe. Il link è uno solo per tutte le card, ed è voluto:
|
||
// il catalogo lo presenta bsport dopo l'accesso.
|
||
const BSPORT_URL = 'https://backoffice.bsport.io/login/signup?membership=3428';
|
||
export const prerender = false;
|
||
|
||
// Quale promozione è in vetrina lo decide il calendario, non un deploy: l'edizione attiva è
|
||
// quella la cui finestra contiene questo istante (vedi `data/promo-edizioni.ts`). Alla
|
||
// scadenza dell'una subentra l'altra da sola, e quando non ce n'è nessuna la pagina lo dice
|
||
// invece di lasciare online i prezzi di un'offerta finita.
|
||
//
|
||
// Anteprima: `?anteprima=2026-08-20` mostra l'edizione di quella data. Solo per chi è loggato
|
||
// — un visitatore non deve poter vedere prezzi non ancora validi, o non più.
|
||
const param = Astro.url.searchParams.get('anteprima');
|
||
const token = Astro.cookies.get(SESSION_COOKIE)?.value;
|
||
const utente = token ? getSessionUser(getDb(), token) : null;
|
||
// Una data secca (`2026-08-20`) sarebbe letta come UTC: la si porta a mezzogiorno italiano,
|
||
// lontano dai confini di mezzanotte dove sta la differenza fra un'edizione e l'altra.
|
||
const quandoParam = param && /^\d{4}-\d{2}-\d{2}$/.test(param) ? `${param}T12:00:00+02:00` : param;
|
||
const dataLeggibile = Boolean(quandoParam && !Number.isNaN(Date.parse(quandoParam)));
|
||
const anteprima = Boolean(utente && dataLeggibile);
|
||
// Un'anteprima chiesta e non applicata va **detta**: altrimenti la pagina risponde con
|
||
// l'edizione di oggi e due date diverse danno lo stesso risultato — che si legge come «la
|
||
// funzione non funziona» invece che «non sei entrato».
|
||
const anteprimaIgnorata = !param || anteprima ? null : (!utente ? 'login' : 'data');
|
||
const quando = anteprima ? new Date(quandoParam!) : new Date();
|
||
const ed = edizioneAttiva(quando);
|
||
// La pagina cambia contenuto col passare del tempo: una copia in cache la congelerebbe alla
|
||
// vecchia edizione, e lo scambio avverrebbe quando pare al proxy invece che a mezzanotte.
|
||
Astro.response.headers.set('Cache-Control', 'no-store');
|
||
|
||
// Tag dell'edizione attiva: `tg('card.1.price')` → `promo.card.1.price` o `bundle.card.1.price`.
|
||
const tg = (suffisso: string) => (ed ? tagEdizione(ed, suffisso) : suffisso);
|
||
// Numero WhatsApp centralizzato: le risposte FAQ contengono il segnaposto {{whatsapp}}
|
||
// che qui viene sostituito col valore del tag globale, così si aggiorna in un punto solo.
|
||
const wa = t('global.whatsapp');
|
||
const waLink = `https://wa.me/39${wa.replace(/\D/g, '')}`;
|
||
// Scadenza dell'edizione in corso (tag editabile dal pannello, ISO con offset). Viene solo
|
||
// passata al client: è il JS a decidere se mostrare il countdown, perché il confronto va fatto
|
||
// col tempo del visitatore.
|
||
const countdownDeadline = ed ? t(tg('countdown.deadline')) : '';
|
||
---
|
||
<Base title="Promo ed eventi — InsanityLab" description={ed ? t(tg('hero.desc')).replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim() : 'Le promozioni di apertura sono terminate: scrivici su WhatsApp per i percorsi attivi.'}>
|
||
<div class="lp">
|
||
<div class="page">
|
||
|
||
{anteprima && (
|
||
<div class="anteprima">
|
||
Anteprima al {quando.toLocaleString('it-IT', { timeZone: 'Europe/Rome' })} —
|
||
{ed ? ` edizione «${ed.id}»` : ' nessuna edizione attiva'}
|
||
</div>
|
||
)}
|
||
{anteprimaIgnorata === 'login' && (
|
||
<div class="anteprima ignorata">
|
||
Anteprima non applicata: questa è la pagina di oggi. Entra da <a href="/admin/login">/admin/login</a> e ricarica.
|
||
</div>
|
||
)}
|
||
{anteprimaIgnorata === 'data' && (
|
||
<div class="anteprima ignorata">
|
||
Anteprima non applicata: «{param}» non è una data leggibile. Usa il formato 2026-08-25.
|
||
</div>
|
||
)}
|
||
|
||
{ed ? (
|
||
<Fragment>
|
||
<section class="hero">
|
||
<T tag={tg('hero.badge')} as="div" class="badge" />
|
||
<T tag={tg('hero.title1')} as="h1" />
|
||
<T tag={tg('hero.accent')} as="p" class="accent" />
|
||
<T tag={tg('hero.title2')} as="h1" />
|
||
<T tag={tg('hero.desc')} as="p" class="desc" />
|
||
<a class="btn-sand" href="#proposte"><T tag={tg('hero.cta')} as="span" /></a>
|
||
</section>
|
||
|
||
{ed.launch && (
|
||
<Fragment>
|
||
<section class="launch">
|
||
<T tag={tg('launch.badge')} as="div" class="badge" />
|
||
<T tag={tg('launch.title')} as="h1" />
|
||
<T tag={tg('launch.accent')} as="p" class="accent" />
|
||
<T tag={tg('launch.desc')} as="p" class="desc" />
|
||
</section>
|
||
|
||
<section class="why5">
|
||
<T tag={tg('why5.text')} as="p" />
|
||
</section>
|
||
</Fragment>
|
||
)}
|
||
|
||
<section class="cards-wrap" id="proposte">
|
||
<div class="countdown" data-deadline={countdownDeadline} hidden>
|
||
<T tag={tg('countdown.label')} as="div" class="cd-label" />
|
||
<div class="cd-boxes">
|
||
<div class="cd-box"><span class="cd-num" data-cd="days">–</span><T tag={tg('countdown.unit.days')} as="span" class="cd-unit" /></div>
|
||
<div class="cd-box"><span class="cd-num" data-cd="hours">–</span><T tag={tg('countdown.unit.hours')} as="span" class="cd-unit" /></div>
|
||
<div class="cd-box"><span class="cd-num" data-cd="minutes">–</span><T tag={tg('countdown.unit.minutes')} as="span" class="cd-unit" /></div>
|
||
<div class="cd-box"><span class="cd-num" data-cd="seconds">–</span><T tag={tg('countdown.unit.seconds')} as="span" class="cd-unit" /></div>
|
||
</div>
|
||
</div>
|
||
<div class:list={['cards', `cards-${ed.cards.length}`]}>
|
||
{ed.cards.map((c) => (
|
||
<div class:list={['card', c.featured && 'featured']}>
|
||
{c.featured && <T tag={tg(`card.${c.id}.badge`)} as="div" class="featured-badge" />}
|
||
<T tag={tg(`card.${c.id}.eyebrow`)} as="div" class="eyebrow" />
|
||
<T tag={tg(`card.${c.id}.title`)} as="div" class="title" />
|
||
{c.question && <T tag={tg(`card.${c.id}.question`)} as="p" class="question" />}
|
||
<div class="price-row">
|
||
<T tag={tg(`card.${c.id}.price`)} as="div" class="price" />
|
||
{c.priceOld && <T tag={tg(`card.${c.id}.price-old`)} as="div" class="price-old" />}
|
||
</div>
|
||
{c.unit && <T tag={tg(`card.${c.id}.unit`)} as="div" class="unit" />}
|
||
<ul>
|
||
{Array.from({ length: c.features }, (_, i) => i + 1).map((m) => (
|
||
<T tag={tg(`card.${c.id}.feature.${m}`)} as="li" />
|
||
))}
|
||
</ul>
|
||
{c.desc && <T tag={tg(`card.${c.id}.desc`)} as="p" class="card-desc" />}
|
||
{c.note && (
|
||
<div class="note-box">
|
||
<T tag={tg(`card.${c.id}.note-label`)} as="div" class="note-label" />
|
||
<T tag={tg(`card.${c.id}.note`)} as="div" class="note-text" />
|
||
</div>
|
||
)}
|
||
<a class="buy" href={BSPORT_URL} target="_blank" rel="noopener"><T tag={tg('buy-label')} as="span" /></a>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<T tag={tg('validity-note')} as="p" class="validity-note" />
|
||
</section>
|
||
|
||
<section class="faq-wrap">
|
||
<T tag={tg('faq.title')} as="h1" />
|
||
{ed.faq.map((n) => {
|
||
const a = resolveContent(tg(`faq.${n}.a`));
|
||
return (
|
||
<div class="faq-block">
|
||
<div class="faq-q"><T tag={tg(`faq.${n}.q`)} as="p" /><div class="faq-toggle">+</div></div>
|
||
<p class:list={['faq-a', a.classes]} data-tag={tg(`faq.${n}.a`)} data-guid={a.guid || undefined} set:html={a.value.replaceAll('{{whatsapp}}', wa)} />
|
||
</div>
|
||
);
|
||
})}
|
||
</section>
|
||
</Fragment>
|
||
) : (
|
||
<section class="hero chiusa">
|
||
<T tag="promo.chiusa.title" as="h1" />
|
||
<T tag="promo.chiusa.desc" as="p" class="desc" />
|
||
<a class="btn-sand" href={waLink} target="_blank" rel="noopener"><T tag="promo.chiusa.cta" as="span" /></a>
|
||
</section>
|
||
)}
|
||
|
||
</div>
|
||
</div>
|
||
</Base>
|
||
|
||
<script>
|
||
// Countdown alla scadenza della promo. Parte nascosto (attributo `hidden` nel markup) e viene
|
||
// mostrato solo se la deadline è una data valida ancora futura: così, a promo finita, il blocco
|
||
// semplicemente non compare — nessun messaggio, nessun salto di layout.
|
||
const cd = document.querySelector<HTMLElement>('.lp .countdown');
|
||
if (cd) {
|
||
const end = Date.parse(cd.dataset.deadline || '');
|
||
const nums = new Map<string, HTMLElement>();
|
||
cd.querySelectorAll<HTMLElement>('.cd-num').forEach((n) => nums.set(n.dataset.cd!, n));
|
||
const pad = (n: number) => String(n).padStart(2, '0');
|
||
// Dichiarato prima di `tick` perché la prima chiamata è sincrona: se la promo è già scaduta
|
||
// il timer va fermato subito, e leggerlo dopo il `setInterval` darebbe un errore di TDZ.
|
||
let timer = 0;
|
||
const tick = () => {
|
||
const left = end - Date.now();
|
||
if (Number.isNaN(end) || left <= 0) { cd.hidden = true; clearInterval(timer); return; }
|
||
const s = Math.floor(left / 1000);
|
||
nums.get('days')!.textContent = String(Math.floor(s / 86400));
|
||
nums.get('hours')!.textContent = pad(Math.floor(s / 3600) % 24);
|
||
nums.get('minutes')!.textContent = pad(Math.floor(s / 60) % 60);
|
||
nums.get('seconds')!.textContent = pad(s % 60);
|
||
cd.hidden = false;
|
||
};
|
||
tick();
|
||
timer = setInterval(tick, 1000);
|
||
}
|
||
|
||
// Accordion FAQ: click (o Invio/Spazio) sul quesito apre/chiude la risposta.
|
||
document.querySelectorAll('.lp .faq-block .faq-q').forEach((q) => {
|
||
const block = q.closest('.faq-block');
|
||
const toggle = q.querySelector('.faq-toggle');
|
||
q.setAttribute('role', 'button');
|
||
q.setAttribute('tabindex', '0');
|
||
q.setAttribute('aria-expanded', 'false');
|
||
const set = (open: boolean) => {
|
||
block?.classList.toggle('is-open', open);
|
||
q.setAttribute('aria-expanded', String(open));
|
||
if (toggle) toggle.textContent = open ? '−' : '+';
|
||
};
|
||
q.addEventListener('click', () => set(!block?.classList.contains('is-open')));
|
||
q.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); set(!block?.classList.contains('is-open')); }
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<!-- is:global perché i testi editabili sono renderizzati dal componente <T> (fuori dallo
|
||
scope della pagina): con lo scope Astro il CSS non li raggiungerebbe. Ogni regola è
|
||
prefissata `.lp`, quindi resta confinata a questa landing senza toccare il resto del sito. -->
|
||
<style is:global>
|
||
.lp { background: #ECE7DC; }
|
||
.lp .page { max-width: 1000px; margin: 0 auto; background: #FFFFFF; font-family: 'Inter', system-ui, Arial, sans-serif; }
|
||
|
||
/* HERO */
|
||
.lp .hero { background: #2D2D2D; padding: 72px 64px 84px; }
|
||
.lp .badge {
|
||
display: inline-block; padding: 7px 16px; border: 0.5px solid rgba(196,168,130,0.5);
|
||
border-radius: 999px; margin-bottom: 24px; font-size: 11px; letter-spacing: 0.08em;
|
||
color: #C4A882; font-weight: 500; text-transform: uppercase;
|
||
}
|
||
.lp .hero h1 { margin: 0; font-size: 44px; line-height: 1.2; font-weight: 700; color: #FFFFFF; }
|
||
.lp .hero .accent { margin: 4px 0; font-size: 38px; line-height: 1.3; font-style: italic; color: #C4A882; font-family: Georgia, serif; }
|
||
.lp .hero p.desc { margin: 28px 0 32px; font-size: 16px; line-height: 1.7; color: #B8B6AE; max-width: 480px; }
|
||
.lp .btn-sand {
|
||
display: inline-block; background: #C4A882; color: #2C2C2A; border: none; padding: 15px 30px;
|
||
border-radius: 999px; font-size: 13px; font-weight: 700; letter-spacing: 0.04em; cursor: pointer;
|
||
text-decoration: none;
|
||
}
|
||
/* La freccia è decorazione (il testo del tag non la include più) */
|
||
.lp .btn-sand::after { content: ' →'; }
|
||
.lp .card .buy::after { content: ' →'; }
|
||
|
||
/* LAUNCH */
|
||
.lp .launch { background: #F2ECE1; padding: 72px 64px; }
|
||
.lp .launch .badge { border-color: rgba(140,107,55,0.35); color: #8A6E3F; }
|
||
.lp .launch h1 { margin: 0; font-size: 40px; line-height: 1.25; font-weight: 700; color: #2D2D2D; }
|
||
.lp .launch .accent { margin: 8px 0 28px; font-size: 30px; line-height: 1.35; font-style: italic; color: #8A6E3F; font-family: Georgia, serif; }
|
||
.lp .launch p.desc { margin: 0; font-size: 16px; line-height: 1.7; color: #5C564A; max-width: 540px; }
|
||
|
||
/* PERCHE 5 */
|
||
.lp .why5 { padding: 48px 64px 8px; }
|
||
.lp .why5 p { margin: 0; font-size: 18px; line-height: 1.7; font-style: italic; color: #2D2D2D; font-family: Georgia, serif; max-width: 620px; }
|
||
|
||
/* COUNTDOWN */
|
||
/* `hidden` va forzato: i display flex qui sotto avrebbero comunque la meglio sul default UA. */
|
||
.lp .countdown[hidden] { display: none !important; }
|
||
.lp .countdown {
|
||
display: flex; flex-direction: column; align-items: center; gap: 14px;
|
||
background: #2D2D2D; border-radius: 14px; padding: 26px 24px; margin: 0 0 28px;
|
||
}
|
||
.lp .countdown .cd-label {
|
||
font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700; color: #C4A882;
|
||
}
|
||
.lp .countdown .cd-boxes { display: flex; gap: 10px; }
|
||
.lp .countdown .cd-box {
|
||
display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 74px;
|
||
background: rgba(255,255,255,0.06); border: 0.5px solid rgba(196,168,130,0.35);
|
||
border-radius: 10px; padding: 12px 10px;
|
||
}
|
||
.lp .countdown .cd-num { font-size: 30px; font-weight: 700; line-height: 1; color: #FFFFFF; font-variant-numeric: tabular-nums; }
|
||
.lp .countdown .cd-unit { font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: #B8B6AE; }
|
||
|
||
/* CARDS */
|
||
.lp .cards-wrap { padding: 32px 64px 72px; }
|
||
.lp .cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
|
||
.lp .card { background: #FAF8F3; border: 0.5px solid #E3DCCB; border-radius: 14px; padding: 26px; position: relative; }
|
||
.lp .card.featured { border: 2px solid #2D2D2D; }
|
||
.lp .featured-badge {
|
||
position: absolute; top: -12px; left: 50%; transform: translateX(-50%); background: #2D2D2D;
|
||
color: #FFFFFF; font-size: 11px; padding: 4px 14px; border-radius: 999px; white-space: nowrap; font-weight: 500;
|
||
}
|
||
.lp .card .eyebrow { font-size: 11px; letter-spacing: 0.08em; color: #8A8980; margin: 0 0 4px; text-transform: uppercase; font-weight: 700; }
|
||
.lp .card .title { font-size: 19px; font-weight: 700; color: #2D2D2D; margin: 0 0 14px; }
|
||
.lp .card .price-row { display: flex; align-items: baseline; gap: 10px; margin: 0 0 2px; }
|
||
.lp .card .price { font-size: 26px; font-weight: 700; color: #2D2D2D; margin: 0; }
|
||
.lp .card .price-old { font-size: 15px; color: #ADABA1; text-decoration: line-through; font-weight: 400; }
|
||
.lp .card .unit { font-size: 12px; color: #8A8980; margin: 0 0 18px; }
|
||
.lp .card ul { list-style: none; padding: 0; margin: 0 0 20px; display: flex; flex-direction: column; gap: 9px; }
|
||
.lp .card li { font-size: 13.5px; line-height: 1.5; color: #5C564A; display: flex; gap: 8px; }
|
||
.lp .card li::before { content: "—"; color: #C4A882; font-weight: 700; flex-shrink: 0; }
|
||
.lp .card .buy {
|
||
display: block; width: 100%; box-sizing: border-box; text-align: center; text-decoration: none;
|
||
background: #2D2D2D; color: #FFFFFF; border: none; padding: 13px; border-radius: 999px;
|
||
font-size: 12px; font-weight: 700; letter-spacing: 0.03em; cursor: pointer;
|
||
}
|
||
.lp .card.featured .buy { background: #C4A882; color: #2C2C2A; }
|
||
|
||
.lp .validity-note { text-align: center; font-size: 12px; color: #8A8980; margin: 22px 0 0; }
|
||
|
||
/* CARD DELLE EDIZIONI A TRE (Opening Bundles): domanda in testa, descrizione e riquadro
|
||
col periodo di utilizzo. Le card restano in colonna, quindi il bottone finisce ad altezze
|
||
diverse se i testi hanno lunghezze diverse: `flex` + `margin-top:auto` lo tiene in fondo. */
|
||
.lp .cards-3 { grid-template-columns: repeat(3, 1fr); }
|
||
.lp .cards-3 .card { display: flex; flex-direction: column; }
|
||
.lp .cards-3 .card .buy { margin-top: auto; }
|
||
|
||
/* CARD DELLE EDIZIONI A CINQUE (promo settembre). Cinque non si divide in una griglia a due
|
||
o a tre colonne senza lasciare un buco in fondo: la griglia è a sei colonne e ogni card ne
|
||
occupa due, così le ultime due si spostano al centro invece di restare allineate a sinistra
|
||
con un vuoto accanto. */
|
||
.lp .cards-5 { grid-template-columns: repeat(6, 1fr); }
|
||
.lp .cards-5 .card { grid-column: span 2; display: flex; flex-direction: column; }
|
||
.lp .cards-5 .card .buy { margin-top: auto; }
|
||
.lp .cards-5 .card:nth-child(4) { grid-column: 2 / span 2; }
|
||
.lp .cards-5 .card:nth-child(5) { grid-column: 4 / span 2; }
|
||
.lp .card .question {
|
||
margin: 0 0 14px; font-size: 15px; line-height: 1.4; font-weight: 700; color: #2D2D2D;
|
||
}
|
||
.lp .card .card-desc {
|
||
margin: 0 0 18px; font-size: 13px; line-height: 1.6; color: #6E685C; font-style: italic;
|
||
}
|
||
.lp .card .note-box { background: #F2ECE1; border-radius: 8px; padding: 10px 12px; margin: 0 0 18px; }
|
||
.lp .card .note-label {
|
||
font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; font-weight: 700;
|
||
color: #8A6E3F; margin-bottom: 3px;
|
||
}
|
||
.lp .card .note-text { font-size: 12px; line-height: 1.5; color: #5C564A; }
|
||
|
||
/* Pagina senza nessuna promozione attiva: fra un'edizione e la successiva. */
|
||
.lp .hero.chiusa { padding-bottom: 72px; }
|
||
.lp .hero.chiusa h1 { font-size: 34px; }
|
||
.lp .hero.chiusa p.desc { margin-bottom: 28px; }
|
||
|
||
/* Barra di anteprima: visibile solo a chi è loggato e ha chiesto una data (?anteprima=…).
|
||
Deve essere impossibile scambiarla per la pagina vera. */
|
||
.lp .anteprima {
|
||
background: #8A6E3F; color: #FFFFFF; font-size: 12px; font-weight: 700; letter-spacing: 0.04em;
|
||
padding: 10px 16px; text-align: center;
|
||
}
|
||
/* Anteprima chiesta e non applicata: colore diverso, perché il contenuto sotto è quello di
|
||
oggi e non quello della data richiesta. */
|
||
.lp .anteprima.ignorata { background: #8B2E2E; }
|
||
.lp .anteprima.ignorata a { color: #FFFFFF; }
|
||
|
||
/* FAQ */
|
||
.lp .faq-wrap { padding: 72px 64px; }
|
||
.lp .faq-wrap h1 { margin: 0 0 40px; font-size: 34px; line-height: 1.25; font-weight: 700; color: #2D2D2D; max-width: 480px; }
|
||
.lp .faq-block { border-top: 0.5px solid #E3DCCB; padding: 22px 0; }
|
||
.lp .faq-block:last-child { border-bottom: 0.5px solid #E3DCCB; }
|
||
.lp .faq-q { display: flex; justify-content: space-between; align-items: center; gap: 24px; cursor: pointer; }
|
||
.lp .faq-q p { margin: 0; font-size: 16px; font-weight: 500; color: #2D2D2D; }
|
||
.lp .faq-q:focus-visible { outline: 2px solid #8A6E3F; outline-offset: 4px; }
|
||
.lp .faq-toggle {
|
||
width: 30px; height: 30px; border-radius: 50%; border: 0.5px solid #C9C3B4; display: flex;
|
||
align-items: center; justify-content: center; flex-shrink: 0; font-size: 16px; color: #2D2D2D;
|
||
transition: background .2s ease, color .2s ease, border-color .2s ease;
|
||
}
|
||
.lp .faq-block.is-open .faq-toggle { background: #2D2D2D; color: #FFFFFF; border-color: #2D2D2D; }
|
||
.lp .faq-a { display: none; margin: 14px 0 0; font-size: 14.5px; line-height: 1.7; color: #8A8980; max-width: 620px; }
|
||
.lp .faq-block.is-open .faq-a { display: block; }
|
||
.lp .faq-a .hl { color: #8A6E3F; }
|
||
|
||
/* Sotto i 1000px tre colonne diventano troppo strette per un prezzo e un elenco: le cinque
|
||
card passano a due per riga (griglia a quattro, span due), con la quinta al centro.
|
||
Nessun `min-width` a chiudere la fascia in basso: a viewport frazionari (uno zoom del
|
||
browser dà larghezze come 700,5px) fra 700 e 701 non varrebbe nessuna delle due regole e
|
||
le card tornerebbero a sei colonne. Ci pensa il blocco ≤700px, che viene dopo e a pari
|
||
specificità vince. */
|
||
@media (max-width: 1000px) {
|
||
.lp .cards-5 { grid-template-columns: repeat(4, 1fr); }
|
||
.lp .cards-5 .card:nth-child(4) { grid-column: span 2; }
|
||
.lp .cards-5 .card:nth-child(5) { grid-column: 2 / span 2; }
|
||
}
|
||
|
||
@media (max-width: 700px) {
|
||
.lp .hero, .lp .launch, .lp .why5, .lp .cards-wrap, .lp .faq-wrap { padding-left: 28px; padding-right: 28px; }
|
||
.lp .cards { grid-template-columns: 1fr; }
|
||
/* A una colonna gli span e le collocazioni delle ultime due card lascerebbero righe vuote. */
|
||
.lp .cards-5 .card,
|
||
.lp .cards-5 .card:nth-child(4),
|
||
.lp .cards-5 .card:nth-child(5) { grid-column: auto; }
|
||
.lp .countdown .cd-boxes { gap: 6px; }
|
||
.lp .countdown .cd-box { min-width: 0; flex: 1; padding: 10px 4px; }
|
||
.lp .countdown .cd-num { font-size: 22px; }
|
||
.lp .countdown .cd-unit { font-size: 9.5px; }
|
||
.lp .hero h1 { font-size: 32px; }
|
||
.lp .launch h1 { font-size: 28px; }
|
||
}
|
||
</style>
|