232 lines
11 KiB
HTML
232 lines
11 KiB
HTML
<!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>
|
||
<p class="caption" style="margin-top:10px">Tipp: Aktualisiere regelmäßig – Nutzer lieben sichtbare Entwicklung.</p>
|
||
</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" id="storeCta" href="https://app.vanityontour.de" target="_blank" rel="noopener">Zum App-Link</a>
|
||
<a class="btn secondary" id="altCta" href="https://vanityontour.de/landing" target="_blank" rel="noopener">Tracking-Landing</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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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>
|