<?php
// StreamVault — index.php
// Detects PHP or serves pure HTML/JS (works standalone too)
$isPhp = true;
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>StreamVault — Live TV &amp; On Demand</title>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;1,9..40,300&display=swap" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<style>
/* ═══════════════════════════════════════════════════════════════════
   STREAMVAULT — Design: Obsidian Luxury / Holographic Accents
═══════════════════════════════════════════════════════════════════ */
:root {
  --void:       #050508;
  --deep:       #0c0c14;
  --surface:    #111120;
  --panel:      #171728;
  --rim:        #1e1e35;
  --ghost:      #ffffff08;
  --mist:       #ffffff14;
  --smoke:      #ffffff22;

  --plasma:     #7b6cff;
  --plasma2:    #a78bfa;
  --neon:       #00e5ff;
  --ember:      #ff5f40;
  --jade:       #00e890;
  --gold:       #fbbf24;

  --text:       #e8e8f4;
  --sub:        #8888a8;
  --dim:        #44445a;

  --radius:     14px;
  --radius-sm:  8px;
  --glow-plasma: 0 0 40px #7b6cff44, 0 0 80px #7b6cff22;
  --glow-neon:   0 0 30px #00e5ff55;
  --glow-live:   0 0 20px #ff5f4066;

  --font-display: 'Syne', sans-serif;
  --font-body:    'DM Sans', sans-serif;

  --sidebar-w:  260px;
  --header-h:   64px;
  --bar-h:      72px;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  background: var(--void);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 15px;
  overflow: hidden;
}

/* ── Animated background mesh ─────────────────────────────────── */
body::before {
  content:'';
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(ellipse 80% 50% at 20% 10%, #7b6cff18 0%, transparent 60%),
    radial-gradient(ellipse 60% 40% at 80% 80%, #00e5ff10 0%, transparent 55%),
    radial-gradient(ellipse 40% 60% at 60% 20%, #ff5f4009 0%, transparent 50%);
  animation: meshDrift 20s ease-in-out infinite alternate;
}
@keyframes meshDrift {
  0%   { transform: scale(1) translate(0,0); }
  50%  { transform: scale(1.05) translate(-20px, 15px); }
  100% { transform: scale(1) translate(10px,-10px); }
}

/* Noise grain overlay */
body::after {
  content:'';
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
  opacity: 0.4;
}

/* ── Layout ────────────────────────────────────────────────────── */
#app {
  position: relative; z-index: 1;
  display: grid;
  grid-template-rows: var(--header-h) 1fr var(--bar-h);
  grid-template-columns: var(--sidebar-w) 1fr;
  height: 100vh;
  width: 100vw;
}

/* ── Header ────────────────────────────────────────────────────── */
#header {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 16px;
  padding: 0 24px;
  background: linear-gradient(90deg, #0c0c1a 0%, #101020 100%);
  border-bottom: 1px solid var(--rim);
  backdrop-filter: blur(20px);
  position: relative; z-index: 100;
}

.logo {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--font-display);
  font-size: 1.35rem; font-weight: 800;
  letter-spacing: -0.02em;
  text-decoration: none; color: var(--text);
  flex-shrink: 0;
}
.logo-icon {
  width: 34px; height: 34px;
  background: linear-gradient(135deg, var(--plasma), var(--neon));
  border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.1rem;
  box-shadow: var(--glow-plasma);
  animation: logoPulse 4s ease-in-out infinite;
}
@keyframes logoPulse {
  0%,100% { box-shadow: 0 0 20px #7b6cff55; }
  50%      { box-shadow: 0 0 40px #7b6cff88, 0 0 80px #00e5ff33; }
}

.header-search {
  flex: 1; max-width: 440px; margin: 0 auto 0 20px;
  position: relative;
}
.header-search input {
  width: 100%;
  background: var(--mist);
  border: 1px solid var(--rim);
  border-radius: 50px;
  padding: 9px 20px 9px 44px;
  color: var(--text);
  font-family: var(--font-body); font-size: 0.9rem;
  outline: none;
  transition: border-color .2s, background .2s, box-shadow .2s;
}
.header-search input::placeholder { color: var(--sub); }
.header-search input:focus {
  border-color: var(--plasma);
  background: var(--ghost);
  box-shadow: 0 0 0 3px #7b6cff22;
}
.header-search .icon {
  position: absolute; left: 15px; top: 50%; transform: translateY(-50%);
  color: var(--sub); pointer-events: none;
}

.header-right { display: flex; align-items: center; gap: 12px; margin-left: auto; }

.btn-icon {
  width: 36px; height: 36px;
  background: var(--ghost);
  border: 1px solid var(--rim);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; transition: all .2s; color: var(--sub);
  font-size: 1rem;
}
.btn-icon:hover { background: var(--mist); color: var(--text); border-color: var(--plasma); }

.live-badge {
  display: flex; align-items: center; gap: 6px;
  background: linear-gradient(90deg, #ff5f4022, #ff5f4011);
  border: 1px solid #ff5f4044;
  border-radius: 50px; padding: 5px 12px;
  font-size: 0.75rem; font-weight: 600; color: var(--ember);
  letter-spacing: .05em;
}
.live-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--ember);
  animation: blink 1.4s infinite;
}
@keyframes blink { 0%,100%{opacity:1;} 50%{opacity:0.3;} }

/* ── Sidebar ────────────────────────────────────────────────────── */
#sidebar {
  background: linear-gradient(180deg, #0e0e1c 0%, #0a0a16 100%);
  border-right: 1px solid var(--rim);
  overflow-y: auto; overflow-x: hidden;
  padding-bottom: 20px;
  scrollbar-width: thin;
  scrollbar-color: var(--rim) transparent;
}

.sidebar-section { padding: 20px 16px 8px; }
.sidebar-label {
  font-size: 0.68rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--dim);
  padding: 0 8px; margin-bottom: 8px;
}

.nav-item {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 12px; border-radius: var(--radius-sm);
  cursor: pointer; transition: all .18s;
  color: var(--sub); font-size: 0.9rem;
  border: 1px solid transparent;
  user-select: none;
}
.nav-item:hover {
  background: var(--ghost); color: var(--text);
  border-color: var(--rim);
}
.nav-item.active {
  background: linear-gradient(90deg, #7b6cff18, #7b6cff08);
  color: var(--plasma2);
  border-color: #7b6cff33;
}
.nav-item .nav-icon { font-size: 1.1rem; width: 22px; text-align: center; }
.nav-item .nav-count {
  margin-left: auto; font-size: 0.72rem;
  background: var(--ghost);
  border-radius: 50px; padding: 2px 8px;
  color: var(--dim);
}

.network-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; border-radius: var(--radius-sm);
  cursor: pointer; transition: all .18s;
  color: var(--sub); font-size: 0.85rem;
  border: 1px solid transparent;
}
.network-item:hover { background: var(--ghost); color: var(--text); border-color: var(--rim); }
.network-item.active { background: var(--ghost); color: var(--text); border-color: var(--rim); }
.net-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.net-flag { font-size: 1.1rem; }
.net-count { margin-left: auto; font-size: 0.72rem; color: var(--dim); }

/* ── Main content ───────────────────────────────────────────────── */
#main {
  display: flex; flex-direction: column;
  overflow: hidden;
  position: relative;
}

