<script lang="ts">
  import {
    Download, CheckCircle, Circle, SortAscending, SortDescending,
    CaretDown, ArrowsClockwise, List, SquaresFour, FolderSimplePlus,
    Trash, DownloadSimple, X, MagnifyingGlass, Funnel, Check, FolderOpen,
  } from 'phosphor-svelte'
  import type { Chapter, Category } from '$lib/types'
  import type { ChapterSortMode, ChapterSortDir } from './lib/chapterList'

  interface ContinueChapter {
    chapter:    Chapter
    type:       'start' | 'continue' | 'reread'
    resumePage: number | null
  }

  interface Props {
    chapters:                Chapter[]
    sortedChapters:          Chapter[]
    sortMode:                ChapterSortMode
    sortDir:                 ChapterSortDir
    viewMode:                'list' | 'grid'
    downloadedCount:         number
    totalCount:              number
    deletingAll:             boolean
    hasSelection:            boolean
    selectedCount:           number
    continueChapter:         ContinueChapter | null
    availableScanlators:     string[]
    scanlatorFilter:         string[]
    scanlatorBlacklist:      string[]
    scanlatorForce:          boolean
    allCategories:           Category[]
    mangaCategories:         Category[]
    catsLoading:             boolean
    refreshing:              boolean
    onViewModeToggle:        () => void
    onPageChange:            (page: number) => void
    onDownloadSelected:      () => void
    onDeleteSelected:        () => void
    onMarkSelectedRead:      (isRead: boolean) => void
    onClearSelection:        () => void
    onEnqueueNext:           (n: number) => void
    onEnqueueMultiple:       (ids: number[]) => void
    onDeleteAll:             () => void
    onRefresh:               () => void
    onToggleCategory:        (cat: Category) => void
    onCreateCategory:        (name: string) => void
    onSetScanlatorFilter:    (v: string[]) => void
    onSetScanlatorBlacklist: (v: string[]) => void
    onSetScanlatorForce:     (v: boolean) => void
    onOpenFolder:            () => void
    onSortModeChange:        (v: ChapterSortMode) => void
    onSortDirChange:         (v: ChapterSortDir) => void
  }

  let {
    chapters, sortedChapters, sortMode, sortDir, viewMode,
    downloadedCount, totalCount, deletingAll,
    hasSelection, selectedCount, continueChapter,
    availableScanlators, scanlatorFilter, scanlatorBlacklist, scanlatorForce,
    allCategories, mangaCategories, catsLoading, refreshing,
    onViewModeToggle, onPageChange, onDownloadSelected, onDeleteSelected,
    onMarkSelectedRead, onClearSelection, onEnqueueNext, onEnqueueMultiple,
    onDeleteAll, onRefresh, onToggleCategory, onCreateCategory,
    onSetScanlatorFilter, onSetScanlatorBlacklist, onSetScanlatorForce,
    onOpenFolder, onSortModeChange, onSortDirChange,
  }: Props = $props()

  let sortMenuOpen:    boolean = $state(false)
  let jumpOpen:        boolean = $state(false)
  let jumpInput:       string  = $state('')
  let scanFilterOpen:  boolean = $state(false)
  let scanTab: 'prefer' | 'block' = $state('prefer')
  let dlOpen:          boolean = $state(false)
  let showRange:       boolean = $state(false)
  let rangeFrom:       string  = $state('')
  let rangeTo:         string  = $state('')
  let folderPickerOpen: boolean = $state(false)
  let folderCreating:   boolean = $state(false)
  let folderNewName:    string  = $state('')
  let dlDropRef:        HTMLDivElement | undefined = $state()
  let folderPickerRef:  HTMLDivElement | undefined = $state()

  const hasFolders = $derived(mangaCategories.filter(c => c.id !== 0).length > 0)

  const jumpChapter = $derived.by(() => {
    const q = jumpInput.trim().toLowerCase()
    if (!q) return null
    const num = parseFloat(q)
    if (!isNaN(num)) return sortedChapters.find(c => c.chapterNumber === num) ?? null
    return sortedChapters.find(c => c.name.toLowerCase().includes(q)) ?? null
  })

  function focusOnMount(node: HTMLElement) { node.focus() }

  function doJump() {
    if (!jumpChapter) return
    const pageIdx = sortedChapters.indexOf(jumpChapter)
    if (pageIdx >= 0) onPageChange(Math.floor(pageIdx / 25) + 1)
    jumpOpen = false; jumpInput = ''
  }

  function enqueueRange() {
    const from = parseFloat(rangeFrom), to = parseFloat(rangeTo)
    if (isNaN(from) || isNaN(to)) return
    const lo = Math.min(from, to), hi = Math.max(from, to)
    onEnqueueMultiple(sortedChapters.filter(c => c.chapterNumber >= lo && c.chapterNumber <= hi && !c.downloaded).map(c => c.id))
  }

  function submitNewFolder() {
    const name = folderNewName.trim()
    if (!name) return
    onCreateCategory(name)
    folderNewName = ''; folderCreating = false
  }

  $effect(() => {
    if (dlOpen) { setTimeout(() => document.addEventListener('mousedown', handleDlOutside, true), 0) }
    else document.removeEventListener('mousedown', handleDlOutside, true)
  })

  $effect(() => {
    if (folderPickerOpen) { setTimeout(() => document.addEventListener('mousedown', handleFolderOutside, true), 0) }
    else document.removeEventListener('mousedown', handleFolderOutside, true)
  })

  $effect(() => {
    if (!scanFilterOpen) return
    function onOutside(e: MouseEvent) {
      if (!(e.target as HTMLElement).closest('.scan-filter-wrap')) scanFilterOpen = false
    }
    setTimeout(() => document.addEventListener('mousedown', onOutside, true), 0)
    return () => document.removeEventListener('mousedown', onOutside, true)
  })

  function handleDlOutside(e: MouseEvent) {
    if (dlDropRef && !dlDropRef.contains(e.target as Node)) dlOpen = false
  }
  function handleFolderOutside(e: MouseEvent) {
    if (folderPickerRef && !folderPickerRef.contains(e.target as Node)) {
      folderPickerOpen = false; folderCreating = false; folderNewName = ''
    }
  }
