const { useState, useEffect, useRef, useMemo } = React;

/* ============ ICONS ============ */
const Icon = ({ d, size = 20, fill = "none", stroke = "currentColor", sw = 1.6, children }) => (
  <svg viewBox="0 0 24 24" width={size} height={size} fill={fill} stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round">
    {d ? <path d={d} /> : children}
  </svg>
);
const Sparkle = ({ size = 14, className = "spark" }) => (
  <svg className={className} viewBox="0 0 24 24" width={size} height={size}>
    <path d="M12 2 L13.8 9.4 L21 11.2 L13.8 13 L12 20.4 L10.2 13 L3 11.2 L10.2 9.4 Z" />
    <path d="M19 3 L19.7 5.3 L22 6 L19.7 6.7 L19 9 L18.3 6.7 L16 6 L18.3 5.3 Z" />
  </svg>
);
const IconSearch = (p) => <Icon {...p} d="M11 19a8 8 0 1 1 5.3-2L21 21" />;
const IconMic = (p) => <Icon {...p}><rect x="9" y="3" width="6" height="12" rx="3"/><path d="M5 11a7 7 0 0 0 14 0M12 18v3"/></Icon>;
const IconBell = (p) => <Icon {...p} d="M6 9a6 6 0 1 1 12 0c0 5 2 6 2 6H4s2-1 2-6M10 21a2 2 0 0 0 4 0" />;
const IconPlus = (p) => <Icon {...p} d="M12 5v14M5 12h14" />;
const IconClose = (p) => <Icon {...p} d="M6 6l12 12M18 6L6 18" />;
const IconClock = (p) => <Icon {...p}><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></Icon>;
const IconMenu = (p) => <Icon {...p} d="M4 6h16M4 12h16M4 18h16" />;
const IconHome = (p) => <Icon {...p} d="M3 11l9-8 9 8M5 10v10h14V10" />;
const IconShorts = (p) => <Icon {...p}><path d="M17 4l-2 1.2v13.6L17 20a3 3 0 0 0 4-2.6V6.6A3 3 0 0 0 17 4z"/><path d="M7 4l2 1.2v13.6L7 20a3 3 0 0 1-4-2.6V6.6A3 3 0 0 1 7 4z"/><path d="M10 9l5 3-5 3z" fill="currentColor"/></Icon>;
const IconSubs = (p) => <Icon {...p}><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M7 3h10M10 11l5 3-5 3z" fill="currentColor"/></Icon>;
const IconYou = (p) => <Icon {...p}><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></Icon>;
const IconHistory = (p) => <Icon {...p}><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l3 2"/></Icon>;
const IconPlaylist = (p) => <Icon {...p} d="M4 6h13M4 12h13M4 18h9M17 14l5 4-5 4z" />;
const IconThumbUp = (p) => <Icon {...p} d="M7 22V9l5-7c1 0 2 1 2 2v5h5a2 2 0 0 1 2 2l-2 8a2 2 0 0 1-2 2H10a3 3 0 0 1-3-3z" />;
const IconPlay = (p) => <Icon {...p} fill="currentColor" stroke="none" d="M7 5l12 7-12 7z" />;
const IconArrow = (p) => <Icon {...p} d="M5 12h14M13 5l7 7-7 7" />;
const IconCopy = (p) => <Icon {...p}><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></Icon>;
const IconShare = (p) => <Icon {...p} d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7M16 6l-4-4-4 4M12 2v14" />;
const IconRefresh = (p) => <Icon {...p}><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 3v6h-6"/></Icon>;

/* ============ DATA ============ */
const SUGGESTIONS = [
  { ai: true, text: "Best projectors for home", desc: "Summary + sub-topics" },
  { ai: true, text: "Compare Epson 5050UB vs BenQ HT2050A", desc: "Side-by-side comparison" },
  { ai: false, text: "best projector for bright living room 2026" },
  { ai: false, text: "ultra short throw projector reviews" },
  { ai: false, text: "epson home cinema 5050ub setup" },
  { ai: false, text: "diy home theater under $1500" },
];

const PICKS = [
  { tag: "Top overall", tagClass: "gold", name: "Epson Home Cinema 5050UB", sub: "Pixel-shift 4K • 2,600 lumens • $3,000" },
  { tag: "Best budget", tagClass: "green", name: "BenQ HT2050A", sub: "1080p • 2,200 lumens • $700" },
  { tag: "Best UST", tagClass: "", name: "Hisense PX1-Pro Laser", sub: "Triple laser • 2,200 lumens • $2,400" },
];

const REFINE = [
  { label: "Under $1,000" },
  { label: "Ultra short-throw" },
  { label: "Bright living rooms" },
  { label: "Best for PS5 & gaming" },
  { label: "True 4K (no shift)" },
];

