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: "How to cook a grilled cheese sandwich", desc: "Summary + sub-topics" },
  { ai: true, text: "Best cheese for grilled cheese", desc: "AI comparison" },
  { ai: false, text: "grilled cheese sandwich recipe easy" },
  { ai: false, text: "diner-style grilled cheese with mayo" },
  { ai: false, text: "three-cheese grilled cheese tiktok" },
  { ai: false, text: "grilled cheese in air fryer" },
];

const PICKS = [
  { tag: "Classic diner", tagClass: "gold", name: "Butter, white bread, American", sub: "Low heat, 4 min/side, lid on the last minute" },
  { tag: "Quick weeknight", tagClass: "green", name: "Mayo, sourdough, sharp cheddar", sub: "Medium heat, 3 min/side, ready in 6 minutes" },
  { tag: "Gourmet", tagClass: "", name: "Brioche, gruyère + fontina, garlic butter", sub: "Two cheeses, brush both sides, 5 min/side" },
];

const REFINE = [
  { label: "Under 10 minutes" },
  { label: "Mayo vs butter" },
  { label: "Vegan & dairy-free" },
  { label: "Air fryer method" },
  { label: "Restaurant-style" },
];

const TOPICS = [
  {
    num: "01",
    title: "Bread, cheese, and the right fat",
    desc: "The three ingredient choices that decide everything before the pan even heats up. Pros agree on what makes the crust golden and the inside meltable, and where home cooks usually go wrong.",
    items: [
      { title: "The perfect grilled cheese, explained by a chef", channel: "Saveur Kitchen", views: "2.4M", ago: "1 year ago", ts: "08:12", thumbHue: 38, label: "chef-take",
        moments: [ { ts: "01:24", label: "Why sourdough beats white bread" }, { ts: "03:40", label: "Two-cheese blend rule" }, { ts: "06:05", label: "Mayo vs butter showdown" } ] },
      { title: "I tried every cheese, here's the winner", channel: "Test Kitchen Daily", views: "1.1M", ago: "5 months ago", ts: "11:30", thumbHue: 50, label: "cheese-test",
        moments: [ { ts: "02:15", label: "Melt-vs-flavor tradeoff" }, { ts: "07:50", label: "The overlooked Havarti tip" } ] },
      { title: "3 breads, 3 outcomes, one clear winner", channel: "Bread & Bake", views: "482K", ago: "2 weeks ago", ts: "06:48", thumbHue: 24, label: "bread-test",
        moments: [ { ts: "01:10", label: "Crumb structure matters" }, { ts: "04:30", label: "Why brioche burns first" } ] },
    ],
  },
  {
    num: "02",
    title: "Pan technique and heat control",
    desc: "The skillet move that separates a soggy sandwich from a shatter-crisp one. Three chefs walk through their exact heat curves.",
    items: [
      { title: "Low and slow, the chef method for grilled cheese", channel: "Saveur Kitchen", views: "698K", ago: "3 weeks ago", ts: "09:45", thumbHue: 12, label: "low-slow",
        moments: [ { ts: "02:00", label: "Heat setting walk-through" }, { ts: "05:30", label: "When to add the lid" }, { ts: "08:00", label: "The press-and-wait trick" } ] },
      { title: "Cast iron vs nonstick, the verdict", channel: "Test Kitchen Daily", views: "312K", ago: "6 weeks ago", ts: "07:20", thumbHue: 200, label: "pan-test",
        moments: [ { ts: "01:40", label: "Crust color comparison" }, { ts: "04:55", label: "Even-melt winner" } ] },
      { title: "Why your grilled cheese keeps burning", channel: "Home Cook Lab", views: "845K", ago: "4 months ago", ts: "05:15", thumbHue: 0, label: "avoid-burn",
        moments: [ { ts: "01:05", label: "The high-heat mistake" }, { ts: "03:20", label: "Steaming with a lid" }, { ts: "04:30", label: "Salvage tips" } ] },
    ],
  },
  {
    num: "03",
    title: "Upgrades and add-ins",
    desc: "Tomato, jam, caramelized onion, and the gourmet moves that turn a 5-minute lunch into a dinner-grade sandwich.",
    two: true,
    items: [
      { title: "5 upgrades that make grilled cheese a meal", channel: "Saveur Kitchen", views: "1.6M", ago: "2 months ago", ts: "10:08", thumbHue: 320, label: "upgrades",
        moments: [ { ts: "01:30", label: "Caramelized onion done fast" }, { ts: "04:15", label: "Fig jam pairing" }, { ts: "07:40", label: "Apple, brie, honey combo" } ] },
      { title: "Tomato soup pairing, the ratio that matters", channel: "Bread & Bake", views: "253K", ago: "1 week ago", ts: "08:32", thumbHue: 4, label: "soup-pair",
        moments: [ { ts: "01:20", label: "Cream vs broth base" }, { ts: "05:00", label: "Dunkability test" } ] },
    ],
  },
];

