/** * BUILD — macht aus config.js die fertige dist/index.html. * Aufruf: node build.js * * Hier steckt das DESIGN. Normalerweise musst du diese Datei nicht anfassen — * nur config.js ausfüllen. Wer das Layout ändern will, ist hier richtig. */ const fs = require('fs'); const path = require('path'); const c = require('./config.js'); // kleine Helfer ------------------------------------------------------- const esc = (s = '') => String(s).replace(/&/g, '&').replace(//g, '>'); const has = (x) => Array.isArray(x) ? x.length > 0 : !!x; const papier = c.theme !== 'clean'; // Standard = Papier-Look // CSS ---------------------------------------------------------------- const css = ` :root{ --bg:${c.colors.bg}; --text:${c.colors.text}; --accent:${c.colors.accent}; --marker:${c.colors.marker}; --grau:color-mix(in srgb, var(--text) 55%, transparent); --linie:color-mix(in srgb, var(--text) 18%, transparent); color-scheme:light; } *{margin:0;padding:0;box-sizing:border-box} html{scroll-behavior:smooth} body{ font-family:${papier ? "Georgia,'Times New Roman',serif" : "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif"}; color:var(--text); line-height:1.7; font-size:17px; background:${papier ? "repeating-linear-gradient(0deg,transparent 0 34px,rgba(0,0,0,.025) 34px 35px),var(--bg)" : "var(--bg)"}; } .wrap{max-width:720px;margin:0 auto;padding:0 20px 72px} a{color:var(--accent)} .kopf{display:flex;justify-content:space-between;align-items:baseline;padding:20px 0 12px;border-bottom:2px solid var(--text);font-size:15px} .marke{font-weight:700;font-size:17px;text-decoration:none;color:var(--text)} header{padding:44px 0 8px} .kicker{font-size:13px;color:var(--grau);text-transform:uppercase;letter-spacing:.14em;margin-bottom:10px} h1{font-size:clamp(34px,7vw,52px);line-height:1.12;font-weight:700;letter-spacing:-.01em} .mark{background:linear-gradient(104deg,transparent 2%,var(--marker) 4%,var(--marker) 96%,transparent 98%);padding:0 6px;box-decoration-break:clone;-webkit-box-decoration-break:clone} .anrede{margin-top:24px;font-size:19px;max-width:560px} .hero-fuss{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-top:30px} .cta{display:inline-block;padding:16px 28px;background:var(--accent);color:#fff;text-decoration:none;font-family:inherit;font-size:17px;font-weight:700;border:2px solid var(--text);box-shadow:5px 5px 0 var(--text);transition:transform .12s,box-shadow .12s} .cta:hover{transform:translate(2px,2px);box-shadow:3px 3px 0 var(--text)} .cta-sek{background:#fff;color:var(--accent)} .stempel{display:inline-block;padding:7px 14px;border:2.5px solid var(--accent);color:var(--accent);font-family:${papier ? "'Courier New',monospace" : 'inherit'};font-weight:700;font-size:14px;letter-spacing:.1em;text-transform:uppercase;${papier ? 'transform:rotate(-4deg);' : 'border-radius:6px;'}opacity:.9} section{margin-top:60px} h2{font-size:15px;text-transform:uppercase;letter-spacing:.14em;color:var(--grau);font-weight:400;margin-bottom:22px;display:flex;align-items:center;gap:12px} h2::after{content:'';flex:1;border-top:1px solid var(--linie)} h2 .nr{font-family:'Courier New',monospace} .grid{display:grid;grid-template-columns:1fr 1fr;gap:14px} @media(max-width:560px){.grid{grid-template-columns:1fr}} .karte{background:#fff;border:1px solid var(--linie);padding:20px 22px;${papier ? 'box-shadow:4px 5px 0 rgba(0,0,0,.06);' : 'border-radius:12px;'}} .karte .ico{font-size:26px} .karte h3{font-size:19px;margin:8px 0 4px} .karte p{font-size:15px;color:var(--grau)} .zahl{font-size:38px;font-weight:700;color:var(--accent);line-height:1.1} .zahl-erkl{font-size:15px;color:var(--grau);margin-top:6px} .zahl-q{font-size:12px;color:var(--grau);margin-top:8px;font-family:'Courier New',monospace} .schritt{display:flex;gap:20px;padding:18px 4px;border-bottom:1px solid var(--linie);align-items:baseline} .schritt:last-child{border-bottom:none} .schritt .n{font-family:'Courier New',monospace;font-size:28px;font-weight:700;color:var(--accent);width:48px;flex-shrink:0} .schritt strong{font-size:19px;display:block;margin-bottom:2px} .schritt span{color:var(--grau);font-size:16px} .antrag{background:#fff;border:2px solid var(--text);${papier ? 'box-shadow:7px 8px 0 rgba(0,0,0,.85);' : 'border-radius:14px;box-shadow:0 4px 16px rgba(0,0,0,.08);'}padding:28px 26px} .antrag h3{font-size:26px;line-height:1.25} .antrag p{margin-top:12px;max-width:520px} .bonus{margin:16px 0;padding:12px 16px;background:linear-gradient(104deg,transparent 1%,var(--marker) 3%,var(--marker) 97%,transparent 99%);font-size:16px} .wl-form{display:flex;flex-direction:column;gap:12px;margin-top:6px} .wl-input{width:100%;padding:15px 16px;font-size:17px;font-family:inherit;border:2px solid var(--text);background:#fff;color:var(--text)} .wl-btn{width:100%;cursor:pointer;text-align:center;font-family:inherit} .klein{font-size:13.5px;color:var(--grau);margin-top:12px} details{border-bottom:1px solid var(--linie);padding:14px 4px} summary{font-weight:700;cursor:pointer;font-size:17px;list-style:none;display:flex;justify-content:space-between;gap:12px} summary::-webkit-details-marker{display:none} summary::after{content:'+';font-family:'Courier New',monospace;font-size:22px;color:var(--accent)} details[open] summary::after{content:'–'} details p{margin-top:10px;color:var(--grau);font-size:16px;max-width:580px} footer{margin-top:56px;padding-top:16px;border-top:2px solid var(--text);font-size:14.5px;color:var(--grau)} footer a{color:var(--grau)} `; // Abschnitte --------------------------------------------------------- const nr = (n) => papier ? `§ ${n} ` : ''; let s = 0; // Abschnittszähler function features() { if (!has(c.features)) return ''; s++; return `

