/* Sponzza glass panel for LinkedIn (classic). Load order:
   config, fuse.global, matcher.global, rates.global, auth.global, db.global, content */
(function () {
  if (document.getElementById('sponzza-launcher')) return;
  console.log('[Sponzza] injected on', location.href);

  var SPONZZA_VERSION='0.3.0';
  var matcher = new SponzzaMatcher();
  var T = SPONZZA_CONFIG.THRESHOLDS;
  var state = { tab: 'sponsorship', view: 'main', analysis: {}, newEntrant: false,
                user: null, session: null, activity: null, logged: {},
                resumeText: null, resumeName: '', resumeBusy: false };

  function ic(n) {
    var a = 'fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"';
    var m = {
      search:'<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>',
      check:'<polyline points="5 13 10 18 19 6"/>', x:'<line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/>',
      shield:'<path d="M12 3l7 3v5c0 4.5-3 7.5-7 9-4-1.5-7-4.5-7-9V6z"/><polyline points="9 12 11 14 15 9"/>',
      tracker:'<rect x="3" y="4" width="18" height="16" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="9" x2="9" y2="20"/>',
      salary:'<circle cx="12" cy="12" r="9"/><path d="M9 8.5h5M8.5 12H13M9.5 8.5v6.5c0 .8.6 1.5 1.5 1.5h3"/>',
      people:'<circle cx="9" cy="8" r="3"/><path d="M3.5 19a5.5 5.5 0 0 1 11 0"/><path d="M16 6a3 3 0 0 1 0 6"/><path d="M16.5 14.2A5.5 5.5 0 0 1 20.5 19"/>',
      doc:'<path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><polyline points="14 3 14 8 19 8"/><line x1="9" y1="13" x2="15" y2="13"/>',
      info:'<circle cx="12" cy="12" r="9"/><line x1="12" y1="11" x2="12" y2="16"/><circle cx="12" cy="8" r="0.6" fill="currentColor"/>',
      user:'<circle cx="12" cy="8" r="4"/><path d="M4 20a8 8 0 0 1 16 0"/>', bolt:'<path d="M13 2 4 14h7l-1 8 9-12h-7z"/>',
      upload:'<path d="M12 16V4"/><polyline points="7 9 12 4 17 9"/><path d="M4 18v2h16v-2"/>',
      briefcase:'<rect x="3" y="7" width="18" height="13" rx="2"/><path d="M8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="3" y1="12" x2="21" y2="12"/>'
    };
    return '<svg viewBox="0 0 24 24" ' + a + '>' + m[n] + '</svg>';
  }
  function esc(s){return (s||'').replace(/[&<>"]/g,function(c){return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'})[c];});}
  function clean(t){return (t||'').split('\u00b7')[0].split('\n')[0].replace(/\s+/g,' ').trim();}
  function logoSvg(suf){var g='spzG'+suf;return '<svg viewBox="0 0 48 48" fill="none">'+
    '<rect x="2" y="2" width="44" height="44" rx="13" fill="#F4EEFC"/>'+
    '<path d="M 24 6.5 L 28.35 18.01 L 40.64 18.59 L 31.04 26.29 L 34.29 38.16 L 24 31.4 L 13.71 38.16 L 16.96 26.29 L 7.36 18.59 L 19.65 18.01 Z" fill="none" stroke="url(#'+g+')" stroke-width="2.4" stroke-linejoin="round"/>'+
    '<circle cx="24" cy="24.5" r="3.8" fill="none" stroke="url(#'+g+')" stroke-width="2"/>'+
    '<circle cx="24" cy="24.5" r="1.5" fill="url(#'+g+')"/>'+
    '<defs><linearGradient id="'+g+'" x1="6" y1="6" x2="42" y2="42" gradientUnits="userSpaceOnUse"><stop stop-color="#7C3AED"/><stop offset="1" stop-color="#C026D3"/></linearGradient></defs></svg>';}
  function wordmark(){return 'spon<span class="spz-zz">zza</span>';}
  function jobUrl(){var m=location.search.match(/currentJobId=(\d+)/);return m?('https://www.linkedin.com/jobs/view/'+m[1]+'/'):location.href.split('?')[0];}
  function fmtDate(x){if(!x)return'';var d=new Date(x);return d.toLocaleDateString('en-GB',{day:'numeric',month:'short',year:'numeric'});}
  function fileToBase64(file){return new Promise(function(res,rej){var r=new FileReader();r.onload=function(){res(String(r.result).split(',')[1]);};r.onerror=rej;r.readAsDataURL(file);});}
  function postJson(url,body,ms){
    var ctrl=(typeof AbortController!=='undefined')?new AbortController():null;
    var to=ctrl?setTimeout(function(){ctrl.abort();},ms||60000):null;
    return fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body),signal:ctrl?ctrl.signal:undefined})
      .then(function(r){return r.json();})
      .then(function(j){if(to)clearTimeout(to);return j;})
      .catch(function(e){if(to)clearTimeout(to);throw (e&&e.name==='AbortError')?new Error('Timed out — the AI took too long'):e;});
  }

  // ---- robust detection (company link is far more stable than LinkedIn class names) ----
  function ft(sel){for(var i=0;i<sel.length;i++){var e=document.querySelector(sel[i]);if(e&&e.textContent.trim())return clean(e.textContent);}return'';}
  var CO_SELS=[
    '.job-details-jobs-unified-top-card__company-name a',
    '.job-details-jobs-unified-top-card__company-name',
    '.jobs-unified-top-card__company-name a',
    '.jobs-unified-top-card__company-name',
    '.job-details-jobs-unified-top-card__primary-description-container a',
    '.jobs-unified-top-card__subtitle-primary-grouping a'
  ];
  var DESC_SELS=[
    '.jobs-description__content','.jobs-description-content__text',
    '.jobs-box__html-content','#job-details',
    '.jobs-description','.description__text',
    '[class*="jobs-description"]','[class*="description__text"]'
  ];
  var TITLE_SELS=[
    '.job-details-jobs-unified-top-card__job-title h1',
    '.job-details-jobs-unified-top-card__job-title',
    '.jobs-unified-top-card__job-title h1',
    '.jobs-unified-top-card__job-title',
    '.t-24.t-bold.inline','[class*="job-title"] h1','[class*="job-title"]','h1.t-24'
  ];
  // Parse document.title: LinkedIn sets "Job Title | Company | LinkedIn" when a job is open
  function fromPageTitle(){
    var raw=(document.title||'').replace(/\s*[-–]\s*LinkedIn\s*$/, '').replace(/\s*\|\s*LinkedIn\s*$/, '').trim();
    if(!raw) return null;
    var parts=raw.split(/\s*\|\s*/);
    if(parts.length>=2) return {title:clean(parts[0]),company:clean(parts[1])};
    if(parts.length===1&&parts[0].length>2&&parts[0].length<200) return {title:clean(parts[0]),company:''};
    return null;
  }
  function detectCompany(){
    var byClass=ft(CO_SELS);
    if(byClass) return byClass;
    var links=document.querySelectorAll('a[href*="/company/"]');
    for(var i=0;i<links.length;i++){var t=clean(links[i].textContent);if(t&&t.length>1&&!/^(follow|see all|show more|view|about|people|jobs|linkedin)/i.test(t))return t;}
    return '';
  }
  function detect(){
    var pg=fromPageTitle();
    var domTitle=ft(TITLE_SELS);
    var domCompany=detectCompany();
    // Prefer: DOM title if found, else page-title; DOM company always preferred
    var title = (domTitle && domTitle.length>3 && domTitle.length<200 && !/notification/i.test(domTitle)) ? domTitle : (pg&&pg.title)||'';
    var company = domCompany || (pg&&pg.company) || '';
    // Exclude LinkedIn chrome text that bled into h1/h2
    if(/^\d+ notification/i.test(title)) title='';
    if(/^linkedin/i.test(title)) title='';
    return {
      company: company,
      title: title,
      desc: ft(DESC_SELS).slice(0,3000)
    };
  }

  // ---- shell ----
  var launcher=document.createElement('button'); launcher.id='sponzza-launcher';
  launcher.title='Open Sponzza';
  launcher.innerHTML='<span class="spz-l-icon">'+logoSvg('L')+'</span><span class="spz-l-text">'+wordmark()+'</span>';
  var panel=document.createElement('div'); panel.id='sponzza-panel';
  panel.innerHTML=
    '<div class="spz-header">'+
      '<div class="spz-logo">'+logoSvg('H')+'</div>'+
      '<div class="spz-brand"><div class="spz-name">'+wordmark()+'</div><div class="spz-tag">Sponsor Intelligence</div></div>'+
      '<button class="spz-icon-btn" id="spz-acct" title="Account" style="display:none">'+ic('user')+'</button>'+
      '<button class="spz-icon-btn spz-close" title="Close">'+ic('x')+'</button>'+
    '</div>'+
    '<div class="spz-nav" id="spz-nav"></div>'+
    '<div class="spz-body" id="spz-body"></div>';
  function open(){panel.classList.add('spz-open');launcher.classList.add('spz-hidden');render();}
  function close(){panel.classList.remove('spz-open');launcher.classList.remove('spz-hidden');}
  launcher.addEventListener('click',open);
  (document.body||document.documentElement).appendChild(launcher);
  (document.body||document.documentElement).appendChild(panel);
  panel.querySelector('.spz-close').addEventListener('click',close);
  panel.querySelector('#spz-acct').addEventListener('click',function(){state.view=state.view==='account'?'main':'account';render();});

  var TABS=[{id:'sponsorship',label:'Sponsor',icon:'shield'},{id:'salary',label:'Salary',icon:'salary'},
            {id:'tracker',label:'Tracker',icon:'tracker'},{id:'referrals',label:'Referrals',icon:'people',neu:true},
            {id:'cvmatch',label:'CV Match',icon:'doc'}];
  var SOON={referrals:'Surface the right people to contact at this company, with AI-drafted outreach when the formal route stalls.'};

  // Auto-expand LinkedIn's collapsed job description ("...more" / "Show more")
  function expandDescription(){
    var all=document.querySelectorAll('button, a[role="button"]');
    for(var i=0;i<all.length;i++){
      var el=all[i];
      var txt=(el.textContent||'').replace(/\s+/g,' ').trim().toLowerCase();
      var cls=(el.className||'').toLowerCase();
      // Match LinkedIn's footer/show-more buttons but ignore filter, search, premium buttons
      if((txt==='show more'||txt==='see more'||/footer-button/.test(cls)||/see-more/.test(cls))
         &&!/filter|search|premium|connection|follow|save|dismiss|react/i.test(cls)
         &&!/filter|search|premium/i.test(txt)){
        try{ el.click(); }catch(e){}
        break;
      }
    }
  }

  // ---- activity logging ----

  function renderNav(){
    var nav=document.getElementById('spz-nav');
    if(!state.user||state.view!=='main'){nav.style.display='none';nav.innerHTML='';return;}
    nav.style.display='flex';
    nav.innerHTML=TABS.map(function(t){return '<button class="spz-chip'+(state.tab===t.id?' active':'')+'" data-tab="'+t.id+'">'+ic(t.icon)+'<span>'+t.label+'</span>'+(t.neu?'<span class="spz-new">NEW</span>':'')+'</button>';}).join('');
    nav.querySelectorAll('.spz-chip').forEach(function(el){el.addEventListener('click',function(){state.tab=el.getAttribute('data-tab');render();});});
  }

  // ---- views ----
  function gateHtml(){
    return '<div class="spz-gate">'+
      '<div class="spz-gate-logo">'+logoSvg('G')+'</div>'+
      '<div class="spz-wordmark">'+wordmark()+'</div>'+
      '<div class="spz-gate-tag">Sponsor Intelligence</div>'+
      '<div class="spz-jobtitle" style="margin:16px 0 26px">Sign in to check sponsorship, match SOC codes &amp; salary, and track every role you apply to.</div>'+
      '<button class="spz-btn spz-btn-google" id="spz-google"><span class="spz-g">G</span> Continue with Google</button>'+
      '<div class="spz-msg" id="spz-gmsg"></div>'+
    '</div>';
  }

  function hero(kind,icon,head,company,sub,title,note,caveat,foot,extra){
    return '<div class="spz-hero">'+
      '<div class="spz-ring '+kind+'">'+ic(icon)+'</div>'+
      '<div class="spz-headline '+kind+'">'+head+'</div>'+
      (company?'<div class="spz-company">'+company+'</div>':'')+
      (title?'<div class="spz-jobtitle">'+esc(title)+'</div>':'')+
      (sub?'<div class="spz-jobtitle" style="margin-top:10px">'+sub+'</div>':'')+
      (note||'')+
      (caveat?'<div class="spz-caveat">'+ic('info')+'<div>'+caveat+'</div></div>':'')+
      (extra||'')+
      (foot?'<div class="spz-foot">'+foot+'</div>':'')+
    '</div>';
  }
  function signalStrength(sig){
    if(!sig) return 'standard';
    if(sig.rating==='B') return 'caution';
    if(sig.rating==='P') return 'provisional';
    if(!sig.sw) return 'limited';
    if(sig.routes>=3) return 'strong';
    if(sig.routes>=2) return 'active';
    return 'standard';
  }
  function signalBadge(sig){
    var s=signalStrength(sig);
    var labels={strong:'Strong sponsor signal',active:'Active sponsor',standard:'Sponsor',limited:'Limited signal',caution:'Caution — action plan',provisional:'Provisional licence'};
    var colors={strong:'#1d8a3f',active:'#1d8a3f',standard:'#9333EA',limited:'#b06000',caution:'#c5221f',provisional:'#b06000'};
    var c=colors[s]||'#9333EA';
    return '<div class="spz-signal-badge" style="color:'+c+';border-color:'+c+'20;background:'+c+'10">'+labels[s]+'</div>';
  }
  function signalMeta(sig){
    if(!sig) return '';
    var parts=[];
    parts.push(sig.rating==='A'?'A-rated':(sig.rating==='B'?'B-rated (action plan)':'Provisional'));
    if(sig.sw) parts.push('Skilled Worker route');
    else parts.push('No Skilled Worker route');
    if(sig.routes>1) parts.push(sig.routes+(sig.routes>=9?'+':'')+' routes');
    return '<div class="spz-signal-meta">'+parts.join(' · ')+'</div>';
  }
  function signalCaveat(sig){
    if(!sig) return '';
    var s=signalStrength(sig);
    if(s==='caution') return '<div class="spz-caveat" style="background:rgba(255,59,48,0.07);color:#c5221f">'+ic('info')+'<div>This company is currently on a Home Office <b>compliance action plan (B-rated)</b>. They can still sponsor, but this is worth raising at interview stage.</div></div>';
    if(s==='provisional') return '<div class="spz-caveat">'+ic('info')+'<div>This company holds a <b>provisional licence</b>. Recently registered — no track record of sponsorship yet.</div></div>';
    if(s==='limited') return '<div class="spz-caveat">'+ic('info')+'<div>No <b>Skilled Worker route</b> found for this company. They may sponsor via other routes, but this role may not be eligible. Verify at interview stage.</div></div>';
    if(s==='strong'||s==='active') return '<div class="spz-caveat" style="background:rgba(52,199,89,0.07);color:#1d8a3f">'+ic('check')+'<div>Sponsors across <b>'+sig.routes+(sig.routes>=9?'+':'')+' routes</b> — a strong indicator they actively use their licence.</div></div>';
    return '';
  }

  function sponsorshipHtml(d){
    if(!d.company) return hero('idle','briefcase','Open a role to check','','Open any job on LinkedIn and Sponzza reads the employer automatically.');
    if(!matcher.ready) return hero('idle','search','Loading register…','','');
    var r=matcher.match(d.company), m=matcher.meta||{};
    var foot='Home Office register'+(m.uniqueKeys?' · '+m.uniqueKeys.toLocaleString()+' sponsors':'');
    if(r.status==='green'){
      var note=r.method==='exact'?'':'<div class="spz-matched">Listed as <b>'+esc(r.matchedName)+'</b></div>';
      var sig=r.signal||null;
      return hero('green','check','Likely sponsor',esc(d.company),'',d.title,
        note+signalBadge(sig)+signalMeta(sig),
        'Holds a licence on the Home Office register. A licence means they <b>can</b> sponsor — not that every role will. Confirm at interview stage.',
        foot, signalCaveat(sig));
    }
    return hero('red','x','Not on the register',esc(d.company),'',d.title,'',
      'No licence found under this name. They may still sponsor, or use a different legal entity. Verify before investing time.',foot);
  }

  function trackerToggle(jobUrl, field, current){
    var patch={}; patch[field]=!current;
    SponzzaDB.updateActivity(state.session,jobUrl,patch).then(function(){
      var row=state.activity&&state.activity.find(function(a){return a.job_url===jobUrl;});
      if(row) row[field]=!current;
      if(state.tab==='tracker'&&state.view==='main') render();
    }).catch(function(e){showToast('Could not save: '+e.message,'err');});
  }
  function tog(label,field,val,jobUrl){
    var on=!!val;
    return '<button class="spz-tog'+(on?' spz-tog-on':'')+'" data-field="'+field+'" data-url="'+esc(jobUrl)+'" data-val="'+(on?'1':'0')+'">'+label+(on?' \u2713':'')+' </button>';
  }
  function exportExcel(){
    if(typeof SponzzaXLSX==='undefined'){ showToast('Excel library not loaded','err'); return; }
    var rows=(state.activity||[]).filter(function(a){return a.action==='applied';});
    var hdr=['Company','Job Role','Applied Date','Job Link','Interview Call','Hiring Manager LinkedIn','Connection Request Sent','Direct Message Sent','Replied'];
    var data=[hdr].concat(rows.map(function(a){
      return [a.company||'',a.job_title||'',a.updated_at?new Date(a.updated_at).toLocaleDateString('en-GB'):'',
              a.job_url||'',a.interview_call?'Yes':'No',a.hiring_manager_url||'',
              a.connection_sent?'Yes':'No',a.dm_sent?'Yes':'No',a.replied?'Yes':'No'];
    }));
    var wb=SponzzaXLSX.utils.book_new();
    var ws=SponzzaXLSX.utils.aoa_to_sheet(data);
    ws['!cols']=[{wch:22},{wch:34},{wch:14},{wch:42},{wch:16},{wch:34},{wch:24},{wch:16},{wch:12}];
    var ynCols=[4,6,7,8];
    rows.forEach(function(a,ri){
      ynCols.forEach(function(ci){
        var addr=SponzzaXLSX.utils.encode_cell({r:ri+1,c:ci});
        var cell=ws[addr]; if(!cell) return;
        cell.s={fill:{fgColor:{rgb:cell.v==='Yes'?'C6EFCE':'FFC7CE'}},font:{color:{rgb:cell.v==='Yes'?'276221':'9C0006'}}};
      });
    });
    SponzzaXLSX.utils.book_append_sheet(wb,ws,'Applications');
    SponzzaXLSX.writeFile(wb,'Sponzza_Applications.xlsx',{cellStyles:true});
  }
  function trackerHtml(){
    if(state.activity===null){ loadActivity(); return '<div class="spz-hero"><div class="spz-ring idle spz-spin">'+ic('tracker')+'</div><div class="spz-headline idle">Loading\u2026</div></div>'; }
    var rows=state.activity.filter(function(a){return a.action==='applied';});
    if(!rows.length) return '<div class="spz-soon"><div class="spz-soon-ic">'+ic('tracker')+'</div><h3>No applications yet</h3><p>Click <b>Apply</b> on a sponsoring company on LinkedIn and it appears here automatically.</p></div>';
    return '<div class="spz-tlist">'+rows.map(function(a){
      var url=a.job_url||'#';
      return '<div class="spz-trow">'+
        '<div class="spz-trow-head">'+
          '<a class="spz-trow-co" href="'+esc(url)+'" target="_blank" rel="noopener">'+esc(a.company||'\u2014')+'</a>'+
          '<div class="spz-trow-role">'+esc(a.job_title||'')+'</div>'+
          '<div class="spz-trow-date">Applied '+fmtDate(a.updated_at||a.created_at)+'</div>'+
        '</div>'+
        '<input class="spz-url-input" placeholder="Hiring manager LinkedIn URL\u2026" value="'+esc(a.hiring_manager_url||'')+'" data-url="'+esc(url)+'">'+
        '<div class="spz-trow-togs">'+
          tog('Interview',   'interview_call',  a.interview_call,  url)+
          tog('Connection',  'connection_sent', a.connection_sent, url)+
          tog('DM Sent',     'dm_sent',         a.dm_sent,         url)+
          tog('Replied',     'replied',         a.replied,         url)+
        '</div>'+
      '</div>';
    }).join('')+
    '<button class="spz-btn spz-btn-excel" id="spz-export-xl">'+ic('doc')+' Download as Excel</button>'+
    '</div>';
  }
  function loadActivity(){
    SponzzaDB.listActivity(state.session).then(function(rows){state.activity=rows||[];if(state.tab==='tracker'&&state.view==='main')render();})
      .catch(function(){state.activity=[];});
  }

  function accountHtml(){
    var cvStatus = state.resumeBusy
      ? '<div class="spz-cv-loading">'+ic('bolt')+' Reading your CV…</div>'
      : (state.resumeText
          ? '<div class="spz-cv-have">'+ic('check')+' CV on file'+(state.resumeName?' · '+esc(state.resumeName):'')+'</div>'
          : '<div class="spz-jobtitle" style="text-align:left;margin-bottom:8px">No CV yet. Upload one to unlock match scores.</div>');
    return '<div class="spz-signin">'+
      '<div class="spz-ring green" style="margin:8px auto 14px">'+ic('user')+'</div>'+
      '<div class="spz-headline idle" style="font-size:18px">Signed in</div>'+
      '<div style="font-size:11px;font-weight:700;color:#9333EA;margin-top:2px">Sponzza v'+SPONZZA_VERSION+'</div>'+
      '<div class="spz-jobtitle" style="margin:6px 0 22px">'+esc(state.user.email||'')+'</div>'+
      '<div class="spz-section-label" style="text-align:left">Your CV</div>'+
      cvStatus +
      '<label class="spz-btn spz-btn-ghost" for="spz-resume" style="margin-top:8px">'+ic('upload')+' '+(state.resumeText?'Replace CV':'Upload CV (PDF)')+'</label>'+
      '<input type="file" id="spz-resume" accept="application/pdf,.pdf" style="display:none">'+
      '<button class="spz-btn spz-btn-ghost" id="spz-signout" style="margin-top:18px">Sign out</button>'+
    '</div>';
  }

  function render(){
    var acct=document.getElementById('spz-acct');
    if(acct) acct.style.display = state.user ? 'flex' : 'none';
    renderNav();
    var body=document.getElementById('spz-body'); if(!body) return;
    var html;
    if(!state.user){ html=gateHtml(); }
    else if(state.view==='account') html=accountHtml();
    else if(state.tab==='sponsorship') html=sponsorshipHtml(detect());
    else if(state.tab==='salary') html=salaryTabHtml(detect());
    else if(state.tab==='cvmatch') html=cvMatchTabHtml(detect());
    else if(state.tab==='tracker') html=trackerHtml();
    else html='<div class="spz-soon"><div class="spz-soon-ic">'+ic(TABS.filter(function(t){return t.id===state.tab;})[0].icon)+'</div><h3>'+TABS.filter(function(t){return t.id===state.tab;})[0].label+'</h3><p>'+SOON[state.tab]+'</p><span class="spz-pill">Coming soon</span></div>';
    body.classList.add('spz-fading');
    clearTimeout(render._t);
    render._t=setTimeout(function(){body.innerHTML=html;body.classList.remove('spz-fading');wire();},120);
  }

  // ---- salary (Worker) ----
  function A(title){ if(!state.analysis[title]) state.analysis[title]={}; return state.analysis[title]; }

  function ensureSoc(d){
    var a=A(d.title); if(a.socLoading||a.soc) return;
    a.socLoading=true; a.offered=SponzzaRates.detectSalary(d.title+' '+d.desc);
    postJson(SPONZZA_CONFIG.WORKER_URL+'/soc-match',{title:d.title,description:d.desc})
      .then(function(res){a.socLoading=false;a.soc=res;rerenderAnalysis(d.title);})
      .catch(function(e){a.socLoading=false;a.socErr=true;a.socErrMsg=e.message;rerenderAnalysis(d.title);});
  }
  function ensureCv(d,socTitle){
    var a=A(d.title); if(!state.resumeText||a.cvLoading||a.cv) return;
    a.cvLoading=true;
    postJson(SPONZZA_CONFIG.WORKER_URL+'/cv-match',{resume_text:state.resumeText,job_title:d.title,job_description:d.desc,soc_title:socTitle||''})
      .then(function(res){a.cvLoading=false;a.cv=res;rerenderAnalysis(d.title);})
      .catch(function(e){a.cvLoading=false;a.cvErr=true;a.cvErrMsg=e.message;rerenderAnalysis(d.title);});
  }
  function rerenderAnalysis(title){
    if(panel.classList.contains('spz-open')&&state.view==='main'&&(state.tab==='salary'||state.tab==='cvmatch')&&detect().title===title) render();
  }

  function socBlock(d){
    var a=A(d.title), g=SponzzaRates.gbp;
    if(a.socLoading) return '<div class="spz-loading-block">'+ic('bolt')+'<span>Analysing role…</span></div>';
    if(a.socErr) return '<div class="spz-caveat" style="background:rgba(255,59,48,0.08);color:#c5221f">'+ic('info')+'<div>'+esc(a.socErrMsg||'Couldn’t reach the analysis service.')+'</div></div>';
    var res=a.soc||{};
    if(res.error) return '<div class="spz-caveat" style="background:rgba(255,59,48,0.08);color:#c5221f">'+ic('info')+'<div>'+esc(res.error)+'</div></div>';
    if(!res.soc) return '<div class="spz-caveat">'+ic('info')+'<div>No confident SOC match — '+esc(res.reason||'this role may not map to an eligible occupation.')+'</div></div>';
    var as=SponzzaRates.assess(String(res.soc),a.offered,T);
    if(!as) return '<div class="spz-caveat">'+ic('info')+'<div>SOC '+esc(res.soc)+' isn’t in the rates table.</div></div>';
    var req=state.newEntrant?as.newEntrantRequired:as.stdRequired, meets=state.newEntrant?as.meetsNE:as.meetsStd;
    var verdict=as.offered?(meets?'<div class="spz-caveat" style="background:rgba(52,199,89,0.1);color:#1d8a3f">'+ic('check')+'<div>Listed pay '+g(as.offered)+' meets the '+(state.newEntrant?'new-entrant':'standard')+' requirement of '+g(req)+'.</div></div>':'<div class="spz-caveat" style="background:rgba(255,59,48,0.08);color:#c5221f">'+ic('info')+'<div>Listed pay '+g(as.offered)+' is below the '+(state.newEntrant?'new-entrant':'standard')+' requirement of '+g(req)+'.</div></div>'):'<div class="spz-caveat">'+ic('info')+'<div>No salary detected. Compare your offer against '+g(req)+'.</div></div>';
    ensureCv(d,as.title);
    return '<div class="spz-soc-code">SOC '+esc(as.code)+'</div><div class="spz-soc-title">'+esc(as.title)+'</div>'+
      '<div class="spz-rate-grid">'+rateRow('Going rate (standard)',g(as.goingStd)+' / yr',as.hourlyStd?('£'+as.hourlyStd+'/hr'):'')+rateRow('General threshold',g(state.newEntrant?T.generalNewEntrant:T.generalStandard)+' / yr','')+rateRow('Required (the higher)',g(req)+' / yr','',true)+(as.offered?rateRow('Listed on this role',g(as.offered)+' / yr',''):'')+'</div>'+
      '<label class="spz-toggle"><input type="checkbox" id="spz-ne"'+(state.newEntrant?' checked':'')+'> I’m a new entrant (under 26, or switching from a Graduate/Student visa)</label>'+verdict;
  }

  function salaryTabHtml(d){
    if(!d.title) return hero('idle','salary','Open a role','','','','','Open a job on LinkedIn to see its SOC code and salary threshold.','');
    ensureSoc(d);
    var a=A(d.title);
    if(!a.soc&&!a.socLoading&&!a.socErr) return hero('idle','salary','Open a role','');
    return '<div class="spz-tab-content"><div class="spz-block-label">Occupation &amp; salary</div>'+socBlock(d)+'<div class="spz-foot">Indicative — verify against gov.uk Appendix Skilled Occupations.</div></div>';
  }

  function cvMatchTabHtml(d){
    if(!d.title) return hero('idle','doc','Open a role','','','','','Open a job on LinkedIn to score your CV against it.','');
    ensureSoc(d);
    var a=A(d.title);
    if(a.soc&&a.soc.soc){ var as=SponzzaRates.assess(String(a.soc.soc),a.offered,T); if(as) ensureCv(d,as.title); }
    return '<div class="spz-tab-content"><div class="spz-block-label">CV match</div>'+cvBlock(d)+'</div>';
  }
  function cvBlock(d){
    if(!state.resumeText) return '<div class="spz-cv-upsell"><div>Upload your CV to see how well you match this role.</div><button class="spz-btn spz-btn-primary" id="spz-go-upload" style="margin-top:10px">'+ic('upload')+' Upload CV</button></div>';
    var a=A(d.title);
    if(a.cvLoading) return '<div class="spz-cv-loading">'+ic('bolt')+' Scoring your CV against this role…</div>';
    if(a.cvErr||!a.cv) return '<div class="spz-caveat">'+ic('info')+'<div>'+esc((a.cvErrMsg)||'Couldn\u2019t score your CV for this role yet.')+'</div></div>';
    var c=a.cv; if(c.error) return '<div class="spz-caveat">'+ic('info')+'<div>'+esc(c.error)+'</div></div>';
    var sc=Math.max(0,Math.min(100,parseInt(c.score,10)||0));
    var band=sc>=75?'green':(sc>=50?'amber':'red');
    var lists='';
    if(c.strengths&&c.strengths.length) lists+='<div class="spz-sg"><div class="spz-sg-h spz-sg-pos">Strengths</div>'+c.strengths.map(function(x){return '<div class="spz-sg-row">+ '+esc(x)+'</div>';}).join('')+'</div>';
    if(c.gaps&&c.gaps.length) lists+='<div class="spz-sg"><div class="spz-sg-h spz-sg-neg">Gaps</div>'+c.gaps.map(function(x){return '<div class="spz-sg-row">– '+esc(x)+'</div>';}).join('')+'</div>';
    return '<div class="spz-score-wrap"><div class="spz-score spz-score-'+band+'">'+sc+'<span>/100</span></div>'+
      '<div class="spz-score-verdict spz-score-'+band+'">'+esc(c.verdict||'')+'</div></div>'+lists;
  }
  function rateRow(k,v,sub,strong){return '<div class="spz-rate-row'+(strong?' strong':'')+'"><span class="k">'+k+'</span><span class="v">'+v+(sub?' <em>'+sub+'</em>':'')+'</span></div>';}

  function wire(){
    var d=detect();
    var goog=document.getElementById('spz-google');
    if(goog) goog.addEventListener('click',function(){
      var msg=document.getElementById('spz-gmsg'); if(msg)msg.textContent='Opening Google…';
      SponzzaAuth.signInGoogle().then(function(sess){state.session=sess;state.user=sess.user;state.view='main';render();SponzzaDB.loadResumeText(sess).then(function(t){if(t){state.resumeText=t;state.resumeName='cv.pdf';}else{state.view='account';}render();});})
        .catch(function(e){if(msg)msg.textContent=e.message;});
    });
    var an=document.getElementById('spz-analyse'); if(an) an.addEventListener('click',function(){render();});
    var go=document.getElementById('spz-go-upload'); if(go) go.addEventListener('click',function(){state.view='account';render();});
    var ne=document.getElementById('spz-ne'); if(ne) ne.addEventListener('change',function(){state.newEntrant=ne.checked;render();});
    var rf=document.getElementById('spz-resume'); if(rf) rf.addEventListener('change',function(){
      var f=rf.files&&rf.files[0]; if(!f) return;
      if(f.type&&f.type.indexOf('pdf')<0){ showToast('Please upload a PDF','err'); return; }
      state.resumeBusy=true; state.resumeName=f.name; render(); showToast('Reading your CV\u2026','ok');
      fileToBase64(f).then(function(b64){
        return postJson(SPONZZA_CONFIG.WORKER_URL+'/resume-extract',{pdf_base64:b64,media_type:f.type||'application/pdf'},90000);
      }).then(function(j){
        if(!j.text) throw new Error(j.error||'Could not read this PDF');
        return SponzzaDB.saveResume(state.session,f,j.text).then(function(){ state.resumeText=j.text; });
      }).then(function(){
        state.resumeBusy=false; state.analysis={}; state.view='main'; state.tab='cvmatch'; render(); showToast('CV saved \u2713','ok');
      }).catch(function(e){ state.resumeBusy=false; render(); showToast('CV upload failed: '+e.message,'err'); });
    });
    var so=document.getElementById('spz-signout'); if(so) so.addEventListener('click',function(){SponzzaAuth.signOut().then(function(){state.user=null;state.session=null;state.view='main';state.activity=null;state.resumeText=null;render();});});
    var xl=document.getElementById('spz-export-xl'); if(xl) xl.addEventListener('click',exportExcel);
    document.querySelectorAll('.spz-tog').forEach(function(btn){
      btn.addEventListener('click',function(){
        trackerToggle(btn.getAttribute('data-url'), btn.getAttribute('data-field'), btn.getAttribute('data-val')==='1');
      });
    });
    document.querySelectorAll('.spz-url-input').forEach(function(inp){
      var timer;
      inp.addEventListener('input',function(){
        clearTimeout(timer);
        var url=inp.getAttribute('data-url'), val=inp.value;
        timer=setTimeout(function(){
          if(!state.session) return;
          var patch={hiring_manager_url:val||null};
          SponzzaDB.updateActivity(state.session,url,patch).then(function(){
            var row=state.activity&&state.activity.find(function(a){return a.job_url===url;});
            if(row) row.hiring_manager_url=val;
          }).catch(function(){});
        },800);
      });
    });
    var tb=document.getElementById('spz-test'); if(tb) tb.addEventListener('click',function(){
      var out=document.getElementById('spz-diag'); if(!out) return;
      var d=detect();
      var sLines='--- detect() ---\ncompany: '+JSON.stringify(d.company)+'\ntitle:   '+JSON.stringify(d.title)+'\ndesc:    '+d.desc.length+' chars\n\n--- selector scan ---\n';
      var pg2=fromPageTitle();
      sLines+='--- document.title ---\n'+document.title+'\nparsed: '+JSON.stringify(pg2)+'\n\n--- DOM selectors ---\n';
      TITLE_SELS.forEach(function(sel){var e=document.querySelector(sel);sLines+=(e?'[HIT] ':'[miss]')+' '+sel+(e?' => '+clean(e.textContent).slice(0,40):'')+' \n';});
      out.textContent=sLines+'\nTesting Worker…';
      postJson(SPONZZA_CONFIG.WORKER_URL+'/soc-match',{title:'Software Engineer',description:'Design and build web applications using JavaScript and Python.'},30000)
        .then(function(j){ out.textContent=sLines+'\nSOC test OK:\n'+JSON.stringify(j,null,1); })
        .catch(function(e){ out.textContent=sLines+'\nSOC test FAILED:\n'+e.message; });
    });
  }

  // react to job changes
  var last='',sched=false;
  new MutationObserver(function(){if(sched)return;sched=true;requestAnimationFrame(function(){sched=false;
    if(!document.getElementById('sponzza-launcher')) (document.body||document.documentElement).appendChild(launcher);
    if(!document.getElementById('sponzza-panel')) (document.body||document.documentElement).appendChild(panel);
    if(!panel.classList.contains('spz-open')||!state.user||state.view!=='main'||(state.tab!=='sponsorship'&&state.tab!=='salary'&&state.tab!=='cvmatch'))return;
    var c=detectCompany(); if(c!==last){last=c;render();setTimeout(expandDescription,900);}
  });}).observe(document.documentElement,{childList:true,subtree:true});

  // toast (shows even when the panel is closed)
  function showToast(msg, kind) {
    var t = document.getElementById('spz-toast');
    if (!t) { t = document.createElement('div'); t.id = 'spz-toast'; (document.body || document.documentElement).appendChild(t); }
    t.textContent = msg;
    t.className = 'spz-toast-' + (kind || 'ok') + ' spz-toast-show';
    clearTimeout(showToast._t);
    showToast._t = setTimeout(function () { t.className = ''; }, 3200);
  }

  function isApplyClick(target) {
    var el = target.closest && target.closest('button, a, [role="button"]');
    if (!el) return false;
    if (el.closest && el.closest('[class*="filter"], .search-reusables__filter-list')) return false; // ignore filter chips
    if (el.matches && el.matches('.jobs-apply-button, [class*="jobs-apply-button"], [class*="apply-button"]')) return true;
    if (el.closest && el.closest('.jobs-apply-button, [class*="jobs-apply-button"], [class*="apply-button"]')) return true;
    var al = (el.getAttribute && (el.getAttribute('aria-label') || '')) || '';
    if (/apply to /i.test(al)) return true;
    var txt = (el.textContent || '').replace(/\s+/g, ' ').trim();
    if (/^(easy apply|apply)$/i.test(txt)) return true;
    return false;
  }

  // Track ONLY when Apply / Easy Apply is clicked, and ONLY for sponsors.
  document.addEventListener('click', function (e) {
    if (!isApplyClick(e.target)) return;
    if (!state.session) { showToast('Sign in to track applications', 'err'); return; }
    if (!matcher.ready) return;
    var d = detect();
    if (!d.company) { showToast('Couldn\u2019t read the company on this page', 'err'); return; }
    if (matcher.match(d.company).status !== 'green') return; // sponsors only — silent
    showToast('Saving ' + d.company + ' to tracker\u2026', 'ok');
    SponzzaDB.logActivity(state.session, {
      company: d.company, job_title: d.title, job_url: jobUrl(),
      sponsor_status: 'sponsor', action: 'applied'
    }).then(function () {
      state.activity = null;
      showToast('Saved ' + d.company + ' to tracker \u2713', 'ok');
      if (panel.classList.contains('spz-open') && state.view === 'main' && state.tab === 'tracker') render();
    }).catch(function (err) { showToast('Couldn\u2019t save: ' + err.message, 'err'); });
  }, true);

  // boot
  SponzzaRates.load(chrome.runtime.getURL('data/going_rates.json')).catch(function(){});
  SponzzaAuth.getSession().then(function(s){
    if(s){ state.session=s; state.user=s.user;
      SponzzaDB.loadResumeText(s).then(function(t){ if(t){state.resumeText=t;state.resumeName='cv.pdf';} if(panel.classList.contains('spz-open'))render(); });
    }
    if(panel.classList.contains('spz-open'))render();
  });
  matcher.load(chrome.runtime.getURL('data/sponsors.json'),chrome.runtime.getURL('data/aliases.json'))
    .then(function(m){console.log('[Sponzza] ready — '+(m.uniqueKeys||0)+' sponsors');if(panel.classList.contains('spz-open'))render();})
    .catch(function(e){console.error('[Sponzza] sponsor data load failed',e);});
})();
