Files
geograsim/App/sims/kofferdetektiv/game-3d.html
T
Adminator 11c48817de Kofferdetektiv iPad: zwei feste Joysticks (immer 50% sichtbar)
Statt schwebendem Links-Joystick + Rechts-Wisch jetzt zwei feste,
halbtransparente Joysticks:
- links = Umsehen (Auge 👁), ratenbasiertes Drehen/Neigen in update()
- rechts = Bewegen (Fuß 🦶)
Touch wird dem naeheren Joystick im Fangradius zugewiesen (assign/stickVec).
Interaktions-Button (🧳) nach unten-mittig verschoben (kollidierte sonst
mit dem rechten Bewegungs-Joystick). Joysticks nur auf Touch sichtbar
(body.kd-touch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 13:57:20 +02:00

791 lines
44 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.0, maximum-scale=1.0, user-scalable=no">
<title>Kofferdetektiv — GeoGraSim</title>
<style>
/* ============ Design-System-Variablen (Fallback, identisch zur Plattform) ============ */
:root{
--ggs-fjord:#4a7c8a; --ggs-fjord-dark:#1f4e5a; --ggs-fjord-light:#dae8ec;
--ggs-moss:#5a8a5e; --ggs-moss-dark:#3a6b3e; --ggs-moss-light:#dcead0;
--ggs-sand:#e8d5b5; --ggs-sand-dark:#c4a97a;
--ggs-coral:#c85c4a; --ggs-coral-light:#f0d0ca; --ggs-orange:#e8833a;
--ggs-bg:#f5f2eb; --ggs-bg-dark:#e8e4d8; --ggs-white:#fff;
--ggs-text:#2a2a2a; --ggs-text-muted:#8a8a8a; --ggs-border:#e0ddd4;
--ggs-radius-sm:6px; --ggs-radius-md:12px; --ggs-radius-lg:16px;
--ggs-shadow-lg:0 4px 16px rgba(0,0,0,.16);
--ggs-font:'Inter',system-ui,-apple-system,sans-serif;
--ggs-ease:cubic-bezier(.22,1,.36,1); --ggs-duration:.25s;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;overflow:hidden}
body{font-family:var(--ggs-font);font-size:16px;color:var(--ggs-text);background:#0a0f16}
button{font-family:inherit;font-size:16px;cursor:pointer}
img{max-width:100%}
/* ============ Plattform-Header (Sim-Spec §4.2) ============ */
.ggs-header{display:flex;align-items:center;gap:16px;padding:0 24px;height:52px;
background:var(--ggs-bg);color:var(--ggs-text);font-size:14px;z-index:1000;position:relative;
border-bottom:1px solid var(--ggs-border)}
.ggs-header-logo{display:flex;align-items:center;gap:10px;text-decoration:none}
.ggs-header-logo-icon{height:38px}
.ggs-header-logo-text{height:20px}
.ggs-header-logo img[data-broken]{display:none}
.ggs-header-separator{width:1px;height:26px;background:var(--ggs-border)}
.ggs-header-module{font-weight:600;font-size:14px;color:var(--ggs-fjord-dark)}
.ggs-header-badge{background:var(--ggs-fjord-light);color:var(--ggs-fjord-dark);
padding:3px 10px;border-radius:999px;font-size:12px;font-weight:700}
.ggs-header-spacer{flex:1}
.ggs-header-actions{display:flex;align-items:center;gap:8px}
.ggs-btn-ghost{background:transparent;border:none;color:var(--ggs-text-muted);padding:8px 12px;
min-width:36px;min-height:36px;border-radius:var(--ggs-radius-sm);text-decoration:none;
display:inline-flex;align-items:center;justify-content:center;font-size:17px;
transition:all var(--ggs-duration) var(--ggs-ease)}
.ggs-btn-ghost:hover{background:var(--ggs-bg-dark);color:var(--ggs-fjord-dark)}
/* ============ Buttons ============ */
.kd-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:12px 22px;
border:none;border-radius:var(--ggs-radius-sm);font-size:15px;font-weight:700;min-height:44px;
transition:all var(--ggs-duration) var(--ggs-ease)}
.kd-btn-primary{background:var(--ggs-fjord);color:#fff}
.kd-btn-primary:hover{background:var(--ggs-fjord-dark)}
.kd-btn-success{background:var(--ggs-moss);color:#fff}
.kd-btn-success:hover{background:var(--ggs-moss-dark)}
.kd-btn-quiet{background:var(--ggs-bg-dark);color:var(--ggs-text)}
.kd-btn-danger{background:var(--ggs-coral);color:#fff}
/* ============ 3D-Stage + HUD ============ */
#stage{position:fixed;inset:52px 0 0 0;background:#0a0f16}
#gl{width:100%;height:100%;display:block;touch-action:none}
#hud{position:absolute;inset:0;pointer-events:none}
.hud-pill{position:absolute;background:rgba(13,21,32,.82);color:#cfe3ee;border:1px solid #2c3a4a;
border-radius:10px;padding:8px 14px;font-size:14px;font-weight:600;backdrop-filter:blur(4px)}
.hud-pill:empty{display:none}
#hud-case{top:14px;left:16px}
#hud-points{top:14px;left:150px}
#hud-timer{top:14px;left:260px}
#hud-timer.warn{color:#f0b9ad;border-color:#c85c4a}
#hud-loc{bottom:18px;left:16px;font-weight:500}
#hud-dist{bottom:18px;left:50%;transform:translateX(-50%);font-weight:500;color:#9fb6c6}
#hud-coords{bottom:18px;right:16px;padding:6px 11px;font-size:12px;font-weight:600;color:#9fb6c6;
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;letter-spacing:.3px}
#minimap-wrap{position:absolute;top:14px;right:16px;border:1px solid #2c3a4a;border-radius:10px;
overflow:hidden;background:rgba(13,21,32,.85);pointer-events:auto}
#minimap{display:block}
#btn-map{position:absolute;top:166px;right:16px;pointer-events:auto;background:rgba(13,21,32,.82);
color:#cfe3ee;border:1px solid #2c3a4a;border-radius:10px;padding:7px 12px;font-size:13px;min-height:38px}
#mode-badge{position:absolute;top:214px;right:16px;pointer-events:none;display:none;max-width:210px;
background:rgba(13,21,32,.82);border:1px solid #2c3a4a;border-radius:10px;padding:7px 11px;
text-align:right;backdrop-filter:blur(4px)}
#mode-badge .mb-title{font-size:13px;font-weight:700;color:#cfe3ee}
#mode-badge .mb-hint{font-size:11px;font-weight:500;color:#9fb6c6;line-height:1.35;margin-top:1px}
#mode-badge.look{border-color:#3a6f4a}
#mode-badge.look .mb-title{color:#a8e0bb}
#mode-badge.cursor{border-color:#3a5a7a}
#mode-badge.cursor .mb-title{color:#aecdf0}
#prompt{position:absolute;left:50%;bottom:90px;transform:translateX(-50%);
background:rgba(232,197,71,.94);color:#1f2733;font-weight:700;padding:10px 18px;border-radius:10px;
font-size:15px;opacity:0;transition:opacity .2s}
#prompt.visible{opacity:1}
#btn-touch-act{position:absolute;left:50%;bottom:44px;transform:translateX(-50%);width:84px;height:84px;border-radius:50%;
background:rgba(232,197,71,.92);border:none;font-size:34px;display:none;pointer-events:auto;z-index:45;
box-shadow:0 4px 14px rgba(0,0,0,.4)}
#btn-touch-act.visible{display:block}
/* Zwei feste Joysticks (nur Touch), IMMER halbtransparent sichtbar:
links Umsehen (Auge), rechts Bewegen (Fuß). */
#joystick,#lookstick{position:absolute;bottom:30px;width:132px;height:132px;border-radius:50%;
border:2px solid rgba(159,216,239,.45);background:rgba(13,21,32,.30);
display:none;opacity:.5;pointer-events:none;touch-action:none;z-index:40}
body.kd-touch #joystick,body.kd-touch #lookstick{display:block}
#lookstick{left:24px}
#joystick{right:24px}
#joy-knob,#look-knob{position:absolute;left:50%;top:50%;width:58px;height:58px;margin:-29px 0 0 -29px;border-radius:50%;
background:rgba(159,216,239,.55);display:flex;align-items:center;justify-content:center;
font-size:28px;line-height:1;transition:transform .04s linear}
#toast{position:absolute;top:70px;left:50%;transform:translateX(-50%);max-width:min(640px,90vw);
background:rgba(13,21,32,.92);color:#e8eef4;border:1px solid #2c3a4a;border-radius:12px;
padding:12px 20px;font-size:15px;opacity:0;transition:opacity .3s;pointer-events:none;z-index:50;text-align:center}
#toast.visible{opacity:1}
#crosshair{position:absolute;left:50%;top:50%;width:6px;height:6px;margin:-3px 0 0 -3px;border-radius:50%;
background:rgba(207,227,238,.85);box-shadow:0 0 6px rgba(159,216,239,.6);display:none}
#crosshair.visible{display:block}
#lock-hint{position:absolute;left:50%;bottom:46px;transform:translateX(-50%);
background:rgba(13,21,32,.78);color:#9fb6c6;border:1px solid #2c3a4a;border-radius:999px;
padding:7px 16px;font-size:13px;opacity:0;transition:opacity .3s;pointer-events:none}
#lock-hint.visible{opacity:1}
/* Koffer-Kalibrierung (F9) */
#kalib-panel{position:fixed;top:64px;right:12px;z-index:1600;width:320px;max-height:80vh;overflow:auto;
background:var(--ggs-white);border:1px solid var(--ggs-border);border-radius:var(--ggs-radius-md);
box-shadow:var(--ggs-shadow-lg);padding:14px;display:none}
#kalib-panel.visible{display:block}
#kalib-panel h3{font-size:15px;color:var(--ggs-fjord-dark);margin-bottom:6px}
#kalib-panel p{font-size:12px;color:var(--ggs-text-muted);margin-bottom:10px;line-height:1.45}
.kalib-row{display:grid;grid-template-columns:96px 1fr 44px;gap:8px;align-items:center;
padding:7px 8px;border-radius:8px;cursor:pointer;border:1.5px solid transparent}
.kalib-row:hover{background:var(--ggs-bg)}
.kalib-row.active{border-color:var(--ggs-fjord);background:var(--ggs-fjord-light)}
.kalib-row label{font-size:12px;font-weight:700;cursor:pointer}
.kalib-row input[type=range]{width:100%;accent-color:var(--ggs-fjord)}
.kalib-row .kv{font-size:12px;text-align:right;color:var(--ggs-text-muted);font-variant-numeric:tabular-nums}
/* Musik-Player (Popover unterm Header) */
#music-panel{position:fixed;top:56px;right:12px;z-index:1500;width:300px;background:var(--ggs-white);
border:1px solid var(--ggs-border);border-radius:var(--ggs-radius-md);box-shadow:var(--ggs-shadow-lg);
padding:14px;display:none}
#music-panel.visible{display:block}
#mp-now-row{font-size:12px;color:var(--ggs-text-muted);margin-bottom:4px}
#mp-now{display:block;font-size:13.5px;font-weight:700;color:var(--ggs-fjord-dark);
white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:10px}
#mp-controls{display:flex;gap:6px;align-items:center;margin-bottom:10px}
#mp-controls button{border:1.5px solid var(--ggs-border);background:var(--ggs-bg);border-radius:8px;
min-width:42px;min-height:38px;font-size:17px}
#mp-controls button:hover{border-color:var(--ggs-fjord)}
#mp-shuffle.on{background:var(--ggs-fjord-light);border-color:var(--ggs-fjord);color:var(--ggs-fjord-dark)}
#mp-vol{flex:1;accent-color:var(--ggs-fjord);min-width:60px}
#mp-select{width:100%;padding:9px;font-size:13.5px;border:1.5px solid var(--ggs-border);
border-radius:var(--ggs-radius-sm);background:var(--ggs-bg)}
/* ============ Overlays ============ */
.kd-overlay{position:fixed;inset:0;background:rgba(8,12,18,.66);backdrop-filter:blur(5px);
display:flex;align-items:center;justify-content:center;z-index:2000;opacity:0;pointer-events:none;
transition:opacity var(--ggs-duration)}
.kd-overlay.visible{opacity:1;pointer-events:auto}
/* Ladebildschirm */
#kd-loader{position:fixed;inset:0;z-index:4000;display:flex;align-items:center;justify-content:center;
background:radial-gradient(circle at 50% 38%,#16243a 0%,#0b1019 70%);
transition:opacity .55s var(--ggs-ease)}
#kd-loader.done{opacity:0;pointer-events:none}
.kd-loader-inner{text-align:center;color:#e8e4d8;max-width:420px;padding:24px}
.kd-loader-badge{font-size:54px;margin-bottom:14px;animation:kdl-bob 1.8s ease-in-out infinite}
@keyframes kdl-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-10px)}}
.kd-loader-inner h2{font-size:22px;color:#fff;margin-bottom:8px}
.kd-loader-inner p{font-size:14px;color:#aeb8c6;margin-bottom:22px;line-height:1.5}
.kd-loader-track{height:9px;border-radius:999px;background:rgba(255,255,255,.12);overflow:hidden}
.kd-loader-fill{height:100%;width:0;border-radius:999px;
background:linear-gradient(90deg,#4a7c4e,#e8c547);transition:width .25s linear}
.kd-card{background:var(--ggs-white);border-radius:var(--ggs-radius-lg);padding:28px;
max-width:560px;width:92%;max-height:88vh;overflow:auto;box-shadow:var(--ggs-shadow-lg)}
.kd-card h2{font-size:21px;color:var(--ggs-fjord-dark);margin-bottom:10px}
.kd-card p{line-height:1.55;margin-bottom:12px}
.kd-card .muted{color:var(--ggs-text-muted)}
.kd-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:18px;flex-wrap:wrap}
/* Tutorial */
#tut-img{width:100%;max-width:330px;border-radius:var(--ggs-radius-md);display:block;margin:0 auto 16px}
#tut-dots{text-align:center;margin:10px 0}
#tut-dots .dot{display:inline-block;width:9px;height:9px;border-radius:50%;background:var(--ggs-border);margin:0 4px}
#tut-dots .dot.on{background:var(--ggs-fjord)}
/* Start / Konfiguration */
.cfg-row{margin-bottom:16px}
.cfg-row>label{display:block;font-weight:700;font-size:13px;text-transform:uppercase;
letter-spacing:.5px;color:var(--ggs-text-muted);margin-bottom:8px}
.cfg-opts{display:flex;gap:8px;flex-wrap:wrap}
.cfg-opts label{display:inline-flex;align-items:center;gap:6px;background:var(--ggs-bg);
border:1.5px solid var(--ggs-border);border-radius:var(--ggs-radius-sm);padding:9px 14px;
cursor:pointer;font-size:14px;min-height:40px}
.cfg-opts input{accent-color:var(--ggs-fjord)}
.cfg-opts label:has(input:checked){border-color:var(--ggs-fjord);background:var(--ggs-fjord-light)}
#cfg-continent{padding:10px;border:1.5px solid var(--ggs-border);border-radius:var(--ggs-radius-sm);
font-size:15px;background:var(--ggs-bg);min-height:42px}
/* Analyse: Koffer-Grafik mit Objekt-Chips */
#ana-card{max-width:1020px;display:flex;flex-direction:column}
#ana-head{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;margin-bottom:4px}
#ana-status{color:var(--ggs-text-muted);font-size:13px}
#ana-body{display:grid;grid-template-columns:minmax(330px,1fr) 320px;gap:18px;align-items:start}
@media (max-width:880px){#ana-body{grid-template-columns:1fr}}
#ana-case{position:relative;width:100%;aspect-ratio:1/1;max-width:520px;margin:0 auto;
background-size:cover;background-position:center;border-radius:var(--ggs-radius-md);
background-color:#efe9da}
.obj-chip{position:absolute;transform:translate(-50%,-50%);display:flex;flex-direction:column;
align-items:center;gap:2px;background:rgba(255,255,255,.9);border:1.5px solid var(--ggs-border);
border-radius:12px;padding:6px 7px 5px;min-width:72px;box-shadow:0 3px 8px rgba(40,40,30,.22);
transition:all var(--ggs-duration) var(--ggs-ease)}
.obj-chip:hover{border-color:var(--ggs-fjord);background:#fff;z-index:5;transform:translate(-50%,-50%) scale(1.07)}
.obj-chip.open{background:rgba(220,234,208,.94);border-color:var(--ggs-moss)}
.obj-chip.open .oc-img{opacity:.75}
.obj-chip.active{border-color:var(--ggs-fjord-dark);box-shadow:0 0 0 2.5px var(--ggs-fjord-light),0 3px 8px rgba(40,40,30,.22);z-index:6}
.oc-icon{font-size:26px;line-height:1.6}
.oc-img{width:56px;height:56px;border-radius:9px;object-fit:cover;display:block}
.oc-label{font-size:10px;font-weight:700;color:var(--ggs-text);white-space:nowrap}
/* Inspektions-Bühne (liegt über dem Koffer) */
#obj-stage{position:absolute;inset:0;background:rgba(34,40,48,.55);backdrop-filter:blur(3px);
border-radius:var(--ggs-radius-md);display:none;flex-direction:column;align-items:center;
justify-content:center;gap:8px;padding:14px;z-index:10}
#obj-stage.visible{display:flex;animation:os-in .25s var(--ggs-ease)}
@keyframes os-in{from{opacity:0}to{opacity:1}}
#os-cat{background:rgba(255,255,255,.95);border-radius:999px;padding:6px 14px;font-size:13px;
font-weight:700;color:var(--ggs-fjord-dark);display:flex;align-items:center;gap:7px}
#os-img{width:min(46%,230px);filter:drop-shadow(0 10px 22px rgba(0,0,0,.45));
animation:os-pop .4s var(--ggs-ease)}
@keyframes os-pop{from{transform:scale(.6)}to{transform:scale(1)}}
#os-paper{background:rgba(255,252,243,.96);border-radius:10px;padding:12px 16px;max-width:86%;
box-shadow:0 6px 18px rgba(0,0,0,.35);transform:rotate(-1.3deg);text-align:center}
.os-text{font-size:14.5px;line-height:1.5;font-family:Georgia,'Times New Roman',serif;color:#2e2a22}
.os-flag{width:130px;border:1px solid var(--ggs-border);border-radius:4px;margin-top:8px}
.os-outline{margin-top:8px;background:#f2efe6;border-radius:8px}
.os-photo{width:200px;border:6px solid #fff;border-radius:3px;box-shadow:0 4px 12px rgba(0,0,0,.3);
display:block;margin:0 auto 8px;transform:rotate(1.5deg)}
#os-done{min-height:40px;font-size:14px}
.lvl{font-size:10.5px;padding:2px 7px;border-radius:999px;font-weight:700}
.glossar-btn{border:1px solid var(--ggs-border);background:#fff;border-radius:50%;width:20px;height:20px;
font-size:11px;color:var(--ggs-text-muted);line-height:1}
/* Flieger (FLIP-Animation Koffer ↔ Bühne ↔ Protokoll) */
#flyer{position:fixed;display:none;z-index:3000;pointer-events:none;object-fit:contain;
filter:drop-shadow(0 8px 16px rgba(0,0,0,.4))}
/* Protokoll-Karten */
#ana-cards{max-height:240px;overflow:auto;display:flex;flex-direction:column;gap:6px;margin-bottom:12px}
.log-card{display:flex;gap:9px;align-items:flex-start;text-align:left;background:#fff;
border:1.5px solid var(--ggs-border);border-radius:10px;padding:8px;
transition:all var(--ggs-duration) var(--ggs-ease)}
.log-card:hover{border-color:var(--ggs-fjord);background:var(--ggs-fjord-light)}
.log-card.flash{border-color:var(--ggs-moss);background:var(--ggs-moss-light)}
.lc-img{width:38px;height:38px;object-fit:contain;flex:none}
.lc-body{font-size:11.5px;line-height:1.4;color:var(--ggs-text)}
.lc-body b{display:block;font-size:12px;color:var(--ggs-fjord-dark)}
.lc-text{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.lvl{font-size:10.5px;padding:2px 7px;border-radius:999px;font-weight:700}
.lvl1{background:var(--ggs-sand);color:#6b5a32}
.lvl2{background:var(--ggs-fjord-light);color:var(--ggs-fjord-dark)}
.lvl3{background:var(--ggs-moss-light);color:var(--ggs-moss-dark)}
.glossar-btn{border:1px solid var(--ggs-border);background:#fff;border-radius:50%;width:20px;height:20px;
font-size:11px;color:var(--ggs-text-muted);line-height:1}
#ana-side{background:var(--ggs-bg);border-radius:var(--ggs-radius-md);padding:14px}
#ana-side h3{font-size:12px;text-transform:uppercase;letter-spacing:.5px;color:var(--ggs-text-muted);margin-bottom:8px}
#ana-log{list-style:none;font-size:12.5px;line-height:1.5;max-height:200px;overflow:auto;margin-bottom:12px}
#ana-log li{margin-bottom:6px;border-bottom:1px dashed var(--ggs-border);padding-bottom:5px}
#ana-feedback{background:var(--ggs-coral-light);color:#7c3a2e;border-radius:var(--ggs-radius-sm);
padding:10px;font-size:13.5px;display:none;margin-bottom:10px}
#ana-feedback.visible{display:block}
/* Länderwahl */
#picker-panel{display:none;margin-top:10px}
#picker-panel.visible{display:block}
#picker-search{width:100%;padding:11px;font-size:16px;border:1.5px solid var(--ggs-fjord);
border-radius:var(--ggs-radius-sm);margin-bottom:8px}
.cont-chips{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:8px}
.cont-chip{border:1px solid var(--ggs-border);background:#fff;border-radius:999px;
padding:5px 11px;font-size:12px;min-height:30px}
.cont-chip.on{background:var(--ggs-fjord);color:#fff;border-color:var(--ggs-fjord)}
#picker-list{max-height:230px;overflow:auto;border:1px solid var(--ggs-border);border-radius:var(--ggs-radius-sm);background:#fff}
.picker-item{display:flex;justify-content:space-between;align-items:center;width:100%;padding:10px 12px;
border:none;background:none;border-bottom:1px solid var(--ggs-border);text-align:left;min-height:40px}
.picker-item:hover{background:var(--ggs-fjord-light)}
.pi-name{font-weight:600;font-size:14px}
.pi-cont{font-size:11.5px;color:var(--ggs-text-muted)}
/* Fall-Ergebnis: Visitenkarte des Staates */
#cr-icon{font-size:26px;vertical-align:-3px}
#vk{background:linear-gradient(160deg,#fbf9f3 0%,#f0ece1 100%);border:1.5px solid var(--ggs-sand-dark);
border-radius:var(--ggs-radius-lg);padding:18px;margin:10px 0;box-shadow:inset 0 0 40px rgba(196,169,122,.12)}
#vk-head{display:flex;align-items:center;gap:14px;border-bottom:1.5px solid var(--ggs-sand-dark);
padding-bottom:12px;margin-bottom:12px}
#cr-flag{width:86px;border:1px solid var(--ggs-border);border-radius:6px;box-shadow:0 2px 6px rgba(40,40,30,.18)}
#vk-title{flex:1}
#cr-country{font-size:25px;font-weight:800;color:var(--ggs-fjord-dark);line-height:1.15}
#vk-sub{font-size:13px;color:var(--ggs-text-muted);margin-top:3px}
#cr-wappen{height:74px;max-width:90px;object-fit:contain}
#vk-media{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-bottom:12px}
#vk-media figure{margin:0;text-align:center}
#vk-media figcaption{font-size:11px;color:var(--ggs-text-muted);margin-top:4px;text-transform:uppercase;letter-spacing:.5px}
#cr-landmark{width:220px;border-radius:10px;border:1px solid var(--ggs-border);display:block}
#cr-outline{background:#f4f1e8;border-radius:10px;border:1px solid var(--ggs-border);display:block}
#cr-facts{list-style:none;font-size:13.5px;line-height:1.65;
display:grid;grid-template-columns:1fr 1fr;gap:2px 18px}
@media (max-width:640px){#cr-facts{grid-template-columns:1fr}}
#cr-facts b{color:var(--ggs-fjord-dark)}
#cr-points{text-align:center;font-weight:700;color:var(--ggs-moss-dark)}
/* Auswertung */
#rr-stars{font-size:36px;text-align:center;color:var(--ggs-orange);letter-spacing:6px}
#rr-score{text-align:center;font-size:20px;font-weight:800;color:var(--ggs-fjord-dark);margin-bottom:10px}
#rr-stats,#rr-cases{list-style:none;font-size:14px;line-height:1.8;background:var(--ggs-bg);
border-radius:var(--ggs-radius-md);padding:14px;margin-bottom:12px}
#rr-answer{width:100%;min-height:84px;padding:10px;font-size:15px;border:1.5px solid var(--ggs-border);
border-radius:var(--ggs-radius-sm);resize:vertical;font-family:inherit}
#rr-done-note{display:none;background:var(--ggs-moss-light);color:var(--ggs-moss-dark);
border-radius:var(--ggs-radius-sm);padding:12px;font-weight:600}
/* Länderatlas */
#atlas-svg-wrap{background:#0d1826;border-radius:var(--ggs-radius-md);overflow:hidden}
#atlas-svg-wrap svg{display:block;width:100%;height:auto}
.ac{fill:#2b3a4a;stroke:#0d1826;stroke-width:.4;cursor:pointer;transition:filter .15s}
.ac:hover{filter:brightness(1.5)}
.ac.pool{fill:#46586c}
.ac.read{fill:#e8c547}
.ac.solved{fill:#5a8a5e}
.al{display:inline-block;width:12px;height:12px;border-radius:3px;vertical-align:-2px;margin-right:3px}
.al-solved{background:#5a8a5e}.al-read{background:#e8c547}.al-pool{background:#46586c}
/* Badge-Sammlung */
#badges-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;margin:14px 0;
max-height:60vh;overflow:auto}
.badge-tile{background:var(--ggs-bg);border:1.5px solid var(--ggs-border);border-radius:var(--ggs-radius-md);
padding:12px 10px;text-align:center;opacity:.55}
.badge-tile.got{opacity:1;border-color:var(--ggs-moss);background:var(--ggs-fjord-light)}
.bt-emoji{font-size:34px;line-height:1.1}
.bt-name{font-weight:700;font-size:13px;margin:4px 0 5px;color:var(--ggs-fjord-dark)}
.bt-info{font-size:11.5px;line-height:1.45;color:var(--ggs-text-muted)}
/* Gold-Prestige (Trolley / Weltkugel) */
.gold-line{display:flex;align-items:center;gap:8px;margin:4px 0 10px;padding:8px 12px;font-size:14px;
font-weight:600;color:#8a6a1e;background:linear-gradient(90deg,#fbf3d6,#f3e3a8);
border:1.5px solid #e8c547;border-radius:var(--ggs-radius-sm)}
.gold-line b{color:#7a5a12;font-size:16px}
.gold-ic{width:30px;height:30px;object-fit:contain;filter:drop-shadow(0 1px 2px rgba(160,120,20,.4))}
.kd-btn-gold{background:linear-gradient(90deg,#e8c547,#d9a92e)!important;color:#3a2c08!important;
border:none!important;font-weight:700!important;box-shadow:0 2px 8px rgba(216,169,46,.45)}
.kd-btn-platinum{background:linear-gradient(90deg,#cfd8e3,#9fb0c4)!important;color:#1f2b3a!important;
border:none!important;font-weight:700!important;box-shadow:0 2px 10px rgba(150,170,200,.6)}
#atlas-gold span{display:inline-flex;align-items:center;gap:6px}
/* Tasten-Chips auf Buttons/Links */
.kbd{display:inline-block;min-width:16px;padding:0 5px;margin-left:7px;border-radius:5px;
background:rgba(255,255,255,.22);border:1px solid rgba(255,255,255,.45);font-size:11px;
font-weight:700;line-height:17px;text-align:center;vertical-align:middle}
.kd-btn-quiet .kbd,.ip-source .kbd{background:rgba(40,60,80,.12);border-color:rgba(40,60,80,.3);color:inherit}
.ggs-btn-ghost .hdr-key{position:absolute;right:1px;bottom:0;font-size:9px;font-weight:700;
background:var(--ggs-fjord);color:#fff;border-radius:4px;padding:0 3px;line-height:12px}
.ggs-btn-ghost{position:relative}
/* Info-Punkt: Fakt mit Quelle */
.ip-fact{display:block;background:var(--ggs-fjord-light);border-left:4px solid var(--ggs-fjord);
border-radius:var(--ggs-radius-sm);padding:12px 14px;margin:6px 0 4px;font-size:14.5px;line-height:1.55}
.ip-fact-badge{display:inline-block;font-weight:700;color:var(--ggs-fjord-dark);margin-right:6px}
.ip-source{font-size:12px;color:var(--ggs-text-muted);font-style:italic;margin:2px 0 0}
.ip-source a{color:var(--ggs-fjord);font-style:normal;font-weight:600;text-decoration:none}
.ip-source a:hover{text-decoration:underline}
/* Große Karte */
#bigmap-card{max-width:760px}
#bigmap-canvas{width:100%;border-radius:var(--ggs-radius-md);background:#0d1520}
</style>
</head>
<body>
<!-- Ladebildschirm: federt den anfänglichen Asset-Upload (GLB-Props, Texturen) ab -->
<div id="kd-loader">
<div class="kd-loader-inner">
<div class="kd-loader-badge">🧳</div>
<h2>Spätdienst wird vorbereitet …</h2>
<p>Die Halle wird beleuchtet und das Gepäck einsortiert.</p>
<div class="kd-loader-track"><div class="kd-loader-fill" id="kd-loader-fill"></div></div>
</div>
</div>
<header class="ggs-header">
<a href="/schueler" class="ggs-header-logo" id="ggs-home-logo">
<img src="/assets/img/bildLogo.png" alt="" class="ggs-header-logo-icon" onerror="this.setAttribute('data-broken',1)">
<img src="/assets/img/textlogo_geograsim.svg" alt="GeoGraSim" class="ggs-header-logo-text" onerror="this.setAttribute('data-broken',1)">
</a>
<div class="ggs-header-separator"></div>
<span class="ggs-header-module">Kofferdetektiv</span>
<span class="ggs-header-badge" id="hdr-level">Stufe 1</span>
<div class="ggs-header-spacer"></div>
<div class="ggs-header-actions">
<button class="ggs-btn-ghost" id="btn-info" title="Anleitung nochmal zeigen (Taste H)"><span class="hdr-key">H</span></button>
<a class="ggs-btn-ghost" id="ggs-lehrplan-link" href="#" target="_blank" title="Lehrplan-Bezug">📚</a>
<button class="ggs-btn-ghost" id="btn-atlas" title="Länderatlas — dein Lernfortschritt (Taste L)">🌍<span class="hdr-key">L</span></button>
<a class="ggs-btn-ghost" id="btn-welt" href="welt.html" target="_blank" rel="noopener" title="Welt-Übersicht aller Länder (öffnet im neuen Tab)">🗺️</a>
<button class="ggs-btn-ghost" id="btn-badges" title="Deine gesammelten Reise-Fundsachen (Taste I)">🎒<span class="hdr-key">I</span></button>
<button class="ggs-btn-ghost" id="btn-music" title="Musik aus/an" style="display:none">🎵</button>
<button class="ggs-btn-ghost" id="btn-mute" title="Ton aus/an">🔊</button>
<button class="ggs-btn-ghost" id="btn-reset" title="Fortschritt zurücksetzen">🔄</button>
<a href="/schueler" class="ggs-btn-ghost" id="ggs-home-link" title="Zurück zum Cockpit">🏠</a>
</div>
</header>
<img id="flyer" alt="">
<!-- Koffer-Kalibrierung (F9) -->
<div id="kalib-panel">
<h3>🧰 Koffer-Größen (F9)</h3>
<p>Zeile anklicken → Vorschau erscheint vor dir.<br>Regler = Höhe in Metern, wird gespeichert.</p>
<div id="kalib-list"></div>
<div style="display:flex;gap:8px;margin-top:10px">
<button class="kd-btn kd-btn-quiet" id="kalib-reset" style="flex:1;font-size:13px">Zurücksetzen</button>
<button class="kd-btn kd-btn-primary" id="kalib-close" style="flex:1;font-size:13px">Schließen</button>
</div>
</div>
<!-- Musik-Player -->
<div id="music-panel">
<div id="mp-now-row">♪ Jetzt läuft</div>
<span id="mp-now"></span>
<div id="mp-controls">
<button id="mp-play" title="Abspielen/Pause"></button>
<button id="mp-next" title="Nächster Titel"></button>
<button id="mp-shuffle" title="Zufallswiedergabe">🔀</button>
<input id="mp-vol" type="range" min="0" max="100" value="40" title="Musik-Lautstärke">
</div>
<select id="mp-select" size="1" title="Titel wählen"></select>
</div>
<div id="stage">
<canvas id="gl"></canvas>
<div id="hud">
<div class="hud-pill" id="hud-case">Fall / </div>
<div class="hud-pill" id="hud-points">0 P</div>
<div class="hud-pill" id="hud-timer"></div>
<div class="hud-pill" id="hud-loc"></div>
<div class="hud-pill" id="hud-dist"></div>
<div class="hud-pill" id="hud-coords" title="deine Position & Blickrichtung — praktisch zum Mitteilen"></div>
<div id="minimap-wrap"><canvas id="minimap" width="230" height="140"></canvas></div>
<button id="btn-map">🗺️ Karte (M)</button>
<div id="mode-badge"></div>
<div id="prompt">E — Gepäckstück untersuchen</div>
<button id="btn-touch-act" aria-label="Gepäckstück untersuchen">🧳</button>
<div id="lookstick"><div id="look-knob">👁️</div></div>
<div id="joystick"><div id="joy-knob">🦶</div></div>
<div id="crosshair"></div>
<div id="lock-hint">🖱️ Klicken, um dich umzusehen — Esc gibt die Maus frei</div>
<div id="toast"></div>
</div>
</div>
<!-- Tutorial -->
<div class="kd-overlay" id="ov-tutorial">
<div class="kd-card">
<img id="tut-img" alt="">
<h2 id="tut-h"></h2>
<p id="tut-t"></p>
<div id="tut-dots"></div>
<div class="kd-actions">
<button class="kd-btn kd-btn-quiet" id="tut-back" data-key="f">◂ Zurück <span class="kbd">F</span></button>
<button class="kd-btn kd-btn-primary" id="tut-next" data-key="e">Weiter ▸ <span class="kbd">Leertaste</span></button>
</div>
</div>
</div>
<!-- Start / Konfiguration -->
<div class="kd-overlay" id="ov-start">
<div class="kd-card">
<h2>🕵️ Kofferdetektiv — Spätdienst beginnen</h2>
<p class="muted">Finde die Spur. Erkenne das Land. Löse den Fall.</p>
<div class="cfg-row">
<label>Schwierigkeitsstufe</label>
<div class="cfg-opts">
<label><input type="radio" name="cfg-level" value="1" checked> Stufe 1 · bekannte Länder</label>
<label><input type="radio" name="cfg-level" value="2"> Stufe 2 · weniger bekannte</label>
<label><input type="radio" name="cfg-level" value="3"> Stufe 3 · Expert:in</label>
<label><input type="radio" name="cfg-level" value="4"> 💎 Stufe 4 · Welt-Experten (Platin)</label>
</div>
<p class="muted" id="cfg-level-note" style="font-size:12.5px;margin-top:6px"></p>
</div>
<div class="cfg-row">
<label>Anzahl der Fälle</label>
<div class="cfg-opts">
<label><input type="radio" name="cfg-cases" value="3"> 3 Fälle (kurz)</label>
<label><input type="radio" name="cfg-cases" value="4" checked> 4 Fälle</label>
<label><input type="radio" name="cfg-cases" value="6"> 6 Fälle (lang)</label>
</div>
</div>
<div class="cfg-row">
<label>Zeit pro Fall</label>
<div class="cfg-opts">
<label><input type="radio" name="cfg-time" value="none"> ohne Zeitlimit</label>
<label><input type="radio" name="cfg-time" value="leicht"> 10 min</label>
<label><input type="radio" name="cfg-time" value="mittel" checked> 8 min</label>
<label><input type="radio" name="cfg-time" value="anspruchsvoll"> 5 min</label>
</div>
</div>
<!-- 3D-Umgebungs-Wahl entfernt 2026-06-12: Entscheidung, die eigene
prozedurale Halle auszubauen statt Sketchfab-Szenen zu verwenden.
Der GLB-Loader (assets/vendor) bleibt für einzelne Props erhalten. -->
<div class="cfg-row">
<label>Länderpool</label>
<select id="cfg-continent">
<option value="alle" selected>Alle Kontinente</option>
<option value="Europa">Europa</option>
<option value="Asien">Asien</option>
<option value="Afrika">Afrika</option>
<option value="Nordamerika">Nordamerika</option>
<option value="Südamerika">Südamerika</option>
<option value="Ozeanien">Ozeanien</option>
</select>
</div>
<div class="kd-actions">
<button class="kd-btn kd-btn-success" id="btn-start" data-key="e">Dienst antreten ▸ <span class="kbd">Leertaste</span></button>
</div>
</div>
</div>
<!-- Analyse-Ansicht -->
<div class="kd-overlay" id="ov-analysis">
<div class="kd-card" id="ana-card">
<div id="ana-head">
<h2 id="ana-title">Fall</h2>
<span id="ana-status"></span>
</div>
<p class="muted" style="font-size:13px">Tippe die Gegenstände im Koffer an, um sie zu untersuchen. Jeder Gegenstand enthält einen Hinweis auf das Herkunftsland.</p>
<div id="ana-body">
<div id="ana-case">
<div id="ana-objects"></div>
<!-- Inspektions-Bühne: das angeklickte Objekt in groß, Hinweis „aufgedruckt“ -->
<div id="obj-stage">
<div id="os-cat"></div>
<img id="os-img" alt="">
<div id="os-paper"></div>
<button class="kd-btn kd-btn-primary" id="os-done">Ins Protokoll ▸</button>
</div>
</div>
<div id="ana-side">
<h3>Hinweisprotokoll</h3>
<div id="ana-cards"></div>
<div id="ana-feedback"></div>
<button class="kd-btn kd-btn-success" id="ana-guess-btn" data-key="g" style="width:100%">🌍 Land bestimmen <span class="kbd">G</span></button>
<div id="picker-panel">
<input id="picker-search" type="text" placeholder="Land suchen …" autocomplete="off">
<div class="cont-chips">
<button class="cont-chip on" data-c="alle">Alle</button>
<button class="cont-chip" data-c="EU">Europa</button>
<button class="cont-chip" data-c="AS">Asien</button>
<button class="cont-chip" data-c="AF">Afrika</button>
<button class="cont-chip" data-c="NA">Nordam.</button>
<button class="cont-chip" data-c="SA">Südam.</button>
<button class="cont-chip" data-c="OC">Ozeanien</button>
</div>
<div id="picker-list"></div>
</div>
<div class="kd-actions" style="justify-content:space-between">
<button class="kd-btn kd-btn-quiet" id="ana-giveup" data-key="v" style="font-size:13px">Fall abgeben <span class="kbd">V</span></button>
<button class="kd-btn kd-btn-primary" id="ana-close" data-key="e">Zurück in die Halle <span class="kbd">Leertaste</span></button>
</div>
</div>
</div>
</div>
</div>
<!-- Fall-Ergebnis: Visitenkarte des Staates -->
<div class="kd-overlay" id="ov-case-result">
<div class="kd-card" style="max-width:720px">
<h2 id="cr-h" style="text-align:center"><span id="cr-icon"></span> Fall gelöst!</h2>
<div id="vk-slot-case"><div id="vk">
<div id="vk-head">
<img id="cr-flag" alt="Flagge">
<div id="vk-title">
<div id="cr-country"></div>
<div id="vk-sub"></div>
</div>
<img id="cr-wappen" alt="Wappen" title="Staatswappen">
</div>
<div id="vk-media">
<figure><img id="cr-landmark" alt="Wahrzeichen"><figcaption id="cr-landmark-cap">Wahrzeichen</figcaption></figure>
<figure><canvas id="cr-outline" width="190" height="140"></canvas><figcaption>Umriss</figcaption></figure>
</div>
<ul id="cr-facts"></ul>
</div></div>
<p id="cr-points"></p>
<div class="kd-actions" style="justify-content:center">
<button class="kd-btn kd-btn-primary" id="cr-next" data-key="e">Nächster Fall ▸ <span class="kbd">Leertaste</span></button>
</div>
</div>
</div>
<!-- Durchgangs-Auswertung -->
<div class="kd-overlay" id="ov-round-result">
<div class="kd-card">
<h2 style="text-align:center">Dienst beendet — deine Auswertung</h2>
<div id="rr-stars">☆☆☆</div>
<div id="rr-score">0 / 100</div>
<ul id="rr-stats"></ul>
<ul id="rr-cases"></ul>
<div id="rr-reflect-wrap" style="background:#fdf6e0;border:2px solid #e8c547;border-radius:10px;padding:14px 16px;margin:12px 0">
<div style="font-size:.7rem;letter-spacing:.18em;text-transform:uppercase;color:#8a6516;font-weight:700;margin-bottom:6px">💭 Deine Reflexion</div>
<p style="font-weight:600;margin:0 0 8px;font-size:.98rem" id="rr-question"></p>
<textarea id="rr-answer" placeholder="Schreibe ein paar Sätze — was war für dich der wichtigste Moment? Mindestens 40 Zeichen."></textarea>
<div style="display:flex;justify-content:space-between;font-size:.72rem;color:#8a6516;margin-top:4px">
<span id="rr-answer-count">0 Zeichen</span>
<span>Deine Antwort sieht die Lehrperson — und du selbst in deinem Profil.</span>
</div>
</div>
<p id="rr-done-note">✔ Dein Ergebnis wurde übermittelt. Gut gearbeitet!</p>
<div class="kd-actions">
<button class="kd-btn kd-btn-success" id="rr-finish" data-key="e" disabled title="Schreibe zuerst eine kurze Reflexion (mindestens 40 Zeichen).">Ergebnis übermitteln <span class="kbd">Leertaste</span></button>
<button class="kd-btn kd-btn-primary" id="rr-new" data-key="f">Neuer Durchgang ▸ <span class="kbd">F</span></button>
</div>
</div>
</div>
<!-- Reise-Information / Länder-Visitenkarte (Poster & Atlas) -->
<div class="kd-overlay" id="ov-country">
<div class="kd-card" style="max-width:720px">
<h2 id="cc-title" style="text-align:center">📰 Reise-Information</h2>
<div id="vk-slot-info"></div>
<div class="kd-actions" style="justify-content:center">
<button class="kd-btn kd-btn-primary" id="cc-close" data-key="e">Schließen <span class="kbd">Leertaste</span></button>
</div>
</div>
</div>
<!-- Länderatlas: persistenter Lernfortschritt -->
<div class="kd-overlay" id="ov-atlas">
<div class="kd-card" style="max-width:900px">
<h2>🌍 Dein Länderatlas</h2>
<p class="muted" id="atlas-stats" style="font-size:13px"></p>
<div id="atlas-gold" class="gold-line"></div>
<div id="atlas-svg-wrap"></div>
<p class="muted" style="font-size:12.5px;margin-top:8px">
<span class="al al-solved"></span> Fall korrekt gelöst ·
<span class="al al-read"></span> Visitenkarte gelesen ·
<span class="al al-pool"></span> mögliches Fall-Land ·
Tippe ein farbiges Land an, um seine Visitenkarte zu öffnen.</p>
<div class="kd-actions">
<button class="kd-btn kd-btn-gold" id="atlas-reset" data-key="g" style="display:none">🌍✨ Goldene Weltkugel holen & Atlas zurücksetzen</button>
<button class="kd-btn kd-btn-primary" id="atlas-close" data-key="e">Schließen <span class="kbd">Leertaste</span></button>
</div>
</div>
</div>
<!-- Info-Punkt: Sachinformation zu Flughafen-Einrichtungen -->
<div class="kd-overlay" id="ov-info">
<div class="kd-card" style="max-width:520px">
<h2 id="ip-title" style="display:flex;align-items:center;gap:10px"><span id="ip-icon" style="font-size:30px"></span><span id="ip-name"></span></h2>
<p id="ip-text" style="font-size:15px;line-height:1.6"></p>
<div id="ip-fact" class="ip-fact"><span class="ip-fact-badge">📊 Wusstest du?</span><span id="ip-fact-text"></span></div>
<p id="ip-source" class="ip-source"></p>
<div class="kd-actions" style="justify-content:center">
<button class="kd-btn kd-btn-primary" id="ip-close" data-key="e">Alles klar! <span class="kbd">Leertaste</span></button>
</div>
</div>
</div>
<!-- Fundsache aufgehoben (Badge) -->
<div class="kd-overlay" id="ov-badge">
<div class="kd-card" style="max-width:480px;text-align:center">
<div id="bdg-emoji" style="font-size:64px;line-height:1.1;margin-bottom:6px"></div>
<p id="bdg-note" style="color:var(--ggs-moss);font-weight:700;font-size:13.5px;margin-bottom:10px"></p>
<h2 id="bdg-name" style="justify-content:center"></h2>
<p id="bdg-info" style="font-size:15px;line-height:1.6"></p>
<div class="kd-actions" style="justify-content:center">
<button class="kd-btn kd-btn-quiet" id="bdg-collection" data-key="f">🎒 Sammlung <span class="kbd">F</span></button>
<button class="kd-btn kd-btn-primary" id="bdg-close" data-key="e">Weiter <span class="kbd">Leertaste</span></button>
</div>
</div>
</div>
<!-- Badge-Sammlung (Reise-Utensilien) -->
<div class="kd-overlay" id="ov-badges">
<div class="kd-card" style="max-width:900px">
<h2>🎒 Deine Reise-Fundsachen <span id="badges-count" class="muted" style="font-size:15px;font-weight:600"></span></h2>
<p class="muted" style="font-size:13px">Am Flughafen liegen kleine Fundsachen herum. Hebe sie auf — jede bringt dir ein Badge und eine Reise-Info. Pro Runde erscheinen 3 neue. Hast du alle gesammelt, gibt es beim Zurücksetzen einen goldenen Trolley!</p>
<div id="badges-gold" class="gold-line"></div>
<div id="badges-grid"></div>
<!-- Welt-Erkundungen: Sprach-, Kontinent- und Klassen-Sammlungen -->
<div style="margin-top: 22px; padding-top: 18px; border-top: 2px solid rgba(0,0,0,.08)">
<h3 style="margin: 0 0 4px; font-size: 1.05rem">🌐 Welt-Erkundungen <span id="achievements-count" class="muted" style="font-size:14px;font-weight:600"></span></h3>
<p class="muted" style="font-size:12px;margin:0 0 10px">Löse Länder nach Sprache, Kontinent oder Schwierigkeits-Klasse. Jede Sammlung schaltet ein eigenes Achievement frei.</p>
<div id="achievements-grid"></div>
</div>
<div class="kd-actions">
<button class="kd-btn kd-btn-quiet" id="badges-reset" data-key="g" title="Alle Fundstücke wieder verstecken, damit du sie neu suchen kannst">🔄 Sammlung zurücksetzen <span class="kbd">G</span></button>
<button class="kd-btn kd-btn-primary" id="badges-close" data-key="e">Schließen <span class="kbd">Leertaste</span></button>
</div>
</div>
</div>
<!-- Fall abgeben (Bestätigung) -->
<div class="kd-overlay" id="ov-giveup">
<div class="kd-card" style="max-width:460px;text-align:center">
<img src="assets/img/intro/fall-abgeben.webp" alt="" id="giveup-img"
style="width:200px;border-radius:14px;margin:0 auto 14px;display:block">
<h2>Diesen Fall abgeben?</h2>
<p class="muted">Der Fall wird ohne Lösung geschlossen und bringt keine Punkte.
Du siehst aber die Auflösung — und im nächsten Fall geht es weiter.</p>
<div class="kd-actions" style="justify-content:center">
<button class="kd-btn kd-btn-quiet" id="giveup-no" data-key="e">Weiter ermitteln <span class="kbd">Leertaste</span></button>
<button class="kd-btn kd-btn-danger" id="giveup-yes" data-key="g">Fall abgeben <span class="kbd">G</span></button>
</div>
</div>
</div>
<!-- Reset-Bestätigung -->
<div class="kd-overlay" id="ov-confirm">
<div class="kd-card" style="max-width:420px">
<h2>Fortschritt zurücksetzen?</h2>
<p>Dein aktueller Durchgang geht dabei verloren. Bist du sicher?</p>
<div class="kd-actions">
<button class="kd-btn kd-btn-quiet" id="confirm-no" data-key="e">Abbrechen <span class="kbd">Leertaste</span></button>
<button class="kd-btn kd-btn-danger" id="confirm-yes" data-key="g">Ja, zurücksetzen <span class="kbd">G</span></button>
</div>
</div>
</div>
<!-- Große Karte -->
<div class="kd-overlay" id="ov-bigmap">
<div class="kd-card" id="bigmap-card">
<h2>🗺️ Übersichtskarte</h2>
<canvas id="bigmap-canvas" width="700" height="420"></canvas>
<p class="muted" style="font-size:12.5px;margin-top:8px">
🔴 gemeldetes Gepäckstück · 🟡 gefunden, Land noch offen · 🟢 Fall gelöst · ▲ deine Position</p>
<div class="kd-actions"><button class="kd-btn kd-btn-primary" id="bigmap-close">Schließen (M)</button></div>
</div>
</div>
<script>
// Basis-Pfad: wird vom PHP-Wrapper gesetzt; standalone fällt er auf './' zurück
window.KD_BASE = window.KD_BASE || './';
// Header-Links auf Plattform-Basis umbiegen, falls vorhanden
window.addEventListener('DOMContentLoaded', function () {
var g = window.__GGS__;
if (g && g.basePath) {
['ggs-home-logo', 'ggs-home-link'].forEach(function (id) {
var el = document.getElementById(id);
if (el) el.href = g.basePath + '/schueler';
});
var imgs = document.querySelectorAll('.ggs-header-logo img');
imgs[0].src = g.basePath + '/assets/img/bildLogo.png';
imgs[1].src = g.basePath + '/assets/img/textlogo_geograsim.svg';
}
if (g && g.level) {
document.getElementById('hdr-level').textContent = 'Stufe ' + g.level;
}
});
</script>
<script>
/* Three.js laden: moderne Browser bekommen die Modul-Variante (nötig für die
GLB-Flughafen-Szene via GLTFLoader); alte Browser fallen auf das klassische
Bundle zurück und sehen nur die prozedurale Halle. */
(function () {
var base = window.KD_BASE || './';
var hasImportmap = !!(window.HTMLScriptElement && HTMLScriptElement.supports && HTMLScriptElement.supports('importmap'));
window.__kdModules = hasImportmap;
if (hasImportmap) {
window.__kdThreePromise = new Promise(function (res) { window.__kdThreeReady = res; });
document.write('<script type="importmap">' + JSON.stringify({
imports: { three: base + 'assets/vendor/three.module.js' }
}) + '<\/script>');
document.write('<script type="module">' +
'import * as THREE from "three";' +
'import { GLTFLoader } from "' + base + 'assets/vendor/GLTFLoader.js";' +
'import { MeshoptDecoder } from "' + base + 'assets/vendor/meshopt_decoder.module.js";' +
'import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast } from "' + base + 'assets/vendor/three-mesh-bvh.module.js";' +
'import { RoomEnvironment } from "' + base + 'assets/vendor/RoomEnvironment.js";' +
'window.THREE = THREE;' +
'window.KD_GLTFLoader = GLTFLoader;' +
'window.KD_Meshopt = MeshoptDecoder;' +
'window.KD_RoomEnvironment = RoomEnvironment;' +
'window.KD_BVH = { computeBoundsTree: computeBoundsTree, disposeBoundsTree: disposeBoundsTree, acceleratedRaycast: acceleratedRaycast };' +
'window.__kdThreeReady();' +
'<\/script>');
} else {
window.__kdThreePromise = null;
document.write('<script src="' + base + 'assets/vendor/three.min.js"><\/script>');
}
})();
</script>
<script src="data.js"></script>
<script src="engine.js"></script>
</body>
</html>