"""Flarlo – TikTok-ähnliches Frontend (HTML/CSS/JS)."""

from __future__ import annotations

from pathlib import Path

INDEX_HTML = """<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<meta name="theme-color" content="#000000">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>Flarlo – Flare. Flow. Dein Clip.</title>
<link rel="icon" type="image/png" href="img/logo.png">
<link rel="apple-touch-icon" href="img/logo.png">
<link rel="stylesheet" href="css/vibeloop.css?v=__BUILD_ID__">
</head>
<body>
<div id="app">
  <div id="splash" class="hidden">
    <img src="img/logo.png" alt="Flarlo" class="brand-logo brand-logo--splash">
    <p class="splash-tagline">Flare. Flow. Dein Clip.</p>
  </div>

  <div id="auth-screen" class="landing-screen">
    <div class="landing-wrap">
      <section class="landing-hero" aria-label="Über Flarlo">
        <img src="img/logo.png" alt="Flarlo" class="brand-logo brand-logo--landing">
        <p class="landing-kicker">Kurzvideo-Plattform aus Deutschland</p>
        <h1 class="landing-title">Flare. Flow. Dein Clip.</h1>
        <p class="landing-lead">
          <strong>Flarlo</strong> ist deine App für Kurzvideos wie TikTok – mit über 100 Nischen,
          Live-Streaming, Geschenken und einer Community, die zu deinen Interessen passt.
        </p>
        <ul class="landing-features">
          <li><span class="landing-feat-icon">🎬</span> Clips drehen &amp; in deiner Nische posten</li>
          <li><span class="landing-feat-icon">🔴</span> Live gehen, chatten &amp; Geschenke erhalten</li>
          <li><span class="landing-feat-icon">🔍</span> Trends entdecken – Comedy, Musik, Sport &amp; mehr</li>
          <li><span class="landing-feat-icon">💬</span> Folgen, kommentieren &amp; Nachrichten schreiben</li>
        </ul>
      </section>
      <section class="landing-auth" aria-label="Anmelden oder registrieren">
        <div class="landing-auth-card">
          <div class="auth-tabs" role="tablist">
            <button type="button" id="auth-tab-login" class="auth-tab active" role="tab" aria-selected="true">Anmelden</button>
            <button type="button" id="auth-tab-register" class="auth-tab" role="tab" aria-selected="false">Registrieren</button>
          </div>
          <p id="auth-panel-hint" class="auth-panel-hint">Willkommen zurück – melde dich mit deinem Konto an.</p>
          <input id="auth-email" type="email" placeholder="E-Mail" autocomplete="email" inputmode="email">
          <input id="auth-pass" type="password" placeholder="Passwort" autocomplete="current-password">
          <div id="auth-gender-wrap" class="hidden">
            <label for="auth-gender" class="auth-label">Geschlecht</label>
            <select id="auth-gender" class="auth-gender" aria-label="Geschlecht">
              <option value="Frau">Frau</option>
              <option value="Mann">Mann</option>
              <option value="Divers">Divers</option>
            </select>
          </div>
          <button type="button" id="auth-login" class="btn-primary">Anmelden</button>
          <button type="button" id="auth-register" class="btn-primary hidden">Kostenlos starten</button>
          <p id="auth-error" class="error"></p>
          <p class="landing-legal">Mit der Registrierung akzeptierst du unsere Community-Regeln (kein Mobbing, faire Clips).</p>
        </div>
      </section>
    </div>
  </div>

  <div id="niche-screen" class="hidden niche-screen">
    <header class="niche-head">
      <h2 class="niche-title">Was interessiert dich?</h2>
      <p id="niche-hint" class="niche-sub">Tippe bis zu 5 Nischen an – dein Feed wird personalisiert.</p>
      <div class="niche-progress" id="niche-progress" aria-label="Auswahl-Fortschritt">
        <span class="niche-dot"></span><span class="niche-dot"></span><span class="niche-dot"></span><span class="niche-dot"></span><span class="niche-dot"></span>
      </div>
      <label class="niche-search-box">
        <span class="niche-search-icon" aria-hidden="true">🔍</span>
        <input id="niche-search" type="search" placeholder="Nische suchen…" autocomplete="off">
      </label>
      <div id="niche-cats" class="niche-cat-rail"></div>
    </header>
    <div class="niche-scroll">
      <div id="niche-grid" class="niche-card-grid"></div>
      <p id="niche-empty" class="niche-empty hidden">Keine Nische gefunden – probier einen anderen Suchbegriff.</p>
    </div>
    <footer class="niche-bottom">
      <button type="button" id="niche-save" class="niche-save-btn" disabled>Feed starten</button>
    </footer>
  </div>

  <div id="explore-screen" class="hidden tt-page tt-discover">
    <header class="explore-header">
      <button type="button" id="explore-back" class="profile-icon-btn" aria-label="Zurück">‹</button>
      <h1 class="explore-title">Erkunden</h1>
      <input id="explore-search" type="search" class="explore-search" placeholder="Suchen" autocomplete="off">
    </header>
    <div id="explore-categories" class="explore-categories">
      <button type="button" data-niche="" class="active">Alle</button>
    </div>
    <div id="explore-grid" class="explore-grid"></div>
    <p id="explore-status" class="explore-status hidden"></p>
  </div>

  <div id="inbox-screen" class="hidden tt-page tt-inbox">
    <header class="inbox-header">
      <button type="button" id="inbox-back" class="profile-icon-btn" aria-label="Zurück">‹</button>
      <h1 class="inbox-title">Nachrichten</h1>
      <button type="button" id="inbox-coins-btn" class="inbox-coins-btn" title="Coins">🪙</button>
    </header>
    <div id="inbox-layout" class="inbox-layout">
      <div id="inbox-threads" class="inbox-threads"></div>
      <div id="inbox-chat" class="inbox-chat hidden">
        <div class="inbox-chat-head">
          <button type="button" id="inbox-chat-back" class="profile-icon-btn inbox-chat-back">‹</button>
          <img id="inbox-chat-avatar" class="inbox-chat-avatar" src="" alt="">
          <strong id="inbox-chat-name"></strong>
        </div>
        <div id="inbox-messages" class="inbox-messages" role="log"></div>
        <div class="inbox-composer">
          <input id="inbox-input" type="text" maxlength="2000" placeholder="Nachricht schreiben…" autocomplete="off">
          <button type="button" id="inbox-send" class="inbox-send-btn">→</button>
        </div>
      </div>
    </div>
  </div>

  <div id="coins-modal" class="modal hidden">
    <div class="modal-inner">
      <h3>Coins aufladen</h3>
      <p id="coins-balance" class="coins-balance"></p>
      <div id="coins-pack-grid" class="coins-pack-grid"></div>
      <p class="hint">Sicher bezahlen mit Stripe (Karte, PayPal, Klarna …)</p>
      <button id="coins-close" type="button" class="btn-ghost">Schließen</button>
    </div>
  </div>

  <aside id="tt-sidebar" class="tt-sidebar hidden">
    <a href="/" class="tt-side-logo" aria-label="Flarlo"><img src="img/logo.png" alt="Flarlo"></a>
    <label class="tt-side-search">
      <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"><circle cx="11" cy="11" r="7" fill="none" stroke="currentColor" stroke-width="2"/><path d="M20 20l-4-4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
      <input type="search" id="tt-side-search" placeholder="Suchen" autocomplete="off">
    </label>
    <nav class="tt-side-nav">
      <button type="button" class="tt-side-item active" data-side="home" data-feed="foryou"><svg viewBox="0 0 24 24" width="26" height="26"><path fill="currentColor" d="M12 3l9 8h-3v10h-5v-6H11v6H6V11H3l9-8z"/></svg><span>Für dich</span></button>
      <button type="button" class="tt-side-item" data-side="explore"><svg viewBox="0 0 24 24" width="26" height="26"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"/><polygon points="16 12 11 15 11 9" fill="currentColor"/></svg><span>Erkunden</span></button>
      <button type="button" class="tt-side-item" data-side="following" data-feed="following"><svg viewBox="0 0 24 24" width="26" height="26"><circle cx="9" cy="8" r="3.5" fill="none" stroke="currentColor" stroke-width="2"/><path fill="none" stroke="currentColor" stroke-width="2" d="M3 20c0-3.5 3-5.5 6-5.5s6 2 6 5.5"/><path fill="currentColor" d="M17 11h6v2h-6zM20 9v6"/></svg><span>Gefolgt</span></button>
      <button type="button" class="tt-side-item" data-side="live"><svg viewBox="0 0 24 24" width="26" height="26"><rect x="3" y="6" width="18" height="12" rx="2" fill="none" stroke="currentColor" stroke-width="2"/><path fill="currentColor" d="M10 9l6 3-6 3V9z"/></svg><span>LIVE</span></button>
      <button type="button" class="tt-side-item" data-side="upload"><svg viewBox="0 0 24 24" width="26" height="26"><path fill="none" stroke="currentColor" stroke-width="2" d="M12 5v14M5 12h14"/></svg><span>Hochladen</span></button>
      <button type="button" class="tt-side-item" data-side="profile"><svg viewBox="0 0 24 24" width="26" height="26"><circle cx="12" cy="8" r="4" fill="none" stroke="currentColor" stroke-width="2"/><path fill="none" stroke="currentColor" stroke-width="2" d="M4 20c0-4 4-6 8-6s8 2 8 6"/></svg><span>Profil</span></button>
      <button type="button" class="tt-side-item" data-side="inbox"><svg viewBox="0 0 24 24" width="26" height="26"><path fill="currentColor" d="M20 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V6a2 2 0 00-2-2zm0 2v.5L12 13 4 6.5V6h16zM4 18V8.2l7.4 6.2a1 1 0 001.2 0L20 8.2V18H4z"/></svg><span>Nachrichten</span></button>
    </nav>
    <div class="tt-side-footer">
      <p>© Flarlo</p>
    </div>
  </aside>

  <div id="main-screen" class="hidden tt-page tt-app">
    <header id="top-bar" class="tt-top-bar tt-mobile-only">
      <button type="button" id="tab-live" class="tt-live-btn">LIVE</button>
      <div class="tt-feed-tabs">
        <button type="button" class="tab" data-mode="following">Folge ich</button>
        <button type="button" class="tab active" data-mode="foryou">Für dich</button>
      </div>
      <button type="button" id="tt-top-search" class="tt-top-search" aria-label="Entdecken">
        <svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><circle cx="11" cy="11" r="7" fill="none" stroke="currentColor" stroke-width="2"/><path d="M20 20l-4-4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
      </button>
    </header>

    <div class="tt-stage">
      <div class="tt-player-column">
        <div class="tt-player-frame" id="tt-player-frame">
          <div id="feed-container" class="tt-feed"></div>
        </div>
        <aside id="action-bar" class="tt-action-bar"></aside>
      </div>
      <div class="tt-scroll-btns tt-desktop-only">
        <button type="button" id="tt-scroll-up" class="tt-scroll-btn" aria-label="Vorheriges Video">▲</button>
        <button type="button" id="tt-scroll-down" class="tt-scroll-btn" aria-label="Nächstes Video">▼</button>
      </div>
    </div>
    <div id="feed-gift-toast" class="feed-gift-toast hidden"></div>

    <nav id="bottom-nav" class="tt-bottom-nav tt-mobile-only">
      <button type="button" data-nav="home" class="active" aria-label="Start">
        <svg class="tt-nav-svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 10.5L12 3l9 7.5V20a1 1 0 01-1 1h-5v-6H9v6H4a1 1 0 01-1-1v-9.5z" fill="currentColor"/></svg>
      </button>
      <button type="button" data-nav="explore" aria-label="Entdecken">
        <svg class="tt-nav-svg" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"/><polygon points="16 12 11 15 11 9" fill="currentColor"/></svg>
      </button>
      <button type="button" data-nav="upload" class="tt-create-btn" aria-label="Erstellen">
        <span class="tt-create-outer"><span class="tt-create-inner">+</span></span>
      </button>
      <button type="button" data-nav="inbox" aria-label="Nachrichten">
        <svg class="tt-nav-svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 4H4a2 2 0 00-2 2v12a2 2 0 002 2h16a2 2 0 002-2V6a2 2 0 00-2-2zm0 2v.5L12 13 4 6.5V6h16zM4 18V8.2l7.4 6.2a1 1 0 001.2 0L20 8.2V18H4z" fill="currentColor"/></svg>
      </button>
      <button type="button" data-nav="profile" aria-label="Profil">
        <svg class="tt-nav-svg" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="8" r="4" fill="none" stroke="currentColor" stroke-width="2"/><path d="M4 20c0-4 4-6 8-6s8 2 8 6" fill="none" stroke="currentColor" stroke-width="2"/></svg>
      </button>
    </nav>
  </div>

  <div id="comment-modal" class="modal tt-sheet hidden">
    <div class="modal-inner tt-comment-sheet">
      <div class="tt-sheet-handle"></div>
      <h3>Kommentare</h3>
      <div id="comment-list" class="tt-comment-list"></div>
      <div class="tt-comment-compose">
        <input id="comment-input" placeholder="Kommentar hinzufügen…">
        <button type="button" id="comment-send" class="tt-comment-send">Posten</button>
      </div>
    </div>
  </div>

  <div id="safety-modal" class="modal hidden">
    <div class="modal-inner">
      <h3 id="safety-title">Melden & Sperren</h3>
      <p id="safety-sub" class="hint"></p>
      <div id="safety-actions" class="safety-actions"></div>
      <button id="safety-close" type="button" class="btn-ghost">Schließen</button>
    </div>
  </div>

  <div id="report-modal" class="modal hidden">
    <div class="modal-inner">
      <h3>Inhalt melden</h3>
      <p id="report-sub" class="hint"></p>
      <div id="report-reasons" class="report-reasons"></div>
      <textarea id="report-details" maxlength="500" placeholder="Optional: mehr Details…" rows="3"></textarea>
      <button id="report-submit" type="button" class="btn-primary" disabled>Meldung senden</button>
      <button id="report-cancel" type="button" class="btn-ghost">Abbrechen</button>
    </div>
  </div>

  <div id="gift-modal" class="modal hidden">
    <div class="modal-inner">
      <h3>Geschenk senden</h3>
      <div id="gift-grid"></div>
      <button id="gift-close" class="btn-ghost">Schließen</button>
    </div>
  </div>

  <div id="profile-screen" class="hidden tt-page tt-profile">
    <div class="profile-top-bar">
      <button type="button" id="profile-back" class="profile-icon-btn" aria-label="Zurück">‹</button>
      <span id="profile-top-title" class="profile-top-title">Profil</span>
      <button type="button" id="profile-share-top" class="profile-icon-btn" aria-label="Teilen">↗</button>
    </div>
    <div class="profile-body">
      <div id="profile-header"></div>
      <div class="profile-toolbar">
        <div id="profile-tabs" class="profile-tabs">
          <button type="button" data-ptab="video" class="active">Videos</button>
          <button type="button" data-ptab="photo">Fotos</button>
          <button type="button" data-ptab="all">Alle</button>
        </div>
        <div id="profile-sort" class="profile-sort">
          <button type="button" data-psort="recent" class="active">Aktuelles</button>
          <button type="button" data-psort="popular">Beliebt</button>
          <button type="button" data-psort="oldest">Älteste</button>
        </div>
      </div>
      <div id="profile-grid"></div>
    </div>
  </div>

  <div id="profile-edit-modal" class="modal hidden">
    <div class="modal-inner">
      <h3>Profil bearbeiten</h3>
      <label class="pe-label" for="pe-username">Username</label>
      <input id="pe-username" type="text" maxlength="24" placeholder="dein_name" autocomplete="username">
      <label class="pe-label" for="pe-bio">Bio</label>
      <textarea id="pe-bio" maxlength="300" placeholder="Über dich…" rows="4"></textarea>
      <label class="pe-label" for="pe-link">Link in Bio</label>
      <input id="pe-link" type="url" placeholder="https://…" autocomplete="url">
      <p id="pe-link-hint" class="hint hidden">Link in Bio ab 1.000 Followern freischalten</p>
      <button id="pe-save" type="button" class="btn-primary">Speichern</button>
      <button id="pe-cancel" type="button" class="btn-ghost">Abbrechen</button>
    </div>
  </div>

  <div id="profile-settings-modal" class="modal hidden">
    <div class="modal-inner">
      <h3>Creator & Einstellungen</h3>
      <div id="profile-intro"></div>
      <div id="profile-tier"></div>
      <div id="premium-panel" class="hidden">
        <h4>Flarlo+ Premium</h4>
        <p id="premium-status"></p>
        <ul id="premium-benefits"></ul>
        <button id="premium-subscribe" class="btn-primary hidden">Premium aktivieren</button>
      </div>
      <div id="coins-panel" class="hidden">
        <h4>Coins</h4>
        <p id="coins-wallet-balance"></p>
        <button id="profile-buy-coins" type="button" class="btn-primary">Coins aufladen</button>
      </div>
      <div id="earnings-panel" class="hidden">
        <h4>Creator-Verdienst</h4>
        <p id="earnings-balance"></p>
        <button id="payout-btn" class="btn-primary">Auszahlung anfordern</button>
      </div>
      <div id="community-panel">
        <h4>Community-Regeln</h4>
        <p class="hint" id="community-summary"></p>
        <ul id="community-rules" class="copyright-rules"></ul>
      </div>
      <div id="copyright-panel">
        <h4>Urheberrecht</h4>
        <p class="hint" id="copyright-summary"></p>
        <ul id="copyright-rules" class="copyright-rules"></ul>
      </div>
      <div id="blocked-panel">
        <h4>Gesperrte Accounts</h4>
        <div id="blocked-list" class="blocked-list"><p class="hint">Lade…</p></div>
      </div>
      <button id="profile-settings-close" type="button" class="btn-ghost">Schließen</button>
    </div>
  </div>

  <div id="intro-modal" class="modal hidden">
    <div class="modal-inner">
      <h3>Vorstellungsvideo</h3>
      <p class="hint">Max. 60 Sek. – kostenlos, keine Coins (ab 100 Follower)</p>
      <input type="file" id="intro-file" accept="video/*">
      <video id="intro-preview" class="hidden" controls playsinline></video>
      <button id="intro-submit" class="btn-primary">Speichern</button>
      <button id="intro-delete" class="btn-ghost hidden">Video entfernen</button>
      <button id="intro-cancel" class="btn-ghost">Abbrechen</button>
    </div>
  </div>

  <div id="intro-player" class="modal hidden">
    <div class="modal-inner intro-player-inner">
      <video id="intro-player-v" controls playsinline></video>
      <button id="intro-player-close" class="btn-ghost">Schließen</button>
    </div>
  </div>

  <div id="live-screen" class="hidden studio-app">
    <div class="studio-layout">
      <aside class="studio-left">
        <div class="studio-left-head">
          <button type="button" id="live-screen-back" class="studio-back-btn" aria-label="Zurück">‹</button>
          <div>
            <h1 class="studio-brand">LIVE Studio</h1>
            <span class="studio-badge">Flarlo Creator</span>
          </div>
        </div>
        <div class="studio-panel">
          <div class="studio-panel-head">
            <span>Studioansicht</span>
            <div class="studio-orient">
              <button type="button" id="studio-orient-portrait" class="active" data-orient="portrait">Hochformat</button>
              <button type="button" id="studio-orient-landscape" data-orient="landscape">Querformat</button>
            </div>
          </div>
        </div>
        <div class="studio-panel">
          <div class="studio-panel-head">
            <span>Szenen</span>
            <button type="button" class="studio-icon-btn" title="Szene hinzufügen">＋</button>
          </div>
          <div class="studio-scene-active">
            <span class="studio-scene-dot"></span>
            <span id="studio-scene-name">Hauptszene</span>
          </div>
        </div>
        <div class="studio-panel studio-panel-grow">
          <div class="studio-panel-head"><span>Quellen</span></div>
          <ul class="studio-sources">
            <li><button type="button" id="studio-src-camera" class="studio-source active" data-src="camera"><span class="studio-src-icon">📷</span>Kamera</button></li>
            <li><button type="button" id="studio-src-screen" class="studio-source" data-src="screen"><span class="studio-src-icon">🖥</span>Bildschirm</button></li>
            <li><span class="studio-source locked"><span class="studio-src-icon">💬</span>Chatbox</span></li>
            <li><span class="studio-source locked"><span class="studio-src-icon">🏆</span>Zuschauer-Rang</span></li>
            <li><span class="studio-source locked"><span class="studio-src-icon">Aa</span>Text-Overlay</span></li>
          </ul>
          <div class="studio-tools-label">Tools</div>
          <div class="studio-tools-grid">
            <span class="studio-tool locked">Co-Host</span>
            <span class="studio-tool locked">Multi-Gast</span>
            <button type="button" id="studio-match-btn" class="studio-tool studio-tool-btn">LIVE Match</button>
            <span class="studio-tool locked">Umfrage</span>
            <span class="studio-tool locked">Spiel-Belohnung</span>
            <span class="studio-tool locked">Fanclub</span>
          </div>
        </div>
        <div id="studio-tier-box" class="studio-tier-box"></div>
      </aside>

      <main class="studio-center">
        <div id="studio-preview-wrap" class="studio-preview-wrap studio-preview--portrait">
          <video id="studio-preview-v" autoplay playsinline muted class="hidden"></video>
          <div id="studio-preview-placeholder" class="studio-preview-placeholder">
            <span class="studio-preview-icon">📷</span>
            <p>Kamera-Vorschau</p>
          </div>
          <div id="studio-live-pill" class="studio-live-pill hidden">● Du bist LIVE</div>
          <div class="studio-preview-overlay" id="studio-preview-label">Flarlo LIVE</div>
        </div>
        <div class="studio-controls">
          <div class="studio-src-quick">
            <button type="button" class="studio-quick-btn" data-src="screen" title="Bildschirm">🖥</button>
            <button type="button" class="studio-quick-btn active" data-src="camera" title="Kamera">📷</button>
          </div>
          <div class="studio-mixer">
            <label class="studio-mixer-row">
              <span>Mikrofon</span>
              <input type="range" id="studio-mic-volume" min="0" max="100" value="80">
            </label>
            <label class="studio-mixer-row">
              <span>Desktop-Audio</span>
              <input type="range" id="studio-desk-volume" min="0" max="100" value="60" disabled>
            </label>
          </div>
          <div class="studio-go-row">
            <input id="studio-title" type="text" maxlength="100" class="studio-title-input" placeholder="Live-Titel…" autocomplete="off">
            <button type="button" id="go-live-btn" class="studio-go-live-btn hidden">LIVE starten</button>
            <button type="button" id="studio-resume-live" class="studio-resume-btn hidden">Zurück zum Live</button>
          </div>
        </div>
      </main>

      <aside class="studio-right">
        <div class="studio-panel">
          <div class="studio-panel-head"><span>LIVE-Leistung</span></div>
          <div class="studio-perf-grid" id="studio-perf-grid">
            <div class="studio-perf-item"><span class="studio-perf-val" id="studio-stat-coins">0</span><span>🪙 Coins</span></div>
            <div class="studio-perf-item"><span class="studio-perf-val" id="studio-stat-viewers">0</span><span>👀 Zuschauer</span></div>
            <div class="studio-perf-item"><span class="studio-perf-val" id="studio-stat-followers">0</span><span>Follower</span></div>
            <div class="studio-perf-item"><span class="studio-perf-val" id="studio-stat-likes">0</span><span>❤ Likes</span></div>
          </div>
        </div>
        <div class="studio-panel studio-panel-grow">
          <div class="studio-panel-head"><span>Aktivität</span></div>
          <div id="live-list" class="studio-activity-list"></div>
        </div>
        <div class="studio-panel studio-chat-preview">
          <div class="studio-panel-head"><span>LIVE-Chat</span></div>
          <div id="studio-chat-preview" class="studio-chat-placeholder">
            <p>Geschenke und Kommentare erscheinen hier, sobald du LIVE bist.</p>
            <p class="studio-chat-sub">Während dem Stream siehst du den Chat rechts im Broadcast.</p>
          </div>
        </div>
      </aside>
    </div>
    <footer class="studio-statusbar">
      <span id="studio-stat-cpu">CPU —</span>
      <span id="studio-stat-mem">RAM —</span>
      <span id="studio-stat-bitrate">Bitrate 0 kbps</span>
      <span id="studio-stat-fps">FPS 30</span>
      <span class="studio-status-brand">Flarlo LIVE Studio</span>
    </footer>
  </div>

  <div id="live-broadcast-screen" class="hidden live-room live-room--host">
    <main class="live-stage">
      <header class="live-stage-header">
        <span class="live-badge-inline">● LIVE</span>
        <div class="live-host-meta">
          <strong id="live-broadcast-host-name">Du</strong>
          <span id="live-broadcast-title" class="live-host-sub"></span>
        </div>
        <div id="live-broadcast-stats" class="live-stats-inline"></div>
        <button type="button" id="end-live-btn" class="btn-danger live-end-btn">Beenden</button>
      </header>
      <div class="live-stage-main">
        <div id="live-pk-invite-banner" class="live-pk-invite hidden"></div>
        <div id="live-pk-bar-host" class="live-pk-bar hidden"></div>
        <div class="live-video-wrap" id="live-broadcast-video-wrap">
          <video id="live-broadcast-v" autoplay playsinline muted></video>
          <div id="live-pk-opponent-host" class="live-pk-opponent hidden">
            <video id="live-pk-opponent-v-host" autoplay playsinline muted></video>
            <img id="live-pk-opponent-img-host" class="hidden" alt="">
          </div>
          <div id="live-gift-floats" class="live-gift-floats"></div>
        </div>
      </div>
    </main>
    <aside class="live-chat-panel">
      <div class="live-chat-top">
        <span id="live-broadcast-viewer-count" class="live-viewer-label">Zuschauer 0</span>
        <div id="live-broadcast-top-gifters" class="live-top-gifters"></div>
      </div>
      <div id="live-broadcast-chat" class="live-chat-messages" role="log" aria-live="polite"></div>
      <div class="live-chat-composer">
        <input id="live-broadcast-chat-input" type="text" maxlength="200" placeholder="Mit Zuschauern chatten…" autocomplete="off">
        <button type="button" id="live-broadcast-chat-send" class="live-chat-send-btn">→</button>
      </div>
    </aside>
  </div>

  <div id="live-watch-screen" class="hidden live-room">
    <aside class="live-suggest-sidebar">
      <div class="live-suggest-title">Live jetzt</div>
      <div id="live-suggest-list"></div>
    </aside>
    <main class="live-stage">
      <header class="live-stage-header">
        <img id="live-host-avatar" class="live-host-avatar" src="" alt="">
        <div class="live-host-meta">
          <strong id="live-host-name"></strong>
          <span id="live-host-title" class="live-host-sub"></span>
        </div>
        <button type="button" id="live-host-follow" class="live-follow-btn hidden">Folgen</button>
        <button type="button" id="live-watch-share" class="profile-icon-btn" aria-label="Teilen">↗</button>
        <button type="button" id="live-watch-close" class="live-room-close" aria-label="Schließen">✕</button>
      </header>
      <div class="live-stage-main">
        <div id="live-pk-bar-watch" class="live-pk-bar hidden"></div>
        <div class="live-video-wrap" id="live-watch-video-wrap">
          <video id="live-watch-v" class="hidden" autoplay playsinline></video>
          <img id="live-watch-img" class="hidden" alt="">
          <div id="live-pk-opponent-watch" class="live-pk-opponent hidden">
            <video id="live-pk-opponent-v-watch" autoplay playsinline muted></video>
            <img id="live-pk-opponent-img-watch" class="hidden" alt="">
          </div>
          <div id="live-watch-connecting" class="live-connecting">Verbinde mit Live…</div>
          <div class="live-badge-top">● LIVE</div>
          <div id="live-watch-gift-floats" class="live-gift-floats"></div>
        </div>
      </div>
      <footer class="live-gift-footer">
        <div id="live-pk-likes" class="live-pk-likes hidden">
          <button type="button" id="live-pk-like-a" class="live-pk-like-btn">❤️ <span id="live-pk-like-a-label">Links</span></button>
          <button type="button" id="live-pk-like-b" class="live-pk-like-btn">❤️ <span id="live-pk-like-b-label">Rechts</span></button>
        </div>
        <span id="live-watch-coins" class="live-coins-hint"></span>
        <div id="live-gift-strip" class="live-gift-strip"></div>
        <div id="live-gift-strip-b" class="live-gift-strip hidden"></div>
        <button type="button" id="live-recharge-btn" class="live-recharge-btn">+ Aufladen</button>
      </footer>
    </main>
    <aside class="live-chat-panel">
      <div class="live-chat-top">
        <span id="live-viewer-count" class="live-viewer-label">Zuschauer 0</span>
        <div id="live-top-gifters" class="live-top-gifters"></div>
      </div>
      <div id="live-chat-messages" class="live-chat-messages" role="log" aria-live="polite"></div>
      <div class="live-chat-composer">
        <button type="button" id="live-chat-emoji" class="live-chat-emoji-btn" title="Emoji">😊</button>
        <input id="live-chat-input" type="text" maxlength="200" placeholder="Sag etwas…" autocomplete="off">
        <button type="button" id="live-chat-send" class="live-chat-send-btn">→</button>
      </div>
    </aside>
  </div>

  <div id="toast"></div>
</div>
<script src="js/vibeloop-api.js?v=__BUILD_ID__"></script>
<script src="js/vibeloop-emoji.js?v=__BUILD_ID__"></script>
<script src="js/vibeloop-sounds.js?v=__BUILD_ID__"></script>
<script src="js/vibeloop-copyright.js?v=__BUILD_ID__"></script>
<script src="js/vibeloop-community.js?v=__BUILD_ID__"></script>
<script src="js/vibeloop-editor.js?v=__BUILD_ID__"></script>
<script src="js/vibeloop-live.js?v=__BUILD_ID__"></script>
<script src="js/app.js?v=__BUILD_ID__"></script>
</body>
</html>"""