// Pool for the infinite-scroll grid (standard YT-style cards)
const MORE_POOL = [
  { title: "Diner-style grilled cheese, the mayo trick", channel: "Home Cook Lab", views: "884K", ago: "4 days ago", ts: "05:42", hue: 40, label: "mayo-trick" },
  { title: "3-cheese grilled cheese that broke TikTok", channel: "Snack Vault", views: "3.2M", ago: "2 months ago", ts: "04:14", hue: 24, label: "viral" },
  { title: "Air fryer grilled cheese, does it work?", channel: "Gadget Cook", views: "512K", ago: "3 weeks ago", ts: "06:08", hue: 200, label: "air-fryer" },
  { title: "Why I never use butter anymore", channel: "Test Kitchen Daily", views: "1.4M", ago: "1 month ago", ts: "07:30", hue: 60, label: "no-butter" },
  { title: "Camp stove grilled cheese, 3 ingredients", channel: "Trail Plate", views: "189K", ago: "5 days ago", ts: "04:55", hue: 120, label: "camping" },
  { title: "Kid-friendly grilled cheese, three ways", channel: "Family Plate", views: "672K", ago: "2 weeks ago", ts: "08:48", hue: 320, label: "kids" },
  { title: "Sheet pan grilled cheese for a crowd", channel: "Saveur Kitchen", views: "892K", ago: "4 months ago", ts: "10:22", hue: 30, label: "sheet-pan" },
  { title: "Vegan grilled cheese that actually melts", channel: "Plant Pantry", views: "256K", ago: "1 week ago", ts: "07:30", hue: 140, label: "vegan" },
  { title: "Inside-out grilled cheese, crust on the inside", channel: "Snack Vault", views: "1.1M", ago: "3 weeks ago", ts: "05:15", hue: 12, label: "inside-out" },
  { title: "The cheapest grilled cheese that still slaps", channel: "Home Cook Lab", views: "1.8M", ago: "1 month ago", ts: "06:40", hue: 50, label: "cheap" },
  { title: "French onion grilled cheese, a classic upgrade", channel: "Bread & Bake", views: "394K", ago: "2 days ago", ts: "09:00", hue: 28, label: "french-onion" },
  { title: "Korean cheese toast, the next-level version", channel: "Street Plate", views: "815K", ago: "5 days ago", ts: "08:55", hue: 16, label: "korean" },
  { title: "How to keep grilled cheese crispy on the side", channel: "Test Kitchen Daily", views: "203K", ago: "1 month ago", ts: "04:18", hue: 45, label: "crispy-side" },
  { title: "Restaurant secret, the brown butter wash", channel: "Saveur Kitchen", views: "788K", ago: "6 weeks ago", ts: "07:24", hue: 36, label: "brown-butter" },
  { title: "Tomato soup ratio guide, perfect pairing", channel: "Bread & Bake", views: "412K", ago: "2 months ago", ts: "06:50", hue: 0, label: "soup-ratio" },
  { title: "I made 5 viral grilled cheese recipes, ranked", channel: "Snack Vault", views: "2.6M", ago: "2 weeks ago", ts: "11:12", hue: 18, label: "ranked" },
];

/* ============ 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: "00:45", label: "Pick your bread" },
    { ts: "01:30", label: "Pick your cheese" },
    { ts: "02:20", label: "Butter vs mayo" },
    { ts: "03:10", label: "Pan and heat" },
    { ts: "04:30", label: "The flip" },
    { ts: "05:40", label: "Final plate" },
  ];
  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 · 4 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 walkthrough of grilled cheese from 4 top chefs</div>
          <div className="player-channel">
            <div className="ch-avatar" />
            Stitched from <strong style={{margin:"0 4px"}}>Saveur Kitchen</strong> · <strong style={{margin:"0 4px"}}>Test Kitchen Daily</strong> · <strong style={{marginLeft:4}}>Bread & Bake</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 9 videos</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">
        Across top cooking channels, the consensus is to keep the heat <em>medium-low</em> and use <em>mayo or butter on the outside</em> of the bread for an even golden crust. Chefs split on cheese: most blend two for melt + flavor, and sourdough beats white bread for structure.
      </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">9 videos · 5 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 4 source channels, so the user gets the consensus 'how-to' 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 9 top videos, so the user can decide without scrubbing any single one.",
      "Three ranked picks (Classic / Quick / Gourmet) 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 ('vegan version?') 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 grilled cheese: 'mayo vs butter', 'air fryer method', 'vegan', 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 'Pan technique' 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: 'Mayo vs butter showdown' at 06:05 is one click, not a 12-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("How to cook a grilled cheese sandwich");
  const [focused, setFocused] = useState(t.searchState === "focused");
  const [activeRefine, setActiveRefine] = useState(null);
  const [toast, setToast] = useState(null);

  // Demo-mode (feature explainers)
  const [demoMode, setDemoMode] = useState(true);
  const [activeFeature, setActiveFeature] = useState(null);
  const [activeRect, setActiveRect] = useState(null);
  const closeTimerRef = useRef(null);

  // Toggle <body class="demo-mode"> for CSS scoping
  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 know. 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 />);
