11c48817de
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>
791 lines
44 KiB
HTML
791 lines
44 KiB
HTML
<!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>
|