const TOPICS = [
  {
    num: "01",
    title: "Lumens, resolution & throw distance",
    desc: "What the specs actually mean for the room you're putting it in. Reviewers explain how to translate ANSI lumens to real-world brightness and why throw distance dictates which mounting position is even possible.",
    items: [
      { title: "The complete home theater buying guide", channel: "Stage & Screen", views: "1.2M", ago: "3 weeks ago", ts: "12:48", thumbHue: 18, label: "tech-review",
        moments: [ { ts: "02:14", label: "ANSI lumens, properly explained" }, { ts: "07:20", label: "Pixel-shift vs native 4K" }, { ts: "10:05", label: "Throw distance math" } ] },
      { title: "Plan your home theater before you spend a dollar", channel: "House & Light", views: "412K", ago: "5 days ago", ts: "08:30", thumbHue: 268, label: "room-planning",
        moments: [ { ts: "01:30", label: "Measuring your room" }, { ts: "04:45", label: "Short-throw vs long-throw" } ] },
      { title: "Why your projector looks washed out in daylight", channel: "Glass Optics", views: "298K", ago: "2 weeks ago", ts: "11:12", thumbHue: 32, label: "daylight-test",
        moments: [ { ts: "03:22", label: "Bright-room lumens floor" }, { ts: "06:50", label: "ALR screens explained" } ] },
    ],
  },
  {
    num: "02",
    title: "Best picks by budget tier",
    desc: "The cross-reviewer consensus on what's actually worth buying at $500, $1k, and $3k.",
    items: [
      { title: "I tested 10 cheap projectors , these 3 actually win", channel: "Budget Cinema", views: "847K", ago: "1 month ago", ts: "14:20", thumbHue: 142, label: "budget-setup",
        moments: [ { ts: "06:30", label: "Best pick under $500" }, { ts: "09:15", label: "The $500–$1k tier" }, { ts: "12:00", label: "What to avoid" } ] },
      { title: "Epson 5050UB , 8 months later, honest review", channel: "Stage & Screen", views: "612K", ago: "2 months ago", ts: "16:42", thumbHue: 210, label: "long-term",
        moments: [ { ts: "01:12", label: "Image quality after 1,200 hrs" }, { ts: "08:40", label: "Fan noise at peak brightness" }, { ts: "14:05", label: "Vs native-4K rivals" } ] },
      { title: "BenQ HT2050A , still the $700 king in 2026", channel: "Budget Cinema", views: "1.4M", ago: "6 months ago", ts: "12:30", thumbHue: 96, label: "budget-pick",
        moments: [ { ts: "02:58", label: "Black levels tested" }, { ts: "06:30", label: "The cheap 4K trap" }, { ts: "11:20", label: "Rooms where it fails" } ] },
    ],
  },
  {
    num: "03",
    title: "Gaming & low-latency setups",
    desc: "Input lag, motion handling, and projectors that don't ruin your PS5 or Xbox.",
    two: true,
    items: [
      { title: "The 4 projectors that don't ruin gaming", channel: "Frame Latency", views: "201K", ago: "3 weeks ago", ts: "09:30", thumbHue: 320, label: "ps5-gaming",
        moments: [ { ts: "02:40", label: "Input lag tested, all 4" }, { ts: "05:15", label: "Best for 120Hz" } ] },
      { title: "I played PS5 on a $4k projector for a month", channel: "Couch Cinema", views: "356K", ago: "1 week ago", ts: "13:05", thumbHue: 4, label: "couch-test",
        moments: [ { ts: "01:20", label: "Setup walkthrough" }, { ts: "04:30", label: "Motion handling" }, { ts: "10:00", label: "Worth it?" } ] },
    ],
  },
];