</script>

<div class="list-header">
  <div class="list-header-left">
    {#if hasSelection}
      <span class="sel-count">{selectedCount} selected</span>
      <button class="sel-action-btn" onclick={onDownloadSelected} title="Download selected"><Download size={13} weight="light" /></button>
      <button class="sel-action-btn sel-action-danger" onclick={onDeleteSelected} title="Delete selected downloads"><Trash size={13} weight="light" /></button>
      <button class="sel-action-btn" onclick={() => onMarkSelectedRead(true)} title="Mark selected as read"><CheckCircle size={13} weight="light" /></button>
      <button class="sel-action-btn" onclick={() => onMarkSelectedRead(false)} title="Mark selected as unread"><Circle size={13} weight="light" /></button>
      <button class="sel-action-btn" onclick={onClearSelection} title="Clear selection"><X size={13} weight="light" /></button>
    {:else}
      <div class="sort-wrap">
        <button class="sort-btn" onclick={() => sortMenuOpen = !sortMenuOpen}>
          {#if sortDir === 'desc'}<SortDescending size={14} weight="light" />{:else}<SortAscending size={14} weight="light" />{/if}
          {{ source: 'Source order', chapterNumber: 'Ch. number', uploadDate: 'Upload date' }[sortMode]}
          <CaretDown size={10} weight="light" />
        </button>
        {#if sortMenuOpen}
          <div class="sort-menu" role="presentation" onmouseleave={() => sortMenuOpen = false}>
            {#each [['source','Source order'],['chapterNumber','Chapter number'],['uploadDate','Upload date']] as [val, label]}
              <button
                class="sort-option"
                class:active={sortMode === val}
                onclick={() => { onSortModeChange(val as ChapterSortMode); onPageChange(1); sortMenuOpen = false }}
              >{label}</button>
            {/each}
            <div class="sort-divider"></div>
            <button class="sort-option" onclick={() => { onSortDirChange(sortDir === 'desc' ? 'asc' : 'desc'); onPageChange(1); sortMenuOpen = false }}>
              {sortDir === 'desc' ? '↑ Ascending' : '↓ Descending'}
            </button>
          </div>
        {/if}
      </div>
      <button class="icon-btn" class:active={viewMode === 'grid'} onclick={onViewModeToggle} title={viewMode === 'list' ? 'Grid view' : 'List view'}>
        {#if viewMode === 'list'}<SquaresFour size={14} weight="light" />{:else}<List size={14} weight="light" />{/if}
      </button>
    {/if}
  </div>

  <div class="list-header-right">

    <div class="jump-wrap">
      <button class="icon-btn" class:active={jumpOpen} onclick={() => { jumpOpen = !jumpOpen; jumpInput = '' }} title="Jump to chapter">
        <MagnifyingGlass size={14} weight="light" />
      </button>
      {#if jumpOpen}
        <div class="jump-popover">
          <input
            class="jump-input"
            placeholder="Chapter # or name…"
            bind:value={jumpInput}
            use:focusOnMount
            onkeydown={(e) => { if (e.key === 'Enter') doJump(); if (e.key === 'Escape') { jumpOpen = false; jumpInput = '' } }}
          />
          {#if jumpChapter}
            <button class="jump-go" onclick={doJump}>Go · {jumpChapter.name}</button>
          {:else if jumpInput.trim()}
            <p class="jump-none">No match</p>
          {/if}
        </div>
      {/if}
    </div>

    {#if availableScanlators.length > 1}
      <div class="scan-filter-wrap">
        <button
          class="icon-btn"
          class:active={scanlatorFilter.length > 0 || scanlatorBlacklist.length > 0}
          onclick={() => scanFilterOpen = !scanFilterOpen}
          title="Filter by scanlator"
        >
          <Funnel size={14} weight={scanlatorFilter.length > 0 || scanlatorBlacklist.length > 0 ? 'fill' : 'light'} />
        </button>
        {#if scanFilterOpen}
          <div class="scan-filter-panel" role="menu">
            <div class="scan-filter-header">
              <div class="scan-filter-tabs">
                <button class="scan-filter-tab" class:scan-filter-tab-active={scanTab === 'prefer'} onclick={() => scanTab = 'prefer'}>Prefer</button>
                <button class="scan-filter-tab" class:scan-filter-tab-active={scanTab === 'block'}  onclick={() => scanTab = 'block'}>Block</button>
              </div>
              {#if scanTab === 'prefer' && scanlatorFilter.length > 0}
                <button class="scan-filter-clear" onclick={() => { onSetScanlatorFilter([]); onSetScanlatorForce(false); onPageChange(1) }}>Clear</button>
              {:else if scanTab === 'block' && scanlatorBlacklist.length > 0}
                <button class="scan-filter-clear" onclick={() => { onSetScanlatorBlacklist([]); onPageChange(1) }}>Clear</button>
              {/if}
            </div>
            <div class="scan-filter-divider"></div>
            {#if scanTab === 'prefer'}
              <div class="scan-filter-force-row">
                <span class="scan-filter-force-label" title="Hide chapters with no preferred group match, rather than falling back to any available group.">Enforce</span>
                <button class="scan-force-toggle" class:scan-force-on={scanlatorForce} onclick={() => { onSetScanlatorForce(!scanlatorForce); onPageChange(1) }}>
                  {scanlatorForce ? 'On' : 'Off'}
                </button>
              </div>
              <div class="scan-filter-divider"></div>
              {#each availableScanlators as s}
                <button
                  class="scan-filter-item"
                  class:scan-filter-item-active={scanlatorFilter.includes(s)}
                  role="menuitem"
                  onclick={() => { onSetScanlatorFilter(scanlatorFilter.includes(s) ? scanlatorFilter.filter(x => x !== s) : [...scanlatorFilter, s]); onPageChange(1) }}
                >
                  <span class="scan-filter-check" class:scan-filter-check-on={scanlatorFilter.includes(s)}>
                    {#if scanlatorFilter.includes(s)}<Check size={9} weight="bold" />{/if}
                  </span>
                  {s}
                </button>
              {/each}
            {:else}
              {#each availableScanlators as s}
                <button
                  class="scan-filter-item"
                  class:scan-filter-item-active={scanlatorBlacklist.includes(s)}
                  class:scan-filter-item-block={scanlatorBlacklist.includes(s)}
                  role="menuitem"
                  onclick={() => { onSetScanlatorBlacklist(scanlatorBlacklist.includes(s) ? scanlatorBlacklist.filter(x => x !== s) : [...scanlatorBlacklist, s]); onPageChange(1) }}
                >
                  <span class="scan-filter-check" class:scan-filter-check-block={scanlatorBlacklist.includes(s)}>
                    {#if scanlatorBlacklist.includes(s)}<X size={9} weight="bold" />{/if}
                  </span>
                  {s}
                </button>
              {/each}
            {/if}
          </div>
        {/if}
      </div>
    {/if}

    <button class="icon-btn" onclick={onRefresh} disabled={refreshing} title="Refresh chapters">
      <ArrowsClockwise size={14} weight="light" class={refreshing ? 'anim-spin' : ''} />
    </button>

    {#if downloadedCount > 0}
      <button class="icon-btn" onclick={onOpenFolder} title="Open manga folder">
        <FolderOpen size={14} weight="light" />
      </button>
    {/if}

    <div class="fp-wrap" bind:this={folderPickerRef}>
      <button class="icon-btn" class:active={hasFolders} onclick={() => folderPickerOpen = !folderPickerOpen}>
        <FolderSimplePlus size={14} weight={hasFolders ? 'fill' : 'light'} />
      </button>
      {#if folderPickerOpen}
        <div class="fp-menu">
          {#if catsLoading}
            <p class="fp-empty">Loading…</p>
          {:else if allCategories.length === 0 && !folderCreating}
            <p class="fp-empty">No folders yet</p>
          {/if}
          {#each allCategories as cat}
            {@const isIn = mangaCategories.some(c => c.id === cat.id)}
            <button class="fp-item" class:fp-item-active={isIn} onclick={() => onToggleCategory(cat)}>
              <span class="fp-check">{isIn ? '✓' : ''}</span>{cat.name}
            </button>
          {/each}
          <div class="fp-div"></div>
          {#if folderCreating}
            <div class="fp-create">
              <input
                class="fp-input"
                placeholder="Folder name…"
                bind:value={folderNewName}
                use:focusOnMount
                onkeydown={(e) => { if (e.key === 'Enter') submitNewFolder(); if (e.key === 'Escape') { folderCreating = false; folderNewName = '' } }}
              />
              <button class="fp-confirm" onclick={submitNewFolder} disabled={!folderNewName.trim()}>Add</button>
              <button class="fp-cancel" onclick={() => { folderCreating = false; folderNewName = '' }}><X size={12} weight="light" /></button>
            </div>
          {:else}
            <button class="fp-new" onclick={() => folderCreating = true}>+ New folder</button>
          {/if}
        </div>
      {/if}
    </div>

    {#if chapters.length > 0}
      <div class="dl-wrap" bind:this={dlDropRef}>
        <button class="icon-btn dl-unified-btn" class:active={dlOpen} class:dl-has-count={downloadedCount > 0} onclick={() => dlOpen = !dlOpen} title="Download options">
          <Download size={13} weight={downloadedCount > 0 ? 'fill' : 'light'} />
          {#if downloadedCount > 0}<span class="dl-unified-count">{downloadedCount}</span>{/if}
        </button>
        {#if dlOpen}
          <div class="dl-dropdown">
            {#if downloadedCount > 0}
              <p class="dl-section-label">{downloadedCount} / {totalCount} downloaded</p>
              <div class="dl-divider"></div>
            {/if}
            {#if continueChapter}
              {@const contIdx = sortedChapters.indexOf(continueChapter.chapter)}
              {#if contIdx >= 0}
                <p class="dl-section-label">From Ch.{continueChapter.chapter.chapterNumber}</p>
                <div class="dl-next-row">
                  {#each [5, 10, 25] as n}
                    {@const avail = sortedChapters.slice(contIdx, contIdx + n).filter(c => !c.downloaded).length}
                    <button class="dl-next-btn" disabled={avail === 0} onclick={() => { onEnqueueNext(n); dlOpen = false }}>
                      <span>Next {n}</span><span class="dl-next-sub">{avail} new</span>
                    </button>
                  {/each}
                </div>
                <div class="dl-divider"></div>
              {/if}
            {/if}
            {#if !showRange}
              <button class="dl-item" onclick={() => showRange = true}>
                <span>Custom range…</span><span class="dl-item-sub">Enter chapter numbers</span>
              </button>
            {:else}
              <div class="dl-range-row">
                <button class="dl-range-back" onclick={() => showRange = false}>‹</button>
                <input class="dl-range-input" placeholder="From" bind:value={rangeFrom} onkeydown={(e) => e.key === 'Enter' && enqueueRange()} use:focusOnMount />
                <span class="dl-range-sep">–</span>
                <input class="dl-range-input" placeholder="To" bind:value={rangeTo} onkeydown={(e) => e.key === 'Enter' && enqueueRange()} />
                <button class="dl-range-go" disabled={!rangeFrom.trim() || !rangeTo.trim()} onclick={enqueueRange}>Go</button>
              </div>
            {/if}
            <div class="dl-divider"></div>
            <button class="dl-item" onclick={() => { onEnqueueMultiple(sortedChapters.filter(c => !c.read && !c.downloaded).map(c => c.id)); dlOpen = false }}>
              <span>Unread chapters</span><span class="dl-item-sub">{sortedChapters.filter(c => !c.read && !c.downloaded).length} remaining</span>
            </button>
            <button class="dl-item" onclick={() => { onEnqueueMultiple(sortedChapters.filter(c => !c.downloaded).map(c => c.id)); dlOpen = false }}>
              <span>Download all</span><span class="dl-item-sub">{sortedChapters.filter(c => !c.downloaded).length} not downloaded</span>
            </button>
            {#if downloadedCount > 0}
              <div class="dl-divider"></div>
              <button class="dl-item dl-item-danger" onclick={() => { onDeleteAll(); dlOpen = false }} disabled={deletingAll}>
                <span>{deletingAll ? 'Deleting…' : 'Delete all downloads'}</span>
                <span class="dl-item-sub">{downloadedCount} downloaded</span>
              </button>
            {/if}
          </div>
        {/if}
      </div>
    {/if}

  </div>
</div>

<style>
  .list-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border-dim);
    flex-shrink: 0; gap: var(--sp-2); flex-wrap: wrap;
  }
  .list-header-left,
  .list-header-right { display: flex; align-items: center; gap: var(--sp-1); }

  .sort-btn {
    display: flex; align-items: center; gap: 5px;
    font-family: var(--font-ui); font-size: var(--text-xs); letter-spacing: var(--tracking-wide);
    padding: 4px var(--sp-2); border-radius: var(--radius-sm);
    color: var(--text-muted); transition: color var(--t-base), background var(--t-base);
  }
  .sort-btn:hover { color: var(--text-secondary); background: var(--bg-raised); }
  .sort-wrap { position: relative; }
  .sort-menu {
    position: absolute; top: calc(100% + 4px); left: 0; min-width: 160px;
    background: var(--bg-raised); border: 1px solid var(--border-base);
    border-radius: var(--radius-md); padding: var(--sp-1); z-index: 200;
    box-shadow: 0 8px 24px rgba(0,0,0,0.5);
    animation: scaleIn 0.1s ease both; transform-origin: top left;
  }
  .sort-option {
    display: block; width: 100%; padding: 6px var(--sp-3); border-radius: var(--radius-sm);
    font-family: var(--font-ui); font-size: var(--text-xs); color: var(--text-secondary);
    background: none; border: none; cursor: pointer; text-align: left;
    transition: background var(--t-fast), color var(--t-fast);
  }
  .sort-option:hover { background: var(--bg-overlay); color: var(--text-primary); }
  .sort-option.active { color: var(--accent-fg); }
  .sort-divider { height: 1px; background: var(--border-dim); margin: var(--sp-1) var(--sp-2); }

  .icon-btn {
    display: flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; border-radius: var(--radius-md);
    border: 1px solid var(--border-dim); color: var(--text-muted);
    background: none; cursor: pointer;
    transition: color var(--t-base), border-color var(--t-base), background var(--t-base);
  }
  .icon-btn:hover:not(:disabled) { color: var(--text-secondary); border-color: var(--border-strong); background: var(--bg-raised); }
  .icon-btn.active { color: var(--accent-fg); border-color: var(--accent-dim); background: var(--accent-muted); }
  .icon-btn:disabled { opacity: 0.3; cursor: default; }

  .jump-wrap { position: relative; }
  .jump-popover {
    position: absolute; top: calc(100% + 4px); right: 0; width: 220px;
    background: var(--bg-raised); border: 1px solid var(--border-base);
    border-radius: var(--radius-md); padding: var(--sp-2); z-index: 200;
    box-shadow: 0 8px 24px rgba(0,0,0,0.5);
    animation: scaleIn 0.1s ease both; transform-origin: top right;
    display: flex; flex-direction: column; gap: var(--sp-1);
  }
  .jump-input {
    width: 100%; background: var(--bg-overlay); border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); padding: 5px 9px;
    font-size: var(--text-xs); color: var(--text-secondary); outline: none;
    transition: border-color var(--t-base);
  }
  .jump-input:focus { border-color: var(--border-focus); }
  .jump-go {
    width: 100%; padding: 6px var(--sp-2); border-radius: var(--radius-sm);
    background: var(--accent-muted); border: 1px solid var(--accent-dim); color: var(--accent-fg);
    font-family: var(--font-ui); font-size: var(--text-xs); letter-spacing: var(--tracking-wide);
    cursor: pointer; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    transition: background var(--t-fast), border-color var(--t-fast);
  }
  .jump-go:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast, #fff); }
  .jump-none { font-family: var(--font-ui); font-size: var(--text-xs); color: var(--text-faint); padding: 4px var(--sp-1); letter-spacing: var(--tracking-wide); }

  .fp-wrap { position: relative; }
  .fp-menu {
    position: absolute; top: calc(100% + 4px); right: 0; min-width: 180px;
    background: var(--bg-raised); border: 1px solid var(--border-base);
    border-radius: var(--radius-md); padding: var(--sp-1); z-index: 200;
    box-shadow: 0 8px 24px rgba(0,0,0,0.5);
    animation: scaleIn 0.1s ease both; transform-origin: top right;
  }
  .fp-empty { padding: var(--sp-2) var(--sp-3); font-size: var(--text-xs); color: var(--text-faint); }
  .fp-item {
    display: flex; align-items: center; gap: var(--sp-2); width: 100%;
    padding: 6px var(--sp-3); border-radius: var(--radius-sm); font-size: var(--text-xs);
    color: var(--text-secondary); background: none; border: none; cursor: pointer; text-align: left;
    transition: background var(--t-fast), color var(--t-fast);
  }
  .fp-item:hover { background: var(--bg-overlay); }
  .fp-item.fp-item-active { color: var(--accent-fg); }
  .fp-check { width: 12px; font-size: var(--text-xs); color: var(--accent-fg); flex-shrink: 0; }
  .fp-div { height: 1px; background: var(--border-dim); margin: var(--sp-1) var(--sp-2); }
  .fp-create { display: flex; align-items: center; gap: var(--sp-1); padding: 4px var(--sp-2); }
  .fp-input {
    flex: 1; background: var(--bg-overlay); border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); padding: 4px 8px;
    font-size: var(--text-xs); color: var(--text-secondary); outline: none; min-width: 0;
  }
  .fp-input:focus { border-color: var(--border-focus); }
  .fp-confirm {
    font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide);
    padding: 4px 8px; border-radius: var(--radius-sm);
    border: 1px solid var(--accent-dim); background: var(--accent-muted); color: var(--accent-fg); cursor: pointer;
  }
  .fp-confirm:disabled { opacity: 0.4; cursor: default; }
  .fp-cancel {
    display: flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: var(--radius-sm);
    border: 1px solid transparent; background: none; color: var(--text-faint); cursor: pointer;
    transition: color var(--t-base), border-color var(--t-base);
  }
  .fp-cancel:hover { color: var(--text-muted); border-color: var(--border-dim); }
  .fp-new {
    width: 100%; padding: 6px var(--sp-3); border-radius: var(--radius-sm);
    font-size: var(--text-xs); color: var(--text-faint); background: none; border: none;
    cursor: pointer; text-align: left; transition: color var(--t-fast), background var(--t-fast);
  }
  .fp-new:hover { color: var(--text-secondary); background: var(--bg-overlay); }

  .dl-wrap { position: relative; }
  .dl-dropdown {
    position: absolute; top: calc(100% + 4px); right: 0; min-width: 220px;
    background: var(--bg-raised); border: 1px solid var(--border-base);
    border-radius: var(--radius-lg); padding: var(--sp-1); z-index: 200;
    box-shadow: 0 8px 32px rgba(0,0,0,0.5);
    animation: scaleIn 0.1s ease both; transform-origin: top right;
  }
  .dl-section-label {
    padding: 6px var(--sp-3) 4px; font-family: var(--font-ui); font-size: var(--text-2xs);
    color: var(--text-faint); letter-spacing: var(--tracking-wider); text-transform: uppercase;
  }
  .dl-next-row { display: flex; gap: 4px; padding: 2px var(--sp-2) var(--sp-2); }
  .dl-next-btn {
    flex: 1; display: flex; align-items: center; justify-content: center; gap: 5px;
    padding: 5px 6px; border-radius: var(--radius-md); border: 1px solid var(--border-dim);
    background: var(--bg-overlay); color: var(--text-secondary);
    font-family: var(--font-ui); font-size: var(--text-xs); letter-spacing: var(--tracking-wide); cursor: pointer;
    transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
  }
  .dl-next-btn:hover:not(:disabled) { background: var(--accent-muted); border-color: var(--accent-dim); color: var(--accent-fg); }
  .dl-next-btn:disabled { opacity: 0.3; cursor: default; }
  .dl-next-sub { font-size: var(--text-2xs); color: var(--text-faint); }
  .dl-divider { height: 1px; background: var(--border-dim); margin: var(--sp-1) var(--sp-2); }
  .dl-item {
    display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
    width: 100%; padding: 7px var(--sp-3); border-radius: var(--radius-md);
    font-size: var(--text-sm); color: var(--text-secondary); background: none; border: none;
    cursor: pointer; text-align: left; transition: background var(--t-fast), color var(--t-fast);
  }
  .dl-item:hover:not(:disabled) { background: var(--bg-overlay); color: var(--text-primary); }
  .dl-item:disabled { opacity: 0.3; cursor: default; }
  .dl-item.dl-item-danger { color: var(--color-error); }
  .dl-item.dl-item-danger:hover:not(:disabled) { background: var(--color-error-bg); }
  .dl-item-sub { font-size: var(--text-xs); color: var(--text-faint); }
  .dl-range-row { display: flex; align-items: center; gap: 4px; padding: 7px var(--sp-2); }
  .dl-range-back {
    display: flex; align-items: center; justify-content: center;
    width: 20px; height: 20px; border-radius: var(--radius-sm);
    border: 1px solid var(--border-dim); background: none; color: var(--text-faint); font-size: 14px; cursor: pointer;
  }
  .dl-range-back:hover { color: var(--text-muted); background: var(--bg-overlay); }
  .dl-range-input {
    flex: 1; min-width: 0; padding: 4px 8px; background: var(--bg-overlay);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    color: var(--text-secondary); font-family: var(--font-ui); font-size: var(--text-xs);
    outline: none; text-align: center;
  }
  .dl-range-input:focus { border-color: var(--border-focus); }
  .dl-range-sep { color: var(--text-faint); font-size: var(--text-xs); }
  .dl-range-go {
    padding: 4px 10px; border-radius: var(--radius-sm);
    border: 1px solid var(--accent-dim); background: var(--accent-muted); color: var(--accent-fg);
    font-family: var(--font-ui); font-size: var(--text-xs); cursor: pointer;
  }
  .dl-range-go:disabled { opacity: 0.3; cursor: default; }
  .dl-unified-btn { gap: 5px; padding: 0 8px; width: auto; min-width: 28px; }
  .dl-unified-count {
    font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide);
    color: var(--text-faint); transition: color var(--t-base);
  }
  .dl-unified-btn:hover .dl-unified-count,
  .dl-unified-btn.active .dl-unified-count { color: var(--text-secondary); }
  .dl-unified-btn.dl-has-count { color: var(--accent-fg); border-color: var(--accent-dim); background: var(--accent-muted); }
  .dl-unified-btn.dl-has-count .dl-unified-count { color: var(--accent-fg); opacity: 0.8; }
  .dl-unified-btn.dl-has-count:hover { background: var(--accent-muted); border-color: var(--accent); opacity: 0.9; }
  .dl-unified-btn.active { color: var(--accent-fg); border-color: var(--accent-dim); background: var(--accent-muted); }

  .sel-count {
    font-family: var(--font-ui); font-size: var(--text-xs); color: var(--text-muted);
    letter-spacing: var(--tracking-wide); padding: 0 var(--sp-1);
  }
  .sel-action-btn {
    display: flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; border-radius: var(--radius-sm);
    border: 1px solid var(--border-dim); background: none; color: var(--text-muted); cursor: pointer;
    transition: color var(--t-base), background var(--t-base), border-color var(--t-base);
  }
  .sel-action-btn:hover { color: var(--text-primary); background: var(--bg-raised); border-color: var(--border-strong); }
  .sel-action-danger { color: var(--color-error) !important; }
  .sel-action-danger:hover { background: var(--color-error-bg) !important; border-color: var(--color-error) !important; }

  .scan-filter-wrap { position: relative; }
  .scan-filter-panel {
    position: absolute; top: calc(100% + 6px); right: 0; z-index: 200; min-width: 200px;
    background: var(--bg-raised); border: 1px solid var(--border-base);
    border-radius: var(--radius-lg); padding: var(--sp-1);
    box-shadow: 0 8px 32px rgba(0,0,0,0.5);
    animation: scaleIn 0.1s ease both; transform-origin: top right;
  }
  .scan-filter-header { display: flex; align-items: center; justify-content: space-between; padding: 4px 8px 6px; }
  .scan-filter-clear {
    font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide);
    color: var(--text-faint); background: none; border: none; cursor: pointer; padding: 0;
    transition: color var(--t-base);
  }
  .scan-filter-clear:hover { color: var(--color-error); }
  .scan-filter-divider { height: 1px; background: var(--border-dim); margin: 0 2px 4px; }
  .scan-filter-item {
    display: flex; align-items: center; gap: var(--sp-2); width: 100%;
    padding: 7px 10px; border-radius: var(--radius-sm); border: none; background: transparent;
    color: var(--text-muted); font-family: var(--font-ui); font-size: var(--text-xs);
    cursor: pointer; text-align: left;
    transition: background var(--t-base), color var(--t-base);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .scan-filter-item:hover { background: var(--bg-overlay); color: var(--text-primary); }
  .scan-filter-item-active { color: var(--accent-fg); background: var(--accent-muted); }
  .scan-filter-item-active:hover { background: var(--accent-dim); }
  .scan-filter-tabs {
    display: flex; gap: 2px; background: var(--bg-overlay);
    border: 1px solid var(--border-base); border-radius: var(--radius-sm); padding: 2px;
  }
  .scan-filter-tab {
    font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide);
    padding: 2px 8px; border-radius: 2px; border: none; background: none;
    color: var(--text-faint); cursor: pointer; transition: color var(--t-fast), background var(--t-fast);
  }
  .scan-filter-tab:hover { color: var(--text-muted); }
  .scan-filter-tab.scan-filter-tab-active { background: var(--bg-surface); color: var(--text-primary); box-shadow: 0 1px 3px rgba(0,0,0,0.3); }
  .scan-filter-force-row { display: flex; align-items: center; justify-content: space-between; padding: 5px 10px; }
  .scan-filter-force-label {
    font-family: var(--font-ui); font-size: var(--text-xs); color: var(--text-muted);
    letter-spacing: var(--tracking-wide); cursor: default;
    text-decoration: underline; text-decoration-style: dotted;
    text-decoration-color: var(--border-strong); text-underline-offset: 3px;
  }
  .scan-force-toggle {
    font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide);
    padding: 2px 8px; border-radius: var(--radius-sm); border: 1px solid var(--border-dim);
    background: none; color: var(--text-faint); cursor: pointer;
    transition: color var(--t-base), border-color var(--t-base), background var(--t-base);
  }
  .scan-force-toggle:hover { color: var(--text-muted); border-color: var(--border-strong); }
  .scan-force-toggle.scan-force-on { color: var(--accent-fg); border-color: var(--accent-dim); background: var(--accent-muted); }
  .scan-filter-check {
    width: 13px; height: 13px; border-radius: 2px; border: 1px solid var(--border-strong);
    background: transparent; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center; color: var(--bg-base);
    transition: background var(--t-base), border-color var(--t-base);
  }
  .scan-filter-check-on    { background: var(--accent); border-color: var(--accent); }
  .scan-filter-check-block { background: var(--color-error); border-color: var(--color-error); }
  .scan-filter-item-block  { color: var(--color-error) !important; background: color-mix(in srgb, var(--color-error) 8%, transparent) !important; }
  .scan-filter-item-block:hover { background: color-mix(in srgb, var(--color-error) 14%, transparent) !important; }

  @keyframes scaleIn { from { opacity: 0; transform: scale(0.97) } to { opacity: 1; transform: scale(1) } }
</style>