Cockpit-Triage + Sterne->%+Balken durchgaengig (teacher.html)

Cockpit (Uebersicht):
- Farben auf kanonische Palette (--ivg..--ivr = _pctColor-Hex); ivAmp-
  Schwellen auf 50/60/80/90.
- ivTriage neu: >=90 Fordern (kann alles) / 50-89 Laeuft gut /
  <50 & fleissig -> Begleiten / <50 sonst -> Foerdern. Bestaetigen entfaellt.
- Fleiss im Klassenvergleich (_fleissig = 0.5*Zeit + 0.5*Durchgaenge >= Schnitt),
  fuer HEUTE- und insgesamt-Triage. IV_META-Texte angepasst.

Sterne raus -> %+Balken (colorblind-freundlich):
- _pctBadge (Run-Karten) neu. Alle 7 Run-Karten + generische Sonnensystem-
  Karte auf %+Balken (Busfahrt/Flug rechnen /3). Heli Start/Landung/Gesamt-
  Spalten in Aktuell auf _pctBar. Spectator, Drilldown (ivStars,
  Top-Performers), fmtMetric, Heli-Detail: Balken bzw. %.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 10:17:05 +02:00
parent 3bc0f418be
commit a4e57d7aa6
+68 -48
View File
@@ -667,6 +667,16 @@ function _pctBar(pct) {
+ '<div style="margin-top:2px;height:4px;border-radius:99px;background:rgba(0,0,0,.07);overflow:hidden"><div style="height:100%;width:'+pct+'%;background:'+c+'"></div></div>'
+ '</div>';
}
// Größere %+Balken-Plakette für Run-Karten (Ersatz für Sterne) — Balken statt
// Sterne, weil auch für Farbenblinde am Füllstand unterscheidbar.
function _pctBadge(pct) {
if (pct == null || isNaN(pct)) return '<span style="color:#9a9a9a"></span>';
pct = Math.max(0, Math.min(100, Math.round(pct)));
var c = _pctColor(pct);
return '<span style="display:inline-flex;align-items:center;gap:.4rem">'
+ '<span style="width:64px;height:8px;border-radius:99px;background:rgba(0,0,0,.09);overflow:hidden;display:inline-block"><span style="display:block;height:100%;width:'+pct+'%;background:'+c+'"></span></span>'
+ '<b style="color:'+c+';font-size:.85rem;font-variant-numeric:tabular-nums">'+pct+' %</b></span>';
}
// Das EINE Leistungs-% (0100) je Sim aus dem Live-State. Sims mit eigenem
// 0100-Score nutzen ihn direkt; sonst aus den vorhandenen Kennzahlen abgeleitet.
function _leistungPct(simId, s) {
@@ -944,19 +954,13 @@ var LIVE_PRIMARY_FIELDS = {
: '<span style="color:'+c+';font-weight:700">'+n+'</span>';
}},
{k:'startScore', label:'🛫 Start', fmt:function(v){
var n = Math.max(0, Math.min(5, v||0));
var c = n>=4 ? '#5a8a5e' : n>=2 ? '#c9913a' : '#c07a6b';
return v==null ? '—' : '<span style="color:'+c+';font-weight:700">'+'★'.repeat(n)+'<span style="color:#ccc">'+'★'.repeat(5-n)+'</span></span>';
return v==null ? '<span style="color:#9a9a9a"></span>' : _pctBar(Math.round(Math.max(0,Math.min(5,v))/5*100));
}},
{k:'landingScore', label:'🛬 Landung', fmt:function(v){
var n = Math.max(0, Math.min(5, v||0));
var c = n>=4 ? '#5a8a5e' : n>=2 ? '#c9913a' : '#c07a6b';
return v==null ? '—' : '<span style="color:'+c+';font-weight:700">'+'★'.repeat(n)+'<span style="color:#ccc">'+'★'.repeat(5-n)+'</span></span>';
return v==null ? '<span style="color:#9a9a9a"></span>' : _pctBar(Math.round(Math.max(0,Math.min(5,v))/5*100));
}},
{k:'totalStars', label:'⭐ Gesamt', fmt:function(v){
var n = Math.max(0, Math.min(5, v||0));
var c = n>=4 ? '#5a8a5e' : n>=2 ? '#c9913a' : '#c07a6b';
return v==null ? '—' : '<span style="color:'+c+';font-weight:800;font-size:.82rem">'+'★'.repeat(n)+'<span style="color:#ccc">'+'★'.repeat(5-n)+'</span></span>';
return v==null ? '<span style="color:#9a9a9a"></span>' : _pctBar(Math.round(Math.max(0,Math.min(5,v))/5*100));
}},
{k:'failed', label:'Status', align:'center', fmt:function(v, row){
var s = row.state||{};
@@ -2536,7 +2540,7 @@ async function openLiveSessionDetail(sessionKey) {
var dur = _liveFmtMin(sess.durationSec||0);
var stars = sess.stars;
var starsHtml = stars != null
? '<span style="color:#e8c547;font-size:1.1rem">'+'★'.repeat(stars)+'</span><span style="color:#ccc;font-size:1.1rem">'+'★'.repeat(5-stars)+'</span>'
? _pctBadge(Math.round(Math.max(0,Math.min(5,stars))/5*100))
: '<span style="color:#aaa;font-size:.74rem">noch keine Wertung</span>';
var statusLabels = {good:'<span style="color:#5a8a5e;font-weight:700">✓ gut</span>',
mid:'<span style="color:#c9913a;font-weight:700">⚠ mittel</span>',
@@ -2578,8 +2582,8 @@ async function openLiveSessionDetail(sessionKey) {
if (nested.missionTitle) rows.push(['Auftrag', nested.missionTitle]);
if (nested.planCorrect != null) rows.push(['🗺️ Treffer', nested.planCorrect + ' / ' + (nested.planTotal||'?')]);
if (nested.planPct != null) rows.push(['🎯 Genauigkeit', nested.planPct + ' %']);
if (nested.startScore != null) rows.push(['🛫 Start', '★'.repeat(nested.startScore||0) + '☆'.repeat(5-(nested.startScore||0))]);
if (nested.landingScore != null)rows.push(['🛬 Landung', '★'.repeat(nested.landingScore||0) + '☆'.repeat(5-(nested.landingScore||0))]);
if (nested.startScore != null) rows.push(['🛫 Start', Math.round((nested.startScore||0)/5*100) + ' %']);
if (nested.landingScore != null)rows.push(['🛬 Landung', Math.round((nested.landingScore||0)/5*100) + ' %']);
if (nested.planTakeover) rows.push(['Routenplanung', '<span style="color:#c07a6b">Chefpilot übernahm</span>']);
if (nested.failed) rows.push(['Mission', '<span style="color:#c07a6b">gescheitert</span>'+(nested.failReason?': '+esc(nested.failReason):'')]);
}
@@ -2844,8 +2848,8 @@ function startLiveStatusPoll() {
// === INTERVENTIONS-ÜBERSICHT (neu, oben im Übersicht-Tab) ===
var IV_CSS = ''
+ '.iv-wrap{--ivg:#3e9b63;--ivli:#7aa03a;--ivy:#dcae35;--ivo:#db8a33;--ivr:#cc4a37;'
+ '--ivns:#5b7fa6;--ivnm:#3f8f8f;--ivnw:#8f6f9c;--ivngd:#6a72b0;--ivni:#8c857c;'
+ '.iv-wrap{--ivg:#1f7a3d;--ivli:#7cb342;--ivy:#f9c33b;--ivo:#ef8b2c;--ivr:#d8453a;'
+ '--ivns:#b04a3e;--ivnm:#155e2a;--ivnw:#d98b2c;--ivngd:#6a72b0;--ivni:#8c857c;'
+ '--ivtrack:rgba(41,52,46,.13);--ivline:rgba(0,0,0,.08);--ivsoft:#f5f3ec;--ivmut:#8a8a8a;font-size:14px}'
+ '.iv-wrap *{box-sizing:border-box}'
+ '.iv-h1{font-size:1.15rem;font-weight:800;margin:0 0 14px;color:#3d4a44}'
@@ -2907,7 +2911,7 @@ var IV_CSS = ''
+ '@media (prefers-reduced-motion:reduce){.iv-dotA{animation:none!important}}';
function ivInjectCss(){ if(!document.getElementById('iv-css')){ var s=document.createElement('style'); s.id='iv-css'; s.textContent=IV_CSS; document.head.appendChild(s); } }
function ivAmp(v){ return v<38?'iv-r':v<50?'iv-o':v<62?'iv-y':v<75?'iv-li':'iv-g'; }
function ivAmp(v){ return v<50?'iv-r':v<60?'iv-o':v<80?'iv-y':v<90?'iv-li':'iv-g'; } // = _pctColor-Grenzen
function ivErf(v){
if (v==null||isNaN(v)) return '<span class="iv-erf"><span class="iv-ebar"><i style="width:0"></i></span><b style="color:var(--ivmut)"></b></span>';
v=Math.round(v);
@@ -2937,16 +2941,18 @@ var IV_ICON={
good:'<path d="m4 15 5-5 4 4 7-8"/><path d="M17 6h3v3"/>',
idle:'<circle cx="12" cy="12" r="9" stroke-dasharray="3 3"/><path d="M12 8v4l2 2"/>',
calm:'<circle cx="12" cy="12" r="9"/><path d="m8.5 12.5 2.5 2.5 4.5-5"/>'};
var IV_META={support:['Fördern','zuletzt schwächere Läufe','--ivns'],more:['Fordern','zuletzt sicher, kaum gefordert','--ivnm'],
work:['Begleiten','zuletzt viel gearbeitet, wenig Ertrag','--ivnw'],good:['Bestätigen','zuletzt besser geworden','--ivngd'],
calm:['Läuft gut','solide unterwegs, keine Auffälligkeiten','--ivg'],idle:['Nicht aktiv','länger nicht mehr dran (oder noch nie)','--ivni']};
var IV_META={support:['Fördern','schwach (< 50 %) braucht Hilfe','--ivns'],more:['Fordern','kann alles ( 90 %) mehr herausfordern','--ivnm'],
work:['Begleiten','schwach, aber fleißiger als der Klassenschnitt','--ivnw'],good:['Bestätigen','zuletzt besser geworden','--ivngd'],
calm:['Läuft gut','solide unterwegs (5089 %)','--ivg'],idle:['Nicht aktiv','länger nicht mehr dran (oder noch nie)','--ivni']};
var IV_WEEK_CAP = 10800; // eine Woche zählt max. 3 h aktive Zeit (Ausreißer-Schutz)
// Rendert die 5 Triage-Karten (support/more/work/good/idle) aus fertigen Gruppen.
// Wiederverwendet für „HEUTE" und „insgesamt". todayMode passt Untertitel/Tooltips an.
function _ivTriageCards(groups, maxSec, todayMode){
var h='<div class="iv-grid">';
['support','more','work','good','calm','idle'].forEach(function(key){
// 4 Handlungs-Felder (aus dem Leistungs-% + Fleiß) + „Nicht aktiv". „Bestätigen"
// entfällt — geht in „Läuft gut" auf.
['support','work','calm','more','idle'].forEach(function(key){
var rows=groups[key], m=IV_META[key];
var sub=(todayMode && key==='idle')?'heute noch nicht dran':m[1];
h+='<div class="iv-card" style="--iva:var('+m[2]+')"><div class="iv-chead"><span class="iv-ic"><svg viewBox="0 0 24 24">'+IV_ICON[key]+'</svg></span><div class="iv-ct"><h4>'+m[0]+'</h4><p>'+sub+'</p></div><span class="iv-cn num">'+rows.length+'</span></div>';
@@ -2968,14 +2974,15 @@ function _ivTriageCards(groups, maxSec, todayMode){
// Gesehene, kein Allzeit-Rückblick): recentErfolg = Ø letzte 3 Läufe, daysSince =
// Tage seit letztem Lauf, recentPlays = Läufe der letzten 14 Tage, rising =
// zuletzt besser als der bisherige Schnitt.
function ivTriage(recErf, daysSince, recentPlays, rising){
if (daysSince == null || daysSince > 14) return 'idle'; // länger nicht dran / nie
if (recErf == null) return 'idle';
if (recErf < 48 && recentPlays >= 5) return 'work'; // zuletzt viel, wenig Ertrag
if (recErf < 48) return 'support'; // zuletzt schwach
if (recErf >= 65 && recentPlays <= 2) return 'more'; // zuletzt sicher, kaum gefordert
if (rising) return 'good'; // zuletzt besser geworden
return null; // läuft rund
// Triage aus dem Leistungs-% (0100) × Fleiß-im-Klassenvergleich.
// ≥90 → Fordern (kann alles) · 5089 → Läuft gut · <50 & fleißig Begleiten ·
// <50 & nicht fleißig Fördern. Länger als 14 Tage weg / kein Wert Nicht aktiv.
function ivTriage(erf, daysSince, fleissig){
if (daysSince == null || daysSince > 14) return 'idle';
if (erf == null) return 'idle';
if (erf >= 90) return 'more'; // Fordern
if (erf >= 50) return 'calm'; // Läuft gut
return fleissig ? 'work' : 'support'; // Begleiten : Fördern
}
function ivZuletzt(days){
if (days == null) return '<span class="iv-last iv-last-old">nie</span>';
@@ -3012,6 +3019,22 @@ function ivBuildOverview(cockpit, weeks, studentsCount, matrix, todaySummary, to
var playsById={};
((matrix&&matrix.students)||[]).forEach(function(s){ playsById[s.id]=(matrix.totals[s.id]||{}).plays||0; });
// Fleiß-Referenz (Klassenschnitt): aktive Zeit (Wochen-Buckets) + Durchgänge.
var _secTot={}, _secSum=0, _secCnt=0, _playSum=0, _playCnt=0;
cs.forEach(function(s){
var sid=s.studentId||s.id;
var w=wById[sid]; var tsec=w?w.buckets.reduce(function(a,b){return a+Math.min(IV_WEEK_CAP,b.sum||0);},0):0;
_secTot[sid]=tsec; if(tsec>0){_secSum+=tsec;_secCnt++;}
var pl=playsById[sid]||0; if(pl>0){_playSum+=pl;_playCnt++;}
});
var _avgSec=_secCnt?_secSum/_secCnt:1, _avgPlay=_playCnt?_playSum/_playCnt:1;
// „fleißig" = kombiniert (Zeit + Durchgänge) mindestens Klassenschnitt.
function _fleissig(sid, plays){
var t=(_secTot[sid]||0)/Math.max(1,_avgSec);
var p=(plays!=null?plays:(playsById[sid]||0))/Math.max(1,_avgPlay);
return (0.5*t + 0.5*p) >= 1;
}
var groups={support:[],more:[],work:[],good:[],calm:[],idle:[]}, erfSum=0,erfN=0, weekTotals=[];
cs.forEach(function(s){
var sid=s.studentId||s.id;
@@ -3025,7 +3048,7 @@ function ivBuildOverview(cockpit, weeks, studentsCount, matrix, todaySummary, to
var rising=(recErf!=null && s.earlyErfolg!=null && recErf > s.earlyErfolg+10); // letzte 3 besser als frühere
if(recErf!=null){ erfSum+=recErf; erfN++; }
weekTotals.push(secs);
var g=ivTriage(recErf, days, rplays, rising);
var g=ivTriage(recErf, days, _fleissig(sid, playsById[sid]));
var row={sid:sid, name:s.displayName, erf:recErf, status:s.status, secs:secs, days:days};
if(g) groups[g].push(row); else groups.calm.push(row);
});
@@ -3100,15 +3123,12 @@ function ivBuildOverview(cockpit, weeks, studentsCount, matrix, todaySummary, to
var tErf=todayErfById[sid];
var w2=wById[sid], secs2=w2?w2.buckets.map(function(b){return Math.min(IV_WEEK_CAP, b.sum||0);}):[];
while(secs2.length<10) secs2.unshift(0);
// Totalversagen heute (ein Lauf < 40) immer Fördern, egal wie der Schnitt ist.
var g2 = !played ? 'idle'
: (todayMinById[sid]!=null && todayMinById[sid] < 40) ? 'support'
: ivTriage(tErf, 0, todayPlaysById[sid], false);
var g2 = !played ? 'idle' : ivTriage(tErf, 0, _fleissig(sid, todayPlaysById[sid]));
var row2={sid:sid, name:s.displayName, erf:played?tErf:null, status:s.status, secs:secs2, days:played?0:(s.daysSince!=null?s.daysSince:null)};
if(g2) heuteGroups[g2].push(row2); else heuteGroups.calm.push(row2);
});
var heutePlayed=Object.keys(todayPlaysById).length;
var need=groups.support.length+groups.more.length+groups.work.length+groups.good.length+groups.idle.length;
var need=groups.support.length+groups.work.length+groups.more.length+groups.idle.length;
// 1) HEUTE (oben) — genau dieselbe Darstellung, nur auf heute gefiltert
h+='<div class="iv-sec">Empfohlene Aktivität für dich — <b style="color:#2a5a2e">HEUTE</b><span>'+heutePlayed+' heute aktiv</span></div>';
@@ -3170,7 +3190,7 @@ function ivAvatar(emoji){
}
return '<span class="em" style="font-size:1.6rem">'+(emoji||'🧑‍🎓')+'</span>';
}
function ivStars(n){ return (typeof ggsStarsHtml==='function') ? ggsStarsHtml(n) : ('★'.repeat(Math.max(0,Math.min(5,Math.round(n)))) ); }
function ivStars(n){ return _pctBar(Math.round(Math.max(0,Math.min(5,n||0))/5*100)); }
async function ivOpenStudent(sid){
ivInjectCss();
var d = await api('live?student_runs='+sid);
@@ -3381,7 +3401,7 @@ function ivTodayAvatar(emoji, px) {
function ivBuildTodaySection(today, modById) {
var tt = (today && today.topToday) || [];
var prog = (today && today.progress) || [];
var starsHtml = (typeof ggsStarsHtml === 'function') ? ggsStarsHtml : function(n){ return '★'.repeat(Math.max(0,Math.min(5,n))); };
var starsHtml = function(n){ return _pctBadge(Math.round(Math.max(0,Math.min(5,n||0))/5*100)); };
// Heute neu erreichte Abzeichen: Bedingung jetzt erfüllt, vor heute nicht.
var newBadges = [];
@@ -4689,7 +4709,7 @@ function renderModDetail() {
stu.forEach(function(s) {
var stars = s.best_stars || 0;
var starStr = '★'.repeat(stars) + '<span style="opacity:.3">'+'★'.repeat(5-stars)+'</span>';
var starStr = _pctBar(Math.round(Math.max(0,Math.min(5,stars))/5*100));
var lastDate = s.last_played ? new Date(s.last_played.replace(' ','T')+'Z').toLocaleDateString('de-AT', {day:'2-digit',month:'2-digit'}) : '—';
var dur = s.total_duration_ms ? Math.round(s.total_duration_ms/60000)+' min' : '—';
var avatar = (typeof s.emoji === 'string' && s.emoji.indexOf('avatar:') === 0)
@@ -4948,7 +4968,7 @@ function renderSonnensystemSessionTeacher(sess) {
}
var quote = total > 0 ? Math.round(done/total*100) : 0;
var starsHtml = sess.complete
? '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>'
? _pctBadge(quote)
: '<span style="font-size:.7rem;color:#c9913a;font-weight:700">⏸ läuft / abgebrochen</span>';
var statusEmoji = sess.complete ? '🪐' : '⏳';
var statusLabel = sess.complete ? 'Aufgaben-Runde abgeschlossen' : 'Teil-Durchlauf (nicht abgeschlossen)';
@@ -5033,7 +5053,7 @@ function renderRunsLineChart(simId, runs) {
}
function fmtMetric(m, v){
if (v === null) return '';
if (m.type === 'stars' || m.type === 'starsSmall') return Math.round(v) + '★';
if (m.type === 'stars' || m.type === 'starsSmall') return _pctBar(Math.round(Math.max(0,Math.min(5,v))/5*100));
if (m.type === 'pct' || m.type === 'pctInv' || m.type === 'ratio') return Math.round(v) + ' %';
if (m.type === 'temp') return (Math.round(v*10)/10).toLocaleString('de-AT') + ' °C';
var n = Math.abs(v) >= 100 ? Math.round(v) : Math.round(v * 10) / 10;
@@ -5136,7 +5156,7 @@ function renderRunDetailCard(simId, run, idx) {
function renderSonnensystemRun(dt, dur, r, d) {
var stars = Math.max(0, Math.min(5, r.stars || (d.stars||0)));
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
var starsHtml = _pctBadge(Math.round(stars/5*100));
var done = d.completed || 0;
var total = d.total || 0;
var quote = d.scoreQuote != null ? d.scoreQuote : (total > 0 ? Math.round(done/total*100) : 0);
@@ -5196,7 +5216,7 @@ function renderEnergiemanagerRun(dt, dur, r, d) {
+ '</div>';
}
var stars = Math.max(0, Math.min(5, r.stars || 0));
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
var starsHtml = _pctBadge(Math.round(stars/5*100));
var setIcon = d.setIcon || '⚡';
var setTitle = d.setTitle || d.setId || '?';
var blocksB = d.blocksBalanced || 0;
@@ -5238,7 +5258,7 @@ function renderEnergiemanagerRun(dt, dur, r, d) {
function renderEuWerkstattRun(dt, dur, r, d) {
var stars = Math.max(0, Math.min(5, r.stars || 0));
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
var starsHtml = _pctBadge(Math.round(stars/5*100));
var endTypeLabel = ({success:'🏆 Gewonnen', mixed:'⚖️ Teilweise', rejected:'❌ Verloren'})[d.endType] || '—';
var endTypeColor = ({success:'#5a8a5e', mixed:'#c9913a', rejected:'#c07a6b'})[d.endType] || '#8a8a8a';
var diffLabel = d.difficulty === 'expert' ? '🦉 Profi' : '🌱 Einfach';
@@ -5285,8 +5305,8 @@ function renderEuWerkstattRun(dt, dur, r, d) {
}
function renderBusfahrtRun(dt, dur, r, d) {
var stars = Math.max(0, Math.min(5, r.stars || 0));
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
var stars = Math.max(0, Math.min(3, r.stars || 0)); // Busfahrt: 03-Skala
var starsHtml = _pctBadge(Math.round(stars/3*100));
var lvl = (d.level || 'l1').toString().toUpperCase();
var budget = d.results && d.results.budget != null ? d.results.budget : (d.budget != null ? d.budget : 0);
var earnings = d.results && d.results.earnings != null ? d.results.earnings : (d.earnings != null ? d.earnings : 0);
@@ -5314,7 +5334,7 @@ function renderHeliRun(dt, dur, r, d) {
// Heli liefert results direkt im d (siehe submitHeliMission in game.html)
var res = d.results || d;
var stars = Math.max(0, Math.min(5, r.stars || (d.stars || 0)));
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
var starsHtml = _pctBadge(Math.round(stars/5*100));
var failed = !!res.failed;
var statusIcon = failed ? '🧑‍✈️' : '🚁';
var statusLbl = failed ? 'Chefpilot übernahm' : 'Einsatz erfolgreich';
@@ -5344,8 +5364,8 @@ function renderHeliRun(dt, dur, r, d) {
+ '</div>'
+ '<div class="run-stats">'
+ '<span>🗺️ Navigation: <b>'+planCorrect+'</b>/'+planTotal+' ('+planPct+'%)</span>'
+ '<span>🛫 Start: <b>'+'★'.repeat(startScore)+'</b></span>'
+ '<span>🛬 Landung: <b>'+'★'.repeat(landingScore)+'</b></span>'
+ '<span>🛫 Start: <b style="color:'+_pctColor((startScore||0)/5*100)+'">'+Math.round((startScore||0)/5*100)+' %</b></span>'
+ '<span>🛬 Landung: <b style="color:'+_pctColor((landingScore||0)/5*100)+'">'+Math.round((landingScore||0)/5*100)+' %</b></span>'
+ (heliName ? '<span>'+esc(heliName)+'</span>' : '')
+ '</div>'
+ (takeoverNotes.length ? '<div style="margin-top:.4rem;color:#c9913a;font-size:.78rem">⚠ '+takeoverNotes.join(' · ')+'</div>' : '')
@@ -5354,8 +5374,8 @@ function renderHeliRun(dt, dur, r, d) {
}
function renderFluggesellschaftRun(dt, dur, r, d) {
var stars = Math.max(0, Math.min(5, r.stars || 0));
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
var stars = Math.max(0, Math.min(3, r.stars || 0)); // Fluggesellschaft: 03-Skala
var starsHtml = _pctBadge(Math.round(stars/3*100));
var lvl = (d.level || 'l1').toString().toUpperCase();
var res = d.results || d;
var budget = res.budget != null ? res.budget : 0;