// Pool for the infinite-scroll grid (standard YT-style cards)
const MORE_POOL = [
  { title: "Hisense PX1-Pro vs Samsung LSP9T , UST shootout", channel: "Stage & Screen", views: "284K", ago: "4 days ago", ts: "18:42", hue: 200, label: "ust-shootout" },
  { title: "The ONE setting that fixes washed-out projector colors", channel: "Glass Optics", views: "1.1M", ago: "2 months ago", ts: "06:14", hue: 60, label: "color-fix" },
  { title: "Building a $2,500 home theater from scratch", channel: "House & Light", views: "523K", ago: "3 weeks ago", ts: "22:08", hue: 280, label: "diy-build" },
  { title: "Why I sold my 4K TV for a 1080p projector", channel: "Couch Cinema", views: "412K", ago: "1 month ago", ts: "11:30", hue: 12, label: "tv-vs-projector" },
  { title: "Epson 3800 long-term , should you still buy it in 2026?", channel: "Budget Cinema", views: "189K", ago: "5 days ago", ts: "14:55", hue: 220, label: "epson-3800" },
  { title: "Best portable projectors for backyard movie night", channel: "Outdoor Cinema", views: "672K", ago: "2 weeks ago", ts: "09:48", hue: 130, label: "portable" },
  { title: "I built a screen for $80 that beats a $1,200 screen", channel: "House & Light", views: "892K", ago: "4 months ago", ts: "12:22", hue: 30, label: "diy-screen" },
  { title: "ALR screens tested , which one actually works?", channel: "Glass Optics", views: "256K", ago: "1 week ago", ts: "16:30", hue: 195, label: "alr-test" },
  { title: "JVC NZ7 review , is laser the real upgrade?", channel: "Stage & Screen", views: "147K", ago: "3 weeks ago", ts: "19:15", hue: 250, label: "jvc-laser" },
  { title: "The cheapest projector that doesn't suck , 2026 edition", channel: "Budget Cinema", views: "1.8M", ago: "1 month ago", ts: "13:40", hue: 350, label: "cheap-2026" },
  { title: "Calibrating your projector with a $30 colorimeter", channel: "Frame Latency", views: "94K", ago: "2 days ago", ts: "21:00", hue: 170, label: "calibration" },
  { title: "Anker Nebula Capsule 3 , tiny projector, big surprise", channel: "Outdoor Cinema", views: "415K", ago: "5 days ago", ts: "08:55", hue: 290, label: "nebula" },
  { title: "How to soundproof your projector room", channel: "House & Light", views: "203K", ago: "1 month ago", ts: "15:18", hue: 45, label: "soundproof" },
  { title: "The truth about 'pixel-shift 4K' , is it real 4K?", channel: "Stage & Screen", views: "788K", ago: "6 weeks ago", ts: "17:24", hue: 240, label: "pixel-shift" },
  { title: "Setting up Atmos with a projector , full walkthrough", channel: "Couch Cinema", views: "312K", ago: "2 months ago", ts: "24:50", hue: 0, label: "atmos-setup" },
  { title: "I returned 5 projectors , here's what to avoid", channel: "Budget Cinema", views: "956K", ago: "2 weeks ago", ts: "10:12", hue: 110, label: "avoid-list" },
];

/* ============ SUB-COMPONENTS ============ */
function Logo() {
  return (
    <div className="logo">
      <button className="logo-hamburger" title="Menu"><IconMenu /></button>
      <div className="logo-mark">
        <svg viewBox="0 0 24 24" fill="currentColor">
          <path d="M8 6 L8 18 L18 12 Z" />
        </svg>
      </div>
      <div className="logo-word">YouTube</div>
    </div>
  );
}

function SideNav() {
  const items = [
    { icon: IconHome, label: "Home", active: false },
    { icon: IconShorts, label: "Shorts", active: false },
    { icon: IconSubs, label: "Subs", active: false },
    { icon: IconYou, label: "You", active: true },
  ];
  return (
    <aside className="sidebar">
      {items.map((it, i) => (
        <div key={i} className={`side-item ${it.active ? "active" : ""}`}>
          <it.icon />
          <span>{it.label}</span>
        </div>
      ))}
    </aside>
  );
}

function SearchBar({ query, setQuery, focused, setFocused, demoCtx }) {
  const demo = useDemoHandlers('searchBar', demoCtx);
  return (
    <div className={`search-wrap ${demo.className || ''}`} style={{display:'flex',alignItems:'center'}}
         onMouseEnter={demo.onMouseEnter} onMouseLeave={demo.onMouseLeave}>
      <div className={`search ${focused ? "focused" : ""}`} style={{flex:1}}>
        <div className="search-inner">
          <div className="search-spark" title="AI-enhanced search">
            <Sparkle size={16} />
          </div>
          <input
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            onFocus={() => setFocused(true)}
            onBlur={() => setTimeout(() => setFocused(false), 150)}
            placeholder="Search"
          />
          {query && (
            <button className="search-btn" onClick={() => setQuery("")} title="Clear">
              <IconClose size={18} />
            </button>
          )}
        </div>
        <button className="search-submit" title="Search">
          <IconSearch size={20} sw={2} />
        </button>
      </div>
      <button className="search-voice" title="Search with your voice">
        <IconMic size={20} />
      </button>
      {focused && <Suggestions query={query} onPick={(v) => { setQuery(v); setFocused(false); }} />}
    </div>
  );
}

function Suggestions({ query, onPick }) {
  const list = SUGGESTIONS.filter(s =>
    !query || s.text.toLowerCase().includes(query.toLowerCase())
  );
  return (
    <div className="suggest">
      <div className="suggest-head">Suggestions</div>
      {list.map((s, i) => (
        <div key={i} className={`suggest-item ${s.ai ? "ai" : ""}`} onMouseDown={(e) => { e.preventDefault(); onPick(s.text); }}>
          {s.ai ? <Sparkle size={15} className="" /> : <IconClock size={15} />}
          <div>
            <div>{s.text}</div>
            {s.desc && <div style={{font:"400 11px/1 Inter", color:"var(--dim)", marginTop:4}}>{s.desc}</div>}
          </div>
          {s.ai && <Sparkle size={11} />}
        </div>
      ))}
    </div>
  );
}

