<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta http-equiv="x-ua-compatible" content="ie=edge" />
  <title>Taliferro Music Radio | Live Stream</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
  <meta content="telephone=no" name="format-detection" />
  <meta name="HandheldFriendly" content="true" />
  <meta name="description" content="Taliferro Music Radio — a live stream featuring Taliferro Music artists." />
  <!-- Icons -->
  <link rel="icon" type="image/png" href="img/favicon-96x96.png" sizes="96x96" />
  <link rel="icon" type="image/svg+xml" href="img/favicon.svg" />
  <link rel="shortcut icon" href="img/favicon.ico" />
  <link rel="apple-touch-icon" sizes="180x180" href="img/apple-touch-icon.png" />
  <meta name="apple-mobile-web-app-title" content="TaliferroMusic" />
  <link rel="manifest" href="img/site.webmanifest" />
  <!-- Structured Data (JSON-LD) -->
  <script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "RadioStation",
  "name": "Taliferro Music Radio",
  "url": "https://music.taliferro.com/",
  "image": "https://music.taliferro.com/assets/cover.jpg",
  "description": "Live stream featuring Taliferro Music artists.",
  "broadcastFrequency": "Online",
  "areaServed": "Worldwide",
  "inLanguage": "en"
}
</script>
  <!-- Open Graph / Social -->
  <meta property="og:title" content="Taliferro Music Radio | Live Stream" />
  <meta property="og:description" content="Press play. Live radio featuring Taliferro Music artists." />
  <meta property="og:type" content="website" />
  <meta property="og:url" content="https://music.taliferro.com/" />
  <meta property="og:image" content="https://music.taliferro.com/assets/cover.jpg" />
  <meta property="og:image:alt" content="Taliferro Music Radio logo" />
  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="Taliferro Music Radio | Live Stream" />
  <meta name="twitter:description" content="Press play. Live radio featuring Taliferro Music artists." />
  <meta name="twitter:image" content="https://music.taliferro.com/assets/cover.jpg" />
  <meta name="twitter:image:alt" content="Taliferro Music Radio logo" />
  <meta name="msapplication-TileColor" content="#0b0d12" />
  <meta name="theme-color" content="#0b0d12" />
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Inter:ital,wght@0,100;0,200;0,300;0,400;0,500;0,700;1,300;1,400;1,500&amp;display=swap" />
  <style>
    :root {
      --tm-bg: #0b0d12;
      --tm-panel: rgba(255, 255, 255, 0.06);
      --tm-panel-border: rgba(255, 255, 255, 0.10);
      --tm-text: rgba(255, 255, 255, 0.92);
      --tm-muted: rgba(255, 255, 255, 0.62);
      --tm-accent: rgba(255, 255, 255, 0.92);
      --tm-accent-a: #34e6dc;
      --tm-accent-b: #1f4bff;
    }

    /* Light mode (system preference) */
    @media (prefers-color-scheme: light) {
      :root {
        --tm-bg: #fbfdff;
        --tm-panel: rgba(255, 255, 255, 0.98);
        --tm-panel-border: rgba(10, 15, 25, 0.08);
        --tm-text: rgba(10, 15, 25, 0.92);
        --tm-muted: rgba(10, 15, 25, 0.58);
        --tm-accent: rgba(10, 15, 25, 0.92);
      }

      body {
        background:
          radial-gradient(1200px 700px at 10% 10%, rgba(52, 230, 220, 0.22), rgba(0, 0, 0, 0) 58%),
          radial-gradient(1200px 700px at 90% 12%, rgba(31, 75, 255, 0.16), rgba(0, 0, 0, 0) 55%),
          radial-gradient(1200px 700px at 70% 92%, rgba(52, 230, 220, 0.14), rgba(0, 0, 0, 0) 58%),
          linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(246, 249, 255, 1));
      }

      .tm-card {
        background: rgba(255, 255, 255, 0.86);
        backdrop-filter: blur(18px);
        border-color: rgba(10, 15, 25, 0.10);
        box-shadow:
          0 24px 70px rgba(10, 15, 25, 0.10),
          0 2px 12px rgba(10, 15, 25, 0.06);
      }

      .tm-panel {
        background: rgba(255, 255, 255, 0.92);
      }

      .tm-item {
        background: rgba(255, 255, 255, 0.92);
      }

      .tm-btn {
        background: rgba(255, 255, 255, 0.92);
      }

      .tm-btn:hover {
        background: rgba(255, 255, 255, 1);
      }

      /* add a thin brand accent to badges */
      .tm-badge {
        border-color: rgba(10, 15, 25, 0.14);
        background:
          linear-gradient(90deg, rgba(52, 230, 220, 0.16), rgba(31, 75, 255, 0.12));
      }

      /* make the player chrome feel lighter */
      .tm-note code {
        color: rgba(10, 15, 25, 0.80);
      }
    }

    body {
      background: var(--tm-bg);
      font-family: Arial, Helvetica, sans-serif;
    }

    .tm-shell {
      min-height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 48px 18px;
    }

    .tm-card {
      width: min(980px, 100%);
      border: 1px solid var(--tm-panel-border);
      background: var(--tm-panel);
      border-radius: 18px;
      padding: 28px;
      backdrop-filter: blur(10px);
    }

    .tm-top {
      display: flex;
      gap: 18px;
      align-items: flex-start;
      justify-content: space-between;
      flex-wrap: wrap;
    }

    .tm-brand {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .tm-title {
      font-size: 38px;
      line-height: 1.08;
      margin: 0;
      color: var(--tm-text);
      letter-spacing: -0.02em;
    }

    .tm-sub {
      margin: 0;
      color: var(--tm-muted);
      font-size: 15px;
    }

    .tm-badges {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
    }

    .tm-badge {
      border: 1px solid var(--tm-panel-border);
      padding: 8px 12px;
      border-radius: 999px;
      color: var(--tm-text);
      font-size: 12px;
      letter-spacing: 0.02em;
    }

    .tm-grid {
      display: grid;
      grid-template-columns: 1.2fr 0.8fr;
      gap: 16px;
      margin-top: 18px;
    }

    @media (max-width: 860px) {
      .tm-grid {
        grid-template-columns: 1fr;
      }
    }

    .tm-panel {
      border: 1px solid var(--tm-panel-border);
      background: rgba(0, 0, 0, 0.20);
      border-radius: 14px;
      padding: 18px;
    }

    .tm-panel h2 {
      margin: 0 0 10px 0;
      font-size: 16px;
      color: var(--tm-text);
    }

    .tm-panel p {
      margin: 0;
      color: var(--tm-muted);
      font-size: 14px;
    }

    .tm-playerRow {
      display: flex;
      gap: 14px;
      align-items: center;
      flex-wrap: wrap;
      margin-top: 14px;
    }

    .tm-audio {
      width: min(520px, 100%);
    }

    .tm-now {
      display: grid;
      grid-template-columns: 1fr;
      gap: 8px;
      margin-top: 12px;
    }

    .tm-kv {
      display: flex;
      justify-content: space-between;
      gap: 12px;
    }

    .tm-kv .k {
      color: var(--tm-muted);
      font-size: 12px;
    }

    .tm-kv .v {
      color: var(--tm-text);
      font-size: 12px;
      text-align: right;
    }

    .tm-actions {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      margin-top: 16px;
    }

    .tm-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 10px;
      border: 1px solid var(--tm-panel-border);
      border-radius: 12px;
      padding: 10px 12px;
      color: var(--tm-text);
      text-decoration: none;
      font-size: 13px;
      background: rgba(255, 255, 255, 0.03);
    }

    .tm-btn:hover {
      background: rgba(255, 255, 255, 0.06);
    }

    /* Small buttons for artist platform links */
    .tm-btn--small {
      padding: 8px 10px;
      font-size: 12px;
      border-radius: 10px;
      gap: 8px;
    }

    .tm-artist-links {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      margin-top: 10px;
    }

    .tm-list {
      margin: 12px 0 0 0;
      padding: 0;
      list-style: none;
      display: grid;
      gap: 10px;
    }

    .tm-item {
      padding: 12px;
      border: 1px solid var(--tm-panel-border);
      border-radius: 12px;
      background: rgba(255, 255, 255, 0.03);
    }

    .tm-item .a {
      color: var(--tm-text);
      font-size: 13px;
      margin: 0 0 4px 0;
    }

    .tm-item .b {
      color: var(--tm-muted);
      font-size: 12px;
      margin: 0;
    }

    .tm-foot {
      margin-top: 16px;
      display: flex;
      justify-content: space-between;
      gap: 12px;
      flex-wrap: wrap;
    }

    .tm-foot small {
      color: var(--tm-muted);
    }

    .tm-link {
      color: var(--tm-text);
      text-decoration: none;
      border-bottom: 1px solid rgba(255, 255, 255, 0.18);
    }

    .tm-link:hover {
      border-bottom-color: rgba(255, 255, 255, 0.45);
    }

    .tm-note {
      margin-top: 10px;
      font-size: 12px;
      color: var(--tm-muted);
    }

    code {
      color: var(--tm-text);
    }

    /* Light mode overrides (placed last so they win the cascade) */
    @media (prefers-color-scheme: light) {
      body {
        background:
          radial-gradient(1200px 700px at 10% 10%, rgba(52, 230, 220, 0.22), rgba(0, 0, 0, 0) 58%),
          radial-gradient(1200px 700px at 90% 12%, rgba(31, 75, 255, 0.16), rgba(0, 0, 0, 0) 55%),
          radial-gradient(1200px 700px at 70% 92%, rgba(52, 230, 220, 0.14), rgba(0, 0, 0, 0) 58%),
          linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(246, 249, 255, 1));
      }

      /* Card becomes bright “glass” with a faint brand edge */
      .tm-card {
        background: rgba(255, 255, 255, 0.88);
        border-color: rgba(10, 15, 25, 0.10);
        backdrop-filter: blur(18px);
        box-shadow:
          0 24px 70px rgba(10, 15, 25, 0.10),
          0 2px 12px rgba(10, 15, 25, 0.06);
        position: relative;
        overflow: hidden;
      }

      .tm-card::before {
        content: "";
        position: absolute;
        inset: 0;
        pointer-events: none;
        background:
          radial-gradient(900px 500px at 0% 0%, rgba(52, 230, 220, 0.10), rgba(0, 0, 0, 0) 55%),
          radial-gradient(900px 500px at 100% 0%, rgba(31, 75, 255, 0.08), rgba(0, 0, 0, 0) 55%);
        opacity: 1;
      }

      .tm-title {
        color: rgba(10, 15, 25, 0.86);
      }

      .tm-sub {
        color: rgba(10, 15, 25, 0.60);
      }

      .tm-panel h2 {
        color: rgba(10, 15, 25, 0.86);
      }

      .tm-panel p {
        color: rgba(10, 15, 25, 0.60);
      }

      .tm-kv .k {
        color: rgba(10, 15, 25, 0.58);
      }

      .tm-kv .v {
        color: rgba(10, 15, 25, 0.86);
      }

      /* Panels: bright white with a subtle gradient border */
      .tm-panel {
        background: rgba(255, 255, 255, 0.94);
        border-color: rgba(10, 15, 25, 0.10);
        position: relative;
      }

      /* soft brand “accent rail” on the player panel only */
      section.tm-panel[aria-label="radio player"] {
        border-left: 4px solid rgba(52, 230, 220, 0.55);
      }

      /* Buttons: white base + tasteful brand hover */
      .tm-btn {
        background: rgba(255, 255, 255, 0.95);
        color: rgba(10, 15, 25, 0.86);
        border-color: rgba(10, 15, 25, 0.12);
      }

      .tm-btn:hover {
        background: linear-gradient(90deg, rgba(52, 230, 220, 0.18), rgba(31, 75, 255, 0.14));
      }

      /* List items */
      .tm-item {
        background: rgba(255, 255, 255, 0.95);
        border-color: rgba(10, 15, 25, 0.10);
      }

      .tm-item .a {
        color: rgba(10, 15, 25, 0.86);
      }

      .tm-item .b {
        color: rgba(10, 15, 25, 0.58);
      }

      /* Badges: slightly more colorful but still professional */
      .tm-badge {
        color: rgba(10, 15, 25, 0.82);
        border-color: rgba(10, 15, 25, 0.12);
        background: linear-gradient(90deg, rgba(52, 230, 220, 0.18), rgba(31, 75, 255, 0.12));
      }

      /* Links */
      .tm-link {
        color: rgba(10, 15, 25, 0.86);
        border-bottom-color: rgba(10, 15, 25, 0.22);
      }

      /* Video element background so it doesn’t read as a dark slab */
      #tm-video {
        background: rgba(10, 15, 25, 0.06);
        border-radius: 10px;
      }

      .tm-note code {
        color: rgba(10, 15, 25, 0.72);
      }

      code {
        color: rgba(10, 15, 25, 0.86);
      }
    }
  </style>
