/* assets/editor.js */
(function (wp) {
  if (!wp || !wp.plugins || !wp.editPost || !wp.data || !wp.element || !wp.components) return;

  const { registerPlugin } = wp.plugins;
  const { PluginDocumentSettingPanel, PluginSidebar, PluginSidebarMoreMenuItem } = wp.editPost;
  const {
    TextControl, TextareaControl, CheckboxControl, Tooltip, Notice, Button, ButtonGroup, PanelBody
  } = wp.components;
  const { useSelect, useDispatch } = wp.data;
  const { createElement: h, useMemo, Fragment, Component, useEffect, useState } = wp.element;
  const { __ } = wp.i18n;

  /* ---------------- Inject minimal styles (one-time) ---------------- */
  (function injectCSS(){
    if (document.getElementById('mc-seo-editor-css')) return;
    const css = `
      .mc-seo-panel .mc-header{display:flex;gap:12px;align-items:center;margin-bottom:12px}
      .mc-header-left{flex:0 0 auto;display:flex;align-items:center;gap:8px}
      .mc-header-left img{width:28px;height:28px;border-radius:6px;object-fit:cover;filter:drop-shadow(0 1px 1px rgba(0,0,0,.08))}
      .mc-header-right{flex:1 1 auto}
      .mc-level{font-weight:800;margin-bottom:6px}
      .mc-bar{height:8px;background:#eceff1;border-radius:999px;overflow:hidden}
      .mc-bar-fill{height:100%;background:linear-gradient(90deg,#00c875,#00fff0)}
      .mc-badges{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
      .mc-chip{display:inline-block;background:#eefdf7;border:1px solid #d4f7e8;border-radius:999px;padding:2px 8px;font-size:12px}
      .mc-card{background:#fff;border:1px solid #e8ecec;border-radius:14px;padding:12px 14px;margin-top:12px}
      .mc-card-title{font-weight:700;margin-bottom:6px}
      .mc-wins{margin:6px 0 10px 18px}
      .mc-wins-done{margin:0;opacity:.8}
      .mc-checks{margin:6px 0 0 18px}
      .mc-checks li{display:flex;gap:6px;align-items:center;margin:4px 0}
      .good{color:#139e52;font-weight:600}.bad{color:#e53935;font-weight:600}.warn{color:#ff9800;font-weight:600}
      .mc-ring{width:84px;height:84px}
      .mc-ring .track{fill:none;stroke:#e8ecec;stroke-width:10}
      .mc-ring .prog{fill:none;stroke:url(#mc-grad);stroke-width:10;stroke-linecap:round;transform:rotate(-90deg);transform-origin:50% 50%}
      .mc-ring-text{font-size:16px;font-weight:700;fill:#0b1b1a}

      /* Snippet preview (Google-like) */
      .mc-snippet{border:1px solid #e8ecec;border-radius:14px;padding:12px;margin-top:8px;background:#fff}
      .mc-snippet .bar{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
      .mc-snippet .switch{display:flex;gap:6px}
      .mc-sim{border:1px dashed #e0e6e6;border-radius:12px;padding:12px;background:#fafafa}
      .mc-sim.mobile{max-width:375px}
      .mc-sim.desktop{max-width:640px}
      .mc-serp-top{display:flex;align-items:center;gap:8px;margin-bottom:4px}
      .mc-serp-fav{width:16px;height:16px;border-radius:4px;object-fit:cover}
      .mc-serp-crumb{font-size:12px;color:#1a7f64}
      .mc-serp-title{font-size:20px;line-height:1.3;color:#1a0dab;margin:0 0 4px 0}
      .mc-serp-title a{text-decoration:none;color:#1a0dab}
      .mc-serp-desc{font-size:14px;line-height:1.4;color:#4d5156;margin:0}
      .mc-over{outline:2px solid rgba(229,57,53,.3);outline-offset:2px;border-radius:6px}
      .mc-mini{font-size:12px;opacity:.75;margin-top:6px}

      /* Gamification spice */
      .mc-hero{background:linear-gradient(135deg,#ffffff 0%,#f7fffd 70%);border:1px solid #e8ecec;border-radius:16px;padding:10px;margin:10px 0}
      .mc-celebrate{box-shadow:0 0 0 3px rgba(0,200,117,.12), 0 8px 24px rgba(0,0,0,.06);animation:mcPulse 1.4s ease-in-out infinite}
      @keyframes mcPulse{0%{transform:scale(1)}50%{transform:scale(1.01)}100%{transform:scale(1)}}
      .mc-confetti{position:relative}
      .mc-confetti:after{
        content:"🎉"; position:absolute; right:4px; top:4px; opacity:.85; filter:drop-shadow(0 1px 0 rgba(0,0,0,.05));
        animation:mcConfetti 1.2s ease-in-out infinite;
      }
      @keyframes mcConfetti{0%{transform:translateY(0) rotate(0)}50%{transform:translateY(-2px) rotate(7deg)}100%{transform:translateY(0) rotate(0)}}
    `;
    const el = document.createElement('style'); el.id = 'mc-seo-editor-css'; el.textContent = css; document.head.appendChild(el);
  })();

  /* ---------------- Error boundary ---------------- */
  class ErrorBoundary extends Component{
    constructor(p){ super(p); this.state={hasError:false}; }
    componentDidCatch(e){ this.setState({hasError:true}); console.error('[MC-SEO]',e); }
    render(){ return this.state.hasError
      ? h(Notice,{status:'error',isDismissible:false},'My Creative SEO sidebar failed to render.')
      : this.props.children; }
  }

  /* ---------------- Meta helpers ---------------- */
  function usePostMeta(keys){
    const meta = useSelect(s=>{
      const ed = s('core/editor');
      return (ed && ed.getEditedPostAttribute && ed.getEditedPostAttribute('meta')) || {};
    },[]);
    const { editPost } = useDispatch('core/editor');
    
    // Alternative approach: Use individual meta field updates
    const setMeta = (k,v)=> {
      if (!editPost) return;
      
      // Get the current meta to ensure we don't lose other fields
      const currentMeta = {...(meta || {})};
      
      // Only update if the value actually changed
      if (currentMeta[k] === v) return;
      
      console.log(`[MC-SEO] setMeta called: ${k} = "${v}"`);
      console.log(`[MC-SEO] Current meta state:`, currentMeta);
      
      // Try a different approach - use the core/editor store's editPost with explicit meta preservation
      try {
        // First, get the most current meta from the store
        const storeState = wp.data.select('core/editor');
        const currentStoreMeta = storeState ? storeState.getEditedPostAttribute('meta') : {};
        
        console.log(`[MC-SEO] Store meta:`, currentStoreMeta);
        
        // Merge with current meta and add the new value
        const finalMeta = {
          ...currentStoreMeta,
          ...currentMeta,
          [k]: v
        };
        
        console.log(`[MC-SEO] Final meta to save:`, finalMeta);
        
        // Update using editPost
        editPost({
          meta: finalMeta
        });
        
      } catch (error) {
        console.error('[MC-SEO] Error updating meta:', error);
        
        // Fallback to simple approach
        editPost({
          meta: {
            ...currentMeta,
            [k]: v
          }
        });
      }
    };
    
    return [meta,setMeta];
  }

  /* ---------------- Live analyzer ---------------- */
  function analyze(raw, kp, postTitle, slug, metaDesc){
    const html = String(raw||'');
    const text = html.replace(/<[^>]+>/g,' ');
    const words = (text.trim().match(/\S+/g)||[]).length;
    const sentences = Math.max(1,(text.match(/[.!?]\s+/g)||[]).length);
    const avg = Math.round((words/sentences)*10)/10;
    const h1 = (html.match(/<h1[\s>]/gi)||[]).length;
    const h2 = (html.match(/<h2[\s>]/gi)||[]).length;
    const h3 = (html.match(/<h3[\s>]/gi)||[]).length;
    const m = html.match(/<p[^>]*>([\s\S]*?)<\/p>/i);
    const first = (m?m[1]:'').replace(/<[^>]+>/g,' ');
    const fp = (first.trim().match(/\S+/g)||[]).length;
    const hasAlt = /<img\b[^>]*\balt\s*=\s*(['"])[^'"]*\1/i.test(html);
    const re = kp ? new RegExp(String(kp).replace(/[.*+?^${}()|[\]\\]/g,'\\$&'),'i') : null;
    return {
      words, avg, h1, h2, h3, fp, hasAlt,
      kp:{
        inTitle: !!(re && re.test(String(postTitle||''))),
        inFirst: !!(re && re.test(first)),
        inHead:  !!(re && re.test(html.replace(/<(?!h[1-3]\b)[^>]+>/gi,''))),
        inBody:  !!(re && re.test(text)),
        inSlug:  !!(re && re.test(String(slug||''))),
        inDesc:  !!(re && re.test(String(metaDesc||''))),
      }
    };
  }

  function compute(meta, stats, keys){
    const title = meta[keys.title] || '';
    const desc  = meta[keys.desc]  || '';
    const checks = {
      titleLen: title.length>=30 && title.length<=60,
      hasDesc:  !!desc,
      descLen:  desc.length>=90 && desc.length<=160,
      noindex:  !meta[keys.noindex],
      read:     stats.avg>0 && stats.avg<=22,
      oneH1:    stats.h1===1,
      h2h3:     (stats.h2+stats.h3)>=2,
      words:    stats.words>=600,
      intro:    stats.fp>=40 && stats.fp<=160,
      imgAlt:   stats.hasAlt,
      kpTitle:  stats.kp.inTitle,
      kpFirst:  stats.kp.inFirst,
      kpHead:   stats.kp.inHead,
      kpBody:   stats.kp.inBody,
      kpSlug:   stats.kp.inSlug,
      kpDesc:   stats.kp.inDesc,
    };
    const weight = {
      titleLen:6, hasDesc:4, descLen:4, noindex:8,
      read:8, oneH1:6, h2h3:4, words:6, intro:6, imgAlt:4,
      kpTitle:6, kpFirst:5, kpHead:4, kpBody:4, kpSlug:3, kpDesc:3
    };
    const max = Object.values(weight).reduce((a,b)=>a+b,0);
    const score = Object.entries(checks).reduce((s,[k,ok])=> s + (ok?weight[k]:0), 0);
    const pct = Math.round(score*100/max);
    const level = pct>=90?'Platinum':pct>=75?'Gold':pct>=55?'Silver':pct>=35?'Bronze':'Starter';

    const badges=[];
    if(checks.read && checks.intro && checks.h2h3) badges.push('AEO Ready');
    if(checks.read) badges.push('Readable');
    if(checks.imgAlt) badges.push('Alt-ready');
    if(checks.kpTitle && checks.kpFirst) badges.push('Keyphrase Aligned');

    const quickOrder = [
      ['titleLen','Tighten your title length (30–60 chars).'],
      ['hasDesc','Add a meta description (~120–160 chars).'],
      ['descLen','Adjust meta description length.'],
      ['read','Shorten sentences to ≤ 22 words on average.'],
      ['intro','Keep the opening paragraph 40–160 words and answer first.'],
      ['oneH1','Use exactly one H1.'],
      ['h2h3','Add H2/H3 subheadings (questions work well).'],
      ['imgAlt','Add an image with a descriptive alt attribute.'],
      ['words','Expand visible copy to roughly 600+ words.'],
      ['kpTitle','Include the keyphrase in the title.'],
      ['kpFirst','Mention the keyphrase in the intro.'],
      ['kpHead','Use the keyphrase in a subheading.'],
      ['kpBody','Use the keyphrase naturally in the body.'],
      ['kpDesc','Use the keyphrase in the meta description.'],
      ['kpSlug','Include the keyphrase in the slug.'],
    ];
    const quickWins = quickOrder.filter(([k])=>!checks[k]).slice(0,3).map(p=>p[1]);

    return { pct, level, checks, badges, quickWins };
  }

  /* ---------------- Templates + snippet utils ---------------- */
  function applyTitleTemplate(tpl, postTitle, siteName, sep){
    if (!tpl) return postTitle || '';
    const map = { '%%title%%': postTitle||'', '%%sitename%%': siteName||'', '%%sep%%': sep||'-' };
    return String(tpl).replace(/%%title%%|%%sitename%%|%%sep%%/g, m => map[m] ?? '');
  }
  function buildExcerptFromHTML(html, words=26){
    const text = String(html||'').replace(/<[^>]+>/g,' ').trim().replace(/\s+/g,' ');
    const parts = text.split(' ');
    return parts.slice(0, words).join(' ');
  }
  function clamp(str, max){ str = String(str||''); return (str.length>max)?str.slice(0,max-1)+'…':str; }

  /* ---------- Mirror: sidebar ↔ classic meta box (with proper sync) ---------- */
  function syncBridge(keys, meta, setMeta){
    // Skip sync entirely if no meta object to prevent clearing
    if (!meta || typeof meta !== 'object') {
      console.log('[MC-SEO] syncBridge: No meta object, skipping');
      return;
    }
    
    console.log('[MC-SEO] syncBridge: Starting with keys:', keys);
    
    const byId = id => /** @type {HTMLInputElement|HTMLTextAreaElement|null} */(document.getElementById(id));
    const map = {
      [keys.title]:       byId('mc_seo_title'),
      [keys.desc]:        /** @type any */ (byId('mc_seo_desc')),
      [keys.keywords]:    byId('mc_seo_keywords'),
      [keys.focus]:       byId('mc_seo_focus'),
      [keys.noindex]:     /** @type any */ (document.getElementById('mc_seo_noindex')),
      [keys.cornerstone]: /** @type any */ (document.getElementById('mc_seo_cornerstone')),
      [keys.og_title]:    byId('mc_seo_og_title'),
      [keys.og_desc]:     /** @type any */ (byId('mc_seo_og_desc')),
      [keys.og_image]:    byId('mc_seo_og_image'),
    };

    // Debug: check which elements are found
    Object.entries(map).forEach(([k,el]) => {
      console.log(`[MC-SEO] Element check: ${k} →`, el ? 'FOUND' : 'NOT FOUND');
    });

    // STEP 1: Set up event listeners for classic editor fields → update meta store
    Object.entries(map).forEach(([k,el])=>{
      if(!el || el.__mcBound) return;
      el.__mcBound = true;
      
      // Use both 'input' and 'change' events for better coverage
      const events = el.type==='checkbox' ? ['change'] : ['input', 'change', 'keyup', 'paste'];
      
      events.forEach(eventType => {
        el.addEventListener(eventType, (e) => {
          // Skip if we're currently syncing to prevent loops
          if (window.__mcSyncInProgress) return;
          
          const v = (el.type==='checkbox') ? !!el.checked : el.value;
          console.log(`[MC-SEO] Classic→Meta (${eventType}): ${k} = "${v}"`);
          
          // Clear any existing timeout
          clearTimeout(el.__mcDebounce);
          
          // For immediate events like paste, update faster
          const delay = (eventType === 'paste' || eventType === 'change') ? 50 : 300;
          
          el.__mcDebounce = setTimeout(() => {
            window.__mcSyncInProgress = true;
            console.log(`[MC-SEO] Actually updating meta: ${k} = "${v}"`);
            setMeta(k, v);
            setTimeout(() => { 
              window.__mcSyncInProgress = false; 
              console.log(`[MC-SEO] Sync lock released for ${k}`);
            }, 100);
          }, delay);
        });
      });
    });

    // STEP 2: Update classic editor fields when meta changes (from sidebar)
    Object.entries(map).forEach(([k,el])=>{
      if(!el) return;
      
      // Skip if sync in progress or element is focused
      if (window.__mcSyncInProgress || document.activeElement === el) return;
      
      const metaValue = meta[k];
      const currentValue = (el.type==='checkbox') ? !!el.checked : String(el.value || '');
      const newValue = (el.type==='checkbox') ? !!metaValue : String(metaValue || '');
      
      // Only update if values are actually different
      if (currentValue !== newValue) {
        console.log(`[MC-SEO] Meta→Classic: ${k} "${currentValue}" → "${newValue}"`);
        
        if (el.type==='checkbox') {
          el.checked = !!metaValue;
        } else {
          el.value = metaValue || '';
        }
      }
    });
  }

  /* ---------- LEFT: render snippet + mirrored sections ---------- */
  function renderClassicLeft(selector, leftHTML){
    const box = document.querySelector(selector);
    if(!box) return;
    box.innerHTML = leftHTML;
  }

  function buildLeftHTML(state, derived, checks, favURL){
    const li = (ok,text)=> `<li class="${ok?'good':'bad'}"><span>${ok?'✅':'❌'}</span> ${text}</li>`;
    const sim = (mobile)=>`
      <div class="mc-sim ${mobile?'mobile':'desktop'}" id="mc-sim-left">
        <div class="mc-serp-top">
          ${favURL ? `<img class="mc-serp-fav" src="${favURL}" alt="">` : `<span class="mc-serp-fav" style="background:#c4f5ef;display:inline-block"></span>`}
          <div class="mc-serp-crumb">${derived.urlShow}</div>
        </div>
        <h3 class="mc-serp-title ${derived.titleOver?'mc-over':''}"><a href="#">${derived.titleShort}</a></h3>
        <p class="mc-serp-desc ${derived.descOver?'mc-over':''}">${derived.descShort}</p>
      </div>
      <div class="mc-mini">Title: ${derived.title.length} chars · Description: ${derived.desc.length} chars</div>
    `;
    return `
      <div class="mc-card mc-hero ${state.pct>=90?'mc-celebrate mc-confetti':''}">
        <div style="display:flex;gap:10px;align-items:center;margin-bottom:4px">
          <div style="font-weight:800">${state.level}</div>
          <div class="mc-bar" style="flex:1"><div class="mc-bar-fill" style="width:${state.pct}%"></div></div>
          <div class="mc-chip">${state.pct}%</div>
        </div>
      </div>

      <div class="mc-card mc-snippet">
        <div class="mc-card-title">Snippet Preview</div>
        <div class="bar">
          <div class="mc-chip">Live</div>
          <div class="switch">
            <button type="button" class="button button-small" id="mc-left-mob">Mobile</button>
            <button type="button" class="button button-small" id="mc-left-desk">Desktop</button>
          </div>
        </div>
        ${sim(false)}
      </div>

      <div class="mc-card">
        <div class="mc-card-title">Quick Wins</div>
        ${ state.quickWins.length
            ? `<ul class="mc-wins">${state.quickWins.map(w=>`<li>⚡ ${w}</li>`).join('')}</ul>`
            : `<p class="mc-wins-done">Nice! No obvious quick wins left.</p>`
        }
      </div>

      <div class="mc-card">
        <div class="mc-card-title">Readability & AEO</div>
        <ul class="mc-checks">
          ${li(checks.read, '≤ 22 words per sentence')}
          ${li(checks.oneH1, 'Exactly one H1')}
          ${li(checks.h2h3, 'Use H2/H3 subheadings')}
          ${li(checks.words, '~600+ words')}
          ${li(checks.intro, 'Intro paragraph 40–160 words')}
          ${li(checks.imgAlt, 'Has image with alt attribute')}
        </ul>
      </div>

      <div class="mc-card">
        <div class="mc-card-title">Focus Keyphrase</div>
        <ul class="mc-checks">
          ${li(checks.kpTitle, 'Keyphrase in title')}
          ${li(checks.kpFirst, 'Keyphrase in first paragraph')}
          ${li(checks.kpHead, 'Keyphrase in a heading')}
          ${li(checks.kpBody, 'Keyphrase in content')}
          ${li(checks.kpSlug, 'Keyphrase in slug')}
          ${li(checks.kpDesc, 'Keyphrase in meta description')}
        </ul>
      </div>

      <div class="mc-card">
        <div class="mc-card-title">Meta basics</div>
        <ul class="mc-checks">
          ${li(checks.titleLen, 'Title 30–60 chars')}
          ${li(checks.hasDesc, 'Meta description present')}
          ${li(checks.descLen, 'Description 90–160 chars')}
          ${li(checks.noindex, 'Indexable (no noindex)')}
        </ul>
      </div>
    `;
  }

  function wireLeftSnippetButtons(){
    const sim = document.getElementById('mc-sim-left');
    const mob = document.getElementById('mc-left-mob');
    const desk= document.getElementById('mc-left-desk');
    if (mob && sim) mob.onclick = ()=>{ sim.classList.remove('desktop'); sim.classList.add('mobile'); };
    if (desk && sim) desk.onclick= ()=>{ sim.classList.remove('mobile');  sim.classList.add('desktop'); };
  }

  /* ---------------- RIGHT: Snippet Preview ---------------- */
  function SnippetPreview({ postTitle, slug, contentHTML, meta, keys, postType }){
    const [mobile,setMobile] = useState(false);
    const opts = (window.MCSEO || {});
    const templates = opts.templates || {};
    const siteName = (opts.site && opts.site.name) || document.title || '';
    const siteUrl  = (opts.site && opts.site.url)  || (location.origin + '/');
    const sep      = opts.sep || '–';
    const favicon  = (opts.site && opts.site.icon) || '';

    const userTitle = meta[keys.title] || '';
    const userDesc  = meta[keys.desc]  || '';
    let derivedTitle = userTitle || applyTitleTemplate((postType==='page')?templates.page:templates.post, postTitle, siteName, sep);
    const excerpt = buildExcerptFromHTML(contentHTML, 26);
    const derivedDesc = userDesc || excerpt;
    const urlShow = (siteUrl.replace(/\/+$/,'') + '/' + String(slug||'').replace(/^\/+|\/+$/g,'')).replace(/\/+$/,'');

    const titleOver = (derivedTitle.length>60);
    const descOver  = (derivedDesc.length>160);
    const simClass  = 'mc-sim ' + (mobile?'mobile':'desktop');

    return h('div',{className:'mc-card mc-snippet'},
      h('div',{className:'bar'},
        h('div',{style:{fontWeight:700,display:'flex',alignItems:'center',gap:8}},
          'Snippet Preview'
        ),
        h(ButtonGroup,{className:'switch'},
          h(Button,{isSecondary:!mobile,isPrimary:mobile,onClick:()=>setMobile(true)},'Mobile'),
          h(Button,{isSecondary:mobile,isPrimary:!mobile,onClick:()=>setMobile(false)},'Desktop')
        )
      ),
      h('div',{className:simClass},
        h('div',{className:'mc-serp-top'},
          favicon ? h('img',{className:'mc-serp-fav',src:favicon,alt:''}) : h('span',{className:'mc-serp-fav',style:{background:'#c4f5ef',display:'inline-block'}}),
          h('div',{className:'mc-serp-crumb'}, urlShow)
        ),
        h('h3',{className:'mc-serp-title ' + (titleOver?'mc-over':'')}, h('a',{href:'#'}, clamp(derivedTitle, 70))),
        h('p',{className:'mc-serp-desc ' + (descOver?'mc-over':'')}, clamp(derivedDesc, 170))
      ),
      h('div',{className:'mc-mini'},
        h('span',null, 'Title: ', derivedTitle.length, ' chars · '),
        h('span',null, 'Description: ', derivedDesc.length, ' chars')
      )
    );
  }

  /* ---------------- UI bits ---------------- */
  function Ring({ pct }){
    const r=38, c=2*Math.PI*r, o=c*(1-Math.min(100,Math.max(0,pct))/100);
    return h('svg',{viewBox:'0 0 92 92',className:'mc-ring'},
      h('defs',null,h('linearGradient',{id:'mc-grad',x1:'0%',y1:'0%',x2:'100%',y2:'0%'},
        h('stop',{offset:'0%','stopColor':'#00c875'}), h('stop',{offset:'100%','stopColor':'#00fff0'}))),
      h('circle',{cx:46,cy:46,r:38,className:'track'}),
      h('circle',{cx:46,cy:46,r:38,className:'prog',style:{strokeDasharray:c,strokeDashoffset:o}}),
      h('text',{x:46,y:52,textAnchor:'middle',className:'mc-ring-text'}, pct+'%')
    );
  }
  function Chip({children}){ return h('span',{className:'mc-chip'},children); }
  function Item({ok,label,tip}){ return h('li',{className:ok?'good':'bad',style:{display:'flex',gap:'6px',alignItems:'center',margin:'4px 0'}},
    h('span',null,ok?'✅':'❌'), h('span',null,label), tip ? h(Tooltip,{text:tip},h('span',{className:'mc-i'},' ⓘ')):null ); }

  /* ---------------- Panel ---------------- */
  function Panel(){
    const keys = (window.MCSEO && window.MCSEO.metaKeys) || {};
    const [meta,setMeta] = usePostMeta(keys);
    const content = useSelect(s=>{ const ed=s('core/editor'); return ed && ed.getEditedPostContent && ed.getEditedPostContent(); },[]);
    const postTitle   = useSelect(s=>{ const ed=s('core/editor'); return ed && ed.getEditedPostAttribute && ed.getEditedPostAttribute('title'); },[]);
    const slug    = useSelect(s=>{ const ed=s('core/editor'); return ed && ed.getEditedPostAttribute && ed.getEditedPostAttribute('slug'); },[]);
    const postType = useSelect(s=>{ const ed=s('core/editor'); return ed && ed.getCurrentPostType && ed.getCurrentPostType(); },[]);

    const stats = useMemo(()=> analyze(content, meta[keys.focus]||'', postTitle, slug, meta[keys.desc]||''), [content, meta[keys.focus], postTitle, slug, meta[keys.desc]]);
    const state = useMemo(()=> compute(meta, stats, keys), [meta, stats, keys]);

    /* --- derive snippet data for left preview --- */
    const opts = (window.MCSEO || {});
    const templates = opts.templates || {};
    const siteName = (opts.site && opts.site.name) || document.title || '';
    const siteUrl  = (opts.site && opts.site.url)  || (location.origin + '/');
    const sep      = opts.sep || '–';
    const userTitle = meta[keys.title] || '';
    const userDesc  = meta[keys.desc]  || '';
    const titleDerived = userTitle || applyTitleTemplate((postType==='page')?templates.page:templates.post, postTitle, siteName, sep);
    const excerpt = buildExcerptFromHTML(content, 26);
    const descDerived = userDesc || excerpt;
    const urlShow = (siteUrl.replace(/\/+$/,'') + '/' + String(slug||'').replace(/^\/+|\/+$/g,'')).replace(/\/+$/,'');
    const leftDerived = {
      title: titleDerived,
      titleShort: clamp(titleDerived, 70),
      titleOver: titleDerived.length>60,
      desc: descDerived,
      descShort: clamp(descDerived, 170),
      descOver: descDerived.length>160,
      urlShow
    };

    // Bridge + render Left (snippet + mirrored sections)
    useEffect(()=>{
      // Re-enable sync with improved logic
      console.log('[MC-SEO] Setting up sync bridge with keys:', keys);
      console.log('[MC-SEO] Current meta in useEffect:', meta);
      syncBridge(keys, meta, setMeta);
      const leftHTML = buildLeftHTML(state, leftDerived, state.checks, (opts.site && opts.site.icon) || '');
      renderClassicLeft('#mc-seo-reco', leftHTML);
      wireLeftSnippetButtons();
    }, [meta, state, keys, postType, postTitle, slug, content]);

    const celebrate = state.pct>=90 ? ' mc-celebrate mc-confetti' : '';
    const logoSrc = (window.MCSEO && window.MCSEO.assets && window.MCSEO.assets.logo) || '';

    const header = h('div',{className:'mc-header' + celebrate},
      h('div',{className:'mc-header-left'},
        logoSrc ? h('img',{src:logoSrc, alt:'logo'}) : null,
        h(Ring,{pct:state.pct})
      ),
      h('div',{className:'mc-header-right'},
        h('div',{className:'mc-level'}, state.level),
        h('div',{className:'mc-bar'}, h('div',{className:'mc-bar-fill',style:{width:state.pct+'%'}})),
        h('div',{className:'mc-badges'}, state.badges.map(b=>h(Chip,{key:b},b)))
      )
    );

    /* --- Accordions (PanelBody) --- */
    const inputsBasic = h(PanelBody,{title:__('Basic Meta','my-creative-seo'), initialOpen:true},
      h(TextControl,{label:__('SEO Title','my-creative-seo'), value:meta[keys.title]||'', onChange:v=>setMeta(keys.title,v), help:__('Aim for 30–60 characters','my-creative-seo'), maxLength:70}),
      h(TextareaControl,{label:__('Meta Description','my-creative-seo'), value:meta[keys.desc]||'', onChange:v=>setMeta(keys.desc,v), help:__('Aim for ~120–160 characters','my-creative-seo')}),
      h(TextControl,{label:__('Meta Keywords (legacy)','my-creative-seo'), value:meta[keys.keywords]||'', onChange:v=>setMeta(keys.keywords,v), placeholder:__('keyword1, keyword2','my-creative-seo')}),
      h(TextControl,{label:__('Focus Keyphrase','my-creative-seo'), value:meta[keys.focus]||'', onChange:v=>setMeta(keys.focus,v), placeholder:__('your main keyphrase','my-creative-seo'), help:__('Used for the live checks below; not output on the front-end.','my-creative-seo')}),
      h(CheckboxControl,{label:__('Noindex this page','my-creative-seo'), checked:!!meta[keys.noindex], onChange:v=>setMeta(keys.noindex,!!v)}),
      keys.cornerstone ? h(CheckboxControl,{label:__('Mark as Cornerstone','my-creative-seo'), checked:!!meta[keys.cornerstone], onChange:v=>setMeta(keys.cornerstone,!!v)}) : null,
    );

    const inputsSocial = h(PanelBody,{title:__('Social (OG/X)','my-creative-seo'), initialOpen:false},
      h(TextControl,{id:'mc_seo_og_title', label:__('OG/X Title (optional)','my-creative-seo'), value:meta[keys.og_title]||'', onChange:v=>setMeta(keys.og_title,v)}),
      h(TextareaControl,{id:'mc_seo_og_desc', label:__('OG/X Description (optional)','my-creative-seo'), value:meta[keys.og_desc]||'', onChange:v=>setMeta(keys.og_desc,v)}),
      h(TextControl,{id:'mc_seo_og_image', label:__('OG/X Image URL (optional)','my-creative-seo'), value:meta[keys.og_image]||'', onChange:v=>setMeta(keys.og_image,v), placeholder:__('https://…/social.jpg','my-creative-seo')}),
    );

    const winsCard = h('div',{className:'mc-card'},
      h('div',{className:'mc-card-title'},'Quick Wins'),
      state.quickWins.length ? h('ul',{className:'mc-wins'}, state.quickWins.map((t,i)=>h('li',{key:i},'⚡ ',t)))
                             : h('p',{className:'mc-wins-done'},'Nice! No obvious quick wins left.')
    );

    const readCard = h('div',{className:'mc-card'},
      h('div',{className:'mc-card-title'},'Readability & AEO'),
      h('ul',{className:'mc-checks'},[
        h(Item,{ok:state.checks.read,label:'≤ 22 words per sentence'}),
        h(Item,{ok:state.checks.oneH1,label:'Exactly one H1'}),
        h(Item,{ok:state.checks.h2h3,label:'Use H2/H3 subheadings'}),
        h(Item,{ok:state.checks.words,label:'~600+ words'}),
        h(Item,{ok:state.checks.intro,label:'Intro paragraph 40–160 words'}),
        h(Item,{ok:state.checks.imgAlt,label:'Has image with alt attribute'}),
      ])
    );

    const kpCard = h('div',{className:'mc-card'},
      h('div',{className:'mc-card-title'},'Focus Keyphrase'),
      h('ul',{className:'mc-checks'},[
        h(Item,{ok:state.checks.kpTitle,label:'Keyphrase in title'}),
        h(Item,{ok:state.checks.kpFirst,label:'Keyphrase in first paragraph'}),
        h(Item,{ok:state.checks.kpHead,label:'Keyphrase in a heading'}),
        h(Item,{ok:state.checks.kpBody,label:'Keyphrase in content'}),
        h(Item,{ok:state.checks.kpSlug,label:'Keyphrase in slug'}),
        h(Item,{ok:state.checks.kpDesc,label:'Keyphrase in meta description'}),
      ])
    );

    const metaBasicsCard = h('div',{className:'mc-card'},
      h('div',{className:'mc-card-title'},'Meta basics'),
      h('ul',{className:'mc-checks'},[
        h(Item,{ok:state.checks.titleLen,label:'Title 30–60 chars'}),
        h(Item,{ok:state.checks.hasDesc,label:'Meta description present'}),
        h(Item,{ok:state.checks.descLen,label:'Description 90–160 chars'}),
        h(Item,{ok:state.checks.noindex,label:'Indexable (no noindex)'}),
      ])
    );

    const body = h(Fragment,null,
      header,
      /* Right snippet preview */
      h(SnippetPreview,{ postTitle, slug, contentHTML:content, meta, keys, postType }),
      /* Accordions */
      inputsBasic,
      inputsSocial,
      /* Cards */
      winsCard,
      readCard,
      kpCard,
      metaBasicsCard,
    );

    if (PluginDocumentSettingPanel) {
      return h(ErrorBoundary,null,
        h(PluginDocumentSettingPanel,{name:'mc-seo-panel',title:'My Creative SEO',className:'mc-seo-panel'}, body)
      );
    }
    return h(ErrorBoundary,null,
      h(Fragment,null,
        h(PluginSidebarMoreMenuItem,{target:'mc-seo-sidebar'},'My Creative SEO'),
        h(PluginSidebar,{name:'mc-seo-sidebar',title:'My Creative SEO'}, body)
      )
    );
  }

  try { registerPlugin('mc-seo', { render: Panel, icon: null }); }
  catch(e){ console.error('[MC-SEO] registerPlugin failure:', e); }

})(window.wp);