function TopBar(props) {
  return (
    <header className="topbar">
      <Logo />
      <SearchBar {...props} />
      <div className="topright">
        <button className="icon-btn" title="Create"><IconPlus /></button>
        <button className="icon-btn" title="Notifications"><IconBell /></button>
        <div className="avatar">JK</div>
      </div>
    </header>
  );
}

function ChapterTimeline({ active, onSelect }) {
  const chapters = [
    { ts: "00:00", label: "Intro" },
    { ts: "01:12", label: "Bright vs dark rooms" },
    { ts: "02:14", label: "Lumens explained" },
    { ts: "04:45", label: "Throw distance" },
    { ts: "06:30", label: "Budget picks" },
    { ts: "08:40", label: "Fan noise compared" },
    { ts: "11:20", label: "Final verdict" },
  ];
  return (
    <div className="timeline">
      {chapters.map((c, i) => (
        <div key={i} className={`seg ${active === i ? "active" : ""}`} onClick={() => onSelect(i)}>
          <div className="tip"><span style={{color:"#FF6B6B",fontFamily:"Roboto,Inter",fontWeight:600,marginRight:8}}>{c.ts}</span>{c.label}</div>
        </div>
      ))}
    </div>
  );
}

function Player({ hue = 220, demoCtx }) {
  const [active, setActive] = useState(2);
  const demo = useDemoHandlers('synthesizedPlayer', demoCtx);
  return (
    <div className={`player ${demo.className || ''}`}
         onMouseEnter={demo.onMouseEnter} onMouseLeave={demo.onMouseLeave}>
      <div className="thumb" data-label="synthesized clip" style={{filter:`hue-rotate(${hue}deg)`}} />
      <div className="player-ov">
        <div className="player-top">
          <div className="ai-pill">
            <Sparkle size={11} />
            <span>Synthesized highlights · 3 sources</span>
          </div>
          <div className="ai-pill" style={{background:"rgba(15,15,15,.7)"}}>
            <span style={{fontFamily:"Roboto,Inter", color:"#fff", fontWeight:500}}>00:42 / 03:18</span>
          </div>
        </div>
        <button className="player-play" aria-label="Play"><IconPlay /></button>
        <div className="player-bot">
          <div className="player-title">A 3-minute synthesis of the top projector reviews</div>
          <div className="player-channel">
            <div className="ch-avatar" />
            Stitched from <strong style={{margin:"0 4px"}}>Stage & Screen</strong> · <strong style={{margin:"0 4px"}}>Budget Cinema</strong> · <strong style={{marginLeft:4}}>House & Light</strong>
          </div>
          <ChapterTimeline active={active} onSelect={setActive} />
        </div>
      </div>
    </div>
  );
}

function AIAnswer({ showSources, onFollowup, demoCtx }) {
  const [draft, setDraft] = useState("");
  const demo = useDemoHandlers('geminiAnswer', demoCtx);
  return (
    <div className={`answer ${demo.className || ''}`}
         onMouseEnter={demo.onMouseEnter} onMouseLeave={demo.onMouseLeave}>
      <div className="answer-head">
        <div className="answer-title">
          <Sparkle size={16} />
          <span className="ai-text">Gemini Answer</span>
          <span style={{fontSize:11,color:"var(--dim)",fontWeight:500,marginLeft:4}}>· synthesized from 12 reviews</span>
        </div>
        <div className="answer-actions">
          <button className="icon-btn" title="Regenerate"><IconRefresh size={14} /></button>
          <button className="icon-btn" title="Copy"><IconCopy size={14} /></button>
          <button className="icon-btn" title="Share"><IconShare size={14} /></button>
        </div>
      </div>

      <div className="answer-body">
        Based on top tech reviews, picking a home projector comes down to <em>budget</em> and <em>how much light is in the room</em>. For controlled dark rooms, reviewers consistently favor pixel-shift 4K models in the $2–3k range; for living rooms with windows, brightness (lumens) matters more than raw resolution.
      </div>

      <div className="picks">
        {PICKS.map((p, i) => (
          <div key={i} className="pick">
            <span className={`pick-tag ${p.tagClass}`}>{p.tag}</span>
            <div>
              <div className="pick-name">{p.name}</div>
              <div className="pick-sub">{p.sub}</div>
            </div>
            <span className="pick-arrow">→</span>
          </div>
        ))}
      </div>

      {showSources && (
        <div className="sources">
          <strong>Sources</strong>
          <div className="src-stack">
            {[18, 142, 268, 32, 200].map((h, i) => (
              <div key={i} className="src" style={{background:`hsl(${h} 30% 22%)`}} />
            ))}
          </div>
          <span className="src-count">12 videos · 4 channels</span>
        </div>
      )}

      <div className="followup">
        <Sparkle size={13} />
        <input
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          onKeyDown={(e) => { if (e.key === "Enter" && draft) { onFollowup(draft); setDraft(""); } }}
          placeholder="Ask a follow-up , e.g. for a 12-ft throw…"
        />
        <button onClick={() => { if (draft) { onFollowup(draft); setDraft(""); } }}>
          <IconArrow size={13} sw={2.4} />
        </button>
      </div>
    </div>
  );
}