</head>

<body>
  <div class="tm-shell">
    <div class="tm-card">
      <div class="tm-top">
        <div class="tm-brand">
          <h1 class="tm-title">Taliferro Music Radio</h1>
          <p class="tm-sub">Live stream featuring Taliferro Music artists. Press play and let it ride.</p>
        </div>
        <div class="tm-badges" aria-label="station badges">
          <span class="tm-badge">LIVE</span>
          <span class="tm-badge">24/7</span>
          <span class="tm-badge">Taliferro Music Artists</span>
        </div>
      </div>
      <div class="tm-grid">
        <!-- LEFT: PLAYER -->
        <section class="tm-panel" aria-label="radio player">
          <h2>Listen now</h2>
          <p>Tap play. If your browser blocks autoplay, it’s normal — just hit play once.</p>
          <div class="tm-playerRow">
            <video class="tm-audio" id="tm-video" controls playsinline preload="metadata" data-stream-url="https://taliferro-music-radio-633736143723.us-west1.run.app/hls/index.m3u8"> Your browser does not support video playback. </video>
          </div>
          <div class="tm-now" aria-label="now playing">
            <div class="tm-kv">
              <div class="k">Now playing</div>
              <div class="v" id="tm-now-track">Station warm-up</div>
            </div>
            <div class="tm-kv">
              <div class="k">Artist</div>
              <div class="v" id="tm-now-artist">Taliferro Music</div>
            </div>
            <div class="tm-kv">
              <div class="k">Status</div>
              <div class="v" id="tm-status">Ready</div>
            </div>
          </div>
          <div class="tm-actions" aria-label="station actions">
            <a class="tm-btn" href="#shows" aria-label="see show schedule">Show schedule</a>
            <a class="tm-btn" href="#artists" aria-label="see featured artists">Featured artists</a>
            <a class="tm-btn" href="mailto:info@taliferro.com?subject=Taliferro%20Music%20Radio" aria-label="contact Taliferro Music">Contact</a>
          </div>
          <div class="tm-note">
            <div>Stream URL to set: <code>https://taliferro-music-radio-633736143723.us-west1.run.app/hls/index.m3u8</code></div>
          </div>
        </section>
        <!-- RIGHT: SCHEDULE + CTA -->
        <aside class="tm-panel" aria-label="station info">
          <h2 id="shows">Shows</h2>
          <ul class="tm-list" aria-label="show list">
            <li class="tm-item">
              <p class="a">Morning Spin</p>
              <p class="b">Weekdays • 7–10 AM PT • Upbeat sets</p>
            </li>
            <li class="tm-item">
              <p class="a">Office Hours</p>
              <p class="b">Weekdays • 10 AM–3 PM PT • Work flow music</p>
            </li>
            <li class="tm-item">
              <p class="a">After Dark</p>
              <p class="b">Daily • 7 PM–12 AM PT • Slower, deeper cuts</p>
            </li>
          </ul>
          <div style="height:14px"></div>
          <h2 id="artists">Featured</h2>
          <ul class="tm-list" aria-label="artist list">
            <li class="tm-item">
              <p class="a">Calima Shatiday</p>
              <p class="b">Downtempo • Chillout</p>
              <div class="tm-artist-links" aria-label="Calima Shatiday links">
                <a class="tm-btn tm-btn--small" href="https://music.apple.com/us/artist/calima-shatiday/307747843" target="_blank" rel="noopener">Listen on Apple Music</a>
                <a class="tm-btn tm-btn--small" href="https://open.spotify.com/artist/0dvd9DVM2vF5JIIVMQvzQm" target="_blank" rel="noopener">Open in Spotify</a>
              </div>
            </li>
            <li class="tm-item">
              <p class="a">Trestal</p>
              <p class="b">Electronic • Experimental</p>
              <div class="tm-artist-links" aria-label="Trestal links">
                <a class="tm-btn tm-btn--small" href="https://music.apple.com/us/artist/trestal/306791410" target="_blank" rel="noopener">Listen on Apple Music</a>
                <a class="tm-btn tm-btn--small" href="https://open.spotify.com/artist/1vQz9DbSIDoabSCxFNFnyT" target="_blank" rel="noopener">Open in Spotify</a>
              </div>
            </li>
            <li class="tm-item">
              <p class="a">Kerboo</p>
              <p class="b">R&B • HipHop</p>
              <div class="tm-artist-links" aria-label="Kerboo links">
                <a class="tm-btn tm-btn--small" href="https://music.apple.com/us/artist/kerboo/673637442" target="_blank" rel="noopener">Listen on Apple Music</a>
                <a class="tm-btn tm-btn--small" href="https://open.spotify.com/artist/52ZEBVXi2S20Tw7mqw1ijB" target="_blank" rel="noopener">Open in Spotify</a>
              </div>
            </li>
            <li class="tm-item">
              <p class="a">B T S</p>
              <p class="b">Electronic • Dance Music</p>
              <a class="tm-btn tm-btn--small" href="https://b-t-s.bandcamp.com/" target="_blank" rel="noopener">Support on Bandcamp</a>
            </li>
            <li class="tm-item">
              <p class="a">Ty Showers</p>
              <p class="b">Jazz • Fusion</p>
              <a class="tm-btn tm-btn--small" href="https://tyshowers.bandcamp.com/" target="_blank" rel="noopener">Support on Bandcamp</a>
            </li>
          </ul>
        </aside>
      </div>
      <div class="tm-foot">
        <small>© <span id="tm-year"></span> Taliferro Music</small>
        <small>
          <a class="tm-link" href="https://music.taliferro.com" rel="noopener">Taliferro Music</a> &nbsp;•&nbsp; <a class="tm-link" href="#" id="tm-copy-link" aria-label="copy station link">Copy link</a>
        </small>
      </div>
    </div>
  </div>
  <script>
    (function () {
      var yearEl = document.getElementById('tm-year');
      if (yearEl) yearEl.textContent = new Date().getFullYear();

      var video = document.getElementById('tm-video');
      var statusEl = document.getElementById('tm-status');

      function setStatus(s) { if (statusEl) statusEl.textContent = s; }

      if (video) {
        video.addEventListener('play', function () { setStatus('Playing'); });
        video.addEventListener('pause', function () { setStatus('Paused'); });
        video.addEventListener('waiting', function () { setStatus('Buffering'); });
        video.addEventListener('stalled', function () { setStatus('Stalled'); });
        video.addEventListener('error', function () { setStatus('Stream error'); });
        video.addEventListener('ended', function () { setStatus('Ended'); });
      }

      // Copy link helper
      var copyBtn = document.getElementById('tm-copy-link');
      if (copyBtn) {
        copyBtn.addEventListener('click', function (e) {
          e.preventDefault();
          var url = window.location.href;
          if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(url).then(function () {
              copyBtn.textContent = 'Copied';
              setTimeout(function () { copyBtn.textContent = 'Copy link'; }, 1200);
            });
          }
        });
      }

      // OPTIONAL: "Now Playing" fetch (if you add an endpoint later)
      // - Create an endpoint that returns JSON like: {"track":"...","artist":"..."}
      // - Then set NOW_PLAYING_URL and uncomment the polling block.
      //
      // var NOW_PLAYING_URL = '/now-playing.json';
      // var trackEl = document.getElementById('tm-now-track');
      // var artistEl = document.getElementById('tm-now-artist');
      // function pollNowPlaying(){
      //   fetch(NOW_PLAYING_URL, {cache:'no-store'}).then(function(r){ return r.json(); }).then(function(d){
      //     if (d && trackEl) trackEl.textContent = d.track || 'Live';
      //     if (d && artistEl) artistEl.textContent = d.artist || 'Taliferro Music';
      //   }).catch(function(){});
      // }
      // pollNowPlaying();
      // setInterval(pollNowPlaying, 15000);

      // Live "Now Playing" from Cloud Run
      var trackEl = document.getElementById('tm-now-track');
      var artistEl = document.getElementById('tm-now-artist');
      var NOW_PLAYING_URL = 'https://taliferro-music-radio-633736143723.us-west1.run.app/now-playing';

      function updateNowPlaying() {
        fetch(NOW_PLAYING_URL, { cache: 'no-store' })
          .then(function (r) { return r.json(); })
          .then(function (d) {
            if (d && trackEl) trackEl.textContent = d.track || 'Live';
            if (d && artistEl) artistEl.textContent = d.artist || 'Taliferro Music';

          })
          .catch(function () { /* ignore */ });
      }

      // Update on load and periodically
      updateNowPlaying();
      setInterval(updateNowPlaying, 15000);

      // Also update when user presses play
      if (video) {
        video.addEventListener('play', updateNowPlaying);
      }
    })();
  </script>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
  <script>
    (function () {
      // HLS support across browsers
      var video = document.getElementById('tm-video');
      if (!video) return;

      var streamUrl = (video.dataset && video.dataset.streamUrl) ? video.dataset.streamUrl : '';
      if (!streamUrl) return;

      var statusEl = document.getElementById('tm-status');
      function setStatus(s) { try { if (statusEl) statusEl.textContent = s; } catch (e) { } }

      // Safari / iOS Safari: native HLS
      if (video.canPlayType('application/vnd.apple.mpegurl')) {
        video.src = streamUrl;
        return;
      }

      // Chrome/Edge/Firefox: attach hls.js, but only start loading when user presses play
      if (!(window.Hls && window.Hls.isSupported())) {
        setStatus('HLS unsupported');
        return;
      }

      var hls;
      var isAttached = false;

      function attachHlsIfNeeded() {
        if (isAttached) return;
        isAttached = true;

        setStatus('Buffering');
        // Ensure CORS-safe media element behavior
        try { video.crossOrigin = 'anonymous'; } catch (e) { }

        hls = new window.Hls({
          lowLatencyMode: true,
          liveSyncDurationCount: 3,
          maxLiveSyncPlaybackRate: 1.5,
          // Retry / timeout tuning for live streams
          manifestLoadingTimeOut: 20000,
          manifestLoadingMaxRetry: 6,
          manifestLoadingRetryDelay: 1000,
          levelLoadingTimeOut: 20000,
          levelLoadingMaxRetry: 6,
          levelLoadingRetryDelay: 1000,
          fragLoadingTimeOut: 20000,
          fragLoadingMaxRetry: 6,
          fragLoadingRetryDelay: 1000,
          fragLoadingMaxRetryTimeout: 64000
        });

        hls.on(window.Hls.Events.ERROR, function (_, data) {
          try {
            if (data && data.details) setStatus('HLS: ' + data.details);
          } catch (e) { }

          if (data && data.fatal) {
            try { hls.destroy(); } catch (e) { }
            isAttached = false;
            setStatus('Stream error');
          }
        });

        hls.on(window.Hls.Events.MANIFEST_LOADING, function () { setStatus('Loading manifest'); });
        hls.on(window.Hls.Events.MANIFEST_LOADED, function () { setStatus('Manifest loaded'); });
        hls.on(window.Hls.Events.LEVEL_LOADING, function () { setStatus('Loading stream'); });
        hls.on(window.Hls.Events.FRAG_LOADING, function () { setStatus('Loading segment'); });
        hls.on(window.Hls.Events.FRAG_LOADED, function () {
          try {
            if (!video.paused) setStatus('Playing');
          } catch (e) { }
        });

        hls.loadSource(streamUrl);
        hls.attachMedia(video);

        // Stall watchdog: if we stay buffering too long, rebuild the HLS instance
        var lastProgressMs = Date.now();
        var watchdog = setInterval(function () {
          try {
            // readyState < 2 means not enough data to play
            if (video && !video.paused && video.readyState >= 2) {
              lastProgressMs = Date.now();
              return;
            }
            // If attached and user is trying to play but we haven't progressed for 12s, reset
            if (video && !video.paused && (Date.now() - lastProgressMs) > 12000) {
              setStatus('Reconnecting');
              try { if (hls) hls.destroy(); } catch (e) { }
              isAttached = false;
              clearInterval(watchdog);
              attachHlsIfNeeded();
              // Try to play again (still within user's intent context)
              try { video.play(); } catch (e) { }
            }
          } catch (e) { }
        }, 1500);

        // Clear watchdog if the element ends
        video.addEventListener('ended', function () { try { clearInterval(watchdog); } catch (e) { } }, { once: true });
      }

      // Attach as early as possible on a user gesture (before the browser attempts playback)
      function prime() {
        attachHlsIfNeeded();
        // Trigger play inside the user gesture for autoplay-policy reliability
        try {
          if (video.paused) video.play();
        } catch (e) { }
        setTimeout(function () {
          try { if (video && video.paused) video.play(); } catch (e) { }
        }, 600);
      }

      video.addEventListener('pointerdown', prime, { once: true });
      video.addEventListener('mousedown', prime, { once: true });
      video.addEventListener('touchstart', prime, { once: true });
      video.addEventListener('keydown', prime, { once: true });

      // Fallback: if play fires first for any reason, still attach
      video.addEventListener('play', prime, { once: true });
    })();
  </script>
</body>

</html>