${nr(s)}${esc(c.featuresTitle || 'Vorteile')}

${c.features.map(f => `
${f.icon ? `
${esc(f.icon)}
` : ''}

${esc(f.title)}

${esc(f.text)}

`).join('')}
`; } function stats() { if (!has(c.stats)) return ''; s++; return `

${nr(s)}In Zahlen

${c.stats.map(z => `
${esc(z.value)}
${esc(z.label)}
${z.source ? `
Quelle: ${esc(z.source)}
` : ''}
`).join('')}
`; } function steps() { if (!has(c.steps)) return ''; s++; return `

${nr(s)}${esc(c.stepsTitle || 'So funktioniert es')}

${c.steps.map((st, i) => `
${String(i + 1).padStart(2, '0')}
${esc(st.title)}${esc(st.text)}
`).join('')}
`; } function waitlist() { const w = c.waitlist; if (!w || !w.title) return ''; s++; const form = w.formAction ? `
` : `${esc(w.buttonLabel || 'Per E-Mail eintragen')}`; return `

${nr(s)}Warteliste

${esc(w.title)}

${w.text ? `

${esc(w.text)}

` : ''} ${w.bonus ? `

${esc(w.bonus)}

` : ''} ${form} ${w.note ? `

${esc(w.note)}

` : ''}
`; } function faq() { if (!has(c.faq)) return ''; s++; return `

${nr(s)}${esc(c.faqTitle || 'Häufige Fragen')}

${c.faq.map(f => `
${esc(f.q)}

${esc(f.a)}

`).join('')}
`; } // Seite zusammenbauen ------------------------------------------------ const h = c.hero || {}; const secondary = h.secondaryCta && h.secondaryCta.href !== h.primaryCta?.href ? `${esc(h.secondaryCta.label)}` : ''; const html = ` ${esc(c.seo.title || c.brand.name)} ${c.brand.url ? `` : ''} ${c.seo.ogImage ? `` : ''}
${c.brand.emoji ? esc(c.brand.emoji) + ' ' : ''}${esc(c.brand.name)}
${h.kicker ? `

${esc(h.kicker)}

` : ''}

${esc(h.title || c.brand.name)}${h.titleHighlight ? `
${esc(h.titleHighlight)}` : ''}

${h.text ? `

${esc(h.text)}

` : ''}
${h.primaryCta ? `${esc(h.primaryCta.label)}` : ''} ${secondary} ${h.stamp ? `${esc(h.stamp)}` : ''}
${features()} ${stats()} ${steps()} ${waitlist()} ${faq()}
${c.analyticsToken ? `` : ''} `; // schreiben ---------------------------------------------------------- const out = path.join(__dirname, 'dist'); fs.mkdirSync(out, { recursive: true }); fs.writeFileSync(path.join(out, 'index.html'), html); // assets/ (Favicon etc.) mitkopieren, falls vorhanden const assets = path.join(__dirname, 'assets'); if (fs.existsSync(assets)) { for (const f of fs.readdirSync(assets)) fs.copyFileSync(path.join(assets, f), path.join(out, f)); } console.log(`✓ Fertig: dist/index.html (Theme: ${c.theme}, ${s} Abschnitte)`);