function RefinePills({ active, setActive, demoCtx }) {
  const demo = useDemoHandlers('refinePills', demoCtx);
  return (
    <div className={`refine ${demo.className || ''}`}
         onMouseEnter={demo.onMouseEnter} onMouseLeave={demo.onMouseLeave}>
      <div className="refine-label">
        <Sparkle size={11} />
        <span>Refine by sub-topic</span>
      </div>
      {REFINE.map((r) => (
        <button
          key={r.label}
          className={`ai-chip ${active === r.label ? "active" : ""}`}
          onClick={() => setActive(active === r.label ? null : r.label)}
        >
          <span className="ai-chip-inner">{r.label}</span>
        </button>
      ))}
    </div>
  );
}

function KeyMoments({ moments, onJump, demoCtx, showDemo }) {
  const demo = showDemo ? useDemoHandlers('keyMoments', demoCtx) : {};
  return (
    <div className={`keymoments ${demo.className || ''}`}
         onMouseEnter={demo.onMouseEnter} onMouseLeave={demo.onMouseLeave}>
      <div className="keymoments-head">
        <Sparkle size={11} />
        <span>Key moments</span>
      </div>
      <div className="keymoments-list">
        {moments.map((m, i) => (
          <div key={i} className="km-row" onClick={(e) => { e.stopPropagation(); onJump && onJump(m); }}>
            <span className="ts">{m.ts}</span>
            <span className="label">{m.label}</span>
            <span className="jump">→</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function TopicCard({ v, onJump, demoCtx, showKeyMomentsDemo }) {
  return (
    <div className="tvcard">
      <div className="vthumb">
        <div className="thumb" data-label={v.label} style={{filter:`hue-rotate(${v.thumbHue}deg)`}} />
        <div className="ts-overlay">
          <IconPlay size={9} />
          <span>{v.ts}</span>
        </div>
      </div>
      <div className="vtitle">{v.title}</div>
      <div className="vsub">
        {v.channel}<span className="dot"/>{v.views} views<span className="dot"/>{v.ago}
      </div>
      <KeyMoments moments={v.moments} onJump={onJump} demoCtx={demoCtx} showDemo={showKeyMomentsDemo} />
    </div>
  );
}

function TopicBlock({ topic, onJump, demoCtx, isFirstTopic }) {
  const totalMoments = topic.items.reduce((n, v) => n + v.moments.length, 0);
  const demo = useDemoHandlers('breakdownByTopic', demoCtx);
  return (
    <section
      className={`topic ${isFirstTopic ? (demo.className || '') : ''}`}
      onMouseEnter={isFirstTopic ? demo.onMouseEnter : undefined}
      onMouseLeave={isFirstTopic ? demo.onMouseLeave : undefined}
    >
      <header className="topic-head">
        <div className="topic-num">TOPIC {topic.num}</div>
        <div className="topic-titles">
          <h3 className="topic-title">
            <Sparkle size={18} />
            <span>{topic.title}</span>
          </h3>
          <p className="topic-desc">{topic.desc}</p>
        </div>
        <div className="topic-stats">
          <div className="topic-stat"><strong>{topic.items.length}</strong>videos</div>
          <div className="topic-stat"><strong>{totalMoments}</strong>jump-points</div>
        </div>
      </header>
      <div className={`topic-grid ${topic.two ? "two" : ""}`}>
        {topic.items.map((v, i) => <TopicCard
          key={i} v={v} onJump={onJump} demoCtx={demoCtx}
          showKeyMomentsDemo={isFirstTopic && i === 0}
        />)}
      </div>
    </section>
  );
}

/* ============ MORE VIDEOS , infinite scroll ============ */
function MoreVideoCard({ v }) {
  return (
    <div className="mcard">
      <div className="vthumb">
        <div className="thumb" data-label={v.label} style={{filter:`hue-rotate(${v.hue}deg)`}} />
        <div className="ts-overlay"><span>{v.ts}</span></div>
      </div>
      <div className="vmeta">
        <div className="ch-avatar" style={{background:`hsl(${v.hue} 30% 30%)`}} />
        <div className="vinfo">
          <div className="vtitle">{v.title}</div>
          <div className="vchan">{v.channel}</div>
          <div className="vmeta-line">{v.views} views<span className="dot"/>{v.ago}</div>
        </div>
      </div>
    </div>
  );
}

function MoreVideos() {
  const [count, setCount] = useState(8);
  const [loading, setLoading] = useState(false);
  const sentinelRef = useRef(null);

  // Build the visible list , cycle the pool with slight variation so it feels endless
  const items = useMemo(() => {
    const out = [];
    for (let i = 0; i < count; i++) {
      const base = MORE_POOL[i % MORE_POOL.length];
      // Slight hue shift per cycle for visual variety on repeats
      const cycle = Math.floor(i / MORE_POOL.length);
      out.push({ ...base, hue: (base.hue + cycle * 47) % 360 });
    }
    return out;
  }, [count]);

  useEffect(() => {
    const el = sentinelRef.current;
    if (!el) return;
    const obs = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting && !loading) {
        setLoading(true);
        // Simulated network delay
        setTimeout(() => {
          setCount((c) => c + 8);
          setLoading(false);
        }, 600);
      }
    }, { rootMargin: "400px" });
    obs.observe(el);
    return () => obs.disconnect();
  }, [loading]);

  return (
    <>
      <div className="more-grid">
        {items.map((v, i) => <MoreVideoCard key={i} v={v} />)}
      </div>
      <div ref={sentinelRef} />
      <div className="more-loading">
        <div className="loading-dot" />
        <span>Loading more results…</span>
      </div>
    </>
  );
}

/* ============ FEATURE EXPLAINERS (demo mode) ============ */
const FEATURES = {
  searchBar: {
    title: "AI-enhanced search bar",
    tagline: "The sparkle icon signals that queries are interpreted by Gemini, not just keyword-matched.",
    points: [
      "Two query types get the AI overview treatment: informational 'how to do X' queries, and product discovery queries.",
      "Informational queries like 'how to cook a grilled cheese' get a synthesized walkthrough at the top of results.",
      "Product discovery queries like 'best projectors for home' get ranked picks with budget tiers and source citations.",
      "The visual cue is intentionally minimal, one icon, so it doesn't crowd the existing YouTube search affordance.",
    ],
  },
  synthesizedPlayer: {
    title: "Synthesized highlights player",
    tagline: "Instead of opening one video, the user watches a 3-minute cut stitched from the top results.",
    points: [
      "Highlights are auto-selected from 3 source channels, so the user gets the consensus review without picking a winner upfront.",
      "Chapter timeline at the bottom is generated, not creator-supplied, so it works on any video corpus.",
      "The 'Stitched from' credit line stays visible so original creators get attribution on every synthesized view.",
    ],
  },
  geminiAnswer: {
    title: "Synthesized answer at the top",
    tagline: "The user sees the answer before they pick a video, grounded in real creator content.",
    points: [
      "A 2-sentence summary distilled from 12 top reviews, so the user can decide without scrubbing any single one.",
      "Three ranked picks (Top overall / Best budget / Best UST) appear as scannable cards instead of being buried at minute 7.",
      "A visible source stack with channel avatars proves the answer is grounded in actual creators, not hallucinated.",
      "Follow-up input lets the user refine ('for a 12-ft throw?') without retyping the whole query.",
    ],
  },
  refinePills: {
    title: "AI-suggested sub-topics",
    tagline: "Gemini infers the dimensions a user is likely to want to filter by, before the user thinks of them.",
    points: [
      "For projectors: 'under $1,000', 'ultra short-throw', 'best for PS5 & gaming', these are derived from clustering the top videos.",
      "Replaces the current YouTube filter row that's mostly generic (Today, 4K, Subtitles) and rarely matches search intent.",
      "Tapping a pill rewrites the result page in-place rather than starting a new search.",
    ],
  },
  breakdownByTopic: {
    title: "Breakdown by topic",
    tagline: "Gemini groups the top 10 to 15 results into 3 named sub-topics, each as its own shelf.",
    points: [
      "Today, all 15 videos are dumped into one infinite list; the user has to read 15 titles to find the angle they want.",
      "With topical shelves, the user can jump straight to 'Best picks by budget' if that's what they're stuck on.",
      "Each shelf has a 1-sentence AI description so the user knows why the grouping exists.",
    ],
  },
  keyMoments: {
    title: "Key moments per video",
    tagline: "Every result lists its 2 to 3 most relevant timestamps as click-to-jump chips, not just a length.",
    points: [
      "Saves the user from scrubbing: 'Best picks under $500' at 06:30 is one click, not a 14-minute watch.",
      "Timestamps are extracted by the model from transcript + visual signals, even for creators who don't add chapters.",
      "The mini-shelf inside each card stays compact so the existing video card layout still works.",
    ],
  },
};

function ExplainerTooltip({ feature, rect, onEnter, onLeave }) {
  if (!feature || !rect) return null;
  const W = 380;
  const top = rect.bottom + 28;
  let left = rect.left + rect.width / 2 - W / 2;
  left = Math.max(16, Math.min(left, window.innerWidth - W - 16));
  const arrowLeft = rect.left + rect.width / 2 - left;
  return (
    <div className="explainer" style={{ top, left, width: W }} onMouseEnter={onEnter} onMouseLeave={onLeave}>
      <div className="explainer-arrow" style={{ left: arrowLeft }}>
        <svg viewBox="0 0 24 28" fill="none">
          <path d="M12 26 Q 14 16, 10 8 Q 9 4, 14 2" stroke="#D97706" strokeWidth="1.8" strokeLinecap="round" fill="none" strokeDasharray="0" />
          <path d="M9 25 L12 27 L13 23" stroke="#D97706" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" fill="none" />
        </svg>
      </div>
      <div className="explainer-author">Tejas's notes</div>
      <div className="explainer-divider" />
      <div className="explainer-title">{feature.title}</div>
      <p className="explainer-tagline">{feature.tagline}</p>
      <ul className="explainer-points">
        {feature.points.map((p, i) => <li key={i}>{p}</li>)}
      </ul>
    </div>
  );
}

// Helper — returns onMouseEnter/onMouseLeave handlers + className for any demo-targeted element
function useDemoHandlers(featureKey, demoCtx) {
  if (!demoCtx || !demoCtx.demoMode) return { className: '' };
  return {
    className: `demo-target ${demoCtx.activeKey === featureKey ? 'active' : ''}`,
    onMouseEnter: (e) => demoCtx.onEnter(featureKey, e.currentTarget),
    onMouseLeave: demoCtx.onLeave,
  };
}

const IconPencil = (p) => <Icon {...p}><path d="M12 20h9M16.5 3.5a2.1 2.1 0 1 1 3 3L7 19l-4 1 1-4z"/></Icon>;

function DemoToggle({ on, onClick }) {
  return (
    <button className={`demo-toggle ${on ? "on" : ""}`} onClick={onClick} title="Toggle designer notes">
      <span className="dot" />
      <IconPencil size={14} className="pencil" />
      <span>{on ? "Notes: ON" : "Notes: OFF"}</span>
    </button>
  );
}

/* ============ MAIN APP ============ */
function App() {
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "heroLayout": "split",
    "showSources": true,
    "aiPalette": 0,
    "searchState": "idle"
  }/*EDITMODE-END*/;

  const [t, setTweak] = window.useTweaks(TWEAK_DEFAULTS);
  const [query, setQuery] = useState("Best projectors for home");
  const [focused, setFocused] = useState(t.searchState === "focused");
  const [activeRefine, setActiveRefine] = useState(null);
  const [toast, setToast] = useState(null);

  // Demo-mode (Tejas's notes)
  const [demoMode, setDemoMode] = useState(true);
  const [activeFeature, setActiveFeature] = useState(null);
  const [activeRect, setActiveRect] = useState(null);
  const closeTimerRef = useRef(null);

  useEffect(() => {
    document.body.classList.toggle('demo-mode', demoMode);
    if (!demoMode) { setActiveFeature(null); setActiveRect(null); }
  }, [demoMode]);

  const showFeature = (key, target) => {
    if (!demoMode) return;
    clearTimeout(closeTimerRef.current);
    setActiveFeature({ key, ...FEATURES[key] });
    setActiveRect(target.getBoundingClientRect());
  };
  const scheduleHide = () => {
    closeTimerRef.current = setTimeout(() => { setActiveFeature(null); setActiveRect(null); }, 200);
  };
  const cancelHide = () => clearTimeout(closeTimerRef.current);

  useEffect(() => {
    const palettes = [
      { name:"Gemini default", c:["#4285F4","#9B72CB","#D96570"] },
      { name:"Aurora",         c:["#3DD9C7","#7B61FF","#FF8FB1"] },
      { name:"Sunset",         c:["#FFC65C","#FF6B9E","#9F6BFF"] },
      { name:"Deep dusk",      c:["#4A6BFF","#7E4BD9","#C04B92"] },
    ];
    const p = palettes[t.aiPalette] || palettes[0];
    const root = document.documentElement;
    root.style.setProperty("--ai-1", p.c[0]);
    root.style.setProperty("--ai-2", p.c[1]);
    root.style.setProperty("--ai-3", p.c[2]);
    root.style.setProperty("--ai-grad", `linear-gradient(110deg,${p.c[0]} 0%,${p.c[1]} 50%,${p.c[2]} 100%)`);
    const stops = document.querySelectorAll('#aiGrad stop');
    if (stops.length === 3) {
      stops[0].setAttribute('stop-color', p.c[0]);
      stops[1].setAttribute('stop-color', p.c[1]);
      stops[2].setAttribute('stop-color', p.c[2]);
    }
  }, [t.aiPalette]);

  useEffect(() => {
    if (t.searchState === "focused") setFocused(true);
    else if (t.searchState === "idle") setFocused(false);
  }, [t.searchState]);

  const showToast = (msg) => {
    setToast(msg);
    setTimeout(() => setToast(null), 2200);
  };

  const handleFollowup = (q) => showToast(`Following up: "${q}"`);
  const handleRefine = (label) => {
    setActiveRefine(label);
    if (label) showToast(`Refining by "${label}"`);
  };
  const handleJump = (m) => showToast(`Jumping to ${m.ts} , ${m.label}`);

  const demoCtx = demoMode ? {
    demoMode: true,
    activeKey: activeFeature && activeFeature.key,
    onEnter: showFeature,
    onLeave: scheduleHide,
  } : null;

  return (
    <div className="app">
      <TopBar query={query} setQuery={setQuery} focused={focused} setFocused={setFocused} demoCtx={demoCtx} />

      <div className="body">
        <SideNav />

        <main className="main">
          {/* Query header */}
          <div className="query-line">
            <div className="col" style={{gap:6}}>
              <div className="query-eyebrow">Search results</div>
              <div className="query-title">{query || "Untitled search"}</div>
            </div>
          </div>

          {/* HERO */}
          <section className={`hero ${t.heroLayout === "stacked" ? "stacked" : ""}`}>
            <Player hue={220} demoCtx={demoCtx} />
            <AIAnswer showSources={t.showSources} onFollowup={handleFollowup} demoCtx={demoCtx} />
          </section>

          {/* Refinement pills */}
          <RefinePills active={activeRefine} setActive={handleRefine} demoCtx={demoCtx} />

          {/* Breakdown by topic */}
          <div className="section-head">
            <div>
              <div className="section-title">
                <Sparkle size={16} />
                <span>Breakdown by topic</span>
              </div>
              <div className="section-sub" style={{marginTop:6}}>
                We grouped the top results into the sub-topics you actually need to compare. Each video lists its exact jump-points , click any timestamp to scrub straight to that moment.
              </div>
            </div>
            <a className="section-link" href="#">See all topics →</a>
          </div>

          {TOPICS.map((tp, i) => (
            <TopicBlock key={tp.num} topic={tp} onJump={handleJump} demoCtx={demoCtx} isFirstTopic={i === 0} />
          ))}

          {/* More results , infinite scroll */}
          <div className="section-head" style={{marginTop: 32}}>
            <div>
              <div className="section-title" style={{color: 'var(--text)'}}>
                <span>More results</span>
              </div>
              <div className="section-sub" style={{marginTop:6}}>
                Every video matching your search , keep scrolling.
              </div>
            </div>
            <a className="section-link" href="#">Sort: Relevance ↓</a>
          </div>
          <MoreVideos />
        </main>
      </div>

      {toast && (
        <div className="toast">
          <Sparkle size={12} />
          {toast}
        </div>
      )}

      {/* Demo-mode UI */}
      <DemoToggle on={demoMode} onClick={() => setDemoMode(!demoMode)} />
      {demoMode && activeFeature && activeRect && (
        <ExplainerTooltip feature={activeFeature} rect={activeRect}
                          onEnter={cancelHide} onLeave={scheduleHide} />
      )}

      {/* TWEAKS PANEL */}
      <window.TweaksPanel title="Tweaks">
        <window.TweakSection label="AI accent">
          <window.TweakSelect
            label="Palette"
            value={t.aiPalette}
            onChange={(v) => setTweak("aiPalette", parseInt(v))}
            options={[
              { value: 0, label: "Default (blue → purple → pink)" },
              { value: 1, label: "Aurora (teal → violet → rose)" },
              { value: 2, label: "Sunset (amber → rose → violet)" },
              { value: 3, label: "Deep dusk (indigo → plum → magenta)" },
            ]}
          />
        </window.TweakSection>

        <window.TweakSection label="Layout">
          <window.TweakRadio
            label="Hero layout"
            value={t.heroLayout}
            onChange={(v) => setTweak("heroLayout", v)}
            options={[
              { value: "split", label: "Split" },
              { value: "stacked", label: "Stacked" },
            ]}
          />
        </window.TweakSection>

        <window.TweakSection label="Gemini Answer">
          <window.TweakToggle
            label="Show source stack"
            value={t.showSources}
            onChange={(v) => setTweak("showSources", v)}
          />
        </window.TweakSection>

        <window.TweakSection label="Search state">
          <window.TweakRadio
            label="State"
            value={t.searchState}
            onChange={(v) => setTweak("searchState", v)}
            options={[
              { value: "idle", label: "Idle" },
              { value: "focused", label: "Suggesting" },
            ]}
          />
        </window.TweakSection>
      </window.TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
