<script lang="ts">
  import { onMount } from 'svelte';

  // ── Types ──
  interface Feed { name: string; count: number; id?: number; }
  interface FeedCategory { name: string; feeds: Feed[]; expanded: boolean; id?: number; }
  interface Article {
    id: number; title: string; feed: string; timeAgo: string; readingTime: string;
    content: string; thumbnail?: string; starred: boolean; read: boolean;
    author?: string; url?: string;
  }

  // ── State ──
  let navItems = $state([
    { label: 'Today', count: 0, icon: 'calendar' },
    { label: 'Starred', count: 0, icon: 'star' },
    { label: 'History', count: 0, icon: 'clock' }
  ]);
  let feedCategories = $state<FeedCategory[]>([]);
  let articles = $state<Article[]>([]);
  let activeNav = $state('Today');
  let activeFeedId = $state<number | null>(null);
  let selectedArticle = $state<Article | null>(null);
  let filterMode = $state<'unread' | 'all'>('unread');
  let sidebarOpen = $state(false);
  let autoScrollActive = $state(false);
  let autoScrollSpeed = $state(1.5);
  let articleListEl: HTMLDivElement;
  let scrollRAF: number | null = null;
  let scrollInterval: ReturnType<typeof setInterval> | null = null;
  let lastScrollTs = 0;
  let loading = $state(true);
  let loadingMore = $state(false);
  let hasMore = $state(true);
  let totalUnread = $state(0);
  const LIMIT = 50;
  let feedCounters: Record<string, number> = {};

  // ── Helpers ──
  function timeAgo(dateStr: string): string {
    const mins = Math.floor((Date.now() - new Date(dateStr).getTime()) / 60000);
    if (mins < 1) return 'now';
    if (mins < 60) return `${mins}m ago`;
    const hrs = Math.floor(mins / 60);
    if (hrs < 24) return `${hrs}h ago`;
    const days = Math.floor(hrs / 24);
    return `${days}d ago`;
  }

  function extractThumb(html: string): string | null {
    const match = html?.match(/<img[^>]+src=["']([^"']+)["']/i);
    if (!match) return null;
    return match[1].replace(/&amp;/g, '&');
  }

  function stripHtml(html: string): string {
    if (!html) return '';
    return html.replace(/<[^>]*>/g, '').replace(/&nbsp;/g, ' ').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/\s+/g, ' ').trim();
  }

  function mapEntry(e: any): Article {
    return {
      id: e.id, title: e.title, feed: e.feed?.title || '', url: e.url,
      timeAgo: timeAgo(e.published_at), readingTime: `${e.reading_time || 1} min`,
      content: e.content || '', thumbnail: e.thumbnail || extractThumb(e.content),
      starred: e.starred || false, read: e.status === 'read',
      author: e.author || ''
    };
  }

  // ── API ──
  async function api(path: string, opts: RequestInit = {}) {
    const res = await fetch(`/api/reader${path}`, { credentials: 'include', ...opts });
    if (!res.ok) throw new Error(`${res.status}`);
    return res.json();
  }

  async function loadSidebar() {
    try {
      const [cats, feeds, counters] = await Promise.all([
        api('/categories'), api('/feeds'), api('/feeds/counters')
      ]);
      feedCounters = counters.unreads || {};
      totalUnread = Object.values(feedCounters).reduce((s: number, c: any) => s + (c as number), 0);

      feedCategories = cats.map((c: any) => ({
        id: c.id, name: c.title,
        expanded: false,
        feeds: feeds
          .filter((f: any) => f.category.id === c.id)
          .map((f: any) => ({ id: f.id, name: f.title, count: feedCounters[String(f.id)] || 0 }))
      })).filter((c: FeedCategory) => c.feeds.length > 0);

      // Expand first category
      if (feedCategories.length > 0) feedCategories[0].expanded = true;

      // Update nav counts
      navItems[0].count = totalUnread;
      try {
        const [starred, history] = await Promise.all([
          api('/entries?starred=true&limit=1'),
          api('/entries?status=read&limit=1')
        ]);
        navItems[1].count = starred.total || 0;
        navItems[2].count = history.total || 0;
      } catch { /* silent */ }
    } catch { /* silent */ }
  }

  async function loadEntries(append = false) {
    if (loadingMore) return;
    if (!append) { loading = true; articles = []; hasMore = true; }
    else loadingMore = true;

    try {
      let params = `limit=${LIMIT}&direction=desc&order=published_at`;
      if (!append) params += '&offset=0';
      else params += `&offset=${articles.length}`;

      if (activeFeedId) {
        params += `&feed_id=${activeFeedId}`;
      }

      if (activeNav === 'Today') {
        params += '&status=unread';
      } else if (activeNav === 'Starred') {
        params += '&starred=true';
      } else if (activeNav === 'History') {
        params += '&status=read';
      }

      const data = await api(`/entries?${params}`);
      const mapped = (data.entries || []).map(mapEntry);

      if (append) {
        articles = [...articles, ...mapped];
      } else {
        articles = mapped;
      }
      hasMore = mapped.length === LIMIT;
    } catch { /* silent */ }
    finally { loading = false; loadingMore = false; }
  }

  async function markEntryRead(id: number) {
    try {
      await api('/entries', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ entry_ids: [id], status: 'read' })
      });
    } catch { /* silent */ }
  }

  async function markEntryUnread(id: number) {
    try {
      await api('/entries', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ entry_ids: [id], status: 'unread' })
      });
    } catch { /* silent */ }
  }

  async function toggleStarAPI(id: number) {
    try {
      await api(`/entries/${id}/bookmark`, { method: 'PUT' });
    } catch { /* silent */ }
  }

  async function markAllReadAPI() {
    try {
      const body: any = {};
      if (activeFeedId) body.feed_id = activeFeedId;
      await api('/entries/mark-all-read', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body)
      });
    } catch { /* silent */ }
  }

  // ── Feed management ──
  let showFeedManager = $state(false);
  let addFeedUrl = $state('');
  let addFeedCategoryId = $state<number | null>(null);
  let addingFeed = $state(false);

  async function addFeed() {
    if (!addFeedUrl.trim()) return;
    addingFeed = true;
    try {
      const body: any = { feed_url: addFeedUrl.trim() };
      if (addFeedCategoryId) body.category_id = addFeedCategoryId;
      await api('/feeds', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      });
      addFeedUrl = '';
      await loadSidebar();
    } catch (e) {
      alert('Failed to add feed. Check the URL.');
    }
    addingFeed = false;
  }

  async function deleteFeed(feedId: number, feedName: string) {
    if (!confirm(`Delete "${feedName}" and all its articles?`)) return;
    try {
      await api(`/feeds/${feedId}`, { method: 'DELETE' });
      if (activeFeedId === feedId) { activeFeedId = null; activeNav = 'Today'; }
      await loadSidebar();
      await loadEntries();
    } catch { /* silent */ }
  }

  let refreshing = $state(false);

  async function refreshFeed(feedId: number) {
    refreshing = true;
    try {
      await api(`/feeds/${feedId}/refresh`, { method: 'POST' });
      await loadSidebar();
      await loadEntries();
    } catch { /* silent */ }
    refreshing = false;
  }

  async function refreshAllFeeds() {
    refreshing = true;
    try {
      await api('/feeds/refresh-all', { method: 'POST' });
      await loadSidebar();
      await loadEntries();
    } catch { /* silent */ }
    refreshing = false;
  }

  // ── Save to Brain ──
  let brainSavedIds = $state<Record<number, string>>({});
  let savingToBrain = $state<Set<number>>(new Set());

  async function toggleBrainSave(article: Article, e?: Event) {
    e?.stopPropagation();
    e?.preventDefault();
    if (savingToBrain.has(article.id)) return;
    const articleUrl = article.url || '';
    if (!articleUrl && !brainSavedIds[article.id]) return;

    savingToBrain = new Set([...savingToBrain, article.id]);
    try {
      if (brainSavedIds[article.id]) {
        // Un-save: delete from Brain
        await fetch(`/api/brain/items/${brainSavedIds[article.id]}`, {
          method: 'DELETE', credentials: 'include',
        });
        const next = { ...brainSavedIds };
        delete next[article.id];
        brainSavedIds = next;
      } else {
        // Save to Brain
        const res = await fetch('/api/brain/items', {
          method: 'POST', credentials: 'include',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ type: 'link', url: articleUrl, title: article.title || undefined })
        });
        if (res.ok) {
          const data = await res.json();
          brainSavedIds = { ...brainSavedIds, [article.id]: data.id };
        }
      }
    } catch (err) {
      console.error('Brain save error:', err);
    } finally {
      const next = new Set(savingToBrain);
      next.delete(article.id);
      savingToBrain = next;
    }
  }

  // ── Touch to stop auto-scroll ──
  function handleScrollInterrupt() {
    if (autoScrollActive) stopAutoScroll();
  }

  // ── Event handlers ──
  function decrementUnread(count = 1) {
    totalUnread = Math.max(0, totalUnread - count);
    navItems[0].count = totalUnread;
    navItems = [...navItems];
  }

  const filteredArticles = $derived(articles);

  const currentIndex = $derived(
    selectedArticle ? filteredArticles.findIndex(a => a.id === selectedArticle!.id) : -1
  );

  function selectArticle(article: Article) {
    selectedArticle = article;
    if (!article.read) {
      article.read = true;
      articles = [...articles];
      markEntryRead(article.id);
      decrementUnread();
    }
    // Fetch full entry content (slim list doesn't include it)
    loadArticleContent(article);
  }

  async function loadArticleContent(article: Article) {
    try {
      const data = await api(`/entries/${article.id}`);
      const content = data.full_content || data.content || '';
      article.content = content;
      if (data.thumbnail) article.thumbnail = data.thumbnail;
      if (selectedArticle?.id === article.id) {
        selectedArticle = { ...article };
      }
      articles = [...articles];
      // If content is short, try fetching full article via crawler
      if (content.length < 1500) {
        fetchFullContent(article);
      }
    } catch { /* silent */ }
  }

  async function fetchFullContent(article: Article) {
    try {
      const data = await api(`/entries/${article.id}/fetch-full-content`, { method: 'POST' });
      if (data.full_content || data.content) {
        const fullHtml = data.full_content || data.content;
        if (fullHtml.length > (article.content?.length || 0)) {
          article.content = fullHtml;
          if (selectedArticle?.id === article.id) {
            selectedArticle = { ...article };
          }
          articles = [...articles];
        }
      }
    } catch { /* silent — keep RSS summary */ }
  }

  function closeArticle() { selectedArticle = null; }

  function toggleStar(article: Article, e?: Event) {
    e?.stopPropagation();
    article.starred = !article.starred;
    articles = [...articles];
    toggleStarAPI(article.id);
  }

  function toggleRead(article: Article) {
    const wasRead = article.read;
    article.read = !article.read;
    articles = [...articles];
    if (article.read) { markEntryRead(article.id); decrementUnread(); }
    else { markEntryUnread(article.id); totalUnread++; navItems[0].count = totalUnread; navItems = [...navItems]; }
  }

  async function markAllRead() {
    const label = activeFeedId
      ? feedCategories.flatMap(c => c.feeds).find(f => f.id === activeFeedId)?.name || 'this feed'
      : 'all feeds';
    if (!confirm(`Mark all unread in ${label} as read?`)) return;
    await markAllReadAPI();
    articles = articles.map(a => ({ ...a, read: true }));
    totalUnread = 0;
    navItems[0].count = 0;
    navItems = [...navItems];
    // Refresh counters
    await loadSidebar();
  }

  function goNext() {
    if (currentIndex < filteredArticles.length - 1) selectArticle(filteredArticles[currentIndex + 1]);
  }
  function goPrev() {
    if (currentIndex > 0) selectArticle(filteredArticles[currentIndex - 1]);
  }

  function toggleCategory(index: number) {
    feedCategories[index].expanded = !feedCategories[index].expanded;
  }

  function selectFeed(feedId: number) {
    activeFeedId = feedId;
    activeNav = '';
    sidebarOpen = false;
    loadEntries();
  }

  function selectNav(label: string) {
    activeNav = label;
    activeFeedId = null;
    sidebarOpen = false;
    loadEntries();
  }

  // ── Auto-scroll (requestAnimationFrame for smoothness) ──
  function usesPageScroll(): boolean {
    return window.matchMedia('(max-width: 1024px)').matches;
  }

  function startAutoScroll() {
    if (scrollRAF) cancelAnimationFrame(scrollRAF);
    if (scrollInterval) clearInterval(scrollInterval);
    if (!usesPageScroll() && !articleListEl) return;
    autoScrollActive = true;
    lastScrollTs = 0;
    if (usesPageScroll()) {
      scrollInterval = setInterval(() => {
        if (!autoScrollActive) return;
        const scroller = document.scrollingElement;
        if (!scroller) return;
        const delta = Math.max(1, Math.round(4 * autoScrollSpeed));
        const nextY = scroller.scrollTop + delta;
        const maxY = Math.max(0, scroller.scrollHeight - window.innerHeight);
        scroller.scrollTop = Math.min(nextY, maxY);
        checkScrolledCards();
        if (nextY >= maxY - 1) {
          stopAutoScroll();
        }
      }, 40);
      return;
    }
    function step(timestamp: number) {
      if (!autoScrollActive) return;
      const dt = lastScrollTs ? Math.min(34, timestamp - lastScrollTs) : 16;
      lastScrollTs = timestamp;
      const pxPerSecond = 72 * autoScrollSpeed;
      const delta = pxPerSecond * (dt / 1000);

      if (!articleListEl) return;
      articleListEl.scrollTop += delta;
      const maxScroll = articleListEl.scrollHeight - articleListEl.clientHeight;
      checkScrolledCards();
      if (articleListEl.scrollTop >= maxScroll - 1) {
        stopAutoScroll();
        return;
      }

      scrollRAF = requestAnimationFrame(step);
    }
    scrollRAF = requestAnimationFrame(step);
  }
  function stopAutoScroll() {
    autoScrollActive = false;
    lastScrollTs = 0;
    if (scrollRAF) { cancelAnimationFrame(scrollRAF); scrollRAF = null; }
    if (scrollInterval) { clearInterval(scrollInterval); scrollInterval = null; }
  }
  function toggleAutoScroll() {
    if (autoScrollActive) stopAutoScroll();
    else startAutoScroll();
  }
  function adjustSpeed(delta: number) {
    autoScrollSpeed = Math.max(0.25, Math.min(5, +(autoScrollSpeed + delta).toFixed(2)));
  }

  function handleKeydown(e: KeyboardEvent) {
    if (e.key === 'j' || e.key === 'ArrowDown') { e.preventDefault(); goNext(); }
    if (e.key === 'k' || e.key === 'ArrowUp') { e.preventDefault(); goPrev(); }
    if (e.key === 'Escape' && selectedArticle) { closeArticle(); }
    if (e.key === 's' && selectedArticle) { toggleStar(selectedArticle); }
    if (e.key === 'm' && selectedArticle) { toggleRead(selectedArticle); }
  }

  // ── Mark-as-read on scroll (throttled to avoid jitter) ──
  let pendingReadIds: number[] = [];
  let flushTimer: ReturnType<typeof setTimeout> | null = null;
  let scrollCheckTimer: ReturnType<typeof setTimeout> | null = null;

  function handleListScroll() {
    if (usesPageScroll()) return;
    // Throttle: only check every 400ms
    if (scrollCheckTimer) return;
    scrollCheckTimer = setTimeout(() => {
      scrollCheckTimer = null;
      checkScrolledCards();
    }, 400);
  }

  function handleViewportScroll() {
    if (!usesPageScroll()) return;
    if (scrollCheckTimer) return;
    scrollCheckTimer = setTimeout(() => {
      scrollCheckTimer = null;
      checkScrolledCards();
    }, 240);
  }

  function checkScrolledCards() {
    if (!articleListEl) return;

    const cards = articleListEl.querySelectorAll('[data-entry-id]');
    if (usesPageScroll()) {
      // Check if the last card is near the viewport bottom
      const lastCard = cards.length ? cards[cards.length - 1] : null;
      const nearBottom = lastCard
        ? lastCard.getBoundingClientRect().top < window.innerHeight + 800
        : (window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 500);
      if (hasMore && !loadingMore && nearBottom) {
        loadEntries(true);
      }
    } else {
      const { scrollTop, scrollHeight, clientHeight } = articleListEl;
      if (hasMore && !loadingMore && scrollHeight - scrollTop - clientHeight < 300) {
        loadEntries(true);
      }
    }

    let newlyRead = 0;

    cards.forEach(card => {
      const rect = card.getBoundingClientRect();
      const passedThreshold = usesPageScroll()
        ? rect.bottom < 84
        : rect.bottom < articleListEl.getBoundingClientRect().top + 20;
      if (passedThreshold) {
        const id = Number(card.getAttribute('data-entry-id'));
        if (!id) return;
        const article = articles.find(a => a.id === id);
        if (article && !article.read) {
          article.read = true;
          pendingReadIds.push(id);
          newlyRead++;
        }
      }
    });

    if (newlyRead > 0) {
      articles = [...articles];
      decrementUnread(newlyRead);
      if (flushTimer) clearTimeout(flushTimer);
      flushTimer = setTimeout(flushPendingReads, 1000);
    }
  }

  async function flushPendingReads() {
    if (!pendingReadIds.length) return;
    const ids = [...pendingReadIds];
    pendingReadIds = [];
    try {
      await api('/entries', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ entry_ids: ids, status: 'read' })
      });
    } catch { /* silent */ }
  }

  // ── Init ──
  onMount(() => {
    loadSidebar();
    loadEntries();
    return () => {
      if (flushTimer) clearTimeout(flushTimer);
      if (scrollCheckTimer) clearTimeout(scrollCheckTimer);
      if (scrollInterval) clearInterval(scrollInterval);
    };
  });