CSS = """
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--bg:#000;--accent:#fe2c55;--accent2:#25f4ee;--text:#fff;--muted:rgba(255,255,255,.75);--safe-b:env(safe-area-inset-bottom,0px);--tt-font:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
html,body,#app{height:100%;overflow:hidden;background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Noto Color Emoji','Apple Color Emoji',sans-serif;-webkit-tap-highlight-color:transparent}
.cp-emoji{display:inline-block;vertical-align:middle;object-fit:contain;user-select:none;-webkit-user-drag:none}
.cp-emoji-sm{width:22px!important;height:22px!important}
.cp-emoji-md{width:36px!important;height:36px!important}
.cp-emoji-lg{width:52px!important;height:52px!important}
.cp-emoji-xl{width:72px!important;height:72px!important}
.cp-emoji-glow{filter:drop-shadow(0 2px 8px rgba(255,255,255,.35)) drop-shadow(0 0 16px rgba(254,44,85,.25))}
.cp-emoji-pop{animation:emojiPop .45s cubic-bezier(.34,1.56,.64,1)}
@keyframes emojiPop{0%{transform:scale(.2) rotate(-12deg);opacity:0}70%{transform:scale(1.15) rotate(4deg);opacity:1}100%{transform:scale(1) rotate(0);opacity:1}}
.cp-emoji-float{animation:emojiFloat 2.5s ease-in-out infinite}
@keyframes emojiFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}
.hidden{display:none!important}
#splash{position:fixed;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--bg);z-index:9999;gap:.75rem}
.splash-tagline{color:var(--muted);font-size:1.9rem;margin:0}
.brand-logo{display:block;object-fit:contain;user-select:none;-webkit-user-drag:none}
.brand-logo--splash{width:min(560px,82vw);height:auto;animation:logoPulse 1.8s ease infinite}
.brand-logo--auth{width:min(440px,78vw);height:auto;margin:0 auto 1rem}
@keyframes logoPulse{0%,100%{transform:scale(1);filter:drop-shadow(0 0 0 transparent)}50%{transform:scale(1.03);filter:drop-shadow(0 8px 24px rgba(254,44,85,.25))}}
.landing-screen{position:fixed;inset:0;z-index:9000;overflow-y:auto;background:#fff;color:#161823}
@media(max-width:991px){
  .landing-screen{background:var(--bg);color:var(--text);
    background-image:radial-gradient(ellipse 90% 55% at 50% -15%,rgba(254,44,85,.18),transparent)}
}
.landing-wrap{max-width:1200px;margin:0 auto;padding:1.25rem 1.25rem calc(1.5rem + var(--safe-b));display:grid;gap:1.75rem;align-items:center;min-height:100%;min-height:100dvh}
@media(min-width:820px){.landing-wrap{grid-template-columns:1.1fr .9fr;gap:2.5rem;padding:2rem 2rem 2.5rem}}
.landing-hero{text-align:left}
.brand-logo--landing{width:min(400px,78vw);height:auto;margin-bottom:1rem}
.landing-kicker{font-size:1.56rem;text-transform:uppercase;letter-spacing:.08em;color:var(--accent);margin:0 0 .65rem;font-weight:700}
.landing-title{font-size:clamp(3.3rem,10vw,4.7rem);line-height:1.12;margin:0 0 1.25rem;font-weight:800}
.landing-lead{color:#d1d5db;font-size:1.9rem;line-height:1.5;margin:0 0 1.75rem;max-width:42rem}
.landing-lead strong{color:#fff}
.landing-features{list-style:none;margin:0;padding:0;display:grid;gap:.85rem}
.landing-features li{display:flex;align-items:flex-start;gap:.85rem;font-size:1.76rem;color:var(--muted);line-height:1.35}
.landing-feat-icon{flex-shrink:0;font-size:2.2rem;line-height:1.2}
.landing-auth{display:flex;justify-content:center}
.landing-auth-card{width:100%;max-width:400px;background:#12121a;border:1px solid #2a2a35;border-radius:20px;padding:1.15rem 1.15rem 1rem;box-shadow:0 18px 50px rgba(0,0,0,.45)}
.auth-tabs{display:grid;grid-template-columns:1fr 1fr;gap:.4rem;margin-bottom:.85rem;background:#0a0a0f;border-radius:12px;padding:.3rem}
.auth-tab{border:none;background:transparent;color:var(--muted);padding:.55rem .5rem;border-radius:10px;font-weight:700;font-size:.88rem;cursor:pointer}
.auth-tab.active{background:var(--accent);color:#fff}
.auth-panel-hint{font-size:.82rem;color:var(--muted);margin:0 0 .85rem;line-height:1.4;min-height:2.4em}
.auth-label{display:block;font-size:.78rem;color:var(--muted);margin-bottom:.35rem;text-align:left}
.landing-auth-card input,.landing-auth-card select.auth-gender{width:100%;padding:.85rem 1rem;margin-bottom:.75rem;border:1px solid #333;border-radius:12px;background:#16161f;color:var(--text);font-size:1rem}
.landing-legal{font-size:.7rem;color:#6b7280;margin:.65rem 0 0;line-height:1.35;text-align:center}
.tagline{color:var(--muted);margin-bottom:1.5rem}
.btn-primary{width:100%;padding:.9rem;border:none;border-radius:12px;background:var(--accent);color:#fff;font-size:1rem;font-weight:600;cursor:pointer;margin-bottom:.5rem}
.btn-ghost{width:100%;padding:.75rem;border:1px solid #444;border-radius:12px;background:transparent;color:var(--muted);cursor:pointer}
.btn-danger{padding:.75rem 1.5rem;border:none;border-radius:12px;background:#c00;color:#fff;cursor:pointer}
.error{color:var(--accent);font-size:.85rem;margin-top:.5rem;min-height:1.2em}
.niche-screen{position:fixed;inset:0;z-index:8000;display:flex;flex-direction:column;background:#08080d;
  background-image:radial-gradient(ellipse 70% 50% at 15% -5%,rgba(254,44,85,.2),transparent),radial-gradient(ellipse 55% 40% at 95% 5%,rgba(37,244,238,.08),transparent)}
.niche-head{flex-shrink:0;padding:calc(env(safe-area-inset-top,0px) + 1rem) 1rem .5rem}
.niche-title{margin:0;font-size:1.55rem;font-weight:800;line-height:1.2;letter-spacing:-.02em}
.niche-sub{margin:.45rem 0 .85rem;color:#9ca3af;font-size:.9rem;line-height:1.45}
.niche-progress{display:flex;gap:.55rem;margin-bottom:1rem}
.niche-dot{width:11px;height:11px;border-radius:50%;background:#2a2a36;border:2px solid #3a3a48;transition:all .22s ease}
.niche-dot.on{background:var(--accent);border-color:var(--accent);box-shadow:0 0 14px rgba(254,44,85,.55);transform:scale(1.08)}
.niche-search-box{display:flex;align-items:center;gap:.6rem;padding:.72rem 1rem;border-radius:16px;border:1px solid rgba(255,255,255,.08);
  background:rgba(255,255,255,.04);backdrop-filter:blur(8px);margin-bottom:.85rem}
.niche-search-icon{font-size:1rem;opacity:.55}
.niche-search-box input{flex:1;border:none;background:transparent;color:var(--text);font-size:1rem;outline:none;min-width:0}
.niche-search-box input::placeholder{color:#6b7280}
.niche-cat-rail{display:flex;gap:.55rem;overflow-x:auto;padding:.15rem .1rem .35rem;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.niche-cat-rail::-webkit-scrollbar{display:none}
.niche-cat-btn{flex:0 0 auto;width:52px;height:52px;border-radius:16px;border:2px solid rgba(255,255,255,.08);background:rgba(255,255,255,.04);
  font-size:1.45rem;line-height:1;cursor:pointer;transition:transform .15s,border-color .15s,background .15s,box-shadow .15s}
.niche-cat-btn.active{border-color:var(--accent);background:rgba(254,44,85,.18);box-shadow:0 8px 22px rgba(254,44,85,.22)}
.niche-cat-btn:active{transform:scale(.94)}
.niche-scroll{flex:1;overflow-y:auto;padding:.35rem 1rem 6rem;-webkit-overflow-scrolling:touch}
.niche-card-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.75rem}
@media(min-width:520px){.niche-card-grid{grid-template-columns:repeat(3,1fr);gap:.85rem}}
.niche-card{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:118px;padding:.85rem .65rem;
  border-radius:20px;border:2px solid rgba(255,255,255,.07);background:linear-gradient(160deg,rgba(255,255,255,.06),rgba(255,255,255,.02));
  color:#f3f4f6;text-align:center;cursor:pointer;transition:transform .18s,border-color .18s,box-shadow .18s,background .18s}
.niche-card:hover{border-color:rgba(255,255,255,.16);background:linear-gradient(160deg,rgba(255,255,255,.09),rgba(255,255,255,.03))}
.niche-card.selected{border-color:var(--accent);background:linear-gradient(160deg,rgba(254,44,85,.22),rgba(37,244,238,.06));
  box-shadow:0 10px 28px rgba(254,44,85,.2),inset 0 0 0 1px rgba(254,44,85,.25)}
.niche-card:active{transform:scale(.97)}
.niche-card.hidden-niche{display:none}
.niche-card-check{position:absolute;top:.55rem;right:.55rem;width:22px;height:22px;border-radius:50%;background:var(--accent);color:#fff;
  font-size:.72rem;font-weight:800;line-height:22px;text-align:center;opacity:0;transform:scale(.6);transition:opacity .18s,transform .18s}
.niche-card.selected .niche-card-check{opacity:1;transform:scale(1)}
.niche-card-emoji{font-size:2.15rem;line-height:1;filter:drop-shadow(0 4px 10px rgba(0,0,0,.35))}
.niche-card-label{font-size:.86rem;font-weight:700;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.niche-empty{margin:2rem .5rem 0;text-align:center;color:#6b7280;font-size:.92rem;line-height:1.5}
.niche-bottom{flex-shrink:0;padding:.75rem 1rem calc(.75rem + var(--safe-b));border-top:1px solid rgba(255,255,255,.06);
  background:rgba(8,8,13,.92);backdrop-filter:blur(12px)}
.niche-save-btn{width:100%;padding:1rem;border:none;border-radius:18px;background:#2a2a36;color:#8b8fa0;
  font-size:1.05rem;font-weight:800;cursor:pointer;transition:background .2s,color .2s,box-shadow .2s}
.niche-save-btn:not(:disabled){background:linear-gradient(135deg,var(--accent),#ff4d6d);color:#fff;box-shadow:0 14px 34px rgba(254,44,85,.32)}
.tt-sidebar{position:fixed;left:0;top:0;bottom:0;width:240px;z-index:300;display:flex;flex-direction:column;padding:16px 10px 20px;background:#fff;border-right:1px solid rgba(22,24,35,.12);color:#161823}
.tt-side-logo{display:block;padding:8px 12px 16px}
.tt-side-logo img{height:28px;width:auto;display:block;object-fit:contain}
.tt-side-search{display:flex;align-items:center;gap:8px;margin:0 8px 12px;padding:10px 12px;border-radius:999px;background:#f1f1f2;color:#8a8b91}
.tt-side-search input{flex:1;border:none;background:transparent;color:#161823;font-size:15px;outline:none;min-width:0}
.tt-side-search input::placeholder{color:#8a8b91}
.tt-side-nav{display:flex;flex-direction:column;gap:2px;flex:1;overflow-y:auto}
.tt-side-item{display:flex;align-items:center;gap:14px;width:100%;padding:10px 14px;border:none;border-radius:8px;background:transparent;color:#161823;font-size:18px;font-weight:600;cursor:pointer;text-align:left}
.tt-side-item svg{flex-shrink:0;color:#161823}
.tt-side-item:hover{background:#f8f8f8}
.tt-side-item.active{color:#fe2c55}
.tt-side-item.active svg{color:#fe2c55}
.tt-side-footer{margin-top:auto;padding:12px 14px 0;font-size:12px;color:#8a8b91}
body.tt-logged-in .tt-page{left:0}
@media(min-width:992px){
  body.tt-logged-in .tt-page{left:240px;width:calc(100% - 240px)}
  body.tt-logged-in .tt-sidebar{display:flex}
  .tt-mobile-only{display:none!important}
  .tt-desktop-only{display:flex!important}
}
@media(max-width:991px){
  .tt-sidebar{display:none!important}
  .tt-desktop-only{display:none!important}
  body.tt-logged-in .tt-page{left:0;width:100%}
}
.tt-page{position:fixed;inset:0;display:flex;flex-direction:column;background:#fff;color:#161823}
.tt-app{background:#000}
@media(min-width:992px){.tt-app{background:#fff}}
#main-screen{position:fixed;inset:0;display:flex;flex-direction:column}
.tt-top-bar{position:absolute;top:0;left:0;right:0;z-index:100;display:grid;grid-template-columns:64px 1fr 44px;align-items:center;padding:calc(env(safe-area-inset-top,10px) + 6px) .65rem 10px;pointer-events:none;background:linear-gradient(180deg,rgba(0,0,0,.45),transparent)}
.tt-top-bar>*{pointer-events:auto}
.tt-live-btn{justify-self:start;padding:.28rem .55rem;border:1.5px solid rgba(255,255,255,.85);border-radius:4px;background:transparent;color:#fff;font-size:.72rem;font-weight:800;letter-spacing:.04em;cursor:pointer}
.tt-feed-tabs{display:flex;justify-content:center;gap:1.15rem}
.tab{background:none;border:none;color:rgba(255,255,255,.55);font-size:1.05rem;font-weight:700;cursor:pointer;padding:.35rem .15rem;position:relative;font-family:var(--tt-font)}
.tab.active{color:#fff}
.tab.active::after{content:'';position:absolute;left:50%;bottom:0;width:22px;height:3px;margin-left:-11px;background:#fff;border-radius:2px}
.tt-top-search{justify-self:end;width:40px;height:40px;border:none;border-radius:50%;background:transparent;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center}
.tt-stage{flex:1;display:flex;align-items:center;justify-content:center;position:relative;min-height:0;background:#000}
@media(min-width:992px){.tt-stage{background:#fff}}
.tt-player-column{display:flex;align-items:flex-end;justify-content:center;gap:20px;max-width:100%;max-height:100%;padding:16px 0}
.tt-player-frame{position:relative;flex-shrink:0;background:#000;overflow:hidden;border-radius:0;width:100%;height:100%}
@media(min-width:992px){
  .tt-player-frame{width:auto;height:calc(100vh - 32px);max-height:calc(100vh - 32px);aspect-ratio:9/16;max-width:min(692px,calc((100vh - 32px) * 9 / 16));border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,.06)}
}
#feed-container,.tt-feed{width:100%;height:100%;overflow-y:scroll;scroll-snap-type:y mandatory;-webkit-overflow-scrolling:touch;background:#000}
.feed-item{position:relative;width:100%;height:100%;min-height:100%;scroll-snap-align:start;scroll-snap-stop:always;background:#000}
@media(max-width:991px){.feed-item{height:100vh;height:100dvh}}
.feed-item video,.feed-item img{width:100%;height:100%;object-fit:cover}
.feed-media-wrap{position:absolute;inset:0}
.tt-scroll-btns{position:absolute;right:24px;top:50%;transform:translateY(-50%);flex-direction:column;gap:8px;z-index:60}
.tt-scroll-btn{width:40px;height:40px;border-radius:50%;border:1px solid rgba(22,24,35,.12);background:#fff;color:#161823;font-size:14px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.08)}
.tt-scroll-btn:hover{background:#f8f8f8}
.feed-ad{position:relative;width:100%;height:100%;min-height:100%;scroll-snap-align:start;scroll-snap-stop:always;background:#000;display:flex;flex-direction:column;align-items:center;justify-content:center}
@media(max-width:991px){.feed-ad{height:100vh;height:100dvh}}
.feed-ad iframe{width:100%;height:100%;border:0}
.feed-ad-label{position:absolute;top:calc(env(safe-area-inset-top,12px) + 12px);left:1rem;background:rgba(0,0,0,.7);padding:.35rem .75rem;border-radius:8px;font-size:.75rem;color:var(--muted);z-index:2}
.feed-ad-skip{position:absolute;bottom:calc(90px + var(--safe-b));right:1rem;z-index:2;padding:.6rem 1rem;border:none;border-radius:20px;background:rgba(255,255,255,.2);color:#fff;font-weight:600;cursor:pointer;backdrop-filter:blur(8px)}
.feed-ad-skip:disabled{opacity:.5;cursor:default}
.premium-badge{display:inline-block;background:linear-gradient(135deg,#ffd700,#ff8c00);color:#000;font-size:.7rem;font-weight:700;padding:.15rem .5rem;border-radius:6px;margin-left:.4rem}
#premium-panel{background:#16161f;border-radius:12px;padding:1rem;margin:1rem 0}
#premium-panel ul{margin:.75rem 0 .75rem 1.2rem;font-size:.9rem;color:var(--muted)}
#premium-panel li{margin:.35rem 0}
.tt-feed-info{position:absolute;left:0;right:72px;bottom:calc(58px + var(--safe-b));padding:0 12px 10px;z-index:4;
  background:linear-gradient(180deg,transparent,rgba(0,0,0,.35) 30%,rgba(0,0,0,.55));pointer-events:none}
.tt-feed-info *{pointer-events:auto}
.tt-feed-user-row{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}
.tt-feed-username{font-weight:700;font-size:16px;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.5)}
.tt-feed-follow-btn{padding:5px 14px;border:none;border-radius:4px;background:var(--accent);color:#fff;font-size:13px;font-weight:700;cursor:pointer}
.tt-feed-caption{font-size:14px;line-height:1.35;color:#fff;margin-bottom:10px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-shadow:0 1px 2px rgba(0,0,0,.45)}
.tt-feed-tags{font-size:14px;color:#fff;opacity:.95;margin-bottom:8px}
.tt-feed-sound{display:flex;align-items:center;gap:8px;overflow:hidden;max-width:100%}
.tt-sound-icon{font-size:13px;color:#fff;flex-shrink:0}
.tt-sound-marquee{overflow:hidden;white-space:nowrap;flex:1;min-width:0}
.tt-sound-marquee span{display:inline-block;font-size:13px;color:#fff;animation:ttMarquee 12s linear infinite}
@keyframes ttMarquee{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}
.feed-author,.feed-caption,.feed-tags{display:none}
.verified{color:var(--accent2)}
.tt-action-bar{position:absolute;right:6px;bottom:calc(52px + var(--safe-b));z-index:50;display:flex;flex-direction:column;align-items:center}
@media(min-width:992px){.tt-action-bar{position:static;bottom:auto;right:auto;padding-bottom:72px}}
.tt-action-stack{display:flex;flex-direction:column;align-items:center;gap:20px}
.tt-action{background:none;border:none;color:#fff;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:4px;padding:0;font-family:var(--tt-font)}
.tt-action span{font-size:12px;font-weight:600;text-shadow:0 1px 3px rgba(0,0,0,.65)}
@media(min-width:992px){.tt-action{color:#161823}.tt-action span{color:#161823;text-shadow:none;font-size:13px}}
.tt-action:active{transform:scale(.9)}
.tt-action--liked .tt-svg{color:var(--accent);filter:drop-shadow(0 0 6px rgba(254,44,85,.6))}
.tt-action--saved .tt-svg{color:var(--accent)}
.tt-action--extra{opacity:.85}
.tt-svg{width:34px;height:34px;display:block;filter:drop-shadow(0 2px 4px rgba(0,0,0,.55))}
@media(min-width:992px){.tt-svg{width:32px;height:32px;filter:none;color:#161823}}
.tt-action-avatar-wrap{position:relative;margin-bottom:4px}
.tt-action-avatar{width:48px;height:48px;border-radius:50%;border:2px solid #fff;padding:0;overflow:hidden;background:#222;cursor:pointer}
.tt-action-avatar img{width:100%;height:100%;object-fit:cover;display:block}
.tt-action-follow{position:absolute;left:50%;bottom:-8px;transform:translateX(-50%);width:22px;height:22px;border-radius:50%;border:none;background:var(--accent);color:#fff;font-size:16px;font-weight:700;line-height:22px;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,.4)}
.tt-disc{width:40px;height:40px;border-radius:50%;border:8px solid #222;border-top-color:#fff;border-right-color:rgba(255,255,255,.35);animation:ttSpin 4s linear infinite;margin-top:4px;overflow:hidden;background:#111}
.tt-disc img{width:100%;height:100%;object-fit:cover;border-radius:50%}
@keyframes ttSpin{to{transform:rotate(360deg)}}
.action-btn,.action-icon,.avatar-ring{display:none}
.tt-bottom-nav{position:absolute;bottom:0;left:0;right:0;z-index:100;display:flex;justify-content:space-around;align-items:flex-end;padding:6px 8px calc(4px + var(--safe-b));background:#000}
.tt-bottom-nav button{background:none;border:none;color:rgba(255,255,255,.55);cursor:pointer;padding:6px 10px;display:flex;align-items:center;justify-content:center;min-width:48px;min-height:44px}
.tt-bottom-nav button.active{color:#fff}
.tt-nav-svg{width:26px;height:26px;display:block}
.tt-create-btn{padding:0 4px 2px!important;min-width:56px!important;background:transparent!important}
.tt-create-outer{display:flex;align-items:center;justify-content:center;width:48px;height:30px;border-radius:8px;background:linear-gradient(135deg,#25f4ee,#fe2c55);padding:2px}
.tt-create-inner{display:flex;align-items:center;justify-content:center;width:100%;height:100%;border-radius:6px;background:#000;color:#fff;font-size:22px;font-weight:300;line-height:1}
.tt-sheet .modal-inner{background:#fff;border-radius:12px 12px 0 0;padding:0;max-height:75vh}
.tt-comment-sheet{display:flex;flex-direction:column;max-height:75vh}
.tt-sheet-handle{width:40px;height:4px;border-radius:2px;background:#d9d9d9;margin:10px auto 8px}
.tt-comment-sheet h3{font-size:16px;font-weight:700;text-align:center;margin:0;padding:12px 16px;border-bottom:1px solid #f1f1f2;color:#161823}
.tt-comment-list{flex:1;overflow-y:auto;padding:8px 16px;min-height:200px}
.tt-comment-compose{display:flex;align-items:center;gap:8px;padding:12px 16px calc(12px + var(--safe-b));border-top:1px solid #f1f1f2;background:#fff}
.tt-comment-compose input{flex:1;margin:0;padding:10px 14px;border:1px solid #e8e8e8;border-radius:999px;background:#f1f1f2;color:#161823;font-size:15px}
.tt-comment-send{border:none;background:transparent;color:#fe2c55;font-size:15px;font-weight:700;cursor:pointer;white-space:nowrap;padding:8px}
.tt-comment-send:disabled{color:#b0b0b0}
@media(max-width:991px){
  .tt-sheet .modal-inner{background:#121212}
  .tt-comment-sheet h3{color:#fff;border-bottom-color:#2a2a2a}
  .tt-comment-compose{background:#121212;border-top-color:#2a2a2a}
  .tt-comment-compose input{background:#2a2a2a;border-color:#333;color:#fff}
}
.modal{position:fixed;inset:0;z-index:5000;background:rgba(0,0,0,.85);display:flex;align-items:flex-end;justify-content:center}
.modal-inner{width:100%;max-width:480px;background:#16161f;border-radius:20px 20px 0 0;padding:1.5rem;max-height:85vh;overflow-y:auto}
.modal textarea,.modal input[type=text],#comment-input{width:100%;padding:.75rem;margin:.75rem 0;border:1px solid #333;border-radius:10px;background:#0a0a0f;color:#fff}
.safety-actions{display:flex;flex-direction:column;gap:.5rem;margin:.75rem 0}
.safety-actions button{width:100%;padding:.85rem;border-radius:12px;font-weight:600;cursor:pointer;border:1px solid #333;background:#16161f;color:#fff}
.safety-actions button.danger{border-color:#a00;background:rgba(170,0,0,.2);color:#ff6b6b}
.report-reasons{display:flex;flex-direction:column;gap:.4rem;margin:.75rem 0;max-height:40vh;overflow-y:auto}
.report-reasons label{display:flex;align-items:center;gap:.6rem;padding:.65rem .75rem;border:1px solid #333;border-radius:10px;background:#0a0a0f;cursor:pointer;font-size:.9rem}
.report-reasons input{accent-color:var(--accent)}
.comment-head{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
.comment-report{border:none;background:transparent;color:#888;font-size:1.1rem;cursor:pointer;padding:.15rem .35rem}
.comment-body{font-size:.9rem;line-height:1.4}
.blocked-list{display:flex;flex-direction:column;gap:.5rem;margin:.5rem 0 1rem;max-height:200px;overflow-y:auto}
.blocked-item{display:flex;align-items:center;gap:.65rem;padding:.5rem;border:1px solid #333;border-radius:10px;background:#0a0a0f}
.blocked-item img{width:36px;height:36px;border-radius:50%;object-fit:cover}
.blocked-item .blocked-name{flex:1;font-size:.85rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.blocked-item button{padding:.35rem .65rem;border:1px solid #555;border-radius:8px;background:transparent;color:#ccc;font-size:.75rem;cursor:pointer}
.feed-more-btn{position:absolute;top:calc(env(safe-area-inset-top,12px) + 8px);right:4.5rem;z-index:3;width:36px;height:36px;border:none;border-radius:50%;background:rgba(0,0,0,.45);color:#fff;font-size:1.1rem;cursor:pointer;backdrop-filter:blur(6px)}
#gift-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.65rem;margin:1rem 0}
.gift-item{padding:.85rem .5rem;border:1px solid rgba(255,255,255,.08);border-radius:16px;text-align:center;cursor:pointer;background:linear-gradient(160deg,rgba(255,255,255,.06),rgba(255,255,255,.02));transition:transform .2s,border-color .2s,box-shadow .2s}
.gift-item:hover,.gift-item:active{border-color:var(--accent2);transform:scale(1.04);box-shadow:0 8px 24px rgba(37,244,238,.15)}
.gift-item-label{font-size:.72rem;color:#ddd;margin-top:.35rem;font-weight:600}
.gift-item-coins{font-size:.68rem;color:var(--accent2);margin-top:.15rem}
.gift-item-emoji-wrap{display:flex;align-items:center;justify-content:center;min-height:76px}
.tt-profile{background:#fff;color:#161823}
@media(max-width:991px){.tt-profile{background:#000;color:#fff}}
.tt-inbox{background:#fff;color:#161823}
@media(max-width:991px){.tt-inbox{background:#000;color:#fff}}
#profile-screen{position:fixed;inset:0;z-index:200;background:inherit;overflow-y:auto;display:flex;flex-direction:column}
.profile-btn-follow{border-radius:4px;background:#fe2c55}
.profile-top-bar{display:flex;align-items:center;justify-content:space-between;padding:.65rem 1rem;border-bottom:1px solid #f1f1f2;position:sticky;top:0;background:inherit;z-index:10;color:inherit}
@media(max-width:991px){.profile-top-bar{border-bottom-color:#1a1a22}}
.profile-top-title{font-weight:700;font-size:1rem;flex:1;text-align:center}
.profile-icon-btn{width:40px;height:40px;border:none;border-radius:50%;background:transparent;color:#fff;font-size:1.35rem;cursor:pointer;display:flex;align-items:center;justify-content:center}
.profile-icon-btn:active{background:rgba(255,255,255,.08)}
.profile-body{flex:1;padding:0 1rem 5rem;max-width:1200px;width:100%;margin:0 auto}
#profile-header{padding:1.25rem 0 .75rem}
.profile-hero{display:flex;gap:1.25rem;align-items:flex-start}
.profile-hero-left{flex-shrink:0}
.profile-hero-right{flex:1;min-width:0;padding-top:.25rem}
.profile-display-name{font-size:1.35rem;font-weight:700;margin:0 0 .15rem;line-height:1.2}
.profile-username{color:var(--muted);font-size:.9rem;margin:0 0 .75rem}
.profile-stats{display:flex;gap:1.25rem;margin-bottom:.85rem;flex-wrap:wrap}
.profile-stat{text-align:center;min-width:52px}
.profile-stat strong{display:block;font-size:1.05rem;font-weight:700;color:#fff}
.profile-stat span{display:block;font-size:.72rem;color:var(--muted);margin-top:.1rem}
.profile-actions{display:flex;gap:.5rem;align-items:center;flex-wrap:wrap}
.profile-btn-edit{flex:1;min-width:140px;padding:.55rem 1rem;border:1px solid #444;border-radius:8px;background:#16161f;color:#fff;font-weight:600;font-size:.85rem;cursor:pointer}
.profile-btn-edit:active{background:#222}
.profile-btn-follow{flex:1;min-width:140px;padding:.55rem 1rem;border:none;border-radius:8px;background:var(--accent);color:#fff;font-weight:700;font-size:.85rem;cursor:pointer}
.profile-btn-follow.following{background:#333;border:1px solid #555}
.profile-bio{font-size:.9rem;line-height:1.45;color:#eee;margin:.75rem 0 .35rem;white-space:pre-wrap;word-break:break-word}
.profile-link{display:inline-block;color:var(--accent2);font-size:.85rem;word-break:break-all;text-decoration:none;margin-bottom:.5rem}
.profile-link:hover{text-decoration:underline}
.premium-badge{display:inline-block;font-size:.65rem;font-weight:700;background:linear-gradient(135deg,#ffd700,#ff8c00);color:#000;padding:.15rem .45rem;border-radius:6px;vertical-align:middle;margin-left:.35rem}
.pe-label{display:block;font-size:.75rem;color:var(--muted);margin:.5rem 0 .25rem}
#profile-edit-modal input,#profile-edit-modal textarea{width:100%;padding:.65rem;border:1px solid #333;border-radius:10px;background:#0a0a0f;color:#fff;font-size:.9rem}
#profile-edit-modal textarea{resize:vertical;min-height:88px}
.profile-avatar-wrap{position:relative;width:116px;height:116px}
.profile-avatar-wrap img{width:116px;height:116px;border-radius:50%;object-fit:cover;border:2px solid rgba(255,255,255,.12);background:#222}
.profile-avatar-wrap.has-intro img{border-color:var(--accent2);cursor:pointer}
.profile-avatar-play{position:absolute;bottom:4px;right:4px;width:34px;height:34px;border-radius:50%;background:var(--accent);border:2px solid #fff;display:flex;align-items:center;justify-content:center;font-size:.8rem;box-shadow:0 2px 8px rgba(0,0,0,.4)}
#profile-intro{margin:0 0 1rem}
.profile-toolbar{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #222;margin-bottom:0;gap:.5rem;flex-wrap:wrap}
@media(min-width:720px){.profile-avatar-wrap,.profile-avatar-wrap img{width:140px;height:140px}.profile-display-name{font-size:1.6rem}#profile-grid{grid-template-columns:repeat(6,1fr)!important}}
.intro-card{position:relative;border-radius:16px;overflow:hidden;background:#16161f;aspect-ratio:9/16;max-height:320px;margin:0 auto;max-width:200px}
.intro-card video,.intro-card img{width:100%;height:100%;object-fit:cover}
.intro-card-play{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.35);font-size:2.5rem;cursor:pointer;border:none;color:#fff}
.intro-actions{display:flex;gap:.5rem;justify-content:center;margin-top:.75rem;flex-wrap:wrap}
.intro-actions button{width:auto;padding:.6rem 1rem;font-size:.85rem}
.intro-locked{text-align:center;color:var(--muted);font-size:.85rem;padding:1rem;background:#16161f;border-radius:12px}
.intro-player-inner{max-width:100%;padding:0;background:#000}
.intro-player-inner video{width:100%;max-height:70vh;border-radius:12px 12px 0 0}
#profile-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:2px;margin-top:0}
#profile-grid .thumb{aspect-ratio:9/16;background:#16161f;overflow:hidden;cursor:pointer;position:relative}
#profile-grid .thumb img,#profile-grid .thumb video{width:100%;height:100%;object-fit:cover;display:block}
.thumb-views{position:absolute;bottom:6px;left:6px;font-size:.72rem;font-weight:600;color:#fff;text-shadow:0 1px 3px #000;display:flex;align-items:center;gap:.2rem;z-index:2}
.thumb-pin{position:absolute;top:4px;left:4px;font-size:.65rem;background:rgba(0,0,0,.65);padding:2px 6px;border-radius:6px;z-index:2}
#profile-tier{background:#16161f;border-radius:12px;padding:1rem;margin:1rem 0;font-size:.85rem;color:var(--muted)}
#premium-panel h4,#earnings-panel h4{margin:1rem 0 .5rem;font-size:.95rem}
.profile-sort{display:flex;gap:.35rem;font-size:.72rem}
.profile-sort button{background:none;border:none;color:var(--muted);cursor:pointer;padding:.35rem .5rem;font-weight:600}
.profile-sort button.active{color:#fff}
#explore-screen{position:fixed;inset:0;z-index:180;background:var(--bg);display:flex;flex-direction:column;overflow:hidden}
.tt-discover{background:#fff}
@media(max-width:991px){.tt-discover{background:#000}}
.explore-header{display:flex;align-items:center;gap:.5rem;padding:.65rem 1rem;border-bottom:1px solid #f1f1f2;flex-shrink:0;background:#fff;z-index:5;color:#161823}
@media(max-width:991px){.explore-header{background:#000;color:#fff;border-bottom-color:#1a1a22}}
.explore-title{color:inherit}
.explore-search{background:#f1f1f2;border:none;color:#161823}
@media(max-width:991px){.explore-search{background:#16161f;color:#fff}}
.explore-title{font-size:1.05rem;font-weight:700;margin:0;flex-shrink:0}
.explore-search{flex:1;max-width:280px;margin-left:auto;padding:.5rem .75rem;border:1px solid #333;border-radius:999px;background:#16161f;color:#fff;font-size:.85rem}
.explore-search::placeholder{color:var(--muted)}
.explore-categories{display:flex;gap:.4rem;padding:.6rem 1rem;overflow-x:auto;flex-shrink:0;border-bottom:1px solid #1a1a22;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.explore-categories::-webkit-scrollbar{display:none}
.explore-categories button{flex:0 0 auto;padding:.4rem .85rem;border:1px solid #333;border-radius:999px;background:#16161f;color:#ccc;font-size:.78rem;font-weight:600;cursor:pointer;white-space:nowrap;transition:.15s}
.explore-categories button.active{background:var(--accent);border-color:var(--accent);color:#fff}
.explore-categories button:hover:not(.active){border-color:#555;color:#fff}
.explore-grid{flex:1;overflow-y:auto;padding:2px;display:grid;grid-template-columns:repeat(3,1fr);gap:2px;align-content:start}
.explore-card{cursor:pointer;background:#16161f}
.explore-thumb{position:relative;aspect-ratio:9/16;background:#111;overflow:hidden}
.explore-thumb img,.explore-thumb video{width:100%;height:100%;object-fit:cover;display:block}
.explore-views{position:absolute;bottom:6px;left:6px;font-size:.7rem;font-weight:700;color:#fff;text-shadow:0 1px 3px #000;display:flex;align-items:center;gap:.2rem}
.explore-meta{display:flex;align-items:center;gap:.35rem;padding:.4rem .35rem .5rem;min-width:0}
.explore-author-avatar{width:20px;height:20px;border-radius:50%;object-fit:cover;flex-shrink:0;background:#333}
.explore-author-name{font-size:.68rem;color:#ddd;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.explore-verified{color:#3b82f6;font-size:.6rem;margin-left:.1rem}
.explore-status{text-align:center;padding:1rem;color:var(--muted);font-size:.8rem}
.explore-empty{grid-column:1/-1;text-align:center;padding:3rem 1rem;color:var(--muted)}
@media(min-width:720px){.explore-grid{grid-template-columns:repeat(4,1fr);padding:4px;gap:3px}}
@media(min-width:1024px){.explore-grid{grid-template-columns:repeat(6,1fr);max-width:1400px;margin:0 auto;width:100%}}
.studio-app{position:fixed;inset:0;z-index:150;background:#0d0d12;display:flex;flex-direction:column;overflow:hidden}
.studio-layout{flex:1;display:flex;min-height:0;overflow:hidden}
.studio-left{width:240px;flex-shrink:0;background:#12121a;border-right:1px solid #1e1e28;display:flex;flex-direction:column;overflow-y:auto;padding:.65rem .5rem}
.studio-left-head{display:flex;align-items:center;gap:.45rem;padding:.35rem .25rem .75rem;border-bottom:1px solid #222;margin-bottom:.5rem}
.studio-back-btn{width:32px;height:32px;border:none;border-radius:8px;background:#1e1e28;color:#fff;font-size:1.2rem;cursor:pointer;flex-shrink:0}
.studio-brand{font-size:.88rem;font-weight:800;margin:0;line-height:1.2}
.studio-badge{display:inline-block;font-size:.58rem;font-weight:700;padding:.12rem .4rem;border-radius:4px;background:linear-gradient(135deg,#ff2d55,#fe2c55);color:#fff;margin-top:.15rem}
.studio-panel{margin-bottom:.5rem;background:#16161f;border-radius:10px;padding:.55rem .6rem;border:1px solid #222}
.studio-panel-grow{flex:1;min-height:0;overflow-y:auto}
.studio-panel-head{display:flex;align-items:center;justify-content:space-between;font-size:.72rem;font-weight:700;color:#aaa;margin-bottom:.45rem;text-transform:uppercase;letter-spacing:.03em}
.studio-icon-btn{width:22px;height:22px;border:none;border-radius:6px;background:#222;color:#fff;font-size:.85rem;cursor:pointer}
.studio-orient{display:flex;gap:.2rem}
.studio-orient button{padding:.25rem .45rem;border:1px solid #333;border-radius:6px;background:#0d0d12;color:#888;font-size:.62rem;font-weight:600;cursor:pointer}
.studio-orient button.active{background:#2a1520;border-color:var(--accent);color:#fff}
.studio-scene-active{display:flex;align-items:center;gap:.4rem;font-size:.78rem;padding:.35rem .4rem;background:#0d0d12;border-radius:8px}
.studio-scene-dot{width:8px;height:8px;border-radius:50%;background:#4ade80}
.studio-sources{list-style:none;margin:0;padding:0}
.studio-sources li{margin-bottom:.25rem}
.studio-source{display:flex;align-items:center;gap:.45rem;width:100%;padding:.4rem .45rem;border:none;border-radius:8px;background:transparent;color:#ccc;font-size:.75rem;cursor:pointer;text-align:left}
.studio-source.active{background:rgba(254,44,85,.15);color:#fff;border:1px solid rgba(254,44,85,.3)}
.studio-source.locked{opacity:.45;cursor:default;padding:.4rem .45rem;display:flex}
.studio-src-icon{width:22px;text-align:center;font-size:.85rem;flex-shrink:0}
.studio-tools-label{font-size:.68rem;color:var(--muted);margin:.65rem 0 .35rem;font-weight:600}
.studio-tools-grid{display:grid;grid-template-columns:1fr 1fr;gap:.3rem}
.studio-tool{font-size:.65rem;padding:.35rem .4rem;background:#0d0d12;border:1px solid #2a2a35;border-radius:6px;color:#666;text-align:center}
.studio-tool.locked{opacity:.55}
.studio-tier-box{margin-top:auto;background:#16161f;border-radius:10px;padding:.65rem;font-size:.72rem;color:var(--muted);border:1px solid #222}
.studio-tier-bar{height:5px;background:#222;border-radius:999px;margin:.4rem 0 .3rem;overflow:hidden}
.studio-tier-fill{height:100%;background:linear-gradient(90deg,var(--accent),var(--accent2));border-radius:999px}
.studio-tier-ok{color:#4ade80;font-weight:600}
.studio-center{flex:1;display:flex;flex-direction:column;min-width:0;background:#0a0a0f;padding:.75rem}
.studio-preview-wrap{position:relative;flex:1;min-height:0;margin:0 auto;width:100%;max-width:100%;background:#000;border-radius:12px;overflow:hidden;border:1px solid #2a2a35}
.studio-preview--portrait{max-width:320px;aspect-ratio:9/16}
.studio-preview--landscape{max-width:100%;aspect-ratio:16/9;flex:none;height:auto;max-height:55vh}
.studio-preview-wrap video{width:100%;height:100%;object-fit:cover;display:block}
.studio-preview-placeholder{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--muted);gap:.35rem}
.studio-preview-icon{font-size:2.2rem;opacity:.5}
.studio-preview-placeholder p{margin:0;font-size:.8rem}
.studio-live-pill{position:absolute;top:.5rem;left:.5rem;background:rgba(255,0,0,.92);color:#fff;padding:.25rem .55rem;border-radius:6px;font-weight:700;font-size:.68rem;z-index:3}
.studio-preview-overlay{position:absolute;bottom:12%;left:50%;transform:translateX(-50%);font-size:1.1rem;font-weight:800;color:#fff;text-shadow:0 2px 12px #000;pointer-events:none;z-index:2;white-space:nowrap}
.studio-controls{flex-shrink:0;padding-top:.65rem;display:flex;flex-direction:column;gap:.55rem}
.studio-src-quick{display:flex;gap:.35rem}
.studio-quick-btn{width:40px;height:36px;border:1px solid #333;border-radius:8px;background:#16161f;color:#fff;font-size:1rem;cursor:pointer}
.studio-quick-btn.active{border-color:var(--accent);background:rgba(254,44,85,.15)}
.studio-mixer{display:grid;grid-template-columns:1fr 1fr;gap:.65rem}
.studio-mixer-row{display:flex;flex-direction:column;gap:.25rem;font-size:.68rem;color:var(--muted)}
.studio-mixer-row input[type=range]{width:100%;accent-color:var(--accent)}
.studio-go-row{display:flex;gap:.5rem;align-items:center}
.studio-title-input{flex:1;padding:.6rem .75rem;border:1px solid #333;border-radius:10px;background:#16161f;color:#fff;font-size:.85rem;min-width:0}
.studio-go-live-btn{flex-shrink:0;padding:.65rem 1.35rem;border:none;border-radius:999px;background:linear-gradient(135deg,#ff2d55,#e91e63);color:#fff;font-weight:800;font-size:.88rem;cursor:pointer;white-space:nowrap;box-shadow:0 4px 18px rgba(254,44,85,.4)}
.studio-go-live-btn:disabled{opacity:.4;cursor:not-allowed;box-shadow:none}
.studio-resume-btn{flex-shrink:0;padding:.6rem 1rem;border:1px solid var(--accent);border-radius:999px;background:rgba(254,44,85,.1);color:#fff;font-weight:700;font-size:.82rem;cursor:pointer;white-space:nowrap}
.studio-right{width:260px;flex-shrink:0;background:#12121a;border-left:1px solid #1e1e28;display:flex;flex-direction:column;overflow:hidden;padding:.65rem .5rem;gap:.5rem}
.studio-perf-grid{display:grid;grid-template-columns:1fr 1fr;gap:.4rem}
.studio-perf-item{background:#0d0d12;border-radius:8px;padding:.5rem;text-align:center;font-size:.62rem;color:var(--muted)}
.studio-perf-val{display:block;font-size:1rem;font-weight:800;color:#fff;margin-bottom:.1rem}
.studio-activity-list{overflow-y:auto;max-height:180px;display:flex;flex-direction:column;gap:.35rem}
.studio-chat-preview{flex-shrink:0}
.studio-chat-placeholder{font-size:.72rem;color:var(--muted);line-height:1.45;padding:.25rem 0}
.studio-chat-placeholder p{margin:0 0 .5rem}
.studio-chat-sub{font-size:.65rem!important;opacity:.8}
.studio-statusbar{display:flex;align-items:center;gap:1rem;padding:.35rem .85rem;background:#0a0a0f;border-top:1px solid #1e1e28;font-size:.62rem;color:#555;flex-shrink:0}
.studio-status-brand{margin-left:auto;color:#444}
.live-card{display:flex;gap:.5rem;padding:.5rem;background:#0d0d12;border-radius:8px;align-items:center;cursor:pointer;border:1px solid transparent;font-size:.72rem}
.live-card:hover{border-color:rgba(254,44,85,.3)}
.live-card img{width:32px;height:32px;border-radius:50%;object-fit:cover;flex-shrink:0}
.live-dot{color:#f00;font-weight:700;font-size:.65rem}
@media(max-width:960px){
  .studio-left{width:200px}
  .studio-right{display:none}
  .studio-mixer{grid-template-columns:1fr}
}
@media(max-width:720px){
  .studio-layout{flex-direction:column}
  .studio-left{width:100%;max-height:28vh;flex-shrink:0;border-right:none;border-bottom:1px solid #1e1e28}
  .studio-left .studio-panel-grow,.studio-tools-grid,.studio-tier-box{display:none}
  .studio-center{padding:.5rem}
  .studio-preview--portrait{max-width:220px}
  .studio-go-row{flex-wrap:wrap}
  .studio-go-live-btn,.studio-resume-btn{width:100%}
}
#live-broadcast-screen,#live-watch-screen{position:fixed;inset:0;z-index:8500;background:#0a0a0f}
.live-room{display:flex;height:100vh;height:100dvh;overflow:hidden}
.live-suggest-sidebar{display:none;width:220px;flex-shrink:0;border-right:1px solid #1e1e28;padding:1rem .75rem;overflow-y:auto;background:#0a0a0f}
.live-suggest-title{font-size:.8rem;font-weight:700;color:var(--muted);margin-bottom:.75rem;text-transform:uppercase;letter-spacing:.04em}
.live-suggest-item{display:flex;align-items:center;gap:.55rem;padding:.45rem 0;cursor:pointer;border:none;background:none;color:#fff;width:100%;text-align:left}
.live-suggest-item img{width:36px;height:36px;border-radius:50%;object-fit:cover;flex-shrink:0}
.live-suggest-item strong{display:block;font-size:.78rem}
.live-suggest-item span{font-size:.68rem;color:var(--muted)}
.live-stage{flex:1;display:flex;flex-direction:column;min-width:0;background:#000}
.live-stage-header{display:flex;align-items:center;gap:.55rem;padding:.65rem .85rem;background:#0a0a0f;border-bottom:1px solid #1e1e28;flex-shrink:0;z-index:8}
.live-host-avatar{width:40px;height:40px;border-radius:50%;object-fit:cover;border:2px solid rgba(255,255,255,.12);flex-shrink:0}
.live-host-meta{flex:1;min-width:0}
.live-host-meta strong{display:block;font-size:.9rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.live-host-sub{display:block;font-size:.72rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.live-follow-btn{padding:.4rem 1rem;border:none;border-radius:8px;background:var(--accent);color:#fff;font-weight:700;font-size:.78rem;cursor:pointer;flex-shrink:0}
.live-follow-btn.following{background:#333;border:1px solid #555}
.live-room-close,.live-stage-header .profile-icon-btn{flex-shrink:0}
.live-badge-inline{background:rgba(255,0,0,.9);color:#fff;padding:.25rem .55rem;border-radius:6px;font-weight:700;font-size:.72rem}
.live-stats-inline{font-size:.72rem;color:var(--muted);margin-left:auto;margin-right:.5rem}
.live-end-btn{padding:.4rem .75rem;font-size:.78rem}
.live-stage-main{flex:1;position:relative;min-height:0;display:flex;align-items:center;justify-content:center;background:#111}
.live-video-wrap{position:relative;width:100%;height:100%;max-height:100%;background:#000;overflow:hidden}
.live-video-wrap video,.live-video-wrap img{width:100%;height:100%;object-fit:contain;display:block}
.live-badge-top{position:absolute;top:.75rem;left:.75rem;background:rgba(255,0,0,.85);color:#fff;padding:.3rem .65rem;border-radius:8px;font-weight:700;font-size:.75rem;z-index:5}
.live-gift-footer{display:flex;align-items:center;gap:.5rem;padding:.55rem .75rem calc(.55rem + var(--safe-b));background:#0a0a0f;border-top:1px solid #1e1e28;flex-shrink:0;flex-wrap:wrap}
.live-gift-footer .live-gift-strip{flex:1;min-width:0}
.live-pk-bar{padding:.45rem .65rem;background:linear-gradient(90deg,rgba(254,44,85,.35),rgba(37,244,238,.35));border-bottom:1px solid #333;flex-shrink:0}
.live-pk-bar-inner{display:flex;align-items:center;gap:.5rem;font-size:.78rem;font-weight:700}
.live-pk-host{display:flex;align-items:center;gap:.35rem;min-width:0;flex:1}
.live-pk-host img{width:28px;height:28px;border-radius:50%;object-fit:cover;border:2px solid #fff}
.live-pk-host.right{flex-direction:row-reverse;text-align:right}
.live-pk-score{font-size:1.05rem;min-width:2rem;text-align:center}
.live-pk-timer{min-width:3.2rem;text-align:center;color:#ffe066;font-variant-numeric:tabular-nums}
.live-pk-progress{flex:1;height:8px;background:#222;border-radius:999px;overflow:hidden;display:flex}
.live-pk-progress-a{background:var(--accent);height:100%;transition:width .3s}
.live-pk-progress-b{background:var(--accent2);height:100%;transition:width .3s;margin-left:auto}
.live-video-wrap.live-pk-split{display:grid;grid-template-columns:1fr 1fr;gap:2px}
.live-video-wrap.live-pk-split #live-watch-v,.live-video-wrap.live-pk-split #live-broadcast-v,.live-video-wrap.live-pk-split #live-watch-img{grid-column:1;grid-row:1}
.live-video-wrap.live-pk-split .live-pk-opponent{position:relative;grid-column:2;grid-row:1;display:block!important;width:100%;height:100%;border-left:2px solid #fff}
.live-pk-opponent video,.live-pk-opponent img{width:100%;height:100%;object-fit:cover;display:block}
.live-pk-likes{width:100%;display:flex;gap:.5rem;margin-bottom:.25rem}
.live-pk-like-btn{flex:1;padding:.55rem;border:none;border-radius:10px;background:linear-gradient(135deg,#ff4d6d,#fe2c55);color:#fff;font-weight:700;font-size:.82rem;cursor:pointer}
.live-pk-like-btn:last-child{background:linear-gradient(135deg,#25f4ee,#00c4cc)}
.live-pk-gift-label{display:block;width:100%;font-size:.68rem;color:var(--muted);margin-bottom:.2rem;font-weight:700}
.live-pk-invite{padding:.65rem 1rem;background:#1a1020;border-bottom:1px solid #442255;display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
.live-pk-invite button{padding:.45rem .85rem;border:none;border-radius:8px;font-weight:700;cursor:pointer}
.live-pk-invite .accept{background:var(--accent);color:#fff}
.live-pk-invite .decline{background:#333;color:#ccc}
.studio-tool-btn{border:1px solid #444;background:#16161f;color:#fff;cursor:pointer;padding:.45rem .55rem;border-radius:8px;font-size:.72rem}
.studio-tool-btn:hover{border-color:var(--accent)}
.live-gift-strip{display:flex;gap:.45rem;overflow-x:auto;padding:.15rem 0;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.live-gift-strip::-webkit-scrollbar{display:none}
.live-gift-chip{flex:0 0 auto;padding:.45rem .55rem;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:linear-gradient(180deg,#1e1e28,#12121a);text-align:center;cursor:pointer;min-width:64px;transition:transform .15s}
.live-gift-chip:active{transform:scale(.95)}
.live-gift-chip .cp-emoji{width:36px!important;height:36px!important}
.live-gift-chip small{display:block;font-size:.62rem;color:var(--accent2);margin-top:.15rem;font-weight:600}
.live-recharge-btn{flex-shrink:0;padding:.45rem .7rem;border:none;border-radius:10px;background:linear-gradient(135deg,#ffd700,#ff8c00);color:#000;font-weight:700;font-size:.72rem;cursor:pointer}
.live-chat-panel{width:300px;flex-shrink:0;display:flex;flex-direction:column;background:#16161f;border-left:1px solid #1e1e28}
.live-chat-top{padding:.65rem .75rem;border-bottom:1px solid #222;flex-shrink:0}
.live-viewer-label{display:block;font-size:.78rem;font-weight:700;margin-bottom:.5rem}
.live-top-gifters{display:flex;gap:.35rem;align-items:center}
.live-top-gifter{position:relative;flex-shrink:0}
.live-top-gifter img{width:32px;height:32px;border-radius:50%;object-fit:cover;border:2px solid #333}
.live-top-gifter .rank{position:absolute;top:-4px;left:-4px;width:16px;height:16px;border-radius:50%;background:var(--accent);color:#fff;font-size:.55rem;font-weight:800;display:flex;align-items:center;justify-content:center}
.live-top-gifter.rank-1 img{border-color:#ffd700}
.live-top-gifter.rank-2 img{border-color:#c0c0c0}
.live-top-gifter.rank-3 img{border-color:#cd7f32}
.live-chat-messages{flex:1;overflow-y:auto;padding:.5rem .65rem;display:flex;flex-direction:column;gap:.35rem;scrollbar-width:thin}
.live-chat-msg{display:flex;gap:.4rem;align-items:flex-start;font-size:.78rem;line-height:1.35;animation:chatIn .25s ease}
.live-chat-msg.system,.live-chat-msg.join{color:var(--muted);font-size:.72rem;padding:.2rem 0}
.live-chat-msg.system .live-chat-body{background:rgba(255,255,255,.06);border-radius:10px;padding:.45rem .55rem}
.live-chat-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;flex-shrink:0;margin-top:2px;background:#333}
.live-chat-body{min-width:0;word-break:break-word}
.live-chat-name{font-weight:700;margin-right:.25rem}
.live-chat-badge{display:inline-block;font-size:.58rem;font-weight:800;padding:.1rem .3rem;border-radius:4px;margin-right:.2rem;vertical-align:middle;line-height:1.2}
.live-chat-badge.tier{background:#3b82f6;color:#fff}
.live-chat-badge.premium{background:linear-gradient(135deg,#ffd700,#ff8c00);color:#000}
@keyframes chatIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.live-chat-composer{display:flex;align-items:center;gap:.35rem;padding:.55rem .65rem calc(.55rem + var(--safe-b));border-top:1px solid #222;background:#12121a;flex-shrink:0}
.live-chat-composer input{flex:1;padding:.55rem .65rem;border:1px solid #333;border-radius:999px;background:#0a0a0f;color:#fff;font-size:.82rem}
.live-chat-emoji-btn{width:36px;height:36px;border:none;border-radius:50%;background:#222;color:#fff;font-size:1.1rem;cursor:pointer;flex-shrink:0}
.live-chat-send-btn{width:36px;height:36px;border:none;border-radius:50%;background:var(--accent);color:#fff;font-size:1rem;cursor:pointer;flex-shrink:0}
.live-gift-floats{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:4}
.live-gift-float{position:absolute;left:.5rem;max-width:75%;animation:liveGiftFly 3s ease-out forwards;z-index:6}
.live-gift-float-card{display:flex;align-items:center;gap:.5rem;background:rgba(0,0,0,.72);backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.15);border-radius:14px;padding:.45rem .7rem;box-shadow:0 4px 20px rgba(0,0,0,.4)}
.gift-float-emoji{display:flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:14px;background:linear-gradient(145deg,rgba(254,44,85,.25),rgba(37,244,238,.15));flex-shrink:0}
.gift-float-emoji .cp-emoji{width:36px!important;height:36px!important}
.gift-float-meta{display:flex;flex-direction:column;min-width:0}
.gift-float-name{font-size:.78rem;font-weight:700;color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:140px}
.gift-float-label{font-size:.65rem;color:var(--accent2)}
@keyframes liveGiftFly{0%{opacity:0;transform:translateY(20px) scale(.9)}15%{opacity:1;transform:translateY(0) scale(1)}85%{opacity:1}100%{opacity:0;transform:translateY(-30px)}}
.feed-gift-toast{position:absolute;left:50%;top:45%;transform:translate(-50%,-50%);z-index:120;pointer-events:none}
.feed-gift-toast .live-gift-float-card{transform:scale(1.1)}
.feed-gift-toast.show{animation:feedToastPop 2.8s ease forwards}
@keyframes feedToastPop{0%,78%{opacity:1;transform:translate(-50%,-50%) scale(1)}100%{opacity:0;transform:translate(-50%,-60%) scale(.9)}}
.live-coins-hint{font-size:.7rem;color:var(--muted);flex-shrink:0;white-space:nowrap}
.live-connecting{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.55);color:#fff;font-size:.95rem;z-index:3;backdrop-filter:blur(4px)}
.live-connecting.hidden{display:none}
@media(min-width:960px){
  .live-suggest-sidebar{display:block}
  .live-chat-panel{width:340px}
  .live-room--host .live-stage-main{max-width:720px;margin:0 auto;width:100%}
}
@media(max-width:719px){
  .live-chat-panel{position:absolute;right:0;top:3.5rem;bottom:5.5rem;width:46%;max-width:210px;background:transparent;border:none;pointer-events:none;z-index:7}
  .live-chat-top,.live-chat-messages{pointer-events:none}
  .live-chat-composer{pointer-events:auto;background:rgba(0,0,0,.55);backdrop-filter:blur(6px);border-radius:12px 0 0 0;margin-left:.25rem}
  .live-chat-messages{-webkit-mask-image:linear-gradient(transparent,#000 12%,#000 88%,transparent);mask-image:linear-gradient(transparent,#000 12%,#000 88%,transparent)}
  .live-stage-header .live-host-meta{max-width:42%}
}
#inbox-screen{position:fixed;inset:0;z-index:175;background:var(--bg);display:flex;flex-direction:column}
.inbox-header{display:flex;align-items:center;gap:.5rem;padding:.65rem 1rem;border-bottom:1px solid #1a1a22;flex-shrink:0}
.inbox-title{flex:1;font-size:1.05rem;font-weight:700;margin:0}
.inbox-coins-btn{border:none;background:#16161f;border-radius:999px;padding:.35rem .65rem;font-size:.85rem;cursor:pointer;color:#fff}
.inbox-layout{flex:1;display:flex;min-height:0;overflow:hidden}
.inbox-threads{flex:1;overflow-y:auto;padding:.5rem}
.inbox-thread{display:flex;gap:.65rem;padding:.75rem;border-radius:12px;cursor:pointer;border:1px solid transparent;margin-bottom:.35rem}
.inbox-thread:hover,.inbox-thread.active{background:#16161f;border-color:#2a2a35}
.inbox-thread img{width:48px;height:48px;border-radius:50%;object-fit:cover;flex-shrink:0;background:#333}
.inbox-thread-meta{flex:1;min-width:0}
.inbox-thread-meta strong{display:block;font-size:.88rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.inbox-thread-preview{font-size:.75rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:.15rem}
.inbox-chat{flex:1;display:flex;flex-direction:column;min-width:0;border-left:1px solid #1a1a22}
.inbox-chat-head{display:flex;align-items:center;gap:.5rem;padding:.65rem 1rem;border-bottom:1px solid #1a1a22;flex-shrink:0}
.inbox-chat-back{display:none}
.inbox-chat-avatar{width:36px;height:36px;border-radius:50%;object-fit:cover}
.inbox-messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:.5rem}
.inbox-msg{max-width:82%;padding:.55rem .75rem;border-radius:14px;font-size:.88rem;line-height:1.35;word-break:break-word}
.inbox-msg.self{align-self:flex-end;background:var(--accent);color:#fff;border-bottom-right-radius:4px}
.inbox-msg.other{align-self:flex-start;background:#16161f;color:#eee;border-bottom-left-radius:4px}
.inbox-composer{display:flex;gap:.4rem;padding:.65rem 1rem calc(.65rem + var(--safe-b));border-top:1px solid #1a1a22;flex-shrink:0}
.inbox-composer input{flex:1;padding:.6rem .75rem;border:1px solid #333;border-radius:999px;background:#16161f;color:#fff;font-size:.88rem}
.inbox-send-btn{width:40px;height:40px;border:none;border-radius:50%;background:var(--accent);color:#fff;font-size:1.1rem;cursor:pointer;flex-shrink:0}
.inbox-empty{text-align:center;padding:2.5rem 1rem;color:var(--muted);font-size:.88rem}
.coins-balance{font-size:.95rem;margin:.5rem 0 1rem;color:var(--accent2);font-weight:600}
.coins-pack-grid{display:grid;grid-template-columns:1fr 1fr;gap:.65rem;margin:1rem 0}
.coins-pack{padding:1rem;border:1px solid #333;border-radius:14px;background:#16161f;text-align:center;cursor:pointer;transition:border-color .15s,transform .15s}
.coins-pack:hover,.coins-pack.popular{border-color:var(--accent)}
.coins-pack.popular{box-shadow:0 0 0 1px var(--accent)}
.coins-pack-emoji{font-size:1.75rem;margin-bottom:.25rem}
.coins-pack-coins{font-weight:800;font-size:1.1rem}
.coins-pack-price{font-size:.8rem;color:var(--accent2);margin-top:.25rem}
.coins-pack-badge{font-size:.62rem;color:var(--accent);font-weight:700;margin-top:.2rem}
.profile-msg-btn{margin-left:.35rem}
@media(min-width:720px){
  .inbox-layout{max-width:900px;margin:0 auto;width:100%}
  .inbox-threads{width:280px;flex:none;border-right:1px solid #1a1a22}
  .inbox-chat-back{display:none!important}
}
@media(max-width:719px){
  .inbox-threads{width:100%}
  .inbox-chat{position:absolute;inset:0;z-index:2;background:var(--bg)}
  .inbox-chat-back{display:flex!important}
  .inbox-layout.inbox-chat-open .inbox-threads{display:none}
}
#toast{position:fixed;bottom:calc(80px + var(--safe-b));left:50%;transform:translateX(-50%);background:rgba(0,0,0,.85);padding:.6rem 1.2rem;border-radius:20px;font-size:.85rem;z-index:9999;opacity:0;transition:.3s;pointer-events:none}
#toast.show{opacity:1}
.hint{font-size:.8rem;color:var(--muted);margin:.5rem 0}
.comment-item{padding:.6rem 0;border-bottom:1px solid #222;font-size:.9rem}
.comment-author{font-weight:600;font-size:.8rem;color:var(--accent2)}
.profile-tabs{display:flex;gap:.25rem}
.profile-tabs button{background:none;border:none;color:var(--muted);font-weight:600;cursor:pointer;padding:.65rem .75rem;font-size:.85rem;border-bottom:2px solid transparent;margin-bottom:-1px}
.profile-tabs button.active{color:#fff;border-bottom-color:#fff}
.profile-grid-upload{background:#16161f;border:2px dashed #444;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--muted);cursor:pointer;aspect-ratio:9/16;gap:.15rem}
.feed-media-wrap{position:relative;width:100%;height:100%}
.cp-post-overlays{position:absolute;inset:0;pointer-events:none;overflow:hidden}
.cp-post-overlay{position:absolute;white-space:nowrap;font-weight:700;text-shadow:0 1px 4px rgba(0,0,0,.8)}
.cp-post-overlay span{padding:.25rem .6rem;border-radius:8px;display:inline-block}
#cp-editor-screen{position:fixed;inset:0;z-index:9500;background:var(--bg);display:flex;flex-direction:column}
.cp-editor-top{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;padding-top:calc(env(safe-area-inset-top,8px) + .75rem);border-bottom:1px solid #222}
.cp-editor-top span{font-weight:700}
.cp-editor-post-btn{width:auto;padding:.5rem 1rem;font-size:.9rem}
.cp-editor-stage{flex:1;display:flex;align-items:center;justify-content:center;background:#000;overflow:hidden}
.cp-editor-stage .cp-editor-pick{color:#666}
.cp-editor-media-wrap{position:relative;width:100%;max-width:420px;aspect-ratio:9/16;margin:0 auto}
.cp-editor-media-wrap video,.cp-editor-media-wrap img{width:100%;height:100%;object-fit:cover;display:block}
.cp-editor-overlays{position:absolute;inset:0;touch-action:none}
.cp-overlay-item{position:absolute;cursor:grab;user-select:none;touch-action:none;white-space:nowrap;font-weight:700}
.cp-overlay-item span{padding:.3rem .65rem;border-radius:8px;display:inline-block}
.cp-overlay-emoji,.cp-overlay-sticker{filter:drop-shadow(0 3px 10px rgba(0,0,0,.65))}
.cp-overlay-emoji .cp-emoji,.cp-overlay-sticker .cp-emoji{width:48px!important;height:48px!important}
.cp-editor-tools{background:#16161f;border-top:1px solid #222;padding:.75rem 1rem calc(.75rem + var(--safe-b));max-height:42vh;overflow-y:auto}
.cp-editor-tabs{display:flex;gap:.35rem;margin-bottom:.75rem;flex-wrap:wrap}
.cp-editor-tabs button{flex:1;min-width:70px;padding:.45rem .25rem;border:1px solid #333;border-radius:10px;background:#0a0a0f;color:#fff;font-size:.75rem;cursor:pointer}
.cp-editor-tabs button.active{border-color:var(--accent);background:rgba(254,44,85,.15)}
.cp-hook-list{display:flex;flex-wrap:wrap;gap:.4rem;margin-bottom:.5rem;max-height:100px;overflow-y:auto}
.cp-hook-chip{padding:.35rem .55rem;border:1px solid #333;border-radius:999px;background:#0a0a0f;color:#ddd;font-size:.72rem;cursor:pointer}
.cp-hook-list button:hover{border-color:var(--accent2)}
.cp-editor-panel input,.cp-editor-panel textarea,#cp-editor-caption{width:100%;padding:.65rem;margin:.4rem 0;border:1px solid #333;border-radius:10px;background:#0a0a0f;color:#fff;font-size:.9rem}
#cp-editor-caption{min-height:56px;resize:none}
.cp-editor-copyright{margin:.5rem 0;padding:.55rem .65rem;border-radius:10px;background:rgba(254,44,85,.08);border:1px solid rgba(254,44,85,.25);font-size:.78rem;line-height:1.35;color:#ddd}
.cp-editor-copyright strong{color:#fff}
.cp-editor-copyright-warn{color:#ff6b8a;margin-top:.35rem}
.copyright-rules{margin:.35rem 0 .5rem;padding-left:1.1rem;font-size:.82rem;color:var(--muted)}
.copyright-rules li{margin:.2rem 0}
.cp-emoji-grid,.cp-filter-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:.45rem;margin:.5rem 0}
.cp-emoji-btn,.cp-filter-btn{padding:.5rem;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:linear-gradient(180deg,#1a1a24,#0e0e14);cursor:pointer;transition:transform .15s,box-shadow .15s}
.cp-emoji-btn .cp-emoji{width:36px!important;height:36px!important}
.cp-emoji-btn:hover{transform:scale(1.08);box-shadow:0 6px 16px rgba(37,244,238,.12)}
.cp-filter-btn{font-size:.7rem}
.cp-filter-btn.active,.cp-emoji-btn:active{border-color:var(--accent)}
.cp-editor-edit-row{display:flex;gap:.5rem;margin:.5rem 0}
.cp-editor-edit-row select{flex:1;padding:.5rem;border-radius:8px;background:#0a0a0f;color:#fff;border:1px solid #333}
"""