/* ── Video player area ─────────────────────────────────────────── */
#player-area {
  background: #000;
  position: relative;
  height: 0;
  transition: height .4s cubic-bezier(.4,0,.2,1);
  overflow: hidden;
  flex-shrink: 0;
}
#player-area.active { height: 280px; }
@media (max-height: 700px) { #player-area.active { height: 220px; } }

#video-player {
  width: 100%; height: 100%; object-fit: contain;
  background: #000;
}

.player-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(to top, #00000099 0%, transparent 60%);
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 16px 20px;
  opacity: 0; transition: opacity .25s;
  pointer-events: none;
}
#player-area:hover .player-overlay { opacity: 1; pointer-events: all; }

.player-info { display: flex; align-items: flex-end; gap: 12px; }
.player-channel-name {
  font-family: var(--font-display); font-size: 1.2rem; font-weight: 700;
  color: #fff;
}
.player-controls {
  display: flex; align-items: center; gap: 8px; margin-top: 10px;
}
.pctrl {
  background: #ffffff22; border: none;
  color: #fff; border-radius: 8px; padding: 7px 12px;
  cursor: pointer; font-size: 0.85rem; transition: background .2s;
  backdrop-filter: blur(10px);
}
.pctrl:hover { background: #ffffff44; }
.pctrl.close-player { margin-left: auto; }

.player-loading {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: #000;
}
.spinner {
  width: 40px; height: 40px; border-radius: 50%;
  border: 3px solid var(--rim);
  border-top-color: var(--plasma);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Content area ──────────────────────────────────────────────── */
#content {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: 20px;
  scrollbar-width: thin;
  scrollbar-color: var(--rim) transparent;
}

/* Hero / Feature strip */
#hero {
  display: none;
  margin-bottom: 24px;
}
#hero.visible { display: block; }

.hero-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 12px;
  height: 200px;
}
.hero-main {
  border-radius: var(--radius);
  background: linear-gradient(135deg, #1a1a30, #111128);
  border: 1px solid var(--rim);
  overflow: hidden; position: relative; cursor: pointer;
  transition: transform .2s;
}
.hero-main:hover { transform: scale(1.01); }
.hero-bg {
  position: absolute; inset: 0;
  background: linear-gradient(135deg, #7b6cff22 0%, #00e5ff11 100%);
}
.hero-content {
  position: relative; z-index: 2;
  padding: 24px; height: 100%;
  display: flex; flex-direction: column; justify-content: flex-end;
}
.hero-label {
  font-size: 0.72rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--plasma2); margin-bottom: 6px;
}
.hero-title {
  font-family: var(--font-display); font-size: 1.4rem; font-weight: 800;
  line-height: 1.2; margin-bottom: 12px;
}
.btn-watch {
  display: inline-flex; align-items: center; gap: 8px;
  background: linear-gradient(90deg, var(--plasma), var(--neon));
  border: none; border-radius: 50px;
  padding: 9px 20px; color: #fff;
  font-family: var(--font-body); font-size: 0.85rem; font-weight: 600;
  cursor: pointer; transition: opacity .2s, transform .2s;
  align-self: flex-start;
}
.btn-watch:hover { opacity: 0.9; transform: translateY(-1px); }

.hero-side {
  display: flex; flex-direction: column; gap: 8px;
}
.hero-side-item {
  flex: 1; border-radius: var(--radius-sm);
  background: var(--panel); border: 1px solid var(--rim);
  padding: 12px 14px; cursor: pointer; transition: all .2s;
  display: flex; align-items: center; gap: 10px;
}
.hero-side-item:hover { border-color: var(--plasma); background: var(--ghost); }
.hsi-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--jade); flex-shrink: 0; }
.hsi-name { font-size: 0.85rem; font-weight: 500; }
.hsi-net { font-size: 0.72rem; color: var(--sub); }

