Dies ist eine alte Version des Dokuments!
<!DOCTYPE html>
<html lang=„de“> <head> <meta charset=„UTF-8“> <meta name=„viewport“ content=„width=device-width, initial-scale=1.0“> <title>Unterrichts-Timer</title> <link href=„https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Bebas+Neue&family=DM+Sans:wght@300;400;500;600&family=Roboto+Mono:wght@400;500;600&display=swap“ rel=„stylesheet“> <style> /* ══════════════════════════════════════════════════
ALL STYLES SCOPED TO #ut-app — zero global leakage
═══════════════════════════════════════════════════ */ #ut-app {
- -ut-green: #16a34a;
- -ut-green-mid: #22c55e;
- -ut-green-light: #dcfce7;
- -ut-green-glow: rgba(22,163,74,0.22);
- -ut-green-dim: rgba(22,163,74,0.10);
- -ut-red: #dc2626;
- -ut-red-mid: #ef4444;
- -ut-red-light: #fee2e2;
- -ut-red-glow: rgba(220,38,38,0.18);
- -ut-red-dim: rgba(220,38,38,0.09);
- -ut-surface: rgba(255,255,255,0.85);
- -ut-surface2: rgba(248,249,252,0.98);
- -ut-border: rgba(0,0,0,0.09);
- -ut-shadow: 0 2px 14px rgba(0,0,0,0.07);
- -ut-text: #1a1a2e;
- -ut-muted: #6b7280;
- -ut-dim: #9ca3af;
- -ut-purple: #7c3aed;
font-family: ‘DM Sans’, system-ui, sans-serif; font-size: 16px; line-height: 1.5; color: #1a1a2e; background: transparent; box-sizing: border-box; /* Kein festes height/overflow — passt sich dem Container an */ position: relative; width: 100%; min-width: 0; }
/* Hard reset only inside the app — no bleed outward */ #ut-app *:not(select):not(input):not(button):not(option), #ut-app *::before, #ut-app *::after { box-sizing: border-box; margin: 0; font-family: inherit; } #ut-app * { box-sizing: border-box; font-family: inherit; }
/* ── Screens ── */ #ut-setup, #ut-timer, #ut-result { position: relative; width: 100%; display: none; flex-direction: column; align-items: center; justify-content: flex-start; } #ut-setup { display: flex; } #ut-timer { align-items: stretch; min-height: 500px; } #ut-result { position: fixed; inset: 0; z-index: 10; background: rgba(255,255,255,0.97); backdrop-filter: blur(10px); align-items: center; justify-content: center; }
/* ══════════════════════════════════════════════════ SETUP SCREEN ═══════════════════════════════════════════════════ */ #ut-setup { padding: 20px 40px; gap: 0; }
.ut-hdr { text-align: center; margin-bottom: 20px; } .ut-hdr h1 { font-family: ‘Bebas Neue’, sans-serif; font-size: clamp(24px, 3.8vw, 46px); letter-spacing: 4px; background: linear-gradient(135deg, #16a34a 15%, #4ade80 52%, #dc2626 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .ut-hdr p { color: #6b7280; font-size: 11px; letter-spacing: 2.5px; text-transform: uppercase; margin-top: 2px; }
.ut-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; width: min(800px, 86vw); margin-bottom: 12px; }
.ut-card { background: rgba(255,255,255,0.94); border: 1px solid rgba(0,0,0,0.07); border-radius: 16px; padding: 22px 22px 20px 22px; display: flex; flex-direction: column; gap: 12px; box-shadow: 0 1px 0 rgba(0,0,0,0.05), 0 4px 18px rgba(0,0,0,0.08); backdrop-filter: blur(12px); } .ut-card label, .ut-card-label { font-size: 10px; letter-spacing: 2.5px; text-transform: uppercase; color: #6b7280; font-weight: 600; display: block; margin: 0; padding: 0; line-height: 1.4; }
.ut-card select { background: rgba(248,249,252,0.98); border: 1.5px solid rgba(0,0,0,0.10); border-radius: 9px; color: #1a1a2e; font-family: ‘DM Mono’, monospace; font-size: 14px; padding: 12px 36px 12px 14px; width: 100%; outline: none; cursor: pointer; appearance: none; -webkit-appearance: none; background-image: url(“data:image/svg+xml,%3Csvg xmlns=‘http://www.w3.org/2000/svg’ width=‘12’ height=‘8’ viewBox=‘0 0 12 8’%3E%3Cpath d=‘M1 1l5 5 5-5’ stroke=’%236b7280’ stroke-width=‘1.5’ fill=‘none’ stroke-linecap=‘round’/%3E%3C/svg%3E”); background-repeat: no-repeat; background-position: right 14px center; box-shadow: 0 1px 3px rgba(0,0,0,0.06) inset; margin: 0; } .ut-card select:focus { border-color: #16a34a; box-shadow: 0 0 0 3px rgba(22,163,74,0.10); }
/* Timer choice */ .ut-tchoice { display: flex; gap: 8px; } .ut-tbtn { flex: 1; padding: 11px 16px; border-radius: 10px; border: 1.5px solid rgba(0,0,0,0.10); background: rgba(248,249,252,0.98); color: #6b7280; font-family: ‘DM Sans’, sans-serif; font-size: 12px; font-weight: 700; cursor: pointer; transition: all 0.16s; letter-spacing: 1px; text-transform: uppercase; box-shadow: 0 1px 0 rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04); } .ut-tbtn.green.active { background: linear-gradient(160deg, #f0fdf4 0%, #dcfce7 100%); border-color: #16a34a; color: #15803d; box-shadow: 0 1px 0 rgba(21,128,61,0.15), 0 3px 12px rgba(22,163,74,0.22); } .ut-tbtn.red.active { background: linear-gradient(160deg, #fff5f5 0%, #fee2e2 100%); border-color: #dc2626; color: #b91c1c; box-shadow: 0 1px 0 rgba(185,28,28,0.15), 0 3px 12px rgba(220,38,38,0.20); } .ut-tbtn:hover:not(.active) { border-color: #9ca3af; color: #1a1a2e; background: #fff; }
/* Goal */ .ut-goal-val { font-family: ‘Bebas Neue’, sans-serif; font-size: 34px; text-align: center; color: #16a34a; letter-spacing: 2px; line-height: 1; } .ut-goal-sub { font-size: 9px; text-align: center; color: #6b7280; letter-spacing: 1.5px; text-transform: uppercase; }
#ut-app input[type=range] { -webkit-appearance: none; appearance: none; height: 5px; border-radius: 3px; background: linear-gradient(90deg, #22c55e, #dcfce7); width: 100%; cursor: pointer; border: none; outline: none; display: block; } #ut-app input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 20px; height: 20px; border-radius: 50%; background: #16a34a; border: 3px solid #fff; box-shadow: 0 1px 6px rgba(22,163,74,0.25); cursor: pointer; } #ut-app input[type=range]::-moz-range-thumb { width: 20px; height: 20px; border-radius: 50%; background: #16a34a; border: 3px solid #fff; box-shadow: 0 1px 6px rgba(22,163,74,0.25); cursor: pointer; } .ut-rlabels { display: flex; justify-content: space-between; font-size: 10px; color: #9ca3af; font-family: ‘DM Mono’, monospace; }
/* Button row */ .ut-btnrow { display: flex; gap: 10px; width: min(800px, 86vw); align-items: stretch; } .ut-start-btn { flex: 1; padding: 15px 40px; border-radius: 12px; border: none; background: linear-gradient(160deg, #22c55e 0%, #16a34a 100%); color: #fff; font-family: ‘Bebas Neue’, sans-serif; font-size: 18px; letter-spacing: 3.5px; cursor: pointer; transition: all 0.16s; box-shadow: 0 2px 0 #166534, 0 5px 18px rgba(22,163,74,0.30); text-shadow: 0 1px 2px rgba(0,0,0,0.15); } .ut-start-btn:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 3px 0 #166534, 0 8px 26px rgba(22,163,74,0.35); } .ut-start-btn:active:not(:disabled) { transform: translateY(1px); box-shadow: 0 1px 0 #166534, 0 2px 10px rgba(22,163,74,0.22); } .ut-start-btn:disabled { opacity: 0.38; cursor: not-allowed; transform: none; box-shadow: none; }
.ut-now-btn { padding: 15px 28px; border-radius: 12px; border: 1.5px solid #16a34a; background: linear-gradient(160deg, #f0fdf4 0%, #dcfce7 100%); color: #15803d; font-family: ‘DM Sans’, sans-serif; font-size: 12px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; transition: all 0.16s; white-space: nowrap; display: flex; align-items: center; gap: 6px; box-shadow: 0 2px 0 rgba(21,128,61,0.18), 0 3px 10px rgba(22,163,74,0.14); } .ut-now-btn:hover:not(:disabled) { background: linear-gradient(160deg, #22c55e 0%, #16a34a 100%); color: #fff; border-color: #15803d; box-shadow: 0 2px 0 #166534, 0 6px 18px rgba(22,163,74,0.30); transform: translateY(-1px); } .ut-now-btn:active:not(:disabled) { transform: translateY(1px); box-shadow: 0 1px 0 rgba(21,128,61,0.18); } .ut-now-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.ut-wait-note { margin-top: 9px; font-size: 11px; color: #6b7280; letter-spacing: 1.5px; text-transform: uppercase; display: none; text-align: center; width: min(800px, 86vw); } .ut-wait-note.show { display: block; } .ut-wait-note span { font-family: ‘DM Mono’, monospace; color: #1a1a2e; font-weight: 600; } .ut-wait-note button { background: none; border: none; color: #16a34a; font-size: 11px; letter-spacing: 1px; text-transform: uppercase; font-weight: 700; cursor: pointer; text-decoration: underline; font-family: ‘DM Sans’, sans-serif; }
/* ══════════════════════════════════════════════════ TIMER SCREEN ═══════════════════════════════════════════════════ */ #ut-timer { display: none; position: relative; width: 100%; height: 100vh; min-height: 500px; }
.ut-topbar { height: auto; min-height: 50px; display: flex; align-items: center; justify-content: space-between; padding: 8px 16px; border-bottom: 1.5px solid rgba(0,0,0,0.08); background: rgba(255,255,255,0.90); flex-shrink: 0; gap: 10px; backdrop-filter: blur(12px); box-shadow: 0 1px 8px rgba(0,0,0,0.06); /* Keine flex-wrap: Leiste bleibt immer einzeilig und stabil */ flex-wrap: nowrap; overflow: hidden; } .ut-clock { font-family: ‘Roboto Mono’, monospace; font-size: 19px; font-weight: 500; letter-spacing: 0; color: #1a1a2e; font-variant-numeric: tabular-nums; width: 7ch; flex-shrink: 0; } .ut-topinfo { display: flex; align-items: center; gap: 12px; flex-wrap: nowrap; flex-shrink: 0; } .ut-chip { display: flex; align-items: center; gap: 5px; font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #6b7280; white-space: nowrap; flex-shrink: 0; } /* Label-Teil des Chips: feste Breite je nach Inhalt */ .ut-chip span:first-child { flex-shrink: 0; } .ut-chip .utv { font-family: ‘Roboto Mono’, monospace; font-size: 13px; color: #1a1a2e; font-weight: 500; letter-spacing: 0; font-variant-numeric: tabular-nums; display: inline-block; text-align: left; flex-shrink: 0; } /* Zeitwerte MM:SS — genau 5 Zeichen */ .ut-chip .utv.utv-time { width: auto; display: inline-flex; align-items: center; } /* Prozentwerte — genau 4 Zeichen für “100%” */ .ut-chip .utv.utv-pct { width: auto; display: inline-flex; align-items: center; }
/* Jede Ziffer in einer eigenen Box — absolute Pixelbreite */ .utd { display: inline-block; width: 0.62em; text-align: center; font-family: ‘Roboto Mono’, monospace; font-variant-numeric: tabular-nums; } /* Ziffern für Prozentwert */ .utdd { display: inline-block; width: 0.62em; text-align: center; font-family: ‘Roboto Mono’, monospace; font-variant-numeric: tabular-nums; } /* Prozentzeichen — feste Breite */ .utdpc { display: inline-block; width: 0.7em; text-align: center; font-family: ‘Roboto Mono’, monospace; } /* Doppelpunkt-Trennzeichen etwas enger */ .utdc { width: 0.38em; } /* Zeitwerte: max. “99:99” = 5 Zeichen */ .ut-chip .utv.utv-time { width: 5ch; } /* Prozentwerte: max. “100%” = 4 Zeichen */ .ut-chip .utv.utv-pct { width: 4ch; }
.ut-chip.gc .utv { color: #16a34a; }
.ut-otbadge { padding: 3px 11px; border-radius: 20px; font-size: 10px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase; background: #fee2e2; color: #dc2626; border: 1.5px solid rgba(220,38,38,0.30); animation: ut-pred 1.2s ease-in-out infinite; } @keyframes ut-pred { 0%,100% { box-shadow: 0 0 0 0 rgba(220,38,38,0.18); } 50% { box-shadow: 0 0 10px 3px rgba(220,38,38,0.18); } }
/* Timers area */ .ut-area { flex: 1; display: flex; align-items: center; justify-content: center; padding: 10px 14px; min-height: 0; } .ut-panel { flex: 1; display: flex; align-items: center; justify-content: center; height: 100%; } .ut-divider { width: 1.5px; height: 52%; background: rgba(0,0,0,0.08); flex-shrink: 0; }
.ut-rwrap { position: relative; cursor: pointer; transition: transform 0.13s ease; border-radius: 50%; } .ut-rwrap:active { transform: scale(0.966); } .ut-rwrap svg { display: block; }
.ut-rinner { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; pointer-events: none; gap: 3px; } .ut-rlbl { font-size: 11px; letter-spacing: 3px; text-transform: uppercase; font-weight: 700; } .ut-rtime { font-family: ‘Bebas Neue’, sans-serif; line-height: 1; letter-spacing: 2px; } .ut-rstat { font-size: 10px; letter-spacing: 2px; text-transform: uppercase; font-weight: 600; opacity: 0.55; }
.ut-panel.ag .ut-rwrap { filter: drop-shadow(0 0 16px rgba(22,163,74,0.22)) drop-shadow(0 4px 10px rgba(22,163,74,0.10)); animation: ut-ps 2.2s ease-in-out infinite; } .ut-panel.ar .ut-rwrap { filter: drop-shadow(0 0 16px rgba(220,38,38,0.18)) drop-shadow(0 4px 10px rgba(220,38,38,0.10)); animation: ut-ps 2.2s ease-in-out infinite; } @keyframes ut-ps { 0%,100% { transform:scale(1); } 50% { transform:scale(1.012); } }
/* ══════════════════════════════════════════════════ RESULT SCREEN ═══════════════════════════════════════════════════ */ #ut-result { display: none; position: absolute; inset: 0; align-items: center; justify-content: center; } .ut-rcard { width: min(640px, 92vw); background: #fff; border: 1.5px solid rgba(0,0,0,0.08); border-radius: 22px; padding: 30px 34px; text-align: center; box-shadow: 0 8px 40px rgba(0,0,0,0.10); } .ut-rtitle { font-family: ‘Bebas Neue’, sans-serif; font-size: clamp(26px, 3.8vw, 42px); letter-spacing: 4px; margin-bottom: 4px; } .ut-rtitle.ok { color: #16a34a; } .ut-rtitle.no { color: #dc2626; } .ut-rsub { font-size: 11px; letter-spacing: 2px; color: #6b7280; text-transform: uppercase; margin-bottom: 24px; } .ut-rbars { display: flex; gap: 12px; margin-bottom: 18px; } .ut-rbi { flex: 1; background: rgba(248,249,252,0.98); border-radius: 12px; padding: 14px 12px; border: 1px solid rgba(0,0,0,0.08); } .ut-rbil { font-size: 10px; letter-spacing: 2px; text-transform: uppercase; font-weight: 700; margin-bottom: 9px; } .ut-rbil.g { color: #16a34a; } .ut-rbil.r { color: #dc2626; } .ut-rbt { height: 8px; background: #e5e7eb; border-radius: 4px; overflow: hidden; margin-bottom: 8px; } .ut-rbf { height: 100%; border-radius: 4px; width: 0; transition: width 1s ease; } .ut-rbf.g { background: linear-gradient(90deg, #16a34a, #22c55e); } .ut-rbf.r { background: linear-gradient(90deg, #dc2626, #ef4444); } .ut-rbp { font-family: ‘Bebas Neue’, sans-serif; font-size: 30px; letter-spacing: 1px; line-height: 1; } .ut-rbp.g { color: #16a34a; } .ut-rbp.r { color: #dc2626; } .ut-rbt2 { font-family: ‘DM Mono’, monospace; font-size: 11px; color: #6b7280; } .ut-rgoal { font-size: 13px; color: #6b7280; margin-bottom: 18px; line-height: 1.7; } .ut-rgoal strong { color: #1a1a2e; } .ut-racts { display: flex; gap: 10px; justify-content: center; } .ut-rbtn { padding: 11px 24px; border-radius: 9px; border: 1.5px solid rgba(0,0,0,0.09); background: rgba(248,249,252,0.98); color: #1a1a2e; font-family: ‘DM Sans’, sans-serif; font-size: 12px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase; cursor: pointer; transition: all 0.16s; } .ut-rbtn:hover { border-color: #9ca3af; } .ut-rbtn.p { background: #dcfce7; border-color: #16a34a; color: #16a34a; } /* ══════════════════════════════════════════════════ RESPONSIVE — Tablet & Mobile ═══════════════════════════════════════════════════ */
/* Tablet: schmaler als 700px */ @media (max-width: 700px) { #ut-setup { padding: 14px 16px; overflow-y: auto; justify-content: flex-start; padding-top: 20px; }
.ut-hdr { margin-bottom: 14px; } .ut-hdr h1 { font-size: clamp(22px, 6vw, 36px); letter-spacing: 2px; } .ut-hdr p { font-size: 10px; letter-spacing: 1.5px; }
.ut-grid { grid-template-columns: 1fr 1fr; width: 100%; gap: 10px; margin-bottom: 10px; }
/* Ziel-Karte nimmt alle Spalten ein */ .ut-grid .ut-card:last-child { grid-column: 1 / -1; }
.ut-btnrow { width: 100%; flex-direction: column; gap: 8px; } .ut-start-btn { font-size: 16px; padding: 14px 24px; } .ut-now-btn { justify-content: center; padding: 13px 24px; }
.ut-wait-note { width: 100%; font-size: 10px; }
/* Timer-Leiste: zweizeilig auf Hochkant */ .ut-topbar { flex-wrap: wrap; padding: 8px 12px; gap: 6px; } .ut-topinfo { gap: 8px; width: 100%; justify-content: flex-start; } .ut-clock { font-size: 15px; width: 7.5ch; } .ut-chip { font-size: 9px; gap: 3px; } .ut-chip .utv { font-size: 11px; } .ut-chip .utv.utv-time { width: 5ch; } .ut-chip .utv.utv-pct { width: 4ch; }
/* Timer-Ringe kleiner */ .ut-area { padding: 6px 10px; } }
/* Smartphone Hochkant: schmaler als 480px */ @media (max-width: 480px) { #ut-setup { padding: 12px 12px; }
.ut-grid { grid-template-columns: 1fr; gap: 8px; } .ut-grid .ut-card:last-child { grid-column: 1; }
/* Ziel-Karte: vertikal stapeln */ .ut-grid .ut-card[style*=“flex-direction:row”], .ut-grid .ut-card[style*=“flex-direction: row”] { flex-direction: column !important; align-items: flex-start !important; }
.ut-card { padding: 16px 16px; } .ut-card select { font-size: 15px; padding: 13px 36px 13px 14px; } .ut-tbtn { font-size: 13px; padding: 12px 12px; }
.ut-start-btn { font-size: 15px; letter-spacing: 2px; } .ut-now-btn { font-size: 13px; }
/* Topbar: alles kompakter */ .ut-clock { font-size: 15px; } .ut-chip { font-size: 8.5px; } .ut-chip .utv { font-size: 11px; } .ut-otbadge { font-size: 9px; padding: 2px 8px; }
/* Timer-Ringe: stapeln */ .ut-area { flex-direction: column; padding: 6px 12px; gap: 0; } .ut-divider { width: 60%; height: 1.5px; } .ut-panel { height: auto; flex: 0 0 auto; } }
/* Querformat erzwingen wenn klein genug */ @media (max-height: 420px) { #ut-setup { flex-direction: row; flex-wrap: wrap; align-content: center; padding: 8px 16px; overflow-y: auto; } .ut-hdr { width: 100%; margin-bottom: 8px; } .ut-hdr h1 { font-size: 22px; } .ut-grid { gap: 8px; margin-bottom: 8px; } .ut-card { padding: 12px 14px; gap: 8px; } .ut-topbar { padding: 6px 12px; } }
</style> </head> <body>
<div id=„ut-app“>
<!-- ══ SETUP ══ -->
<div id="ut-setup">
<div class="ut-hdr">
<h1>Unterrichts-Timer</h1>
<p>Konfiguration der Stunde</p>
</div>
``` <div class=„ut-grid“>
<div class="ut-card"> <label>Startzeit</label> <select id="ut-sel-start"></select> </div>
<div class="ut-card">
<label>Stundendauer</label>
<select id="ut-sel-dur">
<option value="45" selected>45 Minuten</option>
<option value="90">90 Minuten</option>
</select>
</div>
<div class="ut-card">
<label>Erster aktiver Timer</label>
<div class="ut-tchoice">
<button class="ut-tbtn green active" id="ut-btn-g" onclick="utSetFirst('green')">● Grün</button>
<button class="ut-tbtn red" id="ut-btn-r" onclick="utSetFirst('red')">● Rot</button>
</div>
</div>
<div class="ut-card" style="grid-column:1/-1;flex-direction:row;align-items:center;gap:20px;padding:22px 22px 20px 22px;">
<div style="flex-shrink:0;min-width:78px;text-align:center;">
<div class="ut-goal-val" id="ut-goal-val">75%</div>
<div class="ut-goal-sub">Ziel Grün</div>
</div>
<div style="flex:1;display:flex;flex-direction:column;gap:8px;">
<label class="ut-card-label">Ziel: Anteil grüner Timer-Zeit</label>
<input type="range" id="ut-slider" min="50" max="95" step="5" value="75" oninput="utGoal(this.value)">
<div class="ut-rlabels"><span>50%</span><span>95%</span></div>
</div>
</div>
</div>
<div class=„ut-btnrow“>
<button class="ut-start-btn" id="ut-sbtn" onclick="utConfirm()">Stunde konfigurieren & warten</button> <button class="ut-now-btn" id="ut-nbtn" onclick="utNow()" title="Sofort starten ohne Wartezeit">▶ Sofort starten</button>
</div> <div class=„ut-wait-note“ id=„ut-wnote“>
Warte auf Startzeit: <span id="ut-wdisp">--:--</span> · <button onclick="utNow()">Sofort starten</button>
</div> ```
</div>
<!-- ══ TIMER ══ -->
<div id="ut-timer">
<div class="ut-topbar">
<div class="ut-clock" id="ut-clock"><span class="utd" id="utd-ch0">0</span><span class="utd" id="utd-ch1">0</span><span class="utd utdc">:</span><span class="utd" id="utd-cm0">0</span><span class="utd" id="utd-cm1">0</span><span class="utd utdc">:</span><span class="utd" id="utd-cs0">0</span><span class="utd" id="utd-cs1">0</span></div>
<div class="ut-topinfo">
<div class="ut-chip"><span>Gesamt</span><span class="utv utv-time" id="ut-total"><span class="utd" id="utd-total-0">0</span><span class="utd" id="utd-total-1">0</span><span class="utd utdc">:</span><span class="utd" id="utd-total-2">0</span><span class="utd" id="utd-total-3">0</span></span></div>
<div class="ut-chip"><span>Verbleibend</span><span class="utv utv-time" id="ut-rem"><span class="utd" id="utd-rem-0">4</span><span class="utd" id="utd-rem-1">5</span><span class="utd utdc">:</span><span class="utd" id="utd-rem-2">0</span><span class="utd" id="utd-rem-3">0</span></span></div>
<div class="ut-chip gc"><span>Ziel</span><span class="utv utv-pct" id="ut-gtop">75%</span></div>
<div class="ut-chip gc"><span>Grün</span><span class="utv utv-pct" id="ut-gcur"><span class="utd utdd" id="utd-gp-h"> </span><span class="utd utdd" id="utd-gp-t">0</span><span class="utd utdd" id="utd-gp-o">0</span><span class="utdd utdpc">%</span></span></div>
<span class="ut-otbadge" id="ut-otbadge" style="display:none">Nachzeit</span>
</div>
</div>
``` <div class=„ut-area“>
<!-- GREEN -->
<div class="ut-panel" id="ut-pg">
<div class="ut-rwrap" onclick="utSwitch('green')">
<svg id="ut-svg-g" viewBox="0 0 300 300" width="260" height="260">
<circle cx="150" cy="150" r="130" fill="rgba(255,255,255,0.75)"/>
<circle cx="150" cy="150" r="130" fill="none" stroke="#dcfce7" stroke-width="15"/>
<circle id="ut-prog-g" cx="150" cy="150" r="130" fill="none"
stroke="#22c55e" stroke-width="15" stroke-linecap="round"
stroke-dasharray="816.8" stroke-dashoffset="816.8"
transform="rotate(-90 150 150)"/>
</svg>
<div class="ut-rinner">
<div class="ut-rlbl" style="color:#16a34a">Grün</div>
<div class="ut-rtime" id="ut-tg" style="color:#16a34a">00:00</div>
<div class="ut-rstat" id="ut-sg" style="color:#16a34a">Aktiv</div>
</div>
</div>
</div>
<div class="ut-divider"></div>
<!-- RED -->
<div class="ut-panel" id="ut-pr">
<div class="ut-rwrap" onclick="utSwitch('red')">
<svg id="ut-svg-r" viewBox="0 0 300 300" width="260" height="260">
<circle cx="150" cy="150" r="130" fill="rgba(255,255,255,0.75)"/>
<circle cx="150" cy="150" r="130" fill="none" stroke="#fee2e2" stroke-width="15"/>
<circle id="ut-prog-r" cx="150" cy="150" r="130" fill="none"
stroke="#ef4444" stroke-width="15" stroke-linecap="round"
stroke-dasharray="816.8" stroke-dashoffset="816.8"
transform="rotate(-90 150 150)"/>
</svg>
<div class="ut-rinner">
<div class="ut-rlbl" style="color:#dc2626">Rot</div>
<div class="ut-rtime" id="ut-tr" style="color:#dc2626">00:00</div>
<div class="ut-rstat" id="ut-sr" style="color:#dc2626">Pausiert</div>
</div>
</div>
</div>
</div> ```
</div>
<!-- ══ RESULT ══ -->
<div id="ut-result">
<div class="ut-rcard">
<div class="ut-rtitle" id="ut-rtitle">Ziel erreicht!</div>
<div class="ut-rsub">Auswertung der Unterrichtsstunde</div>
<div class="ut-rbars">
<div class="ut-rbi">
<div class="ut-rbil g">Grüne Zeit</div>
<div class="ut-rbt"><div class="ut-rbf g" id="ut-bg"></div></div>
<div class="ut-rbp g" id="ut-pg-pct">0%</div>
<div class="ut-rbt2" id="ut-ag">0.0 min</div>
</div>
<div class="ut-rbi">
<div class="ut-rbil r">Rote Zeit</div>
<div class="ut-rbt"><div class="ut-rbf r" id="ut-br"></div></div>
<div class="ut-rbp r" id="ut-pr-pct">0%</div>
<div class="ut-rbt2" id="ut-ar">0.0 min</div>
</div>
</div>
<div class="ut-rgoal" id="ut-rgoal"></div>
<div class="ut-racts">
<button class="ut-rbtn p" onclick="utReset()">Neu starten</button>
</div>
</div>
</div>
</div><!– #ut-app –>
<script> (function(){ 'use strict';
var cfg = { startH:7, startM:0, duration:45, first:'green', goal:75 }; var st = { phase:'idle', active:'green', gMs:0, rMs:0, last:null,
total:0, limit:0, wIv:null, tIv:null, cIv:null };
/* ── helpers ── */ function pad(n){ return String(n).padStart(2,'0'); } function berlin(){
var d=new Date(), b=new Date(d.toLocaleString('en-US',{timeZone:'Europe/Berlin'}));
return {h:b.getHours(),m:b.getMinutes(),s:b.getSeconds(),date:b};
} function mmss(ms){
var s=Math.floor(ms/1000), m=Math.floor(s/60); return pad(m)+':'+pad(s%60);
} function el(id){ return document.getElementById(id); }
/* ── Init ── */ function init(){
var sel=el('ut-sel-start');
for(var h=7;h<=17;h++){
for(var m=0;m<60;m+=5){
if(h===17&&m>0) break;
var o=document.createElement('option');
o.value=h+':'+m;
o.textContent=pad(h)+':'+pad(m)+' Uhr';
sel.appendChild(o);
}
}
var now=berlin(), bh=now.h, bm=Math.ceil((now.m+1)/5)*5;
if(bm>=60){bm=0;bh++;}
if(bh>17){bh=17;bm=0;}
sel.value=bh+':'+bm;
startClock();
}
function startClock(){
function tick(){
var b=berlin();
var hh=pad(b.h), mm=pad(b.m), ss=pad(b.s);
el('utd-ch0').textContent=hh[0]; el('utd-ch1').textContent=hh[1];
el('utd-cm0').textContent=mm[0]; el('utd-cm1').textContent=mm[1];
el('utd-cs0').textContent=ss[0]; el('utd-cs1').textContent=ss[1];
}
tick();
st.cIv=setInterval(tick,500);
}
/* ── Setup callbacks ── */ window.utSetFirst=function(w){
cfg.first=w;
el('ut-btn-g').classList.toggle('active',w==='green');
el('ut-btn-r').classList.toggle('active',w==='red');
}; window.utGoal=function(v){
cfg.goal=parseInt(v);
el('ut-goal-val').textContent=v+'%';
el('ut-slider').value=v;
}; function applyCfg(){
var p=el('ut-sel-start').value.split(':').map(Number);
cfg.startH=p[0]; cfg.startM=p[1];
cfg.duration=parseInt(el('ut-sel-dur').value);
st.limit=cfg.duration*60*1000;
} window.utConfirm=function(){
applyCfg();
el('ut-sbtn').disabled=true;
el('ut-nbtn').disabled=true;
el('ut-sbtn').textContent='Warte auf Startzeit…';
el('ut-wnote').classList.add('show');
updWait();
st.wIv=setInterval(function(){
updWait();
var n=berlin();
if(n.h===cfg.startH&&n.m===cfg.startM&&n.s<3){
clearInterval(st.wIv); begin();
}
},500);
}; window.utNow=function(){
clearInterval(st.wIv); applyCfg(); begin();
}; function updWait(){ el('ut-wdisp').textContent=pad(cfg.startH)+':'+pad(cfg.startM); }
/* ── Begin ── */ function begin(){
st.phase='running'; st.active=cfg.first;
st.gMs=0; st.rMs=0; st.total=0;
st.last=Date.now();
el('ut-gtop').textContent=cfg.goal+'%';
// Verbleibend korrekt initialisieren über Digit-Spans (nicht textContent!)
setMmss('utd-rem-0','utd-rem-1','utd-rem-2','utd-rem-3', st.limit);
el('ut-setup').style.display='none';
el('ut-timer').style.display='flex';
panels(); resize();
st.tIv=setInterval(tick,100);
}
/* ── Tick ── */ function tick(){
if(st.phase==='done') return;
var now=Date.now(), d=now-st.last; st.last=now;
if(st.active==='green') st.gMs+=d; else st.rMs+=d;
st.total=st.gMs+st.rMs;
if(st.phase==='running'&&st.total>=st.limit){
if(st.active==='green'){ end(); return; }
else{ st.phase='overtime'; el('ut-otbadge').style.display='inline-flex'; }
}
render();
}
/* ── Switch ── */ window.utSwitch=function(w){
if(st.phase==='done'||st.phase==='idle') return;
if(st.phase==='overtime'&&w==='green'){
var now=Date.now(),d=now-st.last; st.last=now;
if(st.active==='red') st.rMs+=d; else st.gMs+=d;
st.total=st.gMs+st.rMs;
end(); return;
}
if(st.active===w) return;
st.active=w; st.last=Date.now();
panels(); render();
};
/* ── Digit helpers ── */ function setMmss(d0, d1, d2, d3, ms){
var s=Math.floor(ms/1000), m=Math.floor(s/60); var mm=pad(m), ss=pad(s%60); el(d0).textContent=mm[0]; el(d1).textContent=mm[1]; el(d2).textContent=ss[0]; el(d3).textContent=ss[1];
} function setGreenPct(gp){
// Hunderter: '1' bei 100, sonst geschütztes Leerzeichen
el('utd-gp-h').textContent = gp === 100 ? '1' : '\u2007';
// Zehner: Ziffer wenn >= 10, sonst Leerzeichen
el('utd-gp-t').textContent = gp >= 10 ? String(Math.floor(gp / 10) % 10) : '\u2007';
// Einer: immer eine Ziffer
el('utd-gp-o').textContent = String(gp % 10);
}
/* ── Render ── */ function render(){
el('ut-tg').textContent=mmss(st.gMs);
el('ut-tr').textContent=mmss(st.rMs);
setMmss('utd-total-0','utd-total-1','utd-total-2','utd-total-3', st.total);
setMmss('utd-rem-0', 'utd-rem-1', 'utd-rem-2', 'utd-rem-3', Math.max(0,st.limit-st.total));
var gp=st.total>0?Math.round(st.gMs/st.total*100):0; setGreenPct(gp);
if(st.limit>0){
var C=816.8;
el('ut-prog-g').style.strokeDashoffset=Math.max(0,C-(st.gMs/st.limit)*C);
el('ut-prog-r').style.strokeDashoffset=Math.max(0,C-(st.rMs/st.limit)*C);
}
} function panels(){
el('ut-pg').className='ut-panel'+(st.active==='green'?' ag':'');
el('ut-pr').className='ut-panel'+(st.active==='red'?' ar':'');
el('ut-sg').textContent=st.active==='green'?'Läuft':'Pausiert';
el('ut-sr').textContent=st.active==='red'?'Läuft':'Pausiert';
}
/* ── End ── */ function end(){
st.phase='done'; clearInterval(st.tIv); render();
var tot=st.total, gp=tot>0?Math.round(st.gMs/tot*100):0, rp=100-gp, ok=gp>=cfg.goal;
setTimeout(function(){
var gm=(st.gMs/60000).toFixed(1), rm=(st.rMs/60000).toFixed(1);
el('ut-rtitle').textContent=ok?'✓ Ziel erreicht!':'✗ Ziel nicht erreicht';
el('ut-rtitle').className='ut-rtitle '+(ok?'ok':'no');
el('ut-pg-pct').textContent=gp+'%'; el('ut-pr-pct').textContent=rp+'%';
el('ut-ag').textContent=gm+' min'; el('ut-ar').textContent=rm+' min';
el('ut-rgoal').innerHTML='Ziel: <strong>'+cfg.goal+'% grüne Zeit</strong> · Ergebnis: <strong>'+gp+'% grün</strong> / <strong>'+rp+'% rot</strong>';
el('ut-result').style.display='flex';
setTimeout(function(){ el('ut-bg').style.width=gp+'%'; el('ut-br').style.width=rp+'%'; },80);
},350);
}
/* ── Reset ── */ window.utReset=function(){
clearInterval(st.tIv); clearInterval(st.wIv);
st={phase:'idle',active:'green',gMs:0,rMs:0,last:null,total:0,limit:0,wIv:null,tIv:null,cIv:st.cIv};
el('ut-result').style.display='none';
el('ut-timer').style.display='none';
el('ut-setup').style.display='flex';
el('ut-sbtn').disabled=false; el('ut-nbtn').disabled=false;
el('ut-sbtn').textContent='Stunde konfigurieren & warten';
el('ut-wnote').classList.remove('show');
el('ut-otbadge').style.display='none';
el('ut-prog-g').style.strokeDashoffset=816.8;
el('ut-prog-r').style.strokeDashoffset=816.8;
};
/* ── Resize ── */ function resize(){
var area=document.querySelector('#ut-app .ut-area');
if(!area) return;
var areaH = area.clientHeight > 0 ? area.clientHeight : window.innerHeight - 60;
var areaW = area.clientWidth > 0 ? area.clientWidth : window.innerWidth;
var sz=Math.max(150, Math.min(areaH - 20, areaW / 2 - 44, 340));
['ut-svg-g','ut-svg-r'].forEach(function(id){
var s=el(id); if(s){s.style.width=sz+'px';s.style.height=sz+'px';}
});
var fs=Math.max(24,sz*0.205);
document.querySelectorAll('#ut-app .ut-rtime').forEach(function(e){e.style.fontSize=fs+'px';});
} window.addEventListener('resize',resize);
init(); })(); </script>
</body> </html>
