Files
aidadrinks/index.html
T

530 lines
32 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, 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}
h2 .tag{font-size:10.5px;letter-spacing:.3px;color:var(--mut);text-transform:none;font-weight:500}
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,.28);border-radius:8px;overflow:hidden;margin:12px 0 2px}
#status .bar{background:rgba(255,255,255,.25)}
.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 */
.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}
.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}
.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}
.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)}
.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)}
.drink.dim{opacity:.5}
.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.warned{color:var(--red);border-color:#eec4c0}
.dayband{font-size:12px;color:var(--sea);background:var(--sky);border-radius:10px;padding:8px 11px;margin:-2px 0 6px;font-weight:600}
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}
</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>
<div class="card">
<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" 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">Der Paketpreis <b>variiert je nach Reise, Zeitraum &amp; Fahrtgebiet</b> die 159&nbsp;€ sind nur ein Vorschlag. Trag deinen echten myAIDA-Preis ein; der zählt.</p>
</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>
<div class="card" id="panel"></div>
<div class="card">
<h2>Getränke wählen</h2>
<div class="dayband" id="dayband">Du erfasst gerade <b>Tag 1</b>. Tippe <b>+</b> für jedes Getränk.</div>
<p class="hint" style="margin-top:0">Grün = im Paket (zählt zum Break-even). Orange = nicht im Paket → wird <b>on top</b> als Zusatzkosten berechnet. Alles wird lokal gespeichert und läuft offline.</p>
<div id="drinks"></div>
<div class="btnbar">
<button id="nextDay">Tag abschließen → nächster Tag</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. Bei euch: Felix.</p>
<div id="kidDrinks"></div>
<div class="warn info" id="kidLine" style="margin-bottom:0">Noch nichts fürs Kind erfasst.</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"};
const DRINKS=[];
CAT.forEach((c,ci)=>c[1].forEach((d,di)=>DRINKS.push(
{id:"d"+ci+"_"+di,name:d[0],size:d[1],price:d[2],tag:d[3],alc:!!d[4],cat:c[0]})));
const PRICE={}; DRINKS.forEach(d=>PRICE[d.id]={price:d.price,tag:d.tag,alc:d.alc});
/* ---- state (Konsum je Reisetag) ---- */
const KEY="aidaBreakeven_v3";
let S={pkg:"fun",price:159,persons:2,days:7,curday:1,byday:{},kid:{}};
try{const r=localStorage.getItem(KEY);if(r)S=Object.assign(S,JSON.parse(r));}catch(e){}
function save(){try{localStorage.setItem(KEY,JSON.stringify(S));}catch(e){}}
function today(){return S.byday[S.curday]||(S.byday[S.curday]={});}
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;};
/* value of a counts-object */
function valOf(counts){let incl=0,extra=0,alcV=0,alcN=0;
for(const id in counts){const q=counts[id],p=PRICE[id];if(!p)continue;
if(inPkg(p.tag,S.pkg)){incl+=p.price*q;if(p.alc){alcV+=p.price*q;alcN+=q;}}else extra+=p.price*q;}
return{incl,extra,alcV,alcN};}
function totals(){let incl=0,extra=0,alcV=0,alcN=0;
for(const d in S.byday){const v=valOf(S.byday[d]);incl+=v.incl;extra+=v.extra;alcV+=v.alcV;alcN+=v.alcN;}
return{incl,extra,alcV,alcN};}
/* ---- render drinks (edit current day) ---- */
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);
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(d[2])+'</span>');
if(incl&&alc)badge+=' <span class="badge b-alc">Alkohol</span>';
const meta=[d[1],eur(d[2])].filter(Boolean).join(" · ");
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">'+meta+'</div></div>'+
'<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>';
wrap.appendChild(row);
});host.appendChild(wrap);
});
}
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);});
}
/* ---- chart ---- */
function renderChart(perDay){
const host=$("#chart");host.innerHTML="";
const vals=[];for(let d=1;d<=S.days;d++)vals.push(valOf(S.byday[d]||{}).incl);
const scale=Math.max(perDay*1.25,Math.max.apply(null,vals.concat([1]))*1.1,1);
// target line
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";
const b=document.createElement("div");b.className="b";
const future=d>S.curday&&v===0;
if(future)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 + render ---- */
function compute(){
S.price=num($("#price").value,0);
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());
const remaining=pkgTotal-T.incl;
/* overall status */
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)+"%";
/* today */
const tcard=$("#todayCard");
$("#todayTag").textContent="Tag "+S.curday;
const tpct=Math.min(100,perDay>0?tv.incl/perDay*100:0);
$("#tbar").style.width=tpct+"%";
tcard.classList.toggle("done",tv.incl>=perDay&&perDay>0);
if(tv.incl>=perDay&&perDay>0){
$("#todayMsg").innerHTML="Für heute ausgereizt "+eur(tv.incl)+" von "+eur(perDay)+
"<small>Tagesziel geknackt. Alles Weitere heute ist reiner Gewinn.</small>";
}else{
$("#todayMsg").innerHTML="Noch nicht ausgereizt für heute"+
"<small>"+eur(tv.incl)+" von "+eur(perDay)+" noch <b>"+eur(perDay-tv.incl)+"</b> bis dein Paket sich für heute rechnet.</small>";
}
$("#todayExtra").innerHTML=tv.extra>0?"Heute zusätzlich außerhalb des Pakets: <b>"+eur(tv.extra)+"</b>":"";
/* overall stats */
$("#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, onTrack=proj>=pkgTotal;
pj.style.display="block";pj.className="warn "+(onTrack?"info":"amber");
pj.innerHTML="<b>Hochrechnung</b>Dein Schnitt: "+eur(paceDay)+"/Tag → bis Reiseende ca. <b>"+eur(proj)+"</b> von "+eur(pkgTotal)+". "+
(onTrack?"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(perDay);
/* panel: alcohol (allin) / value tips */
const p=$("#panel");
if(S.pkg==="allin"){
const alcPerDay=T.alcN/S.curday, needAlc=perDay/8.5;
let body='<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){body+='<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{body+='<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=body;
}else{
const incl=DRINKS.filter(d=>inPkg(d.tag,S.pkg));
const top=incl.slice().sort((a,b)=>b.price-a.price)
.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(d.price)+'</span>').join('')+'</div>';
h+='<div class="warn info" style="margin:10px 0 0">Tipp: Kaffeespezialitäten ('+eur(4.1)+') zählen im FUN voll mit Latte zum Frühstück, Cappuccino nachmittags, Mocktail abends bringen dich Richtung Break-even, ganz ohne Alkohol.</div>';
p.innerHTML=h;
}
/* kid bonus */
let kidVal=0,kidN=0;KID.forEach((d,i)=>{const q=S.kid["k"+i]||0;kidVal+=d[1]*q;kidN+=q;});
const kl=$("#kidLine");
kl.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.";
$("#dayband").innerHTML="Du erfasst gerade <b>Tag "+S.curday+"</b> von "+S.days+". Tippe <b>+</b> für jedes Getränk.";
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();});
$("#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");
[...$("#pkgPick").children].forEach(x=>x.classList.toggle("active",x===b));
$("#priceHint").innerHTML=S.pkg==="allin"
?"ALL IN ist ab 5 Reisetagen buchbar und in den ALL-IN-Tarifen ab 25 J. im Reisepreis. Preis variiert je Reise trag deinen echten myAIDA-Preis ein."
:"Der Paketpreis <b>variiert je nach Reise, Zeitraum &amp; Fahrtgebiet</b> die Vorgabe ist nur ein Vorschlag. Trag deinen echten myAIDA-Preis ein.";
renderDrinks();compute();});
["price","persons","days","curday"].forEach(f=>$("#"+f).addEventListener("input",()=>{renderDrinks();compute();}));
$("#nextDay").addEventListener("click",()=>{
if(S.curday<S.days){S.curday++;$("#curday").value=S.curday;renderDrinks();compute();
window.scrollTo({top:0,behavior:"smooth"});}
else alert("Das ist bereits dein letzter Reisetag ("+S.days+").");});
$("#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 + Einstellungen)?")){
try{localStorage.removeItem(KEY);}catch(e){}
S={pkg:"fun",price:159,persons:2,days:7,curday:1,byday:{},kid:{}};initForm();renderDrinks();renderKid();compute();}});
$("#rules").innerHTML=[
"<b>Kabinen-Regel:</b> Alle Reisenden ab 16 J. in einer Kabine müssen dasselbe Paket buchen (FUN) bzw. alle Volljährigen dasselbe (ALL IN). Deshalb haben Lea &amp; du beide FUN Tante Astrid in eigener Kabine ist frei.",
"<b>Mythos aufgeklärt:</b> Nein, es müssen <b>nicht alle Mitreisenden</b> ein Paket buchen nur alle ab 16 J. in <b>derselben Kabine</b>. Andere Kabinen 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:</b> ALL IN erst ab 5 Reisetagen; FUN &amp; Kids-Pakete ab 1 Tag. 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 ist er hier frei eingebbar.",
"<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.price;$("#persons").value=S.persons;$("#days").value=S.days;$("#curday").value=S.curday;
[...$("#pkgPick").children].forEach(x=>x.classList.toggle("active",x.getAttribute("data-pkg")===S.pkg));}
initForm();renderDrinks();renderKid();compute();
</script>
</body>
</html>