Files

1696 lines
59 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.0">
<meta name="color-scheme" content="light only">
<meta name="supported-color-schemes" content="light">
<title>Der letzte Auftrag JGA Abenteuer</title>
<link href="https://fonts.googleapis.com/css2?family=MedievalSharp&family=Cinzel+Decorative:wght@700&family=Cinzel:wght@400;600&family=Crimson+Pro:ital,wght@0,400;0,600;1,400&display=swap" rel="stylesheet">
<style>
:root {
--parchment: #f4e8c1;
--parchment-dark: #e8d5a0;
--ink: #1a0f00;
--ink-light: #3d2200;
--red: #8b1a1a;
--gold: #c9a227;
--gold-light: #f0c94a;
--green: #1a4a1a;
--blue: #1a2a5a;
--purple: #4a1a6a;
--shadow: rgba(0,0,0,0.4);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
/* Force light mode verhindert iOS/Koder Dark Mode Override */
:root { color-scheme: light only; }
@media (prefers-color-scheme: dark) {
body { background: #0d0800 !important; }
.parchment { background: #f4e8c1 !important; }
.parchment p, .parchment li, .parchment div { color: #3d2200 !important; }
.quest-card { background: #e8d5a0 !important; }
.rules-box { background: rgba(138,101,32,0.15) !important; }
input, textarea { background: #f4e8c1 !important; color: #1a0f00 !important; }
}
body {
background: #0d0800;
font-family: 'Crimson Pro', Georgia, serif;
color: #1a0f00;
min-height: 100vh;
background-image:
radial-gradient(ellipse at 50% 0%, #2a1500 0%, #0d0800 60%);
}
/* ── COVER ─────────────────────────────────── */
#cover {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 2rem;
position: relative;
overflow: hidden;
}
#cover::before {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(ellipse at 50% 40%, rgba(201,162,39,0.15) 0%, transparent 70%);
pointer-events: none;
}
.stars {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.star {
position: absolute;
width: 2px; height: 2px;
background: #fff;
border-radius: 50%;
animation: twinkle var(--d, 3s) ease-in-out infinite var(--delay, 0s);
opacity: 0;
}
@keyframes twinkle {
0%,100% { opacity: 0; }
50% { opacity: var(--bright, 0.8); }
}
.cover-emblem {
font-size: 5rem;
margin-bottom: 1rem;
filter: drop-shadow(0 0 20px rgba(201,162,39,0.6));
animation: float 4s ease-in-out infinite;
}
@keyframes float {
0%,100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.cover-subtitle {
font-family: 'Cinzel', serif;
font-size: 0.9rem;
letter-spacing: 0.4em;
color: var(--gold);
text-transform: uppercase;
margin-bottom: 0.8rem;
}
.cover-title {
font-family: 'Cinzel Decorative', serif;
font-size: clamp(2rem, 6vw, 4.5rem);
color: var(--gold-light);
text-shadow:
0 0 30px rgba(201,162,39,0.8),
2px 2px 0 #5a3a00,
4px 4px 0 #3a2000;
line-height: 1.15;
margin-bottom: 1.5rem;
}
.cover-divider {
width: 300px;
height: 2px;
background: linear-gradient(90deg, transparent, var(--gold), transparent);
margin: 1.5rem auto;
position: relative;
}
.cover-divider::before, .cover-divider::after {
content: '◆';
position: absolute;
top: 50%;
transform: translateY(-50%);
color: var(--gold);
font-size: 1rem;
}
.cover-divider::before { left: -10px; }
.cover-divider::after { right: -10px; }
.cover-desc {
color: #c8a86a;
font-size: 1.15rem;
font-style: italic;
max-width: 500px;
line-height: 1.7;
margin-bottom: 2.5rem;
}
.btn-primary {
font-family: 'Cinzel', serif;
font-size: 1rem;
letter-spacing: 0.15em;
color: var(--ink);
background: linear-gradient(135deg, var(--gold-light), var(--gold));
border: none;
padding: 0.9rem 2.5rem;
cursor: pointer;
clip-path: polygon(8px 0%, 100% 0%, calc(100% - 8px) 100%, 0% 100%);
transition: all 0.2s;
font-weight: 600;
box-shadow: 0 4px 15px rgba(201,162,39,0.4);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 25px rgba(201,162,39,0.6);
background: linear-gradient(135deg, #ffe070, var(--gold-light));
}
/* ── MAIN APP ───────────────────────────────── */
#app { display: none; }
.page-wrap {
max-width: 900px;
margin: 0 auto;
padding: 2rem 1rem 4rem;
background: #f0e4b8;
min-height: 100vh;
}
/* ── NAV ───────────────────────────────────── */
.top-nav {
background: linear-gradient(90deg, #1a0800, #2d1500, #1a0800);
border-bottom: 2px solid var(--gold);
padding: 0.8rem 1.5rem;
display: flex;
align-items: center;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 100;
flex-wrap: wrap;
gap: 0.5rem;
}
.nav-title {
font-family: 'Cinzel', serif;
color: var(--gold);
font-size: 1rem;
letter-spacing: 0.1em;
}
.nav-tabs {
display: flex;
gap: 0.3rem;
flex-wrap: wrap;
}
.nav-tab {
font-family: 'Cinzel', serif;
font-size: 0.75rem;
letter-spacing: 0.08em;
color: #a07840;
background: transparent;
border: 1px solid #4a2800;
padding: 0.35rem 0.8rem;
cursor: pointer;
transition: all 0.2s;
}
.nav-tab:hover, .nav-tab.active {
color: var(--gold-light);
border-color: var(--gold);
background: rgba(201,162,39,0.1);
}
/* ── PARCHMENT SECTIONS ────────────────────── */
.parchment {
background: #f4e8c1 !important;
border: 3px solid #8a6520;
border-radius: 4px;
padding: 2rem;
margin-bottom: 2rem;
box-shadow:
0 0 0 1px #c9a227,
0 8px 30px rgba(0,0,0,0.5),
inset 0 1px 0 rgba(255,255,255,0.3);
position: relative;
}
.parchment::before {
content: '';
position: absolute;
inset: 6px;
border: 1px solid rgba(138,101,32,0.3);
pointer-events: none;
}
.section-title {
font-family: 'Cinzel Decorative', serif;
font-size: 1.4rem;
color: var(--red);
text-align: center;
margin-bottom: 0.5rem;
text-shadow: 1px 1px 0 rgba(0,0,0,0.1);
}
.section-subtitle {
font-family: 'Cinzel', serif;
font-size: 0.8rem;
letter-spacing: 0.2em;
color: #8a6520;
text-align: center;
text-transform: uppercase;
margin-bottom: 1.5rem;
}
.ornament {
text-align: center;
color: var(--gold);
font-size: 1.2rem;
letter-spacing: 0.5em;
margin: 1rem 0;
opacity: 0.7;
}
p {
font-size: 1.05rem;
line-height: 1.8;
margin-bottom: 0.8rem;
color: var(--ink-light);
}
.flavor-text {
font-style: italic;
background: rgba(138,101,32,0.08);
border-left: 3px solid var(--gold);
padding: 0.8rem 1rem;
margin: 1rem 0;
border-radius: 0 4px 4px 0;
font-size: 1rem;
}
/* ── QUEST CARDS ───────────────────────────── */
.quest-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.2rem;
margin-top: 1.5rem;
}
.quest-card {
background: #e8d5a0;
border: 2px solid #8a6520;
border-radius: 4px;
padding: 1.2rem;
position: relative;
overflow: hidden;
transition: transform 0.2s, box-shadow 0.2s;
cursor: pointer;
}
.quest-card:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0,0,0,0.3);
}
.quest-card.done {
opacity: 0.6;
filter: saturate(0.4);
}
.quest-card.done::after {
content: '✓ ERLEDIGT';
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%,-50%) rotate(-15deg);
font-family: 'Cinzel', serif;
font-size: 1.5rem;
color: var(--green);
border: 3px solid var(--green);
padding: 0.3rem 0.8rem;
opacity: 0.8;
white-space: nowrap;
}
.quest-badge {
display: inline-block;
font-family: 'Cinzel', serif;
font-size: 0.65rem;
letter-spacing: 0.1em;
padding: 0.2rem 0.6rem;
border-radius: 2px;
margin-bottom: 0.5rem;
font-weight: 600;
}
.badge-easy { background: #2a5a1a; color: #a8ff80; }
.badge-medium { background: #5a4a00; color: #ffd060; }
.badge-hard { background: #5a1a00; color: #ff8060; }
.badge-tabletop { background: #2a1a5a; color: #a080ff; }
.quest-name {
font-family: 'Cinzel', serif;
font-size: 1rem;
color: var(--red);
margin-bottom: 0.4rem;
font-weight: 600;
}
.quest-holder {
font-size: 0.85rem;
color: #5a3a10;
margin-bottom: 0.5rem;
}
.quest-holder strong { color: var(--ink); }
.quest-desc {
font-size: 0.9rem;
line-height: 1.5;
color: var(--ink-light);
margin-bottom: 0.8rem;
}
.quest-rewards {
display: flex;
justify-content: space-between;
font-size: 0.8rem;
border-top: 1px solid #c9a22760;
padding-top: 0.6rem;
margin-top: 0.5rem;
}
.reward-success {
color: var(--green);
font-weight: 600;
}
.reward-fail {
color: var(--red);
font-weight: 600;
}
.quest-expand {
display: none;
margin-top: 0.8rem;
padding-top: 0.8rem;
border-top: 1px dashed #c9a22780;
font-size: 0.9rem;
line-height: 1.6;
color: var(--ink);
}
.quest-card.expanded .quest-expand { display: block; }
.expand-hint {
font-family: 'Cinzel', serif;
font-size: 0.7rem;
color: #8a6520;
text-align: right;
margin-top: 0.5rem;
}
/* ── INVENTORY TRACKER ─────────────────────── */
.inventory-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.item-slot {
border: 2px dashed #8a6520;
border-radius: 4px;
padding: 1rem;
text-align: center;
min-height: 100px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.4rem;
transition: all 0.3s;
cursor: pointer;
position: relative;
background: rgba(138,101,32,0.05);
}
.item-slot.collected {
border: 2px solid var(--gold);
background: rgba(201,162,39,0.1);
box-shadow: 0 0 10px rgba(201,162,39,0.3);
}
.item-emoji { font-size: 2.5rem; }
.item-name {
font-family: 'Cinzel', serif;
font-size: 0.75rem;
color: var(--ink-light);
line-height: 1.3;
}
.item-slot.collected .item-name { color: var(--gold); }
.item-check {
position: absolute;
top: 5px; right: 8px;
font-size: 1rem;
color: var(--gold-light);
opacity: 0;
transition: opacity 0.3s;
}
.item-slot.collected .item-check { opacity: 1; }
/* ── DRINKS TRACKER ────────────────────────── */
.drink-tracker {
text-align: center;
padding: 1rem;
}
.drink-count {
font-family: 'Cinzel Decorative', serif;
font-size: 4rem;
color: var(--red);
text-shadow: 2px 2px 0 rgba(0,0,0,0.2);
line-height: 1;
}
.drink-label {
font-family: 'Cinzel', serif;
font-size: 0.8rem;
letter-spacing: 0.3em;
color: #8a6520;
margin-top: 0.3rem;
text-transform: uppercase;
}
.drink-btns {
display: flex;
gap: 1rem;
justify-content: center;
margin-top: 1.2rem;
}
.btn-drink {
font-family: 'Cinzel', serif;
font-size: 1.1rem;
border: 2px solid;
padding: 0.5rem 1.5rem;
cursor: pointer;
transition: all 0.2s;
background: transparent;
border-radius: 3px;
}
.btn-drink.add { border-color: var(--red); color: var(--red); }
.btn-drink.add:hover { background: var(--red); color: #fff; }
.btn-drink.sub { border-color: #8a6520; color: #8a6520; }
.btn-drink.sub:hover { background: #8a6520; color: #fff; }
/* ── TABLETOP SECTION ──────────────────────── */
.encounter-box {
background: #0d0500;
border: 2px solid var(--gold);
border-radius: 4px;
padding: 1.5rem;
margin: 1rem 0;
color: #e8d5a0;
position: relative;
overflow: hidden;
}
.encounter-box::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse at 50% 0%, rgba(201,162,39,0.08), transparent 60%);
pointer-events: none;
}
.encounter-title {
font-family: 'Cinzel', serif;
font-size: 1.1rem;
color: var(--gold-light);
margin-bottom: 0.8rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.encounter-box p { color: #c8a86a; }
.encounter-box .flavor-text {
border-left-color: var(--gold);
background: rgba(201,162,39,0.05);
color: #e8d5a0;
}
.stat-block {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.5rem;
margin: 1rem 0;
background: #1a0800;
border: 1px solid #4a2800;
padding: 0.8rem;
border-radius: 4px;
}
.stat-item {
text-align: center;
font-family: 'Cinzel', serif;
}
.stat-val {
font-size: 1.4rem;
color: var(--gold-light);
}
.stat-name {
font-size: 0.6rem;
letter-spacing: 0.15em;
color: #8a6520;
text-transform: uppercase;
}
/* ── DICE ROLLER ───────────────────────────── */
.dice-roller {
background: #1a0800;
border: 1px solid #4a2800;
border-radius: 4px;
padding: 1rem;
margin-top: 1rem;
}
.dice-roller-title {
font-family: 'Cinzel', serif;
font-size: 0.8rem;
letter-spacing: 0.2em;
color: var(--gold);
text-transform: uppercase;
margin-bottom: 0.8rem;
}
.dice-btns {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 0.8rem;
}
.dice-btn {
font-family: 'Cinzel', serif;
font-size: 0.85rem;
color: #c8a86a;
background: #2a1000;
border: 1px solid #4a2800;
padding: 0.4rem 0.8rem;
cursor: pointer;
border-radius: 3px;
transition: all 0.2s;
}
.dice-btn:hover {
border-color: var(--gold);
color: var(--gold-light);
background: #3a1800;
}
.dice-result {
font-family: 'Cinzel Decorative', serif;
font-size: 2.5rem;
color: var(--gold-light);
text-align: center;
min-height: 3rem;
display: flex;
align-items: center;
justify-content: center;
text-shadow: 0 0 15px rgba(201,162,39,0.5);
transition: all 0.3s;
}
.dice-result.rolling {
animation: diceRoll 0.4s ease-out;
}
@keyframes diceRoll {
0%,20%,40%,60%,80% { transform: rotate(-5deg) scale(1.1); }
10%,30%,50%,70%,90% { transform: rotate(5deg) scale(1.1); }
100% { transform: none; }
}
.dice-log {
font-size: 0.8rem;
color: #5a3a10;
min-height: 1.2rem;
text-align: center;
font-style: italic;
}
/* ── TIMELINE ──────────────────────────────── */
.timeline {
position: relative;
padding-left: 2rem;
margin-top: 1rem;
}
.timeline::before {
content: '';
position: absolute;
left: 0.5rem;
top: 0; bottom: 0;
width: 2px;
background: linear-gradient(180deg, var(--gold), var(--gold) 80%, transparent);
}
.timeline-item {
position: relative;
margin-bottom: 1.5rem;
}
.timeline-item::before {
content: '◆';
position: absolute;
left: -1.7rem;
color: var(--gold);
font-size: 0.8rem;
top: 0.1rem;
}
.timeline-time {
font-family: 'Cinzel', serif;
font-size: 0.7rem;
letter-spacing: 0.15em;
color: var(--gold);
text-transform: uppercase;
margin-bottom: 0.2rem;
}
.timeline-event {
font-size: 1rem;
color: var(--ink-light);
font-weight: 600;
}
.timeline-note {
font-size: 0.9rem;
color: #5a3a10;
font-style: italic;
}
/* ── RULES BOX ─────────────────────────────── */
.rules-box {
background: rgba(138,101,32,0.08);
border: 1px solid #c9a22740;
border-radius: 4px;
padding: 1rem 1.2rem;
margin: 0.8rem 0;
}
.rules-title {
font-family: 'Cinzel', serif;
font-size: 0.8rem;
letter-spacing: 0.15em;
color: var(--red);
text-transform: uppercase;
margin-bottom: 0.5rem;
}
.rules-list {
list-style: none;
padding: 0;
}
.rules-list li {
font-size: 0.95rem;
padding: 0.25rem 0 0.25rem 1.2rem;
position: relative;
color: var(--ink-light);
}
.rules-list li::before {
content: '⚔';
position: absolute;
left: 0;
font-size: 0.75rem;
color: var(--gold);
top: 0.35rem;
}
/* ── TABS ──────────────────────────────────── */
.tab-content { display: none; }
.tab-content.active { display: block; }
/* ── SPIELER ───────────────────────────────── */
.spieler-add-row {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-bottom: 1.2rem;
}
.spieler-input {
font-family: 'Crimson Pro', serif;
font-size: 1rem;
background: #fdf5dc;
border: 1px solid #8a6520;
border-radius: 3px;
padding: 0.5rem 0.8rem;
color: #1a0f00;
outline: none;
flex: 1;
min-width: 120px;
}
.spieler-input:focus { border-color: #c9a227; box-shadow: 0 0 0 2px rgba(201,162,39,0.2); }
.spieler-select {
font-family: 'Crimson Pro', serif;
font-size: 0.95rem;
background: #fdf5dc;
border: 1px solid #8a6520;
border-radius: 3px;
padding: 0.5rem 0.6rem;
color: #1a0f00;
cursor: pointer;
outline: none;
}
.btn-add {
font-family: 'Cinzel', serif;
font-size: 0.8rem;
letter-spacing: 0.1em;
background: #2a5a1a;
color: #a8ff80;
border: none;
padding: 0.5rem 1rem;
border-radius: 3px;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
}
.btn-add:hover { background: #1a4a0a; }
.spieler-list {
display: flex;
flex-direction: column;
gap: 0.6rem;
}
.spieler-item {
display: flex;
align-items: center;
gap: 0.7rem;
background: #fdf5dc;
border: 1px solid #c9a22760;
border-radius: 4px;
padding: 0.7rem 1rem;
flex-wrap: wrap;
}
.spieler-avatar {
width: 38px; height: 38px;
border-radius: 50%;
background: linear-gradient(135deg, #8a6520, #c9a227);
display: flex; align-items: center; justify-content: center;
font-size: 1.1rem;
flex-shrink: 0;
color: #fff;
font-weight: bold;
font-family: 'Cinzel', serif;
}
.spieler-info { flex: 1; min-width: 100px; }
.spieler-name-display {
font-family: 'Cinzel', serif;
font-size: 0.95rem;
color: #1a0f00;
font-weight: 600;
}
.spieler-rolle-display {
font-size: 0.8rem;
color: #5a3a10;
margin-top: 0.1rem;
font-style: italic;
}
.spieler-rolle-badge {
font-family: 'Cinzel', serif;
font-size: 0.65rem;
letter-spacing: 0.08em;
padding: 0.15rem 0.5rem;
border-radius: 2px;
white-space: nowrap;
flex-shrink: 0;
}
.rolle-held { background: #5a3a00; color: #ffd060; }
.rolle-hüter { background: #2a1a5a; color: #a080ff; }
.rolle-ratsmitglied { background: #4a1a1a; color: #ff9060; }
.rolle-einsteiger { background: #1a4a1a; color: #a8ff80; }
.rolle-spielleiter { background: #1a1a4a; color: #80c0ff; }
.rolle-gast { background: #3a3a1a; color: #e0e080; }
.spieler-quest-sel {
font-family: 'Crimson Pro', serif;
font-size: 0.8rem;
background: #f4e8c1;
border: 1px solid #8a652060;
border-radius: 3px;
padding: 0.25rem 0.4rem;
color: #3d2200;
cursor: pointer;
max-width: 160px;
}
.btn-del {
background: transparent;
border: 1px solid #8b1a1a60;
color: #8b1a1a;
border-radius: 3px;
padding: 0.25rem 0.6rem;
cursor: pointer;
font-size: 0.85rem;
transition: all 0.2s;
flex-shrink: 0;
}
.btn-del:hover { background: #8b1a1a; color: #fff; }
.spieler-empty {
text-align: center;
font-style: italic;
color: #8a6520;
padding: 2rem;
font-size: 0.95rem;
}
.spieler-summary {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #c9a22740;
}
.summary-chip {
font-family: 'Cinzel', serif;
font-size: 0.7rem;
letter-spacing: 0.08em;
padding: 0.2rem 0.6rem;
border-radius: 20px;
border: 1px solid #c9a22760;
color: #5a3a10;
}
/* ── PRINT HINT ────────────────────────────── */
.print-hint {
text-align: center;
font-family: 'Cinzel', serif;
font-size: 0.75rem;
letter-spacing: 0.15em;
color: #5a3a10;
padding: 1rem;
}
/* ── RESPONSIVE ────────────────────────────── */
@media (max-width: 600px) {
.cover-title { font-size: 1.8rem; }
.parchment { padding: 1.2rem; }
.quest-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<!-- ══════════════════ COVER ══════════════════ -->
<div id="cover">
<div class="stars" id="stars"></div>
<div class="cover-emblem">⚔️</div>
<div class="cover-subtitle">Ein Junggesellenabschied der Legenden</div>
<div class="cover-title">Der letzte<br>Auftrag</div>
<div class="cover-divider"></div>
<div class="cover-desc">
Eine Heldengeschichte, Trinkspiele & tabletop Quests <br>
für den tapfersten Krieger, der je eine Braut zu retten wagte.
</div>
<button class="btn-primary" onclick="startApp()">📜 Abenteuer beginnen</button>
</div>
<!-- ══════════════════ APP ══════════════════ -->
<div id="app">
<nav class="top-nav">
<span class="nav-title">⚔ Der letzte Auftrag</span>
<div class="nav-tabs">
<button class="nav-tab active" onclick="showTab('intro')">Intro</button>
<button class="nav-tab" onclick="showTab('quests')">Quests</button>
<button class="nav-tab" onclick="showTab('tabletop')">Tabletop</button>
<button class="nav-tab" onclick="showTab('tracker')">Tracker</button>
<button class="nav-tab" onclick="showTab('spieler')">Spieler</button>
<button class="nav-tab" onclick="showTab('ablauf')">Ablauf</button>
</div>
</nav>
<div class="page-wrap">
<!-- ═══ INTRO ═══ -->
<div id="tab-intro" class="tab-content active">
<div class="parchment">
<div class="section-title">Das Abenteuer</div>
<div class="section-subtitle">Für Spielleiter & Organisatoren</div>
<div class="ornament">✦ ✦ ✦</div>
<div class="flavor-text">
„Höre mich an, tapferer Held. Deine zukünftige Gemahlin wurde von den mächtigen Hütern ihrer Habseligkeiten gefangen gehalten. Acht Gegenstände von unschätzbarem Wert trägt sie bei sich oder vielmehr: tragen sie ANDERE für sie. Nur wer alle acht erbeuten kann, verdient es, sie zum Traualtar zu führen."
</div>
<p>Das Konzept ist simpel: <strong>Der Bräutigam ist der Held.</strong> Jeder Gast (oder eine Gruppe) hütet ein Besitztum der Braut. Durch Quests und Aufgaben erkämpft er sich die Gegenstände oder trinkt bei Misserfolg.</p>
<div class="rules-box">
<div class="rules-title">Grundregeln</div>
<ul class="rules-list">
<li>Der Held löst Aufgaben bei den verschiedenen Gästen (= Hüter).</li>
<li>Erfolg → Hüter übergibt seinen Gegenstand (oder einen Hinweis-Zettel).</li>
<li>Misserfolg → Der Held trinkt. Der Gegenstand bleibt beim Hüter.</li>
<li>Er kann es später nochmal versuchen mit erhöhtem Schwierigkeitsgrad.</li>
<li>Hat er am Ende ALLE 8 Gegenstände, darf er die Braut symbolisch freikaufen.</li>
<li>Fehlt noch etwas? Er trinkt für jeden fehlenden Gegenstand nochmal.</li>
<li>Einsteiger-Quests brauchen keine Vorkenntnisse. Der Tabletop-Block ist optional für RPG-Enthusiasten.</li>
</ul>
</div>
<div class="rules-box">
<div class="rules-title">Was braucht ihr?</div>
<ul class="rules-list">
<li>8 physische Gegenstände (Fotos, kleine Dinge oder Zettel mit Clue-Beschreibungen)</li>
<li>Diese App / Ausgedrucktes für den Spielleiter</li>
<li>Getränke in ausreichender Menge 🍺</li>
<li>Gute Laune & keine Scham</li>
<li>Für den Tabletop-Block: W6, W10 oder diese App als Würfel</li>
</ul>
</div>
<div class="ornament">✦ ✦ ✦</div>
<p style="text-align:center; font-style:italic; color: #5a3a10;">Der Held weiß am Anfang NUR, dass er Quests lösen muss.<br>Die Hüter kennen ihre Aufgabe, er nicht.</p>
</div>
<div class="parchment">
<div class="section-title">Die 8 Besitztümer</div>
<div class="section-subtitle">Für die Braut vorbereiten</div>
<p>Bereite diese 8 Gegenstände (oder Zettel mit ihrer Beschreibung) vor. Die Hüter bekommen sie vorab zugewiesen:</p>
<div class="inventory-grid">
<div class="item-slot" id="inv-0" onclick="toggleItem(0)">
<span class="item-emoji">💍</span>
<span class="item-name">Der Ring der Verbindung</span>
<span class="item-check"></span>
</div>
<div class="item-slot" id="inv-1" onclick="toggleItem(1)">
<span class="item-emoji">👠</span>
<span class="item-name">Der Schuh der Flucht</span>
<span class="item-check"></span>
</div>
<div class="item-slot" id="inv-2" onclick="toggleItem(2)">
<span class="item-emoji">💌</span>
<span class="item-name">Der Brief des ersten Dates</span>
<span class="item-check"></span>
</div>
<div class="item-slot" id="inv-3" onclick="toggleItem(3)">
<span class="item-emoji">🌹</span>
<span class="item-name">Die Blume der Ewigkeit</span>
<span class="item-check"></span>
</div>
<div class="item-slot" id="inv-4" onclick="toggleItem(4)">
<span class="item-emoji">🔑</span>
<span class="item-name">Der Schlüssel zur Zukunft</span>
<span class="item-check"></span>
</div>
<div class="item-slot" id="inv-5" onclick="toggleItem(5)">
<span class="item-emoji">📸</span>
<span class="item-name">Das Foto der gemeinsamen Zeit</span>
<span class="item-check"></span>
</div>
<div class="item-slot" id="inv-6" onclick="toggleItem(6)">
<span class="item-emoji">🧸</span>
<span class="item-name">Das Maskottchen des Heils</span>
<span class="item-check"></span>
</div>
<div class="item-slot" id="inv-7" onclick="toggleItem(7)">
<span class="item-emoji">🏆</span>
<span class="item-name">Die Urkunde der Heldentaten</span>
<span class="item-check"></span>
</div>
</div>
</div>
</div>
<!-- ═══ QUESTS ═══ -->
<div id="tab-quests" class="tab-content">
<div class="parchment">
<div class="section-title">Die Quests</div>
<div class="section-subtitle">Aufgaben der Hüter — Klicken zum Aufklappen</div>
<div class="rules-box">
<div class="rules-title">Farbcode</div>
<ul class="rules-list" style="columns:2; gap:1rem;">
<li><span class="quest-badge badge-easy">EINSTEIGER</span> Keine Vorkenntnisse</li>
<li><span class="quest-badge badge-medium">MITTEL</span> Etwas Geschick</li>
<li><span class="quest-badge badge-hard">HERAUSFORDERUNG</span> Echte Prüfung</li>
<li><span class="quest-badge badge-tabletop">TABLETOP</span> RPG-Erfahrene</li>
</ul>
</div>
<div class="quest-grid" id="questGrid"></div>
</div>
</div>
<!-- ═══ TABLETOP ═══ -->
<div id="tab-tabletop" class="tab-content">
<div class="parchment">
<div class="section-title">Tabletop-Block</div>
<div class="section-subtitle">Für erfahrene Rollenspieler · W6/W10-System</div>
<div class="flavor-text">
Dieser Block ist für den Moment gedacht, wenn die erfahrenen Rollenspieler in der Runde zusammenkommen. Eine kurze Kampf- und Entscheidungsszene, vollständig vorbereitet. Dauer: ca. 3060 Minuten.
</div>
<div class="encounter-box">
<div class="encounter-title">🐉 Szene I Der Wächter des Tores</div>
<div class="flavor-text">
„Der Held steht vor dem letzten Tor zur Festung der Braut. Der Wächter ein alter Vertrauter versperrt den Weg. Er verlangt den Beweis, dass der Held seiner Gemahlin würdig ist."
</div>
<p>Der Wächter stellt <strong>3 Fragen</strong> echte Fragen über die Beziehung (Spielleiter vorbereiten!). Für jede richtige Antwort: kein Würfelwurf nötig. Für jede falsche: Würfelduell.</p>
<div class="stat-block">
<div class="stat-item">
<div class="stat-val">W10</div>
<div class="stat-name">System</div>
</div>
<div class="stat-item">
<div class="stat-val">7+</div>
<div class="stat-name">Erfolg</div>
</div>
<div class="stat-item">
<div class="stat-val">3</div>
<div class="stat-name">Fragen</div>
</div>
</div>
<p style="font-size:0.9rem;"><strong>Beispielfragen:</strong> Wann hatten sie ihr erstes Date? Was ist ihr Lieblingsessen? Was war ihr erster gemeinsamer Urlaub?</p>
</div>
<div class="encounter-box">
<div class="encounter-title">⚔ Szene II Die Prüfung des Mutes</div>
<div class="flavor-text">
„Der Rat der Weisen (= die erfahrenen Rollenspieler) sitzt im Kreis. Jeder hat eine Herausforderung für den Held: einen Skill-Check, eine moralische Entscheidung oder eine Improvisation."
</div>
<p>Jeder RPG-erfahrene Spieler ist ein <strong>Ratsmitglied</strong> und beschreibt eine kurze Szene. Der Held muss reagieren (Roleplay + Würfelwurf). Das Ratsmitglied entscheidet, ob der Wurf ausreicht.</p>
<div class="rules-box" style="background: rgba(201,162,39,0.05); border-color: #4a2800;">
<div class="rules-title" style="color: var(--gold);">Beispiel-Szenen für Ratsmitglieder</div>
<ul class="rules-list">
<li>„Du stehst vor einem Drachen, der dich nach ihrem schlimmsten Eigenschaft fragt. Antworte weise." (Charisma-Check)</li>
<li>„Ein alter Rivale erscheint. Er behauptet, ihr erster Kuss war mit ihm." (Einschüchterungs-Check)</li>
<li>„Du musst in 30 Sekunden 3 Dinge nennen, die du an ihr liebst ohne zu stocken." (Wisdom-Check)</li>
<li>„Der Magier bietet dir: ENTWEDER ihre Schuhe ODER ein ewiges Leben ohne sie. Was wählst du?" (Roleplay kein Würfelwurf)</li>
</ul>
</div>
</div>
<div class="encounter-box">
<div class="encounter-title">🎲 Szene III Der finale Würfelkampf</div>
<div class="flavor-text">
„Ein letztes Tor. Dahinter wartet der Wächter der Wächter der härteste Gegner des Abends. Er fordert den Held zum entscheidenden Duell."
</div>
<p>Klassischer Würfelkampf: Beide würfeln W10. Wer höher würfelt, gewinnt die Runde. Beste 3 von 5 Runden. Bei Unentschieden: Stechershot trinkt der Verlierer.</p>
<div class="stat-block">
<div class="stat-item">
<div class="stat-val">W10</div>
<div class="stat-name">Würfel</div>
</div>
<div class="stat-item">
<div class="stat-val">3/5</div>
<div class="stat-name">Best-of</div>
</div>
<div class="stat-item">
<div class="stat-val">🍺</div>
<div class="stat-name">Einsatz</div>
</div>
</div>
<p style="font-size:0.9rem;">Jede verlorene Runde = ein Schluck. Wer das Duell verliert, ERZÄHLT außerdem einer Anekdote über die Braut.</p>
</div>
<div class="dice-roller">
<div class="dice-roller-title">🎲 Würfel-Assistent</div>
<div class="dice-btns" id="diceBtns"></div>
<div class="dice-result" id="diceResult"></div>
<div class="dice-log" id="diceLog"></div>
</div>
</div>
</div>
<!-- ═══ TRACKER ═══ -->
<div id="tab-tracker" class="tab-content">
<div class="parchment">
<div class="section-title">Helden-Tracker</div>
<div class="section-subtitle">Live-Übersicht während des Abends</div>
<div style="display:grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom:1.5rem;">
<div class="drink-tracker">
<div class="drink-count" id="drinkCount">0</div>
<div class="drink-label">Getrunken 🍺</div>
<div class="drink-btns">
<button class="btn-drink add" onclick="changeDrinks(1)">+1</button>
<button class="btn-drink sub" onclick="changeDrinks(-1)">1</button>
</div>
</div>
<div class="drink-tracker">
<div class="drink-count" id="questCount" style="color: var(--green);">0/8</div>
<div class="drink-label">Besitztümer ⚔</div>
<div style="margin-top:1rem; font-family: 'Cinzel', serif; font-size:0.75rem; color:#8a6520; letter-spacing:0.1em;">
Im Intro-Tab verwalten
</div>
</div>
</div>
<div class="rules-box">
<div class="rules-title">Quest-Status</div>
<div id="questStatusList" style="display:flex; flex-direction:column; gap:0.4rem; margin-top:0.5rem;"></div>
</div>
<div class="rules-box" style="margin-top:1rem;">
<div class="rules-title">Notizen</div>
<textarea id="notes" placeholder="Hier Notizen eingeben..."
style="width:100%; background:rgba(138,101,32,0.05); border:1px solid #c9a22740; border-radius:4px; padding:0.6rem; font-family:'Crimson Pro',serif; font-size:0.95rem; color:var(--ink-light); resize:vertical; min-height:80px; outline:none;"
oninput="saveNotes()"></textarea>
</div>
<div style="text-align:center; margin-top:1.5rem;">
<button class="btn-primary" onclick="resetAll()">↺ Neustart</button>
</div>
</div>
</div>
<!-- ═══ SPIELER ═══ -->
<div id="tab-spieler" class="tab-content">
<div class="parchment">
<div class="section-title">Die Gefährten</div>
<div class="section-subtitle">Spieler, Rollen & Hüter-Zuteilung</div>
<div class="flavor-text">
Trage hier alle Teilnehmer des Abends ein und weise ihnen ihre Rolle zu. So behältst du den Überblick, wer welche Quest hütet.
</div>
<!-- Hinzufügen -->
<div class="rules-box">
<div class="rules-title">Spieler hinzufügen</div>
<div class="spieler-add-row" style="margin-top:0.8rem;">
<input class="spieler-input" id="sName" placeholder="Name..." maxlength="30" onkeydown="if(event.key==='Enter')addSpieler()">
<select class="spieler-select" id="sRolle">
<option value="held">⚔ Held (Bräutigam)</option>
<option value="spielleiter">📜 Spielleiter</option>
<option value="hüter" selected>🔒 Hüter</option>
<option value="ratsmitglied">🧙 Ratsmitglied</option>
<option value="einsteiger">🌱 Einsteiger</option>
<option value="gast">🍺 Gast</option>
</select>
<select class="spieler-select" id="sQuest">
<option value=""> Quest zuweisen </option>
</select>
<button class="btn-add" onclick="addSpieler()">+ Hinzufügen</button>
</div>
</div>
<!-- Liste -->
<div id="spielerList" class="spieler-list">
<div class="spieler-empty">Keine Teilnehmer eingetragen.</div>
</div>
<!-- Zusammenfassung -->
<div class="spieler-summary" id="spielerSummary" style="display:none;">
<span class="summary-chip" id="sumTotal">0 Teilnehmer</span>
<span class="summary-chip" id="sumHüter">0 Hüter</span>
<span class="summary-chip" id="sumRat">0 Ratsmitglieder</span>
<span class="summary-chip" id="sumEinsteiger">0 Einsteiger</span>
</div>
</div>
<!-- Rollen-Legende -->
<div class="parchment">
<div class="section-title" style="font-size:1.1rem;">Rollen-Übersicht</div>
<div class="ornament">✦ ✦ ✦</div>
<div class="rules-box">
<ul class="rules-list">
<li><span class="spieler-rolle-badge rolle-held">⚔ HELD</span> &nbsp;Der Bräutigam löst alle Quests.</li>
<li><span class="spieler-rolle-badge rolle-spielleiter">📜 SPIELLEITER</span> &nbsp;Organisator kennt alle Aufgaben, leitet den Abend.</li>
<li><span class="spieler-rolle-badge rolle-hüter">🔒 HÜTER</span> &nbsp;Bewacht ein Besitztum & stellt die zugewiesene Quest.</li>
<li><span class="spieler-rolle-badge rolle-ratsmitglied">🧙 RATSMITGLIED</span> &nbsp;Erfahrener RPG-Spieler nimmt am Tabletop-Block teil.</li>
<li><span class="spieler-rolle-badge rolle-einsteiger">🌱 EINSTEIGER</span> &nbsp;Tabletop-Neuling bekommt einfache Quests.</li>
<li><span class="spieler-rolle-badge rolle-gast">🍺 GAST</span> &nbsp;Schaut zu, feuert an und trinkt mit.</li>
</ul>
</div>
</div>
</div>
<!-- ═══ ABLAUF ═══ -->
<div id="tab-ablauf" class="tab-content">
<div class="parchment">
<div class="section-title">Ablaufplan</div>
<div class="section-subtitle">Empfohlener Zeitplan für den Abend</div>
<div class="timeline">
<div class="timeline-item">
<div class="timeline-time">Beginn · Ankunft</div>
<div class="timeline-event">Versammlung & Einstimmung</div>
<div class="timeline-note">Alle Hüter bekommen ihren Gegenstand & ihre Aufgabe erklärt. Der Held weiß noch nichts.</div>
</div>
<div class="timeline-item">
<div class="timeline-time">~30 Min. nach Beginn</div>
<div class="timeline-event">Die Verkündigung 📜</div>
<div class="timeline-note">Der Spielleiter liest dem Held feierlich die Quest vor (Intro-Text). Dann beginnt das Abenteuer.</div>
</div>
<div class="timeline-item">
<div class="timeline-time">Phase 1 · 45-60 Min.</div>
<div class="timeline-event">Einsteiger-Quests (15)</div>
<div class="timeline-note">Der Held absolviert die ersten Aufgaben und erbeutet die ersten Besitztümer.</div>
</div>
<div class="timeline-item">
<div class="timeline-time">Pause · ~Mitte des Abends</div>
<div class="timeline-event">Inventur & Halftime-Ritual</div>
<div class="timeline-note">Für jeden fehlenden Gegenstand trinkt der Held einen kleinen Schluck. Dann gehts weiter.</div>
</div>
<div class="timeline-item">
<div class="timeline-time">Phase 2 · 45-60 Min.</div>
<div class="timeline-event">Herausforderungs-Quests (68)</div>
<div class="timeline-note">Anspruchsvollere Aufgaben mit höherem Einsatz.</div>
</div>
<div class="timeline-item">
<div class="timeline-time">Optional · nach Phase 2</div>
<div class="timeline-event">⚔ Tabletop-Block</div>
<div class="timeline-note">Die RPG-erfahrenen Spieler ziehen sich für 30-60 Min. in die Ratssitzung zurück. Andere trinken weiter.</div>
</div>
<div class="timeline-item">
<div class="timeline-time">Finale · Abend-Ende</div>
<div class="timeline-event">Die Befreiung der Braut 🏆</div>
<div class="timeline-note">Der Held präsentiert alle Besitztümer. Für jeden fehlenden: Straftrunk. Dann: Feier ohne Quests!</div>
</div>
</div>
<div class="rules-box" style="margin-top:1.5rem;">
<div class="rules-title">Hinweise für den Spielleiter</div>
<ul class="rules-list">
<li>Hüter vorab briefen! Aufgabe kennen, Gegenstand bereit haben.</li>
<li>Zweite Chance immer erlauben aber mit Extratrinken.</li>
<li>Atmosphäre hat Vorrang vor strikter Regelauslegung.</li>
<li>Wasser & Snacks bereitstellen, wenn es länger geht.</li>
</ul>
</div>
</div>
</div>
</div><!-- /page-wrap -->
</div><!-- /app -->
<script>
// ══════════════════════════════════════════
// DATA
// ══════════════════════════════════════════
const quests = [
{
id: 0,
badge: 'easy',
badgeLabel: 'EINSTEIGER',
name: 'Das Lied der Liebe',
holder: 'Hüter des <strong>Briefes</strong>',
item: '💌 Brief des ersten Dates',
desc: 'Der Held muss der Runde einen Liebesfilm-Titel nennen und ihn pantomimisch darstellen die anderen raten.',
success: '✓ Brief bekommt er',
fail: '✗ Ein Schluck',
details: `<strong>Ablauf:</strong> Der Hüter flüstert dem Held einen Filmtitel ins Ohr (z.B. Titanic, Die Schöne und das Biest, Notting Hill). Der Held darf nicht sprechen und muss den Titel pantomimisch darstellen. Zeitlimit: 60 Sekunden.`,
},
{
id: 1,
badge: 'easy',
badgeLabel: 'EINSTEIGER',
name: 'Der Wissenstest',
holder: 'Hüter des <strong>Rings</strong>',
item: '💍 Ring der Verbindung',
desc: 'Der Hüter stellt 5 Fragen über die Braut. Der Held muss mindestens 3 richtig beantworten.',
success: '✓ Ring bekommt er',
fail: '✗ Pro Falschantwort ein Schluck',
details: `<strong>Fragen vorbereiten (Hüter):</strong> Wähle 5 aus diesen Kategorien:<br>
• Lieblingsessen? • Lieblingsfilm? • Wo wollte sie immer hinreisen?<br>
• Was ist ihr größter Tick? • Wie heißt ihre beste Freundin?<br>
• Was war ihr erstes Haustier? • Was mag sie am Bräutigam am meisten?<br><br>
<strong>Hinweis:</strong> Fragen vorab mit der Braut abstimmen.`,
},
{
id: 2,
badge: 'easy',
badgeLabel: 'EINSTEIGER',
name: 'Das Duell der Münzen',
holder: 'Hüter des <strong>Schlüssels</strong>',
item: '🔑 Schlüssel zur Zukunft',
desc: 'Münze oder Kronkorken werfen: Wer 3 von 5 Würfen in einen Becher trifft, gewinnt.',
success: '✓ Schlüssel bekommt er',
fail: '✗ Ein ordentlicher Schluck',
details: `<strong>Setup:</strong> Einen Becher auf den Tisch stellen, ca. 12 Meter Abstand. Jeder wirft abwechselnd (Held vs. Hüter). Bester aus 5 Versuchen gewinnt.<br><br>
<strong>Regelung bei Gleichstand:</strong> Plötzlicher Tod wer als nächstes trifft, gewinnt.`,
},
{
id: 3,
badge: 'easy',
badgeLabel: 'EINSTEIGER',
name: 'Der Schwur der tausend Worte',
holder: 'Hüter der <strong>Blume</strong>',
item: '🌹 Blume der Ewigkeit',
desc: 'Der Held muss in 90 Sekunden einen Heiratsantrag improvisieren vor allen Gästen.',
success: '✓ Blume bekommt er',
fail: '✗ Kein Applaus = trinken',
details: `<strong>Ablauf:</strong> Der Held hat 90 Sekunden Zeit für einen improvisierten Heiratsantrag an die (nicht anwesende) Braut. Die Gäste klatschen am Ende.<br><br>
<strong>Bewertung:</strong> Lautester Applaus = Erfolg. Stille oder Widerspruch = Misserfolg. Der Hüter entscheidet im Zweifelsfall.`,
},
{
id: 4,
badge: 'medium',
badgeLabel: 'MITTEL',
name: 'Die Chronik der Scham',
holder: 'Hüter des <strong>Fotos</strong>',
item: '📸 Foto der gemeinsamen Zeit',
desc: 'Der Hüter erzählt eine peinliche Geschichte des Helden. Wer davon die meisten Details wusste, gewinnt.',
success: '✓ Foto und eine Geschichte über die Braut zurück',
fail: '✗ Der Held trinkt und erzählt selbst die Geschichte',
details: `<strong>Ablauf:</strong> Der Hüter hat vorab eine peinliche Geschichte über den Helden vorbereitet. Er liest sie vor und stellt danach 3 Detailfragen. Kann der Held 2 von 3 beantworten? → Erfolg.<br><br>
<strong>Empfehlung:</strong> Eine Person, die den Helden gut kennt, übernimmt diese Rolle.`,
},
{
id: 5,
badge: 'medium',
badgeLabel: 'MITTEL',
name: 'Das Blindverkostung',
holder: 'Hüter des <strong>Schuhs</strong>',
item: '👠 Schuh der Flucht',
desc: '5 Getränke blind verkosten und benennen. Mindestens 3 richtig.',
success: '✓ Schuh bekommt er',
fail: '✗ Alle falsch erkannten Getränke austrinken',
details: `<strong>Vorbereitung:</strong> 5 Getränke in nummerierten Bechern vorbereiten (Mischung aus Alkohol und Softdrinks). Augenbinde aufsetzen.<br><br>
<strong>Regeln:</strong> Riechen ist erlaubt. Geraten werden darf nur auf Basis der Verkostung.`,
},
{
id: 6,
badge: 'hard',
badgeLabel: 'HERAUSFORDERUNG',
name: 'Der Zweikampf der Weisheit',
holder: 'Hüter des <strong>Maskottchens</strong>',
item: '🧸 Maskottchen des Heils',
desc: 'Ein Trivia-Duell: Pop-Kultur, Warhammer & DnD-Fragen. Wer 3 von 5 beantwortet, gewinnt.',
success: '✓ Maskottchen und ein Ehrentitel',
fail: '✗ Zwei große Schlucke',
details: `<strong>Beispielfragen:</strong><br>
• RPG: „Wie viele Seiten hat ein W20?" (20)<br>
• Warhammer: „Welche Fraktion trägt Schädelornamente als Zeichen der Loyalität?" (Space Marines / diverse)<br>
• DnD: „Was ist ein nat. 20?" (Kritischer Erfolg)<br>
• Allgemein: „Wer schrieb 'Der Herr der Ringe'?" (Tolkien)<br>
• Persönlich: Eine Frage über das Paar.<br><br>
<strong>Hinweis:</strong> Schwierigkeitsgrad der Fragen an die Gruppe anpassen.`,
},
{
id: 7,
badge: 'hard',
badgeLabel: 'HERAUSFORDERUNG',
name: 'Die letzte Prüfung',
holder: 'Hüter der <strong>Urkunde</strong>',
item: '🏆 Urkunde der Heldentaten',
desc: 'Der Held muss einen Abend-Rückblick halten: Die 5 peinlichsten Momente des Abends, dramatisch vorgetragen.',
success: '✓ Urkunde + Applaus der Legende',
fail: '✗ Nochmal von vorne lauter!',
details: `<strong>Ablauf:</strong> Diese Quest steht am Ende des Abends. Der Held fasst in einer Rede (23 Min.) die Ereignisse des Abends zusammen pointiert und feierlich vorgetragen.<br><br>
<strong>Bewertung:</strong> Stehende Ovationen = Erfolg. Unzureichende Darbietung = Neuversuch.<br><br>
<strong>Abschluss:</strong> Nach bestandener Quest wird die Urkunde feierlich überreicht.`,
},
];
const itemCollected = new Array(8).fill(false);
let drinks = 0;
const questDone = new Array(quests.length).fill(false);
// ══════════════════════════════════════════
// STARS
// ══════════════════════════════════════════
(function createStars() {
const c = document.getElementById('stars');
for (let i = 0; i < 120; i++) {
const s = document.createElement('div');
s.className = 'star';
s.style.cssText = `
left:${Math.random()*100}%;
top:${Math.random()*100}%;
--d:${2+Math.random()*4}s;
--delay:${Math.random()*5}s;
--bright:${0.4+Math.random()*0.6};
width:${Math.random()<0.3?3:2}px;
height:${Math.random()<0.3?3:2}px;
`;
c.appendChild(s);
}
})();
// ══════════════════════════════════════════
// START
// ══════════════════════════════════════════
function startApp() {
document.getElementById('cover').style.display = 'none';
document.getElementById('app').style.display = 'block';
buildQuests();
buildDice();
loadState();
loadSpieler();
updateQuestStatus();
}
// ══════════════════════════════════════════
// TABS
// ══════════════════════════════════════════
function showTab(id) {
document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active'));
document.getElementById('tab-' + id).classList.add('active');
event.target.classList.add('active');
}
// ══════════════════════════════════════════
// QUESTS
// ══════════════════════════════════════════
function buildQuests() {
const grid = document.getElementById('questGrid');
grid.innerHTML = '';
quests.forEach((q, i) => {
const card = document.createElement('div');
card.className = 'quest-card' + (questDone[i] ? ' done' : '');
card.id = 'qcard-' + i;
card.innerHTML = `
<span class="quest-badge badge-${q.badge}">${q.badgeLabel}</span>
<div class="quest-name">${q.name}</div>
<div class="quest-holder">Hüter: ${q.holder}</div>
<div class="quest-desc">${q.desc}</div>
<div class="quest-rewards">
<span class="reward-success">${q.success}</span>
<span class="reward-fail">${q.fail}</span>
</div>
<div class="quest-expand">${q.details}<br><br>
<button onclick="event.stopPropagation(); toggleDone(${i})"
style="font-family:'Cinzel',serif; font-size:0.75rem; background:transparent; border:1px solid #8a6520; padding:0.3rem 0.8rem; cursor:pointer; color:#8a6520; border-radius:3px;">
${questDone[i] ? '↩ Als offen markieren' : '✓ Als erledigt markieren'}
</button>
</div>
<div class="expand-hint">▼ Aufklappen für Details</div>
`;
card.addEventListener('click', () => toggleExpand(i));
grid.appendChild(card);
});
}
function toggleExpand(i) {
const card = document.getElementById('qcard-' + i);
card.classList.toggle('expanded');
}
function toggleDone(i) {
questDone[i] = !questDone[i];
saveState();
buildQuests();
updateQuestStatus();
}
// ══════════════════════════════════════════
// INVENTORY
// ══════════════════════════════════════════
function toggleItem(i) {
itemCollected[i] = !itemCollected[i];
const slot = document.getElementById('inv-' + i);
slot.classList.toggle('collected', itemCollected[i]);
saveState();
updateQuestStatus();
}
// ══════════════════════════════════════════
// DRINKS
// ══════════════════════════════════════════
function changeDrinks(n) {
drinks = Math.max(0, drinks + n);
document.getElementById('drinkCount').textContent = drinks;
saveState();
}
// ══════════════════════════════════════════
// STATUS
// ══════════════════════════════════════════
function updateQuestStatus() {
const count = itemCollected.filter(Boolean).length;
document.getElementById('questCount').textContent = count + '/8';
const list = document.getElementById('questStatusList');
if (!list) return;
list.innerHTML = quests.map((q, i) => `
<div style="display:flex; align-items:center; gap:0.5rem; font-size:0.9rem; color:${questDone[i]?'#2a5a1a':'#5a3a10'};">
<span>${questDone[i] ? '✓' : '○'}</span>
<span>${q.name}</span>
<span style="margin-left:auto; font-size:0.8rem; opacity:0.7">${q.item}</span>
</div>
`).join('');
}
// ══════════════════════════════════════════
// DICE
// ══════════════════════════════════════════
function buildDice() {
const btn = document.getElementById('diceBtns');
if (!btn) return;
[4,6,8,10,12,20,100].forEach(d => {
const b = document.createElement('button');
b.className = 'dice-btn';
b.textContent = 'W' + d;
b.onclick = () => rollDice(d);
btn.appendChild(b);
});
}
function rollDice(sides) {
const result = Math.floor(Math.random() * sides) + 1;
const el = document.getElementById('diceResult');
const log = document.getElementById('diceLog');
el.classList.remove('rolling');
void el.offsetWidth;
el.classList.add('rolling');
el.textContent = result;
const reactions = result === sides ? '⭐ KRITISCH! Legendär!' :
result === 1 ? '💀 Kritischer Fehlschlag! Trinken!' :
result >= Math.ceil(sides * 0.7) ? '✓ Starkes Ergebnis!' :
result >= Math.ceil(sides * 0.4) ? '~ Mittelmaß...' : '✗ Schwach. Trinken?';
log.textContent = `W${sides}${result} · ${reactions}`;
}
// ══════════════════════════════════════════
// SPIELER
// ══════════════════════════════════════════
let spieler = [];
const rolleLabels = {
held: '⚔ Held',
spielleiter: '📜 Spielleiter',
hüter: '🔒 Hüter',
ratsmitglied: '🧙 Ratsmitglied',
einsteiger: '🌱 Einsteiger',
gast: '🍺 Gast',
};
function populateQuestSelect() {
const sel = document.getElementById('sQuest');
if (!sel) return;
const current = sel.value;
sel.innerHTML = '<option value=""> Quest zuweisen </option>';
quests.forEach((q, i) => {
const opt = document.createElement('option');
opt.value = i;
opt.textContent = q.name;
sel.appendChild(opt);
});
sel.value = current;
}
function addSpieler() {
const nameEl = document.getElementById('sName');
const rolleEl = document.getElementById('sRolle');
const questEl = document.getElementById('sQuest');
const name = nameEl.value.trim();
if (!name) { nameEl.focus(); return; }
const rolle = rolleEl.value;
const questIdx = questEl.value !== '' ? parseInt(questEl.value) : null;
spieler.push({ id: Date.now(), name, rolle, questIdx });
nameEl.value = '';
questEl.value = '';
saveSpieler();
renderSpieler();
}
function deleteSpieler(id) {
spieler = spieler.filter(s => s.id !== id);
saveSpieler();
renderSpieler();
}
function changeRolle(id, rolle) {
const s = spieler.find(s => s.id === id);
if (s) { s.rolle = rolle; saveSpieler(); renderSpieler(); }
}
function changeQuest(id, val) {
const s = spieler.find(s => s.id === id);
if (s) { s.questIdx = val !== '' ? parseInt(val) : null; saveSpieler(); renderSpieler(); }
}
function getInitial(name) {
return name.charAt(0).toUpperCase();
}
function renderSpieler() {
const list = document.getElementById('spielerList');
if (!list) return;
if (spieler.length === 0) {
list.innerHTML = '<div class="spieler-empty">Keine Teilnehmer eingetragen.</div>';
} else {
list.innerHTML = spieler.map(s => {
const questName = s.questIdx !== null ? quests[s.questIdx]?.name : null;
return `
<div class="spieler-item">
<div class="spieler-avatar">${getInitial(s.name)}</div>
<div class="spieler-info">
<div class="spieler-name-display">${s.name}</div>
<div class="spieler-rolle-display">${questName ? '🔒 ' + questName : rolleLabels[s.rolle]}</div>
</div>
<span class="spieler-rolle-badge rolle-${s.rolle}">${rolleLabels[s.rolle]}</span>
<select class="spieler-quest-sel" onchange="changeQuest(${s.id}, this.value)">
<option value=""> Keine Quest </option>
${quests.map((q,i) => `<option value="${i}" ${s.questIdx===i?'selected':''}>${q.name}</option>`).join('')}
</select>
<select class="spieler-select" style="font-size:0.8rem; padding:0.25rem 0.4rem;" onchange="changeRolle(${s.id}, this.value)">
${Object.entries(rolleLabels).map(([k,v]) => `<option value="${k}" ${s.rolle===k?'selected':''}>${v}</option>`).join('')}
</select>
<button class="btn-del" onclick="deleteSpieler(${s.id})">✕</button>
</div>`;
}).join('');
}
// Summary
const summary = document.getElementById('spielerSummary');
if (summary) {
summary.style.display = spieler.length ? 'flex' : 'none';
document.getElementById('sumTotal').textContent = spieler.length + ' Teilnehmer';
document.getElementById('sumHüter').textContent = spieler.filter(s=>s.rolle==='hüter'||s.questIdx!==null).length + ' Hüter';
document.getElementById('sumRat').textContent = spieler.filter(s=>s.rolle==='ratsmitglied').length + ' Ratsmitglieder';
document.getElementById('sumEinsteiger').textContent = spieler.filter(s=>s.rolle==='einsteiger').length + ' Einsteiger';
}
}
function saveSpieler() {
try { localStorage.setItem('jga_spieler', JSON.stringify(spieler)); } catch(e) {}
}
function loadSpieler() {
try {
const s = JSON.parse(localStorage.getItem('jga_spieler') || 'null');
if (s) spieler = s;
} catch(e) {}
populateQuestSelect();
renderSpieler();
}
function saveState() {
try {
localStorage.setItem('jga_state', JSON.stringify({ itemCollected, drinks, questDone }));
} catch(e) {}
}
function loadState() {
try {
const s = JSON.parse(localStorage.getItem('jga_state') || 'null');
if (!s) return;
s.itemCollected?.forEach((v, i) => {
itemCollected[i] = v;
const slot = document.getElementById('inv-' + i);
if (slot && v) slot.classList.add('collected');
});
drinks = s.drinks || 0;
document.getElementById('drinkCount').textContent = drinks;
s.questDone?.forEach((v, i) => { questDone[i] = v; });
const notes = localStorage.getItem('jga_notes');
if (notes) document.getElementById('notes').value = notes;
} catch(e) {}
}
function saveNotes() {
try { localStorage.setItem('jga_notes', document.getElementById('notes').value); } catch(e) {}
}
function resetAll() {
if (!confirm('Wirklich alles zurücksetzen?')) return;
itemCollected.fill(false);
questDone.fill(false);
drinks = 0;
document.querySelectorAll('.item-slot').forEach(s => s.classList.remove('collected'));
document.getElementById('drinkCount').textContent = 0;
document.getElementById('notes').value = '';
spieler = [];
renderSpieler();
try { localStorage.clear(); } catch(e) {}
buildQuests();
updateQuestStatus();
}
</script>
</body>
</html>