/* ── Filters bar ───────────────────────────────────────────────── */
.filters-bar {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 20px; flex-wrap: wrap;
}
.filter-chip {
  padding: 7px 16px; border-radius: 50px;
  border: 1px solid var(--rim); background: var(--ghost);
  color: var(--sub); font-size: 0.82rem; cursor: pointer;
  transition: all .18s; white-space: nowrap;
  font-family: var(--font-body);
}
.filter-chip:hover { border-color: var(--plasma); color: var(--plasma2); }
.filter-chip.active {
  background: linear-gradient(90deg, #7b6cff, #a78bfa);
  border-color: transparent; color: #fff;
  box-shadow: 0 0 20px #7b6cff44;
}

/* ── Section heading ─────────────────────────────────────────────  */
.section-head {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 14px;
}
.section-title {
  font-family: var(--font-display); font-size: 1rem; font-weight: 700;
}
.section-count { font-size: 0.78rem; color: var(--sub); }
.section-action {
  margin-left: auto; font-size: 0.8rem; color: var(--plasma2);
  cursor: pointer; background: none; border: none; font-family: var(--font-body);
}
.section-action:hover { text-decoration: underline; }

/* ── Channel grid ───────────────────────────────────────────────── */
#channel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
}

.channel-card {
  background: var(--panel);
  border: 1px solid var(--rim);
  border-radius: var(--radius);
  padding: 16px;
  cursor: pointer;
  transition: all .22s cubic-bezier(.4,0,.2,1);
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 10px;
  animation: cardIn .4s ease-out both;
}
@keyframes cardIn {
  from { opacity:0; transform: translateY(12px) scale(.97); }
  to   { opacity:1; transform: none; }
}
.channel-card::before {
  content:'';
  position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, var(--plasma), var(--neon));
  opacity: 0; transition: opacity .2s;
}
.channel-card:hover {
  transform: translateY(-4px);
  border-color: #7b6cff55;
  box-shadow: 0 12px 40px #00000066, 0 0 0 1px #7b6cff22;
}
.channel-card:hover::before { opacity: 1; }

.card-logo {
  width: 100%; height: 52px;
  display: flex; align-items: center; justify-content: center;
}
.card-logo img {
  max-width: 80%; max-height: 48px;
  object-fit: contain; filter: brightness(0.9);
  transition: filter .2s;
}
.channel-card:hover .card-logo img { filter: brightness(1.1); }
.logo-fallback {
  width: 44px; height: 44px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.3rem; font-weight: 800;
  font-family: var(--font-display);
  color: #fff; background: linear-gradient(135deg, var(--plasma), var(--neon));
}

.card-name {
  font-size: 0.9rem; font-weight: 600;
  line-height: 1.3; color: var(--text);
  font-family: var(--font-display);
}
.card-meta {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-top: auto;
}
.card-category {
  font-size: 0.7rem; padding: 3px 8px; border-radius: 50px;
  background: var(--ghost); border: 1px solid var(--rim);
  color: var(--sub);
}
.card-live-badge {
  font-size: 0.68rem; font-weight: 700;
  letter-spacing: .06em; padding: 3px 7px; border-radius: 50px;
  background: #ff5f4022; border: 1px solid #ff5f4044; color: var(--ember);
}
.card-net {
  margin-left: auto; font-size: 0.72rem; color: var(--dim);
}
.net-color-bar {
  position: absolute; bottom: 0; left: 0; right: 0; height: 2px;
  opacity: 0.4;
}

/* ── Bottom player bar ──────────────────────────────────────────── */
#playerbar {
  grid-column: 1 / -1;
  background: linear-gradient(90deg, #0d0d1c 0%, #0a0a18 100%);
  border-top: 1px solid var(--rim);
  display: flex; align-items: center; gap: 16px;
  padding: 0 20px;
  position: relative; z-index: 100;
  transition: all .3s;
}
#playerbar.hidden { opacity: 0; pointer-events: none; }

.bar-thumb {
  width: 44px; height: 44px; border-radius: 10px;
  object-fit: cover;
  background: var(--rim);
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.2rem; font-weight: 700;
  color: #fff; overflow: hidden;
}
.bar-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.bar-name { font-size: 0.9rem; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bar-network { font-size: 0.75rem; color: var(--sub); }

.bar-controls { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.bar-btn {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--ghost); border: 1px solid var(--rim);
  color: var(--sub); display: flex; align-items: center; justify-content: center;
  cursor: pointer; transition: all .18s; font-size: 0.95rem;
}
.bar-btn:hover { background: var(--mist); color: var(--text); border-color: var(--plasma); }
.bar-btn.primary {
  background: linear-gradient(135deg, var(--plasma), var(--plasma2));
  border-color: transparent; color: #fff;
  box-shadow: 0 0 20px #7b6cff55;
}
.bar-btn.primary:hover { box-shadow: 0 0 30px #7b6cff88; }

.volume-wrap { display: flex; align-items: center; gap: 8px; }
#volume-slider {
  -webkit-appearance: none; appearance: none;
  width: 70px; height: 3px;
  background: linear-gradient(90deg, var(--plasma) var(--vol,70%), var(--rim) var(--vol,70%));
  border-radius: 99px; cursor: pointer; outline: none;
}
#volume-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--plasma2);
  box-shadow: 0 0 6px #7b6cff88;
  transition: transform .15s;
}
#volume-slider::-webkit-slider-thumb:hover { transform: scale(1.3); }