APP_JS = r"""
(function () {
  const api = VibeLoopApi.init();
  let config = null;
  let profile = null;
  let posts = [];
  let feedItems = [];
  let showAds = true;
  let currentIdx = 0;
  let feedMode = 'foryou';
  let adVideoIds = [];
  let selectedNiches = [];
  let activePostId = null;
  let profilePostsCache = [];
  let profileTab = 'video';
  let profileSort = 'recent';
  let profileViewUserId = null;
  let editorReturnTo = 'feed';
  let giftTarget = null;
  let reportDraft = null;
  let activeLiveId = null;
  let livePollTimer = null;
  let liveCamStream = null;
  let liveWatchCleanup = null;
  let seenLiveGiftKeys = new Set();
  let feedCursor = '';
  let feedHasMore = false;
  let feedLoading = false;
  let liveChatSince = '';
  let liveChatSeenIds = new Set();
  let liveHostId = null;
  let liveMatchState = null;
  let pkOpponentCleanup = null;
  let lastMatchStatus = null;
  let liveHostFollowing = false;
  let exploreNiche = '';
  let explorePosts = [];
  let exploreCursor = '';
  let exploreHasMore = false;
  let exploreLoading = false;
  let exploreSearchQuery = '';
  let studioPreviewStream = null;
  let studioOrientation = 'portrait';
  let studioSourceMode = 'camera';
  let studioStatusTimer = null;
  let studioMicVolume = 80;
  let inboxActiveUserId = null;
  let inboxPollTimer = null;

  const $ = (s) => document.querySelector(s);
  const $$ = (s) => document.querySelectorAll(s);

  function bindClick(sel, fn) {
    const el = $(sel);
    if (el) el.onclick = fn;
  }

  function em(emoji, cls, size) {
    if (window.VlEmoji) return VlEmoji.html(emoji, cls, size);
    return esc(emoji);
  }

  function upgradeStaticEmojis() {
    if (window.VlEmoji) VlEmoji.upgradeAll(document);
  }

  function toast(msg) {
    const t = $('#toast');
    t.textContent = msg;
    t.classList.add('show');
    setTimeout(() => t.classList.remove('show'), 2500);
  }

  function show(el) { el.classList.remove('hidden'); }
  function hide(el) { el.classList.add('hidden'); }

  function enterAppShell() {
    document.body.classList.add('tt-logged-in');
    const sb = $('#tt-sidebar');
    if (sb) sb.classList.remove('hidden');
  }

  function exitAppShell() {
    document.body.classList.remove('tt-logged-in');
    const sb = $('#tt-sidebar');
    if (sb) sb.classList.add('hidden');
  }

  function setSidebarActive(side) {
    $$('.tt-side-item').forEach((b) => b.classList.toggle('active', b.dataset.side === side));
    if (side === 'home' || side === 'following') {
      $$('#bottom-nav button').forEach((b) => b.classList.toggle('active', b.dataset.nav === 'home'));
    } else {
      $$('#bottom-nav button').forEach((b) => b.classList.toggle('active', b.dataset.nav === side));
    }
  }

  function scrollToFeedIdx(idx) {
    const container = $('#feed-container');
    if (!container) return;
    const max = feedItems.length - 1;
    currentIdx = Math.max(0, Math.min(max, idx));
    const el = container.querySelector('[data-idx="' + currentIdx + '"]');
    if (el) el.scrollIntoView({ behavior: 'smooth' });
    renderCurrentItem();
    playVisible();
  }

  async function handlePaymentReturn() {
    const params = new URLSearchParams(location.search);
    if (params.get('payment') !== 'success' || !api._token) return;
    const sid = params.get('session_id');
    if (!sid) return;
    history.replaceState({}, '', location.pathname);
    try {
      const r = await api.confirmCoins(sid);
      if (r.credited) {
        profile = r.profile || profile;
        if (profile) profile.coins = r.coins;
        toast('+' + fmt(r.credited) + ' Coins gutgeschrieben! 🪙');
        return;
      }
    } catch (_) {}
    try {
      const r = await api.confirmPremium(sid);
      profile = r.profile || profile;
      if (profile) profile.isPremium = true;
      showAds = false;
      toast('Flarlo+ aktiv – keine Werbung mehr!');
    } catch (e) {
      toast(e.message || 'Zahlung wird noch verarbeitet…');
    }
  }

  function openCoinsShop() {
    const grid = $('#coins-pack-grid');
    grid.innerHTML = '';
    $('#coins-balance').textContent = 'Dein Guthaben: ' + fmt(profile?.coins || 0) + ' Coins';
    const packs = (config.monetization && config.monetization.coinPacks) || [];
    packs.forEach((pack) => {
      const d = document.createElement('button');
      d.type = 'button';
      d.className = 'coins-pack' + (pack.popular ? ' popular' : '');
      d.innerHTML =
        '<div class="coins-pack-emoji">' + (pack.emoji || '🪙') + '</div>' +
        '<div class="coins-pack-coins">' + pack.coins + '</div>' +
        '<div class="coins-pack-price">' + pack.priceEur.toFixed(2).replace('.', ',') + ' €</div>' +
        (pack.popular ? '<div class="coins-pack-badge">Beliebt</div>' : '');
      d.onclick = async () => {
        try {
          const r = await api.checkoutCoins(pack.id);
          if (r.url) location.href = r.url;
          else toast('Checkout nicht verfügbar');
        } catch (e) { toast(e.message); }
      };
      grid.appendChild(d);
    });
    if (!packs.length) grid.innerHTML = '<p class="hint">Coin-Pakete nicht geladen</p>';
    show($('#coins-modal'));
  }

  $('#coins-close').onclick = () => hide($('#coins-modal'));

  function stopInboxPoll() {
    if (inboxPollTimer) { clearInterval(inboxPollTimer); inboxPollTimer = null; }
  }

  function renderInboxMessages(messages) {
    const box = $('#inbox-messages');
    box.innerHTML = '';
    (messages || []).forEach((m) => {
      const d = document.createElement('div');
      d.className = 'inbox-msg ' + (m.isSelf ? 'self' : 'other');
      d.textContent = m.text;
      box.appendChild(d);
    });
    box.scrollTop = box.scrollHeight;
  }

  async function loadInboxThreads() {
    const list = $('#inbox-threads');
    list.innerHTML = '<p class="inbox-empty">Laden…</p>';
    try {
      const j = await api.getInbox();
      list.innerHTML = '';
      if (!(j.threads || []).length) {
        list.innerHTML = '<p class="inbox-empty">Noch keine Nachrichten.<br>Folge Creatorn und schreib ihnen vom Profil aus.</p>';
        return;
      }
      j.threads.forEach((t) => {
        const p = t.profile || {};
        const d = document.createElement('div');
        d.className = 'inbox-thread' + (t.otherId === inboxActiveUserId ? ' active' : '');
        const name = p.username ? '@' + p.username : (p.displayName || 'Nutzer');
        const preview = (t.lastFromMe ? 'Du: ' : '') + (t.lastBody || '…');
        d.innerHTML = '<img src="' + (p.photo || '') + '" alt="">' +
          '<div class="inbox-thread-meta"><strong>' + esc(name) + '</strong>' +
          '<div class="inbox-thread-preview">' + esc(preview) + '</div></div>';
        d.onclick = () => openInboxChat(t.otherId);
        list.appendChild(d);
      });
    } catch (e) {
      list.innerHTML = '<p class="inbox-empty">' + esc(e.message) + '</p>';
    }
  }

  async function openInboxChat(userId) {
    inboxActiveUserId = userId;
    stopInboxPoll();
    $$('.inbox-thread').forEach((el) => el.classList.remove('active'));
    try {
      const j = await api.getInboxMessages(userId);
      const p = j.other || {};
      $('#inbox-chat-name').textContent = p.username ? '@' + p.username : (p.displayName || 'Chat');
      $('#inbox-chat-avatar').src = p.photo || '';
      renderInboxMessages(j.messages);
      show($('#inbox-chat'));
      $('#inbox-layout').classList.add('inbox-chat-open');
      $('#inbox-input').disabled = !j.canMessage;
      $('#inbox-send').disabled = !j.canMessage;
      if (!j.canMessage) {
        $('#inbox-input').placeholder = 'Folge-Verbindung nötig zum Schreiben';
      } else {
        $('#inbox-input').placeholder = 'Nachricht schreiben…';
      }
      inboxPollTimer = setInterval(async () => {
        try {
          const u = await api.getInboxMessages(userId);
          renderInboxMessages(u.messages);
        } catch (_) {}
      }, 4000);
    } catch (e) { toast(e.message); }
  }

  async function showInbox(openUserId) {
    setSidebarActive('inbox');
    hide($('#main-screen'));
    hide($('#explore-screen'));
    hide($('#profile-screen'));
    hide($('#live-screen'));
    stopStudioPreview();
    show($('#inbox-screen'));
    await loadInboxThreads();
    if (openUserId) await openInboxChat(openUserId);
  }

  $('#inbox-back').onclick = () => {
    stopInboxPoll();
    inboxActiveUserId = null;
    hide($('#inbox-chat'));
    $('#inbox-layout').classList.remove('inbox-chat-open');
    hide($('#inbox-screen'));
    show($('#main-screen'));
    $$('#bottom-nav button').forEach((b) => b.classList.toggle('active', b.dataset.nav === 'home'));
  };

  $('#inbox-chat-back').onclick = () => {
    stopInboxPoll();
    inboxActiveUserId = null;
    hide($('#inbox-chat'));
    $('#inbox-layout').classList.remove('inbox-chat-open');
    loadInboxThreads();
  };

  $('#inbox-coins-btn').onclick = () => openCoinsShop();

  $('#inbox-send').onclick = async () => {
    const text = ($('#inbox-input').value || '').trim();
    if (!text || !inboxActiveUserId) return;
    if (!rejectCommunityText(text)) return;
    $('#inbox-send').disabled = true;
    try {
      await api.sendInboxMessage(inboxActiveUserId, text);
      $('#inbox-input').value = '';
      const j = await api.getInboxMessages(inboxActiveUserId);
      renderInboxMessages(j.messages);
      loadInboxThreads();
    } catch (e) { toast(e.message); }
    $('#inbox-send').disabled = false;
  };

  $('#inbox-input').onkeydown = (e) => {
    if (e.key === 'Enter') { e.preventDefault(); $('#inbox-send').click(); }
  };

  async function trackFlarloHit(type) {
    try {
      await fetch('/api/analytics/hit', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          type: 'flarlo_' + type,
          path: location.pathname || '/',
          source: 'flarlo',
        }),
      });
    } catch (_) {}
  }

  function initFlarloAnalytics() {
    if (!sessionStorage.getItem('flarlo_pv')) {
      sessionStorage.setItem('flarlo_pv', '1');
      trackFlarloHit('page_view');
    }
    const regTab = $('#auth-tab-register');
    const regBtn = $('#auth-register');
    if (regTab && !regTab.dataset.tracked) {
      regTab.dataset.tracked = '1';
      regTab.addEventListener('click', () => trackFlarloHit('register_click'));
    }
    if (regBtn && !regBtn.dataset.tracked) {
      regBtn.dataset.tracked = '1';
      regBtn.addEventListener('click', () => trackFlarloHit('register_click'));
    }
  }

  function setAuthMode(mode) {
    const isLogin = mode === 'login';
    const tabLogin = $('#auth-tab-login');
    const tabRegister = $('#auth-tab-register');
    if (tabLogin) {
      tabLogin.classList.toggle('active', isLogin);
      tabLogin.setAttribute('aria-selected', isLogin ? 'true' : 'false');
    }
    if (tabRegister) {
      tabRegister.classList.toggle('active', !isLogin);
      tabRegister.setAttribute('aria-selected', !isLogin ? 'true' : 'false');
    }
    const genderWrap = $('#auth-gender-wrap');
    if (genderWrap) genderWrap.classList.toggle('hidden', isLogin);
    const btnLogin = $('#auth-login');
    const btnRegister = $('#auth-register');
    if (btnLogin) btnLogin.classList.toggle('hidden', !isLogin);
    if (btnRegister) btnRegister.classList.toggle('hidden', isLogin);
    const hint = $('#auth-panel-hint');
    if (hint) {
      hint.textContent = isLogin
        ? 'Willkommen zurück – melde dich mit deinem Konto an.'
        : 'Erstelle dein kostenloses Flarlo-Konto und wähle deine Nischen.';
    }
    const err = $('#auth-error');
    if (err) err.textContent = '';
  }

  function initAuthTabs() {
    const tabLogin = $('#auth-tab-login');
    const tabRegister = $('#auth-tab-register');
    if (!tabLogin || !tabRegister) return;
    tabLogin.onclick = () => setAuthMode('login');
    tabRegister.onclick = () => setAuthMode('register');
    setAuthMode('login');
  }

  function showGuestLanding() {
    exitAppShell();
    hide($('#splash'));
    show($('#auth-screen'));
    initAuthTabs();
  }

  function resetSessionAndUrl() {
    api.setToken('');
    ['jami_token', 'vl_token', 'cp_token'].forEach((k) => localStorage.removeItem(k));
    sessionStorage.removeItem('flarlo_pv');
    const u = new URL(location.href);
    u.searchParams.delete('reset');
    u.searchParams.delete('reload');
    history.replaceState({}, '', u.pathname + u.search + u.hash);
  }

  async function boot() {
    if (new URLSearchParams(location.search).get('reset') === '1') {
      resetSessionAndUrl();
    }
    upgradeStaticEmojis();
    initFlarloAnalytics();
    initAuthTabs();
    if (!api._token) showGuestLanding();
    const configTimeout = new Promise((_, reject) => {
      setTimeout(() => reject(new Error('Config-Timeout')), 12000);
    });
    try {
      config = await Promise.race([api.getConfig(), configTimeout]);
      if (window.VlCopyright && config.copyrightPolicy) VlCopyright.setPolicy(config.copyrightPolicy);
      renderCommunityRules();
      renderCopyrightRules();
      adVideoIds = (config.ads && config.ads.youtubeVideoIds) || [];
      if (config.live && !config.live.requiresR2) {
        console.warn('Flarlo Live: R2 nicht aktiv – Broadcasting eingeschränkt');
      }
      if (api._token) {
        await handlePaymentReturn();
        try {
          profile = await api.me();
          if (!profile.niches || !profile.niches.length) {
            showNichePicker();
            return;
          }
        } catch (e) {
          api.setToken('');
        }
      }
      if (!api._token) { showGuestLanding(); return; }
      hide($('#splash'));
      hide($('#auth-screen'));
      enterAppShell();
      show($('#main-screen'));
      setSidebarActive('home');
      await loadFeed();
      await openClipFromUrl();
      await openProfileFromUrl();
      await openLiveFromUrl();
    } catch (e) {
      console.warn('[Flarlo boot]', e.message || e);
      if (!api._token) showGuestLanding();
      else {
        hide($('#splash'));
        hide($('#auth-screen'));
        enterAppShell();
        show($('#main-screen'));
        setSidebarActive('home');
      }
    }
  }

  async function openClipFromUrl() {
    const clipId = new URLSearchParams(location.search).get('clip');
    if (!clipId) return;
    try {
      const j = await api.getPost(clipId);
      if (j.post && !posts.some((p) => p.id === j.post.id)) posts.unshift(j.post);
      buildFeedItems();
      renderFeed();
      history.replaceState({}, '', location.pathname);
    } catch (_) {}
  }

  async function openProfileFromUrl() {
    const userId = new URLSearchParams(location.search).get('profile');
    if (!userId) return;
    history.replaceState({}, '', location.pathname);
    await showProfile(userId);
  }

  async function openLiveFromUrl() {
    const liveId = new URLSearchParams(location.search).get('live');
    if (!liveId) return;
    history.replaceState({}, '', location.pathname);
    await watchLive(liveId);
  }

  const NICHE_SECTIONS = [
    { id: 'entertainment', title: 'Comedy & Entertainment', emoji: '🎭', ids: ['comedy', 'memes', 'pranks', 'pov', 'reactions', 'sketches', 'standup', 'challenges'] },
    { id: 'music', title: 'Tanz & Musik', emoji: '🎵', ids: ['dance', 'musik', 'singing', 'kpop', 'hiphop', 'deutschrap', 'edm', 'rock', 'pop', 'schlager', 'latin', 'klassik', 'instruments', 'dj'] },
    { id: 'sport', title: 'Sport & Fitness', emoji: '💪', ids: ['fitness', 'sport', 'fussball', 'bundesliga', 'basketball', 'tennis', 'boxen', 'laufen', 'radfahren', 'yoga', 'bodybuilding', 'skaten', 'surfen', 'ski', 'klettern', 'parkour', 'esports', 'handball', 'formel1'] },
    { id: 'food', title: 'Food & Drink', emoji: '🍔', ids: ['kochen', 'food', 'backen', 'streetfood', 'vegan', 'coffee', 'cocktails', 'bbq', 'mukbang', 'restaurant'] },
    { id: 'beauty', title: 'Beauty & Fashion', emoji: '💄', ids: ['beauty', 'makeup', 'skincare', 'hair', 'nails', 'mode', 'streetwear', 'luxus', 'vintage', 'tattoos'] },
    { id: 'gaming', title: 'Gaming & Tech', emoji: '🎮', ids: ['gaming', 'minecraft', 'fortnite', 'valorant', 'gta', 'lol', 'streaming', 'tech', 'ai', 'coding', 'crypto'] },
    { id: 'lifestyle', title: 'Lifestyle & Home', emoji: '🏠', ids: ['diy', 'handwerk', 'interior', 'cleaning', 'garden', 'wedding', 'party', 'karneval'] },
    { id: 'travel', title: 'Travel & Natur', emoji: '✈️', ids: ['reisen', 'natur', 'camping', 'wandern', 'strand', 'stadt', 'lokal', 'roadtrip'] },
    { id: 'pets', title: 'Tiere', emoji: '🐾', ids: ['haustiere', 'hunde', 'katzen', 'tiere', 'pferde', 'aquarium'] },
    { id: 'education', title: 'Bildung & Karriere', emoji: '📚', ids: ['bildung', 'wissenschaft', 'mathe', 'geschichte', 'sprachen', 'buchtok', 'studium', 'karriere'] },
    { id: 'business', title: 'Business & Finanzen', emoji: '💼', ids: ['business', 'finanzen', 'marketing', 'immobilien', 'aktien', 'freelance'] },
    { id: 'health', title: 'Gesundheit', emoji: '🩺', ids: ['gesundheit', 'mentalhealth', 'motivation', 'meditation', 'ernaehrung'] },
    { id: 'family', title: 'Familie & Dating', emoji: '❤️', ids: ['familie', 'baby', 'teen', 'dating', 'beziehungen'] },
    { id: 'arts', title: 'Kunst & Kreativ', emoji: '🎨', ids: ['kunst', 'fotografie', 'film', 'cosplay', 'zeichnen', 'anime', 'crafting'] },
    { id: 'auto', title: 'Auto & Motor', emoji: '🚗', ids: ['auto', 'motorrad', 'tuning', 'trucks', 'aviation'] },
    { id: 'culture', title: 'Kultur & mehr', emoji: '🌐', ids: ['news', 'politik', 'truecrime', 'horror', 'asmr', 'lgbtq', 'spiritualitaet', 'astrologie', 'magie', 'interview', 'vtuber', 'fishing', 'landwirtschaft', 'military', 'recht', 'weltraum'] },
  ];

  let nichePickerReady = false;
  let nicheActiveCategory = 'all';

  function updateNichePickerUI() {
    const count = selectedNiches.length;
    $$('#niche-progress .niche-dot').forEach((dot, i) => {
      dot.classList.toggle('on', i < count);
    });
    const saveBtn = $('#niche-save');
    if (saveBtn) saveBtn.disabled = count === 0;
  }

  function syncNicheSelection() {
    $$('#niche-grid .niche-card').forEach((d) => {
      d.classList.toggle('selected', selectedNiches.includes(d.dataset.nicheId));
    });
  }

  function applyNicheFilter() {
    const search = $('#niche-search');
    const q = String(search ? search.value : '').trim().toLowerCase();
    const cat = nicheActiveCategory;
    let visible = 0;
    $$('#niche-grid .niche-card').forEach((card) => {
      const catMatch = cat === 'all' || card.dataset.sectionId === cat;
      const match = !q || card.dataset.nicheLabel.includes(q) || card.dataset.nicheId.includes(q);
      const show = catMatch && match;
      card.classList.toggle('hidden-niche', !show);
      if (show) visible += 1;
    });
    const empty = $('#niche-empty');
    if (empty) empty.classList.toggle('hidden', visible > 0);
  }

  function setNicheCategory(id) {
    nicheActiveCategory = id;
    $$('#niche-cats .niche-cat-btn').forEach((b) => b.classList.toggle('active', b.dataset.cat === id));
    applyNicheFilter();
  }

  function toggleNiche(id) {
    if (selectedNiches.includes(id)) {
      selectedNiches = selectedNiches.filter((x) => x !== id);
    } else if (selectedNiches.length < 5) {
      selectedNiches.push(id);
    } else {
      toast('Max. 5 Nischen');
      return;
    }
    syncNicheSelection();
    updateNichePickerUI();
  }

  function renderNichePicker() {
    const grid = $('#niche-grid');
    const cats = $('#niche-cats');
    if (!grid || !cats) return;
    if (!nichePickerReady) {
      cats.innerHTML = '';
      const allBtn = document.createElement('button');
      allBtn.type = 'button';
      allBtn.className = 'niche-cat-btn active';
      allBtn.dataset.cat = 'all';
      allBtn.textContent = '✨';
      allBtn.title = 'Alle Nischen';
      allBtn.setAttribute('aria-label', 'Alle Nischen');
      allBtn.onclick = () => setNicheCategory('all');
      cats.appendChild(allBtn);
      NICHE_SECTIONS.forEach((sec) => {
        const btn = document.createElement('button');
        btn.type = 'button';
        btn.className = 'niche-cat-btn';
        btn.dataset.cat = sec.id;
        btn.textContent = sec.emoji;
        btn.title = sec.title;
        btn.setAttribute('aria-label', sec.title);
        btn.onclick = () => setNicheCategory(sec.id);
        cats.appendChild(btn);
      });
      grid.innerHTML = '';
      const nicheMap = {};
      (config.niches || []).forEach((n) => { nicheMap[n.id] = n; });
      NICHE_SECTIONS.forEach((sec) => {
        sec.ids.forEach((id) => {
          const n = nicheMap[id];
          if (!n) return;
          const card = document.createElement('button');
          card.type = 'button';
          card.className = 'niche-card';
          card.dataset.nicheId = n.id;
          card.dataset.nicheLabel = (n.label || n.id).toLowerCase();
          card.dataset.sectionId = sec.id;
          card.innerHTML =
            '<span class="niche-card-check" aria-hidden="true">✓</span>' +
            '<span class="niche-card-emoji">' + (n.emoji || '✨') + '</span>' +
            '<span class="niche-card-label">' + (n.label || n.id) + '</span>';
          card.onclick = () => toggleNiche(n.id);
          grid.appendChild(card);
        });
      });
      nichePickerReady = true;
    }
    syncNicheSelection();
    applyNicheFilter();
    updateNichePickerUI();
  }

  function showNichePicker() {
    hide($('#splash'));
    show($('#niche-screen'));
    selectedNiches = [];
    nicheActiveCategory = 'all';
    nichePickerReady = false;
    const count = config.nicheCount || (config.niches || []).length;
    const hint = $('#niche-hint');
    if (hint && count) {
      hint.textContent = 'Tippe bis zu 5 aus ' + count + ' Nischen – dein Feed wird personalisiert.';
    }
    const search = $('#niche-search');
    if (search) {
      search.value = '';
      search.oninput = () => applyNicheFilter();
    }
    renderNichePicker();
  }

  $('#niche-save').onclick = async () => {
    profile = await api.updateMe({ niches: selectedNiches });
    hide($('#niche-screen'));
    enterAppShell();
    show($('#main-screen'));
    setSidebarActive('home');
    await loadFeed();
  };

  $('#auth-pass').onkeydown = (e) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      if ($('#auth-login').classList.contains('hidden')) $('#auth-register').click();
      else $('#auth-login').click();
    }
  };

  $('#auth-login').onclick = async () => {
    $('#auth-error').textContent = '';
    try {
      await api.login($('#auth-email').value, $('#auth-pass').value);
      profile = await api.me();
      hide($('#auth-screen'));
      if (!profile.niches || !profile.niches.length) showNichePicker();
      else { show($('#main-screen')); await loadFeed(); }
    } catch (e) { $('#auth-error').textContent = e.message; }
  };

  $('#auth-register').onclick = async () => {
    $('#auth-error').textContent = '';
    const email = $('#auth-email').value;
    const pass = $('#auth-pass').value;
    if (!email || !pass) { $('#auth-error').textContent = 'E-Mail und Passwort eingeben'; return; }
    try {
      const gender = ($('#auth-gender') && $('#auth-gender').value) || 'Divers';
      await api.register({
        email, password: pass, displayName: email.split('@')[0],
        age: 18, city: 'Deutschland', country: 'DE', gender, seeking: 'Alle',
        signupSource: 'flarlo', source: 'flarlo',
      });
      trackFlarloHit('registration');
      profile = await api.me();
      hide($('#auth-screen'));
      showNichePicker();
    } catch (e) { $('#auth-error').textContent = e.message; }
  };

  function buildFeedItems() {
    feedItems = [];
    const everyN = (config.ads && config.ads.everyN) || 4;
    const adsOn = showAds && !(profile && profile.isPremium);
    posts.forEach((post, i) => {
      feedItems.push({ kind: 'post', post: post, postIdx: i });
      if (adsOn && adVideoIds.length && (i + 1) % everyN === 0) {
        feedItems.push({ kind: 'ad', adIdx: feedItems.length });
      }
    });
  }

  function pickAdVideoId(idx) {
    if (!adVideoIds.length) return '';
    return adVideoIds[idx % adVideoIds.length];
  }

  async function loadFeed(append) {
    if (feedLoading) return;
    feedLoading = true;
    const container = $('#feed-container');
    if (!append) container.innerHTML = '<div style="padding:2rem;text-align:center">Laden…</div>';
    try {
      const mode = feedMode === 'live' ? 'foryou' : feedMode;
      const j = await api.feed({ mode, cursor: append ? feedCursor : '' });
      if (append) posts = posts.concat(j.posts || []);
      else {
        posts = j.posts || [];
        currentIdx = 0;
      }
      feedHasMore = !!j.hasMore;
      feedCursor = j.nextCursor || '';
      showAds = j.showAds !== false;
      if (profile) profile.isPremium = !!j.isPremium;
      buildFeedItems();
      renderFeed();
    } catch (e) {
      if (!append) container.innerHTML = '<div style="padding:2rem;text-align:center">' + e.message + '</div>';
      else toast(e.message);
    }
    feedLoading = false;
  }

  function renderFeed() {
    const container = $('#feed-container');
    container.innerHTML = '';
    if (!posts.length) {
      container.innerHTML = '<div style="padding:3rem;text-align:center;color:#aaa">Noch keine Clips – sei der Erste!</div>';
      return;
    }
    const skipSec = (config.ads && config.ads.skipAfterSec) || 5;
    feedItems.forEach((fi, i) => {
      if (fi.kind === 'ad') {
        const ad = document.createElement('div');
        ad.className = 'feed-ad';
        ad.dataset.idx = i;
        ad.dataset.kind = 'ad';
        const vid = pickAdVideoId(fi.adIdx || i);
        ad.innerHTML =
          '<span class="feed-ad-label">Werbung</span>' +
          '<iframe src="https://www.youtube.com/embed/' + vid + '?autoplay=1&mute=1&controls=0&rel=0" allow="autoplay; encrypted-media" allowfullscreen></iframe>' +
          '<button class="feed-ad-skip" disabled>Überspringen in ' + skipSec + 's</button>';
        const skipBtn = ad.querySelector('.feed-ad-skip');
        let left = skipSec;
        const timer = setInterval(() => {
          left -= 1;
          if (left <= 0) {
            clearInterval(timer);
            skipBtn.disabled = false;
            skipBtn.textContent = 'Überspringen';
          } else {
            skipBtn.textContent = 'Überspringen in ' + left + 's';
          }
        }, 1000);
        skipBtn.onclick = () => {
          if (skipBtn.disabled) return;
          const next = container.querySelector('[data-idx="' + (i + 1) + '"]');
          if (next) next.scrollIntoView({ behavior: 'smooth' });
        };
        container.appendChild(ad);
        return;
      }
      const post = fi.post;
      const item = document.createElement('div');
      item.className = 'feed-item';
      item.dataset.idx = i;
      item.dataset.kind = 'post';
      const wrap = document.createElement('div');
      wrap.className = 'feed-media-wrap';
      if (post.type === 'photo') {
        wrap.innerHTML = '<img src="' + post.mediaUrl + '" alt="">';
      } else {
        wrap.innerHTML = '<video src="' + post.mediaUrl + '" loop playsinline muted preload="metadata" poster="' + (post.thumbUrl || '') + '"></video>';
      }
      item.appendChild(wrap);
      const overlay = document.createElement('div');
      overlay.className = 'feed-overlay tt-feed-info';
      const author = post.author || {};
      const uname = author.username || author.displayName || '?';
      const tags = (post.hashtags || []).map((h) => '#' + h).join(' ');
      const caption = esc(post.caption || '');
      const isOwnPost = profile && author.id === profile.id;
      overlay.innerHTML =
        '<div class="tt-feed-user-row">' +
        '<span class="tt-feed-username">@' + esc(uname) + (author.verified ? ' <span class="verified">✓</span>' : '') + '</span>' +
        (!isOwnPost ? '<button type="button" class="tt-feed-follow-btn">Folgen</button>' : '') +
        '</div>' +
        (caption ? '<div class="tt-feed-caption">' + caption + '</div>' : '') +
        (tags ? '<div class="tt-feed-tags">' + esc(tags) + '</div>' : '') +
        '<div class="tt-feed-sound"><span class="tt-sound-icon">♪</span>' +
        '<div class="tt-sound-marquee"><span>Originalton – @' + esc(uname) + ' · ' + esc(uname) + '</span></div></div>';
      const followBtn = overlay.querySelector('.tt-feed-follow-btn');
      if (followBtn) {
        followBtn.onclick = async (e) => {
          e.stopPropagation();
          try {
            const r = await api.follow(author.id);
            followBtn.textContent = r.following ? 'Folge ich' : 'Folgen';
            followBtn.style.background = r.following ? '#333' : '';
          } catch (err) { toast(err.message); }
        };
      }
      item.appendChild(overlay);
      if ((post.author || {}).id !== profile.id) {
        const more = document.createElement('button');
        more.type = 'button';
        more.className = 'feed-more-btn';
        more.setAttribute('aria-label', 'Melden');
        more.textContent = '⋯';
        more.onclick = (e) => {
          e.stopPropagation();
          openSafetyMenu({
            targetType: 'post',
            targetId: post.id,
            targetUserId: author.id,
            author,
            label: userLabel(author) + ' · Clip',
          });
        };
        item.appendChild(more);
      }
      container.appendChild(item);
      const v = wrap.querySelector('video');
      if (window.VibeLoopEditor) VibeLoopEditor.renderOverlaysOnPost(wrap, post.overlays, v);
    });
    renderCurrentItem();
    setupScrollObserver();
    playVisible();
  }

  function currentFeedItem() {
    return feedItems[currentIdx] || null;
  }

  function renderCurrentItem() {
    const fi = currentFeedItem();
    if (!fi || fi.kind !== 'post') {
      $('#action-bar').innerHTML = '';
      return;
    }
    renderActions(fi.post);
  }

  function esc(s) {
    const d = document.createElement('div');
    d.textContent = s || '';
    return d.innerHTML;
  }

  function userLabel(u) {
    if (!u) return 'Nutzer';
    return u.username ? '@' + u.username : (u.displayName || 'Nutzer');
  }

  function removeBlockedFromFeed(userId) {
    posts = posts.filter((p) => (p.author || {}).id !== userId);
    explorePosts = explorePosts.filter((p) => (p.author || {}).id !== userId);
    buildFeedItems();
    if ($('#main-screen') && !$('#main-screen').classList.contains('hidden')) renderFeed();
    if ($('#explore-screen') && !$('#explore-screen').classList.contains('hidden')) renderExploreGrid();
  }

  async function blockUserAccount(userId, label) {
    if (!userId || userId === profile.id) return;
    const name = label || 'diesen Account';
    if (!confirm('Account sperren? Du siehst keine Inhalte mehr von ' + name + '.')) return;
    try {
      await api.blockUser(userId);
      removeBlockedFromFeed(userId);
      toast('Account gesperrt');
      if (profileViewUserId === userId) {
        hide($('#profile-screen'));
        show($('#main-screen'));
      }
      loadBlockedList();
    } catch (e) { toast(e.message); }
  }

  function openSafetyMenu(ctx) {
    const userId = ctx.targetUserId || (ctx.author && ctx.author.id);
    if (!userId || userId === profile.id) return;
    const label = ctx.label || userLabel(ctx.author);
    $('#safety-title').textContent = 'Melden & Sperren';
    $('#safety-sub').textContent = label;
    const box = $('#safety-actions');
    box.innerHTML = '';
    const reportBtn = document.createElement('button');
    reportBtn.type = 'button';
    reportBtn.textContent = '🚩 Inhalt melden';
    reportBtn.onclick = () => {
      hide($('#safety-modal'));
      openReportModal(ctx);
    };
    const blockBtn = document.createElement('button');
    blockBtn.type = 'button';
    blockBtn.className = 'danger';
    blockBtn.textContent = '🚫 Account sperren';
    blockBtn.onclick = async () => {
      hide($('#safety-modal'));
      await blockUserAccount(userId, label);
    };
    box.appendChild(reportBtn);
    box.appendChild(blockBtn);
    show($('#safety-modal'));
  }

  function openReportModal(ctx) {
    reportDraft = {
      targetType: ctx.targetType || 'user',
      targetId: ctx.targetId || ctx.targetUserId || (ctx.author && ctx.author.id),
      targetUserId: ctx.targetUserId || (ctx.author && ctx.author.id),
      label: ctx.label || userLabel(ctx.author),
    };
    $('#report-sub').textContent = reportDraft.label;
    $('#report-details').value = '';
    const box = $('#report-reasons');
    box.innerHTML = '';
    const reasons = config.reportReasons || [];
    reasons.forEach((r) => {
      const lbl = document.createElement('label');
      const inp = document.createElement('input');
      inp.type = 'radio';
      inp.name = 'report-reason';
      inp.value = r.id;
      inp.onchange = () => { $('#report-submit').disabled = false; };
      lbl.appendChild(inp);
      lbl.appendChild(document.createTextNode(r.label));
      box.appendChild(lbl);
    });
    $('#report-submit').disabled = true;
    show($('#report-modal'));
  }

  async function submitReport() {
    if (!reportDraft) return;
    const reason = ($('input[name=report-reason]:checked', $('#report-reasons')) || {}).value;
    if (!reason) { toast('Bitte Grund wählen'); return; }
    $('#report-submit').disabled = true;
    try {
      await api.report({
        targetType: reportDraft.targetType,
        targetId: reportDraft.targetId,
        targetUserId: reportDraft.targetUserId,
        reason,
        details: $('#report-details').value.trim(),
      });
      hide($('#report-modal'));
      toast('Meldung gesendet – danke!');
      reportDraft = null;
    } catch (e) { toast(e.message); }
    $('#report-submit').disabled = false;
  }

  async function loadBlockedList() {
    const box = $('#blocked-list');
    if (!box) return;
    try {
      const j = await api.getBlocks();
      const list = j.blocked || [];
      if (!list.length) {
        box.innerHTML = '<p class="hint">Keine gesperrten Accounts</p>';
        return;
      }
      box.innerHTML = '';
      list.forEach((u) => {
        const row = document.createElement('div');
        row.className = 'blocked-item';
        row.innerHTML =
          '<img src="' + esc(u.photo || '') + '" alt="">' +
          '<span class="blocked-name">' + esc(userLabel(u)) + '</span>' +
          '<button type="button">Entsperren</button>';
        row.querySelector('button').onclick = async () => {
          try {
            await api.unblockUser(u.id);
            toast('Account entsperrt');
            loadBlockedList();
          } catch (e) { toast(e.message); }
        };
        box.appendChild(row);
      });
    } catch (e) {
      box.innerHTML = '<p class="hint">Liste nicht geladen</p>';
    }
  }

  function ttSvg(name, filled) {
    const paths = {
      heart: filled
        ? '<path fill="currentColor" d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>'
        : '<path fill="none" stroke="currentColor" stroke-width="2" d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>',
      comment: '<path fill="none" stroke="currentColor" stroke-width="2" d="M21 14c0 3.3-4 6-9 6-.9 0-1.8-.1-2.6-.3L3 21l1.5-5.2C3.5 14.4 3 12.8 3 11c0-3.3 4-6 9-6s9 2.7 9 6z"/>',
      bookmark: '<path fill="none" stroke="currentColor" stroke-width="2" d="M7 4h10v16l-5-4-5 4V4z"/>',
      gift: '<path fill="none" stroke="currentColor" stroke-width="2" d="M20 12H4V8a2 2 0 012-2h1.2A2.5 2.5 0 018 3.5 2.5 2.5 0 0110.5 6H12m8 6v8a2 2 0 01-2 2H6a2 2 0 01-2-2v-8m16 0H4m16 0l-1.5-4H5.5L4 12M12 6v16"/>',
      share: '<path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M17 9l-8 4 8 4V9z"/><path fill="none" stroke="currentColor" stroke-width="2" d="M5 5v14"/>',
      boost: '<path fill="none" stroke="currentColor" stroke-width="2" d="M4 16l6-12 4 8 6-4-4 12H8L4 16z"/>',
      more: '<circle cx="6" cy="12" r="1.5" fill="currentColor"/><circle cx="12" cy="12" r="1.5" fill="currentColor"/><circle cx="18" cy="12" r="1.5" fill="currentColor"/>',
    };
    return '<svg class="tt-svg" viewBox="0 0 24 24" aria-hidden="true">' + (paths[name] || '') + '</svg>';
  }

  function renderActions(post) {
    if (!post) return;
    const bar = $('#action-bar');
    const author = post.author || {};
    const isOwn = profile && author.id === profile.id;
    const boostCost = (config.monetization && config.monetization.boostCostCoins) || 50;
    const photo = esc(author.photo || '');
    let html = '<div class="tt-action-stack">';
    if (!isOwn) {
      html += '<div class="tt-action-avatar-wrap">' +
        '<button type="button" class="tt-action-avatar" data-act="avatar"><img src="' + photo + '" alt=""></button>' +
        '<button type="button" class="tt-action-follow" data-act="follow">+</button></div>';
    }
    html +=
      '<button type="button" class="tt-action' + (post.liked ? ' tt-action--liked' : '') + '" data-act="like">' +
        ttSvg('heart', post.liked) + '<span>' + fmt(post.likeCount) + '</span></button>' +
      '<button type="button" class="tt-action" data-act="comment">' + ttSvg('comment') + '<span>' + fmt(post.commentCount) + '</span></button>' +
      '<button type="button" class="tt-action' + (post.saved ? ' tt-action--saved' : '') + '" data-act="bookmark">' +
        ttSvg('bookmark', post.saved) + '<span>' + fmt(post.saveCount || 0) + '</span></button>' +
      '<button type="button" class="tt-action" data-act="share">' + ttSvg('share') + '<span>' + fmt(post.shareCount) + '</span></button>';
    if (isOwn) {
      html += '<button type="button" class="tt-action tt-action--extra" data-act="boost" title="Boost">' + ttSvg('boost') + '<span>' + boostCost + '</span></button>';
      html += '<button type="button" class="tt-action tt-action--extra" data-act="gift" title="Geschenk">' + ttSvg('gift') + '<span>' + fmt(post.giftCoins) + '</span></button>';
    }
    if (!isOwn) {
      html += '<button type="button" class="tt-action" data-act="more" title="Melden">' + ttSvg('more') + '</button>';
    }
    html += '<div class="tt-disc"><img src="' + photo + '" alt=""></div></div>';
    bar.innerHTML = html;
    bar.querySelector('[data-act=like]').onclick = () => toggleLike(post);
    bar.querySelector('[data-act=comment]').onclick = () => openComments(post.id);
    const bookmarkBtn = bar.querySelector('[data-act=bookmark]');
    if (bookmarkBtn) {
      bookmarkBtn.onclick = () => {
        post.saved = !post.saved;
        post.saveCount = (post.saveCount || 0) + (post.saved ? 1 : -1);
        if (post.saveCount < 0) post.saveCount = 0;
        renderActions(post);
        toast(post.saved ? 'Zu Favoriten hinzugefügt' : 'Aus Favoriten entfernt');
      };
    }
    const giftBtn = bar.querySelector('[data-act=gift]');
    if (giftBtn) giftBtn.onclick = () => openGifts(post);
    const boostBtn = bar.querySelector('[data-act=boost]');
    if (boostBtn) boostBtn.onclick = () => boostPost(post);
    bar.querySelector('[data-act=share]').onclick = () => sharePost(post);
    const avatarBtn = bar.querySelector('[data-act=avatar]');
    if (avatarBtn) avatarBtn.onclick = () => showProfile(author.id);
    const followBtn = bar.querySelector('[data-act=follow]');
    if (followBtn) {
      followBtn.onclick = async () => {
        try {
          const r = await api.follow(author.id);
          followBtn.style.display = r.following ? 'none' : '';
          toast(r.following ? 'Folge ich' : 'Entfolgt');
        } catch (e) { toast(e.message); }
      };
    }
    const moreBtn = bar.querySelector('[data-act=more]');
    if (moreBtn) {
      moreBtn.onclick = () => openSafetyMenu({
        targetType: 'post',
        targetId: post.id,
        targetUserId: author.id,
        author,
        label: userLabel(author) + ' · Clip',
      });
    }
    api.viewPost(post.id).catch(() => {});
  }

  async function boostPost(post) {
    const cost = (config.monetization && config.monetization.boostCostCoins) || 50;
    if (!confirm('Post 24h boosten für ' + cost + ' Coins?')) return;
    try {
      const j = await api.boostPost(post.id);
      if (profile) profile.coins = j.coins;
      post.isBoosted = true;
      toast('Post geboostet! 🚀');
      renderActions(post);
    } catch (e) { toast(e.message); }
  }

  function fmt(n) { return n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n || 0); }

  function fmtViews(n) {
    n = n || 0;
    if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
    if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, '') + 'K';
    return String(n);
  }

  function buildExploreCategories() {
    const box = $('#explore-categories');
    if (!box || box.dataset.ready) return;
    (config.niches || []).forEach((n) => {
      const btn = document.createElement('button');
      btn.type = 'button';
      btn.dataset.niche = n.id;
      btn.textContent = n.label || n.id;
      box.appendChild(btn);
    });
    box.dataset.ready = '1';
    $$('#explore-categories button').forEach((btn) => {
      btn.onclick = () => {
        exploreNiche = btn.dataset.niche || '';
        $$('#explore-categories button').forEach((b) => b.classList.toggle('active', b === btn));
        loadExplore(false);
      };
    });
  }

  function filterExplorePosts(list) {
    const q = exploreSearchQuery.trim().toLowerCase();
    if (!q) return list;
    return list.filter((p) => {
      const author = p.author || {};
      const name = ((author.username || '') + ' ' + (author.displayName || '') + ' ' + (p.caption || '')).toLowerCase();
      return name.includes(q);
    });
  }

  function renderExploreGrid() {
    const grid = $('#explore-grid');
    grid.innerHTML = '';
    const items = filterExplorePosts(explorePosts);
    if (!items.length && !exploreLoading) {
      grid.innerHTML = '<p class="explore-empty">Noch keine Clips in dieser Kategorie</p>';
      return;
    }
    items.forEach((post) => {
      const idx = explorePosts.indexOf(post);
      const card = document.createElement('div');
      card.className = 'explore-card';
      const author = post.author || {};
      const handle = author.username ? '@' + author.username : (author.displayName || 'Nutzer');
      const views = fmtViews(post.likeCount || post.viewCount || 0);
      card.innerHTML =
        '<div class="explore-thumb">' +
        (post.thumbUrl
          ? '<img src="' + post.thumbUrl + '" alt="" loading="lazy">'
          : (post.type === 'photo'
            ? '<img src="' + post.mediaUrl + '" alt="" loading="lazy">'
            : '<video src="' + post.mediaUrl + '" muted preload="metadata" poster="' + (post.thumbUrl || '') + '"></video>')) +
        '<span class="explore-views">♥ ' + views + '</span></div>' +
        '<div class="explore-meta">' +
        (author.photo ? '<img class="explore-author-avatar" src="' + author.photo + '" alt="">' : '') +
        '<span class="explore-author-name">' + esc(handle) +
        (author.verified ? '<span class="explore-verified"> ✓</span>' : '') + '</span></div>';
      card.onclick = () => openExplorePost(idx);
      grid.appendChild(card);
    });
  }

  function openExplorePost(idx) {
    posts = explorePosts.slice();
    currentIdx = Math.max(0, Math.min(idx, posts.length - 1));
    showAds = false;
    buildFeedItems();
    hide($('#explore-screen'));
    hide($('#profile-screen'));
    hide($('#live-screen'));
    show($('#main-screen'));
    renderFeed();
    playVisible();
    const container = $('#feed-container');
    const el = container.querySelector('[data-idx="' + currentIdx + '"]');
    if (el) el.scrollIntoView({ behavior: 'instant', block: 'start' });
  }

  async function loadExplore(append) {
    if (exploreLoading) return;
    exploreLoading = true;
    const status = $('#explore-status');
    if (!append) {
      explorePosts = [];
      exploreCursor = '';
      $('#explore-grid').innerHTML = '<p class="explore-empty">Laden…</p>';
    } else {
      show(status);
      status.textContent = 'Laden…';
    }
    try {
      const j = await api.feed({
        mode: 'explore',
        niche: exploreNiche || undefined,
        cursor: append ? exploreCursor : '',
        limit: 24,
      });
      if (append) explorePosts = explorePosts.concat(j.posts || []);
      else explorePosts = j.posts || [];
      exploreHasMore = !!j.hasMore;
      exploreCursor = j.nextCursor || '';
      renderExploreGrid();
    } catch (e) {
      if (!append) $('#explore-grid').innerHTML = '<p class="explore-empty">' + esc(e.message) + '</p>';
      else toast(e.message);
    }
    hide(status);
    exploreLoading = false;
  }

  async function showExplore() {
    setSidebarActive('explore');
    hide($('#main-screen'));
    hide($('#profile-screen'));
    hide($('#live-screen'));
    show($('#explore-screen'));
    buildExploreCategories();
    if (!explorePosts.length) await loadExplore(false);
    else renderExploreGrid();
  }

  $('#explore-back').onclick = () => {
    hide($('#explore-screen'));
    show($('#main-screen'));
    $$('#bottom-nav button').forEach((b) => b.classList.toggle('active', b.dataset.nav === 'home'));
  };

  $('#explore-search').oninput = () => {
    exploreSearchQuery = $('#explore-search').value;
    renderExploreGrid();
  };

  $('#explore-grid').onscroll = () => {
    const el = $('#explore-grid');
    if (!el || !exploreHasMore || exploreLoading) return;
    if (el.scrollTop + el.clientHeight >= el.scrollHeight - 240) loadExplore(true);
  };

  async function toggleLike(post) {
    try {
      const j = await api.likePost(post.id);
      post.liked = j.liked;
      post.likeCount = j.likeCount;
      renderActions(post);
    } catch (e) { toast(e.message); }
  }

  function setupScrollObserver() {
    const container = $('#feed-container');
    container.onscroll = () => {
      const items = container.querySelectorAll('.feed-item, .feed-ad');
      let best = 0, bestDist = Infinity;
      items.forEach((el) => {
        const rect = el.getBoundingClientRect();
        const dist = Math.abs(rect.top);
        const idx = parseInt(el.dataset.idx, 10);
        if (dist < bestDist) { bestDist = dist; best = idx; }
      });
      if (best !== currentIdx) {
        currentIdx = best;
        renderCurrentItem();
        playVisible();
      }
      if (feedHasMore && !feedLoading && currentIdx >= feedItems.length - 3) {
        loadFeed(true);
      }
    };
  }

  function playVisible() {
    $$('.feed-item video').forEach((v) => { v.pause(); v.muted = true; });
    const container = $('#feed-container');
    const item = container.querySelector('[data-idx="' + currentIdx + '"][data-kind="post"]');
    if (!item) return;
    const v = item.querySelector('video');
    if (v) { v.muted = false; v.play().catch(() => { v.muted = true; v.play(); }); }
  }

  $$('#top-bar .tab').forEach((tab) => {
    tab.onclick = async () => {
      $$('#top-bar .tab').forEach((t) => t.classList.remove('active'));
      tab.classList.add('active');
      feedMode = tab.dataset.mode;
      setSidebarActive(feedMode === 'following' ? 'following' : 'home');
      stopStudioPreview();
      hide($('#live-screen'));
      show($('#main-screen'));
      await loadFeed();
    };
  });

  $('#tab-live').onclick = async () => {
    $$('#top-bar .tab').forEach((t) => t.classList.remove('active'));
    feedMode = 'live';
    await showLiveStudio();
  };

  $('#tt-top-search').onclick = () => showExplore();

  const sideSearch = $('#tt-side-search');
  if (sideSearch) {
    sideSearch.onkeydown = (e) => {
      if (e.key === 'Enter') {
        exploreSearchQuery = sideSearch.value.trim();
        showExplore();
        const es = $('#explore-search');
        if (es) es.value = sideSearch.value;
        loadExplore();
      }
    };
  }

  $$('.tt-side-item').forEach((btn) => {
    btn.onclick = async () => {
      const side = btn.dataset.side;
      setSidebarActive(side);
      if (side === 'home') {
        feedMode = btn.dataset.feed || 'foryou';
        $$('#top-bar .tab').forEach((t) => t.classList.toggle('active', t.dataset.mode === feedMode));
        hide($('#profile-screen'));
        hide($('#explore-screen'));
        hide($('#inbox-screen'));
        hide($('#live-screen'));
        stopStudioPreview();
        stopInboxPoll();
        show($('#main-screen'));
        await loadFeed();
      } else if (side === 'following') {
        feedMode = 'following';
        $$('#top-bar .tab').forEach((t) => t.classList.toggle('active', t.dataset.mode === 'following'));
        hide($('#profile-screen'));
        hide($('#explore-screen'));
        hide($('#inbox-screen'));
        hide($('#live-screen'));
        stopStudioPreview();
        stopInboxPoll();
        show($('#main-screen'));
        await loadFeed();
      } else if (side === 'explore') {
        await showExplore();
      } else if (side === 'live') {
        feedMode = 'live';
        $$('#top-bar .tab').forEach((t) => t.classList.remove('active'));
        await showLiveStudio();
      } else if (side === 'upload') {
        openEditor('feed');
      } else if (side === 'profile') {
        await showProfile(profile.id);
      } else if (side === 'inbox') {
        await showInbox();
      }
    };
  });

  const scrollUp = $('#tt-scroll-up');
  const scrollDown = $('#tt-scroll-down');
  if (scrollUp) scrollUp.onclick = () => scrollToFeedIdx(currentIdx - 1);
  if (scrollDown) scrollDown.onclick = () => scrollToFeedIdx(currentIdx + 1);

  function stopLivePoll() {
    if (livePollTimer) { clearInterval(livePollTimer); livePollTimer = null; }
  }

  function playGiftSound(giftType) {
    if (window.VibeLoopSounds) VibeLoopSounds.play(giftType || 'rose');
  }

  function mySenderName() {
    if (!profile) return 'Du';
    return profile.username ? '@' + profile.username : (profile.displayName || 'Du');
  }

  function showGiftNotice(emoji, fromName, giftLabel, giftType, playSound) {
    if (playSound !== false && giftType) playGiftSound(giftType);
    const name = fromName || 'Jemand';
    const label = giftLabel || '';
    return { emoji, fromName: name, giftLabel: label };
  }

  function floatGift(containerId, emoji, fromName, giftType, playSound, giftLabel) {
    const box = $(containerId);
    if (!box) return;
    const info = showGiftNotice(emoji, fromName, giftLabel, giftType, playSound);
    const el = document.createElement('div');
    el.className = 'live-gift-float';
    el.style.bottom = (18 + Math.random() * 28) + '%';
    el.innerHTML =
      '<div class="live-gift-float-card">' +
      '<span class="gift-float-emoji">' + em(info.emoji, 'cp-emoji-pop cp-emoji-glow', 40) + '</span>' +
      '<div class="gift-float-meta">' +
      '<span class="gift-float-name">' + esc(info.fromName) + '</span>' +
      (info.giftLabel ? '<span class="gift-float-label">' + esc(info.giftLabel) + '</span>' : '') +
      '</div></div>';
    box.appendChild(el);
    setTimeout(() => el.remove(), 3000);
    return info;
  }

  function resetLiveChat() {
    liveChatSince = '';
    liveChatSeenIds = new Set();
    ['#live-chat-messages', '#live-broadcast-chat'].forEach((sel) => {
      const el = $(sel);
      if (el) el.innerHTML = '';
    });
  }

  function chatUserLabel(from) {
    if (!from) return '';
    return from.username ? '@' + from.username : (from.displayName || 'Nutzer');
  }

  function renderChatMessage(msg, container) {
    if (!container || !msg || liveChatSeenIds.has(msg.id)) return;
    liveChatSeenIds.add(msg.id);
    const row = document.createElement('div');
    if (msg.kind === 'system') {
      row.className = 'live-chat-msg system';
      row.innerHTML = '<div class="live-chat-body">' + esc(msg.body) + '</div>';
    } else if (msg.kind === 'join') {
      row.className = 'live-chat-msg join';
      row.innerHTML = esc(msg.body);
    } else {
      const from = msg.from || {};
      const badges =
        (from.tierLevel ? '<span class="live-chat-badge tier">' + from.tierLevel + '</span>' : '') +
        (from.isPremium ? '<span class="live-chat-badge premium">+</span>' : '');
      row.className = 'live-chat-msg';
      row.innerHTML =
        (from.photo ? '<img class="live-chat-avatar" src="' + esc(from.photo) + '" alt="">' : '') +
        '<div class="live-chat-body">' + badges +
        '<span class="live-chat-name">' + esc(chatUserLabel(from)) + '</span>' +
        esc(msg.body) + '</div>';
    }
    container.appendChild(row);
    while (container.children.length > 120) container.removeChild(container.firstChild);
    container.scrollTop = container.scrollHeight;
  }

  function renderTopGifters(gifters, containerId) {
    const box = $(containerId);
    if (!box) return;
    box.innerHTML = '';
    (gifters || []).forEach((g) => {
      const d = document.createElement('div');
      d.className = 'live-top-gifter rank-' + g.rank;
      d.title = (g.username ? '@' + g.username : g.displayName) + ' · ' + fmt(g.coins) + ' Coins';
      d.innerHTML = '<span class="rank">' + g.rank + '</span><img src="' + esc(g.photo || '') + '" alt="">';
      box.appendChild(d);
    });
  }

  async function pollLiveChat(liveId, messageSelectors, meta) {
    try {
      const j = await api.getLiveChat(liveId, { since: liveChatSince || undefined });
      (j.messages || []).forEach((m) => {
        messageSelectors.forEach((sel) => renderChatMessage(m, $(sel)));
      });
      if (j.latestAt) liveChatSince = j.latestAt;
      if (meta) {
        if (meta.viewerEl && j.viewerCount != null) {
          $(meta.viewerEl).textContent = 'Zuschauer ' + fmt(j.viewerCount);
        }
        if (meta.giftersEl) renderTopGifters(j.topGifters, meta.giftersEl);
      }
    } catch (_) {}
  }

  async function sendLiveChatMessage(liveId, text, messageSelectors) {
    const body = String(text || '').trim();
    if (!body) return;
    if (!rejectCommunityText(body)) return;
    try {
      const r = await api.sendLiveChat(liveId, body);
      if (r.message) {
        messageSelectors.forEach((sel) => renderChatMessage(r.message, $(sel)));
        if (r.message.at) liveChatSince = r.message.at;
      }
    } catch (e) { toast(e.message); }
  }

  function bindLiveChatInput({ liveId, inputId, sendId, messageSelectors }) {
    const input = $(inputId);
    const send = $(sendId);
    if (!input || !send) return;
    const submit = () => {
      const text = input.value;
      input.value = '';
      sendLiveChatMessage(liveId, text, messageSelectors);
    };
    send.onclick = submit;
    input.onkeydown = (e) => {
      if (e.key === 'Enter') { e.preventDefault(); submit(); }
    };
  }

  async function renderLiveSuggestList(currentLiveId) {
    const list = $('#live-suggest-list');
    if (!list) return;
    try {
      const j = await api.getLives();
      list.innerHTML = '';
      (j.lives || []).forEach((l) => {
        if (l.liveId === currentLiveId) return;
        const btn = document.createElement('button');
        btn.type = 'button';
        btn.className = 'live-suggest-item';
        btn.innerHTML = '<img src="' + esc(l.host.photo || '') + '" alt="">' +
          '<div><strong>' + esc(l.host.displayName || l.host.username || 'Live') + '</strong>' +
          '<span>👀 ' + fmt(l.viewerPeak) + '</span></div>';
        btn.onclick = () => { closeLiveWatch().then(() => watchLive(l.liveId)); };
        list.appendChild(btn);
      });
      if (!list.children.length) {
        list.innerHTML = '<p style="font-size:.75rem;color:var(--muted)">Keine weiteren Lives</p>';
      }
    } catch (_) {}
  }

  function showFeedGiftToast(emoji, fromName, giftLabel, giftType) {
    showGiftNotice(emoji, fromName, giftLabel, giftType, true);
    const t = $('#feed-gift-toast');
    t.innerHTML =
      '<div class="live-gift-float-card">' +
      '<span class="gift-float-emoji">' + em(emoji, 'cp-emoji-xl cp-emoji-pop cp-emoji-glow', 56) + '</span>' +
      '<div class="gift-float-meta">' +
      '<span class="gift-float-name">' + esc(fromName) + '</span>' +
      '<span class="gift-float-label">' + esc(giftLabel || 'Geschenk') + '</span>' +
      '</div></div>';
    t.classList.remove('hidden');
    t.classList.remove('show');
    void t.offsetWidth;
    t.classList.add('show');
    setTimeout(() => { t.classList.add('hidden'); t.classList.remove('show'); }, 2800);
  }

  function pushGiftChatNotice(g, chatSelectors) {
    const fakeMsg = {
      id: 'gift_' + g.at + g.from + g.type,
      kind: 'join',
      body: g.from + ' sendet ' + (g.label || 'Geschenk') + ' ' + (g.emoji || '🎁'),
    };
    (chatSelectors || []).forEach((sel) => renderChatMessage(fakeMsg, $(sel)));
  }

  async function pollLiveGifts(liveId, floatBoxId, chatSelectors) {
    try {
      const j = await api.getLiveGifts(liveId);
      (j.gifts || []).forEach((g) => {
        const key = g.at + g.from + g.type;
        if (seenLiveGiftKeys.has(key)) return;
        seenLiveGiftKeys.add(key);
        floatGift(floatBoxId, g.emoji, g.from, g.type, true, g.label);
        pushGiftChatNotice(g, chatSelectors);
      });
    } catch (e) {}
  }

  function formatPkTime(sec) {
    const s = Math.max(0, sec || 0);
    const m = Math.floor(s / 60);
    const r = s % 60;
    return m + ':' + String(r).padStart(2, '0');
  }

  function pkHostLabel(h) {
    if (!h) return '?';
    return h.username ? '@' + h.username : (h.displayName || 'Host');
  }

  function renderLivePkBar(match, barId) {
    const bar = $(barId);
    if (!bar) return;
    if (!match || match.status !== 'active') {
      hide(bar);
      return;
    }
    show(bar);
    const total = Math.max(1, (match.scoreA || 0) + (match.scoreB || 0));
    const pctA = Math.round(((match.scoreA || 0) / total) * 100);
    const pctB = 100 - pctA;
    bar.innerHTML =
      '<div class="live-pk-bar-inner">' +
      '<div class="live-pk-host"><img src="' + esc(match.hostA?.photo || '') + '" alt="">' +
      '<span>' + esc(pkHostLabel(match.hostA)) + '</span><span class="live-pk-score">' + fmt(match.scoreA) + '</span></div>' +
      '<div class="live-pk-progress"><div class="live-pk-progress-a" style="width:' + pctA + '%"></div></div>' +
      '<span class="live-pk-timer">' + formatPkTime(match.timeLeftSec) + '</span>' +
      '<div class="live-pk-progress"><div class="live-pk-progress-b" style="width:' + pctB + '%"></div></div>' +
      '<div class="live-pk-host right"><span class="live-pk-score">' + fmt(match.scoreB) + '</span>' +
      '<span>' + esc(pkHostLabel(match.hostB)) + '</span><img src="' + esc(match.hostB?.photo || '') + '" alt=""></div>' +
      '</div>';
  }

  function clearPkOpponentVideo() {
    if (pkOpponentCleanup) { pkOpponentCleanup(); pkOpponentCleanup = null; }
    ['#live-pk-opponent-v-watch', '#live-pk-opponent-v-host'].forEach((sel) => {
      const v = $(sel);
      if (v) { v.srcObject = null; v.removeAttribute('src'); hide(v); }
    });
    ['#live-pk-opponent-img-watch', '#live-pk-opponent-img-host'].forEach((sel) => {
      const img = $(sel);
      if (img) { img.removeAttribute('src'); hide(img); }
    });
    ['#live-pk-opponent-watch', '#live-pk-opponent-host'].forEach((sel) => hide($(sel)));
    ['#live-watch-video-wrap', '#live-broadcast-video-wrap'].forEach((sel) => {
      const w = $(sel);
      if (w) w.classList.remove('live-pk-split');
    });
  }

  async function connectPkOpponent(match, videoId, imgId, boxId, wrapId) {
    const opp = match && match.opponentLive;
    const box = $(boxId);
    const wrap = $(wrapId);
    if (!match || match.status !== 'active' || !opp) {
      clearPkOpponentVideo();
      return;
    }
    if (wrap) wrap.classList.add('live-pk-split');
    show(box);
    const v = $(videoId);
    const img = $(imgId);
    if (opp.whepUrl && window.VibeLoopLive && v) {
      show(v);
      hide(img);
      if (pkOpponentCleanup) pkOpponentCleanup();
      pkOpponentCleanup = await VibeLoopLive.watch({
        liveId: opp.liveId,
        videoEl: v,
        api,
        whepUrl: opp.whepUrl,
        onFallback: () => {
          if (opp.host && opp.host.photo) {
            hide(v);
            show(img);
            img.src = opp.host.photo;
          }
        },
      });
    } else if (opp.host && opp.host.photo && img) {
      hide(v);
      show(img);
      img.src = opp.host.photo;
    }
  }

  function renderLivePkInviteBanner(match) {
    const box = $('#live-pk-invite-banner');
    if (!box) return;
    if (!match || match.status !== 'pending' || !match.canAccept) {
      hide(box);
      return;
    }
    show(box);
    box.innerHTML =
      '<span>⚔️ ' + esc(pkHostLabel(match.hostA)) + ' fordert dich zum LIVE Match!</span>' +
      '<button type="button" class="accept">Annehmen</button>' +
      '<button type="button" class="decline">Ablehnen</button>';
    box.querySelector('.accept').onclick = async () => {
      try {
        const j = await api.acceptLiveMatch(activeLiveId);
        liveMatchState = j.match;
        renderLivePkUi(liveMatchState, 'broadcast');
        toast('Match gestartet!');
      } catch (e) { toast(e.message); }
    };
    box.querySelector('.decline').onclick = async () => {
      try {
        await api.declineLiveMatch(activeLiveId);
        liveMatchState = null;
        hide(box);
        toast('Herausforderung abgelehnt');
      } catch (e) { toast(e.message); }
    };
  }

  function renderLivePkLikes(match) {
    const box = $('#live-pk-likes');
    if (!box) return;
    if (!match || match.status !== 'active') {
      hide(box);
      return;
    }
    show(box);
    $('#live-pk-like-a-label').textContent = pkHostLabel(match.hostA);
    $('#live-pk-like-b-label').textContent = pkHostLabel(match.hostB);
    $('#live-pk-like-a').onclick = () => tapLivePkLike('a');
    $('#live-pk-like-b').onclick = () => tapLivePkLike('b');
  }

  async function tapLivePkLike(side) {
    if (!activeLiveId) return;
    try {
      const j = await api.likeLiveMatch(activeLiveId, side);
      liveMatchState = j.match;
      renderLivePkBar(liveMatchState, '#live-pk-bar-watch');
      if (window.VibeLoopSounds) VibeLoopSounds.play('clap');
    } catch (e) { toast(e.message); }
  }

  function renderLiveGiftStripsForMatch(match, liveId) {
    const stripA = $('#live-gift-strip');
    const stripB = $('#live-gift-strip-b');
    if (!match || match.status !== 'active' || !match.hostA || !match.hostB) {
      hide(stripB);
      if (liveHostId) renderLiveGiftStrip(liveHostId, liveId);
      return;
    }
    show(stripA);
    show(stripB);
    function fillStrip(strip, host, label) {
      strip.innerHTML = '<span class="live-pk-gift-label">' + esc(label) + '</span>';
      Object.entries(config.gifts || {}).forEach(([id, g]) => {
        const chip = document.createElement('button');
        chip.type = 'button';
        chip.className = 'live-gift-chip';
        chip.innerHTML = em(g.emoji, 'cp-emoji-glow', 36) + '<small>' + g.coins + '</small>';
        chip.onclick = async () => {
          try {
            const r = await api.sendGift({ toId: host.id, liveId, giftType: id });
            profile.coins = r.coins ?? r.coinsLeft ?? profile.coins;
            $('#live-watch-coins').textContent = fmt(profile.coins || 0) + ' 🪙';
            const sender = r.fromName || mySenderName();
            floatGift('#live-watch-gift-floats', g.emoji, sender, id, false, g.label);
            pushGiftChatNotice({ at: Date.now(), from: sender, type: id, emoji: g.emoji, label: g.label }, ['#live-chat-messages']);
            playGiftSound(id);
            await pollLiveMatch(liveId);
          } catch (e) { toast(e.message); }
        };
        strip.appendChild(chip);
      });
    }
    fillStrip(stripA, match.hostA, pkHostLabel(match.hostA));
    fillStrip(stripB, match.hostB, pkHostLabel(match.hostB));
  }

  function renderLivePkUi(match, mode) {
    liveMatchState = match;
    if (mode === 'watch') {
      renderLivePkBar(match, '#live-pk-bar-watch');
      renderLivePkLikes(match);
      renderLiveGiftStripsForMatch(match, activeLiveId);
      connectPkOpponent(match, '#live-pk-opponent-v-watch', '#live-pk-opponent-img-watch', '#live-pk-opponent-watch', '#live-watch-video-wrap');
    } else if (mode === 'broadcast') {
      renderLivePkBar(match, '#live-pk-bar-host');
      renderLivePkInviteBanner(match);
      connectPkOpponent(match, '#live-pk-opponent-v-host', '#live-pk-opponent-img-host', '#live-pk-opponent-host', '#live-broadcast-video-wrap');
    }
    if (match && match.status === 'ended' && lastMatchStatus === 'active') {
      if (match.isDraw) toast('Match unentschieden!');
      else if (match.winner) toast('Sieger: ' + pkHostLabel(match.winner) + ' 🏆');
      setTimeout(() => clearPkOpponentVideo(), 4000);
    }
    lastMatchStatus = match ? match.status : null;
  }

  async function pollLiveMatch(liveId) {
    try {
      const j = await api.getLiveMatch(liveId);
      const match = j.match;
      if ($('#live-watch-screen') && !$('#live-watch-screen').classList.contains('hidden')) {
        renderLivePkUi(match, 'watch');
      } else if ($('#live-broadcast-screen') && !$('#live-broadcast-screen').classList.contains('hidden')) {
        renderLivePkUi(match, 'broadcast');
      }
      return match;
    } catch (_) { return null; }
  }

  async function resolveMyLiveId() {
    if (activeLiveId) return activeLiveId;
    if (!profile || !profile.isLive) return null;
    const j = await api.getLives();
    const mine = (j.lives || []).find((l) => l.host && l.host.id === profile.id);
    return mine ? mine.liveId : null;
  }

  async function inviteLiveMatchOpponent() {
    const myLiveId = await resolveMyLiveId();
    if (!profile || !profile.isLive || !myLiveId) {
      toast('Starte zuerst ein Live');
      return;
    }
    activeLiveId = myLiveId;
    try {
      const j = await api.getLives();
      const others = (j.lives || []).filter((l) => l.liveId !== myLiveId && l.host && l.host.id !== profile.id);
      if (!others.length) { toast('Kein anderer Live-Host online'); return; }
      const names = others.map((l, i) => (i + 1) + '. ' + (l.host.displayName || l.host.username || 'Live')).join('\n');
      const pick = prompt('Gegner wählen (Nummer):\n' + names);
      const idx = parseInt(pick, 10) - 1;
      if (!Number.isFinite(idx) || idx < 0 || idx >= others.length) return;
      const r = await api.inviteLiveMatch(myLiveId, others[idx].liveId);
      liveMatchState = r.match;
      toast('Herausforderung gesendet an ' + (others[idx].host.displayName || others[idx].host.username));
    } catch (e) { toast(e.message); }
  }

  async function stopLiveCamera() {
    if (window.VibeLoopLive) VibeLoopLive.stopBroadcast();
    if (liveCamStream) {
      liveCamStream.getTracks().forEach((t) => t.stop());
      liveCamStream = null;
    }
    const v = $('#live-broadcast-v');
    if (v) v.srcObject = null;
  }

  async function startLiveBroadcast(title, liveId, liveMeta) {
    activeLiveId = liveId;
    seenLiveGiftKeys.clear();
    resetLiveChat();
    hide($('#live-screen'));
    hide($('#main-screen'));
    show($('#live-broadcast-screen'));
    $('#live-broadcast-title').textContent = title || 'Live';
    $('#live-broadcast-host-name').textContent = profile.username ? '@' + profile.username : (profile.displayName || 'Du');
    try {
      if (window.VibeLoopLive) {
        liveCamStream = await VibeLoopLive.startBroadcast({
          liveId,
          videoEl: $('#live-broadcast-v'),
          api,
          whipUrl: liveMeta && liveMeta.whipUrl,
        });
        if (VibeLoopLive.getMode && VibeLoopLive.getMode() === 'stream') {
          toast('Cloudflare Stream · Echtzeit-Live');
        }
      } else {
        liveCamStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user' }, audio: true });
        $('#live-broadcast-v').srcObject = liveCamStream;
      }
    } catch (e) {
      toast('Kamera nicht verfügbar – Live ohne Video');
    }
    bindLiveChatInput({
      liveId,
      inputId: '#live-broadcast-chat-input',
      sendId: '#live-broadcast-chat-send',
      messageSelectors: ['#live-broadcast-chat'],
    });
    const hostChatMeta = {
      viewerEl: '#live-broadcast-viewer-count',
      giftersEl: '#live-broadcast-top-gifters',
    };
    await pollLiveChat(liveId, ['#live-broadcast-chat'], hostChatMeta);
    stopLivePoll();
    livePollTimer = setInterval(async () => {
      try {
        const j = await api.getLive(liveId);
        const mode = window.VibeLoopLive && VibeLoopLive.getMode ? VibeLoopLive.getMode() : '';
        const segs = window.VibeLoopLive && VibeLoopLive.segmentCount ? VibeLoopLive.segmentCount() : 0;
        const liveLine = mode === 'stream' ? 'Stream' : (j.latestSeq ? 'Seq ' + fmt(j.latestSeq) : segs + ' Seg.');
        $('#live-broadcast-stats').textContent =
          '👀 ' + fmt(j.viewerPeak) + ' · 🎁 ' + fmt(j.totalGiftCoins) + ' · ' + liveLine;
        await pollLiveChat(liveId, ['#live-broadcast-chat'], hostChatMeta);
        await pollLiveGifts(liveId, '#live-gift-floats', ['#live-broadcast-chat']);
        await pollLiveMatch(liveId);
      } catch (e) {}
    }, 2500);
    await pollLiveMatch(liveId);
  }

  async function endLiveBroadcast() {
    stopLivePoll();
    clearPkOpponentVideo();
    liveMatchState = null;
    await stopLiveCamera();
    try { await api.endLive(); } catch (e) {}
    profile.isLive = false;
    activeLiveId = null;
    hide($('#live-broadcast-screen'));
    await showLiveStudio();
    toast('Live beendet');
  }

  function showLiveFallback(host) {
    const v = $('#live-watch-v');
    const img = $('#live-watch-img');
    v.srcObject = null;
    v.removeAttribute('src');
    hide(v);
    hide(img);
    if (host.introVideo) {
      show(v);
      v.src = host.introVideo;
      v.loop = true;
      v.muted = true;
      v.classList.remove('hidden');
      v.play().catch(() => {});
    } else if (host.photo) {
      show(img);
      img.src = host.photo;
      img.classList.remove('hidden');
    }
  }

  async function watchLive(liveId) {
    activeLiveId = liveId;
    seenLiveGiftKeys.clear();
    resetLiveChat();
    try {
      await api.joinLive(liveId);
      const j = await api.getLive(liveId);
      const host = j.host || {};
      liveHostId = host.id;
      hide($('#live-screen'));
      hide($('#main-screen'));
      show($('#live-watch-screen'));
      const v = $('#live-watch-v');
      const img = $('#live-watch-img');
      const connecting = $('#live-watch-connecting');
      v.srcObject = null;
      v.removeAttribute('src');
      v.loop = false;
      hide(v);
      hide(img);
      show(connecting);
      connecting.textContent = 'Verbinde mit Live…';
      $('#live-host-avatar').src = host.photo || '';
      $('#live-host-name').textContent = host.username ? '@' + host.username : (host.displayName || 'Live');
      $('#live-host-title').textContent = j.title || '';
      $('#live-viewer-count').textContent = 'Zuschauer ' + fmt(j.viewerPeak);
      $('#live-watch-coins').textContent = fmt(profile.coins || 0) + ' 🪙';
      const followBtn = $('#live-host-follow');
      if (host.id && host.id !== profile.id) {
        show(followBtn);
        try {
          const pj = await api.getProfile(host.id);
          liveHostFollowing = !!pj.isFollowing;
          followBtn.textContent = liveHostFollowing ? 'Entfolgen' : 'Folgen';
          followBtn.classList.toggle('following', liveHostFollowing);
        } catch (_) {
          followBtn.textContent = 'Folgen';
        }
        followBtn.onclick = async () => {
          const r = await api.follow(host.id);
          liveHostFollowing = r.following;
          followBtn.textContent = r.following ? 'Entfolgen' : 'Folgen';
          followBtn.classList.toggle('following', r.following);
        };
      } else {
        hide(followBtn);
      }
      $('#live-watch-share').onclick = () => {
        const url = location.origin + '/?live=' + encodeURIComponent(liveId);
        if (navigator.share) navigator.share({ title: 'Flarlo Live', url }).catch(() => {});
        else navigator.clipboard.writeText(url).then(() => toast('Live-Link kopiert'));
      };
      renderLiveGiftStrip(host.id, liveId);
      if (j.match) renderLivePkUi(j.match, 'watch');
      renderLiveSuggestList(liveId);
      bindLiveChatInput({
        liveId,
        inputId: '#live-chat-input',
        sendId: '#live-chat-send',
        messageSelectors: ['#live-chat-messages'],
      });
      $('#live-chat-emoji').onclick = () => {
        const emojis = ['😊', '🔥', '❤️', '👏', '😂', '🎉', '💯', '🙌'];
        const input = $('#live-chat-input');
        input.value += emojis[Math.floor(Math.random() * emojis.length)];
        input.focus();
      };
      $('#live-recharge-btn').onclick = () => openCoinsShop();
      const watchChatMeta = { viewerEl: '#live-viewer-count', giftersEl: '#live-top-gifters' };
      await pollLiveChat(liveId, ['#live-chat-messages'], watchChatMeta);
      stopLivePoll();
      livePollTimer = setInterval(async () => {
        try {
          await pollLiveChat(liveId, ['#live-chat-messages'], watchChatMeta);
          await pollLiveGifts(liveId, '#live-watch-gift-floats', ['#live-chat-messages']);
          await pollLiveMatch(liveId);
        } catch (e) {}
      }, 2200);
      await pollLiveMatch(liveId);
      if (window.VibeLoopLive) {
        if (liveWatchCleanup) liveWatchCleanup();
        liveWatchCleanup = await VibeLoopLive.watch({
          liveId,
          videoEl: v,
          api,
          whepUrl: j.whepUrl || null,
          onConnected: () => {
            hide(connecting);
            hide(img);
            toast(j.mode === 'stream' ? 'Echtzeit-Live verbunden' : 'Live-Stream verbunden');
          },
          onFallback: () => {
            hide(connecting);
            showLiveFallback(host);
            toast('Stream noch nicht bereit – Vorschau');
          },
        });
      } else {
        hide(connecting);
        showLiveFallback(host);
      }
    } catch (e) { toast(e.message); }
  }

  function renderLiveGiftStrip(hostId, liveId) {
    const strip = $('#live-gift-strip');
    strip.innerHTML = '';
    Object.entries(config.gifts || {}).forEach(([id, g]) => {
      const chip = document.createElement('button');
      chip.type = 'button';
      chip.className = 'live-gift-chip';
      chip.innerHTML = em(g.emoji, 'cp-emoji-glow', 40) + '<small>' + g.coins + ' ' + em('🪙', '', 14) + '</small>';
      chip.onclick = async () => {
        try {
          const r = await api.sendGift({ toId: hostId, liveId: liveId, giftType: id });
          profile.coins = r.coins ?? r.coinsLeft ?? profile.coins;
          $('#live-watch-coins').textContent = 'Deine Coins: ' + fmt(profile.coins || 0);
          const sender = r.fromName || mySenderName();
          floatGift('#live-watch-gift-floats', g.emoji, sender, id, false, g.label);
          pushGiftChatNotice({ at: Date.now(), from: sender, type: id, emoji: g.emoji, label: g.label }, ['#live-chat-messages']);
          playGiftSound(id);
          toast(sender + ' sendet ' + g.emoji + ' ' + g.label);
        } catch (e) { toast(e.message); }
      };
      strip.appendChild(chip);
    });
  }

  async function closeLiveWatch() {
    stopLivePoll();
    clearPkOpponentVideo();
    liveMatchState = null;
    if (window.VibeLoopLive) VibeLoopLive.stopWatch();
    if (liveWatchCleanup) { liveWatchCleanup(); liveWatchCleanup = null; }
    if (activeLiveId) api.leaveLive(activeLiveId).catch(() => {});
    const v = $('#live-watch-v');
    if (v) { v.srcObject = null; v.removeAttribute('src'); }
    hide($('#live-watch-connecting'));
    activeLiveId = null;
    liveHostId = null;
    resetLiveChat();
    hide($('#live-watch-screen'));
    show($('#live-screen'));
  }

  function stopStudioPreview() {
    stopStudioStatusBar();
    if (studioPreviewStream) {
      studioPreviewStream.getTracks().forEach((t) => t.stop());
      studioPreviewStream = null;
    }
    const v = $('#studio-preview-v');
    if (v) {
      v.srcObject = null;
      hide(v);
    }
    show($('#studio-preview-placeholder'));
    $('#studio-preview-placeholder').innerHTML =
      '<span class="studio-preview-icon">📷</span><p>Kamera-Vorschau</p>';
  }

  async function startStudioPreview(mode) {
    if (profile && profile.isLive) return;
    const src = mode || studioSourceMode;
    stopStudioPreview();
    try {
      if (src === 'screen' && navigator.mediaDevices.getDisplayMedia) {
        studioPreviewStream = await navigator.mediaDevices.getDisplayMedia({
          video: { width: { ideal: 1920 }, height: { ideal: 1080 } },
          audio: false,
        });
        studioSourceMode = 'screen';
      } else {
        studioPreviewStream = await navigator.mediaDevices.getUserMedia({
          video: { facingMode: 'user', width: { ideal: 1280 }, height: { ideal: 720 } },
          audio: false,
        });
        studioSourceMode = 'camera';
      }
      syncStudioSourceUi();
      const v = $('#studio-preview-v');
      v.srcObject = studioPreviewStream;
      show(v);
      hide($('#studio-preview-placeholder'));
      v.play().catch(() => {});
    } catch (_) {
      show($('#studio-preview-placeholder'));
      $('#studio-preview-placeholder').innerHTML =
        '<span class="studio-preview-icon">' + (src === 'screen' ? '🖥' : '📷') + '</span>' +
        '<p>' + (src === 'screen' ? 'Bildschirm-Freigabe abgebrochen' : 'Kamera nicht verfügbar') + '</p>' +
        '<p style="font-size:.72rem">Live startet trotzdem</p>';
      if (src === 'screen') {
        studioSourceMode = 'camera';
        syncStudioSourceUi();
      }
    }
  }

  function syncStudioSourceUi() {
    $$('.studio-source[data-src]').forEach((el) => {
      el.classList.toggle('active', el.dataset.src === studioSourceMode);
    });
    $$('.studio-quick-btn[data-src]').forEach((el) => {
      el.classList.toggle('active', el.dataset.src === studioSourceMode);
    });
  }

  function setStudioOrientation(mode) {
    studioOrientation = mode;
    const wrap = $('#studio-preview-wrap');
    wrap.classList.toggle('studio-preview--portrait', mode === 'portrait');
    wrap.classList.toggle('studio-preview--landscape', mode === 'landscape');
    $$('.studio-orient button').forEach((b) => {
      b.classList.toggle('active', b.dataset.orient === mode);
    });
  }

  function updateStudioPreviewLabel() {
    const title = ($('#studio-title').value || '').trim();
    $('#studio-preview-label').textContent = title || 'Flarlo LIVE';
    $('#studio-scene-name').textContent = title ? title.slice(0, 28) : 'Hauptszene';
  }

  function renderStudioPerf() {
    if (!profile) return;
    $('#studio-stat-followers').textContent = fmt(profile.followerCount || 0);
    $('#studio-stat-coins').textContent = fmt(profile.creatorCoins || profile.coins || 0);
    $('#studio-stat-viewers').textContent = '0';
    $('#studio-stat-likes').textContent = fmt(profile.totalViews || 0);
  }

  function startStudioStatusBar() {
    if (studioStatusTimer) clearInterval(studioStatusTimer);
    let frames = 0;
    let last = performance.now();
    studioStatusTimer = setInterval(() => {
      frames += 1;
      const now = performance.now();
      if (now - last >= 1000) {
        const fps = Math.min(60, frames);
        $('#studio-stat-fps').textContent = 'FPS ' + fps;
        frames = 0;
        last = now;
      }
      if (performance.memory) {
        const mb = (performance.memory.usedJSHeapSize / 1048576).toFixed(1);
        $('#studio-stat-mem').textContent = 'RAM ' + mb + ' MB';
      }
      $('#studio-stat-cpu').textContent = 'CPU ~' + (5 + Math.floor(Math.random() * 8)) + '%';
      const v = $('#studio-preview-v');
      const on = v && v.srcObject && !v.classList.contains('hidden');
      $('#studio-stat-bitrate').textContent = on ? 'Bitrate ~1200 kbps' : 'Bitrate 0 kbps';
    }, 500);
  }

  function stopStudioStatusBar() {
    if (studioStatusTimer) { clearInterval(studioStatusTimer); studioStatusTimer = null; }
  }

  function bindStudioControls() {
    $('#studio-orient-portrait').onclick = () => setStudioOrientation('portrait');
    $('#studio-orient-landscape').onclick = () => setStudioOrientation('landscape');
    $('#studio-src-camera').onclick = () => startStudioPreview('camera');
    $('#studio-src-screen').onclick = () => startStudioPreview('screen');
    $$('.studio-quick-btn[data-src]').forEach((btn) => {
      btn.onclick = () => startStudioPreview(btn.dataset.src);
    });
    $('#studio-title').oninput = updateStudioPreviewLabel;
    $('#studio-mic-volume').oninput = (e) => { studioMicVolume = parseInt(e.target.value, 10) || 80; };
  }

  function renderStudioSetup() {
    const tier = profile && profile.tierInfo;
    const followers = profile ? (profile.followerCount || 0) : 0;
    const need = 100;
    const pct = Math.min(100, Math.round((followers / need) * 100));
    const box = $('#studio-tier-box');
    const goBtn = $('#go-live-btn');
    const resumeBtn = $('#studio-resume-live');
    const livePill = $('#studio-live-pill');

    if (profile && profile.isLive) {
      box.innerHTML = '<span class="studio-tier-ok">● Dein Live läuft gerade</span>';
      hide(goBtn);
      show(resumeBtn);
      show(livePill);
      return;
    }
    hide(livePill);
    hide(resumeBtn);

    if (tier && tier.canLive) {
      box.innerHTML =
        '<span class="studio-tier-ok">✓ Live freigeschaltet</span><br>' +
        '<span>' + fmt(followers) + ' Follower · Tier ' + esc(tier.tier || 'live') + '</span>';
      goBtn.disabled = false;
      show(goBtn);
    } else {
      box.innerHTML =
        '<strong>Live ab ' + need + ' Followern</strong>' +
        '<div class="studio-tier-bar"><div class="studio-tier-fill" style="width:' + pct + '%"></div></div>' +
        '<span>' + fmt(followers) + ' / ' + need + ' Follower (' + pct + '%)</span>';
      goBtn.disabled = true;
      show(goBtn);
    }
  }

  async function showLiveStudio() {
    hide($('#main-screen'));
    hide($('#explore-screen'));
    hide($('#profile-screen'));
    show($('#live-screen'));
    if (!$('#studio-title').value) {
      $('#studio-title').value = 'Hey, ich bin live!';
    }
    bindStudioControls();
    setStudioOrientation(studioOrientation);
    updateStudioPreviewLabel();
    renderStudioPerf();
    renderStudioSetup();
    startStudioStatusBar();
    await startStudioPreview(studioSourceMode);
    await loadLives();
  }

  async function loadLives() {
    const j = await api.getLives();
    const list = $('#live-list');
    list.innerHTML = '';
    if (!(j.lives || []).length) {
      list.innerHTML = '<p style="color:#666;text-align:center;padding:.75rem;font-size:.72rem">Gerade niemand live – sei der Erste!</p>';
    }
    (j.lives || []).forEach((l) => {
      const d = document.createElement('div');
      d.className = 'live-card';
      d.innerHTML = '<img src="' + (l.host.photo || '') + '"><div><div class="live-dot">● LIVE</div><strong>' +
        esc(l.host.displayName) + '</strong> · 👀 ' + fmt(l.viewerPeak) + '<br>' +
        esc(l.title || '') + '</div>';
      d.onclick = () => watchLive(l.liveId);
      list.appendChild(d);
    });
    renderStudioSetup();
  }

  $('#live-screen-back').onclick = () => {
    stopStudioPreview();
    hide($('#live-screen'));
    show($('#main-screen'));
    $$('#top-bar .tab').forEach((t) => t.classList.toggle('active', t.dataset.mode === 'foryou'));
  };

  $('#studio-match-btn').onclick = () => inviteLiveMatchOpponent();

  $('#go-live-btn').onclick = async () => {
    const tier = profile && profile.tierInfo;
    if (!tier || !tier.canLive) {
      toast('Live ab 100 Followern (' + fmt(profile.followerCount || 0) + '/100)');
      return;
    }
    const title = ($('#studio-title').value || '').trim() || 'Hey, ich bin live!';
    const goBtn = $('#go-live-btn');
    goBtn.disabled = true;
    if (studioSourceMode === 'screen') toast('Live sendet Kamera – Bildschirm war nur Vorschau');
    try {
      stopStudioPreview();
      const r = await api.startLive(title);
      profile.isLive = true;
      await startLiveBroadcast(title, r.liveId, r);
      toast('Du bist LIVE! 🔴');
    } catch (e) {
      toast(e.message);
      await startStudioPreview();
    }
    goBtn.disabled = false;
    renderStudioSetup();
  };

  $('#studio-resume-live').onclick = async () => {
    if (!profile || !profile.isLive) return;
    try {
      const j = await api.getLives();
      const mine = (j.lives || []).find((l) => l.host && l.host.id === profile.id);
      if (!mine) {
        toast('Live-Session nicht gefunden – starte neu');
        profile.isLive = false;
        renderStudioSetup();
        await startStudioPreview();
        return;
      }
      stopStudioPreview();
      let whipUrl = null;
      try {
        const detail = await api.getLive(mine.liveId);
        whipUrl = detail.whipUrl || null;
      } catch (_) {}
      await startLiveBroadcast(mine.title, mine.liveId, { whipUrl });
    } catch (e) { toast(e.message); }
  };

  $('#end-live-btn').onclick = () => endLiveBroadcast();

  $('#live-watch-close').onclick = () => closeLiveWatch();

  $$('#bottom-nav button').forEach((btn) => {
    btn.onclick = async () => {
      $$('#bottom-nav button').forEach((b) => b.classList.remove('active'));
      btn.classList.add('active');
      const nav = btn.dataset.nav;
      if (nav === 'upload') openEditor('feed');
      else if (nav === 'profile') showProfile(profile.id);
      else if (nav === 'home') {
        hide($('#profile-screen'));
        hide($('#explore-screen'));
        hide($('#inbox-screen'));
        stopStudioPreview();
        stopInboxPoll();
        hide($('#live-screen'));
        show($('#main-screen'));
        $$('#top-bar .tab').forEach((t) => t.classList.toggle('active', t.dataset.mode === feedMode && feedMode !== 'live'));
      } else if (nav === 'explore') showExplore();
      else if (nav === 'inbox') showInbox();
    };
  });

  function rejectCommunityText(text) {
    if (!window.VlCommunity) return true;
    const r = VlCommunity.check(text);
    if (!r.ok) { toast(r.reason); return false; }
    return true;
  }

  function renderCommunityRules() {
    const cp = config && config.communityPolicy;
    const summary = $('#community-summary');
    const list = $('#community-rules');
    if (!summary || !list || !cp) return;
    summary.textContent = cp.summary || 'Keine Hassrede oder Mobbing.';
    list.innerHTML = '';
    (cp.rules || []).forEach((r) => {
      const li = document.createElement('li');
      li.textContent = r;
      list.appendChild(li);
    });
  }

  function renderCopyrightRules() {
    const cp = config && config.copyrightPolicy;
    const summary = $('#copyright-summary');
    const list = $('#copyright-rules');
    if (!summary || !list || !cp) return;
    summary.textContent = cp.summary || 'Musik und Filme nur als Teilausschnitt.';
    list.innerHTML = '';
    (cp.rules || []).forEach((r) => {
      const li = document.createElement('li');
      li.textContent = r;
      list.appendChild(li);
    });
  }

  async function postFromEditor(data) {
    if (!rejectCommunityText(data.caption || '')) throw new Error('community');
    if (window.VlCopyright) {
      const cr = VlCopyright.check({
        type: data.type,
        caption: data.caption,
        hashtags: data.hashtags,
        niches: [],
        durationSec: data.durationSec || 0,
      });
      if (!cr.ok) { toast(cr.reason); throw new Error(cr.reason); }
    }
    await api.uploadPost({
      type: data.type,
      dataUrl: data.dataUrl,
      thumbDataUrl: data.thumbDataUrl,
      caption: data.caption,
      hashtags: data.hashtags,
      niches: profile.niches || [],
      durationSec: data.durationSec || 0,
      overlays: data.overlays,
    });
    toast('Gepostet! 🎉');
  }

  function openEditor(returnTo) {
    editorReturnTo = returnTo || 'feed';
    VibeLoopEditor.open({
      onPost: postFromEditor,
      onPosted: async () => {
        if (editorReturnTo === 'profile') {
          show($('#profile-screen'));
          await showProfile(profile.id);
        } else {
          show($('#main-screen'));
          await loadFeed();
        }
      },
      onCancel: () => {
        if (editorReturnTo === 'profile') show($('#profile-screen'));
        else show($('#main-screen'));
      },
    });
  }

  function filterProfilePosts(tab) {
    if (tab === 'video') return profilePostsCache.filter((p) => p.type !== 'photo');
    if (tab === 'photo') return profilePostsCache.filter((p) => p.type === 'photo');
    return profilePostsCache;
  }

  function sortProfilePosts(list) {
    const items = list.slice();
    if (profileSort === 'popular') {
      items.sort((a, b) => (b.viewCount || 0) + (b.likeCount || 0) * 2 - ((a.viewCount || 0) + (a.likeCount || 0) * 2));
    } else if (profileSort === 'oldest') {
      items.sort((a, b) => String(a.createdAt || '').localeCompare(String(b.createdAt || '')));
    } else {
      items.sort((a, b) => String(b.createdAt || '').localeCompare(String(a.createdAt || '')));
    }
    return items;
  }

  function profileTotalLikes(posts) {
    return (posts || []).reduce((sum, p) => sum + (p.likeCount || 0), 0);
  }

  function renderProfileGrid(postList, isSelf) {
    const grid = $('#profile-grid');
    grid.innerHTML = '';
    const sorted = sortProfilePosts(postList);
    if (isSelf) {
      const up = document.createElement('div');
      up.className = 'thumb profile-grid-upload';
      up.innerHTML = '<span style="font-size:2rem">＋</span><span style="font-size:.65rem;margin-top:.25rem">Hochladen</span>';
      up.title = 'Video oder Foto mit Hook, Text & Emoji';
      up.onclick = () => openEditor('profile');
      grid.appendChild(up);
    }
    sorted.forEach((post) => {
      const d = document.createElement('div');
      d.className = 'thumb';
      const hasHook = post.overlays && post.overlays.items && post.overlays.items.some((o) => o.kind === 'hook');
      const views = fmt(post.viewCount || 0);
      d.innerHTML =
        (post.thumbUrl ? '<img src="' + post.thumbUrl + '" alt="">' :
          (post.type === 'photo' ? '<img src="' + post.mediaUrl + '" alt="">' :
            '<video src="' + post.mediaUrl + '" muted preload="metadata" poster="' + (post.thumbUrl || '') + '"></video>')) +
        '<span class="thumb-views">▶ ' + views + '</span>' +
        (hasHook ? '<span class="thumb-pin">🎣</span>' : '');
      d.onclick = () => openProfilePost(post);
      grid.appendChild(d);
    });
    if (!sorted.length && !isSelf) {
      grid.innerHTML = '<p style="grid-column:1/-1;text-align:center;color:var(--muted);padding:2.5rem 1rem">Noch keine Clips</p>';
    }
  }

  function openProfilePost(post) {
    hide($('#profile-screen'));
    show($('#main-screen'));
    posts = [post];
    showAds = false;
    buildFeedItems();
    renderFeed();
    toast('Clip im Vollbild');
  }

  function bindProfileTabs(isSelf) {
    $$('#profile-tabs button').forEach((btn) => {
      btn.onclick = () => {
        profileTab = btn.dataset.ptab;
        $$('#profile-tabs button').forEach((b) => b.classList.toggle('active', b.dataset.ptab === profileTab));
        renderProfileGrid(filterProfilePosts(profileTab), isSelf);
      };
    });
    $$('#profile-sort button').forEach((btn) => {
      btn.onclick = () => {
        profileSort = btn.dataset.psort;
        $$('#profile-sort button').forEach((b) => b.classList.toggle('active', b.dataset.psort === profileSort));
        renderProfileGrid(filterProfilePosts(profileTab), isSelf);
      };
    });
  }

  function openProfileEditModal(p, isSelf) {
    if (!isSelf) return;
    $('#pe-username').value = p.username || '';
    $('#pe-bio').value = p.bio || '';
    $('#pe-link').value = p.linkUrl || '';
    const canLink = !!(p.tierInfo && p.tierInfo.canLinkBio);
    $('#pe-link').disabled = !canLink;
    $('#pe-link-hint').classList.toggle('hidden', canLink);
    show($('#profile-edit-modal'));
  }

  $('#pe-cancel').onclick = () => hide($('#profile-edit-modal'));
  $('#profile-settings-close').onclick = () => hide($('#profile-settings-modal'));

  function captureThumb(video) {
    return new Promise((resolve) => {
      const c = document.createElement('canvas');
      c.width = 360; c.height = 640;
      const ctx = c.getContext('2d');
      video.currentTime = 0.5;
      video.onseeked = () => {
        ctx.drawImage(video, 0, 0, c.width, c.height);
        resolve(c.toDataURL('image/jpeg', 0.8));
      };
      setTimeout(() => resolve(''), 2000);
    });
  }


  async function openComments(postId) {
    activePostId = postId;
    show($('#comment-modal'));
    const j = await api.getComments(postId);
    const list = $('#comment-list');
    list.innerHTML = '';
    (j.comments || []).forEach((c) => {
      const d = document.createElement('div');
      d.className = 'comment-item';
      const isOwn = c.author && c.author.id === profile.id;
      d.innerHTML =
        '<div class="comment-head">' +
        '<div class="comment-author">@' + esc(c.author.username || c.author.displayName) + '</div>' +
        (!isOwn ? '<button type="button" class="comment-report" title="Melden">⋯</button>' : '') +
        '</div><div class="comment-body">' + esc(c.body) + '</div>';
      const rpt = d.querySelector('.comment-report');
      if (rpt) {
        rpt.onclick = () => openSafetyMenu({
          targetType: 'comment',
          targetId: c.id,
          targetUserId: c.author.id,
          author: c.author,
          label: userLabel(c.author) + ' · Kommentar',
        });
      }
      list.appendChild(d);
    });
  }

  $('#comment-send').onclick = async () => {
    const body = $('#comment-input').value.trim();
    if (!body || !activePostId) return;
    if (!rejectCommunityText(body)) return;
    await api.addComment(activePostId, body);
    $('#comment-input').value = '';
    openComments(activePostId);
  };

  const commentModal = $('#comment-modal');
  if (commentModal) {
    commentModal.onclick = (e) => { if (e.target === commentModal) hide(commentModal); };
  }
  $('#safety-close').onclick = () => hide($('#safety-modal'));
  $('#report-cancel').onclick = () => { hide($('#report-modal')); reportDraft = null; };
  $('#report-submit').onclick = () => submitReport();

  function openGifts(post) {
    giftTarget = { toId: post.author.id, postId: post.id };
    show($('#gift-modal'));
    const grid = $('#gift-grid');
    grid.innerHTML = '<p class="hint" style="grid-column:1/-1">Deine Coins: ' + fmt(profile.coins || 0) + '</p>';
    Object.entries(config.gifts || {}).forEach(([id, g]) => {
      const d = document.createElement('div');
      d.className = 'gift-item';
      d.innerHTML =
        '<div class="gift-item-emoji-wrap">' + em(g.emoji, 'cp-emoji-xl cp-emoji-glow', 72) + '</div>' +
        '<div class="gift-item-label">' + esc(g.label) + '</div>' +
        '<div class="gift-item-coins">' + g.coins + ' Coins</div>';
      d.onclick = async () => {
        try {
          const j = await api.sendGift({ ...giftTarget, giftType: id });
          profile.coins = j.coins ?? j.coinsLeft ?? profile.coins;
          const sender = j.fromName || mySenderName();
          showFeedGiftToast(g.emoji, sender, g.label, id);
          toast(sender + ' sendet ' + g.emoji + ' ' + g.label);
          hide($('#gift-modal'));
        } catch (e) { toast(e.message); }
      };
      grid.appendChild(d);
    });
  }

  $('#gift-close').onclick = () => hide($('#gift-modal'));

  async function sharePost(post) {
    const url = location.origin + '/?clip=' + post.id;
    try {
      if (navigator.share) await navigator.share({ title: 'Flarlo', url });
      else { await navigator.clipboard.writeText(url); toast('Link kopiert'); }
    } catch (_) {
      try { await navigator.clipboard.writeText(url); toast('Link kopiert'); } catch (e2) {}
    }
    try {
      const j = await api.sharePost(post.id);
      post.shareCount = j.shareCount;
    } catch (_) {
      post.shareCount = (post.shareCount || 0) + 1;
    }
    renderActions(post);
  }

  let introUploadData = null;

  function playIntroVideo(url) {
    show($('#intro-player'));
    const v = $('#intro-player-v');
    v.src = url;
    v.play().catch(() => {});
  }

  $('#intro-player-close').onclick = () => {
    hide($('#intro-player'));
    $('#intro-player-v').pause();
    $('#intro-player-v').src = '';
  };

  function renderProfileIntro(p, isSelf) {
    const box = $('#profile-intro');
    box.innerHTML = '';
    const tier = p.tierInfo || {};
    if (p.introVideo && p.introVideo.mediaUrl) {
      const card = document.createElement('div');
      card.className = 'intro-card';
      const poster = p.introVideo.thumbUrl || '';
      card.innerHTML = (poster ? '<img src="' + poster + '" alt="">' : '') +
        '<button class="intro-card-play" type="button">▶</button>';
      card.querySelector('.intro-card-play').onclick = () => playIntroVideo(p.introVideo.mediaUrl);
      box.appendChild(card);
      const label = document.createElement('p');
      label.style.cssText = 'text-align:center;font-size:.8rem;color:#aaa;margin-top:.4rem';
      label.textContent = 'Vorstellungsvideo' + (p.introVideo.durationSec ? ' · ' + Math.round(p.introVideo.durationSec) + 's' : '');
      box.appendChild(label);
      if (isSelf && tier.canIntroVideo) {
        const actions = document.createElement('div');
        actions.className = 'intro-actions';
        actions.innerHTML = '<button class="btn-ghost" id="intro-edit-btn">Video ändern</button>';
        box.appendChild(actions);
        $('#intro-edit-btn').onclick = () => openIntroModal(p);
      }
      return;
    }
    if (isSelf && tier.canIntroVideo) {
      box.innerHTML = '<div class="intro-locked"><p>Noch kein Vorstellungsvideo</p>' +
        '<p style="font-size:.8rem;margin:.5rem 0">Kostenlos · keine Coins</p>' +
        '<button class="btn-primary" id="intro-add-btn" style="margin-top:.75rem">Vorstellungsvideo aufnehmen</button></div>';
      $('#intro-add-btn').onclick = () => openIntroModal(p);
      return;
    }
    if (isSelf && !tier.canIntroVideo) {
      box.innerHTML = '<div class="intro-locked">Vorstellungsvideo ab 100 Followern (' + fmt(p.followerCount) + '/100)</div>';
    }
  }

  function openIntroModal(p) {
    introUploadData = null;
    hide($('#intro-preview'));
    $('#intro-file').value = '';
    show($('#intro-modal'));
    if (p.introVideo && p.introVideo.mediaUrl) {
      show($('#intro-delete'));
      show($('#intro-preview'));
      $('#intro-preview').src = p.introVideo.mediaUrl;
    } else {
      hide($('#intro-delete'));
    }
  }

  $('#intro-file').onchange = (e) => {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => {
      introUploadData = reader.result;
      show($('#intro-preview'));
      $('#intro-preview').src = introUploadData;
    };
    reader.readAsDataURL(file);
  };

  $('#intro-submit').onclick = async () => {
    if (!introUploadData) { toast('Video wählen'); return; }
    $('#intro-submit').disabled = true;
    try {
      const v = $('#intro-preview');
      const thumb = await captureThumb(v);
      const dur = v.duration && isFinite(v.duration) ? v.duration : 30;
      if (dur > 60) { toast('Max. 60 Sekunden'); $('#intro-submit').disabled = false; return; }
      await api.uploadIntroVideo({ dataUrl: introUploadData, thumbDataUrl: thumb, durationSec: dur });
      toast('Vorstellungsvideo gespeichert!');
      hide($('#intro-modal'));
      showProfile(profile.id);
    } catch (e) { toast(e.message); }
    $('#intro-submit').disabled = false;
  };

  $('#intro-delete').onclick = async () => {
    if (!confirm('Vorstellungsvideo wirklich entfernen?')) return;
    try {
      await api.deleteIntroVideo();
      toast('Video entfernt');
      hide($('#intro-modal'));
      showProfile(profile.id);
    } catch (e) { toast(e.message); }
  };

  $('#intro-cancel').onclick = () => hide($('#intro-modal'));

  function shareProfileUrl(userId) {
    const url = location.origin + '/?profile=' + encodeURIComponent(userId);
    if (navigator.share) {
      navigator.share({ title: 'Flarlo Profil', url }).catch(() => {});
    } else {
      navigator.clipboard.writeText(url).then(() => toast('Profil-Link kopiert')).catch(() => {});
    }
  }

  async function showProfile(userId) {
    setSidebarActive(profile && userId === profile.id ? 'profile' : 'home');
    hide($('#main-screen'));
    hide($('#explore-screen'));
    hide($('#inbox-screen'));
    stopInboxPoll();
    show($('#profile-screen'));
    profileViewUserId = userId;
    let j;
    try {
      j = await api.getProfile(userId);
    } catch (e) {
      toast(e.message || 'Profil nicht verfügbar');
      hide($('#profile-screen'));
      show($('#main-screen'));
      return;
    }
    const p = j.profile;
    const isSelf = userId === profile.id;
    const hasIntro = !!(p.introVideo && p.introVideo.mediaUrl);
    const avatarClass = hasIntro ? 'profile-avatar-wrap has-intro' : 'profile-avatar-wrap';
    const displayName = p.displayName || p.username || 'Nutzer';
    const handle = p.username ? '@' + p.username : '';
    const totalLikes = profileTotalLikes(j.posts || []);
    const premiumBadge = p.isPremium ? '<span class="premium-badge">Flarlo+</span>' : '';

    $('#profile-top-title').textContent = isSelf ? 'Profil' : (p.username || displayName);
    $('#profile-header').innerHTML =
      '<div class="profile-hero">' +
      '<div class="profile-hero-left"><div class="' + avatarClass + '" id="profile-avatar">' +
      '<img src="' + (p.photo || '') + '" alt="">' +
      (hasIntro ? '<span class="profile-avatar-play">▶</span>' : '') +
      '</div></div>' +
      '<div class="profile-hero-right">' +
      '<h1 class="profile-display-name">' + esc(displayName) + premiumBadge + '</h1>' +
      (handle ? '<p class="profile-username">' + esc(handle) + '</p>' : '') +
      '<div class="profile-stats">' +
      '<div class="profile-stat"><strong>' + fmt(p.followingCount) + '</strong><span>Gefolgt</span></div>' +
      '<div class="profile-stat"><strong>' + fmt(p.followerCount) + '</strong><span>Follower</span></div>' +
      '<div class="profile-stat"><strong>' + fmt(totalLikes) + '</strong><span>Likes</span></div>' +
      '</div>' +
      '<div class="profile-actions">' +
      (isSelf
        ? '<button type="button" id="profile-edit-btn" class="profile-btn-edit">Profil bearbeiten</button>' +
          '<button type="button" id="profile-settings-btn" class="profile-icon-btn" title="Einstellungen">⚙</button>'
        : '<button type="button" id="follow-btn" class="profile-btn-follow' +
          (j.isFollowing ? ' following' : '') + '">' + (j.isFollowing ? 'Entfolgen' : 'Folgen') + '</button>' +
          '<button type="button" id="profile-msg-btn" class="profile-icon-btn profile-msg-btn" title="Nachricht">💬</button>' +
          '<button type="button" id="profile-more-btn" class="profile-icon-btn" title="Melden & Sperren">⋯</button>') +
      '</div></div></div>' +
      (p.bio ? '<p class="profile-bio">' + esc(p.bio) + '</p>' : '') +
      (p.linkUrl ? '<a class="profile-link" href="' + esc(p.linkUrl) + '" target="_blank" rel="noopener">' + esc(p.linkUrl) + '</a>' : '');

    if (hasIntro) {
      $('#profile-avatar').onclick = () => playIntroVideo(p.introVideo.mediaUrl);
    }
    if (isSelf) {
      $('#profile-edit-btn').onclick = () => openProfileEditModal(p, true);
      $('#profile-settings-btn').onclick = () => {
        show($('#profile-settings-modal'));
        loadBlockedList();
      };
      const canLink = !!(p.tierInfo && p.tierInfo.canLinkBio);
      $('#pe-save').onclick = async () => {
        $('#pe-save').disabled = true;
        try {
          const patch = {
            username: $('#pe-username').value.trim(),
            bio: $('#pe-bio').value.trim(),
          };
          if (canLink) patch.linkUrl = $('#pe-link').value.trim();
          const updated = await api.updateMe(patch);
          profile = updated;
          hide($('#profile-edit-modal'));
          toast('Profil gespeichert');
          showProfile(userId);
        } catch (e) { toast(e.message); }
        $('#pe-save').disabled = false;
      };
    } else {
      $('#follow-btn').onclick = async () => {
        const r = await api.follow(userId);
        const btn = $('#follow-btn');
        btn.textContent = r.following ? 'Entfolgen' : 'Folgen';
        btn.classList.toggle('following', r.following);
      };
      const msgBtn = $('#profile-msg-btn');
      if (msgBtn) msgBtn.onclick = () => showInbox(userId);
      const moreBtn = $('#profile-more-btn');
      if (moreBtn) {
        moreBtn.onclick = () => openSafetyMenu({
          targetType: 'user',
          targetId: userId,
          targetUserId: userId,
          author: p,
          label: userLabel(p),
        });
      }
    }
    $('#profile-share-top').onclick = () => shareProfileUrl(userId);
    renderProfileIntro(p, isSelf);
    const tier = p.tierInfo || {};
    let tierHtml =
      '<strong>Tier: ' + tier.tier + '</strong><br>' +
      (tier.nextTier ? 'Nächstes Level bei ' + fmt(tier.nextTierAt) + ' Followern (' + tier.nextTier + ')' : 'Max-Level! ' + em('🏆', 'cp-emoji-glow', 22)) +
      '<br>Features: ' + (tier.features || []).join(', ');
    if (isSelf && tier.canLive && !p.isLive) {
      tierHtml += '<br><button type="button" id="profile-go-live" class="btn-primary" style="margin-top:.75rem">' +
        em('🔴', 'cp-emoji-pop', 18) + ' Live Studio öffnen</button>';
    }
    $('#profile-tier').innerHTML = tierHtml;
    const plive = $('#profile-go-live');
    if (plive) plive.onclick = async () => {
      hide($('#profile-screen'));
      $$('#top-bar .tab').forEach((t) => t.classList.remove('active'));
      feedMode = 'live';
      await showLiveStudio();
      $('#studio-title').focus();
    };
    profilePostsCache = j.posts || [];
    profileTab = 'video';
    profileSort = 'recent';
    $$('#profile-tabs button').forEach((b) => b.classList.toggle('active', b.dataset.ptab === 'video'));
    $$('#profile-sort button').forEach((b) => b.classList.toggle('active', b.dataset.psort === 'recent'));
    bindProfileTabs(isSelf);
    renderProfileGrid(filterProfilePosts('video'), isSelf);
    if (isSelf) {
      show($('#premium-panel'));
      const prem = await api.getPremium();
      profile.isPremium = prem.isPremium;
      $('#premium-status').textContent = prem.isPremium
        ? 'Flarlo+ aktiv bis ' + new Date(prem.premiumUntil).toLocaleDateString('de-DE')
        : 'Werbung zwischen Videos – Flarlo+ werbefrei per Stripe (keine Coins).';
      const benefits = $('#premium-benefits');
      benefits.innerHTML = '';
      (prem.benefits || []).forEach((b) => {
        const li = document.createElement('li');
        li.textContent = b;
        benefits.appendChild(li);
      });
      const subBtn = $('#premium-subscribe');
      if (prem.isPremium) {
        hide(subBtn);
      } else {
        show(subBtn);
        subBtn.textContent = 'Flarlo+ für ' + (prem.monthlyEur || 9.99) + ' € / Monat';
        subBtn.onclick = async () => {
          try {
            const r = await api.checkoutPremium();
            if (r.url) location.href = r.url;
            else toast('Checkout nicht verfügbar');
          } catch (e) { toast(e.message); }
        };
      }
      show($('#coins-panel'));
      $('#coins-wallet-balance').textContent = 'Guthaben: ' + fmt(profile.coins || 0) + ' Coins (für Geschenke & Boost)';
      $('#profile-buy-coins').onclick = () => { hide($('#profile-settings-modal')); openCoinsShop(); };
      show($('#earnings-panel'));
      const e = await api.getEarnings();
      $('#earnings-balance').textContent =
        'Creator-Coins: ' + fmt(e.creatorCoins) + ' (≈ ' + (e.creatorCoins * e.coinToEur).toFixed(2) + ' €)';
      $('#payout-btn').onclick = async () => {
        try {
          const r = await api.requestPayout();
          toast('Auszahlung angefordert: ' + r.eurAmount + ' €');
        } catch (e) { toast(e.message); }
      };
    } else {
      hide($('#earnings-panel'));
      hide($('#premium-panel'));
    }
  }

  $('#profile-back').onclick = () => {
    hide($('#profile-screen'));
    show($('#main-screen'));
  };

  let lastTap = 0;
  document.addEventListener('click', (e) => {
    if (!e.target.closest('.feed-item')) return;
    const fi = currentFeedItem();
    const now = Date.now();
    if (now - lastTap < 300 && fi && fi.kind === 'post') toggleLike(fi.post);
    lastTap = now;
  });

  boot();
})();
"""


_BRAND_DIR = Path(__file__).resolve().parents[1] / "data" / "flarlo"


def write_assets(site_root: Path, *, site_url: str = "") -> None:
    from datetime import datetime

    build_id = datetime.utcnow().strftime("%Y%m%d%H%M")
    html = INDEX_HTML.replace("__BUILD_ID__", build_id)
    img_dir = site_root / "img"
    img_dir.mkdir(parents=True, exist_ok=True)
    for name in ("logo.png",):
        src = _BRAND_DIR / name
        if src.is_file():
            (img_dir / name).write_bytes(src.read_bytes())
    (site_root / "index.html").write_text(html, encoding="utf-8")
    (site_root / "css" / "vibeloop.css").write_text(CSS, encoding="utf-8")
    (site_root / "js" / "app.js").write_text(APP_JS, encoding="utf-8")
