<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bitcoin Renten-Rechner</title>
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Syne:wght@400;600;800&display=swap" rel="stylesheet">
<style>
:root {
--bg:#0a0a0f; --surface:#12121a; --card:#1a1a26; --border:#2a2a3d;
--accent:#f7931a; --accent2:#ffd166; --green:#06d6a0; --red:#ef476f;
--text:#e8e8f0; --muted:#7070a0; --glow:rgba(247,147,26,0.15);
}
*{margin:0;padding:0;box-sizing:border-box;}
body{background:var(--bg);color:var(--text);font-family:'Syne',sans-serif;min-height:100vh;overflow-x:hidden;}
body::before{
content:'';position:fixed;top:-50%;left:-50%;width:200%;height:200%;
background:radial-gradient(ellipse at 20% 20%,rgba(247,147,26,0.06) 0%,transparent 50%),
radial-gradient(ellipse at 80% 80%,rgba(6,214,160,0.04) 0%,transparent 50%);
animation:bgPulse 8s ease-in-out infinite alternate;pointer-events:none;z-index:0;
}
@keyframes bgPulse{0%{transform:scale(1);}100%{transform:scale(1.08) rotate(2deg);}}
.container{position:relative;z-index:1;max-width:920px;margin:0 auto;padding:40px 20px;}
header{text-align:center;margin-bottom:48px;}
.btc-icon{
width:52px;height:52px;background:var(–accent);border-radius:50%;
display:inline-flex;align-items:center;justify-content:center;
font-size:26px;font-weight:700;color:#000;
box-shadow:0 0 30px rgba(247,147,26,0.5);
animation:glow 2s ease-in-out infinite alternate;margin-bottom:14px;
}
@keyframes glow{0%{box-shadow:0 0 20px rgba(247,147,26,0.4);}100%{box-shadow:0 0 50px rgba(247,147,26,0.8);}}
h1{
font-size:clamp(26px,5vw,42px);font-weight:800;letter-spacing:-1px;
background:linear-gradient(135deg,#f7931a,#ffd166,#f7931a);background-size:200% auto;
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;
animation:shimmer 3s linear infinite;
}
@keyframes shimmer{0%{background-position:0% center;}100%{background-position:200% center;}}
.subtitle{color:var(–muted);font-size:13px;letter-spacing:2px;text-transform:uppercase;margin-top:8px;font-family:‘Space Mono’,monospace;}
/* PRICE BANNER */
.price-banner{
background:linear-gradient(135deg,rgba(247,147,26,0.08),rgba(255,209,102,0.04));
border:1px solid rgba(247,147,26,0.25);border-radius:16px;padding:22px 28px;
display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:16px;margin-bottom:24px;
}
.pbl{display:flex;flex-direction:column;gap:4px;}
.pbl-lbl{font-size:10px;letter-spacing:2px;text-transform:uppercase;color:var(–muted);font-family:‘Space Mono’,monospace;}
.pbl-val{font-size:26px;font-weight:700;font-family:‘Space Mono’,monospace;color:var(–accent);}
.pbl-date{font-size:11px;color:var(–muted);font-family:‘Space Mono’,monospace;}
.pb-stats{display:flex;gap:24px;flex-wrap:wrap;}
.pbs{display:flex;flex-direction:column;gap:3px;}
.pbs-lbl{font-size:9px;letter-spacing:1.5px;text-transform:uppercase;color:var(–muted);font-family:‘Space Mono’,monospace;}
.pbs-val{font-size:15px;font-weight:700;font-family:‘Space Mono’,monospace;}
/* VALUATION CARD */
.valuation-card{
background:var(–card);border:1px solid var(–border);border-radius:20px;
padding:28px;margin-bottom:24px;position:relative;overflow:hidden;
}
.val-title{font-size:11px;letter-spacing:2px;text-transform:uppercase;color:var(–muted);font-family:‘Space Mono’,monospace;margin-bottom:18px;}
.price-input-row{display:flex;align-items:center;gap:16px;margin-bottom:22px;flex-wrap:wrap;}
.price-input-row label{font-size:11px;letter-spacing:1.5px;text-transform:uppercase;color:var(–muted);font-family:‘Space Mono’,monospace;white-space:nowrap;}
.pi-wrap{position:relative;flex:1;min-width:160px;max-width:260px;}
.pi-wrap input{
width:100%;background:var(–surface);border:1px solid var(–border);border-radius:10px;
padding:11px 46px 11px 16px;color:var(–text);font-size:16px;
font-family:‘Space Mono’,monospace;font-weight:700;outline:none;
transition:border-color .3s,box-shadow .3s;
}
.pi-wrap input:focus{border-color:var(–accent);box-shadow:0 0 12px var(–glow);}
.pi-wrap .unit{position:absolute;right:13px;top:50%;transform:translateY(-50%);color:var(–muted);font-family:‘Space Mono’,monospace;font-size:12px;pointer-events:none;}
.heatmap-scale{
position:relative;height:30px;border-radius:15px;overflow:hidden;margin-bottom:12px;
background:linear-gradient(90deg,
#06d6a0 0%, #3ad67a 15%, #a8d630 28%, #ffd166 42%,
#f7931a 58%, #ef6c47 72%, #ef476f 85%, #c0134e 100%);
}
.heatmap-needle{
position:absolute;top:-3px;width:4px;height:36px;background:#fff;border-radius:2px;
box-shadow:0 0 10px rgba(255,255,255,0.9);
transition:left 0.9s cubic-bezier(0.34,1.56,0.64,1);
}
.heatmap-needle::after{
content:’’;position:absolute;bottom:-6px;left:50%;transform:translateX(-50%);
width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:7px solid #fff;
}
.heatmap-labels{
display:flex;justify-content:space-between;
font-size:9px;color:var(–muted);font-family:‘Space Mono’,monospace;
letter-spacing:1px;text-transform:uppercase;margin-bottom:18px;
}
.val-result{display:flex;align-items:center;gap:18px;flex-wrap:wrap;}
.val-pct{font-size:30px;font-weight:800;font-family:‘Space Mono’,monospace;transition:all .5s;min-width:90px;}
.val-badge{padding:9px 20px;border-radius:50px;font-size:15px;font-weight:700;font-family:‘Space Mono’,monospace;transition:all .5s;}
.val-desc{font-size:12px;color:var(–muted);font-family:‘Space Mono’,monospace;line-height:1.7;flex:1;min-width:180px;}
/* GRID */
.grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:24px;}
@media(max-width:600px){.grid{grid-template-columns:1fr;}}
.card{
background:var(–card);border:1px solid var(–border);border-radius:16px;padding:26px;
position:relative;overflow:hidden;transition:border-color .3s,transform .3s;
}
.card:hover{border-color:rgba(247,147,26,0.3);transform:translateY(-2px);}
.card::before{
content:’’;position:absolute;top:0;left:0;right:0;height:2px;
background:linear-gradient(90deg,transparent,var(–accent),transparent);opacity:0;transition:opacity .3s;
}
.card:hover::before{opacity:1;}
.sec-title{font-size:10px;letter-spacing:2px;text-transform:uppercase;color:var(–muted);font-family:‘Space Mono’,monospace;margin-bottom:14px;padding-bottom:8px;border-bottom:1px solid var(–border);}
label{display:block;font-size:11px;letter-spacing:2px;text-transform:uppercase;color:var(–muted);margin-bottom:10px;font-family:‘Space Mono’,monospace;}
input[type=“range”]{
-webkit-appearance:none;width:100%;height:4px;background:var(–border);
border-radius:2px;outline:none;cursor:pointer;margin:12px 0 8px;
}
input[type=“range”]::-webkit-slider-thumb{
-webkit-appearance:none;width:20px;height:20px;border-radius:50%;background:var(–accent);
cursor:pointer;box-shadow:0 0 10px rgba(247,147,26,0.5);transition:box-shadow .2s,transform .2s;
}
input[type=“range”]::-webkit-slider-thumb:hover{box-shadow:0 0 20px rgba(247,147,26,0.8);transform:scale(1.2);}
.ni-wrap{position:relative;}
.ni-wrap input[type=“number”]{
width:100%;background:var(–surface);border:1px solid var(–border);border-radius:10px;
padding:12px 50px 12px 16px;color:var(–text);font-size:18px;
font-family:‘Space Mono’,monospace;font-weight:700;outline:none;
transition:border-color .3s,box-shadow .3s;
}
.ni-wrap input:focus{border-color:var(–accent);box-shadow:0 0 15px var(–glow);}
.ni-wrap .unit{position:absolute;right:14px;top:50%;transform:translateY(-50%);color:var(–muted);font-family:‘Space Mono’,monospace;font-size:13px;pointer-events:none;}
.vsub{font-size:11px;color:var(–muted);font-family:‘Space Mono’,monospace;margin-top:4px;}
/* RESULT */
.result-card{
background:linear-gradient(135deg,var(–card) 0%,rgba(247,147,26,0.08) 100%);
border:1px solid rgba(247,147,26,0.3);border-radius:20px;
padding:36px;margin-bottom:24px;text-align:center;position:relative;overflow:hidden;
}
.result-card::after{content:‘₿’;position:absolute;right:-20px;bottom:-40px;font-size:160px;opacity:0.03;font-weight:700;color:var(–accent);}
.r-label{font-size:11px;letter-spacing:3px;text-transform:uppercase;color:var(–muted);font-family:‘Space Mono’,monospace;margin-bottom:12px;}
.r-year{font-size:clamp(52px,10vw,80px);font-weight:800;color:var(–accent);line-height:1;text-shadow:0 0 40px rgba(247,147,26,0.4);transition:all .5s;}
.r-sub{color:var(–muted);font-size:13px;margin-top:8px;font-family:‘Space Mono’,monospace;}
.r-badge{display:inline-block;padding:4px 14px;background:rgba(6,214,160,0.1);border:1px solid rgba(6,214,160,0.3);border-radius:20px;color:var(–green);font-size:12px;font-family:‘Space Mono’,monospace;margin-top:10px;}
.already{color:var(–green);font-size:28px;font-weight:700;}
/* STATS */
.stats-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:24px;}
@media(max-width:500px){.stats-grid{grid-template-columns:1fr 1fr;}.stats-grid .sc:last-child{grid-column:span 2;}}
.sc{background:var(–card);border:1px solid var(–border);border-radius:14px;padding:20px;text-align:center;transition:all .3s;}
.sc:hover{border-color:rgba(6,214,160,0.3);}
.sc-val{font-size:18px;font-weight:700;font-family:‘Space Mono’,monospace;color:var(–green);margin-bottom:4px;transition:all .5s;}
.sc-lbl{font-size:10px;color:var(–muted);text-transform:uppercase;letter-spacing:1.5px;font-family:‘Space Mono’,monospace;}
/* TIMELINE */
.tl-card{background:var(–card);border:1px solid var(–border);border-radius:16px;padding:28px;margin-bottom:24px;}
.tl-title{font-size:11px;letter-spacing:2px;text-transform:uppercase;color:var(–muted);font-family:‘Space Mono’,monospace;margin-bottom:20px;}
.tl-row{display:flex;align-items:center;gap:14px;margin-bottom:12px;padding:10px 14px;border-radius:10px;transition:background .2s;}
.tl-row:hover{background:rgba(255,255,255,0.03);}
.tl-yr{min-width:52px;height:28px;border-radius:6px;background:var(–surface);border:1px solid var(–border);font-family:‘Space Mono’,monospace;font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center;color:var(–muted);}
.tl-yr.hl{background:rgba(247,147,26,0.15);border-color:var(–accent);color:var(–accent);}
.tl-bw{flex:1;height:8px;background:var(–border);border-radius:4px;overflow:hidden;}
.tl-b{height:100%;border-radius:4px;background:linear-gradient(90deg,var(–accent),var(–accent2));transition:width .8s cubic-bezier(0.34,1.56,0.64,1);}
.tl-amt{min-width:110px;font-family:‘Space Mono’,monospace;font-size:12px;font-weight:700;text-align:right;}
.tl-mark{font-size:14px;min-width:24px;text-align:center;}
.info-box{
background:rgba(247,147,26,0.05);border:1px solid rgba(247,147,26,0.15);
border-radius:12px;padding:20px;font-size:12px;color:var(–muted);
font-family:‘Space Mono’,monospace;line-height:1.8;
}
.info-box strong{color:var(–accent2);}
.fade-in{animation:fi .5s ease forwards;}
@keyframes fi{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}
/* LIVE PRICE */
.live-dot{
display:inline-block;width:8px;height:8px;border-radius:50%;
background:var(–green);margin-right:6px;
box-shadow:0 0 6px var(–green);
animation:blink 1.4s ease-in-out infinite;
}
@keyframes blink{0%,100%{opacity:1;}50%{opacity:.3;}}
.live-dot.loading{background:var(–muted);box-shadow:none;animation:spin 1s linear infinite;}
@keyframes spin{to{transform:rotate(360deg);}}
.live-dot.error{background:var(–red);box-shadow:0 0 6px var(–red);animation:none;}
.market-price-display{
display:flex;flex-direction:column;gap:3px;
padding:14px 20px;background:rgba(6,214,160,0.06);
border:1px solid rgba(6,214,160,0.2);border-radius:12px;
}
.mp-lbl{font-size:9px;letter-spacing:2px;text-transform:uppercase;color:var(–muted);font-family:‘Space Mono’,monospace;}
.mp-val{font-size:22px;font-weight:700;font-family:‘Space Mono’,monospace;color:var(–green);}
.mp-sub{font-size:10px;color:var(–muted);font-family:‘Space Mono’,monospace;}
.refresh-btn{
background:none;border:1px solid var(–border);border-radius:8px;
color:var(–muted);font-family:‘Space Mono’,monospace;font-size:11px;
padding:6px 12px;cursor:pointer;transition:all .2s;white-space:nowrap;
}
.refresh-btn:hover{border-color:var(–accent);color:var(–accent);}
.refresh-btn:disabled{opacity:.4;cursor:not-allowed;}
</style>
</head>
<body>
<div class="container">
<header>
<div class="btc-icon">₿</div>
<h1>Bitcoin Renten-Rechner</h1>
<p class="subtitle">Power Law · Rentenziel · Kaufbewertung</p>
</header>
<!-- POWER LAW + LIVE PRICE BANNER -->
<div class="price-banner">
<div class="pbl">
<span class="pbl-lbl">⚡ Power Law Median-Preis heute</span>
<span id="plPriceToday" class="pbl-val">–</span>
<span id="plDateToday" class="pbl-date">–</span>
</div>
<!-- LIVE MARKET PRICE -->
<div class="market-price-display">
<span class="mp-lbl"><span class="live-dot loading" id="liveDot"></span>Live Marktpreis (Binance)</span>
<span id="livePrice" class="mp-val">Lädt…</span>
<span id="liveSub" class="mp-sub">–</span>
</div>
<div class="pb-stats">
<div class="pbs">
<span class="pbs-lbl">Unteres Band</span>
<span id="plFloor" class="pbs-val" style="color:var(--green)">–</span>
</div>
<div class="pbs">
<span class="pbs-lbl">Oberes Band</span>
<span id="plCeil" class="pbs-val" style="color:var(--red)">–</span>
</div>
<div class="pbs">
<span class="pbs-lbl">Tage seit Genesis</span>
<span id="plDays" class="pbs-val" style="color:var(--muted)">–</span>
</div>
<div class="pbs">
<span class="pbs-lbl">Ø Wachstum / Jahr (10J)</span>
<span id="plGrowth" class="pbs-val" style="color:var(--accent2)">–</span>
</div>
</div>
</div>
<!-- KAUFBEWERTUNG HEATMAP -->
<div class="valuation-card">
<div class="val-title">🌡️ Kaufbewertung — Wie teuer ist Bitcoin gerade?</div>
<div class="price-input-row">
<label>Aktueller Marktpreis:</label>
<div class="pi-wrap">
<input type="number" id="marketPrice" value="85000" min="1000" max="10000000" step="1000">
<span class="unit">€</span>
</div>
<button class="refresh-btn" id="refreshBtn" onclick="fetchLivePrice()">↻ Aktualisieren</button>
<span class="vsub" style="flex:1">Wird automatisch geladen</span>
</div>
<div class="heatmap-scale">
<div class="heatmap-needle" id="heatNeedle" style="left:50%"></div>
</div>
<div class="heatmap-labels">
<span>Extrem günstig</span>
<span>Fair</span>
<span>Teuer</span>
<span>Extrem teuer</span>
</div>
<div class="val-result">
<div id="valPct" class="val-pct">–</div>
<div id="valBadge" class="val-badge">–</div>
<div id="valDesc" class="val-desc">–</div>
</div>
</div>
<!-- INPUTS -->
<div class="grid">
<div class="card">
<div class="sec-title">Mein Portfolio</div>
<label>Bitcoin Anzahl (BTC)</label>
<div class="ni-wrap">
<input type="number" id="btcAmount" value="0.5" min="0" max="21" step="0.001">
<span class="unit">BTC</span>
</div>
<input type="range" id="btcSlider" min="0" max="10" step="0.01" value="0.5">
<div class="vsub">Portfolio heute (PL-Preis): <span id="portfolioNow" style="color:var(--accent2)">–</span></div>
</div>
```
<div class="card">
<div class="sec-title">Rentenziel</div>
<label>Monatlicher Bedarf (€)</label>
<div class="ni-wrap">
<input type="number" id="monthlyNeed" value="3000" min="100" max="50000" step="100">
<span class="unit">€/Mo</span>
</div>
<input type="range" id="monthlySlider" min="500" max="20000" step="100" value="3000">
<div class="vsub">Benötigtes Kapital: <span id="targetCapital" style="color:var(--accent2)">–</span></div>
</div>
<div class="card">
<div class="sec-title">Entnahme-Strategie</div>
<label>Jährliche Entnahmerate (%)</label>
<div class="ni-wrap">
<input type="number" id="withdrawRate" value="4" min="1" max="10" step="0.5">
<span class="unit">%</span>
</div>
<input type="range" id="withdrawSlider" min="1" max="10" step="0.5" value="4">
<div class="vsub" style="margin-top:4px">4% = Kapital bleibt langfristig erhalten</div>
</div>
<div class="card" style="background:linear-gradient(135deg,var(--card),rgba(247,147,26,0.05));display:flex;flex-direction:column;justify-content:center;gap:16px;">
<div class="sec-title">Power Law heute</div>
<div>
<div style="font-size:9px;letter-spacing:1.5px;text-transform:uppercase;color:var(--muted);font-family:'Space Mono',monospace;margin-bottom:3px;">Median-Preis (Modell)</div>
<div id="plCardPrice" style="font-size:22px;font-weight:700;font-family:'Space Mono',monospace;color:var(--accent);">–</div>
</div>
<div>
<div style="font-size:9px;letter-spacing:1.5px;text-transform:uppercase;color:var(--muted);font-family:'Space Mono',monospace;margin-bottom:3px;">Ø jährl. Wachstum (10J)</div>
<div id="plCardGrowth" style="font-size:22px;font-weight:700;font-family:'Space Mono',monospace;color:var(--green);">–</div>
</div>
</div>
```
</div>
<!-- MAIN RESULT -->
<div class="result-card fade-in">
<div class="r-label">⚡ Du kannst voraussichtlich in Rente gehen im Jahr</div>
<div id="retYear" class="r-year">–</div>
<div id="retSub" class="r-sub">Basierend auf dem Bitcoin Power Law Modell</div>
<div id="retBadge" class="r-badge">Wird berechnet…</div>
</div>
<!-- STATS -->
<div class="stats-grid">
<div class="sc"><div id="sPriceRet" class="sc-val">–</div><div class="sc-lbl">₿ Preis bei Rente</div></div>
<div class="sc"><div id="sPortRet" class="sc-val">–</div><div class="sc-lbl">Portfolio-Wert</div></div>
<div class="sc"><div id="sMonthRet" class="sc-val">–</div><div class="sc-lbl">Monatl. Entnahme</div></div>
</div>
<!-- TIMELINE -->
<div class="tl-card">
<div class="tl-title">📈 Power Law Entwicklung — Dein Weg zur Rente</div>
<div id="timeline"></div>
</div>
<!-- INFO -->
<div class="info-box">
<strong>Bitcoin Power Law</strong> (Giovanni Santostasi / Harold Christopher Burger)<br><br>
<strong>log₁₀(P) = 5.84 × log₁₀(Tage seit Genesis) + B</strong><br>
B = −17.01 (Median) | −17.668 (Unteres Band) | −16.352 (Oberes Band)<br><br>
Die <strong>Kaufbewertung</strong> zeigt: Marktpreis ÷ PL-Median. Unter 1,0× = günstig / über 2,0× = historisch teuer.<br><br>
⚠️ Keine Anlageberatung. Modellprognosen sind spekulativ. Vergangene Wertentwicklung ist kein Indikator für die Zukunft.
</div>
</div>
<script>
// ── LIVE PRICE – Fallback-Kette: Binance → Kraken → manuell ──────────────
// HINWEIS: Funktioniert nur wenn die Datei lokal im Browser geöffnet wird
// (nicht in eingebetteten Iframes wie Claude.ai – dort blockiert die CSP externe Fetches)
let liveMarketPrice = null;
// Versuche 1: Binance (BTCUSDT / EURUSDT)
async function tryBinance() {
const [btcRes, eurRes] = await Promise.all([
fetch('https://api.binance.com/api/v3/ticker/24hr?symbol=BTCUSDT', { signal: AbortSignal.timeout(6000) }),
fetch('https://api.binance.com/api/v3/ticker/24hr?symbol=EURUSDT', { signal: AbortSignal.timeout(6000) })
]);
if(!btcRes.ok || !eurRes.ok) throw new Error('Binance HTTP');
const btc = await btcRes.json();
const eur = await eurRes.json();
const btcEur = parseFloat(btc.lastPrice) / parseFloat(eur.lastPrice);
return { price: btcEur, change: parseFloat(btc.priceChangePercent), source: 'Binance' };
}
// Versuch 2: Kraken (XBTEUR direkt)
async function tryKraken() {
const res = await fetch('https://api.kraken.com/0/public/Ticker?pair=XBTEUR', { signal: AbortSignal.timeout(6000) });
if(!res.ok) throw new Error('Kraken HTTP');
const data = await res.json();
if(data.error && data.error.length) throw new Error('Kraken: '+data.error[0]);
const ticker = data.result['XXBTZEUR'] || data.result['XBTEUR'];
if(!ticker) throw new Error('Kraken: kein Ticker');
const price = parseFloat(ticker.c[0]);
const open = parseFloat(ticker.o);
const change = ((price - open) / open) * 100;
return { price, change, source: 'Kraken' };
}
// Versuch 3: Coinbase (BTC-EUR)
async function tryCoinbase() {
const res = await fetch('https://api.coinbase.com/v2/prices/BTC-EUR/spot', { signal: AbortSignal.timeout(6000) });
if(!res.ok) throw new Error('Coinbase HTTP');
const data = await res.json();
const price = parseFloat(data.data.amount);
return { price, change: null, source: 'Coinbase' };
}
async function fetchLivePrice() {
const dot = document.getElementById('liveDot');
const priceEl = document.getElementById('livePrice');
const subEl = document.getElementById('liveSub');
const btn = document.getElementById('refreshBtn');
dot.className = 'live-dot loading';
priceEl.textContent = 'Lädt…';
subEl.textContent = '–';
if(btn) btn.disabled = true;
const attempts = [tryBinance, tryKraken, tryCoinbase];
let result = null;
let lastErr = '';
for(const attempt of attempts) {
try {
result = await attempt();
break;
} catch(e) {
lastErr = e.message;
console.warn('Preisfetch fehlgeschlagen:', e.message, '– nächste API…');
}
}
if(result) {
liveMarketPrice = result.price;
document.getElementById('marketPrice').value = Math.round(result.price);
priceEl.textContent = fmt(result.price);
if(result.change !== null) {
const sign = result.change >= 0 ? '+' : '';
subEl.textContent = `${sign}${result.change.toFixed(2)}% (24h) · ${result.source}`;
subEl.style.color = result.change >= 0 ? 'var(--green)' : 'var(--red)';
} else {
subEl.textContent = `via ${result.source}`;
subEl.style.color = 'var(--muted)';
}
dot.className = 'live-dot';
updateValuation();
} else {
dot.className = 'live-dot error';
priceEl.textContent = 'Nicht verfügbar';
subEl.textContent = '⚠ Datei lokal öffnen oder Preis manuell eingeben';
subEl.style.color = 'var(--muted)';
console.warn('Alle APIs fehlgeschlagen. Letzter Fehler:', lastErr);
}
if(btn) btn.disabled = false;
}
// ── POWER LAW ──────────────────────────────────────────────────────────────
const PL_A = 5.84;
const PL_B_MED = -17.01;
const PL_B_FLOOR = -17.668;
const PL_B_CEIL = -16.352;
const GENESIS = new Date('2009-01-03T00:00:00Z');
function daysSince(date){ return Math.floor((date - GENESIS)/86400000); }
function plPrice(date, B=PL_B_MED){ return Math.pow(10, PL_A * Math.log10(daysSince(date)) + B); }
function plYear(y, B=PL_B_MED){ return plPrice(new Date(y,6,1), B); }
// ── FORMATTERS ─────────────────────────────────────────────────────────────
function fmt(v){
if(v>=1e9) return (v/1e9).toFixed(2)+' Mrd €';
if(v>=1e6) return (v/1e6).toFixed(2)+' Mio €';
if(v>=1e3) return Math.round(v).toLocaleString('de-DE')+' €';
return v.toFixed(0)+' €';
}
function fmtS(v){
if(v>=1e9) return (v/1e9).toFixed(1)+'Mrd';
if(v>=1e6) return (v/1e6).toFixed(1)+'Mio';
return Math.round(v).toLocaleString('de-DE');
}
function fmtK(v){
if(v>=1e6) return (v/1e6).toFixed(0)+'M €';
if(v>=1e3) return (v/1e3).toFixed(0)+'K €';
return Math.round(v)+' €';
}
// ── INIT CONSTANTS ─────────────────────────────────────────────────────────
const TODAY = new Date();
const CY = TODAY.getFullYear();
const PL_TODAY = plPrice(TODAY);
const PL_F_TODAY = plPrice(TODAY, PL_B_FLOOR);
const PL_C_TODAY = plPrice(TODAY, PL_B_CEIL);
const DAYS_TODAY = daysSince(TODAY);
const growth10 = (Math.pow(PL_TODAY / plYear(CY-10), 0.1) - 1) * 100;
// Fill banner
document.getElementById('plPriceToday').textContent = fmt(PL_TODAY);
document.getElementById('plDateToday').textContent = 'Stand: '+TODAY.toLocaleDateString('de-DE',{day:'2-digit',month:'long',year:'numeric'});
document.getElementById('plFloor').textContent = fmtK(PL_F_TODAY);
document.getElementById('plCeil').textContent = fmtK(PL_C_TODAY);
document.getElementById('plDays').textContent = DAYS_TODAY.toLocaleString('de-DE');
document.getElementById('plGrowth').textContent = '+'+growth10.toFixed(1)+'% p.a.';
document.getElementById('plCardPrice').textContent = fmt(PL_TODAY);
document.getElementById('plCardGrowth').textContent = '+'+growth10.toFixed(1)+'% p.a.';
// ── VALUATION ──────────────────────────────────────────────────────────────
function getVal(ratio){
if(ratio<0.5) return{l:'Extrem günstig',c:'#06d6a0',d:'Historisch seltene Kaufgelegenheit – weit unter dem Power Law Median.'};
if(ratio<0.8) return{l:'Günstig', c:'#3ad67a',d:'Unter dem Modell-Median. Historisch gutes Einstiegsniveau.'};
if(ratio<1.0) return{l:'Leicht günstig',c:'#a8d630',d:'Etwas unter dem Median. Solides Kaufniveau laut Power Law.'};
if(ratio<1.2) return{l:'Fair bewertet', c:'#ffd166',d:'Nahe am Power Law Median. Faire Bewertung nach dem Modell.'};
if(ratio<1.8) return{l:'Erhöht', c:'#f7931a',d:'Über dem Median. Kein Schnäppchen, aber noch im normalen Bereich.'};
if(ratio<2.5) return{l:'Teuer', c:'#ef6c47',d:'Deutlich über dem Median – historisch erhöhtes Korrekturrisiko.'};
if(ratio<4.0) return{l:'Sehr teuer', c:'#ef476f',d:'Weit über dem Power Law. Entspricht historischen Blasen-Niveaus.'};
return{l:'Extrem teuer', c:'#c0134e',d:'Extremes Niveau – historisch kurz vor starken Korrekturen.'};
}
function updateValuation(){
const mp = parseFloat(document.getElementById('marketPrice').value)||PL_TODAY;
const ratio = mp / PL_TODAY;
const v = getVal(ratio);
// Needle: log-scale, ratio 0.25…5 → 2%…98%
const lMin=Math.log(0.25), lMax=Math.log(5);
const lR=Math.log(Math.max(0.25,Math.min(5,ratio)));
const pct = 2 + ((lR-lMin)/(lMax-lMin))*96;
document.getElementById('heatNeedle').style.left = pct.toFixed(1)+'%';
const sign = ratio>=1?'+':'';
const diff = ((ratio-1)*100).toFixed(0);
const pctEl = document.getElementById('valPct');
pctEl.textContent = sign+diff+'%';
pctEl.style.color = v.c;
const badge = document.getElementById('valBadge');
badge.textContent = v.l;
badge.style.background = v.c+'22';
badge.style.border = '1px solid '+v.c+'55';
badge.style.color = v.c;
document.getElementById('valDesc').textContent = v.d;
}
// ── RETIREMENT CALC ────────────────────────────────────────────────────────
function calculate(){
const btc = parseFloat(document.getElementById('btcAmount').value)||0;
const mn = parseFloat(document.getElementById('monthlyNeed').value)||3000;
const wr = parseFloat(document.getElementById('withdrawRate').value)/100||0.04;
const yn = mn*12;
const target = yn/wr;
document.getElementById('portfolioNow').textContent = fmt(btc*PL_TODAY);
document.getElementById('targetCapital').textContent = fmt(target);
let ry=null, rp=null, rv=null;
for(let y=CY;y<=2070;y++){
const p=plYear(y), pv=btc*p;
if(pv>=target){ry=y;rp=p;rv=pv;break;}
}
const yEl=document.getElementById('retYear');
const sEl=document.getElementById('retSub');
const bEl=document.getElementById('retBadge');
if(ry===null){
yEl.innerHTML='<span style="color:var(--muted);font-size:40px">Nach 2070</span>';
sEl.textContent='Mehr BTC oder niedrigeres Monatsziel nötig';
bEl.textContent='> 44 Jahre'; bEl.style.color='var(--muted)';
} else if(ry<=CY){
yEl.innerHTML='<span class="already">Heute! 🎉</span>';
sEl.textContent='Du könntest schon jetzt in Rente gehen!';
bEl.textContent='✅ Bereits erreicht!'; bEl.style.color='var(--green)';
} else {
yEl.textContent=ry;
const yl=ry-CY;
sEl.textContent='₿ Preis laut Power Law bei '+fmt(rp);
bEl.textContent=yl+' Jahr'+(yl===1?'':'e')+' bis zur Rente';
bEl.style.color='var(--green)';
}
const fp=rp||PL_TODAY, fv=rv||(btc*PL_TODAY);
document.getElementById('sPriceRet').textContent = fmtS(fp)+' €';
document.getElementById('sPortRet').textContent = fmtS(fv)+' €';
document.getElementById('sMonthRet').textContent = fmt((fv*wr)/12);
buildTimeline(btc, target, ry, wr);
}
// ── TIMELINE ───────────────────────────────────────────────────────────────
function buildTimeline(btc, target, ry, wr){
const c=document.getElementById('timeline');
c.innerHTML='';
const end=Math.min(ry?ry+2:CY+20, 2060);
let yrs=[];
const span=end-CY, step=Math.max(1,Math.ceil(span/10));
for(let y=CY;y<=end;y+=step) yrs.push(y);
if(ry&&!yrs.includes(ry)) yrs.push(ry);
yrs=[...new Set(yrs)].sort((a,b)=>a-b);
const maxPV=btc*plYear(end)*1.2||1;
yrs.forEach(y=>{
const p=plYear(y), pv=btc*p;
const pct=Math.min((pv/maxPV)*100,100);
const isRet=ry&&y===ry, reached=pv>=target;
const row=document.createElement('div');
row.className='tl-row';
row.innerHTML=`
<div class="tl-yr ${isRet?'hl':''}">${y}</div>
<div class="tl-bw"><div class="tl-b" style="width:0%;background:${isRet?'linear-gradient(90deg,var(--accent),var(--green))':'linear-gradient(90deg,var(--accent),var(--accent2))'}"></div></div>
<div class="tl-amt">${fmt(pv)}</div>
<div class="tl-mark">${isRet?'🎯':reached?'✅':''}</div>`;
c.appendChild(row);
setTimeout(()=>{row.querySelector('.tl-b').style.width=pct+'%';},50);
});
}
// ── SYNC ───────────────────────────────────────────────────────────────────
function sync(iId,sId,cb){
const i=document.getElementById(iId), s=document.getElementById(sId);
i.addEventListener('input',()=>{s.value=i.value;cb();});
s.addEventListener('input',()=>{i.value=s.value;cb();});
}
sync('btcAmount','btcSlider',calculate);
sync('monthlyNeed','monthlySlider',calculate);
sync('withdrawRate','withdrawSlider',calculate);
document.getElementById('marketPrice').addEventListener('input',updateValuation);
updateValuation();
calculate();
// Live-Preis beim Start laden
fetchLivePrice();
</script>
</body>
</html>