/* ── Toasts ─────────────────────────────────────────────────────── */
#toast-wrap {
  position: fixed; bottom: 90px; right: 20px; z-index: 9999;
  display: flex; flex-direction: column; gap: 8px;
  pointer-events: none;
}
.toast {
  background: var(--panel); border: 1px solid var(--rim);
  border-radius: var(--radius-sm); padding: 12px 16px;
  font-size: 0.85rem; color: var(--text);
  max-width: 280px; backdrop-filter: blur(20px);
  animation: toastIn .3s ease-out;
  pointer-events: all;
}
@keyframes toastIn { from { opacity:0; transform: translateX(20px); } }
.toast.error { border-color: #ff5f4055; color: var(--ember); }
.toast.success { border-color: #00e89055; color: var(--jade); }

/* ── Loading screen ─────────────────────────────────────────────── */
#loading-screen {
  position: fixed; inset: 0; z-index: 9000;
  background: var(--void);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 20px;
  transition: opacity .6s;
}
#loading-screen.fade { opacity: 0; pointer-events: none; }
.loading-logo {
  font-family: var(--font-display); font-size: 2rem; font-weight: 800;
  background: linear-gradient(90deg, var(--plasma2), var(--neon));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.loading-bar {
  width: 200px; height: 3px; background: var(--rim); border-radius: 99px; overflow: hidden;
}
.loading-fill {
  height: 100%; width: 0;
  background: linear-gradient(90deg, var(--plasma), var(--neon));
  border-radius: 99px;
  transition: width .3s ease;
}
.loading-status { font-size: 0.8rem; color: var(--sub); }

/* ── Empty / error states ────────────────────────────────────────── */
.empty-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 60px 20px; color: var(--sub); text-align: center; gap: 12px;
}
.empty-icon { font-size: 3rem; }
.empty-title { font-family: var(--font-display); font-size: 1.1rem; color: var(--text); }

/* ── Responsive ─────────────────────────────────────────────────── */
@media (max-width: 860px) {
  :root { --sidebar-w: 56px; }
  .nav-item span:not(.nav-icon), .sidebar-label,
  .network-item .net-name, .net-count, .network-item .net-flag { display: none; }
  .network-item { justify-content: center; padding: 10px; }
  .nav-item { justify-content: center; }
  .nav-item .nav-count { display: none; }
  .hero-grid { grid-template-columns: 1fr; }
  .hero-side { display: none; }
}
@media (max-width: 600px) {
  #channel-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .header-search { max-width: 200px; }
}
</style>
</head>
<body>

<!-- Loading screen -->
<div id="loading-screen">
  <div class="loading-logo">⚡ StreamVault</div>
  <div class="loading-bar"><div class="loading-fill" id="load-fill"></div></div>
  <div class="loading-status" id="load-status">Connecting to networks…</div>
</div>

<!-- Toast container -->
<div id="toast-wrap"></div>

<div id="app">

  <!-- ══ HEADER ══════════════════════════════════════════════════ -->
  <header id="header">
    <a class="logo" href="#">
      <div class="logo-icon">⚡</div>
      StreamVault
    </a>
    <div class="header-search">
      <span class="icon">🔍</span>
      <input type="text" id="search-input" placeholder="Search channels, networks, categories…" autocomplete="off"/>
    </div>
    <div class="header-right">
      <div class="live-badge"><div class="live-dot"></div>LIVE</div>
      <button class="btn-icon" id="btn-refresh" title="Refresh channels">↺</button>
      <button class="btn-icon" id="btn-grid-toggle" title="Toggle layout">⊞</button>
      <button class="btn-icon" title="Settings">⚙</button>
    </div>
  </header>

  <!-- ══ SIDEBAR ═════════════════════════════════════════════════ -->
  <aside id="sidebar">
    <div class="sidebar-section">
      <div class="sidebar-label">Navigate</div>
      <div class="nav-item active" data-view="home">
        <span class="nav-icon">🏠</span><span>Home</span>
      </div>
      <div class="nav-item" data-view="live">
        <span class="nav-icon">📡</span><span>Live TV</span>
        <span class="nav-count" id="count-live">—</span>
      </div>
      <div class="nav-item" data-view="news">
        <span class="nav-icon">📰</span><span>News</span>
      </div>
      <div class="nav-item" data-view="sports">
        <span class="nav-icon">⚽</span><span>Sports</span>
      </div>
      <div class="nav-item" data-view="movies">
        <span class="nav-icon">🎬</span><span>Movies</span>
      </div>
      <div class="nav-item" data-view="entertainment">
        <span class="nav-icon">🎭</span><span>Entertainment</span>
      </div>
      <div class="nav-item" data-view="favourites">
        <span class="nav-icon">❤️</span><span>Favourites</span>
        <span class="nav-count" id="count-favs">0</span>
      </div>
    </div>

    <div class="sidebar-section">
      <div class="sidebar-label">Regions</div>
      <div class="nav-item" data-region="NZ">
        <span class="nav-icon">🇳🇿</span><span>New Zealand</span>
      </div>
      <div class="nav-item" data-region="UK">
        <span class="nav-icon">🇬🇧</span><span>United Kingdom</span>
      </div>
      <div class="nav-item" data-region="US">
        <span class="nav-icon">🇺🇸</span><span>United States</span>
      </div>
    </div>

    <div class="sidebar-section">
      <div class="sidebar-label">Networks</div>
      <div id="network-list">
        <!-- Populated by JS -->
      </div>
    </div>
  </aside>

  <!-- ══ MAIN ════════════════════════════════════════════════════ -->
  <main id="main">

    <!-- Inline video player -->
    <div id="player-area">
      <div class="player-loading" id="player-loading"><div class="spinner"></div></div>
      <video id="video-player" playsinline controls></video>
      <div class="player-overlay">
        <div class="player-info">
          <div>
            <div class="player-channel-name" id="player-title">—</div>
          </div>
        </div>
        <div class="player-controls">
          <button class="pctrl" onclick="toggleFullscreen()">⛶ Fullscreen</button>
          <button class="pctrl" id="fav-btn" onclick="toggleFav()">♡ Favourite</button>
          <button class="pctrl close-player" onclick="closePlayer()">✕ Close</button>
        </div>
      </div>
    </div>

    <!-- Content scroll area -->
    <div id="content">
      <!-- Hero (home only) -->
      <div id="hero" class="visible">
        <div class="hero-grid">
          <div class="hero-main" id="hero-main">
            <div class="hero-bg"></div>
            <div class="hero-content">
              <div class="hero-label">✦ Featured Live</div>
              <div class="hero-title" id="hero-title">Loading channels…</div>
              <button class="btn-watch" id="hero-watch-btn">▶ Watch Now</button>
            </div>
          </div>
          <div class="hero-side" id="hero-side">
            <!-- Populated by JS -->
          </div>
        </div>
      </div>

      <!-- Filters -->
      <div class="filters-bar" id="filters-bar">
        <button class="filter-chip active" data-filter="all">All</button>
        <!-- Dynamic category chips added by JS -->
      </div>

      <!-- Channel section -->
      <div class="section-head">
        <div class="section-title" id="section-title">All Channels</div>
        <div class="section-count" id="section-count"></div>
        <button class="section-action" id="sort-btn">Sort ↕</button>
      </div>

      <div id="channel-grid">
        <!-- Populated by JS -->
      </div>
    </div>
  </main>

  <!-- ══ PLAYER BAR ═══════════════════════════════════════════════ -->
  <div id="playerbar" class="hidden">
    <div class="bar-thumb" id="bar-thumb">⚡</div>
    <div class="bar-info">
      <div class="bar-name" id="bar-name">Nothing playing</div>
      <div class="bar-network" id="bar-network">—</div>
    </div>
    <div class="bar-controls">
      <div class="volume-wrap">
        <span style="color:var(--sub);font-size:.9rem">🔊</span>
        <input type="range" id="volume-slider" min="0" max="100" value="70"/>
      </div>
      <button class="bar-btn" onclick="closePlayer()" title="Stop">⏹</button>
      <button class="bar-btn primary" id="bar-play-btn" onclick="togglePlay()" title="Play/Pause">⏸</button>
      <button class="bar-btn" onclick="openPlayerPanel()" title="Expand">⬆</button>
    </div>
  </div>
</div>

<script>
/* ═══════════════════════════════════════════════════════════════════
   StreamVault — Frontend Engine
   Talks to backend-engine.php; falls back to direct JSON loading
═══════════════════════════════════════════════════════════════════ */

const API = 'backend-engine.php';
const USE_BACKEND = (location.protocol !== 'file:'); // false when opened as static file

let STATE = {
  channels:      [],
  networks:      [],
  categories:    [],
  filtered:      [],
  favourites:    JSON.parse(localStorage.getItem('sv_favs') || '[]'),
  currentChannel: null,
  view:          'home',
  filter:        'all',
  searchQuery:   '',
  sortMode:      'default',
  gridMode:      'grid',   // 'grid' | 'list'
  isPlaying:     false,
};

let hls = null;
const video = document.getElementById('video-player');

// ─── LOADING SCREEN ─────────────────────────────────────────────
function setLoadProgress(pct, msg) {
  document.getElementById('load-fill').style.width = pct + '%';
  if (msg) document.getElementById('load-status').textContent = msg;
}
function hideLoader() {
  const l = document.getElementById('loading-screen');
  l.classList.add('fade');
  setTimeout(() => l.remove(), 700);
}

// ─── DATA FETCHING ───────────────────────────────────────────────
async function fetchJSON(url) {
  const r = await fetch(url, { cache: 'no-store' });
  if (!r.ok) throw new Error(`HTTP ${r.status} for ${url}`);
  return r.json();
}

async function loadFromBackend() {
  setLoadProgress(20, 'Fetching channels from engine…');
  const data = await fetchJSON(`${API}?action=channels`);
  setLoadProgress(60, 'Loading networks…');
  const nets = await fetchJSON(`${API}?action=networks`);
  setLoadProgress(80, 'Loading categories…');
  const cats = await fetchJSON(`${API}?action=categories`);
  return { channels: data.channels, networks: nets.networks, categories: cats.categories };
}

async function loadDirect() {
  // Fallback: load networks.json then each network file directly
  setLoadProgress(15, 'Loading networks.json…');
  const netList = await fetchJSON('networks.json');
  const files = netList.files || [];

  let allChannels = [];
  let allNetworks = [];
  let categorySet = new Set();

  for (let i = 0; i < files.length; i++) {
    const f = files[i];
    setLoadProgress(15 + Math.round((i / files.length) * 70), `Loading ${f}…`);
    try {
      const data = await fetchJSON(`json/${f}`);
      if (!data.channels) continue;
      const channels = data.channels.map(ch => ({
        ...ch,
        network:    data.network || f,
        network_id: f,
        region:     data.region  || '',
        flag:       data.flag    || '',
        netColor:   data.color   || '#7b6cff',
      }));
      allChannels.push(...channels);
      channels.forEach(ch => ch.category && categorySet.add(ch.category));
      allNetworks.push({
        file:   f,
        name:   data.network || f,
        region: data.region  || '',
        flag:   data.flag    || '',
        color:  data.color   || '#7b6cff',
        count:  channels.length,
      });
    } catch (e) { console.warn('Failed to load', f, e); }
  }
  return { channels: allChannels, networks: allNetworks, categories: [...categorySet] };
}

async function initData() {
  try {
    let data;
    if (USE_BACKEND) {
      try { data = await loadFromBackend(); }
      catch (e) {
        console.warn('Backend failed, trying direct:', e);
        toast('Backend unavailable — loading direct', 'error');
        data = await loadDirect();
      }
    } else {
      data = await loadDirect();
    }

    setLoadProgress(95, 'Building interface…');
    STATE.channels   = data.channels;
    STATE.networks   = data.networks;
    STATE.categories = data.categories;
    STATE.filtered   = [...data.channels];

    renderSidebar();
    buildCategoryChips();
    updateGrid();
    renderHero();

    document.getElementById('count-live').textContent = data.channels.filter(c=>c.type==='live').length;
    document.getElementById('count-favs').textContent = STATE.favourites.length;

    setLoadProgress(100, 'Ready!');
    setTimeout(hideLoader, 400);

    toast(`Loaded ${data.channels.length} channels across ${data.networks.length} networks`, 'success');
  } catch (e) {
    console.error(e);
    document.getElementById('load-status').textContent = 'Error: ' + e.message;
    toast('Failed to load channels: ' + e.message, 'error');
  }
}

// ─── RENDER SIDEBAR ──────────────────────────────────────────────
function renderSidebar() {
  const el = document.getElementById('network-list');
  el.innerHTML = STATE.networks.map(n => `
    <div class="network-item" data-network="${n.file}" onclick="filterByNetwork('${n.file}')">
      <div class="net-dot" style="background:${n.color}"></div>
      <span class="net-flag">${n.flag}</span>
      <span class="net-name">${n.name}</span>
      <span class="net-count">${n.count}</span>
    </div>
  `).join('');
}

// ─── RENDER HERO ─────────────────────────────────────────────────
function renderHero() {
  const live = STATE.channels.filter(c => c.type === 'live');
  if (!live.length) return;

  const featured = live[0];
  document.getElementById('hero-title').textContent = featured.name;
  const btn = document.getElementById('hero-watch-btn');
  btn.onclick = () => playChannel(featured);

  const side = document.getElementById('hero-side');
  const picks = live.slice(1, 3);
  side.innerHTML = picks.map(ch => `
    <div class="hero-side-item" onclick="playChannel(${JSON.stringify(ch).replace(/"/g,"'")})">
      <div class="hsi-dot" style="background:${ch.netColor}"></div>
      <div>
        <div class="hsi-name">${ch.name}</div>
        <div class="hsi-net">${ch.flag} ${ch.network}</div>
      </div>
    </div>
  `).join('');
}

// ─── CATEGORY CHIPS ─────────────────────────────────────────────
function buildCategoryChips() {
  const bar = document.getElementById('filters-bar');
  bar.innerHTML = `<button class="filter-chip active" data-filter="all" onclick="setFilter('all')">All</button>`;
  STATE.categories.sort().forEach(cat => {
    const btn = document.createElement('button');
    btn.className = 'filter-chip';
    btn.dataset.filter = cat;
    btn.textContent = cat;
    btn.onclick = () => setFilter(cat);
    bar.appendChild(btn);
  });
}

function setFilter(cat) {
  STATE.filter = cat;
  document.querySelectorAll('.filter-chip').forEach(b => {
    b.classList.toggle('active', b.dataset.filter === cat);
  });
  applyFiltersAndRender();
}

// ─── FILTERING ───────────────────────────────────────────────────
function applyFiltersAndRender() {
  let chs = [...STATE.channels];

  if (STATE.searchQuery) {
    const q = STATE.searchQuery.toLowerCase();
    chs = chs.filter(c =>
      [c.name, c.category, c.network, c.description, c.region]
        .some(f => (f||'').toLowerCase().includes(q))
    );
  }

  if (STATE.view === 'favourites') {
    chs = chs.filter(c => STATE.favourites.includes(c.id));
  } else if (STATE.view === 'live') {
    chs = chs.filter(c => c.type === 'live');
  } else if (STATE.view !== 'home') {
    chs = chs.filter(c => (c.category||'').toLowerCase() === STATE.view.toLowerCase());
  }

  if (STATE.filter !== 'all') {
    chs = chs.filter(c => c.category === STATE.filter);
  }

  if (STATE.regionFilter) {
    chs = chs.filter(c => c.region === STATE.regionFilter);
  }
  if (STATE.networkFilter) {
    chs = chs.filter(c => c.network_id === STATE.networkFilter);
  }

  if (STATE.sortMode === 'az') chs.sort((a,b) => a.name.localeCompare(b.name));
  if (STATE.sortMode === 'za') chs.sort((a,b) => b.name.localeCompare(a.name));

  STATE.filtered = chs;
  updateGrid();
}

function updateGrid() {
  const grid = document.getElementById('channel-grid');
  const title = document.getElementById('section-title');
  const count = document.getElementById('section-count');

  if (!STATE.filtered.length) {
    grid.innerHTML = `<div class="empty-state" style="grid-column:1/-1">
      <div class="empty-icon">📡</div>
      <div class="empty-title">No channels found</div>
      <div>Try a different filter or search term</div>
    </div>`;
    count.textContent = '';
    return;
  }

  count.textContent = `${STATE.filtered.length} channel${STATE.filtered.length !== 1 ? 's' : ''}`;

  grid.innerHTML = STATE.filtered.map((ch, i) => {
    const isFav = STATE.favourites.includes(ch.id);
    const logoHtml = ch.logo
      ? `<img src="${ch.logo}" alt="${ch.name}" onerror="this.parentNode.innerHTML='<div class=logo-fallback>${ch.name.slice(0,2).toUpperCase()}</div>'" loading="lazy"/>`
      : `<div class="logo-fallback">${(ch.name||'?').slice(0,2).toUpperCase()}</div>`;
    return `
      <div class="channel-card" onclick="playChannel(${JSON.stringify(ch).replace(/\"/g,'&quot;')})"
           style="animation-delay:${Math.min(i * 0.03, 0.5)}s">
        <div class="net-color-bar" style="background:${ch.netColor}"></div>
        <div class="card-logo">${logoHtml}</div>
        <div class="card-name">${ch.name}</div>
        <div class="card-meta">
          ${ch.category ? `<span class="card-category">${ch.category}</span>` : ''}
          ${ch.type === 'live' ? `<span class="card-live-badge">● LIVE</span>` : ''}
          <span class="card-net">${ch.flag||''}</span>
          <span style="margin-left:auto;cursor:pointer;font-size:1rem;color:${isFav?'#ff5f40':'var(--dim)'};"
                onclick="event.stopPropagation();toggleFavById('${ch.id}',this)">
            ${isFav ? '♥' : '♡'}
          </span>
        </div>
      </div>`;
  }).join('');
}

// ─── PLAYER ──────────────────────────────────────────────────────
function playChannel(ch) {
  if (typeof ch === 'string') ch = JSON.parse(ch);
  STATE.currentChannel = ch;

  // Update UI
  document.getElementById('player-area').classList.add('active');
  document.getElementById('player-loading').style.display = 'flex';
  document.getElementById('player-title').textContent = ch.name;
  document.getElementById('playerbar').classList.remove('hidden');
  document.getElementById('bar-name').textContent = ch.name;
  document.getElementById('bar-network').textContent = `${ch.flag || ''} ${ch.network || ''}`;
  document.getElementById('fav-btn').textContent = STATE.favourites.includes(ch.id) ? '♥ Unfavourite' : '♡ Favourite';

  // Bar thumb
  const thumb = document.getElementById('bar-thumb');
  if (ch.logo) {
    thumb.innerHTML = `<img src="${ch.logo}" style="width:100%;height:100%;object-fit:contain" onerror="this.parentNode.textContent='⚡'"/>`;
  } else {
    thumb.textContent = (ch.name||'?').slice(0,2).toUpperCase();
  }

  loadStream(ch.stream);
}

function loadStream(url) {
  if (hls) { hls.destroy(); hls = null; }
  video.src = '';

  if (!url) {
    toast('No stream URL for this channel', 'error');
    return;
  }

  const onReady = () => {
    document.getElementById('player-loading').style.display = 'none';
    STATE.isPlaying = true;
    document.getElementById('bar-play-btn').textContent = '⏸';
  };

  // HLS.js
  if (Hls.isSupported() && (url.includes('.m3u8') || url.includes('m3u'))) {
    hls = new Hls({ enableWorker: true, lowLatencyMode: true });
    hls.loadSource(url);
    hls.attachMedia(video);
    hls.on(Hls.Events.MANIFEST_PARSED, () => {
      video.play().then(onReady).catch(e => {
        toast('Autoplay blocked — click play ▶', 'error');
        document.getElementById('player-loading').style.display = 'none';
      });
    });
    hls.on(Hls.Events.ERROR, (_, d) => {
      if (d.fatal) {
        toast('Stream error: ' + d.type, 'error');
        document.getElementById('player-loading').style.display = 'none';
      }
    });
  } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    // Native HLS (Safari/iOS)
    video.src = url;
    video.play().then(onReady).catch(() => {});
  } else {
    // Direct src fallback
    video.src = url;
    video.play().then(onReady).catch(() => {});
  }
}

