{# Self-contained Ctrl+K / ⌘K spotlight search for cp-shell pages (main_layout).
Mirrors the classic skeleton palette; hits the same `spotlight_search` endpoint
so results stay consistent. Include once near </body>. #}
<div id="cpSpot" class="cpspot" role="dialog" aria-hidden="true">
<div class="cpspot-box">
<div class="cpspot-input">
<i class="fa fa-search"></i>
<input type="text" id="cpSpotInput" placeholder="Search modules, clients, suppliers, documents…" autocomplete="off" spellcheck="false">
<kbd>Esc</kbd>
</div>
<div class="cpspot-results" id="cpSpotResults">
<div class="cpspot-empty">Type to search across modules, records and documents</div>
</div>
<div class="cpspot-foot"><span><kbd>↑</kbd><kbd>↓</kbd> navigate</span><span><kbd>↵</kbd> open</span><span><kbd>Ctrl</kbd>+<kbd>K</kbd></span></div>
</div>
</div>
<style>
.cpspot{display:none;position:fixed;inset:0;z-index:3000;background:rgba(20,20,40,.45);align-items:flex-start;justify-content:center;}
.cpspot.open{display:flex;}
.cpspot-box{margin-top:9vh;width:600px;max-width:92vw;background:#fff;border-radius:14px;box-shadow:0 30px 80px -20px rgba(0,0,0,.5);overflow:hidden;display:flex;flex-direction:column;max-height:70vh;}
.cpspot-input{display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1px solid #eef0f4;}
.cpspot-input i{color:#b58a63;}
.cpspot-input input{flex:1;border:0;outline:none;font-size:var(--cp-fs-lg, 16px);background:transparent;}
.cpspot-input kbd{font-size:12px;color:#9aa1b2;border:1px solid #e1e4ea;border-radius:5px;padding:1px 6px;}
.cpspot-results{flex:1;overflow:auto;padding:6px;}
.cpspot-empty,.cpspot-loading{padding:34px;text-align:center;color:#9aa1b2;font-size:14px;}
.cpspot-group{font-size:var(--cp-fs-xs, 11.5px);text-transform:uppercase;letter-spacing:.06em;color:#9aa1b2;padding:8px 12px 4px;}
.cpspot-item{display:flex;align-items:center;gap:11px;padding:9px 12px;border-radius:9px;cursor:pointer;color:#1f2430;text-decoration:none;}
.cpspot-item:hover,.cpspot-item.active{background:#f7f1ea;}
.cpspot-item .ci{width:30px;height:30px;border-radius:8px;background:#f5ece1;color:#b06a30;display:flex;align-items:center;justify-content:center;font-size:var(--cp-fs-base, 13px);flex:0 0 auto;}
.cpspot-item .cn{flex:1;font-size:14px;}
.cpspot-item .ca{font-size:12px;color:#9aa1b2;}
.cpspot-item em{background:#fbe9d2;font-style:normal;border-radius:3px;}
.cpspot-foot{display:flex;gap:16px;justify-content:center;padding:9px;border-top:1px solid #eef0f4;font-size:12px;color:#9aa1b2;}
.cpspot-foot kbd{border:1px solid #e1e4ea;border-radius:4px;padding:0 5px;}
</style>
<script>
(function(){
var SPOT_TPL='{{ path('spotlight_search', {queryStr: '__Q__'}) }}';
var TYPES={module:{i:'fa-th-large',a:'Go'},client:{i:'fa-building',a:'View'},supplier:{i:'fa-truck',a:'View'},document:{i:'fa-file',a:'Open'},employee:{i:'fa-user',a:'View'}};
var modal=document.getElementById('cpSpot'), input=document.getElementById('cpSpotInput'), res=document.getElementById('cpSpotResults');
if(!modal) return;
var idx=-1, timer, xhr;
function esc(s){return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
function open(){ modal.classList.add('open'); setTimeout(function(){input.focus();input.select();},60); }
function close(){ modal.classList.remove('open'); input.value=''; placeholder(); idx=-1; }
function placeholder(){ res.innerHTML='<div class="cpspot-empty">Type to search across modules, records and documents</div>'; idx=-1; }
function items(){ return res.querySelectorAll('.cpspot-item'); }
function move(d){ var it=items(); if(!it.length)return; if(it[idx])it[idx].classList.remove('active'); idx=Math.max(0,Math.min(it.length-1,(idx<0?(d>0?0:it.length-1):idx+d))); it[idx].classList.add('active'); it[idx].scrollIntoView({block:'nearest'}); }
function render(data,q){
idx=-1;
if(!data||!data.length){ res.innerHTML='<div class="cpspot-empty">No results for <b>'+esc(q)+'</b></div>'; return; }
var groups={},order=[];
data.forEach(function(d){ var t=d.type||'module'; if(!groups[t]){groups[t]=[];order.push(t);} groups[t].push(d); });
var html='';
order.forEach(function(t){
var meta=TYPES[t]||TYPES.module;
html+='<div class="cpspot-group">'+t+'s</div>';
groups[t].forEach(function(d){
html+='<a class="cpspot-item" href="'+(d.url||'#')+'"><span class="ci"><i class="fa '+meta.i+'"></i></span>'
+'<span class="cn">'+esc(d.name||d.title||'')+'</span><span class="ca">'+meta.a+'</span></a>';
});
});
res.innerHTML=html;
}
document.addEventListener('keydown',function(e){
if((e.ctrlKey||e.metaKey)&&(e.key==='k'||e.key==='K')){ e.preventDefault(); modal.classList.contains('open')?close():open(); return; }
if(!modal.classList.contains('open'))return;
if(e.key==='Escape'){close();} else if(e.key==='ArrowDown'){e.preventDefault();move(1);} else if(e.key==='ArrowUp'){e.preventDefault();move(-1);}
else if(e.key==='Enter'){e.preventDefault(); var it=items(); if(idx>=0&&it[idx])it[idx].click(); else if(it.length===1)it[0].click();}
});
modal.addEventListener('click',function(e){ if(e.target===modal)close(); });
input.addEventListener('input',function(){
var q=this.value.trim(); clearTimeout(timer);
if(!q){ placeholder(); return; }
res.innerHTML='<div class="cpspot-loading"><i class="fa fa-circle-o-notch fa-spin"></i></div>';
timer=setTimeout(function(){
if(xhr)xhr.abort();
xhr=$.ajax({url:SPOT_TPL.replace('__Q__',encodeURIComponent(q)),type:'GET',
success:function(r){ render((r&&r.data)||[],q); },
error:function(x){ if(x.statusText!=='abort') res.innerHTML='<div class="cpspot-empty">Search error.</div>'; }});
},220);
});
// expose for an optional topbar button
window.cpOpenSpotlight=open;
}());
</script>