diff --git a/packages/dashboard/index.html b/packages/dashboard/index.html
index 5cf9187..8287f5c 100644
--- a/packages/dashboard/index.html
+++ b/packages/dashboard/index.html
@@ -8129,12 +8129,51 @@ async function loadSwitchCompat() {
}
/* ── C: Supply Squeeze Detector ────────────────────────────────────────── */
+
+/* ── Verfügbarkeit pro Speed-Tier (mit Warum-Begründung) ────────────────── */
+async function renderAvailabilityPanel() {
+ var host = el('proc-squeeze-summary');
+ if (!host) return;
+ try {
+ var d = await api('/api/procurement/availability');
+ var tiers = (d && d.tiers) || [];
+ if (!tiers.length) return;
+ var meta = {
+ scarce: { c:'#ef4444', bg:'rgba(239,68,68,0.10)', lbl:'KNAPP' },
+ constrained: { c:'#f97316', bg:'rgba(249,115,22,0.10)', lbl:'eng' },
+ moderate: { c:'#f59e0b', bg:'rgba(245,158,11,0.10)', lbl:'moderat' },
+ abundant: { c:'#22c55e', bg:'rgba(34,197,94,0.10)', lbl:'breit' }
+ };
+ function spd(g){ return g>=1000 ? (g/1000)+'T' : g+'G'; }
+ var html = '
'
+ + '
📦 Verfügbarkeit nach Geschwindigkeit — aus Anbieter-Diversität, Stock-Coverage & Preis-Momentum (echte Daten)
'
+ + '
';
+ tiers.forEach(function(t) {
+ var m = meta[t.availability] || meta.moderate;
+ var pt = t.price_trend, pd = t.price_delta_pct;
+ var priceTag = pt==='rising' ? '
▲ +'+pd+'%' : pt==='falling' ? '
▼ '+pd+'%' : '
→ stabil';
+ html += '
'
+ + '
'
+ + ''+spd(t.speed_gbps)+''
+ + ''+m.lbl+''
+ + '
'
+ + '
'+t.suppliers+' Anbieter · '+(t.in_stock_pct!=null?t.in_stock_pct+'% stock':'k.A.')+' · '+priceTag+'
'
+ + '
'+esc((t.why||'').charAt(0).toUpperCase()+(t.why||'').slice(1))+'
'
+ + '
';
+ });
+ html += '
';
+ // prepend to summary (before the squeeze severity badges, which get set after)
+ host.insertAdjacentHTML('afterbegin', html);
+ } catch(e) { /* silent */ }
+}
+
async function loadSupplySqueeze() {
var token = (window.loadToken ? window.loadToken() : '') || '';
var summEl = el('proc-squeeze-summary');
var listEl = el('proc-squeeze-list');
if (listEl) listEl.innerHTML = 'Analysiere Preis- & Nachfragesignale…
';
try {
+ renderAvailabilityPanel();
var r = await fetch('/api/procurement/supply-squeeze', { headers: { 'Authorization': 'Bearer ' + token } });
var d = await r.json();
if (!d.success) throw new Error(d.error);