function togglePlay() {
  if (!video.src && !hls) return;
  if (video.paused) {
    video.play();
    STATE.isPlaying = true;
    document.getElementById('bar-play-btn').textContent = '⏸';
  } else {
    video.pause();
    STATE.isPlaying = false;
    document.getElementById('bar-play-btn').textContent = '▶';
  }
}

function closePlayer() {
  if (hls) { hls.destroy(); hls = null; }
  video.pause(); video.src = '';
  document.getElementById('player-area').classList.remove('active');
  document.getElementById('playerbar').classList.add('hidden');
  STATE.currentChannel = null; STATE.isPlaying = false;
}

function openPlayerPanel() {
  document.getElementById('player-area').classList.add('active');
  document.getElementById('content').scrollTop = 0;
}

function toggleFullscreen() {
  const el = document.getElementById('player-area');
  if (!document.fullscreenElement) el.requestFullscreen();
  else document.exitFullscreen();
}

// ─── FAVOURITES ──────────────────────────────────────────────────
function toggleFav() {
  if (!STATE.currentChannel) return;
  toggleFavById(STATE.currentChannel.id);
  document.getElementById('fav-btn').textContent = STATE.favourites.includes(STATE.currentChannel.id)
    ? '♥ Unfavourite' : '♡ Favourite';
}
function toggleFavById(id, el) {
  const idx = STATE.favourites.indexOf(id);
  if (idx > -1) {
    STATE.favourites.splice(idx, 1);
    if (el) { el.textContent = '♡'; el.style.color = 'var(--dim)'; }
  } else {
    STATE.favourites.push(id);
    if (el) { el.textContent = '♥'; el.style.color = '#ff5f40'; }
  }
  localStorage.setItem('sv_favs', JSON.stringify(STATE.favourites));
  document.getElementById('count-favs').textContent = STATE.favourites.length;
}

