1e51ef7def
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/ - 12 code-workspace-Files - STATUS-*.md - viele M/D/R-Änderungen an bereits getrackten Files - .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
860 lines
36 KiB
HTML
860 lines
36 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||
<title>Staustufen — Wasserkraft in der Kaskade</title>
|
||
<link rel="icon" type="image/png" href="../../favicon-96x96.png" sizes="96x96">
|
||
<link rel="stylesheet" href="../../assets/fonts/inter.css">
|
||
<link rel="stylesheet" href="../../assets/css/design-system.css">
|
||
<style>
|
||
/* ===== Modul-spezifische Styles =====
|
||
Plattform-Klassen aus design-system.css. Hier nur Staustufen-eigenes. */
|
||
|
||
html, body { height: 100%; margin: 0; padding: 0;
|
||
font-family: 'Inter', system-ui, sans-serif;
|
||
background: var(--ggs-bg); color: var(--ggs-text);
|
||
touch-action: manipulation; }
|
||
|
||
.st-layout { display: grid; grid-template-rows: auto 1fr auto;
|
||
height: 100dvh; }
|
||
|
||
.st-main {
|
||
display: grid;
|
||
grid-template-columns: 240px 1fr;
|
||
gap: 12px; padding: 12px;
|
||
min-height: 0;
|
||
}
|
||
@media (max-width: 1199px) and (min-width: 900px) {
|
||
.st-main { grid-template-columns: 215px 1fr; }
|
||
}
|
||
@media (max-width: 899px) {
|
||
.st-main { grid-template-columns: 1fr;
|
||
grid-template-rows: auto 1fr; }
|
||
}
|
||
|
||
.st-panel { background: var(--ggs-white, #fff);
|
||
border-radius: var(--ggs-radius-md);
|
||
padding: 14px;
|
||
box-shadow: var(--ggs-shadow-sm);
|
||
overflow-y: auto;
|
||
}
|
||
.st-panel h3 { margin: 0 0 10px;
|
||
font-size: 0.95rem; color: var(--ggs-fjord-dark); }
|
||
|
||
/* === Status-Karten (links) === */
|
||
.st-kpi { margin-bottom: 12px; }
|
||
.st-kpi-label { font-size: 0.74rem; color: var(--ggs-text-muted);
|
||
text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 2px; }
|
||
.st-kpi-value { font-size: 1.3rem; font-weight: 700;
|
||
color: var(--ggs-fjord-dark); font-variant-numeric: tabular-nums; }
|
||
.st-kpi-sub { font-size: 0.78rem; color: var(--ggs-text-muted); }
|
||
|
||
.st-day-pill { display: inline-block; padding: 4px 12px;
|
||
border-radius: var(--ggs-radius-pill, 16px);
|
||
background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark);
|
||
font-weight: 700; font-size: 0.85rem; margin-bottom: 10px; }
|
||
|
||
.st-progressbar { height: 8px; background: #e8e4d8;
|
||
border-radius: 4px; overflow: hidden; margin-top: 4px; }
|
||
.st-progressbar-fill { height: 100%; background: var(--ggs-moss);
|
||
transition: width 0.3s ease; }
|
||
.st-progressbar-fill.warn { background: #d6a04e; }
|
||
.st-progressbar-fill.danger { background: var(--ggs-coral); }
|
||
|
||
/* Korridor-Status-Farben (Atlas-Update Netzbalance) */
|
||
.st-balance-green { color: var(--ggs-moss-dark); }
|
||
.st-balance-yellow { color: #b07a1f; }
|
||
.st-balance-red { color: var(--ggs-coral); }
|
||
|
||
/* === Bipolar-Live-Meter === Plus/Minus von der Mitte weg.
|
||
Skala: −50 % (Lücke ganz links) → 0 (Korridor-Mitte) → +50 % (Überschuss).
|
||
Werte über ±50 % werden visuell gecappt. */
|
||
.st-livemeter {
|
||
position: relative; height: 56px; margin: 6px 0 4px;
|
||
}
|
||
.st-livemeter-track {
|
||
position: absolute; left: 0; right: 0; top: 4px; height: 18px;
|
||
border-radius: 9px;
|
||
background: linear-gradient(90deg,
|
||
#c85c4a 0%, /* rote Lücke */
|
||
#c85c4a 20%,
|
||
#d6a04e 20%, #d6a04e 35%, /* gelbe Lücke */
|
||
#5a8a5e 35%, #5a8a5e 50%, /* grüner Korridor links */
|
||
#5a8a5e 50%, #5a8a5e 65%, /* grüner Korridor rechts */
|
||
#d6a04e 65%, #d6a04e 80%, /* gelber Überschuss */
|
||
#c85c4a 80%, #c85c4a 100%); /* roter Überschuss */
|
||
box-shadow: inset 0 1px 2px rgba(0,0,0,.15);
|
||
}
|
||
/* Mittellinie für 0 — kontrastierender Strich */
|
||
.st-livemeter-track::after {
|
||
content: ''; position: absolute;
|
||
left: 50%; top: -2px; width: 2px; height: 22px;
|
||
background: #fff; box-shadow: 0 0 0 1px #455a64;
|
||
transform: translateX(-50%); border-radius: 1px;
|
||
}
|
||
/* Indikator-Nadel — wird per JS positioniert */
|
||
.st-livemeter-needle {
|
||
position: absolute; top: 0; left: 50%;
|
||
width: 4px; height: 26px;
|
||
background: #1a1a1a; border: 1.5px solid #fff;
|
||
border-radius: 2px;
|
||
transform: translateX(-50%);
|
||
box-shadow: 0 1px 3px rgba(0,0,0,.4);
|
||
transition: left 0.4s ease;
|
||
pointer-events: none;
|
||
}
|
||
.st-livemeter-needle::after {
|
||
content: ''; position: absolute;
|
||
top: -4px; left: 50%; transform: translateX(-50%);
|
||
width: 0; height: 0;
|
||
border-left: 5px solid transparent;
|
||
border-right: 5px solid transparent;
|
||
border-bottom: 5px solid #1a1a1a;
|
||
}
|
||
.st-livemeter-axis {
|
||
position: absolute; left: 0; right: 0; top: 28px;
|
||
display: flex; justify-content: space-between;
|
||
font-size: 0.62rem; color: var(--ggs-text-muted);
|
||
line-height: 1.15; padding: 0 2px;
|
||
}
|
||
.st-livemeter-axis span { text-align: center; flex: 0 0 auto; }
|
||
.st-livemeter-axis span:first-child { text-align: left; }
|
||
.st-livemeter-axis span:last-child { text-align: right; }
|
||
.st-livemeter-axis b { font-weight: 700; color: var(--ggs-text); }
|
||
|
||
/* === Querschnitt-Canvas (Mitte) === */
|
||
.st-canvas { background: var(--ggs-white, #fff);
|
||
border-radius: var(--ggs-radius-md);
|
||
box-shadow: var(--ggs-shadow-sm);
|
||
display: grid;
|
||
grid-template-rows: 1fr auto;
|
||
min-height: 0; overflow: hidden;
|
||
position: relative;
|
||
}
|
||
.st-cross-section {
|
||
width: 100%; height: 100%;
|
||
display: block;
|
||
background: linear-gradient(180deg, #d6e7ee 0%, #e8eef0 65%, #f0ebde 100%);
|
||
}
|
||
|
||
/* Bergkörper */
|
||
.st-mountain { fill: #4a7c4e; }
|
||
.st-mountain-shade{ fill: #2f5a32; opacity: 0.55; }
|
||
.st-mountain-rock { fill: #6a6a6a; opacity: 0.18; }
|
||
.st-snow { fill: #fafaf6; }
|
||
.st-tal-floor { fill: #d8d2c0; }
|
||
.st-tal-grass { fill: #b8c89b; }
|
||
|
||
/* Wasser */
|
||
.st-water-top, .st-water-bot {
|
||
fill: url(#stWaterGrad);
|
||
transition: y 0.6s ease, height 0.6s ease;
|
||
}
|
||
.st-water-top.danger, .st-water-bot.danger { fill: #c85c4a; opacity: 0.85; }
|
||
|
||
/* Druckrohre — graue Schale (Vorlage-Stil) */
|
||
.st-pipe-shell {
|
||
stroke: #9E9E9E; stroke-width: 18;
|
||
stroke-linejoin: round; stroke-linecap: round;
|
||
fill: none;
|
||
}
|
||
|
||
/* Wasser-Animation im Rohr — state-driven via Klassen.
|
||
Pfad-Richtung: Pfad A geht Oberbecken→Maschinenhaus,
|
||
Pfad B Maschinenhaus→Unterbecken. Im Turbinenbetrieb wandern Striche
|
||
in Pfad-Richtung (forward), im Pumpbetrieb gegen die Richtung. */
|
||
.st-pipe-flow {
|
||
stroke-width: 3; fill: none;
|
||
stroke-linecap: round;
|
||
stroke-dasharray: 15 10;
|
||
stroke-dashoffset: 0;
|
||
opacity: 0;
|
||
transition: opacity 0.25s ease, stroke 0.25s ease;
|
||
}
|
||
.st-pipe-flow.active.turbine { stroke: #01579B; opacity: 1;
|
||
animation: st-flow-fwd var(--st-flow-dur, 1.5s) linear infinite; }
|
||
.st-pipe-flow.active.pump { stroke: #00E676; opacity: 1;
|
||
animation: st-flow-bwd var(--st-flow-dur, 1.5s) linear infinite; }
|
||
@keyframes st-flow-fwd { to { stroke-dashoffset: -25; } }
|
||
@keyframes st-flow-bwd { to { stroke-dashoffset: 25; } }
|
||
|
||
.st-pump-symbol, .st-turb-symbol {
|
||
transition: filter 0.2s ease, transform 0.2s ease;
|
||
transform-box: fill-box; transform-origin: center;
|
||
}
|
||
.st-pump-symbol.active { filter: drop-shadow(0 0 6px rgba(74,124,138,.6)); }
|
||
.st-turb-symbol.active { filter: drop-shadow(0 0 6px rgba(90,138,94,.6)); }
|
||
|
||
/* Beschriftungen auf dem Querschnitt — Lesbarkeits-Outline,
|
||
font-size kommt vom SVG-Attribut (Vorlage-Werte 13..20). */
|
||
.st-cs-label {
|
||
font-family: 'Segoe UI', 'Inter', Roboto, system-ui, sans-serif;
|
||
paint-order: stroke;
|
||
stroke: rgba(255,255,255,.85);
|
||
stroke-width: 4px;
|
||
stroke-linejoin: round;
|
||
}
|
||
.st-cs-pct {
|
||
font-family: 'Segoe UI', 'Inter', Roboto, system-ui, sans-serif;
|
||
font-variant-numeric: tabular-nums;
|
||
paint-order: stroke;
|
||
stroke: rgba(255,255,255,.9);
|
||
stroke-width: 4px;
|
||
stroke-linejoin: round;
|
||
}
|
||
|
||
/* === Bidirektionaler Schieberegler — Bedien-Footer unter dem Querschnitt ===
|
||
Als HTML-Element (NICHT foreignObject — auf iPad/iOS Safari unzuverlässig).
|
||
Mittelpunkt = aus, links Pumpe (grün), rechts Turbine (blau).
|
||
Werte stufenlos von −100 bis +100. */
|
||
.st-machine-control {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
grid-template-rows: auto auto;
|
||
gap: 6px 10px;
|
||
align-items: center;
|
||
padding: 10px 16px 14px;
|
||
background: linear-gradient(180deg, #ECEFF1 0%, #CFD8DC 100%);
|
||
border-top: 2px solid #90A4AE;
|
||
box-sizing: border-box;
|
||
font-family: 'Inter', system-ui, sans-serif;
|
||
}
|
||
.st-machine-readout {
|
||
font-size: 13px; font-weight: 700; color: #263238;
|
||
text-align: center; grid-column: 1 / -1;
|
||
font-variant-numeric: tabular-nums;
|
||
text-transform: uppercase; letter-spacing: 0.04em;
|
||
padding: 2px 0;
|
||
}
|
||
.st-bipolar-wrap {
|
||
display: grid; grid-template-columns: auto 1fr auto;
|
||
align-items: center; gap: 8px;
|
||
}
|
||
.st-bipolar-end {
|
||
font-size: 11px; font-weight: 700; user-select: none;
|
||
white-space: nowrap;
|
||
}
|
||
.st-bipolar-end.pump-end { color: #2E7D32; }
|
||
.st-bipolar-end.turb-end { color: #01579B; }
|
||
.st-bipolar-zero {
|
||
appearance: none; background: #ECEFF1;
|
||
border: 1px solid #B0BEC5; border-radius: 50%;
|
||
width: 38px; height: 38px;
|
||
cursor: pointer; font-size: 14px; line-height: 1;
|
||
color: #607D8B; padding: 0;
|
||
font-family: inherit; font-weight: 700;
|
||
touch-action: manipulation;
|
||
}
|
||
.st-bipolar-zero:hover { background: #fff; color: #455A64; }
|
||
.st-bipolar-zero:active { background: #B0BEC5; color: #fff; }
|
||
|
||
.st-bipolar-slider {
|
||
-webkit-appearance: none; appearance: none;
|
||
width: 100%; height: 36px;
|
||
background: transparent; cursor: pointer;
|
||
touch-action: manipulation;
|
||
margin: 0; padding: 0;
|
||
}
|
||
.st-bipolar-slider:focus { outline: none; }
|
||
/* Track: links Pumpe-grün-Bereich, Mitte hell, rechts Turbine-blau-Bereich.
|
||
CSS-Variable --st-thumb-pct steuert Highlight ab Mitte zur Thumb-Position. */
|
||
.st-bipolar-slider::-webkit-slider-runnable-track {
|
||
height: 8px; border-radius: 4px;
|
||
background: linear-gradient(90deg,
|
||
rgba(46,125,50,.18) 0%, rgba(46,125,50,.18) 50%,
|
||
rgba(1,87,155,.18) 50%, rgba(1,87,155,.18) 100%);
|
||
}
|
||
.st-bipolar-slider.mode-turbine::-webkit-slider-runnable-track {
|
||
background: linear-gradient(90deg,
|
||
rgba(46,125,50,.18) 0%, rgba(46,125,50,.18) 50%,
|
||
#01579B 50%, #01579B var(--st-thumb-pct, 50%),
|
||
rgba(1,87,155,.18) var(--st-thumb-pct, 50%), rgba(1,87,155,.18) 100%);
|
||
}
|
||
.st-bipolar-slider.mode-pump::-webkit-slider-runnable-track {
|
||
background: linear-gradient(90deg,
|
||
rgba(46,125,50,.18) 0%, rgba(46,125,50,.18) var(--st-thumb-pct, 50%),
|
||
#00C853 var(--st-thumb-pct, 50%), #00C853 50%,
|
||
rgba(1,87,155,.18) 50%, rgba(1,87,155,.18) 100%);
|
||
}
|
||
/* Mittelmarkierung: zarter Strich bei 50 % */
|
||
.st-bipolar-wrap { position: relative; }
|
||
.st-bipolar-wrap::after {
|
||
content: ''; position: absolute;
|
||
left: calc(50% + 4px); transform: translateX(-50%);
|
||
top: 50%; margin-top: -7px;
|
||
width: 2px; height: 14px;
|
||
background: #607D8B; border-radius: 1px;
|
||
pointer-events: none; opacity: 0.7;
|
||
}
|
||
/* Thumb — iPad-Touch-Target ≥ 36 px */
|
||
.st-bipolar-slider::-webkit-slider-thumb {
|
||
-webkit-appearance: none; appearance: none;
|
||
width: 38px; height: 38px; border-radius: 19px;
|
||
background: #fff;
|
||
border: 3px solid #455A64;
|
||
margin-top: -15px;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,.25);
|
||
cursor: grab;
|
||
}
|
||
.st-bipolar-slider.mode-pump::-webkit-slider-thumb { border-color: #2E7D32; }
|
||
.st-bipolar-slider.mode-turbine::-webkit-slider-thumb { border-color: #01579B; }
|
||
.st-bipolar-slider:active::-webkit-slider-thumb { cursor: grabbing; }
|
||
.st-bipolar-slider::-moz-range-track {
|
||
height: 8px; border-radius: 4px; background: #ECEFF1;
|
||
}
|
||
.st-bipolar-slider::-moz-range-thumb {
|
||
width: 36px; height: 36px; border-radius: 18px;
|
||
background: #fff;
|
||
border: 3px solid #455A64;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,.25);
|
||
cursor: grab;
|
||
}
|
||
.st-bipolar-slider.mode-pump::-moz-range-thumb { border-color: #2E7D32; }
|
||
.st-bipolar-slider.mode-turbine::-moz-range-thumb { border-color: #01579B; }
|
||
|
||
/* Tick-Skala unter dem Slider — 21 Striche je 10 %, längere bei 0/±50/±100 */
|
||
.st-bipolar-slider-area { position: relative; padding-bottom: 24px; }
|
||
.st-tick-rail {
|
||
position: absolute; left: 18px; right: 18px; top: 32px;
|
||
display: flex; justify-content: space-between;
|
||
pointer-events: none;
|
||
}
|
||
.st-tick-rail .tick {
|
||
width: 1.5px; height: 7px; background: #90A4AE; border-radius: 1px;
|
||
}
|
||
.st-tick-rail .tick.t-mid { height: 11px; background: #607D8B; }
|
||
.st-tick-rail .tick.t-zero { height: 14px; width: 2.5px; background: #263238; }
|
||
.st-tick-rail .tick.t-100 { height: 11px; background: #607D8B; }
|
||
.st-tick-labels {
|
||
position: absolute; left: 14px; right: 14px; top: 50px;
|
||
display: flex; justify-content: space-between;
|
||
font-size: 10.5px; color: #455A64; font-weight: 700;
|
||
pointer-events: none; user-select: none;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
/* === Bedarfs-Graph (unten) === */
|
||
.st-graph-zone {
|
||
background: var(--ggs-white, #fff);
|
||
border-top: 1px solid var(--ggs-border, #e2ddd1);
|
||
padding: 12px 16px; height: 240px;
|
||
display: flex; gap: 16px; align-items: stretch;
|
||
}
|
||
.st-graph-meta { width: 200px; flex-shrink: 0;
|
||
display: flex; flex-direction: column; gap: 4px;
|
||
font-size: 0.78rem; color: var(--ggs-text-muted); }
|
||
.st-graph-meta b { color: var(--ggs-text);
|
||
font-variant-numeric: tabular-nums; font-weight: 700; }
|
||
.st-graph-meta .st-legend-row { display: flex; align-items: center; gap: 6px; }
|
||
.st-graph-swatch { display: inline-block; width: 14px; height: 4px;
|
||
border-radius: 2px; }
|
||
.st-graph-swatch.demand { background: #c85c4a; }
|
||
.st-graph-swatch.sun { background: #e8a82e; }
|
||
.st-graph-swatch.supply { background: #1f4e5a; }
|
||
.st-graph-swatch.cursor { background: var(--ggs-fjord); height: 14px; width: 2px; }
|
||
|
||
.st-graph-svg { flex: 1; min-width: 0; }
|
||
|
||
/* === Speed-Controls === */
|
||
.st-speed { display: flex; gap: 4px; }
|
||
.st-speed-btn { min-width: 36px; min-height: 36px;
|
||
padding: 4px 10px;
|
||
border: 1px solid var(--ggs-border, #e2ddd1);
|
||
background: var(--ggs-white, #fff); color: var(--ggs-text);
|
||
border-radius: 6px; cursor: pointer;
|
||
font-size: 0.85rem; font-weight: 600;
|
||
font-family: inherit;
|
||
}
|
||
.st-speed-btn:hover { background: var(--ggs-bg-dark, #e8e4d8); }
|
||
.st-speed-btn.active { background: var(--ggs-fjord);
|
||
color: #fff; border-color: var(--ggs-fjord-dark); }
|
||
|
||
/* === InfoCards (slide-in von rechts, nicht blockierend) ===
|
||
Pattern: Logistik-Infocards. Triggern bei didaktisch passenden Momenten,
|
||
verschwinden automatisch nach ~8 s oder per Klick. */
|
||
.st-infocards {
|
||
position: fixed; top: 76px; right: 16px; z-index: 1200;
|
||
display: flex; flex-direction: column; gap: 10px;
|
||
pointer-events: none;
|
||
max-width: calc(100% - 32px);
|
||
}
|
||
.st-infocard {
|
||
background: #fff; border-radius: 12px; overflow: hidden;
|
||
box-shadow: 0 8px 24px rgba(0,0,0,.18);
|
||
width: 320px; pointer-events: auto;
|
||
transform: translateX(380px); opacity: 0;
|
||
transition: transform .35s ease, opacity .35s ease;
|
||
border-left: 4px solid var(--ggs-fjord);
|
||
}
|
||
.st-infocard.visible { transform: translateX(0); opacity: 1; }
|
||
.st-infocard.dismissing { transform: translateX(380px); opacity: 0; }
|
||
.st-infocard.tone-pump { border-left-color: var(--ggs-fjord); }
|
||
.st-infocard.tone-turbine { border-left-color: var(--ggs-moss); }
|
||
.st-infocard.tone-warn { border-left-color: #d6a04e; }
|
||
.st-infocard.tone-spike { border-left-color: var(--ggs-coral); }
|
||
.st-infocard-img {
|
||
width: 100%; height: 130px;
|
||
background: var(--ggs-fjord-light);
|
||
background-size: cover; background-position: center;
|
||
}
|
||
.st-infocard-body { padding: 12px 14px 14px; }
|
||
.st-infocard-title {
|
||
font-size: 0.95rem; font-weight: 700;
|
||
color: var(--ggs-fjord-dark); margin: 0 0 5px;
|
||
display: flex; justify-content: space-between;
|
||
align-items: center; gap: 6px;
|
||
}
|
||
.st-infocard-title-text { flex: 1; min-width: 0; }
|
||
.st-infocard-text {
|
||
font-size: 0.85rem; line-height: 1.5;
|
||
color: var(--ggs-text); margin: 0;
|
||
}
|
||
.st-infocard-text b { color: var(--ggs-fjord-dark); }
|
||
.st-infocard-close {
|
||
background: none; border: 0; cursor: pointer;
|
||
font-size: 18px; color: var(--ggs-text-muted);
|
||
padding: 0; min-width: 28px; min-height: 28px;
|
||
line-height: 1; flex-shrink: 0;
|
||
border-radius: 4px; font-family: inherit;
|
||
}
|
||
.st-infocard-close:hover {
|
||
color: var(--ggs-text); background: var(--ggs-bg-dark, #e8e4d8);
|
||
}
|
||
|
||
/* === Mute-Toggle im Header === */
|
||
.ggs-btn-ghost.muted { opacity: 0.55; }
|
||
|
||
/* === Hint-Overlay === */
|
||
.st-hint-overlay {
|
||
position: fixed; inset: 0; background: rgba(20,40,50,.55);
|
||
backdrop-filter: blur(4px);
|
||
display: none; align-items: center; justify-content: center;
|
||
z-index: 9000; padding: 16px;
|
||
}
|
||
.st-hint-overlay.open { display: flex; }
|
||
.st-hint-modal { background: #fff; border-radius: 12px;
|
||
padding: 24px 28px; max-width: 480px; width: 100%;
|
||
box-shadow: 0 12px 40px rgba(0,0,0,.3);
|
||
}
|
||
.st-hint-modal h2 { margin: 0 0 8px; color: var(--ggs-fjord-dark);
|
||
font-size: 1.15rem; }
|
||
.st-hint-modal p { margin: 0 0 12px; color: var(--ggs-text);
|
||
font-size: 0.95rem; line-height: 1.55; }
|
||
.st-hint-modal-actions { text-align: right; margin-top: 14px; }
|
||
.st-hint-modal-actions .ggs-btn-primary {
|
||
min-height: 40px; padding: 8px 18px;
|
||
background: var(--ggs-fjord); color: #fff; border: none;
|
||
border-radius: 8px; cursor: pointer; font-weight: 600;
|
||
font-family: inherit; font-size: 0.9rem;
|
||
}
|
||
|
||
/* End-Screen-Body-Spezifika (nutzt .ggs-endscreen aus Design-System) */
|
||
.st-endscreen-grade {
|
||
font-size: 4rem; font-weight: 800; line-height: 1;
|
||
margin: 8px 0 4px;
|
||
}
|
||
.st-endscreen-grade.A { color: var(--ggs-moss-dark); }
|
||
.st-endscreen-grade.B { color: var(--ggs-moss); }
|
||
.st-endscreen-grade.C { color: #d6a04e; }
|
||
.st-endscreen-grade.D { color: #c47744; }
|
||
.st-endscreen-grade.E { color: var(--ggs-coral); }
|
||
|
||
.st-endscreen-detail { font-size: 0.88rem; line-height: 1.5;
|
||
margin-bottom: 10px; color: var(--ggs-text); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<script>
|
||
/*__STAUSTUFEN_INJECTION__*/
|
||
|
||
/* Fallback fuer Direkt-Aufruf der Datei (ohne Wrapper) */
|
||
if (typeof window.STAUSTUFEN_BASE === 'undefined') {
|
||
window.STAUSTUFEN_BASE = '../../sims/staustufen/';
|
||
window.STAUSTUFEN_SESSION_MODE = 'free';
|
||
window.STAUSTUFEN_FORCED_LEVEL = null;
|
||
window.STAUSTUFEN_SESSION_ID = null;
|
||
window.STUDENT_EASY = false;
|
||
window.STAUSTUFEN_API_BASE = '/geograsim/App/api';
|
||
}
|
||
</script>
|
||
|
||
<div class="st-layout">
|
||
|
||
<!-- ============= Header (Plattform-Standard) ============= -->
|
||
<header class="ggs-header">
|
||
<a href="../../" class="ggs-header-logo">
|
||
<img src="../../assets/img/bildLogo.png" alt="" class="ggs-header-logo-icon">
|
||
<img src="../../assets/img/textlogo_geograsim.svg" alt="GeoGraSim" class="ggs-header-logo-text">
|
||
</a>
|
||
<div class="ggs-header-separator"></div>
|
||
<span class="ggs-header-module">Staustufen — Wasserkraft in der Kaskade</span>
|
||
<span class="ggs-header-badge" id="levelBadge">Level 1</span>
|
||
|
||
<div class="ggs-header-spacer"></div>
|
||
|
||
<div class="ggs-header-actions">
|
||
<div class="st-speed" role="group" aria-label="Tempo">
|
||
<button class="st-speed-btn" id="speedPause" title="Pause" aria-label="Pause">⏸</button>
|
||
<button class="st-speed-btn active" id="speed1x" title="Normales Tempo">1×</button>
|
||
<button class="st-speed-btn" id="speed4x" title="Schneller Vorlauf">4×</button>
|
||
</div>
|
||
<button class="ggs-btn-ghost muted" id="btnAudioToggle"
|
||
title="Sprachausgabe an/aus" aria-label="Sprachausgabe umschalten"
|
||
aria-pressed="false">🔇</button>
|
||
<a class="ggs-btn-ghost" href="../../schueler" title="Zurück zum Cockpit"
|
||
aria-label="Zurück zum Cockpit">🏠</a>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- InfoCard-Stapel (slide-in von rechts) -->
|
||
<div class="st-infocards" id="stInfocards" aria-live="polite"></div>
|
||
|
||
<!-- ============= Hauptbereich ============= -->
|
||
<main class="st-main">
|
||
|
||
<!-- Status-Spalte links -->
|
||
<aside class="st-panel" id="statusPanel">
|
||
<span class="st-day-pill" id="dayPill">Tag 1 / 5</span>
|
||
|
||
<!-- Live-Netzbalance: Bipolar-Meter, Mitte = im Korridor,
|
||
links = Lücke (Verbrauch > Versorgung), rechts = Überschuss -->
|
||
<div class="st-kpi">
|
||
<div class="st-kpi-label">Netzbalance jetzt</div>
|
||
<div class="st-livemeter">
|
||
<div class="st-livemeter-track" aria-hidden="true"></div>
|
||
<div class="st-livemeter-needle" id="livemeterNeedle"></div>
|
||
<div class="st-livemeter-axis">
|
||
<span>−50 %<br><b>Lücke</b></span>
|
||
<span>0</span>
|
||
<span>+50 %<br><b>Überschuss</b></span>
|
||
</div>
|
||
</div>
|
||
<div class="st-kpi-value st-balance-green" id="kpiKorridor"
|
||
style="font-size:0.95rem; margin-top:6px;">🟢 im Korridor</div>
|
||
<div class="st-kpi-sub" id="kpiKorridorSub">±15 % um den Bedarf</div>
|
||
</div>
|
||
|
||
<div class="st-kpi">
|
||
<div class="st-kpi-label">Anteil im Korridor (bisher)</div>
|
||
<div class="st-kpi-value" id="kpiNetzbalance"
|
||
style="font-size:1.05rem;">100 %</div>
|
||
<div class="st-progressbar">
|
||
<div class="st-progressbar-fill" id="kpiNetzbalanceBar"
|
||
style="width:100%"></div>
|
||
</div>
|
||
<div class="st-kpi-sub" id="kpiNetzbalanceSub">Bisher im Gleichgewicht.</div>
|
||
</div>
|
||
|
||
<div class="st-kpi">
|
||
<div class="st-kpi-label">Verluste</div>
|
||
<div class="st-kpi-value" id="kpiVerluste"
|
||
style="font-size:1.05rem;">0 GWh</div>
|
||
<div class="st-kpi-sub">Pumpen + Turbinieren</div>
|
||
</div>
|
||
|
||
<div class="st-kpi">
|
||
<div class="st-kpi-label">Tageszeit</div>
|
||
<div class="st-kpi-value" id="kpiUhrzeit"
|
||
style="font-size:1.05rem;">00:00</div>
|
||
<div class="st-kpi-sub" id="kpiTickInfo">Tick 0 / 12</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- Sim-Zone Mitte: Querschnitt-SVG (Vorlage-Stil, state-driven) -->
|
||
<section class="st-canvas">
|
||
|
||
<svg class="st-cross-section" viewBox="0 0 1000 650"
|
||
preserveAspectRatio="xMidYMid meet"
|
||
aria-label="Querschnitt einer Pumpspeicher-Anlage">
|
||
<defs>
|
||
<linearGradient id="stSkyGrad" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0%" stop-color="#4FC3F7"/>
|
||
<stop offset="100%" stop-color="#E1F5FE"/>
|
||
</linearGradient>
|
||
<linearGradient id="stEarthGrad" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0%" stop-color="#6D4C41"/>
|
||
<stop offset="100%" stop-color="#3E2723"/>
|
||
</linearGradient>
|
||
<linearGradient id="stWaterGrad" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0%" stop-color="rgba(3,169,244,0.6)"/>
|
||
<stop offset="100%" stop-color="rgba(2,119,189,0.85)"/>
|
||
</linearGradient>
|
||
<clipPath id="stUpperLakeClip">
|
||
<polygon points="660,140 910,140 850,200 680,200"/>
|
||
</clipPath>
|
||
<clipPath id="stLowerLakeClip">
|
||
<polygon points="130,420 383,420 300,480 150,480"/>
|
||
</clipPath>
|
||
|
||
<!-- Wiederverwendbare Symbole (aus Vorlage) -->
|
||
<g id="stTree">
|
||
<polygon points="0,-15 -8,0 8,0" fill="#2E7D32"/>
|
||
<polygon points="0,-25 -6,-8 6,-8" fill="#388E3C"/>
|
||
<rect x="-1.5" y="0" width="3" height="4" fill="#5D4037"/>
|
||
</g>
|
||
<g id="stBird">
|
||
<path d="M -10,-5 Q -5,-15 0,-5 Q 5,-15 10,-5 Q 5,-8 0,-2 Q -5,-8 -10,-5 Z"
|
||
fill="#424242"/>
|
||
</g>
|
||
<g id="stFish">
|
||
<ellipse cx="0" cy="0" rx="6" ry="3" fill="#FF9800"/>
|
||
<polygon points="-5,0 -10,-4 -10,4" fill="#FF9800"/>
|
||
<circle cx="3" cy="-1" r="0.8" fill="#212121"/>
|
||
</g>
|
||
</defs>
|
||
|
||
<!-- Himmel + Sonne -->
|
||
<rect width="1000" height="650" fill="url(#stSkyGrad)"/>
|
||
<circle cx="850" cy="80" r="40" fill="#FFF59D" opacity="0.8"/>
|
||
|
||
<!-- Bergsilhouetten (Tiefen-Layer) -->
|
||
<path d="M 0,400 L 150,220 L 350,380 L 600,150 L 800,280 L 1000,120 L 1000,500 L 0,500 Z"
|
||
fill="#CFD8DC" opacity="0.7"/>
|
||
<path d="M 0,450 L 250,280 L 500,420 L 800,200 L 1000,300 L 1000,500 L 0,500 Z"
|
||
fill="#B0BEC5" opacity="0.5"/>
|
||
|
||
<!-- Berg-/Erdkörper (Hauptpfad, Querschnitt) -->
|
||
<path d="M 0,650 L 1000,650 L 1000,50 L 850,200 L 680,200 L 620,250 L 300,480 L 150,480 L 100,500 L 0,500 Z"
|
||
fill="url(#stEarthGrad)"/>
|
||
|
||
<!-- Schnee auf rechtem Berg -->
|
||
<path d="M 1000,50 L 850,200 L 890,200 L 920,160 L 940,180 L 970,120 L 1000,150 Z"
|
||
fill="#FFFFFF" opacity="0.9"/>
|
||
|
||
<!-- Druckrohre (Schale) — zwei Pfade, beidseits des Maschinenhauses -->
|
||
<polyline class="st-pipe-shell" points="750,200 750,330 400,530 330,530"/>
|
||
<polyline class="st-pipe-shell" points="270,530 200,530 200,480"/>
|
||
|
||
<!-- Staumauern -->
|
||
<polygon points="650,120 670,120 680,200 620,250" fill="#90A4AE"
|
||
stroke="#607D8B" stroke-width="2"/>
|
||
<path d="M 650,120 L 670,120 L 680,200" fill="none" stroke="#ECEFF1" stroke-width="2"/>
|
||
<polygon points="120,400 140,400 150,480 100,500" fill="#90A4AE"
|
||
stroke="#607D8B" stroke-width="2"/>
|
||
<path d="M 120,400 L 140,400 L 150,480" fill="none" stroke="#ECEFF1" stroke-width="2"/>
|
||
|
||
<!-- Wasser im Oberbecken (state-driven, height/y) -->
|
||
<rect class="st-water-top" id="stWaterTop"
|
||
x="660" y="140" width="250" height="60"
|
||
clip-path="url(#stUpperLakeClip)"/>
|
||
<line x1="660" y1="140" x2="910" y2="140" stroke="#81D4FA" stroke-width="2"/>
|
||
|
||
<!-- Wasser im Unterbecken (state-driven) -->
|
||
<rect class="st-water-bot" id="stWaterBot"
|
||
x="130" y="420" width="253" height="60"
|
||
clip-path="url(#stLowerLakeClip)"/>
|
||
<line x1="130" y1="420" x2="383" y2="420" stroke="#81D4FA" stroke-width="2"/>
|
||
|
||
<!-- Wiesenränder neben Becken/Tal -->
|
||
<path d="M 0,500 L 100,500" fill="none" stroke="#689F38" stroke-width="8" stroke-linecap="round"/>
|
||
<path d="M 300,480 L 620,250" fill="none" stroke="#689F38" stroke-width="8" stroke-linecap="round"/>
|
||
<path d="M 850,200 L 900,150" fill="none" stroke="#689F38" stroke-width="8" stroke-linecap="round"/>
|
||
|
||
<!-- Bäume entlang Hängen + Tal -->
|
||
<use href="#stTree" x="20" y="496" transform="scale(1.2) translate(-5, 0)"/>
|
||
<use href="#stTree" x="40" y="496"/>
|
||
<use href="#stTree" x="65" y="496" transform="scale(1.5) translate(-22, 0)"/>
|
||
<use href="#stTree" x="480" y="350"/>
|
||
<use href="#stTree" x="510" y="325" transform="scale(1.3) translate(-120, -55)"/>
|
||
<use href="#stTree" x="550" y="300"/>
|
||
|
||
<!-- Vögel + Fische — Atmosphäre -->
|
||
<use href="#stBird" x="650" y="70" transform="scale(0.8)"/>
|
||
<use href="#stBird" x="680" y="50" transform="scale(0.6)"/>
|
||
<use href="#stFish" x="800" y="170"/>
|
||
<use href="#stFish" x="760" y="185" transform="scale(0.8) translate(50, 40)"/>
|
||
<use href="#stFish" x="220" y="450" transform="scale(1.2) translate(-35, -75)"/>
|
||
|
||
<!-- Wanderer auf dem Tal-Pfad -->
|
||
<g transform="translate(420, 395)">
|
||
<rect x="0" y="3" width="16" height="9" rx="3" fill="#795548"/>
|
||
<rect x="2" y="11" width="2" height="6" fill="#4E342E"/>
|
||
<rect x="12" y="11" width="2" height="6" fill="#4E342E"/>
|
||
<circle cx="17" cy="4" r="3.5" fill="#795548"/>
|
||
<polygon points="17,1 15,-2 19,-2" fill="#795548"/>
|
||
<circle cx="16" cy="9" r="1.5" fill="#FFC107"/>
|
||
</g>
|
||
|
||
<!-- Maschinenhaus (Kavernen-Optik) -->
|
||
<rect x="250" y="470" width="100" height="80" rx="10" fill="#BDBDBD"/>
|
||
<rect x="255" y="475" width="90" height="70" rx="6" fill="#F5F5F5"/>
|
||
|
||
<!-- Türen + Symbole — Pumpe links (grün), Turbine rechts (blau) -->
|
||
<rect x="290" y="500" width="20" height="35" rx="3" fill="#D32F2F"/>
|
||
<polygon class="st-turb-symbol" id="stTurbSymbol"
|
||
points="310,517 330,505 330,530" fill="#0288D1"/>
|
||
<polygon class="st-pump-symbol" id="stPumpSymbol"
|
||
points="290,517 270,505 270,530" fill="#388E3C"/>
|
||
|
||
<!-- Helle Innenröhre (Hintergrund-Layer für Animation) -->
|
||
<polyline points="750,200 750,330 400,530 330,530" fill="none"
|
||
stroke="#B3E5FC" stroke-width="10" stroke-linejoin="round"/>
|
||
<polyline points="270,530 200,530 200,480" fill="none"
|
||
stroke="#B3E5FC" stroke-width="10" stroke-linejoin="round"/>
|
||
|
||
<!-- Wasser-Animation: zwei Pfade, beide gleichzeitig je nach Modus -->
|
||
<polyline class="st-pipe-flow" id="stFlowA"
|
||
points="750,200 750,330 400,530 330,530"
|
||
fill="none" stroke-linejoin="round"/>
|
||
<polyline class="st-pipe-flow" id="stFlowB"
|
||
points="270,530 200,530 200,480"
|
||
fill="none" stroke-linejoin="round"/>
|
||
|
||
<!-- Strommast + Stromleitungen, links unten -->
|
||
<g stroke="#424242" stroke-width="2" fill="none">
|
||
<line x1="50" y1="500" x2="50" y2="430"/>
|
||
<polygon points="50,430 40,490 60,490"/>
|
||
<line x1="30" y1="445" x2="70" y2="445"/>
|
||
<line x1="20" y1="460" x2="80" y2="460"/>
|
||
</g>
|
||
<path d="M -10,435 Q 20,450 30,445 M 70,445 Q 120,460 250,490"
|
||
fill="none" stroke="#757575" stroke-width="1.5"/>
|
||
<path d="M -10,450 Q 15,465 20,460 M 80,460 Q 150,475 250,510"
|
||
fill="none" stroke="#757575" stroke-width="1.5"/>
|
||
|
||
<!-- Beschriftungen -->
|
||
<text x="780" y="125" font-size="18" fill="#01579B" font-weight="bold"
|
||
class="st-cs-label">Oberbecken</text>
|
||
<text x="780" y="105" font-size="20" fill="#01579B" font-weight="bold"
|
||
class="st-cs-pct" id="stTopPctLabel">30 %</text>
|
||
<text x="180" y="395" font-size="18" fill="#01579B" font-weight="bold"
|
||
class="st-cs-label">Unterbecken</text>
|
||
<text x="180" y="378" font-size="20" fill="#01579B" font-weight="bold"
|
||
class="st-cs-pct" id="stBotPctLabel">70 %</text>
|
||
|
||
<text x="680" y="92" font-size="13" fill="#424242" font-weight="bold"
|
||
class="st-cs-label">Staumauer</text>
|
||
<text x="100" y="395" font-size="13" fill="#424242" font-weight="bold"
|
||
class="st-cs-label">Staumauer</text>
|
||
|
||
<g transform="rotate(-34, 550, 420)">
|
||
<text x="550" y="420" font-size="15" fill="#212121" font-weight="bold"
|
||
class="st-cs-label">Fallleitung / Druckstollen</text>
|
||
</g>
|
||
|
||
<text x="300" y="461" font-size="14" fill="#3E2723" font-weight="bold"
|
||
text-anchor="middle" class="st-cs-label">Kavernenkraftwerk</text>
|
||
|
||
<text x="50" y="425" font-size="13" fill="#424242" font-weight="bold"
|
||
class="st-cs-label">Netzanbindung</text>
|
||
|
||
<!-- Pfeil hinunter zum Maschinenhaus, leitet zum Bedien-Footer -->
|
||
<text x="300" y="630" font-size="13" fill="#3E2723" font-weight="bold"
|
||
text-anchor="middle" class="st-cs-label">↓ Bedienpult Maschinenhaus ↓</text>
|
||
</svg>
|
||
|
||
<!-- Bedien-Footer (HTML, kein foreignObject — iPad-tauglich) -->
|
||
<div class="st-machine-control" id="machineControl">
|
||
<div class="st-machine-readout" id="readoutControl">aus</div>
|
||
<div class="st-bipolar-wrap">
|
||
<span class="st-bipolar-end pump-end">⬆ Pumpe</span>
|
||
<div class="st-bipolar-slider-area">
|
||
<input type="range" class="st-bipolar-slider mode-off"
|
||
id="sliderControl"
|
||
min="-100" max="100" step="any" value="0"
|
||
aria-label="Steuerung — Pumpe (links) bis Turbine (rechts), stufenlos">
|
||
<!-- Tick-Skala: 21 Marker je 10 % — längere Striche bei 0 / ±50 / ±100 -->
|
||
<div class="st-tick-rail" aria-hidden="true">
|
||
<span class="tick t-100"></span><span class="tick"></span>
|
||
<span class="tick"></span><span class="tick"></span>
|
||
<span class="tick"></span><span class="tick t-mid"></span>
|
||
<span class="tick"></span><span class="tick"></span>
|
||
<span class="tick"></span><span class="tick"></span>
|
||
<span class="tick t-zero"></span>
|
||
<span class="tick"></span><span class="tick"></span>
|
||
<span class="tick"></span><span class="tick"></span>
|
||
<span class="tick t-mid"></span><span class="tick"></span>
|
||
<span class="tick"></span><span class="tick"></span>
|
||
<span class="tick"></span><span class="tick t-100"></span>
|
||
</div>
|
||
<div class="st-tick-labels" aria-hidden="true">
|
||
<span>100</span><span>50</span><span>0</span><span>50</span><span>100</span>
|
||
</div>
|
||
</div>
|
||
<span class="st-bipolar-end turb-end">Turbine ⬇</span>
|
||
</div>
|
||
<button type="button" class="st-bipolar-zero" id="btnControlZero"
|
||
title="zurück auf 0" aria-label="zurück auf 0">●</button>
|
||
</div>
|
||
|
||
</section>
|
||
|
||
</main>
|
||
|
||
<!-- Bedarfsgraph (Bottom) -->
|
||
<section class="st-graph-zone">
|
||
<div class="st-graph-meta">
|
||
<div><b id="graphDemandNow">0</b> aktueller Bedarf</div>
|
||
<div><b id="graphSupplyNow">0</b> Solar+Wind jetzt</div>
|
||
<div class="st-legend-row">
|
||
<span class="st-graph-swatch demand"></span> Bedarf
|
||
</div>
|
||
<div class="st-legend-row">
|
||
<span class="st-graph-swatch sun"></span> Solar/Wind (frei)
|
||
</div>
|
||
<div class="st-legend-row">
|
||
<span class="st-graph-swatch supply"></span> Versorgung netto
|
||
</div>
|
||
<div style="font-size:0.74rem; color: var(--ggs-text-muted); margin-top:6px;">
|
||
<b>Solar > Bedarf</b> → Überschuss → pumpen.<br>
|
||
<b>Solar < Bedarf</b> → Lücke → turbinieren.
|
||
</div>
|
||
</div>
|
||
<svg class="st-graph-svg" id="graphSvg" viewBox="0 0 1200 240" preserveAspectRatio="none"
|
||
aria-label="Strombedarf und Versorgung über 5 Tage"></svg>
|
||
</section>
|
||
|
||
</div>
|
||
|
||
<!-- Hint-Overlay -->
|
||
<div class="st-hint-overlay" id="hintOverlay" role="dialog" aria-modal="true">
|
||
<div class="st-hint-modal">
|
||
<h2 id="hintTitle">Hinweis</h2>
|
||
<p id="hintBody"></p>
|
||
<div class="st-hint-modal-actions">
|
||
<button class="ggs-btn-primary" id="hintClose">Verstanden</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- End-Screen Overlay (nutzt Design-System .ggs-endscreen-overlay/.ggs-endscreen) -->
|
||
<div class="ggs-endscreen-overlay" id="endOverlay" role="dialog" aria-modal="true"
|
||
style="display:none;">
|
||
<div class="ggs-endscreen" id="endCard">
|
||
<div class="ggs-endscreen-icon" id="endIcon">🏞️</div>
|
||
<div class="ggs-endscreen-title" id="endTitle">Durchgang abgeschlossen</div>
|
||
<div class="ggs-endscreen-subtitle" id="endSubtitle">5 Tage Netzbalance bilanziert</div>
|
||
<div class="st-endscreen-grade" id="endGrade">B</div>
|
||
<div class="ggs-endscreen-body">
|
||
<div class="ggs-endscreen-kpi-grid">
|
||
<div class="ggs-endscreen-kpi"><div class="n" id="endNetzbalance">100 %</div>
|
||
<div class="l">Netzbalance</div></div>
|
||
<div class="ggs-endscreen-kpi"><div class="n" id="endVerluste">0 GWh</div>
|
||
<div class="l">Verluste</div></div>
|
||
<div class="ggs-endscreen-kpi"><div class="n" id="endDeviation">0 GWh</div>
|
||
<div class="l" id="endDeviationLabel">Abweichung</div></div>
|
||
</div>
|
||
<div class="st-endscreen-detail" id="endNarrative"></div>
|
||
</div>
|
||
<div class="ggs-endscreen-actions">
|
||
<button class="ggs-btn-primary" id="endRestart">Neu starten</button>
|
||
<a class="ggs-btn-ghost" href="../../schueler">Zum Cockpit</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
/* Engine wird absolut geladen — STAUSTUFEN_BASE kommt vom Wrapper bzw.
|
||
vom Fallback im Head. So funktioniert die Datei sowohl unter
|
||
/geograsim/App/staustufen (Wrapper) als auch bei direktem Aufruf. */
|
||
(function () {
|
||
var s = document.createElement('script');
|
||
s.src = window.STAUSTUFEN_BASE + 'engine.js';
|
||
document.head.appendChild(s);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|