
  @import url('https://fonts.googleapis.com/css2?family=Creepster&family=Bebas+Neue&family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600;700;800&display=swap');

  :root{
    --bg:#0a0b0e;
    --bg-glow: radial-gradient(90% 60% at 50% -10%, #1b1206 0%, #0a0b0e 55%), radial-gradient(70% 50% at 85% 110%, rgba(255,122,41,.05) 0%, transparent 60%);
    --panel:#141519;
    --panel-2:#1a1c21;
    --panel-grad:linear-gradient(160deg, #191a20 0%, #121317 100%);
    --border:#25272e;
    --ink:#f3f1ec;
    --ink-dim:#8d919a;
    --accent:#ff7a29;
    --accent-2:#ffa35c;
    --accent-grad:linear-gradient(135deg, var(--accent), var(--accent-2));
    --mansion:#4b4f56;
    --valley:#3b82f6;
    --phobia:#ef4444;
    --gold:#ffcf5c;
    --silver-glow:#d4d6dd;
    --copper-glow:#e6a06e;
    --danger:#e0524a;
    --radius:18px;
    --radius-sm:11px;
    /* Mobile-Design-Audit (14.09.2026), Schritt 3: NEUE, rein additive Tokens --
       bestehende Werte (--radius/--radius-sm oben) bleiben unveraendert, damit
       dieser Schritt garantiert keine einzige sichtbare Aenderung hat. Die Werte
       hier benennen nur Zahlen, die im Code bereits mehrfach wiederkehrend
       verwendet werden (siehe Audit-Bericht), aber bisher an jeder Stelle neu
       hart codiert waren. Noch NICHT auf bestehende Selektoren angewendet --
       das passiert erst schrittweise in Schritt 4, ein Bereich nach dem
       anderen, jeweils mit eigenem Vorher/Nachher-Vergleich. */
    --radius-tag:6px;     /* Tags/Chips/kleine Badges -- bereits so an 5 Stellen verwendet */
    --radius-pill:999px;  /* Pillen/Kapsel-Buttons -- bereits so an 8 Stellen verwendet */
    --icon-sm:14px;       /* kompakte Inline-Icons neben kleiner Beschriftung */
    --icon-md:16px;       /* Standard-Icon neben normalem Fließtext */
    --gap-sm:14px;        /* Abstand innerhalb eines Blocks/einer Karte */
    --gap-md:20px;        /* Abstand zwischen zwei Abschnitten */
    --shadow-card:0 18px 40px -22px rgba(0,0,0,.7), inset 0 1px 0 rgba(255,255,255,.03);
    --shadow-accent:0 10px 26px -10px rgba(255,122,41,.55);
  }
  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;height:100%;}
  body{
    background:var(--bg-glow), var(--bg);
    color:var(--ink);
    font-family:'Inter',system-ui,sans-serif;
    -webkit-font-smoothing:antialiased;
    overflow-x:hidden;
    /* Höhe zuerst mit 100vh als Fallback, dann mit 100dvh überschrieben (berücksichtigt
       auf Handys die ein-/ausblendende Adressleiste korrekt; ältere Browser ignorieren
       die zweite Zeile einfach und bleiben beim vh-Fallback). Body selbst scrollt nicht
       mehr — das eigentliche Markup steckt zur Laufzeit in #dw-app-root (siehe unten). */
    height:100vh;
    height:100dvh;
    overflow:hidden;
  }
  /* Mitarbeiterbereich (Team-Liste/Kader-Editor/Highscore-Eingabe/Verlauf,
     js/staff.js + js/roster.js + js/scores.js) hat -- anders als die TV-/
     Gast-Rangliste -- KEINEN eigenen internen Scroll-Container (kein
     .tv-scroll-Äquivalent), sondern rendert direkt in #app-root. Mit dem
     oben fest auf 100dvh + overflow:hidden gesetzten body war der Inhalt bei
     mehreren Teams/Einträgen dadurch nach unten hin abgeschnitten und nicht
     erreichbar (live gemeldet: "kann bei mehreren Teams nicht weiter nach
     unten"). Diese Klasse wird von den betroffenen render*()-Funktionen auf
     document.body gesetzt (und beim Zurückwechseln zur Rangliste wieder
     entfernt) und hebt das feste Voll-Höhe-Verhalten gezielt für diese
     Seiten auf -- die Rangliste/TV-Anzeige selbst ist davon nicht betroffen. */
  body.staff-scroll{
    height:auto;
    min-height:100dvh;
    overflow-y:auto;
    overflow-x:hidden;
  }
  /* #dw-app-root ist der tatsächliche Eltern-Container von Kopfzeile und Ansichten
     (wird beim Start aus der Vorlage befüllt) — er, nicht body, ist die feste,
     bildschirmfüllende Spalte: die Kopfzeile bleibt oben, die jeweils aktive Ansicht
     füllt den Rest und scrollt bei Bedarf innerhalb ihrer selbst. Dadurch kann z.B. die
     Rangliste unten eine fest sichtbare Navigation haben, ohne dass Kopf- oder
     Fußbereich mitscrollen. */
  #dw-app-root{height:100%;display:flex;flex-direction:column;overflow:hidden;}
  @keyframes riseIn{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}
  .display{font-family:'Space Grotesk',sans-serif;}
  .numeric{font-family:'Bebas Neue',sans-serif; letter-spacing:.5px;}
  .flourish{font-family:'Creepster',cursive; font-weight:400;}

  .icon{width:18px;height:18px;stroke:currentColor;fill:none;flex:none;vertical-align:middle;}
  .icon.fill{fill:currentColor;stroke:none;}

  /* ---------- Loading ---------- */
  #loading{
    position:fixed;inset:0;background:var(--bg);z-index:999;
    display:flex;align-items:center;justify-content:center;flex-direction:column;gap:14px;
    transition:opacity .35s ease;
  }
  /* 54. Pruefrunde Nachtrag 7: sanftes Ausblenden statt hartem display:none (siehe
     init() weiter unten in der App-Logik) -- macht den unvermeidbaren automatischen
     Seiten-Reload nach jedem Speichern (loest die Artifact-Plattform selbst aus,
     auch auf dem speichernden Geraet) optisch zu einer kurzen Ueberblendung statt
     eines harten Blinkens. */
  #loading.fade-out{opacity:0;pointer-events:none;}
  #loading .icon{width:46px;height:46px;color:var(--accent);animation:pulse 1.3s ease-in-out infinite;}
  @keyframes pulse{0%,100%{transform:scale(1);opacity:1;}50%{transform:scale(1.12);opacity:.6;}}
  #loading span.label{font-family:'Inter';color:var(--ink-dim);font-size:13px;letter-spacing:.4px;}

  /* ---------- Topbar ---------- */
  .topbar{
    position:sticky;top:0;z-index:50;flex:none;
    display:flex;align-items:center;justify-content:space-between;
    padding:15px 24px;
    background:rgba(10,11,14,.92);
    border-bottom:1px solid var(--border);
    box-shadow:0 12px 30px -18px rgba(0,0,0,.8);
    backdrop-filter:blur(10px);
    flex-wrap:wrap;
    gap:12px;
  }
  .topbar::after{
    content:'';position:absolute;left:0;right:0;bottom:-1px;height:2px;pointer-events:none;
    background:linear-gradient(90deg, transparent 2%, var(--accent) 22%, #ff3b30 52%, #8f1d17 78%, transparent 98%);
    opacity:.8;
  }
  .brand{display:flex;align-items:center;gap:13px;}
  .logo-badge{
    width:46px;height:46px;border-radius:13px;
    background:var(--panel-grad);border:1px solid var(--border);
    display:flex;align-items:center;justify-content:center;
    overflow:hidden;flex:none;
    box-shadow:0 0 0 1px rgba(255,122,41,.12), 0 8px 18px -8px rgba(255,122,41,.45);
  }
  .logo-badge img{width:100%;height:100%;object-fit:contain;padding:6px;}
  .brand-text{line-height:1.15;}
  .brand-title{font-family:'Space Grotesk';font-weight:700;font-size:15px;letter-spacing:.6px;color:var(--ink);}
  .event-badge{
    display:inline-flex;align-items:center;gap:6px;margin-top:5px;
    /* Mobile-Design-Audit, Schritt 4a: border-radius war bereits hart 999px --
       reine Token-Umbenennung, kein Wertwechsel (siehe Token-Vorschlag Abschnitt C). */
    padding:3px 11px 3px 9px;border-radius:var(--radius-pill);
    background:linear-gradient(90deg, rgba(224,82,74,.14), rgba(255,122,41,.07));
    border:1px solid rgba(255,90,60,.28);
  }
  .event-badge .icon{width:11px;height:11px;color:var(--accent);flex:none;}
  .event-badge-logo{height:10px;width:auto;display:block;filter:drop-shadow(0 0 4px rgba(255,60,40,.5));}
  .event-badge .flourish{color:var(--accent-2);font-size:14px;line-height:1;white-space:nowrap;}

  .mode-switch{display:flex;background:var(--panel);border:1px solid var(--border);border-radius:999px;padding:4px;gap:4px;box-shadow:var(--shadow-card);}
  .mode-btn{
    display:flex;align-items:center;gap:7px;
    border:none;background:transparent;color:var(--ink-dim);
    font-family:'Inter';font-weight:700;font-size:13px;
    padding:9px 16px;border-radius:999px;cursor:pointer;
    transition:.18s;
  }
  .mode-btn .icon{width:15px;height:15px;}
  .mode-btn.active{background:var(--accent-grad);color:#1a0e02;box-shadow:var(--shadow-accent);}
  .mode-btn .lock-indicator{width:12px;height:12px;opacity:.8;}

  .lock-btn{
    display:flex;align-items:center;gap:6px;margin-left:10px;
    background:transparent;color:var(--ink-dim);border:1px solid var(--border);
    font-family:'Inter';font-weight:700;font-size:12px;
    padding:8px 14px;border-radius:999px;cursor:pointer;transition:.18s;
  }
  .lock-btn:hover{border-color:var(--danger);color:var(--danger);box-shadow:0 6px 16px -8px rgba(224,82,74,.5);}
  .lock-btn .icon{width:13px;height:13px;}

  .testdata-flag{
    display:none;align-items:center;gap:6px;margin-left:10px;
    font-family:'Inter';font-weight:800;font-size:10.5px;letter-spacing:.5px;
    color:#fff;background:linear-gradient(90deg,#ff7a29,#e0524a);
    padding:7px 13px;border-radius:999px;white-space:nowrap;
    box-shadow:0 4px 14px -4px rgba(224,82,74,.55);
  }
  .testdata-flag .icon{width:11px;height:11px;flex:none;}

  .view{display:none;padding:22px;max-width:1400px;margin:0 auto;width:100%;}
  .view.active{display:block;flex:1 1 0;min-height:0;overflow-y:auto;}

  /* ---------- Panels ---------- */
  .panel{
    background:var(--panel-grad);
    border:1px solid var(--border);
    border-radius:var(--radius);
    padding:22px;
    box-shadow:var(--shadow-card);
    position:relative;
    overflow:hidden;
    animation:riseIn .4s ease both;
  }
  .panel::before{
    content:'';position:absolute;top:0;left:18px;right:18px;height:2px;
    background:linear-gradient(90deg,transparent,var(--accent),transparent);
    opacity:.55;border-radius:2px;
  }
  .panel h2{
    display:flex;align-items:center;gap:9px;
    font-family:'Space Grotesk';font-weight:700;font-size:16px;color:var(--ink);
    margin:0 0 16px 0;
  }
  .panel h2 .icon{color:var(--accent);width:19px;height:19px;filter:drop-shadow(0 0 6px rgba(255,122,41,.4));}
  .grid-2{display:grid;grid-template-columns:1fr 1fr;gap:18px;}
  @media (max-width:900px){.grid-2{grid-template-columns:1fr;}}

  label{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.6px;color:var(--ink-dim);margin-bottom:6px;font-weight:700;}
  input[type=text], input[type=number], input[type=password], select{
    width:100%;background:var(--panel-2);border:1px solid var(--border);color:var(--ink);
    padding:11px 13px;border-radius:var(--radius-sm);font-size:15px;font-family:'Inter';
  }
  input:focus, select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,122,41,.15);}
  input.input-error, select.input-error{border-color:var(--danger);}
  input.input-error:focus, select.input-error:focus{box-shadow:0 0 0 3px rgba(224,82,74,.18);}
  /* 54. Pruefrunde Nachtrag 6: Live-Hinweis unter Team-/Spieler-Namensfeldern */
  .name-dupe-hint{display:none;align-items:center;gap:5px;font-size:11.5px;font-weight:600;margin-top:6px;line-height:1.3;}
  .name-dupe-hint.show-error{display:flex;color:var(--danger);}
  .name-dupe-hint.show-ok{display:flex;color:#4ade80;}
  .name-dupe-hint .icon{width:12px;height:12px;flex:none;}
  .field{margin-bottom:14px;}
  .row{display:flex;gap:10px;}
  .row > *{flex:1;}

  button{cursor:pointer;font-family:'Inter';}
  .btn{
    display:inline-flex;align-items:center;justify-content:center;gap:8px;
    background:var(--accent-grad);color:#1a0e02;border:none;font-weight:700;
    padding:12px 18px;border-radius:var(--radius-sm);font-size:14px;
    box-shadow:var(--shadow-accent);
    transition:.18s;
  }
  .btn:hover{filter:brightness(1.06);transform:translateY(-1px);box-shadow:0 14px 30px -10px rgba(255,122,41,.65);}
  .btn:active{transform:translateY(0) scale(.98);}
  .btn.secondary{background:var(--panel-2);color:var(--ink);border:1px solid var(--border);box-shadow:none;}
  .btn.secondary:hover{border-color:var(--accent);filter:none;box-shadow:0 8px 18px -10px rgba(0,0,0,.5);}
  .btn.danger{background:transparent;color:var(--danger);border:1px solid var(--danger);font-weight:700;padding:6px 10px;font-size:12px;box-shadow:none;}
  .btn.danger:hover{background:var(--danger);color:#fff;filter:none;transform:none;box-shadow:0 8px 18px -10px rgba(224,82,74,.5);}
  .btn:disabled{opacity:.4;cursor:not-allowed;transform:none;box-shadow:none;}
  .btn-block{width:100%;}

  /* game pills */
  .game-pills{display:flex;gap:8px;margin-bottom:14px;flex-wrap:wrap;}
  .game-pill{
    flex:1;min-width:110px;padding:12px 10px;border-radius:var(--radius-sm);border:1.5px solid var(--border);
    background:var(--panel-2);color:var(--ink-dim);text-align:center;font-weight:700;font-size:12px;
    letter-spacing:.3px;transition:.18s;font-family:'Space Grotesk';
  }
  .game-pill[data-game="Mansion"].active{border-color:var(--mansion);background:rgba(160,164,171,.18);color:#b9bcc2;box-shadow:0 8px 20px -8px rgba(160,164,171,.35);}
  .game-pill[data-game="Valley"].active{border-color:var(--valley);background:rgba(59,130,246,.16);color:#93c5fd;box-shadow:0 8px 20px -8px rgba(59,130,246,.35);}
  .game-pill[data-game="Phobia"].active{border-color:var(--phobia);background:rgba(239,68,68,.16);color:#fca5a5;box-shadow:0 8px 20px -8px rgba(239,68,68,.35);}

  #playerInputs{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;margin-bottom:12px;}
  .player-slot input{margin-bottom:6px;}
  .player-slot input:last-child{margin-bottom:0;}
  .player-slot .player-name{font-weight:600;}
  .avg-preview{
    display:flex;justify-content:space-between;align-items:center;
    background:var(--panel-2);border:1px dashed var(--border);border-radius:var(--radius-sm);
    padding:12px 14px;margin-bottom:14px;
  }
  .avg-preview .val{font-family:'Bebas Neue';font-size:28px;color:var(--gold);}

  /* team list */
  #teamList{max-height:430px;overflow-y:auto;padding-right:4px;margin:0 -4px;}
  #teamList::-webkit-scrollbar{width:6px;}
  #teamList::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px;}
  #teamList::-webkit-scrollbar-thumb:hover{background:var(--accent);}
  .team-item{
    display:flex;justify-content:space-between;align-items:center;gap:10px;
    padding:11px 8px;border-bottom:1px solid var(--border);font-size:14px;
    border-radius:8px;transition:.15s;margin:0 0 0 0;
  }
  .team-item:hover{background:rgba(255,122,41,.06);}
  .team-item-info{min-width:0;flex:1;}
  .team-item-name{word-break:break-word;}
  .team-item .meta{color:var(--ink-dim);font-size:12px;margin-top:3px;display:flex;align-items:center;flex-wrap:wrap;gap:6px;row-gap:4px;}
  .team-item-actions{display:flex;gap:6px;flex:none;}
  .team-done-badge{
    display:inline-flex;align-items:center;gap:4px;vertical-align:middle;
    font-size:9.5px;font-weight:800;letter-spacing:.3px;color:#4ade80;
    background:rgba(74,222,128,.12);border:1px solid rgba(74,222,128,.35);
    padding:2px 7px;border-radius:999px;
  }
  .team-done-badge .icon{width:8px;height:8px;}

  /* Kader-Bearbeitung (Roster-Editor je Team) */
  .roster-edit{background:var(--panel-2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px 14px;margin:2px 0 14px;}
  .roster-hint{font-size:11.5px;color:var(--ink-dim);margin-bottom:10px;line-height:1.4;}
  .roster-row-wrap{margin-bottom:8px;}
  .roster-row{display:flex;gap:8px;align-items:center;}
  .roster-row input{flex:1;}
  .roster-remove{
    flex:none;width:38px;height:38px;border-radius:8px;
    background:transparent;border:1px solid var(--border);color:var(--ink-dim);
    display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:700;
  }
  .roster-remove:hover:not(:disabled){border-color:var(--danger);color:var(--danger);}
  .roster-remove:disabled{opacity:.3;cursor:not-allowed;}
  .roster-add{
    background:transparent;border:1.5px dashed var(--border);color:var(--ink-dim);
    border-radius:var(--radius-sm);padding:9px;width:100%;font-size:13px;font-weight:700;margin-top:2px;
  }
  .roster-add:hover:not(:disabled){border-color:var(--accent);color:var(--accent);}
  .roster-add:disabled{opacity:.3;cursor:not-allowed;}
  .roster-actions{display:flex;gap:8px;margin-top:12px;}

  /* history */
  .hist-table{width:100%;border-collapse:collapse;margin-top:8px;font-size:13px;}
  .hist-table th{text-align:left;color:var(--ink-dim);font-weight:700;padding:8px 6px;border-bottom:1px solid var(--border);font-size:10px;text-transform:uppercase;letter-spacing:.4px;}
  .hist-table td{padding:9px 6px;border-bottom:1px solid rgba(255,255,255,.04);}
  .hist-table tr.best td{color:var(--gold);font-weight:700;}
  .tag-game{display:inline-block;padding:3px 9px;border-radius:6px;font-size:11px;font-weight:700;font-family:'Space Grotesk';}
  .tag-game.Mansion{background:rgba(160,164,171,.18);color:#b9bcc2;}
  .tag-game.Valley{background:rgba(59,130,246,.16);color:#93c5fd;}
  .tag-game.Phobia{background:rgba(239,68,68,.16);color:#fca5a5;}

  /* Run-Typ-Kennzeichnung (Inklusiv/Zusatz), siehe js/scores.js — Phase 1 der PDF-Prüfung */
  .tag-runtype{display:inline-block;padding:3px 9px;border-radius:6px;font-size:11px;font-weight:700;font-family:'Space Grotesk';background:rgba(74,222,128,.14);color:#86efac;white-space:nowrap;}

  /* ---------- Toast ---------- */
  #toast-wrap{position:fixed;top:20px;left:50%;transform:translateX(-50%);z-index:200;display:flex;flex-direction:column;gap:8px;align-items:center;}
  .toast{
    display:flex;align-items:center;gap:9px;
    background:var(--panel);border:1px solid var(--accent);color:var(--ink);
    padding:12px 20px;border-radius:var(--radius-sm);font-size:14px;font-weight:700;
    box-shadow:0 10px 34px rgba(0,0,0,.55);
    animation:toastIn .25s ease-out, toastOut .3s ease-in 2.7s forwards;
  }
  .toast .icon{color:var(--accent);width:16px;height:16px;}
  .toast.error{border-color:var(--danger);}
  .toast.error .icon{color:var(--danger);}
  @keyframes toastIn{from{opacity:0;transform:translateY(-10px);}to{opacity:1;transform:translateY(0);}}
  @keyframes toastOut{to{opacity:0;transform:translateY(-10px);}}

  /* ---------- Rank movement arrows ---------- */
  .rank-arrow{
    display:inline-flex;align-items:center;justify-content:center;
    margin-left:5px;font-weight:800;vertical-align:middle;font-family:'Inter';
    font-size:9px;letter-spacing:.3px;border-radius:5px;padding:1px 6px;
    min-width:26px;box-sizing:border-box;border:1px solid currentColor;
  }
  .rank-arrow.up{color:#3ddc84;}
  .rank-arrow.down{color:var(--danger);}
  .rank-arrow.same{color:var(--ink-dim);}
  .rank-arrow.new{color:var(--gold);}

  /* ---------- Lock / PIN ---------- */
  #pinOverlay{position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:300;display:none;align-items:center;justify-content:center;padding:20px;}
  #pinOverlay.active{display:flex;}
  .pin-box{background:var(--panel-grad);border:1px solid var(--accent);border-radius:var(--radius);padding:30px;text-align:center;width:280px;max-width:100%;box-shadow:0 30px 70px -20px rgba(0,0,0,.7), 0 0 0 1px rgba(255,122,41,.15);animation:riseIn .3s ease both;}
  .pin-box .pin-icon{width:34px;height:34px;color:var(--accent);margin-bottom:10px;filter:drop-shadow(0 0 10px rgba(255,122,41,.5));}
  .pin-box h3{font-family:'Space Grotesk';color:var(--ink);margin:0 0 6px;font-size:18px;font-weight:700;}
  .pin-box p{color:var(--ink-dim);font-size:12px;margin:0 0 16px;}
  .pin-box input{text-align:center;font-size:26px;letter-spacing:10px;margin-bottom:14px;}
  .pin-box .pin-actions{display:flex;gap:8px;}
  .pin-box p#confirmText{white-space:pre-line;}
  .btn.danger.btn-block{width:100%;padding:12px 18px;font-size:14px;background:var(--danger);color:#fff;border:1px solid var(--danger);}
  .btn.danger.btn-block:hover{background:#c43f38;}
  #confirmOverlay{position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:300;display:none;align-items:center;justify-content:center;padding:20px;}
  #confirmOverlay.active{display:flex;}
  .field-label-small{font-size:11px;text-transform:uppercase;letter-spacing:.6px;color:var(--ink-dim);margin:14px 0 6px;text-align:left;font-weight:700;}
  #changePinOverlay{position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:300;display:none;align-items:center;justify-content:center;padding:20px;}
  #changePinOverlay.active{display:flex;}
  #testDataOverlay{position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:300;display:none;align-items:center;justify-content:center;padding:20px;}
  #testDataOverlay.active{display:flex;}

  /* ---------- Confetti ---------- */
  .confetti-piece{position:fixed;top:-24px;z-index:250;pointer-events:none;animation:confettiFall linear forwards;}
  @keyframes confettiFall{
    0%{transform:translateY(0) rotate(0deg);opacity:1;}
    100%{transform:translateY(110vh) rotate(380deg);opacity:.15;}
  }

  .settings-row{display:flex;justify-content:flex-end;flex-wrap:wrap;margin-top:24px;gap:8px;}
  .settings-row button{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--ink-dim);background:none;border:1px solid var(--border);padding:7px 12px;border-radius:8px;}
  .settings-row button:hover{border-color:var(--danger);color:var(--danger);}
  .settings-row button#btnLoadTestData:hover{border-color:var(--accent);color:var(--accent);}

  ::-webkit-scrollbar{width:8px;}
  ::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px;}

  /* ---------- Rangliste view (TV) ---------- */
  /* #view-rangliste bekommt kein eigenes Scrollen mehr (overflow:hidden) — stattdessen
     übernimmt .tv-scroll INNERHALB von .tv-stage das Scrollen, damit Titel (oben) sowie
     Punkte-Navigation/Tempo/Fußzeile (unten) fest sichtbar bleiben und nur der Inhalt
     dazwischen (Podium, Tabellen) scrollt. */
  #view-rangliste.view.active{padding:0;max-width:none;overflow:hidden;}
  .tv-stage{
    height:100%;
    /* #app-root ist seit der Mehrstandort-Umstellung ein Flex-Container
       (display:flex; flex-direction:column, siehe app-extra.css) und
       .tv-stage dessen direktes Kind. In einem Spalten-Flex-Container
       bestimmt height:100% NICHT die Hauptachsen-Größe -- ohne
       flex-grow bleibt .tv-stage nur so hoch wie sein Inhalt, der Rest
       des Bildschirms bleibt leer/schwarz. Besonders sichtbar im
       Hochformat (iPad), wo der Inhalt deutlich niedriger ist als der
       Viewport. flex:1 1 auto + min-height:0 lässt .tv-stage auf die
       volle verfügbare Höhe wachsen und weiterhin schrumpfen dürfen
       (nötig, damit .tv-scroll intern scrollen kann). */
    flex:1 1 auto;
    min-height:0;
    display:flex;flex-direction:column;
    padding:38px 44px 22px;
    position:relative;
    overflow:hidden;
  }
  .tv-head{flex:none;}
  .tv-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;scrollbar-gutter:stable;}
  .dots, .footer-note, .tv-footer-row{flex:none;}
  .tv-stage::before{
    content:'';position:absolute;inset:0;z-index:-1;pointer-events:none;
    background:
      radial-gradient(55% 38% at 12% 100%, rgba(224,82,74,.07), transparent 70%),
      radial-gradient(48% 30% at 88% 0%, rgba(255,122,41,.06), transparent 70%);
  }

  /* ---------- Deadwood-Hero (Gesamt-Rangliste) ---------- */
  .deadwood-hero{
    display:flex;align-items:center;justify-content:center;gap:26px;
    padding:6px 0 30px;
  }
  .hero-text{text-align:center;}
  .hero-deadwood-logo{
    display:block;height:auto;width:auto;max-width:420px;max-height:76px;
    filter:drop-shadow(0 0 30px rgba(255,60,40,.35)) drop-shadow(0 6px 18px rgba(0,0,0,.6));
  }
  .hero-banner{
    display:inline-flex;align-items:center;gap:9px;margin-top:10px;
    font-family:'Space Grotesk';font-weight:700;font-size:14px;letter-spacing:3px;
    text-transform:uppercase;color:var(--ink-dim);
  }
  .hero-banner .icon{width:14px;height:14px;color:var(--accent);}
  .hero-decor{width:52px;height:52px;color:var(--border);opacity:.6;flex:none;}
  .hero-decor.left{transform:scaleX(-1);}
  @media (max-width:640px){
    .hero-deadwood-logo{max-width:230px;max-height:42px;}
    .hero-banner{font-size:11px;letter-spacing:2px;}
    .hero-decor{width:30px;height:30px;}
    .deadwood-hero{gap:12px;padding:2px 0 18px;}
  }
  /* Mobile-Design-Audit (14.09.2026), Schritt 1: hier stand frueher eine zweite
     .tv-head-Regel (margin-bottom:26px), die aus der Zeit VOR der "ERWEITERUNGSRUNDE"
     (siehe die echte, aktive .tv-head-Regel mit den 3 Spalten/margin-bottom:22px
     weiter unten in dieser Datei) stammte. Per getComputedStyle() bestaetigt: diese
     alte Regel gewann in der CSS-Kaskade nie (die spaetere Regel mit derselben
     Spezifitaet ueberschrieb sie immer) -- totes CSS ohne jede sichtbare Wirkung,
     jetzt entfernt. Keine visuelle Aenderung durch diesen Schritt. */
  .tv-title{font-family:'Space Grotesk';font-weight:700;font-size:34px;color:var(--ink);letter-spacing:.5px;text-shadow:0 0 34px rgba(255,122,41,.22);}
  .tv-title .accent{color:var(--accent);text-shadow:0 0 24px rgba(255,122,41,.55);}
  .title-logo{height:.8em;width:auto;vertical-align:-.06em;margin-right:2px;filter:drop-shadow(0 2px 8px rgba(0,0,0,.55));}
  .tv-sub{display:flex;align-items:center;gap:8px;color:var(--ink-dim);font-size:13px;margin-top:4px;}
  .live-dot{display:inline-block;width:8px;height:8px;border-radius:50%;background:#3ddc84;box-shadow:0 0 8px #3ddc84;animation:blink 1.6s infinite;}
  @keyframes blink{0%,100%{opacity:1;}50%{opacity:.3;}}

  .slide{display:none;}
  /* 34. Prüfrunde: die Basis-Einblendanimation liegt jetzt NICHT mehr direkt auf
     .slide.active, sondern auf einer eigenen, sich selbst wieder entfernenden
     .enter-Klasse (siehe showSlide()). Grund: .slide.active{animation:fadeIn...} lag
     als CSS-Eigenschaft dauerhaft auf der Folie, auch während des Crossfades in
     changeSlideAnimated() -- dort gewinnt zwar kurzzeitig die spezifischere
     .fx-in-Regel (slideFadeIn) die Kaskade, sobald .fx-in aber nach Abschluss des
     Crossfades wieder entfernt wird, fiel die Kaskade automatisch auf .slide.active
     zurück -- und da sich dadurch der animation-name (von slideFadeIn zu fadeIn)
     änderte, startete der Browser fadeIn NEU von 0% (Opacity 0, leichter Versatz),
     obwohl die Folie da bereits voll sichtbar war. Das erzeugte genau das gemeldete
     kurze "Blinken" ca. 300-400ms NACH jedem abgeschlossenen Folienwechsel. Jetzt
     trägt .slide.active selbst gar keine Animation mehr, die einfache Einblendung
     wird nur noch dort per JS ausgelöst, wo tatsächlich eine gewünscht ist (z. B.
     erster Seitenaufruf, Wechsel von der Eingabe- in die Rangliste-Ansicht) -- der
     Crossfade in changeSlideAnimated() bleibt davon komplett unberührt. */
  .slide.active{display:block;}
  .slide.active.enter{animation:fadeIn .4s ease;}
  @keyframes fadeIn{from{opacity:0;transform:translateY(8px);}to{opacity:1;transform:translateY(0);}}

  .dots{display:flex;gap:8px;justify-content:center;margin-top:20px;}
  .dot{width:9px;height:9px;border-radius:50%;background:var(--border);border:none;cursor:pointer;}
  .dot.active{background:var(--accent);width:26px;border-radius:5px;transition:.25s;}

  /* Tempo-Auswahl fürs automatische Weiterschalten der Folien — wirkt sich NUR auf dieses
     eine Gerät aus (in localStorage gespeichert, nicht Teil der geteilten Turnierdaten),
     damit z. B. ein Spieler auf seinem Handy sein eigenes Tempo wählen kann, ohne den
     Store-Bildschirm oder andere Betrachter zu beeinflussen. */

  /* Podium — clean medal cards */
  .stats-strip{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-bottom:14px;}
  .stat-chip{
    display:flex;align-items:center;gap:6px;
    padding:6px 13px;border-radius:999px;border:1px solid var(--border);background:var(--panel-grad);
    font-family:'Inter';font-size:11px;font-weight:600;color:var(--ink-dim);letter-spacing:.2px;
    box-shadow:0 8px 18px -12px rgba(0,0,0,.6);
  }
  .stat-chip-label{display:flex;align-items:center;gap:6px;}
  .stat-chip-count{display:flex;align-items:center;gap:2px;white-space:nowrap;}
  .stat-chip .dot{width:7px;height:7px;border-radius:50%;flex:none;border:1px solid rgba(255,255,255,.3);}
  .stat-chip b{font-family:'Bebas Neue';font-size:15px;color:var(--ink);margin:0 2px;letter-spacing:.5px;}
  /* 54. Pruefrunde: flex-wrap von "wrap" auf "nowrap" -- diese Basisregel ist die
     einzige, die fuer JEDE Bildschirmbreite gilt, bevor eine der beiden @media-Regeln
     weiter unten (max-width:640px bzw. min-width:641px) zusaetzlich greift. Mit "wrap"
     haette der Browser bei einer (eigentlich nie vorgesehenen) Zwischenbreite die
     dritte Karte in eine zweite Zeile umbrechen koennen, statt sie -- wie von
     computePodiumSizing() vorgesehen -- ueber --p-badge-h kleiner zu rechnen. "nowrap"
     stellt sicher, dass IMMER die Bisektion (nicht der Zeilenumbruch) fuer die
     Groesse zustaendig ist -- die einzige Anordnung, bei der sich zwei Icons
     ueberhaupt beruehren koennten (uebereinander statt nebeneinander), ist damit
     grundsaetzlich ausgeschlossen. */
  .podium{display:flex;align-items:flex-end;justify-content:center;gap:22px;margin:6px 0 18px;flex-wrap:nowrap;}
  /* Podium-Kärtchen seit der 26. Prüfrunde bewusst OHNE Rahmen/Hintergrund/Schatten —
     nur noch das Abzeichen-Icon, Name, Punkte und die MVP-Balken. Die Hervorhebung von
     Platz 1 entsteht allein über die größere Icon-Größe und die Anhebung (transform). */
  /* 52. Pruefrunde (Bugfix): .medal-card nutzte bisher dieselbe globale
     @keyframes riseIn{...to{transform:translateY(0);}} Animation wie viele andere
     Elemente (Panels, PIN-Box, MVP-Hero, ...) -- mit animation-fill-mode "both"
     (Teil des "both" im Shorthand) bleibt der LETZTE Keyframe-Wert einer Eigenschaft
     dauerhaft aktiv, auch nach Animationsende, und Animations-Werte ueberschreiben
     dabei IMMER die normal deklarierten CSS-Werte derselben Eigenschaft, unabhaengig
     von Spezifitaet. Das bedeutete: der eigentlich gewuenschte, rang-spezifische
     transform:translateY(...) (siehe .medal-card.rank1/.rank2 weiter unten sowie die
     @media-Bloecke) wurde von riseIn's "to{transform:translateY(0)}" komplett
     ueberschrieben -- Platz 1/2/3 landeten dadurch praktisch IMMER auf transform:
     translateY(0), und welche Karte am Ende optisch am hoechsten stand, ergab sich
     nur noch zufaellig aus den natuerlichen (nicht transformierten) Kartenhoehen
     innerhalb des align-items:flex-end-Flex-Containers -- das konnte je nach
     Schriftgroessen-Rundung dazu fuehren, dass Platz 2 (oder sogar Platz 3) optisch
     hoeher stand als Platz 1 (vom Nutzer per Screenshot gemeldet). Nachweis: In
     Playwright zeigte getComputedStyle(.medal-card.rankN).transform ueberall
     "matrix(1,0,0,1,0,0)" (~0px Verschiebung) statt der erwarteten, aus --p-badge-h
     berechneten Werte -- reproduzierbar bereits im veroeffentlichten Stand der 50.
     Runde, also ein bereits laenger bestehender Fehler, keine Regression der 51.
     Runde. Fix: .medal-card bekommt eine EIGENE, nur auf opacity wirkende Einblend-
     Animation (siehe @keyframes medalCardRiseIn direkt darunter) statt der
     gemeinsamen riseIn -- dadurch "besetzt" die Animation die transform-Eigenschaft
     gar nicht mehr, und die rang-spezifischen transform-Regeln (inkl. der
     dynamischen @media-Ueberschreibungen fuer --p-badge-h) greifen wieder normal.
     Die anderen riseIn-Nutzer (Panels, PIN-Box, MVP-Hero-Karte usw.) sind bewusst
     NICHT angefasst -- dort gibt es keine konkurrierende rang-/element-spezifische
     transform-Regel, das kurze Hochgleiten beim Einblenden bleibt dort unveraendert
     erhalten. */
  @keyframes medalCardRiseIn{from{opacity:0;}to{opacity:1;}}
  .medal-card{
    width:210px;padding:24px 16px 20px;text-align:center;position:relative;z-index:1;
    animation:medalCardRiseIn .45s ease both;
  }
  .medal-card.rank1{width:240px;order:2;transform:translateY(-16px);}
  .medal-card.rank2{order:1;width:212px;transform:translateY(-4px);padding-top:20px;padding-bottom:16px;}
  .medal-card.rank3{order:3;width:120px;transform:translateY(0);padding-top:16px;padding-bottom:14px;}
  /* 54. Pruefrunde (Nachtrag 2): Groessen-Angaben von .medal-badge-img auf den neuen
     .medal-badge-clip-Rahmen verlagert (siehe @media-Bloecke weiter unten fuer die
     ausfuehrliche Begruendung -- Schatten-Clipping gegen sichtbares Ueberragen in
     Nachbar-Icons). WICHTIG: diese Basis-Regel hier (ausserhalb jeder @media-Bedingung)
     galt bisher als praktisch "tot", da @media(max-width:640px) und
     @media(min-width:641px) gemeinsam JEDE Bildschirmbreite abdecken -- SIE GILT ABER
     TROTZDEM UNBEDINGT (unabhaengig von jeder Media Query) und mit derselben
     CSS-Spezifitaet wie die frueheren Media-Query-Regeln. Als hier vorher noch feste
     Pixel-Breiten auf .medal-card.rankN .medal-badge-img standen, gewannen genau DIESE
     (nicht die neuen, dynamischen Media-Query-Regeln) bei kleinen Bildschirmen den
     CSS-Kaskaden-Vergleich, sobald die feste Breite (150px) groesser war als der
     inzwischen auf die tatsaechliche Icon-Groesse geschrumpfte Clip-Rahmen -- das Bild
     ragte dadurch sichtbar (und messbar per getBoundingClientRect()) ueber seinen
     eigenen Rahmen hinaus in die Nachbarkarte hinein (vom Nutzer gemeldet, per
     Playwright auf mehreren Handy-Breiten reproduziert). Fix: auch hier auf
     width:100%/height:100% ihres jeweiligen .medal-badge-clip-Rahmens umgestellt --
     dadurch kann diese Basis-Regel nie wieder eine der Media-Query-Groessen ueberstimmen,
     unabhaengig von CSS-Spezifitaet. */
  .medal-badge-clip{display:block;overflow:hidden;margin:0 auto 10px;}
  .medal-card.rank1 .medal-badge-clip{width:170px;height:200px;margin-top:-10px;}
  .medal-card.rank2 .medal-badge-clip{width:197px;height:200px;margin-top:-4px;}
  .medal-card.rank3 .medal-badge-clip{width:205px;height:200px;}
  .medal-badge-img{display:block;width:100%;height:100%;margin:0;object-fit:contain;object-position:center bottom;filter:drop-shadow(0 10px 18px rgba(0,0,0,.5));}
  .medal-name{font-family:'Space Grotesk';font-weight:700;font-size:calc(16px * var(--name-fit-scale,1));margin-bottom:4px;line-height:1.25;hyphens:auto;overflow-wrap:break-word;word-break:normal;min-height:40px;display:flex;align-items:center;justify-content:center;}
  .medal-name-accent{width:34px;height:3px;margin:0 auto 9px;border-radius:2px;background:var(--ink-dim);opacity:.9;transition:background-color .2s,box-shadow .2s;}
  .medal-points{font-family:'Bebas Neue';font-size:36px;color:var(--ink);}
  .medal-card.rank1 .medal-points{color:var(--gold);font-size:46px;text-shadow:0 0 20px rgba(255,207,92,.35);}
  .medal-card.rank2 .medal-points{color:var(--silver-glow);font-size:33px;text-shadow:0 0 16px rgba(212,214,221,.4);}
  .medal-card.rank3 .medal-points{color:var(--copper-glow);font-size:29px;text-shadow:0 0 14px rgba(230,160,110,.4);}
  /* Punkte jetzt auf allen drei Plaetzen in der jeweiligen Rang-Farbe (31. Prüfrunde) —
     dieselben Variablen/Glow-Staerke-Verhaeltnisse wie beim Team-Namen weiter unten, nur
     proportional zur (kleineren) Punkte-Schriftgroesse skaliert. Die bereits vorhandene
     farbige Guideline zwischen Name und Punkten (siehe .medal-name-accent) verbindet
     dadurch jetzt zwei GLEICHFARBIGE Elemente und wirkt so als bewusst gewaehltes,
     zum Gesamtbild passendes Bindeglied -- eine zweite, separate Guideline extra fuer
     die Punkte haette das Kaertchen unnoetig unruhiger gemacht. */
  .medal-points-label{font-size:10px;text-transform:uppercase;letter-spacing:1px;color:var(--ink-dim);margin-top:2px;}

  /* Board list */
  .board-list{border-radius:var(--radius);overflow:hidden;border:1px solid var(--border);box-shadow:var(--shadow-card);}
  .board-row{
    display:grid;grid-template-columns:64px 1fr auto auto;gap:14px;align-items:center;
    padding:14px 20px;background:var(--panel);border-bottom:1px solid var(--border);
    border-left:3px solid transparent;position:relative;
    /* Nachtrag (24.09.2026): nur Farben weich überblenden, NICHT alle
       Eigenschaften -- "transition:.15s" animierte auch das Padding, das
       sich mit --list-scale/--incomplete-scale ändert. Die Größenberechnung
       (fitOverallList() in tv.js) hat dadurch Zwischenwerte gemessen, eine zu
       große Skalierung gewählt und sie ~0,5 s nach dem Folienwechsel sichtbar
       wieder verkleinert (Nutzer-Video: "Skalierung extrem zu sehen"). */
    transition:background-color .15s, border-color .15s, box-shadow .15s;
  }
  /* Feste 64px-Rang-Spalte brach bei zweistelligem Rang ("10") + Pfeil-/NEU-Badge
     zeilenweise um (Text-Umbruch, siehe 30. Prüfrunde) — #slide-overall-list
     ueberschreibt das unten mit einer skalierbaren, breiteren Spalte. */
  .board-row:last-child{border-bottom:none;}
  .board-row:nth-child(even){background:var(--panel-2);}
  .board-row:hover{box-shadow:inset 0 0 0 999px rgba(255,122,41,.05);}
  .board-row.top{border-left-color:var(--gold);box-shadow:inset 0 0 0 999px rgba(255,207,92,.04);}
  .board-row.top:hover{box-shadow:inset 0 0 0 999px rgba(255,207,92,.08);}
  .board-row .rank{font-family:'Bebas Neue';font-size:23px;color:var(--ink-dim);text-align:center;}
  .board-row.top .rank{color:var(--gold);}
  .board-row .name{font-family:'Space Grotesk';font-weight:700;font-size:calc(16px * var(--name-fit-scale,1));}
  .board-row .breakdown{display:flex;gap:8px;font-size:11px;color:var(--ink-dim);font-family:'Inter';}
  .board-row .breakdown b{color:var(--ink);}
  .board-row .points{font-family:'Bebas Neue';font-size:22px;color:var(--gold);min-width:70px;text-align:right;}
  .board-row .attempts{font-size:11px;color:var(--ink-dim);min-width:80px;text-align:right;}

  .empty-note{
    display:flex;flex-direction:column;align-items:center;gap:12px;
    text-align:center;color:var(--ink-dim);padding:70px 20px;font-size:14px;
    border:1px dashed var(--border);border-radius:var(--radius);
  }
  .empty-note .icon{width:36px;height:36px;color:var(--border);}

  /* Nachtrag (21.09.2026): margin-top/padding-top/border-top (die Trennlinie
     unter den Punkten) sitzt auf .tv-footer-row (dem gemeinsamen Container mit
     dem QR-Code, siehe unten) statt direkt hier.
     Nachtrag 2 (21.09.2026, Nutzer-Korrektur): "Info Einblendung soll mittig
     stehen, QR-Code an Ort und Stelle links in der Ecke" -- der QR-Code ist
     jetzt per position:absolute links in der Zeile verankert (siehe
     .tv-qr-widget unten) und dadurch aus dem Flex-Fluss raus. .footer-note
     ist dadurch das einzige verbleibende Flex-Kind von .tv-footer-row und
     füllt automatisch die komplette Zeilenbreite -- text-align:center
     zentriert den Text dadurch echt auf der ganzen Seite, unabhängig von der
     QR-Code-Breite (kein Verschieben durch den danebenstehenden QR-Code
     mehr). Schrift zusätzlich vergrößert (11px -> 15px), da die Zeile jetzt
     nicht mehr mit dem QR-Code um Platz konkurriert.
     Nachtrag 3 (21.09.2026, Nutzer-Wunsch "nur die Info-Schrift vergrößern,
     QR-Code und der Balken selbst sollen nicht größer werden"): NUR die
     Schriftgröße nochmal angehoben (15px -> 19px), .tv-qr-widget (Größe)
     und min-height der Zeile (78px) bewusst UNVERÄNDERT gelassen -- der
     größere Text nutzt einfach den schon vorhandenen freien Raum in der
     durch den QR-Code ohnehin 78px hohen Zeile besser aus, ohne dass die
     Zeile selbst wächst. */
  .footer-note{display:flex;align-items:center;justify-content:center;gap:10px;text-align:center;color:var(--ink-dim);font-size:19px;letter-spacing:.3px;flex:1 1 auto;min-width:0;}
  .footer-note .icon{width:19px;height:19px;color:var(--accent);}
  .tv-footer-row{position:relative;display:flex;align-items:center;justify-content:center;min-height:78px;margin-top:14px;padding-top:14px;border-top:1px solid var(--border);}

  /* Dezenter Notausstieg aus dem Gast-Modus: nur sichtbar, wenn die Kopfzeile
     (und damit der normale Weg zur PIN-Eingabe) ausgeblendet ist. */
  .guest-escape-btn{
    position:fixed;right:12px;bottom:12px;z-index:60;
    width:32px;height:32px;border-radius:50%;
    background:rgba(20,21,26,.6);border:1px solid var(--border);
    color:var(--ink-dim);display:none;align-items:center;justify-content:center;
    opacity:.32;transition:opacity .2s;cursor:pointer;
  }
  .guest-escape-btn:hover{opacity:1;color:var(--accent);border-color:var(--accent);}

  /* Spieler-Rangliste */
  /* 51. Pruefrunde (Vorschau): zurueck auf 3 gleichwertige Spalten -- der Turnier-MVP
     hat jetzt seine eigene Spotlight-Folie (siehe .mvp-spotlight weiter unten) statt
     einer vierten, optisch untergeordneten Spalte hier. */
  /* Score-Skala-Pruefung (24.09.2026): minmax(0,1fr) statt 1fr -- sonst dehnt der
     nicht umbrechende Text (Name · Team) die Spalten, und bei 1280 px/iPad wurde
     die dritte Spalte (Phobia) aus dem Bild geschoben. */
  .player-boards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px;}
  .player-board-col{min-width:0;background:var(--panel-grad);border:1px solid var(--border);border-radius:var(--radius);padding:16px;box-shadow:var(--shadow-card);}
  .player-board-col h3{
    display:flex;align-items:center;gap:8px;font-family:'Space Grotesk';font-weight:700;font-size:13px;
    text-transform:uppercase;letter-spacing:.5px;color:var(--ink);margin:0 0 12px;
  }
  .player-board-col .pdot{width:8px;height:8px;border-radius:50%;flex:none;border:1px solid rgba(255,255,255,.3);}
  .player-row{display:flex;align-items:center;gap:10px;padding:9px 2px;border-bottom:1px solid var(--border);}
  .player-row:last-child{border-bottom:none;}
  .player-row .prank{font-family:'Bebas Neue';font-size:16px;color:var(--ink-dim);width:18px;flex:none;}
  .player-row .pname{flex:1;font-weight:600;font-size:13px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .player-row .pteam{font-weight:400;font-size:11px;color:var(--ink-dim);}
  .player-row .pscore{font-family:'Bebas Neue';font-size:18px;color:var(--gold);flex:none;}
  .player-empty{color:var(--ink-dim);font-size:12px;padding:10px 2px;}
  @media (max-width:900px){ .player-boards{grid-template-columns:minmax(0,1fr);} }
  /* Handy: Team in eigene, kleinere Zeile unter den Spielernamen -- so bleiben
     Name UND Punktzahl immer vollstaendig sichtbar. */
  @media (max-width:640px){
    .player-row .pname{white-space:normal;overflow:visible;line-height:1.25;overflow-wrap:break-word;}
    .player-row .pteam{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px;}
    .player-row .psep{display:none;}
  }

  /* 51. Pruefrunde (Vorschau): eigene MVP-Spotlight-Folie -- ein grosses Hero-Kaertchen
     fuer Platz 1 (Turnier-MVP), darunter eine schlanke Liste fuer Platz 2-5. Urspruenglich
     bewusst NICHT an die dynamische Bisektions-Groessenanpassung der Team-Podium-Abzeichen
     gekoppelt (computePodiumSizing()) -- stattdessen lieferte clamp() eine einfachere,
     nur an die Bildschirm-BREITE (vw) gekoppelte Groessenstaffelung.
     54. Pruefrunde (Nachtrag 5, Nutzer-Feedback: auf TV wird die Folie sichtbar
     abgeschnitten): genau dieser Verzicht auf eine echte Anpassung war die Ursache --
     clamp()/vw reagiert NUR auf die Fensterbreite, nie auf die verfuegbare Bildschirm-
     HOEHE. Auf vielen TV-Bildschirmen (grosse Breite, dadurch clamp() nahe am oberen
     Wert von 290px/44px/... Schriftgroesse) reichte die tatsaechlich verfuegbare Hoehe
     von #tvScroll trotzdem nicht fuer Bild + Info-Block + komplette Platz-2-5-Liste --
     der Ueberschuss lief unbemerkt in den (auf einem TV ohne Maus/Fernbedienung-Scroll
     praktisch unerreichbaren) Scroll-Bereich von #tvScroll und wirkte dadurch fuer den
     Betrachter wie "abgeschnitten". Fix: alle Groessen dieser Folie sind jetzt wie beim
     Podium ueber eine einzige, per JS gemessene Basisgroesse (--mvp-img-w, siehe
     computeMvpSpotlightSizing() in der App-Logik) gekoppelt -- echte Bisektion prueft
     bei jeder Kandidatengroesse live per getBoundingClientRect(), ob die komplette
     Folie sowohl in die Hoehe von #tvScroll passt ALS AUCH nicht horizontal ueberlaeuft,
     genau wie computePodiumSizing() es fuer das Podium bereits tut. Alle frueheren
     vw-Werte sind durch calc(var(--mvp-img-w) * Faktor) ersetzt, mit Faktoren, die dem
     jeweiligen alten Mittelwert entsprechen (Bild-Basis 240px) -- der optische Eindruck
     bei "normalen" Bildschirmgroessen bleibt dadurch praktisch unveraendert, auf zu
     knappen Hoehen (TV, kurze Fensterhoehen) schrumpft jetzt aber garantiert alles
     gemeinsam, statt zu ueberlaufen. */
  .mvp-spotlight{display:flex;flex-direction:column;align-items:center;gap:var(--gap-sm);padding:calc(8px + var(--mvp-img-w,240px) * .07) 0 10px;}
  /* Mobile-Design-Nachtrag (16.09.2026, Nutzerwunsch "MVP-Liste zu klein im
     Vergleich zu den anderen Ranglisten"): der goldene Rahmen/Kartenhintergrund
     um .mvp-hero entfaellt -- Icon + Name/Team/Score stehen jetzt frei auf dem
     Folienhintergrund, genau wie in der vom Nutzer bestaetigten Vorschau. Die
     Liste (Plaetze 2-5, .mvp-list-row) skaliert NICHT mehr mit --mvp-img-w,
     sondern hat feste, an .board-row angelehnte Groessen (s.u.) -- dadurch
     wirkt sie endlich gleichwertig zu den anderen Ranglisten im Tool statt wie
     bisher deutlich kleiner. Die dadurch gewonnene Frei-Flaeche fliesst
     automatisch zurueck in eine groessere Icon-Darstellung: computeMvpSpotlightSizing()
     (App-Logik) misst weiterhin per echter Bisektion, wie gross --mvp-img-w
     (Icon + Name/Team/Score) werden darf, damit die GESAMTE Folie (Icon-Block
     + jetzt groessere Liste) ohne Scrollen in #tvScroll passt -- exakt dasselbe
     Prinzip wie computePodiumSizing() beim Podium. */
  .mvp-hero{width:100%;max-width:520px;text-align:center;animation:riseIn .45s ease both;}
  /* Glut-Nachtrag (24.09.2026): der goldene drop-shadow direkt am Bild verstaerkte
     den kaum sichtbaren Schleier im mvp_badge.webp zu einem leuchtenden KASTEN, der
     an der Oberkante des Folienbereichs hart abriss. Jetzt ohne Filter am Bild;
     stattdessen eine weiche, runde Glut hinter dem Emblem (.mvp-hero::before),
     die komplett auslaeuft, bevor sie irgendeinen Rand erreicht. */
  .mvp-hero-badge-img{
    display:block;width:var(--mvp-img-w,240px);max-width:100%;height:auto;margin:0 auto calc(var(--mvp-img-w,240px) * .013);
    object-fit:contain;filter:none;
  }
  .mvp-hero{position:relative;isolation:isolate;}
  .mvp-hero::before{
    content:'';position:absolute;left:50%;top:calc(var(--mvp-img-w,240px) * .5);
    width:calc(var(--mvp-img-w,240px) * 1.7);height:calc(var(--mvp-img-w,240px) * 1.15);
    transform:translate(-50%,-50%);pointer-events:none;z-index:-1;
    background:radial-gradient(closest-side,rgba(255,196,96,.55),rgba(255,130,50,.28) 36%,rgba(225,70,40,.10) 66%,transparent 100%);
  }
  .mvp-hero-info{margin-top:2px;}
  .mvp-hero-name{font-family:'Space Grotesk';font-weight:700;font-size:calc(var(--mvp-img-w,240px) * .127);color:var(--gold);text-shadow:0 0 14px rgba(255,207,92,.4);line-height:1.2;}
  .mvp-hero-team{color:var(--ink-dim);font-size:calc(var(--mvp-img-w,240px) * .087);margin-top:2px;}
  .mvp-hero-score{font-family:'Bebas Neue';font-size:calc(var(--mvp-img-w,240px) * .213);color:var(--ink);margin-top:2px;letter-spacing:.5px;}
  .mvp-hero-list{width:100%;max-width:640px;border-radius:var(--radius);overflow:hidden;border:1px solid var(--border);box-shadow:var(--shadow-card);background:var(--panel);padding:6px;}
  /* Feste Groessen statt calc(var(--mvp-img-w) * Faktor) -- bewusste Angleichung
     an .board-row (Team-/Spiel-Ranglisten), s. Kommentar oben. Jede Zeile ist
     jetzt eine eigene abgerundete Karte (statt Trennlinien in einem durchgehenden
     Panel) mit einem farbigen Hintergrund-"Balken", dessen Breite den Score im
     Verhaeltnis zum Anfuehrer zeigt und dessen Farbton den Rang kennzeichnet:
     Platz 2 = Silber, Platz 3 = Kupfer (wie das Podium), Platz 4+5 = der
     "Geist"-Blauton --spectral (app-extra.css), den das Tool bereits fuer
     "noch nicht gewertet" nutzt -- Nutzerwunsch, weil ein zunaechst getestetes
     Orange zu nah an Kupfer (Platz 3) lag. Farbklassen werden in tv.js ueber
     die Anzeige-POSITION vergeben, nicht ueber r.rank (kann bei Gleichstand
     Luecken haben). */
  .mvp-list-row{position:relative;display:flex;align-items:center;gap:var(--gap-sm);padding:10px 12px;margin-bottom:6px;border-radius:10px;background:var(--panel-2);overflow:hidden;}
  .mvp-list-row:last-child{margin-bottom:0;}
  .mvp-list-bar{position:absolute;left:0;top:0;bottom:0;z-index:0;background:linear-gradient(90deg, rgba(141,145,154,.22), rgba(141,145,154,.02));}
  .mvp-list-row.mvp-tier-2 .mvp-list-bar{background:linear-gradient(90deg, rgba(212,214,221,.28), rgba(212,214,221,.03));}
  .mvp-list-row.mvp-tier-3 .mvp-list-bar{background:linear-gradient(90deg, rgba(230,160,110,.28), rgba(230,160,110,.03));}
  .mvp-list-row.mvp-tier-45 .mvp-list-bar{background:linear-gradient(90deg, rgba(157,184,255,.22), rgba(157,184,255,.03));}
  .mvp-list-rank{position:relative;z-index:1;font-family:'Bebas Neue';font-size:20px;color:var(--ink-dim);width:24px;flex:none;}
  .mvp-list-row.mvp-tier-2 .mvp-list-rank{color:var(--silver-glow);}
  .mvp-list-row.mvp-tier-3 .mvp-list-rank{color:var(--copper-glow);}
  .mvp-list-row.mvp-tier-45 .mvp-list-rank{color:var(--spectral,#9db8ff);}
  .mvp-list-name{position:relative;z-index:1;flex:1;font-family:'Space Grotesk';font-weight:700;font-size:15.5px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .mvp-list-team{display:block;font-weight:400;font-size:11.5px;color:var(--ink-dim);margin-top:1px;}
  .mvp-list-score{position:relative;z-index:1;font-family:'Bebas Neue';font-size:20px;color:var(--gold);flex:none;}
  .mvp-list-row.mvp-tier-2 .mvp-list-score{color:var(--silver-glow);}
  .mvp-list-row.mvp-tier-3 .mvp-list-score{color:var(--copper-glow);}
  .mvp-list-row.mvp-tier-45 .mvp-list-score{color:var(--spectral,#9db8ff);}

  /* 51. Pruefrunde (Vorschau): Platz 4 (Finale-Grenze, Top 4 statt Top 3 ziehen laut
     PDF ins Finale ein) wird in der Platz-4-10-Liste durch eine eigene, golden
     abgesetzte Box von den restlichen, nicht mehr im Finale vertretenen Plaetzen
     getrennt -- als zwei separate .board-list-Bloecke statt EINER Liste mit
     Trenner-Zeile, damit die bestehende nth-child-Zebra-Streifung und das Subgrid
     jeder Liste unangetastet bleiben (siehe renderOverallList()). */
  /* Mobile-Design-Audit, Schritt 4a: beide margin-bottom:14px waren bereits dieser Wert --
     reine Token-Umbenennung auf --gap-sm, kein Wertwechsel. */
  .board-list.finale-cut-list{margin-bottom:var(--gap-sm);position:relative;overflow:hidden;}
  .board-list.finale-cut-list .board-row{border-left-color:var(--gold);box-shadow:inset 0 0 0 999px rgba(255,207,92,.05);}
  .finale-divider{
    display:flex;align-items:center;gap:12px;margin:0 0 var(--gap-sm);
    color:var(--ink-dim);font-size:10.5px;text-transform:uppercase;letter-spacing:.6px;
  }
  .finale-divider::before,.finale-divider::after{content:'';flex:1;height:1px;background:var(--border);}
  /* 53. Pruefrunde: "Finals Spot"-Eckband auf Platz 4 (per Design-Canvas-Vergleich
     vom Nutzer ausgewaehlt, Variante "Ribbon"/E, Korrektur "Fix 2") -- Platz 4 bleibt
     dabei textlich komplett unveraendert (immer noch schlicht "4"), das Band ist ein
     rein zusaetzliches, dekoratives Element. Zwei vom Nutzer gemeldete Probleme einer
     ersten Fassung wurden dabei bereits vorab gefixt: (1) die Punktzahl ueberlappte
     mit dem Band -- daher bekommen ALLE Zeilen dieser Liste (Platz 4 UND 5-10, siehe
     #slide-overall-list .board-row weiter unten) denselben zusaetzlichen rechten
     Abstand, damit die Punktzahl-Spalte ueber die komplette Liste exakt ausgerichtet
     bleibt, nicht nur in Zeile 4. (2) Das Band selbst wurde ausserdem flacher gedreht
     (37deg -> 20deg) und verkleinert -- bei der ersten, staerker gedrehten/breiteren
     Fassung ragten die Band-Ecken ueber den abgerundeten Kartenrahmen (overflow:hidden
     oben auf .board-list.finale-cut-list) hinaus und wurden dort mit angeschnitten,
     was den Text "FINALS SPOT" unleserlich machte (per Playwright-Screenshot
     nachgewiesen: "FI" am Anfang und "T" am Ende fehlten). Mit den hier verwendeten
     Werten (Breite 90px, 20deg, top 17px, right 6px, jeweils mit --list-scale
     skaliert wie alles andere auf dieser Folie) bleibt das komplette rotierte Band
     innerhalb der Karte -- rechnerisch mit Playwright-Screenshot bei mehreren
     Team-Anzahlen (unterschiedliches --list-scale) verifiziert. */
  .finale-ribbon{
    position:absolute;top:calc(17px * var(--list-scale,1));right:calc(6px * var(--list-scale,1));
    width:calc(90px * var(--list-scale,1));padding:calc(3px * var(--list-scale,1)) 0;
    background:linear-gradient(135deg,var(--gold),#ffe6a8);color:#241a04;
    font-family:'Inter';font-weight:800;font-size:calc(9px * var(--list-scale,1));
    letter-spacing:.5px;text-align:center;transform:rotate(20deg);
    box-shadow:0 4px 10px rgba(0,0,0,.45);pointer-events:none;z-index:2;white-space:nowrap;
  }

  /* ---------- Responsive: schmale Bildschirme (Handy) ---------- */
  @media (max-width:640px){
    .tv-stage{padding:22px 16px 16px;}
    .tv-title{font-size:24px;}
    /* Icon-Größe seit der 28. Prüfrunde DYNAMISCH per JS (computePodiumSizing(),
       siehe App-Logik), genau wie ab 641px seit der 27. Runde — die vorher hier fest
       verdrahteten Prozent-/Pixel-Werte (31%/37%/... Kartenbreite, 34/52/38/32px
       Icon-Breite) ließen auf breiteren Handys viel ungenutzten Leerraum zwischen den
       Icons stehen (per Screenshot vom Nutzer gemeldet). --p-badge-h/--p-text-scale
       funktionieren identisch zum Desktop-Block, nur mit einer kleineren Basis (siehe
       Fallback-Werte unten sowie die baseline-Konstante in computePodiumSizing()) —
       die Kartenbreite entspricht wie auf dem Desktop immer exakt der Icon-Breite
       (kein Rahmen mehr, der zusätzlichen Innenabstand bräuchte). */
    .podium{flex-direction:row;flex-wrap:nowrap;align-items:flex-end;justify-content:center;gap:calc(var(--p-text-scale,1) * 10px);margin:0 0 8px;}
    /* 54. Pruefrunde: statt gleicher Icon-Hoehe fuer alle drei Plaetze (frueher jeweils
       exakt var(--p-badge-h)) bekommt jeder Rang jetzt einen EIGENEN Hoehen-Faktor
       (1.12 / 0.98 / 0.90) -- dadurch ist Platz 1 sichtbar groesser als Platz 2, dieser
       wiederum groesser als Platz 3, ganz ohne zusaetzliche Zahlen (Nutzerwunsch: die
       Icons allein sollen die Platzierung erkennbar machen). Die Breiten-Faktoren
       (.84/.61/.67) sind zusaetzlich exakt auf das SEITENVERHAELTNIS der jeweiligen
       Abzeichen-Grafik abgestimmt (344x461 / 248x400 / 224x301 Px) -- vorher war die
       Box bei Platz 2/3 schmaler als das Bild selbst gebraucht hätte, wodurch
       object-fit:contain dort zusaetzlichen, ungenutzten Leerraum liess und die Icons
       kleiner wirkten als ihre Box vermuten liess. Mit passendem Seitenverhaeltnis
       fuellt jedes Abzeichen seine Box jetzt vollstaendig aus -- ein spuerbarer
       Groessengewinn, ohne dass die Icons sich dadurch naeherkommen (die Kartenbreite,
       und damit der Abstand zueinander, wird weiterhin exakt von computePodiumSizing()
       ueber die Bisektion bestimmt, siehe App-Logik). */
    /* 54. Pruefrunde: die bisherige zusaetzliche transform:translateY()-Anhebung von
       Platz 1 entfaellt hier -- der jetzt eigene, groessere Hoehen-Faktor des Platz-1-
       Abzeichens (1.12x, siehe .medal-badge-img weiter unten) macht die Karte bereits
       von sich aus hoeher, wodurch sie bei align-items:flex-end automatisch weiter nach
       oben herausragt, ganz ohne zusaetzlichen Transform-Versatz. Die alte Kombination
       aus BEIDEM (grosser Hoehen-Faktor UND zusaetzlicher Lift) hat rechnerisch zu weit
       nach oben gereicht und waere auf normalen Bildschirmhoehen mit der
       Statistik-Chip-Leiste kollidiert (per Playwright-Bisektionspruefung
       nachgewiesen: computePodiumSizing() musste dadurch auf praktisch jeder
       getesteten Bildschirmgroesse bis zum Sicherheitsboden herunterregeln, statt den
       verfuegbaren Platz zu nutzen). Ohne den Transform bleibt genug vertikaler
       Spielraum, damit die Bisektion wieder wie vorgesehen mit der Bildschirmgroesse
       mitwaechst. */
    /* 54. Pruefrunde (Nachtrag-Fix): transform:none hier explizit ergaenzt --
       ohne diese Zeile "gewinnt" fuer die Eigenschaft transform weiterhin die
       nicht-Media-Basisregel .medal-card.rank1/.rank2 (transform:translateY(-16px)
       bzw. -4px, siehe oben), da diese @media-Regel dieselbe Selektor-Spezifitaet
       hat und transform bisher gar nicht selbst setzte -- eine Media-Regel
       ueberschreibt nur die Eigenschaften, die sie auch tatsaechlich deklariert.
       Die alte, feste Anhebung blieb dadurch unbemerkt aktiv und riss auf kleinen
       Bildschirmen (wenig Hoehen-Budget) die Bisektion bis zum Sicherheitsboden
       herunter (per Playwright nachgewiesen: 390x844 kollabierte auf --p-badge-h=40px
       statt wie vor dieser Runde auf ca. 172px). */
    .medal-card.rank1{width:calc(var(--p-badge-h,69px) * .95);order:2;padding:0;transform:none;}
    .medal-card.rank2{width:calc(var(--p-badge-h,69px) * .97);order:1;padding:0;transform:none;}
    .medal-card.rank3{width:calc(var(--p-badge-h,69px) * .92);order:3;padding:0;}
    /* 54. Pruefrunde (Nachtrag 2, Nutzer-Feedback: "schwarze Flaeche von Icon Platz 3
       ueberdeckt Platz 1"): filter:drop-shadow() auf .medal-badge-img (siehe Basis-Regel
       weiter oben) malt seinen weichen Schatten AUSSERHALB der eigentlichen Element-Box
       -- das ist bei CSS-Filtern so vorgesehen und wird von der reinen
       Bounding-Box-Kollisionspruefung in computePodiumSizing() bzw. im
       Playwright-Verifikationstest NICHT erfasst (beide vergleichen nur die Icon-eigene
       Layout-Box, nicht den zusaetzlichen, ueber die Box hinausragenden
       Schatten-Pixelbereich). Da die Schattenunschaerfe (18px Blur-Radius) FEST in Pixeln
       ist, waehrend der Abstand zwischen den Karten mit der Icon-Groesse mitschrumpft
       (siehe --p-text-scale-abhaengiges gap weiter oben), kann der Schatten bei kleineren
       Bildschirmen/Icon-Groessen den fest bleibenden Blur-Radius ueberragen und optisch in
       das benachbarte Icon hineinlaufen. Da Platz 3 (order:3) im Flex-Layout NACH Platz 1
       (order:2) gemalt wird (Flexbox malt in "order"-Reihenfolge, nicht DOM-Reihenfolge),
       landet sein Schatten dabei sichtbar VOR/UEBER Platz 1 -- exakt das gemeldete Bild.
       Fix: jedes Icon bekommt jetzt einen eigenen, exakt gleich grossen Clip-Rahmen
       (.medal-badge-clip, overflow:hidden) -- die Groessen-Regeln wandern von
       .medal-badge-img hierher, das <img> selbst wird zu width:100%/height:100% seines
       Rahmens (siehe Regel weiter unten). Der Schatten wird dadurch IMMER exakt an der
       eigenen Icon-Kante abgeschnitten, unabhaengig von Bildschirmgroesse oder
       Kartenabstand -- ein geometrischer Ausschluss statt einer nur meistens
       ausreichenden Abstands-Heuristik. */
    /* 54. Pruefrunde (Nachtrag 3): neue, vom Nutzer gelieferte Abzeichen-Grafiken ohne
       dunkle Medaillon-Scheibe (echter Alpha-Freisteller statt der bisherigen, fast
       vollflaechig gefuellten Kreisscheibe) -- Breiten-Faktoren daher neu auf das jetzt
       nahezu quadratische Seitenverhaeltnis der drei neuen Bilder abgestimmt (442x520 /
       513x520 / 520x508px, siehe App-Logik-Kommentar bei BADGE_IMG). Hoehen-Faktoren
       (1.12/.98/.90) bleiben unveraendert -- sie steuern weiterhin die gewuenschte
       Groessen-Hierarchie Platz1 > Platz2 > Platz3. */
    .medal-card .medal-badge-clip{display:block;overflow:hidden;width:calc(var(--p-badge-h,69px) * .97);height:calc(var(--p-badge-h,69px) * .98);margin:0 auto calc(var(--p-text-scale,1) * 5px);}
    /* Mobile-Design-Nachtrag (16.09.2026, Nutzerwunsch "Idee 2"): Platz 1 noch
       deutlicher dominant (1.12 -> 1.16), Platz 2 und 3 jetzt exakt gleich hoch
       (vorher .98 / .90 -- ungleich) und beide etwas kleiner als zuvor Platz 2,
       fuer ein klareres Sieger-Podest-Bild. */
    .medal-card.rank1 .medal-badge-clip{width:calc(var(--p-badge-h,69px) * .95);height:calc(var(--p-badge-h,69px) * 1.16);margin:0 auto calc(var(--p-text-scale,1) * 6px);}
    .medal-card.rank2 .medal-badge-clip{width:calc(var(--p-badge-h,69px) * .97);height:calc(var(--p-badge-h,69px) * .92);margin:0 auto calc(var(--p-text-scale,1) * 5px);}
    .medal-card.rank3 .medal-badge-clip{width:calc(var(--p-badge-h,69px) * .92);height:calc(var(--p-badge-h,69px) * .92);margin:0 auto calc(var(--p-text-scale,1) * 4px);}
    .medal-card .medal-badge-img{width:100%;height:100%;margin:0;}
    .medal-card .medal-name{
      font-size:calc(var(--p-text-scale,1) * 10.5px * var(--name-fit-scale,1));
      min-height:calc(var(--p-text-scale,1) * 24.15px);margin-bottom:calc(var(--p-text-scale,1) * 2px);line-height:1.15;
      display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;
    }
    .medal-card.rank1 .medal-name{font-size:calc(var(--p-text-scale,1) * 12px * var(--name-fit-scale,1));min-height:calc(var(--p-text-scale,1) * 27.6px);}
    .medal-card .medal-name-accent{
      width:calc(var(--p-text-scale,1) * 20px);
      height:calc(var(--p-text-scale,1) * 2px);
      margin:0 auto calc(var(--p-text-scale,1) * 5px);
    }
    .medal-card.rank1 .medal-name-accent{width:calc(var(--p-text-scale,1) * 24px);}
    .medal-card .medal-points{font-size:calc(var(--p-text-scale,1) * 18px);}
    .medal-card.rank1 .medal-points{font-size:calc(var(--p-text-scale,1) * 25px);}
    .medal-card.rank2 .medal-points{font-size:calc(var(--p-text-scale,1) * 16px);}
    .medal-card.rank3 .medal-points{font-size:calc(var(--p-text-scale,1) * 14px);}
    /* Mobile-Design-Nachtrag (16.09.2026, Nutzerwunsch "Idee 2"): das NEU/Auf-/Ab-
       Abzeichen stand bisher zu eng neben "Survival Score" (nur margin-left:5px
       von der .rank-arrow-Basisregel, nicht mit --p-text-scale mitskaliert). Jetzt
       eigene Zeile darunter statt daneben -- wirkt luftiger als ein reiner
       Abstandswert und bleibt auch bei kleinen Icon-Groessen gut lesbar getrennt. */
    .medal-card .medal-points-label{font-size:calc(var(--p-text-scale,1) * 7.5px);margin-top:1px;display:flex;flex-direction:column;align-items:center;gap:calc(var(--p-text-scale,1) * 3px);}
    .medal-card .medal-points-label .rank-arrow{font-size:calc(var(--p-text-scale,1) * 8px);margin-left:0;}
    /* BUGFIX (16.09.2026): .medal-gap ("-XXX zu Platz 1"-Abstandsanzeige,
       siehe app-extra.css) war auf Mobile bisher NICHT an --p-text-scale
       gekoppelt -- sie behielt die feste TV-Groesse (font-size:13px,
       padding:2px 10px). Bei echten Turnierdaten mit groesserem Punkte-
       abstand sprengte dieser fixe Text die schmale Mobile-Karte, lief auf
       mehrere Zeilen um und wirkte dadurch überdimensioniert/verrutscht.
       Fix: wie .medal-points-label mit --p-text-scale mitskalieren und per
       max-width auf die Kartenbreite begrenzen. */
    .medal-card .medal-gap{
      font-size:calc(var(--p-text-scale,1) * 8.5px);
      padding:calc(var(--p-text-scale,1) * 2px) calc(var(--p-text-scale,1) * 6px);
      margin-top:calc(var(--p-text-scale,1) * 3px);
      border-radius:6px;
      max-width:100%;
      box-sizing:border-box;
      line-height:1.25;
    }
    .stats-strip{
      display:grid;grid-template-columns:repeat(3,1fr);gap:0;
      border:1px solid var(--border);border-radius:14px;overflow:hidden;
      background:var(--panel-grad);box-shadow:0 8px 18px -12px rgba(0,0,0,.6);
    }
    .stat-chip{
      flex-direction:column;justify-content:center;gap:5px;
      border:none;border-radius:0;background:transparent;box-shadow:none;
      padding:6px 4px;border-right:1px solid var(--border);
    }
    .stat-chip:last-child{border-right:none;}
    .stat-chip-label{font-size:10px;}
    .stat-chip-count{font-size:11px;}
    .stat-chip b{font-size:19px;}
    .board-row{
      display:flex;flex-wrap:wrap;align-items:center;
      gap:4px 10px;padding:12px 14px;border-left-width:3px;
    }
    /* Score-Skala-Pruefung (24.09.2026): feste Breite fuer Rang + Abzeichen --
       vorher rueckten Name und Punkte ab Platz 10 ("10 NEU") sichtbar nach rechts. */
    .board-row .rank{flex:0 0 68px;width:68px;font-size:18px;min-width:0;text-align:left;white-space:nowrap;}
    /* Mobile-Design-Audit (14.09.2026), Schritt 2: flex-basis von "auto" auf "0"
       geaendert -- exakt derselbe Fix, der fuer #slide-overall-list .board-row .name
       weiter unten in dieser Datei bereits existiert (dort ausfuehrlich begruendet).
       Mit "auto" entscheidet der Browser anhand der UNGEBROCHENEN Textbreite, ob der
       Name noch auf die erste Zeile passt -- bei einem laengeren Namen (z. B. "Blutmond
       Crew") "fiel" das gesamte Namens-Element dadurch in eine eigene Zeile, was
       Runden-Anzahl und Punktzahl mit nach unten riss, waehrend kuerzere Namen in
       derselben Liste einzeilig blieben (per Screenshot auf allen 3 Spiel-Boards +
       Laeuft-noch-Liste reproduziert). "flex:1 1 0" startet die Breitenberechnung
       stattdessen immer bei 0 -- der Name bleibt zuverlaessig neben Rang/Badge auf der
       ersten Zeile und bricht bei Bedarf nur INTERN um, Rang/Runden/Punktzahl ruecken
       dadurch nie mehr unterschiedlich weit.
       Mobile-Design-Audit-Nachtrag (15.09.2026): word-break:break-word ersetzt durch
       dieselbe Kombination, die .medal-name auf dem Podium schon bewaehrt einsetzt
       (word-break:normal + hyphens:auto, overflow-wrap:break-word bleibt als Netz).
       Grund: die Auto-Schrumpf-Logik (fitBoardRowNames()) schrumpft Namen nur bis zu
       einer festen Untergrenze (NAME_FIT_MIN=0.75) -- bei sehr langen Namen reicht das
       manchmal nicht ganz zum vollstaendigen Einzeiler. Mit word-break:break-word riss
       das dann genau ein ueberstehendes Zeichen allein in die zweite Zeile (z. B.
       "Kellerkinde"/"r", vom Nutzer entdeckt). word-break:normal+hyphens:auto laesst
       den Browser stattdessen an einer sinnvollen Trennstelle umbrechen (ggf. mit
       Bindestrich), keine einzelnen Buchstaben mehr allein auf der zweiten Zeile. */
    .board-row .name{flex:1 1 0;min-width:0;font-size:calc(14px * var(--name-fit-scale,1));overflow-wrap:break-word;word-break:normal;hyphens:auto;}
    /* Score-Skala-Pruefung (24.09.2026): "1 Runde(n)" auf dem Handy ausgeblendet --
       neben 6-stelligen Punktzahlen blieb fuer den Teamnamen sonst kaum Platz. */
    .board-row .attempts{display:none;}
    .board-row .points{flex:0 0 auto;font-size:17px;min-width:0;order:4;margin-left:auto;}
    .board-row .breakdown{flex:1 1 100%;order:5;font-size:10px;padding-left:38px;gap:10px;}
  }


  /* Team-Name Akzentfarben je Rang (29. Prüfrunde, Variante C_full) */
  .medal-card.rank1 .medal-name-accent{background:var(--gold);box-shadow:0 0 10px rgba(255,207,92,.5);}
  .medal-card.rank2 .medal-name-accent{background:var(--silver-glow);box-shadow:0 0 10px rgba(212,214,221,.55);}
  .medal-card.rank3 .medal-name-accent{background:var(--copper-glow);box-shadow:0 0 10px rgba(230,160,110,.5);}
  .medal-card.rank1 .medal-name{color:var(--gold);text-shadow:0 0 14px rgba(255,207,92,.4);}
  .medal-card.rank2 .medal-name{color:var(--silver-glow);text-shadow:0 0 14px rgba(212,214,221,.45);}
  .medal-card.rank3 .medal-name{color:var(--copper-glow);text-shadow:0 0 14px rgba(230,160,110,.4);}
/* =========================================================
   ERWEITERUNGSRUNDE — Gast-Rangliste modernisiert:
   3-spaltige Kopfzeile, größeres Podium, Punkte-Breakdown-Chips,
   Nebel-Seitenwechsel, Neuer-Highscore-Feier (Explosion + Charakter + Fanfare)
   ========================================================= */
.tv-head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;margin-bottom:22px;flex-wrap:wrap;}
.tv-head-meta{display:flex;flex-direction:column;gap:6px;min-width:0;flex:1;}
.tv-head-meta.right{align-items:flex-end;text-align:right;}
.tv-head-center{flex:2;min-width:0;text-align:center;}
.tv-head-date{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:600;color:var(--ink-dim);white-space:nowrap;}
/* Mobile-Design-Audit, Schritt 4a: 16px war bereits der Wert -- reine Token-Umbenennung. */
.tv-head-date .icon{width:var(--icon-md);height:var(--icon-md);color:var(--accent);}
.tv-head-date b{color:var(--ink);font-weight:700;}
.tv-head-count{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:600;color:var(--ink-dim);white-space:nowrap;}
.tv-head-count .icon{width:var(--icon-md);height:var(--icon-md);color:var(--accent);}
.tv-head-count b{color:var(--ink);font-weight:700;}
/* 51. Pruefrunde (Vorschau): schlanker Turnierphasen-Indikator unter dem Datum --
   zeigt je nach echtem Kalenderdatum "Qualifikation läuft" / "Finale-Woche" /
   "Finale heute!" an, siehe computeTournamentPhase() in der App-Logik. Bleibt
   ausserhalb des definierten Zeitraums (vor/nach dem Turnier) per [hidden]
   versteckt statt einen falschen Zustand zu zeigen. */
.tv-phase-badge[hidden]{display:none;}
.tv-phase-badge{
  display:inline-flex;align-items:center;gap:6px;margin-top:2px;
  /* Mobile-Design-Audit, Schritt 4a: border-radius war bereits hart 999px -- reine Token-Umbenennung. */
  padding:3px 11px;border-radius:var(--radius-pill);font-size:10.5px;font-weight:800;
  letter-spacing:.4px;text-transform:uppercase;white-space:nowrap;
  border:1px solid var(--border);color:var(--ink-dim);background:var(--panel-2);
}
.tv-phase-badge.phase-quali{border-color:rgba(59,130,246,.4);color:#93c5fd;background:rgba(59,130,246,.1);}
.tv-phase-badge.phase-finale-week{border-color:rgba(255,207,92,.45);color:var(--gold);background:rgba(255,207,92,.1);}
.tv-phase-badge.phase-finale{border-color:rgba(224,82,74,.55);color:#fca5a5;background:rgba(224,82,74,.14);animation:pulse 1.6s ease-in-out infinite;}
@media (max-width:760px){
  .tv-head{flex-direction:column;align-items:center;text-align:center;}
  .tv-head-meta, .tv-head-meta.right{align-items:center;}
}

/* Podium — Icon-Größe seit der 27. Prüfrunde (Desktop/TV) und seit der 28. Prüfrunde
   auch auf dem Handy (siehe @media max-width:640px weiter oben) DYNAMISCH per JS
   berechnet. computePodiumSizing() (siehe App-Logik) ermittelt bei jedem Render/Resize
   per Bisektion die größtmögliche Abzeichen-Höhe, die weder horizontal überlappt noch
   die Folie zum Scrollen bringt, und setzt sie als --p-badge-h — mit je Breakpoint
   eigener Basisgröße (Desktop 200px, Handy ca. 69px, siehe baseline-Konstante in
   computePodiumSizing()). Die Kartenbreite entspricht IMMER exakt der
   Abzeichen-Breite (kein Rahmen mehr, der zusätzlichen Innenabstand bräuchte) — das
   Icon ist dadurch stets das größte Element im Kärtchen. Name/Punkte/Balken wachsen
   bewusst langsamer mit (--p-text-scale, in JS an --p-badge-h gekoppelt, Faktor 0.4)
   — sie bleiben klar sekundär zum Abzeichen, wie vom Nutzer gewünscht ("Icon soll
   größer sein als die weiteren Daten"). Die var()-Fallbacks (200px / 1 hier, 69px / 1
   im Handy-Block) greifen nur, falls JS aus irgendeinem Grund noch nicht gelaufen
   ist, und entsprechen der alten, alten Fest-Größe. */
@media (min-width:641px){
  .medal-card{
    width:calc(var(--p-badge-h,200px) * .61); padding:0;
  }
  /* 52. Pruefrunde (Bugfix-Folgekorrektur): die Anhebung (transform) skaliert jetzt
     mit --p-text-scale statt direkt mit --p-badge-h. Hintergrund: --p-text-scale
     waechst gedaempft (Faktor 0.4 der relativen Abweichung von der Basisgroesse,
     siehe computePodiumSizing()) -- genau wie der eigentliche GRUND fuer die
     Anhebung (Platz 1/2 brauchen wegen ihrer GROESSEREN Punkte-Schrift natuerlich
     mehr vertikalen Platz als Platz 3, was ihre Karte im flex-Layout tiefer haengen
     laesst). Die vorherige, direkt an --p-badge-h gekoppelte Version wuchs bei sehr
     grossen Bildschirmen (TV-4K/8K) VIEL staerker als dieser natuerliche
     Groessenunterschied und schoss dadurch weit ueber das noetige Mass hinaus --
     das liess die angehobene Karte auf grossen Bildschirmen unnoetig weit in die
     Punkte-Chips-Leiste darueber hineinragen (siehe computePodiumSizing()-Fix
     weiter oben, der das jetzt zusaetzlich absichert). Mit --p-text-scale bleibt die
     Anhebung deutlich langsamer als vorher. Das alleine reichte aber immer noch nicht:
     selbst gedaempft waechst --p-text-scale unbegrenzt weiter, und bei sehr grossen
     Bildschirmen (TV-4K/8K) fraess sich die Anhebung dadurch weiterhin zunehmend in
     die Punkte-Chips-Leiste darueber (per Playwright nachgewiesen). Deshalb zusaetzlich
     max(...) als HARTE Obergrenze: der Betrag der Anhebung waechst proportional NUR bis
     zu diesem Deckel und bleibt darueber hinaus konstant -- bei kleinen/mittleren
     Bildschirmen (dort, wo die ungleiche Punkte-Schriftgroesse den Karten tatsaechlich
     unterschiedlich viel Platz abverlangt und die Reihenfolge kippen kann) greift die
     volle, proportionale Anhebung wie gewuenscht, bei sehr grossen Bildschirmen bleibt
     sie bei einem kleinen, konstanten Pixelbetrag stehen, der dort nicht mehr ins
     Gewicht faellt. 200px = dieselbe Basisgroesse, mit der auch --p-text-scale selbst
     berechnet wird (siehe baseline-Konstante in computePodiumSizing()). Die
     computePodiumSizing()-Sicherheitspruefung weiter oben bleibt zusaetzlich als
     letztes Netz bestehen, falls die Werte hier in einer spaeteren Runde nochmal
     angepasst werden. */
  .medal-card.rank1{
    width:calc(var(--p-badge-h,200px) * .95); order:2; transform:none;
  }
  .medal-card.rank2{
    order:1; width:calc(var(--p-badge-h,200px) * .97); transform:none;
  }
  .medal-card.rank3{
    order:3; width:calc(var(--p-badge-h,200px) * .92);
  }
  /* 54. Pruefrunde (Nachtrag-Fix): transform:none oben bei rank1/rank2 explizit
     ergaenzt -- siehe ausfuehrliche Begruendung im @media(max-width:640px)-Block
     weiter oben (identischer CSS-Spezifitaets-Mechanismus, hier zusaetzlich fuer
     Desktop/TV betroffen: die alte fixe -16px/-4px-Anhebung aus der
     Nicht-Media-Basisregel blieb sonst unbemerkt aktiv). */
  /* 54. Pruefrunde: die bisherige zusaetzliche transform:translateY()-Anhebung von
     Platz 1/2 (siehe vorherige Rundenkommentare) entfaellt hier komplett -- der jetzt
     eigene, groessere Hoehen-Faktor jedes Abzeichens (Platz 1: 1.12x, Platz 2: 0.98x,
     Platz 3: 0.90x, siehe .medal-badge-img weiter unten) macht die Karten von sich aus
     unterschiedlich hoch, wodurch Platz 1 bei align-items:flex-end automatisch am
     weitesten nach oben herausragt -- ganz ohne zusaetzlichen Transform-Versatz. Die
     alte Kombination aus BEIDEM (groesserer Hoehen-Faktor UND zusaetzlicher Lift)
     reichte rechnerisch zu weit nach oben und kollidierte auf normalen
     Bildschirmhoehen mit der Statistik-Chip-Leiste darueber (per
     Playwright-Bisektionspruefung nachgewiesen: computePodiumSizing() musste dadurch
     auf praktisch jeder getesteten Bildschirmgroesse -- Handy wie 4K-TV gleichermassen
     -- bis zum Sicherheitsboden herunterregeln, statt den tatsaechlich verfuegbaren
     Platz zu nutzen; --p-badge-h blieb dadurch ueberall bei ca. 132px stecken, obwohl
     z.B. bei 3840x2160 deutlich mehr Platz vorhanden gewesen waere). Ohne den
     zusaetzlichen Transform bleibt genug vertikaler Spielraum, damit die Bisektion
     wieder wie vorgesehen mit der Bildschirmgroesse mitwaechst. */
  /* Die Icon-Höhe ist NICHT mehr für alle drei Ränge gleich --
     jeder Rang bekommt einen eigenen Höhen-Faktor auf --p-badge-h (Platz 1: 1.12,
     Platz 2: 0.98, Platz 3: 0.90), damit die Icon-GRÖSSE selbst die Platzierung klar
     erkennbar macht (Nutzerwunsch: "Icons sollen die wichtigste visuelle Information
     sein", ausdrücklich OHNE zusätzliche Platzierungszahlen). Die Breiten-Faktoren
     (.84/.61/.67) sind zusätzlich exakt auf das tatsächliche Seitenverhältnis der
     jeweiligen Abzeichen-Grafik abgestimmt (344x461 / 248x400 / 224x301 Px, also
     ~0.75 / 0.62 / 0.74 Breite:Höhe) statt wie zuvor auf die alten, nicht mehr
     passenden Fest-Pixel-Verhältnisse (150:200 / 112:200 / 96:200) -- vorher war die
     Box bei Platz 2 und 3 schmaler als die Grafik bei voller Höhe gebraucht hätte,
     wodurch object-fit:contain dort unbemerkt Leerraum ließ und beide Abzeichen
     kleiner wirkten, als ihre Box vermuten ließ (Platz 3 rendete dadurch sogar
     kleiner als Platz 2, obwohl seine Box "größer" aussah). Mit exakt passendem
     Seitenverhältnis füllt jedes Abzeichen seine Box jetzt vollständig aus -- ein
     spürbarer Größengewinn für alle drei Plätze, ohne dass sich die Icons dadurch
     näherkommen: der Abstand zueinander (Kartenbreite + gap) wird weiterhin exakt
     von computePodiumSizing() über die Bisektion bestimmt (siehe App-Logik) und
     zusätzlich per flex-wrap:nowrap (siehe .podium weiter oben) gegen Zeilenumbruch
     abgesichert.
     Die älteren, nicht in einer @media-Regel stehenden Selektoren
     .medal-card.rankN .medal-badge-img{width:...px} (siehe oben) haben durch die
     Klassenkombination .medal-card.rankN dieselbe CSS-Spezifität wie die Regeln hier
     — sie werden durch die spätere Position im Quelltext überstimmt (siehe frühere
     Prüfrunden zur CSS-Spezifitäts-Falle). Höhe wird zusätzlich explizit gesetzt, da
     sie sonst (mangels eigener CSS-Regel) vom width/height-HTML-Attribut des <img>
     fix auf 200px hängen bleiben würde, unabhängig von --p-badge-h. */
  /* 54. Pruefrunde (Nachtrag 2): siehe ausfuehrliche Begruendung im
     @media(max-width:640px)-Block weiter oben -- derselbe Clip-Rahmen-Mechanismus hier
     fuer Desktop/TV, wo der feste 18px-Blur-Radius des drop-shadow-Filters bei kleineren
     Fenstergroessen (schmalere Bereiche zwischen min-width:641px und ca. 900px, wo
     --p-badge-h entsprechend klein ausfaellt) ebenfalls in das benachbarte Icon
     hineinlaufen konnte. */
  /* 54. Pruefrunde (Nachtrag 3): siehe ausfuehrliche Begruendung im
     @media(max-width:640px)-Block -- Breiten-Faktoren auf das neue, nahezu quadratische
     Seitenverhaeltnis der vom Nutzer gelieferten Abzeichen (ohne Medaillon-Scheibe)
     umgestellt. */
  .medal-card.rank1 .medal-badge-clip{
    display:block;overflow:hidden;
    width:calc(var(--p-badge-h,200px) * .95); height:calc(var(--p-badge-h,200px) * 1.12);
    margin:0 0 calc(var(--p-text-scale,1) * 10px);
  }
  .medal-card.rank2 .medal-badge-clip{
    display:block;overflow:hidden;
    width:calc(var(--p-badge-h,200px) * .97); height:calc(var(--p-badge-h,200px) * .98);
    margin:0 0 calc(var(--p-text-scale,1) * 10px);
  }
  .medal-card.rank3 .medal-badge-clip{
    display:block;overflow:hidden;
    width:calc(var(--p-badge-h,200px) * .92); height:calc(var(--p-badge-h,200px) * .90);
    margin:0 0 calc(var(--p-text-scale,1) * 10px);
  }
  .medal-card .medal-badge-img{width:100%;height:100%;margin:0;}
  .medal-card .medal-name{
    font-size:calc(var(--p-text-scale,1) * 16px * var(--name-fit-scale,1));
    margin-bottom:calc(var(--p-text-scale,1) * 4px);
    min-height:calc(var(--p-text-scale,1) * 34px);
  }
  .medal-card .medal-name-accent{
    width:calc(var(--p-text-scale,1) * 34px);
    height:calc(var(--p-text-scale,1) * 3px);
    margin:0 auto calc(var(--p-text-scale,1) * 9px);
  }
  .medal-card.rank1 .medal-points{font-size:calc(var(--p-text-scale,1) * 54px);}
  .medal-card.rank2 .medal-points{font-size:calc(var(--p-text-scale,1) * 39px);}
  .medal-card.rank3 .medal-points{font-size:calc(var(--p-text-scale,1) * 34px);}
  .medal-card .medal-points-label{
    font-size:calc(var(--p-text-scale,1) * 10px);
    margin-top:calc(var(--p-text-scale,1) * 2px);
  }
  .medal-card .medal-points-label .rank-arrow{font-size:calc(var(--p-text-scale,1) * 12px);}
  /* 54. Pruefrunde: Basis-Abstand 22px -> 28px, damit zwischen den jetzt groesseren
     Icons weiterhin sichtbar "Luft" bleibt (Nutzerwunsch: "ausreichend Abstand
     zwischen den einzelnen Elementen"). computePodiumSizing() liest diesen Wert live
     per getComputedStyle() aus (siehe App-Logik) und bezieht ihn direkt in die
     Bisektion ein -- eine Vergroesserung hier verkleinert bei Bedarf automatisch
     --p-badge-h, statt die Ueberlappungs-Pruefung zu umgehen. */
  .podium{gap:calc(var(--p-text-scale,1) * 28px);}
}

/* Zweite Seite der Gesamt-Rangliste zeigt seit der 33. Prüfrunde nur noch Platz 4–10
   (Platz 1–3 sind ja bereits als Podium hervorgehoben) -- siehe renderOverallList() /
   overallListEmpty in der App-Logik. Der Filterbereich ist bewusst als eigene
   Konstante (siehe OVERALL_LIST_RANGE dort) gepflegt, nicht hier im CSS-Kommentar. */
/* Restliche-Platzierungen-Liste dynamisch skaliert (30. Prüfrunde: Bisektions-Fit gegen
   #tvScroll; 33. Prüfrunde: zeigt nur noch 7 statt 10 Zeilen UND darf bei Restplatz nach
   OBEN über die normale Größe hinaus wachsen, siehe fitOverallList() -- MAX_GROW dort).
   --list-scale steuert Zeilenhöhe/Schrift NUR auf dieser Folie (Selektoren extra mit
   #slide-overall-list/#overallListWrap-Präfix, damit die Mansion/Valley/Phobia-
   Einzelspiel-Ranglisten, die dieselbe .board-row-Klasse nutzen, unverändert bleiben).
   Die Rang-Spalte ist bewusst GROSSZÜGIG bemessen (nicht nur für "10" + Pfeil-/NEU-Badge
   bei normaler Größe, siehe 30. Prüfrunde, sondern jetzt zusätzlich mit Reserve für die
   33. Prüfrunde, da bei wenig Inhalt --list-scale auch über 1 hinaus wächst und die
   Rang-Ziffer dabei schneller breiter wird als das size-fixe NEU/Pfeil-Badge daneben). */
/* 32. Prüfrunde: die Spalten (Rang/Name/Punkte-Chips/Punkte) wurden bisher PRO ZEILE
   unabhängig als eigenes Grid berechnet ( .board-row war selbst der Grid-Container).
   Bei einem auffällig langen Team-Namen wuchs dessen "1fr"-Spalte in DIESER einen
   Zeile über ihren fairen Anteil hinaus, wodurch Chips/Punkte in genau dieser Zeile
   nach rechts verschoben wurden ("die hintere Anzeige verändert sich", vom Nutzer
   gemeldet) -- alle anderen Zeilen blieben unberührt, das Bild wirkte "springend".
   Saubere, moderne Lösung: #slide-overall-list .board-list ist jetzt selbst das Grid
   und legt die Spaltenbreiten EINMAL für die gesamte Liste fest; jede .board-row
   nimmt per CSS Subgrid (grid-template-columns:subgrid) exakt an diesen Spalten teil,
   behält aber ihre eigene Box (Hintergrund-Streifen, Rahmen, Hover) — Rang/Name/
   Chips/Punkte sind dadurch über ALLE Zeilen hinweg pixelgenau ausgerichtet, egal wie
   lang ein einzelner Name ist. Zusätzlich wird ein zu langer Name jetzt sauber mit
   Ellipse abgeschnitten statt die Spalte zu sprengen (voller Name als Tooltip beim
   Daruberfahren, siehe title-Attribut in renderOverallList). Fallback für die
   (praktisch nicht mehr vorkommenden) Browser ohne Subgrid-Unterstützung weiter unten. */
@media (min-width:641px){
  /* 32. Prüfrunde (Korrektur): die Subgrid-Umstellung muss auf die Desktop/TV-Breite
     beschränkt bleiben. #slide-overall-list .board-row ist eine ID-Selektor-Regel und
     schlägt daher in der CSS-Spezifität IMMER die bestehende
     @media(max-width:640px){ .board-row{display:flex;...} } Mobil-Regel weiter unten,
     unabhängig von der @media-Verschachtelung. Ohne dieses Scoping würde die neue
     Grid/Subgrid-Regel das mobile Flex-Layout ungewollt überschreiben (verifiziert:
     iPhoneSE 375px zeigte dadurch einen Überlauf). Deshalb hier explizit auf
     min-width:641px begrenzt -- das mobile Board-Row-Verhalten bleibt unverändert. */
  #slide-overall-list .board-list{
    display:grid;
    grid-template-columns:calc(50px + 34px * var(--list-scale,1)) 1fr auto;
    column-gap:calc(14px * var(--list-scale,1));
  }
  #slide-overall-list .board-row{
    display:grid;grid-template-columns:subgrid;grid-column:1 / -1;
    /* 53. Pruefrunde: rechtes Padding um 62px*scale erweitert (20 -> 82) -- Platz fuer
       das "Finals Spot"-Band auf Zeile 4 (siehe .finale-ribbon weiter oben). Bewusst
       auf ALLEN Zeilen dieser Liste gesetzt (nicht nur Zeile 4), damit die
       Punktzahl-Spalte ueber die komplette Liste exakt ausgerichtet bleibt -- vom
       Nutzer nach einer ersten, nur-Zeile-4-Fassung ausdruecklich so gewuenscht.
       54. Pruefrunde (Nachtrag 4, Nutzer-Feedback: "final spot label ueberdeckt
       genau den Highscore"): die bisherigen 82px waren rechnerisch zu knapp --
       das Band ist 90px breit, sitzt zusaetzlich mit right:6px vom Rand ab, braucht
       also mindestens 96px Platz (Drehung um 20deg aendert die horizontale
       Ausdehnung bei dieser flachen Bandform kaum, da die geringe Bandhoehe den
       Rotations-Effekt nahezu aufhebt -- rechnerisch wie per Playwright-Messung
       bestaetigt). Bei kleinerer Team-Anzahl waechst --list-scale zudem ueber 1
       (mehr vertikaler Platz pro Zeile), wodurch die fehlenden ~14px zusaetzlich
       skaliert wurden und die Punktzahl in Zeile 4 sichtbar unter dem Band lag
       (vom Nutzer gemeldet und mit echten Turnierdaten reproduziert, z. B. 10
       Teams: Ueberlappung von ca. 18x32px bei --list-scale=1.3). Fix: rechtes
       Padding auf 104px*scale erhoeht -- deckt die tatsaechliche Bandbreite
       (96px) plus Sicherheitsabstand ab, mit Playwright ueber mehrere
       Team-Anzahlen/Bildschirmbreiten (inkl. sehr grosser Punktzahlen) auf 0
       Ueberlappung verifiziert. Der Mobil-Block weiter unten (max-width:640px)
       ist von diesem Fehler nicht betroffen (dort bereits ausreichend Puffer,
       separat verifiziert) und bleibt unveraendert. */
    padding:calc(14px * var(--list-scale,1)) calc(104px * var(--list-scale,1)) calc(14px * var(--list-scale,1)) calc(20px * var(--list-scale,1));
  }
  #slide-overall-list .board-row .rank{font-size:calc(23px * var(--list-scale,1));white-space:nowrap;}
  #slide-overall-list .board-row .name{
    font-size:calc(16px * var(--list-scale,1) * var(--name-fit-scale,1));
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;
  }
  @supports not (grid-template-columns:subgrid){
    /* Sicherheitsnetz für sehr alte Browser: dieselbe pro-Zeile-Spaltenbreite wie vor der
       32. Runde -- die Ellipse oben verhindert auch hier bereits das Kernproblem (zu
       lange Namen verschieben die restliche Zeile). */
    #slide-overall-list .board-row{
      grid-template-columns:calc(50px + 34px * var(--list-scale,1)) 1fr auto;
      gap:calc(14px * var(--list-scale,1));
    }
  }
}
#slide-overall-list .board-row .points{
  font-size:calc(22px * var(--list-scale,1));
  min-width:calc(40px + 30px * var(--list-scale,1));
}
/* 39. Pruefrunde (Nutzer-Feedback zur 38. Runde): das bisherige Zwei-Spalten-Grid
   (Rang+Name links, Punktzahl+Chips rechts gestapelt, siehe Git-Historie) wirkte
   uneinheitlich/"verrutscht" und liess die Namen kleiner ausfallen als auf den
   anderen Ranglisten (Mansion/Valley/Phobia). Jetzt exakt dasselbe, bereits
   bewaehrte Flex-Layout wie dort (siehe .board-row-Basisregel im Handy-Block weiter
   oben): Rang + Name teilen sich die erste Zeile (Name waechst per flex:1 1 auto und
   nutzt die volle Zeilenbreite, dieselbe grosse einheitliche Schrift wie ueberall
   -- siehe --name-fit-scale/fitBoardRowNames() in der App-Logik), Punktzahl sitzt
   rechtsbuendig auf derselben Zeile, die drei Spiel-Chips brechen als eigene, volle
   Zeile darunter um. Nur ein Name, der bei der einheitlichen Groesse nicht mehr auf
   die erste Zeile passt, wird individuell verkleinert -- reicht auch das nicht,
   bricht ganz normal ein zweiter Absatz um (weiches Verhalten statt Ellipse/
   Zeilenlimit), OHNE dass sich Rang, Punktzahl oder die Chip-Zeile darunter dadurch
   verschieben (sie sitzen in eigenen, vom Namen unabhaengigen Flex-Posten, genau wie
   bei den Einzelspiel-Ranglisten) -- das Layout bleibt dadurch von Zeile zu Zeile
   vorhersehbar gleich, auch bei wechselnder Namenslaenge. Zusaetzliche Korrektur
   gegenueber der Einzelspiel-Vorlage: die Rang-Spalte (.rank) bekommt hier eine FESTE
   Breite statt nur min-width, denn sie enthaelt neben der Rang-Ziffer immer auch ein
   Status-Badge (NEU/Pfeil-hoch/-runter/Strich, siehe arrowHtml()) -- bei variabler
   Breite (1-stellige vs. 2-stellige Rang-Ziffer, "NEU" vs. ein einzelnes Pfeilzeichen)
   waere sonst genau der Effekt entstanden, den der Nutzer bemaengelt hat: Name/Punkte
   ruecken je nach Zeile unterschiedlich weit nach rechts. Die feste Breite ist bewusst
   ein Summand aus einem MIT --list-scale skalierten Teil (Rang-Ziffer, deren Schrift
   ja selbst mitskaliert) und einem KONSTANTEN Teil (das Badge/.rank-arrow ist absichtlich
   nicht an --list-scale gekoppelt, siehe CSS oben) -- ein reiner Faktor waere bei
   kleinem --list-scale (viele Teams, Schrift schrumpft) schmaler als das konstant
   grosse Badge und haette es ueberlaufen lassen (per Messreihe empirisch gefittet:
   Breite(scale) ≈ 22.5px*scale + 37px, hier mit Sicherheitsabstand auf 24px*scale+40px
   aufgerundet). */
@media (max-width:640px){
  #slide-overall-list .board-row{
    display:flex;flex-wrap:wrap;align-items:center;
    gap:calc(4px * var(--list-scale,1)) calc(10px * var(--list-scale,1));
    /* 53. Pruefrunde: rechtes Padding wie im Desktop-Block erweitert (14 -> 82) --
       Platz fuer das kleinere, mobile "Finals Spot"-Band (.finale-ribbon, siehe
       @media max-width:640px weiter unten), auf allen Zeilen fuer einheitliche
       Ausrichtung der Punktzahl. */
    padding:calc(12px * var(--list-scale,1)) calc(82px * var(--list-scale,1)) calc(12px * var(--list-scale,1)) calc(14px * var(--list-scale,1));
  }
  #slide-overall-list .board-row .rank{
    flex:0 0 calc(24px * var(--list-scale,1) + 40px);width:calc(24px * var(--list-scale,1) + 40px);
    font-size:max(12px, calc(18px * var(--list-scale,1)));text-align:left;white-space:nowrap;
  }
  #slide-overall-list .board-row .name{
    /* flex-basis:0 statt "auto": bei "auto" entscheidet der Browser anhand der
       ungebrochenen (max-content) Textbreite, ob das Element ueberhaupt noch auf die
       erste Zeile passt -- bei einem sehr langen Namen "faellt" das GESAMTE
       Namens-Element dann als Ganzes in eine eigene neue Zeile UNTER Rang/Badge (statt
       nur intern umzubrechen), was Rang und Name dieser einen Zeile sichtbar
       auseinanderreisst. Mit flex-basis:0 startet die Breitenberechnung immer bei 0,
       das Element bleibt zuverlaessig neben Rang/Badge auf der ersten Zeile und bricht
       nur INTERN (durch white-space:normal) in eine zweite Namens-Zeile um, sobald es
       nicht mehr passt -- exakt das gewuenschte "Absatz an der richtigen Stelle".
       40. Pruefrunde: Schriftgroesse jetzt NUR noch ueber --name-fit-scale
       gesteuert (nicht mehr zusaetzlich mit --list-scale multipliziert) --
       exakt wie bei den Einzelspiel-Ranglisten, damit die Namen hier genauso
       gross erscheinen wie dort.
       Mobile-Design-Audit-Nachtrag (15.09.2026): word-break:break-word ersetzt durch
       word-break:normal + hyphens:auto (identisch zu .medal-name auf dem Podium,
       overflow-wrap:break-word bleibt als Netz). fitBoardRowNames() schrumpft Namen
       nur bis zur Untergrenze NAME_FIT_MIN=0.75 -- reicht das bei einem sehr langen
       Namen nicht ganz fuer eine Zeile, riss word-break:break-word bisher genau ein
       ueberstehendes Zeichen allein in die zweite Zeile (z. B. "Kellerkinde"/"r", vom
       Nutzer entdeckt). Mit word-break:normal+hyphens:auto bricht der Browser
       stattdessen an einer sinnvollen Trennstelle (ggf. mit Bindestrich, dank
       lang="de" auf <html>) statt einzelne Buchstaben abzutrennen. */
    flex:1 1 0;min-width:0;
    font-size:calc(14px * max(1, var(--list-scale,1)) * var(--name-fit-scale,1));line-height:1.22;
    white-space:normal;overflow-wrap:break-word;word-break:normal;hyphens:auto;
  }
  #slide-overall-list .board-row .points{
    /* 48. Pruefrunde: die Chip-Zeile (frueher order:3 darunter) ist entfallen --
       Rang+Name+Punktzahl bleiben zusammen auf einer Zeile, kein order noetig. */
    flex:0 0 auto;font-size:max(11px, calc(17px * var(--list-scale,1)));min-width:0;margin-left:8px;
  }
  #slide-overall-list.active{
    display:flex;flex-direction:column;justify-content:center;min-height:100%;
  }
  /* 53. Pruefrunde: kleinere Fassung des "Finals Spot"-Bands fuer schmale Bildschirme --
     dieselbe Herangehensweise wie beim Desktop-Band (siehe .finale-ribbon weiter oben),
     nur mit kleineren Grundwerten, damit es bei den hier deutlich schmaleren
     Karten (~300-380px) ebenfalls vollstaendig innerhalb des Kartenrahmens bleibt
     (per Playwright bei mehreren Handy-Breiten verifiziert). */
  .finale-ribbon{
    top:calc(12px * var(--list-scale,1));right:calc(4px * var(--list-scale,1));
    width:calc(62px * var(--list-scale,1));padding:calc(2px * var(--list-scale,1)) 0;
    font-size:calc(7px * var(--list-scale,1));letter-spacing:.3px;transform:rotate(14deg);
  }
}

/* ---------- Seitenwechsel: Crossfade + Weichzeichner ---------- */
/* Sanfter Uebergang: die aktive Folie blendet mit leichtem Weichzeichner
   und Skalierung aus, danach blendet die neue Folie ebenso weich wieder
   ein. Rein CSS-animationsbasiert, arbeitet mit der bereits vorhandenen
   .slide/.active-Struktur (kein zusaetzliches Overlay-Element noetig),
   daher automatisch vollstaendig blickdicht zwischen den beiden Phasen.
   Nachtrag (17.09.2026, Nutzerwunsch "Skalieren bei Folienwechsel wirkt
   sehr extrem und dauert lang"): Blur (war 10px) und Skalierung (war
   1.025/1.03, ca. 2.5-3% groesser/kleiner) deutlich zurueckgenommen -- auf
   einem grossen TV-Bildschirm wirkten 2.5-3% Groessensprung plus starker
   Weichzeichner beim Betrachten aus der Naehe/auf dem Screenshot viel
   praesenter als am Schreibtisch beabsichtigt. Bewusst NICHT komplett
   entfernt (reiner Opacity-Fade), weil ein Nutzerwunsch vom 16.09.2026
   ausdruecklich einen spuerbaren Unterschied zwischen "neues Thema" (mit
   Skalierung/Blur) und "gleicher Abschnitt" (.fx-*-soft, reiner
   Opacity-Fade, s.u.) wollte -- nur die Intensitaet UND die Dauer (war
   .3s/.38s) sind jetzt kleiner, damit der Effekt eher "spuerbar" als
   "auffaellig" wirkt. Die Dauer ist zusammen mit den Timeouts in
   changeSlideAnimated() (tv.js) angepasst -- beide Stellen bei weiteren
   Aenderungen bitte synchron halten. */
.tv-scroll .slide.fx-out{animation:slideFadeOut .22s ease forwards;}
.tv-scroll .slide.fx-in{animation:slideFadeIn .3s ease forwards;}
@keyframes slideFadeOut{
  0%{opacity:1;filter:blur(0);transform:scale(1);}
  100%{opacity:0;filter:blur(3px);transform:scale(1.008);}
}
@keyframes slideFadeIn{
  0%{opacity:0;filter:blur(3px);transform:scale(1.01);}
  100%{opacity:1;filter:blur(0);transform:scale(1);}
}
/* Nachtrag (16.09.2026, Nutzerwunsch): ruhigere Variante des obigen
   Crossfades, ausschließlich für den Wechsel Podium <-> "weitere
   Platzierungen" (siehe isSameSectionSlideTransition() in tv.js) -- beide
   Folien zeigen denselben Titel "TEAM TOPLIST", der Blur+Zoom-Effekt wirkte
   dort wie ein unmotivierter Größensprung. Reiner Opacity-Fade, bewusst
   dieselben Zeitdauern (.22s/.3s, s. Nachtrag 17.09.2026 oben) wie beim
   normalen Effekt, damit die fxBusy-Zeitsteuerung in changeSlideAnimated()
   fuer beide Varianten gleich funktioniert. */
.tv-scroll .slide.fx-out-soft{animation:slideFadeOutSoft .22s ease forwards;}
.tv-scroll .slide.fx-in-soft{animation:slideFadeInSoft .3s ease forwards;}
@keyframes slideFadeOutSoft{
  0%{opacity:1;}
  100%{opacity:0;}
}
@keyframes slideFadeInSoft{
  0%{opacity:0;}
  100%{opacity:1;}
}

/* ---------- Neuer Highscore: Doppel-Schockwelle, Muzzle-Flash-Charakter & Sieger-Fanfare ---------- */
.hs-stage{position:absolute;inset:0;z-index:150;pointer-events:none;display:none;}
.hs-stage.hs-active{display:block;}
.hs-overlay{position:absolute;inset:0;z-index:5;display:flex;align-items:center;justify-content:center;}
.hs-card{
  position:relative;background:var(--panel-grad);border:1px solid rgba(255,207,92,.5);border-radius:22px;
  padding:32px 54px;text-align:center;box-shadow:0 30px 90px -20px rgba(0,0,0,.75);
  opacity:0;transform:scale(.7);z-index:2;
}
.hs-card .lbl{font-family:'Space Grotesk';font-size:13px;letter-spacing:2px;color:var(--gold);text-transform:uppercase;margin-bottom:9px;}
.hs-card .game-tag{font-family:'Inter';font-weight:600;font-size:12px;letter-spacing:.5px;color:var(--ink-dim);margin-bottom:11px;padding-bottom:11px;border-bottom:1px solid var(--border);}
.hs-card .team{font-family:'Space Grotesk';font-weight:700;font-size:26px;margin-bottom:7px;}
.hs-card .score{font-family:'Bebas Neue';font-size:62px;color:var(--gold);line-height:1;}
.hs-flash{position:absolute;inset:0;z-index:4;}
@keyframes hsCardIn{0%{opacity:0;transform:scale(.4) rotate(-6deg);}55%{opacity:1;transform:scale(1.1) rotate(2deg);}75%{transform:scale(.97) rotate(-1deg);}100%{opacity:1;transform:scale(1) rotate(0);}}
.hs-stage.hs-active .hs-card{animation:hsCardIn .7s cubic-bezier(.34,1.56,.64,1) .08s forwards;}

.shockwave{position:absolute;left:50%;top:50%;width:24px;height:24px;margin:-12px 0 0 -12px;border-radius:50%;
  border:3px solid rgba(255,190,110,.9);opacity:0;box-shadow:0 0 18px rgba(255,160,80,.4);z-index:1;}
@keyframes shockExpand{0%{opacity:.95;transform:scale(.3);}55%{opacity:.4;}100%{opacity:0;transform:scale(var(--sc,15));}}
.hs-stage.hs-active .shockwave{animation:shockExpand 2.6s cubic-bezier(.15,.7,.3,1) forwards;}

.hs-spark{position:absolute;border-radius:50%;
  background:radial-gradient(circle, #fff6d8, #ffb24d 45%, #ff5a29 75%, transparent 100%);opacity:0;z-index:3;}
@keyframes sparkFly{0%{opacity:0;transform:translate(0,0) scale(.3);}10%{opacity:1;}55%{opacity:.9;}100%{opacity:0;transform:translate(var(--dx),var(--dy)) scale(.35);}}
.hs-stage.hs-active .hs-spark{animation:sparkFly 2.6s cubic-bezier(.18,.7,.35,1) forwards;}

.hs-star{position:absolute;font-size:19px;line-height:1;opacity:0;color:var(--gold);
  text-shadow:0 0 10px rgba(255,207,92,.95), 0 0 4px #fff;z-index:3;}
/* Nachtrag 2 (19.09.2026, Nutzer-Wunsch): vorher blieben die Sterne nach dem
   initialen Rausfliegen (bis 38%) fuer den Rest der (bei der grossen Feier
   sehr langen, ~26s) Animation bewegungslos an derselben Stelle stehen und
   haben nur noch getwinkelt -- das wirkte wie "haengengeblieben".
   Nachtrag 3: der erste Fix hat an JEDEM Twinkle-Schritt auch einen neuen
   transform-Wert gesetzt -- dadurch hat jedes Segment erneut mit der (stark
   abbremsenden) Kurve des Rausflug-Teils abgebremst und ist kurz vor dem
   naechsten Schritt fast stehengeblieben, bevor es neu beschleunigt hat. Das
   sah im Video wie ein Stop-and-Go aus, nicht wie ein Fallen. Jetzt hat die
   Fallbewegung nur noch EINEN durchgehenden linearen Abschnitt (30% -> 100%,
   siehe animation-timing-function:linear), das Twinkeln laeuft unabhaengig
   davon nur ueber die Opacity weiter (eigene Zwischenschritte OHNE eigene
   transform-Werte, beeinflusst die Bewegung also nicht mehr). --fall ist pro
   Stern zufaellig (siehe hsSpawnStars), damit nicht alle im Gleichschritt
   fallen. */
@keyframes starFlyTwinkle{
  0%{opacity:0; transform:translate(0,0) scale(.2) rotate(0deg);}
  14%{opacity:1;}
  30%{opacity:1; transform:translate(var(--dx),var(--dy)) scale(1) rotate(var(--rot,150deg)); animation-timing-function:linear;}
  46%{opacity:.55;}
  58%{opacity:1;}
  72%{opacity:.5;}
  86%{opacity:1;}
  100%{opacity:0; transform:translate(var(--dx),calc(var(--dy) + var(--fall,620px))) scale(.8) rotate(var(--rot,150deg));}
}
.hs-stage.hs-active .hs-star{animation:starFlyTwinkle 4.3s cubic-bezier(.2,.75,.3,1) forwards;}

@keyframes hsFlashDouble{0%{background:rgba(255,255,255,0);}4%{background:rgba(255,255,255,.9);}10%{background:rgba(255,207,92,.1);}22%{background:rgba(255,207,92,.65);}30%{background:rgba(255,207,92,0);}100%{background:rgba(255,207,92,0);}}
.hs-stage.hs-active .hs-flash{animation:hsFlashDouble 1.1s ease forwards;}

.hs-char-layer{position:absolute;right:1%;bottom:0;height:100%;width:38%;z-index:2;
  display:flex;align-items:flex-end;justify-content:center;overflow:visible;}
.hs-char-img{position:relative;max-height:96%;max-width:112%;object-fit:contain;
  transform:translateX(92%) scale(.8);opacity:0;
  filter:drop-shadow(0 16px 26px rgba(0,0,0,.65));}
@keyframes charPunchIn{
  0%{opacity:0; transform:translateX(92%) scale(.8);}
  40%{opacity:1; transform:translateX(-2%) scale(1.06);}
  62%{transform:translateX(1%) scale(.97);}
  100%{opacity:1; transform:translateX(0) scale(1);}
}
.hs-stage.hs-active .hs-char-img{animation:charPunchIn .68s cubic-bezier(.36,.6,.2,1) forwards;}
.hs-char-flash{position:absolute;right:16%;bottom:36%;width:150px;height:150px;border-radius:50%;
  background:radial-gradient(circle, rgba(255,224,160,.95), rgba(255,150,60,.55) 42%, transparent 74%);
  opacity:0;transform:translate(50%,50%) scale(.3);}
@keyframes muzzleFlash{0%{opacity:0;transform:translate(50%,50%) scale(.2);}6%{opacity:1;transform:translate(50%,50%) scale(1.3);}16%{opacity:.6;transform:translate(50%,50%) scale(1.6);}45%{opacity:.28;transform:translate(50%,50%) scale(1.9);}100%{opacity:0;transform:translate(50%,50%) scale(2.3);}}
.hs-stage.hs-active .hs-char-flash{animation:muzzleFlash 2.6s ease-out .05s forwards;}
.hs-ember{position:absolute;width:5px;height:5px;border-radius:50%;
  background:radial-gradient(circle, #ffe3a8, #ff7a29 60%, transparent 100%);opacity:0;}
@keyframes emberFly{0%{opacity:0;transform:translate(0,0) scale(.5);}10%{opacity:1;}55%{opacity:.7;}100%{opacity:0;transform:translate(var(--ex),var(--ey)) scale(1.1);}}
.hs-stage.hs-active .hs-ember{animation:emberFly 2.6s ease-out forwards;}

/* Nachtrag 2 (19.09.2026, Nutzer-Wunsch): urspruengliche, stehenbleibende
   Einschusslöcher durch kurze Funkeln im selben Stil wie die Feier-Sterne
   (.hs-star) ersetzt -- blitzen synchron zu den echten Schuss-Transienten im
   MG-Audio-Clip auf (siehe HS_SHOT_TIMES/spawnShotSpark in js/tv.js), fallen
   ein Stück und verblassen wieder von selbst (self-removal per
   'animationend' in spawnShotSpark, bleiben NICHT bis zum Ende der Feier
   stehen). */
.shot-spark{position:absolute;font-size:19px;line-height:1;opacity:0;color:var(--gold);
  text-shadow:0 0 12px rgba(255,207,92,1), 0 0 5px #fff;z-index:6;pointer-events:none;
  transform:translate(-50%,-50%) scale(.3) rotate(0deg);
  animation:shotSparkFall .85s cubic-bezier(.22,.6,.32,1) forwards;}
@keyframes shotSparkFall{
  0%{opacity:0; transform:translate(-50%,-50%) scale(.25) rotate(0deg);}
  12%{opacity:1; transform:translate(-50%,-50%) scale(1.3) rotate(var(--sr,20deg));}
  30%{opacity:1; transform:translate(-50%,calc(-50% + 14px)) scale(1) rotate(var(--sr,20deg));}
  100%{opacity:0; transform:translate(-50%,calc(-50% + 64px)) scale(.55) rotate(var(--sr,20deg));}
}

.hs-stage.hs-fading .hs-overlay,
.hs-stage.hs-fading .hs-char-layer,
.hs-stage.hs-fading .hs-flash{
  transition:opacity .8s ease;
  opacity:0;
}
#slide-overall-podium.active{display:flex;flex-direction:column;justify-content:center;min-height:100%;}
@media (max-width:640px){
  /* Auf sehr hohen Handys (z.B. iPhone 14/15) bleibt sonst viel Leerraum ober- und
     unterhalb des kompakten Podiums stehen, weil die Regel oben den Inhalt vertikal
     zentriert (sinnvoll für die TV-Ansicht) — auf schmalen Bildschirmen wirkt das
     eher wie ein Layout-Fehler. Auf Mobile daher oben ausrichten; überschüssiger
     Platz sammelt sich stattdessen unten vor den Punkten, was gewohnter aussieht. */
  #slide-overall-podium.active{justify-content:flex-start;}
}
.tv-head-badge{margin-top:6px;}