// ─── NAVIGATION ──────────────────────────────────────────────────
document.querySelectorAll('[data-view]').forEach(el => {
  el.addEventListener('click', () => {
    STATE.view = el.dataset.view;
    STATE.regionFilter = null; STATE.networkFilter = null;
    document.querySelectorAll('[data-view],[data-region]').forEach(e => e.classList.remove('active'));
    el.classList.add('active');
    document.getElementById('hero').classList.toggle('visible', STATE.view === 'home');
    document.getElementById('section-title').textContent = el.querySelector('span:last-of-type')?.textContent || 'Channels';
    applyFiltersAndRender();
  });
});

document.querySelectorAll('[data-region]').forEach(el => {
  el.addEventListener('click', () => {
    STATE.regionFilter = el.dataset.region;
    STATE.view = 'region'; STATE.networkFilter = null;
    document.querySelectorAll('[data-view],[data-region]').forEach(e => e.classList.remove('active'));
    el.classList.add('active');
    document.getElementById('hero').classList.remove('visible');
    document.getElementById('section-title').textContent = el.querySelector('span:last-of-type')?.textContent + ' Channels';
    applyFiltersAndRender();
  });
});

function filterByNetwork(file) {
  STATE.networkFilter = file; STATE.regionFilter = null; STATE.view = 'network';
  document.querySelectorAll('.network-item').forEach(e => e.classList.remove('active'));
  document.querySelector(`.network-item[data-network="${file}"]`)?.classList.add('active');
  document.getElementById('hero').classList.remove('visible');
  const net = STATE.networks.find(n => n.file === file);
  document.getElementById('section-title').textContent = (net?.name || file) + ' Channels';
  applyFiltersAndRender();
}

