Files

720 lines
47 KiB
HTML
Raw Permalink 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, viewport-fit=cover">
<meta name="theme-color" content="#0b3a54">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="AIDA Drinks">
<title>AIDA Getränkepaket Break-even-Rechner</title>
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%230b3a54'/%3E%3Cpath d='M15 19h34l-14 16v9h6v4H23v-4h6v-9z' fill='%23e2673d'/%3E%3Ccircle cx='42' cy='15' r='3' fill='%23eaf1f5'/%3E%3Cpath d='M12 50c5 0 5-3 10-3s5 3 10 3 5-3 10-3 5 3 10 3' fill='none' stroke='%2312556f' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E">
<link rel="apple-touch-icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%230b3a54'/%3E%3Cpath d='M15 19h34l-14 16v9h6v4H23v-4h6v-9z' fill='%23e2673d'/%3E%3Ccircle cx='42' cy='15' r='3' fill='%23eaf1f5'/%3E%3C/svg%3E">
<style>
:root{
--sea:#0b3a54; --sea2:#12556f; --sky:#eaf1f5; --coral:#e2673d; --coral2:#c9542e;
--red:#c2352a; --green:#1f9257; --amber:#c0801e; --ink:#151b1f; --mut:#5f7480; --line:#dbe5eb;
--card:#ffffff; --bg:#edf2f5;
--sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,"Helvetica Neue",sans-serif;
--mono:ui-monospace,"SF Mono",Menlo,Consolas,"Roboto Mono",monospace;
}
*{box-sizing:border-box}
html,body{margin:0}
body{font-family:var(--sans);background:var(--bg);color:var(--ink);line-height:1.45;
-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased}
@keyframes fadeUp{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@keyframes grow{from{transform:scaleY(0)}to{transform:scaleY(1)}}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
.wrap{max-width:640px;margin:0 auto;padding:0 14px 96px}
header{background:linear-gradient(140deg,var(--sea),var(--sea2));color:#fff;
padding:calc(18px + env(safe-area-inset-top)) 16px 16px}
header .wrap{padding:0;max-width:612px}
.brand{display:flex;align-items:center;gap:12px}
.brand svg{flex:none;filter:drop-shadow(0 2px 5px rgba(0,0,0,.28))}
h1{font-size:20px;margin:0;letter-spacing:-.3px;font-weight:700;line-height:1.05}
.sub{font-size:12.5px;opacity:.82;margin:6px 0 0;max-width:54ch}
.pstamp{display:inline-flex;align-items:center;gap:5px;margin-top:9px;font-size:11px;
background:rgba(255,255,255,.14);padding:3px 9px;border-radius:20px}
.pstamp b{font-weight:600}
.card{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:15px;margin:14px 0;
box-shadow:0 1px 2px rgba(11,58,84,.05),0 8px 24px -18px rgba(11,58,84,.35);animation:fadeUp .4s ease both}
h2{font-size:11.5px;text-transform:uppercase;letter-spacing:1.2px;color:var(--sea2);margin:0 0 11px;font-weight:700;
display:flex;justify-content:space-between;align-items:center;gap:8px}
h2 .tag{font-size:10.5px;letter-spacing:.3px;color:var(--mut);text-transform:none;font-weight:500;text-align:right}
label{font-size:12px;color:var(--mut);display:block;margin:0 0 5px}
.row{display:flex;gap:10px;flex-wrap:wrap}
.row>div{flex:1;min-width:120px}
input,select{width:100%;font-size:16px;padding:9px 10px;border:1px solid var(--line);border-radius:10px;
background:#fbfdff;color:var(--ink);font-family:inherit}
input:focus,select:focus{outline:2px solid var(--sea2);border-color:var(--sea2)}
.hint{font-size:11.5px;color:var(--mut);margin:6px 0 0}
#status{border-radius:18px;padding:17px;color:#fff;text-align:center;margin:14px 0;transition:background .3s}
#status.no{background:linear-gradient(140deg,#9c352b,#c2352a)}
#status.close{background:linear-gradient(140deg,#b1701a,#c9821f)}
#status.yes{background:linear-gradient(140deg,#166b42,#1f9257)}
#statusMain{font-size:18px;font-weight:700;margin:0 0 4px;letter-spacing:-.2px}
#statusSub{font-size:13px;opacity:.95;margin:0}
.bignum{font-family:var(--mono);font-size:32px;font-weight:700;line-height:1.05;margin:8px 0 2px;letter-spacing:-1px}
.bar{height:14px;background:rgba(255,255,255,.25);border-radius:8px;overflow:hidden;margin:12px 0 2px}
.bar>span{display:block;height:100%;background:#fff;width:0;transition:width .35s cubic-bezier(.16,1,.3,1)}
.today{border:1px solid var(--line);border-radius:14px;padding:13px;background:linear-gradient(180deg,#fff,#f7fafc)}
.today .tbar{height:12px;background:#e3edf2;border-radius:7px;overflow:hidden;margin:9px 0 8px}
.today .tbar>span{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--sea2),var(--coral));transition:width .35s cubic-bezier(.16,1,.3,1)}
.today.done .tbar>span{background:linear-gradient(90deg,var(--green),#40c584)}
.todayMsg{font-size:13.5px;font-weight:600}
.todayMsg small{display:block;font-weight:400;color:var(--mut);font-size:12px;margin-top:2px}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:12px}
.stat{background:var(--sky);border-radius:11px;padding:11px}
.stat b{display:block;font-size:19px;font-family:var(--mono);letter-spacing:-.5px}
.stat span{font-size:11px;color:var(--mut)}
.stat.extra{background:#fdeee9}.stat.extra b{color:var(--coral2)}
.chart{position:relative;height:132px;display:flex;align-items:flex-end;gap:4px;padding-top:6px;margin-top:4px}
.col{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%;gap:4px;cursor:pointer}
.col .b{width:100%;max-width:26px;border-radius:5px 5px 2px 2px;background:var(--sea2);transform-origin:bottom;animation:grow .4s ease both}
.col .b.hit{background:var(--green)}
.col .b.today{outline:2px solid var(--coral);outline-offset:1px}
.col .b.future{background:#dbe5eb}
.col small{font-size:9.5px;color:var(--mut);font-family:var(--mono)}
.tline{position:absolute;left:0;right:0;border-top:2px dashed var(--coral);opacity:.75}
.tlabel{position:absolute;right:0;font-size:9.5px;color:var(--coral2);background:var(--card);padding:0 3px;transform:translateY(-50%)}
.clegend{font-size:11px;color:var(--mut);margin-top:8px;display:flex;gap:12px;flex-wrap:wrap}
.dot{display:inline-block;width:9px;height:9px;border-radius:3px;vertical-align:-1px;margin-right:4px}
.bestday{font-size:12px;color:var(--mut);margin-top:10px}.bestday b{color:var(--sea)}
.seg{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.seg button{font:inherit;font-size:13px;font-weight:600;padding:11px 8px;border-radius:12px;border:1.5px solid var(--line);
background:#fff;color:var(--sea);cursor:pointer;text-align:center;transition:transform .08s,background .15s,border-color .15s}
.seg button:active{transform:translateY(1px)}
.seg button small{display:block;font-weight:400;font-size:10.5px;color:var(--mut);margin-top:2px}
.seg button.active{background:var(--sea);color:#fff;border-color:var(--sea);box-shadow:0 6px 16px -10px var(--sea)}
.seg button.active small{color:#cfe3ef}
.daynav{display:flex;align-items:center;gap:10px;margin:-2px 0 8px}
.navbtn{width:40px;height:40px;flex:none;border:1px solid var(--line);background:#fff;border-radius:11px;font-size:22px;
line-height:1;color:var(--sea);cursor:pointer;transition:transform .08s,background .15s}
.navbtn:active{transform:scale(.92);background:var(--sky)}
.navbtn[disabled]{opacity:.35;cursor:default}
.daylabel{flex:1;text-align:center}
.daylabel b{display:block;font-size:15px;color:var(--sea)}
.daylabel span{font-size:12px;color:var(--mut);font-family:var(--mono)}
.daystrip{display:flex;gap:6px;overflow-x:auto;padding:2px 0 8px;-webkit-overflow-scrolling:touch}
.chip{flex:none;min-width:54px;border:1px solid var(--line);border-radius:10px;padding:6px 7px;background:#fff;
text-align:center;cursor:pointer;transition:transform .08s,border-color .15s}
.chip:active{transform:translateY(1px)}
.chip .cd{font-size:10.5px;color:var(--mut);font-weight:600}
.chip .cv{font-size:11px;font-family:var(--mono);color:var(--sea);margin-top:1px}
.chip.cur{border-color:var(--coral);box-shadow:0 0 0 1px var(--coral)}
.chip.hit{background:#e9f6ef}
.chip.best{border-color:var(--green)}
.chip .crown{color:var(--green);font-size:9px;font-weight:700;display:block;line-height:1}
.swipehint{font-size:11px;color:var(--mut);text-align:center;margin:2px 0 8px}
.cat h3{font-size:13px;margin:16px 0 4px;color:var(--ink);font-weight:700}
.drink{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid #eef3f6}
.drink:last-child{border-bottom:0}
.dinfo{flex:1;min-width:0}
.dname{font-size:14px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.dmeta{font-size:11.5px;color:var(--mut);font-family:var(--mono)}
.dmeta.ov{color:var(--coral2)}
.badge{font-size:10px;padding:1px 6px;border-radius:20px;font-weight:600;white-space:nowrap}
.b-in{background:#e0f2e9;color:#177049}.b-out{background:#fdece7;color:#b5461f}.b-alc{background:#fbeecf;color:#8a5c10}
.stepper{display:flex;align-items:center;gap:0}
.stepper button{width:34px;height:34px;border:1px solid var(--line);background:#fff;border-radius:10px;font-size:19px;line-height:1;
color:var(--sea);cursor:pointer;font-family:inherit;transition:transform .08s,background .15s}
.stepper button:active{background:var(--sky);transform:scale(.9)}
.qty{min-width:28px;text-align:center;font-size:15px;font-weight:700;font-family:var(--mono)}
.pxin{width:78px;font-size:14px;padding:6px 8px;text-align:right;font-family:var(--mono)}
.drink.dim{opacity:.5}
.delx{width:30px;height:30px;flex:none;border:1px solid var(--line);background:#fff;border-radius:9px;color:var(--coral2);
font-size:16px;line-height:1;cursor:pointer;margin-left:6px}
.delx:active{background:#fdeee9}
.addrow{display:flex;gap:6px;align-items:center;margin-top:8px}
.addrow input[type=text]{flex:1;min-width:0;font-size:14px;padding:8px 9px}
.addrow .pxin{flex:none}
.addbtn{flex:none;font:inherit;font-size:13px;font-weight:600;padding:8px 11px;border-radius:10px;border:1px solid var(--sea);
background:var(--sea);color:#fff;cursor:pointer}
.addbtn:active{transform:translateY(1px)}
#customBox h3{font-size:13px;margin:16px 0 4px;color:var(--ink);font-weight:700}
.warn{border-radius:12px;padding:12px;font-size:13px;margin:10px 0}
.warn.red{background:#fdecea;border:1px solid #f0c1bb;color:#8f2018}
.warn.amber{background:#fdf3e3;border:1px solid #eed49a;color:#7a4d12}
.warn.info{background:#eaf3fa;border:1px solid #c3ddf1;color:#1c4a6e}
.warn b{display:block;margin-bottom:2px}
.pill{display:inline-block;background:var(--sky);color:var(--sea);font-size:11px;padding:3px 9px;border-radius:20px;margin:2px 4px 2px 0;font-weight:500}
.btnbar{display:flex;gap:8px;margin-top:12px}
.btnbar button{flex:1;font:inherit;font-size:13px;font-weight:600;padding:11px;border-radius:11px;border:1px solid var(--line);
background:#fff;color:var(--sea);cursor:pointer;transition:transform .08s,background .15s}
.btnbar button:active{transform:translateY(1px);background:var(--sky)}
.btnbar button.primary{background:var(--sea);color:#fff;border-color:var(--sea)}
.btnbar button.warned{color:var(--red);border-color:#eec4c0}
.togg{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--sea);margin-top:2px;cursor:pointer;user-select:none}
.togg input{width:auto}
details{margin-top:8px}
summary{font-size:12.5px;color:var(--sea2);cursor:pointer;padding:4px 0}
.fine{font-size:11.5px;color:var(--mut)}.fine p{margin:8px 0}
.foot{font-size:11px;color:var(--mut);text-align:center;margin:18px 0 0}
.printonly{display:none}
@media print{
header{background:var(--sea)!important;-webkit-print-color-adjust:exact;print-color-adjust:exact}
#setupCard,#drinksCard,#exportCard,#kidDrinks,details,.pstamp,.btnbar,.foot{display:none!important}
.card{box-shadow:none;break-inside:avoid;animation:none}
body{background:#fff}
.printonly{display:block;font-size:12px;color:var(--mut);margin:6px 0 0}
}
</style>
</head>
<body>
<header>
<div class="wrap">
<div class="brand">
<svg width="38" height="38" viewBox="0 0 64 64" aria-hidden="true">
<rect width="64" height="64" rx="14" fill="rgba(255,255,255,.10)"/>
<path d="M15 19h34l-14 16v9h6v4H23v-4h6v-9z" fill="#e2673d"/>
<circle cx="42" cy="15" r="3" fill="#eaf1f5"/>
<path d="M12 50c5 0 5-3 10-3s5 3 10 3 5-3 10-3 5 3 10 3" fill="none" stroke="#9fd0e6" stroke-width="3" stroke-linecap="round"/>
</svg>
<div>
<h1>Getränkepaket · Break-even</h1>
<p class="sub">Lohnt sich dein AIDA-Paket schon heute und über die ganze Reise?</p>
</div>
</div>
<span class="pstamp">Preisstand: AIDA-Barkarte <b>01.11.2025</b> · geprüft Juli 2026</span>
</div>
</header>
<div class="wrap">
<div id="status" class="no">
<div id="statusMain"></div>
<div class="bignum" id="statusNum"></div>
<div id="statusSub"></div>
<div class="bar"><span id="bar"></span></div>
<div class="printonly" id="printHead"></div>
</div>
<div class="card" id="setupCard">
<h2>Dein Paket</h2>
<div class="seg" id="pkgPick">
<button data-pkg="fun" class="active">FUN <small>alkoholfrei · ab 16 J.</small></button>
<button data-pkg="allin">ALL IN <small>mit Alkohol · ab 18 J.</small></button>
<button data-pkg="kfun">Kids &amp; Teens FUN <small>417 J.</small></button>
<button data-pkg="kallin">Kids &amp; Teens ALL IN <small>alkoholfrei · 417 J.</small></button>
</div>
<div class="row" style="margin-top:12px">
<div><label>Preis pro Person &amp; Paket (€)</label>
<input id="price" type="number" inputmode="decimal" min="0" step="0.01" value="159"></div>
<div><label>Personen mit Paket</label>
<input id="persons" type="number" inputmode="numeric" min="1" step="1" value="2"></div>
</div>
<div class="row" style="margin-top:10px">
<div><label>Reisedauer (Tage)</label>
<input id="days" type="number" inputmode="numeric" min="1" max="30" step="1" value="7"></div>
<div><label>Heute = Reisetag Nr.</label>
<input id="curday" type="number" inputmode="numeric" min="1" step="1" value="1"></div>
</div>
<p class="hint" id="priceHint"></p>
<div class="warn info" style="margin:10px 0 0">Gemischte Kabine? Erlaubt: Erwachsene und Kinder dürfen <b>unterschiedliche</b> Pakete haben (z.B. 2× ALL IN + 1× Kids &amp; Teens FUN) nur <b>innerhalb</b> einer Altersgruppe muss es gleich sein. Rechne jede Paket-Gruppe hier einzeln durch.</div>
</div>
<div class="card today" id="todayCard">
<h2>Heute <span class="tag" id="todayTag">Tag 1</span></h2>
<div class="todayMsg" id="todayMsg"></div>
<div class="tbar"><span id="tbar"></span></div>
<div class="hint" id="todayExtra"></div>
</div>
<div class="card">
<h2>Über die ganze Reise</h2>
<div class="grid2">
<div class="stat"><b id="sPkg"></b><span>Paketkosten gesamt</span></div>
<div class="stat"><b id="sCons"></b><span>Angerechneter Wert bisher</span></div>
<div class="stat"><b id="sPerDay"></b><span>Break-even pro Tag</span></div>
<div class="stat extra"><b id="sExtra"></b><span>Zusatzkosten (nicht im Paket)</span></div>
</div>
<div id="projLine" class="warn info" style="display:none;margin-bottom:0"></div>
</div>
<div class="card">
<h2>Tag für Tag <span class="tag">angerechneter Wert je Tag</span></h2>
<div class="chart" id="chart"></div>
<div class="clegend">
<span><i class="dot" style="background:var(--green)"></i>Tagesziel erreicht</span>
<span><i class="dot" style="background:var(--sea2)"></i>drunter</span>
<span><i class="dot" style="background:#dbe5eb"></i>kommt noch</span>
<span><i class="dot" style="background:var(--coral)"></i>heute</span>
</div>
<div class="bestday" id="bestDay"></div>
</div>
<div class="card" id="panel"></div>
<div class="card" id="drinksCard">
<h2>Getränke erfassen</h2>
<div class="daynav">
<button id="prevDay" class="navbtn" aria-label="Tag zurück"></button>
<div class="daylabel"><b id="dayNavLabel">Tag 1 / 7</b><span id="dayNavVal">0,00 €</span></div>
<button id="nextDayNav" class="navbtn" aria-label="Tag vor"></button>
</div>
<div class="daystrip" id="dayStrip"></div>
<p class="swipehint"> wischen oder Tag antippen · rückwirkend bearbeitbar </p>
<label class="togg"><input type="checkbox" id="editPrices"> Getränkepreise anpassen (falls an Bord anders)</label>
<p class="hint" style="margin-top:6px" id="drinksHelp">Grün = im Paket (zählt zum Break-even). Orange = nicht im Paket → <b>on top</b> als Zusatzkosten. Lokal gespeichert, offline.</p>
<div id="drinks"></div>
<div id="customBox">
<h3>Eigene Drinks · on top (z.B. Starbucks Iced Latte)</h3>
<div id="customList"></div>
<div class="addrow">
<input type="text" id="cName" placeholder="Name, z.B. Starbucks Iced Latte" maxlength="40">
<input type="number" id="cPrice" class="pxin" inputmode="decimal" min="0" step="0.1" placeholder="€">
<button class="addbtn" id="cAdd">+ Drink</button>
</div>
<p class="hint" style="margin-top:6px">Diese Drinks kennt die Barkarte nicht sie zählen immer als <b>Zusatzkosten</b> (nie im Paket).</p>
</div>
<div class="btnbar" id="priceResetBar" style="display:none">
<button id="resetPrices">Standardpreise wiederherstellen</button>
</div>
<div class="btnbar">
<button id="resetDay">Diesen Tag leeren</button>
<button id="resetAll" class="warned">Alles zurücksetzen</button>
</div>
</div>
<div class="card" id="kidCard">
<h2>Kind unter 4 · gratis dabei</h2>
<p class="hint" style="margin-top:0">Kinder unter 4 bekommen über das Paket der Eltern das <b>Kids &amp; Teens FUN gratis</b> (ohne eigene Karte). Was das Kind trinkt, kostet nichts zählt aber als geschenkter Wert obendrauf, <b>separat</b> vom Break-even.</p>
<div id="kidDrinks"></div>
<div class="warn info" id="kidLine" style="margin-bottom:0">Noch nichts fürs Kind erfasst.</div>
</div>
<div class="card" id="exportCard">
<h2>Abrechnung exportieren</h2>
<p class="hint" style="margin-top:0">Momentaufnahme deiner Getränkepaket-Abrechnung sichern.</p>
<div class="btnbar">
<button id="expImg" class="primary">Als Bild (JPG)</button>
<button id="expPdf">Als PDF speichern</button>
</div>
</div>
<details>
<summary>Kleingedrucktes &amp; Regeln (AGB-Auszug, ehrlich)</summary>
<div class="fine" id="rules"></div>
</details>
<p class="foot">Inoffizieller Helfer · Preise &amp; Paketinhalte laut AIDA-Barkarte/AGB (Stand 01.11.2025, geprüft Juli 2026). Auswahl kann je nach Fahrtgebiet, Zeitraum &amp; Bar leicht variieren im Zweifel gilt die Barkarte an Bord.</p>
</div>
<script>
"use strict";
/* ---- Getränke-DB (echte Barkarten-Preise €) ----
tag: base=alle Pakete | mock=alkfr. Cocktails (alle Pakete) | coffee=Kaffeespez. (FUN & ALL IN)
prem=Premium-Softdrink/alkfr. Bier (nur ALL IN) | alc=alkoholisch (nur ALL IN) | none=kein Paket */
const CAT=[
["Alkoholfrei · Basics",[
["Coca-Cola","0,2 l",2.6,"base"],["Coca-Cola Zero","0,2 l",2.6,"base"],["Fanta","0,2 l",2.6,"base"],
["Sprite","0,2 l",2.6,"base"],["Saft / Schorle","0,2 l",2.6,"base"],["Sodawasser","0,2 l",2.0,"base"],
["AIDA Lemonade","",5.5,"base"],["AIDA Iced Tea","",5.5,"base"]]],
["Heißgetränke",[
["Kakao","Kinder",1.5,"base"],["Eilles Tee","",3.4,"base"],["Heiße Schokolade","",2.9,"base"],
["Espresso","",2.2,"coffee"],["Kaffee","",2.9,"coffee"],["Espresso Macchiato","",2.9,"coffee"],
["Doppelter Espresso","",3.6,"coffee"],["Cappuccino","",4.1,"coffee"],["Milchkaffee","",4.1,"coffee"],
["Latte Macchiato","",4.1,"coffee"]]],
["Alkoholfreie Cocktails, Mocktails & Shakes",[
["Aquarium (alkoholfrei)","",7.5,"mock"],["Cherry Bomb (alkoholfrei)","",7.5,"mock"],
["Erdbeer Colada (alkoholfrei)","",7.5,"mock"],["Ipanema (alkoholfrei)","",7.5,"mock"],
["Palm Beach (alkoholfrei)","",6.5,"mock"],["Banana Split (alkoholfrei)","",6.5,"mock"],
["Sunrise (alkoholfrei)","",6.5,"mock"],["Coco Choco Loco (alkoholfrei)","",5.5,"mock"],
["Apple Crumble (alkoholfrei)","",5.5,"mock"],["AIDA Virgin Sprizz","",7.9,"mock"],
["Virgin Hugo","",7.9,"mock"],["Aperitivo Sprizz (alkoholfrei)","",8.9,"mock"],
["Little Berry (alkoholfrei)","",8.9,"mock"],["Milchshake","",5.5,"base"],
["Kids & Teens Cocktail (Dodo & Co.)","",5.5,"base"]]],
["Premium-Softdrinks & alkoholfreies Bier",[
["BioZisch Limonade (Voelkel)","0,33 l",3.9,"prem"],["Kombucha","0,33 l",3.9,"prem"],
["ChariTea","0,33 l",3.9,"prem"],["Fever-Tree Tonic & Co.","0,2 l",3.9,"prem"],
["Red Bull","0,25 l",3.9,"prem"],["Alkoholfreies Bier (Jever Fun u.a.)","0,33 l",4.6,"prem"]]],
["Bier",[
["Radeberger Pilsner","0,3 l",4.0,"alc",true],["Radeberger Pilsner","0,5 l",5.5,"alc",true],
["Schöfferhofer Hefeweizen","0,5 l",5.5,"alc",true],["Corona","0,355 l",4.8,"alc",true],
["Salitos","0,33 l",4.6,"alc",true],["Heineken","0,33 l",4.6,"alc",true],
["Schöfferhofer Grapefruit","0,33 l",4.6,"alc",true]]],
["Wein & Sekt (im Glas)",[
["Wein im Glas (Edition AIDA)","0,2 l",8.0,"alc",true],["Primitivo / Cabernet","0,2 l",10.0,"alc",true],
["Sekt / Prosecco","0,1 l",4.9,"alc",true],["Sekt / Prosecco auf Eis","0,15 l",6.6,"alc",true]]],
["Cocktails",[
["AIDA Honey Mule","",10.2,"alc",true],["Dark & Stormy","",9.2,"alc",true],
["Deep Purple Magic Sour","",11.2,"alc",true],["Coconut Mai Tai","",10.2,"alc",true],
["White Basil","",8.2,"alc",true],["AIDA Icebreaker","",10.2,"alc",true],
["AIDA Mai Tai","",10.2,"alc",true],["Mojito","",9.2,"alc",true],["Sex on the Beach","",9.2,"alc",true],
["Caipirinha","",9.2,"alc",true],["Piña Colada","",9.9,"alc",true],["Hurricane","",10.2,"alc",true],
["Swimming Pool","",10.2,"alc",true],["Long Island Iced Tea","",11.9,"alc",true],
["Margarita","",8.2,"alc",true],["Negroni","",10.2,"alc",true],["Whiskey Sour","",8.2,"alc",true],
["Cosmopolitan","",7.9,"alc",true],["Espresso Martini","",8.2,"alc",true]]],
["Longdrinks & Gin Tonics",[
["Gin Tonic","",10.6,"alc",true],["Vodka Red Bull","",9.6,"alc",true],["Vodka Cranberry","",8.3,"alc",true],
["Whiskey Cola","",9.3,"alc",true],["Campari Orange","",8.3,"alc",true],["Rum Cola","",8.3,"alc",true],
["Cuba Libre","",7.9,"alc",true],["Deep & Purple G&T","",11.6,"alc",true],
["AIDA Guilty Pleasure","",11.2,"alc",true],["Garden & Tonic","",10.2,"alc",true],
["Italian G&T","",12.0,"alc",true]]],
["Sprizz & Shots",[
["Aperol Sprizz","",9.2,"alc",true],["AIDA Sprizz","",8.2,"alc",true],["Lillet Berry","",9.2,"alc",true],
["Hugo","",8.2,"alc",true],["Shot (B-52, Hellfire …)","4 cl",4.9,"alc",true]]],
["Spirituosen, Aperitifs & Liköre",[
["Spirituose Standard (Gin, Rum, Vodka …)","4 cl",5.7,"alc",true],
["Premium-Spirituose (Monkey 47, Grey Goose Altius …)","4 cl",9.5,"alc",true],
["Aperitif / Likör / Bitter (Aperol, Baileys, Jägermeister …)","4 cl",4.9,"alc",true],
["Grappa","4 cl",6.7,"alc",true]]],
["Nicht im Paket (immer extra)",[
["Mineralwasser Flasche","0,75 l",6.9,"none"],["Champagner (Glas)","0,1 l",9.6,"none"],
["Weinflasche","0,75 l",27.9,"none"],["Champagner-Flasche","0,75 l",149.0,"none"],
["Starbucks (ca.)","",5.0,"none"]]]
];
const KID=[["Milchshake",5.5],["Kids & Teens Cocktail (Dodo & Co.)",5.5],["Mocktail (Coco Choco Loco)",5.5],
["Saft / Schorle",2.6],["Softdrink (Cola, Fanta …)",2.6],["Kakao",1.5],["Eilles Tee",3.4]];
function inPkg(tag,pkg){switch(pkg){
case"fun":return tag==="base"||tag==="mock"||tag==="coffee";
case"allin":return tag!=="none";
case"kfun":case"kallin":return tag==="base"||tag==="mock";}return false;}
const PKGNAME={fun:"FUN",allin:"ALL IN",kfun:"Kids & Teens FUN",kallin:"Kids & Teens ALL IN"};
/* AIDA-Richtwerte €/Tag (Recherche Juli 2026, variiert je Reise) */
const SUG={fun:{lo:16,hi:21,d:19},allin:{lo:36,hi:47,d:39.9},kallin:{lo:16,hi:20,d:17.9},kfun:{lo:12,hi:16,d:13.9}};
const DRINKS=[];
CAT.forEach((c,ci)=>c[1].forEach((d,di)=>DRINKS.push(
{id:"d"+ci+"_"+di,name:d[0],size:d[1],base:d[2],tag:d[3],alc:!!d[4],cat:c[0]})));
const BYID={}; DRINKS.forEach(d=>BYID[d.id]=d);
/* ---- state ---- */
const KEY="aidaBreakeven_v4";
let S={pkg:"fun",priceByPkg:{fun:159,allin:279,kfun:97,kallin:125},persons:2,days:7,curday:1,
byday:{},kid:{},priceOv:{},editPrices:false,custom:[]};
try{const r=localStorage.getItem(KEY);if(r){const o=JSON.parse(r);S=Object.assign(S,o);
S.priceByPkg=Object.assign({fun:159,allin:279,kfun:97,kallin:125},o.priceByPkg||{});
if(!Array.isArray(S.custom))S.custom=[];}}catch(e){}
function save(){try{localStorage.setItem(KEY,JSON.stringify(S));}catch(e){}}
function today(){return S.byday[S.curday]||(S.byday[S.curday]={});}
function effPrice(id){const o=S.priceOv[id];return(typeof o==="number"&&o>=0)?o:BYID[id].base;}
function custom(id){return S.custom.find(x=>x.id===id);}
function itemPrice(id){if(BYID[id])return effPrice(id);const c=custom(id);return c?c.price:0;}
function itemIncl(id){return BYID[id]?inPkg(BYID[id].tag,S.pkg):false;}
function itemAlc(id){return BYID[id]?BYID[id].alc:false;}
const eur=x=>x.toLocaleString("de-DE",{minimumFractionDigits:2,maximumFractionDigits:2})+" €";
const $=s=>document.querySelector(s);
const num=(v,f)=>{v=parseFloat((""+v).replace(",","."));return isFinite(v)&&v>=0?v:f;};
function valOf(counts){let incl=0,extra=0,alcV=0,alcN=0;
for(const id in counts){const q=counts[id];if(!BYID[id]&&!custom(id))continue;const p=itemPrice(id);
if(itemIncl(id)){incl+=p*q;if(itemAlc(id)){alcV+=p*q;alcN+=q;}}else extra+=p*q;}
return{incl,extra,alcV,alcN};}
function totals(){let incl=0,extra=0,alcV=0,alcN=0;
for(const dd in S.byday){const v=valOf(S.byday[dd]);incl+=v.incl;extra+=v.extra;alcV+=v.alcV;alcN+=v.alcN;}
return{incl,extra,alcV,alcN};}
function dayVals(){const a=[];for(let d=1;d<=S.days;d++)a.push(valOf(S.byday[d]||{}).incl);return a;}
/* ---- render drinks (count- oder edit-mode) ---- */
function renderDrinks(){
const host=$("#drinks");host.innerHTML="";const tc=today();
CAT.forEach((c,ci)=>{
const wrap=document.createElement("div");wrap.className="cat";
wrap.innerHTML='<h3>'+c[0]+'</h3>';
c[1].forEach((d,di)=>{
const id="d"+ci+"_"+di,tag=d[3],alc=!!d[4],incl=inPkg(tag,S.pkg),p=effPrice(id),ov=S.priceOv[id]!=null;
let badge=incl?'<span class="badge b-in">im Paket</span>'
:(tag==="none"?'<span class="badge b-out">nie im Paket</span>':'<span class="badge b-out">extra +'+eur(p)+'</span>');
if(incl&&alc)badge+=' <span class="badge b-alc">Alkohol</span>';
const meta=[d[1],eur(p)+(ov?" ✎":"")].filter(Boolean).join(" · ");
const right=S.editPrices
? '<input class="pxin" type="number" inputmode="decimal" min="0" step="0.1" value="'+p+'" data-px="'+id+'" aria-label="Preis '+d[0]+'">'
: '<div class="stepper"><button data-m="'+id+'" aria-label="minus"></button>'+
'<span class="qty" id="q_'+id+'">'+(tc[id]||0)+'</span><button data-p="'+id+'" aria-label="plus">+</button></div>';
const row=document.createElement("div");row.className="drink"+(incl?"":" dim");
row.innerHTML='<div class="dinfo"><div class="dname">'+d[0]+' '+badge+'</div><div class="dmeta'+(ov?' ov':'')+'">'+meta+'</div></div>'+right;
wrap.appendChild(row);
});host.appendChild(wrap);
});
}
function renderCustom(){
const host=$("#customList");host.innerHTML="";const tc=today();
S.custom.forEach(c=>{const q=tc[c.id]||0;
const row=document.createElement("div");row.className="drink";
row.innerHTML='<div class="dinfo"><div class="dname">'+c.name+' <span class="badge b-out">extra +'+eur(c.price)+'</span></div>'+
'<div class="dmeta">'+eur(c.price)+' · on top</div></div>'+
'<div class="stepper"><button data-m="'+c.id+'" aria-label="minus"></button>'+
'<span class="qty" id="q_'+c.id+'">'+q+'</span><button data-p="'+c.id+'" aria-label="plus">+</button></div>'+
'<button class="delx" data-del="'+c.id+'" aria-label="löschen">×</button>';
host.appendChild(row);});
}
function renderKid(){
const host=$("#kidDrinks");host.innerHTML="";
KID.forEach((d,i)=>{const id="k"+i,q=S.kid[id]||0;
const row=document.createElement("div");row.className="drink";
row.innerHTML='<div class="dinfo"><div class="dname">'+d[0]+' <span class="badge b-in">gratis</span></div>'+
'<div class="dmeta">Wert '+eur(d[1])+'</div></div>'+
'<div class="stepper"><button data-km="'+id+'" aria-label="minus"></button>'+
'<span class="qty" id="qk_'+id+'">'+q+'</span><button data-kp="'+id+'" aria-label="plus">+</button></div>';
host.appendChild(row);});
}
/* ---- day strip ---- */
function renderStrip(vals,perDay,bestDay){
const host=$("#dayStrip");host.innerHTML="";
for(let d=1;d<=S.days;d++){
const v=vals[d-1],c=document.createElement("div");
c.className="chip"+(d===S.curday?" cur":"")+(v>=perDay&&v>0?" hit":"")+(d===bestDay&&v>0?" best":"");
c.setAttribute("data-day",d);
c.innerHTML='<div class="cd">Tag '+d+'</div><div class="cv">'+v.toFixed(0)+'€</div>'+(d===bestDay&&v>0?'<div class="crown">TOP</div>':'');
host.appendChild(c);
}
const cur=host.querySelector(".chip.cur");if(cur)cur.scrollIntoView({inline:"center",block:"nearest"});
}
/* ---- chart ---- */
function renderChart(vals,perDay,bestDay){
const host=$("#chart");host.innerHTML="";
const scale=Math.max(perDay*1.25,Math.max.apply(null,vals.concat([1]))*1.1,1);
const tl=document.createElement("div");tl.className="tline";tl.style.bottom=(perDay/scale*100)+"%";host.appendChild(tl);
const lab=document.createElement("div");lab.className="tlabel";lab.style.bottom=(perDay/scale*100)+"%";
lab.textContent="Ziel "+eur(perDay);host.appendChild(lab);
for(let d=1;d<=S.days;d++){const v=vals[d-1],col=document.createElement("div");col.className="col";col.setAttribute("data-day",d);
const b=document.createElement("div");b.className="b";
if(d>S.curday&&v===0)b.classList.add("future");else if(v>=perDay&&v>0)b.classList.add("hit");
if(d===S.curday)b.classList.add("today");
b.style.height=Math.max(v/scale*100,v>0?3:0)+"%";col.appendChild(b);
const s=document.createElement("small");s.textContent=d;col.appendChild(s);host.appendChild(col);}
}
/* ---- compute ---- */
let LAST={};
function compute(){
S.price=num($("#price").value,0);S.priceByPkg[S.pkg]=S.price;
S.persons=Math.max(1,Math.round(num($("#persons").value,1)));
S.days=Math.max(1,Math.round(num($("#days").value,1)));
S.curday=Math.max(1,Math.min(S.days,Math.round(num($("#curday").value,1))));
const pkgTotal=S.price*S.persons, perDay=pkgTotal/S.days;
const T=totals(), tv=valOf(today()), vals=dayVals();
const remaining=pkgTotal-T.incl;
let bestDay=0,bestVal=0;vals.forEach((v,i)=>{if(v>bestVal){bestVal=v;bestDay=i+1;}});
const st=$("#status");
if(remaining>0.001){const ratio=pkgTotal>0?T.incl/pkgTotal:0;
st.className=ratio>=0.75?"close":"no";
$("#statusMain").textContent=ratio>=0.75?"Fast am Break-even":"Lohnt sich noch nicht";
$("#statusNum").textContent="noch "+eur(remaining);
$("#statusSub").textContent="bis sich dein "+PKGNAME[S.pkg]+"-Paket über die Reise rechnet";
}else{st.className="yes";$("#statusMain").textContent="Break-even erreicht";
$("#statusNum").textContent="+"+eur(-remaining)+" Plus";
$("#statusSub").textContent="Jeder weitere Drink im Paket ist ab jetzt geschenkt";}
$("#bar").style.width=Math.min(100,pkgTotal>0?T.incl/pkgTotal*100:0)+"%";
$("#todayTag").textContent="Tag "+S.curday;
$("#tbar").style.width=Math.min(100,perDay>0?tv.incl/perDay*100:0)+"%";
const tdone=tv.incl>=perDay&&perDay>0;$("#todayCard").classList.toggle("done",tdone);
$("#todayMsg").innerHTML=tdone
?"Für heute ausgereizt "+eur(tv.incl)+" von "+eur(perDay)+"<small>Tagesziel geknackt. Alles Weitere heute ist reiner Gewinn.</small>"
:"Noch nicht ausgereizt für heute<small>"+eur(tv.incl)+" von "+eur(perDay)+" noch <b>"+eur(perDay-tv.incl)+"</b> bis dein Paket sich heute rechnet.</small>";
$("#todayExtra").innerHTML=tv.extra>0?"Heute zusätzlich außerhalb des Pakets: <b>"+eur(tv.extra)+"</b>":"";
$("#sPkg").textContent=eur(pkgTotal);$("#sCons").textContent=eur(T.incl);
$("#sPerDay").textContent=eur(perDay);$("#sExtra").textContent=eur(T.extra);
const pj=$("#projLine");
if(T.incl>0&&S.curday<S.days){const paceDay=T.incl/S.curday,proj=paceDay*S.days,ok=proj>=pkgTotal;
pj.style.display="block";pj.className="warn "+(ok?"info":"amber");
pj.innerHTML="<b>Hochrechnung</b>Dein Schnitt: "+eur(paceDay)+"/Tag → bis Reiseende ca. <b>"+eur(proj)+"</b> von "+eur(pkgTotal)+". "+
(ok?"Du liegst über dem Break-even.":"Das ist <b>unter</b> Break-even ca. "+eur(Math.max(0,remaining)/Math.max(1,S.days-S.curday+1))+" pro Resttag nötig.");
}else pj.style.display="none";
renderChart(vals,perDay,bestDay);renderStrip(vals,perDay,bestDay);renderCustom();
$("#bestDay").innerHTML=bestVal>0?"Stärkster Tag: <b>Tag "+bestDay+"</b> mit "+eur(bestVal)+" angerechnetem Wert.":"Noch keine Getränke erfasst leg an einem Tag los.";
$("#dayNavLabel").textContent="Tag "+S.curday+" / "+S.days;
$("#dayNavVal").textContent=eur(tv.incl)+(tv.extra>0?" (+"+eur(tv.extra)+" extra)":"");
$("#prevDay").disabled=S.curday<=1;$("#nextDayNav").disabled=S.curday>=S.days;
/* panel */
const p=$("#panel");
if(S.pkg==="allin"){const alcPerDay=T.alcN/S.curday,needAlc=perDay/8.5;
let b='<h2>Fairer Alkohol-Check</h2><p class="hint" style="margin-top:0">Vom angerechneten Wert entfallen <b>'+eur(T.alcV)+
'</b> auf Alkohol ('+T.alcN+' Drink'+(T.alcN!==1?'s':'')+', Ø '+alcPerDay.toFixed(1)+'/Tag).</p>';
if(alcPerDay>=5)b+='<div class="warn red" style="margin-bottom:0"><b>Bitte aufpassen.</b>Das sind Ø '+alcPerDay.toFixed(1)+
' alkoholische Getränke pro Tag. Ein Paket rechnet sich <b>nie</b>, indem man mehr trinkt, als einem guttut. Der Break-even ist kein Trinkziel Wasser, Kaffee &amp; Mocktails zählen genauso.</div>';
else b+='<div class="warn amber" style="margin-bottom:0"><b>Denk dran:</b> Rein über Alkohol bräuchtest du ~'+needAlc.toFixed(1)+
' Drinks/Tag für den Break-even. Muss nicht sein Kaffeespezialitäten, Softdrinks &amp; alkoholfreie Cocktails zählen voll mit. Verantwortungsvoll genießen.</div>';
p.innerHTML=b;
}else{const incl=DRINKS.filter(d=>inPkg(d.tag,S.pkg));
const top=incl.slice().sort((a,b)=>effPrice(b.id)-effPrice(a.id))
.filter((d,i,a)=>a.findIndex(x=>x.name.replace(/\s*\(alkoholfrei\)/,"")===d.name.replace(/\s*\(alkoholfrei\)/,""))===i).slice(0,6);
let h='<h2>So holst du am meisten raus</h2><p class="hint" style="margin-top:0">Break-even = <b>'+eur(perDay)+
' pro Tag</b>. Das sind z.B. ~'+(perDay/4.1).toFixed(1)+' Latte Macchiato <i>oder</i> ~'+(perDay/7.5).toFixed(1)+' Mocktails am Tag.</p>';
h+='<div style="font-size:12px;color:var(--mut);margin:8px 0 3px">Teuerste inkludierte Drinks (bringen Wert am schnellsten):</div><div>'+
top.map(d=>'<span class="pill">'+d.name.replace(/\s*\(alkoholfrei\)/,'')+' · '+eur(effPrice(d.id))+'</span>').join('')+'</div>';
p.innerHTML=h;}
let kidVal=0,kidN=0;KID.forEach((d,i)=>{const q=S.kid["k"+i]||0;kidVal+=d[1]*q;kidN+=q;});
$("#kidLine").innerHTML=kidN>0?"Das Kind hat für <b>"+eur(kidVal)+"</b> getrunken komplett <b>gratis</b> über euer Paket. Reiner Bonus, unabhängig vom Break-even oben.":"Noch nichts fürs Kind erfasst.";
const perD=(S.price/S.days), sg=SUG[S.pkg];
$("#priceHint").innerHTML="Aktuell ≈ <b>"+eur(perD)+"/Tag</b>. AIDA-Richtwert "+PKGNAME[S.pkg]+": ~"+sg.lo+""+sg.hi+
" €/Tag (variiert je Reise, Zeitraum &amp; Fahrtgebiet trag deinen echten myAIDA-Preis ein)."+
(S.pkg==="allin"?" ALL IN erst ab 5 Reisetagen buchbar.":"");
$("#printHead").innerHTML="Abrechnung "+PKGNAME[S.pkg]+" · "+S.persons+"× · "+S.days+" Tage · Stand "+new Date().toLocaleDateString("de-DE");
LAST={pkgTotal,consumed:T.incl,extra:T.extra,remaining,perDay,vals,bestDay,bestVal,kidVal,
alcV:T.alcV,alcN:T.alcN,pkg:S.pkg,persons:S.persons,days:S.days,curday:S.curday};
save();
}
/* ---- events ---- */
$("#drinks").addEventListener("click",e=>{
const plus=e.target.getAttribute("data-p"),minus=e.target.getAttribute("data-m"),id=plus||minus;if(!id)return;
const tc=today();let q=tc[id]||0;q+=plus?1:-1;if(q<0)q=0;if(q===0)delete tc[id];else tc[id]=q;
const el=document.getElementById("q_"+id);if(el)el.textContent=q;compute();});
$("#drinks").addEventListener("change",e=>{const id=e.target.getAttribute("data-px");if(!id)return;
const v=num(e.target.value,BYID[id].base);S.priceOv[id]=v;renderDrinks();compute();});
$("#customList").addEventListener("click",e=>{
const del=e.target.getAttribute("data-del");
if(del){if(confirm("Diesen eigenen Drink löschen?")){S.custom=S.custom.filter(x=>x.id!==del);
for(const d in S.byday)delete S.byday[d][del];renderCustom();compute();}return;}
const plus=e.target.getAttribute("data-p"),minus=e.target.getAttribute("data-m"),id=plus||minus;if(!id)return;
const tc=today();let q=tc[id]||0;q+=plus?1:-1;if(q<0)q=0;if(q===0)delete tc[id];else tc[id]=q;
const el=document.getElementById("q_"+id);if(el)el.textContent=q;compute();});
$("#cAdd").addEventListener("click",()=>{
const nm=($("#cName").value||"").trim().slice(0,40),pr=num($("#cPrice").value,-1);
if(!nm){$("#cName").focus();return;}
if(pr<0){$("#cPrice").focus();return;}
S.custom.push({id:"c"+Date.now().toString(36),name:nm,price:pr});
$("#cName").value="";$("#cPrice").value="";renderCustom();compute();});
$("#kidDrinks").addEventListener("click",e=>{
const kp=e.target.getAttribute("data-kp"),km=e.target.getAttribute("data-km"),id=kp||km;if(!id)return;
let q=S.kid[id]||0;q+=kp?1:-1;if(q<0)q=0;if(q===0)delete S.kid[id];else S.kid[id]=q;
const el=document.getElementById("qk_"+id);if(el)el.textContent=q;compute();});
$("#pkgPick").addEventListener("click",e=>{const b=e.target.closest("button");if(!b)return;
S.pkg=b.getAttribute("data-pkg");$("#price").value=S.priceByPkg[S.pkg];
[...$("#pkgPick").children].forEach(x=>x.classList.toggle("active",x===b));
renderDrinks();compute();});
["persons","days","curday"].forEach(f=>$("#"+f).addEventListener("input",()=>{renderDrinks();compute();}));
$("#price").addEventListener("input",compute);
$("#editPrices").addEventListener("change",e=>{S.editPrices=e.target.checked;
$("#priceResetBar").style.display=S.editPrices?"flex":"none";
$("#drinksHelp").innerHTML=S.editPrices?"Preis-Modus: passe einzelne Getränkepreise an (z.B. wenn an Bord anders). Häkchen entfernen, um wieder zu zählen.":
"Grün = im Paket (zählt zum Break-even). Orange = nicht im Paket → <b>on top</b> als Zusatzkosten. Lokal gespeichert, offline.";
renderDrinks();compute();});
$("#resetPrices").addEventListener("click",()=>{if(confirm("Alle Getränkepreise auf Standard zurücksetzen?")){S.priceOv={};renderDrinks();compute();}});
function goDay(d){S.curday=Math.max(1,Math.min(S.days,d));$("#curday").value=S.curday;renderDrinks();compute();}
$("#prevDay").addEventListener("click",()=>goDay(S.curday-1));
$("#nextDayNav").addEventListener("click",()=>goDay(S.curday+1));
$("#dayStrip").addEventListener("click",e=>{const c=e.target.closest(".chip");if(c)goDay(+c.getAttribute("data-day"));});
$("#chart").addEventListener("click",e=>{const c=e.target.closest(".col");if(c)goDay(+c.getAttribute("data-day"));});
/* swipe on drinks card */
(function(){let x0=null,y0=null;const el=$("#drinksCard");
el.addEventListener("touchstart",e=>{const t=e.changedTouches[0];x0=t.clientX;y0=t.clientY;},{passive:true});
el.addEventListener("touchend",e=>{if(x0==null)return;const t=e.changedTouches[0],dx=t.clientX-x0,dy=t.clientY-y0;
if(Math.abs(dx)>55&&Math.abs(dx)>Math.abs(dy)*1.5)goDay(S.curday+(dx<0?1:-1));x0=null;});})();
$("#resetDay").addEventListener("click",()=>{if(confirm("Tag "+S.curday+" leeren?")){S.byday[S.curday]={};renderDrinks();compute();}});
$("#resetAll").addEventListener("click",()=>{if(confirm("Wirklich alles zurücksetzen (alle Tage, Preise + Einstellungen)?")){
try{localStorage.removeItem(KEY);}catch(e){}
S={pkg:"fun",priceByPkg:{fun:159,allin:279,kfun:97,kallin:125},persons:2,days:7,curday:1,byday:{},kid:{},priceOv:{},editPrices:false,custom:[]};
$("#editPrices").checked=false;$("#priceResetBar").style.display="none";initForm();renderDrinks();renderKid();compute();}});
$("#expPdf").addEventListener("click",exportPdf);
$("#expImg").addEventListener("click",exportImage);
/* ---- Abrechnung als Canvas rendern (offline) ---- */
function buildReceipt(){
const L=LAST,W=760,pad=44,dpr=2;
const y0=142, step=40, chartTop=356, ch=120;
const barTop=chartTop+18, labelY=barTop+ch+15, footY=labelY+34, H=footY+18;
const cv=document.createElement("canvas");
cv.width=W*dpr;cv.height=H*dpr;const g=cv.getContext("2d");g.scale(dpr,dpr);
g.textBaseline="alphabetic";
g.fillStyle="#eef3f6";g.fillRect(0,0,W,H);
// header bar
g.fillStyle="#0b3a54";g.fillRect(0,0,W,96);
// sauberes Martini-Logo
const lx=pad,ly=30;
g.fillStyle="#e2673d";
g.beginPath();g.moveTo(lx,ly);g.lineTo(lx+30,ly);g.lineTo(lx+15,ly+18);g.closePath();g.fill(); // Kelch
g.fillRect(lx+13.5,ly+18,3,15); // Stiel
g.fillRect(lx+5,ly+33,20,3); // Fuß
g.beginPath();g.arc(lx+24,ly+3,2.6,0,7);g.fillStyle="#9fd0e6";g.fill(); // Olive
// Titel
g.fillStyle="#ffffff";g.font="700 23px "+FF();g.fillText("Getränkepaket-Abrechnung",lx+48,42);
g.fillStyle="#bcd4e2";g.font="13px "+FF();
g.fillText(PKGNAME[L.pkg]+" · "+L.persons+"× · Tag "+L.curday+" von "+L.days+" · "+new Date().toLocaleDateString("de-DE"),lx+48,67);
// Zeilen
let y=y0;
const line=(lab,val,col,sub)=>{
g.fillStyle="#33454f";g.font="15px "+FF();g.fillText(lab,pad,y);
g.fillStyle=col||"#151b1f";g.font="700 19px "+MF();g.textAlign="right";g.fillText(val,W-pad,y);g.textAlign="left";
if(sub){g.fillStyle="#8a9aa4";g.font="12px "+FF();g.fillText(sub,pad,y+16);}
g.strokeStyle="#dbe5eb";g.beginPath();g.moveTo(pad,y+ (sub?24:11));g.lineTo(W-pad,y+(sub?24:11));g.stroke();
y+=step+(sub?12:0);};
const done=L.remaining<=0.001;
line("Paketkosten gesamt",eur(L.pkgTotal),"#0b3a54");
line("Angerechneter Wert (im Paket)",eur(L.consumed),"#12556f");
line(done?"Break-even erreicht Plus":"Noch bis Break-even",done?"+"+eur(-L.remaining):eur(L.remaining),done?"#1f9257":"#c2352a","Break-even pro Tag: "+eur(L.perDay));
line("Zusatzkosten (nicht im Paket)",eur(L.extra),"#c9542e");
line("Gratis-Bonus Kind unter 4",eur(L.kidVal),"#1f9257");
// Chart
const cx=pad,cw=W-2*pad;
g.fillStyle="#5f7480";g.font="12px "+FF();g.fillText("Angerechneter Wert je Tag · Ziel "+eur(L.perDay)+"/Tag",cx,chartTop+4);
const scale=Math.max(L.perDay*1.25,Math.max.apply(null,L.vals.concat([1]))*1.1,1);
const n=L.vals.length,slot=cw/n,bw=Math.min(28,slot-6);
const ty=barTop+ch-(L.perDay/scale*ch);
g.strokeStyle="#e2673d";g.setLineDash([5,4]);g.beginPath();g.moveTo(cx,ty);g.lineTo(cx+cw,ty);g.stroke();g.setLineDash([]);
for(let i=0;i<n;i++){const v=L.vals[i],bh=Math.max(v/scale*ch,v>0?3:0),bx=cx+i*slot+(slot-bw)/2,by=barTop+ch-bh;
g.fillStyle=(i+1===L.bestDay&&v>0)?"#1f9257":(v>=L.perDay&&v>0?"#2e8f5c":"#12556f");
g.fillRect(bx,by,bw,bh);
g.fillStyle="#9fb3bf";g.font="10px "+MF();g.textAlign="center";g.fillText(i+1,bx+bw/2,barTop+ch+14);g.textAlign="left";}
// Footer
g.fillStyle="#5f7480";g.font="12px "+FF();
g.fillText("aidadrinks.heidrich-digital.de · Preisstand AIDA-Barkarte 01.11.2025 · inoffizieller Helfer",pad,footY);
return{cv,W,H};
}
function download(blob,name){const u=URL.createObjectURL(blob),a=document.createElement("a");
a.href=u;a.download=name;a.click();setTimeout(()=>URL.revokeObjectURL(u),4000);}
function exportImage(){const{cv}=buildReceipt();cv.toBlob(b=>download(b,"Getraenkepaket-Abrechnung.jpg"),"image/jpeg",0.92);}
/* echtes PDF ohne Druckdialog: JPG in minimales 1-Seiten-PDF verpacken (offline, keine Library) */
function exportPdf(){
const{cv,W,H}=buildReceipt();
cv.toBlob(async b=>{
const jpg=new Uint8Array(await b.arrayBuffer());
download(jpgToPdf(jpg,W,H),"Getraenkepaket-Abrechnung.pdf");
},"image/jpeg",0.92);
}
function jpgToPdf(jpg,w,h){
w=Math.round(w);h=Math.round(h);
const enc=new TextEncoder(),chunks=[];let len=0;const off={};
const push=u=>{chunks.push(u);len+=u.length;};
const s=t=>push(enc.encode(t));
const start=n=>{off[n]=len;s(n+" 0 obj\n");};
s("%PDF-1.3\n");
start(1);s("<< /Type /Catalog /Pages 2 0 R >>\nendobj\n");
start(2);s("<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n");
start(3);s("<< /Type /Page /Parent 2 0 R /MediaBox [0 0 "+w+" "+h+"] /Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>\nendobj\n");
start(4);s("<< /Type /XObject /Subtype /Image /Width "+(w*2)+" /Height "+(h*2)+" /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length "+jpg.length+" >>\nstream\n");
push(jpg);s("\nendstream\nendobj\n");
const content="q "+w+" 0 0 "+h+" 0 0 cm /Im0 Do Q";
start(5);s("<< /Length "+content.length+" >>\nstream\n"+content+"\nendstream\nendobj\n");
const xref=len;let x="xref\n0 6\n0000000000 65535 f \n";
for(let i=1;i<=5;i++)x+=String(off[i]).padStart(10,"0")+" 00000 n \n";
s(x);s("trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n"+xref+"\n%%EOF");
const out=new Uint8Array(len);let o=0;chunks.forEach(c=>{out.set(c,o);o+=c.length;});
return new Blob([out],{type:"application/pdf"});
}
function FF(){return '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif';}
function MF(){return 'ui-monospace,Menlo,Consolas,monospace';}
$("#rules").innerHTML=[
"<b>Kabinen-Regel:</b> Innerhalb einer Kabine muss jede Altersgruppe dasselbe Paket haben alle Erwachsenen dasselbe (FUN oder ALL IN), alle Kinder/Teens dasselbe Kids-Paket. Erwachsene und Kinder dürfen sich unterscheiden.",
"<b>Mythos aufgeklärt:</b> Nein, es müssen <b>nicht alle Mitreisenden</b> ein Paket buchen nur alle derselben Altersgruppe in <b>derselben Kabine</b>. Andere Kabinen (z.B. mitreisende Verwandte in eigener Kabine) entscheiden frei.",
"<b>Personengebunden:</b> Paket gilt laut AGB nur für Eigenverzehr, 1 Getränk pro Bestellung, Bordkarte vorzeigen. Jemandem ohne Paket etwas »ausgeben« ist offiziell nicht vorgesehen. Regelkonforme Ausnahme: Kleinkinder unter 4 (siehe oben).",
"<b>Buchbarkeit &amp; Dauer:</b> ALL IN erst ab 5 Reisetagen; FUN &amp; Kids-Pakete ab 1 Tag. Gilt für die ganze Reise (5, 7, 11, 14 … Tage) keine tageweise Buchung, keine rückwirkende Umbuchung.",
"<b>Nicht enthalten:</b> Minibar, Automaten, Spa-Getränke, Flaschen-/Dosenwasser, Starbucks, selbst gebraute Biere, Champagner &amp; Flaschenweine.",
"<b>Preis:</b> je nach Reise, Zeitraum &amp; Fahrtgebiet unterschiedlich nur myAIDA zeigt deinen echten Preis. Deshalb hier frei eingebbar; auch einzelne Getränkepreise kannst du anpassen.",
"<i>Datenstand Barkarte/AGB: 01.11.2025, geprüft Juli 2026. Auswahl kann je Fahrtgebiet/Bar leicht abweichen. Kids-Paket-Details in myAIDA gegenprüfen.</i>"
].map(t=>"<p>"+t+"</p>").join("");
function initForm(){
$("#price").value=S.priceByPkg[S.pkg];$("#persons").value=S.persons;$("#days").value=S.days;$("#curday").value=S.curday;
$("#editPrices").checked=!!S.editPrices;$("#priceResetBar").style.display=S.editPrices?"flex":"none";
[...$("#pkgPick").children].forEach(x=>x.classList.toggle("active",x.getAttribute("data-pkg")===S.pkg));}
initForm();renderDrinks();renderKid();compute();
</script>
</body>
</html>