diff --git a/panel.html b/panel.html
index ce8502c..cc408dc 100644
--- a/panel.html
+++ b/panel.html
@@ -151,6 +151,16 @@ input[type=range]::-moz-range-thumb{width:22px;height:26px;border-radius:5px;
text-transform:uppercase}
.reveal{animation:rise .7s both}
@keyframes rise{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
+.rblist{display:flex;flex-direction:column;gap:6px;margin-top:10px;max-height:300px;overflow-y:auto}
+.rbrow{display:flex;flex-direction:column;gap:3px;
+ background:#0c0b08;border:1px solid #3a352b;border-radius:8px;padding:9px 11px;cursor:pointer}
+.rbrow:active{transform:translateY(1px)}
+.rbrow .cs{font-family:'Saira Condensed',sans-serif;font-weight:800;color:var(--ink);letter-spacing:.06em}
+.rbrow .dv{color:var(--accent-hi);border:1px solid var(--accent);border-radius:10px;
+ padding:1px 6px;font-size:9px;letter-spacing:.1em;margin-left:6px}
+.rbrow .meta{color:var(--ink-dim);font-size:11px}
+.rbrow .steps{font-family:'Chivo Mono';font-size:10px;letter-spacing:.04em;color:var(--ink-dim);min-height:12px}
+.rbrow .steps .ok{color:var(--led-on)} .rbrow .steps .no{color:var(--red)}
@@ -198,6 +208,13 @@ input[type=range]::-moz-range-thumb{width:22px;height:26px;border-radius:5px;
--
+
+
Repeaters · nearest first
+
+
+
+
+
@@ -272,6 +289,41 @@ wireFader('sql','/api/sql','#sqlv');
$('#fset').onclick=()=>{const v=parseFloat($('#finp').value);if(!isNaN(v))post('/api/freq',{mhz:v});};
$('#finp').addEventListener('keydown',e=>{if(e.key==='Enter')$('#fset').click();});
+const rbq=$('#rbq'), rblist=$('#rblist');
+function stepHTML(steps){
+ if(!steps) return '';
+ return Object.entries(steps).map(([k,v])=>
+ `${k} ${v?'✓':'✗'}`).join(' ');
+}
+async function loadRepeaters(){
+ try{
+ const r=await fetch('/api/repeaters?limit=40&q='+encodeURIComponent(rbq.value||''));
+ const j=await r.json(); if(!j.ok) return;
+ rblist.innerHTML='';
+ j.repeaters.forEach(rep=>{
+ const row=document.createElement('div'); row.className='rbrow';
+ const dup=rep.duplex==='simplex'?'':(' '+rep.duplex);
+ const tone=rep.tone_hz?(' · '+rep.tone_hz):'';
+ row.innerHTML=
+ `${rep.callsign||'—'}`+
+ `${rep.mode==='DV'?'DV':''}
`+
+ `${rep.location||''} · ${(rep.output_mhz||0).toFixed(3)}${dup}${tone}
`+
+ ``;
+ row.onclick=()=>tuneRepeater(rep,row);
+ rblist.appendChild(row);
+ });
+ }catch(e){/* offline: leave list as-is */}
+}
+async function tuneRepeater(rep,row){
+ const s=row.querySelector('.steps'); s.textContent='tuning…';
+ try{
+ const r=await fetch('/api/tune-repeater',{method:'POST',
+ headers:{'Content-Type':'application/json'},body:JSON.stringify(rep)});
+ const j=await r.json(); s.innerHTML=stepHTML(j.steps); refresh();
+ }catch(e){ s.innerHTML='send failed'; }
+}
+let rbt; rbq.addEventListener('input',()=>{clearTimeout(rbt);rbt=setTimeout(loadRepeaters,250);});
+loadRepeaters();
refresh();
setInterval(refresh,900);