// ─── SEARCH ──────────────────────────────────────────────────────
let searchTimeout;
document.getElementById('search-input').addEventListener('input', e => {
  clearTimeout(searchTimeout);
  searchTimeout = setTimeout(() => {
    STATE.searchQuery = e.target.value.trim();
    applyFiltersAndRender();
  }, 220);
});

// ─── SORT ────────────────────────────────────────────────────────
document.getElementById('sort-btn').addEventListener('click', () => {
  const modes = ['default', 'az', 'za'];
  const i = modes.indexOf(STATE.sortMode);
  STATE.sortMode = modes[(i + 1) % modes.length];
  document.getElementById('sort-btn').textContent = { default: 'Sort ↕', az: 'A→Z ↑', za: 'Z→A ↓' }[STATE.sortMode];
  applyFiltersAndRender();
});

// ─── REFRESH ─────────────────────────────────────────────────────
document.getElementById('btn-refresh').addEventListener('click', async () => {
  toast('Refreshing channels…');
  if (USE_BACKEND) {
    await fetch(`${API}?action=refresh`).catch(() => {});
  }
  await initData();
});

// ─── GRID TOGGLE ─────────────────────────────────────────────────
document.getElementById('btn-grid-toggle').addEventListener('click', () => {
  STATE.gridMode = STATE.gridMode === 'grid' ? 'list' : 'grid';
  document.getElementById('channel-grid').style.gridTemplateColumns =
    STATE.gridMode === 'list' ? '1fr' : '';
  document.getElementById('btn-grid-toggle').textContent = STATE.gridMode === 'list' ? '⊟' : '⊞';
});

