vomebook commited on
Commit
7ff552a
·
verified ·
1 Parent(s): 46fc1c6

Upload 3 files

Browse files
Files changed (3) hide show
  1. static/app.js +6 -6
  2. static/index.html +66 -36
  3. static/style.css +1875 -251
static/app.js CHANGED
@@ -3,19 +3,19 @@ const $ = (s) => document.querySelector(s);
3
  const DOM = {};
4
 
5
  function escapeHTML(v){return String(v ?? '').replace(/[&<>"']/g,(c)=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));}
6
- function toast(msg){const el=DOM.toast;el.textContent=msg;el.hidden=false;clearTimeout(toast.t);toast.t=setTimeout(()=>{el.hidden=true},2400);}
7
 
8
  function parseUrl(){const p=new URLSearchParams(location.search);STATE.q=p.get('q')||'';STATE.page=Math.max(1,parseInt(p.get('page')||'1',10)||1);STATE.pageSize=parseInt(p.get('page_size')||'20',10)||20;STATE.exact=p.get('exact')!=='0';STATE.source=p.get('source')||'';STATE.leftOpen=p.get('sidebar')!=='0';STATE.rightOpen=p.get('filters')==='1';STATE.preview=p.get('preview')||'';}
9
  function syncUrl(replace=true){const p=new URLSearchParams();if(STATE.q)p.set('q',STATE.q);if(STATE.page>1)p.set('page',String(STATE.page));if(STATE.pageSize!==20)p.set('page_size',String(STATE.pageSize));if(!STATE.exact)p.set('exact','0');if(STATE.source)p.set('source',STATE.source);if(!STATE.leftOpen)p.set('sidebar','0');if(STATE.rightOpen)p.set('filters','1');if(STATE.preview)p.set('preview',STATE.preview);const url=p.toString()?`/?${p}`:'/';history[replace?'replaceState':'pushState'](null,'',url);}
10
- function applyState(){DOM.searchInput.value=STATE.q;DOM.exactToggle.checked=STATE.exact;DOM.pageSize.value=String(STATE.pageSize);DOM.leftSidebar.classList.toggle('collapsed',!STATE.leftOpen);DOM.rightSidebar.classList.toggle('collapsed',!STATE.rightOpen);}
11
 
12
  async function api(path, opts){const r=await fetch(path,opts);if(!r.ok)throw new Error(`HTTP ${r.status}`);return r.json();}
13
  async function loadSources(){try{STATE.sources=await api('/api/sources');renderSources();}catch(e){DOM.sourceList.innerHTML='<div class="loading">来源加载失败</div>';}}
14
- function renderSources(){let html=`<button class="source-item ${!STATE.source?'active':''}" data-source=""><span>全部来源</span><small>${STATE.total?STATE.total.toLocaleString():''}</small></button>`;for(const s of STATE.sources){html+=`<button class="source-item ${STATE.source===s.name?'active':''}" data-source="${escapeHTML(s.name)}"><span>${escapeHTML(s.name)}</span><small>${Number(s.count||0).toLocaleString()}</small></button>`;}DOM.sourceList.innerHTML=html;}
15
  async function search(){applyState();syncUrl();DOM.statusText.textContent='搜索中...';try{const data=await api('/api/search',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({q:STATE.q,page:STATE.page,page_size:STATE.pageSize,exact:STATE.exact,source:STATE.source||null})});STATE.results=data.results||[];STATE.total=data.total||0;renderResults();renderSources();if(STATE.preview) restorePreview(false);DOM.statusText.textContent='';}catch(e){DOM.statusText.textContent='搜索失败';toast('搜索失败');}}
16
- function renderResults(){DOM.resultCount.textContent=STATE.total?`共 ${STATE.total.toLocaleString()} 条结果`:'';DOM.pageInfo.textContent=`${STATE.page} / ${Math.max(1,Math.ceil(STATE.total/STATE.pageSize))}`;DOM.prevPage.disabled=STATE.page<=1;DOM.nextPage.disabled=STATE.page>=Math.max(1,Math.ceil(STATE.total/STATE.pageSize));DOM.results.innerHTML=STATE.results.map(r=>`<article class="result-card"><div class="result-title">${escapeHTML(r.title)}</div><div class="result-meta">${escapeHTML(r.publication_name)}${r.authors&&r.authors.length?' / '+escapeHTML(r.authors.join(', ')):''}${r.publication_type?' / '+escapeHTML(r.publication_type):''}</div>${(r.snippet||[]).map(s=>`<p class="snippet">${s}</p>`).join('')}<div class="result-actions"><button data-preview="${escapeHTML(r.doc_id)}">在线预览</button><a href="/api/download/txt/${encodeURIComponent(r.doc_id)}">下载 TXT</a><a href="/api/download/source/${encodeURIComponent(r.doc_id)}">下载原文件</a></div></article>`).join('')||'<div class="loading">暂无结果</div>';}
17
- async function restorePreview(scroll=true){if(!STATE.preview){DOM.previewPanel.hidden=true;DOM.previewPanel.innerHTML='';syncUrl();return;}try{const p=await api(`/api/preview/${encodeURIComponent(STATE.preview)}`);DOM.previewPanel.innerHTML=`<div class="preview-head"><div><strong>${escapeHTML(p.title)}</strong><small>${escapeHTML(p.publication_name)}${p.authors&&p.authors.length?' / '+escapeHTML(p.authors.join(', ')):''}</small></div><button data-close-preview>×</button></div><div class="preview-body">${escapeHTML(p.content)}</div>`;DOM.previewPanel.hidden=false;if(scroll)DOM.previewPanel.scrollIntoView({behavior:'smooth',block:'start'});syncUrl();}catch(e){STATE.preview='';DOM.previewPanel.hidden=true;toast('预览失败');syncUrl();}}
18
 
19
  function bind(){DOM.searchForm.addEventListener('submit',e=>{e.preventDefault();STATE.q=DOM.searchInput.value.trim();STATE.page=1;search();});DOM.leftToggle.addEventListener('click',()=>{STATE.leftOpen=!STATE.leftOpen;applyState();syncUrl();});DOM.rightToggle.addEventListener('click',()=>{STATE.rightOpen=!STATE.rightOpen;applyState();syncUrl();});DOM.closeFilters.addEventListener('click',()=>{STATE.rightOpen=false;applyState();syncUrl();});DOM.exactToggle.addEventListener('change',()=>{STATE.exact=DOM.exactToggle.checked;STATE.page=1;search();});DOM.pageSize.addEventListener('change',()=>{STATE.pageSize=parseInt(DOM.pageSize.value,10);STATE.page=1;search();});DOM.clearFilters.addEventListener('click',()=>{STATE.source='';STATE.exact=true;STATE.page=1;search();});DOM.sourceList.addEventListener('click',e=>{const btn=e.target.closest('[data-source]');if(!btn)return;STATE.source=btn.dataset.source;STATE.page=1;search();});DOM.results.addEventListener('click',e=>{const btn=e.target.closest('[data-preview]');if(!btn)return;STATE.preview=btn.dataset.preview;restorePreview();});DOM.previewPanel.addEventListener('click',e=>{if(e.target.closest('[data-close-preview]')){STATE.preview='';restorePreview(false);}});DOM.prevPage.addEventListener('click',()=>{if(STATE.page>1){STATE.page--;search();}});DOM.nextPage.addEventListener('click',()=>{if(STATE.page<Math.ceil(STATE.total/STATE.pageSize)){STATE.page++;search();}});window.addEventListener('popstate',()=>{parseUrl();applyState();search();});}
20
- function init(){Object.assign(DOM,{leftSidebar:$('#left-sidebar'),rightSidebar:$('#right-sidebar'),sourceList:$('#source-list'),searchForm:$('#search-form'),searchInput:$('#search-input'),leftToggle:$('#left-toggle'),rightToggle:$('#right-toggle'),closeFilters:$('#close-filters'),exactToggle:$('#exact-toggle'),pageSize:$('#page-size'),clearFilters:$('#clear-filters'),resultCount:$('#result-count'),statusText:$('#status-text'),results:$('#results'),previewPanel:$('#preview-panel'),prevPage:$('#prev-page'),nextPage:$('#next-page'),pageInfo:$('#page-info'),toast:$('#toast')});parseUrl();applyState();bind();loadSources();search();}
21
  document.addEventListener('DOMContentLoaded',init);
 
3
  const DOM = {};
4
 
5
  function escapeHTML(v){return String(v ?? '').replace(/[&<>"']/g,(c)=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));}
6
+ function toast(msg){const el=DOM.toast;el.textContent=msg;el.hidden=false;el.style.display='';clearTimeout(toast.t);toast.t=setTimeout(()=>{el.hidden=true;el.style.display='none'},2400);}
7
 
8
  function parseUrl(){const p=new URLSearchParams(location.search);STATE.q=p.get('q')||'';STATE.page=Math.max(1,parseInt(p.get('page')||'1',10)||1);STATE.pageSize=parseInt(p.get('page_size')||'20',10)||20;STATE.exact=p.get('exact')!=='0';STATE.source=p.get('source')||'';STATE.leftOpen=p.get('sidebar')!=='0';STATE.rightOpen=p.get('filters')==='1';STATE.preview=p.get('preview')||'';}
9
  function syncUrl(replace=true){const p=new URLSearchParams();if(STATE.q)p.set('q',STATE.q);if(STATE.page>1)p.set('page',String(STATE.page));if(STATE.pageSize!==20)p.set('page_size',String(STATE.pageSize));if(!STATE.exact)p.set('exact','0');if(STATE.source)p.set('source',STATE.source);if(!STATE.leftOpen)p.set('sidebar','0');if(STATE.rightOpen)p.set('filters','1');if(STATE.preview)p.set('preview',STATE.preview);const url=p.toString()?`/?${p}`:'/';history[replace?'replaceState':'pushState'](null,'',url);}
10
+ function applyState(){DOM.searchInput.value=STATE.q;DOM.exactToggle.checked=STATE.exact;DOM.pageSize.value=String(STATE.pageSize);DOM.leftSidebar.classList.toggle('collapsed',!STATE.leftOpen);DOM.rightSidebar.classList.toggle('collapsed',!STATE.rightOpen);DOM.leftSidebar.classList.toggle('open',STATE.leftOpen);DOM.rightSidebar.classList.toggle('open',STATE.rightOpen);}
11
 
12
  async function api(path, opts){const r=await fetch(path,opts);if(!r.ok)throw new Error(`HTTP ${r.status}`);return r.json();}
13
  async function loadSources(){try{STATE.sources=await api('/api/sources');renderSources();}catch(e){DOM.sourceList.innerHTML='<div class="loading">来源加载失败</div>';}}
14
+ function renderSources(){let html=`<div class="repo-list-item ${!STATE.source?'active':''}" data-source=""><span class="ui-icon ui-icon-stack" aria-hidden="true"></span><span class="repo-name">全部来源</span><span class="repo-count">${STATE.total?STATE.total.toLocaleString():''}</span></div>`;for(const s of STATE.sources){html+=`<div class="repo-list-item ${STATE.source===s.name?'active':''}" data-source="${escapeHTML(s.name)}"><span class="ui-icon ui-icon-stack" aria-hidden="true"></span><span class="repo-name">${escapeHTML(s.name)}</span><span class="repo-count">${Number(s.count||0).toLocaleString()}</span></div>`;}DOM.sourceList.innerHTML=html;}
15
  async function search(){applyState();syncUrl();DOM.statusText.textContent='搜索中...';try{const data=await api('/api/search',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({q:STATE.q,page:STATE.page,page_size:STATE.pageSize,exact:STATE.exact,source:STATE.source||null})});STATE.results=data.results||[];STATE.total=data.total||0;renderResults();renderSources();if(STATE.preview) restorePreview(false);DOM.statusText.textContent='';}catch(e){DOM.statusText.textContent='搜索失败';toast('搜索失败');}}
16
+ function renderResults(){DOM.resultCount.textContent=STATE.total?`共 ${STATE.total.toLocaleString()} 条结果`:'';DOM.pageInfo.textContent=`${STATE.page} / ${Math.max(1,Math.ceil(STATE.total/STATE.pageSize))}`;DOM.prevPage.disabled=STATE.page<=1;DOM.nextPage.disabled=STATE.page>=Math.max(1,Math.ceil(STATE.total/STATE.pageSize));DOM.results.innerHTML=STATE.results.map(r=>`<div class="result-item"><div class="result-file-icon"><span class="ui-icon ui-icon-file" aria-hidden="true"></span></div><div class="result-info"><div class="result-title">${escapeHTML(r.title)}</div><div class="result-path"><span class="path-folder" data-source="${escapeHTML(r.publication_name)}">${escapeHTML(r.publication_name)}</span>${r.authors&&r.authors.length?`<span class="path-sep">/</span><span>${escapeHTML(r.authors.join(', '))}</span>`:''}${r.publication_type?`<span class="path-sep">/</span><span>${escapeHTML(r.publication_type)}</span>`:''}</div>${(r.snippet||[]).map(s=>`<div class="result-snippet">${s}</div>`).join('')}</div><div class="result-actions"><button class="result-action-btn" data-preview="${escapeHTML(r.doc_id)}">在线预览</button><a class="result-action-btn primary" href="/api/download/txt/${encodeURIComponent(r.doc_id)}">下载 TXT</a><a class="result-action-btn primary" href="/api/download/source/${encodeURIComponent(r.doc_id)}">下载原文件</a></div></div>`).join('')||'<div class="sidebar-loading">暂无结果</div>';DOM.emptyState.style.display=STATE.results.length?'none':'flex';}
17
+ async function restorePreview(scroll=true){if(!STATE.preview){DOM.previewPanel.hidden=true;DOM.previewPanel.style.display='none';DOM.previewPanel.innerHTML='';syncUrl();return;}try{const p=await api(`/api/preview/${encodeURIComponent(STATE.preview)}`);DOM.previewPanel.innerHTML=`<div class="preview-header"><div><div class="preview-title">${escapeHTML(p.title)}</div><div class="preview-path">${escapeHTML(p.publication_name)}${p.authors&&p.authors.length?' / '+escapeHTML(p.authors.join(', ')):''}</div></div><button class="icon-btn-sm" data-close-preview>×</button></div><pre class="preview-body">${escapeHTML(p.content)}</pre>`;DOM.previewPanel.hidden=false;DOM.previewPanel.style.display='block';if(scroll)DOM.previewPanel.scrollIntoView({behavior:'smooth',block:'start'});syncUrl();}catch(e){STATE.preview='';DOM.previewPanel.hidden=true;DOM.previewPanel.style.display='none';toast('预览失败');syncUrl();}}
18
 
19
  function bind(){DOM.searchForm.addEventListener('submit',e=>{e.preventDefault();STATE.q=DOM.searchInput.value.trim();STATE.page=1;search();});DOM.leftToggle.addEventListener('click',()=>{STATE.leftOpen=!STATE.leftOpen;applyState();syncUrl();});DOM.rightToggle.addEventListener('click',()=>{STATE.rightOpen=!STATE.rightOpen;applyState();syncUrl();});DOM.closeFilters.addEventListener('click',()=>{STATE.rightOpen=false;applyState();syncUrl();});DOM.exactToggle.addEventListener('change',()=>{STATE.exact=DOM.exactToggle.checked;STATE.page=1;search();});DOM.pageSize.addEventListener('change',()=>{STATE.pageSize=parseInt(DOM.pageSize.value,10);STATE.page=1;search();});DOM.clearFilters.addEventListener('click',()=>{STATE.source='';STATE.exact=true;STATE.page=1;search();});DOM.sourceList.addEventListener('click',e=>{const btn=e.target.closest('[data-source]');if(!btn)return;STATE.source=btn.dataset.source;STATE.page=1;search();});DOM.results.addEventListener('click',e=>{const btn=e.target.closest('[data-preview]');if(!btn)return;STATE.preview=btn.dataset.preview;restorePreview();});DOM.previewPanel.addEventListener('click',e=>{if(e.target.closest('[data-close-preview]')){STATE.preview='';restorePreview(false);}});DOM.prevPage.addEventListener('click',()=>{if(STATE.page>1){STATE.page--;search();}});DOM.nextPage.addEventListener('click',()=>{if(STATE.page<Math.ceil(STATE.total/STATE.pageSize)){STATE.page++;search();}});window.addEventListener('popstate',()=>{parseUrl();applyState();search();});}
20
+ function init(){Object.assign(DOM,{leftSidebar:$('#left-sidebar'),rightSidebar:$('#right-sidebar'),sourceList:$('#source-list'),searchForm:$('#search-form'),searchInput:$('#search-input'),leftToggle:$('#left-toggle'),rightToggle:$('#right-toggle'),closeFilters:$('#close-filters'),exactToggle:$('#exact-toggle'),pageSize:$('#page-size'),clearFilters:$('#clear-filters'),resultCount:$('#result-count'),statusText:$('#status-text'),results:$('#results'),emptyState:$('#empty-state'),previewPanel:$('#preview-panel'),prevPage:$('#prev-page'),nextPage:$('#next-page'),pageInfo:$('#page-info'),toast:$('#toast')});parseUrl();applyState();bind();loadSources();search();}
21
  document.addEventListener('DOMContentLoaded',init);
static/index.html CHANGED
@@ -2,51 +2,81 @@
2
  <html lang="zh-CN">
3
  <head>
4
  <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
  <title>BHA Search Lite</title>
7
  <link rel="stylesheet" href="/static/style.css">
8
  </head>
9
- <body>
10
- <div class="app-shell">
11
- <aside id="left-sidebar" class="sidebar left-sidebar">
12
- <div class="sidebar-header"><span>来源</span></div>
13
- <div id="source-list" class="sidebar-content"><div class="loading">加载中...</div></div>
14
- </aside>
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- <main class="main">
17
- <header class="topbar">
18
- <button id="left-toggle" class="icon-btn" title="展开/收起来源"></button>
19
- <form id="search-form" class="search-form">
20
- <input id="search-input" type="search" placeholder="搜索档案全文..." autocomplete="off">
21
- <button type="submit">搜索</button>
22
- </form>
23
- <button id="right-toggle" class="icon-btn" title="搜索过滤">⚙</button>
24
- </header>
25
 
26
- <div class="status-row">
27
- <span id="result-count"></span>
28
- <span id="status-text"></span>
 
 
 
 
 
29
  </div>
 
30
 
31
- <section id="preview-panel" class="preview-panel" hidden></section>
32
- <section id="results" class="results"></section>
33
- <div class="pager">
34
- <button id="prev-page">上一页</button>
35
- <span id="page-info">1 / 1</span>
36
- <button id="next-page">下一页</button>
37
  </div>
38
- </main>
39
 
40
- <aside id="right-sidebar" class="sidebar right-sidebar collapsed">
41
- <div class="sidebar-header"><span>搜索过滤</span><button id="close-filters" class="icon-btn small">×</button></div>
42
- <div class="sidebar-content">
43
- <label class="toggle-row"><span>精准搜索(支持通配符*和?)</span><input id="exact-toggle" type="checkbox" checked></label>
44
- <label class="field-row"><span>每页数量</span><select id="page-size"><option value="10">10</option><option value="20" selected>20</option><option value="50">50</option></select></label>
45
- <button id="clear-filters" class="wide-btn">清空过滤</button>
 
 
 
 
 
 
 
 
 
 
46
  </div>
47
- </aside>
48
- </div>
49
- <div id="toast" class="toast" hidden></div>
50
- <script src="/static/app.js"></script>
 
 
 
 
 
 
 
51
  </body>
52
  </html>
 
2
  <html lang="zh-CN">
3
  <head>
4
  <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
6
  <title>BHA Search Lite</title>
7
  <link rel="stylesheet" href="/static/style.css">
8
  </head>
9
+ <body class="dark force-desktop">
10
+ <header id="header">
11
+ <div class="header-left">
12
+ <button id="left-toggle" class="icon-btn" aria-label="菜单"><span class="ui-icon ui-icon-menu" aria-hidden="true"></span></button>
13
+ <a class="header-logo" href="/" title="BHA Search"><span>BHA Search</span></a>
14
+ </div>
15
+ <div class="header-center">
16
+ <form id="search-form" class="search-box">
17
+ <span class="search-icon"><span class="ui-icon ui-icon-search" aria-hidden="true"></span></span>
18
+ <input id="search-input" class="search-input" type="search" placeholder="搜索档案全文..." autocomplete="off" spellcheck="false">
19
+ <kbd class="search-kbd">/</kbd>
20
+ </form>
21
+ </div>
22
+ <div class="header-right">
23
+ <button id="right-toggle" class="icon-btn" aria-label="搜索设置"><span class="ui-icon ui-icon-sliders" aria-hidden="true"></span></button>
24
+ </div>
25
+ </header>
26
 
27
+ <div id="app-body" class="app-body">
28
+ <aside id="left-sidebar" class="sidebar left-sidebar">
29
+ <div class="sidebar-header"><span>来源</span></div>
30
+ <div id="source-list" class="sidebar-content"><div class="sidebar-loading">加载中...</div></div>
31
+ </aside>
 
 
 
 
32
 
33
+ <main id="main-content" class="main-content">
34
+ <div id="status-bar" class="status-bar">
35
+ <div class="status-left">
36
+ <span id="result-count" class="result-count"></span>
37
+ <span id="status-text" class="did-you-mean"></span>
38
+ </div>
39
+ <div class="status-right">
40
+ <button id="clear-filters" class="text-btn">清空筛选</button>
41
  </div>
42
+ </div>
43
 
44
+ <div id="results-container" class="results-container">
45
+ <div id="preview-panel" class="preview-panel" hidden></div>
46
+ <div id="results" class="results-list"></div>
47
+ <div id="empty-state" class="empty-state" style="display:none">
48
+ <div class="empty-title">没有找到结果</div>
49
+ <div class="empty-desc">试试其他关键词</div>
50
  </div>
51
+ </div>
52
 
53
+ <div id="load-info" class="load-info">
54
+ <button id="prev-page" class="text-btn-sm">上一页</button>
55
+ <span id="page-info">1 / 1</span>
56
+ <button id="next-page" class="text-btn-sm">下一页</button>
57
+ </div>
58
+ </main>
59
+
60
+ <aside id="right-sidebar" class="sidebar right-sidebar collapsed">
61
+ <div class="sidebar-header"><span>搜索过滤</span><button id="close-filters" class="icon-btn-sm" aria-label="关闭过滤">×</button></div>
62
+ <div class="sidebar-content">
63
+ <div class="filter-section" id="exact-search-section">
64
+ <label class="toggle-row">
65
+ <span class="toggle-label">精准搜索(支持通配符*和?)</span>
66
+ <input id="exact-toggle" type="checkbox" checked>
67
+ <span class="toggle-switch"></span>
68
+ </label>
69
  </div>
70
+ <div class="filter-section">
71
+ <div class="filter-section-title">每页数量</div>
72
+ <select id="page-size" class="sort-select"><option value="10">10</option><option value="20" selected>20</option><option value="50">50</option></select>
73
+ </div>
74
+ </div>
75
+ </aside>
76
+ </div>
77
+
78
+ <div id="overlay" class="overlay" style="display:none"></div>
79
+ <div id="toast" class="toast" hidden></div>
80
+ <script src="/static/app.js"></script>
81
  </body>
82
  </html>
static/style.css CHANGED
@@ -1,384 +1,2008 @@
 
 
 
 
 
1
  :root {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  color-scheme: dark;
3
- --bg: #0f1115;
4
- --panel: #151821;
5
- --panel-2: #181c25;
6
- --line: #2b303b;
7
- --line-strong: #3a4150;
8
- --text: #e6e9ef;
9
- --muted: #9aa3b2;
10
- --accent: #8fb4ff;
11
- --mark-bg: #5b4a00;
12
- --mark-text: #fff1a8;
13
- }
14
-
15
- * {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  box-sizing: border-box;
 
 
 
 
 
 
 
17
  }
18
 
19
  body {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  margin: 0;
21
- height: 100vh;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  overflow: hidden;
23
- background: var(--bg);
24
- color: var(--text);
25
- font-family: Arial, "Microsoft YaHei", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
 
28
- .app-shell {
29
- display: grid;
30
- grid-template-columns: 280px minmax(0, 1fr) 300px;
31
- height: 100vh;
 
 
32
  overflow: hidden;
 
 
 
33
  }
34
 
35
- .sidebar {
36
- background: var(--panel);
37
- overflow: auto;
38
- transition: transform 180ms ease;
 
 
 
 
 
 
 
 
 
 
39
  }
40
-
41
- .left-sidebar {
42
- border-right: 1px solid var(--line);
 
43
  }
44
 
45
- .right-sidebar {
46
- border-left: 1px solid var(--line);
 
 
 
 
 
47
  }
48
 
49
- .sidebar-header {
50
- position: sticky;
51
- top: 0;
52
- z-index: 2;
53
- display: flex;
54
- align-items: center;
55
- justify-content: space-between;
56
- min-height: 44px;
57
  padding: 0 12px;
58
- background: var(--panel);
59
- border-bottom: 1px solid var(--line);
60
- font-size: 14px;
61
- font-weight: 700;
62
  }
63
 
64
- .sidebar-content {
65
- padding: 6px;
 
 
 
66
  }
67
 
68
- .source-item {
69
  display: flex;
70
- width: 100%;
71
  align-items: center;
72
  justify-content: space-between;
73
- gap: 10px;
74
- padding: 8px 9px;
75
- border: 0;
76
- border-bottom: 1px solid rgba(255,255,255,.04);
77
- background: transparent;
78
- color: var(--text);
79
- text-align: left;
80
- cursor: pointer;
81
- }
82
-
83
- .source-item:hover,
84
- .source-item.active {
85
- background: #202633;
86
- }
87
-
88
- .source-item span {
89
- overflow: hidden;
90
- text-overflow: ellipsis;
91
- white-space: nowrap;
92
  }
93
 
94
- .source-item small {
95
- flex: none;
96
- color: var(--muted);
97
- font-size: 11px;
98
  }
99
 
100
- .main {
101
- min-width: 0;
102
- overflow: auto;
103
- background: var(--bg);
104
  }
105
 
106
- .topbar {
107
- position: sticky;
108
- top: 0;
109
- z-index: 3;
110
  display: flex;
 
111
  gap: 8px;
112
- padding: 10px 12px;
113
- background: var(--bg);
114
- border-bottom: 1px solid var(--line);
 
 
 
 
 
115
  }
116
 
117
- .search-form {
118
- display: flex;
119
- flex: 1;
120
- min-width: 0;
121
- gap: 8px;
122
  }
123
 
124
- .search-form input {
125
- flex: 1;
126
- min-width: 0;
127
- height: 36px;
128
- padding: 0 10px;
129
- border: 1px solid var(--line-strong);
130
- background: #10141b;
131
- color: var(--text);
132
- outline: none;
133
  }
134
 
135
- .search-form input:focus {
136
- border-color: var(--accent);
 
 
 
 
 
137
  }
138
 
139
- button,
140
- .icon-btn,
141
- .wide-btn,
142
- .result-actions a {
143
- min-height: 32px;
144
- border: 1px solid var(--line-strong);
145
- background: #1a202b;
146
- color: var(--text);
147
- padding: 0 10px;
148
  cursor: pointer;
149
- text-decoration: none;
150
- font: inherit;
 
 
 
 
151
  }
152
 
153
- button:hover,
154
- .result-actions a:hover {
155
- border-color: var(--accent);
156
- background: #222a38;
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
158
 
159
- button:disabled {
160
- opacity: .45;
161
- cursor: not-allowed;
 
 
 
 
 
 
 
162
  }
163
 
164
- .icon-btn {
165
- min-width: 36px;
166
- padding: 0 8px;
 
167
  }
168
 
169
- .icon-btn.small {
170
- min-width: 28px;
171
- min-height: 28px;
172
- padding: 0 6px;
173
  }
174
 
175
- .status-row {
176
- display: flex;
177
- justify-content: space-between;
178
- gap: 12px;
179
- padding: 8px 12px;
180
- color: var(--muted);
181
- font-size: 12px;
182
- border-bottom: 1px solid var(--line);
183
  }
184
 
185
- .results {
186
- padding: 8px 12px 0;
 
 
 
 
187
  }
188
 
189
- .result-card {
190
- padding: 10px 0;
191
- border-bottom: 1px solid var(--line);
192
  }
193
 
194
- .result-title {
195
- font-size: 15px;
196
- font-weight: 700;
197
- line-height: 1.45;
 
 
 
 
 
 
 
 
198
  }
199
 
200
- .result-meta {
201
- margin-top: 3px;
202
- color: var(--muted);
203
- font-size: 12px;
 
204
  }
205
 
206
- .snippet {
207
- margin: 7px 0 0;
208
- color: #c9d0da;
209
- font-size: 13px;
210
- line-height: 1.65;
211
  }
212
 
213
- .snippet em,
214
- mark {
215
- background: var(--mark-bg);
216
- color: var(--mark-text);
217
- font-style: normal;
218
- padding: 0 2px;
219
  }
220
 
221
- .result-actions {
 
222
  display: flex;
223
- flex-wrap: wrap;
224
  gap: 6px;
225
- margin-top: 9px;
226
  }
227
 
228
- .result-actions a,
229
- .result-actions button {
230
- display: inline-flex;
231
- align-items: center;
232
- min-height: 28px;
233
- padding: 0 9px;
 
 
 
 
 
 
 
 
234
  font-size: 12px;
 
 
 
 
 
 
235
  }
236
 
237
- .preview-panel {
238
- margin: 10px 12px 4px;
239
- border: 1px solid var(--line);
240
- background: var(--panel-2);
241
  }
242
 
243
- .preview-head {
 
 
 
 
244
  display: flex;
245
- justify-content: space-between;
 
 
 
 
246
  gap: 12px;
247
- padding: 10px 12px;
248
- border-bottom: 1px solid var(--line);
249
  }
250
 
251
- .preview-head strong {
252
- display: block;
253
- font-size: 14px;
 
 
254
  }
255
 
256
- .preview-head small {
257
- display: block;
258
- margin-top: 3px;
259
- color: var(--muted);
260
- font-size: 12px;
261
  }
262
 
263
- .preview-body {
264
- max-height: 42vh;
265
- margin: 0;
266
- padding: 12px;
267
- overflow: auto;
268
- white-space: pre-wrap;
269
- color: #d8dde5;
270
  font-size: 13px;
271
- line-height: 1.75;
 
272
  }
273
 
274
- .pager {
 
 
 
 
 
 
 
 
275
  display: flex;
276
  align-items: center;
277
- justify-content: center;
278
- gap: 12px;
279
- padding: 14px;
280
- color: var(--muted);
281
  }
282
 
283
- .toggle-row,
284
- .field-row {
 
 
 
285
  display: flex;
286
  align-items: center;
287
- justify-content: space-between;
288
- gap: 12px;
289
- min-height: 40px;
290
- padding: 8px 6px;
291
- border-bottom: 1px solid rgba(255,255,255,.06);
292
- font-size: 13px;
293
  }
294
 
295
- .field-row select {
296
- height: 30px;
297
- border: 1px solid var(--line-strong);
298
- background: #10141b;
299
- color: var(--text);
 
 
300
  }
301
 
302
- .wide-btn {
303
- width: 100%;
304
- margin-top: 10px;
305
  }
306
 
307
- .loading {
308
- padding: 10px;
309
- color: var(--muted);
310
- font-size: 13px;
 
 
 
 
 
311
  }
312
 
 
 
 
 
313
  .toast {
314
  position: fixed;
 
315
  left: 50%;
316
- bottom: 18px;
317
- z-index: 50;
318
  transform: translateX(-50%);
319
- border: 1px solid var(--line-strong);
320
- background: #1a202b;
321
- color: var(--text);
322
- padding: 8px 12px;
323
  font-size: 13px;
 
 
 
324
  }
325
 
326
- @media (min-width: 901px) {
327
- .left-sidebar.collapsed {
328
- display: none;
 
329
  }
330
-
331
- .right-sidebar.collapsed {
332
- display: none;
333
  }
 
334
 
335
- .app-shell:has(.left-sidebar.collapsed) {
336
- grid-template-columns: minmax(0, 1fr) 300px;
337
- }
338
 
339
- .app-shell:has(.right-sidebar.collapsed) {
340
- grid-template-columns: 280px minmax(0, 1fr);
341
- }
 
 
342
 
343
- .app-shell:has(.left-sidebar.collapsed):has(.right-sidebar.collapsed) {
344
- grid-template-columns: minmax(0, 1fr);
345
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  }
347
 
348
- @media (max-width: 900px) {
349
- .app-shell {
350
- grid-template-columns: 1fr;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  }
352
 
353
- .sidebar {
354
  position: fixed;
355
- top: 0;
 
356
  bottom: 0;
357
- width: min(84vw, 340px);
358
- z-index: 10;
 
 
359
  }
360
 
361
- .left-sidebar {
362
- left: 0;
 
 
 
 
 
 
 
363
  }
364
 
365
- .right-sidebar {
366
- right: 0;
367
  }
368
 
369
- .left-sidebar.collapsed {
370
- transform: translateX(-105%);
 
 
 
 
 
 
 
371
  }
372
 
373
- .right-sidebar.collapsed {
374
- transform: translateX(105%);
375
  }
376
 
377
- .topbar {
378
- padding: 8px;
379
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
 
381
- .search-form {
382
- gap: 6px;
 
 
 
383
  }
384
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ═══════════════════════════════════════════════════════════
2
+ VoiceOfML Search — MD3 Design System
3
+ ═══════════════════════════════════════════════════════════ */
4
+
5
+ /* ── CSS Variables (Dark Theme — Default) ────────────── */
6
  :root {
7
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
8
+ --font-mono: "SF Mono", "Cascadia Code", "Consolas", "Menlo", monospace;
9
+
10
+ --header-h: 48px;
11
+
12
+ --surface: #1a1c1e;
13
+ --surface-variant: #282a2d;
14
+ --surface-container: #1e2022;
15
+ --on-surface: #e2e2e6;
16
+ --on-surface-variant: #c4c6cf;
17
+
18
+ --primary: #8ab4f8;
19
+ --on-primary: #003258;
20
+ --primary-container: #004a7c;
21
+ --on-primary-container: #d1e4ff;
22
+
23
+ --secondary: #bcc7db;
24
+ --on-secondary: #263141;
25
+ --secondary-container: #3c4758;
26
+ --on-secondary-container: #d8e3f8;
27
+
28
+ --outline: #8d9099;
29
+ --outline-variant: #43474e;
30
+
31
+ --error: #f2b8b5;
32
+ --on-error: #601410;
33
+
34
+ --radius-sm: 6px;
35
+ --radius-md: 12px;
36
+ --radius-lg: 16px;
37
+ --radius-full: 999px;
38
+
39
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
40
+ --shadow-md: 0 2px 8px rgba(0,0,0,0.4);
41
+ --shadow-lg: 0 4px 16px rgba(0,0,0,0.5);
42
+
43
+ --transition: 150ms ease;
44
+
45
+ --sidebar-w: 280px;
46
+ --sidebar-expanded-w: min(50vw, 720px);
47
+
48
  color-scheme: dark;
49
+ }
50
+
51
+ /* ── Light Theme ─────────────────────────────────────── */
52
+ body.light {
53
+ --surface: #fdfbff;
54
+ --surface-variant: #f0eff4;
55
+ --surface-container: #f5f3f7;
56
+ --on-surface: #1a1c1e;
57
+ --on-surface-variant: #44474e;
58
+
59
+ --primary: #1a6db5;
60
+ --on-primary: #ffffff;
61
+ --primary-container: #d1e4ff;
62
+ --on-primary-container: #001d36;
63
+
64
+ --secondary: #525f70;
65
+ --on-secondary: #ffffff;
66
+ --secondary-container: #d8e3f8;
67
+ --on-secondary-container: #121c29;
68
+
69
+ --outline: #74777f;
70
+ --outline-variant: #c4c6cf;
71
+
72
+ --error: #ba1a1a;
73
+ --on-error: #ffffff;
74
+
75
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.08);
76
+ --shadow-md: 0 2px 8px rgba(0,0,0,0.12);
77
+ --shadow-lg: 0 4px 16px rgba(0,0,0,0.16);
78
+
79
+ color-scheme: light;
80
+ }
81
+
82
+ /* ── Reset & Base ────────────────────────────────────── */
83
+ *, *::before, *::after {
84
  box-sizing: border-box;
85
+ margin: 0;
86
+ padding: 0;
87
+ }
88
+
89
+ html, body {
90
+ height: 100%;
91
+ overflow: hidden;
92
  }
93
 
94
  body {
95
+ font-family: var(--font-sans);
96
+ font-size: 14px;
97
+ line-height: 1.5;
98
+ color: var(--on-surface);
99
+ background: var(--surface);
100
+ display: flex;
101
+ flex-direction: column;
102
+ -webkit-font-smoothing: antialiased;
103
+ -moz-osx-font-smoothing: grayscale;
104
+ }
105
+
106
+ :focus-visible {
107
+ outline: 2px solid var(--primary);
108
+ outline-offset: 2px;
109
+ }
110
+ .icon-btn:focus-visible,
111
+ .icon-btn-sm:focus-visible,
112
+ .text-btn:focus-visible,
113
+ .text-btn-sm:focus-visible,
114
+ .browser-item:focus-visible,
115
+ .repo-list-item:focus-visible,
116
+ .result-action-btn:focus-visible,
117
+ .random-book-btn:focus-visible,
118
+ .random-big-btn:focus-visible,
119
+ .filter-checkbox-item:focus-visible,
120
+ .back-to-global:focus-visible,
121
+ .sidebar-breadcrumb .crumb-item:focus-visible,
122
+ input:focus-visible,
123
+ select:focus-visible {
124
+ outline: none;
125
+ }
126
+
127
+ a {
128
+ color: var(--primary);
129
+ text-decoration: none;
130
+ }
131
+ a:hover, a:focus-visible {
132
+ text-decoration: underline;
133
+ }
134
+
135
+ button {
136
+ font-family: inherit;
137
+ font-size: inherit;
138
+ cursor: pointer;
139
+ border: none;
140
+ background: none;
141
+ color: inherit;
142
+ }
143
+
144
+ input, select {
145
+ font-family: inherit;
146
+ font-size: inherit;
147
+ color: var(--on-surface);
148
+ background: none;
149
+ border: none;
150
+ outline: none;
151
+ }
152
+
153
+ ul, li {
154
+ list-style: none;
155
+ }
156
+
157
+ /* ── Scrollbar ───────────────────────────────────────── */
158
+ ::-webkit-scrollbar {
159
+ width: 6px;
160
+ height: 6px;
161
+ }
162
+ ::-webkit-scrollbar-track {
163
+ background: transparent;
164
+ }
165
+ ::-webkit-scrollbar-thumb {
166
+ background: var(--outline-variant);
167
+ border-radius: 3px;
168
+ }
169
+ ::-webkit-scrollbar-thumb:hover {
170
+ background: var(--outline);
171
+ }
172
+
173
+ /* ═══════════════════════════════════════════════════════════
174
+ Header
175
+ ═══════════════════════════════════════════════════════════ */
176
+
177
+ #header {
178
+ height: var(--header-h);
179
+ display: flex;
180
+ align-items: center;
181
+ gap: 8px;
182
+ padding: 0 8px;
183
+ background: var(--surface-container);
184
+ border-bottom: 1px solid var(--outline-variant);
185
+ z-index: 100;
186
+ flex-shrink: 0;
187
+ }
188
+
189
+ .header-left {
190
+ display: flex;
191
+ align-items: center;
192
+ gap: 4px;
193
+ flex-shrink: 0;
194
+ }
195
+
196
+ .header-logo {
197
+ display: flex;
198
+ align-items: center;
199
+ gap: 6px;
200
+ color: var(--on-surface);
201
+ font-weight: 600;
202
+ font-size: 15px;
203
+ padding: 4px 8px;
204
+ border-radius: var(--radius-sm);
205
+ transition: background var(--transition);
206
+ text-decoration: none;
207
+ }
208
+ .header-logo:hover, .header-logo:focus-visible {
209
+ background: var(--surface-variant);
210
+ text-decoration: none;
211
+ }
212
+
213
+ .header-center {
214
+ flex: 1;
215
+ display: flex;
216
+ justify-content: center;
217
+ min-width: 0;
218
+ }
219
+
220
+ .search-box {
221
+ position: relative;
222
+ display: flex;
223
+ align-items: center;
224
+ width: 100%;
225
+ max-width: 680px;
226
+ height: 36px;
227
+ background: var(--surface);
228
+ border: 1px solid var(--outline-variant);
229
+ border-radius: var(--radius-full);
230
+ transition: all var(--transition);
231
+ }
232
+ .search-box:focus-within {
233
+ border-color: var(--primary);
234
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 25%, transparent);
235
+ }
236
+
237
+ .search-icon {
238
+ flex-shrink: 0;
239
+ margin-left: 12px;
240
+ color: var(--on-surface-variant);
241
+ display: flex;
242
+ align-items: center;
243
+ justify-content: center;
244
+ }
245
+
246
+ .search-input {
247
+ flex: 1;
248
+ height: 100%;
249
+ padding: 0 8px;
250
+ font-size: 14px;
251
+ background: transparent;
252
+ min-width: 0;
253
+ }
254
+ .search-input::placeholder {
255
+ color: var(--on-surface-variant);
256
+ opacity: 0.6;
257
+ }
258
+
259
+ .search-kbd {
260
+ flex-shrink: 0;
261
+ margin-right: 8px;
262
+ font-size: 10px;
263
+ padding: 2px 6px;
264
+ border-radius: 4px;
265
+ border: 1px solid var(--outline-variant);
266
+ color: var(--on-surface-variant);
267
+ opacity: 0.7;
268
+ font-family: var(--font-mono);
269
+ }
270
+
271
+ .search-history-dropdown {
272
+ position: absolute;
273
+ top: 100%;
274
+ left: 0;
275
+ right: 0;
276
+ margin-top: 4px;
277
+ background: var(--surface);
278
+ border: 1px solid var(--outline-variant);
279
+ border-radius: var(--radius-md);
280
+ box-shadow: 0 8px 24px rgba(0,0,0,0.3);
281
+ z-index: 100;
282
+ max-height: 320px;
283
+ overflow-y: auto;
284
+ padding: 4px 0;
285
+ }
286
+ .search-history-dropdown .history-item {
287
+ display: flex;
288
+ align-items: center;
289
+ gap: 8px;
290
+ padding: 8px 12px;
291
+ cursor: pointer;
292
+ font-size: 13px;
293
+ color: var(--on-surface);
294
+ transition: background 0.1s;
295
+ }
296
+ .search-history-dropdown .history-item:hover {
297
+ background: var(--surface-variant);
298
+ }
299
+ .search-history-dropdown .history-icon {
300
+ flex-shrink: 0;
301
+ color: var(--on-surface-variant);
302
+ opacity: 0.5;
303
+ }
304
+ .search-history-dropdown .history-text {
305
+ flex: 1;
306
+ overflow: hidden;
307
+ text-overflow: ellipsis;
308
+ white-space: nowrap;
309
+ }
310
+ .search-history-dropdown .history-del {
311
+ flex-shrink: 0;
312
+ width: 22px;
313
+ height: 22px;
314
+ border-radius: 999px;
315
+ color: var(--on-surface-variant);
316
+ display: inline-flex;
317
+ align-items: center;
318
+ justify-content: center;
319
+ font-size: 14px;
320
+ opacity: 0.65;
321
+ }
322
+ .search-history-dropdown .history-del:hover,
323
+ .search-history-dropdown .history-del:focus-visible {
324
+ background: var(--surface-variant);
325
+ color: var(--primary);
326
+ opacity: 1;
327
+ }
328
+ .search-history-dropdown .history-footer {
329
+ padding: 6px 12px;
330
+ border-top: 1px solid var(--outline-variant);
331
+ display: flex;
332
+ justify-content: center;
333
+ }
334
+ .search-history-dropdown .history-clear-all {
335
+ font-size: 12px;
336
+ color: var(--primary);
337
+ padding: 4px 8px;
338
+ border-radius: 6px;
339
+ }
340
+ .search-history-dropdown .history-clear-all:hover,
341
+ .search-history-dropdown .history-clear-all:focus-visible {
342
+ background: var(--surface-variant);
343
+ }
344
+ .search-history-dropdown .history-del {
345
+ flex-shrink: 0;
346
+ background: none;
347
+ border: none;
348
+ color: var(--on-surface-variant);
349
+ font-size: 16px;
350
+ cursor: pointer;
351
+ padding: 0 6px;
352
+ opacity: 0.5;
353
+ }
354
+ .search-history-dropdown .history-del:hover {
355
+ opacity: 1;
356
+ color: var(--error);
357
+ }
358
+ .search-history-dropdown .history-footer {
359
+ padding: 4px 12px;
360
+ border-top: 1px solid var(--outline-variant);
361
+ text-align: center;
362
+ }
363
+ .search-history-dropdown .history-clear-all {
364
+ background: none;
365
+ border: none;
366
+ color: var(--on-surface-variant);
367
+ font-size: 12px;
368
+ cursor: pointer;
369
+ opacity: 0.6;
370
+ }
371
+ .search-history-dropdown .history-clear-all:hover {
372
+ opacity: 1;
373
+ }
374
+
375
+ .header-right {
376
+ display: flex;
377
+ align-items: center;
378
+ gap: 2px;
379
+ flex-shrink: 0;
380
+ }
381
+
382
+ /* Icon Buttons */
383
+ .icon-btn {
384
+ width: 36px;
385
+ height: 36px;
386
+ display: flex;
387
+ align-items: center;
388
+ justify-content: center;
389
+ border-radius: var(--radius-full);
390
+ color: var(--on-surface-variant);
391
+ transition: all var(--transition);
392
+ }
393
+ .icon-btn:hover, .icon-btn:focus-visible {
394
+ background: var(--surface-variant);
395
+ color: var(--on-surface);
396
+ }
397
+
398
+ .icon-btn .ui-icon,
399
+ .search-icon .ui-icon {
400
+ width: 18px;
401
+ height: 18px;
402
+ }
403
+
404
+ .icon-btn-sm {
405
+ width: 28px;
406
+ height: 28px;
407
+ display: flex;
408
+ align-items: center;
409
+ justify-content: center;
410
+ border-radius: var(--radius-full);
411
+ color: var(--on-surface-variant);
412
+ font-size: 18px;
413
+ transition: all var(--transition);
414
+ }
415
+ .icon-btn-sm:hover, .icon-btn-sm:focus-visible {
416
+ background: var(--surface-variant);
417
+ color: var(--on-surface);
418
+ }
419
+
420
+ .text-btn {
421
+ font-size: 12px;
422
+ color: var(--primary);
423
+ padding: 4px 10px;
424
+ border-radius: var(--radius-sm);
425
+ transition: background var(--transition);
426
+ }
427
+ .text-btn:hover, .text-btn:focus-visible {
428
+ background: var(--surface-variant);
429
+ }
430
+
431
+ .text-btn-sm {
432
+ font-size: 11px;
433
+ color: var(--primary);
434
+ padding: 2px 6px;
435
+ border-radius: 4px;
436
+ transition: background var(--transition);
437
+ }
438
+ .text-btn-sm:hover, .text-btn-sm:focus-visible {
439
+ background: var(--surface-variant);
440
+ }
441
+
442
+ .preview-panel {
443
+ margin: 12px 16px 20px;
444
+ background: var(--surface-container);
445
+ border: 1px solid var(--outline-variant);
446
+ border-radius: var(--radius-md);
447
+ overflow: hidden;
448
+ }
449
+
450
+ .preview-header {
451
+ display: flex;
452
+ align-items: flex-start;
453
+ justify-content: space-between;
454
+ gap: 12px;
455
+ padding: 12px 14px;
456
+ border-bottom: 1px solid var(--outline-variant);
457
+ }
458
+
459
+ .preview-title {
460
+ font-size: 15px;
461
+ font-weight: 600;
462
+ }
463
+
464
+ .preview-path {
465
+ font-size: 12px;
466
+ opacity: 0.7;
467
+ margin-top: 4px;
468
+ word-break: break-all;
469
+ }
470
+
471
+ .preview-body {
472
  margin: 0;
473
+ padding: 14px;
474
+ max-height: 50vh;
475
+ overflow: auto;
476
+ white-space: pre-wrap;
477
+ word-break: break-word;
478
+ font-family: var(--font-sans);
479
+ line-height: 1.75;
480
+ }
481
+
482
+ .result-snippet {
483
+ margin-top: 6px;
484
+ font-size: 12px;
485
+ opacity: 0.85;
486
+ display: -webkit-box;
487
+ -webkit-line-clamp: 3;
488
+ -webkit-box-orient: vertical;
489
+ overflow: hidden;
490
+ }
491
+
492
+ /* ═══════════════════════════════════════════════════════════
493
+ Layout
494
+ ═══════════════════════════════════════════════════════════ */
495
+
496
+ .app-body {
497
+ display: flex;
498
+ flex: 1;
499
+ min-height: 0;
500
+ overflow: hidden;
501
+ }
502
+
503
+ /* ── Sidebar ─────────────────────────────────────────── */
504
+ .sidebar {
505
+ width: var(--sidebar-w);
506
+ flex-shrink: 0;
507
+ display: flex;
508
+ flex-direction: column;
509
+ border-right: 1px solid var(--outline-variant);
510
+ background: var(--surface-container);
511
+ transition: width var(--transition), opacity var(--transition);
512
+ overflow: hidden;
513
+ }
514
+ .sidebar.collapsed {
515
+ width: 0 !important;
516
+ border-right: none;
517
+ }
518
+ .right-sidebar {
519
+ border-right: none;
520
+ border-left: 1px solid var(--outline-variant);
521
+ }
522
+ .right-sidebar.collapsed {
523
+ border-left: none;
524
+ }
525
+
526
+ .sidebar-header {
527
+ display: flex;
528
+ align-items: center;
529
+ justify-content: space-between;
530
+ padding: 10px 12px;
531
+ font-size: 12px;
532
+ height: 36px;
533
+ min-height: 36px;
534
+ font-weight: 600;
535
+ color: var(--on-surface-variant);
536
+ text-transform: uppercase;
537
+ letter-spacing: 0.5px;
538
+ border-bottom: 1px solid var(--outline-variant);
539
+ flex-shrink: 0;
540
+ }
541
+
542
+ .sidebar-content {
543
+ flex: 1;
544
+ overflow-y: auto;
545
+ overflow-x: hidden;
546
+ padding: 0;
547
+ }
548
+
549
+ .sidebar-loading {
550
+ padding: 16px;
551
+ text-align: center;
552
+ font-size: 13px;
553
+ color: var(--on-surface-variant);
554
+ opacity: 0.7;
555
+ }
556
+ .sidebar.expanded-wide {
557
+ width: var(--sidebar-expanded-w) !important;
558
+ }
559
+
560
+ /* ── Breadcrumb (Left Sidebar) ───────────────────────── */
561
+ .sidebar-breadcrumb {
562
+ display: flex;
563
+ align-items: center;
564
+ gap: 2px;
565
+ padding: 6px 8px;
566
+ border-bottom: 1px solid var(--outline-variant);
567
+ flex-wrap: wrap;
568
+ font-size: 12px;
569
+ flex-shrink: 0;
570
+ min-height: 32px;
571
+ }
572
+
573
+ .sidebar-breadcrumb .crumb-item {
574
+ color: var(--primary);
575
+ cursor: pointer;
576
+ padding: 2px 4px;
577
+ border-radius: 4px;
578
+ white-space: nowrap;
579
+ transition: background var(--transition);
580
+ }
581
+ .sidebar-breadcrumb .crumb-item:hover,
582
+ .sidebar-breadcrumb .crumb-item:focus-visible {
583
+ background: var(--surface-variant);
584
+ }
585
+
586
+ .sidebar-breadcrumb .crumb-item.current {
587
+ color: var(--on-surface-variant);
588
+ cursor: default;
589
+ font-weight: 500;
590
+ }
591
+ .sidebar-breadcrumb .crumb-item.current:hover,
592
+ .sidebar-breadcrumb .crumb-item.current:focus-visible {
593
+ background: none;
594
+ }
595
+
596
+ .sidebar-breadcrumb .crumb-sep {
597
+ color: var(--outline);
598
+ flex-shrink: 0;
599
+ }
600
+
601
+ /* Back to global button */
602
+ .back-to-global {
603
+ display: flex;
604
+ align-items: center;
605
+ gap: 6px;
606
+ padding: 0 12px;
607
+ height: 40px;
608
+ font-size: 13px;
609
+ color: var(--primary);
610
+ cursor: pointer;
611
+ border-bottom: 1px solid var(--outline-variant);
612
+ transition: background var(--transition);
613
+ flex-shrink: 0;
614
+ box-sizing: border-box;
615
+ }
616
+ .back-to-global:hover, .back-to-global:focus-visible {
617
+ background: var(--surface-variant);
618
+ }
619
+
620
+ /* ── Sidebar Browser Items (Folder + File) ───────────── */
621
+ .browser-list {
622
+ flex: 1;
623
+ overflow-y: auto;
624
+ }
625
+
626
+ .browser-item {
627
+ display: flex;
628
+ align-items: center;
629
+ gap: 8px;
630
+ padding: 7px 12px;
631
+ cursor: pointer;
632
+ transition: background var(--transition);
633
+ font-size: 13px;
634
+ color: var(--on-surface);
635
+ }
636
+ .browser-item:hover, .browser-item:focus-visible {
637
+ background: var(--surface-variant);
638
+ }
639
+
640
+ /* 默认:无条纹 */
641
+ .browser-item:nth-child(even) {
642
+ background: transparent;
643
+ }
644
+
645
+ /* 展开后:显示条纹 */
646
+ .left-sidebar.expanded-wide .browser-item:nth-child(even) {
647
+ background: rgba(255, 255, 255, 0.04);
648
+ }
649
+
650
+ body.light .left-sidebar.expanded-wide .browser-item:nth-child(even) {
651
+ background: rgba(0, 0, 0, 0.04);
652
+ }
653
+
654
+ .left-sidebar.expanded-wide .browser-item:nth-child(even):hover,
655
+ .left-sidebar.expanded-wide .browser-item:nth-child(even):focus-visible {
656
+ background: var(--surface-variant);
657
+ }
658
+
659
+ .browser-item .browser-icon {
660
+ width: 18px;
661
+ height: 18px;
662
+ flex-shrink: 0;
663
+ color: var(--on-surface-variant);
664
+ }
665
+
666
+ .browser-item .browser-name {
667
+ flex: 1;
668
+ min-width: 0;
669
+ overflow: hidden;
670
+ text-overflow: ellipsis;
671
+ white-space: nowrap;
672
+ }
673
+
674
+ .browser-item .browser-count {
675
+ font-size: 10px;
676
+ color: var(--on-surface-variant);
677
+ flex-shrink: 0;
678
+ }
679
+
680
+ .browser-item .browser-size {
681
+ font-size: 10px;
682
+ color: var(--on-surface-variant);
683
+ flex-shrink: 0;
684
+ margin-right: 4px;
685
+ }
686
+
687
+ .browser-item .browser-action {
688
+ font-size: 10px;
689
+ padding: 2px 6px;
690
+ border-radius: 4px;
691
+ color: var(--primary);
692
+ border: 1px solid var(--primary);
693
+ flex-shrink: 0;
694
+ transition: all var(--transition);
695
+ white-space: nowrap;
696
+ }
697
+ .browser-item .browser-action:hover,
698
+ .browser-item .browser-action:focus-visible {
699
+ background: var(--primary-container);
700
+ color: var(--on-primary-container);
701
+ }
702
+
703
+ /* ── Main Content ────────────────────────────────────── */
704
+ .main-content {
705
+ flex: 1;
706
+ display: flex;
707
+ flex-direction: column;
708
+ min-width: 0;
709
+ position: relative;
710
+ overflow: hidden;
711
+ }
712
+
713
+ /* ═══════════════════════════════════════════════════════════
714
+ Repo List (Left Sidebar — Global Mode)
715
+ ═══════════════════════════════════════════════════════════ */
716
+
717
+ .repo-list-item {
718
+ display: flex;
719
+ align-items: center;
720
+ gap: 10px;
721
+ padding: 8px 12px;
722
+ cursor: pointer;
723
+ transition: background var(--transition);
724
+ }
725
+ .repo-list-item:hover, .repo-list-item:focus-visible {
726
+ background: var(--surface-variant);
727
+ }
728
+ .repo-list-item.active {
729
+ background: var(--secondary-container);
730
+ }
731
+
732
+ .repo-list-item .repo-icon {
733
+ width: 18px;
734
+ height: 18px;
735
+ color: var(--on-surface-variant);
736
+ flex-shrink: 0;
737
+ }
738
+
739
+ .repo-list-item .repo-name {
740
+ flex: 1;
741
+ font-size: 13px;
742
+ font-weight: 500;
743
+ color: var(--on-surface);
744
+ min-width: 0;
745
+ overflow: hidden;
746
+ text-overflow: ellipsis;
747
+ white-space: nowrap;
748
+ }
749
+
750
+ .repo-list-item .repo-count {
751
+ font-size: 11px;
752
+ color: var(--on-surface-variant);
753
+ flex-shrink: 0;
754
+ }
755
+
756
+ .ui-icon {
757
+ position: relative;
758
+ display: inline-block;
759
+ width: 16px;
760
+ height: 16px;
761
+ flex-shrink: 0;
762
+ color: var(--on-surface-variant);
763
+ }
764
+
765
+ .ui-icon-folder::before {
766
+ content: "";
767
+ position: absolute;
768
+ left: 1px;
769
+ top: 4px;
770
+ width: 14px;
771
+ height: 10px;
772
+ border: 1.5px solid currentColor;
773
+ border-radius: 3px;
774
+ }
775
+
776
+ .ui-icon-folder::after {
777
+ content: "";
778
+ position: absolute;
779
+ left: 3px;
780
+ top: 1px;
781
+ width: 6px;
782
+ height: 4px;
783
+ border: 1.5px solid currentColor;
784
+ border-bottom: none;
785
+ border-radius: 3px 3px 0 0;
786
+ }
787
+
788
+ .ui-icon-file::before {
789
+ content: "";
790
+ position: absolute;
791
+ left: 3px;
792
+ top: 1px;
793
+ width: 10px;
794
+ height: 13px;
795
+ border: 1.5px solid currentColor;
796
+ border-radius: 3px;
797
+ }
798
+
799
+ .ui-icon-file::after {
800
+ content: "";
801
+ position: absolute;
802
+ left: 6px;
803
+ top: 5px;
804
+ width: 4px;
805
+ height: 1.5px;
806
+ background: currentColor;
807
+ box-shadow: 0 3px 0 currentColor, 0 6px 0 currentColor;
808
+ }
809
+
810
+ .ui-icon-stack::before,
811
+ .ui-icon-stack::after {
812
+ content: "";
813
+ position: absolute;
814
+ width: 11px;
815
+ height: 7px;
816
+ border: 1.5px solid currentColor;
817
+ border-radius: 3px;
818
+ }
819
+
820
+ .ui-icon-stack::before {
821
+ left: 1px;
822
+ top: 6px;
823
+ }
824
+
825
+ .ui-icon-stack::after {
826
+ left: 4px;
827
+ top: 2px;
828
+ background: var(--surface-container);
829
+ }
830
+
831
+ .ui-icon-book::before,
832
+ .ui-icon-book::after {
833
+ content: "";
834
+ position: absolute;
835
+ top: 2px;
836
+ width: 6px;
837
+ height: 11px;
838
+ border: 1.5px solid currentColor;
839
+ border-radius: 2px;
840
+ }
841
+
842
+ .ui-icon-book::before {
843
+ left: 1px;
844
+ border-right-width: 0.75px;
845
+ }
846
+
847
+ .ui-icon-book::after {
848
+ right: 1px;
849
+ border-left-width: 0.75px;
850
+ }
851
+
852
+ .ui-icon-menu::before {
853
+ content: "";
854
+ position: absolute;
855
+ left: 2px;
856
+ top: 4px;
857
+ width: 12px;
858
+ height: 1.8px;
859
+ background: currentColor;
860
+ border-radius: 999px;
861
+ box-shadow: 0 4px 0 currentColor, 0 8px 0 currentColor;
862
+ }
863
+
864
+ .ui-icon-search::before {
865
+ content: "";
866
+ position: absolute;
867
+ left: 2px;
868
+ top: 2px;
869
+ width: 9px;
870
+ height: 9px;
871
+ border: 1.8px solid currentColor;
872
+ border-radius: 50%;
873
+ }
874
+
875
+ .ui-icon-search::after {
876
+ content: "";
877
+ position: absolute;
878
+ right: 2px;
879
+ bottom: 2px;
880
+ width: 6px;
881
+ height: 1.8px;
882
+ background: currentColor;
883
+ border-radius: 999px;
884
+ transform: rotate(45deg);
885
+ }
886
+
887
+ .ui-icon-sliders::before,
888
+ .ui-icon-sliders::after {
889
+ content: "";
890
+ position: absolute;
891
+ left: 2px;
892
+ width: 12px;
893
+ height: 1.8px;
894
+ background: currentColor;
895
+ border-radius: 999px;
896
+ }
897
+
898
+ .ui-icon-sliders::before {
899
+ top: 5px;
900
+ box-shadow: 0 6px 0 currentColor;
901
+ }
902
+
903
+ .ui-icon-sliders::after {
904
+ top: 5px;
905
+ left: 8px;
906
+ width: 4px;
907
+ height: 4px;
908
+ border: 1.8px solid currentColor;
909
+ border-radius: 50%;
910
+ background: var(--surface-container);
911
+ box-shadow: -6px 6px 0 -1px var(--surface-container), -6px 6px 0 0 currentColor;
912
+ }
913
+
914
+ .ui-icon-theme::before {
915
+ content: "";
916
+ position: absolute;
917
+ inset: 2px;
918
+ border-radius: 50%;
919
+ border: 1.8px solid currentColor;
920
+ }
921
+
922
+ .ui-icon-theme::after {
923
+ content: "";
924
+ position: absolute;
925
+ top: 2px;
926
+ right: 2px;
927
+ width: 7px;
928
+ height: 11px;
929
+ background: var(--surface-container);
930
+ border-radius: 999px;
931
+ }
932
+
933
+ .ui-icon-phone::before {
934
+ content: "";
935
+ position: absolute;
936
+ left: 4px;
937
+ top: 1px;
938
+ width: 8px;
939
+ height: 14px;
940
+ border: 1.8px solid currentColor;
941
+ border-radius: 3px;
942
+ }
943
+
944
+ .ui-icon-phone::after {
945
+ content: "";
946
+ position: absolute;
947
+ left: 7px;
948
+ bottom: 3px;
949
+ width: 2px;
950
+ height: 2px;
951
+ background: currentColor;
952
+ border-radius: 50%;
953
+ }
954
+
955
+ .ui-icon-desktop::before {
956
+ content: "";
957
+ position: absolute;
958
+ left: 1px;
959
+ top: 2px;
960
+ width: 14px;
961
+ height: 9px;
962
+ border: 1.8px solid currentColor;
963
+ border-radius: 3px;
964
+ }
965
+
966
+ .ui-icon-desktop::after {
967
+ content: "";
968
+ position: absolute;
969
+ left: 5px;
970
+ bottom: 1px;
971
+ width: 6px;
972
+ height: 1.8px;
973
+ background: currentColor;
974
+ border-radius: 999px;
975
+ box-shadow: 0 -3px 0 -0.3px var(--surface-container);
976
+ }
977
+
978
+ .ui-icon-devices::before {
979
+ content: "";
980
+ position: absolute;
981
+ left: 1px;
982
+ top: 3px;
983
+ width: 9px;
984
+ height: 7px;
985
+ border: 1.8px solid currentColor;
986
+ border-radius: 2px;
987
+ }
988
+
989
+ .ui-icon-devices::after {
990
+ content: "";
991
+ position: absolute;
992
+ right: 1px;
993
+ top: 1px;
994
+ width: 5px;
995
+ height: 10px;
996
+ border: 1.8px solid currentColor;
997
+ border-radius: 2px;
998
+ }
999
+
1000
+ /* ═══════════════════════════════════════════════════════════
1001
+ Status Bar
1002
+ ═══════════════════════════════════════════════════════════ */
1003
+
1004
+ .status-bar {
1005
+ display: flex;
1006
+ align-items: center;
1007
+ justify-content: space-between;
1008
+ padding: 6px 16px;
1009
+ border-bottom: 1px solid var(--outline-variant);
1010
+ flex-shrink: 0;
1011
+ min-height: 36px;
1012
+ height: 36px;
1013
+ }
1014
+
1015
+ .status-left {
1016
+ display: flex;
1017
+ align-items: center;
1018
+ gap: 6px;
1019
+ font-size: 12px;
1020
+ color: var(--on-surface-variant);
1021
+ }
1022
+
1023
+ .status-right {
1024
+ display: flex;
1025
+ align-items: center;
1026
+ gap: 8px;
1027
+ }
1028
+
1029
+ body.mobile .status-bar {
1030
+ padding: 4px 8px;
1031
+ gap: 4px;
1032
+ }
1033
+ body.mobile .status-left { gap: 4px; }
1034
+ body.mobile .status-right { gap: 4px; }
1035
+ body.mobile .multi-toggle { padding: 1px 4px; font-size: 11px; }
1036
+
1037
+ .sort-group {
1038
+ display: flex;
1039
+ align-items: center;
1040
+ gap: 4px;
1041
+ font-size: 12px;
1042
+ color: var(--on-surface-variant);
1043
+ }
1044
+
1045
+ .sort-select {
1046
+ font-size: 12px;
1047
+ padding: 2px 6px;
1048
+ border: 1px solid var(--outline-variant);
1049
+ border-radius: var(--radius-sm);
1050
+ background: var(--surface);
1051
+ color: var(--on-surface);
1052
+ cursor: pointer;
1053
+ }
1054
+
1055
+ .did-you-mean {
1056
+ font-size: 12px;
1057
+ color: var(--primary);
1058
+ cursor: pointer;
1059
+ }
1060
+ .did-you-mean:hover, .did-you-mean:focus-visible {
1061
+ text-decoration: underline;
1062
+ }
1063
+
1064
+ /* ═══════════════════════════════════════════════════════════
1065
+ Results
1066
+ ═══════════════════════════════════════════════════════════ */
1067
+
1068
+ .results-container {
1069
+ flex: 1;
1070
+ overflow-y: auto;
1071
+ overflow-x: hidden;
1072
+ }
1073
+
1074
+ /* ── Result Item ─────────────────────────────────────── */
1075
+ .result-item {
1076
+ display: flex;
1077
+ gap: 10px;
1078
+ padding: 10px 16px;
1079
+ border-bottom: 1px solid var(--outline-variant);
1080
+ transition: background var(--transition);
1081
+ }
1082
+ .result-item:nth-child(even) {
1083
+ background: var(--surface-variant);
1084
+ }
1085
+ .result-item:nth-child(even):hover,
1086
+ .result-item:nth-child(even):focus-visible {
1087
+ background: var(--secondary-container);
1088
+ }
1089
+ .result-item:hover, .result-item:focus-visible {
1090
+ background: var(--surface-variant);
1091
+ }
1092
+
1093
+ .result-file-icon {
1094
+ width: 24px;
1095
+ height: 24px;
1096
+ flex-shrink: 0;
1097
+ margin-top: 2px;
1098
+ color: var(--on-surface-variant);
1099
+ display: flex;
1100
+ align-items: center;
1101
+ justify-content: center;
1102
+ }
1103
+ .result-file-icon svg {
1104
+ width: 22px;
1105
+ height: 22px;
1106
+ }
1107
+
1108
+ .result-info {
1109
+ flex: 1;
1110
+ min-width: 0;
1111
+ }
1112
+
1113
+ .result-title {
1114
+ font-size: 14px;
1115
+ font-weight: 500;
1116
+ color: var(--on-surface);
1117
+ word-break: break-all;
1118
+ line-height: 1.4;
1119
+ }
1120
+
1121
+ .result-title mark {
1122
+ background: var(--primary-container);
1123
+ color: var(--on-primary-container);
1124
+ border-radius: 2px;
1125
+ padding: 0 1px;
1126
+ }
1127
+
1128
+ .result-path {
1129
+ font-size: 11px;
1130
+ color: var(--on-surface-variant);
1131
+ margin-top: 3px;
1132
+ font-family: var(--font-mono);
1133
+ word-break: break-all;
1134
+ display: flex;
1135
+ align-items: center;
1136
+ gap: 2px;
1137
+ flex-wrap: wrap;
1138
+ line-height: 1.5;
1139
+ }
1140
+
1141
+ .result-path .path-sep {
1142
+ color: var(--outline);
1143
+ margin: 0 2px;
1144
+ user-select: none;
1145
+ }
1146
+
1147
+ .result-path .path-folder {
1148
+ cursor: pointer;
1149
+ color: var(--secondary);
1150
+ transition: color var(--transition);
1151
+ }
1152
+ .result-path .path-folder:hover,
1153
+ .result-path .path-folder:focus-visible {
1154
+ color: var(--primary);
1155
+ text-decoration: underline;
1156
+ }
1157
+
1158
+ .result-preview {
1159
+ font-size: 12px;
1160
+ color: var(--on-surface-variant);
1161
+ margin-top: 4px;
1162
+ line-height: 1.5;
1163
+ max-height: 3em;
1164
  overflow: hidden;
1165
+ font-family: var(--font-mono);
1166
+ }
1167
+
1168
+ .result-preview mark {
1169
+ background: var(--primary-container);
1170
+ color: var(--on-primary-container);
1171
+ border-radius: 2px;
1172
+ padding: 0 1px;
1173
+ }
1174
+
1175
+ .result-meta {
1176
+ display: flex;
1177
+ align-items: center;
1178
+ gap: 8px;
1179
+ margin-top: 4px;
1180
+ flex-wrap: wrap;
1181
+ }
1182
+
1183
+ .result-repo-tag {
1184
+ font-size: 10px;
1185
+ padding: 2px 8px;
1186
+ border-radius: 10px;
1187
+ background: var(--primary-container);
1188
+ color: var(--on-primary-container);
1189
+ cursor: pointer;
1190
+ font-weight: 500;
1191
+ transition: opacity var(--transition);
1192
+ }
1193
+ .result-repo-tag:hover, .result-repo-tag:focus-visible {
1194
+ opacity: 0.8;
1195
+ }
1196
+
1197
+ .result-size {
1198
+ font-size: 11px;
1199
+ color: var(--on-surface-variant);
1200
+ }
1201
+
1202
+ .result-actions {
1203
+ display: flex;
1204
+ gap: 4px;
1205
+ flex-shrink: 0;
1206
+ flex-wrap: wrap;
1207
+ align-items: flex-start;
1208
+ }
1209
+
1210
+ .result-action-btn {
1211
+ font-size: 11px;
1212
+ padding: 3px 8px;
1213
+ border-radius: var(--radius-sm);
1214
+ color: var(--on-surface-variant);
1215
+ border: 1px solid var(--outline-variant);
1216
+ white-space: nowrap;
1217
+ transition: all var(--transition);
1218
+ }
1219
+ .result-action-btn:hover, .result-action-btn:focus-visible {
1220
+ background: var(--surface-variant);
1221
+ border-color: var(--outline);
1222
+ }
1223
+ .result-action-btn.primary {
1224
+ color: var(--primary);
1225
+ border-color: var(--primary);
1226
+ }
1227
+ .result-action-btn.primary:hover,
1228
+ .result-action-btn.primary:focus-visible {
1229
+ background: var(--primary-container);
1230
+ border-color: var(--primary);
1231
+ color: var(--on-primary-container);
1232
+ }
1233
+
1234
+ /* ═══════════════════════════════════════════════════════════
1235
+ Load Info
1236
+ ═══════════════════════════════════════════════════════════ */
1237
+
1238
+ .load-info {
1239
+ text-align: center;
1240
+ padding: 12px;
1241
+ font-size: 12px;
1242
+ color: var(--on-surface-variant);
1243
+ border-top: 1px solid var(--outline-variant);
1244
+ flex-shrink: 0;
1245
+ position: relative;
1246
+ }
1247
+
1248
+ .mobile-selected-count {
1249
+ display: none;
1250
+ color: var(--on-surface-variant);
1251
+ white-space: nowrap;
1252
+ flex-shrink: 0;
1253
+ }
1254
+
1255
+ body.mobile .load-info {
1256
+ text-align: center;
1257
+ }
1258
+
1259
+ body.mobile .mobile-selected-count {
1260
+ position: absolute;
1261
+ left: 12px;
1262
+ top: 50%;
1263
+ transform: translateY(-50%);
1264
+ }
1265
+
1266
+ /* ═══════════════════════════════════════════════════════════
1267
+ Quick Scroll Track
1268
+ ═══════════════════════════════════════════════════════════ */
1269
+
1270
+ .scroll-track {
1271
+ position: absolute;
1272
+ right: 2px;
1273
+ top: 40px;
1274
+ bottom: 50px;
1275
+ width: 8px;
1276
+ opacity: 0;
1277
+ transition: opacity 0.3s;
1278
+ pointer-events: none;
1279
+ }
1280
+ .scroll-track.visible {
1281
+ opacity: 1;
1282
+ pointer-events: auto;
1283
+ }
1284
+ .scroll-thumb {
1285
+ position: absolute;
1286
+ width: 6px;
1287
+ left: 1px;
1288
+ border-radius: 3px;
1289
+ background: var(--outline);
1290
+ cursor: pointer;
1291
+ min-height: 30px;
1292
+ transition: background var(--transition);
1293
+ }
1294
+ .scroll-thumb:hover, .scroll-thumb:focus-visible {
1295
+ background: var(--on-surface-variant);
1296
+ }
1297
+
1298
+ /* ═══════════════════════════════════════════════════════════
1299
+ Footer
1300
+ ═══════════════════════════════════════════════════════════ */
1301
+
1302
+ .footer {
1303
+ height: 44px;
1304
+ display: flex;
1305
+ align-items: center;
1306
+ justify-content: center;
1307
+ border-top: 1px solid var(--outline-variant);
1308
+ padding: 0 16px;
1309
+ flex-shrink: 0;
1310
+ position: relative;
1311
+ background: var(--surface-container);
1312
  }
1313
 
1314
+ .hitokoto {
1315
+ font-size: 11px;
1316
+ color: var(--on-surface-variant);
1317
+ opacity: 0.55;
1318
+ text-align: center;
1319
+ max-width: 70%;
1320
  overflow: hidden;
1321
+ white-space: nowrap;
1322
+ text-overflow: ellipsis;
1323
+ min-height: 18px;
1324
  }
1325
 
1326
+ .random-book-btn {
1327
+ position: absolute;
1328
+ right: 16px;
1329
+ top: 50%;
1330
+ transform: translateY(-50%);
1331
+ display: flex;
1332
+ align-items: center;
1333
+ gap: 4px;
1334
+ font-size: 11px;
1335
+ color: var(--on-surface-variant);
1336
+ padding: 4px 10px;
1337
+ border-radius: var(--radius-sm);
1338
+ border: 1px solid var(--outline-variant);
1339
+ transition: all var(--transition);
1340
  }
1341
+ .random-book-btn:hover, .random-book-btn:focus-visible {
1342
+ background: var(--surface-variant);
1343
+ border-color: var(--outline);
1344
+ color: var(--primary);
1345
  }
1346
 
1347
+ /* ═══════════════════════════════════════════════════════════
1348
+ Filter Section (Right Sidebar)
1349
+ ═══════════════════════════════════════════════════════════ */
1350
+
1351
+ .filter-section {
1352
+ border-bottom: 1px solid var(--outline-variant);
1353
+ padding: 10px 12px;
1354
  }
1355
 
1356
+ #fulltext-toggle-section,
1357
+ #search-paths-toggle-section,
1358
+ #history-toggle-section,
1359
+ #exact-search-section {
 
 
 
 
1360
  padding: 0 12px;
1361
+ height: 40px;
1362
+ box-sizing: border-box;
 
 
1363
  }
1364
 
1365
+ #fulltext-toggle-section .toggle-row,
1366
+ #search-paths-toggle-section .toggle-row,
1367
+ #history-toggle-section .toggle-row,
1368
+ #exact-search-section .toggle-row {
1369
+ height: 100%;
1370
  }
1371
 
1372
+ .filter-section-title {
1373
  display: flex;
 
1374
  align-items: center;
1375
  justify-content: space-between;
1376
+ font-size: 12px;
1377
+ font-weight: 600;
1378
+ color: var(--on-surface-variant);
1379
+ text-transform: uppercase;
1380
+ letter-spacing: 0.5px;
1381
+ margin-bottom: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
1382
  }
1383
 
1384
+ .filter-actions {
1385
+ display: flex;
1386
+ gap: 4px;
 
1387
  }
1388
 
1389
+ .filter-checkbox-list {
1390
+ max-height: 240px;
1391
+ overflow-y: auto;
 
1392
  }
1393
 
1394
+ .filter-checkbox-item {
 
 
 
1395
  display: flex;
1396
+ align-items: center;
1397
  gap: 8px;
1398
+ padding: 4px 0;
1399
+ font-size: 13px;
1400
+ color: var(--on-surface);
1401
+ cursor: pointer;
1402
+ transition: color var(--transition);
1403
+ }
1404
+ .filter-checkbox-item:hover, .filter-checkbox-item:focus-visible {
1405
+ color: var(--primary);
1406
  }
1407
 
1408
+ .filter-checkbox-item input[type="checkbox"] {
1409
+ accent-color: var(--primary);
1410
+ width: 16px;
1411
+ height: 16px;
1412
+ cursor: pointer;
1413
  }
1414
 
1415
+ .filter-checkbox-item .checkbox-count {
1416
+ font-size: 10px;
1417
+ color: var(--on-surface-variant);
1418
+ margin-left: auto;
 
 
 
 
 
1419
  }
1420
 
1421
+ /* ── Folder Tree Filter ──────────────────────────────── */
1422
+ .filter-folder-tree {
1423
+ max-height: 280px;
1424
+ overflow-y: auto;
1425
+ overflow-x: auto;
1426
+ white-space: nowrap;
1427
+ position: relative;
1428
  }
1429
 
1430
+ .filter-folder-item {
1431
+ display: flex;
1432
+ align-items: center;
1433
+ gap: 4px;
1434
+ padding: 3px 0 3px calc(var(--fdepth, 0) * 16px);
1435
+ font-size: 13px;
1436
+ color: var(--on-surface);
 
 
1437
  cursor: pointer;
1438
+ white-space: nowrap;
1439
+ flex-wrap: nowrap;
1440
+ width: max-content;
1441
+ min-width: 100%;
1442
+ transform-origin: top left;
1443
+ will-change: transform, opacity;
1444
  }
1445
 
1446
+ .filter-folder-item .tree-toggle {
1447
+ width: 16px;
1448
+ height: 16px;
1449
+ display: flex;
1450
+ align-items: center;
1451
+ justify-content: center;
1452
+ flex-shrink: 0;
1453
+ color: var(--on-surface-variant);
1454
+ font-size: 10px;
1455
+ border: 0;
1456
+ background: transparent;
1457
+ padding: 0;
1458
+ border-radius: 4px;
1459
+ cursor: pointer;
1460
+ transition: color 0.16s ease, background 0.16s ease;
1461
+ position: relative;
1462
  }
1463
 
1464
+ .filter-folder-item .tree-toggle-glyph {
1465
+ display: block;
1466
+ width: 7px;
1467
+ height: 7px;
1468
+ border-right: 1.8px solid currentColor;
1469
+ border-bottom: 1.8px solid currentColor;
1470
+ box-sizing: border-box;
1471
+ transform: rotate(-45deg);
1472
+ transform-origin: 50% 50%;
1473
+ transition: transform 220ms ease;
1474
  }
1475
 
1476
+ .filter-folder-item .tree-toggle:hover,
1477
+ .filter-folder-item .tree-toggle:focus-visible {
1478
+ color: var(--primary);
1479
+ background: color-mix(in srgb, var(--primary) 12%, transparent);
1480
  }
1481
 
1482
+ .filter-folder-item .tree-toggle.expanded .tree-toggle-glyph {
1483
+ transform: rotate(45deg);
 
 
1484
  }
1485
 
1486
+ .filter-folder-item .tree-toggle-placeholder {
1487
+ width: 16px;
1488
+ height: 16px;
1489
+ display: inline-block;
1490
+ flex-shrink: 0;
 
 
 
1491
  }
1492
 
1493
+ .filter-folder-item input[type="checkbox"] {
1494
+ accent-color: var(--primary);
1495
+ width: 14px;
1496
+ height: 14px;
1497
+ cursor: pointer;
1498
+ flex-shrink: 0;
1499
  }
1500
 
1501
+ .filter-folder-item .folder-name {
1502
+ flex: 0 0 auto;
 
1503
  }
1504
 
1505
+ .folder-self-toggle {
1506
+ border: 1px solid var(--outline-variant);
1507
+ background: transparent;
1508
+ color: var(--on-surface-variant);
1509
+ border-radius: 999px;
1510
+ font-size: 11px;
1511
+ line-height: 1;
1512
+ padding: 4px 8px;
1513
+ cursor: pointer;
1514
+ transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
1515
+ flex-shrink: 0;
1516
+ margin-left: 4px;
1517
  }
1518
 
1519
+ .filter-folder-item .folder-count {
1520
+ font-size: 10px;
1521
+ color: var(--on-surface-variant);
1522
+ margin-left: auto;
1523
+ flex-shrink: 0;
1524
  }
1525
 
1526
+ .folder-self-toggle:hover,
1527
+ .folder-self-toggle:focus-visible {
1528
+ border-color: var(--primary);
1529
+ color: var(--primary);
 
1530
  }
1531
 
1532
+ .folder-self-toggle.active {
1533
+ background: color-mix(in srgb, var(--primary) 18%, transparent);
1534
+ border-color: var(--primary);
1535
+ color: var(--primary);
 
 
1536
  }
1537
 
1538
+ /* ── Size Filter ─────────────────────────────────────── */
1539
+ .filter-size-row {
1540
  display: flex;
1541
+ align-items: center;
1542
  gap: 6px;
 
1543
  }
1544
 
1545
+ .size-input {
1546
+ flex: 1;
1547
+ font-size: 12px;
1548
+ padding: 6px 8px;
1549
+ border: 1px solid var(--outline-variant);
1550
+ border-radius: var(--radius-sm);
1551
+ background: var(--surface);
1552
+ color: var(--on-surface);
1553
+ min-width: 0;
1554
+ }
1555
+ .size-input:focus {
1556
+ border-color: var(--primary);
1557
+ }
1558
+ .size-unit {
1559
  font-size: 12px;
1560
+ padding: 6px 4px;
1561
+ border: 1px solid var(--outline-variant);
1562
+ border-radius: var(--radius-sm);
1563
+ background: var(--surface);
1564
+ color: var(--on-surface);
1565
+ cursor: pointer;
1566
  }
1567
 
1568
+ .size-sep {
1569
+ font-size: 12px;
1570
+ color: var(--on-surface-variant);
1571
+ flex-shrink: 0;
1572
  }
1573
 
1574
+ /* ═══════════════════════════════════════════════════════════
1575
+ Empty State
1576
+ ═══════════════════════════════════════════════════════════ */
1577
+
1578
+ .empty-state {
1579
  display: flex;
1580
+ flex-direction: column;
1581
+ align-items: center;
1582
+ justify-content: center;
1583
+ padding: 64px 16px;
1584
+ text-align: center;
1585
  gap: 12px;
 
 
1586
  }
1587
 
1588
+ .empty-icon {
1589
+ width: 56px;
1590
+ height: 56px;
1591
+ color: var(--outline-variant);
1592
+ opacity: 0.4;
1593
  }
1594
 
1595
+ .empty-title {
1596
+ font-size: 18px;
1597
+ font-weight: 600;
1598
+ color: var(--on-surface-variant);
 
1599
  }
1600
 
1601
+ .empty-desc {
 
 
 
 
 
 
1602
  font-size: 13px;
1603
+ color: var(--on-surface-variant);
1604
+ opacity: 0.6;
1605
  }
1606
 
1607
+ .random-big-btn {
1608
+ font-size: 14px;
1609
+ padding: 10px 24px;
1610
+ border-radius: 24px;
1611
+ background: var(--primary);
1612
+ color: var(--on-primary);
1613
+ font-weight: 500;
1614
+ cursor: pointer;
1615
+ transition: opacity var(--transition);
1616
  display: flex;
1617
  align-items: center;
1618
+ gap: 8px;
1619
+ }
1620
+ .random-big-btn:hover, .random-big-btn:focus-visible {
1621
+ opacity: 0.85;
1622
  }
1623
 
1624
+ /* ═══════════════════════════════════════════════════════════
1625
+ Loading Spinner
1626
+ ═══════════════════════════════════════════════════════════ */
1627
+
1628
+ .loading-spinner {
1629
  display: flex;
1630
  align-items: center;
1631
+ justify-content: center;
1632
+ padding: 24px;
 
 
 
 
1633
  }
1634
 
1635
+ .spinner {
1636
+ width: 28px;
1637
+ height: 28px;
1638
+ border: 3px solid var(--outline-variant);
1639
+ border-top-color: var(--primary);
1640
+ border-radius: 50%;
1641
+ animation: spin 0.6s linear infinite;
1642
  }
1643
 
1644
+ @keyframes spin {
1645
+ to { transform: rotate(360deg); }
 
1646
  }
1647
 
1648
+ /* ═══════════════════════════════════════════════════════════
1649
+ Overlay
1650
+ ═══════════════════════════════════════════════════════════ */
1651
+
1652
+ .overlay {
1653
+ position: fixed;
1654
+ inset: 0;
1655
+ background: rgba(0, 0, 0, 0.5);
1656
+ z-index: 90;
1657
  }
1658
 
1659
+ /* ═══════════════════════════════════════════════════════════
1660
+ Toast
1661
+ ═══════════════════════════════════════════════════════════ */
1662
+
1663
  .toast {
1664
  position: fixed;
1665
+ bottom: 60px;
1666
  left: 50%;
 
 
1667
  transform: translateX(-50%);
1668
+ background: var(--secondary-container);
1669
+ color: var(--on-secondary-container);
1670
+ padding: 8px 20px;
1671
+ border-radius: var(--radius-full);
1672
  font-size: 13px;
1673
+ z-index: 200;
1674
+ box-shadow: var(--shadow-md);
1675
+ animation: toast-in 0.2s ease;
1676
  }
1677
 
1678
+ @keyframes toast-in {
1679
+ from {
1680
+ opacity: 0;
1681
+ transform: translateX(-50%) translateY(10px);
1682
  }
1683
+ to {
1684
+ opacity: 1;
1685
+ transform: translateX(-50%) translateY(0);
1686
  }
1687
+ }
1688
 
1689
+ /* ═══════════════════════════════════════════════════════════
1690
+ Mobile
1691
+ ═══════════════════════════════════════════════════════════ */
1692
 
1693
+ body.mobile .sidebar:not(.open) {
1694
+ width: 0;
1695
+ border-right: none;
1696
+ border-left: none;
1697
+ }
1698
 
1699
+ body.mobile .left-sidebar.open {
1700
+ position: fixed;
1701
+ top: var(--header-h);
1702
+ left: 0;
1703
+ bottom: 0;
1704
+ z-index: 95;
1705
+ width: 85vw;
1706
+ max-width: 320px;
1707
+ box-shadow: var(--shadow-lg);
1708
+ }
1709
+
1710
+ body.mobile .right-sidebar.open {
1711
+ position: fixed;
1712
+ top: var(--header-h);
1713
+ right: 0;
1714
+ bottom: 0;
1715
+ z-index: 95;
1716
+ width: 85vw;
1717
+ max-width: 320px;
1718
+ box-shadow: var(--shadow-lg);
1719
+ }
1720
+
1721
+ body.mobile .footer {
1722
+ display: none;
1723
+ }
1724
+
1725
+ body.mobile .header-logo {
1726
+ display: none;
1727
+ }
1728
+
1729
+ body.mobile .random-book-btn {
1730
+ position: fixed;
1731
+ bottom: 20px;
1732
+ right: 20px;
1733
+ z-index: 50;
1734
+ transform: none;
1735
+ background: var(--surface-container);
1736
+ box-shadow: var(--shadow-md);
1737
+ border-radius: var(--radius-full);
1738
+ }
1739
+
1740
+ body.mobile .hitokoto {
1741
+ display: none;
1742
+ }
1743
+
1744
+ body.mobile .search-kbd {
1745
+ display: none;
1746
  }
1747
 
1748
+ body.mobile .status-bar {
1749
+ padding: 4px 8px;
1750
+ }
1751
+
1752
+ body.mobile .result-actions {
1753
+ flex-direction: column;
1754
+ align-items: flex-end;
1755
+ }
1756
+
1757
+ /* ═══════════════════════════════════════════════════════════
1758
+ Responsive (auto-detect)
1759
+ ═══════════════════════════════════════════════════════════ */
1760
+
1761
+ @media (max-width: 768px) {
1762
+ body:not(.force-desktop) .sidebar:not(.open) {
1763
+ width: 0;
1764
+ border-right: none;
1765
+ border-left: none;
1766
  }
1767
 
1768
+ body:not(.force-desktop) .left-sidebar.open {
1769
  position: fixed;
1770
+ top: var(--header-h);
1771
+ left: 0;
1772
  bottom: 0;
1773
+ z-index: 95;
1774
+ width: 85vw;
1775
+ max-width: 320px;
1776
+ box-shadow: var(--shadow-lg);
1777
  }
1778
 
1779
+ body:not(.force-desktop) .right-sidebar.open {
1780
+ position: fixed;
1781
+ top: var(--header-h);
1782
+ right: 0;
1783
+ bottom: 0;
1784
+ z-index: 95;
1785
+ width: 85vw;
1786
+ max-width: 320px;
1787
+ box-shadow: var(--shadow-lg);
1788
  }
1789
 
1790
+ body:not(.force-desktop) .footer {
1791
+ display: none;
1792
  }
1793
 
1794
+ body:not(.force-desktop) .random-book-btn {
1795
+ position: fixed;
1796
+ bottom: 20px;
1797
+ right: 20px;
1798
+ z-index: 50;
1799
+ transform: none;
1800
+ background: var(--surface-container);
1801
+ box-shadow: var(--shadow-md);
1802
+ border-radius: var(--radius-full);
1803
  }
1804
 
1805
+ body:not(.force-desktop) .hitokoto {
1806
+ display: none;
1807
  }
1808
 
1809
+ body:not(.force-desktop) .search-kbd {
1810
+ display: none;
1811
  }
1812
+ }
1813
+
1814
+ /* ═══════════════════════════════════════════════════════════
1815
+ Utility
1816
+ ═══════════════════════════════════════════════════════════ */
1817
+
1818
+ .hidden {
1819
+ display: none !important;
1820
+ }
1821
+
1822
+ .truncate {
1823
+ overflow: hidden;
1824
+ text-overflow: ellipsis;
1825
+ white-space: nowrap;
1826
+ }
1827
+
1828
+ /* ── Toggle Switch ──────────────────────────────────── */
1829
+ .toggle-row {
1830
+ display: flex;
1831
+ align-items: center;
1832
+ justify-content: space-between;
1833
+ cursor: pointer;
1834
+ }
1835
+
1836
+ .toggle-label {
1837
+ font-size: 13px;
1838
+ color: var(--on-surface);
1839
+ }
1840
+
1841
+ .toggle-row input[type="checkbox"] {
1842
+ display: none;
1843
+ }
1844
+
1845
+ .toggle-switch {
1846
+ position: relative;
1847
+ width: 40px;
1848
+ height: 22px;
1849
+ background: var(--outline-variant);
1850
+ border-radius: 11px;
1851
+ transition: background var(--transition);
1852
+ flex-shrink: 0;
1853
+ }
1854
+
1855
+ .toggle-switch::after {
1856
+ content: "";
1857
+ position: absolute;
1858
+ top: 2px;
1859
+ left: 2px;
1860
+ width: 18px;
1861
+ height: 18px;
1862
+ background: var(--surface);
1863
+ border-radius: 50%;
1864
+ transition: transform var(--transition);
1865
+ }
1866
+
1867
+ .toggle-row input:checked + .toggle-switch {
1868
+ background: var(--primary);
1869
+ }
1870
+
1871
+ .toggle-row input:checked + .toggle-switch::after {
1872
+ transform: translateX(18px);
1873
+ }
1874
+
1875
+ input[type="checkbox"]:indeterminate {
1876
+ accent-color: var(--primary);
1877
+ }
1878
+ body.theme-transitioning *,
1879
+ body.theme-transitioning *::before,
1880
+ body.theme-transitioning *::after {
1881
+ transition: background-color 0.4s ease, color 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease !important;
1882
+ }
1883
+
1884
+ .theme-ripple {
1885
+ position: fixed;
1886
+ border-radius: 50%;
1887
+ pointer-events: none;
1888
+ z-index: 9999;
1889
+ width: 0;
1890
+ height: 0;
1891
+ animation: theme-ripple-expand 0.6s ease-out forwards;
1892
+ }
1893
+
1894
+ @keyframes theme-ripple-expand {
1895
+ 0% { width: 0; height: 0; opacity: 0.6; }
1896
+ 100% { width: 300vmax; height: 300vmax; opacity: 0; transform: translate(-150vmax, -150vmax); }
1897
+ }
1898
 
1899
+ @media (prefers-reduced-motion: reduce) {
1900
+ *, *::before, *::after {
1901
+ animation-duration: 0.01ms !important;
1902
+ animation-iteration-count: 1 !important;
1903
+ transition-duration: 0.01ms !important;
1904
  }
1905
  }
1906
+
1907
+ /* ════════════════════════════════════════════════��══════════
1908
+ Multi-Select
1909
+ ═══════════════════════════════════════════════════════════ */
1910
+
1911
+ .multi-toggle {
1912
+ display: inline-flex;
1913
+ align-items: center;
1914
+ gap: 4px;
1915
+ font-size: 12px;
1916
+ color: var(--on-surface-variant);
1917
+ cursor: pointer;
1918
+ margin-left: 0;
1919
+ padding: 2px 6px;
1920
+ border-radius: var(--radius-sm);
1921
+ border: 1px solid transparent;
1922
+ }
1923
+ .multi-toggle:hover { border-color: var(--outline-variant); }
1924
+ .multi-toggle input { margin: 0; }
1925
+
1926
+ .multi-action-bar {
1927
+ display: flex;
1928
+ align-items: center;
1929
+ gap: 8px;
1930
+ padding: 0 12px;
1931
+ height: 40px;
1932
+ background: var(--surface-variant);
1933
+ border-bottom: 1px solid var(--outline-variant);
1934
+ box-sizing: border-box;
1935
+ flex-wrap: nowrap;
1936
+ }
1937
+ .multi-action-btn {
1938
+ padding: 4px 12px;
1939
+ border: 1px solid var(--outline-variant);
1940
+ border-radius: var(--radius-sm);
1941
+ background: var(--surface);
1942
+ color: var(--on-surface);
1943
+ font-size: 12px;
1944
+ cursor: pointer;
1945
+ }
1946
+ .multi-action-btn.primary {
1947
+ background: var(--primary);
1948
+ color: var(--on-primary);
1949
+ border-color: var(--primary);
1950
+ }
1951
+ .multi-action-btn:hover { opacity: 0.85; }
1952
+ .multi-selected-count {
1953
+ margin-left: auto;
1954
+ font-size: 12px;
1955
+ color: var(--on-surface-variant);
1956
+ white-space: nowrap;
1957
+ flex-shrink: 0;
1958
+ }
1959
+
1960
+ body.mobile .multi-action-btn {
1961
+ flex-shrink: 0;
1962
+ }
1963
+
1964
+ .result-checkbox {
1965
+ display: none;
1966
+ flex-shrink: 0;
1967
+ width: 18px;
1968
+ height: 18px;
1969
+ margin-right: 6px;
1970
+ align-self: center;
1971
+ accent-color: var(--primary);
1972
+ opacity: 0.85;
1973
+ }
1974
+ body.multiselect .result-checkbox { display: block; }
1975
+ body.multiselect .result-file-icon { display: none; }
1976
+ .result-item.selected {
1977
+ background: color-mix(in srgb, var(--primary) 15%, transparent) !important;
1978
+ }
1979
+
1980
+ /* BHA Search Lite overrides */
1981
+ body {
1982
+ height: 100vh;
1983
+ }
1984
+
1985
+ .load-info {
1986
+ display: flex;
1987
+ align-items: center;
1988
+ justify-content: center;
1989
+ gap: 12px;
1990
+ }
1991
+
1992
+ .load-info .text-btn-sm:disabled {
1993
+ opacity: 0.45;
1994
+ cursor: not-allowed;
1995
+ }
1996
+
1997
+ .result-snippet em {
1998
+ background: var(--primary-container);
1999
+ color: var(--on-primary-container);
2000
+ border-radius: 2px;
2001
+ padding: 0 1px;
2002
+ font-style: normal;
2003
+ }
2004
+
2005
+ .right-sidebar.collapsed,
2006
+ .left-sidebar.collapsed {
2007
+ overflow: hidden;
2008
+ }