landingpage-vanity-expense/index.html

230 lines
10 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Vanity Expense Logbook die App fürs Vanlife</title>
<!-- Basis-SEO bleibt statisch -->
<meta name="description" content="Behalte Ausgaben im Blick tracke Tank, Gas & Service. iOS-App fürs Vanlife." />
<meta property="og:title" content="Vanity Expense Logbook" />
<meta property="og:description" content="Behalte Ausgaben im Blick tracke Tank, Gas & Service." />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://app.vanityontour.de/assets/img/hero-van.png" />
<meta name="theme-color" content="#111827" />
<style>
:root { --bg:#0b0f17; --fg:#e5e7eb; --muted:#9ca3af; --brand:#2563eb; --card:#111827; }
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font:16px/1.6 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial}
a{color:var(--fg);text-decoration:none}
.wrap{max-width:1100px;margin:0 auto;padding:20px}
.hero{position:relative;border-radius:20px;overflow:hidden;background:#000}
.hero img{width:100%;height:440px;object-fit:cover;opacity:.55;display:block}
.hero .overlay{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;padding:28px 24px 24px}
.eyebrow{letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:600;margin-bottom:6px}
h1{font-size:clamp(28px,4vw,44px);line-height:1.1;margin:.2em 0 .3em}
h2,h3{margin:.2em 0 .4em}
.sub{max-width:60ch;color:#d1d5db}
.cta{margin-top:18px;display:flex;gap:12px;flex-wrap:wrap}
.btn{background:var(--brand);color:#fff;padding:12px 18px;border-radius:12px;font-weight:700;display:inline-flex;align-items:center;gap:10px}
.btn.secondary{background:#374151}
.grid{display:grid;gap:18px;margin-top:28px}
@media(min-width:900px){ .grid.cols-3{grid-template-columns:repeat(3,1fr)} .grid.cols-2{grid-template-columns:repeat(2,1fr)} }
.card{background:var(--card);border:1px solid #1f2937;border-radius:16px;padding:18px}
.feat{display:flex;gap:12px;align-items:flex-start}
.feat .dot{width:10px;height:10px;border-radius:999px;background:var(--brand);margin-top:.6em}
.gallery{display:grid;gap:14px;margin-top:8px}
@media(min-width:750px){ .gallery{grid-template-columns:repeat(3,1fr)} }
.gallery figure{margin:0}
.gallery img{width:100%;height:100%;object-fit:cover;border-radius:14px;border:1px solid #1f2937;cursor:zoom-in}
.caption{font-size:14px;color:var(--muted);margin-top:6px}
.footer{margin:40px 0 10px;color:var(--muted);font-size:14px;text-align:center}
.badges{display:flex;gap:12px;flex-wrap:wrap}
/* Lightbox */
.lightbox{position:fixed;inset:0;background:rgba(0,0,0,.8);display:none;align-items:center;justify-content:center;padding:24px;z-index:50}
.lightbox.open{display:flex}
.lightbox img{max-width:min(100%,1100px);max-height:90vh;border-radius:12px}
.lightbox .close{position:absolute;top:14px;right:14px;background:#111827;border:1px solid #374151;border-radius:10px;padding:8px 12px;cursor:pointer}
</style>
</head>
<body>
<main class="wrap">
<!-- HERO -->
<section class="hero" aria-labelledby="hero-title">
<img id="heroImage" src="assets/img/hero-van.png" alt="Camper am See bei Sonnenuntergang" loading="lazy" />
<div class="overlay">
<div class="eyebrow" id="eyebrow">Vanlife • iOS</div>
<h1 id="headline">Vanity Expense Logbook die App fürs Vanlife!</h1>
<p class="sub" id="subline">Behalte Ausgaben im Blick & tracke Tank, Gas & Service. Einfache Übersicht statt Zettelchaos.</p>
<div class="cta">
<a class="btn" id="primaryCta" href="https://app.vanityontour.de" target="_blank" rel="noopener">
<span>Jetzt entdecken</span>
</a>
<a class="btn secondary" id="secondaryCta" href="#features"><span>Mehr erfahren</span></a>
</div>
</div>
</section>
<!-- FEATURES -->
<section id="features" class="grid cols-3" aria-labelledby="features-title" style="scroll-margin-top:24px">
<h2 id="features-title" class="sr-only" style="position:absolute;left:-9999px">Funktionen</h2>
<!-- JS füllt Karten -->
</section>
<!-- GALLERY + CHANGELOG nebeneinander auf großen Screens -->
<section class="grid cols-2" style="margin-top:28px">
<!-- GALLERY -->
<div class="card">
<h2 style="margin:0 0 8px">Einblicke</h2>
<p class="caption" id="galleryIntro">Screens aus der App & Mockups zum Durchklicken.</p>
<div class="gallery" id="gallery" aria-live="polite"></div>
</div>
<!-- CHANGELOG -->
<div class="card">
<h2 style="margin:0 0 8px">Changelog</h2>
<div id="changelog">
<!-- JS rendert Einträge; zeigt Fallback, falls leer -->
</div>
</div>
</section>
<!-- INFO / CTAs -->
<section class="grid" style="margin-top:28px">
<div class="card">
<h3 style="margin-top:0" id="infoTitle">Was ist neu?</h3>
<div id="infoBody">
<p>Verbesserte Auswertung, Karten-Pins & optimierte Eintragsmaske.</p>
</div>
<div class="badges" style="margin-top:10px">
<a class="btn secondary" id="storeCta" href="https://vanityontour.de/landing" target="_blank" rel="noopener">Zum App-Link</a>
</div>
</div>
</section>
<p class="footer">
<span id="footerNote">© Vanity on Tour • Bildmaterial teils KI-generiert • Letzte Aktualisierung: <span id="lastUpdated"></span></span>
</p>
</main>
<!-- Lightbox -->
<div class="lightbox" id="lightbox" role="dialog" aria-modal="true" aria-label="Bildansicht">
<button class="close" id="lbClose" aria-label="Schließen">Schließen</button>
<img id="lbImg" alt="" />
</div>
<script>
// Helper: HTML escapen für Changelog
const esc = (s) => (s || "").replace(/[&<>"']/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));
(async () => {
try {
const res = await fetch('content.json?bust=' + Date.now());
if (!res.ok) throw new Error('content.json nicht gefunden');
const c = await res.json();
// HERO
if (c.hero?.image) {
const hi = document.getElementById('heroImage');
hi.src = c.hero.image.src;
hi.alt = c.hero.image.alt || 'Hero';
}
document.getElementById('eyebrow').textContent = c.hero?.eyebrow ?? 'Vanlife • iOS';
document.getElementById('headline').textContent = c.hero?.headline ?? '';
document.getElementById('subline').textContent = c.hero?.subline ?? '';
if (c.hero?.primaryCta) {
const p = document.getElementById('primaryCta');
p.href = c.hero.primaryCta.href;
p.firstElementChild.textContent = c.hero.primaryCta.label;
}
if (c.hero?.secondaryCta) {
const s = document.getElementById('secondaryCta');
s.href = c.hero.secondaryCta.href;
s.firstElementChild.textContent = c.hero.secondaryCta.label;
}
// FEATURES
const fWrap = document.getElementById('features');
(c.features ?? []).forEach(f => {
const el = document.createElement('div');
el.className = 'card feat';
el.innerHTML = `
<div class="dot"></div>
<div>
<h3 style="margin:0 0 6px">${esc(f.title)}</h3>
<p style="margin:0;color:var(--muted)">${esc(f.text)}</p>
</div>`;
fWrap.appendChild(el);
});
// GALLERY
document.getElementById('galleryIntro').textContent = c.gallery?.intro ?? '';
const g = document.getElementById('gallery');
(c.gallery?.images ?? []).forEach((img, i) => {
const fig = document.createElement('figure');
fig.innerHTML = `
<img src="${img.src}" alt="${esc(img.alt || '')}" data-full="${img.full || img.src}" loading="lazy">
${img.caption ? `<figcaption class="caption">${esc(img.caption)}</figcaption>` : ''}`;
g.appendChild(fig);
});
// CHANGELOG
const ch = document.getElementById('changelog');
if (c.changelog && c.changelog.length) {
ch.innerHTML = c.changelog
.map(entry => `
<div style="padding:10px 0;border-bottom:1px solid #1f2937">
<strong>v${esc(entry.version)}</strong>
<span class="caption"> ${esc(entry.date)}</span>
${entry.notes && entry.notes.length
? `<ul style="margin:6px 0 0 18px">${entry.notes.map(n => `<li>${esc(n)}</li>`).join('')}</ul>`
: ''
}
</div>
`).join('');
} else {
ch.innerHTML = `<p class="caption">Noch keine Einträge. Trage Versionen in <code>content.json</code> unter <code>changelog</code> ein.</p>`;
}
// INFO/CTAs
document.getElementById('infoTitle').textContent = c.info?.title ?? 'Info';
document.getElementById('infoBody').innerHTML = c.info?.bodyHtml ?? '';
if (c.info?.storeCta) {
const a = document.getElementById('storeCta');
a.href = c.info.storeCta.href; a.textContent = c.info.storeCta.label;
}
if (c.info?.altCta) {
const a = document.getElementById('altCta');
a.href = c.info.altCta.href; a.textContent = c.info.altCta.label;
}
// Footer / Datum
document.getElementById('lastUpdated').textContent = c.lastUpdated ?? new Date().toLocaleDateString('de-DE');
} catch (e) {
console.warn('Fallback aktiv:', e.message);
}
// LIGHTBOX: öffnen/schließen
const lb = document.getElementById('lightbox');
const lbImg = document.getElementById('lbImg');
const lbClose = document.getElementById('lbClose');
document.getElementById('gallery').addEventListener('click', (ev) => {
const t = ev.target;
if (t.tagName === 'IMG') {
lbImg.src = t.dataset.full || t.src;
lbImg.alt = t.alt || '';
lb.classList.add('open');
}
});
const close = () => lb.classList.remove('open');
lbClose.addEventListener('click', close);
lb.addEventListener('click', (e) => { if (e.target === lb) close(); });
window.addEventListener('keydown', (e) => { if (e.key === 'Escape') close(); });
})();
</script>
</body>
</html>