// ─── VOLUME ──────────────────────────────────────────────────────
document.getElementById('volume-slider').addEventListener('input', e => {
  const v = e.target.value;
  video.volume = v / 100;
  e.target.style.setProperty('--vol', v + '%');
});

// ─── TOASTS ──────────────────────────────────────────────────────
function toast(msg, type = '') {
  const wrap = document.getElementById('toast-wrap');
  const el = document.createElement('div');
  el.className = 'toast ' + type;
  el.textContent = msg;
  wrap.appendChild(el);
  setTimeout(() => el.remove(), 3500);
}

// ─── KEYBOARD SHORTCUTS ──────────────────────────────────────────
document.addEventListener('keydown', e => {
  if (e.target.tagName === 'INPUT') return;
  if (e.code === 'Space') { e.preventDefault(); togglePlay(); }
  if (e.code === 'Escape') closePlayer();
  if (e.code === 'KeyF') toggleFullscreen();
  if (e.code === 'ArrowUp') { video.volume = Math.min(1, video.volume + .1); }
  if (e.code === 'ArrowDown') { video.volume = Math.max(0, video.volume - .1); }
  if (e.code === 'KeyS') document.getElementById('search-input').focus();
});

// ─── INIT ────────────────────────────────────────────────────────
initData();
</script>
</body>
</html>