</script>

<svelte:window onkeydown={handleKeydown} onscroll={handleViewportScroll} />

<div class="reader-layout">

  <!-- Left Sidebar -->
  <!-- svelte-ignore a11y_no_static_element_interactions -->
  <aside class="reader-sidebar" class:open={sidebarOpen}>
    <div class="sidebar-header">
      <svg class="rss-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 11a9 9 0 0 1 9 9"/><path d="M4 4a16 16 0 0 1 16 16"/><circle cx="5" cy="19" r="1"/></svg>
      <span class="sidebar-title">Reader</span>
    </div>

    <nav class="sidebar-nav">
      {#each navItems as item}
        <button
          class="nav-item"
          class:active={activeNav === item.label}
          onclick={() => selectNav(item.label)}
        >
          <div class="nav-icon">
            {#if item.icon === 'inbox'}
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/><path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/></svg>
            {:else if item.icon === 'calendar'}
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
            {:else if item.icon === 'star'}
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
            {:else}
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
            {/if}
          </div>
          <span class="nav-label">{item.label}</span>
          {#if item.count > 0}
            <span class="nav-count">{item.count}</span>
          {/if}
        </button>
      {/each}
    </nav>

    <div class="sidebar-separator"></div>

    <div class="feeds-section">
      <div class="feeds-header">
        <span>Feeds</span>
        <div class="feeds-header-actions">
          <button class="feeds-action-btn" title="Refresh all feeds" onclick={refreshAllFeeds}>
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13"><path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/><path d="M16 16h5v5"/></svg>
          </button>
          <button class="feeds-action-btn" title={showFeedManager ? 'Done' : 'Manage feeds'} onclick={() => showFeedManager = !showFeedManager}>
            {#if showFeedManager}
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="13" height="13"><path d="M20 6L9 17l-5-5"/></svg>
            {:else}
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
            {/if}
          </button>
        </div>
      </div>

      {#if showFeedManager}
        <div class="add-feed-row">
          <input class="add-feed-input" type="text" placeholder="Paste feed URL..." bind:value={addFeedUrl}
            onkeydown={(e) => { if (e.key === 'Enter') addFeed(); }} />
          <button class="add-feed-btn" onclick={addFeed} disabled={addingFeed || !addFeedUrl.trim()}>
            {addingFeed ? '...' : '+'}
          </button>
        </div>
      {/if}

      {#each feedCategories as cat, i}
        <div class="feed-category">
          <button class="category-toggle" onclick={() => toggleCategory(i)}>
            <svg class="expand-icon" class:expanded={cat.expanded} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
            <span class="category-name">{cat.name}</span>
            <span class="category-count">{cat.feeds.reduce((s, f) => s + f.count, 0)}</span>
          </button>
          {#if cat.expanded}
            <div class="feed-list">
              {#each cat.feeds as feed}
                <div class="feed-item-row">
                  <button class="feed-item" onclick={() => selectFeed(feed.id || 0)}>
                    <span class="feed-name">{feed.name}</span>
                    {#if feed.count > 0}
                      <span class="feed-count">{feed.count}</span>
                    {/if}
                  </button>
                  {#if showFeedManager}
                    <button class="feed-manage-btn" title="Refresh" onclick={(e) => { e.stopPropagation(); refreshFeed(feed.id || 0); }}>
                      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="11" height="11"><path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
                    </button>
                    <button class="feed-manage-btn feed-delete-btn" title="Delete" onclick={(e) => { e.stopPropagation(); deleteFeed(feed.id || 0, feed.name); }}>
                      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="11" height="11"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg>
                    </button>
                  {/if}
                </div>
              {/each}
            </div>
          {/if}
        </div>
      {/each}
    </div>
  </aside>

  <!-- Sidebar overlay for mobile -->
  {#if sidebarOpen}
    <!-- svelte-ignore a11y_no_static_element_interactions -->
    <div class="sidebar-overlay" onclick={() => sidebarOpen = false}></div>
  {/if}

  <!-- Middle Panel: Article List -->
  <div class="reader-list">
    <div class="list-header">
      <div class="list-header-top">
        <button class="mobile-menu" onclick={() => sidebarOpen = !sidebarOpen} aria-label="Toggle sidebar">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
        </button>
        <div class="list-heading">
          <div class="list-kicker">Reading desk</div>
          <div class="list-view-name">{activeFeedId ? feedCategories.flatMap(c => c.feeds).find(f => f.id === activeFeedId)?.name || 'Feed' : activeNav} <span class="list-count">{activeNav === 'Today' && !activeFeedId ? totalUnread : filteredArticles.length}</span></div>
          <div class="list-subtitle">
            {#if activeFeedId}
              Focused source view with full article detail one click away.
            {:else if activeNav === 'Today'}
              Fresh unread stories across your active feeds.
            {:else if activeNav === 'Starred'}
              Saved stories worth keeping in rotation.
            {:else}
              Previously read entries and archive context.
            {/if}
          </div>
        </div>
        <div class="list-actions">
          <button class="btn-icon" title="Refresh">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
          </button>
          <button class="btn-icon" title="Mark all read" onclick={markAllRead}>
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
          </button>
          <button class="btn-icon" class:active-icon={autoScrollActive} onclick={toggleAutoScroll} title="Auto-scroll feed">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14"/><path d="M19 12l-7 7-7-7"/></svg>
          </button>
          {#if autoScrollActive}
            <div class="speed-control">
              <button class="speed-btn" onclick={() => adjustSpeed(-0.25)}>-</button>
              <span class="speed-value">{autoScrollSpeed}x</span>
              <button class="speed-btn" onclick={() => adjustSpeed(0.25)}>+</button>
            </div>
          {/if}
        </div>
      </div>
    </div>

    <div class="mobile-scroll-fab" class:active={autoScrollActive}>
      <button class="mobile-scroll-pill" onclick={toggleAutoScroll} title="Auto-scroll feed">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14"/><path d="M19 12l-7 7-7-7"/></svg>
        <span>{autoScrollActive ? 'Stop' : 'Auto'}</span>
      </button>
      {#if autoScrollActive}
        <div class="mobile-speed-pod">
          <button class="speed-btn" onclick={() => adjustSpeed(-0.25)}>-</button>
          <span class="speed-value">{autoScrollSpeed}x</span>
          <button class="speed-btn" onclick={() => adjustSpeed(0.25)}>+</button>
        </div>
      {/if}
    </div>

    <!-- svelte-ignore a11y_no_static_element_interactions -->
    <!-- svelte-ignore a11y_no_static_element_interactions -->
    <div class="article-list" bind:this={articleListEl} ontouchstart={handleScrollInterrupt} onwheel={handleScrollInterrupt} onscroll={handleListScroll}>
      {#each filteredArticles as article, index (article.id)}
        <!-- svelte-ignore a11y_no_static_element_interactions -->
        <div
          class="article-card"
          class:featured={!selectedArticle && !article.read && article.thumbnail && index === 0}
          class:selected={selectedArticle?.id === article.id}
          class:is-read={article.read}
          data-entry-id={article.id}
          onclick={() => selectArticle(article)}
        >
          <!-- Card header: source + time + actions -->
          <div class="card-header">
            <div class="card-source">
              <svg class="card-rss" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 11a9 9 0 0 1 9 9"/><path d="M4 4a16 16 0 0 1 16 16"/><circle cx="5" cy="19" r="1"/></svg>
              <span class="card-feed-name">{article.feed}</span>
              {#if article.author}
                <span class="card-author">· {article.author}</span>
              {/if}
            </div>
            <div class="card-header-right">
              <button class="bookmark-btn" class:saved={!!brainSavedIds[article.id]} onclick={(e) => toggleBrainSave(article, e)} title={brainSavedIds[article.id] ? 'Saved to Brain' : 'Save to Brain'}>
                {#if savingToBrain.has(article.id)}
                  <svg class="spinning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
                {:else}
                  <svg viewBox="0 0 24 24" fill={brainSavedIds[article.id] ? 'currentColor' : 'none'} stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
                {/if}
              </button>
              <span class="card-time">{article.timeAgo}</span>
            </div>
          </div>

          <div class="card-main">
            <div class="card-copy">
              <div class="card-title">{article.title}</div>
              <div class="card-preview">{stripHtml(article.content).slice(0, 200)}</div>
            </div>

            {#if article.thumbnail}
              <div class="card-banner" style="background-image:url('{article.thumbnail}')"></div>
            {/if}
          </div>

          <div class="card-footer">
            <span class="card-reading-time">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
              {article.readingTime}
            </span>
            {#if !article.read}
              <span class="card-state">Unread</span>
            {/if}
          </div>
        </div>
      {/each}
      {#if filteredArticles.length === 0}
        <div class="list-empty">No articles to show</div>
      {/if}
      {#if hasMore && !loading}
        <button class="load-more-btn" onclick={() => loadEntries(true)} disabled={loadingMore}>
          {loadingMore ? 'Loading...' : 'Load more articles'}
        </button>
      {/if}
    </div>
  </div>
</div>

<!-- Reading pane — slide-in overlay -->
{#if selectedArticle}
  <!-- svelte-ignore a11y_no_static_element_interactions -->
  <div class="pane-overlay" onclick={closeArticle}>
    <!-- svelte-ignore a11y_no_static_element_interactions -->
    <div class="pane-sheet" onclick={(e) => e.stopPropagation()}>
      <div class="pane-toolbar">
        <div class="pane-nav">
          <button class="pane-nav-btn" onclick={closeArticle} title="Close (Esc)">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
          </button>
          <button class="pane-nav-btn" onclick={goPrev} disabled={currentIndex <= 0} title="Previous (k)">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
          </button>
          <span class="pane-nav-pos">{currentIndex + 1} / {filteredArticles.length}</span>
          <button class="pane-nav-btn" onclick={goNext} disabled={currentIndex >= filteredArticles.length - 1} title="Next (j)">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
          </button>
        </div>
        <div class="pane-actions">
          <button class="pane-action-btn" class:saved-brain={!!brainSavedIds[selectedArticle.id]} onclick={() => toggleBrainSave(selectedArticle!)} title={brainSavedIds[selectedArticle.id] ? 'Saved to Brain' : 'Save to Brain'}>
            {#if savingToBrain.has(selectedArticle.id)}
              <svg class="spinning" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
            {:else}
              <svg viewBox="0 0 24 24" fill={brainSavedIds[selectedArticle.id] ? 'currentColor' : 'none'} stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
            {/if}
          </button>
          <button class="pane-action-btn" onclick={() => toggleRead(selectedArticle!)} title="Toggle read (m)">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
          </button>
          {#if selectedArticle.url}
            <a href={selectedArticle.url} target="_blank" rel="noopener" class="pane-action-btn" title="Open original">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
            </a>
          {/if}
        </div>
      </div>

      <div class="pane-scroll">
        <div class="pane-content">
          {#if selectedArticle.thumbnail}
            <div class="pane-hero">
              <div class="pane-hero-image" style="background-image:url('{selectedArticle.thumbnail}')"></div>
            </div>
          {/if}
          <h1 class="pane-title">{selectedArticle.title}</h1>
          <div class="pane-meta">
            <span class="pane-source">{selectedArticle.feed}</span>
            <span class="pane-dot"></span>
            <span>{selectedArticle.timeAgo}</span>
            <span class="pane-dot"></span>
            <span>{selectedArticle.readingTime} read</span>
            {#if selectedArticle.author}
              <span class="pane-dot"></span>
              <span class="pane-author">by {selectedArticle.author}</span>
            {/if}
          </div>
          <div class="pane-body">
            {@html selectedArticle.content}
          </div>
        </div>
      </div>
    </div>
  </div>
{/if}

<style>
  .reader-layout {
    display: grid;
    grid-template-columns: 248px minmax(0, 1fr);
    height: 100%;
    min-height: 0;
    overflow: hidden;
    position: relative;
    gap: 0;
    background:
      radial-gradient(circle at top left, rgba(255,251,245,0.82), transparent 34%),
      linear-gradient(180deg, #f3eadf 0%, #e8ddce 100%);
  }

  /* ── Mobile menu ── */
  .mobile-menu {
    display: none; width: 32px; height: 32px; border-radius: 10px;
    background: rgba(255,255,255,0.66); border: 1px solid rgba(35,26,17,0.08);
    align-items: center; justify-content: center; cursor: pointer;
    flex-shrink: 0; transition: all var(--transition);
  }
  .mobile-menu:hover { background: rgba(255,255,255,0.9); }
  .mobile-menu svg { width: 16px; height: 16px; color: #5e5248; }

  /* ── Sidebar ── */
  .reader-sidebar {
    width: 248px;
    flex-shrink: 0;
    min-height: 0;
    background: rgba(245, 237, 228, 0.82);
    border-right: 1px solid rgba(35,26,17,0.1);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    padding: 16px 0 12px;
    backdrop-filter: blur(16px);
  }
  .sidebar-header { display: flex; align-items: center; gap: 8px; padding: 0 18px 12px; }
  .rss-icon { width: 16px; height: 16px; color: #7f5f3d; }
  .sidebar-title { font-size: 1rem; font-weight: 700; color: #1f1811; letter-spacing: -0.03em; }

  .sidebar-nav { display: flex; flex-direction: column; gap: 4px; padding: 0 12px; }
  .nav-item {
    display: flex; align-items: center; gap: var(--sp-2);
    padding: 10px 12px; border-radius: 14px; background: none; border: none;
    font-size: var(--text-sm); color: #65584c; cursor: pointer;
    transition: all var(--transition); text-align: left; width: 100%; font-family: var(--font);
  }
  .nav-item:hover { background: rgba(255,248,241,0.72); color: #1f1811; }
  .nav-item.active { background: rgba(255,248,241,0.92); color: #1f1811; font-weight: 600; box-shadow: inset 0 0 0 1px rgba(35,26,17,0.08); }
  .nav-icon { width: 15px; height: 15px; flex-shrink: 0; }
  .nav-icon svg { width: 15px; height: 15px; }
  .nav-label { flex: 1; }
  .nav-count { font-size: var(--text-xs); color: #8a7a68; font-family: var(--mono); }
  .nav-item.active .nav-count { color: #7f5f3d; opacity: 0.8; }

  .sidebar-separator { height: 1px; background: rgba(35,26,17,0.08); margin: 12px 18px; }

  .feeds-section { padding: 0 12px; flex: 1; }
  .feeds-header {
    font-size: 11px; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.12em; color: #8a7a68; padding: 6px 12px 8px;
    display: flex; align-items: center; justify-content: space-between;
  }
  .feeds-header-actions { display: flex; gap: 2px; }
  .feeds-action-btn {
    width: 24px; height: 24px; border-radius: 6px; border: none;
    background: none; color: #8a7a68; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: all 150ms;
  }
  .feeds-action-btn:hover { background: rgba(255,248,241,0.8); color: #1e1812; }
  .add-feed-row {
    display: flex; gap: 4px; padding: 4px 12px 8px;
  }
  .add-feed-input {
    flex: 1; padding: 6px 10px; border-radius: 8px;
    border: 1px solid rgba(35,26,17,0.12); background: rgba(255,255,255,0.5);
    font-size: 12px; font-family: var(--font); color: #1e1812; outline: none;
  }
  .add-feed-input:focus { border-color: rgba(35,26,17,0.3); }
  .add-feed-input::placeholder { color: #8a7a68; }
  .add-feed-btn {
    width: 28px; height: 28px; border-radius: 8px; border: none;
    background: #1e1812; color: white; font-size: 14px; font-weight: 600;
    cursor: pointer; display: flex; align-items: center; justify-content: center;
    transition: opacity 150ms;
  }
  .add-feed-btn:hover { opacity: 0.8; }
  .add-feed-btn:disabled { opacity: 0.3; cursor: not-allowed; }
  .feed-item-row {
    display: flex; align-items: center;
  }
  .feed-item-row .feed-item { flex: 1; min-width: 0; }
  .feed-manage-btn {
    width: 22px; height: 22px; border-radius: 5px; border: none;
    background: none; color: #8a7a68; cursor: pointer; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    transition: all 150ms;
  }
  .feed-manage-btn:hover { background: rgba(255,248,241,0.8); color: #1e1812; }
  .feed-delete-btn:hover { color: #8f3928; }
  .feed-category { margin-bottom: 1px; }
  .category-toggle {
    display: flex; align-items: center; gap: 5px; width: 100%;
    padding: 7px 12px; background: none; border: none; font-size: var(--text-sm);
    font-weight: 500; color: #352b22; cursor: pointer; border-radius: 12px;
    transition: all var(--transition); font-family: var(--font);
  }
  .category-toggle:hover { background: rgba(255,248,241,0.72); }
  .category-name { flex: 1; text-align: left; }
  .category-count { font-size: var(--text-xs); color: #8a7a68; font-family: var(--mono); }
  .expand-icon { width: 11px; height: 11px; transition: transform var(--transition); flex-shrink: 0; color: #8a7a68; }
  .expand-icon.expanded { transform: rotate(90deg); }

  .feed-list { padding-left: 18px; }
  .feed-item {
    display: flex; align-items: center; justify-content: space-between; width: 100%;
    padding: 6px 10px; background: none; border: none; font-size: var(--text-sm);
    color: #65584c; cursor: pointer; border-radius: 10px;
    transition: all var(--transition); text-align: left; font-family: var(--font);
  }
  .feed-item:hover { background: rgba(255,248,241,0.72); color: #1f1811; }
  .feed-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .feed-count { font-size: var(--text-xs); font-family: var(--mono); color: #8a7a68; flex-shrink: 0; margin-left: var(--sp-1.5); }

  .sidebar-overlay { position: fixed; inset: 0; background: var(--overlay); z-index: 39; }

  /* ── Feed list panel ── */
  .reader-list {
    flex: 1;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    background: transparent;
    overflow: hidden;
  }
  .list-header { padding: 18px 22px 8px; background: transparent; border-bottom: none; flex-shrink: 0; }
  .list-header-top { display: flex; align-items: start; justify-content: space-between; margin-bottom: 0; gap: 14px; }
  .list-heading { display: grid; gap: 4px; }
  .list-kicker {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: #8a7a68;
  }
  .list-view-name { font-size: 1.86rem; font-weight: 700; color: #1b140d; display: flex; align-items: center; gap: 10px; letter-spacing: -0.055em; line-height: 0.96; }
  .list-count { font-size: 0.82rem; font-weight: 500; color: #8a7a68; font-family: var(--mono); letter-spacing: 0; }
  .list-subtitle { color: #5f5448; font-size: 0.9rem; line-height: 1.45; max-width: 40ch; }
  .list-actions { display: flex; gap: var(--sp-1); }
  .btn-icon { width: 32px; height: 32px; border-radius: 10px; background: rgba(252,247,241,0.8); border: 1px solid rgba(35,26,17,0.08); display: flex; align-items: center; justify-content: center; cursor: pointer; color: #65584c; transition: all var(--transition); }
  .btn-icon:hover { color: #1f1811; background: rgba(255,250,244,0.96); }
  .btn-icon.active-icon { color: #7f5f3d; border-color: rgba(127,95,61,0.18); background: rgba(255,248,242,0.92); }
  .btn-icon svg { width: 14px; height: 14px; }

  .list-filters { display: flex; gap: var(--sp-1); }
  .filter-btn {
    padding: 4px 10px 6px; border-radius: 0; background: none;
    border: none; border-bottom: 2px solid transparent; font-size: var(--text-sm); font-weight: 500;
    color: var(--text-3); cursor: pointer; transition: all var(--transition); font-family: var(--font);
    position: relative;
  }
  .filter-btn:hover { color: var(--text-2); }
  .filter-btn.active { color: var(--text-1); font-weight: 600; border-bottom-color: var(--accent); }

  .speed-control { display: flex; align-items: center; gap: var(--sp-0.5); margin-left: var(--sp-1); }
  .speed-btn {
    width: 22px; height: 22px; border-radius: 8px; background: rgba(255,255,255,0.72);
    border: 1px solid rgba(35,26,17,0.08); font-size: var(--text-sm); font-weight: 600;
    color: #65584c; cursor: pointer; display: flex; align-items: center; justify-content: center;
  }
  .speed-btn:hover { color: #1f1811; }
  .speed-value { font-size: var(--text-xs); font-family: var(--mono); color: #65584c; min-width: 24px; text-align: center; }

  /* ── Article cards (feed style) ── */
  .article-list {
    flex: 1;
    overflow-y: auto;
    padding: 6px 22px 72px;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }

  .load-more-btn {
    width: 100%;
    padding: 14px;
    margin: 8px 0;
    border-radius: 14px;
    border: 1px dashed rgba(35,26,17,0.15);
    background: transparent;
    color: #8a7a68;
    font-size: 0.88rem;
    font-weight: 500;
    font-family: var(--font);
    cursor: pointer;
    transition: all 160ms;
  }
  .load-more-btn:hover { background: rgba(255,248,241,0.7); color: #1e1812; }
  .load-more-btn:disabled { opacity: 0.5; cursor: not-allowed; }

  .mobile-scroll-fab {
    display: none;
  }
  .article-card {
    position: relative;
    padding: 14px 2px 16px 14px;
    border-radius: 0;
    background: transparent;
    border: none;
    border-bottom: 1px solid rgba(35,26,17,0.1);
    box-shadow: none;
    cursor: pointer; transition: all var(--transition);
  }
  .article-card::before {
    content: "";
    position: absolute;
    left: 0;
    top: 14px;
    bottom: 14px;
    width: 2px;
    border-radius: 999px;
    background: transparent;
    transition: background 160ms ease;
  }
  .article-card:hover { transform: translateX(2px); background: linear-gradient(90deg, rgba(249, 241, 232, 0.58), rgba(249, 241, 232, 0)); }
  .article-card.selected { background: linear-gradient(90deg, rgba(244,234,222,0.92), rgba(244,234,222,0.18) 58%, rgba(244,234,222,0)); border-bottom-color: rgba(127,95,61,0.16); }
  .article-card.selected::before,
  .article-card:not(.is-read)::before { background: rgba(127, 95, 61, 0.5); }
  .article-card.featured {
    padding: 20px 20px 20px 22px;
    margin: 2px 0 10px;
    border-radius: 26px;
    background: linear-gradient(145deg, rgba(252,246,239,0.96), rgba(241,231,219,0.9));
    border: 1px solid rgba(35,26,17,0.08);
    box-shadow: 0 12px 28px rgba(45,32,21,0.045);
  }
  .article-card.featured .card-main {
    grid-template-columns: minmax(0, 1fr) 208px;
    gap: 22px;
  }
  .article-card.featured .card-title {
    font-size: 1.72rem;
    line-height: 1.02;
  }
  .article-card.featured .card-banner {
    width: 208px;
    height: 152px;
    border-radius: 20px;
  }
  .article-card.featured .card-preview {
    -webkit-line-clamp: 3;
  }
  .article-card.is-read { opacity: 0.76; }
  .article-card.is-read .card-title { color: #695b4d; }
  .article-card.is-read .card-preview { color: #8a7a68; }
  .article-card.is-read:hover { opacity: 0.82; }

  .card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 7px; }
  .card-source { display: flex; align-items: center; gap: var(--sp-1); font-size: 10px; color: #6a5d51; font-weight: 600; min-width: 0; text-transform: uppercase; letter-spacing: 0.09em; }
  .card-rss { width: 12px; height: 12px; color: #8a7a68; flex-shrink: 0; }
  .card-feed-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .card-author { color: #8a7a68; font-weight: 400; white-space: nowrap; text-transform: none; letter-spacing: 0; }
  .card-header-right { display: flex; align-items: center; gap: var(--sp-1); flex-shrink: 0; }
  .card-time { font-size: 0.72rem; color: #8a7a68; }
  .star-btn {
    width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
    background: none; border: none; cursor: pointer; color: var(--text-4); border-radius: var(--radius-sm);
    transition: all var(--transition); opacity: 0;
  }
  .article-card:hover .star-btn { opacity: 1; }
  .star-btn.starred { opacity: 1; color: #F59E0B; }
  .star-btn:hover { color: #F59E0B; }
  .star-btn svg { width: 13px; height: 13px; }

  .bookmark-btn {
    width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
    background: none; border: none; cursor: pointer; color: #8a7a68; border-radius: 10px;
    transition: all var(--transition); opacity: 0.45;
  }
  .article-card:hover .bookmark-btn { opacity: 0.85; }
  .bookmark-btn.saved { opacity: 1; color: #F59E0B; }
  .bookmark-btn:hover { opacity: 1; color: #F59E0B; }
  .bookmark-btn svg { width: 13px; height: 13px; }

  .spinning { animation: spin 0.8s linear infinite; }
  @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

  .card-main { display: grid; grid-template-columns: minmax(0, 1fr) 136px; gap: 14px; align-items: start; }
  .card-copy {
    min-width: 0;
  }
  .card-title { font-size: 1.04rem; font-weight: 700; color: #1b140d; line-height: 1.14; margin-bottom: 6px; letter-spacing: -0.038em; }
  .card-banner { width: 136px; height: 96px; border-radius: 16px; background: rgba(229,220,209,0.8) center/cover no-repeat; }
  .card-preview { font-size: 0.9rem; color: #5d5349; line-height: 1.52; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 0; }
  .card-footer { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
  .card-reading-time { display: flex; align-items: center; gap: 3px; font-size: var(--text-xs); color: #8a7a68; }
  .card-reading-time svg { width: 11px; height: 11px; }
  .card-state {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: #8a7a68;
  }
  .list-empty { padding: var(--sp-10); text-align: center; color: #65584c; font-size: var(--text-sm); }

  /* ── Reading pane (slide-in sheet) ── */
  .pane-overlay {
    position: fixed; inset: 0; background: rgba(17,13,10,0.46); z-index: 60;
    display: flex; justify-content: flex-end; animation: paneFadeIn 150ms ease;
    backdrop-filter: blur(16px);
  }
  @keyframes paneFadeIn { from { opacity: 0; } to { opacity: 1; } }
  .pane-sheet {
    width: 740px; max-width: 100%; height: 100%; background: linear-gradient(180deg, #f7efe6 0%, #ece1d3 100%);
    display: flex; flex-direction: column;
    border-left: 1px solid rgba(35,26,17,0.1);
    box-shadow: -16px 0 44px rgba(0,0,0,0.14);
    animation: paneSlideIn 200ms ease;
  }
  @keyframes paneSlideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }
  .pane-scroll { flex: 1; overflow-y: auto; }

  .pane-toolbar {
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px 22px; border-bottom: 1px solid rgba(35,26,17,0.1); flex-shrink: 0;
    background: rgba(249,242,233,0.72);
    backdrop-filter: blur(12px);
  }
  .pane-nav { display: flex; align-items: center; gap: var(--sp-1); }
  .pane-nav-btn {
    width: 34px; height: 34px; border-radius: 10px; background: rgba(255,250,244,0.74);
    border: 1px solid rgba(35,26,17,0.08); display: flex; align-items: center; justify-content: center;
    cursor: pointer; color: #65584c; transition: all var(--transition);
  }
  .pane-nav-btn:hover { color: #1f1811; background: rgba(255,250,244,0.95); }
  .pane-nav-btn:disabled { opacity: 0.25; cursor: default; }
  .pane-nav-btn svg { width: 14px; height: 14px; }
  .pane-nav-pos { font-size: var(--text-xs); color: #8a7a68; font-family: var(--mono); min-width: 44px; text-align: center; }

  .pane-actions { display: flex; align-items: center; gap: var(--sp-0.5); }
  .pane-action-btn {
    width: 34px; height: 34px; border-radius: 10px; background: none;
    border: none; display: flex; align-items: center; justify-content: center;
    cursor: pointer; color: #65584c; transition: all var(--transition); text-decoration: none;
  }
  .pane-action-btn:hover { color: #1f1811; background: rgba(255,250,244,0.92); }
  .pane-action-btn.active { color: var(--accent); }
  .pane-action-btn.saved-brain { color: #F59E0B; }
  .pane-action-btn svg { width: 15px; height: 15px; }

  .pane-content { max-width: 680px; margin: 0 auto; padding: 30px 36px 88px; overflow-x: hidden; }
  .pane-hero { margin-bottom: 18px; }
  .pane-hero-image {
    width: 100%;
    height: 280px;
    border-radius: 24px;
    background: rgba(228,217,204,0.9) center/cover no-repeat;
    box-shadow: 0 16px 36px rgba(39,28,18,0.09);
  }
  .pane-title { font-size: clamp(2rem, 3.6vw, 3rem); font-weight: 700; line-height: 1.04; color: #1b140d; margin: 0 0 14px; letter-spacing: -0.05em; }
  .pane-meta {
    display: flex; align-items: center; gap: var(--sp-1.5); flex-wrap: wrap;
    font-size: var(--text-sm); color: #65584c; margin-bottom: var(--sp-6);
    padding-bottom: var(--sp-4); border-bottom: 1px solid rgba(35,26,17,0.08);
  }
  .pane-source { font-weight: 600; color: #1f1811; }
  .pane-author { font-style: italic; }
  .pane-dot { width: 3px; height: 3px; border-radius: 50%; background: #8a7a68; }
  .pane-body { font-size: 1.02rem; line-height: 1.9; color: #42372d; overflow-wrap: break-word; word-break: break-word; }
  .pane-body :global(p) { margin-bottom: 18px; }
  .pane-body :global(img) { max-width: 100%; height: auto; border-radius: var(--radius-md); margin: var(--sp-3) 0; }
  .pane-body :global(table) { max-width: 100%; overflow-x: auto; display: block; }
  .pane-body :global(iframe) { max-width: 100%; }
  .pane-body :global(*) { max-width: 100%; }
  .pane-body :global(a) { color: var(--accent); text-decoration: underline; }
  .pane-body :global(a:hover) { opacity: 0.8; }
  .pane-body :global(blockquote) { border-left: 3px solid var(--border); padding-left: var(--sp-4); margin: var(--sp-4) 0; color: var(--text-3); font-style: italic; }
  .pane-body :global(pre) { background: var(--surface-secondary); padding: 14px; border-radius: var(--radius-md); overflow-x: auto; font-size: var(--text-sm); font-family: var(--mono); margin: var(--sp-4) 0; }
  .pane-body :global(code) { font-family: var(--mono); font-size: 0.9em; }
  .pane-body :global(h1), .pane-body :global(h2), .pane-body :global(h3) { color: var(--text-1); margin: 20px 0 10px; }
  .pane-body :global(ul), .pane-body :global(ol) { padding-left: var(--sp-5); margin-bottom: var(--sp-4); }
  .pane-body :global(li) { margin-bottom: var(--sp-1.5); }

  /* ── Responsive ── */
  @media (min-width: 1025px) {
    :global(body) {
      overflow: hidden;
    }

    :global(.shell) {
      height: 100vh;
      min-height: 100vh;
      overflow: hidden;
    }

    :global(.shell-main) {
      min-height: 0;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }

    :global(.shell-content) {
      flex: 1;
      min-height: 0;
      overflow: hidden;
    }
  }

  @media (max-width: 1024px) {
    :global(body) {
      background: linear-gradient(180deg, #f3eadf 0%, #e8ddce 100%);
      overflow: auto;
    }

    .reader-layout {
      position: relative;
      grid-template-columns: minmax(0, 1fr);
      height: auto;
      min-height: 100vh;
      overflow: visible;
    }

    .reader-sidebar { display: none; }
    .reader-sidebar.open { display: flex; position: fixed; left: 0; top: 56px; bottom: 0; z-index: 40; box-shadow: 8px 0 24px rgba(0,0,0,0.08); }
    .mobile-menu { display: flex; }
    .reader-list {
      overflow: visible;
    }
    .article-list {
      overflow: visible;
    }
  }
  @media (max-width: 768px) {
    :global(.mobile-bar) {
      position: fixed;
      inset: 0 0 auto 0;
      background: transparent;
      border-bottom: none;
      backdrop-filter: none;
      pointer-events: none;
    }

    :global(.mobile-bar .mobile-menu-btn),
    :global(.mobile-bar .command-trigger.mobile),
    :global(.mobile-bar .mobile-brand) {
      pointer-events: auto;
    }

    .reader-sidebar { display: none; }
    .reader-sidebar.open { display: flex; position: fixed; left: 0; top: 56px; bottom: 0; z-index: 40; box-shadow: 8px 0 24px rgba(0,0,0,0.08); width: 260px; }
    .mobile-menu { display: flex; }
    .reader-list {
      background: transparent;
      overflow: visible;
      position: relative;
    }
    .list-header {
      padding: 8px 14px 8px;
      position: relative;
      background: transparent;
      backdrop-filter: none;
      border-bottom: none;
    }
    .list-header-top {
      gap: 12px;
    }
    .list-view-name { font-size: 1.44rem; }
    .list-subtitle { font-size: 0.84rem; line-height: 1.4; }
    .article-list {
      padding: 2px 14px calc(env(safe-area-inset-bottom, 0px) + 18px);
      gap: 8px;
      background: transparent;
      overflow: visible;
    }
    .list-actions {
      display: none;
    }
    .mobile-scroll-fab {
      position: fixed;
      right: 14px;
      bottom: calc(env(safe-area-inset-bottom, 0px) + 18px);
      z-index: 18;
      display: grid;
      justify-items: end;
      gap: 8px;
    }
    .mobile-scroll-pill {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      padding: 11px 14px;
      border-radius: 999px;
      border: 1px solid rgba(35,26,17,0.1);
      background: rgba(249, 242, 233, 0.94);
      color: #54493f;
      box-shadow: 0 14px 28px rgba(31, 21, 14, 0.12);
      backdrop-filter: blur(14px);
      font-size: 0.84rem;
      font-weight: 600;
      letter-spacing: -0.01em;
    }
    .mobile-scroll-pill svg {
      width: 15px;
      height: 15px;
    }
    .mobile-scroll-fab.active .mobile-scroll-pill {
      background: rgba(89, 69, 49, 0.92);
      color: #fff8f0;
      border-color: rgba(89, 69, 49, 0.92);
    }
    .mobile-speed-pod {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      padding: 7px 9px;
      border-radius: 999px;
      border: 1px solid rgba(35,26,17,0.1);
      background: rgba(249, 242, 233, 0.94);
      box-shadow: 0 12px 22px rgba(31, 21, 14, 0.1);
      backdrop-filter: blur(14px);
    }
    .article-card {
      padding: 12px 12px 13px;
      border-radius: 18px;
      background: rgba(250, 244, 237, 0.88);
      border: 1px solid rgba(35,26,17,0.08);
      box-shadow: 0 8px 18px rgba(35,24,15,0.035);
    }
    .article-card::before { display: none; }
    .article-card:hover {
      transform: none;
    }
    .article-card.selected {
      background: rgba(247, 238, 227, 0.96);
      border-color: rgba(127,95,61,0.16);
    }
    .article-card.featured {
      padding: 16px;
      margin: 0;
      border-radius: 24px;
    }
    .card-main { grid-template-columns: 1fr; }
    .article-card.featured .card-main {
      grid-template-columns: 1fr;
      gap: 12px;
    }
    .card-title { font-size: 1rem; }
    .article-card.featured .card-title {
      font-size: 1.24rem;
    }
    .card-banner { width: 100%; height: 148px; border-radius: 15px; margin-top: 6px; }
    .card-preview { -webkit-line-clamp: 2; }
    .pane-sheet { width: 100%; }
    .pane-content { padding: 24px 18px 80px; }
    .pane-hero-image {
      height: 200px;
      border-radius: 18px;
    }
    .pane-title { font-size: 1.8rem; }
    .pane-body { font-size: var(--text-md); line-height: 1.75; }
    .pane-toolbar {
      padding: 10px 14px;
      background: rgba(246, 238, 228, 0.94);
    }
  }
</style>
