aether-framework/fuel/index.html
Jerrit Fritzsche e4b82dd685 Aether-Framework: Fachmodule, Admin-Panel und Docker-Datenbank
- aether-medical: Stationen/Belegung, Intensivstation, Blutbank, Fuhrpark,
  Nachrichten/Aushänge, Berichte und Textbausteine (server + NUI)
- aether-admin: vollständiges Server-Admin-Panel als echtes FiveM-Resource,
  an aether-core gekoppelt (Rechte über aether_users.admin_group), mit
  WebRTC-Live-Kameras (Bildschirmfreigabe) und separatem Media-Server (SFU)
- Prototypen admin/ vervollständigt
- SQL-Schema aether_admin.sql
- Docker-Compose mit MariaDB und idempotenter Auto-Migration (schema_migrations)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 18:55:32 +02:00

425 lines
22 KiB
HTML
Raw 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">
<meta name="color-scheme" content="dark">
<title>Aether — Tankstelle</title>
<style>
:root{
--bg:#0a0806;--fg:#f3efe4;--muted:#9c9585; /* Grundfarben */
--surface:rgba(26,22,16,0.72);--surface2:rgba(38,32,22,0.9); /* Panel-Flächen */
--slot:#120f0a;--border:rgba(255,255,255,0.09); /* Eingaben/Rahmen */
--accent:#f5a742;--accent2:#f0d24a;--ink:#241603;/* Tank-Orange als Leitfarbe */
--accent-soft:rgba(245,167,66,0.15);
--violet:#7c5cf6; /* Aether-Akzent (sekundär) */
--green:#34d39a;--red:#f0506e; /* Erfolg / Fehler */
}
*{box-sizing:border-box;} html,body{margin:0;height:100%;}
body{min-height:100%;background:radial-gradient(1000px 620px at 50% -8%,rgba(245,167,66,0.1),transparent 55%),var(--bg);
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;color:var(--fg);
display:flex;align-items:center;justify-content:center;padding:22px 14px;}
.app{position:relative;width:min(940px,98vw);border-radius:22px;overflow:hidden;
background:linear-gradient(160deg,rgba(24,20,14,0.96),rgba(8,7,5,0.98));border:1px solid rgba(245,167,66,0.22);
box-shadow:0 50px 100px -30px rgba(0,0,0,0.85);}
/* Kopfzeile */
.head{display:flex;align-items:center;gap:12px;padding:16px 22px;border-bottom:1px solid var(--border);}
.head .bl{width:40px;height:40px;border-radius:11px;display:grid;place-items:center;font-size:21px;
background:linear-gradient(150deg,var(--accent),#c47f1e);color:var(--ink);}
.head .bn{font-size:17px;font-weight:800;letter-spacing:-0.02em;}
.head .bn span{color:var(--accent);} .head .bn small{display:block;font-size:10.5px;color:var(--muted);font-weight:500;letter-spacing:0.04em;}
.head .money{margin-left:auto;display:flex;gap:8px;}
.head .chip{background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:7px 13px;text-align:right;}
.head .chip .l{font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:0.06em;font-weight:800;}
.head .chip .v{font-size:15px;font-weight:800;font-variant-numeric:tabular-nums;}
/* Zweispaltiger Körper: Zapfsäulen-Display + Steuerung */
.body{display:grid;grid-template-columns:1fr 1fr;gap:0;}
.display{padding:24px;border-right:1px solid var(--border);}
.controls{padding:24px;display:flex;flex-direction:column;gap:16px;}
/* Digitale Zähler (Liter + Preis) */
.counters{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:18px;}
.counter{background:#0c0a06;border:1px solid rgba(245,167,66,0.3);border-radius:14px;padding:14px 16px;
box-shadow:inset 0 0 24px rgba(0,0,0,0.6);}
.counter .cl{font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.08em;font-weight:800;}
.counter .cv{font-size:34px;font-weight:800;font-variant-numeric:tabular-nums;letter-spacing:0.02em;
color:var(--accent2);text-shadow:0 0 14px rgba(240,210,74,0.35);line-height:1.15;}
.counter .cu{font-size:13px;color:var(--muted);font-weight:700;}
.counter.preis .cv{color:var(--accent);text-shadow:0 0 14px rgba(245,167,66,0.4);}
.ppl{text-align:center;font-size:11.5px;color:var(--muted);margin:-6px 0 16px;font-weight:600;}
/* Tankanzeige (Balken E → F) + Pumpe */
.tankwrap{display:flex;align-items:center;gap:16px;}
.nozzle{font-size:40px;transition:.15s;filter:drop-shadow(0 4px 8px rgba(0,0,0,0.5));}
.nozzle.on{animation:wackel .5s ease-in-out infinite;color:var(--accent);}
@keyframes wackel{50%{transform:translateY(-3px) rotate(-4deg);}}
.tank{flex:1;}
.tank .tl{display:flex;justify-content:space-between;font-size:11px;color:var(--muted);font-weight:800;margin-bottom:5px;}
.tank .bar{height:26px;border-radius:9px;background:var(--slot);border:1px solid var(--border);overflow:hidden;position:relative;}
.tank .bar .fill{height:100%;width:0;border-radius:8px 0 0 8px;transition:width .12s linear;
background:linear-gradient(90deg,var(--accent),var(--accent2));}
.tank .bar .pct{position:absolute;inset:0;display:grid;place-items:center;font-size:12.5px;font-weight:800;
color:#fff;text-shadow:0 1px 3px rgba(0,0,0,0.7);font-variant-numeric:tabular-nums;}
.tank .bar .fill.warn{background:linear-gradient(90deg,var(--red),#f0806e);}
/* Steuerungs-Bausteine */
.card{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:14px;}
.lh{font-size:10px;text-transform:uppercase;letter-spacing:0.07em;color:var(--muted);font-weight:800;margin:0 0 10px;}
.veh{display:flex;align-items:center;gap:12px;}
.veh .vi{font-size:26px;}
.veh .vn{font-weight:800;font-size:15px;} .veh .vs{font-size:12px;color:var(--muted);}
.veh .plate{margin-left:auto;background:#f3efe4;color:#1a1a1a;font-weight:800;border-radius:6px;padding:5px 10px;
font-size:13px;letter-spacing:0.06em;border:2px solid #c9c4b4;}
.seg{display:flex;gap:6px;} .seg button{flex:1;border:1px solid var(--border);background:var(--slot);color:var(--fg);
border-radius:10px;padding:10px 8px;font-family:inherit;font-weight:700;font-size:12.5px;cursor:pointer;transition:.12s;}
.seg button:hover{border-color:var(--accent);} .seg button.active{background:var(--accent-soft);border-color:var(--accent);color:var(--accent);}
.seg.sorte button{flex-direction:column;gap:3px;display:flex;align-items:center;}
.seg.sorte button .p{font-size:10.5px;color:var(--muted);font-weight:600;} .seg.sorte button.active .p{color:var(--accent);}
.inrow{display:flex;align-items:center;gap:10px;margin-top:10px;}
.input{flex:1;background:var(--slot);border:1px solid var(--border);border-radius:10px;padding:11px 13px;color:var(--fg);
font-size:16px;font-family:inherit;font-weight:700;outline:none;font-variant-numeric:tabular-nums;}
.input:focus{border-color:var(--accent);} .inrow .unit{font-size:14px;color:var(--muted);font-weight:800;}
.zahlung{display:flex;gap:8px;} .zahlung button{flex:1;display:flex;align-items:center;justify-content:space-between;
border:1px solid var(--border);background:var(--slot);color:var(--fg);border-radius:10px;padding:10px 12px;
font-family:inherit;font-weight:700;font-size:13px;cursor:pointer;transition:.12s;}
.zahlung button:hover{border-color:var(--accent);} .zahlung button.active{background:var(--accent-soft);border-color:var(--accent);}
.zahlung button .zb{font-size:11px;color:var(--muted);font-weight:700;}
.btn{border:none;border-radius:12px;padding:15px;font-size:15px;font-weight:800;cursor:pointer;font-family:inherit;transition:.12s;width:100%;}
.btn.go{background:linear-gradient(150deg,var(--accent),#d88a1e);color:var(--ink);} .btn.go:hover{filter:brightness(1.06);}
.btn.stop{background:var(--red);color:#2a0910;} .btn:active{transform:scale(0.99);}
.btn:disabled{opacity:.45;cursor:not-allowed;}
.hint{font-size:12px;color:var(--muted);text-align:center;min-height:16px;}
.hint.err{color:var(--red);} .hint.ok{color:var(--green);}
/* Beleg-Overlay */
.overlay{position:absolute;inset:0;background:rgba(6,5,3,0.72);backdrop-filter:blur(4px);display:none;
align-items:center;justify-content:center;padding:20px;z-index:10;}
.overlay.on{display:flex;}
.beleg{width:320px;background:#f6f2e8;color:#2a2418;border-radius:12px;padding:22px;font-family:"SF Mono",ui-monospace,Menlo,monospace;
box-shadow:0 30px 60px -20px rgba(0,0,0,0.8);animation:auf .3s ease;}
@keyframes auf{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}
.beleg h3{margin:0 0 3px;text-align:center;font-size:15px;letter-spacing:0.1em;}
.beleg .sub{text-align:center;font-size:11px;color:#8a8270;margin-bottom:14px;}
.beleg .bz{display:flex;justify-content:space-between;font-size:12.5px;padding:4px 0;border-bottom:1px dashed #cfc7b2;}
.beleg .bz.sum{border-bottom:none;border-top:2px solid #2a2418;margin-top:6px;padding-top:8px;font-size:15px;font-weight:800;}
.beleg .dank{text-align:center;font-size:11px;color:#8a8270;margin-top:14px;}
.beleg .b-btn{margin-top:16px;width:100%;border:none;background:#2a2418;color:#f6f2e8;border-radius:9px;padding:12px;
font-family:inherit;font-weight:700;cursor:pointer;font-size:13px;}
/* Demo-Leiste */
.demo{display:flex;align-items:center;gap:8px;padding:11px 22px;border-top:1px solid var(--border);}
.demo span{font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.06em;font-weight:800;}
.demo button{border:1px solid var(--border);background:rgba(255,255,255,0.04);color:var(--fg);border-radius:8px;
padding:6px 11px;font-family:inherit;font-size:11.5px;font-weight:700;cursor:pointer;}
.demo button:hover{border-color:var(--violet);color:#fff;}
@media (max-width:820px){.body{grid-template-columns:1fr;} .display{border-right:none;border-bottom:1px solid var(--border);}}
</style>
</head>
<body>
<div class="app">
<!-- Kopfzeile mit Geld-Anzeige -->
<div class="head">
<span class="bl">⛽</span>
<div class="bn">Aether <span>Fuel</span><small>Tankstelle · Los Santos</small></div>
<div class="money">
<div class="chip"><div class="l">Bargeld</div><div class="v" id="mBar">–</div></div>
<div class="chip"><div class="l">Konto</div><div class="v" id="mKonto">–</div></div>
</div>
</div>
<div class="body">
<!-- Zapfsäulen-Display -->
<div class="display">
<div class="counters">
<div class="counter"><div class="cl">Menge</div><div class="cv" id="cLiter">0,00</div><div class="cu">Liter</div></div>
<div class="counter preis"><div class="cl">Zu zahlen</div><div class="cv" id="cPreis">0,00</div><div class="cu">Euro</div></div>
</div>
<div class="ppl" id="ppl">Grundpreis –</div>
<div class="tankwrap">
<div class="nozzle" id="nozzle">⛽</div>
<div class="tank">
<div class="tl"><span>E — leer</span><span>voll — F</span></div>
<div class="bar"><div class="fill" id="tankFill"></div><div class="pct" id="tankPct">0 %</div></div>
</div>
</div>
</div>
<!-- Steuerung -->
<div class="controls">
<div class="card">
<div class="lh">Fahrzeug</div>
<div class="veh">
<span class="vi">🚗</span>
<div><div class="vn" id="vName">–</div><div class="vs" id="vTank">–</div></div>
<span class="plate" id="vPlate">–</span>
</div>
</div>
<div>
<div class="lh">Spritsorte</div>
<div class="seg sorte" id="sorte"></div>
</div>
<div>
<div class="lh">Menge wählen</div>
<div class="seg" id="modus">
<button data-m="voll" class="active">Volltanken</button>
<button data-m="liter">Liter</button>
<button data-m="betrag">Betrag €</button>
</div>
<div class="inrow" id="inrow" style="display:none">
<input class="input" id="wert" type="number" min="1" placeholder="0">
<span class="unit" id="unit">L</span>
</div>
</div>
<div>
<div class="lh">Zahlung</div>
<div class="zahlung" id="zahlung">
<button data-z="bargeld" class="active">💵 Bargeld <span class="zb" id="zBar"></span></button>
<button data-z="konto">💳 Karte <span class="zb" id="zKonto"></span></button>
</div>
</div>
<button class="btn go" id="actionBtn">⛽ Tanken</button>
<div class="hint" id="hint">Wähle Sorte und Menge, dann tanken.</div>
</div>
</div>
<!-- Demo-Leiste -->
<div class="demo">
<span>Demo</span>
<button id="dLeer">🚗 Tank leeren (Probefahrt)</button>
<button id="dReich">💰 Geld auffüllen</button>
</div>
<!-- Beleg-Overlay -->
<div class="overlay" id="overlay"><div class="beleg" id="beleg"></div></div>
</div>
<script>
"use strict";
/* Kurzer Zugriffshelfer auf Elemente per ID */
const $=id=>document.getElementById(id);
/* Betrag als Euro (2 Nachkommastellen) formatieren */
const euro=n=>n.toLocaleString("de-DE",{minimumFractionDigits:2,maximumFractionDigits:2})+" €";
/* Litermenge formatieren */
const liter=n=>n.toLocaleString("de-DE",{minimumFractionDigits:2,maximumFractionDigits:2});
/* Ganze Euro (für Geld-Chips) */
const euroKurz=n=>Math.round(n).toLocaleString("de-DE")+" €";
/* =====================================================================
DATEN (im echten Betrieb per NUI vom Client gesetzt) — vom UI getrennt.
===================================================================== */
const spieler={bargeld:1200, konto:8600}; // Geld des Spielers
const fahrzeug={modell:"Übermacht Sentinel", kennzeichen:"LS-4821",
tankGroesse:65, fuellstand:24}; // Tank in Litern (aktuell / max)
const spritSorten=[ // Verfügbare Sorten mit Literpreis
{id:"e10", name:"Benzin E10", preis:1.72},
{id:"super", name:"Super Plus", preis:1.94},
{id:"diesel", name:"Diesel", preis:1.66},
];
const FLUSS_L_PRO_S=9; // Durchfluss der Zapfsäule (Liter pro Sekunde)
const TICK_MS=45; // Aktualisierungsintervall der Zapf-Animation
/* =====================================================================
ZUSTAND
===================================================================== */
let sorteIndex=0; // gewählte Spritsorte
let modus="voll"; // Tankmodus: voll | liter | betrag
let zahlung="bargeld"; // Zahlungsart: bargeld | konto
let phase="idle"; // idle | tanken | fertig
let pumpTimer=null; // Timer der laufenden Zapf-Animation
let aktLiter=0; // bereits gezapfte Liter dieser Sitzung
let zielLiter=0; // Zielmenge der aktuellen Zapfung
let geklemmtDurchGeld=false; // wurde die Zielmenge durchs Guthaben begrenzt?
/* Aktuell gewählte Sorte / verfügbares Guthaben je Zahlungsart */
const sorte=()=>spritSorten[sorteIndex];
const guthaben=()=>zahlung==="bargeld"?spieler.bargeld:spieler.konto;
/* =====================================================================
RENDERING
===================================================================== */
/* Geld-Chips oben und in der Zahlungsauswahl aktualisieren */
function renderGeld(){
$("mBar").textContent=euroKurz(spieler.bargeld);
$("mKonto").textContent=euroKurz(spieler.konto);
$("zBar").textContent=euroKurz(spieler.bargeld);
$("zKonto").textContent=euroKurz(spieler.konto);
}
/* Fahrzeug-Karte füllen */
function renderFahrzeug(){
$("vName").textContent=fahrzeug.modell;
$("vTank").textContent="Tank "+fahrzeug.tankGroesse+" L";
$("vPlate").textContent=fahrzeug.kennzeichen;
}
/* Spritsorten-Buttons aufbauen */
function renderSorten(){
$("sorte").innerHTML=spritSorten.map((s,i)=>
`<button data-s="${i}" class="${i===sorteIndex?'active':''}">${s.name}<span class="p">${euro(s.preis)}/L</span></button>`).join('');
$("sorte").querySelectorAll("[data-s]").forEach(b=>b.addEventListener("click",()=>{if(phase==="tanken")return;sorteIndex=+b.dataset.s;renderSorten();renderPreisInfo();aktualisiereHint();}));
}
/* Grundpreis-Zeile unter den Zählern */
function renderPreisInfo(){$("ppl").textContent="Grundpreis "+euro(sorte().preis)+" / Liter";}
/* Digitale Zähler + Tankbalken aktualisieren */
function renderDisplay(){
$("cLiter").textContent=liter(aktLiter);
$("cPreis").textContent=liter(aktLiter*sorte().preis);
const gesamt=Math.min(fahrzeug.tankGroesse,fahrzeug.fuellstand+aktLiter);
const pct=gesamt/fahrzeug.tankGroesse*100;
$("tankFill").style.width=pct+"%";
$("tankFill").classList.toggle("warn",pct<15);
$("tankPct").textContent=Math.round(pct)+" %";
}
/* =====================================================================
ZIELMENGE BERECHNEN
===================================================================== */
/* Ermittelt die Zielmenge in Litern (begrenzt durch Tank und Guthaben) */
function berechneZiel(){
const platz=fahrzeug.tankGroesse-fahrzeug.fuellstand; // freier Platz im Tank
const proLiter=sorte().preis;
let ziel;
if(modus==="voll") ziel=platz;
else if(modus==="liter") ziel=Math.min(platz,parseFloat($("wert").value)||0);
else /* betrag */ ziel=Math.min(platz,(parseFloat($("wert").value)||0)/proLiter);
// Durch verfügbares Guthaben begrenzen
const maxDurchGeld=guthaben()/proLiter;
geklemmtDurchGeld=ziel>maxDurchGeld+1e-6;
ziel=Math.min(ziel,maxDurchGeld);
return Math.max(0,ziel);
}
/* Hinweistext passend zur aktuellen Auswahl setzen */
function aktualisiereHint(){
if(phase==="tanken")return;
const platz=fahrzeug.tankGroesse-fahrzeug.fuellstand;
if(platz<0.1){setHint("Tank ist bereits voll.","err");$("actionBtn").disabled=true;return;}
const ziel=berechneZiel();
$("actionBtn").disabled=ziel<0.1;
if(ziel<0.1){setHint(modus==="voll"?"Tank ist voll.":"Bitte gültige Menge / genug Guthaben.","err");return;}
const kosten=ziel*sorte().preis;
setHint((geklemmtDurchGeld?"Durch Guthaben begrenzt: ":"")+
liter(ziel)+" L ≈ "+euro(kosten)+" ("+(zahlung==="bargeld"?"Bargeld":"Karte")+")",geklemmtDurchGeld?"":"");
}
/* Hinweiszeile setzen */
function setHint(text,typ){$("hint").textContent=text;$("hint").className="hint"+(typ?" "+typ:"");}
/* =====================================================================
ZAPFVORGANG
===================================================================== */
/* Startet die Zapfung bis zur berechneten Zielmenge */
function starteTanken(){
zielLiter=berechneZiel();
if(zielLiter<0.1){aktualisiereHint();return;}
phase="tanken"; aktLiter=0;
$("nozzle").classList.add("on");
$("actionBtn").className="btn stop"; $("actionBtn").textContent="■ Stopp";
sperreEingaben(true);
const schritt=FLUSS_L_PRO_S*TICK_MS/1000; // Liter pro Tick
pumpTimer=setInterval(()=>{
aktLiter=Math.min(zielLiter,aktLiter+schritt);
renderDisplay();
setHint("Zapfe … "+liter(aktLiter)+" L · "+euro(aktLiter*sorte().preis),"");
if(aktLiter>=zielLiter-1e-6)stoppeTanken();
},TICK_MS);
}
/* Beendet die Zapfung (regulär oder per Stopp), bucht ab und zeigt den Beleg */
function stoppeTanken(){
clearInterval(pumpTimer); pumpTimer=null;
$("nozzle").classList.remove("on");
const kosten=aktLiter*sorte().preis;
// Betrag von der gewählten Zahlungsart abbuchen
if(zahlung==="bargeld")spieler.bargeld-=kosten; else spieler.konto-=kosten;
fahrzeug.fuellstand=Math.min(fahrzeug.tankGroesse,fahrzeug.fuellstand+aktLiter);
phase="fertig";
renderGeld(); renderDisplay();
zeigeBeleg(aktLiter,kosten);
}
/* Ein-/Ausgaben während der Zapfung sperren */
function sperreEingaben(sperr){
$("sorte").querySelectorAll("button").forEach(b=>b.disabled=sperr);
$("modus").querySelectorAll("button").forEach(b=>b.disabled=sperr);
$("zahlung").querySelectorAll("button").forEach(b=>b.disabled=sperr);
$("wert").disabled=sperr;
}
/* =====================================================================
BELEG
===================================================================== */
/* Zeigt den Kassenbeleg nach der Zapfung */
function zeigeBeleg(l,kosten){
const kontoRest=zahlung==="bargeld"?spieler.bargeld:spieler.konto;
$("beleg").innerHTML=`
<h3>AETHER FUEL</h3><div class="sub">Tankstelle Los Santos · ${new Date().toLocaleTimeString("de-DE",{hour:"2-digit",minute:"2-digit"})}</div>
<div class="bz"><span>Fahrzeug</span><span>${fahrzeug.kennzeichen}</span></div>
<div class="bz"><span>Sorte</span><span>${sorte().name}</span></div>
<div class="bz"><span>Menge</span><span>${liter(l)} L</span></div>
<div class="bz"><span>Literpreis</span><span>${euro(sorte().preis)}</span></div>
<div class="bz"><span>Zahlung</span><span>${zahlung==="bargeld"?"Bargeld":"Karte"}</span></div>
<div class="bz"><span>Neuer Füllstand</span><span>${Math.round(fahrzeug.fuellstand/fahrzeug.tankGroesse*100)} %</span></div>
<div class="bz sum"><span>Gesamt</span><span>${euro(kosten)}</span></div>
<div class="bz" style="border:none;padding-top:6px"><span>Restguthaben</span><span>${euroKurz(kontoRest)}</span></div>
<div class="dank">Gute Fahrt — danke fürs Tanken!</div>
<button class="b-btn" id="belegOk">Fertig</button>`;
$("overlay").classList.add("on");
$("belegOk").addEventListener("click",belegSchliessen);
}
/* Schließt den Beleg und stellt den Ausgangszustand wieder her */
function belegSchliessen(){
$("overlay").classList.remove("on");
phase="idle"; aktLiter=0;
$("actionBtn").className="btn go"; $("actionBtn").textContent="⛽ Tanken";
sperreEingaben(false);
renderDisplay(); aktualisiereHint();
}
/* =====================================================================
STEUERUNG (Buttons / Eingaben)
===================================================================== */
/* Tankmodus umschalten (blendet das Eingabefeld passend ein) */
$("modus").querySelectorAll("[data-m]").forEach(b=>b.addEventListener("click",()=>{
if(phase==="tanken")return;
modus=b.dataset.m;
$("modus").querySelectorAll("button").forEach(x=>x.classList.toggle("active",x===b));
$("inrow").style.display=modus==="voll"?"none":"flex";
$("unit").textContent=modus==="liter"?"L":"€";
$("wert").value="";
aktualisiereHint();
}));
$("wert").addEventListener("input",aktualisiereHint);
/* Zahlungsart umschalten */
$("zahlung").querySelectorAll("[data-z]").forEach(b=>b.addEventListener("click",()=>{
if(phase==="tanken")return;
zahlung=b.dataset.z;
$("zahlung").querySelectorAll("button").forEach(x=>x.classList.toggle("active",x===b));
aktualisiereHint();
}));
/* Haupt-Button: startet die Zapfung oder stoppt sie */
$("actionBtn").addEventListener("click",()=>{ if(phase==="tanken")stoppeTanken(); else starteTanken(); });
/* Demo-Buttons */
$("dLeer").addEventListener("click",()=>{ if(phase==="tanken")return;
fahrzeug.fuellstand=Math.round(Math.random()*18+4); renderFahrzeug(); renderDisplay(); aktualisiereHint(); });
$("dReich").addEventListener("click",()=>{ spieler.bargeld+=2000; spieler.konto+=5000; renderGeld(); aktualisiereHint(); });
/* =====================================================================
START
===================================================================== */
renderGeld(); renderFahrzeug(); renderSorten(); renderPreisInfo();
renderDisplay(); aktualisiereHint();
</script>
</body>
</html>